diff --git a/.gitmodules b/.gitmodules index 1fea4e1..7f1267f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/chimera"] path = lib/chimera url = https://github.com/Recon-Fuzz/chimera +[submodule "lib/erc7540-reusable-properties"] + path = lib/erc7540-reusable-properties + url = https://github.com/Recon-Fuzz/erc7540-reusable-properties diff --git a/echidna-property.yaml b/echidna-property.yaml deleted file mode 100644 index e11b3ba..0000000 --- a/echidna-property.yaml +++ /dev/null @@ -1,8 +0,0 @@ -testMode: "property" -prefix: "invariant_" -coverage: true -corpusDir: "echidna" -balanceAddr: 0x1043561a8829300000 -balanceContract: 0x1043561a8829300000 -filterFunctions: [] -cryticArgs: ["--foundry-compile-all"] \ No newline at end of file diff --git a/echidna-assertion.yaml b/echidna.yaml similarity index 72% rename from echidna-assertion.yaml rename to echidna.yaml index 3c1f0a5..e9f77cd 100644 --- a/echidna-assertion.yaml +++ b/echidna.yaml @@ -1,9 +1,9 @@ testMode: "assertion" -prefix: "invariant_" +prefix: "crytic_" coverage: true corpusDir: "echidna" balanceAddr: 0x1043561a8829300000 balanceContract: 0x1043561a8829300000 filterFunctions: [] -cryticArgs: ["--foundry-compile-all"] \ No newline at end of file +cryticArgs: ["--foundry-compile-all"] diff --git a/lib/chimera b/lib/chimera index 13ccf08..d5cf52b 160000 --- a/lib/chimera +++ b/lib/chimera @@ -1 +1 @@ -Subproject commit 13ccf08db630a402b5681c24397380ce2ea565f6 +Subproject commit d5cf52bc5bbf75f988f8aada23fd12d0bcf7798a diff --git a/lib/erc7540-reusable-properties b/lib/erc7540-reusable-properties new file mode 160000 index 0000000..7691dce --- /dev/null +++ b/lib/erc7540-reusable-properties @@ -0,0 +1 @@ +Subproject commit 7691dce847b4f1085517b88f3623c4d510c91708 diff --git a/medusa-aggregator.json b/medusa-aggregator.json deleted file mode 100644 index 6e87890..0000000 --- a/medusa-aggregator.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "fuzzing": { - "workers": 10, - "workerResetLimit": 50, - "timeout": 0, - "testLimit": 0, - "callSequenceLength": 100, - "corpusDirectory": "medusa-aggregator", - "coverageEnabled": true, - "deploymentOrder": [ - "CryticAggregatorTester" - ], - "targetContracts": [ - "CryticAggregatorTester" - ], - "targetContractsBalances": [ - "0x27b46536c66c8e3000000" - ], - "constructorArgs": {}, - "deployerAddress": "0x30000", - "senderAddresses": [ - "0x10000", - "0x20000", - "0x30000" - ], - "blockNumberDelayMax": 60480, - "blockTimestampDelayMax": 604800, - "blockGasLimit": 125000000, - "transactionGasLimit": 12500000, - "testing": { - "stopOnFailedTest": false, - "stopOnFailedContractMatching": false, - "stopOnNoTests": true, - "testAllContracts": false, - "traceAll": false, - "assertionTesting": { - "enabled": true, - "testViewMethods": true, - "panicCodeConfig": { - "failOnCompilerInsertedPanic": false, - "failOnAssertion": true, - "failOnArithmeticUnderflow": false, - "failOnDivideByZero": false, - "failOnEnumTypeConversionOutOfBounds": false, - "failOnIncorrectStorageAccess": false, - "failOnPopEmptyArray": false, - "failOnOutOfBoundsArrayAccess": false, - "failOnAllocateTooMuchMemory": false, - "failOnCallUninitializedVariable": false - } - }, - "propertyTesting": { - "enabled": true, - "testPrefixes": [ - "invariant_" - ] - }, - "optimizationTesting": { - "enabled": false, - "testPrefixes": [ - "optimize_" - ] - } - }, - "chainConfig": { - "codeSizeCheckDisabled": true, - "cheatCodes": { - "cheatCodesEnabled": true, - "enableFFI": false - } - } - }, - "compilation": { - "platform": "crytic-compile", - "platformConfig": { - "target": ".", - "solcVersion": "", - "exportDirectory": "", - "args": ["--foundry-compile-all"] - } - }, - "logging": { - "level": "info", - "logDirectory": "" - } -} \ No newline at end of file diff --git a/medusa-core.json b/medusa.json similarity index 94% rename from medusa-core.json rename to medusa.json index 2947c20..11c4b3b 100644 --- a/medusa-core.json +++ b/medusa.json @@ -5,7 +5,7 @@ "timeout": 0, "testLimit": 0, "callSequenceLength": 100, - "corpusDirectory": "medusa-core", + "corpusDirectory": "medusa", "coverageEnabled": true, "deploymentOrder": [ "CryticTester" @@ -17,7 +17,7 @@ "0x27b46536c66c8e3000000" ], "constructorArgs": {}, - "deployerAddress": "0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38", + "deployerAddress": "0x30000", "senderAddresses": [ "0x10000", "0x20000", @@ -52,7 +52,7 @@ "propertyTesting": { "enabled": true, "testPrefixes": [ - "invariant_" + "crytic_" ] }, "optimizationTesting": { diff --git a/script/Axelar.s.sol b/script/Axelar.s.sol deleted file mode 100644 index 8ee8e64..0000000 --- a/script/Axelar.s.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.26; - -import {AxelarAdapter} from "src/gateway/adapters/axelar/Adapter.sol"; -import {Deployer} from "script/Deployer.sol"; - -// Script to deploy Liquidity Pools with an Axelar Adapter. -contract AxelarScript is Deployer { - function setUp() public {} - - function run() public { - vm.startBroadcast(); - - adminSafe = vm.envAddress("ADMIN"); - - deploy(msg.sender); - AxelarAdapter adapter = new AxelarAdapter( - address(gateway), address(vm.envAddress("AXELAR_GATEWAY")), address(vm.envAddress("AXELAR_GAS_SERVICE")) - ); - wire(address(adapter)); - - removeDeployerAccess(address(adapter), msg.sender); - - vm.stopBroadcast(); - } -} diff --git a/script/Deployer.sol b/script/Deployer.sol deleted file mode 100644 index 6afcdee..0000000 --- a/script/Deployer.sol +++ /dev/null @@ -1,143 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.26; - -import {Root} from "src/Root.sol"; -import {Gateway} from "src/gateway/Gateway.sol"; -import {GasService} from "src/gateway/GasService.sol"; -import {InvestmentManager} from "src/InvestmentManager.sol"; -import {TrancheFactory} from "src/factories/TrancheFactory.sol"; -import {ERC7540VaultFactory} from "src/factories/ERC7540VaultFactory.sol"; -import {RestrictionManager} from "src/token/RestrictionManager.sol"; -import {TransferProxyFactory} from "src/factories/TransferProxyFactory.sol"; -import {PoolManager} from "src/PoolManager.sol"; -import {Escrow} from "src/Escrow.sol"; -import {CentrifugeRouter} from "src/CentrifugeRouter.sol"; -import {Guardian} from "src/admin/Guardian.sol"; -import {IAuth} from "src/interfaces/IAuth.sol"; -import "forge-std/Script.sol"; - -contract Deployer is Script { - uint256 internal constant delay = 48 hours; - address adminSafe; - address[] adapters; - - Root public root; - InvestmentManager public investmentManager; - PoolManager public poolManager; - Escrow public escrow; - Escrow public routerEscrow; - Guardian public guardian; - Gateway public gateway; - GasService public gasService; - CentrifugeRouter public router; - TransferProxyFactory public transferProxyFactory; - address public vaultFactory; - address public restrictionManager; - address public trancheFactory; - - function deploy(address deployer) public { - // If no salt is provided, a pseudo-random salt is generated, - // thus effectively making the deployment non-deterministic - bytes32 salt = vm.envOr( - "DEPLOYMENT_SALT", keccak256(abi.encodePacked(string(abi.encodePacked(blockhash(block.number - 1))))) - ); - - uint64 messageCost = uint64(vm.envOr("MESSAGE_COST", uint256(20000000000000000))); // in Weight - uint64 proofCost = uint64(vm.envOr("PROOF_COST", uint256(20000000000000000))); // in Weigth - uint128 gasPrice = uint128(vm.envOr("GAS_PRICE", uint256(2500000000000000000))); // Centrifuge Chain - uint256 tokenPrice = vm.envOr("TOKEN_PRICE", uint256(178947400000000)); // CFG/ETH - - escrow = new Escrow{salt: salt}(deployer); - routerEscrow = new Escrow{salt: keccak256(abi.encodePacked(salt, "escrow2"))}(deployer); - root = new Root{salt: salt}(address(escrow), delay, deployer); - vaultFactory = address(new ERC7540VaultFactory(address(root))); - restrictionManager = address(new RestrictionManager{salt: salt}(address(root), deployer)); - trancheFactory = address(new TrancheFactory{salt: salt}(address(root), deployer)); - investmentManager = new InvestmentManager(address(root), address(escrow)); - poolManager = new PoolManager(address(escrow), vaultFactory, trancheFactory); - transferProxyFactory = new TransferProxyFactory{salt: salt}(address(root), deployer); - gasService = new GasService(messageCost, proofCost, gasPrice, tokenPrice); - gateway = new Gateway(address(root), address(poolManager), address(investmentManager), address(gasService)); - router = new CentrifugeRouter(address(routerEscrow), address(gateway), address(poolManager)); - guardian = new Guardian(adminSafe, address(root), address(gateway)); - - _endorse(); - _rely(); - _file(); - } - - function _endorse() internal { - root.endorse(address(router)); - root.endorse(address(escrow)); - } - - function _rely() internal { - // Rely on PoolManager - escrow.rely(address(poolManager)); - IAuth(vaultFactory).rely(address(poolManager)); - IAuth(trancheFactory).rely(address(poolManager)); - IAuth(restrictionManager).rely(address(poolManager)); - - // Rely on Root - router.rely(address(root)); - poolManager.rely(address(root)); - investmentManager.rely(address(root)); - gateway.rely(address(root)); - gasService.rely(address(root)); - escrow.rely(address(root)); - routerEscrow.rely(address(root)); - transferProxyFactory.rely(address(root)); - IAuth(vaultFactory).rely(address(root)); - IAuth(trancheFactory).rely(address(root)); - IAuth(restrictionManager).rely(address(root)); - - // Rely on guardian - root.rely(address(guardian)); - gateway.rely(address(guardian)); - - // Rely on gateway - root.rely(address(gateway)); - investmentManager.rely(address(gateway)); - poolManager.rely(address(gateway)); - gasService.rely(address(gateway)); - - // Rely on others - routerEscrow.rely(address(router)); - investmentManager.rely(address(vaultFactory)); - } - - function _file() public { - poolManager.file("investmentManager", address(investmentManager)); - poolManager.file("gasService", address(gasService)); - poolManager.file("gateway", address(gateway)); - - investmentManager.file("poolManager", address(poolManager)); - investmentManager.file("gateway", address(gateway)); - - gateway.file("payers", address(router), true); - - transferProxyFactory.file("poolManager", address(poolManager)); - } - - function wire(address adapter) public { - adapters.push(adapter); - gateway.file("adapters", adapters); - IAuth(adapter).rely(address(root)); - } - - function removeDeployerAccess(address adapter, address deployer) public { - IAuth(adapter).deny(deployer); - IAuth(vaultFactory).deny(deployer); - IAuth(trancheFactory).deny(deployer); - IAuth(restrictionManager).deny(deployer); - transferProxyFactory.deny(deployer); - root.deny(deployer); - investmentManager.deny(deployer); - poolManager.deny(deployer); - escrow.deny(deployer); - routerEscrow.deny(deployer); - gateway.deny(deployer); - router.deny(deployer); - gasService.deny(deployer); - } -} diff --git a/script/Forwarder.s.sol b/script/Forwarder.s.sol deleted file mode 100644 index 415319a..0000000 --- a/script/Forwarder.s.sol +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.26; - -import {AxelarForwarder} from "src/gateway/adapters/axelar/Forwarder.sol"; -import "forge-std/Script.sol"; - -// Script to deploy Axelar over XCM relayer. -contract ForwarderScript is Script { - function setUp() public {} - - function run() public { - vm.startBroadcast(); - - address admin = vm.envAddress("ADMIN"); - - AxelarForwarder adapter = new AxelarForwarder(address(vm.envAddress("AXELAR_GATEWAY"))); - - adapter.rely(admin); - - vm.stopBroadcast(); - } -} diff --git a/script/Permissionless.s.sol b/script/Permissionless.s.sol deleted file mode 100644 index 49d0a78..0000000 --- a/script/Permissionless.s.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.26; - -import {PermissionlessAdapter} from "test/mocks/PermissionlessAdapter.sol"; -import {InvestmentManager} from "src/InvestmentManager.sol"; -import {Deployer} from "script/Deployer.sol"; - -// Script to deploy Liquidity Pools with a permissionless adapter for testing. -contract PermissionlessScript is Deployer { - function setUp() public {} - - function run() public { - vm.startBroadcast(); - - adminSafe = msg.sender; - - deploy(msg.sender); - PermissionlessAdapter adapter = new PermissionlessAdapter(address(gateway)); - wire(address(adapter)); - - vm.stopBroadcast(); - } -} diff --git a/test/recon/CryticTester.sol b/test/recon/CryticTester.sol new file mode 100644 index 0000000..20043a2 --- /dev/null +++ b/test/recon/CryticTester.sol @@ -0,0 +1,14 @@ + +// SPDX-License-Identifier: GPL-2.0 +pragma solidity ^0.8.0; + +import {TargetFunctions} from "./TargetFunctions.sol"; +import {CryticAsserts} from "@chimera/CryticAsserts.sol"; + +// echidna . --contract CryticTester --config echidna.yaml +// medusa fuzz +contract CryticTester is TargetFunctions, CryticAsserts { + constructor() payable { + setup(); + } +} diff --git a/test/recon/CryticToFoundry.sol b/test/recon/CryticToFoundry.sol new file mode 100644 index 0000000..df65982 --- /dev/null +++ b/test/recon/CryticToFoundry.sol @@ -0,0 +1,17 @@ + +// SPDX-License-Identifier: GPL-2.0 +pragma solidity ^0.8.0; + +import {Test} from "forge-std/Test.sol"; +import {TargetFunctions} from "./TargetFunctions.sol"; +import {FoundryAsserts} from "@chimera/FoundryAsserts.sol"; + +contract CryticToFoundry is Test, TargetFunctions, FoundryAsserts { + function setUp() public { + setup(); + } + + function testDemo() public { + // TODO: Given any target function and foundry assert, test your results + } +} diff --git a/test/recon/Properties.sol b/test/recon/Properties.sol new file mode 100644 index 0000000..f441e39 --- /dev/null +++ b/test/recon/Properties.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 +pragma solidity ^0.8.0; + +import {Asserts} from "@chimera/Asserts.sol"; +import {Setup} from "./Setup.sol"; +import {ERC7540Properties} from "erc7540-reusable-properties/ERC7540Properties.sol"; + +abstract contract Properties is Setup, Asserts, ERC7540Properties {} diff --git a/test/recon/Setup.sol b/test/recon/Setup.sol new file mode 100644 index 0000000..41d150c --- /dev/null +++ b/test/recon/Setup.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 +pragma solidity ^0.8.0; + +import {BaseSetup} from "@chimera/BaseSetup.sol"; + +import "src/ERC7540Vault.sol"; +import {ERC20} from "src/token/ERC20.sol"; + +abstract contract Setup is BaseSetup { + address admin; + ERC20 asset; + ERC20 share; + ERC7540Vault vault; + + function setup() internal virtual override { + admin = address(this); + asset = new ERC20(uint8(18)); + share = new ERC20(uint8(18)); + bytes16 trancheId = hex"01"; + vault = new ERC7540Vault( + 1, + trancheId, + address(asset), + address(share), + admin, + admin, + admin + ); + } +} diff --git a/test/recon/TargetFunctions.sol b/test/recon/TargetFunctions.sol new file mode 100644 index 0000000..64bd16f --- /dev/null +++ b/test/recon/TargetFunctions.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0 +pragma solidity ^0.8.0; + +import {BaseTargetFunctions} from "@chimera/BaseTargetFunctions.sol"; +import {Properties} from "./Properties.sol"; +import {vm} from "@chimera/Hevm.sol"; + +abstract contract TargetFunctions is BaseTargetFunctions, Properties { + function vault_requestDeposit(uint256 assets) public { + // clamp inputs to use the values from setup + vault.requestDeposit(assets, admin, admin); + } + + function vault_mint(uint256 shares) public { + vault.mint(shares, admin); + } + + // NOTE: this is required for checks in properties to be valid, could have multiple actors in setup and switch between them + function setup_switchActor() public { + actor = admin; + } +}