Contents
IAllowanceHolder
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
| Name | Type | Description |
|---|---|---|
subaccount | address | The recipient Safe (subaccount). |
amount | uint256 | Amount of tokens to transfer. |
owner
Returns the token owner whose funds can be pulled.
function owner() external view returns (address ownerAddr);
Returns
| Name | Type | Description |
|---|---|---|
ownerAddr | address | The owner address. |
token
Returns the ERC-20 token managed by this holder.
function token() external view returns (address tokenAddr);
Returns
| Name | Type | Description |
|---|---|---|
tokenAddr | address | The ERC-20 token address. |
allowanceManager
Returns the AllowanceManager that is authorised to call pull.
function allowanceManager() external view returns (address manager);
Returns
| Name | Type | Description |
|---|---|---|
manager | address | The manager address. |
IAllowanceManager
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
| Name | Type | Description |
|---|---|---|
owner | address | The owner of the allowance (signer of the permit given to the holder). |
subaccount | address | Subaccount that the allowance applies to. |
token | address | ERC-20 token address. |
amount | uint256 | Allowance amount. |
timeframe | uint256 | Time 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
| Name | Type | Description |
|---|---|---|
owner | address | The token owner that will grant allowance. |
token | address | The ERC-20 token the holder will manage. |
holder | address | The 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
| Name | Type | Description |
|---|---|---|
holder | address | The 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
| Name | Type | Description |
|---|---|---|
owner | address | The owner of the subaccounts |
requests | AllowanceRequestBatch | The AllowanceRequestBatch struct containing the requests |
signature | bytes | The 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
| Name | Type | Description |
|---|---|---|
owner | address | The owner of the subaccount |
token | address | The token to spend the allowance for |
amount | uint256 | The 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
| Name | Type | Description |
|---|---|---|
owner | address | The token owner. |
token | address | The ERC-20 token. |
Returns
| Name | Type | Description |
|---|---|---|
holder | address | The 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
| Name | Type | Description |
|---|---|---|
owner | address | The owner of the subaccount. |
subaccount | address | The subaccount that the allowance is set for. |
token | address | The token that the allowance is set for. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | Allowance | allowance 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
| Name | Type | Description |
|---|---|---|
owner | address | The owner of the subaccount. |
subaccount | address | The subaccount that the allowance is set for. |
token | address | The token that the allowance is set for. |
Returns
| Name | Type | Description |
|---|---|---|
nonce | uint256 | The nonce of the allowance. |
permit2
The Permit2 contract address
function permit2() external view returns (address permit2Addr);
Returns
| Name | Type | Description |
|---|---|---|
permit2Addr | address | The Permit2 contract address. |
allowanceRequestTypehash
EIP-712(x) typehash for the AllowanceRequest struct (per-subaccount)
function allowanceRequestTypehash() external pure returns (bytes32 typeHash);
Returns
| Name | Type | Description |
|---|---|---|
typeHash | bytes32 | The struct type-hash. |
allowanceRequestBatchTypehash
EIP-712(x) typehash for the AllowanceRequestBatch struct (per-subaccount)
function allowanceRequestBatchTypehash() external pure returns (bytes32 typeHash);
Returns
| Name | Type | Description |
|---|---|---|
typeHash | bytes32 | The 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
| Name | Type | Description |
|---|---|---|
batch | AllowanceRequestBatch | The AllowanceRequestBatch struct to hash. |
Returns
| Name | Type | Description |
|---|---|---|
structHash | bytes32 | The 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
| Name | Type | Description |
|---|---|---|
owner | address | The owner of the subaccount. |
subaccount | address | The subaccount that the allowance is set for. |
token | address | The token that the allowance is set for. |
amount | uint256 | The amount of the allowance. |
timeframe | uint256 | The 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
| Name | Type | Description |
|---|---|---|
owner | address | The owner of the subaccount. |
subaccount | address | The subaccount that the allowance is spent for. |
token | address | The token that the allowance is spent for. |
amount | uint256 | The 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
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
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
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
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
| Name | Type | Description |
|---|---|---|
error | bytes5 | The 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
| Name | Type | Description |
|---|---|---|
safeTx | SafeTx | The SafeTx struct containing all transaction parameters. |
Returns
| Name | Type | Description |
|---|---|---|
success | bool | True 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:
- The refund occurs after successful execution of the user’s transaction.
- Only the entity footing the gas bill (
refundReceiverortx.origin) receives the refund. - The 2 300-gas stipend of
.sendmitigates 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
| Name | Type | Description |
|---|---|---|
gasUsed | uint256 | Gas used by the Safe transaction. |
baseGas | uint256 | Gas costs that are independent of the transaction execution (e.g. base transaction fee, signature check, payment of the refund). |
gasPrice | uint256 | Gas price that should be used for the payment calculation. |
gasToken | address | Token address (or 0 if ETH) that is used for the payment. |
refundReceiver | address payable | Address that will receive the refund (defaults to tx.origin). |
Returns
| Name | Type | Description |
|---|---|---|
payment | uint256 | The 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
| Name | Type | Description |
|---|---|---|
to | address | Destination address. |
value | uint256 | Ether value. |
data | bytes | Data payload. |
operation | Enum.Operation | Operation type. |
safeTxGas | uint256 | Gas that should be used for the safe transaction. |
baseGas | uint256 | Gas costs for that are independent of the transaction execution(e.g. base transaction fee, signature check, payment of the refund) |
gasPrice | uint256 | Maximum gas price that should be used for this transaction. |
gasToken | address | Token address (or 0 if ETH) that is used for the payment. |
refundReceiver | address | Address of receiver of gas payment (or 0 if tx.origin). |
_nonce | uint256 | Transaction nonce. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bytes | Transaction 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
| Name | Type | Description |
|---|---|---|
separator | bytes32 | The 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
| Name | Type | Description |
|---|---|---|
<none> | uint256 | The 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
| Name | Type | Description |
|---|---|---|
txHash | bytes32 | The EIP-712 hash of the SafeTx. |
payment | uint256 | The gas payment that was attempted. |
ExecutionSuccess
Emitted when a delegated Safe transaction executes successfully.
event ExecutionSuccess(bytes32 indexed txHash, uint256 indexed payment);
Parameters
| Name | Type | Description |
|---|---|---|
txHash | bytes32 | The EIP-712 hash of the SafeTx. |
payment | uint256 | The gas payment that was made. |
ZeroAddress
Custom error thrown when a zero address is provided where it is not allowed.
error ZeroAddress();
UnauthorizedCaller
Custom error thrown when a caller other than the AllowanceManager attempts to pull funds.
error UnauthorizedCaller();
PullFailure
Custom error thrown when the token transfer fails in _pull.
error PullFailure();
ZeroAmount
error ZeroAmount();
AllowanceHolder
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
| Name | Type | Description |
|---|---|---|
_owner | address | Token holder who signed the permit. |
_token | address | ERC-20 token address the permit refers to. |
_allowanceManager | address | Contract authorised to request pulls and hold quotas. |
_subaccount | address | First subaccount to give spending quota to. |
_quotaAmount | uint256 | Amount of tokens _subaccount may pull per timeframe. |
_timeframe | uint256 | Quota reset window in seconds. |
_initialFund | uint256 | Optional initial transfer to _subaccount executed during construction. |
_firstCallData | bytes | Optional 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
| Name | Type | Description |
|---|---|---|
subaccount | address | The recipient Safe (subaccount). |
amount | uint256 | Amount of tokens to transfer. |
owner
Returns the token owner whose funds can be pulled.
function owner() external view override returns (address);
Returns
| Name | Type | Description |
|---|---|---|
<none> | address | ownerAddr The owner address. |
token
Returns the ERC-20 token managed by this holder.
function token() external view override returns (address);
Returns
| Name | Type | Description |
|---|---|---|
<none> | address | tokenAddr The ERC-20 token address. |
allowanceManager
Returns the AllowanceManager that is authorised to call pull.
function allowanceManager() external view override returns (address);
Returns
| Name | Type | Description |
|---|---|---|
<none> | address | manager 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
| Name | Type | Description |
|---|---|---|
subaccount | address | The Safe (subaccount) the call is executed on. |
firstCallData | bytes | Calldata to be forwarded to the subaccount. |
Returns
| Name | Type | Description |
|---|---|---|
innerOk | bool | True 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:
_OWNERis an immutable variable that is set exactly once at construction time and never changes afterwards.- Only the associated
AllowanceManagercontract can callpull, which in turn guarantees that internal accounting (spend quotas, non-reentrancy, etc.) is satisfied before any transfer is attempted. - 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
| Name | Type | Description |
|---|---|---|
subaccount | address | The Safe (subaccount) that should receive the funds. |
amount | uint256 | The amount of tokens to transfer. |
Returns
| Name | Type | Description |
|---|---|---|
ok | bool | True 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
| Name | Type | Description |
|---|---|---|
subaccount | address | The Safe (subaccount) that received the tokens. |
amount | uint256 | The amount that has been transferred. |
InitialFundFailed
Emitted when the initial funding transfer during construction fails.
event InitialFundFailed(address indexed subaccount, uint256 indexed amount);
Parameters
| Name | Type | Description |
|---|---|---|
subaccount | address | The Safe (subaccount) that should have received the funds. |
amount | uint256 | The amount that failed to transfer. |
FirstCallFailed
Emitted when the first arbitrary call during construction reverts.
event FirstCallFailed(address indexed subaccount);
Parameters
| Name | Type | Description |
|---|---|---|
subaccount | address | The Safe (subaccount) the call was executed on. |
AllowanceManager
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:
- A freshly deployed
AllowanceHolder(preferred – no approvals necessary after the initial permit) - 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
| Name | Type | Description |
|---|---|---|
_permit2 | address | Address of the Permit2 contract. |
factoryAddr | address | Canonical 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
| Name | Type | Description |
|---|---|---|
owner | address | The owner of the allowance (signer of the permit given to the holder). |
subaccount | address | Subaccount that the allowance applies to. |
token | address | ERC-20 token address. |
amount | uint256 | Allowance amount. |
timeframe | uint256 | Time 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
| Name | Type | Description |
|---|---|---|
owner | address | The token owner. |
token | address | The ERC-20 token managed by the holder. |
holder | address | The 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
| Name | Type | Description |
|---|---|---|
holder | address | The 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
| Name | Type | Description |
|---|---|---|
owner | address | The token owner. |
token | address | The ERC-20 token. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | address | holder 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
| Name | Type | Description |
|---|---|---|
factoryAddress | address | The address of the canonical SubaccountFactory. |
permit2
The Permit2 contract address
function permit2() external view returns (address);
Returns
| Name | Type | Description |
|---|---|---|
<none> | address | permit2Addr 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
| Name | Type | Description |
|---|---|---|
owner | address | The owner of the subaccount. |
subaccount | address | The subaccount that the allowance is set for. |
token | address | The token that the allowance is set for. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | Allowance | allowance The allowance struct. |
allowanceRequestBatchTypehash
EIP-712(x) typehash for the AllowanceRequestBatch struct (per-subaccount)
function allowanceRequestBatchTypehash() external pure returns (bytes32);
Returns
| Name | Type | Description |
|---|---|---|
<none> | bytes32 | typeHash The struct type-hash. |
allowanceRequestTypehash
EIP-712(x) typehash for the AllowanceRequest struct (per-subaccount)
function allowanceRequestTypehash() external pure returns (bytes32);
Returns
| Name | Type | Description |
|---|---|---|
<none> | bytes32 | typeHash The struct type-hash. |
setAllowances
Sets allowances for a batch of subaccounts
function setAllowances(address owner, AllowanceRequestBatch calldata requests, bytes calldata signature) public;
Parameters
| Name | Type | Description |
|---|---|---|
owner | address | The owner of the subaccounts |
requests | AllowanceRequestBatch | The AllowanceRequestBatch struct containing the requests |
signature | bytes | The 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
| Name | Type | Description |
|---|---|---|
owner | address | The owner of the subaccount |
token | address | The token to spend the allowance for |
amount | uint256 | The 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
| Name | Type | Description |
|---|---|---|
batch | AllowanceRequestBatch | The AllowanceRequestBatch struct to hash. |
Returns
| Name | Type | Description |
|---|---|---|
structHash | bytes32 | The 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:
- The function is
internaland only reached viaspendAllowance, after* the allowance window and per-subaccount quota checks have passed. - 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.
- 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
| Name | Type | Description |
|---|---|---|
owner | address | The owner of the allowance. |
subaccount | address | The subaccount that the allowance is transferred to. |
token | address | The token that the allowance is transferred for. |
amount | uint256 | The 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
| Name | Type | Description |
|---|---|---|
owner | address | The owner address. |
token | address | The ERC-20 token address. |
Returns
| Name | Type | Description |
|---|---|---|
holder | address | The 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
| Name | Type | Description |
|---|---|---|
name | string | The domain name. |
version | string | The 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
| Name | Type | Description |
|---|---|---|
holder | address | The holder contract that was called. |
subaccount | address | The subaccount the funds were destined to. |
amount | uint256 | The 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
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
| Name | Type | Description |
|---|---|---|
dataHash | bytes32 | Keccak256 hash of the encoded transaction data. |
data | bytes | The original pre-image that was signed. |
signatures | bytes | Concatenated signatures. |
MultiChainSignaturesModule
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
| Name | Type | Description |
|---|---|---|
_siglessTransactionExecutor | address | Address 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
| Name | Type | Description |
|---|---|---|
account | address | The Safe account the batch applies to. |
safeTxBatch | SafeTxBatch | Batch struct containing transactions and their intended chainIds. |
signatures | bytes | Concatenated owner signatures over the EIP-712 batch preimage. |
Returns
| Name | Type | Description |
|---|---|---|
success | bool | True 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
| Name | Type | Description |
|---|---|---|
safeTxBatch | SafeTxBatch | Batch struct containing the transactions and their target chainIds. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bytes | encodedData 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
| Name | Type | Description |
|---|---|---|
separator | bytes32 | The 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
| Name | Type | Description |
|---|---|---|
safe | address | The Safe account that attempted the execution. |
index | uint256 | The 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
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
| Name | Type | Description |
|---|---|---|
_siglessTransactionExecutor | address | Address of the SiglessTransactionExecutor library. |
_safeSingleton | address | Canonical Safe singleton implementation. |
_safeProxyFactory | address | Safe proxy factory address. |
_safeModuleSetup | address | Helper used to enable modules during Safe setup. |
_permit2 | address | Permit2 contract used by AllowanceManager. |
ISafe
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
| Name | Type | Description |
|---|---|---|
to | address | Destination address of module transaction. |
value | uint256 | Ether value of module transaction. |
data | bytes | Data payload of module transaction. |
operation | uint8 | Operation 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
| Name | Type | Description |
|---|---|---|
to | address | Destination address of module transaction. |
value | uint256 | Ether value of module transaction. |
data | bytes | Data payload of module transaction. |
operation | uint8 | Operation type of module transaction. |
Returns
| Name | Type | Description |
|---|---|---|
success | bool | Boolean flag indicating if the call succeeded. |
returnData | bytes | Data 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
| Name | Type | Description |
|---|---|---|
dataHash | bytes32 | Hash of the data (could be either a message hash or transaction hash) |
data | bytes | That should be signed (this is passed to an external validator contract) |
signatures | bytes | Signature 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
| Name | Type | Description |
|---|---|---|
<none> | bytes32 | bytes32 The domain separator hash. |
getModulesPaginated
Returns array of modules.
function getModulesPaginated(address start, uint256 pageSize)
external
view
returns (address[] memory array, address next);
Parameters
| Name | Type | Description |
|---|---|---|
start | address | Start of the page. |
pageSize | uint256 | Maximum number of modules that should be returned. |
Returns
| Name | Type | Description |
|---|---|---|
array | address[] | Array of modules. |
next | address | Start 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
| Name | Type | Description |
|---|---|---|
module | address | Module to be enabled. |
getThreshold
Returns the number of required confirmations for a Safe transaction aka the threshold.
function getThreshold() external view returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Threshold number. |
SafeModuleSetup
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
| Name | Type | Description |
|---|---|---|
modules | address[] | The modules to enable. |
SubaccountAddressMismatch
error SubaccountAddressMismatch();
HolderAddressMismatch
error HolderAddressMismatch();
AllowanceManagerRegistrationFailed
error AllowanceManagerRegistrationFailed();
SubaccountFactory
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
| Name | Type | Description |
|---|---|---|
_safeSingleton | address | Address of the Safe singleton (logic contract). |
_safeProxyFactory | address | Address of the SafeProxyFactory. |
_safeModuleSetup | address | Address of the helper contract for module setup. |
_multichainSignaturesModule | address | Address of the MultiChainSignaturesModule to enable. |
_allowanceManager | address | Address 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
| Name | Type | Description |
|---|---|---|
owner | address | Token owner that signed the permit. |
token | address | ERC-20 token address. |
subaccount | address | Subaccount that will spend from the allowance. |
quotaAmount | uint256 | Per-timeframe allowance amount. |
quotaTimeframe | uint256 | Seconds per allowance reset window. |
initialFund | uint256 | Optional initial transfer executed by the holder constructor. |
firstCallData | bytes | Optional calldata executed on the subaccount during holder construction. |
permitDeadline | uint256 | Deadline used in the ERC-2612 permit. |
v | uint8 | Signature v component of the permit. |
r | bytes32 | Signature r component of the permit. |
s | bytes32 | Signature s component of the permit. |
Returns
| Name | Type | Description |
|---|---|---|
holder | address | The 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
| Name | Type | Description |
|---|---|---|
owner | address | The future owner of the subaccount. |
appDomain | string | Application domain to namespace the subaccount (e.g. "my.cool.app"). |
accountIndex | uint32 | Sequential index chosen by the owner (use 0 for the first account). |
Returns
| Name | Type | Description |
|---|---|---|
subaccount | address | The 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
| Name | Type | Description |
|---|---|---|
owner | address | The owner that will control the subaccount. |
appDomain | string | Application domain used to namespace the subaccount. |
accountIndex | uint32 | Sequential index for the domain chosen by the owner. |
Returns
| Name | Type | Description |
|---|---|---|
subaccount | address | Predicted Safe proxy address. |
isDeployed | bool | Boolean 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
| Name | Type | Description |
|---|---|---|
owner | address | The token owner. |
token | address | ERC-20 token address. |
subaccount | address | Subaccount that will draw from the allowance. |
quotaAmount | uint256 | Quota amount per timeframe (passed to constructor). |
timeframe | uint256 | Quota reset window in seconds. |
initialFund | uint256 | Optional initial transfer executed by the holder constructor. |
firstCallData | bytes | Optional calldata executed on subaccount by the holder constructor. |
Returns
| Name | Type | Description |
|---|---|---|
holder | address | Predicted AllowanceHolder address. |
isDeployed | bool | Whether 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
| Name | Type | Description |
|---|---|---|
owner | address | Token owner that signed the permit. |
token | address | ERC-20 token address. |
spender | address | Address that receives unlimited allowance (the AllowanceHolder). |
deadline | uint256 | Permit deadline (same as in signature). |
v | uint8 | Signature v value. |
r | bytes32 | Signature r value. |
s | bytes32 | Signature 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
| Name | Type | Description |
|---|---|---|
owner | address | Token owner. |
token | address | ERC-20 token managed by the holder. |
subaccount | address | Subaccount that will draw from the allowance. |
quotaAmount | uint256 | Per-timeframe allowance. |
quotaTimeframe | uint256 | Time window for resets. |
initialFund | uint256 | Optional immediate transfer. |
firstCallData | bytes | Optional call executed on subaccount. |
predictedHolder | address | Pre-computed address where the holder must be deployed. |
Returns
| Name | Type | Description |
|---|---|---|
holder | address | Deployed 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
| Name | Type | Description |
|---|---|---|
owner | address | The address to be set as the sole owner of the Safe. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bytes | The 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
| Name | Type | Description |
|---|---|---|
appDomain | string | The application domain string (e.g., "my.cool.app"). |
accountIndex | uint32 | The index of the account in the app domain. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | The 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
| Name | Type | Description |
|---|---|---|
owner | address | Token owner. |
token | address | ERC-20 token. |
manager | address | AllowanceManager address. |
subaccount | address | Subaccount address. |
amount | uint256 | Allowance amount. |
timeframe | uint256 | Timeframe seconds for resets. |
initialFund | uint256 | Optional initial fund. |
firstCallHash | bytes32 | Keccak256 hash of firstCallData. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bytes32 | salt 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
| Name | Type | Description |
|---|---|---|
owner | address | Token owner. |
token | address | ERC-20 token. |
manager | address | AllowanceManager address. |
subaccount | address | Subaccount. |
amount | uint256 | Allowance amount. |
timeframe | uint256 | Timeframe seconds. |
initialFund | uint256 | Initial fund amount. |
firstCallData | bytes | Calldata executed on subaccount. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bytes | creationCode 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
| Name | Type | Description |
|---|---|---|
deployer | address | Address performing the CREATE2. |
owner | address | Token owner (used in salt). |
token | address | ERC-20 token. |
manager | address | AllowanceManager address. |
subaccount | address | Subaccount. |
amount | uint256 | Allowance amount. |
timeframe | uint256 | Timeframe seconds. |
initialFund | uint256 | Initial fund amount. |
firstCallData | bytes | Encoded first call data. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | address | addr 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
| Name | Type | Description |
|---|---|---|
owner | address | The address to include in the array. |
Returns
| Name | Type | Description |
|---|---|---|
owners | address[] | 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
| Name | Type | Description |
|---|---|---|
subaccount | address | The deployed Safe proxy address. |
owner | address | The owner address passed to the subaccount. |
appDomain | string | The app-domain string that namespaced this subaccount. |
accountIndex | uint32 | The 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
| Name | Type | Description |
|---|---|---|
holder | address | Deployed AllowanceHolder address. |
owner | address | The token owner that signed the pull-permit. |
token | address | The ERC-20 token the holder controls. |