Deploy Smart Contracts (Go)
This guide shows the core steps to deploy a compiled ULVM smart contract using the ULedger Go SDK.
You will:
- Load a wallet (the signer)
- Read your compiled contract (
.wator.wasm) - Create a
UL_TransactionSessionto a node - Submit a
DEPLOY_SMART_CONTRACTtransaction
Before deploying contracts on most networks, your wallet must be registered on-chain (TX_CREATE_WALLET).
If deployment fails with authorization errors, register your wallet first.
1. Prerequisites
You need:
- A node endpoint (you can use a testnet endpoint:
https://tn-w-3.uledger.net/) - A
blockchainId - A wallet you can sign with
- A compiled contract file:
contract.wat(text), orcontract.wasm(binary)
2. Load a wallet (recommended approach)
Recommended: load from a .ukey file created by the SDK wallet flow.
w, err := wallet.LoadFromFile("./wallets/my_wallet.ukey", "")
if err != nil {
return fmt.Errorf("load wallet: %w", err)
}
Avoid hardcoding private keys in examples. Use
.ukeyfiles or environment variables.
3. Read the contract file
If you deploy .wat (text)
watBytes, err := os.ReadFile("./auction.wat")
if err != nil {
return fmt.Errorf("read contract file: %w", err)
}
sourceCode := string(watBytes)
If you deploy .wasm (binary)
Most transaction payloads are strings, so a common approach is base64:
wasmBytes, err := os.ReadFile("./auction.wasm")
if err != nil {
return fmt.Errorf("read contract file: %w", err)
}
sourceCode := base64.StdEncoding.EncodeToString(wasmBytes)
Whether your network expects WAT text or base64 WASM depends on node configuration.
4. Choose a contract address
Many chains treat the contract address as an explicit value (passed as To).
A simple, safe default is 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
session, err := transaction.NewUL_TransactionSession(nodeEndpoint, w)
if err != nil {
return fmt.Errorf("create session: %w", err)
}
NewUL_TransactionSession typically performs initial node calls (like /health) and prepares the session to sign and submit transactions.
6. Build and submit the deploy transaction
This is the “core logic”:
input := transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: w.Address, // signer wallet address
To: contractAddress, // where the contract will live
Payload: sourceCode, // WAT text or base64 WASM
PayloadType: transaction.DEPLOY_SMART_CONTRACT.String(),
}
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:", tx.TransactionId)
fmt.Println("Status:", tx.Status)
fmt.Println("Output:", tx.Output)
fmt.Println("Contract address:", contractAddress)
What to expect back
tx.TransactionId= transaction identifiertx.Status=SUBMITTED,ACCEPTED, orREJECTEDtx.Output=SUCCESSor a rejection reason
7. Next step: Invoke the contract
Once deployed, you can call the contract with an INVOKE_SMART_CONTRACT transaction.
Continue to: **Invoke Smart Contracts (Go).