33This guide explains how to develop a new bridge provider for the ` CoW Protocol ` ` BridgingSDK ` .
44Bridge providers integrate third-party bridging protocols into the ` CoW ecosystem ` , enabling cross-chain token swaps.
55
6- > You can see existing providers code in [ ` src/bridging/providers ` ] ( ./providers ) directory.
6+ > You can see existing providers code in [ ` src/bridging/providers ` ] ( ./providers ) directory.
77
88## Table of Contents
99
@@ -92,37 +92,31 @@ interface BridgeProvider<Q extends BridgeQuoteResult> {
9292
9393### Required Methods
9494
95- | Method | Purpose | Required |
96- | --------| ---------| ----------|
97- | ` getNetworks() ` | Get supported destination chains | ✅ |
98- | ` getBuyTokens() ` | Get supported tokens for a chain | ✅ |
99- | ` getIntermediateTokens() ` | Get bridgeable tokens on source chain | ✅ |
100- | ` getQuote() ` | Generate bridge quote | ✅ |
101- | ` getUnsignedBridgeCall() ` | Create unsigned bridge transaction | ✅ |
102- | ` getGasLimitEstimationForHook() ` | Estimate gas for hook execution | ✅ |
103- | ` getSignedHook() ` | Generate pre-authorized hook | ✅ |
104- | ` getStatus() ` | Check bridge transaction status | ✅ |
105- | ` getBridgingParams() ` | Extract bridge params from settlement | ✅ |
106- | ` getExplorerUrl() ` | Get bridge explorer URL | ✅ |
107- | ` decodeBridgeHook() ` | Decode hook data | Optional* |
108- | ` getCancelBridgingTx() ` | Create cancel transaction | Optional* |
109- | ` getRefundBridgingTx() ` | Create refund transaction | Optional* |
110-
111- * Can throw "Not implemented" error if unsupported
95+ | Method | Purpose | Required |
96+ | -------------------------------- | ------------------------------------- | ---------- |
97+ | ` getNetworks() ` | Get supported destination chains | ✅ |
98+ | ` getBuyTokens() ` | Get supported tokens for a chain | ✅ |
99+ | ` getIntermediateTokens() ` | Get bridgeable tokens on source chain | ✅ |
100+ | ` getQuote() ` | Generate bridge quote | ✅ |
101+ | ` getUnsignedBridgeCall() ` | Create unsigned bridge transaction | ✅ |
102+ | ` getGasLimitEstimationForHook() ` | Estimate gas for hook execution | ✅ |
103+ | ` getSignedHook() ` | Generate pre-authorized hook | ✅ |
104+ | ` getStatus() ` | Check bridge transaction status | ✅ |
105+ | ` getBridgingParams() ` | Extract bridge params from settlement | ✅ |
106+ | ` getExplorerUrl() ` | Get bridge explorer URL | ✅ |
107+ | ` decodeBridgeHook() ` | Decode hook data | Optional\ * |
108+ | ` getCancelBridgingTx() ` | Create cancel transaction | Optional\ * |
109+ | ` getRefundBridgingTx() ` | Create refund transaction | Optional\ * |
110+
111+ \ * Can throw "Not implemented" error if unsupported
112112
113113## Implementation Guide
114114
115115### Step 1: Basic Provider Class
116116
117117``` typescript
118- import {
119- BridgeProvider ,
120- BridgeProviderInfo ,
121- BridgeQuoteResult
122- } from ' ../../types'
123- import {
124- SupportedChainId , mainnet , polygon , arbitrumOne , optimism , ChainInfo
125- } from ' ../../../chains'
118+ import { BridgeProvider , BridgeProviderInfo , BridgeQuoteResult } from ' ../../types'
119+ import { SupportedChainId , mainnet , polygon , arbitrumOne , optimism , ChainInfo } from ' ../../../chains'
126120import { CowShedSdk } from ' ../../../cow-shed'
127121import { JsonRpcProvider } from ' @ethersproject/providers'
128122import { HOOK_DAPP_BRIDGE_PROVIDER_PREFIX } from ' ../../const'
@@ -134,7 +128,7 @@ export const YOUR_BRIDGE_SUPPORTED_NETWORKS = [
134128 mainnet ,
135129 polygon ,
136130 arbitrumOne ,
137- optimism
131+ optimism ,
138132 // Add your supported chains
139133 // If there are no needed chains, add them to `src/chains`
140134]
@@ -187,7 +181,7 @@ export class YourBridgeProvider implements BridgeProvider<YourBridgeQuoteResult>
187181 sellTokenAddress: params .sellTokenAddress ,
188182 })
189183
190- return tokens .map (token => ({
184+ return tokens .map (( token ) => ({
191185 chainId: token .chainId ,
192186 address: token .address ,
193187 name: token .name ,
@@ -204,14 +198,11 @@ export class YourBridgeProvider implements BridgeProvider<YourBridgeQuoteResult>
204198 async getIntermediateTokens(request : QuoteBridgeRequest ): Promise <TokenInfo []> {
205199 // Validate order kind
206200 if (request .kind !== OrderKind .SELL ) {
207- throw new BridgeProviderQuoteError (
208- BridgeQuoteErrors .ONLY_SELL_ORDER_SUPPORTED ,
209- {kind: request .kind }
210- )
201+ throw new BridgeProviderQuoteError (BridgeQuoteErrors .ONLY_SELL_ORDER_SUPPORTED , { kind: request .kind })
211202 }
212203
213204 // Get tokens on source chain that can bridge to target token
214- const {sellTokenChainId, buyTokenChainId, buyTokenAddress} = request
205+ const { sellTokenChainId, buyTokenChainId, buyTokenAddress } = request
215206
216207 const intermediateTokens = await this .api .getIntermediateTokens ({
217208 fromChainId: sellTokenChainId ,
@@ -238,15 +229,12 @@ export class YourBridgeProvider implements BridgeProvider<YourBridgeQuoteResult>
238229 amount,
239230 receiver,
240231 account,
241- owner
232+ owner,
242233 } = request
243234
244235 // Get CoW Shed account for the owner
245236 const ownerAddress = owner ?? account
246- const cowShedAccount = this .cowShedSdk .getCowShedAccount (
247- sellTokenChainId ,
248- ownerAddress
249- )
237+ const cowShedAccount = this .cowShedSdk .getCowShedAccount (sellTokenChainId , ownerAddress )
250238
251239 // Request quote from external bridge
252240 const externalQuote = await this .api .getQuote ({
@@ -266,23 +254,14 @@ export class YourBridgeProvider implements BridgeProvider<YourBridgeQuoteResult>
266254 return this .convertToBridgeQuote (externalQuote , request )
267255 }
268256
269- private async validateQuote(
270- externalQuote : YourBridgeQuote ,
271- request : QuoteBridgeRequest
272- ): Promise <void > {
257+ private async validateQuote(externalQuote : YourBridgeQuote , request : QuoteBridgeRequest ): Promise <void > {
273258 // Add validation logic
274259 if (! externalQuote .isValid ) {
275- throw new BridgeProviderQuoteError (
276- BridgeQuoteErrors .NO_ROUTES_FOUND ,
277- {quote: externalQuote }
278- )
260+ throw new BridgeProviderQuoteError (BridgeQuoteErrors .NO_ROUTES_FOUND , { quote: externalQuote })
279261 }
280262 }
281263
282- private convertToBridgeQuote(
283- externalQuote : YourBridgeQuote ,
284- request : QuoteBridgeRequest
285- ): YourBridgeQuoteResult {
264+ private convertToBridgeQuote(externalQuote : YourBridgeQuote , request : QuoteBridgeRequest ): YourBridgeQuoteResult {
286265 return {
287266 isSell: true ,
288267 amountsAndCosts: {
@@ -328,17 +307,15 @@ export class YourBridgeProvider implements BridgeProvider<YourBridgeQuoteResult>
328307
329308> It very depends on your smart-contract implementation.
330309> Basically, the smart-contract should:
310+ >
331311> 1 . Approve sell token spending from ` CoW Shed proxy `
332312> 2 . Transfer funds from ` CoW Shed proxy ` to your deposit smart-contract
333313>
334314> See ` createBungeeDepositCall ` as an example
335315
336316``` typescript
337317export class YourBridgeProvider implements BridgeProvider <YourBridgeQuoteResult > {
338- async getUnsignedBridgeCall(
339- request : QuoteBridgeRequest ,
340- quote : YourBridgeQuoteResult
341- ): Promise <EvmCall > {
318+ async getUnsignedBridgeCall(request : QuoteBridgeRequest , quote : YourBridgeQuoteResult ): Promise <EvmCall > {
342319 // Create the bridge transaction that will be executed by CoW Shed
343320 return createYourBridgeCall ({
344321 request ,
@@ -356,7 +333,10 @@ import { EvmCall } from '../../../common'
356333import { YourBridgeQuoteResult } from ' ../types'
357334import { YOUR_BRIDGE_CONTRACTS , YOUR_BRIDGE_ABI } from ' ../const'
358335
359- export async function createYourBridgeCall({ request , quote }: {
336+ export async function createYourBridgeCall({
337+ request ,
338+ quote ,
339+ }: {
360340 request: QuoteBridgeRequest
361341 quote: YourBridgeQuoteResult
362342}): Promise <EvmCall > {
@@ -401,13 +381,14 @@ import { getGasLimitEstimationForHook } from '../utils/getGasLimitEstimationForH
401381
402382export class YourBridgeProvider implements BridgeProvider <YourBridgeQuoteResult > {
403383 async getGasLimitEstimationForHook(
404- request : Omit <QuoteBridgeRequest , ' amount' >
384+ request : Omit <QuoteBridgeRequest , ' amount' > & { extraGas ? : number },
405385 ): Promise <number > {
406386 // Use utility function or implement custom gas estimation
407387 return getGasLimitEstimationForHook (
408388 this .cowShedSdk ,
409389 request as QuoteBridgeRequest , // cast needed due to omit
410- this .getRpcProvider (request .sellTokenChainId )
390+ this .getRpcProvider (request .sellTokenChainId ),
391+ request .extraGas , // to add extra gas to the hook.
411392 )
412393 }
413394
@@ -421,21 +402,23 @@ export class YourBridgeProvider implements BridgeProvider<YourBridgeQuoteResult>
421402 ): Promise <BridgeHook > {
422403 // Sign the multicall using CoW Shed SDK
423404 const { signedMulticall, cowShedAccount, gasLimit } = await this .cowShedSdk .signCalls ({
424- calls: [{
425- target: unsignedCall .to ,
426- value: unsignedCall .value ,
427- callData: unsignedCall .data ,
428- allowFailure: false ,
429- isDelegateCall: true ,
430- }],
405+ calls: [
406+ {
407+ target: unsignedCall .to ,
408+ value: unsignedCall .value ,
409+ callData: unsignedCall .data ,
410+ allowFailure: false ,
411+ isDelegateCall: true ,
412+ },
413+ ],
431414 chainId ,
432415 signer ,
433416 gasLimit: BigInt (hookGasLimit ),
434417 deadline ,
435418 nonce: bridgeHookNonce ,
436419 })
437420
438- const {to, data} = signedMulticall
421+ const { to, data } = signedMulticall
439422 return {
440423 postHook: {
441424 target: to ,
@@ -647,9 +630,7 @@ describe('YourBridgeProvider', () => {
647630 // ... rest of request
648631 }
649632
650- await expect (provider .getQuote (request ))
651- .rejects
652- .toThrow (' Only sell orders are supported' )
633+ await expect (provider .getQuote (request )).rejects .toThrow (' Only sell orders are supported' )
653634 })
654635 })
655636
@@ -663,23 +644,17 @@ describe('YourBridgeProvider', () => {
663644
664645``` typescript
665646// Use specific error types
666- throw new BridgeProviderQuoteError (
667- BridgeQuoteErrors .INSUFFICIENT_LIQUIDITY ,
668- {
669- availableLiquidity: ' 1000' ,
670- requestedAmount: ' 2000'
671- }
672- )
647+ throw new BridgeProviderQuoteError (BridgeQuoteErrors .INSUFFICIENT_LIQUIDITY , {
648+ availableLiquidity: ' 1000' ,
649+ requestedAmount: ' 2000' ,
650+ })
673651
674652// Handle API failures gracefully
675653try {
676654 return await this .api .getQuote (params )
677655} catch (error ) {
678656 if (error .status === 404 ) {
679- throw new BridgeProviderQuoteError (
680- BridgeQuoteErrors .NO_ROUTES_FOUND ,
681- { params }
682- )
657+ throw new BridgeProviderQuoteError (BridgeQuoteErrors .NO_ROUTES_FOUND , { params })
683658 }
684659 throw error
685660}
@@ -768,6 +743,7 @@ const bridgingSdk = new BridgingSdk({
768743### Common Issues
769744
7707451 . ** Gas Estimation Failures**
746+
771747 ``` typescript
772748 import { getGasLimitEstimationForHook } from ' ../utils/getGasLimitEstimationForHook'
773749
@@ -783,6 +759,7 @@ const bridgingSdk = new BridgingSdk({
783759 ```
784760
7857612 . ** API Rate Limiting**
762+
786763 ``` typescript
787764 // Implement retry logic with exponential backoff
788765 private async withRetry <T >(operation : () => Promise < T > , maxRetries = 3 ): Promise < T > {
@@ -816,4 +793,3 @@ const bridgingSdk = new BridgingSdk({
816793- Test with small amounts first
817794- Validate against existing providers like ` MockBridgeProvider `
818795- Monitor transaction status on both chains
819-
0 commit comments