Skip to content

Commit

Permalink
Update TBA Contract (#459)
Browse files Browse the repository at this point in the history
* wip

* wip

* delete commented out receive fn

* remove unnecessary register-with-factory

---------

Co-authored-by: Krishang <[email protected]>
  • Loading branch information
ciaranightingale and nkrishang authored Aug 17, 2023
1 parent f643686 commit c5903b2
Showing 1 changed file with 153 additions and 9 deletions.
162 changes: 153 additions & 9 deletions contracts/token-bound-account/TokenBoundAccount.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,42 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

/* solhint-disable avoid-low-level-calls */
/* solhint-disable no-inline-assembly */
/* solhint-disable reason-string */

// Base
import "../smart-wallet/utils/BaseAccount.sol";

// Extensions
import "../extension/Multicall.sol";
import "../dynamic-contracts/extension/Initializable.sol";
import "../dynamic-contracts/extension/ContractMetadata.sol";
import "../openzeppelin-presets/token/ERC721/utils/ERC721Holder.sol";
import "../openzeppelin-presets/token/ERC1155/utils/ERC1155Holder.sol";
import "../eip/ERC1271.sol";

// Utils
import "../openzeppelin-presets/utils/cryptography/ECDSA.sol";
import "../smart-wallet/utils/BaseAccountFactory.sol";

import "./erc6551-utils/ERC6551AccountLib.sol";
import "./erc6551-utils/IERC6551Account.sol";

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

contract TokenBoundAccount is Account, IERC6551Account {
contract TokenBoundAccount is
Initializable,
ERC1271,
Multicall,
BaseAccount,
ContractMetadata,
ERC721Holder,
ERC1155Holder,
IERC6551Account,
EIP712
{
using ECDSA for bytes32;

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

/*///////////////////////////////////////////////////////////////
Constructor
State
//////////////////////////////////////////////////////////////*/

/// @notice EIP 4337 factory for this contract.
address public immutable factory;

/// @notice EIP 4337 Entrypoint contract.
IEntryPoint private immutable entrypointContract;

/*///////////////////////////////////////////////////////////////
Constructor, Initializer, Modifiers
//////////////////////////////////////////////////////////////*/

/**
Expand All @@ -27,14 +66,22 @@ contract TokenBoundAccount is Account, IERC6551Account {
* @param _factory - The factory contract address to issue token Bound accounts
*
*/
constructor(IEntryPoint _entrypoint, address _factory) Account(_entrypoint, _factory) {
constructor(IEntryPoint _entrypoint, address _factory) EIP712("TokenBoundAccount", "1") {
_disableInitializers();
factory = _factory;
entrypointContract = _entrypoint;
}

receive() external payable override(IERC6551Account, Account) {}
// solhint-disable-next-line no-empty-blocks
receive() external payable virtual {}

/// @notice Initializes the smart contract wallet.
function initialize(address _defaultAdmin, bytes calldata _data) public virtual initializer {
emit TokenBoundAccountCreated(_defaultAdmin, _data);
}

/// @notice Returns whether a signer is authorized to perform transactions using the wallet.
function isValidSigner(address _signer, UserOperation calldata) public view override returns (bool) {
function isValidSigner(address _signer, UserOperation calldata) public view returns (bool) {
return (owner() == _signer);
}

Expand Down Expand Up @@ -70,7 +117,7 @@ contract TokenBoundAccount is Account, IERC6551Account {
}

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

/// @notice See {IERC165-supportsInterface}.
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC1155Receiver) returns (bool) {
return
interfaceId == type(IERC1155Receiver).interfaceId ||
interfaceId == type(IERC721Receiver).interfaceId ||
super.supportsInterface(interfaceId);
}

/// @notice Returns the EIP 4337 entrypoint contract.
function entryPoint() public view virtual override returns (IEntryPoint) {
return entrypointContract;
}

/// @notice Returns the balance of the account in Entrypoint.
function getDeposit() public view virtual returns (uint256) {
return entryPoint().balanceOf(address(this));
}

/// @notice Executes a transaction (called directly from an admin, or by entryPoint)
function execute(
address _target,
uint256 _value,
bytes calldata _calldata
) external virtual onlyAdminOrEntrypoint {
_call(_target, _value, _calldata);
}

/// @notice Executes a sequence transaction (called directly from an admin, or by entryPoint)
function executeBatch(
address[] calldata _target,
uint256[] calldata _value,
bytes[] calldata _calldata
) external virtual onlyAdminOrEntrypoint {
require(_target.length == _calldata.length && _target.length == _value.length, "Account: wrong array lengths.");
for (uint256 i = 0; i < _target.length; i++) {
_call(_target[i], _value[i], _calldata[i]);
}
}

/// @notice Deposit funds for this account in Entrypoint.
function addDeposit() public payable virtual {
entryPoint().depositTo{ value: msg.value }(address(this));
}

/*///////////////////////////////////////////////////////////////
Internal Functions
//////////////////////////////////////////////////////////////*/
Expand All @@ -99,7 +190,7 @@ contract TokenBoundAccount is Account, IERC6551Account {
address _target,
uint256 value,
bytes memory _calldata
) internal virtual override returns (bytes memory result) {
) internal virtual returns (bytes memory result) {
bool success;
(success, result) = _target.call{ value: value }(_calldata);
if (!success) {
Expand All @@ -109,12 +200,65 @@ contract TokenBoundAccount is Account, IERC6551Account {
}
}

/// @notice Validates the signature of a user operation.
function _validateSignature(UserOperation calldata userOp, bytes32 userOpHash)
internal
virtual
override
returns (uint256 validationData)
{
bytes32 hash = userOpHash.toEthSignedMessageHash();
address signer = hash.recover(userOp.signature);

if (!isValidSigner(signer, userOp)) return SIG_VALIDATION_FAILED;
return 0;
}

function getFunctionSignature(bytes calldata data) internal pure returns (bytes4 functionSelector) {
require(data.length >= 4, "Data too short");
return bytes4(data[:4]);
}

function decodeExecuteCalldata(bytes calldata data) internal pure returns (address _target, uint256 _value) {
require(data.length >= 4 + 32 + 32, "Data too short");

// Decode the address, which is bytes 4 to 35
_target = abi.decode(data[4:36], (address));

// Decode the value, which is bytes 36 to 68
_value = abi.decode(data[36:68], (uint256));
}

function decodeExecuteBatchCalldata(bytes calldata data)
internal
pure
returns (
address[] memory _targets,
uint256[] memory _values,
bytes[] memory _callData
)
{
require(data.length >= 4 + 32 + 32 + 32, "Data too short");

(_targets, _values, _callData) = abi.decode(data[4:], (address[], uint256[], bytes[]));
}

/// @dev Returns whether contract metadata can be set in the given execution context.
function _canSetContractURI() internal view virtual override returns (bool) {
return msg.sender == owner();
}

/*///////////////////////////////////////////////////////////////
Modifiers
//////////////////////////////////////////////////////////////*/

modifier onlyAdminOrEntrypoint() override {
modifier onlyAdminOrEntrypoint() {
require(msg.sender == address(entryPoint()) || msg.sender == owner(), "Account: not admin or EntryPoint.");
_;
}

modifier onlyAdmin() {
require(msg.sender == owner(), "Account: not admin.");
_;
}
}

0 comments on commit c5903b2

Please sign in to comment.