diff --git a/.cspell.json b/.cspell.json index 3e83029f..1de3c234 100644 --- a/.cspell.json +++ b/.cspell.json @@ -1,13 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json", "version": "0.2", - "ignorePaths": [ - "**/*.json", - "**/*.css", - "node_modules", - "**/*.log", - "/lib" - ], + "ignorePaths": ["**/*.json", "**/*.css", "node_modules", "**/*.log", "/lib"], "useGitignore": true, "language": "en", "words": [ @@ -53,18 +47,7 @@ "XDAI", "xmark" ], - "dictionaries": [ - "typescript", - "node", - "software-terms", - "html" - ], - "import": [ - "@cspell/dict-typescript/cspell-ext.json", - "@cspell/dict-node/cspell-ext.json", - "@cspell/dict-software-terms" - ], - "ignoreRegExpList": [ - "[0-9a-fA-F]{6}" - ] + "dictionaries": ["typescript", "node", "software-terms", "html"], + "import": ["@cspell/dict-typescript/cspell-ext.json", "@cspell/dict-node/cspell-ext.json", "@cspell/dict-software-terms"], + "ignoreRegExpList": ["[0-9a-fA-F]{6}"] } diff --git a/.github/workflows/kebab-case.yml b/.github/workflows/kebab-case.yml deleted file mode 100644 index 1adda494..00000000 --- a/.github/workflows/kebab-case.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Enforce kebab-case - -on: - push: - pull_request: - -jobs: - check-filenames: - runs-on: ubuntu-latest - steps: - - name: Check out code - uses: actions/checkout@v4 - - - name: Check For Non Kebab-Cased TypeScript Files - run: .github/workflows/scripts/kebab-case.sh diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml deleted file mode 100644 index 5c626001..00000000 --- a/.github/workflows/release-please.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: release-please - -on: - workflow_dispatch: - push: - branches: - - main - -permissions: - contents: write - pull-requests: write - -jobs: - release-please: - runs-on: ubuntu-latest - steps: - - uses: google-github-actions/release-please-action@v3 - with: - release-type: node - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: "20.10.0" - registry-url: https://registry.npmjs.org/ - - run: | - yarn install --immutable --immutable-cache --check-cache diff --git a/.github/workflows/scripts/kebab-case.sh b/.github/workflows/scripts/kebab-case.sh deleted file mode 100755 index fa93d7fe..00000000 --- a/.github/workflows/scripts/kebab-case.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -non_compliant_files=() -ignoreList=("^\.\/.git" "^\.\/\..*" "^\.\/[^\/]*$") -ignoreList+=("^\.\/node_modules") -while IFS= read -r line; do -ignoreList+=(".*$line") -done < .gitignore -while read -r file; do -basefile=$(basename "$file") -ignoreFile=false -for pattern in "${ignoreList[@]}"; do - if [[ "$file" =~ $pattern ]]; then - ignoreFile=true - break - fi -done -if $ignoreFile; then - continue -elif ! echo "$basefile" | grep -q -E "^([a-z0-9]+-)*[a-z0-9]+(\.[a-zA-Z0-9]+)?$|^([a-z0-9]+_)*[a-z0-9]+(\.[a-zA-Z0-9]+)?$"; then - non_compliant_files+=("$file") - echo "::warning file=$file::This file is not in kebab-case or snake_case" -fi -done < <(find . -type f -name '*.ts' -print | grep -E '/[a-z]+[a-zA-Z]*\.ts$') -if [ ${#non_compliant_files[@]} -ne 0 ]; then -echo "The following files are not in kebab-case or snake_case:" -for file in "${non_compliant_files[@]}"; do - echo " - $file" -done -exit 1 -fi \ No newline at end of file diff --git a/.github/workflows/scripts/kebabalize.sh b/.github/workflows/scripts/kebabalize.sh deleted file mode 100755 index 47892d48..00000000 --- a/.github/workflows/scripts/kebabalize.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -non_compliant_files=() -ignoreList=("^\.\/.git" "^\.\/\..*" "^\.\/[^\/]*$") -while IFS= read -r line; do -ignoreList+=(".*$line") -done < .gitignore -while read -r file; do -basefile=$(basename "$file") -ignoreFile=false -for pattern in "${ignoreList[@]}"; do - if [[ "$file" =~ $pattern ]]; then - ignoreFile=true - break - fi -done -if $ignoreFile; then - continue -elif ! echo "$basefile" | grep -q -E "^([a-z0-9]+-)*[a-z0-9]+(\.[a-zA-Z0-9]+)?$|^([a-z0-9]+_)*[a-z0-9]+(\.[a-zA-Z0-9]+)?$"; then - non_compliant_files+=("$file") - echo "::warning file=$file::This file is not in kebab-case or snake_case" - newfile=$(dirname "$file")/$(echo "$basefile" | sed -r 's/([a-z0-9])([A-Z])/\1-\2/g' | tr '[:upper:]' '[:lower:]' | sed 's/_/-/g') - mv "$file" "$newfile" -fi -done < <(find . -type f -name '*.ts' -print | grep -E '/[a-z]+[a-zA-Z]*\.ts$') -if [ ${#non_compliant_files[@]} -ne 0 ]; then -echo "The following files are not in kebab-case or snake_case:" -for file in "${non_compliant_files[@]}"; do - echo " - $file" -done -exit 1 -fi \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 09880b1d..00000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: test - -on: workflow_dispatch - -env: - FOUNDRY_PROFILE: ci - -jobs: - check: - strategy: - fail-fast: true - - name: Foundry project - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 - with: - version: nightly - - - name: Run Forge build - run: | - forge --version - forge build --sizes - id: build - - - name: Run Forge tests - run: | - forge test -vvv - id: test diff --git a/package.json b/package.json index e558cef4..d6d7ba99 100644 --- a/package.json +++ b/package.json @@ -94,4 +94,4 @@ "@commitlint/config-conventional" ] } -} +} \ No newline at end of file diff --git a/scripts/solidity/test/Permit2.t.sol b/scripts/solidity/test/Permit2.t.sol deleted file mode 100644 index 1a1c3a57..00000000 --- a/scripts/solidity/test/Permit2.t.sol +++ /dev/null @@ -1,435 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Test.sol"; -import "forge-std/console.sol"; -import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import "permit2/src/Permit2.sol"; -import "permit2/src/interfaces/ISignatureTransfer.sol"; - -contract Permit2Test is Test { - bytes32 constant TOKEN_PERMISSIONS_TYPEHASH = - keccak256("TokenPermissions(address token,uint256 amount)"); - bytes32 constant PERMIT_TRANSFER_FROM_TYPEHASH = keccak256( - "PermitTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline)TokenPermissions(address token,uint256 amount)" - ); - - string testMnemonic = "whale pepper wink eight disease negative renew volume dream forest clean rent"; - - // DAI address - // mainnet: 0x6b175474e89094c44da98b954eedeac495271d0f - // goerli: 0x11fE4B6AE13d2a6055C8D9cF65c55bac32B5d844 - IERC20 daiContract = IERC20(0x11fE4B6AE13d2a6055C8D9cF65c55bac32B5d844); - - // the same address on mainnet and goerli - Permit2 permit2Contract = Permit2(0x000000000022D473030F116dDEE9F6B43aC78BA3); - - address botAddress = 0xa701216C86b1fFC1F0E4D592DA4186eD519eaDf9; - address userAddress = 0x398cb4c0a4821667373DDEB713dd3371c968460b; - address userAddress2 = 0xe3E77B89CCa2A37d98359A3463822e8e888EB363; - - uint botPrivateKey = vm.deriveKey(testMnemonic, 0); - - function setUp() public { - // use goerli fork - vm.selectFork( - vm.createFork("https://goerli.infura.io/v3/42c7a210df614077867503863d375617") - ); - // bot allows permit2 to spend 1k DAI (this operation should run only once) - vm.prank(botAddress); - daiContract.approve(address(permit2Contract), 1000e18); - } - - function testPermitTransferFrom() public { - // bot (or admin) creates a signature for bounty hunter - ISignatureTransfer.PermitTransferFrom memory permitTransferFromData = ISignatureTransfer.PermitTransferFrom({ - permitted: ISignatureTransfer.TokenPermissions({ - token: address(daiContract), - amount: 1e18 - }), - nonce: 0, - deadline: block.timestamp - }); - bytes memory sig = _signPermit(permitTransferFromData, userAddress, botPrivateKey); - - // bounty hunter's balance before - uint userBalanceBefore = daiContract.balanceOf(userAddress); - - // bounty hunter calls permitTransferFrom and transfers reward - ISignatureTransfer.SignatureTransferDetails memory transferDetails = ISignatureTransfer.SignatureTransferDetails({ - to: userAddress, - requestedAmount: 1e18 - }); - vm.prank(userAddress); - permit2Contract.permitTransferFrom(permitTransferFromData, transferDetails, botAddress, sig); - - // bounty hunter's balance after - uint userBalanceAfter = daiContract.balanceOf(userAddress); - assertEq(userBalanceAfter, userBalanceBefore + 1 ether); - } - - function testPermitTransferFrom_ShouldRevert_IfNonceHasAlreadyBeenUsed() public { - // bot (or admin) creates a signature for bounty hunter - ISignatureTransfer.PermitTransferFrom memory permitTransferFromData = ISignatureTransfer.PermitTransferFrom({ - permitted: ISignatureTransfer.TokenPermissions({ - token: address(daiContract), - amount: 1e18 - }), - nonce: 0, - deadline: block.timestamp - }); - bytes memory sig = _signPermit(permitTransferFromData, userAddress, botPrivateKey); - - // bounty hunter calls permitTransferFrom and transfers reward - ISignatureTransfer.SignatureTransferDetails memory transferDetails = ISignatureTransfer.SignatureTransferDetails({ - to: userAddress, - requestedAmount: 1e18 - }); - vm.prank(userAddress); - permit2Contract.permitTransferFrom(permitTransferFromData, transferDetails, botAddress, sig); - - // bounty hunter tries to execute the same tx again - vm.prank(userAddress); - vm.expectRevert(); - permit2Contract.permitTransferFrom(permitTransferFromData, transferDetails, botAddress, sig); - } - - function testPermitTransferFrom_ShouldRevert_IfNonceIsNotValid() public { - // bot (or admin) creates a signature for bounty hunter - ISignatureTransfer.PermitTransferFrom memory permitTransferFromData = ISignatureTransfer.PermitTransferFrom({ - permitted: ISignatureTransfer.TokenPermissions({ - token: address(daiContract), - amount: 1e18 - }), - nonce: 0, - deadline: block.timestamp - }); - bytes memory sig = _signPermit(permitTransferFromData, userAddress, botPrivateKey); - - // bounty hunter calls permitTransferFrom and transfers reward - ISignatureTransfer.SignatureTransferDetails memory transferDetails = ISignatureTransfer.SignatureTransferDetails({ - to: userAddress, - requestedAmount: 1e18 - }); - permitTransferFromData.nonce = 1; - vm.prank(userAddress); - vm.expectRevert(); - permit2Contract.permitTransferFrom(permitTransferFromData, transferDetails, botAddress, sig); - } - - function testPermitTransferFrom_ShouldRevert_IfUserModifiedPermitAmount() public { - // bot (or admin) creates a signature for bounty hunter - ISignatureTransfer.PermitTransferFrom memory permitTransferFromData = ISignatureTransfer.PermitTransferFrom({ - permitted: ISignatureTransfer.TokenPermissions({ - token: address(daiContract), - amount: 1e18 - }), - nonce: 0, - deadline: block.timestamp - }); - bytes memory sig = _signPermit(permitTransferFromData, userAddress, botPrivateKey); - - // bounty hunter calls permitTransferFrom and transfers reward - ISignatureTransfer.SignatureTransferDetails memory transferDetails = ISignatureTransfer.SignatureTransferDetails({ - to: userAddress, - requestedAmount: 1e18 - }); - permitTransferFromData.permitted.amount = 2e18; - vm.prank(userAddress); - vm.expectRevert(); - permit2Contract.permitTransferFrom(permitTransferFromData, transferDetails, botAddress, sig); - } - - function testPermitTransferFrom_ShouldRevert_IfUserTriesToExecuteNotHisTx() public { - // bot (or admin) creates a signature for bounty hunter - ISignatureTransfer.PermitTransferFrom memory permitTransferFromData = ISignatureTransfer.PermitTransferFrom({ - permitted: ISignatureTransfer.TokenPermissions({ - token: address(daiContract), - amount: 1e18 - }), - nonce: 0, - deadline: block.timestamp - }); - bytes memory sig = _signPermit(permitTransferFromData, userAddress, botPrivateKey); - - // bounty hunter calls permitTransferFrom and transfers reward - ISignatureTransfer.SignatureTransferDetails memory transferDetails = ISignatureTransfer.SignatureTransferDetails({ - to: userAddress, - requestedAmount: 1e18 - }); - vm.prank(userAddress2); - vm.expectRevert(); - permit2Contract.permitTransferFrom(permitTransferFromData, transferDetails, botAddress, sig); - } - - function testPermitTransferFrom_ShouldSendRewardToAnotherAddress() public { - // bot (or admin) creates a signature for bounty hunter - ISignatureTransfer.PermitTransferFrom memory permitTransferFromData = ISignatureTransfer.PermitTransferFrom({ - permitted: ISignatureTransfer.TokenPermissions({ - token: address(daiContract), - amount: 1e18 - }), - nonce: 0, - deadline: block.timestamp - }); - bytes memory sig = _signPermit(permitTransferFromData, userAddress, botPrivateKey); - - uint user2BalanceBefore = daiContract.balanceOf(userAddress2); - - // bounty hunter calls permitTransferFrom and transfers reward - ISignatureTransfer.SignatureTransferDetails memory transferDetails = ISignatureTransfer.SignatureTransferDetails({ - to: userAddress2, - requestedAmount: 1e18 - }); - vm.prank(userAddress); - permit2Contract.permitTransferFrom(permitTransferFromData, transferDetails, botAddress, sig); - - uint user2BalanceAfter = daiContract.balanceOf(userAddress2); - assertEq(user2BalanceAfter, user2BalanceBefore + 1 ether); - } - - function testPermitTransferFrom_ShouldSendHalfOfTheReward() public { - // bot (or admin) creates a signature for bounty hunter - ISignatureTransfer.PermitTransferFrom memory permitTransferFromData = ISignatureTransfer.PermitTransferFrom({ - permitted: ISignatureTransfer.TokenPermissions({ - token: address(daiContract), - amount: 1e18 - }), - nonce: 0, - deadline: block.timestamp - }); - bytes memory sig = _signPermit(permitTransferFromData, userAddress, botPrivateKey); - - uint userBalanceBefore = daiContract.balanceOf(userAddress); - - // bounty hunter calls permitTransferFrom and transfers reward - ISignatureTransfer.SignatureTransferDetails memory transferDetails = ISignatureTransfer.SignatureTransferDetails({ - to: userAddress, - requestedAmount: 0.5 ether - }); - vm.prank(userAddress); - permit2Contract.permitTransferFrom(permitTransferFromData, transferDetails, botAddress, sig); - - uint userBalanceAfter = daiContract.balanceOf(userAddress); - assertEq(userBalanceAfter, userBalanceBefore + 0.5 ether); - } - - function testPermitTransferFrom_ShouldRevert_IfRequestedAmountIsGreaterThanPermitted() public { - // bot (or admin) creates a signature for bounty hunter - ISignatureTransfer.PermitTransferFrom memory permitTransferFromData = ISignatureTransfer.PermitTransferFrom({ - permitted: ISignatureTransfer.TokenPermissions({ - token: address(daiContract), - amount: 1e18 - }), - nonce: 0, - deadline: block.timestamp - }); - bytes memory sig = _signPermit(permitTransferFromData, userAddress, botPrivateKey); - - // bounty hunter calls permitTransferFrom and transfers reward - ISignatureTransfer.SignatureTransferDetails memory transferDetails = ISignatureTransfer.SignatureTransferDetails({ - to: userAddress, - requestedAmount: 2 ether - }); - vm.prank(userAddress); - vm.expectRevert(); - permit2Contract.permitTransferFrom(permitTransferFromData, transferDetails, botAddress, sig); - } - - function testPermitTransferFrom_ShouldRevert_IfOwnerAddressIsInvalid() public { - // bot (or admin) creates a signature for bounty hunter - ISignatureTransfer.PermitTransferFrom memory permitTransferFromData = ISignatureTransfer.PermitTransferFrom({ - permitted: ISignatureTransfer.TokenPermissions({ - token: address(daiContract), - amount: 1e18 - }), - nonce: 0, - deadline: block.timestamp - }); - bytes memory sig = _signPermit(permitTransferFromData, userAddress, botPrivateKey); - - // bounty hunter calls permitTransferFrom and transfers reward - ISignatureTransfer.SignatureTransferDetails memory transferDetails = ISignatureTransfer.SignatureTransferDetails({ - to: userAddress, - requestedAmount: 1 ether - }); - vm.prank(userAddress); - vm.expectRevert(); - permit2Contract.permitTransferFrom(permitTransferFromData, transferDetails, userAddress, sig); - } - - function testPermitTransferFrom_ShouldRevert_IfUserTriesToUseSignatureFromAlreadyPaidBounty() public { - // bot (or admin) creates a signature for bounty hunter - ISignatureTransfer.PermitTransferFrom memory permitTransferFromData = ISignatureTransfer.PermitTransferFrom({ - permitted: ISignatureTransfer.TokenPermissions({ - token: address(daiContract), - amount: 1e18 - }), - nonce: 0, - deadline: block.timestamp - }); - bytes memory sig = _signPermit(permitTransferFromData, userAddress, botPrivateKey); - - // bounty hunter calls permitTransferFrom and transfers reward - ISignatureTransfer.SignatureTransferDetails memory transferDetails = ISignatureTransfer.SignatureTransferDetails({ - to: userAddress, - requestedAmount: 1 ether - }); - vm.prank(userAddress); - permit2Contract.permitTransferFrom(permitTransferFromData, transferDetails, botAddress, sig); - - // bot (or admin) creates a 2nd signature for bounty hunter - ISignatureTransfer.PermitTransferFrom memory permitTransferFromData2 = ISignatureTransfer.PermitTransferFrom({ - permitted: ISignatureTransfer.TokenPermissions({ - token: address(daiContract), - amount: 1e18 - }), - nonce: 1, - deadline: block.timestamp - }); - - // bounty hunter calls 2nd permitTransferFrom with the old signature - ISignatureTransfer.SignatureTransferDetails memory transferDetails2 = ISignatureTransfer.SignatureTransferDetails({ - to: userAddress, - requestedAmount: 1 ether - }); - vm.prank(userAddress); - vm.expectRevert(); - permit2Contract.permitTransferFrom(permitTransferFromData2, transferDetails2, botAddress, sig); - } - - function testPermitTransferFrom_ShouldRevert_IfUserTriesToUseSignatureFromNotYetPaidBounty() public { - // bot (or admin) creates a signature for bounty hunter - ISignatureTransfer.PermitTransferFrom memory permitTransferFromData = ISignatureTransfer.PermitTransferFrom({ - permitted: ISignatureTransfer.TokenPermissions({ - token: address(daiContract), - amount: 1e18 - }), - nonce: 0, - deadline: block.timestamp - }); - bytes memory sig = _signPermit(permitTransferFromData, userAddress, botPrivateKey); - - // bot (or admin) creates a 2nd signature for bounty hunter - ISignatureTransfer.PermitTransferFrom memory permitTransferFromData2 = ISignatureTransfer.PermitTransferFrom({ - permitted: ISignatureTransfer.TokenPermissions({ - token: address(daiContract), - amount: 1e18 - }), - nonce: 1, - deadline: block.timestamp - }); - - // bounty hunter calls 2nd permitTransferFrom with the signature from tx with nonce 0 - ISignatureTransfer.SignatureTransferDetails memory transferDetails2 = ISignatureTransfer.SignatureTransferDetails({ - to: userAddress, - requestedAmount: 1 ether - }); - vm.prank(userAddress); - vm.expectRevert(); - permit2Contract.permitTransferFrom(permitTransferFromData2, transferDetails2, botAddress, sig); - } - - function testPermitTransferFrom_ShouldRevert_IfNonceHasBeenInvalidated() public { - // nonce we are invalidating - uint nonce = 999; - - // bot (or admin) creates a signature for bounty hunter - ISignatureTransfer.PermitTransferFrom memory permitTransferFromData = ISignatureTransfer.PermitTransferFrom({ - permitted: ISignatureTransfer.TokenPermissions({ - token: address(daiContract), - amount: 1e18 - }), - nonce: nonce, - deadline: block.timestamp - }); - bytes memory sig = _signPermit(permitTransferFromData, userAddress, botPrivateKey); - - // check that nonce is not used - assertFalse(_isNonceUsed(botAddress, nonce)); - - // invalidate nonce - (uint wordPos, uint mask) = _getParamsForNonceInvalidation(botAddress, nonce); - vm.prank(botAddress); - permit2Contract.invalidateUnorderedNonces(wordPos, mask); - - // check that nonce is marked as used - assertTrue(_isNonceUsed(botAddress, nonce)); - - // bounty hunter calls permitTransferFrom and transfers reward - ISignatureTransfer.SignatureTransferDetails memory transferDetails = ISignatureTransfer.SignatureTransferDetails({ - to: userAddress, - requestedAmount: 1e18 - }); - vm.prank(userAddress); - vm.expectRevert(); - permit2Contract.permitTransferFrom(permitTransferFromData, transferDetails, botAddress, sig); - } - - /** - * Helper functions - */ - - // Generate a signature for a permit message. - function _signPermit( - ISignatureTransfer.PermitTransferFrom memory permit, - address spender, - uint256 signerKey - ) - internal - view - returns (bytes memory sig) - { - (uint8 v, bytes32 r, bytes32 s) = - vm.sign(signerKey, _getEIP712Hash(permit, spender)); - return abi.encodePacked(r, s, v); - } - - // Compute the EIP712 hash of the permit object. - // Normally this would be implemented off-chain. - function _getEIP712Hash(ISignatureTransfer.PermitTransferFrom memory permit, address spender) - internal - view - returns (bytes32 h) - { - return keccak256(abi.encodePacked( - "\x19\x01", - permit2Contract.DOMAIN_SEPARATOR(), - keccak256(abi.encode( - PERMIT_TRANSFER_FROM_TYPEHASH, - keccak256(abi.encode( - TOKEN_PERMISSIONS_TYPEHASH, - permit.permitted.token, - permit.permitted.amount - )), - spender, - permit.nonce, - permit.deadline - )) - )); - } - - // Checks whether a permit nonce is used - function _isNonceUsed(address from, uint nonce) internal returns (bool) { - // find word position (first 248 bits of nonce) - uint wordPos = uint248(nonce >> 8); - // find bit position in bitmap - uint bitPos = uint8(nonce); - // prepare a mask for target bit - uint256 bit = 1 << bitPos; - // get bitmap with a flipped bit - uint sourceBitmap = permit2Contract.nonceBitmap(from, wordPos); - uint256 flipped = sourceBitmap ^= bit; - // check if any bit has been updated - return flipped & bit == 0; - } - - // Returns params to be used in "SignatureTransfer.invalidateUnorderedNonces()" - function _getParamsForNonceInvalidation(address from, uint nonce) internal returns(uint256 wordPos, uint256 mask) { - wordPos = uint248(nonce >> 8); - uint bitPos = uint8(nonce); - uint256 bit = 1 << bitPos; - uint sourceBitmap = permit2Contract.nonceBitmap(from, wordPos); - mask = sourceBitmap | bit; - } -}