Skip to content

Commit cd34bb6

Browse files
committed
idle WIP
1 parent 81b850b commit cd34bb6

File tree

7 files changed

+2099
-7
lines changed

7 files changed

+2099
-7
lines changed

src/swapService/config/mainnet.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ const PT_WSTUSR1740182579 = "0xd0097149aa4cc0d0e1fc99b8bd73fc17dc32c1e9"
1515
const PT_WSTUSR_27MAR2025_MAINNET = "0xA8c8861b5ccF8CCe0ade6811CD2A7A7d3222B0B8"
1616
const USD0PLUSPLUS_MAINNET = "0x35d8949372d46b7a3d5a56006ae77b215fc69bc0"
1717
const YNETHX_MAINNET = "0x657d9aba1dbb59e53f9f3ecaa878447dcfc96dcb"
18+
const IDLEAATRANCHEFASANARA_MAINNET =
19+
"0x45054c6753b4Bce40C5d54418DabC20b070F85bE"
1820

1921
const mainnetRoutingConfig: ChainRoutingConfig = [
2022
// WRAPPERS
@@ -37,6 +39,15 @@ const mainnetRoutingConfig: ChainRoutingConfig = [
3739
excludeTokensInOrOut: [PT_WSTUSR_27MAR2025_MAINNET],
3840
},
3941
},
42+
{
43+
strategy: StrategyBalmySDK.name(),
44+
config: {
45+
sourcesFilter: {
46+
includeSources: ["idle-tranche"],
47+
},
48+
},
49+
match: { tokensInOrOut: [IDLEAATRANCHEFASANARA_MAINNET] },
50+
},
4051
{
4152
strategy: StrategyBalmySDK.name(),
4253
config: {

src/swapService/strategies/balmySDK/customSourceList.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { IFetchService, IProviderService } from "@balmy/sdk"
22
import { LocalSourceList } from "@balmy/sdk/dist/services/quotes/source-lists/local-source-list"
3+
import { CustomIdleAATrancheQuoteSource } from "./idleAATrancheQuoteSource"
34
import { CustomLiFiQuoteSource } from "./lifiQuoteSource"
45
import { CustomNeptuneQuoteSource } from "./neptuneQuoteSource"
56
import { CustomOdosQuoteSource } from "./odosQuoteSource"
@@ -19,6 +20,7 @@ const customSources = {
1920
"open-ocean": new CustomOpenOceanQuoteSource(),
2021
neptune: new CustomNeptuneQuoteSource(),
2122
odos: new CustomOdosQuoteSource(),
23+
"idle-tranche": new CustomIdleAATrancheQuoteSource(),
2224
}
2325
export class CustomSourceList extends LocalSourceList {
2426
constructor({ providerService, fetchService }: ConstructorParameters) {
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
import { Chains } from "@balmy/sdk"
2+
import type {
3+
BuildTxParams,
4+
IQuoteSource,
5+
QuoteParams,
6+
QuoteSourceMetadata,
7+
SourceQuoteResponse,
8+
SourceQuoteTransaction,
9+
} from "@balmy/sdk/dist/services/quotes/quote-sources/types"
10+
import {
11+
addQuoteSlippage,
12+
calculateAllowanceTarget,
13+
} from "@balmy/sdk/dist/services/quotes/quote-sources/utils"
14+
import {
15+
type Address,
16+
type PublicClient,
17+
encodeFunctionData,
18+
isAddressEqual,
19+
} from "viem"
20+
21+
const assets: {
22+
cdo: Address
23+
aaTranche: Address
24+
aaTrancheVault: Address
25+
token: Address
26+
tokenDecimals: bigint
27+
swapHandler: Address
28+
priceOne: bigint
29+
}[] = [
30+
{
31+
// IdleCDO AA Tranche - idle_Fasanara
32+
cdo: "0xf6223C567F21E33e859ED7A045773526E9E3c2D5",
33+
aaTranche: "0x45054c6753b4Bce40C5d54418DabC20b070F85bE",
34+
aaTrancheVault: "0xd820C8129a853a04dC7e42C64aE62509f531eE5A",
35+
token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
36+
swapHandler: "0xA24689b6Ab48eCcF7038c70eBC39f9ed4217aFE3",
37+
tokenDecimals: 6n,
38+
priceOne: 1000000n,
39+
},
40+
]
41+
42+
// Supported networks: https://docs.1inch.io/docs/aggregation-protocol/introduction/#supported-networkschains
43+
export const IDLEAATRANCHE_METADATA: QuoteSourceMetadata<IdleAATrancheSupport> =
44+
{
45+
name: "IdleCDO",
46+
supports: {
47+
chains: [Chains.ETHEREUM.chainId],
48+
swapAndTransfer: true,
49+
buyOrders: false,
50+
},
51+
logoURI: "",
52+
}
53+
54+
type IdleAATrancheSupport = { buyOrders: false; swapAndTransfer: true }
55+
type IdleAATrancheConfig = object
56+
type IdleAATrancheData = { tx: SourceQuoteTransaction }
57+
export class CustomIdleAATrancheQuoteSource
58+
implements
59+
IQuoteSource<IdleAATrancheSupport, IdleAATrancheConfig, IdleAATrancheData>
60+
{
61+
getMetadata() {
62+
return IDLEAATRANCHE_METADATA
63+
}
64+
65+
async quote(
66+
params: QuoteParams<IdleAATrancheSupport, IdleAATrancheConfig>,
67+
): Promise<SourceQuoteResponse<IdleAATrancheData>> {
68+
const asset = assets.find(
69+
(a) =>
70+
a.aaTranche === params.request.sellToken ||
71+
a.aaTranche === params.request.buyToken,
72+
)
73+
if (!asset) throw new Error("Asset not found")
74+
75+
const viemClient = params.components.providerService.getViemPublicClient({
76+
chainId: params.request.chainId,
77+
}) as PublicClient
78+
const virtualPrice = await fetchVirtualPrice(
79+
viemClient,
80+
asset.cdo,
81+
asset.aaTranche,
82+
)
83+
84+
const to = asset.swapHandler
85+
let amountOut
86+
let data
87+
if (isAddressEqual(params.request.buyToken as Address, asset.aaTranche)) {
88+
amountOut =
89+
(params.request.order.sellAmount *
90+
asset.priceOne *
91+
10n ** (18n - asset.tokenDecimals)) /
92+
virtualPrice
93+
data = encodeSwapExactTokensForAATranche(params.request.order.sellAmount)
94+
} else {
95+
amountOut =
96+
(params.request.order.sellAmount *
97+
virtualPrice *
98+
10n ** (18n - asset.tokenDecimals)) /
99+
asset.priceOne
100+
data = encodeSwapExactAATrancheForTokens(params.request.order.sellAmount)
101+
}
102+
103+
const quote = {
104+
sellAmount: params.request.order.sellAmount,
105+
buyAmount: BigInt(amountOut),
106+
estimatedGas: undefined,
107+
allowanceTarget: calculateAllowanceTarget(params.request.sellToken, to),
108+
customData: {
109+
tx: {
110+
to,
111+
calldata: data,
112+
value: 0n,
113+
},
114+
},
115+
}
116+
117+
return addQuoteSlippage(
118+
quote,
119+
params.request.order.type,
120+
params.request.config.slippagePercentage,
121+
)
122+
}
123+
124+
async buildTx({
125+
request,
126+
}: BuildTxParams<
127+
IdleAATrancheConfig,
128+
IdleAATrancheData
129+
>): Promise<SourceQuoteTransaction> {
130+
return request.customData.tx
131+
}
132+
133+
isConfigAndContextValidForQuoting(
134+
config: Partial<IdleAATrancheConfig> | undefined,
135+
): config is IdleAATrancheConfig {
136+
return true
137+
}
138+
139+
isConfigAndContextValidForTxBuilding(
140+
config: Partial<IdleAATrancheConfig> | undefined,
141+
): config is IdleAATrancheConfig {
142+
return true
143+
}
144+
}
145+
146+
function encodeSwapExactTokensForAATranche(amount: bigint) {
147+
const abiItem = {
148+
inputs: [{ name: "amountIn", type: "uint256" }],
149+
name: "swapExactTokensForAATranche",
150+
stateMutability: "nonpayable",
151+
type: "function",
152+
}
153+
154+
const functionData = encodeFunctionData({
155+
abi: [abiItem],
156+
args: [amount],
157+
})
158+
159+
return functionData
160+
}
161+
162+
function encodeSwapExactAATrancheForTokens(amount: bigint) {
163+
const abiItem = {
164+
inputs: [{ name: "amountIn", type: "uint256" }],
165+
name: "swapExactAATrancheForTokens",
166+
stateMutability: "nonpayable",
167+
type: "function",
168+
}
169+
170+
const functionData = encodeFunctionData({
171+
abi: [abiItem],
172+
args: [amount],
173+
})
174+
175+
return functionData
176+
}
177+
178+
export async function fetchVirtualPrice(
179+
client: PublicClient,
180+
cdo: Address,
181+
tranche: Address,
182+
) {
183+
const abiItem = {
184+
name: "virtualPrice",
185+
inputs: [{ name: "_tranche", type: "address" }],
186+
outputs: [{ name: "_virtualPrice", type: "uint256" }],
187+
stateMutability: "view",
188+
type: "function",
189+
}
190+
191+
const query = {
192+
address: cdo,
193+
abi: [abiItem],
194+
functionName: "virtualPrice",
195+
args: [tranche],
196+
} as const
197+
198+
const data = (await client.readContract(query)) as bigint
199+
200+
return data
201+
}

src/swapService/strategies/strategyBalmySDK.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,7 @@ export class StrategyBalmySDK {
505505
? getAddress(sdkQuote.source.allowanceTarget)
506506
: undefined
507507

508+
console.log("allowanceTarget: ", allowanceTarget)
508509
return {
509510
swapParams,
510511
amountIn: sdkQuote.sellAmount.amount,

src/swapService/strategies/strategyERC4626Wrapper.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -230,10 +230,11 @@ export class StrategyERC4626Wrapper {
230230
)
231231

232232
const vaultData = this.getSupportedVault(swapParams.tokenIn.addressInfo)
233-
233+
const tokenIn = findToken(swapParams.chainId, vaultData.asset)
234+
if (!tokenIn) throw new Error("Inner token not found")
234235
const innerSwapParams = {
235236
...swapParams,
236-
tokenIn: findToken(swapParams.chainId, vaultData.asset),
237+
tokenIn,
237238
amount: redeemAmountOut,
238239
}
239240

@@ -325,10 +326,11 @@ export class StrategyERC4626Wrapper {
325326
swapParams: SwapParams,
326327
): Promise<SwapApiResponse> {
327328
const vaultData = this.getSupportedVault(swapParams.tokenOut.addressInfo)
328-
329+
const tokenOut = findToken(swapParams.chainId, vaultData.asset)
330+
if (!tokenOut) throw new Error("Inner token not found")
329331
const innerSwapParams = {
330332
...swapParams,
331-
tokenOut: findToken(swapParams.chainId, vaultData.asset),
333+
tokenOut,
332334
receiver: swapParams.from,
333335
}
334336

@@ -436,9 +438,11 @@ export class StrategyERC4626Wrapper {
436438
): Promise<SwapApiResponse> {
437439
// TODO expects dust out - add to dust list
438440
const vaultData = this.getSupportedVault(swapParams.tokenIn.addressInfo)
441+
const tokenIn = findToken(swapParams.chainId, vaultData.asset)
442+
if (!tokenIn) throw new Error("Inner token not found")
439443
const innerSwapParams = {
440444
...swapParams,
441-
tokenIn: findToken(swapParams.chainId, vaultData.asset),
445+
tokenIn,
442446
vaultIn: vaultData.assetDustEVault,
443447
onlyFixedInputExactOut: true, // eliminate dust in the intermediate asset (vault underlying)
444448
}
@@ -548,9 +552,11 @@ export class StrategyERC4626Wrapper {
548552
const vaultData = this.getSupportedVault(swapParams.tokenOut.addressInfo)
549553

550554
const mintAmount = adjustForInterest(swapParams.amount)
555+
const tokenIn = findToken(swapParams.chainId, vaultData.asset)
556+
if (!tokenIn) throw new Error("Inner token in not found")
551557
const mintSwapParams = {
552558
...swapParams,
553-
tokenIn: findToken(swapParams.chainId, vaultData.asset),
559+
tokenIn,
554560
vaultIn: vaultData.assetDustEVault,
555561
}
556562

@@ -565,10 +571,12 @@ export class StrategyERC4626Wrapper {
565571
swapParams.from,
566572
)
567573

574+
const tokenOut = findToken(swapParams.chainId, vaultData.asset)
575+
if (!tokenOut) throw new Error("Inner token not found")
568576
const innerSwapParams = {
569577
...swapParams,
570578
amount: mintAmountIn,
571-
tokenOut: findToken(swapParams.chainId, vaultData.asset),
579+
tokenOut,
572580
receiver: swapParams.from,
573581
onlyFixedInputExactOut: true, // this option will overswap, which should cover growing exchange rate
574582
}

0 commit comments

Comments
 (0)