import { Contract, Signer, utils, ethers } from 'ethers'; import { signTypedData_v4 } from 'eth-sig-util'; import { fromRpcSig, ECDSASignature } from 'ethereumjs-util'; import BigNumber from 'bignumber.js'; import { getDb, DRE, waitForTx } from './misc-utils'; import { tEthereumAddress, eContractid, tStringTokenSmallUnits, eEthereumNetwork, AavePools, iParamsPerNetwork, iParamsPerPool, } from './types'; import { MintableERC20 } from '../types/MintableERC20'; import { Artifact } from 'hardhat/types'; import { Artifact as BuidlerArtifact } from '@nomiclabs/buidler/types'; import { verifyContract } from './etherscan-verification'; import { getIErc20Detailed } from './contracts-getters'; import { addGas, totalGas } from '../gas-tracker'; export type MockTokenMap = { [symbol: string]: MintableERC20 }; export const registerContractInJsonDb = async (contractId: string, contractInstance: Contract) => { const currentNetwork = DRE.network.name; const MAINNET_FORK = process.env.MAINNET_FORK === 'true'; if (MAINNET_FORK || (currentNetwork !== 'hardhat' && !currentNetwork.includes('coverage'))) { console.log(`*** ${contractId} ***\n`); console.log(`Network: ${currentNetwork}`); console.log(`tx: ${contractInstance.deployTransaction.hash}`); console.log(`contract address: ${contractInstance.address}`); console.log(`deployer address: ${contractInstance.deployTransaction.from}`); console.log(`gas price: ${contractInstance.deployTransaction.gasPrice}`); console.log(`gas used: ${contractInstance.deployTransaction.gasLimit}`); console.log(`\n******`); console.log(); } await getDb() .set(`${contractId}.${currentNetwork}`, { address: contractInstance.address, deployer: contractInstance.deployTransaction.from, }) .write(); }; export const insertContractAddressInDb = async (id: eContractid, address: tEthereumAddress) => await getDb() .set(`${id}.${DRE.network.name}`, { address, }) .write(); export const rawInsertContractAddressInDb = async (id: string, address: tEthereumAddress) => await getDb() .set(`${id}.${DRE.network.name}`, { address, }) .write(); export const getEthersSigners = async (): Promise => await Promise.all(await DRE.ethers.getSigners()); export const getEthersSignersAddresses = async (): Promise => await Promise.all((await DRE.ethers.getSigners()).map((signer) => signer.getAddress())); export const getCurrentBlock = async () => { return DRE.ethers.provider.getBlockNumber(); }; export const decodeAbiNumber = (data: string): number => parseInt(utils.defaultAbiCoder.decode(['uint256'], data).toString()); export const deployContract = async ( contractName: string, args: any[] ): Promise => { const contract = (await (await DRE.ethers.getContractFactory(contractName)).deploy( ...args )) as ContractType; await waitForTx(contract.deployTransaction); await registerContractInJsonDb(contractName, contract); return contract; }; export const withSaveAndVerify = async ( instance: ContractType, id: string, args: (string | string[])[], verify?: boolean ): Promise => { addGas(instance.deployTransaction.gasLimit); console.log("Current totalGas value:", totalGas); console.log("Logged gas limit:", instance.deployTransaction.gasLimit); await waitForTx(instance.deployTransaction); await registerContractInJsonDb(id, instance); if (DRE.network.name.includes('tenderly')) { await (DRE as any).tenderlyRPC.verify({ name: id, address: instance.address, }); } if (verify) { await verifyContract(instance.address, args); } return instance; }; export const getContract = async ( contractName: string, address: string ): Promise => (await DRE.ethers.getContractAt(contractName, address)) as ContractType; export const linkBytecode = (artifact: BuidlerArtifact | Artifact, libraries: any) => { let bytecode = artifact.bytecode; for (const [fileName, fileReferences] of Object.entries(artifact.linkReferences)) { for (const [libName, fixups] of Object.entries(fileReferences)) { const addr = libraries[libName]; if (addr === undefined) { continue; } for (const fixup of fixups) { bytecode = bytecode.substr(0, 2 + fixup.start * 2) + addr.substr(2) + bytecode.substr(2 + (fixup.start + fixup.length) * 2); } } } return bytecode; }; export const getParamPerNetwork = ( { kovan, ropsten, main, buidlerevm, coverage, tenderlyMain }: iParamsPerNetwork, network: eEthereumNetwork ) => { const MAINNET_FORK = process.env.MAINNET_FORK === 'true'; if (MAINNET_FORK) { return main; } switch (network) { case eEthereumNetwork.coverage: return coverage; case eEthereumNetwork.buidlerevm: return buidlerevm; case eEthereumNetwork.hardhat: return buidlerevm; case eEthereumNetwork.kovan: return kovan; case eEthereumNetwork.ropsten: return ropsten; case eEthereumNetwork.main: return main; case eEthereumNetwork.tenderlyMain: return tenderlyMain; } }; export const getParamPerPool = ({ proto, uniswap }: iParamsPerPool, pool: AavePools) => { switch (pool) { case AavePools.proto: return proto; case AavePools.uniswap: return uniswap; default: return proto; } }; export const convertToCurrencyDecimals = async (tokenAddress: tEthereumAddress, amount: string) => { const token = await getIErc20Detailed(tokenAddress); let decimals = (await token.decimals()).toString(); return ethers.utils.parseUnits(amount, decimals); }; export const convertToCurrencyUnits = async (tokenAddress: string, amount: string) => { const token = await getIErc20Detailed(tokenAddress); let decimals = new BigNumber(await token.decimals()); const currencyUnit = new BigNumber(10).pow(decimals); const amountInCurrencyUnits = new BigNumber(amount).div(currencyUnit); return amountInCurrencyUnits.toFixed(); }; export const buildPermitParams = ( chainId: number, token: tEthereumAddress, revision: string, tokenName: string, owner: tEthereumAddress, spender: tEthereumAddress, nonce: number, deadline: string, value: tStringTokenSmallUnits ) => ({ types: { EIP712Domain: [ { name: 'name', type: 'string' }, { name: 'version', type: 'string' }, { name: 'chainId', type: 'uint256' }, { name: 'verifyingContract', type: 'address' }, ], Permit: [ { name: 'owner', type: 'address' }, { name: 'spender', type: 'address' }, { name: 'value', type: 'uint256' }, { name: 'nonce', type: 'uint256' }, { name: 'deadline', type: 'uint256' }, ], }, primaryType: 'Permit' as const, domain: { name: tokenName, version: revision, chainId: chainId, verifyingContract: token, }, message: { owner, spender, value, nonce, deadline, }, }); export const getSignatureFromTypedData = ( privateKey: string, typedData: any // TODO: should be TypedData, from eth-sig-utils, but TS doesn't accept it ): ECDSASignature => { const signature = signTypedData_v4(Buffer.from(privateKey.substring(2, 66), 'hex'), { data: typedData, }); return fromRpcSig(signature); };