Interface
Every chain connector implementsChainConnector:
Supporting Types
EVM Connector
A singleEVMConnector class covers all EVM-compatible chains. Different chains use different configuration — same code.
Configuration
How Methods Map to SDK Primitives
| Interface Method | Implementation |
|---|---|
deriveKeys(seed) | SHA-256 seed -> privateKeyToAccount -> sign message -> deriveStealthKeys(sig) -> encodeStealthMetaAddress |
sendPayment | decodeStealthMetaAddress -> generateStealthAddress -> writeContract(WraithSender, "sendETH") |
scanPayments | Query subgraph for Announcement events -> scanAnnouncements(events, ...) -> fetch balances |
getBalance | publicClient.getBalance + readContract(erc20, balanceOf) per token |
withdraw | deriveStealthPrivateKey -> privateKeyToAccount -> sendTransaction |
registerName | signNameRegistration(name, metaBytes, spendingKey) -> writeContract(WraithNames, "register") |
resolveName | readContract(WraithNames, "resolve", [name]) -> decode meta-address |
fundWallet | POST to faucet API (testnet) |
Adding a New EVM Chain
No code changes required. Register a new chain with its config:- Deploy the 4 Solidity contracts (Announcer, Registry, Sender, Names)
- Set up a subgraph to index Announcement events
- Add config to the registry
Stellar Connector
How Methods Map to SDK Primitives
| Interface Method | Implementation |
|---|---|
deriveKeys(seed) | SHA-256 seed -> ed25519 seed -> sign message -> deriveStealthKeys(sig) -> encodeStealthMetaAddress |
sendPayment | decodeStealthMetaAddress -> generateStealthAddress -> Operation.createAccount -> Soroban announcer |
scanPayments | Fetch Soroban events -> scanAnnouncements(events, ...) -> fetch balances from Horizon |
getBalance | GET /accounts/{key} from Horizon |
withdraw | deriveStealthPrivateScalar -> build payment tx -> signStellarTransaction -> submit to Horizon |
registerName | Call Soroban WraithNames register(name, metaAddress) |
resolveName | Simulate Soroban WraithNames resolve(name) |
fundWallet | Stellar Friendbot GET /friendbot?addr={key} |
Stellar-Specific Considerations
- Account creation: Stellar requires accounts to exist with a minimum balance (1 XLM). Sending to a new stealth address uses
Operation.createAccount, notOperation.payment. - Signing: Stealth private keys are derived scalars. Must use
signWithScalar()from the SDK — can’t useKeypair.fromRawEd25519Seed(). - Events: Soroban contract events are fetched via
sorobanServer.getEvents(), not a subgraph.
Configuration
Solana Connector
Handles Solana using ed25519 and Anchor programs.How Methods Map to SDK Primitives
| Interface Method | Implementation |
|---|---|
deriveKeys(seed) | SHA-256 seed -> ed25519 seed -> sign message -> deriveStealthKeys(sig) -> encodeStealthMetaAddress |
sendPayment | decodeStealthMetaAddress -> generateStealthAddress -> Anchor instruction wraith_sender.send_sol (transfer + announce atomically) |
scanPayments | Fetch announcer program transaction logs -> parse Anchor events -> scanAnnouncements(events, ...) -> fetch balances |
getBalance | connection.getBalance(address) for SOL, connection.getTokenAccountsByOwner() for SPL tokens |
withdraw | deriveStealthPrivateScalar -> signWithScalar to sign SystemProgram.transfer -> sendRawTransaction |
registerName | Anchor instruction wraith_names.register with name + 64-byte meta-address |
resolveName | Derive PDA from ["name", nameBytes] -> fetch and decode NameRecord account |
fundWallet | connection.requestAirdrop(address, lamports) (devnet) |
Solana-Specific Considerations
- No account deployment: An ed25519 public key is a valid Solana address. Send SOL directly — no
createAccountneeded. - Rent exemption: Accounts need ~0.00089 SOL minimum. When withdrawing all, send
balance - 5000 lamports(tx fee). - SPL tokens: Use associated token accounts (ATAs). The ATA must be created for the stealth address before transferring SPL tokens.
- Signing: Uses
signWithScalar()from the SDK — stealth scalars can’t be used with standardKeypair. - Events: Anchor
emit!()writes to program logs. Parse via transaction history, not subgraph.
Configuration
CKB Connector
Handles Nervos CKB using secp256k1 and the UTXO-based Cell model. CKB is architecturally unique: there is no separate announcer contract. Cells are the announcements — the ephemeral public key and stealth address hash are embedded directly in the Cell’s lock script args.How Methods Map to SDK Primitives
| Interface Method | Implementation |
|---|---|
deriveKeys(seed) | SHA-256 seed -> privateKeyToAccount -> sign message -> deriveStealthKeys(sig) -> encodeStealthMetaAddress |
sendPayment | decodeStealthMetaAddress -> generateStealthAddress -> create Cell with stealth-lock (args = ephemeral_pub || blake160) |
scanPayments | get_cells RPC filtered by stealth-lock code hash -> scanStealthCells(cells, ...) -> return matched Cells with balances |
getBalance | Sum capacity of all matched stealth Cells |
withdraw | deriveStealthPrivateKey -> sign transaction consuming stealth Cell -> create destination Cell |
registerName | buildRegisterName({ name, spendingPubKey, viewingPubKey }) -> create Cell with wraith-names-type type script, lock = owner |
resolveName | buildResolveName({ name }) -> get_cells RPC filtered by type script -> metaAddressFromNameData(data) |
fundWallet | CKB testnet faucet |
CKB-Specific Considerations
- UTXO model: CKB uses Cells (UTXOs), not accounts. Sending creates a Cell; withdrawing consumes it. There’s no balance to query on an address — you query live Cells.
- No separate announcer: The Cell’s lock script args contain the ephemeral public key. No event infrastructure needed.
- Minimum capacity: A stealth-lock Cell requires at least 61 CKB due to the 53-byte args field.
- blake160: Address hashing uses blake2b with
"ckb-default-hash"personalization, truncated to 20 bytes. - SHA-256 for shared secret: Unlike EVM (keccak256), CKB uses SHA-256 for hashing the ECDH shared secret.
- No view tags: Every Cell is fully checked. The
get_cellsRPC pre-filters by code hash, keeping the scan set small. - Name ownership via lock script: Unlike EVM/Solana where names are owned by the spending key, CKB name ownership is determined by the Cell’s lock script. Whoever can spend the Cell controls the name.
Configuration
Chain Registry
The TEE server maintains a registry of available chain connectors:Usage in Agent Service
Adding a New Chain Family
To add support for a completely new chain family (e.g., Solana):- Create a connector class implementing
ChainConnector - Implement all methods using the chain’s SDK and cryptographic primitives
- Write the crypto module at
@wraith-protocol/sdk/chains/solana - Deploy stealth address contracts on the target chain
- Register the connector in the chain registry
Key Differences Between Chain Families
| Aspect | EVM | Stellar | Solana | CKB |
|---|---|---|---|---|
| Curve | secp256k1 | ed25519 | ed25519 | secp256k1 |
| Address format | 0x... (20 bytes) | G... (56 chars) | Base58 | bech32m |
| Meta-address prefix | st:eth:0x | st:xlm: | st:sol: | st:ckb: |
| Contracts | Solidity | Soroban (Rust) | Solana Programs | RISC-V Lock Script |
| Announcements | EVM events / subgraph | Soroban events / Horizon | Program log events | Embedded in Cell args |
| Native asset | ETH | XLM | SOL | CKB |
| Account model | Balance-based | Account must exist first | Balance-based | UTXO (Cell) |

