Skip to content
This repository has been archived by the owner on Jan 24, 2024. It is now read-only.

Commit

Permalink
fix(lyra-avalon): Fix Pool positions on Optimism and update stkLyra p…
Browse files Browse the repository at this point in the history
…osition (#2847)
  • Loading branch information
wpoulin authored Jul 29, 2023
1 parent d23de24 commit db16be2
Show file tree
Hide file tree
Showing 9 changed files with 211 additions and 232 deletions.
88 changes: 5 additions & 83 deletions src/apps/lyra-avalon/arbitrum/lyra-avalon.pool.token-fetcher.ts
Original file line number Diff line number Diff line change
@@ -1,89 +1,11 @@
import { Inject } from '@nestjs/common';
import { gql } from 'graphql-request';

import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface';
import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator';
import { gqlFetch } from '~app-toolkit/helpers/the-graph.helper';
import { AppTokenTemplatePositionFetcher } from '~position/template/app-token.template.position-fetcher';
import {
DefaultAppTokenDefinition,
DefaultAppTokenDataProps,
GetAddressesParams,
GetPricePerShareParams,
} from '~position/template/app-token.template.types';

import { LyraAvalonContractFactory, LyraLiquidityToken } from '../contracts';

// TODO: find better way to determine available markets
type QueryResponse = {
markets: {
id: string;
baseAddress: string;
quoteAddress: string;
liquidityPool: {
id: string;
};
}[];
};
const QUERY = gql`
{
markets(where: { isRemoved: false }) {
id
baseAddress
quoteAddress
liquidityPool {
id
}
}
}
`;
import { LyraAvalonPoolTokenFetcher } from '../common/lyra-avalon.pool.token-fetcher';

@PositionTemplate()
export class ArbitrumLyraAvalonPoolTokenFetcher extends AppTokenTemplatePositionFetcher<LyraLiquidityToken> {
groupLabel = 'Pools';

constructor(
@Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit,
@Inject(LyraAvalonContractFactory) protected readonly contractFactory: LyraAvalonContractFactory,
) {
super(appToolkit);
}

getContract(address: string): LyraLiquidityToken {
return this.contractFactory.lyraLiquidityToken({ address, network: this.network });
}

async getAddresses({ multicall }: GetAddressesParams<DefaultAppTokenDefinition>) {
const registryContract = this.contractFactory.lyraRegistry({
address: '0x6c87e4364fd44b0d425adfd0328e56b89b201329',
network: this.network,
});

const marketsResponse = await gqlFetch<QueryResponse>({
endpoint: 'https://api.lyra.finance/subgraph/arbitrum/v2/api',
query: QUERY,
});

const markets = await Promise.all(
marketsResponse.markets.map(market => multicall.wrap(registryContract).marketAddresses(market.id)),
);

return markets.map(market => market.liquidityToken);
}

async getUnderlyingTokenDefinitions() {
return [{ address: '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', network: this.network }];
}

async getPricePerShare({
contract,
multicall,
}: GetPricePerShareParams<LyraLiquidityToken, DefaultAppTokenDataProps, DefaultAppTokenDefinition>) {
const pool = await contract.liquidityPool();
const poolContract = this.contractFactory.lyraLiquidityPool({ address: pool, network: this.network });
const ratioRaw = await multicall.wrap(poolContract).getTokenPrice();
const ratio = Number(ratioRaw) / 10 ** 18;
export class ArbitrumLyraAvalonPoolTokenFetcher extends LyraAvalonPoolTokenFetcher {
subgraphUrl = 'https://api.lyra.finance/subgraph/arbitrum/v2/api';

return [ratio];
}
registryContractAddress = '0x6c87e4364fd44b0d425adfd0328e56b89b201329';
underlyingContractAddress = '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8';
}
91 changes: 91 additions & 0 deletions src/apps/lyra-avalon/common/lyra-avalon.pool.token-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { Inject } from '@nestjs/common';
import { gql } from 'graphql-request';

import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface';
import { gqlFetch } from '~app-toolkit/helpers/the-graph.helper';
import { AppTokenTemplatePositionFetcher } from '~position/template/app-token.template.position-fetcher';
import {
DefaultAppTokenDefinition,
DefaultAppTokenDataProps,
GetAddressesParams,
GetPricePerShareParams,
} from '~position/template/app-token.template.types';

import { LyraAvalonContractFactory, LyraLiquidityToken } from '../contracts';

// TODO: find better way to determine available markets
type QueryResponse = {
markets: {
id: string;
baseAddress: string;
quoteAddress: string;
liquidityPool: {
id: string;
};
}[];
};
const QUERY = gql`
{
markets(where: { isRemoved: false }) {
id
baseAddress
quoteAddress
liquidityPool {
id
}
}
}
`;

export abstract class LyraAvalonPoolTokenFetcher extends AppTokenTemplatePositionFetcher<LyraLiquidityToken> {
groupLabel = 'Pools';

abstract subgraphUrl: string;
abstract registryContractAddress: string;
abstract underlyingContractAddress: string;

constructor(
@Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit,
@Inject(LyraAvalonContractFactory) protected readonly contractFactory: LyraAvalonContractFactory,
) {
super(appToolkit);
}

getContract(address: string): LyraLiquidityToken {
return this.contractFactory.lyraLiquidityToken({ address, network: this.network });
}

async getAddresses({ multicall }: GetAddressesParams<DefaultAppTokenDefinition>) {
const registryContract = this.contractFactory.lyraRegistry({
address: this.registryContractAddress,
network: this.network,
});

const marketsResponse = await gqlFetch<QueryResponse>({
endpoint: this.subgraphUrl,
query: QUERY,
});

const markets = await Promise.all(
marketsResponse.markets.map(market => multicall.wrap(registryContract).marketAddresses(market.id)),
);

return markets.map(market => market.liquidityToken);
}

async getUnderlyingTokenDefinitions() {
return [{ address: this.underlyingContractAddress, network: this.network }];
}

async getPricePerShare({
contract,
multicall,
}: GetPricePerShareParams<LyraLiquidityToken, DefaultAppTokenDataProps, DefaultAppTokenDefinition>) {
const pool = await contract.liquidityPool();
const poolContract = this.contractFactory.lyraLiquidityPool({ address: pool, network: this.network });
const ratioRaw = await multicall.wrap(poolContract).getTokenPrice();
const ratio = Number(ratioRaw) / 10 ** 18;

return [ratio];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Inject } from '@nestjs/common';

import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface';
import { getLabelFromToken } from '~app-toolkit/helpers/presentation/image.present';
import { MetaType } from '~position/position.interface';
import { ContractPositionTemplatePositionFetcher } from '~position/template/contract-position.template.position-fetcher';
import {
DefaultContractPositionDefinition,
GetDisplayPropsParams,
GetTokenBalancesParams,
GetTokenDefinitionsParams,
} from '~position/template/contract-position.template.types';

import { LyraAvalonContractFactory, LyraStkLyra } from '../contracts';

export abstract class LyraAvalonStkLyraClaimableContractPositionFetcher extends ContractPositionTemplatePositionFetcher<LyraStkLyra> {
groupLabel = 'stkLYRA Rewards';

abstract stkLyraContractAddress: string;

constructor(
@Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit,
@Inject(LyraAvalonContractFactory) protected readonly contractFactory: LyraAvalonContractFactory,
) {
super(appToolkit);
}

getContract(address: string): LyraStkLyra {
return this.contractFactory.lyraStkLyra({ address, network: this.network });
}

async getDefinitions(): Promise<DefaultContractPositionDefinition[]> {
return [{ address: this.stkLyraContractAddress }];
}

async getTokenDefinitions({ contract }: GetTokenDefinitionsParams<LyraStkLyra>) {
return [
{
metaType: MetaType.CLAIMABLE,
address: await contract.STAKED_TOKEN(),
network: this.network,
},
];
}

async getLabel({ contractPosition }: GetDisplayPropsParams<LyraStkLyra>) {
return `Claimable ${getLabelFromToken(contractPosition.tokens[0])}`;
}

async getTokenBalancesPerPosition({ address, contract }: GetTokenBalancesParams<LyraStkLyra>) {
const rewardBalance = await contract.getTotalRewardsBalance(address);
return [rewardBalance];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator';

import { LyraAvalonStkLyraClaimableContractPositionFetcher } from '../common/lyra-avalon.stk-lyra-claimable.contract-position-fetcher';

@PositionTemplate()
export class EthereumLyraAvalonStkLyraClaimableContractPositionFetcher extends LyraAvalonStkLyraClaimableContractPositionFetcher {
groupLabel = 'stkLYRA Rewards';

stkLyraContractAddress = '0xcb9f85730f57732fc899fb158164b9ed60c77d49';
}
10 changes: 7 additions & 3 deletions src/apps/lyra-avalon/lyra-avalon.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import { ArbitrumLyraAvalonPoolTokenFetcher } from './arbitrum/lyra-avalon.pool.
import { ArbitrumLyraAvalonStkLyraTokenFetcher } from './arbitrum/lyra-avalon.stk-lyra.token-fetcher';
import { LyraAvalonContractFactory } from './contracts';
import { EthereumLyraAvalonStakingContractPositionFetcher } from './ethereum/lyra-avalon.staking.contract-position-fetcher';
import { EthereumLyraAvalonStkLyraClaimableContractPositionFetcher } from './ethereum/lyra-avalon.stk-lyra-claimable.contract-position-fetcher';
import { EthereumLyraAvalonStkLyraTokenFetcher } from './ethereum/lyra-avalon.stk-lyra.token-fetcher';
import { OptimismLyraAvalonOptionsContractPositionFetcher } from './optimism/lyra-avalon.options.contract-position-fetcher';
import { OptimismLyraAvalonPoolTokenFetcher } from './optimism/lyra-avalon.pool.token-fetcher';
import { OptimismLyraAvalonStakingContractPositionFetcher } from './optimism/lyra-avalon.staking.contract-position-fetcher';
import { OptimismLyraAvalonStkLyraClaimableContractPositionFetcher } from './optimism/lyra-avalon.stk-lyra-claimable.contract-position-fetcher';
import { OptimismLyraAvalonStkLyraOldTokenFetcher } from './optimism/lyra-avalon.stk-lyra-old.token-fetcher';
import { OptimismLyraAvalonStkLyraTokenFetcher } from './optimism/lyra-avalon.stk-lyra.token-fetcher';

@Module({
Expand All @@ -21,15 +23,17 @@ import { OptimismLyraAvalonStkLyraTokenFetcher } from './optimism/lyra-avalon.st
ArbitrumLyraAvalonOptionsContractPositionFetcher,
ArbitrumLyraAvalonStkLyraTokenFetcher,
ArbitrumLyraAvalonPoolTokenFetcher,
// Ethereum
EthereumLyraAvalonStkLyraTokenFetcher,
EthereumLyraAvalonStakingContractPositionFetcher,
EthereumLyraAvalonStkLyraClaimableContractPositionFetcher,
// Optimism
OptimismLyraAvalonOptionsContractPositionFetcher,
OptimismLyraAvalonPoolTokenFetcher,
OptimismLyraAvalonStakingContractPositionFetcher,
OptimismLyraAvalonStkLyraOldTokenFetcher,
OptimismLyraAvalonStkLyraTokenFetcher,
OptimismLyraAvalonStkLyraClaimableContractPositionFetcher,
// Ethereum
EthereumLyraAvalonStkLyraTokenFetcher,
EthereumLyraAvalonStakingContractPositionFetcher,
],
})
export class LyraAvalonAppModule extends AbstractApp() {}
103 changes: 5 additions & 98 deletions src/apps/lyra-avalon/optimism/lyra-avalon.pool.token-fetcher.ts
Original file line number Diff line number Diff line change
@@ -1,103 +1,10 @@
import { Inject } from '@nestjs/common';
import Axios from 'axios';
import { gql } from 'graphql-request';

import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface';
import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator';
import { gqlFetch } from '~app-toolkit/helpers/the-graph.helper';
import { AppTokenTemplatePositionFetcher } from '~position/template/app-token.template.position-fetcher';
import {
DefaultAppTokenDefinition,
DefaultAppTokenDataProps,
GetAddressesParams,
GetPricePerShareParams,
} from '~position/template/app-token.template.types';

import { LyraAvalonContractFactory, LyraLiquidityToken } from '../contracts';

type LyraMainnetAddresses = Record<
string,
{
contractName: string;
source: string;
address: string;
txn: string;
blockNumber: number;
}
>;

// TODO: find better way to determine available markets
type QueryResponse = {
markets: {
id: string;
baseAddress: string;
quoteAddress: string;
liquidityPool: {
id: string;
};
}[];
};
const QUERY = gql`
{
markets(where: { isRemoved: false }) {
id
baseAddress
quoteAddress
liquidityPool {
id
}
}
}
`;
import { LyraAvalonPoolTokenFetcher } from '../common/lyra-avalon.pool.token-fetcher';

@PositionTemplate()
export class OptimismLyraAvalonPoolTokenFetcher extends AppTokenTemplatePositionFetcher<LyraLiquidityToken> {
groupLabel = 'Pools';

constructor(
@Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit,
@Inject(LyraAvalonContractFactory) protected readonly contractFactory: LyraAvalonContractFactory,
) {
super(appToolkit);
}

getContract(address: string): LyraLiquidityToken {
return this.contractFactory.lyraLiquidityToken({ address, network: this.network });
}

async getAddresses({ multicall }: GetAddressesParams<DefaultAppTokenDefinition>) {
const dataUrl = 'https://raw.githubusercontent.com/lyra-finance/subgraph/master/addresses/mainnet-ovm/lyra.json';
const { data: addresses } = await Axios.get<LyraMainnetAddresses>(dataUrl);
const registryContract = this.contractFactory.lyraRegistry({
address: addresses.LyraRegistry.address,
network: this.network,
});

const marketsResponse = await gqlFetch<QueryResponse>({
endpoint: 'https://api.lyra.finance/subgraph/optimism/v1/api',
query: QUERY,
});

const markets = await Promise.all(
marketsResponse.markets.map(market => multicall.wrap(registryContract).marketAddresses(market.id)),
);

return markets.map(market => market.liquidityToken);
}

async getUnderlyingTokenDefinitions() {
return [{ address: '0x8c6f28f2f1a3c87f0f938b96d27520d9751ec8d9', network: this.network }];
}

async getPricePerShare({
appToken,
contract,
multicall,
}: GetPricePerShareParams<LyraLiquidityToken, DefaultAppTokenDataProps, DefaultAppTokenDefinition>) {
const pool = await contract.liquidityPool();
const poolContract = this.contractFactory.lyraLiquidityPool({ address: pool, network: this.network });
const ratioRaw = await multicall.wrap(poolContract).getTokenPrice();
const ratio = Number(ratioRaw) / 10 ** appToken.tokens[0].decimals;
return [ratio];
}
export class OptimismLyraAvalonPoolTokenFetcher extends LyraAvalonPoolTokenFetcher {
subgraphUrl = 'https://api.lyra.finance/subgraph/optimism/v2/api';
registryContractAddress = '0x0fed189bcd4a680e05b153dc7c3dc87004e162fb';
underlyingContractAddress = '0x7f5c764cbc14f9669b88837ca1490cca17c31607';
}
Loading

0 comments on commit db16be2

Please sign in to comment.