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

cowprotocol / cow-sdk / #1652

13 May 2025 05:59PM UTC coverage: 76.091% (+0.5%) from 75.554%
#1652

Pull #306

anxolin
tests: assert cases for slippage calculations
Pull Request #306: feat: allow AUTO slippage in the quote and add standalone method to get an estimation of the slippage

458 of 652 branches covered (70.25%)

Branch coverage included in aggregate %.

56 of 61 new or added lines in 5 files covered. (91.8%)

1006 of 1272 relevant lines covered (79.09%)

18.26 hits per line

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

90.32
/src/trading/getQuote.ts
1
import {
2
  AppDataInfo,
3
  BuildAppDataParams,
4
  QuoteResults,
5
  QuoterParameters,
6
  SwapAdvancedSettings,
7
  SwapParameters,
8
  TradeParameters,
9
} from './types'
10
import { DEFAULT_QUOTE_VALIDITY, DEFAULT_SLIPPAGE_BPS } from './consts'
11
import { log } from '../common/utils/log'
12

13
import {
14
  getQuoteAmountsAndCosts,
15
  OrderBookApi,
16
  OrderQuoteRequest,
17
  OrderQuoteResponse,
18
  OrderQuoteSideKindBuy,
19
  OrderQuoteSideKindSell,
20
  PriceQuality,
21
  SigningScheme,
22
} from '../order-book'
23
import { buildAppData } from './appDataUtils'
24
import { getOrderToSign } from './getOrderToSign'
25
import { adjustEthFlowOrderParams, getIsEthFlowOrder, swapParamsToLimitOrderParams } from './utils'
26
import { Signer } from '@ethersproject/abstract-signer'
27
import { getOrderTypedData } from './getOrderTypedData'
28
import { getSigner } from '../common/utils/wallet'
29
import { AccountAddress } from '../common/types/wallets'
30
import { suggestSlippageBps } from './suggestSlippageBps'
31

32
// ETH-FLOW orders require different quote params
33
// check the isEthFlow flag and set in quote req obj
34
const ETH_FLOW_AUX_QUOTE_PARAMS = {
4✔
35
  signingScheme: SigningScheme.EIP1271,
36
  onchainOrder: true,
37
  // Ethflow orders are subsidized in the backend.
38
  // This means we can assume the verification gas costs are zero for the quote/fee estimation
39
  verificationGasLimit: 0,
40
}
41

42
export type QuoteResultsWithSigner = {
43
  result: QuoteResults & { signer: Signer }
44
  orderBookApi: OrderBookApi
45
}
46

47
interface GetQuoteRawResult {
48
  quote: OrderQuoteResponse
49
  appDataInfo: AppDataInfo
50
  orderBookApi: OrderBookApi
51
  tradeParameters: TradeParameters
52
  slippageBps: number
53
  suggestedSlippageBps: number
54
}
55

56
export async function getQuoteRaw(
57
  _tradeParameters: TradeParameters,
58
  trader: QuoterParameters,
59
  advancedSettings?: SwapAdvancedSettings,
60
  _orderBookApi?: OrderBookApi
61
): Promise<GetQuoteRawResult> {
62
  const { appCode, chainId, account: from } = trader
21✔
63
  const isEthFlow = getIsEthFlowOrder(_tradeParameters)
21✔
64

65
  const tradeParameters = isEthFlow
21✔
66
    ? {
67
        ..._tradeParameters,
68
        ...adjustEthFlowOrderParams(chainId, _tradeParameters),
69
      }
70
    : _tradeParameters
71

72
  const {
73
    sellToken,
74
    buyToken,
75
    amount,
76
    kind,
77
    partnerFee,
78
    validFor = DEFAULT_QUOTE_VALIDITY,
21✔
79
    slippageBps,
80
    env = 'prod',
21✔
81
  } = tradeParameters
21✔
82

83
  log(
21✔
84
    `getQuote for: Swap ${amount} ${sellToken} for ${buyToken} on chain ${chainId} with ${
85
      slippageBps ? slippageBps + ' BPS' : 'AUTO'
21✔
86
    } slippage`
87
  )
88

89
  const orderBookApi = _orderBookApi || new OrderBookApi({ chainId, env })
21!
90

91
  const receiver = tradeParameters.receiver || from
21✔
92
  const isSell = kind === 'sell'
21✔
93

94
  log('Building app data...')
21✔
95

96
  // If slippageBps is undefined, we use the default slippage
97
  const slippageBpsOrDefault = slippageBps ?? DEFAULT_SLIPPAGE_BPS
21✔
98

99
  const buildAppDataParams: BuildAppDataParams = {
21✔
100
    slippageBps: slippageBpsOrDefault,
101
    orderClass: 'market',
102
    appCode,
103
    partnerFee,
104
  }
105
  const appDataInfo = await buildAppData(buildAppDataParams, advancedSettings?.appData)
21✔
106

107
  const { appDataKeccak256, fullAppData } = appDataInfo
21✔
108
  log(`App data: appDataKeccak256=${appDataKeccak256} fullAppData=${fullAppData}`)
21✔
109

110
  const quoteRequest: OrderQuoteRequest = {
21✔
111
    from,
112
    sellToken,
113
    buyToken,
114
    receiver,
115
    validFor,
116
    appData: fullAppData,
117
    appDataHash: appDataKeccak256,
118
    priceQuality: PriceQuality.OPTIMAL, // Do not change this parameter because we rely on the fact that quote has id
119
    signingScheme: SigningScheme.EIP712,
120
    ...(isEthFlow ? ETH_FLOW_AUX_QUOTE_PARAMS : {}),
21✔
121
    ...(isSell
21✔
122
      ? { kind: OrderQuoteSideKindSell.SELL, sellAmountBeforeFee: amount }
123
      : { kind: OrderQuoteSideKindBuy.BUY, buyAmountAfterFee: amount }),
124
    ...advancedSettings?.quoteRequest,
125
  }
126

127
  log('Getting quote...')
21✔
128

129
  const quote = await orderBookApi.getQuote(quoteRequest)
21✔
130

131
  // Get the suggested slippage based on the quote
132
  const suggestedSlippageBps = suggestSlippageBps({ quote, tradeParameters, trader, advancedSettings })
21✔
133

134
  // If no slippage is specified. AUTO slippage is used
135
  if (slippageBps === undefined) {
21✔
136
    // If suggested slippage is greater than default, we use the suggested slippage
137
    if (suggestedSlippageBps > DEFAULT_SLIPPAGE_BPS) {
2!
138
      // Recursive call, this time using the suggested slippage
NEW
139
      log(
×
140
        `Suggested slippage is greater than ${DEFAULT_SLIPPAGE_BPS} BPS (default), using the suggested slippage (${suggestedSlippageBps} BPS)`
141
      )
142

NEW
143
      const newAppDataInfo = await buildAppData(
×
144
        {
145
          ...buildAppDataParams,
146
          slippageBps: suggestedSlippageBps,
147
        },
148
        advancedSettings?.appData
149
      )
NEW
150
      log(
×
151
        `App data with new suggested slippage: appDataKeccak256=${newAppDataInfo.appDataKeccak256} fullAppData=${newAppDataInfo.fullAppData}`
152
      )
153

NEW
154
      return {
×
155
        slippageBps: suggestedSlippageBps,
156
        suggestedSlippageBps,
157
        tradeParameters: { ..._tradeParameters, slippageBps: suggestedSlippageBps },
158
        appDataInfo: newAppDataInfo,
159

160
        // We reuse the quote, because the slippage has no fundamental impact on the quote
161
        quote,
162
        orderBookApi,
163
      }
164
    } else {
165
      log(
2✔
166
        `Suggested slippage is only ${suggestedSlippageBps} BPS. Using the default slippage (${DEFAULT_SLIPPAGE_BPS} BPS)`
167
      )
168
    }
169
  }
170

171
  return {
21✔
172
    quote,
173
    appDataInfo,
174
    orderBookApi,
175
    tradeParameters,
176
    slippageBps: slippageBpsOrDefault,
177
    suggestedSlippageBps,
178
  }
179
}
180

181
export async function getQuote(
182
  _tradeParameters: TradeParameters,
183
  trader: QuoterParameters,
184
  advancedSettings?: SwapAdvancedSettings,
185
  _orderBookApi?: OrderBookApi
186
): Promise<{ result: QuoteResults; orderBookApi: OrderBookApi }> {
187
  const { quote, orderBookApi, tradeParameters, slippageBps, suggestedSlippageBps, appDataInfo } = await getQuoteRaw(
21✔
188
    _tradeParameters,
189
    trader,
190
    advancedSettings,
191
    _orderBookApi
192
  )
193
  const { partnerFee, sellTokenDecimals, buyTokenDecimals } = tradeParameters
21✔
194
  const { chainId, account: from } = trader
21✔
195

196
  const amountsAndCosts = getQuoteAmountsAndCosts({
21✔
197
    orderParams: quote.quote,
198
    slippagePercentBps: slippageBps,
199
    partnerFeeBps: partnerFee?.bps,
200
    sellDecimals: sellTokenDecimals,
201
    buyDecimals: buyTokenDecimals,
202
  })
203

204
  const orderToSign = getOrderToSign(
21✔
205
    { from, networkCostsAmount: quote.quote.feeAmount },
206
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
207
    swapParamsToLimitOrderParams(tradeParameters, quote),
208
    appDataInfo.appDataKeccak256
209
  )
210

211
  const orderTypedData = await getOrderTypedData(chainId, orderToSign)
21✔
212

213
  return {
21✔
214
    result: {
215
      tradeParameters,
216
      suggestedSlippageBps,
217
      amountsAndCosts,
218
      orderToSign,
219
      quoteResponse: quote,
220
      appDataInfo,
221
      orderTypedData,
222
    },
223
    orderBookApi,
224
  }
225
}
226

227
export async function getTrader(signer: Signer, swapParameters: SwapParameters): Promise<QuoterParameters> {
228
  const account = swapParameters.owner || ((await signer.getAddress()) as AccountAddress)
21✔
229

230
  return {
21✔
231
    chainId: swapParameters.chainId,
232
    appCode: swapParameters.appCode,
233
    account,
234
  }
235
}
236

237
export async function getQuoteWithSigner(
238
  swapParameters: SwapParameters,
239
  advancedSettings?: SwapAdvancedSettings,
240
  orderBookApi?: OrderBookApi
241
): Promise<QuoteResultsWithSigner> {
242
  const signer = getSigner(swapParameters.signer)
21✔
243
  const trader = await getTrader(signer, swapParameters)
21✔
244
  const result = await getQuote(swapParameters, trader, advancedSettings, orderBookApi)
21✔
245

246
  return {
21✔
247
    result: {
248
      ...result.result,
249
      signer,
250
    },
251
    orderBookApi: result.orderBookApi,
252
  }
253
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc