diff --git a/.assets/03014f6d7e563bf95e08a254d6612c2a1779bb37.svg b/.assets/03014f6d7e563bf95e08a254d6612c2a1779bb37.svg new file mode 100644 index 00000000..6fedc0f3 --- /dev/null +++ b/.assets/03014f6d7e563bf95e08a254d6612c2a1779bb37.svg @@ -0,0 +1 @@ + Borrow APR, variableBorrow APR, stable0%25%50%75%100%0%20%40%60%80%uOptimal 90%uOptimal 90% \ No newline at end of file diff --git a/.assets/120b3346a2da5456c1e5eeb7876b093ce7188e35.svg b/.assets/120b3346a2da5456c1e5eeb7876b093ce7188e35.svg new file mode 100644 index 00000000..6fedc0f3 --- /dev/null +++ b/.assets/120b3346a2da5456c1e5eeb7876b093ce7188e35.svg @@ -0,0 +1 @@ + Borrow APR, variableBorrow APR, stable0%25%50%75%100%0%20%40%60%80%uOptimal 90%uOptimal 90% \ No newline at end of file diff --git a/.assets/249bca66f2e25caf04da3e3bc7e387fbf24599b2.svg b/.assets/249bca66f2e25caf04da3e3bc7e387fbf24599b2.svg new file mode 100644 index 00000000..a1ee31b7 --- /dev/null +++ b/.assets/249bca66f2e25caf04da3e3bc7e387fbf24599b2.svg @@ -0,0 +1 @@ + Borrow APR, variableBorrow APR, stable0%25%50%75%100%0%50%100%uOptimal 90%uOptimal 90% \ No newline at end of file diff --git a/.assets/973f0be01f7b244858ae3b53b46574f4a94ae9e0.svg b/.assets/973f0be01f7b244858ae3b53b46574f4a94ae9e0.svg new file mode 100644 index 00000000..988e727a --- /dev/null +++ b/.assets/973f0be01f7b244858ae3b53b46574f4a94ae9e0.svg @@ -0,0 +1 @@ + Borrow APR, variableBorrow APR, stable0%25%50%75%100%0%50%100%uOptimal 80%uOptimal 80% \ No newline at end of file diff --git a/.assets/c0ca34be405c22dc36ffd20c54b1dc8cf5ac741b.svg b/.assets/c0ca34be405c22dc36ffd20c54b1dc8cf5ac741b.svg new file mode 100644 index 00000000..7a0ae68d --- /dev/null +++ b/.assets/c0ca34be405c22dc36ffd20c54b1dc8cf5ac741b.svg @@ -0,0 +1 @@ + Borrow APR, variableBorrow APR, stable0%25%50%75%100%0%100%200%300%uOptimal 45%uOptimal 45% \ No newline at end of file diff --git a/.assets/e8496300bba6992ef8245dbc827b240692a59ca4.svg b/.assets/e8496300bba6992ef8245dbc827b240692a59ca4.svg new file mode 100644 index 00000000..588e84c0 --- /dev/null +++ b/.assets/e8496300bba6992ef8245dbc827b240692a59ca4.svg @@ -0,0 +1 @@ + Borrow APR, variableBorrow APR, stable0%25%50%75%100%0%50%100%uOptimal 90%uOptimal 90% \ No newline at end of file diff --git a/diffs/adi_test_adi_diffs_before_adi_test_adi_diffs_after.md b/diffs/adi_test_adi_diffs_before_adi_test_adi_diffs_after.md new file mode 100644 index 00000000..63700142 --- /dev/null +++ b/diffs/adi_test_adi_diffs_before_adi_test_adi_diffs_after.md @@ -0,0 +1,30 @@ +## Raw diff + +```json +{ + "forwarderAdaptersByChain": { + "1": { + "0xDA4B6024aA06f7565BBcAaD9B8bE24C3c229AAb5": { + "from": "0x2a323be63e08E08536Fc3b5d8C6f24825e68895e", + "to": null + }, + "0x7FAE7765abB4c8f778d57337bB720d0BC53057e3": { + "from": null, + "to": "0x8410d9BD353b420ebA8C48ff1B0518426C280FCC" + } + } + }, + "receiverAdaptersByChain": { + "1": { + "0xDA4B6024aA06f7565BBcAaD9B8bE24C3c229AAb5": { + "from": true, + "to": null + }, + "0x7FAE7765abB4c8f778d57337bB720d0BC53057e3": { + "from": null, + "to": true + } + } + } +} +``` \ No newline at end of file diff --git a/diffs/preTestEngineListing_postTestEngineListing.md b/diffs/preTestEngineListing_postTestEngineListing.md index 28f2d679..f6a8df89 100644 --- a/diffs/preTestEngineListing_postTestEngineListing.md +++ b/diffs/preTestEngineListing_postTestEngineListing.md @@ -18,18 +18,18 @@ | oracle | [0x443C5116CdF663Eb387e72C688D276e702135C87](https://polygonscan.com/address/0x443C5116CdF663Eb387e72C688D276e702135C87) | | oracleDecimals | 8 | | oracleDescription | 1INCH / USD | -| oracleLatestAnswer | 0.36313594 | +| oracleLatestAnswer | 0.54658874 | | usageAsCollateralEnabled | true | | ltv | 82.5 % | | liquidationThreshold | 86 % | | liquidationBonus | 5 % | | liquidationProtocolFee | 10 % | | reserveFactor | 10 % | -| aToken | [0xA4D94019934D8333Ef880ABFFbF2FDd611C762BD](https://polygonscan.com/address/0xA4D94019934D8333Ef880ABFFbF2FDd611C762BD) | +| aToken | [0xF107d93D67A487B5586AEB8B840755c36fB77b72](https://polygonscan.com/address/0xF107d93D67A487B5586AEB8B840755c36fB77b72) | | aTokenImpl | [0xCf85FF1c37c594a10195F7A9Ab85CBb0a03f69dE](https://polygonscan.com/address/0xCf85FF1c37c594a10195F7A9Ab85CBb0a03f69dE) | -| variableDebtToken | [0xE701126012EC0290822eEA17B794454d1AF8b030](https://polygonscan.com/address/0xE701126012EC0290822eEA17B794454d1AF8b030) | +| variableDebtToken | [0xb2122f310A5cB1d7f46d0FDb2fC5d36392Aaac96](https://polygonscan.com/address/0xb2122f310A5cB1d7f46d0FDb2fC5d36392Aaac96) | | variableDebtTokenImpl | [0x79b5e91037AE441dE0d9e6fd3Fd85b96B83d4E93](https://polygonscan.com/address/0x79b5e91037AE441dE0d9e6fd3Fd85b96B83d4E93) | -| stableDebtToken | [0xc889e9f8370D14A428a9857205d99BFdB400b757](https://polygonscan.com/address/0xc889e9f8370D14A428a9857205d99BFdB400b757) | +| stableDebtToken | [0x106Daa74Bd93f436D455953966ba6b70EBdCbFdb](https://polygonscan.com/address/0x106Daa74Bd93f436D455953966ba6b70EBdCbFdb) | | stableDebtTokenImpl | [0xF4294973B7E6F6C411dD8A388592E7c7D32F2486](https://polygonscan.com/address/0xF4294973B7E6F6C411dD8A388592E7c7D32F2486) | | borrowingEnabled | true | | stableBorrowRateEnabled | false | @@ -72,7 +72,7 @@ "0x9c2C5fd7b07E95EE044DDeba0E97a665F142394f": { "from": null, "to": { - "aToken": "0xA4D94019934D8333Ef880ABFFbF2FDd611C762BD", + "aToken": "0xF107d93D67A487B5586AEB8B840755c36fB77b72", "aTokenImpl": "0xCf85FF1c37c594a10195F7A9Ab85CBb0a03f69dE", "aTokenName": "Aave Polygon 1INCH", "aTokenSymbol": "aPol1INCH", @@ -98,10 +98,10 @@ "oracle": "0x443C5116CdF663Eb387e72C688D276e702135C87", "oracleDecimals": 8, "oracleDescription": "1INCH / USD", - "oracleLatestAnswer": 36313594, + "oracleLatestAnswer": 54658874, "reserveFactor": 1000, "stableBorrowRateEnabled": false, - "stableDebtToken": "0xc889e9f8370D14A428a9857205d99BFdB400b757", + "stableDebtToken": "0x106Daa74Bd93f436D455953966ba6b70EBdCbFdb", "stableDebtTokenImpl": "0xF4294973B7E6F6C411dD8A388592E7c7D32F2486", "stableDebtTokenName": "Aave Polygon Stable Debt 1INCH", "stableDebtTokenSymbol": "stableDebtPol1INCH", @@ -110,7 +110,7 @@ "underlying": "0x9c2C5fd7b07E95EE044DDeba0E97a665F142394f", "usageAsCollateralEnabled": true, "variableBorrowIndex": "1000000000000000000000000000", - "variableDebtToken": "0xE701126012EC0290822eEA17B794454d1AF8b030", + "variableDebtToken": "0xb2122f310A5cB1d7f46d0FDb2fC5d36392Aaac96", "variableDebtTokenImpl": "0x79b5e91037AE441dE0d9e6fd3Fd85b96B83d4E93", "variableDebtTokenName": "Aave Polygon Variable Debt 1INCH", "variableDebtTokenSymbol": "variableDebtPol1INCH" diff --git a/diffs/preTestEnginePolV3_postTestEnginePolV3.md b/diffs/preTestEnginePolV3_postTestEnginePolV3.md index ed36389e..7a15ebc8 100644 --- a/diffs/preTestEnginePolV3_postTestEnginePolV3.md +++ b/diffs/preTestEnginePolV3_postTestEnginePolV3.md @@ -1,6 +1,6 @@ ## Reserve changes -### Reserves altered +### Reserve altered #### WETH ([0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619](https://polygonscan.com/address/0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619)) @@ -11,6 +11,26 @@ | baseStableBorrowRate | 6.3 % | 6.8 % | | interestRate | ![before](/.assets/bc821e780dbf0cd88aa89ae21f339014e1053ceb.svg) | ![after](/.assets/e7c3905f5d41473b5148fbd1df41bdc06ae104fb.svg) | +#### miMATIC ([0xa3Fa99A148fA48D14Ed51d610c367C61876997F1](https://polygonscan.com/address/0xa3Fa99A148fA48D14Ed51d610c367C61876997F1)) + +| description | value before | value after | +| --- | --- | --- | +| reserveFactor | 95 % | 20 % | +| interestRateStrategy | [0x44CaDF6E49895640D9De85ac01d97D44429Ad0A4](https://polygonscan.com/address/0x44CaDF6E49895640D9De85ac01d97D44429Ad0A4) | [0x8F183Ee74C790CB558232a141099b316D6C8Ba6E](https://polygonscan.com/address/0x8F183Ee74C790CB558232a141099b316D6C8Ba6E) | +| optimalUsageRatio | 45 % | 80 % | +| variableRateSlope2 | 300 % | 75 % | +| maxExcessUsageRatio | 55 % | 20 % | +| interestRate | ![before](/.assets/c0ca34be405c22dc36ffd20c54b1dc8cf5ac741b.svg) | ![after](/.assets/8f84201aa8a64dd4068a65bba6c43cc7622ae5b8.svg) | + +#### USDT ([0xc2132D05D31c914a87C6611C10748AEb04B58e8F](https://polygonscan.com/address/0xc2132D05D31c914a87C6611C10748AEb04B58e8F)) + +| description | value before | value after | +| --- | --- | --- | +| interestRateStrategy | [0x2402C25e7E45b1466c53Ef7766AAd878A4CbC237](https://polygonscan.com/address/0x2402C25e7E45b1466c53Ef7766AAd878A4CbC237) | [0xC0B875907514131C2Fd43f0FBf59EdaB84C7e260](https://polygonscan.com/address/0xC0B875907514131C2Fd43f0FBf59EdaB84C7e260) | +| optimalUsageRatio | 90 % | 80 % | +| maxExcessUsageRatio | 10 % | 20 % | +| interestRate | ![before](/.assets/249bca66f2e25caf04da3e3bc7e387fbf24599b2.svg) | ![after](/.assets/973f0be01f7b244858ae3b53b46574f4a94ae9e0.svg) | + ## Raw diff ```json @@ -21,6 +41,22 @@ "from": "0xf6733B9842883BFE0e0a940eA2F572676af31bde", "to": "0x27eFE5db315b71753b2a38ED3d5dd7E9362ba93F" } + }, + "0xa3Fa99A148fA48D14Ed51d610c367C61876997F1": { + "interestRateStrategy": { + "from": "0x44CaDF6E49895640D9De85ac01d97D44429Ad0A4", + "to": "0x8F183Ee74C790CB558232a141099b316D6C8Ba6E" + }, + "reserveFactor": { + "from": 9500, + "to": 2000 + } + }, + "0xc2132D05D31c914a87C6611C10748AEb04B58e8F": { + "interestRateStrategy": { + "from": "0x2402C25e7E45b1466c53Ef7766AAd878A4CbC237", + "to": "0xC0B875907514131C2Fd43f0FBf59EdaB84C7e260" + } } }, "strategies": { @@ -37,6 +73,38 @@ "from": "33000000000000000000000000", "to": "38000000000000000000000000" } + }, + "0xa3Fa99A148fA48D14Ed51d610c367C61876997F1": { + "address": { + "from": "0x44CaDF6E49895640D9De85ac01d97D44429Ad0A4", + "to": "0x8F183Ee74C790CB558232a141099b316D6C8Ba6E" + }, + "maxExcessUsageRatio": { + "from": "550000000000000000000000000", + "to": "200000000000000000000000000" + }, + "optimalUsageRatio": { + "from": "450000000000000000000000000", + "to": "800000000000000000000000000" + }, + "variableRateSlope2": { + "from": "3000000000000000000000000000", + "to": "750000000000000000000000000" + } + }, + "0xc2132D05D31c914a87C6611C10748AEb04B58e8F": { + "address": { + "from": "0x2402C25e7E45b1466c53Ef7766AAd878A4CbC237", + "to": "0xC0B875907514131C2Fd43f0FBf59EdaB84C7e260" + }, + "maxExcessUsageRatio": { + "from": "100000000000000000000000000", + "to": "200000000000000000000000000" + }, + "optimalUsageRatio": { + "from": "900000000000000000000000000", + "to": "800000000000000000000000000" + } } } } diff --git a/diffs/preTestEnginePriceFeed_postTestEnginePriceFeed.md b/diffs/preTestEnginePriceFeed_postTestEnginePriceFeed.md index 01f67875..3173db2b 100644 --- a/diffs/preTestEnginePriceFeed_postTestEnginePriceFeed.md +++ b/diffs/preTestEnginePriceFeed_postTestEnginePriceFeed.md @@ -6,9 +6,9 @@ | description | value before | value after | | --- | --- | --- | -| oracle | [0x72484B12719E23115761D5DA1646945632979bB6](https://polygonscan.com/address/0x72484B12719E23115761D5DA1646945632979bB6) | [0xfE4A8cc5b5B2366C1B58Bea3858e81843581b2F7](https://polygonscan.com/address/0xfE4A8cc5b5B2366C1B58Bea3858e81843581b2F7) | -| oracleDescription | AAVE / USD | USDC / USD | -| oracleLatestAnswer | 92.5207 | 1.0001 | +| oracle | [0x72484B12719E23115761D5DA1646945632979bB6](https://polygonscan.com/address/0x72484B12719E23115761D5DA1646945632979bB6) | [0x17E33D122FC34c7ad8FBd4a1995Dff9c8aE675eb](https://polygonscan.com/address/0x17E33D122FC34c7ad8FBd4a1995Dff9c8aE675eb) | +| oracleDescription | AAVE / USD | Capped USDC/USD | +| oracleLatestAnswer | 113.57664588 | 0.99991524 | ## Raw diff @@ -19,15 +19,15 @@ "0xD6DF932A45C0f255f85145f286eA0b292B21C90B": { "oracle": { "from": "0x72484B12719E23115761D5DA1646945632979bB6", - "to": "0xfE4A8cc5b5B2366C1B58Bea3858e81843581b2F7" + "to": "0x17E33D122FC34c7ad8FBd4a1995Dff9c8aE675eb" }, "oracleDescription": { "from": "AAVE / USD", - "to": "USDC / USD" + "to": "Capped USDC/USD" }, "oracleLatestAnswer": { - "from": 9252070000, - "to": 100010000 + "from": 11357664588, + "to": 99991524 } } } diff --git a/src/adi/BaseAdaptersUpdate.sol b/src/adi/BaseAdaptersUpdate.sol new file mode 100644 index 00000000..cb137abc --- /dev/null +++ b/src/adi/BaseAdaptersUpdate.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IProposalGenericExecutor} from '../interfaces/IProposalGenericExecutor.sol'; +import './BaseReceiverAdaptersUpdate.sol'; +import './BaseForwarderAdaptersUpdate.sol'; + +/** + * @title Base payload aDI and bridge adapters update + * @author BGD Labs @bgdlabs + */ +abstract contract BaseAdaptersUpdate is + BaseReceiverAdaptersUpdate, + BaseForwarderAdaptersUpdate, + IProposalGenericExecutor +{ + address public immutable CROSS_CHAIN_CONTROLLER; + + /** + * @param crossChainController address of the CCC of the network where payload will be deployed + */ + constructor(address crossChainController) { + CROSS_CHAIN_CONTROLLER = crossChainController; + } + + function execute() public override { + executeReceiversUpdate(CROSS_CHAIN_CONTROLLER); + + executeForwardersUpdate(CROSS_CHAIN_CONTROLLER); + } +} diff --git a/src/adi/BaseForwarderAdaptersUpdate.sol b/src/adi/BaseForwarderAdaptersUpdate.sol new file mode 100644 index 00000000..a28b12de --- /dev/null +++ b/src/adi/BaseForwarderAdaptersUpdate.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IBaseForwarderAdaptersUpdate, ICrossChainForwarder} from './interfaces/IBaseForwarderAdaptersUpdate.sol'; + +/** + * @title Base forwarder payload. It has the methods to update the forwarder bridge adapters. + * @author BGD Labs @bgdlabs + */ +abstract contract BaseForwarderAdaptersUpdate is IBaseForwarderAdaptersUpdate { + /// @inheritdoc IBaseForwarderAdaptersUpdate + function getForwarderBridgeAdaptersToRemove() + public + view + virtual + returns (ICrossChainForwarder.BridgeAdapterToDisable[] memory) + { + return new ICrossChainForwarder.BridgeAdapterToDisable[](0); + } + + /// @inheritdoc IBaseForwarderAdaptersUpdate + function getForwarderBridgeAdaptersToEnable() + public + view + virtual + returns (ICrossChainForwarder.ForwarderBridgeAdapterConfigInput[] memory) + { + return new ICrossChainForwarder.ForwarderBridgeAdapterConfigInput[](0); + } + + /// @inheritdoc IBaseForwarderAdaptersUpdate + function executeForwardersUpdate(address crossChainController) public virtual { + // remove forwarding adapters + ICrossChainForwarder.BridgeAdapterToDisable[] + memory forwardersToRemove = getForwarderBridgeAdaptersToRemove(); + if (forwardersToRemove.length != 0) { + ICrossChainForwarder(crossChainController).disableBridgeAdapters(forwardersToRemove); + } + + // add forwarding adapters + ICrossChainForwarder.ForwarderBridgeAdapterConfigInput[] + memory forwardersToEnable = getForwarderBridgeAdaptersToEnable(); + if (forwardersToEnable.length != 0) { + ICrossChainForwarder(crossChainController).enableBridgeAdapters(forwardersToEnable); + } + } +} diff --git a/src/adi/BaseReceiverAdaptersUpdate.sol b/src/adi/BaseReceiverAdaptersUpdate.sol new file mode 100644 index 00000000..1b5ee264 --- /dev/null +++ b/src/adi/BaseReceiverAdaptersUpdate.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IBaseReceiverAdaptersUpdate, ICrossChainReceiver} from './interfaces/IBaseReceiverAdaptersUpdate.sol'; + +/** + * @title Base receiver payload. It has the methods to update the receiver bridge adapters. + * @author BGD Labs @bgdlabs + */ +abstract contract BaseReceiverAdaptersUpdate is IBaseReceiverAdaptersUpdate { + /// @inheritdoc IBaseReceiverAdaptersUpdate + function getReceiverBridgeAdaptersToRemove() + public + view + virtual + returns (ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[] memory) + { + // remove old Receiver bridge adapter + return new ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[](0); + } + + /// @inheritdoc IBaseReceiverAdaptersUpdate + function getReceiverBridgeAdaptersToAllow() + public + view + virtual + returns (ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[] memory) + { + return new ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[](0); + } + + /// @inheritdoc IBaseReceiverAdaptersUpdate + function executeReceiversUpdate(address crossChainController) public virtual { + // remove old Receiver bridge adapter + ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[] + memory receiversToRemove = getReceiverBridgeAdaptersToRemove(); + if (receiversToRemove.length != 0) { + ICrossChainReceiver(crossChainController).disallowReceiverBridgeAdapters(receiversToRemove); + } + + // add receiver adapters + ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[] + memory receiversToAllow = getReceiverBridgeAdaptersToAllow(); + if (receiversToAllow.length != 0) { + ICrossChainReceiver(crossChainController).allowReceiverBridgeAdapters(receiversToAllow); + } + } +} diff --git a/src/adi/SimpleOneToManyAdapterUpdate.sol b/src/adi/SimpleOneToManyAdapterUpdate.sol new file mode 100644 index 00000000..c4cb3c78 --- /dev/null +++ b/src/adi/SimpleOneToManyAdapterUpdate.sol @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import './BaseAdaptersUpdate.sol'; + +/** + * @title Base payload aDI and bridge adapters update + * @author BGD Labs @bgdlabs + * @dev This payload should be used when wanting to substitute an adapter that receives and also forwards + */ +abstract contract SimpleOneToManyAdapterUpdate is BaseAdaptersUpdate { + struct ConstructorInput { + address ccc; + address adapterToRemove; + address newAdapter; + } + + struct DestinationAdaptersInput { + address adapter; + uint256 chainId; + } + + address public immutable ADAPTER_TO_REMOVE; + address public immutable NEW_ADAPTER; + + constructor(ConstructorInput memory constructorInput) BaseAdaptersUpdate(constructorInput.ccc) { + ADAPTER_TO_REMOVE = constructorInput.adapterToRemove; + NEW_ADAPTER = constructorInput.newAdapter; + } + + /** + * @notice method used to get the adapters for the destination chain ids + * @return array of adapter - destination chain pairs + */ + function getDestinationAdapters() + public + pure + virtual + returns (DestinationAdaptersInput[] memory) + { + return new DestinationAdaptersInput[](0); + } + + /** + * @notice method to get the chains that a new adapter will receive messages from + * @return an array of chain ids + */ + function getChainsToReceive() public pure virtual returns (uint256[] memory); + + /** + * @notice method to get a list of chain ids that the new adapter will use to send messages to + * @return an array of chain ids + */ + function getChainsToSend() public pure virtual returns (uint256[] memory) { + DestinationAdaptersInput[] memory destinationAdapters = getDestinationAdapters(); + uint256[] memory chainsToSend = new uint256[](destinationAdapters.length); + for (uint256 i = 0; i < destinationAdapters.length; i++) { + chainsToSend[i] = destinationAdapters[i].chainId; + } + return chainsToSend; + } + + /// @inheritdoc IBaseReceiverAdaptersUpdate + function getReceiverBridgeAdaptersToRemove() + public + view + virtual + override + returns (ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[] memory) + { + // remove old Receiver bridge adapter + ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[] + memory bridgeAdaptersToRemove = new ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[](1); + + bridgeAdaptersToRemove[0] = ICrossChainReceiver.ReceiverBridgeAdapterConfigInput({ + bridgeAdapter: ADAPTER_TO_REMOVE, + chainIds: getChainsToReceive() + }); + + return bridgeAdaptersToRemove; + } + + /// @inheritdoc IBaseForwarderAdaptersUpdate + function getForwarderBridgeAdaptersToRemove() + public + view + virtual + override + returns (ICrossChainForwarder.BridgeAdapterToDisable[] memory) + { + ICrossChainForwarder.BridgeAdapterToDisable[] + memory forwarderAdaptersToRemove = new ICrossChainForwarder.BridgeAdapterToDisable[](1); + + forwarderAdaptersToRemove[0] = ICrossChainForwarder.BridgeAdapterToDisable({ + bridgeAdapter: ADAPTER_TO_REMOVE, + chainIds: getChainsToSend() + }); + + return forwarderAdaptersToRemove; + } + + /// @inheritdoc IBaseReceiverAdaptersUpdate + function getReceiverBridgeAdaptersToAllow() + public + view + virtual + override + returns (ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[] memory) + { + ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[] + memory bridgeAdapterConfig = new ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[](1); + + bridgeAdapterConfig[0] = ICrossChainReceiver.ReceiverBridgeAdapterConfigInput({ + bridgeAdapter: NEW_ADAPTER, + chainIds: getChainsToReceive() + }); + + return bridgeAdapterConfig; + } + + /// @inheritdoc IBaseForwarderAdaptersUpdate + function getForwarderBridgeAdaptersToEnable() + public + view + virtual + override + returns (ICrossChainForwarder.ForwarderBridgeAdapterConfigInput[] memory) + { + DestinationAdaptersInput[] memory destinationAdapters = getDestinationAdapters(); + + ICrossChainForwarder.ForwarderBridgeAdapterConfigInput[] + memory bridgeAdaptersToEnable = new ICrossChainForwarder.ForwarderBridgeAdapterConfigInput[]( + destinationAdapters.length + ); + + for (uint256 i = 0; i < destinationAdapters.length; i++) { + bridgeAdaptersToEnable[i] = ICrossChainForwarder.ForwarderBridgeAdapterConfigInput({ + currentChainBridgeAdapter: NEW_ADAPTER, + destinationBridgeAdapter: destinationAdapters[i].adapter, + destinationChainId: destinationAdapters[i].chainId + }); + } + + return bridgeAdaptersToEnable; + } +} diff --git a/src/adi/SimpleReceiverAdapterUpdate.sol b/src/adi/SimpleReceiverAdapterUpdate.sol new file mode 100644 index 00000000..f8762498 --- /dev/null +++ b/src/adi/SimpleReceiverAdapterUpdate.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import './BaseAdaptersUpdate.sol'; + +/** + * @title Base payload aDI and bridge adapters update + * @author BGD Labs @bgdlabs + * @dev This payload should be used when wanting to add or remove (or both) a receiver adapter. If one of the + addresses is left as 0, the addition or removal will not be done + */ +abstract contract SimpleReceiverAdapterUpdate is BaseAdaptersUpdate { + struct ConstructorInput { + address ccc; + address adapterToRemove; + address newAdapter; + } + + struct DestinationAdaptersInput { + address adapter; + uint256 chainId; + } + + address public immutable ADAPTER_TO_REMOVE; + address public immutable NEW_ADAPTER; + + constructor(ConstructorInput memory constructorInput) BaseAdaptersUpdate(constructorInput.ccc) { + ADAPTER_TO_REMOVE = constructorInput.adapterToRemove; + NEW_ADAPTER = constructorInput.newAdapter; + } + + /** + * @notice method to get the chains that a new adapter will receive messages from + * @return an array of chain ids + */ + function getChainsToReceive() public pure virtual returns (uint256[] memory); + + /// @inheritdoc IBaseReceiverAdaptersUpdate + function getReceiverBridgeAdaptersToRemove() + public + view + virtual + override + returns (ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[] memory) + { + if (ADAPTER_TO_REMOVE != address(0)) { + // remove old Receiver bridge adapter + ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[] + memory bridgeAdaptersToRemove = new ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[]( + 1 + ); + + bridgeAdaptersToRemove[0] = ICrossChainReceiver.ReceiverBridgeAdapterConfigInput({ + bridgeAdapter: ADAPTER_TO_REMOVE, + chainIds: getChainsToReceive() + }); + + return bridgeAdaptersToRemove; + } else { + return new ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[](0); + } + } + + /// @inheritdoc IBaseReceiverAdaptersUpdate + function getReceiverBridgeAdaptersToAllow() + public + view + virtual + override + returns (ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[] memory) + { + if (NEW_ADAPTER != address(0)) { + ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[] + memory bridgeAdapterConfig = new ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[](1); + + bridgeAdapterConfig[0] = ICrossChainReceiver.ReceiverBridgeAdapterConfigInput({ + bridgeAdapter: NEW_ADAPTER, + chainIds: getChainsToReceive() + }); + + return bridgeAdapterConfig; + } else { + return new ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[](0); + } + } +} diff --git a/src/adi/interfaces/IBaseAdaptersUpdate.sol b/src/adi/interfaces/IBaseAdaptersUpdate.sol new file mode 100644 index 00000000..1074ba26 --- /dev/null +++ b/src/adi/interfaces/IBaseAdaptersUpdate.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IBaseReceiverAdaptersUpdate} from './IBaseReceiverAdaptersUpdate.sol'; +import {IBaseForwarderAdaptersUpdate} from './IBaseForwarderAdaptersUpdate.sol'; + +/** + * @title Interface of the base payload aDI and bridge adapters update + * @author BGD Labs @bgdlabs + */ +interface IBaseAdaptersUpdate is IBaseReceiverAdaptersUpdate, IBaseForwarderAdaptersUpdate { + +} diff --git a/src/adi/interfaces/IBaseForwarderAdaptersUpdate.sol b/src/adi/interfaces/IBaseForwarderAdaptersUpdate.sol new file mode 100644 index 00000000..615a1b27 --- /dev/null +++ b/src/adi/interfaces/IBaseForwarderAdaptersUpdate.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {ICrossChainForwarder} from 'aave-address-book/common/ICrossChainController.sol'; + +/** + * @title Interface for base forwarder payload. + * @author BGD Labs @bgdlabs + */ +interface IBaseForwarderAdaptersUpdate { + /** + * @notice method to get the forwarder adapters to remove + * @return object array with the adapter to remove and an array of chain ids to remove it from + */ + function getForwarderBridgeAdaptersToRemove() + external + view + returns (ICrossChainForwarder.BridgeAdapterToDisable[] memory); + + /** + * @notice method to get the forwarder adapters to enable + * @return object array with the current and destination pair of adapters to enable and the chainId + to communicate with + */ + function getForwarderBridgeAdaptersToEnable() + external + view + returns (ICrossChainForwarder.ForwarderBridgeAdapterConfigInput[] memory); + + /** + * @notice method to add and remove forwarder adapters + * @param crossChainController address of the CCC on the networks where the adapters are going to be updated + */ + function executeForwardersUpdate(address crossChainController) external; +} diff --git a/src/adi/interfaces/IBaseReceiverAdaptersUpdate.sol b/src/adi/interfaces/IBaseReceiverAdaptersUpdate.sol new file mode 100644 index 00000000..77cccb1b --- /dev/null +++ b/src/adi/interfaces/IBaseReceiverAdaptersUpdate.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {ICrossChainReceiver} from 'aave-address-book/common/ICrossChainController.sol'; + +/** + * @title Interface of the base payload aDI and bridge adapters update + * @author BGD Labs @bgdlabs + */ +interface IBaseReceiverAdaptersUpdate { + /** + * @notice method to get the receiver adapters to remove + * @return object array with the adapter to remove and an array of chain ids to remove it from + */ + function getReceiverBridgeAdaptersToRemove() + external + view + returns (ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[] memory); + + /** + * @notice method to get the receiver adapters to allow + * @return object array with the adapter to allow and an array of chain ids to allow it to receive messages from + */ + function getReceiverBridgeAdaptersToAllow() + external + view + returns (ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[] memory); + + /** + * @notice method to add and remove receiver adapters + * @param crossChainController address of the CCC on the networks where the adapters are going to be updated + */ + function executeReceiversUpdate(address crossChainController) external; +} diff --git a/src/adi/test/ADITestBase.sol b/src/adi/test/ADITestBase.sol new file mode 100644 index 00000000..cbc901dc --- /dev/null +++ b/src/adi/test/ADITestBase.sol @@ -0,0 +1,649 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.0; + +import 'forge-std/StdJson.sol'; +import 'forge-std/Test.sol'; +import {ICrossChainReceiver, ICrossChainForwarder} from 'aave-address-book/common/ICrossChainController.sol'; +import {ChainIds, ChainHelpers} from '../../ChainIds.sol'; +import {GovV3Helpers} from '../../GovV3Helpers.sol'; +import {IBaseAdaptersUpdate} from '../interfaces/IBaseAdaptersUpdate.sol'; + +import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol'; +import {GovernanceV3Polygon} from 'aave-address-book/GovernanceV3Polygon.sol'; +import {GovernanceV3Avalanche} from 'aave-address-book/GovernanceV3Avalanche.sol'; +import {GovernanceV3Optimism} from 'aave-address-book/GovernanceV3Optimism.sol'; +import {GovernanceV3BNB} from 'aave-address-book/GovernanceV3BNB.sol'; +import {GovernanceV3Metis} from 'aave-address-book/GovernanceV3Metis.sol'; +import {GovernanceV3Base} from 'aave-address-book/GovernanceV3Base.sol'; +import {GovernanceV3Arbitrum} from 'aave-address-book/GovernanceV3Arbitrum.sol'; +import {GovernanceV3Gnosis} from 'aave-address-book/GovernanceV3Gnosis.sol'; +import {GovernanceV3Scroll} from 'aave-address-book/GovernanceV3Scroll.sol'; +import {IBaseAdapter} from 'aave-address-book/common/IBaseAdapter.sol'; + +contract ADITestBase is Test { + using stdJson for string; + + struct ReceiverConfigByChain { + uint8 requiredConfirmations; + uint256 chainId; + uint256 validityTimestamp; + } + + struct ReceiverAdaptersByChain { + uint256 chainId; + address[] receiverAdapters; + } + + struct ForwarderAdaptersByChain { + uint256 chainId; + ICrossChainForwarder.ChainIdBridgeConfig[] forwarders; + } + + struct CCCConfig { + ReceiverConfigByChain[] receiverConfigs; + ReceiverAdaptersByChain[] receiverAdaptersConfig; + ForwarderAdaptersByChain[] forwarderAdaptersConfig; + } + + struct ForwarderAdapters { + ICrossChainForwarder.ChainIdBridgeConfig[] adapters; + uint256 chainId; + } + + struct AdaptersByChain { + address[] adapters; + uint256 chainId; + } + + struct DestinationPayload { + uint256 chainId; + bytes payloadCode; + } + + function executePayload(Vm vm, address payload) internal { + GovV3Helpers.executePayload(vm, payload); + } + + /** + * @dev generates the diff between two reports + */ + function diffReports(string memory reportBefore, string memory reportAfter) internal { + string memory outPath = string( + abi.encodePacked('./diffs/', reportBefore, '_', reportAfter, '.md') + ); + string memory beforePath = string(abi.encodePacked('./reports/', reportBefore, '.json')); + string memory afterPath = string(abi.encodePacked('./reports/', reportAfter, '.json')); + + string[] memory inputs = new string[](7); + inputs[0] = 'npx'; + inputs[1] = '@bgd-labs/aave-cli@0.10.0'; + inputs[2] = 'adi-diff-snapshots'; + inputs[3] = beforePath; + inputs[4] = afterPath; + inputs[5] = '-o'; + inputs[6] = outPath; + vm.ffi(inputs); + } + + function defaultTest( + string memory reportName, + address crossChainController, + address payload, + bool runE2E + ) public returns (CCCConfig memory, CCCConfig memory) { + string memory beforeString = string(abi.encodePacked('adi_', reportName, '_before')); + CCCConfig memory configBefore = createConfigurationSnapshot(beforeString, crossChainController); + + uint256 snapshotId = vm.snapshot(); + + executePayload(vm, payload); + + string memory afterString = string(abi.encodePacked('adi_', reportName, '_after')); + CCCConfig memory configAfter = createConfigurationSnapshot(afterString, crossChainController); + + diffReports(beforeString, afterString); + + vm.revertTo(snapshotId); + if (runE2E) e2eTest(payload, crossChainController); + + return (configBefore, configAfter); + } + + function e2eTest(address payload, address crossChainController) public { + // test receivers + ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[] + memory receiversToAllow = IBaseAdaptersUpdate(payload).getReceiverBridgeAdaptersToAllow(); + if (receiversToAllow.length != 0) { + _testCorrectReceiverAdaptersConfiguration(payload, receiversToAllow, crossChainController); + _testCorrectTrustedRemotes(receiversToAllow); + } + ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[] + memory receiversToRemove = IBaseAdaptersUpdate(payload).getReceiverBridgeAdaptersToRemove(); + if (receiversToRemove.length != 0) { + _testOnlyRemovedSpecifiedReceiverAdapters(payload, receiversToRemove, crossChainController); + } + + // test forwarders + ICrossChainForwarder.ForwarderBridgeAdapterConfigInput[] + memory forwardersToEnable = IBaseAdaptersUpdate(payload).getForwarderBridgeAdaptersToEnable(); + if (forwardersToEnable.length != 0) { + _testCorrectForwarderAdaptersConfiguration(payload, crossChainController, forwardersToEnable); + _testDestinationAdapterIsRegistered(payload, crossChainController, forwardersToEnable); + } + ICrossChainForwarder.BridgeAdapterToDisable[] memory forwardersToRemove = IBaseAdaptersUpdate( + payload + ).getForwarderBridgeAdaptersToRemove(); + if (forwardersToRemove.length != 0) { + _testOnlyRemovedSpecificForwarderAdapters(payload, crossChainController, forwardersToRemove); + } + } + + function getDestinationPayloadsByChain() + public + view + virtual + returns (DestinationPayload[] memory) + { + return new DestinationPayload[](0); + } + + function _testDestinationAdapterIsRegistered( + address payload, + address crossChainController, + ICrossChainForwarder.ForwarderBridgeAdapterConfigInput[] memory forwardersToEnable + ) internal { + DestinationPayload[] memory destinationPayloads = getDestinationPayloadsByChain(); + bytes memory empty; + + for (uint256 i = 0; i < forwardersToEnable.length; i++) { + uint256 currentChainId = block.chainid; + // change fork to destination network + (uint256 previousFork, ) = ChainHelpers.selectChain( + vm, + forwardersToEnable[i].destinationChainId + ); + address destinationCCC = getCCCByChainId(block.chainid); + if (destinationPayloads.length > 0) { + for (uint256 j = 0; j < destinationPayloads.length; j++) { + if (destinationPayloads[j].chainId == forwardersToEnable[i].destinationChainId) { + if (keccak256(destinationPayloads[j].payloadCode) != keccak256(empty)) { + address destinationPayload = GovV3Helpers.deployDeterministic( + destinationPayloads[j].payloadCode + ); + + executePayload(vm, destinationPayload); + // check that adapter is registered + assertEq( + ICrossChainReceiver(destinationCCC).isReceiverBridgeAdapterAllowed( + forwardersToEnable[i].destinationBridgeAdapter, + currentChainId + ), + true + ); + break; + } + } + } + } else { + assertEq( + ICrossChainReceiver(destinationCCC).isReceiverBridgeAdapterAllowed( + forwardersToEnable[i].destinationBridgeAdapter, + currentChainId + ), + true + ); + } + vm.selectFork(previousFork); + } + } + + function _testOnlyRemovedSpecificForwarderAdapters( + address payload, + address crossChainController, + ICrossChainForwarder.BridgeAdapterToDisable[] memory adaptersToRemove + ) internal { + ForwarderAdapters[] + memory forwardersBridgeAdaptersByChainBefore = _getCurrentForwarderAdaptersByChain( + crossChainController, + block.chainid + ); + + executePayload(vm, payload); + + ForwarderAdapters[] + memory forwardersBridgeAdaptersByChainAfter = _getCurrentForwarderAdaptersByChain( + crossChainController, + block.chainid + ); + + for (uint256 l = 0; l < forwardersBridgeAdaptersByChainBefore.length; l++) { + for (uint256 j = 0; j < forwardersBridgeAdaptersByChainAfter.length; j++) { + if ( + forwardersBridgeAdaptersByChainBefore[l].chainId == + forwardersBridgeAdaptersByChainAfter[j].chainId + ) { + for (uint256 i = 0; i < forwardersBridgeAdaptersByChainBefore[l].adapters.length; i++) { + bool forwarderFound; + for (uint256 m = 0; m < forwardersBridgeAdaptersByChainAfter[j].adapters.length; m++) { + if ( + forwardersBridgeAdaptersByChainBefore[l].adapters[i].destinationBridgeAdapter == + forwardersBridgeAdaptersByChainAfter[j].adapters[m].destinationBridgeAdapter && + forwardersBridgeAdaptersByChainBefore[l].adapters[i].currentChainBridgeAdapter == + forwardersBridgeAdaptersByChainAfter[j].adapters[m].currentChainBridgeAdapter + ) { + forwarderFound = true; + break; + } + } + if (!forwarderFound) { + bool isAdapterToBeRemoved; + for (uint256 k = 0; k < adaptersToRemove.length; k++) { + if ( + forwardersBridgeAdaptersByChainBefore[l].adapters[i].currentChainBridgeAdapter == + adaptersToRemove[k].bridgeAdapter + ) { + for (uint256 n = 0; n < adaptersToRemove[k].chainIds.length; n++) { + if ( + forwardersBridgeAdaptersByChainBefore[l].chainId == + adaptersToRemove[k].chainIds[n] + ) { + isAdapterToBeRemoved = true; + break; + } + } + } + } + assertEq(isAdapterToBeRemoved, true); + } + } + } + } + } + } + + function _testCorrectForwarderAdaptersConfiguration( + address payload, + address crossChainController, + ICrossChainForwarder.ForwarderBridgeAdapterConfigInput[] memory forwardersToEnable + ) internal { + executePayload(vm, payload); + + for (uint256 i = 0; i < forwardersToEnable.length; i++) { + ICrossChainForwarder.ChainIdBridgeConfig[] + memory forwardersBridgeAdaptersByChain = ICrossChainForwarder(crossChainController) + .getForwarderBridgeAdaptersByChain(forwardersToEnable[i].destinationChainId); + bool newAdapterFound; + for (uint256 j = 0; j < forwardersBridgeAdaptersByChain.length; j++) { + if ( + forwardersBridgeAdaptersByChain[j].destinationBridgeAdapter == + forwardersToEnable[i].destinationBridgeAdapter && + forwardersBridgeAdaptersByChain[j].currentChainBridgeAdapter == + forwardersToEnable[i].currentChainBridgeAdapter + ) { + newAdapterFound = true; + break; + } + } + assertEq(newAdapterFound, true); + } + } + + function _testCorrectTrustedRemotes( + ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[] memory receiversToAllow + ) internal { + for (uint256 i = 0; i < receiversToAllow.length; i++) { + for (uint256 j = 0; j < receiversToAllow[i].chainIds.length; j++) { + address trustedRemote = IBaseAdapter(receiversToAllow[i].bridgeAdapter) + .getTrustedRemoteByChainId(receiversToAllow[i].chainIds[j]); + assertEq(trustedRemote, getCCCByChainId(receiversToAllow[i].chainIds[j])); + } + } + } + + function _testOnlyRemovedSpecifiedReceiverAdapters( + address payload, + ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[] memory adaptersToRemove, + address crossChainController + ) internal { + AdaptersByChain[] memory adaptersBefore = _getCurrentReceiverAdaptersByChain( + crossChainController + ); + + executePayload(vm, payload); + + for (uint256 i = 0; i < adaptersBefore.length; i++) { + for (uint256 j = 0; j < adaptersToRemove.length; j++) { + for (uint256 x = 0; x < adaptersToRemove[j].chainIds.length; x++) { + if (adaptersToRemove[j].chainIds[x] == adaptersBefore[i].chainId) { + for (uint256 k = 0; k < adaptersBefore[i].adapters.length; k++) { + if (adaptersBefore[i].adapters[k] == adaptersToRemove[j].bridgeAdapter) { + assertEq( + ICrossChainReceiver(crossChainController).isReceiverBridgeAdapterAllowed( + adaptersToRemove[j].bridgeAdapter, + adaptersBefore[i].chainId + ), + false + ); + } else { + assertEq( + ICrossChainReceiver(crossChainController).isReceiverBridgeAdapterAllowed( + adaptersBefore[i].adapters[k], + adaptersBefore[i].chainId + ), + true + ); + } + } + } + } + } + } + } + + function _testCorrectReceiverAdaptersConfiguration( + address payload, + ICrossChainReceiver.ReceiverBridgeAdapterConfigInput[] memory receiversToAllow, + address crossChainController + ) internal { + for (uint256 i = 0; i < receiversToAllow.length; i++) { + for (uint256 j = 0; j < receiversToAllow[i].chainIds.length; j++) { + assertEq( + ICrossChainReceiver(crossChainController).isReceiverBridgeAdapterAllowed( + receiversToAllow[i].bridgeAdapter, + receiversToAllow[i].chainIds[j] + ), + false + ); + } + } + + executePayload(vm, payload); + + for (uint256 i = 0; i < receiversToAllow.length; i++) { + for (uint256 j = 0; j < receiversToAllow[i].chainIds.length; j++) { + assertEq( + ICrossChainReceiver(crossChainController).isReceiverBridgeAdapterAllowed( + receiversToAllow[i].bridgeAdapter, + receiversToAllow[i].chainIds[j] + ), + true + ); + } + } + } + + /** + * @dev Generates a markdown compatible snapshot of the whole CrossChainController configuration into `/reports`. + * @param reportName filename suffix for the generated reports. + * @param crossChainController the ccc to be snapshot + * @return ReserveConfig[] list of configs + */ + function createConfigurationSnapshot( + string memory reportName, + address crossChainController + ) public returns (CCCConfig memory) { + return createConfigurationSnapshot(reportName, crossChainController, true, true, true); + } + + function createConfigurationSnapshot( + string memory reportName, + address crossChainController, + bool receiverConfigs, + bool receiverAdapterConfigs, + bool forwarderAdapterConfigs + ) public returns (CCCConfig memory) { + string memory path = string(abi.encodePacked('./reports/', reportName, '.json')); + // overwrite with empty json to later be extended + vm.writeFile( + path, + '{ "receiverConfigsByChain": {}, "receiverAdaptersByChain": {}, "forwarderAdaptersByChain": {}}' + ); + vm.serializeUint('root', 'chainId', block.chainid); + CCCConfig memory config = _getCCCConfig(crossChainController); + if (receiverConfigs) _writeReceiverConfigs(path, config); + if (receiverAdapterConfigs) _writeReceiverAdapters(path, config); + if (forwarderAdapterConfigs) _writeForwarderAdatpers(path, config); + + return config; + } + + function _writeForwarderAdatpers(string memory path, CCCConfig memory config) internal { + // keys for json stringification + string memory forwarderAdaptersKey = 'forwarderAdapters'; + string memory content = '{}'; + vm.serializeJson(forwarderAdaptersKey, '{}'); + ForwarderAdaptersByChain[] memory forwarderConfig = config.forwarderAdaptersConfig; + + for (uint256 i = 0; i < forwarderConfig.length; i++) { + uint256 chainId = forwarderConfig[i].chainId; + string memory key = vm.toString(chainId); + vm.serializeJson(key, '{}'); + string memory object; + + ICrossChainForwarder.ChainIdBridgeConfig[] memory forwarders = forwarderConfig[i].forwarders; + for (uint256 j = 0; j < forwarders.length; j++) { + if (j == forwarders.length - 1) { + object = vm.serializeString( + key, + vm.toString(forwarders[j].currentChainBridgeAdapter), + vm.toString(forwarders[j].destinationBridgeAdapter) + ); + } else { + vm.serializeString( + key, + vm.toString(forwarders[j].currentChainBridgeAdapter), + vm.toString(forwarders[j].destinationBridgeAdapter) + ); + } + } + content = vm.serializeString(forwarderAdaptersKey, key, object); + } + string memory output = vm.serializeString('root', 'forwarderAdaptersByChain', content); + vm.writeJson(output, path); + } + + function _writeReceiverAdapters(string memory path, CCCConfig memory config) internal { + // keys for json stringification + string memory receiverAdaptersKey = 'receiverAdapters'; + string memory content = '{}'; + vm.serializeJson(receiverAdaptersKey, '{}'); + ReceiverAdaptersByChain[] memory receiverConfig = config.receiverAdaptersConfig; + + for (uint256 i = 0; i < receiverConfig.length; i++) { + uint256 chainId = receiverConfig[i].chainId; + string memory key = vm.toString(chainId); + vm.serializeJson(key, '{}'); + string memory object; + + for (uint256 j = 0; j < receiverConfig[i].receiverAdapters.length; j++) { + if (j == receiverConfig[i].receiverAdapters.length - 1) { + object = vm.serializeString( + key, + vm.toString(receiverConfig[i].receiverAdapters[j]), + vm.toString(true) + ); + } else { + vm.serializeString( + key, + vm.toString(receiverConfig[i].receiverAdapters[j]), + vm.toString(true) + ); + } + } + content = vm.serializeString(receiverAdaptersKey, key, object); + } + string memory output = vm.serializeString('root', 'receiverAdaptersByChain', content); + vm.writeJson(output, path); + } + + function _writeReceiverConfigs(string memory path, CCCConfig memory configs) internal { + // keys for json stringification + string memory receiverConfigsKey = 'receiverConfigs'; + string memory content = '{}'; + vm.serializeJson(receiverConfigsKey, '{}'); + ReceiverConfigByChain[] memory receiverConfig = configs.receiverConfigs; + for (uint256 i = 0; i < receiverConfig.length; i++) { + uint256 chainId = receiverConfig[i].chainId; + string memory key = vm.toString(chainId); + vm.serializeJson(key, '{}'); + string memory object; + vm.serializeString( + key, + 'requiredConfirmations', + vm.toString(receiverConfig[i].requiredConfirmations) + ); + object = vm.serializeString( + key, + 'validityTimestamp', + vm.toString(receiverConfig[i].validityTimestamp) + ); + + content = vm.serializeString(receiverConfigsKey, key, object); + } + string memory output = vm.serializeString('root', 'receiverConfigs', content); + vm.writeJson(output, path); + } + + function _getCCCConfig(address ccc) internal view returns (CCCConfig memory) { + CCCConfig memory config; + + // get supported networks + uint256[] memory receiverSupportedChains = ICrossChainReceiver(ccc).getSupportedChains(); + ReceiverConfigByChain[] memory receiverConfigs = new ReceiverConfigByChain[]( + receiverSupportedChains.length + ); + ReceiverAdaptersByChain[] memory receiverAdaptersConfig = new ReceiverAdaptersByChain[]( + receiverSupportedChains.length + ); + for (uint256 i = 0; i < receiverSupportedChains.length; i++) { + uint256 chainId = receiverSupportedChains[i]; + ICrossChainReceiver.ReceiverConfiguration memory receiverConfig = ICrossChainReceiver(ccc) + .getConfigurationByChain(chainId); + receiverConfigs[i] = ReceiverConfigByChain({ + chainId: chainId, + requiredConfirmations: receiverConfig.requiredConfirmation, + validityTimestamp: receiverConfig.validityTimestamp + }); + receiverAdaptersConfig[i] = ReceiverAdaptersByChain({ + chainId: chainId, + receiverAdapters: ICrossChainReceiver(ccc).getReceiverBridgeAdaptersByChain(chainId) + }); + } + + config.receiverAdaptersConfig = receiverAdaptersConfig; + config.receiverConfigs = receiverConfigs; + + // get receiver configs by network + uint256[] memory supportedForwardingNetworks = _getForwarderSupportedChainsByChainId( + block.chainid + ); + ForwarderAdaptersByChain[] memory forwardersByChain = new ForwarderAdaptersByChain[]( + supportedForwardingNetworks.length + ); + for (uint256 i = 0; i < supportedForwardingNetworks.length; i++) { + uint256 chainId = supportedForwardingNetworks[i]; + forwardersByChain[i] = ForwarderAdaptersByChain({ + chainId: chainId, + forwarders: ICrossChainForwarder(ccc).getForwarderBridgeAdaptersByChain(chainId) + }); + } + config.forwarderAdaptersConfig = forwardersByChain; + + return config; + } + + /// @dev Update when supporting new forwarding networks + function _getForwarderSupportedChainsByChainId( + uint256 chainId + ) internal pure returns (uint256[] memory) { + if (chainId == ChainIds.MAINNET) { + uint256[] memory chainIds = new uint256[](10); + chainIds[0] = ChainIds.MAINNET; + chainIds[1] = ChainIds.POLYGON; + chainIds[2] = ChainIds.AVALANCHE; + chainIds[3] = ChainIds.BNB; + chainIds[4] = ChainIds.GNOSIS; + chainIds[5] = ChainIds.ARBITRUM; + chainIds[6] = ChainIds.OPTIMISM; + chainIds[7] = ChainIds.METIS; + chainIds[8] = ChainIds.BASE; + chainIds[9] = ChainIds.SCROLL; + + return chainIds; + } else if (chainId == ChainIds.POLYGON) { + uint256[] memory chainIds = new uint256[](1); + chainIds[0] = ChainIds.MAINNET; + + return chainIds; + } else if (chainId == ChainIds.AVALANCHE) { + uint256[] memory chainIds = new uint256[](1); + chainIds[0] = ChainIds.MAINNET; + + return chainIds; + } else { + return new uint256[](0); + } + } + + function _getCurrentForwarderAdaptersByChain( + address crossChainController, + uint256 chainId + ) internal view returns (ForwarderAdapters[] memory) { + uint256[] memory supportedChains = _getForwarderSupportedChainsByChainId(chainId); + + ForwarderAdapters[] memory forwarderAdapters = new ForwarderAdapters[](supportedChains.length); + + for (uint256 i = 0; i < supportedChains.length; i++) { + ICrossChainForwarder.ChainIdBridgeConfig[] memory forwarders = ICrossChainForwarder( + crossChainController + ).getForwarderBridgeAdaptersByChain(supportedChains[i]); + + forwarderAdapters[i] = ForwarderAdapters({adapters: forwarders, chainId: supportedChains[i]}); + } + return forwarderAdapters; + } + + function _getCurrentReceiverAdaptersByChain( + address crossChainController + ) internal view returns (AdaptersByChain[] memory) { + uint256[] memory supportedChains = ICrossChainReceiver(crossChainController) + .getSupportedChains(); + + AdaptersByChain[] memory receiverAdapters = new AdaptersByChain[](supportedChains.length); + + for (uint256 i = 0; i < supportedChains.length; i++) { + address[] memory receivers = ICrossChainReceiver(crossChainController) + .getReceiverBridgeAdaptersByChain(supportedChains[i]); + + receiverAdapters[i] = AdaptersByChain({adapters: receivers, chainId: supportedChains[i]}); + } + + return receiverAdapters; + } + + /// @dev add new chains t + function getCCCByChainId(uint256 chainId) public pure returns (address) { + if (chainId == ChainIds.MAINNET) { + return GovernanceV3Ethereum.CROSS_CHAIN_CONTROLLER; + } else if (chainId == ChainIds.POLYGON) { + return GovernanceV3Polygon.CROSS_CHAIN_CONTROLLER; + } else if (chainId == ChainIds.AVALANCHE) { + return GovernanceV3Avalanche.CROSS_CHAIN_CONTROLLER; + } else if (chainId == ChainIds.OPTIMISM) { + return GovernanceV3Optimism.CROSS_CHAIN_CONTROLLER; + } else if (chainId == ChainIds.BNB) { + return GovernanceV3BNB.CROSS_CHAIN_CONTROLLER; + } else if (chainId == ChainIds.METIS) { + return GovernanceV3Metis.CROSS_CHAIN_CONTROLLER; + } else if (chainId == ChainIds.BASE) { + return GovernanceV3Base.CROSS_CHAIN_CONTROLLER; + } else if (chainId == ChainIds.ARBITRUM) { + return GovernanceV3Arbitrum.CROSS_CHAIN_CONTROLLER; + } else if (chainId == ChainIds.GNOSIS) { + return GovernanceV3Gnosis.CROSS_CHAIN_CONTROLLER; + } else if (chainId == ChainIds.SCROLL) { + return GovernanceV3Scroll.CROSS_CHAIN_CONTROLLER; + } + revert(); + } +} diff --git a/tests/adi/SimpleOneToManyAdapterUpdatePayloadTest.t.sol b/tests/adi/SimpleOneToManyAdapterUpdatePayloadTest.t.sol new file mode 100644 index 00000000..01c17237 --- /dev/null +++ b/tests/adi/SimpleOneToManyAdapterUpdatePayloadTest.t.sol @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {SimpleOneToManyAdapterUpdate} from '../../src/adi/SimpleOneToManyAdapterUpdate.sol'; +import {GovernanceV3Polygon} from 'aave-address-book/GovernanceV3Polygon.sol'; +import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol'; +import {ChainIds} from '../../src/ChainIds.sol'; +import 'forge-std/Test.sol'; +import '../../src/adi/test/ADITestBase.sol'; + +contract SimpleOneToManyAdapterUpdatePayload is + SimpleOneToManyAdapterUpdate( + SimpleOneToManyAdapterUpdate.ConstructorInput({ + ccc: GovernanceV3Polygon.CROSS_CHAIN_CONTROLLER, + newAdapter: 0x7FAE7765abB4c8f778d57337bB720d0BC53057e3, + adapterToRemove: 0xDA4B6024aA06f7565BBcAaD9B8bE24C3c229AAb5 + }) + ) +{ + function getChainsToReceive() public pure override returns (uint256[] memory) { + uint256[] memory chains = new uint256[](1); + chains[0] = ChainIds.MAINNET; + return chains; + } + + function getDestinationAdapters() + public + pure + override + returns (DestinationAdaptersInput[] memory) + { + DestinationAdaptersInput[] memory destinationAdapters = new DestinationAdaptersInput[](1); + + destinationAdapters[0].adapter = 0x8410d9BD353b420ebA8C48ff1B0518426C280FCC; + destinationAdapters[0].chainId = ChainIds.MAINNET; + + return destinationAdapters; + } +} + +contract SimpleOneToManyAdapterUpdateEthereumPayload is + SimpleOneToManyAdapterUpdate( + SimpleOneToManyAdapterUpdate.ConstructorInput({ + ccc: GovernanceV3Ethereum.CROSS_CHAIN_CONTROLLER, + newAdapter: 0x8410d9BD353b420ebA8C48ff1B0518426C280FCC, // POLYGON native bridge adapter + adapterToRemove: 0xb13712De579E1f9943502FFCf72eab6ec348cF79 // POLYGON + }) + ) +{ + function getChainsToReceive() public pure override returns (uint256[] memory) { + uint256[] memory chains = new uint256[](1); + chains[0] = ChainIds.POLYGON; + return chains; + } +} + +// provably here we should just define the blockNumber and network. And use base test that in theory could generate diffs +contract SimpleOneToManyAdapterUpdatePayloadTest is ADITestBase { + SimpleOneToManyAdapterUpdatePayload public payload; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('polygon'), 55428042); + payload = new SimpleOneToManyAdapterUpdatePayload(); + } + + function getDestinationPayloadsByChain() + public + view + override + returns (DestinationPayload[] memory) + { + DestinationPayload[] memory destinationPayload = new DestinationPayload[](1); + destinationPayload[0] = DestinationPayload({ + chainId: ChainIds.MAINNET, + payloadCode: type(SimpleOneToManyAdapterUpdateEthereumPayload).creationCode + }); + + return destinationPayload; + } + + function test_defaultTest() public { + defaultTest( + 'test_adi_diffs', + GovernanceV3Polygon.CROSS_CHAIN_CONTROLLER, + address(payload), + true + ); + } +}