fixed TBA 4fae5c79
Steve · 2023-10-09 13:37 11 file(s) · +317 −26
contracts/ERC6551Account.sol (added) +101 −0
1 +
// SPDX-License-Identifier: MIT
2 +
pragma solidity ^0.8.20;
3 +
4 +
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
5 +
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
6 +
import "@openzeppelin/contracts/interfaces/IERC1271.sol";
7 +
import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
8 +
9 +
import "./interfaces/IERC6551Account.sol";
10 +
import "./interfaces/IERC6551Executable.sol";
11 +
12 +
contract ERC6551Account is
13 +
    IERC165,
14 +
    IERC1271,
15 +
    IERC6551Account,
16 +
    IERC6551Executable
17 +
{
18 +
    uint256 public state;
19 +
20 +
    receive() external payable {}
21 +
22 +
    function execute(
23 +
        address to,
24 +
        uint256 value,
25 +
        bytes calldata data,
26 +
        uint256 operation
27 +
    ) public payable virtual returns (bytes memory result) {
28 +
        require(_isValidSigner(msg.sender), "Invalid signer");
29 +
        require(operation == 0, "Only call operations are supported");
30 +
31 +
        ++state;
32 +
33 +
        bool success;
34 +
        (success, result) = to.call{value: value}(data);
35 +
36 +
        if (!success) {
37 +
            assembly {
38 +
                revert(add(result, 32), mload(result))
39 +
            }
40 +
        }
41 +
    }
42 +
43 +
    function isValidSigner(
44 +
        address signer,
45 +
        bytes calldata
46 +
    ) public view virtual returns (bytes4) {
47 +
        if (_isValidSigner(signer)) {
48 +
            return IERC6551Account.isValidSigner.selector;
49 +
        }
50 +
51 +
        return bytes4(0);
52 +
    }
53 +
54 +
    function isValidSignature(
55 +
        bytes32 hash,
56 +
        bytes memory signature
57 +
    ) public view virtual returns (bytes4 magicValue) {
58 +
        bool isValid = SignatureChecker.isValidSignatureNow(
59 +
            owner(),
60 +
            hash,
61 +
            signature
62 +
        );
63 +
64 +
        if (isValid) {
65 +
            return IERC1271.isValidSignature.selector;
66 +
        }
67 +
68 +
        return "";
69 +
    }
70 +
71 +
    function supportsInterface(
72 +
        bytes4 interfaceId
73 +
    ) public pure virtual returns (bool) {
74 +
        return (interfaceId == type(IERC165).interfaceId ||
75 +
            interfaceId == type(IERC6551Account).interfaceId ||
76 +
            interfaceId == type(IERC6551Executable).interfaceId);
77 +
    }
78 +
79 +
    function token() public view virtual returns (uint256, address, uint256) {
80 +
        bytes memory footer = new bytes(0x60);
81 +
82 +
        assembly {
83 +
            extcodecopy(address(), add(footer, 0x20), 0x4d, 0x60)
84 +
        }
85 +
86 +
        return abi.decode(footer, (uint256, address, uint256));
87 +
    }
88 +
89 +
    function owner() public view virtual returns (address) {
90 +
        (uint256 chainId, address tokenContract, uint256 tokenId) = token();
91 +
        if (chainId != block.chainid) return address(0);
92 +
93 +
        return IERC721(tokenContract).ownerOf(tokenId);
94 +
    }
95 +
96 +
    function _isValidSigner(
97 +
        address signer
98 +
    ) internal view virtual returns (bool) {
99 +
        return signer == owner();
100 +
    }
101 +
}
contracts/ERC6551Registry.sol (added) +82 −0
1 +
// SPDX-License-Identifier: MIT
2 +
pragma solidity ^0.8.20;
3 +
4 +
import "@openzeppelin/contracts/utils/Create2.sol";
5 +
6 +
import "./interfaces/IERC6551Registry.sol";
7 +
import "./lib/ERC6551BytecodeLib.sol";
8 +
9 +
contract ERC6551Registry is IERC6551Registry {
10 +
    error AccountCreationFailed();
11 +
12 +
    function createAccount(
13 +
        address implementation,
14 +
        uint256 chainId,
15 +
        address tokenContract,
16 +
        uint256 tokenId,
17 +
        uint256 salt,
18 +
        bytes calldata initData
19 +
    ) external returns (address) {
20 +
        bytes memory code = ERC6551BytecodeLib.getCreationCode(
21 +
            implementation,
22 +
            chainId,
23 +
            tokenContract,
24 +
            tokenId,
25 +
            salt
26 +
        );
27 +
28 +
        address _account = Create2.computeAddress(
29 +
            bytes32(salt),
30 +
            keccak256(code)
31 +
        );
32 +
33 +
        if (_account.code.length != 0) return _account;
34 +
35 +
        emit AccountCreated(
36 +
            _account,
37 +
            implementation,
38 +
            chainId,
39 +
            tokenContract,
40 +
            tokenId,
41 +
            salt
42 +
        );
43 +
44 +
        assembly {
45 +
            _account := create2(0, add(code, 0x20), mload(code), salt)
46 +
        }
47 +
48 +
        if (_account == address(0)) revert AccountCreationFailed();
49 +
50 +
        if (initData.length != 0) {
51 +
            (bool success, bytes memory result) = _account.call(initData);
52 +
53 +
            if (!success) {
54 +
                assembly {
55 +
                    revert(add(result, 32), mload(result))
56 +
                }
57 +
            }
58 +
        }
59 +
60 +
        return _account;
61 +
    }
62 +
63 +
    function account(
64 +
        address implementation,
65 +
        uint256 chainId,
66 +
        address tokenContract,
67 +
        uint256 tokenId,
68 +
        uint256 salt
69 +
    ) external view returns (address) {
70 +
        bytes32 bytecodeHash = keccak256(
71 +
            ERC6551BytecodeLib.getCreationCode(
72 +
                implementation,
73 +
                chainId,
74 +
                tokenContract,
75 +
                tokenId,
76 +
                salt
77 +
            )
78 +
        );
79 +
80 +
        return Create2.computeAddress(bytes32(salt), bytecodeHash);
81 +
    }
82 +
}
contracts/IERC6551Account.sol → contracts/interfaces/IERC6551Account.sol +0 −0
contracts/IERC6551Executable.sol → contracts/interfaces/IERC6551Executable.sol +0 −0
contracts/IERC6551Registry.sol → contracts/interfaces/IERC6551Registry.sol +0 −0
contracts/Operator.sol (added) +7 −0
1 +
// SPDX-License-Identifier: MIT
2 +
pragma solidity ^0.8.20;
3 +
4 +
import "./CosmicCowboy.sol";
5 +
import "./SpaceGrub.sol";
6 +
import "./RocketFuel.sol";
7 +
import "./GoldenCorn.sol";
contracts/lib/ERC6551AccountLib.sol (added) +50 −0
1 +
// SPDX-License-Identifier: MIT
2 +
pragma solidity ^0.8.20;
3 +
4 +
import "@openzeppelin/contracts/utils/Create2.sol";
5 +
import "./ERC6551BytecodeLib.sol";
6 +
7 +
library ERC6551AccountLib {
8 +
    function computeAddress(
9 +
        address registry,
10 +
        address implementation,
11 +
        uint256 chainId,
12 +
        address tokenContract,
13 +
        uint256 tokenId,
14 +
        uint256 _salt
15 +
    ) internal pure returns (address) {
16 +
        bytes32 bytecodeHash = keccak256(
17 +
            ERC6551BytecodeLib.getCreationCode(
18 +
                implementation,
19 +
                chainId,
20 +
                tokenContract,
21 +
                tokenId,
22 +
                _salt
23 +
            )
24 +
        );
25 +
26 +
        return Create2.computeAddress(bytes32(_salt), bytecodeHash, registry);
27 +
    }
28 +
29 +
    function token() internal view returns (uint256, address, uint256) {
30 +
        bytes memory footer = new bytes(0x60);
31 +
32 +
        assembly {
33 +
            // copy 0x60 bytes from end of footer
34 +
            extcodecopy(address(), add(footer, 0x20), 0x4d, 0x60)
35 +
        }
36 +
37 +
        return abi.decode(footer, (uint256, address, uint256));
38 +
    }
39 +
40 +
    function salt() internal view returns (uint256) {
41 +
        bytes memory footer = new bytes(0x20);
42 +
43 +
        assembly {
44 +
            // copy 0x20 bytes from beginning of footer
45 +
            extcodecopy(address(), add(footer, 0x20), 0x2d, 0x20)
46 +
        }
47 +
48 +
        return abi.decode(footer, (uint256));
49 +
    }
50 +
}
contracts/lib/ERC6551BytecodeLib.sol (added) +20 −0
1 +
// SPDX-License-Identifier: MIT
2 +
pragma solidity ^0.8.20;
3 +
4 +
library ERC6551BytecodeLib {
5 +
    function getCreationCode(
6 +
        address implementation_,
7 +
        uint256 chainId_,
8 +
        address tokenContract_,
9 +
        uint256 tokenId_,
10 +
        uint256 salt_
11 +
    ) internal pure returns (bytes memory) {
12 +
        return
13 +
            abi.encodePacked(
14 +
                hex"3d60ad80600a3d3981f3363d3d373d3d3d363d73",
15 +
                implementation_,
16 +
                hex"5af43d82803e903d91602b57fd5bf3",
17 +
                abi.encode(salt_, chainId_, tokenContract_, tokenId_)
18 +
            );
19 +
    }
20 +
}
package-lock.json +7 −21
6 6
    "": {
7 7
      "name": "hardhat-project",
8 8
      "dependencies": {
9 -
        "@openzeppelin/contracts": "^5.0.0"
9 +
        "@openzeppelin/contracts": "^5.0.0",
10 +
        "ethers": "^6.7.1"
10 11
      },
11 12
      "devDependencies": {
12 13
        "@nomicfoundation/hardhat-toolbox": "^3.0.0",
17 18
    "node_modules/@adraffy/ens-normalize": {
18 19
      "version": "1.9.2",
19 20
      "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.9.2.tgz",
20 -
      "integrity": "sha512-0h+FrQDqe2Wn+IIGFkTCd4aAwTJ+7834Ek1COohCyV26AXhwQ7WQaz+4F/nLOeVl/3BtWHOHLPsq46V8YB46Eg==",
21 -
      "dev": true,
22 -
      "peer": true
21 +
      "integrity": "sha512-0h+FrQDqe2Wn+IIGFkTCd4aAwTJ+7834Ek1COohCyV26AXhwQ7WQaz+4F/nLOeVl/3BtWHOHLPsq46V8YB46Eg=="
23 22
    },
24 23
    "node_modules/@chainsafe/as-sha256": {
25 24
      "version": "0.3.1",
955 954
      "version": "1.1.2",
956 955
      "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz",
957 956
      "integrity": "sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==",
958 -
      "dev": true,
959 957
      "funding": [
960 958
        {
961 959
          "type": "individual",
962 960
          "url": "https://paulmillr.com/funding/"
963 961
        }
964 -
      ],
965 -
      "peer": true
962 +
      ]
966 963
    },
967 964
    "node_modules/@noble/secp256k1": {
968 965
      "version": "1.7.1",
969 966
      "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz",
970 967
      "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==",
971 -
      "dev": true,
972 968
      "funding": [
973 969
        {
974 970
          "type": "individual",
2066 2062
    "node_modules/aes-js": {
2067 2063
      "version": "4.0.0-beta.5",
2068 2064
      "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz",
2069 -
      "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==",
2070 -
      "dev": true,
2071 -
      "peer": true
2065 +
      "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q=="
2072 2066
    },
2073 2067
    "node_modules/agent-base": {
2074 2068
      "version": "6.0.2",
3466 3460
      "version": "6.7.1",
3467 3461
      "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.7.1.tgz",
3468 3462
      "integrity": "sha512-qX5kxIFMfg1i+epfgb0xF4WM7IqapIIu50pOJ17aebkxxa4BacW5jFrQRmCJpDEg2ZK2oNtR5QjrQ1WDBF29dA==",
3469 -
      "dev": true,
3470 3463
      "funding": [
3471 3464
        {
3472 3465
          "type": "individual",
3477 3470
          "url": "https://www.buymeacoffee.com/ricmoo"
3478 3471
        }
3479 3472
      ],
3480 -
      "peer": true,
3481 3473
      "dependencies": {
3482 3474
        "@adraffy/ens-normalize": "1.9.2",
3483 3475
        "@noble/hashes": "1.1.2",
3494 3486
    "node_modules/ethers/node_modules/@types/node": {
3495 3487
      "version": "18.15.13",
3496 3488
      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz",
3497 -
      "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==",
3498 -
      "dev": true,
3499 -
      "peer": true
3489 +
      "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q=="
3500 3490
    },
3501 3491
    "node_modules/ethjs-unit": {
3502 3492
      "version": "0.1.6",
6807 6797
    "node_modules/tslib": {
6808 6798
      "version": "2.4.0",
6809 6799
      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
6810 -
      "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
6811 -
      "dev": true,
6812 -
      "peer": true
6800 +
      "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
6813 6801
    },
6814 6802
    "node_modules/tsort": {
6815 6803
      "version": "0.0.1",
7262 7250
      "version": "8.5.0",
7263 7251
      "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
7264 7252
      "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
7265 -
      "dev": true,
7266 -
      "peer": true,
7267 7253
      "engines": {
7268 7254
        "node": ">=10.0.0"
7269 7255
      },
package.json +2 −1
6 6
    "mocha": "^10.2.0"
7 7
  },
8 8
  "dependencies": {
9 -
    "@openzeppelin/contracts": "^5.0.0"
9 +
    "@openzeppelin/contracts": "^5.0.0",
10 +
    "ethers": "^6.7.1"
10 11
  }
11 12
}
test/Test.js +48 −4
1 -
const { expect } = require("chai");
2 -
3 1
// Import the ethers library
4 2
const { ethers } = require("hardhat");
5 3
6 -
async function runTests() {
4 +
async function CosmicCoyboys() {
7 5
  let contract;
8 6
  let owner;
9 7
  let addr1;
28 26
  console.log(tokenURI);
29 27
};
30 28
31 -
runTests()
29 +
async function GoldenCorn() {
30 +
  let contract;
31 +
  let owner;
32 +
  let addr1;
33 +
  let addr2;
34 +
35 +
  // Get the signers from ethers
36 +
  [owner, addr1, addr2] = await ethers.getSigners();
37 +
38 +
  // Deploy the contract
39 +
  const Contract = await ethers.getContractFactory("GoldenCorn");
40 +
  contract = await Contract.deploy(owner.address);
41 +
  const contractAddress = await contract.getAddress()
42 +
  console.log("Contract deployed to address:", contractAddress);
43 +
44 +
  // Mint tokens
45 +
  await contract.mint(addr1.address, 100);
46 +
  console.log("Minted token 1");
47 +
48 +
  const balance = await contract.balanceOf(addr1.address);
49 +
  console.log(balance)
50 +
51 +
}
52 +
53 +
async function TBA() {
54 +
  let contract;
55 +
  let owner;
56 +
  let addr1;
57 +
  let addr2;
58 +
59 +
  // Get the signers from ethers
60 +
  [owner, addr1, addr2] = await ethers.getSigners();
61 +
62 +
  // Deploy the contract
63 +
  const RegistryContract = await ethers.getContractFactory("ERC6551Registry");
64 +
  const regristryContract = await RegistryContract.deploy();
65 +
  const registryContractAddress = await regristryContract.getAddress()
66 +
  console.log("Registry Contract deployed to address:", registryContractAddress);
67 +
68 +
  const AccountContract = await ethers.getContractFactory("ERC6551Account");
69 +
  const accountContract = await AccountContract.deploy();
70 +
  const accountContractAddress = await accountContract.getAddress()
71 +
  console.log("Account Contract deployed to address:", accountContractAddress);
72 +
73 +
}
74 +
75 +
TBA()