From f79735f2ee71f0c554692506866de69057d53850 Mon Sep 17 00:00:00 2001 From: Santiago Date: Wed, 13 May 2026 21:46:23 -0300 Subject: [PATCH] feat: replace integration section with SDK reference pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous integration pages documented an older codegen-based workflow where users ran `trix bindgen` and called generated per-protocol functions from a `protocol.ts` / `protocol.go` / etc. file. That model no longer matches reality: each of the four official SDK repos (web/Go/Rust/Python) now ships a runtime library that loads a compiled `.tii` protocol, binds parties and signers, and drives the full resolve → sign → submit → wait lifecycle through TRP. Replace the section with one page per SDK, authored from the corresponding repo's README: - typescript.md (tx3-lang/web-sdk) - go.md (tx3-lang/go-sdk) - rust.md (tx3-lang/rust-sdk) - python.md (tx3-lang/python-sdk) Also add an Integration index page introducing the four SDKs, and drop a dead link in tooling/trix.mdx that pointed at the removed integration/codegen page. Co-Authored-By: Claude Opus 4.7 (1M context) --- integration/codegen.mdx | 93 ------------------ integration/go.md | 201 +++++++++++++++++++++++++++----------- integration/index.mdx | 18 ++++ integration/nodejs.md | 93 ------------------ integration/python.md | 164 +++++++++++++++++++++---------- integration/rust.md | 188 +++++++++++++++++++++++++---------- integration/typescript.md | 195 ++++++++++++++++++++++++++++++++++++ tooling/trix.mdx | 1 - 8 files changed, 599 insertions(+), 354 deletions(-) delete mode 100644 integration/codegen.mdx create mode 100644 integration/index.mdx delete mode 100644 integration/nodejs.md create mode 100644 integration/typescript.md diff --git a/integration/codegen.mdx b/integration/codegen.mdx deleted file mode 100644 index 72e4344..0000000 --- a/integration/codegen.mdx +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: Codegen Basics -sidebar: - order: 0 ---- - -import { Aside, TabItem, Tabs, LinkCard } from "@astrojs/starlight/components" - -Declaring the interface for blockchain protocol is not very useful unless we have a way to interact programmatically with it. - -**Codgen** is the process of automatically generating code of that can interact with your Tx3 protocol in a particular way and in a particular general-purpose language. - -## Common Uses - -Codegen could potentially be used for anything since it's based on open templates. Here are a few categories of things that could be useful: - -- **client libs**: generate bindings, glue code that allows you to execute the transaction building logic by calling language-specific functions that represent the templates in your protocol. -- **servers**: generate backend servers that wrap your protocol as an API service that hides all of the internal implementation details. - -## Supported Languages - -Tx3 bindgen feature can currently generate code in the following target languages: - -- Typescript -- Rust -- Go -- Python - -We plan on adding more languages as we move forward. - - - -## Configuration - -The codegen configuration lives inside the `trix.toml` file. You need to provide an entry for each language target. Here's an example of the config that targets both Typescript and Rust: - -```toml -[[bindings]] -plugin = "ts-client" -options = { standalone = true } - -[[bindings]] -plugin = "rust-client" -output_dir = "./gen/rust" -``` - -Each `bindings` entry has the following schema: - -- `plugin`: keyword indicating the target language. Valid values are `typescript`, `rust`, `go` & `python`. -- `output_dir`: root location where the generated code artifacts will be output to. -- `options`: target-specific options that allows you to customize the output. - - - - -## Code generation procedure - -To run the code generation process, execute the following `trix` command from your CLI: - -``` -trix codegen -``` - -If things went well, you should have new code artifacts in the corresponding output locations as defined in your `trix.toml` file. - -The output of the previous configuration example would yield the following new files: - - - - ``` - gen/ - └── typescript/ - ├── my-protocol.ts - ├── test.ts - ├── package.json - └── tsconfig.json - ``` - - - ``` - gen/ - └── rust/ - ├── my-protocol.rs - └── Cargo.toml - ``` - - - -We won't get into details of how to use the _ts_ code, you can read more about it on the [NodeJS](./bindings/nodejs) bindings reference. diff --git a/integration/go.md b/integration/go.md index 6b1baf4..ed15a96 100644 --- a/integration/go.md +++ b/integration/go.md @@ -1,91 +1,174 @@ --- -title: Go +title: Go SDK +sidebar: + label: Go + order: 2 --- -To integrate with Go you need to make sure that your _trix_ project has the `go` bindings generation enabled in the config: +The official Go SDK for Tx3 is published as the [`github.com/tx3-lang/go-sdk/sdk`](https://pkg.go.dev/github.com/tx3-lang/go-sdk/sdk) module. It loads a compiled `.tii` protocol, binds parties and signers, and drives the full transaction lifecycle (resolve, sign, submit, confirm) via the Transaction Resolve Protocol (TRP). -```toml -[[bindings]] -plugin = "go" -output_dir = "./gen/go" -``` +The source lives at [tx3-lang/go-sdk](https://github.com/tx3-lang/go-sdk). -With the binding generation enabled, run the following command from your CLI to trigger the code generator: +## Installation -``` -trix bindgen +```bash +go get github.com/tx3-lang/go-sdk/sdk ``` -If things went well, you should see the following new files: +## Quick start -``` -my-protocol/ -└── gen/ - └── go/ - ├── protocol.go - └── go.mod +```go +package 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() { + // Load a compiled .tii protocol + protocol, err := tx3.ProtocolFromFile("transfer.tii") + if err != nil { + log.Fatal(err) + } + + // Connect to a TRP server + trpClient := tx3.NewTRPClient(trp.ClientOptions{ + Endpoint: "http://localhost:3000", + }) + + // Create a Cardano signer from a mnemonic + mySigner, err := signer.CardanoSignerFromMnemonic( + "addr_test1qz...", + "word1 word2 ... word24", + ) + if err != nil { + log.Fatal(err) + } + + // Configure client with profile and parties + client := tx3.NewClient(protocol, trpClient). + WithProfile("preprod"). + WithParty("sender", tx3.SignerParty(mySigner)). + WithParty("receiver", tx3.AddressParty("addr_test1qz...")) + + ctx := context.Background() + + // Build, resolve, sign, submit, and wait for confirmation + status, err := client.Tx("transfer"). + Arg("quantity", 10_000_000). + Resolve(ctx) // -> ResolvedTx + .Sign() // -> SignedTx + .Submit(ctx) // -> SubmittedTx + .WaitForConfirmed(ctx, tx3.DefaultPollConfig()) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Confirmed at stage %s\n", status.Stage) +} ``` -That new `protocol.go` file has the required types and functions to call your protocol from a Go code. +> **Note:** The quick-start example above shows the conceptual flow. In real code, check errors at each step since Go doesn't chain errors across method calls. -The `go.mod` file is a standard file for Go projects. +## Concepts -Now, we can use the generated bindings to build a transaction using our protocol. +| SDK Type | Glossary Term | Description | +|---|---|---| +| `Protocol` | TII / Protocol | Loaded `.tii` file exposing transactions, parties, and profiles | +| `Tx3Client` | Facade | Entry point holding protocol, TRP client, and party bindings | +| `TxBuilder` | Invocation builder | Collects args, resolves via TRP | +| `Party` | Party | Named participant — `AddressParty` (read-only) or `SignerParty` (signing) | +| `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 | -First access to the `gen/go` folder and install the required dependencies: -```bash -cd gen/go && go mod tidy -``` +## Low-level TRP client -We need to open `protocol.go` and update the TRP endpoint to point to the correct URL (unless you have it configured on trix.toml). The default value is `http://localhost:3000/trp`, but if you're using different port or endpoint, make sure to update it. +```go +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}) +``` -For the following example, we will use the main.tx3 file generated by trix. +## Custom Signer -Now, we can create a new file called `test.go` with the following code on the same folder as `main.tx3`: +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`. ```go -package main +import "github.com/tx3-lang/go-sdk/sdk/signer" -import ( - "fmt" - "log" +type MySigner struct { /* ... */ } - "protocol" -) +func (s *MySigner) Address() string { return "addr_test1..." } -func main() { - // Create parameters for the transfer - params := protocol.TransferParams{ - Sender: "addr_test1vpgcjapuwl7gfnzhzg6svtj0ph3gxu8kyuadudmf0kzsksqrfugfc", - Receiver: "addr_test1vpry6n9s987fpnmjqcqt9un35t2rx5t66v4x06k9awdm5hqpma4pp", - Quantity: 100000000, - } - - // Execute the transfer transaction - cbor, err := protocol.DefaultClient.TransferTx(params) - if err != nil { - log.Fatalf("Error: %v", err) - } - - // Print the result - fmt.Printf("%+v\n", cbor) +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{})) ``` -For this, add the protocol as a dependency in your `go.mod` file: +## Manual witness attachment + +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()`: ```go -require protocol v0.0.0 +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 { /* ... */ } -replace protocol => ./gen/go +submitted, err := signed.Submit(ctx) ``` -Finally, we can run the test file to build a transaction using our protocol: -```bash -go run test.go +`AddWitness` may be called any number of times; manual witnesses are appended after registered-signer witnesses in attach order. + +## Error handling + +All errors are discriminable via `errors.As()` — no string matching needed: + +```go +import "github.com/tx3-lang/go-sdk/sdk/facade" + +_, err := client.Tx("transfer").Resolve(ctx) +var unknownParty *facade.UnknownPartyError +if errors.As(err, &unknownParty) { + fmt.Printf("Party %q not found in protocol\n", unknownParty.Name) +} ``` -If everything went well, you should see a message like this: -```bash -&{Tx:84a400d9010281825820705e5d956d318 264043baf8031e250c14b8703b69947ab2d3212d67210c4b240010182a200581d60464d4cb029fc90cf720600b2f271a2d433517ad32a67eac5eb9bba5c011a05f5e100a200581d605189743c77fc84cc571235062e4f0de28370f6273ade37697d850b40011b0000000248151b86021a0005833d0f00a0f5f6 Bytes: Encoding:} -``` \ No newline at end of file +## Tx3 protocol compatibility + +- **TRP protocol version:** v1beta0 +- **TII schema version:** v1beta0 diff --git a/integration/index.mdx b/integration/index.mdx new file mode 100644 index 0000000..38e8c64 --- /dev/null +++ b/integration/index.mdx @@ -0,0 +1,18 @@ +--- +title: Integration +sidebar: + label: Introduction + order: 0 +pagefind: false +--- + +import { LinkCard, CardGrid } from '@astrojs/starlight/components'; + +Tx3 ships official runtime SDKs in four languages. Each SDK loads a compiled `.tii` protocol, binds parties and signers, and drives the `resolve → sign → submit → wait` lifecycle through a TRP server. + + + + + + + diff --git a/integration/nodejs.md b/integration/nodejs.md deleted file mode 100644 index 24fdb32..0000000 --- a/integration/nodejs.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: NodeJS Backend -sidebar: - label: NodeJS Backend ---- - -To integrate with NodeJS you need to make sure that your _trix_ project has the `typescript` bindings generation enabled in the config: - -```toml -[[bindings]] -plugin = "typescript" -output_dir = "./gen/typescript" -``` - -With the binding generation enabled, run the following command from your CLI to trigger the code generator: - -``` -trix bindgen -``` - -If things went well, you should see the following new files: - -``` -my-protocol/ -└── gen/ - └── typescript/ - ├── protocol.ts - ├── package.json - └── tsconfig.json -``` - -That new protocol.ts file has the required types and functions to call your protocol from a Typescript code. It works on the backend and also in the browser (with the corresponding bundling). - -The `package.json` and `tsconfig.json` files are standard files for Typescript projects. - -Now, we can use the generated bindings to build a transaction using our protocol. - -First access to the `gen/typescript` folder and install the dependencies: - -```bash -cd gen/typescript && npm install -``` - -**NOTE**: You can use yarn, pnpm or any other package manager you prefer. - -We need to open `protocol.ts` and update the TRP endpoint to point to the correct URL (unless you have it configured in `trix.toml`). For the local Devnet the TRP endpoint is available at `http://localhost:8164/` (update this value accordingly if you're using a different port or network). - -For the following example, we will use the main.tx3 file generated by trix. - -Before running the example below, start the local Devnet in a separate terminal with: - -```bash -trix devnet -``` - -The Devnet prints sample wallets and addresses when it starts. If you don't see addresses in the console, fetch a wallet's addresses with `trix wallet ` (for example `trix wallet alice`). - -Now, create a new file called `test.ts` in the same folder (or any other folder) and add the following code (replace the placeholder addresses with the ones printed by `trix devnet` or obtained via `trix wallet`): - -```typescript -import { protocol, type TransferParams } from './gen/typescript/protocol'; - -const transferParams: TransferParams ={ - sender: 'addr_test1vqn7k6gfllvhauxkzw478ds2f9axmu4qw78pm86dtyd40yqg2w3rq', - receiver: 'addr_test1vqxazu4ekxrxlk238wt0e03h3gk44hrlkjvef85gvh2nahcgnmpfc', - quantity: 100000000, -}; - -async function main() { - try { - let cbor = await protocol.transferTx(transferParams); - console.log(cbor); - } catch (error) { - console.error('Error:', error); - } -} - -main(); -``` - -Finally, we can run the test file to build a transaction using our protocol: - -```bash -npx tsx test.ts -``` - -If everything went well, you should see a message like this: - -```bash -{ - tx: '84a400d9010281825820705e5d956d318264043baf8031e250c14b8703b69947ab2d3212d67210c4b240010182a200581d60464d4cb029fc90cf720600b2f271a2d433517ad32a67eac5eb9bba5c011a05f5e100a200581d605189743c77fc84cc571235062e4f0de28370f6273ade37697d850b40011b0000000248151b86021a0005833d0f00a0f5f6' -} -``` diff --git a/integration/python.md b/integration/python.md index c19a47d..27363cf 100644 --- a/integration/python.md +++ b/integration/python.md @@ -1,78 +1,134 @@ --- -title: Python +title: Python SDK +sidebar: + label: Python + order: 4 --- -To integrate with Python you need to make sure that your _trix_ project has the `python` bindings generation enabled in the config: +The official Python SDK for Tx3 is published on PyPI as [`tx3-sdk`](https://pypi.org/project/tx3-sdk/). It loads a compiled `.tii` protocol, binds parties and signers, and drives the full transaction lifecycle (`resolve -> sign -> submit -> wait`) through TRP. -```toml -[[bindings]] -plugin = "python" -output_dir = "./gen/python" -``` +The source lives at [tx3-lang/python-sdk](https://github.com/tx3-lang/python-sdk). -With the binding generation enabled, run the following command from your CLI to trigger the code generator: +## Installation -``` -trix bindgen +```bash +pip install tx3-sdk ``` -If things went well, you should see the following new files: +## Quick start -``` -my-protocol/ -└── gen/ - └── python/ - ├── __init__.py - └── requirements.txt -``` +```python +import asyncio -That new `__init__.py` file has the required types and functions to call your protocol from a Python code. +from tx3_sdk import CardanoSigner, Party, PollConfig, Protocol, TrpClient, Tx3Client -The `requirements.txt` file is a standard file for Python projects. -Now, we can use the generated bindings to build a transaction using our protocol. +async def main() -> None: + # 1) Load a compiled .tii protocol + protocol = Protocol.from_file("examples/transfer.tii") -First access to the `gen/python` folder and install the required dependencies: -```bash -cd gen/python && pip install -r requirements.txt + # 2) Create a low-level TRP client + trp = TrpClient(endpoint="https://preprod.trp.tx3.dev") + + # 3) Configure signer and parties + sender_signer = CardanoSigner.from_mnemonic( + address="addr_test1qz...", + phrase="word1 word2 ... word24", + ) + + client = ( + Tx3Client(protocol, trp) + .with_profile("preprod") + .with_party("sender", Party.signer(sender_signer)) + .with_party("receiver", Party.address("addr_test1qz...")) + ) + + # 4) Build, resolve, sign, submit + submitted = await ( + await ( + await client.tx("transfer").arg("quantity", 10_000_000).resolve() + ).sign() + ).submit() + + # 5) Wait for confirmation + status = await submitted.wait_for_confirmed(PollConfig.default()) + print(f"Confirmed at stage: {status.stage}") + + +asyncio.run(main()) ``` -We need to open `__init__.py` and update the TRP endpoint to point to the correct URL (unless you have it configured on trix.toml). The default value is `http://localhost:3000/trp`, but if you're using different port or endpoint, make sure to update it. +## Concepts + +| SDK Type | Glossary Term | Description | +|---|---|---| +| `Protocol` | TII / Protocol | Loaded `.tii` with transactions, parties, and profiles | +| `Tx3Client` | Facade | High-level client holding protocol + TRP + party bindings | +| `TxBuilder` | Invocation builder | Collects args and resolves transactions | +| `Party` | Party | `Party.address(...)` or `Party.signer(...)` | +| `Signer` | Signer | Protocol producing a `TxWitness` for a `SignRequest` | +| `SignRequest` | SignRequest | Input passed to `Signer.sign`: `tx_hash_hex` + `tx_cbor_hex` | +| `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 | Poll attempts and delay for wait modes | + +## Low-level TRP client -For the following example, we will use the main.tx3 file generated by trix. +```python +from tx3_sdk import TrpClient +from tx3_sdk.trp import ResolveParams -Now, we can create a new file called `test.py` with the following code on the same folder as `main.tx3`: +trp = TrpClient(endpoint="http://localhost:8000", headers={"Authorization": "Bearer token"}) +envelope = await trp.resolve(ResolveParams(tir=..., args={"quantity": 100})) +``` + +## Custom Signer + +Implement the `Signer` protocol. `sign` receives a `SignRequest` carrying both +the tx hash and the full tx CBOR; hash-based signers read `tx_hash_hex`, +tx-based signers (e.g. wallet bridges) read `tx_cbor_hex`. ```python -import asyncio -from gen.python import TransferParams, protocol - -params = TransferParams( - sender="addr_test1vpgcjapuwl7gfnzhzg6svtj0ph3gxu8kyuadudmf0kzsksqrfugfc", - receiver="addr_test1vpry6n9s987fpnmjqcqt9un35t2rx5t66v4x06k9awdm5hqpma4pp", - quantity=100000000 -) - -async def main(): - try: - # Call the transfer_tx method of the protocol - cbor = await protocol.transfer_tx(params) - print(cbor) - except Exception as error: - print(f"Error: {error}") - -if __name__ == "__main__": - # Run the asynchronous main function - asyncio.run(main()) +from tx3_sdk import SignRequest, Signer +from tx3_sdk.signer import TxWitness +from tx3_sdk.signer.witness import vkey_witness + + +class MySigner(Signer): + def address(self) -> str: + return "addr_test1..." + + def sign(self, request: SignRequest) -> TxWitness: + # sign request.tx_hash_hex with your key + return vkey_witness(public_key_hex="aabb", signature_hex="ccdd") ``` -Finally, we can run the test file to build a transaction using our protocol: -```bash -python test.py +## Manual witness attachment + +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()`: + +```python +from tx3_sdk.signer.witness import vkey_witness + +resolved = await client.tx("transfer").arg("quantity", 10_000_000).resolve() + +# Hand resolved.hash (or resolved.tx_hex) to the external wallet and get +# back a witness. The wallet needs the resolved tx to sign. +witness = vkey_witness(public_key_hex="aabb", signature_hex="ccdd") # sign resolved.hash with external wallet + +signed = await resolved.add_witness(witness).sign() +submitted = await signed.submit() ``` -If everything went well, you should see a message like this: +`add_witness` may be called any number of times; manual witnesses are appended after registered-signer witnesses in attach order. Note: `ResolvedTx` is a frozen dataclass, so `add_witness` returns a new instance. -```bash -{'tx': '84a400d9010281825820705e5d956d318264043baf8031e250c14b8703b69947ab2d3212d67210c4b240010182a200581d60464d4cb029fc90cf720600b2f271a2d433517ad32a67eac5eb9bba5c011a05f5e100a200581d605189743c77fc84cc571235062e4f0de28370f6273ade37697d850b40011b0000000248151b86021a0005833d0f00a0f5f6'} -``` \ No newline at end of file +## Tx3 protocol compatibility + +- TRP protocol version: `v1beta0` +- TII schema version: `v1beta0` diff --git a/integration/rust.md b/integration/rust.md index 05eac3a..763e7b1 100644 --- a/integration/rust.md +++ b/integration/rust.md @@ -1,82 +1,162 @@ --- -title: Rust +title: Rust SDK +sidebar: + label: Rust + order: 3 --- -To integrate with Rust you need to make sure that your _trix_ project has the `rust` bindings generation enabled in the config: +The official Rust SDK for Tx3 is published as the [`tx3-sdk`](https://crates.io/crates/tx3-sdk) crate. It loads a compiled `.tii` protocol, binds parties and signers, and drives the full transaction lifecycle (resolve, sign, submit, confirm) via the Transaction Resolve Protocol (TRP). -```toml -[[bindings]] -plugin = "rust" -output_dir = "./gen/rust" -``` +The source lives at [tx3-lang/rust-sdk](https://github.com/tx3-lang/rust-sdk). API reference is available on [docs.rs](https://docs.rs/tx3-sdk). -With the binding generation enabled, run the following command from your CLI to trigger the code generator: +## Installation -``` -trix bindgen +```bash +cargo add tx3-sdk ``` -If things went well, you should see the following new files: +Or in `Cargo.toml`: +```toml +[dependencies] +tx3-sdk = "0.11" +serde_json = "1" +tokio = { version = "1", features = ["full"] } ``` -my-protocol/ -└── gen/ - └── rust/ - ├── lib.rs - └── Cargo.toml + +## Quick start + +```rust +use serde_json::json; +use tx3_sdk::trp::{Client, ClientOptions}; +use tx3_sdk::{CardanoSigner, Party, PollConfig, Tx3Client}; + +#[tokio::main] +async fn main() -> Result<(), tx3_sdk::Error> { + // 1. Load a compiled .tii protocol + let protocol = tx3_sdk::tii::Protocol::from_file("./examples/transfer.tii")?; + + // 2. Connect to a TRP server + let trp = Client::new(ClientOptions { + endpoint: "https://trp.example.com".to_string(), + headers: None, + }); + + // 3. Configure signer and parties + let signer = CardanoSigner::from_mnemonic( + "addr_test1...", + "word1 word2 ... word24", + )?; + + let tx3 = Tx3Client::new(protocol, trp) + .with_profile("preprod") + .with_party("sender", Party::signer(signer)) + .with_party("receiver", Party::address("addr_test1...")); + + // 4. Build, resolve, sign, submit, and wait for confirmation + let status = tx3 + .tx("transfer") + .arg("quantity", json!(10_000_000)) + .resolve() + .await? + .sign()? + .submit() + .await? + .wait_for_confirmed(PollConfig::default()) + .await?; + + println!("Confirmed at stage: {:?}", status.stage); + Ok(()) +} ``` -That new `lib.rs` file has the required types and functions to call your protocol from a Rust code. +## Concepts -The `Cargo.toml` file is a standard file for Rust projects. +| SDK Type | Glossary Term | Description | +|---|---|---| +| `tii::Protocol` | TII / Protocol | Loaded `.tii` exposing transactions, parties, profiles | +| `Tx3Client` | Facade | Entry point holding protocol, TRP client, and party bindings | +| `TxBuilder` | Invocation builder | Collects args, resolves via TRP | +| `Party` | Party | `Party::address(...)` (read-only) or `Party::signer(...)` (signing) | +| `Signer` | Signer | Trait producing a `TxWitness` for a `SignRequest` | +| `SignRequest` | SignRequest | Input passed to `Signer::sign`: `tx_hash_hex` + `tx_cbor_hex` | +| `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 `wait_for_confirmed` / `wait_for_finalized` polling | -Now, we can use the generated bindings to build a transaction using our protocol. +## Low-level TRP client -First access to the `gen/rust` folder -```bash -cd gen/rust -``` +If you don't want the facade, drive TRP directly: -We need to open `lib.rs` and update the TRP endpoint to point to the correct URL (unless you have it configured on trix.toml). The default value is `http://localhost:3000/trp`, but if you're using different port or endpoint, make sure to update it. +```rust +use tx3_sdk::trp::{Client, ClientOptions, ResolveParams}; -For the following example, we will use the main.tx3 file generated by trix. +let client = Client::new(ClientOptions { + endpoint: "https://trp.example.com".to_string(), + headers: None, +}); -Now, we can create a new file called `src/bin/test.rs` in the same folder (or any other folder) and add the following code: +// build ResolveParams and call client.resolve(...).await +``` + +## Custom Signer + +Implement the `Signer` trait. `sign` receives a `SignRequest` carrying both the +tx hash and the full tx CBOR; hash-based signers read `tx_hash_hex`, tx-based +signers (e.g. wallet bridges) read `tx_cbor_hex`. ```rust -use tx3_sdk::trp::args::ArgValue; +use tx3_sdk::{SignRequest, Signer}; +use tx3_sdk::trp::TxWitness; -#[tokio::main] -async fn main() { - let params = trix_example::TransferParams { - sender: ArgValue::from("addr_test1vpgcjapuwl7gfnzhzg6svtj0ph3gxu8kyuadudmf0kzsksqrfugfc"), - receiver: ArgValue::from("addr_test1vpry6n9s987fpnmjqcqt9un35t2rx5t66v4x06k9awdm5hqpma4pp"), - quantity: ArgValue::from(100000000), - }; - - match trix_example::PROTOCOL.transfer_tx(params).await { - Ok(cbor) => { - println!("{:?}", cbor); - }, - Err(error) => { - eprintln!("Error: {:?}", error); - } +struct MySigner { /* ... */ } + +impl Signer for MySigner { + fn address(&self) -> &str { "addr_test1..." } + + fn sign( + &self, + request: &SignRequest, + ) -> Result> { + // sign request.tx_hash_hex with your key + unimplemented!() } } ``` -**NOTE**: Replace `trix_example` with the name of your protocol. -To your Cargo.toml file, add the following dependencies: -```toml -tokio = { version = "1", features = ["full"] } -``` +## Manual witness attachment -Finally, we can run the test file to build a transaction using our protocol: -```bash -cargo run --bin test -``` -If everything went well, you should see a message like this: +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()`: -```bash -TxEnvelope { tx: "84a400d9010281825820705e5d956d318264043baf8031e250c14b8703b69947ab2d3212d67210c4b240010182a200581d60464d4cb029fc90cf720600b2f271a2d433517ad32a67eac5eb9bba5c011a05f5e100a200581d605189743c77fc84cc571235062e4f0de28370f6273ade37697d850b40011b0000000248151ad6021a000583ed0f00a0f5f6" } +```rust +let resolved = tx3 + .tx("transfer") + .arg("quantity", json!(10_000_000)) + .resolve() + .await?; + +// Hand `resolved.hash` (or `resolved.tx_hex`) to the external wallet +// and get back a witness. The wallet needs the resolved tx to sign. +let witness: tx3_sdk::trp::TxWitness = /* sign resolved.hash with external wallet */; + +let status = resolved + .add_witness(witness) + .sign()? + .submit() + .await? + .wait_for_confirmed(PollConfig::default()) + .await?; ``` + +`add_witness` may be called any number of times; manual witnesses are appended after registered-signer witnesses in attach order. + +## Tx3 protocol compatibility + +- **TRP protocol version:** v1beta0 +- **TII schema version:** v1beta0 diff --git a/integration/typescript.md b/integration/typescript.md new file mode 100644 index 0000000..534346c --- /dev/null +++ b/integration/typescript.md @@ -0,0 +1,195 @@ +--- +title: TypeScript / JavaScript SDK +sidebar: + label: TypeScript + order: 1 +--- + +The official TypeScript / JavaScript SDK for Tx3 is published on npm as [`tx3-sdk`](https://www.npmjs.com/package/tx3-sdk). It loads a compiled `.tii` protocol, binds parties and signers, and drives the full transaction lifecycle (resolve, sign, submit, confirm) via the Transaction Resolve Protocol (TRP). It runs on Node.js 18+, modern browsers (ESM), Bun, and Deno. + +The source lives at [tx3-lang/web-sdk](https://github.com/tx3-lang/web-sdk). + +## Installation + +```bash +npm install tx3-sdk +``` + +## Quick start + +```ts +import { + Tx3Client, + Protocol, + TrpClient, + Party, + Ed25519Signer, + PollConfig, +} from "tx3-sdk"; + +// 1. Load a compiled .tii protocol +const protocol = await Protocol.fromFile("./transfer.tii"); + +// 2. Connect to a TRP server +const trp = new TrpClient({ endpoint: "http://localhost:3000/rpc" }); + +// 3. Configure signer and parties +const signer = Ed25519Signer.fromHex("addr_test1...", "deadbeef..."); + +const tx3 = new Tx3Client(protocol, trp) + .withProfile("preprod") + .withParty("sender", Party.signer(signer)) + .withParty("receiver", Party.address("addr_test1...")); + +// 4. Build, resolve, sign, submit, and wait for confirmation +const status = await tx3 + .tx("transfer") + .arg("quantity", 10_000_000n) + .resolve() + .then((r) => r.sign()) + .then((s) => s.submit()) + .then((sub) => sub.waitForConfirmed(PollConfig.default())); + +console.log(status.stage); // "confirmed" +``` + +## Concepts + +| SDK Type | Glossary Term | Description | +|---|---|---| +| `Protocol` | TII / Protocol | Loaded `.tii` exposing transactions, parties, profiles | +| `Tx3Client` | Facade | Entry point holding protocol, TRP client, and party bindings | +| `TxBuilder` | Invocation builder | Collects args, resolves via TRP | +| `Party` | Party | `Party.address(...)` (read-only) or `Party.signer(...)` (signing) | +| `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 | +| `Cip30Signer` | CIP-30 Wallet Signer | Browser-wallet (Eternl, Lace, Nami…) signer over CIP-30 | +| `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 | + +## Subpath imports + +The package exposes granular entry points for tree-shaking or when you only need a subset: + +```ts +import { TrpClient } from "tx3-sdk/trp"; +import { Protocol } from "tx3-sdk/tii"; +import { CardanoSigner, Cip30Signer, cip30Party } from "tx3-sdk/signer"; +``` + +## Browser usage + +`Protocol.fromFile` uses `node:fs` and is Node-only. In the browser, fetch the `.tii` JSON yourself: + +```ts +const protocol = Protocol.fromString(await (await fetch("/transfer.tii")).text()); +``` + +## Low-level TRP client + +If you don't want the facade, drive TRP directly: + +```ts +import { TrpClient } from "tx3-sdk/trp"; + +const trp = new TrpClient({ + endpoint: "https://trp.example.com", + headers: { "Authorization": "Bearer token" }, +}); + +const envelope = await trp.resolve({ tir: { /* ... */ }, args: { quantity: 100 } }); +const submitResp = await trp.submit({ tx: { content: envelope.tx, contentType: "hex" }, witnesses: [] }); +const status = await trp.checkStatus([submitResp.hash]); +``` + +## Custom Signer + +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`. + +```ts +import type { SignRequest, Signer } from "tx3-sdk"; +import type { TxWitness } from "tx3-sdk"; + +class MySigner implements Signer { + address(): string { return "addr_test1..."; } + + async sign(request: SignRequest): Promise { + // sign request.txHashHex with your key + return { + key: { content: "aabb", contentType: "hex" }, + signature: { content: "ccdd", contentType: "hex" }, + type: "vkey", + }; + } +} +``` + +## CIP-30 wallet bridge + +`Cip30Signer` plus the `cip30Party(api)` factory let you plug a CIP-30 browser +wallet (Eternl, Lace, Nami, …) directly into the standard chain: + +```ts +import { Tx3Client, Party } from "tx3-sdk"; +import { cip30Party } from "tx3-sdk/signer"; + +const api = await window.cardano.eternl.enable(); + +const tx3 = new Tx3Client(protocol, trp) + .withProfile("preprod") + .withParty("sender", await cip30Party(api)) + .withParty("receiver", Party.address("addr_test1...")); +``` + +For multi-key wallets (where one wallet returns several vkey witnesses for a +single tx), call `decodeWitnessSet` directly and attach each witness via +`addWitness` (see below). + +## Manual witness attachment + +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()`: + +```ts +import type { TxWitness } from "tx3-sdk"; + +const resolved = await tx3 + .tx("transfer") + .arg("quantity", 10_000_000n) + .resolve(); + +// Hand `resolved.hash` (or `resolved.txHex`) to the external wallet and +// get back a witness. The wallet needs the resolved tx to sign. +const witness: TxWitness = /* sign resolved.hash with external wallet */; + +const signed = await resolved.addWitness(witness).sign(); +const submitted = await signed.submit(); +const status = await submitted.waitForConfirmed(PollConfig.default()); +``` + +`addWitness` may be called any number of times; manual witnesses are appended after registered-signer witnesses in attach order. + +## Framework integrations + +Build-time codegen for typed `.tx3` imports lives outside the runtime SDK: + +- [`vite-plugin-tx3`](https://www.npmjs.com/package/vite-plugin-tx3) — Vite plugin +- [`rollup-plugin-tx3`](https://www.npmjs.com/package/rollup-plugin-tx3) — Rollup plugin +- [`next-tx3`](https://www.npmjs.com/package/next-tx3) — Next.js integration +- [`install-tx3-nextjs`](https://www.npmjs.com/package/install-tx3-nextjs) — scaffolder for a Next.js + Tx3 starter + +These are additions, not substitutes, for the runtime API documented above. + +## Tx3 protocol compatibility + +- **TRP protocol version:** v1beta0 +- **TII schema version:** v1beta0 +- **Runtime:** Node.js 18+, modern browsers (ESM), Bun, Deno diff --git a/tooling/trix.mdx b/tooling/trix.mdx index 450d9b6..ee077a9 100644 --- a/tooling/trix.mdx +++ b/tooling/trix.mdx @@ -118,7 +118,6 @@ trix explore ### `trix codegen` Generate code that can interact with your protocol by interpreting the interfaces defined by your Tx3 project. Code is generated from templates that use handlebars and can target multiple languages, including TypeScript, Rust, Go, and Python. -For detailed information about configuring and using bindgen, see the [Bindgen Guide](../integration/codegen). Usage: