Deploy Smart Contracts (Go)
This guide shows the core steps to deploy a ULVM smart contract using the ULedger Go SDK.
You will:
- Load a wallet (the signer)
- Read your compiled contract (
.wator.wasm) - Create a transaction session to a node
- Submit a
DEPLOY_SMART_CONTRACTtransaction
On many chains, your wallet must be registered on-chain (TX_CREATE_WALLET) before it can deploy contracts.
If you get REJECTED_BY_UNAUTHORIZED, register the wallet first.
1. Prerequisites
You need:
nodeEndpoint(example:http://localhost:8080)blockchainId- A wallet that can sign transactions
- Your contract source file:
auction.wat(text), orauction.wasm(binary — encoding depends on node rules)
2. Load a wallet (recommended)
Use the .ukey file from the wallet tutorial:
w, err := wallet.LoadFromFile("./wallets/my_wallet.ukey", "")
if err != nil {
return fmt.Errorf("load wallet: %w", err)
}
3. Read the contract source code
Option A: Deploy a .wat file (text)
watBytes, err := os.ReadFile("./auction.wat")
if err != nil {
return fmt.Errorf("read .wat file: %w", err)
}
contractSource := string(watBytes)
Option B: Deploy a .wasm file (binary)
Your node may expect raw bytes, hex, or base64. A common approach is base64:
wasmBytes, err := os.ReadFile("./auction.wasm")
if err != nil {
return fmt.Errorf("read .wasm file: %w", err)
}
contractSource := base64.StdEncoding.EncodeToString(wasmBytes)
Use the format your node expects for
DEPLOY_SMART_CONTRACT.
4. Pick a contract address (this goes in To)
In the Go SDK transaction input, To is required.
For deploy transactions, To is typically treated as the contract address (the place the contract “lives” on-chain).
Here’s a simple helper that generates a random 32-byte hex string:
func randomHex32() (string, error) {
b := make([]byte, 32)
if _, err := rand.Read(b); err != nil {
return "", err
}
return hex.EncodeToString(b), nil
}
contractAddress, err := randomHex32()
if err != nil {
return fmt.Errorf("generate contract address: %w", err)
}
5. Create a transaction session
This call fetches /health (to get nodeId) and checks /blockchains.
session, err := transaction.NewUL_TransactionSession(nodeEndpoint, w)
if err != nil {
return fmt.Errorf("create transaction session: %w", err)
}
6. Build the exact ULTransactionInput and deploy
Your Go struct is:
type ULTransactionInput struct {
BlockchainId string
To string
From string
Payload string
SenderSignature string
PayloadType string
Suggestor string
SenderTimestamp time.Time
PayloadRoot string
KeyType crypto.KeyType
}
For deploy, you only need to set these fields manually:
BlockchainIdTo(contract address)Payload(your contract source)PayloadType(must beDEPLOY_SMART_CONTRACT.String())
Everything else is filled by GenerateTransaction().
input := transaction.ULTransactionInput{
BlockchainId: blockchainId,
To: contractAddress,
Payload: contractSource,
PayloadType: transaction.DEPLOY_SMART_CONTRACT.String(),
}
Now submit:
tx, err := session.GenerateTransaction(input)
if err != nil {
return fmt.Errorf("deploy contract: %w", err)
}
if tx.TransactionId == "" {
return fmt.Errorf("deploy returned empty transaction id")
}
fmt.Println("Deploy TX ID:", tx.TransactionId)
fmt.Println("Status:", tx.Status)
fmt.Println("Output:", tx.Output)
fmt.Println("Contract Address (To):", contractAddress)
What GenerateTransaction() does for you (deploy case)
Based on your transaction_session.go:
- Sets
input.Suggestor = nodeId(from/health) - Sets
input.SenderTimestamp = now (UTC) - Sets
input.From = session.wallet.Address(because it’s notTX_CREATE_WALLET) - Sets
input.KeyType = session.wallet.GetKey().GetType() - Computes
input.PayloadRootusingGetUnboundCommitment(...)for deploy/upgrade/create/alter wallet - Signs commitment and fills
input.SenderSignature - POSTs to:
POST {nodeEndpoint}/blockchains/{blockchainId}/transactions
7. Next step: Invoke the contract
To call your deployed contract, you’ll submit an INVOKE_SMART_CONTRACT transaction.
Continue to: Invoke Smart Contracts (Go) (next guide).