diff --git a/.github/workflows/forge.yml b/.github/workflows/forge.yml index 0f9d75208..d63a381e6 100644 --- a/.github/workflows/forge.yml +++ b/.github/workflows/forge.yml @@ -14,6 +14,7 @@ jobs: ETH_NODE_URI_GOERLI: ${{ secrets.ETH_NODE_URI_GOERLI }} ETH_NODE_URI_ARBITRUM: ${{ secrets.ETH_NODE_URI_ARBITRUM }} ETH_NODE_URI_GNOSIS: ${{ secrets.ETH_NODE_URI_GNOSIS }} + ETH_NODE_URI_BSC_TESTNET: ${{ secrets.ETH_NODE_URI_BSC_TESTNET }} FORK_NUMBER: ${{ secrets.FORK_NUMBER }} POLYGON_FORK_NUMBER: ${{ secrets.POLYGON_FORK_NUMBER }} FORK_NUMBER_POLYGON: 36004499 diff --git a/config/ccip.json b/config/ccip.json new file mode 100644 index 000000000..54bcc8981 --- /dev/null +++ b/config/ccip.json @@ -0,0 +1,64 @@ +{ + "routers": { + "mainnet": { + "router": "0xE561d5E02207fb5eB32cca20a699E0d8919a1476" + }, + "sepolia": { + "router": "0xD0daae2231E9CB96b94C8512223533293C3693Bf" + }, + "optimism": { + "router": "0x261c05167db67B2b619f9d312e0753f3721ad6E8" + }, + "avalanche": { + "router": "0x27F39D0af3303703750D4001fCc1844c6491563c" + }, + "base": { + "router": "0x673aa85efd75080031d44fca061575d1da427a28" + }, + "mumbai": { + "router": "0x70499c328e1E2a3c41108bd3730F6670a44595D1" + }, + "polygon": { + "router": "0x3C3D92629A02a8D95D5CB9650fe49C3544f69B43" + }, + "bsc-testnet": { + "router": "0x9527e2d01a3064ef6b50c1da1c0cc523803bcff2" + } + }, + "chainSelectors": [ + { + "chainId": 1, + "selector": 5009297550715157269 + }, + { + "chainId": 10, + "selector": 3734403246176062136 + }, + { + "chainId": 43114, + "selector": 6433500567565415381 + }, + { + "chainId": 137, + "selector": 4051577828743386545 + }, + { + "chainId": 8453, + "selector": 15971525489660198786 + } + ], + "testChainSelectors": [ + { + "chainId": 11155111, + "selector": 16015286601757825753 + }, + { + "chainId": 80001, + "selector": 12532609583862916517 + }, + { + "chainId": 97, + "selector": 13264668187771770619 + } + ] +} diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index 8edf2fe2e..a819e0eef 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -465,6 +465,18 @@ "VERIFIED": "true" } ] + }, + "staging": { + "1.0.0": [ + { + "ADDRESS": "0x0E7C95Cd5087B9cf28848938544794Df18060364", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-27 14:59:37", + "CONSTRUCTOR_ARGS": "0x", + "SALT": "", + "VERIFIED": "true" + } + ] } }, "linea": { @@ -974,6 +986,18 @@ "VERIFIED": "true" } ] + }, + "staging": { + "1.0.0": [ + { + "ADDRESS": "0xa07464a132C0C3588ff3e2857920267072407B18", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-27 15:10:02", + "CONSTRUCTOR_ARGS": "0x", + "SALT": "", + "VERIFIED": "true" + } + ] } }, "linea": { @@ -1484,6 +1508,18 @@ "VERIFIED": "true" } ] + }, + "staging": { + "1.0.0": [ + { + "ADDRESS": "0x8d9A296ABcfe7F3a4046633eD0685D12264628Cf", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-27 15:11:00", + "CONSTRUCTOR_ARGS": "0x", + "SALT": "", + "VERIFIED": "true" + } + ] } }, "linea": { @@ -1956,6 +1992,18 @@ "VERIFIED": "true" } ] + }, + "staging": { + "1.0.0": [ + { + "ADDRESS": "0x9530d861dbe3002d6466541aA95AA77f77657c74", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-27 15:29:37", + "CONSTRUCTOR_ARGS": "0x", + "SALT": "", + "VERIFIED": "true" + } + ] } }, "linea": { @@ -2427,6 +2475,18 @@ "VERIFIED": "true" } ] + }, + "staging": { + "1.0.0": [ + { + "ADDRESS": "0x769b85186cB944F33deFE2C57e389EE20a136Ef0", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-27 15:21:19", + "CONSTRUCTOR_ARGS": "0x", + "SALT": "", + "VERIFIED": "false" + } + ] } }, "linea": { @@ -2912,6 +2972,18 @@ "VERIFIED": "true" } ] + }, + "staging": { + "1.0.0": [ + { + "ADDRESS": "0x3b77FCFaD7426C905050dB60e880EfC2825e07B5", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-27 15:47:32", + "CONSTRUCTOR_ARGS": "0x", + "SALT": "", + "VERIFIED": "true" + } + ] } }, "linea": { @@ -3411,6 +3483,18 @@ "VERIFIED": "true" } ] + }, + "staging": { + "1.0.0": [ + { + "ADDRESS": "0x78c16da94Ba24F62e340653e2a4eBf960B9C5771", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-27 15:33:32", + "CONSTRUCTOR_ARGS": "0x", + "SALT": "", + "VERIFIED": "true" + } + ] } }, "linea": { @@ -3885,6 +3969,18 @@ "VERIFIED": "true" } ] + }, + "staging": { + "1.0.0": [ + { + "ADDRESS": "0x276Ba9B74EaB0c0B544CC6506Db71a956E4B1282", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-27 15:49:41", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000552008c0f6870c2f77e5cc1d2eb9bdff03e30ea00000000000000000000000000e7c95cd5087b9cf28848938544794df18060364", + "SALT": "", + "VERIFIED": "true" + } + ] } }, "linea": { @@ -5217,6 +5313,18 @@ "VERIFIED": "true" } ] + }, + "staging": { + "1.0.0": [ + { + "ADDRESS": "0x9BFADfb17157599a6ac0Cd3aADb4d5b06c5ec405", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-27 15:54:23", + "CONSTRUCTOR_ARGS": "0x", + "SALT": "", + "VERIFIED": "true" + } + ] } }, "linea": { @@ -6165,6 +6273,18 @@ "VERIFIED": "true" } ] + }, + "staging": { + "1.0.0": [ + { + "ADDRESS": "0x3A9e75FD1EDd3746572A3932FCf2d1084ECf9EC8", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-27 15:53:26", + "CONSTRUCTOR_ARGS": "0x", + "SALT": "", + "VERIFIED": "true" + } + ] } }, "linea": { @@ -7772,6 +7892,18 @@ "VERIFIED": "true" } ] + }, + "staging": { + "1.0.0": [ + { + "ADDRESS": "0x133E6EA10e1c70C17D957A3c36078bEc443ef7D5", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-27 15:50:42", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000552008c0f6870c2f77e5cc1d2eb9bdff03e30ea0", + "SALT": "", + "VERIFIED": "true" + } + ] } }, "linea": { @@ -8430,6 +8562,18 @@ "VERIFIED": "true" } ] + }, + "staging": { + "2.0.0": [ + { + "ADDRESS": "0xECeD7eB0FF5c63DE883F7DEe66af24f58faC417b", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-27 15:55:32", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000133e6ea10e1c70c17d957a3c36078bec443ef7d5", + "SALT": "", + "VERIFIED": "true" + } + ] } }, "linea": { @@ -13461,6 +13605,18 @@ "VERIFIED": "true" } ] + }, + "staging": { + "1.0.0": [ + { + "ADDRESS": "0x09071b88aDf0E95D064296d635ca63a1Cc06c304", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-27 15:44:50", + "CONSTRUCTOR_ARGS": "0x", + "SALT": "", + "VERIFIED": "true" + } + ] } }, "boba": { @@ -13769,6 +13925,18 @@ "VERIFIED": "true" } ] + }, + "staging": { + "1.1.1": [ + { + "ADDRESS": "0x438c07D8a3121619a3DAF24795E59700d94afe55", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-27 15:46:34", + "CONSTRUCTOR_ARGS": "0x", + "SALT": "", + "VERIFIED": "true" + } + ] } }, "bsc": { @@ -14122,400 +14290,435 @@ } } }, - "LiFuelFeeCollector": { - "polygon": { + "CCIPFacet": { + "avalanche": { "staging": { - "1.0.0": [ - { - "ADDRESS": "0x86130169737b68fAF1b619A75c92205784601Da6", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-09 11:51:33", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "true" - } - ] - }, - "production": { - "1.0.0": [ - { - "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-25 17:38:28", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "true" - } - ] - } - }, - "goerli": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xdFC2983401614118E1F2D5A5FD93C17Fecf8BdC6", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-10 17:16:05", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "true" - } - ] - } - }, - "lineatest": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xdFC2983401614118E1F2D5A5FD93C17Fecf8BdC6", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-09 16:12:01", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "false" - } - ] + "0.0.1": [ + { + "ADDRESS": "0x6982237928de8e4a1a3622226D3a9C2bd89515Fc", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-09-29 16:56:13", + "CONSTRUCTOR_ARGS": "0x00000000000000000000000027f39d0af3303703750d4001fcc1844c6491563c", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "LiFuelFeeCollector": { + "polygon": { + "staging": { + "1.0.0": [ + { + "ADDRESS": "0x86130169737b68fAF1b619A75c92205784601Da6", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-09 11:51:33", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "mainnet": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-26 11:28:49", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "goerli": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xdFC2983401614118E1F2D5A5FD93C17Fecf8BdC6", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-10 17:16:05", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "lineatest": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xdFC2983401614118E1F2D5A5FD93C17Fecf8BdC6", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-09 16:12:01", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "false" + } + ] + } + }, + "arbitrum": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-25 17:23:19", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "aurora": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-25 17:24:09", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "avalanche": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-25 17:24:56", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "base": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-25 17:26:04", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "boba": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-25 17:26:50", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "bsc": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-25 17:27:38", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "cronos": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xdFC2983401614118E1F2D5A5FD93C17Fecf8BdC6", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-10 17:00:10", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "false" + } + ] + } + }, + "evmos": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-25 17:29:02", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "fantom": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-25 17:29:46", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "fuse": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-25 17:30:39", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "gnosis": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-25 17:31:47", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "moonbeam": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-25 17:33:47", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "moonriver": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-25 17:35:02", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "okx": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-25 17:36:17", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "false" + } + ] + } + }, + "optimism": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-25 17:37:25", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "polygonzkevm": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-25 18:05:42", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "false" + } + ] + } + }, + "linea": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x6CC48E94C1148A0787D7F137745af58e3Eb47780", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-26 12:27:48", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "mumbai": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xdFC2983401614118E1F2D5A5FD93C17Fecf8BdC6", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-10 18:27:05", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "false" + } + ] + } + }, + "zksync": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xbE07Edbb7d0fc7eb941078486F6b59061bF0336C", + "OPTIMIZER_RUNS": "10000", + "TIMESTAMP": "2023-10-11 14:04:54", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "true" + }, + { + "ADDRESS": "0xbE07Edbb7d0fc7eb941078486F6b59061bF0336C", + "OPTIMIZER_RUNS": "10000", + "TIMESTAMP": "2023-10-11 14:04:54", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "VERIFIED": "false" + }, + { + "ADDRESS": "0xbE07Edbb7d0fc7eb941078486F6b59061bF0336C", + "OPTIMIZER_RUNS": "10000", + "TIMESTAMP": "2023-10-11 14:04:54", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "VERIFIED": "false" + }, + { + "ADDRESS": "0xbE07Edbb7d0fc7eb941078486F6b59061bF0336C", + "OPTIMIZER_RUNS": "10000", + "TIMESTAMP": "2023-10-11 14:04:54", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "VERIFIED": "false" + }, + { + "ADDRESS": "0x4A751A91647CA1749d7ec505026427FF601C6966", + "OPTIMIZER_RUNS": "10000", + "TIMESTAMP": "2023-10-26 09:10:07", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "VERIFIED": "false" + } + ] + } + }, + "opbnb": { + "production": { + "1.1.0": [ + { + "ADDRESS": "0xaE77c9aD4af61fAec96f04bD6723F6F6A804a567", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-09-18 11:36:54", + "CONSTRUCTOR_ARGS": "0x", + "SALT": "", + "VERIFIED": "false" + } + ], + "1.0.0": [ + { + "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-25 17:36:54", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "false" + } + ] + } + }, + "velas": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-10-25 18:03:06", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "SALT": "", + "VERIFIED": "false" + } + ] + } } }, "mainnet": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-26 11:28:49", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "true" - } - ] - } - }, - "arbitrum": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-25 17:23:19", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "true" - } - ] - } - }, - "aurora": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-25 17:24:09", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "true" - } - ] - } - }, - "avalanche": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-25 17:24:56", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "true" - } - ] - } - }, - "base": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-25 17:26:04", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "true" - } - ] - } - }, - "boba": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-25 17:26:50", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "true" - } - ] - } - }, - "bsc": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-25 17:27:38", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "true" - } - ] - } - }, - "cronos": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xdFC2983401614118E1F2D5A5FD93C17Fecf8BdC6", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-10 17:00:10", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "false" - } - ] - } - }, - "evmos": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-25 17:29:02", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "true" - } - ] - } - }, - "fantom": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-25 17:29:46", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "true" - } - ] - } - }, - "fuse": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-25 17:30:39", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "true" - } - ] - } - }, - "gnosis": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-25 17:31:47", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "true" - } - ] - } - }, - "moonbeam": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-25 17:33:47", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "true" - } - ] - } - }, - "moonriver": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-25 17:35:02", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "true" - } - ] - } - }, - "okx": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-25 17:36:17", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "false" - } - ] - } - }, - "optimism": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-25 17:37:25", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "true" - } - ] - } - }, - "polygonzkevm": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-25 18:05:42", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "false" - } - ] - } - }, - "linea": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0x6CC48E94C1148A0787D7F137745af58e3Eb47780", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-26 12:27:48", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "true" - } - ] - } - }, - "mumbai": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xdFC2983401614118E1F2D5A5FD93C17Fecf8BdC6", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-10 18:27:05", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "SALT": "", - "VERIFIED": "false" - } - ] - } - }, - "zksync": { - "production": { - "1.0.0": [ - { - "ADDRESS": "0xbE07Edbb7d0fc7eb941078486F6b59061bF0336C", - "OPTIMIZER_RUNS": "10000", - "TIMESTAMP": "2023-10-11 14:04:54", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "VERIFIED": "true" - }, - { - "ADDRESS": "0xbE07Edbb7d0fc7eb941078486F6b59061bF0336C", - "OPTIMIZER_RUNS": "10000", - "TIMESTAMP": "2023-10-11 14:04:54", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "VERIFIED": "false" - }, - { - "ADDRESS": "0xbE07Edbb7d0fc7eb941078486F6b59061bF0336C", - "OPTIMIZER_RUNS": "10000", - "TIMESTAMP": "2023-10-11 14:04:54", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "VERIFIED": "false" - }, - { - "ADDRESS": "0xbE07Edbb7d0fc7eb941078486F6b59061bF0336C", - "OPTIMIZER_RUNS": "10000", - "TIMESTAMP": "2023-10-11 14:04:54", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "VERIFIED": "false" - }, - { - "ADDRESS": "0x4A751A91647CA1749d7ec505026427FF601C6966", - "OPTIMIZER_RUNS": "10000", - "TIMESTAMP": "2023-10-26 09:10:07", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", - "VERIFIED": "false" - } - ] - } - }, - "opbnb": { - "production": { - "1.1.0": [ - { - "ADDRESS": "0xaE77c9aD4af61fAec96f04bD6723F6F6A804a567", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-09-18 11:36:54", - "CONSTRUCTOR_ARGS": "0x", - "SALT": "", - "VERIFIED": "false" - } - ], - "1.0.0": [ + "staging": { + "0.0.1": [ { - "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "ADDRESS": "0x9BE903AB2ad61dfC71f7E2171A3dBf0884a4cdBF", "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-25 17:36:54", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "TIMESTAMP": "2023-10-27 15:42:31", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000e561d5e02207fb5eb32cca20a699e0d8919a1476", "SALT": "", "VERIFIED": "false" } ] } - }, - "velas": { - "production": { - "1.0.0": [ + } + }, + "CCIPMsgReceiver": { + "base": { + "staging": { + "0.0.1": [ { - "ADDRESS": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "ADDRESS": "0x76ad5bC7B89E951915010b5d5d24d045dC15D976", "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2023-10-25 18:03:06", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000c71284231a726a18ac85c94d75f9fe17a185beaf", + "TIMESTAMP": "2023-10-30 14:54:10", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000156cebba59deb2cb23742f70dcb0a11cc775591f000000000000000000000000673aa85efd75080031d44fca061575d1da427a28000000000000000000000000eced7eb0ff5c63de883f7dee66af24f58fac417b00000000000000000000000000000000000000000000000000000000000186a0", "SALT": "", "VERIFIED": "false" } diff --git a/deployments/avalanche.diamond.staging.json b/deployments/avalanche.diamond.staging.json index 5c3795fd6..57d5c865c 100644 --- a/deployments/avalanche.diamond.staging.json +++ b/deployments/avalanche.diamond.staging.json @@ -48,6 +48,10 @@ "0xe1FaF1759cAB242c5A790Da72c8f0cC7F5e09f59": { "Name": "CircleBridgeFacet", "Version": "1.0.0" + }, + "0x6982237928de8e4a1a3622226D3a9C2bd89515Fc": { + "Name": "CCIPFacet", + "Version": "0.0.1" } }, "Periphery": { diff --git a/deployments/avalanche.staging.json b/deployments/avalanche.staging.json index 9a38f735d..a66db980d 100644 --- a/deployments/avalanche.staging.json +++ b/deployments/avalanche.staging.json @@ -16,5 +16,6 @@ "FeeCollector": "0x0222D030e8DFAEDE2a4e7B5F181Ac1A4206A75f0", "Receiver": "0x59B341fF54543D66C7393FfD2A050E256c97669E", "ServiceFeeCollector": "0x9cc3164f01ED3796Fdf7Da538484D634608D2203", - "CircleBridgeFacet": "0xe1FaF1759cAB242c5A790Da72c8f0cC7F5e09f59" + "CircleBridgeFacet": "0xe1FaF1759cAB242c5A790Da72c8f0cC7F5e09f59", + "CCIPFacet": "0x6982237928de8e4a1a3622226D3a9C2bd89515Fc" } \ No newline at end of file diff --git a/deployments/base.diamond.staging.json b/deployments/base.diamond.staging.json new file mode 100644 index 000000000..d9002d8e4 --- /dev/null +++ b/deployments/base.diamond.staging.json @@ -0,0 +1,60 @@ +{ + "LiFiDiamond": { + "Facets": { + "0x0E7C95Cd5087B9cf28848938544794Df18060364": { + "Name": "DiamondCutFacet", + "Version": "1.0.0" + }, + "0xa07464a132C0C3588ff3e2857920267072407B18": { + "Name": "DiamondLoupeFacet", + "Version": "1.0.0" + }, + "0x8d9A296ABcfe7F3a4046633eD0685D12264628Cf": { + "Name": "OwnershipFacet", + "Version": "1.0.0" + }, + "0x3b77FCFaD7426C905050dB60e880EfC2825e07B5": { + "Name": "WithdrawFacet", + "Version": "1.0.0" + }, + "0x9530d861dbe3002d6466541aA95AA77f77657c74": { + "Name": "DexManagerFacet", + "Version": "1.0.0" + }, + "0x769b85186cB944F33deFE2C57e389EE20a136Ef0": { + "Name": "AccessManagerFacet", + "Version": "1.0.0" + }, + "0x78c16da94Ba24F62e340653e2a4eBf960B9C5771": { + "Name": "PeripheryRegistryFacet", + "Version": "1.0.0" + }, + "0x3A9e75FD1EDd3746572A3932FCf2d1084ECf9EC8": { + "Name": "LIFuelFacet", + "Version": "1.0.0" + }, + "0x9BFADfb17157599a6ac0Cd3aADb4d5b06c5ec405": { + "Name": "GenericSwapFacet", + "Version": "1.0.0" + }, + "0x09071b88aDf0E95D064296d635ca63a1Cc06c304": { + "Name": "StandardizedCallFacet", + "Version": "1.0.0" + }, + "0x438c07D8a3121619a3DAF24795E59700d94afe55": { + "Name": "CalldataVerificationFacet", + "Version": "1.1.1" + } + }, + "Periphery": { + "CCIPMsgReceiver": "0x76ad5bC7B89E951915010b5d5d24d045dC15D976", + "ERC20Proxy": "0x133E6EA10e1c70C17D957A3c36078bEc443ef7D5", + "Executor": "0xECeD7eB0FF5c63DE883F7DEe66af24f58faC417b", + "FeeCollector": "", + "LiFuelFeeCollector": "", + "Receiver": "", + "RelayerCelerIM": "", + "ServiceFeeCollector": "" + } + } +} \ No newline at end of file diff --git a/deployments/base.staging.json b/deployments/base.staging.json new file mode 100644 index 000000000..b13c1e4e8 --- /dev/null +++ b/deployments/base.staging.json @@ -0,0 +1,17 @@ +{ + "DiamondCutFacet": "0x0E7C95Cd5087B9cf28848938544794Df18060364", + "DiamondLoupeFacet": "0xa07464a132C0C3588ff3e2857920267072407B18", + "OwnershipFacet": "0x8d9A296ABcfe7F3a4046633eD0685D12264628Cf", + "AccessManagerFacet": "0x769b85186cB944F33deFE2C57e389EE20a136Ef0", + "DexManagerFacet": "0x9530d861dbe3002d6466541aA95AA77f77657c74", + "PeripheryRegistryFacet": "0x78c16da94Ba24F62e340653e2a4eBf960B9C5771", + "StandardizedCallFacet": "0x09071b88aDf0E95D064296d635ca63a1Cc06c304", + "CalldataVerificationFacet": "0x438c07D8a3121619a3DAF24795E59700d94afe55", + "WithdrawFacet": "0x3b77FCFaD7426C905050dB60e880EfC2825e07B5", + "LiFiDiamond": "0x276Ba9B74EaB0c0B544CC6506Db71a956E4B1282", + "ERC20Proxy": "0x133E6EA10e1c70C17D957A3c36078bEc443ef7D5", + "LIFuelFacet": "0x3A9e75FD1EDd3746572A3932FCf2d1084ECf9EC8", + "GenericSwapFacet": "0x9BFADfb17157599a6ac0Cd3aADb4d5b06c5ec405", + "Executor": "0xECeD7eB0FF5c63DE883F7DEe66af24f58faC417b", + "CCIPMsgReceiver": "0x76ad5bC7B89E951915010b5d5d24d045dC15D976" +} \ No newline at end of file diff --git a/deployments/mainnet.diamond.staging.json b/deployments/mainnet.diamond.staging.json index 0bf3c729c..798ce1722 100644 --- a/deployments/mainnet.diamond.staging.json +++ b/deployments/mainnet.diamond.staging.json @@ -63,7 +63,7 @@ }, "0xe7072402217EfF9b73cf457731cEE2A3824360dc": { "Name": "AllBridgeFacet", - "Version": "2.0.0" + "Version": "" }, "0x987f67811Ef841da0466746E10B4139Daff95053": { "Name": "ArbitrumBridgeFacet", @@ -92,12 +92,18 @@ "0xE8Ff7BFEF5DacB57E87bC2d0B6CCFefBE5f546BC": { "Name": "OptimismBridgeFacet", "Version": "1.0.0" + }, + "0x9BE903AB2ad61dfC71f7E2171A3dBf0884a4cdBF": { + "Name": "CCIPFacet", + "Version": "0.0.1" } }, "Periphery": { + "CCIPMsgReceiver": "", "ERC20Proxy": "0x0654EbA982ec082036A3D0f59964D302f1ba5cdA", "Executor": "0xBe27F03C8e6a61E2a4B1EE7940dbcb9204744d1c", "FeeCollector": "0x9ca271A532392230EAe919Fb5460aEa9D9718424", + "LiFuelFeeCollector": "", "Receiver": "0xC850013FC01A264018D58D112000E32835D15fBC", "RelayerCelerIM": "", "ServiceFeeCollector": "0xf068cc770f32042Ff4a8fD196045641234dFaa47" diff --git a/deployments/mainnet.staging.json b/deployments/mainnet.staging.json index 31a03866d..cd3cd54c3 100644 --- a/deployments/mainnet.staging.json +++ b/deployments/mainnet.staging.json @@ -28,5 +28,6 @@ "RoninBridgeFacet": "0xFB4C992Cc7cfA7Eb3e44b928C6f756C07a3feb04", "SquidFacet": "0x933A3AfE2087FB8F5c9EE9A033477C42CC14c18E", "SynapseBridgeFacet": "0x57F98A94AC66e197AF6776D5c094FF0da2C0B198", - "ThorSwapFacet": "0xa696287F37d21D566B9A80AC29b2640FF910C176" -} + "ThorSwapFacet": "0xa696287F37d21D566B9A80AC29b2640FF910C176", + "CCIPFacet": "0x9BE903AB2ad61dfC71f7E2171A3dBf0884a4cdBF" +} \ No newline at end of file diff --git a/docs/CCIPFacet.md b/docs/CCIPFacet.md new file mode 100644 index 000000000..76267020e --- /dev/null +++ b/docs/CCIPFacet.md @@ -0,0 +1,98 @@ +# CCIP Facet + +## How it works + +The CCIP Facet works by forwarding CCIP specific calls to the CCIP router contract. + +```mermaid +graph LR; + D{LiFiDiamond}-- DELEGATECALL -->CCIPFacet; + CCIPFacet -- CALL --> C(CCIP) +``` + +## Public Methods + +- `function startBridgeTokensViaCCIP(BridgeData calldata _bridgeData, CCIPData calldata _ccipData)` + - Simply bridges tokens using ccip +- `swapAndStartBridgeTokensViaccip(BridgeData memory _bridgeData, LibSwap.SwapData[] calldata _swapData, ccipData memory _ccipData)` + - Performs swap(s) before bridging tokens using ccip +- `quoteCCIPFee(BridgeData memory _bridgeData, CCIPData memory _ccipData) returns (uint256)` + - Returns the amount of fee in native tokens needed to bridge the tokens using ccip +- `encodeDestinationArgs(uint256 gasLimit, bool strictSequencing) returns (bytes memory)` + - Encodes the destination args for ccip. Only needed if doing calls on the destination chain. + +## ccip Specific Parameters + +The methods listed above take a variable labeled `_ccipData`. This data is specific to ccip and is represented as the following struct type: + +```solidity +/// @param callData destination calldata (optional). +/// @param extraArgs extra arguments for destination call (only needed for destination calls) +struct CCIPData { + bytes callData; + bytes extraArgs; +} +``` + +## Swap Data + +Some methods accept a `SwapData _swapData` parameter. + +Swapping is performed by a swap specific library that expects an array of calldata to can be run on variaous DEXs (i.e. Uniswap) to make one or multiple swaps before performing another action. + +The swap library can be found [here](../src/Libraries/LibSwap.sol). + +## LiFi Data + +Some methods accept a `BridgeData _bridgeData` parameter. + +This parameter is strictly for analytics purposes. It's used to emit events that we can later track and index in our subgraphs and provide data on how our contracts are being used. `BridgeData` and the events we can emit can be found [here](../src/Interfaces/ILiFi.sol). + +## Getting Sample Calls to interact with the Facet + +In the following some sample calls are shown that allow you to retrieve a populated transaction that can be sent to our contract via your wallet. + +All examples use our [/quote endpoint](https://apidocs.li.fi/reference/get_quote) to retrieve a quote which contains a `transactionRequest`. This request can directly be sent to your wallet to trigger the transaction. + +The quote result looks like the following: + +```javascript +const quoteResult = { + id: '0x...', // quote id + type: 'lifi', // the type of the quote (all lifi contract calls have the type "lifi") + tool: 'ccip', // the bridge tool used for the transaction + action: {}, // information about what is going to happen + estimate: {}, // information about the estimated outcome of the call + includedSteps: [], // steps that are executed by the contract as part of this transaction, e.g. a swap step and a cross step + transactionRequest: { + // the transaction that can be sent using a wallet + data: '0x...', + to: '0x...', + value: '0x00', + from: '{YOUR_WALLET_ADDRESS}', + chainId: 100, + gasLimit: '0x...', + gasPrice: '0x...', + }, +} +``` + +A detailed explanation on how to use the /quote endpoint and how to trigger the transaction can be found [here](https://docs.li.fi/products/more-integration-options/li.fi-api/transferring-tokens-example). + +**Hint**: Don't forget to replace `{YOUR_WALLET_ADDRESS}` with your real wallet address in the examples. + +### Cross Only + +To get a transaction for a transfer from 30 USDC.e on Avalanche to USDC on Binance you can execute the following request: + +```shell +curl 'https://li.quest/v1/quote?fromChain=AVA&fromAmount=30000000&fromToken=USDC&toChain=BSC&toToken=USDC&slippage=0.03&allowBridges=ccip&fromAddress={YOUR_WALLET_ADDRESS}' +``` + +### Swap & Cross + +To get a transaction for a transfer from 30 USDT on Avalanche to USDC on Binance you can execute the following request: + +```shell +curl 'https://li.quest/v1/quote?fromChain=AVA&fromAmount=30000000&fromToken=USDT&toChain=BSC&toToken=USDC&slippage=0.03&allowBridges=ccip&fromAddress={YOUR_WALLET_ADDRESS}' +``` diff --git a/docs/README.md b/docs/README.md index 2693eb214..7fea6ef61 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,6 +7,7 @@ - [Arbitrum Bridge Facet](./ArbitrumBridgeFacet.md) - [CalldataVerification Facet](./CalldataVerificationFacet.md) - [CBridge Facet](./CBridgeFacet.md) +- [CCIP Facet](./CCIPFacet.md) - [Celer Circle Bridge Facet](./CelerCircleBridgeFacet.md) - [Circle Bridge Facet](./CircleBridgeFacet.md) - [DeBridge Facet](./DeBridgeFacet.md) diff --git a/package.json b/package.json index 62ffd5993..935be8379 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ }, "dependencies": { "@arbitrum/sdk": "^3.0.0", + "@chainlink/contracts-ccip": "^0.7.6", "@hop-protocol/sdk": "0.0.1-beta.310", "@safe-global/api-kit": "^1.1.0", "@safe-global/protocol-kit": "^1.0.1", diff --git a/remappings.txt b/remappings.txt index a933e8bf9..da4422629 100644 --- a/remappings.txt +++ b/remappings.txt @@ -3,8 +3,8 @@ eth-gas-reporter/=node_modules/eth-gas-reporter/ hardhat/=node_modules/hardhat/ hardhat-deploy/=node_modules/hardhat-deploy/ - @openzeppelin/=lib/openzeppelin-contracts/ +@chainlink-ccip/=node_modules/@chainlink/contracts-ccip/src/ celer-network/=lib/sgn-v2-contracts/ create3-factory/=lib/create3-factory/src/ solmate/=lib/solmate/src/ diff --git a/script/demoScripts/demoCCIP.ts b/script/demoScripts/demoCCIP.ts new file mode 100644 index 000000000..f025ccd0a --- /dev/null +++ b/script/demoScripts/demoCCIP.ts @@ -0,0 +1,135 @@ +import { providers, Wallet, utils, constants, Contract } from 'ethers' +import { CCIPFacet__factory, ERC20__factory } from '../../typechain' +import chalk from 'chalk' +import dotenv from 'dotenv' + +dotenv.config() + +const msg = (msg: string) => { + console.log(chalk.green(msg)) +} + +const LIFI_ADDRESS = '0xbEbCDb5093B47Cd7add8211E4c77B6826aF7bc5F' // LiFiDiamond address on MAINNET stating +const R_TOKEN_ADDRESS = '0x183015a9ba6ff60230fdeadc3f43b3d788b13e21' +const R_TOKEN_ADDRESS_BASE = '0xaFB2820316e7Bc5Ef78d295AB9b8Bb2257534576' +const USDC_TOKEN_ADDRESS_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' +const UNISWAP_ADDRESS = '0xcF77a3Ba9A5CA399B7c97c74d54e5b1Beb874E43' +const CCIP_MSG_RECEIVER_ADDR = '0x76ad5bC7B89E951915010b5d5d24d045dC15D976' +const EXECUTOR_ADDR = '0xECeD7eB0FF5c63DE883F7DEe66af24f58faC417b' +const destinationChainId = 8453 // Base Chain + +async function main() { + const jsonProvider = new providers.JsonRpcProvider( + process.env.ETH_NODE_URI_MAINNET + ) + const jsonProvider2 = new providers.JsonRpcProvider( + process.env.ETH_NODE_URI_BASE + ) + const provider = new providers.FallbackProvider([jsonProvider]) + const provider2 = new providers.FallbackProvider([jsonProvider2]) + + let wallet = new Wallet(process.env.PRIVATE_KEY) + wallet = wallet.connect(provider) + const walletAddress = await wallet.getAddress() + + const lifi = CCIPFacet__factory.connect(LIFI_ADDRESS, wallet) + + // Bridge amount + const amount = utils.parseEther('1') + + // LIFI Data + const lifiData = { + transactionId: utils.randomBytes(32), + bridge: 'CCIP', + integrator: 'ACME Devs', + referrer: constants.AddressZero, + sendingAssetId: R_TOKEN_ADDRESS, + receiver: walletAddress, + minAmount: amount, + destinationChainId: destinationChainId, + hasSourceSwaps: false, + hasDestinationCall: true, + } + + // Bridge ERC20 + lifiData.sendingAssetId = R_TOKEN_ADDRESS + + const extraArgs = await lifi.encodeDestinationArgs(1000000, false) + + // Swap Data + const uniswap = new Contract(UNISWAP_ADDRESS, [ + 'function swapExactTokensForTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts)', + ]) + const path = [R_TOKEN_ADDRESS_BASE, USDC_TOKEN_ADDRESS_BASE] + const deadline = Math.floor(Date.now() / 1000) + 60 * 45 // 45 minutes from the current Unix time + + const amountOutMin = utils.parseEther('1') + const usdcAmountOutMin = utils.parseUnits('0.80', 6) + + const swapData = await uniswap.populateTransaction.swapExactTokensForTokens( + amountOutMin, + usdcAmountOutMin, + path, + EXECUTOR_ADDR, + deadline + ) + + const payload = utils.defaultAbiCoder.encode( + [ + 'bytes32', + 'tuple(address callTo, address approveTo, address sendingAssetId, address receivingAssetId, uint256 fromAmount, bytes callData, bool requiresDeposit)[]', + 'address', + ], + [ + lifiData.transactionId, + [ + { + callTo: swapData.to, + approveTo: swapData.to, + sendingAssetId: R_TOKEN_ADDRESS_BASE, + receivingAssetId: USDC_TOKEN_ADDRESS_BASE, + fromAmount: amountOutMin, + callData: swapData?.data, + requiresDeposit: true, + }, + ], + walletAddress, + ] + ) + + const bridgeData = { + callData: payload, + extraArgs, + receiver: CCIP_MSG_RECEIVER_ADDR, + } + + const fee = await lifi.quoteCCIPFee(lifiData, bridgeData) + + msg('Wallet Address: ' + walletAddress) + + msg('Approving R...') + const BETS = ERC20__factory.connect(R_TOKEN_ADDRESS, wallet) + let tx = await BETS.approve(LIFI_ADDRESS, amount) + await tx.wait() + + msg('Sending R to Base via CCIP...') + tx = await lifi.startBridgeTokensViaCCIP(lifiData, bridgeData, { + gasLimit: '500000', + value: fee, + }) + msg('TX Sent. Waiting for receipt...') + await tx.wait() + msg('TX Hash: ' + tx.hash) + msg('Done!') +} + +main() + .then(() => { + console.error('Success') + process.exit(0) + }) + .catch((error) => { + console.error('error') + console.error(error) + process.exit(1) + }) diff --git a/script/deploy/_targetState.json b/script/deploy/_targetState.json index 8d50a2663..e921c4dc5 100644 --- a/script/deploy/_targetState.json +++ b/script/deploy/_targetState.json @@ -315,6 +315,13 @@ "StargateFacet": "2.2.0", "HopFacetOptimized": "2.0.0" } + }, + "staging": { + "LiFiDiamond": { + "ERC20Proxy": "1.0.0", + "Executor": "2.0.0", + "CCIPMsgReceiver": "1.0.0" + } } }, "bsc": { diff --git a/script/deploy/facets/DeployCCIPFacet.s.sol b/script/deploy/facets/DeployCCIPFacet.s.sol new file mode 100644 index 000000000..cfcbe5ffc --- /dev/null +++ b/script/deploy/facets/DeployCCIPFacet.s.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.17; + +import { DeployScriptBase } from "./utils/DeployScriptBase.sol"; +import { stdJson } from "forge-std/Script.sol"; +import { CCIPFacet } from "lifi/Facets/CCIPFacet.sol"; + +contract DeployScript is DeployScriptBase { + using stdJson for string; + + constructor() DeployScriptBase("CCIPFacet") {} + + function run() + public + returns (CCIPFacet deployed, bytes memory constructorArgs) + { + string memory path = string.concat( + vm.projectRoot(), + "/config/ccip.json" + ); + string memory json = vm.readFile(path); + address router = json.readAddress( + string.concat(".routers.", network, ".router") + ); + + constructorArgs = abi.encode(router); + + vm.startBroadcast(deployerPrivateKey); + + if (isDeployed()) { + return (CCIPFacet(payable(predicted)), constructorArgs); + } + + deployed = CCIPFacet( + payable( + factory.deploy( + salt, + bytes.concat(type(CCIPFacet).creationCode, constructorArgs) + ) + ) + ); + + vm.stopBroadcast(); + } +} diff --git a/script/deploy/facets/DeployCCIPMsgReceiver.s.sol b/script/deploy/facets/DeployCCIPMsgReceiver.s.sol new file mode 100644 index 000000000..99d6ccdfb --- /dev/null +++ b/script/deploy/facets/DeployCCIPMsgReceiver.s.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.17; + +import { DeployScriptBase } from "./utils/DeployScriptBase.sol"; +import { stdJson } from "forge-std/Script.sol"; +import { CCIPMsgReceiver } from "lifi/Periphery/CCIPMsgReceiver.sol"; + +contract DeployScript is DeployScriptBase { + using stdJson for string; + + constructor() DeployScriptBase("CCIPMsgReceiver") {} + + function run() + public + returns (CCIPMsgReceiver deployed, bytes memory constructorArgs) + { + constructorArgs = getConstructorArgs(); + + deployed = CCIPMsgReceiver(deploy(type(CCIPMsgReceiver).creationCode)); + } + + function getConstructorArgs() internal override returns (bytes memory) { + // get path of global config file + string memory globalConfigPath = string.concat( + root, + "/config/global.json" + ); + + // read file into json variable + string memory globalConfigJson = vm.readFile(globalConfigPath); + + // extract refundWallet address + address refundWalletAddress = globalConfigJson.readAddress( + ".refundWallet" + ); + + // obtain address of Stargate router in current network from config file + string memory path = string.concat(root, "/config/ccip.json"); + string memory json = vm.readFile(path); + + address ccipRouter = json.readAddress( + string.concat(".routers.", network, ".router") + ); + + path = string.concat( + root, + "/deployments/", + network, + ".", + fileSuffix, + "json" + ); + json = vm.readFile(path); + address executor = json.readAddress(".Executor"); + + return abi.encode(refundWalletAddress, ccipRouter, executor, 100000); + } +} diff --git a/script/deploy/facets/UpdateCCIPFacet.s.sol b/script/deploy/facets/UpdateCCIPFacet.s.sol new file mode 100644 index 000000000..3f8f89dd0 --- /dev/null +++ b/script/deploy/facets/UpdateCCIPFacet.s.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.17; + +import { UpdateScriptBase } from "./utils/UpdateScriptBase.sol"; +import { stdJson } from "forge-std/StdJson.sol"; +import { CCIPFacet } from "lifi/Facets/CCIPFacet.sol"; + +contract DeployScript is UpdateScriptBase { + using stdJson for string; + + function run() + public + returns (address[] memory facets, bytes memory cutData) + { + return update("CCIPFacet"); + } + + function getExcludes() internal pure override returns (bytes4[] memory) { + bytes4[] memory excludes = new bytes4[](1); + excludes[0] = CCIPFacet.initCCIP.selector; + + return excludes; + } + + function getCallData() internal override returns (bytes memory) { + path = string.concat(root, "/config/ccip.json"); + json = vm.readFile(path); + bytes memory rawChainSelectors = json.parseRaw(".chainSelectors"); + CCIPFacet.ChainSelector[] memory chainSelectors = abi.decode( + rawChainSelectors, + (CCIPFacet.ChainSelector[]) + ); + + bytes memory callData = abi.encodeWithSelector( + CCIPFacet.initCCIP.selector, + chainSelectors + ); + + return callData; + } +} diff --git a/src/Facets/CCIPFacet.sol b/src/Facets/CCIPFacet.sol new file mode 100644 index 000000000..e5c8a590e --- /dev/null +++ b/src/Facets/CCIPFacet.sol @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import { ILiFi } from "../Interfaces/ILiFi.sol"; +import { LibDiamond } from "../Libraries/LibDiamond.sol"; +import { LibAsset, IERC20 } from "../Libraries/LibAsset.sol"; +import { LibSwap } from "../Libraries/LibSwap.sol"; +import { ReentrancyGuard } from "../Helpers/ReentrancyGuard.sol"; +import { SwapperV2 } from "../Helpers/SwapperV2.sol"; +import { Validatable } from "../Helpers/Validatable.sol"; +import { Client } from "@chainlink-ccip/v0.8/ccip/libraries/Client.sol"; +import { IRouterClient } from "@chainlink-ccip/v0.8/ccip/interfaces/IRouterClient.sol"; +import { InformationMismatch } from "../Errors/GenericErrors.sol"; + +/// @title CCIP Facet +/// @author Li.Finance (https://li.finance) +/// @notice Allows for bridging assets using Chainlink's CCIP protocol +/// @custom:version 0.0.1 +contract CCIPFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { + /// Storage /// + + bytes32 internal constant NAMESPACE = keccak256("com.lifi.facets.ccip"); // Optional. Only use if you need to store data in the diamond storage. + + // @notice the CCIP router contract + IRouterClient public immutable routerClient; + + /// Types /// + + /// @dev Optional bridge specific struct + /// @param callData The calldata for the destination calldata + /// @param extraArgs The extra arguments for the destination call + struct CCIPData { + bytes callData; + bytes extraArgs; + address receiver; + } + + /// @dev Local storage layout for CCIP + struct Storage { + mapping(uint256 => uint64) chainSelectors; + } + + /// @dev Chain selector for CCIP + /// @param chainId Standard chain ID + /// @param selector CCIP specific chain selector + struct ChainSelector { + uint256 chainId; + uint64 selector; + } + + /// Errors /// + + error UnknownCCIPChainSelector(); + + /// Events /// + + event CCIPInitialized(ChainSelector[] chainSelectors); + + event CCIPChainSelectorUpdated(uint256 indexed chainId, uint64 selector); + + /// Constructor /// + + /// @notice Constructor for the contract. + /// @param _routerClient CCIP router contract. + constructor(IRouterClient _routerClient) { + routerClient = _routerClient; + } + + /// Init /// + + /// @notice Initializes the CCIP facet. + /// @param chainSelectors An array of chain selectors for CCIP + function initCCIP(ChainSelector[] calldata chainSelectors) external { + LibDiamond.enforceIsContractOwner(); + + Storage storage s = getStorage(); + + for (uint256 i = 0; i < chainSelectors.length; i++) { + s.chainSelectors[chainSelectors[i].chainId] = chainSelectors[i] + .selector; + } + + emit CCIPInitialized(chainSelectors); + } + + /// External Methods /// + + /// @notice Bridges tokens via CCIP + /// @param _bridgeData The core information needed for bridging + /// @param _ccipData Data specific to CCIP + function startBridgeTokensViaCCIP( + ILiFi.BridgeData memory _bridgeData, + CCIPData calldata _ccipData + ) + external + payable + nonReentrant + refundExcessNative(payable(msg.sender)) + validateBridgeData(_bridgeData) + doesNotContainSourceSwaps(_bridgeData) + { + validateDestinationCallFlag(_bridgeData, _ccipData); + LibAsset.depositAsset( + _bridgeData.sendingAssetId, + _bridgeData.minAmount + ); + _startBridge(_bridgeData, _ccipData); + } + + /// @notice Performs a swap before bridging via CCIP + /// @param _bridgeData The core information needed for bridging + /// @param _swapData An array of swap related data for performing swaps before bridging + /// @param _ccipData Data specific to CCIP + function swapAndStartBridgeTokensViaCCIP( + ILiFi.BridgeData memory _bridgeData, + LibSwap.SwapData[] calldata _swapData, + CCIPData calldata _ccipData + ) + external + payable + nonReentrant + refundExcessNative(payable(msg.sender)) + containsSourceSwaps(_bridgeData) + validateBridgeData(_bridgeData) + { + validateDestinationCallFlag(_bridgeData, _ccipData); + _bridgeData.minAmount = _depositAndSwap( + _bridgeData.transactionId, + _bridgeData.minAmount, + _swapData, + payable(msg.sender) + ); + _startBridge(_bridgeData, _ccipData); + } + + /// @notice Quotes the fee for bridging via CCIP + /// @param _bridgeData The core information needed for bridging + /// @param _ccipData Data specific to CCIP + function quoteCCIPFee( + ILiFi.BridgeData memory _bridgeData, + CCIPData calldata _ccipData + ) external view returns (uint256) { + Client.EVMTokenAmount[] memory amounts = new Client.EVMTokenAmount[]( + 1 + ); + amounts[0] = Client.EVMTokenAmount({ + token: _bridgeData.sendingAssetId, + amount: _bridgeData.minAmount + }); + + Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ + receiver: abi.encode(_ccipData.receiver), + data: _ccipData.callData, + tokenAmounts: amounts, + feeToken: address(0), + extraArgs: _ccipData.extraArgs + }); + + return + routerClient.getFee( + getCCIPChainSelector(_bridgeData.destinationChainId), + message + ); + } + + /// @notice Encodes the extra arguments for the destination call + /// @param gasLimit The gas limit for the destination call + /// @param strictSequencing Whether or not to use strict sequencing (see https://docs.chain.link/ccip/best-practices#sequencing) + function encodeDestinationArgs( + uint256 gasLimit, + bool strictSequencing + ) external pure returns (bytes memory) { + Client.EVMExtraArgsV1 memory args = Client.EVMExtraArgsV1({ + gasLimit: gasLimit, + strict: strictSequencing + }); + return Client._argsToBytes(args); + } + + /// Internal Methods /// + + /// @dev Contains the business logic for the bridge via CCIP + /// @param _bridgeData The core information needed for bridging + /// @param _ccipData Data specific to CCIP + function _startBridge( + ILiFi.BridgeData memory _bridgeData, + CCIPData calldata _ccipData + ) internal { + Client.EVMTokenAmount[] memory amounts = new Client.EVMTokenAmount[]( + 1 + ); + amounts[0] = Client.EVMTokenAmount({ + token: _bridgeData.sendingAssetId, + amount: _bridgeData.minAmount + }); + + Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ + receiver: abi.encode(_ccipData.receiver), + data: _ccipData.callData, + tokenAmounts: amounts, + feeToken: address(0), + extraArgs: _ccipData.extraArgs + }); + + LibAsset.maxApproveERC20( + IERC20(_bridgeData.sendingAssetId), + address(routerClient), + _bridgeData.minAmount + ); + + routerClient.ccipSend{ value: msg.value }( + getCCIPChainSelector(_bridgeData.destinationChainId), + message + ); + emit LiFiTransferStarted(_bridgeData); + } + + /// @notice Sets the CCIP chain selector for a given chain ID + /// @param _chainId Standard chain ID + /// @param _selector CCIP specific chain selector + /// @dev This is used to map a chain ID to its CCIP chain selector + function setCCIPChainSelector( + uint256 _chainId, + uint64 _selector + ) external { + LibDiamond.enforceIsContractOwner(); + Storage storage s = getStorage(); + + s.chainSelectors[_chainId] = _selector; + emit CCIPChainSelectorUpdated(_chainId, _selector); + } + + /// @notice Gets the CCIP chain selector for a given chain ID + /// @param _chainId Standard chain ID + /// @return selector CCIP specific chain selector + function getCCIPChainSelector( + uint256 _chainId + ) private view returns (uint64) { + Storage storage s = getStorage(); + uint64 selector = s.chainSelectors[_chainId]; + if (selector == 0) revert UnknownCCIPChainSelector(); + return selector; + } + + /// @dev Validates the destination call flag + function validateDestinationCallFlag( + ILiFi.BridgeData memory _bridgeData, + CCIPData calldata _ccipData + ) private pure { + if ( + (_ccipData.callData.length > 0) != _bridgeData.hasDestinationCall + ) { + revert InformationMismatch(); + } + } + + /// @dev fetch local storage + function getStorage() private pure returns (Storage storage s) { + bytes32 namespace = NAMESPACE; + // solhint-disable-next-line no-inline-assembly + assembly { + s.slot := namespace + } + } +} diff --git a/src/Periphery/CCIPMsgReceiver.sol b/src/Periphery/CCIPMsgReceiver.sol new file mode 100644 index 000000000..cafa7cc5c --- /dev/null +++ b/src/Periphery/CCIPMsgReceiver.sol @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import { IERC20, SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import { ReentrancyGuard } from "../Helpers/ReentrancyGuard.sol"; +import { LibSwap } from "../Libraries/LibSwap.sol"; +import { LibAsset } from "../Libraries/LibAsset.sol"; +import { ILiFi } from "../Interfaces/ILiFi.sol"; +import { IExecutor } from "../Interfaces/IExecutor.sol"; +import { TransferrableOwnership } from "../Helpers/TransferrableOwnership.sol"; +import { ExternalCallFailed, UnAuthorized } from "../Errors/GenericErrors.sol"; +import { Client } from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol"; +import { CCIPReceiver as CCIPRcvr } from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol"; + +/// @title CCIPReceiver +/// @author LI.FI (https://li.fi) +/// @notice Arbitrary execution contract used for cross-chain swaps +// and message passing via Chainlink CCIP +/// @custom:version 0.0.1 +contract CCIPMsgReceiver is + CCIPRcvr, + ILiFi, + ReentrancyGuard, + TransferrableOwnership +{ + using SafeERC20 for IERC20; + + /// Storage /// + IExecutor public executor; + uint256 public recoverGas; + + /// Events /// + event CCIPRouterSet(address indexed router); + event ExecutorSet(address indexed executor); + event RecoverGasSet(uint256 indexed recoverGas); + + constructor( + address _owner, + address _router, + address _executor, + uint256 _recoverGas + ) TransferrableOwnership(_owner) CCIPRcvr(_router) { + executor = IExecutor(_executor); + recoverGas = _recoverGas; + emit CCIPRouterSet(_router); + emit ExecutorSet(_executor); + emit RecoverGasSet(_recoverGas); + } + + /// @notice Send remaining token to receiver + /// @param assetId token received from the other chain + /// @param receiver address that will receive tokens in the end + /// @param amount amount of token + function pullToken( + address assetId, + address payable receiver, + uint256 amount + ) external onlyOwner { + if (LibAsset.isNativeAsset(assetId)) { + // solhint-disable-next-line avoid-low-level-calls + (bool success, ) = receiver.call{ value: amount }(""); + if (!success) revert ExternalCallFailed(); + } else { + IERC20(assetId).safeTransfer(receiver, amount); + } + } + + /// Internal Functions /// + + /// @notice Receive CCIP message and execute arbitrary calls + /// @param message CCIP message + function _ccipReceive( + Client.Any2EVMMessage memory message + ) internal override { + // Extract swap data from message + ( + bytes32 transactionId, + LibSwap.SwapData[] memory swapData, + address receiver + ) = abi.decode(message.data, (bytes32, LibSwap.SwapData[], address)); + _swapAndCompleteBridgeTokens( + transactionId, + swapData, + message.destTokenAmounts[0].token, + payable(receiver), + message.destTokenAmounts[0].amount + ); + } + + /// @notice Performs a swap before completing a cross-chain transaction + /// @param _transactionId the transaction id associated with the operation + /// @param _swapData array of data needed for swaps + /// @param assetId token received from the other chain + /// @param receiver address that will receive tokens in the end + /// @param amount amount of token + function _swapAndCompleteBridgeTokens( + bytes32 _transactionId, + LibSwap.SwapData[] memory _swapData, + address assetId, + address payable receiver, + uint256 amount + ) private { + if (LibAsset.isNativeAsset(assetId)) { + // case 1: native asset + uint256 cacheGasLeft = gasleft(); + if (cacheGasLeft < recoverGas) { + // case 1a: not enough gas left to execute calls + // solhint-disable-next-line avoid-low-level-calls + (bool success, ) = receiver.call{ value: amount }(""); + if (!success) revert ExternalCallFailed(); + + emit LiFiTransferRecovered( + _transactionId, + assetId, + receiver, + amount, + block.timestamp + ); + return; + } + + // case 1b: enough gas left to execute calls + // solhint-disable no-empty-blocks + try + executor.swapAndCompleteBridgeTokens{ + value: amount, + gas: cacheGasLeft - recoverGas + }(_transactionId, _swapData, assetId, receiver) + {} catch { + // solhint-disable-next-line avoid-low-level-calls + (bool success, ) = receiver.call{ value: amount }(""); + if (!success) revert ExternalCallFailed(); + + emit LiFiTransferRecovered( + _transactionId, + assetId, + receiver, + amount, + block.timestamp + ); + } + } else { + // case 2: ERC20 asset + uint256 cacheGasLeft = gasleft(); + IERC20 token = IERC20(assetId); + token.safeApprove(address(executor), 0); + + if (cacheGasLeft < recoverGas) { + // case 2a: not enough gas left to execute calls + token.safeTransfer(receiver, amount); + + emit LiFiTransferRecovered( + _transactionId, + assetId, + receiver, + amount, + block.timestamp + ); + return; + } + + // case 2b: enough gas left to execute calls + token.safeIncreaseAllowance(address(executor), amount); + try + executor.swapAndCompleteBridgeTokens{ + gas: cacheGasLeft - recoverGas + }(_transactionId, _swapData, assetId, receiver) + {} catch { + token.safeTransfer(receiver, amount); + emit LiFiTransferRecovered( + _transactionId, + assetId, + receiver, + amount, + block.timestamp + ); + } + + token.safeApprove(address(executor), 0); + } + } + + /// @notice Receive native asset directly. + /// @dev Some bridges may send native asset before execute external calls. + // solhint-disable-next-line no-empty-blocks + receive() external payable {} +} diff --git a/test/solidity/Facets/CCIPFacet.t.sol b/test/solidity/Facets/CCIPFacet.t.sol new file mode 100644 index 000000000..eeead3c4d --- /dev/null +++ b/test/solidity/Facets/CCIPFacet.t.sol @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity 0.8.17; + +import { DSTest } from "ds-test/test.sol"; +import { DiamondTest, LiFiDiamond } from "../utils/DiamondTest.sol"; +import { Vm } from "forge-std/Vm.sol"; +import { Test } from "forge-std/Test.sol"; +import { CCIPFacet } from "lifi/Facets/CCIPFacet.sol"; +import { ILiFi } from "lifi/Interfaces/ILiFi.sol"; +import { LibSwap } from "lifi/Libraries/LibSwap.sol"; +import { LibAllowList } from "lifi/Libraries/LibAllowList.sol"; +import { ERC20 } from "solmate/tokens/ERC20.sol"; +import { UniswapV2Router02 } from "../utils/Interfaces.sol"; +import { IRouterClient } from "@chainlink-ccip/v0.8/ccip/interfaces/IRouterClient.sol"; +import "lifi/Errors/GenericErrors.sol"; + +// Stub CCIPFacet Contract +contract TestCCIPFacet is CCIPFacet { + constructor(IRouterClient _routerClient) CCIPFacet(_routerClient) {} + + function addDex(address _dex) external { + LibAllowList.addAllowedContract(_dex); + } + + function setFunctionApprovalBySignature(bytes4 _signature) external { + LibAllowList.addAllowedSelector(_signature); + } +} + +contract FooSwap is Test { + function swap( + ERC20 inToken, + ERC20 outToken, + uint256 inAmount, + uint256 outAmount + ) external { + inToken.transferFrom(msg.sender, address(this), inAmount); + deal(address(outToken), msg.sender, outAmount); + } +} + +contract CCIPFacetTest is Test, DiamondTest { + // These values are for BSC Testnet + address internal constant USDC_ADDRESS = + 0x64544969ed7EBf5f083679233325356EbE738930; + address internal constant CCIP_TEST_TOKEN_ADDRESS = + 0x79a4Fc27f69323660f5Bfc12dEe21c3cC14f5901; // CCIP Burn & Mint Test Token + address internal constant ROUTER_CLIENT = + 0x9527E2d01A3064ef6b50c1Da1C0cC523803BCFF2; + uint256 internal constant DSTCHAIN_ID = 11155111; // Sepolia + + // ----- + + LiFiDiamond internal diamond; + TestCCIPFacet internal ccipFacet; + ERC20 internal usdc; + ERC20 internal ccipTestToken; + ILiFi.BridgeData internal validBridgeData; + CCIPFacet.CCIPData internal validCCIPData; + FooSwap internal fooSwap; + + function fork() internal { + string memory rpcUrl = vm.envString("ETH_NODE_URI_BSC_TESTNET"); + uint256 blockNumber = 33259557; + vm.createSelectFork(rpcUrl, blockNumber); + } + + function setUp() public { + fork(); + + diamond = createDiamond(); + ccipFacet = new TestCCIPFacet(IRouterClient(ROUTER_CLIENT)); + ccipTestToken = ERC20(CCIP_TEST_TOKEN_ADDRESS); + usdc = ERC20(USDC_ADDRESS); + fooSwap = new FooSwap(); + + bytes4[] memory functionSelectors = new bytes4[](7); + functionSelectors[0] = ccipFacet.startBridgeTokensViaCCIP.selector; + functionSelectors[1] = ccipFacet + .swapAndStartBridgeTokensViaCCIP + .selector; + functionSelectors[2] = ccipFacet.initCCIP.selector; + functionSelectors[3] = ccipFacet.addDex.selector; + functionSelectors[4] = ccipFacet + .setFunctionApprovalBySignature + .selector; + functionSelectors[5] = ccipFacet.quoteCCIPFee.selector; + functionSelectors[6] = ccipFacet.encodeDestinationArgs.selector; + + addFacet(diamond, address(ccipFacet), functionSelectors); + + CCIPFacet.ChainSelector[] + memory configs = new CCIPFacet.ChainSelector[](2); + configs[0] = CCIPFacet.ChainSelector(97, 13264668187771770619); // BSC Testnet + configs[1] = CCIPFacet.ChainSelector(11155111, 16015286601757825753); // Sepolia + + ccipFacet = TestCCIPFacet(address(diamond)); + ccipFacet.initCCIP(configs); + + ccipFacet.addDex(address(fooSwap)); + ccipFacet.setFunctionApprovalBySignature(fooSwap.swap.selector); + + vm.label(CCIP_TEST_TOKEN_ADDRESS, "CCIP Test Token"); + vm.label(USDC_ADDRESS, "USDC Token"); + vm.label(ROUTER_CLIENT, "CCIP Router"); + + validBridgeData = ILiFi.BridgeData({ + transactionId: "ccipId", + bridge: "ccip", + integrator: "", + referrer: address(0), + sendingAssetId: CCIP_TEST_TOKEN_ADDRESS, + receiver: address(this), + minAmount: 10 * 10 ** ccipTestToken.decimals(), + destinationChainId: DSTCHAIN_ID, + hasSourceSwaps: false, + hasDestinationCall: false + }); + validCCIPData = CCIPFacet.CCIPData("", "", address(this)); + } + + function testRevertToBridgeTokensWhenSendingAmountIsZero() public { + deal( + address(ccipTestToken), + address(this), + 10 * 10 ** ccipTestToken.decimals() + ); + + ccipTestToken.approve( + address(ccipFacet), + 10_000 * 10 ** ccipTestToken.decimals() + ); + + ILiFi.BridgeData memory bridgeData = validBridgeData; + bridgeData.minAmount = 0; + + vm.expectRevert(InvalidAmount.selector); + ccipFacet.startBridgeTokensViaCCIP(bridgeData, validCCIPData); + } + + function testRevertToBridgeTokensWhenReceiverIsZeroAddress() public { + deal( + address(ccipTestToken), + address(this), + 10 * 10 ** ccipTestToken.decimals() + ); + + ccipTestToken.approve( + address(ccipFacet), + 10_000 * 10 ** ccipTestToken.decimals() + ); + + ILiFi.BridgeData memory bridgeData = validBridgeData; + bridgeData.receiver = address(0); + + vm.expectRevert(InvalidReceiver.selector); + ccipFacet.startBridgeTokensViaCCIP(bridgeData, validCCIPData); + } + + function testRevertToBridgeTokensWhenInformationMismatch() public { + deal( + address(ccipTestToken), + address(this), + 10 * 10 ** ccipTestToken.decimals() + ); + + ccipTestToken.approve( + address(ccipFacet), + 10_000 * 10 ** ccipTestToken.decimals() + ); + + ILiFi.BridgeData memory bridgeData = validBridgeData; + bridgeData.hasSourceSwaps = true; + + vm.expectRevert(InformationMismatch.selector); + ccipFacet.startBridgeTokensViaCCIP(bridgeData, validCCIPData); + } + + function testCanBridgeERC20Tokens() public { + deal( + address(ccipTestToken), + address(this), + 10 * 10 ** ccipTestToken.decimals() + ); + + ccipTestToken.approve( + address(ccipFacet), + 10_000 * 10 ** ccipTestToken.decimals() + ); + + uint256 fee = ccipFacet.quoteCCIPFee(validBridgeData, validCCIPData); + + ccipFacet.startBridgeTokensViaCCIP{ value: fee }( + validBridgeData, + validCCIPData + ); + } + + function testCanSwapAndBridgeTokens() public { + deal(address(usdc), address(this), 10_000 * 10 ** usdc.decimals()); + usdc.approve(address(ccipFacet), 10_000 * 10 ** usdc.decimals()); + + uint256 inAmount = 10_000 * 10 ** usdc.decimals(); + uint256 outAmount = 10_000 * 10 ** ccipTestToken.decimals(); + + // Calculate DAI amount + LibSwap.SwapData[] memory swapData = new LibSwap.SwapData[](1); + swapData[0] = LibSwap.SwapData( + address(fooSwap), + address(fooSwap), + USDC_ADDRESS, + CCIP_TEST_TOKEN_ADDRESS, + inAmount, + abi.encodeWithSelector( + fooSwap.swap.selector, + ERC20(USDC_ADDRESS), + ERC20(CCIP_TEST_TOKEN_ADDRESS), + inAmount, + outAmount + ), + true + ); + + ILiFi.BridgeData memory bridgeData = validBridgeData; + bridgeData.hasSourceSwaps = true; + + uint256 fee = ccipFacet.quoteCCIPFee(validBridgeData, validCCIPData); + + ccipFacet.swapAndStartBridgeTokensViaCCIP{ value: fee }( + bridgeData, + swapData, + validCCIPData + ); + } + + function testCanBridgeERC20TokensWithDestinationCall() public { + deal( + address(ccipTestToken), + address(this), + 10 * 10 ** ccipTestToken.decimals() + ); + + ccipTestToken.approve( + address(ccipFacet), + 10_000 * 10 ** ccipTestToken.decimals() + ); + + bytes memory callData = abi.encodeWithSignature("foo()"); + bytes memory args = ccipFacet.encodeDestinationArgs({ + gasLimit: 100000, + strictSequencing: false + }); + + validCCIPData.callData = callData; + validCCIPData.extraArgs = args; + validBridgeData.hasDestinationCall = true; + + uint256 fee = ccipFacet.quoteCCIPFee(validBridgeData, validCCIPData); + + ccipFacet.startBridgeTokensViaCCIP{ value: fee }( + validBridgeData, + validCCIPData + ); + } + + function testCanSwapAndBridgeTokensWithDestinationCall() public { + deal(address(usdc), address(this), 10_000 * 10 ** usdc.decimals()); + usdc.approve(address(ccipFacet), 10_000 * 10 ** usdc.decimals()); + + uint256 inAmount = 10_000 * 10 ** usdc.decimals(); + uint256 outAmount = 10_000 * 10 ** ccipTestToken.decimals(); + + // Calculate DAI amount + LibSwap.SwapData[] memory swapData = new LibSwap.SwapData[](1); + swapData[0] = LibSwap.SwapData( + address(fooSwap), + address(fooSwap), + USDC_ADDRESS, + CCIP_TEST_TOKEN_ADDRESS, + inAmount, + abi.encodeWithSelector( + fooSwap.swap.selector, + ERC20(USDC_ADDRESS), + ERC20(CCIP_TEST_TOKEN_ADDRESS), + inAmount, + outAmount + ), + true + ); + + ILiFi.BridgeData memory bridgeData = validBridgeData; + bridgeData.hasSourceSwaps = true; + + bytes memory callData = abi.encodeWithSignature("foo()"); + bytes memory args = ccipFacet.encodeDestinationArgs({ + gasLimit: 100000, + strictSequencing: false + }); + + validCCIPData.callData = callData; + validCCIPData.extraArgs = args; + bridgeData.hasDestinationCall = true; + + uint256 fee = ccipFacet.quoteCCIPFee(validBridgeData, validCCIPData); + + ccipFacet.swapAndStartBridgeTokensViaCCIP{ value: fee }( + bridgeData, + swapData, + validCCIPData + ); + } +} diff --git a/test/solidity/Periphery/CCIPMsgReceiver.t.sol b/test/solidity/Periphery/CCIPMsgReceiver.t.sol new file mode 100644 index 000000000..ba02a0f92 --- /dev/null +++ b/test/solidity/Periphery/CCIPMsgReceiver.t.sol @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity 0.8.17; + +import { Test, TestBase, LiFiDiamond, DSTest, ILiFi, LibSwap, LibAllowList, console, InvalidAmount, ERC20, UniswapV2Router02 } from "../utils/TestBase.sol"; +import { OnlyContractOwner } from "src/Errors/GenericErrors.sol"; +import { CCIPMsgReceiver } from "lifi/Periphery/CCIPMsgReceiver.sol"; +import { stdJson } from "forge-std/Script.sol"; +import { ERC20Proxy } from "lifi/Periphery/ERC20Proxy.sol"; +import { Executor } from "lifi/Periphery/Executor.sol"; +import { Client } from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol"; + +contract CCIPMsgReceiverTest is TestBase { + using stdJson for string; + + CCIPMsgReceiver internal receiver; + + error UnAuthorized(); + + string path; + string json; + address ccipRouter; + Executor executor; + ERC20Proxy erc20Proxy; + + event CCIPRouterSet(address indexed router); + event ExecutorSet(address indexed executor); + event RecoverGasSet(uint256 indexed recoverGas); + + function setUp() public { + initTestBase(); + + // obtain address of Stargate router in current network from config file + path = string.concat(vm.projectRoot(), "/config/ccip.json"); + json = vm.readFile(path); + ccipRouter = json.readAddress( + string.concat(".routers.mainnet.router") + ); + + erc20Proxy = new ERC20Proxy(address(this)); + executor = new Executor(address(erc20Proxy)); + receiver = new CCIPMsgReceiver( + address(this), + ccipRouter, + address(executor), + 100000 + ); + vm.label(address(receiver), "Receiver"); + vm.label(address(executor), "Executor"); + vm.label(address(erc20Proxy), "ERC20Proxy"); + vm.label(ccipRouter, "CCIPRouter"); + } + + function test_revert_OwnerCanPullToken() public { + // send token to receiver + vm.startPrank(USER_SENDER); + dai.transfer(address(receiver), 1000); + vm.stopPrank(); + + // pull token + vm.startPrank(USER_DIAMOND_OWNER); + + receiver.pullToken(ADDRESS_DAI, payable(USER_RECEIVER), 1000); + + assertEq(1000, dai.balanceOf(USER_RECEIVER)); + } + + function test_revert_PullTokenNonOwner() public { + vm.startPrank(USER_SENDER); + vm.expectRevert(UnAuthorized.selector); + receiver.pullToken(ADDRESS_DAI, payable(USER_RECEIVER), 1000); + } + + function test_CCIP_ExecutesCrossChainMessage() public { + // create swap data + delete swapData; + // Swap DAI -> USDC + address[] memory swapPath = new address[](2); + swapPath[0] = ADDRESS_DAI; + swapPath[1] = ADDRESS_USDC; + + uint256 amountOut = defaultUSDCAmount; + + // Calculate DAI amount + uint256[] memory amounts = uniswap.getAmountsIn(amountOut, swapPath); + uint256 amountIn = amounts[0]; + + swapData.push( + LibSwap.SwapData({ + callTo: address(uniswap), + approveTo: address(uniswap), + sendingAssetId: ADDRESS_DAI, + receivingAssetId: ADDRESS_USDC, + fromAmount: amountIn, + callData: abi.encodeWithSelector( + uniswap.swapExactTokensForTokens.selector, + amountIn, + amountOut, + swapPath, + address(executor), + block.timestamp + 20 minutes + ), + requiresDeposit: true + }) + ); + + // create callData that will be sent to our CCIPMsgReceiver + bytes32 txId = keccak256("txId"); + bytes memory payload = abi.encode(txId, swapData); + + // fund receiver with sufficient DAI to execute swap + vm.startPrank(USER_DAI_WHALE); + dai.transfer(address(receiver), swapData[0].fromAmount); + vm.stopPrank(); + + // call sgReceive function as Stargate router + vm.startPrank(ccipRouter); + dai.approve(address(receiver), swapData[0].fromAmount); + + // prepare check for events + vm.expectEmit(true, true, true, true, address(executor)); + emit AssetSwapped( + txId, + address(uniswap), + ADDRESS_DAI, + ADDRESS_USDC, + swapData[0].fromAmount, + defaultUSDCAmount, + block.timestamp + ); + vm.expectEmit(true, true, true, true, address(executor)); + emit LiFiTransferCompleted( + txId, + ADDRESS_DAI, + USER_RECEIVER, + defaultUSDCAmount, + block.timestamp + ); + + Client.EVMTokenAmount[] + memory tokenAmounts = new Client.EVMTokenAmount[](1); + tokenAmounts[0] = Client.EVMTokenAmount({ + token: ADDRESS_DAI, + amount: swapData[0].fromAmount + }); + + Client.Any2EVMMessage memory message = Client.Any2EVMMessage({ + messageId: txId, + sourceChainSelector: 3734403246176062136, // Optimism chain selector, + sender: abi.encode(address(receiver)), + data: payload, + destTokenAmounts: tokenAmounts + }); + + // call ccipReceive function to complete transaction + receiver.ccipReceive(message); + } + + function test_CCIP_EmitsCorrectEventOnRecovery() public { + // (mock) transfer "bridged funds" to CCIPMsgReceiver.sol + bytes32 txId = keccak256("txId"); + vm.startPrank(USER_SENDER); + usdc.transfer(address(receiver), defaultUSDCAmount); + vm.stopPrank(); + + bytes memory payload = abi.encode(txId, swapData); + + vm.startPrank(ccipRouter); + vm.expectEmit(true, true, true, true, address(receiver)); + emit LiFiTransferRecovered( + txId, + ADDRESS_USDC, + address(receiver), + defaultUSDCAmount, + block.timestamp + ); + + Client.EVMTokenAmount[] + memory tokenAmounts = new Client.EVMTokenAmount[](1); + tokenAmounts[0] = Client.EVMTokenAmount({ + token: ADDRESS_USDC, + amount: defaultUSDCAmount + }); + + Client.Any2EVMMessage memory message = Client.Any2EVMMessage({ + messageId: txId, + sourceChainSelector: 3734403246176062136, // Optimism chain selector, + sender: abi.encode(address(receiver)), + data: payload, + destTokenAmounts: tokenAmounts + }); + + // call ccipReceive function to complete transaction + receiver.ccipReceive(message); + } +} diff --git a/yarn.lock b/yarn.lock index 1ec991099..4136a2190 100644 --- a/yarn.lock +++ b/yarn.lock @@ -38,6 +38,16 @@ resolved "https://registry.yarnpkg.com/@balena/dockerignore/-/dockerignore-1.0.2.tgz#9ffe4726915251e8eb69f44ef3547e0da2c03e0d" integrity sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q== +"@chainlink/contracts-ccip@^0.7.6": + version "0.7.6" + resolved "https://registry.yarnpkg.com/@chainlink/contracts-ccip/-/contracts-ccip-0.7.6.tgz#5bf4568a0bbf4e29d2e8c32348e5ecc6ced006d2" + integrity sha512-yNbCBFpLs3R+ALymto9dQYKz3vatnjqYGu1pnMD0i2fHEMthiXe0+otaNCGNht6n8k7ruNaA0DNpz3F+2jHQXw== + dependencies: + "@eth-optimism/contracts" "^0.5.21" + "@openzeppelin/contracts" "~4.3.3" + "@openzeppelin/contracts-upgradeable-4.7.3" "npm:@openzeppelin/contracts-upgradeable@v4.7.3" + "@openzeppelin/contracts-v0.7" "npm:@openzeppelin/contracts@v3.4.2" + "@chainsafe/as-sha256@^0.3.1": version "0.3.1" resolved "https://registry.yarnpkg.com/@chainsafe/as-sha256/-/as-sha256-0.3.1.tgz#3639df0e1435cab03f4d9870cc3ac079e57a6fc9" @@ -105,6 +115,15 @@ "@ethersproject/abstract-provider" "^5.7.0" "@ethersproject/abstract-signer" "^5.7.0" +"@eth-optimism/contracts@^0.5.21": + version "0.5.40" + resolved "https://registry.yarnpkg.com/@eth-optimism/contracts/-/contracts-0.5.40.tgz#d13a04a15ea947a69055e6fc74d87e215d4c936a" + integrity sha512-MrzV0nvsymfO/fursTB7m/KunkPsCndltVgfdHaT1Aj5Vi6R/doKIGGkOofHX+8B6VMZpuZosKCMQ5lQuqjt8w== + dependencies: + "@eth-optimism/core-utils" "0.12.0" + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@eth-optimism/core-utils@0.12.0": version "0.12.0" resolved "https://registry.yarnpkg.com/@eth-optimism/core-utils/-/core-utils-0.12.0.tgz#6337e4599a34de23f8eceb20378de2a2de82b0ea" @@ -958,6 +977,21 @@ resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-waffle/-/hardhat-waffle-2.0.5.tgz#97c217f1db795395c04404291937edb528f3f218" integrity sha512-U1RH9OQ1mWYQfb+moX5aTgGjpVVlOcpiFI47wwnaGG4kLhcTy90cNiapoqZenxcRAITVbr0/+QSduINL5EsUIQ== +"@openzeppelin/contracts-upgradeable-4.7.3@npm:@openzeppelin/contracts-upgradeable@v4.7.3": + version "4.7.3" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.7.3.tgz#f1d606e2827d409053f3e908ba4eb8adb1dd6995" + integrity sha512-+wuegAMaLcZnLCJIvrVUDzA9z/Wp93f0Dla/4jJvIhijRrPabjQbZe6fWiECLaJyfn5ci9fqf9vTw3xpQOad2A== + +"@openzeppelin/contracts-v0.7@npm:@openzeppelin/contracts@v3.4.2": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.2.tgz#d81f786fda2871d1eb8a8c5a73e455753ba53527" + integrity sha512-z0zMCjyhhp4y7XKAcDAi3Vgms4T2PstwBdahiO0+9NaGICQKjynK3wduSRplTgk4LXmoO1yfDGO5RbjKYxtuxA== + +"@openzeppelin/contracts@~4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.3.3.tgz#ff6ee919fc2a1abaf72b22814bfb72ed129ec137" + integrity sha512-tDBopO1c98Yk7Cv/PZlHqrvtVjlgK5R4J6jxLwoO7qxK4xqOiZG+zSkIvGFpPZ0ikc3QOED3plgdqjgNTnBc7g== + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"