In this step, you’ll learn how to find tradeable tokens, calculate price impact, and execute token purchases using the Odin platform.
What You’ll Accomplish
By the end of this step, you’ll have:
- ✅ Fetched available tokens from the Odin API
- ✅ Calculated price impact for trades
- ✅ Calculated minimum amounts received for trades
- ✅ Implemented risk assessment logic
- ✅ Executed your first token purchase
- ✅ Implemented slippage protection for safer trading
- ✅ Learned best practices for trade configuration
Understanding Token Trading
Price Impact and Risk Assessment
Before executing any trade, it’s crucial to understand the price impact your transaction will have on the token’s price. This guide provides utilities to calculate this impact and assess the risk level.
Learn more about price impact calculations
here
Trade Types and Settings
Odin supports different trade types and allows you to configure various settings including slippage tolerance to protect against unfavorable price movements.
Update Type Definitions
First, let’s update our type definitions to support trading functionality:
/**
* Result of a successful deposit operation
*/
export interface DepositResult {
/** New balance after deposit */
newBalance: bigint;
}
export type TokenAmount = bigint;
export type TradeType = { buy: null } | { sell: null }
export type TradeAmount = { btc: TokenAmount } | { token: TokenAmount }
export interface TradeSettings { 'slippage' : [] | [[TokenAmount, bigint]] }
export interface TradeRequest {
tokenid: string;
typeof: TradeType;
amount: TradeAmount;
settings: [] | [TradeSettings];
}
export type TradeResponse = { 'ok' : null } |
{ 'err' : string };
/**
* Odin Actor Interface
*/
export interface OdinActor {
/** Deposit tokens to the canister */
token_deposit(tokenId: string, amount: bigint): Promise<DepositResult>;
/** Execute token trades (buy/sell) */
token_trade(tradeRequest: TradeRequest): Promise<TradeResponse>;
}
Token Discovery and Trading Logic
Now let’s implement the complete trading workflow:
import { login, prepare } from './core/prepare';
import { createSampleWallet } from './sample-wallet';
import { authenticateCallback } from './core/auth-callback';
import { getActor } from './utils/odin';
import { Token } from './types/token';
import { getPriceImpact } from './utils/getPriceImpact';
import { getMinimumReceived } from './utils/getMinimumReceived';
import { TradeRequest, TradeSettings } from './types/odin';
const ODIN_API_URL = 'https://api.odin.fun/dev'; // https://api.odin.fun/v1 for prod
(async () => {
const wallet = await createSampleWallet();
const result = await prepare(wallet.address);
const signature = await wallet.signMessage(result.message);
const identity = await login({
address: wallet.address,
message: result.message,
signature: signature,
publicKey: wallet.publicKey,
signatureType: 'Bip322Simple',
});
// Used for API calls.
const token = await authenticateCallback(identity);
const odin = getActor(identity);
const BITCOIN = {
id: 'btc',
name: 'Bitcoin',
ticker: 'BTC',
image: 'https://upload.wikimedia.org/wikipedia/commons/4/46/Bitcoin.svg',
rune: null,
divisibility: 8,
decimals: 3,
};
// First, deposit some BTC for trading
const btc = 0.001;
const amount = BigInt(btc * 10 ** (BITCOIN.divisibility + BITCOIN.decimals));
const newBalance = await odin.token_deposit(BITCOIN.id, amount);
console.log({
newBalance,
});
// Step 1: Fetch available tokens from the API
const response = await fetch(`${ODIN_API_URL}/tokens?page=1&limit=30`);
const data: { data: Token[] } = await response.json();
// Step 2: Find a token that's currently trading
const tokenToBuy = data.data.find((token: any) => token.trading);
if (!tokenToBuy) {
throw new Error('No token to buy found');
}
// Step 3: Calculate price impact for the trade
const amountToBuyBtc = 0.0001;
const amountToBuy = BigInt(amountToBuyBtc * 10 ** (BITCOIN.divisibility + BITCOIN.decimals));
const priceImpact = getPriceImpact("btc", amountToBuyBtc.toString(), tokenToBuy, true);
if (!priceImpact) {
throw new Error('Price impact not found');
}
const percentageImpact = priceImpact * 100;
// Step 4: Risk assessment
if (percentageImpact > 10) {
throw new Error('Price impact is too high');
}
const warningLevel = percentageImpact > 10 ? "⚠️ High" :
percentageImpact > 5 ? "⚡ Medium" :
"✅ Low";
console.log(` Impact: ${warningLevel} ${percentageImpact.toFixed(2)}%`);
// Step 4.5: Calculate minimum amount received
const minimumReceived = getMinimumReceived("btc", amountToBuyBtc.toString(), tokenToBuy, true);
console.log(`📊 Minimum tokens to receive: ${minimumReceived.toLocaleString()}`);
// Step 5: Execute the trade
const buyRequest: TradeRequest = {
tokenid: tokenToBuy.id,
typeof: { buy: null },
amount: { btc: amountToBuy },
settings: [],
};
const buyResult = await odin.token_trade(buyRequest);
if ('err' in buyResult) {
throw new Error(buyResult.err);
}
console.log('✅ Token purchase completed successfully!');
console.log(`Purchased ${tokenToBuy.name} tokens with ${amountToBuyBtc} BTC`);
// Step 6: Advanced trading with slippage protection
const userSlippage = 0.5; // 5%
const expectedPrice = tokenToBuy.price;
const allowedSlippage = userSlippage
? (priceImpact ?? 0) + userSlippage
: null;
let slippage: any = [];
if (expectedPrice && allowedSlippage !== null) {
slippage = [
[expectedPrice, BigInt(Math.floor(allowedSlippage * 100000))],
];
}
const buyRequestWithSlippage: TradeRequest = {
tokenid: tokenToBuy.id,
typeof: { buy: null },
amount: { btc: amountToBuy },
settings: [{slippage}],
};
const buyResultWithSlippage = await odin.token_trade(buyRequestWithSlippage);
if ('err' in buyResultWithSlippage) {
throw new Error(buyResultWithSlippage.err);
}
console.log('\n\n✅ Token purchase completed successfully with slippage protection!');
console.log(`Purchased ${tokenToBuy.name} tokens with ${amountToBuyBtc} BTC with slippage protection`);
})();
Key Trading Concepts
1. Token Discovery
// Fetch tokens from the API with pagination
const response = await fetch(`${ODIN_API_URL}/tokens?page=1&limit=30`);
const data: { data: Token[] } = await response.json();
// Find tokens that are actively trading
const tokenToBuy = data.data.find((token: any) => token.trading);
2. Price Impact Calculation
// Calculate how your trade will affect the token price
const priceImpact = getPriceImpact("btc", amountToBuyBtc.toString(), tokenToBuy, true);
const percentageImpact = priceImpact * 100;
3. Minimum Amount Calculation
// Calculate the minimum amount of tokens you'll receive
const minimumReceived = getMinimumReceived("btc", amountToBuyBtc.toString(), tokenToBuy, true);
console.log(`📊 Minimum tokens to receive: ${minimumReceived.toLocaleString()}`);
4. Risk Assessment
// Implement safety checks before executing trades
if (percentageImpact > 10) {
throw new Error('Price impact is too high');
}
const warningLevel = percentageImpact > 10 ? "⚠️ High" :
percentageImpact > 5 ? "⚡ Medium" :
"✅ Low";
5. Trade Execution
// Create a buy order with proper type structure
const buyRequest: TradeRequest = {
tokenid: tokenToBuy.id,
typeof: { buy: null }, // Specify buy operation
amount: { btc: amountToBuy }, // Amount in BTC to spend
settings: [], // Optional trade settings (e.g., slippage)
};
// Execute the trade
const buyResult = await odin.token_trade(buyRequest);
6. Slippage Protection
// Configure slippage protection
const userSlippage = 0.5; // 5% user-defined slippage
const expectedPrice = tokenToBuy.price;
const allowedSlippage = userSlippage
? (priceImpact ?? 0) + userSlippage
: null;
// Create slippage settings
let slippage: any = [];
if (expectedPrice && allowedSlippage !== null) {
slippage = [
[expectedPrice, BigInt(Math.floor(allowedSlippage * 100000))],
];
}
// Execute trade with slippage protection
const buyRequestWithSlippage: TradeRequest = {
tokenid: tokenToBuy.id,
typeof: { buy: null },
amount: { btc: amountToBuy },
settings: [{slippage}], // Include slippage protection
};
token_trade documentation available
here.
Testing Your Trading Implementation
Run the complete trading workflow:
{ newBalance: 10000000n }
Impact: ✅ Low 2.15%
📊 Minimum tokens to receive: 45,678
✅ Token purchase completed successfully!
Purchased SampleToken tokens with 0.0001 BTC
✅ Token purchase completed successfully with slippage protection!
Purchased SampleToken tokens with 0.0001 BTC with slippage protection
Understanding Slippage Protection
What is Slippage?
Slippage occurs when the actual execution price of a trade differs from the expected price. This typically happens in volatile markets or when trading large amounts that impact the token’s price.
How Slippage Protection Works
// Calculate total allowable slippage
const userSlippage = 0.5; // 5% user-defined tolerance
const allowedSlippage = (priceImpact ?? 0) + userSlippage;
// Convert to required format for the canister
const slippageSettings = [
[expectedPrice, BigInt(Math.floor(allowedSlippage * 100000))],
];
Slippage Calculation Breakdown
- Price Impact: The immediate effect your trade has on the token price
- User Slippage: Additional tolerance you’re willing to accept (e.g., 5%)
- Total Allowable Slippage: Price impact + user-defined slippage
- Precision: Converted to basis points (100000 = 100%)
Trade Types with Slippage
// Buy tokens with slippage protection
const buyRequest: TradeRequest = {
tokenid: tokenToBuy.id,
typeof: { buy: null },
amount: { btc: amountToBuy },
settings: [{slippage: slippageSettings}],
};
// Sell tokens with slippage protection
const sellRequest: TradeRequest = {
tokenid: tokenToSell.id,
typeof: { sell: null },
amount: { token: tokenAmount },
settings: [{slippage: slippageSettings}],
};
Error Handling
Always implement proper error handling for trading operations:
// Check for trade execution errors
const buyResult = await odin.token_trade(buyRequest);
if ('err' in buyResult) {
console.error('Trade failed:', buyResult.err);
throw new Error(buyResult.err);
}
// Validate price impact before trading
if (!priceImpact) {
throw new Error('Price impact calculation failed');
}
// Validate slippage settings
if (expectedPrice && allowedSlippage !== null) {
if (allowedSlippage < 0) {
throw new Error('Slippage cannot be negative');
}
if (allowedSlippage > 1) {
console.warn('⚠️ High slippage tolerance set:', allowedSlippage * 100 + '%');
}
}
Always calculate and validate price impact before executing trades. High price impact can result in significant slippage and poor trade execution.
Development Environment Note: After executing trades, deposits, or withdrawals in the development environment, changes may not immediately appear in the Odin Dev UI. This is because the development indexer can sometimes lag behind or be temporarily offline.
Congratulations! You’ve successfully implemented token trading functionality with price impact analysis and risk assessment.