diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e7b79eeb..300f519c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,9 @@ jobs: - name: Install dependencies run: yarn --frozen-lockfile --network-concurrency 1 + - name: Create mock files with smock + run: yarn smock + - name: Precompile using 0.8.14 and via-ir=false run: yarn build @@ -58,6 +61,9 @@ jobs: - name: Install dependencies run: yarn --frozen-lockfile --network-concurrency 1 + - name: Create mock files with smock + run: yarn smock + - name: Precompile using 0.8.14 and via-ir=false run: yarn build diff --git a/.gitignore b/.gitignore index 9209344d..c6c30df9 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,6 @@ broadcast/*/*/* # Out dir out + +# Smock dir +test/smock diff --git a/package.json b/package.json index c1dc0bba..eb4e08f9 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "lint:sol-logic": "solhint -c .solhint.json 'src/**/*.sol' 'script/**/*.sol'", "lint:sol-tests": "solhint -c .solhint.tests.json 'test/**/*.sol'", "prepare": "husky install", + "smock": "smock-foundry --contracts src/contracts", "test": "forge test -vvv", "test:integration": "forge test --match-contract Integration -vvv", "test:unit": "forge test --match-contract Unit -vvv", @@ -42,6 +43,7 @@ "@commitlint/cli": "19.3.0", "@commitlint/config-conventional": "19.2.2", "@defi-wonderland/natspec-smells": "1.1.1", + "@defi-wonderland/smock-foundry": "1.5.0", "forge-std": "github:foundry-rs/forge-std#5475f85", "husky": ">=8", "lint-staged": ">=10", diff --git a/src/contracts/BFactory.sol b/src/contracts/BFactory.sol index b96e7a78..bdf561f6 100644 --- a/src/contracts/BFactory.sol +++ b/src/contracts/BFactory.sol @@ -22,7 +22,7 @@ contract BFactory is BBronze { event LOG_BLABS(address indexed caller, address indexed blabs); - mapping(address => bool) private _isBPool; + mapping(address => bool) internal _isBPool; function isBPool(address b) external view returns (bool) { return _isBPool[b]; @@ -36,7 +36,7 @@ contract BFactory is BBronze { return bpool; } - address private _blabs; + address internal _blabs; constructor() { _blabs = msg.sender; diff --git a/src/contracts/BPool.sol b/src/contracts/BPool.sol index dc0f326a..33aa0e13 100644 --- a/src/contracts/BPool.sol +++ b/src/contracts/BPool.sol @@ -19,7 +19,7 @@ import './BToken.sol'; contract BPool is BBronze, BToken, BMath { struct Record { bool bound; // is token bound to pool - uint256 index; // private + uint256 index; // internal uint256 denorm; // denormalized weight uint256 balance; } @@ -55,20 +55,20 @@ contract BPool is BBronze, BToken, BMath { _; } - bool private _mutex; + bool internal _mutex; - address private _factory; // BFactory address to push token exitFee to - address private _controller; // has CONTROL role - bool private _publicSwap; // true if PUBLIC can call SWAP functions + address internal _factory; // BFactory address to push token exitFee to + address internal _controller; // has CONTROL role + bool internal _publicSwap; // true if PUBLIC can call SWAP functions // `setSwapFee` and `finalize` require CONTROL // `finalize` sets `PUBLIC can SWAP`, `PUBLIC can JOIN` - uint256 private _swapFee; - bool private _finalized; + uint256 internal _swapFee; + bool internal _finalized; - address[] private _tokens; - mapping(address => Record) private _records; - uint256 private _totalWeight; + address[] internal _tokens; + mapping(address => Record) internal _records; + uint256 internal _totalWeight; constructor() { _controller = msg.sender; diff --git a/src/contracts/BToken.sol b/src/contracts/BToken.sol index 11e0a808..7fcdd028 100644 --- a/src/contracts/BToken.sol +++ b/src/contracts/BToken.sol @@ -65,9 +65,9 @@ abstract contract BTokenBase is BNum, IERC20 { } contract BToken is BTokenBase { - string private _name = 'Balancer Pool Token'; - string private _symbol = 'BPT'; - uint8 private _decimals = 18; + string internal _name = 'Balancer Pool Token'; + string internal _symbol = 'BPT'; + uint8 internal _decimals = 18; function name() public view returns (string memory) { return _name; diff --git a/src/contracts/Migrations.sol b/src/contracts/Migrations.sol deleted file mode 100644 index d55802c1..00000000 --- a/src/contracts/Migrations.sol +++ /dev/null @@ -1,23 +0,0 @@ -pragma solidity 0.8.23; - -contract Migrations { - address public owner; - uint256 public lastCompletedMigration; - - constructor() { - owner = msg.sender; - } - - modifier restricted() { - if (msg.sender == owner) _; - } - - function setCompleted(uint256 completed) external restricted { - lastCompletedMigration = completed; - } - - function upgrade(address new_address) external restricted { - Migrations upgraded = Migrations(new_address); - upgraded.setCompleted(lastCompletedMigration); - } -} diff --git a/test/unit/BPool.t.sol b/test/unit/BPool.t.sol index 64038c18..58293520 100644 --- a/test/unit/BPool.t.sol +++ b/test/unit/BPool.t.sol @@ -1,8 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.23; -import {BConst} from 'contracts/BConst.sol'; import {BPool} from 'contracts/BPool.sol'; +import {MockBPool} from 'test/smock/MockBPool.sol'; + +import {BConst} from 'contracts/BConst.sol'; import {IERC20} from 'contracts/BToken.sol'; import {Test} from 'forge-std/Test.sol'; import {LibString} from 'solmate/utils/LibString.sol'; @@ -18,11 +20,11 @@ abstract contract BasePoolTest is Test, BConst, Utils { uint256 internal constant _RECORD_MAPPING_SLOT_NUMBER = 10; uint256 internal constant _TOKENS_ARRAY_SLOT_NUMBER = 9; - BPool public bPool; + MockBPool public bPool; address[TOKENS_AMOUNT] public tokens; function setUp() public { - bPool = new BPool(); + bPool = new MockBPool(); // Create fake tokens for (uint256 i = 0; i < tokens.length; i++) { @@ -48,31 +50,23 @@ abstract contract BasePoolTest is Test, BConst, Utils { } function _setTokens(address[] memory _tokens) internal { - _writeArrayLengthToStorage(address(bPool), _TOKENS_ARRAY_SLOT_NUMBER, _tokens.length); // write length - for (uint256 i = 0; i < _tokens.length; i++) { - _writeAddressArrayItemToStorage(address(bPool), _TOKENS_ARRAY_SLOT_NUMBER, i, _tokens[i]); // write token - } - } - - function _setRecordBound(address _token) internal { - _writeStructPropertyAtAddressMapping(address(bPool), _RECORD_MAPPING_SLOT_NUMBER, _token, 0, 1); // bound (1 == true) + bPool.set__tokens(_tokens); } - function _setRecordBalance(address _token, uint256 _balance) internal { - _writeStructPropertyAtAddressMapping(address(bPool), _RECORD_MAPPING_SLOT_NUMBER, _token, 3, _balance); // balance + function _setRecord(address _token, BPool.Record memory _record) internal { + bPool.set__records(_token, _record); } function _setPublicSwap(bool _isPublicSwap) internal { - // TODO: make it depend on the bool value - _writeUintToStorage(address(bPool), 6, 0x0000000000000000000000010000000000000000000000000000000000000000); + bPool.set__publicSwap(_isPublicSwap); } function _setFinalize(bool _isFinalized) internal { - // TODO: make it depend on the bool value - _writeUintToStorage(address(bPool), 8, 1); + bPool.set__finalized(_isFinalized); } function _setTotalSupply(uint256 _totalSupply) internal { + // NOTE: not in smock as it uses ERC20.totalSupply() _writeUintToStorage(address(bPool), 2, _totalSupply); } } @@ -340,8 +334,15 @@ contract BPool_Unit_JoinPool is BasePoolTest { // Set balances for (uint256 i = 0; i < tokens.length; i++) { - _setRecordBound(tokens[i]); - _setRecordBalance(tokens[i], _fuzz.balance[i]); + _setRecord( + tokens[i], + BPool.Record({ + bound: true, + index: 0, // NOTE: irrelevant for this method + denorm: 0, // NOTE: irrelevant for this method + balance: _fuzz.balance[i] + }) + ); } // Set public swap diff --git a/yarn.lock b/yarn.lock index b7c9332e..8c16eda5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -183,6 +183,16 @@ solc-typed-ast "18.1.2" yargs "17.7.2" +"@defi-wonderland/smock-foundry@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@defi-wonderland/smock-foundry/-/smock-foundry-1.5.0.tgz#e0f91cb40a1805644fbe1bcec8bbb6556d72253c" + integrity sha512-GjMc8Tgg+jBzV0zp00WTSlExptthocrnE3FY58Zm4Li+jWwrphKRflGRf4F65f1t976SNIW63mleWJViFa4mRA== + dependencies: + fast-glob "3.3.2" + handlebars "4.7.7" + solc-typed-ast "18.1.3" + yargs "17.7.2" + "@noble/curves@1.3.0", "@noble/curves@~1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.3.0.tgz#01be46da4fd195822dab821e72f71bf4aeec635e" @@ -371,7 +381,7 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" -axios@^1.6.7: +axios@^1.6.7, axios@^1.6.8: version "1.6.8" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== @@ -952,6 +962,18 @@ graceful-fs@^4.2.0: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +handlebars@4.7.7: + version "4.7.7" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" + integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.0" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -1387,7 +1409,7 @@ minimatch@^5.0.1: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.8: +minimist@^1.2.5, minimist@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -1397,6 +1419,11 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +neo-async@^2.6.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + npm-run-path@^5.1.0: version "5.3.0" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f" @@ -1676,6 +1703,22 @@ solc-typed-ast@18.1.2: src-location "^1.1.0" web3-eth-abi "^4.2.0" +solc-typed-ast@18.1.3: + version "18.1.3" + resolved "https://registry.yarnpkg.com/solc-typed-ast/-/solc-typed-ast-18.1.3.tgz#243cc0c5a4f701445ac10341224bf8c18a6ed252" + integrity sha512-11iBtavJJtkzrmQdlAiZ7sz3C3WOQ8MaN7+r4b9C6B/3ORqg4oTUW5/ANyGyus5ppXDXzPyT90BYCfP73df3HA== + dependencies: + axios "^1.6.8" + commander "^12.0.0" + decimal.js "^10.4.3" + findup-sync "^5.0.0" + fs-extra "^11.2.0" + jsel "^1.1.6" + semver "^7.6.0" + solc "0.8.25" + src-location "^1.1.0" + web3-eth-abi "^4.2.0" + solc@0.8.24: version "0.8.24" resolved "https://registry.yarnpkg.com/solc/-/solc-0.8.24.tgz#6e5693d28208d00a20ff2bdabc1dec85a5329bbb" @@ -1689,6 +1732,19 @@ solc@0.8.24: semver "^5.5.0" tmp "0.0.33" +solc@0.8.25: + version "0.8.25" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.8.25.tgz#393f3101617388fb4ba2a58c5b03ab02678e375c" + integrity sha512-7P0TF8gPeudl1Ko3RGkyY6XVCxe2SdD/qQhtns1vl3yAbK/PDifKDLHGtx1t7mX3LgR7ojV7Fg/Kc6Q9D2T8UQ== + dependencies: + command-exists "^1.2.8" + commander "^8.1.0" + follow-redirects "^1.12.1" + js-sha3 "0.8.0" + memorystream "^0.3.1" + semver "^5.5.0" + tmp "0.0.33" + "solhint@github:solhint-community/solhint-community#v4.0.0-rc01": version "4.0.0-rc01" resolved "https://codeload.github.com/solhint-community/solhint-community/tar.gz/b04088d3d2bb6ceb8c9bf77783e71195d19fb653" @@ -1736,6 +1792,11 @@ sort-package-json@2.10.0: semver "^7.6.0" sort-object-keys "^1.1.3" +source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + split2@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" @@ -1858,6 +1919,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +uglify-js@^3.1.4: + version "3.17.4" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" + integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== + unicorn-magic@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" @@ -1956,6 +2022,11 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"