diff --git a/apps/nextra/pages/en/build/sdks/go-sdk.mdx b/apps/nextra/pages/en/build/sdks/go-sdk.mdx index e8f0189ac..5fd96c8db 100644 --- a/apps/nextra/pages/en/build/sdks/go-sdk.mdx +++ b/apps/nextra/pages/en/build/sdks/go-sdk.mdx @@ -2,16 +2,12 @@ title: "Go SDK" --- -import { Callout } from "nextra/components"; +import { Card, Cards } from '@components/index' # Aptos Go SDK ## Installing the Go SDK - - The Go SDK is currently in a Beta, and its interfaces are subject to change - - Aptos provides an official Go SDK in the [aptos-go-sdk GitHub](https://github.com/aptos-labs/aptos-go-sdk) repository. To use the Go SDK, get the main package here: @@ -20,78 +16,20 @@ To use the Go SDK, get the main package here: go get github.com/aptos-labs/aptos-go-sdk ``` -## Using the Go SDK - -### Creating a client - -You can create a client by importing the aptos-go-sdk, and creating a `Client` - -```go -package main - -import ( - "fmt" - "github.com/aptos-labs/aptos-go-sdk" -) - -func main() { - client, err := aptos.NewClient(aptos.DevnetConfig) - if err != nil { - panic("Failed to create client:" + err.Error()) - } - fmt.Println("client", client) -} -``` - -You can configure the network with the `aptos.NetworkConfig`, or use a -preexisting `aptos.DevnetConfig`, `aptos.TestnetConfig`, -or `aptos.MainnetConfig` - -### Creating a private key - -You can create a new `Ed25519` account's private key by -calling `NewEd25519Account()`. - -```go -account, err := aptos.NewEd25519Account() -if err != nil { - return err -} -``` - -### Funding accounts - -You can create and fund an account with a faucet on any network that is not -mainnet - -```go -account, err := aptos.NewEd25519Account() -err = client.Fund(account.Address, 100_000_000) -``` - -### Sending a transaction - -You can send a AptosCoin via a transaction - -```go - account, err := aptos.NewEd25519account() - - // Build transaction - signed_txn, err := aptos.APTTransferTransaction(client, account, AccountOne, 100) - - // Submit transaction - result, err := client.SubmitTransaction(signed_txn) - hash := result["hash"].(string) - - // Wait for the transaction - _, err = client.WaitForTransaction(hash) - - // Read transaction by hash - txn, err := client.TransactionByHash(hash) -``` - -### More examples +## Usage + + + + Fetching Data + Learn how to fetch data with the Go SDK + + + Submitting Transactions + Learn how to submit transactions with the Go SDK + + + Examples + Explore Go examples provided in the SDK + + -You can see more examples in -the [`examples/` folder](https://github.com/aptos-labs/aptos-go-sdk/tree/main/examples) -of the Go SDK repository diff --git a/apps/nextra/pages/en/build/sdks/go-sdk/_meta.tsx b/apps/nextra/pages/en/build/sdks/go-sdk/_meta.tsx new file mode 100644 index 000000000..1baa6fd34 --- /dev/null +++ b/apps/nextra/pages/en/build/sdks/go-sdk/_meta.tsx @@ -0,0 +1,18 @@ +export default { + "---usage---": { + type: "separator", + title: "Usage", + }, + account: { + title: "Accounts", + }, + "fetch-data-via-sdk": { + title: "Fetch Data", + }, + "building-transactions": { + title: "Transactions", + }, + "go-examples": { + title: "Example Code", + }, +}; diff --git a/apps/nextra/pages/en/build/sdks/go-sdk/account.mdx b/apps/nextra/pages/en/build/sdks/go-sdk/account.mdx new file mode 100644 index 000000000..1164b3121 --- /dev/null +++ b/apps/nextra/pages/en/build/sdks/go-sdk/account.mdx @@ -0,0 +1,93 @@ +--- +title: "Go SDK - Creating and Managing Accounts" +--- + +# Creating and Managing Accounts + +There are several ways to generate account credentials using the Go SDK. You can +use: +- `aptos.NewEd25519Account()` +- `aptos.NewSecp256k1Account()` +- `aptos.NewEd25519SingleSenderAccount()` +- `aptos.NewAccountFromSigner()` + +`Account.NewEd25519Account()` is the most commonly used method to create keys +for a new account. It defaults to `ED25519` key types, but you can also specify +which signing scheme you would prefer like so: + +```go +// To derive an ed25519 account +account1 := aptos.NewEd25519Account() + +// To derive a secp256k1 account +account2 := aptos.NewSecp256k1Account() +``` + +Once you have generated credentials, you **must** fund it for the network to know it exists. + +In test environments this can be done with a faucet by running the following command: + +```go filename="fund.go" +client, err = aptos.NewClient(aptos.DevnetConfig) +if err != nil { + panic("Failed to create client:" + err.Error()) +} + +// Fund an account with 1 Devnet APT +client.Fund(account1.Address, 100_000_000) +``` + +## Other Ways To Represent Accounts +If you have a private key, or equivalent representation, you can use that to +create an `Account` struct to manage those credentials while using the Go SDK. + +Here are several examples that show how to do so with specific encoding schemes. + +### Derive an account from private key + +The SDK supports deriving an account from a private key with `NewAccountFromSigner()` method. +In addition, this method supports deriving an account from a private key and account address. +This method uses a local calculation and therefore is used to derive an `Account` that has not had its authentication key rotated. + +```go +// to derive an account with a Ed25519 key scheme +privateKey := &aptos.Ed25519PrivateKey{} +err := privateKey.FromHex(privateKeyHex) +if err != nil { + panic("Failed to parse private key:" + err.Error()) +} +account := aptos.NewAccountFromSigner(privateKey) + +// to derive an account with a Single Sender Ed25519 key scheme +privateKey := &aptos.Ed25519PrivateKey{} +err := privateKey.FromHex(privateKeyHex) +if err != nil { + panic("Failed to parse private key:" + err.Error()) +} +singleSigner := &crypto.SingleSigner{Signer: privateKey} +account := aptos.NewAccountFromSigner(singleSigner) + +// to derive an account with a Single Sender Secp256k1 key scheme +privateKey := &aptos.Secp256k1PrivateKey{} +err := privateKey.FromHex(privateKeyHex) +if err != nil { + panic("Failed to parse private key:" + err.Error()) +} +singleSigner := &crypto.SingleSigner{Signer: privateKey} +account := aptos.NewAccountFromSigner(singleSigner) + +// to derive an account with a private key and account address +address := &aptos.AccountAddress{} +err := address.ParseStringRelaxed(addressHex) +if err != nil { + panic("Failed to parse address:" + err.Error()) +} +privateKey := &aptos.Ed25519PrivateKey{} +err := privateKey.FromHex(privateKeyHex) +if err != nil { + panic("Failed to parse private key:" + err.Error()) +} +account := aptos.NewAccountFromSigner(privateKey, address.AuthKey()) +``` + +{/* TODO: Once derivation path is supported ### Derive an account from derivation path */} diff --git a/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions.mdx b/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions.mdx new file mode 100644 index 000000000..09ffda707 --- /dev/null +++ b/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions.mdx @@ -0,0 +1,330 @@ +--- +title: "Go SDK - Building Transactions" +--- + +import { Callout, Steps } from 'nextra/components'; + +# Using Transactions + +Transactions allow you to change on-chain data or trigger events. Generally, +transactions follow 5 steps from building to executing on chain: building, +simulating, signing, submitting, and waiting. + + + For these examples, `client` is an instance of the + [`Client`](https://pkg.go.dev/github.com/aptos-labs/aptos-go-sdk#Client) + object. + + + + ### Build + + Building a transaction is how you specify: + + 1. **The `Sender` account.**
This account normally pays the gas fees for + this transaction. See [Sponsoring Transactions](building-transactions/sponsoring-transactions.mdx) + to learn how to have another account pay for transaction fees. + 2. **The `Function` being called on-chain.**
This is the identifier for + the smart contract entry function on-chain that will trigger when you execute + this transaction. + 3. **The `ArgTypes` and `Args`.**
This is any data the function needs to + run. + + This can be made for a single account like so: + + ```go filename="build_a_transaction.go" + // 1. Build transaction + accountBytes, err := bcs.Serialize(&bob.Address) + if err != nil { + panic("Failed to serialize bob's address:" + err.Error()) + } + + amountBytes, err := bcs.SerializeU64(TransferAmount) + if err != nil { + panic("Failed to serialize transfer amount:" + err.Error()) + } + rawTxn, err := client.BuildTransaction(alice.AccountAddress(), aptos.TransactionPayload{ + Payload: &aptos.EntryFunction{ + Module: aptos.ModuleId{ + Address: aptos.AccountOne, + Name: "aptos_account", + }, + Function: "transfer", + ArgTypes: []aptos.TypeTag{}, + Args: [][]byte{ + accountBytes, + amountBytes, + }, + }}, + ) + ``` + + + All arguments `Args` must be serialized to bytes before being passed in. They + must be serialized with [Binary Canonical Serialization (BCS)](building-transactions/bcs-format.mdx) + + + #### Building Options + + You can customize the way your transaction executes by passing in + `options` when building. Some of the most commonly used options are: + + 1. `MaxGasAmount` - This caps the amount of gas you are willing to pay for to + execute this transaction. + 2. `GasUnitPrice` - You can specify a higher than minimum price per gas to be + executed with higher priority by the Aptos network. + 3. `ExpirationSeconds` - This gives a concrete time the transaction must execute + by or it will be canceled. + + The SDK provides sensible defaults for these values if they are not specified + explicitly. + + ### Simulate (Optional) + + Every transaction on the Aptos chain has a gas fee associated with how much + work the network machines have to do when executing the transaction. In order + to estimate the cost associated with that, you can simulate transactions + before committing them. + + + This simulation only requires the `PublicKey` of an account since it will + not impact the actual state of the ledger. + + + You can execute the simulation by using + `aptos.SimulateTransaction` like so: + + ```go filename="build_a_transaction.go" + // 2. Simulate transaction (optional) + // This is useful for understanding how much the transaction will cost + // and to ensure that the transaction is valid before sending it to the network + // This is optional, but recommended + simulationResult, err := client.SimulateTransaction(rawTxn, alice) + + // If the fee looks ok, continue to signing! + ``` + + ### Sign + + Once the transaction is built and the fees seem reasonable, you can sign the + transaction with `rawTransaction.SignedTransaction()`. The signature must come + from the `sender` account. + + ```go filename="build_a_transaction.go" + // 3. Sign transaction + signedTxn, err := rawTxn.SignedTransaction(alice) + ``` + + ### Submit + + Now that the transaction is signed, you can submit it to the network using + `client.SubmitTransaction()` like so: + + ```go filename="build_a_transaction.go" + // 4. Submit transaction + submitResult, err := client.SubmitTransaction(signedTxn) + ``` + + ### Wait + + Finally, you can wait for the result of the transaction by using + `client.WaitForTransaction()` and specifying the hash of the transaction you + just submitted like so: + + ```go filename="build_a_transaction.go" + // 5. Wait for the transaction to complete + txnHash := submitResult.Hash + _, err = client.WaitForTransaction(txnHash) + ``` +
+ +## Full Go Example + +```go filename="build_a_transaction.go" +// transfer_coin is an example of how to make a coin transfer transaction in the simplest possible way +package main + +import ( + "fmt" + + "github.com/aptos-labs/aptos-go-sdk" + "github.com/aptos-labs/aptos-go-sdk/bcs" +) + +const FundAmount = 100_000_000 +const TransferAmount = 1_000 + +// example This example shows you how to make an APT transfer transaction in the simplest possible way +func example(networkConfig aptos.NetworkConfig) { + // Create a client for Aptos + client, err := aptos.NewClient(networkConfig) + if err != nil { + panic("Failed to create client:" + err.Error()) + } + + // Create accounts locally for alice and bob + alice, err := aptos.NewEd25519Account() + if err != nil { + panic("Failed to create alice:" + err.Error()) + } + bob, err := aptos.NewEd25519Account() + if err != nil { + panic("Failed to create bob:" + err.Error()) + } + + fmt.Printf("\n=== Addresses ===\n") + fmt.Printf("Alice: %s\n", alice.Address.String()) + fmt.Printf("Bob:%s\n", bob.Address.String()) + + // Fund the sender with the faucet to create it on-chain + err = client.Fund(alice.Address, FundAmount) + if err != nil { + panic("Failed to fund alice:" + err.Error()) + } + + aliceBalance, err := client.AccountAPTBalance(alice.Address) + if err != nil { + panic("Failed to retrieve alice balance:" + err.Error()) + } + bobBalance, err := client.AccountAPTBalance(bob.Address) + if err != nil { + panic("Failed to retrieve bob balance:" + err.Error()) + } + fmt.Printf("\n=== Initial Balances ===\n") + fmt.Printf("Alice: %d\n", aliceBalance) + fmt.Printf("Bob:%d\n", bobBalance) + + // 1. Build transaction + accountBytes, err := bcs.Serialize(&bob.Address) + if err != nil { + panic("Failed to serialize bob's address:" + err.Error()) + } + + amountBytes, err := bcs.SerializeU64(TransferAmount) + if err != nil { + panic("Failed to serialize transfer amount:" + err.Error()) + } + rawTxn, err := client.BuildTransaction(alice.AccountAddress(), aptos.TransactionPayload{ + Payload: &aptos.EntryFunction{ + Module: aptos.ModuleId{ + Address: aptos.AccountOne, + Name: "aptos_account", + }, + Function: "transfer", + ArgTypes: []aptos.TypeTag{}, + Args: [][]byte{ + accountBytes, + amountBytes, + }, + }}, + ) + + if err != nil { + panic("Failed to build transaction:" + err.Error()) + } + + // 2. Simulate transaction (optional) + // This is useful for understanding how much the transaction will cost + // and to ensure that the transaction is valid before sending it to the network + // This is optional, but recommended + simulationResult, err := client.SimulateTransaction(rawTxn, alice) + if err != nil { + panic("Failed to simulate transaction:" + err.Error()) + } + fmt.Printf("\n=== Simulation ===\n") + fmt.Printf("Gas unit price: %d\n", simulationResult[0].GasUnitPrice) + fmt.Printf("Gas used: %d\n", simulationResult[0].GasUsed) + fmt.Printf("Total gas fee: %d\n", simulationResult[0].GasUsed*simulationResult[0].GasUnitPrice) + fmt.Printf("Status: %s\n", simulationResult[0].VmStatus) + + // 3. Sign transaction + signedTxn, err := rawTxn.SignedTransaction(alice) + if err != nil { + panic("Failed to sign transaction:" + err.Error()) + } + + // 4. Submit transaction + submitResult, err := client.SubmitTransaction(signedTxn) + if err != nil { + panic("Failed to submit transaction:" + err.Error()) + } + txnHash := submitResult.Hash + + // 5. Wait for the transaction to complete + _, err = client.WaitForTransaction(txnHash) + if err != nil { + panic("Failed to wait for transaction:" + err.Error()) + } + + // Check balances + aliceBalance, err = client.AccountAPTBalance(alice.Address) + if err != nil { + panic("Failed to retrieve alice balance:" + err.Error()) + } + bobBalance, err = client.AccountAPTBalance(bob.Address) + if err != nil { + panic("Failed to retrieve bob balance:" + err.Error()) + } + fmt.Printf("\n=== Intermediate Balances ===\n") + fmt.Printf("Alice: %d\n", aliceBalance) + fmt.Printf("Bob:%d\n", bobBalance) + + // Now do it again, but with a different method + resp, err := client.BuildSignAndSubmitTransaction(alice, aptos.TransactionPayload{ + Payload: &aptos.EntryFunction{ + Module: aptos.ModuleId{ + Address: aptos.AccountOne, + Name: "aptos_account", + }, + Function: "transfer", + ArgTypes: []aptos.TypeTag{}, + Args: [][]byte{ + accountBytes, + amountBytes, + }, + }}, + ) + if err != nil { + panic("Failed to sign transaction:" + err.Error()) + } + + _, err = client.WaitForTransaction(resp.Hash) + if err != nil { + panic("Failed to wait for transaction:" + err.Error()) + } + + aliceBalance, err = client.AccountAPTBalance(alice.Address) + if err != nil { + panic("Failed to retrieve alice balance:" + err.Error()) + } + bobBalance, err = client.AccountAPTBalance(bob.Address) + if err != nil { + panic("Failed to retrieve bob balance:" + err.Error()) + } + fmt.Printf("\n=== Final Balances ===\n") + fmt.Printf("Alice: %d\n", aliceBalance) + fmt.Printf("Bob:%d\n", bobBalance) +} + +func main() { + example(aptos.DevnetConfig) +} +``` + +## Summary + +Building and sending transactions on-chain involves the following 5 steps: +1. **Build** the transaction. +2. **Simulate** the cost. (Optional) +3. **Sign** the transaction (if the simulated cost seems ok). +4. **Submit** the transaction to the network. +5. **Wait** for the chain to validate and update. + +## Explore Advanced Transaction Features + +Transactions have a couple of additional features which let them adapt to your needs which you can learn about here: + +1. [Multi-Agent Signatures](building-transactions/multi-agent-transactions.mdx) - Allowing multiple accounts to be used for a single contract. +2. [Sponsoring Transactions](building-transactions/sponsoring-transactions.mdx) - Have another account pay gas fees for this transaction. +3. [Batch Submit Transactions](building-transactions/batching-transactions.mdx) - How to send multiple transactions quickly from a single account. +4. [Binary Canonical Serialization (BCS)](building-transactions/bcs-format.mdx) - The format used to serialize data for Aptos transactions. diff --git a/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions/_meta.tsx b/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions/_meta.tsx new file mode 100644 index 000000000..7c065fafe --- /dev/null +++ b/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions/_meta.tsx @@ -0,0 +1,17 @@ +export default { + "simulating-transactions": { + title: "Simulating Transactions", + }, + "multi-agent-transactions": { + title: "Multi-Agent Transactions", + }, + "sponsoring-transactions": { + title: "Sponsoring Transactions", + }, + "batching-transactions": { + title: "Batching Transactions", + }, + "bcs-format": { + title: "BCS Format", + }, +}; diff --git a/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions/batching-transactions.mdx b/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions/batching-transactions.mdx new file mode 100644 index 000000000..b765b6d8f --- /dev/null +++ b/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions/batching-transactions.mdx @@ -0,0 +1,146 @@ +--- +title: "Go SDK - Batching Transactions" +--- + +# Batching Transactions + +The Go SDK has a built-in way to send many transactions concurrently, and order +them. This can be a convenient tool when trying to execute multiple transactions +quickly from the same account. + +This can be done with `client.BuildSignAndSubmitTransactions` as can be seen in the below example. + +## Full Go Example + +```go filename="batch.go" +// sending_concurrent_transactions shows how to submit transactions serially or concurrently on a single account +package main + +import ( + "github.com/aptos-labs/aptos-go-sdk" + "github.com/aptos-labs/aptos-go-sdk/api" + "time" +) + +func setup(networkConfig aptos.NetworkConfig) (*aptos.Client, aptos.TransactionSigner) { + client, err := aptos.NewClient(networkConfig) + if err != nil { + panic("Failed to create client:" + err.Error()) + } + + sender, err := aptos.NewEd25519Account() + if err != nil { + panic("Failed to create sender:" + err.Error()) + } + + err = client.Fund(sender.Address, 100_000_000) + if err != nil { + panic("Failed to fund sender:" + err.Error()) + } + + return client, sender +} + +func payload() aptos.TransactionPayload { + receiver := aptos.AccountAddress{} + err := receiver.ParseStringRelaxed("0xBEEF") + if err != nil { + panic("Failed to parse address:" + err.Error()) + } + amount := uint64(100) + p, err := aptos.CoinTransferPayload(nil, receiver, amount) + if err != nil { + panic("Failed to serialize arguments:" + err.Error()) + } + return aptos.TransactionPayload{Payload: p} +} + +func sendManyTransactionsSerially(networkConfig aptos.NetworkConfig, numTransactions uint64) { + client, sender := setup(networkConfig) + + responses := make([]*api.SubmitTransactionResponse, numTransactions) + payload := payload() + + senderAddress := sender.AccountAddress() + sequenceNumber := uint64(0) + for i := uint64(0); i < numTransactions; i++ { + rawTxn, err := client.BuildTransaction(senderAddress, payload, aptos.SequenceNumber(sequenceNumber)) + if err != nil { + panic("Failed to build transaction:" + err.Error()) + } + + signedTxn, err := rawTxn.SignedTransaction(sender) + if err != nil { + panic("Failed to sign transaction:" + err.Error()) + } + + submitResult, err := client.SubmitTransaction(signedTxn) + if err != nil { + panic("Failed to submit transaction:" + err.Error()) + } + responses[i] = submitResult + sequenceNumber++ + } + + // Wait on last transaction + response, err := client.WaitForTransaction(responses[numTransactions-1].Hash) + if err != nil { + panic("Failed to wait for transaction:" + err.Error()) + } + if response.Success == false { + panic("Transaction failed due to " + response.VmStatus) + } +} + +func sendManyTransactionsConcurrently(networkConfig aptos.NetworkConfig, numTransactions uint64) { + client, sender := setup(networkConfig) + payload := payload() + + // start submission goroutine + payloads := make(chan aptos.TransactionBuildPayload, 50) + results := make(chan aptos.TransactionSubmissionResponse, 50) + go client.BuildSignAndSubmitTransactions(sender, payloads, results) + + // Submit transactions to goroutine + go func() { + for i := uint64(0); i < numTransactions; i++ { + payloads <- aptos.TransactionBuildPayload{ + Id: i, + Type: aptos.TransactionSubmissionTypeSingle, + Inner: payload, + } + } + close(payloads) + }() + + // Wait for all transactions to be processed + for result := range results { + if result.Err != nil { + panic("Failed to submit and wait for transaction:" + result.Err.Error()) + } + } +} + +// example This example shows you how to improve performance of the transaction submission +// +// Speed can be improved by locally handling the sequence number, gas price, and other factors +func example(networkConfig aptos.NetworkConfig, numTransactions uint64) { + println("Sending", numTransactions, "transactions Serially") + startSerial := time.Now() + sendManyTransactionsSerially(networkConfig, numTransactions) + endSerial := time.Now() + println("Serial:", time.Duration.Milliseconds(endSerial.Sub(startSerial)), "ms") + + println("Sending", numTransactions, "transactions Concurrently") + startConcurrent := time.Now() + sendManyTransactionsConcurrently(networkConfig, numTransactions) + endConcurrent := time.Now() + println("Concurrent:", time.Duration.Milliseconds(endConcurrent.Sub(startConcurrent)), "ms") + + println("Concurrent is", time.Duration.Milliseconds(endSerial.Sub(startSerial)-endConcurrent.Sub(startConcurrent)), "ms faster than Serial") +} + +func main() { + example(aptos.DevnetConfig, 100) +} +``` diff --git a/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions/bcs-format.mdx b/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions/bcs-format.mdx new file mode 100644 index 000000000..efcf5060c --- /dev/null +++ b/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions/bcs-format.mdx @@ -0,0 +1,41 @@ +--- +title: "Go SDK - Binary Canonical Serialization (BCS) Format" +--- + +# Binary Canonical Serialization (BCS) Format + +All transaction arguments for the Aptos Go SDK are encoded as bytes in Binary +Canonical Serialization (BCS) format. This is the format the Aptos chain +recognizes, with specific types (ex. Instead of an uint64 or big.Int, it uses +types like `u64` or `u128`) + + +You can directly use the BCS format to build transactions by specifying argument types explicitly like so: + +```go filename="example.go" + accountBytes, err := bcs.Serialize(&bob.Address) + if err != nil { + panic("Failed to serialize bob's address:" + err.Error()) + } + + amountBytes, err := bcs.SerializeU64(TransferAmount) + if err != nil { + panic("Failed to serialize transfer amount:" + err.Error()) + } + rawTxn, err := client.BuildTransaction(alice.AccountAddress(), aptos.TransactionPayload{ + Payload: &aptos.EntryFunction{ + Module: aptos.ModuleId{ + Address: aptos.AccountOne, + Name: "aptos_account", + }, + Function: "transfer", + ArgTypes: []aptos.TypeTag{}, + Args: [][]byte{ + accountBytes, + amountBytes, + }, + }}, + ) +``` + +You can learn more about BCS by exploring the [BCS GitHub repo](https://github.com/aptos-labs/bcs). diff --git a/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions/multi-agent-transactions.mdx b/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions/multi-agent-transactions.mdx new file mode 100644 index 000000000..092467f60 --- /dev/null +++ b/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions/multi-agent-transactions.mdx @@ -0,0 +1,96 @@ +--- +title: "Go SDK - Multi-Agent Transactions" +--- + +import { Callout, Steps } from 'nextra/components' + +# Multi-Agent Transactions + +Multi-agent transactions allow multiple accounts to participate in the logic of +a Move contract. + +This can be used to require multiple parties agree to a transaction before +executing or to use resources from multiple accounts. + +## Writing Multi-Agent Transactions + +Creating and executing a multi-agent transaction follows a similar flow to the +[regular transaction flow](../building-transactions.mdx), but with several +notable differences. + + + Instead of `client.BuildTransaction`, multi-agent and sponsored transactions + use `client.BuildTransactionMultiAgent`. + + + + ### Build the transaction by including `aptos.AdditionalSigners` with a list of each additional signers. + + + Make sure to replace the `Function` field below with your entry function + that requires multiple agents to sign. + + + ```go filename="multi_agent.go" + transaction, err := client.BuildTransactionMultiAgent(alice.AccountAddress(), aptos.TransactionPayload{ + Payload: &aptos.EntryFunction{ + // Replace module and function with your multi-agent function + Module: aptos.ModuleId{ + Address: aptos.AccountOne, + Name: "aptos_account", + }, + Function: "transfer", + ArgTypes: []aptos.TypeTag{}, + Args: [][]byte{ + accountBytes, + amountBytes, + }, + }, + AdditionalSigners: []aptos.AccountAddress{bob.AccountAddress()}, + }) + ``` + +{/* TODO Support simulation of multiagent */} + + ### Sign once for each signer. + + You will combine these signatures in the next step. + + ```go filename="multi_agent.go" + aliceAuth, err := rawTxn.Sign(alice) + if err != nil { + panic("Failed to sign transaction as sender:" + err.Error()) + } + bobAuth, err := rawTxn.Sign(bob) + if err != nil { + panic("Failed to sign transaction as second signer:" + err.Error()) + } + ``` + + ### Combine the signatures with the raw transaction to create a multi-agent signed transaction. + + ```go filename="multi_agent.go" + signedTxn, ok := rawTxn.ToMultiAgentSignedTransaction(aliceAuth, []crypto.AccountAuthenticator{bobAuth}) + ``` + ### Submit the transaction by combining all agent signatures via the `aptos.AdditionalSigners` parameter. + + ```go filename="multi_agent.go" + submitResponse, err := client.SubmitTransaction(signedTxn) + ``` + + ### Lastly, wait for the transaction to resolve. + + ```go filename="multi_agent.go" + txnResult, err := client.WaitForTransaction(submitResponse.Hash) + ``` + + +{/* TODO: Add full code snippet for Go */} + +## Common Errors + +`NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH` - This happens when you are attempting to +do multi-agent signing for a function which does not require that number of +accounts. For example, if you try using multiple signatures for a +`0x1::aptos_account::transfer` function - it only expects one address, and so +produces an error when more than one is provided. diff --git a/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions/simulating-transactions.mdx b/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions/simulating-transactions.mdx new file mode 100644 index 000000000..24d3efc2d --- /dev/null +++ b/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions/simulating-transactions.mdx @@ -0,0 +1,124 @@ +--- +title: "Go SDK - Simulating Transactions" +--- + +# Simulating Transactions + +Simulating transactions allows you to preview the cost and effect of submitting +a transaction without paying fees. You can use this to estimate fees, test a +transaction, or to check what the output might be. + +To simulate a transaction, you must pass in a transaction and which account +would be the signer: + +```go filename="simulate_a_transaction.go" +// transfer_coin is an example of how to make a coin transfer transaction in the simplest possible way +package main + +import ( + "fmt" + + "github.com/aptos-labs/aptos-go-sdk" + "github.com/aptos-labs/aptos-go-sdk/bcs" +) + +const FundAmount = 100_000_000 +const TransferAmount = 1_000 + +// example This example shows you how to make an APT transfer transaction in the simplest possible way +func example(networkConfig aptos.NetworkConfig) { + // Create a client for Aptos + client, err := aptos.NewClient(networkConfig) + if err != nil { + panic("Failed to create client:" + err.Error()) + } + + // Create accounts locally for alice and bob + alice, err := aptos.NewEd25519Account() + if err != nil { + panic("Failed to create alice:" + err.Error()) + } + bob, err := aptos.NewEd25519Account() + if err != nil { + panic("Failed to create bob:" + err.Error()) + } + + fmt.Printf("\n=== Addresses ===\n") + fmt.Printf("Alice: %s\n", alice.Address.String()) + fmt.Printf("Bob:%s\n", bob.Address.String()) + + // Fund the sender with the faucet to create it on-chain + err = client.Fund(alice.Address, FundAmount) + if err != nil { + panic("Failed to fund alice:" + err.Error()) + } + + aliceBalance, err := client.AccountAPTBalance(alice.Address) + if err != nil { + panic("Failed to retrieve alice balance:" + err.Error()) + } + bobBalance, err := client.AccountAPTBalance(bob.Address) + if err != nil { + panic("Failed to retrieve bob balance:" + err.Error()) + } + fmt.Printf("\n=== Initial Balances ===\n") + fmt.Printf("Alice: %d\n", aliceBalance) + fmt.Printf("Bob:%d\n", bobBalance) + + // 1. Build transaction + accountBytes, err := bcs.Serialize(&bob.Address) + if err != nil { + panic("Failed to serialize bob's address:" + err.Error()) + } + + amountBytes, err := bcs.SerializeU64(TransferAmount) + if err != nil { + panic("Failed to serialize transfer amount:" + err.Error()) + } + rawTxn, err := client.BuildTransaction(alice.AccountAddress(), aptos.TransactionPayload{ + Payload: &aptos.EntryFunction{ + Module: aptos.ModuleId{ + Address: aptos.AccountOne, + Name: "aptos_account", + }, + Function: "transfer", + ArgTypes: []aptos.TypeTag{}, + Args: [][]byte{ + accountBytes, + amountBytes, + }, + }}, + ) + + if err != nil { + panic("Failed to build transaction:" + err.Error()) + } + + // 2. Simulate transaction + // This is useful for understanding how much the transaction will cost + // and to ensure that the transaction is valid before sending it to the network + // This is optional, but recommended + simulationResult, err := client.SimulateTransaction(rawTxn, alice) + if err != nil { + panic("Failed to simulate transaction:" + err.Error()) + } + fmt.Printf("\n=== Simulation ===\n") + fmt.Printf("Gas unit price: %d\n", simulationResult[0].GasUnitPrice) + fmt.Printf("Gas used: %d\n", simulationResult[0].GasUsed) + fmt.Printf("Total gas fee: %d\n", simulationResult[0].GasUsed*simulationResult[0].GasUnitPrice) + fmt.Printf("Status: %s\n", simulationResult[0].VmStatus) +} + +func main() { + example(aptos.DevnetConfig) +} +``` + +{ /* TODO: Details on the simulation result */ } + + +Look [here](../building-transactions.mdx) to see the full example of how to build, simulate, and submit a transaction. + +You can also learn how to simulate more advanced transactions by looking at the following guides: + - [Sponsored Transactions](sponsoring-transactions.mdx) + - [Multi-Agent Transactions](multi-agent-transactions.mdx) diff --git a/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions/sponsoring-transactions.mdx b/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions/sponsoring-transactions.mdx new file mode 100644 index 000000000..66a186ff8 --- /dev/null +++ b/apps/nextra/pages/en/build/sdks/go-sdk/building-transactions/sponsoring-transactions.mdx @@ -0,0 +1,305 @@ +--- +title: "Go SDK - Sponsoring Transactions" +--- + +import { Callout, Steps } from 'nextra/components' + +# Sponsoring Transactions + +Normally, the account that is executing a transaction pays for the gas fees. You +can allow another account to cover those charges by sponsoring a transaction. + +This can be used to help manage fees from a central account when working with +complicated smart contracts. + +## How To Sponsor a Transaction + + +### Build the transaction with the parameter `FeePayer()`. + +```go filename="sponsor.go" + rawTxn, err := client.BuildTransactionMultiAgent( + alice.Address, + aptos.TransactionPayload{ + Payload: transferPayload, + }, + aptos.FeePayer(&sponsor.Address), + ) +``` + + + The `FeePayer()` function is used to specify the account that will pay the + gas fees for the transaction. You can use `AccountZero` to indicate that the + fee payer is not known ahead of time. + + +### Sign the transaction with BOTH the sender and the feePayer. + +1. Sign with the sender account using `rawTxn.Sign()`. +2. Sign with the sponsor account using `rawTxn.Sign()`. + +```go filename="sponsor.go" + aliceAuth, err := rawTxn.Sign(alice) + if err != nil { + panic("Failed to sign transaction as sender:" + err.Error()) + } + sponsorAuth, err := rawTxn.Sign(sponsor) + if err != nil { + panic("Failed to sign transaction as sponsor:" + err.Error()) + } +``` + {/* TODO: Support simulation for fee payer +### (Optional) When simulating the transaction, include the parameter `feePayerPublicKey: account.publicKey` + + +Currently, simulating a sponsor transaction must happen AFTER signing with the sponsor or it will fail to recognize this transaction has a sponsor. + + +```ts filename="sponsor.ts" +const [userTransactionResponse] = await aptos.transaction.simulate.simple({ + signerPublicKey: sender.publicKey, + feePayerPublicKey: feePayer.publicKey, + transaction, +}); +``` +*/} +### Submit the transaction by combining both signatures. + +```go filename="sponsor.go" + signedFeePayerTxn, ok = rawTxn.ToFeePayerSignedTransaction( + aliceAuth, + sponsorAuth, + []crypto.AccountAuthenticator{}, + ) + if !ok { + panic("Failed to build fee payer signed transaction") + } + + // Submit and wait for it to complete + submitResult, err = client.SubmitTransaction(signedFeePayerTxn) + if err != nil { + panic("Failed to submit transaction:" + err.Error()) + } +``` + +### Wait for the transaction to execute. + +```go filename="sponsor.go" + // Wait for the transaction + _, err = client.WaitForTransaction(txnHash) +``` + + +## Go Sponsored Transaction Code Sample + +```go filename="sponsor.go" +// sponsored_transaction is an example of how to make a sponsored transaction in Aptos. +package main + +import ( + "fmt" + + "github.com/aptos-labs/aptos-go-sdk" + "github.com/aptos-labs/aptos-go-sdk/crypto" +) + +const FundAmount = 100_000_000 +const TransferAmount = 1_000 + +// example This example shows you how to make an APT transfer transaction in the simplest possible way +func example(networkConfig aptos.NetworkConfig) { + // Create a client for Aptos + client, err := aptos.NewClient(networkConfig) + if err != nil { + panic("Failed to create client:" + err.Error()) + } + + // Create accounts locally for alice and bob + alice, err := aptos.NewEd25519Account() + if err != nil { + panic("Failed to create alice:" + err.Error()) + } + bob, err := aptos.NewEd25519Account() + if err != nil { + panic("Failed to create bob:" + err.Error()) + } + sponsor, err := aptos.NewEd25519Account() + if err != nil { + panic("Failed to create sponsor:" + err.Error()) + } + + fmt.Printf("\n=== Addresses ===\n") + fmt.Printf("Alice: %s\n", alice.Address.String()) + fmt.Printf("Bob:%s\n", bob.Address.String()) + fmt.Printf("Sponsor:%s\n", sponsor.Address.String()) + + // Fund the alice with the faucet to create it on-chain + err = client.Fund(alice.Address, FundAmount) + if err != nil { + panic("Failed to fund alice:" + err.Error()) + } + + // And the sponsor + err = client.Fund(sponsor.Address, FundAmount) + if err != nil { + panic("Failed to fund sponsor:" + err.Error()) + } + + aliceBalance, err := client.AccountAPTBalance(alice.Address) + if err != nil { + panic("Failed to retrieve alice balance:" + err.Error()) + } + bobBalance, err := client.AccountAPTBalance(bob.Address) + if err != nil { + panic("Failed to retrieve bob balance:" + err.Error()) + } + sponsorBalance, err := client.AccountAPTBalance(sponsor.Address) + if err != nil { + panic("Failed to retrieve sponsor balance:" + err.Error()) + } + fmt.Printf("\n=== Initial Balances ===\n") + fmt.Printf("Alice: %d\n", aliceBalance) + fmt.Printf("Bob: %d\n", bobBalance) + fmt.Printf("Sponsor: %d\n", sponsorBalance) + + // Build transaction + transferPayload, err := aptos.CoinTransferPayload(&aptos.AptosCoinTypeTag, bob.Address, TransferAmount) + if err != nil { + panic("Failed to build transfer payload:" + err.Error()) + } + rawTxn, err := client.BuildTransactionMultiAgent( + alice.Address, + aptos.TransactionPayload{ + Payload: transferPayload, + }, + aptos.FeePayer(&sponsor.Address), + + ) + if err != nil { + panic("Failed to build transaction:" + err.Error()) + } + + // Sign transaction + aliceAuth, err := rawTxn.Sign(alice) + if err != nil { + panic("Failed to sign transaction as sender:" + err.Error()) + } + sponsorAuth, err := rawTxn.Sign(sponsor) + if err != nil { + panic("Failed to sign transaction as sponsor:" + err.Error()) + } + + signedFeePayerTxn, ok := rawTxn.ToFeePayerSignedTransaction( + aliceAuth, + sponsorAuth, + []crypto.AccountAuthenticator{}, + ) + if !ok { + panic("Failed to build fee payer signed transaction") + } + + // Submit and wait for it to complete + submitResult, err := client.SubmitTransaction(signedFeePayerTxn) + if err != nil { + panic("Failed to submit transaction:" + err.Error()) + } + txnHash := submitResult.Hash + println("Submitted transaction hash:", txnHash) + + // Wait for the transaction + _, err = client.WaitForTransaction(txnHash) + if err != nil { + panic("Failed to wait for transaction:" + err.Error()) + } + aliceBalance, err = client.AccountAPTBalance(alice.Address) + if err != nil { + panic("Failed to retrieve alice balance:" + err.Error()) + } + bobBalance, err = client.AccountAPTBalance(bob.Address) + if err != nil { + panic("Failed to retrieve bob balance:" + err.Error()) + } + sponsorBalance, err = client.AccountAPTBalance(sponsor.Address) + if err != nil { + panic("Failed to retrieve sponsor balance:" + err.Error()) + } + fmt.Printf("\n=== Intermediate Balances ===\n") + fmt.Printf("Alice: %d\n", aliceBalance) + fmt.Printf("Bob: %d\n", bobBalance) + fmt.Printf("Sponsor: %d\n", sponsorBalance) + + fmt.Printf("\n=== Now do it without knowing the signer ahead of time ===\n") + + rawTxn, err = client.BuildTransactionMultiAgent( + alice.Address, + aptos.TransactionPayload{ + Payload: transferPayload, + }, + aptos.FeePayer(&aptos.AccountZero), // Note that the Address is 0x0, because we don't know the signer + ) + if err != nil { + panic("Failed to build transaction:" + err.Error()) + } + + // Alice signs the transaction, without knowing the sponsor + aliceAuth, err = rawTxn.Sign(alice) + if err != nil { + panic("Failed to sign transaction as sender:" + err.Error()) + } + + // The sponsor has to add themselves to the transaction to sign, note that this would likely be on a different + // server + ok = rawTxn.SetFeePayer(sponsor.Address) + if !ok { + panic("Failed to set fee payer") + } + + sponsorAuth, err = rawTxn.Sign(sponsor) + if err != nil { + panic("Failed to sign transaction as sponsor:" + err.Error()) + } + + signedFeePayerTxn, ok = rawTxn.ToFeePayerSignedTransaction( + aliceAuth, + sponsorAuth, + []crypto.AccountAuthenticator{}, + ) + if !ok { + panic("Failed to build fee payer signed transaction") + } + + // Submit and wait for it to complete + submitResult, err = client.SubmitTransaction(signedFeePayerTxn) + if err != nil { + panic("Failed to submit transaction:" + err.Error()) + } + txnHash = submitResult.Hash + println("Submitted transaction hash:", txnHash) + + // Wait for the transaction + _, err = client.WaitForTransaction(txnHash) + if err != nil { + panic("Failed to wait for transaction:" + err.Error()) + } + aliceBalance, err = client.AccountAPTBalance(alice.Address) + if err != nil { + panic("Failed to retrieve alice balance:" + err.Error()) + } + bobBalance, err = client.AccountAPTBalance(bob.Address) + if err != nil { + panic("Failed to retrieve bob balance:" + err.Error()) + } + sponsorBalance, err = client.AccountAPTBalance(sponsor.Address) + if err != nil { + panic("Failed to retrieve sponsor balance:" + err.Error()) + } + fmt.Printf("\n=== Final Balances ===\n") + fmt.Printf("Alice: %d\n", aliceBalance) + fmt.Printf("Bob: %d\n", bobBalance) + fmt.Printf("Sponsor: %d\n", sponsorBalance) +} + +func main() { + example(aptos.DevnetConfig) +} +``` diff --git a/apps/nextra/pages/en/build/sdks/go-sdk/fetch-data-via-sdk.mdx b/apps/nextra/pages/en/build/sdks/go-sdk/fetch-data-via-sdk.mdx new file mode 100644 index 000000000..cb53dfa5c --- /dev/null +++ b/apps/nextra/pages/en/build/sdks/go-sdk/fetch-data-via-sdk.mdx @@ -0,0 +1,105 @@ +--- +title: "Go SDK - Fetch Data" +--- + +import { Callout } from 'nextra/components' + +# Fetch Data via the Go SDK + +You can use the `Aptos` client to get on-chain data using a variety of helper +functions. Specifically, many of the functions listed in the [reference docs](https://pkg.go.dev/github.com/aptos-labs/aptos-go-sdk) +will retrieve data from on-chain e.g. `Account`, `AccountResources`, `Transactions`. + +Here’s an example showing how to fetch common data you may need in your application: + +```go filename="fetch_data.go" +client, err := aptos.NewClient(aptos.DevnetConfig) +if err != nil { + panic("Failed to create client:" + err.Error()) +} + +address := aptos.AccountAddress{} +err := address.ParseStringRelaxed("0x123") +if err != nil { + panic("Failed to parse address:" + err.Error()) +} + +accountInfo, err := client.Account(address) +resources, err := client.AccountResources(address) +transactions, err := client.Transactions() +``` + + + Many have optional inputs such as `ledgerVersion` to specify which ledger + version to query state. + + +The `Aptos` client can out of the box query both network data from +[fullnodes](https://api.mainnet.aptoslabs.com/v1/spec#/) and the +[Indexer](https://cloud.hasura.io/public/graphiql?endpoint=https://api.mainnet.aptoslabs.com/v1/graphql) +API which contains aggregated and enriched data. If you want to use a custom +query for Indexer API data, you can use `client.QueryIndexer()` like so: + +```go filename="fetch_data.go" + var out []CoinBalance + var q struct { + Current_coin_balances []struct { + CoinType string `graphql:"coin_type"` + Amount uint64 + OwnerAddress string `graphql:"owner_address"` + } `graphql:"current_coin_balances(where: {owner_address: {_eq: $address}})"` + } + + variables := map[string]any{ + "address": address.StringLong(), + } + err := ic.Query(&q, variables) + + if err != nil { + return nil, err + } + + for _, coin := range q.Current_coin_balances { + out = append(out, CoinBalance{ + CoinType: coin.CoinType, + Amount: coin.Amount, + }) + } +``` + + + Note that all values in the GraphQL must be capitalized and CamelCased. To + convert to direct database field names, use the `graphql` tag. + + +## Using Move View Functions + +You can call view functions which return custom data from on-chain by using `client.View`. + +For example, you can look up the network you are using with the `chain_id` view function: + +```go filename="fetch_data.go" +viewResponse, err := client.View(&aptos.ViewPayload { + Module: aptos.ModuleId{Address: aptos.AccountAddress{}, Name: "chain_id"}, + Function: "get", + ArgTypes: []aptos.TypeTag{}, + Args: [][]byte{}, +) + +chainId := viewResponse[0] +``` + +## Ensuring Fresh Indexer Data + +Behind the scenes, some requests use the [Indexer API](../../indexer.mdx) to +access data which has been processed or aggregated. That extra parsing can take +a bit of time, so the data may lag slightly behind the latest ledger. + +If you want to ensure that the data is fresh, you can wait on a specific version +from the indexer. + + +```go filename="fetch_data.go" +// Wait on processorName to reach version 12345 +err := client.WaitOnIndexer("processorName", 12345) +``` diff --git a/apps/nextra/pages/en/build/sdks/go-sdk/go-examples.mdx b/apps/nextra/pages/en/build/sdks/go-sdk/go-examples.mdx new file mode 100644 index 000000000..62fe56465 --- /dev/null +++ b/apps/nextra/pages/en/build/sdks/go-sdk/go-examples.mdx @@ -0,0 +1,52 @@ +--- +title: "Go SDK - Examples" +--- + +import { Steps } from 'nextra/components'; + +# Go SDK Example Code + +For sample code which explains the core concepts of how to use the SDK, see: + - [Fetching Data](fetch-data-via-sdk.mdx) + - [Building, Simulating, and Submitting Transactions](building-transactions.mdx) + +Below are additional resources which may be more suited for your individual use case. + +## Code Snippets + +The [`examples` folder](https://github.com/aptos-labs/aptos-go-sdk/tree/main/examples) +in the SDK repo has many code snippets you can customize to your needs. + +### How to run examples + +To run one of the example scripts: + + ### Clone the [aptos-go-sdk repository](https://github.com/aptos-labs/aptos-go-sdk) + + ```bash filename="Terminal" + git clone https://github.com/aptos-labs/aptos-go-sdk.git + ``` + + ### From the top-level of the package, install all dependencies. + + ```bash filename="Terminal" + go install + ``` + + ### Build the package. + + ```bash filename="Terminal" + go build ./... + ``` + + ### Run an example + + ```bash filename="Terminal" + go run examples/transfer_coin/main.go + ``` + + +## Helpful Reference Code + + - [SDK source code](https://github.com/aptos-labs/aptos-go-sdk/tree/main) - This has in-line comments explaining what each function does. + - [SDK reference docs](https://pkg.go.dev/github.com/aptos-labs/aptos-go-sdk) - These are another way to view the in-line documentation with built-in search. diff --git a/apps/nextra/utils/generated/localeMap.ts b/apps/nextra/utils/generated/localeMap.ts index ca9bbe852..bb02ae892 100644 --- a/apps/nextra/utils/generated/localeMap.ts +++ b/apps/nextra/utils/generated/localeMap.ts @@ -286,6 +286,33 @@ export const localeMap = { "/build/sdks/cpp-sdk": { "en": true }, + "/build/sdks/go-sdk/account": { + "en": true + }, + "/build/sdks/go-sdk/building-transactions/batching-transactions": { + "en": true + }, + "/build/sdks/go-sdk/building-transactions/bcs-format": { + "en": true + }, + "/build/sdks/go-sdk/building-transactions/multi-agent-transactions": { + "en": true + }, + "/build/sdks/go-sdk/building-transactions/simulating-transactions": { + "en": true + }, + "/build/sdks/go-sdk/building-transactions/sponsoring-transactions": { + "en": true + }, + "/build/sdks/go-sdk/building-transactions": { + "en": true + }, + "/build/sdks/go-sdk/fetch-data-via-sdk": { + "en": true + }, + "/build/sdks/go-sdk/go-examples": { + "en": true + }, "/build/sdks/go-sdk": { "en": true },