From 8257a9b1eb8f51049c88bdfd35b5b23015286f5e Mon Sep 17 00:00:00 2001 From: Dhruv Chauhan Date: Mon, 6 Jan 2025 15:25:01 +0530 Subject: [PATCH 1/4] add subgraph --- deployment/deployment.json | 34 + subgraphs/aave-forks/package-lock.json | 1 - .../configurations.json | 18 + .../config/templates/superlend.template.yaml | 199 +++++ .../protocols/superlend/src/constants.ts | 71 ++ .../protocols/superlend/src/mapping.ts | 766 ++++++++++++++++++ subgraphs/aave-forks/schema.graphql | 1 + subgraphs/aave-forks/src/constants.ts | 1 + 8 files changed, 1090 insertions(+), 1 deletion(-) create mode 100644 subgraphs/aave-forks/protocols/superlend/config/deployments/superlend-etherlink-mainnet/configurations.json create mode 100644 subgraphs/aave-forks/protocols/superlend/config/templates/superlend.template.yaml create mode 100644 subgraphs/aave-forks/protocols/superlend/src/constants.ts create mode 100644 subgraphs/aave-forks/protocols/superlend/src/mapping.ts diff --git a/deployment/deployment.json b/deployment/deployment.json index bba57fe04c..ea7d35335c 100644 --- a/deployment/deployment.json +++ b/deployment/deployment.json @@ -13970,5 +13970,39 @@ } } } + }, + "superlend": { + "schema": "lending", + "base": "aave-forks", + "protocol": "superlend", + "project": "superlend", + "deployments": { + "superlend-etherlink-mainnet": { + "network": "etherlink-mainnet", + "status": "prod", + "versions": { + "schema": "3.1.0", + "subgraph": "1.0.0", + "methodology": "1.0.0" + }, + "files": { + "template": "superlend.template.yaml" + }, + "options": { + "prepare:yaml": true, + "prepare:constants": false + }, + "services": { + "hosted-service": { + "slug": "superlend-etherlink-mainnet", + "query-id": "superlend-etherlink-mainnet" + }, + "decentralized-network": { + "slug": "superlend-etherlink-mainnet", + "query-id": "todo" + } + } + } + } } } diff --git a/subgraphs/aave-forks/package-lock.json b/subgraphs/aave-forks/package-lock.json index ea290c5902..b98a7cb310 100644 --- a/subgraphs/aave-forks/package-lock.json +++ b/subgraphs/aave-forks/package-lock.json @@ -4,7 +4,6 @@ "requires": true, "packages": { "": { - "name": "aave-forks", "license": "MIT", "dependencies": { "@graphprotocol/graph-cli": "^0.60.0", diff --git a/subgraphs/aave-forks/protocols/superlend/config/deployments/superlend-etherlink-mainnet/configurations.json b/subgraphs/aave-forks/protocols/superlend/config/deployments/superlend-etherlink-mainnet/configurations.json new file mode 100644 index 0000000000..31dd31de6e --- /dev/null +++ b/subgraphs/aave-forks/protocols/superlend/config/deployments/superlend-etherlink-mainnet/configurations.json @@ -0,0 +1,18 @@ +{ + "network": "etherlink-mainnet", + "factory": { + "address": "0x5ccf60c7e10547c5389e9cbff543e5d0db9f4fec", + "startBlock": 3283856 + }, + "lendingPoolConfigurator": { + "address": "0x30f6880bb1cf780a49eb4ef64e64585780aae060", + "startBlock": 3283856 + }, + "LendingPool": { + "address": "0x3bd16d195786fb2f509f2e2d7f69920262ef114d", + "startBlock": 3283856 + }, + "graftEnabled": false, + "subgraphId": "", + "graftStartBlock": 0 +} diff --git a/subgraphs/aave-forks/protocols/superlend/config/templates/superlend.template.yaml b/subgraphs/aave-forks/protocols/superlend/config/templates/superlend.template.yaml new file mode 100644 index 0000000000..4cf8e08aca --- /dev/null +++ b/subgraphs/aave-forks/protocols/superlend/config/templates/superlend.template.yaml @@ -0,0 +1,199 @@ +specVersion: 1.0.0 +schema: + file: ./schema.graphql +indexerHints: + prune: auto +{{#graftEnabled}} +description: ... +features: + - grafting +graft: + base: {{subgraphId}} # Subgraph ID of base subgraph + block: {{graftStartBlock}} # Block number +{{/graftEnabled}} +dataSources: + - kind: ethereum + name: LendingPoolAddressesProvider + network: {{ network }} + source: + abi: LendingPoolAddressesProvider + address: "{{ factory.address }}" + startBlock: {{ factory.startBlock }} + mapping: + kind: ethereum/events + apiVersion: 0.0.7 + language: wasm/assemblyscript + entities: [] + abis: + - name: LendingPoolAddressesProvider + file: ./abis/aave-v3/LendingPoolAddressesProvider.json + eventHandlers: + - event: PriceOracleUpdated(indexed address,indexed address) + handler: handlePriceOracleUpdated + file: ./protocols/superlend/src/mapping.ts + - kind: ethereum + name: LendingPoolConfigurator + network: {{ network }} + source: + abi: LendingPoolConfigurator + address: "{{ lendingPoolConfigurator.address }}" + startBlock: {{ lendingPoolConfigurator.startBlock }} + mapping: + kind: ethereum/events + apiVersion: 0.0.7 + language: wasm/assemblyscript + entities: [] + abis: + - name: LendingPoolConfigurator + file: ./abis/aave-v3/LendingPoolConfigurator.json + - name: ERC20 + file: ./abis/ERC20.json + - name: ERC20NameBytes + file: ./abis/ERC20NameBytes.json + - name: ERC20SymbolBytes + file: ./abis/ERC20SymbolBytes.json + - name: AToken + file: ./abis/aave-v3/AToken.json + eventHandlers: + - event: CollateralConfigurationChanged(indexed address,uint256,uint256,uint256) + handler: handleCollateralConfigurationChanged + - event: ReserveActive(indexed address,bool) + handler: handleReserveActive + - event: ReserveBorrowing(indexed address,bool) + handler: handleReserveBorrowing + - event: ReserveFrozen(indexed address,bool) + handler: handleReserveFrozen + - event: ReserveInitialized(indexed address,indexed address,address,address,address) + handler: handleReserveInitialized + - event: ReservePaused(indexed address,bool) + handler: handleReservePaused + - event: ReserveFactorChanged(indexed address,uint256,uint256) + handler: handleReserveFactorChanged + - event: LiquidationProtocolFeeChanged(indexed address,uint256,uint256) + handler: handleLiquidationProtocolFeeChanged + - event: FlashloanPremiumTotalUpdated(uint128,uint128) + handler: handleFlashloanPremiumTotalUpdated + - event: FlashloanPremiumToProtocolUpdated(uint128,uint128) + handler: handleFlashloanPremiumToProtocolUpdated + - event: SiloedBorrowingChanged(indexed address,bool,bool) + handler: handleSiloedBorrowingChanged + file: ./protocols/superlend/src/mapping.ts + - kind: ethereum + name: LendingPool + network: {{ network }} + source: + abi: LendingPool + address: "{{ LendingPool.address }}" + startBlock: {{ LendingPool.startBlock }} + mapping: + kind: ethereum/events + apiVersion: 0.0.7 + language: wasm/assemblyscript + entities: [] + abis: + - name: LendingPool + file: ./abis/aave-v3/LendingPool.json + - name: ERC20 + file: ./abis/ERC20.json + - name: ERC20NameBytes + file: ./abis/ERC20NameBytes.json + - name: ERC20SymbolBytes + file: ./abis/ERC20SymbolBytes.json + - name: AaveOracle + file: ./abis/aave-v3/AaveOracle.json + - name: IPriceOracleGetter + file: ./abis/IPriceOracleGetter.json + - name: AToken + file: ./abis/aave-v3/AToken.json + - name: StableDebtToken + file: ./abis/aave-v3/StableDebtToken.json + - name: VariableDebtToken + file: ./abis/aave-v3/VariableDebtToken.json + eventHandlers: + - event: Borrow(indexed address,address,indexed address,uint256,uint8,uint256,indexed uint16) + handler: handleBorrow + - event: SwapBorrowRateMode(indexed address,indexed address,uint8) + handler: handleSwapBorrowRateMode + - event: LiquidationCall(indexed address,indexed address,indexed address,uint256,uint256,address,bool) + handler: handleLiquidationCall + receipt: true + - event: Repay(indexed address,indexed address,indexed address,uint256,bool) + handler: handleRepay + receipt: true + - event: ReserveDataUpdated(indexed address,uint256,uint256,uint256,uint256,uint256) + handler: handleReserveDataUpdated + receipt: true + - event: Supply(indexed address,address,indexed address,uint256,indexed uint16) + handler: handleDeposit + - event: Withdraw(indexed address,indexed address,indexed address,uint256) + handler: handleWithdraw + - event: ReserveUsedAsCollateralEnabled(indexed address,indexed address) + handler: handleReserveUsedAsCollateralEnabled + - event: ReserveUsedAsCollateralDisabled(indexed address,indexed address) + handler: handleReserveUsedAsCollateralDisabled + - event: FlashLoan(indexed address,address,indexed address,uint256,uint8,uint256,indexed uint16) + handler: handleFlashloan + - event: UserEModeSet(indexed address,uint8) + handler: handleUserEModeSet + - event: MintedToTreasury(indexed address,uint256) + handler: handleMintedToTreasury + file: ./protocols/superlend/src/mapping.ts +templates: + - kind: ethereum + name: AToken + network: {{ network }} + source: + abi: AToken + mapping: + kind: ethereum/events + apiVersion: 0.0.7 + language: wasm/assemblyscript + entities: [] + abis: + - name: AToken + file: ./abis/aave-v3/AToken.json + - name: ERC20 + file: ./abis/ERC20.json + eventHandlers: + - event: Transfer(indexed address,indexed address,uint256) + handler: handleCollateralTransfer + receipt: true + file: ./protocols/superlend/src/mapping.ts + - kind: ethereum + name: StableDebtToken + network: {{ network }} + source: + abi: StableDebtToken + mapping: + kind: ethereum/events + apiVersion: 0.0.7 + language: wasm/assemblyscript + entities: [] + abis: + - name: StableDebtToken + file: ./abis/aave-v3/StableDebtToken.json + - name: ERC20 + file: ./abis/ERC20.json + eventHandlers: + - event: Transfer(indexed address,indexed address,uint256) + handler: handleStableTransfer + file: ./protocols/superlend/src/mapping.ts + - kind: ethereum + name: VariableDebtToken + network: {{ network }} + source: + abi: VariableDebtToken + mapping: + kind: ethereum/events + apiVersion: 0.0.7 + language: wasm/assemblyscript + entities: [] + abis: + - name: VariableDebtToken + file: ./abis/aave-v3/VariableDebtToken.json + - name: ERC20 + file: ./abis/ERC20.json + eventHandlers: + - event: Transfer(indexed address,indexed address,uint256) + handler: handleVariableTransfer + file: ./protocols/superlend/src/mapping.ts diff --git a/subgraphs/aave-forks/protocols/superlend/src/constants.ts b/subgraphs/aave-forks/protocols/superlend/src/constants.ts new file mode 100644 index 0000000000..4a33667b1d --- /dev/null +++ b/subgraphs/aave-forks/protocols/superlend/src/constants.ts @@ -0,0 +1,71 @@ +import { + Address, + ByteArray, + crypto, + dataSource, + log, +} from "@graphprotocol/graph-ts"; +import { Network, ZERO_ADDRESS } from "../../../src/constants"; + +/////////////////////////////// +///// Etherlink Addresses ///// +/////////////////////////////// + +export const USDC_TOKEN_ADDRESS = "0x796ea11fa2dd751ed01b53c372ffdb4aaa8f00f9"; + +///////////////////////////// +///// Protocol Specific ///// +///////////////////////////// + +export namespace Protocol { + export const PROTOCOL = "Superlend"; + export const NAME = "Superlend v3"; + export const SLUG = "superlend-v3"; +} + +export const AAVE_DECIMALS = 8; + +export namespace InterestRateMode { + export const NONE = 0 as i32; + export const STABLE = 1 as i32; + export const VARIABLE = 2 as i32; +} + +//////////////////////////// +///// Network Specific ///// +//////////////////////////// + +export class NetworkSpecificConstant { + constructor( + public readonly protocolAddress: Address, // aka, PoolAddressesProviderRegistry + public readonly network: string + ) {} +} + +export function getNetworkSpecificConstant(): NetworkSpecificConstant { + const network = dataSource.network(); + if (equalsIgnoreCase(network, Network.ETHERLINK_MAINNET)) { + return new NetworkSpecificConstant( + Address.fromString("0xa5cf001755d54e5e84a45757e1637f29f0a19f2f"), + Network.ETHERLINK_MAINNET + ); + } else { + log.error("[getNetworkSpecificConstant] Unsupported network: {}", [ + network, + ]); + return new NetworkSpecificConstant(Address.fromString(ZERO_ADDRESS), ""); + } +} + +export function equalsIgnoreCase(a: string, b: string): boolean { + return a.replace("-", "_").toLowerCase() == b.replace("-", "_").toLowerCase(); +} + +// Context keys +export const PROTOCOL_ID_KEY = "protocolId"; +export const POOL_ADDRESSES_PROVIDER_ID_KEY = "poolAddressesProviderId"; + +export const BALANCE_TRANSFER_SIGNATURE = crypto.keccak256( + ByteArray.fromUTF8("BalanceTransfer(address,address,uint256,uint256)") +); +export const BALANCE_TRANSFER_DATA_TYPE = "(uint256,uint256)"; diff --git a/subgraphs/aave-forks/protocols/superlend/src/mapping.ts b/subgraphs/aave-forks/protocols/superlend/src/mapping.ts new file mode 100644 index 0000000000..ba62545680 --- /dev/null +++ b/subgraphs/aave-forks/protocols/superlend/src/mapping.ts @@ -0,0 +1,766 @@ +import { + Address, + BigDecimal, + BigInt, + ByteArray, + Bytes, + crypto, + dataSource, + ethereum, + log, +} from "@graphprotocol/graph-ts"; +import { PriceOracleUpdated } from "../../../generated/LendingPoolAddressesProvider/LendingPoolAddressesProvider"; +// import { AssetConfigUpdated } from "../../../generated/RewardsController/RewardsController"; +import { Transfer as CollateralTransfer } from "../../../generated/templates/AToken/AToken"; +import { Transfer as StableTransfer } from "../../../generated/templates/StableDebtToken/StableDebtToken"; +import { Transfer as VariableTransfer } from "../../../generated/templates/VariableDebtToken/VariableDebtToken"; +import { AaveOracle } from "../../../generated/LendingPool/AaveOracle"; +import { + CollateralConfigurationChanged, + ReserveFactorChanged, + ReserveActive, + ReserveBorrowing, + ReserveFrozen, + ReserveInitialized, + ReservePaused, + LiquidationProtocolFeeChanged, + FlashloanPremiumTotalUpdated, + FlashloanPremiumToProtocolUpdated, + SiloedBorrowingChanged, +} from "../../../generated/LendingPoolConfigurator/LendingPoolConfigurator"; +import { + Borrow, + FlashLoan, + LendingPool as LendingPoolContract, + LiquidationCall, + Repay, + ReserveDataUpdated, + ReserveUsedAsCollateralDisabled, + ReserveUsedAsCollateralEnabled, + Supply, + SwapBorrowRateMode, + Withdraw, + UserEModeSet, + MintedToTreasury, +} from "../../../generated/LendingPool/LendingPool"; +import { Market, _DefaultOracle } from "../../../generated/schema"; +import { + AAVE_DECIMALS, + getNetworkSpecificConstant, + InterestRateMode, + Protocol, + BALANCE_TRANSFER_DATA_TYPE, + BALANCE_TRANSFER_SIGNATURE, + equalsIgnoreCase, + USDC_TOKEN_ADDRESS, +} from "./constants"; +import { + // _handleAssetConfigUpdated, + _handleBorrow, + _handleBorrowingDisabledOnReserve, + _handleBorrowingEnabledOnReserve, + _handleCollateralConfigurationChanged, + _handleDeposit, + _handleFlashLoan, + _handleFlashloanPremiumToProtocolUpdated, + _handleFlashloanPremiumTotalUpdated, + _handleLiquidate, + _handleLiquidationProtocolFeeChanged, + _handleMintedToTreasury, + _handlePriceOracleUpdated, + _handleRepay, + _handleReserveActivated, + _handleReserveDataUpdated, + _handleReserveDeactivated, + _handleReserveFactorChanged, + _handleReserveInitialized, + _handleReserveUsedAsCollateralDisabled, + _handleReserveUsedAsCollateralEnabled, + _handleSwapBorrowRateMode, + _handleTransfer, + _handleWithdraw, +} from "../../../src/mapping"; +import { + BIGDECIMAL_ZERO, + BIGINT_ONE_RAY, + BIGINT_TEN, + BIGINT_ZERO, + INT_FOUR, + INT_ONE, + INT_ZERO, + Network, +} from "../../../src/constants"; + +import { DataManager, ProtocolData } from "../../../src/sdk/manager"; +import { + readValue, + getMarketFromToken, + exponentToBigDecimal, + getOrCreateFlashloanPremium, + getBorrowBalances, +} from "../../../src/helpers"; +import { + LendingType, + CollateralizationType, + PermissionType, + RiskType, + InterestRateType, + PositionSide, + INT_TEN, + INT_152, + INT_THIRTY_TWO, +} from "../../../src/sdk/constants"; +import { AccountManager } from "../../../src/sdk/account"; +import { IPriceOracleGetter } from "../../../generated/LendingPool/IPriceOracleGetter"; + +function getProtocolData(): ProtocolData { + const constants = getNetworkSpecificConstant(); + return new ProtocolData( + constants.protocolAddress, + Protocol.PROTOCOL, + Protocol.NAME, + Protocol.SLUG, + constants.network, + LendingType.POOLED, + PermissionType.PERMISSIONLESS, + PermissionType.PERMISSIONLESS, + PermissionType.ADMIN, + CollateralizationType.OVER_COLLATERALIZED, + RiskType.GLOBAL + ); +} + +const protocolData = getProtocolData(); + +//////////////////////////////////////// +///// PoolAddressProvider Handlers ///// +//////////////////////////////////////// + +export function handlePriceOracleUpdated(event: PriceOracleUpdated): void { + _handlePriceOracleUpdated(event.params.newAddress, protocolData, event); +} + +///////////////////////////////////// +///// RewardController Handlers ///// +///////////////////////////////////// + +// export function handleAssetConfigUpdated(event: AssetConfigUpdated): void { +// // it is not clear which market.oracle shouild we use +// // use the protocol-wide defaultOracle +// const defaultOracle = _DefaultOracle.load(protocolData.protocolID); +// let rewardTokenPriceUSD = BIGDECIMAL_ZERO; +// if (!defaultOracle || !defaultOracle.oracle) { +// log.warning( +// "[handleAssetConfigUpdated]_DefaultOracle for {} not set; rewardTokenPriceUSD set to default 0.0", +// [protocolData.protocolID.toHexString()] +// ); +// } else { +// rewardTokenPriceUSD = getAssetPriceInUSDC( +// event.params.reward, +// Address.fromBytes(defaultOracle.oracle) +// ); +// } + +// _handleAssetConfigUpdated( +// event, +// event.params.asset, +// event.params.reward, +// rewardTokenPriceUSD, +// event.params.newEmission, +// event.params.newDistributionEnd, +// protocolData +// ); +// } + +///////////////////////////////////// +///// PoolConfigurator Handlers ///// +///////////////////////////////////// + +export function handleReserveInitialized(event: ReserveInitialized): void { + // This function handles market entity from reserve creation event + // Attempt to load or create the market implementation + + _handleReserveInitialized( + event, + event.params.asset, + event.params.aToken, + event.params.variableDebtToken, + protocolData, + event.params.stableDebtToken + ); +} + +export function handleCollateralConfigurationChanged( + event: CollateralConfigurationChanged +): void { + _handleCollateralConfigurationChanged( + event.params.asset, + event.params.liquidationBonus, + event.params.liquidationThreshold, + event.params.ltv, + protocolData + ); +} + +export function handleReserveActive(event: ReserveActive): void { + _handleReserveActivated(event.params.asset, protocolData); +} + +export function handleReserveBorrowing(event: ReserveBorrowing): void { + if (event.params.enabled) { + _handleBorrowingEnabledOnReserve(event.params.asset, protocolData); + } else { + _handleBorrowingDisabledOnReserve(event.params.asset, protocolData); + } +} + +export function handleReserveFrozen(event: ReserveFrozen): void { + _handleReserveDeactivated(event.params.asset, protocolData); +} + +export function handleReservePaused(event: ReservePaused): void { + _handleReserveDeactivated(event.params.asset, protocolData); +} + +export function handleReserveFactorChanged(event: ReserveFactorChanged): void { + _handleReserveFactorChanged( + event.params.asset, + event.params.newReserveFactor.times(BIGINT_TEN), + protocolData + ); +} + +export function handleLiquidationProtocolFeeChanged( + event: LiquidationProtocolFeeChanged +): void { + _handleLiquidationProtocolFeeChanged( + event.params.asset, + event.params.newFee, + protocolData + ); +} + +export function handleFlashloanPremiumTotalUpdated( + event: FlashloanPremiumTotalUpdated +): void { + const rate = event.params.newFlashloanPremiumTotal + .toBigDecimal() + .div(exponentToBigDecimal(INT_FOUR)); + + _handleFlashloanPremiumTotalUpdated(rate, protocolData); +} + +export function handleFlashloanPremiumToProtocolUpdated( + event: FlashloanPremiumToProtocolUpdated +): void { + const rate = event.params.newFlashloanPremiumToProtocol + .toBigDecimal() + .div(exponentToBigDecimal(INT_FOUR)); + _handleFlashloanPremiumToProtocolUpdated(rate, protocolData); +} + +///////////////////////////////// +///// Lending Pool Handlers ///// +///////////////////////////////// + +export function handleReserveDataUpdated(event: ReserveDataUpdated): void { + const market = getMarketFromToken(event.params.reserve, protocolData); + if (!market) { + log.warning("[handleReserveDataUpdated] Market not found for reserve {}", [ + event.params.reserve.toHexString(), + ]); + return; + } + const manager = new DataManager( + market.id, + market.inputToken, + event, + protocolData + ); + + const assetPriceUSD = getAssetPriceInUSDC( + Address.fromBytes(market.inputToken), + manager.getOracleAddress(), + event.transaction.hash.toHexString() + ); + + _handleReserveDataUpdated( + event, + event.params.liquidityRate, + event.params.liquidityIndex, + event.params.variableBorrowIndex, + event.params.variableBorrowRate, + event.params.stableBorrowRate, + protocolData, + event.params.reserve, + assetPriceUSD + ); +} + +export function handleReserveUsedAsCollateralEnabled( + event: ReserveUsedAsCollateralEnabled +): void { + // This Event handler enables a reserve/market to be used as collateral + _handleReserveUsedAsCollateralEnabled( + event.params.reserve, + event.params.user, + protocolData + ); +} + +export function handleReserveUsedAsCollateralDisabled( + event: ReserveUsedAsCollateralDisabled +): void { + // This Event handler disables a reserve/market being used as collateral + _handleReserveUsedAsCollateralDisabled( + event.params.reserve, + event.params.user, + protocolData + ); +} + +export function handleDeposit(event: Supply): void { + _handleDeposit( + event, + event.params.amount, + event.params.reserve, + protocolData, + event.params.onBehalfOf + ); +} + +export function handleWithdraw(event: Withdraw): void { + _handleWithdraw( + event, + event.params.amount, + event.params.reserve, + protocolData, + event.params.user + ); +} + +export function handleBorrow(event: Borrow): void { + // determine whether the borrow position is in isolated mode + // borrow in isolated mode will have an IsolationModeTotalDebtUpdated event emitted + // before the Borrow event + // https://github.com/aave/aave-v3-core/blob/29ff9b9f89af7cd8255231bc5faf26c3ce0fb7ce/contracts/protocol/libraries/logic/BorrowLogic.sol#L139 + let isIsolated = false; + const receipt = event.receipt; + if (!receipt) { + log.warning( + "[handleBorrow]No receipt for tx {}; cannot set isIsolated flag", + [event.transaction.hash.toHexString()] + ); + } else { + isIsolated = getIsIsolatedFlag(event); + } + + let interestRateType: InterestRateType | null = null; + if (event.params.interestRateMode === InterestRateMode.STABLE) { + interestRateType = InterestRateType.STABLE; + } else if (event.params.interestRateMode === InterestRateMode.VARIABLE) { + interestRateType = InterestRateType.VARIABLE; + } + + _handleBorrow( + event, + event.params.amount, + event.params.reserve, + protocolData, + event.params.onBehalfOf, + interestRateType, + isIsolated + ); +} + +export function handleRepay(event: Repay): void { + _handleRepay( + event, + event.params.amount, + event.params.reserve, + protocolData, + event.params.user + ); +} + +export function handleLiquidationCall(event: LiquidationCall): void { + const collateralMarket = getMarketFromToken( + event.params.collateralAsset, + protocolData + ); + if (!collateralMarket) { + log.error("[handleLiquidationCall]Failed to find market for asset {}", [ + event.params.collateralAsset.toHexString(), + ]); + return; + } + + if (!collateralMarket._liquidationProtocolFee) { + storeLiquidationProtocolFee( + collateralMarket, + event.address, + event.params.collateralAsset + ); + } + + let balanceTransferValue = BIGINT_ZERO; + let balanceTransferIndex = BIGINT_ZERO; + if (event.receipt) { + const logs = event.receipt!.logs; + for (let i = 0; i < logs.length; i++) { + const thisLog = logs[i]; + if (!thisLog.topics.length) continue; + + if (thisLog.topics[0] == BALANCE_TRANSFER_SIGNATURE) { + const decoded = ethereum.decode( + BALANCE_TRANSFER_DATA_TYPE, + thisLog.data + ); + if (!decoded) return; + + const logData = decoded.toTuple(); + balanceTransferValue = logData[INT_ZERO].toBigInt(); + balanceTransferIndex = logData[INT_ONE].toBigInt(); + } + } + } + const balanceTransferAmount = balanceTransferValue.times( + balanceTransferIndex.div(BIGINT_ONE_RAY) + ); + + _handleLiquidate( + event, + event.params.liquidatedCollateralAmount, + event.params.collateralAsset, + protocolData, + event.params.liquidator, + event.params.user, + event.params.debtAsset, + event.params.debtToCover, + balanceTransferAmount + ); +} + +export function handleFlashloan(event: FlashLoan): void { + const flashloanPremium = getOrCreateFlashloanPremium(protocolData); + + _handleFlashLoan( + event.params.asset, + event.params.amount, + event.params.initiator, + protocolData, + event, + event.params.premium, + flashloanPremium + ); +} + +export function handleMintedToTreasury(event: MintedToTreasury): void { + _handleMintedToTreasury( + event, + protocolData, + event.params.reserve, + event.params.amountMinted + ); +} + +///////////////////////// +//// Transfer Events //// +///////////////////////// + +export function handleCollateralTransfer(event: CollateralTransfer): void { + // determine the transfer amount because different versions of the AToken contract + // pass discounted and undiscounted amount to Transfer() and BalanceTransfer() event + // here we get the higher of the two amount and use it as the transfer amount + // e.g. https://arbiscan.io/tx/0x7ee837a19f37f0f74acb75be2eb07de85adcf1fcca1b66e8d2118958ce4fe8a1#eventlog + // logIndex 18 and 21 + let amount = event.params.value; + const receipt = event.receipt; + if (!receipt) { + log.warning("[handleBorrow]No receipt for tx {}", [ + event.transaction.hash.toHexString(), + ]); + } else { + const btAmount = getBalanceTransferAmount(event); + amount = btAmount.gt(amount) ? btAmount : amount; + } + + _handleTransfer( + event, + protocolData, + PositionSide.COLLATERAL, + event.params.to, + event.params.from, + amount + ); +} + +export function handleVariableTransfer(event: VariableTransfer): void { + _handleTransfer( + event, + protocolData, + PositionSide.BORROWER, + event.params.to, + event.params.from, + event.params.value + ); +} + +export function handleStableTransfer(event: StableTransfer): void { + _handleTransfer( + event, + protocolData, + PositionSide.BORROWER, + event.params.to, + event.params.from, + event.params.value + ); +} + +export function handleSwapBorrowRateMode(event: SwapBorrowRateMode): void { + const interestRateMode = event.params.interestRateMode; + if ( + ![InterestRateMode.STABLE, InterestRateMode.VARIABLE].includes( + event.params.interestRateMode + ) + ) { + log.error( + "[handleSwapBorrowRateMode]interestRateMode {} is not one of [{}, {}]", + [ + interestRateMode.toString(), + InterestRateMode.STABLE.toString(), + InterestRateMode.VARIABLE.toString(), + ] + ); + return; + return; + } + const interestRateType = + event.params.interestRateMode === InterestRateMode.STABLE + ? InterestRateType.STABLE + : InterestRateType.VARIABLE; + const market = getMarketFromToken(event.params.reserve, protocolData); + if (!market) { + log.error("[handleLiquidationCall]Failed to find market for asset {}", [ + event.params.reserve.toHexString(), + ]); + return; + } + const newBorrowBalances = getBorrowBalances(market, event.params.user); + _handleSwapBorrowRateMode( + event, + market, + event.params.user, + newBorrowBalances, + interestRateType, + protocolData + ); +} + +export function handleSiloedBorrowingChanged( + event: SiloedBorrowingChanged +): void { + const market = getMarketFromToken(event.params.asset, protocolData); + if (!market) { + log.error("[handleSiloedBorrowingChanged]market not found for token {}", [ + event.params.asset.toHexString(), + ]); + return; + } + market._siloedBorrowing = event.params.newState; + market.save(); +} + +export function handleUserEModeSet(event: UserEModeSet): void { + const account = new AccountManager(event.params.user).getAccount(); + account._eMode = true; + account.save(); +} + +/////////////////// +///// Helpers ///// +/////////////////// + +function getAssetPriceInUSDC( + tokenAddress: Address, + priceOracle: Address, + hash: string +): BigDecimal { + const oracle = AaveOracle.bind(priceOracle); + let oracleResult = readValue( + oracle.try_getAssetPrice(tokenAddress), + BIGINT_ZERO + ); + + // if the result is zero or less, try the fallback oracle + if (!oracleResult.gt(BIGINT_ZERO)) { + const tryFallback = oracle.try_getFallbackOracle(); + if (tryFallback) { + const fallbackOracle = IPriceOracleGetter.bind(tryFallback.value); + oracleResult = readValue( + fallbackOracle.try_getAssetPrice(tokenAddress), + BIGINT_ZERO + ); + } + } + + // if (equalsIgnoreCase(dataSource.network(), Network.ETHERLINK_MAINNET)) { + const priceUSDCInEth = readValue( + oracle.try_getAssetPrice(Address.fromString(USDC_TOKEN_ADDRESS)), + BIGINT_ZERO + ); + + // if (priceUSDCInEth.equals(BIGINT_ZERO)) { + // return BIGDECIMAL_ZERO; + // } else { + const retval1 = oracleResult + .toBigDecimal() + .div(priceUSDCInEth.toBigDecimal()); + + // return retval; + // } + // } + + const retval2 = oracleResult + .toBigDecimal() + .div(exponentToBigDecimal(AAVE_DECIMALS)); + + log.warning( + "[getAssetPriceInUSDC] token: {} res: {} usdcineth: {} r1: {} r2: {} tx: {}", + [ + tokenAddress.toHexString(), + oracleResult.toString(), + priceUSDCInEth.toString(), + retval1.toString(), + retval2.toString(), + hash, + ] + ); + + return retval2; + // return BIGDECIMAL_ZERO; +} + +function storeLiquidationProtocolFee( + market: Market, + poolAddress: Address, + reserve: Address +): void { + // Store LiquidationProtocolFee if not set, as setLiquidationProtocolFee() may be never called + // and no LiquidationProtocolFeeChanged event is emitted + // see https://github.com/aave/aave-v3-core/blob/1e46f1cbb7ace08995cb4c8fa4e4ece96a243be3/contracts/protocol/libraries/configuration/ReserveConfiguration.sol#L491 + // for how to decode configuration data to get _liquidationProtocolFee + const liquidationProtocolFeeMask = + "0xFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"; + const liquidationProtocolFeeStartBitPosition = INT_152 as u8; + const pool = LendingPoolContract.bind(poolAddress); + const poolConfigData = pool.getConfiguration(reserve).data; + const liquidationProtocolFee = decodeConfig( + poolConfigData, + liquidationProtocolFeeMask, + liquidationProtocolFeeStartBitPosition + ) + .toBigDecimal() + .div(exponentToBigDecimal(INT_FOUR)); + + log.info("[storeLiquidationProtocolFee]market {} liquidationProtocolFee={}", [ + market.id.toHexString(), + liquidationProtocolFee.toString(), + ]); + market._liquidationProtocolFee = liquidationProtocolFee; + market.save(); +} + +function decodeConfig( + storedData: BigInt, + maskStr: string, + startBitPosition: u8 +): BigInt { + // aave-v3 stores configuration in packed bits (ReserveConfiguration.sol) + // decoding them by applying a bit_not mask and right shift by startBitPosition + // see https://github.com/aave/aave-v3-core/blob/1e46f1cbb7ace08995cb4c8fa4e4ece96a243be3/contracts/protocol/libraries/configuration/ReserveConfiguration.sol#L491 + // for how to decode configuration data to get _liquidationProtocolFee + + const maskArray = new Uint8Array(INT_THIRTY_TWO); + maskArray.set(Bytes.fromHexString(maskStr)); + // BITWISE NOT + for (let i = 0; i < maskArray.length; i++) { + maskArray[i] = ~maskArray[i]; + } + // reverse for little endian + const configMaskBigInt = BigInt.fromUnsignedBytes( + Bytes.fromUint8Array(maskArray.reverse()) + ); + + const config = storedData + .bitAnd(configMaskBigInt) + .rightShift(startBitPosition); + + return config; +} + +function getIsIsolatedFlag(event: ethereum.Event): boolean { + let isIsolated = false; + const ISOLATE_MODE = "IsolationModeTotalDebtUpdated(address,uint256)"; + const eventSignature = crypto.keccak256(ByteArray.fromUTF8(ISOLATE_MODE)); + const logs = event.receipt!.logs; + //IsolationModeTotalDebtUpdated emitted before Borrow's event.logIndex + // e.g. https://etherscan.io/tx/0x4b038b26555d4b6c057cd612057b39e6482a7c60eb44058ee61d299332efdf29#eventlog + const eventLogIndex = event.logIndex; + for (let i = 0; i < logs.length; i++) { + const thisLog = logs[i]; + if (thisLog.topics.length > INT_ZERO) { + if (thisLog.logIndex.gt(eventLogIndex)) { + // no IsolationModeTotalDebtUpdated log before Borrow + break; + } + // topics[0] - signature + const logSignature = thisLog.topics[0]; + if (thisLog.address == event.address && logSignature == eventSignature) { + log.info( + "[getIsIsolatedFlag]found IsolationModeTotalDebtUpdated event isolated=true tx {}", + [event.transaction.hash.toHexString()] + ); + isIsolated = true; + break; + } + } + } + return isIsolated; +} + +function getBalanceTransferAmount(event: ethereum.Event): BigInt { + let btAmount = BIGINT_ZERO; + const BALANCE_TRANSFER = + "BalanceTransfer(address, address, uint256, uint256)"; + const eventSignature = crypto.keccak256(ByteArray.fromUTF8(BALANCE_TRANSFER)); + const logs = event.receipt!.logs; + // BalanceTransfer emitted after Transfer's event.logIndex + // e.g. https://arbiscan.io/tx/0x7ee837a19f37f0f74acb75be2eb07de85adcf1fcca1b66e8d2118958ce4fe8a1#eventlog + const eventLogIndex = event.logIndex; + for (let i = 0; i < logs.length; i++) { + const thisLog = logs[i]; + if (thisLog.topics.length > INT_ZERO) { + if (thisLog.logIndex.le(eventLogIndex)) { + // skip event with logIndex < event.logIndex + continue; + } + // topics[0] - signature + const logSignature = thisLog.topics[0]; + if (thisLog.address == event.address && logSignature == eventSignature) { + const UINT256_UINT256 = "(uint256,uint256)"; + const decoded = ethereum.decode(UINT256_UINT256, thisLog.data); + if (!decoded) continue; + + const logData = decoded.toTuple(); + btAmount = logData[0].toBigInt(); + log.info( + "[handleCollateralTransfer] BalanceTransfer amount= {} tx {}", + [btAmount.toString(), event.transaction.hash.toHexString()] + ); + break; + } + } + } + return btAmount; +} diff --git a/subgraphs/aave-forks/schema.graphql b/subgraphs/aave-forks/schema.graphql index f37d1c2bc9..f8cc8c9f1d 100644 --- a/subgraphs/aave-forks/schema.graphql +++ b/subgraphs/aave-forks/schema.graphql @@ -31,6 +31,7 @@ enum Network { SCROLL XLAYER_MAINNET ZKSYNC_ERA + ETHERLINK_MAINNET } enum ProtocolType { diff --git a/subgraphs/aave-forks/src/constants.ts b/subgraphs/aave-forks/src/constants.ts index d5e51c0387..2058a3c7e9 100644 --- a/subgraphs/aave-forks/src/constants.ts +++ b/subgraphs/aave-forks/src/constants.ts @@ -33,6 +33,7 @@ export namespace Network { export const SCROLL = "SCROLL"; export const XLAYER_MAINNET = "XLAYER_MAINNET"; export const ZKSYNC_ERA = "ZKSYNC_ERA"; + export const ETHERLINK_MAINNET = "ETHERLINK_MAINNET"; } export namespace ProtocolType { From 52b22f28a65e10313e01bce74e3631fbc61aee7e Mon Sep 17 00:00:00 2001 From: Dhruv Chauhan Date: Mon, 6 Jan 2025 15:35:22 +0530 Subject: [PATCH 2/4] nit: fix lint --- .../protocols/superlend/src/mapping.ts | 39 ------------------- 1 file changed, 39 deletions(-) diff --git a/subgraphs/aave-forks/protocols/superlend/src/mapping.ts b/subgraphs/aave-forks/protocols/superlend/src/mapping.ts index ba62545680..e4ffc81294 100644 --- a/subgraphs/aave-forks/protocols/superlend/src/mapping.ts +++ b/subgraphs/aave-forks/protocols/superlend/src/mapping.ts @@ -5,12 +5,10 @@ import { ByteArray, Bytes, crypto, - dataSource, ethereum, log, } from "@graphprotocol/graph-ts"; import { PriceOracleUpdated } from "../../../generated/LendingPoolAddressesProvider/LendingPoolAddressesProvider"; -// import { AssetConfigUpdated } from "../../../generated/RewardsController/RewardsController"; import { Transfer as CollateralTransfer } from "../../../generated/templates/AToken/AToken"; import { Transfer as StableTransfer } from "../../../generated/templates/StableDebtToken/StableDebtToken"; import { Transfer as VariableTransfer } from "../../../generated/templates/VariableDebtToken/VariableDebtToken"; @@ -51,11 +49,9 @@ import { Protocol, BALANCE_TRANSFER_DATA_TYPE, BALANCE_TRANSFER_SIGNATURE, - equalsIgnoreCase, USDC_TOKEN_ADDRESS, } from "./constants"; import { - // _handleAssetConfigUpdated, _handleBorrow, _handleBorrowingDisabledOnReserve, _handleBorrowingEnabledOnReserve, @@ -81,14 +77,12 @@ import { _handleWithdraw, } from "../../../src/mapping"; import { - BIGDECIMAL_ZERO, BIGINT_ONE_RAY, BIGINT_TEN, BIGINT_ZERO, INT_FOUR, INT_ONE, INT_ZERO, - Network, } from "../../../src/constants"; import { DataManager, ProtocolData } from "../../../src/sdk/manager"; @@ -106,7 +100,6 @@ import { RiskType, InterestRateType, PositionSide, - INT_TEN, INT_152, INT_THIRTY_TWO, } from "../../../src/sdk/constants"; @@ -140,38 +133,6 @@ export function handlePriceOracleUpdated(event: PriceOracleUpdated): void { _handlePriceOracleUpdated(event.params.newAddress, protocolData, event); } -///////////////////////////////////// -///// RewardController Handlers ///// -///////////////////////////////////// - -// export function handleAssetConfigUpdated(event: AssetConfigUpdated): void { -// // it is not clear which market.oracle shouild we use -// // use the protocol-wide defaultOracle -// const defaultOracle = _DefaultOracle.load(protocolData.protocolID); -// let rewardTokenPriceUSD = BIGDECIMAL_ZERO; -// if (!defaultOracle || !defaultOracle.oracle) { -// log.warning( -// "[handleAssetConfigUpdated]_DefaultOracle for {} not set; rewardTokenPriceUSD set to default 0.0", -// [protocolData.protocolID.toHexString()] -// ); -// } else { -// rewardTokenPriceUSD = getAssetPriceInUSDC( -// event.params.reward, -// Address.fromBytes(defaultOracle.oracle) -// ); -// } - -// _handleAssetConfigUpdated( -// event, -// event.params.asset, -// event.params.reward, -// rewardTokenPriceUSD, -// event.params.newEmission, -// event.params.newDistributionEnd, -// protocolData -// ); -// } - ///////////////////////////////////// ///// PoolConfigurator Handlers ///// ///////////////////////////////////// From 3eff1b193b7690828a7f3b4810c3ed275afc3715 Mon Sep 17 00:00:00 2001 From: Dhruv Chauhan Date: Mon, 6 Jan 2025 15:42:15 +0530 Subject: [PATCH 3/4] nit: fix lint --- .../protocols/superlend/src/mapping.ts | 44 ++----------------- 1 file changed, 4 insertions(+), 40 deletions(-) diff --git a/subgraphs/aave-forks/protocols/superlend/src/mapping.ts b/subgraphs/aave-forks/protocols/superlend/src/mapping.ts index e4ffc81294..f0bd60a8bf 100644 --- a/subgraphs/aave-forks/protocols/superlend/src/mapping.ts +++ b/subgraphs/aave-forks/protocols/superlend/src/mapping.ts @@ -41,7 +41,7 @@ import { UserEModeSet, MintedToTreasury, } from "../../../generated/LendingPool/LendingPool"; -import { Market, _DefaultOracle } from "../../../generated/schema"; +import { Market } from "../../../generated/schema"; import { AAVE_DECIMALS, getNetworkSpecificConstant, @@ -241,8 +241,7 @@ export function handleReserveDataUpdated(event: ReserveDataUpdated): void { const assetPriceUSD = getAssetPriceInUSDC( Address.fromBytes(market.inputToken), - manager.getOracleAddress(), - event.transaction.hash.toHexString() + manager.getOracleAddress() ); _handleReserveDataUpdated( @@ -544,8 +543,7 @@ export function handleUserEModeSet(event: UserEModeSet): void { function getAssetPriceInUSDC( tokenAddress: Address, - priceOracle: Address, - hash: string + priceOracle: Address ): BigDecimal { const oracle = AaveOracle.bind(priceOracle); let oracleResult = readValue( @@ -565,41 +563,7 @@ function getAssetPriceInUSDC( } } - // if (equalsIgnoreCase(dataSource.network(), Network.ETHERLINK_MAINNET)) { - const priceUSDCInEth = readValue( - oracle.try_getAssetPrice(Address.fromString(USDC_TOKEN_ADDRESS)), - BIGINT_ZERO - ); - - // if (priceUSDCInEth.equals(BIGINT_ZERO)) { - // return BIGDECIMAL_ZERO; - // } else { - const retval1 = oracleResult - .toBigDecimal() - .div(priceUSDCInEth.toBigDecimal()); - - // return retval; - // } - // } - - const retval2 = oracleResult - .toBigDecimal() - .div(exponentToBigDecimal(AAVE_DECIMALS)); - - log.warning( - "[getAssetPriceInUSDC] token: {} res: {} usdcineth: {} r1: {} r2: {} tx: {}", - [ - tokenAddress.toHexString(), - oracleResult.toString(), - priceUSDCInEth.toString(), - retval1.toString(), - retval2.toString(), - hash, - ] - ); - - return retval2; - // return BIGDECIMAL_ZERO; + return oracleResult.toBigDecimal().div(exponentToBigDecimal(AAVE_DECIMALS)); } function storeLiquidationProtocolFee( From 9493b74b57fd73952edd3c31d2c05faab0cf770f Mon Sep 17 00:00:00 2001 From: Dhruv Chauhan Date: Mon, 6 Jan 2025 15:43:49 +0530 Subject: [PATCH 4/4] nit: fix lint --- subgraphs/aave-forks/protocols/superlend/src/mapping.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/subgraphs/aave-forks/protocols/superlend/src/mapping.ts b/subgraphs/aave-forks/protocols/superlend/src/mapping.ts index f0bd60a8bf..954cfddffd 100644 --- a/subgraphs/aave-forks/protocols/superlend/src/mapping.ts +++ b/subgraphs/aave-forks/protocols/superlend/src/mapping.ts @@ -49,7 +49,6 @@ import { Protocol, BALANCE_TRANSFER_DATA_TYPE, BALANCE_TRANSFER_SIGNATURE, - USDC_TOKEN_ADDRESS, } from "./constants"; import { _handleBorrow,