@jagad/icsi
Version:
Internet Computer Subaccount Indexer Library - TypeScript SDK for ICP multi-token subaccount management, transaction tracking, and automated sweeping with webhook support
175 lines (174 loc) • 6.58 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Tokens = void 0;
exports.getTokenConfig = getTokenConfig;
exports.getDepositAddresses = getDepositAddresses;
exports.getBalances = getBalances;
exports.getTransactionsByTokenType = getTransactionsByTokenType;
const query_1 = require("./query");
const update_1 = require("./update");
exports.Tokens = {
ICP: { ICP: null },
CKUSDC: { CKUSDC: null },
CKUSDT: { CKUSDT: null },
CKBTC: { CKBTC: null },
};
function getTokenConfig(tokenType) {
if ('ICP' in tokenType) {
return {
canisterId: process.env.ICP_CANISTER_ID || 'ryjl3-tyaaa-aaaaa-aaaba-cai',
symbol: 'ICP',
decimals: 8,
};
}
else if ('CKUSDC' in tokenType) {
return {
canisterId: process.env.CKUSDC_CANISTER_ID || 'xevnm-gaaaa-aaaar-qafnq-cai',
symbol: 'CKUSDC',
decimals: 6,
};
}
else if ('CKUSDT' in tokenType) {
return {
canisterId: process.env.CKUSDT_CANISTER_ID || 'cngnf-vqaaa-aaaar-qag4q-cai',
symbol: 'CKUSDT',
decimals: 6,
};
}
else if ('CKBTC' in tokenType) {
return {
canisterId: process.env.CKBTC_CANISTER_ID || 'mxzaz-hqaaa-aaaar-qaada-cai',
symbol: 'CKBTC',
decimals: 8,
};
}
throw new Error('Unknown token type');
}
async function getDepositAddresses(agent, canisterId) {
const addresses = [];
// Get registered tokens
const tokensResult = await (0, query_1.getRegisteredTokens)(agent, canisterId);
if ('Err' in tokensResult) {
throw new Error(`Failed to get registered tokens: ${tokensResult.Err}`);
}
const tokens = tokensResult.Ok;
for (const [tokenType, tokenName] of tokens) {
try {
// Get nonce for subaccount creation
const index = await (0, query_1.getNonce)(agent, canisterId);
// Try to create subaccount (might already exist)
await (0, update_1.addSubaccountForToken)(agent, canisterId, tokenType).catch(() => {
// Ignore if already exists
});
// Get subaccount ID
const subaccountId = await (0, query_1.getSubaccountId)(agent, canisterId, index, tokenType);
// Get ICRC account (deposit address)
const icrcAccount = await (0, query_1.getIcrcAccount)(agent, canisterId, index);
addresses.push({
tokenType,
tokenName,
subaccountId,
depositAddress: icrcAccount,
});
}
catch (error) {
console.error(`Error processing token ${tokenName}:`, error);
}
}
return addresses;
}
async function getBalances(agent, canisterId) {
const balances = [];
// Get all transactions
const transactionsResult = await (0, query_1.getUserVaultTransactions)(agent, canisterId, BigInt(0));
if ('Err' in transactionsResult) {
throw new Error(`Failed to get transactions: ${transactionsResult.Err}`);
}
const transactions = transactionsResult.Ok;
// Group by token type and calculate balances
const balanceMap = new Map();
for (const tx of transactions) {
const tokenKey = JSON.stringify(tx.token_type);
// Only count non-swept transactions
if (tx.sweep_status && 'NotSwept' in tx.sweep_status) {
let amount = BigInt(0);
if (tx.operation && tx.operation[0]) {
if ('Mint' in tx.operation[0]) {
amount = tx.operation[0].Mint.amount.e8s;
}
else if ('Transfer' in tx.operation[0]) {
amount = tx.operation[0].Transfer.amount.e8s;
}
}
const current = balanceMap.get(tokenKey) || {
tokenType: tx.token_type,
tokenName: '',
amount: BigInt(0),
};
current.amount += amount;
balanceMap.set(tokenKey, current);
}
}
// Get token names and decimals
const tokensResult = await (0, query_1.getRegisteredTokens)(agent, canisterId);
if ('Ok' in tokensResult) {
for (const [tokenType, tokenName] of tokensResult.Ok) {
const tokenKey = JSON.stringify(tokenType);
const balance = balanceMap.get(tokenKey);
if (balance && balance.amount > 0) {
const config = getTokenConfig(tokenType);
balances.push({
tokenType,
tokenName,
amount: balance.amount,
decimals: config.decimals,
});
}
}
}
return balances;
}
async function getTransactionsByTokenType(agent, canisterId, tokenType) {
// Validate token type
if (!('ICP' in tokenType ||
'CKUSDC' in tokenType ||
'CKUSDT' in tokenType ||
'CKBTC' in tokenType)) {
throw new Error('Invalid token type');
}
const transactionsResult = await (0, query_1.getUserVaultTransactions)(agent, canisterId, BigInt(0));
if ('Err' in transactionsResult) {
throw new Error(`Failed to get transactions: ${transactionsResult.Err}`);
}
const allTransactions = transactionsResult.Ok;
// Filter by token type
const tokenKey = JSON.stringify(tokenType);
const filteredTransactions = allTransactions.filter((tx) => JSON.stringify(tx.token_type) === tokenKey);
// Map to a more user-friendly format
return filteredTransactions.map((tx) => {
let amount = BigInt(0);
let from = '';
let to = '';
if (tx.operation && tx.operation[0]) {
if ('Mint' in tx.operation[0]) {
amount = tx.operation[0].Mint.amount.e8s;
to = Buffer.from(tx.operation[0].Mint.to).toString('hex');
}
else if ('Transfer' in tx.operation[0]) {
amount = tx.operation[0].Transfer.amount.e8s;
from = Buffer.from(tx.operation[0].Transfer.from).toString('hex');
to = Buffer.from(tx.operation[0].Transfer.to).toString('hex');
}
}
return {
blockIndex: tx.index,
amount: amount.toString(),
from,
to,
timestamp: tx.created_at_time.timestamp_nanos,
sweepStatus: tx.sweep_status,
memo: tx.memo.toString(),
txHash: tx.tx_hash,
};
});
}