import { Contract, ethers } from 'ethers'; import fs from 'fs'; import { TransactionExecutedEvent } from '../src/events'; import { TransactionManagerRequest } from '../src/transaction-inputs/transaction-manager-request'; import { TransactionManagerResponse } from '../src/transaction-inputs/transaction-manager-response'; import { TransactionManager } from '../src/transaction-manager'; import TokenAbi from './test-abis/TokenAbi.json'; export const TEST_PASSPHRASE = process.env.PASSPHRASE || ''; export type TransactionResult = { transactionId: string; transactionHash: string | undefined; blockNumber: number | undefined; }; // get a dummy wallet and provider from ethers export async function getWallet( phassphrase: string, network: string, ): Promise { const identityFile = 'test/identity.json'; let wallet; // check if identity file already exists try { const walletString = fs.readFileSync(identityFile); // read wallet from file wallet = await ethers.Wallet.fromEncryptedJson( walletString.toString(), phassphrase, ); } catch (e) { wallet = ethers.Wallet.createRandom(); // set passphrase in the wallet const encryptedWallet = await wallet.encrypt(phassphrase); fs.writeFileSync('test/identity.json', encryptedWallet); } wallet = connectNetworkToWallet(network, wallet); return wallet; } function connectNetworkToWallet( network: string, wallet: ethers.Wallet, ): ethers.Wallet { const networkConfig = { network: network, config: {}, }; networkConfig.config[network] = network === 'goerli' ? `https://eth-goerli.g.alchemy.com/v2/${process.env.GOERLI_ALCHEMY_KEY}` : network == 'maticmum' ? `https://polygon-mumbai.g.alchemy.com/v2/${process.env.MUMBAI_ALCHEMY_KEY}` : `https://polygon-mainnet.g.alchemy.com/v2/${process.env.MATTIC_ALCHEMY_KEY}`; // Setup a provider that we can attach to the wallet const networkProvider = new ethers.providers.JsonRpcProvider( networkConfig.config[network], networkConfig.network, ); // Now we take the signer and save it to a key'd object so we can switch around when interacting with various chains const linkedWallet = wallet.connect(networkProvider); return linkedWallet; } export function getTokenApprovalTransactionGoerli( wallet: ethers.Wallet, chainId: number, ) { const gasLimit = 100; const maxFeePerGas = 100; const maxPriorityFeePerGas = 100; const contractAddress = '0xdB3b43fd36eCb8001930E094F2984bDD9DA61b34'; const params = [wallet.address, 0]; const tokenContract = new ethers.Contract(contractAddress, TokenAbi, wallet); const wrappedTransaction = TransactionManagerRequest.newTransactionManagerRequest( tokenContract, 'approve', params, { gasLimit, maxFeePerGas, maxPriorityFeePerGas, type: 2, }, chainId, ); return wrappedTransaction; } export function getTokenApprovalTransactionPolygon( wallet: ethers.Wallet, chainId: number, ): TransactionManagerRequest { const gasLimit = 100; const maxFeePerGas = 100; const maxPriorityFeePerGas = 100; const contractAddress = '0xCA961A22E3ace0D5Fc7A86b033C81B89bC4985Cb'; const params = [wallet.address, 0]; const tokenContract = new ethers.Contract(contractAddress, TokenAbi, wallet); const gasMultiplier = 1; const wrappedTransaction = TransactionManagerRequest.newTransactionManagerRequest( tokenContract, 'approve', params, { gasLimit, maxFeePerGas, maxPriorityFeePerGas, type: 2, }, chainId, gasMultiplier, { trackingId: 'test-some-address', }, ); return wrappedTransaction; } export function getTokenApprovalTransactionMumbai( wallet: ethers.Wallet, chainId: number, ): TransactionManagerRequest { const gasLimit = 100; const maxFeePerGas = 100; const maxPriorityFeePerGas = 100; const contractAddress = '0x8f7116ca03aeb48547d0e2edd3faa73bfb232538'; const params = [wallet.address, 0]; const tokenContract = new ethers.Contract(contractAddress, TokenAbi, wallet); const wrappedTransaction = TransactionManagerRequest.newTransactionManagerRequest( tokenContract, 'approve', params, { gasLimit, // maxFeePerGas, // maxPriorityFeePerGas, type: 1, }, chainId, ); return wrappedTransaction; } export function printTransactionResults( transactionResults: Array, chainId: number, ) { // print chain id console.log( `========================== Chain Id : ${chainId} ==========================`, ); console.log( '================== Transaction Manager Results ===================', ); // total transactions console.log(`Total Transactions : ${transactionResults.length}`); transactionResults.map((transactionResult) => { console.log( `Transaction Id : ${transactionResult.transactionId} Transaction Hash : ${transactionResult.transactionHash} Block Number : ${transactionResult.blockNumber}`, ); }); // calculate transactions per block let transactionsPerBlock: Map = new Map(); transactionResults.map((transactionResult) => { if (transactionResult.blockNumber) { if (transactionsPerBlock.has(transactionResult.blockNumber)) { transactionsPerBlock.set( transactionResult.blockNumber, transactionsPerBlock.get(transactionResult.blockNumber)! + 1, ); } else { transactionsPerBlock.set(transactionResult.blockNumber, 1); } } }); console.log('================== Transactions Per Block ==================='); transactionsPerBlock.forEach((value: number, key: number) => { console.log(`Block Number : ${key} Transactions : ${value}`); }); console.log( '================== Transaction Manager Results ===================', ); // max transactions in block with block number let maxTransactions = 0; let maxTransactionsBlockNumber = 0; transactionsPerBlock.forEach((value: number, key: number) => { if (value > maxTransactions) { maxTransactions = value; maxTransactionsBlockNumber = key; } }); console.log( `Maximum transactions in block : ${maxTransactions} Block Number : ${maxTransactionsBlockNumber}`, ); // min transactions in block with block number let minTransactions = 0; let minTransactionsBlockNumber = 0; transactionsPerBlock.forEach((value: number, key: number) => { if (value < minTransactions) { minTransactions = value; minTransactionsBlockNumber = key; } }); console.log( `Minimum transactions in block : ${minTransactions} Block Number : ${minTransactionsBlockNumber}`, ); // calculate average transactions per block let totalTransactions = 0; transactionsPerBlock.forEach((value: number) => { totalTransactions += value; }); console.log( `Average transactions per block : ${ totalTransactions / transactionsPerBlock.size }`, ); // calculate min transactions in block } export async function runTransactions( chainId: number, transactions: Array, transactionManager: TransactionManager, ): Promise { // await transactionManager.start(); let transactionManagerResponses: Array = []; let transactionResults: Array = []; for (const transaction of transactions) { const transactionManagerResponse = await transactionManager.queueTransaction( transaction, 1, ); console.log('Transaction queued', transactionManagerResponse.transactionId); transactionManagerResponse.events.on( 'transactionExecuted', (event: TransactionExecutedEvent) => { console.log('Transaction executed', event); transactionResults.push({ transactionId: event.transactionId, transactionHash: event.transactionReceipt?.transactionHash, blockNumber: event.transactionReceipt?.blockNumber, } as TransactionResult); }, ); transactionManagerResponses.push(transactionManagerResponse); } // wait for last transaction with timeout let transactionTimeout = 5 * 60 * 60 * 1000; await transactionManagerResponses[ transactionManagerResponses.length - 1 ].wait(transactionTimeout); console.log('Processing completed'); printTransactionResults(transactionResults, chainId); }