Skip to content

Commit

Permalink
aave staking adapter (#370)
Browse files Browse the repository at this point in the history
* aave staking adapter

* corrections

* test update
  • Loading branch information
bergarces authored Nov 7, 2024
1 parent 77d2c19 commit 892aa4a
Show file tree
Hide file tree
Showing 13 changed files with 7,509 additions and 2,170 deletions.
Binary file modified ethereum.db
Binary file not shown.
1,901 changes: 1,901 additions & 0 deletions packages/adapters-library/src/adapters/aave-v3/contracts/StakeToken.ts

Large diffs are not rendered by default.

1,771 changes: 1,771 additions & 0 deletions packages/adapters-library/src/adapters/aave-v3/contracts/abis/stake-token.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
/* eslint-disable */
export { IncentivesContract__factory } from "./IncentivesContract__factory";
export { PoolContract__factory } from "./PoolContract__factory";
export { StakeToken__factory } from "./StakeToken__factory";
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
/* eslint-disable */
export type { IncentivesContract } from "./IncentivesContract";
export type { PoolContract } from "./PoolContract";
export type { StakeToken } from "./StakeToken";
export * as factories from "./factories";
export { IncentivesContract__factory } from "./factories/IncentivesContract__factory";
export { PoolContract__factory } from "./factories/PoolContract__factory";
export { StakeToken__factory } from "./factories/StakeToken__factory";
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
import { AdaptersController } from '../../../../core/adaptersController'
import { Chain } from '../../../../core/constants/chains'
import { CacheToDb } from '../../../../core/decorators/cacheToDb'
import { CustomJsonRpcProvider } from '../../../../core/provider/CustomJsonRpcProvider'
import { filterMapAsync } from '../../../../core/utils/filters'
import { Helpers } from '../../../../scripts/helpers'
import {
IProtocolAdapter,
ProtocolToken,
} from '../../../../types/IProtocolAdapter'
import {
GetEventsInput,
GetPositionsInput,
GetRewardPositionsInput,
GetTotalValueLockedInput,
MovementsByBlock,
PositionType,
ProtocolAdapterParams,
ProtocolDetails,
ProtocolPosition,
ProtocolTokenTvl,
TokenType,
UnderlyingReward,
UnwrapExchangeRate,
UnwrapInput,
} from '../../../../types/adapter'
import { Erc20Metadata } from '../../../../types/erc20Metadata'
import { Protocol } from '../../../protocols'
import { StakeToken__factory } from '../../contracts'
import { AAVE_ICON_URL } from '../rewards/aaveV3RewardsAdapter'

type AdditionalMetadata = {
rewardTokens: Erc20Metadata[]
}

export class AaveV3StakingAdapter implements IProtocolAdapter {
productId = 'staking'
protocolId: Protocol
chainId: Chain
helpers: Helpers

adapterSettings = {
enablePositionDetectionByProtocolTokenTransfer: true,
includeInUnwrap: true,
}

private provider: CustomJsonRpcProvider

adaptersController: AdaptersController

constructor({
provider,
chainId,
protocolId,
adaptersController,
helpers,
}: ProtocolAdapterParams) {
this.provider = provider
this.chainId = chainId
this.protocolId = protocolId
this.adaptersController = adaptersController
this.helpers = helpers
}

getProtocolDetails(): ProtocolDetails {
return {
protocolId: this.protocolId,
name: 'Aave v3 Staking',
description: 'AaveV3 defi adapter for Safety Module staking',
siteUrl: 'https://app.aave.com/staking/',
iconUrl: AAVE_ICON_URL,
positionType: PositionType.Staked,
chainId: this.chainId,
productId: this.productId,
}
}

@CacheToDb
async getProtocolTokens(): Promise<ProtocolToken<AdditionalMetadata>[]> {
const protocolTokens = await Promise.all(
[
'0x4da27a545c0c5B758a6BA100e3a049001de870f5', // stkAAVE
'0x1a88Df1cFe15Af22B3c4c783D4e6F7F9e0C1885d', // stkGHO
'0x9eDA81C21C273a82BE9Bbc19B6A6182212068101', // stkAAVEwstETHBPTv2
].map(async (address) => this.helpers.getTokenMetadata(address)),
)

return await Promise.all(
protocolTokens.map(async (protocolToken) => {
const stakeToken = StakeToken__factory.connect(
protocolToken.address,
this.provider,
)

const [underlyingTokenAddress, rewardTokenAddress] = await Promise.all([
stakeToken.STAKED_TOKEN(),
stakeToken.REWARD_TOKEN(),
])

const [underlyingToken, rewardToken] = await Promise.all([
this.helpers.getTokenMetadata(underlyingTokenAddress),
this.helpers.getTokenMetadata(rewardTokenAddress),
])

return {
...protocolToken,
underlyingTokens: [underlyingToken],
rewardTokens: [rewardToken],
}
}),
)
}

private async getProtocolTokenByAddress(protocolTokenAddress: string) {
return this.helpers.getProtocolTokenByAddress({
protocolTokens: await this.getProtocolTokens(),
protocolTokenAddress,
})
}

async getPositions(input: GetPositionsInput): Promise<ProtocolPosition[]> {
return this.helpers.getBalanceOfTokens({
...input,
protocolTokens: await this.getProtocolTokens(),
})
}

async getRewardPositions({
userAddress,
protocolTokenAddress,
blockNumber,
}: GetRewardPositionsInput): Promise<UnderlyingReward[]> {
const protocolToken = this.helpers.getProtocolTokenByAddress({
protocolTokenAddress,
protocolTokens: await this.getProtocolTokens(),
})

if (!protocolToken.rewardTokens) {
return []
}

return await filterMapAsync(
protocolToken.rewardTokens,
async (rewardTokenMetadata) => {
const stakeToken = StakeToken__factory.connect(
protocolToken.address,
this.provider,
)

const rewardBalance = await stakeToken.getTotalRewardsBalance(
userAddress,
{
blockTag: blockNumber,
},
)

if (rewardBalance === 0n) {
return undefined
}

return {
...rewardTokenMetadata,
type: TokenType.UnderlyingClaimable,
balanceRaw: rewardBalance,
}
},
)
}

async getWithdrawals({
protocolTokenAddress,
fromBlock,
toBlock,
userAddress,
}: GetEventsInput): Promise<MovementsByBlock[]> {
return this.helpers.withdrawals({
protocolToken: await this.getProtocolTokenByAddress(protocolTokenAddress),
filter: { fromBlock, toBlock, userAddress },
})
}

async getDeposits({
protocolTokenAddress,
fromBlock,
toBlock,
userAddress,
}: GetEventsInput): Promise<MovementsByBlock[]> {
return this.helpers.deposits({
protocolToken: await this.getProtocolTokenByAddress(protocolTokenAddress),
filter: { fromBlock, toBlock, userAddress },
})
}

async getTotalValueLocked({
protocolTokenAddresses,
blockNumber,
}: GetTotalValueLockedInput): Promise<ProtocolTokenTvl[]> {
const protocolTokens = await this.getProtocolTokens()

return await this.helpers.tvl({
protocolTokens,
filterProtocolTokenAddresses: protocolTokenAddresses,
blockNumber,
})
}

async unwrap({
protocolTokenAddress,
}: UnwrapInput): Promise<UnwrapExchangeRate> {
return this.helpers.unwrapOneToOne({
protocolToken: await this.getProtocolTokenByAddress(protocolTokenAddress),
underlyingTokens: (
await this.getProtocolTokenByAddress(protocolTokenAddress)
).underlyingTokens,
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"blockNumber": 21136131,
"latency": "Latency: 1.787 seconds",
"aggregatedValues": ["USD389,150.47"],
"snapshot": [
{
"protocolId": "aave-v3",
"name": "Aave v3 Staking",
"description": "AaveV3 defi adapter for Safety Module staking",
"siteUrl": "https://app.aave.com/staking/",
"iconUrl": "https://cryptologos.cc/logos/aave-aave-logo.png",
"positionType": "stake",
"chainId": 1,
"productId": "staking",
"chainName": "ethereum",
"success": true,
"tokens": [
{
"address": "0x1a88Df1cFe15Af22B3c4c783D4e6F7F9e0C1885d",
"name": "stk GHO",
"symbol": "stkGHO",
"decimals": 18,
"balanceRaw": "386984197341887736805015n",
"type": "protocol",
"tokens": [
{
"address": "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9",
"name": "Aave Token",
"symbol": "AAVE",
"decimals": 18,
"type": "underlying-claimable",
"balanceRaw": "11644419193456656219n",
"balance": 11.644419193456656,
"price": 182.98103348656943,
"iconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/logo.png"
},
{
"address": "0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f",
"name": "Gho Token",
"symbol": "GHO",
"decimals": 18,
"type": "underlying",
"balanceRaw": "386984197341887736805015n",
"balance": 386984.19734188775,
"price": 1.0000918951901496,
"iconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f/logo.png"
}
],
"balance": 386984.19734188775
}
]
}
],
"rpcResponses": {
"2d9e20727fe7076c64cdb0e9eefb83d5": {
"result": "0x0000000000000000000000000000000000000000000051f27493604bd37b7697"
},
"386fb807616c3892bc7920d682f3f37a": {
"result": "0x000000000000000000000000000000000000000000000000a199498fc9b97f5b"
},
"731ce42c7bf681cde66ce61e5f845885": {
"result": "0x00000000000000000000000000000000000000000000000700000000000006e90000000000000000000000000000000000000000000000000000004190af6cd000000000000000000000000000000000000000000000000000000000672cb65400000000000000000000000000000000000000000000000000000000672cb66300000000000000000000000000000000000000000000000700000000000006e9"
},
"a527f1dc81f463bf406299c2a6112af0": {
"result": "0x00000000000000000000000000000000000000000000000000014300d9716556"
},
"540e67fcfa7c02400c79a292fc5da6ca": {
"result": "0x00000000000000000000000000000000000000000000000000e6da0cd3b408b2"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Chain } from '../../../../../core/constants/chains'
import type { TestCase } from '../../../../../types/testCase'

export const testCases: TestCase[] = [
{
key: 'stkGHO',
chainId: Chain.Ethereum,
method: 'positions',

input: {
userAddress: '0x10fd41ec6FDFE7f9C7Cc7c12DC4f0B4e77659BfA',
filterProtocolTokens: ['0x1a88Df1cFe15Af22B3c4c783D4e6F7F9e0C1885d'],
},

blockNumber: 21136131,
},
]
2 changes: 2 additions & 0 deletions packages/adapters-library/src/adapters/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { testCases as aaveV2StableDebtTokenTestCases } from './aave-v2/products/
import { testCases as aaveV2VariableDebtTokenTestCases } from './aave-v2/products/variable-debt-token/tests/testCases'
import { testCases as aaveV3ATokenTestCases } from './aave-v3/products/a-token/tests/testCases'
import { testCases as aaveV3StableDebtTokenTestCases } from './aave-v3/products/stable-debt-token/tests/testCases'
import { testCases as aaveV3StakingTestCases } from './aave-v3/products/staking/tests/testCases'
import { testCases as aaveV3VariableDebtTokenTestCases } from './aave-v3/products/variable-debt-token/tests/testCases'
import { testCases as angleProtocolSavingsTestCases } from './angle-protocol/products/savings/tests/testCases'
import { testCases as beefyCowTokenTestCases } from './beefy/products/cow-token/tests/testCases'
Expand Down Expand Up @@ -140,6 +141,7 @@ const allTestCases: Record<Protocol, Record<string, TestCase[]>> = {
[Protocol.AaveV3]: {
['a-token']: aaveV3ATokenTestCases,
['stable-debt-token']: aaveV3StableDebtTokenTestCases,
['staking']: aaveV3StakingTestCases,
['variable-debt-token']: aaveV3VariableDebtTokenTestCases,
},
[Protocol.AngleProtocol]: {
Expand Down
3 changes: 3 additions & 0 deletions packages/adapters-library/src/adapters/supportedProtocols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ import { GmxFarmingAdapter } from './gmx/products/farming/gmxFarmingAdapter'

import { GmxVestingAdapter } from './gmx/products/vesting/gmxVestingAdapter'

import { AaveV3StakingAdapter } from './aave-v3/products/staking/aaveV3StakingAdapter'

export const supportedProtocols: Record<
Protocol,
Partial<
Expand Down Expand Up @@ -164,6 +166,7 @@ export const supportedProtocols: Record<
AaveV3StableDebtTokenPoolAdapter,
AaveV3VariableDebtTokenPoolAdapter,
AaveV3RewardsAdapter,
AaveV3StakingAdapter,
],
[Chain.Polygon]: [
AaveV3ATokenPoolAdapter,
Expand Down
Loading

0 comments on commit 892aa4a

Please sign in to comment.