From a9e6c3717bdee052493166a521ae0b389d751c12 Mon Sep 17 00:00:00 2001 From: Max Klenk Date: Tue, 30 Apr 2024 10:07:24 +0700 Subject: [PATCH] feat(squid): Add squid bridge 1.0.0 (#496) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Revert "chore: move squid implementation" This reverts commit d51688fe0009480261a1a04a37b8cc363b3b3b27. * deploy: staging deployments * fix: updates implementation to work with new Squid router implementation incl. tests * deploy: staging re-deployments (BSC,POL) * fix: removes check for token symbol from facet (causing errors when Squid internally swqps e.g. USDC > axlUSDC) * deploy: staging re-deployments (BSC,POL) v0.0.3 * deploy: staging re-deployments (BSC,POL) v0.0.4 * fix: removes failing test * deploy: BSC & POL staging redeployment * deploy: redeploy to POL staging * deploy: various staging redeployments * fix: fix failing tests * adds missing config values * Deploy to some chains * Deploy to remaining chains and add to diamond --------- Co-authored-by: Ed Zynda Co-authored-by: Daniel <77058885+0xDEnYO@users.noreply.github.com> Co-authored-by: Daniel Bläcker Co-authored-by: Ed Zynda --- config/squid.json | 38 ++ deployments/_deployments_log_file.json | 302 ++++++++++++++ deployments/arbitrum.diamond.json | 6 +- deployments/arbitrum.json | 3 +- deployments/avalanche.diamond.json | 4 + deployments/avalanche.diamond.staging.json | 6 +- deployments/avalanche.json | 3 +- deployments/avalanche.staging.json | 3 +- deployments/base.diamond.json | 4 + deployments/base.json | 5 +- deployments/bsc.diamond.json | 4 + deployments/bsc.diamond.staging.json | 22 +- deployments/bsc.json | 3 +- deployments/bsc.staging.json | 2 +- deployments/fantom.diamond.json | 4 + deployments/fantom.json | 3 +- deployments/linea.diamond.json | 4 + deployments/linea.json | 3 +- deployments/mainnet.diamond.json | 4 + deployments/mainnet.json | 3 +- deployments/moonbeam.diamond.json | 4 + deployments/moonbeam.json | 3 +- deployments/optimism.diamond.json | 4 + deployments/optimism.json | 3 +- deployments/polygon.diamond.json | 4 + deployments/polygon.diamond.staging.json | 6 +- deployments/polygon.json | 3 +- deployments/polygon.staging.json | 2 +- deployments/scroll.diamond.json | 6 +- deployments/scroll.json | 3 +- docs/README.md | 2 + docs/SquidFacet.md | 79 ++++ launchJob | 6 + script/demoScripts/demoSquid.ts | 114 ++++++ script/deploy/facets/DeploySquidFacet.s.sol | 32 ++ script/deploy/facets/UpdateSquidFacet.s.sol | 13 + .../deploy/resources/deployRequirements.json | 18 + script/helperFunctions.sh | 12 +- src/Facets/SquidFacet.sol | 212 ++++++++++ src/Interfaces/ISquidMulticall.sol | 19 + src/Interfaces/ISquidRouter.sol | 37 ++ test/solidity/Facets/SquidFacet.t.sol | 387 ++++++++++++++++++ test/solidity/utils/Interfaces.sol | 8 + 43 files changed, 1371 insertions(+), 32 deletions(-) create mode 100644 config/squid.json create mode 100644 docs/SquidFacet.md create mode 100644 launchJob create mode 100644 script/demoScripts/demoSquid.ts create mode 100644 script/deploy/facets/DeploySquidFacet.s.sol create mode 100644 script/deploy/facets/UpdateSquidFacet.s.sol create mode 100644 src/Facets/SquidFacet.sol create mode 100644 src/Interfaces/ISquidMulticall.sol create mode 100644 src/Interfaces/ISquidRouter.sol create mode 100644 test/solidity/Facets/SquidFacet.t.sol diff --git a/config/squid.json b/config/squid.json new file mode 100644 index 000000000..56a0eaa70 --- /dev/null +++ b/config/squid.json @@ -0,0 +1,38 @@ +{ + "mainnet": { + "router": "0xce16F69375520ab01377ce7B88f5BA8C48F8D666" + }, + "polygon": { + "router": "0xce16F69375520ab01377ce7B88f5BA8C48F8D666" + }, + "bsc": { + "router": "0xce16F69375520ab01377ce7B88f5BA8C48F8D666" + }, + "arbitrum": { + "router": "0xce16F69375520ab01377ce7B88f5BA8C48F8D666" + }, + "avalanche": { + "router": "0xce16F69375520ab01377ce7B88f5BA8C48F8D666" + }, + "fantom": { + "router": "0xce16F69375520ab01377ce7B88f5BA8C48F8D666" + }, + "celo": { + "router": "0xce16F69375520ab01377ce7B88f5BA8C48F8D666" + }, + "moonbeam": { + "router": "0xce16F69375520ab01377ce7B88f5BA8C48F8D666" + }, + "base": { + "router": "0xce16F69375520ab01377ce7B88f5BA8C48F8D666" + }, + "scroll": { + "router": "0xce16F69375520ab01377ce7B88f5BA8C48F8D666" + }, + "linea": { + "router": "0xce16F69375520ab01377ce7B88f5BA8C48F8D666" + }, + "optimism": { + "router": "0xce16F69375520ab01377ce7B88f5BA8C48F8D666" + } +} diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index 9a5e62d59..fd5b75c27 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -14474,6 +14474,88 @@ "SALT": "27062023", "VERIFIED": "true" } + ], + "0.0.2": [ + { + "ADDRESS": "0x2e8B3C5732c4440264d6fCF14843F5F793926DD8", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-12-19 12:49:43", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "true" + } + ], + "0.0.3": [ + { + "ADDRESS": "0xa1Be6Aaa705545f79735d42852e77720B168392e", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-12-20 09:12:44", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "true" + } + ], + "0.0.4": [ + { + "ADDRESS": "0x8Aa355a7f67351F00Dfd84a6A402CA0e97E80311", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-09 12:16:28", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "true" + } + ], + "0.0.5": [ + { + "ADDRESS": "0xe932f4B167a4C9ED70EaEEe69c976C558B3bD9a9", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-09 12:20:48", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "true" + } + ], + "0.0.6": [ + { + "ADDRESS": "0x3F5C241E7FD2dBa3a7F0c0d17295c6f7137D32D3", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-09 13:55:12", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "true" + } + ], + "0.0.7": [ + { + "ADDRESS": "0x94f45ea923b5A99c8C6Bcc3178e78Bd13D2dAC56", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-10 11:54:43", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "true" + } + ], + "0.0.8": [ + { + "ADDRESS": "0x8BFC9f022ACb65dfa0Eb6CCbA45c04C2a6cb9A34", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-10 17:24:03", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "true" + } + ] + }, + "production": { + "1.0.0": [ + { + "ADDRESS": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-29 13:02:55", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "false" + } ] } }, @@ -14489,6 +14571,18 @@ "VERIFIED": "true" } ] + }, + "production": { + "1.0.0": [ + { + "ADDRESS": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-26 16:21:42", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "true" + } + ] } }, "mainnet": { @@ -14503,6 +14597,214 @@ "VERIFIED": "true" } ] + }, + "production": { + "1.0.0": [ + { + "ADDRESS": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-29 13:54:33", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "avalanche": { + "staging": { + "1.0.0": [ + { + "ADDRESS": "0x9951B2384a36a439C2afAfFf12c43F88Babde7c1", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-12-14 12:47:14", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "false" + } + ] + }, + "production": { + "1.0.0": [ + { + "ADDRESS": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-29 13:01:48", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "bsc": { + "staging": { + "0.0.2": [ + { + "ADDRESS": "0x2e8B3C5732c4440264d6fCF14843F5F793926DD8", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-12-19 12:40:31", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "true" + } + ], + "0.0.3": [ + { + "ADDRESS": "0xa1Be6Aaa705545f79735d42852e77720B168392e", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2023-12-20 09:10:25", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "true" + } + ], + "0.0.4": [ + { + "ADDRESS": "0x8Aa355a7f67351F00Dfd84a6A402CA0e97E80311", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-09 10:54:18", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "false" + } + ], + "0.0.5": [ + { + "ADDRESS": "0xe932f4B167a4C9ED70EaEEe69c976C558B3bD9a9", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-09 12:23:10", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "true" + } + ], + "0.0.6": [ + { + "ADDRESS": "0x3F5C241E7FD2dBa3a7F0c0d17295c6f7137D32D3", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-09 14:14:20", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "false" + } + ], + "0.0.7": [ + { + "ADDRESS": "0x94f45ea923b5A99c8C6Bcc3178e78Bd13D2dAC56", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-10 11:54:31", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "true" + } + ], + "0.0.8": [ + { + "ADDRESS": "0x8BFC9f022ACb65dfa0Eb6CCbA45c04C2a6cb9A34", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-10 17:24:17", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "true" + } + ] + }, + "production": { + "1.0.0": [ + { + "ADDRESS": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-26 16:21:28", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "fantom": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-26 17:33:38", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "moonbeam": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-29 13:05:06", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "false" + } + ] + } + }, + "base": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-29 13:05:54", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "optimism": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-29 13:06:31", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "scroll": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-29 13:21:59", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "false" + } + ] + } + }, + "linea": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0xEa8EF3E532db49915FDbDdB29244a83E76768111", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-04-29 13:20:16", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666", + "SALT": "", + "VERIFIED": "false" + } + ] } } }, diff --git a/deployments/arbitrum.diamond.json b/deployments/arbitrum.diamond.json index 1caee7bd5..5c173c5f2 100644 --- a/deployments/arbitrum.diamond.json +++ b/deployments/arbitrum.diamond.json @@ -108,6 +108,10 @@ "0xe12b2488c71432F9a116E9ac244D3Ef4c2386d3a": { "Name": "SymbiosisFacet", "Version": "1.0.0" + }, + "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c": { + "Name": "SquidFacet", + "Version": "1.0.0" } }, "Periphery": { @@ -121,4 +125,4 @@ "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } -} \ No newline at end of file +} diff --git a/deployments/arbitrum.json b/deployments/arbitrum.json index f5a810687..2b1b2eb2d 100644 --- a/deployments/arbitrum.json +++ b/deployments/arbitrum.json @@ -41,5 +41,6 @@ "AcrossFacetPacked": "0xE397c4883ec89ed4Fc9D258F00C689708b2799c9", "AmarokFacetPacked": "0xF18A285f4e6f720Eb9b4e05df71f88b9552E6ADB", "SymbiosisFacet": "0xe12b2488c71432F9a116E9ac244D3Ef4c2386d3a", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "SquidFacet": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c" } \ No newline at end of file diff --git a/deployments/avalanche.diamond.json b/deployments/avalanche.diamond.json index cc7c497fd..eb0a56300 100644 --- a/deployments/avalanche.diamond.json +++ b/deployments/avalanche.diamond.json @@ -80,6 +80,10 @@ "0xe12b2488c71432F9a116E9ac244D3Ef4c2386d3a": { "Name": "SymbiosisFacet", "Version": "1.0.0" + }, + "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c": { + "Name": "SquidFacet", + "Version": "1.0.0" } }, "Periphery": { diff --git a/deployments/avalanche.diamond.staging.json b/deployments/avalanche.diamond.staging.json index d8bc98d38..7294326d0 100644 --- a/deployments/avalanche.diamond.staging.json +++ b/deployments/avalanche.diamond.staging.json @@ -52,6 +52,10 @@ "0x6982237928de8e4a1a3622226D3a9C2bd89515Fc": { "Name": "", "Version": "" + }, + "0x9951B2384a36a439C2afAfFf12c43F88Babde7c1": { + "Name": "SquidFacet", + "Version": "1.0.0" } }, "Periphery": { @@ -64,4 +68,4 @@ "ServiceFeeCollector": "0x9cc3164f01ED3796Fdf7Da538484D634608D2203" } } -} \ No newline at end of file +} diff --git a/deployments/avalanche.json b/deployments/avalanche.json index 6453b28d5..fe0a27878 100644 --- a/deployments/avalanche.json +++ b/deployments/avalanche.json @@ -38,5 +38,6 @@ "AllBridgeFacet": "0x85f9c55B22bd2c389b560130F878CeB8b289C4c4", "ThorSwapFacet": "0xC3cD90545f30F4F9E71e582510Cc64ab5a743E85", "SymbiosisFacet": "0xe12b2488c71432F9a116E9ac244D3Ef4c2386d3a", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "SquidFacet": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c" } \ No newline at end of file diff --git a/deployments/avalanche.staging.json b/deployments/avalanche.staging.json index 3e7a1cd76..c55bbbc50 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", + "SquidFacet": "0x9951B2384a36a439C2afAfFf12c43F88Babde7c1" } \ No newline at end of file diff --git a/deployments/base.diamond.json b/deployments/base.diamond.json index 215120653..3adc3f780 100644 --- a/deployments/base.diamond.json +++ b/deployments/base.diamond.json @@ -96,6 +96,10 @@ "0xe12b2488c71432F9a116E9ac244D3Ef4c2386d3a": { "Name": "SymbiosisFacet", "Version": "1.0.0" + }, + "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c": { + "Name": "SquidFacet", + "Version": "1.0.0" } }, "Periphery": { diff --git a/deployments/base.json b/deployments/base.json index 9cc2bae24..18d9d3843 100644 --- a/deployments/base.json +++ b/deployments/base.json @@ -31,5 +31,6 @@ "AmarokFacet": "0x8782a38Afb46B291E4b7409C90De10B95b96a78D", "AmarokFacetPacked": "0xF18A285f4e6f720Eb9b4e05df71f88b9552E6ADB", "SymbiosisFacet": "0xe12b2488c71432F9a116E9ac244D3Ef4c2386d3a", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" -} + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "SquidFacet": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c" +} \ No newline at end of file diff --git a/deployments/bsc.diamond.json b/deployments/bsc.diamond.json index 6fa36b9f7..7677df8a3 100644 --- a/deployments/bsc.diamond.json +++ b/deployments/bsc.diamond.json @@ -84,6 +84,10 @@ "0xe12b2488c71432F9a116E9ac244D3Ef4c2386d3a": { "Name": "SymbiosisFacet", "Version": "1.0.0" + }, + "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c": { + "Name": "SquidFacet", + "Version": "1.0.0" } }, "Periphery": { diff --git a/deployments/bsc.diamond.staging.json b/deployments/bsc.diamond.staging.json index 82ff5fd51..16ef7f51f 100644 --- a/deployments/bsc.diamond.staging.json +++ b/deployments/bsc.diamond.staging.json @@ -45,9 +45,9 @@ "Name": "", "Version": "" }, - "0xA08EcCb4aDd1556CC42ABD5d8dFbEe8a56012359": { - "Name": "GenericSwapFacet", - "Version": "1.0.0" + "0xcED4B608A468ce334c75c6652e18E2Ba7f3F44dA": { + "Name": "", + "Version": "" }, "0xAfcC5c55d5Ec3082675D51331E7Ed9AdE195db48": { "Name": "StargateFacet", @@ -57,23 +57,29 @@ "Name": "ThorSwapFacet", "Version": "0.0.3" }, - "0xa1Be6Aaa705545f79735d42852e77720B168392e": { - "Name": "", - "Version": "" + "0x8BFC9f022ACb65dfa0Eb6CCbA45c04C2a6cb9A34": { + "Name": "SquidFacet", + "Version": "0.0.8" }, "0x7ac3EB2D191EBAb9E925CAbFD4F8155be066b3aa": { "Name": "AmarokFacetPacked", "Version": "1.0.0" + }, + "0xA08EcCb4aDd1556CC42ABD5d8dFbEe8a56012359": { + "Name": "GenericSwapFacet", + "Version": "1.0.0" } }, "Periphery": { "ERC20Proxy": "0xf90a432dD1D0541470BC9C440d9dEc3659755238", "Executor": "0x4f3B1b1075cC19daA15b7cc681b28e2fB82145eD", "FeeCollector": "0x7f98D45c7902f079fDb65811B633522e2d227BB6", - "LiFuelFeeCollector": "", + "GasRebateDistributor": "", + "LiFuelFeeCollector": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", "Receiver": "0x76EE0F8fb09047284B6ea89881595Fc6F5B09E12", "RelayerCelerIM": "", - "ServiceFeeCollector": "0xdF4257A2f61E5D432Dedbc9c65917C268323421d" + "ServiceFeeCollector": "0xdF4257A2f61E5D432Dedbc9c65917C268323421d", + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } } \ No newline at end of file diff --git a/deployments/bsc.json b/deployments/bsc.json index c4ad3ff97..e505f7e06 100644 --- a/deployments/bsc.json +++ b/deployments/bsc.json @@ -39,5 +39,6 @@ "ThorSwapFacet": "0xC3cD90545f30F4F9E71e582510Cc64ab5a743E85", "AmarokFacetPacked": "0xF18A285f4e6f720Eb9b4e05df71f88b9552E6ADB", "SymbiosisFacet": "0xe12b2488c71432F9a116E9ac244D3Ef4c2386d3a", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "SquidFacet": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c" } \ No newline at end of file diff --git a/deployments/bsc.staging.json b/deployments/bsc.staging.json index 71f98dfcc..9add3c733 100644 --- a/deployments/bsc.staging.json +++ b/deployments/bsc.staging.json @@ -12,7 +12,7 @@ "WithdrawFacet": "0x83be9a6642c41f7ef78A0B60a355B5D7f3C9A62f", "PeripheryRegistryFacet": "0x27A0B9Dd7ee2762e15CCF36DF2F54A4A7B7a9304", "LiFiDiamond": "0xbEbCDb5093B47Cd7add8211E4c77B6826aF7bc5F", - "SquidFacet": "0x95d1a0D0B807257d58aDF1Ec6A129219EF289e3E", + "SquidFacet": "0x8BFC9f022ACb65dfa0Eb6CCbA45c04C2a6cb9A34", "ServiceFeeCollector": "0xdF4257A2f61E5D432Dedbc9c65917C268323421d", "WormholeFacet": "0xb414f9BBCd4B359c9B4a1d8138Cd573F96E5896C", "FeeCollector": "0x7f98D45c7902f079fDb65811B633522e2d227BB6", diff --git a/deployments/fantom.diamond.json b/deployments/fantom.diamond.json index 9a0244969..0a8a2c710 100644 --- a/deployments/fantom.diamond.json +++ b/deployments/fantom.diamond.json @@ -56,6 +56,10 @@ "0x7A5c119ec5dDbF9631cf40f6e5DB28f31d4332a0": { "Name": "CalldataVerificationFacet", "Version": "1.1.1" + }, + "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c": { + "Name": "SquidFacet", + "Version": "1.0.0" } }, "Periphery": { diff --git a/deployments/fantom.json b/deployments/fantom.json index 13a5c4f52..0608ce2a0 100644 --- a/deployments/fantom.json +++ b/deployments/fantom.json @@ -31,5 +31,6 @@ "StandardizedCallFacet": "0x2E61751366B7e006f8D53becB4b697890B30144F", "CalldataVerificationFacet": "0x7A5c119ec5dDbF9631cf40f6e5DB28f31d4332a0", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "SquidFacet": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c" } \ No newline at end of file diff --git a/deployments/linea.diamond.json b/deployments/linea.diamond.json index 539b204d1..4f28b346e 100644 --- a/deployments/linea.diamond.json +++ b/deployments/linea.diamond.json @@ -92,6 +92,10 @@ "0x29f9a4741EC691AE54068De461Ca291794aa1D05": { "Name": "AcrossFacetPacked", "Version": "1.0.0" + }, + "0xEa8EF3E532db49915FDbDdB29244a83E76768111": { + "Name": "SquidFacet", + "Version": "1.0.0" } }, "Periphery": { diff --git a/deployments/linea.json b/deployments/linea.json index 42b16f898..efab3ef63 100644 --- a/deployments/linea.json +++ b/deployments/linea.json @@ -29,5 +29,6 @@ "SymbiosisFacet": "0x57F4d19AeC341F50BaBB55beDF73C4B87EE400ba", "TokenWrapper": "0xf6C9605c6E231C1547b7a6545d93e7233f97322a", "AcrossFacet": "0xf7B252c92e34589a062659417d3a851cc3dF043F", - "AcrossFacetPacked": "0x29f9a4741EC691AE54068De461Ca291794aa1D05" + "AcrossFacetPacked": "0x29f9a4741EC691AE54068De461Ca291794aa1D05", + "SquidFacet": "0xEa8EF3E532db49915FDbDdB29244a83E76768111" } \ No newline at end of file diff --git a/deployments/mainnet.diamond.json b/deployments/mainnet.diamond.json index 2ac493046..33d30e5ec 100644 --- a/deployments/mainnet.diamond.json +++ b/deployments/mainnet.diamond.json @@ -120,6 +120,10 @@ "0xe12b2488c71432F9a116E9ac244D3Ef4c2386d3a": { "Name": "SymbiosisFacet", "Version": "1.0.0" + }, + "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c": { + "Name": "SquidFacet", + "Version": "1.0.0" } }, "Periphery": { diff --git a/deployments/mainnet.json b/deployments/mainnet.json index bd4fbaeb1..5327aef8b 100644 --- a/deployments/mainnet.json +++ b/deployments/mainnet.json @@ -49,5 +49,6 @@ "AcrossFacetPacked": "0xE397c4883ec89ed4Fc9D258F00C689708b2799c9", "AmarokFacetPacked": "0xF18A285f4e6f720Eb9b4e05df71f88b9552E6ADB", "SymbiosisFacet": "0xe12b2488c71432F9a116E9ac244D3Ef4c2386d3a", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "SquidFacet": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c" } \ No newline at end of file diff --git a/deployments/moonbeam.diamond.json b/deployments/moonbeam.diamond.json index fcbc625e1..82e84867e 100644 --- a/deployments/moonbeam.diamond.json +++ b/deployments/moonbeam.diamond.json @@ -44,6 +44,10 @@ "0x7A5c119ec5dDbF9631cf40f6e5DB28f31d4332a0": { "Name": "CalldataVerificationFacet", "Version": "1.1.1" + }, + "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c": { + "Name": "SquidFacet", + "Version": "1.0.0" } }, "Periphery": { diff --git a/deployments/moonbeam.json b/deployments/moonbeam.json index 23340a4e8..3589731f4 100644 --- a/deployments/moonbeam.json +++ b/deployments/moonbeam.json @@ -25,5 +25,6 @@ "StandardizedCallFacet": "0x2E61751366B7e006f8D53becB4b697890B30144F", "CalldataVerificationFacet": "0x7A5c119ec5dDbF9631cf40f6e5DB28f31d4332a0", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "SquidFacet": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c" } \ No newline at end of file diff --git a/deployments/optimism.diamond.json b/deployments/optimism.diamond.json index 8fb584b5c..f9b08586c 100644 --- a/deployments/optimism.diamond.json +++ b/deployments/optimism.diamond.json @@ -108,6 +108,10 @@ "0xe12b2488c71432F9a116E9ac244D3Ef4c2386d3a": { "Name": "SymbiosisFacet", "Version": "1.0.0" + }, + "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c": { + "Name": "SquidFacet", + "Version": "1.0.0" } }, "Periphery": { diff --git a/deployments/optimism.json b/deployments/optimism.json index e5327b918..80c8a456e 100644 --- a/deployments/optimism.json +++ b/deployments/optimism.json @@ -40,5 +40,6 @@ "AcrossFacetPacked": "0xE397c4883ec89ed4Fc9D258F00C689708b2799c9", "AmarokFacetPacked": "0xF18A285f4e6f720Eb9b4e05df71f88b9552E6ADB", "SymbiosisFacet": "0xe12b2488c71432F9a116E9ac244D3Ef4c2386d3a", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "SquidFacet": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c" } \ No newline at end of file diff --git a/deployments/polygon.diamond.json b/deployments/polygon.diamond.json index 9f07a1fcb..3cb44227f 100644 --- a/deployments/polygon.diamond.json +++ b/deployments/polygon.diamond.json @@ -100,6 +100,10 @@ "0xe12b2488c71432F9a116E9ac244D3Ef4c2386d3a": { "Name": "SymbiosisFacet", "Version": "1.0.0" + }, + "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c": { + "Name": "SquidFacet", + "Version": "1.0.0" } }, "Periphery": { diff --git a/deployments/polygon.diamond.staging.json b/deployments/polygon.diamond.staging.json index 8a740908c..8f4f20408 100644 --- a/deployments/polygon.diamond.staging.json +++ b/deployments/polygon.diamond.staging.json @@ -101,6 +101,10 @@ "Name": "", "Version": "" }, + "0x8BFC9f022ACb65dfa0Eb6CCbA45c04C2a6cb9A34": { + "Name": "SquidFacet", + "Version": "0.0.8" + }, "0xC34b1387F1b6F6E58a2D708780934DaB84e6aA2e": { "Name": "", "Version": "" @@ -130,4 +134,4 @@ "TokenWrapper": "0x888C42f345c25e276327504CE2F1Da6a9C60c73E" } } -} \ No newline at end of file +} diff --git a/deployments/polygon.json b/deployments/polygon.json index d0fe1119a..827f115ae 100644 --- a/deployments/polygon.json +++ b/deployments/polygon.json @@ -44,5 +44,6 @@ "AcrossFacetPacked": "0xE397c4883ec89ed4Fc9D258F00C689708b2799c9", "AmarokFacetPacked": "0xF18A285f4e6f720Eb9b4e05df71f88b9552E6ADB", "SymbiosisFacet": "0xe12b2488c71432F9a116E9ac244D3Ef4c2386d3a", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "SquidFacet": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c" } \ No newline at end of file diff --git a/deployments/polygon.staging.json b/deployments/polygon.staging.json index 4b2e9fd60..93d1c7723 100644 --- a/deployments/polygon.staging.json +++ b/deployments/polygon.staging.json @@ -22,7 +22,7 @@ "LIFuelFacet": "0x9a077b9dD746237d8854848BDB01521B47edC8DF", "MultichainFacet": "0x2735b41F96895E8e26e5a7D51c1387cC637Ef652", "OFTWrapperFacet": "0x3004db169fa7956609A872736452E4951D4BDA8b", - "SquidFacet": "0x933A3AfE2087FB8F5c9EE9A033477C42CC14c18E", + "SquidFacet": "0x8BFC9f022ACb65dfa0Eb6CCbA45c04C2a6cb9A34", "StargateFacet": "0xAfcC5c55d5Ec3082675D51331E7Ed9AdE195db48", "SynapseBridgeFacet": "0x57F98A94AC66e197AF6776D5c094FF0da2C0B198", "WormholeFacet": "0x7260Fd3F8D0bEb06fF5935C6eadE9f406107c270", diff --git a/deployments/scroll.diamond.json b/deployments/scroll.diamond.json index 99a2641af..9868e174a 100644 --- a/deployments/scroll.diamond.json +++ b/deployments/scroll.diamond.json @@ -60,6 +60,10 @@ "0x66861f292099cAF644F4A8b6091De49BEC5E8a15": { "Name": "LIFuelFacet", "Version": "1.0.1" + }, + "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c": { + "Name": "SquidFacet", + "Version": "1.0.0" } }, "Periphery": { @@ -74,4 +78,4 @@ "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } -} \ No newline at end of file +} diff --git a/deployments/scroll.json b/deployments/scroll.json index 58f436e13..0ab84a695 100644 --- a/deployments/scroll.json +++ b/deployments/scroll.json @@ -21,5 +21,6 @@ "LIFuelFacet": "0x66861f292099cAF644F4A8b6091De49BEC5E8a15", "Receiver": "0x0561fFe9855541C02D17951c93405A4407Df74BC", "ServiceFeeCollector": "0x346b1F1896f2772ffee205e34246ac7adc55B43D", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "SquidFacet": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c" } \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index d27d62c4d..e75118a46 100644 --- a/docs/README.md +++ b/docs/README.md @@ -25,6 +25,8 @@ - [Optimism Bridge Facet](./OptimismBridgeFacet.md) - [Periphery Registry Facet](./PeripheryRegistryFacet.md) - [Polygon Bridge Facet](./PolygonBridgeFacet.md) +- [Ronin Bridge Facet](./RoninBridgeFacet.md) +- [Squid Facet](./SquidFacet.md) - [Standardized Call Facet](./StandardizedCallFacet.md) - [Stargate Facet](./StargateFacet.md) - [Synapse Bridge Facet](./SynapseBridgeFacet.md) diff --git a/docs/SquidFacet.md b/docs/SquidFacet.md new file mode 100644 index 000000000..2f53e8f06 --- /dev/null +++ b/docs/SquidFacet.md @@ -0,0 +1,79 @@ +# Squid Bridge Facet + +## How it works + +The Squid bridge facet works by forwarding calls to the Squid Router contract on the source chain. It is possible to find the contract addresses [here](https://docs.squidrouter.com/resources/urls-and-addresses). + +Squid is the cross-chain swap and liquidity routing protocol on Axelar Network. +Squid utilises existing DEXs to swap and send any native token between chains. This can be done via our SDK, Front End or Contracts directly. + +Swaps are composable with Axelar's generalised message passing, so Squid can enable one-click transactions between any application and any user, using any asset. + +Buy NFTs from any marketplace, use multi-chain DeFi, play a game on another chain, all without signing multiple transactions or downloading multiple wallets. + +```mermaid +graph LR; + D{LiFiDiamond}-- DELEGATECALL -->C(SquidFacet); + C(SquidFacet) -- CALL --> D(Squid Router) +``` + +## Public Methods + +- `function startBridgeTokensViaSquid(BridgeData memory _bridgeData, SquidData calldata _squidData)` + - Simply bridges tokens using SquidFacet +- `function swapAndStartBridgeTokensViaSquid(BridgeData memory _bridgeData, SwapData[] calldata _swapData, SquidData calldata _squidData)` + - Performs swap(s) before bridging tokens using bridgeFacet + +## Bridge Specific Parameters + +Some of the methods listed above take a variable labeled `_squidData`. + +This data is specific to Squid and is represented as the following struct type: + +```solidity +/// @notice Contains the data needed for bridging via Squid squidRouter +/// @param RouteType The type of route to use +/// @param destinationChain The chain to bridge tokens to +/// @param bridgedTokenSymbol The symbol of the bridged token +/// @param sourceCalls The calls to make on the source chain +/// @param destinationCalls The calls to make on the destination chain +/// @param fee The fee to pay +/// @param forecallEnabled Whether or not to forecall (Squid Router's instant execution service) +struct SquidData { + RouteType routeType; + string destinationChain; + string bridgedTokenSymbol; + Call[] sourceCalls; + Call[] destinationCalls; + uint256 fee; + bool forecallEnabled; +} +``` +The Squid router performs various different call types depending on the starting token and the desired token received on the destination chain. + +```solidity +enum RouteType { + BridgeCall, // Simply bridges then makes call on the destination chain + CallBridge, // Makes a call on the source chain then bridges + CallBridgeCall // Makes calls on both chains while bridging in between +} +``` +## 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 various 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 + +Most of the methods accept a `BridgeData _bridgeData` parameter. + +In the Squid contract call the fields `minAmount` and `sendingAssetId` are used for the transfer amount and the asset to be sent. Since the Squid bridge does not support native token bridging (it's mainly a stablecoin bridge) the methods will fail if native assets are tried to be bridged. + +It's also 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 diff --git a/launchJob b/launchJob new file mode 100644 index 000000000..5b3b02649 --- /dev/null +++ b/launchJob @@ -0,0 +1,6 @@ +Script started on Tue Apr 16 10:50:17 2024 +Command: bootstrap +script: bootstrap: No such file or directory + +Command exit status: 1 +Script done on Tue Apr 16 10:50:17 2024 diff --git a/script/demoScripts/demoSquid.ts b/script/demoScripts/demoSquid.ts new file mode 100644 index 000000000..7495be7d0 --- /dev/null +++ b/script/demoScripts/demoSquid.ts @@ -0,0 +1,114 @@ +import deployments from '../../deployments/polygon.staging.json' +import { + SquidFacet__factory, + ILiFi, + SquidFacet, + ERC20__factory, + ISquidRouter__factory, +} from '../typechain' +import { ethers, utils } from 'ethers' +import dotenv from 'dotenv' +dotenv.config() + +const ROUTE_TYPES: Record = { + CALL_BRIDGE: 0, + BRIDGE_CALL: 1, + CALL_BRIDGE_CALL: 2, +} + +const main = async () => { + const RPC_URL = process.env.ETH_NODE_URI_BSC + const PRIVATE_KEY = process.env.PRIVATE_KEY + const LIFI_ADDRESS = deployments.LiFiDiamond + + const provider = new ethers.providers.JsonRpcProvider(RPC_URL) + const signer = new ethers.Wallet(PRIVATE_KEY as string, provider) + const squidFacet = SquidFacet__factory.connect(LIFI_ADDRESS, provider) + + // Get a route from the Squid API (https://squidrouter.readme.io/reference/get_route) + const route = await fetch( + 'https://api.0xsquid.com/v1/route?fromChain=56&toChain=42161&fromToken=0x55d398326f99059fF775485246999027B3197955&toToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE&fromAmount=5000000000000000000&toAddress=0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0&slippage=1' + ) + const routeJson = await route.json() + + const token = ERC20__factory.connect( + routeJson.route.params.fromToken.address, + provider + ) + const iface = ISquidRouter__factory.createInterface() + + let decodedData + switch (routeJson.route.transactionRequest.routeType) { + case 'CALL_BRIDGE': + decodedData = iface.decodeFunctionData( + 'callBridge', + routeJson.route.transactionRequest.data + ) + break + case 'BRIDGE_CALL': + decodedData = iface.decodeFunctionData( + 'bridgeCall', + routeJson.route.transactionRequest.data + ) + break + case 'CALL_BRIDGE_CALL': + decodedData = iface.decodeFunctionData( + 'callBridgeCall', + routeJson.route.transactionRequest.data + ) + break + } + + const bridgeData: ILiFi.BridgeDataStruct = { + transactionId: utils.randomBytes(32), + bridge: 'Squid', + integrator: 'ACME Devs', + referrer: '0x0000000000000000000000000000000000000000', + sendingAssetId: routeJson.route.params.fromToken.address, + receiver: routeJson.route.params.toAddress, + minAmount: routeJson.route.params.fromAmount, + destinationChainId: routeJson.route.params.toChain, + hasSourceSwaps: decodedData?.sourceCalls.length > 0, + hasDestinationCall: decodedData?.destinationCalls.length > 0, + } + + const squidData: SquidFacet.SquidDataStruct = { + routeType: ROUTE_TYPES[routeJson.route.transactionRequest.routeType], + destinationChain: decodedData?.destinationChain, + bridgedTokenSymbol: decodedData?.bridgedTokenSymbol, + sourceCalls: decodedData?.sourceCalls || [], + destinationCalls: decodedData?.destinationCalls || [], + fee: routeJson.route.estimate.feeCosts[0].amount, // Could be multiple fees + forecallEnabled: routeJson.route.transactionRequest.forecallEnabled, + } + + const txRequest = routeJson.route.transactionRequest + const { value, maxFeePerGas, maxPriorityFeePerGas } = txRequest + + let tx = await token + .connect(signer) + .approve(LIFI_ADDRESS, bridgeData.minAmount, { + maxFeePerGas, + maxPriorityFeePerGas, + }) + await tx.wait() + tx = await squidFacet + .connect(signer) + .startBridgeTokensViaSquid(bridgeData, squidData, { + value, + maxFeePerGas, + maxPriorityFeePerGas, + }) + await tx.wait() +} + +main() + .then(() => { + console.log('Success') + process.exit(0) + }) + .catch((error) => { + console.error('error') + console.error(error) + process.exit(1) + }) diff --git a/script/deploy/facets/DeploySquidFacet.s.sol b/script/deploy/facets/DeploySquidFacet.s.sol new file mode 100644 index 000000000..3d0ba708e --- /dev/null +++ b/script/deploy/facets/DeploySquidFacet.s.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.17; + +import { DeployScriptBase } from "./utils/DeployScriptBase.sol"; +import { stdJson } from "forge-std/Script.sol"; +import { SquidFacet } from "lifi/Facets/SquidFacet.sol"; + +contract DeployScript is DeployScriptBase { + using stdJson for string; + + constructor() DeployScriptBase("SquidFacet") {} + + function run() + public + returns (SquidFacet deployed, bytes memory constructorArgs) + { + constructorArgs = getConstructorArgs(); + + deployed = SquidFacet(deploy(type(SquidFacet).creationCode)); + } + + function getConstructorArgs() internal override returns (bytes memory) { + string memory path = string.concat(root, "/config/squid.json"); + string memory json = vm.readFile(path); + + address router = json.readAddress( + string.concat(".", network, ".router") + ); + + return abi.encode(router); + } +} diff --git a/script/deploy/facets/UpdateSquidFacet.s.sol b/script/deploy/facets/UpdateSquidFacet.s.sol new file mode 100644 index 000000000..0dc36f4fd --- /dev/null +++ b/script/deploy/facets/UpdateSquidFacet.s.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.17; + +import { UpdateScriptBase } from "./utils/UpdateScriptBase.sol"; + +contract DeployScript is UpdateScriptBase { + function run() + public + returns (address[] memory facets, bytes memory cutData) + { + return update("SquidFacet"); + } +} diff --git a/script/deploy/resources/deployRequirements.json b/script/deploy/resources/deployRequirements.json index e3b6effb5..f56ff988d 100644 --- a/script/deploy/resources/deployRequirements.json +++ b/script/deploy/resources/deployRequirements.json @@ -281,6 +281,24 @@ } } }, + "RoninBridgeFacet": { + "configData": { + "_gateway": { + "configFileName": "ronin.json", + "keyInConfigFile": "..gateway", + "allowToDeployWithZeroAddress": "false" + } + } + }, + "SquidFacet": { + "configData": { + "_squidRouter": { + "configFileName": "squid.json", + "keyInConfigFile": "..router", + "allowToDeployWithZeroAddress": "false" + } + } + }, "StargateFacet": { "configData": { "_router": { diff --git a/script/helperFunctions.sh b/script/helperFunctions.sh index 13a1d1f80..48b3ddd34 100755 --- a/script/helperFunctions.sh +++ b/script/helperFunctions.sh @@ -3591,18 +3591,20 @@ function test_getContractNameFromDeploymentLogs() { function test_tmp() { - CONTRACT="CelerIMFacetMutable" - NETWORK="polygonzkevm" - ADDRESS="0x4D476e7D7dbBAF55c04987523f9307Ede62b4689" + CONTRACT="SquidFacet" + NETWORK="avalanche" + ADDRESS="0x9951B2384a36a439C2afAfFf12c43F88Babde7c1" ENVIRONMENT="production" VERSION="2.0.0" DIAMOND_CONTRACT_NAME="LiFiDiamondImmutable" - ARGS="0x0000000000000000000000003ad9d0648cdaa2426331e894e980d0a5ed16257f000000000000000000000000156cebba59deb2cb23742f70dcb0a11cc775591f000000000000000000000000bebcdb5093b47cd7add8211e4c77b6826af7bc5f0000000000000000000000000000000000000000000000000000000000000000" + ARGS="0x000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666" # ADDRESS=$(getContractOwner "$NETWORK" "$ENVIRONMENT" "ERC20Proxy"); # if [[ "$ADDRESS" != "$ZERO_ADDRESS" ]]; then # error "ERC20Proxy ownership was not transferred to address(0)" # exit 1 # fi - getPeripheryAddressFromDiamond "$NETWORK" "0x9b11bc9FAc17c058CAB6286b0c785bE6a65492EF" "RelayerCelerIM" + #getPeripheryAddressFromDiamond "$NETWORK" "0x9b11bc9FAc17c058CAB6286b0c785bE6a65492EF" "RelayerCelerIM" + verifyContract "$NETWORK" "$CONTRACT" "$ADDRESS" "$ARGS" } + diff --git a/src/Facets/SquidFacet.sol b/src/Facets/SquidFacet.sol new file mode 100644 index 000000000..eaa119c9e --- /dev/null +++ b/src/Facets/SquidFacet.sol @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import { ILiFi } from "../Interfaces/ILiFi.sol"; +import { ISquidRouter } from "../Interfaces/ISquidRouter.sol"; +import { ISquidMulticall } from "../Interfaces/ISquidMulticall.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 { LibBytes } from "../Libraries/LibBytes.sol"; +import { InformationMismatch } from "../Errors/GenericErrors.sol"; +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +/// @title Squid Facet +/// @author LI.FI (https://li.fi) +/// @notice Provides functionality for bridging through Squid Router +/// @custom:version 1.0.0 +contract SquidFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { + /// Types /// + + enum RouteType { + BridgeCall, + CallBridge, + CallBridgeCall + } + + /// @dev Contains the data needed for bridging via Squid squidRouter + /// @param RouteType The type of route to use + /// @param destinationChain The chain to bridge tokens to + /// @param destinationAddress The receiver address in dst chain format + /// @param bridgedTokenSymbol The symbol of the to-be-bridged token + /// @param depositAssetId The asset to be deposited on src network (input for optional Squid-internal src swaps) + /// @param sourceCalls The calls to be made by Squid on the source chain before bridging the bridgeData.sendingAsssetId token + /// @param payload The payload for the calls to be made at dest chain + /// @param fee The fee to be payed in native token on src chain + /// @param enableExpress enable Squid Router's instant execution service + struct SquidData { + RouteType routeType; + string destinationChain; + string destinationAddress; // required to allow future bridging to non-EVM networks + string bridgedTokenSymbol; + address depositAssetId; + ISquidMulticall.Call[] sourceCalls; + bytes payload; + uint256 fee; + bool enableExpress; + } + + // introduced to tacke a stack-too-deep error + struct BridgeContext { + ILiFi.BridgeData bridgeData; + SquidData squidData; + uint256 msgValue; + } + + /// Errors /// + error InvalidRouteType(); + + /// State /// + + ISquidRouter private immutable squidRouter; + + /// Constructor /// + + constructor(ISquidRouter _squidRouter) { + squidRouter = _squidRouter; + } + + /// External Methods /// + + /// @notice Bridges tokens via Squid Router + /// @param _bridgeData The core information needed for bridging + /// @param _squidData Data specific to Squid Router + function startBridgeTokensViaSquid( + ILiFi.BridgeData memory _bridgeData, + SquidData calldata _squidData + ) + external + payable + nonReentrant + refundExcessNative(payable(msg.sender)) + doesNotContainSourceSwaps(_bridgeData) + validateBridgeData(_bridgeData) + { + LibAsset.depositAsset( + _squidData.depositAssetId, + _bridgeData.minAmount + ); + + _startBridge(_bridgeData, _squidData); + } + + /// @notice Swaps and bridges tokens via Squid Router + /// @param _bridgeData The core information needed for bridging + /// @param _swapData An array of swap related data for performing swaps before bridging + /// @param _squidData Data specific to Squid Router + function swapAndStartBridgeTokensViaSquid( + ILiFi.BridgeData memory _bridgeData, + LibSwap.SwapData[] calldata _swapData, + SquidData calldata _squidData + ) + external + payable + nonReentrant + refundExcessNative(payable(msg.sender)) + containsSourceSwaps(_bridgeData) + validateBridgeData(_bridgeData) + { + // in case of native we need to keep the fee as reserve from the swap + _bridgeData.minAmount = _depositAndSwap( + _bridgeData.transactionId, + _bridgeData.minAmount, + _swapData, + payable(msg.sender), + _squidData.fee + ); + + _startBridge(_bridgeData, _squidData); + } + + /// Internal Methods /// + + /// @dev Contains the business logic for the bridge via Squid Router + /// @param _bridgeData The core information needed for bridging + /// @param _squidData Data specific to Squid Router + function _startBridge( + ILiFi.BridgeData memory _bridgeData, + SquidData calldata _squidData + ) internal { + BridgeContext memory context = BridgeContext({ + bridgeData: _bridgeData, + squidData: _squidData, + msgValue: _calculateMsgValue(_bridgeData, _squidData) + }); + + // ensure max approval if non-native asset + if (!LibAsset.isNativeAsset(context.squidData.depositAssetId)) { + LibAsset.maxApproveERC20( + IERC20(context.squidData.depositAssetId), + address(squidRouter), + context.bridgeData.minAmount + ); + } + + // make the call to Squid router based on RouteType + if (_squidData.routeType == RouteType.BridgeCall) { + _bridgeCall(context); + } else if (_squidData.routeType == RouteType.CallBridge) { + _callBridge(context); + } else if (_squidData.routeType == RouteType.CallBridgeCall) { + _callBridgeCall(context); + } else { + revert InvalidRouteType(); + } + + emit LiFiTransferStarted(_bridgeData); + } + + function _bridgeCall(BridgeContext memory _context) internal { + squidRouter.bridgeCall{ value: _context.msgValue }( + _context.squidData.bridgedTokenSymbol, + _context.bridgeData.minAmount, + _context.squidData.destinationChain, + _context.squidData.destinationAddress, + _context.squidData.payload, + _context.bridgeData.receiver, + _context.squidData.enableExpress + ); + } + + function _callBridge(BridgeContext memory _context) private { + squidRouter.callBridge{ value: _context.msgValue }( + LibAsset.isNativeAsset(_context.squidData.depositAssetId) + ? 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE + : _context.squidData.depositAssetId, + _context.bridgeData.minAmount, + _context.squidData.sourceCalls, + _context.squidData.bridgedTokenSymbol, + _context.squidData.destinationChain, + LibBytes.toHexString(uint160(_context.bridgeData.receiver), 20) + ); + } + + function _callBridgeCall(BridgeContext memory _context) private { + squidRouter.callBridgeCall{ value: _context.msgValue }( + LibAsset.isNativeAsset(_context.squidData.depositAssetId) + ? 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE + : _context.squidData.depositAssetId, + _context.bridgeData.minAmount, + _context.squidData.sourceCalls, + _context.squidData.bridgedTokenSymbol, + _context.squidData.destinationChain, + _context.squidData.destinationAddress, + _context.squidData.payload, + _context.bridgeData.receiver, + _context.squidData.enableExpress + ); + } + + function _calculateMsgValue( + ILiFi.BridgeData memory _bridgeData, + SquidData calldata _squidData + ) private pure returns (uint256) { + uint256 msgValue = _squidData.fee; + if (LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) { + msgValue += _bridgeData.minAmount; + } + return msgValue; + } +} diff --git a/src/Interfaces/ISquidMulticall.sol b/src/Interfaces/ISquidMulticall.sol new file mode 100644 index 000000000..844106075 --- /dev/null +++ b/src/Interfaces/ISquidMulticall.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +interface ISquidMulticall { + enum CallType { + Default, + FullTokenBalance, + FullNativeBalance, + CollectTokenBalance + } + + struct Call { + CallType callType; + address target; + uint256 value; + bytes callData; + bytes payload; + } +} diff --git a/src/Interfaces/ISquidRouter.sol b/src/Interfaces/ISquidRouter.sol new file mode 100644 index 000000000..dee9926d3 --- /dev/null +++ b/src/Interfaces/ISquidRouter.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import { ISquidMulticall } from "./ISquidMulticall.sol"; + +interface ISquidRouter { + function bridgeCall( + string calldata bridgedTokenSymbol, + uint256 amount, + string calldata destinationChain, + string calldata destinationAddress, + bytes calldata payload, + address gasRefundRecipient, + bool enableExpress + ) external payable; + + function callBridge( + address token, + uint256 amount, + ISquidMulticall.Call[] calldata calls, + string calldata bridgedTokenSymbol, + string calldata destinationChain, + string calldata destinationAddress + ) external payable; + + function callBridgeCall( + address token, + uint256 amount, + ISquidMulticall.Call[] calldata calls, + string calldata bridgedTokenSymbol, + string calldata destinationChain, + string calldata destinationAddress, + bytes calldata payload, + address gasRefundRecipient, + bool enableExpress + ) external payable; +} diff --git a/test/solidity/Facets/SquidFacet.t.sol b/test/solidity/Facets/SquidFacet.t.sol new file mode 100644 index 000000000..f102f8e1d --- /dev/null +++ b/test/solidity/Facets/SquidFacet.t.sol @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity 0.8.17; + +import { LibAllowList, TestBaseFacet, console, ERC20 } from "../utils/TestBaseFacet.sol"; +import { SquidFacet } from "lifi/Facets/SquidFacet.sol"; +import { LibSwap } from "lifi/Libraries/LibSwap.sol"; +import { LibBytes } from "lifi/Libraries/LibBytes.sol"; +import { ISquidRouter } from "lifi/Interfaces/ISquidRouter.sol"; +import { ISquidMulticall } from "lifi/Interfaces/ISquidMulticall.sol"; + +// Stub SquidFacet Contract +contract TestSquidFacet is SquidFacet { + address internal constant ADDRESS_WETH = + 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + + constructor(ISquidRouter _squidRouter) SquidFacet(_squidRouter) {} + + function addDex(address _dex) external { + LibAllowList.addAllowedContract(_dex); + } + + function setFunctionApprovalBySignature(bytes4 _signature) external { + LibAllowList.addAllowedSelector(_signature); + } +} + +contract SquidFacetTest is TestBaseFacet { + error InformationMismatch(); + + // These values are for Ethereum Mainnet + address internal constant ETH_HOLDER = + 0xb5d85CBf7cB3EE0D56b3bB207D5Fc4B82f43F511; + address internal constant WETH_HOLDER = + 0xD022510A3414f255150Aa54b2e42DB6129a20d9E; + address internal constant SQUID_ROUTER = + 0xce16F69375520ab01377ce7B88f5BA8C48F8D666; + address internal constant SQUID_MULTICALL = + 0x4fd39C9E151e50580779bd04B1f7eCc310079fd3; + // ----- + // SquidFacet.SquidData internal baseSquidData; + ISquidMulticall.Call internal sourceCall; + TestSquidFacet internal squidFacet; + + function setUp() public { + customBlockNumberForForking = 18810880; + customBlockNumberForForking = 19664946; + initTestBase(); + + squidFacet = new TestSquidFacet(ISquidRouter(SQUID_ROUTER)); + bytes4[] memory functionSelectors = new bytes4[](4); + functionSelectors[0] = squidFacet.startBridgeTokensViaSquid.selector; + functionSelectors[1] = squidFacet + .swapAndStartBridgeTokensViaSquid + .selector; + functionSelectors[2] = squidFacet.addDex.selector; + functionSelectors[3] = squidFacet + .setFunctionApprovalBySignature + .selector; + + addFacet(diamond, address(squidFacet), functionSelectors); + squidFacet = TestSquidFacet(address(diamond)); + squidFacet.addDex(ADDRESS_UNISWAP); + squidFacet.setFunctionApprovalBySignature( + uniswap.swapExactTokensForTokens.selector + ); + squidFacet.setFunctionApprovalBySignature( + uniswap.swapTokensForExactETH.selector + ); + squidFacet.setFunctionApprovalBySignature( + uniswap.swapETHForExactTokens.selector + ); + + setFacetAddressInTestBase(address(squidFacet), "SquidFacet"); + + // adjust bridgeData + bridgeData.bridge = "squid router"; + bridgeData.destinationChainId = 43114; + bridgeData.sendingAssetId = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; + bridgeData.minAmount = 10000000; + bridgeData.hasSourceSwaps = false; + + // addToMessageValue = 1 ether; + + vm.label(SQUID_ROUTER, "SquidRouter"); + vm.label(SQUID_MULTICALL, "SquidMulticall"); + vm.label(0xdAC17F958D2ee523a2206206994597C13D831ec7, "USDT_TOKEN"); + vm.label(0x99a58482BD75cbab83b27EC03CA68fF489b5788f, "Vyper_contract"); + } + + function initiateBridgeTxWithFacet(bool isNative) internal override { + SquidFacet.SquidData + memory validSquidData = _getSquidDataForCallBridgeCallNative(); + + addToMessageValue = validSquidData.fee; + + if (isNative) { + squidFacet.startBridgeTokensViaSquid{ + value: bridgeData.minAmount + addToMessageValue + }(bridgeData, validSquidData); + } else { + squidFacet.startBridgeTokensViaSquid{ value: addToMessageValue }( + bridgeData, + validSquidData + ); + } + } + + function initiateSwapAndBridgeTxWithFacet( + bool isNative + ) internal override { + SquidFacet.SquidData + memory validSquidData = _getSquidDataForCallBridgeCallNative(); + + if (isNative) { + squidFacet.swapAndStartBridgeTokensViaSquid{ + value: bridgeData.minAmount + addToMessageValue + }(bridgeData, swapData, validSquidData); + } else { + squidFacet.swapAndStartBridgeTokensViaSquid{ + value: addToMessageValue + }(bridgeData, swapData, validSquidData); + } + } + + // function testBase_CanBridgeTokens_fuzzed(uint256 amount) public override { + // vm.assume(amount > 100 && amount < 100_000); + // super.testBase_CanBridgeTokens_fuzzed(amount); + // } + + function testBase_CanBridgeTokens() public override { + // requires custom implementation + } + + function testBase_CanSwapAndBridgeTokens() public override { + // requires custom implementation + } + + function testBase_CanBridgeTokens_fuzzed(uint256 amount) public override { + // requires custom implementation + } + + function testBase_CanBridgeNativeTokens() public override { + // facet does not support native bridging + } + + function testBase_CanSwapAndBridgeNativeTokens() public override { + // requires custom implementation + } + + function testBase_Revert_CallerHasInsufficientFunds() public override { + // does not work for this facet + } + + function testBase_Revert_BridgeWithInvalidDestinationCallFlag() + public + override + { + // while Squid may internally execute calls/swaps on destination, this is not what we call a destination call + // we do not send payload and execute it using our executor, Squid does this for us + // therefore the bridgeData will always have "hasDestinationSwaps = false" unless we send our own payload cross-chain + // which the implementation currently does not support + } + + function _getSquidDataForCallBridgeCallNative() + internal + pure + returns (SquidFacet.SquidData memory) + { + // https://etherscan.io/tx/0x6207e7342f9a815db973a0e30398875a57ef52ac4088ec3dda62b1554acf2cdc + ISquidMulticall.Call[] memory calls = new ISquidMulticall.Call[](1); + + // swap ETH to USDC on Uniswap + calls[0].callType = ISquidMulticall.CallType.FullNativeBalance; + calls[0].target = 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45; // SwapRouter02 + calls[0].value = 0; + calls[0] + .callData = hex"04e45aaf000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000001f4000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000cb586c20000000000000000000000000000000000000000000000000000000000000000"; + calls[0].payload = hex""; + + SquidFacet.SquidData memory squidData = SquidFacet.SquidData({ + routeType: SquidFacet.RouteType.CallBridgeCall, + destinationChain: "osmosis", + destinationAddress: LibBytes.toHexString( + uint160(USER_RECEIVER), + 20 + ), + bridgedTokenSymbol: "USDC", + depositAssetId: address(0), + sourceCalls: calls, + payload: "0x000000027b22737761705f776974685f616374696f6e223a7b22737761705f6d7367223a7b22746f6b656e5f6f75745f6d696e5f616d6f756e74223a223137313833313233222c2270617468223a5b7b22706f6f6c5f6964223a2231323233222c22746f6b656e5f6f75745f64656e6f6d223a226962632f34393841303735314337393841304439413338394141333639313132334441444135374441413446453136354435433735383934353035423837364241364534227d2c7b22706f6f6c5f6964223a2231323437222c22746f6b656e5f6f75745f64656e6f6d223a226962632f44373945374438334142333939424646463933343333453534464141343830433139313234384643353536393234413241383335314145323633384233383737227d5d7d2c2261667465725f737761705f616374696f6e223a7b226962635f7472616e73666572223a7b227265636569766572223a2263656c6573746961316a39767a7572786d776e646c706a746c30396c353033657679706a617161717474653938746a222c226368616e6e656c223a226368616e6e656c2d36393934227d7d2c226c6f63616c5f66616c6c6261636b5f61646472657373223a226f736d6f316a39767a7572786d776e646c706a746c30396c353033657679706a61716171746a6738383864227d7d", + fee: 168211815457577, + enableExpress: false + }); + + return squidData; + } + + function test_CanBridgeTokens_CallBridgeCall_Native() public { + bridgeData.sendingAssetId = address(0); + bridgeData.minAmount = 100000000000000000; + + vm.startPrank(USER_SENDER); + usdc.approve(_facetTestContractAddress, bridgeData.minAmount); + + SquidFacet.SquidData + memory squidData = _getSquidDataForCallBridgeCallNative(); + + squidFacet.startBridgeTokensViaSquid{ + value: bridgeData.minAmount + squidData.fee + }(bridgeData, squidData); + } + + function test_CanBridgeTokens_BridgeCall_ERC20() public { + bridgeData.sendingAssetId = ADDRESS_USDC; + bridgeData.minAmount = defaultUSDCAmount; + + vm.startPrank(USER_SENDER); + usdc.approve(_facetTestContractAddress, bridgeData.minAmount); + + SquidFacet.SquidData + memory squidData = _getSquidDataForCallBridgeCallNative(); + + squidData.routeType = SquidFacet.RouteType.BridgeCall; + delete squidData.sourceCalls; + squidData.bridgedTokenSymbol = "USDC"; + squidData.depositAssetId = ADDRESS_USDC; + squidData.fee = 0; + + squidFacet.startBridgeTokensViaSquid{ + value: bridgeData.minAmount + squidData.fee + }(bridgeData, squidData); + } + + // USDC > USDT => BRIDGE + function _getSquidDataForCallBridgeCallERC20() + internal + view + returns (SquidFacet.SquidData memory) + { + address ADDRESS_USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; + + ISquidMulticall.Call[] memory calls = new ISquidMulticall.Call[](2); + + // create uniswap calldata + // Swap USDC > USDT + address[] memory swapPath = new address[](2); + swapPath[0] = ADDRESS_USDC; + swapPath[1] = ADDRESS_USDT; + + bytes memory callData = abi.encodeWithSelector( + uniswap.swapExactTokensForTokens.selector, + defaultUSDCAmount, + 0, + swapPath, + SQUID_ROUTER, + block.timestamp + 20 minutes + ); + + // create call for approving USDC to Uniswap router + calls[0].callType = ISquidMulticall.CallType.Default; + calls[0].target = ADDRESS_USDC; // USDC + calls[0].value = 0; + calls[0] + .callData = hex"095ea7b30000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d000000000000000000000000000000000000000000000000000000003b9aca00"; + calls[0].payload = ""; + + // create call for swapping USDC to USDT using Uniswap + calls[1].callType = ISquidMulticall.CallType.Default; + calls[1].target = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D; // Uniswap contract + calls[1].value = 0; + calls[1].callData = callData; + calls[1].payload = ""; + + // create SquidData + SquidFacet.SquidData memory squidData = SquidFacet.SquidData({ + routeType: SquidFacet.RouteType.CallBridgeCall, + destinationChain: "Avalanche", + destinationAddress: LibBytes.toHexString( + uint160(USER_RECEIVER), + 20 + ), + bridgedTokenSymbol: "USDT", + depositAssetId: ADDRESS_USDC, + sourceCalls: calls, + payload: "", + fee: 0, + enableExpress: false + }); + + return squidData; + } + + function test_CanBridgeTokens_CallBridgeCall_ERC20() public { + // this test case will use Squid to do a pre-bridge swap from USDC to USDT and then bridge the USDC + address ADDRESS_USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; + bridgeData.minAmount = 100000000; + bridgeData.sendingAssetId = ADDRESS_USDT; + + vm.startPrank(USER_SENDER); + + // give USDT balance to user + deal(ADDRESS_USDT, USER_SENDER, 100000000); + + usdc.approve(_facetTestContractAddress, bridgeData.minAmount); + + SquidFacet.SquidData + memory squidData = _getSquidDataForCallBridgeCallERC20(); + + squidFacet.startBridgeTokensViaSquid{ value: squidData.fee }( + bridgeData, + squidData + ); + } + + function test_CanSwapERC20ToNativeAndBridge() public { + vm.startPrank(USER_SENDER); + // prepare bridgeData + bridgeData.hasSourceSwaps = true; + bridgeData.sendingAssetId = address(0); + + // prepare swap data + address[] memory path = new address[](2); + path[0] = ADDRESS_USDC; + path[1] = ADDRESS_WETH; + + uint256 amountOut = 1e17; // 0.1 eth + + // Calculate USDC input amount + uint256[] memory amounts = uniswap.getAmountsIn(amountOut, path); + uint256 amountIn = amounts[0]; + + usdc.approve(address(squidFacet), amountIn); + + bridgeData.minAmount = amountOut; + + delete swapData; + swapData.push( + LibSwap.SwapData({ + callTo: address(uniswap), + approveTo: address(uniswap), + sendingAssetId: ADDRESS_USDC, + receivingAssetId: address(0), + fromAmount: amountIn, + callData: abi.encodeWithSelector( + uniswap.swapTokensForExactETH.selector, + amountOut, + amountIn, + path, + _facetTestContractAddress, + block.timestamp + 20 minutes + ), + requiresDeposit: true + }) + ); + + SquidFacet.SquidData + memory squidData = _getSquidDataForCallBridgeCallNative(); + + squidData.routeType = SquidFacet.RouteType.CallBridgeCall; + squidData.destinationChain = "binance"; + squidData + .destinationAddress = "0xce16F69375520ab01377ce7B88f5BA8C48F8D666"; + squidData.bridgedTokenSymbol = "USDC"; + squidData.depositAssetId = address(0); + + ISquidMulticall.Call[] memory calls = new ISquidMulticall.Call[](1); + + // swap ETH to USDC on Uniswap + calls[0].callType = ISquidMulticall.CallType.FullNativeBalance; + calls[0].target = 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45; + calls[0].value = 0; + calls[0] + .callData = hex"04e45aaf000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000001f4000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000000000000000000000000000000000000122ff1030000000000000000000000000000000000000000000000000000000000000000"; + calls[0] + .payload = hex"000000000000000000000000000000000000000000000000000000000000004000000000000000000000000029dacdf7ccadf4ee67c923b4c22255a4b2494ed7000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000056000000000000000000000000000000000000000000000000000000000000006e000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf300000000000000000000000000000000000000000000000000000000000000010000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb1400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf300000000000000000000000055d398326f99059ff775485246999027b31979550000000000000000000000000000000000000000000000000000000000000064000000000000000000000000ea749fd6ba492dbc14c24fe8a3d08769229b896c0000000000000000000000000000000000000000000000000000018ee51cb96d00000000000000000000000000000000000000000000000000000000124c2d970000000000000000000000000000000000000000000000107f70cd1f496abbd100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf30000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000100000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000019609b03c976cca288fbdae5c21d4290e9a4add7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000055d398326f99059ff775485246999027b31979550000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000019609b03c976cca288fbdae5c21d4290e9a4add7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000000000164d44107a400000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000010a19f0635dc4642580000000000000000000000000000000000000000000000107675974e6fb8541c00000000000000000000000029dacdf7ccadf4ee67c923b4c22255a4b2494ed70000000000000000000000000000000000000000000000000000018ee51cb96d000000000000000000000000000000000000000000000000000000000000000200000000000000000000000055d398326f99059ff775485246999027b31979550000000000000000000000008ac76a51cc950d9822d68b83fe1ad97b32cd580d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000312bc7eaaf93f1c60dc5afc115fccde161055fb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000055d398326f99059ff775485246999027b31979550000000000000000000000000000000000000000000000000000000000000002"; + + delete squidData.sourceCalls; + squidData.sourceCalls = calls; + squidData.fee = 321320627929967; + + squidFacet.swapAndStartBridgeTokensViaSquid{ value: squidData.fee }( + bridgeData, + swapData, + squidData + ); + } +} diff --git a/test/solidity/utils/Interfaces.sol b/test/solidity/utils/Interfaces.sol index d97661b7b..eb6c40add 100644 --- a/test/solidity/utils/Interfaces.sol +++ b/test/solidity/utils/Interfaces.sol @@ -17,6 +17,14 @@ interface UniswapV2Router02 { uint256 deadline ) external; + function swapTokensForExactTokens( + uint256 amountOut, + uint256 amountInMax, + address[] calldata path, + address to, + uint256 deadline + ) external; + function swapExactTokensForETH( uint256 amountIn, uint256 amountOutMin,