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

cowprotocol / cow-sdk / 15134473304

20 May 2025 09:52AM UTC coverage: 77.449% (+0.4%) from 77.017%
15134473304

push

github

web-flow
feat: implement volume slippage + sum all slippages (#313)

* feat: implement volume slippage

* chore: bump

* fix: fix broken build

* chore: bump

* chore: make error message consistent and more clear

* fix: fix name in log

* fix: fix comment

* chore: apply nit

471 of 664 branches covered (70.93%)

Branch coverage included in aggregate %.

40 of 40 new or added lines in 5 files covered. (100.0%)

1 existing line in 1 file now uncovered.

1071 of 1327 relevant lines covered (80.71%)

20.19 hits per line

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

95.16
/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/misc'
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 = {
5✔
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
24✔
63
  const isEthFlow = getIsEthFlowOrder(_tradeParameters)
24✔
64

65
  const tradeParameters = isEthFlow
24✔
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,
24✔
79
    slippageBps,
80
    env = 'prod',
24✔
81
  } = tradeParameters
24✔
82

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

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

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

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

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

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

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

110
  const quoteRequest: OrderQuoteRequest = {
24✔
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 : {}),
24✔
121
    ...(isSell
24✔
122
      ? { kind: OrderQuoteSideKindSell.SELL, sellAmountBeforeFee: amount }
123
      : { kind: OrderQuoteSideKindBuy.BUY, buyAmountAfterFee: amount }),
124
    ...advancedSettings?.quoteRequest,
125
  }
126

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

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

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

134
  // If no slippage is specified. AUTO slippage is used
135
  if (slippageBps === undefined) {
24✔
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
139
      log(
2✔
140
        `Suggested slippage is greater than ${DEFAULT_SLIPPAGE_BPS} BPS (default), using the suggested slippage (${suggestedSlippageBps} BPS)`
141
      )
142

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

154
      return {
2✔
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 {
UNCOV
165
      log(
×
166
        `Suggested slippage is only ${suggestedSlippageBps} BPS. Using the default slippage (${DEFAULT_SLIPPAGE_BPS} BPS)`
167
      )
168
    }
169
  }
170

171
  return {
22✔
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(
24✔
188
    _tradeParameters,
189
    trader,
190
    advancedSettings,
191
    _orderBookApi
192
  )
193
  const { partnerFee, sellTokenDecimals, buyTokenDecimals } = tradeParameters
24✔
194
  const { chainId, account: from } = trader
24✔
195

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

204
  const orderToSign = getOrderToSign(
24✔
205
    { from, networkCostsAmount: quote.quote.feeAmount },
206
    swapParamsToLimitOrderParams(tradeParameters, quote),
207
    appDataInfo.appDataKeccak256
208
  )
209

210
  const orderTypedData = await getOrderTypedData(chainId, orderToSign)
24✔
211

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

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

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

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

245
  return {
24✔
246
    result: {
247
      ...result.result,
248
      signer,
249
    },
250
    orderBookApi: result.orderBookApi,
251
  }
252
}
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