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

Fix old compound v3 on several chains + add base #771

Merged
merged 3 commits into from
Aug 21, 2023
Merged
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
27 changes: 11 additions & 16 deletions src/adapters/compound-v3/arbitrum/index.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,47 @@
import { getAssetsContracts } from '@adapters/compound-v3/common/asset'
import { getCompLendBalances, getCompRewardBalances } from '@adapters/compound-v3/common/balance'
import type { BalancesContext, BaseContext, Contract, GetBalancesHandler } from '@lib/adapter'
import { resolveBalances } from '@lib/balance'
import type { BalanceWithExtraProps } from '@lib/compound/v2/lending'
import { getHealthFactor } from '@lib/compound/v2/lending'
import { getSingleStakeBalances } from '@lib/stake'
import type { Token } from '@lib/token'

import { getAssetsContracts, getLendBorrowBalances } from '../common/lend'
import { getRewardBalances } from '../common/rewards'
import { getStakeBalances } from '../common/stake'

const USDC: Token = {
chain: 'arbitrum',
address: '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8',
decimals: 6,
symbol: 'USDC',
}

const CompoundUSDCv3: Contract = {
const cUSDCv3: Contract = {
chain: 'arbitrum',
address: '0xA5EDBDD9646f8dFF606d7448e414884C7d905dCA',
underlyings: [USDC],
}

const CompoundRewards: Contract = {
const rewarder: Contract = {
chain: 'arbitrum',
address: '0x88730d254A2f7e6AC8388c3198aFd694bA9f7fae',
}

export const getContracts = async (ctx: BaseContext) => {
const assets = await getAssetsContracts(ctx, [CompoundUSDCv3])
const assets = await getAssetsContracts(ctx, [cUSDCv3])

return {
contracts: { compounders: [CompoundUSDCv3], assets, CompoundRewards },
contracts: { compounders: [cUSDCv3], assets, rewarder },
}
}

const compoundBalances = async (ctx: BalancesContext, compounders: Contract[], rewarder: Contract) => {
return Promise.all([getStakeBalances(ctx, compounders), getRewardBalances(ctx, rewarder, compounders)])
return Promise.all([getSingleStakeBalances(ctx, compounders), getCompRewardBalances(ctx, rewarder, compounders)])
}

export const getBalances: GetBalancesHandler<typeof getContracts> = async (ctx, contracts) => {
const balances = await resolveBalances<typeof getContracts>(ctx, contracts, {
assets: (...args) => getLendBorrowBalances(...args, [CompoundUSDCv3]),
compounders: (...args) => compoundBalances(...args, CompoundRewards),
assets: (...args) => getCompLendBalances(...args, [cUSDCv3]),
compounders: (...args) => compoundBalances(...args, rewarder),
})

const healthFactor = await getHealthFactor(balances as BalanceWithExtraProps[])

return {
groups: [{ balances, healthFactor }],
groups: [{ balances }],
}
}
60 changes: 60 additions & 0 deletions src/adapters/compound-v3/base/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { getAssetsContracts } from '@adapters/compound-v3/common/asset'
import { getCompLendBalances, getCompRewardBalances } from '@adapters/compound-v3/common/balance'
import type { BalancesContext, BaseContext, Contract, GetBalancesHandler } from '@lib/adapter'
import { resolveBalances } from '@lib/balance'
import { getSingleStakeBalances } from '@lib/stake'
import type { Token } from '@lib/token'

const USDbC: Token = {
chain: 'base',
address: '0xd9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca',
decimals: 6,
symbol: 'USDbC',
}

const WETH: Token = {
chain: 'base',
address: '0x4200000000000000000000000000000000000006',
decimals: 18,
symbol: 'WETH',
}

const cUSDbCv3: Contract = {
chain: 'base',
address: '0x9c4ec768c28520B50860ea7a15bd7213a9fF58bf',
underlyings: [USDbC],
}

const cWETHv3: Contract = {
chain: 'base',
address: '0x46e6b214b524310239732D51387075E0e70970bf',
underlyings: [WETH],
}

const rewarder: Contract = {
chain: 'base',
address: '0x123964802e6ABabBE1Bc9547D72Ef1B69B00A6b1',
}

export const getContracts = async (ctx: BaseContext) => {
const assets = await getAssetsContracts(ctx, [cUSDbCv3, cWETHv3])

return {
contracts: { compounders: [cUSDbCv3, cWETHv3], assets, rewarder },
}
}

const compoundBalances = async (ctx: BalancesContext, compounders: Contract[], rewarder: Contract) => {
return Promise.all([getSingleStakeBalances(ctx, compounders), getCompRewardBalances(ctx, rewarder, compounders)])
}

export const getBalances: GetBalancesHandler<typeof getContracts> = async (ctx, contracts) => {
const balances = await resolveBalances<typeof getContracts>(ctx, contracts, {
assets: (...args) => getCompLendBalances(...args, [cUSDbCv3, cWETHv3]),
compounders: (...args) => compoundBalances(...args, rewarder),
})

return {
groups: [{ balances }],
}
}
72 changes: 72 additions & 0 deletions src/adapters/compound-v3/common/asset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import type { BaseContext, Contract } from '@lib/adapter'
import { mapSuccessFilter, range } from '@lib/array'
import { multicall } from '@lib/multicall'

const abi = {
getAssetInfo: {
inputs: [{ internalType: 'uint8', name: 'i', type: 'uint8' }],
name: 'getAssetInfo',
outputs: [
{
components: [
{ internalType: 'uint8', name: 'offset', type: 'uint8' },
{ internalType: 'address', name: 'asset', type: 'address' },
{ internalType: 'address', name: 'priceFeed', type: 'address' },
{ internalType: 'uint64', name: 'scale', type: 'uint64' },
{
internalType: 'uint64',
name: 'borrowCollateralFactor',
type: 'uint64',
},
{
internalType: 'uint64',
name: 'liquidateCollateralFactor',
type: 'uint64',
},
{
internalType: 'uint64',
name: 'liquidationFactor',
type: 'uint64',
},
{ internalType: 'uint128', name: 'supplyCap', type: 'uint128' },
],
internalType: 'struct CometCore.AssetInfo',
name: '',
type: 'tuple',
},
],
stateMutability: 'view',
type: 'function',
},
numAssets: {
inputs: [],
name: 'numAssets',
outputs: [{ internalType: 'uint8', name: '', type: 'uint8' }],
stateMutability: 'view',
type: 'function',
},
} as const

export async function getAssetsContracts(ctx: BaseContext, compounders: Contract[]): Promise<Contract[]> {
const numberOfAssets = await multicall({
ctx,
calls: compounders.map((contract) => ({ target: contract.address })),
abi: abi.numAssets,
})

const assetsInfoRes = await multicall({
ctx,
//@ts-expect-error
calls: mapSuccessFilter(numberOfAssets, (responses) =>
range(0, responses.output).map((res) => ({ target: responses.input.target, params: [res] })),
).flat(),
abi: abi.getAssetInfo,
})

return mapSuccessFilter(assetsInfoRes, (res) => ({
chain: ctx.chain,
address: res.output.asset,
compounder: res.input.target,
collateralFactor: res.output.borrowCollateralFactor,
}))
}
129 changes: 129 additions & 0 deletions src/adapters/compound-v3/common/balance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import type { Balance, BalancesContext, Contract } from '@lib/adapter'
import { mapSuccessFilter } from '@lib/array'
import { multicall } from '@lib/multicall'
import { isNotNullish } from '@lib/type'

const abi = {
borrowBalanceOf: {
inputs: [{ internalType: 'address', name: 'account', type: 'address' }],
name: 'borrowBalanceOf',
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
stateMutability: 'view',
type: 'function',
},
userCollateral: {
inputs: [
{ internalType: 'address', name: '', type: 'address' },
{ internalType: 'address', name: '', type: 'address' },
],
name: 'userCollateral',
outputs: [
{ internalType: 'uint128', name: 'balance', type: 'uint128' },
{ internalType: 'uint128', name: '_reserved', type: 'uint128' },
],
stateMutability: 'view',
type: 'function',
},
getRewardOwed: {
inputs: [
{ internalType: 'address', name: 'comet', type: 'address' },
{ internalType: 'address', name: 'account', type: 'address' },
],
name: 'getRewardOwed',
outputs: [
{
components: [
{ internalType: 'address', name: 'token', type: 'address' },
{ internalType: 'uint256', name: 'owed', type: 'uint256' },
],
internalType: 'struct CometRewards.RewardOwed',
name: '',
type: 'tuple',
},
],
stateMutability: 'nonpayable',
type: 'function',
},
} as const

const COMP: { [key: string]: `0x${string}` } = {
arbitrum: '0x354A6dA3fcde098F8389cad84b0182725c6C91dE',
base: '0x9e1028F5F1D5eDE59748FFceE5532509976840E0',
ethereum: '0xc00e94cb662c3520282e6f5717214004a7f26888',
polygon: '0x8505b9d2254A7Ae468c0E9dd10Ccea3A837aef5c',
}

export async function getCompLendBalances(
ctx: BalancesContext,
assets: Contract[],
compounders: Contract[],
): Promise<Balance[]> {
const [userLendBalances, userBorrowBalances] = await Promise.all([
multicall({
ctx,
calls: assets.map((asset) => ({ target: asset.compounder, params: [ctx.address, asset.address] }) as const),
abi: abi.userCollateral,
}),
multicall({
ctx,
calls: compounders.map((compounder) => ({ target: compounder.address, params: [ctx.address] }) as const),
abi: abi.borrowBalanceOf,
}),
])

const supplyBalance: Balance[] = mapSuccessFilter(userLendBalances, (res, index) => {
const [balance, _reserved] = res.output

return {
chain: ctx.chain,
decimals: assets[index].decimals,
symbol: assets[index].symbol,
address: assets[index].address,
amount: balance,
collateralFactor: assets[index].collateralFactor,
category: 'lend',
}
})

const borrowBalance: Balance[] = mapSuccessFilter(userBorrowBalances, (res, index) => {
const underlying = compounders[index].underlyings?.[0] as Contract

if (!underlying) {
return null
}

return {
chain: ctx.chain,
decimals: underlying.decimals,
symbol: underlying.symbol,
address: underlying.address,
amount: res.output,
category: 'borrow',
}
}).filter(isNotNullish) as Balance[]

return [...supplyBalance, ...borrowBalance]
}

export async function getCompRewardBalances(
ctx: BalancesContext,
rewarder: Contract,
compounders: Contract[],
): Promise<Balance[]> {
const pendingCompRewards = await multicall({
ctx,
calls: compounders.map(
(contract) => ({ target: rewarder.address, params: [contract.address, ctx.address] }) as const,
),
abi: abi.getRewardOwed,
})

return mapSuccessFilter(pendingCompRewards, (res) => ({
chain: ctx.chain,
address: COMP[ctx.chain],
decimals: 18,
symbol: 'COMP',
amount: res.output.owed,
category: 'reward',
}))
}
Loading
Loading