diff --git a/contracts/Comptroller.sol b/contracts/Comptroller.sol index 565a21d5d..81e2d27b0 100644 --- a/contracts/Comptroller.sol +++ b/contracts/Comptroller.sol @@ -168,7 +168,7 @@ contract Comptroller is ComptrollerV3Storage, ComptrollerInterface, ComptrollerE /** * @notice Removes asset from sender's account liquidity calculation * @dev Sender must not have an outstanding borrow balance in the asset, - * or be providing neccessary collateral for an outstanding borrow. + * or be providing necessary collateral for an outstanding borrow. * @param cTokenAddress The address of the asset to be removed * @return Whether or not the account successfully exited the market */ @@ -1081,26 +1081,9 @@ contract Comptroller is ComptrollerV3Storage, ComptrollerInterface, ComptrollerE return state; } - function _become(Unitroller unitroller, uint compRate_, address[] memory compMarketsToAdd, address[] memory otherMarketsToAdd) public { + function _become(Unitroller unitroller) public { require(msg.sender == unitroller.admin(), "only unitroller admin can change brains"); require(unitroller._acceptImplementation() == 0, "change not authorized"); - - Comptroller(address(unitroller))._becomeG3(compRate_, compMarketsToAdd, otherMarketsToAdd); - } - - function _becomeG3(uint compRate_, address[] memory compMarketsToAdd, address[] memory otherMarketsToAdd) public { - require(msg.sender == comptrollerImplementation, "only brains can become itself"); - - for (uint i = 0; i < compMarketsToAdd.length; i++) { - _addMarketInternal(address(compMarketsToAdd[i])); - } - - for (uint i = 0; i < otherMarketsToAdd.length; i++) { - _addMarketInternal(address(otherMarketsToAdd[i])); - } - - _setCompRate(compRate_); - _addCompMarkets(compMarketsToAdd); } /** diff --git a/scenario/src/Builder/ComptrollerImplBuilder.ts b/scenario/src/Builder/ComptrollerImplBuilder.ts index 61f4b0c65..244ade70b 100644 --- a/scenario/src/Builder/ComptrollerImplBuilder.ts +++ b/scenario/src/Builder/ComptrollerImplBuilder.ts @@ -14,6 +14,9 @@ const ComptrollerScenarioG1Contract = getTestContract('ComptrollerScenarioG1'); const ComptrollerG2Contract = getContract('ComptrollerG2'); const ComptrollerScenarioG2Contract = getContract('ComptrollerScenarioG2'); +const ComptrollerG3Contract = getContract('ComptrollerG3'); +const ComptrollerScenarioG3Contract = getContract('ComptrollerScenarioG3'); + const ComptrollerScenarioContract = getTestContract('ComptrollerScenario'); const ComptrollerContract = getContract('Comptroller'); @@ -48,6 +51,7 @@ export async function buildComptrollerImpl( description: 'ScenarioG1 Comptroller Impl' }) ), + new Fetcher<{ name: StringV }, ComptrollerImplData>( ` #### ScenarioG2 @@ -65,6 +69,23 @@ export async function buildComptrollerImpl( }) ), + new Fetcher<{ name: StringV }, ComptrollerImplData>( + ` + #### ScenarioG3 + + * "ScenarioG3 name:" - The Comptroller Scenario for local testing (G3) + * E.g. "ComptrollerImpl Deploy ScenarioG3 MyScen" + `, + 'ScenarioG3', + [new Arg('name', getStringV)], + async (world, { name }) => ({ + invokation: await ComptrollerScenarioG3Contract.deploy(world, from, []), + name: name.val, + contract: 'ComptrollerScenarioG3Contract', + description: 'ScenarioG3 Comptroller Impl' + }) + ), + new Fetcher<{ name: StringV }, ComptrollerImplData>( ` #### Scenario @@ -81,6 +102,26 @@ export async function buildComptrollerImpl( description: 'Scenario Comptroller Impl' }) ), + + new Fetcher<{ name: StringV }, ComptrollerImplData>( + ` + #### StandardG1 + + * "StandardG1 name:" - The standard generation 1 Comptroller contract + * E.g. "Comptroller Deploy StandardG1 MyStandard" + `, + 'StandardG1', + [new Arg('name', getStringV)], + async (world, { name }) => { + return { + invokation: await ComptrollerG1Contract.deploy(world, from, []), + name: name.val, + contract: 'ComptrollerG1', + description: 'StandardG1 Comptroller Impl' + }; + } + ), + new Fetcher<{ name: StringV }, ComptrollerImplData>( ` #### StandardG2 @@ -99,24 +140,26 @@ export async function buildComptrollerImpl( }; } ), - new Fetcher<{ name: StringV }, ComptrollerImplData>( + + new Fetcher<{ name: StringV }, ComptrollerImplData>( ` - #### StandardG1 + #### StandardG3 - * "StandardG1 name:" - The standard generation 1 Comptroller contract - * E.g. "Comptroller Deploy StandardG1 MyStandard" + * "StandardG3 name:" - The standard generation 2 Comptroller contract + * E.g. "Comptroller Deploy StandardG3 MyStandard" `, - 'StandardG1', + 'StandardG3', [new Arg('name', getStringV)], async (world, { name }) => { return { - invokation: await ComptrollerG1Contract.deploy(world, from, []), + invokation: await ComptrollerG3Contract.deploy(world, from, []), name: name.val, - contract: 'ComptrollerG1', - description: 'StandardG1 Comptroller Impl' + contract: 'ComptrollerG3', + description: 'StandardG3 Comptroller Impl' }; } ), + new Fetcher<{ name: StringV }, ComptrollerImplData>( ` #### Standard @@ -135,6 +178,7 @@ export async function buildComptrollerImpl( }; } ), + new Fetcher<{ name: StringV }, ComptrollerImplData>( ` #### Borked diff --git a/scenario/src/ContractLookup.ts b/scenario/src/ContractLookup.ts index b7065588f..8bb377c4d 100644 --- a/scenario/src/ContractLookup.ts +++ b/scenario/src/ContractLookup.ts @@ -106,7 +106,7 @@ export function getErc20Address(world: World, erc20Arg: string): string { } export function getGovernorAddress(world: World, governorArg: string): string { - return getContractDataString(world, [['Governor', governorArg, 'address']]); + return getContractDataString(world, [['Contracts', governorArg]]); } export async function getPriceOracleProxy(world: World): Promise { diff --git a/scenario/src/Event/ComptrollerImplEvent.ts b/scenario/src/Event/ComptrollerImplEvent.ts index 2dd04b457..49e2daf5e 100644 --- a/scenario/src/Event/ComptrollerImplEvent.ts +++ b/scenario/src/Event/ComptrollerImplEvent.ts @@ -31,43 +31,72 @@ async function genComptrollerImpl(world: World, from: string, params: Event): Pr return world; } -async function become( +async function mergeABI( + world: World, + from: string, + comptrollerImpl: ComptrollerImpl, + unitroller: Unitroller +): Promise { + if (!world.dryRun) { + // Skip this specifically on dry runs since it's likely to crash due to a number of reasons + world = await mergeContractABI(world, 'Comptroller', unitroller, unitroller.name, comptrollerImpl.name); + } + + return world; +} + +async function becomeG1( world: World, from: string, comptrollerImpl: ComptrollerImpl, unitroller: Unitroller, - compRate: encodedNumber, - compMarkets: string[], - otherMarkets: string[] + priceOracleAddr: string, + closeFactor: encodedNumber, + maxAssets: encodedNumber ): Promise { let invokation = await invoke( world, - comptrollerImpl.methods._become(unitroller._address, compRate, compMarkets, otherMarkets), + comptrollerImpl.methods._become(unitroller._address, priceOracleAddr, closeFactor, maxAssets, false), from, ComptrollerErrorReporter ); - if (!world.dryRun) { // Skip this specifically on dry runs since it's likely to crash due to a number of reasons world = await mergeContractABI(world, 'Comptroller', unitroller, unitroller.name, comptrollerImpl.name); } - world = addAction(world, `Become ${unitroller._address}'s Comptroller Impl`, invokation); + world = addAction( + world, + `Become ${unitroller._address}'s Comptroller Impl with priceOracle=${priceOracleAddr},closeFactor=${closeFactor},maxAssets=${maxAssets}`, + invokation + ); return world; } - -async function mergeABI( +// Recome calls `become` on the G1 Comptroller, but passes a flag to not modify any of the initialization variables. +async function recome( world: World, from: string, comptrollerImpl: ComptrollerImpl, unitroller: Unitroller ): Promise { - if (!world.dryRun) { - // Skip this specifically on dry runs since it's likely to crash due to a number of reasons - world = await mergeContractABI(world, 'Comptroller', unitroller, unitroller.name, comptrollerImpl.name); - } + let invokation = await invoke( + world, + comptrollerImpl.methods._become( + unitroller._address, + '0x0000000000000000000000000000000000000000', + 0, + 0, + true + ), + from, + ComptrollerErrorReporter + ); + + world = await mergeContractABI(world, 'Comptroller', unitroller, unitroller.name, comptrollerImpl.name); + + world = addAction(world, `Recome ${unitroller._address}'s Comptroller Impl`, invokation); return world; } @@ -95,36 +124,33 @@ async function becomeG2( return world; } -async function becomeG1( +async function becomeG3( world: World, from: string, comptrollerImpl: ComptrollerImpl, unitroller: Unitroller, - priceOracleAddr: string, - closeFactor: encodedNumber, - maxAssets: encodedNumber + compRate: encodedNumber, + compMarkets: string[], + otherMarkets: string[] ): Promise { let invokation = await invoke( world, - comptrollerImpl.methods._become(unitroller._address, priceOracleAddr, closeFactor, maxAssets, false), + comptrollerImpl.methods._become(unitroller._address, compRate, compMarkets, otherMarkets), from, ComptrollerErrorReporter ); + if (!world.dryRun) { // Skip this specifically on dry runs since it's likely to crash due to a number of reasons world = await mergeContractABI(world, 'Comptroller', unitroller, unitroller.name, comptrollerImpl.name); } - world = addAction( - world, - `Become ${unitroller._address}'s Comptroller Impl with priceOracle=${priceOracleAddr},closeFactor=${closeFactor},maxAssets=${maxAssets}`, - invokation - ); + world = addAction(world, `Become ${unitroller._address}'s Comptroller Impl`, invokation); return world; } -// Recome calls `become` on the G1 Comptroller, but passes a flag to not modify any of the initialization variables. -async function recome( + +async function become( world: World, from: string, comptrollerImpl: ComptrollerImpl, @@ -132,20 +158,17 @@ async function recome( ): Promise { let invokation = await invoke( world, - comptrollerImpl.methods._become( - unitroller._address, - '0x0000000000000000000000000000000000000000', - 0, - 0, - true - ), + comptrollerImpl.methods._become(unitroller._address), from, ComptrollerErrorReporter ); - world = await mergeContractABI(world, 'Comptroller', unitroller, unitroller.name, comptrollerImpl.name); + if (!world.dryRun) { + // Skip this specifically on dry runs since it's likely to crash due to a number of reasons + world = await mergeContractABI(world, 'Comptroller', unitroller, unitroller.name, comptrollerImpl.name); + } - world = addAction(world, `Recome ${unitroller._address}'s Comptroller Impl`, invokation); + world = addAction(world, `Become ${unitroller._address}'s Comptroller Impl`, invokation); return world; } @@ -228,6 +251,7 @@ export function comptrollerImplCommands() { ), { namePos: 1 } ), + new Command<{ unitroller: Unitroller; comptrollerImpl: ComptrollerImpl; @@ -255,12 +279,12 @@ export function comptrollerImplCommands() { otherMarkets: ArrayV; }>( ` - #### Become + #### BecomeG3 - * "ComptrollerImpl Become " - Become the comptroller, if possible. - * E.g. "ComptrollerImpl MyImpl Become 0.1e18 [cDAI, cETH, cUSDC] + * "ComptrollerImpl BecomeG3 " - Become the comptroller, if possible. + * E.g. "ComptrollerImpl MyImpl BecomeG3 0.1e18 [cDAI, cETH, cUSDC] `, - 'Become', + 'BecomeG3', [ new Arg('unitroller', getUnitroller, { implicit: true }), new Arg('comptrollerImpl', getComptrollerImpl), @@ -269,7 +293,28 @@ export function comptrollerImplCommands() { new Arg('otherMarkets', getArrayV(getAddressV), { default: new ArrayV([]) }) ], (world, from, { unitroller, comptrollerImpl, compRate, compMarkets, otherMarkets }) => { - return become(world, from, comptrollerImpl, unitroller, compRate.encode(), compMarkets.val.map(a => a.val), otherMarkets.val.map(a => a.val)) + return becomeG3(world, from, comptrollerImpl, unitroller, compRate.encode(), compMarkets.val.map(a => a.val), otherMarkets.val.map(a => a.val)) + }, + { namePos: 1 } + ), + + new Command<{ + unitroller: Unitroller; + comptrollerImpl: ComptrollerImpl; + }>( + ` + #### Become + + * "ComptrollerImpl Become " - Become the comptroller, if possible. + * E.g. "ComptrollerImpl MyImpl Become 0.1e18 [cDAI, cETH, cUSDC] + `, + 'Become', + [ + new Arg('unitroller', getUnitroller, { implicit: true }), + new Arg('comptrollerImpl', getComptrollerImpl) + ], + (world, from, { unitroller, comptrollerImpl }) => { + return become(world, from, comptrollerImpl, unitroller) }, { namePos: 1 } ), diff --git a/spec/scenario/CoreMacros b/spec/scenario/CoreMacros index a57860852..904afaf42 100644 --- a/spec/scenario/CoreMacros +++ b/spec/scenario/CoreMacros @@ -15,7 +15,7 @@ Macro PricedComptroller closeFactor=0.1 maxAssets=20 ComptrollerImpl ScenComptrollerG1 BecomeG1 (PriceOracleProxy Address) closeFactor maxAssets ComptrollerImpl Deploy Scenario ScenComptroller Unitroller SetPendingImpl ScenComptroller - ComptrollerImpl ScenComptroller Become 1e18 [] + ComptrollerImpl ScenComptroller Become Macro NewComptroller price=1.0 closeFactor=0.1 maxAssets=20 --g1 @@ -30,9 +30,13 @@ Macro NewComptroller price=1.0 closeFactor=0.1 maxAssets=20 Unitroller SetPendingImpl ComptrollerG2 ComptrollerImpl ComptrollerG2 BecomeG2 --g3 + ComptrollerImpl Deploy StandardG3 ComptrollerG3 + Unitroller SetPendingImpl ComptrollerG3 + ComptrollerImpl ComptrollerG3 BecomeG3 1e18 [] + --g4 ComptrollerImpl Deploy Scenario ScenComptroller Unitroller SetPendingImpl ScenComptroller - ComptrollerImpl ScenComptroller Become 1e18 [] + ComptrollerImpl ScenComptroller Become Macro NewCToken erc20 cToken borrowRate=0.000005 initialExchangeRate=2e9 decimals=8 tokenType=Standard delegatorType=CErc20DelegatorScenario cTokenType=CErc20DelegateScenario admin=Admin becomeImplementationData="0x0" Erc20 Deploy tokenType erc20 erc20 diff --git a/spec/scenario/Flywheel/Flywheel.scen b/spec/scenario/Flywheel/Flywheel.scen index db25ede23..ef498ec12 100644 --- a/spec/scenario/Flywheel/Flywheel.scen +++ b/spec/scenario/Flywheel/Flywheel.scen @@ -17,9 +17,9 @@ Macro FlywheelComptroller price=1.0 borrowRate=0.000005 compInitAmount=5000000e1 Support cZRX collateralFactor:0.5 Support cBAT collateralFactor:0.5 -- final - ComptrollerImpl Deploy Scenario ComptrollerScen + ComptrollerImpl Deploy ScenarioG3 ComptrollerScen Unitroller SetPendingImpl ComptrollerScen - ComptrollerImpl ComptrollerScen Become 1e18 [cZRX cBAT] + ComptrollerImpl ComptrollerScen BecomeG3 1e18 [cZRX cBAT] Erc20 Deploy Standard COMP "COMP Token" 18 Give (Address Comptroller) compInitAmount COMP Comptroller Send "setCompAddress(address)" (Address COMP) diff --git a/spec/scenario/PriceOracleProxy.scen b/spec/scenario/PriceOracleProxy.scen index 42fd5551d..600d0536a 100644 --- a/spec/scenario/PriceOracleProxy.scen +++ b/spec/scenario/PriceOracleProxy.scen @@ -10,10 +10,14 @@ Macro SetupPriceOracleProxy ComptrollerImpl Deploy StandardG2 ComptrollerG2 Unitroller SetPendingImpl ComptrollerG2 ComptrollerImpl ComptrollerG2 BecomeG2 + -- Update to G3 + ComptrollerImpl Deploy StandardG3 ComptrollerG3 + Unitroller SetPendingImpl ComptrollerG3 + ComptrollerImpl ComptrollerG3 BecomeG3 1e18 [] -- Update to G* ComptrollerImpl Deploy Scenario ScenComptroller Unitroller SetPendingImpl ScenComptroller - ComptrollerImpl ScenComptroller Become 1e18 [] + ComptrollerImpl ScenComptroller Become NewEtherToken cETH NewCToken USDC cUSDC NewCToken SAI cSAI diff --git a/spec/scenario/Timelock.scen b/spec/scenario/Timelock.scen index 2f1b067da..bf2e039ca 100644 --- a/spec/scenario/Timelock.scen +++ b/spec/scenario/Timelock.scen @@ -201,11 +201,11 @@ Test "Set Pending Comptroller implementation on Unitroller from Timelock" Assert Equal (Unitroller PendingAdmin) (Timelock Address) Assert Equal (Unitroller PendingImplementation) (ComptrollerImpl ScenComptroller Address) From Coburn (Timelock QueueTransaction (Unitroller Address) 0 604900 "_acceptAdmin()" "") - From Coburn (Timelock QueueTransaction (ComptrollerImpl ScenComptroller Address) 0 604900 "_become(address,uint256,address[],address[])" (Unitroller Address) "1000000000000000000" [] []) + From Coburn (Timelock QueueTransaction (ComptrollerImpl ScenComptroller Address) 0 604900 "_become(address)" (Unitroller Address)) FreezeTime 604900 From Coburn (Timelock ExecuteTransaction (Unitroller Address) 0 604900 "_acceptAdmin()" "") Assert Equal (Unitroller Admin) (Timelock Address) Assert Equal (Unitroller PendingAdmin) (Address Zero) - From Coburn (Timelock ExecuteTransaction (ComptrollerImpl ScenComptroller Address) 0 604900 "_become(address,uint256,address[],address[])" (Unitroller Address) "1000000000000000000" [] []) + From Coburn (Timelock ExecuteTransaction (ComptrollerImpl ScenComptroller Address) 0 604900 "_become(address)" (Unitroller Address)) Assert Equal (Unitroller Implementation) (Address ScenComptroller) Assert Equal (Unitroller PendingImplementation) (Address Zero) diff --git a/spec/scenario/Unitroller.scen b/spec/scenario/Unitroller.scen index 289f552c7..cb854e1bf 100644 --- a/spec/scenario/Unitroller.scen +++ b/spec/scenario/Unitroller.scen @@ -19,12 +19,12 @@ Test "Standard Upgrade" Assert Equal (Comptroller MaxAssets) 20 Assert Equal (Comptroller Implementation) (Address StandardComptrollerG2) -- Upgrade to G3 - ComptrollerImpl Deploy Scenario ScenComptroller - Unitroller SetPendingImpl ScenComptroller - ComptrollerImpl ScenComptroller Become 1e18 [cZRX cDAI] + ComptrollerImpl Deploy ScenarioG3 ScenComptrollerG3 + Unitroller SetPendingImpl ScenComptrollerG3 + ComptrollerImpl ScenComptrollerG3 BecomeG3 1e18 [cZRX cDAI] Assert Equal (Comptroller CloseFactor) 0.2 Assert Equal (Comptroller MaxAssets) 20 - Assert Equal (Comptroller Implementation) (Address ScenComptroller) + Assert Equal (Comptroller Implementation) (Address ScenComptrollerG3) Test "Standard Upgrade, then downgrade then upgrade again" Unitroller Deploy @@ -48,13 +48,13 @@ Test "Standard Upgrade, then downgrade then upgrade again" Assert Equal (Comptroller MaxAssets) 20 Assert Equal (Comptroller Implementation) (Address ComptrollerG2) -- Upgrade to G3 - ComptrollerImpl Deploy Scenario ScenComptroller - Unitroller SetPendingImpl ScenComptroller - ComptrollerImpl ScenComptroller Become 1e18 [cZRX cDAI] + ComptrollerImpl Deploy ScenarioG3 ScenComptrollerG3 + Unitroller SetPendingImpl ScenComptrollerG3 + ComptrollerImpl ScenComptrollerG3 BecomeG3 1e18 [cZRX cDAI] Assert Equal (Comptroller PauseGuardian) (Address Coburn) Assert Equal (Comptroller CloseFactor) 0.2 Assert Equal (Comptroller MaxAssets) 20 - Assert Equal (Comptroller Implementation) (Address ScenComptroller) + Assert Equal (Comptroller Implementation) (Address ScenComptrollerG3) -- Downgrade to G2 Unitroller SetPendingImpl ComptrollerG2 ComptrollerImpl ComptrollerG2 BecomeG2 @@ -62,13 +62,13 @@ Test "Standard Upgrade, then downgrade then upgrade again" Assert Equal (Comptroller MaxAssets) 20 Assert Equal (Comptroller Implementation) (Address ComptrollerG2) -- Upgrade to G3 again - Unitroller SetPendingImpl ScenComptroller - ComptrollerImpl ScenComptroller Become 1e18 [] + Unitroller SetPendingImpl ScenComptrollerG3 + ComptrollerImpl ScenComptrollerG3 BecomeG3 1e18 [] Assert Equal (Comptroller GetCompMarkets) [(Address cZRX) (Address cDAI)] Assert Equal (Comptroller PauseGuardian) (Address Coburn) Assert Equal (Comptroller CloseFactor) 0.2 Assert Equal (Comptroller MaxAssets) 20 - Assert Equal (Comptroller Implementation) (Address ScenComptroller) + Assert Equal (Comptroller Implementation) (Address ScenComptrollerG3) Test "Once become, can become again" -- TODO: See why ABI is broken here @@ -254,11 +254,20 @@ Pending "Keeps all storage" CheckV1Storage CheckV2Storage -- - ComptrollerImpl Deploy Standard ComptrollerG3 + ComptrollerImpl Deploy StandardG3 ComptrollerG3 Unitroller SetPendingImpl ComptrollerG3 - ComptrollerImpl ComptrollerG3 Become 1e18 [cZRX cBAT] + ComptrollerImpl ComptrollerG3 BecomeG3 1e18 [cZRX cBAT] -- CheckUnitrollerStorage (Address ComptrollerG3) CheckV1Storage CheckV2Storage CheckV3Storage + -- + ComptrollerImpl Deploy Standard ComptrollerG4 + Unitroller SetPendingImpl ComptrollerG4 + ComptrollerImpl ComptrollerG4 Become + -- + CheckUnitrollerStorage (Address ComptrollerG4) + CheckV1Storage + CheckV2Storage + CheckV3Storage diff --git a/spec/sim/0001-comp-distribution-patch/hypothetical_upgrade.scen b/spec/sim/0001-comp-distribution-patch/hypothetical_upgrade.scen new file mode 100755 index 000000000..c65586c0b --- /dev/null +++ b/spec/sim/0001-comp-distribution-patch/hypothetical_upgrade.scen @@ -0,0 +1,176 @@ +#!/usr/bin/env yarn repl -s + +PrintTransactionLogs +Alias CompHolder "0x19bc62ff7cd9ffd6bdced9802ff718f09f7259f1" +Alias USDCWhale "0x92d7796c04ee34d1d16c57fab92fc2bccf434468" +Alias cBATBorrower "0xe5f3dbcc3dcf75a6946822aae7df5160505d3069" +Web3Fork "https://mainnet-eth.compound.finance/@10331520" (CompHolder USDCWhale cBATBorrower) +UseConfigs mainnet + +-- Deploy the flywheel impl + +ComptrollerImpl Deploy Standard ComptrollerG4 + +-- Propose to apply the patch + +From CompHolder (Comp Delegate CompHolder) +From CompHolder (Governor GovernorAlpha Propose "COMP Distribution Patch" [(Address Unitroller) (Address ComptrollerG4)] [0 0] ["_setPendingImplementation(address)" "_become(address)"] [[(Address ComptrollerG4)] [(Address Unitroller)]]) + +-- Vote for, queue, and execute the proposal + +MineBlock +From CompHolder (Governor GovernorAlpha Proposal LastProposal Vote For) +AdvanceBlocks 20000 +Governor GovernorAlpha Proposal LastProposal Queue +IncreaseTime 604910 +Governor GovernorAlpha Proposal LastProposal Execute + +-- Sanity check the upgrade + +Assert Equal (Comptroller CompRate) 0.25e18 +Assert Equal (Comptroller CheckIsComped cBAT) True +Assert Equal (Comptroller CheckIsComped cDAI) True +Assert Equal (Comptroller CheckIsComped cETH) True +Assert Equal (Comptroller CheckIsComped cREP) True +Assert Equal (Comptroller CheckIsComped cSAI) False +Assert Equal (Comptroller CheckIsComped cUSDC) True +Assert Equal (Comptroller CheckIsComped cUSDT) True +Assert Equal (Comptroller CheckisComped cWBTC) True +Assert Equal (Comptroller CheckIsComped cZRX) True + +-- Sanity check the speeds + +Assert Equal (Comptroller CompSpeed cBAT) 0.211801939788283286e18 +Assert Equal (Comptroller CompSpeed cDAI) 0.001175763573454607e18 +Assert Equal (Comptroller CompSpeed cETH) 0.000020887685831991e18 +Assert Equal (Comptroller CompSpeed cREP) 0.000007175802675015e18 +Assert Equal (Comptroller CompSpeed cSAI) 0 +Assert Equal (Comptroller CompSpeed cUSDC) 0.000884273506888560e18 +Assert Equal (Comptroller CompSpeed cUSDT) 0.020341204032904166e18 +Assert Equal (Comptroller CompSpeed cWBTC) 0.005869479211613005e18 +Assert Equal (Comptroller CompSpeed cZRX) 0.009899276398349366e18 + +-- Check the market borrows + +Assert Equal (CToken cBAT TotalBorrows) 816167647101108360363936140 +Assert Equal (CToken cDAI TotalBorrows) 23521775646627969501566436 +Assert Equal (CToken cETH TotalBorrows) 1260824767124865834187 +Assert Equal (CToken cREP TotalBorrows) 5305791273320090270164 +Assert Equal (CToken cSAI TotalBorrows) 87120566019791136455808 +Assert Equal (CToken cUSDC TotalBorrows) 9369602511430 +Assert Equal (CToken cUSDT TotalBorrows) 51344064688134 +Assert Equal (CToken cWBTC TotalBorrows) 134858882039 +Assert Equal (CToken cZRX TotalBorrows) 54734120640511560139309871 + +-- Check the market prices + +Assert Equal (PriceOracleProxy Price cBAT) 1057982500000000 +Assert Equal (PriceOracleProxy Price cDAI) 4296489354886929 +Assert Equal (PriceOracleProxy Price cETH) 1000000000000000000 +Assert Equal (PriceOracleProxy Price cREP) 71436527500000010 +Assert Equal (PriceOracleProxy Price cSAI) 5285551943761727 +Assert Equal (PriceOracleProxy Price cUSDC) 4261783149807061700000000000 +Assert Equal (PriceOracleProxy Price cUSDT) 4261783149807061700000000000 +Assert Equal (PriceOracleProxy Price cWBTC) 397626172108253540000000000000 +Assert Equal (PriceOracleProxy Price cZRX) 1464966666666666 + +-- Refresh speeds + +Comptroller RefreshCompSpeeds + +-- Check the new speeds match utility metric +-- Total Utility = +-- 816167647101108360363936140 * 1057982500000000 + +-- 23521775646627969501566436 * 4296489354886929 + +-- 1260824767124865834187 * 1000000000000000000 + +-- 5305791273320090270164 * 71436527500000010 + +-- 87120566019791136455808 * 5285551943761727 * 0 (cSAI not comped) + +-- 9369602511430 * 4261783149807061700000000000 + +-- 51344064688134 * 4261783149807061700000000000 + +-- 134858882039 * 397626172108253540000000000000 + +-- 54734120640511560139309871 * 1464966666666666 +-- = 1358747565585977723277660096116431304676770 + +-- .25e18 * 816167647101108360363936140 * 1057982500000000 / 1358747565585977723277660096116431304676770 +-- = 158876289748264717 +Assert Equal (Comptroller CompSpeed cBAT) 158876289748264702 + +-- .25e18 * 23521775646627969501566436 * 4296489354886929 / 1358747565585977723277660096116431304676770 +-- = 18594524331344760 +Assert Equal (Comptroller CompSpeed cDAI) 18594524331344758 + +-- .25e18 * 1260824767124865834187 * 1000000000000000000 / 1358747565585977723277660096116431304676770 +-- = 231982893485648 +Assert Equal (Comptroller CompSpeed cETH) 231982893485648 + +-- .25e18 * 5305791273320090270164 * 71436527500000010 / 1358747565585977723277660096116431304676770 +-- = 69738359391711 +Assert Equal (Comptroller CompSpeed cREP) 69738359391711 + +-- not comped +Assert Equal (Comptroller CompSpeed cSAI) 0 + +-- .25e18 * 9369602511430 * 4261783149807061700000000000 / 1358747565585977723277660096116431304676770 +-- = 7347062676498972 +Assert Equal (Comptroller CompSpeed cUSDC) 7347062676498971 + +-- .25e18 * 51344064688134 * 4261783149807061700000000000 / 1358747565585977723277660096116431304676770 +-- = 40260839333339567 +Assert Equal (Comptroller CompSpeed cUSDT) 40260839333339565 + +-- .25e18 * 134858882039 * 397626172108253540000000000000 / 1358747565585977723277660096116431304676770 +-- = 9866332495845221 +Assert Equal (Comptroller CompSpeed cWBTC) 9866332495845221 + +-- .25e18 * 54734120640511560139309871 * 1464966666666666 / 1358747565585977723277660096116431304676770 +-- = 14753230161829421 +Assert Equal (Comptroller CompSpeed cZRX) 14753230161829420 + +-- Now sanity check that we can continue to use the market as usual + +-- First check the USDC Whale, mint + +Assert Equal (Erc20 cUSDC TokenBalance USDCWhale) 0 + +From USDCWhale (Trx GasPrice 0 (Erc20 USDC Approve cUSDC UInt256Max)) +From USDCWhale (Trx GasPrice 0 (CToken cUSDC Mint 10000e6)) + +Assert Equal (Erc20 cUSDC TokenBalance USDCWhale) 47481980579468 +Assert Equal (Erc20 cUSDC TotalSupply) 927050045835492296 + +-- Next check the BAT borrower, borrow a little more + +Assert Equal (Erc20 cETH TokenBalance cBATBorrower) 384765976900 +Assert Equal (Erc20 cETH TotalSupply) 4585405574577755 + +Assert Equal (CToken cBAT BorrowBalance cBATBorrower) 37550980777226218529804 +Assert Equal (CToken cBAT TotalBorrows) 816167647101108360363936140 + +Expect Changes (CToken cBAT BorrowBalance cBATBorrower) 6666005042256047300989 +From cBATBorrower (CToken cBAT Borrow 6666000000000000000000) + +Assert Equal (CToken cBAT BorrowBalance cBATBorrower) 44216985819482265830793 +Assert Equal (CToken cBAT TotalBorrows) 818374059192055804747435060 + +-- Claim comp to true up their balances + +Comptroller ClaimComp USDCWhale +Comptroller ClaimComp cBATBorrower + +-- Now move the clock forward + +AdvanceBlocks 1000000 + +-- And check that they receive the right amount of COMP when claimed + +-- cUSDC: 47481980579468 / 927050045835492296 * 7347062676498971 * 1e6 = 376304482038247230 +Expect Changes (Erc20 Comp TokenBalance USDCWhale) 0.376305610951693392e18 +Comptroller ClaimComp USDCWhale + +-- cETH: 384765976900 / 4585405574577755 * 231982893485648 * 1e6 = 19465917067611490 +-- cBAT: 44216985819482265830793 / 818374059192055804747435060 * 158876289748264702 * 1e6 = 8584131635094196000 +-- = 0.01946591706761149 + 8.584131635094196000 = 8.603597552161807 +Expect Changes (Erc20 Comp TokenBalance cBATBorrower) 8.603623362954463581e18 +Comptroller ClaimComp cBATBorrower + +Print "COMP distribution patch OK!" diff --git a/tests/Contracts/CErc20Harness.sol b/tests/Contracts/CErc20Harness.sol index 6c3a3a6d5..116582a5d 100644 --- a/tests/Contracts/CErc20Harness.sol +++ b/tests/Contracts/CErc20Harness.sol @@ -4,7 +4,7 @@ import "../../contracts/CErc20Immutable.sol"; import "../../contracts/CErc20Delegator.sol"; import "../../contracts/CErc20Delegate.sol"; import "../../contracts/CDaiDelegate.sol"; -import "./ComptrollerHarness.sol"; +import "./ComptrollerScenario.sol"; contract CErc20Harness is CErc20Immutable { uint blockNumber = 100000; diff --git a/tests/Contracts/CEtherHarness.sol b/tests/Contracts/CEtherHarness.sol index 78db4e7ef..4d494cf0a 100644 --- a/tests/Contracts/CEtherHarness.sol +++ b/tests/Contracts/CEtherHarness.sol @@ -1,7 +1,7 @@ pragma solidity ^0.5.16; import "../../contracts/CEther.sol"; -import "./ComptrollerHarness.sol"; +import "./ComptrollerScenario.sol"; contract CEtherHarness is CEther { uint harnessExchangeRate; diff --git a/tests/Contracts/ComptrollerHarness.sol b/tests/Contracts/ComptrollerHarness.sol index a60c65f6d..7e5812c0c 100644 --- a/tests/Contracts/ComptrollerHarness.sol +++ b/tests/Contracts/ComptrollerHarness.sol @@ -112,63 +112,6 @@ contract ComptrollerHarness is Comptroller { } } -contract ComptrollerScenario is Comptroller { - uint public blockNumber; - address public compAddress; - - constructor() Comptroller() public {} - - function setCompAddress(address compAddress_) public { - compAddress = compAddress_; - } - - function getCompAddress() public view returns (address) { - return compAddress; - } - - function membershipLength(CToken cToken) public view returns (uint) { - return accountAssets[address(cToken)].length; - } - - function fastForward(uint blocks) public returns (uint) { - blockNumber += blocks; - - return blockNumber; - } - - function setBlockNumber(uint number) public { - blockNumber = number; - } - - function getBlockNumber() public view returns (uint) { - return blockNumber; - } - - function getCompMarkets() public view returns (address[] memory) { - uint m = allMarkets.length; - uint n = 0; - for (uint i = 0; i < m; i++) { - if (markets[address(allMarkets[i])].isComped) { - n++; - } - } - - address[] memory compMarkets = new address[](n); - uint k = 0; - for (uint i = 0; i < m; i++) { - if (markets[address(allMarkets[i])].isComped) { - compMarkets[k++] = address(allMarkets[i]); - } - } - return compMarkets; - } - - function unlist(CToken cToken) public { - markets[address(cToken)].isListed = false; - } -} - - contract ComptrollerBorked { function _become(Unitroller unitroller, PriceOracle _oracle, uint _closeFactorMantissa, uint _maxAssets, bool _reinitializing) public { _oracle; diff --git a/tests/Contracts/ComptrollerScenario.sol b/tests/Contracts/ComptrollerScenario.sol new file mode 100644 index 000000000..4c6b189ed --- /dev/null +++ b/tests/Contracts/ComptrollerScenario.sol @@ -0,0 +1,26 @@ +pragma solidity ^0.5.16; + +import "../../contracts/Comptroller.sol"; + +contract ComptrollerScenario is Comptroller { + uint public blockNumber; + + constructor() Comptroller() public {} + + function fastForward(uint blocks) public returns (uint) { + blockNumber += blocks; + return blockNumber; + } + + function setBlockNumber(uint number) public { + blockNumber = number; + } + + function membershipLength(CToken cToken) public view returns (uint) { + return accountAssets[address(cToken)].length; + } + + function unlist(CToken cToken) public { + markets[address(cToken)].isListed = false; + } +} diff --git a/tests/Contracts/ComptrollerScenarioG3.sol b/tests/Contracts/ComptrollerScenarioG3.sol new file mode 100644 index 000000000..6b4d17ebf --- /dev/null +++ b/tests/Contracts/ComptrollerScenarioG3.sol @@ -0,0 +1,59 @@ +pragma solidity ^0.5.16; + +import "../../contracts/ComptrollerG3.sol"; + +contract ComptrollerScenarioG3 is ComptrollerG3 { + uint public blockNumber; + address public compAddress; + + constructor() ComptrollerG3() public {} + + function setCompAddress(address compAddress_) public { + compAddress = compAddress_; + } + + function getCompAddress() public view returns (address) { + return compAddress; + } + + function membershipLength(CToken cToken) public view returns (uint) { + return accountAssets[address(cToken)].length; + } + + function fastForward(uint blocks) public returns (uint) { + blockNumber += blocks; + + return blockNumber; + } + + function setBlockNumber(uint number) public { + blockNumber = number; + } + + function getBlockNumber() public view returns (uint) { + return blockNumber; + } + + function getCompMarkets() public view returns (address[] memory) { + uint m = allMarkets.length; + uint n = 0; + for (uint i = 0; i < m; i++) { + if (markets[address(allMarkets[i])].isComped) { + n++; + } + } + + address[] memory compMarkets = new address[](n); + uint k = 0; + for (uint i = 0; i < m; i++) { + if (markets[address(allMarkets[i])].isComped) { + compMarkets[k++] = address(allMarkets[i]); + } + } + return compMarkets; + } + + function unlist(CToken cToken) public { + markets[address(cToken)].isListed = false; + } +} diff --git a/tests/Flywheel/FlywheelTest.js b/tests/Flywheel/FlywheelTest.js index 1909483ea..33e16094b 100644 --- a/tests/Flywheel/FlywheelTest.js +++ b/tests/Flywheel/FlywheelTest.js @@ -31,24 +31,24 @@ describe('Flywheel upgrade', () => { describe('becomes the comptroller', () => { it('adds the comp markets', async () => { let root = saddle.accounts[0]; - let unitroller = await makeComptroller({kind: 'unitroller-prior'}); + let unitroller = await makeComptroller({kind: 'unitroller-g2'}); let compMarkets = await Promise.all([1, 2, 3].map(async _ => { return makeCToken({comptroller: unitroller, supportMarket: true}); })); compMarkets = compMarkets.map(c => c._address); - unitroller = await makeComptroller({kind: 'unitroller', unitroller, compMarkets}); + unitroller = await makeComptroller({kind: 'unitroller-g3', unitroller, compMarkets}); expect(await call(unitroller, 'getCompMarkets')).toEqual(compMarkets); }); it('adds the other markets', async () => { let root = saddle.accounts[0]; - let unitroller = await makeComptroller({kind: 'unitroller-prior'}); + let unitroller = await makeComptroller({kind: 'unitroller-g2'}); let allMarkets = await Promise.all([1, 2, 3].map(async _ => { return makeCToken({comptroller: unitroller, supportMarket: true}); })); allMarkets = allMarkets.map(c => c._address); unitroller = await makeComptroller({ - kind: 'unitroller', + kind: 'unitroller-g3', unitroller, compMarkets: allMarkets.slice(0, 1), otherMarkets: allMarkets.slice(1) @@ -59,7 +59,7 @@ describe('Flywheel upgrade', () => { it('_supportMarket() adds to all markets, and only once', async () => { let root = saddle.accounts[0]; - let unitroller = await makeComptroller({kind: 'unitroller'}); + let unitroller = await makeComptroller({kind: 'unitroller-g3'}); let allMarkets = []; for (let _ of Array(10)) { allMarkets.push(await makeCToken({comptroller: unitroller, supportMarket: true})); @@ -67,7 +67,7 @@ describe('Flywheel upgrade', () => { expect(await call(unitroller, 'getAllMarkets')).toEqual(allMarkets.map(c => c._address)); expect( makeComptroller({ - kind: 'unitroller', + kind: 'unitroller-g3', unitroller, otherMarkets: [allMarkets[0]._address] }) diff --git a/tests/Utils/Compound.js b/tests/Utils/Compound.js index f71901d74..0eb8f8132 100644 --- a/tests/Utils/Compound.js +++ b/tests/Utils/Compound.js @@ -36,9 +36,9 @@ async function makeComptroller(opts = {}) { return Object.assign(comptroller, { priceOracle }); } - if (kind == 'unitroller-prior') { + if (kind == 'unitroller-g2') { const unitroller = opts.unitroller || await deploy('Unitroller'); - const comptroller = await deploy('ComptrollerG2'); + const comptroller = await deploy('ComptrollerScenarioG2'); const priceOracle = opts.priceOracle || await makePriceOracle(opts.priceOracleOpts); const closeFactor = etherMantissa(dfn(opts.closeFactor, .051)); const maxAssets = etherUnsigned(dfn(opts.maxAssets, 10)); @@ -55,14 +55,13 @@ async function makeComptroller(opts = {}) { return Object.assign(unitroller, { priceOracle }); } - if (kind == 'unitroller') { + if (kind == 'unitroller-g3') { const unitroller = opts.unitroller || await deploy('Unitroller'); - const comptroller = await deploy('ComptrollerHarness'); + const comptroller = await deploy('ComptrollerScenarioG3'); const priceOracle = opts.priceOracle || await makePriceOracle(opts.priceOracleOpts); const closeFactor = etherMantissa(dfn(opts.closeFactor, .051)); const maxAssets = etherUnsigned(dfn(opts.maxAssets, 10)); const liquidationIncentive = etherMantissa(1); - const comp = opts.comp || await deploy('Comp', [opts.compOwner || root]); const compRate = etherUnsigned(dfn(opts.compRate, 1e18)); const compMarkets = opts.compMarkets || []; const otherMarkets = opts.otherMarkets || []; @@ -74,7 +73,29 @@ async function makeComptroller(opts = {}) { await send(unitroller, '_setCloseFactor', [closeFactor]); await send(unitroller, '_setMaxAssets', [maxAssets]); await send(unitroller, '_setPriceOracle', [priceOracle._address]); + + return Object.assign(unitroller, { priceOracle }); + } + + if (kind == 'unitroller') { + const unitroller = opts.unitroller || await deploy('Unitroller'); + const comptroller = await deploy('ComptrollerHarness'); + const priceOracle = opts.priceOracle || await makePriceOracle(opts.priceOracleOpts); + const closeFactor = etherMantissa(dfn(opts.closeFactor, .051)); + const maxAssets = etherUnsigned(dfn(opts.maxAssets, 10)); + const liquidationIncentive = etherMantissa(1); + const comp = opts.comp || await deploy('Comp', [opts.compOwner || root]); + const compRate = etherUnsigned(dfn(opts.compRate, 1e18)); + + await send(unitroller, '_setPendingImplementation', [comptroller._address]); + await send(comptroller, '_become', [unitroller._address]); + mergeInterface(unitroller, comptroller); + await send(unitroller, '_setLiquidationIncentive', [liquidationIncentive]); + await send(unitroller, '_setCloseFactor', [closeFactor]); + await send(unitroller, '_setMaxAssets', [maxAssets]); + await send(unitroller, '_setPriceOracle', [priceOracle._address]); await send(unitroller, 'setCompAddress', [comp._address]); // harness only + await send(unitroller, '_setCompRate', [compRate]); return Object.assign(unitroller, { priceOracle, comp }); }