From c71b9f3ad25a6fd72d61a7856b0ca6bb0183cc7c Mon Sep 17 00:00:00 2001 From: b00ste Date: Thu, 22 Aug 2024 14:46:30 +0300 Subject: [PATCH 1/3] test: add extreme case tests --- .../contracts/mock/InfiniteLoopURD.sol | 24 +++++ .../contracts/mock/ReturnBomb.sol | 26 +++++ .../mock/SelfDestructOnInterfaceCheck.sol | 23 +++++ .../tests/LSP26FollowerSystem.test.ts | 96 +++++++++++++++++++ 4 files changed, 169 insertions(+) create mode 100644 packages/lsp26-contracts/contracts/mock/InfiniteLoopURD.sol create mode 100644 packages/lsp26-contracts/contracts/mock/ReturnBomb.sol create mode 100644 packages/lsp26-contracts/contracts/mock/SelfDestructOnInterfaceCheck.sol diff --git a/packages/lsp26-contracts/contracts/mock/InfiniteLoopURD.sol b/packages/lsp26-contracts/contracts/mock/InfiniteLoopURD.sol new file mode 100644 index 000000000..8517a6241 --- /dev/null +++ b/packages/lsp26-contracts/contracts/mock/InfiniteLoopURD.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.17; + +// interfaces +import { + ILSP1UniversalReceiver +} from "@lukso/lsp1-contracts/contracts/ILSP1UniversalReceiver.sol"; + +contract InfiniteLoopURD is ILSP1UniversalReceiver { + uint256 public counter; + + function supportsInterface(bytes4) external pure returns (bool) { + return true; + } + + function universalReceiver( + bytes32, + bytes memory + ) external payable returns (bytes memory) { + while (true) { + ++counter; + } + } +} diff --git a/packages/lsp26-contracts/contracts/mock/ReturnBomb.sol b/packages/lsp26-contracts/contracts/mock/ReturnBomb.sol new file mode 100644 index 000000000..7212088da --- /dev/null +++ b/packages/lsp26-contracts/contracts/mock/ReturnBomb.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.17; + +// interfaces +import { + ILSP1UniversalReceiver +} from "@lukso/lsp1-contracts/contracts/ILSP1UniversalReceiver.sol"; + +contract ReturnBomb is ILSP1UniversalReceiver { + uint256 public counter; + + function supportsInterface(bytes4) external pure returns (bool) { + return true; + } + + function universalReceiver( + bytes32, + bytes memory + ) external payable returns (bytes memory) { + ++counter; + // solhint-disable-next-line no-inline-assembly + assembly { + revert(0, 10000) + } + } +} diff --git a/packages/lsp26-contracts/contracts/mock/SelfDestructOnInterfaceCheck.sol b/packages/lsp26-contracts/contracts/mock/SelfDestructOnInterfaceCheck.sol new file mode 100644 index 000000000..517ad6f82 --- /dev/null +++ b/packages/lsp26-contracts/contracts/mock/SelfDestructOnInterfaceCheck.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.17; + +// interfaces +import { + ILSP1UniversalReceiver +} from "@lukso/lsp1-contracts/contracts/ILSP1UniversalReceiver.sol"; + +contract SelfDestructOnInterfaceCheck is ILSP1UniversalReceiver { + uint256 public counter; + + function supportsInterface(bytes4) external returns (bool) { + selfdestruct(payable(msg.sender)); + return true; + } + + function universalReceiver( + bytes32, + bytes memory + ) external payable returns (bytes memory) { + return ""; + } +} diff --git a/packages/lsp26-contracts/tests/LSP26FollowerSystem.test.ts b/packages/lsp26-contracts/tests/LSP26FollowerSystem.test.ts index fe21b6d22..ef00ff770 100644 --- a/packages/lsp26-contracts/tests/LSP26FollowerSystem.test.ts +++ b/packages/lsp26-contracts/tests/LSP26FollowerSystem.test.ts @@ -15,6 +15,12 @@ import { LSP0ERC725Account__factory, RevertOnFollow__factory, RevertOnFollow, + ReturnBomb__factory, + ReturnBomb, + SelfDestructOnInterfaceCheck__factory, + SelfDestructOnInterfaceCheck, + InfiniteLoopURD, + InfiniteLoopURD__factory, } from '../types'; describe('testing `LSP26FollowerSystem`', () => { @@ -115,6 +121,96 @@ describe('testing `LSP26FollowerSystem`', () => { }); }); + describe('testing follow/unfollow a contract that self destructs on interface check', async () => { + let selfDestruct: SelfDestructOnInterfaceCheck; + + before(async () => { + selfDestruct = await new SelfDestructOnInterfaceCheck__factory(context.owner).deploy(); + }); + + it('should pass following', async () => { + await context.followerSystem.connect(context.owner).follow(await selfDestruct.getAddress()); + + expect( + await context.followerSystem.isFollowing( + context.owner.address, + await selfDestruct.getAddress(), + ), + ).to.be.true; + }); + + it('should pass unfollowing', async () => { + await context.followerSystem.connect(context.owner).unfollow(await selfDestruct.getAddress()); + + expect( + await context.followerSystem.isFollowing( + context.owner.address, + await selfDestruct.getAddress(), + ), + ).to.be.false; + }); + }); + + describe('testing follow/unfollow a contract with return bomb', () => { + let returnBomb: ReturnBomb; + + before(async () => { + returnBomb = await new ReturnBomb__factory(context.owner).deploy(); + }); + + it('should pass following', async () => { + await context.followerSystem.connect(context.owner).follow(await returnBomb.getAddress()); + + expect( + await context.followerSystem.isFollowing( + context.owner.address, + await returnBomb.getAddress(), + ), + ).to.be.true; + }); + + it('should pass unfollowing', async () => { + await context.followerSystem.connect(context.owner).unfollow(await returnBomb.getAddress()); + + expect( + await context.followerSystem.isFollowing( + context.owner.address, + await returnBomb.getAddress(), + ), + ).to.be.false; + }); + }); + + describe('testing follow/unfollow a contract that has an infinite loop in urd', () => { + let infiniteLoop: InfiniteLoopURD; + + before(async () => { + infiniteLoop = await new InfiniteLoopURD__factory(context.owner).deploy(); + }); + + it('should pass following', async () => { + await context.followerSystem.connect(context.owner).follow(await infiniteLoop.getAddress()); + + expect( + await context.followerSystem.isFollowing( + context.owner.address, + await infiniteLoop.getAddress(), + ), + ).to.be.true; + }); + + it('should pass unfollowing', async () => { + await context.followerSystem.connect(context.owner).unfollow(await infiniteLoop.getAddress()); + + expect( + await context.followerSystem.isFollowing( + context.owner.address, + await infiniteLoop.getAddress(), + ), + ).to.be.false; + }); + }); + describe.skip('gas tests', () => { const gasCostResult: { followingGasCost?: number[]; From 126059c7872f72a2e169f4a7651c491186326b27 Mon Sep 17 00:00:00 2001 From: b00ste Date: Wed, 21 Aug 2024 12:19:11 +0300 Subject: [PATCH 2/3] feat: create script to deploy LSP26 Follower System --- .../016_deploy_lsp26_follower_system.ts | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 packages/lsp-smart-contracts/deploy/016_deploy_lsp26_follower_system.ts diff --git a/packages/lsp-smart-contracts/deploy/016_deploy_lsp26_follower_system.ts b/packages/lsp-smart-contracts/deploy/016_deploy_lsp26_follower_system.ts new file mode 100644 index 000000000..ba60f0db8 --- /dev/null +++ b/packages/lsp-smart-contracts/deploy/016_deploy_lsp26_follower_system.ts @@ -0,0 +1,57 @@ +import { getCreate2Address, concat, keccak256 } from 'ethers'; +import { config, ethers } from 'hardhat'; +import { DeployFunction } from 'hardhat-deploy/types'; + +const deployFollowerSystem: DeployFunction = async ({ getNamedAccounts }) => { + const { owner: account } = await getNamedAccounts(); + const deployer = await ethers.getSigner(account); + console.log('Deploying with deployer address: ', deployer.address); + + const { deterministicDeployment } = config; + const nickFactoryAddress = deterministicDeployment['luksoTestnet'].factory; + + console.log('Deploying contract 🆙🔄 using Nick Factory 🧙🏻‍♂️ at address: ', nickFactoryAddress); + + // Salt found: 0x7c0acd1428c1a42815d06ceeb50b11fcb9beddb1dcc582ddf5f9ca37979c7e4d with address: 0xf01103E5a9909Fc0DBe8166dA7085e0285daDDcA + // Salt found: 0xc1432fd06941442324a9c2ae23cbb4ac622901a14ee172c419eb9f9e0a324c20 with address: 0xf01103E59c502Eb836A3dfB5f80D101c3FD0f53b + + const EXPECTED_ADDRESS = '0xf01103E5a9909Fc0DBe8166dA7085e0285daDDcA'; + const STANDARD_SALT = '0x7c0acd1428c1a42815d06ceeb50b11fcb9beddb1dcc582ddf5f9ca37979c7e4d'; + const CONTRACT_BYTEODE = + '0x608060405234801561001057600080fd5b50610d6c806100206000396000f3fe608060405234801561001057600080fd5b50600436106100a35760003560e01c8063645487071161007657806399ec3a421161005b57806399ec3a421461013c578063b2a8d0691461015f578063cf8711c81461017257600080fd5b806364548707146101165780638dd1e47e1461012957600080fd5b8063015a4ead146100a857806330b3a890146100bd5780634dbf27cc146100e35780635a39c581146100f6575b600080fd5b6100bb6100b6366004610a01565b610185565b005b6100d06100cb366004610a01565b610191565b6040519081526020015b60405180910390f35b6100bb6100f1366004610a01565b6101b8565b610109610104366004610a1c565b6101c1565b6040516100da9190610a4f565b6100d0610124366004610a01565b610292565b6100bb610137366004610ae3565b6102b3565b61014f61014a366004610b90565b6102f5565b60405190151581526020016100da565b61010961016d366004610a1c565b61031e565b6100bb610180366004610ae3565b6103e5565b61018e81610423565b50565b6001600160a01b03811660009081526020819052604081206101b2906105d0565b92915050565b61018e816105da565b606060006101cf8484610bd9565b905060008167ffffffffffffffff8111156101ec576101ec610a9c565b604051908082528060200260200182016040528015610215578160200160208202803683370190505b50905060005b828110156102885761024e6102308288610bec565b6001600160a01b038916600090815260016020526040902090610752565b82828151811061026057610260610bff565b6001600160a01b039092166020928302919091019091015261028181610c15565b905061021b565b5095945050505050565b6001600160a01b03811660009081526001602052604081206101b2906105d0565b60005b81518110156102f1576102e18282815181106102d4576102d4610bff565b6020026020010151610423565b6102ea81610c15565b90506102b6565b5050565b6001600160a01b0382166000908152600160205260408120610317908361075e565b9392505050565b6060600061032c8484610bd9565b905060008167ffffffffffffffff81111561034957610349610a9c565b604051908082528060200260200182016040528015610372578160200160208202803683370190505b50905060005b82811015610288576103ab61038d8288610bec565b6001600160a01b038916600090815260208190526040902090610752565b8282815181106103bd576103bd610bff565b6001600160a01b03909216602092830291909101909101526103de81610c15565b9050610378565b60005b81518110156102f15761041382828151811061040657610406610bff565b60200260200101516105da565b61041c81610c15565b90506103e8565b33600090815260016020526040812061043c9083610780565b905080610485576040517fc70bad4e0000000000000000000000000000000000000000000000000000000081526001600160a01b03831660048201526024015b60405180910390fd5b6001600160a01b03821660009081526020819052604090206104a79033610780565b50604080513381526001600160a01b03841660208201527f083700fd0d85112c9d8c5823585c7542e8fadb693c9902e5bc590ab367f7a15e910160405180910390a16105036001600160a01b038316631aed5a8560e21b610795565b156102f1576040516bffffffffffffffffffffffff193360601b1660208201526001600160a01b03831690636bb56a14907f9d3c0b4012b69658977b099bdaa51eff0f0460f421fba96d15669506c00d1c4f906034015b6040516020818303038152906040526040518363ffffffff1660e01b8152600401610586929190610c52565b6000604051808303816000875af19250505080156105c657506040513d6000823e601f3d908101601f191682016040526105c39190810190610c8c565b60015b156102f157505050565b60006101b2825490565b6001600160a01b038116330361061c576040517fea61954200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360009081526001602052604081206106359083610864565b905080610679576040517f6feacbf60000000000000000000000000000000000000000000000000000000081526001600160a01b038316600482015260240161047c565b6001600160a01b038216600090815260208190526040902061069b9033610864565b50604080513381526001600160a01b03841660208201527fbccc71dc7842b86291138666aa18e133ee6d41aa71e6d7c650debad1a0576635910160405180910390a16106f76001600160a01b038316631aed5a8560e21b610795565b156102f1576040516bffffffffffffffffffffffff193360601b1660208201526001600160a01b03831690636bb56a14907f71e02f9f05bcd5816ec4f3134aa2e5a916669537ec6c77fe66ea595fabc2d51a9060340161055a565b60006103178383610879565b6001600160a01b03811660009081526001830160205260408120541515610317565b6000610317836001600160a01b0384166108a3565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d9150600051905082801561084d575060208210155b80156108595750600081115b979650505050505050565b6000610317836001600160a01b038416610996565b600082600001828154811061089057610890610bff565b9060005260206000200154905092915050565b6000818152600183016020526040812054801561098c5760006108c7600183610bd9565b85549091506000906108db90600190610bd9565b90508181146109405760008660000182815481106108fb576108fb610bff565b906000526020600020015490508087600001848154811061091e5761091e610bff565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061095157610951610d20565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506101b2565b60009150506101b2565b60008181526001830160205260408120546109dd575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556101b2565b5060006101b2565b80356001600160a01b03811681146109fc57600080fd5b919050565b600060208284031215610a1357600080fd5b610317826109e5565b600080600060608486031215610a3157600080fd5b610a3a846109e5565b95602085013595506040909401359392505050565b6020808252825182820181905260009190848201906040850190845b81811015610a905783516001600160a01b031683529284019291840191600101610a6b565b50909695505050505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715610adb57610adb610a9c565b604052919050565b60006020808385031215610af657600080fd5b823567ffffffffffffffff80821115610b0e57600080fd5b818501915085601f830112610b2257600080fd5b813581811115610b3457610b34610a9c565b8060051b9150610b45848301610ab2565b8181529183018401918481019088841115610b5f57600080fd5b938501935b83851015610b8457610b75856109e5565b82529385019390850190610b64565b98975050505050505050565b60008060408385031215610ba357600080fd5b610bac836109e5565b9150610bba602084016109e5565b90509250929050565b634e487b7160e01b600052601160045260246000fd5b818103818111156101b2576101b2610bc3565b808201808211156101b2576101b2610bc3565b634e487b7160e01b600052603260045260246000fd5b600060018201610c2757610c27610bc3565b5060010190565b60005b83811015610c49578181015183820152602001610c31565b50506000910152565b8281526040602082015260008251806040840152610c77816060850160208701610c2e565b601f01601f1916919091016060019392505050565b600060208284031215610c9e57600080fd5b815167ffffffffffffffff80821115610cb657600080fd5b818401915084601f830112610cca57600080fd5b815181811115610cdc57610cdc610a9c565b610cef601f8201601f1916602001610ab2565b9150808252856020828501011115610d0657600080fd5b610d17816020840160208601610c2e565b50949350505050565b634e487b7160e01b600052603160045260246000fdfea2646970667358221220d49dd5b2a7c580c2c5c73e52c9cd6bd492943d285801069245b07017db7893bd64736f6c63430008110033'; + + const preCalculatedAddress = getCreate2Address( + nickFactoryAddress, + STANDARD_SALT, + keccak256(CONTRACT_BYTEODE), + ); + + if (preCalculatedAddress !== EXPECTED_ADDRESS) { + throw new Error( + `❌ Aborting CREATE2 deployment: Incorrect pre-calculated address with CREATE2. Expected ${EXPECTED_ADDRESS}, got ${preCalculatedAddress}`, + ); + } else { + function Create2Address(name, address) { + this.name = name; + this.address = address; + } + + console.log(`Pre-calculated address match! 🪄`); + console.table([ + new Create2Address('EXPECTED_ADDRESS', EXPECTED_ADDRESS), + new Create2Address('getCreate2Address', preCalculatedAddress), + ]); + console.log('✅ Processing with deployment: '); + } + + const deploymentTx = { + to: nickFactoryAddress, + data: concat([STANDARD_SALT, CONTRACT_BYTEODE]), + }; + + const tx = await deployer.sendTransaction(deploymentTx); + console.log('Deployment tx: ', tx); +}; + +export default deployFollowerSystem; +deployFollowerSystem.tags = ['LSP26FollowerSystem']; From 4dc2bcf8e084cf3749e5640780067ce2d626ebea Mon Sep 17 00:00:00 2001 From: Andreas Richter <708186+richtera@users.noreply.github.com> Date: Fri, 30 Aug 2024 05:01:30 -0400 Subject: [PATCH 3/3] fix: Repair node-versions to match everywhere. --- .github/workflows/benchmark.yml | 8 ++++---- .github/workflows/build-lint-test.yml | 2 +- .github/workflows/coverage.yml | 6 +++--- .github/workflows/deploy-verify.yml | 2 +- .github/workflows/mythx-analysis.yml | 6 +++--- .github/workflows/solc_version.yml | 6 +++--- .tool-versions | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index da10a1024..7ab47765e 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -34,7 +34,7 @@ jobs: fetch-depth: 0 - name: Use Node.js v20 - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: "20.x" cache: "npm" @@ -59,10 +59,10 @@ jobs: with: clean: false - - name: Use Node.js '16.15.0' - uses: actions/setup-node@v2 + - name: Use Node.js '20.x' + uses: actions/setup-node@v4 with: - node-version: "16.15.0" + node-version: "20.x" cache: "npm" - name: 📦 Install dependencies diff --git a/.github/workflows/build-lint-test.yml b/.github/workflows/build-lint-test.yml index 94745231b..4a530cea2 100644 --- a/.github/workflows/build-lint-test.yml +++ b/.github/workflows/build-lint-test.yml @@ -13,7 +13,7 @@ jobs: # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ - name: Use Node.js v20 - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: "20.x" cache: "npm" diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index cef18cecc..31c70e4eb 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -22,10 +22,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Use Node.js v16 - uses: actions/setup-node@v2 + - name: Use Node.js v20.x + uses: actions/setup-node@v4 with: - node-version: "16.x" + node-version: "20.x" cache: "npm" - name: Install dependencies diff --git a/.github/workflows/deploy-verify.yml b/.github/workflows/deploy-verify.yml index 410b78a52..51e51d8cb 100644 --- a/.github/workflows/deploy-verify.yml +++ b/.github/workflows/deploy-verify.yml @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@v3 - name: Use Node.js v20 - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: "20.x" cache: "npm" diff --git a/.github/workflows/mythx-analysis.yml b/.github/workflows/mythx-analysis.yml index 438c4c212..8503487e4 100644 --- a/.github/workflows/mythx-analysis.yml +++ b/.github/workflows/mythx-analysis.yml @@ -15,10 +15,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Setup Node.js '16.15.0' - uses: actions/setup-node@v2 + - name: Setup Node.js '20.x' + uses: actions/setup-node@v4 with: - node-version: "16.15.0" + node-version: "20.x" cache: "npm" - name: Set up Python 3.8 diff --git a/.github/workflows/solc_version.yml b/.github/workflows/solc_version.yml index 469cf228a..e1563ae70 100644 --- a/.github/workflows/solc_version.yml +++ b/.github/workflows/solc_version.yml @@ -49,10 +49,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Use Node.js '16.15.0' - uses: actions/setup-node@v2 + - name: Use Node.js '20.x' + uses: actions/setup-node@v4 with: - node-version: "16.15.0" + node-version: "20.x" cache: "npm" - name: 📦 Install dependencies diff --git a/.tool-versions b/.tool-versions index 5f732e608..3e511092e 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -nodejs 16.19.0 \ No newline at end of file +nodejs 20