Biconomy SDK
Search…
⌃K

Base Wallet Contract

Derives

Functions

Constructor - NA

1. init()

method for initialising the smart account proxy with necessary states
function init(address _owner, address _entryPointAddress, address _handler)
public override initializer

2. handlePayment()

private method to be used to transfer ERC20 tokens from Smart wallet to refund receiver
function handlePayment(
uint256 gasUsed,
uint256 baseGas,
uint256 gasPrice,
uint256 tokenGasPriceFactor,
address gasToken,
address payable refundReceiver
) private nonReentrant returns (uint256 payment)

Parameters

Name
Type
Description
gasUsed
uint256
gas units to be accounted for fee calculation (passed on from main transactional method execTransaction)
baseGas
uint256
gas units to be accounted for other actions
gasPrice
uint256
gasPrice or tokenGasPrice
tokenGasPriceFactor
uint256
indicates decimals of fee token
gasToken
address
address of the gas token (0x for native)
refundReceiver
address
refund receiver

Return Values

Name
Type
Description
Payment
uint256
gas token refund amount

3. execTransaction()

Main method to dispatch transactions from Smart account to Dapp's contracts using owner/s signature. Gnosis style transaction with optional repay in native tokens OR ERC20
function execTransaction(
Transaction memory _tx,
uint256 batchId,
FeeRefund memory refundInfo,
bytes memory signatures
) public payable virtual override returns (bool success) {

Parameters

struct Transaction {
address to;
uint256 value;
bytes data;
Enum.Operation operation; // call or delegate call
uint256 targetTxGas; // gasLimit for internal transaction
}
struct FeeRefund {
uint256 baseGas;
uint256 gasPrice; //gasPrice or tokenGasPrice
uint256 tokenGasPriceFactor;
address gasToken;
address payable refundReceiver;
}
Name
Type
Description
_tx
Transaction
Smart Account transaction as described above
batchId
uint256
batchId freedom to use 2D nonces for parallel transactions in different nonce spaces
refundInfo
FeeRefund
as described above
signatures
bytes
required owner signature

Returns Success or Failure and Emits Following Events.

event ExecutionFailure(address to, uint256 value, bytes data, Enum.Operation operation, uint256 txGas);
event ExecutionSuccess(address to, uint256 value, bytes data, Enum.Operation operation, uint256 txGas);

4. setOwner()

Allows to rotate signing keys. Definition below
function setOwner(address _newOwner) external mixedAuth {
require(_newOwner != address(0), "Smart Account:: new Signatory address cannot be zero");
address oldOwner = owner;
owner = _newOwner;
emit EOAChanged(address(this), oldOwner, _newOwner);
}
// where mixedAuth is
// onlyOwner OR self (allows transaction from social recovery module via Guardians!)
modifier mixedAuth {
require(msg.sender == owner || msg.sender == address(this),"Only owner or self");
_;
}

5. updateEntryPoint()

Allows to update the set entry point this wallet accepts account abstraction transactions from
function updateEntryPoint(address _newEntryPoint) external mixedAuth {
require(_newEntryPoint != address(0), "Smart Account:: new entry point address cannot be zero");
emit EntryPointChanged(address(_entryPoint), _newEntryPoint);
_entryPoint = IEntryPoint(payable(_newEntryPoint));
}

6. checkSignatures()

Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise.
function checkSignatures(
bytes32 dataHash,
bytes memory data,
bytes memory signatures
) public view virtual

7. requiredTxGas()

Allows to estimate a transaction. This method is only meant for estimation purpose, therefore the call will always revert and encode the result in the revert data. Biconomy SDK backend has the API to estimate gas using above method
/// @param to Destination address of transaction.
/// @param value Ether value of transaction.
/// @param data Data payload of transaction.
/// @param operation Operation type of transaction.
/// @return Estimate without refunds and overhead fees (base transaction and payload data gas costs).
function requiredTxGas(
address to,
uint256 value,
bytes calldata data,
Enum.Operation operation
) external returns (uint256) {
uint256 startGas = gasleft();
// We don't provide an error message here, as we use it to return the estimate
require(execute(to, value, data, operation, gasleft()));
uint256 requiredGas = startGas - gasleft();
// Convert response to string and return via error message
revert(string(abi.encodePacked(requiredGas)));
}
Extra Util functions which current rotating key of Smart account can directly access and pay for gas.
function transfer(address payable dest, uint amount) external nonReentrant onlyOwner {
require(dest != address(0), "this action will burn your funds");
(bool success,) = dest.call{value:amount}("");
require(success,"transfer failed");
}
function pullTokens(address token, address dest, uint256 amount) external onlyOwner {
IERC20 tokenContract = IERC20(token);
SafeERC20.safeTransfer(tokenContract, dest, amount);
}
function exec(address dest, uint value, bytes calldata func) external onlyOwner{
_call(dest, value, func);
}
function execBatch(address[] calldata dest, bytes[] calldata func) external onlyOwner{
require(dest.length == func.length, "wrong array lengths");
for (uint i = 0; i < dest.length;) {
_call(dest[i], 0, func[i]);
unchecked {
++i;
}
}
}

8. execFromEntryPoint()

// Called by entryPoint, only after validateUserOp succeeded.
function execFromEntryPoint(address dest, uint value, bytes calldata func, Enum.Operation operation, uint256 gasLimit) external onlyEntryPoint returns (bool success) {
success = execute(dest, value, func, operation, gasLimit);
require(success, "Userop Failed");
}

9. validateUserOp()

Implements IWallet interface by EIP4337.
/**
* Validate user's signature and nonce.
*/
function validateUserOp(UserOperation calldata userOp, bytes32 requestId, address aggregator, uint256 missingWalletFunds) external override {
_requireFromEntryPoint();
_validateSignature(userOp, requestId, aggregator);
//during construction, the "nonce" field hold the salt.
// if we assert it is zero, then we allow only a single wallet per owner.
if (userOp.initCode.length == 0) {
_validateAndUpdateNonce(userOp);
}
_payPrefund(missingWalletFunds);
}
Breakdown of Implementation contract SmartWallet.sol is below Derives
contract SmartWallet is
Singleton,
BaseSmartWallet,
IERC165,
ModuleManager,solid
SignatureDecoder,
SecuredTokenTransfer,
ISignatureValidatorConstants,
FallbackManager,
Initializable,
ReentrancyGuardUpgradeable
{
....
//States
// Version
string public constant VERSION = "1.0.1"; // Forward enabled refund enhancements
// Domain Seperators
// keccak256(
// "EIP712Domain(uint256 chainId,address verifyingContract)"
// );
bytes32 internal constant DOMAIN_SEPARATOR_TYPEHASH = 0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218;
// keccak256(
// "WalletTx(address to,uint256 value,bytes data,uint8 operation,uint256 targetTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)"
// );
bytes32 internal constant WALLET_TX_TYPEHASH = 0xeedfef42e81fe8cd0e4185e4320e9f8d52fd97eb890b85fa9bd7ad97c9a18de2;
// Owner storage
address public owner;
// uint256 public nonce; //changed to 2D nonce below
mapping(uint256 => uint256) public nonces;
// AA storage
IEntryPoint private _entryPoint;
//Events
// Events
// EOA + Version tracking
event ImplementationUpdated(address _scw, string version, address newImplementation);
event EntryPointChanged(address oldEntryPoint, address newEntryPoint);
event EOAChanged(address indexed _scw, address indexed _oldEOA, address indexed _newEOA);
event WalletHandlePayment(bytes32 txHash, uint256 payment);
//Modifiers
// onlyOwner
/**
* @notice Throws if the sender is not an the owner.
*/
modifier onlyOwner {
require(msg.sender == owner, "Smart Account:: Sender is not authorized");
_;
}
// onlyOwner OR self
modifier mixedAuth {
require(msg.sender == owner || msg.sender == address(this),"Only owner or self");
_;
}
// only from EntryPoint
modifier onlyEntryPoint {
require(msg.sender == address(entryPoint()), "wallet: not from EntryPoint");
_;
}
//Getters
function nonce() public view virtual override returns (uint256) {
return nonces[0];
}
function entryPoint() public view virtual override returns (IEntryPoint) {
return _entryPoint;
}
....
}