Account
import "@thirdweb-dev/contracts/smart-wallet/non-upgradeable/Account.sol";
This contract inherits from the BaseAccount
contract.
The Account smart wallet is a non-upgradable, simple smart wallet that comes with all the basic benefits of account abstraction, alongside the default features:
- Have multiple owners
- Execute transactions (single and batched).
- Send and receive native tokens.
- Send and receive ERC-721 and ERC-1155 NFTs.
- Multicall-able.
- Store contract metadata.
Developers should use this wallet if they do not anticipate making any future upgrades to their users’ wallets.
App developers can issue Account
smart wallets programmatically by deploying an AccountFactory
smart contract.
If you intend to issue accounts programmatically using a custom factory contract, you must do the following:
Create a new factory contract by extending the BaseAccountFactory extension.
Override the
_initializeAccount
function on the factory to create a new wallet. (reference)
Detected Extensions
Once deployed, you can use the features made available by these extensions on the SDK and dashboard:
Click on each feature to learn more about what functions are available.
Usage
Import the contract and inherit from it. This is an example contract demonstrating one way that you could override the functionality to create a token bound account.
import "@thirdweb-dev/contracts/smart-wallet/non-upgradable/Account.sol";
import "@thirdweb-dev/contracts/eip/interface/IERC721.sol";
contract TokenBoundAccount is Account {
uint256 chainId;
address tokenContract;
uint256 tokenId;
constructor(
IEntryPoint _entrypoint,
address _factory
) Account(_entrypoint, _factory) {
_disableInitializers();
}
function isValidSigner(
address _signer
) public view override returns (bool) {
return (isOwner(_signer));
}
function isOwner(address _signer) public view returns (bool) {
if (chainId != block.chainid) {
revert("Invalid chainId");
}
return _signer == IERC721(tokenContract).ownerOf(tokenId);
}
function initialize(
address _admin,
bytes calldata _data
) public override initializer {
super.initialize(_admin);
(chainId, tokenContract, tokenId) = abi.decode(
_data,
(uint256, address, uint256)
);
}
}
Functions to Override
The following functions have been implemented on this contract & are available to be overridden to add custom logic:
initialize
Initializes the smart contract wallet.
function initialize(address _defaultAdmin, bytes calldata _data) public virtual initializer {
_setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin);
}
_defaultAdmin
The account admin. Must be of type address
.
_data
The abi encoded data to initialize the contract with. Must be of type bytes
.
getImplementationForFunction
Returns the extension implementation address, stored in router, for the given function.
function getImplementationForFunction(bytes4 _functionSelector) public view virtual override returns (address) {
address impl = getExtensionForFunction(_functionSelector).implementation;
return impl != address(0) ? impl : defaultExtension;
}
_functionSelector
The function selector to get the extension implementation address. Must be of type bytes4
.
isValidSigner
Returns whether a signer is authorized to perform transactions using the wallet.
function isValidSigner(address _signer) public view virtual returns (bool) {
return _hasRole(SIGNER_ROLE, _signer) || _hasRole(DEFAULT_ADMIN_ROLE, _signer);
}
_signer
The signer to check authorization for. Must be of type address
.
entryPoint
Returns the entry point contract address.
function entryPoint() public view virtual override returns (IEntryPoint) {
return entrypointContract;
}
_canSetExtension
Returns whether an extension can be set in the given execution context.
function _canSetExtension() internal view virtual override returns (bool) {
return _hasRole(EXTENSION_ADMIN_ROLE, msg.sender);
}
_validateSignature
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)) return SIG_VALIDATION_FAILED;
return 0;
}