diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 08ae113..0000000 --- a/.eslintignore +++ /dev/null @@ -1,3 +0,0 @@ -build/ -generated/ -node_modules/ diff --git a/src/entities/exchangeRates.ts b/src/entities/exchangeRates.ts index b2246f7..5c92437 100644 --- a/src/entities/exchangeRates.ts +++ b/src/entities/exchangeRates.ts @@ -1,4 +1,4 @@ -import { ExchangeRateSnapshot, Network, UniswapPool } from '../../generated/schema' +import { ExchangeRateSnapshot, UniswapPool, ExchangeRate } from '../../generated/schema' import { Address, BigDecimal, BigInt, Bytes, ethereum } from '@graphprotocol/graph-ts' import { ASSETS_USD_PRICE_FEED, @@ -17,8 +17,9 @@ import { chunkedMulticall } from '../helpers/utils' import { isGnosisNetwork } from './network' const latestAnswerSelector = '0x50d25bcd' +const exchangeRateId = '0' -export function updateExchangeRates(network: Network, timestamp: BigInt): void { +export function updateExchangeRates(exchangeRate: ExchangeRate, timestamp: BigInt): void { if (NETWORK == 'chiado' || NETWORK == 'holesky') { return } @@ -119,17 +120,17 @@ export function updateExchangeRates(network: Network, timestamp: BigInt): void { const usdToKrwRate = krwToUsdRate.gt(zero) ? one.div(krwToUsdRate) : zero const usdToAudRate = audToUsdRate.gt(zero) ? one.div(audToUsdRate) : zero - network.assetsUsdRate = assetsUsdRate - network.swiseUsdRate = swiseUsdRate - network.usdToEurRate = usdToEurRate - network.usdToGbpRate = usdToGbpRate - network.usdToCnyRate = usdToCnyRate - network.usdToJpyRate = usdToJpyRate - network.usdToKrwRate = usdToKrwRate - network.usdToAudRate = usdToAudRate - network.daiUsdRate = daiUsdRate - network.usdcUsdRate = usdcUsdRate - network.save() + exchangeRate.assetsUsdRate = assetsUsdRate + exchangeRate.swiseUsdRate = swiseUsdRate + exchangeRate.usdToEurRate = usdToEurRate + exchangeRate.usdToGbpRate = usdToGbpRate + exchangeRate.usdToCnyRate = usdToCnyRate + exchangeRate.usdToJpyRate = usdToJpyRate + exchangeRate.usdToKrwRate = usdToKrwRate + exchangeRate.usdToAudRate = usdToAudRate + exchangeRate.daiUsdRate = daiUsdRate + exchangeRate.usdcUsdRate = usdcUsdRate + exchangeRate.save() const exchangeRateSnapshot = new ExchangeRateSnapshot(timestamp.toString()) exchangeRateSnapshot.timestamp = timestamp.toI64() @@ -145,3 +146,28 @@ export function updateExchangeRates(network: Network, timestamp: BigInt): void { exchangeRateSnapshot.usdToAudRate = usdToAudRate exchangeRateSnapshot.save() } + +export function createOrLoadExchangeRate(): ExchangeRate { + let exchangeRate = loadExchangeRate() + + if (exchangeRate === null) { + exchangeRate = new ExchangeRate(exchangeRateId) + exchangeRate.assetsUsdRate = BigDecimal.zero() + exchangeRate.swiseUsdRate = BigDecimal.zero() + exchangeRate.daiUsdRate = BigDecimal.zero() + exchangeRate.usdcUsdRate = BigDecimal.zero() + exchangeRate.usdToEurRate = BigDecimal.zero() + exchangeRate.usdToGbpRate = BigDecimal.zero() + exchangeRate.usdToCnyRate = BigDecimal.zero() + exchangeRate.usdToJpyRate = BigDecimal.zero() + exchangeRate.usdToKrwRate = BigDecimal.zero() + exchangeRate.usdToAudRate = BigDecimal.zero() + exchangeRate.save() + } + + return exchangeRate +} + +export function loadExchangeRate(): ExchangeRate | null { + return ExchangeRate.load(exchangeRateId) +} diff --git a/src/entities/merkleDistributor.ts b/src/entities/merkleDistributor.ts index cc738f1..3dca71c 100644 --- a/src/entities/merkleDistributor.ts +++ b/src/entities/merkleDistributor.ts @@ -4,6 +4,7 @@ import { Distributor, DistributorClaim, DistributorReward, + ExchangeRate, LeverageStrategyPosition, Network, OsToken, @@ -173,6 +174,7 @@ export function convertStringToDistributionType(distTypeString: string): Distrib export function updateDistributions( network: Network, + exchangeRate: ExchangeRate, osToken: OsToken, distributor: Distributor, currentTimestamp: BigInt, @@ -227,7 +229,7 @@ export function updateDistributions( // dist data is the pool address const uniPool = loadUniswapPool(Address.fromBytes(dist.data))! principalAssets = distributeToSwiseAssetUniPoolUsers( - network, + exchangeRate, uniPool, Address.fromBytes(dist.token), distributedAmount, @@ -237,7 +239,7 @@ export function updateDistributions( // dist data is the pool address const uniPool = loadUniswapPool(Address.fromBytes(dist.data))! principalAssets = distributeToOsTokenUsdcUniPoolUsers( - network, + exchangeRate, osToken, uniPool, Address.fromBytes(dist.token), @@ -252,8 +254,11 @@ export function updateDistributions( let distributedAssets: BigInt = BigInt.zero() if (dist.token.equals(OS_TOKEN)) { distributedAssets = convertOsTokenSharesToAssets(osToken, distributedAmount) - } else if (dist.token.equals(SWISE_TOKEN) && network.assetsUsdRate.gt(BigDecimal.zero())) { - distributedAssets = distributedAmount.toBigDecimal().times(network.swiseUsdRate).div(network.assetsUsdRate).digits + } else if (dist.token.equals(SWISE_TOKEN) && exchangeRate.assetsUsdRate.gt(BigDecimal.zero())) { + distributedAssets = distributedAmount + .toBigDecimal() + .times(exchangeRate.swiseUsdRate) + .div(exchangeRate.assetsUsdRate).digits } else { log.error('[MerkleDistributor] Unknown token={} price to update APY', [dist.token.toHex()]) } @@ -330,15 +335,15 @@ export function distributeToVaultUsers(vault: Vault, token: Address, totalReward } export function distributeToSwiseAssetUniPoolUsers( - network: Network, + exchangeRate: ExchangeRate, pool: UniswapPool, token: Address, totalReward: BigInt, ): BigInt { const swiseToken = SWISE_TOKEN const assetToken = Address.fromString(ASSET_TOKEN) - const swiseUsdRate = network.swiseUsdRate - const assetsUsdRate = network.assetsUsdRate + const swiseUsdRate = exchangeRate.swiseUsdRate + const assetsUsdRate = exchangeRate.assetsUsdRate if ( (pool.token0.notEqual(swiseToken) || pool.token1.notEqual(assetToken)) && (pool.token0.notEqual(assetToken) || pool.token1.notEqual(swiseToken)) @@ -391,7 +396,7 @@ export function distributeToSwiseAssetUniPoolUsers( } export function distributeToOsTokenUsdcUniPoolUsers( - network: Network, + exchangeRate: ExchangeRate, osToken: OsToken, pool: UniswapPool, token: Address, @@ -404,8 +409,8 @@ export function distributeToOsTokenUsdcUniPoolUsers( ) { assert(false, "Pool doesn't contain USDC and OsToken tokens") } - const assetsUsdRate = network.assetsUsdRate - const usdcUsdRate = network.usdcUsdRate + const assetsUsdRate = exchangeRate.assetsUsdRate + const usdcUsdRate = exchangeRate.usdcUsdRate if (assetsUsdRate.equals(BigDecimal.zero()) || usdcUsdRate.equals(BigDecimal.zero())) { assert(false, 'Missing USD rates for OsToken or USDC token') } diff --git a/src/entities/network.ts b/src/entities/network.ts index c3a7d34..ec5efa6 100644 --- a/src/entities/network.ts +++ b/src/entities/network.ts @@ -1,4 +1,4 @@ -import { Address, BigDecimal, BigInt, Bytes, store } from '@graphprotocol/graph-ts' +import { Address, BigInt, Bytes, store } from '@graphprotocol/graph-ts' import { Network, RewardSplitter, User, Vault } from '../../generated/schema' import { NETWORK, V2_REWARD_TOKEN, V2_STAKED_TOKEN } from '../helpers/constants' @@ -21,16 +21,6 @@ export function createOrLoadNetwork(): Network { network.osTokenVaultIds = [] network.oraclesConfigIpfsHash = '' network.lastSnapshotTimestamp = BigInt.zero() - network.assetsUsdRate = BigDecimal.zero() - network.swiseUsdRate = BigDecimal.zero() - network.daiUsdRate = BigDecimal.zero() - network.usdcUsdRate = BigDecimal.zero() - network.usdToEurRate = BigDecimal.zero() - network.usdToGbpRate = BigDecimal.zero() - network.usdToCnyRate = BigDecimal.zero() - network.usdToJpyRate = BigDecimal.zero() - network.usdToKrwRate = BigDecimal.zero() - network.usdToAudRate = BigDecimal.zero() network.save() } diff --git a/src/mappings/exchangeRates.ts b/src/mappings/exchangeRates.ts index 328a9b9..19c2b4e 100644 --- a/src/mappings/exchangeRates.ts +++ b/src/mappings/exchangeRates.ts @@ -1,24 +1,23 @@ import { ethereum, log } from '@graphprotocol/graph-ts' -import { loadNetwork } from '../entities/network' -import { updateExchangeRates } from '../entities/exchangeRates' +import { createOrLoadExchangeRate, updateExchangeRates } from '../entities/exchangeRates' export function handleExchangeRates(block: ethereum.Block): void { - const network = loadNetwork()! - updateExchangeRates(network, block.timestamp) + const exchangeRate = createOrLoadExchangeRate() + updateExchangeRates(exchangeRate, block.timestamp) log.info( '[ExchangeRates] assetsUsdRate={} usdToEurRate={} usdToGbpRate={} usdToCnyRate={} usdToJpyRate={} usdToKrwRate={} usdToAudRate={} daiUsdRate={} usdcUsdRate={} swiseUsdRate={} timestamp={}', [ - network.assetsUsdRate.toString(), - network.usdToEurRate.toString(), - network.usdToGbpRate.toString(), - network.usdToCnyRate.toString(), - network.usdToJpyRate.toString(), - network.usdToKrwRate.toString(), - network.usdToAudRate.toString(), - network.daiUsdRate.toString(), - network.usdcUsdRate.toString(), - network.swiseUsdRate.toString(), + exchangeRate.assetsUsdRate.toString(), + exchangeRate.usdToEurRate.toString(), + exchangeRate.usdToGbpRate.toString(), + exchangeRate.usdToCnyRate.toString(), + exchangeRate.usdToJpyRate.toString(), + exchangeRate.usdToKrwRate.toString(), + exchangeRate.usdToAudRate.toString(), + exchangeRate.daiUsdRate.toString(), + exchangeRate.usdcUsdRate.toString(), + exchangeRate.swiseUsdRate.toString(), block.timestamp.toString(), ], ) diff --git a/src/mappings/merkleDistributor.ts b/src/mappings/merkleDistributor.ts index 1d7cc3b..8a334c8 100644 --- a/src/mappings/merkleDistributor.ts +++ b/src/mappings/merkleDistributor.ts @@ -17,14 +17,14 @@ import { } from '../entities/merkleDistributor' import { createTransaction } from '../entities/transaction' import { OS_TOKEN } from '../helpers/constants' -import { loadNetwork } from '../entities/network' +import { loadExchangeRate } from '../entities/exchangeRates' const secondsInYear = '31536000' export function handlePeriodicDistributionAdded(event: PeriodicDistributionAdded): void { const token = event.params.token const extraData = event.params.extraData - const network = loadNetwork()! + const exchangeRate = loadExchangeRate()! const distType = getDistributionType(extraData) if (distType == DistributionType.UNKNOWN) { @@ -35,13 +35,13 @@ export function handlePeriodicDistributionAdded(event: PeriodicDistributionAdded return } else if ( distType == DistributionType.SWISE_ASSET_UNI_POOL && - (network.assetsUsdRate.equals(BigDecimal.zero()) || network.swiseUsdRate.equals(BigDecimal.zero())) + (exchangeRate.assetsUsdRate.equals(BigDecimal.zero()) || exchangeRate.swiseUsdRate.equals(BigDecimal.zero())) ) { log.error('[MerkleDistributor] Swise asset Uni pool distribution assetsUsdRate or swiseUsdRate is zero', []) return } else if ( distType == DistributionType.OS_TOKEN_USDC_UNI_POOL && - (network.assetsUsdRate.equals(BigDecimal.zero()) || network.usdcUsdRate.equals(BigDecimal.zero())) + (exchangeRate.assetsUsdRate.equals(BigDecimal.zero()) || exchangeRate.usdcUsdRate.equals(BigDecimal.zero())) ) { log.error('[MerkleDistributor] OsToken USDC Uni pool distribution assetsUsdRate or usdcUsdRate is zero', []) return diff --git a/src/mappings/periodicTasks.ts b/src/mappings/periodicTasks.ts index 2706d26..9eb7fb6 100644 --- a/src/mappings/periodicTasks.ts +++ b/src/mappings/periodicTasks.ts @@ -9,6 +9,7 @@ import { updateOsTokenExitRequests } from '../entities/osTokenVaultEscrow' import { loadOsTokenConfig } from '../entities/osTokenConfig' import { loadAave, updateAaveApys } from '../entities/aave' import { loadDistributor, updateDistributions } from '../entities/merkleDistributor' +import { loadExchangeRate } from '../entities/exchangeRates' const secondsInHour = 3600 const secondsInDay = 86400 @@ -17,8 +18,9 @@ export function handlePeriodicTasks(block: ethereum.Block): void { const timestamp = block.timestamp const blockNumber = block.number const network = loadNetwork() + const exchangeRate = loadExchangeRate() const aave = loadAave() - if (!network || !aave) { + if (!network || !exchangeRate || !aave) { return } @@ -41,7 +43,7 @@ export function handlePeriodicTasks(block: ethereum.Block): void { // update distributions // NB! if blocksInHour config is updated, the average apy calculation must be updated const distributor = loadDistributor()! - updateDistributions(network, osToken, distributor, timestamp) + updateDistributions(network, exchangeRate, osToken, distributor, timestamp) const vaultIds = network.vaultIds let vaultAddress: Address diff --git a/src/schema.graphql b/src/schema.graphql index c264601..50545fb 100644 --- a/src/schema.graphql +++ b/src/schema.graphql @@ -450,36 +450,6 @@ type Network @entity { "Total number of vaults" vaultsCount: Int! - "The USD rate of the assets (e.g. ETH, GNO)" - assetsUsdRate: BigDecimal! - - "The USD rate of the SWISE" - swiseUsdRate: BigDecimal! - - "The USD rate of DAI" - daiUsdRate: BigDecimal! - - "The USD rate of USDC" - usdcUsdRate: BigDecimal! - - "The USD to EUR rate" - usdToEurRate: BigDecimal! - - "The USD to GBP rate" - usdToGbpRate: BigDecimal! - - "The USD to CNY rate" - usdToCnyRate: BigDecimal! - - "The USD to JPY rate" - usdToJpyRate: BigDecimal! - - "The USD to KRW rate" - usdToKrwRate: BigDecimal! - - "The USD to AUD rate" - usdToAudRate: BigDecimal! - "The non repeated addresses of all the vaults" vaultIds: [String!]! @@ -886,6 +856,44 @@ type VaultStats @aggregation(intervals: ["day"], source: "VaultSnapshot") { apy: BigDecimal! @aggregate(fn: "last", arg: "apy") } +""" +The latest exchange rates +""" +type ExchangeRate @entity { + "Always 0" + id: ID! + + "The USD rate of the assets (e.g. ETH, GNO)" + assetsUsdRate: BigDecimal! + + "The USD rate of the SWISE" + swiseUsdRate: BigDecimal! + + "The USD rate of DAI" + daiUsdRate: BigDecimal! + + "The USD rate of USDC" + usdcUsdRate: BigDecimal! + + "The USD to EUR rate" + usdToEurRate: BigDecimal! + + "The USD to GBP rate" + usdToGbpRate: BigDecimal! + + "The USD to CNY rate" + usdToCnyRate: BigDecimal! + + "The USD to JPY rate" + usdToJpyRate: BigDecimal! + + "The USD to KRW rate" + usdToKrwRate: BigDecimal! + + "The USD to AUD rate" + usdToAudRate: BigDecimal! +} + """ The snapshot of the exchange rates """ diff --git a/src/subgraph.template.yaml b/src/subgraph.template.yaml index 76230a8..2536f2d 100644 --- a/src/subgraph.template.yaml +++ b/src/subgraph.template.yaml @@ -22,6 +22,7 @@ dataSources: file: ./mappings/periodicTasks.ts entities: - Network + - ExchangeRate - Aave - AavePosition - OsToken @@ -77,7 +78,9 @@ dataSources: language: wasm/assemblyscript file: ./mappings/exchangeRates.ts entities: - - Network + - ExchangeRate + - ExchangeRateSnapshot + - ExchangeRateStats abis: - name: PriceFeed file: ./abis/PriceFeed.json