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

publish to prod #1619

Merged
merged 5 commits into from
Feb 13, 2025
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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# backend

## 1.33.3

### Patch Changes

- c61054e: add sts rewards claimed
- 57e96d3: Bump balancer maths dependency to v0.0.22
- 9e83d67: autodiscovery for aave aprs
- 0600f58: apply underlying APR if any

## 1.33.2

### Patch Changes
Expand Down
10 changes: 10 additions & 0 deletions apps/api/gql/generated-schema-ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3106,6 +3106,11 @@ export const schema = gql`
"""
protocolFee24h: String!

"""
The total rewards claimed in the last 24 hours.
"""
rewardsClaimed24h: String!

"""
The current rebasing APR for stS.
"""
Expand Down Expand Up @@ -3151,6 +3156,11 @@ export const schema = gql`
"""
protocolFee24h: String!

"""
The total rewards claimed during that day.
"""
rewardsClaimed24h: String!

"""
The timestamp of the snapshot. Timestamp is end of day midnight.
"""
Expand Down
6 changes: 6 additions & 0 deletions apps/api/gql/generated-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2127,6 +2127,8 @@ export interface GqlStakedSonicData {
exchangeRate: Scalars['String'];
/** The total protocol fee collected in the last 24 hours. */
protocolFee24h: Scalars['String'];
/** The total rewards claimed in the last 24 hours. */
rewardsClaimed24h: Scalars['String'];
/** The current rebasing APR for stS. */
stakingApr: Scalars['String'];
/** Total amount of S in custody of stS. Delegated S plus pool S. */
Expand All @@ -2152,6 +2154,8 @@ export interface GqlStakedSonicSnapshot {
id: Scalars['ID'];
/** The total protocol fee collected during that day. */
protocolFee24h: Scalars['String'];
/** The total rewards claimed during that day. */
rewardsClaimed24h: Scalars['String'];
/** The timestamp of the snapshot. Timestamp is end of day midnight. */
timestamp: Scalars['Int'];
/** Total amount of S in custody of stS. Delegated S plus pool S. */
Expand Down Expand Up @@ -5106,6 +5110,7 @@ export type GqlStakedSonicDataResolvers<
delegatedValidators?: Resolver<Array<ResolversTypes['GqlStakedSonicDelegatedValidator']>, ParentType, ContextType>;
exchangeRate?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
protocolFee24h?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
rewardsClaimed24h?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
stakingApr?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
totalAssets?: Resolver<ResolversTypes['AmountHumanReadable'], ParentType, ContextType>;
totalAssetsDelegated?: Resolver<ResolversTypes['AmountHumanReadable'], ParentType, ContextType>;
Expand All @@ -5129,6 +5134,7 @@ export type GqlStakedSonicSnapshotResolvers<
exchangeRate?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
protocolFee24h?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
rewardsClaimed24h?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
timestamp?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
totalAssets?: Resolver<ResolversTypes['AmountHumanReadable'], ParentType, ContextType>;
totalAssetsDelegated?: Resolver<ResolversTypes['AmountHumanReadable'], ParentType, ContextType>;
Expand Down
8 changes: 8 additions & 0 deletions apps/api/gql/schema/sts.gql
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ type GqlStakedSonicData {
"""
protocolFee24h: String!
"""
The total rewards claimed in the last 24 hours.
"""
rewardsClaimed24h: String!
"""
Current exchange rate for stS -> S
"""
exchangeRate: String!
Expand Down Expand Up @@ -87,4 +91,8 @@ type GqlStakedSonicSnapshot {
The total protocol fee collected during that day.
"""
protocolFee24h: String!
"""
The total rewards claimed during that day.
"""
rewardsClaimed24h: String!
}
Binary file modified bun.lockb
Binary file not shown.
6 changes: 6 additions & 0 deletions modules/actions/sts/sync-staking-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ export async function syncStakingData(
const stakingData24hrsAgo = await subgraphService.getStakingData(block24HrsAgo);

let protocolFee24hrs = 0;
let rewardsClaimed24hrs = 0;
if (latestStakingData && stakingData24hrsAgo) {
protocolFee24hrs =
parseFloat(latestStakingData.totalProtocolFee) - parseFloat(stakingData24hrsAgo.totalProtocolFee);
rewardsClaimed24hrs =
parseFloat(latestStakingData.totalRewardsClaimed) - parseFloat(stakingData24hrsAgo.totalRewardsClaimed);
}

const sPrice = await prisma.prismaTokenCurrentPrice.findFirst({
Expand All @@ -41,6 +44,7 @@ export async function syncStakingData(
});

protocolFee24hrs = protocolFee24hrs * (sPrice?.price || 0);
rewardsClaimed24hrs = rewardsClaimed24hrs * (sPrice?.price || 0);

const response = await fetch(baseAprUrl);
const data = (await response.json()) as SonicApiResponse;
Expand All @@ -63,6 +67,7 @@ export async function syncStakingData(
exchangeRate: stakingDataOnchain.exchangeRate,
stakingApr: `${stakingApr}`,
protocolFee24h: `${protocolFee24hrs}`,
rewardsClaimed24h: `${rewardsClaimed24hrs}`,
},
update: {
id: stakingContractAddress,
Expand All @@ -72,6 +77,7 @@ export async function syncStakingData(
exchangeRate: stakingDataOnchain.exchangeRate,
stakingApr: `${stakingApr}`,
protocolFee24h: `${protocolFee24hrs}`,
rewardsClaimed24h: `${rewardsClaimed24hrs}`,
},
});

Expand Down
2 changes: 2 additions & 0 deletions modules/actions/sts/sync-staking-snapshots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export async function syncSonicStakingSnapshots(
});

const protocolFee24hrsUsd = parseFloat(snapshot.protocolFee24h) * (sPrice?.price || 0);
const rewardsClaimed24hUsd = parseFloat(snapshot.rewardsClaimed24h) * (sPrice?.price || 0);

const snapshotData = {
id: snapshot.id,
Expand All @@ -37,6 +38,7 @@ export async function syncSonicStakingSnapshots(
exchangeRate: snapshot.exchangeRate,
sonicStakingId: stakingContractAddress,
protocolFee24h: `${protocolFee24hrsUsd}`,
rewardsClaimed24h: `${rewardsClaimed24hUsd}`,
};
operations.push(
prisma.prismaSonicStakingDataSnapshot.upsert({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import { AprHandler } from '..';
import { Chain } from '@prisma/client';
import { getViemClient } from '../../../../../sources/viem-client';
import { parseAbi } from 'viem';
import { env } from '../../../../../../apps/env';
import { prisma } from '../../../../../../prisma/prisma-client';

/** Sets the config data used internally */
const config = {
ARBITRUM: {
subgraphURL: `https://gateway-arbitrum.network.thegraph.com/api/${env.THEGRAPH_API_KEY_BALANCER}/subgraphs/id/DLuE98kEb5pQNXAcKFQGQgfSQ57Xdou4jnVbAEqMfy3B`,
},
AVALANCHE: {
subgraphURL: `https://gateway-arbitrum.network.thegraph.com/api/${env.THEGRAPH_API_KEY_BALANCER}/subgraphs/id/2h9woxy8RTjHu1HJsCEnmzpPHFArU33avmUh4f71JpVn`,
},
BASE: {
subgraphURL: `https://gateway-arbitrum.network.thegraph.com/api/${env.THEGRAPH_API_KEY_BALANCER}/subgraphs/id/GQFbb95cE6d8mV989mL5figjaGaKCQB3xqYrr1bRyXqF`,
},
GNOSIS: {
subgraphURL: `https://gateway-arbitrum.network.thegraph.com/api/${env.THEGRAPH_API_KEY_BALANCER}/subgraphs/id/HtcDaL8L8iZ2KQNNS44EBVmLruzxuNAz1RkBYdui1QUT`,
},
MAINNET: {
subgraphURL: `https://gateway-arbitrum.network.thegraph.com/api/${env.THEGRAPH_API_KEY_BALANCER}/subgraphs/id/Cd2gEDVeqnjBn1hSeqFMitw8Q1iiyV9FYUZkLNRcL87g`,
},
OPTIMISM: {
subgraphURL: `https://gateway-arbitrum.network.thegraph.com/api/${env.THEGRAPH_API_KEY_BALANCER}/subgraphs/id/DSfLz8oQBUeU5atALgUFQKMTSYV9mZAVYp4noLSXAfvb`,
},
POLYGON: {
subgraphURL: `https://gateway-arbitrum.network.thegraph.com/api/${env.THEGRAPH_API_KEY_BALANCER}/subgraphs/id/Co2URyXjnxaw8WqxKyVHdirq9Ahhm5vcTs4dMedAq211`,
},
};

const query = `query getReserves($underlyingTokens: [Bytes!]) {
reserves(
where: {
underlyingAsset_in: $underlyingTokens
isActive: true
}
) {
id
aToken {
id
}
underlyingAsset
liquidityRate
}
}`;

/** Makes handler callable by chain */
export const chains = Object.keys(config) as Chain[];

export class Handler implements AprHandler {
async getAprs(chain: Chain) {
if (!chains.includes(chain)) {
return {};
}

// Get AAVE pools
const aavePools = await prisma.prismaPool.findMany({
where: {
chain,
OR: [
{
name: {
contains: 'aave',
mode: 'insensitive' as const,
},
},
{
tokens: {
some: {
token: {
name: {
contains: 'aave',
mode: 'insensitive' as const,
},
},
},
},
},
],
},
include: {
tokens: {
include: {
token: true,
},
},
},
});

const wrapperToUnderlying = aavePools
.map((pool) =>
pool.tokens
.filter(
(token) => token.token.name.toLowerCase().match('aave') && token.token.underlyingTokenAddress,
)
.map((token) => [token.address, token.token.underlyingTokenAddress!]),
)
.flat()
.filter((item, index, self) => self.findIndex((w) => w[0] === item[0]) === index);

// Get atokens
const client = getViemClient(chain);
const contracts = wrapperToUnderlying
.map(([wrapper]) => wrapper)
.map((wrapper) => ({
address: wrapper as `0x${string}`,
abi: parseAbi(['function aToken() returns (address)']),
functionName: 'aToken',
}));
const aTokens = await client.multicall({ contracts, allowFailure: false });
const wrappersToATokens = wrapperToUnderlying.map(([wrapper, underlying], index) => [
wrapper,
aTokens[index].toLowerCase(),
underlying,
]);

const requestQuery = {
operationName: 'getReserves',
query,
variables: {
underlyingTokens: wrapperToUnderlying.map(([_, underlying]) => underlying),
},
};

const response = await fetch(config[chain as keyof typeof config].subgraphURL, {
method: 'post',
body: JSON.stringify(requestQuery),
headers: { 'Content-Type': 'application/json' },
});

const data = await response.json();

const {
data: { reserves },
} = data as ReserveResponse;

// For each reserve, match the wrapper by aToken address
const aprsByUnderlyingAddress = Object.fromEntries(
reserves
.map((r) => [
wrappersToATokens.find(([_, aToken]) => aToken === r.aToken.id)?.[0].toLowerCase(),
// Converting from aave ray number (27 digits) to float
{ apr: Number(r.liquidityRate.slice(0, 27)) / 1e27, isIbYield: true },
])
.filter((r) => r[0]),
);

return aprsByUnderlyingAddress;
}
}

interface ReserveResponse {
data: {
reserves: [
{
underlyingAsset: string;
liquidityRate: string;
aToken: {
id: string;
};
},
];
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ export * from './ovix-apr-handler';
export * from './bloom-apr-handler';
export * as MakerGnosis from './maker-gnosis-apr-handler'; // Not used, not sure why it's not referenced anywhere ???
export * from './silo-apr-handler';
export * as AaveAuto from './aave-auto-apr-handler';
6 changes: 6 additions & 0 deletions modules/pool/lib/apr-data-sources/yb-tokens-apr.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ export class YbTokensAprService implements PoolAprService {
if (underlyingApr) {
userApr = ((1 + token.apr) * (1 + underlyingApr.apr) - 1) * token.share;
}
} else if (token.token.underlyingTokenAddress) {
// When underlying has yield
const underlyingApr = aprs.get(token.token.underlyingTokenAddress);
if (underlyingApr) {
userApr = ((1 + token.apr) * (1 + underlyingApr.apr) - 1) * token.share;
}
}

let fee = 0;
Expand Down
13 changes: 7 additions & 6 deletions modules/sor/sor-debug.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { initRequestScopedContext, setRequestScopedContextValue } from '../conte
import { chainIdToChain } from '../network/chain-id-to-chain';
import { PoolController } from '../controllers/pool-controller';
import { TokenController } from '../controllers/token-controller';
import { ContentController } from '../controllers/content-controller';
import { sorService } from './sor.service';

describe('sor debugging', () => {
Expand Down Expand Up @@ -40,7 +41,7 @@ describe('sor debugging', () => {

it('sor v3', async () => {
const useProtocolVersion = 3;
const chain = Chain.SEPOLIA;
const chain = Chain.ARBITRUM;

const chainId = Object.keys(chainIdToChain).find((key) => chainIdToChain[key] === chain) as string;
initRequestScopedContext();
Expand All @@ -50,20 +51,20 @@ describe('sor debugging', () => {
// await PoolController().syncHookData(chain);
// await TokenController().syncErc4626Tokens(chain);
// await TokenController().syncErc4626UnwrapRates(chain);
// await ContentController().syncErc4626Data();

// to update liquidity values, first update the token prices through a mutation
// yarn dev; yarn mutation 'tokenReloadTokenPrices(chains: [MAINNET])' 1
// await PoolController().updateLiquidityValuesForActivePools(chain);

const swaps = await sorService.getSorSwapPaths({
chain,
tokenIn: '0x94a9d9ac8a22534e3faca9f4e7f2e2cf85d5e4c8', // USDCaave
tokenOut: '0xaa8e23fb1079ea71e0a56f48a2aa51851d8433d0', // USDTaave
tokenIn: '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9', // USDT0
tokenOut: '0x7788a3538c5fc7f9c7c8a74eac4c898fc8d87d92', // sUSDx
swapType: 'EXACT_IN',
swapAmount: '10000',
swapAmount: '5',
useProtocolVersion,
considerPoolsWithHooks: true,
// poolIds: ['0x9b677c72a1160e1e03fe542bfd2b0f373fa94a8c'], // boosted
poolIds: ['0xc2b0d1a1b4cdda10185859b5a5c543024c2df869'], // boosted
});

console.log(swaps.returnAmount);
Expand Down
3 changes: 0 additions & 3 deletions modules/sources/github/pool-metadata-tags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,8 @@ export const getPoolMetadataTags = async (
}

if (tag.tokens) {
console.log('tag.tokens', tag.tokens);
for (const chainId in tag.tokens) {
console.log(chainId);
for (const tokenAddress of tag.tokens[chainId]) {
console.log(tokenAddress);
const chain = chainIdToChain[chainId];
const poolsWithToken = await prisma.prismaPool.findMany({
where: { chain: chain, allTokens: { some: { tokenAddress: tokenAddress.toLowerCase() } } },
Expand Down
Loading