diff --git a/scenario/src/Contract/CToken.ts b/scenario/src/Contract/CToken.ts index 81742a7e4..9bfd0ea41 100644 --- a/scenario/src/Contract/CToken.ts +++ b/scenario/src/Contract/CToken.ts @@ -47,6 +47,7 @@ export interface CTokenMethods { pendingAdmin(): Callable; _setPendingAdmin(address: string): Sendable; _acceptAdmin(): Sendable; + gulp(): Sendable; } export interface CTokenScenarioMethods extends CTokenMethods { diff --git a/scenario/src/Contract/Comptroller.ts b/scenario/src/Contract/Comptroller.ts index af27b4ae2..82c912a5c 100644 --- a/scenario/src/Contract/Comptroller.ts +++ b/scenario/src/Contract/Comptroller.ts @@ -66,6 +66,10 @@ interface ComptrollerMethods { _setBorrowCapGuardian(string): Sendable borrowCapGuardian(): Callable borrowCaps(string): Callable + _setMarketSupplyCaps(cTokens:string[], supplyCaps:encodedNumber[]): Sendable + _setSupplyCapGuardian(string): Sendable + supplyCapGuardian(): Callable + supplyCaps(string): Callable } export interface Comptroller extends Contract { diff --git a/scenario/src/Event/CTokenEvent.ts b/scenario/src/Event/CTokenEvent.ts index 71282faea..5c17af17f 100644 --- a/scenario/src/Event/CTokenEvent.ts +++ b/scenario/src/Event/CTokenEvent.ts @@ -205,6 +205,18 @@ async function evilSeize(world: World, from: string, cToken: CToken, treasure: C return world; } +async function gulp(world: World, from: string, cToken: CToken): Promise { + let invokation = await invoke(world, cToken.methods.gulp(), from, CTokenErrorReporter); + + world = addAction( + world, + `CToken ${cToken.name}: Gulp`, + invokation + ); + + return world; +} + async function setPendingAdmin(world: World, from: string, cToken: CToken, newPendingAdmin: string): Promise { let invokation = await invoke(world, cToken.methods._setPendingAdmin(newPendingAdmin), from, CTokenErrorReporter); @@ -653,6 +665,18 @@ export function cTokenCommands() { (world, from, { cToken, treasure, liquidator, borrower, seizeTokens }) => evilSeize(world, from, cToken, treasure, liquidator.val, borrower.val, seizeTokens), { namePos: 1 } ), + new Command<{ cToken: CToken}>(` + #### Gulp + * "CToken Gulp" - Gulps for the cToken + * E.g. "CToken cZRX Gulp" + `, + "Gulp", + [ + new Arg("cToken", getCTokenV) + ], + (world, from, { cToken }) => gulp(world, from, cToken), + { namePos: 1 } + ), new Command<{ cToken: CToken, amount: NumberV }>(` #### ReduceReserves diff --git a/scenario/src/Event/ComptrollerEvent.ts b/scenario/src/Event/ComptrollerEvent.ts index 50d83024f..aa1bca93b 100644 --- a/scenario/src/Event/ComptrollerEvent.ts +++ b/scenario/src/Event/ComptrollerEvent.ts @@ -430,6 +430,30 @@ async function setBorrowCapGuardian(world: World, from: string, comptroller: Com return world; } +async function setMarketSupplyCaps(world: World, from: string, comptroller: Comptroller, cTokens: CToken[], supplyCaps: NumberV[]): Promise { + let invokation = await invoke(world, comptroller.methods._setMarketSupplyCaps(cTokens.map(c => c._address), supplyCaps.map(c => c.encode())), from, ComptrollerErrorReporter); + + world = addAction( + world, + `Supply caps on ${cTokens} set to ${supplyCaps}`, + invokation + ); + + return world; +} + +async function setSupplyCapGuardian(world: World, from: string, comptroller: Comptroller, newSupplyCapGuardian: string): Promise { + let invokation = await invoke(world, comptroller.methods._setSupplyCapGuardian(newSupplyCapGuardian), from, ComptrollerErrorReporter); + + world = addAction( + world, + `Comptroller: ${describeUser(world, from)} sets supply cap guardian to ${newSupplyCapGuardian}`, + invokation + ); + + return world; +} + export function comptrollerCommands() { return [ new Command<{comptrollerParams: EventV}>(` @@ -840,6 +864,31 @@ export function comptrollerCommands() { new Arg("newBorrowCapGuardian", getAddressV) ], (world, from, {comptroller, newBorrowCapGuardian}) => setBorrowCapGuardian(world, from, comptroller, newBorrowCapGuardian.val) + ), + new Command<{comptroller: Comptroller, cTokens: CToken[], supplyCaps: NumberV[]}>(` + #### SetMarketSupplyCaps + * "Comptroller SetMarketSupplyCaps ( ...) ( ...)" - Sets Market Supply Caps + * E.g. "Comptroller SetMarketSupplyCaps (cZRX cUSDC) (10000.0e18, 1000.0e6) + `, + "SetMarketSupplyCaps", + [ + new Arg("comptroller", getComptroller, {implicit: true}), + new Arg("cTokens", getCTokenV, {mapped: true}), + new Arg("supplyCaps", getNumberV, {mapped: true}) + ], + (world, from, {comptroller, cTokens, supplyCaps}) => setMarketSupplyCaps(world, from, comptroller, cTokens, supplyCaps) + ), + new Command<{comptroller: Comptroller, newSupplyCapGuardian: AddressV}>(` + #### SetSupplyCapGuardian + * "Comptroller SetSupplyCapGuardian newSupplyCapGuardian:
" - Sets the Supply Cap Guardian for the Comptroller + * E.g. "Comptroller SetSupplyCapGuardian Geoff" + `, + "SetSupplyCapGuardian", + [ + new Arg("comptroller", getComptroller, {implicit: true}), + new Arg("newSupplyCapGuardian", getAddressV) + ], + (world, from, {comptroller, newSupplyCapGuardian}) => setSupplyCapGuardian(world, from, comptroller, newSupplyCapGuardian.val) ) ]; } diff --git a/scenario/src/Value/ComptrollerValue.ts b/scenario/src/Value/ComptrollerValue.ts index a8b809a8c..725cdccc1 100644 --- a/scenario/src/Value/ComptrollerValue.ts +++ b/scenario/src/Value/ComptrollerValue.ts @@ -573,7 +573,31 @@ export function comptrollerFetchers() { async (world, {comptroller, CToken}) => { return new NumberV(await comptroller.methods.borrowCaps(CToken._address).call()); } - ) + ), + new Fetcher<{comptroller: Comptroller}, AddressV>(` + #### SupplyCapGuardian + * "SupplyCapGuardian" - Returns the Comptrollers's SupplyCapGuardian + * E.g. "Comptroller SupplyCapGuardian" + `, + "SupplyCapGuardian", + [ + new Arg("comptroller", getComptroller, {implicit: true}) + ], + async (world, {comptroller}) => new AddressV(await comptroller.methods.supplyCapGuardian().call()) + ), + new Fetcher<{comptroller: Comptroller, CToken: CToken}, NumberV>(` + #### SupplyCaps + * "Comptroller SupplyCaps cZRX + `, + "SupplyCaps", + [ + new Arg("comptroller", getComptroller, {implicit: true}), + new Arg("CToken", getCTokenV), + ], + async (world, {comptroller, CToken}) => { + return new NumberV(await comptroller.methods.supplyCaps(CToken._address).call()); + } + ), ]; } diff --git a/spec/scenario/Borrow.scen b/spec/scenario/Borrow.scen index 203135c19..80dcb9722 100644 --- a/spec/scenario/Borrow.scen +++ b/spec/scenario/Borrow.scen @@ -3,7 +3,7 @@ Test "Borrow some BAT and enters BAT if BAT not entered" NewComptroller price:1.0 NewCToken ZRX cZRX NewCToken BAT cBAT - Give cBAT 10e18 BAT -- Faucet some bat to borrow + GiveCToken cBAT 10e18 BAT -- Faucet some bat to borrow Support cZRX collateralFactor:0.5 Support cBAT collateralFactor:0.5 Prep Geoff Some ZRX cZRX @@ -53,7 +53,7 @@ Test "Borrow fails if market not listed" NewComptroller price:1.0 NewCToken ZRX cZRX NewCToken BAT cBAT - Give cBAT 10e18 BAT -- Faucet some bat to borrow + GiveCToken cBAT 10e18 BAT -- Faucet some bat to borrow Support cZRX collateralFactor:0.5 Prep Geoff Some ZRX cZRX Mint Geoff 100e18 cZRX @@ -67,7 +67,7 @@ Test "Borrow some BAT from Excess Cash" NewComptroller price:1.0 NewCToken ZRX cZRX NewCToken BAT cBAT - Give cBAT 10e18 BAT -- Faucet some bat to borrow + GiveCToken cBAT 10e18 BAT -- Faucet some bat to borrow Support cZRX collateralFactor:0.5 Support cBAT collateralFactor:0.5 Prep Geoff Some ZRX cZRX @@ -83,7 +83,7 @@ Test "Borrow some BAT reverts if borrow is paused" Comptroller SetPauseGuardian Coburn NewCToken ZRX cZRX NewCToken BAT cBAT - Give cBAT 10e18 BAT -- Faucet some bat to borrow + GiveCToken cBAT 10e18 BAT -- Faucet some bat to borrow Support cZRX collateralFactor:0.5 Support cBAT collateralFactor:0.5 Prep Geoff Some ZRX cZRX diff --git a/spec/scenario/BorrowBalance.scen b/spec/scenario/BorrowBalance.scen index 4ba070485..2d4e15709 100644 --- a/spec/scenario/BorrowBalance.scen +++ b/spec/scenario/BorrowBalance.scen @@ -4,7 +4,7 @@ Macro NewBorrow borrowAmount borrowRate user=Geoff NewComptroller price:1.0 -- TODO: This should really be a price for a specific asset NewCToken ZRX cZRX NewCToken BAT cBAT borrowRate -- note: cannot use macros with named args right now - Give cBAT 10e18 BAT -- Faucet some bat to borrow + GiveCToken cBAT 10e18 BAT -- Faucet some bat to borrow Support cZRX collateralFactor:0.5 Support cBAT collateralFactor:0.5 SimpleBorrow user borrowAmount diff --git a/spec/scenario/BorrowCap.scen b/spec/scenario/BorrowCap.scen index 26c73f807..1822fbb22 100644 --- a/spec/scenario/BorrowCap.scen +++ b/spec/scenario/BorrowCap.scen @@ -5,7 +5,7 @@ Test "Attempt to borrow over set cap ERC20" NewCToken BAT cBAT Comptroller SetMarketBorrowCaps (cBAT) (0.5e18) Assert Equal (Comptroller BorrowCaps cBAT) (Exactly 0.5e18) - Give cBAT 10e18 BAT -- Faucet some bat to borrow + GiveCToken cBAT 10e18 BAT -- Faucet some bat to borrow Support cZRX collateralFactor:0.5 Support cBAT collateralFactor:0.5 Prep Geoff Some ZRX cZRX @@ -23,7 +23,7 @@ Test "Attempt to borrow at set cap ERC20" NewCToken ZRX cZRX NewCToken BAT cBAT Comptroller SetMarketBorrowCaps (cBAT) (1000000000000000001) - Give cBAT 10e18 BAT -- Faucet some bat to borrow + GiveCToken cBAT 10e18 BAT -- Faucet some bat to borrow Support cZRX collateralFactor:0.5 Support cBAT collateralFactor:0.5 Prep Geoff Some ZRX cZRX @@ -42,7 +42,7 @@ Test "Attempt to borrow below set cap ERC20" NewCToken ZRX cZRX NewCToken BAT cBAT Comptroller SetMarketBorrowCaps (cBAT) (10e18) - Give cBAT 10e18 BAT -- Faucet some bat to borrow + GiveCToken cBAT 10e18 BAT -- Faucet some bat to borrow Support cZRX collateralFactor:0.5 Support cBAT collateralFactor:0.5 Prep Geoff Some ZRX cZRX @@ -148,8 +148,8 @@ Test "SetBorrowCaps works correctly too" Comptroller SetMarketBorrowCaps (cBAT cUSDC) (0.5e18 1000001) Assert Equal (Comptroller BorrowCaps cBAT) (Exactly 0.5e18) Assert Equal (Comptroller BorrowCaps cUSDC) (Exactly 1000001) - Give cBAT 10e18 BAT -- Faucet some bat to borrow - Give cUSDC 20e6 USDC + GiveCToken cBAT 10e18 BAT -- Faucet some bat to borrow + GiveCToken cUSDC 20e6 USDC Support cZRX collateralFactor:0.5 Support cBAT collateralFactor:0.5 Support cUSDC collateralFactor:0.5 diff --git a/spec/scenario/BorrowWBTC.scen b/spec/scenario/BorrowWBTC.scen index ae7d79df4..62d03dc4f 100644 --- a/spec/scenario/BorrowWBTC.scen +++ b/spec/scenario/BorrowWBTC.scen @@ -4,7 +4,7 @@ Test "Borrow some WBTC enters WBTC and succeeds when not entered" NewComptroller price:1.0 NewCToken ZRX cZRX NewCToken WBTC cWBTC tokenType:WBTC - Give cWBTC 10e8 WBTC -- Faucet some WBTC to borrow + GiveCToken cWBTC 10e8 WBTC -- Faucet some WBTC to borrow Support cZRX collateralFactor:0.5 Support cWBTC collateralFactor:0.5 Prep Geoff Some ZRX cZRX @@ -33,7 +33,7 @@ Test "Borrow some WBTC fails when WBTC paused" NewComptroller price:1.0 NewCToken ZRX cZRX NewCToken WBTC cWBTC tokenType:WBTC - Give cWBTC 10e8 WBTC -- Faucet some WBTC to borrow + GiveCToken cWBTC 10e8 WBTC -- Faucet some WBTC to borrow Support cZRX collateralFactor:0.5 Support cWBTC collateralFactor:0.5 Prep Geoff Some ZRX cZRX @@ -50,7 +50,7 @@ Test "Borrow some WBTC from Excess Cash" NewComptroller price:1.0 NewCToken ZRX cZRX NewCToken WBTC cWBTC tokenType:WBTC - Give cWBTC 10e8 WBTC -- Faucet some WBTC to borrow + GiveCToken cWBTC 10e8 WBTC -- Faucet some WBTC to borrow Support cZRX collateralFactor:0.5 Support cWBTC collateralFactor:0.5 Prep Geoff Some ZRX cZRX diff --git a/spec/scenario/BreakLiquidate.scen b/spec/scenario/BreakLiquidate.scen index 1dc0e9c8c..2f1c01a2c 100644 --- a/spec/scenario/BreakLiquidate.scen +++ b/spec/scenario/BreakLiquidate.scen @@ -4,7 +4,7 @@ Macro NewBorrow borrowAmount mintAmount borrowRate=0.000005 user=Geoff collatera Comptroller LiquidationIncentive liquidationIncentive NewCToken ZRX cZRX NewCToken BAT cBAT borrowRate - Give cBAT 10e18 BAT -- Faucet some bat to borrow + GiveCToken cBAT 10e18 BAT -- Faucet some bat to borrow PriceOracle SetPrice cZRX collateralPrice Support cZRX collateralFactor:0.7 PriceOracle SetPrice cBAT borrowPrice diff --git a/spec/scenario/ChangeDelegate.scen b/spec/scenario/ChangeDelegate.scen index 08dbbc919..94bafc816 100644 --- a/spec/scenario/ChangeDelegate.scen +++ b/spec/scenario/ChangeDelegate.scen @@ -9,3 +9,20 @@ Test "Change the delegate" CTokenDelegate Deploy CErc20Delegate cErc20Delegate2 CToken cDEL SetImplementation (CTokenDelegate cErc20Delegate2 Address) True "0x0" Redeem Jared 50e9 cDEL + +Test "Update the delegate for internal cash" + NewComptroller + NewCToken ZRX cZRX + Support cZRX collateralFactor:0.5 + Prep Jared Some ZRX cZRX + Mint Jared 100e18 cZRX + Give cZRX 100e18 ZRX -- Transfer 100e18 ZRX to cZRX + -- Cash: 100e18, Balance: 200e18 + Assert Equal (CToken cZRX Cash) (Exactly 100e18) + Assert Equal (Erc20 ZRX TokenBalance cZRX) (Exactly 200e18) + -- New Deleagte + CTokenDelegate Deploy CErc20Delegate cErc20Delegate2 + CToken cZRX SetImplementation (CTokenDelegate cErc20Delegate2 Address) True "0x0" + -- Cash: 200e18, Balance: 200e18 + Assert Equal (CToken cZRX Cash) (Exactly 200e18) + Assert Equal (Erc20 ZRX TokenBalance cZRX) (Exactly 200e18) diff --git a/spec/scenario/CoreMacros b/spec/scenario/CoreMacros index 3d0f0efc5..eae0e10c4 100644 --- a/spec/scenario/CoreMacros +++ b/spec/scenario/CoreMacros @@ -97,6 +97,10 @@ Macro SetPriceCF cToken price collateralFactor Macro Give user amount erc20 Erc20 erc20 Faucet user amount +Macro GiveCToken cToken amount erc20 + Erc20 erc20 Faucet cToken amount + CToken cToken Gulp + Macro Donate token amount (Trx Value amount (CToken token Donate)) diff --git a/spec/scenario/EnterExitMarkets.scen b/spec/scenario/EnterExitMarkets.scen index 43671f0c0..2a05ea49d 100644 --- a/spec/scenario/EnterExitMarkets.scen +++ b/spec/scenario/EnterExitMarkets.scen @@ -103,7 +103,7 @@ Test "Realistic Market Scenario" EnterMarkets Geoff cZRX Assert Equal (Comptroller Liquidity Geoff) 1.0e18 -- Fail to borrow BAT due to liquidity - Give cBAT 1000e18 BAT + GiveCToken cBAT 1000e18 BAT HoldInvariants Borrow Geoff 1000e18 cBAT -- 1000e18 * 0.0015 = 1.5e18 required liquidity -- But since we're only in ZRX, we only have 1.0e18 liquidity diff --git a/spec/scenario/ExchangeRate.scen b/spec/scenario/ExchangeRate.scen index 2db30838b..5e3c97371 100644 --- a/spec/scenario/ExchangeRate.scen +++ b/spec/scenario/ExchangeRate.scen @@ -37,6 +37,7 @@ Test "ZRX: Exch. Rate:2e9, Cash(51e18) + Borrows(2.0e18) - Reserves(0.5e18) / To -- Set cash Erc20 ZRX Faucet cZRX 1.0e18 Assert Equal (Erc20 ZRX TokenBalance cZRX) (Exactly 51.0e18) + CToken cZRX Gulp -- Mock total borrows CToken cZRX Mock totalBorrows 2.0e18 Assert Equal (CToken cZRX TotalBorrows) (Exactly 2.0e18) @@ -62,6 +63,7 @@ Test "USDC: Exch. Rate:2e-3, Cash(51e18) + Borrows(2.0e18) - Reserves(0.5e18) / -- Set cash Erc20 USDC Faucet cUSDC 2.0e6 Assert Equal (Erc20 USDC TokenBalance cUSDC) (Exactly 52.0e6) + CToken cUSDC Gulp -- Mock total borrows CToken cUSDC Mock totalBorrows 5.0e6 Assert Equal (CToken cUSDC TotalBorrows) (Exactly 5.0e6) diff --git a/spec/scenario/Fee.scen b/spec/scenario/Fee.scen index 87f6eba67..ab8f4650c 100644 --- a/spec/scenario/Fee.scen +++ b/spec/scenario/Fee.scen @@ -26,7 +26,7 @@ Test "Repay borrow should work and not change exchange rate" PriceOracle SetPrice cUSDT 1.0 Support cZRX 0.5 Support cUSDT 0.5 - Give cUSDT 10e18 USDT -- Faucet some Tether to borrow + GiveCToken cUSDT 10e18 USDT -- Faucet some Tether to borrow Invariant Static (CToken cUSDT ExchangeRate) Prep Torrey 100e18 ZRX cZRX Mint Torrey 100e18 cZRX @@ -52,7 +52,7 @@ Test "Should be able to liquidate fee token borrow" PriceOracle SetPrice cUSDT 1.0 Support cZRX 0.5 Support cUSDT 0.5 - Give cUSDT 10e18 USDT -- Faucet some Tether to borrow + GiveCToken cUSDT 10e18 USDT -- Faucet some Tether to borrow Invariant Static (CToken cUSDT ExchangeRate) Invariant Static (CToken cZRX ExchangeRate) Prep Torrey 2e18 ZRX cZRX diff --git a/spec/scenario/InKindLiquidation.scen b/spec/scenario/InKindLiquidation.scen index c23c6bfd0..8c6449e70 100644 --- a/spec/scenario/InKindLiquidation.scen +++ b/spec/scenario/InKindLiquidation.scen @@ -3,7 +3,7 @@ Macro InKindBorrow borrowAmount borrowRate user=Geoff borrowPrice=1.0 mintAmount PricedComptroller Comptroller LiquidationIncentive 1.1 NewCToken BAT cBAT borrowRate 2e9 8 borrowTokenType -- note: cannot use macros with named args right now - Give cBAT giveAmount BAT -- Faucet some bat + GiveCToken cBAT giveAmount BAT -- Faucet some bat PriceOracle SetPrice cBAT borrowPrice Support cBAT collateralFactor:0.5 Prep user mintAmount BAT cBAT diff --git a/spec/scenario/RepayBorrow.scen b/spec/scenario/RepayBorrow.scen index ffdadf878..f75a98cb6 100644 --- a/spec/scenario/RepayBorrow.scen +++ b/spec/scenario/RepayBorrow.scen @@ -4,7 +4,7 @@ Macro NewBorrow borrowAmount borrowRate NewComptroller price:1.0 -- TODO: This should really be a price for a specific asset NewCToken ZRX cZRX NewCToken BAT cBAT borrowRate -- note: cannot use macros with named args right now - Give cBAT 10e18 BAT -- Faucet some bat to borrow + GiveCToken cBAT 10e18 BAT -- Faucet some bat to borrow Support cZRX collateralFactor:0.5 Support cBAT collateralFactor:0.5 Prep Geoff Some ZRX cZRX diff --git a/spec/scenario/RepayBorrowWBTC.scen b/spec/scenario/RepayBorrowWBTC.scen index b28648098..149a979e7 100644 --- a/spec/scenario/RepayBorrowWBTC.scen +++ b/spec/scenario/RepayBorrowWBTC.scen @@ -4,7 +4,7 @@ Macro NewBorrow borrowAmount borrowRate NewComptroller price:1.0 -- TODO: This should really be a price for a specific asset NewCToken ZRX cZRX NewCToken WBTC cWBTC borrowRate 0.1 8 WBTC -- note: cannot use macros with named args right now - Give cWBTC 10e8 WBTC -- Faucet some WBTC to borrow + GiveCToken cWBTC 10e8 WBTC -- Faucet some WBTC to borrow Support cZRX collateralFactor:0.5 Support cWBTC collateralFactor:0.5 Prep Geoff Some ZRX cZRX diff --git a/spec/scenario/SupplyCap.scen b/spec/scenario/SupplyCap.scen new file mode 100644 index 000000000..9c01966eb --- /dev/null +++ b/spec/scenario/SupplyCap.scen @@ -0,0 +1,123 @@ +Test "Attempt to supply over set cap ERC20" + NewComptroller price:1.0 + NewCToken ZRX cZRX + Comptroller SetMarketSupplyCaps (cZRX) (0.5e18) + Assert Equal (Comptroller SupplyCaps cZRX) (Exactly 0.5e18) + Support cZRX collateralFactor:0.5 + Prep Geoff 0.5e18 ZRX cZRX + AllowFailures + Mint Geoff 0.5e18 cZRX + Assert Revert + Assert Equal (Erc20 ZRX TokenBalance Geoff) (Exactly 0.5e18) + Assert Equal (Erc20 ZRX TokenBalance cZRX) (Exactly 0) + +Test "Attempt to supply at set cap ERC20" + NewComptroller price:1.0 + NewCToken ZRX cZRX + NewCToken BAT cBAT + Comptroller SetMarketSupplyCaps (cZRX) (100000000000000000001) + Support cZRX collateralFactor:0.5 + Prep Geoff Some ZRX cZRX + Mint Geoff 100e18 cZRX + Assert Equal (Erc20 ZRX TokenBalance Geoff) (Exactly 0) + Assert Equal (Erc20 ZRX TokenBalance cZRX) (Exactly 100e18) + +Test "Attempt to supply below set cap ERC20" + NewComptroller price:1.0 + NewCToken ZRX cZRX + Comptroller SetMarketSupplyCaps (cZRX) (1e18) + Support cZRX collateralFactor:0.5 + Prep Geoff Some ZRX cZRX + Mint Geoff 0.5e18 cZRX + Assert Equal (Erc20 ZRX TokenBalance Geoff) (Exactly 99.5e18) + Assert Equal (Erc20 ZRX TokenBalance cZRX) (Exactly 0.5e18) + +Test "Cannot supply more even all underlying is borrowed" + NewComptroller price:1.0 + NewCToken ZRX cZRX + NewCToken BAT cBAT + Support cZRX collateralFactor:0.5 + Support cBAT collateralFactor:0.5 + Comptroller SetMarketSupplyCaps (cZRX) (2) + Prep Jared Some ZRX cZRX + Mint Jared 1 cZRX + Prep Robert Some BAT cBAT + Mint Robert 100e18 cBAT + EnterMarkets Robert cBAT + Borrow Robert 1 cZRX -- Robert borrows all ZRX + Assert Equal (Erc20 ZRX TokenBalance cZRX) (Exactly 0) + Prep Geoff Some ZRX cZRX + AllowFailures + Mint Geoff 1 cZRX + Assert Revert + Assert Equal (Erc20 ZRX TokenBalance Geoff) (Exactly Some) + +Test "Setting supply cap restricted to admin" + NewComptroller price:1.0 + ListedCToken ZRX cZRX + SetCollateralFactor cZRX collateralFactor:0.5 + AllowFailures + From Robert (Comptroller SetMarketSupplyCaps (cZRX) (0.01e18)) + Assert Revert + +Test "Supply cap guardian can set supply caps" + NewComptroller price:1.0 + ListedCToken ZRX cZRX + SetCollateralFactor cZRX collateralFactor:0.5 + Comptroller SetSupplyCapGuardian Geoff + From Geoff (Comptroller SetMarketSupplyCaps (cZRX) (0.5e18)) + Assert Equal (Comptroller SupplyCaps cZRX) (Exactly 0.5e18) + Assert Equal (Comptroller SupplyCapGuardian) (User Geoff Address) + AllowFailures + From Robert (Comptroller SetMarketSupplyCaps (cZRX) (0.01e18)) -- Robert still can't... + Assert Revert + From Robert (Comptroller SetMarketSupplyCaps (cZRX) (0.01e18)) + Assert Revert + +Test "Only admin can set Supply Cap Guardian" + NewComptroller price:1.0 + AllowFailures + From Robert (Comptroller SetSupplyCapGuardian Robert) -- Robert has really gone rogue + Assert Revert + +Test "Reserves should not affect supply cap" + NewComptroller price:1.0 + NewCToken USDC cUSDC + Support cUSDC collateralFactor:0.5 + Prep Geoff Some USDC cUSDC + Mint Geoff 14e18 cUSDC + AddReserves 1e18 cUSDC Geoff + Assert Equal (Erc20 USDC TokenBalance cUSDC) (Exactly 15e18) + Assert Equal (CToken cUSDC Reserves) (Exactly 1e18) + -- Current supply level should exclude reserves, which should be 15e18 - 1e18 = 14e18. + -- Setting supply caps to 14e18 should block users from supplying. + Comptroller SetMarketSupplyCaps (cUSDC) (14e18) + AllowFailures + Mint Geoff 1 cUSDC + Assert Revert + Successfully + Comptroller SetMarketSupplyCaps (cUSDC) (15e18) + Mint Geoff 999999999999999999 cUSDC + +Test "Infimint attack" + NewComptroller price:1.0 + NewCToken ZRX cZRX + NewCToken BAT cBAT + GiveCToken cBAT 10e18 BAT -- Faucet some bat to borrow + Support cZRX collateralFactor:0.5 + Support cBAT collateralFactor:0.5 + Prep Geoff Some BAT cBAT + Mint Geoff 100e18 cBAT + EnterMarkets Geoff cBAT + Give Geoff 10000e18 BAT + --Attack + From Geoff (Erc20 BAT Transfer (Address cBAT) 10000e18) + AllowFailures + Borrow Geoff 1000e18 cZRX + Assert Failure COMPTROLLER_REJECTION BORROW_COMPTROLLER_REJECTION INSUFFICIENT_LIQUIDITY + Successfully + CToken cBAT Gulp + AllowFailures + Borrow Geoff 1000e18 cZRX + Assert Failure COMPTROLLER_REJECTION BORROW_COMPTROLLER_REJECTION INSUFFICIENT_LIQUIDITY + Assert Equal (CToken cBAT Reserves) 10010e18 diff --git a/spec/scenario/Unitroller.scen b/spec/scenario/Unitroller.scen index cb854e1bf..84672f697 100644 --- a/spec/scenario/Unitroller.scen +++ b/spec/scenario/Unitroller.scen @@ -232,7 +232,7 @@ Pending "Keeps all storage" --- NewCToken ZRX cZRX delegatorType:CErc20Delegator cTokenType:CErc20Delegate NewCToken BAT cBAT delegatorType:CErc20Delegator cTokenType:CErc20Delegate - Give cBAT 10e18 BAT -- Faucet some bat to borrow + GiveCToken cBAT 10e18 BAT -- Faucet some bat to borrow Support cZRX collateralFactor:0.5 Support cBAT collateralFactor:0.4 Prep Geoff Some ZRX cZRX diff --git a/tests/Contracts/CErc20Harness.sol b/tests/Contracts/CErc20Harness.sol index 116582a5d..e9ce67c03 100644 --- a/tests/Contracts/CErc20Harness.sol +++ b/tests/Contracts/CErc20Harness.sol @@ -372,6 +372,10 @@ contract CErc20DelegateHarness is CErc20Delegate { function harnessCallBorrowAllowed(uint amount) public returns (uint) { return comptroller.borrowAllowed(address(this), msg.sender, amount); } + + function harnessSetInternalCash(uint amount) public { + internalCash = amount; + } } contract CErc20DelegateScenario is CErc20Delegate { diff --git a/tests/SpinaramaTest.js b/tests/SpinaramaTest.js index b22899dcf..065d3b81d 100644 --- a/tests/SpinaramaTest.js +++ b/tests/SpinaramaTest.js @@ -74,6 +74,7 @@ describe('Spinarama', () => { await send(cToken, 'harnessSetExchangeRate', [etherMantissa(1)]); await send(cToken, 'harnessSetBalance', [from, 10]); await send(cToken.underlying, 'harnessSetBalance', [cToken._address, 10]); + await send(cToken, 'gulp'); await send(cToken.underlying, 'approve', [cToken._address, 10], {from}); await minerStop(); const p1 = send(cToken, 'redeem', [10], {from}); @@ -92,6 +93,7 @@ describe('Spinarama', () => { await send(cToken1.underlying, 'harnessSetBalance', [from, 10]); await send(cToken1.underlying, 'approve', [cToken1._address, 10], {from}); await send(cToken2.underlying, 'harnessSetBalance', [cToken2._address, 10]); + await send(cToken2, 'gulp'); await send(cToken2, 'harnessSetTotalSupply', [100]); await send(cToken2.underlying, 'approve', [cToken2._address, 10], {from}); await send(cToken2, 'harnessSetExchangeRate', [etherMantissa(1)]); diff --git a/tests/Tokens/borrowAndRepayTest.js b/tests/Tokens/borrowAndRepayTest.js index 2d8087ab4..4dbc41072 100644 --- a/tests/Tokens/borrowAndRepayTest.js +++ b/tests/Tokens/borrowAndRepayTest.js @@ -23,6 +23,7 @@ async function preBorrow(cToken, borrower, borrowAmount) { await send(cToken.comptroller, 'setBorrowVerify', [true]); await send(cToken.interestRateModel, 'setFailBorrowRate', [false]); await send(cToken.underlying, 'harnessSetBalance', [cToken._address, borrowAmount]); + await send(cToken, 'gulp'); await send(cToken, 'harnessSetFailTransferToAddress', [borrower, false]); await send(cToken, 'harnessSetAccountBorrows', [borrower, 0, 0]); await send(cToken, 'harnessSetTotalBorrows', [0]); diff --git a/tests/Tokens/cTokenTest.js b/tests/Tokens/cTokenTest.js index a42445b53..e37868635 100644 --- a/tests/Tokens/cTokenTest.js +++ b/tests/Tokens/cTokenTest.js @@ -199,20 +199,16 @@ describe('CToken', function () { it("calculates with cash and cTokenSupply", async () => { const cTokenSupply = 5e18, totalBorrows = 0, totalReserves = 0; - expect( - await send(cToken.underlying, 'transfer', [cToken._address, etherMantissa(500)]) - ).toSucceed(); await send(cToken, 'harnessExchangeRateDetails', [cTokenSupply, totalBorrows, totalReserves].map(etherUnsigned)); + expect(await send(cToken, 'harnessSetInternalCash', [etherMantissa(500)])).toSucceed(); const result = await call(cToken, 'exchangeRateStored'); expect(result).toEqualNumber(etherMantissa(100)); }); it("calculates with cash, borrows, reserves and cTokenSupply", async () => { const cTokenSupply = 500e18, totalBorrows = 500e18, totalReserves = 5e18; - expect( - await send(cToken.underlying, 'transfer', [cToken._address, etherMantissa(500)]) - ).toSucceed(); await send(cToken, 'harnessExchangeRateDetails', [cTokenSupply, totalBorrows, totalReserves].map(etherUnsigned)); + expect(await send(cToken, 'harnessSetInternalCash', [etherMantissa(500)])).toSucceed(); const result = await call(cToken, 'exchangeRateStored'); expect(result).toEqualNumber(etherMantissa(1.99)); }); diff --git a/tests/Tokens/mintAndRedeemTest.js b/tests/Tokens/mintAndRedeemTest.js index 4a7b66351..bdcab3da0 100644 --- a/tests/Tokens/mintAndRedeemTest.js +++ b/tests/Tokens/mintAndRedeemTest.js @@ -44,6 +44,7 @@ async function preRedeem(cToken, redeemer, redeemTokens, redeemAmount, exchangeR await send(cToken.comptroller, 'setRedeemVerify', [true]); await send(cToken.interestRateModel, 'setFailBorrowRate', [false]); await send(cToken.underlying, 'harnessSetBalance', [cToken._address, redeemAmount]); + await send(cToken, 'harnessSetInternalCash', [redeemAmount]); await send(cToken.underlying, 'harnessSetBalance', [redeemer, 0]); await send(cToken.underlying, 'harnessSetFailTransferToAddress', [redeemer, false]); await send(cToken, 'harnessSetExchangeRate', [etherMantissa(exchangeRate)]); @@ -193,6 +194,7 @@ describe('CToken', function () { it("fails if insufficient protocol cash to transfer out", async() => { await send(cToken.underlying, 'harnessSetBalance', [cToken._address, 1]); + await send(cToken, 'harnessSetInternalCash', [1]); expect(await redeemFresh(cToken, redeemer, redeemTokens, redeemAmount)).toHaveTokenFailure('TOKEN_INSUFFICIENT_CASH', 'REDEEM_TRANSFER_OUT_NOT_POSSIBLE'); }); @@ -258,6 +260,7 @@ describe('CToken', function () { it("returns error from redeemFresh without emitting any extra logs", async () => { await setBalance(cToken.underlying, cToken._address, 0); + await send(cToken, 'harnessSetInternalCash', [0]); expect(await quickRedeem(cToken, redeemer, redeemTokens, {exchangeRate})).toHaveTokenFailure('TOKEN_INSUFFICIENT_CASH', 'REDEEM_TRANSFER_OUT_NOT_POSSIBLE'); }); diff --git a/tests/Tokens/reservesTest.js b/tests/Tokens/reservesTest.js index 13864a8ce..dd867f54d 100644 --- a/tests/Tokens/reservesTest.js +++ b/tests/Tokens/reservesTest.js @@ -106,6 +106,7 @@ describe('CToken', function () { expect( await send(cToken.underlying, 'harnessSetBalance', [cToken._address, cash]) ).toSucceed(); + expect(await send(cToken, 'harnessSetInternalCash', [cash])).toSucceed(); }); it("fails if called by non-admin", async () => { @@ -129,6 +130,7 @@ describe('CToken', function () { it("fails if amount exceeds available cash", async () => { const cashLessThanReserves = reserves.minus(2); await send(cToken.underlying, 'harnessSetBalance', [cToken._address, cashLessThanReserves]); + await send(cToken, 'harnessSetInternalCash', [cashLessThanReserves]); expect(await send(cToken, 'harnessReduceReservesFresh', [reserves])).toHaveTokenFailure('TOKEN_INSUFFICIENT_CASH', 'REDUCE_RESERVES_CASH_NOT_AVAILABLE'); expect(await call(cToken, 'totalReserves')).toEqualNumber(reserves); }); @@ -159,6 +161,7 @@ describe('CToken', function () { expect( await send(cToken.underlying, 'harnessSetBalance', [cToken._address, cash]) ).toSucceed(); + expect(await send(cToken, 'harnessSetInternalCash', [cash])).toSucceed(); }); it("emits a reserve-reduction failure if interest accrual fails", async () => { @@ -179,4 +182,20 @@ describe('CToken', function () { expect(await send(cToken, '_reduceReserves', [reduction])).toSucceed(); }); }); + + describe('gulp', () => { + let cToken; + beforeEach(async () => { + cToken = await makeCToken(); + }); + + it('absorbs excess cash into reserves', async () => { + expect( + await send(cToken.underlying, 'transfer', [cToken._address, cash]) + ).toSucceed(); + expect(await send(cToken, 'gulp')).toSucceed(); + expect(await call(cToken, 'getCash')).toEqualNumber(cash); + expect(await call(cToken, 'totalReserves')).toEqualNumber(cash); + }); + }); });