diff --git a/deployment/deployment.json b/deployment/deployment.json index 8068bd08f7..b96df3bfd5 100644 --- a/deployment/deployment.json +++ b/deployment/deployment.json @@ -13714,5 +13714,39 @@ } } } + }, + "usual": { + "schema": "generic", + "base": "usual", + "protocol": "usual", + "project": "usual", + "deployments": { + "usual-ethereum": { + "network": "ethereum", + "status": "prod", + "versions": { + "schema": "3.0.0", + "subgraph": "1.0.0", + "methodology": "1.0.0" + }, + "files": { + "template": "usual.template.yaml" + }, + "options": { + "prepare:yaml": true, + "prepare:constants": true + }, + "services": { + "hosted-service": { + "slug": "usual-ethereum", + "query-id": "usual-ethereum" + }, + "decentralized-network": { + "slug": "usual-ethereum", + "query-id": "todo" + } + } + } + } } } diff --git a/subgraphs/usual/.gitignore b/subgraphs/usual/.gitignore new file mode 100644 index 0000000000..09140957fa --- /dev/null +++ b/subgraphs/usual/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +configure.ts +docs/ +package-lock.json diff --git a/subgraphs/usual/README.md b/subgraphs/usual/README.md new file mode 100644 index 0000000000..485550c8b6 --- /dev/null +++ b/subgraphs/usual/README.md @@ -0,0 +1,17 @@ +# Usual Protocol Subgraph + +## Methodology v1.0.0 + +## Metrics + +### Usage and Transactions + +### TVL + +### Revenue + +## Useful Links + +- Landing Page: +- Docs: +- Contracts: diff --git a/subgraphs/usual/abis/Tokens/ChainlinkDataFeed.json b/subgraphs/usual/abis/Tokens/ChainlinkDataFeed.json new file mode 100644 index 0000000000..bca8a84144 --- /dev/null +++ b/subgraphs/usual/abis/Tokens/ChainlinkDataFeed.json @@ -0,0 +1,509 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_aggregator", + "type": "address" + }, + { + "internalType": "address", + "name": "_accessController", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessController", + "outputs": [ + { + "internalType": "contract AccessControllerInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "aggregator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_aggregator", + "type": "address" + } + ], + "name": "confirmAggregator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "name": "phaseAggregators", + "outputs": [ + { + "internalType": "contract AggregatorV2V3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "phaseId", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_aggregator", + "type": "address" + } + ], + "name": "proposeAggregator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "proposedAggregator", + "outputs": [ + { + "internalType": "contract AggregatorV2V3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "proposedGetRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proposedLatestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_accessController", + "type": "address" + } + ], + "name": "setController", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/subgraphs/usual/abis/Tokens/ERC20.json b/subgraphs/usual/abis/Tokens/ERC20.json new file mode 100644 index 0000000000..405d6b3648 --- /dev/null +++ b/subgraphs/usual/abis/Tokens/ERC20.json @@ -0,0 +1,222 @@ +[ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "balance", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + }, + { + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + } +] diff --git a/subgraphs/usual/abis/Tokens/ERC20NameBytes.json b/subgraphs/usual/abis/Tokens/ERC20NameBytes.json new file mode 100644 index 0000000000..2d3c877a8c --- /dev/null +++ b/subgraphs/usual/abis/Tokens/ERC20NameBytes.json @@ -0,0 +1,17 @@ +[ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/subgraphs/usual/abis/Tokens/ERC20SymbolBytes.json b/subgraphs/usual/abis/Tokens/ERC20SymbolBytes.json new file mode 100644 index 0000000000..a76d616366 --- /dev/null +++ b/subgraphs/usual/abis/Tokens/ERC20SymbolBytes.json @@ -0,0 +1,17 @@ +[ + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/subgraphs/usual/abis/Tokens/_ERC20.json b/subgraphs/usual/abis/Tokens/_ERC20.json new file mode 100644 index 0000000000..5c47f76a53 --- /dev/null +++ b/subgraphs/usual/abis/Tokens/_ERC20.json @@ -0,0 +1,828 @@ +[ + { + "name": "Transfer", + "inputs": [ + { "type": "address", "name": "sender", "indexed": true }, + { "type": "address", "name": "receiver", "indexed": true }, + { "type": "uint256", "name": "value", "indexed": false } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "Approval", + "inputs": [ + { "type": "address", "name": "owner", "indexed": true }, + { "type": "address", "name": "spender", "indexed": true }, + { "type": "uint256", "name": "value", "indexed": false } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "StrategyAdded", + "inputs": [ + { "type": "address", "name": "strategy", "indexed": true }, + { "type": "uint256", "name": "debtRatio", "indexed": false }, + { "type": "uint256", "name": "rateLimit", "indexed": false }, + { "type": "uint256", "name": "performanceFee", "indexed": false } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "StrategyReported", + "inputs": [ + { "type": "address", "name": "strategy", "indexed": true }, + { "type": "uint256", "name": "gain", "indexed": false }, + { "type": "uint256", "name": "loss", "indexed": false }, + { "type": "uint256", "name": "totalGain", "indexed": false }, + { "type": "uint256", "name": "totalLoss", "indexed": false }, + { "type": "uint256", "name": "totalDebt", "indexed": false }, + { "type": "uint256", "name": "debtAdded", "indexed": false }, + { "type": "uint256", "name": "debtRatio", "indexed": false } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "UpdateGovernance", + "inputs": [{ "type": "address", "name": "governance", "indexed": false }], + "anonymous": false, + "type": "event" + }, + { + "name": "UpdateManagement", + "inputs": [{ "type": "address", "name": "management", "indexed": false }], + "anonymous": false, + "type": "event" + }, + { + "name": "UpdateGuestList", + "inputs": [{ "type": "address", "name": "guestList", "indexed": false }], + "anonymous": false, + "type": "event" + }, + { + "name": "UpdateRewards", + "inputs": [{ "type": "address", "name": "rewards", "indexed": false }], + "anonymous": false, + "type": "event" + }, + { + "name": "UpdateDepositLimit", + "inputs": [{ "type": "uint256", "name": "depositLimit", "indexed": false }], + "anonymous": false, + "type": "event" + }, + { + "name": "UpdatePerformanceFee", + "inputs": [ + { "type": "uint256", "name": "performanceFee", "indexed": false } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "UpdateManagementFee", + "inputs": [ + { "type": "uint256", "name": "managementFee", "indexed": false } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "UpdateGuardian", + "inputs": [{ "type": "address", "name": "guardian", "indexed": false }], + "anonymous": false, + "type": "event" + }, + { + "name": "EmergencyShutdown", + "inputs": [{ "type": "bool", "name": "active", "indexed": false }], + "anonymous": false, + "type": "event" + }, + { + "name": "UpdateWithdrawalQueue", + "inputs": [{ "type": "address[20]", "name": "queue", "indexed": false }], + "anonymous": false, + "type": "event" + }, + { + "name": "StrategyUpdateDebtRatio", + "inputs": [ + { "type": "address", "name": "strategy", "indexed": true }, + { "type": "uint256", "name": "debtRatio", "indexed": false } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "StrategyUpdateRateLimit", + "inputs": [ + { "type": "address", "name": "strategy", "indexed": true }, + { "type": "uint256", "name": "rateLimit", "indexed": false } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "StrategyUpdatePerformanceFee", + "inputs": [ + { "type": "address", "name": "strategy", "indexed": true }, + { "type": "uint256", "name": "performanceFee", "indexed": false } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "StrategyMigrated", + "inputs": [ + { "type": "address", "name": "oldVersion", "indexed": true }, + { "type": "address", "name": "newVersion", "indexed": true } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "StrategyRevoked", + "inputs": [{ "type": "address", "name": "strategy", "indexed": true }], + "anonymous": false, + "type": "event" + }, + { + "name": "StrategyRemovedFromQueue", + "inputs": [{ "type": "address", "name": "strategy", "indexed": true }], + "anonymous": false, + "type": "event" + }, + { + "name": "StrategyAddedToQueue", + "inputs": [{ "type": "address", "name": "strategy", "indexed": true }], + "anonymous": false, + "type": "event" + }, + { + "name": "initialize", + "outputs": [], + "inputs": [ + { "type": "address", "name": "token" }, + { "type": "address", "name": "governance" }, + { "type": "address", "name": "rewards" }, + { "type": "string", "name": "nameOverride" }, + { "type": "string", "name": "symbolOverride" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "name": "initialize", + "outputs": [], + "inputs": [ + { "type": "address", "name": "token" }, + { "type": "address", "name": "governance" }, + { "type": "address", "name": "rewards" }, + { "type": "string", "name": "nameOverride" }, + { "type": "string", "name": "symbolOverride" }, + { "type": "address", "name": "guardian" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "name": "apiVersion", + "outputs": [{ "type": "string", "name": "" }], + "inputs": [], + "stateMutability": "pure", + "type": "function", + "gas": 4519 + }, + { + "name": "setName", + "outputs": [], + "inputs": [{ "type": "string", "name": "name" }], + "stateMutability": "nonpayable", + "type": "function", + "gas": 107017 + }, + { + "name": "setSymbol", + "outputs": [], + "inputs": [{ "type": "string", "name": "symbol" }], + "stateMutability": "nonpayable", + "type": "function", + "gas": 71867 + }, + { + "name": "setGovernance", + "outputs": [], + "inputs": [{ "type": "address", "name": "governance" }], + "stateMutability": "nonpayable", + "type": "function", + "gas": 36338 + }, + { + "name": "acceptGovernance", + "outputs": [], + "inputs": [], + "stateMutability": "nonpayable", + "type": "function", + "gas": 37610 + }, + { + "name": "setManagement", + "outputs": [], + "inputs": [{ "type": "address", "name": "management" }], + "stateMutability": "nonpayable", + "type": "function", + "gas": 37748 + }, + { + "name": "setGuestList", + "outputs": [], + "inputs": [{ "type": "address", "name": "guestList" }], + "stateMutability": "nonpayable", + "type": "function", + "gas": 37778 + }, + { + "name": "setRewards", + "outputs": [], + "inputs": [{ "type": "address", "name": "rewards" }], + "stateMutability": "nonpayable", + "type": "function", + "gas": 37808 + }, + { + "name": "setDepositLimit", + "outputs": [], + "inputs": [{ "type": "uint256", "name": "limit" }], + "stateMutability": "nonpayable", + "type": "function", + "gas": 37738 + }, + { + "name": "setPerformanceFee", + "outputs": [], + "inputs": [{ "type": "uint256", "name": "fee" }], + "stateMutability": "nonpayable", + "type": "function", + "gas": 37872 + }, + { + "name": "setManagementFee", + "outputs": [], + "inputs": [{ "type": "uint256", "name": "fee" }], + "stateMutability": "nonpayable", + "type": "function", + "gas": 37902 + }, + { + "name": "setGuardian", + "outputs": [], + "inputs": [{ "type": "address", "name": "guardian" }], + "stateMutability": "nonpayable", + "type": "function", + "gas": 39146 + }, + { + "name": "setEmergencyShutdown", + "outputs": [], + "inputs": [{ "type": "bool", "name": "active" }], + "stateMutability": "nonpayable", + "type": "function", + "gas": 39217 + }, + { + "name": "setWithdrawalQueue", + "outputs": [], + "inputs": [{ "type": "address[20]", "name": "queue" }], + "stateMutability": "nonpayable", + "type": "function", + "gas": 763893 + }, + { + "name": "transfer", + "outputs": [{ "type": "bool", "name": "" }], + "inputs": [ + { "type": "address", "name": "receiver" }, + { "type": "uint256", "name": "amount" } + ], + "stateMutability": "nonpayable", + "type": "function", + "gas": 76733 + }, + { + "name": "transferFrom", + "outputs": [{ "type": "bool", "name": "" }], + "inputs": [ + { "type": "address", "name": "sender" }, + { "type": "address", "name": "receiver" }, + { "type": "uint256", "name": "amount" } + ], + "stateMutability": "nonpayable", + "type": "function", + "gas": 116496 + }, + { + "name": "approve", + "outputs": [{ "type": "bool", "name": "" }], + "inputs": [ + { "type": "address", "name": "spender" }, + { "type": "uint256", "name": "amount" } + ], + "stateMutability": "nonpayable", + "type": "function", + "gas": 38244 + }, + { + "name": "increaseAllowance", + "outputs": [{ "type": "bool", "name": "" }], + "inputs": [ + { "type": "address", "name": "spender" }, + { "type": "uint256", "name": "amount" } + ], + "stateMutability": "nonpayable", + "type": "function", + "gas": 40285 + }, + { + "name": "decreaseAllowance", + "outputs": [{ "type": "bool", "name": "" }], + "inputs": [ + { "type": "address", "name": "spender" }, + { "type": "uint256", "name": "amount" } + ], + "stateMutability": "nonpayable", + "type": "function", + "gas": 40309 + }, + { + "name": "permit", + "outputs": [{ "type": "bool", "name": "" }], + "inputs": [ + { "type": "address", "name": "owner" }, + { "type": "address", "name": "spender" }, + { "type": "uint256", "name": "amount" }, + { "type": "uint256", "name": "expiry" }, + { "type": "bytes", "name": "signature" } + ], + "stateMutability": "nonpayable", + "type": "function", + "gas": 81237 + }, + { + "name": "totalAssets", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 4123 + }, + { + "name": "deposit", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "name": "deposit", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [{ "type": "uint256", "name": "_amount" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "name": "deposit", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [ + { "type": "uint256", "name": "_amount" }, + { "type": "address", "name": "recipient" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "name": "maxAvailableShares", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 364171 + }, + { + "name": "withdraw", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "name": "withdraw", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [{ "type": "uint256", "name": "maxShares" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "name": "withdraw", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [ + { "type": "uint256", "name": "maxShares" }, + { "type": "address", "name": "recipient" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "name": "withdraw", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [ + { "type": "uint256", "name": "maxShares" }, + { "type": "address", "name": "recipient" }, + { "type": "uint256", "name": "maxLoss" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "name": "pricePerShare", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 12412 + }, + { + "name": "addStrategy", + "outputs": [], + "inputs": [ + { "type": "address", "name": "strategy" }, + { "type": "uint256", "name": "debtRatio" }, + { "type": "uint256", "name": "rateLimit" }, + { "type": "uint256", "name": "performanceFee" } + ], + "stateMutability": "nonpayable", + "type": "function", + "gas": 1450351 + }, + { + "name": "updateStrategyDebtRatio", + "outputs": [], + "inputs": [ + { "type": "address", "name": "strategy" }, + { "type": "uint256", "name": "debtRatio" } + ], + "stateMutability": "nonpayable", + "type": "function", + "gas": 115316 + }, + { + "name": "updateStrategyRateLimit", + "outputs": [], + "inputs": [ + { "type": "address", "name": "strategy" }, + { "type": "uint256", "name": "rateLimit" } + ], + "stateMutability": "nonpayable", + "type": "function", + "gas": 41467 + }, + { + "name": "updateStrategyPerformanceFee", + "outputs": [], + "inputs": [ + { "type": "address", "name": "strategy" }, + { "type": "uint256", "name": "performanceFee" } + ], + "stateMutability": "nonpayable", + "type": "function", + "gas": 41344 + }, + { + "name": "migrateStrategy", + "outputs": [], + "inputs": [ + { "type": "address", "name": "oldVersion" }, + { "type": "address", "name": "newVersion" } + ], + "stateMutability": "nonpayable", + "type": "function", + "gas": 1105801 + }, + { + "name": "revokeStrategy", + "outputs": [], + "inputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "name": "revokeStrategy", + "outputs": [], + "inputs": [{ "type": "address", "name": "strategy" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "name": "addStrategyToQueue", + "outputs": [], + "inputs": [{ "type": "address", "name": "strategy" }], + "stateMutability": "nonpayable", + "type": "function", + "gas": 1196920 + }, + { + "name": "removeStrategyFromQueue", + "outputs": [], + "inputs": [{ "type": "address", "name": "strategy" }], + "stateMutability": "nonpayable", + "type": "function", + "gas": 23091666 + }, + { + "name": "debtOutstanding", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "name": "debtOutstanding", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [{ "type": "address", "name": "strategy" }], + "stateMutability": "view", + "type": "function" + }, + { + "name": "creditAvailable", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "name": "creditAvailable", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [{ "type": "address", "name": "strategy" }], + "stateMutability": "view", + "type": "function" + }, + { + "name": "availableDepositLimit", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 9808 + }, + { + "name": "expectedReturn", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "name": "expectedReturn", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [{ "type": "address", "name": "strategy" }], + "stateMutability": "view", + "type": "function" + }, + { + "name": "report", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [ + { "type": "uint256", "name": "gain" }, + { "type": "uint256", "name": "loss" }, + { "type": "uint256", "name": "_debtPayment" } + ], + "stateMutability": "nonpayable", + "type": "function", + "gas": 937520 + }, + { + "name": "sweep", + "outputs": [], + "inputs": [{ "type": "address", "name": "token" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "name": "sweep", + "outputs": [], + "inputs": [ + { "type": "address", "name": "token" }, + { "type": "uint256", "name": "amount" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "name": "name", + "outputs": [{ "type": "string", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 9053 + }, + { + "name": "symbol", + "outputs": [{ "type": "string", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 8106 + }, + { + "name": "decimals", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 2711 + }, + { + "name": "balanceOf", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [{ "type": "address", "name": "arg0" }], + "stateMutability": "view", + "type": "function", + "gas": 2956 + }, + { + "name": "allowance", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [ + { "type": "address", "name": "arg0" }, + { "type": "address", "name": "arg1" } + ], + "stateMutability": "view", + "type": "function", + "gas": 3201 + }, + { + "name": "totalSupply", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 2801 + }, + { + "name": "token", + "outputs": [{ "type": "address", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 2831 + }, + { + "name": "governance", + "outputs": [{ "type": "address", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 2861 + }, + { + "name": "management", + "outputs": [{ "type": "address", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 2891 + }, + { + "name": "guardian", + "outputs": [{ "type": "address", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 2921 + }, + { + "name": "guestList", + "outputs": [{ "type": "address", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 2951 + }, + { + "name": "strategies", + "outputs": [ + { "type": "uint256", "name": "performanceFee" }, + { "type": "uint256", "name": "activation" }, + { "type": "uint256", "name": "debtRatio" }, + { "type": "uint256", "name": "rateLimit" }, + { "type": "uint256", "name": "lastReport" }, + { "type": "uint256", "name": "totalDebt" }, + { "type": "uint256", "name": "totalGain" }, + { "type": "uint256", "name": "totalLoss" } + ], + "inputs": [{ "type": "address", "name": "arg0" }], + "stateMutability": "view", + "type": "function", + "gas": 10322 + }, + { + "name": "withdrawalQueue", + "outputs": [{ "type": "address", "name": "" }], + "inputs": [{ "type": "uint256", "name": "arg0" }], + "stateMutability": "view", + "type": "function", + "gas": 3120 + }, + { + "name": "emergencyShutdown", + "outputs": [{ "type": "bool", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 3041 + }, + { + "name": "depositLimit", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 3071 + }, + { + "name": "debtRatio", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 3101 + }, + { + "name": "totalDebt", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 3131 + }, + { + "name": "lastReport", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 3161 + }, + { + "name": "activation", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 3191 + }, + { + "name": "rewards", + "outputs": [{ "type": "address", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 3221 + }, + { + "name": "managementFee", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 3251 + }, + { + "name": "performanceFee", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 3281 + }, + { + "name": "nonces", + "outputs": [{ "type": "uint256", "name": "" }], + "inputs": [{ "type": "address", "name": "arg0" }], + "stateMutability": "view", + "type": "function", + "gas": 3526 + }, + { + "name": "DOMAIN_SEPARATOR", + "outputs": [{ "type": "bytes32", "name": "" }], + "inputs": [], + "stateMutability": "view", + "type": "function", + "gas": 3341 + } +] diff --git a/subgraphs/usual/abis/Usual/USYC.json b/subgraphs/usual/abis/Usual/USYC.json new file mode 100644 index 0000000000..77d8eb30a7 --- /dev/null +++ b/subgraphs/usual/abis/Usual/USYC.json @@ -0,0 +1,681 @@ +[ + { + "inputs": [ + { "internalType": "address", "name": "_authority", "type": "address" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { "inputs": [], "name": "BadAddress", "type": "error" }, + { "inputs": [], "name": "BadAmount", "type": "error" }, + { "inputs": [], "name": "BadPrice", "type": "error" }, + { "inputs": [], "name": "InvalidSignature", "type": "error" }, + { "inputs": [], "name": "NoAccess", "type": "error" }, + { "inputs": [], "name": "NoUnderlying", "type": "error" }, + { "inputs": [], "name": "NotPermissioned", "type": "error" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "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": "amount", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beacon", + "type": "address" + } + ], + "name": "BeaconUpgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "FeeProcessed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newRecipient", + "type": "address" + } + ], + "name": "FeeRecipientSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "managementFee", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newManagementFee", + "type": "uint256" + } + ], + "name": "ManagementFeeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "MinterConfigured", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oracle", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newOracle", + "type": "address" + } + ], + "name": "OracleSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "teller", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newTeller", + "type": "address" + } + ], + "name": "TellerSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "TradeToFiat", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "UnderlyingSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Withdrawal", + "type": "event" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "", "type": "address" }, + { "internalType": "address", "name": "", "type": "address" } + ], + "name": "allowance", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "approve", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "authority", + "outputs": [ + { "internalType": "contract IAuthority", "name": "", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "balanceOf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_amount", "type": "uint256" } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_from", "type": "address" }, + { "internalType": "uint256", "name": "_amount", "type": "uint256" } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_from", "type": "address" }, + { "internalType": "uint256", "name": "_amount", "type": "uint256" } + ], + "name": "burnFor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_amount", "type": "uint256" } + ], + "name": "deposit", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_recipient", "type": "address" }, + { "internalType": "uint256", "name": "_amount", "type": "uint256" } + ], + "name": "depositFor", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "_name", "type": "string" }, + { "internalType": "string", "name": "_symbol", "type": "string" }, + { "internalType": "uint8", "name": "_dec", "type": "uint8" }, + { "internalType": "address", "name": "_owner", "type": "address" }, + { "internalType": "address", "name": "_minter", "type": "address" }, + { "internalType": "address", "name": "_teller", "type": "address" }, + { "internalType": "address", "name": "_underlying", "type": "address" } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "managementFee", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_to", "type": "address" }, + { "internalType": "uint256", "name": "_amount", "type": "uint256" } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "minterAllowance", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "nonces", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "value", "type": "uint256" }, + { "internalType": "uint256", "name": "deadline", "type": "uint256" }, + { "internalType": "uint8", "name": "v", "type": "uint8" }, + { "internalType": "bytes32", "name": "r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "s", "type": "bytes32" } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "proxiableUUID", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_minter", "type": "address" }, + { "internalType": "uint256", "name": "_amount", "type": "uint256" } + ], + "name": "setMinterAllowance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_teller", "type": "address" } + ], + "name": "setTeller", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_token", "type": "address" } + ], + "name": "setUnderlying", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "teller", + "outputs": [ + { + "internalType": "contract YieldTokenTellerV2", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_token", "type": "address" }, + { "internalType": "uint256", "name": "_amount", "type": "uint256" }, + { "internalType": "address", "name": "_recipient", "type": "address" } + ], + "name": "tradeToFiat", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_to", "type": "address" }, + { "internalType": "uint256", "name": "_amount", "type": "uint256" } + ], + "name": "transfer", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_from", "type": "address" }, + { "internalType": "address", "name": "_to", "type": "address" }, + { "internalType": "uint256", "name": "_amount", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "underlying", + "outputs": [ + { + "internalType": "contract IERC20Metadata", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { "internalType": "bytes", "name": "data", "type": "bytes" } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_amount", "type": "uint256" }, + { "internalType": "uint8", "name": "_v", "type": "uint8" }, + { "internalType": "bytes32", "name": "_r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "_s", "type": "bytes32" } + ], + "name": "withdraw", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_recipient", "type": "address" }, + { "internalType": "uint256", "name": "_amount", "type": "uint256" }, + { "internalType": "uint8", "name": "_v", "type": "uint8" }, + { "internalType": "bytes32", "name": "_r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "_s", "type": "bytes32" } + ], + "name": "withdrawTo", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/subgraphs/usual/configurations/configurations/configurations.ts b/subgraphs/usual/configurations/configurations/configurations.ts new file mode 100644 index 0000000000..20e9973d6d --- /dev/null +++ b/subgraphs/usual/configurations/configurations/configurations.ts @@ -0,0 +1,20 @@ +import { log } from "@graphprotocol/graph-ts"; + +import { Configurations } from "./interface"; +import { Deploy } from "./deploy"; +import { UsualEthereumConfigurations } from "../../protocols/usual/config/deployments/usual-ethereum/configurations"; + +export function getNetworkConfigurations(deploy: i32): Configurations { + switch (deploy) { + case Deploy.USUAL_ETHEREUM: { + return new UsualEthereumConfigurations(); + } + default: { + log.critical( + "No configurations found for deployment protocol/network", + [] + ); + return new UsualEthereumConfigurations(); + } + } +} diff --git a/subgraphs/usual/configurations/configurations/deploy.ts b/subgraphs/usual/configurations/configurations/deploy.ts new file mode 100644 index 0000000000..022c95e003 --- /dev/null +++ b/subgraphs/usual/configurations/configurations/deploy.ts @@ -0,0 +1,3 @@ +export namespace Deploy { + export const USUAL_ETHEREUM = 0; +} diff --git a/subgraphs/usual/configurations/configurations/interface.ts b/subgraphs/usual/configurations/configurations/interface.ts new file mode 100644 index 0000000000..42853be02d --- /dev/null +++ b/subgraphs/usual/configurations/configurations/interface.ts @@ -0,0 +1,6 @@ +export interface Configurations { + getNetwork(): string; + getProtocolId(): string; + getProtocolName(): string; + getProtocolSlug(): string; +} diff --git a/subgraphs/usual/configurations/configure.mustache b/subgraphs/usual/configurations/configure.mustache new file mode 100644 index 0000000000..971ba89573 --- /dev/null +++ b/subgraphs/usual/configurations/configure.mustache @@ -0,0 +1,6 @@ +import { getNetworkConfigurations } from "./configurations/configurations"; +import { Deploy } from "./configurations/deploy"; + +let deployment = Deploy.{{ deployment }}; + +export const NetworkConfigs = getNetworkConfigurations(deployment); diff --git a/subgraphs/usual/package.json b/subgraphs/usual/package.json new file mode 100644 index 0000000000..7112c30d91 --- /dev/null +++ b/subgraphs/usual/package.json @@ -0,0 +1,15 @@ +{ + "name": "usual", + "license": "MIT", + "scripts": { + "format": "npx prettier --write .", + "prepare:constants": "mustache protocols/${npm_config_protocol}/config/deployments/${npm_config_id}/configurations.json configurations/configure.mustache > configurations/configure.ts" + }, + "dependencies": { + "@graphprotocol/graph-cli": "^0.80.1", + "@graphprotocol/graph-ts": "^0.35.1" + }, + "devDependencies": { + "prettier": "^3.2.5" + } +} diff --git a/subgraphs/usual/protocols/usual/config/deployments/usual-ethereum/configurations.json b/subgraphs/usual/protocols/usual/config/deployments/usual-ethereum/configurations.json new file mode 100644 index 0000000000..b9098e6eda --- /dev/null +++ b/subgraphs/usual/protocols/usual/config/deployments/usual-ethereum/configurations.json @@ -0,0 +1,12 @@ +{ + "deployment": "USUAL_ETHEREUM", + "network": "mainnet", + "file": "./src/mappings/handlers.ts", + "usyc": { + "address": "0x136471a34f6ef19fe571effc1ca711fdb8e49f2b", + "startBlock": "19932493" + }, + "graftEnabled": false, + "subgraphId": "", + "graftStartBlock": 0 +} diff --git a/subgraphs/usual/protocols/usual/config/deployments/usual-ethereum/configurations.ts b/subgraphs/usual/protocols/usual/config/deployments/usual-ethereum/configurations.ts new file mode 100644 index 0000000000..e968622ff9 --- /dev/null +++ b/subgraphs/usual/protocols/usual/config/deployments/usual-ethereum/configurations.ts @@ -0,0 +1,21 @@ +import { Configurations } from "../../../../../configurations/configurations/interface"; +import { + PROTOCOL_NAME, + PROTOCOL_SLUG, +} from "../../../../../src/common/constants"; +import { Network } from "../../../../../src/sdk/util/constants"; + +export class UsualEthereumConfigurations implements Configurations { + getNetwork(): string { + return Network.MAINNET; + } + getProtocolId(): string { + return "0x136471a34f6ef19fe571effc1ca711fdb8e49f2b"; + } + getProtocolName(): string { + return PROTOCOL_NAME; + } + getProtocolSlug(): string { + return PROTOCOL_SLUG; + } +} diff --git a/subgraphs/usual/protocols/usual/config/templates/usual.template.yaml b/subgraphs/usual/protocols/usual/config/templates/usual.template.yaml new file mode 100644 index 0000000000..d8b3c316ab --- /dev/null +++ b/subgraphs/usual/protocols/usual/config/templates/usual.template.yaml @@ -0,0 +1,51 @@ +specVersion: 0.0.8 +schema: + file: ./schema.graphql +{{#graftEnabled}} +features: + - grafting +graft: + base: {{ subgraphId }} # Subgraph ID of base subgraph + block: {{ graftStartBlock }} # Block number +{{/graftEnabled}} +dataSources: + - kind: ethereum + name: USYC + network: {{ network }} + source: + address: "{{ usyc.address }}" + abi: USYC + startBlock: {{ usyc.startBlock }} + mapping: + kind: ethereum/events + apiVersion: 0.0.7 + language: wasm/assemblyscript + entities: + - Token + - Pool + - PoolDailySnapshot + - PoolHourlySnapshot + - Protocol + - FinancialsDailySnapshot + - UsageMetricsDailySnapshot + - UsageMetricsHourlySnapshot + abis: + - name: USYC + file: ./abis/Usual/USYC.json + + - name: ERC20 + file: ./abis/Tokens/ERC20.json + - name: _ERC20 + file: ./abis/Tokens/_ERC20.json + - name: ERC20SymbolBytes + file: ./abis/Tokens/ERC20SymbolBytes.json + - name: ERC20NameBytes + file: ./abis/Tokens/ERC20NameBytes.json + - name: ChainlinkDataFeed + file: ./abis/Tokens/ChainlinkDataFeed.json + eventHandlers: + - event: Transfer(indexed address,indexed address,uint256) + handler: handleTransfer + - event: FeeProcessed(indexed address,uint256) + handler: handleFeeProcessed + file: {{{ file }}} diff --git a/subgraphs/usual/schema.graphql b/subgraphs/usual/schema.graphql new file mode 100644 index 0000000000..1e338a6709 --- /dev/null +++ b/subgraphs/usual/schema.graphql @@ -0,0 +1,353 @@ +# Subgraph Schema: Generic +# Version: 3.0.0 +# See https://github.com/messari/subgraphs/blob/master/docs/SCHEMA.md for details + +enum Network { + ARBITRUM_ONE + ARWEAVE_MAINNET + AURORA + AVALANCHE + BASE + BOBA + BSC # aka BNB Chain + CELO + COSMOS + CRONOS + MAINNET # Ethereum Mainnet + FANTOM + FUSE + HARMONY + JUNO + MOONBEAM + MOONRIVER + NEAR_MAINNET + OPTIMISM + OSMOSIS + MATIC # aka Polygon + GNOSIS + BASE +} + +enum ProtocolType { + EXCHANGE + LENDING + YIELD + BRIDGE + GENERIC + # Will add more +} + +enum TokenType { + MULTIPLE + UNKNOWN + ERC20 + ERC721 + ERC1155 + BEP20 + BEP721 + BEP1155 + # Will add more +} + +type Token @entity @regularPolling { + " Smart contract address of the token " + id: Bytes! + + " Name of the token, mirrored from the smart contract " + name: String! + + " Symbol of the token, mirrored from the smart contract " + symbol: String! + + " The number of decimal places this token uses, default to 18 " + decimals: Int! + + " Optional field to track the price of a token, mostly for caching purposes " + lastPriceUSD: BigDecimal + + " Optional field to track the block number of the last token price " + lastPriceBlockNumber: BigInt +} + +############################# +##### Protocol Metadata ##### +############################# + +type Protocol @entity @regularPolling { + " Smart contract address of the protocol's main contract (Factory, Registry, etc) " + id: Bytes! + + " Name of the protocol, including version. e.g. Uniswap v3 " + name: String! + + " Slug of protocol, including version. e.g. uniswap-v3 " + slug: String! + + " Version of the subgraph schema, in SemVer format (e.g. 1.0.0) " + schemaVersion: String! + + " Version of the subgraph implementation, in SemVer format (e.g. 1.0.0) " + subgraphVersion: String! + + " Version of the methodology used to compute metrics, loosely based on SemVer format (e.g. 1.0.0) " + methodologyVersion: String! + + " The blockchain network this subgraph is indexing on " + network: Network! + + " The type of protocol (e.g. DEX, Lending, Yield, etc) " + type: ProtocolType! + + ##### Quantitative Data ##### + + " Current TVL (Total Value Locked) of the entire protocol " + totalValueLockedUSD: BigDecimal! + + " Revenue claimed by suppliers to the protocol. LPs on DEXs (e.g. 0.25% of the swap fee in Sushiswap). Depositors on Lending Protocols. NFT sellers on OpenSea. " + cumulativeSupplySideRevenueUSD: BigDecimal! + + " Gross revenue for the protocol (revenue claimed by protocol). Examples: AMM protocol fee (Sushi’s 0.05%). OpenSea 10% sell fee. " + cumulativeProtocolSideRevenueUSD: BigDecimal! + + " All revenue generated by the protocol. e.g. 0.30% of swap fee in Sushiswap, all yield generated by Yearn. " + cumulativeTotalRevenueUSD: BigDecimal! + + " Total number of transactions. Transactions include events triggered by outside users (ie, deposit, withdraw, etc.)" + cumulativeTransactionCount: Int! + + " Number of cumulative unique users " + cumulativeUniqueUsers: Int! + + " Total number of pools " + totalPoolCount: Int! + + " Day ID of the most recent daily snapshot " + lastSnapshotDayID: Int! + + " Timestamp of the last time this entity was updated " + lastUpdateTimestamp: BigInt! + + ##### Snapshots ##### + + " Daily usage metrics for this protocol " + dailyUsageMetrics: [UsageMetricsDailySnapshot!]! + @derivedFrom(field: "protocol") + + " Daily financial metrics for this protocol " + financialMetrics: [FinancialsDailySnapshot!]! @derivedFrom(field: "protocol") + + ##### Pools ##### + + " All pools that belong to this protocol " + pools: [Pool!]! @derivedFrom(field: "protocol") +} + +############################### +##### Protocol Timeseries ##### +############################### + +type UsageMetricsDailySnapshot @entity @dailySnapshot { + " ID is # of days since Unix epoch time " + id: Bytes! + + " Number of days since Unix epoch time " + day: Int! + + " Protocol this snapshot is associated with " + protocol: Protocol! + + " Number of unique daily active users " + dailyActiveUsers: Int! + + " Number of cumulative unique users " + cumulativeUniqueUsers: Int! + + " Total number of transactions occurred in a day. Transactions include all entities that implement the Event interface. " + dailyTransactionCount: Int! + + " Total number of transactions. Transactions include events triggered by outside users (ie, deposit, withdraw, etc.)" + cumulativeTransactionCount: Int! + + " Total number of pools " + totalPoolCount: Int! + + " Timestamp of when this snapshot was taken/last modified (May be taken after interval has passed) " + timestamp: BigInt! + + " Block number of when this snapshot was taken/last modified (May be taken after interval has passed) " + blockNumber: BigInt! +} + +type FinancialsDailySnapshot @entity @dailySnapshot { + " ID is # of days since Unix epoch time " + id: Bytes! + + " Number of days since Unix epoch time " + day: Int! + + " Protocol this snapshot is associated with " + protocol: Protocol! + + " Current TVL (Total Value Locked) of the entire protocol " + totalValueLockedUSD: BigDecimal! + + " Revenue claimed by suppliers to the protocol. LPs on DEXs (e.g. 0.25% of the swap fee in Sushiswap). Depositors on Lending Protocols. NFT sellers on OpenSea. " + dailySupplySideRevenueUSD: BigDecimal! + + " Revenue claimed by suppliers to the protocol. LPs on DEXs (e.g. 0.25% of the swap fee in Sushiswap). Depositors on Lending Protocols. NFT sellers on OpenSea. " + cumulativeSupplySideRevenueUSD: BigDecimal! + + " Gross revenue for the protocol (revenue claimed by protocol). Examples: AMM protocol fee (Sushi’s 0.05%). OpenSea 10% sell fee. " + dailyProtocolSideRevenueUSD: BigDecimal! + + " Gross revenue for the protocol (revenue claimed by protocol). Examples: AMM protocol fee (Sushi’s 0.05%). OpenSea 10% sell fee. " + cumulativeProtocolSideRevenueUSD: BigDecimal! + + " All revenue generated by the protocol. e.g. 0.30% of swap fee in Sushiswap, all yield generated by Yearn. " + dailyTotalRevenueUSD: BigDecimal! + + " All revenue generated by the protocol. e.g. 0.30% of swap fee in Sushiswap, all yield generated by Yearn. " + cumulativeTotalRevenueUSD: BigDecimal! + + " Timestamp of when this snapshot was taken/last modified (May be taken after interval has passed) " + timestamp: BigInt! + + " Block number of when this snapshot was taken/last modified (May be taken after interval has passed) " + blockNumber: BigInt! +} + +########################### +##### Pool-Level Data ##### +########################### + +type Pool @entity @regularPolling { + " Smart contract address of the pool " + id: Bytes! + + " The protocol this pool belongs to " + protocol: Protocol! + + " Name of the pool (e.g. Curve.fi DAI/USDC/USDT) " + name: String + + " Symbol of liquidity pool (e.g. 3CRV) " + symbol: String + + # Generally protocols accept one or multiple tokens and mint tokens to the depositor to track ownership + # Some protocols don't mint any tokens to track ownership, in that case outputToken is null. + + " Token that is minted to track ownership of position in protocol " + outputToken: Token + + " Tokens that need to be deposited to take a position in protocol. e.g. WETH and USDC to deposit into the WETH-USDC pool. Array to account for multi-asset pools like Curve and Balancer " + inputTokens: [Token!]! + + " Creation timestamp " + createdTimestamp: BigInt! + + " Creation block number " + createdBlockNumber: BigInt! + + ##### Quantitative Data ##### + + " Current TVL (Total Value Locked) of this pool in USD " + totalValueLockedUSD: BigDecimal! + + " All revenue generated by the pool, accrued to the supply side. " + cumulativeSupplySideRevenue: BigInt! + cumulativeSupplySideRevenueUSD: BigDecimal! + + " All revenue generated by the pool, accrued to the protocol. " + cumulativeProtocolSideRevenue: BigInt! + cumulativeProtocolSideRevenueUSD: BigDecimal! + + " All revenue generated by the pool. " + cumulativeTotalRevenueUSD: BigDecimal! + + " Amount of input tokens in the pool. The ordering should be the same as the pool's `inputTokens` field. " + inputTokenBalances: [BigInt!]! + + " The USD value of input tokens in this pool. Should be the same order as the pool's `inputTokens` field. " + inputTokenBalancesUSD: [BigDecimal!]! + + " Total supply of output token. Note that certain DEXes don't have an output token (e.g. Bancor) " + outputTokenSupply: BigInt + + " Day ID of the most recent daily snapshot " + lastSnapshotDayID: Int! + + " Timestamp of the last time this entity was updated " + lastUpdateTimestamp: BigInt! +} + +type PoolDailySnapshot @entity @dailySnapshot { + " { Smart contract address of the pool }-{ # of days since Unix epoch time } " + id: Bytes! + + " Number of days since Unix epoch time " + day: Int! + + " The protocol this snapshot belongs to " + protocol: Protocol! + + " The pool this snapshot belongs to " + pool: Pool! + + ##### Quantitative Data ##### + + " Current TVL (Total Value Locked) of this pool " + totalValueLockedUSD: BigDecimal! + + " All revenue generated by the pool, accrued to the supply side. " + cumulativeSupplySideRevenueUSD: BigDecimal! + + " Daily revenue generated by the pool, accrued to the supply side. " + dailySupplySideRevenueUSD: BigDecimal! + + " All revenue generated by the pool, accrued to the protocol. " + cumulativeProtocolSideRevenueUSD: BigDecimal! + + " Daily revenue generated by the pool, accrued to the protocol. " + dailyProtocolSideRevenueUSD: BigDecimal! + + " All revenue generated by the pool. " + cumulativeTotalRevenueUSD: BigDecimal! + + " Daily revenue generated by the pool. " + dailyTotalRevenueUSD: BigDecimal! + + " Amount of input tokens in the pool. The ordering should be the same as the pool's `inputTokens` field. " + inputTokenBalances: [BigInt!]! + + " The USD value of input tokens in this pool. Should be the same order as the pool's `inputTokens` field. " + inputTokenBalancesUSD: [BigDecimal!]! + + " Total supply of output token. Note that certain DEXes don't have an output token (e.g. Bancor) " + outputTokenSupply: BigInt + + " Timestamp of when this snapshot was taken/last modified (May be taken after interval has passed) " + timestamp: BigInt! + + " Block number of when this snapshot was taken/last modified (May be taken after interval has passed) " + blockNumber: BigInt! +} + +# An account is a unique Ethereum address +# Helps to accumulate total unique users +type Account @entity @regularPolling { + " Address of the account " + id: ID! +} + +# Helper entity for calculating daily active users +type ActiveAccount @entity { + " { daily }-{ Address of the account }-{ Days since Unix epoch } " + id: ID! +} + +type _ActivityHelper @entity { + " { daily }-{ Days since Unix epoch} " + id: Bytes! + + dailyActiveUsers: Int! +} diff --git a/subgraphs/usual/src/common/constants.ts b/subgraphs/usual/src/common/constants.ts new file mode 100644 index 0000000000..15067ddceb --- /dev/null +++ b/subgraphs/usual/src/common/constants.ts @@ -0,0 +1,6 @@ +////////////////////////////// +///// Protocol Constants ///// +////////////////////////////// + +export const PROTOCOL_NAME = "Usual"; +export const PROTOCOL_SLUG = "usual"; diff --git a/subgraphs/usual/src/mappings/handlers.ts b/subgraphs/usual/src/mappings/handlers.ts new file mode 100644 index 0000000000..7fb21a4af4 --- /dev/null +++ b/subgraphs/usual/src/mappings/handlers.ts @@ -0,0 +1,107 @@ +import { Address, BigDecimal, BigInt, log } from "@graphprotocol/graph-ts"; + +import { Versions } from "../versions"; +import { NetworkConfigs } from "../../configurations/configure"; + +import { SDK } from "../sdk/protocols/generic"; +import { ProtocolConfig, TokenPricer } from "../sdk/protocols/config"; +import { TokenInitializer, TokenParams } from "../sdk/protocols/generic/tokens"; +import { bigIntToBigDecimal } from "../sdk/util/numbers"; +import { + BIGDECIMAL_ONE, + BIGINT_ZERO, + ETH_ADDRESS, + INT_ZERO, + ZERO_ADDRESS, +} from "../sdk/util/constants"; + +import { Transfer, FeeProcessed, USYC } from "../../generated/USYC/USYC"; +import { _ERC20 } from "../../generated/USYC/_ERC20"; +import { Token } from "../../generated/schema"; + +const conf = new ProtocolConfig( + NetworkConfigs.getProtocolId(), + NetworkConfigs.getProtocolName(), + NetworkConfigs.getProtocolSlug(), + Versions +); +const TREASURY = Address.fromString( + "0xdd82875f0840aad58a455a70b88eed9f59cec7c7" +); + +class Pricer implements TokenPricer { + getTokenPrice(token: Token): BigDecimal { + log.debug("[getTokenPrice] token: {}", [token.id.toHexString()]); + return BIGDECIMAL_ONE; + } + + getAmountValueUSD(token: Token, amount: BigInt): BigDecimal { + const usdPrice = this.getTokenPrice(token); + const _amount = bigIntToBigDecimal(amount, token.decimals); + + return usdPrice.times(_amount); + } +} + +class TokenInit implements TokenInitializer { + getTokenParams(address: Address): TokenParams { + let name = "unknown"; + let symbol = "UNKNOWN"; + let decimals = INT_ZERO as i32; + + if (address == Address.fromString(ETH_ADDRESS)) { + name = "eth"; + symbol = "ETH"; + decimals = 18 as i32; + } else { + const erc20 = _ERC20.bind(address); + name = erc20.name(); + symbol = erc20.symbol(); + decimals = erc20.decimals().toI32(); + } + return new TokenParams(name, symbol, decimals); + } +} + +export function handleTransfer(event: Transfer): void { + const sdk = SDK.initializeFromEvent( + conf, + new Pricer(), + new TokenInit(), + event + ); + const token = sdk.Tokens.getOrCreateToken(event.address); + const pool = sdk.Pools.loadPool(token.id); + if (!pool.isInitialized) { + pool.initialize(token.name, token.symbol, [token.id], null); + } + + const contract = USYC.bind(event.address); + const supply = contract.balanceOf(TREASURY); + pool.setInputTokenBalances([supply], true); + + if ( + event.params.from == Address.fromString(ZERO_ADDRESS) || + event.params.to == Address.fromString(ZERO_ADDRESS) + ) { + const user = event.transaction.from; + const account = sdk.Accounts.loadAccount(user); + account.trackActivity(); + } +} + +export function handleFeeProcessed(event: FeeProcessed): void { + const sdk = SDK.initializeFromEvent( + conf, + new Pricer(), + new TokenInit(), + event + ); + const token = sdk.Tokens.getOrCreateToken(event.address); + const pool = sdk.Pools.loadPool(token.id); + if (!pool.isInitialized) { + pool.initialize(token.name, token.symbol, [token.id], null); + } + + pool.addRevenueNative(token, BIGINT_ZERO, event.params.fee); +} diff --git a/subgraphs/usual/src/sdk/README.md b/subgraphs/usual/src/sdk/README.md new file mode 100644 index 0000000000..c8e3bddd41 --- /dev/null +++ b/subgraphs/usual/src/sdk/README.md @@ -0,0 +1,25 @@ +## Wat dis? + +This folder contains a library which abstracts the developer from most of the _schema related_ functionality. Ideally, building a subgraph should only consist on understading a protocol and translating certain events into actions and metrics. But the particularities of how this metrics are stored and how they relate to each other inside the schema can and should be abstracted away. Things like taking snapshots of entities every X amount of time, updating TVL every time there is a deposit/withdrawal, updating fees at _swap & pool & protocol & snapshot levels_, etc ... This library aims to do that. + +When using this library, entities should not be updated directly, but always through the library unless absolutely needed. An exception to this is if you create your own auxiliary entity to aid on the handling of some events. + +## How It's Organized + +Pretty straightforward, `/protocols` and `/util`. + +Because we are currently experimenting different approaches, each protocol type has a different schema, and AssemblyScript has some limitations when dealing with interfaces, so far, each protocol type has its own implementation. They all live in their respective folders under `/protocols`, and have very little shared code. + +`/util` contains all these common functions and constants we use over and over. + +## Setting it up + +It would be ideal to have the library to get setup automatically with the messari-cli, in a similar way as we do to generate versions. We might eventually get there, but so far it consists on a manual copy process. + +Refer to each protocol type readme, since requirements might vary: + +- [Bridges](./protocols/bridge/README.md) +- [Lending](./protocols/lending/README.md) +- [Perpetual Futures](./protocols/perpfutures/README.md) +- DEX +- Yield diff --git a/subgraphs/usual/src/sdk/protocols/config.ts b/subgraphs/usual/src/sdk/protocols/config.ts new file mode 100644 index 0000000000..f75004e165 --- /dev/null +++ b/subgraphs/usual/src/sdk/protocols/config.ts @@ -0,0 +1,45 @@ +import { BigDecimal, BigInt } from "@graphprotocol/graph-ts"; +import { Token } from "../../../generated/schema"; +import { Versions } from "../../../../../deployment/context/interface"; + +export interface ProtocolConfigurer { + getID(): string; + getName(): string; + getSlug(): string; + getVersions(): Versions; +} + +export class ProtocolConfig implements ProtocolConfigurer { + id: string; + name: string; + slug: string; + versions: Versions; + + constructor(id: string, name: string, slug: string, versions: Versions) { + this.id = id; + this.name = name; + this.slug = slug; + this.versions = versions; + } + + getID(): string { + return this.id; + } + + getName(): string { + return this.name; + } + + getSlug(): string { + return this.slug; + } + + getVersions(): Versions { + return this.versions; + } +} + +export interface TokenPricer { + getTokenPrice(token: Token): BigDecimal; + getAmountValueUSD(token: Token, amount: BigInt): BigDecimal; +} diff --git a/subgraphs/usual/src/sdk/protocols/generic/account.ts b/subgraphs/usual/src/sdk/protocols/generic/account.ts new file mode 100644 index 0000000000..b962ef2360 --- /dev/null +++ b/subgraphs/usual/src/sdk/protocols/generic/account.ts @@ -0,0 +1,93 @@ +import { + ActiveAccount, + Account as AccountSchema, +} from "../../../../generated/schema"; +import { TokenManager } from "./tokens"; +import { ProtocolManager } from "./protocol"; +import { Address } from "@graphprotocol/graph-ts"; +import { CustomEventType, getUnixDays, getUnixHours } from "../../util/events"; + +/** + * This file contains the AccountClass, which does + * the operations on the Account entity. This includes: + * - Creating a new Account + * - Updating an existing Account + * + * Schema Version: 3.0.0 + * SDK Version: 1.1.0 + * Author(s): + * - @steegecs + * - @shashwatS22 + */ + +export class AccountManager { + protocol: ProtocolManager; + tokens: TokenManager; + + constructor(protocol: ProtocolManager, tokens: TokenManager) { + this.protocol = protocol; + this.tokens = tokens; + } + + loadAccount(address: Address): Account { + let acc = AccountSchema.load(address.toHexString()); + if (acc) { + return new Account(this.protocol, acc, this.tokens); + } + + acc = new AccountSchema(address.toHexString()); + acc.save(); + + this.protocol.addUser(); + + return new Account(this.protocol, acc, this.tokens); + } +} + +export class AccountWasActive { + hourly: boolean; + daily: boolean; +} + +export class Account { + account: AccountSchema; + event: CustomEventType; + protocol: ProtocolManager; + tokens: TokenManager; + + constructor( + protocol: ProtocolManager, + account: AccountSchema, + tokens: TokenManager + ) { + this.account = account; + this.protocol = protocol; + this.event = protocol.getCurrentEvent(); + this.tokens = tokens; + } + + trackActivity(): void { + const days = getUnixDays(this.event.block); + const hours = getUnixHours(this.event.block); + + const generalHourlyID = `${this.account.id}-hourly-${hours}`; + const generalDailyID = `${this.account.id}-daily-${days}`; + + const generalActivity: AccountWasActive = { + daily: this.isActiveByActivityID(generalDailyID), + hourly: this.isActiveByActivityID(generalHourlyID), + }; + + this.protocol.addActiveUser(generalActivity); + this.protocol.addTransaction(); + } + + private isActiveByActivityID(id: string): boolean { + const dAct = ActiveAccount.load(id); + if (!dAct) { + new ActiveAccount(id).save(); + return true; + } + return false; + } +} diff --git a/subgraphs/usual/src/sdk/protocols/generic/index.ts b/subgraphs/usual/src/sdk/protocols/generic/index.ts new file mode 100644 index 0000000000..59f201e67f --- /dev/null +++ b/subgraphs/usual/src/sdk/protocols/generic/index.ts @@ -0,0 +1,72 @@ +import { PoolManager } from "./pool"; +import { AccountManager } from "./account"; +import { ProtocolManager } from "./protocol"; +import { BIGINT_ZERO } from "../../util/constants"; +import { ethereum } from "@graphprotocol/graph-ts"; +import { CustomEventType } from "../../util/events"; +import { TokenManager, TokenInitializer } from "./tokens"; +import { ProtocolConfigurer, TokenPricer } from "../config"; + +/** + * This file contains the SDK class, which initializes + * all managers from event or call. + * + * Schema Version: 3.0.0 + * SDK Version: 1.1.0 + * Author(s): + * - @steegecs + * - @shashwatS22 + + */ + +export class SDK { + Protocol: ProtocolManager; + Accounts: AccountManager; + Pools: PoolManager; + Tokens: TokenManager; + Pricer: TokenPricer; + + constructor( + config: ProtocolConfigurer, + pricer: TokenPricer, + tokenInitializer: TokenInitializer, + event: CustomEventType + ) { + this.Protocol = ProtocolManager.load(config, pricer, event); + this.Tokens = new TokenManager(this.Protocol, tokenInitializer); + this.Accounts = new AccountManager(this.Protocol, this.Tokens); + this.Pools = new PoolManager(this.Protocol, this.Tokens); + this.Pricer = pricer; + + this.Protocol.sdk = this; + } + + static initializeFromEvent( + config: ProtocolConfigurer, + pricer: TokenPricer, + tokenInitializer: TokenInitializer, + event: ethereum.Event + ): SDK { + const customEvent = CustomEventType.initialize( + event.block, + event.transaction, + event.logIndex, + event + ); + return new SDK(config, pricer, tokenInitializer, customEvent); + } + + static initializeFromCall( + config: ProtocolConfigurer, + pricer: TokenPricer, + tokenInitializer: TokenInitializer, + event: ethereum.Call + ): SDK { + const customEvent = CustomEventType.initialize( + event.block, + event.transaction, + BIGINT_ZERO + ); + return new SDK(config, pricer, tokenInitializer, customEvent); + } +} diff --git a/subgraphs/usual/src/sdk/protocols/generic/pool.ts b/subgraphs/usual/src/sdk/protocols/generic/pool.ts new file mode 100644 index 0000000000..007e801ea5 --- /dev/null +++ b/subgraphs/usual/src/sdk/protocols/generic/pool.ts @@ -0,0 +1,339 @@ +import { TokenManager } from "./tokens"; +import { ProtocolManager } from "./protocol"; +import { PoolSnapshot } from "./poolSnapshot"; +import { BIGDECIMAL_ZERO, BIGINT_ZERO } from "../../util/constants"; +import { Pool as PoolSchema, Token } from "../../../../generated/schema"; +import { Bytes, BigDecimal, BigInt, Address } from "@graphprotocol/graph-ts"; + +/** + * This file contains the PoolManager, which is used to + * initialize new pools in the protocol. + * + * Schema Version: 3.0.0 + * SDK Version: 1.1.0 + * Author(s): + * - @steegecs + * - @shashwatS22 + */ + +export class PoolManager { + protocol: ProtocolManager; + tokens: TokenManager; + + constructor(protocol: ProtocolManager, tokens: TokenManager) { + this.protocol = protocol; + this.tokens = tokens; + } + + loadPool(id: Bytes): Pool { + let entity = PoolSchema.load(id); + if (entity) { + return new Pool(this.protocol, entity, this.tokens, true); + } + + entity = new PoolSchema(id); + entity.protocol = this.protocol.getBytesID(); + + const pool = new Pool(this.protocol, entity, this.tokens, false); + pool.isInitialized = false; + return pool; + } +} + +export class Pool { + pool: PoolSchema; + protocol: ProtocolManager; + tokens: TokenManager; + snapshoter: PoolSnapshot | null = null; + + public isInitialized: boolean = true; + + constructor( + protocol: ProtocolManager, + pool: PoolSchema, + tokens: TokenManager, + isInitialized: bool + ) { + this.pool = pool; + this.protocol = protocol; + this.tokens = tokens; + + if (isInitialized) { + this.snapshoter = new PoolSnapshot(pool, protocol.event); + this.pool.lastUpdateTimestamp = protocol.event.block.timestamp; + this.save(); + } + } + + private save(): void { + this.pool.save(); + } + + initialize( + name: string, + symbol: string, + inputTokens: Bytes[], + outputToken: Token | null + ): void { + if (this.isInitialized) { + return; + } + + const event = this.protocol.getCurrentEvent(); + this.pool.protocol = this.protocol.getBytesID(); + this.pool.name = name; + this.pool.symbol = symbol; + this.pool.inputTokens = inputTokens; + this.pool.outputToken = outputToken ? outputToken.id : null; + this.pool.createdTimestamp = event.block.timestamp; + this.pool.createdBlockNumber = event.block.number; + + const inputTokenBalances: BigInt[] = []; + const inputTokenBalancesUSD: BigDecimal[] = []; + for (let i = 0; i < inputTokens.length; i++) { + inputTokenBalances.push(BIGINT_ZERO); + inputTokenBalancesUSD.push(BIGDECIMAL_ZERO); + } + this.pool.inputTokenBalances = inputTokenBalances; + this.pool.inputTokenBalancesUSD = inputTokenBalancesUSD; + this.pool.totalValueLockedUSD = BIGDECIMAL_ZERO; + this.pool.cumulativeSupplySideRevenue = BIGINT_ZERO; + this.pool.cumulativeSupplySideRevenueUSD = BIGDECIMAL_ZERO; + this.pool.cumulativeProtocolSideRevenue = BIGINT_ZERO; + this.pool.cumulativeProtocolSideRevenueUSD = BIGDECIMAL_ZERO; + this.pool.cumulativeTotalRevenueUSD = BIGDECIMAL_ZERO; + + this.pool.lastSnapshotDayID = 0; + this.pool.lastUpdateTimestamp = BIGINT_ZERO; + this.save(); + + this.protocol.addPool(); + } + + /** + * Recalculates the total value locked for this pool based on its current input token balance. + * This function will also update the protocol's total value locked based on the change in this pool's. + */ + private refreshTotalValueLocked(): void { + let totalValueLockedUSD = BIGDECIMAL_ZERO; + + for (let idx = 0; idx < this.pool.inputTokens.length; idx++) { + const inputTokenBalanceUSD = this.pool.inputTokenBalancesUSD[idx]; + totalValueLockedUSD = totalValueLockedUSD.plus(inputTokenBalanceUSD); + } + + this.setTotalValueLocked(totalValueLockedUSD); + } + + /** + * Updates the total value locked for this pool to the given value. + * Will also update the protocol's total value locked based on the change in this pool's. + */ + setTotalValueLocked(newTVL: BigDecimal): void { + const delta = newTVL.minus(this.pool.totalValueLockedUSD); + this.addTotalValueLocked(delta); + this.save(); + } + + /** + * Adds the given delta to the total value locked for this pool. + * Will also update the protocol's total value locked based on the change in this pool's. + * + * @param delta The change in total value locked for this pool. + */ + addTotalValueLocked(delta: BigDecimal): void { + this.pool.totalValueLockedUSD = this.pool.totalValueLockedUSD.plus(delta); + this.protocol.addTotalValueLocked(delta); + this.save(); + } + + /** + * Utility function to update token price. + * + * @param token + * @returns + */ + setTokenPrice(token: Token): void { + if ( + !token.lastPriceBlockNumber || + (token.lastPriceBlockNumber && + token.lastPriceBlockNumber! < this.protocol.event.block.number) + ) { + const pricePerToken = this.protocol.getTokenPricer().getTokenPrice(token); + token.lastPriceUSD = pricePerToken; + token.lastPriceBlockNumber = this.protocol.event.block.number; + token.save(); + } + } + + /** + * Utility function to convert some amount of input token to USD. + * + * @param token + * @param amount the amount of inputToken to convert to USD + * @returns The converted amount. + */ + getInputTokenAmountPrice(token: Token, amount: BigInt): BigDecimal { + this.setTokenPrice(token); + + return this.protocol.getTokenPricer().getAmountValueUSD(token, amount); + } + + addInputTokenBalances( + amounts: BigInt[], + updateMetrics: boolean = true + ): void { + if (amounts.length != this.pool.inputTokenBalances.length) return; + + const newBalances: BigInt[] = []; + for (let idx = 0; idx < this.pool.inputTokenBalances.length; idx++) { + newBalances[idx] = this.pool.inputTokenBalances[idx].plus(amounts[idx]); + } + this.setInputTokenBalances(newBalances, updateMetrics); + } + + /** + * Sets the pool's input token balance to the given amount. It will optionally + * update the pool's and protocol's total value locked. If not stated, will default to true. + * + * @param amount amount to be set as the pool's input token balance. + * @param updateMetrics optional parameter to indicate whether to update the pool's and protocol's total value locked. + */ + setInputTokenBalances( + newBalances: BigInt[], + updateMetrics: boolean = true + ): void { + this.pool.inputTokenBalances = newBalances; + this.setInputTokenBalancesUSD(); + if (updateMetrics) { + this.refreshTotalValueLocked(); + } + } + + /** + * Sets the pool's input token balance USD by calculating it for each token. + */ + private setInputTokenBalancesUSD(): void { + const inputTokenBalancesUSD: BigDecimal[] = []; + for (let idx = 0; idx < this.pool.inputTokens.length; idx++) { + const inputTokenBalance = this.pool.inputTokenBalances[idx]; + const inputToken = this.tokens.getOrCreateToken( + Address.fromBytes(this.pool.inputTokens[idx]) + ); + + const amountUSD = this.getInputTokenAmountPrice( + inputToken, + inputTokenBalance + ); + inputTokenBalancesUSD.push(amountUSD); + } + this.pool.inputTokenBalancesUSD = inputTokenBalancesUSD; + } + + getBytesID(): Bytes { + return this.pool.id; + } + + /** + * Adds a given USD value to the pool and protocol supplySideRevenue. It can be a positive or negative amount. + * Same as for the rest of setters, this is mostly to be called internally by the library. + * But you can use it if you need to. It will also update the protocol's snapshots. + * @param rev {BigDecimal} The value to add to the protocol's supplySideRevenue. + */ + addSupplySideRevenueUSD(rev: BigDecimal): void { + this.pool.cumulativeTotalRevenueUSD = + this.pool.cumulativeTotalRevenueUSD.plus(rev); + this.pool.cumulativeSupplySideRevenueUSD = + this.pool.cumulativeSupplySideRevenueUSD.plus(rev); + this.save(); + + this.protocol.addSupplySideRevenueUSD(rev); + } + + /** + * Adds a given USD value to the pool and protocol protocolSideRevenue. It can be a positive or negative amount. + * Same as for the rest of setters, this is mostly to be called internally by the library. + * But you can use it if you need to. It will also update the protocol's snapshots. + * @param rev {BigDecimal} The value to add to the protocol's protocolSideRevenue. + */ + addProtocolSideRevenueUSD(rev: BigDecimal): void { + this.pool.cumulativeTotalRevenueUSD = + this.pool.cumulativeTotalRevenueUSD.plus(rev); + this.pool.cumulativeProtocolSideRevenueUSD = + this.pool.cumulativeProtocolSideRevenueUSD.plus(rev); + this.save(); + + this.protocol.addProtocolSideRevenueUSD(rev); + } + + /** + * Adds a given USD value to the pool and protocol's supplySideRevenue and protocolSideRevenue. It can be a positive or negative amount. + * Same as for the rest of setters, this is mostly to be called internally by the library. + * But you can use it if you need to. It will also update the protocol's snapshots. + * @param protocolSide {BigDecimal} The value to add to the protocol's protocolSideRevenue. + * @param supplySide {BigDecimal} The value to add to the protocol's supplySideRevenue. + */ + addRevenueUSD(protocolSide: BigDecimal, supplySide: BigDecimal): void { + this.addSupplySideRevenueUSD(supplySide); + this.addProtocolSideRevenueUSD(protocolSide); + } + + /** + * Convenience method to add revenue denominated in the pool's input token. It converts it to USD + * under the hood and calls addRevenueUSD. + */ + addRevenueNative( + inputToken: Token, + supplySide: BigInt, + protocolSide: BigInt + ): void { + const pricer = this.protocol.pricer; + + const pAmountUSD = pricer.getAmountValueUSD(inputToken, protocolSide); + const sAmountUSD = pricer.getAmountValueUSD(inputToken, supplySide); + + this.pool.cumulativeProtocolSideRevenue = + this.pool.cumulativeProtocolSideRevenue.plus(protocolSide); + this.pool.cumulativeSupplySideRevenue = + this.pool.cumulativeSupplySideRevenue.plus(supplySide); + + this.addRevenueUSD(pAmountUSD, sAmountUSD); + } + + getRevenueUSD(): BigDecimal[] { + return [ + this.pool.cumulativeProtocolSideRevenueUSD, + this.pool.cumulativeSupplySideRevenueUSD, + ]; + } + + getRevenue(): BigInt[] { + return [ + this.pool.cumulativeProtocolSideRevenue, + this.pool.cumulativeSupplySideRevenue, + ]; + } + + /** + * Adds a given amount to the pool's outputTokenSupply. It should only be used for pools + * of type LIQUIDITY. Or pools that emit some kind of LP token on deposit. + * @param amount + */ + addOutputTokenSupply(amount: BigInt): void { + if (!this.pool.outputTokenSupply) { + this.pool.outputTokenSupply = BIGINT_ZERO; + } + this.pool.outputTokenSupply = this.pool.outputTokenSupply!.plus(amount); + this.save(); + } + + /** + * Sets the pool's outputTokenSupply value. It should only be used for pools + * of type LIQUIDITY. Or pools that emit some kind of LP token on deposit. + * @param amount + */ + setOutputTokenSupply(amount: BigInt): void { + this.pool.outputTokenSupply = amount; + this.save(); + } +} diff --git a/subgraphs/usual/src/sdk/protocols/generic/poolSnapshot.ts b/subgraphs/usual/src/sdk/protocols/generic/poolSnapshot.ts new file mode 100644 index 0000000000..c7945dbb64 --- /dev/null +++ b/subgraphs/usual/src/sdk/protocols/generic/poolSnapshot.ts @@ -0,0 +1,93 @@ +import { + Pool as PoolSchema, + PoolDailySnapshot, +} from "../../../../generated/schema"; +import { SECONDS_PER_DAY } from "../../util/constants"; +import { CustomEventType, getUnixDays, getUnixHours } from "../../util/events"; + +/** + * This file contains the PoolSnapshot, which is used to + * make all of the storage changes that occur in the pool daily and hourly snapshots. + * + * Schema Version: 3.0.0 + * SDK Version: 1.1.0 + * Author(s): + * - @steegecs + * - @shashwatS22 + */ + +export class PoolSnapshot { + pool: PoolSchema; + event: CustomEventType; + dayID: i32; + hourID: i32; + + constructor(pool: PoolSchema, event: CustomEventType) { + this.pool = pool; + this.event = event; + this.dayID = getUnixDays(event.block); + this.hourID = getUnixHours(event.block); + this.takeSnapshots(); + } + + private takeSnapshots(): void { + if (!this.pool.lastUpdateTimestamp) return; + + const snapshotDayID = + this.pool.lastUpdateTimestamp.toI32() / SECONDS_PER_DAY; + + if (snapshotDayID != this.dayID) { + this.takeDailySnapshot(snapshotDayID); + this.pool.lastSnapshotDayID = snapshotDayID; + this.pool.save(); + } + } + + private takeDailySnapshot(day: i32): void { + const snapshot = new PoolDailySnapshot(this.pool.id.concatI32(day)); + const previousSnapshot = PoolDailySnapshot.load( + this.pool.id.concatI32(this.pool.lastSnapshotDayID) + ); + + snapshot.day = day; + snapshot.protocol = this.pool.protocol; + snapshot.pool = this.pool.id; + snapshot.timestamp = this.event.block.timestamp; + snapshot.blockNumber = this.event.block.number; + + // tvl and balances + snapshot.totalValueLockedUSD = this.pool.totalValueLockedUSD; + snapshot.inputTokenBalances = this.pool.inputTokenBalances; + snapshot.inputTokenBalancesUSD = this.pool.inputTokenBalancesUSD; + + // revenues + snapshot.cumulativeSupplySideRevenueUSD = + this.pool.cumulativeSupplySideRevenueUSD; + snapshot.cumulativeProtocolSideRevenueUSD = + this.pool.cumulativeProtocolSideRevenueUSD; + snapshot.cumulativeTotalRevenueUSD = this.pool.cumulativeTotalRevenueUSD; + + // deltas + let supplySideRevenueDelta = snapshot.cumulativeSupplySideRevenueUSD; + let protocolSideRevenueDelta = snapshot.cumulativeProtocolSideRevenueUSD; + let totalRevenueDelta = snapshot.cumulativeTotalRevenueUSD; + + if (previousSnapshot) { + supplySideRevenueDelta = snapshot.cumulativeSupplySideRevenueUSD.minus( + previousSnapshot.cumulativeSupplySideRevenueUSD + ); + protocolSideRevenueDelta = + snapshot.cumulativeProtocolSideRevenueUSD.minus( + previousSnapshot.cumulativeProtocolSideRevenueUSD + ); + totalRevenueDelta = snapshot.cumulativeTotalRevenueUSD.minus( + previousSnapshot.cumulativeTotalRevenueUSD + ); + } + snapshot.dailySupplySideRevenueUSD = supplySideRevenueDelta; + snapshot.dailyProtocolSideRevenueUSD = protocolSideRevenueDelta; + snapshot.dailyTotalRevenueUSD = totalRevenueDelta; + + snapshot.save(); + } +} diff --git a/subgraphs/usual/src/sdk/protocols/generic/protocol.ts b/subgraphs/usual/src/sdk/protocols/generic/protocol.ts new file mode 100644 index 0000000000..c09aac2905 --- /dev/null +++ b/subgraphs/usual/src/sdk/protocols/generic/protocol.ts @@ -0,0 +1,257 @@ +import { SDK } from "."; +import { + dataSource, + Address, + Bytes, + BigDecimal, +} from "@graphprotocol/graph-ts"; +import { AccountWasActive } from "./account"; +import * as constants from "../../util/constants"; +import { BIGINT_ZERO } from "../../util/constants"; +import { CustomEventType } from "../../util/events"; +import { ProtocolSnapshot } from "./protocolSnapshot"; +import { ProtocolConfigurer, TokenPricer } from "../config"; +import { Protocol as ProtocolSchema } from "../../../../generated/schema"; +import { Versions } from "../../../../../../deployment/context/interface"; + +/** + * This file contains the ProtocolManager class, which is used to + * make all of the storage changes that occur in a protocol. + * + * Schema Version: 3.0.0 + * SDK Version: 1.1.0 + * Author(s): + * - @steegecs + * - @shashwatS22 + */ + +/** + * ProtocolManager is a wrapper around the ProtocolSchema entity that takes care of + * safely and conveniently updating the entity. Updating the Protocol entity using this + * wrapper also takes care of the Financials and Usage snapshots. + */ +export class ProtocolManager { + protocol: ProtocolSchema; + event: CustomEventType; + pricer: TokenPricer; + snapshoter: ProtocolSnapshot; + sdk: SDK | null = null; + /** + * Creates a new Protocol instance. This should only be called by the Protocol.load + * @private + */ + private constructor( + protocol: ProtocolSchema, + pricer: TokenPricer, + event: CustomEventType + ) { + this.protocol = protocol; + this.event = event; + this.pricer = pricer; + this.snapshoter = new ProtocolSnapshot(protocol, event); + this.protocol.lastUpdateTimestamp = event.block.timestamp; + } + + /** + * This is the main function to instantiate a Protocol entity. Most times it is not called directly, but from the SDK initializer. + * + * @param conf {ProtocolConfigurer} An object that implements the ProtocolConfigurer interface, to set some of the protocol's properties + * @param pricer {TokenPricer} An object that implements the TokenPricer interface, to allow the wrapper to access pricing data + * @param event {CustomEventType} The event being handled at a time. + * @returns Protocol + */ + static load( + conf: ProtocolConfigurer, + pricer: TokenPricer, + event: CustomEventType + ): ProtocolManager { + const id = Address.fromString(conf.getID()); + let protocol = ProtocolSchema.load(id); + if (protocol) { + const proto = new ProtocolManager(protocol, pricer, event); + proto.setVersions(conf.getVersions()); + return proto; + } + + protocol = new ProtocolSchema(id); + protocol.name = conf.getName(); + protocol.slug = conf.getSlug(); + protocol.network = dataSource.network().toUpperCase().replace("-", "_"); + protocol.type = constants.ProtocolType.GENERIC; + protocol.totalValueLockedUSD = constants.BIGDECIMAL_ZERO; + protocol.cumulativeSupplySideRevenueUSD = constants.BIGDECIMAL_ZERO; + protocol.cumulativeProtocolSideRevenueUSD = constants.BIGDECIMAL_ZERO; + protocol.cumulativeTotalRevenueUSD = constants.BIGDECIMAL_ZERO; + + protocol.cumulativeTransactionCount = 0; + protocol.cumulativeUniqueUsers = 0; + protocol.totalPoolCount = 0; + + protocol.lastSnapshotDayID = 0; + protocol.lastUpdateTimestamp = BIGINT_ZERO; + + protocol.schemaVersion = conf.getVersions().getSchemaVersion(); + protocol.subgraphVersion = conf.getVersions().getSubgraphVersion(); + protocol.methodologyVersion = conf.getVersions().getMethodologyVersion(); + + const proto = new ProtocolManager(protocol, pricer, event); + proto.save(); + return proto; + } + + /** + * Updates the protocol entity versions. This is called on load to make sure we update the version + * if we've grafted the subgraph. + * + * @param versions {Versions} An object that implements the Versions interface, to get the protocol's versions + */ + private setVersions(versions: Versions): void { + this.protocol.schemaVersion = versions.getSchemaVersion(); + this.protocol.subgraphVersion = versions.getSubgraphVersion(); + this.protocol.methodologyVersion = versions.getMethodologyVersion(); + this.save(); + } + + /** + * This will save the entity to storage. If any other action needs to be performed on + * save, it should be added here. + * It is meant to be used internally. If you need to save the entity from outside the wrapper + * you should probably be using some of the setters instead. + * @private + */ + private save(): void { + this.protocol.save(); + } + + /** + * + * @returns {string} The ID of the protocol entity. + */ + getID(): string { + return this.protocol.id.toHexString(); + } + + /** + * + * @returns {Bytes} The ID of the protocol entity, as Bytes. + */ + getBytesID(): Bytes { + return this.protocol.id; + } + + /** + * + * @returns {CustomEventType} the event currently being handled. + */ + getCurrentEvent(): CustomEventType { + return this.event; + } + + /** + * + * @returns {TokenPricer} The pricer object used by the wrapper. + * @see TokenPricer + */ + getTokenPricer(): TokenPricer { + return this.pricer; + } + + /** + * Sets the TVL in USD for the protocol. Most times this will be called internally by + * other members of the library when TVL changes are made to them. But if the library + * is not well fitted to a given protocol and you need to set the TVL manually, you can + * use this method. + * It will also update the protocol's snapshots. + * @param tvl {BigDecimal} The new total value locked for the protocol. + */ + setTotalValueLocked(tvl: BigDecimal): void { + this.protocol.totalValueLockedUSD = tvl; + this.save(); + } + + /** + * Adds a given USD value to the protocol's TVL. It can be a positive or negative amount. + * Same as for setTotalValueLocked, this is mostly to be called internally by the library. + * But you can use it if you need to. It will also update the protocol's snapshots. + * @param tvl {BigDecimal} The value to add to the protocol's TVL. + */ + addTotalValueLocked(tvl: BigDecimal): void { + this.protocol.totalValueLockedUSD = + this.protocol.totalValueLockedUSD.plus(tvl); + this.save(); + } + + /** + * Adds a given USD value to the protocol supplySideRevenue. It can be a positive or negative amount. + * Same as for the rest of setters, this is mostly to be called internally by the library. + * But you can use it if you need to. It will also update the protocol's snapshots. + * @param rev {BigDecimal} The value to add to the protocol's supplySideRevenue. + */ + addSupplySideRevenueUSD(rev: BigDecimal): void { + this.protocol.cumulativeTotalRevenueUSD = + this.protocol.cumulativeTotalRevenueUSD.plus(rev); + this.protocol.cumulativeSupplySideRevenueUSD = + this.protocol.cumulativeSupplySideRevenueUSD.plus(rev); + this.save(); + } + + /** + * Adds a given USD value to the protocol protocolSideRevenue. It can be a positive or negative amount. + * Same as for the rest of setters, this is mostly to be called internally by the library. + * But you can use it if you need to. It will also update the protocol's snapshots. + * @param rev {BigDecimal} The value to add to the protocol's protocolSideRevenue. + */ + addProtocolSideRevenueUSD(rev: BigDecimal): void { + this.protocol.cumulativeTotalRevenueUSD = + this.protocol.cumulativeTotalRevenueUSD.plus(rev); + this.protocol.cumulativeProtocolSideRevenueUSD = + this.protocol.cumulativeProtocolSideRevenueUSD.plus(rev); + this.save(); + } + + /** + * Adds a given USD value to the protocol's supplySideRevenue and protocolSideRevenue. It can be a positive or negative amount. + * Same as for the rest of setters, this is mostly to be called internally by the library. + * But you can use it if you need to. It will also update the protocol's snapshots. + * @param protocolSide {BigDecimal} The value to add to the protocol's protocolSideRevenue. + * @param supplySide {BigDecimal} The value to add to the protocol's supplySideRevenue. + */ + addRevenueUSD(protocolSide: BigDecimal, supplySide: BigDecimal): void { + this.addSupplySideRevenueUSD(supplySide); + this.addProtocolSideRevenueUSD(protocolSide); + } + + /** + * Adds some value to the cumulativeUniqueUsers counter. If the value is omitted it will default to 1. + * If you are loading accounts with the AccountManager you won't need to use this method. + * @param count {u8} The value to add to the counter. + */ + addUser(count: u8 = 1): void { + this.protocol.cumulativeUniqueUsers += count; + this.save(); + } + + /** + * Will increase the hourly and daily active users counters. These will be reflected + * on the next Usage snapshot whenever it comes up. + */ + addActiveUser(activity: AccountWasActive): void { + this.snapshoter.addActiveUser(activity); + } + + /** + * Increases the totalPoolCount counter by the given value. + * If you are using the PoolManager class you won't need to use this method. + * @param count {u8} The value to add to the counter. + * @see PoolManager + */ + addPool(count: u8 = 1): void { + this.protocol.totalPoolCount += count; + this.save(); + } + + addTransaction(): void { + this.protocol.cumulativeTransactionCount += 1; + this.save(); + } +} diff --git a/subgraphs/usual/src/sdk/protocols/generic/protocolSnapshot.ts b/subgraphs/usual/src/sdk/protocols/generic/protocolSnapshot.ts new file mode 100644 index 0000000000..85d80bbc36 --- /dev/null +++ b/subgraphs/usual/src/sdk/protocols/generic/protocolSnapshot.ts @@ -0,0 +1,165 @@ +import { + _ActivityHelper, + FinancialsDailySnapshot, + UsageMetricsDailySnapshot, + Protocol as ProtocolSchema, +} from "../../../../generated/schema"; +import { AccountWasActive } from "./account"; +import { Bytes } from "@graphprotocol/graph-ts"; +import { SECONDS_PER_DAY } from "../../util/constants"; +import { CustomEventType, getUnixDays, getUnixHours } from "../../util/events"; + +const ActivityHelperID = Bytes.fromUTF8("_ActivityHelper"); + +/** + * This file contains the ProtocolSnapshot, which is used to + * make all of the storage changes that occur in the protocol's + * daily and hourly snapshots. + * + * Schema Version: 3.0.0 + * SDK Version: 1.1.0 + * Author(s): + * - @steegecs + * - @shashwatS22 + */ + +/** + * Helper class to manage Financials and Usage snapshots. + * It is not meant to be used directly, but rather by the Protocol and Account lib classes. + * Whenever it is instantiated it will check if it is time to take any of the + * dailyFinancials, dailyUsage or hourlyUsage snapshots. + * + * Snapshots are taken in a way that allows the snapshot entity to be immutable. + */ +export class ProtocolSnapshot { + protocol: ProtocolSchema; + event: CustomEventType; + dayID: i32; + hourID: i32; + activityHelper: _ActivityHelper; + + constructor(protocol: ProtocolSchema, event: CustomEventType) { + this.protocol = protocol; + this.event = event; + this.dayID = getUnixDays(event.block); + this.hourID = getUnixHours(event.block); + this.activityHelper = initActivityHelper(); + this.takeSnapshots(); + } + + addActiveUser(activity: AccountWasActive): void { + this.activityHelper.dailyActiveUsers += activity.daily ? 1 : 0; + this.activityHelper.save(); + } + + private takeSnapshots(): void { + const snapshotDayID = + this.protocol.lastUpdateTimestamp.toI32() / SECONDS_PER_DAY; + + if (snapshotDayID != this.dayID) { + this.takeFinancialsDailySnapshot(snapshotDayID); + this.takeUsageDailySnapshot(snapshotDayID); + this.protocol.lastSnapshotDayID = snapshotDayID; + this.protocol.save(); + } + } + + private takeFinancialsDailySnapshot(day: i32): void { + const snapshot = new FinancialsDailySnapshot(Bytes.fromI32(day)); + const previousSnapshot = FinancialsDailySnapshot.load( + Bytes.fromI32(this.protocol.lastSnapshotDayID) + ); + + snapshot.day = day; + snapshot.protocol = this.protocol.id; + snapshot.blockNumber = this.event.block.number; + snapshot.timestamp = this.event.block.timestamp; + + // tvl + snapshot.totalValueLockedUSD = this.protocol.totalValueLockedUSD; + + // revenues + snapshot.cumulativeSupplySideRevenueUSD = + this.protocol.cumulativeSupplySideRevenueUSD; + snapshot.cumulativeProtocolSideRevenueUSD = + this.protocol.cumulativeProtocolSideRevenueUSD; + snapshot.cumulativeTotalRevenueUSD = + this.protocol.cumulativeTotalRevenueUSD; + + // deltas + let supplySideRevenueDelta = snapshot.cumulativeSupplySideRevenueUSD; + let protocolSideRevenueDelta = snapshot.cumulativeProtocolSideRevenueUSD; + let totalRevenueDelta = snapshot.cumulativeTotalRevenueUSD; + + if (previousSnapshot) { + supplySideRevenueDelta = snapshot.cumulativeSupplySideRevenueUSD.minus( + previousSnapshot.cumulativeSupplySideRevenueUSD + ); + protocolSideRevenueDelta = + snapshot.cumulativeProtocolSideRevenueUSD.minus( + previousSnapshot.cumulativeProtocolSideRevenueUSD + ); + totalRevenueDelta = snapshot.cumulativeTotalRevenueUSD.minus( + previousSnapshot.cumulativeTotalRevenueUSD + ); + } + snapshot.dailySupplySideRevenueUSD = supplySideRevenueDelta; + snapshot.dailyProtocolSideRevenueUSD = protocolSideRevenueDelta; + snapshot.dailyTotalRevenueUSD = totalRevenueDelta; + + snapshot.save(); + } + + private takeUsageDailySnapshot(day: i32): void { + const activity = this.activityHelper; + + const snapshot = new UsageMetricsDailySnapshot(Bytes.fromI32(day)); + const previousSnapshot = UsageMetricsDailySnapshot.load( + Bytes.fromI32(this.protocol.lastSnapshotDayID) + ); + + snapshot.protocol = this.protocol.id; + snapshot.day = day; + snapshot.blockNumber = this.event.block.number; + snapshot.timestamp = this.event.block.timestamp; + + // unique users + snapshot.cumulativeUniqueUsers = this.protocol.cumulativeUniqueUsers; + + // daily activity + snapshot.dailyActiveUsers = activity.dailyActiveUsers; + + // transaction counts + snapshot.cumulativeTransactionCount = + this.protocol.cumulativeTransactionCount; + + // misc + snapshot.totalPoolCount = this.protocol.totalPoolCount; + + // deltas + let transactionDelta = snapshot.cumulativeTransactionCount; + + if (previousSnapshot) { + transactionDelta = + snapshot.cumulativeTransactionCount - + previousSnapshot.cumulativeTransactionCount; + } + snapshot.dailyTransactionCount = transactionDelta; + snapshot.save(); + + activity.dailyActiveUsers = 0; + activity.save(); + } +} + +function initActivityHelper(): _ActivityHelper { + let helper = _ActivityHelper.load(ActivityHelperID); + if (helper) { + return helper; + } + helper = new _ActivityHelper(ActivityHelperID); + helper.dailyActiveUsers = 0; + + helper.save(); + return helper; +} diff --git a/subgraphs/usual/src/sdk/protocols/generic/tokens.ts b/subgraphs/usual/src/sdk/protocols/generic/tokens.ts new file mode 100644 index 0000000000..7095cf896c --- /dev/null +++ b/subgraphs/usual/src/sdk/protocols/generic/tokens.ts @@ -0,0 +1,58 @@ +import { ProtocolManager } from "./protocol"; +import { Address } from "@graphprotocol/graph-ts"; +import { Token } from "../../../../generated/schema"; +import { BIGDECIMAL_ZERO } from "../../util/constants"; + +/** + * This file contains the TokenManagerClass, which initializes + * token entities. + * + * Schema Version: 3.0.0 + * SDK Version: 1.1.0 + * Author(s): + * - @steegecs + * - @shashwatS22 + */ + +export interface TokenInitializer { + getTokenParams(address: Address): TokenParams; +} + +export class TokenParams { + name: string; + symbol: string; + decimals: i32; + + constructor(name: string, symbol: string, decimals: i32) { + this.name = name; + this.symbol = symbol; + this.decimals = decimals; + } +} + +export class TokenManager { + protocol: ProtocolManager; + initializer: TokenInitializer; + + constructor(protocol: ProtocolManager, init: TokenInitializer) { + this.protocol = protocol; + this.initializer = init; + } + + getOrCreateToken(address: Address): Token { + let token = Token.load(address); + if (token) { + return token; + } + + const params = this.initializer.getTokenParams(address); + token = new Token(address); + token.name = params.name; + token.symbol = params.symbol; + token.decimals = params.decimals; + token.lastPriceUSD = BIGDECIMAL_ZERO; + token.save(); + + return token; + } +} diff --git a/subgraphs/usual/src/sdk/util/arrays.ts b/subgraphs/usual/src/sdk/util/arrays.ts new file mode 100644 index 0000000000..d610008f16 --- /dev/null +++ b/subgraphs/usual/src/sdk/util/arrays.ts @@ -0,0 +1,103 @@ +import { Bytes } from "@graphprotocol/graph-ts"; + +// A function which given 3 arrays of arbitrary types of the same length, +// where the first one holds the reference order, the second one holds the same elements +// as the first but in different order, and the third any arbitrary elements. It will return +// the third array after sorting it according to the order of the first one. +// For example: +// sortArrayByReference(['a', 'c', 'b'], ['a', 'b', 'c'], [1, 2, 3]) => [1, 3, 2] +export function sortArrayByReference( + reference: T[], + array: T[], + toSort: K[] +): K[] { + const sorted: K[] = new Array(); + for (let i = 0; i < reference.length; i++) { + const index = array.indexOf(reference[i]); + sorted.push(toSort[index]); + } + return sorted; +} + +// sortBytesArray will sort an array of Bytes in ascending order +// by comparing their hex string representation. +export function sortBytesArray(array: Bytes[]): Bytes[] { + const toSort = array.map((item) => item.toHexString()); + toSort.sort(); + return toSort.map((item) => Bytes.fromHexString(item)); +} + +export function updateArrayAtIndex(x: T[], item: T, index: i32): T[] { + if (x.length == 0) { + return [item]; + } + if (index == -1 || index > x.length) { + index = x.length; + } + const retval = new Array(); + let i = 0; + while (i < index) { + retval.push(x[i]); + i += 1; + } + retval.push(item); + i += 1; + while (i < x.length) { + retval.push(x[i]); + i += 1; + } + return retval; +} + +export function addToArrayAtIndex(x: T[], item: T, index: i32 = -1): T[] { + if (x.length == 0) { + return [item]; + } + if (index == -1 || index > x.length) { + index = x.length; + } + const retval = new Array(); + let i = 0; + while (i < index) { + retval.push(x[i]); + i += 1; + } + retval.push(item); + while (i < x.length) { + retval.push(x[i]); + i += 1; + } + return retval; +} + +export function addArrays(a: T[], b: T[]): T[] { + const retval = new Array(); + const arraysByLength = a.length <= b.length ? [a, b] : [b, a]; + + let i = 0; + while (i < arraysByLength[0].length) { + retval.push(a[i].plus(b[i])); + i += 1; + } + while (i < arraysByLength[1].length) { + retval.push(arraysByLength[1][i]); + i += 1; + } + return retval; +} + +export function subtractArrays(a: T[], b: T[]): T[] { + const retval = new Array(); + const arraysByLength = a.length <= b.length ? [a, b] : [b, a]; + + let i = 0; + while (i < arraysByLength[0].length) { + retval.push(a[i].minus(b[i])); + i += 1; + } + while (i < arraysByLength[1].length) { + retval.push(arraysByLength[1][i]); + i += 1; + } + return retval; +} diff --git a/subgraphs/usual/src/sdk/util/constants.ts b/subgraphs/usual/src/sdk/util/constants.ts new file mode 100644 index 0000000000..1cdf19dd52 --- /dev/null +++ b/subgraphs/usual/src/sdk/util/constants.ts @@ -0,0 +1,235 @@ +import { BigDecimal, BigInt } from "@graphprotocol/graph-ts"; + +//////////////////////// +///// Schema Enums ///// +//////////////////////// + +// The network names corresponding to the Network enum in the schema. +// They also correspond to the ones in `dataSource.network()` after converting to lower case. +// See below for a complete list: +// https://thegraph.com/docs/en/hosted-service/what-is-hosted-service/#supported-networks-on-the-hosted-service +export namespace Network { + export const ARBITRUM_ONE = "ARBITRUM_ONE"; + export const AVALANCHE = "AVALANCHE"; + export const AURORA = "AURORA"; + export const BASE = "BASE"; + export const BSC = "BSC"; // aka BNB Chain + export const CELO = "CELO"; + export const MAINNET = "MAINNET"; // Ethereum mainnet + export const FANTOM = "FANTOM"; + export const FUSE = "FUSE"; + export const MOONBEAM = "MOONBEAM"; + export const MOONRIVER = "MOONRIVER"; + export const NEAR_MAINNET = "NEAR_MAINNET"; + export const OPTIMISM = "OPTIMISM"; + export const MATIC = "MATIC"; // aka Polygon + export const XDAI = "XDAI"; // aka Gnosis Chain + + // other networks + export const UBIQ = "UBIQ"; + export const SONGBIRD = "SONGBIRD"; + export const ELASTOS = "ELASTOS"; + export const KARDIACHAIN = "KARDIACHAIN"; + export const CRONOS = "CRONOS"; + export const RSK = "RSK"; + export const TELOS = "TELOS"; + export const XDC = "XDC"; + export const ZYX = "ZYX"; + export const CSC = "CSC"; + export const SYSCOIN = "SYSCOIN"; + export const GOCHAIN = "GOCHAIN"; + export const ETHEREUMCLASSIC = "ETHEREUMCLASSIC"; + export const OKEXCHAIN = "OKEXCHAIN"; + export const HOO = "HOO"; + export const METER = "METER"; + export const NOVA_NETWORK = "NOVA_NETWORK"; + export const TOMOCHAIN = "TOMOCHAIN"; + export const VELAS = "VELAS"; + export const THUNDERCORE = "THUNDERCORE"; + export const HECO = "HECO"; + export const XDAIARB = "XDAIARB"; + export const ENERGYWEB = "ENERGYWEB"; + export const HPB = "HPB"; + export const BOBA = "BOBA"; + export const KUCOIN = "KUCOIN"; + export const SHIDEN = "SHIDEN"; + export const THETA = "THETA"; + export const SX = "SX"; + export const CANDLE = "CANDLE"; + export const ASTAR = "ASTAR"; + export const CALLISTO = "CALLISTO"; + export const WANCHAIN = "WANCHAIN"; + export const METIS = "METIS"; + export const ULTRON = "ULTRON"; + export const STEP = "STEP"; + export const DOGECHAIN = "DOGECHAIN"; + export const RONIN = "RONIN"; + export const KAVA = "KAVA"; + export const IOTEX = "IOTEX"; + export const XLC = "XLC"; + export const NAHMII = "NAHMII"; + export const TOMBCHAIN = "TOMBCHAIN"; + export const CANTO = "CANTO"; + export const KLAYTN = "KLAYTN"; + export const EVMOS = "EVMOS"; + export const SMARTBCH = "SMARTBCH"; + export const BITGERT = "BITGERT"; + export const FUSION = "FUSION"; + export const OHO = "OHO"; + export const ARB_NOVA = "ARB_NOVA"; + export const OASIS = "OASIS"; + export const REI = "REI"; + export const REICHAIN = "REICHAIN"; + export const GODWOKEN = "GODWOKEN"; + export const POLIS = "POLIS"; + export const KEKCHAIN = "KEKCHAIN"; + export const VISION = "VISION"; + export const HARMONY = "HARMONY"; + export const PALM = "PALM"; + export const CURIO = "CURIO"; + + export const UNKNOWN_NETWORK = "UNKNOWN_NETWORK"; +} +export type Network = string; + +export namespace ProtocolType { + export const EXCHANGE = "EXCHANGE"; + export const LENDING = "LENDING"; + export const YIELD = "YIELD"; + export const BRIDGE = "BRIDGE"; + export const OPTION = "OPTION"; + export const PERPETUAL = "PERPETUAL"; + export const GENERIC = "GENERIC"; +} + +export namespace VaultFeeType { + export const MANAGEMENT_FEE = "MANAGEMENT_FEE"; + export const PERFORMANCE_FEE = "PERFORMANCE_FEE"; + export const DEPOSIT_FEE = "DEPOSIT_FEE"; + export const WITHDRAWAL_FEE = "WITHDRAWAL_FEE"; +} + +export namespace LiquidityPoolFeeType { + export const FIXED_TRADING_FEE = "FIXED_TRADING_FEE"; + export const TIERED_TRADING_FEE = "TIERED_TRADING_FEE"; + export const DYNAMIC_TRADING_FEE = "DYNAMIC_TRADING_FEE"; + export const FIXED_LP_FEE = "FIXED_LP_FEE"; + export const DYNAMIC_LP_FEE = "DYNAMIC_LP_FEE"; + export const FIXED_PROTOCOL_FEE = "FIXED_PROTOCOL_FEE"; + export const DYNAMIC_PROTOCOL_FEE = "DYNAMIC_PROTOCOL_FEE"; +} +export type LiquidityPoolFeeType = string; + +export namespace RewardTokenType { + export const DEPOSIT = "DEPOSIT"; + export const BORROW = "BORROW"; + export const STAKE = "STAKE"; +} +export type RewardTokenType = string; + +export namespace ActivityInterval { + export const HOURLY = "HOURLY"; + export const DAILY = "DAILY"; +} + +export namespace LendingType { + export const CDP = "CDP"; + export const POOLED = "POOLED"; +} + +export namespace RiskType { + export const GLOBAL = "GLOBAL"; + export const ISOLATED = "ISOLATED"; +} + +export namespace InterestRateType { + export const STABLE = "STABLE"; + export const VARIABLE = "VARIABLE"; + export const FIXED_TERM = "FIXED_TERM"; +} + +export namespace InterestRateSide { + export const LENDER = "LENDER"; + export const BORROWER = "BORROWER"; +} + +export namespace UsageType { + export const DEPOSIT = "DEPOSIT"; + export const WITHDRAW = "WITHDRAW"; + export const SWAP = "SWAP"; +} + +export namespace PositionSide { + export const LONG = "LONG"; + export const SHORT = "SHORT"; +} +export type PositionSide = string; + +////////////////////////////// +///// Ethereum Addresses ///// +////////////////////////////// + +export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"; +export const ETH_ADDRESS = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + +//////////////////////// +///// Type Helpers ///// +//////////////////////// + +export const DEFAULT_DECIMALS = 18; + +export const USDC_DECIMALS = 6; +export const USDC_DENOMINATOR = BigDecimal.fromString("1000000"); + +export const BIGINT_ZERO = BigInt.fromI32(0); +export const BIGINT_ONE = BigInt.fromI32(1); +export const BIGINT_TWO = BigInt.fromI32(2); +export const BIGINT_NINE = BigInt.fromI32(9); +export const BIGINT_TEN = BigInt.fromI32(10); +export const BIGINT_HUNDRED = BigInt.fromI32(100); +export const BIGINT_THOUSAND = BigInt.fromI32(1000); +export const BIGINT_TEN_TO_EIGHTEENTH = BigInt.fromString("10").pow(18); +export const BIGINT_MINUS_ONE = BigInt.fromI32(-1); +export const BIGINT_MAX = BigInt.fromString( + "115792089237316195423570985008687907853269984665640564039457584007913129639935" +); + +export const INT_NEGATIVE_ONE = -1 as i32; +export const INT_ZERO = 0 as i32; +export const INT_ONE = 1 as i32; +export const INT_TWO = 2 as i32; +export const INT_FOUR = 4 as i32; + +export const BIGDECIMAL_ZERO = new BigDecimal(BIGINT_ZERO); +export const BIGDECIMAL_ONE = new BigDecimal(BIGINT_ONE); +export const BIGDECIMAL_TWO = new BigDecimal(BIGINT_TWO); +export const BIGDECIMAL_TEN = new BigDecimal(BIGINT_TEN); +export const BIGDECIMAL_HUNDRED = new BigDecimal(BIGINT_HUNDRED); +export const BIGDECIMAL_THOUSAND = new BigDecimal(BIGINT_THOUSAND); +export const BIGDECIMAL_MINUS_ONE = new BigDecimal(BIGINT_MINUS_ONE); + +export const MAX_UINT = BigInt.fromI32(2).times(BigInt.fromI32(255)); +export const QI92 = BigDecimal.fromString( + "6277101735386680763835789423207666416102355444464034512896" +); // 2 ** 192 + +///////////////////// +///// Date/Time ///// +///////////////////// + +export const SECONDS_PER_DAY = 60 * 60 * 24; // 86400 +export const SECONDS_PER_HOUR = 60 * 60; // 3600 +export const SECONDS_PER_DAY_BI = BigInt.fromI32(SECONDS_PER_DAY); +export const SECONDS_PER_HOUR_BI = BigInt.fromI32(SECONDS_PER_HOUR); +export const MS_PER_DAY = new BigDecimal(BigInt.fromI32(24 * 60 * 60 * 1000)); +export const DAYS_PER_YEAR = new BigDecimal(BigInt.fromI32(365)); +export const MS_PER_YEAR = DAYS_PER_YEAR.times( + new BigDecimal(BigInt.fromI32(24 * 60 * 60 * 1000)) +); + +//////////////// +///// Misc ///// +//////////////// + +export const ETH_SYMBOL = "ETH"; +export const ETH_NAME = "Ether"; diff --git a/subgraphs/usual/src/sdk/util/events.ts b/subgraphs/usual/src/sdk/util/events.ts new file mode 100644 index 0000000000..5c6ee1e4d1 --- /dev/null +++ b/subgraphs/usual/src/sdk/util/events.ts @@ -0,0 +1,70 @@ +import { BigInt, Address, Bytes, ethereum } from "@graphprotocol/graph-ts"; +import { + BIGINT_ZERO, + SECONDS_PER_DAY, + SECONDS_PER_HOUR, + ZERO_ADDRESS, +} from "./constants"; + +export class CustomEventType { + block: ethereum.Block; + transaction: ethereum.Transaction; + logIndex: BigInt; + event: ethereum.Event | null; + + constructor() { + this.block = new ethereum.Block( + Bytes.empty(), + Bytes.empty(), + Bytes.empty(), + Address.fromString(ZERO_ADDRESS), + Bytes.empty(), + Bytes.empty(), + Bytes.empty(), + BIGINT_ZERO, + BIGINT_ZERO, + BIGINT_ZERO, + BIGINT_ZERO, + BIGINT_ZERO, + BIGINT_ZERO, + null, + null + ); + this.transaction = new ethereum.Transaction( + Bytes.empty(), + BIGINT_ZERO, + Address.fromString(ZERO_ADDRESS), + null, + BIGINT_ZERO, + BIGINT_ZERO, + BIGINT_ZERO, + Bytes.empty(), + BIGINT_ZERO + ); + this.logIndex = BIGINT_ZERO; + this.event = null; + } + + static initialize( + block: ethereum.Block, + transaction: ethereum.Transaction, + logIndex: BigInt, + event: ethereum.Event | null = null + ): CustomEventType { + const customEvent = new CustomEventType(); + customEvent.block = block; + customEvent.transaction = transaction; + customEvent.logIndex = logIndex; + customEvent.event = event; + + return customEvent; + } +} + +export function getUnixDays(block: ethereum.Block): i32 { + return block.timestamp.toI32() / SECONDS_PER_DAY; +} + +export function getUnixHours(block: ethereum.Block): i32 { + return block.timestamp.toI32() / SECONDS_PER_HOUR; +} diff --git a/subgraphs/usual/src/sdk/util/numbers.ts b/subgraphs/usual/src/sdk/util/numbers.ts new file mode 100644 index 0000000000..725140a922 --- /dev/null +++ b/subgraphs/usual/src/sdk/util/numbers.ts @@ -0,0 +1,61 @@ +import { BigDecimal, BigInt } from "@graphprotocol/graph-ts"; +import { + BIGDECIMAL_ZERO, + BIGINT_TEN, + DEFAULT_DECIMALS, + INT_TWO, +} from "./constants"; + +export function bigIntToBigDecimal( + quantity: BigInt, + decimals: i32 = DEFAULT_DECIMALS +): BigDecimal { + return quantity.divDecimal(BIGINT_TEN.pow(decimals as u8).toBigDecimal()); +} + +export function bigDecimalToBigInt(input: BigDecimal): BigInt { + const str = input.truncate(0).toString(); + return BigInt.fromString(str); +} + +// returns 10^exp +export function exponentToBigDecimal(exp: i32 = DEFAULT_DECIMALS): BigDecimal { + let bd = BigDecimal.fromString("1"); + const ten = BigDecimal.fromString("10"); + for (let i = 0; i < exp; i++) { + bd = bd.times(ten); + } + return bd; +} + +export function calculateAverage(prices: BigDecimal[]): BigDecimal { + let sum = BigDecimal.fromString("0"); + for (let i = 0; i < prices.length; i++) { + sum = sum.plus(prices[i]); + } + + return sum.div( + BigDecimal.fromString(BigInt.fromI32(prices.length).toString()) + ); +} + +export function calculateMedian(prices: BigDecimal[]): BigDecimal { + const sorted = prices.sort((a, b) => { + return a.equals(b) ? 0 : a.gt(b) ? 1 : -1; + }); + + const mid = Math.ceil(sorted.length / 2) as i32; + if (sorted.length % 2 == 0) { + return sorted[mid] + .plus(sorted[mid - 1]) + .div(BigDecimal.fromString(INT_TWO.toString())); + } + + return sorted[mid - 1]; +} + +export function safeDivide(a: BigDecimal, b: BigDecimal): BigDecimal { + if (b == BIGDECIMAL_ZERO) return BIGDECIMAL_ZERO; + + return a.div(b); +} diff --git a/subgraphs/usual/src/sdk/util/rewards.ts b/subgraphs/usual/src/sdk/util/rewards.ts new file mode 100644 index 0000000000..ad68987205 --- /dev/null +++ b/subgraphs/usual/src/sdk/util/rewards.ts @@ -0,0 +1,295 @@ +///////////////////// +// VERSION 1.0.3 //// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// The purpose of this program is to dynamically estimate the blocks generated for the 24 HR period following the most recent update. // +// It does so by calculating the moving average block rate for an arbitrary length of time preceding the current block. // +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +import { log, BigDecimal, BigInt, dataSource } from "@graphprotocol/graph-ts"; +import { _CircularBuffer } from "../../../generated/schema"; +import { + Network, + BIGDECIMAL_ZERO, + INT_FOUR, + INT_NEGATIVE_ONE, + INT_ONE, + INT_TWO, + INT_ZERO, +} from "./constants"; + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// WINDOW_SIZE_SECONDS, TIMESTAMP_STORAGE_INTERVALS, and BUFFER_SIZE can be modified. These are just recommended values - 'somewhat' arbitrary. // +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// The storage interval tells you to only store blocks where the timestamps are separated by at least this amount. +// Increasing this value will mean less blocks stored and less frequently computes blocksSpeed. +export const TIMESTAMP_STORAGE_INTERVAL = 600; + +// The window size determines the range of blocks that you track from the current block minus the window size. +// Window of block time used to calculate the moving average. +// Increasing means less deviation but also less sensitivity to changing block speeds. +export const WINDOW_SIZE_SECONDS = 86400; + +// BUFFER_SIZE determined the size of the array +// Makes the buffer the maximum amount of blocks that can be stored given the block rate and storage interval +// Recommended value is (RATE_IN_SECODNDS / TIMESTAMP_STORAGE_INTERVAL) * 2 - > Round up to nearest even integer +export const BUFFER_SIZE = 288; + +// Add this entity to the schema. +// type _CircularBuffer @entity { +// " 'CIRCULAR_BUFFER' " +// id: ID! + +// " Array of sorted block numbers sorted continuously " +// blocks: [Int!]! + +// " The index in the blocks array which will be used with the newest block to calculate block speed (Usally set to about a day before newest block) " +// windowStartIndex: Int! + +// " The next index in the blocks array that will be replaced with the newest block " +// nextIndex: Int! + +// " This determines the size of the blocks array. Should be set to contain at least a days worth of blocks according to a 1 day window for measuring speed" +// bufferSize: Int! + +// " The current calculated number of blocks per day based on calculated block speed " +// blocksPerDay: BigDecimal! + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +export const CIRCULAR_BUFFER = "CIRCULAR_BUFFER"; + +// Describes whether the interval for which rewards are emitted is done by block or timestamp +export namespace RewardIntervalType { + export const BLOCK = "BLOCK"; + export const TIMESTAMP = "TIMESTAMP"; +} + +// Forecast period. This gives you the time period that you want to estimate count of blocks per interval, based on moving average block speed. +// 86400 = 1 Day +export const RATE_IN_SECONDS = 86400; +export const RATE_IN_SECONDS_BD = BigDecimal.fromString( + RATE_IN_SECONDS.toString() +); + +// Estimated seconds per block of the protocol +export const STARTING_BLOCKS_PER_DAY = RATE_IN_SECONDS_BD.div( + getStartingBlockRate() +); + +export const WINDOW_SIZE_SECONDS_BD = BigDecimal.fromString( + WINDOW_SIZE_SECONDS.toString() +); + +// Call this function in event handlers frequently enough so that it calls on blocks frequently enough +/** + * @param {BigInt} currentTimestamp - Timestamp for current event + * @param {BigInt} currentBlockNumber - Block nunmber of current event + * @param {BigInt} rewardRate - Rate of reward emissions per reward interval + * @param {BigInt} rewardType - Describes whether rewards are given per block or timestamp + * @returns {BigDecimal} - Returns estimated blocks for specified rate + */ +export function getRewardsPerDay( + currentTimestamp: BigInt, + currentBlockNumber: BigInt, + rewardRate: BigDecimal, + rewardType: string +): BigDecimal { + const circularBuffer = getOrCreateCircularBuffer(); + + // Create entity for the current block + const currentTimestampI32 = currentTimestamp.toI32(); + const currentBlockNumberI32 = currentBlockNumber.toI32(); + + const blocks = circularBuffer.blocks; + + // Interval between index and the index of the start of the window block + const windowWidth = abs( + circularBuffer.windowStartIndex - circularBuffer.nextIndex + ); + if (windowWidth == INT_ZERO) { + if (circularBuffer.nextIndex >= circularBuffer.bufferSize) { + blocks[INT_ZERO] = currentTimestampI32; + blocks[INT_ONE] = currentBlockNumberI32; + circularBuffer.nextIndex = INT_TWO; + } else { + blocks[circularBuffer.nextIndex] = currentTimestampI32; + blocks[circularBuffer.nextIndex + INT_ONE] = currentBlockNumberI32; + circularBuffer.nextIndex += INT_TWO; + } + + circularBuffer.save(); + + // return because there is only 1 reference point. + if (rewardType == RewardIntervalType.TIMESTAMP) { + return rewardRate.times(RATE_IN_SECONDS_BD); + } else { + return circularBuffer.blocksPerDay.times(rewardRate); + } + } + + // Add current timestamp and block numnber to array if new block is at least X blocks later than previously stored. + // Used to save memory and efficiency on array resizing. + let recentSavedTimestamp: i32; + if (circularBuffer.nextIndex == INT_ZERO) { + recentSavedTimestamp = blocks[circularBuffer.bufferSize - INT_TWO]; + } else { + recentSavedTimestamp = blocks[circularBuffer.nextIndex - INT_TWO]; + } + + if ( + currentTimestampI32 - recentSavedTimestamp <= + TIMESTAMP_STORAGE_INTERVAL + ) { + if (rewardType == RewardIntervalType.TIMESTAMP) { + return rewardRate.times(RATE_IN_SECONDS_BD); + } else { + return circularBuffer.blocksPerDay.times(rewardRate); + } + } + + blocks[circularBuffer.nextIndex] = currentTimestampI32; + blocks[circularBuffer.nextIndex + INT_ONE] = currentBlockNumberI32; + if (circularBuffer.nextIndex >= BUFFER_SIZE - INT_TWO) { + circularBuffer.nextIndex = INT_ZERO; + } else { + circularBuffer.nextIndex += INT_TWO; + } + // The timestamp at the start of the window (default 24 hours in seconds). + const startTimestamp = currentTimestampI32 - WINDOW_SIZE_SECONDS; + + // Make sure to still have 2 blocks to calculate rate (This shouldn't happen past the beginning). + while (true) { + if (circularBuffer.nextIndex > circularBuffer.windowStartIndex) { + if ( + circularBuffer.nextIndex - circularBuffer.windowStartIndex <= + INT_FOUR + ) { + break; + } + } else { + if ( + BUFFER_SIZE - + circularBuffer.windowStartIndex + + circularBuffer.nextIndex <= + INT_FOUR + ) { + break; + } + } + const windowIndexBlockTimestamp = blocks[circularBuffer.windowStartIndex]; + + // Shift the start of the window if the current timestamp moves out of desired rate window + if (windowIndexBlockTimestamp < startTimestamp) { + circularBuffer.windowStartIndex = + circularBuffer.windowStartIndex + INT_TWO; + if (circularBuffer.windowStartIndex >= circularBuffer.bufferSize) { + circularBuffer.windowStartIndex = INT_ZERO; + } + } else { + break; + } + } + + // Wideness of the window in seconds. + const windowSecondsCount = BigDecimal.fromString( + (currentTimestampI32 - blocks[circularBuffer.windowStartIndex]).toString() + ); + + // Wideness of the window in blocks. + const windowBlocksCount = BigDecimal.fromString( + ( + currentBlockNumberI32 - blocks[circularBuffer.windowStartIndex + INT_ONE] + ).toString() + ); + + // Estimate block speed for the window in seconds. + const unnormalizedBlockSpeed = + WINDOW_SIZE_SECONDS_BD.div(windowSecondsCount).times(windowBlocksCount); + + // block speed converted to specified rate. + const normalizedBlockSpeed = RATE_IN_SECONDS_BD.div( + WINDOW_SIZE_SECONDS_BD + ).times(unnormalizedBlockSpeed); + + // Update BlockTracker with new values. + circularBuffer.blocksPerDay = normalizedBlockSpeed; + circularBuffer.blocks = blocks; + + circularBuffer.save(); + + if (rewardType == RewardIntervalType.TIMESTAMP) { + return rewardRate.times(RATE_IN_SECONDS_BD); + } else { + return rewardRate.times(circularBuffer.blocksPerDay); + } +} + +function getOrCreateCircularBuffer(): _CircularBuffer { + let circularBuffer = _CircularBuffer.load(CIRCULAR_BUFFER); + + if (!circularBuffer) { + circularBuffer = new _CircularBuffer(CIRCULAR_BUFFER); + + const blocks = new Array(BUFFER_SIZE); + for (let i = INT_ZERO; i < BUFFER_SIZE; i += INT_TWO) { + blocks[i] = INT_NEGATIVE_ONE; + blocks[i + INT_ONE] = INT_NEGATIVE_ONE; + } + + circularBuffer.blocks = blocks; + circularBuffer.windowStartIndex = INT_ZERO; + circularBuffer.nextIndex = INT_ZERO; + circularBuffer.bufferSize = BUFFER_SIZE; + circularBuffer.blocksPerDay = STARTING_BLOCKS_PER_DAY; + + circularBuffer.save(); + } + + return circularBuffer; +} + +function getStartingBlockRate(): BigDecimal { + // Block rates pulled from google searches - rough estimates + + const network = dataSource.network().toUpperCase().replace("-", "_"); + if (network == Network.MAINNET) { + return BigDecimal.fromString("13.39"); + } else if (network == Network.ARBITRUM_ONE) { + return BigDecimal.fromString("15"); + } else if (network == Network.AURORA) { + return BigDecimal.fromString("1.03"); + } else if (network == Network.BSC) { + return BigDecimal.fromString("5"); + } else if (network == Network.CELO) { + return BigDecimal.fromString("5"); + } else if (network == Network.FANTOM) { + return BigDecimal.fromString("1"); + } else if (network == Network.FUSE) { + return BigDecimal.fromString("1"); + } else if (network == Network.OPTIMISM) { + return BigDecimal.fromString("12.5"); + } else if (network == Network.MATIC) { + return BigDecimal.fromString("2"); + } else if (network == Network.XDAI) { + return BigDecimal.fromString("5"); + } else if (network == Network.MOONBEAM) { + return BigDecimal.fromString("13.39"); + } else if (network == Network.MOONRIVER) { + return BigDecimal.fromString("13.39"); + } else if (network == Network.AVALANCHE) { + return BigDecimal.fromString("13.39"); + } else if (network == Network.CRONOS) { + return BigDecimal.fromString("5.5"); + } else if (network == Network.BASE) { + // assuming same block rate as OPTIMISM + return BigDecimal.fromString("12.5"); + } + + // else if (network == SubgraphNetwork.AVALANCHE) return BigDecimal.fromString("2.5") + // else if (dataSource.network() == "harmony") return BigDecimal.fromString("13.39") + else { + log.warning("getStartingBlockRate(): Network not found", []); + return BIGDECIMAL_ZERO; + } +} diff --git a/subgraphs/usual/tsconfig.json b/subgraphs/usual/tsconfig.json new file mode 100644 index 0000000000..5c5d17c928 --- /dev/null +++ b/subgraphs/usual/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@graphprotocol/graph-ts/types/tsconfig.base.json", + "include": ["src"] +}