Skip to content

Commit

Permalink
feat:Adapter,Fluid, ethereum (#1366)
Browse files Browse the repository at this point in the history
  • Loading branch information
0xpeluche authored Mar 22, 2024
1 parent c6d6f5c commit 01ba5d8
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,8 @@
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
94 changes: 94 additions & 0 deletions src/adapters/fluid/ethereum/balance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import type { Balance, BalancesContext, Contract } from '@lib/adapter'
import { mapMultiSuccessFilter, mapSuccessFilter } from '@lib/array'
import { abi as erc20Abi } from '@lib/erc20'
import { multicall } from '@lib/multicall'
import { isNotNullish } from '@lib/type'

const abi = {
convertToAssets: {
inputs: [{ internalType: 'uint256', name: 'shares_', type: 'uint256' }],
name: 'convertToAssets',
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
stateMutability: 'view',
type: 'function',
},
earned: {
inputs: [{ internalType: 'address', name: 'account', type: 'address' }],
name: 'earned',
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
stateMutability: 'view',
type: 'function',
},
} as const

export async function getFluidBalances(ctx: BalancesContext, pools: Contract[]): Promise<Balance[]> {
const userShares = await multicall({
ctx,
calls: pools.map((pool) => ({ target: pool.address, params: [ctx.address] }) as const),
abi: erc20Abi.balanceOf,
})

const poolsBalances: Balance[] = mapSuccessFilter(userShares, (res, index) => ({
...pools[index],
amount: res.output,
}))

return getProcessUnderlyings(ctx, poolsBalances)
}

export async function getFluidFarmBalances(ctx: BalancesContext, pools: Contract[]): Promise<Balance[]> {
const [userBalances, userRewards] = await Promise.all([
multicall({
ctx,
calls: pools.map((pool) => ({ target: pool.address, params: [ctx.address] }) as const),
abi: erc20Abi.balanceOf,
}),
multicall({
ctx,
calls: pools.map((pool) => ({ target: pool.address, params: [ctx.address] }) as const),
abi: abi.earned,
}),
])

const poolBalances: Balance[] = mapMultiSuccessFilter(
userBalances.map((_, i) => [userBalances[i], userRewards[i]]),

(res, index) => {
const pool = pools[index]
const underlying = pool.underlyings![0] as Contract
const reward = pool.rewards![0] as Contract

if (!underlying || !reward) return null

const [{ output: amount }, { output: pendingReward }] = res.inputOutputPairs

return {
...pool,
amount,
underlyings: [underlying],
rewards: [{ ...reward, amount: pendingReward }],
category: 'farm',
}
},
).filter(isNotNullish)

return getProcessUnderlyings(ctx, poolBalances)
}

async function getProcessUnderlyings(ctx: BalancesContext, poolBalances: Balance[]): Promise<Balance[]> {
const userAssets = await multicall({
ctx,
calls: poolBalances.map((pool) => ({ target: pool.token ?? pool.address, params: [pool.amount] }) as const),
abi: abi.convertToAssets,
})

return mapSuccessFilter(userAssets, (res, index) => {
return {
...poolBalances[index],
amount: poolBalances[index].amount,
underlyings: [{ ...(poolBalances[index].underlyings![0] as Contract), amount: res.output }],
rewards: undefined,
category: 'farm',
}
})
}
41 changes: 41 additions & 0 deletions src/adapters/fluid/ethereum/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { getFluidBalances, getFluidFarmBalances } from '@adapters/fluid/ethereum/balance'
import { getFluidContracts, getFluidFarmingContracts } from '@adapters/fluid/ethereum/pool'
import type { AdapterConfig, BaseContext, GetBalancesHandler } from '@lib/adapter'
import { resolveBalances } from '@lib/balance'

const poolsAddresses: `0x${string}`[] = [
'0x9fb7b4477576fe5b32be4c1843afb1e55f251b33', // fUSDC
'0x5c20b550819128074fd538edf79791733ccedd18', // fUSDT
'0x90551c1795392094fe6d29b758eccd233cfaa260', // fWETH
]

const farmerAddresses: `0x${string}`[] = [
'0x2fa6c95b69c10f9f52b8990b6c03171f13c46225',
'0x490681095ed277b45377d28ca15ac41d64583048',
]

export const getContracts = async (ctx: BaseContext) => {
const [pools, farmers] = await Promise.all([
getFluidContracts(ctx, poolsAddresses),
getFluidFarmingContracts(ctx, farmerAddresses),
])

return {
contracts: { pools, farmers },
}
}

export const getBalances: GetBalancesHandler<typeof getContracts> = async (ctx, contracts) => {
const balances = await resolveBalances<typeof getContracts>(ctx, contracts, {
pools: getFluidBalances,
farmers: getFluidFarmBalances,
})

return {
groups: [{ balances }],
}
}

export const config: AdapterConfig = {
startDate: 1708819200,
}
76 changes: 76 additions & 0 deletions src/adapters/fluid/ethereum/pool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import type { BaseContext, Contract } from '@lib/adapter'
import { mapMultiSuccessFilter, mapSuccessFilter } from '@lib/array'
import { multicall } from '@lib/multicall'

const abi = {
asset: {
inputs: [],
name: 'asset',
outputs: [{ internalType: 'address', name: '', type: 'address' }],
stateMutability: 'view',
type: 'function',
},
stakingToken: {
inputs: [],
name: 'stakingToken',
outputs: [{ internalType: 'contract IERC20', name: '', type: 'address' }],
stateMutability: 'view',
type: 'function',
},
rewardsToken: {
inputs: [],
name: 'rewardsToken',
outputs: [{ internalType: 'contract IERC20', name: '', type: 'address' }],
stateMutability: 'view',
type: 'function',
},
} as const

export async function getFluidContracts(ctx: BaseContext, poolsAddresses: `0x${string}`[]): Promise<Contract[]> {
const assets = await multicall({
ctx,
calls: poolsAddresses.map((pool) => ({ target: pool }) as const),
abi: abi.asset,
})

return mapSuccessFilter(assets, (res, index) => {
return {
chain: ctx.chain,
address: poolsAddresses[index],
underlyings: [res.output],
}
})
}

export async function getFluidFarmingContracts(
ctx: BaseContext,
farmersAddresses: `0x${string}`[],
): Promise<Contract[]> {
const [stakingTokens, rewardTokens] = await Promise.all([
multicall({ ctx, calls: farmersAddresses.map((farmer) => ({ target: farmer }) as const), abi: abi.stakingToken }),
multicall({ ctx, calls: farmersAddresses.map((farmer) => ({ target: farmer }) as const), abi: abi.rewardsToken }),
])

const assets = await multicall({
ctx,
calls: mapSuccessFilter(stakingTokens, (res) => ({ target: res.output }) as const),
abi: abi.asset,
})

return mapMultiSuccessFilter(
stakingTokens.map((_, i) => [stakingTokens[i], rewardTokens[i], assets[i]]),

(res, index) => {
const address = farmersAddresses[index]
const [{ output: token }, { output: reward }, { output: underlying }] = res.inputOutputPairs

return {
chain: ctx.chain,
address,
token,
underlyings: [underlying],
rewards: [reward],
}
},
)
}
10 changes: 10 additions & 0 deletions src/adapters/fluid/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { Adapter } from '@lib/adapter'

import * as ethereum from './ethereum'

const adapter: Adapter = {
id: 'fluid',
ethereum: ethereum,
}

export default adapter

0 comments on commit 01ba5d8

Please sign in to comment.