Skip to main content

BaseAccount

import "@thirdweb-dev/contracts/smart-wallet/utils/BaseAccount.sol";

The BaseAccount smart contract is an extension that is intended to be used when creating your own smart account contract which does not use one of our ready-to-use base-contract account smart contracts.

It is abstract, allowing you to customize the functionality for your use case.

info

This smart contract extension conforms to the IAccount interface which is detectable on the dashboard as the SmartWallet extension.


Usage

The BaseAccount extension is an abstract contract, and expects you to implement the following functions by yourself:

NameTypeDescription
entryPointpublic viewReturn the current entryPoint used by this account.
_validateSignatureinternalValidate the signature is valid for this message.
_validateNonceinternalValidate the nonce of the UserOperation.

This is an example factory smart contract demonstrating how to inherit from this extension and override the functions to add (optional) custom functionality.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@thirdweb-dev/contracts/smart-wallet/utils/BaseAccountFactory.sol";

contract MyAccountFactory is BaseAccountFactory { ... }

SDK Usage

This extension unlocks the use of the smart wallet in the wallet SDK.

Base Contracts Implementing This Extension

Full API reference

getNonce

This method returns the next sequential nonce.

function getNonce() public view virtual returns (uint256) {
return entryPoint().getNonce(address(this), 0);
}
entryPoint

Subclass should return the current entryPoint used by this account.

function entryPoint() public view virtual returns (IEntryPoint);
validateUserOp

Validate user's signature and nonce.

Subclass doesn't need to override this method. Instead, it should override the specific internal validation methods.

function validateUserOp(
UserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
) external virtual override returns (uint256 validationData) {
_requireFromEntryPoint();
validationData = _validateSignature(userOp, userOpHash);
_validateNonce(userOp.nonce);
_payPrefund(missingAccountFunds);
}
_requireFromEntryPoint

Ensure the request comes from the known entrypoint.

function _requireFromEntryPoint() internal view virtual {
require(msg.sender == address(entryPoint()), "account: not from EntryPoint");
}
_validateSignature

Validate the signature is valid for this message.

function _validateSignature(UserOperation calldata userOp, bytes32 userOpHash)
internal
virtual
returns (uint256 validationData);

userOp

Validate the userOp.signature field

userOpHash

the hash of the request, to check the signature against (also hashes the entrypoint and chain id)

Return value

signature and time-range of this operation:

  • sigAuthorizer - 0 for valid signature, 1 to mark signature failure, otherwise, an address of an "authorizer" contract.
  • validUntil - last timestamp this operation is valid. 0 for "indefinite"
  • validAfter - first timestamp this operation is valid.
_validateNonce

Validate the nonce of the UserOperation. To limit the nonce to use sequenced UserOps only (no "out of order" UserOps):

  • require(nonce < type(uint64).max)

For a hypothetical account that requires the nonce to be out-of-order:

  • require(nonce & type(uint64).max == 0)

The actual nonce uniqueness is managed by the EntryPoint, and thus no other action is needed by the account itself.

function _validateNonce(uint256 nonce) internal view virtual {}

nonce

uint256 representing the nonce to validate.

_payPrefund

Sends to the entrypoint (msg.sender) the missing funds for this transaction. Subclass MAY override this method for better funds management

For example, send to the entryPoint more than the minimum required, so that in future transactions it will not be required to send again.

function _payPrefund(uint256 missingAccountFunds) internal virtual {
if (missingAccountFunds != 0) {
(bool success, ) = payable(msg.sender).call{ value: missingAccountFunds, gas: type(uint256).max }("");
(success);
//ignore failure (its EntryPoint's job to verify, not account.)
}
}

missingAccountFunds

The minimum value this method should send the entrypoint. This value MAY be zero, in case there is enough deposit, or the userOp has a paymaster.