diff --git a/README.md b/README.md index 2694bfe..02e6f97 100644 --- a/README.md +++ b/README.md @@ -43,14 +43,9 @@ List pools showing their tokens and how many transactions have been performed on Positions for a specific lender across all pools: ``` { - accounts(where: {id:"0x31bcbe14ad30b2f7e1e4a14cab2c16849b73dac3"}) { + accounts(where: {id:"0x4eb7f19d6efcace59eaed70220da5002709f9b71"}) { id lends { - bucket { - bucketIndex - deposit - collateral - } pool { id quoteToken { @@ -60,6 +55,11 @@ Positions for a specific lender across all pools: symbol } } + bucket { + bucketIndex + deposit + collateral + } } } } @@ -68,25 +68,21 @@ Positions for a specific lender across all pools: Details for a specific pool: ``` { - pool(id: "0xe1200aefd60559d494d4419e17419571ef8fc1eb") { + pool(id: "0xc2b64ca87090fe79786a8773009d7fb1288d3db1") { id + quoteToken { symbol } + collateralToken { symbol } + poolSize + debt actualUtilization - currentDebt htp hpb lup - maxBorrower - poolSize reserves - targetUtilization + borrowRate + lendRate totalAjnaBurned totalInterestEarned - quoteToken { - symbol - } - collateralToken { - symbol - } } } ``` diff --git a/abis/ERC20Pool.json b/abis/ERC20Pool.json index 1beef09..2680843 100644 --- a/abis/ERC20Pool.json +++ b/abis/ERC20Pool.json @@ -96,7 +96,7 @@ }, { "inputs": [], - "name": "InsufficientLPs", + "name": "InsufficientLP", "type": "error" }, { @@ -104,6 +104,11 @@ "name": "InsufficientLiquidity", "type": "error" }, + { + "inputs": [], + "name": "InvalidAllowancesInput", + "type": "error" + }, { "inputs": [], "name": "InvalidAmount", @@ -428,7 +433,7 @@ "type": "address[]" } ], - "name": "ApproveLpTransferors", + "name": "ApproveLPTransferors", "type": "event" }, { @@ -449,7 +454,7 @@ { "indexed": false, "internalType": "uint256", - "name": "lps", + "name": "lp", "type": "uint256" }, { @@ -599,6 +604,37 @@ "name": "BucketTakeLPAwarded", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "indexes", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "name": "DecreaseLPAllowance", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -630,6 +666,62 @@ "name": "DrawDebt", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Flashloan", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "indexes", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "name": "IncreaseLPAllowance", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -661,6 +753,31 @@ "name": "Kick", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "claimableReservesRemaining", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "auctionPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "currentBurnEpoch", + "type": "uint256" + } + ], + "name": "KickReserveAuction", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -851,19 +968,19 @@ "anonymous": false, "inputs": [ { - "indexed": true, - "internalType": "address", - "name": "spender", - "type": "address" + "indexed": false, + "internalType": "uint256", + "name": "oldRate", + "type": "uint256" }, { "indexed": false, - "internalType": "uint256[]", - "name": "indexes", - "type": "uint256[]" + "internalType": "uint256", + "name": "newRate", + "type": "uint256" } ], - "name": "RevokeLpAllowance", + "name": "ResetInterestRate", "type": "event" }, { @@ -872,17 +989,23 @@ { "indexed": true, "internalType": "address", - "name": "lender", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", "type": "address" }, { "indexed": false, - "internalType": "address[]", - "name": "transferors", - "type": "address[]" + "internalType": "uint256[]", + "name": "indexes", + "type": "uint256[]" } ], - "name": "RevokeLpTransferors", + "name": "RevokeLPAllowance", "type": "event" }, { @@ -891,23 +1014,17 @@ { "indexed": true, "internalType": "address", - "name": "spender", + "name": "lender", "type": "address" }, { "indexed": false, - "internalType": "uint256[]", - "name": "indexes", - "type": "uint256[]" - }, - { - "indexed": false, - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" + "internalType": "address[]", + "name": "transferors", + "type": "address[]" } ], - "name": "SetLpAllowance", + "name": "RevokeLPTransferors", "type": "event" }, { @@ -990,11 +1107,11 @@ { "indexed": false, "internalType": "uint256", - "name": "lps", + "name": "lp", "type": "uint256" } ], - "name": "TransferLPs", + "name": "TransferLP", "type": "event" }, { @@ -1038,7 +1155,7 @@ "outputs": [ { "internalType": "uint256", - "name": "bucketLPs_", + "name": "bucketLP_", "type": "uint256" } ], @@ -1049,7 +1166,7 @@ "inputs": [ { "internalType": "uint256", - "name": "quoteTokenAmountToAdd_", + "name": "amount_", "type": "uint256" }, { @@ -1067,7 +1184,7 @@ "outputs": [ { "internalType": "uint256", - "name": "bucketLPs_", + "name": "bucketLP_", "type": "uint256" } ], @@ -1082,7 +1199,7 @@ "type": "address[]" } ], - "name": "approveLpTransferors", + "name": "approveLPTransferors", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -1123,52 +1240,52 @@ "outputs": [ { "internalType": "address", - "name": "kicker", + "name": "kicker_", "type": "address" }, { "internalType": "uint256", - "name": "bondFactor", + "name": "bondFactor_", "type": "uint256" }, { "internalType": "uint256", - "name": "bondSize", + "name": "bondSize_", "type": "uint256" }, { "internalType": "uint256", - "name": "kickTime", + "name": "kickTime_", "type": "uint256" }, { "internalType": "uint256", - "name": "kickMomp", + "name": "kickMomp_", "type": "uint256" }, { "internalType": "uint256", - "name": "neutralPrice", + "name": "neutralPrice_", "type": "uint256" }, { "internalType": "address", - "name": "head", + "name": "head_", "type": "address" }, { "internalType": "address", - "name": "next", + "name": "next_", "type": "address" }, { "internalType": "address", - "name": "prev", + "name": "prev_", "type": "address" }, { "internalType": "bool", - "name": "alreadyTaken", + "name": "alreadyTaken_", "type": "bool" } ], @@ -1208,7 +1325,7 @@ "inputs": [ { "internalType": "uint256", - "name": "bucketIndex", + "name": "bucketIndex_", "type": "uint256" } ], @@ -1386,6 +1503,11 @@ "name": "", "type": "uint256" }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, { "internalType": "uint256", "name": "", @@ -1437,6 +1559,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index_", + "type": "uint256" + } + ], + "name": "depositScale", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "depositSize", @@ -1450,6 +1591,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index_", + "type": "uint256" + } + ], + "name": "depositUpToIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "depositUtilization", @@ -1653,12 +1813,12 @@ "inputs": [ { "internalType": "address", - "name": "borrowerAddress_", + "name": "borrower_", "type": "address" }, { "internalType": "uint256", - "name": "limitIndex_", + "name": "npLimitIndex_", "type": "uint256" } ], @@ -1667,6 +1827,13 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "kickReserveAuction", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1676,7 +1843,7 @@ }, { "internalType": "uint256", - "name": "limitIndex_", + "name": "npLimitIndex_", "type": "uint256" } ], @@ -1837,7 +2004,7 @@ "inputs": [ { "internalType": "uint256", - "name": "maxAmountToMove_", + "name": "maxAmount_", "type": "uint256" }, { @@ -1860,12 +2027,12 @@ "outputs": [ { "internalType": "uint256", - "name": "fromBucketLPs_", + "name": "fromBucketLP_", "type": "uint256" }, { "internalType": "uint256", - "name": "toBucketLPs_", + "name": "toBucketLP_", "type": "uint256" }, { @@ -1978,12 +2145,12 @@ "outputs": [ { "internalType": "uint256", - "name": "collateralAmount_", + "name": "removedAmount_", "type": "uint256" }, { "internalType": "uint256", - "name": "lpAmount_", + "name": "redeemedLP_", "type": "uint256" } ], @@ -2012,7 +2179,7 @@ }, { "internalType": "uint256", - "name": "redeemedLPs_", + "name": "redeemedLP_", "type": "uint256" } ], @@ -2106,7 +2273,7 @@ "type": "address[]" } ], - "name": "revokeLpTransferors", + "name": "revokeLPTransferors", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -2136,13 +2303,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [], - "name": "startClaimableReserveAuction", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -2152,7 +2312,7 @@ }, { "internalType": "uint256", - "name": "collateral_", + "name": "maxAmount_", "type": "uint256" }, { @@ -2247,7 +2407,7 @@ "type": "uint256[]" } ], - "name": "transferLPs", + "name": "transferLP", "outputs": [], "stateMutability": "nonpayable", "type": "function" diff --git a/abis/ERC20PoolFactory.json b/abis/ERC20PoolFactory.json index cbcf0cd..3d1d82a 100644 --- a/abis/ERC20PoolFactory.json +++ b/abis/ERC20PoolFactory.json @@ -15,6 +15,11 @@ "name": "CreateFail", "type": "error" }, + { + "inputs": [], + "name": "DeployQuoteCollateralSameToken", + "type": "error" + }, { "inputs": [], "name": "DeployWithZeroAddress", diff --git a/abis/ERC721PoolFactory.json b/abis/ERC721PoolFactory.json index 3d31358..aaed386 100644 --- a/abis/ERC721PoolFactory.json +++ b/abis/ERC721PoolFactory.json @@ -15,6 +15,11 @@ "name": "CreateFail", "type": "error" }, + { + "inputs": [], + "name": "DeployQuoteCollateralSameToken", + "type": "error" + }, { "inputs": [], "name": "DeployWithZeroAddress", diff --git a/abis/GrantFund.json b/abis/GrantFund.json index 488aa80..da73080 100644 --- a/abis/GrantFund.json +++ b/abis/GrantFund.json @@ -1,48 +1,124 @@ [ - { "inputs": [], "name": "AlreadyVoted", "type": "error" }, - { "inputs": [], "name": "ChallengePeriodNotEnded", "type": "error" }, - { "inputs": [], "name": "DelegateRewardInvalid", "type": "error" }, - { "inputs": [], "name": "DistributionPeriodStillActive", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "ajnaToken_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AlreadyVoted", + "type": "error" + }, + { + "inputs": [], + "name": "ChallengePeriodNotEnded", + "type": "error" + }, + { + "inputs": [], + "name": "DelegateRewardInvalid", + "type": "error" + }, + { + "inputs": [], + "name": "DistributionPeriodStillActive", + "type": "error" + }, { "inputs": [], "name": "ExecuteExtraordinaryProposalInvalid", "type": "error" }, - { "inputs": [], "name": "ExecuteProposalInvalid", "type": "error" }, + { + "inputs": [], + "name": "ExecuteProposalInvalid", + "type": "error" + }, { "inputs": [], "name": "ExtraordinaryFundingProposalInactive", "type": "error" }, - { "inputs": [], "name": "FundingVoteWrongDirection", "type": "error" }, - { "inputs": [], "name": "InsufficientVotingPower", "type": "error" }, - { "inputs": [], "name": "InvalidProposal", "type": "error" }, - { "inputs": [], "name": "InvalidProposalSlate", "type": "error" }, - { "inputs": [], "name": "InvalidVote", "type": "error" }, - { "inputs": [], "name": "ProposalAlreadyExists", "type": "error" }, - { "inputs": [], "name": "ProposalNotFound", "type": "error" }, - { "inputs": [], "name": "ProposalNotSuccessful", "type": "error" }, - { "inputs": [], "name": "RewardAlreadyClaimed", "type": "error" }, - { "inputs": [], "name": "ScreeningPeriodEnded", "type": "error" }, + { + "inputs": [], + "name": "FundingVoteWrongDirection", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientRemainingVotingPower", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientVotingPower", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidProposal", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidProposalSlate", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidVote", + "type": "error" + }, + { + "inputs": [], + "name": "ProposalAlreadyExists", + "type": "error" + }, + { + "inputs": [], + "name": "ProposalNotFound", + "type": "error" + }, + { + "inputs": [], + "name": "ProposalNotSuccessful", + "type": "error" + }, + { + "inputs": [], + "name": "RewardAlreadyClaimed", + "type": "error" + }, + { + "inputs": [], + "name": "ScreeningPeriodEnded", + "type": "error" + }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", - "name": "delegateeAddress_", + "name": "delegateeAddress", "type": "address" }, { "indexed": true, "internalType": "uint256", - "name": "distributionId_", + "name": "distributionId", "type": "uint256" }, { "indexed": false, "internalType": "uint256", - "name": "rewardClaimed_", + "name": "rewardClaimed", "type": "uint256" } ], @@ -74,13 +150,13 @@ { "indexed": true, "internalType": "uint256", - "name": "distributionId_", + "name": "distributionId", "type": "uint256" }, { "indexed": true, "internalType": "bytes32", - "name": "fundedSlateHash_", + "name": "fundedSlateHash", "type": "bytes32" } ], @@ -167,19 +243,19 @@ { "indexed": true, "internalType": "uint256", - "name": "distributionId_", + "name": "distributionId", "type": "uint256" }, { "indexed": false, "internalType": "uint256", - "name": "startBlock_", + "name": "startBlock", "type": "uint256" }, { "indexed": false, "internalType": "uint256", - "name": "endBlock_", + "name": "endBlock", "type": "uint256" } ], @@ -226,26 +302,52 @@ { "inputs": [], "name": "ajnaTokenAddress", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "uint24", "name": "distributionId_", "type": "uint24" } + { + "internalType": "uint24", + "name": "distributionId_", + "type": "uint24" + } ], "name": "claimDelegateReward", "outputs": [ - { "internalType": "uint256", "name": "rewardClaimed_", "type": "uint256" } + { + "internalType": "uint256", + "name": "rewardClaimed_", + "type": "uint256" + } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ - { "internalType": "address[]", "name": "targets_", "type": "address[]" }, - { "internalType": "uint256[]", "name": "values_", "type": "uint256[]" }, - { "internalType": "bytes[]", "name": "calldatas_", "type": "bytes[]" }, + { + "internalType": "address[]", + "name": "targets_", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "values_", + "type": "uint256[]" + }, + { + "internalType": "bytes[]", + "name": "calldatas_", + "type": "bytes[]" + }, { "internalType": "bytes32", "name": "descriptionHash_", @@ -254,16 +356,32 @@ ], "name": "executeExtraordinary", "outputs": [ - { "internalType": "uint256", "name": "proposalId_", "type": "uint256" } + { + "internalType": "uint256", + "name": "proposalId_", + "type": "uint256" + } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ - { "internalType": "address[]", "name": "targets_", "type": "address[]" }, - { "internalType": "uint256[]", "name": "values_", "type": "uint256[]" }, - { "internalType": "bytes[]", "name": "calldatas_", "type": "bytes[]" }, + { + "internalType": "address[]", + "name": "targets_", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "values_", + "type": "uint256[]" + }, + { + "internalType": "bytes[]", + "name": "calldatas_", + "type": "bytes[]" + }, { "internalType": "bytes32", "name": "descriptionHash_", @@ -272,14 +390,22 @@ ], "name": "executeStandard", "outputs": [ - { "internalType": "uint256", "name": "proposalId_", "type": "uint256" } + { + "internalType": "uint256", + "name": "proposalId_", + "type": "uint256" + } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ - { "internalType": "uint256", "name": "proposalId_", "type": "uint256" } + { + "internalType": "uint256", + "name": "proposalId_", + "type": "uint256" + } ], "name": "findMechanismOfProposal", "outputs": [ @@ -294,7 +420,11 @@ }, { "inputs": [ - { "internalType": "uint256", "name": "fundingAmount_", "type": "uint256" } + { + "internalType": "uint256", + "name": "fundingAmount_", + "type": "uint256" + } ], "name": "fundTreasury", "outputs": [], @@ -310,7 +440,11 @@ "name": "proposalId", "type": "uint256" }, - { "internalType": "int256", "name": "votesUsed", "type": "int256" } + { + "internalType": "int256", + "name": "votesUsed", + "type": "int256" + } ], "internalType": "struct IStandardFunding.FundingVoteParams[]", "name": "voteParams_", @@ -319,19 +453,35 @@ ], "name": "fundingVote", "outputs": [ - { "internalType": "uint256", "name": "votesCast_", "type": "uint256" } + { + "internalType": "uint256", + "name": "votesCast_", + "type": "uint256" + } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ - { "internalType": "uint24", "name": "distributionId_", "type": "uint24" }, - { "internalType": "address", "name": "voter_", "type": "address" } + { + "internalType": "uint24", + "name": "distributionId_", + "type": "uint24" + }, + { + "internalType": "address", + "name": "voter_", + "type": "address" + } ], "name": "getDelegateReward", "outputs": [ - { "internalType": "uint256", "name": "rewards_", "type": "uint256" } + { + "internalType": "uint256", + "name": "rewards_", + "type": "uint256" + } ], "stateMutability": "view", "type": "function" @@ -339,75 +489,173 @@ { "inputs": [], "name": "getDistributionId", - "outputs": [{ "internalType": "uint24", "name": "", "type": "uint24" }], + "outputs": [ + { + "internalType": "uint24", + "name": "", + "type": "uint24" + } + ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "uint24", "name": "distributionId_", "type": "uint24" } + { + "internalType": "uint24", + "name": "distributionId_", + "type": "uint24" + } ], "name": "getDistributionPeriodInfo", "outputs": [ - { "internalType": "uint24", "name": "", "type": "uint24" }, - { "internalType": "uint48", "name": "", "type": "uint48" }, - { "internalType": "uint48", "name": "", "type": "uint48" }, - { "internalType": "uint128", "name": "", "type": "uint128" }, - { "internalType": "uint256", "name": "", "type": "uint256" }, - { "internalType": "bytes32", "name": "", "type": "bytes32" } + { + "internalType": "uint24", + "name": "", + "type": "uint24" + }, + { + "internalType": "uint48", + "name": "", + "type": "uint48" + }, + { + "internalType": "uint48", + "name": "", + "type": "uint48" + }, + { + "internalType": "uint128", + "name": "", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "uint256", "name": "proposalId_", "type": "uint256" } + { + "internalType": "uint256", + "name": "proposalId_", + "type": "uint256" + } ], "name": "getExtraordinaryProposalInfo", "outputs": [ - { "internalType": "uint256", "name": "", "type": "uint256" }, - { "internalType": "uint128", "name": "", "type": "uint128" }, - { "internalType": "uint128", "name": "", "type": "uint128" }, - { "internalType": "uint128", "name": "", "type": "uint128" }, - { "internalType": "uint120", "name": "", "type": "uint120" }, - { "internalType": "bool", "name": "", "type": "bool" } + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "", + "type": "uint128" + }, + { + "internalType": "uint120", + "name": "", + "type": "uint120" + }, + { + "internalType": "bool", + "name": "", + "type": "bool" + } ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "uint256", "name": "proposalId_", "type": "uint256" } + { + "internalType": "uint256", + "name": "proposalId_", + "type": "uint256" + } ], "name": "getExtraordinaryProposalSucceeded", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "bytes32", "name": "slateHash_", "type": "bytes32" } + { + "internalType": "bytes32", + "name": "slateHash_", + "type": "bytes32" + } ], "name": "getFundedProposalSlate", "outputs": [ - { "internalType": "uint256[]", "name": "", "type": "uint256[]" } + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "uint256", "name": "votingPower_", "type": "uint256" } + { + "internalType": "uint256", + "name": "votingPower_", + "type": "uint256" + } ], "name": "getFundingPowerVotes", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], "stateMutability": "pure", "type": "function" }, { "inputs": [ - { "internalType": "uint24", "name": "distributionId_", "type": "uint24" }, - { "internalType": "address", "name": "account_", "type": "address" } + { + "internalType": "uint24", + "name": "distributionId_", + "type": "uint24" + }, + { + "internalType": "address", + "name": "account_", + "type": "address" + } ], "name": "getFundingVotesCast", "outputs": [ @@ -418,7 +666,11 @@ "name": "proposalId", "type": "uint256" }, - { "internalType": "int256", "name": "votesUsed", "type": "int256" } + { + "internalType": "int256", + "name": "votesUsed", + "type": "int256" + } ], "internalType": "struct IStandardFunding.FundingVoteParams[]", "name": "", @@ -431,22 +683,56 @@ { "inputs": [], "name": "getMinimumThresholdPercentage", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" }, { "inputs": [ - { "internalType": "uint256", "name": "proposalId_", "type": "uint256" } + { + "internalType": "uint256", + "name": "proposalId_", + "type": "uint256" + } ], "name": "getProposalInfo", "outputs": [ - { "internalType": "uint256", "name": "", "type": "uint256" }, - { "internalType": "uint24", "name": "", "type": "uint24" }, - { "internalType": "uint128", "name": "", "type": "uint128" }, - { "internalType": "uint128", "name": "", "type": "uint128" }, - { "internalType": "int128", "name": "", "type": "int128" }, - { "internalType": "bool", "name": "", "type": "bool" } + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint24", + "name": "", + "type": "uint24" + }, + { + "internalType": "uint128", + "name": "", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "", + "type": "uint128" + }, + { + "internalType": "int128", + "name": "", + "type": "int128" + }, + { + "internalType": "bool", + "name": "", + "type": "bool" + } ], "stateMutability": "view", "type": "function" @@ -460,112 +746,244 @@ } ], "name": "getSlateHash", - "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], "stateMutability": "pure", "type": "function" }, { "inputs": [ - { "internalType": "uint256", "name": "percentage_", "type": "uint256" } + { + "internalType": "uint256", + "name": "percentage_", + "type": "uint256" + } ], "name": "getSliceOfNonTreasury", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "uint256", "name": "percentage_", "type": "uint256" } + { + "internalType": "uint256", + "name": "percentage_", + "type": "uint256" + } ], "name": "getSliceOfTreasury", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "uint24", "name": "distributionId_", "type": "uint24" } + { + "internalType": "uint24", + "name": "distributionId_", + "type": "uint24" + } ], "name": "getTopTenProposals", "outputs": [ - { "internalType": "uint256[]", "name": "", "type": "uint256[]" } + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "uint24", "name": "distributionId_", "type": "uint24" }, - { "internalType": "address", "name": "account_", "type": "address" } + { + "internalType": "uint24", + "name": "distributionId_", + "type": "uint24" + }, + { + "internalType": "address", + "name": "account_", + "type": "address" + } ], "name": "getVoterInfo", "outputs": [ - { "internalType": "uint128", "name": "", "type": "uint128" }, - { "internalType": "uint128", "name": "", "type": "uint128" }, - { "internalType": "uint256", "name": "", "type": "uint256" } + { + "internalType": "uint128", + "name": "", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "address", "name": "account_", "type": "address" }, - { "internalType": "uint256", "name": "proposalId_", "type": "uint256" } + { + "internalType": "address", + "name": "account_", + "type": "address" + }, + { + "internalType": "uint256", + "name": "proposalId_", + "type": "uint256" + } ], "name": "getVotesExtraordinary", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "uint24", "name": "distributionId_", "type": "uint24" }, - { "internalType": "address", "name": "account_", "type": "address" } + { + "internalType": "uint24", + "name": "distributionId_", + "type": "uint24" + }, + { + "internalType": "address", + "name": "account_", + "type": "address" + } ], "name": "getVotesFunding", "outputs": [ - { "internalType": "uint256", "name": "votes_", "type": "uint256" } + { + "internalType": "uint256", + "name": "votes_", + "type": "uint256" + } ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "uint24", "name": "distributionId_", "type": "uint24" }, - { "internalType": "address", "name": "account_", "type": "address" } + { + "internalType": "uint24", + "name": "distributionId_", + "type": "uint24" + }, + { + "internalType": "address", + "name": "account_", + "type": "address" + } ], "name": "getVotesScreening", "outputs": [ - { "internalType": "uint256", "name": "votes_", "type": "uint256" } + { + "internalType": "uint256", + "name": "votes_", + "type": "uint256" + } ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "uint256", "name": "", "type": "uint256" }, - { "internalType": "address", "name": "", "type": "address" } + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } ], "name": "hasClaimedReward", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "uint256", "name": "", "type": "uint256" }, - { "internalType": "address", "name": "", "type": "address" } + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } ], "name": "hasVotedExtraordinary", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "address[]", "name": "targets_", "type": "address[]" }, - { "internalType": "uint256[]", "name": "values_", "type": "uint256[]" }, - { "internalType": "bytes[]", "name": "calldatas_", "type": "bytes[]" }, + { + "internalType": "address[]", + "name": "targets_", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "values_", + "type": "uint256[]" + }, + { + "internalType": "bytes[]", + "name": "calldatas_", + "type": "bytes[]" + }, { "internalType": "bytes32", "name": "descriptionHash_", @@ -574,36 +992,84 @@ ], "name": "hashProposal", "outputs": [ - { "internalType": "uint256", "name": "proposalId_", "type": "uint256" } + { + "internalType": "uint256", + "name": "proposalId_", + "type": "uint256" + } ], "stateMutability": "pure", "type": "function" }, { "inputs": [ - { "internalType": "uint256", "name": "endBlock_", "type": "uint256" }, - { "internalType": "address[]", "name": "targets_", "type": "address[]" }, - { "internalType": "uint256[]", "name": "values_", "type": "uint256[]" }, - { "internalType": "bytes[]", "name": "calldatas_", "type": "bytes[]" }, - { "internalType": "string", "name": "description_", "type": "string" } + { + "internalType": "uint256", + "name": "endBlock_", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "targets_", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "values_", + "type": "uint256[]" + }, + { + "internalType": "bytes[]", + "name": "calldatas_", + "type": "bytes[]" + }, + { + "internalType": "string", + "name": "description_", + "type": "string" + } ], "name": "proposeExtraordinary", "outputs": [ - { "internalType": "uint256", "name": "proposalId_", "type": "uint256" } + { + "internalType": "uint256", + "name": "proposalId_", + "type": "uint256" + } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ - { "internalType": "address[]", "name": "targets_", "type": "address[]" }, - { "internalType": "uint256[]", "name": "values_", "type": "uint256[]" }, - { "internalType": "bytes[]", "name": "calldatas_", "type": "bytes[]" }, - { "internalType": "string", "name": "description_", "type": "string" } + { + "internalType": "address[]", + "name": "targets_", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "values_", + "type": "uint256[]" + }, + { + "internalType": "bytes[]", + "name": "calldatas_", + "type": "bytes[]" + }, + { + "internalType": "string", + "name": "description_", + "type": "string" + } ], "name": "proposeStandard", "outputs": [ - { "internalType": "uint256", "name": "proposalId_", "type": "uint256" } + { + "internalType": "uint256", + "name": "proposalId_", + "type": "uint256" + } ], "stateMutability": "nonpayable", "type": "function" @@ -617,7 +1083,11 @@ "name": "proposalId", "type": "uint256" }, - { "internalType": "uint256", "name": "votes", "type": "uint256" } + { + "internalType": "uint256", + "name": "votes", + "type": "uint256" + } ], "internalType": "struct IStandardFunding.ScreeningVoteParams[]", "name": "voteParams_", @@ -626,18 +1096,36 @@ ], "name": "screeningVote", "outputs": [ - { "internalType": "uint256", "name": "votesCast_", "type": "uint256" } + { + "internalType": "uint256", + "name": "votesCast_", + "type": "uint256" + } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ - { "internalType": "uint256", "name": "", "type": "uint256" }, - { "internalType": "address", "name": "", "type": "address" } + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } ], "name": "screeningVotesCast", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], "stateMutability": "view", "type": "function" }, @@ -656,7 +1144,11 @@ }, { "inputs": [ - { "internalType": "uint256", "name": "proposalId_", "type": "uint256" } + { + "internalType": "uint256", + "name": "proposalId_", + "type": "uint256" + } ], "name": "state", "outputs": [ @@ -672,7 +1164,13 @@ { "inputs": [], "name": "treasury", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], "stateMutability": "view", "type": "function" }, @@ -683,20 +1181,38 @@ "name": "proposalIds_", "type": "uint256[]" }, - { "internalType": "uint24", "name": "distributionId_", "type": "uint24" } + { + "internalType": "uint24", + "name": "distributionId_", + "type": "uint24" + } ], "name": "updateSlate", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "outputs": [ + { + "internalType": "bool", + "name": "newTopSlate_", + "type": "bool" + } + ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ - { "internalType": "uint256", "name": "proposalId_", "type": "uint256" } + { + "internalType": "uint256", + "name": "proposalId_", + "type": "uint256" + } ], "name": "voteExtraordinary", "outputs": [ - { "internalType": "uint256", "name": "votesCast_", "type": "uint256" } + { + "internalType": "uint256", + "name": "votesCast_", + "type": "uint256" + } ], "stateMutability": "nonpayable", "type": "function" diff --git a/abis/PoolInfoUtils.json b/abis/PoolInfoUtils.json index 3fb3bbe..bb905a0 100644 --- a/abis/PoolInfoUtils.json +++ b/abis/PoolInfoUtils.json @@ -123,6 +123,55 @@ "name": "PRBMath__MulDivOverflow", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "ajnaPool_", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower_", + "type": "address" + } + ], + "name": "auctionStatus", + "outputs": [ + { + "internalType": "uint256", + "name": "kickTime_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "collateral_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "debtToCover_", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isCollateralized_", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "price_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "neutralPrice_", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -208,7 +257,7 @@ }, { "internalType": "uint256", - "name": "bucketLPs_", + "name": "bucketLP_", "type": "uint256" }, { @@ -275,7 +324,7 @@ "outputs": [ { "internalType": "uint256", - "name": "", + "name": "htp_", "type": "uint256" } ], @@ -329,7 +378,7 @@ }, { "internalType": "uint256", - "name": "lps_", + "name": "lp_", "type": "uint256" }, { @@ -338,7 +387,7 @@ "type": "uint256" } ], - "name": "lpsToCollateral", + "name": "lpToCollateral", "outputs": [ { "internalType": "uint256", @@ -358,7 +407,7 @@ }, { "internalType": "uint256", - "name": "lps_", + "name": "lp_", "type": "uint256" }, { @@ -367,7 +416,7 @@ "type": "uint256" } ], - "name": "lpsToQuoteTokens", + "name": "lpToQuoteTokens", "outputs": [ { "internalType": "uint256", diff --git a/abis/PositionManager.json b/abis/PositionManager.json index cae3091..a82b296 100644 --- a/abis/PositionManager.json +++ b/abis/PositionManager.json @@ -266,6 +266,18 @@ "internalType": "uint256", "name": "toIndex", "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "lpRedeemedFrom", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "lpAwardedTo", + "type": "uint256" } ], "name": "MoveLiquidity", @@ -441,7 +453,7 @@ "type": "uint256" } ], - "name": "getLPs", + "name": "getLP", "outputs": [ { "internalType": "uint256", diff --git a/abis/RewardsManager.json b/abis/RewardsManager.json index ee18526..8dedf4d 100644 --- a/abis/RewardsManager.json +++ b/abis/RewardsManager.json @@ -22,12 +22,12 @@ }, { "inputs": [], - "name": "EpochNotAvailable", + "name": "DeployWithZeroAddress", "type": "error" }, { "inputs": [], - "name": "ExchangeRateUpdateTooLate", + "name": "EpochNotAvailable", "type": "error" }, { diff --git a/copy-abis.sh b/copy-abis.sh index ed5959a..d0db902 100755 --- a/copy-abis.sh +++ b/copy-abis.sh @@ -1,16 +1,19 @@ #!/bin/bash -ABI_PATH="${1:-../contracts/forge_out}" +ABI_PATH_CONTRACTS="${1:-../contracts/forge_out}" +ABI_PATH_GRANTS="${2:-../ecosystem-coordination/out}" # This script generates ABIs from forge output. -# Before running, ensure forge output in your ABI_PATH has been compiled from the -# appropriate branch and commit. +# Before running, ensure forge output in your ABI_PATHs have been compiled from the +# appropriate branches and commits. rm abis/* -jq '.abi' "${ABI_PATH}/ERC20.sol/ERC20.json" > abis/ERC20.json -jq '.abi' "${ABI_PATH}/ERC20Pool.sol/ERC20Pool.json" > abis/ERC20Pool.json -jq '.abi' "${ABI_PATH}/ERC20PoolFactory.sol/ERC20PoolFactory.json" > abis/ERC20PoolFactory.json -jq '.abi' "${ABI_PATH}/ERC721PoolFactory.sol/ERC721PoolFactory.json" > abis/ERC721PoolFactory.json -jq '.abi' "${ABI_PATH}/PoolInfoUtils.sol/PoolInfoUtils.json" > abis/PoolInfoUtils.json -jq '.abi' "${ABI_PATH}/PositionManager.sol/PositionManager.json" > abis/PositionManager.json -jq '.abi' "${ABI_PATH}/RewardsManager.sol/RewardsManager.json" > abis/RewardsManager.json +jq '.abi' "${ABI_PATH_CONTRACTS}/ERC20.sol/ERC20.json" > abis/ERC20.json +jq '.abi' "${ABI_PATH_CONTRACTS}/ERC20Pool.sol/ERC20Pool.json" > abis/ERC20Pool.json +jq '.abi' "${ABI_PATH_CONTRACTS}/ERC20PoolFactory.sol/ERC20PoolFactory.json" > abis/ERC20PoolFactory.json +jq '.abi' "${ABI_PATH_CONTRACTS}/ERC721PoolFactory.sol/ERC721PoolFactory.json" > abis/ERC721PoolFactory.json +jq '.abi' "${ABI_PATH_CONTRACTS}/PoolInfoUtils.sol/PoolInfoUtils.json" > abis/PoolInfoUtils.json +jq '.abi' "${ABI_PATH_CONTRACTS}/PositionManager.sol/PositionManager.json" > abis/PositionManager.json +jq '.abi' "${ABI_PATH_CONTRACTS}/RewardsManager.sol/RewardsManager.json" > abis/RewardsManager.json + +jq '.abi' "${ABI_PATH_GRANTS}/GrantFund.sol/GrantFund.json" > abis/GrantFund.json \ No newline at end of file diff --git a/networks.json b/networks.json index 6a3eb78..44401a0 100644 --- a/networks.json +++ b/networks.json @@ -1,22 +1,22 @@ { "goerli": { "PositionManager": { - "address": "0x31BcbE14Ad30B2f7E1E4A14caB2C16849B73Dac3" + "address": "0x83AB3762A4AeC9FBD4e7c01581C9495f2160630b" }, "PoolInfoUtils": { - "address": "0xEa36b2a4703182d07df9DdEe46BF97f9979F0cCf" + "address": "0x1F9F7732ff409FC0AbcAAea94634A7b41F445299" }, "ERC20PoolFactory": { - "address": "0x9684b8eC942985b23d343cB82D2F30EdA8fD7179" + "address": "0x68ced2E7d257da67794B00556B31203A344d3c1e" }, "RewardsManager": { - "address": "0xEd6890d748e62ddbb3f80e7256Deeb2fBb853476" + "address": "0xaF9bc1F09fe561CbD00018fC352507fD23cD46E2" }, "ERC721PoolFactory": { - "address": "0x7DB5ba7cB6dAD74b45AAB027A7E7388eF18656DF" + "address": "0x86EEA73d8071b03D998f1c466fe2125f0D3C4195" }, "GrantFund": { - "address": "0xaeB91e664A49829FaBf06BE35d4447938d83A271" + "address": "0x54110a15011bcb145a8CD4f5adf108B2B6939A1e" } } } \ No newline at end of file diff --git a/package.json b/package.json index 3c3da08..7c84d05 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "deploy": "graph deploy --node https://api.studio.thegraph.com/deploy/ ajna", "create-local": "graph create --node http://localhost:8020/ ajna", "remove-local": "graph remove --node http://localhost:8020/ ajna", - "deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 ajna", + "deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 ajna -l rc4", "test": "graph test" }, "dependencies": { diff --git a/schema.graphql b/schema.graphql index 51eeca4..b1b5dce 100644 --- a/schema.graphql +++ b/schema.graphql @@ -10,7 +10,7 @@ type Token @entity { # token name name: String! # token decimals - decimals: BigInt! + decimals: Int! # flag for whether the token is an ERC721 # TODO: make this a string, enabling new pool types (such as ERC1155) isERC721: Boolean! @@ -46,14 +46,18 @@ type Pool @entity { quoteToken: Token! # fee rate of the pool feeRate: BigDecimal! - # current total debt in the pool - currentDebt: BigDecimal! - # current pool inflator snapshot + # amount of deposit in the pool + poolSize: BigDecimal! + # total debt in the pool + debt: BigDecimal! + # debt in T0 terms, useful when the caller knows the pending inflator + t0debt: BigDecimal! + # pool inflator snapshot inflator: BigDecimal! - # block timestamp at which the inflator was last updated - inflatorUpdate: BigInt! - # current pool interest rate - interestRate: BigDecimal! + # interest rate paid by borrowers, excluding fees + borrowRate: BigDecimal! + # interest rate awarded to lenders, excluding fees + lendRate: BigDecimal! # total collateral pledged to the pool pledgedCollateral: BigDecimal! # total interest earned by all lenders in the pool @@ -62,11 +66,11 @@ type Pool @entity { txCount: BigInt! # LOANS INFORMATION - poolSize: BigDecimal! # total amount of quote tokens in the pool loansCount: BigInt! # total number of loans in the pool maxBorrower: Bytes! # address of the borrower with the highest TP in the pool - pendingInflator: BigDecimal! # pending inflator value - pendingInterestFactor: BigDecimal! # pending interest factor used to scale the inflator + # normalized total amount of tokens flashloaned from the pool + quoteTokenFlashloaned: BigDecimal! + collateralFlashloaned: BigDecimal! # PRICES INFORMATION hpb: BigDecimal! # current highest price bucket @@ -110,6 +114,10 @@ type Pool @entity { totalBondEscrowed: BigDecimal! # list of active liquidation auctions in the pool, sorted by kick time liquidationAuctions: [LiquidationAuction!]! + + # normalized contract balances, used for TVL + quoteTokenBalance: BigDecimal! + collateralBalance: BigDecimal! } type Bucket @entity { @@ -117,6 +125,8 @@ type Bucket @entity { id: Bytes! # bucket index bucketIndex: Int! + # bucket price + bucketPrice: BigDecimal! # current exchange rate of the bucket exchangeRate: BigDecimal! # pool address @@ -160,6 +170,8 @@ type Loan @entity { pool: Pool! # boolean indicating whether the loan is in liquidations inLiquidation: Boolean! + # most recent liquidation, whether active or inactive + liquidationAuction: LiquidationAuction # collateral tokens deposited in a pool by the borrower collateralPledged: BigDecimal! # collateralization of the borrow position in the pool @@ -196,7 +208,7 @@ type LiquidationAuction @entity { id: Bytes! # $poolAddress + '|' + $loanId + '|' + blockNumber pool: Pool! # pool in which the liquidation occurred borrower: Bytes! # address of the borrower being liquidated - auctionPrice: BigDecimal! # initial price at which auction was kicked # FIXME: not exposed + auctionPrice: BigDecimal! # price of auction upon kick or last take collateral: BigDecimal! # initial collateral up for auction collateralRemaining: BigDecimal! # collateral which has not been taken debt: BigDecimal! # initial debt to be covered by the auction @@ -207,7 +219,8 @@ type LiquidationAuction @entity { kickTime: BigInt! # block timestamp at which the kick was executed takes: [Take!]! # list of takes which occured using outside liquidity bucketTakes: [BucketTake!]! # list of takes which involved the pool's book - settle: AuctionSettle # used to reconcile the liquidation + settles: [Settle!]! # list of possibly-intermediate settles performed on the auction + settle: AuctionSettle # final settlement of the auction settleTime: BigInt # block timestamp at which the liquidation was settled settled: Boolean! # boolean indicating whether the auction has been settled bondSize: BigDecimal! # bond provided by kicker to initate auction @@ -219,19 +232,17 @@ type LiquidationAuction @entity { # used as start and take events are emitted as ReserveAuction type ReserveAuction @entity { id: Bytes! # $poolAddress + '|' + $burnEpoch - kicker: Bytes! # address of the kicker - kickerAward: BigDecimal! # amount of quote rewarded to kicker for starting reserves auction - kickTime: BigInt! # block timestamp at which the reserve auction started pool: Pool! # Pool in which the reserve auction occurred claimableReservesRemaining: BigDecimal! # uint256 claimable reserves remaining at start or at latest take auctionPrice: BigDecimal! # uint256 price at start or at latest take burnEpoch: BigInt! # uint256 burn epoch at which the reserve auction was started - reserveAuctionTakes: [ReserveAuctionKickOrTake!]! # list of reserve auction takes that occured during this auction process - ajnaBurnedAcrossAllTakes: BigDecimal! # total amount of ajna burned across all takes in the reserve auction + kick: ReserveAuctionKick! # kick information + takes: [ReserveAuctionTake!]! # list of reserve auction takes that occured during this auction process + ajnaBurned: BigDecimal! # total amount of ajna burned across all takes in the reserve auction } -# updated upon ApproveLpTransferors and RevokeLpTransferors -type LPTransferors @entity { +# LP transferors approved by a lender for a pool, updated upon Approve/RevokeLpTransferors +type LPTransferorList @entity { id: Bytes! # $poolAddress + '|' + $lender pool: Pool! # pool in which these transferors have been approved lender: Bytes! # address of the lender who has approved transferors @@ -245,7 +256,7 @@ type LPAllowance @entity { amount: BigDecimal! # size of the allowance (measured in LP) } -# updated upon SetLpAllowance and RevokeLpAllowance +# updated upon Increase/Decrease/RevokeLPAllowance type LPAllowances @entity { id: Bytes! # $poolAddress + '|' + $lender + '|' + $spender pool: Pool! # pool in which LP allowances have been granted @@ -289,7 +300,7 @@ type AuctionNFTSettle @entity(immutable: true) { id: Bytes! borrower: Bytes! # address collateral: BigDecimal! # uint256 - lps: BigDecimal! # uint256 + lp: BigDecimal! # uint256 index: Int! # uint256 blockNumber: BigInt! blockTimestamp: BigInt! @@ -331,16 +342,17 @@ type BucketBankruptcy @entity(immutable: true) { type BucketTake @entity(immutable: true) { id: Bytes! - taker: Bytes! # address of the taker + taker: Bytes! # address of the taker liquidationAuction: LiquidationAuction! # liquidation auction in which the take is occuring - loan: Loan! # loan which was taken - pool: Pool! # pool in which the take is occuring - borrower: Bytes! # address - index: Int! # uint256 - amount: BigDecimal! # uint256 - collateral: BigDecimal! # uint256 - bondChange: BigDecimal! # uint256 - isReward: Boolean! # bool + loan: Loan! # loan which was taken + pool: Pool! # pool in which the take is occuring + borrower: Bytes! # address + index: Int! # uint256 + auctionPrice: BigDecimal! # price of auction when taken + amount: BigDecimal! # uint256 + collateral: BigDecimal! # uint256 + bondChange: BigDecimal! # uint256 + isReward: Boolean! # bool blockNumber: BigInt! blockTimestamp: BigInt! transactionHash: Bytes! @@ -370,19 +382,27 @@ type DrawDebt @entity(immutable: true) { transactionHash: Bytes! } +type Flashloan @entity(immutable: true) { + id: Bytes! + pool: Pool! + borrower: Bytes! # address + amount: BigDecimal! # uint256 +} + type Kick @entity { id: Bytes! - kicker: Bytes! # address of the kicker - kickMomp: BigDecimal! # pool MOMP at the time of kicking - pool: Pool! # Pool in which a kick occurred - loan: Loan! # Loan which was kicked - locked: BigDecimal! # amount of quote from the bond locked in the kick (updated on take) - claimable: BigDecimal! # amount of quote from the bond claimable by the kicker (updated on settle) + kicker: Bytes! # address of the kicker + kickMomp: BigDecimal! # pool MOMP at the time of kicking + pool: Pool! # Pool in which a kick occurred + loan: Loan! # Loan which was kicked + locked: BigDecimal! # amount of quote from the bond locked in the kick (updated on take) TODO: rename bondLocked + claimable: BigDecimal! # amount of quote from the bond claimable by the kicker (updated on settle) TODO: rename bondClaimable? liquidationAuction: LiquidationAuction! # Liquidation auction which was initiated - borrower: Bytes! # address of the borrower being liquidated - debt: BigDecimal! # uint256 - collateral: BigDecimal! # uint256 - bond: BigDecimal! # uint256 + borrower: Bytes! # address of the borrower being liquidated + debt: BigDecimal! # amount of debt to be covered + collateral: BigDecimal! # amount of collateral available to liquidate + bond: BigDecimal! # liquidation bond paid by kicker + startingPrice: BigDecimal! # initial price of auction blockNumber: BigInt! blockTimestamp: BigInt! transactionHash: Bytes! @@ -451,16 +471,43 @@ type RepayDebt @entity(immutable: true) { transactionHash: Bytes! } -# a reserve auction take or start event -type ReserveAuctionKickOrTake @entity(immutable: true) { +# starts a claimable reserve auction +type ReserveAuctionKick @entity(immutable: true) { + id: Bytes! # transaction.hash + transaction.from + kicker: Bytes # address of the taker, null if emitted as start of event + kickerAward: BigDecimal! # amount of quote rewarded to kicker for starting reserves auction + reserveAuction: ReserveAuction! # reserve auction in which the take is occuring + pool: Pool! # pool in which the auction was kicked + claimableReserves: BigDecimal! # uint256 initial amount of claimable reserves in the auction + startingPrice: BigDecimal! # uint256 initial auction price + blockNumber: BigInt! + blockTimestamp: BigInt! + transactionHash: Bytes! +} + +# indicates AJNA was burned in a claimable reserve auction +type ReserveAuctionTake @entity(immutable: true) { id: Bytes! # transaction.hash + transaction.from taker: Bytes # address of the taker, null if emitted as start of event reserveAuction: ReserveAuction! # reserve auction in which the take is occuring pool: Pool! # pool in which the take is occuring claimableReservesRemaining: BigDecimal! # uint256 remaining amount of claimable reserves in the auction - auctionPrice: BigDecimal! # uint256 auction price at time of event emit - incrementalAjnaBurned: BigDecimal! # amount of ajna burned in this take - currentBurnEpoch: BigInt! # current burn epoch + auctionPrice: BigDecimal! # uint256 cost of purchasing one quote token, denominated in AJNA + quotePurchased: BigDecimal! # amount of quote token purchased by taker in this take + ajnaBurned: BigDecimal! # amount of AJNA burned by taker in this take + blockNumber: BigInt! + blockTimestamp: BigInt! + transactionHash: Bytes! +} + +# indicates rate was reset on an underutilized pool with high interest rate +type ResetInterestRate @entity(immutable: true) { + id: Bytes! + pool: Pool! # Pool whose interest rate was reset + oldBorrowRate: BigDecimal! # uint256 + newBorrowRate: BigDecimal! # uint256 + oldLendRate: BigDecimal! # uint256 + newLendRate: BigDecimal! # uint256 blockNumber: BigInt! blockTimestamp: BigInt! transactionHash: Bytes! @@ -481,26 +528,27 @@ type Settle @entity(immutable: true) { type Take @entity(immutable: true) { id: Bytes! - taker: Bytes! # address of the taker - pool: Pool! # Pool in which the take occurred - borrower: Bytes! # address of the borrower being liquidated + taker: Bytes! # address of the taker + pool: Pool! # Pool in which the take occurred + borrower: Bytes! # address of the borrower being liquidated liquidationAuction: LiquidationAuction! # Liquidation auction which was taken - loan: Loan! # Loan which was taken - amount: BigDecimal! # uint256 - collateral: BigDecimal! # uint256 - bondChange: BigDecimal! # uint256 - isReward: Boolean! # bool + loan: Loan! # Loan which was taken + auctionPrice: BigDecimal! # price of auction when taken + amount: BigDecimal! # uint256 + collateral: BigDecimal! # uint256 + bondChange: BigDecimal! # uint256 + isReward: Boolean! # bool blockNumber: BigInt! blockTimestamp: BigInt! transactionHash: Bytes! } -type TransferLPs @entity(immutable: true) { +type TransferLP @entity(immutable: true) { id: Bytes! owner: Bytes! # address newOwner: Bytes! # address indexes: [Int!]! # uint256[] - lps: BigDecimal! # uint256 + lp: BigDecimal! # uint256 blockNumber: BigInt! blockTimestamp: BigInt! transactionHash: Bytes! @@ -508,9 +556,11 @@ type TransferLPs @entity(immutable: true) { type UpdateInterestRate @entity(immutable: true) { id: Bytes! - pool: Pool! # Pool whose interest rate was updated - oldRate: BigDecimal! # uint256 - newRate: BigDecimal! # uint256 + pool: Pool! # Pool whose interest rate was updated + oldBorrowRate: BigDecimal! # uint256 + newBorrowRate: BigDecimal! # uint256 + oldLendRate: BigDecimal! # uint256 + newLendRate: BigDecimal! # uint256 blockNumber: BigInt! blockTimestamp: BigInt! transactionHash: Bytes! @@ -635,7 +685,7 @@ type StakedPosition @entity { epochs: [EpochReward!]! # list of EpochRewards associated to a staked position stakedEpoch: Int! # epoch in which the position was staked lastClaimedEpoch: Int! # last epoch which the position staked or claimed rewards in - # lpsAtStakeTime: BigDecimal! # the amount of LPB associated with a given index TODO: store information per index? + # lpAtStakeTime: BigDecimal! # the amount of LPB associated with a given index TODO: store information per index? # rateAtStakeTime: BigDecimal! # the exchange rate at a given index at stake time } @@ -650,7 +700,7 @@ type EpochReward @entity { # house state tracking information about a staked position in a single bucket index type StakedLiquidity @entity { id: Bytes! # $lendId + '|' + $StakedPosition.id - lpsAtStakeTime: BigDecimal! # the amount of LPB associated with a given index + lpAtStakeTime: BigDecimal! # the amount of LPB associated with a given index rateAtStakeTime: BigDecimal! # the exchange rate at a given index at stake time } diff --git a/src/erc-20-pool-factory.ts b/src/erc-20-pool-factory.ts index aa1cad5..c936011 100644 --- a/src/erc-20-pool-factory.ts +++ b/src/erc-20-pool-factory.ts @@ -1,3 +1,4 @@ +import { Address, BigInt } from "@graphprotocol/graph-ts" import { PoolCreated as PoolCreatedEvent } from "../generated/ERC20PoolFactory/ERC20PoolFactory" import { PoolCreated, Token } from "../generated/schema" import { ERC20PoolFactory, Pool } from "../generated/schema" @@ -47,6 +48,8 @@ export function handlePoolCreated(event: PoolCreatedEvent): void { // get pool initial interest rate const interestRateResults = poolContract.interestRateInfo() + // upon pool creation, MAU=0, so NIM=0.15, and LIM=0.85 + const lenderInterestMargin = BigInt.fromString("850000000000000000") // create Token entites associated with the pool const collateralTokenAddress = poolContract.collateralAddress() @@ -89,11 +92,12 @@ export function handlePoolCreated(event: PoolCreatedEvent): void { pool.createdAtBlockNumber = event.block.number pool.collateralToken = collateralToken.id pool.quoteToken = quoteToken.id - pool.currentDebt = ZERO_BD + pool.debt = ZERO_BD + pool.t0debt = ZERO_BD pool.feeRate = wadToDecimal(interestRateResults.value1) - pool.inflator = ONE_BD //ONE_WAD_BD - pool.inflatorUpdate = event.block.timestamp - pool.interestRate = wadToDecimal(interestRateResults.value0) + pool.inflator = ONE_BD + pool.borrowRate = wadToDecimal(interestRateResults.value0) + pool.lendRate = wadToDecimal(interestRateResults.value0.times(lenderInterestMargin)) pool.pledgedCollateral = ZERO_BD pool.totalInterestEarned = ZERO_BD // updated on ReserveAuction pool.txCount = ZERO_BI @@ -102,8 +106,8 @@ export function handlePoolCreated(event: PoolCreatedEvent): void { pool.poolSize = ZERO_BD pool.loansCount = ZERO_BI pool.maxBorrower = ZERO_ADDRESS - pool.pendingInflator = ONE_WAD_BD - pool.pendingInterestFactor = ZERO_BD + pool.quoteTokenFlashloaned = ZERO_BD + pool.collateralFlashloaned = ZERO_BD // pool prices information pool.hpb = ZERO_BD @@ -134,6 +138,10 @@ export function handlePoolCreated(event: PoolCreatedEvent): void { pool.totalBondEscrowed = ZERO_BD pool.liquidationAuctions = [] + // TVL information + pool.quoteTokenBalance = ZERO_BD + pool.collateralBalance = ZERO_BD + // add pool reference to factories' list of pools factory.pools = factory.pools.concat([pool.id]) diff --git a/src/erc-20-pool.ts b/src/erc-20-pool.ts index 5f6e051..8d25df0 100644 --- a/src/erc-20-pool.ts +++ b/src/erc-20-pool.ts @@ -1,29 +1,33 @@ -import { BigInt, Bytes } from "@graphprotocol/graph-ts" +import { BigInt, Bytes, log } from "@graphprotocol/graph-ts" import { AddCollateral as AddCollateralEvent, AddQuoteToken as AddQuoteTokenEvent, - ApproveLpTransferors as ApproveLpTransferorsEvent, + ApproveLPTransferors as ApproveLPTransferorsEvent, AuctionNFTSettle as AuctionNFTSettleEvent, AuctionSettle as AuctionSettleEvent, BondWithdrawn as BondWithdrawnEvent, BucketBankruptcy as BucketBankruptcyEvent, BucketTake as BucketTakeEvent, BucketTakeLPAwarded as BucketTakeLPAwardedEvent, + DecreaseLPAllowance as DecreaseLPAllowanceEvent, DrawDebt as DrawDebtEvent, + Flashloan as FlashloanEvent, + IncreaseLPAllowance as IncreaseLPAllowanceEvent, Kick as KickEvent, + KickReserveAuction as KickReserveAuctionEvent, LoanStamped as LoanStampedEvent, MoveQuoteToken as MoveQuoteTokenEvent, RemoveCollateral as RemoveCollateralEvent, RemoveQuoteToken as RemoveQuoteTokenEvent, RepayDebt as RepayDebtEvent, ReserveAuction as ReserveAuctionEvent, - RevokeLpAllowance as RevokeLpAllowanceEvent, - RevokeLpTransferors as RevokeLpTransferorsEvent, - SetLpAllowance as SetLpAllowanceEvent, + ResetInterestRate as ResetInterestRateEvent, + RevokeLPAllowance as RevokeLPAllowanceEvent, + RevokeLPTransferors as RevokeLPTransferorsEvent, Settle as SettleEvent, Take as TakeEvent, - TransferLPs as TransferLPsEvent, + TransferLP as TransferLPEvent, UpdateInterestRate as UpdateInterestRateEvent } from "../generated/templates/ERC20Pool/ERC20Pool" import { @@ -38,36 +42,41 @@ import { BucketTake, BucketTakeLPAwarded, DrawDebt, + Flashloan, Kick, Lend, LiquidationAuction, + Loan, LoanStamped, MoveQuoteToken, Pool, RemoveCollateral, RemoveQuoteToken, RepayDebt, - ReserveAuctionKickOrTake, + ReserveAuction, + ReserveAuctionKick, + ReserveAuctionTake, Settle, Take, Token, - TransferLPs, + TransferLP, UpdateInterestRate } from "../generated/schema" -import { ZERO_BD, ONE_BI } from "./utils/constants" +import { ZERO_BD, ONE_BI, TEN_BI } from "./utils/constants" import { addressToBytes, bigIntArrayToIntArray, wadToDecimal } from "./utils/convert" import { loadOrCreateAccount, updateAccountLends, updateAccountLoans, updateAccountPools, updateAccountKicks, updateAccountTakes, updateAccountSettles, updateAccountReserveAuctions } from "./utils/account" import { getBucketId, getBucketInfo, loadOrCreateBucket } from "./utils/bucket" import { getLendId, loadOrCreateLend } from "./utils/lend" import { getLoanId, loadOrCreateLoan } from "./utils/loan" -import { getBucketTakeLPAwardedId, getLiquidationAuctionId, getAuctionInfoERC20Pool, loadOrCreateLiquidationAuction, updateLiquidationAuction } from "./utils/liquidation" -import { getBurnInfo, getCurrentBurnEpoch, updatePool, addLiquidationToPool, addReserveAuctionToPool, getLenderInfo } from "./utils/pool" +import { getBucketTakeLPAwardedId, getLiquidationAuctionId, getAuctionInfoERC20Pool, loadOrCreateLiquidationAuction, updateLiquidationAuction, getAuctionStatus } from "./utils/liquidation" +import { getBurnInfo, getCurrentBurnEpoch, updatePool, addLiquidationToPool, addReserveAuctionToPool, getLenderInfo, getLenderInterestMargin } from "./utils/pool" import { collateralizationAtLup, lpbValueInQuote, thresholdPrice } from "./utils/common" import { getReserveAuctionId, loadOrCreateReserveAuction, reserveAuctionKickerReward } from "./utils/reserve-auction" import { incrementTokenTxCount } from "./utils/token-erc20" import { approveTransferors, loadOrCreateTransferors, revokeTransferors } from "./utils/lp-transferors" -import { loadOrCreateAllowances, revokeAllowances, setAllowances } from "./utils/lp-allowances" +import { loadOrCreateAllowances, increaseAllowances, decreaseAllowances, revokeAllowances } from "./utils/lp-allowances" +import { wmul } from "./utils/math" export function handleAddCollateral(event: AddCollateralEvent): void { const addCollateral = new AddCollateral( @@ -109,7 +118,7 @@ export function handleAddCollateral(event: AddCollateralEvent): void { // update lend state const lendId = getLendId(bucketId, accountId) const lend = loadOrCreateLend(bucketId, lendId, pool.id, addCollateral.actor) - lend.lpb = lend.lpb.plus(wadToDecimal(event.params.lpAwarded)) + lend.lpb = lend.lpb.plus(addCollateral.lpAwarded) lend.lpbValueInQuote = lpbValueInQuote(pool.id, bucket.bucketIndex, lend.lpb) // update account's list of pools and lends if necessary @@ -170,7 +179,7 @@ export function handleAddQuoteToken(event: AddQuoteTokenEvent): void { // update lend state const lendId = getLendId(bucketId, accountId) const lend = loadOrCreateLend(bucketId, lendId, pool.id, addQuoteToken.lender) - lend.lpb = lend.lpb.plus(wadToDecimal(event.params.lpAwarded)) + lend.lpb = lend.lpb.plus(addQuoteToken.lpAwarded) lend.lpbValueInQuote = lpbValueInQuote(pool.id, bucket.bucketIndex, lend.lpb) // update account's list of pools and lends if necessary @@ -190,8 +199,8 @@ export function handleAddQuoteToken(event: AddQuoteTokenEvent): void { addQuoteToken.save() } -export function handleApproveLpTransferors( - event: ApproveLpTransferorsEvent +export function handleApproveLPTransferors( + event: ApproveLPTransferorsEvent ): void { const poolId = addressToBytes(event.address) const entity = loadOrCreateTransferors(poolId, event.params.lender) @@ -207,7 +216,7 @@ export function handleAuctionNFTSettle(event: AuctionNFTSettleEvent): void { ) entity.borrower = event.params.borrower entity.collateral = wadToDecimal(event.params.collateral) - entity.lps = wadToDecimal(event.params.lps) + entity.lp = wadToDecimal(event.params.lp) entity.index = event.params.index.toU32() entity.blockNumber = event.block.number @@ -231,33 +240,30 @@ export function handleAuctionSettle(event: AuctionSettleEvent): void { auctionSettle.transactionHash = event.transaction.hash // update entities - const pool = Pool.load(addressToBytes(event.address)) - if (pool != null) { - // pool doesn't need to be updated here as it was already updated in the concurrent Settle event - - // update auction state - const loanId = getLoanId(pool.id, addressToBytes(event.params.borrower)) - const auctionId = getLiquidationAuctionId(pool.id, loanId, event.block.number) - const auction = LiquidationAuction.load(auctionId)! - auction.settle = auctionSettle.id - auction.settleTime = auctionSettle.blockTimestamp - auction.settled = true - - // update loan state - const loan = loadOrCreateLoan(loanId, pool.id, addressToBytes(event.params.borrower)) - loan.debt = ZERO_BD - loan.collateralPledged = auctionSettle.collateral - loan.inLiquidation = false - loan.collateralization = ZERO_BD - loan.tp = ZERO_BD - - // save entities to the store - loan.save() - - // update auctionSettle pointer - auctionSettle.loan = loan.id - } - + const pool = Pool.load(addressToBytes(event.address))! + // pool doesn't need to be updated here as it was already updated in the concurrent Settle event + + // update auction state + const loanId = getLoanId(pool.id, addressToBytes(event.params.borrower)) + const loan = Loan.load(loanId)! + const auctionId = loan.liquidationAuction! + const auction = LiquidationAuction.load(auctionId)! + auction.settle = auctionSettle.id + auction.settleTime = auctionSettle.blockTimestamp + auction.settled = true + auction.save() + + // update loan state + loan.debt = ZERO_BD + loan.collateralPledged = auctionSettle.collateral + loan.inLiquidation = false + loan.collateralization = ZERO_BD + loan.tp = ZERO_BD + loan.save() + + // update auctionSettle pointers + auctionSettle.pool = pool.id + auctionSettle.loan = loan.id auctionSettle.save() } @@ -329,72 +335,69 @@ export function handleBucketTake(event: BucketTakeEvent): void { bucketTake.transactionHash = event.transaction.hash // update entities - const pool = Pool.load(addressToBytes(event.address)) - if (pool != null) { - // update pool state - updatePool(pool) - pool.txCount = pool.txCount.plus(ONE_BI) - - // update tx count for a pools tokens - incrementTokenTxCount(pool) - - // update taker account state - const account = loadOrCreateAccount(bucketTake.taker) - account.txCount = account.txCount.plus(ONE_BI) - updateAccountTakes(account, bucketTake.id) - updateAccountPools(account, pool) - - // update loan state - const loanId = getLoanId(pool.id, addressToBytes(event.params.borrower)) - const loan = loadOrCreateLoan(loanId, pool.id, addressToBytes(event.params.borrower)) - loan.debt = loan.debt.minus(wadToDecimal(event.params.amount)) - loan.collateralPledged = loan.collateralPledged.minus(wadToDecimal(event.params.collateral)) - if (loan.debt.notEqual(ZERO_BD) && loan.collateralPledged.notEqual(ZERO_BD)) { - loan.collateralization = collateralizationAtLup(loan.debt, loan.collateralPledged, pool.lup) - loan.tp = thresholdPrice(loan.debt, loan.collateralPledged) - } - else { - // set collateralization and tp to zero if loan is fully repaid - loan.collateralization = ZERO_BD - loan.tp = ZERO_BD - } - - // retrieve auction information on the take's auction - const auctionInfo = getAuctionInfoERC20Pool(bucketTake.borrower, pool) - - // update liquidation auction state - const auctionId = getLiquidationAuctionId(pool.id, loan.id, bucketTake.blockNumber) - const auction = LiquidationAuction.load(auctionId)! - updateLiquidationAuction(auction, auctionInfo, pool.id) - auction.debtRemaining = auction.debtRemaining.minus(wadToDecimal(event.params.amount)) - auction.collateralRemaining = auction.collateralRemaining.minus(wadToDecimal(event.params.collateral)) - auction.bucketTakes.push(bucketTake.id) - - // update kick and pool for the change in bond as a result of the take - const kick = Kick.load(auction.kick)! - if (bucketTake.isReward) { - // reward kicker if take is below neutral price - pool.totalBondEscrowed = pool.totalBondEscrowed.plus(wadToDecimal(event.params.bondChange)) - kick.locked = kick.locked.plus(wadToDecimal(event.params.bondChange)) - } else { - // penalize kicker if take is above neutral price - pool.totalBondEscrowed = pool.totalBondEscrowed.minus(wadToDecimal(event.params.bondChange)) - kick.locked = kick.locked.minus(wadToDecimal(event.params.bondChange)) - } - - // update bucketTake pointers - bucketTake.liquidationAuction = auction.id - bucketTake.loan = loanId - bucketTake.pool = pool.id + const pool = Pool.load(addressToBytes(event.address))! + // update pool state + updatePool(pool) + pool.txCount = pool.txCount.plus(ONE_BI) + incrementTokenTxCount(pool) + + // update taker account state + const account = loadOrCreateAccount(bucketTake.taker) + account.txCount = account.txCount.plus(ONE_BI) + updateAccountTakes(account, bucketTake.id) + updateAccountPools(account, pool) + + // update loan state + const loanId = getLoanId(pool.id, addressToBytes(event.params.borrower)) + const loan = loadOrCreateLoan(loanId, pool.id, addressToBytes(event.params.borrower)) + loan.debt = loan.debt.minus(wadToDecimal(event.params.amount)) + loan.collateralPledged = loan.collateralPledged.minus(wadToDecimal(event.params.collateral)) + if (loan.debt.notEqual(ZERO_BD) && loan.collateralPledged.notEqual(ZERO_BD)) { + loan.collateralization = collateralizationAtLup(loan.debt, loan.collateralPledged, pool.lup) + loan.tp = thresholdPrice(loan.debt, loan.collateralPledged) + } else { + // set collateralization and tp to zero if loan is fully repaid + loan.collateralization = ZERO_BD + loan.tp = ZERO_BD + } - // save entities to the store - account.save() - auction.save() - loan.save() - pool.save() + // retrieve auction information on the take's auction + const auctionInfo = getAuctionInfoERC20Pool(bucketTake.borrower, pool) + const auctionStatus = getAuctionStatus(pool, event.params.borrower) + + // update liquidation auction state + const auctionId = loan.liquidationAuction! + const auction = LiquidationAuction.load(auctionId)! + updateLiquidationAuction(auction, auctionInfo, auctionStatus) + auction.debtRemaining = auction.debtRemaining.minus(wadToDecimal(event.params.amount)) + auction.collateralRemaining = auction.collateralRemaining.minus(wadToDecimal(event.params.collateral)) + auction.bucketTakes = auction.bucketTakes.concat([bucketTake.id]) + + bucketTake.auctionPrice = wadToDecimal(auctionStatus.price) + + // update kick and pool for the change in bond as a result of the take + const kick = Kick.load(auction.kick)! + if (bucketTake.isReward) { + // reward kicker if take is below neutral price + pool.totalBondEscrowed = pool.totalBondEscrowed.plus(wadToDecimal(event.params.bondChange)) + kick.locked = kick.locked.plus(wadToDecimal(event.params.bondChange)) + } else { + // penalize kicker if take is above neutral price + pool.totalBondEscrowed = pool.totalBondEscrowed.minus(wadToDecimal(event.params.bondChange)) + kick.locked = kick.locked.minus(wadToDecimal(event.params.bondChange)) } + // update bucketTake pointers + bucketTake.liquidationAuction = auction.id + bucketTake.loan = loanId + bucketTake.pool = pool.id + + // save entities to the store + account.save() + auction.save() + loan.save() + pool.save() bucketTake.save() } @@ -461,6 +464,21 @@ export function handleBucketTakeLPAwarded( bucketTakeLpAwarded.save() } +export function handleDecreaseLPAllowance(event: DecreaseLPAllowanceEvent): void { + const poolId = addressToBytes(event.address) + const lender = event.transaction.from + const entity = loadOrCreateAllowances(poolId, lender, event.params.spender) + decreaseAllowances(entity, event.params.indexes, event.params.amounts) + + const pool = Pool.load(poolId) + if (pool != null) { + pool.txCount = pool.txCount.plus(ONE_BI) + pool.save() + } + + entity.save() +} + export function handleDrawDebt(event: DrawDebtEvent): void { const drawDebt = new DrawDebt( event.transaction.hash.concatI32(event.logIndex.toI32()) @@ -478,7 +496,7 @@ export function handleDrawDebt(event: DrawDebtEvent): void { const pool = Pool.load(addressToBytes(event.address)) if (pool != null) { // update pool state - pool.currentDebt = pool.currentDebt.plus(wadToDecimal(event.params.amountBorrowed)) + pool.debt = pool.debt.plus(wadToDecimal(event.params.amountBorrowed)) pool.pledgedCollateral = pool.pledgedCollateral.plus(wadToDecimal(event.params.collateralPledged)) updatePool(pool) pool.txCount = pool.txCount.plus(ONE_BI) @@ -514,6 +532,45 @@ export function handleDrawDebt(event: DrawDebtEvent): void { drawDebt.save() } +export function handleFlashloan(event: FlashloanEvent): void { + const flashloan = new Flashloan(event.transaction.hash.concatI32(event.logIndex.toI32())) + const pool = Pool.load(addressToBytes(event.address))! + const token = Token.load(addressToBytes(event.params.token))! + const scaleFactor = TEN_BI.pow(18 - token.decimals as u8) + + flashloan.pool = pool.id + flashloan.borrower = event.params.receiver + + const normalizedAmount = wadToDecimal(event.params.amount.times(scaleFactor)) + flashloan.amount = normalizedAmount + if (token.id == pool.quoteToken) { + pool.quoteTokenFlashloaned = pool.quoteTokenFlashloaned.plus(normalizedAmount) + } else if (token.id == pool.collateralToken) { + pool.collateralFlashloaned = pool.collateralFlashloaned.plus(normalizedAmount) + } + token.txCount = token.txCount.plus(ONE_BI) + pool.txCount = pool.txCount.plus(ONE_BI) + + token.save() + pool.save() + flashloan.save() +} + +export function handleIncreaseLPAllowance(event: IncreaseLPAllowanceEvent): void { + const poolId = addressToBytes(event.address) + const lender = event.transaction.from + const entity = loadOrCreateAllowances(poolId, lender, event.params.spender) + increaseAllowances(entity, event.params.indexes, event.params.amounts) + + const pool = Pool.load(poolId) + if (pool != null) { + pool.txCount = pool.txCount.plus(ONE_BI) + pool.save() + } + + entity.save() +} + export function handleKick(event: KickEvent): void { const kick = new Kick( event.transaction.hash.concatI32(event.logIndex.toI32()) @@ -532,54 +589,53 @@ export function handleKick(event: KickEvent): void { kick.kicker = event.transaction.from // update entities - const pool = Pool.load(addressToBytes(event.address)) - if (pool != null) { - // update pool state - updatePool(pool) - pool.totalBondEscrowed = pool.totalBondEscrowed.plus(wadToDecimal(event.params.bond)) - pool.txCount = pool.txCount.plus(ONE_BI) - - // update tx count for a pools tokens - incrementTokenTxCount(pool) - - // update kicker account state - const account = loadOrCreateAccount(kick.kicker) - account.txCount = account.txCount.plus(ONE_BI) - updateAccountKicks(account, kick) - updateAccountPools(account, pool) - - // update loan state - const loanId = getLoanId(pool.id, addressToBytes(event.params.borrower)) - const loan = loadOrCreateLoan(loanId, pool.id, kick.borrower) - loan.inLiquidation = true - loan.collateralPledged = kick.collateral - loan.debt = kick.debt // update loan debt to account for kick penalty - loan.collateralization = collateralizationAtLup(loan.debt, loan.collateralPledged, pool.lup) - loan.tp = thresholdPrice(loan.debt, loan.collateralPledged) - - // retrieve auction information on the kicked loan - const auctionInfo = getAuctionInfoERC20Pool(kick.borrower, pool) - - // update liquidation auction state - const auctionId = getLiquidationAuctionId(pool.id, loan.id, kick.blockNumber) - const auction = loadOrCreateLiquidationAuction(pool.id, auctionId, kick, loan) - updateLiquidationAuction(auction, auctionInfo, pool.id) - - kick.kickMomp = wadToDecimal(auctionInfo.kickMomp) - kick.pool = pool.id - kick.loan = loan.id - kick.liquidationAuction = auctionId - - // track new liquidation auction at the pool level - addLiquidationToPool(pool, auction) - - // save entities to store - account.save() - auction.save() - loan.save() - pool.save() - } + const pool = Pool.load(addressToBytes(event.address))! + // update pool state + updatePool(pool) + pool.totalBondEscrowed = pool.totalBondEscrowed.plus(wadToDecimal(event.params.bond)) + pool.txCount = pool.txCount.plus(ONE_BI) + incrementTokenTxCount(pool) + + // update kicker account state + const account = loadOrCreateAccount(kick.kicker) + account.txCount = account.txCount.plus(ONE_BI) + updateAccountKicks(account, kick) + updateAccountPools(account, pool) + + // update loan state + const loanId = getLoanId(pool.id, addressToBytes(event.params.borrower)) + const loan = loadOrCreateLoan(loanId, pool.id, kick.borrower) + loan.inLiquidation = true + loan.collateralPledged = kick.collateral + loan.debt = kick.debt // update loan debt to account for kick penalty + loan.collateralization = collateralizationAtLup(loan.debt, loan.collateralPledged, pool.lup) + loan.tp = thresholdPrice(loan.debt, loan.collateralPledged) + + // retrieve auction information on the kicked loan + const auctionInfo = getAuctionInfoERC20Pool(kick.borrower, pool) + const auctionStatus = getAuctionStatus(pool, event.params.borrower) + + // update liquidation auction state + const auctionId = getLiquidationAuctionId(pool.id, loan.id, kick.blockNumber) + const auction = loadOrCreateLiquidationAuction(pool.id, auctionId, kick, loan) + updateLiquidationAuction(auction, auctionInfo, auctionStatus) + + kick.kickMomp = wadToDecimal(auctionInfo.kickMomp) + kick.startingPrice = wadToDecimal(auctionStatus.price) + kick.pool = pool.id + kick.loan = loan.id + kick.liquidationAuction = auctionId + loan.liquidationAuction = auctionId + + // track new liquidation auction at the pool level + addLiquidationToPool(pool, auction) + + // save entities to store + account.save() + auction.save() + loan.save() + pool.save() kick.save() } @@ -642,7 +698,14 @@ export function handleMoveQuoteToken(event: MoveQuoteTokenEvent): void { // update from bucket lend state const fromBucketLendId = getLendId(fromBucketId, event.params.lender) const fromBucketLend = loadOrCreateLend(fromBucketId, fromBucketLendId, pool.id, moveQuoteToken.lender) - fromBucketLend.lpb = fromBucketLend.lpb.minus(wadToDecimal(event.params.lpRedeemedFrom)) + const lpRedeemedFrom = wadToDecimal(event.params.lpRedeemedFrom) + if (lpRedeemedFrom.le(fromBucketLend.lpb)) { + fromBucketLend.lpb = fromBucketLend.lpb.minus(lpRedeemedFrom) + } else { + log.warning('handleMoveQuoteToken: lender {} redeemed more LP ({}) than Lend entity was aware of ({}); resetting to 0', + [moveQuoteToken.lender.toHexString(), lpRedeemedFrom.toString(), fromBucketLend.lpb.toString()]) + fromBucketLend.lpb = ZERO_BD + } fromBucketLend.lpbValueInQuote = lpbValueInQuote(pool.id, fromBucket.bucketIndex, fromBucketLend.lpb) // update to bucket lend state @@ -714,7 +777,13 @@ export function handleRemoveCollateral(event: RemoveCollateralEvent): void { // update lend state const lendId = getLendId(bucketId, accountId) const lend = loadOrCreateLend(bucketId, lendId, pool.id, removeCollateral.claimer) - lend.lpb = lend.lpb.minus(removeCollateral.lpRedeemed) + if (removeCollateral.lpRedeemed.le(lend.lpb)) { + lend.lpb = lend.lpb.minus(removeCollateral.lpRedeemed) + } else { + log.warning('handleRemoveCollateral: claimer {} redeemed more LP ({}) than Lend entity was aware of ({}); resetting to 0', + [removeCollateral.claimer.toHexString(), removeCollateral.lpRedeemed.toString(), lend.lpb.toString()]) + lend.lpb = ZERO_BD + } lend.lpbValueInQuote = lpbValueInQuote(pool.id, bucket.bucketIndex, lend.lpb) // update account's list of pools and lends if necessary @@ -775,12 +844,14 @@ export function handleRemoveQuoteToken(event: RemoveQuoteTokenEvent): void { // update lend state const lendId = getLendId(bucketId, accountId) const lend = loadOrCreateLend(bucketId, lendId, pool.id, removeQuote.lender) - - // FIXME: seems this sometimes underflows - // if (removeQuote.lpRedeemed >= lend.lpb) { - // lend.lpb = lend.lpb.minus(removeQuote.lpRedeemed) - // lend.lpbValueInQuote = lpbValueInQuote(pool.id, bucket.bucketIndex, lend.lpb) - // } + if (removeQuote.lpRedeemed.le(lend.lpb)) { + lend.lpb = lend.lpb.minus(removeQuote.lpRedeemed) + } else { + log.warning('handleRemoveQuoteToken: lender {} redeemed more LP ({}) than Lend entity was aware of ({}); resetting to 0', + [removeQuote.lender.toHexString(), removeQuote.lpRedeemed.toString(), lend.lpb.toString()]) + lend.lpb = ZERO_BD + } + lend.lpbValueInQuote = lpbValueInQuote(pool.id, bucket.bucketIndex, lend.lpb) // update account's list of pools and lends if necessary updateAccountPools(account, pool) @@ -816,7 +887,7 @@ export function handleRepayDebt(event: RepayDebtEvent): void { const pool = Pool.load(addressToBytes(event.address)) if (pool != null) { // update pool state - pool.currentDebt = pool.currentDebt.minus(wadToDecimal(event.params.quoteRepaid)) + pool.debt = pool.debt.minus(wadToDecimal(event.params.quoteRepaid)) pool.pledgedCollateral = pool.pledgedCollateral.minus(wadToDecimal(event.params.collateralPulled)) updatePool(pool) pool.txCount = pool.txCount.plus(ONE_BI) @@ -851,91 +922,128 @@ export function handleRepayDebt(event: RepayDebtEvent): void { repayDebt.save() } + // called on both start and take reserves -export function handleReserveAuction(event: ReserveAuctionEvent): void { - const reserveAuctionEvent = new ReserveAuctionKickOrTake( +export function handleReserveAuctionKick(event: KickReserveAuctionEvent): void { + // create the ReserveAuctionKick entity (immutable) and ReserveAuction entity (mutable) + const reserveKick = new ReserveAuctionKick( event.transaction.hash.concat(event.transaction.from) ) - reserveAuctionEvent.claimableReservesRemaining = wadToDecimal(event.params.claimableReservesRemaining) - reserveAuctionEvent.auctionPrice = wadToDecimal(event.params.auctionPrice) - reserveAuctionEvent.currentBurnEpoch = event.params.currentBurnEpoch - - reserveAuctionEvent.blockNumber = event.block.number - reserveAuctionEvent.blockTimestamp = event.block.timestamp - reserveAuctionEvent.transactionHash = event.transaction.hash - - // update entities - const pool = Pool.load(addressToBytes(event.address)) - if (pool != null) { - // update pool state - updatePool(pool) - pool.txCount = pool.txCount.plus(ONE_BI) - - // update tx count for a pools tokens - incrementTokenTxCount(pool) + const pool = Pool.load(addressToBytes(event.address))! + const reserveAuction = loadOrCreateReserveAuction(pool.id, event.params.currentBurnEpoch) + + reserveKick.kicker = event.transaction.from + reserveKick.reserveAuction = reserveAuction.id + reserveKick.pool = pool.id + reserveKick.claimableReserves = wadToDecimal(event.params.claimableReservesRemaining) + reserveKick.startingPrice = wadToDecimal(event.params.auctionPrice) + + reserveKick.blockNumber = event.block.number + reserveKick.blockTimestamp = event.block.timestamp + reserveKick.transactionHash = event.transaction.hash + + reserveAuction.claimableReservesRemaining = reserveKick.claimableReserves + reserveAuction.auctionPrice = reserveKick.startingPrice + reserveAuction.kick = reserveKick.id + + // update pool state + pool.burnEpoch = event.params.currentBurnEpoch + updatePool(pool) + addReserveAuctionToPool(pool, reserveAuction) + pool.txCount = pool.txCount.plus(ONE_BI) + reserveKick.kickerAward = reserveAuctionKickerReward(pool) - // update account state - const accountId = addressToBytes(event.transaction.from) - const account = loadOrCreateAccount(accountId) - account.txCount = account.txCount.plus(ONE_BI) - updateAccountReserveAuctions(account, reserveAuctionEvent.id) - - // retrieve ajna burn information from the pool - const currentBurnEpoch = getCurrentBurnEpoch(pool) - const burnInfo = getBurnInfo(pool, currentBurnEpoch) - - // update reserve auction process state - const reserveAuctionId = getReserveAuctionId(pool.id, currentBurnEpoch) - const reserveAuction = loadOrCreateReserveAuction(pool.id, reserveAuctionId) - - // if kicker is null, then assume this is the start of the auction, - // and set kicker to the caller of startReserveAuction and calculate kickerAward - if (reserveAuction.kicker == Bytes.empty()) { - reserveAuction.burnEpoch = currentBurnEpoch - reserveAuction.kicker = event.transaction.from - reserveAuction.kickerAward = reserveAuctionKickerReward(pool) - reserveAuction.kickTime = event.block.timestamp - reserveAuction.pool = pool.id - - // set the total ajna burned at the start of the auction - pool.totalAjnaBurned = wadToDecimal(burnInfo.totalBurned) - } - else { - // if kicker is not null, then assume this is the takeReserveAuction call - // and set taker to the caller of takeReserveAuction - reserveAuctionEvent.taker = event.transaction.from - // concat the additional event into the reserveAuction list - reserveAuction.reserveAuctionTakes = reserveAuction.reserveAuctionTakes.concat([reserveAuctionEvent.id]) - } + // update account state + const account = loadOrCreateAccount(addressToBytes(event.transaction.from)) + account.txCount = account.txCount.plus(ONE_BI) + updateAccountReserveAuctions(account, reserveAuction.id) - // update ReserveAuction with latest auction state - reserveAuction.auctionPrice = wadToDecimal(event.params.auctionPrice) - reserveAuction.claimableReservesRemaining = wadToDecimal(event.params.claimableReservesRemaining) + account.save() + pool.save() + reserveAuction.save() + reserveKick.save() +} - // update burn information of the reserve auction take - // since only one reserve auction can occur at a time, look at the difference since the last reserve auction - reserveAuctionEvent.incrementalAjnaBurned = wadToDecimal(burnInfo.totalBurned).minus(pool.totalAjnaBurned) - reserveAuction.ajnaBurnedAcrossAllTakes = reserveAuction.ajnaBurnedAcrossAllTakes.plus(reserveAuctionEvent.incrementalAjnaBurned) +// called on both start and take reserves +export function handleReserveAuctionTake(event: ReserveAuctionEvent): void { + const reserveTake = new ReserveAuctionTake( + event.transaction.hash.concat(event.transaction.from) + ) + const pool = Pool.load(addressToBytes(event.address))! + const reserveAuction = loadOrCreateReserveAuction(pool.id, event.params.currentBurnEpoch) + + reserveTake.taker = event.transaction.from + reserveTake.reserveAuction = reserveAuction.id + reserveTake.pool = pool.id + reserveTake.claimableReservesRemaining = wadToDecimal(event.params.claimableReservesRemaining) + reserveTake.auctionPrice = wadToDecimal(event.params.auctionPrice) + + // retrieve ajna burn information from the pool + const burnInfo = getBurnInfo(pool, event.params.currentBurnEpoch) + // update burn information of the reserve auction take + // since only one reserve auction can occur at a time, look at the difference since the last reserve auction + reserveTake.ajnaBurned = wadToDecimal(burnInfo.totalBurned).minus(pool.totalAjnaBurned) + reserveAuction.claimableReservesRemaining = reserveTake.claimableReservesRemaining + reserveAuction.auctionPrice = reserveTake.auctionPrice + reserveAuction.ajnaBurned = reserveAuction.ajnaBurned.plus(reserveTake.ajnaBurned) + reserveAuction.takes = reserveAuction.takes.concat([reserveTake.id]) + + // event does not provide amount purchased; use auctionPrice and ajnaBurned to calculate + reserveTake.quotePurchased = reserveTake.ajnaBurned.div(reserveTake.auctionPrice) + + reserveTake.blockNumber = event.block.number + reserveTake.blockTimestamp = event.block.timestamp + reserveTake.transactionHash = event.transaction.hash + + // update pool state + pool.totalAjnaBurned = wadToDecimal(burnInfo.totalBurned) + pool.totalInterestEarned = wadToDecimal(burnInfo.totalInterest) + updatePool(pool) + pool.txCount = pool.txCount.plus(ONE_BI) + incrementTokenTxCount(pool) - // update pool burn and interest earned information - pool.burnEpoch = currentBurnEpoch - pool.totalAjnaBurned = wadToDecimal(burnInfo.totalBurned) - pool.totalInterestEarned = wadToDecimal(burnInfo.totalInterest) - addReserveAuctionToPool(pool, reserveAuction) + // update account state + const account = loadOrCreateAccount(addressToBytes(event.transaction.from)) + account.txCount = account.txCount.plus(ONE_BI) + updateAccountReserveAuctions(account, reserveAuction.id) - reserveAuctionEvent.pool = pool.id - reserveAuctionEvent.reserveAuction = reserveAuctionId + // save entities to store + account.save() + pool.save() + reserveAuction.save() + reserveTake.save() +} - // save entities to store - account.save() - pool.save() - reserveAuction.save() - } +export function handleResetInterestRate(event: ResetInterestRateEvent): void { + const resetInterestRate = new UpdateInterestRate( + event.transaction.hash.concatI32(event.logIndex.toI32()) + ) + const poolAddress = addressToBytes(event.address) + const pool = Pool.load(poolAddress)! + const lenderInterestMargin = getLenderInterestMargin(poolAddress) + + resetInterestRate.pool = pool.id + resetInterestRate.oldBorrowRate = pool.borrowRate + resetInterestRate.oldLendRate = pool.lendRate + resetInterestRate.newBorrowRate = wadToDecimal(event.params.newRate) + resetInterestRate.newLendRate = wadToDecimal(wmul(event.params.newRate, lenderInterestMargin)) + + resetInterestRate.blockNumber = event.block.number + resetInterestRate.blockTimestamp = event.block.timestamp + resetInterestRate.transactionHash = event.transaction.hash + + // update pool state + updatePool(pool) + pool.borrowRate = resetInterestRate.newBorrowRate + pool.lendRate = resetInterestRate.newLendRate + pool.txCount = pool.txCount.plus(ONE_BI) - reserveAuctionEvent.save() + // save entities to the store + pool.save() + resetInterestRate.save() } -export function handleRevokeLpAllowance(event: RevokeLpAllowanceEvent): void { +export function handleRevokeLPAllowance(event: RevokeLPAllowanceEvent): void { const poolId = addressToBytes(event.address) const lender = event.transaction.from const entity = loadOrCreateAllowances(poolId, lender, event.params.spender) @@ -950,8 +1058,8 @@ export function handleRevokeLpAllowance(event: RevokeLpAllowanceEvent): void { entity.save() } -export function handleRevokeLpTransferors( - event: RevokeLpTransferorsEvent +export function handleRevokeLPTransferors( + event: RevokeLPTransferorsEvent ): void { const poolId = addressToBytes(event.address) const entity = loadOrCreateTransferors(poolId, event.params.lender) @@ -966,21 +1074,6 @@ export function handleRevokeLpTransferors( entity.save() } -export function handleSetLpAllowance(event: SetLpAllowanceEvent): void { - const poolId = addressToBytes(event.address) - const lender = event.transaction.from - const entity = loadOrCreateAllowances(poolId, lender, event.params.spender) - setAllowances(entity, event.params.indexes, event.params.amounts) - - const pool = Pool.load(poolId) - if (pool != null) { - pool.txCount = pool.txCount.plus(ONE_BI) - pool.save() - } - - entity.save() -} - export function handleTake(event: TakeEvent): void { const take = new Take( event.transaction.hash.concatI32(event.logIndex.toI32()) @@ -996,74 +1089,69 @@ export function handleTake(event: TakeEvent): void { take.blockTimestamp = event.block.timestamp take.transactionHash = event.transaction.hash - // update entities - const pool = Pool.load(addressToBytes(event.address)) - if (pool != null) { - // update pool state - updatePool(pool) - pool.txCount = pool.txCount.plus(ONE_BI) - - // update tx count for a pools tokens - incrementTokenTxCount(pool) - - // update taker account state - const account = loadOrCreateAccount(take.taker) - account.txCount = account.txCount.plus(ONE_BI) - updateAccountTakes(account, take.id) - updateAccountPools(account, pool) - - // update loan state - const loanId = getLoanId(pool.id, addressToBytes(event.params.borrower)) - const loan = loadOrCreateLoan(loanId, pool.id, take.borrower) - loan.debt = loan.debt.minus(wadToDecimal(event.params.amount)) - loan.collateralPledged = loan.collateralPledged.minus(wadToDecimal(event.params.collateral)) - if (loan.debt.notEqual(ZERO_BD) && loan.collateralPledged.notEqual(ZERO_BD)) { - loan.collateralization = collateralizationAtLup(loan.debt, loan.collateralPledged, pool.lup) - loan.tp = thresholdPrice(loan.debt, loan.collateralPledged) - } else { - // set collateralization and tp to zero if loan is fully repaid - loan.collateralization = ZERO_BD - loan.tp = ZERO_BD - } - - // retrieve auction information on the take's auction - const auctionInfo = getAuctionInfoERC20Pool(take.borrower, pool) - - // update liquidation auction state - const auctionId = getLiquidationAuctionId(pool.id, loan.id, take.blockNumber) - const auction = LiquidationAuction.load(auctionId)! - updateLiquidationAuction(auction, auctionInfo, pool.id) - auction.takes.push(take.id) - const debtCovered = wadToDecimal(event.params.amount) - auction.debtRemaining = auction.debtRemaining.minus(debtCovered) - const collateralPurchased = wadToDecimal(event.params.collateral) - auction.collateralRemaining = auction.collateralRemaining.minus(collateralPurchased) - pool.pledgedCollateral = pool.pledgedCollateral.minus(collateralPurchased) - - // update kick and pool for the change in bond as a result of the take - const kick = Kick.load(auction.kick)! - if (take.isReward) { - // reward kicker if take is below neutral price - pool.totalBondEscrowed = pool.totalBondEscrowed.plus(wadToDecimal(event.params.bondChange)) - kick.locked = kick.locked.plus(wadToDecimal(event.params.bondChange)) - } else { - // penalize kicker if take is above neutral price - pool.totalBondEscrowed = pool.totalBondEscrowed.minus(wadToDecimal(event.params.bondChange)) - kick.locked = kick.locked.minus(wadToDecimal(event.params.bondChange)) - } - - // update take pointers - take.liquidationAuction = auction.id - take.loan = loan.id - take.pool = pool.id + // update pool state + const pool = Pool.load(addressToBytes(event.address))! + updatePool(pool) + pool.txCount = pool.txCount.plus(ONE_BI) + incrementTokenTxCount(pool) + + // update taker account state + const account = loadOrCreateAccount(take.taker) + account.txCount = account.txCount.plus(ONE_BI) + updateAccountTakes(account, take.id) + updateAccountPools(account, pool) + + // update loan state + const loanId = getLoanId(pool.id, addressToBytes(event.params.borrower)) + const loan = Loan.load(loanId)! + loan.debt = loan.debt.minus(wadToDecimal(event.params.amount)) + loan.collateralPledged = loan.collateralPledged.minus(wadToDecimal(event.params.collateral)) + if (loan.debt.notEqual(ZERO_BD) && loan.collateralPledged.notEqual(ZERO_BD)) { + loan.collateralization = collateralizationAtLup(loan.debt, loan.collateralPledged, pool.lup) + loan.tp = thresholdPrice(loan.debt, loan.collateralPledged) + } else { + // set collateralization and tp to zero if loan is fully repaid + loan.collateralization = ZERO_BD + loan.tp = ZERO_BD + } - // save entities to the store - account.save() - auction.save() - loan.save() - pool.save() + // update liquidation auction state + const auctionId = loan.liquidationAuction! + const auction = LiquidationAuction.load(auctionId)! + auction.takes = auction.takes.concat([take.id]) + const auctionInfo = getAuctionInfoERC20Pool(take.borrower, pool) + const auctionStatus = getAuctionStatus(pool, event.params.borrower) + updateLiquidationAuction(auction, auctionInfo, auctionStatus) + + const debtCovered = wadToDecimal(event.params.amount) + auction.debtRemaining = auction.debtRemaining.minus(debtCovered) + const collateralPurchased = wadToDecimal(event.params.collateral) + auction.collateralRemaining = auction.collateralRemaining.minus(collateralPurchased) + pool.pledgedCollateral = pool.pledgedCollateral.minus(collateralPurchased) + take.auctionPrice = wadToDecimal(auctionStatus.price) + + // update kick and pool for the change in bond as a result of the take + const kick = Kick.load(auction.kick)! + if (take.isReward) { + // reward kicker if take is below neutral price + pool.totalBondEscrowed = pool.totalBondEscrowed.plus(wadToDecimal(event.params.bondChange)) + kick.locked = kick.locked.plus(wadToDecimal(event.params.bondChange)) + } else { + // penalize kicker if take is above neutral price + pool.totalBondEscrowed = pool.totalBondEscrowed.minus(wadToDecimal(event.params.bondChange)) + kick.locked = kick.locked.minus(wadToDecimal(event.params.bondChange)) } + // update take pointers + take.liquidationAuction = auction.id + take.loan = loanId + take.pool = pool.id + + // save entities to the store + account.save() + auction.save() + loan.save() + pool.save() take.save() } @@ -1078,55 +1166,50 @@ export function handleSettle(event: SettleEvent): void { settle.blockTimestamp = event.block.timestamp settle.transactionHash = event.transaction.hash - // update entities - const pool = Pool.load(addressToBytes(event.address)) - if (pool != null) { - // update pool state - updatePool(pool) - pool.loansCount = pool.loansCount.minus(ONE_BI) - pool.txCount = pool.txCount.plus(ONE_BI) - - // update tx count for a pools tokens - incrementTokenTxCount(pool) - - // update settler account state - const account = loadOrCreateAccount(event.transaction.from) - account.txCount = account.txCount.plus(ONE_BI) - updateAccountPools(account, pool) - updateAccountSettles(account, settle) - - const loanId = getLoanId(pool.id, settle.borrower) - - // retrieve auction information on the auction after settle - const auctionInfo = getAuctionInfoERC20Pool(settle.borrower, pool) - - // update liquidation auction state - const liquidationAuctionId = getLiquidationAuctionId(pool.id, loanId, settle.blockNumber) - const liquidationAuction = LiquidationAuction.load(liquidationAuctionId)! - updateLiquidationAuction(liquidationAuction, auctionInfo, pool.id) - - // update settle pointers - settle.pool = pool.id - settle.liquidationAuction = liquidationAuctionId - settle.loan = loanId - - // save entities to the store - account.save() - liquidationAuction.save() - pool.save() - } + // update pool state + const pool = Pool.load(addressToBytes(event.address))! + updatePool(pool) + pool.loansCount = pool.loansCount.minus(ONE_BI) + pool.txCount = pool.txCount.plus(ONE_BI) + incrementTokenTxCount(pool) + + // update settler account state + const account = loadOrCreateAccount(event.transaction.from) + account.txCount = account.txCount.plus(ONE_BI) + updateAccountPools(account, pool) + updateAccountSettles(account, settle) + + // update liquidation auction state + const loanId = getLoanId(pool.id, settle.borrower) + const loan = Loan.load(loanId)! + const auctionId = loan.liquidationAuction! + const auction = LiquidationAuction.load(auctionId)! + const auctionInfo = getAuctionInfoERC20Pool(settle.borrower, pool) + // price upon settle is irrelevant, so do not pass auction status + updateLiquidationAuction(auction, auctionInfo, null) + auction.settles = auction.settles.concat([settle.id]) + + // update settle pointers + settle.pool = pool.id + settle.liquidationAuction = auctionId + settle.loan = loanId + + // save entities to the store + account.save() + auction.save() + pool.save() settle.save() } -export function handleTransferLPs(event: TransferLPsEvent): void { - const entity = new TransferLPs( +export function handleTransferLP(event: TransferLPEvent): void { + const entity = new TransferLP( event.transaction.hash.concatI32(event.logIndex.toI32()) ) entity.owner = event.params.owner entity.newOwner = event.params.newOwner entity.indexes = bigIntArrayToIntArray(event.params.indexes) - entity.lps = wadToDecimal(event.params.lps) + entity.lp = wadToDecimal(event.params.lp) entity.blockNumber = event.block.number entity.blockTimestamp = event.block.timestamp @@ -1175,25 +1258,27 @@ export function handleUpdateInterestRate(event: UpdateInterestRateEvent): void { const updateInterestRate = new UpdateInterestRate( event.transaction.hash.concatI32(event.logIndex.toI32()) ) - updateInterestRate.oldRate = wadToDecimal(event.params.oldRate) - updateInterestRate.newRate = wadToDecimal(event.params.newRate) + const poolAddress = addressToBytes(event.address) + const pool = Pool.load(poolAddress)! + const lenderInterestMargin = getLenderInterestMargin(poolAddress) + + updateInterestRate.pool = pool.id + updateInterestRate.oldBorrowRate = pool.borrowRate + updateInterestRate.oldLendRate = pool.lendRate + updateInterestRate.newBorrowRate = wadToDecimal(event.params.newRate) + updateInterestRate.newLendRate = wadToDecimal(wmul(event.params.newRate, lenderInterestMargin)) updateInterestRate.blockNumber = event.block.number updateInterestRate.blockTimestamp = event.block.timestamp updateInterestRate.transactionHash = event.transaction.hash - const pool = Pool.load(addressToBytes(event.address)) - if (pool != null) { - // update pool state - updatePool(pool) - pool.interestRate = wadToDecimal(event.params.newRate) - pool.txCount = pool.txCount.plus(ONE_BI) - - updateInterestRate.pool = pool.id - - // save entities to the store - pool.save() - } + // update pool state + updatePool(pool) + pool.borrowRate = updateInterestRate.newBorrowRate + pool.lendRate = updateInterestRate.newLendRate + pool.txCount = pool.txCount.plus(ONE_BI) + // save entities to the store + pool.save() updateInterestRate.save() } diff --git a/src/grant-fund.ts b/src/grant-fund.ts index a06eca8..e5aa239 100644 --- a/src/grant-fund.ts +++ b/src/grant-fund.ts @@ -1,4 +1,4 @@ -import { BigInt, Bytes, ethereum, log } from '@graphprotocol/graph-ts' +import { Address, BigInt, Bytes, ethereum, log } from '@graphprotocol/graph-ts' import { DelegateRewardClaimed as DelegateRewardClaimedEvent, @@ -27,7 +27,7 @@ import { } from "../generated/schema" import { ZERO_ADDRESS, ZERO_BD } from './utils/constants' -import { addressArrayToBytesArray, addressToBytes, bigIntToBytes, bytesToAddress, bytesToBigInt, wadToDecimal } from "./utils/convert" +import { addressArrayToBytesArray, addressToBytes, bigIntToBytes, bytesToBigInt, wadToDecimal } from "./utils/convert" import { getMechanismOfProposal, getProposalParamsId, getProposalsInSlate, loadOrCreateProposal, removeProposalFromList } from './utils/grants/proposal' import { getCurrentDistributionId, getCurrentStage, loadOrCreateDistributionPeriod } from './utils/grants/distribution' import { getDistributionPeriodVoteId, getExtraordinaryVoteId, getFundingStageVotingPower, getFundingVoteId, getScreeningStageVotingPower, getScreeningVoteId, loadOrCreateDistributionPeriodVote, loadOrCreateVoter } from './utils/grants/voter' @@ -39,14 +39,14 @@ export function handleDelegateRewardClaimed( const delegateRewardClaimed = new DelegateRewardClaimed( event.transaction.hash.concatI32(event.logIndex.toI32()) ) - delegateRewardClaimed.delegateeAddress_ = event.params.delegateeAddress_ - delegateRewardClaimed.rewardClaimed_ = event.params.rewardClaimed_ + delegateRewardClaimed.delegateeAddress_ = event.params.delegateeAddress + delegateRewardClaimed.rewardClaimed_ = event.params.rewardClaimed delegateRewardClaimed.blockNumber = event.block.number delegateRewardClaimed.blockTimestamp = event.block.timestamp delegateRewardClaimed.transactionHash = event.transaction.hash - const rewardsClaimed = wadToDecimal(event.params.rewardClaimed_) + const rewardsClaimed = wadToDecimal(event.params.rewardClaimed) // update DistributionPeriod entity const distributionId = bigIntToBytes(getCurrentDistributionId()) @@ -91,17 +91,17 @@ export function handleFundedSlateUpdated(event: FundedSlateUpdatedEvent): void { const fundedSlateUpdated = new FundedSlateUpdated( event.transaction.hash.concatI32(event.logIndex.toI32()) ) - fundedSlateUpdated.distributionId_ = event.params.distributionId_ - fundedSlateUpdated.fundedSlateHash_ = event.params.fundedSlateHash_ + fundedSlateUpdated.distributionId_ = event.params.distributionId + fundedSlateUpdated.fundedSlateHash_ = event.params.fundedSlateHash fundedSlateUpdated.blockNumber = event.block.number fundedSlateUpdated.blockTimestamp = event.block.timestamp fundedSlateUpdated.transactionHash = event.transaction.hash // update DistributionPeriod entity - const distributionId = bigIntToBytes(event.params.distributionId_) + const distributionId = bigIntToBytes(event.params.distributionId) const distributionPeriod = loadOrCreateDistributionPeriod(distributionId) - distributionPeriod.topSlate = event.params.fundedSlateHash_ + distributionPeriod.topSlate = event.params.fundedSlateHash // create FundedSlate entity const fundedSlate = new FundedSlate(distributionId) as FundedSlate @@ -258,10 +258,10 @@ export function handleQuarterlyDistributionStarted( const distributionStarted = new QuarterlyDistributionStarted( event.transaction.hash.concatI32(event.logIndex.toI32()) ) - const distributionId = bigIntToBytes(event.params.distributionId_) + const distributionId = bigIntToBytes(event.params.distributionId) distributionStarted.distribution = distributionId - distributionStarted.startBlock_ = event.params.startBlock_ - distributionStarted.endBlock_ = event.params.endBlock_ + distributionStarted.startBlock_ = event.params.startBlock + distributionStarted.endBlock_ = event.params.endBlock distributionStarted.blockNumber = event.block.number distributionStarted.blockTimestamp = event.block.timestamp @@ -340,7 +340,7 @@ export function handleVoteCast(event: VoteCastEvent): void { // update voter's distributionPeriodVote entity if it hasn't been recorded yet if (distributionPeriodVote.screeningStageVotingPower === ZERO_BD) { - distributionPeriodVote.screeningStageVotingPower = getScreeningStageVotingPower(bytesToBigInt(distributionId), bytesToAddress(voter.id)) + distributionPeriodVote.screeningStageVotingPower = getScreeningStageVotingPower(bytesToBigInt(distributionId), Address.fromBytes(voter.id)) } // add additional screening votes to voter's distributionPeriodVote entity @@ -361,7 +361,7 @@ export function handleVoteCast(event: VoteCastEvent): void { // update voter's distributionPeriodVote entity if it hasn't been recorded yet if (distributionPeriodVote.screeningStageVotingPower === ZERO_BD) { - distributionPeriodVote.fundingStageVotingPower = getFundingStageVotingPower(bytesToBigInt(distributionId), bytesToAddress(voter.id)) + distributionPeriodVote.fundingStageVotingPower = getFundingStageVotingPower(bytesToBigInt(distributionId), Address.fromBytes(voter.id)) } // save fundingVote to the store diff --git a/src/position-manager.ts b/src/position-manager.ts index ee2f1f0..f6f9779 100644 --- a/src/position-manager.ts +++ b/src/position-manager.ts @@ -158,11 +158,18 @@ export function handleMoveLiquidity(event: MoveLiquidityEvent): void { const bucketIdTo = getBucketId(moveLiquidity.pool, moveLiquidity.toIndex) const lendIdTo = getLendId(bucketIdTo, moveLiquidity.lender) const lendTo = loadOrCreateLend(bucketIdTo, lendIdTo, moveLiquidity.pool, moveLiquidity.lender) - // FIXME: determine how much liquidity was moved - // lendTo.lpb = ??? - // lendTo.lpbValueInQuote = lpbValueInQuote(moveLiquidity.pool, moveLiquidity.toIndex, lendTo.lpb) - lendFrom.lpb = ZERO_BD - lendFrom.lpbValueInQuote = ZERO_BD + + lendTo.lpb = lendTo.lpb.plus(wadToDecimal(event.params.lpAwardedTo)) + lendTo.lpbValueInQuote = lpbValueInQuote(moveLiquidity.pool, moveLiquidity.toIndex, lendTo.lpb) + const lpRedeemedFrom = wadToDecimal(event.params.lpRedeemedFrom) + if (lpRedeemedFrom.le(lendFrom.lpb)) { + lendFrom.lpb = lendFrom.lpb.minus(wadToDecimal(event.params.lpRedeemedFrom)) + } else { + log.warning('handleMoveLiquidity: lender {} redeemed more LP ({}) than Lend entity was aware of ({}); resetting to 0', + [moveLiquidity.lender.toHexString(), lpRedeemedFrom.toString(), lendFrom.lpb.toString()]) + lendFrom.lpb = ZERO_BD + } + lendFrom.lpbValueInQuote = lpbValueInQuote(moveLiquidity.pool, moveLiquidity.toIndex, lendFrom.lpb) lendFrom.save() lendTo.save() diff --git a/src/utils/bucket.ts b/src/utils/bucket.ts index cebc379..67b9b13 100644 --- a/src/utils/bucket.ts +++ b/src/utils/bucket.ts @@ -4,7 +4,7 @@ import { Bucket } from "../../generated/schema" import { PoolInfoUtils } from '../../generated/templates/ERC20Pool/PoolInfoUtils' import { poolInfoUtilsNetworkLookUpTable, ONE_BD, ONE_BI, ZERO_BD, ZERO_BI, ONE_WAD_BD } from "./constants" -import { wadToDecimal } from "./convert" +import { indexToPrice, wadToDecimal } from "./convert" export function getBucketId(pool: Bytes, index: u32): Bytes { return pool.concat(Bytes.fromUTF8('#' + index.toString())) @@ -54,6 +54,7 @@ export function loadOrCreateBucket(poolId: Bytes, bucketId: Bytes, index: u32): bucket = new Bucket(bucketId) as Bucket bucket.bucketIndex = index + bucket.bucketPrice = indexToPrice(index) bucket.poolAddress = poolId.toHexString() bucket.collateral = ZERO_BD bucket.deposit = ZERO_BD diff --git a/src/utils/common.ts b/src/utils/common.ts index f771613..7c8699e 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -3,26 +3,22 @@ import { Address, BigDecimal, BigInt, Bytes, dataSource, log } from "@graphproto import { PoolInfoUtils } from '../../generated/templates/ERC20Pool/PoolInfoUtils' import { ONE_BD, ZERO_BD, poolInfoUtilsNetworkLookUpTable } from "./constants" -import { bigDecimalWadToBigInt, wadToDecimal } from "./convert" +import { decimalToWad, wadToDecimal } from "./convert" export function lpbValueInQuote(pool: Bytes, bucketIndex: u32, lpAmount: BigDecimal): BigDecimal { const poolAddress = Address.fromBytes(pool) const poolInfoUtilsAddress = poolInfoUtilsNetworkLookUpTable.get(dataSource.network())! const poolInfoUtilsContract = PoolInfoUtils.bind(poolInfoUtilsAddress) - const quoteTokenAmount = poolInfoUtilsContract.lpsToQuoteTokens( + const quoteTokenAmount = poolInfoUtilsContract.lpToQuoteTokens( poolAddress, - bigDecimalWadToBigInt(lpAmount), + decimalToWad(lpAmount), BigInt.fromU32(bucketIndex) ) return wadToDecimal(quoteTokenAmount) } -export function encumberance(debt: BigInt, price: BigInt): BigInt { - return debt.div(price) -} - export function collateralization(debt: BigDecimal, encumberedCollateral: BigDecimal): BigDecimal { return debt.div(encumberedCollateral) } diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 578f299..1b3abe3 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -1,38 +1,41 @@ import { BigInt, Address, BigDecimal, TypedMap } from '@graphprotocol/graph-ts' // address of the factory deployed to Goerli -export const ERC20_FACTORY_ADDRESS = Address.fromString('0x9684b8eC942985b23d343cB82D2F30EdA8fD7179') +export const ERC20_FACTORY_ADDRESS = Address.fromString('0x68ced2E7d257da67794B00556B31203A344d3c1e') export const ZERO_ADDRESS = Address.fromString('0x0000000000000000000000000000000000000000') // BigInt constants -export const ZERO_BI = BigInt.zero() -export const ONE_BI = BigInt.fromI32(1) -export const ONE_RAY_BI = BigInt.fromString("1000000000000000000000000000") -export const ONE_WAD_BI = BigInt.fromString("1000000000000000000") +export const ZERO_BI = BigInt.zero() +export const ONE_BI = BigInt.fromI32(1) +export const TEN_BI = BigInt.fromI32(10) +export const ONE_PERCENT_BI = BigInt.fromString("10000000000000000") // 0.01 * 1e18 export const FIVE_PERCENT_BI = BigInt.fromString("50000000000000000") // 0.05 * 1e18 +export const HALF_WAD_BI = BigInt.fromString("500000000000000000") +export const ONE_WAD_BI = BigInt.fromString("1000000000000000000") // BigDecimal constants +export const ZERO_BD = BigDecimal.zero() export const EXP_18_BD = BigDecimal.fromString('1000000000000000000') export const ONE_BD = BigDecimal.fromString('1') export const ONE_WAD_BD = EXP_18_BD -export const FIVE_PERCENT_BD = BigDecimal.fromString("50000000000000000") // 0.05 * 1e18 -export const ZERO_BD = BigDecimal.zero() // max price of the pool is 1_004_968_987.606512354182109771 * 1e18 export const MAX_PRICE = BigDecimal.fromString(`${1_004_968_987.606512354182109771}`) export const MAX_PRICE_INDEX = 0 export const MAX_PRICE_BI = BigInt.fromString('1004968987606512354182109771') +export const MIN_BUCKET_INDEX = -3232; +export const MAX_BUCKET_INDEX = 4156; // poolInfoUtils contract address per network export const poolInfoUtilsNetworkLookUpTable = new TypedMap() -poolInfoUtilsNetworkLookUpTable.set('goerli', Address.fromString('0xEa36b2a4703182d07df9DdEe46BF97f9979F0cCf')) +poolInfoUtilsNetworkLookUpTable.set('goerli', Address.fromString('0x1F9F7732ff409FC0AbcAAea94634A7b41F445299')) export const positionManagerNetworkLookUpTable = new TypedMap() -positionManagerNetworkLookUpTable.set('goerli', Address.fromString('0x31BcbE14Ad30B2f7E1E4A14caB2C16849B73Dac3')) +positionManagerNetworkLookUpTable.set('goerli', Address.fromString('0x83AB3762A4AeC9FBD4e7c01581C9495f2160630b')) export const grantFundNetworkLookUpTable = new TypedMap() -grantFundNetworkLookUpTable.set('goerli', Address.fromString('0xaeB91e664A49829FaBf06BE35d4447938d83A271')) +grantFundNetworkLookUpTable.set('goerli', Address.fromString('0x54110a15011bcb145a8CD4f5adf108B2B6939A1e')) // GrantFund constants export const CHALLENGE_PERIOD_LENGTH = BigInt.fromI32(50400) diff --git a/src/utils/convert.ts b/src/utils/convert.ts index ba4bc30..e0fd8d8 100644 --- a/src/utils/convert.ts +++ b/src/utils/convert.ts @@ -1,19 +1,14 @@ import { BigInt, BigDecimal, Bytes, Address, log } from '@graphprotocol/graph-ts' -import { EXP_18_BD, ONE_BI, ZERO_BD, ZERO_BI } from './constants' - -/****************************/ -/*** To Address Functions ***/ -/****************************/ - -export function bytesToAddress(bytes: Bytes): Address { - return Address.fromHexString(bytes.toHexString()) as Address -} +import { EXP_18_BD, MAX_BUCKET_INDEX, MIN_BUCKET_INDEX, ONE_BI, ZERO_BD, ZERO_BI } from './constants' +import { prices } from './prices' /**************************/ /*** To Bytes Functions ***/ /**************************/ +// use Address.fromBytes() to convert bytes to an Address + export function addressToBytes(address: Address): Bytes { // return address.map((b: Bytes) => b) return Bytes.fromHexString(address.toHexString()) as Bytes @@ -39,8 +34,8 @@ export function bytesToBigInt(bytes: Bytes): BigInt { return BigInt.fromUnsignedBytes(bytes) } -// converts a BigDecimal WAD to a BigInt -export function bigDecimalWadToBigInt(value: BigDecimal): BigInt { +// converts a BigDecimal to a BigInt scaled to WAD precision +export function decimalToWad(value: BigDecimal): BigInt { return BigInt.fromString(value.times(EXP_18_BD).toString()) } @@ -56,16 +51,6 @@ export function exponentToBigDecimal(decimals: BigInt): BigDecimal { return bd } -// TODO: move this to a separate math library -// returns 0 if denominator is 0 in division -export function safeDiv(amount0: BigDecimal, amount1: BigDecimal): BigDecimal { - if (amount1.equals(ZERO_BD)) { - return ZERO_BD - } else { - return amount0.div(amount1) - } -} - // convert an 18 decimal int to a decimal export function wadToDecimal(wad: BigInt): BigDecimal { return wad.toBigDecimal().div(EXP_18_BD) @@ -83,13 +68,10 @@ export function bigIntArrayToIntArray(indexes: BigInt[]): i32[] { return retval } - -// import prices from '../../prices.json' -// export function indexToPrice(index: BigInt): BigDecimal { -// const bucketIndex = MAX_BUCKET_INDEX - index; -// if (bucketIndex < MIN_BUCKET_INDEX || bucketIndex > MAX_BUCKET_INDEX) { -// throw new Error('ERR_BUCKET_INDEX_OUT_OF_BOUNDS'); -// } -// } +export function indexToPrice(index: u32): BigDecimal { + const bucketIndex = MAX_BUCKET_INDEX - index; + assert(bucketIndex >= MIN_BUCKET_INDEX && bucketIndex <= MAX_BUCKET_INDEX, 'Invalid bucket index') + return wadToDecimal(BigInt.fromString(prices[index])); +} // export function priceToIndex(price: BigDecimal): BigInt { diff --git a/src/utils/liquidation.ts b/src/utils/liquidation.ts index 570a24f..b5e4b59 100644 --- a/src/utils/liquidation.ts +++ b/src/utils/liquidation.ts @@ -1,13 +1,14 @@ -import { Address, BigDecimal, BigInt, Bytes } from "@graphprotocol/graph-ts" +import { Address, BigDecimal, BigInt, Bytes, Value, dataSource } from "@graphprotocol/graph-ts" import { LiquidationAuction, Kick, Loan, Pool } from "../../generated/schema" import { ERC20Pool } from '../../generated/templates/ERC20Pool/ERC20Pool' import { wadToDecimal } from "./convert" -import { ONE_BI, ZERO_BD } from "./constants" +import { ONE_BI, ZERO_BD, poolInfoUtilsNetworkLookUpTable } from "./constants" +import { PoolInfoUtils } from "../../generated/templates/ERC20Pool/PoolInfoUtils" -export function getLiquidationAuctionId(poolId: Bytes, loanId: Bytes, blockNumber: BigInt): Bytes { - return poolId.concat(Bytes.fromUTF8('|' + loanId.toString() + '|' + blockNumber.toString())) +export function getLiquidationAuctionId(poolId: Bytes, loanId: Bytes, kickBlock: BigInt): Bytes { + return poolId.concat(Bytes.fromUTF8('|' + loanId.toString() + '|' + kickBlock.toString())) } export function getBucketTakeLPAwardedId(transactionHash: Bytes, logIndex: BigInt): Bytes { @@ -40,11 +41,22 @@ export function loadOrCreateLiquidationAuction(poolId: Bytes, liquidationAuction // collections liquidationAuction.takes = [] liquidationAuction.bucketTakes = [] + liquidationAuction.settles = [] } return liquidationAuction } -export function updateLiquidationAuction(liquidationAuction: LiquidationAuction, auctionInfo: AuctionInfo, poolId: Bytes): void { +export function updateLiquidationAuction( + liquidationAuction: LiquidationAuction, + auctionInfo: AuctionInfo, + auctionStatus: AuctionStatus | null): void { + // intentionally not passed upon settle; we care about state as of last take + if (auctionStatus) { + liquidationAuction.auctionPrice = wadToDecimal(auctionStatus.price) + liquidationAuction.collateralRemaining = wadToDecimal(auctionStatus.collateral) + liquidationAuction.debtRemaining = wadToDecimal(auctionStatus.debtToCover) + } + liquidationAuction.kickTime = auctionInfo.kickTime liquidationAuction.bondSize = wadToDecimal(auctionInfo.bondSize) liquidationAuction.bondFactor = wadToDecimal(auctionInfo.bondFactor) @@ -92,3 +104,33 @@ export function getAuctionInfoERC20Pool(borrower: Bytes, pool: Pool): AuctionInf ) return auctionInfo } + +export class AuctionStatus { + kickTime: BigInt + collateral: BigInt + debtToCover: BigInt + isCollateralized: bool + price: BigInt + neutralPrice: BigInt + constructor(kickTime: BigInt, collateral: BigInt, debtToCover: BigInt, isCollateralized: bool, price: BigInt, neutralPrice: BigInt) { + this.kickTime = kickTime + this.collateral = collateral + this.debtToCover = debtToCover + this.isCollateralized = isCollateralized + this.price = price + this.neutralPrice = neutralPrice + } +} +export function getAuctionStatus(pool: Pool, borrower: Address): AuctionStatus { + const poolInfoUtilsAddress = poolInfoUtilsNetworkLookUpTable.get(dataSource.network())! + const poolInfoUtilsContract = PoolInfoUtils.bind(poolInfoUtilsAddress) + const result = poolInfoUtilsContract.auctionStatus(Address.fromBytes(pool.id), borrower) + return new AuctionStatus( + result.value0, + result.value1, + result.value2, + result.value3, + result.value4, + result.value5 + ) +} \ No newline at end of file diff --git a/src/utils/loan.ts b/src/utils/loan.ts index 8b204c0..8822243 100644 --- a/src/utils/loan.ts +++ b/src/utils/loan.ts @@ -22,6 +22,7 @@ export function loadOrCreateLoan(loanId: Bytes, poolId: Bytes, borrower: Bytes): loan.debt = ZERO_BD loan.tp = ZERO_BD loan.inLiquidation = false + loan.liquidationAuction = null } return loan diff --git a/src/utils/lp-allowances.ts b/src/utils/lp-allowances.ts index b9da0bb..46cce71 100644 --- a/src/utils/lp-allowances.ts +++ b/src/utils/lp-allowances.ts @@ -23,27 +23,59 @@ export function loadOrCreateAllowances(poolId: Bytes, lenderId: Bytes, spenderId return entity; } -export function setAllowances(entity: LPAllowances, indexes: Array, amounts: Array): void { - let id = entity.id; +export function increaseAllowances(entity: LPAllowances, indexes: Array, amounts: Array): void { + const id = entity.id; + const entityAllowances = entity.allowances; for (var i=0; i, amounts: Array): void { + const id = entity.id; + const entityAllowances = entity.allowances; + for (var i=0; i): void { - let id = entity.id; + const id = entity.id; + const entityAllowances = entity.allowances; for (var i=0; i=0.5.0) // https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0 @@ -54,13 +54,13 @@ describe("ERC20PoolFactory assertions", () => { assert.fieldEquals( "ERC20PoolFactory", - "0x9684b8ec942985b23d343cb82d2f30eda8fd7179", + ERC20_FACTORY_ADDRESS.toHexString(), "poolCount", `${ONE_BI}` ) assert.fieldEquals( "ERC20PoolFactory", - "0x9684b8ec942985b23d343cb82d2f30eda8fd7179", + ERC20_FACTORY_ADDRESS.toHexString(), "txCount", `${ONE_BI}` ) @@ -90,7 +90,7 @@ describe("ERC20PoolFactory assertions", () => { assert.fieldEquals( "Pool", "0x0000000000000000000000000000000000000001", - "currentDebt", + "debt", `${ZERO_BI}` ) assert.fieldEquals( diff --git a/tests/erc-20-pool.test.ts b/tests/erc-20-pool.test.ts index ad04f38..db68cfe 100644 --- a/tests/erc-20-pool.test.ts +++ b/tests/erc-20-pool.test.ts @@ -5,35 +5,38 @@ import { clearStore, beforeEach, afterEach, - logStore, beforeAll, - dataSourceMock + dataSourceMock, } from "matchstick-as/assembly/index" import { Address, BigDecimal, BigInt, Bytes } from "@graphprotocol/graph-ts" -import { handleAddCollateral, handleAddQuoteToken, handleBucketBankruptcy, handleBucketTake, handleBucketTakeLPAwarded, handleDrawDebt, handleKick, handleMoveQuoteToken, handleRepayDebt, handleReserveAuction, handleTake, handleUpdateInterestRate } from "../src/erc-20-pool" -import { createAddCollateralEvent, createAddQuoteTokenEvent, createBucketBankruptcyEvent, createBucketTakeEvent, createBucketTakeLPAwardedEvent, createDrawDebtEvent, createKickEvent, createMoveQuoteTokenEvent, createRepayDebtEvent, createReserveAuctionEvent, createTakeEvent, createUpdateInterestRateEvent } from "./utils/erc-20-pool-utils" +import { handleAddCollateral, handleAddQuoteToken, handleBucketBankruptcy, handleBucketTake, handleBucketTakeLPAwarded, handleDrawDebt, handleKick, handleMoveQuoteToken, handleRepayDebt, handleReserveAuctionKick, handleReserveAuctionTake, handleTake, handleUpdateInterestRate } from "../src/erc-20-pool" +import { createAddCollateralEvent, createAddQuoteTokenEvent, createBucketBankruptcyEvent, createBucketTakeEvent, createBucketTakeLPAwardedEvent, createDrawDebtEvent, createKickEvent, createMoveQuoteTokenEvent, createRepayDebtEvent, createReserveAuctionKickEvent, createReserveAuctionTakeEvent, createTakeEvent, createUpdateInterestRateEvent } from "./utils/erc-20-pool-utils" import { assertBucketUpdate, assertLendUpdate, assertPoolUpdate, createPool, mockGetAuctionInfoERC20Pool, + mockGetAuctionStatus, mockGetBucketInfo, mockGetBurnInfo, mockGetCurrentBurnEpoch, mockGetDebtInfo, mockGetLPBValueInQuote, - mockPoolInfoUtilsPoolUpdateCalls + mockGetLenderInterestMargin, + mockPoolInfoUtilsPoolUpdateCalls, + mockTokenBalance } from "./utils/common" import { BucketInfo, getBucketId } from "../src/utils/bucket" import { addressToBytes, wadToDecimal } from "../src/utils/convert" -import { EXP_18_BD, FIVE_PERCENT_BD, FIVE_PERCENT_BI, MAX_PRICE, MAX_PRICE_BI, MAX_PRICE_INDEX, ONE_BI, ONE_WAD_BI, ZERO_ADDRESS, ZERO_BD, ZERO_BI } from "../src/utils/constants" -import { Account, Lend, Loan, ReserveAuctionKickOrTake } from "../generated/schema" +import { FIVE_PERCENT_BI, MAX_PRICE, MAX_PRICE_BI, MAX_PRICE_INDEX, ONE_BI, ONE_PERCENT_BI, ONE_WAD_BI, ZERO_ADDRESS, ZERO_BD, ZERO_BI } from "../src/utils/constants" +import { Account, Lend, Loan, Pool } from "../generated/schema" import { getLendId } from "../src/utils/lend" import { getLoanId } from "../src/utils/loan" -import { AuctionInfo, getLiquidationAuctionId } from "../src/utils/liquidation" +import { AuctionInfo, AuctionStatus, getLiquidationAuctionId } from "../src/utils/liquidation" import { BurnInfo, DebtInfo } from "../src/utils/pool" import { getReserveAuctionId } from "../src/utils/reserve-auction" +import { wmul } from "../src/utils/math" // Tests structure (matchstick-as >=0.5.0) // https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0 @@ -47,13 +50,12 @@ describe("ERC20Pool assertions", () => { beforeEach(() => { // deploy pool contract - const pool_ = Address.fromString("0x0000000000000000000000000000000000000001") + const pool = Address.fromString("0x0000000000000000000000000000000000000001") const collateralToken = Address.fromString("0x0000000000000000000000000000000000000010") const quoteToken = Address.fromString("0x0000000000000000000000000000000000000012") const expectedInitialInterestRate = FIVE_PERCENT_BI const expectedInitialFeeRate = ZERO_BI - - createPool(pool_, collateralToken, quoteToken, expectedInitialInterestRate, expectedInitialFeeRate) + createPool(pool, collateralToken, quoteToken, expectedInitialInterestRate, expectedInitialFeeRate) }) afterEach(() => { @@ -91,8 +93,7 @@ describe("ERC20Pool assertions", () => { debt: ZERO_BI, loansCount: ZERO_BI, maxBorrower: ZERO_ADDRESS, - pendingInflator: ONE_WAD_BI, - pendingInterestFactor: ZERO_BI, + inflator: ONE_WAD_BI, hpb: ZERO_BI, //TODO: indexToPrice(price) hpbIndex: index, htp: ZERO_BI, //TODO: indexToPrice(price) @@ -112,6 +113,9 @@ describe("ERC20Pool assertions", () => { targetUtilization: ONE_WAD_BI }) + mockTokenBalance(Address.fromString("0x0000000000000000000000000000000000000012"), poolAddress, ZERO_BI) + mockTokenBalance(Address.fromString("0x0000000000000000000000000000000000000010"), poolAddress, ZERO_BI) + // mock addCollateralEvent const newAddCollateralEvent = createAddCollateralEvent( poolAddress, @@ -196,8 +200,7 @@ describe("ERC20Pool assertions", () => { debt: ZERO_BI, loansCount: ZERO_BI, maxBorrower: ZERO_ADDRESS, - pendingInflator: ONE_WAD_BI, - pendingInterestFactor: ZERO_BI, + inflator: ONE_WAD_BI, hpb: ZERO_BI, //TODO: indexToPrice(price) hpbIndex: index, htp: ZERO_BI, //TODO: indexToPrice(price) @@ -280,9 +283,7 @@ describe("ERC20Pool assertions", () => { loansCount: ZERO_BI, maxBorrower: ZERO_ADDRESS.toHexString(), inflator: ONE_WAD_BI, - pendingInflator: ONE_WAD_BI, - pendingInterestFactor: ZERO_BI, - currentDebt: ZERO_BI, + debt: ZERO_BI, pledgedCollateral: ZERO_BI, hpb: ZERO_BI, hpbIndex: index, @@ -475,7 +476,7 @@ describe("ERC20Pool assertions", () => { const collateralPledged = BigInt.fromI32(1067) const lup = BigInt.fromString("9529276179422528643") // 9.529276179422528643 * 1e18 - const expectedPoolDebtInfo = new DebtInfo(amountBorrowed, ZERO_BI, ZERO_BI) + const expectedPoolDebtInfo = new DebtInfo(amountBorrowed, ZERO_BI, ZERO_BI, ZERO_BI) mockGetDebtInfo(poolAddress, expectedPoolDebtInfo) // mock drawDebt event @@ -522,7 +523,7 @@ describe("ERC20Pool assertions", () => { assert.fieldEquals( "Pool", `${addressToBytes(poolAddress).toHexString()}`, - "currentDebt", + "debt", `${wadToDecimal(amountBorrowed)}` ) assert.fieldEquals( @@ -631,6 +632,7 @@ describe("ERC20Pool assertions", () => { const next = Address.fromString("0x0000000000000000000000000000000000000000") const prev = Address.fromString("0x0000000000000000000000000000000000000000") const alreadyTaken = false + const startPrice = neutralPrice.times(BigInt.fromU32(32)) const expectedAuctionInfo = new AuctionInfo( kicker, bondFactor, @@ -644,6 +646,15 @@ describe("ERC20Pool assertions", () => { alreadyTaken ) mockGetAuctionInfoERC20Pool(borrower, poolAddress, expectedAuctionInfo) + const expectedAuctionStatus = new AuctionStatus( + kickTime, + collateral, + debt, + false, + startPrice, + neutralPrice + ) + mockGetAuctionStatus(poolAddress, borrower, expectedAuctionStatus) // mock kick event const newKickEvent = createKickEvent( @@ -754,9 +765,8 @@ describe("ERC20Pool assertions", () => { // loansCount: ZERO_BI, // should be one while kicked but not settled // maxBorrower: borrower.toHexString(), // inflator: ONE_WAD_BI, - // pendingInflator: ONE_WAD_BI, - // pendingInterestFactor: ZERO_BI, - // currentDebt: ZERO_BI, + // inflator: ONE_WAD_BI, + // debt: ZERO_BI, // pledgedCollateral: ZERO_BI, // hpb: ZERO_BI, // hpbIndex: ZERO_BI, @@ -849,6 +859,15 @@ describe("ERC20Pool assertions", () => { alreadyTaken ) mockGetAuctionInfoERC20Pool(borrower, poolAddress, expectedAuctionInfo) + const expectedAuctionStatus = new AuctionStatus( + kickTime, + collateral, + debt, + false, + wmul(neutralPrice, BigInt.fromString("970000000000000000")), // take price = neutral price * 0.97 + neutralPrice + ) + mockGetAuctionStatus(poolAddress, borrower, expectedAuctionStatus) // mock take event const newTakeEvent = createTakeEvent( @@ -1018,6 +1037,15 @@ describe("ERC20Pool assertions", () => { alreadyTaken ) mockGetAuctionInfoERC20Pool(borrower, poolAddress, expectedAuctionInfo) + const expectedAuctionStatus = new AuctionStatus( + kickTime, + collateral, + debt, + false, + wmul(neutralPrice, BigInt.fromString("1020000000000000000")), // take price = neutral price * 1.02 + neutralPrice + ) + mockGetAuctionStatus(poolAddress, borrower, expectedAuctionStatus) // mock bucket take event const newBucketTakeEvent = createBucketTakeEvent( @@ -1190,12 +1218,13 @@ describe("ERC20Pool assertions", () => { const poolAddress = Address.fromString("0x0000000000000000000000000000000000000001") const kicker = Address.fromString("0x0000000000000000000000000000000000000008") const taker = Address.fromString("0x0000000000000000000000000000000000000018") - let claimableReservesRemaining = BigInt.fromString("100000000000000000000") // Wad(100) + const claimableReserves = BigInt.fromString("100000000000000000000") // Wad(100) const auctionPrice = BigInt.fromI32(456) const seventyTwoHours = BigInt.fromString("259200") let timestamp = BigInt.fromI32(123) let totalInterest = ZERO_BI - const totalBurnedAtKick = ONE_WAD_BI + const totalBurnedAtKick = BigInt.fromString("12000000000000000000") // Wad(12) + const burnEpoch = BigInt.fromI32(9998101) /****************************/ /*** Kick Reserve Auction ***/ @@ -1207,8 +1236,7 @@ describe("ERC20Pool assertions", () => { debt: ZERO_BI, loansCount: ZERO_BI, maxBorrower: ZERO_ADDRESS, - pendingInflator: ONE_WAD_BI, - pendingInterestFactor: ZERO_BI, + inflator: ONE_WAD_BI, hpb: ZERO_BI, //TODO: indexToPrice(price) hpbIndex: ZERO_BI, htp: ZERO_BI, //TODO: indexToPrice(price) @@ -1217,10 +1245,10 @@ describe("ERC20Pool assertions", () => { lupIndex: BigInt.fromU32(MAX_PRICE_INDEX), momp: BigInt.fromI32(623801), reserves: ZERO_BI, - claimableReserves: claimableReservesRemaining, - claimableReservesRemaining: claimableReservesRemaining, + claimableReserves: claimableReserves, + claimableReservesRemaining: claimableReserves, reserveAuctionPrice: auctionPrice, - currentBurnEpoch: BigInt.fromI32(9998101), + currentBurnEpoch: burnEpoch, reserveAuctionTimeRemaining: seventyTwoHours, minDebtAmount: ZERO_BI, collateralization: ONE_WAD_BI, @@ -1229,71 +1257,96 @@ describe("ERC20Pool assertions", () => { }) // mock burnInfo contract calls - const expectedBurnEpoch = ONE_BI - mockGetCurrentBurnEpoch(poolAddress, expectedBurnEpoch) + mockGetCurrentBurnEpoch(poolAddress, burnEpoch) - let expectedBurnInfo = new BurnInfo( - timestamp, - totalInterest, - totalBurnedAtKick - ) - mockGetBurnInfo(poolAddress, ONE_BI, expectedBurnInfo) + // Pretend the pool already had some AJNA burned. Kick event should not need to check burn info. + const pool = Pool.load(Address.fromBytes(poolAddress))! + pool.totalAjnaBurned = wadToDecimal(totalBurnedAtKick) + pool.save() - let newReserveAuctionEvent = createReserveAuctionEvent( + const kickEvent = createReserveAuctionKickEvent( kicker, poolAddress, - claimableReservesRemaining, + claimableReserves, auctionPrice, - expectedBurnEpoch, + burnEpoch, ) - handleReserveAuction(newReserveAuctionEvent) + handleReserveAuctionKick(kickEvent) /*********************************/ /*** Assert Reserve Kick State ***/ /*********************************/ - assert.entityCount("ReserveAuctionKickOrTake", 1) + assert.entityCount("ReserveAuctionKick", 1) + const kickReserveAuctionID = Bytes.fromHexString("0xa16081f360e3847006db660bae1c6d1b2e17ec2a").concat(addressToBytes(kicker)) + const reserveAuctionId = getReserveAuctionId(addressToBytes(poolAddress), burnEpoch) - const reserveAuctionId = getReserveAuctionId(addressToBytes(poolAddress), expectedBurnEpoch) - assert.entityCount("ReserveAuction", 1) + // assert kick reserve auction entity assert.fieldEquals( - "ReserveAuction", - `${reserveAuctionId.toHexString()}`, + "ReserveAuctionKick", + `${kickReserveAuctionID.toHexString()}`, + "pool", + `${poolAddress.toHexString()}` + ) + assert.fieldEquals( + "ReserveAuctionKick", + `${kickReserveAuctionID.toHexString()}`, + "reserveAuction", + `${reserveAuctionId.toHexString()}` + ) + assert.fieldEquals( + "ReserveAuctionKick", + `${kickReserveAuctionID.toHexString()}`, + "claimableReserves", + `${wadToDecimal(claimableReserves)}` + ) + assert.fieldEquals( + "ReserveAuctionKick", + `${kickReserveAuctionID.toHexString()}`, "kicker", `${kicker.toHexString()}` ) assert.fieldEquals( - "ReserveAuction", - `${reserveAuctionId.toHexString()}`, + "ReserveAuctionKick", + `${kickReserveAuctionID.toHexString()}`, "kickerAward", - `${wadToDecimal(claimableReservesRemaining.times(BigInt.fromString("10000000000000000"))).div(EXP_18_BD)}` + `${wadToDecimal(wmul(claimableReserves, ONE_PERCENT_BI))}` ) + + assert.entityCount("ReserveAuction", 1) assert.fieldEquals( "ReserveAuction", `${reserveAuctionId.toHexString()}`, "pool", `${poolAddress.toHexString()}` ) + assert.fieldEquals( + "ReserveAuction", + `${reserveAuctionId.toHexString()}`, + "claimableReservesRemaining", + `${wadToDecimal(claimableReserves)}` + ) assert.fieldEquals( "ReserveAuction", `${reserveAuctionId.toHexString()}`, "burnEpoch", - `${expectedBurnEpoch}` + `${burnEpoch}` ) assert.fieldEquals( "ReserveAuction", `${reserveAuctionId.toHexString()}`, - "ajnaBurnedAcrossAllTakes", - `${0}` + "ajnaBurned", + `${ZERO_BD}` ) /****************************/ /*** Take Reserve Auction ***/ /****************************/ - const totalBurnedAtTake = BigInt.fromString("100000000000000000000") // Wad(100) - const claimableReservesRemainingAfterTake = BigInt.fromString("500000000000000000000") // Wad(500) - const seventyHours = BigInt.fromString("252000") + const totalBurnedAtTake = BigInt.fromString("14000000000000000000") // Wad(14) (2 AJNA burned) + timestamp = timestamp.plus(BigInt.fromU32(3600 * 2)) // two hours elapsed + const seventyHours = BigInt.fromString("252000") // seventy hours remain + const claimableReservesAfterTake = BigInt.fromString("500000000000000000000") // Wad(500) // mock pool info contract calls mockPoolInfoUtilsPoolUpdateCalls(poolAddress, { @@ -1301,8 +1354,7 @@ describe("ERC20Pool assertions", () => { debt: ZERO_BI, loansCount: ZERO_BI, maxBorrower: ZERO_ADDRESS, - pendingInflator: ONE_WAD_BI, - pendingInterestFactor: ZERO_BI, + inflator: ONE_WAD_BI, hpb: ZERO_BI, //TODO: indexToPrice(price) hpbIndex: ZERO_BI, htp: ZERO_BI, //TODO: indexToPrice(price) @@ -1311,10 +1363,10 @@ describe("ERC20Pool assertions", () => { lupIndex: BigInt.fromU32(MAX_PRICE_INDEX), //TODO: indexToPrice(lup) momp: BigInt.fromI32(623802), reserves: ZERO_BI, - claimableReserves: claimableReservesRemainingAfterTake, - claimableReservesRemaining: claimableReservesRemainingAfterTake, + claimableReserves: claimableReservesAfterTake, + claimableReservesRemaining: claimableReservesAfterTake, reserveAuctionPrice: auctionPrice, - currentBurnEpoch: BigInt.fromI32(9998104), + currentBurnEpoch: burnEpoch, reserveAuctionTimeRemaining: seventyHours, minDebtAmount: ZERO_BI, collateralization: ONE_WAD_BI, @@ -1322,97 +1374,55 @@ describe("ERC20Pool assertions", () => { targetUtilization: ONE_WAD_BI }) - timestamp = BigInt.fromI32(456) totalInterest = BigInt.fromString("100000000000000000000") // Wad(100) - expectedBurnInfo = new BurnInfo( + const expectedBurnInfo = new BurnInfo( timestamp, totalInterest, totalBurnedAtTake ) - mockGetBurnInfo(poolAddress, ONE_BI, expectedBurnInfo) + mockGetBurnInfo(poolAddress, burnEpoch, expectedBurnInfo) - newReserveAuctionEvent = createReserveAuctionEvent( + const takeEvent = createReserveAuctionTakeEvent( taker, poolAddress, - claimableReservesRemainingAfterTake, + claimableReservesAfterTake, auctionPrice, - timestamp, + burnEpoch, ) - handleReserveAuction(newReserveAuctionEvent) - - /*********************************/ - /*** Assert Reserve Kick State ***/ - /*********************************/ + handleReserveAuctionTake(takeEvent) - const kickReserveAuctionID = Bytes.fromHexString("0xa16081f360e3847006db660bae1c6d1b2e17ec2a").concat(addressToBytes(kicker)) const takeReserveAuctionID = Bytes.fromHexString("0xa16081f360e3847006db660bae1c6d1b2e17ec2a").concat(addressToBytes(taker)) - const loadedKickReserveAuction = ReserveAuctionKickOrTake.load(kickReserveAuctionID)! - const loadedTakeReserveAuction = ReserveAuctionKickOrTake.load(takeReserveAuctionID)! const incrementalAjnaBurned = wadToDecimal(totalBurnedAtTake.minus(totalBurnedAtKick)) - assert.entityCount("ReserveAuctionKickOrTake", 2) - // assert first kick reserve auction entity - assert.fieldEquals( - "ReserveAuctionKickOrTake", - `${kickReserveAuctionID.toHexString()}`, - "pool", - `${poolAddress.toHexString()}` - ) - assert.fieldEquals( - "ReserveAuctionKickOrTake", - `${kickReserveAuctionID.toHexString()}`, - "reserveAuction", - `${reserveAuctionId.toHexString()}` - ) - assert.fieldEquals( - "ReserveAuctionKickOrTake", - `${kickReserveAuctionID.toHexString()}`, - "incrementalAjnaBurned", - `${0}` - ) - // assert.assertNull(loadedKickReserveAuction.taker) // FIXME: this is failing with a type issue - - // assert second take reserve auction entity + // assert take reserve auction entity + assert.entityCount("ReserveAuctionTake", 1) assert.fieldEquals( - "ReserveAuctionKickOrTake", + "ReserveAuctionTake", `${takeReserveAuctionID.toHexString()}`, "pool", `${poolAddress.toHexString()}` ) assert.fieldEquals( - "ReserveAuctionKickOrTake", + "ReserveAuctionTake", `${takeReserveAuctionID.toHexString()}`, "taker", `${taker.toHexString()}` ) assert.fieldEquals( - "ReserveAuctionKickOrTake", + "ReserveAuctionTake", `${takeReserveAuctionID.toHexString()}`, "reserveAuction", `${reserveAuctionId.toHexString()}` ) assert.fieldEquals( - "ReserveAuctionKickOrTake", + "ReserveAuctionTake", `${takeReserveAuctionID.toHexString()}`, - "incrementalAjnaBurned", + "ajnaBurned", `${incrementalAjnaBurned}` ) - // assert.assertNotNull(loadedTakeReserveAuction.taker) // FIXME: this is failing with a type issue - // assert reserve auction process entity + // assert reserve auction entity assert.entityCount("ReserveAuction", 1) - assert.fieldEquals( - "ReserveAuction", - `${reserveAuctionId.toHexString()}`, - "kicker", - `${kicker.toHexString()}` - ) - assert.fieldEquals( - "ReserveAuction", - `${reserveAuctionId.toHexString()}`, - "kickerAward", - `${wadToDecimal(claimableReservesRemaining.times(BigInt.fromString("10000000000000000"))).div(EXP_18_BD)}` - ) assert.fieldEquals( "ReserveAuction", `${reserveAuctionId.toHexString()}`, @@ -1423,18 +1433,18 @@ describe("ERC20Pool assertions", () => { "ReserveAuction", `${reserveAuctionId.toHexString()}`, "claimableReservesRemaining", - `${wadToDecimal(claimableReservesRemainingAfterTake)}` + `${wadToDecimal(claimableReservesAfterTake)}` ) assert.fieldEquals( "ReserveAuction", `${reserveAuctionId.toHexString()}`, "burnEpoch", - `${expectedBurnEpoch}` + `${burnEpoch}` ) assert.fieldEquals( "ReserveAuction", `${reserveAuctionId.toHexString()}`, - "ajnaBurnedAcrossAllTakes", + "ajnaBurned", `${incrementalAjnaBurned}` ) @@ -1460,9 +1470,7 @@ describe("ERC20Pool assertions", () => { loansCount: ZERO_BI, maxBorrower: ZERO_ADDRESS.toHexString(), inflator: ONE_WAD_BI, - pendingInflator: ONE_WAD_BI, - pendingInterestFactor: ZERO_BI, - currentDebt: ZERO_BI, + debt: ZERO_BI, pledgedCollateral: ZERO_BI, hpb: ZERO_BI, hpbIndex: ZERO_BI, @@ -1471,8 +1479,8 @@ describe("ERC20Pool assertions", () => { lup: MAX_PRICE_BI, lupIndex: BigInt.fromU32(MAX_PRICE_INDEX), reserves: ZERO_BI, - claimableReserves: claimableReservesRemainingAfterTake, - claimableReservesRemaining: claimableReservesRemainingAfterTake, + claimableReserves: claimableReservesAfterTake, + claimableReservesRemaining: claimableReservesAfterTake, reserveAuctionPrice: auctionPrice, reserveAuctionTimeRemaining: seventyHours, minDebtAmount: ZERO_BI, @@ -1483,7 +1491,6 @@ describe("ERC20Pool assertions", () => { // liquidationAuctions: Bytes.fromHexString("0x"), txCount: BigInt.fromI32(2) }) - }) test("BucketBankruptcy", () => { @@ -1568,10 +1575,13 @@ describe("ERC20Pool assertions", () => { assert.fieldEquals( "Pool", `${poolAddress.toHexString()}`, - "interestRate", + "borrowRate", `${wadToDecimal(oldInterestRate)}` ) + // mock required contract calls + mockGetLenderInterestMargin(poolAddress, BigInt.fromString("920000000000000000")) + // mock update interest rate event const newUpdateInterestRateEvent = createUpdateInterestRateEvent( poolAddress, @@ -1587,7 +1597,7 @@ describe("ERC20Pool assertions", () => { assert.fieldEquals( "Pool", `${poolAddress.toHexString()}`, - "interestRate", + "borrowRate", `${wadToDecimal(newInterestRate)}` ) }) diff --git a/tests/position-manager.test.ts b/tests/position-manager.test.ts index f30f49a..9d6c2b1 100644 --- a/tests/position-manager.test.ts +++ b/tests/position-manager.test.ts @@ -11,8 +11,12 @@ import { import { Address, BigInt } from "@graphprotocol/graph-ts" import { handleApproval, handleBurn, handleMemorializePosition, handleMint, handleMoveLiquidity, handleRedeemPosition } from "../src/position-manager" import { assertPosition, createApprovalEvent, createBurnEvent, createMemorializePositionEvent, createMintEvent, createMoveLiquidityEvent, createRedeemPositionEvent, mintPosition } from "./utils/position-manager-utils" -import { bigIntToBytes } from "../src/utils/convert" -import { mockGetPoolKey, mockGetTokenName, mockGetTokenSymbol } from "./utils/common" +import { bigIntToBytes, wadToDecimal } from "../src/utils/convert" +import { mockGetLPBValueInQuote, mockGetPoolKey, mockGetTokenName, mockGetTokenSymbol } from "./utils/common" +import { Lend } from "../generated/schema" +import { getLendId, loadOrCreateLend } from "../src/utils/lend" +import { getBucketId } from "../src/utils/bucket" +import { ZERO_BI } from "../src/utils/constants" // Tests structure (matchstick-as >=0.5.0) // https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0 @@ -236,6 +240,9 @@ describe("Describe entity assertions", () => { const indexes:BigInt[] = [] const fromIndex = BigInt.fromI32(5000) const toIndex = BigInt.fromI32(4000) + const lpRedeemedFrom = BigInt.fromString("63380000000000000000") // 63.38 + const lpRedeemedto = BigInt.fromString("62740000000000000000") // 62.74 + const lpValueInQuote = BigInt.fromString("64380000000000000000") /*********************/ /*** Mint Position ***/ @@ -251,6 +258,16 @@ describe("Describe entity assertions", () => { /*** Memorialize Position ***/ /****************************/ + const bucketId = getBucketId(pool, fromIndex.toU32()) + const lend = new Lend(getLendId(bucketId, lender)) + lend.bucket = bucketId + lend.lender = lender + lend.pool = pool + lend.poolAddress = pool.toHexString() + lend.lpb = wadToDecimal(lpRedeemedFrom) + lend.lpbValueInQuote = wadToDecimal(lpValueInQuote) + lend.save(); + mockGetPoolKey(tokenId, pool) // memorialize existing position const newMemorializeEvent = createMemorializePositionEvent(lender, tokenId, indexes) @@ -261,6 +278,7 @@ describe("Describe entity assertions", () => { // TODO: check index attributes assert.entityCount("Mint", 1) + assert.entityCount("Lend", 1) assert.entityCount("MemorializePosition", 1) assert.entityCount("Position", 1) assert.entityCount("MoveLiquidity", 0) @@ -268,8 +286,11 @@ describe("Describe entity assertions", () => { /**********************/ /*** Move Liquidity ***/ /**********************/ - - const newMoveLiquidityEvent = createMoveLiquidityEvent(lender, tokenId, fromIndex, toIndex) + + mockGetLPBValueInQuote(pool, lpRedeemedFrom, fromIndex, lpValueInQuote) + mockGetLPBValueInQuote(pool, ZERO_BI, toIndex, ZERO_BI) + mockGetLPBValueInQuote(pool, lpRedeemedto, toIndex, lpValueInQuote) + const newMoveLiquidityEvent = createMoveLiquidityEvent(lender, tokenId, fromIndex, toIndex, lpRedeemedFrom, lpRedeemedto) handleMoveLiquidity(newMoveLiquidityEvent) // check position attributes diff --git a/tests/utils/common.ts b/tests/utils/common.ts index 5bca53a..0720290 100644 --- a/tests/utils/common.ts +++ b/tests/utils/common.ts @@ -6,9 +6,9 @@ import { createPoolCreatedEvent } from "./erc-20-pool-factory-utils" import { BucketInfo, getBucketId } from "../../src/utils/bucket" import { addressToBytes, wadToDecimal } from "../../src/utils/convert" -import { grantFundNetworkLookUpTable, positionManagerNetworkLookUpTable, poolInfoUtilsNetworkLookUpTable, ZERO_BI } from "../../src/utils/constants" +import { grantFundNetworkLookUpTable, positionManagerNetworkLookUpTable, poolInfoUtilsNetworkLookUpTable, ZERO_BI, ONE_BI } from "../../src/utils/constants" import { BurnInfo, DebtInfo, LoansInfo, PoolPricesInfo, PoolUtilizationInfo, ReservesInfo } from "../../src/utils/pool" -import { AuctionInfo } from "../../src/utils/liquidation" +import { AuctionInfo, AuctionStatus } from "../../src/utils/liquidation" /*************************/ /*** Bucket Assertions ***/ @@ -86,9 +86,7 @@ export class PoolUpdatedParams { loansCount: BigInt maxBorrower: String inflator: BigInt - pendingInflator: BigInt - pendingInterestFactor: BigInt - currentDebt: BigInt + debt: BigInt pledgedCollateral: BigInt // prices info hpb: BigInt @@ -143,20 +141,8 @@ export function assertPoolUpdate(params: PoolUpdatedParams): void { assert.fieldEquals( "Pool", `${params.poolAddress}`, - "pendingInflator", - `${wadToDecimal(params.pendingInflator)}` - ) - assert.fieldEquals( - "Pool", - `${params.poolAddress}`, - "pendingInterestFactor", - `${wadToDecimal(params.pendingInterestFactor)}` - ) - assert.fieldEquals( - "Pool", - `${params.poolAddress}`, - "currentDebt", - `${wadToDecimal(params.currentDebt)}` + "debt", + `${wadToDecimal(params.debt)}` ) assert.fieldEquals( "Pool", @@ -391,17 +377,24 @@ export function mockGetBucketInfo(pool: Address, bucketIndex: BigInt, expectedIn } export function mockGetDebtInfo(pool: Address, expectInfo: DebtInfo): void { - createMockedFunction(pool, 'debtInfo', 'debtInfo():(uint256,uint256,uint256)') + createMockedFunction(pool, 'debtInfo', 'debtInfo():(uint256,uint256,uint256,uint256)') .returns([ ethereum.Value.fromUnsignedBigInt(expectInfo.pendingDebt), ethereum.Value.fromUnsignedBigInt(expectInfo.accruedDebt), ethereum.Value.fromUnsignedBigInt(expectInfo.liquidationDebt), + ethereum.Value.fromUnsignedBigInt(expectInfo.t0Debt2ToCollateral) ]) } +export function mockGetLenderInterestMargin(pool: Address, expectedValue: BigInt): void { + createMockedFunction(poolInfoUtilsNetworkLookUpTable.get(dataSource.network())!, 'lenderInterestMargin', 'lenderInterestMargin(address):(uint256)') + .withArgs([ethereum.Value.fromAddress(pool)]) + .returns([ethereum.Value.fromUnsignedBigInt(expectedValue)]) +} + // mock getLPBValueInQuote contract calls export function mockGetLPBValueInQuote(pool: Address, lpb: BigInt, bucketIndex: BigInt, expectedValue: BigInt): void { - createMockedFunction(poolInfoUtilsNetworkLookUpTable.get(dataSource.network())!, 'lpsToQuoteTokens', 'lpsToQuoteTokens(address,uint256,uint256):(uint256)') + createMockedFunction(poolInfoUtilsNetworkLookUpTable.get(dataSource.network())!, 'lpToQuoteTokens', 'lpToQuoteTokens(address,uint256,uint256):(uint256)') .withArgs([ethereum.Value.fromAddress(pool), ethereum.Value.fromUnsignedBigInt(lpb), ethereum.Value.fromUnsignedBigInt(bucketIndex)]) .returns([ethereum.Value.fromUnsignedBigInt(expectedValue)]) } @@ -485,6 +478,21 @@ export function mockGetAuctionInfoERC20Pool(borrower: Address, pool: Address, ex ]) } +// mock auctionStatus poolInfoUtils calls +export function mockGetAuctionStatus(pool: Address, borrower: Address, expectedInfo: AuctionStatus): void { + createMockedFunction(poolInfoUtilsNetworkLookUpTable.get(dataSource.network())!, + 'auctionStatus', 'auctionStatus(address,address):(uint256,uint256,uint256,bool,uint256,uint256)') + .withArgs([ethereum.Value.fromAddress(pool), ethereum.Value.fromAddress(borrower)]) + .returns([ + ethereum.Value.fromUnsignedBigInt(expectedInfo.kickTime), + ethereum.Value.fromUnsignedBigInt(expectedInfo.collateral), + ethereum.Value.fromUnsignedBigInt(expectedInfo.debtToCover), + ethereum.Value.fromBoolean(expectedInfo.isCollateralized), + ethereum.Value.fromUnsignedBigInt(expectedInfo.price), + ethereum.Value.fromUnsignedBigInt(expectedInfo.neutralPrice) + ]) +} + // mock currentBurnEpoch contract calls export function mockGetCurrentBurnEpoch(pool: Address, expectedEpoch: BigInt): void { createMockedFunction(pool, 'currentBurnEpoch', 'currentBurnEpoch():(uint256)') @@ -524,8 +532,7 @@ export class PoolMockParams { debt: BigInt loansCount: BigInt maxBorrower: Address - pendingInflator: BigInt - pendingInterestFactor: BigInt + inflator: BigInt // prices info mock params hpb: BigInt hpbIndex: BigInt @@ -553,12 +560,12 @@ export function mockPoolInfoUtilsPoolUpdateCalls(pool: Address, params: PoolMock params.poolSize, params.loansCount, params.maxBorrower, - params.pendingInflator, - params.pendingInterestFactor + params.inflator, + ONE_BI ) mockGetPoolLoansInfo(pool, expectedPoolLoansInfo) - const expectedPoolDebtInfo = new DebtInfo(params.debt, ZERO_BI, ZERO_BI) + const expectedPoolDebtInfo = new DebtInfo(params.debt, ZERO_BI, ZERO_BI, ZERO_BI) mockGetDebtInfo(pool, expectedPoolDebtInfo) const expectedPoolPricesInfo = new PoolPricesInfo( @@ -589,4 +596,18 @@ export function mockPoolInfoUtilsPoolUpdateCalls(pool: Address, params: PoolMock params.targetUtilization ) mockGetPoolUtilizationInfo(pool, expectedPoolUtilizationInfo) + + mockGetLenderInterestMargin(pool, BigInt.fromString("850000000000000000")) // 0.85 * 1e18 +} + +/****************************/ +/*** Token Mock Functions ***/ +/****************************/ + +export function mockTokenBalance(tokenAddress: Address, address: Address, expectedBalance: BigInt): void { + createMockedFunction(tokenAddress, 'balanceOf', 'balanceOf(address):(uint256)') + .withArgs([ethereum.Value.fromAddress(address)]) + .returns([ + ethereum.Value.fromUnsignedBigInt(expectedBalance), + ]) } diff --git a/tests/utils/erc-20-pool-utils.ts b/tests/utils/erc-20-pool-utils.ts index 152f470..0cf711d 100644 --- a/tests/utils/erc-20-pool-utils.ts +++ b/tests/utils/erc-20-pool-utils.ts @@ -10,6 +10,7 @@ import { BucketTakeLPAwarded, DrawDebt, Kick, + KickReserveAuction, MoveQuoteToken, RemoveCollateral, RemoveQuoteToken, @@ -17,9 +18,10 @@ import { ReserveAuction, Settle, Take, - TransferLPs, + TransferLP, UpdateInterestRate } from "../../generated/templates/ERC20Pool/ERC20Pool" +import { ReserveAuctionKick, ReserveAuctionTake } from "../../generated/schema" export function createAddCollateralEvent( pool: Address, @@ -94,7 +96,7 @@ export function createAddQuoteTokenEvent( export function createAuctionNFTSettleEvent( borrower: Address, collateral: BigInt, - lps: BigInt, + lp: BigInt, index: BigInt ): AuctionNFTSettle { let auctionNftSettleEvent = changetype(newMockEvent()) @@ -111,7 +113,7 @@ export function createAuctionNFTSettleEvent( ) ) auctionNftSettleEvent.parameters.push( - new ethereum.EventParam("lps", ethereum.Value.fromUnsignedBigInt(lps)) + new ethereum.EventParam("lp", ethereum.Value.fromUnsignedBigInt(lp)) ) auctionNftSettleEvent.parameters.push( new ethereum.EventParam("index", ethereum.Value.fromUnsignedBigInt(index)) @@ -465,7 +467,44 @@ export function createRepayDebtEvent( return repayDebtEvent } -export function createReserveAuctionEvent( +export function createReserveAuctionKickEvent( + operator: Address, + pool: Address, + claimableReservesRemaining: BigInt, + auctionPrice: BigInt, + currentBurnEpoch: BigInt, +): KickReserveAuction { + let reserveAuctionEvent = changetype(newMockEvent()) + + reserveAuctionEvent.parameters = new Array() + + reserveAuctionEvent.parameters.push( + new ethereum.EventParam( + "claimableReservesRemaining", + ethereum.Value.fromUnsignedBigInt(claimableReservesRemaining) + ) + ) + reserveAuctionEvent.parameters.push( + new ethereum.EventParam( + "auctionPrice", + ethereum.Value.fromUnsignedBigInt(auctionPrice) + ) + ) + reserveAuctionEvent.parameters.push( + new ethereum.EventParam( + "currentBurnEpoch", + ethereum.Value.fromUnsignedBigInt(currentBurnEpoch) + ) + ) + + // update transaction target to the expected pool address + reserveAuctionEvent.transaction.from = operator + reserveAuctionEvent.address = pool + + return reserveAuctionEvent; +} + +export function createReserveAuctionTakeEvent( operator: Address, pool: Address, claimableReservesRemaining: BigInt, @@ -571,13 +610,13 @@ export function createTakeEvent( return takeEvent } -export function createTransferLPsEvent( +export function createTransferLPEvent( owner: Address, newOwner: Address, indexes: Array, - lps: BigInt -): TransferLPs { - let transferLpTokensEvent = changetype(newMockEvent()) + lp: BigInt +): TransferLP { + let transferLpTokensEvent = changetype(newMockEvent()) transferLpTokensEvent.parameters = new Array() @@ -595,8 +634,8 @@ export function createTransferLPsEvent( ) transferLpTokensEvent.parameters.push( new ethereum.EventParam( - "lps", - ethereum.Value.fromUnsignedBigInt(lps) + "lp", + ethereum.Value.fromUnsignedBigInt(lp) ) ) diff --git a/tests/utils/position-manager-utils.ts b/tests/utils/position-manager-utils.ts index 74ed61e..721d201 100644 --- a/tests/utils/position-manager-utils.ts +++ b/tests/utils/position-manager-utils.ts @@ -133,7 +133,9 @@ export function createMoveLiquidityEvent( lender: Address, tokenId: BigInt, fromIndex: BigInt, - toIndex: BigInt + toIndex: BigInt, + lpRedeemedFrom: BigInt, + lpAwardedTo: BigInt ): MoveLiquidity { let moveLiquidityEvent = changetype(newMockEvent()) @@ -154,6 +156,12 @@ export function createMoveLiquidityEvent( moveLiquidityEvent.parameters.push( new ethereum.EventParam("toIndex", ethereum.Value.fromUnsignedBigInt(toIndex)) ) + moveLiquidityEvent.parameters.push( + new ethereum.EventParam("lpRedeemedFrom", ethereum.Value.fromUnsignedBigInt(lpRedeemedFrom)) + ) + moveLiquidityEvent.parameters.push( + new ethereum.EventParam("lpAwardedTo", ethereum.Value.fromUnsignedBigInt(lpAwardedTo)) + ) return moveLiquidityEvent diff --git a/tsconfig.json b/tsconfig.json index 5c5d17c..fe02398 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,8 @@ { + "compilerOptions": { + "esModuleInterop": true, + "resolveJsonModule": true + }, "extends": "@graphprotocol/graph-ts/types/tsconfig.base.json", "include": ["src"] }