Skip to content
Open
Show file tree
Hide file tree
Changes from 56 commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
9cf765d
initial impl
ezynda3 Dec 22, 2025
5e5ac45
tests and staging depl
ezynda3 Dec 22, 2025
b32d8fd
Update Across V4 Swap config with verified addresses from official docs
ezynda3 Dec 22, 2025
871251d
Merge branch 'main' into implement-across-swap-facet
ezynda3 Dec 23, 2025
ea87c76
demo script
ezynda3 Dec 24, 2025
ab7d2bc
fix: correct API params and transferType extraction in AcrossV4Swap demo
ezynda3 Dec 24, 2025
c3a2471
fix: address CodeRabbitAI review comments
ezynda3 Dec 24, 2025
65da376
Merge branch 'main' into implement-across-swap-facet
ezynda3 Dec 24, 2025
f391f84
refactor: use helper utilities and config in AcrossV4Swap demo script
ezynda3 Dec 30, 2025
68c8cd4
feat: add --collect-fee flag for fee collection via FeeCollector
ezynda3 Dec 30, 2025
76ac3ad
fix: add doesNotContainDestinationCalls modifier to AcrossV4SwapFacet
ezynda3 Dec 30, 2025
5a77143
fix: append transaction hash to explorer URL in AcrossV4Swap demo
ezynda3 Dec 30, 2025
4f39cb8
fix: add validation for recipient and destinationChainId in AcrossV4S…
ezynda3 Dec 30, 2025
25c6712
chore: add deploy requirements and zkSync scripts for AcrossV4SwapFacet
ezynda3 Dec 30, 2025
12a0f22
Merge branch 'main' into implement-across-swap-facet
ezynda3 Dec 30, 2025
049fd16
Merge branch 'main' into implement-across-swap-facet
ezynda3 Dec 31, 2025
339fc8f
added non-evm destination handling and some validations + test cases …
0xDEnYO Jan 5, 2026
ea7bf4a
remove duplicate entry from gitignore
0xDEnYO Jan 5, 2026
cfd2cb6
Merge branch 'main' of github.com:lifinance/contracts into implement-…
0xDEnYO Jan 5, 2026
0f0aa84
fix diamond log update logic (when initial file corrupt it would keep…
0xDEnYO Jan 5, 2026
801999f
Merge branch 'main' of github.com:lifinance/contracts into implement-…
0xDEnYO Jan 7, 2026
b227f4a
added non-EVM destination handling, native input token support and ou…
0xDEnYO Jan 7, 2026
f4f3a4e
Merge branch 'main' of github.com:lifinance/contracts into implement-…
0xDEnYO Jan 14, 2026
b81a313
implemented new version of AcrossSwapFacet incl 4 different paths/rou…
0xDEnYO Jan 14, 2026
4621a2d
update config and deploy requirements
0xDEnYO Jan 15, 2026
c9b2010
move and fix tests
0xDEnYO Jan 15, 2026
5d53b54
deployed to arbitrum staging
0xDEnYO Jan 15, 2026
a6acded
allow Sponsored.. contracts to be zero address parameters (not availa…
0xDEnYO Jan 15, 2026
9b31575
update main test file to not use mocks but call actual router contracts
0xDEnYO Jan 15, 2026
0d63f58
add missing networks to config file
0xDEnYO Jan 16, 2026
1860b1a
remove receiver address validation from spokepool paths and add discl…
0xDEnYO Jan 20, 2026
e0a8a62
redeployed to arbitrum staging
0xDEnYO Jan 20, 2026
0a8df72
removed failing tests (for receiver address validation)
0xDEnYO Jan 21, 2026
1809f09
added mapping for HyperCore to CCTP domain 19
0xDEnYO Jan 21, 2026
8ea66e6
redeployed to arbitrum
0xDEnYO Jan 21, 2026
3a8033e
update tests (still WIP)
0xDEnYO Jan 21, 2026
662419f
Merge branch 'main' of github.com:lifinance/contracts into implement-…
0xDEnYO Jan 28, 2026
d4013e8
update tests and update scripts
0xDEnYO Jan 29, 2026
23e0edf
update demo script
0xDEnYO Jan 29, 2026
4d4b5b2
add backend signature validation for spokepool paths
0xDEnYO Jan 29, 2026
be1abe3
Merge branch 'main' of github.com:lifinance/contracts into implement-…
0xDEnYO Jan 29, 2026
8a7c54c
add backend signature handling to existing AcrossV4SwapApi tests
0xDEnYO Jan 29, 2026
c0944aa
move backend signer helper functions into dedicated test file
0xDEnYO Jan 29, 2026
324b345
move backend signer addresses into global.json (to be reused by multi…
0xDEnYO Jan 29, 2026
48bfefc
redeployed to arbitrum staging
0xDEnYO Jan 29, 2026
b9a8f4c
Merge branch 'main' into implement-across-swap-facet
0xDEnYO Jan 29, 2026
dcb48e1
fix tests increase coverage
0xDEnYO Jan 30, 2026
bf8154e
minor changes
0xDEnYO Jan 30, 2026
8e1ddfb
update demo script
0xDEnYO Jan 30, 2026
bd3117b
Merge branch 'main' of github.com:lifinance/contracts into implement-…
0xDEnYO Feb 3, 2026
2724fe4
add megaeth to config
0xDEnYO Feb 3, 2026
b491790
add monad to chain list
0xDEnYO Feb 3, 2026
6d1e86d
allow spokepoolPeriphery parameter to be zero address
0xDEnYO Feb 3, 2026
ec3ac96
fix bug in demo script
0xDEnYO Feb 3, 2026
abd5350
improve diamond log helper function
0xDEnYO Feb 3, 2026
4c7ddea
add test to hit invalid swapApiTarget line
0xDEnYO Feb 3, 2026
534c7c6
Merge branch 'main' of github.com:lifinance/contracts into implement-…
0xDEnYO Feb 6, 2026
dc876f4
store isNative in variable
0xDEnYO Feb 6, 2026
1e41d25
inherit from TestBaseFacet instead of TestBase
0xDEnYO Feb 6, 2026
ad9e3bb
update tests and docs to reflect added refund parameter
0xDEnYO Feb 6, 2026
17b03b4
clean up tmp directory
0xDEnYO Feb 6, 2026
1af8e05
deactivate base tests
0xDEnYO Feb 6, 2026
4021316
remove unnecessary validation
0xDEnYO Feb 10, 2026
be36b53
add test cases for better coverage
0xDEnYO Feb 10, 2026
370d725
redeployed to arbitrum staging
0xDEnYO Feb 10, 2026
857a684
address coderabbit comment
0xDEnYO Feb 10, 2026
2159382
formatting
0xDEnYO Feb 12, 2026
56144c0
Merge branch 'main' of github.com:lifinance/contracts into implement-…
0xDEnYO Feb 12, 2026
61c545e
helper script refactoring (coderabbit comment)
0xDEnYO Feb 12, 2026
bf907b6
update interface definitions according to latest Across changes
0xDEnYO Feb 13, 2026
ad49eb8
bump interface version
0xDEnYO Feb 13, 2026
9ac15db
add two more parameter validations
0xDEnYO Feb 16, 2026
7569036
Merge branch 'main' of github.com:lifinance/contracts into implement-…
0xDEnYO Feb 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cursor/rules/100-solidity-basics.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ globs:
- Our own files: `// SPDX-License-Identifier: LGPL-3.0-only` immediately followed by the pragma statement with no blank line in between.
- External copies retain original license + source note.
- All contracts must use `pragma solidity ^0.8.17;`.
- Do not mirror legacy `Unlicense` headers from older test utilities when creating new `.sol` files; new Solidity files in this repo must follow the LGPL+pragma convention above.

## Naming Conventions ([CONV:NAMING])

Expand Down
4 changes: 4 additions & 0 deletions .cursor/rules/102-facets.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ globs:
- Parameter handling:
- `receiverAddress` first in `{facetName}Data`, must match `bridgeData.receiver` (EVM).
- Validate `targetChainId` vs `bridgeData.destinationChain` (EVM↔EVM).
- Opaque receiver / calldata-driven flows:
- If the facet consumes **opaque calldata** such that the final protocol receiver **cannot be reliably validated on-chain** against `bridgeData.receiver` (e.g., receiver encoded in dynamic destination calldata), you **must** add additional security that gates usage to trusted calldata sources.
- Preferred pattern: require a **backend EIP-712 signature** that commits to the relevant `BridgeData` fields and a hash of the opaque calldata, and verify it on-chain against an authorized signer.
- Document the changed trust assumptions prominently in the facet NatSpec and in `docs/` (integrators must understand that the receiver is not purely enforced on-chain for these flows).
- Use LibAsset/LibSwap/LibAllowList + Validatable/SwapperV2; reserve native fees via `_depositAndSwap` variants when needed.
- Positive slippage handling: When a bridge has a `minAmountOut` (or similar) parameter (e.g., `outputAmount` in AcrossV4), it must be updated in `swapAndStartBridgeTokensVia{FacetName}` to account for positive slippage from swaps. After `_depositAndSwap` updates `_bridgeData.minAmount`, adjust the bridge's minAmountOut parameter proportionally (accounting for decimal differences if applicable). See `AcrossFacetV4.sol` lines 137-147 for reference implementation.

Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,3 @@ test/solidity/TestPlayground.t.sol

AGENTS.md
CLAUDE.md
config/whitelist.staging.json
135 changes: 135 additions & 0 deletions config/acrossV4Swap.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
{
"LinkToDeployedToAddresses": "https://github.com/across-protocol/contracts/blob/master/broadcast/deployed-addresses.json",
"mainnet": {
"spokePoolPeriphery": "0x89415a82d909a7238d69094C3Dd1dCC1aCbDa85C",
"spokePool": "0x5c7BCd6E7De5423a257D81B442095A1a6ced35C5",
"sponsoredOftSrcPeriphery": "0x4607bceaf7b22cb0c46882ffc9fab3c6efe66e5a",
"sponsoredCctpSrcPeriphery": "0x89004ea51bac007fec55976967135b2aa6e838d4"
},
"arbitrum": {
"spokePoolPeriphery": "0x89415a82d909a7238d69094C3Dd1dCC1aCbDa85C",
"spokePool": "0xe35e9842fceaCA96570B734083f4a58e8F7C5f2A",
"sponsoredOftSrcPeriphery": "0x2ac5ee3796e027da274fbde84c82173a65868940",
"sponsoredCctpSrcPeriphery": "0xce1ffe01ebb4f8521c12e74363a396ee3d337e1b"
},
"base": {
"spokePoolPeriphery": "0x0000000000000000000000000000000000000000",
"spokePool": "0x09aea4b2242abC8bb4BB78D537A67a245A7bEC64",
"sponsoredOftSrcPeriphery": "0x0000000000000000000000000000000000000000",
"sponsoredCctpSrcPeriphery": "0xa7a8d1efc1ee3e69999d370380949092251a5c20"
},
"blast": {
"spokePoolPeriphery": "0x89415a82d909a7238d69094C3Dd1dCC1aCbDa85C",
"spokePool": "0x2D509190Ed0172ba588407D4c2df918F955Cc6E1",
"sponsoredOftSrcPeriphery": "0x0000000000000000000000000000000000000000",
"sponsoredCctpSrcPeriphery": "0x0000000000000000000000000000000000000000"
},
"boba": {
"spokePoolPeriphery": "0x0000000000000000000000000000000000000000",
"spokePool": "0xBbc6009fEfFc27ce705322832Cb2068F8C1e0A58",
"sponsoredOftSrcPeriphery": "0x0000000000000000000000000000000000000000",
"sponsoredCctpSrcPeriphery": "0x0000000000000000000000000000000000000000"
},
"bsc": {
"spokePoolPeriphery": "0x89415a82d909a7238d69094C3Dd1dCC1aCbDa85C",
"spokePool": "0x4e8E101924eDE233C13e2D8622DC8aED2872d505",
"sponsoredOftSrcPeriphery": "0x0000000000000000000000000000000000000000",
"sponsoredCctpSrcPeriphery": "0x0000000000000000000000000000000000000000"
},
"hyperevm": {
"spokePoolPeriphery": "0xF1BF00D947267Da5cC63f8c8A60568c59FA31bCb",
"spokePool": "0x35E63eA3eb0fb7A3bc543C71FB66412e1F6B0E04",
"sponsoredOftSrcPeriphery": "0x0000000000000000000000000000000000000000",
"sponsoredCctpSrcPeriphery": "0x0000000000000000000000000000000000000000"
},
"ink": {
"spokePoolPeriphery": "0x89415a82d909a7238d69094C3Dd1dCC1aCbDa85C",
"spokePool": "0xeF684C38F94F48775959ECf2012D7E864ffb9dd4",
"sponsoredOftSrcPeriphery": "0x0000000000000000000000000000000000000000",
"sponsoredCctpSrcPeriphery": "0x0000000000000000000000000000000000000000"
},
"lens": {
"spokePoolPeriphery": "0x8A8cA9c4112c67b7Dae7dF7E89EA45D592362107",
"spokePool": "0xb234cA484866c811d0e6D3318866F583781ED045",
"sponsoredOftSrcPeriphery": "0x0000000000000000000000000000000000000000",
"sponsoredCctpSrcPeriphery": "0x0000000000000000000000000000000000000000"
},
"linea": {
"spokePoolPeriphery": "0xE0BCff426509723B18D6b2f0D8F4602d143bE3e0",
"spokePool": "0x7E63A5f1a8F0B4d0934B2f2327DAED3F6bb2ee75",
"sponsoredOftSrcPeriphery": "0x0000000000000000000000000000000000000000",
"sponsoredCctpSrcPeriphery": "0x60eb88a83434f13095b0a138cdcbf5078aa5005c"
},
"lisk": {
"spokePoolPeriphery": "0x89415a82d909a7238d69094C3Dd1dCC1aCbDa85C",
"spokePool": "0x9552a0a6624A23B848060AE5901659CDDa1f83f8",
"sponsoredOftSrcPeriphery": "0x0000000000000000000000000000000000000000",
"sponsoredCctpSrcPeriphery": "0x0000000000000000000000000000000000000000"
},
"megaeth": {
"spokePoolPeriphery": "0xf0aBCe137a493185c5E768F275E7E931109f8981",
"spokePool": "0x3db06da8f0a24a525f314eec954fc5c6a973d40e",
"sponsoredOftSrcPeriphery": "0x5be9f2a2f00475406f09e5be82c06eff206721d9",
"sponsoredCctpSrcPeriphery": "0x0000000000000000000000000000000000000000"
},
"mode": {
"spokePoolPeriphery": "0x89415a82d909a7238d69094C3Dd1dCC1aCbDa85C",
"spokePool": "0x3baD7AD0728f9917d1Bf08af5782dCbD516cDd96",
"sponsoredOftSrcPeriphery": "0x0000000000000000000000000000000000000000",
"sponsoredCctpSrcPeriphery": "0x0000000000000000000000000000000000000000"
},
"monad": {
"spokePoolPeriphery": "0xe9b0666DFfC176Df6686726CB9aaC78fD83D20d7",
"spokePool": "0xd2ecb3afe598b746F8123CaE365a598DA831A449",
"sponsoredOftSrcPeriphery": "0xa3de5f042efd4c732498883100a2d319bbb3c1a1",
"sponsoredCctpSrcPeriphery": "0xcbf361ee59cc74b9d6e7af947fe4136828faf2c5"
},
"optimism": {
"spokePoolPeriphery": "0x89415a82d909a7238d69094C3Dd1dCC1aCbDa85C",
"spokePool": "0x6f26Bf09B1C792e3228e5467807a900A503c0281",
"sponsoredOftSrcPeriphery": "0x0000000000000000000000000000000000000000",
"sponsoredCctpSrcPeriphery": "0x986e476f93a423d7a4cd0baf362c5e0903268142"
},
"plasma": {
"spokePoolPeriphery": "0xF1BF00D947267Da5cC63f8c8A60568c59FA31bCb",
"spokePool": "0x50039fAEfebef707cFD94D6d462fE6D10B39207a",
"sponsoredOftSrcPeriphery": "0x0000000000000000000000000000000000000000",
"sponsoredCctpSrcPeriphery": "0x0000000000000000000000000000000000000000"
},
"polygon": {
"spokePoolPeriphery": "0x89415a82d909a7238d69094C3Dd1dCC1aCbDa85C",
"spokePool": "0x9295ee1d8C5b022Be115A2AD3c30C72E34e7F096",
"sponsoredOftSrcPeriphery": "0xc6a21e6a57777f2183312c19e614dd6054b1a54f",
"sponsoredCctpSrcPeriphery": "0x473debe3db7338e03e3c8dc8e980bb1dacb25bc5"
},
"scroll": {
"spokePoolPeriphery": "0x89415a82d909a7238d69094C3Dd1dCC1aCbDa85C",
"spokePool": "0x3baD7AD0728f9917d1Bf08af5782dCbD516cDd96",
"sponsoredOftSrcPeriphery": "0x0000000000000000000000000000000000000000",
"sponsoredCctpSrcPeriphery": "0x0000000000000000000000000000000000000000"
},
"soneium": {
"spokePoolPeriphery": "0x89415a82d909a7238d69094C3Dd1dCC1aCbDa85C",
"spokePool": "0x3baD7AD0728f9917d1Bf08af5782dCbD516cDd96",
"sponsoredOftSrcPeriphery": "0x0000000000000000000000000000000000000000",
"sponsoredCctpSrcPeriphery": "0x0000000000000000000000000000000000000000"
},
"unichain": {
"spokePoolPeriphery": "0x89415a82d909a7238d69094C3Dd1dCC1aCbDa85C",
"spokePool": "0x09aea4b2242abC8bb4BB78D537A67a245A7bEC64",
"sponsoredOftSrcPeriphery": "0x0bf1a44ae69869cf7aea7e0cba76624792fad4de",
"sponsoredCctpSrcPeriphery": "0x2918236893ec1ec739a96c381e00403d52ac560f"
},
"worldchain": {
"spokePoolPeriphery": "0x89415a82d909a7238d69094C3Dd1dCC1aCbDa85C",
"spokePool": "0x09aea4b2242abC8bb4BB78D537A67a245A7bEC64",
"sponsoredOftSrcPeriphery": "0x0000000000000000000000000000000000000000",
"sponsoredCctpSrcPeriphery": "0x1c8243198570658f818fc56538f2c837c2a32958"
},
"zksync": {
"spokePoolPeriphery": "0x672b9ba0CE73b69b5F940362F0ee36AAA3F02986",
"spokePool": "0xE0B015E54d54fc84a6cB9B666099c46adE9335FF",
"sponsoredOftSrcPeriphery": "0x0000000000000000000000000000000000000000",
"sponsoredCctpSrcPeriphery": "0x0000000000000000000000000000000000000000"
}
}
8 changes: 7 additions & 1 deletion config/global.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
"deployerWallet": "0xb137683965ADC470f140df1a1D05B0D25C14E269",
"deployerWalletTron": "TS8EymUNucdNwseZMcLZRzTLy7Yz768yeD",
"devWallet": "0x85A8F3cf6d255BD497Bd1Dd83a338Cce0B14C3B3",
"backendSigner": {
"staging": "0x981CCF8c09633F6F2AF3fe661C285ca1DB09caE1",
"production": "0xAF4B7A83591a6c4c8B9d1341C3F08BBc3b800fc5"
},
"approvedSelectorsForRefundWallet": [
{
"selector": "0x0d19e519",
Expand Down Expand Up @@ -49,7 +53,9 @@
"Permit2Proxy",
"TokenWrapper"
],
"blacklistedFunctionSelectors": ["0x23b872dd"],
"blacklistedFunctionSelectors": [
"0x23b872dd"
],
"whitelistPeripheryFunctions": {
"FeeCollector": [
{
Expand Down
9 changes: 0 additions & 9 deletions config/nearintents.json

This file was deleted.

14 changes: 9 additions & 5 deletions deployments/arbitrum.diamond.staging.json
Original file line number Diff line number Diff line change
Expand Up @@ -199,19 +199,23 @@
},
"0xc547F046174A880d08A251aF10FA659c339d30Ff": {
"Name": "PolymerCCTPFacet",
"Version": "1.0.0"
"Version": ""
},
"0x2c748Eda11e9717Bd487c8CF6e52862351909Ad0": {
"Name": "NEARIntentsFacet",
"Version": "1.0.0"
},
"0x13Dab942D27D5E84584c163fE0D732901b4733E3": {
"0x4094F1887ec45c6c82B0328ED7d7AE5EE329f4f3": {
"Name": "AcrossV4SwapFacet",
"Version": "1.0.1"
"Version": "1.0.0"
},
"0xd35fC069dcBD50780c706750A41AfC418f25f5D6": {
"Name": "WhitelistRecoveryFacet",
"Version": "1.0.2"
},
"0x9C820C1569ffE99DCF54BBDf3C54682E33a40379": {
"Name": "AcrossV4SwapFacet",
"Version": ""
}
},
"Periphery": {
Expand All @@ -220,8 +224,8 @@
"FeeCollector": "0x7F8E9bEBd1Dea263A36a6916B99bd84405B9654a",
"FeeForwarder": "",
"GasZipPeriphery": "",
"LiFiDEXAggregator": "",
"LidoWrapper": "",
"LiFiDEXAggregator": "",
"Patcher": "0x3971A968c03cd9640239C937F8d30D024840E691",
"Permit2Proxy": "0x6DfAFe261B5d489aeF52e9AfA6303c291F520902",
"ReceiverAcrossV3": "0xe4F3DEF14D61e47c696374453CD64d438FD277F8",
Expand All @@ -232,4 +236,4 @@
"TokenWrapper": "0xF63b27AE2Dc887b88f82E2Cc597d07fBB2E78E70"
}
}
}
}
1 change: 1 addition & 0 deletions deployments/arbitrum.staging.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"LiFiIntentEscrowFacet": "0xb3B9C1d75bCc95bcD07F0c45c50A72A513FF8515",
"NEARIntentsFacet": "0x2c748Eda11e9717Bd487c8CF6e52862351909Ad0",
"CelerCircleBridgeV2Facet": "0xfeac1be55bCccc46D88743F9dBCCF60f66562357",
"AcrossV4SwapFacet": "0x9C820C1569ffE99DCF54BBDf3C54682E33a40379",
"ReceiverOIF": "0x55C74AdB64A9C11EFA5637dF58AC17Bc77D5c911",
"WhitelistRecoveryFacet": "0xd35fC069dcBD50780c706750A41AfC418f25f5D6",
"EcoFacet": "0x532CEdcf24B769EA6dB2D6970220C38b15C9fAF2"
Expand Down
127 changes: 127 additions & 0 deletions docs/AcrossV4SwapFacet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# AcrossV4SwapFacet

> **WARNING**
>
> **This facet consumes opaque calldata generated off-chain.**
>
> For `swapApiTarget = SpokePool` and `swapApiTarget = SpokePoolPeriphery`, the final receiver is encoded inside dynamic calldata that cannot be reliably validated on-chain against `BridgeData.receiver` (unlike most LI.FI facets).
> To protect integrators/clients/partners, these two paths require a LI.FI backend signature over `BridgeData` and the calldata hash, which gates usage to backend-generated calldata and prevents arbitrary calldata injection via public entrypoints.
>
> The sponsored paths (`swapApiTarget = SponsoredOFTSrcPeriphery` / `SponsoredCCTPSrcPeriphery`) already include receiver validation in the signed quote flow, so this additional facet-level signature is not required there.

For more information on the Across Swap API, see [Across docs](https://docs.across.to/developer-quickstart/introduction-to-swap-api).

## How it works

The AcrossV4SwapFacet integrates with Across Protocol's Swap API contracts to enable calldata-driven bridge (and optional swap) execution.

```mermaid
graph LR;
D{LiFiDiamond}-- DELEGATECALL -->AcrossV4SwapFacet;
AcrossV4SwapFacet -- CALL --> SPP(SpokePoolPeriphery)
SPP -- CALL --> SP(SwapProxy)
SP -- CALL --> DEX(DEX Router)
SPP -- CALL --> V4(Across V4 SpokePool)
```

## Public Methods

- `function startBridgeTokensViaAcrossV4Swap(BridgeData calldata _bridgeData, AcrossV4SwapFacetData calldata _acrossV4SwapFacetData)`
- Bridges tokens via Across Swap API contracts using `swapApiTarget` + selector-less `callData`
- `function swapAndStartBridgeTokensViaAcrossV4Swap(BridgeData memory _bridgeData, LibSwap.SwapData[] calldata _swapData, AcrossV4SwapFacetData calldata _acrossV4SwapFacetData)`
- Performs LI.FI internal swap(s) before bridging (supports positive-slippage adjustments depending on target)

## AcrossV4SwapFacet Specific Parameters

The methods listed above take `_acrossV4SwapFacetData`, represented as:

```solidity
enum SwapApiTarget {
SpokePool, // callData = abi.encode(IAcrossSpokePoolV4.DepositParams)
SpokePoolPeriphery, // callData = abi.encode(ISpokePoolPeriphery.SwapAndDepositData)
SponsoredOFTSrcPeriphery, // callData = abi.encode(ISponsoredOFTSrcPeriphery.Quote, bytes signature)
SponsoredCCTPSrcPeriphery // callData = abi.encode(ISponsoredCCTPSrcPeriphery.SponsoredCCTPQuote, bytes signature)
}

/// @param swapApiTarget Which Across contract should be called
/// @param callData Selector-less ABI-encoded calldata for the selected target
/// @param signature Required only for `SpokePool` / `SpokePoolPeriphery` (EIP-712 signature produced by LI.FI backend)
struct AcrossV4SwapFacetData {
SwapApiTarget swapApiTarget;
bytes callData;
bytes signature;
}
```

## Signature requirement (SpokePool / SpokePoolPeriphery)

For `swapApiTarget = SpokePool` and `swapApiTarget = SpokePoolPeriphery`, the facet requires an EIP-712 signature that commits to:

- selected `BridgeData` fields (`transactionId`, `minAmount`, `receiver`, `destinationChainId`, `sendingAssetId`)
- `swapApiTarget`
- `callDataHash = keccak256(callData)`

This signature is validated against a configured backend signer address, and exists because the receiver cannot be safely validated on-chain for these opaque calldata flows.

## Swap Data

Some methods accept a `SwapData _swapData` parameter.

Swapping is performed by a swap-specific library that expects an array of calldata that 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 in [LibSwap.sol](../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 in [ILiFi.sol](../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: 'acrossV4Swap', // 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
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 in the [LI.FI API documentation](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 on Ethereum to USDC on Polygon you can execute the following request:

```shell
curl 'https://li.quest/v1/quote?fromChain=ETH&fromAmount=30000000&fromToken=USDC&toChain=POL&toToken=USDC&slippage=0.03&allowBridges=acrossV4Swap&fromAddress={YOUR_WALLET_ADDRESS}'
```

### Swap & Cross

To get a transaction for a transfer from 30 DAI on Ethereum to USDC on Polygon you can execute the following request:

```shell
curl 'https://li.quest/v1/quote?fromChain=ETH&fromAmount=30000000000000000000&fromToken=DAI&toChain=POL&toToken=USDC&slippage=0.03&allowBridges=acrossV4Swap&fromAddress={YOUR_WALLET_ADDRESS}'
```
Loading
Loading