Skip to content

Standardize Gas Sponsorship Fallback Pattern #49

@azeemshaik025

Description

@azeemshaik025

Title

[Gasless] Standardize fallback pattern for sponsored→user_pays transaction failures

Body

## Description
The Starkzap SDK supports gas sponsorship via `feeMode: "sponsored"` but provides no standard
error detection or fallback pattern when sponsorship fails. Each developer must reinvent error
handling, leading to inconsistent UX across applications.

## Problem
When a transaction with `feeMode: "sponsored"` fails (e.g., custom token not whitelisted on testnet,
or policy limit exceeded), the SDK throws a generic error. Developers must:

1. Parse error messages as strings to detect sponsorship failures
2. Manually retry with `feeMode: "user_pays"`
3. Communicate to users whether they must pay gas or not

### Impact
- **DX:** Developers waste time on boilerplate error handling
- **UX:** Inconsistent user messaging across apps ("Fee required" vs. "Transaction failed")
- **Reliability:** String-based error parsing is fragile and breaks with SDK updates

## Expected Behavior
The SDK should provide:

1. **Structured error types:**
   ```typescript
   class SponsorshipNotAvailableError extends Error {
     reason: "not_whitelisted" | "policy_exceeded" | "network_unavailable";
     fallbackFeeRequired: boolean; // true if user must pay gas
   }
  1. Automatic fallback option:

    const tx = await wallet.transfer(token, [...], {
      feeMode: "sponsored",
      fallbackTo: "user_pays", // Auto-retry if sponsorship fails
      onFallback: (error) => console.log("User pays gas:", error.reason),
    });
  2. Clear documentation with examples for each use case (custom tokens, multiple networks, etc.)

Reproduction Steps

  1. Deploy a custom ERC20 token on Sepolia testnet
  2. Create a Cartridge policy allowing transfer of that token
  3. Attempt transfer with feeMode: "sponsored"
  4. Observe: Generic error, no structured way to detect sponsorship failure
  5. Must retry manually with feeMode: "user_pays" and parse error string

Code Example

// Current (fragile, error-prone)
try {
  const tx = await wallet.transfer(customToken, [...], { feeMode: "sponsored" });
} catch (err) {
  if (err.message.includes("not in policies") || err.message.includes("sponsored")) {
    // Hope this pattern is stable...
    return wallet.transfer(customToken, [...], { feeMode: "user_pays" });
  }
  throw err;
}

// Desired (clean, maintainable)
try {
  const tx = await wallet.transfer(customToken, [...], {
    feeMode: "sponsored",
    fallbackTo: "user_pays",
  });
} catch (err) {
  if (err instanceof SponsorshipNotAvailableError) {
    showUserMessage(`Fee will be deducted (reason: ${err.reason})`);
  }
  throw err;
}

Environment

  • Starkzap SDK Version: Latest
  • Network: Sepolia testnet (primary issue), Mainnet (secondary)
  • Use case: StarkSplit bill-splitting app with custom token transfers

Real-World Impact

StarkSplit uses this pattern in lib/starkzap.ts::transferCustomToken(). Any app supporting
custom tokens on testnet or non-whitelisted networks will hit this.

Suggested Solution

  1. Add SponsorshipNotAvailableError with reason and fallbackFeeRequired fields
  2. Add optional fallbackTo parameter to transfer options
  3. Document with examples for testnet + custom tokens scenario
  4. (Optional) Consider wallet provider standardization (WalletConnect, ethers.js patterns)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions