feat(bridging): add bridge-then-swap provider abstraction and Bungee implementation - #845
feat(bridging): add bridge-then-swap provider abstraction and Bungee implementation#845anxolin wants to merge 1 commit into
Conversation
…implementation Extend the bridging package to support the reverse cross-chain flow: bridge tokens from a source chain first, then automatically create a CoW Protocol order on the destination chain via OrderFlowFactory. - Add BridgeThenSwapProvider interface as a third provider type - Add BridgeThenSwapQuoteAndPost result type with bridge tx + destination swap estimate - Implement getQuoteWithBridgeThenSwap orchestration (8-step flow) - Implement BungeeBridgeThenSwapProvider using Bungee destination zaps - Add CREATE2 address computation and order data ABI encoding utilities - Wire dispatch in getQuoteWithBridge for the new provider type
📝 WalkthroughWalkthroughThe PR introduces a bridge-then-swap flow to the bridging SDK, enabling sequential cross-chain bridge and swap operations. It adds a new Changes
Sequence DiagramsequenceDiagram
participant SDK as BridgingSdk
participant Provider as BungeeBridgeThenSwapProvider
participant Bridge as Bridge Service
participant TradingSdk as TradingSdk
participant DestChain as Destination Chain
SDK->>Provider: getQuoteWithBridgeThenSwap(params)
Provider->>Provider: Validate order type & owner
Provider->>Provider: determineIntermediateToken()
Provider->>Bridge: Request bridge quote<br/>(sell token → intermediate)
Bridge-->>Provider: Bridge quote + buyAmount
Provider->>TradingSdk: getQuoteResults<br/>(intermediate → buy token)
TradingSdk-->>Provider: Destination swap quote
Provider->>Provider: computeOrderFlowAddress(owner)
Provider->>Provider: encodeOrderData(destination order)
Provider->>DestChain: getDestinationGasLimit(order)
DestChain-->>Provider: Gas estimate + buffer
Provider->>Bridge: getBridgeTransaction<br/>(bridge quote, destination payload)
Bridge-->>Provider: Built transaction (bridge + execution)
Provider-->>SDK: BridgeThenSwapQuoteAndPost<br/>(bridge quote, destination swap,<br/>transaction, submitHelper)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
📦 GitHub Packages PublishedLast updated: Mar 24, 2026, 03:42:52 PM UTC The following packages have been published to GitHub Packages with pre-release version
InstallationThese packages require authentication to install from GitHub Packages. First, create a # Create .npmrc file in your project root
echo "@cowprotocol:registry=https://npm.pkg.github.com" > .npmrc
echo "//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN" >> .npmrcTo get your GitHub token:
Then install any of the packages above, either by exact version (i.e. # Yarn
yarn add npm:@cowprotocol/cow-sdk@pr-845
# pnpm
pnpm install npm:@cowprotocol/cow-sdk@pr-845
# NPM
npm install npm:@cowprotocol/cow-sdk@pr-845Update to the latest version (only if you used the tag)Every commit will publish a new package. To upgrade to the latest version, run: # Yarn
yarn upgrade @cowprotocol/cow-sdk
# pnpm
pnpm update @cowprotocol/cow-sdk
# NPM
npm update @cowprotocol/cow-sdkView PackagesYou can view the published packages at: https://github.com/cowprotocol/cow-sdk/packages |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
packages/bridging/src/BridgingSdk/getQuoteWithBridgeThenSwap.ts (1)
129-131: Consider making the validity window configurable.The hardcoded 1-hour validity (
validTo) might be insufficient for slower bridges combined with order settlement time. Consider accepting this as a configurable parameter throughadvancedSettingsor provider options.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bridging/src/BridgingSdk/getQuoteWithBridgeThenSwap.ts` around lines 129 - 131, The hardcoded 1-hour validity window set via the validTo calculation in getQuoteWithBridgeThenSwap should be made configurable; add an optional parameter (e.g., advancedSettings.validityWindowSeconds or providerOptions.validToSeconds) and use it to compute validTo with a sensible default of 3600 if unset, update call sites and any types/interfaces for getQuoteWithBridgeThenSwap and related provider functions to accept and propagate this option, and ensure unit tests or consumers still default to current behavior when the option is omitted.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/bridging/src/BridgingSdk/getQuoteWithBridge.ts`:
- Line 19: Remove the unused type import BridgeThenSwapProvider from the import
list (it's causing the ESLint error) while keeping the runtime type guard
isBridgeThenSwapProvider intact; locate the import that currently includes
BridgeThenSwapProvider and delete that symbol from the import clause so only
used symbols remain, then run lint to verify the ESLint error is resolved.
In `@packages/bridging/src/BridgingSdk/getQuoteWithBridgeThenSwap.ts`:
- Around line 180-186: The buyTokenInfo object incorrectly assigns name and
symbol from destinationSwapResult.swapResult.tradeParameters.buyToken (which is
the token address); update the construction of buyTokenInfo so name and symbol
come from actual token metadata (e.g., use provided request metadata fields if
available, or fetch token details from a registry/service using buyTokenAddress
and buyTokenChainId), and if metadata is not available set name and symbol to
undefined/null or a safe placeholder; locate this change around the buyTokenInfo
variable and references to buyTokenAddress and
destinationSwapResult.swapResult.tradeParameters.buyToken.
In `@packages/bridging/src/providers/bungee/BungeeBridgeThenSwapProvider.ts`:
- Around line 193-212: getBridgeTransaction and the earlier getQuote do not pass
or use destinationPayload/destinationGasLimit, so the destination execution data
is never sent to Bungee; update the bungeeQuoteRequest construction in getQuote
to include destinationPayload and destinationGasLimit (ensure the keys match
Bungee API expectations) or re-request buildTx with those params so the returned
BungeeBridgeThenSwapQuoteResult.buildTx contains the destination execution data,
then modify getBridgeTransaction to return buildTx.txData (or the newly fetched
buildTx) that includes the destination payload and gas limit so the returned
BridgeThenSwapTransaction to/from values carry the destination execution info.
In `@packages/bridging/src/types.ts`:
- Around line 471-478: The types for quoteId are inconsistent between
destinationSwap (quoteId?: string) and DestinationOrderParams (quoteId?:
number); update one so both use the same type across the module — e.g., change
DestinationOrderParams.quoteId to quoteId?: string (or alternatively change
destinationSwap.quoteId to number) and update any code that constructs/parses
these fields to convert types accordingly; ensure references to destinationSwap
and DestinationOrderParams compile after the change.
---
Nitpick comments:
In `@packages/bridging/src/BridgingSdk/getQuoteWithBridgeThenSwap.ts`:
- Around line 129-131: The hardcoded 1-hour validity window set via the validTo
calculation in getQuoteWithBridgeThenSwap should be made configurable; add an
optional parameter (e.g., advancedSettings.validityWindowSeconds or
providerOptions.validToSeconds) and use it to compute validTo with a sensible
default of 3600 if unset, update call sites and any types/interfaces for
getQuoteWithBridgeThenSwap and related provider functions to accept and
propagate this option, and ensure unit tests or consumers still default to
current behavior when the option is omitted.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e0bd70b7-fd77-47b2-a4bf-1abc791746c4
📒 Files selected for processing (12)
packages/bridging/src/BridgingSdk/getQuoteWithBridge.test.tspackages/bridging/src/BridgingSdk/getQuoteWithBridge.tspackages/bridging/src/BridgingSdk/getQuoteWithBridgeThenSwap.tspackages/bridging/src/BridgingSdk/types.tspackages/bridging/src/index.tspackages/bridging/src/providers/bungee/BungeeBridgeThenSwapProvider.tspackages/bridging/src/providers/bungee/bridgeThenSwap/computeOrderFlowAddress.tspackages/bridging/src/providers/bungee/bridgeThenSwap/encodeOrderData.tspackages/bridging/src/providers/bungee/bridgeThenSwap/orderFlowAbi.tspackages/bridging/src/providers/bungee/types.tspackages/bridging/src/types.tspackages/bridging/src/utils.ts
| BridgeQuoteAndPost, | ||
| BridgeQuoteResult, | ||
| BridgeQuoteResults, | ||
| BridgeThenSwapProvider, |
There was a problem hiding this comment.
Remove unused import BridgeThenSwapProvider to fix ESLint error.
The BridgeThenSwapProvider type is imported but never used in this file. The type guard isBridgeThenSwapProvider (line 33) is used instead for the runtime check. This unused import is causing the ESLint pipeline failure.
🔧 Proposed fix
import {
BridgeQuoteAndPost,
BridgeQuoteResult,
BridgeQuoteResults,
- BridgeThenSwapProvider,
BridgeThenSwapQuoteAndPost,
QuoteBridgeRequest,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| BridgeThenSwapProvider, | |
| import { | |
| BridgeQuoteAndPost, | |
| BridgeQuoteResult, | |
| BridgeQuoteResults, | |
| BridgeThenSwapQuoteAndPost, | |
| QuoteBridgeRequest, |
🧰 Tools
🪛 GitHub Actions: ESLint check
[error] 19-3: ESLint (@typescript-eslint/no-unused-vars): 'BridgeThenSwapProvider' is defined but never used. Allowed unused vars must match /^_/u
[error] lint step failed: eslint src/**/*.ts
🪛 GitHub Check: eslint
[failure] 19-19:
'BridgeThenSwapProvider' is defined but never used
[failure] 19-19:
'BridgeThenSwapProvider' is defined but never used. Allowed unused vars must match /^_/u
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/bridging/src/BridgingSdk/getQuoteWithBridge.ts` at line 19, Remove
the unused type import BridgeThenSwapProvider from the import list (it's causing
the ESLint error) while keeping the runtime type guard isBridgeThenSwapProvider
intact; locate the import that currently includes BridgeThenSwapProvider and
delete that symbol from the import clause so only used symbols remain, then run
lint to verify the ESLint error is resolved.
| const buyTokenInfo: TokenInfo = { | ||
| address: buyTokenAddress, | ||
| decimals: buyTokenDecimals, | ||
| chainId: buyTokenChainId, | ||
| name: destinationSwapResult.swapResult.tradeParameters.buyToken, | ||
| symbol: destinationSwapResult.swapResult.tradeParameters.buyToken, | ||
| } |
There was a problem hiding this comment.
buyTokenInfo.name and symbol are set to the token address, not actual metadata.
tradeParameters.buyToken is the token address string, not the token name or symbol. This will result in incorrect metadata in the returned buyTokenInfo.
🛠️ Proposed fix
Consider passing token metadata from the request or fetching it separately:
// Build buy token info
const buyTokenInfo: TokenInfo = {
address: buyTokenAddress,
decimals: buyTokenDecimals,
chainId: buyTokenChainId,
- name: destinationSwapResult.swapResult.tradeParameters.buyToken,
- symbol: destinationSwapResult.swapResult.tradeParameters.buyToken,
+ name: swapAndBridgeRequest.buyTokenName,
+ symbol: swapAndBridgeRequest.buyTokenSymbol,
}Alternatively, if the metadata isn't available in the request, you could leave these fields as undefined or fetch token info from a registry.
🧰 Tools
🪛 GitHub Actions: ESLint check
[error] lint step failed: eslint src/**/*.ts
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/bridging/src/BridgingSdk/getQuoteWithBridgeThenSwap.ts` around lines
180 - 186, The buyTokenInfo object incorrectly assigns name and symbol from
destinationSwapResult.swapResult.tradeParameters.buyToken (which is the token
address); update the construction of buyTokenInfo so name and symbol come from
actual token metadata (e.g., use provided request metadata fields if available,
or fetch token details from a registry/service using buyTokenAddress and
buyTokenChainId), and if metadata is not available set name and symbol to
undefined/null or a safe placeholder; locate this change around the buyTokenInfo
variable and references to buyTokenAddress and
destinationSwapResult.swapResult.tradeParameters.buyToken.
| async getBridgeTransaction(params: { | ||
| bridgeQuote: BungeeBridgeThenSwapQuoteResult | ||
| destinationPayload: string | ||
| destinationGasLimit: number | ||
| }): Promise<BridgeThenSwapTransaction> { | ||
| const { bridgeQuote } = params | ||
| const { buildTx } = bridgeQuote | ||
|
|
||
| // The build-tx from Bungee already contains the bridge transaction. | ||
| // For destination execution, we need to re-request with the destination payload. | ||
| // However, since the quote was already obtained with the factory as receiver, | ||
| // we use the build-tx directly. The destination payload is included in the | ||
| // Bungee quote request params (destinationPayload, destinationGasLimit). | ||
| return { | ||
| to: buildTx.txData.to, | ||
| data: buildTx.txData.data, | ||
| value: buildTx.txData.value, | ||
| chainId: buildTx.txData.chainId, | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if BungeeQuoteAPIRequest type supports destinationPayload and destinationGasLimit
ast-grep --pattern $'interface BungeeQuoteAPIRequest {
$$$
}'
# Also check how other Bungee providers use these fields
rg -n "destinationPayload|destinationGasLimit" --type=ts packages/bridging/src/providers/bungee/Repository: cowprotocol/cow-sdk
Length of output: 2159
🌐 Web query:
Bungee Socket API destination payload destinationCallData parameter
💡 Result:
The Bungee Socket API (legacy Socket API used by Bungee Exchange, a cross-chain bridge and swap aggregator powered by Socket Protocol) includes a "destination payload" in its route/quote responses and request parameters. This payload enables "zaps" or arbitrary contract execution on the destination chain after the bridged funds arrive. The parameter is named destinationPayload (hex-encoded calldata, e.g., "0x1234acbd") in the Bungee /api/v1/bungee/quote API, used alongside destinationGasLimit. No source explicitly names it destinationCallData, but it serves the same purpose: the calldata for the destination call. In Socket Protocol's broader context (underlying Bungee), cross-chain messages use a payload parameter for arbitrary data execution on destination chains. Example quote request with destination payload: curl "https://public-backend.bungee.exchange/api/v1/bungee/quote?originChainId=1&destinationChainId=8453&inputToken=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48&outputToken=0xd9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca&inputAmount=100000000&userAddress=0xd8da6bf26964af9d7eed9e03e53415d37aa96045&receiverAddress=0xd8da6bf26964af9d7eed9e03e53415d37aa96045&destinationPayload=0x1234acbd&destinationGasLimit=100000" This executes the specified calldata on the destination chain post-transfer, enabling use cases like vault deposits, staking, or LP additions without extra user steps.
Citations:
- 1: https://docs.bungee.exchange/overview/use-cases/zaps
- 2: https://docs.bungee.exchange/bungee-api/for-agents
- 3: https://docs.bungee.exchange/bungee-api/integration-guides/
- 4: https://docs.bungee.exchange/bungee-api/integration-guides/manual-route-selection/
- 5: https://registry.npmjs.org/%40socket.tech%2Fbungee-protocol
- 6: https://registry.npmjs.org/%40socket.tech%2Fbungee
- 7: https://developer.socket.tech/Build/contract-setup/sending/
🏁 Script executed:
#!/bin/bash
# Get the getQuote method implementation in BungeeBridgeThenSwapProvider.ts
cat -n packages/bridging/src/providers/bungee/BungeeBridgeThenSwapProvider.ts | sed -n '133,180p'Repository: cowprotocol/cow-sdk
Length of output: 2188
🏁 Script executed:
#!/bin/bash
# Check the BungeeBridgeThenSwapQuoteResult type definition
rg -n "BungeeBridgeThenSwapQuoteResult" --type=ts packages/bridging/src/providers/bungee/ -A 15Repository: cowprotocol/cow-sdk
Length of output: 8082
🏁 Script executed:
#!/bin/bash
# Check if there are any calls to getQuote to see how destinationPayload is passed
rg -n "getQuote" --type=ts packages/bridging/src/providers/bungee/ -B 3 -A 10Repository: cowprotocol/cow-sdk
Length of output: 9399
The destination payload and gas limit parameters are unused and not passed to the Bungee API.
The getBridgeTransaction method receives destinationPayload and destinationGasLimit but doesn't include them in the returned transaction. More critically, the getQuote method (lines 146-158) doesn't pass these parameters to the Bungee API either—only basic routing parameters are sent. The comment claiming "destination payload is included in the Bungee quote request params" is incorrect; the bungeeQuoteRequest object does not contain destinationPayload or destinationGasLimit.
As a result, the bridge transaction won't include the destination execution data, and the computed destination payload will never be transmitted to Bungee or executed on the destination chain.
🧰 Tools
🪛 GitHub Actions: ESLint check
[error] lint step failed: eslint src/**/*.ts
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/bridging/src/providers/bungee/BungeeBridgeThenSwapProvider.ts`
around lines 193 - 212, getBridgeTransaction and the earlier getQuote do not
pass or use destinationPayload/destinationGasLimit, so the destination execution
data is never sent to Bungee; update the bungeeQuoteRequest construction in
getQuote to include destinationPayload and destinationGasLimit (ensure the keys
match Bungee API expectations) or re-request buildTx with those params so the
returned BungeeBridgeThenSwapQuoteResult.buildTx contains the destination
execution data, then modify getBridgeTransaction to return buildTx.txData (or
the newly fetched buildTx) that includes the destination payload and gas limit
so the returned BridgeThenSwapTransaction to/from values carry the destination
execution info.
| destinationSwap: { | ||
| sellToken: TokenInfo | ||
| buyToken: TokenInfo | ||
| estimatedBuyAmount: bigint | ||
| minBuyAmount: bigint | ||
| validTo: number | ||
| quoteId?: string | ||
| } |
There was a problem hiding this comment.
Type inconsistency: quoteId is string here but number in DestinationOrderParams.
In destinationSwap.quoteId, the type is string?, but in DestinationOrderParams.quoteId (Line 372), it's number?. This inconsistency could cause type mismatches when passing data between these structures.
🛠️ Proposed fix
Align the types:
destinationSwap: {
sellToken: TokenInfo
buyToken: TokenInfo
estimatedBuyAmount: bigint
minBuyAmount: bigint
validTo: number
- quoteId?: string
+ quoteId?: number
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| destinationSwap: { | |
| sellToken: TokenInfo | |
| buyToken: TokenInfo | |
| estimatedBuyAmount: bigint | |
| minBuyAmount: bigint | |
| validTo: number | |
| quoteId?: string | |
| } | |
| destinationSwap: { | |
| sellToken: TokenInfo | |
| buyToken: TokenInfo | |
| estimatedBuyAmount: bigint | |
| minBuyAmount: bigint | |
| validTo: number | |
| quoteId?: number | |
| } |
🧰 Tools
🪛 GitHub Actions: ESLint check
[error] lint step failed: eslint src/**/*.ts
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/bridging/src/types.ts` around lines 471 - 478, The types for quoteId
are inconsistent between destinationSwap (quoteId?: string) and
DestinationOrderParams (quoteId?: number); update one so both use the same type
across the module — e.g., change DestinationOrderParams.quoteId to quoteId?:
string (or alternatively change destinationSwap.quoteId to number) and update
any code that constructs/parses these fields to convert types accordingly;
ensure references to destinationSwap and DestinationOrderParams compile after
the change.
Summary
Extends the bridging package to support the bridge-then-swap flow — the reverse of the existing swap-then-bridge pattern. Users bridge tokens from a source chain to a destination chain, where an
OrderFlowFactorycontract automatically creates a CoW Protocol order to swap the bridged tokens into the desired token.BridgeThenSwapProvideras a third provider type alongsideHookBridgeProviderandReceiverAccountBridgeProviderBungeeBridgeThenSwapProviderusing Bungee's destination zaps (destination execution) to deliver calldata toOrderFlowFactoryon the destination chaingetQuoteWithBridgeThenSwaporchestration: intermediate token selection -> bridge quote -> destination swap quote -> CREATE2 address computation -> order encoding -> bridge transaction buildingOrderFlowFactoryaddresses andOrderFlowinit code hash are configurable per-chain via provider optionsNew files
getQuoteWithBridgeThenSwap.tsBungeeBridgeThenSwapProvider.tsBridgeThenSwapProviderbridgeThenSwap/orderFlowAbi.tsbridgeThenSwap/encodeOrderData.tsbridgeThenSwap/computeOrderFlowAddress.tsModified files
types.tsBridgeThenSwapProvider,BridgeThenSwapQuoteAndPost,DestinationOrderParams,BridgeThenSwapTransactionutils.tsisBridgeThenSwapProvider,isBridgeThenSwapQuoteAndPostgetQuoteWithBridge.tsBridgeThenSwapProviderbungee/types.tsdestinationPayloadanddestinationGasLimitonBungeeQuoteAPIRequestindex.tsTest plan
1. Build the bridging package
cd packages/bridging pnpm buildCJS and ESM should build successfully. DTS has pre-existing errors unrelated to this change (
AddressPerChainnot found).2. Run existing tests (regression)
pnpm --filter @cowprotocol/sdk-bridging testExisting tests should pass -- the new provider type does not affect existing Hook/ReceiverAccount provider flows.
3. Type-check new code
Should only show the pre-existing
getCorrelatedTokenserror (same asgetIntermediateSwapResult.ts:71).4. Verify new provider instantiation
Should instantiate without errors.
5. Verify type guards
6. E2E quote flow (requires deployed contracts + Bungee API access)
Summary by CodeRabbit
New Features
Tests