Why an allowance is needed
A self-custody account holder controls their own wallet. For Venly to move that wallet’s tokens — for an account-to-account transfer or a PULL pay-out — the wallet must grant Venly’s orchestration wallet an ERC-20 allowance — permission to pull funds viatransferFrom. Which assets your tenant can settle in is configuration, not a fixed list — read GET /supported-assets rather than hard-coding one.
Gasless permits are a property of the token contract on a specific chain, not of the symbol. The same symbol can support permits on one chain and not another. Check with your Venly contact before committing to an asset that isn’t already enabled for you.
Why a permit instead of a plain approve
A normal approve() costs gas and needs the wallet to hold the chain’s native coin. Instead, the holder signs an EIP-2612 message (off-chain, no gas) and Venly’s orchestration wallet submits it on-chain and pays the gas.
A permit needs a wallet controlled by a single key. Smart-contract wallets — a Safe, for example — can’t produce that signature and are not supported for now.
The permit flow (self-custody account wallet)
1
Get the message to sign
GET /accounts/{accountId}/wallets/{walletId}/permits returns, per asset, a supportedAssetId and an EIP-712 typedData object. walletId sets the chain (e.g. Base vs Avalanche). The nonce and the token’s domain name/version are read from the contract for you.2
Sign it with the owner's key
Sign
typedData with the key for the wallet’s owner address. The signature must recover to the owner — a signature from any other wallet makes the permit FAILED. Produces v, r, s.3
Submit the signature
POST .../permits with the supportedAssetId and the signature. Returns HTTP 200 with result.status — check the status, not just the code. Re-submitting an already-confirmed permit returns 409.4
Wait for CONFIRMED
Settlement is asynchronous — poll
GET .../permits until CONFIRMED (or FAILED). On CONFIRMED the allowance is live and Venly can move the wallet’s funds.Permit status lifecycle
Signing the message
Take thetypedData from the permit response and sign it with the owner’s key. With ethers.js:
v, r, and s as strings in the signature body — ethers returns v as a number, so convert it (String(v)). Use the supportedAssetId from the GET .../permits response:
Verifying the allowance
Before initiating a payment or transfer, confirm the allowance is in place:orchestrationWallet that holds the allowance, and the allowance as a human-readable decimal (on-chain raw value ÷ 10^decimals). The authoritative signal is the permit status: CONFIRMED (above) — not the allowance figure. An unlimited permit reports a very large allowance (uint256-max), but the value can also be finite; use this endpoint to read the current spending headroom.
Once a permit is
CONFIRMED, the allowance persists until it’s used up — the holder doesn’t sign again for every transfer.Next steps
Get wallet allowances
Confirm the allowance is live before moving funds.
Create a crypto transfer
Move funds once the permit is
CONFIRMED.
