Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adapter swell: predeposit l2 pendle pt #1410

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 65 additions & 2 deletions src/adapters/swell/ethereum/balance.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { getPendleBalances } from '@adapters/pendle/common/balance'
import { getPendlePools } from '@adapters/pendle/common/pool'
import type { Balance, BalancesContext, Contract } from '@lib/adapter'
import type { Category } from '@lib/category'
import { abi as erc20Abi } from '@lib/erc20'
Expand Down Expand Up @@ -41,14 +43,75 @@ export async function getSwellBalances(ctx: BalancesContext, contracts: Contract
.map((balanceOf, index) => {
const rate = rates[index]
if (!rate.success || !balanceOf.success) return null

const amount = (balanceOf.output * rate.output) / parseEther('1.0')
return {
...contracts[index],
amount: balanceOf.output,
underlyings: [{ ...WETH, amount: (balanceOf.output * rate.output) / parseEther('1.0') }],
underlyings: [{ ...WETH, amount }],
rewards: undefined,
balanceUSD: amount * BigInt(3500),
category: 'farm' as Category,
}
})
.filter(isNotNullish)
}

const SimpleStakingERC20 = {
stakedBalances: {
inputs: [
{ internalType: 'address', name: '', type: 'address' },
{ internalType: 'contract IERC20', name: '', type: 'address' },
],
name: 'stakedBalances',
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
stateMutability: 'view',
type: 'function',
} as const,
}

// This is the contract where users predeposit their tokens for Swell L2
const depositContract = '0x38d43a6cb8da0e855a42fb6b0733a0498531d774'

export async function getSwellL2Balances(ctx: BalancesContext, contracts: Contract[]): Promise<Balance[]> {
return getSwellL2BalancesPendle(ctx, contracts)
}

/**
* This function is used to get the balances of the user in the Pendle pools in the Swell L2 contract
* We first get the staked balances of the user in the deposit contract.
* Then we get the list of pt from pendle pools that the user could stake in.
* Finally we set the balances of the user in the pendle pools and return them.
* @param ctx
* @param contracts
* @returns
*/

async function getSwellL2BalancesPendle(ctx: BalancesContext, contracts: Contract[]): Promise<Balance[]> {
const userStakedBalances = (
await Promise.all([
multicall({
ctx,
calls: contracts.map((contract) => ({
target: depositContract,
params: [ctx.address, contract.address] as any,
})),
abi: SimpleStakingERC20.stakedBalances,
}),
])
)[0].filter((balance) => balance.success && balance.output > BigInt(0))
const tokenWithBalance = userStakedBalances.map((balance) => {
return balance.input.params[1]
})
const pendlePools = (await getPendlePools(ctx)).filter((pool) => tokenWithBalance.includes(pool.address))

const depositContractCTX = ctx
depositContractCTX.address = depositContract
const balances = (await getPendleBalances(depositContractCTX, pendlePools))[1]

const updatedBalances = balances.map((balance) => {
const stakedBalance = userStakedBalances.find((stakedBalance) => stakedBalance.input.params[1] === balance.address)
const amount = stakedBalance && stakedBalance.output ? stakedBalance.output : BigInt(0)
return { ...balance, amount }
})
return updatedBalances
}
55 changes: 55 additions & 0 deletions src/adapters/swell/ethereum/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import type { Contract } from '@lib/adapter'
import { type Chain, chainByChainId } from '@lib/chains'

type SwellTags = 'LST' | 'Pendle' | 'AVS' | 'Eigenpie' | 'LRT'

interface SwellApiToken {
chainId: number
address: string
symbol: string
name: string
decimals: number
logoUri: string
tags: SwellTags[]
}

interface TokenListResponse {
tokenList: {
name: string
timestamp: number
tokens: SwellApiToken[]
}
}

/**
* This returns a list of tokens that are accepted by Swell in there predeposit l2 contract.
* There is no way to get this info onchain.
* @returns {Promise<SwellApiToken[]>}
*/

export async function getAcceptedTokens(): Promise<SwellApiToken[]> {
try {
const resp = await fetch(
'https://v3-lst.svc.swellnetwork.io/swell.v3.PreDepositService/TokenList?connect=v1&encoding=json&message=%7B%7D',
)
const data: TokenListResponse = await resp.json()
return data.tokenList.tokens
} catch (e) {
console.error('Failed to fetch token list from Swell:', e)
return []
}
}

/**
* The Swell API format is different from the contract format. We convert the chainId (int) to
* the chainName (string) and the address to lowercase.
* @param tokens
* @returns
*/
export function fromSwellApiTokenToContract(tokens: SwellApiToken[]): Contract[] {
return tokens.map((token) => ({
chain: chainByChainId[token.chainId].id as Chain,
address: token.address.toLowerCase() as `0x${string}`,
decimals: token.decimals,
}))
}
14 changes: 11 additions & 3 deletions src/adapters/swell/ethereum/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { fromSwellApiTokenToContract, getAcceptedTokens } from '@adapters/swell/ethereum/common'
import type { Contract, GetBalancesHandler } from '@lib/adapter'
import { resolveBalances } from '@lib/balance'

import { getSwellBalances } from './balance'
import { getSwellBalances, getSwellL2Balances } from './balance'

const swETH: Contract = {
chain: 'ethereum',
Expand All @@ -17,15 +18,22 @@ const rswETH: Contract = {
symbol: 'rswETH',
}

export const getContracts = () => {
export const getContracts = async () => {
const acceptedTokens = await getAcceptedTokens()

const pendleContracts = fromSwellApiTokenToContract(
acceptedTokens.filter((token: any) => token.tags.includes('Pendle')),
)

return {
contracts: { LSTs: [swETH, rswETH] },
contracts: { LSTs: [swETH, rswETH], SwellL2PreDeposit: pendleContracts },
}
}

export const getBalances: GetBalancesHandler<typeof getContracts> = async (ctx, contracts) => {
const balances = await resolveBalances<typeof getContracts>(ctx, contracts, {
LSTs: getSwellBalances,
SwellL2PreDeposit: getSwellL2Balances,
})

return {
Expand Down