Skip to content

Commit 07f4edd

Browse files
committed
add CoW quote source
1 parent 81e8296 commit 07f4edd

File tree

2 files changed

+124
-0
lines changed

2 files changed

+124
-0
lines changed

src/swapService/strategies/aggregators/customSourceList.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
} from "@balmy/sdk"
1010
import { LocalSourceList } from "@balmy/sdk/dist/services/quotes/source-lists/local-source-list"
1111
import { CustomZRXQuoteSource } from "./sources/0xMatchaQuoteSource"
12+
import { CustomCoWQuoteSource } from "./sources/cowQuoteSource"
1213
import { CustomEnsoQuoteSource } from "./sources/ensoQuoteSource"
1314
import { CustomKyberswapQuoteSource } from "./sources/kyberswapQuoteSource"
1415
import { CustomLiFiQuoteSource } from "./sources/lifiQuoteSource"
@@ -46,6 +47,7 @@ const customSources = {
4647
"okx-dex": new CustomOKXDexQuoteSource(),
4748
paraswap: new CustomParaswapQuoteSource(),
4849
"0x": new CustomZRXQuoteSource(),
50+
cow: new CustomCoWQuoteSource(),
4951
spectra: new CustomSpectraQuoteSource(),
5052
oku_bob_icecreamswap: new CustomOkuQuoteSource(
5153
"icecreamswap",
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { type ChainId, Chains } from "@balmy/sdk"
2+
import { AlwaysValidConfigAndContextSource } from "@balmy/sdk/dist/services/quotes/quote-sources/base/always-valid-source"
3+
import type {
4+
QuoteParams,
5+
QuoteSourceMetadata,
6+
SourceQuoteResponse,
7+
SourceQuoteTransaction,
8+
} from "@balmy/sdk/dist/services/quotes/quote-sources/types"
9+
import {
10+
addQuoteSlippage,
11+
failed,
12+
} from "@balmy/sdk/dist/services/quotes/quote-sources/utils"
13+
import { keccak256, toHex, zeroAddress } from "viem"
14+
15+
const SUPPORTED_CHAINS: Record<ChainId, string> = {
16+
[Chains.ETHEREUM.chainId]: "mainnet",
17+
[Chains.BNB_CHAIN.chainId]: "bnb",
18+
[Chains.BASE.chainId]: "base",
19+
[Chains.ARBITRUM.chainId]: "arbitrum_one",
20+
[Chains.POLYGON.chainId]: "polygon",
21+
[Chains.AVALANCHE.chainId]: "avalanche",
22+
[Chains.LINEA.chainId]: "avalanche",
23+
[Chains.GNOSIS.chainId]: "xdai",
24+
[232]: "lens",
25+
}
26+
27+
const COW_METADATA: QuoteSourceMetadata<CoWSupport> = {
28+
name: "CoW Swap",
29+
supports: {
30+
chains: Object.keys(SUPPORTED_CHAINS).map(Number),
31+
swapAndTransfer: true,
32+
buyOrders: true,
33+
},
34+
logoURI: "",
35+
}
36+
type CoWSupport = { buyOrders: true; swapAndTransfer: true }
37+
type CoWConfig = object
38+
type CoWData = object
39+
export class CustomCoWQuoteSource extends AlwaysValidConfigAndContextSource<
40+
CoWSupport,
41+
CoWConfig,
42+
CoWData
43+
> {
44+
getMetadata() {
45+
return COW_METADATA
46+
}
47+
48+
async quote({
49+
components: { fetchService },
50+
request: {
51+
chainId,
52+
sellToken,
53+
buyToken,
54+
order,
55+
accounts: { takeFrom, recipient },
56+
config: { slippagePercentage, timeout },
57+
},
58+
config,
59+
}: QuoteParams<CoWSupport, CoWConfig>): Promise<
60+
SourceQuoteResponse<CoWData>
61+
> {
62+
const appData = `{\"appCode\":\"CoW Swap\",\"environment\":\"production\",\"metadata\":{\"orderClass\":{\"orderClass\":\"market\"},\"quote\":{\"slippageBips\":${Math.floor(slippagePercentage / 100)},\"smartSlippage\":true}},\"version\":\"1.10.0\"}`
63+
const appDataHash = keccak256(toHex(appData))
64+
const queryBody = {
65+
sellToken,
66+
buyToken,
67+
receiver: recipient ?? takeFrom,
68+
appData,
69+
appDataHash,
70+
from: takeFrom,
71+
priceQuality: "optimal",
72+
signingScheme: "eip712",
73+
validFor: 1800,
74+
kind: order.type,
75+
...(order.type === "sell"
76+
? { sellAmountBeforeFee: order.sellAmount.toString() }
77+
: { buyAmountAfterFee: order.buyAmount.toString() }),
78+
}
79+
80+
const quoteUrl = `https://api.cow.fi/${SUPPORTED_CHAINS[chainId]}/api/v1/quote`
81+
// const quoteUrl = `http://localhost:8080/api/v1/quote`
82+
83+
const headers: Record<string, string> = {
84+
"Content-Type": "application/json",
85+
}
86+
const quoteResponse = await fetchService.fetch(quoteUrl, {
87+
method: "POST",
88+
timeout,
89+
headers,
90+
body: JSON.stringify(queryBody),
91+
})
92+
93+
if (!quoteResponse.ok) {
94+
failed(
95+
COW_METADATA,
96+
chainId,
97+
sellToken,
98+
buyToken,
99+
await quoteResponse.text(),
100+
)
101+
}
102+
const {
103+
quote: { sellAmount, buyAmount },
104+
} = await quoteResponse.json()
105+
106+
const quote = {
107+
sellAmount,
108+
buyAmount,
109+
allowanceTarget: zeroAddress,
110+
customData: {},
111+
}
112+
113+
return addQuoteSlippage(quote, order.type, slippagePercentage)
114+
}
115+
116+
async buildTx(): Promise<SourceQuoteTransaction> {
117+
return {
118+
to: zeroAddress,
119+
calldata: "",
120+
}
121+
}
122+
}

0 commit comments

Comments
 (0)