Skip to content

Commit a3c4c44

Browse files
add erc-6551 implementation contract (#442)
* add erc-6551 implementation contract * forge install: reference v0.2.0 * add erc6551 dependency * minor fixes * Rename to TokenBoundAccount * override isValidSignature EIP 1271 * Resolve dependency issues --------- Co-authored-by: Krishang <[email protected]>
1 parent 5d50aa0 commit a3c4c44

File tree

8 files changed

+294
-2
lines changed

8 files changed

+294
-2
lines changed

.gitmodules

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
[submodule "lib/ERC721A-Upgradeable"]
2020
path = lib/ERC721A-Upgradeable
2121
url = https://github.com/chiru-labs/ERC721A-Upgradeable
22+
branch = v3.3.0
2223
[submodule "lib/ERC721A"]
2324
path = lib/ERC721A
2425
url = https://github.com/chiru-labs/ERC721A
26+
branch = v3.3.0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// SPDX-License-Identifier: MIT
2+
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Create2.sol)
3+
4+
pragma solidity ^0.8.0;
5+
6+
/**
7+
* @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
8+
* `CREATE2` can be used to compute in advance the address where a smart
9+
* contract will be deployed, which allows for interesting new mechanisms known
10+
* as 'counterfactual interactions'.
11+
*
12+
* See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
13+
* information.
14+
*/
15+
library Create2 {
16+
/**
17+
* @dev Deploys a contract using `CREATE2`. The address where the contract
18+
* will be deployed can be known in advance via {computeAddress}.
19+
*
20+
* The bytecode for a contract can be obtained from Solidity with
21+
* `type(contractName).creationCode`.
22+
*
23+
* Requirements:
24+
*
25+
* - `bytecode` must not be empty.
26+
* - `salt` must have not been used for `bytecode` already.
27+
* - the factory must have a balance of at least `amount`.
28+
* - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
29+
*/
30+
function deploy(
31+
uint256 amount,
32+
bytes32 salt,
33+
bytes memory bytecode
34+
) internal returns (address) {
35+
address addr;
36+
require(address(this).balance >= amount, "Create2: insufficient balance");
37+
require(bytecode.length != 0, "Create2: bytecode length is zero");
38+
/// @solidity memory-safe-assembly
39+
assembly {
40+
addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
41+
}
42+
require(addr != address(0), "Create2: Failed on deploy");
43+
return addr;
44+
}
45+
46+
/**
47+
* @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
48+
* `bytecodeHash` or `salt` will result in a new destination address.
49+
*/
50+
function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
51+
return computeAddress(salt, bytecodeHash, address(this));
52+
}
53+
54+
/**
55+
* @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
56+
* `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
57+
*/
58+
function computeAddress(
59+
bytes32 salt,
60+
bytes32 bytecodeHash,
61+
address deployer
62+
) internal pure returns (address) {
63+
bytes32 _data = keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash));
64+
return address(uint160(uint256(_data)));
65+
}
66+
}

contracts/pack/PackVRFDirect.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol";
2323
import "@openzeppelin/contracts/interfaces/IERC721Receiver.sol";
2424
import { IERC1155Receiver } from "@openzeppelin/contracts/interfaces/IERC1155Receiver.sol";
2525

26-
import "@chainlink/contracts/src/v0.8/VRFV2WrapperConsumerBase.sol";
26+
import "@chainlink/contracts/src/v0.8/vrf/VRFV2WrapperConsumerBase.sol";
2727

2828
// ========== Internal imports ==========
2929

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.0;
3+
4+
import "./erc6551-utils/ERC6551AccountLib.sol";
5+
import "./erc6551-utils/IERC6551Account.sol";
6+
7+
import "../eip/interface/IERC721.sol";
8+
import "../smart-wallet/non-upgradeable/Account.sol";
9+
10+
contract TokenBoundAccount is Account, IERC6551Account {
11+
using ECDSA for bytes32;
12+
13+
/*///////////////////////////////////////////////////////////////
14+
Events
15+
//////////////////////////////////////////////////////////////*/
16+
17+
event TokenBoundAccountCreated(address indexed account, bytes indexed data);
18+
19+
/*///////////////////////////////////////////////////////////////
20+
Constructor
21+
//////////////////////////////////////////////////////////////*/
22+
23+
/**
24+
* @notice Executes once when a contract is created to initialize state variables
25+
*
26+
* @param _entrypoint - 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789
27+
* @param _factory - The factory contract address to issue token Bound accounts
28+
*
29+
*/
30+
constructor(IEntryPoint _entrypoint, address _factory) Account(_entrypoint, _factory) {
31+
_disableInitializers();
32+
}
33+
34+
receive() external payable override(IERC6551Account, Account) {}
35+
36+
/// @notice Returns whether a signer is authorized to perform transactions using the wallet.
37+
function isValidSigner(address _signer, UserOperation calldata) public view override returns (bool) {
38+
return (owner() == _signer);
39+
}
40+
41+
/// @notice See EIP-1271
42+
function isValidSignature(bytes32 _hash, bytes memory _signature)
43+
public
44+
view
45+
virtual
46+
override
47+
returns (bytes4 magicValue)
48+
{
49+
address signer = _hash.recover(_signature);
50+
51+
if (owner() == signer) {
52+
magicValue = MAGICVALUE;
53+
}
54+
}
55+
56+
function owner() public view returns (address) {
57+
(uint256 chainId, address tokenContract, uint256 tokenId) = ERC6551AccountLib.token();
58+
59+
if (chainId != block.chainid) return address(0);
60+
61+
return IERC721(tokenContract).ownerOf(tokenId);
62+
}
63+
64+
function executeCall(
65+
address to,
66+
uint256 value,
67+
bytes calldata data
68+
) external payable onlyAdminOrEntrypoint returns (bytes memory result) {
69+
return _call(to, value, data);
70+
}
71+
72+
/// @notice Withdraw funds for this account from Entrypoint.
73+
function withdrawDepositTo(address payable withdrawAddress, uint256 amount) public virtual override {
74+
require(owner() == msg.sender, "Account: not NFT owner");
75+
entryPoint().withdrawTo(withdrawAddress, amount);
76+
}
77+
78+
function token()
79+
external
80+
view
81+
returns (
82+
uint256 chainId,
83+
address tokenContract,
84+
uint256 tokenId
85+
)
86+
{
87+
return ERC6551AccountLib.token();
88+
}
89+
90+
function nonce() external view returns (uint256) {
91+
return getNonce();
92+
}
93+
94+
/*///////////////////////////////////////////////////////////////
95+
Internal Functions
96+
//////////////////////////////////////////////////////////////*/
97+
98+
function _call(
99+
address _target,
100+
uint256 value,
101+
bytes memory _calldata
102+
) internal virtual override returns (bytes memory result) {
103+
bool success;
104+
(success, result) = _target.call{ value: value }(_calldata);
105+
if (!success) {
106+
assembly {
107+
revert(add(result, 32), mload(result))
108+
}
109+
}
110+
}
111+
112+
/*///////////////////////////////////////////////////////////////
113+
Modifiers
114+
//////////////////////////////////////////////////////////////*/
115+
116+
modifier onlyAdminOrEntrypoint() override {
117+
require(msg.sender == address(entryPoint()) || msg.sender == owner(), "Account: not admin or EntryPoint.");
118+
_;
119+
}
120+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
import "../../openzeppelin-presets/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(implementation, chainId, tokenContract, tokenId, _salt)
18+
);
19+
20+
return Create2.computeAddress(bytes32(_salt), bytecodeHash, registry);
21+
}
22+
23+
function token()
24+
internal
25+
view
26+
returns (
27+
uint256,
28+
address,
29+
uint256
30+
)
31+
{
32+
bytes memory footer = new bytes(0x60);
33+
34+
assembly {
35+
// copy 0x60 bytes from end of footer
36+
extcodecopy(address(), add(footer, 0x20), 0x4d, 0xad)
37+
}
38+
39+
return abi.decode(footer, (uint256, address, uint256));
40+
}
41+
42+
function salt() internal view returns (uint256) {
43+
bytes memory footer = new bytes(0x20);
44+
45+
assembly {
46+
// copy 0x20 bytes from beginning of footer
47+
extcodecopy(address(), add(footer, 0x20), 0x2d, 0x4d)
48+
}
49+
50+
return abi.decode(footer, (uint256));
51+
}
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.0;
3+
4+
interface IERC6551AccountProxy {
5+
function implementation() external view returns (address);
6+
}
7+
8+
/// @dev the ERC-165 identifier for this interface is `0xeff4d378`
9+
interface IERC6551Account {
10+
event TransactionExecuted(address indexed target, uint256 indexed value, bytes data);
11+
12+
receive() external payable;
13+
14+
function executeCall(
15+
address to,
16+
uint256 value,
17+
bytes calldata data
18+
) external payable returns (bytes memory);
19+
20+
function token()
21+
external
22+
view
23+
returns (
24+
uint256 chainId,
25+
address tokenContract,
26+
uint256 tokenId
27+
);
28+
29+
function owner() external view returns (address);
30+
31+
function nonce() external view returns (uint256);
32+
}

lib/chainlink

Submodule chainlink updated 2366 files

0 commit comments

Comments
 (0)