Building Transactions (Go)
This guide walks the full life of a transaction: describe it in TxPlan YAML, build it offline, sign it with the right keys, and submit it with your own HTTP client. The YAML shapes for every intent — staking, governance, pools, minting, Plutus — are cataloged in the TxPlan reference; this page shows how to drive them from Go.
The workflow
Section titled “The workflow”Every transaction follows the same four steps:
bridge, err := ccl.New()if err != nil { log.Fatal(err) }defer bridge.Close()
provider := ccl.NewYaciProvider("") // or a BlockfrostProvider, or your own
// 1. Describe — TxPlan YAML (see the intent catalog)yaml := fmt.Sprintf(`version: 1.0transaction: - tx: from: %s intents: - type: payment address: %s amounts: - unit: lovelace quantity: "5000000"`, sender, receiver)
// 2. Build — offline; UTXO selection, fee, and change happen in the native libresult, err := bridge.QuickTx.BuildWith(yaml, provider, sender, 0)// (or bridge.QuickTx.Build(yaml, utxos, protocolParams, additionalSigners) with your own chain data)
// 3. Sign — with the key roles the transaction's certificates requiresigned, err := bridge.Account.SignTx(mnemonic, ccl.Testnet, 0, 0, result.TxCbor)
// 4. Submit — any Blockfrost-compatible endpoint; the library never submitstxBytes, _ := hex.DecodeString(signed)resp, err := http.Post(submitURL+"/tx/submit", "application/cbor", bytes.NewReader(txBytes))Which keys sign what
Section titled “Which keys sign what”SignTx witnesses with the payment key only. Certificates need their own witness — use SignTxWithKeys with roles in order:
| Transaction contains | Roles |
|---|---|
| Payments, metadata, minting, Plutus operations | "payment" (or plain SignTx) |
stake_registration / stake_deregistration / stake_delegation / stake_withdrawal / voting_delegation | "payment", "stake" |
drep_registration / drep_update / drep_deregistration / voting | "payment", "drep" |
governance_proposal | "payment" |
pool_registration / pool_update / pool_retirement | "payment", "stake" when the pool is keyed to the account’s stake key |
A missing witness is rejected by the node with MissingVKeyWitnessesUTXOW.
The same table gives the fee’s witness budget: pass additional_signers = len(keys) - 1 to the build (the input UTXOs already cover the payment key). For a native-script spend whose only inputs sit at the script address, pass the number of the script’s sig keys instead.
Worked example: register and delegate stake
Section titled “Worked example: register and delegate stake”Two transactions — the registration must be on-chain before the delegation:
stakeYaml := fmt.Sprintf(`version: 1.0transaction: - tx: from: %s intents: - type: stake_registration stake_address: %s`, sender, account.StakeAddress)
reg, err := bridge.QuickTx.BuildWith(stakeYaml, provider, sender, 1)signedReg, err := bridge.Account.SignTxWithKeys(mnemonic, ccl.Testnet, 0, 0, reg.TxCbor, "payment", "stake")// submit signedReg; wait for inclusion before the next step
delegYaml := fmt.Sprintf(`version: 1.0transaction: - tx: from: %s intents: - type: stake_delegation stake_address: %s pool_id: pool1...`, sender, account.StakeAddress)
deleg, err := bridge.QuickTx.BuildWith(delegYaml, provider, sender, 1)signedDeleg, err := bridge.Account.SignTxWithKeys(mnemonic, ccl.Testnet, 0, 0, deleg.TxCbor, "payment", "stake")Worked example: DRep registration, then vote
Section titled “Worked example: DRep registration, then vote”The DRep credential comes from the governance API:
drep, err := bridge.Gov.DrepKeyFromMnemonic(mnemonic, ccl.Testnet, 0)
drepYaml := fmt.Sprintf(`version: 1.0transaction: - tx: from: %s intents: - type: drep_registration drep_credential_hex: %s drep_credential_type: key_hash anchor_url: https://example.com/meta.json anchor_hash: %s`, sender, drep.VerificationKeyHash, anchorHash)
reg, err := bridge.QuickTx.BuildWith(drepYaml, provider, sender, 1)signedReg, err := bridge.Account.SignTxWithKeys(mnemonic, ccl.Testnet, 0, 0, reg.TxCbor, "payment", "drep")To vote on a governance action, the action id is the proposal transaction’s hash plus its index (a proposal you submit yourself returns its hash from Build — result.TxHash). Sign the voting transaction with "payment", "drep".
Worked example: mint under a native script
Section titled “Worked example: mint under a native script”mintYaml := fmt.Sprintf(`version: 1.0transaction: - tx: from: %s intents: - type: minting assets: - name: TestNFT value: 1 receiver: %s script_hex: "820180" script_type: 0`, sender, receiver)
mint, err := bridge.QuickTx.BuildWith(mintYaml, provider, sender, 0)signedMint, err := bridge.Account.SignTx(mnemonic, ccl.Testnet, 0, 0, mint.TxCbor)An empty ScriptAll policy (820180) needs no extra signature; a sig-keyed policy needs the corresponding key’s witness.
Worked example: Plutus mint
Section titled “Worked example: Plutus mint”By default execution units are computed offline (embedded Scalus evaluator) — a Plutus transaction is a normal build:
result, err := bridge.QuickTx.BuildWith(plutusMintYaml, provider, sender, 0)To cost against a real node instead, pass an evaluator — BuildWith then runs the two-pass flow (draft → remote evaluate → rebuild):
evaluator, _ := ccl.NewBlockfrostEvaluator(projectID, "preprod")result, err := bridge.QuickTx.BuildWith(plutusMintYaml, provider, sender, 0, evaluator)Or supply units yourself with the offline Build:
result, err := bridge.QuickTx.Build(plutusMintYaml, utxos, params, 0, []map[string]interface{}{{"mem": 2000000, "steps": 500000000}})For spending a script UTXO (script_collect_from), supply the locked UTXO (with its data_hash) plus a separate UTXO for fee/collateral in utxos — see the catalog entry and the end-to-end lock-then-spend flow in wrappers/go/ccl/script_spend_test.go.
Errors you’ll meet
Section titled “Errors you’ll meet”CCL Error -10(ErrTxBuild) — the plan didn’t build: malformed YAML, wrong intent field, or a Plutus costing problem. Compare against the catalog.CCL Error -8(ErrInsufficientFunds) — the supplied UTXOs can’t cover outputs + fee.- Node rejection
MissingVKeyWitnessesUTXOW— a certificate wasn’t witnessed; check the roles table above.