diff --git a/.gitmodules b/.gitmodules index 58a2bef..85885d3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,9 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std -[submodule "lib/superform-core"] - path = lib/superform-core - url = https://github.com/superform-xyz/superform-core [submodule "lib/tokenized-strategy"] path = lib/tokenized-strategy url = https://github.com/yearn/tokenized-strategy +[submodule "lib/superform-core"] + path = lib/superform-core + url = https://github.com/superform-xyz/superform-core diff --git a/Makefile b/Makefile index dff0f5a..4b87651 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,7 @@ ifeq ($(ENVIRONMENT), local) export FANTOM_RPC_URL := $(shell op read op://5ylebqljbh3x6zomdxi3qd7tsa/FANTOM_RPC_URL/credential) export LINEA_RPC_URL := $(shell op read op://5ylebqljbh3x6zomdxi3qd7tsa/LINEA_RPC_URL/credential) export BLAST_RPC_URL := $(shell op read op://5ylebqljbh3x6zomdxi3qd7tsa/BLAST_RPC_URL/credential) + export BARTIO_RPC_URL := $(shell op read op://5ylebqljbh3x6zomdxi3qd7tsa/BARTIO_RPC_URL/credential) export ETHEREUM_RPC_URL_QN := $(shell op read op://5ylebqljbh3x6zomdxi3qd7tsa/ETHEREUM_RPC_URL/credential) export BSC_RPC_URL_QN := $(shell op read op://5ylebqljbh3x6zomdxi3qd7tsa/BSC_RPC_URL/credential) export AVALANCHE_RPC_URL_QN := $(shell op read op://5ylebqljbh3x6zomdxi3qd7tsa/AVALANCHE_RPC_URL/credential) @@ -27,6 +28,7 @@ ifeq ($(ENVIRONMENT), local) export BSC_TESTNET_RPC_URL_QN := $(shell op read op://5ylebqljbh3x6zomdxi3qd7tsa/BSC_TESTNET_RPC_URL/credential) export LINEA_RPC_URL_QN := $(shell op read op://5ylebqljbh3x6zomdxi3qd7tsa/LINEA_RPC_URL/credential) export BLAST_RPC_URL_QN := $(shell op read op://5ylebqljbh3x6zomdxi3qd7tsa/BLAST_RPC_URL/credential) + export BARTIO_RPC_URL_QN := $(shell op read op://5ylebqljbh3x6zomdxi3qd7tsa/BARTIO_RPC_URL/credential) endif @@ -37,7 +39,7 @@ update:; forge update # Build & test build :; FOUNDRY_PROFILE=production forge build build-sizes :; FOUNDRY_PROFILE=production forge build --sizes -test-vvv :; forge test --match-test test_superVault_rebalance_notWhitelisted --evm-version cancun -vvv +test-vvv :; forge test --match-test test_superVaultConstructorReverts --evm-version cancun -vvv ftest :; forge test --evm-version cancun coverage :; forge coverage --evm-version cancun --report lcov clean :; forge clean diff --git a/lcov.info b/lcov.info index b231cb6..e5c356e 100644 --- a/lcov.info +++ b/lcov.info @@ -1,316 +1,407 @@ TN: SF:script/forge-scripts/Deploy.SuperVault.s.sol -FN:10,MainnetDeploySuperVault.deploySuperVault +FN:11,MainnetDeploySuperVault.deploySuperVault FNDA:0,MainnetDeploySuperVault.deploySuperVault -DA:11,0 -DA:13,0 -DA:15,0 -BRDA:15,0,0,- -BRDA:15,0,1,- +DA:12,0 +DA:14,0 DA:16,0 -BRDA:16,1,0,- -BRDA:16,1,1,- +BRDA:16,0,0,- +BRDA:16,0,1,- DA:17,0 -DA:19,0 -DA:22,0 -BRDA:22,2,0,- -BRDA:22,2,1,- +BRDA:17,1,0,- +BRDA:17,1,1,- +DA:18,0 +DA:20,0 DA:23,0 -DA:25,0 -DA:29,0 -BRDA:29,3,0,- -BRDA:29,3,1,- -DA:35,0 +BRDA:23,2,0,- +BRDA:23,2,1,- +DA:24,0 +DA:26,0 +DA:30,0 +BRDA:30,3,0,- +BRDA:30,3,1,- DA:36,0 -DA:38,0 +DA:37,0 DA:39,0 -DA:41,0 -DA:51,0 -DA:52,0 -DA:54,0 +DA:40,0 +DA:43,0 +DA:44,0 +DA:46,0 DA:56,0 -BRDA:56,4,0,- -BRDA:56,4,1,- DA:58,0 -DA:59,0 -DA:60,0 -DA:61,0 FNF:1 FNH:0 -LF:23 +LF:19 +LH:0 +BRF:8 +BRH:0 +end_of_record +TN: +SF:script/forge-scripts/Deploy.SuperVaultFactory.s.sol +FN:9,MainnetDeploySuperVaultFactory.deploySuperVaultFactory +FNDA:0,MainnetDeploySuperVaultFactory.deploySuperVaultFactory +DA:10,0 +DA:12,0 +DA:14,0 +BRDA:14,0,0,- +BRDA:14,0,1,- +DA:15,0 +BRDA:15,1,0,- +BRDA:15,1,1,- +DA:16,0 +DA:18,0 +DA:21,0 +BRDA:21,2,0,- +BRDA:21,2,1,- +DA:22,0 +DA:24,0 +DA:28,0 +BRDA:28,3,0,- +BRDA:28,3,1,- +DA:31,0 +DA:33,0 +FNF:1 +FNH:0 +LF:12 LH:0 -BRF:10 +BRF:8 BRH:0 end_of_record TN: SF:src/SuperVault.sol -FN:54,SuperVault.onlySuperVaultsStrategist -FNDA:211,SuperVault.onlySuperVaultsStrategist -DA:55,211 -BRDA:55,0,0,- -DA:56,0 -FN:71,SuperVault. -FNDA:42,SuperVault. -DA:81,42 -DA:82,42 -BRDA:82,1,0,1 -DA:83,1 -DA:86,41 -BRDA:86,2,0,1 -DA:87,1 -DA:90,40 -BRDA:90,3,0,1 -DA:91,1 -DA:94,39 -BRDA:94,4,0,- -DA:95,0 -DA:98,39 -DA:100,39 -DA:102,39 -DA:104,39 -DA:105,39 -DA:107,39 -DA:109,113 -DA:111,113 -BRDA:111,5,0,1 -DA:112,1 -DA:115,112 -BRDA:115,6,0,1 -DA:116,1 -DA:119,111 -DA:122,37 -BRDA:122,7,0,1 -DA:124,36 -DA:125,36 -DA:126,36 -DA:127,36 -FN:136,SuperVault.setDepositLimit +FN:64,SuperVault.onlySuperVaultsStrategist +FNDA:212,SuperVault.onlySuperVaultsStrategist +DA:65,212 +BRDA:65,0,0,- +DA:66,0 +FN:81,SuperVault. +FNDA:52,SuperVault. +DA:92,52 +DA:94,52 +DA:95,52 +DA:97,52 +DA:99,52 +BRDA:99,1,0,- +DA:100,0 +DA:103,52 +DA:104,52 +DA:106,52 +DA:108,152 +DA:110,152 +BRDA:110,2,0,1 +DA:111,1 +DA:114,151 +BRDA:114,3,0,1 +DA:115,1 +DA:118,150 +DA:121,50 +BRDA:121,4,0,1 +DA:123,49 +DA:125,49 +DA:126,49 +DA:127,49 +DA:128,49 +FN:137,SuperVault.setDepositLimit FNDA:2,SuperVault.setDepositLimit -DA:137,1 -DA:139,1 -FN:143,SuperVault.rebalance -FNDA:211,SuperVault.rebalance -DA:144,211 -DA:145,211 -DA:146,211 -DA:148,211 -BRDA:148,8,0,1 -DA:149,210 -BRDA:149,9,0,- -DA:152,210 -BRDA:152,10,0,1 -DA:153,1 -DA:158,209 -DA:159,209 -DA:161,209 -DA:162,213 -DA:163,220 -BRDA:163,11,0,212 -DA:164,212 -DA:165,212 -DA:170,209 -BRDA:170,12,0,1 -DA:171,1 -DA:174,208 -DA:175,4 -BRDA:175,13,0,1 -DA:176,1 -DA:180,207 -DA:181,207 -BRDA:181,14,0,- -DA:182,0 -DA:187,207 -DA:195,206 -DA:196,206 -DA:199,206 -DA:201,206 -DA:206,206 -FN:217,SuperVault.getSuperVaultData -FNDA:206,SuperVault.getSuperVaultData -DA:222,206 -FN:226,SuperVault.onERC1155Received +DA:138,1 +DA:140,1 +FN:145,SuperVault.setStrategist +FNDA:1,SuperVault.setStrategist +DA:146,1 +DA:148,1 +FN:152,SuperVault.rebalance +FNDA:212,SuperVault.rebalance +DA:153,212 +DA:154,212 +DA:155,212 +DA:157,212 +BRDA:157,5,0,1 +DA:158,211 +BRDA:158,6,0,- +DA:161,211 +BRDA:161,7,0,1 +DA:162,1 +DA:167,210 +DA:168,210 +DA:170,210 +DA:171,215 +DA:172,223 +BRDA:172,8,0,214 +DA:173,214 +DA:174,214 +DA:179,210 +BRDA:179,9,0,1 +DA:180,1 +DA:183,209 +DA:184,5 +BRDA:184,10,0,1 +DA:185,1 +DA:189,208 +DA:190,208 +BRDA:190,11,0,1 +DA:191,1 +DA:196,207 +DA:204,206 +DA:205,206 +DA:208,206 +DA:210,206 +DA:215,206 +FN:219,SuperVault.forwardDustToPaymaster +FNDA:3,SuperVault.forwardDustToPaymaster +DA:220,2 +DA:221,2 +DA:223,2 +DA:224,2 +BRDA:224,12,0,1 +DA:225,1 +DA:226,1 +FN:235,SuperVault.getSuperVaultData +FNDA:208,SuperVault.getSuperVaultData +DA:240,208 +FN:244,SuperVault.onERC1155Received FNDA:1,SuperVault.onERC1155Received -DA:238,1 -FN:242,SuperVault.onERC1155BatchReceived -FNDA:413,SuperVault.onERC1155BatchReceived -DA:254,413 -FN:260,SuperVault.supportsInterface -FNDA:413,SuperVault.supportsInterface -DA:261,413 -FN:265,SuperVault.availableDepositLimit -FNDA:208,SuperVault.availableDepositLimit -DA:266,208 -DA:267,208 -DA:268,208 -FN:277,SuperVault._deployFunds -FNDA:207,SuperVault._deployFunds -DA:278,207 -DA:279,207 -DA:281,207 -DA:285,207 -DA:288,207 -DA:290,207 -DA:292,207 -BRDA:292,15,0,197 -FN:297,SuperVault._freeFunds +DA:256,1 +FN:260,SuperVault.onERC1155BatchReceived +FNDA:441,SuperVault.onERC1155BatchReceived +DA:272,441 +FN:278,SuperVault.supportsInterface +FNDA:441,SuperVault.supportsInterface +DA:279,441 +FN:283,SuperVault.availableDepositLimit +FNDA:236,SuperVault.availableDepositLimit +DA:284,236 +DA:285,236 +DA:286,236 +FN:295,SuperVault._deployFunds +FNDA:235,SuperVault._deployFunds +DA:296,235 +DA:297,235 +DA:299,235 +DA:303,235 +DA:306,235 +DA:308,235 +DA:310,235 +BRDA:310,13,0,197 +FN:315,SuperVault._freeFunds FNDA:2,SuperVault._freeFunds -DA:298,2 -DA:299,2 -DA:300,2 -DA:304,2 -DA:309,2 -DA:311,2 -FN:316,SuperVault._harvestAndReport +DA:316,2 +DA:317,2 +DA:318,2 +DA:322,2 +DA:327,2 +DA:329,2 +FN:334,SuperVault._harvestAndReport FNDA:1,SuperVault._harvestAndReport -DA:319,1 -DA:320,1 -DA:321,1 -DA:322,1 -DA:323,3 -DA:324,3 -DA:325,3 -DA:328,1 -FN:339,SuperVault._prepareMultiVaultData -FNDA:209,SuperVault._prepareMultiVaultData -DA:347,209 -DA:349,209 -DA:350,209 -DA:351,209 -DA:352,209 -DA:353,209 -DA:354,209 -DA:355,209 -DA:356,209 -DA:357,209 -DA:360,209 -DA:361,209 -DA:363,209 -DA:364,627 -DA:366,627 -DA:367,627 -DA:369,621 -BRDA:369,16,0,621 -BRDA:369,16,1,6 -DA:371,621 -DA:372,621 -DA:374,6 -DA:376,6 -DA:380,627 -FN:385,SuperVault._getAddress -FNDA:1496,SuperVault._getAddress -DA:386,1496 -FN:395,SuperVault._prepareRebalanceArgs +DA:335,1 +DA:336,1 +DA:337,1 +DA:339,1 +DA:341,1 +DA:342,3 +DA:345,3 +DA:346,3 +DA:349,1 +FN:360,SuperVault._prepareMultiVaultData +FNDA:237,SuperVault._prepareMultiVaultData +DA:368,237 +DA:370,237 +DA:371,237 +DA:372,237 +DA:373,237 +DA:374,237 +DA:375,237 +DA:376,237 +DA:377,237 +DA:378,237 +DA:381,237 +DA:382,237 +DA:384,237 +DA:385,711 +DA:387,711 +DA:388,711 +DA:390,705 +BRDA:390,14,0,705 +BRDA:390,14,1,6 +DA:392,705 +DA:393,705 +DA:396,6 +DA:398,6 +DA:401,711 +FN:406,SuperVault._getAddress +FNDA:1273,SuperVault._getAddress +DA:407,1273 +FN:416,SuperVault._prepareRebalanceArgs FNDA:207,SuperVault._prepareRebalanceArgs -DA:406,207 -DA:407,207 -DA:408,207 -DA:409,207 -DA:410,207 -DA:412,207 -DA:413,207 -DA:416,207 -DA:419,207 -DA:420,207 -DA:422,206 -DA:427,206 -DA:428,206 -FN:438,SuperVault._prepareSingleDirectMultiVaultStateReq +DA:427,207 +DA:428,207 +DA:429,207 +DA:430,207 +DA:431,207 +DA:433,207 +DA:434,207 +DA:437,207 +DA:440,207 +DA:441,207 +DA:443,206 +DA:448,206 +DA:449,206 +FN:459,SuperVault._prepareSingleDirectMultiVaultStateReq FNDA:413,SuperVault._prepareSingleDirectMultiVaultStateReq -DA:448,413 -DA:449,413 -DA:450,413 -DA:452,413 -DA:454,413 -DA:455,413 -DA:456,413 -DA:457,413 -DA:459,413 -DA:460,621 -DA:462,210 -BRDA:462,17,0,210 -BRDA:462,17,1,411 -DA:463,210 -DA:465,411 -DA:468,621 -DA:470,621 -DA:471,621 -DA:472,621 +DA:469,413 +DA:470,413 +DA:471,413 +DA:473,413 DA:475,413 DA:476,413 +DA:477,413 DA:478,413 DA:480,413 -DA:482,413 -FN:489,SuperVault._calculateAmounts +DA:481,621 +DA:483,210 +BRDA:483,15,0,210 +BRDA:483,15,1,411 +DA:484,210 +DA:486,411 +DA:489,621 +DA:491,621 +DA:492,621 +DA:493,621 +DA:496,413 +DA:497,413 +DA:499,413 +DA:501,413 +DA:503,413 +FN:510,SuperVault._calculateAmounts FNDA:206,SuperVault._calculateAmounts -DA:497,206 -DA:498,206 -DA:499,411 -FN:508,SuperVault._filterNonZeroWeights +DA:518,206 +DA:519,206 +DA:520,411 +FN:529,SuperVault._filterNonZeroWeights FNDA:207,SuperVault._filterNonZeroWeights -DA:516,207 -DA:517,207 -DA:518,414 -BRDA:518,18,0,412 -DA:519,412 -DA:523,207 -DA:524,207 -DA:526,207 -DA:527,207 -DA:528,207 -DA:529,414 -BRDA:529,19,0,412 -DA:530,412 -DA:531,412 -DA:532,412 -DA:533,412 -DA:536,207 -BRDA:536,20,0,1 -FN:542,SuperVault._updateSVData +DA:537,207 +DA:538,207 +DA:539,207 +DA:540,414 +BRDA:540,16,0,412 +DA:541,412 +DA:545,207 +DA:546,207 +DA:548,207 +DA:549,207 +DA:550,207 +DA:551,414 +BRDA:551,17,0,412 +DA:552,412 +DA:553,412 +DA:554,412 +DA:555,412 +DA:558,207 +BRDA:558,18,0,1 +FN:564,SuperVault._updateSVData FNDA:208,SuperVault._updateSVData -DA:543,208 -DA:545,208 -DA:546,208 -BRDA:546,21,0,- -DA:548,208 -DA:551,208 -DA:552,208 -DA:553,208 -DA:554,208 -DA:557,208 -DA:558,415 -BRDA:558,22,0,1 -DA:559,1 -DA:562,414 -DA:564,414 -BRDA:564,23,0,1 -DA:565,1 -DA:568,413 -DA:569,413 -DA:571,413 -DA:572,413 -DA:576,206 -DA:577,206 -DA:578,207 -DA:579,207 -DA:583,206 -DA:586,206 -DA:587,206 -DA:588,206 -DA:590,206 -FNF:19 -FNH:19 -LF:189 -LH:186 -BRF:26 -BRH:21 +DA:565,208 +DA:567,208 +DA:568,208 +BRDA:568,19,0,- +DA:570,208 +DA:573,208 +DA:574,208 +DA:575,208 +DA:576,208 +DA:579,208 +DA:580,415 +BRDA:580,20,0,1 +DA:581,1 +DA:584,414 +DA:586,414 +BRDA:586,21,0,1 +DA:587,1 +DA:590,413 +DA:591,413 +DA:593,413 +DA:594,413 +DA:598,206 +DA:599,206 +DA:600,207 +DA:601,207 +DA:605,206 +DA:608,206 +DA:609,206 +DA:610,206 +DA:612,206 +FNF:21 +FNH:21 +LF:194 +LH:192 +BRF:24 +BRH:20 +end_of_record +TN: +SF:src/SuperVaultFactory.sol +FN:48,SuperVaultFactory.onlyManagement +FNDA:12,SuperVaultFactory.onlyManagement +DA:49,12 +BRDA:49,0,0,- +DA:50,0 +FN:60,SuperVaultFactory. +FNDA:7,SuperVaultFactory. +DA:63,7 +BRDA:63,1,0,- +DA:64,0 +DA:66,7 +DA:67,7 +FN:76,SuperVaultFactory.createSuperVault +FNDA:12,SuperVaultFactory.createSuperVault +DA:84,12 +BRDA:84,2,0,2 +DA:85,2 +DA:88,10 +DA:90,10 +BRDA:90,3,0,1 +DA:91,1 +DA:94,9 +BRDA:94,4,0,1 +DA:95,1 +DA:98,8 +DA:106,8 +DA:118,7 +DA:119,7 +DA:120,7 +BRDA:120,5,0,- +DA:121,0 +DA:124,7 +DA:125,7 +DA:127,7 +FN:135,SuperVaultFactory.isSuperVault +FNDA:1,SuperVaultFactory.isSuperVault +DA:136,1 +FN:139,SuperVaultFactory.getSuperVaultData +FNDA:1,SuperVaultFactory.getSuperVaultData +DA:140,1 +FN:144,SuperVaultFactory.getSuperformIds +FNDA:1,SuperVaultFactory.getSuperformIds +DA:145,1 +DA:146,1 +FN:150,SuperVaultFactory.getSuperVaultCount +FNDA:2,SuperVaultFactory.getSuperVaultCount +DA:151,2 +FN:159,SuperVaultFactory._getAddress +FNDA:0,SuperVaultFactory._getAddress +DA:160,0 +FNF:8 +FNH:7 +LF:28 +LH:24 +BRF:6 +BRH:3 end_of_record TN: SF:test/SuperVault.t.sol -FN:12,SuperVaultHarness. -FNDA:18,SuperVaultHarness. -FN:24,SuperVaultHarness.updateSVData +FN:15,SuperVaultHarness. +FNDA:21,SuperVaultHarness. +FN:27,SuperVaultHarness.updateSVData FNDA:2,SuperVaultHarness.updateSVData -DA:25,2 +DA:28,2 FNF:2 FNH:2 LF:1 diff --git a/lib/superform-core b/lib/superform-core index 52cbfbb..a4f65be 160000 --- a/lib/superform-core +++ b/lib/superform-core @@ -1 +1 @@ -Subproject commit 52cbfbb86561a072ebf3334828fc40912e53b315 +Subproject commit a4f65beecbee272b9aa3a9f4078369286e97c5a2 diff --git a/script/forge-scripts/Deploy.SuperVault.s.sol b/script/forge-scripts/Deploy.SuperVault.s.sol index 4c1ed68..d7e4d21 100644 --- a/script/forge-scripts/Deploy.SuperVault.s.sol +++ b/script/forge-scripts/Deploy.SuperVault.s.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.23; import { Script } from "forge-std/Script.sol"; -import { ISuperRegistry } from "superform-core/src/interfaces/ISuperRegistry.sol"; -import { SuperRBAC } from "superform-core/src/settings/SuperRBAC.sol"; import { SuperVault } from "src/SuperVault.sol"; +import { ITokenizedStrategy } from "tokenized-strategy/interfaces/ITokenizedStrategy.sol"; +import { ISuperRegistry } from "superform-core/src/interfaces/ISuperRegistry.sol"; contract MainnetDeploySuperVault is Script { function deploySuperVault(bool isStaging, uint256 chainId) external { @@ -37,29 +37,30 @@ contract MainnetDeploySuperVault is Script { uint256[] memory startingWeights = new uint256[](1); startingWeights[0] = 10_000; - - address VAULT_MANAGER = address(0xDEAD); - new SuperVault( - superRegistry, - 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913, // USDC - VAULT_MANAGER, - "USDCSuperVaultMoonwellFlagship", - type(uint256).max, - superformIds, - startingWeights - ); /// @dev TODO set later the correct address, as this is currently rewards admin address REWARDS_ADMIN = isStaging ? 0x1F05a8Ff6d895Ba04C84c5031c5d63FA1afCDA6F : 0xf82F3D7Df94FC2994315c32322DA6238cA2A2f7f; + address VAULT_MANAGER = address(0xDEAD); - address superRBAC = ISuperRegistry(superRegistry).getAddress(keccak256("SUPER_RBAC")); + address superVault = address( + new SuperVault( + superRegistry, + 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913, // USDC + REWARDS_ADMIN, + VAULT_MANAGER, + "USDCSuperVaultMoonwellFlagship", + type(uint256).max, + superformIds, + startingWeights + ) + ); - assert(superRBAC != address(0)); + (bool success,) = address(superVault).call(abi.encodeWithSelector(ITokenizedStrategy.acceptManagement.selector)); + if (!success) { + revert("Accept management failed"); + } - SuperRBAC superRBACC = SuperRBAC(superRBAC); - superRBACC.setRoleAdmin(keccak256("SUPER_VAULTS_STRATEGIST"), superRBACC.PROTOCOL_ADMIN_ROLE()); - superRBACC.grantRole(keccak256("SUPER_VAULTS_STRATEGIST"), REWARDS_ADMIN); vm.stopBroadcast(); } } diff --git a/script/forge-scripts/Deploy.SuperVaultFactory.s.sol b/script/forge-scripts/Deploy.SuperVaultFactory.s.sol new file mode 100644 index 0000000..f3eddda --- /dev/null +++ b/script/forge-scripts/Deploy.SuperVaultFactory.s.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.23; + +import { Script } from "forge-std/Script.sol"; +import { ISuperRegistry } from "superform-core/src/interfaces/ISuperRegistry.sol"; +import { SuperVaultFactory } from "src/SuperVaultFactory.sol"; + +contract MainnetDeploySuperVaultFactory is Script { + function deploySuperVaultFactory(bool isStaging, uint256 chainId) external { + vm.startBroadcast(); + + address superRegistry; + + if (isStaging) { + if (chainId == 250) { + superRegistry = 0x7B8d68f90dAaC67C577936d3Ce451801864EF189; + } else { + superRegistry = 0xB2C097ac459aFAc892ae5b35f6bd6a9Dd3071F47; + } + } else { + if (chainId == 250) { + superRegistry = 0x7feB31d18E43E2faeC718EEd2D7f34402c3e27b4; + } else { + superRegistry = 0x17A332dC7B40aE701485023b219E9D6f493a2514; + } + } + + assert(superRegistry != address(0)); + + /// @notice Deploy SuperVaultFactory + new SuperVaultFactory(superRegistry); + + vm.stopBroadcast(); + } +} diff --git a/src/ISuperVault.sol b/src/ISuperVault.sol index 4d949d4..ceb7a53 100644 --- a/src/ISuperVault.sol +++ b/src/ISuperVault.sol @@ -41,6 +41,9 @@ interface ISuperVault is IERC1155Receiver { /// @notice Error thrown when no superforms are provided in constructor error ZERO_SUPERFORMS(); + /// @notice Error thrown when the address is zero + error ZERO_ADDRESS(); + /// @notice Error thrown when duplicate superform IDs to rebalance from are provided error DUPLICATE_SUPERFORM_IDS_REBALANCE_FROM(); @@ -56,12 +59,6 @@ interface ISuperVault is IERC1155Receiver { /// @notice Error thrown when the caller is not the Super Vaults strategist error NOT_SUPER_VAULTS_STRATEGIST(); - /// @notice Error thrown when a zero address is provided - error ZERO_ADDRESS(); - - /// @notice Error thrown when trying to forward shares to the paymaster - error CANNOT_FORWARD_SHARES(); - /// @notice Error thrown when the amounts to rebalance from array is empty error EMPTY_AMOUNTS_REBALANCE_FROM(); @@ -83,6 +80,9 @@ interface ISuperVault is IERC1155Receiver { /// @notice Error thrown when a superform ID is not found in the final superform IDs error REBALANCE_FROM_ID_NOT_FOUND_IN_FINAL_IDS(); + /// @notice Error thrown when the caller is not the pending management + error NOT_PENDING_MANAGEMENT(); + /// @notice Error thrown when the caller is not the vault manager error NOT_VAULT_MANAGER(); @@ -106,6 +106,10 @@ interface ISuperVault is IERC1155Receiver { /// @param dust The amount of dust forwarded event DustForwardedToPaymaster(uint256 dust); + /// @notice Emitted when the strategist is set + /// @param strategist The new strategist + event StrategistSet(address strategist); + /// @notice Emitted when a superform is whitelisted /// @param superformId The superform ID that was whitelisted /// @param isWhitelisted Whether the superform was whitelisted diff --git a/src/ISuperVaultFactory.sol b/src/ISuperVaultFactory.sol new file mode 100644 index 0000000..999461c --- /dev/null +++ b/src/ISuperVaultFactory.sol @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import { ISuperVault } from "./ISuperVault.sol"; + +/// @title ISuperVaultFactory Interface +/// @notice Interface for the SuperVaultFactory contract +/// @author SuperForm Labs +interface ISuperVaultFactory { + ////////////////////////////////////////////////////////////// + // ERRORS // + ////////////////////////////////////////////////////////////// + + /// @notice Error thrown when the caller is not the management role + error NOT_MANAGEMENT(); + + /// @notice Error thrown when the pending management is not set + error FAILED_TO_SET_PENDING_MANAGEMENT(); + + /// @notice Error thrown when duplicate final superform IDs are provided + error DUPLICATE_FINAL_SUPERFORM_IDS(); + + /// @notice Error thrown when array lengths do not match + error ARRAY_LENGTH_MISMATCH(); + + /// @notice Error thrown when the number of superforms is zero + error ZERO_SUPERFORMS(); + + /// @notice Error thrown when the caller is not the Super Vaults strategist + error NOT_SUPER_VAULTS_STRATEGIST(); + + /// @notice Error thrown when a zero address is provided + error ZERO_ADDRESS(); + + /// @notice Error thrown when the final superform IDs array is empty + error EMPTY_FINAL_SUPERFORM_IDS(); + + /// @notice Error thrown when the block chain ID is out of bounds + error BLOCK_CHAIN_ID_OUT_OF_BOUNDS(); + + ////////////////////////////////////////////////////////////// + // EVENTS // + ////////////////////////////////////////////////////////////// + + /// @notice Emitted when a new SuperVault is created + /// @param superVault Address of the SuperVault + event SuperVaultCreated(address indexed superVault); + + /// @notice Emitted when the strategist for a SuperVault is updated + /// @param superVault Address of the SuperVault + /// @param strategist Address of the strategist + event VaultStrategistUpdated(address indexed superVault, address indexed strategist); + + /// @dev emitted when a new SuperRegistry is set + /// @param superRegistry is the address of the super registry + event SuperRegistrySet(address indexed superRegistry); + + ////////////////////////////////////////////////////////////// + // EXTERNAL VIEW FUNCTIONS // + ////////////////////////////////////////////////////////////// + + /// @notice Returns whether a SuperVault exists + /// @param superVault_ Address of the SuperVault + /// @return Whether the SuperVault exists + function isSuperVault(address superVault_) external view returns (bool); + + /// @notice Returns the data for a SuperVault + /// @param superVault_ Address of the SuperVault + /// @return numberOfSuperforms The number of Superforms + /// @return superformIds Array of Superform IDs + /// @return weights Array of weights for each Superform + function getSuperVaultData(address superVault_) + external + view + returns (uint256 numberOfSuperforms, uint256[] memory superformIds, uint256[] memory weights); + + /// @notice Returns the Superform IDs for a SuperVault + /// @param superVault_ Address of the SuperVault + /// @return Array of Superform IDs + function getSuperformIds(address superVault_) external view returns (uint256[] memory); + + /// @notice Returns all SuperVaults + /// @return Array of SuperVault addresses + //function getSuperVaults() external view returns (address[] memory); + + /// @notice Returns the number of SuperVaults + /// @return Number of SuperVaults + function getSuperVaultCount() external view returns (uint256); + + ////////////////////////////////////////////////////////////// + // EXTERNAL WRITE FUNCTIONS // + ////////////////////////////////////////////////////////////// + + /// @notice Creates a new SuperVault + /// @dev Sets pending management to deployer, deployer will have to accept management in SuperVault + /// @param asset_ Address of the asset token + /// @param strategist_ Address of the strategist + /// @param vaultManager_ Address of the vault manager + /// @param name_ Name of the strategy + /// @param depositLimit_ Maximum deposit limit + /// @param superformIds_ Array of Superform IDs + /// @param startingWeights_ Array of starting weights for each Superform + function createSuperVault( + address asset_, + address strategist_, + address vaultManager_, + string memory name_, + uint256 depositLimit_, + uint256[] memory superformIds_, + uint256[] memory startingWeights_ + ) + external + returns (address); +} diff --git a/src/SuperVault.sol b/src/SuperVault.sol index 0a85927..5ac4045 100644 --- a/src/SuperVault.sol +++ b/src/SuperVault.sol @@ -17,6 +17,7 @@ import { ISuperformRouterPlus } from "superform-core/src/interfaces/ISuperformRo import { ISuperRegistry } from "superform-core/src/interfaces/ISuperRegistry.sol"; import { ISuperformFactory } from "superform-core/src/interfaces/ISuperformFactory.sol"; import { BaseStrategy } from "tokenized-strategy/BaseStrategy.sol"; +import { ITokenizedStrategy } from "tokenized-strategy/interfaces/ITokenizedStrategy.sol"; import { ISuperVault, IERC1155Receiver } from "./ISuperVault.sol"; /// @title SuperVault @@ -36,9 +37,15 @@ contract SuperVault is BaseStrategy, ISuperVault { /// @notice The chain ID of the network this contract is deployed on uint64 public immutable CHAIN_ID; + /// @notice The address of the SuperVault Strategist + address public strategist; + /// @notice The address of the SuperRegistry contract ISuperRegistry public immutable superRegistry; + /// @notice The address of the SuperformFactory contract + ISuperformFactory public immutable superformFactory; + /// @notice The total weight used for calculating proportions (10000 = 100%) uint256 public constant TOTAL_WEIGHT = 10_000; @@ -60,7 +67,7 @@ contract SuperVault is BaseStrategy, ISuperVault { /// @notice Ensures that only the Super Vaults Strategist can call the function modifier onlySuperVaultsStrategist() { - if (_getAddress(keccak256("SUPER_VAULTS_STRATEGIST")) != msg.sender) { + if (strategist != msg.sender) { revert NOT_SUPER_VAULTS_STRATEGIST(); } _; @@ -87,6 +94,7 @@ contract SuperVault is BaseStrategy, ISuperVault { constructor( address superRegistry_, address asset_, + address strategist_, address vaultManager_, string memory name_, uint256 depositLimit_, @@ -96,6 +104,7 @@ contract SuperVault is BaseStrategy, ISuperVault { BaseStrategy(asset_, name_) { uint256 numberOfSuperforms = superformIds_.length; + if (numberOfSuperforms == 0) { revert ZERO_SUPERFORMS(); } @@ -104,11 +113,7 @@ contract SuperVault is BaseStrategy, ISuperVault { revert ARRAY_LENGTH_MISMATCH(); } - if (superRegistry_ == address(0)) { - revert ZERO_ADDRESS(); - } - - if (vaultManager_ == address(0)) { + if (superRegistry_ == address(0) || strategist_ == address(0) || vaultManager_ == address(0)) { revert ZERO_ADDRESS(); } @@ -119,8 +124,13 @@ contract SuperVault is BaseStrategy, ISuperVault { CHAIN_ID = uint64(block.chainid); superRegistry = ISuperRegistry(superRegistry_); + superformFactory = ISuperformFactory(superRegistry.getAddress(keccak256("SUPERFORM_FACTORY"))); - ISuperformFactory factory = ISuperformFactory(_getAddress(keccak256("SUPERFORM_FACTORY"))); + CHAIN_ID = uint64(block.chainid); + + if (CHAIN_ID > type(uint64).max) { + revert BLOCK_CHAIN_ID_OUT_OF_BOUNDS(); + } uint256 totalWeight; address superform; @@ -129,7 +139,7 @@ contract SuperVault is BaseStrategy, ISuperVault { /// @dev this superVault only supports superforms that have the same asset as the vault (superform,,) = superformIds_[i].getSuperform(); - if (!factory.isSuperform(superformIds_[i])) { + if (!superformFactory.isSuperform(superformIds_[i])) { revert SUPERFORM_DOES_NOT_EXIST(superformIds_[i]); } @@ -145,6 +155,7 @@ contract SuperVault is BaseStrategy, ISuperVault { if (totalWeight != TOTAL_WEIGHT) revert INVALID_WEIGHTS(); + strategist = strategist_; SV.vaultManager = vaultManager_; SV.numberOfSuperforms = numberOfSuperforms; SV.superformIds = superformIds_; @@ -163,6 +174,14 @@ contract SuperVault is BaseStrategy, ISuperVault { emit DepositLimitSet(depositLimit_); } + /// @notice Sets the strategist for the vault + /// @param strategist_ The new strategist + function setStrategist(address strategist_) external onlyManagement { + strategist = strategist_; + + emit StrategistSet(strategist_); + } + /// @inheritdoc ISuperVault function rebalance(RebalanceArgs calldata rebalanceArgs) external payable override onlySuperVaultsStrategist { uint256 lenRebalanceFrom = rebalanceArgs.superformIdsRebalanceFrom.length; diff --git a/src/SuperVaultFactory.sol b/src/SuperVaultFactory.sol new file mode 100644 index 0000000..e7111ce --- /dev/null +++ b/src/SuperVaultFactory.sol @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import { SuperVault } from "./SuperVault.sol"; +import { ISuperVault } from "./ISuperVault.sol"; +import { ISuperVaultFactory } from "./ISuperVaultFactory.sol"; +import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; +import { DataLib } from "superform-core/src/libraries/DataLib.sol"; +import { IBaseForm } from "superform-core/src/interfaces/IBaseForm.sol"; +import { ITokenizedStrategy } from "tokenized-strategy/interfaces/ITokenizedStrategy.sol"; +import { ISuperRegistry } from "superform-core/src/interfaces/ISuperRegistry.sol"; +import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"; + +/// @title SuperVaultFactory +/// @notice Factory for creating SuperVaults +/// @dev Implements the ISuperVaultFactory interface +/// @author SuperForm Labs +contract SuperVaultFactory is ISuperVaultFactory, AccessControl { + using Math for uint256; + using DataLib for uint256; + + ////////////////////////////////////////////////////////////// + // STATE VARIABLES // + ////////////////////////////////////////////////////////////// + + /// @notice The SuperRegistry contract + ISuperRegistry public immutable superRegistry; + + /// @notice The TokenizedStrategy contract + ITokenizedStrategy public immutable tokenizedStrategy; + + /// @notice The number of SuperVaults created + uint256 public superVaultCount; + + /// @notice The total weight used for calculating proportions (10000 = 100%) + uint256 public constant TOTAL_WEIGHT = 10_000; + + /// @notice The array of registered SuperVaults + address[] public superVaults; + + /// @notice The mapping of registered SuperVaults + mapping(address superVault => bool registered) public registeredSuperVaults; + + ////////////////////////////////////////////////////////////// + // MODIFIERS // + ////////////////////////////////////////////////////////////// + + modifier onlyManagement() { + if (!hasRole(keccak256("MANAGEMENT_ROLE"), msg.sender)) { + revert NOT_MANAGEMENT(); + } + _; + } + + ////////////////////////////////////////////////////////////// + // CONSTRUCTOR // + ////////////////////////////////////////////////////////////// + + /// @param superRegistry_ Address of the SuperRegistry + constructor(address superRegistry_) { + if (superRegistry_ == address(0)) { + revert ZERO_ADDRESS(); + } + superRegistry = ISuperRegistry(superRegistry_); + _grantRole(keccak256("MANAGEMENT_ROLE"), msg.sender); + } + + ////////////////////////////////////////////////////////////// + // EXTERNAL WRITE FUNCTIONS // + ////////////////////////////////////////////////////////////// + + /// @inheritdoc ISuperVaultFactory + /// @dev after deploying superVault, deployer needs to accept management + function createSuperVault( + address asset_, + address strategist_, + address vaultManager_, + string memory name_, + uint256 depositLimit_, + uint256[] memory superformIds_, + uint256[] memory startingWeights_ + ) + external + onlyManagement + returns (address) + { + if (asset_ == address(0) || strategist_ == address(0)) { + revert ZERO_ADDRESS(); + } + + uint256 numberOfSuperforms = superformIds_.length; + + if (numberOfSuperforms == 0) { + revert ZERO_SUPERFORMS(); + } + + if (numberOfSuperforms != startingWeights_.length) { + revert ARRAY_LENGTH_MISMATCH(); + } + + bytes32 salt = keccak256(abi.encodePacked(asset_, name_, superformIds_, startingWeights_, "SuperVault")); + + address superVault = address( + new SuperVault{ salt: salt }( + address(superRegistry), + asset_, + strategist_, + vaultManager_, + name_, + depositLimit_, + superformIds_, + startingWeights_ + ) + ); + + /// @dev set pending management to deployer + /// @dev deployer will have to accept management in SuperVault + (bool success,) = address(superVault).call( + abi.encodeWithSelector(ITokenizedStrategy.setPendingManagement.selector, msg.sender) + ); + if (!success) { + revert FAILED_TO_SET_PENDING_MANAGEMENT(); + } + + superVaults.push(superVault); + registeredSuperVaults[superVault] = true; + + return superVault; + } + + ////////////////////////////////////////////////////////////// + // EXTERNAL VIEW FUNCTIONS // + ////////////////////////////////////////////////////////////// + + /// @inheritdoc ISuperVaultFactory + function isSuperVault(address superVault_) external view returns (bool) { + return registeredSuperVaults[superVault_]; + } + + function getSuperVaultData(address superVault_) + external + view + returns (uint256 numberOfSuperforms, uint256[] memory superformIds, uint256[] memory weights) + { + return ISuperVault(superVault_).getSuperVaultData(); + } + + /// @inheritdoc ISuperVaultFactory + function getSuperformIds(address superVault_) external view returns (uint256[] memory) { + (, uint256[] memory superformIds,) = ISuperVault(superVault_).getSuperVaultData(); + return superformIds; + } + + /// @inheritdoc ISuperVaultFactory + function getSuperVaultCount() external view returns (uint256) { + return superVaults.length; + } + + ////////////////////////////////////////////////////////////// + // INTERNAL FUNCTIONS // + ////////////////////////////////////////////////////////////// + + /// @dev returns the address for id_ from super registry + function _getAddress(bytes32 id_) internal view returns (address) { + return superRegistry.getAddress(id_); + } +} diff --git a/test/SuperVault.t.sol b/test/SuperVault.t.sol index 23486dc..3c5594f 100644 --- a/test/SuperVault.t.sol +++ b/test/SuperVault.t.sol @@ -15,13 +15,23 @@ contract SuperVaultHarness is SuperVault { constructor( address superRegistry_, address asset_, + address strategist_, address vaultManager_, string memory name_, uint256 depositLimit_, uint256[] memory superformIds_, uint256[] memory startingWeights_ ) - SuperVault(superRegistry_, asset_, vaultManager_, name_, depositLimit_, superformIds_, startingWeights_) + SuperVault( + superRegistry_, + asset_, + strategist_, + vaultManager_, + name_, + depositLimit_, + superformIds_, + startingWeights_ + ) { } function updateSVData(address superPositions, uint256[] memory finalSuperformIds) public { @@ -118,9 +128,10 @@ contract SuperVaultTest is ProtocolActions { // Deploy SuperVault superVault = new SuperVault( - getContract(SOURCE_CHAIN, "SuperRegistry"), + getContract(ETH, "SuperRegistry"), getContract(ETH, "USDC"), deployer, + deployer, "USDCSuperVaultMorphoEulerAave", type(uint256).max, underlyingSuperformIds, @@ -151,6 +162,7 @@ contract SuperVaultTest is ProtocolActions { getContract(SOURCE_CHAIN, "SuperRegistry"), getContract(ETH, "USDC"), deployer, + deployer, "USDCSuperVaultMorphoEulerAave", type(uint256).max, underlyingSuperformIds, @@ -269,6 +281,7 @@ contract SuperVaultTest is ProtocolActions { getContract(ETH, "SuperRegistry"), getContract(ETH, "USDC"), deployer, + deployer, "TestSuperVault", type(uint256).max, superformIds, @@ -298,16 +311,20 @@ contract SuperVaultTest is ProtocolActions { // Test 1: ZERO_SUPERFORMS revert vm.expectRevert(abi.encodeWithSignature("ZERO_SUPERFORMS()")); - new SuperVault(superRegistry, asset, deployer, name, depositLimit, superformIds, startingWeights); + new SuperVault(superRegistry, asset, deployer, deployer, name, depositLimit, superformIds, startingWeights); superformIds = underlyingSuperformIds; // Test 2.1: ZERO_ADDRESS revert vm.expectRevert(abi.encodeWithSignature("ZERO_ADDRESS()")); - new SuperVault(address(0), asset, deployer, name, depositLimit, superformIds, startingWeights); + new SuperVault(address(0), asset, deployer, deployer, name, depositLimit, superformIds, startingWeights); // Test 2.2: ZERO_ADDRESS revert vm.expectRevert(abi.encodeWithSignature("ZERO_ADDRESS()")); - new SuperVault(superRegistry, asset, address(0), name, depositLimit, superformIds, startingWeights); + new SuperVault(superRegistry, asset, address(0), deployer, name, depositLimit, superformIds, startingWeights); + + // Test 2.3: ZERO_ADDRESS revert + vm.expectRevert(abi.encodeWithSignature("ZERO_ADDRESS()")); + new SuperVault(superRegistry, asset, deployer, address(0), name, depositLimit, superformIds, startingWeights); // Test 3: ARRAY_LENGTH_MISMATCH revert uint256[] memory mismatchedWeights = new uint256[](2); @@ -315,22 +332,29 @@ contract SuperVaultTest is ProtocolActions { mismatchedWeights[1] = 5000; vm.expectRevert(abi.encodeWithSignature("ARRAY_LENGTH_MISMATCH()")); - new SuperVault(superRegistry, asset, deployer, name, depositLimit, superformIds, mismatchedWeights); + new SuperVault(superRegistry, asset, deployer, deployer, name, depositLimit, superformIds, mismatchedWeights); // Test 4: SUPERFORM_DOES_NOT_SUPPORT_ASSET revert vm.expectRevert(abi.encodeWithSignature("SUPERFORM_DOES_NOT_SUPPORT_ASSET()")); new SuperVault( - superRegistry, getContract(ETH, "DAI"), deployer, name, depositLimit, superformIds, startingWeights + superRegistry, + getContract(ETH, "DAI"), + deployer, + deployer, + name, + depositLimit, + superformIds, + startingWeights ); - // Test 5: INVALID_WEIGHTS revert + // Test INVALID_WEIGHTS revert uint256[] memory invalidWeights = new uint256[](3); invalidWeights[0] = 3000; invalidWeights[1] = 3000; invalidWeights[2] = 3000; vm.expectRevert(abi.encodeWithSignature("INVALID_WEIGHTS()")); - new SuperVault(superRegistry, asset, deployer, name, depositLimit, superformIds, invalidWeights); + new SuperVault(superRegistry, asset, deployer, deployer, name, depositLimit, superformIds, invalidWeights); } function test_superVault_assertSuperPositions_splitAccordingToWeights() public { diff --git a/test/SuperVaultFactory.t.sol b/test/SuperVaultFactory.t.sol new file mode 100644 index 0000000..781050c --- /dev/null +++ b/test/SuperVaultFactory.t.sol @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.23; + +import { SuperVault } from "../src/SuperVault.sol"; +import { SuperVaultFactory } from "../src/SuperVaultFactory.sol"; +import { ISuperVaultFactory } from "../src/ISuperVaultFactory.sol"; + +import "superform-core/test/utils/ProtocolActions.sol"; +import { Math } from "openzeppelin/contracts/utils/math/Math.sol"; +import { ERC20 } from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; +import { ITokenizedStrategy } from "tokenized-strategy/interfaces/ITokenizedStrategy.sol"; + +contract SuperVaultFactoryTest is ProtocolActions { + SuperVaultFactory public factory; + SuperVault public superVault; + + uint64 SOURCE_CHAIN; + + uint256[] underlyingSuperformIds; + uint256[] allSuperformIds; + uint256[] weights; + + function sortAllSuperformIds() internal { + uint256 n = allSuperformIds.length; + for (uint256 i = 0; i < n - 1; i++) { + for (uint256 j = 0; j < n - i - 1; j++) { + if (allSuperformIds[j] > allSuperformIds[j + 1]) { + // Swap + uint256 temp = allSuperformIds[j]; + allSuperformIds[j] = allSuperformIds[j + 1]; + allSuperformIds[j + 1] = temp; + } + } + } + } + + function setUp() public override { + super.setUp(); + SOURCE_CHAIN = ETH; + + // Setup + vm.selectFork(FORKS[SOURCE_CHAIN]); + vm.startPrank(deployer); + + address morphoVault = 0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458; + address aaveUsdcVault = 0x73edDFa87C71ADdC275c2b9890f5c3a8480bC9E6; + address eulerUsdcVault = 0x797DD80692c3b2dAdabCe8e30C07fDE5307D48a9; + address sandclockUSDCVault = 0x096697720056886b905D0DEB0f06AfFB8e4665E5; + + address[] memory vaultAddresses = new address[](4); + vaultAddresses[0] = morphoVault; + vaultAddresses[1] = aaveUsdcVault; + vaultAddresses[2] = eulerUsdcVault; + vaultAddresses[3] = sandclockUSDCVault; + + // Get the SuperformFactory + SuperformFactory superformFactory = SuperformFactory(getContract(SOURCE_CHAIN, "SuperformFactory")); + underlyingSuperformIds = new uint256[](vaultAddresses.length - 1); + allSuperformIds = new uint256[](vaultAddresses.length); + address superformAddress; + for (uint256 i = 0; i < vaultAddresses.length; i++) { + (allSuperformIds[i], superformAddress) = superformFactory.createSuperform(1, vaultAddresses[i]); + } + + sortAllSuperformIds(); + + for (uint256 i = 0; i < vaultAddresses.length - 1; i++) { + underlyingSuperformIds[i] = allSuperformIds[i]; + } + + weights = new uint256[](vaultAddresses.length - 1); + for (uint256 i = 0; i < vaultAddresses.length - 1; i++) { + weights[i] = uint256(10_000) / 3; + if (i == 2) { + weights[i] += 1; + } + } + + factory = new SuperVaultFactory(getContract(SOURCE_CHAIN, "SuperRegistry")); + + vm.stopPrank(); + } + + function test_createSuperVault() public { + vm.prank(deployer); + address superVaultTest = factory.createSuperVault( + getContract(ETH, "USDC"), + deployer, + deployer, + "USDCSuperVaultMorphoEulerAave", + type(uint256).max, + underlyingSuperformIds, + weights + ); + assertTrue(factory.isSuperVault(superVaultTest)); + assertEq(factory.getSuperVaultCount(), 1); + assert(superVaultTest != address(0)); + } + + function test_createSuperVault_reverts() public { + vm.startPrank(deployer); + + /// Test zero address for asset + vm.expectRevert(ISuperVaultFactory.ZERO_ADDRESS.selector); + factory.createSuperVault( + address(0), + deployer, + deployer, + "USDCSuperVaultMorphoEulerAave", + type(uint256).max, + underlyingSuperformIds, + weights + ); + + /// Test zero address for strategist + vm.expectRevert(ISuperVaultFactory.ZERO_ADDRESS.selector); + factory.createSuperVault( + getContract(ETH, "USDC"), + address(0), + deployer, + "USDCSuperVaultMorphoEulerAave", + type(uint256).max, + underlyingSuperformIds, + weights + ); + + /// Test superform ids and weights length mismatch + vm.expectRevert(ISuperVaultFactory.ARRAY_LENGTH_MISMATCH.selector); + factory.createSuperVault( + getContract(ETH, "USDC"), + deployer, + deployer, + "USDCSuperVaultMorphoEulerAave", + type(uint256).max, + underlyingSuperformIds, + new uint256[](underlyingSuperformIds.length - 1) + ); + + /// Test no superforms + vm.expectRevert(ISuperVaultFactory.ZERO_SUPERFORMS.selector); + factory.createSuperVault( + getContract(ETH, "USDC"), + deployer, + deployer, + "USDCSuperVaultMorphoEulerAave", + type(uint256).max, + new uint256[](0), + new uint256[](0) + ); + vm.stopPrank(); + } + + function test_getSuperVaultData() public { + vm.prank(deployer); + address superVaultTest = factory.createSuperVault( + getContract(ETH, "USDC"), + deployer, + deployer, + "USDCSuperVaultMorphoEulerAave", + type(uint256).max, + underlyingSuperformIds, + weights + ); + (uint256 numberOfSuperforms, uint256[] memory superformIds, uint256[] memory weightsReceived) = + factory.getSuperVaultData(address(superVaultTest)); + assertEq(numberOfSuperforms, underlyingSuperformIds.length); + assertEq(superformIds.length, underlyingSuperformIds.length); + assertEq(weightsReceived.length, underlyingSuperformIds.length); + for (uint256 i = 0; i < underlyingSuperformIds.length; i++) { + assertEq(superformIds[i], underlyingSuperformIds[i]); + assertEq(weightsReceived[i], weights[i]); + } + } + + function test_getSuperformIds() public { + vm.prank(deployer); + address superVaultTest = factory.createSuperVault( + getContract(ETH, "USDC"), + deployer, + deployer, + "USDCSuperVaultMorphoEulerAave", + type(uint256).max, + underlyingSuperformIds, + weights + ); + uint256[] memory superformIds = factory.getSuperformIds(address(superVaultTest)); + assertEq(superformIds.length, underlyingSuperformIds.length); + for (uint256 i = 0; i < underlyingSuperformIds.length; i++) { + assertEq(superformIds[i], underlyingSuperformIds[i]); + } + } + + function test_getSuperVaultCount() public { + vm.startPrank(deployer); + factory.createSuperVault( + getContract(ETH, "USDC"), + deployer, + deployer, + "USDCSuperVaultMorphoEulerAave", + type(uint256).max, + underlyingSuperformIds, + weights + ); + factory.createSuperVault( + getContract(ETH, "USDC"), + address(12_345), + address(12_345), + "TestSuperVault", + 100e18, + underlyingSuperformIds, + weights + ); + vm.stopPrank(); + assertEq(factory.getSuperVaultCount(), 2); + } + + function test_deployerIsPendingVaultManagement() public { + vm.startPrank(deployer); + address superVaultTest = factory.createSuperVault( + getContract(ETH, "USDC"), + deployer, + deployer, + "USDCSuperVaultMorphoEulerAave", + type(uint256).max, + underlyingSuperformIds, + weights + ); + address(superVaultTest).call(abi.encodeWithSelector(ITokenizedStrategy.acceptManagement.selector)); + SuperVault(superVaultTest).setStrategist(address(0xdead)); + vm.stopPrank(); + assertEq(SuperVault(superVaultTest).strategist(), address(0xdead)); + } + + function test_cannotCreateSameSuperVaultTwice() public { + vm.startPrank(deployer); + factory.createSuperVault( + getContract(ETH, "USDC"), + deployer, + deployer, + "USDCSuperVaultMorphoEulerAave", + type(uint256).max, + underlyingSuperformIds, + weights + ); + vm.expectRevert(); + factory.createSuperVault( + getContract(ETH, "USDC"), + deployer, + deployer, + "USDCSuperVaultMorphoEulerAave", + type(uint256).max, + underlyingSuperformIds, + weights + ); + vm.stopPrank(); + } +}