From 514b826e4cef4426408ee514b64cc9d1041d6b63 Mon Sep 17 00:00:00 2001 From: Chris Jacobs Date: Thu, 19 Oct 2023 18:21:20 -0700 Subject: [PATCH 01/12] - validation work - vault balances fix --- abi/otoken-vault.json | 638 ++++++++++++------ package-lock.json | 8 +- package.json | 2 + src/abi/otoken-vault.abi.ts | 517 +++++++------- src/abi/otoken-vault.ts | 181 ++--- src/main.ts | 3 +- .../exchange-rates/exchange-rates.ts | 2 +- .../validate-oeth/validate-oeth.ts | 76 ++- src/processors/oeth/vault.ts | 70 +- src/utils/jsonify.ts | 9 + 10 files changed, 920 insertions(+), 586 deletions(-) create mode 100644 src/utils/jsonify.ts diff --git a/abi/otoken-vault.json b/abi/otoken-vault.json index 972cc9f8..5c7d0550 100644 --- a/abi/otoken-vault.json +++ b/abi/otoken-vault.json @@ -190,7 +190,12 @@ "name": "PriceProviderUpdated", "type": "event" }, - { "anonymous": false, "inputs": [], "name": "RebasePaused", "type": "event" }, + { + "anonymous": false, + "inputs": [], + "name": "RebasePaused", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -281,6 +286,82 @@ "name": "StrategyRemoved", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_basis", + "type": "uint256" + } + ], + "name": "SwapAllowedUndervalueChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_basis", + "type": "uint256" + } + ], + "name": "SwapSlippageChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_fromAsset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_toAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_fromAssetAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_toAssetAmount", + "type": "uint256" + } + ], + "name": "Swapped", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "SwapperChanged", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -346,47 +427,57 @@ "type": "event" }, { - "inputs": [ - { "internalType": "address", "name": "_addr", "type": "address" } - ], - "name": "approveStrategy", + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "allocate", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { - "inputs": [{ "internalType": "address", "name": "", "type": "address" }], + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], "name": "assetDefaultStrategies", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "autoAllocateThreshold", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "address", "name": "_asset", "type": "address" } + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } ], - "name": "cacheDecimals", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "capitalPaused", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "claimGovernance", + "name": "burnForStrategy", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -394,22 +485,32 @@ { "inputs": [ { - "internalType": "address", - "name": "_strategyToAddress", - "type": "address" - }, - { "internalType": "address[]", "name": "_assets", "type": "address[]" }, - { "internalType": "uint256[]", "name": "_amounts", "type": "uint256[]" } + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } ], - "name": "depositToStrategy", - "outputs": [], - "stateMutability": "nonpayable", + "name": "calculateRedeemOutputs", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", "type": "function" }, { "inputs": [], - "name": "governor", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "capitalPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], "stateMutability": "view", "type": "function" }, @@ -417,69 +518,144 @@ "inputs": [ { "internalType": "address", - "name": "_priceProvider", + "name": "_asset", "type": "address" - }, - { "internalType": "address", "name": "_ousd", "type": "address" } + } ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", + "name": "checkBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", "type": "function" }, { "inputs": [], - "name": "isGovernor", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", + "name": "claimGovernance", + "outputs": [], + "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], - "name": "maxSupplyDiff", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "getAllAssets", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], "stateMutability": "view", "type": "function" }, { "inputs": [], - "name": "netOusdMintForStrategyThreshold", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "getAllStrategies", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], "stateMutability": "view", "type": "function" }, { - "inputs": [], - "name": "netOusdMintedForStrategy", - "outputs": [{ "internalType": "int256", "name": "", "type": "int256" }], + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + } + ], + "name": "getAssetConfig", + "outputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "isSupported", + "type": "bool" + }, + { + "internalType": "enum VaultStorage.UnitConversion", + "name": "unitConversion", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "allowedOracleSlippageBps", + "type": "uint16" + } + ], + "internalType": "struct VaultStorage.Asset", + "name": "config", + "type": "tuple" + } + ], "stateMutability": "view", "type": "function" }, { "inputs": [], - "name": "ousdMetaStrategy", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "getAssetCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], "stateMutability": "view", "type": "function" }, { "inputs": [], - "name": "pauseCapital", - "outputs": [], - "stateMutability": "nonpayable", + "name": "getStrategyCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", "type": "function" }, { "inputs": [], - "name": "pauseRebase", - "outputs": [], - "stateMutability": "nonpayable", + "name": "governor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", "type": "function" }, { "inputs": [], - "name": "priceProvider", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "isGovernor", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], "stateMutability": "view", "type": "function" }, @@ -487,170 +663,246 @@ "inputs": [ { "internalType": "address", - "name": "_strategyFromAddress", + "name": "_asset", "type": "address" - }, + } + ], + "name": "isSupportedAsset", + "outputs": [ { - "internalType": "address", - "name": "_strategyToAddress", - "type": "address" - }, - { "internalType": "address[]", "name": "_assets", "type": "address[]" }, - { "internalType": "uint256[]", "name": "_amounts", "type": "uint256[]" } + "internalType": "bool", + "name": "", + "type": "bool" + } ], - "name": "reallocate", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "rebasePaused", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "view", "type": "function" }, { "inputs": [], - "name": "rebaseThreshold", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "redeemFeeBps", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "maxSupplyDiff", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "address", "name": "_addr", "type": "address" } + { + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minimumOusdAmount", + "type": "uint256" + } ], - "name": "removeStrategy", + "name": "mint", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ - { "internalType": "address", "name": "newImpl", "type": "address" } + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } ], - "name": "setAdminImpl", + "name": "mintForStrategy", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { - "inputs": [ - { "internalType": "address", "name": "_asset", "type": "address" }, - { "internalType": "address", "name": "_strategy", "type": "address" } + "inputs": [], + "name": "netOusdMintForStrategyThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } ], - "name": "setAssetDefaultStrategy", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { - "inputs": [ - { "internalType": "uint256", "name": "_threshold", "type": "uint256" } + "inputs": [], + "name": "netOusdMintedForStrategy", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } ], - "name": "setAutoAllocateThreshold", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { - "inputs": [ - { "internalType": "uint256", "name": "_maxSupplyDiff", "type": "uint256" } + "inputs": [], + "name": "ousdMetaStrategy", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } ], - "name": "setMaxSupplyDiff", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { - "inputs": [ - { "internalType": "uint256", "name": "_threshold", "type": "uint256" } + "inputs": [], + "name": "priceProvider", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } ], - "name": "setNetOusdMintForStrategyThreshold", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", - "name": "_ousdMetaStrategy", + "name": "asset", "type": "address" } ], - "name": "setOusdMetaStrategy", - "outputs": [], - "stateMutability": "nonpayable", + "name": "priceUnitMint", + "outputs": [ + { + "internalType": "uint256", + "name": "price", + "type": "uint256" + } + ], + "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "address", "name": "_priceProvider", "type": "address" } + { + "internalType": "address", + "name": "asset", + "type": "address" + } ], - "name": "setPriceProvider", + "name": "priceUnitRedeem", + "outputs": [ + { + "internalType": "uint256", + "name": "price", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rebase", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { - "inputs": [ - { "internalType": "uint256", "name": "_threshold", "type": "uint256" } + "inputs": [], + "name": "rebasePaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } ], - "name": "setRebaseThreshold", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { - "inputs": [ - { "internalType": "uint256", "name": "_redeemFeeBps", "type": "uint256" } + "inputs": [], + "name": "rebaseThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } ], - "name": "setRedeemFeeBps", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "address", "name": "_address", "type": "address" } + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minimumUnitAmount", + "type": "uint256" + } ], - "name": "setStrategistAddr", + "name": "redeem", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ - { "internalType": "address", "name": "_address", "type": "address" } + { + "internalType": "uint256", + "name": "_minimumUnitAmount", + "type": "uint256" + } ], - "name": "setTrusteeAddress", + "name": "redeemAll", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { - "inputs": [ - { "internalType": "uint256", "name": "_basis", "type": "uint256" } + "inputs": [], + "name": "redeemFeeBps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } ], - "name": "setTrusteeFeeBps", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "uint256", "name": "_vaultBuffer", "type": "uint256" } + { + "internalType": "address", + "name": "newImpl", + "type": "address" + } ], - "name": "setVaultBuffer", + "name": "setAdminImpl", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -658,103 +910,79 @@ { "inputs": [], "name": "strategistAddr", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], "stateMutability": "view", "type": "function" }, { - "inputs": [ - { "internalType": "address", "name": "_asset", "type": "address" }, - { "internalType": "uint8", "name": "_unitConversion", "type": "uint8" } + "inputs": [], + "name": "totalValue", + "outputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } ], - "name": "supportAsset", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "address", "name": "_newGovernor", "type": "address" } + { + "internalType": "address", + "name": "_newGovernor", + "type": "address" + } ], "name": "transferGovernance", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { "internalType": "address", "name": "_asset", "type": "address" }, - { "internalType": "uint256", "name": "_amount", "type": "uint256" } - ], - "name": "transferToken", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [], "name": "trusteeAddress", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "trusteeFeeBps", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "unpauseCapital", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "unpauseRebase", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [], "name": "vaultBuffer", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "withdrawAllFromStrategies", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "_strategyAddr", "type": "address" } - ], - "name": "withdrawAllFromStrategy", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ + "outputs": [ { - "internalType": "address", - "name": "_strategyFromAddress", - "type": "address" - }, - { "internalType": "address[]", "name": "_assets", "type": "address[]" }, - { "internalType": "uint256[]", "name": "_amounts", "type": "uint256[]" } + "internalType": "uint256", + "name": "", + "type": "uint256" + } ], - "name": "withdrawFromStrategy", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" } -] +] \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 48b5dd45..76466b5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,9 +11,11 @@ "@subsquid/graphql-server": "^4.3.1", "@subsquid/typeorm-migration": "^1.2.2", "@subsquid/typeorm-store": "^1.2.4", + "@types/lodash": "^4.14.200", "dayjs": "^1.11.10", "dotenv": "^16.1.4", "ethers": "^6.5.1", + "lodash": "^4.17.21", "pg": "^8.11.0", "type-graphql": "^1.2.0-rc.1", "typeorm": "^0.3.16", @@ -1449,6 +1451,11 @@ "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==", "license": "MIT" }, + "node_modules/@types/lodash": { + "version": "4.14.200", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.200.tgz", + "integrity": "sha512-YI/M/4HRImtNf3pJgbF+W6FrXovqj+T+/HpENLTooK9PnkacBsDpeP3IpHab40CClUfhNmdM2WTNP2sa2dni5Q==" + }, "node_modules/@types/long": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", @@ -3479,7 +3486,6 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, "license": "MIT" }, "node_modules/lodash.defaults": { diff --git a/package.json b/package.json index 45b555ef..81099fe9 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,11 @@ "@subsquid/graphql-server": "^4.3.1", "@subsquid/typeorm-migration": "^1.2.2", "@subsquid/typeorm-store": "^1.2.4", + "@types/lodash": "^4.14.200", "dayjs": "^1.11.10", "dotenv": "^16.1.4", "ethers": "^6.5.1", + "lodash": "^4.17.21", "pg": "^8.11.0", "type-graphql": "^1.2.0-rc.1", "typeorm": "^0.3.16", diff --git a/src/abi/otoken-vault.abi.ts b/src/abi/otoken-vault.abi.ts index 66a4baac..0250f5c8 100644 --- a/src/abi/otoken-vault.abi.ts +++ b/src/abi/otoken-vault.abi.ts @@ -262,6 +262,74 @@ export const ABI_JSON = [ } ] }, + { + "type": "event", + "anonymous": false, + "name": "SwapAllowedUndervalueChanged", + "inputs": [ + { + "type": "uint256", + "name": "_basis", + "indexed": false + } + ] + }, + { + "type": "event", + "anonymous": false, + "name": "SwapSlippageChanged", + "inputs": [ + { + "type": "address", + "name": "_asset", + "indexed": false + }, + { + "type": "uint256", + "name": "_basis", + "indexed": false + } + ] + }, + { + "type": "event", + "anonymous": false, + "name": "Swapped", + "inputs": [ + { + "type": "address", + "name": "_fromAsset", + "indexed": true + }, + { + "type": "address", + "name": "_toAsset", + "indexed": true + }, + { + "type": "uint256", + "name": "_fromAssetAmount", + "indexed": false + }, + { + "type": "uint256", + "name": "_toAssetAmount", + "indexed": false + } + ] + }, + { + "type": "event", + "anonymous": false, + "name": "SwapperChanged", + "inputs": [ + { + "type": "address", + "name": "_address", + "indexed": false + } + ] + }, { "type": "event", "anonymous": false, @@ -320,17 +388,16 @@ export const ABI_JSON = [ } ] }, + { + "type": "fallback", + "stateMutability": "payable" + }, { "type": "function", - "name": "approveStrategy", + "name": "allocate", "constant": false, "payable": false, - "inputs": [ - { - "type": "address", - "name": "_addr" - } - ], + "inputs": [], "outputs": [] }, { @@ -368,17 +435,36 @@ export const ABI_JSON = [ }, { "type": "function", - "name": "cacheDecimals", + "name": "burnForStrategy", "constant": false, "payable": false, "inputs": [ { - "type": "address", - "name": "_asset" + "type": "uint256", + "name": "_amount" } ], "outputs": [] }, + { + "type": "function", + "name": "calculateRedeemOutputs", + "constant": true, + "stateMutability": "view", + "payable": false, + "inputs": [ + { + "type": "uint256", + "name": "_amount" + } + ], + "outputs": [ + { + "type": "uint256[]", + "name": "" + } + ] + }, { "type": "function", "name": "capitalPaused", @@ -393,6 +479,25 @@ export const ABI_JSON = [ } ] }, + { + "type": "function", + "name": "checkBalance", + "constant": true, + "stateMutability": "view", + "payable": false, + "inputs": [ + { + "type": "address", + "name": "_asset" + } + ], + "outputs": [ + { + "type": "uint256", + "name": "" + } + ] + }, { "type": "function", "name": "claimGovernance", @@ -403,73 +508,86 @@ export const ABI_JSON = [ }, { "type": "function", - "name": "depositToStrategy", - "constant": false, + "name": "getAllAssets", + "constant": true, + "stateMutability": "view", "payable": false, - "inputs": [ - { - "type": "address", - "name": "_strategyToAddress" - }, + "inputs": [], + "outputs": [ { "type": "address[]", - "name": "_assets" - }, - { - "type": "uint256[]", - "name": "_amounts" + "name": "" } - ], - "outputs": [] + ] }, { "type": "function", - "name": "governor", + "name": "getAllStrategies", "constant": true, "stateMutability": "view", "payable": false, "inputs": [], "outputs": [ { - "type": "address", + "type": "address[]", "name": "" } ] }, { "type": "function", - "name": "initialize", - "constant": false, + "name": "getAssetConfig", + "constant": true, + "stateMutability": "view", "payable": false, "inputs": [ { "type": "address", - "name": "_priceProvider" - }, - { - "type": "address", - "name": "_ousd" + "name": "_asset" } ], - "outputs": [] + "outputs": [ + { + "type": "tuple", + "name": "config", + "components": [ + { + "type": "bool", + "name": "isSupported" + }, + { + "type": "uint8", + "name": "unitConversion" + }, + { + "type": "uint8", + "name": "decimals" + }, + { + "type": "uint16", + "name": "allowedOracleSlippageBps" + } + ] + } + ] }, { "type": "function", - "name": "isGovernor", + "name": "getAssetCount", "constant": true, "stateMutability": "view", "payable": false, "inputs": [], "outputs": [ { - "type": "bool", + "type": "uint256", "name": "" } ] }, { "type": "function", - "name": "maxSupplyDiff", + "name": "getStrategyCount", "constant": true, "stateMutability": "view", "payable": false, @@ -483,118 +601,102 @@ export const ABI_JSON = [ }, { "type": "function", - "name": "netOusdMintForStrategyThreshold", + "name": "governor", "constant": true, "stateMutability": "view", "payable": false, "inputs": [], "outputs": [ { - "type": "uint256", + "type": "address", "name": "" } ] }, { "type": "function", - "name": "netOusdMintedForStrategy", + "name": "isGovernor", "constant": true, "stateMutability": "view", "payable": false, "inputs": [], "outputs": [ { - "type": "int256", + "type": "bool", "name": "" } ] }, { "type": "function", - "name": "ousdMetaStrategy", + "name": "isSupportedAsset", "constant": true, "stateMutability": "view", "payable": false, - "inputs": [], - "outputs": [ + "inputs": [ { "type": "address", + "name": "_asset" + } + ], + "outputs": [ + { + "type": "bool", "name": "" } ] }, { "type": "function", - "name": "pauseCapital", - "constant": false, - "payable": false, - "inputs": [], - "outputs": [] - }, - { - "type": "function", - "name": "pauseRebase", - "constant": false, - "payable": false, - "inputs": [], - "outputs": [] - }, - { - "type": "function", - "name": "priceProvider", + "name": "maxSupplyDiff", "constant": true, "stateMutability": "view", "payable": false, "inputs": [], "outputs": [ { - "type": "address", + "type": "uint256", "name": "" } ] }, { "type": "function", - "name": "reallocate", + "name": "mint", "constant": false, "payable": false, "inputs": [ { "type": "address", - "name": "_strategyFromAddress" - }, - { - "type": "address", - "name": "_strategyToAddress" + "name": "_asset" }, { - "type": "address[]", - "name": "_assets" + "type": "uint256", + "name": "_amount" }, { - "type": "uint256[]", - "name": "_amounts" + "type": "uint256", + "name": "_minimumOusdAmount" } ], "outputs": [] }, { "type": "function", - "name": "rebasePaused", - "constant": true, - "stateMutability": "view", + "name": "mintForStrategy", + "constant": false, "payable": false, - "inputs": [], - "outputs": [ + "inputs": [ { - "type": "bool", - "name": "" + "type": "uint256", + "name": "_amount" } - ] + ], + "outputs": [] }, { "type": "function", - "name": "rebaseThreshold", + "name": "netOusdMintForStrategyThreshold", "constant": true, "stateMutability": "view", "payable": false, @@ -608,200 +710,173 @@ export const ABI_JSON = [ }, { "type": "function", - "name": "redeemFeeBps", + "name": "netOusdMintedForStrategy", "constant": true, "stateMutability": "view", "payable": false, "inputs": [], "outputs": [ { - "type": "uint256", + "type": "int256", "name": "" } ] }, { "type": "function", - "name": "removeStrategy", - "constant": false, + "name": "ousdMetaStrategy", + "constant": true, + "stateMutability": "view", "payable": false, - "inputs": [ + "inputs": [], + "outputs": [ { "type": "address", - "name": "_addr" + "name": "" } - ], - "outputs": [] + ] }, { "type": "function", - "name": "setAdminImpl", - "constant": false, + "name": "priceProvider", + "constant": true, + "stateMutability": "view", "payable": false, - "inputs": [ + "inputs": [], + "outputs": [ { "type": "address", - "name": "newImpl" + "name": "" } - ], - "outputs": [] + ] }, { "type": "function", - "name": "setAssetDefaultStrategy", - "constant": false, + "name": "priceUnitMint", + "constant": true, + "stateMutability": "view", "payable": false, "inputs": [ { "type": "address", - "name": "_asset" - }, - { - "type": "address", - "name": "_strategy" + "name": "asset" } ], - "outputs": [] - }, - { - "type": "function", - "name": "setAutoAllocateThreshold", - "constant": false, - "payable": false, - "inputs": [ + "outputs": [ { "type": "uint256", - "name": "_threshold" + "name": "price" } - ], - "outputs": [] + ] }, { "type": "function", - "name": "setMaxSupplyDiff", - "constant": false, + "name": "priceUnitRedeem", + "constant": true, + "stateMutability": "view", "payable": false, "inputs": [ { - "type": "uint256", - "name": "_maxSupplyDiff" + "type": "address", + "name": "asset" } ], - "outputs": [] - }, - { - "type": "function", - "name": "setNetOusdMintForStrategyThreshold", - "constant": false, - "payable": false, - "inputs": [ + "outputs": [ { "type": "uint256", - "name": "_threshold" + "name": "price" } - ], - "outputs": [] + ] }, { "type": "function", - "name": "setOusdMetaStrategy", + "name": "rebase", "constant": false, "payable": false, - "inputs": [ - { - "type": "address", - "name": "_ousdMetaStrategy" - } - ], + "inputs": [], "outputs": [] }, { "type": "function", - "name": "setPriceProvider", - "constant": false, + "name": "rebasePaused", + "constant": true, + "stateMutability": "view", "payable": false, - "inputs": [ + "inputs": [], + "outputs": [ { - "type": "address", - "name": "_priceProvider" + "type": "bool", + "name": "" } - ], - "outputs": [] + ] }, { "type": "function", - "name": "setRebaseThreshold", - "constant": false, + "name": "rebaseThreshold", + "constant": true, + "stateMutability": "view", "payable": false, - "inputs": [ + "inputs": [], + "outputs": [ { "type": "uint256", - "name": "_threshold" + "name": "" } - ], - "outputs": [] + ] }, { "type": "function", - "name": "setRedeemFeeBps", + "name": "redeem", "constant": false, "payable": false, "inputs": [ { "type": "uint256", - "name": "_redeemFeeBps" - } - ], - "outputs": [] - }, - { - "type": "function", - "name": "setStrategistAddr", - "constant": false, - "payable": false, - "inputs": [ + "name": "_amount" + }, { - "type": "address", - "name": "_address" + "type": "uint256", + "name": "_minimumUnitAmount" } ], "outputs": [] }, { "type": "function", - "name": "setTrusteeAddress", + "name": "redeemAll", "constant": false, "payable": false, "inputs": [ { - "type": "address", - "name": "_address" + "type": "uint256", + "name": "_minimumUnitAmount" } ], "outputs": [] }, { "type": "function", - "name": "setTrusteeFeeBps", - "constant": false, + "name": "redeemFeeBps", + "constant": true, + "stateMutability": "view", "payable": false, - "inputs": [ + "inputs": [], + "outputs": [ { "type": "uint256", - "name": "_basis" + "name": "" } - ], - "outputs": [] + ] }, { "type": "function", - "name": "setVaultBuffer", + "name": "setAdminImpl", "constant": false, "payable": false, "inputs": [ { - "type": "uint256", - "name": "_vaultBuffer" + "type": "address", + "name": "newImpl" } ], "outputs": [] @@ -822,20 +897,17 @@ export const ABI_JSON = [ }, { "type": "function", - "name": "supportAsset", - "constant": false, + "name": "totalValue", + "constant": true, + "stateMutability": "view", "payable": false, - "inputs": [ - { - "type": "address", - "name": "_asset" - }, + "inputs": [], + "outputs": [ { - "type": "uint8", - "name": "_unitConversion" + "type": "uint256", + "name": "value" } - ], - "outputs": [] + ] }, { "type": "function", @@ -850,23 +922,6 @@ export const ABI_JSON = [ ], "outputs": [] }, - { - "type": "function", - "name": "transferToken", - "constant": false, - "payable": false, - "inputs": [ - { - "type": "address", - "name": "_asset" - }, - { - "type": "uint256", - "name": "_amount" - } - ], - "outputs": [] - }, { "type": "function", "name": "trusteeAddress", @@ -895,22 +950,6 @@ export const ABI_JSON = [ } ] }, - { - "type": "function", - "name": "unpauseCapital", - "constant": false, - "payable": false, - "inputs": [], - "outputs": [] - }, - { - "type": "function", - "name": "unpauseRebase", - "constant": false, - "payable": false, - "inputs": [], - "outputs": [] - }, { "type": "function", "name": "vaultBuffer", @@ -924,47 +963,5 @@ export const ABI_JSON = [ "name": "" } ] - }, - { - "type": "function", - "name": "withdrawAllFromStrategies", - "constant": false, - "payable": false, - "inputs": [], - "outputs": [] - }, - { - "type": "function", - "name": "withdrawAllFromStrategy", - "constant": false, - "payable": false, - "inputs": [ - { - "type": "address", - "name": "_strategyAddr" - } - ], - "outputs": [] - }, - { - "type": "function", - "name": "withdrawFromStrategy", - "constant": false, - "payable": false, - "inputs": [ - { - "type": "address", - "name": "_strategyFromAddress" - }, - { - "type": "address[]", - "name": "_assets" - }, - { - "type": "uint256[]", - "name": "_amounts" - } - ], - "outputs": [] } ] diff --git a/src/abi/otoken-vault.ts b/src/abi/otoken-vault.ts index 0b2f2232..cea63d2a 100644 --- a/src/abi/otoken-vault.ts +++ b/src/abi/otoken-vault.ts @@ -68,6 +68,18 @@ export const events = { StrategyRemoved: new LogEvent<([_addr: string] & {_addr: string})>( abi, '0x09a1db4b80c32706328728508c941a6b954f31eb5affd32f236c1fd405f8fea4' ), + SwapAllowedUndervalueChanged: new LogEvent<([_basis: bigint] & {_basis: bigint})>( + abi, '0xf12c00256bee2b6facb111a88a9b1cff86e79132939b44f1353212d6f7469557' + ), + SwapSlippageChanged: new LogEvent<([_asset: string, _basis: bigint] & {_asset: string, _basis: bigint})>( + abi, '0x8d22e9d2cbe8bb65a3c4412bd8970743864512a1a0e004e8d00fb96277b78b94' + ), + Swapped: new LogEvent<([_fromAsset: string, _toAsset: string, _fromAssetAmount: bigint, _toAssetAmount: bigint] & {_fromAsset: string, _toAsset: string, _fromAssetAmount: bigint, _toAssetAmount: bigint})>( + abi, '0xa078c4190abe07940190effc1846be0ccf03ad6007bc9e93f9697d0b460befbb' + ), + SwapperChanged: new LogEvent<([_address: string] & {_address: string})>( + abi, '0x7d7719313229e558c5a3893cad2eb86a86a049156d1d9ebd5c63a8eedefd1c03' + ), TrusteeAddressChanged: new LogEvent<([_address: string] & {_address: string})>( abi, '0x1e4af5ac389e8cde1bdaa6830881b6c987c62a45cfb3b33d27d805cde3b57750' ), @@ -83,8 +95,8 @@ export const events = { } export const functions = { - approveStrategy: new Func<[_addr: string], {_addr: string}, []>( - abi, '0x3b8ae397' + allocate: new Func<[], {}, []>( + abi, '0xabaa9916' ), assetDefaultStrategies: new Func<[_: string], {}, string>( abi, '0xa403e4d5' @@ -92,30 +104,54 @@ export const functions = { autoAllocateThreshold: new Func<[], {}, bigint>( abi, '0x9fa1826e' ), - cacheDecimals: new Func<[_asset: string], {_asset: string}, []>( - abi, '0x36b6d944' + burnForStrategy: new Func<[_amount: bigint], {_amount: bigint}, []>( + abi, '0x6217f3ea' + ), + calculateRedeemOutputs: new Func<[_amount: bigint], {_amount: bigint}, Array>( + abi, '0x67bd7ba3' ), capitalPaused: new Func<[], {}, boolean>( abi, '0xe6cc5432' ), + checkBalance: new Func<[_asset: string], {_asset: string}, bigint>( + abi, '0x5f515226' + ), claimGovernance: new Func<[], {}, []>( abi, '0x5d36b190' ), - depositToStrategy: new Func<[_strategyToAddress: string, _assets: Array, _amounts: Array], {_strategyToAddress: string, _assets: Array, _amounts: Array}, []>( - abi, '0x840c4c7a' + getAllAssets: new Func<[], {}, Array>( + abi, '0x2acada4d' + ), + getAllStrategies: new Func<[], {}, Array>( + abi, '0xc3b28864' + ), + getAssetConfig: new Func<[_asset: string], {_asset: string}, ([isSupported: boolean, unitConversion: number, decimals: number, allowedOracleSlippageBps: number] & {isSupported: boolean, unitConversion: number, decimals: number, allowedOracleSlippageBps: number})>( + abi, '0x6ec3ab67' + ), + getAssetCount: new Func<[], {}, bigint>( + abi, '0xa0aead4d' + ), + getStrategyCount: new Func<[], {}, bigint>( + abi, '0x31e19cfa' ), governor: new Func<[], {}, string>( abi, '0x0c340a24' ), - initialize: new Func<[_priceProvider: string, _ousd: string], {_priceProvider: string, _ousd: string}, []>( - abi, '0x485cc955' - ), isGovernor: new Func<[], {}, boolean>( abi, '0xc7af3352' ), + isSupportedAsset: new Func<[_asset: string], {_asset: string}, boolean>( + abi, '0x9be918e6' + ), maxSupplyDiff: new Func<[], {}, bigint>( abi, '0x8e510b52' ), + mint: new Func<[_asset: string, _amount: bigint, _minimumOusdAmount: bigint], {_asset: string, _amount: bigint, _minimumOusdAmount: bigint}, []>( + abi, '0x156e29f6' + ), + mintForStrategy: new Func<[_amount: bigint], {_amount: bigint}, []>( + abi, '0xab80dafb' + ), netOusdMintForStrategyThreshold: new Func<[], {}, bigint>( abi, '0x7a2202f3' ), @@ -125,17 +161,17 @@ export const functions = { ousdMetaStrategy: new Func<[], {}, string>( abi, '0x18ce56bd' ), - pauseCapital: new Func<[], {}, []>( - abi, '0x3dbc911f' - ), - pauseRebase: new Func<[], {}, []>( - abi, '0xc5f00841' - ), priceProvider: new Func<[], {}, string>( abi, '0xb888879e' ), - reallocate: new Func<[_strategyFromAddress: string, _strategyToAddress: string, _assets: Array, _amounts: Array], {_strategyFromAddress: string, _strategyToAddress: string, _assets: Array, _amounts: Array}, []>( - abi, '0x7fe2d393' + priceUnitMint: new Func<[asset: string], {asset: string}, bigint>( + abi, '0x3b8fe28d' + ), + priceUnitRedeem: new Func<[asset: string], {asset: string}, bigint>( + abi, '0x5b60f9fc' + ), + rebase: new Func<[], {}, []>( + abi, '0xaf14052c' ), rebasePaused: new Func<[], {}, boolean>( abi, '0x53ca9f24' @@ -143,87 +179,36 @@ export const functions = { rebaseThreshold: new Func<[], {}, bigint>( abi, '0x52d38e5d' ), + redeem: new Func<[_amount: bigint, _minimumUnitAmount: bigint], {_amount: bigint, _minimumUnitAmount: bigint}, []>( + abi, '0x7cbc2373' + ), + redeemAll: new Func<[_minimumUnitAmount: bigint], {_minimumUnitAmount: bigint}, []>( + abi, '0x7136a7a6' + ), redeemFeeBps: new Func<[], {}, bigint>( abi, '0x09f6442c' ), - removeStrategy: new Func<[_addr: string], {_addr: string}, []>( - abi, '0x175188e8' - ), setAdminImpl: new Func<[newImpl: string], {newImpl: string}, []>( abi, '0xfc0cfeee' ), - setAssetDefaultStrategy: new Func<[_asset: string, _strategy: string], {_asset: string, _strategy: string}, []>( - abi, '0xbc90106b' - ), - setAutoAllocateThreshold: new Func<[_threshold: bigint], {_threshold: bigint}, []>( - abi, '0xb2c9336d' - ), - setMaxSupplyDiff: new Func<[_maxSupplyDiff: bigint], {_maxSupplyDiff: bigint}, []>( - abi, '0x663e64ce' - ), - setNetOusdMintForStrategyThreshold: new Func<[_threshold: bigint], {_threshold: bigint}, []>( - abi, '0x636e6c40' - ), - setOusdMetaStrategy: new Func<[_ousdMetaStrategy: string], {_ousdMetaStrategy: string}, []>( - abi, '0xd58e3b3a' - ), - setPriceProvider: new Func<[_priceProvider: string], {_priceProvider: string}, []>( - abi, '0x372aa224' - ), - setRebaseThreshold: new Func<[_threshold: bigint], {_threshold: bigint}, []>( - abi, '0xb890ebf6' - ), - setRedeemFeeBps: new Func<[_redeemFeeBps: bigint], {_redeemFeeBps: bigint}, []>( - abi, '0xeb03654b' - ), - setStrategistAddr: new Func<[_address: string], {_address: string}, []>( - abi, '0x773540b3' - ), - setTrusteeAddress: new Func<[_address: string], {_address: string}, []>( - abi, '0x2da845a8' - ), - setTrusteeFeeBps: new Func<[_basis: bigint], {_basis: bigint}, []>( - abi, '0x0acbda75' - ), - setVaultBuffer: new Func<[_vaultBuffer: bigint], {_vaultBuffer: bigint}, []>( - abi, '0x8ec489a2' - ), strategistAddr: new Func<[], {}, string>( abi, '0x570d8e1d' ), - supportAsset: new Func<[_asset: string, _unitConversion: number], {_asset: string, _unitConversion: number}, []>( - abi, '0x6c7561e8' + totalValue: new Func<[], {}, bigint>( + abi, '0xd4c3eea0' ), transferGovernance: new Func<[_newGovernor: string], {_newGovernor: string}, []>( abi, '0xd38bfff4' ), - transferToken: new Func<[_asset: string, _amount: bigint], {_asset: string, _amount: bigint}, []>( - abi, '0x1072cbea' - ), trusteeAddress: new Func<[], {}, string>( abi, '0x49c1d54d' ), trusteeFeeBps: new Func<[], {}, bigint>( abi, '0x207134b0' ), - unpauseCapital: new Func<[], {}, []>( - abi, '0x94828ffd' - ), - unpauseRebase: new Func<[], {}, []>( - abi, '0x09f49bf5' - ), vaultBuffer: new Func<[], {}, bigint>( abi, '0x1edfe3da' ), - withdrawAllFromStrategies: new Func<[], {}, []>( - abi, '0xc9919112' - ), - withdrawAllFromStrategy: new Func<[_strategyAddr: string], {_strategyAddr: string}, []>( - abi, '0x597c8910' - ), - withdrawFromStrategy: new Func<[_strategyFromAddress: string, _assets: Array, _amounts: Array], {_strategyFromAddress: string, _assets: Array, _amounts: Array}, []>( - abi, '0xae69f3cb' - ), } export class Contract extends ContractBase { @@ -236,10 +221,38 @@ export class Contract extends ContractBase { return this.eth_call(functions.autoAllocateThreshold, []) } + calculateRedeemOutputs(_amount: bigint): Promise> { + return this.eth_call(functions.calculateRedeemOutputs, [_amount]) + } + capitalPaused(): Promise { return this.eth_call(functions.capitalPaused, []) } + checkBalance(_asset: string): Promise { + return this.eth_call(functions.checkBalance, [_asset]) + } + + getAllAssets(): Promise> { + return this.eth_call(functions.getAllAssets, []) + } + + getAllStrategies(): Promise> { + return this.eth_call(functions.getAllStrategies, []) + } + + getAssetConfig(_asset: string): Promise<([isSupported: boolean, unitConversion: number, decimals: number, allowedOracleSlippageBps: number] & {isSupported: boolean, unitConversion: number, decimals: number, allowedOracleSlippageBps: number})> { + return this.eth_call(functions.getAssetConfig, [_asset]) + } + + getAssetCount(): Promise { + return this.eth_call(functions.getAssetCount, []) + } + + getStrategyCount(): Promise { + return this.eth_call(functions.getStrategyCount, []) + } + governor(): Promise { return this.eth_call(functions.governor, []) } @@ -248,6 +261,10 @@ export class Contract extends ContractBase { return this.eth_call(functions.isGovernor, []) } + isSupportedAsset(_asset: string): Promise { + return this.eth_call(functions.isSupportedAsset, [_asset]) + } + maxSupplyDiff(): Promise { return this.eth_call(functions.maxSupplyDiff, []) } @@ -268,6 +285,14 @@ export class Contract extends ContractBase { return this.eth_call(functions.priceProvider, []) } + priceUnitMint(asset: string): Promise { + return this.eth_call(functions.priceUnitMint, [asset]) + } + + priceUnitRedeem(asset: string): Promise { + return this.eth_call(functions.priceUnitRedeem, [asset]) + } + rebasePaused(): Promise { return this.eth_call(functions.rebasePaused, []) } @@ -284,6 +309,10 @@ export class Contract extends ContractBase { return this.eth_call(functions.strategistAddr, []) } + totalValue(): Promise { + return this.eth_call(functions.totalValue, []) + } + trusteeAddress(): Promise { return this.eth_call(functions.trusteeAddress, []) } diff --git a/src/main.ts b/src/main.ts index d1bab6e2..a98fef97 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,5 @@ import * as exchangeRates from './post-processors/exchange-rates' +import * as validateOeth from './post-processors/validate-oeth' import { run } from './processor' import * as aaveCompound from './processors/aave-compound' import * as curve from './processors/curve' @@ -29,7 +30,7 @@ run([ balancerMetaPoolStrategy, ], postProcessors: [exchangeRates], - validators: [], + validators: [validateOeth], }, { name: 'ousd', diff --git a/src/post-processors/exchange-rates/exchange-rates.ts b/src/post-processors/exchange-rates/exchange-rates.ts index 4a3c46f5..d53974ec 100644 --- a/src/post-processors/exchange-rates/exchange-rates.ts +++ b/src/post-processors/exchange-rates/exchange-rates.ts @@ -10,7 +10,7 @@ const useExchangeRates = (ctx: Context) => export const process = async (ctx: Context) => { const [rates] = useExchangeRates(ctx) if (rates.size > 0) { - ctx.log.info({ count: rates.size }, 'exchange-rates') + ctx.log.debug({ count: rates.size }, 'exchange-rates') await ctx.store.insert([...rates.values()]) } } diff --git a/src/post-processors/validate-oeth/validate-oeth.ts b/src/post-processors/validate-oeth/validate-oeth.ts index 68e1c049..5af676c3 100644 --- a/src/post-processors/validate-oeth/validate-oeth.ts +++ b/src/post-processors/validate-oeth/validate-oeth.ts @@ -1,12 +1,70 @@ -import { OETHCurveLP } from '../../model' -import { Context } from '../../processor' +import { Entity, EntityClass } from '@subsquid/typeorm-store' +import assert from 'assert' +import { sortBy } from 'lodash' -export const process = (ctx: Context) => { - // for (const block of ctx.blocks) { - // if (block.header.height) { - // ctx.store.findOne(OETHCurveLP, {}) - // } - // } +import { OETHVault } from '../../model' +import { Block, Context } from '../../processor' +import { jsonify } from '../../utils/jsonify' + +export const name = 'validate-oeth' + +export const process = async (ctx: Context) => { + for (const block of ctx.blocks) { + await validateExpectations(ctx, block, expectations.oethVaults, OETHVault) + } +} + +const validateExpectations = async < + T extends Entity & { timestamp: string; blockNumber: number }, +>( + ctx: Context, + block: Block, + expectations: T[], + Class: EntityClass, +) => { + while (expectations[0]?.blockNumber === block.header.height) { + const entity = expectations.shift()! + await validateExpectation(ctx, block, Class, entity) + } } -const expectations = {} +const validateExpectation = async < + T extends Entity & { timestamp: string; blockNumber: number }, +>( + ctx: Context, + block: Block, + Class: EntityClass, + expectation: T, +) => { + const actual = await ctx.store.findOne(Class, { + where: { blockNumber: block.header.height }, + }) + expectation.timestamp = new Date(expectation.timestamp).toJSON() + assert.deepEqual(JSON.parse(jsonify(actual)), expectation) +} + +const expectations = { + oethVaults: sortBy( + [ + { + id: '2023-04-26T12:09:35.000Z', + timestamp: '2023-04-26T12:09:35.000000Z', + blockNumber: 17130306, + rETH: '0', + stETH: '250206338304274399', + weth: '72000000000000000', + frxETH: '99995919665342123', + }, + { + id: '2023-06-20T09:07:35.000Z', + timestamp: '2023-06-20T09:07:35.000000Z', + blockNumber: 17519916, + rETH: '2554674408676488827943', + stETH: '781566911007603525469', + weth: '9305529378012728527471', + frxETH: '2497474125748771996882', + }, + ], + (v) => v.blockNumber, + ), +} diff --git a/src/processors/oeth/vault.ts b/src/processors/oeth/vault.ts index 37817b94..3b09e2b5 100644 --- a/src/processors/oeth/vault.ts +++ b/src/processors/oeth/vault.ts @@ -3,6 +3,7 @@ import { pad } from 'viem' import * as erc20 from '../../abi/erc20' import * as lido from '../../abi/lido' +import * as otokenVault from '../../abi/otoken-vault' import { OETHVault } from '../../model' import { ensureExchangeRates } from '../../post-processors/exchange-rates' import { Context } from '../../processor' @@ -14,7 +15,7 @@ import { VAULT_ERC20_ADDRESSES, WETH_ADDRESS, } from '../../utils/addresses' -import { getLatestEntity, trackAddressBalances } from '../utils' +import { getLatestEntity } from '../utils' interface ProcessResult { vaults: OETHVault[] @@ -29,20 +30,25 @@ export const setup = (processor: EvmBatchProcessor) => { topic0: [erc20.events.Transfer.topic], topic1: [pad(OETH_VAULT_ADDRESS)], range: { from }, + transaction: false, }) processor.addLog({ address: VAULT_ERC20_ADDRESSES, topic0: [erc20.events.Transfer.topic], topic2: [pad(OETH_VAULT_ADDRESS)], range: { from }, + transaction: false, }) processor.addLog({ address: [STETH_ADDRESS], topic0: [lido.events.TokenRebased.topic], range: { from }, + transaction: false, }) } +const addresses = new Set(VAULT_ERC20_ADDRESSES.map((a) => a.toLowerCase())) + export const process = async (ctx: Context) => { const result: ProcessResult = { vaults: [], @@ -50,9 +56,23 @@ export const process = async (ctx: Context) => { } for (const block of ctx.blocks) { - for (const log of block.logs) { - await processTransfer(ctx, result, block, log) - await processStEthRebase(ctx, result, block, log) + const haveTransfer = block.logs.find( + (log) => + log.topics[0] === erc20.events.Transfer.topic && + addresses.has(log.address as `0x${string}`) && + (log.topics[1] === pad(OETH_VAULT_ADDRESS) || + log.topics[2] === pad(OETH_VAULT_ADDRESS)), + ) + if (haveTransfer) { + await processTransfer(ctx, result, block) + } + const haveStRebase = block.logs.find( + (log) => + log.address === STETH_ADDRESS && + log.topics[0] === lido.events.TokenRebased.topic, + ) + if (haveStRebase) { + await processStEthRebase(ctx, result, block) } } @@ -63,43 +83,27 @@ const processStEthRebase = async ( ctx: Context, result: ProcessResult, block: Context['blocks']['0'], - log: Context['blocks']['0']['logs']['0'], ) => { - if ( - log.address === STETH_ADDRESS && - log.topics[0] === lido.events.TokenRebased.topic - ) { - const { vault } = await getLatestOETHVault(ctx, result, block) - const contract = new lido.Contract(ctx, block.header, STETH_ADDRESS) - vault.stETH = await contract.balanceOf(OETH_VAULT_ADDRESS) - } + const { vault } = await getLatestOETHVault(ctx, result, block) + const contract = new lido.Contract(ctx, block.header, STETH_ADDRESS) + vault.stETH = await contract.balanceOf(OETH_VAULT_ADDRESS) } const processTransfer = async ( ctx: Context, result: ProcessResult, block: Context['blocks']['0'], - log: Context['blocks']['0']['logs']['0'], ) => { - if (log.topics[0] === erc20.events.Transfer.topic) { - await trackAddressBalances({ - log, - address: OETH_VAULT_ADDRESS, - tokens: VAULT_ERC20_ADDRESSES, - fn: async ({ token, change }) => { - const { vault } = await getLatestOETHVault(ctx, result, block) - if (token === WETH_ADDRESS) { - vault.weth += change - } else if (token === RETH_ADDRESS) { - vault.rETH += change - } else if (token === STETH_ADDRESS) { - vault.stETH += change - } else if (token === FRXETH_ADDRESS) { - vault.frxETH += change - } - }, - }) - } + const { vault } = await getLatestOETHVault(ctx, result, block) + const vaultContract = new otokenVault.Contract( + ctx, + block.header, + OETH_VAULT_ADDRESS, + ) + vault.weth = await vaultContract.checkBalance(WETH_ADDRESS) + vault.rETH = await vaultContract.checkBalance(RETH_ADDRESS) + vault.stETH = await vaultContract.checkBalance(STETH_ADDRESS) + vault.frxETH = await vaultContract.checkBalance(FRXETH_ADDRESS) } const getLatestOETHVault = async ( diff --git a/src/utils/jsonify.ts b/src/utils/jsonify.ts new file mode 100644 index 00000000..ac447fd6 --- /dev/null +++ b/src/utils/jsonify.ts @@ -0,0 +1,9 @@ +function replacer(key: string, value: unknown) { + if (typeof value === 'bigint') { + return value.toString() + } else { + return value + } +} + +export const jsonify = (value: any) => JSON.stringify(value, replacer) From a49473fbb6f9cd12aef3f4bc5a3d803b21d194e2 Mon Sep 17 00:00:00 2001 From: Chris Jacobs Date: Fri, 20 Oct 2023 09:49:56 -0700 Subject: [PATCH 02/12] morpho aave change and validation --- .../validate-oeth/validate-oeth.ts | 47 +++++++++++--- src/processors/oeth/morpho-aave.ts | 61 ++++++++----------- 2 files changed, 65 insertions(+), 43 deletions(-) diff --git a/src/post-processors/validate-oeth/validate-oeth.ts b/src/post-processors/validate-oeth/validate-oeth.ts index 5af676c3..d6ecb40e 100644 --- a/src/post-processors/validate-oeth/validate-oeth.ts +++ b/src/post-processors/validate-oeth/validate-oeth.ts @@ -2,7 +2,7 @@ import { Entity, EntityClass } from '@subsquid/typeorm-store' import assert from 'assert' import { sortBy } from 'lodash' -import { OETHVault } from '../../model' +import { OETHMorphoAave, OETHVault } from '../../model' import { Block, Context } from '../../processor' import { jsonify } from '../../utils/jsonify' @@ -10,7 +10,13 @@ export const name = 'validate-oeth' export const process = async (ctx: Context) => { for (const block of ctx.blocks) { - await validateExpectations(ctx, block, expectations.oethVaults, OETHVault) + await validateExpectations(ctx, block, OETHVault, expectations.oethVaults) + await validateExpectations( + ctx, + block, + OETHMorphoAave, + expectations.oethMorphoAave, + ) } } @@ -19,12 +25,17 @@ const validateExpectations = async < >( ctx: Context, block: Block, - expectations: T[], Class: EntityClass, + expectations?: T[], ) => { + if (!expectations) return + assert( + !expectations.length || expectations[0]?.blockNumber >= block.header.height, + 'Something is missing', + ) while (expectations[0]?.blockNumber === block.header.height) { const entity = expectations.shift()! - await validateExpectation(ctx, block, Class, entity) + await validateExpectation(ctx, Class, entity) } } @@ -32,18 +43,17 @@ const validateExpectation = async < T extends Entity & { timestamp: string; blockNumber: number }, >( ctx: Context, - block: Block, Class: EntityClass, expectation: T, ) => { const actual = await ctx.store.findOne(Class, { - where: { blockNumber: block.header.height }, + where: { id: expectation.id }, }) expectation.timestamp = new Date(expectation.timestamp).toJSON() assert.deepEqual(JSON.parse(jsonify(actual)), expectation) } -const expectations = { +const expectations: Record = { oethVaults: sortBy( [ { @@ -67,4 +77,27 @@ const expectations = { ], (v) => v.blockNumber, ), + oethMorphoAave: sortBy( + [ + { + id: '2023-05-31T13:54:35.000Z', + timestamp: '2023-05-31T13:54:35.000000Z', + blockNumber: 17379284, + weth: '57200000000000000000', + }, + { + id: '2023-06-02T09:51:11.000Z', + timestamp: '2023-06-02T09:51:11.000000Z', + blockNumber: 17392282, + weth: '0', + }, + { + id: '2023-06-14T17:54:35.000Z', + timestamp: '2023-06-14T17:54:35.000000Z', + blockNumber: 17479788, + weth: '103288680000000000000', + }, + ], + (v) => v.blockNumber, + ), } diff --git a/src/processors/oeth/morpho-aave.ts b/src/processors/oeth/morpho-aave.ts index 8668ba4f..4e39942a 100644 --- a/src/processors/oeth/morpho-aave.ts +++ b/src/processors/oeth/morpho-aave.ts @@ -2,11 +2,11 @@ import { EvmBatchProcessor } from '@subsquid/evm-processor' import { pad } from 'viem' import * as erc20 from '../../abi/erc20' +import * as initializableAbstractStrategy from '../../abi/initializable-abstract-strategy' import { OETHMorphoAave } from '../../model' import { ensureExchangeRate } from '../../post-processors/exchange-rates' import { Context } from '../../processor' import { OETH_MORPHO_AAVE_ADDRESS, WETH_ADDRESS } from '../../utils/addresses' -import { getLatestEntity, trackAddressBalances } from '../utils' interface ProcessResult { morphoAaves: OETHMorphoAave[] @@ -35,48 +35,37 @@ export const process = async (ctx: Context) => { } for (const block of ctx.blocks) { - for (const log of block.logs) { - await processTransfer(ctx, result, block, log) + const haveTransfer = block.logs.find( + (log) => + log.topics[0] === erc20.events.Transfer.topic && + WETH_ADDRESS === log.address && + (log.topics[1] === pad(OETH_MORPHO_AAVE_ADDRESS) || + log.topics[2] === pad(OETH_MORPHO_AAVE_ADDRESS)), + ) + if (haveTransfer) { + await updateValues(ctx, result, block) } } - await ctx.store.insert(result.morphoAaves) } -const processTransfer = async ( +const updateValues = async ( ctx: Context, result: ProcessResult, block: Context['blocks']['0'], - log: Context['blocks']['0']['logs']['0'], ) => { - if (log.topics[0] === erc20.events.Transfer.topic) { - await trackAddressBalances({ - log, - address: OETH_MORPHO_AAVE_ADDRESS, - tokens: [WETH_ADDRESS], - fn: async ({ log, token, change }) => { - const timestampId = new Date(block.header.timestamp).toISOString() - const { latest, current } = await getLatestEntity( - ctx, - OETHMorphoAave, - result.morphoAaves, - timestampId, - ) - - let morphoAave = current - if (!morphoAave) { - await ensureExchangeRate(ctx, block, 'ETH', 'WETH') // No async since WETH. - morphoAave = new OETHMorphoAave({ - id: timestampId, - timestamp: new Date(block.header.timestamp), - blockNumber: block.header.height, - weth: latest?.weth ?? 0n, - }) - result.morphoAaves.push(morphoAave) - } - - morphoAave.weth += change - }, - }) - } + const timestampId = new Date(block.header.timestamp).toISOString() + await ensureExchangeRate(ctx, block, 'ETH', 'WETH') // No async since WETH. + const contract = new initializableAbstractStrategy.Contract( + ctx, + block.header, + OETH_MORPHO_AAVE_ADDRESS, + ) + const morphoAave = new OETHMorphoAave({ + id: timestampId, + timestamp: new Date(block.header.timestamp), + blockNumber: block.header.height, + weth: await contract.checkBalance(WETH_ADDRESS), + }) + result.morphoAaves.push(morphoAave) } From 3ab8185694df26d3587e1e5e2b0a61cf8521d16c Mon Sep 17 00:00:00 2001 From: Chris Jacobs Date: Fri, 20 Oct 2023 11:23:54 -0700 Subject: [PATCH 03/12] sqd bump --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 76466b5c..d5c15c50 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,7 @@ "name": "origin-squid", "dependencies": { "@subsquid/archive-registry": "^3.3.0", - "@subsquid/evm-processor": "^1.8.4", + "@subsquid/evm-processor": "^1.8.5", "@subsquid/graphql-server": "^4.3.1", "@subsquid/typeorm-migration": "^1.2.2", "@subsquid/typeorm-store": "^1.2.4", @@ -860,9 +860,9 @@ } }, "node_modules/@subsquid/evm-processor": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/@subsquid/evm-processor/-/evm-processor-1.8.4.tgz", - "integrity": "sha512-++XtPPV+UY2YkQ0fb5AVDtL99jbGOWN4BMKzi7yyVco11tiDzAzXaVqSE69rL+QOJala9LrZRTjyKVl989rXLw==", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@subsquid/evm-processor/-/evm-processor-1.8.5.tgz", + "integrity": "sha512-f4wmmFcl/5UT+z1O6SIv6SGkDB1TKCujsOTB/Kml+jTY0ofKUemwg+J4v662QPmxm0uIsjxxiW2tYpPKqg//Jg==", "dependencies": { "@subsquid/http-client": "^1.3.1", "@subsquid/logger": "^1.3.1", diff --git a/package.json b/package.json index 81099fe9..48bbf54d 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ }, "dependencies": { "@subsquid/archive-registry": "^3.3.0", - "@subsquid/evm-processor": "^1.8.4", + "@subsquid/evm-processor": "^1.8.5", "@subsquid/graphql-server": "^4.3.1", "@subsquid/typeorm-migration": "^1.2.2", "@subsquid/typeorm-store": "^1.2.4", From 55e8f066a70bc6c92c0a4629996fceebbf592b63 Mon Sep 17 00:00:00 2001 From: Chris Jacobs Date: Fri, 20 Oct 2023 11:26:41 -0700 Subject: [PATCH 04/12] revert vault changes --- src/processors/oeth/vault.ts | 70 +++++++++++++++++------------------- 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/src/processors/oeth/vault.ts b/src/processors/oeth/vault.ts index 3b09e2b5..37817b94 100644 --- a/src/processors/oeth/vault.ts +++ b/src/processors/oeth/vault.ts @@ -3,7 +3,6 @@ import { pad } from 'viem' import * as erc20 from '../../abi/erc20' import * as lido from '../../abi/lido' -import * as otokenVault from '../../abi/otoken-vault' import { OETHVault } from '../../model' import { ensureExchangeRates } from '../../post-processors/exchange-rates' import { Context } from '../../processor' @@ -15,7 +14,7 @@ import { VAULT_ERC20_ADDRESSES, WETH_ADDRESS, } from '../../utils/addresses' -import { getLatestEntity } from '../utils' +import { getLatestEntity, trackAddressBalances } from '../utils' interface ProcessResult { vaults: OETHVault[] @@ -30,25 +29,20 @@ export const setup = (processor: EvmBatchProcessor) => { topic0: [erc20.events.Transfer.topic], topic1: [pad(OETH_VAULT_ADDRESS)], range: { from }, - transaction: false, }) processor.addLog({ address: VAULT_ERC20_ADDRESSES, topic0: [erc20.events.Transfer.topic], topic2: [pad(OETH_VAULT_ADDRESS)], range: { from }, - transaction: false, }) processor.addLog({ address: [STETH_ADDRESS], topic0: [lido.events.TokenRebased.topic], range: { from }, - transaction: false, }) } -const addresses = new Set(VAULT_ERC20_ADDRESSES.map((a) => a.toLowerCase())) - export const process = async (ctx: Context) => { const result: ProcessResult = { vaults: [], @@ -56,23 +50,9 @@ export const process = async (ctx: Context) => { } for (const block of ctx.blocks) { - const haveTransfer = block.logs.find( - (log) => - log.topics[0] === erc20.events.Transfer.topic && - addresses.has(log.address as `0x${string}`) && - (log.topics[1] === pad(OETH_VAULT_ADDRESS) || - log.topics[2] === pad(OETH_VAULT_ADDRESS)), - ) - if (haveTransfer) { - await processTransfer(ctx, result, block) - } - const haveStRebase = block.logs.find( - (log) => - log.address === STETH_ADDRESS && - log.topics[0] === lido.events.TokenRebased.topic, - ) - if (haveStRebase) { - await processStEthRebase(ctx, result, block) + for (const log of block.logs) { + await processTransfer(ctx, result, block, log) + await processStEthRebase(ctx, result, block, log) } } @@ -83,27 +63,43 @@ const processStEthRebase = async ( ctx: Context, result: ProcessResult, block: Context['blocks']['0'], + log: Context['blocks']['0']['logs']['0'], ) => { - const { vault } = await getLatestOETHVault(ctx, result, block) - const contract = new lido.Contract(ctx, block.header, STETH_ADDRESS) - vault.stETH = await contract.balanceOf(OETH_VAULT_ADDRESS) + if ( + log.address === STETH_ADDRESS && + log.topics[0] === lido.events.TokenRebased.topic + ) { + const { vault } = await getLatestOETHVault(ctx, result, block) + const contract = new lido.Contract(ctx, block.header, STETH_ADDRESS) + vault.stETH = await contract.balanceOf(OETH_VAULT_ADDRESS) + } } const processTransfer = async ( ctx: Context, result: ProcessResult, block: Context['blocks']['0'], + log: Context['blocks']['0']['logs']['0'], ) => { - const { vault } = await getLatestOETHVault(ctx, result, block) - const vaultContract = new otokenVault.Contract( - ctx, - block.header, - OETH_VAULT_ADDRESS, - ) - vault.weth = await vaultContract.checkBalance(WETH_ADDRESS) - vault.rETH = await vaultContract.checkBalance(RETH_ADDRESS) - vault.stETH = await vaultContract.checkBalance(STETH_ADDRESS) - vault.frxETH = await vaultContract.checkBalance(FRXETH_ADDRESS) + if (log.topics[0] === erc20.events.Transfer.topic) { + await trackAddressBalances({ + log, + address: OETH_VAULT_ADDRESS, + tokens: VAULT_ERC20_ADDRESSES, + fn: async ({ token, change }) => { + const { vault } = await getLatestOETHVault(ctx, result, block) + if (token === WETH_ADDRESS) { + vault.weth += change + } else if (token === RETH_ADDRESS) { + vault.rETH += change + } else if (token === STETH_ADDRESS) { + vault.stETH += change + } else if (token === FRXETH_ADDRESS) { + vault.frxETH += change + } + }, + }) + } } const getLatestOETHVault = async ( From bfc12f2d72d95cb82dde47e840e010ead7f024c4 Mon Sep 17 00:00:00 2001 From: Chris Jacobs Date: Fri, 20 Oct 2023 11:39:12 -0700 Subject: [PATCH 05/12] validation update --- .../validate-oeth/validate-oeth.ts | 52 ++++++++++++++----- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/src/post-processors/validate-oeth/validate-oeth.ts b/src/post-processors/validate-oeth/validate-oeth.ts index d6ecb40e..e1b1e976 100644 --- a/src/post-processors/validate-oeth/validate-oeth.ts +++ b/src/post-processors/validate-oeth/validate-oeth.ts @@ -8,6 +8,8 @@ import { jsonify } from '../../utils/jsonify' export const name = 'validate-oeth' +let firstBlock = true + export const process = async (ctx: Context) => { for (const block of ctx.blocks) { await validateExpectations(ctx, block, OETHVault, expectations.oethVaults) @@ -17,6 +19,7 @@ export const process = async (ctx: Context) => { OETHMorphoAave, expectations.oethMorphoAave, ) + firstBlock = false } } @@ -29,6 +32,11 @@ const validateExpectations = async < expectations?: T[], ) => { if (!expectations) return + if (firstBlock) { + while (expectations[0]?.blockNumber < block.header.height) { + expectations.shift() + } + } assert( !expectations.length || expectations[0]?.blockNumber >= block.header.height, 'Something is missing', @@ -57,22 +65,40 @@ const expectations: Record = { oethVaults: sortBy( [ { - id: '2023-04-26T12:09:35.000Z', - timestamp: '2023-04-26T12:09:35.000000Z', - blockNumber: 17130306, - rETH: '0', - stETH: '250206338304274399', - weth: '72000000000000000', - frxETH: '99995919665342123', + id: '2023-04-28T00:39:11.000Z', + timestamp: '2023-04-28T00:39:11.000000Z', + blockNumber: 17141121, + rETH: '9360000000000000', + stETH: '215069621854827437', + weth: '1119651661749004532', + frxETH: '7695288773432093', + }, + { + id: '2023-06-10T19:50:59.000Z', + timestamp: '2023-06-10T19:50:59.000000Z', + blockNumber: 17451954, + rETH: '2554674408676488827943', + stETH: '779656163342992671163', + weth: '1026508082715309353868', + frxETH: '0', + }, + { + id: '2023-06-10T19:52:11.000Z', + timestamp: '2023-06-10T19:52:11.000000Z', + blockNumber: 17451960, + rETH: '2554674408676488827943', + stETH: '779656163342992671163', + weth: '1026508082715309353868', + frxETH: '99144201818216785', }, { - id: '2023-06-20T09:07:35.000Z', - timestamp: '2023-06-20T09:07:35.000000Z', - blockNumber: 17519916, + id: '2023-06-19T06:59:47.000Z', + timestamp: '2023-06-19T06:59:47.000000Z', + blockNumber: 17512166, rETH: '2554674408676488827943', - stETH: '781566911007603525469', - weth: '9305529378012728527471', - frxETH: '2497474125748771996882', + stETH: '781491659677780491225', + weth: '368830581327791252482', + frxETH: '0', }, ], (v) => v.blockNumber, From 7c801cd52d82b0e8c88eb6f1e27ee5b61f5afb6f Mon Sep 17 00:00:00 2001 From: Chris Jacobs Date: Fri, 20 Oct 2023 15:52:49 -0700 Subject: [PATCH 06/12] multiple evm processors does not work on the cloud --- squid.yaml | 2 +- src/main.ts | 44 ++-- .../exchange-rates/price-routing.ts | 1 + src/processor.ts | 201 ++++++++---------- src/processors/oeth/frax-staking.ts | 5 +- src/utils/state.ts | 8 +- 6 files changed, 124 insertions(+), 137 deletions(-) diff --git a/squid.yaml b/squid.yaml index c3fb1331..2a130181 100644 --- a/squid.yaml +++ b/squid.yaml @@ -1,6 +1,6 @@ manifestVersion: subsquid.io/v0.1 name: origin-squid -version: 0 +version: 999 description: 'Origin Protocol 🦑' build: deploy: diff --git a/src/main.ts b/src/main.ts index a98fef97..dc71c3d5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -13,27 +13,23 @@ import * as vault from './processors/oeth/vault' import * as ousd from './processors/ousd' import * as strategies from './processors/strategies' -run([ - { - name: 'block-frequency-updates', - processors: [aaveCompound, curve, strategies], - }, - { - name: 'oeth', - processors: [ - oeth, - vault, - fraxStaking, - morphoAave, - dripper, - curveLp, - balancerMetaPoolStrategy, - ], - postProcessors: [exchangeRates], - validators: [validateOeth], - }, - { - name: 'ousd', - processors: [ousd], - }, -]) +run({ + processors: [ + // Block Frequency Updates + aaveCompound, + curve, + strategies, + // OETH Related + oeth, + vault, + fraxStaking, + morphoAave, + dripper, + curveLp, + balancerMetaPoolStrategy, + // OUSD Related + // ousd, + ], + postProcessors: [exchangeRates], + validators: [validateOeth], +}) diff --git a/src/post-processors/exchange-rates/price-routing.ts b/src/post-processors/exchange-rates/price-routing.ts index dfc5a897..cc47373b 100644 --- a/src/post-processors/exchange-rates/price-routing.ts +++ b/src/post-processors/exchange-rates/price-routing.ts @@ -74,6 +74,7 @@ export const getOethOraclePrice = ( const stakedFraxAddress = '0xac3e018457b222d93114458476f3e3416abbe38f' export const getStakedFraxPrice = (ctx: Context, block: Block) => { + if (block.header.height < 15686046) return undefined const router = new stakedFraxEth.Contract( ctx, block.header, diff --git a/src/processor.ts b/src/processor.ts index 0f5af885..0ac92c16 100644 --- a/src/processor.ts +++ b/src/processor.ts @@ -66,131 +66,118 @@ interface Processor { let initialized = false -export const run = ( - params: { - name: string - processors: Processor[] - postProcessors?: Pick[] - validators?: Pick[] - }[], -) => { - for (const { name, processors, postProcessors, validators } of params) { - if (process.env.PROCESSOR_NAME && process.env.PROCESSOR_NAME !== name) { - continue - } +export const run = ({ + processors, + postProcessors, + validators, +}: { + processors: Processor[] + postProcessors?: Pick[] + validators?: Pick[] +}) => { + const processor = createSquidProcessor() - const processor = createSquidProcessor() - if (name) { - // Hack our logging so it's unique per processor. - const hackableProcessor = processor as any - hackableProcessor.getLogger = () => createLogger(`sqd:processor-${name}`) - } - - processor.setBlockRange({ - from: Math.min( - ...(processors.map((p) => p.from).filter((x) => x) as number[]), - ), - }) - processors.forEach((p) => p.setup?.(processor)) - processor.run( - new TypeormDatabase({ - stateSchema: `${name}-processor`, - supportHotBlocks: true, - }), - async (_ctx) => { - const ctx = _ctx as Context - try { - ctx.name = name - ctx.state = new Map() - let start: number - const time = (name: string) => () => { - const message = `${name} ${Date.now() - start}ms` - return () => ctx.log.info(message) - } + processor.setBlockRange({ + from: Math.min( + ...(processors.map((p) => p.from).filter((x) => x) as number[]), + ), + }) + processors.forEach((p) => p.setup?.(processor)) + processor.run( + new TypeormDatabase({ + supportHotBlocks: true, + }), + async (_ctx) => { + const ctx = _ctx as Context + try { + ctx.__state = new Map() + let start: number + const time = (name: string) => () => { + const message = `${name} ${Date.now() - start}ms` + return () => ctx.log.info(message) + } - // Initialization Run - if (!initialized) { - initialized = true - ctx.log.info(`initializing`) - start = Date.now() - const times = await Promise.all( - processors - .filter((p) => p.initialize) - .map((p, index) => - p.initialize!(ctx).then( - time(p.name ?? `initializing processor-${index}`), - ), + // Initialization Run + if (!initialized) { + initialized = true + ctx.log.info(`initializing`) + start = Date.now() + const times = await Promise.all( + processors + .filter((p) => p.initialize) + .map((p, index) => + p.initialize!(ctx).then( + time(p.name ?? `initializing processor-${index}`), ), - ) - times.forEach((t) => t()) - } + ), + ) + times.forEach((t) => t()) + } - // Main Processing Run + // Main Processing Run + start = Date.now() + const times = await Promise.all( + processors.map((p, index) => + p.process(ctx).then(time(p.name ?? `processor-${index}`)), + ), + ) + if (process.env.DEBUG_PERF === 'true') { + times.forEach((t) => t()) + } + + if (postProcessors) { + // Post Processing Run start = Date.now() - const times = await Promise.all( - processors.map((p, index) => - p.process(ctx).then(time(p.name ?? `processor-${index}`)), + const postTimes = await Promise.all( + postProcessors.map((p, index) => + p.process(ctx).then(time(p.name ?? `postProcessor-${index}`)), ), ) if (process.env.DEBUG_PERF === 'true') { - times.forEach((t) => t()) - } - - if (postProcessors) { - // Post Processing Run - start = Date.now() - const postTimes = await Promise.all( - postProcessors.map((p, index) => - p.process(ctx).then(time(p.name ?? `postProcessor-${index}`)), - ), - ) - if (process.env.DEBUG_PERF === 'true') { - postTimes.forEach((t) => t()) - } + postTimes.forEach((t) => t()) } + } - if (validators) { - // Validation Run - start = Date.now() - const validatorTimes = await Promise.all( - validators.map((p, index) => - p.process(ctx).then(time(p.name ?? `validator-${index}`)), - ), - ) - if (process.env.DEBUG_PERF === 'true') { - validatorTimes.forEach((t) => t()) - } - } - } catch (err) { - ctx.log.info({ - blocks: ctx.blocks.length, - logs: ctx.blocks.reduce((sum, block) => sum + block.logs.length, 0), - traces: ctx.blocks.reduce( - (sum, block) => sum + block.traces.length, - 0, - ), - transactions: ctx.blocks.reduce( - (sum, block) => sum + block.transactions.length, - 0, + if (validators) { + // Validation Run + start = Date.now() + const validatorTimes = await Promise.all( + validators.map((p, index) => + p.process(ctx).then(time(p.name ?? `validator-${index}`)), ), - // logArray: ctx.blocks.reduce( - // (logs, block) => [...logs, ...block.logs], - // [] as Log[], - // ), - }) - throw err + ) + if (process.env.DEBUG_PERF === 'true') { + validatorTimes.forEach((t) => t()) + } } - }, - ) - } + } catch (err) { + ctx.log.info({ + blocks: ctx.blocks.length, + logs: ctx.blocks.reduce((sum, block) => sum + block.logs.length, 0), + traces: ctx.blocks.reduce( + (sum, block) => sum + block.traces.length, + 0, + ), + transactions: ctx.blocks.reduce( + (sum, block) => sum + block.transactions.length, + 0, + ), + // logArray: ctx.blocks.reduce( + // (logs, block) => [...logs, ...block.logs], + // [] as Log[], + // ), + }) + throw err + } + }, + ) } export type Fields = EvmBatchProcessorFields< ReturnType > export type Context = DataHandlerContext & { - name: string - state: Map + __state: Map } export type Block = Context['blocks']['0'] export type Log = Context['blocks']['0']['logs']['0'] diff --git a/src/processors/oeth/frax-staking.ts b/src/processors/oeth/frax-staking.ts index c557b49c..4311c1f9 100644 --- a/src/processors/oeth/frax-staking.ts +++ b/src/processors/oeth/frax-staking.ts @@ -64,7 +64,10 @@ export const process = async (ctx: Context) => { } for (const block of ctx.blocks) { - const haveLog = block.logs.find((log) => topicsToWatch.has(log.topics[0])) + const haveLog = block.logs.find( + (log) => + log.address === SFRXETH_ADDRESS && topicsToWatch.has(log.topics[0]), + ) if (haveLog) { await createOETHFraxStaking(ctx, result, block) } diff --git a/src/utils/state.ts b/src/utils/state.ts index 71d2d5f0..1c8fd35a 100644 --- a/src/utils/state.ts +++ b/src/utils/state.ts @@ -5,16 +5,16 @@ export const useProcessorState = ( key: string, defaultValue: T, ) => { - const { state } = ctx - let value = state.get(key) as T | undefined + const { __state } = ctx + let value = __state.get(key) as T | undefined if (!value) { value = defaultValue - state.set(key, value) + __state.set(key, value) } return [ value, (value: T) => { - state.set(key, value) + __state.set(key, value) }, ] as const } From 45672c7cad506d9614feb978f829ddb54d2a513b Mon Sep 17 00:00:00 2001 From: Chris Jacobs Date: Sat, 21 Oct 2023 09:45:03 -0700 Subject: [PATCH 07/12] remove `total` from OETHBalancerMetaPoolStrategy --- .../{1697749344979-Data.js => 1697906643694-Data.js} | 6 +++--- package.json | 1 + schema-oeth.graphql | 1 - schema.graphql | 1 - .../generated/oethBalancerMetaPoolStrategy.model.ts | 3 --- src/processors/oeth/balancer-meta-pool.ts | 11 ++--------- 6 files changed, 6 insertions(+), 17 deletions(-) rename db/migrations/{1697749344979-Data.js => 1697906643694-Data.js} (99%) diff --git a/db/migrations/1697749344979-Data.js b/db/migrations/1697906643694-Data.js similarity index 99% rename from db/migrations/1697749344979-Data.js rename to db/migrations/1697906643694-Data.js index f77d0b1b..6eebf67b 100644 --- a/db/migrations/1697749344979-Data.js +++ b/db/migrations/1697906643694-Data.js @@ -1,5 +1,5 @@ -module.exports = class Data1697749344979 { - name = 'Data1697749344979' +module.exports = class Data1697906643694 { + name = 'Data1697906643694' async up(db) { await db.query(`CREATE TABLE "exchange_rate" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "pair" text NOT NULL, "base" text NOT NULL, "quote" text NOT NULL, "rate" numeric NOT NULL, CONSTRAINT "PK_5c5d27d2b900ef6cdeef0398472" PRIMARY KEY ("id"))`) @@ -51,7 +51,7 @@ module.exports = class Data1697749344979 { await db.query(`CREATE TABLE "dripper" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "weth" numeric NOT NULL, CONSTRAINT "PK_74fd102c8d1c60f4b1650a61ffc" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_88c58f8948c3294c2a9e2791dc" ON "dripper" ("timestamp") `) await db.query(`CREATE INDEX "IDX_06822c0a260797711acc9023d5" ON "dripper" ("block_number") `) - await db.query(`CREATE TABLE "oeth_balancer_meta_pool_strategy" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "total" numeric NOT NULL, "r_eth" numeric NOT NULL, "weth" numeric NOT NULL, CONSTRAINT "PK_6ddf5b8ba878e6d706e59bd6de0" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "oeth_balancer_meta_pool_strategy" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "r_eth" numeric NOT NULL, "weth" numeric NOT NULL, CONSTRAINT "PK_6ddf5b8ba878e6d706e59bd6de0" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_5e7ef383756fa18cb602f50089" ON "oeth_balancer_meta_pool_strategy" ("timestamp") `) await db.query(`CREATE INDEX "IDX_11d344b3e0e03cdb6697dd61f7" ON "oeth_balancer_meta_pool_strategy" ("block_number") `) await db.query(`CREATE TABLE "ogv" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "circulating" numeric NOT NULL, "total" numeric NOT NULL, CONSTRAINT "PK_f16038abf451ce82bd03ea54ee7" PRIMARY KEY ("id"))`) diff --git a/package.json b/package.json index 48bbf54d..fb516808 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "private": true, "scripts": { "codegen": "echo '# GENERATED, DO NOT MODIFY\n' > schema.graphql && cat schema-*.graphql >> schema.graphql && sqd codegen && git add src/model/generated/*", + "migration:generate": "sqd down && sqd up && sqd migration:generate", "build": "rm -rf lib && tsc", "prettier-check": "prettier --check **/*.ts **/*.json", "prettier-fix": "prettier --write **/*.ts **/*.json" diff --git a/schema-oeth.graphql b/schema-oeth.graphql index 14dd43db..722e33d5 100644 --- a/schema-oeth.graphql +++ b/schema-oeth.graphql @@ -144,7 +144,6 @@ type OETHBalancerMetaPoolStrategy @entity { id: ID! timestamp: DateTime! @index blockNumber: Int! @index - total: BigInt! rETH: BigInt! weth: BigInt! } diff --git a/schema.graphql b/schema.graphql index 17a522d8..0136b539 100644 --- a/schema.graphql +++ b/schema.graphql @@ -207,7 +207,6 @@ type OETHBalancerMetaPoolStrategy @entity { id: ID! timestamp: DateTime! @index blockNumber: Int! @index - total: BigInt! rETH: BigInt! weth: BigInt! } diff --git a/src/model/generated/oethBalancerMetaPoolStrategy.model.ts b/src/model/generated/oethBalancerMetaPoolStrategy.model.ts index f16bb0ad..d833757f 100644 --- a/src/model/generated/oethBalancerMetaPoolStrategy.model.ts +++ b/src/model/generated/oethBalancerMetaPoolStrategy.model.ts @@ -18,9 +18,6 @@ export class OETHBalancerMetaPoolStrategy { @Column_("int4", {nullable: false}) blockNumber!: number - @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - total!: bigint - @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) rETH!: bigint diff --git a/src/processors/oeth/balancer-meta-pool.ts b/src/processors/oeth/balancer-meta-pool.ts index ea7385b9..172a4458 100644 --- a/src/processors/oeth/balancer-meta-pool.ts +++ b/src/processors/oeth/balancer-meta-pool.ts @@ -115,7 +115,7 @@ export const updateValues = async ( addresses.strategy, ) const lens = new originLens.Contract(ctx, block.header, addresses.originLens) - const [{ current, latest }, { total, rETH, weth }] = await Promise.all([ + const [{ current, latest }, { rETH, weth }] = await Promise.all([ getLatestEntity( ctx, OETHBalancerMetaPoolStrategy, @@ -131,25 +131,18 @@ export const updateValues = async ( : lens .getStrategyBalances(addresses.strategy) .then(({ assetBalances: [rETH, weth] }) => ({ - total: rETH + weth, rETH, weth, })), ]) if (!current) { - if ( - !latest || - latest.total !== total || - latest.rETH !== rETH || - latest.weth !== weth - ) { + if (!latest || latest.rETH !== rETH || latest.weth !== weth) { result.strategies.push( new OETHBalancerMetaPoolStrategy({ id: timestampId, blockNumber: block.header.height, timestamp: new Date(block.header.timestamp), - total: total, rETH: rETH, weth: weth, }), From ac8b2926420cca307dfa5ad72b4987c223b40f63 Mon Sep 17 00:00:00 2001 From: Chris Jacobs Date: Sat, 21 Oct 2023 10:31:03 -0700 Subject: [PATCH 08/12] remove `total` from OETHBalancerMetaPoolStrategy --- src/processors/oeth/balancer-meta-pool.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/processors/oeth/balancer-meta-pool.ts b/src/processors/oeth/balancer-meta-pool.ts index 172a4458..5e858280 100644 --- a/src/processors/oeth/balancer-meta-pool.ts +++ b/src/processors/oeth/balancer-meta-pool.ts @@ -124,10 +124,9 @@ export const updateValues = async ( ), block.header.height < originLensProxyDeployBlock ? Promise.all([ - strategy['checkBalance()'](), strategy['checkBalance(address)'](RETH_ADDRESS), strategy['checkBalance(address)'](WETH_ADDRESS), - ]).then(([total, rETH, weth]) => ({ total, rETH, weth })) + ]).then(([rETH, weth]) => ({ rETH, weth })) : lens .getStrategyBalances(addresses.strategy) .then(({ assetBalances: [rETH, weth] }) => ({ From 097c7a73b0505c949e01d45b1f670b4fe91a053c Mon Sep 17 00:00:00 2001 From: Chris Jacobs Date: Sat, 21 Oct 2023 10:37:13 -0700 Subject: [PATCH 09/12] reth eth pool seems to actually be weth reth pool --- docs/REQUIREMENTS.md | 4 ++-- src/processors/curve/curve.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/REQUIREMENTS.md b/docs/REQUIREMENTS.md index 876d7ba1..031d9cfb 100644 --- a/docs/REQUIREMENTS.md +++ b/docs/REQUIREMENTS.md @@ -168,8 +168,8 @@ const balancerMetaStablePoolABI = [ - curvePoolBalanceMetric: `poolContract.balances(0) or poolContract.balances(1)` - EthFrxEthPool: "0xa1f8a6807c402e4a15ef4eba36528a3fed24e577" - ETH frxETH - - REthEthPool: "0x0f3159811670c117c372428d4e69ac32325e4d0f" - ETH stETH - - EthStEthPool: "0xDC24316b9AE028F1497c275EB9192a3Ea0f67022" - rETH ETH + - REthEthPool: "0x0f3159811670c117c372428d4e69ac32325e4d0f" - ETH rETH + - EthStEthPool: "0xDC24316b9AE028F1497c275EB9192a3Ea0f67022" - ETH stETH - WEthStEthPool: "0x828b154032950c8ff7cf8085d841723db2696056" - WETH stETH - OEthEthPool: "0x94B17476A93b3262d87B9a326965D1E91f9c13E7" - OETH ETH diff --git a/src/processors/curve/curve.ts b/src/processors/curve/curve.ts index ca08e7e9..7841391b 100644 --- a/src/processors/curve/curve.ts +++ b/src/processors/curve/curve.ts @@ -40,7 +40,7 @@ const pools = [ count: 2, }, { - name: 'REthEthPool', + name: 'EthRethPool', address: '0x0f3159811670c117c372428d4e69ac32325e4d0f', from: Math.max(16615906, oethDeployFrom), count: 2, From d108fcf1982b0478bdd5d89dd9aad31c762bfaa2 Mon Sep 17 00:00:00 2001 From: Chris Jacobs Date: Sat, 21 Oct 2023 16:49:09 -0700 Subject: [PATCH 10/12] Add `blockFrequencyUpdater` and estimate blocks per second (`bps`) on the fly --- src/processor-templates/curve/curve.ts | 18 ++----- .../erc20-balance/erc20-balance.ts | 16 ++---- src/processor-templates/strategy/strategy.ts | 15 ++---- src/processor.ts | 9 ++++ src/processors/aave-compound.ts | 8 --- src/processors/strategies/strategies.ts | 9 +--- src/utils/blockFrequencyUpdater.ts | 50 +++++++++++++++++++ 7 files changed, 72 insertions(+), 53 deletions(-) create mode 100644 src/utils/blockFrequencyUpdater.ts diff --git a/src/processor-templates/curve/curve.ts b/src/processor-templates/curve/curve.ts index 579b0e86..f5e5e0b4 100644 --- a/src/processor-templates/curve/curve.ts +++ b/src/processor-templates/curve/curve.ts @@ -3,17 +3,13 @@ import { EvmBatchProcessor } from '@subsquid/evm-processor' import * as curveLpToken from '../../abi/curve-lp-token' import { CurvePoolBalance } from '../../model' import { Context } from '../../processor' +import { blockFrequencyUpdater } from '../../utils/blockFrequencyUpdater' import { range } from '../../utils/range' interface ProcessResult { curvePoolBalances: CurvePoolBalance[] } -const ESTIMATED_BPS = 12.06 // Circa 2023 -const SECONDS_PER_DAY = 86400 -const BLOCKS_PER_DAY = SECONDS_PER_DAY / ESTIMATED_BPS -const UPDATE_FREQUENCY = Math.floor(BLOCKS_PER_DAY) - export const createCurveSetup = ( from: number, processor: EvmBatchProcessor, @@ -26,17 +22,12 @@ export const createCurveProcessor = ( count: number, from: number, ) => { - let lastBlockHeightProcessed = 0 + const update = blockFrequencyUpdater({ from }) return async (ctx: Context) => { const result: ProcessResult = { curvePoolBalances: [], } - const nextBlockIndex = ctx.blocks.findIndex( - (b) => b.header.height >= lastBlockHeightProcessed + UPDATE_FREQUENCY, - ) - for (let i = nextBlockIndex; i < ctx.blocks.length; i += UPDATE_FREQUENCY) { - const block = ctx.blocks[i] - if (!block || block.header.height < from) continue + await update(ctx, async (ctx, block) => { const timestamp = new Date(block.header.timestamp) const timestampId = timestamp.toISOString() const contract = new curveLpToken.Contract(ctx, block.header, poolAddress) @@ -55,8 +46,7 @@ export const createCurveProcessor = ( balance2: balances[2] ?? 0n, }) result.curvePoolBalances.push(curve) - lastBlockHeightProcessed = block.header.height - } + }) await ctx.store.insert(result.curvePoolBalances) } } diff --git a/src/processor-templates/erc20-balance/erc20-balance.ts b/src/processor-templates/erc20-balance/erc20-balance.ts index 9e5b4355..45afa24c 100644 --- a/src/processor-templates/erc20-balance/erc20-balance.ts +++ b/src/processor-templates/erc20-balance/erc20-balance.ts @@ -3,6 +3,7 @@ import { EvmBatchProcessor } from '@subsquid/evm-processor' import * as erc20 from '../../abi/erc20' import { Balance } from '../../model' import { Context } from '../../processor' +import { blockFrequencyUpdater } from '../../utils/blockFrequencyUpdater' interface ProcessResult { balances: Balance[] @@ -17,26 +18,18 @@ export const createERC20BalanceProcessor = ({ from, address, token, - frequency, }: { from: number address: string token: string - frequency: number }) => { - let lastBlockHeightProcessed = 0 + const update = blockFrequencyUpdater({ from }) return async (ctx: Context) => { const result: ProcessResult = { balances: [], } - const nextBlockIndex = ctx.blocks.findIndex( - (b) => b.header.height >= lastBlockHeightProcessed + frequency, - ) - for (let i = nextBlockIndex; i < ctx.blocks.length; i += frequency) { - const block = ctx.blocks[i] - if (!block || block.header.height < from) continue + await update(ctx, async (ctx, block) => { const contract = new erc20.Contract(ctx, block.header, token) - const curve = new Balance({ id: `${token}:${address}:${block.header.height}`, blockNumber: block.header.height, @@ -46,8 +39,7 @@ export const createERC20BalanceProcessor = ({ balance: await contract.balanceOf(address), }) result.balances.push(curve) - lastBlockHeightProcessed = block.header.height - } + }) await ctx.store.insert(result.balances) } } diff --git a/src/processor-templates/strategy/strategy.ts b/src/processor-templates/strategy/strategy.ts index 99713669..31dc98e1 100644 --- a/src/processor-templates/strategy/strategy.ts +++ b/src/processor-templates/strategy/strategy.ts @@ -3,6 +3,7 @@ import { EvmBatchProcessor } from '@subsquid/evm-processor' import * as initializableAbstractStrategy from '../../abi/initializable-abstract-strategy' import { StrategyBalance } from '../../model' import { Context } from '../../processor' +import { blockFrequencyUpdater } from '../../utils/blockFrequencyUpdater' export const createStrategySetup = (from: number) => (processor: EvmBatchProcessor) => { @@ -11,28 +12,20 @@ export const createStrategySetup = export const createStrategyProcessor = ({ from, - frequency, address, assets, }: { from: number - frequency: number address: string assets: readonly string[] }) => { - let lastBlockHeightProcessed = 0 + const update = blockFrequencyUpdater({ from }) let lastStrategyBalances = new Map() return async (ctx: Context) => { const results = { strategyBalances: [] as StrategyBalance[], } - const nextBlockIndex = ctx.blocks.findIndex( - (b) => b.header.height >= lastBlockHeightProcessed + frequency, - ) - for (let i = nextBlockIndex; i < ctx.blocks.length; i += frequency) { - const block = ctx.blocks[i] - if (!block || block.header.height < from) continue - + await update(ctx, async (ctx, block) => { const contract = new initializableAbstractStrategy.Contract( ctx, block.header, @@ -73,7 +66,7 @@ export const createStrategyProcessor = ({ lastStrategyBalances.set(asset, strategyBalance) } } - } + }) await ctx.store.insert(results.strategyBalances) } } diff --git a/src/processor.ts b/src/processor.ts index 0ac92c16..ddb6c5d5 100644 --- a/src/processor.ts +++ b/src/processor.ts @@ -91,6 +91,14 @@ export const run = ({ const ctx = _ctx as Context try { ctx.__state = new Map() + if (ctx.blocks.length > 1) { + const timespan = + ctx.blocks[ctx.blocks.length - 1].header.timestamp - + ctx.blocks[0].header.timestamp + ctx.bps = timespan / (ctx.blocks.length - 1) / 1000 + ctx.log.info({ bps: ctx.bps }) + } + let start: number const time = (name: string) => () => { const message = `${name} ${Date.now() - start}ms` @@ -177,6 +185,7 @@ export type Fields = EvmBatchProcessorFields< ReturnType > export type Context = DataHandlerContext & { + bps: number __state: Map } export type Block = Context['blocks']['0'] diff --git a/src/processors/aave-compound.ts b/src/processors/aave-compound.ts index b82dbcc5..eff666c5 100644 --- a/src/processors/aave-compound.ts +++ b/src/processors/aave-compound.ts @@ -4,29 +4,21 @@ import { createERC20BalanceProcessor, } from '../processor-templates/erc20-balance' -const ESTIMATED_BPS = 12.06 // Circa 2023 -const SECONDS_PER_DAY = 86400 -const BLOCKS_PER_DAY = SECONDS_PER_DAY / ESTIMATED_BPS -const UPDATE_FREQUENCY = Math.floor(BLOCKS_PER_DAY) - const tracks: Parameters[0][] = [ { from: 11362821, address: '0x3Ed3B47Dd13EC9a98b44e6204A523E766B225811', // aUSDT token: '0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT - frequency: UPDATE_FREQUENCY, }, { from: 11367200, address: '0xBcca60bB61934080951369a648Fb03DF4F96263C', // aUSDC token: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC - frequency: UPDATE_FREQUENCY, }, { from: 11367184, address: '0x028171bca77440897b824ca71d1c56cac55b68a3', // aDAI token: '0x6b175474e89094c44da98b954eedeac495271d0f', // DAI - frequency: UPDATE_FREQUENCY, }, ] diff --git a/src/processors/strategies/strategies.ts b/src/processors/strategies/strategies.ts index 6370d5df..c4fb7c9f 100644 --- a/src/processors/strategies/strategies.ts +++ b/src/processors/strategies/strategies.ts @@ -6,11 +6,6 @@ import { createStrategySetup, } from '../../processor-templates/strategy' -const ESTIMATED_BPS = 12.06 // Circa 2023 -const SECONDS_PER_DAY = 86400 -const BLOCKS_PER_DAY = SECONDS_PER_DAY / ESTIMATED_BPS -const UPDATE_FREQUENCY = Math.floor(BLOCKS_PER_DAY) - const DAI = '0x6b175474e89094c44da98b954eedeac495271d0f'.toLowerCase() const USDT = '0xdac17f958d2ee523a2206206994597c13d831ec7'.toLowerCase() const USDC = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'.toLowerCase() @@ -109,9 +104,7 @@ export const setup = (processor: EvmBatchProcessor) => { strategies.forEach((s) => createStrategySetup(s.from)(processor)) } -const processors = strategies.map((s) => - createStrategyProcessor({ ...s, frequency: UPDATE_FREQUENCY }), -) +const processors = strategies.map(createStrategyProcessor) export const process = async (ctx: Context) => { await Promise.all(processors.map((p) => p(ctx))) diff --git a/src/utils/blockFrequencyUpdater.ts b/src/utils/blockFrequencyUpdater.ts new file mode 100644 index 00000000..98eb6bea --- /dev/null +++ b/src/utils/blockFrequencyUpdater.ts @@ -0,0 +1,50 @@ +import dayjs from 'dayjs' +import duration from 'dayjs/plugin/duration' + +import { Block, Context } from '../processor' + +dayjs.extend(duration) + +const SECONDS_PER_WEEK = dayjs.duration({ weeks: 1 }).asSeconds() +const SECONDS_PER_DAY = dayjs.duration({ days: 1 }).asSeconds() + +const oneYearAgo = dayjs().subtract(1, 'year') +const oneMonthAgo = dayjs().subtract(1, 'month') + +const getFrequency = (bps: number, timestamp: number) => { + if (dayjs(timestamp).isBefore(oneYearAgo)) { + return (SECONDS_PER_WEEK / bps) ^ 0 + } else if (dayjs(timestamp).isBefore(oneMonthAgo)) { + return (SECONDS_PER_DAY / bps) ^ 0 + } + return (SECONDS_PER_DAY / bps / 96) ^ 0 +} + +export const blockFrequencyUpdater = (params: { from: number }) => { + let lastBlockHeightProcessed = 0 + return async ( + ctx: Context, + fn: (ctx: Context, block: Block) => Promise, + ) => { + // If we're at head, always process. + if (ctx.isHead) { + for (const block of ctx.blocks) { + await fn(ctx, block) + } + } + + // If we're not at head, determine our frequency and then process. + const { bps } = ctx + let frequency: number = getFrequency(bps, ctx.blocks[0].header.timestamp) + const nextBlockIndex = ctx.blocks.findIndex( + (b) => b.header.height >= lastBlockHeightProcessed + frequency, + ) + for (let i = nextBlockIndex; i < ctx.blocks.length; i += frequency) { + const block = ctx.blocks[i] + if (!block || block.header.height < params.from) continue + await fn(ctx, block) + lastBlockHeightProcessed = block.header.height + frequency = getFrequency(bps, block.header.timestamp) + } + } +} From b31fb10528cc2439f704d91e2fe7e56b2503d459 Mon Sep 17 00:00:00 2001 From: Chris Jacobs Date: Sat, 21 Oct 2023 17:15:10 -0700 Subject: [PATCH 11/12] Add `blockFrequencyUpdater` and estimate blocks per second (`bps`) on the fly --- src/processor.ts | 2 -- src/utils/blockFrequencyUpdater.ts | 28 ++++++++++++++++------------ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/processor.ts b/src/processor.ts index ddb6c5d5..f34d7f1a 100644 --- a/src/processor.ts +++ b/src/processor.ts @@ -4,7 +4,6 @@ import { EvmBatchProcessor, EvmBatchProcessorFields, } from '@subsquid/evm-processor' -import { createLogger } from '@subsquid/logger' import { Store, TypeormDatabase } from '@subsquid/typeorm-store' export const createSquidProcessor = () => @@ -96,7 +95,6 @@ export const run = ({ ctx.blocks[ctx.blocks.length - 1].header.timestamp - ctx.blocks[0].header.timestamp ctx.bps = timespan / (ctx.blocks.length - 1) / 1000 - ctx.log.info({ bps: ctx.bps }) } let start: number diff --git a/src/utils/blockFrequencyUpdater.ts b/src/utils/blockFrequencyUpdater.ts index 98eb6bea..e4e2ff78 100644 --- a/src/utils/blockFrequencyUpdater.ts +++ b/src/utils/blockFrequencyUpdater.ts @@ -7,17 +7,27 @@ dayjs.extend(duration) const SECONDS_PER_WEEK = dayjs.duration({ weeks: 1 }).asSeconds() const SECONDS_PER_DAY = dayjs.duration({ days: 1 }).asSeconds() +const SECONDS_PER_MINUTE = 60 -const oneYearAgo = dayjs().subtract(1, 'year') -const oneMonthAgo = dayjs().subtract(1, 'month') +const oneYearAgo = dayjs().subtract(1, 'year').valueOf() +const oneMonthAgo = dayjs().subtract(1, 'month').valueOf() +const oneWeekAgo = dayjs().subtract(1, 'week').valueOf() +const oneDayAgo = dayjs().subtract(1, 'day').valueOf() +const oneHourAgo = dayjs().subtract(1, 'hour').valueOf() const getFrequency = (bps: number, timestamp: number) => { - if (dayjs(timestamp).isBefore(oneYearAgo)) { + if (timestamp < oneYearAgo) { return (SECONDS_PER_WEEK / bps) ^ 0 - } else if (dayjs(timestamp).isBefore(oneMonthAgo)) { + } else if (timestamp < oneMonthAgo) { return (SECONDS_PER_DAY / bps) ^ 0 + } else if (timestamp < oneWeekAgo) { + return (SECONDS_PER_DAY / bps / 4) ^ 0 + } else if (timestamp < oneDayAgo) { + return (SECONDS_PER_DAY / bps / 24) ^ 0 + } else if (timestamp < oneHourAgo) { + return ((SECONDS_PER_MINUTE * 5) / bps) ^ 0 } - return (SECONDS_PER_DAY / bps / 96) ^ 0 + return 1 } export const blockFrequencyUpdater = (params: { from: number }) => { @@ -26,13 +36,7 @@ export const blockFrequencyUpdater = (params: { from: number }) => { ctx: Context, fn: (ctx: Context, block: Block) => Promise, ) => { - // If we're at head, always process. - if (ctx.isHead) { - for (const block of ctx.blocks) { - await fn(ctx, block) - } - } - + if (!ctx.blocks.length) return // If we're not at head, determine our frequency and then process. const { bps } = ctx let frequency: number = getFrequency(bps, ctx.blocks[0].header.timestamp) From 70425d5db03bb2e0014e0d169286d864b68eaf53 Mon Sep 17 00:00:00 2001 From: Chris Jacobs Date: Sun, 22 Oct 2023 09:48:48 -0700 Subject: [PATCH 12/12] Prep `strategy` for Shah to work with --- src/processor-templates/strategy/strategy.ts | 47 +------ src/processors/strategies/strategies.ts | 128 +++++++++---------- 2 files changed, 66 insertions(+), 109 deletions(-) diff --git a/src/processor-templates/strategy/strategy.ts b/src/processor-templates/strategy/strategy.ts index 31dc98e1..8547d92d 100644 --- a/src/processor-templates/strategy/strategy.ts +++ b/src/processor-templates/strategy/strategy.ts @@ -10,62 +10,23 @@ export const createStrategySetup = processor.includeAllBlocks({ from }) } +// Used by `src/processors/strategies/strategies.ts` export const createStrategyProcessor = ({ from, address, - assets, + kind, }: { from: number address: string - assets: readonly string[] + kind: 'CurveAMO' | 'BalancerMetaStablePool' | 'BalancerComposableStablePool' }) => { const update = blockFrequencyUpdater({ from }) - let lastStrategyBalances = new Map() return async (ctx: Context) => { const results = { strategyBalances: [] as StrategyBalance[], } await update(ctx, async (ctx, block) => { - const contract = new initializableAbstractStrategy.Contract( - ctx, - block.header, - address, - ) - for (const asset of assets) { - const lastStrategyBalance = - lastStrategyBalances.get(asset) ?? - (await ctx.store.findOne(StrategyBalance, { - where: { strategy: address, asset }, - order: { - id: 'desc', - }, - })) - const balance = await contract.checkBalance(asset).catch((err) => { - if ( - err.message === 'execution reverted: aToken does not exist' || - err.message === 'execution reverted: pToken does not exist' || - err.message === 'execution reverted: Unsupported asset' || - err.message === 'execution reverted: Unexpected asset address' - ) { - return undefined - } - ctx.log.info({ address, asset }, 'retrieving strategy balance failed') - throw err - }) - if (balance === undefined) continue - if (!lastStrategyBalance || lastStrategyBalance.balance !== balance) { - const strategyBalance = new StrategyBalance({ - id: `${address}:${asset}:${block.header.height}`, - blockNumber: block.header.height, - timestamp: new Date(block.header.timestamp), - strategy: address, - asset, - balance, - }) - results.strategyBalances.push(strategyBalance) - lastStrategyBalances.set(asset, strategyBalance) - } - } + // TODO: Process }) await ctx.store.insert(results.strategyBalances) } diff --git a/src/processors/strategies/strategies.ts b/src/processors/strategies/strategies.ts index c4fb7c9f..57d383e0 100644 --- a/src/processors/strategies/strategies.ts +++ b/src/processors/strategies/strategies.ts @@ -6,97 +6,93 @@ import { createStrategySetup, } from '../../processor-templates/strategy' -const DAI = '0x6b175474e89094c44da98b954eedeac495271d0f'.toLowerCase() -const USDT = '0xdac17f958d2ee523a2206206994597c13d831ec7'.toLowerCase() -const USDC = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'.toLowerCase() -const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'.toLowerCase() -const stETH = '0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84'.toLowerCase() -const rETH = '0xae78736Cd615f374D3085123A210448E74Fc6393'.toLowerCase() -const frxETH = '0x5E8422345238F34275888049021821E8E08CAa1f'.toLowerCase() +// const DAI = '0x6b175474e89094c44da98b954eedeac495271d0f'.toLowerCase() +// const USDT = '0xdac17f958d2ee523a2206206994597c13d831ec7'.toLowerCase() +// const USDC = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'.toLowerCase() -const ousdStrategies = [ - { - from: 14206832, // 13369326, Initial Deploy - name: 'OUSD Aave', - address: '0x5e3646a1db86993f73e6b74a57d8640b69f7e259'.toLowerCase(), - assets: [DAI, USDT, USDC], - }, - { - from: 15896478, - name: 'OUSD Convex OUSD+3Crv (AMO)', - address: '0x89eb88fedc50fc77ae8a18aad1ca0ac27f777a90'.toLowerCase(), - assets: [DAI, USDT, USDC], - }, - { - from: 15949661, - name: 'OUSD Morpho Compound', - address: '0x5a4eee58744d1430876d5ca93cab5ccb763c037d'.toLowerCase(), - assets: [DAI, USDT, USDC], - }, - { - from: 16331911, - name: 'OUSD Morpho Aave', - address: '0x79F2188EF9350A1dC11A062cca0abE90684b0197'.toLowerCase(), - assets: [DAI, USDT, USDC], - }, - { - from: 17877308, - name: 'OUSD Flux', - address: '0x76Bf500B6305Dc4ea851384D3d5502f1C7a0ED44'.toLowerCase(), - assets: [DAI, USDT, USDC], - }, - { - from: 17883036, - name: 'OUSD Maker DSR', - address: '0x6b69B755C629590eD59618A2712d8a2957CA98FC'.toLowerCase(), - assets: [DAI, USDT, USDC], - }, - // Deprecated - // { - // from: 13369299, - // name: 'CompoundStrategy', - // address: '0x9c459eeb3fa179a40329b81c1635525e9a0ef094'.toLowerCase(), - // }, - // { - // from: 13639477, - // name: 'ConvexStrategy', - // address: '0xea2ef2e2e5a749d4a66b41db9ad85a38aa264cb3'.toLowerCase(), - // }, - // { - // from: 16226229, - // name: 'LUSDMetaStrategy', - // address: '0x7A192DD9Cc4Ea9bdEdeC9992df74F1DA55e60a19'.toLowerCase(), - // }, -] as const +// const ousdStrategies = [ +// { +// from: 14206832, // 13369326, Initial Deploy +// name: 'OUSD Aave', +// address: '0x5e3646a1db86993f73e6b74a57d8640b69f7e259'.toLowerCase(), +// assets: [DAI, USDT, USDC], +// }, +// { +// from: 15896478, +// name: 'OUSD Convex OUSD+3Crv (AMO)', +// address: '0x89eb88fedc50fc77ae8a18aad1ca0ac27f777a90'.toLowerCase(), +// assets: [DAI, USDT, USDC], +// }, +// { +// from: 15949661, +// name: 'OUSD Morpho Compound', +// address: '0x5a4eee58744d1430876d5ca93cab5ccb763c037d'.toLowerCase(), +// assets: [DAI, USDT, USDC], +// }, +// { +// from: 16331911, +// name: 'OUSD Morpho Aave', +// address: '0x79F2188EF9350A1dC11A062cca0abE90684b0197'.toLowerCase(), +// assets: [DAI, USDT, USDC], +// }, +// { +// from: 17877308, +// name: 'OUSD Flux', +// address: '0x76Bf500B6305Dc4ea851384D3d5502f1C7a0ED44'.toLowerCase(), +// assets: [DAI, USDT, USDC], +// }, +// { +// from: 17883036, +// name: 'OUSD Maker DSR', +// address: '0x6b69B755C629590eD59618A2712d8a2957CA98FC'.toLowerCase(), +// assets: [DAI, USDT, USDC], +// }, +// Deprecated +// { +// from: 13369299, +// name: 'CompoundStrategy', +// address: '0x9c459eeb3fa179a40329b81c1635525e9a0ef094'.toLowerCase(), +// }, +// { +// from: 13639477, +// name: 'ConvexStrategy', +// address: '0xea2ef2e2e5a749d4a66b41db9ad85a38aa264cb3'.toLowerCase(), +// }, +// { +// from: 16226229, +// name: 'LUSDMetaStrategy', +// address: '0x7A192DD9Cc4Ea9bdEdeC9992df74F1DA55e60a19'.toLowerCase(), +// }, +// ] as const const oethStrategies = [ { from: 18083920, name: 'OETH Convex ETH+OETH (AMO)', address: '0x1827F9eA98E0bf96550b2FC20F7233277FcD7E63'.toLowerCase(), - assets: [WETH, stETH, rETH, frxETH], + kind: 'CurveAMO', }, { from: 17513633, name: 'OETH Frax Staking', address: '0x3fF8654D633D4Ea0faE24c52Aec73B4A20D0d0e5'.toLowerCase(), - assets: [WETH, stETH, rETH, frxETH], + kind: 'CurveAMO', }, { from: 17612333, name: 'OETH Morpho Aave V2', address: '0xc1fc9E5eC3058921eA5025D703CBE31764756319'.toLowerCase(), - assets: [WETH, stETH, rETH, frxETH], + kind: 'CurveAMO', }, { from: 18156225, name: 'OETH Aura rETH/WETH', address: '0x49109629aC1deB03F2e9b2fe2aC4a623E0e7dfDC'.toLowerCase(), - assets: [WETH, stETH, rETH, frxETH], + kind: 'BalancerMetaStablePool', }, ] as const -const strategies = [...ousdStrategies, ...oethStrategies] +const strategies = oethStrategies export const from = Math.min(...strategies.map((s) => s.from))