Skip to content

Commit 8b905e2

Browse files
committed
Merge branch 'redirect-deposit'
2 parents 58653c9 + 3a927cc commit 8b905e2

File tree

6 files changed

+202
-2
lines changed

6 files changed

+202
-2
lines changed

src/swapService/config/mainnet.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
StrategyERC4626Wrapper,
77
StrategyIdleCDOTranche,
88
StrategyMidas,
9+
StrategyRedirectDepositWrapper,
910
StrategyRepayWrapper,
1011
} from "../strategies"
1112

@@ -22,6 +23,8 @@ const IDLEAATRANCHEFASANARA_MAINNET =
2223
"0x45054c6753b4Bce40C5d54418DabC20b070F85bE"
2324
const CUSDOUSDC_CURVELP_MAINNET = "0x90455bd11Ce8a67C57d467e634Dc142b8e4105Aa"
2425

26+
const USUAL_USD0_VAULT_MAINNET = "0xd001f0a15D272542687b2677BA627f48A4333b5d"
27+
2528
const mainnetRoutingConfig: ChainRoutingConfig = [
2629
// WRAPPERS
2730
{
@@ -125,7 +128,13 @@ const mainnetRoutingConfig: ChainRoutingConfig = [
125128
},
126129
},
127130
// FALLBACKS
128-
131+
// If exact out for Usual's USD0 repay doesn't work, over swap with deposit to escrow
132+
{
133+
strategy: StrategyRedirectDepositWrapper.name(),
134+
match: {
135+
repayVaults: [USUAL_USD0_VAULT_MAINNET],
136+
},
137+
},
129138
// Binary search overswap for target debt
130139
{
131140
strategy: StrategyBalmySDK.name(),

src/swapService/interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export interface StrategyMatchConfig {
7171
isPendlePT?: boolean
7272
tokensInOrOut?: Address[]
7373
excludeTokensInOrOut?: Address[]
74+
repayVaults?: Address[]
7475
}
7576

7677
export interface RoutingItem {

src/swapService/runner.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@ export async function runPipeline(
5858
)
5959
}
6060

61-
// console.log('finalResult.response: ', JSON.stringify(finalResult.response, null, 2));
61+
console.log(
62+
"finalResult.response: ",
63+
JSON.stringify(finalResult.response, null, 2),
64+
)
6265
return finalResult.response
6366
}
6467

src/swapService/strategies/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { StrategyCurveLPNG } from "./strategyCurveLPNG"
44
import { StrategyERC4626Wrapper } from "./strategyERC4626Wrapper"
55
import { StrategyIdleCDOTranche } from "./strategyIdleCDOTranche"
66
import { StrategyMidas } from "./strategyMidas"
7+
import { StrategyRedirectDepositWrapper } from "./strategyRedirectDepositWrapper"
78
import { StrategyRepayWrapper } from "./strategyRepayWrapper"
89

910
export {
@@ -14,6 +15,7 @@ export {
1415
StrategyERC4626Wrapper,
1516
StrategyIdleCDOTranche,
1617
StrategyCurveLPNG,
18+
StrategyRedirectDepositWrapper,
1719
}
1820

1921
export const strategies = {
@@ -24,4 +26,5 @@ export const strategies = {
2426
[StrategyERC4626Wrapper.name()]: StrategyERC4626Wrapper,
2527
[StrategyIdleCDOTranche.name()]: StrategyIdleCDOTranche,
2628
[StrategyCurveLPNG.name()]: StrategyCurveLPNG,
29+
[StrategyRedirectDepositWrapper.name()]: StrategyRedirectDepositWrapper,
2730
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import { type Address, isAddressEqual, maxUint256 } from "viem"
2+
import { getRoutingConfig } from "../config"
3+
import { SwapperMode } from "../interface"
4+
import { runPipeline } from "../runner"
5+
import type { StrategyResult, SwapParams } from "../types"
6+
import {
7+
adjustForInterest,
8+
buildApiResponseSwap,
9+
buildApiResponseVerifyDebtMax,
10+
encodeDepositMulticallItem,
11+
encodeRepayAndDepositMulticallItem,
12+
encodeRepayMulticallItem,
13+
encodeSwapMulticallItem,
14+
matchParams,
15+
} from "../utils"
16+
17+
const defaultConfig: {
18+
supportedVaults: Array<{
19+
chainId: number
20+
vault: Address
21+
asset: Address
22+
assetDustEVault: Address
23+
}>
24+
} = {
25+
supportedVaults: [
26+
{
27+
chainId: 1,
28+
vault: "0xd001f0a15D272542687b2677BA627f48A4333b5d",
29+
asset: "0x73A15FeD60Bf67631dC6cd7Bc5B6e8da8190aCF5",
30+
assetDustEVault: "0xB0465546E8D70E667d4a187F66eF959B1522cc77",
31+
},
32+
],
33+
}
34+
35+
// Wrapper which redirects deposit of over-swapped repay to vault other than the debt vault
36+
export class StrategyRedirectDepositWrapper {
37+
static name() {
38+
return "redirect_deposit_wrapper"
39+
}
40+
readonly match
41+
readonly config
42+
43+
constructor(match = {}, config = defaultConfig) {
44+
this.match = match
45+
this.config = config
46+
}
47+
48+
async supports(swapParams: SwapParams) {
49+
return (
50+
swapParams.swapperMode === SwapperMode.TARGET_DEBT &&
51+
this.config.supportedVaults.some(
52+
(v) =>
53+
v.chainId === swapParams.chainId &&
54+
isAddressEqual(v.vault, swapParams.receiver),
55+
)
56+
)
57+
}
58+
59+
async findSwap(swapParams: SwapParams): Promise<StrategyResult> {
60+
const result: StrategyResult = {
61+
strategy: StrategyRedirectDepositWrapper.name(),
62+
supports: await this.supports(swapParams),
63+
match: matchParams(swapParams, this.match),
64+
}
65+
66+
if (!result.supports || !result.match) return result
67+
68+
try {
69+
const vaultData = this.getSupportedVault(swapParams.receiver)
70+
// remove itself from the routing and run the pipeline, directing output to Swapper
71+
const routing = getRoutingConfig(swapParams.chainId).filter(
72+
(r) => r.strategy !== StrategyRedirectDepositWrapper.name(),
73+
)
74+
75+
const innerSwapParams = {
76+
...swapParams,
77+
receiver: swapParams.from,
78+
routingOverride: routing,
79+
}
80+
81+
const innerSwap = await runPipeline(innerSwapParams)
82+
83+
// split target debt repay into swap to Swapper, repay and deposit into escrow vault
84+
85+
const newMulticallItems = innerSwap.swap.multicallItems.flatMap(
86+
(item) => {
87+
if (
88+
item.functionName === "swap" &&
89+
item.args[0].mode === String(SwapperMode.TARGET_DEBT)
90+
) {
91+
const exactInSwapItemArgs = {
92+
...item.args[0],
93+
mode: SwapperMode.EXACT_IN,
94+
}
95+
96+
console.log("exactInSwapItemArgs: ", exactInSwapItemArgs)
97+
const swapItem = encodeSwapMulticallItem(exactInSwapItemArgs)
98+
// if target debt is 0, encode repay(max) to repay all debt, otherwise use all of the available Swapper balance
99+
const repayAmount =
100+
swapParams.targetDebt === 0n ? maxUint256 : maxUint256 - 1n
101+
console.log("repayAmount: ", repayAmount === maxUint256)
102+
const repayItem = encodeRepayMulticallItem(
103+
vaultData.asset,
104+
swapParams.receiver,
105+
repayAmount,
106+
swapParams.accountOut,
107+
)
108+
const depositItem = encodeDepositMulticallItem(
109+
vaultData.asset,
110+
vaultData.assetDustEVault,
111+
5n,
112+
swapParams.accountOut,
113+
)
114+
115+
return [swapItem, repayItem, depositItem]
116+
}
117+
return item
118+
},
119+
)
120+
121+
// reencode everything
122+
123+
const swap = buildApiResponseSwap(swapParams.from, newMulticallItems)
124+
125+
let debtMax = swapParams.currentDebt - BigInt(innerSwap.amountOutMin)
126+
if (debtMax < 0n) debtMax = 0n
127+
debtMax = adjustForInterest(debtMax)
128+
129+
const verify = buildApiResponseVerifyDebtMax(
130+
swapParams.chainId,
131+
swapParams.receiver,
132+
swapParams.accountOut,
133+
debtMax,
134+
swapParams.deadline,
135+
)
136+
console.log("swapParams.deadline: ", swapParams.deadline)
137+
138+
result.response = {
139+
...innerSwap,
140+
swap,
141+
verify,
142+
}
143+
} catch (error) {
144+
result.error = error
145+
}
146+
147+
return result
148+
}
149+
150+
getSupportedVault(vault: Address) {
151+
const supportedVault = this.config.supportedVaults.find((v) =>
152+
isAddressEqual(v.vault, vault),
153+
)
154+
if (!supportedVault) throw new Error("Vault not supported")
155+
156+
return supportedVault
157+
}
158+
}

src/swapService/utils.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,15 @@ export function matchParams(
8383
)
8484
return false
8585
}
86+
if (match.repayVaults) {
87+
if (
88+
!swapParams.isRepay ||
89+
!match.repayVaults.some((vault: Hex) => {
90+
return isAddressEqual(swapParams.receiver, vault)
91+
})
92+
)
93+
return false
94+
}
8695

8796
return true
8897
}
@@ -356,6 +365,23 @@ export const encodeRepayAndDepositMulticallItem = (
356365
}
357366
}
358367

368+
export const encodeRepayMulticallItem = (
369+
token: Address,
370+
vault: Address,
371+
repayAmount: bigint,
372+
account: Address,
373+
): SwapApiResponseMulticallItem => {
374+
return {
375+
functionName: "repay",
376+
args: [token, vault, String(repayAmount), account],
377+
data: encodeFunctionData({
378+
abi: contractBook.swapper.abi,
379+
functionName: "repay",
380+
args: [token, vault, repayAmount, account],
381+
}),
382+
}
383+
}
384+
359385
export const encodeApproveMulticallItem = (
360386
token: Address,
361387
spender: Address,

0 commit comments

Comments
 (0)