From 9943d9269e0d99ccd46bb18110e8119db8756031 Mon Sep 17 00:00:00 2001 From: aalavandhann <6264334+aalavandhan@users.noreply.github.com> Date: Tue, 17 Jun 2025 09:07:19 -0400 Subject: [PATCH 1/4] spot subgraph updates --- .../exported-artifacts/FeePolicy.json | 582 ++++++++++++++++++ spot-subgraph/schema.graphql | 18 + spot-subgraph/src/data/perpetualTranche.ts | 2 + spot-subgraph/src/data/rolloverVault.ts | 78 ++- .../src/mappings/perpetualTranche.ts | 16 +- spot-subgraph/src/mappings/rolloverVault.ts | 70 ++- spot-subgraph/src/utils.ts | 14 + spot-subgraph/subgraph.template.yaml | 6 +- 8 files changed, 781 insertions(+), 5 deletions(-) create mode 100644 spot-contracts/exported-artifacts/FeePolicy.json diff --git a/spot-contracts/exported-artifacts/FeePolicy.json b/spot-contracts/exported-artifacts/FeePolicy.json new file mode 100644 index 00000000..14eabb3f --- /dev/null +++ b/spot-contracts/exported-artifacts/FeePolicy.json @@ -0,0 +1,582 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "InvalidFees", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPerc", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidRange", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "DECIMALS", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ONE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "perpTVL", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vaultTVL", + "type": "uint256" + } + ], + "internalType": "struct SystemTVL", + "name": "s", + "type": "tuple" + } + ], + "name": "computeDeviationRatio", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "drPre", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "drPost", + "type": "uint256" + } + ], + "name": "computeFeePerc", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "perpTVL", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vaultTVL", + "type": "uint256" + } + ], + "internalType": "struct SystemTVL", + "name": "s", + "type": "tuple" + } + ], + "name": "computeRebalanceAmount", + "outputs": [ + { + "internalType": "int256", + "name": "underlyingAmtIntoPerp", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "equilibriumDR", + "outputs": [ + { + "internalType": "uint256", + "name": "lower", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "upper", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "feeFnDRDown", + "outputs": [ + { + "internalType": "uint256", + "name": "x1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "x2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y2", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "feeFnDRUp", + "outputs": [ + { + "internalType": "uint256", + "name": "x1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "x2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y2", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "init", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "perpDebasementLag", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "perpDebasementPercLimits", + "outputs": [ + { + "internalType": "uint256", + "name": "lower", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "upper", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "perpEnrichmentLag", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "perpEnrichmentPercLimits", + "outputs": [ + { + "internalType": "uint256", + "name": "lower", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "upper", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolFeeCollector", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolSharePerc", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rebalanceFreqSec", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "targetSystemRatio", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "lower", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "upper", + "type": "uint256" + } + ], + "internalType": "struct Range", + "name": "equilibriumDR_", + "type": "tuple" + } + ], + "name": "updateEquilibriumDR", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "x1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "x2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y2", + "type": "uint256" + } + ], + "internalType": "struct Line", + "name": "feeFnDRDown_", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "x2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y2", + "type": "uint256" + } + ], + "internalType": "struct Line", + "name": "feeFnDRUp_", + "type": "tuple" + } + ], + "name": "updateFees", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "protocolSharePerc_", + "type": "uint256" + }, + { + "internalType": "address", + "name": "protocolFeeCollector_", + "type": "address" + } + ], + "name": "updateProtocolFeeConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "perpDebasementLag_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "perpEnrichmentLag_", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "lower", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "upper", + "type": "uint256" + } + ], + "internalType": "struct Range", + "name": "perpDebasementPercLimits_", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "lower", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "upper", + "type": "uint256" + } + ], + "internalType": "struct Range", + "name": "perpEnrichmentPercLimits_", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "rebalanceFreqSec_", + "type": "uint256" + } + ], + "name": "updateRebalanceConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "targetSystemRatio_", + "type": "uint256" + } + ], + "name": "updateTargetSystemRatio", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/spot-subgraph/schema.graphql b/spot-subgraph/schema.graphql index c37a2229..e0b27d17 100644 --- a/spot-subgraph/schema.graphql +++ b/spot-subgraph/schema.graphql @@ -252,6 +252,12 @@ type PerpetualTrancheDailyStat @entity { " the perp token supply on the given day " totalSupply: BigDecimal! + + " the total amount of perp tokens paid as fees " + totalPerpFeeAmt: BigDecimal! + + " the total underlying value of fees " + totalUnderlyingFeeValue: BigDecimal! } type RolloverVault @entity { @@ -288,6 +294,12 @@ type RolloverVault @entity { " the price of each token " price: BigDecimal! + " the target system ratio " + targetSystemRatio: BigDecimal! + + " the system deviation ratio " + deviationRatio: BigDecimal! + " reference to the daily stats " dailyStats: [RolloverVaultDailyStat!]! @derivedFrom(field: "vault") } @@ -361,4 +373,10 @@ type RolloverVaultDailyStat @entity { " the total value of perp to underlying swaps on the given day " totalPerpToUnderlyingSwapValue: BigDecimal! + + " the system deviation ratio " + deviationRatio: BigDecimal! + + " the total underlying value of fees " + totalUnderlyingFeeValue: BigDecimal! } \ No newline at end of file diff --git a/spot-subgraph/src/data/perpetualTranche.ts b/spot-subgraph/src/data/perpetualTranche.ts index dd0c5a28..c052c993 100644 --- a/spot-subgraph/src/data/perpetualTranche.ts +++ b/spot-subgraph/src/data/perpetualTranche.ts @@ -179,6 +179,8 @@ export function fetchPerpetualTrancheDailyStat( dailyStat.tvl = BIGDECIMAL_ZERO dailyStat.price = BIGDECIMAL_ZERO dailyStat.totalSupply = BIGDECIMAL_ZERO + dailyStat.totalPerpFeeAmt = BIGDECIMAL_ZERO + dailyStat.totalUnderlyingFeeValue = BIGDECIMAL_ZERO } return dailyStat as PerpetualTrancheDailyStat } diff --git a/spot-subgraph/src/data/rolloverVault.ts b/spot-subgraph/src/data/rolloverVault.ts index 47575d5e..b8b0b2ee 100644 --- a/spot-subgraph/src/data/rolloverVault.ts +++ b/spot-subgraph/src/data/rolloverVault.ts @@ -1,4 +1,4 @@ -import { BigDecimal, BigInt, Address, DataSourceContext } from '@graphprotocol/graph-ts' +import { log, BigDecimal, BigInt, Address, DataSourceContext } from '@graphprotocol/graph-ts' import { RolloverVault, RolloverVaultAsset, @@ -7,6 +7,7 @@ import { Tranche, } from '../../generated/schema' import { RolloverVault as RolloverVaultABI } from '../../generated/RolloverVault/RolloverVault' +import { FeePolicy as FeePolicyABI } from '../../generated/RolloverVault/FeePolicy' import { ERC20 as ERC20ABI } from '../../generated/BondFactory/ERC20' import { RebasingToken as RebasingTokenTemplate } from '../../generated/templates' import { @@ -16,6 +17,7 @@ import { BIGDECIMAL_ONE, stringToAddress, formatBalance, + formatDecimalBalance, getTrancheCDRInfo, } from '../utils' import { fetchPerpetualTranche } from './perpetualTranche' @@ -96,6 +98,8 @@ export function refreshRolloverVaultTVL(vault: RolloverVault): void { if (vaultToken.totalSupply.gt(BIGDECIMAL_ZERO)) { vault.price = vault.tvl.div(vaultToken.totalSupply) } + vault.targetSystemRatio = fetchTargetSystemRatio(vaultAddress) + vault.deviationRatio = fetchDeviationRatio(perpAddress, vaultAddress, vault.targetSystemRatio) vault.save() } @@ -157,6 +161,7 @@ export function refreshRolloverVaultDailyStat(dailyStat: RolloverVaultDailyStat) dailyStat.rebaseMultiplier = vault.rebaseMultiplier dailyStat.price = vault.price dailyStat.totalSupply = vaultToken.totalSupply + dailyStat.deviationRatio = vault.deviationRatio dailyStat.save() } @@ -173,6 +178,8 @@ export function fetchRolloverVault(address: Address): RolloverVault { vault.tvl = BIGDECIMAL_ZERO vault.rebaseMultiplier = BIGDECIMAL_ONE vault.price = BIGDECIMAL_ZERO + vault.targetSystemRatio = BIGDECIMAL_ZERO + vault.deviationRatio = BIGDECIMAL_ZERO refreshRolloverVaultStore(vault as RolloverVault) let underlyingContext = new DataSourceContext() @@ -248,6 +255,75 @@ export function fetchRolloverVaultDailyStat( dailyStat.totalSwapValue = BIGDECIMAL_ZERO dailyStat.totalUnderlyingToPerpSwapValue = BIGDECIMAL_ZERO dailyStat.totalPerpToUnderlyingSwapValue = BIGDECIMAL_ZERO + dailyStat.deviationRatio = BIGDECIMAL_ZERO + dailyStat.totalUnderlyingFeeValue = BIGDECIMAL_ZERO } return dailyStat as RolloverVaultDailyStat } + +function fetchDeviationRatio( + perpAddress: Address, + vaultAddress: Address, + targetSystemRatio: BigDecimal, +): BigDecimal { + let perp = fetchPerpetualTranche(perpAddress) + let vault = fetchRolloverVault(vaultAddress) + return calcDeviationRatio(perp.tvl, vault.tvl, targetSystemRatio) +} + +function fetchTargetSystemRatio(vaultAddress: Address): BigDecimal { + let SYSTEM_RATIO_START = BigDecimal.fromString('3') + let vaultContract = RolloverVaultABI.bind(vaultAddress) + let r1 = vaultContract.try_feePolicy() + if (r1.reverted) { + log.debug('fee policy not set', []) + return SYSTEM_RATIO_START + } + let feePolicyContract = FeePolicyABI.bind(r1.value) + let r2 = feePolicyContract.try_targetSystemRatio() + if (r2.reverted) { + log.debug('fee policy version incorrect', []) + return SYSTEM_RATIO_START + } + return formatBalance(r2.value, BigInt.fromI32(feePolicyContract.decimals())) +} + +function calcDeviationRatio( + perpTVL: BigDecimal, + vaultTVL: BigDecimal, + targetSystemRatio: BigDecimal, +): BigDecimal { + return vaultTVL.div(perpTVL).div(targetSystemRatio) +} + +export function computeFeePerc( + perpTVLPre: BigDecimal, + vaultTVLPre: BigDecimal, + perpTVLPost: BigDecimal, + vaultTVLPost: BigDecimal, + targetSystemRatio: BigDecimal, + vaultAddress: Address, +): BigDecimal { + let vaultContract = RolloverVaultABI.bind(vaultAddress) + let r1 = vaultContract.try_feePolicy() + if (r1.reverted) { + log.debug('fee policy not set', []) + return BIGDECIMAL_ZERO + } + let feePolicyContract = FeePolicyABI.bind(r1.value) + let feePolicyDecimals = BigInt.fromI32(feePolicyContract.decimals()) + let drPre = formatDecimalBalance( + calcDeviationRatio(perpTVLPre, vaultTVLPre, targetSystemRatio), + feePolicyDecimals, + ) + let drPost = formatDecimalBalance( + calcDeviationRatio(perpTVLPost, vaultTVLPost, targetSystemRatio), + feePolicyDecimals, + ) + let r2 = feePolicyContract.try_computeFeePerc(drPre, drPost) + if (r2.reverted) { + log.debug('fee policy version incorrect', []) + return BIGDECIMAL_ZERO + } + return formatBalance(r2.value, feePolicyDecimals) +} diff --git a/spot-subgraph/src/mappings/perpetualTranche.ts b/spot-subgraph/src/mappings/perpetualTranche.ts index a0e01020..3197a1fa 100644 --- a/spot-subgraph/src/mappings/perpetualTranche.ts +++ b/spot-subgraph/src/mappings/perpetualTranche.ts @@ -56,8 +56,9 @@ export function handleUpdatedDepositBond(event: UpdatedDepositBond): void { perp.save() } -export function handleMint(event: Transfer): void { +export function handleTransfer(event: Transfer): void { let from = event.params.from + let to = event.params.to if (from == ADDRESS_ZERO) { log.debug('triggered mint', []) let perpToken = fetchToken(event.address) @@ -72,6 +73,19 @@ export function handleMint(event: Transfer): void { dailyStat.totalMintValue = dailyStat.totalMintValue.plus(perpAmtMinted.times(perp.price)) dailyStat.save() } + + // Mint and burn fees are handled by sending perp tokens to the perp contract + if (to == event.address) { + let perpToken = fetchToken(event.address) + let perp = fetchPerpetualTranche(event.address) + let perpFeeAmt = formatBalance(event.params.value, perpToken.decimals) + let dailyStat = fetchPerpetualTrancheDailyStat(perp, dayTimestamp(event.block.timestamp)) + dailyStat.totalPerpFeeAmt = dailyStat.totalPerpFeeAmt.plus(perpFeeAmt) + dailyStat.totalUnderlyingFeeValue = dailyStat.totalUnderlyingFeeValue.plus( + perpFeeAmt.times(perp.price), + ) + dailyStat.save() + } } export function handleRedeem(call: RedeemCall): void { diff --git a/spot-subgraph/src/mappings/rolloverVault.ts b/spot-subgraph/src/mappings/rolloverVault.ts index 0aad7c70..524d7e62 100644 --- a/spot-subgraph/src/mappings/rolloverVault.ts +++ b/spot-subgraph/src/mappings/rolloverVault.ts @@ -16,6 +16,7 @@ import { refreshRolloverVaultDailyStat, fetchRolloverVaultAsset, fetchRolloverVaultDailyStat, + computeFeePerc, } from '../data/rolloverVault' import { fetchToken, refreshSupply } from '../data/token' import { @@ -30,11 +31,15 @@ import { export function handleDeposit(call: DepositCall): void { log.debug('triggered deposit', []) let vault = fetchRolloverVault(call.to) - let vaultToken = fetchToken(stringToAddress(vault.token)) + let vaultAddress = stringToAddress(vault.token) + let vaultToken = fetchToken(vaultAddress) refreshSupply(vaultToken) refreshRolloverVaultTVL(vault) refreshRolloverVaultRebaseMultiplier(vault) + let perp = fetchPerpetualTranche(stringToAddress(vault.perp)) + refreshPerpetualTrancheTVL(perp) + let underlyingToken = fetchToken(stringToAddress(vault.underlying)) refreshSupply(underlyingToken) let underlyingTokenSupply = underlyingToken.totalSupply @@ -63,17 +68,34 @@ export function handleDeposit(call: DepositCall): void { let dailyStat = fetchRolloverVaultDailyStat(vault, dayTimestamp(call.block.timestamp)) dailyStat.totalMints = dailyStat.totalMints.plus(underlyingAmtIn.div(vault.price)) dailyStat.totalMintValue = dailyStat.totalMintValue.plus(underlyingAmtIn) + + let feePerc = computeFeePerc( + perp.tvl, + vault.tvl.minus(underlyingAmtIn), + perp.tvl, + vault.tvl, + vault.targetSystemRatio, + vaultAddress, + ) + dailyStat.totalUnderlyingFeeValue = dailyStat.totalUnderlyingFeeValue.plus( + underlyingAmtIn.times(feePerc), + ) + dailyStat.save() } export function handleRedeem(call: RedeemCall): void { log.debug('triggered redeem', []) let vault = fetchRolloverVault(call.to) - let vaultToken = fetchToken(stringToAddress(vault.token)) + let vaultAddress = stringToAddress(vault.token) + let vaultToken = fetchToken(vaultAddress) refreshSupply(vaultToken) refreshRolloverVaultTVL(vault) refreshRolloverVaultRebaseMultiplier(vault) + let perp = fetchPerpetualTranche(stringToAddress(vault.perp)) + refreshPerpetualTrancheTVL(perp) + let notesOut = formatBalance(call.inputs.notes, vaultToken.decimals) let scaledAmountOut = vault.totalScaledUnderlyingDeposited .times(notesOut) @@ -95,6 +117,19 @@ export function handleRedeem(call: RedeemCall): void { let dailyStat = fetchRolloverVaultDailyStat(vault, dayTimestamp(call.block.timestamp)) dailyStat.totalRedemptions = dailyStat.totalRedemptions.plus(notesOut) dailyStat.totalRedemptionValue = dailyStat.totalRedemptionValue.plus(notesOut.times(vault.price)) + + let feePerc = computeFeePerc( + perp.tvl, + vault.tvl.times(notesOut + vaultToken.totalSupply).div(vaultToken.totalSupply), + perp.tvl, + vault.tvl, + vault.targetSystemRatio, + vaultAddress, + ) + dailyStat.totalUnderlyingFeeValue = dailyStat.totalUnderlyingFeeValue.plus( + notesOut.times(vault.price).times(feePerc), + ) + dailyStat.save() } @@ -127,9 +162,13 @@ export function handleUnderlyingToPerpSwap(call: SwapUnderlyingForPerpsCall): vo log.debug('triggered UnderlyingToPerpSwap', []) let vault = fetchRolloverVault(call.to) + let vaultAddress = stringToAddress(vault.token) refreshRolloverVaultTVL(vault) refreshRolloverVaultRebaseMultiplier(vault) + let perp = fetchPerpetualTranche(stringToAddress(vault.perp)) + refreshPerpetualTrancheTVL(perp) + let underlyingToken = fetchToken(stringToAddress(vault.underlying)) let underlyingAmtIn = formatBalance(call.inputs.underlyingAmtIn, underlyingToken.decimals) @@ -138,6 +177,19 @@ export function handleUnderlyingToPerpSwap(call: SwapUnderlyingForPerpsCall): vo dailyStat.totalUnderlyingToPerpSwapValue = dailyStat.totalUnderlyingToPerpSwapValue.plus( underlyingAmtIn, ) + + let feePerc = computeFeePerc( + perp.tvl.minus(underlyingAmtIn), + vault.tvl, + perp.tvl, + vault.tvl, + vault.targetSystemRatio, + vaultAddress, + ) + dailyStat.totalUnderlyingFeeValue = dailyStat.totalUnderlyingFeeValue.plus( + underlyingAmtIn.times(feePerc), + ) + dailyStat.save() } @@ -145,6 +197,7 @@ export function handlePerpToUnderlyingSwap(call: SwapPerpsForUnderlyingCall): vo log.debug('triggered PerpToUnderlyingSwap', []) let vault = fetchRolloverVault(call.to) + let vaultAddress = stringToAddress(vault.token) refreshRolloverVaultTVL(vault) refreshRolloverVaultRebaseMultiplier(vault) @@ -159,5 +212,18 @@ export function handlePerpToUnderlyingSwap(call: SwapPerpsForUnderlyingCall): vo dailyStat.totalPerpToUnderlyingSwapValue = dailyStat.totalPerpToUnderlyingSwapValue.plus( perpAmtIn.times(perp.price), ) + + let feePerc = computeFeePerc( + perp.tvl.plus(perpAmtIn.times(perp.price)), + vault.tvl, + perp.tvl, + vault.tvl, + vault.targetSystemRatio, + vaultAddress, + ) + dailyStat.totalUnderlyingFeeValue = dailyStat.totalUnderlyingFeeValue.plus( + perpAmtIn.times(perp.price).times(feePerc), + ) + dailyStat.save() } diff --git a/spot-subgraph/src/utils.ts b/spot-subgraph/src/utils.ts index f44a0e57..5b456d48 100644 --- a/spot-subgraph/src/utils.ts +++ b/spot-subgraph/src/utils.ts @@ -9,6 +9,20 @@ export let BIGDECIMAL_ZERO = new BigDecimal(BIGINT_ZERO) export let BIGDECIMAL_ONE = new BigDecimal(BIGINT_ONE) export let ADDRESS_ZERO = Address.fromString('0x0000000000000000000000000000000000000000') +export const formatDecimalBalance = (wei: BigDecimal, decimals: BigInt): BigInt => { + return toBigInt( + wei.div( + BigInt.fromI32(10) + .pow(decimals.toI32() as u8) + .toBigDecimal(), + ), + ) +} + +function toBigInt(n: BigDecimal): BigInt { + return BigInt.fromString(n.toString().split('.')[0]) +} + export const formatBalance = (wei: BigInt, decimals: BigInt): BigDecimal => { return wei.toBigDecimal().div( BigInt.fromI32(10) diff --git a/spot-subgraph/subgraph.template.yaml b/spot-subgraph/subgraph.template.yaml index 33e8c8d4..873c6928 100644 --- a/spot-subgraph/subgraph.template.yaml +++ b/spot-subgraph/subgraph.template.yaml @@ -140,13 +140,15 @@ dataSources: file: ../spot-contracts/external-artifacts/ERC20ABI.json - name: RebasingERC20 file: ../spot-contracts/external-artifacts/RebasingERC20ABI.json + - name: FeePolicy + file: ../spot-contracts/exported-artifacts/FeePolicy.json eventHandlers: - event: ReserveSynced(address,uint256) handler: handleReserveSynced - event: UpdatedDepositBond(address) handler: handleUpdatedDepositBond - event: Transfer(indexed address,indexed address,uint256) - handler: handleMint + handler: handleTransfer callHandlers: - function: redeem(uint256) handler: handleRedeem @@ -178,6 +180,8 @@ dataSources: file: ../spot-contracts/external-artifacts/ERC20ABI.json - name: RebasingERC20 file: ../spot-contracts/external-artifacts/RebasingERC20ABI.json + - name: FeePolicy + file: ../spot-contracts/exported-artifacts/FeePolicy.json eventHandlers: - event: AssetSynced(address,uint256) handler: handleAssetSynced From 77b26a73e539ad58f66f4bcc2086c434a1ca1cdd Mon Sep 17 00:00:00 2001 From: aalavandhann <6264334+aalavandhan@users.noreply.github.com> Date: Wed, 18 Jun 2025 08:54:40 -0400 Subject: [PATCH 2/4] updated bb subgraph --- spot-staking-subgraph/schema.graphql | 10 +- spot-staking-subgraph/src/billBroker.ts | 143 ++++++++++++++++++++---- spot-staking-subgraph/src/charmVault.ts | 23 +++- spot-staking-subgraph/src/utils.ts | 2 +- 4 files changed, 143 insertions(+), 35 deletions(-) diff --git a/spot-staking-subgraph/schema.graphql b/spot-staking-subgraph/schema.graphql index 61e1d6f3..13f11b4b 100644 --- a/spot-staking-subgraph/schema.graphql +++ b/spot-staking-subgraph/schema.graphql @@ -34,10 +34,9 @@ type BillBrokerDailyStat @entity { perpPrice: BigDecimal! usdPrice: BigDecimal! totalSupply: BigDecimal! - usdSwapAmt: BigDecimal! - perpSwapAmt: BigDecimal! - usdFeeAmt: BigDecimal! - perpFeeAmt: BigDecimal! + swapValue: BigDecimal! + feeValue: BigDecimal! + feeYield: BigDecimal! tvl:BigDecimal! price:BigDecimal! } @@ -50,7 +49,8 @@ type BillBrokerSwap @entity { nonce: BigInt! type: String! swapAmt: BigDecimal! - feeAmt: BigDecimal! + swapValue: BigDecimal! + feeValue: BigDecimal! tx: String! } diff --git a/spot-staking-subgraph/src/billBroker.ts b/spot-staking-subgraph/src/billBroker.ts index c43664be..64d0fbc9 100644 --- a/spot-staking-subgraph/src/billBroker.ts +++ b/spot-staking-subgraph/src/billBroker.ts @@ -18,6 +18,8 @@ import { import { BillBroker__computePerpToUSDSwapAmt1InputSStruct, BillBroker__computeUSDToPerpSwapAmtInputSStruct, + BillBroker__computeMintAmtWithUSD1InputSStruct, + BillBroker__computeMintAmtWithPerp1InputSStruct, } from '../generated/BillBroker/BillBroker' import { BillBroker as BillBrokerABI } from '../generated/BillBroker/BillBroker' import { ERC20 as ERC20ABI } from '../generated/BillBroker/ERC20' @@ -74,7 +76,10 @@ export function fetchBillBroker(address: Address): BillBroker { return vault as BillBroker } -export function fetchBillBrokerDailyStat(vault: BillBroker, timestamp: BigInt): BillBrokerDailyStat { +export function fetchBillBrokerDailyStat( + vault: BillBroker, + timestamp: BigInt, +): BillBrokerDailyStat { let id = vault.id.concat('-').concat(timestamp.toString()) let dailyStat = BillBrokerDailyStat.load(id) if (dailyStat === null) { @@ -86,10 +91,9 @@ export function fetchBillBrokerDailyStat(vault: BillBroker, timestamp: BigInt): dailyStat.perpPrice = BIGDECIMAL_ZERO dailyStat.usdPrice = BIGDECIMAL_ZERO dailyStat.totalSupply = BIGDECIMAL_ZERO - dailyStat.usdSwapAmt = BIGDECIMAL_ZERO - dailyStat.perpSwapAmt = BIGDECIMAL_ZERO - dailyStat.usdFeeAmt = BIGDECIMAL_ZERO - dailyStat.perpFeeAmt = BIGDECIMAL_ZERO + dailyStat.swapValue = BIGDECIMAL_ZERO + dailyStat.feeValue = BIGDECIMAL_ZERO + dailyStat.feeYield = BIGDECIMAL_ZERO dailyStat.tvl = BIGDECIMAL_ZERO dailyStat.price = BIGDECIMAL_ZERO dailyStat.save() @@ -106,7 +110,8 @@ function fetchBillBrokerSwap(vault: BillBroker, nonce: BigInt): BillBrokerSwap { swap.nonce = nonce swap.type = '' swap.swapAmt = BIGDECIMAL_ZERO - swap.feeAmt = BIGDECIMAL_ZERO + swap.swapValue = BIGDECIMAL_ZERO + swap.feeValue = BIGDECIMAL_ZERO swap.tx = '0x' swap.timestamp = BIGINT_ZERO swap.save() @@ -152,6 +157,7 @@ export function handleSwapPerpsForUSD(event: SwapPerpsForUSD): void { vault.usdPrice = formatBalance(event.params.preOpState.usdPrice, vault.decimals) vault.tvl = vault.usdBal.times(vault.usdPrice).plus(vault.perpBal.times(vault.perpPrice)) vault.price = vault.tvl.div(vault.totalSupply) + vault.swapNonce = vault.swapNonce.plus(BIGINT_ONE) vault.save() dailyStat.perpPrice = vault.perpPrice @@ -162,6 +168,7 @@ export function handleSwapPerpsForUSD(event: SwapPerpsForUSD): void { swap.type = 'perps' swap.swapAmt = formatBalance(event.params.perpAmtIn, vault.perpDecimals) + swap.swapValue = swap.swapAmt.times(vault.perpPrice) swap.tx = event.transaction.hash.toHex() swap.timestamp = event.block.timestamp swap.save() @@ -180,15 +187,16 @@ export function handleSwapPerpsForUSD(event: SwapPerpsForUSD): void { let r = vaultContract.try_computePerpToUSDSwapAmt1(event.params.perpAmtIn, reserveStateStruct) if (!r.reverted) { let swapAmts = r.value - dailyStat.perpSwapAmt = dailyStat.perpSwapAmt.plus( - formatBalance(event.params.perpAmtIn, vault.perpDecimals), - ) - dailyStat.usdFeeAmt = dailyStat.usdFeeAmt.plus( - formatBalance(swapAmts.value1, vault.usdDecimals), - ) + let perpAmtIn = swap.swapAmt + let usdAmtOut = formatBalance(swapAmts.value0, vault.usdDecimals) + let estUsdAmtOut = perpAmtIn.times(vault.perpPrice).div(vault.usdPrice) + let usdFeeAmt = estUsdAmtOut.minus(usdAmtOut) + let feeValue = usdFeeAmt.times(vault.usdPrice) + dailyStat.swapValue = dailyStat.swapValue.plus(swap.swapValue) + dailyStat.feeValue = dailyStat.feeValue.plus(feeValue) + dailyStat.feeYield = dailyStat.feeValue.div(dailyStat.tvl) dailyStat.save() - - swap.feeAmt = dailyStat.usdFeeAmt + swap.feeValue = feeValue swap.save() } } @@ -204,6 +212,7 @@ export function handleSwapUSDForPerps(event: SwapUSDForPerps): void { vault.usdPrice = formatBalance(event.params.preOpState.usdPrice, vault.decimals) vault.tvl = vault.usdBal.times(vault.usdPrice).plus(vault.perpBal.times(vault.perpPrice)) vault.price = vault.tvl.div(vault.totalSupply) + vault.swapNonce = vault.swapNonce.plus(BIGINT_ONE) vault.save() dailyStat.perpPrice = vault.perpPrice @@ -213,7 +222,8 @@ export function handleSwapUSDForPerps(event: SwapUSDForPerps): void { dailyStat.save() swap.type = 'usd' - swap.swapAmt = formatBalance(event.params.usdAmtIn, vault.perpDecimals) + swap.swapAmt = formatBalance(event.params.usdAmtIn, vault.usdDecimals) + swap.swapValue = swap.swapAmt.times(vault.usdPrice) swap.tx = event.transaction.hash.toHex() swap.timestamp = event.block.timestamp swap.save() @@ -232,15 +242,16 @@ export function handleSwapUSDForPerps(event: SwapUSDForPerps): void { let r = vaultContract.try_computeUSDToPerpSwapAmt(event.params.usdAmtIn, reserveStateStruct) if (!r.reverted) { let swapAmts = r.value - dailyStat.usdSwapAmt = dailyStat.usdSwapAmt.plus( - formatBalance(event.params.usdAmtIn, vault.usdDecimals), - ) - dailyStat.perpFeeAmt = dailyStat.perpFeeAmt.plus( - formatBalance(swapAmts.value1, vault.perpDecimals), - ) + let usdAmtIn = swap.swapAmt + let perpAmtOut = formatBalance(swapAmts.value0, vault.perpDecimals) + let estPerpAmtOut = usdAmtIn.times(vault.usdPrice).div(vault.perpPrice) + let perpFeeAmt = estPerpAmtOut.minus(perpAmtOut) + let feeValue = perpFeeAmt.times(vault.perpPrice) + dailyStat.swapValue = dailyStat.swapValue.plus(swap.swapValue) + dailyStat.feeValue = dailyStat.feeValue.plus(feeValue) + dailyStat.feeYield = dailyStat.feeValue.div(dailyStat.tvl) dailyStat.save() - - swap.feeAmt = dailyStat.perpFeeAmt + swap.feeValue = feeValue swap.save() } } @@ -249,6 +260,7 @@ export function handleDepositUSD(event: DepositUSD): void { log.warning('triggered single sided deposit', []) let vault = fetchBillBroker(event.address) let dailyStat = fetchBillBrokerDailyStat(vault, dayTimestamp(event.block.timestamp)) + let swap = fetchBillBrokerSwap(vault, vault.swapNonce.plus(BIGINT_ONE)) refreshBillBrokerStats(vault, dailyStat) vault.perpPrice = formatBalance(event.params.preOpState.perpPrice, vault.decimals) @@ -262,12 +274,55 @@ export function handleDepositUSD(event: DepositUSD): void { dailyStat.tvl = vault.tvl dailyStat.price = vault.price dailyStat.save() + + let vaultContract = BillBrokerABI.bind(stringToAddress(vault.id)) + let reserveStateValues: Array = [ + ethereum.Value.fromUnsignedBigInt(event.params.preOpState.usdBalance), + ethereum.Value.fromUnsignedBigInt(event.params.preOpState.perpBalance), + ethereum.Value.fromUnsignedBigInt(event.params.preOpState.usdPrice), + ethereum.Value.fromUnsignedBigInt(event.params.preOpState.perpPrice), + ] + let reserveStateTuple = changetype(reserveStateValues) + let reserveStateStruct = changetype( + reserveStateTuple, + ) + let r = vaultContract.try_computeMintAmtWithUSD1(event.params.usdAmtIn, reserveStateStruct) + if (!r.reverted) { + let usdAmtIn = formatBalance(event.params.usdAmtIn, vault.usdDecimals) + let valueIn = usdAmtIn.times(vault.usdPrice) + let estMintAmt = formatBalance(r.value, vault.decimals) + let mintAmt = valueIn.div(vault.tvl).times(vault.totalSupply) + let feePerc = estMintAmt.minus(mintAmt).div(estMintAmt) + + let usdClaimPost = vault.usdBal.times(estMintAmt).div(vault.totalSupply) + let swapAmt = usdAmtIn.minus(usdClaimPost) + let feeValue = swapAmt.times(feePerc) + + vault.swapNonce = vault.swapNonce.plus(BIGINT_ONE) + vault.save() + + swap.type = 'usd' + swap.swapAmt = swapAmt + swap.swapValue = swap.swapAmt.times(vault.usdPrice) + swap.tx = event.transaction.hash.toHex() + swap.timestamp = event.block.timestamp + swap.save() + + dailyStat.swapValue = dailyStat.swapValue.plus(swap.swapValue) + dailyStat.feeValue = dailyStat.feeValue.plus(feeValue) + dailyStat.feeYield = dailyStat.feeValue.div(dailyStat.tvl) + dailyStat.save() + + swap.feeValue = feeValue + swap.save() + } } export function handleDepositPerp(event: DepositPerp): void { log.warning('triggered single sided deposit', []) let vault = fetchBillBroker(event.address) let dailyStat = fetchBillBrokerDailyStat(vault, dayTimestamp(event.block.timestamp)) + let swap = fetchBillBrokerSwap(vault, vault.swapNonce.plus(BIGINT_ONE)) refreshBillBrokerStats(vault, dailyStat) vault.perpPrice = formatBalance(event.params.preOpState.perpPrice, vault.decimals) @@ -281,4 +336,46 @@ export function handleDepositPerp(event: DepositPerp): void { dailyStat.tvl = vault.tvl dailyStat.price = vault.price dailyStat.save() + + let vaultContract = BillBrokerABI.bind(stringToAddress(vault.id)) + let reserveStateValues: Array = [ + ethereum.Value.fromUnsignedBigInt(event.params.preOpState.usdBalance), + ethereum.Value.fromUnsignedBigInt(event.params.preOpState.perpBalance), + ethereum.Value.fromUnsignedBigInt(event.params.preOpState.usdPrice), + ethereum.Value.fromUnsignedBigInt(event.params.preOpState.perpPrice), + ] + let reserveStateTuple = changetype(reserveStateValues) + let reserveStateStruct = changetype( + reserveStateTuple, + ) + let r = vaultContract.try_computeMintAmtWithPerp1(event.params.perpAmtIn, reserveStateStruct) + if (!r.reverted) { + let perpAmtIn = formatBalance(event.params.perpAmtIn, vault.perpDecimals) + let valueIn = perpAmtIn.times(vault.perpPrice) + let estMintAmt = formatBalance(r.value, vault.decimals) + let mintAmt = valueIn.div(vault.tvl).times(vault.totalSupply) + let feePerc = estMintAmt.minus(mintAmt).div(estMintAmt) + + let perpClaimPost = vault.perpBal.times(estMintAmt).div(vault.totalSupply) + let swapAmt = perpAmtIn.minus(perpClaimPost) + let feeValue = swapAmt.times(feePerc) + + vault.swapNonce = vault.swapNonce.plus(BIGINT_ONE) + vault.save() + + swap.type = 'perp' + swap.swapAmt = swapAmt + swap.swapValue = swap.swapAmt.times(vault.perpPrice) + swap.tx = event.transaction.hash.toHex() + swap.timestamp = event.block.timestamp + swap.save() + + dailyStat.swapValue = dailyStat.swapValue.plus(swap.swapValue) + dailyStat.feeValue = dailyStat.feeValue.plus(feeValue) + dailyStat.feeYield = dailyStat.feeValue.div(dailyStat.tvl) + dailyStat.save() + + swap.feeValue = feeValue + swap.save() + } } diff --git a/spot-staking-subgraph/src/charmVault.ts b/spot-staking-subgraph/src/charmVault.ts index 3a420458..a7b49067 100644 --- a/spot-staking-subgraph/src/charmVault.ts +++ b/spot-staking-subgraph/src/charmVault.ts @@ -59,7 +59,7 @@ export function fetchCharmVault(address: Address): CharmVault { vault.tvl = BIGDECIMAL_ZERO vault.price = BIGDECIMAL_ZERO vault.totalSupply = BIGDECIMAL_ZERO - + let context = new DataSourceContext() context.setString('charmVault', id) RebasingERC20.createWithContext(getUnderlyingAddress(token1Address), context) @@ -68,7 +68,10 @@ export function fetchCharmVault(address: Address): CharmVault { return vault as CharmVault } -export function fetchCharmVaultDailyStat(vault: CharmVault, timestamp: BigInt): CharmVaultDailyStat { +export function fetchCharmVaultDailyStat( + vault: CharmVault, + timestamp: BigInt, +): CharmVaultDailyStat { let id = vault.id.concat('-').concat(timestamp.toString()) let dailyStat = CharmVaultDailyStat.load(id) if (dailyStat === null) { @@ -103,7 +106,9 @@ export function refreshCharmVaultStats(vault: CharmVault, dailyStat: CharmVaultD vault.token1Bal = formatBalance(tokenBals.value1, vault.token1Decimals) vault.token0Price = BIGDECIMAL_ONE vault.token1Price = prices[0] - vault.tvl = vault.token0Bal.times(vault.token0Price).plus(vault.token1Bal.times(vault.token1Price)) + vault.tvl = vault.token0Bal + .times(vault.token0Price) + .plus(vault.token1Bal.times(vault.token1Price)) vault.totalSupply = formatBalance(vaultContract.totalSupply(), vault.decimals) vault.price = vault.tvl.div(vault.totalSupply) vault.save() @@ -145,9 +150,15 @@ export function handleFees(event: CollectFees): void { let dailyStat = fetchCharmVaultDailyStat(vault, dayTimestamp(event.block.timestamp)) refreshCharmVaultStats(vault, dailyStat) - dailyStat.token0Fees = dailyStat.token0Fees.plus(formatBalance(event.params.feesToVault0, vault.token0Decimals)) - dailyStat.token1Fees = dailyStat.token1Fees.plus(formatBalance(event.params.feesToVault1, vault.token1Decimals)) - dailyStat.totalFeeVal = dailyStat.token1Fees.times(dailyStat.token1Price).plus(dailyStat.token0Fees.times(dailyStat.token0Price)) + dailyStat.token0Fees = dailyStat.token0Fees.plus( + formatBalance(event.params.feesToVault0, vault.token0Decimals), + ) + dailyStat.token1Fees = dailyStat.token1Fees.plus( + formatBalance(event.params.feesToVault1, vault.token1Decimals), + ) + dailyStat.totalFeeVal = dailyStat.token1Fees + .times(dailyStat.token1Price) + .plus(dailyStat.token0Fees.times(dailyStat.token0Price)) dailyStat.feeYield = dailyStat.totalFeeVal.div(dailyStat.tvl.minus(dailyStat.totalFeeVal)) dailyStat.save() } diff --git a/spot-staking-subgraph/src/utils.ts b/spot-staking-subgraph/src/utils.ts index a5aa75c4..9176aecd 100644 --- a/spot-staking-subgraph/src/utils.ts +++ b/spot-staking-subgraph/src/utils.ts @@ -67,4 +67,4 @@ export function getUnderlyingAddress(tokenAddress: Address): Address { return collateralResult.value } return Address.fromString('0x0000000000000000000000000000000000000000') -} \ No newline at end of file +} From 171e31861830718050239504a2aeebb74b27983a Mon Sep 17 00:00:00 2001 From: aalavandhann <6264334+aalavandhan@users.noreply.github.com> Date: Wed, 18 Jun 2025 14:01:21 -0400 Subject: [PATCH 3/4] cleaned up spot subgraph --- spot-subgraph/schema.graphql | 49 --------- spot-subgraph/scripts/deploy.sh | 6 +- spot-subgraph/scripts/generate-ipfs-cid.sh | 30 ++++++ spot-subgraph/src/data/perpetualTranche.ts | 5 - spot-subgraph/src/data/rolloverVault.ts | 38 ++----- .../src/mappings/perpetualTranche.ts | 32 ++---- spot-subgraph/src/mappings/rolloverVault.ts | 101 ++++++------------ spot-subgraph/src/utils.ts | 4 +- spot-subgraph/subgraph.template.yaml | 9 +- 9 files changed, 92 insertions(+), 182 deletions(-) create mode 100755 spot-subgraph/scripts/generate-ipfs-cid.sh diff --git a/spot-subgraph/schema.graphql b/spot-subgraph/schema.graphql index e0b27d17..fd300ffe 100644 --- a/spot-subgraph/schema.graphql +++ b/spot-subgraph/schema.graphql @@ -232,18 +232,6 @@ type PerpetualTrancheDailyStat @entity { " the timestamp of the given day " timestamp: BigInt! - " the total perp tokens minted on the given day " - totalMints: BigDecimal! - - " the total perp tokens redeemed on the given day " - totalRedemptions: BigDecimal! - - " the total value of perp tokens minted on the given day " - totalMintValue: BigDecimal! - - " the total value of perp tokens redeemed on the given day " - totalRedemptionValue: BigDecimal! - " the tvl on the given day " tvl: BigDecimal! @@ -253,9 +241,6 @@ type PerpetualTrancheDailyStat @entity { " the perp token supply on the given day " totalSupply: BigDecimal! - " the total amount of perp tokens paid as fees " - totalPerpFeeAmt: BigDecimal! - " the total underlying value of fees " totalUnderlyingFeeValue: BigDecimal! } @@ -279,12 +264,6 @@ type RolloverVault @entity { " addresses of assets currently in the reserve " activeReserves: [RolloverVaultAsset!]! - " the total fixed mc share of underlying tokens deposited into the system " - totalScaledUnderlyingDeposited: BigDecimal! - - " the total fixed mc share of underlying tokens deposited by each user " - scaledUnderlyingBalances: [ScaledUnderlyingVaultDepositorBalance!]! @derivedFrom(field: "vault") - " the current tvl based on the active reserves " tvl: BigDecimal! @@ -304,13 +283,6 @@ type RolloverVault @entity { dailyStats: [RolloverVaultDailyStat!]! @derivedFrom(field: "vault") } -type ScaledUnderlyingVaultDepositorBalance @entity { - id: ID! - vault: RolloverVault! - account: Bytes! - value: BigDecimal! -} - type RolloverVaultAsset @entity { " Equals to: -" id: ID! @@ -341,18 +313,6 @@ type RolloverVaultDailyStat @entity { " the timestamp of the given day " timestamp: BigInt! - " the total vault notes minted on the given day " - totalMints: BigDecimal! - - " the total vault notes redeemed on the given day " - totalRedemptions: BigDecimal! - - " the total value of vault notes minted on the given day " - totalMintValue: BigDecimal! - - " the total value of vault notes redeemed on the given day " - totalRedemptionValue: BigDecimal! - " the tvl on the given day " tvl: BigDecimal! @@ -365,15 +325,6 @@ type RolloverVaultDailyStat @entity { " the vault note supply on the given day " totalSupply: BigDecimal! - " the total value of swaps on the given day " - totalSwapValue: BigDecimal! - - " the total value of underlying to perp swaps on the given day " - totalUnderlyingToPerpSwapValue: BigDecimal! - - " the total value of perp to underlying swaps on the given day " - totalPerpToUnderlyingSwapValue: BigDecimal! - " the system deviation ratio " deviationRatio: BigDecimal! diff --git a/spot-subgraph/scripts/deploy.sh b/spot-subgraph/scripts/deploy.sh index e6c0c3c6..0c8bcfb8 100755 --- a/spot-subgraph/scripts/deploy.sh +++ b/spot-subgraph/scripts/deploy.sh @@ -7,6 +7,10 @@ yarn codegen yarn build +echo "NOTE: graph deploy to Alchemy fails when you redeploy with the same IPFS hash" + +# yarn graph auth $THE_GRAPH_API_KEY +# yarn graph deploy $2 yarn graph deploy $2 \ --node https://subgraphs.alchemy.com/api/subgraphs/deploy \ - --deploy-key $GRAPH_AUTH \ No newline at end of file + --deploy-key $GRAPH_AUTH diff --git a/spot-subgraph/scripts/generate-ipfs-cid.sh b/spot-subgraph/scripts/generate-ipfs-cid.sh new file mode 100755 index 00000000..d09d8209 --- /dev/null +++ b/spot-subgraph/scripts/generate-ipfs-cid.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# generate-ipfs-cid.sh +# Computes IPFS CID of a given directory using IPFS in Docker without uploading + +set -e + +TARGET_PATH=${1:-build} + +if [ ! -d "$TARGET_PATH" ]; then + echo "❌ Error: Directory '$TARGET_PATH' does not exist." + echo "Usage: $0 [path-to-directory]" + exit 1 +fi + +echo "📦 Computing IPFS CID for '$TARGET_PATH'..." + +CID=$(docker run --rm \ + -v "$(pwd):/data" \ + -e IPFS_PATH=/tmp/ipfs \ + --entrypoint sh \ + ipfs/kubo \ + -c "ipfs init >/dev/null && ipfs add -r --only-hash --quiet /data/$TARGET_PATH" | tail -n 1) + +if [ -z "$CID" ]; then + echo "❌ Failed to generate IPFS CID." + exit 1 +fi + +echo "✅ IPFS CID: $CID" diff --git a/spot-subgraph/src/data/perpetualTranche.ts b/spot-subgraph/src/data/perpetualTranche.ts index c052c993..fac3a83b 100644 --- a/spot-subgraph/src/data/perpetualTranche.ts +++ b/spot-subgraph/src/data/perpetualTranche.ts @@ -172,14 +172,9 @@ export function fetchPerpetualTrancheDailyStat( dailyStat = new PerpetualTrancheDailyStat(id) dailyStat.perp = perp.id dailyStat.timestamp = timestamp - dailyStat.totalMints = BIGDECIMAL_ZERO - dailyStat.totalRedemptions = BIGDECIMAL_ZERO - dailyStat.totalMintValue = BIGDECIMAL_ZERO - dailyStat.totalRedemptionValue = BIGDECIMAL_ZERO dailyStat.tvl = BIGDECIMAL_ZERO dailyStat.price = BIGDECIMAL_ZERO dailyStat.totalSupply = BIGDECIMAL_ZERO - dailyStat.totalPerpFeeAmt = BIGDECIMAL_ZERO dailyStat.totalUnderlyingFeeValue = BIGDECIMAL_ZERO } return dailyStat as PerpetualTrancheDailyStat diff --git a/spot-subgraph/src/data/rolloverVault.ts b/spot-subgraph/src/data/rolloverVault.ts index b8b0b2ee..0a9d21fc 100644 --- a/spot-subgraph/src/data/rolloverVault.ts +++ b/spot-subgraph/src/data/rolloverVault.ts @@ -2,7 +2,6 @@ import { log, BigDecimal, BigInt, Address, DataSourceContext } from '@graphproto import { RolloverVault, RolloverVaultAsset, - ScaledUnderlyingVaultDepositorBalance, RolloverVaultDailyStat, Tranche, } from '../../generated/schema' @@ -173,7 +172,6 @@ export function fetchRolloverVault(address: Address): RolloverVault { vaultToken.save() vault = new RolloverVault(id) vault.token = vaultToken.id - vault.totalScaledUnderlyingDeposited = BIGDECIMAL_ZERO vault.activeReserves = [] vault.tvl = BIGDECIMAL_ZERO vault.rebaseMultiplier = BIGDECIMAL_ONE @@ -219,21 +217,6 @@ export function fetchRolloverVaultAsset( return assetToken as RolloverVaultAsset } -export function fetchScaledUnderlyingVaultDepositorBalance( - vault: RolloverVault, - account: Address, -): ScaledUnderlyingVaultDepositorBalance { - let id = vault.id.concat('-').concat(account.toHexString()) - let balance = ScaledUnderlyingVaultDepositorBalance.load(id) - if (balance == null) { - balance = new ScaledUnderlyingVaultDepositorBalance(id) - balance.vault = vault.id - balance.account = account - balance.value = BIGDECIMAL_ZERO - } - return balance as ScaledUnderlyingVaultDepositorBalance -} - export function fetchRolloverVaultDailyStat( vault: RolloverVault, timestamp: BigInt, @@ -244,17 +227,10 @@ export function fetchRolloverVaultDailyStat( dailyStat = new RolloverVaultDailyStat(id) dailyStat.vault = vault.id dailyStat.timestamp = timestamp - dailyStat.totalMints = BIGDECIMAL_ZERO - dailyStat.totalRedemptions = BIGDECIMAL_ZERO - dailyStat.totalMintValue = BIGDECIMAL_ZERO - dailyStat.totalRedemptionValue = BIGDECIMAL_ZERO dailyStat.tvl = BIGDECIMAL_ZERO dailyStat.rebaseMultiplier = BIGDECIMAL_ONE dailyStat.price = BIGDECIMAL_ZERO dailyStat.totalSupply = BIGDECIMAL_ZERO - dailyStat.totalSwapValue = BIGDECIMAL_ZERO - dailyStat.totalUnderlyingToPerpSwapValue = BIGDECIMAL_ZERO - dailyStat.totalPerpToUnderlyingSwapValue = BIGDECIMAL_ZERO dailyStat.deviationRatio = BIGDECIMAL_ZERO dailyStat.totalUnderlyingFeeValue = BIGDECIMAL_ZERO } @@ -276,15 +252,16 @@ function fetchTargetSystemRatio(vaultAddress: Address): BigDecimal { let vaultContract = RolloverVaultABI.bind(vaultAddress) let r1 = vaultContract.try_feePolicy() if (r1.reverted) { - log.debug('fee policy not set', []) + log.error('fee policy not set', []) return SYSTEM_RATIO_START } let feePolicyContract = FeePolicyABI.bind(r1.value) let r2 = feePolicyContract.try_targetSystemRatio() if (r2.reverted) { - log.debug('fee policy version incorrect', []) + log.error('fee policy version incorrect', []) return SYSTEM_RATIO_START } + log.error('fee policy correct {}:{}', [r1.value.toHexString(), r2.value.toString()]) return formatBalance(r2.value, BigInt.fromI32(feePolicyContract.decimals())) } @@ -307,7 +284,7 @@ export function computeFeePerc( let vaultContract = RolloverVaultABI.bind(vaultAddress) let r1 = vaultContract.try_feePolicy() if (r1.reverted) { - log.debug('fee policy not set', []) + log.error('fee policy not set', []) return BIGDECIMAL_ZERO } let feePolicyContract = FeePolicyABI.bind(r1.value) @@ -322,8 +299,13 @@ export function computeFeePerc( ) let r2 = feePolicyContract.try_computeFeePerc(drPre, drPost) if (r2.reverted) { - log.debug('fee policy version incorrect', []) + log.error('fee policy version incorrect', []) return BIGDECIMAL_ZERO } + log.error('fee policy correct {}:{}:{}', [ + r1.value.toHexString(), + drPre.toString(), + drPost.toString(), + ]) return formatBalance(r2.value, feePolicyDecimals) } diff --git a/spot-subgraph/src/mappings/perpetualTranche.ts b/spot-subgraph/src/mappings/perpetualTranche.ts index 3197a1fa..e3d5b5c6 100644 --- a/spot-subgraph/src/mappings/perpetualTranche.ts +++ b/spot-subgraph/src/mappings/perpetualTranche.ts @@ -63,44 +63,28 @@ export function handleTransfer(event: Transfer): void { log.debug('triggered mint', []) let perpToken = fetchToken(event.address) refreshSupply(perpToken) - let perp = fetchPerpetualTranche(event.address) refreshPerpetualTrancheTVL(perp) + } - let perpAmtMinted = formatBalance(event.params.value, perpToken.decimals) - let dailyStat = fetchPerpetualTrancheDailyStat(perp, dayTimestamp(event.block.timestamp)) - dailyStat.totalMints = dailyStat.totalMints.plus(perpAmtMinted) - dailyStat.totalMintValue = dailyStat.totalMintValue.plus(perpAmtMinted.times(perp.price)) - dailyStat.save() + if (to == ADDRESS_ZERO) { + log.debug('triggered burn', []) + let perpToken = fetchToken(event.address) + refreshSupply(perpToken) + let perp = fetchPerpetualTranche(event.address) + refreshPerpetualTrancheTVL(perp) } // Mint and burn fees are handled by sending perp tokens to the perp contract if (to == event.address) { + log.debug('perp fees paid', []) let perpToken = fetchToken(event.address) let perp = fetchPerpetualTranche(event.address) let perpFeeAmt = formatBalance(event.params.value, perpToken.decimals) let dailyStat = fetchPerpetualTrancheDailyStat(perp, dayTimestamp(event.block.timestamp)) - dailyStat.totalPerpFeeAmt = dailyStat.totalPerpFeeAmt.plus(perpFeeAmt) dailyStat.totalUnderlyingFeeValue = dailyStat.totalUnderlyingFeeValue.plus( perpFeeAmt.times(perp.price), ) dailyStat.save() } } - -export function handleRedeem(call: RedeemCall): void { - log.debug('triggered redeem', []) - let perpToken = fetchToken(call.to) - refreshSupply(perpToken) - - let perp = fetchPerpetualTranche(call.to) - refreshPerpetualTrancheTVL(perp) - - let perpAmtBurnt = formatBalance(call.inputs.perpAmtBurnt, perpToken.decimals) - let dailyStat = fetchPerpetualTrancheDailyStat(perp, dayTimestamp(call.block.timestamp)) - dailyStat.totalRedemptions = dailyStat.totalRedemptions.plus(perpAmtBurnt) - dailyStat.totalRedemptionValue = dailyStat.totalRedemptionValue.plus( - perpAmtBurnt.times(perp.price), - ) - dailyStat.save() -} diff --git a/spot-subgraph/src/mappings/rolloverVault.ts b/spot-subgraph/src/mappings/rolloverVault.ts index 524d7e62..60ebd29e 100644 --- a/spot-subgraph/src/mappings/rolloverVault.ts +++ b/spot-subgraph/src/mappings/rolloverVault.ts @@ -1,5 +1,6 @@ import { log, ethereum } from '@graphprotocol/graph-ts' import { + Transfer, DepositCall, RedeemCall, AssetSynced, @@ -10,7 +11,6 @@ import { RebasingERC20 as RebasingERC20ABI } from '../../generated/templates/Reb import { fetchPerpetualTranche, refreshPerpetualTrancheTVL } from '../data/perpetualTranche' import { fetchRolloverVault, - fetchScaledUnderlyingVaultDepositorBalance, refreshRolloverVaultTVL, refreshRolloverVaultRebaseMultiplier, refreshRolloverVaultDailyStat, @@ -26,49 +26,40 @@ import { removeFromSet, BIGDECIMAL_ZERO, dayTimestamp, + ADDRESS_ZERO, } from '../utils' +export function handleTransfer(event: Transfer): void { + let from = event.params.from + let to = event.params.to + if (from == ADDRESS_ZERO) { + log.debug('triggered mint', []) + let vault = fetchRolloverVault(event.address) + let vaultToken = fetchToken(event.address) + refreshSupply(vaultToken) + refreshRolloverVaultTVL(vault) + refreshRolloverVaultRebaseMultiplier(vault) + } + + if (to == ADDRESS_ZERO) { + log.debug('triggered burn', []) + let vault = fetchRolloverVault(event.address) + let vaultToken = fetchToken(event.address) + refreshSupply(vaultToken) + refreshRolloverVaultTVL(vault) + refreshRolloverVaultRebaseMultiplier(vault) + } +} + export function handleDeposit(call: DepositCall): void { log.debug('triggered deposit', []) let vault = fetchRolloverVault(call.to) let vaultAddress = stringToAddress(vault.token) - let vaultToken = fetchToken(vaultAddress) - refreshSupply(vaultToken) - refreshRolloverVaultTVL(vault) - refreshRolloverVaultRebaseMultiplier(vault) - let perp = fetchPerpetualTranche(stringToAddress(vault.perp)) - refreshPerpetualTrancheTVL(perp) - let underlyingToken = fetchToken(stringToAddress(vault.underlying)) - refreshSupply(underlyingToken) - let underlyingTokenSupply = underlyingToken.totalSupply - - let underlyingTokenContract = RebasingERC20ABI.bind(stringToAddress(vault.underlying)) - let scaledUnderlyingSupply = underlyingTokenContract.scaledTotalSupply().toBigDecimal() let underlyingAmtIn = formatBalance(call.inputs.underlyingAmtIn, underlyingToken.decimals) - let scaledUnderlyingAmountIn = underlyingAmtIn - .times(scaledUnderlyingSupply) - .div(underlyingTokenSupply) - vault.totalScaledUnderlyingDeposited = vault.totalScaledUnderlyingDeposited.plus( - scaledUnderlyingAmountIn, - ) - vault.save() - - let scaledUnderlyingDepositorBalance = fetchScaledUnderlyingVaultDepositorBalance( - vault, - call.from, - ) - scaledUnderlyingDepositorBalance.value = scaledUnderlyingDepositorBalance.value.plus( - scaledUnderlyingAmountIn, - ) - scaledUnderlyingDepositorBalance.save() - let dailyStat = fetchRolloverVaultDailyStat(vault, dayTimestamp(call.block.timestamp)) - dailyStat.totalMints = dailyStat.totalMints.plus(underlyingAmtIn.div(vault.price)) - dailyStat.totalMintValue = dailyStat.totalMintValue.plus(underlyingAmtIn) - let feePerc = computeFeePerc( perp.tvl, vault.tvl.minus(underlyingAmtIn), @@ -80,7 +71,6 @@ export function handleDeposit(call: DepositCall): void { dailyStat.totalUnderlyingFeeValue = dailyStat.totalUnderlyingFeeValue.plus( underlyingAmtIn.times(feePerc), ) - dailyStat.save() } @@ -89,35 +79,10 @@ export function handleRedeem(call: RedeemCall): void { let vault = fetchRolloverVault(call.to) let vaultAddress = stringToAddress(vault.token) let vaultToken = fetchToken(vaultAddress) - refreshSupply(vaultToken) - refreshRolloverVaultTVL(vault) - refreshRolloverVaultRebaseMultiplier(vault) - let perp = fetchPerpetualTranche(stringToAddress(vault.perp)) - refreshPerpetualTrancheTVL(perp) let notesOut = formatBalance(call.inputs.notes, vaultToken.decimals) - let scaledAmountOut = vault.totalScaledUnderlyingDeposited - .times(notesOut) - .div(vaultToken.totalSupply) - vault.totalScaledUnderlyingDeposited = vault.totalScaledUnderlyingDeposited.minus( - scaledAmountOut, - ) - vault.save() - - let scaledUnderlyingDepositorBalance = fetchScaledUnderlyingVaultDepositorBalance( - vault, - call.from, - ) - scaledUnderlyingDepositorBalance.value = scaledUnderlyingDepositorBalance.value.minus( - scaledAmountOut, - ) - scaledUnderlyingDepositorBalance.save() - let dailyStat = fetchRolloverVaultDailyStat(vault, dayTimestamp(call.block.timestamp)) - dailyStat.totalRedemptions = dailyStat.totalRedemptions.plus(notesOut) - dailyStat.totalRedemptionValue = dailyStat.totalRedemptionValue.plus(notesOut.times(vault.price)) - let feePerc = computeFeePerc( perp.tvl, vault.tvl.times(notesOut + vaultToken.totalSupply).div(vaultToken.totalSupply), @@ -129,7 +94,6 @@ export function handleRedeem(call: RedeemCall): void { dailyStat.totalUnderlyingFeeValue = dailyStat.totalUnderlyingFeeValue.plus( notesOut.times(vault.price).times(feePerc), ) - dailyStat.save() } @@ -173,11 +137,6 @@ export function handleUnderlyingToPerpSwap(call: SwapUnderlyingForPerpsCall): vo let underlyingAmtIn = formatBalance(call.inputs.underlyingAmtIn, underlyingToken.decimals) let dailyStat = fetchRolloverVaultDailyStat(vault, dayTimestamp(call.block.timestamp)) - dailyStat.totalSwapValue = dailyStat.totalSwapValue.plus(underlyingAmtIn) - dailyStat.totalUnderlyingToPerpSwapValue = dailyStat.totalUnderlyingToPerpSwapValue.plus( - underlyingAmtIn, - ) - let feePerc = computeFeePerc( perp.tvl.minus(underlyingAmtIn), vault.tvl, @@ -186,6 +145,13 @@ export function handleUnderlyingToPerpSwap(call: SwapUnderlyingForPerpsCall): vo vault.targetSystemRatio, vaultAddress, ) + log.error('computing fee {}:{}:{}:{}', [ + perp.tvl.minus(underlyingAmtIn).toString(), + vault.tvl.toString(), + perp.tvl.toString(), + vault.tvl.toString(), + vault.targetSystemRatio.toString(), + ]) dailyStat.totalUnderlyingFeeValue = dailyStat.totalUnderlyingFeeValue.plus( underlyingAmtIn.times(feePerc), ) @@ -208,11 +174,6 @@ export function handlePerpToUnderlyingSwap(call: SwapPerpsForUnderlyingCall): vo let perpAmtIn = formatBalance(call.inputs.perpAmtIn, underlyingToken.decimals) let dailyStat = fetchRolloverVaultDailyStat(vault, dayTimestamp(call.block.timestamp)) - dailyStat.totalSwapValue = dailyStat.totalSwapValue.plus(perpAmtIn.times(perp.price)) - dailyStat.totalPerpToUnderlyingSwapValue = dailyStat.totalPerpToUnderlyingSwapValue.plus( - perpAmtIn.times(perp.price), - ) - let feePerc = computeFeePerc( perp.tvl.plus(perpAmtIn.times(perp.price)), vault.tvl, diff --git a/spot-subgraph/src/utils.ts b/spot-subgraph/src/utils.ts index 5b456d48..a824c00d 100644 --- a/spot-subgraph/src/utils.ts +++ b/spot-subgraph/src/utils.ts @@ -9,9 +9,9 @@ export let BIGDECIMAL_ZERO = new BigDecimal(BIGINT_ZERO) export let BIGDECIMAL_ONE = new BigDecimal(BIGINT_ONE) export let ADDRESS_ZERO = Address.fromString('0x0000000000000000000000000000000000000000') -export const formatDecimalBalance = (wei: BigDecimal, decimals: BigInt): BigInt => { +export const formatDecimalBalance = (value: BigDecimal, decimals: BigInt): BigInt => { return toBigInt( - wei.div( + value.times( BigInt.fromI32(10) .pow(decimals.toI32() as u8) .toBigDecimal(), diff --git a/spot-subgraph/subgraph.template.yaml b/spot-subgraph/subgraph.template.yaml index 873c6928..a9f36996 100644 --- a/spot-subgraph/subgraph.template.yaml +++ b/spot-subgraph/subgraph.template.yaml @@ -149,9 +149,6 @@ dataSources: handler: handleUpdatedDepositBond - event: Transfer(indexed address,indexed address,uint256) handler: handleTransfer - callHandlers: - - function: redeem(uint256) - handler: handleRedeem file: ./src/mappings/perpetualTranche.ts - kind: ethereum/contract @@ -172,6 +169,8 @@ dataSources: file: ../spot-contracts/exported-artifacts/RolloverVault.json - name: PerpetualTranche file: ../spot-contracts/exported-artifacts/PerpetualTranche.json + - name: PerpetualTrancheV1 + file: ../spot-contracts/exported-artifacts/PerpetualTrancheV1.json - name: BondController file: ../spot-contracts/external-artifacts/BondController.json - name: Tranche @@ -185,6 +184,8 @@ dataSources: eventHandlers: - event: AssetSynced(address,uint256) handler: handleAssetSynced + - event: Transfer(indexed address,indexed address,uint256) + handler: handleTransfer callHandlers: - function: deposit(uint256) handler: handleDeposit @@ -274,6 +275,8 @@ templates: file: ../spot-contracts/external-artifacts/ERC20ABI.json - name: RebasingERC20 file: ../spot-contracts/external-artifacts/RebasingERC20ABI.json + - name: FeePolicy + file: ../spot-contracts/exported-artifacts/FeePolicy.json eventHandlers: - event: Rebase(indexed uint256,uint256) handler: handleRebase From f200d881410907a71aaf8cb6263bf318955c05e9 Mon Sep 17 00:00:00 2001 From: aalavandhann <6264334+aalavandhan@users.noreply.github.com> Date: Tue, 8 Jul 2025 12:19:32 -0400 Subject: [PATCH 4/4] ran linter --- spot-contracts/exported-artifacts/FeePolicy.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spot-contracts/exported-artifacts/FeePolicy.json b/spot-contracts/exported-artifacts/FeePolicy.json index 14eabb3f..746ff17c 100644 --- a/spot-contracts/exported-artifacts/FeePolicy.json +++ b/spot-contracts/exported-artifacts/FeePolicy.json @@ -579,4 +579,4 @@ "stateMutability": "nonpayable", "type": "function" } -] \ No newline at end of file +]