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 @@
+
\ 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
+ );
+ }
+}