Skip to content

Commit 23e2503

Browse files
author
vcart
committed
chore: adding auto conversion to tokenBaseUnits on transfers
1 parent 13e65d5 commit 23e2503

1 file changed

Lines changed: 61 additions & 3 deletions

File tree

src/core/services/transfer.ts

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ const TOKEN_ADDRESSES = {
88
STRK: '0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d'
99
};
1010

11+
// Default decimals for common tokens
12+
const TOKEN_DECIMALS = {
13+
ETH: 18,
14+
STRK: 18
15+
};
16+
1117
// Common interface for all transfer operations
1218
interface TransferResult {
1319
txHash: string;
@@ -23,19 +29,60 @@ interface TransferBaseParams {
2329
maxFee?: string | bigint;
2430
}
2531

32+
/**
33+
* Converts a human-readable amount to token units
34+
* @param amount The amount in human-readable form (e.g., "0.00001")
35+
* @param decimals The number of decimals for the token
36+
* @returns The amount in token units as BigInt
37+
*/
38+
function parseTokenAmount(amount: string | bigint, decimals: number): bigint {
39+
// If amount is already a bigint, assume it's already in token units
40+
if (typeof amount === 'bigint') {
41+
return amount;
42+
}
43+
44+
// Handle decimal amounts
45+
const amountStr = amount.toString();
46+
47+
// Check if the amount contains a decimal point
48+
if (amountStr.includes('.')) {
49+
const [integerPart, fractionalPart = ''] = amountStr.split('.');
50+
51+
// Ensure the fractional part isn't longer than allowed decimals
52+
if (fractionalPart.length > decimals) {
53+
throw new Error(`Amount has too many decimal places. Maximum allowed: ${decimals}`);
54+
}
55+
56+
// Pad the fractional part with zeros if needed
57+
const paddedFractionalPart = fractionalPart.padEnd(decimals, '0');
58+
59+
// Combine the integer and fractional parts without the decimal point
60+
const combinedAmount = `${integerPart}${paddedFractionalPart}`;
61+
62+
// Remove leading zeros (if any) and convert to BigInt
63+
return BigInt(combinedAmount.replace(/^0+/, '') || '0');
64+
}
65+
66+
// No decimal point, multiply by 10^decimals
67+
return BigInt(amountStr) * BigInt(10) ** BigInt(decimals);
68+
}
69+
2670
/**
2771
* Prepare a transfer transaction
2872
* @param params Common transfer parameters
2973
* @param tokenAddress The token contract address
74+
* @param decimals The number of decimals for the token
3075
* @param network Network name
3176
* @returns Prepared account, transaction, and addresses
3277
*/
3378
async function prepareTransfer(
3479
params: TransferBaseParams,
3580
tokenAddress: string,
81+
decimals: number,
3682
network: string
3783
) {
38-
const amount = typeof params.amount === 'string' ? BigInt(params.amount) : params.amount;
84+
// Convert amount to token units, accounting for decimals
85+
const amount = parseTokenAmount(params.amount, decimals);
3986
const fromAddress = parseStarknetAddress(params.from);
4087

4188
// Resolve the 'to' parameter which could be either an address or a Starknet ID
@@ -115,6 +162,7 @@ export async function transferETH(
115162
const { account, tx } = await prepareTransfer(
116163
params,
117164
TOKEN_ADDRESSES.ETH,
165+
TOKEN_DECIMALS.ETH,
118166
network
119167
);
120168

@@ -139,6 +187,7 @@ export async function transferSTRK(
139187
const { account, tx } = await prepareTransfer(
140188
params,
141189
TOKEN_ADDRESSES.STRK,
190+
TOKEN_DECIMALS.STRK,
142191
network
143192
);
144193

@@ -156,15 +205,25 @@ export async function transferSTRK(
156205
* @returns Transaction details
157206
*/
158207
export async function transferERC20(
159-
params: TransferBaseParams & { tokenAddress: string },
208+
params: TransferBaseParams & { tokenAddress: string; decimals?: number },
160209
network = 'mainnet'
161210
): Promise<TransferResult> {
162211
try {
163212
const tokenAddress = parseStarknetAddress(params.tokenAddress);
164213

214+
// If decimals not provided, fetch them from the token contract
215+
let decimals = params.decimals;
216+
if (decimals === undefined) {
217+
const provider = getProvider(network);
218+
const contract = getContract(tokenAddress, provider, network);
219+
const decimalsResponse = await contract.call('decimals', []);
220+
decimals = Number(decimalsResponse.toString());
221+
}
222+
165223
const { account, tx } = await prepareTransfer(
166224
params,
167225
tokenAddress,
226+
decimals,
168227
network
169228
);
170229

@@ -193,7 +252,6 @@ export async function executeContract(
193252
network = 'mainnet'
194253
): Promise<TransferResult> {
195254
try {
196-
const provider = getProvider(network);
197255
const accountAddress = parseStarknetAddress(params.accountAddress);
198256

199257
// Resolve the contract address which could be either an address or a Starknet ID

0 commit comments

Comments
 (0)