diff --git a/abis/PublicAllocator.json b/abis/PublicAllocator.json new file mode 100644 index 0000000..36bb4da --- /dev/null +++ b/abis/PublicAllocator.json @@ -0,0 +1,579 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "morpho", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AlreadySet", + "type": "error" + }, + { + "inputs": [], + "name": "DepositMarketInWithdrawals", + "type": "error" + }, + { + "inputs": [], + "name": "EmptyWithdrawals", + "type": "error" + }, + { + "inputs": [], + "name": "InconsistentWithdrawals", + "type": "error" + }, + { + "inputs": [], + "name": "IncorrectFee", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "Id", + "name": "id", + "type": "bytes32" + } + ], + "name": "MarketNotEnabled", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "Id", + "name": "id", + "type": "bytes32" + } + ], + "name": "MaxInflowExceeded", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "Id", + "name": "id", + "type": "bytes32" + } + ], + "name": "MaxOutflowExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "MaxSettableFlowCapExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "NotAdminNorVaultOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "Id", + "name": "id", + "type": "bytes32" + } + ], + "name": "NotEnoughSupply", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "Id", + "name": "id", + "type": "bytes32" + } + ], + "name": "WithdrawZero", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "vault", + "type": "address" + }, + { + "indexed": true, + "internalType": "Id", + "name": "supplyMarketId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "suppliedAssets", + "type": "uint256" + } + ], + "name": "PublicReallocateTo", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "vault", + "type": "address" + }, + { + "indexed": true, + "internalType": "Id", + "name": "id", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "withdrawnAssets", + "type": "uint256" + } + ], + "name": "PublicWithdrawal", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "vault", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "admin", + "type": "address" + } + ], + "name": "SetAdmin", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "vault", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "SetFee", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "vault", + "type": "address" + }, + { + "components": [ + { + "internalType": "Id", + "name": "id", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "maxIn", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "maxOut", + "type": "uint128" + } + ], + "internalType": "struct FlowCaps", + "name": "caps", + "type": "tuple" + } + ], + "indexed": false, + "internalType": "struct FlowCapsConfig[]", + "name": "config", + "type": "tuple[]" + } + ], + "name": "SetFlowCaps", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "vault", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "feeRecipient", + "type": "address" + } + ], + "name": "TransferFee", + "type": "event" + }, + { + "inputs": [], + "name": "MORPHO", + "outputs": [ + { + "internalType": "contract IMorpho", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "accruedFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "fee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "Id", + "name": "", + "type": "bytes32" + } + ], + "name": "flowCaps", + "outputs": [ + { + "internalType": "uint128", + "name": "maxIn", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "maxOut", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vault", + "type": "address" + }, + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "loanToken", + "type": "address" + }, + { + "internalType": "address", + "name": "collateralToken", + "type": "address" + }, + { + "internalType": "address", + "name": "oracle", + "type": "address" + }, + { + "internalType": "address", + "name": "irm", + "type": "address" + }, + { + "internalType": "uint256", + "name": "lltv", + "type": "uint256" + } + ], + "internalType": "struct MarketParams", + "name": "marketParams", + "type": "tuple" + }, + { + "internalType": "uint128", + "name": "amount", + "type": "uint128" + } + ], + "internalType": "struct Withdrawal[]", + "name": "withdrawals", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "address", + "name": "loanToken", + "type": "address" + }, + { + "internalType": "address", + "name": "collateralToken", + "type": "address" + }, + { + "internalType": "address", + "name": "oracle", + "type": "address" + }, + { + "internalType": "address", + "name": "irm", + "type": "address" + }, + { + "internalType": "uint256", + "name": "lltv", + "type": "uint256" + } + ], + "internalType": "struct MarketParams", + "name": "supplyMarketParams", + "type": "tuple" + } + ], + "name": "reallocateTo", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vault", + "type": "address" + }, + { + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "setAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vault", + "type": "address" + }, + { + "internalType": "uint256", + "name": "newFee", + "type": "uint256" + } + ], + "name": "setFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vault", + "type": "address" + }, + { + "components": [ + { + "internalType": "Id", + "name": "id", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "maxIn", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "maxOut", + "type": "uint128" + } + ], + "internalType": "struct FlowCaps", + "name": "caps", + "type": "tuple" + } + ], + "internalType": "struct FlowCapsConfig[]", + "name": "config", + "type": "tuple[]" + } + ], + "name": "setFlowCaps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vault", + "type": "address" + }, + { + "internalType": "address payable", + "name": "feeRecipient", + "type": "address" + } + ], + "name": "transferFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/networks.json b/networks.json index 9783836..32b220b 100644 --- a/networks.json +++ b/networks.json @@ -8,6 +8,10 @@ "MetaMorphoFactory": { "address": "0xa9c3d3a366466fa809d1ae982fb2c46e5fc41101", "startBlock": 18925584 + }, + "PublicAllocator": { + "address": "0xfd32fA2ca22c76dD6E550706Ad913FC6CE91c75D", + "startBlock": 19375099 } }, "base": { @@ -19,6 +23,10 @@ "MetaMorphoFactory": { "address": "0xa9c3d3a366466fa809d1ae982fb2c46e5fc41101", "startBlock": 13978134 + }, + "PublicAllocator": { + "address": "0xA090dD1a701408Df1d4d0B85b716c87565f90467", + "startBlock": 13979545 } } } \ No newline at end of file diff --git a/schema.graphql b/schema.graphql index 08f2483..e198ff4 100644 --- a/schema.graphql +++ b/schema.graphql @@ -1368,6 +1368,11 @@ type Account @entity @regularPolling { metaMorphoTransferFrom: [MetaMorphoTransfer!]! @derivedFrom(field: "from") metaMorphoTransferTo: [MetaMorphoTransfer!]! @derivedFrom(field: "to") + publicAllocatorAdmin: [MetaMorphoPublicAllocator!]! @derivedFrom(field: "admin") + publicAllocatorReallocateToAuthor: [PublicAllocatorReallocationToEvent!]! @derivedFrom(field: "author") + publicAllocatorWithdrawalAuthor: [PublicAllocatorWithdrawalEvent!]! @derivedFrom(field: "author") + publicAllocatorSetFlowCapsAuthor: [SetFlowCapsEvent!]! @derivedFrom(field: "author") + } @@ -2047,6 +2052,7 @@ type MetaMorpho @entity { owner: Account! curator: Account allocators: [MetaMorphoAllocator!]! @derivedFrom(field: "metaMorpho") + hasPublicAllocator: Boolean! guardian: Account timelock: BigInt! @@ -2082,6 +2088,9 @@ type MetaMorpho @entity { deposits: [MetaMorphoDeposit!]! @derivedFrom(field: "metaMorpho") withdraws: [MetaMorphoWithdraw!]! @derivedFrom(field: "metaMorpho") transfers: [MetaMorphoTransfer!]! @derivedFrom(field: "metaMorpho") + + publicAllocator: MetaMorphoPublicAllocator @derivedFrom(field: "metaMorpho") + } type FeeRecipient @entity { id: Bytes! @@ -2103,6 +2112,9 @@ type MetaMorphoMarket @entity { isInSupplyQueue: Boolean! isInWithdrawQueue: Boolean! pendingCaps: [PendingCap!]! @derivedFrom(field: "metaMorphoMarket") + + publicAllocatorMarket: MetaMorphoPublicAllocatorMarket @derivedFrom(field: "market") + # TODO: add position } @@ -2177,6 +2189,9 @@ type MetaMorphoAllocator @entity { account: Account! metaMorpho: MetaMorpho! isCurrentAllocator: Boolean! + isPublicAllocator: Boolean! + + publicAllocatorConfig: MetaMorphoPublicAllocator @derivedFrom(field: "allocator") } @@ -2373,4 +2388,233 @@ type MetaMorphoTransfer @entity(immutable: true) { rate: InterestRate! -} \ No newline at end of file +} + +type MetaMorphoPublicAllocator @entity { + " { MetaMorpho ID } " + id: Bytes! + + " The MetaMorpho associated with this public allocator vault configuration " + metaMorpho: MetaMorpho! + + " The allocator associated with this public allocator vault configuration" + allocator: MetaMorphoAllocator + + " The current fee per public reallocation " + fee: BigInt! + " The fee accrued by the admin " + accruedFee: BigInt! + " The fee that can be claimed by the admin " + claimableFee: BigInt! + " The fee claimed by the admin " + claimedFee: BigInt! + + " The current admin of the public allocator. Value is optional & fallbacks on the vault owner " + admin: Account + + " All the markets public allocator configurations. Values contained are the latest values " + markets: [MetaMorphoPublicAllocatorMarket!]! @derivedFrom(field: "metaMorphoPublicAllocator") + + " All the public flow caps changes for this MetaMorpho (on all markets) " + setFlowCapsEvents: [SetFlowCapsEvent!]! @derivedFrom(field: "metaMorphoPublicAllocator") + + " All the public flowIn modifications for this MetaMorpho (on all markets) " + reallocationToEvents: [PublicAllocatorReallocationToEvent!]! @derivedFrom(field: "metaMorphoPublicAllocator") + + " All the public flowOut modifications for this MetaMorpho (on all markets) " + withdrawalEvents: [PublicAllocatorWithdrawalEvent!]! @derivedFrom(field: "metaMorphoPublicAllocator") + + " All the flow caps changes for this MetaMorpho (on all markets) " + flowCapsChanges: [MarketFlowCapsSet!]! @derivedFrom(field: "metaMorphoPublicAllocator") + +} + +type MetaMorphoPublicAllocatorMarket @entity { + " { MetaMorpho ID }-{ Market ID } " + id: Bytes! + + " The MetaMorpho public allocator configuration. Values contained are the latest values " + metaMorphoPublicAllocator: MetaMorphoPublicAllocator! + " The MetaMorpho market associated with the public allocator " + market: MetaMorphoMarket! + + " The current flow cap in for this market for the given MetaMorpho " + flowCapIn: BigInt! + " The current flow cap out for this market for the given MetaMorpho " + flowCapOut: BigInt! + + " All the public flowIn modifications for this market for the given MetaMorpho " + reallocationToEvents: [PublicAllocatorReallocationToEvent!]! @derivedFrom(field: "marketPublicAllocator") + + " All the public flowOut modifications for this market for the given MetaMorpho " + withdrawalEvents: [PublicAllocatorWithdrawalEvent!]! @derivedFrom(field: "marketPublicAllocator") + + " All the flow caps changes for this market for the given MetaMorpho " + flowCapsChanges: [MarketFlowCapsSet!]! @derivedFrom(field: "marketPublicAllocator") + +} + +" The event emitted when the flow caps are set by the public allocator admin " +type SetFlowCapsEvent @entity(immutable: true) @transaction { + + " { Transaction hash }{ Log index } " + id: Bytes! + + " Transaction hash of the transaction that emitted this event " + hash: Bytes! + + " Nonce of the transaction that emitted this event " + nonce: BigInt! + + " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " + logIndex: Int! + + " Price of gas in this transaction " + gasPrice: BigInt + + " Gas used in this transaction. (Optional because not every chain will support this) " + gasUsed: BigInt + + " Gas limit of this transaction. e.g. the amount of gas the sender will pay " + gasLimit: BigInt + + " Block number of this event " + blockNumber: BigInt! + + " Timestamp of this event " + timestamp: BigInt! + + " Author of the Public allocator call " + author: Account! + + " The metaMorpho public allocator configuration. Values contained are the latest values " + metaMorphoPublicAllocator: MetaMorphoPublicAllocator! + + " The different flow caps set by this event. " + flowCaps: [MarketFlowCapsSet!]! @derivedFrom(field: "setFlowCapsEvent") +} + +" The event emitted when the public allocator reallocates assets to a market. This function is publicly accessible " +type PublicAllocatorReallocationToEvent @entity(immutable: true) @transaction { + + " { Transaction hash }{ Log index } " + id: Bytes! + + " Transaction hash of the transaction that emitted this event " + hash: Bytes! + + " Nonce of the transaction that emitted this event " + nonce: BigInt! + + " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " + logIndex: Int! + + " Price of gas in this transaction " + gasPrice: BigInt + + " Gas used in this transaction. (Optional because not every chain will support this) " + gasUsed: BigInt + + " Gas limit of this transaction. e.g. the amount of gas the sender will pay " + gasLimit: BigInt + + " Block number of this event " + blockNumber: BigInt! + + " Timestamp of this event " + timestamp: BigInt! + + " Author of the Public allocator call " + author: Account! + + " The metaMorpho public allocator configuration. Values contained are the latest values " + metaMorphoPublicAllocator: MetaMorphoPublicAllocator! + + " The latest market public allocator configuration. Values contained are the latest values " + marketPublicAllocator: MetaMorphoPublicAllocatorMarket! + + " The amount of assets supplied " + suppliedAssets: BigInt! + + " The flow caps modification set by this event " + flowCaps: MarketFlowCapsSet! @derivedFrom(field: "publicReallocationEvent") +} + +" The event emitted when the public allocator withdraws assets from a market. This function is publicly accessible " +type PublicAllocatorWithdrawalEvent @entity(immutable: true) @transaction { + + " { Transaction hash }{ Log index } " + id: Bytes! + + " Transaction hash of the transaction that emitted this event " + hash: Bytes! + + " Nonce of the transaction that emitted this event " + nonce: BigInt! + + " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " + logIndex: Int! + + " Price of gas in this transaction " + gasPrice: BigInt + + " Gas used in this transaction. (Optional because not every chain will support this) " + gasUsed: BigInt + + " Gas limit of this transaction. e.g. the amount of gas the sender will pay " + gasLimit: BigInt + + " Block number of this event " + blockNumber: BigInt! + + " Timestamp of this event " + timestamp: BigInt! + + " Author of the Public allocator call " + author: Account! + + " The metaMorpho public allocator configuration. Values contained are the latest values " + metaMorphoPublicAllocator: MetaMorphoPublicAllocator + + " The latest market public allocator configuration. Values contained are the latest values " + marketPublicAllocator: MetaMorphoPublicAllocatorMarket + + " The amount of assets withdrawn " + withdrawnAssets: BigInt! + + " The flow caps modification set by this event " + flowCaps: MarketFlowCapsSet! @derivedFrom(field: "publicWithdrawalEvent") +} + +" Register of any change of the flow caps, either if it comes from a public call or an admin call (FlowCapSetEvent) " +type MarketFlowCapsSet @entity(immutable: true) { + " { Transaction hash }{ Log index }{ markedId if applicable } " + id: Bytes! + + " The metaMorpho public allocator configuration. Values contained are the latest values " + metaMorphoPublicAllocator: MetaMorphoPublicAllocator! + + " The latest market public allocator configuration. Values contained are the latest values " + marketPublicAllocator: MetaMorphoPublicAllocatorMarket! + + " The previous flow cap in value " + prevFlowCapIn: BigInt! + " The new flow cap in value " + flowCapIn: BigInt! + + " The previous flow cap out value " + prevFlowCapOut: BigInt! + " The new flow cap out value " + flowCapOut: BigInt! + + + " The set flow caps event that has set this flow caps. null if this is not set by a set flow caps event " + setFlowCapsEvent: SetFlowCapsEvent + + " The public reallocation event that has set this flow caps. null if this is not set by a reallocation event " + publicReallocationEvent: PublicAllocatorReallocationToEvent + + " The public withdrawal event that has set this flow caps. null if this is not set by a withdrawal event " + publicWithdrawalEvent: PublicAllocatorWithdrawalEvent + +} diff --git a/src/meta-morpho-factory.ts b/src/meta-morpho-factory.ts index abbd14b..ac322bb 100644 --- a/src/meta-morpho-factory.ts +++ b/src/meta-morpho-factory.ts @@ -48,5 +48,7 @@ export function handleCreateMetaMorpho(event: CreateMetaMorphoEvent): void { event.params.metaMorpho ).getAccount().id; + metaMorpho.hasPublicAllocator = false; + metaMorpho.save(); } diff --git a/src/meta-morpho.ts b/src/meta-morpho.ts index 3eed129..63d9ea8 100644 --- a/src/meta-morpho.ts +++ b/src/meta-morpho.ts @@ -47,6 +47,7 @@ import { ReallocateWithdraw as ReallocateWithdrawEvent, } from "../generated/templates/MetaMorpho/MetaMorpho"; +import { loadPublicAllocatorVault } from "./public-allocator"; import { AccountManager } from "./sdk/account"; import { loadMetaMorpho, @@ -58,6 +59,7 @@ import { } from "./sdk/metamorpho"; import { TokenManager } from "./sdk/token"; import { toMetaMorphoAssetsUp } from "./utils/metaMorphoUtils"; +import { getPublicAllocatorAddress } from "./utils/publicAllocator"; import { cloneRate } from "./utils/rate"; export function handleSubmitMarketRemoval( @@ -394,8 +396,24 @@ export function handleSetIsAllocator(event: SetIsAllocatorEvent): void { allocator.metaMorpho = mm.id; } allocator.isCurrentAllocator = event.params.isAllocator; + allocator.isPublicAllocator = getPublicAllocatorAddress().equals( + event.params.allocator + ); + allocator.save(); + if (allocator.isPublicAllocator && event.params.isAllocator) { + mm.hasPublicAllocator = true; + + // Update the public allocator vault. It can be created before the allocator is set, so we need to update it here. + const publicAllocatorVault = loadPublicAllocatorVault(event.address); + publicAllocatorVault.allocator = allocator.id; + publicAllocatorVault.save(); + } else { + mm.hasPublicAllocator = false; + } + mm.save(); + const allocatorSet = new AllocatorSet( event.transaction.hash.concat(Bytes.fromI32(event.logIndex.toI32())) ); @@ -420,6 +438,7 @@ export function handleSetSkimRecipient(event: SetSkimRecipientEvent): void {} export function handleSetSupplyQueue(event: SetSupplyQueueEvent): void { // Supply queue on subgraph is a list of MetaMorphoMarket ids, not Market ids. const mm = loadMetaMorpho(event.address); + const newSupplyQueue: Array = []; const addedMarkets: Array = []; const seen = new Map(); diff --git a/src/public-allocator.ts b/src/public-allocator.ts new file mode 100644 index 0000000..1ac8414 --- /dev/null +++ b/src/public-allocator.ts @@ -0,0 +1,270 @@ +import { Address, BigInt, Bytes } from "@graphprotocol/graph-ts"; + +import { + PublicReallocateTo, + PublicWithdrawal, + SetAdmin, + SetFee, + SetFlowCaps, + TransferFee, +} from "../generated/PublicAllocator/PublicAllocator"; +import { + Account, + MarketFlowCapsSet, + MetaMorpho, + MetaMorphoAllocator, + MetaMorphoPublicAllocator, + MetaMorphoPublicAllocatorMarket, + PublicAllocatorReallocationToEvent, + PublicAllocatorWithdrawalEvent, + SetFlowCapsEvent, +} from "../generated/schema"; + +import { loadMetaMorpho, loadMetaMorphoMarket } from "./sdk/metamorpho"; +import { getPublicAllocatorAddress } from "./utils/publicAllocator"; + +export function loadPublicAllocatorVault( + metaMorpho: Address +): MetaMorphoPublicAllocator { + const id = getPublicAllocatorAddress().concat(metaMorpho); + + let pa = MetaMorphoPublicAllocator.load(id); + + if (!pa) { + pa = new MetaMorphoPublicAllocator(id); + + const mmAllocator = MetaMorphoAllocator.load(id); + if (mmAllocator) { + // a vault owner can set flow caps before having set a public allocator. + // in this case, allocator is added dynamically. + pa.allocator = mmAllocator.id; + } + const mm = loadMetaMorpho(metaMorpho); + pa.metaMorpho = mm.id; + + pa.fee = BigInt.zero(); + pa.accruedFee = BigInt.zero(); + pa.claimableFee = BigInt.zero(); + pa.claimedFee = BigInt.zero(); + pa.save(); + } + return pa; +} + +export function loadPublicAllocatorMarket( + metaMorpho: Address, + market: Bytes +): MetaMorphoPublicAllocatorMarket { + const id = metaMorpho.concat(market); + let paMarket = MetaMorphoPublicAllocatorMarket.load(id); + + if (!paMarket) { + paMarket = new MetaMorphoPublicAllocatorMarket(id); + + const mmMarket = loadMetaMorphoMarket(metaMorpho, market); + paMarket.market = mmMarket.id; + + const paVault = loadPublicAllocatorVault(metaMorpho); + paMarket.metaMorphoPublicAllocator = paVault.id; + + paMarket.flowCapIn = BigInt.zero(); + paMarket.flowCapOut = BigInt.zero(); + + paMarket.save(); + } + return paMarket; +} + +/** + * Public allocator can be defined for any address, even if its not a vault. + * The indexer supports only metaMorpho created through the factory. + * These vaults cannot have a config defined before creation, because of the msg.sender === vault.owner() check. + * @param address the address of the vault + */ +function metaMorphoExists(address: Address): boolean { + const mm = MetaMorpho.load(address); + return mm !== null; +} + +export function handlePublicReallocateTo(event: PublicReallocateTo): void { + if (!metaMorphoExists(event.params.vault)) { + return; + } + + const paMarket = loadPublicAllocatorMarket( + event.params.vault, + event.params.supplyMarketId + ); + const newFlowCapIn = paMarket.flowCapIn.minus(event.params.suppliedAssets); + const newFlowCapOut = paMarket.flowCapOut.plus(event.params.suppliedAssets); + + const eventId = event.transaction.hash.concat( + Bytes.fromI32(event.logIndex.toI32()) + ); + + const marketFlowCapsSet = new MarketFlowCapsSet(eventId); + marketFlowCapsSet.metaMorphoPublicAllocator = + paMarket.metaMorphoPublicAllocator; + marketFlowCapsSet.marketPublicAllocator = paMarket.market; + + marketFlowCapsSet.prevFlowCapIn = paMarket.flowCapIn; + marketFlowCapsSet.flowCapIn = newFlowCapIn; + + marketFlowCapsSet.prevFlowCapOut = paMarket.flowCapOut; + marketFlowCapsSet.flowCapOut = newFlowCapOut; + + const reallocateToEvent = new PublicAllocatorReallocationToEvent(eventId); + + reallocateToEvent.hash = event.transaction.hash; + reallocateToEvent.nonce = event.transaction.nonce; + reallocateToEvent.logIndex = event.logIndex.toI32(); + reallocateToEvent.gasPrice = event.transaction.gasPrice; + reallocateToEvent.gasUsed = event.receipt ? event.receipt!.gasUsed : null; + reallocateToEvent.gasLimit = event.transaction.gasLimit; + reallocateToEvent.blockNumber = event.block.number; + reallocateToEvent.timestamp = event.block.timestamp; + reallocateToEvent.author = new Account(event.params.sender).id; + reallocateToEvent.metaMorphoPublicAllocator = + paMarket.metaMorphoPublicAllocator; + reallocateToEvent.marketPublicAllocator = paMarket.id; + reallocateToEvent.suppliedAssets = event.params.suppliedAssets; + reallocateToEvent.save(); + + marketFlowCapsSet.publicReallocationEvent = reallocateToEvent.id; + marketFlowCapsSet.save(); + paMarket.flowCapIn = newFlowCapIn; + paMarket.flowCapOut = newFlowCapOut; + paMarket.save(); +} + +export function handlePublicWithdrawal(event: PublicWithdrawal): void { + if (!metaMorphoExists(event.params.vault)) { + return; + } + + const paVault = loadPublicAllocatorVault(event.params.vault); + paVault.accruedFee = paVault.accruedFee.plus(paVault.fee); + paVault.claimableFee = paVault.claimableFee.plus(paVault.fee); + paVault.save(); + + const paMarket = loadPublicAllocatorMarket( + event.params.vault, + event.params.id + ); + const newFlowCapOut = paMarket.flowCapOut.minus(event.params.withdrawnAssets); + const newFlowCapIn = paMarket.flowCapIn.plus(event.params.withdrawnAssets); + + const eventId = event.transaction.hash.concat( + Bytes.fromI32(event.logIndex.toI32()) + ); + + const marketFlowCapsSet = new MarketFlowCapsSet(eventId); + marketFlowCapsSet.metaMorphoPublicAllocator = + paMarket.metaMorphoPublicAllocator; + marketFlowCapsSet.marketPublicAllocator = paMarket.market; + + marketFlowCapsSet.prevFlowCapIn = paMarket.flowCapIn; + marketFlowCapsSet.flowCapIn = newFlowCapIn; + + marketFlowCapsSet.prevFlowCapOut = paMarket.flowCapOut; + marketFlowCapsSet.flowCapOut = newFlowCapOut; + + const withdrawalEvent = new PublicAllocatorWithdrawalEvent(eventId); + + withdrawalEvent.hash = event.transaction.hash; + withdrawalEvent.nonce = event.transaction.nonce; + withdrawalEvent.logIndex = event.logIndex.toI32(); + withdrawalEvent.gasPrice = event.transaction.gasPrice; + withdrawalEvent.gasUsed = event.receipt ? event.receipt!.gasUsed : null; + withdrawalEvent.gasLimit = event.transaction.gasLimit; + withdrawalEvent.blockNumber = event.block.number; + withdrawalEvent.timestamp = event.block.timestamp; + withdrawalEvent.author = new Account(event.params.sender).id; + withdrawalEvent.metaMorphoPublicAllocator = + paMarket.metaMorphoPublicAllocator; + withdrawalEvent.marketPublicAllocator = paMarket.id; + withdrawalEvent.withdrawnAssets = event.params.withdrawnAssets; + withdrawalEvent.save(); + + marketFlowCapsSet.publicWithdrawalEvent = withdrawalEvent.id; + marketFlowCapsSet.save(); + paMarket.flowCapIn = newFlowCapIn; + paMarket.flowCapOut = newFlowCapOut; + paMarket.save(); +} + +export function handleSetAdmin(event: SetAdmin): void { + if (!metaMorphoExists(event.params.vault)) { + return; + } + const paVault = loadPublicAllocatorVault(event.params.vault); + paVault.admin = new Account(event.params.admin).id; + paVault.save(); +} + +export function handleSetFee(event: SetFee): void { + if (!metaMorphoExists(event.params.vault)) { + return; + } + const paVault = loadPublicAllocatorVault(event.params.vault); + paVault.fee = event.params.fee; + paVault.save(); +} + +export function handleSetFlowCaps(event: SetFlowCaps): void { + if (!metaMorphoExists(event.params.vault)) { + return; + } + const paVault = loadPublicAllocatorVault(event.params.vault); + + const eventId = event.transaction.hash.concat( + Bytes.fromI32(event.logIndex.toI32()) + ); + + const setFlowCapsEvent = new SetFlowCapsEvent(eventId); + + setFlowCapsEvent.hash = event.transaction.hash; + setFlowCapsEvent.nonce = event.transaction.nonce; + setFlowCapsEvent.logIndex = event.logIndex.toI32(); + setFlowCapsEvent.gasPrice = event.transaction.gasPrice; + setFlowCapsEvent.gasUsed = event.receipt ? event.receipt!.gasUsed : null; + setFlowCapsEvent.gasLimit = event.transaction.gasLimit; + setFlowCapsEvent.blockNumber = event.block.number; + setFlowCapsEvent.timestamp = event.block.timestamp; + setFlowCapsEvent.author = new Account(event.params.sender).id; + + setFlowCapsEvent.metaMorphoPublicAllocator = paVault.id; + + setFlowCapsEvent.save(); + + for (let i = 0; i < event.params.config.length; i++) { + const config = event.params.config[i]; + + const paMarket = loadPublicAllocatorMarket(event.params.vault, config.id); + + const marketFlowCapsSet = new MarketFlowCapsSet(eventId.concat(config.id)); + marketFlowCapsSet.metaMorphoPublicAllocator = + paMarket.metaMorphoPublicAllocator; + marketFlowCapsSet.marketPublicAllocator = paMarket.id; + marketFlowCapsSet.prevFlowCapIn = paMarket.flowCapIn; + marketFlowCapsSet.flowCapIn = config.caps.maxIn; + marketFlowCapsSet.prevFlowCapOut = paMarket.flowCapOut; + marketFlowCapsSet.flowCapOut = config.caps.maxOut; + marketFlowCapsSet.setFlowCapsEvent = setFlowCapsEvent.id; + marketFlowCapsSet.save(); + + paMarket.flowCapIn = config.caps.maxIn; + paMarket.flowCapOut = config.caps.maxOut; + paMarket.save(); + } +} + +export function handleTransferFee(event: TransferFee): void { + if (!metaMorphoExists(event.params.vault)) { + return; + } + const pa = loadPublicAllocatorVault(event.params.vault); + pa.claimableFee = pa.claimableFee.minus(event.params.amount); + pa.claimedFee = pa.claimedFee.plus(event.params.amount); + pa.save(); +} diff --git a/src/utils/publicAllocator.ts b/src/utils/publicAllocator.ts new file mode 100644 index 0000000..814bc4a --- /dev/null +++ b/src/utils/publicAllocator.ts @@ -0,0 +1,14 @@ +import { Address, dataSource, log } from "@graphprotocol/graph-ts"; + +export function getPublicAllocatorAddress(): Address { + const network = dataSource.network(); + if (network == "mainnet") { + return Address.fromString("0xfd32fA2ca22c76dD6E550706Ad913FC6CE91c75D"); + } + if (network == "base") { + return Address.fromString("0xA090dD1a701408Df1d4d0B85b716c87565f90467"); + } + + log.warning("Unsupported Network id: {}", [network]); + return Address.zero(); +} diff --git a/subgraph.yaml b/subgraph.yaml index 678f043..ff02f9d 100644 --- a/subgraph.yaml +++ b/subgraph.yaml @@ -133,6 +133,43 @@ dataSources: address,string,string,bytes32) handler: handleCreateMetaMorpho file: ./src/meta-morpho-factory.ts + - kind: ethereum + name: PublicAllocator + network: mainnet + source: + address: "0xfd32fA2ca22c76dD6E550706Ad913FC6CE91c75D" + abi: PublicAllocator + startBlock: 19375099 + mapping: + kind: ethereum/events + apiVersion: 0.0.7 + language: wasm/assemblyscript + entities: + - PublicReallocateTo + - PublicWithdrawal + - SetAdmin + - SetFee + - SetFlowCaps + - TransferFee + abis: + - name: PublicAllocator + file: ./abis/PublicAllocator.json + eventHandlers: + - event: PublicReallocateTo(indexed address,indexed address,indexed + bytes32,uint256) + handler: handlePublicReallocateTo + - event: PublicWithdrawal(indexed address,indexed address,indexed bytes32,uint256) + handler: handlePublicWithdrawal + - event: SetAdmin(indexed address,indexed address,address) + handler: handleSetAdmin + - event: SetFee(indexed address,indexed address,uint256) + handler: handleSetFee + - event: SetFlowCaps(indexed address,indexed + address,(bytes32,(uint128,uint128))[]) + handler: handleSetFlowCaps + - event: TransferFee(indexed address,indexed address,uint256,indexed address) + handler: handleTransferFee + file: ./src/public-allocator.ts templates: - kind: ethereum name: MetaMorpho