<!-- tempo-docs-context -->
> Source: [/docs/sdk/go](https://tempo.xyz/developers/docs/sdk/go.md)
> Section: [Get Started](https://tempo.xyz/developers/get-started.md). Choose a starting point, send a test payment, get testnet funds, connect a coding agent, and find the SDKs and CLI.
> [Documentation index](https://tempo.xyz/developers/llms.txt) · [Docs skill](https://tempo.xyz/developers/SKILL.md)

> Tempo MCP: Use `search`, `find_pages`, `read_page`, and `code` at `https://mcp.tempo.xyz` for Tempo and related documentation.
> If an indexed page is missing or reflects an older site, read this site’s [documentation index](https://tempo.xyz/developers/llms.txt) and Markdown pages directly.

# Tempo Go SDK: installing and using it

Tempo distributes a Go SDK for building application clients. The SDK provides packages for RPC communication, transaction signing, and key management.

The Tempo Go SDK can be used to perform common operations with the chain, such as: sending Tempo Transactions, batching multiple calls, fee sponsorship, and more.

::::steps
## Install the Go SDK

To install the Tempo Go SDK:

```bash [go]
go get github.com/tempoxyz/tempo-go@v0.5.0
```

:::tip
The SDK requires Go 1.25.9 or higher.
:::

## Create a Go RPC client

To interact with Tempo, first create an RPC client connected to a Tempo node:

```go [main.go]
package main

import (
    "context"
    "fmt"

    "github.com/tempoxyz/tempo-go/pkg/client"
)

func main() {
    c := client.New("https://rpc.moderato.tempo.xyz")

    ctx := context.Background()
    blockNum, _ := c.GetBlockNumber(ctx)
    fmt.Printf("Connected to Tempo at block %d\n", blockNum)
}
```

For authenticated RPC endpoints:

```go [main.go]
c := client.New("https://rpc.moderato.tempo.xyz",
    client.WithAuth("username", "password"),
)
```

## Create a Go transaction signer

Create a signer to sign transactions. The signer manages your private key and generates signatures:

```go [main.go]
package main

import (
    "fmt"

    "github.com/tempoxyz/tempo-go/pkg/signer"
)

func main() {
    s, err := signer.NewSigner("0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80")
    if err != nil {
        panic(err)
    }

    fmt.Printf("Address: %s\n", s.Address().Hex())
}
```

## Send a Tempo transaction with Go

These examples use Moderato testnet (`42431`). Fund a test account with the [faucet](https://tempo.xyz/developers/docs/quickstart/faucet) and use its private key. The displayed Anvil key is public and must not hold real funds. Estimate gas for your actual calls; the limits below are example caps.

Build and send a transaction using the builder pattern:

```go [main.go]
package main

import (
    "context"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/tempoxyz/tempo-go/pkg/client"
    "github.com/tempoxyz/tempo-go/pkg/signer"
    "github.com/tempoxyz/tempo-go/pkg/transaction"
)

func main() {
    c := client.New("https://rpc.moderato.tempo.xyz")
    s, _ := signer.NewSigner("0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80")

    ctx := context.Background()
    nonce, _ := c.GetTransactionCount(ctx, s.Address().Hex()) // [!code hl]

    recipient := common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8")

    // [!code hl:10]
    tx := transaction.NewBuilder(big.NewInt(42431)). // Moderato testnet
        SetNonce(nonce).
        SetGas(100000).
        SetMaxFeePerGas(big.NewInt(20000000000)). // Example maximum fee per gas
        SetMaxPriorityFeePerGas(big.NewInt(1000000000)).
        AddCall(recipient, big.NewInt(0), []byte{}).
        Build()

    transaction.SignTransaction(tx, s)
    serialized, _ := transaction.Serialize(tx, nil)
    hash, _ := c.SendRawTransaction(ctx, serialized) // [!code hl]
    log.Printf("Transaction hash: %s", hash)
}
```
::::

## Go SDK examples

### Read chain data with Go

Query the blockchain for basic information:

```go [read.go]
ctx := context.Background()

blockNum, _ := c.GetBlockNumber(ctx)
chainID, _ := c.GetChainID(ctx)
nonce, _ := c.GetTransactionCount(ctx, "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb")

fmt.Printf("Block: %d, Chain: %d, Nonce: %d\n", blockNum, chainID, nonce)
```

### Send a token transfer with Go

Send a TIP-20 token transfer using go-ethereum's ABI encoding:

```go [transfer.go]
import (
    "strings"
    "github.com/ethereum/go-ethereum/accounts/abi"
)

erc20ABI, _ := abi.JSON(strings.NewReader(`[{"name":"transfer","type":"function","inputs":[{"name":"to","type":"address"},{"name":"amount","type":"uint256"}]}]`))

recipient := common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8")
amount := big.NewInt(100_000_000) // 100 tokens (6 decimals)

transferData, _ := erc20ABI.Pack("transfer", recipient, amount)

tx := transaction.NewBuilder(big.NewInt(42431)).
    SetNonce(nonce).
    SetGas(100000).
    SetMaxFeePerGas(big.NewInt(10000000000)).
    SetMaxPriorityFeePerGas(big.NewInt(1000000000)).
    AddCall(transaction.AlphaUSDAddress, big.NewInt(0), transferData).
    Build()
```

### Send a memo transfer with Go

Include a memo for payment reconciliation:

```go [memo.go]
tip20ABI, _ := abi.JSON(strings.NewReader(`[{"name":"transferWithMemo","type":"function","inputs":[{"name":"to","type":"address"},{"name":"amount","type":"uint256"},{"name":"memo","type":"bytes32"}]}]`))

recipient := common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8")
amount := big.NewInt(100_000_000)
memo := [32]byte{}
copy(memo[:], "INV-12345")

memoData, _ := tip20ABI.Pack("transferWithMemo", recipient, amount, memo)

tx := transaction.NewBuilder(big.NewInt(42431)).
    SetNonce(nonce).
    SetGas(100000).
    SetMaxFeePerGas(big.NewInt(10000000000)).
    SetMaxPriorityFeePerGas(big.NewInt(1000000000)).
    AddCall(transaction.AlphaUSDAddress, big.NewInt(0), memoData).
    Build()
```

### Batch multiple calls with Go

Execute multiple operations atomically in a single transaction:

```go [batch.go]
tx := transaction.NewBuilder(big.NewInt(42431)).
    SetNonce(nonce).
    SetGas(200000).
    SetMaxFeePerGas(big.NewInt(10000000000)).
    SetMaxPriorityFeePerGas(big.NewInt(1000000000)).
    AddCall(addr1, big.NewInt(0), transfer1Data). // [!code hl]
    AddCall(addr2, big.NewInt(0), transfer2Data). // [!code hl]
    AddCall(addr3, big.NewInt(0), contractCallData). // [!code hl]
    Build()

transaction.SignTransaction(tx, s)
```

### Parallel Transactions (2D Nonces)

Send multiple transactions concurrently using different nonce keys:

```go [parallel.go]
tx1 := transaction.NewBuilder(big.NewInt(42431)).
    SetNonceKey(big.NewInt(1)). // Sequence A // [!code hl]
    SetNonce(0).
    SetGas(100000).
    SetMaxFeePerGas(big.NewInt(10000000000)).
    SetMaxPriorityFeePerGas(big.NewInt(1000000000)).
    AddCall(recipient1, big.NewInt(0), data1).
    Build()

tx2 := transaction.NewBuilder(big.NewInt(42431)).
    SetNonceKey(big.NewInt(2)). // Sequence B (parallel) // [!code hl]
    SetNonce(0).
    SetGas(100000).
    SetMaxFeePerGas(big.NewInt(10000000000)).
    SetMaxPriorityFeePerGas(big.NewInt(1000000000)).
    AddCall(recipient2, big.NewInt(0), data2).
    Build()

transaction.SignTransaction(tx1, s)
transaction.SignTransaction(tx2, s)

// Submit both and wait for each submission result.
results := make(chan error, 2)
for _, tx := range []*transaction.Tx{tx1, tx2} {
    raw, err := transaction.Serialize(tx, nil)
    if err != nil { panic(err) }
    go func(raw string) {
        _, err := c.SendRawTransaction(ctx, raw)
        results <- err
    }(raw)
}
for range 2 {
    if err := <-results; err != nil { panic(err) }
}
```

### Fee sponsorship with Go

Have another account pay for transaction fees:

```go [feepayer.go]
tx := transaction.NewBuilder(big.NewInt(42431)).
    SetNonce(nonce).
    SetGas(100000).
    SetMaxFeePerGas(big.NewInt(10000000000)).
    SetMaxPriorityFeePerGas(big.NewInt(1000000000)).
    SetSponsored(true). // Mark as awaiting fee payer // [!code hl]
    AddCall(recipient, big.NewInt(0), data).
    Build()

transaction.SignTransaction(tx, userSigner)
transaction.AddFeePayerSignature(tx, feePayerSigner)
```

### Transaction validity windows in Go

Set a time window during which the transaction is valid:

```go [validity.go]
now := time.Now()

tx := transaction.NewBuilder(big.NewInt(42431)).
    SetNonce(nonce).
    SetGas(100000).
    SetMaxFeePerGas(big.NewInt(10000000000)).
    SetMaxPriorityFeePerGas(big.NewInt(1000000000)).
    SetValidAfter(uint64(now.Unix())). // [!code hl]
    SetValidBefore(uint64(now.Add(1 * time.Hour).Unix())). // [!code hl]
    AddCall(recipient, big.NewInt(0), data).
    Build()
```

### Batch RPC requests with Go

Send multiple RPC calls efficiently in a single HTTP request. `eth_getBalance` returns a compatibility value on Tempo, not the account’s spendable stablecoins; read TIP-20 `balanceOf` for token balances:

```go [batch_rpc.go]
batch := client.NewBatchRequest()
batch.Add("eth_blockNumber").
    Add("eth_chainId").
    Add("eth_getBalance", "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb", "latest")

responses, _ := c.SendBatch(ctx, batch)
for _, resp := range responses {
    fmt.Printf("Result: %v\n", resp.Result)
}
```

## Account Keychain in the Go SDK

For a non-expiring key, pass `^uint64(0)` (the maximum uint64 timestamp) to `NewKeyRestrictions`; `0` is already expired. Keychain helpers return unsigned calls: choose one, include it in a transaction, sign, submit, and wait for its receipt.

The `keychain` package provides typed helpers for Tempo's [Account Keychain precompile](https://tempo.xyz/developers/docs/protocol/transactions/AccountKeychain), enabling access key management and signing directly from Go.

:::info
Enhanced access key features — periodic spending limits and call scoping — require the [T3 network upgrade](https://tempo.xyz/developers/docs/protocol/upgrades/t3).
:::

```go [keychain_manage.go]
package main

import (
	"context"
	"math/big"
	"time"

	"github.com/ethereum/go-ethereum/common"
	"github.com/tempoxyz/tempo-go/pkg/client"
	"github.com/tempoxyz/tempo-go/pkg/keychain"
	"github.com/tempoxyz/tempo-go/pkg/signer"
	"github.com/tempoxyz/tempo-go/pkg/transaction"
)

func main() {
	c := client.New("https://rpc.moderato.tempo.xyz")
	s, _ := signer.NewSigner("0xYOUR_PRIVATE_KEY")

	ctx := context.Background()
	nonce, _ := c.GetTransactionCount(ctx, s.Address().Hex())

	accessKeyAddr := common.HexToAddress("<ACCESS_KEY_ADDRESS>")

	// Authorize a new access key (secp256k1, no expiry):
	restrictions := keychain.NewKeyRestrictions(^uint64(0))
	call, _ := keychain.AuthorizeKey(accessKeyAddr, keychain.SignatureTypeSecp256k1, restrictions)

	tx := transaction.NewBuilder(big.NewInt(42431)).
		SetNonce(nonce).
		SetGas(200000).
		SetMaxFeePerGas(big.NewInt(20000000000)).
		SetMaxPriorityFeePerGas(big.NewInt(1000000000)).
		AddCall(call.To, big.NewInt(0), call.Data).
		Build()

	// Authorize with a spending limit:
	token := common.HexToAddress("<TOKEN_ADDRESS>")
	restrictions = keychain.NewKeyRestrictions(^uint64(0)).
		WithLimits([]keychain.TokenLimit{{Token: token, Amount: big.NewInt(1_000_000)}})
	call, _ = keychain.AuthorizeKey(accessKeyAddr, keychain.SignatureTypeSecp256k1, restrictions)

	// Authorize with call scopes (restrict to specific contracts/functions):
	scope := keychain.NewCallScopeBuilder(token).
		Transfer(nil).
		Approve(nil).
		Build()
	restrictions = keychain.NewKeyRestrictions(^uint64(0)).
		WithAllowedCalls([]keychain.CallScope{scope})
	call, _ = keychain.AuthorizeKey(accessKeyAddr, keychain.SignatureTypeSecp256k1, restrictions)

	// Full example: 24h expiry + spending limit + call scope:
	expiry := uint64(time.Now().Add(24 * time.Hour).Unix())
	restrictions = keychain.NewKeyRestrictions(expiry).
		WithLimits([]keychain.TokenLimit{{Token: token, Amount: big.NewInt(1_000_000)}}).
		WithAllowedCalls([]keychain.CallScope{
			keychain.NewCallScopeBuilder(token).Transfer(nil).Build(),
		})
	call, _ = keychain.AuthorizeKey(accessKeyAddr, keychain.SignatureTypeSecp256k1, restrictions)

	// Revoke an access key (permanent, cannot be re-authorized):
	call, _ = keychain.RevokeKey(accessKeyAddr)

	// Update spending limit for a key-token pair:
	call, _ = keychain.UpdateSpendingLimit(accessKeyAddr, token, big.NewInt(2_000_000))

	// Replace all call scopes for a key:
	call, _ = keychain.SetAllowedCalls(accessKeyAddr, []keychain.CallScope{
		keychain.NewCallScopeBuilder(token).Transfer(nil).Build(),
	})

	// Remove a target contract from allowed call list:
	call, _ = keychain.RemoveAllowedCalls(accessKeyAddr, token)

	_ = ctx
	_ = tx
	_ = call
}
```

### Sign with an access key in Go

Use `keychain.SignWithAccessKey` to sign a transaction as an access key holder:

```go [access_key_sign.go]
accessKeySigner, _ := signer.NewSigner("<ACCESS_KEY_PRIVATE_KEY>")
rootAccount := common.HexToAddress("<ROOT_ACCOUNT_ADDRESS>")

tx := transaction.NewBuilder(big.NewInt(42431)).
	SetNonce(nonce).
	SetGas(100000).
	SetMaxFeePerGas(big.NewInt(20000000000)).
	SetMaxPriorityFeePerGas(big.NewInt(1000000000)).
	AddCall(recipient, big.NewInt(0), data).
	Build()

keychain.SignWithAccessKey(tx, accessKeySigner, rootAccount) // [!code hl]
serialized, _ := transaction.Serialize(tx, nil)
hash, _ := c.SendRawTransaction(ctx, serialized)
```

### Query remaining spending limits with Go

```go [query_limit.go]
calldata := keychain.EncodeGetRemainingLimitCalldata(
	common.HexToAddress("<ACCOUNT_ADDRESS>"),
	common.HexToAddress("<ACCESS_KEY_ADDRESS>"),
	common.HexToAddress("<TOKEN_ADDRESS>"),
)
response, err := c.SendRequest(ctx, "eth_call", map[string]string{
    "to": keychain.GetKeychainAddress().Hex(), "data": calldata,
}, "latest")
if err != nil { panic(err) }
if err := response.CheckError(); err != nil { panic(err) }
result, ok := response.Result.(string)
if !ok { panic("Expected a hex RPC result") }
remaining := keychain.ParseRemainingLimitResult(common.FromHex(result))
fmt.Printf("Remaining: %s\n", remaining.String())
```

## Go SDK packages

| Package | Description |
| --- | --- |
| `transaction` | TempoTransaction encoding, signing, and validation |
| `client` | RPC client for interacting with Tempo nodes |
| `signer` | Key management and signature generation |
| `keychain` | Account Keychain precompile: access key management and signing |

## Next steps for the Go SDK

After setting up the Go SDK, you can:

* Follow a guide on how to [make payments](https://tempo.xyz/developers/docs/guide/payments), [issue stablecoins](https://tempo.xyz/developers/docs/guide/issuance), [exchange stablecoins](https://tempo.xyz/developers/docs/guide/stablecoin-dex), and [more](https://tempo.xyz/developers).
* View the [examples on GitHub](https://github.com/tempoxyz/tempo-go/tree/main/examples)
