getGaslessWalletExecuteSelectors
Build the exact selector set a session key must hold to run a batch from the gasless wallet — the wallet-execution sentinel plus every inner call’s selector.
import { getGaslessWalletExecuteSelectors, grantDelegation, gaslessWalletExecute } from "@symmio/trading-core";
const calls = [{ target: usdcAddress, abi: erc20Abi, functionName: "transfer", args: [to, amount] }];
await grantDelegation(config, {
account: { addr: subAccount, isPartyB: false },
delegatedSigner: sessionKey,
selectors: getGaslessWalletExecuteSelectors(calls),
expiryTimestamp,
});
await gaslessWalletExecute(config, { calls, signerAccount: subAccount, from: sessionKey });Why every inner selector
The GaslessLayer checks each inner call individually, on top of the sentinel:
if (signedOp.signer == ownerWallet) return; // owner-signed: no delegation at all
_assertInstantDelegation(instantLayer, canonicalAccount, signedOp.signer, WALLET_EXECUTION_SENTINEL_SELECTOR);
for (uint256 i = 0; i < calls.length; i++)
_assertInstantDelegation(instantLayer, canonicalAccount, signedOp.signer, selectorOf(calls[i]));Granting only the sentinel is not enough, and granting the execute wrapper selector (0x3f707e6b) does nothing — it is never checked. A missing grant reverts WalletDelegationMissing(delegator, delegate, selector).
Selectors are derived from the encoded calldata, not from functionName, so an overloaded ABI resolves to the selector the wallet will actually run. Calldata shorter than four bytes yields 0x00000000, matching the contract’s own reading of it.
The delegation must be granted on a sub-account, never on the owner EOA. grantDelegation is onlyAccountOwner,
and the AccountLayer reports no owner for a bare EOA — so an EOA delegator can never be authorized. Pass that same
sub-account to gaslessWalletExecute as signerAccount.
Scope it deliberately
The returned set is scoped to one batch. A key granted transfer can move the wallet’s whole balance to any recipient, because the recipient is a call argument and delegations bind selectors, not arguments. Grant the narrowest batch you can, and set a short expiryTimestamp.
The result is de-duplicated and lowercased — grantDelegation rejects an array containing duplicates, which repeats easily produce (two approve calls on different tokens share one selector).
Parameters
callsreadonly GaslessWalletCall[]requiredThe batch the session key must be able to run. Both call forms are accepted.
Returns
readonly Hex[]The selector set to grant, sentinel first, then each inner selector in call order.