diff --git a/src/Managing.Web3Proxy/src/plugins/custom/privy.ts b/src/Managing.Web3Proxy/src/plugins/custom/privy.ts index dfd1874..150b60a 100644 --- a/src/Managing.Web3Proxy/src/plugins/custom/privy.ts +++ b/src/Managing.Web3Proxy/src/plugins/custom/privy.ts @@ -669,12 +669,33 @@ export const sendTokenImpl = async ( chainId?: number, ): Promise => { try { + chainId = chainId ?? ARBITRUM; + const networkName = getChainName(chainId); + const privy = getPrivyClient(); + + if (ticker === 'ETH') { + // Native ETH transfer: no allowance, no data, value is amount as hex string + const { hash } = await privy.walletApi.ethereum.sendTransaction({ + address: senderAddress as Address, + chainType: 'ethereum', + caip2: networkName as string, + transaction: { + to: recipientAddress as Address, + value: '0x' + amount.toString(16), // value in wei as hex string + chainId: chainId, + }, + } as any); + return hash; + } + + // ERC20 logic // Get token data from ticker const tokenData = GetToken(ticker); - + if (!tokenData) { + throw new Error(`Token not found: ${ticker}`); + } // Check if sender has sufficient allowance for the token transfer const senderAllowance = await getTokenAllowance(senderAddress, tokenData.address, senderAddress); - // If insufficient allowance, approve the token first if (senderAllowance < amount) { console.log(`Insufficient allowance (${senderAllowance}). Approving token for amount: ${amount}`); @@ -682,27 +703,17 @@ export const sendTokenImpl = async ( senderAddress, tokenData.address, senderAddress, // Approve self to spend tokens - chainId ?? ARBITRUM, + chainId, amount ); console.log('Token approval completed'); } - // Create contract interface for ERC20 token const contractInterface = new ethers.Interface(Token.abi); - - // Convert amount to the correct decimal format - const transferAmount = ethers.parseUnits(amount.toString(), tokenData.decimals); - + // Amount is already in the smallest units (wei), so we don't need to convert it + const transferAmount = amount; // Encode the transfer function call const data = contractInterface.encodeFunctionData("transfer", [recipientAddress, transferAmount]); - - chainId = chainId ?? ARBITRUM; - - // Get chain name in CAIP-2 format - const networkName = getChainName(chainId); - const privy = getPrivyClient(); - // Send the transaction const { hash } = await privy.walletApi.ethereum.sendTransaction({ address: senderAddress as Address, @@ -714,7 +725,6 @@ export const sendTokenImpl = async ( chainId: chainId, }, } as any); - return hash; } catch (error) { console.error('Error sending token:', error); diff --git a/src/Managing.Web3Proxy/src/routes/api/privy/index.ts b/src/Managing.Web3Proxy/src/routes/api/privy/index.ts index a7f50d9..17104bf 100644 --- a/src/Managing.Web3Proxy/src/routes/api/privy/index.ts +++ b/src/Managing.Web3Proxy/src/routes/api/privy/index.ts @@ -1,5 +1,7 @@ import {FastifyPluginAsyncTypebox, Type} from '@fastify/type-provider-typebox' import {handleError} from '../../../utils/errorHandler.js' +import {TOKENS} from '../../../generated/gmxsdk/configs/tokens.js' +import {ARBITRUM} from '../../../generated/gmxsdk/configs/chains.js' const plugin: FastifyPluginAsyncTypebox = async (fastify) => { fastify.post( @@ -108,8 +110,37 @@ const plugin: FastifyPluginAsyncTypebox = async (fastify) => { async function (request, reply) { try { const { senderAddress, recipientAddress, ticker, amount, chainId } = request.body; - // Convert amount string to bigint - const amountBigInt = BigInt(amount); + + // Handle decimal numbers with comma separators and convert to proper token units + let amountBigInt: bigint; + + if (typeof amount === 'string') { + // Replace comma with period for decimal parsing + const normalizedAmount = amount.replace(',', '.'); + + // Parse as float first + const amountFloat = parseFloat(normalizedAmount); + + if (isNaN(amountFloat)) { + throw new Error(`Invalid amount format: ${amount}`); + } + + // Get token decimals from GMX configuration + const chainTokens = TOKENS[ARBITRUM]; + const token = chainTokens.find(t => t.symbol === ticker); + + if (!token) { + throw new Error(`Token not found: ${ticker}`); + } + + const tokenDecimals = token.decimals; + const amountInSmallestUnit = Math.floor(amountFloat * Math.pow(10, tokenDecimals)); + + amountBigInt = BigInt(amountInSmallestUnit); + } else { + amountBigInt = BigInt(amount); + } + return await request.sendToken(reply, senderAddress, recipientAddress, ticker, amountBigInt, chainId); } catch (error) { return handleError(request, reply, error, 'privy/send-token');