Published
Edited
Aug 13, 2020
Importers
1 star
Insert cell
Insert cell
Insert cell
async function CompoundLib({abi, address, uint, array, Enum, Struct}, env, vars) {
const {
Comptroller = 'Comptroller',
defaultFrom = address('$0')
} = vars || {};
const EVM = await env.library(EVMLib)
const ERC20 = await env.library(ERC20Lib)

const LiquidityStat = new Struct('LiquidityStat', {error: uint, liquidity: uint, shortfall: uint})
const $enterMarkets = (markets) => ({send: 'enterMarkets', to: Comptroller, args: [array(address)(markets.map(address))]})
const $exitMarket = (market) => ({send: 'exitMarket', to: Comptroller, args: [address(market)]})

const $getAccountLiquidity = (account) => ({call: 'getAccountLiquidity', on: Comptroller, args: [address(account)], returns: LiquidityStat})

const $underlying = (cToken) => ({call: 'underlying', on: cToken, args: [], returns: maybe('address')})

const $mint = (cToken, amount) => ({send: 'mint', to: cToken, args: [amount]})
const $redeem = (cToken, tokens) => ({send: 'redeem', to: cToken, args: [tokens]})
const $redeemUnderlying = (cToken, amount) => ({send: 'redeemUnderlying', to: cToken, args: [amount]})
const $borrow = (cToken, amount) => ({send: 'borrow', to: cToken, args: [amount]})
const $repayBorrow = (cToken, amount) => ({send: 'repayBorrow', to: cToken, args: [amount]})
const $repayBorrowBehalf = (cToken, borrower, repayAmount) => (
{send: 'repayBrorowBehalf', to: cToken, args: [borrower, repayAmount]}
)
const $liquidateBorrow = (cTokenBorrowed, borrower, repayAmount, cTokenCollateral) => (
{send: 'liquidateBorrow', to: cTokenBorrowed, args: [borrower, repayAmount, address(cTokenCollateral)]}
)

const $mintEth = (cToken, amount) => ({send: 'mint', to: cToken, value: amount})
const $repayBorrowEth = (cToken, amount) => ({send: 'repayBorrow', to: cToken, value: amount})
const $repayBorrowBehalfEth = (cToken, borrower, repayAmount) => (
{send: 'repayBorrowBehalf', to: cToken, args: [borrower], value: repayAmount}
)
const $liquidateBorrowEth = (cTokenBorrowed, borrower, repayAmount, cTokenCollateral) => (
{send: 'liquidateBorrow', to: cTokenBorrowed, args: [borrower, address(cTokenCollateral)], value: repayAmount}
)

const $$basicMint = (cToken, mintAmount, from = defaultFrom) => [
async (world) => {
const underlying = await world.exec($underlying(cToken))
if (underlying)
return world.exec([
{...ERC20.$approve(underlying, cToken, mintAmount), from},
{...$mint(cToken, mintAmount), from, emits: 'Mint'}
])
return world.exec({...$mintEth(cToken, mintAmount), from, emits: 'Mint'})
},
]

const $$basicRedeem = (cToken, mintAmount, redeemAmount, from = defaultFrom) => [
$$basicMint(cToken, mintAmount, from),
{...$redeemUnderlying(cToken, redeemAmount), from, emits: 'Redeem'}
]
const $$basicBorrow = (cTokenCollateral, mintAmount, cTokenBorrowed, borrowAmount, from = defaultFrom) => [
$$basicMint(cTokenCollateral, mintAmount, from),
{...$enterMarkets([cTokenCollateral]), from},
{...$getAccountLiquidity(from), expect: r => r.liquidity >= borrowAmount},
{...$borrow(cTokenBorrowed, borrowAmount), from, emits: 'Borrow'}
]

const $$basicRepay = (cTokenCollateral, mintAmount, cTokenBorrowed, borrowAmount, repayAmount, from = defaultFrom) => [
$$basicBorrow(cTokenCollateral, mintAmount, cTokenBorrowed, borrowAmount, from),
async (world) => {
const underlying = await world.exec($underlying(cTokenBorrowed))
if (underlying)
return world.exec([
{...ERC20.$approve(underlying, cTokenBorrowed, repayAmount), from},
{...$repayBorrow(cTokenBorrowed, repayAmount), from, emits: 'RepayBorrow'}
])
return world.exec({...$repayBorrowEth(cTokenBorrowed, repayAmount), from, emits: 'RepayBorrow'})
}
]
const $$basicRepayBehalf = (borrower, cTokenCollateral, mintAmount, cTokenBorrowed, borrowAmount, repayAmount, from = defaultFrom) => [
$$basicBorrow(cTokenCollateral, mintAmount, cTokenBorrowed, borrowAmount, from),
async (world) => {
const underlying = await world.exec($underlying(cTokenBorrowed))
if (underlying)
return world.exec([
{...ERC20.$approve(underlying, cTokenBorrowed, repayAmount), from},
{...$repayBorrowBehalf(cTokenBorrowed, borrower, repayAmount), from, emits: 'RepayBorrow'}
])
return world.exec(
{...$repayBorrowBehalfEth(cTokenBorrowed, borrower, repayAmount), from, emits: 'RepayBorrow'}
)
}
]
const $$basicLiquidate = (borrower, cTokenCollateral, mintAmount, cTokenBorrowed, borrowAmount, repayAmount, from = defaultFrom, interestBlocks = 10e6) => [
$$basicBorrow(cTokenCollateral, mintAmount, cTokenBorrowed, borrowAmount, borrower),
EVM.$$advanceBlocks(interestBlocks),
async (world) => {
const underlying = await world.exec($underlying(cTokenBorrowed))
if (underlying)
return world.exec([
{...ERC20.$approve(underlying, cTokenBorrowed, repayAmount), from},
{...$liquidateBorrow(cTokenBorrowed, borrower, repayAmount, cTokenCollateral), from, emits: 'LiquidateBorrow'}
])
return world.exec(
{...$liquidateBorrowEth(cTokenBorrowed, borrower, repayAmount, cTokenCollateral), from, emits: 'LiquidateBorrow'}
)
}
]
return {
...ERC20,

$enterMarkets,
$exitMarket,

$getAccountLiquidity,
$underlying,
$mint,
$redeem,
$redeemUnderlying,
$borrow,
$repayBorrow,
$repayBorrowBehalf,
$liquidateBorrow,
$mintEth,
$repayBorrowEth,
$repayBorrowBehalfEth,
$liquidateBorrowEth,
$$basicMint,
$$basicRedeem,
$$basicBorrow,
$$basicRepay,
$$basicRepayBehalf,
$$basicLiquidate
}
}
Insert cell
async function CompGovLib({abi, address, array, bool, bytes, string, uint, Enum, Struct}, env, vars) {
const {
Comp = 'Comp',
Gov = 'GovernorAlpha',
Timelock = 'Timelock',
timelockDelay = 604910,
votingPeriod = 20000
} = vars || {};

const Proposal = new Struct('Proposal', {
id: 'uint',
proposer: 'address',
eta: 'uint',
targets: 'address[]',
values: 'uint[]',
signatures: 'string[]',
calldatas: 'bytes[]',
startBlock: 'uint',
endBlock: 'uint',
forVotes: 'uint',
againstVotes: 'uint',
canceled: 'bool',
executed: 'bool'
})

const Receipt = new Struct('Receipt', {
hasVoted: 'bool',
support: 'bool',
votes: 'uint96'
})

const ProposalState = new Enum('ProposalState', [
'Pending',
'Active',
'Canceled',
'Defeated',
'Succeeded',
'Queued',
'Expired',
'Executed'
])

const EVM = await env.library(EVMLib)
const $lastProposal = () => ({call: 'proposalCount', on: Gov, returns: uint})
const $state = (proposalId) => ({call: 'state', on: Gov, args: [proposalId], returns: ProposalState})

const $castVote = (proposalId, support) => ({send: 'castVote', to: Gov, args: [proposalId, support]})
const $delegate = (delegatee) => ({send: 'delegate', to: Comp, args: [delegatee]})

const $propose = (description, actionOrActions) => {
// note that the full signatures are expected, although the types could be implied
// this adds a small bit of safety and clarity since the contracts expect this, though redundant
const actions = Array.isArray(actionOrActions) ? actionOrActions : [actionOrActions];
const targets = [], values = [], signatures = [], calldatas = [];
for (let action of actions) {
const {args = [], value = 0, ...rest} = action;
const [target, signature] = Object.entries(rest)[0]
targets.push(address(target))
values.push(uint(value))
signatures.push(string(signature))
calldatas.push(bytes(abi.encode(args)))
}
return {send: 'propose', to: Gov, args: [
array(address)(targets),
array(uint)(values),
array(string)(signatures),
array(bytes)(calldatas),
string(description)
]}
}
const $queue = (proposalId) => ({send: 'queue', to: Gov, args: [proposalId]})
const $execute = (proposalId) => ({send: 'execute', to: Gov, args: [proposalId]})

const $$waitAndQueue = (proposalId) => [
EVM.$$advanceBlocks(votingPeriod),
{...$state(proposalId), expect: ProposalState.Succeeded},
$queue(proposalId)
]
const $$waitAndExecute = (proposalId) => [
EVM.$increaseTime(timelockDelay),
{...$state(proposalId), expect: ProposalState.Queued},
$execute(proposalId)
]

const $$whipProposal = (voter, proposalId) => [
EVM.$mineBlock(),
{...$castVote(proposalId, true), from: voter},
$$waitAndQueue(proposalId),
$$waitAndExecute(proposalId)
]
async function whipLastProposal(voter, world) {
return world.exec($$whipProposal(voter, await world.exec($lastProposal())))
}
return {
Proposal,
ProposalState,
Receipt,

$lastProposal,
$state,
$castVote,
$delegate,
$propose,
$queue,
$execute,

$$waitAndQueue,
$$waitAndExecute,
$$whipProposal,

whipLastProposal
}
}
Insert cell
Insert cell
async function _runTests() {
async function setupWorld(aliases, network = 'mainnet') {
const world = new World(await GanacheEnv())
await world.env.read(github(`networks/${network}.json`, 'compound-finance/compound-protocol', 'uniswap-oracle'))
await world.fork(network, {unlocked_accounts: Object.values(aliases), useProxy: true})
await world.env.assign(aliases)
return world;
}
const world = await setupWorld({
DAIWhale: '0xF977814e90dA44bFA03b6295A0616a897441aceC',
ETHWhale: '0xF977814e90dA44bFA03b6295A0616a897441aceC',
USDTWhale: '0x7b8c69a0f660cd43ef67948976daae77bc6a019b',
USDCWhale: '0xa700a7e660669ff27025b2db95af2e61bce910e6'
})
const CompGov = await world.env.library(CompGovLib, {Timelock: 'YamTimelock'})
const Compound = await world.env.library(CompoundLib)
const {address} = world.env.bindings()
await world.exec([
Compound.$$basicMint('cDAI', exp(1), address('DAIWhale')),
Compound.$$basicMint('cETH', exp(1)),
Compound.$$basicLiquidate(address('ETHWhale'), 'cETH', exp(1), 'cUSDT', exp(250, 6), exp(100, 6), address('USDTWhale')), // depends on ETH price
Compound.$$basicLiquidate(address('ETHWhale'), 'cETH', exp(1), 'cUSDC', exp(250, 6), exp(100, 6), address('USDCWhale')) // depends on ETH price
])
return world;
}
Insert cell
//_runTests() // uncomment me during development
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more