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

Feature/sftmx #612

Merged
merged 22 commits into from
Dec 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
53 changes: 27 additions & 26 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
name: Checks

on:
- pull_request
- pull_request

jobs:
Build:
env:
BALANCER_SUBGRAPH: https://api.thegraph.com/subgraphs/name/beethovenxfi/beethovenx
MASTERCHEF_SUBGRAPH: https://api.thegraph.com/subgraphs/name/beethovenxfi/masterchefv2
BLOCKS_SUBGRAPH: https://api.thegraph.com/subgraphs/name/danielmkm/optimism-blocks
BEETS_BAR_SUBGRAPH: https://api.thegraph.com/subgraphs/name/beethovenxfi/beets-bar
USER_SNAPSHOT_SUBGRAPH: https://api.thegraph.com/subgraphs/name/danielmkm/user-balances-fantom
GAUGE_SUBGRAPH: https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-gauges-optimism
VEBALLOCKS_SUBGRAPH: https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-gauges
RELIQUARY_SUBGRAPH: https://api.thegraph.com/subgraphs/name/beethovenxfi/reliquary
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: '18.x'
- name: Install deps
run: yarn
- name: Generate Schema
run: yarn generate
- name: Prisma Generate
run: yarn prisma generate
- name: Run build
run: yarn build
Build:
env:
BALANCER_SUBGRAPH: https://api.thegraph.com/subgraphs/name/beethovenxfi/beethovenx
MASTERCHEF_SUBGRAPH: https://api.thegraph.com/subgraphs/name/beethovenxfi/masterchefv2
BLOCKS_SUBGRAPH: https://api.thegraph.com/subgraphs/name/danielmkm/optimism-blocks
BEETS_BAR_SUBGRAPH: https://api.thegraph.com/subgraphs/name/beethovenxfi/beets-bar
USER_SNAPSHOT_SUBGRAPH: https://api.thegraph.com/subgraphs/name/danielmkm/user-balances-fantom
GAUGE_SUBGRAPH: https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-gauges-optimism
VEBALLOCKS_SUBGRAPH: https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-gauges
RELIQUARY_SUBGRAPH: https://api.thegraph.com/subgraphs/name/beethovenxfi/reliquary
SFTMX_SUBGRAPH: 'https://api.thegraph.com/subgraphs/name/beethovenxfi/sftmx'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: '18.x'
- name: Install deps
run: yarn
- name: Generate Schema
run: yarn generate
- name: Prisma Generate
run: yarn prisma generate
- name: Run build
run: yarn build
1 change: 1 addition & 0 deletions buildspec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ env:
GAUGE_SUBGRAPH: 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-gauges-optimism'
USER_SNAPSHOT_SUBGRAPH: 'https://api.thegraph.com/subgraphs/name/beethovenxfi/user-bpt-balances-fantom'
VEBALLOCKS_SUBGRAPH: 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-gauges'
SFTMX_SUBGRAPH: 'https://api.thegraph.com/subgraphs/name/beethovenxfi/sftmx'
phases:
install:
runtime-versions:
Expand Down
18 changes: 18 additions & 0 deletions codegen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,24 @@ generates:
schema: ${RELIQUARY_SUBGRAPH}
plugins:
- schema-ast
modules/subgraphs/sftmx-subgraph/generated/sftmx-subgraph-types.ts:
schema: ${SFTMX_SUBGRAPH}
documents: 'modules/subgraphs/sftmx-subgraph/sftmx-subgraph-queries.graphql'
plugins:
- typescript
- typescript-operations
- typescript-graphql-request
config:
scalars:
BigInt: string
Bytes: string
BigDecimal: string
namingConvention:
enumValues: keep
modules/subgraphs/sftmx-subgraph/generated/sftmx-subgraph-schema.graphql:
schema: ${SFTMX_SUBGRAPH}
plugins:
- schema-ast
modules/subgraphs/blocks-subgraph/generated/blocks-subgraph-types.ts:
schema: ${BLOCKS_SUBGRAPH}
documents: 'modules/subgraphs/blocks-subgraph/block-subgraph-queries.graphql'
Expand Down
1 change: 1 addition & 0 deletions env.local
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ USER_SNAPSHOT_SUBGRAPH=https://api.thegraph.com/subgraphs/name/danielmkm/user-ba
GAUGE_SUBGRAPH=https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-gauges-optimism
VEBALLOCKS_SUBGRAPH=https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-gauges
RELIQUARY_SUBGRAPH=https://api.thegraph.com/subgraphs/name/beethovenxfi/reliquary
SFTMX_SUBGRAPH=https://api.thegraph.com/subgraphs/name/beethovenxfi/sftmx

# CRM
SANITY_API_TOKEN=<insert-me>
Expand Down
10 changes: 10 additions & 0 deletions modules/network/apr-config-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export interface IbAprConfig {
ankr?: AnkrAprConfig;
bloom?: BloomAprConfig;
beefy?: BeefyAprConfig;
sftmx?: SftmxAprConfig;
euler?: EulerAprConfig;
gearbox?: GearBoxAprConfig;
idle?: IdleAprConfig;
Expand Down Expand Up @@ -67,6 +68,15 @@ export interface BloomAprConfig {
};
}

export interface SftmxAprConfig {
tokens: {
[underlyingAssetName: string]: {
address: string;
ftmStakingAddress: string;
};
};
}

export interface EulerAprConfig {
subgraphUrl: string;
tokens: {
Expand Down
26 changes: 25 additions & 1 deletion modules/network/fantom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { env } from '../../app/env';
import { IbTokensAprService } from '../pool/lib/apr-data-sources/ib-tokens-apr.service';
import { BeetswarsGaugeVotingAprService } from '../pool/lib/apr-data-sources/fantom/beetswars-gauge-voting-apr';
import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service';
import { SftmxSubgraphService } from '../subgraphs/sftmx-subgraph/sftmx.service';

const fantomNetworkData: NetworkData = {
chain: {
Expand All @@ -45,6 +46,7 @@ const fantomNetworkData: NetworkData = {
masterchef: 'https://api.thegraph.com/subgraphs/name/beethovenxfi/masterchefv2',
reliquary: 'https://api.thegraph.com/subgraphs/name/beethovenxfi/reliquary',
userBalances: 'https://api.thegraph.com/subgraphs/name/beethovenxfi/user-bpt-balances-fantom',
sftmx: 'https://api.thegraph.com/subgraphs/name/beethovenxfi/sftmx',
},
eth: {
address: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
Expand Down Expand Up @@ -94,7 +96,8 @@ const fantomNetworkData: NetworkData = {
tokenPrices: {
maxHourlyPriceHistoryNumDays: 100,
},
rpcUrl: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'main'
rpcUrl:
(env.DEPLOYMENT_ENV as DeploymentEnv) === 'main'
? `https://rpc.ankr.com/fantom`
: `https://rpc.fantom.gateway.fm`,
rpcMaxBlockRange: 1000,
Expand All @@ -107,6 +110,10 @@ const fantomNetworkData: NetworkData = {
address: '0xf24bcf4d1e507740041c9cfd2dddb29585adce1e',
beetsPriceProviderRpcUrl: 'https://rpc.ftm.tools',
},
sftmx: {
stakingContractAddress: '0xb458bfc855ab504a8a327720fcef98886065529b',
sftmxAddress: '0xd7028092c830b5c8fce061af2e593413ebbc1fc1',
},
fbeets: {
address: '0xfcef8a994209d6916eb2c86cdd2afd60aa6f54b1',
farmId: '22',
Expand Down Expand Up @@ -171,6 +178,14 @@ const fantomNetworkData: NetworkData = {
},
},
},
// sftmx: {
// tokens: {
// sftmx: {
// address: '0xd7028092c830b5c8fce061af2e593413ebbc1fc1',
// ftmStakingAddress: '0xb458bfc855ab504a8a327720fcef98886065529b',
// },
// },
// },
reaper: {
subgraphSource: {
subgraphUrl: 'https://api.thegraph.com/subgraphs/name/byte-masons/multi-strategy-vaults-fantom',
Expand Down Expand Up @@ -333,6 +348,7 @@ export const fantomNetworkConfig: NetworkConfig = {
fantomNetworkData.subgraphs.balancer,
fantomNetworkData.chain.id,
),
sftmxSubgraphService: new SftmxSubgraphService(fantomNetworkData.subgraphs.sftmx!),
},
/*
For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3.
Expand Down Expand Up @@ -444,5 +460,13 @@ export const fantomNetworkConfig: NetworkConfig = {
name: 'feed-data-to-datastudio',
interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(5, 'minutes') : every(5, 'minutes'),
},
{
name: 'sync-sftmx-staking-data',
interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(60, 'minutes') : every(30, 'minutes'),
},
{
name: 'sync-sftmx-withdrawal-requests',
interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(30, 'minutes') : every(5, 'minutes'),
},
],
};
9 changes: 8 additions & 1 deletion modules/network/network-config-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import { TokenPriceHandler } from '../token/token-types';
import { BaseProvider } from '@ethersproject/providers';
import { GqlChain } from '../../schema';
import { ContentService } from '../content/content-types';
import { AaveAprConfig, IbAprConfig } from './apr-config-types';
import { IbAprConfig } from './apr-config-types';
import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service';
import { SftmxSubgraphService } from '../subgraphs/sftmx-subgraph/sftmx.service';

export interface NetworkConfig {
data: NetworkData;
Expand All @@ -23,6 +24,7 @@ export interface NetworkConfig {

interface NetworkServices {
balancerSubgraphService: BalancerSubgraphService;
sftmxSubgraphService?: SftmxSubgraphService;
}

export interface WorkerJob {
Expand Down Expand Up @@ -69,6 +71,7 @@ export interface NetworkData {
blocks: string;
masterchef?: string;
reliquary?: string;
sftmx?: string;
beetsBar?: string;
gauge?: string;
veBalLocks?: string;
Expand All @@ -89,6 +92,10 @@ export interface NetworkData {
poolId: string;
poolAddress: string;
};
sftmx?: {
stakingContractAddress: string;
sftmxAddress: string;
};
bal?: {
address: string;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const sourceToHandler = {
ankr: sources.AnkrAprHandler,
beefy: sources.BeefyAprHandler,
bloom: sources.BloomAprHandler,
sftmx: sources.SftmxAprHandler,
euler: sources.EulerAprHandler,
gearbox: sources.GearboxAprHandler,
idle: sources.IdleAprHandler,
Expand All @@ -18,7 +19,7 @@ const sourceToHandler = {
tranchess: sources.TranchessAprHandler,
yearn: sources.YearnAprHandler,
defaultHandlers: sources.DefaultAprHandler,
}
};

export class IbLinearAprHandlers {
private handlers: AprHandler[] = [];
Expand Down Expand Up @@ -49,23 +50,23 @@ export class IbLinearAprHandlers {

// Add handlers from self-configured sources
Object.values(sources as unknown as any[])
.filter((source): source is { chains: Chain[], Handler: AprHandlerConstructor } => 'chains' in source)
.filter((source): source is { chains: Chain[]; Handler: AprHandlerConstructor } => 'chains' in source)
.filter((source) => this.chain && source.chains.includes(this.chain))
.forEach((source) => {
handlers.push(new source.Handler());
});
});

return handlers;
}

async fetchAprsFromAllHandlers(): Promise<TokenApr[]> {
let aprs: TokenApr[] = this.fixedAprTokens
? Object.values(this.fixedAprTokens).map(({ address, apr, isIbYield, group }) => ({
apr,
address,
isIbYield: isIbYield ?? false,
group
}))
apr,
address,
isIbYield: isIbYield ?? false,
group,
}))
: [];

const results = await Promise.allSettled(this.handlers.map((handler) => handler.getAprs(this.chain)));
Expand All @@ -77,7 +78,7 @@ export class IbLinearAprHandlers {
apr,
address,
isIbYield,
group
group,
})),
);
} else {
Expand All @@ -99,9 +100,9 @@ export interface AprHandler {
[tokenAddress: string]: {
/** Defined as float, eg: 0.01 is 1% */
apr: number;
isIbYield: boolean
isIbYield: boolean;
group?: string;
}
};
}>;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './aave-apr-handler';
export * from './ankr-apr-handler';
export * from './sftmx-apr-handler';
export * from './default-apr-handler';
export * from './euler-apr-handler';
export * from './gearbox-apr-handler';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import * as Sentry from '@sentry/node';
import { AprHandler } from '../ib-linear-apr-handlers';
import { SftmxAprConfig } from '../../../../../network/apr-config-types';
import { BigNumber, ethers } from 'ethers';
import { getContractAt } from '../../../../../web3/contract';
import FTMStaking from '../../../../../sftmx/abi/FTMStaking.json';
import Vault from '../../../../../sftmx/abi/Vault.json';
import { formatFixed } from '@ethersproject/bignumber';

export class SftmxAprHandler implements AprHandler {
tokens: {
[underlyingAssetName: string]: {
address: string;
ftmStakingAddress: string;
};
};

constructor(config: SftmxAprConfig) {
this.tokens = config.tokens;
}

async getAprs(): Promise<{
[tokenAddress: string]: {
/** Defined as float, eg: 0.01 is 1% */
apr: number;
isIbYield: boolean;
group?: string;
};
}> {
const baseApr = 0.018;
const maxLockApr = 0.06;
const validatorFee = 0.15;
const sftmxFee = 0.1;
try {
const aprs: {
[tokenAddress: string]: {
apr: number;
isIbYield: boolean;
group?: string;
};
} = {};

for (const tokenAddress in this.tokens) {
const tokenDefinition = this.tokens[tokenAddress];
const ftmStakingContract = getContractAt(tokenDefinition.ftmStakingAddress, FTMStaking.abi);

const totalFtm = (await ftmStakingContract.totalFTMWorth()) as BigNumber;
const poolFtm = (await ftmStakingContract.getPoolBalance()) as BigNumber;
const maturedVaultCount = await ftmStakingContract.getMaturedVaultLength();

let maturedFtmAmount = BigNumber.from('0');

for (let i = 0; i < maturedVaultCount; i++) {
const vaultAddress = await ftmStakingContract.getMaturedVault(i);
const vaultContract = getContractAt(vaultAddress, Vault.abi);
const vaultAmount = await vaultContract.currentStakeValue();
maturedFtmAmount = maturedFtmAmount.add(vaultAmount);
}

const totalFtmNum = parseFloat(formatFixed(totalFtm.toString(), 18));
const poolFtmNum = parseFloat(formatFixed(poolFtm.toString(), 18));
const maturedFtmNum = parseFloat(formatFixed(maturedFtmAmount.toString(), 18));
const stakedFtmNum = totalFtmNum - poolFtmNum - maturedFtmNum;

const totalMaxLockApr =
(stakedFtmNum / totalFtmNum) * (maxLockApr * (1 - validatorFee)) * (1 - sftmxFee);
const totalBaseApr = (maturedFtmNum / totalFtmNum) * (baseApr * (1 - validatorFee)) * (1 - sftmxFee);

const totalSftmxApr = totalMaxLockApr + totalBaseApr;

aprs[tokenDefinition.address] = {
apr: totalSftmxApr,
isIbYield: true,
};
}
return aprs;
} catch (error) {
console.error('Failed to fetch sftmx APR:', error);
Sentry.captureException(`sftmx IB APR handler failed: ${error}`);
return {};
}
}
}
Loading