The official Go SDK for Tx3 — a DSL and protocol suite for defining and executing UTxO-based blockchain transactions declaratively. Load a compiled .tii protocol, bind parties and signers, and drive the full transaction lifecycle (resolve, sign, submit, confirm) via the Transaction Resolve Protocol (TRP).
This repository is organized as a monorepo. The publishable Go SDK module lives in sdk/.
Tx3 is a domain-specific language and protocol suite for declarative, type-safe UTxO transactions. Authors write .tx3 files describing parties, environment, and transactions; the toolchain compiles them to .tii artifacts that this SDK loads at runtime to drive the resolve → sign → submit → wait lifecycle through a TRP server. See the Tx3 docs for project context.
go get github.com/tx3-lang/go-sdk/sdkpackage main
import (
"context"
"fmt"
"log"
tx3 "github.com/tx3-lang/go-sdk/sdk"
"github.com/tx3-lang/go-sdk/sdk/signer"
"github.com/tx3-lang/go-sdk/sdk/trp"
)
func main() {
// 1. Load a compiled .tii protocol
protocol, err := tx3.ProtocolFromFile("transfer.tii")
if err != nil {
log.Fatal(err)
}
// 2. Build a Cardano signer from a mnemonic
mySigner, err := signer.CardanoSignerFromMnemonic(
"addr_test1qz...",
"word1 word2 ... word24",
)
if err != nil {
log.Fatal(err)
}
// 3. Build a client: configure TRP, profile, and parties on the builder
client, err := tx3.ProtocolClient(protocol).
TRP(trp.ClientOptions{Endpoint: "http://localhost:3000"}).
WithProfile("preprod").
WithParty("sender", tx3.SignerParty(mySigner)).
WithParty("receiver", tx3.AddressParty("addr_test1qz...")).
Build()
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
// 4. Build, resolve, sign, submit, and wait for confirmation
txb, err := client.Tx("transfer")
if err != nil {
log.Fatal(err)
}
resolved, err := txb.Arg("quantity", 10_000_000).Resolve(ctx)
if err != nil {
log.Fatal(err)
}
signed, err := resolved.Sign()
if err != nil {
log.Fatal(err)
}
submitted, err := signed.Submit(ctx)
if err != nil {
log.Fatal(err)
}
status, err := submitted.WaitForConfirmed(ctx, tx3.DefaultPollConfig())
if err != nil {
log.Fatal(err)
}
fmt.Printf("Confirmed at stage %s\n", status.Stage)
}All fallible validation — TRP endpoint present, profile declared, every bound
party declared — happens inside Build(), which returns *MissingTrpEndpointError,
*tii.UnknownProfileError, or *facade.UnknownPartyError (all discriminable via
errors.As). Optional setters never fail, so chains stay fluent. Profile selection
is builder-only: there is no profile-switching method on the built client.
Switching profiles requires a new builder.
Note on naming: Go uses the cross-package factory
tx3.ProtocolClient(p)(equivalentlyfacade.FromProtocol(p)) instead of aProtocol.Client()method to avoid atii → facade → tiiimport cycle. Other SDKs (web, python) do exposeProtocol.client().
| SDK Type | Glossary Term | Description |
|---|---|---|
Protocol |
TII / Protocol | Loaded .tii file exposing transactions, parties, and profiles |
Tx3ClientBuilder |
Client builder | Fluent builder seeded by tx3.ProtocolClient(p) or facade.FromParts(...); absorbs all fallible validation in Build() |
Tx3Client |
Facade | Output of Tx3ClientBuilder.Build() — owns the deconstructed protocol parts, TRP client, profile, and party bindings |
TxBuilder |
Invocation builder | Source-agnostic; collects args, resolves via TRP |
Party |
Party | Named participant — AddressParty (read-only) or SignerParty (signing) |
Profile |
Profile | {Environment, Parties} value baked into the client; embedded by codegen plugins, decomposed from Protocol by FromProtocol |
MissingTrpEndpointError / UnknownPartyError |
Builder errors | Returned by Build(); implement the facade.FacadeError marker |
Signer |
Signer | Interface producing a TxWitness for a SignRequest |
SignRequest |
SignRequest | Input passed to Signer.Sign: TxHashHex + TxCborHex |
CardanoSigner |
Cardano Signer | BIP32-Ed25519 signer at m/1852'/1815'/0'/0/0 |
Ed25519Signer |
Ed25519 Signer | Generic raw-key Ed25519 signer |
ResolvedTx |
Resolved transaction | Output of Resolve(), ready for signing |
SignedTx |
Signed transaction | Output of Sign(), ready for submission |
SubmittedTx |
Submitted transaction | Output of Submit(), pollable for status |
PollConfig |
Poll configuration | Controls WaitForConfirmed / WaitForFinalized polling |
If you've run trix codegen to generate typed bindings, your generated Client
embeds the per-transaction TIR envelopes and per-profile data at codegen time —
no .tii artifact at runtime. Under the hood it seeds the same builder via
facade.FromParts(transactions, profiles, knownParties) and routes typed
per-party setters through WithPartyUnchecked. You can also call FromParts
directly from hand-written code (tx3.FromParts is the same factory re-exported
at the package root):
import tx3 "github.com/tx3-lang/go-sdk/sdk"
client, err := tx3.FromParts(transactions, profiles, []string{"sender", "receiver"}).
TRPEndpoint("http://localhost:3000").
WithPartyUnchecked("sender", tx3.SignerParty(mySigner)).
Build()import "github.com/tx3-lang/go-sdk/sdk/trp"
client := trp.NewClient(trp.ClientOptions{
Endpoint: "http://localhost:3000",
Headers: map[string]string{"Authorization": "Bearer token"},
})
envelope, err := client.Resolve(ctx, trp.ResolveParams{...})
resp, err := client.Submit(ctx, trp.SubmitParams{...})
status, err := client.CheckStatus(ctx, []string{txHash})Implement the Signer interface. Sign receives a SignRequest carrying both
the tx hash and the full tx CBOR; hash-based signers read TxHashHex, tx-based
signers (e.g. wallet bridges) read TxCborHex.
import "github.com/tx3-lang/go-sdk/sdk/signer"
type MySigner struct { /* ... */ }
func (s *MySigner) Address() string { return "addr_test1..." }
func (s *MySigner) Sign(request signer.SignRequest) (*signer.TxWitness, error) {
// sign request.TxHashHex with your key
return signer.NewVKeyWitness(pubKeyHex, signatureHex), nil
}
client.WithParty("sender", tx3.SignerParty(&MySigner{}))When a witness is produced outside any registered signer — for example by an
external wallet app or a remote signing service — resolve the transaction
first, hand the resolved hash (or full tx CBOR) to the wallet, then attach
the returned witness before Sign():
import "github.com/tx3-lang/go-sdk/sdk/trp"
resolved, err := client.Tx("transfer").Arg("quantity", 10_000_000).Resolve(ctx)
if err != nil { /* ... */ }
// Hand resolved.Hash (or resolved.TxHex) to the external wallet and get
// back a witness. The wallet needs the resolved tx to sign.
var witness trp.TxWitness // sign resolved.Hash with external wallet
signed, err := resolved.AddWitness(witness).Sign()
if err != nil { /* ... */ }
submitted, err := signed.Submit(ctx)AddWitness may be called any number of times; manual witnesses are appended after registered-signer witnesses in attach order.
All errors are discriminable via errors.As() — no string matching needed:
import (
"github.com/tx3-lang/go-sdk/sdk/facade"
"github.com/tx3-lang/go-sdk/sdk/tii"
)
_, err := tx3.ProtocolClient(protocol).Build()
var missingTRP *facade.MissingTrpEndpointError
if errors.As(err, &missingTRP) {
// no TRP endpoint supplied via TRP() / TRPEndpoint()
}
_, err = client.Tx("transfer")
var unknownTx *tii.UnknownTxError
if errors.As(err, &unknownTx) {
fmt.Printf("Tx %q not declared by protocol\n", unknownTx.Name)
}- TRP protocol version: v1beta0
- TII schema version: v1beta0
- Unit tests are co-located with the packages they exercise (
*_test.gonext to source files). - End-to-end (e2e) tests live under
sdk/e2e/and are gated by thee2ebuild tag.
# from go-sdk/sdk
go test ./... -count=1
go test -tags=e2e ./e2e -count=1Apache-2.0 — see LICENSE.