Skip to content

Commit

Permalink
Jitosol (#388)
Browse files Browse the repository at this point in the history
  • Loading branch information
bergarces authored Dec 13, 2024
1 parent 491f776 commit 7c44b6a
Show file tree
Hide file tree
Showing 38 changed files with 4,256 additions and 1,628 deletions.
4,963 changes: 3,576 additions & 1,387 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion packages/adapters-library/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@
"typedoc-plugin-mdn-links": "^3.1.0"
},
"dependencies": {
"@metaplex-foundation/js": "^0.20.1",
"@solana/spl-stake-pool": "^1.1.8",
"@solana/spl-token": "^0.4.9",
"@solana/web3.js": "^1.95.8",
"better-sqlite3": "^11.2.1",
"ethers": "^6.7.1",
"evm-maths": "^6.0.0",
Expand All @@ -72,4 +76,4 @@
"pino": "^8.15.0",
"zod": "^3.22.4"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export class AaveV3RewardsAdapter implements IProtocolAdapter {
includeInUnwrap: false,
}

private INCENTIVES_CONTRACT_ADDRESSES = {
private INCENTIVES_CONTRACT_ADDRESSES: Partial<Record<Chain, string>> = {
[Chain.Arbitrum]: getAddress('0x929EC64c34a17401F460460D4B9390518E5B473e'),
[Chain.Ethereum]: getAddress('0x8164Cc65827dcFe994AB23944CBC90e0aa80bFcb'),
[Chain.Fantom]: getAddress('0x929EC64c34a17401F460460D4B9390518E5B473e'),
Expand Down Expand Up @@ -82,20 +82,23 @@ export class AaveV3RewardsAdapter implements IProtocolAdapter {
this.adaptersController = adaptersController
this.helpers = helpers

if (this.chainId === Chain.Linea) {
const incentivesContractAddress =
this.INCENTIVES_CONTRACT_ADDRESSES[this.chainId]

if (!incentivesContractAddress) {
throw new NotImplementedError()
}

this.incentivesContract = IncentivesContract__factory.connect(
this.INCENTIVES_CONTRACT_ADDRESSES[this.chainId],
incentivesContractAddress,
this.provider,
)

/**
* Fake protocol token created to satisfy return type
*/
this.INCENTIVES_CONTRACT_DETAILS = {
address: this.INCENTIVES_CONTRACT_ADDRESSES[this.chainId],
address: incentivesContractAddress,
name: 'Aave V3 Rewards',
symbol: 'Rewards',
decimals: 18,
Expand Down
2 changes: 1 addition & 1 deletion packages/adapters-library/src/adapters/beefy/sdk/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { fetchIchiBalanceBreakdown } from './unwrap/ichi'
import { fetchPendleEquilibriaBalanceBreakdown } from './unwrap/pendle_equilibria'
import { fetchSolidlyBalanceBreakdown } from './unwrap/solidly'

export const chainIdMap: Record<Chain, string> = {
export const chainIdMap: Partial<Record<Chain, string>> = {
[Chain.Arbitrum]: 'arbitrum',
[Chain.Avalanche]: 'avax',
[Chain.Base]: 'base',
Expand Down
5 changes: 5 additions & 0 deletions packages/adapters-library/src/adapters/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import { testCases as gmxFarmingTestCases } from './gmx/products/farming/tests/t
import { testCases as gmxGlpTestCases } from './gmx/products/glp/tests/testCases'
import { testCases as gmxVestingTestCases } from './gmx/products/vesting/tests/testCases'
import { testCases as iZiSwapPoolTestCases } from './iziswap/products/pool/tests/testCases'
import { testCases as jitoJitosolTestCases } from './jito/products/jitosol/tests/testCases'
import { testCases as lidoStEthTestCases } from './lido/products/st-eth/tests/testCases'
import { testCases as lidoWstEthTestCases } from './lido/products/wst-eth/tests/testCases'
import { testCases as lynexAlgebraTestCases } from './lynex/products/algebra/tests/testCases'
Expand Down Expand Up @@ -254,6 +255,10 @@ const allTestCases: Record<Protocol, Record<string, TestCase[]>> = {
['pool']: iZiSwapPoolTestCases,
},

[Protocol.Jito]: {
['jitosol']: jitoJitosolTestCases,
},

[Protocol.Lido]: {
['st-eth']: lidoStEthTestCases,
['wst-eth']: lidoWstEthTestCases,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import { Metaplex } from '@metaplex-foundation/js'
import { getStakePoolAccount } from '@solana/spl-stake-pool'
import { AccountLayout, getMint } from '@solana/spl-token'
import { Connection, PublicKey } from '@solana/web3.js'
import { AdaptersController } from '../../../../core/adaptersController'
import { Chain } from '../../../../core/constants/chains'
import { CacheToDb } from '../../../../core/decorators/cacheToDb'
import { NotImplementedError } from '../../../../core/errors/errors'
import { buildTrustAssetIconUrl } from '../../../../core/utils/buildIconUrl'
import { nativeToken } from '../../../../core/utils/nativeTokens'
import { Helpers, SolanaHelpers } from '../../../../scripts/helpers'
import {
IProtocolAdapter,
ProtocolToken,
} from '../../../../types/IProtocolAdapter'
import {
GetEventsInput,
GetPositionsInput,
GetTotalValueLockedInput,
MovementsByBlock,
PositionType,
ProtocolDetails,
ProtocolPosition,
ProtocolTokenTvl,
SolanaProtocolAdapterParams,
TokenType,
UnwrapExchangeRate,
UnwrapInput,
} from '../../../../types/adapter'
import { Protocol } from '../../../protocols'

const JITO_STAKE_POOL = new PublicKey(
'Jito4APyf642JPZPx3hGc6WWJ8zPKtRbRs4P815Awbb',
)

// TODO Can be extracted from stake pool address
const JITOSOL_TOKEN_ADDRESS = new PublicKey(
'J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn',
)

type AdditionalMetadata = {
stakePool: string
}

export class JitoJitosolAdapter implements IProtocolAdapter {
productId = 'jitosol'
protocolId: Protocol
chainId = Chain.Solana
helpers: SolanaHelpers

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

private provider: Connection

adaptersController: AdaptersController

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

getProtocolDetails(): ProtocolDetails {
return {
protocolId: this.protocolId,
name: 'Jito',
description: 'Jito defi adapter',
siteUrl: 'https://www.jito.network/staking/',
iconUrl: buildTrustAssetIconUrl(
Chain.Solana,
JITOSOL_TOKEN_ADDRESS.toString(),
),
positionType: PositionType.Staked,
chainId: this.chainId,
productId: this.productId,
}
}

@CacheToDb
async getProtocolTokens(): Promise<ProtocolToken<AdditionalMetadata>[]> {
const metaplex = Metaplex.make(this.provider)

const token = await metaplex
.nfts()
.findByMint({ mintAddress: JITOSOL_TOKEN_ADDRESS })

const mintInfo = await getMint(this.provider, JITOSOL_TOKEN_ADDRESS)

return [
{
address: JITOSOL_TOKEN_ADDRESS.toString(),
name: token.name,
symbol: token.symbol,
decimals: mintInfo.decimals,
stakePool: JITO_STAKE_POOL.toString(),
underlyingTokens: [nativeToken[Chain.Solana]],
},
]
}

async getPositions({
userAddress,
blockNumber,
}: GetPositionsInput): Promise<ProtocolPosition[]> {
const { stakePool, underlyingTokens, ...protocolToken } = (
await this.getProtocolTokens()
)[0]!

const tokenAccounts = await this.provider.getTokenAccountsByOwner(
new PublicKey(userAddress),
{
mint: new PublicKey(protocolToken.address),
},
)

const userBalance = tokenAccounts.value.reduce((total, accountInfo) => {
const accountData = AccountLayout.decode(
Uint8Array.from(accountInfo.account.data),
)

return total + accountData.amount
}, 0n)

return [
{
...protocolToken,
type: TokenType.Protocol,
balanceRaw: userBalance,
},
]
}

async getWithdrawals({
protocolTokenAddress,
fromBlock,
toBlock,
userAddress,
}: GetEventsInput): Promise<MovementsByBlock[]> {
throw new NotImplementedError()
}

async getDeposits({
protocolTokenAddress,
fromBlock,
toBlock,
userAddress,
}: GetEventsInput): Promise<MovementsByBlock[]> {
throw new NotImplementedError()
}

async getTotalValueLocked({
protocolTokenAddresses,
blockNumber,
}: GetTotalValueLockedInput): Promise<ProtocolTokenTvl[]> {
throw new NotImplementedError()
}

async unwrap(input: UnwrapInput): Promise<UnwrapExchangeRate> {
const {
stakePool,
underlyingTokens: [underlyingToken],
...protocolToken
} = (await this.getProtocolTokens())[0]!

// totalLamports is the total amount of SOL units in the pool
// poolTokenSupply is the total amount of JitoSOL tokens in circulation
const {
account: {
data: { totalLamports, poolTokenSupply },
},
} = await getStakePoolAccount(this.provider, new PublicKey(stakePool))

const underlyingRateRaw =
(BigInt(totalLamports.toString()) *
10n ** BigInt(protocolToken.decimals)) /
BigInt(poolTokenSupply.toString())

return {
...protocolToken,
baseRate: 1,
type: TokenType.Protocol,
tokens: [
{
...underlyingToken!,
type: TokenType.Underlying,
underlyingRateRaw,
},
],
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { TestCase } from '../../../../../types/testCase'

export const testCases: TestCase[] = [
// {
// chainId: Chain.Ethereum,
// method: 'positions',
// input: {
// userAddress: '0x6b8Be925ED8277fE4D27820aE4677e76Ebf4c255',
// },
// },
// {
// chainId: Chain.Ethereum,
// method: 'profits',
// input: {
// userAddress: '0xCEadFdCCd0E8E370D985c49Ed3117b2572243A4a',
// timePeriod: TimePeriod.oneDay,
// },
// },
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Connection } from '@solana/web3.js'
import { AdaptersController } from '../../../../core/adaptersController'
import { Chain } from '../../../../core/constants/chains'
import { Helpers } from '../../../../scripts/helpers'
import { UnwrapExchangeRate } from '../../../../types/adapter'
import { Erc20Metadata } from '../../../../types/erc20Metadata'
import { IPricesAdapter } from '../../../prices-v2/products/usd/pricesV2UsdAdapter'

export class PricesSolanaUsdAdapter implements IPricesAdapter {
productId = 'usd'
chainId = Chain.Solana
helpers: Helpers

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

private provider: Connection

adaptersController: AdaptersController

constructor({
provider,
adaptersController,
}: {
provider: Connection
adaptersController: AdaptersController
}) {
this.provider = provider
this.adaptersController = adaptersController
this.helpers = {} as Helpers
}

async getPrice({
blockNumber,
tokenMetadata,
}: {
blockNumber: number
tokenMetadata: Erc20Metadata
}): Promise<UnwrapExchangeRate> {
// TODO Implement price fetching
throw new Error('Error fetching price')
}
}
1 change: 1 addition & 0 deletions packages/adapters-library/src/adapters/protocols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const Protocol = {
Gmx: 'gmx',
GmxV2: 'gmx-v2',
IZiSwap: 'iziswap',
Jito: 'jito',
Lido: 'lido',
Lynex: 'lynex',
Maker: 'maker',
Expand Down
Loading

0 comments on commit 7c44b6a

Please sign in to comment.