Skip to content

Commit

Permalink
Feat: integrate morfi yield (#1614)
Browse files Browse the repository at this point in the history
  • Loading branch information
gWhy-j authored Nov 26, 2024
1 parent 5afa690 commit c2ed1ef
Show file tree
Hide file tree
Showing 6 changed files with 529 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/adaptors/morfi/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const coreUrl = 'https://subgraph.morfi.io/subgraphs/name/morfi/core';
const farmUrl = 'https://subgraph.morfi.io/subgraphs/name/morfi/farming';
const blocksUrl = 'https://subgraph.morfi.io/subgraphs/name/morfi/blocks';

module.exports = {
coreUrl,
farmUrl,
blocksUrl
}
108 changes: 108 additions & 0 deletions src/adaptors/morfi/farming.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
const { SubgraphService } = require('./subgraph');
const axios = require('axios');

const tickToSqrtPrice = (tick) => {
return Math.sqrt(Math.pow(1.0001, tick));
};

const getAmounts = (liquidity, tickLower, tickUpper, currentTick) => {
const currentPrice = tickToSqrtPrice(currentTick);
const lowerPrice = tickToSqrtPrice(tickLower);
const upperPrice = tickToSqrtPrice(tickUpper);

let amount0, amount1;
if (currentPrice < lowerPrice) {
amount1 = 0;
amount0 = liquidity * (1 / lowerPrice - 1 / upperPrice);
} else if (lowerPrice <= currentPrice && currentPrice <= upperPrice) {
amount1 = liquidity * (currentPrice - lowerPrice);
amount0 = liquidity * (1 / currentPrice - 1 / upperPrice);
} else {
amount1 = liquidity * (upperPrice - lowerPrice);
amount0 = 0;
}
return [amount0, amount1];
};

class FarmingService {
subgraphService;

constructor() {
this.subgraphService = new SubgraphService();
}

async update() {
await this.updateEternalFarmingsApr(2818);
}

async getEternalFarmingsApr() {
const farmings = await this.subgraphService.getEternalFarmingInfo();

return Promise.all(
farmings.map(async (farming) => {
const deposits =
await this.subgraphService.getPositionsInEternalFarming(
farming.id,
);
const positionIds = deposits.map((deposit) => deposit.id);

const token0Info = await this.subgraphService.getTokenInfoByAddress(
farming.rewardToken,
);

const positions =
await this.subgraphService.getPositionsByIds(positionIds);

const totalNativeAmount = positions.reduce((acc, position) => {
const [amount0, amount1] = getAmounts(
parseInt(position.liquidity, 10),
parseInt(position.tickLower.tickIdx, 10),
parseInt(position.tickUpper.tickIdx, 10),
parseInt(position.pool.tick, 10),
);
const amount0InEth =
(amount0 * parseFloat(position.pool.token0.derivedEth)) /
Math.pow(10, parseInt(position.pool.token0.decimals, 10));
const amount1InEth =
(amount1 * parseFloat(position.pool.token1.derivedEth)) /
Math.pow(10, parseInt(position.pool.token1.decimals, 10));
return acc + amount0InEth + amount1InEth;
}, 0);

let rewardPerSecond =
(parseInt(farming.rewardRate) *
parseFloat(token0Info.derivedEth)) /
Math.pow(10, parseInt(token0Info.decimals, 10));

if (farming.bonusRewardToken !== '0x0000000000000000000000000000000000000000') {
const token1Info =
await this.subgraphService.getTokenInfoByAddress(
farming.bonusRewardToken,
);
rewardPerSecond +=
(parseInt(farming.bonusRewardRate) *
parseFloat(token1Info.derivedEth)) /
Math.pow(10, parseInt(token1Info.decimals, 10));
}

const apr =
totalNativeAmount > 0
? (rewardPerSecond * 86400 * 365 * 100) / totalNativeAmount
: 0;

return {
pool: farming.pool,
apyReward: apr,
rewardTokens: [
farming.rewardToken,
...(farming.bonusRewardToken !== '0x0000000000000000000000000000000000000000' ? [farming.bonusRewardToken] : [])
]
}
}),
);
}
}

module.exports = {
FarmingService
}
155 changes: 155 additions & 0 deletions src/adaptors/morfi/gql-requests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
const { gql } = require('graphql-request');

const findToken = gql`
query findToken($address: String!) {
token(id: $address) {
derivedEth
decimals
}
}
`;

const getPositionsByIds = gql`
query getPositionsByIds($id_in: [ID!], $first: Int, $skip: Int) {
positions(where: { id_in: $id_in }, first: $first, skip: $skip) {
id
liquidity
tickLower {
tickIdx
}
tickUpper {
tickIdx
}
pool {
token0 {
name
decimals
derivedEth
}
token1 {
name
decimals
derivedEth
}
tick
}
}
}
`;

const getPositionsByPoolId = gql`
query getPositionsByPoolId($pool: String!) {
poolPositions(where: { pool: $pool }) {
lowerTick {
tickIdx
}
upperTick {
tickIdx
}
liquidity
pool {
id
token0Price
}
}
}
`;

const getPoolsByBlockNumber = gql`
query getPoolsByBlockNumber($blockNumber: Int!) {
pools(block: { number: $blockNumber }, first: 1000, orderBy: id) {
feesToken0
feesToken1
liquidity
id
token0 {
decimals
name
}
token1 {
decimals
name
}
token0Price
tick
}
}
`;

const getPools = gql`
query getPools {
pools(first: 1000, orderBy: id) {
feesToken0
feesToken1
liquidity
plugin
id
token0 {
decimals
name
symbol
}
token1 {
decimals
name
symbol
}
token0Price
tick
totalValueLockedUSD
}
}
`;

const findEternalFarmingInfos = gql`
query findEternalFarmingInfos {
eternalFarmings {
id
pool
rewardToken
bonusRewardToken
rewardRate
bonusRewardRate
}
}
`;

const getPositionsInEternalFarming = gql`
query getPositionsInEternalFarming(
$eternalFarming: String!
$first: Int
$skip: Int
) {
deposits(
where: { eternalFarming: $eternalFarming }
first: $first
skip: $skip
) {
id
}
}
`;

const getPreviousBlockNumber = gql`
query getPreviousBlockNumber($timestampLt: BigInt!, $timestampGt: BigInt!) {
blocks(
first: 1
orderBy: timestamp
orderDirection: desc
where: { timestamp_lt: $timestampLt, timestamp_gt: $timestampGt }
) {
number
}
}
`;

module.exports = {
findToken,
getPositionsByIds,
getPositionsByPoolId,
getPoolsByBlockNumber,
getPools,
findEternalFarmingInfos,
getPositionsInEternalFarming,
getPreviousBlockNumber
}
34 changes: 34 additions & 0 deletions src/adaptors/morfi/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const { FarmingService } = require('./farming');
const { PoolService } = require('./pool');

const main = async (timestamp = null) => {
const farmingService = new FarmingService();
const poolService = new PoolService();

const farmings = await farmingService.getEternalFarmingsApr();
const pools = await poolService.getPoolsApr();

// Merge APY data from farming and pool based on contract address
const mergedData = pools.map(poolData => {
const farmingData = farmings?.find(farming => farming.pool === poolData.pool);
return {
project: 'morfi',
chain: 'Morph',
pool: poolData.pool, // Add pool address
symbol: poolData.symbol, // Add pool symbol
tvlUsd: Number(poolData.tvlUsd), // Add TVL
url: poolData.url, // Add URL
apyBase: poolData.apyBase || 0,
apyReward: farmingData?.apyReward || 0,
...(farmingData?.rewardTokens?.length > 0 && { rewardTokens: farmingData.rewardTokens })
};
});

return mergedData;
}

module.exports = {
timetravel: false,
apy: main,
};

Loading

0 comments on commit c2ed1ef

Please sign in to comment.