Send tokens
This commit is contained in:
@@ -44,6 +44,7 @@ declare module 'fastify' {
|
||||
signPrivyMessage: typeof signPrivyMessage;
|
||||
approveToken: typeof approveToken;
|
||||
initAddress: typeof initAddress;
|
||||
sendToken: typeof sendToken;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,6 +187,15 @@ const tokenApprovalSchema = z.object({
|
||||
chainId: z.number().positive().optional()
|
||||
});
|
||||
|
||||
// Schema for token-sending request
|
||||
const tokenSendSchema = z.object({
|
||||
senderAddress: z.string().nonempty(),
|
||||
recipientAddress: z.string().nonempty(),
|
||||
ticker: z.string().nonempty(),
|
||||
amount: z.bigint().positive(),
|
||||
chainId: z.number().positive().optional()
|
||||
});
|
||||
|
||||
/**
|
||||
* Gets the chain name based on chain ID
|
||||
* @param chainId The chain ID
|
||||
@@ -642,6 +652,141 @@ export async function initAddress(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends tokens from one address to another using Privy wallet (implementation)
|
||||
* @param senderAddress The sender's wallet address
|
||||
* @param recipientAddress The recipient's wallet address
|
||||
* @param ticker The token ticker or enum value
|
||||
* @param amount The amount to send
|
||||
* @param chainId The chain ID (optional, defaults to ARBITRUM)
|
||||
* @returns The transaction hash
|
||||
*/
|
||||
export const sendTokenImpl = async (
|
||||
senderAddress: string,
|
||||
recipientAddress: string,
|
||||
ticker: string,
|
||||
amount: bigint,
|
||||
chainId?: number,
|
||||
): Promise<string> => {
|
||||
try {
|
||||
// Get token data from ticker
|
||||
const tokenData = GetToken(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}`);
|
||||
await approveContractImpl(
|
||||
senderAddress,
|
||||
tokenData.address,
|
||||
senderAddress, // Approve self to spend tokens
|
||||
chainId ?? ARBITRUM,
|
||||
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);
|
||||
|
||||
// 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,
|
||||
chainType: 'ethereum',
|
||||
caip2: networkName as string,
|
||||
transaction: {
|
||||
to: tokenData.address as Address,
|
||||
data: data,
|
||||
chainId: chainId,
|
||||
},
|
||||
} as any);
|
||||
|
||||
return hash;
|
||||
} catch (error) {
|
||||
console.error('Error sending token:', error);
|
||||
throw new Error(`Failed to send token: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends tokens from one address to another using Privy wallet
|
||||
* @param this The FastifyRequest instance
|
||||
* @param reply The FastifyReply instance
|
||||
* @param senderAddress The sender's wallet address
|
||||
* @param recipientAddress The recipient's wallet address
|
||||
* @param ticker The token ticker or enum value
|
||||
* @param amount The amount to send
|
||||
* @param chainId The chain ID (optional, defaults to ARBITRUM)
|
||||
* @returns The response object with success status and transaction hash
|
||||
*/
|
||||
export async function sendToken(
|
||||
this: FastifyRequest,
|
||||
reply: FastifyReply,
|
||||
senderAddress: string,
|
||||
recipientAddress: string,
|
||||
ticker: string,
|
||||
amount: bigint,
|
||||
chainId?: number
|
||||
) {
|
||||
try {
|
||||
// Validate the request parameters
|
||||
tokenSendSchema.parse({
|
||||
senderAddress,
|
||||
recipientAddress,
|
||||
ticker,
|
||||
amount,
|
||||
chainId
|
||||
});
|
||||
|
||||
if (!senderAddress) {
|
||||
throw new Error('Sender address is required for token transfer');
|
||||
}
|
||||
|
||||
if (!recipientAddress) {
|
||||
throw new Error('Recipient address is required for token transfer');
|
||||
}
|
||||
|
||||
if (!ticker) {
|
||||
throw new Error('Token ticker is required for token transfer');
|
||||
}
|
||||
|
||||
if (!amount || amount <= 0n) {
|
||||
throw new Error('Valid amount is required for token transfer');
|
||||
}
|
||||
|
||||
// Call the sendTokenImpl function
|
||||
const hash = await sendTokenImpl(senderAddress, recipientAddress, ticker, amount, chainId);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
hash: hash
|
||||
};
|
||||
} catch (error) {
|
||||
this.log.error(error);
|
||||
|
||||
// Return appropriate error response
|
||||
reply.status(error instanceof z.ZodError ? 400 : 500);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'An unknown error occurred'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The use of fastify-plugin is required to be able
|
||||
* to export the decorators to the outer scope
|
||||
@@ -662,6 +807,10 @@ export default fp(async (fastify) => {
|
||||
return initAddress.call(this, reply, address);
|
||||
});
|
||||
|
||||
fastify.decorateRequest('sendToken', async function(this: FastifyRequest, reply: FastifyReply, senderAddress: string, recipientAddress: string, ticker: string, amount: bigint, chainId?: number) {
|
||||
return sendToken.call(this, reply, senderAddress, recipientAddress, ticker, amount, chainId);
|
||||
});
|
||||
|
||||
// Test the Privy client initialization
|
||||
try {
|
||||
const testClient = getPrivyClient(fastify);
|
||||
|
||||
Reference in New Issue
Block a user