• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cowprotocol / cow-sdk / 14001954831

21 Mar 2025 10:26PM UTC coverage: 73.659% (-3.4%) from 77.101%
14001954831

Pull #253

github

anxolin
chore: improve signatures
Pull Request #253: feat(bridging): get quote + post cross-chain swap

392 of 552 branches covered (71.01%)

Branch coverage included in aggregate %.

41 of 126 new or added lines in 16 files covered. (32.54%)

23 existing lines in 5 files now uncovered.

844 of 1126 relevant lines covered (74.96%)

16.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

50.0
/src/bridging/providers/across/AcrossBridgeProvider.ts
1
/* eslint-disable @typescript-eslint/no-unused-vars */
2
import { Signer } from 'ethers'
3

4
import {
5
  BridgeDeposit,
6
  BridgeHook,
7
  BridgeProvider,
8
  BridgeProviderInfo,
9
  BridgeQuoteResult,
10
  BridgeStatusResult,
11
  GetBuyTokensParams,
12
  QuoteBridgeRequest,
13
} from '../../types'
14

15
import { DEFAULT_GAS_COST_FOR_HOOK_ESTIMATION, RAW_PROVIDERS_FILES_PATH } from '../../const'
16

17
import { ChainId, ChainInfo, SupportedChainId, TargetChainId } from '../../../chains'
18

19
import { ACROSS_TOKEN_MAPPING } from './const/tokens'
20
import { EvmCall, TokenInfo } from '../../../common'
21

22
import { mainnet } from '../../../chains/details/mainnet'
23
import { polygon } from '../../../chains/details/polygon'
24
import { arbitrumOne } from '../../../chains/details/arbitrum'
25
import { base } from '../../../chains/details/base'
26
import { optimism } from '../../../chains/details/optimism'
27
import { AcrossApi, AcrossApiOptions, SuggestedFeesResponse } from './AcrossApi'
28
import { getChainConfigs, getTokenAddress, getTokenSymbol, toBridgeQuoteResult } from './util'
29
import { CowShedSdk, CowShedSdkOptions } from '../../../cow-shed'
30
import { createAcrossDepositCall } from './createAcrossDepositCall'
31
import { OrderKind } from '@cowprotocol/contracts'
32

33
const HOOK_DAPP_ID = 'cow-sdk://bridging/providers/across'
1✔
34
export const ACROSS_SUPPORTED_NETWORKS = [mainnet, polygon, arbitrumOne, base, optimism]
1✔
35

36
// We need to review if we should set an additional slippage tolerance, for now assuming the quote gives you the exact price of bridging and no further slippage is needed
37
const SLIPPAGE_TOLERANCE_BPS = 0
1✔
38
export interface AcrossBridgeProviderOptions {
39
  /**
40
   * Token info provider
41
   * @param chainId - The chain ID
42
   * @param addresses - The addresses of the tokens to get the info for
43
   * @returns The token infos
44
   */
45
  getTokenInfos?: (chainId: ChainId, addresses: string[]) => Promise<TokenInfo[]>
46

47
  // API options
48
  apiOptions?: AcrossApiOptions
49

50
  // Cow-shed options
51
  cowShedOptions?: CowShedSdkOptions
52
}
53

54
export interface AcrossQuoteResult extends BridgeQuoteResult {
55
  suggestedFees: SuggestedFeesResponse
56
}
57

58
export class AcrossBridgeProvider implements BridgeProvider<AcrossQuoteResult> {
59
  protected api: AcrossApi
60
  protected cowShedSdk: CowShedSdk
61

62
  constructor(private options: AcrossBridgeProviderOptions = {}) {
×
63
    this.api = new AcrossApi(options.apiOptions)
11✔
64
    this.cowShedSdk = new CowShedSdk(options.cowShedOptions)
11✔
65
  }
66

67
  info: BridgeProviderInfo = {
11✔
68
    name: 'Across',
69
    logoUrl: `${RAW_PROVIDERS_FILES_PATH}/across/across-logo.png`,
70
  }
71

72
  async getNetworks(): Promise<ChainInfo[]> {
73
    return ACROSS_SUPPORTED_NETWORKS
1✔
74
  }
75

76
  async getBuyTokens(param: GetBuyTokensParams): Promise<TokenInfo[]> {
77
    if (!this.options.getTokenInfos) {
2!
NEW
78
      throw new Error("'getTokenInfos' parameter is required for AcrossBridgeProvider constructor")
×
79
    }
80

81
    const { targetChainId } = param
2✔
82

83
    const chainConfig = ACROSS_TOKEN_MAPPING[targetChainId as TargetChainId]
2✔
84
    if (!chainConfig) {
2✔
85
      return []
1✔
86
    }
87

88
    const tokenAddresses = Object.values(chainConfig.tokens).filter((address): address is string => Boolean(address))
5✔
89
    return this.options.getTokenInfos(targetChainId, tokenAddresses)
1✔
90
  }
91

92
  async getIntermediateTokens(request: QuoteBridgeRequest): Promise<string[]> {
NEW
93
    if (request.kind !== OrderKind.SELL) {
×
NEW
94
      throw new Error('Only SELL is supported for now')
×
95
    }
96

UNCOV
97
    const { sellTokenChainId, buyTokenChainId, buyTokenAddress } = request
×
98
    const chainConfigs = getChainConfigs(sellTokenChainId, buyTokenChainId)
×
UNCOV
99
    if (!chainConfigs) return []
×
100

101
    const { sourceChainConfig, targetChainConfig } = chainConfigs
×
102

103
    // Find the token symbol for the target token
UNCOV
104
    const targetTokenSymbol = getTokenSymbol(buyTokenAddress, targetChainConfig)
×
105
    if (!targetTokenSymbol) return []
×
106

107
    // Use the tokenSymbol to find the outputToken in the target chain
UNCOV
108
    const intermediateToken = getTokenAddress(targetTokenSymbol, sourceChainConfig)
×
UNCOV
109
    return intermediateToken ? [intermediateToken] : []
×
110
  }
111

112
  async getQuote(request: QuoteBridgeRequest): Promise<AcrossQuoteResult> {
113
    const { sellTokenAddress, sellTokenChainId, buyTokenChainId, amount, receiver } = request
1✔
114

115
    const suggestedFees = await this.api.getSuggestedFees({
1✔
116
      token: sellTokenAddress,
117
      // inputToken: sellTokenAddress,
118
      // outputToken: buyTokenAddress,
119
      originChainId: sellTokenChainId,
120
      destinationChainId: buyTokenChainId,
121
      amount,
122
      recipient: receiver ?? undefined,
1!
123
    })
124

125
    // TODO: The suggested fees contain way more information. As we review more bridge providers we should revisit the
126
    // facade of the quote result.
127
    //
128
    // For example, this contains also information on the limits, so you don't need to quote again for the same pair.
129
    // potentially, this could be cached for a short period of time in the SDK so we can resolve quotes with less
130
    // requests.
131

132
    return toBridgeQuoteResult(request, SLIPPAGE_TOLERANCE_BPS, suggestedFees)
1✔
133
  }
134

135
  async getUnsignedBridgeCall(request: QuoteBridgeRequest, quote: AcrossQuoteResult): Promise<EvmCall> {
UNCOV
136
    return createAcrossDepositCall({
×
137
      request,
138
      quote,
139
      cowShedSdk: this.cowShedSdk,
140
    })
141
  }
142

143
  getGasLimitEstimationForHook(_request: QuoteBridgeRequest): number {
NEW
UNCOV
144
    return DEFAULT_GAS_COST_FOR_HOOK_ESTIMATION
×
145
  }
146

147
  async getSignedHook(chainId: SupportedChainId, unsignedCall: EvmCall, signer: Signer): Promise<BridgeHook> {
148
    // Sign the multicall
UNCOV
149
    const { signedMulticall, cowShedAccount, gasLimit } = await this.cowShedSdk.signCalls({
×
150
      calls: [
151
        {
152
          target: unsignedCall.to,
153
          value: unsignedCall.value,
154
          callData: unsignedCall.data,
155
          allowFailure: false,
156
          isDelegateCall: true,
157
        },
158
      ],
159
      chainId,
160
      signer,
161
    })
162

163
    const { to, data } = signedMulticall
×
UNCOV
164
    return {
×
165
      postHook: {
166
        target: to,
167
        callData: data,
168
        gasLimit: gasLimit.toString(),
169
        dappId: HOOK_DAPP_ID, // TODO: I think we should have some additional parameter to type the hook (using dappId for now)
170
      },
171
      recipient: cowShedAccount,
172
    }
173
  }
174

175
  async decodeBridgeHook(_hook: BridgeHook): Promise<BridgeDeposit> {
176
    throw new Error('Not implemented')
1✔
177
  }
178

179
  async getBridgingId(_orderUid: string, _settlementTx: string): Promise<string> {
180
    // TODO: get events from the mined transaction, extract the deposit id
181
    throw new Error('Not implemented')
1✔
182
  }
183

184
  getExplorerUrl(bridgingId: string): string {
185
    // TODO: Review with across how we get the explorer url based on the bridgingId
186
    return `https://app.across.to/transactions/${bridgingId}`
1✔
187
  }
188

189
  async getStatus(_bridgingId: string): Promise<BridgeStatusResult> {
190
    throw new Error('Not implemented')
1✔
191
  }
192

193
  async getCancelBridgingTx(_bridgingId: string): Promise<EvmCall> {
194
    throw new Error('Not implemented')
1✔
195
  }
196
  async getRefundBridgingTx(_bridgingId: string): Promise<EvmCall> {
197
    throw new Error('Not implemented')
1✔
198
  }
199
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc