SignV2
execution, on both the All path and the limited path.
This feature is pending the authorization-lock program release. The Protocol SDK
TypeScript implementation lives in swig-protocol-sdk.
The Rust port and hosted Developer SDK support are under review; the published
Rust protocol crates at 3.0.0 do not expose the lock APIs below.
What a lock covers
Each lock records:
A lock is active while
amount > 0 and current_slot < expiry_slot. Expired
and fully captured locks (amount == 0) no longer bind anything, but they stay
stored until someone prunes them. A wallet can store at most 32 locks, inactive
ones included.
How locks are enforced
Other roles are held off
While a lock is active, every role other than its holder can spend only the account’s balance minus the active locks that role does not hold. This includesAll, AllButManageAuthority, and the owner. After each SignV2,
each writable locked account must end at or above
min(balance_before, reserved_by_others), where reserved_by_others is the sum
of the active locks on that account held by roles other than the signer.
The account can always receive funds. Native SOL balances only count lamports
above the wallet address’s rent-exempt minimum.
The holder spends, and spending captures
The holder’s own locks don’t restrict it. When the holder signs aSignV2, the
program measures each locked account’s net outflow (before - after, never
negative) and takes it off the holder’s locks on that account. The oldest lock
(lowest lock_id) goes first, and no lock goes below 0. The new amounts are
written back to the config account in place.
Outflow beyond the holder’s locks comes from free balance. The holder still
can’t spend into another role’s lock, because the floor above applies to it
too. Inflows never raise a lock.
A lock captured to 0 stays stored with amount == 0. It is inactive, and it
stays stored until it is pruned with RemoveAuthorizationLockV1 or removed
along with its holder’s role.
Locks don’t change what SignV2 needs or touches:
- it needs no extra accounts because of locks. A transaction’s accounts are the same with or without them;
- it never resizes the config account or moves lamports for locks. A capture only rewrites the lock’s stored amount in place.
Worked example
The wallet holds 100 SOL above its rent-exempt minimum. Bob’s role holdsManageAuthorizationLocks and a 100 SOL limit.
Bob’s limit is charged on each spend: 25, then 45, leaving 30. After the last
step his lock is inactive. It no longer blocks anything, and any admin role can
prune it.
With several holders, each is held off the others’ locks. If the balance is 100,
Bob holds 50 and Carol holds 30, then Bob can bring the balance down to 30,
Carol to 50, and every other role to 80.
Other protections
These apply while any lock on the account is active, whoever signs, the holder included:- A locked token account must keep the wallet as its authority, with no delegate and no foreign close authority. A mint’s freeze authority can still freeze or thaw the account, and a frozen account stays guarded.
- The wallet cannot be closed with
CloseSwigV1, and a locked token account cannot be closed withCloseTokenAccountV1.
Who can create a lock
AddAuthorizationLockV1 creates a lock. Each lock carries its own amount and
expiry. The acting role can use any authority type, including ProgramExec and
ProgramExecSession, and must hold ManageAuthorizationLocks { max_duration_slots }. All alone is not enough.
- Duration (every role):
expiry_slot - current_slotmust be at most the role’smax_duration_slots. - Amount, for roles that can spend everything: a role that also holds
AllorAllButManageAuthoritycan lock up to the account’s unreserved balance. - Amount, for every other role: the role can lock any SOL or token asset,
but its active locks on that asset, including the new one, must fit within
what its own spending limits for the asset still allow. For example, a role
with
ManageAuthorizationLocksand limits on SOL, USDC and USDT can lock each of those up to its limit, and cannot lock an asset it has no limit for. - Every role: active locks on an account, from all holders, can’t exceed its
balance (
AuthorizationLockExceedsBalance).
max_duration_slots. Another role with
ManageAuthority (or All) grants or changes it, and a role may always drop
the permission from itself. The root role is the exception: no other role can
manage it, so it sets its own bound.
The payer funds the config account’s rent growth for each new lock.
Limits are charged on spend
Locking checks the holder’s limits but charges nothing. The holder’s normal SOL, token, recurring and destination limits are charged when it spends throughSignV2, just as without a lock.
For a limited holder, SignV2 runs in this order: execute, charge limits,
capture locks, then check that the holder’s remaining active locks on each mint
still fit what its limits allow. If they don’t, the transaction fails with
AuthorizationLockExceedsSpendLimit, so a role can’t keep a hold it could never
spend. Roles with All or AllButManageAuthority have no limits to check.
For example, Bob has a 1,000 token limit and a 600 lock on token account A:
- spending 400 from another token account B leaves a 600 limit, which still covers the 600 lock;
- spending 401 from B fails, because 599 would not cover 600;
- spending from A charges the limit and captures the lock by the same amount, so the two fall together.
SignV2 would allow at the current slot. One-shot limits use
their remaining amount. Recurring limits use what’s left in the current window,
or the full allowance once the window would reset. With both a general and a
destination limit, the smaller applies.
Releasing and pruning
RemoveAuthorizationLockV1 deletes a lock. The holder doesn’t need to remove a
lock to spend it.
- Release: the holder can remove its own lock at any time. This voids whatever is still reserved.
- Prune: after a lock expires or is fully captured, any role holding
All,AllButManageAuthority,ManageAuthority, orManageAuthorizationLockscan remove it to reclaim space. The holder can too. - No other role can remove an active lock. This includes the root
Allrole. - With the role: removing a holder’s role with
RemoveAuthorityV1also prunes its inactive locks. See Removing a lock holder.
Instruction accounts
AddAuthorizationLockV1 (discriminator 25):
RemoveAuthorizationLockV1 (discriminator 26):
Authority accounts follow, as for other instructions: the Ed25519 or session
key signer, or the instructions sysvar for Secp256r1 and
ProgramExec roles.
The Swig config and wallet address come first so ProgramExec roles can
authorize locks. The preceding instruction that a ProgramExec role
authenticates against must pass the Swig config and the wallet address, in that
order, as its first two accounts. A ProgramExecSession role opens a session
through its program as usual, then signs lock instructions with the session
key.
Effect on role management
A lock is only useful if its holder stays able to spend it. While a role holds any active lock, even if its other locks are inactive:- it cannot be removed with
RemoveAuthorityV1, not even by itself. This fails withAuthorizationLockActive - another role cannot replace its authority with
ReplaceAuthorityV1. Its locks don’t block it from rotating its own authority - another role cannot update its actions with
UpdateAuthorityV1, except for aRemoveActionsByTypethat removes onlyManageAuthorizationLocks
ManageAuthorizationLocks stops the role from creating new locks.
Existing locks still run to expiry, and the holder can still spend or release
them. A delegate therefore cannot make itself irrevocable by continually
renewing locks.
Once all of a role’s locks are inactive (expired or fully captured), these
restrictions lift. UpdateAuthorityV1 and ReplaceAuthorityV1 then work as
usual and leave the role’s lock records in place.
Removing a lock holder
A role whose locks are all inactive can be removed withRemoveAuthorityV1.
The usual permission rules apply. A role with All or ManageAuthority can
remove it, and so can the role itself. AllButManageAuthority can’t remove
other roles.
The same instruction prunes the role’s lock records:
- its inactive locks are deleted, and their rent is refunded with the role’s, to the rent claimer if one is set, otherwise to the payer
- other roles’ locks and the wallet’s lock-id counter are kept
- role ids and lock ids are never reused, so a role added later inherits nothing from the removed one
max_duration_slots, the
longest a role can be pinned is the duration someone else granted it.
Locks require a V2 wallet. For a legacy wallet, first use
MigrateToWalletAddressV1, described in the
program walkthrough.
An existing V2 wallet’s root role cannot lock until it grants itself
ManageAuthorizationLocks, for example with UpdateAuthorityV1 AddActions
on role 0.Durations are measured in slots
max_duration_slots and expiry_slot count slots, not seconds. Slot time is
dropping toward 200ms, so a duration sized for 400ms slots ends sooner in
wall-clock time than intended. See
time-based limits.

