Skip to content

Instantly share code, notes, and snippets.

@mds1
Last active June 1, 2021 23:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mds1/091244017ce40f4debb3415a230e2a29 to your computer and use it in GitHub Desktop.
Save mds1/091244017ce40f4debb3415a230e2a29 to your computer and use it in GitHub Desktop.
const ethers = require('ethers');
const { BigNumber } = ethers;
(async function () {
const provider = ethers.providers.getDefaultProvider();
const multicallAbi = [ { constant: false, inputs: [ { components: [ { internalType: 'address', name: 'target', type: 'address' }, { internalType: 'bytes', name: 'callData', type: 'bytes' }, ], internalType: 'struct Multicall.Call[]', name: 'calls', type: 'tuple[]', }, ], name: 'aggregate', outputs: [ { internalType: 'uint256', name: 'blockNumber', type: 'uint256' }, { internalType: 'bytes[]', name: 'returnData', type: 'bytes[]' }, ], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: true, inputs: [{ internalType: 'uint256', name: 'blockNumber', type: 'uint256' }], name: 'getBlockHash', outputs: [{ internalType: 'bytes32', name: 'blockHash', type: 'bytes32' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [], name: 'getCurrentBlockCoinbase', outputs: [{ internalType: 'address', name: 'coinbase', type: 'address' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [], name: 'getCurrentBlockDifficulty', outputs: [{ internalType: 'uint256', name: 'difficulty', type: 'uint256' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [], name: 'getCurrentBlockGasLimit', outputs: [{ internalType: 'uint256', name: 'gaslimit', type: 'uint256' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [], name: 'getCurrentBlockTimestamp', outputs: [{ internalType: 'uint256', name: 'timestamp', type: 'uint256' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [{ internalType: 'address', name: 'addr', type: 'address' }], name: 'getEthBalance', outputs: [{ internalType: 'uint256', name: 'balance', type: 'uint256' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [], name: 'getLastBlockHash', outputs: [{ internalType: 'bytes32', name: 'blockHash', type: 'bytes32' }], payable: false, stateMutability: 'view', type: 'function', }, ]; // prettier-ignore
const cTokenAbi = [ { inputs: [], payable: false, stateMutability: 'nonpayable', type: 'constructor' }, { anonymous: false, inputs: [ { indexed: false, internalType: 'uint256', name: 'cashPrior', type: 'uint256' }, { indexed: false, internalType: 'uint256', name: 'interestAccumulated', type: 'uint256' }, { indexed: false, internalType: 'uint256', name: 'borrowIndex', type: 'uint256' }, { indexed: false, internalType: 'uint256', name: 'totalBorrows', type: 'uint256' }, ], name: 'AccrueInterest', type: 'event', }, { anonymous: false, inputs: [ { indexed: true, internalType: 'address', name: 'owner', type: 'address' }, { indexed: true, internalType: 'address', name: 'spender', type: 'address' }, { indexed: false, internalType: 'uint256', name: 'amount', type: 'uint256' }, ], name: 'Approval', type: 'event', }, { anonymous: false, inputs: [ { indexed: false, internalType: 'address', name: 'borrower', type: 'address' }, { indexed: false, internalType: 'uint256', name: 'borrowAmount', type: 'uint256' }, { indexed: false, internalType: 'uint256', name: 'accountBorrows', type: 'uint256' }, { indexed: false, internalType: 'uint256', name: 'totalBorrows', type: 'uint256' }, ], name: 'Borrow', type: 'event', }, { anonymous: false, inputs: [ { indexed: false, internalType: 'uint256', name: 'error', type: 'uint256' }, { indexed: false, internalType: 'uint256', name: 'info', type: 'uint256' }, { indexed: false, internalType: 'uint256', name: 'detail', type: 'uint256' }, ], name: 'Failure', type: 'event', }, { anonymous: false, inputs: [ { indexed: false, internalType: 'address', name: 'liquidator', type: 'address' }, { indexed: false, internalType: 'address', name: 'borrower', type: 'address' }, { indexed: false, internalType: 'uint256', name: 'repayAmount', type: 'uint256' }, { indexed: false, internalType: 'address', name: 'cTokenCollateral', type: 'address' }, { indexed: false, internalType: 'uint256', name: 'seizeTokens', type: 'uint256' }, ], name: 'LiquidateBorrow', type: 'event', }, { anonymous: false, inputs: [ { indexed: false, internalType: 'address', name: 'minter', type: 'address' }, { indexed: false, internalType: 'uint256', name: 'mintAmount', type: 'uint256' }, { indexed: false, internalType: 'uint256', name: 'mintTokens', type: 'uint256' }, ], name: 'Mint', type: 'event', }, { anonymous: false, inputs: [ { indexed: false, internalType: 'address', name: 'oldAdmin', type: 'address' }, { indexed: false, internalType: 'address', name: 'newAdmin', type: 'address' }, ], name: 'NewAdmin', type: 'event', }, { anonymous: false, inputs: [ { indexed: false, internalType: 'contract ComptrollerInterface', name: 'oldComptroller', type: 'address' }, { indexed: false, internalType: 'contract ComptrollerInterface', name: 'newComptroller', type: 'address' }, ], name: 'NewComptroller', type: 'event', }, { anonymous: false, inputs: [ { indexed: false, internalType: 'contract InterestRateModel', name: 'oldInterestRateModel', type: 'address' }, { indexed: false, internalType: 'contract InterestRateModel', name: 'newInterestRateModel', type: 'address' }, ], name: 'NewMarketInterestRateModel', type: 'event', }, { anonymous: false, inputs: [ { indexed: false, internalType: 'address', name: 'oldPendingAdmin', type: 'address' }, { indexed: false, internalType: 'address', name: 'newPendingAdmin', type: 'address' }, ], name: 'NewPendingAdmin', type: 'event', }, { anonymous: false, inputs: [ { indexed: false, internalType: 'uint256', name: 'oldReserveFactorMantissa', type: 'uint256' }, { indexed: false, internalType: 'uint256', name: 'newReserveFactorMantissa', type: 'uint256' }, ], name: 'NewReserveFactor', type: 'event', }, { anonymous: false, inputs: [ { indexed: false, internalType: 'address', name: 'redeemer', type: 'address' }, { indexed: false, internalType: 'uint256', name: 'redeemAmount', type: 'uint256' }, { indexed: false, internalType: 'uint256', name: 'redeemTokens', type: 'uint256' }, ], name: 'Redeem', type: 'event', }, { anonymous: false, inputs: [ { indexed: false, internalType: 'address', name: 'payer', type: 'address' }, { indexed: false, internalType: 'address', name: 'borrower', type: 'address' }, { indexed: false, internalType: 'uint256', name: 'repayAmount', type: 'uint256' }, { indexed: false, internalType: 'uint256', name: 'accountBorrows', type: 'uint256' }, { indexed: false, internalType: 'uint256', name: 'totalBorrows', type: 'uint256' }, ], name: 'RepayBorrow', type: 'event', }, { anonymous: false, inputs: [ { indexed: false, internalType: 'address', name: 'benefactor', type: 'address' }, { indexed: false, internalType: 'uint256', name: 'addAmount', type: 'uint256' }, { indexed: false, internalType: 'uint256', name: 'newTotalReserves', type: 'uint256' }, ], name: 'ReservesAdded', type: 'event', }, { anonymous: false, inputs: [ { indexed: false, internalType: 'address', name: 'admin', type: 'address' }, { indexed: false, internalType: 'uint256', name: 'reduceAmount', type: 'uint256' }, { indexed: false, internalType: 'uint256', name: 'newTotalReserves', type: 'uint256' }, ], name: 'ReservesReduced', type: 'event', }, { anonymous: false, inputs: [ { indexed: true, internalType: 'address', name: 'from', type: 'address' }, { indexed: true, internalType: 'address', name: 'to', type: 'address' }, { indexed: false, internalType: 'uint256', name: 'amount', type: 'uint256' }, ], name: 'Transfer', type: 'event', }, { constant: false, inputs: [], name: '_acceptAdmin', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: false, inputs: [{ internalType: 'uint256', name: 'addAmount', type: 'uint256' }], name: '_addReserves', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: false, inputs: [{ internalType: 'bytes', name: 'data', type: 'bytes' }], name: '_becomeImplementation', outputs: [], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: false, inputs: [{ internalType: 'uint256', name: 'reduceAmount', type: 'uint256' }], name: '_reduceReserves', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: false, inputs: [], name: '_resignImplementation', outputs: [], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: false, inputs: [{ internalType: 'contract ComptrollerInterface', name: 'newComptroller', type: 'address' }], name: '_setComptroller', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: false, inputs: [{ internalType: 'contract InterestRateModel', name: 'newInterestRateModel', type: 'address' }], name: '_setInterestRateModel', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: false, inputs: [{ internalType: 'address payable', name: 'newPendingAdmin', type: 'address' }], name: '_setPendingAdmin', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: false, inputs: [{ internalType: 'uint256', name: 'newReserveFactorMantissa', type: 'uint256' }], name: '_setReserveFactor', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: true, inputs: [], name: 'accrualBlockNumber', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: false, inputs: [], name: 'accrueInterest', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: true, inputs: [], name: 'admin', outputs: [{ internalType: 'address payable', name: '', type: 'address' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [ { internalType: 'address', name: 'owner', type: 'address' }, { internalType: 'address', name: 'spender', type: 'address' }, ], name: 'allowance', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: false, inputs: [ { internalType: 'address', name: 'spender', type: 'address' }, { internalType: 'uint256', name: 'amount', type: 'uint256' }, ], name: 'approve', outputs: [{ internalType: 'bool', name: '', type: 'bool' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: true, inputs: [{ internalType: 'address', name: 'owner', type: 'address' }], name: 'balanceOf', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: false, inputs: [{ internalType: 'address', name: 'owner', type: 'address' }], name: 'balanceOfUnderlying', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: false, inputs: [{ internalType: 'uint256', name: 'borrowAmount', type: 'uint256' }], name: 'borrow', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: false, inputs: [{ internalType: 'address', name: 'account', type: 'address' }], name: 'borrowBalanceCurrent', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: true, inputs: [{ internalType: 'address', name: 'account', type: 'address' }], name: 'borrowBalanceStored', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [], name: 'borrowIndex', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [], name: 'borrowRatePerBlock', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [], name: 'comptroller', outputs: [{ internalType: 'contract ComptrollerInterface', name: '', type: 'address' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [], name: 'decimals', outputs: [{ internalType: 'uint8', name: '', type: 'uint8' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: false, inputs: [], name: 'exchangeRateCurrent', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: true, inputs: [], name: 'exchangeRateStored', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [{ internalType: 'address', name: 'account', type: 'address' }], name: 'getAccountSnapshot', outputs: [ { internalType: 'uint256', name: '', type: 'uint256' }, { internalType: 'uint256', name: '', type: 'uint256' }, { internalType: 'uint256', name: '', type: 'uint256' }, { internalType: 'uint256', name: '', type: 'uint256' }, ], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [], name: 'getCash', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [], name: 'implementation', outputs: [{ internalType: 'address', name: '', type: 'address' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: false, inputs: [ { internalType: 'address', name: 'underlying_', type: 'address' }, { internalType: 'contract ComptrollerInterface', name: 'comptroller_', type: 'address' }, { internalType: 'contract InterestRateModel', name: 'interestRateModel_', type: 'address' }, { internalType: 'uint256', name: 'initialExchangeRateMantissa_', type: 'uint256' }, { internalType: 'string', name: 'name_', type: 'string' }, { internalType: 'string', name: 'symbol_', type: 'string' }, { internalType: 'uint8', name: 'decimals_', type: 'uint8' }, ], name: 'initialize', outputs: [], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: false, inputs: [ { internalType: 'contract ComptrollerInterface', name: 'comptroller_', type: 'address' }, { internalType: 'contract InterestRateModel', name: 'interestRateModel_', type: 'address' }, { internalType: 'uint256', name: 'initialExchangeRateMantissa_', type: 'uint256' }, { internalType: 'string', name: 'name_', type: 'string' }, { internalType: 'string', name: 'symbol_', type: 'string' }, { internalType: 'uint8', name: 'decimals_', type: 'uint8' }, ], name: 'initialize', outputs: [], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: true, inputs: [], name: 'interestRateModel', outputs: [{ internalType: 'contract InterestRateModel', name: '', type: 'address' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [], name: 'isCToken', outputs: [{ internalType: 'bool', name: '', type: 'bool' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: false, inputs: [ { internalType: 'address', name: 'borrower', type: 'address' }, { internalType: 'uint256', name: 'repayAmount', type: 'uint256' }, { internalType: 'contract CTokenInterface', name: 'cTokenCollateral', type: 'address' }, ], name: 'liquidateBorrow', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: false, inputs: [{ internalType: 'uint256', name: 'mintAmount', type: 'uint256' }], name: 'mint', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: true, inputs: [], name: 'name', outputs: [{ internalType: 'string', name: '', type: 'string' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [], name: 'pendingAdmin', outputs: [{ internalType: 'address payable', name: '', type: 'address' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: false, inputs: [{ internalType: 'uint256', name: 'redeemTokens', type: 'uint256' }], name: 'redeem', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: false, inputs: [{ internalType: 'uint256', name: 'redeemAmount', type: 'uint256' }], name: 'redeemUnderlying', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: false, inputs: [{ internalType: 'uint256', name: 'repayAmount', type: 'uint256' }], name: 'repayBorrow', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: false, inputs: [ { internalType: 'address', name: 'borrower', type: 'address' }, { internalType: 'uint256', name: 'repayAmount', type: 'uint256' }, ], name: 'repayBorrowBehalf', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: true, inputs: [], name: 'reserveFactorMantissa', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: false, inputs: [ { internalType: 'address', name: 'liquidator', type: 'address' }, { internalType: 'address', name: 'borrower', type: 'address' }, { internalType: 'uint256', name: 'seizeTokens', type: 'uint256' }, ], name: 'seize', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: true, inputs: [], name: 'supplyRatePerBlock', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [], name: 'symbol', outputs: [{ internalType: 'string', name: '', type: 'string' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [], name: 'totalBorrows', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: false, inputs: [], name: 'totalBorrowsCurrent', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: true, inputs: [], name: 'totalReserves', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [], name: 'totalSupply', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], payable: false, stateMutability: 'view', type: 'function', }, { constant: false, inputs: [ { internalType: 'address', name: 'dst', type: 'address' }, { internalType: 'uint256', name: 'amount', type: 'uint256' }, ], name: 'transfer', outputs: [{ internalType: 'bool', name: '', type: 'bool' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: false, inputs: [ { internalType: 'address', name: 'src', type: 'address' }, { internalType: 'address', name: 'dst', type: 'address' }, { internalType: 'uint256', name: 'amount', type: 'uint256' }, ], name: 'transferFrom', outputs: [{ internalType: 'bool', name: '', type: 'bool' }], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: true, inputs: [], name: 'underlying', outputs: [{ internalType: 'address', name: '', type: 'address' }], payable: false, stateMutability: 'view', type: 'function', }, ]; // prettier-ignore
const multicall = new ethers.Contract('0xeefBa1e63905eF1D7ACbA5a8513c70307C1cE441', multicallAbi, provider);
const cDai = new ethers.Contract('0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643', cTokenAbi, provider); // cDAI
const calls = [
{ target: cDai.address, callData: cDai.interface.encodeFunctionData('exchangeRateStored') },
{ target: cDai.address, callData: cDai.interface.encodeFunctionData('totalSupply') },
{ target: cDai.address, callData: cDai.interface.encodeFunctionData('totalReserves') },
{ target: cDai.address, callData: cDai.interface.encodeFunctionData('getCash') },
{ target: cDai.address, callData: cDai.interface.encodeFunctionData('totalBorrows') },
];
const response = await multicall.callStatic.aggregate(calls);
const { blockNumber, returnData } = response;
console.log('blockNumber: ', blockNumber.toString());
const [exchangeRateStored, totalSupply, totalReserves, cash, totalBorrows] = returnData.map(BigNumber.from);
// Check reserves + supply = cash + borrows
const supply = totalSupply.mul(exchangeRateStored).div(10n ** 18n);
const lhs = supply.add(totalReserves); // left-hand side of above equation
const rhs = cash.add(totalBorrows); // right-hand side of above equation
console.log('lhs: ', lhs.toString());
console.log('rhs: ', rhs.toString());
console.log(lhs.toString() === rhs.toString());
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment