Skip to content

Commit 2277a50

Browse files
committed
feat: use open-ocean pro endpoint
1 parent 4a13902 commit 2277a50

File tree

4 files changed

+199
-3
lines changed

4 files changed

+199
-3
lines changed

src/swapService/strategies/balmySDK/customSourceList.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { IFetchService, IProviderService } from "@balmy/sdk"
22
import { LocalSourceList } from "@balmy/sdk/dist/services/quotes/source-lists/local-source-list"
33
import { CustomLiFiQuoteSource } from "./lifiQuoteSource"
44
import { CustomOneInchQuoteSource } from "./oneInchQuoteSource"
5+
import { CustomOpenOceanQuoteSource } from "./openOceanQuoteSource"
56
import { CustomPendleQuoteSource } from "./pendleQuoteSource"
67
type ConstructorParameters = {
78
providerService: IProviderService
@@ -12,6 +13,7 @@ const customSources = {
1213
"1inch": new CustomOneInchQuoteSource(),
1314
"li-fi": new CustomLiFiQuoteSource(),
1415
pendle: new CustomPendleQuoteSource(),
16+
"open-ocean": new CustomOpenOceanQuoteSource(),
1517
}
1618
export class CustomSourceList extends LocalSourceList {
1719
constructor({ providerService, fetchService }: ConstructorParameters) {
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import {
2+
type Address,
3+
Addresses,
4+
type ChainId,
5+
Chains,
6+
isSameAddress,
7+
} from "@balmy/sdk"
8+
import type { GasPrice } from "@balmy/sdk/dist/services/gas/types"
9+
import { AlwaysValidConfigAndContextSource } from "@balmy/sdk/dist/services/quotes/quote-sources/base/always-valid-source"
10+
import type {
11+
BuildTxParams,
12+
QuoteParams,
13+
QuoteSourceMetadata,
14+
SourceQuoteResponse,
15+
SourceQuoteTransaction,
16+
} from "@balmy/sdk/dist/services/quotes/quote-sources/types"
17+
import {
18+
calculateAllowanceTarget,
19+
failed,
20+
} from "@balmy/sdk/dist/services/quotes/quote-sources/utils"
21+
import qs from "qs"
22+
import { formatUnits } from "viem"
23+
24+
// https://docs.openocean.finance/dev/supported-chains
25+
const SUPPORTED_CHAINS: Record<
26+
ChainId,
27+
{ chainKey: string; nativeAsset?: Address }
28+
> = {
29+
[Chains.ETHEREUM.chainId]: { chainKey: "eth" },
30+
[Chains.BNB_CHAIN.chainId]: { chainKey: "bsc" },
31+
[Chains.POLYGON.chainId]: {
32+
chainKey: "polygon",
33+
nativeAsset: "0x0000000000000000000000000000000000001010",
34+
},
35+
[Chains.BASE.chainId]: { chainKey: "base" },
36+
[Chains.LINEA.chainId]: { chainKey: "linea" },
37+
[Chains.FANTOM.chainId]: {
38+
chainKey: "fantom",
39+
nativeAsset: "0x0000000000000000000000000000000000000000",
40+
},
41+
[Chains.AVALANCHE.chainId]: {
42+
chainKey: "avax",
43+
nativeAsset: "0x0000000000000000000000000000000000000000",
44+
},
45+
[Chains.ARBITRUM.chainId]: { chainKey: "arbitrum" },
46+
[Chains.OPTIMISM.chainId]: { chainKey: "optimism" },
47+
[Chains.MOONRIVER.chainId]: { chainKey: "moonriver" },
48+
[Chains.AURORA.chainId]: { chainKey: "aurora" },
49+
[Chains.CRONOS.chainId]: {
50+
chainKey: "cronos",
51+
nativeAsset: "0x0000000000000000000000000000000000000000",
52+
},
53+
[Chains.HARMONY_SHARD_0.chainId]: { chainKey: "harmony" },
54+
[Chains.KAVA.chainId]: { chainKey: "kava" },
55+
[Chains.METIS_ANDROMEDA.chainId]: {
56+
chainKey: "metis",
57+
nativeAsset: "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000",
58+
},
59+
[Chains.CELO.chainId]: {
60+
chainKey: "celo",
61+
nativeAsset: "0x471ece3750da237f93b8e339c536989b8978a438",
62+
},
63+
[Chains.POLYGON_ZKEVM.chainId]: { chainKey: "polygon_zkevm" },
64+
[Chains.ONTOLOGY.chainId]: { chainKey: "ontvm" },
65+
[Chains.GNOSIS.chainId]: {
66+
chainKey: "xdai",
67+
nativeAsset: "0x0000000000000000000000000000000000000000",
68+
},
69+
[Chains.opBNB.chainId]: { chainKey: "opbnb" },
70+
[Chains.BLAST.chainId]: { chainKey: "blast" },
71+
[Chains.ROOTSTOCK.chainId]: { chainKey: "rootstock" },
72+
[Chains.MODE.chainId]: { chainKey: "mode" },
73+
}
74+
75+
const OPEN_OCEAN_METADATA: QuoteSourceMetadata<OpenOceanSupport> = {
76+
name: "Open Ocean",
77+
supports: {
78+
chains: Object.keys(SUPPORTED_CHAINS).map(Number),
79+
swapAndTransfer: true,
80+
buyOrders: false,
81+
},
82+
logoURI: "ipfs://QmP7bVENjMmobmjJcPFX6VbFTmj6pKmFNqv7Qkyqui44dT",
83+
}
84+
type OpenOceanSupport = { buyOrders: false; swapAndTransfer: true }
85+
type OpenOceanConfig = { sourceAllowlist?: string[]; apiKey?: string }
86+
type OpenOceanData = { tx: SourceQuoteTransaction }
87+
export class CustomOpenOceanQuoteSource extends AlwaysValidConfigAndContextSource<
88+
OpenOceanSupport,
89+
OpenOceanConfig,
90+
OpenOceanData
91+
> {
92+
getMetadata() {
93+
return OPEN_OCEAN_METADATA
94+
}
95+
96+
async quote({
97+
components: { fetchService },
98+
request: {
99+
chainId,
100+
sellToken,
101+
buyToken,
102+
order,
103+
accounts: { takeFrom, recipient },
104+
config: { slippagePercentage, timeout },
105+
external,
106+
},
107+
config,
108+
}: QuoteParams<OpenOceanSupport, OpenOceanConfig>): Promise<
109+
SourceQuoteResponse<OpenOceanData>
110+
> {
111+
const [{ sellToken: sellTokenDataResult }, gasPriceResult] =
112+
await Promise.all([
113+
external.tokenData.request(),
114+
external.gasPrice.request(),
115+
])
116+
const legacyGasPrice = eip1159ToLegacy(gasPriceResult)
117+
const gasPrice = Number.parseFloat(formatUnits(legacyGasPrice, 9))
118+
const amount = formatUnits(order.sellAmount, sellTokenDataResult.decimals)
119+
const { chainKey, nativeAsset } = SUPPORTED_CHAINS[chainId]
120+
const native = nativeAsset ?? Addresses.NATIVE_TOKEN
121+
const queryParams = {
122+
inTokenAddress: isSameAddress(sellToken, Addresses.NATIVE_TOKEN)
123+
? native
124+
: sellToken,
125+
outTokenAddress: isSameAddress(buyToken, Addresses.NATIVE_TOKEN)
126+
? native
127+
: buyToken,
128+
amount: amount,
129+
slippage: slippagePercentage,
130+
gasPrice: gasPrice,
131+
account: recipient ?? takeFrom,
132+
referrer: config.referrer?.address,
133+
enabledDexIds: config.sourceAllowlist,
134+
}
135+
const queryString = qs.stringify(queryParams, {
136+
skipNulls: true,
137+
arrayFormat: "comma",
138+
})
139+
const url = `https://open-api-pro.openocean.finance/v3/${chainKey}/swap_quote?${queryString}`
140+
const headers: Record<string, string> = {}
141+
if (config.apiKey) {
142+
headers["apikey"] = config.apiKey
143+
}
144+
145+
const response = await fetchService.fetch(url, { timeout, headers })
146+
if (!response.ok) {
147+
failed(
148+
OPEN_OCEAN_METADATA,
149+
chainId,
150+
sellToken,
151+
buyToken,
152+
await response.text(),
153+
)
154+
}
155+
const {
156+
data: { outAmount, estimatedGas, minOutAmount, to, value, data },
157+
} = await response.json()
158+
159+
return {
160+
sellAmount: order.sellAmount,
161+
maxSellAmount: order.sellAmount,
162+
buyAmount: BigInt(outAmount),
163+
minBuyAmount: BigInt(minOutAmount),
164+
type: "sell",
165+
estimatedGas: BigInt(estimatedGas),
166+
allowanceTarget: calculateAllowanceTarget(sellToken, to),
167+
customData: {
168+
tx: {
169+
to,
170+
calldata: data,
171+
value: BigInt(value ?? 0),
172+
},
173+
},
174+
}
175+
}
176+
177+
async buildTx({
178+
request,
179+
}: BuildTxParams<
180+
OpenOceanConfig,
181+
OpenOceanData
182+
>): Promise<SourceQuoteTransaction> {
183+
return request.customData.tx
184+
}
185+
}
186+
187+
function eip1159ToLegacy(gasPrice: GasPrice): bigint {
188+
if ("gasPrice" in gasPrice) {
189+
return BigInt(gasPrice.gasPrice)
190+
}
191+
return BigInt(gasPrice.maxFeePerGas)
192+
}

src/swapService/strategies/balmySDK/pendleQuoteSource.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { findToken } from "@/swapService/utils"
22
import { Chains } from "@balmy/sdk"
3-
import { isSameAddress } from "@balmy/sdk"
43
import type {
54
BuildTxParams,
65
IQuoteSource,
@@ -15,7 +14,7 @@ import {
1514
failed,
1615
} from "@balmy/sdk/dist/services/quotes/quote-sources/utils"
1716
import qs from "qs"
18-
import { Address, getAddress } from "viem"
17+
import { getAddress } from "viem"
1918

2019
// https://api-v2.pendle.finance/core/docs#/Chains/ChainsController_getSupportedChainIds
2120
export const PENDLE_METADATA: QuoteSourceMetadata<PendleSupport> = {
@@ -51,6 +50,7 @@ export class CustomPendleQuoteSource
5150
params: QuoteParams<PendleSupport, PendleConfig>,
5251
): Promise<SourceQuoteResponse<PendleData>> {
5352
const { dstAmount, to, data } = await this.getQuote(params)
53+
console.log("pendle: ", dstAmount)
5454

5555
const quote = {
5656
sellAmount: params.request.order.sellAmount,

src/swapService/strategies/strategyBalmySDK.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ export class StrategyBalmySDK {
9898
pendle: {
9999
apiKey: String(process.env.PENDLE_API_KEY),
100100
},
101+
"open-ocean": {
102+
apiKey: String(process.env.OPENOCEAN_API_KEY),
103+
},
101104
},
102105
},
103106
},
@@ -295,7 +298,6 @@ export class StrategyBalmySDK {
295298
sourcesFilter?: SourcesFilter,
296299
) {
297300
// TODO type
298-
// console.log('this.sdk.quoteService: ', this.sdk.quoteService);
299301
const bestQuote = await this.sdk.quoteService.getBestQuote({
300302
request: this.#getSDKQuoteFromSwapParams(swapParams, sourcesFilter),
301303
config: {

0 commit comments

Comments
 (0)