Transfer SOL
A complete Solana send flow: check balance, build the transaction, MPC sign, and broadcast.
Components used:
GET_SOLANA_ACCOUNT_BALANCE— verify sufficient fundsBUILD_SOLANA_TRANSACTION— construct the unsigned transaction with blockhashSIGN_WITH_KEY_SHARE— MPC sign the transaction message hash (Ed25519)BUILD_SOLANA_SIGNATURE_PAIR_ITEM— wrap public key + signature into aSignaturePairSIGN_SOLANA_TRANSACTION— apply signatures to produce the final signed transactionBROADCAST_SOLANA_TRANSACTION— submit to the network
Code​
import { WorkspaceClient, ComponentModule } from 'caller-sdk';
const workspace = new WorkspaceClient({ apiKey: process.env.WR_API_KEY! });
const RPC_URL = 'https://api.mainnet-beta.solana.com';
const KEY_ID = 'your-key-id';
const DERIV = [2147483692, 2147484149, 2147483648, 0]; // m/44'/501'/0'/0'
const FROM_ADDR = 'FROMpubkey...'; // base58 sender address
const FROM_PUBKEY_HEX = '02abc123...'; // hex public key (for SIGN_WITH_KEY_SHARE)
const TO_ADDR = 'TOPubkey...'; // base58 recipient address
async function transferSol(lamports: string) {
// 1. Check balance (returned as hex string)
const { value } = await workspace
.call(ComponentModule.GET_SOLANA_ACCOUNT_BALANCE, {
jsonRpcUrl: RPC_URL,
account: FROM_ADDR,
tokenMint: null, // null = native SOL
})
.promise();
console.log(`Balance: ${BigInt(value)} lamports`);
if (BigInt(value) < BigInt(lamports)) throw new Error('Insufficient SOL balance');
// 2. Build unsigned transaction
const { unsignedTransaction, serializedHash } = await workspace
.call(ComponentModule.BUILD_SOLANA_TRANSACTION, {
jsonRpcUrl: RPC_URL,
feePayer: FROM_ADDR,
from: FROM_ADDR,
to: TO_ADDR,
lamports,
instructions: [],
nonceAccount: null, // use recent blockhash
})
.promise();
// 3. MPC sign the transaction hash (Ed25519)
const { signature } = await workspace
.call(ComponentModule.SIGN_WITH_KEY_SHARE, {
keyId: KEY_ID,
derivationPath: DERIV,
messageHash: serializedHash,
})
.promise();
// 4. Wrap into a SignaturePair
const { item } = await workspace
.call(ComponentModule.BUILD_SOLANA_SIGNATURE_PAIR_ITEM, {
publicKey: FROM_ADDR, // base58 public key
signature,
})
.promise();
// 5. Apply signature to transaction
const { signedTransaction } = await workspace
.call(ComponentModule.SIGN_SOLANA_TRANSACTION, {
unsignedTransaction,
signatures: [item],
})
.promise();
// 6. Broadcast
const { transactionSignature } = await workspace
.call(ComponentModule.BROADCAST_SOLANA_TRANSACTION, {
jsonRpcUrl: RPC_URL,
signedTransaction,
})
.promise();
console.log('Transaction signature:', transactionSignature);
return transactionSignature;
}
// Send 0.5 SOL (500,000,000 lamports)
const sig = await transferSol('500000000');
Workflow canvas layout​
GET_SOLANA_ACCOUNT_BALANCE
│ value (guard check)
â–¼
BUILD_SOLANA_TRANSACTION
│ serializedHash │ unsignedTransaction
▼ │
SIGN_WITH_KEY_SHARE │
│ signature │
▼ │
BUILD_SOLANA_SIGNATURE_PAIR_ITEM │
│ item │
└──────────┬──────────────┘
â–¼
SIGN_SOLANA_TRANSACTION
│ signedTransaction
â–¼
BROADCAST_SOLANA_TRANSACTION
│ transactionSignature
â–¼
[Confirmed]
Durable nonces
For workflows that build transactions in advance (e.g. scheduled sends), set nonceAccount to a pre-created durable nonce account. This avoids transaction expiry from blockhash staleness.