Transaction Signing
Guide to sending transactions across all supported chains.
Overview
Stratos Vault handles transaction signing locally using keys derived from WebAuthn credentials. The SDK provides chain-specific methods for different blockchain networks.
EVM Chains (Ethereum, Base)
Send Native Token
typescript
// Simple ETH transfer
const result = await sdk.sendNative(
'0xRecipientAddress', // to
'1000000000000000000', // value in wei (1 ETH)
1 // chainId (1 = Ethereum)
);
console.log('TX Hash:', result.transactionHash);Send ERC-20 Token
typescript
// Encode ERC-20 transfer
const transferData = encodeFunctionData({
abi: erc20Abi,
functionName: 'transfer',
args: [recipientAddress, amount],
});
const result = await sdk.sendContractCall(
tokenContractAddress,
transferData,
1 // chainId
);Full Transaction Control
typescript
const result = await sdk.sendEVMTransaction({
transaction: {
to: '0xContract...',
value: '0x0',
data: '0x...', // Encoded function call
chainId: 1,
// Optional gas settings
gasLimit: '0x5208',
maxFeePerGas: '0x2540BE400',
maxPriorityFeePerGas: '0x3B9ACA00',
}
});Sign Without Broadcasting
typescript
const { signedTransaction, transactionHash } = await sdk.signEVMTransaction({
transaction: {
to: '0x...',
value: '0x0',
chainId: 1,
}
});
// Broadcast later using your own RPCSign EIP-712 Typed Data
typescript
const signature = await sdk.signTypedData({
typedData: {
types: {
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' },
],
Permit: [
{ name: 'owner', type: 'address' },
{ name: 'spender', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
],
},
primaryType: 'Permit',
domain: {
name: 'MyToken',
version: '1',
chainId: 1,
verifyingContract: '0x...',
},
message: {
owner: userAddress,
spender: spenderAddress,
value: '1000000',
nonce: 0,
deadline: Math.floor(Date.now() / 1000) + 3600,
},
}
});Solana
Sign and Send Transaction
typescript
// Get transaction from DEX API (e.g., Jupiter)
const { swapTransaction } = await jupiterApi.getSwap({
quoteResponse,
userPublicKey: solanaAddress,
});
// Sign with wallet
const { signedTransaction } = await sdk.signRawSolanaTransaction({
transaction: swapTransaction, // base64 encoded
network: 'mainnet',
});
// Broadcast
const result = await sdk.sendRawSolanaTransaction({
signedTransaction,
network: 'mainnet',
});
console.log('Signature:', result.signature);TON
Sign and Send Message
typescript
// Build message payload
const payload = buildTonPayload({...});
// Sign with wallet
const { boc } = await sdk.signRawTonMessage({
to: 'EQContractAddress...',
value: '1000000000', // 1 TON in nanotons
payload: payload, // base64 BOC
network: 'mainnet',
});
// Broadcast
const result = await sdk.sendRawTonMessage({
boc,
network: 'mainnet',
});
console.log('Hash:', result.hash);TRON
Trigger Smart Contract
typescript
const { rawTransaction } = await sdk.triggerTronSmartContract({
contractAddress: 'TContract...',
functionSelector: 'transfer(address,uint256)',
parameter: abiEncodedParams,
feeLimit: 100000000, // 100 TRX
network: 'mainnet',
});
// Broadcast
const result = await sdk.broadcastTronTransaction({
signedTransaction: rawTransaction,
network: 'mainnet',
});
console.log('TX ID:', result.txID);Canton
Simple Transfer
typescript
const result = await sdk.transfer({
to: 'receiverPartyId',
amount: '100.0',
symbol: 'CC',
chain: 'canton',
memo: 'Payment reference',
});Accept Transfer Offer
typescript
// Get pending offers
const offers = await sdk.getTransferOffers();
// Accept one
await sdk.acceptTransferOffer(offers[0].contractId);Chain IDs Reference
| Chain | ID | Constant |
|---|---|---|
| Ethereum Mainnet | 1 | EVM_CHAINS.ETHEREUM |
| Sepolia Testnet | 11155111 | EVM_CHAINS.SEPOLIA |
| Base | 8453 | EVM_CHAINS.BASE |
| Base Sepolia | 84532 | EVM_CHAINS.BASE_SEPOLIA |
Transaction Status
All results include a status:
typescript
type TransactionStatus = 'pending' | 'confirmed' | 'failed';
const result = await sdk.sendEVMTransaction({...});
switch (result.status) {
case 'pending':
// Submitted, awaiting confirmation
break;
case 'confirmed':
// Successfully confirmed
break;
case 'failed':
// Transaction failed
break;
}Best Practices
1. Check Connection First
typescript
if (!sdk.isConnected()) {
await sdk.connect();
}2. Validate Before Sending
typescript
const balance = await sdk.getBalance('ETH');
if (balance < requiredAmount) {
throw new Error('Insufficient balance');
}3. Handle Errors Gracefully
typescript
try {
await sdk.sendEVMTransaction({...});
} catch (error) {
if (error.message.includes('User rejected')) {
showMessage('Transaction cancelled');
} else if (error.message.includes('Insufficient funds')) {
showMessage('Not enough balance');
} else {
showMessage(`Error: ${error.message}`);
}
}4. Refresh After Transactions
typescript
await sdk.sendEVMTransaction({...});
await sdk.refresh(); // Update displayed balances5. Show Loading States
typescript
setLoading(true);
try {
await sdk.sendEVMTransaction({...});
showSuccess('Transaction sent!');
} finally {
setLoading(false);
}Next Steps
- SDK Reference - Complete API docs
- Canton Contracts - Daml integration
