import {ethers} from 'ethers'; // ^6.13.4
import type {TransactionRequest} from 'ethers';
const routerApiUrl = 'https://ag-api.hyperflow.fun/v1/hyperevm';
const rpcUrl = 'network-RPC'; // Replace with your network RPC
const privateKey = 'your-private-key-here'; // Replace with your private key
const apiKey = process.env.HYPERFLOW_API_KEY; // Optional: raises the rate limit
const apiHeaders = apiKey ? {'x-api-key': apiKey} : undefined;
const wallet = new ethers.Wallet(privateKey);
const provider = new ethers.JsonRpcProvider(rpcUrl);
async function signAndSendTransaction(tx: TransactionRequest) {
const signer = wallet.connect(provider);
if (!tx.gasLimit) {
const estimatedGas = await signer.estimateGas(tx);
tx.gasLimit = estimatedGas * BigInt(120) / BigInt(100);
}
const txResponse = await signer.sendTransaction(tx);
console.log('Transaction Hash:', txResponse.hash);
const receipt = await txResponse.wait();
console.log('Transaction Mined:', receipt);
return receipt;
}
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 !== undefined && v !== null && v !== ''))
);
return searchParams.toString();
}
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) {
const params = {
...args,
amountIn: args.amountIn.toString(),
exchanges: args.exchanges?.join(','),
};
const quoteResponse = await fetch(`${routerApiUrl}/quote?${serializeParameters(params)}`, {headers: apiHeaders});
return quoteResponse.json();
}
interface SwapParameters extends QuoteParameters {
slippage?: number;
receiver?: Address;
}
// Generates the transaction data needed to execute the swap through the router
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,
};
const encodingResponse = await fetch(`${routerApiUrl}/swap?${serializeParameters(params)}`, {headers: apiHeaders});
const encodingResult = await encodingResponse.json();
const tokenInIsNative = args.tokenIn.toLowerCase() === EtherAddress.toLowerCase();
if (!tokenInIsNative) {
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,
});
}