Skip to main content

Transaction Preparation

This guide explains how to use transaction preparation methods in the Steer Protocol staking system. These methods allow you to prepare transaction data without executing them, giving you more control over the transaction process.

Overview

The Steer Protocol SDK provides three main transaction preparation methods:

  • prepareStakeTx
  • prepareWithdrawTx
  • prepareGetRewardTx

These methods are useful when you need to:

  • Customize transaction handling
  • Implement your own transaction signing logic
  • Batch multiple transactions
  • Simulate transactions before execution
  • Implement custom gas strategies

Preparing Stake Transactions

The prepareStakeTx method prepares the transaction data for staking tokens:

interface StakeParams {
stakingPool: Address;
amount: bigint;
}

// Example usage
const stakeTx = await steerClient.staking.prepareStakeTx({
stakingPool: poolAddress,
amount: BigInt('1000000000000000000') // 1 token with 18 decimals
});

if (stakeTx.success) {
const { address, abi, functionName, args } = stakeTx.data;

// Example: Using the prepared data with your own contract caller
const { request } = await publicClient.simulateContract({
address,
abi,
functionName,
args,
account: userAddress
});

// Execute the transaction with custom parameters
const hash = await walletClient.writeContract({
...request,
gasLimit: calculateGasLimit(request),
maxFeePerGas: calculateMaxFee()
});
}

Preparing Withdraw Transactions

The prepareWithdrawTx method prepares the transaction data for withdrawing staked tokens:

interface WithdrawParams {
stakingPool: Address;
amount: bigint;
}

// Example usage
const withdrawTx = await steerClient.staking.prepareWithdrawTx({
stakingPool: poolAddress,
amount: BigInt('1000000000000000000')
});

if (withdrawTx.success) {
const { address, abi, functionName, args } = withdrawTx.data;

// Example: Custom gas estimation
const gasEstimate = await publicClient.estimateContractGas({
address,
abi,
functionName,
args,
account: userAddress
});

// Execute with custom gas settings
const hash = await walletClient.writeContract({
address,
abi,
functionName,
args,
gas: gasEstimate * BigInt(120) / BigInt(100) // Add 20% buffer
});
}

Preparing Reward Claim Transactions

The prepareGetRewardTx method prepares the transaction data for claiming rewards:

interface GetRewardParams {
stakingPool: Address;
}

// Example usage
const claimTx = await steerClient.staking.prepareGetRewardTx({
stakingPool: poolAddress
});

if (claimTx.success) {
const { address, abi, functionName, args } = claimTx.data;

// Example: Transaction simulation before execution
try {
await publicClient.simulateContract({
address,
abi,
functionName,
args,
account: userAddress
});

// Proceed with execution if simulation succeeds
const hash = await walletClient.writeContract({
address,
abi,
functionName,
args
});
} catch (error) {
console.error('Transaction would fail:', error);
}
}

Advanced Usage

Batching Multiple Transactions

You can prepare multiple transactions and execute them together:

async function batchStakingOperations(
stakingPool: Address,
stakeAmount: bigint
) {
// Prepare multiple transactions
const [stakeTx, claimTx] = await Promise.all([
steerClient.staking.prepareStakeTx({
stakingPool,
amount: stakeAmount
}),
steerClient.staking.prepareGetRewardTx({
stakingPool
})
]);

// Execute transactions in sequence
if (stakeTx.success && claimTx.success) {
const transactions = [
{
...stakeTx.data,
description: 'Stake tokens'
},
{
...claimTx.data,
description: 'Claim rewards'
}
];

// Execute each transaction
for (const tx of transactions) {
try {
const hash = await walletClient.writeContract({
address: tx.address,
abi: tx.abi,
functionName: tx.functionName,
args: tx.args
});
console.log(`${tx.description} hash:`, hash);
} catch (error) {
console.error(`${tx.description} failed:`, error);
}
}
}
}

Custom Gas Strategies

Implement custom gas strategies using prepared transactions:

async function executeWithGasStrategy(
preparedTx: any,
maxRetries: number = 3
) {
const { address, abi, functionName, args } = preparedTx;

for (let i = 0; i < maxRetries; i++) {
try {
// Get current gas prices
const gasPrice = await publicClient.getGasPrice();
const maxPriorityFeePerGas = calculatePriorityFee(i);

// Execute with calculated gas parameters
const hash = await walletClient.writeContract({
address,
abi,
functionName,
args,
maxFeePerGas: gasPrice * BigInt(120) / BigInt(100),
maxPriorityFeePerGas
});

return hash;
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise(r => setTimeout(r, 1000 * (i + 1)));
}
}
}

Transaction Simulation

Simulate transactions before execution to validate them:

async function simulateStakingTx(
preparedTx: any,
userAddress: Address
) {
const { address, abi, functionName, args } = preparedTx;

try {
// Simulate the transaction
const { request } = await publicClient.simulateContract({
address,
abi,
functionName,
args,
account: userAddress
});

// Calculate gas requirements
const gasEstimate = await publicClient.estimateContractGas(request);

// Get user's balance
const balance = await publicClient.getBalance({ address: userAddress });

// Validate user has enough for gas
if (balance < gasEstimate * await publicClient.getGasPrice()) {
throw new Error('Insufficient funds for gas');
}

return {
canExecute: true,
gasEstimate,
request
};
} catch (error) {
return {
canExecute: false,
error
};
}
}

Best Practices

  1. Always Simulate First

    // Simulate before execution
    const simulation = await simulateStakingTx(preparedTx, userAddress);
    if (!simulation.canExecute) {
    console.error('Transaction would fail:', simulation.error);
    return;
    }
  2. Handle Nonce Management

    // Get the next nonce
    const nonce = await publicClient.getTransactionCount({
    address: userAddress
    });

    // Execute with specific nonce
    const hash = await walletClient.writeContract({
    ...preparedTx,
    nonce
    });
  3. Implement Proper Error Recovery

    async function executeWithRetry(preparedTx: any) {
    const maxRetries = 3;
    for (let i = 0; i < maxRetries; i++) {
    try {
    return await walletClient.writeContract(preparedTx);
    } catch (error) {
    if (i === maxRetries - 1) throw error;
    if (error.message.includes('nonce too low')) {
    preparedTx.nonce++;
    }
    await new Promise(r => setTimeout(r, 1000));
    }
    }
    }
  4. Monitor Transaction Status

    async function monitorTransaction(hash: Hash) {
    const receipt = await publicClient.waitForTransactionReceipt({
    hash,
    confirmations: 2
    });

    return {
    status: receipt.status,
    gasUsed: receipt.gasUsed,
    effectiveGasPrice: receipt.effectiveGasPrice
    };
    }