diff --git a/CHANGELOG.md b/CHANGELOG.md index 86c1c45..5d4a308 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- Replace generic SAC terminology with SEP-41 across docs, comments, and error messages ([#39](https://github.com/stellar/stellar-mpp-sdk/pull/39)) + ### Fixed - Fix CHANGELOG entries for v0.3.0 ([#36](https://github.com/stellar/stellar-mpp-sdk/pull/36)) @@ -38,7 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Initial release of `@stellar/mpp` — a TypeScript SDK for Stellar blockchain payment methods in the Machine Payments Protocol (MPP) -- **Charge module**: one-time on-chain SAC token transfers with pull (transaction credential) and push (hash credential) modes, following the [draft-stellar-charge-00](https://paymentauth.org/draft-stellar-charge-00) specification +- **Charge module**: one-time on-chain SEP-41 token transfers with pull (transaction credential) and push (hash credential) modes, following the [draft-stellar-charge-00](https://paymentauth.org/draft-stellar-charge-00) specification - **Channel module**: off-chain payment commitments via one-way payment channel contracts with batch settlement on close (session spec in progress) - Subpath exports for selective imports (`@stellar/mpp/charge/client`, `@stellar/mpp/charge/server`, `@stellar/mpp/channel/client`, `@stellar/mpp/channel/server`, `@stellar/mpp/env`) - Env parsing primitives for Stellar-aware configuration diff --git a/CLAUDE.md b/CLAUDE.md index 07e4690..5424882 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co Stellar MPP SDK — a TypeScript SDK implementing Stellar blockchain payment methods for the Machine Payments Protocol (MPP). Provides two payment modes: -- **Charge**: One-time on-chain SAC (Stellar Asset Contract) token transfers with pull/push credential modes +- **Charge**: One-time on-chain SEP-41 token transfers with pull/push credential modes - **Channel**: Off-chain payment commitments via one-way payment channel contracts (batch settlement on close) Built on the `mppx` framework. Peer dependencies: `@stellar/stellar-sdk` (^14.6.1) and `mppx` (^0.4.11). @@ -110,8 +110,8 @@ Methods.ts (Zod schema) → client/ (create credentials) + server/ (verify crede | Path | Role | | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | `sdk/src/charge/Methods.ts` | Charge method schema (Zod discriminated union: `transaction` vs `hash` credentials) | -| `sdk/src/charge/client/Charge.ts` | Creates SAC `transfer` invocations; handles pull (send XDR) and push (broadcast + send hash) flows | -| `sdk/src/charge/server/Charge.ts` | Verifies and broadcasts SAC transfers; supports fee sponsorship via FeeBumpTransaction | +| `sdk/src/charge/client/Charge.ts` | Creates SEP-41 `transfer` invocations; handles pull (send XDR) and push (broadcast + send hash) flows | +| `sdk/src/charge/server/Charge.ts` | Verifies and broadcasts SEP-41 transfers; supports fee sponsorship via FeeBumpTransaction | | `sdk/src/channel/Methods.ts` | Channel method schema (discriminated union: `open` / `voucher` / `close` actions) | | `sdk/src/channel/client/Channel.ts` | Signs cumulative commitment amounts off-chain via ed25519; handles `open` action (sends signed deploy tx XDR + initial commitment) | | `sdk/src/channel/server/Channel.ts` | Verifies commitment signatures via contract simulation; `open` action broadcasts the deploy tx and initialises cumulative store | @@ -158,7 +158,7 @@ All other `shared/` modules are strictly internal and consumed only by `charge/` - **mppx integration**: Methods defined via `Method.from()`, adapted with `.toClient()` / `.toServer()`. Namespaced as `stellar.charge()` and `stellar.channel()`. - **Serialization locks**: Both Charge and Channel servers use Promise-based locks (`let verifyLock: Promise = Promise.resolve()`) to serialize verification and prevent race conditions on store get/put. -- **Contract simulation**: Uses Soroban RPC `simulateTransaction` for read-only verification — SAC transfer validation, `prepare_commitment` for commitment bytes, and channel state queries. +- **Contract simulation**: Uses Soroban RPC `simulateTransaction` for read-only verification — SEP-41 transfer validation, `prepare_commitment` for commitment bytes, and channel state queries. - **Zod validation**: All method schemas use Zod v4 with discriminated unions for credential/action types. - **Shared utility extraction**: Common logic (polling, fee bumps, simulation, keypair resolution, validation, error types, logging) lives in `shared/` and is imported by both `charge/` and `channel/`. - **Configurable defaults**: Server and client functions accept optional parameters (`pollMaxAttempts`, `pollDelayMs`, `pollTimeoutMs`, `simulationTimeoutMs`, `maxFeeBumpStroops`, `logger`) with defaults from `shared/defaults.ts`, applied via parameter destructuring. diff --git a/README.md b/README.md index 9e2dca1..1d073bc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # @stellar/mpp -Stellar blockchain payment method for the [Machine Payments Protocol (MPP)](https://mpp.dev). Enables machine-to-machine payments using Soroban SAC token transfers on the Stellar network, with optional support for [one-way payment channels](https://github.com/stellar-experimental/one-way-channel) for high-frequency off-chain payments. +Stellar blockchain payment method for the [Machine Payments Protocol (MPP)](https://mpp.dev). Enables machine-to-machine payments using Soroban SEP-41 token transfers on the Stellar network, with optional support for [one-way payment channels](https://github.com/stellar-experimental/one-way-channel) for high-frequency off-chain payments. ## Specification @@ -10,7 +10,7 @@ The charge payment mode implements the [draft-stellar-charge-00](https://payment ### Charge (one-time transfers) -Each payment is a Soroban SAC `transfer` settled on-chain individually. +Each payment is a Soroban SEP-41 `transfer` settled on-chain individually. ``` Client Server Stellar @@ -23,7 +23,7 @@ Client Server Stellar |<------------------------------| | | | | | prepareTransaction ----------------- (simulate) ------------>| - | Sign SAC transfer | | + | Sign SEP-41 transfer | | | Send credential (XDR) | | |------------------------------>| | | | sendTransaction ------------>| @@ -232,7 +232,7 @@ const data = await response.json() ```ts stellar.charge({ recipient: string, // Stellar public key (G...) or contract (C...) - currency: string, // SAC contract address + currency: string, // SEP-41 token contract address network?: 'stellar:testnet' | 'stellar:pubnet', // default: 'stellar:testnet' decimals?: number, // default: 7 rpcUrl?: string, // custom Soroban RPC URL diff --git a/demo/README.md b/demo/README.md index 374cc1d..c834c10 100644 --- a/demo/README.md +++ b/demo/README.md @@ -4,7 +4,7 @@ Interactive playground for testing the Stellar MPP payment flow — via browser Two demo modes are available: -- **Charge** — one-time on-chain SAC token transfers (default) +- **Charge** — one-time on-chain SEP-41 token transfers (default) - **Channel** — off-chain payment channel commitments (no on-chain tx per payment) ## Prerequisites @@ -82,7 +82,7 @@ Client Server | currency, recipient) | |<------------------------------| | | - | Sign Soroban SAC transfer | + | Sign Soroban SEP-41 transfer | | Send credential (XDR/hash) | |------------------------------>| | | diff --git a/diagrams/charge-flow.md b/diagrams/charge-flow.md index 0969a50..27206f8 100644 --- a/diagrams/charge-flow.md +++ b/diagrams/charge-flow.md @@ -1,4 +1,4 @@ -# Charge Payment Flow — On-Chain SAC Transfer +# Charge Payment Flow — On-Chain SEP-41 Transfer > Implements [draft-stellar-charge-00](https://paymentauth.org/draft-stellar-charge-00) @@ -18,7 +18,7 @@ sequenceDiagram SC-->>App: 402 + Challenge JSON
{amount, currency, recipient, methodDetails} App->>CC: createCredential(challenge) - CC->>CC: Resolve network, build SAC transfer(from, to, amount) + CC->>CC: Resolve network, build SEP-41 transfer(from, to, amount) alt Pull Mode — Sponsored (feePayer: true, default when server has feePayer) Note over CC: Uses all-zeros source account (GAAA...WHF)
so server can substitute its own account @@ -29,7 +29,7 @@ sequenceDiagram CC->>CC: Sign only sorobanCredentialsAddress
auth entries (not the envelope) CC-->>App: Credential {type:'transaction', transaction: xdr} App->>SC: Credential with auth-entry-signed XDR - SC->>SC: verifySacInvocation(tx) — validate structure + SC->>SC: verifyTokenInvocation(tx) — validate structure SC->>SC: Detect all-zeros source → spec rebuild path SC->>RPC: getAccount(feePayerKey) RPC-->>SC: Server account with current sequence @@ -51,7 +51,7 @@ sequenceDiagram CC->>CC: keypair.sign(preparedTx) CC-->>App: Credential {type:'transaction', transaction: xdr} App->>SC: Credential with fully-signed XDR - SC->>SC: verifySacInvocation(tx) — validate structure + SC->>SC: verifyTokenInvocation(tx) — validate structure SC->>RPC: simulateTransaction(signedTx) RPC-->>SC: Verify transfer events match challenge SC->>RPC: sendTransaction(signedTx as-is) @@ -71,7 +71,7 @@ sequenceDiagram App->>SC: Credential with tx hash SC->>RPC: getTransaction(hash) RPC-->>SC: TX result - SC->>SC: verifySacTransfer(result) — verify on-chain + SC->>SC: verifyTokenTransfer(result) — verify on-chain SC-->>App: Receipt {status:'success', reference:hash} end ``` diff --git a/diagrams/mindmap.md b/diagrams/mindmap.md index 39f76fd..0a25866 100644 --- a/diagrams/mindmap.md +++ b/diagrams/mindmap.md @@ -40,8 +40,8 @@ mindmap defaults hook: currency + recipient request hook: toBaseUnits, UUID, methodDetails verify hook - Pull: verifySacInvocation → broadcast → poll - Push: fetch tx → verifySacTransfer + Pull: verifyTokenInvocation → broadcast → poll + Push: fetch tx → verifyTokenTransfer Fee-bump wrapping Replay protection via Store Channel — channel/server/Channel.ts diff --git a/examples/charge-client.ts b/examples/charge-client.ts index b978363..d7c7bad 100644 --- a/examples/charge-client.ts +++ b/examples/charge-client.ts @@ -2,7 +2,7 @@ * Example: Stellar MPP Client * * Automatically handles 402 Payment Required responses by paying - * via Soroban SAC transfer on Stellar testnet. + * via Soroban SEP-41 transfer on Stellar testnet. * * Usage: * STELLAR_SECRET=SYOUR_SECRET_KEY npx tsx examples/charge-client.ts diff --git a/examples/charge-server.ts b/examples/charge-server.ts index e58b3ad..5e35f0a 100644 --- a/examples/charge-server.ts +++ b/examples/charge-server.ts @@ -1,7 +1,7 @@ /** * Example: Stellar MPP Server * - * Charges 0.01 USDC per request via Soroban SAC transfer. + * Charges 0.01 USDC per request via Soroban SEP-41 transfer. * Uses Express with security headers (helmet, rate limiting). * * Usage: diff --git a/sdk/src/charge/Methods.ts b/sdk/src/charge/Methods.ts index 7eab0e1..b97689d 100644 --- a/sdk/src/charge/Methods.ts +++ b/sdk/src/charge/Methods.ts @@ -2,11 +2,11 @@ import { Method } from 'mppx' import { z } from 'zod/mini' /** - * Stellar charge intent for one-time SAC token transfers. + * Stellar charge intent for one-time SEP-41 token transfers. * * Supports two credential flows: * - `type: "transaction"` — **server-broadcast** (pull mode): - * Client signs a Soroban SAC `transfer` invocation and sends the + * Client signs a Soroban SEP-41 `transfer` invocation and sends the * serialised XDR as `payload.transaction`. The server broadcasts it. * - `type: "hash"` — **client-broadcast** (push mode): * Client broadcasts itself and sends the transaction hash. @@ -29,7 +29,7 @@ export const charge = Method.from({ request: z.object({ /** Payment amount in base units (stroops). */ amount: z.string(), - /** SAC contract address (C...) for the token to transfer. */ + /** SEP-41 token contract address (C...) for the token to transfer. */ currency: z.string(), /** Recipient Stellar public key (G...) or contract address (C...). */ recipient: z.string(), diff --git a/sdk/src/charge/client/Charge.ts b/sdk/src/charge/client/Charge.ts index c8e4f79..8e1d944 100644 --- a/sdk/src/charge/client/Charge.ts +++ b/sdk/src/charge/client/Charge.ts @@ -36,7 +36,7 @@ import { /** * Creates a Stellar charge method for use on the **client**. * - * Builds a Soroban SAC `transfer` invocation, signs it, and either: + * Builds a Soroban SEP-41 `transfer` invocation, signs it, and either: * - **pull** (default): sends the signed XDR to the server to broadcast * - **push**: broadcasts itself and sends the tx hash * @@ -101,7 +101,7 @@ export function charge(parameters: charge.Parameters) { const networkPassphrase = NETWORK_PASSPHRASE[network] const server = new rpc.Server(resolvedRpcUrl) - // Build SAC `transfer(from, to, amount)` invocation + // Build SEP-41 `transfer(from, to, amount)` invocation const contract = new Contract(currency) const stellarAmount = BigInt(amount) diff --git a/sdk/src/charge/server/Charge.test.ts b/sdk/src/charge/server/Charge.test.ts index adcaeb1..674b3a0 100644 --- a/sdk/src/charge/server/Charge.test.ts +++ b/sdk/src/charge/server/Charge.test.ts @@ -371,7 +371,7 @@ describe('charge transaction verification', () => { await expect( method.verify({ credential: cred as any, request: cred.challenge.request }), - ).rejects.toThrow('matching SAC transfer invocation') + ).rejects.toThrow('matching SEP-41 transfer invocation') }) it('rejects transaction with wrong amount', async () => { @@ -395,7 +395,7 @@ describe('charge transaction verification', () => { await expect( method.verify({ credential: cred as any, request: cred.challenge.request }), - ).rejects.toThrow('matching SAC transfer invocation') + ).rejects.toThrow('matching SEP-41 transfer invocation') }) it('rejects transaction with wrong currency', async () => { @@ -420,7 +420,7 @@ describe('charge transaction verification', () => { await expect( method.verify({ credential: cred as any, request: cred.challenge.request }), - ).rejects.toThrow('matching SAC transfer invocation') + ).rejects.toThrow('matching SEP-41 transfer invocation') }) it('rejects sponsored source without feePayer configured', async () => { diff --git a/sdk/src/charge/server/Charge.ts b/sdk/src/charge/server/Charge.ts index d04231e..c863bd6 100644 --- a/sdk/src/charge/server/Charge.ts +++ b/sdk/src/charge/server/Charge.ts @@ -41,7 +41,7 @@ const STORE_PREFIX = 'stellar:charge' /** * Creates a Stellar charge method for use on the **server**. * - * Verifies and settles Soroban SAC `transfer` invocations received as + * Verifies and settles Soroban SEP-41 `transfer` invocations received as * pull-mode (signed XDR) or push-mode (on-chain tx hash) credentials. * * @see https://paymentauth.org/draft-stellar-charge-00 @@ -161,7 +161,7 @@ export function charge(parameters: charge.Parameters) { timeoutMs: pollTimeoutMs, }) - verifySacTransfer( + verifyTokenTransfer( txResult, { amount: expectedAmount, @@ -195,7 +195,7 @@ export function charge(parameters: charge.Parameters) { verifyExactlyOneInvokeOp(tx) - verifySacInvocation(tx, { + verifyTokenInvocation(tx, { amount: expectedAmount, currency: expectedCurrency, recipient: expectedRecipient, @@ -485,7 +485,7 @@ function verifyExactlyOneInvokeOp(tx: Transaction) { } } -function verifySacInvocation( +function verifyTokenInvocation( tx: Transaction, expected: { amount: bigint; currency: string; recipient: string }, ) { @@ -534,7 +534,7 @@ function verifyFromRawOps( if (!found) { throw new PaymentVerificationError( - `${LOG_PREFIX} Transaction does not contain a matching SAC transfer invocation.`, + `${LOG_PREFIX} Transaction does not contain a matching SEP-41 transfer invocation.`, { currency: expected.currency, recipient: expected.recipient, @@ -544,7 +544,7 @@ function verifyFromRawOps( } } -function verifySacTransfer( +function verifyTokenTransfer( txResult: rpc.Api.GetSuccessfulTransactionResponse, expected: { amount: bigint; currency: string; recipient: string }, networkPassphrase: string, diff --git a/sdk/src/constants.ts b/sdk/src/constants.ts index 3186680..fe0ffd2 100644 --- a/sdk/src/constants.ts +++ b/sdk/src/constants.ts @@ -32,22 +32,22 @@ export const HORIZON_URLS: Record = { } // --------------------------------------------------------------------------- -// USDC Stellar Asset Contract (SAC) addresses +// Well-known SAC (SEP-41) token contract addresses // --------------------------------------------------------------------------- -/** USDC SAC contract address on Stellar mainnet. */ +/** USDC SEP-41 token contract address on Stellar mainnet (SAC). */ export const USDC_SAC_MAINNET = 'CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI' -/** USDC SAC contract address on Stellar testnet. */ +/** USDC SEP-41 token contract address on Stellar testnet (SAC). */ export const USDC_SAC_TESTNET = 'CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA' -/** Native XLM SAC contract address on mainnet. */ +/** Native XLM SEP-41 token contract address on mainnet (SAC). */ export const XLM_SAC_MAINNET = 'CAS3J7GYLGVE45MR3HPSFG352DAANEV5GGMFTO3IZIE4JMCDALQO57Y' -/** Native XLM SAC contract address on testnet. */ +/** Native XLM SEP-41 token contract address on testnet (SAC). */ export const XLM_SAC_TESTNET = 'CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC' -/** Map from network to well-known SAC addresses. */ +/** Map from network to well-known SAC (SEP-41) token addresses (USDC/XLM). */ export const SAC_ADDRESSES = { [STELLAR_PUBNET]: { USDC: USDC_SAC_MAINNET,