Deploy Contract with UDC
This guide demonstrates how to deploy a smart contract using the Universal Deployer Contract (UDC) on Starknet.
Prerequisites
- Go 1.18 or higher
- Starknet.go installed
- A Starknet node URL
- An account with sufficient ETH/STRK for deployment fees
- Contract class hash of the contract you want to deploy
Overview
This example deploys an ERC20 token using the UDC (Universal Deployer Contract) smart contract. The UDC is a standardized contract deployment mechanism on Starknet that enables deterministic contract addresses.
Learn more about the UDC in the Starknet documentation.
Steps
- Rename the ".env.template" file located at the root of the "examples" folder to ".env"
- Uncomment, and assign your Sepolia testnet endpoint to the
RPC_PROVIDER_URLvariable in the ".env" file - Uncomment, and assign your account address to the
ACCOUNT_ADDRESSvariable in the ".env" file (make sure to have a few ETH in it) - Uncomment, and assign your starknet public key to the
PUBLIC_KEYvariable in the ".env" file - Uncomment, and assign your private key to the
PRIVATE_KEYvariable in the ".env" file - Make sure you are in the "deployContractUDC" directory
- Execute
go run main.go
The transaction hash, status, and the deployed contract address will be returned at the end of the execution.
Code Example
main.go
package main
import (
"context"
"fmt"
"math/big"
"time"
"github.com/NethermindEth/juno/core/felt"
"github.com/NethermindEth/starknet.go/account"
setup "github.com/NethermindEth/starknet.go/examples/internal"
"github.com/NethermindEth/starknet.go/rpc"
"github.com/NethermindEth/starknet.go/utils"
)
// More info: https://docs.starknet.io/architecture-and-concepts/accounts/universal-deployer/
// NOTE : Please add in your keys only for testing purposes, in case of a leak you would potentially lose your funds.
var (
// This is the class hash of a modern (Sierra) OpenZeppelin ERC20 contract.
erc20ContractHash, _ = utils.HexToFelt(
"0x073d71c37e20c569186445d2c497d2195b4c0be9a255d72dbad86662fcc63ae6",
)
)
// Example successful transaction created from this example on Sepolia
// https://sepolia.starkscan.co/tx/0x6f70bc3756087f02fb3c281f7895520ba87c38152f87f43e0afa595d026469b
func main() {
fmt.Println("Starting deployContractUDC example")
// Load variables from '.env' file
rpcProviderURL := setup.GetRPCProviderURL()
accountAddress := setup.GetAccountAddress()
accountCairoVersion := setup.GetAccountCairoVersion()
privateKey := setup.GetPrivateKey()
publicKey := setup.GetPublicKey()
// Initialise connection to RPC provider
client, err := rpc.NewProvider(context.Background(), rpcProviderURL)
if err != nil {
panic(fmt.Sprintf("Error dialling the RPC provider: %s", err))
}
// Here we are converting the account address to felt
accountAddressInFelt, err := utils.HexToFelt(accountAddress)
if err != nil {
panic(err)
}
// Initialise the account memkeyStore (set public and private keys)
ks := account.NewMemKeystore()
privKeyBI, ok := new(big.Int).SetString(privateKey, 0)
if !ok {
panic("Failed to convert privKey to bigInt")
}
ks.Put(publicKey, privKeyBI)
fmt.Println("Established connection with the client")
// Initialise the account
accnt, err := account.NewAccount(
client,
accountAddressInFelt,
publicKey,
ks,
accountCairoVersion,
)
if err != nil {
panic(err)
}
// Build the constructor calldata for the ERC20 contract
// ref: https://docs.openzeppelin.com/contracts-cairo/1.0.0/api/erc20#ERC20Upgradeable-constructor
name, err := utils.StringToByteArrFelt("My Test Token")
if err != nil {
panic(err)
}
symbol, err := utils.StringToByteArrFelt("MTT")
if err != nil {
panic(err)
}
supply, err := utils.HexToU256Felt("0x200000000000000000")
if err != nil {
panic(err)
}
recipient := accnt.Address
owner := accnt.Address
constructorCalldata := make([]*felt.Felt, 0, 10)
constructorCalldata = append(constructorCalldata, name...)
constructorCalldata = append(constructorCalldata, symbol...)
constructorCalldata = append(constructorCalldata, supply...)
constructorCalldata = append(constructorCalldata, recipient, owner)
// Deploy the contract with UDC
resp, salt, err := accnt.DeployContractWithUDC(
context.Background(),
erc20ContractHash,
constructorCalldata,
nil,
nil,
)
if err != nil {
panic(err)
}
fmt.Println("Waiting for the transaction status...")
txReceipt, err := accnt.WaitForTransactionReceipt(context.Background(), resp.Hash, time.Second)
if err != nil {
panic(err)
}
// This returns us with the transaction hash and status
fmt.Printf("Transaction hash response: %v\n", resp.Hash)
fmt.Printf("Transaction execution status: %s\n", txReceipt.ExecutionStatus)
fmt.Printf("Transaction status: %s\n", txReceipt.FinalityStatus)
// Compute the contract address
contractAddress := utils.PrecomputeAddressForUDC(
erc20ContractHash,
salt,
constructorCalldata,
utils.UDCCairoV0,
accnt.Address,
)
fmt.Printf("Contract deployed address: %s\n", contractAddress)
}Explanation
- Initialize the RPC client: Connect to a Starknet RPC provider
- Set up account: Initialize your account with private key and keystore
- Prepare constructor calldata: Build the calldata for the ERC20 constructor (name, symbol, supply, recipient, owner)
- Deploy with UDC: Call
DeployContractWithUDCwith the class hash and constructor calldata - Wait for receipt: Wait for the transaction to be confirmed
- Compute address: Use
PrecomputeAddressForUDCto get the deployed contract address
Best Practices
- Always use environment variables for sensitive information (private keys, API keys)
- Store private keys securely and never commit them to version control
- Test on testnet (Sepolia) before deploying to mainnet
- Verify sufficient funds before deployment
- Use appropriate gas estimates with a safety multiplier
- Handle errors appropriately throughout the deployment process
Common Issues
- Insufficient funds: Ensure your account has enough ETH/STRK to cover deployment fees
- Invalid class hash: Verify the class hash exists and is declared on the network
- Constructor calldata mismatch: Ensure calldata matches the contract's constructor signature
- Network connectivity: Check your RPC endpoint is accessible and responsive
- Transaction timeout: Some deployments may take longer; adjust timeout values if needed
Related Examples
- Deploy Account - Learn how to deploy a new account
- Declare Contract - Learn how to declare a contract class
- Invoke Contract - Learn how to call contract functions

