> ## Documentation Index
> Fetch the complete documentation index at: https://docs.velumx.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Paymaster pattern

# The Paymaster Pattern

A paymaster is a Clarity contract that atomically collects a SIP-010 fee from the user and executes a protocol action in a single transaction.

## When Do You Need a Paymaster?

| Policy                  | Paymaster Needed?                                 |
| :---------------------- | :------------------------------------------------ |
| **DEVELOPER\_SPONSORS** | ❌ No — call any contract directly                 |
| **USER\_PAYS**          | ✅ Yes — fee must be collected on-chain atomically |

For DEVELOPER\_SPONSORS, you build any contract call as a sponsored transaction and call `velumx.sponsor()`. No paymaster contract involved.

For USER\_PAYS, you need a paymaster because the SIP-010 fee transfer must happen atomically with the protocol action. If the action fails, the fee reverts. This guarantee requires an on-chain contract.

## The Pattern

Every paymaster function follows the same three-step pattern:

```clarity theme={null}
(define-public (my-action
    (... action params ...)
    (fee-amount uint)
    (relayer principal)
    (fee-token <sip-010-trait>))
  (let ((user tx-sender))
    ;; Step 1: Collect fee atomically
    (try! (collect-fee fee-token fee-amount relayer))

    ;; Step 2: Execute protocol action
    (try! (contract-call? 'SP...target-contract action-function ...))

    ;; Step 3: Emit event
    (print { event: "my-action", user: user, fee-amount: fee-amount })
    (ok true)
  )
)
```

**Atomicity guarantee**: If Step 2 fails, the entire transaction reverts — including the fee transfer in Step 1. Users are never charged without their action executing.

## The VelumX DeFi Reference Paymaster

VelumX deploys [`velumx-defi-paymaster-v1-1`](https://github.com/VelumX/VelumX-Contracts/blob/main/contracts/contracts/velumx-defi-paymaster-v1-1.clar) for the VelumX DeFi frontend. It supports:

* Bitflow StableSwap pool swaps (all pools, both directions)
* Bitflow multi-hop router swaps (all router contracts)
* Velar XYK and stableswap router swaps (single-hop through 4-hop)
* USDCx bridge (burn for cross-chain withdrawal to Ethereum)

This contract is the canonical reference implementation. Developers copy it as a starting point for their own paymaster contracts.

## Building Your Own Paymaster

1. Copy [`velumx-defi-paymaster-v1-1.clar`](https://github.com/VelumX/VelumX-Contracts/blob/main/contracts/contracts/velumx-defi-paymaster-v1-1.clar) from the VelumX Contracts repo
2. Replace the Bitflow/USDCx calls with your own protocol calls
3. Keep the `collect-fee` private function exactly as-is
4. Deploy to mainnet
5. Point your SDK calls at your paymaster contract address

### The `collect-fee` Function

This is the only piece of the paymaster that must stay unchanged. It handles the atomic fee transfer and enforces the two security invariants:

```clarity theme={null}
(define-constant ERR-ZERO-FEE      (err u101))
(define-constant ERR-SELF-TRANSFER (err u102))

(define-private (collect-fee
    (fee-token <sip-010-trait>)
    (fee-amount uint)
    (relayer principal))
  (let ((user tx-sender))
    (asserts! (> fee-amount u0) ERR-ZERO-FEE)
    (asserts! (not (is-eq user relayer)) ERR-SELF-TRANSFER)
    (contract-call? fee-token transfer fee-amount user relayer none)
  )
)
```

* `ERR-ZERO-FEE` — prevents zero-fee calls that would let users bypass the fee entirely.
* `ERR-SELF-TRANSFER` — prevents the user from setting themselves as the relayer to collect their own fee.

### Minimal Paymaster Example

Replace `do-thing` with your own protocol call. The fee args (`fee-amount`, `relayer`, `fee-token`) are always the last three parameters by convention.

```clarity theme={null}
;; my-protocol-paymaster-v1.clar
(use-trait sip-010-trait .sip-010-trait.sip-010-trait)

(define-constant ERR-ZERO-FEE      (err u101))
(define-constant ERR-SELF-TRANSFER (err u102))

(define-private (collect-fee
    (fee-token <sip-010-trait>)
    (fee-amount uint)
    (relayer principal))
  (let ((user tx-sender))
    (asserts! (> fee-amount u0) ERR-ZERO-FEE)
    (asserts! (not (is-eq user relayer)) ERR-SELF-TRANSFER)
    (contract-call? fee-token transfer fee-amount user relayer none)
  )
)

(define-public (my-action
    (amount uint)
    (fee-amount uint)
    (relayer principal)
    (fee-token <sip-010-trait>))
  (let ((user tx-sender))
    ;; 1. Collect fee atomically — reverts with the action if the protocol call fails
    (try! (collect-fee fee-token fee-amount relayer))
    ;; 2. Execute your protocol action
    (try! (contract-call? 'SP...my-protocol do-thing user amount))
    ;; 3. Emit structured event for off-chain indexing
    (print { event: "my-action", user: user, amount: amount, fee-amount: fee-amount })
    (ok true)
  )
)
```

### Calling Your Paymaster from the SDK

The relayer address **always comes from `estimateFee()`** — never hardcode it. The relayer address can change when VelumX rotates keys or reassigns your project to a different node.

```typescript theme={null}
import { buildSponsoredContractCall } from '@velumx/sdk';
import { uintCV, principalCV, contractPrincipalCV } from '@stacks/transactions';

const estimate = await velumx.estimateFee({
  feeToken: 'SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.aeusdc',
  estimatedGas: 150_000,
});

// Guard: relayerAddress is required for USER_PAYS
if (!estimate.relayerAddress) throw new Error('Relayer address missing from fee estimate');

const unsignedTx = await buildSponsoredContractCall({
  contractAddress: 'SP...your-paymaster',
  contractName: 'my-protocol-paymaster-v1',
  functionName: 'my-action',
  functionArgs: [
    uintCV(1_000_000n),                                                            // amount
    uintCV(BigInt(estimate.maxFee)),                                               // fee-amount
    principalCV(estimate.relayerAddress),                                          // relayer — from estimateFee
    contractPrincipalCV('SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR', 'aeusdc'), // fee-token
  ],
  publicKey: userPublicKey,
});

const txForSigning = unsignedTx instanceof Uint8Array
  ? Buffer.from(unsignedTx).toString('hex')
  : unsignedTx;

const signResult = await request('stx_signTransaction', {
  transaction: txForSigning,
  broadcast: false,
});

const { txid } = await velumx.sponsor(signResult.transaction, {
  feeToken: estimate.feeToken,
  feeAmount: estimate.maxFee,
});
```

## What the Paymaster Does NOT Do

* No on-chain token whitelist — the relayer validates supported tokens off-chain
* No fee cap enforcement — the relayer enforces caps per API key
* No admin key or upgrade mechanism — the contract is immutable once deployed
* No VelumX-specific registry or trait required

The paymaster is a simple, stateless contract. The relayer handles all policy.
