Contents

IAllowanceHolder

Git Source

Author: Ultrasound Labs

Interface for AllowanceHolder contracts which can pull tokens from an owner according to an allowance granted off-chain (e.g. via ERC-20 permit).

Functions

pull

Transfers amount of token() from the owner() directly to subaccount.

Can only be called by allowanceManager().

function pull(address subaccount, uint256 amount) external;

Parameters

NameTypeDescription
subaccountaddressThe recipient Safe (subaccount).
amountuint256Amount of tokens to transfer.

owner

Returns the token owner whose funds can be pulled.

function owner() external view returns (address ownerAddr);

Returns

NameTypeDescription
ownerAddraddressThe owner address.

token

Returns the ERC-20 token managed by this holder.

function token() external view returns (address tokenAddr);

Returns

NameTypeDescription
tokenAddraddressThe ERC-20 token address.

allowanceManager

Returns the AllowanceManager that is authorised to call pull.

function allowanceManager() external view returns (address manager);

Returns

NameTypeDescription
manageraddressThe manager address.

IAllowanceManager

Git Source

Author: Ultrasound Labs

Interface for the AllowanceManager contract responsible for time-boxed pull-allowances that subaccounts (Safes) can spend. It supports batch EIP-712(x) signing across chains.

Functions

bootstrapAllowance

Bootstrap an allowance once immediately after AllowanceHolder deployment.

Can only be called by the canonical AllowanceHolder for (owner, token) and only when nonce == 0. After a successful call, allowanceNonces[owner][subaccount][token] will be incremented to 1 so that any subsequent changes require a signed setAllowances().

function bootstrapAllowance(address owner, address subaccount, address token, uint256 amount, uint256 timeframe)
    external;

Parameters

NameTypeDescription
owneraddressThe owner of the allowance (signer of the permit given to the holder).
subaccountaddressSubaccount that the allowance applies to.
tokenaddressERC-20 token address.
amountuint256Allowance amount.
timeframeuint256Time window in seconds for the allowance resets.

commitHolder

Commits the expected AllowanceHolder address for an (owner, token) pair before deployment.

Must be called exactly once per (owner, token) pair and before the first bootstrapAllowance call. The first bootstrap must come from the committed holder; after a successful bootstrap the commit is cleared so future holders (for different subaccounts) can be deployed without an extra commit.

Access control: This function MAY only be called by the canonical SubaccountFactory (address set in the AllowanceManager constructor). Any call from another address MUST revert.

function commitHolder(address owner, address token, address holder) external;

Parameters

NameTypeDescription
owneraddressThe token owner that will grant allowance.
tokenaddressThe ERC-20 token the holder will manage.
holderaddressThe deterministic address where the AllowanceHolder will be deployed (CREATE2 pre-image).

registerHolder

Registers an additional AllowanceHolder address after the canonical one has been initialised.

Must be called by the canonical SubaccountFactory before the new holder constructor executes so that the holder can successfully call bootstrapAllowance. Skips commit-reveal checks and only pre-authorises the address.

function registerHolder(address holder) external;

Parameters

NameTypeDescription
holderaddressThe deterministic address of the new AllowanceHolder.

setAllowances

Sets allowances for a batch of subaccounts

function setAllowances(address owner, AllowanceRequestBatch calldata requests, bytes calldata signature) external;

Parameters

NameTypeDescription
owneraddressThe owner of the subaccounts
requestsAllowanceRequestBatchThe AllowanceRequestBatch struct containing the requests
signaturebytesThe EIP-712x (EIP-712 with chainId = 1) signature of the owner

spendAllowance

Spends an allowance for the caller

function spendAllowance(address owner, address token, uint256 amount) external;

Parameters

NameTypeDescription
owneraddressThe owner of the subaccount
tokenaddressThe token to spend the allowance for
amountuint256The amount to spend

holderAddress

Deterministically computes the canonical AllowanceHolder address for a pair (owner, token).

function holderAddress(address owner, address token) external view returns (address holder);

Parameters

NameTypeDescription
owneraddressThe token owner.
tokenaddressThe ERC-20 token.

Returns

NameTypeDescription
holderaddressThe predicted holder address (or address(0) if unknown).

allowances

Mapping to track allowances for subaccounts.

function allowances(address owner, address subaccount, address token) external view returns (Allowance memory);

Parameters

NameTypeDescription
owneraddressThe owner of the subaccount.
subaccountaddressThe subaccount that the allowance is set for.
tokenaddressThe token that the allowance is set for.

Returns

NameTypeDescription
<none>Allowanceallowance The allowance struct.

allowanceNonces

Mapping to track allowance nonces for subaccounts.

function allowanceNonces(address owner, address subaccount, address token) external view returns (uint256 nonce);

Parameters

NameTypeDescription
owneraddressThe owner of the subaccount.
subaccountaddressThe subaccount that the allowance is set for.
tokenaddressThe token that the allowance is set for.

Returns

NameTypeDescription
nonceuint256The nonce of the allowance.

permit2

The Permit2 contract address

function permit2() external view returns (address permit2Addr);

Returns

NameTypeDescription
permit2AddraddressThe Permit2 contract address.

allowanceRequestTypehash

EIP-712(x) typehash for the AllowanceRequest struct (per-subaccount)

function allowanceRequestTypehash() external pure returns (bytes32 typeHash);

Returns

NameTypeDescription
typeHashbytes32The struct type-hash.

allowanceRequestBatchTypehash

EIP-712(x) typehash for the AllowanceRequestBatch struct (per-subaccount)

function allowanceRequestBatchTypehash() external pure returns (bytes32 typeHash);

Returns

NameTypeDescription
typeHashbytes32The struct type-hash.

hashAllowanceRequestBatch

Computes the EIP-712 struct hash for a batch of allowance requests.

function hashAllowanceRequestBatch(AllowanceRequestBatch calldata batch) external pure returns (bytes32 structHash);

Parameters

NameTypeDescription
batchAllowanceRequestBatchThe AllowanceRequestBatch struct to hash.

Returns

NameTypeDescription
structHashbytes32The EIP-712 struct hash.

Events

AllowanceSet

Emitted when a new allowance is set.

event AllowanceSet(
    address indexed owner, address indexed subaccount, address indexed token, uint256 amount, uint256 timeframe
);

Parameters

NameTypeDescription
owneraddressThe owner of the subaccount.
subaccountaddressThe subaccount that the allowance is set for.
tokenaddressThe token that the allowance is set for.
amountuint256The amount of the allowance.
timeframeuint256The timeframe of the allowance.

AllowanceSpent

Emitted when an allowance is spent.

event AllowanceSpent(address indexed owner, address indexed subaccount, address indexed token, uint256 amount);

Parameters

NameTypeDescription
owneraddressThe owner of the subaccount.
subaccountaddressThe subaccount that the allowance is spent for.
tokenaddressThe token that the allowance is spent for.
amountuint256The amount of the allowance that was spent.

Errors

AllowanceExceeded

Emitted when an allowance is exceeded.

error AllowanceExceeded();

Structs

AllowanceRequest

Struct for a single allowance request.

struct AllowanceRequest {
    address subaccount;
    address token;
    uint256 amount;
    uint256 timeframe;
    uint256 nonce;
}

AllowanceRequestBatch

Struct for a batch of allowance requests.

struct AllowanceRequestBatch {
    AllowanceRequest[] requests;
    uint256[] chainIds;
}

Allowance

Struct for an allowance.

struct Allowance {
    uint256 amount;
    uint256 timeframe;
    uint256 spent;
    uint256 resetsAt;
}

ISafeTx

Git Source

Author: Ultrasound Labs

Interface that exposes the SafeTx struct used across the project for meta-transactions.

Structs

SafeTx

struct SafeTx {
    address to;
    uint256 value;
    bytes data;
    uint8 operation;
    uint256 safeTxGas;
    uint256 baseGas;
    uint256 gasPrice;
    address gasToken;
    address refundReceiver;
    uint256 nonce;
}

Contents

LengthMismatch

Git Source

Centralized custom errors shared across multiple contracts.

Thrown when two arrays expected to be equal in length differ.

This file defines reusable error types to avoid duplicate declarations across source files.

error LengthMismatch();

EIP712x

Git Source

Authors: Ultrasound Labs (https://github.com/ultrasoundlabs), Modified from Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol), Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol), Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol)

Modified EIP-712 contract with chainId hardcoded to 1 for multi-chain signatures.

WARNING: If you are using this for multi-chain signatures, please make sure that the payload you sign handles chainId-based replay protection internally! EIP712x removes this replay protection specifically so that payloads handle it on their end.

State Variables

_HARDCODED_CHAIN_ID

We hardcode chainId to 1 so that signatures generated for Ethereum Mainnet can be verified on other chains using this verifier contract. Why 1? - Because all EVM wallets have Ethereum Mainnet supported by default.

uint256 public constant _HARDCODED_CHAIN_ID = 1;

_DOMAIN_TYPEHASH

keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)").

bytes32 internal constant _DOMAIN_TYPEHASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;

_DOMAIN_TYPEHASH_SANS_CHAIN_ID

keccak256("EIP712Domain(string name,string version,address verifyingContract)"). This is only used in _hashTypedDataSansChainId.

bytes32 internal constant _DOMAIN_TYPEHASH_SANS_CHAIN_ID =
    0x91ab3d17e3a50a9d89e63fd30b92be7f5336b03b287bb946787a83a9d62a2766;

_DOMAIN_TYPEHASH_SANS_CHAIN_ID_AND_VERIFYING_CONTRACT

keccak256("EIP712Domain(string name,string version)"). This is only used in _hashTypedDataSansChainIdAndVerifyingContract.

bytes32 internal constant _DOMAIN_TYPEHASH_SANS_CHAIN_ID_AND_VERIFYING_CONTRACT =
    0xb03948446334eb9b2196d5eb166f69b9d49403eb4a12f36de8d3f9f3cb8e15c3;

_DOMAIN_TYPEHASH_SANS_VERIFYING_CONTRACT

keccak256("EIP712Domain(string name,string version,uint256 chainId)"). This is only used in _hashTypedDataSansVerifyingContract.

bytes32 internal constant _DOMAIN_TYPEHASH_SANS_VERIFYING_CONTRACT =
    0xc2f8787176b8ac6bf7215b4adcc1e069bf4ab82d9ab1df05a57a91d425935b6e;

_cachedThis

uint256 private immutable _cachedThis;

_cachedChainId

uint256 private immutable _cachedChainId;

_cachedNameHash

bytes32 private immutable _cachedNameHash;

_cachedVersionHash

bytes32 private immutable _cachedVersionHash;

_cachedDomainSeparator

bytes32 private immutable _cachedDomainSeparator;

Functions

constructor

Cache the hashes for cheaper runtime gas costs. In the case of upgradeable contracts (i.e. proxies), or if the chain id changes due to a hard fork, the domain separator will be seamlessly calculated on-the-fly.

constructor();

_domainNameAndVersion

*Please override this function to return the domain name and version.

function _domainNameAndVersion()
internal
pure
virtual
returns (string memory name, string memory version)
{
name = "Solady";
version = "1";
}

Note: If the returned result may change after the contract has been deployed, you must override _domainNameAndVersionMayChange() to return true.*

function _domainNameAndVersion() internal view virtual returns (string memory name, string memory version);

_domainNameAndVersionMayChange

Returns if _domainNameAndVersion() may change after the contract has been deployed (i.e. after the constructor). Default: false.

function _domainNameAndVersionMayChange() internal pure virtual returns (bool result);

_domainSeparator

Returns the EIP-712 domain separator.

function _domainSeparator() internal view virtual returns (bytes32 separator);

_hashTypedData

*Returns the hash of the fully encoded EIP-712 message for this domain, given structHash, as defined in https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct. The hash can be used together with ECDSA-recover to obtain the signer of a message:

bytes32 digest = _hashTypedData(keccak256(abi.encode(
keccak256("Mail(address to,string contents)"),
mailTo,
keccak256(bytes(mailContents))
)));
address signer = ECDSA.recover(digest, signature);
```*


```solidity
function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest);

_hashTypedDataSansChainId

Variant of _hashTypedData that excludes the chain ID. Included for the niche use case of cross-chain workflows.

function _hashTypedDataSansChainId(bytes32 structHash) internal view virtual returns (bytes32 digest);

_hashTypedDataSansChainIdAndVerifyingContract

Variant of _hashTypedData that excludes the chain ID and verifying contract. Included for the niche use case of cross-chain and multi-verifier workflows.

function _hashTypedDataSansChainIdAndVerifyingContract(bytes32 structHash)
    internal
    view
    virtual
    returns (bytes32 digest);

_hashTypedDataSansVerifyingContract

Variant of _hashTypedData that excludes the chain ID and verifying contract. Included for the niche use case of multi-verifier workflows.

function _hashTypedDataSansVerifyingContract(bytes32 structHash) internal view virtual returns (bytes32 digest);

eip712Domain

See: https://eips.ethereum.org/EIPS/eip-5267

function eip712Domain()
    public
    view
    virtual
    returns (
        bytes1 fields,
        string memory name,
        string memory version,
        uint256 chainId,
        address verifyingContract,
        bytes32 salt,
        uint256[] memory extensions
    );

_buildDomainSeparator

Returns the EIP-712 domain separator.

function _buildDomainSeparator() private view returns (bytes32 separator);

_cachedDomainSeparatorInvalidated

Returns if the cached domain separator has been invalidated.

function _cachedDomainSeparatorInvalidated() private view returns (bool result);

SiglessTransactionExecutor

Git Source

Inherits: ISafeTx, SecuredTokenTransfer, Executor, GuardManager

Author: Ultrasound Labs - @ultrasoundlabs This contract merges the "sigless" execution (no signature check) with the full Safe transaction execution logic. It allows you to execute a SafeTx struct directly, skipping signature checks, but still performing all the other steps (gas accounting, payment, etc) as in the original Safe contract. We don't import the Safe contract to save on gas costs.

Safe account has to delegatecall this contract to execute SafeTx with a normal flow except for the signature verification

State Variables

DOMAIN_SEPARATOR_TYPEHASH

bytes32 private constant DOMAIN_SEPARATOR_TYPEHASH = 0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218;

SAFE_TX_TYPEHASH

bytes32 private constant SAFE_TX_TYPEHASH = 0xbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d8;

__gap

uint256[5] private __gap;

nonce

Tracks the next expected Safe transaction nonce.

uint256 public nonce;

Functions

revertWithError

Function which uses assembly to revert with the passed error message.

Currently it is expected that the error string is at max 5 bytes of length. Ex: "GSXXX"

function revertWithError(bytes5 error) internal pure;

Parameters

NameTypeDescription
errorbytes5The error string to revert with.

execTransaction

Execute a SafeTx without requiring a signature.

This function unpacks the SafeTx struct and executes the transaction using the same logic as the original Safe contract, but skips signature verification. This is useful for trusted modules or scenarios where signature checks are not needed.

function execTransaction(SafeTx calldata safeTx) public returns (bool success);

Parameters

NameTypeDescription
safeTxSafeTxThe SafeTx struct containing all transaction parameters.

Returns

NameTypeDescription
successboolTrue if the transaction was executed successfully, false otherwise.

_handlePayment

Handles the payment/refund for a Safe transaction.

*Forked from Gnosis Safe handlePayment. Slither’s arbitrary-send-eth detector reports the receiver.send(payment) call below. In the canonical Safe design this is intentional and safe:

  1. The refund occurs after successful execution of the user’s transaction.
  2. Only the entity footing the gas bill (refundReceiver or tx.origin) receives the refund.
  3. The 2 300-gas stipend of .send mitigates re-entrancy; reverting on failure matches upstream semantics and prevents silent griefing.*
function _handlePayment(
    uint256 gasUsed,
    uint256 baseGas,
    uint256 gasPrice,
    address gasToken,
    address payable refundReceiver
) private returns (uint256 payment);

Parameters

NameTypeDescription
gasUseduint256Gas used by the Safe transaction.
baseGasuint256Gas costs that are independent of the transaction execution (e.g. base transaction fee, signature check, payment of the refund).
gasPriceuint256Gas price that should be used for the payment calculation.
gasTokenaddressToken address (or 0 if ETH) that is used for the payment.
refundReceiveraddress payableAddress that will receive the refund (defaults to tx.origin).

Returns

NameTypeDescription
paymentuint256The amount of payment made in the specified token.

encodeTransactionData

Returns the pre-image of the transaction hash (see getTransactionHash).

Forked from Safe.sol

function encodeTransactionData(
    address to,
    uint256 value,
    bytes memory data,
    Enum.Operation operation,
    uint256 safeTxGas,
    uint256 baseGas,
    uint256 gasPrice,
    address gasToken,
    address refundReceiver,
    uint256 _nonce
) internal view returns (bytes memory);

Parameters

NameTypeDescription
toaddressDestination address.
valueuint256Ether value.
databytesData payload.
operationEnum.OperationOperation type.
safeTxGasuint256Gas that should be used for the safe transaction.
baseGasuint256Gas costs for that are independent of the transaction execution(e.g. base transaction fee, signature check, payment of the refund)
gasPriceuint256Maximum gas price that should be used for this transaction.
gasTokenaddressToken address (or 0 if ETH) that is used for the payment.
refundReceiveraddressAddress of receiver of gas payment (or 0 if tx.origin).
_nonceuint256Transaction nonce.

Returns

NameTypeDescription
<none>bytesTransaction hash bytes.

domainSeparator

Returns the EIP-712 domain separator (chainId hard-coded to 1).

Returns the domain separator for this contract, as defined in the EIP-712 standard.

Forked from Safe.sol

function domainSeparator() public view returns (bytes32 separator);

Returns

NameTypeDescription
separatorbytes32The domain separator.

getChainId

Returns the ID of the chain the contract is currently deployed on.

Forked from Safe.sol

function getChainId() public view returns (uint256);

Returns

NameTypeDescription
<none>uint256The ID of the current chain as a uint256.

Events

ExecutionFailure

Emitted when a delegated Safe transaction reverts.

event ExecutionFailure(bytes32 indexed txHash, uint256 indexed payment);

Parameters

NameTypeDescription
txHashbytes32The EIP-712 hash of the SafeTx.
paymentuint256The gas payment that was attempted.

ExecutionSuccess

Emitted when a delegated Safe transaction executes successfully.

event ExecutionSuccess(bytes32 indexed txHash, uint256 indexed payment);

Parameters

NameTypeDescription
txHashbytes32The EIP-712 hash of the SafeTx.
paymentuint256The gas payment that was made.

ZeroAddress

Git Source

Custom error thrown when a zero address is provided where it is not allowed.

error ZeroAddress();

UnauthorizedCaller

Git Source

Custom error thrown when a caller other than the AllowanceManager attempts to pull funds.

error UnauthorizedCaller();

PullFailure

Git Source

Custom error thrown when the token transfer fails in _pull.

error PullFailure();

ZeroAmount

Git Source

error ZeroAmount();

AllowanceHolder

Git Source

Inherits: IAllowanceHolder, ReentrancyGuard

Author: Ultrasound Labs

CREATE2-deployed helper that can pull tokens from a user based on a single off-chain permit and hand out per-subaccount quotas via AllowanceManager.

Note: security-contact: security@ultrasoundlabs.org

State Variables

_OWNER

Holder of the tokens. Stored as immutable to optimise gas and used to compute the CREATE2 salt.

address private immutable _OWNER;

_TOKEN

ERC-20 token managed by this holder.

address private immutable _TOKEN;

_ALLOWANCE_MANAGER

AllowanceManager authorised to call pull.

address private immutable _ALLOWANCE_MANAGER;

Functions

constructor

Deploys a new holder and immediately seeds the first quota / initial transfer.

constructor(
    address _owner,
    address _token,
    address _allowanceManager,
    address _subaccount,
    uint256 _quotaAmount,
    uint256 _timeframe,
    uint256 _initialFund,
    bytes memory _firstCallData
);

Parameters

NameTypeDescription
_owneraddressToken holder who signed the permit.
_tokenaddressERC-20 token address the permit refers to.
_allowanceManageraddressContract authorised to request pulls and hold quotas.
_subaccountaddressFirst subaccount to give spending quota to.
_quotaAmountuint256Amount of tokens _subaccount may pull per timeframe.
_timeframeuint256Quota reset window in seconds.
_initialFunduint256Optional initial transfer to _subaccount executed during construction.
_firstCallDatabytesOptional calldata sent to _subaccount (as a normal call) during construction.

pull

Transfers amount of token() from the owner() directly to subaccount.

Can only be called by allowanceManager().

function pull(address subaccount, uint256 amount) external override nonReentrant;

Parameters

NameTypeDescription
subaccountaddressThe recipient Safe (subaccount).
amountuint256Amount of tokens to transfer.

owner

Returns the token owner whose funds can be pulled.

function owner() external view override returns (address);

Returns

NameTypeDescription
<none>addressownerAddr The owner address.

token

Returns the ERC-20 token managed by this holder.

function token() external view override returns (address);

Returns

NameTypeDescription
<none>addresstokenAddr The ERC-20 token address.

allowanceManager

Returns the AllowanceManager that is authorised to call pull.

function allowanceManager() external view override returns (address);

Returns

NameTypeDescription
<none>addressmanager The manager address.

_executeFirstCall

Executes the optional first call on the Safe proxy and returns whether it succeeded.

function _executeFirstCall(address subaccount, bytes memory firstCallData) internal returns (bool innerOk);

Parameters

NameTypeDescription
subaccountaddressThe Safe (subaccount) the call is executed on.
firstCallDatabytesCalldata to be forwarded to the subaccount.

Returns

NameTypeDescription
innerOkboolTrue if the inner execution succeeded, false otherwise.

_pull

Internal helper that attempts to transfer amount of _TOKEN from _OWNER to subaccount.

*Slither flags _token.transferFrom(_OWNER, subaccount, amount) as an "arbitrary-from" transfer (detector slug: arbitrary-send-erc20). In the context of AllowanceHolder this pattern is expected and safe under the following assumptions:

  1. _OWNER is an immutable variable that is set exactly once at construction time and never changes afterwards.
  2. Only the associated AllowanceManager contract can call pull, which in turn guarantees that internal accounting (spend quotas, non-reentrancy, etc.) is satisfied before any transfer is attempted.
  3. The owner explicitly pre-approved this contract (or granted a Permit2 allowance) during the bootstrap phase, so no third party can trigger an unexpected token movement. Because of these guard-rails the transfer cannot be abused to drain tokens from arbitrary wallets, therefore we silence this specific Slither warning for the remainder of the function.*

Prefers an ERC-20 allowance path; falls back to Permit2 allowance. Does not revert – propagates failures as the returned boolean so the caller can decide whether to emit best-effort events or revert.

function _pull(address subaccount, uint256 amount) internal returns (bool ok);

Parameters

NameTypeDescription
subaccountaddressThe Safe (subaccount) that should receive the funds.
amountuint256The amount of tokens to transfer.

Returns

NameTypeDescription
okboolTrue if the transfer succeeded, false otherwise.

Events

Pulled

Emitted when tokens are successfully pulled from the owner to a subaccount.

event Pulled(address indexed subaccount, uint256 indexed amount);

Parameters

NameTypeDescription
subaccountaddressThe Safe (subaccount) that received the tokens.
amountuint256The amount that has been transferred.

InitialFundFailed

Emitted when the initial funding transfer during construction fails.

event InitialFundFailed(address indexed subaccount, uint256 indexed amount);

Parameters

NameTypeDescription
subaccountaddressThe Safe (subaccount) that should have received the funds.
amountuint256The amount that failed to transfer.

FirstCallFailed

Emitted when the first arbitrary call during construction reverts.

event FirstCallFailed(address indexed subaccount);

Parameters

NameTypeDescription
subaccountaddressThe Safe (subaccount) the call was executed on.

AllowanceManager

Git Source

Inherits: EIP712x, IAllowanceManager, ReentrancyGuard

Author: Ultrasound Labs

Manages time-boxed token allowances that can be pulled by subaccounts.

*Each allowance is namespaced by (owner, subaccount, token) and protected from cross-chain replay via the EIP-712x domain separator (chainId hard-fixed to 1). The manager supports two transfer back-ends:

  1. A freshly deployed AllowanceHolder (preferred – no approvals necessary after the initial permit)
  2. Fallback to a direct ERC-20 allowance or Permit2 allowance given to this contract*

Note: security-contact: security@ultrasoundlabs.org

State Variables

_ALLOWANCE_REQUEST_BATCH_TYPEHASH

Private constant used internally for EIP-712 struct hashing.

bytes32 private constant _ALLOWANCE_REQUEST_BATCH_TYPEHASH = keccak256(
    "AllowanceRequestBatch(AllowanceRequest[] requests,uint256[] chainIds)AllowanceRequest(address subaccount,address token,uint256 amount,uint256 timeframe,uint256 nonce)"
);

_ALLOWANCE_REQUEST_TYPEHASH

Private constant used internally for EIP-712 struct hashing.

bytes32 private constant _ALLOWANCE_REQUEST_TYPEHASH =
    keccak256("AllowanceRequest(address subaccount,address token,uint256 amount,uint256 timeframe,uint256 nonce)");

_PERMIT2

Address of the Permit2 contract used as a back-up transfer mechanism.

address private immutable _PERMIT2;

_factory

Address of the canonical SubaccountFactory allowed to pre-commit holder addresses (set at deploy time or lazily on the first commit if zero).

address private _factory;

_allowances

mapping(address => mapping(address => mapping(address => Allowance))) internal _allowances;

allowanceNonces

Per-(owner,subaccount,token) nonce incremented every time an allowance is (re-)set.

mapping(address => mapping(address => mapping(address => uint256))) public allowanceNonces;

_holderFor

mapping(address => mapping(address => address)) private _holderFor;

_committedHolder

mapping(address => mapping(address => address)) private _committedHolder;

_allowedHolder

Addresses pre-authorised by the canonical factory to call bootstrapAllowance during their constructor. The factory MUST set the flag before deploying the holder via CREATE2 so that the constructor call can pass the check below.

mapping(address => bool) private _allowedHolder;

Functions

constructor

Initializes the AllowanceManager with the Permit2 contract address and the SubaccountFactory.

constructor(address _permit2, address factoryAddr);

Parameters

NameTypeDescription
_permit2addressAddress of the Permit2 contract.
factoryAddraddressCanonical SubaccountFactory that may call commitHolder.

bootstrapAllowance

Bootstrap an allowance once immediately after AllowanceHolder deployment.

Can only be called by the canonical AllowanceHolder for (owner, token) and only when nonce == 0. After a successful call, allowanceNonces[owner][subaccount][token] will be incremented to 1 so that any subsequent changes require a signed setAllowances().

function bootstrapAllowance(address owner, address subaccount, address token, uint256 amount, uint256 timeframe)
    external;

Parameters

NameTypeDescription
owneraddressThe owner of the allowance (signer of the permit given to the holder).
subaccountaddressSubaccount that the allowance applies to.
tokenaddressERC-20 token address.
amountuint256Allowance amount.
timeframeuint256Time window in seconds for the allowance resets.

commitHolder

Commits the expected AllowanceHolder address for a given (owner, token) pair.

Must be called once before the first bootstrapAllowance. Only allowed while no canonical holder has been observed yet.

function commitHolder(address owner, address token, address holder) external;

Parameters

NameTypeDescription
owneraddressThe token owner.
tokenaddressThe ERC-20 token managed by the holder.
holderaddressThe deterministic address where the AllowanceHolder will be deployed.

registerHolder

Registers an additional holder for an (owner, token) pair after the canonical one has already been observed. Must be called by the canonical factory before deploying the new holder so that its constructor can call bootstrapAllowance successfully.

function registerHolder(address holder) external;

Parameters

NameTypeDescription
holderaddressThe deterministic address where the new AllowanceHolder will be deployed.

holderAddress

Deterministically computes the canonical AllowanceHolder address for a pair (owner, token).

function holderAddress(address owner, address token) external view returns (address);

Parameters

NameTypeDescription
owneraddressThe token owner.
tokenaddressThe ERC-20 token.

Returns

NameTypeDescription
<none>addressholder The predicted holder address (or address(0) if unknown).

factory

Returns the canonical factory address permitted to commit holders.

function factory() external view returns (address factoryAddress);

Returns

NameTypeDescription
factoryAddressaddressThe address of the canonical SubaccountFactory.

permit2

The Permit2 contract address

function permit2() external view returns (address);

Returns

NameTypeDescription
<none>addresspermit2Addr The Permit2 contract address.

allowances

Mapping to track allowances for subaccounts.

function allowances(address owner, address subaccount, address token) external view returns (Allowance memory);

Parameters

NameTypeDescription
owneraddressThe owner of the subaccount.
subaccountaddressThe subaccount that the allowance is set for.
tokenaddressThe token that the allowance is set for.

Returns

NameTypeDescription
<none>Allowanceallowance The allowance struct.

allowanceRequestBatchTypehash

EIP-712(x) typehash for the AllowanceRequestBatch struct (per-subaccount)

function allowanceRequestBatchTypehash() external pure returns (bytes32);

Returns

NameTypeDescription
<none>bytes32typeHash The struct type-hash.

allowanceRequestTypehash

EIP-712(x) typehash for the AllowanceRequest struct (per-subaccount)

function allowanceRequestTypehash() external pure returns (bytes32);

Returns

NameTypeDescription
<none>bytes32typeHash The struct type-hash.

setAllowances

Sets allowances for a batch of subaccounts

function setAllowances(address owner, AllowanceRequestBatch calldata requests, bytes calldata signature) public;

Parameters

NameTypeDescription
owneraddressThe owner of the subaccounts
requestsAllowanceRequestBatchThe AllowanceRequestBatch struct containing the requests
signaturebytesThe EIP-712x (EIP-712 with chainId = 1) signature of the owner

spendAllowance

Spends an allowance for the caller

function spendAllowance(address owner, address token, uint256 amount) public nonReentrant;

Parameters

NameTypeDescription
owneraddressThe owner of the subaccount
tokenaddressThe token to spend the allowance for
amountuint256The amount to spend

hashAllowanceRequestBatch

Computes the EIP-712 struct hash for a batch of allowance requests.

function hashAllowanceRequestBatch(AllowanceRequestBatch calldata batch) public pure returns (bytes32 structHash);

Parameters

NameTypeDescription
batchAllowanceRequestBatchThe AllowanceRequestBatch struct to hash.

Returns

NameTypeDescription
structHashbytes32The EIP-712 struct hash.

_transferFromOwner

Transfers an allowance from the owner to the subaccount.

*Slither raises an arbitrary-send-erc20 finding for the safeTransferFrom(owner, subaccount, amount) call inside this helper. The warning is a false positive in our threat model because:

  1. The function is internal and only reached via spendAllowance, after* the allowance window and per-subaccount quota checks have passed.
  2. Success still depends on the owner having granted an explicit ERC-20 allowance or Permit2 allowance to this contract. Without such opt-in the transfer will revert.
  3. The semantics mirror the well-audited Gnosis Safe flow (AllowanceHolder pull, fallback to safeTransferFrom). For these reasons the transfer cannot be exploited to steal funds from arbitrary users, therefore we silence the detector for the body of the function.*
function _transferFromOwner(address owner, address subaccount, address token, uint256 amount) internal;

Parameters

NameTypeDescription
owneraddressThe owner of the allowance.
subaccountaddressThe subaccount that the allowance is transferred to.
tokenaddressThe token that the allowance is transferred for.
amountuint256The amount of the allowance to transfer.

_predictHolderAddress

Fetches the cached canonical AllowanceHolder for an (owner, token) pair if it has been observed via bootstrapAllowance.

function _predictHolderAddress(address owner, address token) internal view returns (address holder);

Parameters

NameTypeDescription
owneraddressThe owner address.
tokenaddressThe ERC-20 token address.

Returns

NameTypeDescription
holderaddressThe cached holder address (zero address if none).

_domainNameAndVersion

Returns the EIP-712 domain name and version used by this contract (chainId is hard-wired to 1 in the parent).

*Please override this function to return the domain name and version.

function _domainNameAndVersion()
internal
pure
virtual
returns (string memory name, string memory version)
{
name = "Solady";
version = "1";
}

Note: If the returned result may change after the contract has been deployed, you must override _domainNameAndVersionMayChange() to return true.*

function _domainNameAndVersion() internal pure override returns (string memory name, string memory version);

Returns

NameTypeDescription
namestringThe domain name.
versionstringThe domain version.

Events

HolderPullFailed

Emitted when an attempt to pull funds via AllowanceHolder fails and the flow falls back to the approval path.

event HolderPullFailed(address indexed holder, address indexed subaccount, uint256 indexed amount);

Parameters

NameTypeDescription
holderaddressThe holder contract that was called.
subaccountaddressThe subaccount the funds were destined to.
amountuint256The amount that failed to pull.

Errors

InvalidNonce

error InvalidNonce();

HolderOwnerMismatch

error HolderOwnerMismatch();

HolderTokenMismatch

error HolderTokenMismatch();

HolderManagerMismatch

error HolderManagerMismatch();

HolderMismatch

error HolderMismatch();

AlreadyInitialised

error AlreadyInitialised();

ZeroAmount

error ZeroAmount();

HolderNotCommitted

error HolderNotCommitted();

HolderCommitMismatch

error HolderCommitMismatch();

HolderAlreadyCommitted

error HolderAlreadyCommitted();

UnauthorizedCaller

error UnauthorizedCaller();

ISafe

Git Source

Author: Safe

Minimal subset of the Safe contract needed for signature verification.

Functions

checkSignatures

Verifies the given signatures for dataHash.

function checkSignatures(bytes32 dataHash, bytes memory data, bytes memory signatures) external view;

Parameters

NameTypeDescription
dataHashbytes32Keccak256 hash of the encoded transaction data.
databytesThe original pre-image that was signed.
signaturesbytesConcatenated signatures.

MultiChainSignaturesModule

Git Source

Inherits: ISafeTx

Author: Ultrasound Labs

Safe module that allows the same batched signature to be replayed across multiple chains; only the transactions whose chainIds[i] match block.chainid are executed.

Note: security-contact: security@ultrasoundlabs.org

State Variables

VERSION

Human-readable contract version (kept for backwards compatibility with Safe tooling).

string public constant VERSION = "1.4.1-MCSM";

DOMAIN_SEPARATOR_TYPEHASH

bytes32 private constant DOMAIN_SEPARATOR_TYPEHASH =
    keccak256("EIP712Domain(uint256 chainId,address verifyingContract)");

SAFE_TX_BATCH_TYPEHASH

bytes32 private constant SAFE_TX_BATCH_TYPEHASH = keccak256(
    "SafeTxBatch(address subaccount,SafeTx[] safeTxs,uint256[] chainIds)SafeTx(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)"
);

SAFE_TX_TYPEHASH

bytes32 private constant SAFE_TX_TYPEHASH = keccak256(
    "SafeTx(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)"
);

SIGLESS_TRANSACTION_EXECUTOR

The address of the SiglessTransactionExecutor library used for delegate-calls.

address public immutable SIGLESS_TRANSACTION_EXECUTOR;

Functions

constructor

Constructs the module.

constructor(address _siglessTransactionExecutor);

Parameters

NameTypeDescription
_siglessTransactionExecutoraddressAddress of the library that will execute SafeTx via delegatecall.

execTransactionBatch

Executes the subset of safeTxBatch targeted to the current chain.

function execTransactionBatch(address account, SafeTxBatch calldata safeTxBatch, bytes calldata signatures)
    public
    returns (bool success);

Parameters

NameTypeDescription
accountaddressThe Safe account the batch applies to.
safeTxBatchSafeTxBatchBatch struct containing transactions and their intended chainIds.
signaturesbytesConcatenated owner signatures over the EIP-712 batch preimage.

Returns

NameTypeDescription
successboolTrue if all on-chain-eligible transactions executed without failures.

encodeTransactionBatchData

Encodes the SafeTxBatch into the pre-image for signature verification following the same structure hashing rules as Safe.

function encodeTransactionBatchData(SafeTxBatch calldata safeTxBatch) public view returns (bytes memory);

Parameters

NameTypeDescription
safeTxBatchSafeTxBatchBatch struct containing the transactions and their target chainIds.

Returns

NameTypeDescription
<none>bytesencodedData The ABI-encoded EIP-712 pre-image that is signed by Safe owners.

domainSeparator

Returns the domain separator used for batch hashing (chainId hard-coded to 1).

function domainSeparator() public view returns (bytes32 separator);

Returns

NameTypeDescription
separatorbytes32The EIP-712 domain separator.

Events

SafeTxFailed

Emitted when a transaction in the batch reverts on the current chain.

event SafeTxFailed(address indexed safe, uint256 indexed index);

Parameters

NameTypeDescription
safeaddressThe Safe account that attempted the execution.
indexuint256The index of the failing transaction within the batch.

Errors

MismatchedSubaccount


Custom errors

error MismatchedSubaccount();

Structs

SafeTxBatch

struct SafeTxBatch {
    address subaccount;
    SafeTx[] safeTxs;
    uint256[] chainIds;
}

OpenSubaccounts

Git Source

Inherits: MultiChainSignaturesModule, SubaccountFactory, AllowanceManager

Author: Ultrasound Labs

Top-level contract that bundles the Subaccount factory, Multi-Chain Signature module and Allowance Manager into a single deployment for convenience.

Note: security-contact: security@ultrasoundlabs.org

Functions

constructor

Constructs a new OpenSubaccounts instance wiring together the SubaccountFactory, MultiChainSignaturesModule and AllowanceManager modules.

constructor(
    address _siglessTransactionExecutor,
    address _safeSingleton,
    address _safeProxyFactory,
    address _safeModuleSetup,
    address _permit2
)
    MultiChainSignaturesModule(_siglessTransactionExecutor)
    SubaccountFactory(_safeSingleton, _safeProxyFactory, _safeModuleSetup, address(this), address(this))
    AllowanceManager(_permit2, address(this));

Parameters

NameTypeDescription
_siglessTransactionExecutoraddressAddress of the SiglessTransactionExecutor library.
_safeSingletonaddressCanonical Safe singleton implementation.
_safeProxyFactoryaddressSafe proxy factory address.
_safeModuleSetupaddressHelper used to enable modules during Safe setup.
_permit2addressPermit2 contract used by AllowanceManager.

ISafe

Git Source

Functions

execTransactionFromModule

Allows a Module to execute a Safe transaction without any further confirmations.

function execTransactionFromModule(address to, uint256 value, bytes memory data, uint8 operation)
    external
    returns (bool success);

Parameters

NameTypeDescription
toaddressDestination address of module transaction.
valueuint256Ether value of module transaction.
databytesData payload of module transaction.
operationuint8Operation type of module transaction.

execTransactionFromModuleReturnData

Execute operation (0: Call, 1: DelegateCall) to to with value (Native Token) and return data

function execTransactionFromModuleReturnData(address to, uint256 value, bytes memory data, uint8 operation)
    external
    returns (bool success, bytes memory returnData);

Parameters

NameTypeDescription
toaddressDestination address of module transaction.
valueuint256Ether value of module transaction.
databytesData payload of module transaction.
operationuint8Operation type of module transaction.

Returns

NameTypeDescription
successboolBoolean flag indicating if the call succeeded.
returnDatabytesData returned by the call.

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) external view;

Parameters

NameTypeDescription
dataHashbytes32Hash of the data (could be either a message hash or transaction hash)
databytesThat should be signed (this is passed to an external validator contract)
signaturesbytesSignature data that should be verified. Can be ECDSA signature, contract signature (EIP-1271) or approved hash.

domainSeparator

Returns the domain separator for this contract, as defined in the EIP-712 standard.

function domainSeparator() external view returns (bytes32);

Returns

NameTypeDescription
<none>bytes32bytes32 The domain separator hash.

getModulesPaginated

Returns array of modules.

function getModulesPaginated(address start, uint256 pageSize)
    external
    view
    returns (address[] memory array, address next);

Parameters

NameTypeDescription
startaddressStart of the page.
pageSizeuint256Maximum number of modules that should be returned.

Returns

NameTypeDescription
arrayaddress[]Array of modules.
nextaddressStart of the next page.

enableModule

Enables the module module for the Safe.

This can only be done via a Safe transaction.

function enableModule(address module) external;

Parameters

NameTypeDescription
moduleaddressModule to be enabled.

getThreshold

Returns the number of required confirmations for a Safe transaction aka the threshold.

function getThreshold() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256Threshold number.

SafeModuleSetup

Git Source

The Safe setup function accepts to and data parameters for a delegate call during initialization. This contract can be specified as the to with data ABI encoding the enableModules call so that a Safe is created with the specified modules. In particular, this allows a ERC-4337 compatible Safe to be created as part of a ERC-4337 user operation with the Safe4337Module enabled right away.

Note: security-contact: bounty@safe.global

Functions

enableModules

Enable the specified Safe modules.

This call will only work if used from a Safe via delegatecall. It is intended to be used as part of the Safe setup, allowing Safes to be created with an initial set of enabled modules.

function enableModules(address[] calldata modules) external;

Parameters

NameTypeDescription
modulesaddress[]The modules to enable.

SubaccountAddressMismatch

Git Source

error SubaccountAddressMismatch();

HolderAddressMismatch

Git Source

error HolderAddressMismatch();

AllowanceManagerRegistrationFailed

Git Source

error AllowanceManagerRegistrationFailed();

SubaccountFactory

Git Source

Inherits: ReentrancyGuard

Author: Ultrasound Labs

Deploys deterministic Safe-based subaccounts and their AllowanceHolders, and wires them up for multi-chain use.

Note: security-contact: security@ultrasoundlabs.org

State Variables

SAFE_SINGLETON

Canonical Safe singleton (implementation) used for new subaccounts.

address public immutable SAFE_SINGLETON;

SAFE_PROXY_FACTORY

Factory for deploying Safe proxies.

address public immutable SAFE_PROXY_FACTORY;

SAFE_MODULE_SETUP

Helper contract for enabling modules during Safe setup.

address public immutable SAFE_MODULE_SETUP;

MULTICHAIN_SIGNATURES_MODULE

Module providing multi-chain batched signatures.

address public immutable MULTICHAIN_SIGNATURES_MODULE;

ALLOWANCE_MANAGER

Address of the AllowanceManager used by holders.

address public immutable ALLOWANCE_MANAGER;

_enableModulesCalldata

Pre-computed calldata for enabling the MultiChainSignaturesModule via the helper in the Safe setup. This is computed once in the constructor to avoid rebuilding the dynamic array on every subaccount creation.

bytes private _enableModulesCalldata;

isDeployedSubaccount

Records whether a given subaccount proxy has already been deployed.

Keyed by subaccount address.

mapping(address => bool) public isDeployedSubaccount;

isDeployedAllowanceHolder

Records whether the AllowanceHolder for (owner,token) has been deployed.

Keyed by AllowanceHolder address.

mapping(address => bool) public isDeployedAllowanceHolder;

Functions

constructor

Initializes the factory with required contract addresses.

constructor(
    address _safeSingleton,
    address _safeProxyFactory,
    address _safeModuleSetup,
    address _multichainSignaturesModule,
    address _allowanceManager
);

Parameters

NameTypeDescription
_safeSingletonaddressAddress of the Safe singleton (logic contract).
_safeProxyFactoryaddressAddress of the SafeProxyFactory.
_safeModuleSetupaddressAddress of the helper contract for module setup.
_multichainSignaturesModuleaddressAddress of the MultiChainSignaturesModule to enable.
_allowanceManageraddressAddress of the AllowanceManager to use for allowance holders.

createAllowanceHolder

Ensures the AllowanceHolder for (owner, token) exists and has allowance via Permit2.

Deploys the holder deterministically via CREATE2 if not yet deployed, immediately executes permit, and optionally pushes initial quota/fund/first call.

function createAllowanceHolder(
    address owner,
    address token,
    address subaccount,
    uint256 quotaAmount,
    uint256 quotaTimeframe,
    uint256 initialFund,
    bytes calldata firstCallData,
    uint256 permitDeadline,
    uint8 v,
    bytes32 r,
    bytes32 s
) public nonReentrant returns (address holder);

Parameters

NameTypeDescription
owneraddressToken owner that signed the permit.
tokenaddressERC-20 token address.
subaccountaddressSubaccount that will spend from the allowance.
quotaAmountuint256Per-timeframe allowance amount.
quotaTimeframeuint256Seconds per allowance reset window.
initialFunduint256Optional initial transfer executed by the holder constructor.
firstCallDatabytesOptional calldata executed on the subaccount during holder construction.
permitDeadlineuint256Deadline used in the ERC-2612 permit.
vuint8Signature v component of the permit.
rbytes32Signature r component of the permit.
sbytes32Signature s component of the permit.

Returns

NameTypeDescription
holderaddressThe AllowanceHolder address (newly deployed or existing).

createSubaccount

Deploys a new Safe-based subaccount for owner under appDomain.

function createSubaccount(address owner, string calldata appDomain, uint32 accountIndex)
    public
    returns (address subaccount);

Parameters

NameTypeDescription
owneraddressThe future owner of the subaccount.
appDomainstringApplication domain to namespace the subaccount (e.g. "my.cool.app").
accountIndexuint32Sequential index chosen by the owner (use 0 for the first account).

Returns

NameTypeDescription
subaccountaddressThe deployed Safe proxy address (or the existing one if already deployed).

generateSubaccountAddress

Predicts the deterministic address of a subaccount without deploying it.

function generateSubaccountAddress(address owner, string calldata appDomain, uint32 accountIndex)
    public
    view
    returns (address subaccount, bool isDeployed);

Parameters

NameTypeDescription
owneraddressThe owner that will control the subaccount.
appDomainstringApplication domain used to namespace the subaccount.
accountIndexuint32Sequential index for the domain chosen by the owner.

Returns

NameTypeDescription
subaccountaddressPredicted Safe proxy address.
isDeployedboolBoolean indicating whether the subaccount already exists.

generateAllowanceHolderAddress

Predicts the AllowanceHolder address for a given (owner, token) pair.

function generateAllowanceHolderAddress(
    address owner,
    address token,
    address subaccount,
    uint256 quotaAmount,
    uint256 timeframe,
    uint256 initialFund,
    bytes memory firstCallData
) public view returns (address holder, bool isDeployed);

Parameters

NameTypeDescription
owneraddressThe token owner.
tokenaddressERC-20 token address.
subaccountaddressSubaccount that will draw from the allowance.
quotaAmountuint256Quota amount per timeframe (passed to constructor).
timeframeuint256Quota reset window in seconds.
initialFunduint256Optional initial transfer executed by the holder constructor.
firstCallDatabytesOptional calldata executed on subaccount by the holder constructor.

Returns

NameTypeDescription
holderaddressPredicted AllowanceHolder address.
isDeployedboolWhether the holder is already deployed.

_executePermit

Executes an unlimited permit using native ERC-2612 if available, otherwise falls back to Uniswap Permit2.

Uses SafeTransferLib.permit2, which first attempts token.permit and gracefully falls back to Permit2 if the token does not support ERC-2612 (or the call fails).

function _executePermit(address owner, address token, address spender, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
    internal;

Parameters

NameTypeDescription
owneraddressToken owner that signed the permit.
tokenaddressERC-20 token address.
spenderaddressAddress that receives unlimited allowance (the AllowanceHolder).
deadlineuint256Permit deadline (same as in signature).
vuint8Signature v value.
rbytes32Signature r value.
sbytes32Signature s value.

_deployAllowanceHolder

Performs the deterministic deployment of the AllowanceHolder using CREATE2.

function _deployAllowanceHolder(
    address owner,
    address token,
    address subaccount,
    uint256 quotaAmount,
    uint256 quotaTimeframe,
    uint256 initialFund,
    bytes calldata firstCallData,
    address predictedHolder
) internal returns (address holder);

Parameters

NameTypeDescription
owneraddressToken owner.
tokenaddressERC-20 token managed by the holder.
subaccountaddressSubaccount that will draw from the allowance.
quotaAmountuint256Per-timeframe allowance.
quotaTimeframeuint256Time window for resets.
initialFunduint256Optional immediate transfer.
firstCallDatabytesOptional call executed on subaccount.
predictedHolderaddressPre-computed address where the holder must be deployed.

Returns

NameTypeDescription
holderaddressDeployed AllowanceHolder address (equal to predictedHolder).

_constructInitializer

Constructs the initializer data for Safe setup.

This prepares the calldata for Safe.setup, setting the owner, threshold, and enabling the module.

function _constructInitializer(address owner) internal view returns (bytes memory);

Parameters

NameTypeDescription
owneraddressThe address to be set as the sole owner of the Safe.

Returns

NameTypeDescription
<none>bytesThe ABI-encoded initializer data for Safe.setup.

_generateSaltNonce

Converts an app domain string and account index to a salt nonce.

Uses NameCoder to encode and namehash the domain.

function _generateSaltNonce(string calldata appDomain, uint32 accountIndex) internal pure returns (uint256);

Parameters

NameTypeDescription
appDomainstringThe application domain string (e.g., "my.cool.app").
accountIndexuint32The index of the account in the app domain.

Returns

NameTypeDescription
<none>uint256The salt nonce.

_salt

Computes the salt used for CREATE2 deployments.

function _salt(
    address owner,
    address token,
    address manager,
    address subaccount,
    uint256 amount,
    uint256 timeframe,
    uint256 initialFund,
    bytes32 firstCallHash
) internal pure returns (bytes32);

Parameters

NameTypeDescription
owneraddressToken owner.
tokenaddressERC-20 token.
manageraddressAllowanceManager address.
subaccountaddressSubaccount address.
amountuint256Allowance amount.
timeframeuint256Timeframe seconds for resets.
initialFunduint256Optional initial fund.
firstCallHashbytes32Keccak256 hash of firstCallData.

Returns

NameTypeDescription
<none>bytes32salt Bytes32 salt value.

_creationCode

Builds the creation bytecode for the AllowanceHolder constructor params.

function _creationCode(
    address owner,
    address token,
    address manager,
    address subaccount,
    uint256 amount,
    uint256 timeframe,
    uint256 initialFund,
    bytes memory firstCallData
) internal pure returns (bytes memory);

Parameters

NameTypeDescription
owneraddressToken owner.
tokenaddressERC-20 token.
manageraddressAllowanceManager address.
subaccountaddressSubaccount.
amountuint256Allowance amount.
timeframeuint256Timeframe seconds.
initialFunduint256Initial fund amount.
firstCallDatabytesCalldata executed on subaccount.

Returns

NameTypeDescription
<none>bytescreationCode ABI encoded constructor + bytecode snippet.

_computeAddress

Computes the deterministic contract address as in EIP-1014.

function _computeAddress(
    address deployer,
    address owner,
    address token,
    address manager,
    address subaccount,
    uint256 amount,
    uint256 timeframe,
    uint256 initialFund,
    bytes memory firstCallData
) internal pure returns (address);

Parameters

NameTypeDescription
deployeraddressAddress performing the CREATE2.
owneraddressToken owner (used in salt).
tokenaddressERC-20 token.
manageraddressAllowanceManager address.
subaccountaddressSubaccount.
amountuint256Allowance amount.
timeframeuint256Timeframe seconds.
initialFunduint256Initial fund amount.
firstCallDatabytesEncoded first call data.

Returns

NameTypeDescription
<none>addressaddr Predicted contract address.

_ownersArray

Helper to create a single-element owners array in memory.

Used internally to format the input for Safe.setup.

function _ownersArray(address owner) private pure returns (address[] memory owners);

Parameters

NameTypeDescription
owneraddressThe address to include in the array.

Returns

NameTypeDescription
ownersaddress[]A dynamic array containing only the owner address.

Events

SubaccountCreated

Emitted after a successful subaccount deployment.

event SubaccountCreated(
    address indexed subaccount, address indexed owner, string indexed appDomain, uint32 accountIndex
);

Parameters

NameTypeDescription
subaccountaddressThe deployed Safe proxy address.
owneraddressThe owner address passed to the subaccount.
appDomainstringThe app-domain string that namespaced this subaccount.
accountIndexuint32The sequential index chosen by the owner for this domain.

AllowanceHolderCreated

Emitted when the canonical AllowanceHolder for (owner,token) has been deployed.

event AllowanceHolderCreated(address indexed holder, address indexed owner, address indexed token);

Parameters

NameTypeDescription
holderaddressDeployed AllowanceHolder address.
owneraddressThe token owner that signed the pull-permit.
tokenaddressThe ERC-20 token the holder controls.