TypeScript client SDK for talking to a stellar-gasless-relayer instance: submit a signed inner transaction and get it relayed as a sponsored FeeBumpTransaction, plus a WebAuthn passkey signer and wallet-detection helpers.
Current status: GaslessClient.submitGaslessTransaction(), PasskeyAdapter.signChallenge(), FreighterAdapter.signTransaction() (2026-09-04), and — as of 2026-09-07 — XBullAdapter.signTransaction() and AlbedoAdapter.signTransaction() are all implemented and tested. FreighterAdapter was rewritten to use the real @stellar/freighter-api package (it previously probed a raw, undocumented window.freighter global) and now supports the full connect → get address → sign flow, using the exact same call pattern already proven against a real connected wallet in this ecosystem's stellar-zkstream, stellar-zkident, and soroban-yield-vault frontends and gasless-relayer-dashboard. XBullAdapter and AlbedoAdapter were previously honest detection-only stubs; both now delegate to @creit.tech/stellar-wallets-kit's individual module classes (not the full kit — see CONTRIBUTING.md for why) for their real connect/sign flows. Breaking change: AlbedoAdapter.isAvailable() changed from synchronous to async, to match the other two adapters' shape — anyone calling it directly needs to add await. There is still no high-level "build a contract call and sign it in one line" helper; you build the inner transaction yourself, sign it with an adapter's signTransaction() (or your own signer), and hand the signed XDR to this SDK.
Real, independently-verified end-to-end proof (2026-09-04): examples/e2e-gasless-relay.mjs runs this SDK's actual built GaslessClient against a real running stellar-gasless-relayer instance and a real deployed contract (stellar-zkident's did_registry) on Stellar testnet — the first time these three separate repos had ever been exercised together. It's fully reproducible and self-funding (no secrets to supply). Result, confirmed independently via Horizon rather than trusted from the script's own output: the relayer's configured sponsor account's balance dropped by the real network fee (fee_account in the Horizon transaction record), while the throwaway user account that built and signed the call never lost a stroop. Running it surfaced two real integration bugs, documented in the example's header comment:
AssembledTransaction#toXDR()returns the unsigned transaction — you needtx.signed.toXDR()aftertx.sign(), or the relayer (correctly) rejects the submission withtx_bad_auth.AssembledTransaction's default timeout is computed from the local machine's clock; if it's behind real network time (measured ~8.5 minutes on the dev machine here) the transaction is already expired by submission time, producing a realtx_too_late. Pass a generoustimeoutInSecondsexplicitly instead of relying on the default — the same class of bug found earlier instellar-zkstream's frontend.
A third real bug was found and fixed on the relayer side during this same test: stellar-gasless-relayer's error handling was surfacing Horizon's generic axios message ("Request failed with status code 400") instead of the actual result_codes that explain what actually went wrong — see that repo's src/index.ts for the fix.
This repository houses the Client SDK & Developer Integration Toolkit for the stellar-gasless-net ecosystem.
- The first time three separate repos in this ecosystem were exercised together.
examples/e2e-gasless-relay.mjsdrives this SDK's actual built client against a real runningstellar-gasless-relayerand a real deployedstellar-zkidentcontract — not three components independently unit-tested and assumed to work together. - Two real integration bugs found by actually running it, not just passing unit tests — a signed-vs-unsigned XDR footgun in
AssembledTransaction#toXDR(), and a local-clock-skew timeout bug. Both documented with the fix, not swept under the rug. - What were honest stubs are now real. xBull and Albedo wallet adapters were detection-only, deliberately, until this project had a real way to verify signing against them without hand-rolling each wallet's own protocol —
@creit.tech/stellar-wallets-kit's individual module classes now provide that, real bridge-connect and popup-intent flows included. - Freighter signing is genuinely wired to the official
@stellar/freighter-apipackage, replacing an earlier version that probed an undocumented rawwindow.freighterglobal.
- SDK Integration Architecture
- Detailed Component Capabilities
- Full Code Integration Examples
- Ecosystem
- Contributing & CONTRIBUTING.md Guidelines
- Future Improvements & SDK Roadmap
┌─────────────────────────────────────────────────────────────────────────────────┐
│ @stellar-gasless/sdk Layer │
│ │
│ ┌───────────────────────────┐ ┌─────────────────────────────┐ │
│ │ PasskeyAdapter │ │ Browser Wallet Adapters │ │
│ │ (WebAuthn TouchID/FaceID) │ │ (Freighter / xBull / Albedo)│ │
│ └─────────────┬─────────────┘ └──────────────┬──────────────┘ │
│ │ │ │
│ └──────────────────────┬───────────────────────┘ │
│ │ │
│ v │
│ ┌──────────────────────────────┐ │
│ │ GaslessClient │ │
│ │ (HTTP Payload Transport) │ │
│ └──────────────┬───────────────┘ │
│ │ │
│ v │
│ ┌──────────────────────────────┐ │
│ │ useGasless Hook (React) │ │
│ └──────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────┘
1. GaslessClient (src/client.ts)
- 1-Line Transport: Submits off-chain signed intents to the Relayer service over HTTP in a single request, with safe JSON/network error parsing so a malformed response or dropped connection comes back as a typed
{success: false, error}instead of a thrown exception. No retry or polling logic yet — see roadmap.
2. PasskeyAdapter (src/adapters/passkey.ts)
- Browser WebAuthn Enclave: Invokes the browser's WebAuthn
navigator.credentials.get()(TouchID/FaceID/security key, whatever the platform authenticator is) to sign a challenge. Throws clearly if WebAuthn isn't available rather than failing silently.
3. Wallet Adapters (src/adapters/)
- Freighter (
freighter.ts):isAvailable(),getPublicKey(), andsignTransaction(xdr, { networkPassphrase, address? })are all implemented, backed by the real@stellar/freighter-apipackage. - xBull (
xbull.ts) / Albedo (albedo.ts):isAvailable(),getPublicKey(), andsignTransaction()are all implemented (2026-09-07), backed by@creit.tech/stellar-wallets-kit's individualxBullModule/AlbedoModuleclasses — real bridge-connect (xBull) and popup-intent (Albedo) flows, not stubs. Note xBull's and Albedo'sisAvailable()don't do real extension detection the way Freighter's does — see each adapter's own doc comment for why that's genuinely how those two wallets' connection models work, not a shortcut.
4. React Toolkit (src/react/useGasless.ts)
useGaslessTransaction(client)Hook: takes aGaslessClientinstance, exposes{ submit, isSubmitting, error, txHash }.submit(signedInnerTxXdr)expects a transaction you've already built and signed — see the usage example below.
import { GaslessClient } from '@stellar-gasless/sdk';
const gaslessClient = new GaslessClient({
relayerUrl: 'https://your-relayer-domain.example', // your own deployed stellar-gasless-relayer
dappApiKey: 'YOUR_DAPP_API_KEY',
});
// signedInnerTxXdr must already be built and signed by the user before this call.
const result = await gaslessClient.submitGaslessTransaction(signedInnerTxXdr);
console.log('Gasless Meta-Tx Hash:', result.hash);import { GaslessClient, FreighterAdapter } from '@stellar-gasless/sdk';
const networkPassphrase = 'Test SDF Network ; September 2015';
const address = await FreighterAdapter.getPublicKey(); // prompts Freighter for access
// Build `unsignedInnerTxXdr` yourself (e.g. via @stellar/stellar-sdk/contract's Client),
// with `address` as the transaction's source account — see examples/e2e-gasless-relay.mjs
// in this repo for a complete, runnable version of this exact flow.
const signedInnerTxXdr = await FreighterAdapter.signTransaction(unsignedInnerTxXdr, { networkPassphrase, address });
const gaslessClient = new GaslessClient({ relayerUrl: 'https://your-relayer-domain.example', dappApiKey: 'YOUR_DAPP_API_KEY' });
const result = await gaslessClient.submitGaslessTransaction(signedInnerTxXdr);
console.log('Gasless Meta-Tx Hash:', result.hash);import { PasskeyAdapter } from '@stellar-gasless/sdk';
// Prompt the browser's WebAuthn platform authenticator (TouchID/FaceID/security key)
const credential = await PasskeyAdapter.signChallenge(challengeHex);
console.log('Passkey Credential ID:', credential.id);import { GaslessClient, useGaslessTransaction } from '@stellar-gasless/sdk';
const client = new GaslessClient({
relayerUrl: 'https://your-relayer-domain.example',
dappApiKey: 'YOUR_DAPP_API_KEY',
});
function GaslessSubmitButton({ signedInnerTxXdr }: { signedInnerTxXdr: string }) {
const { submit, isSubmitting, txHash, error } = useGaslessTransaction(client);
return (
<button onClick={() => submit(signedInnerTxXdr)} disabled={isSubmitting}>
{isSubmitting ? 'Submitting...' : 'Submit Gasless Tx'}
</button>
);
}Part of stellar-gasless-net's gasless meta-transaction protocol suite, alongside:
soroban-gasless-contracts— the on-chain WASM contracts this SDK'sGaslessClientultimately triggers via a relayerstellar-gasless-relayer— the backend service this SDK submits signed transactions togasless-relayer-dashboard— an admin console with a real integration of this SDK'sGaslessClientin its "Real Gasless Transaction" tab
Please review our dedicated CONTRIBUTING.md guide before opening pull requests:
- Claim an issue tagged
good first issue,intermediate, oradvanced. - Run
npm test(vitest) and verify TypeScript compilation (npm run build). - Follow Conventional Commits format (
feat: ...,fix: ...,docs: ...).
- Freighter wallet signing: done 2026-09-04 —
FreighterAdapter.signTransaction(). - xBull & Albedo wallet signing: done 2026-09-07 —
XBullAdapter/AlbedoAdapter.signTransaction(), via@creit.tech/stellar-wallets-kit's individual module classes. - High-level execute helper: a
{contractId, method, params}→ build + sign + submit convenience wrapper (not built yet; you currently build and sign the inner transaction yourself). - React Native & Flutter Adapters: Mobile SDK adapters supporting mobile WebAuthn passkey enclaves.
- Vue & Svelte Component Libraries: Native hooks and wrappers for Vue 3 and Svelte.
- Auto-Retry Failover Engine: Multi-relayer endpoint failover routing.