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.