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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more