Skip to content

Commit c5903b2

Browse files
Update TBA Contract (#459)
* wip * wip * delete commented out receive fn * remove unnecessary register-with-factory --------- Co-authored-by: Krishang <krishang@thirdweb.com>
1 parent f643686 commit c5903b2

File tree

1 file changed

+153
-9
lines changed

1 file changed

+153
-9
lines changed

contracts/token-bound-account/TokenBoundAccount.sol

Lines changed: 153 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,42 @@
11
// SPDX-License-Identifier: UNLICENSED
22
pragma solidity ^0.8.0;
33

4+
/* solhint-disable avoid-low-level-calls */
5+
/* solhint-disable no-inline-assembly */
6+
/* solhint-disable reason-string */
7+
8+
// Base
9+
import "../smart-wallet/utils/BaseAccount.sol";
10+
11+
// Extensions
12+
import "../extension/Multicall.sol";
13+
import "../dynamic-contracts/extension/Initializable.sol";
14+
import "../dynamic-contracts/extension/ContractMetadata.sol";
15+
import "../openzeppelin-presets/token/ERC721/utils/ERC721Holder.sol";
16+
import "../openzeppelin-presets/token/ERC1155/utils/ERC1155Holder.sol";
17+
import "../eip/ERC1271.sol";
18+
19+
// Utils
20+
import "../openzeppelin-presets/utils/cryptography/ECDSA.sol";
21+
import "../smart-wallet/utils/BaseAccountFactory.sol";
22+
423
import "./erc6551-utils/ERC6551AccountLib.sol";
524
import "./erc6551-utils/IERC6551Account.sol";
625

726
import "../eip/interface/IERC721.sol";
8-
import "../smart-wallet/non-upgradeable/Account.sol";
27+
import "../openzeppelin-presets/utils/cryptography/EIP712.sol";
928

10-
contract TokenBoundAccount is Account, IERC6551Account {
29+
contract TokenBoundAccount is
30+
Initializable,
31+
ERC1271,
32+
Multicall,
33+
BaseAccount,
34+
ContractMetadata,
35+
ERC721Holder,
36+
ERC1155Holder,
37+
IERC6551Account,
38+
EIP712
39+
{
1140
using ECDSA for bytes32;
1241

1342
/*///////////////////////////////////////////////////////////////
@@ -17,7 +46,17 @@ contract TokenBoundAccount is Account, IERC6551Account {
1746
event TokenBoundAccountCreated(address indexed account, bytes indexed data);
1847

1948
/*///////////////////////////////////////////////////////////////
20-
Constructor
49+
State
50+
//////////////////////////////////////////////////////////////*/
51+
52+
/// @notice EIP 4337 factory for this contract.
53+
address public immutable factory;
54+
55+
/// @notice EIP 4337 Entrypoint contract.
56+
IEntryPoint private immutable entrypointContract;
57+
58+
/*///////////////////////////////////////////////////////////////
59+
Constructor, Initializer, Modifiers
2160
//////////////////////////////////////////////////////////////*/
2261

2362
/**
@@ -27,14 +66,22 @@ contract TokenBoundAccount is Account, IERC6551Account {
2766
* @param _factory - The factory contract address to issue token Bound accounts
2867
*
2968
*/
30-
constructor(IEntryPoint _entrypoint, address _factory) Account(_entrypoint, _factory) {
69+
constructor(IEntryPoint _entrypoint, address _factory) EIP712("TokenBoundAccount", "1") {
3170
_disableInitializers();
71+
factory = _factory;
72+
entrypointContract = _entrypoint;
3273
}
3374

34-
receive() external payable override(IERC6551Account, Account) {}
75+
// solhint-disable-next-line no-empty-blocks
76+
receive() external payable virtual {}
77+
78+
/// @notice Initializes the smart contract wallet.
79+
function initialize(address _defaultAdmin, bytes calldata _data) public virtual initializer {
80+
emit TokenBoundAccountCreated(_defaultAdmin, _data);
81+
}
3582

3683
/// @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) {
84+
function isValidSigner(address _signer, UserOperation calldata) public view returns (bool) {
3885
return (owner() == _signer);
3986
}
4087

@@ -70,7 +117,7 @@ contract TokenBoundAccount is Account, IERC6551Account {
70117
}
71118

72119
/// @notice Withdraw funds for this account from Entrypoint.
73-
function withdrawDepositTo(address payable withdrawAddress, uint256 amount) public virtual override {
120+
function withdrawDepositTo(address payable withdrawAddress, uint256 amount) public virtual {
74121
require(owner() == msg.sender, "Account: not NFT owner");
75122
entryPoint().withdrawTo(withdrawAddress, amount);
76123
}
@@ -91,6 +138,50 @@ contract TokenBoundAccount is Account, IERC6551Account {
91138
return getNonce();
92139
}
93140

141+
/// @notice See {IERC165-supportsInterface}.
142+
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC1155Receiver) returns (bool) {
143+
return
144+
interfaceId == type(IERC1155Receiver).interfaceId ||
145+
interfaceId == type(IERC721Receiver).interfaceId ||
146+
super.supportsInterface(interfaceId);
147+
}
148+
149+
/// @notice Returns the EIP 4337 entrypoint contract.
150+
function entryPoint() public view virtual override returns (IEntryPoint) {
151+
return entrypointContract;
152+
}
153+
154+
/// @notice Returns the balance of the account in Entrypoint.
155+
function getDeposit() public view virtual returns (uint256) {
156+
return entryPoint().balanceOf(address(this));
157+
}
158+
159+
/// @notice Executes a transaction (called directly from an admin, or by entryPoint)
160+
function execute(
161+
address _target,
162+
uint256 _value,
163+
bytes calldata _calldata
164+
) external virtual onlyAdminOrEntrypoint {
165+
_call(_target, _value, _calldata);
166+
}
167+
168+
/// @notice Executes a sequence transaction (called directly from an admin, or by entryPoint)
169+
function executeBatch(
170+
address[] calldata _target,
171+
uint256[] calldata _value,
172+
bytes[] calldata _calldata
173+
) external virtual onlyAdminOrEntrypoint {
174+
require(_target.length == _calldata.length && _target.length == _value.length, "Account: wrong array lengths.");
175+
for (uint256 i = 0; i < _target.length; i++) {
176+
_call(_target[i], _value[i], _calldata[i]);
177+
}
178+
}
179+
180+
/// @notice Deposit funds for this account in Entrypoint.
181+
function addDeposit() public payable virtual {
182+
entryPoint().depositTo{ value: msg.value }(address(this));
183+
}
184+
94185
/*///////////////////////////////////////////////////////////////
95186
Internal Functions
96187
//////////////////////////////////////////////////////////////*/
@@ -99,7 +190,7 @@ contract TokenBoundAccount is Account, IERC6551Account {
99190
address _target,
100191
uint256 value,
101192
bytes memory _calldata
102-
) internal virtual override returns (bytes memory result) {
193+
) internal virtual returns (bytes memory result) {
103194
bool success;
104195
(success, result) = _target.call{ value: value }(_calldata);
105196
if (!success) {
@@ -109,12 +200,65 @@ contract TokenBoundAccount is Account, IERC6551Account {
109200
}
110201
}
111202

203+
/// @notice Validates the signature of a user operation.
204+
function _validateSignature(UserOperation calldata userOp, bytes32 userOpHash)
205+
internal
206+
virtual
207+
override
208+
returns (uint256 validationData)
209+
{
210+
bytes32 hash = userOpHash.toEthSignedMessageHash();
211+
address signer = hash.recover(userOp.signature);
212+
213+
if (!isValidSigner(signer, userOp)) return SIG_VALIDATION_FAILED;
214+
return 0;
215+
}
216+
217+
function getFunctionSignature(bytes calldata data) internal pure returns (bytes4 functionSelector) {
218+
require(data.length >= 4, "Data too short");
219+
return bytes4(data[:4]);
220+
}
221+
222+
function decodeExecuteCalldata(bytes calldata data) internal pure returns (address _target, uint256 _value) {
223+
require(data.length >= 4 + 32 + 32, "Data too short");
224+
225+
// Decode the address, which is bytes 4 to 35
226+
_target = abi.decode(data[4:36], (address));
227+
228+
// Decode the value, which is bytes 36 to 68
229+
_value = abi.decode(data[36:68], (uint256));
230+
}
231+
232+
function decodeExecuteBatchCalldata(bytes calldata data)
233+
internal
234+
pure
235+
returns (
236+
address[] memory _targets,
237+
uint256[] memory _values,
238+
bytes[] memory _callData
239+
)
240+
{
241+
require(data.length >= 4 + 32 + 32 + 32, "Data too short");
242+
243+
(_targets, _values, _callData) = abi.decode(data[4:], (address[], uint256[], bytes[]));
244+
}
245+
246+
/// @dev Returns whether contract metadata can be set in the given execution context.
247+
function _canSetContractURI() internal view virtual override returns (bool) {
248+
return msg.sender == owner();
249+
}
250+
112251
/*///////////////////////////////////////////////////////////////
113252
Modifiers
114253
//////////////////////////////////////////////////////////////*/
115254

116-
modifier onlyAdminOrEntrypoint() override {
255+
modifier onlyAdminOrEntrypoint() {
117256
require(msg.sender == address(entryPoint()) || msg.sender == owner(), "Account: not admin or EntryPoint.");
118257
_;
119258
}
259+
260+
modifier onlyAdmin() {
261+
require(msg.sender == owner(), "Account: not admin.");
262+
_;
263+
}
120264
}

0 commit comments

Comments
 (0)