Apollo is a pure Go library for constructing Cardano transactions. It uses
the Blink Labs ledger packages for types, CBOR, scripts, addresses, and
transaction bodies — gouroboros
for the ledger, bursa for HD wallets,
and plutigo (or Apollo's own
plutusencoder struct tags) for Plutus data.
Ready to learn? Start with the documentation or jump to the first transaction.
Apollo v2 requires Go 1.25.13 or newer — the go directive in go.mod is a
hard floor, so an older 1.25 patch release fails to build:
go get github.com/Salvionied/apollo/v2Upgrading from v1? See the migration guide.
- Address parsing and validation (canonical bech32; no silent base58 fallback)
- HD wallets via bursa (mnemonic, optional passphrase, watch-only)
- Fluent Conway transaction builder (
Complete→Sign→Submit) - Automatic coin selection (MACS by default, pluggable)
- Chain backends: Blockfrost, Ogmios/Kupo, UTxO RPC, plus
fixedand cache - Plutus V1–V3 scripts, datums, redeemers, and reference inputs
- Struct-tag Plutus encoding (
plutusencoder) - Staking certificates, withdrawals, and CIP-1694 governance
- Race-tested suite (
go test -race ./...)
The builder balances transactions automatically using a pluggable
CoinSelector. A selector receives the available UTxO pool and the target
value and returns a deterministic subset that covers it.
Two selectors ship in-tree:
- MACS (the default): Multi-Asset Coin Selection (IEEE Blockchain 2023). It covers each deficient asset class directly, prefers UTxOs near the pool average, sweeps a bounded amount of dust, and avoids change in the min-UTxO dead band. On multi-asset payments it is substantially cheaper than largest-first; on plain ADA it is within a fraction of a percent while leaving far less dust.
- Largest-First: the v1 greedy behaviour — ADA-only UTxOs first, largest lovelace first. Use it when you need the old input set.
builder := apollo.New(chainContext) // MACS with dust sweeping
builder = builder.SetCoinSelector(&apollo.LargestFirstSelector{})
builder = builder.SetCoinSelector(&apollo.MACSSelector{
DustThreshold: 2_000_000,
MaxDustInputs: 4,
})Details, benchmarks, and the design note: coin selection.
Apollo does not embed a Plutus CEK machine. Script execution units come from
the chain backend's EvaluateTx. Backends implement ChainContext and may
report optional operations through CapabilityReporter — check
CapabilityEvaluateTxAdditionalUtxos before relying on evaluator-supplied
UTxOs for transaction chaining.
| Backend | Package |
|---|---|
| Blockfrost | backend/blockfrost |
| Ogmios + Kupo | backend/ogmios |
| UTxO RPC | backend/utxorpc |
| In-memory tests | backend/fixed |
| TTL cache wrapper | backend/cache |
See backends and capabilities.
When a script transaction lists required signers, the evaluator needs valid
signatures on the preliminary body. Apollo supplies those as
evaluation-only witnesses: they are not kept in the unsigned transaction
Complete() returns.
BursaWallet provides its payment and stake witnesses automatically.
Watch-only, hardware, and remote wallets register an
EvaluationWitnessProvider instead of changing the Wallet interface:
type remoteEvaluationSigner struct{}
func (remoteEvaluationSigner) EvaluationWitnesses(
bodyHash common.Blake2b256,
required []common.Blake2b224,
) ([]common.VkeyWitness, error) {
// Return valid witnesses for any requested hashes controlled remotely.
return nil, nil
}
builder.AddEvaluationWitnessProvider(remoteEvaluationSigner{})Full behaviour: evaluation witnesses.
This sends 1 ADA to a receiver on mainnet via Blockfrost. The full walkthrough is in getting started.
package main
import (
"encoding/hex"
"fmt"
"github.com/blinklabs-io/gouroboros/ledger/common"
apollo "github.com/Salvionied/apollo/v2"
"github.com/Salvionied/apollo/v2/backend/blockfrost"
"github.com/Salvionied/apollo/v2/constants"
)
func main() {
chain := blockfrost.NewBlockFrostChainContext(
constants.BlockfrostBaseUrlMainnet,
1, // mainnet network ID
"your_blockfrost_project_id",
)
// 1.- Build transaction
builder, err := apollo.New(chain).SetWalletFromMnemonic("your mnemonic here")
if err != nil {
panic(err)
}
utxos, err := chain.Utxos(builder.GetWallet().Address())
if err != nil {
panic(err)
}
receiver, err := common.NewAddress("addr1...")
if err != nil {
panic(err)
}
builder, err = builder.
AddLoadedUTxOs(utxos...).
PayToAddress(receiver, 1_000_000).
Complete()
if err != nil {
panic(err)
}
// 2.- Sign transaction
builder, err = builder.Sign()
if err != nil {
panic(err)
}
// 3.- Submit transaction
txId, err := builder.Submit()
if err != nil {
panic(err)
}
fmt.Println(hex.EncodeToString(txId.Bytes()))
}Apollo builds Conway-era transactions, including CIP-1694 governance. You can:
- Register, update, and retire DReps
- Authorize or resign constitutional committee keys
- Cast votes and submit governance action proposals
- Donate to the treasury
- Register stake, delegate to pools and DReps, and withdraw rewards
See Conway governance and staking. Plutus scripts, datums, and reference inputs are covered under Plutus V3 and data attachment.
We welcome contributions. Please read CONTRIBUTING.md for guidelines. Before opening a pull request, run:
gofmt -w $(find . -name '*.go' -not -path './.worktrees/*')
go test -race ./...For questions and requests, join the Apollo Discord.
Created by Edoardo Salvioni (Zhaata).
