This guide will walk you through the process of sending transactions from your wallet using the Crossmint transactions API with custodial signers. Custodial signers enable secure transaction signing without managing private keys directly.

Note: This guide currently only works for wallets set up with a custodial admin signer. We will soon update it to cover all types of signers.

Prerequisites

Ensure you have created a wallet with a custodial signer by following the Server Wallets Quickstart. You will need:

  • API Key: Ensure you have an API key with the scope: wallets:transactions.create
  • Wallet Address: The address of your wallet created via the quickstart.

Sending a Transaction

You can send various types of transactions using the Create Transaction API. Here’s how to do it for different chains:

For EVM-based chains like Ethereum, Polygon, Base, etc., you can send transactions as follows:

const walletLocator = '0x...'; // Address of your wallet from the quickstart guide
const apiKey = 'sk_staging...';
const contractAddress = '0x...';

async function sendTransaction() {
    const response = await fetch(
         `https://staging.crossmint.com/api/2022-06-09/wallets/${walletLocator}/transactions`,
        {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-API-KEY': apiKey,
            },
            body: JSON.stringify({
                params: {
                    calls: [{
                        to: contractAddress,
                        value: '0', // Value in wei (0 for contract calls with no ETH transfer)
                        data: '0x1234abcd...', // The encoded function call data
                    }],
                    chain: 'base-sepolia', // Specify your target chain
                },
            }),
        }
    );

    const data = await response.json();
    console.log(data);
    return data;
}

Encoding Contract Function Calls

When interacting with smart contracts, you need to encode your function calls as data. Here’s how to do it:

import { encodeFunctionData } from 'viem';

// Example: Encoding a "transfer" function call for an ERC-20 token
const recipientAddress = '0x...'; // Address of the recipient
const tokenAmount = 1000000n; // 1 token with 6 decimals

// Encode the function call
const data = encodeFunctionData({
    abi: [{
        name: 'transfer',
        type: 'function',
        inputs: [
            { name: 'to', type: 'address' },
            { name: 'amount', type: 'uint256' }
        ],
        outputs: [{ name: '', type: 'bool' }],
        stateMutability: 'nonpayable'
    }],
    args: [recipientAddress, tokenAmount]
});

console.log(data);
// Example output: 0xa9059cbb000000000000000000000000recipientAddressWithout0x00000000000000000000000000000000000000000000000000000000000003e8

Transaction Status

After sending a transaction, you can check its status by querying the transaction endpoint:

const apiKey = 'sk_staging...';
const walletLocator = '0x...';
const transactionId = 'transaction-id-from-previous-response';

async function checkTransactionStatus() {
    const response = await fetch(
        `https://api.crossmint.com/2022-06-09/wallets/${walletLocator}/transactions/${transactionId}`, {
            method: 'GET',
            headers: {
                'X-API-KEY': apiKey,
                'Content-Type': 'application/json',
            },
        }
    );

    const data = await response.json();
    console.log(data);
    return data;
}

checkTransactionStatus().catch(console.error);

Learn More

For specific transaction types, check out these detailed guides: