From e90a9eb9208763af3de147f68920f163734c80e8 Mon Sep 17 00:00:00 2001 From: RadioJulius <9730701+RadioJulius@users.noreply.github.com> Date: Fri, 4 Nov 2022 11:13:13 -0700 Subject: [PATCH 01/14] attempted deploy and changing license to BSL1.1 --- .openzeppelin/unknown-250.json | 484 ++++++++++++++++++++++ contracts/MagicatsHandlerUpgradeable.sol | 2 +- contracts/ReaperAutoCompoundXBoo.sol | 2 +- contracts/abstract/ReaperBaseStrategy.sol | 2 +- package-lock.json | 58 +-- package.json | 2 +- scripts/deploy/deploy-1-strategy.js | 35 ++ scripts/deploy/deploy-1-vault.js | 31 -- scripts/deploy/deploy-2-strategy.js | 27 -- scripts/deploy/deploy-3-initialize.js | 18 - scripts/deploy/deploy-4-paymentRouter.js | 23 - scripts/deploy/deploy-5-addPools.js | 21 - scripts/deploy/deploy.js | 72 ---- yarn.lock | 28 +- 14 files changed, 566 insertions(+), 239 deletions(-) create mode 100644 .openzeppelin/unknown-250.json create mode 100644 scripts/deploy/deploy-1-strategy.js delete mode 100644 scripts/deploy/deploy-1-vault.js delete mode 100644 scripts/deploy/deploy-2-strategy.js delete mode 100644 scripts/deploy/deploy-3-initialize.js delete mode 100644 scripts/deploy/deploy-4-paymentRouter.js delete mode 100644 scripts/deploy/deploy-5-addPools.js delete mode 100644 scripts/deploy/deploy.js diff --git a/.openzeppelin/unknown-250.json b/.openzeppelin/unknown-250.json new file mode 100644 index 0000000..fde35d7 --- /dev/null +++ b/.openzeppelin/unknown-250.json @@ -0,0 +1,484 @@ +{ + "manifestVersion": "3.2", + "proxies": [ + { + "address": "0x1320da7DB7617c9Fe02dca65c064864E1321a23E", + "txHash": "0xf6b2d22d6ef610b0255bcf17f25f06291a0c90d7893c3c2845dc0d81d08b58ee", + "kind": "uups" + } + ], + "impls": { + "3a214a2c4fc8fda31ba245dc77929a6256a513098a330a42599b3656ea266be4": { + "address": "0x514A94BC22877f245B36cd7fd6EcDFB8Db7a5EB9", + "txHash": "0x6d446ce1bb4c6cfba02e857bc6841e03de52ad1a374e46e441b180ae701b73a8", + "layout": { + "solcVersion": "0.8.9", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC1967UpgradeUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol:211" + }, + { + "label": "__gap", + "offset": 0, + "slot": "51", + "type": "t_array(t_uint256)50_storage", + "contract": "UUPSUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol:107" + }, + { + "label": "__gap", + "offset": 0, + "slot": "101", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" + }, + { + "label": "__gap", + "offset": 0, + "slot": "151", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC165Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol:41" + }, + { + "label": "_roles", + "offset": 0, + "slot": "201", + "type": "t_mapping(t_bytes32,t_struct(RoleData)179_storage)", + "contract": "AccessControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:61" + }, + { + "label": "__gap", + "offset": 0, + "slot": "202", + "type": "t_array(t_uint256)49_storage", + "contract": "AccessControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:259" + }, + { + "label": "_roleMembers", + "offset": 0, + "slot": "251", + "type": "t_mapping(t_bytes32,t_struct(AddressSet)4161_storage)", + "contract": "AccessControlEnumerableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "252", + "type": "t_array(t_uint256)49_storage", + "contract": "AccessControlEnumerableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol:76" + }, + { + "label": "_paused", + "offset": 0, + "slot": "301", + "type": "t_bool", + "contract": "PausableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:29" + }, + { + "label": "__gap", + "offset": 0, + "slot": "302", + "type": "t_array(t_uint256)49_storage", + "contract": "PausableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:116" + }, + { + "label": "harvestLog", + "offset": 0, + "slot": "351", + "type": "t_array(t_struct(Harvest)13763_storage)dyn_storage", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:25" + }, + { + "label": "harvestLogCadence", + "offset": 0, + "slot": "352", + "type": "t_uint256", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:26" + }, + { + "label": "lastHarvestTimestamp", + "offset": 0, + "slot": "353", + "type": "t_uint256", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:27" + }, + { + "label": "upgradeProposalTime", + "offset": 0, + "slot": "354", + "type": "t_uint256", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:29" + }, + { + "label": "cascadingAccess", + "offset": 0, + "slot": "355", + "type": "t_array(t_bytes32)dyn_storage", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:50" + }, + { + "label": "treasury", + "offset": 0, + "slot": "356", + "type": "t_address", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:58" + }, + { + "label": "vault", + "offset": 0, + "slot": "357", + "type": "t_address", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:59" + }, + { + "label": "strategistRemitter", + "offset": 0, + "slot": "358", + "type": "t_address", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:60" + }, + { + "label": "totalFee", + "offset": 0, + "slot": "359", + "type": "t_uint256", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:86" + }, + { + "label": "callFee", + "offset": 0, + "slot": "360", + "type": "t_uint256", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:87" + }, + { + "label": "treasuryFee", + "offset": 0, + "slot": "361", + "type": "t_uint256", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:88" + }, + { + "label": "strategistFee", + "offset": 0, + "slot": "362", + "type": "t_uint256", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:89" + }, + { + "label": "securityFee", + "offset": 0, + "slot": "363", + "type": "t_uint256", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:90" + }, + { + "label": "magicatsHandler", + "offset": 0, + "slot": "364", + "type": "t_address", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:45" + }, + { + "label": "aceLab", + "offset": 0, + "slot": "365", + "type": "t_address", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:46" + }, + { + "label": "WFTMToBOOPath", + "offset": 0, + "slot": "366", + "type": "t_array(t_address)dyn_storage", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:54" + }, + { + "label": "WFTMToUSDCPath", + "offset": 0, + "slot": "367", + "type": "t_array(t_address)dyn_storage", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:55" + }, + { + "label": "poolRewardToWFTMPaths", + "offset": 0, + "slot": "368", + "type": "t_mapping(t_uint256,t_array(t_address)dyn_storage)", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:56" + }, + { + "label": "currentPoolId", + "offset": 0, + "slot": "369", + "type": "t_uint256", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:62" + }, + { + "label": "totalPoolBalance", + "offset": 0, + "slot": "370", + "type": "t_uint256", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:70" + }, + { + "label": "poolXBOOBalance", + "offset": 0, + "slot": "371", + "type": "t_mapping(t_uint256,t_uint256)", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:71" + }, + { + "label": "depositedPools", + "offset": 0, + "slot": "372", + "type": "t_struct(UintSet)4318_storage", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:72" + }, + { + "label": "accCatDebt", + "offset": 0, + "slot": "374", + "type": "t_mapping(t_uint256,t_uint256)", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:79" + }, + { + "label": "catProvisionFee", + "offset": 0, + "slot": "375", + "type": "t_uint256", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:80" + }, + { + "label": "idToSpecialHandler", + "offset": 0, + "slot": "376", + "type": "t_mapping(t_uint256,t_struct(RewardHandler)11240_storage)", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:88" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_address)dyn_storage": { + "label": "address[]", + "numberOfBytes": "32" + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]", + "numberOfBytes": "32" + }, + "t_array(t_struct(Harvest)13763_storage)dyn_storage": { + "label": "struct ReaperBaseStrategyv3.Harvest[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(AddressSet)4161_storage)": { + "label": "mapping(bytes32 => struct EnumerableSetUpgradeable.AddressSet)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(RoleData)179_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_array(t_address)dyn_storage)": { + "label": "mapping(uint256 => address[])", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_struct(RewardHandler)11240_storage)": { + "label": "mapping(uint256 => struct ReaperAutoCompoundXBoov2.RewardHandler)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_uint256)": { + "label": "mapping(uint256 => uint256)", + "numberOfBytes": "32" + }, + "t_struct(AddressSet)4161_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)3860_storage", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Harvest)13763_storage": { + "label": "struct ReaperBaseStrategyv3.Harvest", + "members": [ + { + "label": "timestamp", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "vaultSharePrice", + "type": "t_uint256", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(RewardHandler)11240_storage": { + "label": "struct ReaperAutoCompoundXBoov2.RewardHandler", + "members": [ + { + "label": "requiresSpecialHandling", + "type": "t_bool", + "offset": 0, + "slot": "0" + }, + { + "label": "handler", + "type": "t_address", + "offset": 1, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(RoleData)179_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "members", + "type": "t_mapping(t_address,t_bool)", + "offset": 0, + "slot": "0" + }, + { + "label": "adminRole", + "type": "t_bytes32", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Set)3860_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage", + "offset": 0, + "slot": "0" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(UintSet)4318_storage": { + "label": "struct EnumerableSetUpgradeable.UintSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)3860_storage", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "64" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + } + } + } + } +} diff --git a/contracts/MagicatsHandlerUpgradeable.sol b/contracts/MagicatsHandlerUpgradeable.sol index b2f570c..2218b2d 100644 --- a/contracts/MagicatsHandlerUpgradeable.sol +++ b/contracts/MagicatsHandlerUpgradeable.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: AGPLv3 +// SPDX-License-Identifier: BSL1.1 pragma solidity ^0.8.9; diff --git a/contracts/ReaperAutoCompoundXBoo.sol b/contracts/ReaperAutoCompoundXBoo.sol index 7cca549..138a1ba 100644 --- a/contracts/ReaperAutoCompoundXBoo.sol +++ b/contracts/ReaperAutoCompoundXBoo.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: AGPLv3 +// SPDX-License-Identifier: BSL1.1 pragma solidity 0.8.9; diff --git a/contracts/abstract/ReaperBaseStrategy.sol b/contracts/abstract/ReaperBaseStrategy.sol index 143f2f4..d8756b1 100644 --- a/contracts/abstract/ReaperBaseStrategy.sol +++ b/contracts/abstract/ReaperBaseStrategy.sol @@ -117,7 +117,7 @@ abstract contract ReaperBaseStrategyv3 is totalFee = 450; callFee = 1000; treasuryFee = 9000; - strategistFee = 2500; + strategistFee = 0; securityFee = 0; vault = _vault; diff --git a/package-lock.json b/package-lock.json index 3aee2a1..77ead28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ }, "devDependencies": { "@nomiclabs/hardhat-ethers": "^2.1.1", - "@openzeppelin/hardhat-upgrades": "^1.20.0", + "@openzeppelin/hardhat-upgrades": "^1.21.0", "ethers": "^5.7.0", "hardhat": "^2.10.1", "hardhat-contract-sizer": "^2.3.0", @@ -4721,12 +4721,12 @@ "integrity": "sha512-+wuegAMaLcZnLCJIvrVUDzA9z/Wp93f0Dla/4jJvIhijRrPabjQbZe6fWiECLaJyfn5ci9fqf9vTw3xpQOad2A==" }, "node_modules/@openzeppelin/hardhat-upgrades": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-1.20.0.tgz", - "integrity": "sha512-ign7fc/ZdPe+KAYCB91619o+wlBr7sIEEt1nqLhoXAJ9f0qVuXkwAaTdLB0MTSWH85TzlUUT2fTJp1ZnZ1o4LQ==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-1.21.0.tgz", + "integrity": "sha512-Kwl7IN0Hlhj4HluMTTl0DrtU90OI/Q6rG3sAyd2pv3fababe9EuZqs9DydOlkWM45JwTzC+eBzX3TgHsqI13eA==", "dev": true, "dependencies": { - "@openzeppelin/upgrades-core": "^1.18.0", + "@openzeppelin/upgrades-core": "^1.20.0", "chalk": "^4.1.0", "debug": "^4.1.1", "proper-lockfile": "^4.1.1" @@ -4770,14 +4770,14 @@ "dev": true }, "node_modules/@openzeppelin/upgrades-core": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/@openzeppelin/upgrades-core/-/upgrades-core-1.18.0.tgz", - "integrity": "sha512-fFp5sscGC876yhq7BU595LG45yky21sZFa6cDJigluUjAyJSPoLwF7GD9bSwQMMo4jC7ii1UJBtLipUxN6PVTA==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/@openzeppelin/upgrades-core/-/upgrades-core-1.20.4.tgz", + "integrity": "sha512-Y4/+BPIbnopfE6ZhPOE2CD0V5fnvDxKKm7+kryx5+WrcRCTW3B5DjbXL9xyyoviG8Rn5EXUh5Fk1GLbiYDfu0g==", "dev": true, "dependencies": { "cbor": "^8.0.0", "chalk": "^4.1.0", - "compare-versions": "^4.0.0", + "compare-versions": "^5.0.0", "debug": "^4.1.1", "ethereumjs-util": "^7.0.3", "proper-lockfile": "^4.1.1", @@ -4785,9 +4785,9 @@ } }, "node_modules/@openzeppelin/upgrades-core/node_modules/@types/bn.js": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", - "integrity": "sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", "dev": true, "dependencies": { "@types/node": "*" @@ -9761,9 +9761,9 @@ "license": "MIT" }, "node_modules/compare-versions": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-4.1.3.tgz", - "integrity": "sha512-WQfnbDcrYnGr55UwbxKiQKASnTtNnaAWVi8jZyy8NTpVAXWACSne8lMD1iaIo9AiU6mnuLvSVshCzewVuWxHUg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-5.0.1.tgz", + "integrity": "sha512-v8Au3l0b+Nwkp4G142JcgJFh1/TUhdxut7wzD1Nq1dyp5oa3tXaqb03EXOAB6jS4gMlalkjAUPZBMiAfKUixHQ==", "dev": true }, "node_modules/component-emitter": { @@ -33808,12 +33808,12 @@ "integrity": "sha512-+wuegAMaLcZnLCJIvrVUDzA9z/Wp93f0Dla/4jJvIhijRrPabjQbZe6fWiECLaJyfn5ci9fqf9vTw3xpQOad2A==" }, "@openzeppelin/hardhat-upgrades": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-1.20.0.tgz", - "integrity": "sha512-ign7fc/ZdPe+KAYCB91619o+wlBr7sIEEt1nqLhoXAJ9f0qVuXkwAaTdLB0MTSWH85TzlUUT2fTJp1ZnZ1o4LQ==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-1.21.0.tgz", + "integrity": "sha512-Kwl7IN0Hlhj4HluMTTl0DrtU90OI/Q6rG3sAyd2pv3fababe9EuZqs9DydOlkWM45JwTzC+eBzX3TgHsqI13eA==", "dev": true, "requires": { - "@openzeppelin/upgrades-core": "^1.18.0", + "@openzeppelin/upgrades-core": "^1.20.0", "chalk": "^4.1.0", "debug": "^4.1.1", "proper-lockfile": "^4.1.1" @@ -33837,14 +33837,14 @@ } }, "@openzeppelin/upgrades-core": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/@openzeppelin/upgrades-core/-/upgrades-core-1.18.0.tgz", - "integrity": "sha512-fFp5sscGC876yhq7BU595LG45yky21sZFa6cDJigluUjAyJSPoLwF7GD9bSwQMMo4jC7ii1UJBtLipUxN6PVTA==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/@openzeppelin/upgrades-core/-/upgrades-core-1.20.4.tgz", + "integrity": "sha512-Y4/+BPIbnopfE6ZhPOE2CD0V5fnvDxKKm7+kryx5+WrcRCTW3B5DjbXL9xyyoviG8Rn5EXUh5Fk1GLbiYDfu0g==", "dev": true, "requires": { "cbor": "^8.0.0", "chalk": "^4.1.0", - "compare-versions": "^4.0.0", + "compare-versions": "^5.0.0", "debug": "^4.1.1", "ethereumjs-util": "^7.0.3", "proper-lockfile": "^4.1.1", @@ -33852,9 +33852,9 @@ }, "dependencies": { "@types/bn.js": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", - "integrity": "sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", "dev": true, "requires": { "@types/node": "*" @@ -37554,9 +37554,9 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" }, "compare-versions": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-4.1.3.tgz", - "integrity": "sha512-WQfnbDcrYnGr55UwbxKiQKASnTtNnaAWVi8jZyy8NTpVAXWACSne8lMD1iaIo9AiU6mnuLvSVshCzewVuWxHUg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-5.0.1.tgz", + "integrity": "sha512-v8Au3l0b+Nwkp4G142JcgJFh1/TUhdxut7wzD1Nq1dyp5oa3tXaqb03EXOAB6jS4gMlalkjAUPZBMiAfKUixHQ==", "dev": true }, "component-emitter": { diff --git a/package.json b/package.json index 399f9df..49abaa3 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ }, "devDependencies": { "@nomiclabs/hardhat-ethers": "^2.1.1", - "@openzeppelin/hardhat-upgrades": "^1.20.0", + "@openzeppelin/hardhat-upgrades": "^1.21.0", "ethers": "^5.7.0", "hardhat": "^2.10.1", "hardhat-contract-sizer": "^2.3.0", diff --git a/scripts/deploy/deploy-1-strategy.js b/scripts/deploy/deploy-1-strategy.js new file mode 100644 index 0000000..c421792 --- /dev/null +++ b/scripts/deploy/deploy-1-strategy.js @@ -0,0 +1,35 @@ +async function main() { + const vault = "0xFC550BAD3c14160CBA7bc05ee263b3F060149AFF" + const strategist1 = "0x1E71AEE6081f62053123140aacC7a06021D77348"; + const strategist2 = "0x81876677843D00a7D792E1617459aC2E93202576"; + const strategist3 = "0x1A20D7A31e5B3Bc5f02c8A146EF6f394502a10c4"; + const strategist4 = "0x60BC5E0440C867eEb4CbcE84bB1123fad2b262B1"; + const treasuryAddress = "0x0e7c5313E9BB80b654734d9b7aB1FB01468deE3b"; + const superAdminMultisig = "0x04C710a1E8a738CDf7cAD3a52Ba77A784C35d8CE"; + const AdminMultisig = "0x539eF36C804e4D735d8cAb69e8e441c12d4B88E0"; + const GaurdianMultisig = "0xf20E25f2AB644C8ecBFc992a6829478a85A98F2c"; + + // get artifacts + const ReaperAutoCompoundXBoov2 = await ethers.getContractFactory('ReaperAutoCompoundXBoov2'); + + reaperAutoCompoundXBoov2 = await hre.upgrades.deployProxy( + ReaperAutoCompoundXBoov2, + [ + vault, //vault + [treasuryAddress, treasuryAddress], //feeRemitters + [strategist1,strategist2,strategist3,strategist4],//strategists + [superAdminMultisig,AdminMultisig,GaurdianMultisig]//multisigRoles + ], + {kind: 'uups'}, + ); + await reaperAutoCompoundXBoov2.deployed(); + + console.log('ReaperAutoCompoundXBoov2 deployed to:', reaperAutoCompoundXBoov2.address); +} + + main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); \ No newline at end of file diff --git a/scripts/deploy/deploy-1-vault.js b/scripts/deploy/deploy-1-vault.js deleted file mode 100644 index 8fc3803..0000000 --- a/scripts/deploy/deploy-1-vault.js +++ /dev/null @@ -1,31 +0,0 @@ -async function main() { - const Vault = await ethers.getContractFactory("ReaperVaultv1_3"); - - const booAddress = "0x841FAD6EAe12c286d1Fd18d1d525DFfA75C7EFFE"; - const tokenName = "XBOO Single Stake Vault"; - const tokenSymbol = "rfXBOO"; - const approvalDelay = 86400; - const depositFee = 0; - const tvlCap = ethers.constants.MaxUint256; - const options = { gasPrice: 500000000000, gasLimit: 9000000 }; - - const vault = await Vault.deploy( - booAddress, - tokenName, - tokenSymbol, - approvalDelay, - depositFee, - tvlCap, - options - ); - - await vault.deployed(); - console.log("Vault deployed to:", vault.address); -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/scripts/deploy/deploy-2-strategy.js b/scripts/deploy/deploy-2-strategy.js deleted file mode 100644 index 8d5a39a..0000000 --- a/scripts/deploy/deploy-2-strategy.js +++ /dev/null @@ -1,27 +0,0 @@ -async function main() { - const vaultAddress = "0xd072Fa5E4a19705d14FF02942088118eD6aeFBe5"; - - const Strategy = await ethers.getContractFactory("ReaperAutoCompoundXBoo"); - const treasuryAddress = "0x0e7c5313E9BB80b654734d9b7aB1FB01468deE3b"; - const paymentRouterAddress = "0x603e60d22af05ff77fdcf05c063f582c40e55aae"; - const strategist1 = "0x1E71AEE6081f62053123140aacC7a06021D77348"; - const strategist2 = "0x81876677843D00a7D792E1617459aC2E93202576"; - const strategist3 = "0x1A20D7A31e5B3Bc5f02c8A146EF6f394502a10c4"; - - // const options = { gasPrice: 2000000000000, gasLimit: 9000000 }; - const strategy = await Strategy.deploy( - vaultAddress, - [treasuryAddress, paymentRouterAddress], - [strategist1, strategist2, strategist3] - ); - - await strategy.deployed(); - console.log("Strategy deployed to:", strategy.address); -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/scripts/deploy/deploy-3-initialize.js b/scripts/deploy/deploy-3-initialize.js deleted file mode 100644 index 64bff7b..0000000 --- a/scripts/deploy/deploy-3-initialize.js +++ /dev/null @@ -1,18 +0,0 @@ -async function main() { - const vaultAddress = "0xd072Fa5E4a19705d14FF02942088118eD6aeFBe5"; - const strategyAddress = "0x8931f204107D43299FE0dD483C68F5c62A13bEd0"; - - const Vault = await ethers.getContractFactory("ReaperVaultv1_3"); - const vault = Vault.attach(vaultAddress); - - //const options = { gasPrice: 2000000000000, gasLimit: 9000000 }; - await vault.initialize(strategyAddress); - console.log("Vault initialized"); -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/scripts/deploy/deploy-4-paymentRouter.js b/scripts/deploy/deploy-4-paymentRouter.js deleted file mode 100644 index 2558dac..0000000 --- a/scripts/deploy/deploy-4-paymentRouter.js +++ /dev/null @@ -1,23 +0,0 @@ -async function main() { - const strategyAddress = "0xe3D115671cD4100dbD018aeE78C19CFa18055476"; - - const PaymentRouter = await ethers.getContractFactory("PaymentRouter"); - const strategistFeeReceiver = "0x81876677843D00a7D792E1617459aC2E93202576"; - const paymentRouterAddress = "0x603e60d22af05ff77fdcf05c063f582c40e55aae"; - - const paymentRouter = await PaymentRouter.attach(paymentRouterAddress); - //const options = { gasPrice: 2000000000000, gasLimit: 9000000 }; - await paymentRouter.addStrategy( - strategyAddress, - [strategistFeeReceiver], - [10000] - ); - console.log("Strategy added to payment router"); -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/scripts/deploy/deploy-5-addPools.js b/scripts/deploy/deploy-5-addPools.js deleted file mode 100644 index 2b850d9..0000000 --- a/scripts/deploy/deploy-5-addPools.js +++ /dev/null @@ -1,21 +0,0 @@ -async function main() { - const strategyAddress = "0xB62100d94436f53a87516f1aa3Cf42f8A96Ae049"; - - const Strategy = await ethers.getContractFactory("ReaperAutoCompoundXBoo"); - const strategy = Strategy.attach(strategyAddress); - - const YOSHI_ID = 18; - const WFTM = "0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83"; - const YOSHI = "0x3dc57B391262e3aAe37a08D91241f9bA9d58b570"; - - const tx1 = await strategy.addUsedPool(YOSHI_ID, [YOSHI, WFTM]); - await tx1.wait(); - console.log("Pools added to strategy"); -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/scripts/deploy/deploy.js b/scripts/deploy/deploy.js deleted file mode 100644 index 66f65bc..0000000 --- a/scripts/deploy/deploy.js +++ /dev/null @@ -1,72 +0,0 @@ -const hre = require("hardhat"); - -async function main() { - const Strategy = await ethers.getContractFactory("ReaperAutoCompoundXBoo"); - const PaymentRouter = await ethers.getContractFactory("PaymentRouter"); - const Vault = await ethers.getContractFactory("ReaperVaultv1_3"); - - const booAddress = "0x841FAD6EAe12c286d1Fd18d1d525DFfA75C7EFFE"; - const tokenName = "XBOO Single Stake Vault"; - const tokenSymbol = "rfXBOO"; - const approvalDelay = 432000; - const depositFee = 0; - const tvlCap = ethers.utils.parseEther("33"); - - const vault = await Vault.deploy( - booAddress, - tokenName, - tokenSymbol, - approvalDelay, - depositFee, - tvlCap - ); - - await vault.deployed(); - console.log("Vault deployed to:", vault.address); - - const treasuryAddress = "0x0e7c5313E9BB80b654734d9b7aB1FB01468deE3b"; - const paymentRouterAddress = "0x603e60d22af05ff77fdcf05c063f582c40e55aae"; - const strategist1 = "0x1E71AEE6081f62053123140aacC7a06021D77348"; - const strategist2 = "0x81876677843D00a7D792E1617459aC2E93202576"; - const strategist3 = "0x1A20D7A31e5B3Bc5f02c8A146EF6f394502a10c4"; - - const strategy = await Strategy.deploy( - vault.address, - [treasuryAddress, paymentRouterAddress], - [strategist1, strategist2, strategist3] - ); - - await strategy.deployed(); - console.log("Strategy deployed to:", strategy.address); - - const strategistFeeReceiver = "0x81876677843D00a7D792E1617459aC2E93202576"; - - const paymentRouter = await PaymentRouter.attach(paymentRouterAddress); - await paymentRouter.addStrategy( - strategy.address, - [strategistFeeReceiver], - [10000] - ); - console.log("Strategy added to payment router"); - - const WFTM_ID = 2; - const FOO_ID = 3; - const WFTM = "0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83"; - const FOO = "0xFbc3c04845162F067A0B6F8934383E63899c3524"; - - const tx1 = await strategy.addUsedPool(WFTM_ID, [WFTM, WFTM]); - const tx2 = await strategy.addUsedPool(FOO_ID, [FOO, WFTM]); - await tx1.wait(); - await tx2.wait(); - console.log("Pools added to strategy"); - - await vault.initialize(strategy.address); - console.log("Vault initialized"); -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/yarn.lock b/yarn.lock index 0a48930..2c6e568 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1854,24 +1854,24 @@ "resolved" "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.4.1.tgz" "version" "4.4.1" -"@openzeppelin/hardhat-upgrades@^1.20.0": - "integrity" "sha512-ign7fc/ZdPe+KAYCB91619o+wlBr7sIEEt1nqLhoXAJ9f0qVuXkwAaTdLB0MTSWH85TzlUUT2fTJp1ZnZ1o4LQ==" - "resolved" "https://registry.npmjs.org/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-1.20.0.tgz" - "version" "1.20.0" +"@openzeppelin/hardhat-upgrades@^1.21.0": + "integrity" "sha512-Kwl7IN0Hlhj4HluMTTl0DrtU90OI/Q6rG3sAyd2pv3fababe9EuZqs9DydOlkWM45JwTzC+eBzX3TgHsqI13eA==" + "resolved" "https://registry.npmjs.org/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-1.21.0.tgz" + "version" "1.21.0" dependencies: - "@openzeppelin/upgrades-core" "^1.18.0" + "@openzeppelin/upgrades-core" "^1.20.0" "chalk" "^4.1.0" "debug" "^4.1.1" "proper-lockfile" "^4.1.1" -"@openzeppelin/upgrades-core@^1.18.0": - "integrity" "sha512-fFp5sscGC876yhq7BU595LG45yky21sZFa6cDJigluUjAyJSPoLwF7GD9bSwQMMo4jC7ii1UJBtLipUxN6PVTA==" - "resolved" "https://registry.npmjs.org/@openzeppelin/upgrades-core/-/upgrades-core-1.18.0.tgz" - "version" "1.18.0" +"@openzeppelin/upgrades-core@^1.20.0": + "integrity" "sha512-Y4/+BPIbnopfE6ZhPOE2CD0V5fnvDxKKm7+kryx5+WrcRCTW3B5DjbXL9xyyoviG8Rn5EXUh5Fk1GLbiYDfu0g==" + "resolved" "https://registry.npmjs.org/@openzeppelin/upgrades-core/-/upgrades-core-1.20.4.tgz" + "version" "1.20.4" dependencies: "cbor" "^8.0.0" "chalk" "^4.1.0" - "compare-versions" "^4.0.0" + "compare-versions" "^5.0.0" "debug" "^4.1.1" "ethereumjs-util" "^7.0.3" "proper-lockfile" "^4.1.1" @@ -5064,10 +5064,10 @@ "resolved" "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" "version" "1.0.1" -"compare-versions@^4.0.0": - "integrity" "sha512-WQfnbDcrYnGr55UwbxKiQKASnTtNnaAWVi8jZyy8NTpVAXWACSne8lMD1iaIo9AiU6mnuLvSVshCzewVuWxHUg==" - "resolved" "https://registry.npmjs.org/compare-versions/-/compare-versions-4.1.3.tgz" - "version" "4.1.3" +"compare-versions@^5.0.0": + "integrity" "sha512-v8Au3l0b+Nwkp4G142JcgJFh1/TUhdxut7wzD1Nq1dyp5oa3tXaqb03EXOAB6jS4gMlalkjAUPZBMiAfKUixHQ==" + "resolved" "https://registry.npmjs.org/compare-versions/-/compare-versions-5.0.1.tgz" + "version" "5.0.1" "component-emitter@^1.2.1": "integrity" "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" From 9713cc8142e8d070f5f5b2d942df6b52fcd208ab Mon Sep 17 00:00:00 2001 From: Magnus Brantheim Date: Wed, 9 Nov 2022 13:36:52 +0100 Subject: [PATCH 02/14] Make MagicatHandlerUpgradeable deploy script --- scripts/deploy/deploy-2-magicathandler.js | 34 +++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 scripts/deploy/deploy-2-magicathandler.js diff --git a/scripts/deploy/deploy-2-magicathandler.js b/scripts/deploy/deploy-2-magicathandler.js new file mode 100644 index 0000000..ae8085f --- /dev/null +++ b/scripts/deploy/deploy-2-magicathandler.js @@ -0,0 +1,34 @@ +async function main() { + const vaultAddress = '0xFC550BAD3c14160CBA7bc05ee263b3F060149AFF'; + const strategyAddress = ''; + const strategist1 = '0x1E71AEE6081f62053123140aacC7a06021D77348'; + const strategist2 = '0x81876677843D00a7D792E1617459aC2E93202576'; + const strategist3 = '0x1A20D7A31e5B3Bc5f02c8A146EF6f394502a10c4'; + const strategist4 = '0x60BC5E0440C867eEb4CbcE84bB1123fad2b262B1'; + const superAdminMultisig = '0x04C710a1E8a738CDf7cAD3a52Ba77A784C35d8CE'; + + // get artifacts + const MagicatsHandlerUpgradeable = await ethers.getContractFactory('MagicatsHandlerUpgradeable'); + + const magicatsHandlerUpgradeable = await hre.upgrades.deployProxy( + MagicatsHandlerUpgradeable, + [ + strategyAddress, + vaultAddress, + [strategist1, strategist2, strategist3, strategist4], //strategists + [superAdminMultisig], //multisigRoles + ], + {kind: 'uups'}, + ); + + await magicatsHandlerUpgradeable.deployed(); + + console.log('MagicatsHandlerUpgradeable deployed to:', magicatsHandlerUpgradeable.address); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); From cfa709ad430442823f97f8b259f303b368485a86 Mon Sep 17 00:00:00 2001 From: RadioJulius <9730701+RadioJulius@users.noreply.github.com> Date: Fri, 27 Jan 2023 10:49:32 -0800 Subject: [PATCH 03/14] .openzeppelin folder and used scripts --- .openzeppelin/unknown-250.json | 8 +- scripts/add-keeper.js | 17 +++ scripts/deploy-old/deploy-1-vault.js | 31 +++++ scripts/deploy-old/deploy-2-strategy.js | 27 ++++ scripts/deploy-old/deploy-3-initialize.js | 18 +++ scripts/deploy-old/deploy-4-paymentRouter.js | 23 ++++ scripts/deploy-old/deploy-5-addPools.js | 21 ++++ scripts/deploy-old/deploy.js | 72 +++++++++++ solc-input-reaperautocompoundxboo.json | 124 +++++++++++++++++++ test/vault-strat-migration-test.t.sol | 11 +- 10 files changed, 343 insertions(+), 9 deletions(-) create mode 100644 scripts/add-keeper.js create mode 100644 scripts/deploy-old/deploy-1-vault.js create mode 100644 scripts/deploy-old/deploy-2-strategy.js create mode 100644 scripts/deploy-old/deploy-3-initialize.js create mode 100644 scripts/deploy-old/deploy-4-paymentRouter.js create mode 100644 scripts/deploy-old/deploy-5-addPools.js create mode 100644 scripts/deploy-old/deploy.js create mode 100644 solc-input-reaperautocompoundxboo.json diff --git a/.openzeppelin/unknown-250.json b/.openzeppelin/unknown-250.json index fde35d7..ad7b35b 100644 --- a/.openzeppelin/unknown-250.json +++ b/.openzeppelin/unknown-250.json @@ -2,15 +2,15 @@ "manifestVersion": "3.2", "proxies": [ { - "address": "0x1320da7DB7617c9Fe02dca65c064864E1321a23E", - "txHash": "0xf6b2d22d6ef610b0255bcf17f25f06291a0c90d7893c3c2845dc0d81d08b58ee", + "address": "0x98c3956B492dc4984D1a9752792120ebfCdC9c9B", + "txHash": "0xae09a35f4dab6c7ce8e2f6678e99eb2df2672db93f7e03763b53b74fe58ca92d", "kind": "uups" } ], "impls": { "3a214a2c4fc8fda31ba245dc77929a6256a513098a330a42599b3656ea266be4": { - "address": "0x514A94BC22877f245B36cd7fd6EcDFB8Db7a5EB9", - "txHash": "0x6d446ce1bb4c6cfba02e857bc6841e03de52ad1a374e46e441b180ae701b73a8", + "address": "0x040cD41173553c95D301721B7331ed379Cd78219", + "txHash": "0x9042dc1f1a4d4a8393ee8b0f3eb826c07528214d1a35306ad7847532742ea187", "layout": { "solcVersion": "0.8.9", "storage": [ diff --git a/scripts/add-keeper.js b/scripts/add-keeper.js new file mode 100644 index 0000000..5f4b170 --- /dev/null +++ b/scripts/add-keeper.js @@ -0,0 +1,17 @@ +const hre = require("hardhat"); + +async function main() { + const stratAddress = "0xD3BF27E1606dF8Ac80f4Fd3c4faF47b8c31a1021"; + const Strat = await ethers.getContractFactory("ReaperAutoCompoundXBoov2"); + const keeperAddress = "0x9ccA5c3829224F7ac9077540bC365De4384823A7"; + const strat = await Strat.attach(stratAddress); + await strat.grantRole("0x71a9859d7dd21b24504a6f306077ffc2d510b4d4b61128e931fe937441ad1836",keeperAddress); + console.log("Boo approved"); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/scripts/deploy-old/deploy-1-vault.js b/scripts/deploy-old/deploy-1-vault.js new file mode 100644 index 0000000..8fc3803 --- /dev/null +++ b/scripts/deploy-old/deploy-1-vault.js @@ -0,0 +1,31 @@ +async function main() { + const Vault = await ethers.getContractFactory("ReaperVaultv1_3"); + + const booAddress = "0x841FAD6EAe12c286d1Fd18d1d525DFfA75C7EFFE"; + const tokenName = "XBOO Single Stake Vault"; + const tokenSymbol = "rfXBOO"; + const approvalDelay = 86400; + const depositFee = 0; + const tvlCap = ethers.constants.MaxUint256; + const options = { gasPrice: 500000000000, gasLimit: 9000000 }; + + const vault = await Vault.deploy( + booAddress, + tokenName, + tokenSymbol, + approvalDelay, + depositFee, + tvlCap, + options + ); + + await vault.deployed(); + console.log("Vault deployed to:", vault.address); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/scripts/deploy-old/deploy-2-strategy.js b/scripts/deploy-old/deploy-2-strategy.js new file mode 100644 index 0000000..8d5a39a --- /dev/null +++ b/scripts/deploy-old/deploy-2-strategy.js @@ -0,0 +1,27 @@ +async function main() { + const vaultAddress = "0xd072Fa5E4a19705d14FF02942088118eD6aeFBe5"; + + const Strategy = await ethers.getContractFactory("ReaperAutoCompoundXBoo"); + const treasuryAddress = "0x0e7c5313E9BB80b654734d9b7aB1FB01468deE3b"; + const paymentRouterAddress = "0x603e60d22af05ff77fdcf05c063f582c40e55aae"; + const strategist1 = "0x1E71AEE6081f62053123140aacC7a06021D77348"; + const strategist2 = "0x81876677843D00a7D792E1617459aC2E93202576"; + const strategist3 = "0x1A20D7A31e5B3Bc5f02c8A146EF6f394502a10c4"; + + // const options = { gasPrice: 2000000000000, gasLimit: 9000000 }; + const strategy = await Strategy.deploy( + vaultAddress, + [treasuryAddress, paymentRouterAddress], + [strategist1, strategist2, strategist3] + ); + + await strategy.deployed(); + console.log("Strategy deployed to:", strategy.address); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/scripts/deploy-old/deploy-3-initialize.js b/scripts/deploy-old/deploy-3-initialize.js new file mode 100644 index 0000000..64bff7b --- /dev/null +++ b/scripts/deploy-old/deploy-3-initialize.js @@ -0,0 +1,18 @@ +async function main() { + const vaultAddress = "0xd072Fa5E4a19705d14FF02942088118eD6aeFBe5"; + const strategyAddress = "0x8931f204107D43299FE0dD483C68F5c62A13bEd0"; + + const Vault = await ethers.getContractFactory("ReaperVaultv1_3"); + const vault = Vault.attach(vaultAddress); + + //const options = { gasPrice: 2000000000000, gasLimit: 9000000 }; + await vault.initialize(strategyAddress); + console.log("Vault initialized"); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/scripts/deploy-old/deploy-4-paymentRouter.js b/scripts/deploy-old/deploy-4-paymentRouter.js new file mode 100644 index 0000000..2558dac --- /dev/null +++ b/scripts/deploy-old/deploy-4-paymentRouter.js @@ -0,0 +1,23 @@ +async function main() { + const strategyAddress = "0xe3D115671cD4100dbD018aeE78C19CFa18055476"; + + const PaymentRouter = await ethers.getContractFactory("PaymentRouter"); + const strategistFeeReceiver = "0x81876677843D00a7D792E1617459aC2E93202576"; + const paymentRouterAddress = "0x603e60d22af05ff77fdcf05c063f582c40e55aae"; + + const paymentRouter = await PaymentRouter.attach(paymentRouterAddress); + //const options = { gasPrice: 2000000000000, gasLimit: 9000000 }; + await paymentRouter.addStrategy( + strategyAddress, + [strategistFeeReceiver], + [10000] + ); + console.log("Strategy added to payment router"); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/scripts/deploy-old/deploy-5-addPools.js b/scripts/deploy-old/deploy-5-addPools.js new file mode 100644 index 0000000..2b850d9 --- /dev/null +++ b/scripts/deploy-old/deploy-5-addPools.js @@ -0,0 +1,21 @@ +async function main() { + const strategyAddress = "0xB62100d94436f53a87516f1aa3Cf42f8A96Ae049"; + + const Strategy = await ethers.getContractFactory("ReaperAutoCompoundXBoo"); + const strategy = Strategy.attach(strategyAddress); + + const YOSHI_ID = 18; + const WFTM = "0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83"; + const YOSHI = "0x3dc57B391262e3aAe37a08D91241f9bA9d58b570"; + + const tx1 = await strategy.addUsedPool(YOSHI_ID, [YOSHI, WFTM]); + await tx1.wait(); + console.log("Pools added to strategy"); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/scripts/deploy-old/deploy.js b/scripts/deploy-old/deploy.js new file mode 100644 index 0000000..66f65bc --- /dev/null +++ b/scripts/deploy-old/deploy.js @@ -0,0 +1,72 @@ +const hre = require("hardhat"); + +async function main() { + const Strategy = await ethers.getContractFactory("ReaperAutoCompoundXBoo"); + const PaymentRouter = await ethers.getContractFactory("PaymentRouter"); + const Vault = await ethers.getContractFactory("ReaperVaultv1_3"); + + const booAddress = "0x841FAD6EAe12c286d1Fd18d1d525DFfA75C7EFFE"; + const tokenName = "XBOO Single Stake Vault"; + const tokenSymbol = "rfXBOO"; + const approvalDelay = 432000; + const depositFee = 0; + const tvlCap = ethers.utils.parseEther("33"); + + const vault = await Vault.deploy( + booAddress, + tokenName, + tokenSymbol, + approvalDelay, + depositFee, + tvlCap + ); + + await vault.deployed(); + console.log("Vault deployed to:", vault.address); + + const treasuryAddress = "0x0e7c5313E9BB80b654734d9b7aB1FB01468deE3b"; + const paymentRouterAddress = "0x603e60d22af05ff77fdcf05c063f582c40e55aae"; + const strategist1 = "0x1E71AEE6081f62053123140aacC7a06021D77348"; + const strategist2 = "0x81876677843D00a7D792E1617459aC2E93202576"; + const strategist3 = "0x1A20D7A31e5B3Bc5f02c8A146EF6f394502a10c4"; + + const strategy = await Strategy.deploy( + vault.address, + [treasuryAddress, paymentRouterAddress], + [strategist1, strategist2, strategist3] + ); + + await strategy.deployed(); + console.log("Strategy deployed to:", strategy.address); + + const strategistFeeReceiver = "0x81876677843D00a7D792E1617459aC2E93202576"; + + const paymentRouter = await PaymentRouter.attach(paymentRouterAddress); + await paymentRouter.addStrategy( + strategy.address, + [strategistFeeReceiver], + [10000] + ); + console.log("Strategy added to payment router"); + + const WFTM_ID = 2; + const FOO_ID = 3; + const WFTM = "0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83"; + const FOO = "0xFbc3c04845162F067A0B6F8934383E63899c3524"; + + const tx1 = await strategy.addUsedPool(WFTM_ID, [WFTM, WFTM]); + const tx2 = await strategy.addUsedPool(FOO_ID, [FOO, WFTM]); + await tx1.wait(); + await tx2.wait(); + console.log("Pools added to strategy"); + + await vault.initialize(strategy.address); + console.log("Vault initialized"); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/solc-input-reaperautocompoundxboo.json b/solc-input-reaperautocompoundxboo.json new file mode 100644 index 0000000..e503097 --- /dev/null +++ b/solc-input-reaperautocompoundxboo.json @@ -0,0 +1,124 @@ +{ + "language": "Solidity", + "sources": { + "./contracts/ReaperAutoCompoundXBoo.sol": { + "content": "// SPDX-License-Identifier: BSL1.1\n\npragma solidity 0.8.9;\n\nimport \"./abstract/ReaperBaseStrategy.sol\";\nimport \"./interfaces/IAceLab.sol\";\nimport \"./interfaces/IBooMirrorWorld.sol\";\nimport \"./interfaces/IUniswapRouterETH.sol\";\nimport \"./interfaces/IMagicatsHandler.sol\";\nimport \"./interfaces/IExternalHandler.sol\";\nimport \"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol\";\n\n/**\n * @dev This is a strategy to stake Boo into XBOO, and then stake XBOO in different pools to collect more rewards\n * The strategy will compound the pool rewards into Boo which will be deposited into the strategy for more yield.\n */\ncontract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgradeable {\n using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;\n using SafeERC20Upgradeable for IERC20Upgradeable;\n using SafeERC20Upgradeable for IBooMirrorWorld;\n\n /**\n * @dev Tokens Used:\n * {WFTM} - Required for liquidity routing when doing swaps. Also used to charge fees on yield.\n * {XBOO} - Token generated by staking our funds. Also used to stake in secondary pools.\n * {Boo} - Token that the strategy maximizes.\n */\n address public constant WFTM = 0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83;\n address public constant USDC = 0x04068DA6C83AFCFA0e13ba15A6696662335D5B75;\n IBooMirrorWorld public constant XBOO = IBooMirrorWorld(0xa48d959AE2E88f1dAA7D5F611E01908106dE7598); // XBOO\n IERC20Upgradeable public constant BOO = IERC20Upgradeable(0x841FAD6EAe12c286d1Fd18d1d525DFfA75C7EFFE); // BOO\n\n /**\n * @dev Third Party Contracts:\n * {UNIROUTER} - the UNIROUTER for target DEX\n * {MAGICATS} - NFT collection that improves staking rewards\n * {aceLab} - Address to AceLab, the SpookySwap contract to stake XBOO\n * {magicatsHandler} - NFT vault for magicats that allows for management + deposit/withdraw of magicatNFTs\n */\n address public constant UNIROUTER = 0xF491e7B69E4244ad4002BC14e878a34207E38c29;\n address public constant MAGICATS = 0x2aB5C606a5AA2352f8072B9e2E8A213033e2c4c9;\n address public magicatsHandler;\n address public aceLab;\n\n /**\n * @dev Routes we take to swap tokens\n * {WFTMToBOOPath} - Route we take to get from {WFTM} into {BOO}.\n * {WFTMToUSDCPath} - Route we take to get from {WFTM} into {USDC}.\n * {poolRewardToWFTMPaths} - Routes for each pool to get from {pool reward token} into {WFTM}.\n */\n address[] public WFTMToBOOPath;\n address[] public WFTMToUSDCPath;\n mapping(uint256 => address[]) public poolRewardToWFTMPaths;\n\n /**\n * @dev Variables for pool selection\n * {currentPoolId} - Pool id for the the current pool the strategy deposits XBOO into\n */\n uint256 public currentPoolId;\n\n /**\n * @dev Variables for pool selection\n * {totalPoolBalance} - The total amount of XBOO currently deposited into pools\n * {poolXBOOBalance} - The amount of XBOO deposited into each pool\n * {depositedPools} - Enumerable set containing IDs of all the pools we currently have funds in\n */\n uint256 public totalPoolBalance;\n mapping(uint256 => uint256) public poolXBOOBalance;\n EnumerableSetUpgradeable.UintSet private depositedPools;\n\n /***\n * {accCatDebt} - mapping of poolID -> accumulated catDebt between harvest.\n * Accounted for each time catDebt is reset (deposit/withdraw/harvest).\n * {catProvisionFee} - amount of boosted harvest diverted to magicatsHandler\n */\n mapping(uint256 => uint256) public accCatDebt;\n uint256 public catProvisionFee;\n\n struct RewardHandler {\n bool requiresSpecialHandling;\n address handler;\n }\n //mapping of poolIds to a struct that specifies if the token requires special preperation to turn into WFTM (ex. xTarot)\n //and the address of the contract that can handle it according the pre-set API\n mapping(uint256 => RewardHandler) public idToSpecialHandler;\n\n /**\n * @dev Initializes the strategy. Sets parameters, saves routes, and gives allowances.\n * @notice see documentation for each variable above its respective declaration.\n */\n function initialize(\n address _vault,\n address[] memory _feeRemitters,\n address[] memory _strategists,\n address[] memory _multisigRoles\n ) public initializer {\n __ReaperBaseStrategy_init(_vault, _feeRemitters, _strategists, _multisigRoles);\n\n aceLab = 0x399D73bB7c83a011cD85DF2a3CdF997ED3B3439f;\n\n currentPoolId = 5;\n totalPoolBalance = 0;\n WFTMToBOOPath = [WFTM, address(BOO)];\n WFTMToUSDCPath = [WFTM, USDC];\n catProvisionFee = 5000;\n\n _giveAllowances();\n }\n\n /**\n * @dev Function that puts the funds to work.\n * It gets called whenever someone deposits in the strategy's vault contract.\n * It deposits {BOO} into XBOO (BOOMirrorWorld) to farm {XBOO} and finally,\n * XBOO is deposited into other pools to earn additional rewards\n */\n function _deposit() internal override whenNotPaused {\n uint256 booBalance = BOO.balanceOf(address(this));\n if (booBalance != 0) {\n XBOO.enter(booBalance);\n }\n\n _aceLabDeposit(currentPoolId, XBOO.balanceOf(address(this)));\n }\n\n /**\n * @dev Function to deposit into AceLab while keeping internal accounting\n * updated.\n */\n function _aceLabDeposit(uint256 _poolId, uint256 _XBOOAmount) internal {\n totalPoolBalance += _XBOOAmount;\n poolXBOOBalance[_poolId] += _XBOOAmount;\n if (_XBOOAmount != 0 && !depositedPools.contains(_poolId)) {\n depositedPools.add(_poolId);\n }\n _writeCatDebt(_poolId);\n IAceLab(aceLab).deposit(_poolId, _XBOOAmount);\n }\n\n /**\n * @dev Withdraws funds and sents them back to the vault.\n * It withdraws {BOO} from the AceLab pools.\n * The available {BOO} minus fees is returned to the vault.\n */\n function _withdraw(uint256 _amount) internal override {\n uint256 booBalance = BOO.balanceOf(address(this));\n\n if (booBalance < _amount) {\n uint256 xBooToWithdraw = XBOO.BOOForxBOO(_amount - booBalance);\n uint256[] memory depositedPoolIDs = depositedPools.values();\n uint256 depositPoolsLength = depositedPoolIDs.length;\n\n uint256 withdrawnAmount = 0;\n uint256 amountToWithdraw;\n for (uint256 i = 0; i < depositPoolsLength; i = _uncheckedInc(i)) {\n uint256 currentDepositedPoolId = depositedPoolIDs[i];\n amountToWithdraw = _getMin(poolXBOOBalance[currentDepositedPoolId], _amount - withdrawnAmount);\n _aceLabWithdraw(currentDepositedPoolId, amountToWithdraw);\n withdrawnAmount += amountToWithdraw;\n\n if (withdrawnAmount >= xBooToWithdraw) {\n break;\n }\n }\n\n XBOO.leave(xBooToWithdraw);\n\n booBalance = BOO.balanceOf(address(this));\n if (booBalance < _amount) {\n require(_amount - booBalance <= 10);\n _amount = booBalance;\n }\n }\n\n BOO.safeTransfer(vault, _amount);\n }\n\n /**\n * @dev Function to withdraw from AceLab while keeping internal accounting\n * updated.\n */\n function _aceLabWithdraw(uint256 _poolId, uint256 _XBOOAmount) internal {\n totalPoolBalance -= _XBOOAmount;\n poolXBOOBalance[_poolId] -= _XBOOAmount;\n if (poolXBOOBalance[_poolId] == 0 && depositedPools.contains(_poolId)) {\n depositedPools.remove(_poolId);\n }\n _writeCatDebt(_poolId);\n IAceLab(aceLab).withdraw(_poolId, _XBOOAmount);\n }\n\n /**\n * @dev Function to set Allocations of XBOO in acelab, called by Keepers or strategists to maintain maximal APR\n * {withdrawPoolIds} - Pool Ids that the strategy should reduce the balance of\n * {withdrawAmounts} - corresponding to the withdrawPoolIds, the amount those pIds should be reduced\n * {depositPoolIds} - Pool Ids that the strategy should increase the balance of\n * {depositAmounts} - corresponding to the depositPoolIds, the amount those pIds should be increased\n */\n function setXBooAllocations(\n uint256[] calldata withdrawPoolIds,\n uint256[] calldata withdrawAmounts,\n uint256[] calldata depositPoolIds,\n uint256[] calldata depositAmounts\n ) external {\n _atLeastRole(KEEPER);\n uint256 depositPoolsLength = depositPoolIds.length;\n uint256 withdrawPoolsLength = withdrawPoolIds.length;\n\n for (uint256 i = 0; i < withdrawPoolsLength; i = _uncheckedInc(i)) {\n _aceLabWithdraw(withdrawPoolIds[i], withdrawAmounts[i]);\n }\n\n for (uint256 i = 0; i < depositPoolsLength; i = _uncheckedInc(i)) {\n uint256 XBOOAvailable = IERC20Upgradeable(XBOO).balanceOf(address(this));\n if (XBOOAvailable == 0) {\n return;\n }\n uint256 depositAmount = _getMin(XBOOAvailable, depositAmounts[i]);\n _aceLabDeposit(depositPoolIds[i], depositAmount);\n }\n }\n\n /**\n * @dev Core function of the strat, in charge of collecting and re-investing rewards.\n * 1. It claims rewards from the AceLab pools and estimated the current yield for each pool.\n * 2. It charges the system fees to simplify the split.\n * 3. It swaps the {WFTM} token for {BOO} which is deposited into {XBOO}\n * 4. It distributes the XBOO using a yield optimization algorithm into various pools.\n */\n function _harvestCore() internal override returns (uint256 callerFee) {\n _claimAllRewards();\n uint256 catBoostPercentage = _processRewards();\n callerFee = _chargeFees();\n _swapWFTMToBOO();\n if (magicatsHandler != address(0) && catBoostPercentage != 0) {\n _payMagicatDepositors(catBoostPercentage);\n IMagicatsHandler(magicatsHandler).processRewards();\n }\n _enterXBOO();\n _aceLabDeposit(currentPoolId, XBOO.balanceOf(address(this)));\n }\n\n function _claimAllRewards() internal {\n uint256[] memory depositedPoolIDs = depositedPools.values();\n uint256 depositPoolsLength = depositedPoolIDs.length;\n for (uint256 i = 0; i < depositPoolsLength; i = _uncheckedInc(i)) {\n _aceLabWithdraw(depositedPoolIDs[i], 0);\n }\n }\n\n /**\n * @notice Converts all reward tokens in WFTM and calculates the XBOO % that\n * was boosted by the cats.\n */\n function _processRewards() internal returns (uint256) {\n uint256 depositPoolsLength = depositedPools.length();\n uint256 tokenBal;\n address _handler;\n uint256 WFTMBalBefore;\n uint256 WFTMBalAfter;\n uint256 catBoostPercent;\n uint256 catBoostWFTM;\n uint256 catBoostTotal;\n uint256 totalHarvest;\n address rewardToken;\n uint256 activeIndex;\n for (uint256 index = 0; index < depositPoolsLength; index = _uncheckedInc(index)) {\n activeIndex = depositedPools.at(index);\n rewardToken = address(IAceLab(aceLab).poolInfo(activeIndex).RewardToken);\n tokenBal = IERC20Upgradeable(rewardToken).balanceOf(address(this));\n if (tokenBal != 0) {\n WFTMBalBefore = IERC20Upgradeable(WFTM).balanceOf(address(this));\n\n //leave is pretty standard for xTokens if it does not have leave we will need an external handler\n try IBooMirrorWorld(rewardToken).leave(tokenBal) {} catch {}\n\n if (accCatDebt[activeIndex] != 0) {\n catBoostPercent = (accCatDebt[activeIndex] * PERCENT_DIVISOR) / tokenBal;\n } else {\n catBoostPercent = 0;\n }\n\n _handler = _requireExternalHandling(activeIndex);\n if (_handler == address(this)) {\n _swapRewardToWFTM(activeIndex);\n } else if (_handler != address(0)) {\n IERC20Upgradeable(rewardToken).approve(_handler, tokenBal);\n IExternalHandler(_handler).handle(rewardToken, tokenBal);\n }\n\n WFTMBalAfter = IERC20Upgradeable(WFTM).balanceOf(address(this));\n totalHarvest += (WFTMBalAfter - WFTMBalBefore);\n catBoostWFTM = ((WFTMBalAfter - WFTMBalBefore) * catBoostPercent) / PERCENT_DIVISOR;\n catBoostTotal += catBoostWFTM;\n accCatDebt[activeIndex] = 0;\n }\n }\n\n if (catBoostTotal == 0) {\n return 0;\n }\n return ((catBoostTotal * PERCENT_DIVISOR) / totalHarvest);\n }\n\n /**\n * @dev Swaps any pool reward token to WFTM\n */\n function _swapRewardToWFTM(uint256 _poolId) internal {\n address[] memory rewardToWFTMPath = poolRewardToWFTMPaths[_poolId];\n address rewardToken = rewardToWFTMPath[0];\n uint256 poolRewardTokenBal = IERC20Upgradeable(rewardToken).balanceOf(address(this));\n if (poolRewardTokenBal != 0 && rewardToken != WFTM) {\n IERC20Upgradeable(rewardToken).safeApprove(UNIROUTER, poolRewardTokenBal);\n IUniswapRouterETH(UNIROUTER).swapExactTokensForTokensSupportingFeeOnTransferTokens(\n poolRewardTokenBal,\n 0,\n rewardToWFTMPath,\n address(this),\n block.timestamp\n );\n }\n }\n\n /**\n * @dev Takes out fees from the rewards. Set by constructor\n * callFeeToUser is set as a percentage of the fee,\n * as is treasuryFeeToVault\n */\n function _chargeFees() internal returns (uint256 callFeeToUser) {\n uint256 WFTMFee = (IERC20Upgradeable(WFTM).balanceOf(address(this)) * totalFee) / PERCENT_DIVISOR;\n if (WFTMFee != 0) {\n IUniswapRouterETH(UNIROUTER).swapExactTokensForTokensSupportingFeeOnTransferTokens(\n WFTMFee,\n 0,\n WFTMToUSDCPath,\n address(this),\n block.timestamp\n );\n uint256 USDCBal = IERC20Upgradeable(USDC).balanceOf(address(this));\n callFeeToUser = (USDCBal * callFee) / PERCENT_DIVISOR;\n uint256 treasuryFeeToVault = (USDCBal * treasuryFee) / PERCENT_DIVISOR;\n\n IERC20Upgradeable(USDC).safeTransfer(msg.sender, callFeeToUser);\n IERC20Upgradeable(USDC).safeTransfer(treasury, treasuryFeeToVault);\n }\n }\n\n /**\n * @dev Swaps all {WFTM} into {BOO}\n */\n function _swapWFTMToBOO() internal {\n uint256 WFTMBalance = IERC20Upgradeable(WFTM).balanceOf(address(this));\n if (WFTMBalance != 0) {\n IUniswapRouterETH(UNIROUTER).swapExactTokensForTokensSupportingFeeOnTransferTokens(\n WFTMBalance,\n 0,\n WFTMToBOOPath,\n address(this),\n block.timestamp\n );\n }\n }\n\n function _enterXBOO() internal {\n uint256 booBalance = BOO.balanceOf(address(this));\n XBOO.enter(booBalance);\n }\n\n /**\n * @dev internal function to pay out magicat depositors for their contribution in boosting the APRs\n * @param percentage - the percentage of the harvest (across all rewards) that was contributed by magicat staking\n */\n function _payMagicatDepositors(uint256 percentage) internal {\n uint256 booBalance = BOO.balanceOf(address(this));\n uint256 magicatsCut = (booBalance * percentage) / PERCENT_DIVISOR;\n uint256 magicatPayout = (magicatsCut * catProvisionFee) / PERCENT_DIVISOR;\n IERC20Upgradeable(BOO).transfer(magicatsHandler, magicatPayout);\n }\n\n /**\n * @dev internal function to update the amount of the boosted rewards, per pool id, that magicat staking grants\n * @param _poolId - the pool ID to update\n */\n function _writeCatDebt(uint256 _poolId) internal {\n (, uint256 catReward) = IAceLab(aceLab).pendingRewards(_poolId, address(this));\n accCatDebt[_poolId] += catReward;\n }\n\n /**\n * @dev Function to calculate the total underlaying {BOO} held by the strat.\n * It takes into account both the funds in hand, as the funds allocated in XBOO and the AceLab pools.\n */\n function balanceOf() public view override returns (uint256) {\n return balanceOfBOO() + balanceOfXBOO() + balanceOfPool();\n }\n\n /**\n * @dev It calculates how much {BOO} the contract holds.\n */\n function balanceOfBOO() public view returns (uint256) {\n return BOO.balanceOf(address(this));\n }\n\n /**\n * @dev It calculates how much {BOO} the contract has staked as XBOO.\n */\n function balanceOfXBOO() public view returns (uint256) {\n return XBOO.BOOBalance(address(this));\n }\n\n /**\n * @dev It calculates how much {BOO} the strategy has allocated in the AceLab pools\n */\n function balanceOfPool() public view returns (uint256) {\n return XBOO.xBOOForBOO(totalPoolBalance);\n }\n\n /**\n * @dev Pauses deposits. Withdraws all funds from the AceLab contract, leaving rewards behind.\n */\n function _reclaimWant() internal override {\n uint256[] memory depositedPoolIDs = depositedPools.values();\n uint256 depositPoolsLength = depositedPoolIDs.length;\n uint256 currentDepositedPoolId;\n for (uint256 index = 0; index < depositPoolsLength; index = _uncheckedInc(index)) {\n currentDepositedPoolId = depositedPoolIDs[index];\n IAceLab(aceLab).emergencyWithdraw(currentDepositedPoolId);\n totalPoolBalance -= poolXBOOBalance[currentDepositedPoolId];\n poolXBOOBalance[currentDepositedPoolId] = 0;\n depositedPools.remove(currentDepositedPoolId);\n }\n\n IMagicatsHandler(magicatsHandler).massUnstakeMagicats();\n\n uint256 XBOOBalance = XBOO.balanceOf(address(this));\n XBOO.leave(XBOOBalance);\n }\n\n /**\n * @dev Pauses the strat.\n */\n function pause() public override {\n _atLeastRole(GUARDIAN);\n _pause();\n _removeAllowances();\n }\n\n /**\n * @dev Unpauses the strat.\n */\n function unpause() public override {\n _atLeastRole(ADMIN);\n _giveAllowances();\n _unpause();\n deposit();\n }\n\n /**\n * @dev Gives max allowance of {BOO} for the {XBOO} contract,\n * {XBOO} allowance for the {aceLab} contract,\n * {WFTM} allowance for the {UNIROUTER}\n * in addition to allowance to all pool rewards for the {UNIROUTER}.\n */\n function _giveAllowances() internal {\n // Give XBOO permission to use BOO\n BOO.safeApprove(address(XBOO), 0);\n BOO.safeApprove(address(XBOO), type(uint256).max);\n // Give XBOO contract permission to stake XBOO\n XBOO.safeApprove(aceLab, 0);\n XBOO.safeApprove(aceLab, type(uint256).max);\n // Give UNIROUTER permission to swap WFTM to BOO\n IERC20Upgradeable(WFTM).safeApprove(UNIROUTER, 0);\n IERC20Upgradeable(WFTM).safeApprove(UNIROUTER, type(uint256).max);\n\n _approveMagicatsFor(aceLab);\n }\n\n /**\n * @dev Removes all allowance of {BOO} for the {XBOO} contract,\n * {XBOO} allowance for the {aceLab} contract,\n * {WFTM} allowance for the {UNIROUTER}\n * in addition to allowance to all pool rewards for the {UNIROUTER}.\n */\n function _removeAllowances() internal {\n // Remove XBOO permission to use BOO\n BOO.safeApprove(address(XBOO), 0);\n // Remove XBOO contract permission to stake XBOO\n XBOO.safeApprove(aceLab, 0);\n // Remove UNIROUTER permission to swap WFTM to BOO\n IERC20Upgradeable(WFTM).safeApprove(UNIROUTER, 0);\n // Remove Magicats approvals for the staking contract\n IERC721Upgradeable(MAGICATS).setApprovalForAll(aceLab, false);\n }\n\n /**\n * @dev internal helper for setting mass approvals for magicats NFTs to a specified address\n */\n function _approveMagicatsFor(address operator) internal {\n IERC721Upgradeable(MAGICATS).setApprovalForAll(operator, true);\n }\n\n /**\n * @dev external function, called by MAGICATS_HANDLER usually, but can be bypassed if required, to set magicat staking positions for 1 poolID\n * @param poolID - the poolID of the staking contract the function will change\n * @param IDsToStake - the Magicat NFTs, by ID, that will be staked into the contract\n * @param IDsToUnstake - the Magicat NFTs, by ID, that will be unstaked from the contract\n */\n function updateMagicats(\n uint256 poolID,\n uint256[] memory IDsToStake,\n uint256[] memory IDsToUnstake\n ) public {\n _atLeastRole(MAGICATS_HANDLER);\n if (IDsToUnstake.length > 0) {\n IAceLab(aceLab).withdraw(poolID, 0, IDsToUnstake);\n }\n\n if (IDsToStake.length > 0) {\n IAceLab(aceLab).deposit(poolID, 0, IDsToStake);\n }\n }\n\n /**\n * @dev external function for updating the magicatHandler contract\n * @param handler - the address of the new Handler\n * todo - tess IFF magicats have been withdrawn back to magicatHandler, then strategy will leave them there\n * Since that only happens in the case where we do not want the strategy to have access to magicats,\n * we do not pull them back to the strategy when updating, otherwise the strategy maintains custody over the magicats.\n * In this case, we either have them idle in the strategy or deposited in Acelab for boosting, the seemless transition will have to\n * be programmed into the next magicats handler and will accept v1 rfMagicats in exchange for the NFTs\n *\n */\n function setMagicatsHandler(address handler) external {\n _atLeastRole(DEFAULT_ADMIN_ROLE);\n require(magicatsHandler == address(0));\n grantRole(MAGICATS_HANDLER, handler);\n magicatsHandler = handler;\n _approveMagicatsFor(magicatsHandler);\n }\n\n function _requireExternalHandling(uint256 pid) internal view returns (address) {\n if (idToSpecialHandler[pid].requiresSpecialHandling) {\n return idToSpecialHandler[pid].handler;\n }\n return address(this);\n }\n\n function setExternalHandlerPid(\n uint256 pid,\n bool toggle,\n address _handler\n ) external {\n _atLeastRole(DEFAULT_ADMIN_ROLE);\n idToSpecialHandler[pid].requiresSpecialHandling = toggle;\n idToSpecialHandler[pid].handler = _handler;\n }\n\n function onERC721Received(\n address,\n address,\n uint256,\n bytes calldata\n ) external pure returns (bytes4) {\n return this.onERC721Received.selector;\n }\n\n //required for the strategy interface in existing vault\n function retireStrat() external {}\n\n /**\n * @dev function to update the fee allocated to magicatStakers\n * @param _fee - the new fee to set\n */\n function updateCatProvisionFee(uint256 _fee) external {\n _atLeastRole(DEFAULT_ADMIN_ROLE);\n require(_fee <= PERCENT_DIVISOR);\n catProvisionFee = _fee;\n }\n\n /**\n * @dev external function for strategist to set token trading routes for rewards\n * @param poolId - the poolId to set the route for\n * @param routes - the trading route of the token to WFTM\n */\n function setRoute(uint256 poolId, address[] calldata routes) external {\n _atLeastRole(STRATEGIST);\n poolRewardToWFTMPaths[poolId] = routes;\n }\n\n /// @notice For doing an unchecked increment of an index for gas optimization purposes\n /// @param i - The number to increment\n /// @return The incremented number\n function _uncheckedInc(uint256 i) internal pure returns (uint256) {\n unchecked {\n return i + 1;\n }\n }\n\n /**\n * @dev function to update the new AceLab contract, due to sensitivity it is a DEFUALT_ADMIN_ROLE function\n * @param _aceLab - the new AceLab contract to deposit into\n * @param _defaultPool - the new default pool in that contract to deposit into\n */\n function migrateNewAcelab(address _aceLab, uint256 _defaultPool) external {\n _atLeastRole(DEFAULT_ADMIN_ROLE);\n require(_aceLab != address(0));\n _reclaimWant();\n _removeAllowances();\n aceLab = _aceLab;\n _giveAllowances();\n setCurrentPoolId(_defaultPool);\n updateMagicats(\n _defaultPool,\n IMagicatsHandler(magicatsHandler).getDepositableMagicats(address(this)),\n new uint256[](0)\n );\n deposit();\n }\n\n /**\n * @dev public function for setting the default poolId to deposit into\n * @param _newID - the new default pool ID\n */\n function setCurrentPoolId(uint256 _newID) public {\n _atLeastRole(STRATEGIST);\n require(_newID < IAceLab(aceLab).poolLength());\n currentPoolId = _newID;\n }\n\n /**\n * @dev Gets the minimum of two provided uints.\n */\n function _getMin(uint256 _a, uint256 _b) internal pure returns (uint256 min) {\n if (_a < _b) {\n min = _a;\n } else {\n min = _b;\n }\n }\n}\n" + }, + "./contracts/abstract/ReaperBaseStrategy.sol": { + "content": "pragma solidity ^0.8.0;\n\nimport \"../interfaces/IStrategy.sol\";\nimport \"../interfaces/IVault.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol\";\n\nabstract contract ReaperBaseStrategyv3 is\n IStrategy,\n UUPSUpgradeable,\n AccessControlEnumerableUpgradeable,\n PausableUpgradeable\n{\n uint256 public constant PERCENT_DIVISOR = 10_000;\n uint256 public constant ONE_YEAR = 365 days;\n uint256 public constant UPGRADE_TIMELOCK = 48 hours; // minimum 48 hours for RF\n\n struct Harvest {\n uint256 timestamp;\n uint256 vaultSharePrice;\n }\n\n Harvest[] public harvestLog;\n uint256 public harvestLogCadence;\n uint256 public lastHarvestTimestamp;\n\n uint256 public upgradeProposalTime;\n\n /**\n * Reaper Roles in increasing order of privilege.\n * {KEEPER} - Stricly permissioned trustless access for off-chain programs or third party keepers.\n * {STRATEGIST} - Role conferred to authors of the strategy, allows for tweaking non-critical params.\n * {GUARDIAN} - Multisig requiring 2 signatures for emergency measures such as pausing and panicking.\n * {ADMIN}- Multisig requiring 3 signatures for unpausing.\n *\n * The DEFAULT_ADMIN_ROLE (in-built access control role) will be granted to a multisig requiring 4\n * signatures. This role would have upgrading capability, as well as the ability to grant any other\n * roles.\n *\n * Also note that roles are cascading. So any higher privileged role should be able to perform all the functions\n * of any lower privileged role.\n */\n bytes32 public constant MAGICATS_HANDLER = keccak256(\"MAGICATS_HANDLER\");\n bytes32 public constant KEEPER = keccak256(\"KEEPER\");\n bytes32 public constant STRATEGIST = keccak256(\"STRATEGIST\");\n bytes32 public constant GUARDIAN = keccak256(\"GUARDIAN\");\n bytes32 public constant ADMIN = keccak256(\"ADMIN\");\n bytes32[] private cascadingAccess;\n\n /**\n * @dev Reaper contracts:\n * {treasury} - Address of the Reaper treasury\n * {vault} - Address of the vault that controls the strategy's funds.\n * {strategistRemitter} - Address where strategist fee is remitted to.\n */\n address public treasury;\n address public vault;\n address public strategistRemitter;\n\n /**\n * Fee related constants:\n * {MAX_FEE} - Maximum fee allowed by the strategy. Hard-capped at 10%.\n * {STRATEGIST_MAX_FEE} - Maximum strategist fee allowed by the strategy (as % of treasury fee).\n * Hard-capped at 50%\n * {MAX_SECURITY_FEE} - Maximum security fee charged on withdrawal to prevent\n * flash deposit/harvest attacks.\n */\n uint256 public constant MAX_FEE = 1000;\n uint256 public constant STRATEGIST_MAX_FEE = 5000;\n uint256 public constant MAX_SECURITY_FEE = 10;\n\n /**\n * @dev Distribution of fees earned, expressed as % of the profit from each harvest.\n * {totalFee} - divided by 10,000 to determine the % fee. Set to 4.5% by default and\n * lowered as necessary to provide users with the most competitive APY.\n *\n * {callFee} - Percent of the totalFee reserved for the harvester (1000 = 10% of total fee: 0.45% by default)\n * {treasuryFee} - Percent of the totalFee taken by maintainers of the software (9000 = 90% of total fee: 4.05% by default)\n * {strategistFee} - Percent of the treasuryFee taken by strategist (2500 = 25% of treasury fee: 1.0125% by default)\n *\n * {securityFee} - Fee taxed when a user withdraws funds. Taken to prevent flash deposit/harvest attacks.\n * These funds are redistributed to stakers in the pool.\n */\n uint256 public totalFee;\n uint256 public callFee;\n uint256 public treasuryFee;\n uint256 public strategistFee;\n uint256 public securityFee;\n\n /**\n * {TotalFeeUpdated} Event that is fired each time the total fee is updated.\n * {FeesUpdated} Event that is fired each time callFee+treasuryFee+strategistFee are updated.\n * {StratHarvest} Event that is fired each time the strategy gets harvested.\n * {StrategistRemitterUpdated} Event that is fired each time the strategistRemitter address is updated.\n */\n event TotalFeeUpdated(uint256 newFee);\n event FeesUpdated(uint256 newCallFee, uint256 newTreasuryFee, uint256 newStrategistFee);\n event StratHarvest(address indexed harvester);\n event StrategistRemitterUpdated(address newStrategistRemitter);\n\n /// @custom:oz-upgrades-unsafe-allow constructor\n constructor() initializer {}\n\n function __ReaperBaseStrategy_init(\n address _vault,\n address[] memory _feeRemitters,\n address[] memory _strategists,\n address[] memory _multisigRoles\n ) internal onlyInitializing {\n __UUPSUpgradeable_init();\n __AccessControlEnumerable_init();\n __Pausable_init_unchained();\n\n harvestLogCadence = 1 minutes;\n totalFee = 450;\n callFee = 1000;\n treasuryFee = 9000;\n strategistFee = 0;\n securityFee = 0;\n\n vault = _vault;\n treasury = _feeRemitters[0];\n strategistRemitter = _feeRemitters[1];\n\n for (uint256 i = 0; i < _strategists.length; i++) {\n _grantRole(STRATEGIST, _strategists[i]);\n }\n\n _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);\n _grantRole(DEFAULT_ADMIN_ROLE, _multisigRoles[0]);\n _grantRole(ADMIN, _multisigRoles[1]);\n _grantRole(GUARDIAN, _multisigRoles[2]);\n\n cascadingAccess = [DEFAULT_ADMIN_ROLE, ADMIN, GUARDIAN, STRATEGIST, MAGICATS_HANDLER, KEEPER];\n clearUpgradeCooldown();\n harvestLog.push(Harvest({timestamp: block.timestamp, vaultSharePrice: IVault(_vault).getPricePerFullShare()}));\n }\n\n /**\n * @dev Function that puts the funds to work.\n * It gets called whenever someone deposits in the strategy's vault contract.\n * Deposits go through only when the strategy is not paused.\n */\n function deposit() public override whenNotPaused {\n _deposit();\n }\n\n /**\n * @dev Withdraws funds and sends them back to the vault. Can only\n * be called by the vault. _amount must be valid and security fee\n * is deducted up-front.\n */\n function withdraw(uint256 _amount) external override {\n require(msg.sender == vault);\n require(_amount != 0);\n require(_amount <= balanceOf());\n\n uint256 withdrawFee = (_amount * securityFee) / PERCENT_DIVISOR;\n _amount -= withdrawFee;\n\n _withdraw(_amount);\n }\n\n /**\n * @dev harvest() function that takes care of logging. Subcontracts should\n * override _harvestCore() and implement their specific logic in it.\n */\n function harvest() external override whenNotPaused returns (uint256 callerFee) {\n callerFee = _harvestCore();\n\n if (block.timestamp >= harvestLog[harvestLog.length - 1].timestamp + harvestLogCadence) {\n harvestLog.push(\n Harvest({timestamp: block.timestamp, vaultSharePrice: IVault(vault).getPricePerFullShare()})\n );\n }\n\n lastHarvestTimestamp = block.timestamp;\n emit StratHarvest(msg.sender);\n }\n\n function harvestLogLength() external view returns (uint256) {\n return harvestLog.length;\n }\n\n /**\n * @dev Traverses the harvest log backwards _n items,\n * and returns the average APR calculated across all the included\n * log entries. APR is multiplied by PERCENT_DIVISOR to retain precision.\n */\n function averageAPRAcrossLastNHarvests(int256 _n) external view returns (int256) {\n require(harvestLog.length >= 2);\n\n int256 runningAPRSum;\n int256 numLogsProcessed;\n\n for (uint256 i = harvestLog.length - 1; i > 0 && numLogsProcessed < _n; i--) {\n runningAPRSum += calculateAPRUsingLogs(i - 1, i);\n numLogsProcessed++;\n }\n\n return runningAPRSum / numLogsProcessed;\n }\n\n /**\n * @dev Strategists and roles with higher privilege can edit the log cadence.\n */\n function updateHarvestLogCadence(uint256 _newCadenceInSeconds) external {\n _atLeastRole(STRATEGIST);\n harvestLogCadence = _newCadenceInSeconds;\n }\n\n /**\n * @dev Function to calculate the total {want} held by the strat.\n * It takes into account both the funds in hand, plus the funds in external contracts.\n */\n function balanceOf() public view virtual override returns (uint256);\n\n /**\n * @dev Pauses deposits. Withdraws all funds leaving rewards behind.\n * Guardian and roles with higher privilege can panic.\n */\n function panic() external override {\n _atLeastRole(GUARDIAN);\n _reclaimWant();\n pause();\n }\n\n /**\n * @dev Pauses the strat. Deposits become disabled but users can still\n * withdraw. Guardian and roles with higher privilege can pause.\n */\n function pause() public virtual override {\n _atLeastRole(GUARDIAN);\n _pause();\n }\n\n /**\n * @dev Unpauses the strat. Opens up deposits again and invokes deposit().\n * Admin and roles with higher privilege can unpause.\n */\n function unpause() external virtual override {\n _atLeastRole(ADMIN);\n _unpause();\n deposit();\n }\n\n /**\n * @dev updates the total fee, capped at 5%; only DEFAULT_ADMIN_ROLE.\n */\n function updateTotalFee(uint256 _totalFee) external {\n _atLeastRole(DEFAULT_ADMIN_ROLE);\n require(_totalFee <= MAX_FEE);\n totalFee = _totalFee;\n emit TotalFeeUpdated(totalFee);\n }\n\n /**\n * @dev updates the call fee, treasury fee, and strategist fee\n * call Fee + treasury Fee must add up to PERCENT_DIVISOR\n *\n * strategist fee is expressed as % of the treasury fee and\n * must be no more than STRATEGIST_MAX_FEE\n *\n * only DEFAULT_ADMIN_ROLE.\n */\n function updateFees(\n uint256 _callFee,\n uint256 _treasuryFee,\n uint256 _strategistFee\n ) external returns (bool) {\n _atLeastRole(DEFAULT_ADMIN_ROLE);\n require(_callFee + _treasuryFee == PERCENT_DIVISOR);\n require(_strategistFee <= STRATEGIST_MAX_FEE);\n\n callFee = _callFee;\n treasuryFee = _treasuryFee;\n strategistFee = _strategistFee;\n emit FeesUpdated(callFee, treasuryFee, strategistFee);\n return true;\n }\n\n function updateSecurityFee(uint256 _securityFee) external {\n _atLeastRole(DEFAULT_ADMIN_ROLE);\n require(_securityFee <= MAX_SECURITY_FEE);\n securityFee = _securityFee;\n }\n\n /**\n * @dev only DEFAULT_ADMIN_ROLE can update treasury address.\n */\n function updateTreasury(address newTreasury) external returns (bool) {\n _atLeastRole(DEFAULT_ADMIN_ROLE);\n treasury = newTreasury;\n return true;\n }\n\n /**\n * @dev Updates the current strategistRemitter. Only DEFAULT_ADMIN_ROLE may do this.\n */\n function updateStrategistRemitter(address _newStrategistRemitter) external {\n _atLeastRole(DEFAULT_ADMIN_ROLE);\n require(_newStrategistRemitter != address(0));\n strategistRemitter = _newStrategistRemitter;\n emit StrategistRemitterUpdated(_newStrategistRemitter);\n }\n\n /**\n * @dev Project an APR using the vault share price change between harvests at the provided indices.\n */\n function calculateAPRUsingLogs(uint256 _startIndex, uint256 _endIndex) public view returns (int256) {\n Harvest storage start = harvestLog[_startIndex];\n Harvest storage end = harvestLog[_endIndex];\n bool increasing = true;\n if (end.vaultSharePrice < start.vaultSharePrice) {\n increasing = false;\n }\n\n uint256 unsignedSharePriceChange;\n if (increasing) {\n unsignedSharePriceChange = end.vaultSharePrice - start.vaultSharePrice;\n } else {\n unsignedSharePriceChange = start.vaultSharePrice - end.vaultSharePrice;\n }\n\n uint256 unsignedPercentageChange = (unsignedSharePriceChange * 1e18) / start.vaultSharePrice;\n uint256 timeDifference = end.timestamp - start.timestamp;\n\n uint256 yearlyUnsignedPercentageChange = (unsignedPercentageChange * ONE_YEAR) / timeDifference;\n yearlyUnsignedPercentageChange /= 1e14; // restore basis points precision\n\n if (increasing) {\n return int256(yearlyUnsignedPercentageChange);\n }\n\n return -int256(yearlyUnsignedPercentageChange);\n }\n\n /**\n * @dev This function must be called prior to upgrading the implementation.\n * It's required to wait UPGRADE_TIMELOCK seconds before executing the upgrade.\n * Strategists and roles with higher privilege can initiate this cooldown.\n */\n function initiateUpgradeCooldown() external {\n _atLeastRole(STRATEGIST);\n upgradeProposalTime = block.timestamp;\n }\n\n /**\n * @dev This function is called:\n * - in initialize()\n * - as part of a successful upgrade\n * - manually to clear the upgrade cooldown.\n * Guardian and roles with higher privilege can clear this cooldown.\n */\n function clearUpgradeCooldown() public {\n _atLeastRole(GUARDIAN);\n upgradeProposalTime = block.timestamp + (ONE_YEAR * 100);\n }\n\n /**\n * @dev This function must be overriden simply for access control purposes.\n * Only DEFAULT_ADMIN_ROLE can upgrade the implementation once the timelock\n * has passed.\n */\n function _authorizeUpgrade(address) internal override {\n _atLeastRole(DEFAULT_ADMIN_ROLE);\n require(upgradeProposalTime + UPGRADE_TIMELOCK < block.timestamp);\n clearUpgradeCooldown();\n }\n\n /**\n * @dev Internal function that checks cascading role privileges. Any higher privileged role\n * should be able to perform all the functions of any lower privileged role. This is\n * accomplished using the {cascadingAccess} array that lists all roles from most privileged\n * to least privileged.\n */\n function _atLeastRole(bytes32 role) internal view {\n uint256 numRoles = cascadingAccess.length;\n uint256 specifiedRoleIndex;\n for (uint256 i = 0; i < numRoles; i++) {\n if (role == cascadingAccess[i]) {\n specifiedRoleIndex = i;\n break;\n } else if (i == numRoles - 1) {\n revert();\n }\n }\n\n for (uint256 i = 0; i <= specifiedRoleIndex; i++) {\n if (hasRole(cascadingAccess[i], msg.sender)) {\n break;\n } else if (i == specifiedRoleIndex) {\n revert();\n }\n }\n }\n\n /**\n * @dev subclasses should add their custom deposit logic in this function.\n */\n function _deposit() internal virtual;\n\n /**\n * @dev subclasses should add their custom withdraw logic in this function.\n * Note that security fee has already been deducted, so it shouldn't be deducted\n * again within this function.\n */\n function _withdraw(uint256 _amount) internal virtual;\n\n /**\n * @dev subclasses should add their custom harvesting logic in this function\n * including charging any fees. The amount of fee that is remitted to the\n * caller must be returned.\n */\n function _harvestCore() internal virtual returns (uint256);\n\n /**\n * @dev subclasses should add their custom logic to withdraw the principal from\n * any external contracts in this function. Note that we don't care about rewards,\n * we just want to reclaim our principal as much as possible, and as quickly as possible.\n * So keep this function lean. Principal should be left in the strategy and not sent to\n * the vault.\n */\n function _reclaimWant() internal virtual;\n}\n" + }, + "./contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\ninterface IStrategy {\n //deposits all funds into the farm\n function deposit() external;\n\n //vault only - withdraws funds from the strategy\n function withdraw(uint256 _amount) external;\n\n //returns the balance of all tokens managed by the strategy\n function balanceOf() external view returns (uint256);\n\n //claims farmed tokens, distributes fees, and sells tokens to re-add to the LP & farm\n function harvest() external returns (uint256 callerFee);\n\n //withdraws all tokens and sends them back to the vault\n function retireStrat() external;\n\n //pauses deposits, resets allowances, and withdraws all funds from farm\n function panic() external;\n\n //pauses deposits and resets allowances\n function pause() external;\n\n //unpauses deposits and maxes out allowances again\n function unpause() external;\n\n //updates Total Fee\n function updateTotalFee(uint256 _totalFee) external;\n\n function currentPoolId() external returns (uint256);\n\n function updateMagicats(\n uint256 poolID,\n uint256[] memory IDsToStake,\n uint256[] memory IDsToUnstake\n ) external;\n}\n" + }, + "./contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: agpl-3.0\n\npragma solidity 0.8.9;\n\ninterface IVault {\n function getPricePerFullShare() external view returns (uint256);\n\n function deposit(uint256) external;\n}\n" + }, + "./contracts/interfaces/IAceLab.sol": { + "content": "// SPDX-License-Identifier: MIT\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\npragma solidity 0.8.9;\n\ninterface IAceLab {\n // Info of each user.\n struct UserInfo {\n uint256 amount; // How many tokens the user has provided.\n uint256 rewardDebt; // Reward debt. See explanation below.\n uint256 catDebt; // Cat debt. See explanation below.\n uint256 mp; // Total staked magicat power, sum of all magicat rarities staked by this user in this pool [uint64 enough]\n }\n\n struct PoolInfo {\n //full slot = 32B\n IERC20 RewardToken; //20B Address of reward token contract.\n uint32 userLimitEndTime; //4B\n uint8 TokenPrecision; //1B The precision factor used for calculations, equals the tokens decimals\n //7B [free space available here]\n\n uint256 xBooStakedAmount; //32B # of xboo allocated to this pool\n uint256 mpStakedAmount; //32B # of mp allocated to this pool\n uint256 RewardPerSecond; //32B reward token per second for this pool in wei\n uint256 accRewardPerShare; //32B Accumulated reward per share, times the pools token precision. See below.\n uint256 accRewardPerShareMagicat; //32B Accumulated reward per share, times the pools token precision. See below.\n address protocolOwnerAddress; //20B this address is the owner of the protocol corresponding to the reward token, used for emergency withdraw to them only\n uint32 lastRewardTime; //4B Last block time that reward distribution occurs.\n uint32 endTime; //4B end time of pool\n uint32 startTime; //4B start time of pool\n }\n\n function poolInfo(uint256 _poolId) external view returns (PoolInfo memory);\n\n function userInfo(uint256 _poolId, address _userAddress)\n external\n view\n returns (\n uint256 amount,\n uint256 rewardDebt,\n uint256 catDebt,\n uint256 mp\n );\n\n function poolLength() external view returns (uint256);\n\n // View function to see pending BOOs on frontend.\n function pendingRewards(uint256 _pid, address _user) external view returns (uint256, uint256);\n\n // Update reward variables for all pools. Be careful of gas spending!\n function massUpdatePools() external;\n\n // Deposit tokens.\n function deposit(\n uint256 _pid,\n uint256 _amount,\n uint256[] memory tokenIDs\n ) external;\n\n function deposit(uint256 _pid, uint256 _amount) external;\n\n function withdraw(\n uint256 _pid,\n uint256 _amount,\n uint256[] memory tokenIDs\n ) external;\n\n function rarityOf(uint256) external returns (uint256);\n\n function getStakedMagicats(uint256, address) external returns (uint256[] memory);\n\n // Withdraw tokens.\n function withdraw(uint256 _pid, uint256 _amount) external;\n\n function balanceOf(address) external returns (uint256);\n\n // Withdraw without caring about rewards. EMERGENCY ONLY.\n function emergencyWithdraw(uint256 _pid) external;\n\n function userCurrentStakeableMP(uint256 _pid, address _user) external returns (int256);\n}\n" + }, + "./contracts/interfaces/IBooMirrorWorld.sol": { + "content": "// SPDX-License-Identifier: MIT\n\nimport \"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\";\n\npragma solidity 0.8.9;\n\ninterface IBooMirrorWorld is IERC20Upgradeable {\n // Locks Boo and mints xBoo\n function enter(uint256 _amount) external;\n\n // Unlocks the staked + gained Boo and burns xBoo\n function leave(uint256 _share) external;\n\n // returns the total amount of BOO an address has in the contract including fees earned\n function BOOBalance(address _account) external view returns (uint256 booAmount_);\n\n // returns how much BOO someone gets for redeeming xBOO\n function xBOOForBOO(uint256 _xBOOAmount) external view returns (uint256 booAmount_);\n\n // returns how much xBOO someone gets for depositing BOO\n function BOOForxBOO(uint256 _booAmount) external view returns (uint256 xBOOAmount_);\n}\n" + }, + "./contracts/interfaces/IUniswapRouterETH.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.8.9;\n\ninterface IUniswapRouterETH {\n function addLiquidity(\n address tokenA,\n address tokenB,\n uint256 amountADesired,\n uint256 amountBDesired,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 liquidity\n );\n\n function addLiquidityETH(\n address token,\n uint256 amountTokenDesired,\n uint256 amountTokenMin,\n uint256 amountETHMin,\n address to,\n uint256 deadline\n )\n external\n payable\n returns (\n uint256 amountToken,\n uint256 amountETH,\n uint256 liquidity\n );\n\n function removeLiquidity(\n address tokenA,\n address tokenB,\n uint256 liquidity,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n ) external returns (uint256 amountA, uint256 amountB);\n\n function removeLiquidityETH(\n address token,\n uint256 liquidity,\n uint256 amountTokenMin,\n uint256 amountETHMin,\n address to,\n uint256 deadline\n ) external returns (uint256 amountToken, uint256 amountETH);\n\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n uint256 deadline\n ) external returns (uint256[] memory amounts);\n\n function swapExactETHForTokens(\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n uint256 deadline\n ) external payable returns (uint256[] memory amounts);\n\n function swapExactTokensForETH(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n uint256 deadline\n ) external returns (uint256[] memory amounts);\n\n function swapExactTokensForTokensSupportingFeeOnTransferTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n uint256 deadline\n ) external;\n\n function getAmountsOut(uint256 amountIn, address[] memory path) external view returns (uint256[] memory amounts);\n}\n" + }, + "./contracts/interfaces/IMagicatsHandler.sol": { + "content": "// SPDX-License-Identifier: AGPLv3\n\npragma solidity ^0.8.0;\n\ninterface IMagicatsHandler {\n function processRewards() external;\n\n function massUnstakeMagicats() external;\n\n function withdrawAllMagicatsFromStrategy() external;\n\n function getDepositableMagicats(address) external returns (uint256[] memory);\n}\n" + }, + "./contracts/interfaces/IExternalHandler.sol": { + "content": "// SPDX-License-Identifier: AGPLv3\n\npragma solidity ^0.8.0;\n\ninterface IExternalHandler {\n function handle(address, uint256) external;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20Upgradeable.sol\";\nimport \"../extensions/draft-IERC20PermitUpgradeable.sol\";\nimport \"../../../utils/AddressUpgradeable.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20Upgradeable {\n using AddressUpgradeable for address;\n\n function safeTransfer(\n IERC20Upgradeable token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20Upgradeable token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20Upgradeable token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20Upgradeable token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20Upgradeable token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n function safePermit(\n IERC20PermitUpgradeable token,\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal {\n uint256 nonceBefore = token.nonces(owner);\n token.permit(owner, spender, value, deadline, v, r, s);\n uint256 nonceAfter = token.nonces(owner);\n require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20Upgradeable {\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-IERC20PermitUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n */\ninterface IERC20PermitUpgradeable {\n /**\n * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n * given ``owner``'s signed approval.\n *\n * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n * ordering also apply here.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `deadline` must be a timestamp in the future.\n * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n * over the EIP712-formatted function arguments.\n * - the signature must use ``owner``'s current nonce (see {nonces}).\n *\n * For more information on the signature format, see the\n * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n * section].\n */\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n /**\n * @dev Returns the current nonce for `owner`. This value must be\n * included whenever a signature is generated for {permit}.\n *\n * Every successful call to {permit} increases ``owner``'s nonce by one. This\n * prevents a signature from being used multiple times.\n */\n function nonces(address owner) external view returns (uint256);\n\n /**\n * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n */\n // solhint-disable-next-line func-name-mixedcase\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n" + }, + "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary AddressUpgradeable {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n /// @solidity memory-safe-assembly\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../utils/introspection/IERC165Upgradeable.sol\";\n\n/**\n * @dev Required interface of an ERC721 compliant contract.\n */\ninterface IERC721Upgradeable is IERC165Upgradeable {\n /**\n * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n */\n event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n */\n event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n */\n event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n /**\n * @dev Returns the number of tokens in ``owner``'s account.\n */\n function balanceOf(address owner) external view returns (uint256 balance);\n\n /**\n * @dev Returns the owner of the `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function ownerOf(uint256 tokenId) external view returns (address owner);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId,\n bytes calldata data\n ) external;\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Transfers `tokenId` token from `from` to `to`.\n *\n * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n * The approval is cleared when the token is transferred.\n *\n * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n *\n * Requirements:\n *\n * - The caller must own the token or be an approved operator.\n * - `tokenId` must exist.\n *\n * Emits an {Approval} event.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev Approve or remove `operator` as an operator for the caller.\n * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n *\n * Requirements:\n *\n * - The `operator` cannot be the caller.\n *\n * Emits an {ApprovalForAll} event.\n */\n function setApprovalForAll(address operator, bool _approved) external;\n\n /**\n * @dev Returns the account approved for `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function getApproved(uint256 tokenId) external view returns (address operator);\n\n /**\n * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n *\n * See {setApprovalForAll}\n */\n function isApprovedForAll(address owner, address operator) external view returns (bool);\n}\n" + }, + "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165Upgradeable {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC721 asset contracts.\n */\ninterface IERC721ReceiverUpgradeable {\n /**\n * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n * by `operator` from `from`, this function is called.\n *\n * It must return its Solidity selector to confirm the token transfer.\n * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n *\n * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.\n */\n function onERC721Received(\n address operator,\n address from,\n uint256 tokenId,\n bytes calldata data\n ) external returns (bytes4);\n}\n" + }, + "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n *\n * [WARNING]\n * ====\n * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable.\n * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.\n *\n * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet.\n * ====\n */\nlibrary EnumerableSetUpgradeable {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 => uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastValue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastValue;\n // Update the index for the moved value\n set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n /// @solidity memory-safe-assembly\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n /// @solidity memory-safe-assembly\n assembly {\n result := store\n }\n\n return result;\n }\n}\n" + }, + "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControlEnumerableUpgradeable.sol\";\nimport \"./AccessControlUpgradeable.sol\";\nimport \"../utils/structs/EnumerableSetUpgradeable.sol\";\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Extension of {AccessControl} that allows enumerating the members of each role.\n */\nabstract contract AccessControlEnumerableUpgradeable is Initializable, IAccessControlEnumerableUpgradeable, AccessControlUpgradeable {\n function __AccessControlEnumerable_init() internal onlyInitializing {\n }\n\n function __AccessControlEnumerable_init_unchained() internal onlyInitializing {\n }\n using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;\n\n mapping(bytes32 => EnumerableSetUpgradeable.AddressSet) private _roleMembers;\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControlEnumerableUpgradeable).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {\n return _roleMembers[role].at(index);\n }\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {\n return _roleMembers[role].length();\n }\n\n /**\n * @dev Overload {_grantRole} to track enumerable memberships\n */\n function _grantRole(bytes32 role, address account) internal virtual override {\n super._grantRole(role, account);\n _roleMembers[role].add(account);\n }\n\n /**\n * @dev Overload {_revokeRole} to track enumerable memberships\n */\n function _revokeRole(bytes32 role, address account) internal virtual override {\n super._revokeRole(role, account);\n _roleMembers[role].remove(account);\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[49] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/access/IAccessControlEnumerableUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControlUpgradeable.sol\";\n\n/**\n * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.\n */\ninterface IAccessControlEnumerableUpgradeable is IAccessControlUpgradeable {\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) external view returns (address);\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) external view returns (uint256);\n}\n" + }, + "@openzeppelin/contracts-upgradeable/access/IAccessControlUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControlUpgradeable {\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n *\n * _Available since v3.1._\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {AccessControl-_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) external view returns (bool);\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) external;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControlUpgradeable.sol\";\nimport \"../utils/ContextUpgradeable.sol\";\nimport \"../utils/StringsUpgradeable.sol\";\nimport \"../utils/introspection/ERC165Upgradeable.sol\";\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```\n * function foo() public {\n * require(hasRole(MY_ROLE, msg.sender));\n * ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it.\n */\nabstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable {\n function __AccessControl_init() internal onlyInitializing {\n }\n\n function __AccessControl_init_unchained() internal onlyInitializing {\n }\n struct RoleData {\n mapping(address => bool) members;\n bytes32 adminRole;\n }\n\n mapping(bytes32 => RoleData) private _roles;\n\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n /**\n * @dev Modifier that checks that an account has a specific role. Reverts\n * with a standardized message including the required role.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n *\n * _Available since v4.1._\n */\n modifier onlyRole(bytes32 role) {\n _checkRole(role);\n _;\n }\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) public view virtual override returns (bool) {\n return _roles[role].members[account];\n }\n\n /**\n * @dev Revert with a standard message if `_msgSender()` is missing `role`.\n * Overriding this function changes the behavior of the {onlyRole} modifier.\n *\n * Format of the revert message is described in {_checkRole}.\n *\n * _Available since v4.6._\n */\n function _checkRole(bytes32 role) internal view virtual {\n _checkRole(role, _msgSender());\n }\n\n /**\n * @dev Revert with a standard message if `account` is missing `role`.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n */\n function _checkRole(bytes32 role, address account) internal view virtual {\n if (!hasRole(role, account)) {\n revert(\n string(\n abi.encodePacked(\n \"AccessControl: account \",\n StringsUpgradeable.toHexString(uint160(account), 20),\n \" is missing role \",\n StringsUpgradeable.toHexString(uint256(role), 32)\n )\n )\n );\n }\n }\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {\n return _roles[role].adminRole;\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n *\n * May emit a {RoleGranted} event.\n */\n function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _grantRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n *\n * May emit a {RoleRevoked} event.\n */\n function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _revokeRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been revoked `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n *\n * May emit a {RoleRevoked} event.\n */\n function renounceRole(bytes32 role, address account) public virtual override {\n require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n _revokeRole(role, account);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event. Note that unlike {grantRole}, this function doesn't perform any\n * checks on the calling account.\n *\n * May emit a {RoleGranted} event.\n *\n * [WARNING]\n * ====\n * This function should only be called from the constructor when setting\n * up the initial roles for the system.\n *\n * Using this function in any other way is effectively circumventing the admin\n * system imposed by {AccessControl}.\n * ====\n *\n * NOTE: This function is deprecated in favor of {_grantRole}.\n */\n function _setupRole(bytes32 role, address account) internal virtual {\n _grantRole(role, account);\n }\n\n /**\n * @dev Sets `adminRole` as ``role``'s admin role.\n *\n * Emits a {RoleAdminChanged} event.\n */\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n bytes32 previousAdminRole = getRoleAdmin(role);\n _roles[role].adminRole = adminRole;\n emit RoleAdminChanged(role, previousAdminRole, adminRole);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * Internal function without access restriction.\n *\n * May emit a {RoleGranted} event.\n */\n function _grantRole(bytes32 role, address account) internal virtual {\n if (!hasRole(role, account)) {\n _roles[role].members[account] = true;\n emit RoleGranted(role, account, _msgSender());\n }\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * Internal function without access restriction.\n *\n * May emit a {RoleRevoked} event.\n */\n function _revokeRole(bytes32 role, address account) internal virtual {\n if (hasRole(role, account)) {\n _roles[role].members[account] = false;\n emit RoleRevoked(role, account, _msgSender());\n }\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[49] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract ContextUpgradeable is Initializable {\n function __Context_init() internal onlyInitializing {\n }\n\n function __Context_init_unchained() internal onlyInitializing {\n }\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[50] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.2;\n\nimport \"../../utils/AddressUpgradeable.sol\";\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\n * reused. This mechanism prevents re-execution of each \"step\" but allows the creation of new initialization steps in\n * case an upgrade adds a module that needs to be initialized.\n *\n * For example:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * contract MyToken is ERC20Upgradeable {\n * function initialize() initializer public {\n * __ERC20_init(\"MyToken\", \"MTK\");\n * }\n * }\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\n * function initializeV2() reinitializer(2) public {\n * __ERC20Permit_init(\"MyToken\");\n * }\n * }\n * ```\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() {\n * _disableInitializers();\n * }\n * ```\n * ====\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n * @custom:oz-retyped-from bool\n */\n uint8 private _initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private _initializing;\n\n /**\n * @dev Triggered when the contract has been initialized or reinitialized.\n */\n event Initialized(uint8 version);\n\n /**\n * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\n * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.\n */\n modifier initializer() {\n bool isTopLevelCall = !_initializing;\n require(\n (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),\n \"Initializable: contract is already initialized\"\n );\n _initialized = 1;\n if (isTopLevelCall) {\n _initializing = true;\n }\n _;\n if (isTopLevelCall) {\n _initializing = false;\n emit Initialized(1);\n }\n }\n\n /**\n * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\n * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\n * used to initialize parent contracts.\n *\n * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original\n * initialization step. This is essential to configure modules that are added through upgrades and that require\n * initialization.\n *\n * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\n * a contract, executing them in the right order is up to the developer or operator.\n */\n modifier reinitializer(uint8 version) {\n require(!_initializing && _initialized < version, \"Initializable: contract is already initialized\");\n _initialized = version;\n _initializing = true;\n _;\n _initializing = false;\n emit Initialized(version);\n }\n\n /**\n * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n * {initializer} and {reinitializer} modifiers, directly or indirectly.\n */\n modifier onlyInitializing() {\n require(_initializing, \"Initializable: contract is not initializing\");\n _;\n }\n\n /**\n * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\n * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\n * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\n * through proxies.\n */\n function _disableInitializers() internal virtual {\n require(!_initializing, \"Initializable: contract is initializing\");\n if (_initialized < type(uint8).max) {\n _initialized = type(uint8).max;\n emit Initialized(type(uint8).max);\n }\n }\n}\n" + }, + "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev String operations.\n */\nlibrary StringsUpgradeable {\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\n uint8 private constant _ADDRESS_LENGTH = 20;\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n // Inspired by OraclizeAPI's implementation - MIT licence\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\n\n if (value == 0) {\n return \"0\";\n }\n uint256 temp = value;\n uint256 digits;\n while (temp != 0) {\n digits++;\n temp /= 10;\n }\n bytes memory buffer = new bytes(digits);\n while (value != 0) {\n digits -= 1;\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n value /= 10;\n }\n return string(buffer);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n if (value == 0) {\n return \"0x00\";\n }\n uint256 temp = value;\n uint256 length = 0;\n while (temp != 0) {\n length++;\n temp >>= 8;\n }\n return toHexString(value, length);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n\n /**\n * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n */\n function toHexString(address addr) internal pure returns (string memory) {\n return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n }\n}\n" + }, + "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165Upgradeable.sol\";\nimport \"../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {\n function __ERC165_init() internal onlyInitializing {\n }\n\n function __ERC165_init_unchained() internal onlyInitializing {\n }\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IERC165Upgradeable).interfaceId;\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[50] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/UUPSUpgradeable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../interfaces/draft-IERC1822Upgradeable.sol\";\nimport \"../ERC1967/ERC1967UpgradeUpgradeable.sol\";\nimport \"./Initializable.sol\";\n\n/**\n * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an\n * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.\n *\n * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is\n * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing\n * `UUPSUpgradeable` with a custom implementation of upgrades.\n *\n * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.\n *\n * _Available since v4.1._\n */\nabstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {\n function __UUPSUpgradeable_init() internal onlyInitializing {\n }\n\n function __UUPSUpgradeable_init_unchained() internal onlyInitializing {\n }\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment\n address private immutable __self = address(this);\n\n /**\n * @dev Check that the execution is being performed through a delegatecall call and that the execution context is\n * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case\n * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a\n * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to\n * fail.\n */\n modifier onlyProxy() {\n require(address(this) != __self, \"Function must be called through delegatecall\");\n require(_getImplementation() == __self, \"Function must be called through active proxy\");\n _;\n }\n\n /**\n * @dev Check that the execution is not being performed through a delegate call. This allows a function to be\n * callable on the implementing contract but not through proxies.\n */\n modifier notDelegated() {\n require(address(this) == __self, \"UUPSUpgradeable: must not be called through delegatecall\");\n _;\n }\n\n /**\n * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the\n * implementation. It is used to validate that the this implementation remains valid after an upgrade.\n *\n * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks\n * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this\n * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.\n */\n function proxiableUUID() external view virtual override notDelegated returns (bytes32) {\n return _IMPLEMENTATION_SLOT;\n }\n\n /**\n * @dev Upgrade the implementation of the proxy to `newImplementation`.\n *\n * Calls {_authorizeUpgrade}.\n *\n * Emits an {Upgraded} event.\n */\n function upgradeTo(address newImplementation) external virtual onlyProxy {\n _authorizeUpgrade(newImplementation);\n _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);\n }\n\n /**\n * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call\n * encoded in `data`.\n *\n * Calls {_authorizeUpgrade}.\n *\n * Emits an {Upgraded} event.\n */\n function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy {\n _authorizeUpgrade(newImplementation);\n _upgradeToAndCallUUPS(newImplementation, data, true);\n }\n\n /**\n * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by\n * {upgradeTo} and {upgradeToAndCall}.\n *\n * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.\n *\n * ```solidity\n * function _authorizeUpgrade(address) internal override onlyOwner {}\n * ```\n */\n function _authorizeUpgrade(address newImplementation) internal virtual;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[50] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/interfaces/draft-IERC1822Upgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified\n * proxy whose upgrades are fully controlled by the current implementation.\n */\ninterface IERC1822ProxiableUpgradeable {\n /**\n * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation\n * address.\n *\n * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks\n * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this\n * function revert if invoked through a proxy.\n */\n function proxiableUUID() external view returns (bytes32);\n}\n" + }, + "@openzeppelin/contracts-upgradeable/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)\n\npragma solidity ^0.8.2;\n\nimport \"../beacon/IBeaconUpgradeable.sol\";\nimport \"../../interfaces/draft-IERC1822Upgradeable.sol\";\nimport \"../../utils/AddressUpgradeable.sol\";\nimport \"../../utils/StorageSlotUpgradeable.sol\";\nimport \"../utils/Initializable.sol\";\n\n/**\n * @dev This abstract contract provides getters and event emitting update functions for\n * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.\n *\n * _Available since v4.1._\n *\n * @custom:oz-upgrades-unsafe-allow delegatecall\n */\nabstract contract ERC1967UpgradeUpgradeable is Initializable {\n function __ERC1967Upgrade_init() internal onlyInitializing {\n }\n\n function __ERC1967Upgrade_init_unchained() internal onlyInitializing {\n }\n // This is the keccak-256 hash of \"eip1967.proxy.rollback\" subtracted by 1\n bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;\n\n /**\n * @dev Storage slot with the address of the current implementation.\n * This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /**\n * @dev Emitted when the implementation is upgraded.\n */\n event Upgraded(address indexed implementation);\n\n /**\n * @dev Returns the current implementation address.\n */\n function _getImplementation() internal view returns (address) {\n return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;\n }\n\n /**\n * @dev Stores a new address in the EIP1967 implementation slot.\n */\n function _setImplementation(address newImplementation) private {\n require(AddressUpgradeable.isContract(newImplementation), \"ERC1967: new implementation is not a contract\");\n StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\n }\n\n /**\n * @dev Perform implementation upgrade\n *\n * Emits an {Upgraded} event.\n */\n function _upgradeTo(address newImplementation) internal {\n _setImplementation(newImplementation);\n emit Upgraded(newImplementation);\n }\n\n /**\n * @dev Perform implementation upgrade with additional setup call.\n *\n * Emits an {Upgraded} event.\n */\n function _upgradeToAndCall(\n address newImplementation,\n bytes memory data,\n bool forceCall\n ) internal {\n _upgradeTo(newImplementation);\n if (data.length > 0 || forceCall) {\n _functionDelegateCall(newImplementation, data);\n }\n }\n\n /**\n * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.\n *\n * Emits an {Upgraded} event.\n */\n function _upgradeToAndCallUUPS(\n address newImplementation,\n bytes memory data,\n bool forceCall\n ) internal {\n // Upgrades from old implementations will perform a rollback test. This test requires the new\n // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing\n // this special case will break upgrade paths from old UUPS implementation to new ones.\n if (StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT).value) {\n _setImplementation(newImplementation);\n } else {\n try IERC1822ProxiableUpgradeable(newImplementation).proxiableUUID() returns (bytes32 slot) {\n require(slot == _IMPLEMENTATION_SLOT, \"ERC1967Upgrade: unsupported proxiableUUID\");\n } catch {\n revert(\"ERC1967Upgrade: new implementation is not UUPS\");\n }\n _upgradeToAndCall(newImplementation, data, forceCall);\n }\n }\n\n /**\n * @dev Storage slot with the admin of the contract.\n * This is the keccak-256 hash of \"eip1967.proxy.admin\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\n\n /**\n * @dev Emitted when the admin account has changed.\n */\n event AdminChanged(address previousAdmin, address newAdmin);\n\n /**\n * @dev Returns the current admin.\n */\n function _getAdmin() internal view returns (address) {\n return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value;\n }\n\n /**\n * @dev Stores a new address in the EIP1967 admin slot.\n */\n function _setAdmin(address newAdmin) private {\n require(newAdmin != address(0), \"ERC1967: new admin is the zero address\");\n StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = newAdmin;\n }\n\n /**\n * @dev Changes the admin of the proxy.\n *\n * Emits an {AdminChanged} event.\n */\n function _changeAdmin(address newAdmin) internal {\n emit AdminChanged(_getAdmin(), newAdmin);\n _setAdmin(newAdmin);\n }\n\n /**\n * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.\n * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.\n */\n bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;\n\n /**\n * @dev Emitted when the beacon is upgraded.\n */\n event BeaconUpgraded(address indexed beacon);\n\n /**\n * @dev Returns the current beacon.\n */\n function _getBeacon() internal view returns (address) {\n return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value;\n }\n\n /**\n * @dev Stores a new beacon in the EIP1967 beacon slot.\n */\n function _setBeacon(address newBeacon) private {\n require(AddressUpgradeable.isContract(newBeacon), \"ERC1967: new beacon is not a contract\");\n require(\n AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()),\n \"ERC1967: beacon implementation is not a contract\"\n );\n StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon;\n }\n\n /**\n * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does\n * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).\n *\n * Emits a {BeaconUpgraded} event.\n */\n function _upgradeBeaconToAndCall(\n address newBeacon,\n bytes memory data,\n bool forceCall\n ) internal {\n _setBeacon(newBeacon);\n emit BeaconUpgraded(newBeacon);\n if (data.length > 0 || forceCall) {\n _functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data);\n }\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function _functionDelegateCall(address target, bytes memory data) private returns (bytes memory) {\n require(AddressUpgradeable.isContract(target), \"Address: delegate call to non-contract\");\n\n // solhint-disable-next-line avoid-low-level-calls\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return AddressUpgradeable.verifyCallResult(success, returndata, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[50] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/proxy/beacon/IBeaconUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev This is the interface that {BeaconProxy} expects of its beacon.\n */\ninterface IBeaconUpgradeable {\n /**\n * @dev Must return an address that can be used as a delegate call target.\n *\n * {BeaconProxy} will check that this address is a contract.\n */\n function implementation() external view returns (address);\n}\n" + }, + "@openzeppelin/contracts-upgradeable/utils/StorageSlotUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for reading and writing primitive types to specific storage slots.\n *\n * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.\n * This library helps with reading and writing to such slots without the need for inline assembly.\n *\n * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.\n *\n * Example usage to set ERC1967 implementation slot:\n * ```\n * contract ERC1967 {\n * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n *\n * function _getImplementation() internal view returns (address) {\n * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\n * }\n *\n * function _setImplementation(address newImplementation) internal {\n * require(Address.isContract(newImplementation), \"ERC1967: new implementation is not a contract\");\n * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\n * }\n * }\n * ```\n *\n * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._\n */\nlibrary StorageSlotUpgradeable {\n struct AddressSlot {\n address value;\n }\n\n struct BooleanSlot {\n bool value;\n }\n\n struct Bytes32Slot {\n bytes32 value;\n }\n\n struct Uint256Slot {\n uint256 value;\n }\n\n /**\n * @dev Returns an `AddressSlot` with member `value` located at `slot`.\n */\n function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {\n /// @solidity memory-safe-assembly\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `BooleanSlot` with member `value` located at `slot`.\n */\n function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {\n /// @solidity memory-safe-assembly\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.\n */\n function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {\n /// @solidity memory-safe-assembly\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `Uint256Slot` with member `value` located at `slot`.\n */\n function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {\n /// @solidity memory-safe-assembly\n assembly {\n r.slot := slot\n }\n }\n}\n" + }, + "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/ContextUpgradeable.sol\";\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract PausableUpgradeable is Initializable, ContextUpgradeable {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n function __Pausable_init() internal onlyInitializing {\n __Pausable_init_unchained();\n }\n\n function __Pausable_init_unchained() internal onlyInitializing {\n _paused = false;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n _requireNotPaused();\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n _requirePaused();\n _;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Throws if the contract is paused.\n */\n function _requireNotPaused() internal view virtual {\n require(!paused(), \"Pausable: paused\");\n }\n\n /**\n * @dev Throws if the contract is not paused.\n */\n function _requirePaused() internal view virtual {\n require(paused(), \"Pausable: not paused\");\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[49] private __gap;\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + } + }, + "settings": { + "metadata": { + "useLiteralContent": true + }, + "optimizer": { + "enabled": true, + "runs": 200 + }, + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers" + ], + "": [ + "id", + "ast" + ] + } + } + } +} \ No newline at end of file diff --git a/test/vault-strat-migration-test.t.sol b/test/vault-strat-migration-test.t.sol index df69a21..36a95d9 100644 --- a/test/vault-strat-migration-test.t.sol +++ b/test/vault-strat-migration-test.t.sol @@ -9,6 +9,7 @@ import "./abstracts/XbooConstants.t.sol"; contract productionMigration is XbooConstants { address existingVault = 0xFC550BAD3c14160CBA7bc05ee263b3F060149AFF; address existingStrat = 0xB62100d94436f53a87516f1aa3Cf42f8A96Ae049; + address newStrat = 0x98c3956B492dc4984D1a9752792120ebfCdC9c9B; ReaperAutoCompoundXBoov2 XbooStrat; ReaperAutoCompoundXBoov2 stratIMPL; @@ -23,16 +24,16 @@ contract productionMigration is XbooConstants { vault = ReaperVaultv1_3(existingVault); vm.label(existingVault, "vault"); - stratIMPL = new ReaperAutoCompoundXBoov2(); + /*stratIMPL = new ReaperAutoCompoundXBoov2(); vm.label(address(stratIMPL), "strategy Implementation"); stratProxy = new ERC1967Proxy( address(stratIMPL), "" //args ); - vm.label(address(stratProxy), "ERC1967 Proxy: Strategy Proxy"); - - XbooStrat = ReaperAutoCompoundXBoov2(address(stratProxy)); + vm.label(address(stratProxy), "ERC1967 Proxy: Strategy Proxy");*/ + XbooStrat = ReaperAutoCompoundXBoov2(newStrat); +/* address[] memory feeRemitters = new address[](2); feeRemitters[0] = address(0xb0C9D5851deF8A2Aac4A23031CA2610f8C3483F9); feeRemitters[1] = address(1); @@ -46,7 +47,7 @@ contract productionMigration is XbooConstants { strategists[0] = address(0xb0C9D5851deF8A2Aac4A23031CA2610f8C3483F9); XbooStrat.initialize(address(vault), feeRemitters, strategists, msRoles); - +*/ uint256 time = block.timestamp; oldPPFS = vault.getPricePerFullShare(); oldBalance = vault.balance(); From 24bf615a1a60413a48125cbb3ef917aa9e68eac2 Mon Sep 17 00:00:00 2001 From: Magnus Brantheim Date: Fri, 27 Jan 2023 20:07:11 +0100 Subject: [PATCH 04/14] Add timestamps to allocations for boo strateg and magicats handler --- contracts/MagicatsHandlerUpgradeable.sol | 8 ++++++++ contracts/ReaperAutoCompoundXBoo.sol | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/contracts/MagicatsHandlerUpgradeable.sol b/contracts/MagicatsHandlerUpgradeable.sol index 2218b2d..0426883 100644 --- a/contracts/MagicatsHandlerUpgradeable.sol +++ b/contracts/MagicatsHandlerUpgradeable.sol @@ -75,6 +75,13 @@ contract MagicatsHandlerUpgradeable is // address of the strategy the magicatsHandler hooks into address public strategy; + /** + * @dev Variables for off-chain bot + * {lastAllocationTimestamp} - The block.timestamp of the most recent call to updateStakedMagicats + * allowing the bot not to make unecessary re-allocations + */ + uint256 public lastAllocationTimestamp; + /// @custom:oz-upgrades-unsafe-allow constructor constructor() initializer {} @@ -176,6 +183,7 @@ contract MagicatsHandlerUpgradeable is for (uint256 i = 0; i < IDsToStake.length; i = _uncheckedInc(i)) { magicatIdToStakedPid[IDsToStake[i]] = poolID; } + lastAllocationTimestamp = block.timestamp; } /*** diff --git a/contracts/ReaperAutoCompoundXBoo.sol b/contracts/ReaperAutoCompoundXBoo.sol index 138a1ba..5bbf9cb 100644 --- a/contracts/ReaperAutoCompoundXBoo.sol +++ b/contracts/ReaperAutoCompoundXBoo.sol @@ -87,6 +87,13 @@ contract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgrad //and the address of the contract that can handle it according the pre-set API mapping(uint256 => RewardHandler) public idToSpecialHandler; + /** + * @dev Variables for off-chain bot + * {lastAllocationTimestamp} - The block.timestamp of the most recent call to setXBooAllocations + * allowing the bot not to make unecessary re-allocations + */ + uint256 public lastAllocationTimestamp; + /** * @dev Initializes the strategy. Sets parameters, saves routes, and gives allowances. * @notice see documentation for each variable above its respective declaration. @@ -220,6 +227,8 @@ contract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgrad uint256 depositAmount = _getMin(XBOOAvailable, depositAmounts[i]); _aceLabDeposit(depositPoolIds[i], depositAmount); } + + lastAllocationTimestamp = block.timestamp; } /** From 8c717c3de90f5b89035b21a30b48da388340e5ce Mon Sep 17 00:00:00 2001 From: Zokunei Date: Fri, 27 Jan 2023 14:32:23 -0500 Subject: [PATCH 05/14] various gas optimizations --- contracts/ReaperAutoCompoundXBoo.sol | 34 +++++++++++++--------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/contracts/ReaperAutoCompoundXBoo.sol b/contracts/ReaperAutoCompoundXBoo.sol index 5bbf9cb..7540de4 100644 --- a/contracts/ReaperAutoCompoundXBoo.sol +++ b/contracts/ReaperAutoCompoundXBoo.sol @@ -157,11 +157,10 @@ contract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgrad if (booBalance < _amount) { uint256 xBooToWithdraw = XBOO.BOOForxBOO(_amount - booBalance); uint256[] memory depositedPoolIDs = depositedPools.values(); - uint256 depositPoolsLength = depositedPoolIDs.length; - uint256 withdrawnAmount = 0; + uint256 withdrawnAmount; uint256 amountToWithdraw; - for (uint256 i = 0; i < depositPoolsLength; i = _uncheckedInc(i)) { + for (uint256 i = 0; i < depositedPoolIDs.length; i = _uncheckedInc(i)) { uint256 currentDepositedPoolId = depositedPoolIDs[i]; amountToWithdraw = _getMin(poolXBOOBalance[currentDepositedPoolId], _amount - withdrawnAmount); _aceLabWithdraw(currentDepositedPoolId, amountToWithdraw); @@ -190,8 +189,9 @@ contract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgrad */ function _aceLabWithdraw(uint256 _poolId, uint256 _XBOOAmount) internal { totalPoolBalance -= _XBOOAmount; - poolXBOOBalance[_poolId] -= _XBOOAmount; - if (poolXBOOBalance[_poolId] == 0 && depositedPools.contains(_poolId)) { + uint256 xBOOBalance = poolXBOOBalance[_poolId] - _XBOOAmount; + poolXBOOBalance[_poolId] = xBOOBalance; + if (xBOOBalance == 0 && depositedPools.contains(_poolId)) { depositedPools.remove(_poolId); } _writeCatDebt(_poolId); @@ -212,14 +212,12 @@ contract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgrad uint256[] calldata depositAmounts ) external { _atLeastRole(KEEPER); - uint256 depositPoolsLength = depositPoolIds.length; - uint256 withdrawPoolsLength = withdrawPoolIds.length; - for (uint256 i = 0; i < withdrawPoolsLength; i = _uncheckedInc(i)) { + for (uint256 i = 0; i < withdrawPoolIds.length; i = _uncheckedInc(i)) { _aceLabWithdraw(withdrawPoolIds[i], withdrawAmounts[i]); } - for (uint256 i = 0; i < depositPoolsLength; i = _uncheckedInc(i)) { + for (uint256 i = 0; i < depositPoolIds.length; i = _uncheckedInc(i)) { uint256 XBOOAvailable = IERC20Upgradeable(XBOO).balanceOf(address(this)); if (XBOOAvailable == 0) { return; @@ -253,8 +251,7 @@ contract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgrad function _claimAllRewards() internal { uint256[] memory depositedPoolIDs = depositedPools.values(); - uint256 depositPoolsLength = depositedPoolIDs.length; - for (uint256 i = 0; i < depositPoolsLength; i = _uncheckedInc(i)) { + for (uint256 i = 0; i < depositedPoolIDs.length; i = _uncheckedInc(i)) { _aceLabWithdraw(depositedPoolIDs[i], 0); } } @@ -272,6 +269,7 @@ contract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgrad uint256 catBoostPercent; uint256 catBoostWFTM; uint256 catBoostTotal; + uint256 catDebt; uint256 totalHarvest; address rewardToken; uint256 activeIndex; @@ -285,8 +283,9 @@ contract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgrad //leave is pretty standard for xTokens if it does not have leave we will need an external handler try IBooMirrorWorld(rewardToken).leave(tokenBal) {} catch {} - if (accCatDebt[activeIndex] != 0) { - catBoostPercent = (accCatDebt[activeIndex] * PERCENT_DIVISOR) / tokenBal; + catDebt = accCatDebt[activeIndex]; + if (catDebt != 0) { + catBoostPercent = (catDebt * PERCENT_DIVISOR) / tokenBal; } else { catBoostPercent = 0; } @@ -431,15 +430,14 @@ contract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgrad */ function _reclaimWant() internal override { uint256[] memory depositedPoolIDs = depositedPools.values(); - uint256 depositPoolsLength = depositedPoolIDs.length; uint256 currentDepositedPoolId; - for (uint256 index = 0; index < depositPoolsLength; index = _uncheckedInc(index)) { + for (uint256 index = 0; index < depositedPoolIDs.length; index = _uncheckedInc(index)) { currentDepositedPoolId = depositedPoolIDs[index]; IAceLab(aceLab).emergencyWithdraw(currentDepositedPoolId); - totalPoolBalance -= poolXBOOBalance[currentDepositedPoolId]; poolXBOOBalance[currentDepositedPoolId] = 0; depositedPools.remove(currentDepositedPoolId); } + totalPoolBalance = 0; IMagicatsHandler(magicatsHandler).massUnstakeMagicats(); @@ -522,11 +520,11 @@ contract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgrad uint256[] memory IDsToUnstake ) public { _atLeastRole(MAGICATS_HANDLER); - if (IDsToUnstake.length > 0) { + if (IDsToUnstake.length != 0) { IAceLab(aceLab).withdraw(poolID, 0, IDsToUnstake); } - if (IDsToStake.length > 0) { + if (IDsToStake.length != 0) { IAceLab(aceLab).deposit(poolID, 0, IDsToStake); } } From 2fc59716c0ebd646ee6d289da4804822759e3c8a Mon Sep 17 00:00:00 2001 From: Magnus Brantheim Date: Mon, 30 Jan 2023 12:53:32 +0100 Subject: [PATCH 06/14] Add forge tests for strategy and magicat upgrade --- test/magicat-upgrade-test.t.sol | 52 +++++++++++++++++++++++++++ test/strat-upgrade-test.t.sol | 63 +++++++++++++++++++++++++++++++++ test/testCMDs | 6 +++- 3 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 test/magicat-upgrade-test.t.sol create mode 100644 test/strat-upgrade-test.t.sol diff --git a/test/magicat-upgrade-test.t.sol b/test/magicat-upgrade-test.t.sol new file mode 100644 index 0000000..dcb2aa9 --- /dev/null +++ b/test/magicat-upgrade-test.t.sol @@ -0,0 +1,52 @@ +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "../contracts/MagicatsHandlerUpgradeable.sol"; +import "./abstracts/XbooConstants.t.sol"; + +contract ProductionMagicatUpgrade is XbooConstants { + address payable public existingHandler = payable(0xD3BF27E1606dF8Ac80f4Fd3c4faF47b8c31a1021); + MagicatsHandlerUpgradeable public currentHandler; + MagicatsHandlerUpgradeable public newHandler; + + uint256 public oldMP; + uint256 public oldSupply; + + function setUp() public { + currentHandler = MagicatsHandlerUpgradeable(existingHandler); + vm.label(existingHandler, "handler"); + oldMP = currentHandler.totalMp(); + oldSupply = currentHandler.totalSupply(); + console.log("oldMP: ", oldMP); + console.log("oldSupply: ", oldSupply); + newHandler = new MagicatsHandlerUpgradeable(); + console.log("address(newHandler): ", address(newHandler)); + } + + function testHandlerProductionUpgradeValidity() public { + vm.expectRevert(); + uint256 lastAllocation = currentHandler.lastAllocationTimestamp(); + + vm.startPrank(0xe1610bB38Ce95254dD77cbC82F9c1148569B560e); + currentHandler.upgradeTo(address(newHandler)); + + uint256 newMP = currentHandler.totalMp(); + uint256 newSupply = currentHandler.totalSupply(); + console.log("newMP: ", newMP); + console.log("newSupply: ", newSupply); + assertGe(newMP, oldMP); + assertGe(newSupply, oldSupply); + + lastAllocation = currentHandler.lastAllocationTimestamp(); + console.log("lastAllocation: ", lastAllocation); + assertEq(lastAllocation, 0); + + uint256[] memory args = new uint256[](0); + uint256 poolID = 0; + currentHandler.updateStakedMagicats(poolID, args, args); + lastAllocation = currentHandler.lastAllocationTimestamp(); + console.log("lastAllocation: ", lastAllocation); + assertEq(lastAllocation, block.timestamp); + } +} diff --git a/test/strat-upgrade-test.t.sol b/test/strat-upgrade-test.t.sol new file mode 100644 index 0000000..7f38b20 --- /dev/null +++ b/test/strat-upgrade-test.t.sol @@ -0,0 +1,63 @@ +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; +import "../contracts/ReaperAutoCompoundXBoo.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "../contracts/ReaperVaultv1_3.sol"; +import "./abstracts/XbooConstants.t.sol"; + +contract ProductionStrategyUpgrade is XbooConstants { + address public existingVault = 0xFC550BAD3c14160CBA7bc05ee263b3F060149AFF; + address payable public existingStrat = payable(0x98c3956B492dc4984D1a9752792120ebfCdC9c9B); + ReaperVaultv1_3 public vault; + ReaperAutoCompoundXBoov2 public currentStrat; + ReaperAutoCompoundXBoov2 public newStrat; + + uint256 public oldPPFS; + uint256 public oldSupply; + uint256 public oldBalance; + + function setUp() public { + vault = ReaperVaultv1_3(existingVault); + vm.label(existingVault, "vault"); + oldPPFS = vault.getPricePerFullShare(); + oldBalance = vault.balance(); + oldSupply = vault.totalSupply(); + currentStrat = ReaperAutoCompoundXBoov2(existingStrat); + console.log("oldPPFS: ", oldPPFS); + console.log("oldBalance: ", oldBalance); + console.log("oldSupply: ", oldSupply); + uint256 balance = currentStrat.balanceOf(); + console.log("balance: ", balance); + newStrat = new ReaperAutoCompoundXBoov2(); + console.log("address(newStrat): ", address(newStrat)); + } + + function testStrategyProductionUpgradeValidity() public { + vm.expectRevert(); + uint256 lastAllocation = currentStrat.lastAllocationTimestamp(); + + vm.startPrank(0xe1610bB38Ce95254dD77cbC82F9c1148569B560e); + currentStrat.upgradeTo(address(newStrat)); + + uint256 newPPFS = vault.getPricePerFullShare(); + uint256 newBalance = vault.balance(); + uint256 newSupply = vault.totalSupply(); + console.log("newPPFS: ", newPPFS); + console.log("newBalance: ", newBalance); + console.log("newSupply: ", newSupply); + assertGe(newPPFS, oldPPFS); + assertGe(newBalance, oldBalance); + assertGe(newSupply, oldSupply); + + lastAllocation = currentStrat.lastAllocationTimestamp(); + console.log("lastAllocation: ", lastAllocation); + assertEq(lastAllocation, 0); + + uint256[] memory args = new uint256[](0); + currentStrat.setXBooAllocations(args, args, args, args); + lastAllocation = currentStrat.lastAllocationTimestamp(); + console.log("lastAllocation: ", lastAllocation); + assertEq(lastAllocation, block.timestamp); + } +} diff --git a/test/testCMDs b/test/testCMDs index cca6377..96f13d9 100644 --- a/test/testCMDs +++ b/test/testCMDs @@ -2,4 +2,8 @@ forge test --fork-url https://rpc.ftm.tools --fork-block-number 48900000 --match forge test --match-contract magicatTest --match-test testMagicat --fork-url https://rpc.ftm.tools --fork-block-number 48900000 -vvv -forge test --fork-url https://rpc.ftm.tools --fork-block-number 48900000 --match-contract productionMigration -vvv \ No newline at end of file +forge test --fork-url https://rpc.ftm.tools --fork-block-number 48900000 --match-contract productionMigration -vvv + +forge test --fork-url https://rpc.ftm.tools --match-contract ProductionStrategyUpgrade -vvv + +forge test --fork-url https://rpc.ftm.tools --match-contract ProductionMagicatUpgrade -vvv From c9153190118ea2596e0fdd36da36ef9379baab22 Mon Sep 17 00:00:00 2001 From: Magnus Brantheim Date: Mon, 30 Jan 2023 21:52:34 +0100 Subject: [PATCH 07/14] Add gated harvest, remove withdraw fee, add tests --- contracts/abstract/ReaperBaseStrategy.sol | 4 +--- test/strat-upgrade-test.t.sol | 27 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/contracts/abstract/ReaperBaseStrategy.sol b/contracts/abstract/ReaperBaseStrategy.sol index d8756b1..ef7dda2 100644 --- a/contracts/abstract/ReaperBaseStrategy.sol +++ b/contracts/abstract/ReaperBaseStrategy.sol @@ -157,9 +157,6 @@ abstract contract ReaperBaseStrategyv3 is require(_amount != 0); require(_amount <= balanceOf()); - uint256 withdrawFee = (_amount * securityFee) / PERCENT_DIVISOR; - _amount -= withdrawFee; - _withdraw(_amount); } @@ -168,6 +165,7 @@ abstract contract ReaperBaseStrategyv3 is * override _harvestCore() and implement their specific logic in it. */ function harvest() external override whenNotPaused returns (uint256 callerFee) { + _atLeastRole(KEEPER); callerFee = _harvestCore(); if (block.timestamp >= harvestLog[harvestLog.length - 1].timestamp + harvestLogCadence) { diff --git a/test/strat-upgrade-test.t.sol b/test/strat-upgrade-test.t.sol index 7f38b20..8e3b56c 100644 --- a/test/strat-upgrade-test.t.sol +++ b/test/strat-upgrade-test.t.sol @@ -60,4 +60,31 @@ contract ProductionStrategyUpgrade is XbooConstants { console.log("lastAllocation: ", lastAllocation); assertEq(lastAllocation, block.timestamp); } + + function testStrategyProductionUpgradeHarvestValidity() public { + uint256 callerFee = currentStrat.harvest(); + console.log("callerFee: ", callerFee); + assertGe(callerFee, 0); + + uint256 time = uint256(block.timestamp); + vm.warp(time + 24 hours); + + vm.startPrank(0xe1610bB38Ce95254dD77cbC82F9c1148569B560e); + currentStrat.upgradeTo(address(newStrat)); + vm.stopPrank(); + + vm.expectRevert(); + currentStrat.harvest(); + + address keeper = makeAddr("keeper"); + vm.startPrank(0xe1610bB38Ce95254dD77cbC82F9c1148569B560e); + bytes32 keeperRole = 0x71a9859d7dd21b24504a6f306077ffc2d510b4d4b61128e931fe937441ad1836; + currentStrat.grantRole(keeperRole, keeper); + vm.stopPrank(); + vm.startPrank(keeper); + + callerFee = currentStrat.harvest(); + console.log("callerFee: ", callerFee); + assertGe(callerFee, 0); + } } From 30a0798b7625cee815490e32b76a44e3b7441ef2 Mon Sep 17 00:00:00 2001 From: Magnus Brantheim Date: Tue, 31 Jan 2023 15:54:58 +0100 Subject: [PATCH 08/14] Add back security fee in withdraw, add upgrade function to set keepers, change fee handling in harvest, remove massUnstakeMagicats in reclaimWant --- contracts/ReaperAutoCompoundXBoo.sol | 33 ++++++++++++++++------- contracts/abstract/ReaperBaseStrategy.sol | 3 +++ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/contracts/ReaperAutoCompoundXBoo.sol b/contracts/ReaperAutoCompoundXBoo.sol index 5bbf9cb..7babc11 100644 --- a/contracts/ReaperAutoCompoundXBoo.sol +++ b/contracts/ReaperAutoCompoundXBoo.sol @@ -94,6 +94,12 @@ contract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgrad */ uint256 public lastAllocationTimestamp; + /** + * @dev Variable for contract upgrade + * {v2UpgradeCompleted} - If the one time upgrade function has run or not + */ + bool public v2UpgradeCompleted = false; + /** * @dev Initializes the strategy. Sets parameters, saves routes, and gives allowances. * @notice see documentation for each variable above its respective declaration. @@ -238,10 +244,10 @@ contract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgrad * 3. It swaps the {WFTM} token for {BOO} which is deposited into {XBOO} * 4. It distributes the XBOO using a yield optimization algorithm into various pools. */ - function _harvestCore() internal override returns (uint256 callerFee) { + function _harvestCore() internal override returns (uint256 feeCharged) { _claimAllRewards(); uint256 catBoostPercentage = _processRewards(); - callerFee = _chargeFees(); + feeCharged = _chargeFees(); _swapWFTMToBOO(); if (magicatsHandler != address(0) && catBoostPercentage != 0) { _payMagicatDepositors(catBoostPercentage); @@ -337,7 +343,7 @@ contract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgrad * callFeeToUser is set as a percentage of the fee, * as is treasuryFeeToVault */ - function _chargeFees() internal returns (uint256 callFeeToUser) { + function _chargeFees() internal returns (uint256 feeCharged) { uint256 WFTMFee = (IERC20Upgradeable(WFTM).balanceOf(address(this)) * totalFee) / PERCENT_DIVISOR; if (WFTMFee != 0) { IUniswapRouterETH(UNIROUTER).swapExactTokensForTokensSupportingFeeOnTransferTokens( @@ -347,12 +353,9 @@ contract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgrad address(this), block.timestamp ); - uint256 USDCBal = IERC20Upgradeable(USDC).balanceOf(address(this)); - callFeeToUser = (USDCBal * callFee) / PERCENT_DIVISOR; - uint256 treasuryFeeToVault = (USDCBal * treasuryFee) / PERCENT_DIVISOR; + feeCharged = IERC20Upgradeable(USDC).balanceOf(address(this)); - IERC20Upgradeable(USDC).safeTransfer(msg.sender, callFeeToUser); - IERC20Upgradeable(USDC).safeTransfer(treasury, treasuryFeeToVault); + IERC20Upgradeable(USDC).safeTransfer(treasury, feeCharged); } } @@ -441,8 +444,6 @@ contract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgrad depositedPools.remove(currentDepositedPoolId); } - IMagicatsHandler(magicatsHandler).massUnstakeMagicats(); - uint256 XBOOBalance = XBOO.balanceOf(address(this)); XBOO.leave(XBOOBalance); } @@ -648,4 +649,16 @@ contract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgrad min = _b; } } + + // One-time function to modify state post-V2 upgrade + function completeV2Upgrade(address[] memory _keepers) external { + _atLeastRole(STRATEGIST); + require(!v2UpgradeCompleted, "V2 upgrade has already been completed"); + v2UpgradeCompleted = true; + address keeper; + for (uint256 index = 0; index < _keepers.length; index = _uncheckedInc(index)) { + keeper = _keepers[index]; + grantRole(KEEPER, keeper); + } + } } diff --git a/contracts/abstract/ReaperBaseStrategy.sol b/contracts/abstract/ReaperBaseStrategy.sol index ef7dda2..6afb630 100644 --- a/contracts/abstract/ReaperBaseStrategy.sol +++ b/contracts/abstract/ReaperBaseStrategy.sol @@ -157,6 +157,9 @@ abstract contract ReaperBaseStrategyv3 is require(_amount != 0); require(_amount <= balanceOf()); + uint256 withdrawFee = (_amount * securityFee) / PERCENT_DIVISOR; + _amount -= withdrawFee; + _withdraw(_amount); } From 10e3dd5efbe3cb2d19e9894c2b943c06025b7a04 Mon Sep 17 00:00:00 2001 From: Magnus Brantheim Date: Wed, 1 Feb 2023 09:43:02 +0100 Subject: [PATCH 09/14] Add code review changes --- contracts/MagicatsHandlerUpgradeable.sol | 8 ++++++-- contracts/ReaperAutoCompoundXBoo.sol | 7 ++++--- test/magicat-test.t.sol | 14 +++++--------- test/magicat-upgrade-test.t.sol | 3 ++- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/contracts/MagicatsHandlerUpgradeable.sol b/contracts/MagicatsHandlerUpgradeable.sol index 0426883..dff593b 100644 --- a/contracts/MagicatsHandlerUpgradeable.sol +++ b/contracts/MagicatsHandlerUpgradeable.sol @@ -183,7 +183,6 @@ contract MagicatsHandlerUpgradeable is for (uint256 i = 0; i < IDsToStake.length; i = _uncheckedInc(i)) { magicatIdToStakedPid[IDsToStake[i]] = poolID; } - lastAllocationTimestamp = block.timestamp; } /*** @@ -195,10 +194,15 @@ contract MagicatsHandlerUpgradeable is function updateStakedMagicats( uint256 poolID, uint256[] memory IDsToStake, - uint256[] memory IDsToUnstake + uint256[] memory IDsToUnstake, + bool allocationCompleted ) external { _atLeastRole(KEEPER); _updateStakedMagicats(poolID, IDsToStake, IDsToUnstake); + + if (allocationCompleted) { + lastAllocationTimestamp = block.timestamp; + } } /*** diff --git a/contracts/ReaperAutoCompoundXBoo.sol b/contracts/ReaperAutoCompoundXBoo.sol index 4c61f69..fded4d2 100644 --- a/contracts/ReaperAutoCompoundXBoo.sol +++ b/contracts/ReaperAutoCompoundXBoo.sol @@ -98,7 +98,7 @@ contract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgrad * @dev Variable for contract upgrade * {v2UpgradeCompleted} - If the one time upgrade function has run or not */ - bool public v2UpgradeCompleted = false; + bool public v2UpgradeCompleted; /** * @dev Initializes the strategy. Sets parameters, saves routes, and gives allowances. @@ -345,6 +345,7 @@ contract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgrad function _chargeFees() internal returns (uint256 feeCharged) { uint256 WFTMFee = (IERC20Upgradeable(WFTM).balanceOf(address(this)) * totalFee) / PERCENT_DIVISOR; if (WFTMFee != 0) { + uint256 usdcBalanceBefore = IERC20Upgradeable(USDC).balanceOf(address(this)); IUniswapRouterETH(UNIROUTER).swapExactTokensForTokensSupportingFeeOnTransferTokens( WFTMFee, 0, @@ -352,7 +353,7 @@ contract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgrad address(this), block.timestamp ); - feeCharged = IERC20Upgradeable(USDC).balanceOf(address(this)); + feeCharged = IERC20Upgradeable(USDC).balanceOf(address(this)) - usdcBalanceBefore; IERC20Upgradeable(USDC).safeTransfer(treasury, feeCharged); } @@ -656,7 +657,7 @@ contract ReaperAutoCompoundXBoov2 is ReaperBaseStrategyv3, IERC721ReceiverUpgrad address keeper; for (uint256 index = 0; index < _keepers.length; index = _uncheckedInc(index)) { keeper = _keepers[index]; - grantRole(KEEPER, keeper); + _grantRole(KEEPER, keeper); } } } diff --git a/test/magicat-test.t.sol b/test/magicat-test.t.sol index 70b5fa8..a81d6fd 100644 --- a/test/magicat-test.t.sol +++ b/test/magicat-test.t.sol @@ -13,8 +13,6 @@ contract magicatTest is xBooTest { MagicatsHandlerUpgradeable handlerIMPL; ERC1967Proxy handlerProxy; - - function setUp() public override { xBooTest.setUp(); @@ -22,12 +20,8 @@ contract magicatTest is xBooTest { address[] memory strategists = new address[](1); strategists[0] = address(0xb0C9D5851deF8A2Aac4A23031CA2610f8C3483F9); - handlerIMPL = new MagicatsHandlerUpgradeable(); - handlerProxy = new ERC1967Proxy( - address(handlerIMPL), - "" - ); + handlerProxy = new ERC1967Proxy(address(handlerIMPL), ""); handler = MagicatsHandlerUpgradeable(address(handlerProxy)); handler.initialize(address(XbooStrat), address(vault), strategists, msRoles); @@ -140,7 +134,8 @@ contract magicatTest is xBooTest { console.log(stratIds.length); console.log(stratIds[0]); uint256[] memory empty = new uint256[](0); - handler.updateStakedMagicats(HEC_ID, stratIds, empty); + bool allocationCompleted = true; + handler.updateStakedMagicats(HEC_ID, stratIds, empty, allocationCompleted); vm.stopPrank(); uint256 stratBalanceAfterAllocation = IMagicat(currentMagicats).balanceOf(address(XbooStrat)); @@ -159,7 +154,8 @@ contract magicatTest is xBooTest { console.log(stratIds.length); console.log(stratIds[0]); uint256[] memory empty = new uint256[](0); - _handler.updateStakedMagicats(HEC_ID, stratIds, empty); + bool allocationCompleted = true; + _handler.updateStakedMagicats(HEC_ID, stratIds, empty, allocationCompleted); vm.stopPrank(); uint256 stratBalanceAfterAllocation = IMagicat(currentMagicats).balanceOf(address(XbooStrat)); diff --git a/test/magicat-upgrade-test.t.sol b/test/magicat-upgrade-test.t.sol index dcb2aa9..f9d39f0 100644 --- a/test/magicat-upgrade-test.t.sol +++ b/test/magicat-upgrade-test.t.sol @@ -44,7 +44,8 @@ contract ProductionMagicatUpgrade is XbooConstants { uint256[] memory args = new uint256[](0); uint256 poolID = 0; - currentHandler.updateStakedMagicats(poolID, args, args); + bool allocationCompleted = true; + currentHandler.updateStakedMagicats(poolID, args, args, allocationCompleted); lastAllocation = currentHandler.lastAllocationTimestamp(); console.log("lastAllocation: ", lastAllocation); assertEq(lastAllocation, block.timestamp); From 30ac0dc7f48949ee47ebcb5228e7eb85f2467fbc Mon Sep 17 00:00:00 2001 From: Magnus Brantheim Date: Tue, 7 Feb 2023 09:09:50 +0100 Subject: [PATCH 10/14] Add unfinished gated harvest upgrade script --- scripts/gated-harvests-upgrade.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 scripts/gated-harvests-upgrade.js diff --git a/scripts/gated-harvests-upgrade.js b/scripts/gated-harvests-upgrade.js new file mode 100644 index 0000000..6e89220 --- /dev/null +++ b/scripts/gated-harvests-upgrade.js @@ -0,0 +1,30 @@ +// spiritWallet: 0x55a078AFC2e20C8c20d1aa4420710d827Ee494d4 +// spookyWallet: 0x5241F63D0C1f2970c45234a0F5b345036117E3C2 +// liquidWallet: 0xf58d534290Ce9fc4Ea639B8b9eE238Fe83d2efA6 +// popsicleWallet: 0x5318250BD0b44D1740f47a5b6BE4F7fD5042682D +// hyperWallet: 0xa64A9687F7b37268d4043f2cBf23Be56d666696C +// wakaWallet: 0x33D6cB7E91C62Dd6980F16D61e0cfae082CaBFCA +// steakWallet: 0x51263D56ec81B5e823e34d7665A1F505C327b014 +// alphaWallet: 0x87A5AfC8cdDa71B5054C698366E97DB2F3C2BC2f +// betaWallet: 0xe0268Aa6d55FfE1AA7A77587e56784e5b29004A2 +// thetaWallet: 0x34Df14D42988e4Dc622e37dc318e70429336B6c5 +// omegaWallet: 0x73C882796Ea481fe0A2B8DE499d95e60ff971663 +// angieWallet: 0x36a63324edFc157bE22CF63A6Bf1C3B49a0E72C0 +// arkyWallet: 0x9a2AdcbFb972e0EC2946A342f46895702930064F +// crowWallet: 0x7B540a4D24C906E5fB3d3EcD0Bb7B1aEd3823897 +// birbWallet: 0x8456a746e09A18F9187E5babEe6C60211CA728D1 +// noobWallet: 0xd21E0fE4ba0379Ec8DF6263795c8120414Acd0A3 +// poopWallet: 0x9ccA5c3829224F7ac9077540bC365De4384823A7 <-- already keeper +// duckWallet: 0x2BDAaa2aeD043C473D2bA2AeABdF1A721417AB75 +// bruceWallet: 0x87F3Cb66F773e7Ad8dB9c9B973E7D9684fa3D3a9 + +const hre = require('hardhat'); + +async function main() {} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); From 318e719efe71e031a894b6d0e4161efc4129ed90 Mon Sep 17 00:00:00 2001 From: RadioJulius <9730701+RadioJulius@users.noreply.github.com> Date: Tue, 14 Feb 2023 04:31:06 -0800 Subject: [PATCH 11/14] updates completed, new 250.json --- .openzeppelin/unknown-250.json | 1380 +++++++++++++++++++++++++++++ scripts/gated-harvests-upgrade.js | 13 +- 2 files changed, 1392 insertions(+), 1 deletion(-) diff --git a/.openzeppelin/unknown-250.json b/.openzeppelin/unknown-250.json index ad7b35b..94e0494 100644 --- a/.openzeppelin/unknown-250.json +++ b/.openzeppelin/unknown-250.json @@ -5,6 +5,11 @@ "address": "0x98c3956B492dc4984D1a9752792120ebfCdC9c9B", "txHash": "0xae09a35f4dab6c7ce8e2f6678e99eb2df2672db93f7e03763b53b74fe58ca92d", "kind": "uups" + }, + { + "address": "0xD3BF27E1606dF8Ac80f4Fd3c4faF47b8c31a1021", + "txHash": "0xae14c51d6bc8229d4d9786bfba2b8fd123082a208d9c731fbf32cb5c8579de82", + "kind": "uups" } ], "impls": { @@ -479,6 +484,1381 @@ } } } + }, + "95d15c566fb9956dd511aa9c2a9ae2abd2d358194770273e3a6a0e2068522080": { + "address": "0x40D4D0f489AF35b4B482C0043a7721823Ec42765", + "txHash": "0x1e70cd7056ccbf0e18eca294e6c962a85d240e974753e621b92bff3fc4db3fa8", + "layout": { + "solcVersion": "0.8.9", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC1967UpgradeUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol:211" + }, + { + "label": "__gap", + "offset": 0, + "slot": "51", + "type": "t_array(t_uint256)50_storage", + "contract": "UUPSUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol:107" + }, + { + "label": "__gap", + "offset": 0, + "slot": "101", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" + }, + { + "label": "__gap", + "offset": 0, + "slot": "151", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC165Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol:41" + }, + { + "label": "_roles", + "offset": 0, + "slot": "201", + "type": "t_mapping(t_bytes32,t_struct(RoleData)179_storage)", + "contract": "AccessControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:61" + }, + { + "label": "__gap", + "offset": 0, + "slot": "202", + "type": "t_array(t_uint256)49_storage", + "contract": "AccessControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:259" + }, + { + "label": "_roleMembers", + "offset": 0, + "slot": "251", + "type": "t_mapping(t_bytes32,t_struct(AddressSet)4161_storage)", + "contract": "AccessControlEnumerableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "252", + "type": "t_array(t_uint256)49_storage", + "contract": "AccessControlEnumerableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol:76" + }, + { + "label": "_paused", + "offset": 0, + "slot": "301", + "type": "t_bool", + "contract": "PausableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:29" + }, + { + "label": "__gap", + "offset": 0, + "slot": "302", + "type": "t_array(t_uint256)49_storage", + "contract": "PausableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:116" + }, + { + "label": "harvestLog", + "offset": 0, + "slot": "351", + "type": "t_array(t_struct(Harvest)13807_storage)dyn_storage", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:25" + }, + { + "label": "harvestLogCadence", + "offset": 0, + "slot": "352", + "type": "t_uint256", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:26" + }, + { + "label": "lastHarvestTimestamp", + "offset": 0, + "slot": "353", + "type": "t_uint256", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:27" + }, + { + "label": "upgradeProposalTime", + "offset": 0, + "slot": "354", + "type": "t_uint256", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:29" + }, + { + "label": "cascadingAccess", + "offset": 0, + "slot": "355", + "type": "t_array(t_bytes32)dyn_storage", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:50" + }, + { + "label": "treasury", + "offset": 0, + "slot": "356", + "type": "t_address", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:58" + }, + { + "label": "vault", + "offset": 0, + "slot": "357", + "type": "t_address", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:59" + }, + { + "label": "strategistRemitter", + "offset": 0, + "slot": "358", + "type": "t_address", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:60" + }, + { + "label": "totalFee", + "offset": 0, + "slot": "359", + "type": "t_uint256", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:86" + }, + { + "label": "callFee", + "offset": 0, + "slot": "360", + "type": "t_uint256", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:87" + }, + { + "label": "treasuryFee", + "offset": 0, + "slot": "361", + "type": "t_uint256", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:88" + }, + { + "label": "strategistFee", + "offset": 0, + "slot": "362", + "type": "t_uint256", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:89" + }, + { + "label": "securityFee", + "offset": 0, + "slot": "363", + "type": "t_uint256", + "contract": "ReaperBaseStrategyv3", + "src": "contracts/abstract/ReaperBaseStrategy.sol:90" + }, + { + "label": "magicatsHandler", + "offset": 0, + "slot": "364", + "type": "t_address", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:45" + }, + { + "label": "aceLab", + "offset": 0, + "slot": "365", + "type": "t_address", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:46" + }, + { + "label": "WFTMToBOOPath", + "offset": 0, + "slot": "366", + "type": "t_array(t_address)dyn_storage", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:54" + }, + { + "label": "WFTMToUSDCPath", + "offset": 0, + "slot": "367", + "type": "t_array(t_address)dyn_storage", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:55" + }, + { + "label": "poolRewardToWFTMPaths", + "offset": 0, + "slot": "368", + "type": "t_mapping(t_uint256,t_array(t_address)dyn_storage)", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:56" + }, + { + "label": "currentPoolId", + "offset": 0, + "slot": "369", + "type": "t_uint256", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:62" + }, + { + "label": "totalPoolBalance", + "offset": 0, + "slot": "370", + "type": "t_uint256", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:70" + }, + { + "label": "poolXBOOBalance", + "offset": 0, + "slot": "371", + "type": "t_mapping(t_uint256,t_uint256)", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:71" + }, + { + "label": "depositedPools", + "offset": 0, + "slot": "372", + "type": "t_struct(UintSet)4318_storage", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:72" + }, + { + "label": "accCatDebt", + "offset": 0, + "slot": "374", + "type": "t_mapping(t_uint256,t_uint256)", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:79" + }, + { + "label": "catProvisionFee", + "offset": 0, + "slot": "375", + "type": "t_uint256", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:80" + }, + { + "label": "idToSpecialHandler", + "offset": 0, + "slot": "376", + "type": "t_mapping(t_uint256,t_struct(RewardHandler)11253_storage)", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:88" + }, + { + "label": "lastAllocationTimestamp", + "offset": 0, + "slot": "377", + "type": "t_uint256", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:95" + }, + { + "label": "v2UpgradeCompleted", + "offset": 0, + "slot": "378", + "type": "t_bool", + "contract": "ReaperAutoCompoundXBoov2", + "src": "contracts/ReaperAutoCompoundXBoo.sol:101" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_address)dyn_storage": { + "label": "address[]", + "numberOfBytes": "32" + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]", + "numberOfBytes": "32" + }, + "t_array(t_struct(Harvest)13807_storage)dyn_storage": { + "label": "struct ReaperBaseStrategyv3.Harvest[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(AddressSet)4161_storage)": { + "label": "mapping(bytes32 => struct EnumerableSetUpgradeable.AddressSet)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(RoleData)179_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_array(t_address)dyn_storage)": { + "label": "mapping(uint256 => address[])", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_struct(RewardHandler)11253_storage)": { + "label": "mapping(uint256 => struct ReaperAutoCompoundXBoov2.RewardHandler)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_uint256)": { + "label": "mapping(uint256 => uint256)", + "numberOfBytes": "32" + }, + "t_struct(AddressSet)4161_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)3860_storage", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Harvest)13807_storage": { + "label": "struct ReaperBaseStrategyv3.Harvest", + "members": [ + { + "label": "timestamp", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "vaultSharePrice", + "type": "t_uint256", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(RewardHandler)11253_storage": { + "label": "struct ReaperAutoCompoundXBoov2.RewardHandler", + "members": [ + { + "label": "requiresSpecialHandling", + "type": "t_bool", + "offset": 0, + "slot": "0" + }, + { + "label": "handler", + "type": "t_address", + "offset": 1, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(RoleData)179_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "members", + "type": "t_mapping(t_address,t_bool)", + "offset": 0, + "slot": "0" + }, + { + "label": "adminRole", + "type": "t_bytes32", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Set)3860_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage", + "offset": 0, + "slot": "0" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(UintSet)4318_storage": { + "label": "struct EnumerableSetUpgradeable.UintSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)3860_storage", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "64" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + } + } + }, + "7da136a329b287015b9fa3aca056556f50da4258d398be227dda1e263a632f74": { + "address": "0xfCD753219f1A6F9d39fBAF6a6b9d843d8165F19B", + "txHash": "0x9d3deb497d3558e2027f90d8963252c043f91f56d3609f8d1565d135d860da74", + "layout": { + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin\\contracts-upgradeable\\proxy\\utils\\Initializable.sol:62", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin\\contracts-upgradeable\\proxy\\utils\\Initializable.sol:67" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin\\contracts-upgradeable\\utils\\ContextUpgradeable.sol:36" + }, + { + "label": "__gap", + "offset": 0, + "slot": "51", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC165Upgradeable", + "src": "@openzeppelin\\contracts-upgradeable\\utils\\introspection\\ERC165Upgradeable.sol:41" + }, + { + "label": "_roles", + "offset": 0, + "slot": "101", + "type": "t_mapping(t_bytes32,t_struct(RoleData)179_storage)", + "contract": "AccessControlUpgradeable", + "src": "@openzeppelin\\contracts-upgradeable\\access\\AccessControlUpgradeable.sol:61" + }, + { + "label": "__gap", + "offset": 0, + "slot": "102", + "type": "t_array(t_uint256)49_storage", + "contract": "AccessControlUpgradeable", + "src": "@openzeppelin\\contracts-upgradeable\\access\\AccessControlUpgradeable.sol:259" + }, + { + "label": "_roleMembers", + "offset": 0, + "slot": "151", + "type": "t_mapping(t_bytes32,t_struct(AddressSet)4161_storage)", + "contract": "AccessControlEnumerableUpgradeable", + "src": "@openzeppelin\\contracts-upgradeable\\access\\AccessControlEnumerableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "152", + "type": "t_array(t_uint256)49_storage", + "contract": "AccessControlEnumerableUpgradeable", + "src": "@openzeppelin\\contracts-upgradeable\\access\\AccessControlEnumerableUpgradeable.sol:76" + }, + { + "label": "_name", + "offset": 0, + "slot": "201", + "type": "t_string_storage", + "contract": "ERC721Upgradeable", + "src": "@openzeppelin\\contracts-upgradeable\\token\\ERC721\\ERC721Upgradeable.sol:25" + }, + { + "label": "_symbol", + "offset": 0, + "slot": "202", + "type": "t_string_storage", + "contract": "ERC721Upgradeable", + "src": "@openzeppelin\\contracts-upgradeable\\token\\ERC721\\ERC721Upgradeable.sol:28" + }, + { + "label": "_owners", + "offset": 0, + "slot": "203", + "type": "t_mapping(t_uint256,t_address)", + "contract": "ERC721Upgradeable", + "src": "@openzeppelin\\contracts-upgradeable\\token\\ERC721\\ERC721Upgradeable.sol:31" + }, + { + "label": "_balances", + "offset": 0, + "slot": "204", + "type": "t_mapping(t_address,t_uint256)", + "contract": "ERC721Upgradeable", + "src": "@openzeppelin\\contracts-upgradeable\\token\\ERC721\\ERC721Upgradeable.sol:34" + }, + { + "label": "_tokenApprovals", + "offset": 0, + "slot": "205", + "type": "t_mapping(t_uint256,t_address)", + "contract": "ERC721Upgradeable", + "src": "@openzeppelin\\contracts-upgradeable\\token\\ERC721\\ERC721Upgradeable.sol:37" + }, + { + "label": "_operatorApprovals", + "offset": 0, + "slot": "206", + "type": "t_mapping(t_address,t_mapping(t_address,t_bool))", + "contract": "ERC721Upgradeable", + "src": "@openzeppelin\\contracts-upgradeable\\token\\ERC721\\ERC721Upgradeable.sol:40" + }, + { + "label": "__gap", + "offset": 0, + "slot": "207", + "type": "t_array(t_uint256)44_storage", + "contract": "ERC721Upgradeable", + "src": "@openzeppelin\\contracts-upgradeable\\token\\ERC721\\ERC721Upgradeable.sol:465" + }, + { + "label": "_ownedTokens", + "offset": 0, + "slot": "251", + "type": "t_mapping(t_address,t_mapping(t_uint256,t_uint256))", + "contract": "ERC721EnumerableUpgradeable", + "src": "@openzeppelin\\contracts-upgradeable\\token\\ERC721\\extensions\\ERC721EnumerableUpgradeable.sol:22" + }, + { + "label": "_ownedTokensIndex", + "offset": 0, + "slot": "252", + "type": "t_mapping(t_uint256,t_uint256)", + "contract": "ERC721EnumerableUpgradeable", + "src": "@openzeppelin\\contracts-upgradeable\\token\\ERC721\\extensions\\ERC721EnumerableUpgradeable.sol:25" + }, + { + "label": "_allTokens", + "offset": 0, + "slot": "253", + "type": "t_array(t_uint256)dyn_storage", + "contract": "ERC721EnumerableUpgradeable", + "src": "@openzeppelin\\contracts-upgradeable\\token\\ERC721\\extensions\\ERC721EnumerableUpgradeable.sol:28" + }, + { + "label": "_allTokensIndex", + "offset": 0, + "slot": "254", + "type": "t_mapping(t_uint256,t_uint256)", + "contract": "ERC721EnumerableUpgradeable", + "src": "@openzeppelin\\contracts-upgradeable\\token\\ERC721\\extensions\\ERC721EnumerableUpgradeable.sol:31" + }, + { + "label": "__gap", + "offset": 0, + "slot": "255", + "type": "t_array(t_uint256)46_storage", + "contract": "ERC721EnumerableUpgradeable", + "src": "@openzeppelin\\contracts-upgradeable\\token\\ERC721\\extensions\\ERC721EnumerableUpgradeable.sol:175" + }, + { + "label": "__gap", + "offset": 0, + "slot": "301", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC1967UpgradeUpgradeable", + "src": "@openzeppelin\\contracts-upgradeable\\proxy\\ERC1967\\ERC1967UpgradeUpgradeable.sol:211" + }, + { + "label": "__gap", + "offset": 0, + "slot": "351", + "type": "t_array(t_uint256)50_storage", + "contract": "UUPSUpgradeable", + "src": "@openzeppelin\\contracts-upgradeable\\proxy\\utils\\UUPSUpgradeable.sol:107" + }, + { + "label": "cascadingAccess", + "offset": 0, + "slot": "401", + "type": "t_array(t_bytes32)dyn_storage", + "contract": "MagicatsHandlerUpgradeable", + "src": "contracts\\MagicatsHandlerUpgradeable.sol:27" + }, + { + "label": "upgradeProposalTime", + "offset": 0, + "slot": "402", + "type": "t_uint256", + "contract": "MagicatsHandlerUpgradeable", + "src": "contracts\\MagicatsHandlerUpgradeable.sol:29" + }, + { + "label": "vault", + "offset": 0, + "slot": "403", + "type": "t_address", + "contract": "MagicatsHandlerUpgradeable", + "src": "contracts\\MagicatsHandlerUpgradeable.sol:37" + }, + { + "label": "totalMp", + "offset": 0, + "slot": "404", + "type": "t_uint256", + "contract": "MagicatsHandlerUpgradeable", + "src": "contracts\\MagicatsHandlerUpgradeable.sol:63" + }, + { + "label": "harvests", + "offset": 0, + "slot": "405", + "type": "t_array(t_struct(Harvest)10031_storage)dyn_storage", + "contract": "MagicatsHandlerUpgradeable", + "src": "contracts\\MagicatsHandlerUpgradeable.sol:66" + }, + { + "label": "idToMagicat", + "offset": 0, + "slot": "406", + "type": "t_mapping(t_uint256,t_struct(Magicat)10024_storage)", + "contract": "MagicatsHandlerUpgradeable", + "src": "contracts\\MagicatsHandlerUpgradeable.sol:72" + }, + { + "label": "magicatIdToStakedPid", + "offset": 0, + "slot": "407", + "type": "t_mapping(t_uint256,t_uint256)", + "contract": "MagicatsHandlerUpgradeable", + "src": "contracts\\MagicatsHandlerUpgradeable.sol:73" + }, + { + "label": "strategy", + "offset": 0, + "slot": "408", + "type": "t_address", + "contract": "MagicatsHandlerUpgradeable", + "src": "contracts\\MagicatsHandlerUpgradeable.sol:76" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]", + "numberOfBytes": "32" + }, + "t_array(t_struct(Harvest)10031_storage)dyn_storage": { + "label": "struct MagicatsHandlerUpgradeable.Harvest[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)44_storage": { + "label": "uint256[44]", + "numberOfBytes": "1408" + }, + "t_array(t_uint256)46_storage": { + "label": "uint256[46]", + "numberOfBytes": "1472" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_array(t_uint256)dyn_storage": { + "label": "uint256[]", + "numberOfBytes": "32" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_mapping(t_address,t_bool))": { + "label": "mapping(address => mapping(address => bool))", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_mapping(t_uint256,t_uint256))": { + "label": "mapping(address => mapping(uint256 => uint256))", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(AddressSet)4161_storage)": { + "label": "mapping(bytes32 => struct EnumerableSetUpgradeable.AddressSet)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(RoleData)179_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_address)": { + "label": "mapping(uint256 => address)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_struct(Magicat)10024_storage)": { + "label": "mapping(uint256 => struct MagicatsHandlerUpgradeable.Magicat)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_uint256)": { + "label": "mapping(uint256 => uint256)", + "numberOfBytes": "32" + }, + "t_string_storage": { + "label": "string", + "numberOfBytes": "32" + }, + "t_struct(AddressSet)4161_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)3860_storage", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Harvest)10031_storage": { + "label": "struct MagicatsHandlerUpgradeable.Harvest", + "members": [ + { + "label": "amount", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "totalManaPoints", + "type": "t_uint256", + "offset": 0, + "slot": "1" + }, + { + "label": "timestamp", + "type": "t_uint256", + "offset": 0, + "slot": "2" + } + ], + "numberOfBytes": "96" + }, + "t_struct(Magicat)10024_storage": { + "label": "struct MagicatsHandlerUpgradeable.Magicat", + "members": [ + { + "label": "magicatId", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "manapoints", + "type": "t_uint256", + "offset": 0, + "slot": "1" + }, + { + "label": "lastHarvestClaimed", + "type": "t_uint256", + "offset": 0, + "slot": "2" + } + ], + "numberOfBytes": "96" + }, + "t_struct(RoleData)179_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "members", + "type": "t_mapping(t_address,t_bool)", + "offset": 0, + "slot": "0" + }, + { + "label": "adminRole", + "type": "t_bytes32", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Set)3860_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage", + "offset": 0, + "slot": "0" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + } + } + }, + "1f42569fb3a7336f1aba8e6da6da7d3fdb8d682f2a0b64773e1c95176038d6b9": { + "address": "0xe0253D7CC1f723Cf6D94EE48625637E20a1CB1D8", + "txHash": "0x04fac7a2b856b620f2aa077daceb9caad3636e35ddb988be26cf6e5cf4def61b", + "layout": { + "solcVersion": "0.8.9", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" + }, + { + "label": "__gap", + "offset": 0, + "slot": "51", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC165Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol:41" + }, + { + "label": "_roles", + "offset": 0, + "slot": "101", + "type": "t_mapping(t_bytes32,t_struct(RoleData)179_storage)", + "contract": "AccessControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:61" + }, + { + "label": "__gap", + "offset": 0, + "slot": "102", + "type": "t_array(t_uint256)49_storage", + "contract": "AccessControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:259" + }, + { + "label": "_roleMembers", + "offset": 0, + "slot": "151", + "type": "t_mapping(t_bytes32,t_struct(AddressSet)4161_storage)", + "contract": "AccessControlEnumerableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "152", + "type": "t_array(t_uint256)49_storage", + "contract": "AccessControlEnumerableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol:76" + }, + { + "label": "_name", + "offset": 0, + "slot": "201", + "type": "t_string_storage", + "contract": "ERC721Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol:25" + }, + { + "label": "_symbol", + "offset": 0, + "slot": "202", + "type": "t_string_storage", + "contract": "ERC721Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol:28" + }, + { + "label": "_owners", + "offset": 0, + "slot": "203", + "type": "t_mapping(t_uint256,t_address)", + "contract": "ERC721Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol:31" + }, + { + "label": "_balances", + "offset": 0, + "slot": "204", + "type": "t_mapping(t_address,t_uint256)", + "contract": "ERC721Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol:34" + }, + { + "label": "_tokenApprovals", + "offset": 0, + "slot": "205", + "type": "t_mapping(t_uint256,t_address)", + "contract": "ERC721Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol:37" + }, + { + "label": "_operatorApprovals", + "offset": 0, + "slot": "206", + "type": "t_mapping(t_address,t_mapping(t_address,t_bool))", + "contract": "ERC721Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol:40" + }, + { + "label": "__gap", + "offset": 0, + "slot": "207", + "type": "t_array(t_uint256)44_storage", + "contract": "ERC721Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol:465" + }, + { + "label": "_ownedTokens", + "offset": 0, + "slot": "251", + "type": "t_mapping(t_address,t_mapping(t_uint256,t_uint256))", + "contract": "ERC721EnumerableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol:22" + }, + { + "label": "_ownedTokensIndex", + "offset": 0, + "slot": "252", + "type": "t_mapping(t_uint256,t_uint256)", + "contract": "ERC721EnumerableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol:25" + }, + { + "label": "_allTokens", + "offset": 0, + "slot": "253", + "type": "t_array(t_uint256)dyn_storage", + "contract": "ERC721EnumerableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol:28" + }, + { + "label": "_allTokensIndex", + "offset": 0, + "slot": "254", + "type": "t_mapping(t_uint256,t_uint256)", + "contract": "ERC721EnumerableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol:31" + }, + { + "label": "__gap", + "offset": 0, + "slot": "255", + "type": "t_array(t_uint256)46_storage", + "contract": "ERC721EnumerableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol:175" + }, + { + "label": "__gap", + "offset": 0, + "slot": "301", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC1967UpgradeUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol:211" + }, + { + "label": "__gap", + "offset": 0, + "slot": "351", + "type": "t_array(t_uint256)50_storage", + "contract": "UUPSUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol:107" + }, + { + "label": "cascadingAccess", + "offset": 0, + "slot": "401", + "type": "t_array(t_bytes32)dyn_storage", + "contract": "MagicatsHandlerUpgradeable", + "src": "contracts/MagicatsHandlerUpgradeable.sol:27" + }, + { + "label": "upgradeProposalTime", + "offset": 0, + "slot": "402", + "type": "t_uint256", + "contract": "MagicatsHandlerUpgradeable", + "src": "contracts/MagicatsHandlerUpgradeable.sol:29" + }, + { + "label": "vault", + "offset": 0, + "slot": "403", + "type": "t_address", + "contract": "MagicatsHandlerUpgradeable", + "src": "contracts/MagicatsHandlerUpgradeable.sol:37" + }, + { + "label": "totalMp", + "offset": 0, + "slot": "404", + "type": "t_uint256", + "contract": "MagicatsHandlerUpgradeable", + "src": "contracts/MagicatsHandlerUpgradeable.sol:63" + }, + { + "label": "harvests", + "offset": 0, + "slot": "405", + "type": "t_array(t_struct(Harvest)10031_storage)dyn_storage", + "contract": "MagicatsHandlerUpgradeable", + "src": "contracts/MagicatsHandlerUpgradeable.sol:66" + }, + { + "label": "idToMagicat", + "offset": 0, + "slot": "406", + "type": "t_mapping(t_uint256,t_struct(Magicat)10024_storage)", + "contract": "MagicatsHandlerUpgradeable", + "src": "contracts/MagicatsHandlerUpgradeable.sol:72" + }, + { + "label": "magicatIdToStakedPid", + "offset": 0, + "slot": "407", + "type": "t_mapping(t_uint256,t_uint256)", + "contract": "MagicatsHandlerUpgradeable", + "src": "contracts/MagicatsHandlerUpgradeable.sol:73" + }, + { + "label": "strategy", + "offset": 0, + "slot": "408", + "type": "t_address", + "contract": "MagicatsHandlerUpgradeable", + "src": "contracts/MagicatsHandlerUpgradeable.sol:76" + }, + { + "label": "lastAllocationTimestamp", + "offset": 0, + "slot": "409", + "type": "t_uint256", + "contract": "MagicatsHandlerUpgradeable", + "src": "contracts/MagicatsHandlerUpgradeable.sol:83" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]", + "numberOfBytes": "32" + }, + "t_array(t_struct(Harvest)10031_storage)dyn_storage": { + "label": "struct MagicatsHandlerUpgradeable.Harvest[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)44_storage": { + "label": "uint256[44]", + "numberOfBytes": "1408" + }, + "t_array(t_uint256)46_storage": { + "label": "uint256[46]", + "numberOfBytes": "1472" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_array(t_uint256)dyn_storage": { + "label": "uint256[]", + "numberOfBytes": "32" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_mapping(t_address,t_bool))": { + "label": "mapping(address => mapping(address => bool))", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_mapping(t_uint256,t_uint256))": { + "label": "mapping(address => mapping(uint256 => uint256))", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(AddressSet)4161_storage)": { + "label": "mapping(bytes32 => struct EnumerableSetUpgradeable.AddressSet)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(RoleData)179_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_address)": { + "label": "mapping(uint256 => address)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_struct(Magicat)10024_storage)": { + "label": "mapping(uint256 => struct MagicatsHandlerUpgradeable.Magicat)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_uint256)": { + "label": "mapping(uint256 => uint256)", + "numberOfBytes": "32" + }, + "t_string_storage": { + "label": "string", + "numberOfBytes": "32" + }, + "t_struct(AddressSet)4161_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)3860_storage", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Harvest)10031_storage": { + "label": "struct MagicatsHandlerUpgradeable.Harvest", + "members": [ + { + "label": "amount", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "totalManaPoints", + "type": "t_uint256", + "offset": 0, + "slot": "1" + }, + { + "label": "timestamp", + "type": "t_uint256", + "offset": 0, + "slot": "2" + } + ], + "numberOfBytes": "96" + }, + "t_struct(Magicat)10024_storage": { + "label": "struct MagicatsHandlerUpgradeable.Magicat", + "members": [ + { + "label": "magicatId", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "manapoints", + "type": "t_uint256", + "offset": 0, + "slot": "1" + }, + { + "label": "lastHarvestClaimed", + "type": "t_uint256", + "offset": 0, + "slot": "2" + } + ], + "numberOfBytes": "96" + }, + "t_struct(RoleData)179_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "members", + "type": "t_mapping(t_address,t_bool)", + "offset": 0, + "slot": "0" + }, + { + "label": "adminRole", + "type": "t_bytes32", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Set)3860_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage", + "offset": 0, + "slot": "0" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + } + } } } } diff --git a/scripts/gated-harvests-upgrade.js b/scripts/gated-harvests-upgrade.js index 6e89220..227f1e2 100644 --- a/scripts/gated-harvests-upgrade.js +++ b/scripts/gated-harvests-upgrade.js @@ -20,7 +20,18 @@ const hre = require('hardhat'); -async function main() {} +const upgradeProxy = async () => { + const stratFactory = await ethers.getContractFactory('ReaperAutoCompoundXBoov2'); + await hre.upgrades.upgradeProxy(tusdProxy, stratFactory, { ...options, timeout: 0 }); + console.log('upgradeProxy'); +}; +async function main() { + const proxyADDR = '0xD3BF27E1606dF8Ac80f4Fd3c4faF47b8c31a1021'; + const MagicatsHandlerUpgradeable = await ethers.getContractFactory('MagicatsHandlerUpgradeable'); + await hre.upgrades.upgradeProxy(proxyADDR, MagicatsHandlerUpgradeable); + console.log("updated"); + +} main() .then(() => process.exit(0)) From d0ec89426cd84b9a5fac7a840f6e17d38a2dde7c Mon Sep 17 00:00:00 2001 From: RadioJulius <9730701+RadioJulius@users.noreply.github.com> Date: Mon, 20 Feb 2023 11:52:28 -0800 Subject: [PATCH 12/14] untested partial claiming logic --- contracts/MagicatsHandlerUpgradeable.sol | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/contracts/MagicatsHandlerUpgradeable.sol b/contracts/MagicatsHandlerUpgradeable.sol index dff593b..887822e 100644 --- a/contracts/MagicatsHandlerUpgradeable.sol +++ b/contracts/MagicatsHandlerUpgradeable.sol @@ -82,6 +82,9 @@ contract MagicatsHandlerUpgradeable is */ uint256 public lastAllocationTimestamp; + // catID -> savedPendingRewards + mapping(uint256 => uint256) public savedRewards; + /// @custom:oz-upgrades-unsafe-allow constructor constructor() initializer {} @@ -262,6 +265,8 @@ contract MagicatsHandlerUpgradeable is */ function _claimRewards(uint256 _id) internal { uint256 owed = getMagicatReward(_id); + owed += savedRewards[_id]; + delete savedRewards[_id]; idToMagicat[_id].lastHarvestClaimed = harvests.length; IERC20(vault).transfer(msg.sender, owed); } @@ -429,4 +434,22 @@ contract MagicatsHandlerUpgradeable is require(upgradeProposalTime + UPGRADE_TIMELOCK < block.timestamp); clearUpgradeCooldown(); } + + function partialClaimRewards(uint256 magicatID, uint256 _harvests) public { + uint256 totalHarvests = harvests.length; + Magicat memory cat = idToMagicat[magicatID]; + uint256 magicatShare; + uint256 unclaimedReward; + uint256 lastHarvestClaimed = cat.lastHarvestClaimed; + uint256 lastToClaim = lastHarvestClaimed + _harvests; + if(lastToClaim > totalHarvests){ + lastToClaim = totalHarvests; + } + for(uint256 i = lastHarvestClaimed; i < lastToClaim; i = _uncheckedInc(i)){ + magicatShare = (harvests[i].amount * cat.manapoints) / harvests[i].totalManaPoints; + unclaimedReward += magicatShare; + } + savedRewards[magicatID] += unclaimedReward; + cat.lastHarvestClaimed = lastToClaim; + } } From 8132b83133bdccd678ddcc2fef56a1b9b0976510 Mon Sep 17 00:00:00 2001 From: RadioJulius <9730701+RadioJulius@users.noreply.github.com> Date: Mon, 20 Feb 2023 18:26:31 -0800 Subject: [PATCH 13/14] bugfixes + test update --- contracts/MagicatsHandlerUpgradeable.sol | 5 ++- test/magicat-upgrade-test.t.sol | 42 +++++++++++++++--------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/contracts/MagicatsHandlerUpgradeable.sol b/contracts/MagicatsHandlerUpgradeable.sol index 887822e..3ab8c85 100644 --- a/contracts/MagicatsHandlerUpgradeable.sol +++ b/contracts/MagicatsHandlerUpgradeable.sol @@ -245,7 +245,7 @@ contract MagicatsHandlerUpgradeable is unclaimedReward += magicatShare; } - return unclaimedReward; + return unclaimedReward + savedRewards[id]; } /*** @@ -265,7 +265,6 @@ contract MagicatsHandlerUpgradeable is */ function _claimRewards(uint256 _id) internal { uint256 owed = getMagicatReward(_id); - owed += savedRewards[_id]; delete savedRewards[_id]; idToMagicat[_id].lastHarvestClaimed = harvests.length; IERC20(vault).transfer(msg.sender, owed); @@ -437,7 +436,7 @@ contract MagicatsHandlerUpgradeable is function partialClaimRewards(uint256 magicatID, uint256 _harvests) public { uint256 totalHarvests = harvests.length; - Magicat memory cat = idToMagicat[magicatID]; + Magicat storage cat = idToMagicat[magicatID]; uint256 magicatShare; uint256 unclaimedReward; uint256 lastHarvestClaimed = cat.lastHarvestClaimed; diff --git a/test/magicat-upgrade-test.t.sol b/test/magicat-upgrade-test.t.sol index f9d39f0..7e71cbe 100644 --- a/test/magicat-upgrade-test.t.sol +++ b/test/magicat-upgrade-test.t.sol @@ -6,6 +6,7 @@ import "../contracts/MagicatsHandlerUpgradeable.sol"; import "./abstracts/XbooConstants.t.sol"; contract ProductionMagicatUpgrade is XbooConstants { + uint256 public fantomFork; address payable public existingHandler = payable(0xD3BF27E1606dF8Ac80f4Fd3c4faF47b8c31a1021); MagicatsHandlerUpgradeable public currentHandler; MagicatsHandlerUpgradeable public newHandler; @@ -14,6 +15,8 @@ contract ProductionMagicatUpgrade is XbooConstants { uint256 public oldSupply; function setUp() public { + + fantomFork = vm.createSelectFork('https://rpc.ankr.com/fantom', 56188257); currentHandler = MagicatsHandlerUpgradeable(existingHandler); vm.label(existingHandler, "handler"); oldMP = currentHandler.totalMp(); @@ -25,8 +28,6 @@ contract ProductionMagicatUpgrade is XbooConstants { } function testHandlerProductionUpgradeValidity() public { - vm.expectRevert(); - uint256 lastAllocation = currentHandler.lastAllocationTimestamp(); vm.startPrank(0xe1610bB38Ce95254dD77cbC82F9c1148569B560e); currentHandler.upgradeTo(address(newHandler)); @@ -35,19 +36,28 @@ contract ProductionMagicatUpgrade is XbooConstants { uint256 newSupply = currentHandler.totalSupply(); console.log("newMP: ", newMP); console.log("newSupply: ", newSupply); - assertGe(newMP, oldMP); - assertGe(newSupply, oldSupply); - - lastAllocation = currentHandler.lastAllocationTimestamp(); - console.log("lastAllocation: ", lastAllocation); - assertEq(lastAllocation, 0); - - uint256[] memory args = new uint256[](0); - uint256 poolID = 0; - bool allocationCompleted = true; - currentHandler.updateStakedMagicats(poolID, args, args, allocationCompleted); - lastAllocation = currentHandler.lastAllocationTimestamp(); - console.log("lastAllocation: ", lastAllocation); - assertEq(lastAllocation, block.timestamp); + //assertGe(newMP, oldMP); + //assertGe(newSupply, oldSupply); + + uint256 id = 1786; + uint256 _harvests = 260; + + uint256 beforePartialPending = currentHandler.getMagicatReward(id); + + currentHandler.partialClaimRewards(id, _harvests); + + uint256 afterPartialPending = currentHandler.getMagicatReward(id); + + assertEq(beforePartialPending, afterPartialPending); + + uint256[] memory ids = new uint256[](1); + ids[0] = id; + vm.stopPrank(); + vm.prank(0x60BC5E0440C867eEb4CbcE84bB1123fad2b262B1); + currentHandler.claimRewards(ids); + + assertEq(currentHandler.getMagicatReward(id), 0); + + } } From fbc059078f369c9cf7bbd8fd61feaf5a5d161ab8 Mon Sep 17 00:00:00 2001 From: RadioJulius <9730701+RadioJulius@users.noreply.github.com> Date: Tue, 7 Mar 2023 03:05:30 -0800 Subject: [PATCH 14/14] handler code to slow down harvest cadence --- contracts/MagicatsHandlerUpgradeable.sol | 36 +++++++++++++++++++----- test/magicat-upgrade-test.t.sol | 33 ++++++++++++++++------ 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/contracts/MagicatsHandlerUpgradeable.sol b/contracts/MagicatsHandlerUpgradeable.sol index 3ab8c85..464789e 100644 --- a/contracts/MagicatsHandlerUpgradeable.sol +++ b/contracts/MagicatsHandlerUpgradeable.sol @@ -85,6 +85,10 @@ contract MagicatsHandlerUpgradeable is // catID -> savedPendingRewards mapping(uint256 => uint256) public savedRewards; + bool shouldHarvest; + + uint256 timeBetweenProcesses; + /// @custom:oz-upgrades-unsafe-allow constructor constructor() initializer {} @@ -134,6 +138,7 @@ contract MagicatsHandlerUpgradeable is _safeMint(msg.sender, magicatsIds[i]); } _updateStakedMagicats(IStrategy(strategy).currentPoolId(), magicatsIds, new uint256[](0)); + shouldHarvest = true; } /*** @@ -167,6 +172,7 @@ contract MagicatsHandlerUpgradeable is IERC721Upgradeable(MAGICATS).transferFrom(currentOwner, msg.sender, magicatsIds[i]); } + shouldHarvest = true; } /*** @@ -221,13 +227,23 @@ contract MagicatsHandlerUpgradeable is * writes to harvest log and allows for reward claims */ function processRewards() external { - Harvest memory latestHarvest; - uint256 beforeAmount = IERC20Upgradeable(vault).balanceOf(address(this)); - _redepositGains(); - latestHarvest.amount = IERC20Upgradeable(vault).balanceOf(address(this)) - beforeAmount; - latestHarvest.totalManaPoints = totalMp; - latestHarvest.timestamp = block.timestamp; - harvests.push(latestHarvest); + + if(_passedTimeToProcess() || shouldHarvest) + { + Harvest memory latestHarvest; + uint256 beforeAmount = IERC20Upgradeable(vault).balanceOf(address(this)); + _redepositGains(); + latestHarvest.amount = IERC20Upgradeable(vault).balanceOf(address(this)) - beforeAmount; + latestHarvest.totalManaPoints = totalMp; + latestHarvest.timestamp = block.timestamp; + harvests.push(latestHarvest); + shouldHarvest = false; + } + } + + function _passedTimeToProcess() internal view returns (bool) { + uint256 harvestsLength = harvests.length; + return block.timestamp - harvests[harvestsLength - 1].timestamp >= timeBetweenProcesses; } /*** @@ -451,4 +467,10 @@ contract MagicatsHandlerUpgradeable is savedRewards[magicatID] += unclaimedReward; cat.lastHarvestClaimed = lastToClaim; } + + function setTimeBetweenProcesses(uint256 newTime) external { + _atLeastRole(STRATEGIST); + require(newTime <= 1 days, "time between harvests is too long"); + timeBetweenProcesses = newTime; + } } diff --git a/test/magicat-upgrade-test.t.sol b/test/magicat-upgrade-test.t.sol index 7e71cbe..c8bbf2f 100644 --- a/test/magicat-upgrade-test.t.sol +++ b/test/magicat-upgrade-test.t.sol @@ -8,6 +8,8 @@ import "./abstracts/XbooConstants.t.sol"; contract ProductionMagicatUpgrade is XbooConstants { uint256 public fantomFork; address payable public existingHandler = payable(0xD3BF27E1606dF8Ac80f4Fd3c4faF47b8c31a1021); + address payable public existingXboo = payable(0x98c3956B492dc4984D1a9752792120ebfCdC9c9B); + ReaperAutoCompoundXBoov2 public xbooStrat; MagicatsHandlerUpgradeable public currentHandler; MagicatsHandlerUpgradeable public newHandler; @@ -16,8 +18,9 @@ contract ProductionMagicatUpgrade is XbooConstants { function setUp() public { - fantomFork = vm.createSelectFork('https://rpc.ankr.com/fantom', 56188257); + fantomFork = vm.createSelectFork('https://rpc.ankr.com/fantom',56970000 ); currentHandler = MagicatsHandlerUpgradeable(existingHandler); + xbooStrat = ReaperAutoCompoundXBoov2(existingXboo); vm.label(existingHandler, "handler"); oldMP = currentHandler.totalMp(); oldSupply = currentHandler.totalSupply(); @@ -31,7 +34,7 @@ contract ProductionMagicatUpgrade is XbooConstants { vm.startPrank(0xe1610bB38Ce95254dD77cbC82F9c1148569B560e); currentHandler.upgradeTo(address(newHandler)); - + currentHandler.setTimeBetweenProcesses(4 hours); uint256 newMP = currentHandler.totalMp(); uint256 newSupply = currentHandler.totalSupply(); console.log("newMP: ", newMP); @@ -39,12 +42,14 @@ contract ProductionMagicatUpgrade is XbooConstants { //assertGe(newMP, oldMP); //assertGe(newSupply, oldSupply); - uint256 id = 1786; - uint256 _harvests = 260; - + uint256 id = 523; + uint256 _harvests = 500; + xbooStrat.harvest(); uint256 beforePartialPending = currentHandler.getMagicatReward(id); + for(uint i = 0; i < 1; i ++){ + currentHandler.partialClaimRewards(id, _harvests); + } - currentHandler.partialClaimRewards(id, _harvests); uint256 afterPartialPending = currentHandler.getMagicatReward(id); @@ -53,11 +58,23 @@ contract ProductionMagicatUpgrade is XbooConstants { uint256[] memory ids = new uint256[](1); ids[0] = id; vm.stopPrank(); - vm.prank(0x60BC5E0440C867eEb4CbcE84bB1123fad2b262B1); + vm.prank(0xAf24327EA0e357956e0B0Ec137A62AEB5bF6D0a5); currentHandler.claimRewards(ids); assertEq(currentHandler.getMagicatReward(id), 0); - + + vm.warp(block.timestamp + 1 hours); + vm.roll(block.number + 1 hours); + + vm.stopPrank(); + vm.startPrank(0xe1610bB38Ce95254dD77cbC82F9c1148569B560e); + xbooStrat.harvest(); + vm.warp(block.timestamp + 4 hours); + vm.roll(block.number + 4 hours); + xbooStrat.harvest(); + + + } }