AddDeployAccountTransaction
Submits a deploy account transaction (V3) to the Starknet network to create and deploy a new account contract. Deploy account transactions use counterfactual deployment, allowing the account address to be determined before deployment. The nonce is always 0 for deploy account transactions since the account doesn't exist yet.
Method Signature
func (provider *Provider) AddDeployAccountTransaction(
ctx context.Context,
deployAccountTransaction *BroadcastDeployAccountTxnV3,
) (AddDeployAccountTransactionResponse, error)Source: write.go
Parameters
ctx(context.Context): Context for request cancellation and timeoutdeployAccountTransaction(*BroadcastDeployAccountTxnV3): The V3 deploy account transaction containing class hash, constructor calldata, and salt
Returns
AddDeployAccountTransactionResponse: Response containing both the transaction hash and the deployed contract addresserror: Error if the submission fails
Type Definitions
- Input Type (
BroadcastDeployAccountTxnV3) contains the account class hash, constructor parameters, address salt, and resource bounds. - Result Type (
AddDeployAccountTransactionResponse) returns both the transaction hash for tracking and the deterministic contract address. - Address Derivation uses the class hash, constructor calldata, and salt to compute the account address before deployment.
The method deploys the account contract and initializes it with the constructor calldata in a single atomic transaction.
BroadcastDeployAccountTxnV3
type BroadcastDeployAccountTxnV3 = DeployAccountTxnV3Source: types_broadcast_transaction.go
DeployAccountTxnV3
type DeployAccountTxnV3 struct {
Type TransactionType `json:"type"`
Version TransactionVersion `json:"version"`
Signature []*felt.Felt `json:"signature"`
Nonce *felt.Felt `json:"nonce"`
ContractAddressSalt *felt.Felt `json:"contract_address_salt"`
ConstructorCalldata []*felt.Felt `json:"constructor_calldata"`
ClassHash *felt.Felt `json:"class_hash"`
ResourceBounds *ResourceBoundsMapping `json:"resource_bounds"`
Tip U64 `json:"tip"`
// The data needed to allow the paymaster to pay for the transaction in native tokens
PayMasterData []*felt.Felt `json:"paymaster_data"`
// The storage domain of the account's nonce (an account has a nonce per DA mode)
NonceDataMode DataAvailabilityMode `json:"nonce_data_availability_mode"`
// The storage domain of the account's balance from which fee will be charged
FeeMode DataAvailabilityMode `json:"fee_data_availability_mode"`
}Source: types_transaction.go
AddDeployAccountTransactionResponse
type AddDeployAccountTransactionResponse struct {
Hash *felt.Felt `json:"transaction_hash"`
ContractAddress *felt.Felt `json:"contract_address"`
}Source: types_transaction_response.go
ResourceBoundsMapping
type ResourceBoundsMapping struct {
// The max amount and max price per unit of L1 gas used in this tx
L1Gas ResourceBounds `json:"l1_gas"`
// The max amount and max price per unit of L1 blob gas used in this tx
L1DataGas ResourceBounds `json:"l1_data_gas"`
// The max amount and max price per unit of L2 gas used in this tx
L2Gas ResourceBounds `json:"l2_gas"`
}Source: types_transaction.go
Prerequisites
Before deploying an account, you need:
- A declared account contract class (use AddDeclareTransaction first)
- The account class hash from the declaration
- A private/public key pair for the account
- A salt value for deterministic address generation
- The precomputed account address (from
account.PrecomputeAccountAddress) - STRK tokens in the precomputed account address to pay for deployment fees
Usage Example
package main
import (
"context"
"fmt"
"log"
"math/big"
"os"
"time"
"github.com/NethermindEth/juno/core/felt"
"github.com/NethermindEth/starknet.go/account"
"github.com/NethermindEth/starknet.go/rpc"
"github.com/NethermindEth/starknet.go/utils"
"github.com/joho/godotenv"
)
func main() {
// Load environment variables
if err := godotenv.Load(); err != nil {
log.Fatal("Error loading .env file:", err)
}
rpcURL := os.Getenv("STARKNET_RPC_URL")
privateKeyStr := os.Getenv("ACCOUNT_PRIVATE_KEY")
publicKeyStr := os.Getenv("ACCOUNT_PUBLIC_KEY")
classHashStr := os.Getenv("ACCOUNT_CLASS_HASH")
saltStr := os.Getenv("ACCOUNT_SALT")
addressStr := os.Getenv("ACCOUNT_ADDRESS")
if rpcURL == "" || privateKeyStr == "" || publicKeyStr == "" || classHashStr == "" || saltStr == "" || addressStr == "" {
log.Fatal("Missing required environment variables")
}
// Parse credentials
privateKey, err := new(felt.Felt).SetString(privateKeyStr)
if err != nil {
log.Fatal("Invalid private key:", err)
}
publicKey, err := new(felt.Felt).SetString(publicKeyStr)
if err != nil {
log.Fatal("Invalid public key:", err)
}
classHash, err := new(felt.Felt).SetString(classHashStr)
if err != nil {
log.Fatal("Invalid class hash:", err)
}
salt, err := new(felt.Felt).SetString(saltStr)
if err != nil {
log.Fatal("Invalid salt:", err)
}
address, err := new(felt.Felt).SetString(addressStr)
if err != nil {
log.Fatal("Invalid address:", err)
}
fmt.Printf("Account Address: %s\n", address.String())
fmt.Printf("RPC URL: %s\n\n", rpcURL)
// Connect to RPC provider
ctx := context.Background()
client, err := rpc.NewProvider(ctx, rpcURL)
if err != nil {
log.Fatal("Failed to connect to RPC provider:", err)
}
// Check if account is already deployed
fmt.Println("Checking if account is already deployed...")
classHashResponse, err := client.ClassHashAt(ctx, rpc.BlockID{Tag: "latest"}, address)
if err == nil && classHashResponse != nil {
fmt.Println("Account is already deployed!")
fmt.Printf("Class Hash: %s\n", classHashResponse.String())
return
}
// Check STRK balance
fmt.Println("Checking STRK balance...")
strkBalance, err := getStrkBalance(ctx, client, address)
if err != nil {
log.Printf("Warning: Could not check STRK balance: %v\n", err)
} else {
// Convert to STRK (18 decimals)
strkAmount := new(big.Float).Quo(
new(big.Float).SetInt(strkBalance),
new(big.Float).SetFloat64(1e18),
)
fmt.Printf("STRK Balance: %s STRK\n", strkAmount.Text('f', 4))
if strkBalance.Cmp(big.NewInt(0)) == 0 {
fmt.Println("\nWARNING: Account has 0 STRK balance!")
fmt.Println("Please fund your account first:")
fmt.Printf("Address: %s\n", address.String())
fmt.Println("Faucet: https://starknet-faucet.vercel.app/")
return
}
}
// Get chain ID
chainID, err := client.ChainID(ctx)
if err != nil {
log.Fatal("Failed to get chain ID:", err)
}
fmt.Printf("Chain ID: %s\n\n", chainID)
// Create account manager (undeployed)
ks := account.NewMemKeystore()
ks.Put(publicKey.String(), privateKey.BigInt(new(big.Int)))
// Constructor calldata for OZ account is just the public key
constructorCalldata := []*felt.Felt{publicKey}
// Prepare deploy account transaction
fmt.Println("Deploying account...")
// Create account controller
accnt, err := account.NewAccount(client, address, publicKey.String(), ks, 2)
if err != nil {
log.Fatal("Failed to create account controller:", err)
}
// Build and estimate deploy account transaction (V3 with STRK fees)
deployTx, precomputedAddr, err := accnt.BuildAndEstimateDeployAccountTxn(
ctx,
salt,
classHash,
constructorCalldata,
nil,
)
if err != nil {
log.Fatal("Failed to build deploy account transaction:", err)
}
if precomputedAddr.String() != address.String() {
log.Fatalf("Address mismatch! Expected %s, got %s", address.String(), precomputedAddr.String())
}
fmt.Printf("Estimated gas: L1Gas=%d, L2Gas=%d\n",
deployTx.ResourceBounds.L1Gas.MaxAmount,
deployTx.ResourceBounds.L2Gas.MaxAmount)
// Sign the transaction
err = accnt.SignDeployAccountTransaction(ctx, &rpc.DeployAccountTxnV3{
Type: deployTx.Type,
ClassHash: deployTx.ClassHash,
ContractAddressSalt: deployTx.ContractAddressSalt,
ConstructorCalldata: deployTx.ConstructorCalldata,
Version: deployTx.Version,
Signature: deployTx.Signature,
Nonce: deployTx.Nonce,
ResourceBounds: deployTx.ResourceBounds,
Tip: deployTx.Tip,
PayMasterData: deployTx.PayMasterData,
NonceDataMode: deployTx.NonceDataMode,
FeeMode: deployTx.FeeMode,
}, address)
if err != nil {
log.Fatal("Failed to sign transaction:", err)
}
// Send the transaction
resp, err := client.AddDeployAccountTransaction(ctx, deployTx)
if err != nil {
log.Fatal("Failed to deploy account:", err)
}
fmt.Printf("Deploy transaction sent!\n")
fmt.Printf("Transaction Hash: %s\n", resp.Hash.String())
// Wait for transaction confirmation
fmt.Println("\nWaiting for transaction confirmation...")
receipt, err := waitForTransaction(ctx, client, resp.Hash)
if err != nil {
log.Fatal("Transaction failed:", err)
}
fmt.Printf("Account deployed successfully!\n")
fmt.Printf("Block Number: %d\n", receipt.BlockNumber)
fmt.Printf("Status: %s\n\n", receipt.FinalityStatus)
fmt.Println("Your account is now ready to use!")
fmt.Printf("Address: %s\n", address.String())
}
func getStrkBalance(ctx context.Context, client *rpc.Provider, address *felt.Felt) (*big.Int, error) {
// STRK contract address on Sepolia
strkContract, _ := new(felt.Felt).SetString("0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d")
// Call balanceOf
result, err := client.Call(ctx, rpc.FunctionCall{
ContractAddress: strkContract,
EntryPointSelector: utils.GetSelectorFromNameFelt("balanceOf"),
Calldata: []*felt.Felt{address},
}, rpc.BlockID{Tag: "latest"})
if err != nil {
return nil, err
}
if len(result) == 0 {
return big.NewInt(0), nil
}
// balanceOf returns Uint256 (low, high)
if len(result) >= 2 {
low := result[0].BigInt(new(big.Int))
high := result[1].BigInt(new(big.Int))
// balance = low + high * 2^128
balance := new(big.Int).Lsh(high, 128)
balance.Add(balance, low)
return balance, nil
}
return result[0].BigInt(new(big.Int)), nil
}
// waitForTransaction waits for a transaction to be confirmed on the network
func waitForTransaction(ctx context.Context, client *rpc.Provider, txHash *felt.Felt) (*rpc.TransactionReceiptWithBlockInfo, error) {
for i := 0; i < 60; i++ {
receipt, err := client.TransactionReceipt(ctx, txHash)
if err == nil {
if receipt.FinalityStatus == rpc.TxnFinalityStatusAcceptedOnL2 ||
receipt.FinalityStatus == rpc.TxnFinalityStatusAcceptedOnL1 {
return receipt, nil
}
}
time.Sleep(5 * time.Second)
fmt.Print(".")
}
return nil, fmt.Errorf("transaction confirmation timeout")
}Error Handling
result, err := provider.AddDeployAccountTransaction(ctx, &deployTx)
if err != nil {
switch {
case errors.Is(err, rpc.ErrClassHashNotFound):
log.Printf("Account class not declared - declare it first")
return
case errors.Is(err, rpc.ErrInsufficientAccountBalance):
log.Printf("Insufficient balance - fund the account address before deployment")
return
case errors.Is(err, rpc.ErrInvalidTransactionNonce):
log.Printf("Invalid nonce - must be 0 for deploy account transactions")
return
case errors.Is(err, rpc.ErrValidationFailure):
log.Printf("Transaction validation failed - check signature and parameters")
return
case errors.Is(err, rpc.ErrFeeBelowMinimum):
log.Printf("Resource bounds too low for current network conditions")
return
default:
log.Printf("Failed to deploy account: %v", err)
return
}
}
fmt.Printf("Contract Address: %s\n", result.ContractAddress)Common Use Cases
- Creating new user accounts on Starknet with counterfactual address derivation
- Deploying smart wallet contracts with custom validation logic and features
- Setting up multi-signature accounts by deploying multisig account contracts
- Initializing account contracts with specific public keys or guardian configurations
Related Methods
- ClassHashAt - Verify the deployed account's class hash after deployment
- Nonce - Get account nonce after deployment (should be 1)
- EstimateFee - Estimate deployment fees before submission
- TransactionReceipt - Check deployment status after submission
- SimulateTransactions - Test deployment before submitting to network

