Native Authorization-Based Transfer Module for the Cosmos SDK
Summary
This topic proposes x/wauth (WAuth => “with authorization”), a new Cosmos SDK module that introduces off-chain-signed, on-chain-settled token transfers. The design is semantically equivalent to Ethereum’s EIP-3009 (transferWithAuthorization) but implemented as a first-class Cosmos SDK module with no EVM dependency. A working reference implementation targeting cosmos/evm is available and production-ready.
Motivation
The Cosmos SDK’s transfer model requires the token sender to submit and pay for every transaction. This is the right default, but it makes an entire class of application patterns difficult or impossible to implement cleanly:
-
Meta-transactions / gas abstraction: end users should not need to hold the native fee token to interact with an application.
-
Delegated and pull payments: a recipient or relayer should be able to settle a pre-authorized payment without requiring another round-trip to the sender.
-
Machine-to-machine and API-triggered payments: services built on the x402 pay-per-request standard need to settle blockchain payments in response to HTTP events, not interactive wallet sessions.
-
Subscription and batch billing: a billing relayer needs to settle multiple pre-signed authorizations in a single transaction.
EVM chains address this with EIP-2612 (permit) and EIP-3009 (transferWithAuthorization). These are now ubiquitous building blocks in that ecosystem. Cosmos has x/authz and x/feegrant, which solve adjacent problems (persistent delegated execution grants and fee sponsorship, respectively), but neither provides a stateless, single-use signed payment primitive. x/wauth fills that gap.
Design
Authorization Structure
An Authorization is a signed, off-chain payment intent with the following fields:
| Field | Type | Description |
|---|---|---|
from |
sdk.AccAddress |
Spender (signs the authorization off-chain) |
to |
sdk.AccAddress |
Recipient |
coins |
sdk.Coins |
Multi-denom transfer amount |
nonce |
[32]byte |
Cryptographically random, single-use value |
valid_after |
time.Time |
Optional activation time |
valid_before |
time.Time |
Required expiry deadline |
chain_id |
string |
Prevents cross-chain replay attacks |
memo |
string |
Relayer annotation (excluded from signing digest) |
The memo field is deliberately excluded from the signing digest, sha256(gogoproto.Marshal(authorization_with_memo_cleared)) so relayers can annotate transactions without invalidating the user’s signature.
Message Types
The module exposes four messages, mirroring the EIP-3009 interface:
| Message | Submitter | EIP-3009 Analogue |
|---|---|---|
MsgTransferWithAuthorization |
Relayer (facilitator) | transferWithAuthorization |
MsgReceiveWithAuthorization |
Recipient | receiveWithAuthorization |
MsgMultiTransferWithAuthorization |
Relayer | (batch extension - no EVM equivalent) |
MsgCancelAuthorization |
Original signer | cancelAuthorization |
MsgTransferWithAuthorization
A third-party facilitator submits a user-signed authorization. The chain verifies the sender’s signature, checks the nonce, validates the time window, and calls bankKeeper.SendCoins. The facilitator pays gas; the sender’s funds move without any on-chain action from the sender.
MsgReceiveWithAuthorization
The designated recipient submits the authorization. The receiver field must match authorization.to, preventing front-running by other parties.
MsgMultiTransferWithAuthorization
A batch message supporting up to 100 (authorization, signature) pairs. Each item is validated and settled independently; per-item failures are reported in the response without rolling back the entire batch. This is essential for billing relayers settling many subscriptions in a single block.
MsgCancelAuthorization
Marks a nonce as consumed without executing a transfer, allowing users to revoke an outstanding authorization before it is settled.
Replay Protection
Each nonce is a 32-byte cryptographically random value (not a counter). After settlement or cancellation, the nonce is recorded in the module’s KV store keyed by (sender_address, nonce). Nonce records carry a prune_after timestamp (valid_before + nonce_prune_grace_period) to allow safe cleanup without a pruning race condition.
Signature Verification
The signing digest is sha256(proto_marshal(authorization_without_memo)). Verification supports:
-
secp256k1 (Cosmos-native): standard
VerifySignatureover the 32-byte SHA-256 digest. -
eth_secp256k1 (EVM-compatible): native compact signature verification using standard ECDSA semantics.
Signatures are 64-byte compact (R+S) or 65-byte with recovery bit; the recovery bit is stripped before verification.
Module Parameters (governance-controlled)
| Parameter | Default | Constraint |
|---|---|---|
max_valid_window_seconds |
3600 (1 hour) |
≤ 2,592,000 (30 days) |
nonce_prune_grace_period_seconds |
600 (10 min) |
≥ 0 |
enabled_denoms |
[] (all allowed) |
Optional governance allowlist |
### Relationship to Existing Modules
| Module | Purpose | Difference from x/wauth |
|---|---|---|
x/authz |
Persistent, re-usable execution grants | Grants persist until revoked; x/wauth authorizations are single-use and stateless until settled. |
x/feegrant |
Fee sponsorship for the grantee’s transactions | Pays gas for on-chain txs; does not settle token transfers. |
x/bank |
Core transfer primitive | x/wauth calls bankKeeper.SendCoins — it is a composable layer above, not a replacement. |
x402 Compatibility
x/wauth is designed as the settlement layer for Coinbase/x402, a protocol for HTTP-native blockchain payments. In this model:
-
A client signs an
Authorizationoffline and includes it in an HTTP request header. -
The API server or a payment processor verifies the signature, serves the request, and submits
MsgTransferWithAuthorizationto settle. -
The chain settles the payment atomically on the next block.
This enables pay-per-request APIs, metered subscriptions, and IoT micropayments without requiring the client to interact with the chain directly during the payment flow. The MsgMultiTransferWithAuthorization batch message supports settlement aggregation across many requests per block.
Security Considerations
-
Replay protection: enforced by 32-byte random nonces (not counters) to prevent ordering attacks. Cross-chain replay is blocked by the mandatory
chain_idfield in the signing payload. -
Front-running on
ReceiveWithAuthorization: the receiver address is locked in the signed payload. A front-runner submittingMsgTransferWithAuthorizationwould succeed as a facilitator (funds still go to the correct recipient) but cannot redirect funds. -
Griefing via cancellation:
MsgCancelAuthorizationis signed by the originating account, so only the sender can cancel their own nonces. -
Time window bounds:
max_valid_window_seconds(default 1h) is enforced on-chain to prevent authorizations with indefinitely long validity windows. -
Denom allowlist: chain operators can restrict
x/wauthto specific token denominations via governance. -
Blocked addresses:
bankKeeper.BlockedAddris respected; module accounts and other restricted addresses cannot receive funds throughx/wauth.
Reference Implementation
A complete, tested implementation is available targeting cosmos/evm. It includes:
-
Protobuf definitions for all message and query types
-
Keeper with nonce store and governance-controlled params
-
Full
ValidateBasicand keeper-level validation -
CLI commands for all message types and queries
-
Unit and integration tests
I am prepared to port this to cosmos/cosmos-sdk as a standalone module or as an extension under x/bank.
Questions for the Team
-
Scope: Should this be proposed as a core SDK module, or a standalone maintained module? If core, which repository is the right target (
cosmos-sdkorcosmos/evm)? -
Signing standard: The current digest is
sha256(proto_marshal(auth)). Is there interest in aligning with a Cosmos-native typed signing standard (e.g. SignDoc-compatible) to support hardware wallets and Ledger out of the box?
I would be pleased to provide any additional information upon request. I would also greatly appreciate any feedback or suggestions regarding potential future directions.
Tillo Tulkinbekov
Blockchain Engineer, Gurufin - South Korea
