From a8eb59f08ddcd2ea2d1c7b4fb9f302bc1049e528 Mon Sep 17 00:00:00 2001 From: chad-js Date: Tue, 9 Dec 2025 16:30:29 -0500 Subject: [PATCH 1/6] feat: infinifi integration --- abis/InfinifiGateway.ts | 414 +++++++++++++++++++++++ abis/siUSD.ts | 457 ++++++++++++++++++++++++++ src/constants/contracts.ts | 4 + src/services/routing/getSwapParams.ts | 23 +- src/services/routing/infinifi.ts | 110 +++++++ src/subscribers/auctionCreated.ts | 9 + src/types/index.ts | 6 + 7 files changed, 1022 insertions(+), 1 deletion(-) create mode 100644 abis/InfinifiGateway.ts create mode 100644 abis/siUSD.ts create mode 100644 src/services/routing/infinifi.ts diff --git a/abis/InfinifiGateway.ts b/abis/InfinifiGateway.ts new file mode 100644 index 0000000..7b6e2c8 --- /dev/null +++ b/abis/InfinifiGateway.ts @@ -0,0 +1,414 @@ +const infinifiGatewayAbi = [ + { inputs: [], stateMutability: "nonpayable", type: "constructor" }, + { inputs: [], name: "EnforcedPause", type: "error" }, + { inputs: [], name: "ExpectedPause", type: "error" }, + { inputs: [], name: "InvalidZapFee", type: "error" }, + { inputs: [], name: "InvalidZapRouter", type: "error" }, + { + inputs: [ + { internalType: "uint256", name: "min", type: "uint256" }, + { internalType: "uint256", name: "actual", type: "uint256" }, + ], + name: "MinAssetsOutError", + type: "error", + }, + { inputs: [], name: "PendingLossesUnapplied", type: "error" }, + { inputs: [], name: "ReentrancyGuardReentrantCall", type: "error" }, + { + inputs: [{ internalType: "address", name: "token", type: "address" }], + name: "SafeERC20FailedOperation", + type: "error", + }, + { inputs: [], name: "SwapFailed", type: "error" }, + { + inputs: [{ internalType: "bytes", name: "returnData", type: "bytes" }], + name: "UnderlyingCallReverted", + type: "error", + }, + { + anonymous: false, + inputs: [ + { indexed: false, internalType: "uint256", name: "timestamp", type: "uint256" }, + { indexed: true, internalType: "string", name: "name", type: "string" }, + { indexed: false, internalType: "address", name: "_address", type: "address" }, + ], + name: "AddressSet", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "address", name: "oldCore", type: "address" }, + { indexed: true, internalType: "address", name: "newCore", type: "address" }, + ], + name: "CoreUpdate", + type: "event", + }, + { + anonymous: false, + inputs: [{ indexed: false, internalType: "address", name: "account", type: "address" }], + name: "Paused", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: false, internalType: "uint256", name: "timestamp", type: "uint256" }, + { indexed: false, internalType: "address", name: "router", type: "address" }, + { indexed: false, internalType: "bool", name: "enabled", type: "bool" }, + ], + name: "SetEnabledRouter", + type: "event", + }, + { + anonymous: false, + inputs: [{ indexed: false, internalType: "address", name: "account", type: "address" }], + name: "Unpaused", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: false, internalType: "uint256", name: "timestamp", type: "uint256" }, + { indexed: false, internalType: "uint256", name: "zapFee", type: "uint256" }, + ], + name: "ZapFeeSet", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: false, internalType: "uint256", name: "timestamp", type: "uint256" }, + { indexed: true, internalType: "address", name: "user", type: "address" }, + { indexed: true, internalType: "address", name: "token", type: "address" }, + { indexed: false, internalType: "uint256", name: "amount", type: "uint256" }, + { indexed: false, internalType: "uint256", name: "receiptTokens", type: "uint256" }, + ], + name: "ZapIn", + type: "event", + }, + { + inputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], + name: "addresses", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "_unwindingTimestamp", type: "uint256" }, + { internalType: "uint32", name: "_newUnwindingEpochs", type: "uint32" }, + ], + name: "cancelUnwinding", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { inputs: [], name: "claimRedemption", outputs: [], stateMutability: "nonpayable", type: "function" }, + { + inputs: [], + name: "core", + outputs: [{ internalType: "contract InfiniFiCore", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "_amount", type: "uint256" }, + { internalType: "uint32", name: "_unwindingEpochs", type: "uint32" }, + { internalType: "address", name: "_recipient", type: "address" }, + ], + name: "createPosition", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + components: [ + { internalType: "address", name: "target", type: "address" }, + { internalType: "uint256", name: "value", type: "uint256" }, + { internalType: "bytes", name: "callData", type: "bytes" }, + ], + internalType: "struct CoreControlled.Call[]", + name: "calls", + type: "tuple[]", + }, + ], + name: "emergencyAction", + outputs: [{ internalType: "bytes[]", name: "returnData", type: "bytes[]" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "", type: "address" }], + name: "enabledRouters", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "string", name: "_name", type: "string" }], + name: "getAddress", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "uint32", name: "_oldUnwindingEpochs", type: "uint32" }, + { internalType: "uint32", name: "_newUnwindingEpochs", type: "uint32" }, + { internalType: "uint256", name: "_shares", type: "uint256" }, + ], + name: "increaseUnwindingEpochs", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "_core", type: "address" }], + name: "init", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_to", type: "address" }, + { internalType: "uint256", name: "_amount", type: "uint256" }, + ], + name: "mint", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_to", type: "address" }, + { internalType: "uint256", name: "_amount", type: "uint256" }, + { internalType: "uint32", name: "_unwindingEpochs", type: "uint32" }, + ], + name: "mintAndLock", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_to", type: "address" }, + { internalType: "uint256", name: "_amount", type: "uint256" }, + ], + name: "mintAndStake", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address[]", name: "_assets", type: "address[]" }, + { internalType: "uint32[]", name: "_unwindingEpochs", type: "uint32[]" }, + { + components: [ + { internalType: "address", name: "farm", type: "address" }, + { internalType: "uint96", name: "weight", type: "uint96" }, + ], + internalType: "struct AllocationVoting.AllocationVote[][]", + name: "_liquidVotes", + type: "tuple[][]", + }, + { + components: [ + { internalType: "address", name: "farm", type: "address" }, + { internalType: "uint96", name: "weight", type: "uint96" }, + ], + internalType: "struct AllocationVoting.AllocationVote[][]", + name: "_illiquidVotes", + type: "tuple[][]", + }, + ], + name: "multiVote", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { inputs: [], name: "pause", outputs: [], stateMutability: "nonpayable", type: "function" }, + { + inputs: [], + name: "paused", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_to", type: "address" }, + { internalType: "uint256", name: "_amount", type: "uint256" }, + { internalType: "uint256", name: "_minAssetsOut", type: "uint256" }, + ], + name: "redeem", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "string", name: "_name", type: "string" }, + { internalType: "address", name: "_address", type: "address" }, + ], + name: "setAddress", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "newCore", type: "address" }], + name: "setCore", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_router", type: "address" }, + { internalType: "bool", name: "_enabled", type: "bool" }, + ], + name: "setEnabledRouter", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_zapFee", type: "uint256" }], + name: "setZapFee", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_to", type: "address" }, + { internalType: "uint256", name: "_receiptTokens", type: "uint256" }, + ], + name: "stake", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "_shares", type: "uint256" }, + { internalType: "uint32", name: "_unwindingEpochs", type: "uint32" }, + ], + name: "startUnwinding", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { inputs: [], name: "unpause", outputs: [], stateMutability: "nonpayable", type: "function" }, + { + inputs: [ + { internalType: "address", name: "_to", type: "address" }, + { internalType: "uint256", name: "_stakedTokens", type: "uint256" }, + ], + name: "unstake", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_to", type: "address" }, + { internalType: "uint256", name: "_amount", type: "uint256" }, + { internalType: "uint32", name: "_unwindingEpochs", type: "uint32" }, + ], + name: "unstakeAndLock", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_asset", type: "address" }, + { internalType: "uint32", name: "_unwindingEpochs", type: "uint32" }, + { + components: [ + { internalType: "address", name: "farm", type: "address" }, + { internalType: "uint96", name: "weight", type: "uint96" }, + ], + internalType: "struct AllocationVoting.AllocationVote[]", + name: "_liquidVotes", + type: "tuple[]", + }, + { + components: [ + { internalType: "address", name: "farm", type: "address" }, + { internalType: "uint96", name: "weight", type: "uint96" }, + ], + internalType: "struct AllocationVoting.AllocationVote[]", + name: "_illiquidVotes", + type: "tuple[]", + }, + ], + name: "vote", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_unwindingTimestamp", type: "uint256" }], + name: "withdraw", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "zapFee", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_token", type: "address" }, + { internalType: "uint256", name: "_amount", type: "uint256" }, + { internalType: "address", name: "_router", type: "address" }, + { internalType: "bytes", name: "_routerData", type: "bytes" }, + { internalType: "address", name: "_to", type: "address" }, + ], + name: "zapIn", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_token", type: "address" }, + { internalType: "uint256", name: "_amount", type: "uint256" }, + { internalType: "address", name: "_router", type: "address" }, + { internalType: "bytes", name: "_routerData", type: "bytes" }, + { internalType: "uint32", name: "_unwindingEpochs", type: "uint32" }, + { internalType: "address", name: "_to", type: "address" }, + ], + name: "zapInAndLock", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_token", type: "address" }, + { internalType: "uint256", name: "_amount", type: "uint256" }, + { internalType: "address", name: "_router", type: "address" }, + { internalType: "bytes", name: "_routerData", type: "bytes" }, + { internalType: "address", name: "_to", type: "address" }, + ], + name: "zapInAndStake", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "payable", + type: "function", + }, +] as const; + +export default infinifiGatewayAbi; diff --git a/abis/siUSD.ts b/abis/siUSD.ts new file mode 100644 index 0000000..a59caf1 --- /dev/null +++ b/abis/siUSD.ts @@ -0,0 +1,457 @@ +const siUSDAbi = [ + { + inputs: [ + { internalType: "address", name: "_core", type: "address" }, + { internalType: "address", name: "_receiptToken", type: "address" }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { + inputs: [ + { internalType: "address", name: "spender", type: "address" }, + { internalType: "uint256", name: "allowance", type: "uint256" }, + { internalType: "uint256", name: "needed", type: "uint256" }, + ], + name: "ERC20InsufficientAllowance", + type: "error", + }, + { + inputs: [ + { internalType: "address", name: "sender", type: "address" }, + { internalType: "uint256", name: "balance", type: "uint256" }, + { internalType: "uint256", name: "needed", type: "uint256" }, + ], + name: "ERC20InsufficientBalance", + type: "error", + }, + { + inputs: [{ internalType: "address", name: "approver", type: "address" }], + name: "ERC20InvalidApprover", + type: "error", + }, + { + inputs: [{ internalType: "address", name: "receiver", type: "address" }], + name: "ERC20InvalidReceiver", + type: "error", + }, + { inputs: [{ internalType: "address", name: "sender", type: "address" }], name: "ERC20InvalidSender", type: "error" }, + { + inputs: [{ internalType: "address", name: "spender", type: "address" }], + name: "ERC20InvalidSpender", + type: "error", + }, + { + inputs: [ + { internalType: "address", name: "receiver", type: "address" }, + { internalType: "uint256", name: "assets", type: "uint256" }, + { internalType: "uint256", name: "max", type: "uint256" }, + ], + name: "ERC4626ExceededMaxDeposit", + type: "error", + }, + { + inputs: [ + { internalType: "address", name: "receiver", type: "address" }, + { internalType: "uint256", name: "shares", type: "uint256" }, + { internalType: "uint256", name: "max", type: "uint256" }, + ], + name: "ERC4626ExceededMaxMint", + type: "error", + }, + { + inputs: [ + { internalType: "address", name: "owner", type: "address" }, + { internalType: "uint256", name: "shares", type: "uint256" }, + { internalType: "uint256", name: "max", type: "uint256" }, + ], + name: "ERC4626ExceededMaxRedeem", + type: "error", + }, + { + inputs: [ + { internalType: "address", name: "owner", type: "address" }, + { internalType: "uint256", name: "assets", type: "uint256" }, + { internalType: "uint256", name: "max", type: "uint256" }, + ], + name: "ERC4626ExceededMaxWithdraw", + type: "error", + }, + { inputs: [], name: "EnforcedPause", type: "error" }, + { inputs: [], name: "ExpectedPause", type: "error" }, + { inputs: [], name: "PendingLossesUnapplied", type: "error" }, + { + inputs: [{ internalType: "address", name: "token", type: "address" }], + name: "SafeERC20FailedOperation", + type: "error", + }, + { + inputs: [{ internalType: "bytes", name: "returnData", type: "bytes" }], + name: "UnderlyingCallReverted", + type: "error", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "address", name: "owner", type: "address" }, + { indexed: true, internalType: "address", name: "spender", type: "address" }, + { indexed: false, internalType: "uint256", name: "value", type: "uint256" }, + ], + name: "Approval", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "address", name: "oldCore", type: "address" }, + { indexed: true, internalType: "address", name: "newCore", type: "address" }, + ], + name: "CoreUpdate", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "address", name: "sender", type: "address" }, + { indexed: true, internalType: "address", name: "owner", type: "address" }, + { indexed: false, internalType: "uint256", name: "assets", type: "uint256" }, + { indexed: false, internalType: "uint256", name: "shares", type: "uint256" }, + ], + name: "Deposit", + type: "event", + }, + { + anonymous: false, + inputs: [{ indexed: false, internalType: "address", name: "account", type: "address" }], + name: "Paused", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "address", name: "from", type: "address" }, + { indexed: true, internalType: "address", name: "to", type: "address" }, + { indexed: false, internalType: "uint256", name: "value", type: "uint256" }, + ], + name: "Transfer", + type: "event", + }, + { + anonymous: false, + inputs: [{ indexed: false, internalType: "address", name: "account", type: "address" }], + name: "Unpaused", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "uint256", name: "timestamp", type: "uint256" }, + { indexed: false, internalType: "uint256", name: "epoch", type: "uint256" }, + { indexed: false, internalType: "uint256", name: "assets", type: "uint256" }, + ], + name: "VaultLoss", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "uint256", name: "timestamp", type: "uint256" }, + { indexed: false, internalType: "uint256", name: "epoch", type: "uint256" }, + { indexed: false, internalType: "uint256", name: "assets", type: "uint256" }, + ], + name: "VaultProfit", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "address", name: "sender", type: "address" }, + { indexed: true, internalType: "address", name: "receiver", type: "address" }, + { indexed: true, internalType: "address", name: "owner", type: "address" }, + { indexed: false, internalType: "uint256", name: "assets", type: "uint256" }, + { indexed: false, internalType: "uint256", name: "shares", type: "uint256" }, + ], + name: "Withdraw", + type: "event", + }, + { + inputs: [ + { internalType: "address", name: "owner", type: "address" }, + { internalType: "address", name: "spender", type: "address" }, + ], + name: "allowance", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_amount", type: "uint256" }], + name: "applyLosses", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "spender", type: "address" }, + { internalType: "uint256", name: "value", type: "uint256" }, + ], + name: "approve", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "asset", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "balanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "shares", type: "uint256" }], + name: "convertToAssets", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "assets", type: "uint256" }], + name: "convertToShares", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "core", + outputs: [{ internalType: "contract InfiniFiCore", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "decimals", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "assets", type: "uint256" }, + { internalType: "address", name: "receiver", type: "address" }, + ], + name: "deposit", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_amount", type: "uint256" }], + name: "depositRewards", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + components: [ + { internalType: "address", name: "target", type: "address" }, + { internalType: "uint256", name: "value", type: "uint256" }, + { internalType: "bytes", name: "callData", type: "bytes" }, + ], + internalType: "struct CoreControlled.Call[]", + name: "calls", + type: "tuple[]", + }, + ], + name: "emergencyAction", + outputs: [{ internalType: "bytes[]", name: "returnData", type: "bytes[]" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "epoch", type: "uint256" }], + name: "epochRewards", + outputs: [{ internalType: "uint256", name: "rewards", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "_receiver", type: "address" }], + name: "maxDeposit", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "_receiver", type: "address" }], + name: "maxMint", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "_receiver", type: "address" }], + name: "maxRedeem", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "_receiver", type: "address" }], + name: "maxWithdraw", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "shares", type: "uint256" }, + { internalType: "address", name: "receiver", type: "address" }, + ], + name: "mint", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "name", + outputs: [{ internalType: "string", name: "", type: "string" }], + stateMutability: "view", + type: "function", + }, + { inputs: [], name: "pause", outputs: [], stateMutability: "nonpayable", type: "function" }, + { + inputs: [], + name: "paused", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "assets", type: "uint256" }], + name: "previewDeposit", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "shares", type: "uint256" }], + name: "previewMint", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "shares", type: "uint256" }], + name: "previewRedeem", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "assets", type: "uint256" }], + name: "previewWithdraw", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "shares", type: "uint256" }, + { internalType: "address", name: "receiver", type: "address" }, + { internalType: "address", name: "owner", type: "address" }, + ], + name: "redeem", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "newCore", type: "address" }], + name: "setCore", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "_yieldSharing", type: "address" }], + name: "setYieldSharing", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "symbol", + outputs: [{ internalType: "string", name: "", type: "string" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "totalAssets", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "totalSupply", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "to", type: "address" }, + { internalType: "uint256", name: "value", type: "uint256" }, + ], + name: "transfer", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "from", type: "address" }, + { internalType: "address", name: "to", type: "address" }, + { internalType: "uint256", name: "value", type: "uint256" }, + ], + name: "transferFrom", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "nonpayable", + type: "function", + }, + { inputs: [], name: "unpause", outputs: [], stateMutability: "nonpayable", type: "function" }, + { + inputs: [ + { internalType: "uint256", name: "assets", type: "uint256" }, + { internalType: "address", name: "receiver", type: "address" }, + { internalType: "address", name: "owner", type: "address" }, + ], + name: "withdraw", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "yieldSharing", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, +] as const; + +export default siUSDAbi; \ No newline at end of file diff --git a/src/constants/contracts.ts b/src/constants/contracts.ts index 61c6c20..8e1cf90 100644 --- a/src/constants/contracts.ts +++ b/src/constants/contracts.ts @@ -13,14 +13,18 @@ export const CONTRACT_ADDRESSES: Record = { ETHERFI_DEPOSIT_ADAPTER: "0xcfC6d9Bd7411962Bfe7145451A7EF71A24b6A7A2", ETHERFI_LIQUIDITY_POOL: "0x308861A430be4cce5502d0A12724771Fc6DaF216", FLUID_DEX_RESERVES_RESOLVER: "0xC93876C0EEd99645DD53937b25433e311881A27C", + INFINIFI_GATEWAY: "0x3f04b65Ddbd87f9CE0A2e7Eb24d80e7fb87625b5", + IUSD: "0x48f9e38f3070AD8945DFEae3FA70987722E3D89c", LEVERAGE_MANAGER: "0x5C37EB148D4a261ACD101e2B997A0F163Fb3E351", MULTICALL_EXECUTOR: "0x16D02Ebd89988cAd1Ce945807b963aB7A9Fd22E1", PENDLE_ROUTER: "0x888888888889758F76e7103c6CbF23ABbF58F946", PENDLE_ROUTER_STATIC: "0x263833d47eA3fA4a30f269323aba6a107f9eB14C", PRE_LIQUIDATION_REBALANCER: process.env.PRE_LIQUIDATION_REBALANCER_ADDRESS as Address || zeroAddress, WSTETH: "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", + SIUSD: "0xDBDC1Ef57537E34680B898E1FEBD3D68c7389bCB", UNISWAP_SWAP_ROUTER_02: "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45", UNISWAP_V2_ROUTER_02: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", + USDC: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", WETH: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", WEETH: "0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee", }, diff --git a/src/services/routing/getSwapParams.ts b/src/services/routing/getSwapParams.ts index b3a44bb..a796bc7 100644 --- a/src/services/routing/getSwapParams.ts +++ b/src/services/routing/getSwapParams.ts @@ -16,6 +16,7 @@ import { getPendleSwapQuote } from "./pendle"; import { getBalmyQuote, prepareBalmySwapCalldata } from "./balmy"; import { CHAIN_ID } from "../../constants/chain"; import { CONTRACT_ADDRESSES } from "../../constants/contracts"; +import { getInfinifiSiUSDMintAndStakeQuote, getInfinifiSiUSDUnstakeAndRedeemQuote, prepareInfinifiSiUSDMintAndStakeCalldata, prepareInfinifiSiUSDUnstakeAndRedeemCalldata } from "./infinifi"; export const NoQuotesError = new Error('No quotes found'); @@ -24,7 +25,7 @@ const logger = createComponentLogger('getRebalanceSwapParams'); export const getRebalanceSwapParams = async ( input: GetRebalanceSwapParamsInput ): Promise => { - const { takeAmount, requiredAmountIn, stakeType } = input; + const { takeAmount, requiredAmountIn, stakeType, receiver } = input; // We first check if staking / custom route can be used to swap, which typically provides the best price if (stakeType === StakeType.ETHERFI_ETH_WEETH) { @@ -47,6 +48,26 @@ export const getRebalanceSwapParams = async ( swapCalls: prepareLidoEthStakeCalldata(takeAmount), }; } + } else if (stakeType === StakeType.INFINIFI_SIUSD_MINT_AND_STAKE) { + const siUSDAmountOut = await getInfinifiSiUSDMintAndStakeQuote(takeAmount); + + if (siUSDAmountOut >= requiredAmountIn) { + return { + isProfitable: true, + amountOut: siUSDAmountOut, + swapCalls: prepareInfinifiSiUSDMintAndStakeCalldata(receiver, takeAmount, siUSDAmountOut), + }; + } + } else if (stakeType === StakeType.INFINIFI_SIUSD_UNSTAKE_AND_REDEEM) { + const usdcAmountOut = await getInfinifiSiUSDUnstakeAndRedeemQuote(takeAmount); + + if (usdcAmountOut >= requiredAmountIn) { + return { + isProfitable: true, + amountOut: usdcAmountOut, + swapCalls: prepareInfinifiSiUSDUnstakeAndRedeemCalldata(receiver, takeAmount, usdcAmountOut), + }; + } } return getDexSwapParams(input, requiredAmountIn); diff --git a/src/services/routing/infinifi.ts b/src/services/routing/infinifi.ts new file mode 100644 index 0000000..5c783cb --- /dev/null +++ b/src/services/routing/infinifi.ts @@ -0,0 +1,110 @@ +import { CHAIN_ID } from "../../constants/chain"; +import { CONTRACT_ADDRESSES } from "../../constants/contracts"; +import { Address, encodeFunctionData, erc20Abi } from "viem"; +import siUSDAbi from "../../../abis/siUSD"; +import { publicClient } from "../../utils/transactionHelpers"; +import infinifiGatewayAbi from "../../../abis/InfinifiGateway"; +import { Call } from "../../types"; + +export const getInfinifiSiUSDMintAndStakeQuote = async (usdcAmountIn: bigint) => { + if (CHAIN_ID !== 1) { + throw new Error('Native iUSD staking is only supported by the rebalance bot on Ethereum mainnet'); + } + + const siUSDAmountOut = await publicClient.readContract({ + address: CONTRACT_ADDRESSES[CHAIN_ID].SIUSD as Address, + abi: siUSDAbi, + functionName: 'previewDeposit', + args: [usdcAmountIn], // USDC and iUSD are 1:1 + }); + + return siUSDAmountOut; +}; + +export const getInfinifiSiUSDUnstakeAndRedeemQuote = async (siUSDAmountIn: bigint) => { + if (CHAIN_ID !== 1) { + throw new Error('Native siUSD unstaking is only supported by the rebalance bot on Ethereum mainnet'); + } + + const iUSDAmountOut = await publicClient.readContract({ + address: CONTRACT_ADDRESSES[CHAIN_ID].SIUSD as Address, + abi: siUSDAbi, + functionName: 'previewRedeem', + args: [siUSDAmountIn], + }); + + return iUSDAmountOut; // iUSD and USDC are 1:1 +}; + +export const prepareInfinifiSiUSDMintAndStakeCalldata = (receiver: Address, usdcAmountIn: bigint, siUSDAmountIn: bigint): Call[] => { + const approveCalldata = encodeFunctionData({ + abi: erc20Abi, + functionName: 'approve', + args: [CONTRACT_ADDRESSES[CHAIN_ID].INFINIFI_GATEWAY as Address, usdcAmountIn], + }); + const mintAndStakeCalldata = encodeFunctionData({ + abi: infinifiGatewayAbi, + functionName: 'mintAndStake', + args: [receiver, siUSDAmountIn], + }); + + return [ + { + target: CONTRACT_ADDRESSES[CHAIN_ID].USDC as Address, + data: approveCalldata, + value: 0n, + }, + { + target: CONTRACT_ADDRESSES[CHAIN_ID].INFINIFI_GATEWAY as Address, + data: mintAndStakeCalldata, + value: 0n, + }, + ]; +}; + +export const prepareInfinifiSiUSDUnstakeAndRedeemCalldata = (receiver: Address, siUSDAmountIn: bigint, iUsdAmountIn: bigint): Call[] => { + const siUSDApproveCalldata = encodeFunctionData({ + abi: erc20Abi, + functionName: 'approve', + args: [CONTRACT_ADDRESSES[CHAIN_ID].INFINIFI_GATEWAY as Address, siUSDAmountIn], + }); + const unstakeCalldata = encodeFunctionData({ + abi: infinifiGatewayAbi, + functionName: 'unstake', + args: [CONTRACT_ADDRESSES[CHAIN_ID].MULTICALL_EXECUTOR, siUSDAmountIn], + }); + + const iUSDApproveCalldata = encodeFunctionData({ + abi: erc20Abi, + functionName: 'approve', + args: [CONTRACT_ADDRESSES[CHAIN_ID].INFINIFI_GATEWAY as Address, iUsdAmountIn], + }); + const redeemCalldata = encodeFunctionData({ + abi: infinifiGatewayAbi, + functionName: 'redeem', + args: [receiver, iUsdAmountIn, iUsdAmountIn] + }); + + return [ + { + target: CONTRACT_ADDRESSES[CHAIN_ID].SIUSD as Address, + data: siUSDApproveCalldata, + value: 0n, + }, + { + target: CONTRACT_ADDRESSES[CHAIN_ID].INFINIFI_GATEWAY as Address, + data: unstakeCalldata, + value: 0n, + }, + { + target: CONTRACT_ADDRESSES[CHAIN_ID].IUSD as Address, + data: iUSDApproveCalldata, + value: 0n, + }, + { + target: CONTRACT_ADDRESSES[CHAIN_ID].INFINIFI_GATEWAY as Address, + data: redeemCalldata, + value: 0n, + } + ] +}; \ No newline at end of file diff --git a/src/subscribers/auctionCreated.ts b/src/subscribers/auctionCreated.ts index bad0df6..9270044 100644 --- a/src/subscribers/auctionCreated.ts +++ b/src/subscribers/auctionCreated.ts @@ -127,6 +127,15 @@ const getStakeType = (collateralAsset: Address, debtAsset: Address, isOverCollat CHAIN_ID === 1 ) { return StakeType.LIDO_ETH_WSTETH; + } else if ( + isAddressEqual(collateralAsset, CONTRACT_ADDRESSES[CHAIN_ID].SIUSD as Address) && + isAddressEqual(debtAsset, CONTRACT_ADDRESSES[CHAIN_ID].USDC as Address) + ) { + if (!isOverCollateralized) { + return StakeType.INFINIFI_SIUSD_UNSTAKE_AND_REDEEM; + } else { + return StakeType.INFINIFI_SIUSD_MINT_AND_STAKE; + } } else { return StakeType.NONE; } diff --git a/src/types/index.ts b/src/types/index.ts index 27dc5f3..62dcee2 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -10,6 +10,8 @@ export enum StakeType { NONE = 0, ETHERFI_ETH_WEETH = 1, LIDO_ETH_WSTETH = 2, + INFINIFI_SIUSD_MINT_AND_STAKE = 3, + INFINIFI_SIUSD_UNSTAKE_AND_REDEEM = 4, } export enum RebalanceType { @@ -38,14 +40,18 @@ export interface ContractAddresses { ETHERFI_L2_MODE_SYNC_POOL?: Address; ETHERFI_LIQUIDITY_POOL?: Address; FLUID_DEX_RESERVES_RESOLVER?: Address; + INFINIFI_GATEWAY?: Address; + IUSD?: Address; LEVERAGE_MANAGER: Address; MULTICALL_EXECUTOR: Address; PENDLE_ROUTER?: Address; PENDLE_ROUTER_STATIC?: Address; PRE_LIQUIDATION_REBALANCER: Address; WSTETH?: Address; + SIUSD?: Address; UNISWAP_SWAP_ROUTER_02: Address; UNISWAP_V2_ROUTER_02: Address; + USDC?: Address; WETH: Address; WEETH: Address; } From e50af9fa313f6de2a6b29462c3e688b7b84ea914 Mon Sep 17 00:00:00 2001 From: chad-js Date: Wed, 10 Dec 2025 11:22:41 -0500 Subject: [PATCH 2/6] fixes --- src/services/routing/getSwapParams.ts | 6 +++++- src/services/routing/infinifi.ts | 21 ++++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/services/routing/getSwapParams.ts b/src/services/routing/getSwapParams.ts index a796bc7..cd59cec 100644 --- a/src/services/routing/getSwapParams.ts +++ b/src/services/routing/getSwapParams.ts @@ -51,16 +51,20 @@ export const getRebalanceSwapParams = async ( } else if (stakeType === StakeType.INFINIFI_SIUSD_MINT_AND_STAKE) { const siUSDAmountOut = await getInfinifiSiUSDMintAndStakeQuote(takeAmount); + logger.debug({ siUSDAmountOut, requiredAmountIn, takeAmount }, 'Infinifi siUSD mint and stake quote'); + if (siUSDAmountOut >= requiredAmountIn) { return { isProfitable: true, amountOut: siUSDAmountOut, - swapCalls: prepareInfinifiSiUSDMintAndStakeCalldata(receiver, takeAmount, siUSDAmountOut), + swapCalls: prepareInfinifiSiUSDMintAndStakeCalldata(receiver, takeAmount), }; } } else if (stakeType === StakeType.INFINIFI_SIUSD_UNSTAKE_AND_REDEEM) { const usdcAmountOut = await getInfinifiSiUSDUnstakeAndRedeemQuote(takeAmount); + logger.debug({ usdcAmountOut, requiredAmountIn, takeAmount }, 'Infinifi siUSD unstake and redeem quote'); + if (usdcAmountOut >= requiredAmountIn) { return { isProfitable: true, diff --git a/src/services/routing/infinifi.ts b/src/services/routing/infinifi.ts index 5c783cb..55cc63d 100644 --- a/src/services/routing/infinifi.ts +++ b/src/services/routing/infinifi.ts @@ -11,11 +11,14 @@ export const getInfinifiSiUSDMintAndStakeQuote = async (usdcAmountIn: bigint) => throw new Error('Native iUSD staking is only supported by the rebalance bot on Ethereum mainnet'); } + // USDC and iUSD are 1:1, but iUSD has 18 decimals and USDC has 6 decimals + const iUSDAmountIn = usdcAmountIn * 10n ** 12n; + const siUSDAmountOut = await publicClient.readContract({ address: CONTRACT_ADDRESSES[CHAIN_ID].SIUSD as Address, abi: siUSDAbi, functionName: 'previewDeposit', - args: [usdcAmountIn], // USDC and iUSD are 1:1 + args: [iUSDAmountIn], }); return siUSDAmountOut; @@ -33,10 +36,11 @@ export const getInfinifiSiUSDUnstakeAndRedeemQuote = async (siUSDAmountIn: bigin args: [siUSDAmountIn], }); - return iUSDAmountOut; // iUSD and USDC are 1:1 + // iUSD and USDC are 1:1, but iUSD has 18 decimals and USDC has 6 decimals + return iUSDAmountOut / 10n ** 12n; }; -export const prepareInfinifiSiUSDMintAndStakeCalldata = (receiver: Address, usdcAmountIn: bigint, siUSDAmountIn: bigint): Call[] => { +export const prepareInfinifiSiUSDMintAndStakeCalldata = (receiver: Address, usdcAmountIn: bigint): Call[] => { const approveCalldata = encodeFunctionData({ abi: erc20Abi, functionName: 'approve', @@ -45,7 +49,7 @@ export const prepareInfinifiSiUSDMintAndStakeCalldata = (receiver: Address, usdc const mintAndStakeCalldata = encodeFunctionData({ abi: infinifiGatewayAbi, functionName: 'mintAndStake', - args: [receiver, siUSDAmountIn], + args: [receiver, usdcAmountIn], }); return [ @@ -62,7 +66,7 @@ export const prepareInfinifiSiUSDMintAndStakeCalldata = (receiver: Address, usdc ]; }; -export const prepareInfinifiSiUSDUnstakeAndRedeemCalldata = (receiver: Address, siUSDAmountIn: bigint, iUsdAmountIn: bigint): Call[] => { +export const prepareInfinifiSiUSDUnstakeAndRedeemCalldata = (receiver: Address, siUSDAmountIn: bigint, usdcAmountOut: bigint): Call[] => { const siUSDApproveCalldata = encodeFunctionData({ abi: erc20Abi, functionName: 'approve', @@ -74,15 +78,18 @@ export const prepareInfinifiSiUSDUnstakeAndRedeemCalldata = (receiver: Address, args: [CONTRACT_ADDRESSES[CHAIN_ID].MULTICALL_EXECUTOR, siUSDAmountIn], }); + // iUSD and USDC are 1:1, but iUSD has 18 decimals and USDC has 6 decimals + const iUSDAmountIn = usdcAmountOut * 10n ** 12n; + const iUSDApproveCalldata = encodeFunctionData({ abi: erc20Abi, functionName: 'approve', - args: [CONTRACT_ADDRESSES[CHAIN_ID].INFINIFI_GATEWAY as Address, iUsdAmountIn], + args: [CONTRACT_ADDRESSES[CHAIN_ID].INFINIFI_GATEWAY as Address, iUSDAmountIn], }); const redeemCalldata = encodeFunctionData({ abi: infinifiGatewayAbi, functionName: 'redeem', - args: [receiver, iUsdAmountIn, iUsdAmountIn] + args: [receiver, iUSDAmountIn, usdcAmountOut] }); return [ From 4cf9f3e37a83e3400a496284388e7bd8960912a6 Mon Sep 17 00:00:00 2001 From: chad-js Date: Wed, 10 Dec 2025 12:13:57 -0500 Subject: [PATCH 3/6] fixes --- src/services/routing/getSwapParams.ts | 6 +++--- src/services/routing/infinifi.ts | 12 +++++------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/services/routing/getSwapParams.ts b/src/services/routing/getSwapParams.ts index cd59cec..31494ef 100644 --- a/src/services/routing/getSwapParams.ts +++ b/src/services/routing/getSwapParams.ts @@ -61,15 +61,15 @@ export const getRebalanceSwapParams = async ( }; } } else if (stakeType === StakeType.INFINIFI_SIUSD_UNSTAKE_AND_REDEEM) { - const usdcAmountOut = await getInfinifiSiUSDUnstakeAndRedeemQuote(takeAmount); + const [usdcAmountOut, iUSDAmountOut] = await getInfinifiSiUSDUnstakeAndRedeemQuote(takeAmount); - logger.debug({ usdcAmountOut, requiredAmountIn, takeAmount }, 'Infinifi siUSD unstake and redeem quote'); + logger.debug({ usdcAmountOut, iUSDAmountOut, requiredAmountIn, takeAmount }, 'Infinifi siUSD unstake and redeem quote'); if (usdcAmountOut >= requiredAmountIn) { return { isProfitable: true, amountOut: usdcAmountOut, - swapCalls: prepareInfinifiSiUSDUnstakeAndRedeemCalldata(receiver, takeAmount, usdcAmountOut), + swapCalls: prepareInfinifiSiUSDUnstakeAndRedeemCalldata(receiver, takeAmount, iUSDAmountOut), }; } } diff --git a/src/services/routing/infinifi.ts b/src/services/routing/infinifi.ts index 55cc63d..bde1227 100644 --- a/src/services/routing/infinifi.ts +++ b/src/services/routing/infinifi.ts @@ -37,7 +37,8 @@ export const getInfinifiSiUSDUnstakeAndRedeemQuote = async (siUSDAmountIn: bigin }); // iUSD and USDC are 1:1, but iUSD has 18 decimals and USDC has 6 decimals - return iUSDAmountOut / 10n ** 12n; + const usdcAmountOut = iUSDAmountOut / 10n ** 12n; + return [usdcAmountOut, iUSDAmountOut]; }; export const prepareInfinifiSiUSDMintAndStakeCalldata = (receiver: Address, usdcAmountIn: bigint): Call[] => { @@ -66,7 +67,7 @@ export const prepareInfinifiSiUSDMintAndStakeCalldata = (receiver: Address, usdc ]; }; -export const prepareInfinifiSiUSDUnstakeAndRedeemCalldata = (receiver: Address, siUSDAmountIn: bigint, usdcAmountOut: bigint): Call[] => { +export const prepareInfinifiSiUSDUnstakeAndRedeemCalldata = (receiver: Address, siUSDAmountIn: bigint, iUSDAmountOut: bigint): Call[] => { const siUSDApproveCalldata = encodeFunctionData({ abi: erc20Abi, functionName: 'approve', @@ -78,18 +79,15 @@ export const prepareInfinifiSiUSDUnstakeAndRedeemCalldata = (receiver: Address, args: [CONTRACT_ADDRESSES[CHAIN_ID].MULTICALL_EXECUTOR, siUSDAmountIn], }); - // iUSD and USDC are 1:1, but iUSD has 18 decimals and USDC has 6 decimals - const iUSDAmountIn = usdcAmountOut * 10n ** 12n; - const iUSDApproveCalldata = encodeFunctionData({ abi: erc20Abi, functionName: 'approve', - args: [CONTRACT_ADDRESSES[CHAIN_ID].INFINIFI_GATEWAY as Address, iUSDAmountIn], + args: [CONTRACT_ADDRESSES[CHAIN_ID].INFINIFI_GATEWAY as Address, iUSDAmountOut], }); const redeemCalldata = encodeFunctionData({ abi: infinifiGatewayAbi, functionName: 'redeem', - args: [receiver, iUSDAmountIn, usdcAmountOut] + args: [receiver, iUSDAmountOut, 0n] }); return [ From dd583c5a97423b5c737179f37e965a76f4fdd0be Mon Sep 17 00:00:00 2001 From: chad-js Date: Tue, 16 Dec 2025 11:29:37 -0500 Subject: [PATCH 4/6] fix if chain --- src/subscribers/auctionCreated.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/subscribers/auctionCreated.ts b/src/subscribers/auctionCreated.ts index 9270044..0ad3e0d 100644 --- a/src/subscribers/auctionCreated.ts +++ b/src/subscribers/auctionCreated.ts @@ -121,13 +121,14 @@ const getStakeType = (collateralAsset: Address, debtAsset: Address, isOverCollat ) { return StakeType.ETHERFI_ETH_WEETH; } else if ( + CHAIN_ID === 1 && isAddressEqual(collateralAsset, CONTRACT_ADDRESSES[CHAIN_ID].WSTETH as Address) && isAddressEqual(debtAsset, CONTRACT_ADDRESSES[CHAIN_ID].WETH as Address) && - isOverCollateralized && - CHAIN_ID === 1 + isOverCollateralized ) { return StakeType.LIDO_ETH_WSTETH; } else if ( + CHAIN_ID === 1 && isAddressEqual(collateralAsset, CONTRACT_ADDRESSES[CHAIN_ID].SIUSD as Address) && isAddressEqual(debtAsset, CONTRACT_ADDRESSES[CHAIN_ID].USDC as Address) ) { From 2215aefea2897a38e88dc90260f458ad5576cd51 Mon Sep 17 00:00:00 2001 From: chad-js Date: Tue, 16 Dec 2025 12:21:45 -0500 Subject: [PATCH 5/6] use unstakeAndRedeemHelper --- abis/InfinifiUnstakeAndRedeemHelper.ts | 58 +++++ abis/iUSDMintController.ts | 290 +++++++++++++++++++++++++ src/constants/contracts.ts | 3 +- src/services/routing/getSwapParams.ts | 6 +- src/services/routing/infinifi.ts | 57 ++--- src/types/index.ts | 3 +- 6 files changed, 375 insertions(+), 42 deletions(-) create mode 100644 abis/InfinifiUnstakeAndRedeemHelper.ts create mode 100644 abis/iUSDMintController.ts diff --git a/abis/InfinifiUnstakeAndRedeemHelper.ts b/abis/InfinifiUnstakeAndRedeemHelper.ts new file mode 100644 index 0000000..4598dcc --- /dev/null +++ b/abis/InfinifiUnstakeAndRedeemHelper.ts @@ -0,0 +1,58 @@ +const infinifiUnstakeAndRedeemHelperAbi = [ + { + inputs: [{ internalType: "address", name: "_gateway", type: "address" }], + stateMutability: "nonpayable", + type: "constructor", + }, + { inputs: [], name: "NoReceiptToken", type: "error" }, + { inputs: [], name: "NoStakedToken", type: "error" }, + { + inputs: [{ internalType: "address", name: "token", type: "address" }], + name: "SafeERC20FailedOperation", + type: "error", + }, + { + inputs: [], + name: "gateway", + outputs: [{ internalType: "contract IInfiniFiGateway", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "iUSD", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "siUSD", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_siUSDAmount", type: "uint256" }], + name: "siUSD2USDC", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_siUSDAmount", type: "uint256" }], + name: "siUSD2iUSD", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_siUSDAmount", type: "uint256" }], + name: "unstakeAndRedeem", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, +] as const; + +export default infinifiUnstakeAndRedeemHelperAbi; \ No newline at end of file diff --git a/abis/iUSDMintController.ts b/abis/iUSDMintController.ts new file mode 100644 index 0000000..916bfb3 --- /dev/null +++ b/abis/iUSDMintController.ts @@ -0,0 +1,290 @@ +const iUSDMintControllerAbi = [ + { + inputs: [ + { internalType: "address", name: "_core", type: "address" }, + { internalType: "address", name: "_assetToken", type: "address" }, + { internalType: "address", name: "_receiptToken", type: "address" }, + { internalType: "address", name: "_accounting", type: "address" }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { + inputs: [ + { internalType: "uint256", name: "_amountIn", type: "uint256" }, + { internalType: "uint256", name: "_minAssetAmount", type: "uint256" }, + ], + name: "AssetAmountTooLow", + type: "error", + }, + { + inputs: [ + { internalType: "uint256", name: "newAmount", type: "uint256" }, + { internalType: "uint256", name: "cap", type: "uint256" }, + ], + name: "CapExceeded", + type: "error", + }, + { inputs: [], name: "EnforcedPause", type: "error" }, + { inputs: [], name: "ExpectedPause", type: "error" }, + { + inputs: [{ internalType: "address", name: "token", type: "address" }], + name: "SafeERC20FailedOperation", + type: "error", + }, + { + inputs: [ + { internalType: "uint256", name: "minAssetsOut", type: "uint256" }, + { internalType: "uint256", name: "assetsReceived", type: "uint256" }, + ], + name: "SlippageTooHigh", + type: "error", + }, + { + inputs: [{ internalType: "bytes", name: "returnData", type: "bytes" }], + name: "UnderlyingCallReverted", + type: "error", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "uint256", name: "timestamp", type: "uint256" }, + { indexed: false, internalType: "address", name: "hook", type: "address" }, + ], + name: "AfterMintHookChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: false, internalType: "uint256", name: "timestamp", type: "uint256" }, + { indexed: false, internalType: "uint256", name: "assetsBefore", type: "uint256" }, + { indexed: false, internalType: "uint256", name: "assetsAfter", type: "uint256" }, + ], + name: "AssetsUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [{ indexed: false, internalType: "uint256", name: "newCap", type: "uint256" }], + name: "CapUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "address", name: "oldCore", type: "address" }, + { indexed: true, internalType: "address", name: "newCore", type: "address" }, + ], + name: "CoreUpdate", + type: "event", + }, + { + anonymous: false, + inputs: [{ indexed: false, internalType: "uint256", name: "newMaxSlippage", type: "uint256" }], + name: "MaxSlippageUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "uint256", name: "timestamp", type: "uint256" }, + { indexed: false, internalType: "uint256", name: "amount", type: "uint256" }, + ], + name: "MinAssetAmountUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "uint256", name: "timestamp", type: "uint256" }, + { indexed: true, internalType: "address", name: "to", type: "address" }, + { indexed: false, internalType: "address", name: "asset", type: "address" }, + { indexed: false, internalType: "uint256", name: "amountIn", type: "uint256" }, + { indexed: false, internalType: "uint256", name: "amountOut", type: "uint256" }, + ], + name: "Mint", + type: "event", + }, + { + anonymous: false, + inputs: [{ indexed: false, internalType: "address", name: "account", type: "address" }], + name: "Paused", + type: "event", + }, + { + anonymous: false, + inputs: [{ indexed: false, internalType: "address", name: "account", type: "address" }], + name: "Unpaused", + type: "event", + }, + { + inputs: [], + name: "accounting", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "afterMintHook", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_assetAmount", type: "uint256" }], + name: "assetToReceipt", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "assetToken", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "assets", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "cap", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "core", + outputs: [{ internalType: "contract InfiniFiCore", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { inputs: [], name: "deposit", outputs: [], stateMutability: "nonpayable", type: "function" }, + { + inputs: [ + { + components: [ + { internalType: "address", name: "target", type: "address" }, + { internalType: "uint256", name: "value", type: "uint256" }, + { internalType: "bytes", name: "callData", type: "bytes" }, + ], + internalType: "struct CoreControlled.Call[]", + name: "calls", + type: "tuple[]", + }, + ], + name: "emergencyAction", + outputs: [{ internalType: "bytes[]", name: "returnData", type: "bytes[]" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "liquidity", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "maxDeposit", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "maxSlippage", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "minAssetAmount", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_to", type: "address" }, + { internalType: "uint256", name: "_assetAmountIn", type: "uint256" }, + ], + name: "mint", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { inputs: [], name: "pause", outputs: [], stateMutability: "nonpayable", type: "function" }, + { + inputs: [], + name: "paused", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "receiptToken", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "_afterMintHook", type: "address" }], + name: "setAfterMintHook", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_newCap", type: "uint256" }], + name: "setCap", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "newCore", type: "address" }], + name: "setCore", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_maxSlippage", type: "uint256" }], + name: "setMaxSlippage", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_minAssetAmount", type: "uint256" }], + name: "setMinAssetAmount", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { inputs: [], name: "unpause", outputs: [], stateMutability: "nonpayable", type: "function" }, + { + inputs: [ + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "address", name: "to", type: "address" }, + ], + name: "withdraw", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +] as const; + +export default iUSDMintControllerAbi; \ No newline at end of file diff --git a/src/constants/contracts.ts b/src/constants/contracts.ts index 8e1cf90..a4d3fed 100644 --- a/src/constants/contracts.ts +++ b/src/constants/contracts.ts @@ -14,7 +14,8 @@ export const CONTRACT_ADDRESSES: Record = { ETHERFI_LIQUIDITY_POOL: "0x308861A430be4cce5502d0A12724771Fc6DaF216", FLUID_DEX_RESERVES_RESOLVER: "0xC93876C0EEd99645DD53937b25433e311881A27C", INFINIFI_GATEWAY: "0x3f04b65Ddbd87f9CE0A2e7Eb24d80e7fb87625b5", - IUSD: "0x48f9e38f3070AD8945DFEae3FA70987722E3D89c", + INFINIFI_UNSTAKE_AND_REDEEM_HELPER: "0x4f0122D43aB4893d5977FB0358B73CC178339dFE", + IUSD_MINT_CONTROLLER: "0x49877d937B9a00d50557bdC3D87287b5c3a4C256", LEVERAGE_MANAGER: "0x5C37EB148D4a261ACD101e2B997A0F163Fb3E351", MULTICALL_EXECUTOR: "0x16D02Ebd89988cAd1Ce945807b963aB7A9Fd22E1", PENDLE_ROUTER: "0x888888888889758F76e7103c6CbF23ABbF58F946", diff --git a/src/services/routing/getSwapParams.ts b/src/services/routing/getSwapParams.ts index 31494ef..8249b22 100644 --- a/src/services/routing/getSwapParams.ts +++ b/src/services/routing/getSwapParams.ts @@ -61,15 +61,15 @@ export const getRebalanceSwapParams = async ( }; } } else if (stakeType === StakeType.INFINIFI_SIUSD_UNSTAKE_AND_REDEEM) { - const [usdcAmountOut, iUSDAmountOut] = await getInfinifiSiUSDUnstakeAndRedeemQuote(takeAmount); + const usdcAmountOut = await getInfinifiSiUSDUnstakeAndRedeemQuote(takeAmount); - logger.debug({ usdcAmountOut, iUSDAmountOut, requiredAmountIn, takeAmount }, 'Infinifi siUSD unstake and redeem quote'); + logger.debug({ usdcAmountOut, requiredAmountIn, takeAmount }, 'Infinifi siUSD unstake and redeem quote'); if (usdcAmountOut >= requiredAmountIn) { return { isProfitable: true, amountOut: usdcAmountOut, - swapCalls: prepareInfinifiSiUSDUnstakeAndRedeemCalldata(receiver, takeAmount, iUSDAmountOut), + swapCalls: prepareInfinifiSiUSDUnstakeAndRedeemCalldata(takeAmount), }; } } diff --git a/src/services/routing/infinifi.ts b/src/services/routing/infinifi.ts index bde1227..7ad6d05 100644 --- a/src/services/routing/infinifi.ts +++ b/src/services/routing/infinifi.ts @@ -5,20 +5,26 @@ import siUSDAbi from "../../../abis/siUSD"; import { publicClient } from "../../utils/transactionHelpers"; import infinifiGatewayAbi from "../../../abis/InfinifiGateway"; import { Call } from "../../types"; +import infinifiUnstakeAndRedeemHelperAbi from "../../../abis/InfinifiUnstakeAndRedeemHelper"; +import iUSDMintControllerAbi from "abis/iUSDMintController"; export const getInfinifiSiUSDMintAndStakeQuote = async (usdcAmountIn: bigint) => { if (CHAIN_ID !== 1) { throw new Error('Native iUSD staking is only supported by the rebalance bot on Ethereum mainnet'); } - // USDC and iUSD are 1:1, but iUSD has 18 decimals and USDC has 6 decimals - const iUSDAmountIn = usdcAmountIn * 10n ** 12n; + const iUSDAmount = await publicClient.readContract({ + address: CONTRACT_ADDRESSES[CHAIN_ID].IUSD_MINT_CONTROLLER as Address, + abi: iUSDMintControllerAbi, + functionName: 'assetToReceipt', + args: [usdcAmountIn], + }); const siUSDAmountOut = await publicClient.readContract({ address: CONTRACT_ADDRESSES[CHAIN_ID].SIUSD as Address, abi: siUSDAbi, functionName: 'previewDeposit', - args: [iUSDAmountIn], + args: [iUSDAmount], }); return siUSDAmountOut; @@ -29,16 +35,14 @@ export const getInfinifiSiUSDUnstakeAndRedeemQuote = async (siUSDAmountIn: bigin throw new Error('Native siUSD unstaking is only supported by the rebalance bot on Ethereum mainnet'); } - const iUSDAmountOut = await publicClient.readContract({ - address: CONTRACT_ADDRESSES[CHAIN_ID].SIUSD as Address, - abi: siUSDAbi, - functionName: 'previewRedeem', + const usdcAmountOut = await publicClient.readContract({ + address: CONTRACT_ADDRESSES[CHAIN_ID].INFINIFI_UNSTAKE_AND_REDEEM_HELPER as Address, + abi: infinifiUnstakeAndRedeemHelperAbi, + functionName: 'siUSD2USDC', args: [siUSDAmountIn], }); - // iUSD and USDC are 1:1, but iUSD has 18 decimals and USDC has 6 decimals - const usdcAmountOut = iUSDAmountOut / 10n ** 12n; - return [usdcAmountOut, iUSDAmountOut]; + return usdcAmountOut; }; export const prepareInfinifiSiUSDMintAndStakeCalldata = (receiver: Address, usdcAmountIn: bigint): Call[] => { @@ -67,27 +71,16 @@ export const prepareInfinifiSiUSDMintAndStakeCalldata = (receiver: Address, usdc ]; }; -export const prepareInfinifiSiUSDUnstakeAndRedeemCalldata = (receiver: Address, siUSDAmountIn: bigint, iUSDAmountOut: bigint): Call[] => { +export const prepareInfinifiSiUSDUnstakeAndRedeemCalldata = (siUSDAmountIn: bigint): Call[] => { const siUSDApproveCalldata = encodeFunctionData({ abi: erc20Abi, functionName: 'approve', - args: [CONTRACT_ADDRESSES[CHAIN_ID].INFINIFI_GATEWAY as Address, siUSDAmountIn], + args: [CONTRACT_ADDRESSES[CHAIN_ID].INFINIFI_UNSTAKE_AND_REDEEM_HELPER as Address, siUSDAmountIn], }); const unstakeCalldata = encodeFunctionData({ - abi: infinifiGatewayAbi, - functionName: 'unstake', - args: [CONTRACT_ADDRESSES[CHAIN_ID].MULTICALL_EXECUTOR, siUSDAmountIn], - }); - - const iUSDApproveCalldata = encodeFunctionData({ - abi: erc20Abi, - functionName: 'approve', - args: [CONTRACT_ADDRESSES[CHAIN_ID].INFINIFI_GATEWAY as Address, iUSDAmountOut], - }); - const redeemCalldata = encodeFunctionData({ - abi: infinifiGatewayAbi, - functionName: 'redeem', - args: [receiver, iUSDAmountOut, 0n] + abi: infinifiUnstakeAndRedeemHelperAbi, + functionName: 'unstakeAndRedeem', + args: [siUSDAmountIn], }); return [ @@ -97,19 +90,9 @@ export const prepareInfinifiSiUSDUnstakeAndRedeemCalldata = (receiver: Address, value: 0n, }, { - target: CONTRACT_ADDRESSES[CHAIN_ID].INFINIFI_GATEWAY as Address, + target: CONTRACT_ADDRESSES[CHAIN_ID].INFINIFI_UNSTAKE_AND_REDEEM_HELPER as Address, data: unstakeCalldata, value: 0n, - }, - { - target: CONTRACT_ADDRESSES[CHAIN_ID].IUSD as Address, - data: iUSDApproveCalldata, - value: 0n, - }, - { - target: CONTRACT_ADDRESSES[CHAIN_ID].INFINIFI_GATEWAY as Address, - data: redeemCalldata, - value: 0n, } ] }; \ No newline at end of file diff --git a/src/types/index.ts b/src/types/index.ts index 62dcee2..3be2d72 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -41,7 +41,8 @@ export interface ContractAddresses { ETHERFI_LIQUIDITY_POOL?: Address; FLUID_DEX_RESERVES_RESOLVER?: Address; INFINIFI_GATEWAY?: Address; - IUSD?: Address; + INFINIFI_UNSTAKE_AND_REDEEM_HELPER?: Address; + IUSD_MINT_CONTROLLER?: Address; LEVERAGE_MANAGER: Address; MULTICALL_EXECUTOR: Address; PENDLE_ROUTER?: Address; From a97feb0dc5d3338bb9eea14194d96a77029e3247 Mon Sep 17 00:00:00 2001 From: chad-js Date: Wed, 17 Dec 2025 11:00:24 -0500 Subject: [PATCH 6/6] include yield in preview for siUSD mintAndStake using gateway --- abis/InfinifiYieldSharing.ts | 349 +++++++++++++++++++++++++++++++ src/constants/contracts.ts | 1 + src/services/routing/infinifi.ts | 46 ++-- src/types/index.ts | 1 + 4 files changed, 385 insertions(+), 12 deletions(-) create mode 100644 abis/InfinifiYieldSharing.ts diff --git a/abis/InfinifiYieldSharing.ts b/abis/InfinifiYieldSharing.ts new file mode 100644 index 0000000..8cbee0e --- /dev/null +++ b/abis/InfinifiYieldSharing.ts @@ -0,0 +1,349 @@ +const infinifiYieldSharingAbi = [ + { + inputs: [ + { internalType: "address", name: "_core", type: "address" }, + { internalType: "address", name: "_accounting", type: "address" }, + { internalType: "address", name: "_receiptToken", type: "address" }, + { internalType: "address", name: "_stakedToken", type: "address" }, + { internalType: "address", name: "_lockingModule", type: "address" }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { inputs: [], name: "EnforcedPause", type: "error" }, + { inputs: [], name: "ExpectedPause", type: "error" }, + { + inputs: [{ internalType: "address", name: "_recipient", type: "address" }], + name: "PerformanceFeeRecipientIsZeroAddress", + type: "error", + }, + { + inputs: [{ internalType: "uint256", name: "_percent", type: "uint256" }], + name: "PerformanceFeeTooHigh", + type: "error", + }, + { inputs: [], name: "StakedTokenNotAvailable", type: "error" }, + { + inputs: [{ internalType: "uint256", name: "_ratio", type: "uint256" }], + name: "TargetIlliquidRatioTooHigh", + type: "error", + }, + { + inputs: [{ internalType: "bytes", name: "returnData", type: "bytes" }], + name: "UnderlyingCallReverted", + type: "error", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "address", name: "oldCore", type: "address" }, + { indexed: true, internalType: "address", name: "newCore", type: "address" }, + ], + name: "CoreUpdate", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "uint256", name: "timestamp", type: "uint256" }, + { indexed: false, internalType: "uint256", name: "duration", type: "uint256" }, + ], + name: "InterpolationDurationUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "uint256", name: "timestamp", type: "uint256" }, + { indexed: false, internalType: "uint256", name: "multiplier", type: "uint256" }, + ], + name: "LiquidMultiplierUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [{ indexed: false, internalType: "address", name: "account", type: "address" }], + name: "Paused", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "uint256", name: "timestamp", type: "uint256" }, + { indexed: false, internalType: "uint256", name: "percentage", type: "uint256" }, + { indexed: false, internalType: "address", name: "recipient", type: "address" }, + ], + name: "PerformanceFeeSettingsUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "uint256", name: "timestamp", type: "uint256" }, + { indexed: false, internalType: "uint256", name: "value", type: "uint256" }, + ], + name: "SafetyBufferSizeUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "uint256", name: "timestamp", type: "uint256" }, + { indexed: false, internalType: "uint256", name: "multiplier", type: "uint256" }, + ], + name: "TargetIlliquidRatioUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [{ indexed: false, internalType: "address", name: "account", type: "address" }], + name: "Unpaused", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "uint256", name: "timestamp", type: "uint256" }, + { indexed: false, internalType: "int256", name: "yield", type: "int256" }, + ], + name: "YieldAccrued", + type: "event", + }, + { + inputs: [], + name: "MAX_PERFORMANCE_FEE", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "accounting", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { inputs: [], name: "accrue", outputs: [], stateMutability: "nonpayable", type: "function" }, + { + inputs: [], + name: "canEnterOrExitStakedToken", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "core", + outputs: [{ internalType: "contract InfiniFiCore", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { inputs: [], name: "distributeInterpolationRewards", outputs: [], stateMutability: "nonpayable", type: "function" }, + { + inputs: [ + { + components: [ + { internalType: "address", name: "target", type: "address" }, + { internalType: "uint256", name: "value", type: "uint256" }, + { internalType: "bytes", name: "callData", type: "bytes" }, + ], + internalType: "struct CoreControlled.Call[]", + name: "calls", + type: "tuple[]", + }, + ], + name: "emergencyAction", + outputs: [{ internalType: "bytes[]", name: "returnData", type: "bytes[]" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "escrow", + outputs: [{ internalType: "contract YieldVestingEscrow", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "getCachedStakedReceiptTokens", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "interpolationDuration", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "liquidReturnMultiplier", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "lockingModule", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "_source", type: "address" }], + name: "migrateSafetyBuffer", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { inputs: [], name: "pause", outputs: [], stateMutability: "nonpayable", type: "function" }, + { + inputs: [], + name: "paused", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "performanceFee", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "performanceFeeRecipient", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "point", + outputs: [ + { internalType: "uint32", name: "lastAccrued", type: "uint32" }, + { internalType: "uint32", name: "lastClaimed", type: "uint32" }, + { internalType: "uint208", name: "rate", type: "uint208" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "receiptToken", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "safetyBufferSize", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bool", name: "_value", type: "bool" }], + name: "setCanEnterOrExitStakedToken", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "newCore", type: "address" }], + name: "setCore", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_duration", type: "uint256" }], + name: "setInterpolationDuration", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_multiplier", type: "uint256" }], + name: "setLiquidReturnMultiplier", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "_percent", type: "uint256" }, + { internalType: "address", name: "_recipient", type: "address" }, + ], + name: "setPerformanceFeeAndRecipient", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_safetyBufferSize", type: "uint256" }], + name: "setSafetyBufferSize", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_ratio", type: "uint256" }], + name: "setTargetIlliquidRatio", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "stakedReceiptTokenCache", + outputs: [ + { internalType: "uint48", name: "blockTimestamp", type: "uint48" }, + { internalType: "uint208", name: "amount", type: "uint208" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "stakedToken", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "targetIlliquidRatio", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "unaccruedYield", + outputs: [{ internalType: "int256", name: "", type: "int256" }], + stateMutability: "view", + type: "function", + }, + { inputs: [], name: "unpause", outputs: [], stateMutability: "nonpayable", type: "function" }, + { + inputs: [], + name: "vested", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "vesting", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, +] as const; + +export default infinifiYieldSharingAbi; diff --git a/src/constants/contracts.ts b/src/constants/contracts.ts index a4d3fed..ea57b20 100644 --- a/src/constants/contracts.ts +++ b/src/constants/contracts.ts @@ -15,6 +15,7 @@ export const CONTRACT_ADDRESSES: Record = { FLUID_DEX_RESERVES_RESOLVER: "0xC93876C0EEd99645DD53937b25433e311881A27C", INFINIFI_GATEWAY: "0x3f04b65Ddbd87f9CE0A2e7Eb24d80e7fb87625b5", INFINIFI_UNSTAKE_AND_REDEEM_HELPER: "0x4f0122D43aB4893d5977FB0358B73CC178339dFE", + INFINIFI_YIELD_SHARING: "0x1cb9ED33924741F500E739e38c3215a76cD1f579", IUSD_MINT_CONTROLLER: "0x49877d937B9a00d50557bdC3D87287b5c3a4C256", LEVERAGE_MANAGER: "0x5C37EB148D4a261ACD101e2B997A0F163Fb3E351", MULTICALL_EXECUTOR: "0x16D02Ebd89988cAd1Ce945807b963aB7A9Fd22E1", diff --git a/src/services/routing/infinifi.ts b/src/services/routing/infinifi.ts index 7ad6d05..cef0fa1 100644 --- a/src/services/routing/infinifi.ts +++ b/src/services/routing/infinifi.ts @@ -6,26 +6,48 @@ import { publicClient } from "../../utils/transactionHelpers"; import infinifiGatewayAbi from "../../../abis/InfinifiGateway"; import { Call } from "../../types"; import infinifiUnstakeAndRedeemHelperAbi from "../../../abis/InfinifiUnstakeAndRedeemHelper"; -import iUSDMintControllerAbi from "abis/iUSDMintController"; +import iUSDMintControllerAbi from "../../../abis/iUSDMintController"; +import infinifiYieldSharingAbi from "../../../abis/InfinifiYieldSharing"; export const getInfinifiSiUSDMintAndStakeQuote = async (usdcAmountIn: bigint) => { if (CHAIN_ID !== 1) { throw new Error('Native iUSD staking is only supported by the rebalance bot on Ethereum mainnet'); } - const iUSDAmount = await publicClient.readContract({ - address: CONTRACT_ADDRESSES[CHAIN_ID].IUSD_MINT_CONTROLLER as Address, - abi: iUSDMintControllerAbi, - functionName: 'assetToReceipt', - args: [usdcAmountIn], + const results = await publicClient.multicall({ + contracts: [ + { + address: CONTRACT_ADDRESSES[CHAIN_ID].IUSD_MINT_CONTROLLER as Address, + abi: iUSDMintControllerAbi, + functionName: 'assetToReceipt', + args: [usdcAmountIn], + }, + { + address: CONTRACT_ADDRESSES[CHAIN_ID].INFINIFI_YIELD_SHARING as Address, + abi: infinifiYieldSharingAbi, + functionName: 'vested', + }, + { + address: CONTRACT_ADDRESSES[CHAIN_ID].SIUSD as Address, + abi: siUSDAbi, + functionName: 'totalSupply', + }, + { + address: CONTRACT_ADDRESSES[CHAIN_ID].SIUSD as Address, + abi: siUSDAbi, + functionName: 'totalAssets', + }, + ], }); - const siUSDAmountOut = await publicClient.readContract({ - address: CONTRACT_ADDRESSES[CHAIN_ID].SIUSD as Address, - abi: siUSDAbi, - functionName: 'previewDeposit', - args: [iUSDAmount], - }); + const iUSDAmount = results[0].result as bigint; + const vestedYieldAssets = results[1].result as bigint; + const siUSDTotalSupply = results[2].result as bigint; + const siUSDTotalAssets = results[3].result as bigint; + + const siUSDTotalAssetsWithYield = siUSDTotalAssets + vestedYieldAssets; + + const siUSDAmountOut = siUSDTotalSupply === 0n ? iUSDAmount : ((iUSDAmount * siUSDTotalSupply) / siUSDTotalAssetsWithYield); return siUSDAmountOut; }; diff --git a/src/types/index.ts b/src/types/index.ts index 3be2d72..d3ee2b6 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -42,6 +42,7 @@ export interface ContractAddresses { FLUID_DEX_RESERVES_RESOLVER?: Address; INFINIFI_GATEWAY?: Address; INFINIFI_UNSTAKE_AND_REDEEM_HELPER?: Address; + INFINIFI_YIELD_SHARING?: Address; IUSD_MINT_CONTROLLER?: Address; LEVERAGE_MANAGER: Address; MULTICALL_EXECUTOR: Address;