diff --git a/.github/workflows/deploy-subgraph.yml b/.github/workflows/deploy-subgraph.yml index c8b82de..bc97f30 100644 --- a/.github/workflows/deploy-subgraph.yml +++ b/.github/workflows/deploy-subgraph.yml @@ -13,10 +13,10 @@ jobs: - name: Install modules run: yarn - name: Generate config - run: yarn generate-config:goerli + run: yarn generate-config:sepolia - name: Codegen - run: yarn codegen:goerli + run: yarn codegen:sepolia - name: Authorize TheGraph run: yarn authorize ${{secrets.THE_GRAPH_ACCESS_TOKEN}} - name: Deploy - run: yarn deploy:goerli + run: yarn deploy:sepolia diff --git a/.prettierrc b/.prettierrc index 9f99a3b..584fd18 100644 --- a/.prettierrc +++ b/.prettierrc @@ -18,9 +18,6 @@ } } ], - "importOrder": [ - "^@*/(.*)$", - "^[./]" - ], + "importOrder": ["^@*/(.*)$", "^[./]"], "importOrderSeparation": true } diff --git a/README.md b/README.md index 660e15b..5bd829f 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This subgraph can be found on The Graph Hosted Service for the following networks: - [Mainnet](https://thegraph.com/hosted-service/subgraph/perspectivefi/spectra-mainnet) -- [Goerli](https://thegraph.com/hosted-service/subgraph/perspectivefi/spectra-goerli) +- [Sepolia](https://api.studio.thegraph.com/query/58996/spectra-subgraph-sepolia/version/latest) You can also run this subgraph locally, if you wish. Instructions for that can be found in [The Graph Documentation](https://thegraph.com/docs/en/cookbook/quick-start/). @@ -161,7 +161,7 @@ graph auth https://api.thegraph.com/deploy/ ### 5. Create deployment script in `package.json` (if not exists for your network) -### 6. Add network id (if missing) to the `ChainId` class in `src/utils/ChainId.ts` file +### 6. Add network id (if missing) to the `ChainId` class in `src/utils/ChainId.ts` file ### 7. Deploy the subgraph @@ -180,7 +180,8 @@ yarn deploy: #### IMPORTANT: If your network is not supported you have to go to `src/configs` and add new config file. ## Schema -Graph definition written in PlantUML framework - [schema.puml](schema.puml) + +Graph definition written in PlantUML framework - [schema.puml](schema.puml) ## Example queries diff --git a/schema.graphql b/schema.graphql index d214f80..b7ea8b6 100644 --- a/schema.graphql +++ b/schema.graphql @@ -21,6 +21,7 @@ enum AssetType { LP LP_VAULT_SHARES YIELD + CLAIMED_YIELD UNKNOWN } @@ -138,6 +139,8 @@ type Asset @entity { "in the case the asset is an IBT we create relation between this IBT and its underlying asset" underlying: Asset + "in case the asset is a CLAIMED_YIELD we create relation between this CLAIMED_YIELD and its native IBT" + ibt: Asset fytTokenDetails: FYTTokenDetails lpTokenDetails: LPTokenDetails } diff --git a/src/entities/Yield.ts b/src/entities/Yield.ts index a3a7373..bf17df7 100644 --- a/src/entities/Yield.ts +++ b/src/entities/Yield.ts @@ -1,17 +1,20 @@ -import { Address, BigInt, log } from "@graphprotocol/graph-ts" +import { Address, BigInt } from "@graphprotocol/graph-ts" import { AccountAsset, Asset, Future } from "../../generated/schema" import { ZERO_BI } from "../constants" import { AssetType, generateAccountAssetId } from "../utils" -import { generateYieldAssetId } from "../utils/idGenerators" +import { + generateClaimedYieldAssetId, + generateYieldAssetId, +} from "../utils/idGenerators" import { getAccount } from "./Account" import { getAsset } from "./Asset" import { getERC20Decimals } from "./ERC20" import { getName, getSymbol, - getUnderlying, getCurrentYieldOfUserInIBT, + getIBT, } from "./FutureVault" import { getNetwork } from "./Network" @@ -88,10 +91,87 @@ export function getAccountYieldAsset( return accountAsset } -export function updateAccountAssetBalance( +function createClaimedYieldAsset( + principalToken: Address, + ibtAddress: Address, + timestamp: BigInt +): Asset { + let asset = new Asset(generateClaimedYieldAssetId(principalToken.toHex())) + asset.chainId = getNetwork().chainId + asset.address = ibtAddress + asset.createdAtTimestamp = timestamp + asset.type = AssetType.CLAIMED_YIELD + + asset.name = getName(principalToken) + " Claimed Yield" + asset.symbol = getSymbol(principalToken) + " Claimed Yield" + asset.decimals = getERC20Decimals(ibtAddress) + + let ibtAsset = getAsset(ibtAddress.toHex(), timestamp, AssetType.IBT) + asset.ibt = ibtAsset.id + asset.futureVault = principalToken.toHex() + + asset.save() + return asset +} + +function getClaimedYieldAsset( + principalToken: Address, + ibtAddress: Address, + timestamp: BigInt +): Asset { + let asset = Asset.load(generateClaimedYieldAssetId(principalToken.toHex())) + if (asset) { + return asset + } + + asset = createClaimedYieldAsset(principalToken, ibtAddress, timestamp) + + return asset as Asset +} + +export function getAccountClaimedYieldAsset( + accountAddress: Address, + principalToken: Address, + timestamp: BigInt +): AccountAsset { + const ibtAddress = getIBT(principalToken) + let claimedYieldAsset = getClaimedYieldAsset( + principalToken, + ibtAddress, + timestamp + ) + let ibtAsset = getAsset(ibtAddress.toHex(), timestamp, AssetType.IBT) + + claimedYieldAsset.ibt = ibtAsset.id + claimedYieldAsset.save() + + let account = getAccount(accountAddress.toHex(), timestamp) + let accountAssetId = generateAccountAssetId( + account.address.toHex(), + claimedYieldAsset.id, + "claimed-" + ) + + let accountAsset = AccountAsset.load(accountAssetId) + if (accountAsset) { + return accountAsset + } + + accountAsset = new AccountAsset(accountAssetId) + + accountAsset.createdAtTimestamp = timestamp + accountAsset.balance = ZERO_BI + + accountAsset.asset = claimedYieldAsset.id + accountAsset.account = accountAddress.toHex() + + accountAsset.save() + return accountAsset +} + +export function updateYieldAccountAssetBalance( principalToken: Address, accountAddress: Address, - assetId: string, timestamp: BigInt ): AccountAsset { let yieldAsset = getYieldAsset(principalToken, accountAddress, timestamp) @@ -103,7 +183,6 @@ export function updateAccountAssetBalance( timestamp ) - // TODO: Change IBT to Underlying accountAsset.balance = getCurrentYieldOfUserInIBT( principalToken, accountAddress @@ -113,20 +192,30 @@ export function updateAccountAssetBalance( return accountAsset } -export function updateYield( +export function updateClaimedYieldAccountAssetBalance( principalToken: Address, accountAddress: Address, + claimBalance: BigInt, timestamp: BigInt -): void { - let underlyingAddress = getUnderlying(principalToken) - let yieldAsset = getYieldAsset(principalToken, underlyingAddress, timestamp) - - updateAccountAssetBalance( - principalToken, +): AccountAsset { + let accountAsset = getAccountClaimedYieldAsset( accountAddress, - yieldAsset.id, + principalToken, timestamp ) + + accountAsset.balance = accountAsset.balance.plus(claimBalance) + + accountAsset.save() + return accountAsset +} + +export function updateYield( + principalToken: Address, + accountAddress: Address, + timestamp: BigInt +): void { + updateYieldAccountAssetBalance(principalToken, accountAddress, timestamp) } export function updateYieldForAll( diff --git a/src/mappings/futures.ts b/src/mappings/futures.ts index 4790ee6..02fe9ef 100644 --- a/src/mappings/futures.ts +++ b/src/mappings/futures.ts @@ -49,7 +49,10 @@ import { import { getNetwork } from "../entities/Network" import { createPool } from "../entities/Pool" import { createTransaction } from "../entities/Transaction" -import { updateYieldForAll } from "../entities/Yield" +import { + updateClaimedYieldAccountAssetBalance, + updateYieldForAll, +} from "../entities/Yield" import { AssetType, generateFeeClaimId } from "../utils" // import FutureState from "../utils/FutureState" import transactionType from "../utils/TransactionType" @@ -420,7 +423,12 @@ export function handleYieldClaimed(event: YieldClaimed): void { let future = Future.load(event.address.toHex()) if (future) { - updateYieldForAll(event.address, event.block.timestamp) + updateClaimedYieldAccountAssetBalance( + event.address, + event.params.receiver, + event.params.yieldInIBT, + event.block.timestamp + ) } else { log.warning("YieldClaimed event call for not existing Future {}", [ event.address.toHex(), diff --git a/src/tests/futures.test.ts b/src/tests/futures.test.ts index b0bf415..776e017 100644 --- a/src/tests/futures.test.ts +++ b/src/tests/futures.test.ts @@ -15,6 +15,7 @@ import { Redeem, Unpaused, YieldUpdated, + YieldClaimed, } from "../../generated/templates/PrincipalToken/PrincipalToken" import { DAY_ID_0, ZERO_BI } from "../constants" import { @@ -23,6 +24,7 @@ import { handleRedeem, handleUnpaused, handleYieldUpdated, + handleYieldClaimed, } from "../mappings/futures" import { generateAssetAmountId, @@ -853,3 +855,46 @@ describe("handleCurvePoolDeployed()", () => { ) }) }) + +describe("handleYieldClaimed()", () => { + beforeAll(() => { + let yieldClaimedEvent = changetype(newMockEvent()) + yieldClaimedEvent.address = FIRST_FUTURE_VAULT_ADDRESS_MOCK + + let ownerParam = new ethereum.EventParam( + "owner", + ethereum.Value.fromAddress(FIRST_USER_MOCK) + ) + + let receiverParam = new ethereum.EventParam( + "receiver", + ethereum.Value.fromAddress(FIRST_USER_MOCK) + ) + + let yieldInIBTParam = new ethereum.EventParam( + "yieldInIBT", + ethereum.Value.fromUnsignedBigInt(YIELD_USER_YIELD_IN_IBT_MOCK) + ) + + yieldClaimedEvent.parameters = [ + ownerParam, + receiverParam, + yieldInIBTParam, + ] + + handleYieldClaimed(yieldClaimedEvent) + }) + + test("Should update claim yield in user portfolio", () => { + assert.fieldEquals( + ACCOUNT_ASSET_ENTITY, + generateAccountAssetId( + FIRST_USER_MOCK.toHex(), + `${FIRST_FUTURE_VAULT_ADDRESS_MOCK.toHex()}-claimed-yield`, + "claimed-" + ), + "balance", + YIELD_USER_YIELD_IN_IBT_MOCK.toString() + ) + }) +}) diff --git a/src/utils/AssetType.ts b/src/utils/AssetType.ts index aa1e4cd..48dfc56 100644 --- a/src/utils/AssetType.ts +++ b/src/utils/AssetType.ts @@ -7,6 +7,7 @@ class AssetType { LP: string = "LP" LP_VAULT_SHARES: string = "LP_VAULT_SHARES" YIELD: string = "YIELD" + CLAIMED_YIELD: string = "CLAIMED_YIELD" UNKNOWN: string = "UNKNOWN" } diff --git a/src/utils/idGenerators.ts b/src/utils/idGenerators.ts index 84590da..24fb370 100644 --- a/src/utils/idGenerators.ts +++ b/src/utils/idGenerators.ts @@ -23,8 +23,9 @@ export const generateAssetPriceId = ( // AccountAsset export const generateAccountAssetId = ( accountAddress: string, - assetAddress: string -): string => `${accountAddress}-${assetAddress}` + assetAddress: string, + prefix: string = "" +): string => `${prefix}${accountAddress}-${assetAddress}` // Fees export const generateFeeClaimId = ( @@ -51,3 +52,6 @@ export const generateTransactionId = ( export const generateYieldAssetId = (principalToken: string): string => `${principalToken}-yield` + +export const generateClaimedYieldAssetId = (principalToken: string): string => + `${principalToken}-claimed-yield`