2021-01-14 10:53:48 +00:00
|
|
|
import { Contract, Signer, utils, ethers, BigNumberish } from 'ethers';
|
2020-11-18 18:18:02 +00:00
|
|
|
import { signTypedData_v4 } from 'eth-sig-util';
|
|
|
|
import { fromRpcSig, ECDSASignature } from 'ethereumjs-util';
|
2020-10-15 18:28:12 +00:00
|
|
|
import BigNumber from 'bignumber.js';
|
2020-11-18 18:18:02 +00:00
|
|
|
import { getDb, DRE, waitForTx } from './misc-utils';
|
2020-06-08 12:03:40 +00:00
|
|
|
import {
|
|
|
|
tEthereumAddress,
|
|
|
|
eContractid,
|
|
|
|
tStringTokenSmallUnits,
|
|
|
|
eEthereumNetwork,
|
|
|
|
AavePools,
|
|
|
|
iParamsPerNetwork,
|
|
|
|
iParamsPerPool,
|
2021-02-23 14:42:47 +00:00
|
|
|
ePolygonNetwork,
|
|
|
|
eXDaiNetwork,
|
|
|
|
eNetwork,
|
|
|
|
iParamsPerNetworkAll,
|
|
|
|
iEthereumParamsPerNetwork,
|
|
|
|
iPolygonParamsPerNetwork,
|
|
|
|
iXDaiParamsPerNetwork,
|
2020-07-13 08:54:08 +00:00
|
|
|
} from './types';
|
2020-11-18 18:18:02 +00:00
|
|
|
import { MintableERC20 } from '../types/MintableERC20';
|
|
|
|
import { Artifact } from 'hardhat/types';
|
|
|
|
import { Artifact as BuidlerArtifact } from '@nomiclabs/buidler/types';
|
2021-03-11 15:55:04 +00:00
|
|
|
import { verifyEtherscanContract } from './etherscan-verification';
|
2021-05-17 12:35:21 +00:00
|
|
|
import { getFirstSigner, getIErc20Detailed } from './contracts-getters';
|
2021-03-04 15:10:39 +00:00
|
|
|
import { usingTenderly, verifyAtTenderly } from './tenderly-utils';
|
|
|
|
import { usingPolygon, verifyAtPolygon } from './polygon-utils';
|
2021-05-17 12:35:21 +00:00
|
|
|
import { getDefenderRelaySigner, usingDefender } from './defender-utils';
|
2020-08-25 12:15:35 +00:00
|
|
|
|
2020-11-18 18:18:02 +00:00
|
|
|
export type MockTokenMap = { [symbol: string]: MintableERC20 };
|
2020-07-13 08:54:08 +00:00
|
|
|
|
|
|
|
export const registerContractInJsonDb = async (contractId: string, contractInstance: Contract) => {
|
2020-11-05 12:44:20 +00:00
|
|
|
const currentNetwork = DRE.network.name;
|
2021-05-10 13:34:31 +00:00
|
|
|
const FORK = process.env.FORK;
|
|
|
|
if (FORK || (currentNetwork !== 'hardhat' && !currentNetwork.includes('coverage'))) {
|
2020-05-29 14:55:31 +00:00
|
|
|
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();
|
|
|
|
};
|
|
|
|
|
2020-07-13 08:54:08 +00:00
|
|
|
export const insertContractAddressInDb = async (id: eContractid, address: tEthereumAddress) =>
|
2020-06-08 12:03:40 +00:00
|
|
|
await getDb()
|
2020-11-05 12:44:20 +00:00
|
|
|
.set(`${id}.${DRE.network.name}`, {
|
2020-06-08 12:03:40 +00:00
|
|
|
address,
|
|
|
|
})
|
|
|
|
.write();
|
|
|
|
|
2020-11-10 13:18:48 +00:00
|
|
|
export const rawInsertContractAddressInDb = async (id: string, address: tEthereumAddress) =>
|
|
|
|
await getDb()
|
|
|
|
.set(`${id}.${DRE.network.name}`, {
|
|
|
|
address,
|
|
|
|
})
|
|
|
|
.write();
|
|
|
|
|
2021-05-17 12:35:21 +00:00
|
|
|
export const getEthersSigners = async (): Promise<Signer[]> => {
|
|
|
|
const ethersSigners = await Promise.all(await DRE.ethers.getSigners());
|
|
|
|
|
|
|
|
if (usingDefender()) {
|
|
|
|
const [, ...users] = ethersSigners;
|
|
|
|
return [await getDefenderRelaySigner(), ...users];
|
|
|
|
}
|
|
|
|
return ethersSigners;
|
|
|
|
};
|
2020-05-29 14:55:31 +00:00
|
|
|
|
2020-07-13 08:54:08 +00:00
|
|
|
export const getEthersSignersAddresses = async (): Promise<tEthereumAddress[]> =>
|
2021-05-17 12:35:21 +00:00
|
|
|
await Promise.all((await getEthersSigners()).map((signer) => signer.getAddress()));
|
2020-05-29 14:55:31 +00:00
|
|
|
|
|
|
|
export const getCurrentBlock = async () => {
|
2020-11-05 12:44:20 +00:00
|
|
|
return DRE.ethers.provider.getBlockNumber();
|
2020-05-29 14:55:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
export const decodeAbiNumber = (data: string): number =>
|
2020-07-13 08:54:08 +00:00
|
|
|
parseInt(utils.defaultAbiCoder.decode(['uint256'], data).toString());
|
2020-05-29 14:55:31 +00:00
|
|
|
|
2020-08-10 18:20:08 +00:00
|
|
|
export const deployContract = async <ContractType extends Contract>(
|
2020-05-29 14:55:31 +00:00
|
|
|
contractName: string,
|
|
|
|
args: any[]
|
2020-06-08 12:03:40 +00:00
|
|
|
): Promise<ContractType> => {
|
2021-05-17 12:35:21 +00:00
|
|
|
const contract = (await (await DRE.ethers.getContractFactory(contractName))
|
|
|
|
.connect(await getFirstSigner())
|
|
|
|
.deploy(...args)) as ContractType;
|
2020-10-21 09:42:27 +00:00
|
|
|
await waitForTx(contract.deployTransaction);
|
2020-06-08 12:03:40 +00:00
|
|
|
await registerContractInJsonDb(<eContractid>contractName, contract);
|
|
|
|
return contract;
|
|
|
|
};
|
|
|
|
|
2020-10-15 18:28:12 +00:00
|
|
|
export const withSaveAndVerify = async <ContractType extends Contract>(
|
|
|
|
instance: ContractType,
|
2020-10-15 17:19:02 +00:00
|
|
|
id: string,
|
|
|
|
args: (string | string[])[],
|
|
|
|
verify?: boolean
|
2020-10-15 18:28:12 +00:00
|
|
|
): Promise<ContractType> => {
|
2020-10-23 13:18:01 +00:00
|
|
|
await waitForTx(instance.deployTransaction);
|
2020-10-15 17:19:02 +00:00
|
|
|
await registerContractInJsonDb(id, instance);
|
|
|
|
if (verify) {
|
2021-03-11 15:55:04 +00:00
|
|
|
await verifyContract(id, instance, args);
|
2020-10-15 17:19:02 +00:00
|
|
|
}
|
|
|
|
return instance;
|
|
|
|
};
|
|
|
|
|
2020-07-09 14:27:19 +00:00
|
|
|
export const getContract = async <ContractType extends Contract>(
|
2020-05-29 14:55:31 +00:00
|
|
|
contractName: string,
|
|
|
|
address: string
|
2020-11-05 12:44:20 +00:00
|
|
|
): Promise<ContractType> => (await DRE.ethers.getContractAt(contractName, address)) as ContractType;
|
2020-05-29 14:55:31 +00:00
|
|
|
|
2020-11-05 12:44:20 +00:00
|
|
|
export const linkBytecode = (artifact: BuidlerArtifact | Artifact, libraries: any) => {
|
2020-06-08 12:03:40 +00:00
|
|
|
let bytecode = artifact.bytecode;
|
|
|
|
|
2020-07-13 08:54:08 +00:00
|
|
|
for (const [fileName, fileReferences] of Object.entries(artifact.linkReferences)) {
|
2020-06-08 12:03:40 +00:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2021-02-23 14:42:47 +00:00
|
|
|
export const getParamPerNetwork = <T>(param: iParamsPerNetwork<T>, network: eNetwork) => {
|
|
|
|
const {
|
|
|
|
main,
|
|
|
|
ropsten,
|
|
|
|
kovan,
|
|
|
|
coverage,
|
|
|
|
buidlerevm,
|
|
|
|
tenderlyMain,
|
|
|
|
} = param as iEthereumParamsPerNetwork<T>;
|
|
|
|
const { matic, mumbai } = param as iPolygonParamsPerNetwork<T>;
|
|
|
|
const { xdai } = param as iXDaiParamsPerNetwork<T>;
|
2021-05-10 13:34:31 +00:00
|
|
|
if (process.env.FORK) {
|
|
|
|
return param[process.env.FORK as eNetwork] as T;
|
2020-11-16 15:08:07 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 12:03:40 +00:00
|
|
|
switch (network) {
|
2020-09-14 13:57:11 +00:00
|
|
|
case eEthereumNetwork.coverage:
|
|
|
|
return coverage;
|
|
|
|
case eEthereumNetwork.buidlerevm:
|
|
|
|
return buidlerevm;
|
2020-11-05 14:31:35 +00:00
|
|
|
case eEthereumNetwork.hardhat:
|
|
|
|
return buidlerevm;
|
2020-06-08 12:03:40 +00:00
|
|
|
case eEthereumNetwork.kovan:
|
|
|
|
return kovan;
|
|
|
|
case eEthereumNetwork.ropsten:
|
|
|
|
return ropsten;
|
|
|
|
case eEthereumNetwork.main:
|
|
|
|
return main;
|
2020-11-12 13:12:26 +00:00
|
|
|
case eEthereumNetwork.tenderlyMain:
|
|
|
|
return tenderlyMain;
|
2021-02-23 14:42:47 +00:00
|
|
|
case ePolygonNetwork.matic:
|
|
|
|
return matic;
|
|
|
|
case ePolygonNetwork.mumbai:
|
|
|
|
return mumbai;
|
|
|
|
case eXDaiNetwork.xdai:
|
|
|
|
return xdai;
|
2020-06-08 12:03:40 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-02-24 21:49:23 +00:00
|
|
|
export const getParamPerPool = <T>({ proto, amm, matic }: iParamsPerPool<T>, pool: AavePools) => {
|
2020-06-08 12:03:40 +00:00
|
|
|
switch (pool) {
|
|
|
|
case AavePools.proto:
|
|
|
|
return proto;
|
2021-02-24 21:42:41 +00:00
|
|
|
case AavePools.amm:
|
|
|
|
return amm;
|
2021-02-24 21:49:23 +00:00
|
|
|
case AavePools.matic:
|
|
|
|
return matic;
|
2020-06-08 12:03:40 +00:00
|
|
|
default:
|
|
|
|
return proto;
|
|
|
|
}
|
|
|
|
};
|
2020-06-09 09:11:19 +00:00
|
|
|
|
2020-07-13 08:54:08 +00:00
|
|
|
export const convertToCurrencyDecimals = async (tokenAddress: tEthereumAddress, amount: string) => {
|
2020-08-13 11:06:23 +00:00
|
|
|
const token = await getIErc20Detailed(tokenAddress);
|
|
|
|
let decimals = (await token.decimals()).toString();
|
2020-06-09 09:11:19 +00:00
|
|
|
|
2020-06-12 20:12:53 +00:00
|
|
|
return ethers.utils.parseUnits(amount, decimals);
|
2020-06-09 09:11:19 +00:00
|
|
|
};
|
|
|
|
|
2020-07-13 08:54:08 +00:00
|
|
|
export const convertToCurrencyUnits = async (tokenAddress: string, amount: string) => {
|
2020-08-13 11:06:23 +00:00
|
|
|
const token = await getIErc20Detailed(tokenAddress);
|
|
|
|
let decimals = new BigNumber(await token.decimals());
|
2020-06-09 09:11:19 +00:00
|
|
|
const currencyUnit = new BigNumber(10).pow(decimals);
|
|
|
|
const amountInCurrencyUnits = new BigNumber(amount).div(currencyUnit);
|
|
|
|
return amountInCurrencyUnits.toFixed();
|
|
|
|
};
|
2020-09-14 13:57:11 +00:00
|
|
|
|
|
|
|
export const buildPermitParams = (
|
|
|
|
chainId: number,
|
|
|
|
token: tEthereumAddress,
|
|
|
|
revision: string,
|
|
|
|
tokenName: string,
|
|
|
|
owner: tEthereumAddress,
|
|
|
|
spender: tEthereumAddress,
|
|
|
|
nonce: number,
|
|
|
|
deadline: string,
|
|
|
|
value: tStringTokenSmallUnits
|
|
|
|
) => ({
|
|
|
|
types: {
|
|
|
|
EIP712Domain: [
|
2020-11-18 18:18:02 +00:00
|
|
|
{ name: 'name', type: 'string' },
|
|
|
|
{ name: 'version', type: 'string' },
|
|
|
|
{ name: 'chainId', type: 'uint256' },
|
|
|
|
{ name: 'verifyingContract', type: 'address' },
|
2020-09-14 13:57:11 +00:00
|
|
|
],
|
|
|
|
Permit: [
|
2020-11-18 18:18:02 +00:00
|
|
|
{ name: 'owner', type: 'address' },
|
|
|
|
{ name: 'spender', type: 'address' },
|
|
|
|
{ name: 'value', type: 'uint256' },
|
|
|
|
{ name: 'nonce', type: 'uint256' },
|
|
|
|
{ name: 'deadline', type: 'uint256' },
|
2020-09-14 13:57:11 +00:00
|
|
|
],
|
|
|
|
},
|
2020-09-15 14:02:21 +00:00
|
|
|
primaryType: 'Permit' as const,
|
2020-09-14 13:57:11 +00:00
|
|
|
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 => {
|
2020-09-15 14:02:21 +00:00
|
|
|
const signature = signTypedData_v4(Buffer.from(privateKey.substring(2, 66), 'hex'), {
|
|
|
|
data: typedData,
|
|
|
|
});
|
2020-09-14 13:57:11 +00:00
|
|
|
return fromRpcSig(signature);
|
2020-09-15 14:02:21 +00:00
|
|
|
};
|
2020-11-06 15:12:40 +00:00
|
|
|
|
|
|
|
export const buildLiquiditySwapParams = (
|
|
|
|
assetToSwapToList: tEthereumAddress[],
|
|
|
|
minAmountsToReceive: BigNumberish[],
|
|
|
|
swapAllBalances: BigNumberish[],
|
|
|
|
permitAmounts: BigNumberish[],
|
|
|
|
deadlines: BigNumberish[],
|
|
|
|
v: BigNumberish[],
|
|
|
|
r: (string | Buffer)[],
|
2021-01-14 10:53:48 +00:00
|
|
|
s: (string | Buffer)[],
|
|
|
|
useEthPath: boolean[]
|
2020-11-06 15:12:40 +00:00
|
|
|
) => {
|
|
|
|
return ethers.utils.defaultAbiCoder.encode(
|
|
|
|
[
|
|
|
|
'address[]',
|
|
|
|
'uint256[]',
|
|
|
|
'bool[]',
|
|
|
|
'uint256[]',
|
|
|
|
'uint256[]',
|
|
|
|
'uint8[]',
|
|
|
|
'bytes32[]',
|
|
|
|
'bytes32[]',
|
2021-01-14 10:53:48 +00:00
|
|
|
'bool[]',
|
2020-11-06 15:12:40 +00:00
|
|
|
],
|
2021-01-14 10:53:48 +00:00
|
|
|
[
|
|
|
|
assetToSwapToList,
|
|
|
|
minAmountsToReceive,
|
|
|
|
swapAllBalances,
|
|
|
|
permitAmounts,
|
|
|
|
deadlines,
|
|
|
|
v,
|
|
|
|
r,
|
|
|
|
s,
|
|
|
|
useEthPath,
|
|
|
|
]
|
2020-11-06 15:12:40 +00:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export const buildRepayAdapterParams = (
|
2020-11-20 18:53:50 +00:00
|
|
|
collateralAsset: tEthereumAddress,
|
|
|
|
collateralAmount: BigNumberish,
|
2020-11-09 18:27:18 +00:00
|
|
|
rateMode: BigNumberish,
|
|
|
|
permitAmount: BigNumberish,
|
|
|
|
deadline: BigNumberish,
|
|
|
|
v: BigNumberish,
|
|
|
|
r: string | Buffer,
|
2021-01-14 10:53:48 +00:00
|
|
|
s: string | Buffer,
|
|
|
|
useEthPath: boolean
|
2020-11-06 15:12:40 +00:00
|
|
|
) => {
|
|
|
|
return ethers.utils.defaultAbiCoder.encode(
|
2021-01-14 10:53:48 +00:00
|
|
|
['address', 'uint256', 'uint256', 'uint256', 'uint256', 'uint8', 'bytes32', 'bytes32', 'bool'],
|
|
|
|
[collateralAsset, collateralAmount, rateMode, permitAmount, deadline, v, r, s, useEthPath]
|
2020-11-06 15:12:40 +00:00
|
|
|
);
|
|
|
|
};
|
2021-01-15 15:48:54 +00:00
|
|
|
|
|
|
|
export const buildFlashLiquidationAdapterParams = (
|
|
|
|
collateralAsset: tEthereumAddress,
|
|
|
|
debtAsset: tEthereumAddress,
|
|
|
|
user: tEthereumAddress,
|
|
|
|
debtToCover: BigNumberish,
|
|
|
|
useEthPath: boolean
|
|
|
|
) => {
|
|
|
|
return ethers.utils.defaultAbiCoder.encode(
|
|
|
|
['address', 'address', 'address', 'uint256', 'bool'],
|
|
|
|
[collateralAsset, debtAsset, user, debtToCover, useEthPath]
|
|
|
|
);
|
|
|
|
};
|
2021-03-18 15:44:52 +00:00
|
|
|
|
|
|
|
export const buildParaSwapLiquiditySwapParams = (
|
|
|
|
assetToSwapTo: tEthereumAddress,
|
|
|
|
minAmountToReceive: BigNumberish,
|
|
|
|
swapAllBalanceOffset: BigNumberish,
|
|
|
|
swapCalldata: string | Buffer,
|
|
|
|
augustus: tEthereumAddress,
|
|
|
|
permitAmount: BigNumberish,
|
|
|
|
deadline: BigNumberish,
|
|
|
|
v: BigNumberish,
|
|
|
|
r: string | Buffer,
|
|
|
|
s: string | Buffer
|
|
|
|
) => {
|
|
|
|
return ethers.utils.defaultAbiCoder.encode(
|
|
|
|
[
|
|
|
|
'address',
|
|
|
|
'uint256',
|
|
|
|
'uint256',
|
|
|
|
'bytes',
|
|
|
|
'address',
|
|
|
|
'tuple(uint256,uint256,uint8,bytes32,bytes32)',
|
|
|
|
],
|
|
|
|
[
|
|
|
|
assetToSwapTo,
|
|
|
|
minAmountToReceive,
|
|
|
|
swapAllBalanceOffset,
|
|
|
|
swapCalldata,
|
|
|
|
augustus,
|
|
|
|
[permitAmount, deadline, v, r, s],
|
|
|
|
]
|
|
|
|
);
|
|
|
|
};
|
2021-06-15 15:52:42 +00:00
|
|
|
|
2021-03-11 15:55:04 +00:00
|
|
|
export const verifyContract = async (
|
|
|
|
id: string,
|
|
|
|
instance: Contract,
|
|
|
|
args: (string | string[])[]
|
|
|
|
) => {
|
|
|
|
if (usingPolygon()) {
|
|
|
|
await verifyAtPolygon(id, instance, args);
|
|
|
|
} else {
|
|
|
|
if (usingTenderly()) {
|
|
|
|
await verifyAtTenderly(id, instance);
|
|
|
|
}
|
|
|
|
await verifyEtherscanContract(instance.address, args);
|
|
|
|
}
|
|
|
|
return instance;
|
|
|
|
};
|