Copy import {ethers} from 'ethers' ; // ^6.13.4
import type {TransactionRequest} from 'ethers' ;
const routerApiUrl = 'router-API' ;
const rpcUrl = 'network-RPC' ; // Replace with your Network RPC
const privateKey = 'your-private-key-here' ; // Replace with your private key
const wallet = new ethers .Wallet (privateKey);
// Connect to a provider (e.g., Infura or any Ethereum node)
const provider = new ethers .JsonRpcProvider (rpcUrl);
// Utility
async function signAndSendTransaction (tx : TransactionRequest ) {
try {
const signer = wallet .connect (provider);
if ( ! tx .gasLimit) {
// Estimate the transaction gas
const estimatedGas = await signer .estimateGas (tx);
tx .gasLimit = estimatedGas * BigInt ( 120 ) / BigInt ( 100 );
}
// Sign the transaction
const signedTx = await wallet .signTransaction (tx);
console .log ( 'Signed Transaction:' , signedTx);
// Send the signed transaction to the network
const txResponse = await signer .sendTransaction (tx);
console .log ( 'Transaction Hash:' , txResponse .hash);
// Wait for the transaction to be mined (optional)
const receipt = await txResponse .wait ();
console .log ( 'Transaction Mined:' , receipt);
return receipt;
} catch (error) {
console .error ( 'Error:' , error);
throw error;
}
}
type Address = `0x ${ string } `
export const EtherAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'
const erc20Abi = [
{
"constant" : true ,
"inputs" : [{ "name" : "_owner" , "type" : "address" } , { "name" : "_spender" , "type" : "address" }] ,
"name" : "allowance" ,
"outputs" : [{ "name" : "remaining" , "type" : "uint256" }] ,
"payable" : false ,
"stateMutability" : "view" ,
"type" : "function"
} ,
{
"constant" : false ,
"inputs" : [{ "name" : "_spender" , "type" : "address" } , { "name" : "_value" , "type" : "uint256" }] ,
"name" : "approve" ,
"outputs" : [] ,
"payable" : false ,
"stateMutability" : "nonpayable" ,
"type" : "function"
}
]
async function approve (token : Address , spender : Address , amount : string | bigint ) {
const contract = new ethers .Contract (token , erc20Abi , provider);
const allowance = await contract .allowance ( wallet .address , spender);
if (allowance < BigInt (amount)) {
const data = contract . interface .encodeFunctionData ( 'approve' , [spender , amount]);
return signAndSendTransaction ({to : token , data})
}
return Promise .resolve ( true );
}
function serializeParameters (params : Record < string , any >) {
const searchParams = new URLSearchParams (
Object .fromEntries ( Object .entries (params) .filter (([_ , v]) => !! v))
);
return searchParams .toString ();
}
interface Swap {
amountIn : string ;
amountOut : string ;
data : string ;
exchange : string ;
pool : Address ;
tokenIn : Address ;
tokenOut : Address ;
type : string ;
}
interface Split {
amountIn : string ;
amountOut : string ;
swaps : Swap [];
}
interface QuoteResponse {
amountIn : string ;
amountInUsd : number ;
amountOut : string ;
amountOutUsd : number ;
minAmountOut : string ;
splits : Split [];
tokenIn : Address ;
tokenOut : Address ;
}
interface Transaction {
data : string ;
router : Address ;
}
interface SwapResponse {
quote : QuoteResponse ;
tx : Transaction ;
}
interface QuoteParameters {
tokenIn : Address ;
tokenOut : Address ;
amountIn : string | bigint ;
exchanges ?: string [];
}
// Retrieves the optimal trading route and price quote between two tokens
export async function quote (args : QuoteParameters ) : Promise < QuoteResponse > {
const params = {
... args ,
amountIn : args . amountIn .toString () ,
exchanges : args . exchanges ?.join ( ',' ) ,
};
const quoteResponse = await fetch ( ` ${ routerApiUrl } /quote? ${ serializeParameters (params) } ` );
return quoteResponse .json ();
}
interface SwapParameters extends QuoteParameters {
slippage ?: number ;
receiver ?: Address ;
}
// Generates the transaction data needed to execute the swap through the router contract
export async function swap (args : SwapParameters ) {
const params = {
... args ,
amountIn : args . amountIn .toString () ,
exchanges : args . exchanges ?.join ( ',' ) ,
slippage : args ?. slippage ?.toString () ,
receiver : args .receiver || wallet .address ,
};
// get encoded transaction data
const encodingResponse = await fetch ( ` ${ routerApiUrl } /swap? ${ serializeParameters (params) } ` );
const encodingResult : SwapResponse = await encodingResponse .json ();
const tokenInIsNative = args . tokenIn .toLowerCase () === EtherAddress .toLowerCase ();
if ( ! tokenInIsNative) {
// approve ERC20
await approve ( args .tokenIn , encodingResult . tx .router , args .amountIn);
}
return await signAndSendTransaction ({
to : encodingResult . tx .router ,
value : tokenInIsNative ? args .amountIn : undefined ,
data : encodingResult . tx .data ,
});
}