Skip to content

Commit

Permalink
[SC-455] June 27 Spell (#46)
Browse files Browse the repository at this point in the history
* feat: bootstrap spell

* fix: WIP debugging

* refactor: cleanup

* refactor: remove logs

* refactor: add minor refactorings

* refactor: remove redundant spaces

* feat: add votes, remove pranks, add diff

* refactor: remove redundant _bpsToRay

* fix: remove unused imports

* test: add USDC.e to deal2

* fix: fix USDC.e flashloan test

* fix: fix rounding error

* fix: remove unused import

* fix: remove redundant spaces

* fix: restrict function purity

* feat: deploy
  • Loading branch information
barrutko authored Jun 21, 2024
1 parent 4870e07 commit 840bcf3
Show file tree
Hide file tree
Showing 10 changed files with 552 additions and 5 deletions.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Raw diff

```json
{}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
## Reserve changes

### Reserves added

#### USDC.e ([0x2a22f9c3b484c3629090FeED35F17Ff8F88f76F0](https://gnosisscan.io/address/0x2a22f9c3b484c3629090FeED35F17Ff8F88f76F0))

| description | value |
| --- | --- |
| decimals | 6 |
| isActive | true |
| isFrozen | false |
| supplyCap | 10,000,000 USDC.e |
| borrowCap | 8,000,000 USDC.e |
| debtCeiling | 0 $ |
| isSiloed | false |
| isFlashloanable | true |
| eModeCategory | 0 |
| oracle | [0x6FC2871B6d9A94866B7260896257Fd5b50c09900](https://gnosisscan.io/address/0x6FC2871B6d9A94866B7260896257Fd5b50c09900) |
| oracleDecimals | 8 |
| oracleLatestAnswer | 1 |
| usageAsCollateralEnabled | false |
| ltv | 0 % |
| liquidationThreshold | 0 % |
| liquidationBonus | 0 % |
| liquidationProtocolFee | 0 % |
| reserveFactor | 10 % |
| aToken | [0xA34DB0ee8F84C4B90ed268dF5aBbe7Dcd3c277ec](https://gnosisscan.io/address/0xA34DB0ee8F84C4B90ed268dF5aBbe7Dcd3c277ec) |
| aTokenImpl | [0x856900aa78e856a5df1a2665eE3a66b2487cD68f](https://gnosisscan.io/address/0x856900aa78e856a5df1a2665eE3a66b2487cD68f) |
| variableDebtToken | [0x397b97b572281d0b3e3513BD4A7B38050a75962b](https://gnosisscan.io/address/0x397b97b572281d0b3e3513BD4A7B38050a75962b) |
| variableDebtTokenImpl | [0x0ee554F6A1f7a4Cb4f82D4C124DdC2AD3E37fde1](https://gnosisscan.io/address/0x0ee554F6A1f7a4Cb4f82D4C124DdC2AD3E37fde1) |
| stableDebtToken | [0xC5dfde524371F9424c81F453260B2CCd24936c15](https://gnosisscan.io/address/0xC5dfde524371F9424c81F453260B2CCd24936c15) |
| stableDebtTokenImpl | [0x4370D3b6C9588E02ce9D22e684387859c7Ff5b34](https://gnosisscan.io/address/0x4370D3b6C9588E02ce9D22e684387859c7Ff5b34) |
| borrowingEnabled | true |
| stableBorrowRateEnabled | false |
| isBorrowableInIsolation | true |
| interestRateStrategy | [0xe66F24175A204E7286F0609CC594667D343E7aAE](https://gnosisscan.io/address/0xe66F24175A204E7286F0609CC594667D343E7aAE) |
| aTokenName | Spark USDC.e |
| aTokenSymbol | spUSDC.e |
| isPaused | false |
| stableDebtTokenName | Spark Stable Debt USDC.e |
| stableDebtTokenSymbol | stableDebtUSDC.e |
| variableDebtTokenName | Spark Variable Debt USDC.e |
| variableDebtTokenSymbol | variableDebtUSDC.e |
| optimalUsageRatio | 95 % |
| maxExcessUsageRatio | 5 % |
| baseVariableBorrowRate | 0 % |
| variableRateSlope1 | 9 % |
| variableRateSlope2 | 15 % |
| baseStableBorrowRate | 9 % |
| stableRateSlope1 | 0 % |
| stableRateSlope2 | 0 % |
| optimalStableToTotalDebtRatio | 0 % |
| maxExcessStableToTotalDebtRatio | 100 % |


### Reserves altered

#### USDC ([0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83](https://gnosisscan.io/address/0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83))

| description | value before | value after |
| --- | --- | --- |
| borrowCap | 8,000,000 USDC | 1,000,000 USDC |
| interestRateStrategy | [0xe66F24175A204E7286F0609CC594667D343E7aAE](https://gnosisscan.io/address/0xe66F24175A204E7286F0609CC594667D343E7aAE) | [0x410CB8b77129AeB28fE66F73deef8AC91A36c9AB](https://gnosisscan.io/address/0x410CB8b77129AeB28fE66F73deef8AC91A36c9AB) |
| optimalUsageRatio | 95 % | 80 % |
| maxExcessUsageRatio | 5 % | 20 % |
| variableRateSlope2 | 15 % | 50 % |


## Raw diff

```json
{
"reserves": {
"0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83": {
"borrowCap": {
"from": 8000000,
"to": 1000000
},
"interestRateStrategy": {
"from": "0xe66F24175A204E7286F0609CC594667D343E7aAE",
"to": "0x410CB8b77129AeB28fE66F73deef8AC91A36c9AB"
}
},
"0x2a22f9c3b484c3629090FeED35F17Ff8F88f76F0": {
"from": null,
"to": {
"aToken": "0xA34DB0ee8F84C4B90ed268dF5aBbe7Dcd3c277ec",
"aTokenImpl": "0x856900aa78e856a5df1a2665eE3a66b2487cD68f",
"aTokenName": "Spark USDC.e",
"aTokenSymbol": "spUSDC.e",
"borrowCap": 8000000,
"borrowingEnabled": true,
"debtCeiling": 0,
"decimals": 6,
"eModeCategory": 0,
"interestRateStrategy": "0xe66F24175A204E7286F0609CC594667D343E7aAE",
"isActive": true,
"isBorrowableInIsolation": true,
"isFlashloanable": true,
"isFrozen": false,
"isPaused": false,
"isSiloed": false,
"liquidationBonus": 0,
"liquidationProtocolFee": 0,
"liquidationThreshold": 0,
"ltv": 0,
"oracle": "0x6FC2871B6d9A94866B7260896257Fd5b50c09900",
"oracleDecimals": 8,
"oracleLatestAnswer": 100000000,
"reserveFactor": 1000,
"stableBorrowRateEnabled": false,
"stableDebtToken": "0xC5dfde524371F9424c81F453260B2CCd24936c15",
"stableDebtTokenImpl": "0x4370D3b6C9588E02ce9D22e684387859c7Ff5b34",
"stableDebtTokenName": "Spark Stable Debt USDC.e",
"stableDebtTokenSymbol": "stableDebtUSDC.e",
"supplyCap": 10000000,
"symbol": "USDC.e",
"underlying": "0x2a22f9c3b484c3629090FeED35F17Ff8F88f76F0",
"usageAsCollateralEnabled": false,
"variableDebtToken": "0x397b97b572281d0b3e3513BD4A7B38050a75962b",
"variableDebtTokenImpl": "0x0ee554F6A1f7a4Cb4f82D4C124DdC2AD3E37fde1",
"variableDebtTokenName": "Spark Variable Debt USDC.e",
"variableDebtTokenSymbol": "variableDebtUSDC.e"
}
}
},
"strategies": {
"0x410CB8b77129AeB28fE66F73deef8AC91A36c9AB": {
"from": null,
"to": {
"baseStableBorrowRate": "90000000000000000000000000",
"baseVariableBorrowRate": 0,
"maxExcessStableToTotalDebtRatio": "1000000000000000000000000000",
"maxExcessUsageRatio": "200000000000000000000000000",
"optimalStableToTotalDebtRatio": 0,
"optimalUsageRatio": "800000000000000000000000000",
"stableRateSlope1": 0,
"stableRateSlope2": 0,
"variableRateSlope1": "90000000000000000000000000",
"variableRateSlope2": "500000000000000000000000000"
}
}
}
}
```
9 changes: 8 additions & 1 deletion src/CommonTestBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ contract CommonTestBase is Test {

address public constant USDC_MAINNET = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;

address public constant EURE_GNOSIS = 0xcB444e90D8198415266c6a2724b7900fb12FC56E;
address public constant EURE_GNOSIS = 0xcB444e90D8198415266c6a2724b7900fb12FC56E;
address public constant USDCE_GNOSIS = 0x2a22f9c3b484c3629090FeED35F17Ff8F88f76F0;

/**
* @notice deal doesn't support amounts stored in a script right now.
Expand All @@ -51,6 +52,12 @@ contract CommonTestBase is Test {
IERC20(asset).transfer(user, amount);
return true;
}
// USDC.e
if (asset == USDCE_GNOSIS) {
vm.prank(0xBA12222222228d8Ba445958a75a0704d566BF2C8);
IERC20(asset).transfer(user, amount);
return true;
}
}
return false;
}
Expand Down
9 changes: 5 additions & 4 deletions src/ProtocolV3TestBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -312,12 +312,13 @@ contract ProtocolV3TestBase is CommonTestBase {
ReserveConfig memory borrowConfig,
uint256 collateralAmount
) internal view returns (uint256) {
// Intentionally introducing a slight rounding error to not trigger the LTV edge case failure condition
return collateralAmount
* _getTokenPrice(pool, collateralConfig)
* collateralConfig.ltv
* (10 ** borrowConfig.decimals)
/ _getTokenPrice(pool, borrowConfig)
* (10 ** borrowConfig.decimals)
/ (10 ** collateralConfig.decimals)
* collateralConfig.ltv
/ 100_00;
}

Expand Down Expand Up @@ -560,8 +561,8 @@ contract ProtocolV3TestBase is CommonTestBase {
address pool = abi.decode(params, (address));
assertEq(IERC20(asset).balanceOf(address(this)), amount, 'UNDERLYING_NOT_AMOUNT');

// Temporary measure while USDC/EURe deal gets fixed, set the balance to amount + premium either way
uint256 dealAmount = asset == USDC_MAINNET || asset == EURE_GNOSIS ? premium : amount + premium;
// Temporary measure while USDC/EURe/USDC.e deal gets fixed, set the balance to amount + premium either way
uint256 dealAmount = asset == USDC_MAINNET || asset == EURE_GNOSIS || asset == USDCE_GNOSIS ? premium : amount + premium;
deal2(asset, address(this), dealAmount);

vm.startPrank(address(this));
Expand Down
55 changes: 55 additions & 0 deletions src/proposals/20240627/SparkEthereum_20240627.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.10;

import { IMetaMorpho, MarketParams } from 'lib/metamorpho/src/interfaces/IMetaMorpho.sol';
import { Gnosis } from 'lib/spark-address-registry/src/Gnosis.sol';
import { XChainForwarders } from 'lib/xchain-helpers/src/XChainForwarders.sol';

import { SparkPayloadEthereum, Ethereum } from 'src/SparkPayloadEthereum.sol';

/**
* @title June 27, 2024 Spark Ethereum Proposal
* @notice Update Morpho supply caps, trigger Gnosis payload
* @author Phoenix Labs
* Forum: https://forum.makerdao.com/t/jun-12-2024-proposed-changes-to-sparklend-for-upcoming-spell/24489
* Votes: https://vote.makerdao.com/polling/QmQv9zQR
* https://vote.makerdao.com/polling/QmU6KSGc
* https://vote.makerdao.com/polling/QmdQYTQe
*/
contract SparkEthereum_20240627 is SparkPayloadEthereum {
address public constant GNOSIS_PAYLOAD = 0xd5A8d293Ce8B31123C285d55d0232b3C31c4D217;

function _postExecute()
internal override
{
// Morpho Vault Supply Cap Changes
IMetaMorpho(Ethereum.MORPHO_VAULT_DAI_1).submitCap(
MarketParams({
loanToken: Ethereum.DAI,
collateralToken: Ethereum.SUSDE,
oracle: Ethereum.MORPHO_SUSDE_ORACLE,
irm: Ethereum.MORPHO_DEFAULT_IRM,
lltv: 0.86e18
}),
500_000_000e18
);
IMetaMorpho(Ethereum.MORPHO_VAULT_DAI_1).submitCap(
MarketParams({
loanToken: Ethereum.DAI,
collateralToken: Ethereum.SUSDE,
oracle: Ethereum.MORPHO_SUSDE_ORACLE,
irm: Ethereum.MORPHO_DEFAULT_IRM,
lltv: 0.915e18
}),
200_000_000e18
);

// Trigger Gnosis Payload
XChainForwarders.sendMessageGnosis(
Gnosis.AMB_EXECUTOR,
encodePayloadQueue(GNOSIS_PAYLOAD),
4_000_000
);
}

}
90 changes: 90 additions & 0 deletions src/proposals/20240627/SparkEthereum_20240627.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.10;

import '../../SparkTestBase.sol';

import { IL2BridgeExecutor } from 'spark-gov-relay/interfaces/IL2BridgeExecutor.sol';

import { Domain, GnosisDomain } from 'xchain-helpers/testing/GnosisDomain.sol';

import { SparkGnosis_20240627 } from './SparkGnosis_20240627.sol';

contract SparkEthereum_20240627Test is SparkEthereumTestBase {

address public constant USDCE = 0x2a22f9c3b484c3629090FeED35F17Ff8F88f76F0;

Domain mainnet;
GnosisDomain gnosis;

constructor() {
id = '20240627';
}

function setUp() public {
mainnet = new Domain(getChain('mainnet'));
gnosis = new GnosisDomain(getChain('gnosis_chain'), mainnet);

mainnet.rollFork(20134486); // June 20, 2024
gnosis.rollFork(34563897); // June 20, 2024

payload = 0xc96420Dbe9568e2a65DD57daAD069FDEd37265fa;

loadPoolContext(poolAddressesProviderRegistry.getAddressesProvidersList()[0]);
}

function testGnosisSpellExecution() public {
executePayload(payload);

gnosis.selectFork();

assertEq(IL2BridgeExecutor(Gnosis.AMB_EXECUTOR).getActionsSetCount(), 5);

gnosis.relayFromHost(true);
skip(2 days);

assertEq(IL2BridgeExecutor(Gnosis.AMB_EXECUTOR).getActionsSetCount(), 6);

assertEq(createConfigurationSnapshot('', IPool(Gnosis.POOL)).length, 8);

IL2BridgeExecutor(Gnosis.AMB_EXECUTOR).execute(5);

assertEq(createConfigurationSnapshot('', IPool(Gnosis.POOL)).length, 9);
}

function testMorphoSupplyCapUpdates() public {
MarketParams memory susde1 = MarketParams({
loanToken: Ethereum.DAI,
collateralToken: Ethereum.SUSDE,
oracle: Ethereum.MORPHO_SUSDE_ORACLE,
irm: Ethereum.MORPHO_DEFAULT_IRM,
lltv: 0.86e18
});
MarketParams memory susde2 = MarketParams({
loanToken: Ethereum.DAI,
collateralToken: Ethereum.SUSDE,
oracle: Ethereum.MORPHO_SUSDE_ORACLE,
irm: Ethereum.MORPHO_DEFAULT_IRM,
lltv: 0.915e18
});

_assertMorphoCap(susde1, 400_000_000e18);
_assertMorphoCap(susde2, 100_000_000e18);

executePayload(payload);

_assertMorphoCap(susde1, 400_000_000e18, 500_000_000e18);
_assertMorphoCap(susde2, 100_000_000e18, 200_000_000e18);

assertEq(IMetaMorpho(Ethereum.MORPHO_VAULT_DAI_1).timelock(), 1 days);

skip(1 days);

// These are permissionless (call coming from the test contract)
IMetaMorpho(Ethereum.MORPHO_VAULT_DAI_1).acceptCap(susde1);
IMetaMorpho(Ethereum.MORPHO_VAULT_DAI_1).acceptCap(susde2);

_assertMorphoCap(susde1, 500_000_000e18);
_assertMorphoCap(susde2, 200_000_000e18);
}

}
Loading

0 comments on commit 840bcf3

Please sign in to comment.