Documentation Index Fetch the complete documentation index at: https://mintlify.com/Crossmint/crossmint-sdk/llms.txt
Use this file to discover all available pages before exploring further.
Go beyond simple transfers and build custom transactions for smart contracts, DeFi protocols, and complex blockchain interactions.
Overview
Crossmint’s Wallets SDK supports:
Smart contract interactions - Call any contract method
Token transfers - ERC-20, ERC-721, ERC-1155, SPL tokens
Message signing - For authentication and verification
Typed data signing - EIP-712 support
Raw transactions - Full control over transaction parameters
Prerequisites
npm install @crossmint/wallets-sdk @crossmint/common-sdk-base
For Solana transactions:
npm install @solana/web3.js
EVM Smart Contract Interactions
Calling Contract Methods
Interact with smart contracts using the ABI:
Get an EVM wallet
import { createCrossmint } from "@crossmint/common-sdk-base" ;
import { CrossmintWallets , EVMWallet } from "@crossmint/wallets-sdk" ;
const crossmint = createCrossmint ({ apiKey: "YOUR_API_KEY" });
const wallets = CrossmintWallets . from ( crossmint );
const wallet = await wallets . getOrCreateWallet ({
chain: "ethereum-sepolia" ,
signer: { type: "api-key" , locator: "user-123" },
});
const evmWallet = EVMWallet . from ( wallet );
Define the contract ABI
const nftAbi = [
{
inputs: [
{ name: "to" , type: "address" },
{ name: "tokenId" , type: "uint256" },
],
name: "mint" ,
outputs: [],
stateMutability: "nonpayable" ,
type: "function" ,
},
] as const ;
Execute the transaction
const result = await evmWallet . sendTransaction ({
to: "0xContractAddress123456789" ,
abi: nftAbi ,
functionName: "mint" ,
args: [ "0xRecipientAddress" , 1 ],
});
console . log ( "Transaction hash:" , result . hash );
console . log ( "Explorer link:" , result . explorerLink );
console . log ( "Transaction ID:" , result . transactionId );
ERC-20 Token Transfer
Transfer ERC-20 tokens:
const erc20Abi = [
{
inputs: [
{ name: "to" , type: "address" },
{ name: "amount" , type: "uint256" },
],
name: "transfer" ,
outputs: [{ name: "" , type: "bool" }],
stateMutability: "nonpayable" ,
type: "function" ,
},
] as const ;
const result = await evmWallet . sendTransaction ({
to: "0xTokenContractAddress" ,
abi: erc20Abi ,
functionName: "transfer" ,
args: [
"0xRecipient" ,
"1000000000000000000" , // 1 token with 18 decimals
],
});
ERC-721 NFT Transfer
const erc721Abi = [
{
inputs: [
{ name: "from" , type: "address" },
{ name: "to" , type: "address" },
{ name: "tokenId" , type: "uint256" },
],
name: "transferFrom" ,
outputs: [],
stateMutability: "nonpayable" ,
type: "function" ,
},
] as const ;
const result = await evmWallet . sendTransaction ({
to: "0xNFTContractAddress" ,
abi: erc721Abi ,
functionName: "transferFrom" ,
args: [
evmWallet . address ,
"0xRecipient" ,
42 , // Token ID
],
});
Sending Native Currency
Send ETH, MATIC, or other native tokens:
const result = await evmWallet . sendTransaction ({
to: "0xRecipientAddress" ,
value: "0.1" , // Amount in ETH/MATIC/etc.
});
console . log ( "Sent 0.1 ETH to" , "0xRecipientAddress" );
console . log ( "Transaction:" , result . explorerLink );
Message Signing
Sign a Message
Sign arbitrary messages for authentication:
const signature = await evmWallet . signMessage ({
message: "Sign this message to authenticate" ,
});
console . log ( "Signature:" , signature . signature );
console . log ( "Signature ID:" , signature . signatureId );
Verify Message Signature
import { verifyMessage } from "viem" ;
const isValid = await verifyMessage ({
address: evmWallet . address ,
message: "Sign this message to authenticate" ,
signature: signature . signature ,
});
console . log ( "Signature valid:" , isValid );
EIP-712 Typed Data Signing
Sign structured data following EIP-712:
const domain = {
name: "MyDApp" ,
version: "1" ,
chainId: 11155111 , // Sepolia
verifyingContract: "0xContractAddress" ,
};
const types = {
Person: [
{ name: "name" , type: "string" },
{ name: "wallet" , type: "address" },
],
};
const message = {
name: "Alice" ,
wallet: "0xAliceAddress" ,
};
const signature = await evmWallet . signTypedData ({
domain ,
types ,
primaryType: "Person" ,
message ,
chain: "ethereum-sepolia" ,
});
console . log ( "Typed data signature:" , signature . signature );
EIP-712 signatures are commonly used by DeFi protocols for gasless approvals and permit functions.
Solana Transactions
Basic SOL Transfer
import { SolanaWallet } from "@crossmint/wallets-sdk" ;
import { Transaction , SystemProgram , PublicKey } from "@solana/web3.js" ;
const wallet = await wallets . getOrCreateWallet ({
chain: "solana-devnet" ,
signer: { type: "api-key" , locator: "user-123" },
});
const solanaWallet = SolanaWallet . from ( wallet );
// Create transfer transaction
const transaction = new Transaction (). add (
SystemProgram . transfer ({
fromPubkey: new PublicKey ( solanaWallet . address ),
toPubkey: new PublicKey ( "recipient-address" ),
lamports: 1000000 , // 0.001 SOL
})
);
// Serialize and send
const serialized = transaction . serialize (). toString ( "base64" );
const result = await solanaWallet . sendTransaction ({
serializedTransaction: serialized ,
});
console . log ( "Transaction hash:" , result . hash );
SPL Token Transfer
import {
createTransferInstruction ,
getAssociatedTokenAddress ,
} from "@solana/spl-token" ;
const tokenMint = new PublicKey ( "TokenMintAddress" );
const recipientAddress = new PublicKey ( "RecipientAddress" );
// Get token accounts
const sourceAccount = await getAssociatedTokenAddress (
tokenMint ,
new PublicKey ( solanaWallet . address )
);
const destinationAccount = await getAssociatedTokenAddress (
tokenMint ,
recipientAddress
);
// Create transfer instruction
const transaction = new Transaction (). add (
createTransferInstruction (
sourceAccount ,
destinationAccount ,
new PublicKey ( solanaWallet . address ),
1000000 , // Amount with token decimals
)
);
const serialized = transaction . serialize (). toString ( "base64" );
const result = await solanaWallet . sendTransaction ({
serializedTransaction: serialized ,
});
Solana Program Interaction
Interact with Solana programs (smart contracts):
import { TransactionInstruction } from "@solana/web3.js" ;
const programId = new PublicKey ( "ProgramAddress" );
const instruction = new TransactionInstruction ({
keys: [
{ pubkey: new PublicKey ( solanaWallet . address ), isSigner: true , isWritable: true },
{ pubkey: new PublicKey ( "AccountAddress" ), isSigner: false , isWritable: true },
],
programId ,
data: Buffer . from ([ /* instruction data */ ]),
});
const transaction = new Transaction (). add ( instruction );
const serialized = transaction . serialize (). toString ( "base64" );
const result = await solanaWallet . sendTransaction ({
serializedTransaction: serialized ,
});
Advanced Transaction Options
Prepare-Only Mode
Create transactions without executing them:
const prepared = await evmWallet . sendTransaction ({
to: "0xRecipient" ,
value: "0.1" ,
options: {
experimental_prepareOnly: true ,
},
});
console . log ( "Transaction ID:" , prepared . transactionId );
console . log ( "Hash:" , prepared . hash ); // undefined - not executed yet
// Execute later
const result = await evmWallet . approveTransactionAndWait ( prepared . transactionId );
console . log ( "Executed hash:" , result . hash );
Prepare-only mode is experimental and may change in future versions.
Custom Gas Settings
Control gas prices for EVM transactions:
const result = await evmWallet . sendTransaction ({
to: "0xRecipient" ,
value: "0.1" ,
gasLimit: "21000" ,
maxFeePerGas: "50000000000" , // 50 gwei
maxPriorityFeePerGas: "2000000000" , // 2 gwei
});
Transaction Status Tracking
Monitor transaction status:
// Send transaction
const result = await evmWallet . sendTransaction ({
to: "0xRecipient" ,
value: "0.1" ,
});
// Check status later
const transaction = await wallet . getTransaction ( result . transactionId );
console . log ( "Status:" , transaction . status );
console . log ( "Hash:" , transaction . onChain ?. txId );
console . log ( "Explorer:" , transaction . onChain ?. explorerLink );
Possible status values:
pending - Transaction created but not submitted
submitted - Transaction sent to blockchain
success - Transaction confirmed
failed - Transaction failed
Transaction History
Retrieve past transactions:
const transactions = await wallet . getTransactions ();
for ( const tx of transactions ) {
console . log ( "ID:" , tx . id );
console . log ( "Status:" , tx . status );
console . log ( "Hash:" , tx . onChain ?. txId );
console . log ( "Created:" , new Date ( tx . createdAt ));
console . log ( "---" );
}
Batch Transactions
Execute multiple transactions efficiently:
async function batchMint ( recipients : string []) {
const promises = recipients . map (( recipient ) =>
evmWallet . sendTransaction ({
to: "0xNFTContract" ,
abi: nftAbi ,
functionName: "mint" ,
args: [ recipient , 1 ],
})
);
const results = await Promise . all ( promises );
return results . map (( r ) => r . hash );
}
const txHashes = await batchMint ([
"0xRecipient1" ,
"0xRecipient2" ,
"0xRecipient3" ,
]);
console . log ( "Minted to" , txHashes . length , "recipients" );
Error Handling
Handle transaction errors gracefully:
import {
TransactionNotCreatedError ,
TransactionFailedError ,
InsufficientFundsError
} from "@crossmint/wallets-sdk" ;
try {
const result = await evmWallet . sendTransaction ({
to: "0xRecipient" ,
value: "100" , // Too much!
});
} catch ( error ) {
if ( error instanceof InsufficientFundsError ) {
console . error ( "Not enough funds" );
// Prompt user to add funds
} else if ( error instanceof TransactionFailedError ) {
console . error ( "Transaction failed on-chain" );
// Show error to user
} else if ( error instanceof TransactionNotCreatedError ) {
console . error ( "Failed to create transaction" );
// Retry or show error
} else {
console . error ( "Unknown error:" , error );
}
}
Best Practices
Validate inputs before sending
import { isValidEvmAddress } from "@crossmint/common-sdk-base" ;
function validateRecipient ( address : string ) {
if ( ! isValidEvmAddress ( address )) {
throw new Error ( "Invalid recipient address" );
}
}
validateRecipient ( recipientAddress );
await evmWallet . sendTransaction ({ to: recipientAddress , value: "0.1" });
Check balances before transactions
const balance = await wallet . getBalance ();
const requiredAmount = parseFloat ( "0.1" );
if ( parseFloat ( balance . native . raw ) < requiredAmount ) {
throw new Error ( "Insufficient balance" );
}
await evmWallet . sendTransaction ({
to: "0xRecipient" ,
value: requiredAmount . toString (),
});
Use explorer links for transparency
const result = await evmWallet . sendTransaction ({ ... });
console . log ( "View transaction:" , result . explorerLink );
// Show link to user so they can track progress
Implement retry logic
async function sendWithRetry ( tx : any , maxRetries = 3 ) {
for ( let i = 0 ; i < maxRetries ; i ++ ) {
try {
return await evmWallet . sendTransaction ( tx );
} catch ( error ) {
if ( i === maxRetries - 1 ) throw error ;
await new Promise (( resolve ) => setTimeout ( resolve , 1000 * ( i + 1 )));
}
}
}
Testing Transactions
Always test on testnets first:
// Use testnet chains
const testWallet = await wallets . getOrCreateWallet ({
chain: "ethereum-sepolia" , // Testnet
signer: { type: "api-key" , locator: "test-user" },
});
const testEvmWallet = EVMWallet . from ( testWallet );
// Test your transaction
const result = await testEvmWallet . sendTransaction ({
to: "0xTestRecipient" ,
value: "0.001" ,
});
console . log ( "Test transaction:" , result . explorerLink );
Get free testnet tokens from faucets:
Next Steps
Multi-Chain Support Execute transactions across multiple chains
Embedded Wallets Manage wallets for your users