# Sig Network Manage and use cryptographic key(s) across multiple chains or multiple contexts, with on-chain-enforced conditions ## Introduction Managing multiple accounts across different blockchains is a headache. Every chain has its own wallets, private keys, and authentication systems, making cross-chain interactions complex and fragmented. But what if a single abstract account could seamlessly control multiple accounts across different chains—without compromising security? That's now possible through Chain Signatures. ### What Are Chain Signatures? Chain Signatures are programmable digital signatures that enable cross-chain control, allowing one account on one blockchain to securely manage accounts and assets on entirely different chains. The system: * **Derives keys** from a single base key. * Allows third parties (e.g., dApps, wallets, or protocols) to **request signatures** on-chain. * Introduces **flexible usage controls**, such as daily spending limits or rate limits, by combining these keys with custom on-chain logic. ### Benefits & Use Cases: * **One Identity, Many Chains**: Instead of juggling multiple wallets, a single signature from your Ethereum account could authorize actions on any other supported EVM or non-EVM chain, without needing to manage separate private keys. * **Seamless Cross-Chain Transactions**: Move assets or execute smart contract functions across different chains, without relying on third-party bridges or centralized custodians. * **Usage Controls**: rate or time-based restrictions can be enforced by the on-chain contract that manages key usage (e.g., "this key can only send up to 1 ETH per day"). * **Recovery or Fallback Conditions**: Instead of storing private keys in a single hardware device, you can use a multi-party computation (MPC) setup that offers user-friendly ways to recover accounts. * **Signs the Arbitrary State of a Foreign Chain**: Gives your smart contract awareness of whether a transaction it signed went through so that it can perform actions accordingly. For example, it allows for a smart contract to keep track of local chain assets based on the foreign chain's state. ## How It Works? ### Contract Background The **ChainSignatures** contract: * Holds a base public key (`publicKey`), expressed in affine coordinates on the secp256k1 curve. * Derives new public keys for each `(user, path)` combination by computing an integer `epsilon` (based on a `path` string and user’s address) and performing an EC point addition: ``` DerivedPubKey = BasePubKey + (epsilon × G) ``` * Allows a user to request a signature on a 32-byte payload by calling `sign(...)`. A minimal deposit may be required to discourage spam; any unused deposit is refunded once the request is completed. * Awaits the **MPC network** to compute the actual ECDSA signature offline, then call `respond(...)` with that signature. The contract then emits an event with the signature, and refunds the deposit. # Quick Start ## Installation ```bash npm install signet.js # or yarn add signet.js # or pnpm add signet.js ``` ## Signing a Transaction Here's a basic example using the EVM implementation: ```ts twoslash import { contracts, constants, chainAdapters } from 'signet.js' // [!include ~/snippets/code/evm/env.ts] const evmChainSigContract = new contracts.evm.ChainSignatureContract({ publicClient, walletClient, contractAddress: constants.CONTRACT_ADDRESSES.ETHEREUM .TESTNET_DEV as `0x${string}`, }) const evm = new chainAdapters.evm.EVM({ publicClient, contract: evmChainSigContract, }) const path = 'eth' const predecessorId = walletClient.account.address const keyVersion = 1 const { address: from, publicKey } = await evm.deriveAddressAndPublicKey( predecessorId, path, keyVersion ) const { balance, decimals } = await evm.getBalance(from) const { transaction, hashesToSign } = await evm.prepareTransactionForSigning({ from: from as `0x${string}`, to: '0x4174678c78fEaFd778c1ff319D5D326701449b25', value: 1n, // Amount in wei (1 wei in this example) }) const rsvSignature = await evmChainSigContract?.sign({ payload: hashesToSign[0], path, key_version: keyVersion, }) const tx = evm.finalizeTransactionSigning({ transaction, rsvSignatures: [rsvSignature], }) const txHash = await evm.broadcastTx(tx) ``` ## Supported Chains * [EVM Chains](/signetjs/chain-adapters/evm) - Ethereum and compatible networks * [Bitcoin](/signetjs/chain-adapters/bitcoin) - Bitcoin network * [Cosmos Chains](/signetjs/chain-adapters/cosmos) - Cosmos SDK-based networks ## Architecture The library is built around a core `ChainAdapter` interface that defines common functionality across all supported blockchain networks. Each specific chain implementation extends this interface with network-specific features while maintaining a consistent API. For more details check the [ChainAdapter](/primitives/chain-adapter-interface) page. ## Utility Functions Besides the ChainAdapter classes the library also provides utility functions to assist you on building transactions, requesting signature in wrapped methods. Currently we support: * EVM ## Repositories * Signet.js: [https://github.com/sig-net/signet.js](https://github.com/sig-net/signet.js). * Examples: [https://github.com/sig-net/signet-examples](https://github.com/sig-net/signet-examples). * MPC: [https://github.com/sig-net/mpc](https://github.com/sig-net/mpc). # Sign Bidirectional Flow The Sign Bidirectional flow enables cross-chain transaction execution with verified response callbacks. It allows a source chain contract to execute a transaction on a destination chain and receive the execution result — the contract return value or success/failure status — back on-chain with cryptographic proof, all verified by the MPC network. This page is the canonical, chain-agnostic description of the flow. Everything here applies to every source chain; the exact contract/template API, request-id byte encoding, and verification code are chain-specific and documented per chain — see [Per-Chain Documentation](#per-chain-documentation). ## Supported Chains A chain can participate as a **source chain** (where the request is made and the result is verified), as a **destination chain** (where the signed transaction executes), or both. Any supported source chain can drive transactions on any supported destination chain. | Chain | As source chain | As destination chain | | --------------- | --------------- | -------------------- | | **Solana** | Supported | — | | **EVM chains** | Supported | Supported | | **Canton** | Supported | — | | **Hydration** | Supported | — | | **Bitcoin** | — | In Progress | | **Hyperliquid** | Planned | — | ## Overview The bidirectional flow enables a source chain contract to: 1. Request a signature for a destination chain transaction 2. Broadcast the signed transaction to the destination chain 3. Receive the execution result (contract return value or success/failure) back on the source chain ``` User Source Chain MPC Destination Chain │ │ │ │ │ sign_bidirectional() │ │ │ ├────────────────────────►│ │ │ │ │ SignBidirectionalEvent │ │ │ ├───────────────────────►│ Sign & store │ │ │◄───── respond() ───────┤ │ │ Poll signature │ │ │ │◄─ SignatureRespondedEvent │ │ │ │ │ │ │ Broadcast ──────────────┼────────────────────────┼───────────────────────►│ │ │ │◄──── Observe tx ───────┤ │ │◄─ respond_bidirectional() │ │ Poll response │ │ │ │◄─ RespondBidirectionalEvent │ │ │ │ │ │ ``` The diagram uses generic names; entry-point and event names follow each source chain's conventions (see the per-chain docs), but the lifecycle is identical everywhere. ## Flow Steps ### Phase 1: Sign Request 1. The user calls the source chain's bidirectional entry point with the serialized unsigned destination-chain transaction (plus the two serialization schemas) 2. The source chain contract emits `SignBidirectionalEvent` 3. The MPC indexes the event and computes the unique request ID ### Phase 2: Signature Delivery 1. The MPC threshold-signs the transaction hash with the child key derived from the request's `sender` and `path` (what `sender` is bound to is defined per source chain) 2. It stores the expected transaction in its backlog for destination-chain observation 3. It calls `respond`, emitting `SignatureRespondedEvent` 4. The user polls for `SignatureRespondedEvent` to obtain the signature ### Phase 3: User Broadcast 1. The user assembles the signed transaction (serialized data + signature) 2. The user broadcasts it to the destination chain 3. **The MPC does NOT broadcast** — this is the user's responsibility ### Phase 4: Destination Chain Observation 1. The MPC observes destination chain blocks (via light client or its own indexer) 2. It detects the transaction confirmation by hash 3. It extracts the execution status (success/failure) ### Phase 5: Output Extraction 1. For contract calls: the MPC extracts the call's return value 2. For plain transfers (no return data to extract): a schema-shaped default success value (e.g. `bool` → `true`) 3. The output is parsed using the request's `output_deserialization_schema` ### Phase 6: Respond Bidirectional 1. The MPC serializes the output using the request's `respond_serialization_schema` 2. It signs `keccak256(request_id || serialized_output)` with the response-verification key 3. It calls `respond_bidirectional` on the source chain 4. The source chain contract emits `RespondBidirectionalEvent`; the consumer verifies the signature and acts on the result ## Request IDs Every request has a unique 32-byte ID that correlates all events across the flow. It is a `keccak256` hash over the full request tuple: ```text request_id = keccak256( sender || transaction || caip2_id || key_version || path || algo || dest || params ) ``` The ID is computed identically by the source chain contract, the MPC, and clients — but the exact byte encoding is chain-specific. See the per-chain docs for the formula on each chain. ## Serialization Schemas Cross-chain data encoding uses two schemas, both supplied by the requester: | Schema | Direction | Purpose | | ------------------------------- | ----------------- | ----------------------------------------------- | | `output_deserialization_schema` | Destination → MPC | Parse execution result from destination chain | | `respond_serialization_schema` | MPC → Source | Serialize response for source chain consumption | The response serialization format is fixed per source chain — see the per-chain docs. ## Error Handling Failed destination chain transactions (reverts, or transactions permanently invalidated by their nonce being spent by a different transaction) are still answered: the MPC publishes a signed `RespondBidirectionalEvent` whose `serialized_output` starts with a magic error prefix: ```text MAGIC_ERROR_PREFIX = 0xdeadbeef is_error(output) = output starts with 0xdeadbeef ``` Consumers must check for the prefix before interpreting the output as a successful result. ## Address Derivation Each `(sender, path)` pair gets its own destination chain key, derived from the MPC root public key with the Signet epsilon KDF: ```text epsilon = derive_epsilon(key_version, kdf_chain_id, sender, path) child_pubkey = root_pubkey + epsilon × G ``` `kdf_chain_id` namespaces derivation per source chain (the `KDF_CHAIN_IDS` constants in signet.js). The reference implementation is [`deriveChildPublicKey`](https://github.com/sig-net/signet.js/blob/main/src/utils/cryptography.ts) in signet.js; root public keys per environment are published as `ROOT_PUBLIC_KEYS` (see [Contract Addresses](/primitives/contract-addresses)). ## Response Signature Verification The `respond_bidirectional` payload is signed with a **dedicated response-verification child key**, derived like any other child key but with a fixed, per-source-chain derivation path (a constant of the form `" response key"` — the exact value is in each chain's docs). The signature is computed over: ```text message_hash = keccak256(request_id || serialized_output) ``` To verify a response, a consumer must: 1. Derive the expected response public key from the MPC root (same `sender`, the fixed response path) 2. Check the response signature over `message_hash` against that key, using the source chain's signature-verification primitive (see the per-chain docs) Never verify against the root public key or the transaction-signing key — the response key is intentionally distinct so that a transaction signature can never be replayed as an execution proof. ## Security Properties 1. **Request ID uniqueness**: each request's ID commits to the full request tuple, preventing replay across requests 2. **Response authenticity**: responses are MPC threshold signatures over `keccak256(request_id || serialized_output)` with a dedicated response key 3. **Output consistency**: the two schemas ensure the destination-chain result is encoded/decoded identically by all parties 4. **Key isolation**: every `(sender, path)` pair has an isolated child key; source chains are further isolated by the KDF chain ID 5. **Consumer-side replay protection**: the signing contracts are stateless event buses — consumers must pair each request with a single-use anchor/nullifier before acting on a response (the per-chain guides show how) ## Per-Chain Documentation The contract/template API for each source chain lives with its implementation. Start here: | Source chain | Implementation | Documentation | | ------------ | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Solana** | `solana-signet-program` (Anchor) | [Rust docs on docs.rs](https://docs.rs/chain-signatures-solana-program/latest/chain_signatures/) · [GitHub](https://github.com/sig-net/solana-signet-program) · [Contract API page](/contract-api/solana) | | **EVM** | `signet-evm-contract` (`ChainSignatures.sol`) | [GitHub README](https://github.com/sig-net/signet-evm-contract) · [Contract API pages](/contract-api/evm/functions) | | **Canton** | `signet-signer-v1` Daml templates (sig-net/canton) | [Signer README](https://github.com/sig-net/canton/blob/main/daml-packages/signet-signer-v1/README.md) (authority model, integrator lifecycle, API) · [ERC-20 Vault example](https://github.com/sig-net/canton/blob/main/daml-packages/signet-vault-v1/README.md) · [Canton docs page](/contract-api/canton) | ## Related Documentation * [Chain Signatures Contract](/primitives/chain-contract-interface) - Contract interface * [Contract Addresses](/primitives/contract-addresses) - Deployed addresses and root public keys # Contract Addresses All the necessary constants to interact with the Sig Network are exported from the `constants.ts` file. ## Using the Constants in your code You can import and use them in your code as follows: ```ts twoslash import { constants } from 'signet.js' const evmTestnetAddress = constants.CONTRACT_ADDRESSES.ETHEREUM.TESTNET console.log(evmTestnetAddress) // '0x83458E8Bf8206131Fe5c05127007FA164c0948A2' const testnetPublicKey = constants.ROOT_PUBLIC_KEYS.TESTNET console.log(testnetPublicKey) // 'secp256k1:3Ww8iFjqTHufye5aRGUvrQqETegR4gVUcW8FX5xzscaN9ENhpkffojsxJwi6N1RbbHMTxYa9UyKeqK3fsMuwxjR5' ``` ## Constants file For reference, here is the file with the full list of constants and their explanations: ```ts twoslash // [!include ~/../src/constants.ts] ``` # ChainAdapter ChainAdapter is the most basic interface of the signet.js library. It provides a standard set of methods that all chains must implement. With this interface you will be able to interact with Sig Network smart contracts in a standard way to: * getBalance * deriveAddressAndPublicKey * serializeTransaction * deserializeTransaction * prepareTransactionForSigning * finalizeTransactionSigning * broadcastTx If you want to have a look, this is the chain interface with a brief description of each method: ```ts twoslash // [!include ~/../src/chain-adapters/ChainAdapter.ts] ``` # Implementing a New ChainAdapter This guide explains how to implement support for a new blockchain network in the signet.js library. ## Overview To add support for a new blockchain, you need to implement the `ChainAdapter` interface: ## Step-by-Step Guide ### 1. Create the ChainAdapter Types First, define the transaction types for your chain: ```ts twoslash // [!include ~/../src/types.ts] ``` ### 2. Implement the ChainAdapter Interface Create a new class that implements the `ChainAdapter` interface: ```ts twoslash // [!include ~/../src/chain-adapters/EVM/EVM.ts] ``` # Implementing a Custom Chain Signature Contract This guide explains how to implement a custom Chain Signature Contract to provide as argument when instantiating a Chain instance. ## Overview The `ChainSignatureContract` is an abstract class that defines the interface for interacting with the Sig Network infrastructure. * Key Derivation: Derive the child key from the root key using Sig Network key derivation function * Sign: Signs the given payload using Sig Network MPC * Current Fee: Gets the current signature deposit that handles network congestion * Root Public Key: Gets the root public key of the Smart Contract on Sig Network MPC You have the flexibility to implement your own contract for other blockchain networks. ## Using the EVM Implementation ```ts twoslash import { contracts, chainAdapters } from 'signet.js' const contract = new contracts.evm.ChainSignatureContract({ publicClient: undefined as any, walletClient: undefined as any, contractAddress: '0x...', }) ``` ## Implementing a Custom Chain Signature Contract To create your own implementation to use on the [Chain Adapter](/primitives/chain-adapter-interface) Instance, your contract must implement the `BaseChainSignatureContract` abstract class. ```ts twoslash // [!include ~/../src/contracts/ChainSignatureContract.ts] ``` ### Example: EVM Implementation Refer to the EVM contract in the source tree at `src/contracts/evm/ChainSignaturesContract.ts`. # Holding Ethereum Assets from Solana A concrete, end-to-end example of what the [Sign Bidirectional Flow](/architecture/sign-bidirectional) lets you build: a **Solana program** that custodies and moves **USDC living on Ethereum** — with no bridge, no wrapped assets, and no custodian. This exact example ships today as the MPC-custodied ERC-20 vault. ## The scenario A user wants to deposit 100 USDC (an ERC-20 on Ethereum) into your Solana application. Your Solana program: * owns Ethereum addresses through MPC-derived keys — no private key exists anywhere, * lets the user deposit by sending USDC to an Ethereum address the program controls, * credits the user's balance on Solana only after cryptographic proof that the Ethereum transfer succeeded. ## Walkthrough ### 1. Derive the deposit address Your Solana program deterministically controls a whole family of Ethereum addresses (see [Address Derivation](/architecture/sign-bidirectional#address-derivation)): each user gets a personal deposit address, and the vault has its own treasury address. Your client derives them off-chain with signet.js — no keys are created or stored anywhere. ### 2. The user funds it The user sends 100 USDC to their deposit address on Ethereum with any ordinary wallet — MetaMask, an exchange withdrawal, anything. Nothing Signet-specific happens yet: it is a normal ERC-20 balance sitting on an MPC-controlled Ethereum address. ### 3. Request the transfer signature on Solana The user calls the program's deposit entry point. The program builds the exact Ethereum transaction it wants executed — moving the 100 USDC from the user's deposit address into the vault's treasury address, with real fee parameters — records a single-use pending entry for the request, and asks the MPC network for a signature via `sign_bidirectional`. ### 4. The MPC signs, the client broadcasts to Ethereum The MPC network signs the transaction and delivers the signature back on Solana. The client picks it up, assembles the signed transaction, and broadcasts it to Ethereum — the MPC never broadcasts. ### 5. A verified outcome comes back to Solana The MPC observes Ethereum until the transfer confirms, then publishes a signed execution report back on Solana. The program's claim entry point verifies the MPC's signature on that report, confirms the transfer actually succeeded, retires the pending entry, and credits the user 100 USDC of vault balance on Solana. Withdrawals are the mirror image: debit the Solana balance up front, execute a transfer out of the vault's treasury on Ethereum, and refund automatically if the verified report says the transfer failed. ## See it running The walkthrough above tracks the Solana implementation; the same vault ships on every supported source chain: | Source chain | Implementation | | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Solana** | [`solana-contract-examples`](https://github.com/sig-net/solana-contract-examples) — the vault program this walkthrough follows | | **Canton** | [`signet-vault-v1`](https://github.com/sig-net/canton/blob/main/daml-packages/signet-vault-v1/README.md) — live on DevNet and Testnet, with an end-to-end test against the real MPC | | **EVM** | [`Erc20Vault.sol`](https://github.com/sig-net/signet-evm-contract/blob/main/examples/Erc20Vault.sol) — EVM → EVM example with a two-chain e2e test | ## Beyond deposits: swaps, yield, anything Nothing in the flow is transfer-specific — step 3 signs whatever Ethereum transaction your program builds, and step 5 hands back its verified execution result: * **Swaps**: the program submits a trade on a Uniswap-style DEX; the verified outcome reports the exact amount received, which the program credits on Solana — a swap on Ethereum, initiated and settled from Solana. * **Yield**: the program deposits into an Ethereum yield vault; the verified outcome reports the shares received. Exiting the position later works the same way. * **Anything else**: any transaction an Ethereum account can send, your Solana program can initiate — and, crucially, verify the result of on Solana before updating state. The pattern is always the same: build the foreign transaction → `sign_bidirectional` → broadcast → act on the MPC-verified outcome. ## Related Documentation * [Sign Bidirectional Flow](/architecture/sign-bidirectional) — the chain-agnostic lifecycle * [Per-chain documentation](/architecture/sign-bidirectional#per-chain-documentation) — contract/template APIs for each source chain # Staking on Ethereum from Solana A Solana account initiates an ETH stake on Lido — using the plain `sign` method, without the bidirectional flow. This is the fire-and-forget pattern: build the Ethereum transaction, have the MPC sign it, broadcast it, done. Nothing on Solana needs to observe the outcome, because the result (stETH) simply accrues to the MPC-derived Ethereum address you control. Use this pattern whenever the source chain does not need on-chain proof of what happened on the destination chain. If your Solana program must verify the outcome before updating its own state (crediting balances, minting shares), use the [Sign Bidirectional Flow](/architecture/sign-bidirectional) instead — see [Holding Ethereum Assets from Solana](/examples/cross-chain-deposit). ## Setup Connect to Solana, initialize the Chain Signatures contract, and create an EVM chain adapter that uses it for signing: ```ts twoslash // [!include ~/snippets/code/solana/stake-ethereum.ts:import] // ---cut--- // [!include ~/snippets/code/solana/stake-ethereum.ts:setup] ``` ## Derive the staking address Your Solana account controls an Ethereum address derived from the MPC root key — this is the address that will hold ETH and receive the stETH. Fund it with the ETH you want to stake (from a prior withdrawal, a deposit, or any transfer): ```ts twoslash // [!include ~/snippets/code/solana/stake-ethereum.ts:import] // [!include ~/snippets/code/solana/stake-ethereum.ts:setup] // ---cut--- // [!include ~/snippets/code/solana/stake-ethereum.ts:derive-address] ``` ## Build the stake transaction Build the exact Ethereum transaction to execute: a deposit into Lido's staking contract with the ETH value attached. The adapter fills in nonce and gas and returns the hash to sign: ```ts twoslash // [!include ~/snippets/code/solana/stake-ethereum.ts:import] // [!include ~/snippets/code/solana/stake-ethereum.ts:setup] // [!include ~/snippets/code/solana/stake-ethereum.ts:derive-address] // ---cut--- // [!include ~/snippets/code/solana/stake-ethereum.ts:build-stake-tx] ``` ## Sign on Solana, broadcast to Ethereum Request the signature through the Solana Chain Signatures program, assemble the signed transaction, and broadcast it to Ethereum: ```ts twoslash // [!include ~/snippets/code/solana/stake-ethereum.ts:import] // [!include ~/snippets/code/solana/stake-ethereum.ts:setup] // [!include ~/snippets/code/solana/stake-ethereum.ts:derive-address] // [!include ~/snippets/code/solana/stake-ethereum.ts:build-stake-tx] // ---cut--- // [!include ~/snippets/code/solana/stake-ethereum.ts:sign-and-broadcast] ``` That's the whole flow. The stETH balance accrues to `stakingAddress`; when you want to exit, build Lido's withdrawal (or a stETH transfer or swap) the same way and sign it with the same path. ## When to upgrade to bidirectional The plain `sign` flow trusts nothing and verifies nothing on Solana — which is fine here, because no Solana state depends on the stake succeeding. The moment your program needs to act on the result (credit the staker shares on Solana, refund on failure), switch the request to `sign_bidirectional` and verify the MPC's execution report on-chain, exactly as the [deposit example](/examples/cross-chain-deposit) does. ## Related Documentation * [Holding Ethereum Assets from Solana](/examples/cross-chain-deposit) — the verified-outcome version of cross-chain execution * [Sign Bidirectional Flow](/architecture/sign-bidirectional) — when and how outcomes are verified # What You Can Build Sig Network gives an application on one chain its own accounts on every other chain — and, when it matters, cryptographic proof of what happened there. No bridges, no wrapped assets, no custodians: the assets stay native on their home chain, and your application controls them directly. ## Multichain smart account One smart account or multisig on Ethereum that natively holds and moves Bitcoin, assets on Base, Cosmos tokens — any chain, one identity. The account's existing approval process (signers, spending limits, timelocks) governs every action, while the assets themselves never leave their home chains. Treasury operations that today need a bridge, a wrapped asset, or a custodian become ordinary account operations. ## Cross-chain DEX on Solana A Solana exchange taps Ethereum liquidity directly: users trade on Solana, the swap executes on Uniswap, and the venue credits the exact proceeds back to the trader on Solana — with proof the trade really happened. The same venue can put idle inventory to work on Ethereum, staking it or lending it out, all managed from Solana. ## Institutional settlement from Canton Institutions that run on Canton — where privacy, parties, and compliance live on-ledger — can custody and settle public-chain assets without leaving it. Entitlements and approvals stay on Canton; execution happens on Ethereum; every settlement is verified before the books move. This one is live today: the [Canton ERC-20 vault](https://github.com/sig-net/canton/blob/main/daml-packages/signet-vault-v1/README.md) custodies Ethereum assets under Daml-governed rules. *** Everything above composes two capabilities: **execute** (have any transaction signed and sent from an account your app controls on another chain) and **verify** (receive proof of the result back on your own chain before your app acts on it). The two full walkthroughs — [Holding Ethereum Assets from Solana](/examples/cross-chain-deposit) and [Staking on Ethereum from Solana](/examples/solana-stake-ethereum) — show the mechanics. If it can be phrased as "an application on chain A wants something done on chain B — and maybe proof that it happened," it fits. Start with the [Sign Bidirectional Flow](/architecture/sign-bidirectional) for the verified variant, or the [staking example](/examples/solana-stake-ethereum) for fire-and-forget execution. # EVM Contract Functions This page documents the public functions available in the Signet EVM Contract. For contract addresses, see [Contract Addresses](/primitives/contract-addresses). For type definitions, see [Types](/contract-api/evm/types). ## sign Requests a signature from the MPC network on a 32-byte payload. ```solidity function sign(SignRequest memory _request) external payable ``` ### Parameters | Parameter | Type | Description | | ---------- | ---------------------------------------------------- | ------------------------ | | `_request` | [`SignRequest`](/contract-api/evm/types#signrequest) | Signature request struct | ### Requirements * Caller must send at least `getSignatureDeposit()` wei ### Emits * [`SignatureRequested`](/contract-api/evm/events#signaturerequested) ## respond Returns signatures from the MPC network. Called by MPC responders after signature generation. ```solidity function respond(Response[] calldata _responses) external ``` ### Parameters | Parameter | Type | Description | | ------------ | ------------------------------------------------ | ---------------------------- | | `_responses` | [`Response[]`](/contract-api/evm/types#response) | Array of signature responses | ### Emits * [`SignatureResponded`](/contract-api/evm/events#signatureresponded) for each response ## getSignatureDeposit Returns the current deposit amount required for signature requests. ```solidity function getSignatureDeposit() external view returns (uint256) ``` ### Returns | Type | Description | | --------- | ------------------------------ | | `uint256` | Required deposit amount in wei | # EVM Contract Events This page documents the events emitted by the Signet EVM Contract. For type definitions, see [Types](/contract-api/evm/types). ## SignatureRequested Emitted when a signature is requested via the [`sign`](/contract-api/evm/functions#sign) function. ```solidity event SignatureRequested( address sender, bytes32 payload, uint32 keyVersion, uint256 deposit, uint256 chainId, string path, string algo, string dest, string params ); ``` ### Fields | Field | Type | Indexed | Description | | ------------ | --------- | ------- | --------------------------------- | | `sender` | `address` | No | Address of the requester | | `payload` | `bytes32` | No | 32-byte data to be signed | | `keyVersion` | `uint32` | No | MPC key version | | `deposit` | `uint256` | No | Deposit amount paid | | `chainId` | `uint256` | No | Chain ID where request originated | | `path` | `string` | No | Derivation path for key | | `algo` | `string` | No | Signing algorithm | | `dest` | `string` | No | Response destination | | `params` | `string` | No | Additional parameters | ## SignatureResponded Emitted when the MPC network returns a signature via the [`respond`](/contract-api/evm/functions#respond) function. ```solidity event SignatureResponded( bytes32 indexed requestId, address responder, Signature signature ); ``` ### Fields | Field | Type | Indexed | Description | | ----------- | ------------------------------------------------ | ------- | -------------------------------------- | | `requestId` | `bytes32` | Yes | Identifier linking response to request | | `responder` | `address` | No | Address of MPC responder | | `signature` | [`Signature`](/contract-api/evm/types#signature) | No | ECDSA signature | # EVM Contract Types This page documents the data types used in the Signet EVM Contract. ## SignRequest Request structure for initiating a signature via [`sign`](/contract-api/evm/functions#sign). ```solidity struct SignRequest { bytes32 payload; string path; uint32 keyVersion; string algo; string dest; string params; } ``` ### Fields | Field | Type | Description | | ------------ | --------- | ------------------------------------------------ | | `payload` | `bytes32` | 32-byte data to be signed (typically a hash) | | `path` | `string` | Derivation path for the signing key | | `keyVersion` | `uint32` | Version of the MPC key to use | | `algo` | `string` | Signing algorithm (optional, default: secp256k1) | | `dest` | `string` | Response destination chain (optional) | | `params` | `string` | Additional parameters as JSON (optional) | ## Response Response structure containing a signature for a request. ```solidity struct Response { bytes32 requestId; Signature signature; } ``` ### Fields | Field | Type | Description | | ----------- | ------------------------- | ---------------------------------- | | `requestId` | `bytes32` | Identifier of the original request | | `signature` | [`Signature`](#signature) | ECDSA signature for the request | ## Signature ECDSA signature structure returned by the MPC network. ```solidity struct Signature { AffinePoint bigR; uint256 s; uint8 recoveryId; } ``` ### Fields | Field | Type | Description | | ------------ | ----------------------------- | -------------------------------------------- | | `bigR` | [`AffinePoint`](#affinepoint) | R point of the ECDSA signature | | `s` | `uint256` | s scalar of the signature | | `recoveryId` | `uint8` | Recovery ID for public key recovery (0 or 1) | ## AffinePoint Represents a point on the secp256k1 elliptic curve. ```solidity struct AffinePoint { uint256 x; uint256 y; } ``` ### Fields | Field | Type | Description | | ----- | --------- | ------------------------- | | `x` | `uint256` | x coordinate of the point | | `y` | `uint256` | y coordinate of the point | ### Notes * For the signature's R point, `x` is typically used as the `r` value in RSV format # Solana Program (Rust Docs) The Signet Solana Program is written in Rust using the Anchor framework. The complete API reference is available as generated Rust documentation. ## Documentation The full Rust documentation for the Solana program is available at: **[Signet Solana Program Rust Docs](https://docs.rs/chain-signatures-solana-program/latest/chain_signatures/)** The Rust docs include: * **Instructions** - All program instructions with parameters and examples * **Events** - Event structures emitted by the program * **Types** - Data structures like `Signature`, `AffinePoint`, `ProgramState` * **Error codes** - All possible error conditions For the chain-agnostic signing lifecycle, see the [Sign Bidirectional Flow](/architecture/sign-bidirectional). ## Program Address For program addresses, see [Program Addresses](/primitives/contract-addresses). ## Source Code The source code is available on GitHub: * [solana-signet-program](https://github.com/sig-net/solana-signet-program) # Canton Signer (Daml Docs) On Canton, signature requests do not go through a deployed contract at an address. They go through **Daml templates** — the `Signer` from the `signet-signer-v1` package — authorized by Canton parties and disclosed to consumer contracts. MPC responses are delivered as Canton contracts and verified **on-ledger** with `secp256k1WithEcdsaOnly`. The flow itself is the standard [Sign Bidirectional Flow](/architecture/sign-bidirectional); only the API surface is Canton-specific. ## Documentation The complete Canton documentation lives in the [sig-net/canton](https://github.com/sig-net/canton) repository: * **[Repository overview](https://github.com/sig-net/canton#readme)** - Architecture, package layout, and where to start * **[signet-signer-v1 README](https://github.com/sig-net/canton/blob/main/daml-packages/signet-signer-v1/README.md)** - Authority model and integrator lifecycle, with the full template API in [API.md](https://github.com/sig-net/canton/blob/main/daml-packages/signet-signer-v1/API.md) and the integrator checklist in [SECURITY.md](https://github.com/sig-net/canton/blob/main/daml-packages/signet-signer-v1/SECURITY.md) * **[signet-vault-v1 README](https://github.com/sig-net/canton/blob/main/daml-packages/signet-vault-v1/README.md)** - The worked example to model your consumer on: MPC-custodied ERC-20 vault (deposit / claim / withdraw / refund), meant to be copied as source * **[INTEGRATORS.md](https://github.com/sig-net/canton/blob/main/INTEGRATORS.md)** - Deploying your consumer from your own Canton node: release DARs and package-id rules, vetting, client wiring, DevNet/TestNet go-live * **[canton-sig TypeScript client](https://github.com/sig-net/canton/tree/main/ts-packages/canton-sig)** - Client library for driving the protocol over the Canton JSON Ledger API ([npm](https://www.npmjs.com/package/canton-sig)) ## Key Differences from Other Chains * **No contract address**: integrators receive the `Signer` as a disclosed-contract envelope from the operator's disclosure endpoint, not by address lookup. See [Contract Addresses](/primitives/contract-addresses) for the per-environment root public keys. * **KDF chain ID**: key derivation uses `canton:global` (see `KDF_CHAIN_IDS` in signet.js), with `sender` bound to the hash of the on-ledger operator set. * **On-ledger verification**: the response signature over `keccak256(requestId || serializedOutput)` is checked inside the consumer's Daml choice via `secp256k1WithEcdsaOnly`, using the response child key derived with the fixed path `"canton response key"`. ## Source Code * [sig-net/canton](https://github.com/sig-net/canton) # EVM Chain The EVM (Ethereum Virtual Machine) chain implementation in Signet.js provides support for all EVM-compatible networks, including Ethereum, Binance Smart Chain, Polygon, Arbitrum, and more. ## Overview The EVM implementation allows you to: * Derive addresses and public keys * Check balances * Prepare, sign, and broadcast transactions * Sign messages (EIP-191) * Sign typed data (EIP-712) ## Complete Transaction Example Below is a complete example of sending a transaction on an EVM chain using Signet.js: ```ts twoslash import { chainAdapters } from 'signet.js' // [!include ~/snippets/code/evm/contract.ts] const evm = new chainAdapters.evm.EVM({ publicClient, contract: evmChainSigContract, }) const path = 'eth' const predecessorId = walletClient.account.address const keyVersion = 1 const { address: from, publicKey } = await evm.deriveAddressAndPublicKey( predecessorId, path, keyVersion ) const { balance, decimals } = await evm.getBalance(from) const { transaction, hashesToSign } = await evm.prepareTransactionForSigning({ from: from as `0x${string}`, to: '0x4174678c78fEaFd778c1ff319D5D326701449b25', value: 1n, // Amount in wei (1 wei in this example) }) const rsvSignature = await evmChainSigContract.sign({ payload: hashesToSign[0], path, key_version: keyVersion, }) const tx = evm.finalizeTransactionSigning({ transaction, rsvSignatures: [rsvSignature], }) const txHash = await evm.broadcastTx(tx) ``` ### Supported Networks You can use any EVM-compatible network by providing the appropriate client: ```ts twoslash // [!include ~/snippets/code/contract.ts] // ---cut--- import { chainAdapters } from 'signet.js' import { polygon, bsc, mainnet } from 'viem/chains' // Ethereum Mainnet const clientEthereum = createPublicClient({ chain: mainnet, transport: http(), }) const ethereumChain = new chainAdapters.evm.EVM({ publicClient: clientEthereum, contract: evmChainSigContract, }) // Polygon (Matic) const clientPolygon = createPublicClient({ chain: polygon, transport: http(), }) const polygonChain = new chainAdapters.evm.EVM({ publicClient: clientPolygon, contract: evmChainSigContract, }) // Binance Smart Chain const clientBSC = createPublicClient({ chain: bsc, transport: http(), }) const bscChain = new chainAdapters.evm.EVM({ publicClient: clientBSC, contract: evmChainSigContract, }) ``` ## Working Examples See the test suite for complete, runnable examples: * [Unit tests](https://github.com/sig-net/signet.js/blob/main/tests/unit/evm/EVM.test.ts) — message signing, typed data signing, and transaction signing * [E2E tests](https://github.com/sig-net/signet.js/blob/main/tests/e2e/evm/EVM.e2e.test.ts) — MPC signing on Sepolia, broadcast on local hardhat # prepareTransactionForSigning The `prepareTransactionForSigning` method prepares an EVM transaction for MPC signing by adding necessary fields like gas estimation, nonce, and chain ID. ## Usage ```ts twoslash // [!include ~/snippets/code/chains.ts] // ---cut--- const { transaction, hashesToSign } = await evmChain.prepareTransactionForSigning({ from: '0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199' as `0x${string}`, to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', value: 1n, // 1 wei data: '0x', // Empty data for a simple ETH transfer }) ``` ## Parameters | Parameter | Type | Description | | -------------------- | ----------------------- | ------------------------------ | | `transactionRequest` | `EVMTransactionRequest` | The transaction request object | ## Returns | Property | Type | Description | | -------------- | ------------------------ | ------------------------------------------------- | | `transaction` | `EVMUnsignedTransaction` | The prepared transaction with all required fields | | `hashesToSign` | `HashToSign[]` | Array of hash(es) to be signed by the MPC network | # finalizeTransactionSigning The `finalizeTransactionSigning` method adds a signature to an unsigned transaction, producing a signed transaction that can be broadcast to the network. ## Usage ```ts twoslash // [!include ~/snippets/code/chains.ts] const { transaction, hashesToSign } = await evmChain.prepareTransactionForSigning({ to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', from: '0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199', value: 1n, data: '0x', }) // ---cut--- import { RSVSignature } from 'signet.js' const rsvSignatures: RSVSignature[] = [ { r: '123...', s: '123...', v: 27, }, ] const signedTx = evmChain.finalizeTransactionSigning({ transaction, rsvSignatures, }) ``` ## Parameters | Parameter | Type | Description | | --------------- | ------------------------ | ------------------------------------------------------------------- | | `transaction` | `EVMUnsignedTransaction` | The unsigned transaction prepared by `prepareTransactionForSigning` | | `rsvSignatures` | `RSVSignature[]` | Array of RSV signatures from the MPC network | ## Returns | Type | Description | | --------------------- | ------------------------------------------------------- | | `` `0x02${string}` `` | The serialized signed transaction (EIP-1559 hex string) | # prepareMessageForSigning The `prepareMessageForSigning` method prepares a message for MPC signing according to EIP-191 (Ethereum signed message standard). ## Usage ```ts twoslash // [!include ~/snippets/code/chains.ts] // ---cut--- const message: chainAdapters.evm.EVMMessage = 'Sign this message' const { hashToSign } = await evmChain.prepareMessageForSigning(message) ``` ## Parameters | Parameter | Type | Description | | --------- | ------------ | ------------------------ | | `message` | `EVMMessage` | The message to be signed | ## Returns | Property | Type | Description | | ------------ | ------------ | ---------------------------------------- | | `hashToSign` | `HashToSign` | The hash to be signed by the MPC network | # finalizeMessageSigning The `finalizeMessageSigning` method assembles the message signature ## Usage ```ts twoslash // [!include ~/snippets/code/chains.ts] // ---cut--- import { RSVSignature } from 'signet.js' const rsvSignature: RSVSignature = { r: '123...', s: '123...', v: 27, } const signature = evmChain.finalizeMessageSigning({ rsvSignature, }) ``` ## Parameters | Parameter | Type | Description | | -------------- | -------------- | -------------------------------------- | | `rsvSignature` | `RSVSignature` | The RSV signature from the MPC network | ## Returns | Type | Description | | --------------------------- | -------------------------------------------- | | `Hex` (`` `0x${string}` ``) | The signature in hex format (with 0x prefix) | # prepareTypedDataForSigning The `prepareTypedDataForSigning` method prepares EIP-712 typed data for MPC signing. ## Usage ```ts twoslash // [!include ~/snippets/code/chains.ts] // ---cut--- const typedData: chainAdapters.evm.EVMTypedData = { domain: { name: 'My dApp', version: '1', chainId: 1, verifyingContract: '0x1234567890123456789012345678901234567890', }, types: { Person: [ { name: 'name', type: 'string' }, { name: 'wallet', type: 'address' }, ], }, primaryType: 'Person', message: { name: 'Alice', wallet: '0x1234567890123456789012345678901234567890', }, } const { hashToSign } = await evmChain.prepareTypedDataForSigning(typedData) ``` ## Parameters | Parameter | Type | Description | | ------------------ | -------------- | ------------------------------------------ | | `typedDataRequest` | `EVMTypedData` | The EIP-712 typed data object to be signed | ## Returns | Property | Type | Description | | ------------ | ------------ | ---------------------------------------- | | `hashToSign` | `HashToSign` | The hash to be signed by the MPC network | # finalizeTypedDataSigning The `finalizeTypedDataSigning` method adds a signature to EIP-712 typed data, producing a signature that can be verified by Ethereum-compatible systems. ## Usage ```ts twoslash // [!include ~/snippets/code/chains.ts] // ---cut--- import { RSVSignature } from 'signet.js' const rsvSignature: RSVSignature = { r: '123...', s: '123...', v: 27, } // Assuming you have a signature from the MPC network const signature = evmChain.finalizeTypedDataSigning({ rsvSignature, }) ``` ## Parameters | Parameter | Type | Description | | -------------- | -------------- | -------------------------------------- | | `rsvSignature` | `RSVSignature` | The RSV signature from the MPC network | ## Returns | Type | Description | | --------------------------- | -------------------------------------------- | | `Hex` (`` `0x${string}` ``) | The signature in hex format (with 0x prefix) | # Bitcoin Chain The Bitcoin chain implementation in Signet.js provides support for both Bitcoin mainnet and testnet networks, with a focus on P2WPKH (Native SegWit) transactions. ## Overview The Bitcoin implementation allows you to: * Generate addresses and public keys * Check balances * Prepare, sign, and broadcast transactions * Work with UTXOs (Unspent Transaction Outputs) ## Complete Transaction Example Below is a complete example of sending a transaction on the Bitcoin network using Signet.js: ```ts twoslash import { chainAdapters } from 'signet.js' // [!include ~/snippets/code/evm/contract.ts] const btcRpcAdapter = new chainAdapters.btc.BTCRpcAdapters.Mempool( 'https://mempool.space/api' ) const bitcoin = new chainAdapters.btc.Bitcoin({ network: 'testnet', contract: evmChainSigContract, btcRpcAdapter, }) const path = 'btc' const predecessorId = walletClient.account.address const keyVersion = 1 const { address: from, publicKey } = await bitcoin.deriveAddressAndPublicKey( predecessorId, path, keyVersion ) const { balance, decimals } = await bitcoin.getBalance(from) const { transaction, hashesToSign } = await bitcoin.prepareTransactionForSigning({ from, to: 'tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx', value: '0.001', // 0.001 BTC publicKey, }) const rsvSignature = await evmChainSigContract.sign({ payload: hashesToSign[0], path, key_version: keyVersion, }) const tx = bitcoin.finalizeTransactionSigning({ transaction, rsvSignatures: [rsvSignature], }) const txHash = await bitcoin.broadcastTx(tx) ``` ### Supported Networks You can use different Bitcoin networks by specifying the network parameter: ```ts twoslash // [!include ~/snippets/code/contract.ts] // ---cut--- import { chainAdapters } from 'signet.js' const btcRpcAdapter = new chainAdapters.btc.BTCRpcAdapters.Mempool( 'https://mempool.space/api' ) // Bitcoin Mainnet const mainnetBitcoin = new chainAdapters.btc.Bitcoin({ network: 'mainnet', contract: evmChainSigContract, btcRpcAdapter, }) // Bitcoin Testnet const testnetBitcoin = new chainAdapters.btc.Bitcoin({ network: 'testnet', contract: evmChainSigContract, btcRpcAdapter, }) // Bitcoin Regtest (for local development) const regtestBitcoin = new chainAdapters.btc.Bitcoin({ network: 'regtest', contract: evmChainSigContract, btcRpcAdapter, }) ``` ## RPC Adapter The RPC adapter is a class that provides an interface for interacting with the Bitcoin network. It handles essential operations such as: * Fetching UTXOs (Unspent Transaction Outputs) * Retrieving transaction details * Getting current network fees * Broadcasting transactions * Querying address balances The adapter abstracts away the complexity of different Bitcoin API providers, allowing you to easily switch between services like Mempool.space, BlockCypher, or your own Bitcoin node. For detailed implementation and configuration options, see the [RPC Adapter](/signetjs/chain-adapters/bitcoin/btc-rpc-adapter) documentation. ## Working Examples See the test suite for complete, runnable examples: * [Unit tests](https://github.com/sig-net/signet.js/blob/main/tests/unit/btc/Bitcoin.test.ts) — single and multi-input transactions with mocked signing * [E2E tests](https://github.com/sig-net/signet.js/blob/main/tests/e2e/btc/BTC.e2e.test.ts) — MPC signing on Sepolia, broadcast on regtest ## Types The following types are used on the Bitcoin chain: ```ts twoslash // [!include ~/../src/chain-adapters/Bitcoin/types.ts] ``` # Implementing a Bitcoin RPC Adapter This guide explains how to implement a custom RPC adapter for interacting with Bitcoin nodes or services. ## Overview The `BTCRpcAdapter` provides an abstraction layer for Bitcoin-specific operations like UTXO management, balance queries, and transaction broadcasting. You might want to implement a custom adapter when: * Using a different Bitcoin API provider * Implementing specialized UTXO selection strategies ## Base Class In order to implement a custom adapter, you need to implement the `BTCRpcAdapter` abstract class: ```ts twoslash // [!include ~/../src/chain-adapters/Bitcoin/BTCRpcAdapter/BTCRpcAdapter.ts] ``` ### 2. Example There is an example implementation of the `BTCRpcAdapter` abstract class in the `Mempool` class: ```ts twoslash // [!include ~/../src/chain-adapters/Bitcoin/BTCRpcAdapter/Mempool/Mempool.ts] ``` # prepareTransactionForSigning The `prepareTransactionForSigning` method prepares a Bitcoin transaction for MPC signing by creating a PSBT (Partially Signed Bitcoin Transaction) and computing the hash that needs to be signed. ## Usage ```ts twoslash // [!include ~/snippets/code/chains.ts] // ---cut--- const { transaction, hashesToSign } = await btcChain.prepareTransactionForSigning({ from: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', to: 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4', value: '0.001', // 0.001 BTC publicKey: '0235a3...', }) ``` ## Parameters | Parameter | Type | Description | | -------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------- | | `transactionRequest` | `BTCTransactionRequest` | The transaction request object containing publicKey and either `{inputs, outputs}` or `{from, to, value}` | ## Returns | Property | Type | Description | | -------------- | ------------------------ | -------------------------------------- | | `transaction` | `BTCUnsignedTransaction` | Object containing the unsigned PSBT | | `hashesToSign` | `number[][]` | Array of hashes that need to be signed | # finalizeTransactionSigning The `finalizeTransactionSigning` method adds signatures to a Bitcoin PSBT and finalizes it for broadcasting to the network. ## Usage ```ts twoslash // [!include ~/snippets/code/chains.ts] // ---cut--- const { transaction, hashesToSign } = await btcChain.prepareTransactionForSigning({ from: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', to: 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4', value: '0.001', // 0.001 BTC publicKey: '0235a3...', }) // ---cut--- import { RSVSignature } from 'signet.js' const rsvSignatures: RSVSignature[] = [ { r: '123...', s: '123...', v: 27, }, ] const signedTx = btcChain.finalizeTransactionSigning({ transaction, rsvSignatures, }) ``` ## Parameters | Parameter | Type | Description | | --------------- | ------------------------ | ---------------------------------------------------------- | | `transaction` | `BTCUnsignedTransaction` | The unsigned PSBT returned by prepareTransactionForSigning | | `rsvSignatures` | `RSVSignature[]` | Array of RSV signatures from the MPC network | ## Returns | Type | Description | | -------- | ----------------------------------------------------------------------- | | `string` | The serialized signed transaction in hex format, ready for broadcasting | # Cosmos Chain The Cosmos chain implementation in Signet.js provides support for Cosmos SDK-based networks (Cosmos Hub, Osmosis, etc.) with a focus on standard transactions and IBC transfers. ## Overview The Cosmos implementation allows you to: * Generate addresses and public keys * Check balances * Prepare, sign, and broadcast transactions * Support for various Cosmos SDK message types ## Complete Transaction Example Below is a complete example of sending a transaction on a Cosmos chain using Signet.js: ```ts twoslash import { chainAdapters } from 'signet.js' // [!include ~/snippets/code/evm/contract.ts] const cosmos = new chainAdapters.cosmos.Cosmos({ chainId: 'cosmoshub-4', contract: evmChainSigContract, // Optional: override chain-registry endpoints // endpoints: { rpcUrl: 'http://localhost:26657', restUrl: 'http://localhost:1317' }, }) const path = 'cosmos' const predecessorId = walletClient.account.address const keyVersion = 1 const { address: from, publicKey } = await cosmos.deriveAddressAndPublicKey( predecessorId, path, keyVersion ) const { balance, decimals } = await cosmos.getBalance(from) const { transaction, hashesToSign } = await cosmos.prepareTransactionForSigning( { address: from, publicKey, messages: [ { typeUrl: '/cosmos.bank.v1beta1.MsgSend', value: { fromAddress: from, toAddress: 'cosmos1jq304cthpx0lwhpqzrdjrcza559ukyy347xu57', amount: [ { denom: 'uatom', amount: '1000000', // 1 ATOM (1,000,000 uatom) }, ], }, }, ], memo: 'Sent via Signet.js', } ) const rsvSignature = await evmChainSigContract.sign({ payload: hashesToSign[0], path, key_version: keyVersion, }) const tx = cosmos.finalizeTransactionSigning({ transaction, rsvSignatures: [rsvSignature], }) const txHash = await cosmos.broadcastTx(tx) ``` ### Supported Networks You can use any Cosmos SDK-based network by providing the appropriate chain ID: ```ts twoslash // [!include ~/snippets/code/contract.ts] // ---cut--- import { chainAdapters } from 'signet.js' // Cosmos Hub const cosmosHub = new chainAdapters.cosmos.Cosmos({ chainId: 'cosmoshub-4', contract: evmChainSigContract, }) // Osmosis const osmosis = new chainAdapters.cosmos.Cosmos({ chainId: 'osmosis-1', contract: evmChainSigContract, }) // Juno const juno = new chainAdapters.cosmos.Cosmos({ chainId: 'juno-1', contract: evmChainSigContract, }) ``` ## Working Examples See the test suite for complete, runnable examples: * [Unit tests](https://github.com/sig-net/signet.js/blob/main/tests/unit/cosmos/Cosmos.test.ts) — bank send with mocked signing, including custom `endpoints` * [E2E tests](https://github.com/sig-net/signet.js/blob/main/tests/e2e/cosmos/Cosmos.e2e.test.ts) — MPC signing on Sepolia, broadcast on local gaiad ## Types The following types are used on the Cosmos chain: ```ts twoslash // [!include ~/../src/chain-adapters/Cosmos/types.ts] ``` # prepareTransactionForSigning The `prepareTransactionForSigning` method prepares a Cosmos transaction for MPC signing by creating the transaction structure and computing the hash that needs to be signed. ## Usage ```ts twoslash // [!include ~/snippets/code/chains.ts] // ---cut--- const { transaction, hashesToSign } = await cosmosChain.prepareTransactionForSigning({ address: 'cosmos1...', publicKey: '0350e8...', messages: [ { typeUrl: '/cosmos.bank.v1beta1.MsgSend', value: { fromAddress: 'cosmos1...', toAddress: 'cosmos1...', amount: [{ denom: 'uatom', amount: '1000000' }], }, }, ], memo: 'Token transfer', }) ``` ## Parameters | Parameter | Type | Description | | -------------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------- | | `transactionRequest` | `CosmosTransactionRequest` | The transaction request object containing address, publicKey, messages, and optional memo and fee settings | ## Returns | Property | Type | Description | | -------------- | --------------------------- | ------------------------------------------ | | `transaction` | `CosmosUnsignedTransaction` | Object containing the unsigned transaction | | `hashesToSign` | `number[][]` | Array of hashes that need to be signed | # finalizeTransactionSigning The `finalizeTransactionSigning` method adds signatures to a Cosmos transaction and finalizes it for broadcasting to the network. ## Usage ```ts twoslash // [!include ~/snippets/code/chains.ts] const { transaction, hashesToSign } = await cosmosChain.prepareTransactionForSigning({ address: 'cosmos1...', publicKey: '0350e8...', messages: [ { typeUrl: '/cosmos.bank.v1beta1.MsgSend', value: { fromAddress: 'cosmos1...', toAddress: 'cosmos1...', amount: [{ denom: 'uatom', amount: '1000000' }], }, }, ], memo: 'Token transfer', }) // ---cut--- import { RSVSignature } from 'signet.js' const rsvSignatures: RSVSignature[] = [ { r: '123...', s: '123...', v: 27, }, ] const signedTx = cosmosChain.finalizeTransactionSigning({ transaction, rsvSignatures, }) ``` ## Parameters | Parameter | Type | Description | | --------------- | --------------------------- | ----------------------------------------------------------------- | | `transaction` | `CosmosUnsignedTransaction` | The unsigned transaction object from prepareTransactionForSigning | | `rsvSignatures` | `RSVSignature[]` | Array of RSV signatures from the MPC network | ## Returns | Type | Description | | -------- | ----------------------------------------------------------------------- | | `string` | The serialized signed transaction in hex format, ready for broadcasting | # deriveAddressAndPublicKey The `deriveAddressAndPublicKey` method derives an address and public key from a predecessor ID and derivation path. ## Usage ```ts twoslash // [!include ~/snippets/code/chains.ts] // ---cut--- const predecessor = '0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199' const path = 'my_derivation_path' const keyVersion = 1 const { address, publicKey } = await evmChain.deriveAddressAndPublicKey( predecessor, path, keyVersion ) ``` ## Parameters | Parameter | Type | Description | | ------------- | -------- | ---------------------------------------------------- | | `predecessor` | `string` | The wallet/contract address requesting the signature | | `path` | `string` | The derivation path to use | | `keyVersion` | `number` | The version of the MPC key to use | ## Returns | Property | Type | Description | | ----------- | -------- | ---------------------------------------------------------------------------- | | `address` | `string` | The derived address | | `publicKey` | `string` | The derived public key (compressed for Bitcoin/Cosmos, uncompressed for EVM) | ## Working Examples * [EVM E2E](https://github.com/sig-net/signet.js/blob/main/tests/e2e/evm/EVM.e2e.test.ts) — derive EVM address from MPC key * [Bitcoin E2E](https://github.com/sig-net/signet.js/blob/main/tests/e2e/btc/BTC.e2e.test.ts) — derive P2WPKH address from MPC key * [Cosmos E2E](https://github.com/sig-net/signet.js/blob/main/tests/e2e/cosmos/Cosmos.e2e.test.ts) — derive bech32 address from MPC key # getBalance The `getBalance` method retrieves the native token balance of an address. ## Usage ```ts twoslash // [!include ~/snippets/code/chains.ts] // ---cut--- const { balance, decimals } = await evmChain.getBalance( '0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199' ) ``` ## Parameters | Parameter | Type | Description | | --------- | -------- | ------------------------------------ | | `address` | `string` | The address to check the balance for | ## Returns | Property | Type | Description | | ---------- | -------- | ------------------------------------------------------------------- | | `balance` | `bigint` | The balance in the chain's base units (wei for Ethereum) | | `decimals` | `number` | The number of decimals used to format the balance (18 for Ethereum) | ## Working Examples * [Cosmos](https://github.com/sig-net/signet.js/blob/main/tests/unit/cosmos/Cosmos.test.ts) — check balance after a bank send * [EVM E2E](https://github.com/sig-net/signet.js/blob/main/tests/e2e/evm/EVM.e2e.test.ts) — verify destination balance after transfer # broadcastTx The `broadcastTx` method broadcasts a signed transaction. ## Usage ```ts twoslash // [!include ~/snippets/code/chains.ts] // ---cut--- const txHash = await evmChain.broadcastTx('0x02f8...') ``` ## Parameters | Parameter | Type | Description | | -------------- | -------- | --------------------------------- | | `txSerialized` | `string` | The serialized signed transaction | ## Returns | Type | Description | | -------- | -------------------- | | `string` | The transaction hash | ## Working Examples * [EVM](https://github.com/sig-net/signet.js/blob/main/tests/unit/evm/EVM.test.ts) — broadcast an EIP-1559 transaction * [Bitcoin](https://github.com/sig-net/signet.js/blob/main/tests/unit/btc/Bitcoin.test.ts) — broadcast single and multi-input transactions * [Cosmos](https://github.com/sig-net/signet.js/blob/main/tests/unit/cosmos/Cosmos.test.ts) — broadcast a bank send transaction # EVM Chain Signatures Contract The EVM Chain Signatures Contract provides an implementation of the abstract `ChainSignatureContract` for EVM-compatible blockchains. It allows you to interact with the Chain Signatures contract deployed on EVM chains. ## Usage ```ts twoslash import { contracts, constants } from 'signet.js' import { createPublicClient, createWalletClient, http } from 'viem' import { sepolia } from 'viem/chains' const publicClient = createPublicClient({ chain: sepolia, transport: http(), }) const walletClient = createWalletClient({ chain: sepolia, transport: http(), }) const contract = new contracts.evm.ChainSignatureContract({ publicClient, walletClient, contractAddress: constants.CONTRACT_ADDRESSES.ETHEREUM .TESTNET_DEV as `0x${string}`, }) ``` ## Parameters | Parameter | Type | Description | | ---------------------- | --------------- | ------------------------------------------------------------------------------- | | `args` | `object` | Configuration options for the contract | | `args.publicClient` | `PublicClient` | A Viem PublicClient instance for reading from the blockchain | | `args.walletClient` | `WalletClient` | A Viem WalletClient instance for sending transactions | | `args.contractAddress` | `Hex` | The address of the deployed ChainSignatures contract | | `args.rootPublicKey?` | `RootPublicKey` | Optional root public key. If not provided, it will be derived from the contract | ## Working Examples * [Unit tests](https://github.com/sig-net/signet.js/blob/main/tests/unit/evm/EVM.test.ts) — contract setup with hardhat * [E2E tests](https://github.com/sig-net/signet.js/blob/main/tests/e2e/evm/EVM.e2e.test.ts) — contract setup with Sepolia # getCurrentSignatureDeposit Retrieves the current signature deposit amount required by the contract. ## Usage ```ts twoslash // [!include ~/snippets/code/contract.ts] // ---cut--- const deposit = await evmChainSigContract.getCurrentSignatureDeposit() ``` ## Returns | Type | Description | | ----------------- | ------------------------------------------- | | `Promise` | The current signature deposit amount in wei | # getDerivedPublicKey Derives a public key from a predecessor ID and derivation path. ## Usage ```ts twoslash // [!include ~/snippets/code/contract.ts] // ---cut--- const publicKey = await evmChainSigContract.getDerivedPublicKey({ path: 'my_derivation_path', predecessor: '0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199', keyVersion: 1, }) ``` ## Parameters | Parameter | Type | Description | | ------------------ | -------- | ---------------------------------------------------- | | `args` | `object` | Arguments for deriving the public key | | `args.path` | `string` | The derivation path to use | | `args.predecessor` | `string` | The wallet/contract address requesting the signature | | `args.keyVersion` | `number` | The version of the MPC key to use | ## Returns | Type | Description | | --------------------------------- | -------------------------------------------------- | | `Promise` | The derived public key in SEC1 uncompressed format | # getPublicKey Retrieves the root public key from the contract. ## Usage ```ts twoslash // [!include ~/snippets/code/contract.ts] // ---cut--- const publicKey = await evmChainSigContract.getPublicKey() ``` ## Returns | Type | Description | | --------------------------------- | ----------------------------------------------- | | `Promise` | The root public key in SEC1 uncompressed format | # getLatestKeyVersion Retrieves the latest key version from the contract. ## Usage ```ts twoslash // [!include ~/snippets/code/contract.ts] // ---cut--- const keyVersion = await evmChainSigContract.getLatestKeyVersion() ``` ## Returns | Type | Description | | ----------------- | ---------------------- | | `Promise` | The latest key version | # sign Signs data using the Chain Signatures contract. ## Usage ```ts twoslash // [!include ~/snippets/code/contract.ts] // ---cut--- const signature = await evmChainSigContract.sign({ path: 'my_derivation_path', payload: Array.from(new Uint8Array(32).fill(1)), // 32-byte array key_version: 1, }) ``` ## Parameters | Parameter | Type | Description | | ------------------------------------------- | ------------- | ------------------------------------------------------------ | | `args` | `SignArgs` | Arguments for signing data | | `args.path` | `string` | The derivation path to use | | `args.payload` | `number[]` | The data to sign as an array of 32 bytes | | `args.key_version` | `number` | Version of the key to use | | `options?` | `SignOptions` | Optional configuration for signing | | `options.sign.algo?` | `string` | Optional algorithm to use for signing | | `options.sign.dest?` | `string` | Optional destination for the signature | | `options.sign.params?` | `string` | Optional parameters for the signing process | | `options.retry.delay?` | `number` | Delay between retries in milliseconds (default: 5000) | | `options.retry.retryCount?` | `number` | Number of retries to attempt (default: 12) | | `options.transaction?` | `object` | Optional transaction overrides for the signing contract call | | `options.transaction.gas?` | `bigint` | Gas limit for the signing transaction | | `options.transaction.maxFeePerGas?` | `bigint` | Maximum fee per gas (EIP-1559) | | `options.transaction.maxPriorityFeePerGas?` | `bigint` | Maximum priority fee per gas (EIP-1559) | | `options.transaction.nonce?` | `number` | Nonce for the signing transaction | ## Returns | Type | Description | | ----------------------- | --------------------------- | | `Promise` | The signature in RSV format | ## Working Examples * [E2E tests](https://github.com/sig-net/signet.js/blob/main/tests/e2e/evm/EVM.e2e.test.ts) — real MPC signing with retry options # Solana Chain Signatures Contract The Solana Chain Signatures Contract provides an implementation of the abstract `ChainSignatureContract` for Solana-based blockchains using Anchor and Solana's Web3.js. It enables interaction with Chain Signatures programs deployed on Solana. ## Usage ```ts twoslash import { contracts, constants } from 'signet.js' import { AnchorProvider, Wallet } from '@coral-xyz/anchor' import { Connection, PublicKey, Keypair } from '@solana/web3.js' const connection = new Connection('https://api.devnet.solana.com') const wallet = new Wallet(/* Use your actual keypair here */ Keypair.generate()) const provider = new AnchorProvider( connection, wallet, AnchorProvider.defaultOptions() ) const contract = new contracts.solana.ChainSignatureContract({ provider, programId: new PublicKey(constants.CONTRACT_ADDRESSES.SOLANA.TESTNET_DEV), }) ``` ### With Custom Configuration ```ts twoslash import { contracts, constants } from 'signet.js' import { AnchorProvider, Wallet, type Idl } from '@coral-xyz/anchor' import { Connection, PublicKey, Keypair } from '@solana/web3.js' // Your custom IDL, e.g. loaded from ./custom-idl.json declare const customIdl: contracts.solana.ChainSignaturesProject & Idl const connection = new Connection('https://api.devnet.solana.com') const wallet = new Wallet(Keypair.generate()) const provider = new AnchorProvider( connection, wallet, AnchorProvider.defaultOptions() ) const contract = new contracts.solana.ChainSignatureContract({ provider, programId: new PublicKey(constants.CONTRACT_ADDRESSES.SOLANA.TESTNET_DEV), config: { rootPublicKey: 'secp256k1:...', requesterAddress: 'Dewq9xyD1MZi1rE588XZFvK7uUqkcHLgCnDsn9Ns4H9M', idl: customIdl, }, }) ``` ## Parameters | Parameter | Type | Description | | -------------------------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | | `args` | `object` | Configuration options for the contract | | `args.provider` | `AnchorProvider` | Anchor provider instance for interacting with Solana | | `args.programId` | `string \| PublicKey` | The program ID of the deployed ChainSignatures program | | `args.config?` | `object` | Optional configuration object | | `args.config.rootPublicKey?` | `RootPublicKey` | Optional root public key; derived from program ID if not provided | | `args.config.requesterAddress?` | `string` | Optional requester wallet address. If not provided, defaults to the wallet address. Fee payer is always the wallet address. | | `args.config.disableRetryOnRateLimit?` | `boolean` | If true, disables @solana/web3.js automatic retry on 429 responses. | | `args.config.idl?` | `ChainSignaturesProject & Idl` | Optional custom IDL. If not provided, the default ChainSignatures IDL will be used | ## Working Examples * [Integration tests](https://github.com/sig-net/signet.js/blob/main/tests/integration/solana/Solana.integration.test.ts) — contract setup and signing on devnet # getCurrentSignatureDeposit Retrieves the current signature deposit required by the program. ## Usage ```ts twoslash // [!include ~/snippets/code/solana/fee-delegation.ts] const solanaChainSigContract = chainSigContract // ---cut--- const deposit = await solanaChainSigContract.getCurrentSignatureDeposit() ``` ## Returns | Type | Description | | ----------------- | ----------------------------------------------- | | `Promise` | The current signature deposit amount (lamports) | # getDerivedPublicKey Derives a public key based on a predecessor address and a derivation path. ## Usage ```ts twoslash // [!include ~/snippets/code/solana/fee-delegation.ts] const solanaChainSigContract = chainSigContract // ---cut--- const publicKey = await solanaChainSigContract.getDerivedPublicKey({ path: 'my_derivation_path', predecessor: '7yvgcBEf6TuXeD1s2XcPX7XM5faVerS4rydTE6McVAzX', keyVersion: 1, }) ``` ## Parameters | Parameter | Type | Description | | ------------------ | -------- | ------------------------------------- | | `args` | `object` | Arguments for deriving the public key | | `args.path` | `string` | The derivation path to use | | `args.predecessor` | `string` | Address requesting the signature | | `args.keyVersion` | `number` | The version of the MPC key to use | ## Returns | Type | Description | | --------------------------------- | ---------------------------------------------- | | `Promise` | Derived public key in SEC1 uncompressed format | # getPublicKey Returns the root public key. This key is either provided during construction via `config.rootPublicKey` or resolved from the built-in constants table based on the program ID. ## Usage ```ts twoslash // [!include ~/snippets/code/solana/fee-delegation.ts] const solanaChainSigContract = chainSigContract // ---cut--- const publicKey = await solanaChainSigContract.getPublicKey() ``` ## Returns | Type | Description | | --------------------------------- | ------------------------------------------- | | `Promise` | Root public key in SEC1 uncompressed format | # waitForEvent Waits for a specific event matching a given request ID by combining a WebSocket listener (real-time) with polling backfill (resilience). Use this when invoking the `sign` instruction on the contract without using this class's `sign` method (e.g. for cross-contract invocations or bidirectional flows). ## Usage ```ts twoslash // [!include ~/snippets/code/solana/fee-delegation.ts] const solanaChainSigContract = chainSigContract const requestId = '0x...' const txSignature = 'signatureOfTheSignRequestTransaction' // ---cut--- import { PublicKey } from '@solana/web3.js' const controller = new AbortController() const signature = await solanaChainSigContract.waitForEvent({ eventName: 'signatureRespondedEvent', requestId, signer: new PublicKey('ResponderPublicKeyHere'), afterSignature: txSignature, timeoutMs: 60_000, signal: controller.signal, }) ``` ## Waiting for a Bidirectional Response ```ts twoslash // [!include ~/snippets/code/solana/fee-delegation.ts] const solanaChainSigContract = chainSigContract const requestId = '0x...' const txSignature = 'signatureOfTheSignRequestTransaction' // ---cut--- import { PublicKey } from '@solana/web3.js' const controller = new AbortController() const result = await solanaChainSigContract.waitForEvent({ eventName: 'respondBidirectionalEvent', requestId, signer: new PublicKey('ResponderPublicKeyHere'), afterSignature: txSignature, timeoutMs: 60_000, signal: controller.signal, }) // result.serializedOutput - Buffer with the destination chain execution result // result.signature - raw Signature { bigR, s, recoveryId } ``` ## Parameters | Parameter | Type | Description | | ------------------------ | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | `eventName` | `ChainSignaturesEventName` | Event to listen for (`'signatureRespondedEvent'`, `'signatureErrorEvent'`, or `'respondBidirectionalEvent'`) | | `requestId` | `string` | Unique identifier for the signature request | | `signer` | `PublicKey` | Address to monitor for transactions (e.g. responder address) | | `afterSignature?` | `string` | Transaction signature to start polling from | | `timeoutMs?` | `number` | Timeout in milliseconds (default: 60000) | | `backfillIntervalMs?` | `number` | Interval between backfill polls in milliseconds (default: 30000) | | `backfillLimit?` | `number` | Max transactions to fetch per backfill poll (default: 50) | | `healthCheckIntervalMs?` | `number` | Interval in milliseconds for WebSocket health checks. Triggers reconnection and fast backfill if the connection is lost. (default: 10000) | | `signal?` | `AbortSignal` | Abort signal for cancellation | ## Returns The return type depends on the `eventName`: | Event Name | Return Type | Description | | --------------------------- | ----------------------------------- | ------------------------------------------------------------------- | | `signatureRespondedEvent` | `Promise` | The signature in RSV format | | `signatureErrorEvent` | `Promise` | Error details from the signing request | | `respondBidirectionalEvent` | `Promise` | Serialized output and raw signature from the bidirectional response | # sign Requests a signature through the Chain Signatures program. ## Usage ```ts twoslash // [!include ~/snippets/code/solana/fee-delegation.ts] const solanaChainSigContract = chainSigContract // ---cut--- const signature = await solanaChainSigContract.sign({ path: 'my_derivation_path', payload: Array.from(new Uint8Array(32).fill(1)), // 32-byte array key_version: 1, }) ``` ## Parameters | Parameter | Type | Description | | ---------------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------- | | `args.path` | `string` | The derivation path to use | | `args.payload` | `number[]` | The data to sign as an array of 32 bytes | | `args.key_version` | `number` | Version of the key to use | | `options?` | `Partial & { remainingAccounts?: AccountMeta[]; remainingSigners?: Signer[] }` | Optional configuration for signing | | `options.sign.algo?` | `string` | Optional algorithm for the signature | | `options.sign.dest?` | `string` | Optional destination address for the signature | | `options.sign.params?` | `string` | Additional parameters for signing | | `options.retry.delay?` | `number` | Delay between retries in milliseconds (default: 5000) | | `options.retry.retryCount?` | `number` | Number of retries to attempt (default: 12) | | `options.remainingAccounts?` | `AccountMeta[]` | Additional accounts required for the transaction | | `options.remainingSigners?` | `Signer[]` | Additional signers required for the transaction | ## Returns | Type | Description | | ----------------------- | --------------------------- | | `Promise` | The signature in RSV format | ## Working Examples * [Integration tests](https://github.com/sig-net/signet.js/blob/main/tests/integration/solana/Solana.integration.test.ts) — signing with retry options on devnet # viem Public Actions You followed a link from a code hover: viem's inline documentation links to this path on viem's own docs site. The content lives in the viem documentation: **[viem — Public Actions introduction](https://viem.sh/docs/actions/public/introduction)**