import {Contract, Signer, utils, ethers, BigNumberish} 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 as 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'; 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 => { 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}: iParamsPerPool, pool: AavePools) => { switch (pool) { case AavePools.proto: return proto; 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); }; export const buildLiquiditySwapParams = ( assetToSwapToList: tEthereumAddress[], minAmountsToReceive: BigNumberish[], swapAllBalances: BigNumberish[], permitAmounts: BigNumberish[], deadlines: BigNumberish[], v: BigNumberish[], r: (string | Buffer)[], s: (string | Buffer)[] ) => { return ethers.utils.defaultAbiCoder.encode( [ 'address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]', ], [assetToSwapToList, minAmountsToReceive, swapAllBalances, permitAmounts, deadlines, v, r, s] ); }; export const buildRepayAdapterParams = ( assetToSwapTo: tEthereumAddress, repayAmount: BigNumberish, rateMode: BigNumberish, repayAllDebt: BigNumberish, permitAmount: BigNumberish, deadline: BigNumberish, v: BigNumberish, r: string | Buffer, s: string | Buffer ) => { return ethers.utils.defaultAbiCoder.encode( ['address', 'uint256', 'uint256', 'bool', 'uint256', 'uint256', 'uint8', 'bytes32', 'bytes32'], [assetToSwapTo, repayAmount, rateMode, repayAllDebt, permitAmount, deadline, v, r, s] ); };