Skip to content

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 RPC

Sign 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

ChainIDConstant
Ethereum Mainnet1EVM_CHAINS.ETHEREUM
Sepolia Testnet11155111EVM_CHAINS.SEPOLIA
Base8453EVM_CHAINS.BASE
Base Sepolia84532EVM_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 balances

5. Show Loading States

typescript
setLoading(true);
try {
  await sdk.sendEVMTransaction({...});
  showSuccess('Transaction sent!');
} finally {
  setLoading(false);
}

Next Steps

Enterprise-grade multi-chain wallet infrastructure.