@suimpp/mpp 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -126,12 +126,36 @@ function sui2(options) {
126
126
  `Transferred ${transferredRaw} < requested ${requestedRaw} (raw units)`
127
127
  );
128
128
  }
129
- return mppx.Receipt.from({
129
+ const receipt = mppx.Receipt.from({
130
130
  method: "sui",
131
131
  reference: credential.payload.digest,
132
132
  status: "success",
133
133
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
134
134
  });
135
+ const report = {
136
+ digest,
137
+ sender: resolved.balanceChanges.find(
138
+ (bc) => bc.coinType === options.currency && BigInt(bc.amount) < 0n
139
+ )?.address,
140
+ recipient: options.recipient,
141
+ amount: credential.challenge.request.amount,
142
+ currency: options.currency,
143
+ network
144
+ };
145
+ if (options.onPayment) {
146
+ try {
147
+ options.onPayment(report);
148
+ } catch {
149
+ }
150
+ } else if (options.registryUrl) {
151
+ fetch(options.registryUrl, {
152
+ method: "POST",
153
+ headers: { "content-type": "application/json" },
154
+ body: JSON.stringify({ ...report, serverUrl: options.serverUrl })
155
+ }).catch(() => {
156
+ });
157
+ }
158
+ return receipt;
135
159
  }
136
160
  });
137
161
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/method.ts","../src/utils.ts","../src/constants.ts","../src/client.ts","../src/server.ts"],"names":["Method","z","Transaction","coinWithBalance","Credential","sui","SuiGrpcClient","normalizeSuiAddress","Receipt"],"mappings":";;;;;;;;AAEO,IAAM,SAAA,GAAYA,YAAO,IAAA,CAAK;AAAA,EACnC,MAAA,EAAQ,QAAA;AAAA,EACR,IAAA,EAAM,KAAA;AAAA,EACN,MAAA,EAAQ;AAAA,IACN,UAAA,EAAY;AAAA,MACV,OAAA,EAASC,OAAE,MAAA,CAAO;AAAA,QAChB,MAAA,EAAQA,OAAE,MAAA;AAAO,OAClB;AAAA,KACH;AAAA,IACA,OAAA,EAASA,OAAE,MAAA,CAAO;AAAA,MAChB,MAAA,EAAQA,OAAE,MAAA,EAAO;AAAA,MACjB,QAAA,EAAUA,OAAE,MAAA,EAAO;AAAA,MACnB,SAAA,EAAWA,OAAE,MAAA;AAAO,KACrB;AAAA;AAEL,CAAC;;;ACbM,SAAS,gBAAA,CAAiB,QAAgB,QAAA,EAA0B;AACzE,EAAA,MAAM,CAAC,QAAQ,GAAA,EAAK,IAAA,GAAO,EAAE,CAAA,GAAI,MAAA,CAAO,MAAM,GAAG,CAAA;AACjD,EAAA,MAAM,UAAA,GAAa,KAAK,MAAA,CAAO,QAAA,EAAU,GAAG,CAAA,CAAE,KAAA,CAAM,GAAG,QAAQ,CAAA;AAC/D,EAAA,OAAO,MAAA,CAAO,QAAQ,UAAU,CAAA;AAClC;AAMA,eAAsB,SAAA,CACpB,IACA,EAAE,QAAA,GAAW,GAAG,WAAA,GAAc,GAAA,EAAK,GAAiD,EAAC,EACzE;AACZ,EAAA,IAAI,SAAA;AACJ,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,QAAA,EAAU,CAAA,EAAA,EAAK;AACjC,IAAA,IAAI;AACF,MAAA,OAAO,MAAM,EAAA,EAAG;AAAA,IAClB,SAAS,GAAA,EAAK;AACZ,MAAA,SAAA,GAAY,GAAA;AACZ,MAAA,IAAI,CAAA,GAAI,WAAW,CAAA,EAAG;AACpB,QAAA,MAAM,IAAI,QAAQ,CAAC,CAAA,KAAM,WAAW,CAAA,EAAG,WAAA,IAAe,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA;AAAA,MAC/D;AAAA,IACF;AAAA,EACF;AACA,EAAA,MAAM,SAAA;AACR;;;AC9BO,IAAM,aAAA,GACX;;;ACkBK,SAAS,IAAI,OAAA,EAA2B;AAC7C,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,MAAA,CAAO,YAAA,EAAa;AAC5C,EAAA,MAAM,QAAA,GAAW,QAAQ,QAAA,IAAY,CAAA;AAErC,EAAA,OAAOD,WAAAA,CAAO,SAAS,SAAA,EAAW;AAAA,IAChC,MAAM,gBAAA,CAAiB,EAAE,SAAA,EAAU,EAAG;AACpC,MAAA,MAAM,EAAE,MAAA,EAAQ,QAAA,EAAU,SAAA,KAAc,SAAA,CAAU,OAAA;AAClD,MAAA,MAAM,SAAA,GAAY,gBAAA,CAAiB,MAAA,EAAQ,QAAQ,CAAA;AAEnD,MAAA,MAAM,EAAA,GAAK,IAAIE,wBAAA,EAAY;AAC3B,MAAA,EAAA,CAAG,UAAU,OAAO,CAAA;AAEpB,MAAA,MAAM,UAAUC,4BAAA,CAAgB,EAAE,SAAS,SAAA,EAAW,IAAA,EAAM,UAAU,CAAA;AACtE,MAAA,EAAA,CAAG,eAAA,CAAgB,CAAC,OAAO,CAAA,EAAG,SAAS,CAAA;AAEvC,MAAA,IAAI,MAAA;AACJ,MAAA,IAAI;AACF,QAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,UAAA,MAAA,GAAS,MAAM,OAAA,CAAQ,OAAA,CAAQ,EAAE,CAAA;AAAA,QACnC,CAAA,MAAO;AACL,UAAA,MAAM,KAAA,GAAQ,MAAM,EAAA,CAAG,KAAA,CAAM,EAAE,MAAA,EAAQ,OAAA,CAAQ,QAAQ,CAAA;AACvD,UAAA,MAAM,EAAE,SAAA,EAAU,GAAI,MAAM,OAAA,CAAQ,MAAA,CAAO,gBAAgB,KAAK,CAAA;AAChE,UAAA,MAAM,UAAA,GAAa,MAAM,OAAA,CAAQ,MAAA,CAAO,KAAK,kBAAA,CAAmB;AAAA,YAC9D,WAAA,EAAa,KAAA;AAAA,YACb,UAAA,EAAY,CAAC,SAAS,CAAA;AAAA,YACtB,OAAA,EAAS,EAAE,OAAA,EAAS,IAAA;AAAK,WAC1B,CAAA;AACD,UAAA,IAAI,WAAW,iBAAA,EAAmB;AAChC,YAAA,MAAM,IAAI,KAAA,CAAM,UAAA,CAAW,kBAAkB,MAAA,CAAO,KAAA,EAAO,WAAW,oBAAoB,CAAA;AAAA,UAC5F;AACA,UAAA,MAAA,GAAS,UAAA,CAAW,WAAA;AAAA,QACtB;AAAA,MACF,SAAS,GAAA,EAAc;AACrB,QAAA,MAAM,MAAM,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG,CAAA;AAC3D,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,4BAAA,EAA+B,GAAG,CAAA,CAAE,CAAA;AAAA,MACtD;AAEA,MAAA,OAAOC,gBAAW,SAAA,CAAU;AAAA,QAC1B,SAAA;AAAA,QACA,OAAA,EAAS,EAAE,MAAA,EAAQ,MAAA,CAAO,MAAA;AAAO,OAClC,CAAA;AAAA,IACH;AAAA,GACD,CAAA;AACH;AC5CO,SAASC,KAAI,OAAA,EAA2B;AAC7C,EAAA,MAAM,OAAA,GAAU,QAAQ,OAAA,IAAW,SAAA;AACnC,EAAA,MAAM,QAAA,GAAW,QAAQ,QAAA,IAAY,CAAA;AACrC,EAAA,MAAM,MAAA,GAAS,IAAIC,kBAAA,CAAc;AAAA,IAC/B,OAAA,EAAS,OAAA,CAAQ,MAAA,IAAU,CAAA,iBAAA,EAAoB,OAAO,CAAA,WAAA,CAAA;AAAA,IACtD;AAAA,GACD,CAAA;AAED,EAAA,MAAM,mBAAA,GAAsBC,yBAAA,CAAoB,OAAA,CAAQ,SAAS,CAAA;AAEjE,EAAA,OAAOP,WAAAA,CAAO,SAAS,SAAA,EAAW;AAAA,IAChC,QAAA,EAAU;AAAA,MACR,UAAU,OAAA,CAAQ,QAAA;AAAA,MAClB,WAAW,OAAA,CAAQ;AAAA,KACrB;AAAA,IAEA,MAAM,MAAA,CAAO,EAAE,UAAA,EAAW,EAAG;AAC3B,MAAA,MAAM,MAAA,GAAS,WAAW,OAAA,CAAQ,MAAA;AAElC,MAAA,MAAM,KAAK,MAAM,SAAA;AAAA,QACf,MAAM,MAAA,CAAO,IAAA,CAAK,cAAA,CAAe,EAAE,MAAA,EAAQ,OAAA,EAAS,EAAE,cAAA,EAAgB,IAAA,EAAK,EAAG;AAAA,OAChF,CAAE,MAAM,MAAM;AACZ,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,2CAAA,EAA8C,MAAM,CAAA,CAAA,CAAG,CAAA;AAAA,MACzE,CAAC,CAAA;AAED,MAAA,MAAM,QAAA,GAAW,EAAA,CAAG,WAAA,IAAe,EAAA,CAAG,iBAAA;AACtC,MAAA,IAAI,CAAC,QAAA,EAAU,MAAA,CAAO,OAAA,EAAS;AAC7B,QAAA,MAAM,IAAI,MAAM,6BAA6B,CAAA;AAAA,MAC/C;AAEA,MAAA,MAAM,OAAA,GAAU,SAAS,cAAA,CAAe,IAAA;AAAA,QACtC,CAAC,EAAA,KACC,EAAA,CAAG,QAAA,KAAa,QAAQ,QAAA,IACxBO,yBAAA,CAAoB,EAAA,CAAG,OAAO,CAAA,KAAM,mBAAA,IACpC,MAAA,CAAO,EAAA,CAAG,MAAM,CAAA,GAAI;AAAA,OACxB;AAEA,MAAA,IAAI,CAAC,OAAA,EAAS;AACZ,QAAA,MAAM,IAAI,KAAA;AAAA,UACR;AAAA,SACF;AAAA,MACF;AAEA,MAAA,MAAM,cAAA,GAAiB,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA;AAC5C,MAAA,MAAM,eAAe,gBAAA,CAAiB,UAAA,CAAW,SAAA,CAAU,OAAA,CAAQ,QAAQ,QAAQ,CAAA;AACnF,MAAA,IAAI,iBAAiB,YAAA,EAAc;AACjC,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,YAAA,EAAe,cAAc,CAAA,aAAA,EAAgB,YAAY,CAAA,YAAA;AAAA,SAC3D;AAAA,MACF;AAEA,MAAA,OAAOC,aAAQ,IAAA,CAAK;AAAA,QAClB,MAAA,EAAQ,KAAA;AAAA,QACR,SAAA,EAAW,WAAW,OAAA,CAAQ,MAAA;AAAA,QAC9B,MAAA,EAAQ,SAAA;AAAA,QACR,SAAA,EAAA,iBAAW,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY,OACnC,CAAA;AAAA,IACH;AAAA,GACD,CAAA;AACH","file":"index.cjs","sourcesContent":["import { Method, z } from 'mppx';\n\nexport const suiCharge = Method.from({\n intent: 'charge',\n name: 'sui',\n schema: {\n credential: {\n payload: z.object({\n digest: z.string(),\n }),\n },\n request: z.object({\n amount: z.string(),\n currency: z.string(),\n recipient: z.string(),\n }),\n },\n});\n","/**\n * Parse a string amount to raw bigint units without floating-point math.\n * \"0.01\" with 6 decimals → 10000n\n */\nexport function parseAmountToRaw(amount: string, decimals: number): bigint {\n const [whole = '0', frac = ''] = amount.split('.');\n const paddedFrac = frac.padEnd(decimals, '0').slice(0, decimals);\n return BigInt(whole + paddedFrac);\n}\n\n/**\n * Retry an async function with linear backoff.\n * Throws the last error if all attempts fail.\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n { attempts = 5, baseDelayMs = 1000 }: { attempts?: number; baseDelayMs?: number } = {},\n): Promise<T> {\n let lastError: unknown;\n for (let i = 0; i < attempts; i++) {\n try {\n return await fn();\n } catch (err) {\n lastError = err;\n if (i < attempts - 1) {\n await new Promise((r) => setTimeout(r, baseDelayMs * (i + 1)));\n }\n }\n }\n throw lastError;\n}\n","export const SUI_USDC_TYPE =\n '0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC';\n","import { Method, Credential } from 'mppx';\nimport type { ClientWithCoreApi } from '@mysten/sui/client';\nimport type { Signer } from '@mysten/sui/cryptography';\nimport { coinWithBalance, Transaction } from '@mysten/sui/transactions';\nimport { suiCharge } from './method.js';\nimport { parseAmountToRaw } from './utils.js';\n\nexport { suiCharge } from './method.js';\nexport { SUI_USDC_TYPE } from './constants.js';\n\nexport interface SuiChargeOptions {\n client: ClientWithCoreApi;\n signer: Signer;\n /** Number of decimal places for the currency (default: 6, e.g. USDC). */\n decimals?: number;\n /** Override transaction execution (e.g. to route through a gas manager). */\n execute?: (tx: Transaction) => Promise<{ digest: string }>;\n}\n\nexport function sui(options: SuiChargeOptions) {\n const address = options.signer.toSuiAddress();\n const decimals = options.decimals ?? 6;\n\n return Method.toClient(suiCharge, {\n async createCredential({ challenge }) {\n const { amount, currency, recipient } = challenge.request;\n const amountRaw = parseAmountToRaw(amount, decimals);\n\n const tx = new Transaction();\n tx.setSender(address);\n\n const payment = coinWithBalance({ balance: amountRaw, type: currency });\n tx.transferObjects([payment], recipient);\n\n let result;\n try {\n if (options.execute) {\n result = await options.execute(tx);\n } else {\n const built = await tx.build({ client: options.client });\n const { signature } = await options.signer.signTransaction(built);\n const execResult = await options.client.core.executeTransaction({\n transaction: built,\n signatures: [signature],\n include: { effects: true },\n });\n if (execResult.FailedTransaction) {\n throw new Error(execResult.FailedTransaction.status.error?.message ?? 'Transaction failed');\n }\n result = execResult.Transaction!;\n }\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`Payment transaction failed: ${msg}`);\n }\n\n return Credential.serialize({\n challenge,\n payload: { digest: result.digest },\n });\n },\n });\n}\n","import { Method, Receipt } from 'mppx';\nimport { SuiGrpcClient } from '@mysten/sui/grpc';\nimport { normalizeSuiAddress } from '@mysten/sui/utils';\nimport { suiCharge } from './method.js';\nimport { parseAmountToRaw, withRetry } from './utils.js';\n\nexport { suiCharge } from './method.js';\nexport { SUI_USDC_TYPE } from './constants.js';\n\nexport interface SuiServerOptions {\n currency: string;\n recipient: string;\n /** Number of decimal places for the currency (default: 6, e.g. USDC). */\n decimals?: number;\n rpcUrl?: string;\n network?: 'mainnet' | 'testnet' | 'devnet';\n}\n\nexport function sui(options: SuiServerOptions) {\n const network = options.network ?? 'mainnet';\n const decimals = options.decimals ?? 6;\n const client = new SuiGrpcClient({\n baseUrl: options.rpcUrl ?? `https://fullnode.${network}.sui.io:443`,\n network,\n });\n\n const normalizedRecipient = normalizeSuiAddress(options.recipient);\n\n return Method.toServer(suiCharge, {\n defaults: {\n currency: options.currency,\n recipient: options.recipient,\n },\n\n async verify({ credential }) {\n const digest = credential.payload.digest;\n\n const tx = await withRetry(\n () => client.core.getTransaction({ digest, include: { balanceChanges: true } }),\n ).catch(() => {\n throw new Error(`Could not find the referenced transaction [${digest}]`);\n });\n\n const resolved = tx.Transaction ?? tx.FailedTransaction;\n if (!resolved?.status.success) {\n throw new Error('Transaction failed on-chain');\n }\n\n const payment = resolved.balanceChanges.find(\n (bc) =>\n bc.coinType === options.currency &&\n normalizeSuiAddress(bc.address) === normalizedRecipient &&\n BigInt(bc.amount) > 0n,\n );\n\n if (!payment) {\n throw new Error(\n 'Payment not found in transaction balance changes',\n );\n }\n\n const transferredRaw = BigInt(payment.amount);\n const requestedRaw = parseAmountToRaw(credential.challenge.request.amount, decimals);\n if (transferredRaw < requestedRaw) {\n throw new Error(\n `Transferred ${transferredRaw} < requested ${requestedRaw} (raw units)`,\n );\n }\n\n return Receipt.from({\n method: 'sui',\n reference: credential.payload.digest,\n status: 'success',\n timestamp: new Date().toISOString(),\n });\n },\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/method.ts","../src/utils.ts","../src/constants.ts","../src/client.ts","../src/server.ts"],"names":["Method","z","Transaction","coinWithBalance","Credential","sui","SuiGrpcClient","normalizeSuiAddress","Receipt"],"mappings":"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","file":"index.cjs","sourcesContent":["import { Method, z } from 'mppx';\n\nexport const suiCharge = Method.from({\n intent: 'charge',\n name: 'sui',\n schema: {\n credential: {\n payload: z.object({\n digest: z.string(),\n }),\n },\n request: z.object({\n amount: z.string(),\n currency: z.string(),\n recipient: z.string(),\n }),\n },\n});\n","/**\n * Parse a string amount to raw bigint units without floating-point math.\n * \"0.01\" with 6 decimals → 10000n\n */\nexport function parseAmountToRaw(amount: string, decimals: number): bigint {\n const [whole = '0', frac = ''] = amount.split('.');\n const paddedFrac = frac.padEnd(decimals, '0').slice(0, decimals);\n return BigInt(whole + paddedFrac);\n}\n\n/**\n * Retry an async function with linear backoff.\n * Throws the last error if all attempts fail.\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n { attempts = 5, baseDelayMs = 1000 }: { attempts?: number; baseDelayMs?: number } = {},\n): Promise<T> {\n let lastError: unknown;\n for (let i = 0; i < attempts; i++) {\n try {\n return await fn();\n } catch (err) {\n lastError = err;\n if (i < attempts - 1) {\n await new Promise((r) => setTimeout(r, baseDelayMs * (i + 1)));\n }\n }\n }\n throw lastError;\n}\n","export const SUI_USDC_TYPE =\n '0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC';\n","import { Method, Credential } from 'mppx';\nimport type { ClientWithCoreApi } from '@mysten/sui/client';\nimport type { Signer } from '@mysten/sui/cryptography';\nimport { coinWithBalance, Transaction } from '@mysten/sui/transactions';\nimport { suiCharge } from './method.js';\nimport { parseAmountToRaw } from './utils.js';\n\nexport { suiCharge } from './method.js';\nexport { SUI_USDC_TYPE } from './constants.js';\n\nexport interface SuiChargeOptions {\n client: ClientWithCoreApi;\n signer: Signer;\n /** Number of decimal places for the currency (default: 6, e.g. USDC). */\n decimals?: number;\n /** Override transaction execution (e.g. to route through a gas manager). */\n execute?: (tx: Transaction) => Promise<{ digest: string }>;\n}\n\nexport function sui(options: SuiChargeOptions) {\n const address = options.signer.toSuiAddress();\n const decimals = options.decimals ?? 6;\n\n return Method.toClient(suiCharge, {\n async createCredential({ challenge }) {\n const { amount, currency, recipient } = challenge.request;\n const amountRaw = parseAmountToRaw(amount, decimals);\n\n const tx = new Transaction();\n tx.setSender(address);\n\n const payment = coinWithBalance({ balance: amountRaw, type: currency });\n tx.transferObjects([payment], recipient);\n\n let result;\n try {\n if (options.execute) {\n result = await options.execute(tx);\n } else {\n const built = await tx.build({ client: options.client });\n const { signature } = await options.signer.signTransaction(built);\n const execResult = await options.client.core.executeTransaction({\n transaction: built,\n signatures: [signature],\n include: { effects: true },\n });\n if (execResult.FailedTransaction) {\n throw new Error(execResult.FailedTransaction.status.error?.message ?? 'Transaction failed');\n }\n result = execResult.Transaction!;\n }\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`Payment transaction failed: ${msg}`);\n }\n\n return Credential.serialize({\n challenge,\n payload: { digest: result.digest },\n });\n },\n });\n}\n","import { Method, Receipt } from 'mppx';\nimport { SuiGrpcClient } from '@mysten/sui/grpc';\nimport { normalizeSuiAddress } from '@mysten/sui/utils';\nimport { suiCharge } from './method.js';\nimport { parseAmountToRaw, withRetry } from './utils.js';\n\nexport { suiCharge } from './method.js';\nexport { SUI_USDC_TYPE } from './constants.js';\n\nexport interface PaymentReport {\n digest: string;\n sender?: string;\n recipient: string;\n amount: string;\n currency: string;\n network: string;\n}\n\nexport interface SuiServerOptions {\n currency: string;\n recipient: string;\n /** Number of decimal places for the currency (default: 6, e.g. USDC). */\n decimals?: number;\n rpcUrl?: string;\n network?: 'mainnet' | 'testnet' | 'devnet';\n /**\n * Called after successful on-chain verification with payment data.\n * Use to report payments with full request context (e.g., endpoint, service).\n * When provided, replaces the built-in registryUrl reporting.\n */\n onPayment?: (report: PaymentReport) => void;\n /** @deprecated Use `onPayment` instead. URL to report verified payments to. */\n registryUrl?: string;\n /** @deprecated Use `onPayment` instead. Public URL of the server. */\n serverUrl?: string;\n}\n\nexport function sui(options: SuiServerOptions) {\n const network = options.network ?? 'mainnet';\n const decimals = options.decimals ?? 6;\n const client = new SuiGrpcClient({\n baseUrl: options.rpcUrl ?? `https://fullnode.${network}.sui.io:443`,\n network,\n });\n\n const normalizedRecipient = normalizeSuiAddress(options.recipient);\n\n return Method.toServer(suiCharge, {\n defaults: {\n currency: options.currency,\n recipient: options.recipient,\n },\n\n async verify({ credential }) {\n const digest = credential.payload.digest;\n\n const tx = await withRetry(\n () => client.core.getTransaction({ digest, include: { balanceChanges: true } }),\n ).catch(() => {\n throw new Error(`Could not find the referenced transaction [${digest}]`);\n });\n\n const resolved = tx.Transaction ?? tx.FailedTransaction;\n if (!resolved?.status.success) {\n throw new Error('Transaction failed on-chain');\n }\n\n const payment = resolved.balanceChanges.find(\n (bc) =>\n bc.coinType === options.currency &&\n normalizeSuiAddress(bc.address) === normalizedRecipient &&\n BigInt(bc.amount) > 0n,\n );\n\n if (!payment) {\n throw new Error(\n 'Payment not found in transaction balance changes',\n );\n }\n\n const transferredRaw = BigInt(payment.amount);\n const requestedRaw = parseAmountToRaw(credential.challenge.request.amount, decimals);\n if (transferredRaw < requestedRaw) {\n throw new Error(\n `Transferred ${transferredRaw} < requested ${requestedRaw} (raw units)`,\n );\n }\n\n const receipt = Receipt.from({\n method: 'sui',\n reference: credential.payload.digest,\n status: 'success',\n timestamp: new Date().toISOString(),\n });\n\n const report: PaymentReport = {\n digest,\n sender: resolved.balanceChanges.find(\n (bc) => bc.coinType === options.currency && BigInt(bc.amount) < 0n,\n )?.address,\n recipient: options.recipient,\n amount: credential.challenge.request.amount,\n currency: options.currency,\n network,\n };\n\n if (options.onPayment) {\n try { options.onPayment(report); } catch {}\n } else if (options.registryUrl) {\n fetch(options.registryUrl, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ ...report, serverUrl: options.serverUrl }),\n }).catch(() => {});\n }\n\n return receipt;\n },\n });\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { S as SUI_USDC_TYPE, s as suiCharge } from './constants-D31h0GdU.cjs';
2
2
  export { SuiChargeOptions, sui as suiClient } from './client.cjs';
3
- export { SuiServerOptions, sui as suiServer } from './server.cjs';
3
+ export { PaymentReport, SuiServerOptions, sui as suiServer } from './server.cjs';
4
4
  import 'mppx';
5
5
  import 'zod/v4/core';
6
6
  import 'zod/mini';
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { S as SUI_USDC_TYPE, s as suiCharge } from './constants-D31h0GdU.js';
2
2
  export { SuiChargeOptions, sui as suiClient } from './client.js';
3
- export { SuiServerOptions, sui as suiServer } from './server.js';
3
+ export { PaymentReport, SuiServerOptions, sui as suiServer } from './server.js';
4
4
  import 'mppx';
5
5
  import 'zod/v4/core';
6
6
  import 'zod/mini';
package/dist/index.js CHANGED
@@ -124,12 +124,36 @@ function sui2(options) {
124
124
  `Transferred ${transferredRaw} < requested ${requestedRaw} (raw units)`
125
125
  );
126
126
  }
127
- return Receipt.from({
127
+ const receipt = Receipt.from({
128
128
  method: "sui",
129
129
  reference: credential.payload.digest,
130
130
  status: "success",
131
131
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
132
132
  });
133
+ const report = {
134
+ digest,
135
+ sender: resolved.balanceChanges.find(
136
+ (bc) => bc.coinType === options.currency && BigInt(bc.amount) < 0n
137
+ )?.address,
138
+ recipient: options.recipient,
139
+ amount: credential.challenge.request.amount,
140
+ currency: options.currency,
141
+ network
142
+ };
143
+ if (options.onPayment) {
144
+ try {
145
+ options.onPayment(report);
146
+ } catch {
147
+ }
148
+ } else if (options.registryUrl) {
149
+ fetch(options.registryUrl, {
150
+ method: "POST",
151
+ headers: { "content-type": "application/json" },
152
+ body: JSON.stringify({ ...report, serverUrl: options.serverUrl })
153
+ }).catch(() => {
154
+ });
155
+ }
156
+ return receipt;
133
157
  }
134
158
  });
135
159
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/method.ts","../src/utils.ts","../src/constants.ts","../src/client.ts","../src/server.ts"],"names":["Method","sui"],"mappings":"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","file":"index.js","sourcesContent":["import { Method, z } from 'mppx';\n\nexport const suiCharge = Method.from({\n intent: 'charge',\n name: 'sui',\n schema: {\n credential: {\n payload: z.object({\n digest: z.string(),\n }),\n },\n request: z.object({\n amount: z.string(),\n currency: z.string(),\n recipient: z.string(),\n }),\n },\n});\n","/**\n * Parse a string amount to raw bigint units without floating-point math.\n * \"0.01\" with 6 decimals → 10000n\n */\nexport function parseAmountToRaw(amount: string, decimals: number): bigint {\n const [whole = '0', frac = ''] = amount.split('.');\n const paddedFrac = frac.padEnd(decimals, '0').slice(0, decimals);\n return BigInt(whole + paddedFrac);\n}\n\n/**\n * Retry an async function with linear backoff.\n * Throws the last error if all attempts fail.\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n { attempts = 5, baseDelayMs = 1000 }: { attempts?: number; baseDelayMs?: number } = {},\n): Promise<T> {\n let lastError: unknown;\n for (let i = 0; i < attempts; i++) {\n try {\n return await fn();\n } catch (err) {\n lastError = err;\n if (i < attempts - 1) {\n await new Promise((r) => setTimeout(r, baseDelayMs * (i + 1)));\n }\n }\n }\n throw lastError;\n}\n","export const SUI_USDC_TYPE =\n '0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC';\n","import { Method, Credential } from 'mppx';\nimport type { ClientWithCoreApi } from '@mysten/sui/client';\nimport type { Signer } from '@mysten/sui/cryptography';\nimport { coinWithBalance, Transaction } from '@mysten/sui/transactions';\nimport { suiCharge } from './method.js';\nimport { parseAmountToRaw } from './utils.js';\n\nexport { suiCharge } from './method.js';\nexport { SUI_USDC_TYPE } from './constants.js';\n\nexport interface SuiChargeOptions {\n client: ClientWithCoreApi;\n signer: Signer;\n /** Number of decimal places for the currency (default: 6, e.g. USDC). */\n decimals?: number;\n /** Override transaction execution (e.g. to route through a gas manager). */\n execute?: (tx: Transaction) => Promise<{ digest: string }>;\n}\n\nexport function sui(options: SuiChargeOptions) {\n const address = options.signer.toSuiAddress();\n const decimals = options.decimals ?? 6;\n\n return Method.toClient(suiCharge, {\n async createCredential({ challenge }) {\n const { amount, currency, recipient } = challenge.request;\n const amountRaw = parseAmountToRaw(amount, decimals);\n\n const tx = new Transaction();\n tx.setSender(address);\n\n const payment = coinWithBalance({ balance: amountRaw, type: currency });\n tx.transferObjects([payment], recipient);\n\n let result;\n try {\n if (options.execute) {\n result = await options.execute(tx);\n } else {\n const built = await tx.build({ client: options.client });\n const { signature } = await options.signer.signTransaction(built);\n const execResult = await options.client.core.executeTransaction({\n transaction: built,\n signatures: [signature],\n include: { effects: true },\n });\n if (execResult.FailedTransaction) {\n throw new Error(execResult.FailedTransaction.status.error?.message ?? 'Transaction failed');\n }\n result = execResult.Transaction!;\n }\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`Payment transaction failed: ${msg}`);\n }\n\n return Credential.serialize({\n challenge,\n payload: { digest: result.digest },\n });\n },\n });\n}\n","import { Method, Receipt } from 'mppx';\nimport { SuiGrpcClient } from '@mysten/sui/grpc';\nimport { normalizeSuiAddress } from '@mysten/sui/utils';\nimport { suiCharge } from './method.js';\nimport { parseAmountToRaw, withRetry } from './utils.js';\n\nexport { suiCharge } from './method.js';\nexport { SUI_USDC_TYPE } from './constants.js';\n\nexport interface SuiServerOptions {\n currency: string;\n recipient: string;\n /** Number of decimal places for the currency (default: 6, e.g. USDC). */\n decimals?: number;\n rpcUrl?: string;\n network?: 'mainnet' | 'testnet' | 'devnet';\n}\n\nexport function sui(options: SuiServerOptions) {\n const network = options.network ?? 'mainnet';\n const decimals = options.decimals ?? 6;\n const client = new SuiGrpcClient({\n baseUrl: options.rpcUrl ?? `https://fullnode.${network}.sui.io:443`,\n network,\n });\n\n const normalizedRecipient = normalizeSuiAddress(options.recipient);\n\n return Method.toServer(suiCharge, {\n defaults: {\n currency: options.currency,\n recipient: options.recipient,\n },\n\n async verify({ credential }) {\n const digest = credential.payload.digest;\n\n const tx = await withRetry(\n () => client.core.getTransaction({ digest, include: { balanceChanges: true } }),\n ).catch(() => {\n throw new Error(`Could not find the referenced transaction [${digest}]`);\n });\n\n const resolved = tx.Transaction ?? tx.FailedTransaction;\n if (!resolved?.status.success) {\n throw new Error('Transaction failed on-chain');\n }\n\n const payment = resolved.balanceChanges.find(\n (bc) =>\n bc.coinType === options.currency &&\n normalizeSuiAddress(bc.address) === normalizedRecipient &&\n BigInt(bc.amount) > 0n,\n );\n\n if (!payment) {\n throw new Error(\n 'Payment not found in transaction balance changes',\n );\n }\n\n const transferredRaw = BigInt(payment.amount);\n const requestedRaw = parseAmountToRaw(credential.challenge.request.amount, decimals);\n if (transferredRaw < requestedRaw) {\n throw new Error(\n `Transferred ${transferredRaw} < requested ${requestedRaw} (raw units)`,\n );\n }\n\n return Receipt.from({\n method: 'sui',\n reference: credential.payload.digest,\n status: 'success',\n timestamp: new Date().toISOString(),\n });\n },\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/method.ts","../src/utils.ts","../src/constants.ts","../src/client.ts","../src/server.ts"],"names":["Method","sui"],"mappings":"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","file":"index.js","sourcesContent":["import { Method, z } from 'mppx';\n\nexport const suiCharge = Method.from({\n intent: 'charge',\n name: 'sui',\n schema: {\n credential: {\n payload: z.object({\n digest: z.string(),\n }),\n },\n request: z.object({\n amount: z.string(),\n currency: z.string(),\n recipient: z.string(),\n }),\n },\n});\n","/**\n * Parse a string amount to raw bigint units without floating-point math.\n * \"0.01\" with 6 decimals → 10000n\n */\nexport function parseAmountToRaw(amount: string, decimals: number): bigint {\n const [whole = '0', frac = ''] = amount.split('.');\n const paddedFrac = frac.padEnd(decimals, '0').slice(0, decimals);\n return BigInt(whole + paddedFrac);\n}\n\n/**\n * Retry an async function with linear backoff.\n * Throws the last error if all attempts fail.\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n { attempts = 5, baseDelayMs = 1000 }: { attempts?: number; baseDelayMs?: number } = {},\n): Promise<T> {\n let lastError: unknown;\n for (let i = 0; i < attempts; i++) {\n try {\n return await fn();\n } catch (err) {\n lastError = err;\n if (i < attempts - 1) {\n await new Promise((r) => setTimeout(r, baseDelayMs * (i + 1)));\n }\n }\n }\n throw lastError;\n}\n","export const SUI_USDC_TYPE =\n '0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC';\n","import { Method, Credential } from 'mppx';\nimport type { ClientWithCoreApi } from '@mysten/sui/client';\nimport type { Signer } from '@mysten/sui/cryptography';\nimport { coinWithBalance, Transaction } from '@mysten/sui/transactions';\nimport { suiCharge } from './method.js';\nimport { parseAmountToRaw } from './utils.js';\n\nexport { suiCharge } from './method.js';\nexport { SUI_USDC_TYPE } from './constants.js';\n\nexport interface SuiChargeOptions {\n client: ClientWithCoreApi;\n signer: Signer;\n /** Number of decimal places for the currency (default: 6, e.g. USDC). */\n decimals?: number;\n /** Override transaction execution (e.g. to route through a gas manager). */\n execute?: (tx: Transaction) => Promise<{ digest: string }>;\n}\n\nexport function sui(options: SuiChargeOptions) {\n const address = options.signer.toSuiAddress();\n const decimals = options.decimals ?? 6;\n\n return Method.toClient(suiCharge, {\n async createCredential({ challenge }) {\n const { amount, currency, recipient } = challenge.request;\n const amountRaw = parseAmountToRaw(amount, decimals);\n\n const tx = new Transaction();\n tx.setSender(address);\n\n const payment = coinWithBalance({ balance: amountRaw, type: currency });\n tx.transferObjects([payment], recipient);\n\n let result;\n try {\n if (options.execute) {\n result = await options.execute(tx);\n } else {\n const built = await tx.build({ client: options.client });\n const { signature } = await options.signer.signTransaction(built);\n const execResult = await options.client.core.executeTransaction({\n transaction: built,\n signatures: [signature],\n include: { effects: true },\n });\n if (execResult.FailedTransaction) {\n throw new Error(execResult.FailedTransaction.status.error?.message ?? 'Transaction failed');\n }\n result = execResult.Transaction!;\n }\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`Payment transaction failed: ${msg}`);\n }\n\n return Credential.serialize({\n challenge,\n payload: { digest: result.digest },\n });\n },\n });\n}\n","import { Method, Receipt } from 'mppx';\nimport { SuiGrpcClient } from '@mysten/sui/grpc';\nimport { normalizeSuiAddress } from '@mysten/sui/utils';\nimport { suiCharge } from './method.js';\nimport { parseAmountToRaw, withRetry } from './utils.js';\n\nexport { suiCharge } from './method.js';\nexport { SUI_USDC_TYPE } from './constants.js';\n\nexport interface PaymentReport {\n digest: string;\n sender?: string;\n recipient: string;\n amount: string;\n currency: string;\n network: string;\n}\n\nexport interface SuiServerOptions {\n currency: string;\n recipient: string;\n /** Number of decimal places for the currency (default: 6, e.g. USDC). */\n decimals?: number;\n rpcUrl?: string;\n network?: 'mainnet' | 'testnet' | 'devnet';\n /**\n * Called after successful on-chain verification with payment data.\n * Use to report payments with full request context (e.g., endpoint, service).\n * When provided, replaces the built-in registryUrl reporting.\n */\n onPayment?: (report: PaymentReport) => void;\n /** @deprecated Use `onPayment` instead. URL to report verified payments to. */\n registryUrl?: string;\n /** @deprecated Use `onPayment` instead. Public URL of the server. */\n serverUrl?: string;\n}\n\nexport function sui(options: SuiServerOptions) {\n const network = options.network ?? 'mainnet';\n const decimals = options.decimals ?? 6;\n const client = new SuiGrpcClient({\n baseUrl: options.rpcUrl ?? `https://fullnode.${network}.sui.io:443`,\n network,\n });\n\n const normalizedRecipient = normalizeSuiAddress(options.recipient);\n\n return Method.toServer(suiCharge, {\n defaults: {\n currency: options.currency,\n recipient: options.recipient,\n },\n\n async verify({ credential }) {\n const digest = credential.payload.digest;\n\n const tx = await withRetry(\n () => client.core.getTransaction({ digest, include: { balanceChanges: true } }),\n ).catch(() => {\n throw new Error(`Could not find the referenced transaction [${digest}]`);\n });\n\n const resolved = tx.Transaction ?? tx.FailedTransaction;\n if (!resolved?.status.success) {\n throw new Error('Transaction failed on-chain');\n }\n\n const payment = resolved.balanceChanges.find(\n (bc) =>\n bc.coinType === options.currency &&\n normalizeSuiAddress(bc.address) === normalizedRecipient &&\n BigInt(bc.amount) > 0n,\n );\n\n if (!payment) {\n throw new Error(\n 'Payment not found in transaction balance changes',\n );\n }\n\n const transferredRaw = BigInt(payment.amount);\n const requestedRaw = parseAmountToRaw(credential.challenge.request.amount, decimals);\n if (transferredRaw < requestedRaw) {\n throw new Error(\n `Transferred ${transferredRaw} < requested ${requestedRaw} (raw units)`,\n );\n }\n\n const receipt = Receipt.from({\n method: 'sui',\n reference: credential.payload.digest,\n status: 'success',\n timestamp: new Date().toISOString(),\n });\n\n const report: PaymentReport = {\n digest,\n sender: resolved.balanceChanges.find(\n (bc) => bc.coinType === options.currency && BigInt(bc.amount) < 0n,\n )?.address,\n recipient: options.recipient,\n amount: credential.challenge.request.amount,\n currency: options.currency,\n network,\n };\n\n if (options.onPayment) {\n try { options.onPayment(report); } catch {}\n } else if (options.registryUrl) {\n fetch(options.registryUrl, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ ...report, serverUrl: options.serverUrl }),\n }).catch(() => {});\n }\n\n return receipt;\n },\n });\n}\n"]}
package/dist/server.cjs CHANGED
@@ -86,12 +86,36 @@ function sui(options) {
86
86
  `Transferred ${transferredRaw} < requested ${requestedRaw} (raw units)`
87
87
  );
88
88
  }
89
- return mppx.Receipt.from({
89
+ const receipt = mppx.Receipt.from({
90
90
  method: "sui",
91
91
  reference: credential.payload.digest,
92
92
  status: "success",
93
93
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
94
94
  });
95
+ const report = {
96
+ digest,
97
+ sender: resolved.balanceChanges.find(
98
+ (bc) => bc.coinType === options.currency && BigInt(bc.amount) < 0n
99
+ )?.address,
100
+ recipient: options.recipient,
101
+ amount: credential.challenge.request.amount,
102
+ currency: options.currency,
103
+ network
104
+ };
105
+ if (options.onPayment) {
106
+ try {
107
+ options.onPayment(report);
108
+ } catch {
109
+ }
110
+ } else if (options.registryUrl) {
111
+ fetch(options.registryUrl, {
112
+ method: "POST",
113
+ headers: { "content-type": "application/json" },
114
+ body: JSON.stringify({ ...report, serverUrl: options.serverUrl })
115
+ }).catch(() => {
116
+ });
117
+ }
118
+ return receipt;
95
119
  }
96
120
  });
97
121
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/method.ts","../src/utils.ts","../src/constants.ts","../src/server.ts"],"names":["Method","z","SuiGrpcClient","normalizeSuiAddress","Receipt"],"mappings":"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","file":"server.cjs","sourcesContent":["import { Method, z } from 'mppx';\n\nexport const suiCharge = Method.from({\n intent: 'charge',\n name: 'sui',\n schema: {\n credential: {\n payload: z.object({\n digest: z.string(),\n }),\n },\n request: z.object({\n amount: z.string(),\n currency: z.string(),\n recipient: z.string(),\n }),\n },\n});\n","/**\n * Parse a string amount to raw bigint units without floating-point math.\n * \"0.01\" with 6 decimals → 10000n\n */\nexport function parseAmountToRaw(amount: string, decimals: number): bigint {\n const [whole = '0', frac = ''] = amount.split('.');\n const paddedFrac = frac.padEnd(decimals, '0').slice(0, decimals);\n return BigInt(whole + paddedFrac);\n}\n\n/**\n * Retry an async function with linear backoff.\n * Throws the last error if all attempts fail.\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n { attempts = 5, baseDelayMs = 1000 }: { attempts?: number; baseDelayMs?: number } = {},\n): Promise<T> {\n let lastError: unknown;\n for (let i = 0; i < attempts; i++) {\n try {\n return await fn();\n } catch (err) {\n lastError = err;\n if (i < attempts - 1) {\n await new Promise((r) => setTimeout(r, baseDelayMs * (i + 1)));\n }\n }\n }\n throw lastError;\n}\n","export const SUI_USDC_TYPE =\n '0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC';\n","import { Method, Receipt } from 'mppx';\nimport { SuiGrpcClient } from '@mysten/sui/grpc';\nimport { normalizeSuiAddress } from '@mysten/sui/utils';\nimport { suiCharge } from './method.js';\nimport { parseAmountToRaw, withRetry } from './utils.js';\n\nexport { suiCharge } from './method.js';\nexport { SUI_USDC_TYPE } from './constants.js';\n\nexport interface SuiServerOptions {\n currency: string;\n recipient: string;\n /** Number of decimal places for the currency (default: 6, e.g. USDC). */\n decimals?: number;\n rpcUrl?: string;\n network?: 'mainnet' | 'testnet' | 'devnet';\n}\n\nexport function sui(options: SuiServerOptions) {\n const network = options.network ?? 'mainnet';\n const decimals = options.decimals ?? 6;\n const client = new SuiGrpcClient({\n baseUrl: options.rpcUrl ?? `https://fullnode.${network}.sui.io:443`,\n network,\n });\n\n const normalizedRecipient = normalizeSuiAddress(options.recipient);\n\n return Method.toServer(suiCharge, {\n defaults: {\n currency: options.currency,\n recipient: options.recipient,\n },\n\n async verify({ credential }) {\n const digest = credential.payload.digest;\n\n const tx = await withRetry(\n () => client.core.getTransaction({ digest, include: { balanceChanges: true } }),\n ).catch(() => {\n throw new Error(`Could not find the referenced transaction [${digest}]`);\n });\n\n const resolved = tx.Transaction ?? tx.FailedTransaction;\n if (!resolved?.status.success) {\n throw new Error('Transaction failed on-chain');\n }\n\n const payment = resolved.balanceChanges.find(\n (bc) =>\n bc.coinType === options.currency &&\n normalizeSuiAddress(bc.address) === normalizedRecipient &&\n BigInt(bc.amount) > 0n,\n );\n\n if (!payment) {\n throw new Error(\n 'Payment not found in transaction balance changes',\n );\n }\n\n const transferredRaw = BigInt(payment.amount);\n const requestedRaw = parseAmountToRaw(credential.challenge.request.amount, decimals);\n if (transferredRaw < requestedRaw) {\n throw new Error(\n `Transferred ${transferredRaw} < requested ${requestedRaw} (raw units)`,\n );\n }\n\n return Receipt.from({\n method: 'sui',\n reference: credential.payload.digest,\n status: 'success',\n timestamp: new Date().toISOString(),\n });\n },\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/method.ts","../src/utils.ts","../src/constants.ts","../src/server.ts"],"names":["Method","z","SuiGrpcClient","normalizeSuiAddress","Receipt"],"mappings":"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","file":"server.cjs","sourcesContent":["import { Method, z } from 'mppx';\n\nexport const suiCharge = Method.from({\n intent: 'charge',\n name: 'sui',\n schema: {\n credential: {\n payload: z.object({\n digest: z.string(),\n }),\n },\n request: z.object({\n amount: z.string(),\n currency: z.string(),\n recipient: z.string(),\n }),\n },\n});\n","/**\n * Parse a string amount to raw bigint units without floating-point math.\n * \"0.01\" with 6 decimals → 10000n\n */\nexport function parseAmountToRaw(amount: string, decimals: number): bigint {\n const [whole = '0', frac = ''] = amount.split('.');\n const paddedFrac = frac.padEnd(decimals, '0').slice(0, decimals);\n return BigInt(whole + paddedFrac);\n}\n\n/**\n * Retry an async function with linear backoff.\n * Throws the last error if all attempts fail.\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n { attempts = 5, baseDelayMs = 1000 }: { attempts?: number; baseDelayMs?: number } = {},\n): Promise<T> {\n let lastError: unknown;\n for (let i = 0; i < attempts; i++) {\n try {\n return await fn();\n } catch (err) {\n lastError = err;\n if (i < attempts - 1) {\n await new Promise((r) => setTimeout(r, baseDelayMs * (i + 1)));\n }\n }\n }\n throw lastError;\n}\n","export const SUI_USDC_TYPE =\n '0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC';\n","import { Method, Receipt } from 'mppx';\nimport { SuiGrpcClient } from '@mysten/sui/grpc';\nimport { normalizeSuiAddress } from '@mysten/sui/utils';\nimport { suiCharge } from './method.js';\nimport { parseAmountToRaw, withRetry } from './utils.js';\n\nexport { suiCharge } from './method.js';\nexport { SUI_USDC_TYPE } from './constants.js';\n\nexport interface PaymentReport {\n digest: string;\n sender?: string;\n recipient: string;\n amount: string;\n currency: string;\n network: string;\n}\n\nexport interface SuiServerOptions {\n currency: string;\n recipient: string;\n /** Number of decimal places for the currency (default: 6, e.g. USDC). */\n decimals?: number;\n rpcUrl?: string;\n network?: 'mainnet' | 'testnet' | 'devnet';\n /**\n * Called after successful on-chain verification with payment data.\n * Use to report payments with full request context (e.g., endpoint, service).\n * When provided, replaces the built-in registryUrl reporting.\n */\n onPayment?: (report: PaymentReport) => void;\n /** @deprecated Use `onPayment` instead. URL to report verified payments to. */\n registryUrl?: string;\n /** @deprecated Use `onPayment` instead. Public URL of the server. */\n serverUrl?: string;\n}\n\nexport function sui(options: SuiServerOptions) {\n const network = options.network ?? 'mainnet';\n const decimals = options.decimals ?? 6;\n const client = new SuiGrpcClient({\n baseUrl: options.rpcUrl ?? `https://fullnode.${network}.sui.io:443`,\n network,\n });\n\n const normalizedRecipient = normalizeSuiAddress(options.recipient);\n\n return Method.toServer(suiCharge, {\n defaults: {\n currency: options.currency,\n recipient: options.recipient,\n },\n\n async verify({ credential }) {\n const digest = credential.payload.digest;\n\n const tx = await withRetry(\n () => client.core.getTransaction({ digest, include: { balanceChanges: true } }),\n ).catch(() => {\n throw new Error(`Could not find the referenced transaction [${digest}]`);\n });\n\n const resolved = tx.Transaction ?? tx.FailedTransaction;\n if (!resolved?.status.success) {\n throw new Error('Transaction failed on-chain');\n }\n\n const payment = resolved.balanceChanges.find(\n (bc) =>\n bc.coinType === options.currency &&\n normalizeSuiAddress(bc.address) === normalizedRecipient &&\n BigInt(bc.amount) > 0n,\n );\n\n if (!payment) {\n throw new Error(\n 'Payment not found in transaction balance changes',\n );\n }\n\n const transferredRaw = BigInt(payment.amount);\n const requestedRaw = parseAmountToRaw(credential.challenge.request.amount, decimals);\n if (transferredRaw < requestedRaw) {\n throw new Error(\n `Transferred ${transferredRaw} < requested ${requestedRaw} (raw units)`,\n );\n }\n\n const receipt = Receipt.from({\n method: 'sui',\n reference: credential.payload.digest,\n status: 'success',\n timestamp: new Date().toISOString(),\n });\n\n const report: PaymentReport = {\n digest,\n sender: resolved.balanceChanges.find(\n (bc) => bc.coinType === options.currency && BigInt(bc.amount) < 0n,\n )?.address,\n recipient: options.recipient,\n amount: credential.challenge.request.amount,\n currency: options.currency,\n network,\n };\n\n if (options.onPayment) {\n try { options.onPayment(report); } catch {}\n } else if (options.registryUrl) {\n fetch(options.registryUrl, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ ...report, serverUrl: options.serverUrl }),\n }).catch(() => {});\n }\n\n return receipt;\n },\n });\n}\n"]}
package/dist/server.d.cts CHANGED
@@ -3,6 +3,14 @@ import * as zod_mini from 'zod/mini';
3
3
  import { Method } from 'mppx';
4
4
  export { S as SUI_USDC_TYPE, s as suiCharge } from './constants-D31h0GdU.cjs';
5
5
 
6
+ interface PaymentReport {
7
+ digest: string;
8
+ sender?: string;
9
+ recipient: string;
10
+ amount: string;
11
+ currency: string;
12
+ network: string;
13
+ }
6
14
  interface SuiServerOptions {
7
15
  currency: string;
8
16
  recipient: string;
@@ -10,6 +18,16 @@ interface SuiServerOptions {
10
18
  decimals?: number;
11
19
  rpcUrl?: string;
12
20
  network?: 'mainnet' | 'testnet' | 'devnet';
21
+ /**
22
+ * Called after successful on-chain verification with payment data.
23
+ * Use to report payments with full request context (e.g., endpoint, service).
24
+ * When provided, replaces the built-in registryUrl reporting.
25
+ */
26
+ onPayment?: (report: PaymentReport) => void;
27
+ /** @deprecated Use `onPayment` instead. URL to report verified payments to. */
28
+ registryUrl?: string;
29
+ /** @deprecated Use `onPayment` instead. Public URL of the server. */
30
+ serverUrl?: string;
13
31
  }
14
32
  declare function sui(options: SuiServerOptions): Method.Server<{
15
33
  readonly intent: "charge";
@@ -31,4 +49,4 @@ declare function sui(options: SuiServerOptions): Method.Server<{
31
49
  readonly recipient: string;
32
50
  }, undefined>;
33
51
 
34
- export { type SuiServerOptions, sui };
52
+ export { type PaymentReport, type SuiServerOptions, sui };
package/dist/server.d.ts CHANGED
@@ -3,6 +3,14 @@ import * as zod_mini from 'zod/mini';
3
3
  import { Method } from 'mppx';
4
4
  export { S as SUI_USDC_TYPE, s as suiCharge } from './constants-D31h0GdU.js';
5
5
 
6
+ interface PaymentReport {
7
+ digest: string;
8
+ sender?: string;
9
+ recipient: string;
10
+ amount: string;
11
+ currency: string;
12
+ network: string;
13
+ }
6
14
  interface SuiServerOptions {
7
15
  currency: string;
8
16
  recipient: string;
@@ -10,6 +18,16 @@ interface SuiServerOptions {
10
18
  decimals?: number;
11
19
  rpcUrl?: string;
12
20
  network?: 'mainnet' | 'testnet' | 'devnet';
21
+ /**
22
+ * Called after successful on-chain verification with payment data.
23
+ * Use to report payments with full request context (e.g., endpoint, service).
24
+ * When provided, replaces the built-in registryUrl reporting.
25
+ */
26
+ onPayment?: (report: PaymentReport) => void;
27
+ /** @deprecated Use `onPayment` instead. URL to report verified payments to. */
28
+ registryUrl?: string;
29
+ /** @deprecated Use `onPayment` instead. Public URL of the server. */
30
+ serverUrl?: string;
13
31
  }
14
32
  declare function sui(options: SuiServerOptions): Method.Server<{
15
33
  readonly intent: "charge";
@@ -31,4 +49,4 @@ declare function sui(options: SuiServerOptions): Method.Server<{
31
49
  readonly recipient: string;
32
50
  }, undefined>;
33
51
 
34
- export { type SuiServerOptions, sui };
52
+ export { type PaymentReport, type SuiServerOptions, sui };
package/dist/server.js CHANGED
@@ -84,12 +84,36 @@ function sui(options) {
84
84
  `Transferred ${transferredRaw} < requested ${requestedRaw} (raw units)`
85
85
  );
86
86
  }
87
- return Receipt.from({
87
+ const receipt = Receipt.from({
88
88
  method: "sui",
89
89
  reference: credential.payload.digest,
90
90
  status: "success",
91
91
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
92
92
  });
93
+ const report = {
94
+ digest,
95
+ sender: resolved.balanceChanges.find(
96
+ (bc) => bc.coinType === options.currency && BigInt(bc.amount) < 0n
97
+ )?.address,
98
+ recipient: options.recipient,
99
+ amount: credential.challenge.request.amount,
100
+ currency: options.currency,
101
+ network
102
+ };
103
+ if (options.onPayment) {
104
+ try {
105
+ options.onPayment(report);
106
+ } catch {
107
+ }
108
+ } else if (options.registryUrl) {
109
+ fetch(options.registryUrl, {
110
+ method: "POST",
111
+ headers: { "content-type": "application/json" },
112
+ body: JSON.stringify({ ...report, serverUrl: options.serverUrl })
113
+ }).catch(() => {
114
+ });
115
+ }
116
+ return receipt;
93
117
  }
94
118
  });
95
119
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/method.ts","../src/utils.ts","../src/constants.ts","../src/server.ts"],"names":["Method"],"mappings":"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","file":"server.js","sourcesContent":["import { Method, z } from 'mppx';\n\nexport const suiCharge = Method.from({\n intent: 'charge',\n name: 'sui',\n schema: {\n credential: {\n payload: z.object({\n digest: z.string(),\n }),\n },\n request: z.object({\n amount: z.string(),\n currency: z.string(),\n recipient: z.string(),\n }),\n },\n});\n","/**\n * Parse a string amount to raw bigint units without floating-point math.\n * \"0.01\" with 6 decimals → 10000n\n */\nexport function parseAmountToRaw(amount: string, decimals: number): bigint {\n const [whole = '0', frac = ''] = amount.split('.');\n const paddedFrac = frac.padEnd(decimals, '0').slice(0, decimals);\n return BigInt(whole + paddedFrac);\n}\n\n/**\n * Retry an async function with linear backoff.\n * Throws the last error if all attempts fail.\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n { attempts = 5, baseDelayMs = 1000 }: { attempts?: number; baseDelayMs?: number } = {},\n): Promise<T> {\n let lastError: unknown;\n for (let i = 0; i < attempts; i++) {\n try {\n return await fn();\n } catch (err) {\n lastError = err;\n if (i < attempts - 1) {\n await new Promise((r) => setTimeout(r, baseDelayMs * (i + 1)));\n }\n }\n }\n throw lastError;\n}\n","export const SUI_USDC_TYPE =\n '0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC';\n","import { Method, Receipt } from 'mppx';\nimport { SuiGrpcClient } from '@mysten/sui/grpc';\nimport { normalizeSuiAddress } from '@mysten/sui/utils';\nimport { suiCharge } from './method.js';\nimport { parseAmountToRaw, withRetry } from './utils.js';\n\nexport { suiCharge } from './method.js';\nexport { SUI_USDC_TYPE } from './constants.js';\n\nexport interface SuiServerOptions {\n currency: string;\n recipient: string;\n /** Number of decimal places for the currency (default: 6, e.g. USDC). */\n decimals?: number;\n rpcUrl?: string;\n network?: 'mainnet' | 'testnet' | 'devnet';\n}\n\nexport function sui(options: SuiServerOptions) {\n const network = options.network ?? 'mainnet';\n const decimals = options.decimals ?? 6;\n const client = new SuiGrpcClient({\n baseUrl: options.rpcUrl ?? `https://fullnode.${network}.sui.io:443`,\n network,\n });\n\n const normalizedRecipient = normalizeSuiAddress(options.recipient);\n\n return Method.toServer(suiCharge, {\n defaults: {\n currency: options.currency,\n recipient: options.recipient,\n },\n\n async verify({ credential }) {\n const digest = credential.payload.digest;\n\n const tx = await withRetry(\n () => client.core.getTransaction({ digest, include: { balanceChanges: true } }),\n ).catch(() => {\n throw new Error(`Could not find the referenced transaction [${digest}]`);\n });\n\n const resolved = tx.Transaction ?? tx.FailedTransaction;\n if (!resolved?.status.success) {\n throw new Error('Transaction failed on-chain');\n }\n\n const payment = resolved.balanceChanges.find(\n (bc) =>\n bc.coinType === options.currency &&\n normalizeSuiAddress(bc.address) === normalizedRecipient &&\n BigInt(bc.amount) > 0n,\n );\n\n if (!payment) {\n throw new Error(\n 'Payment not found in transaction balance changes',\n );\n }\n\n const transferredRaw = BigInt(payment.amount);\n const requestedRaw = parseAmountToRaw(credential.challenge.request.amount, decimals);\n if (transferredRaw < requestedRaw) {\n throw new Error(\n `Transferred ${transferredRaw} < requested ${requestedRaw} (raw units)`,\n );\n }\n\n return Receipt.from({\n method: 'sui',\n reference: credential.payload.digest,\n status: 'success',\n timestamp: new Date().toISOString(),\n });\n },\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/method.ts","../src/utils.ts","../src/constants.ts","../src/server.ts"],"names":["Method"],"mappings":"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","file":"server.js","sourcesContent":["import { Method, z } from 'mppx';\n\nexport const suiCharge = Method.from({\n intent: 'charge',\n name: 'sui',\n schema: {\n credential: {\n payload: z.object({\n digest: z.string(),\n }),\n },\n request: z.object({\n amount: z.string(),\n currency: z.string(),\n recipient: z.string(),\n }),\n },\n});\n","/**\n * Parse a string amount to raw bigint units without floating-point math.\n * \"0.01\" with 6 decimals → 10000n\n */\nexport function parseAmountToRaw(amount: string, decimals: number): bigint {\n const [whole = '0', frac = ''] = amount.split('.');\n const paddedFrac = frac.padEnd(decimals, '0').slice(0, decimals);\n return BigInt(whole + paddedFrac);\n}\n\n/**\n * Retry an async function with linear backoff.\n * Throws the last error if all attempts fail.\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n { attempts = 5, baseDelayMs = 1000 }: { attempts?: number; baseDelayMs?: number } = {},\n): Promise<T> {\n let lastError: unknown;\n for (let i = 0; i < attempts; i++) {\n try {\n return await fn();\n } catch (err) {\n lastError = err;\n if (i < attempts - 1) {\n await new Promise((r) => setTimeout(r, baseDelayMs * (i + 1)));\n }\n }\n }\n throw lastError;\n}\n","export const SUI_USDC_TYPE =\n '0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC';\n","import { Method, Receipt } from 'mppx';\nimport { SuiGrpcClient } from '@mysten/sui/grpc';\nimport { normalizeSuiAddress } from '@mysten/sui/utils';\nimport { suiCharge } from './method.js';\nimport { parseAmountToRaw, withRetry } from './utils.js';\n\nexport { suiCharge } from './method.js';\nexport { SUI_USDC_TYPE } from './constants.js';\n\nexport interface PaymentReport {\n digest: string;\n sender?: string;\n recipient: string;\n amount: string;\n currency: string;\n network: string;\n}\n\nexport interface SuiServerOptions {\n currency: string;\n recipient: string;\n /** Number of decimal places for the currency (default: 6, e.g. USDC). */\n decimals?: number;\n rpcUrl?: string;\n network?: 'mainnet' | 'testnet' | 'devnet';\n /**\n * Called after successful on-chain verification with payment data.\n * Use to report payments with full request context (e.g., endpoint, service).\n * When provided, replaces the built-in registryUrl reporting.\n */\n onPayment?: (report: PaymentReport) => void;\n /** @deprecated Use `onPayment` instead. URL to report verified payments to. */\n registryUrl?: string;\n /** @deprecated Use `onPayment` instead. Public URL of the server. */\n serverUrl?: string;\n}\n\nexport function sui(options: SuiServerOptions) {\n const network = options.network ?? 'mainnet';\n const decimals = options.decimals ?? 6;\n const client = new SuiGrpcClient({\n baseUrl: options.rpcUrl ?? `https://fullnode.${network}.sui.io:443`,\n network,\n });\n\n const normalizedRecipient = normalizeSuiAddress(options.recipient);\n\n return Method.toServer(suiCharge, {\n defaults: {\n currency: options.currency,\n recipient: options.recipient,\n },\n\n async verify({ credential }) {\n const digest = credential.payload.digest;\n\n const tx = await withRetry(\n () => client.core.getTransaction({ digest, include: { balanceChanges: true } }),\n ).catch(() => {\n throw new Error(`Could not find the referenced transaction [${digest}]`);\n });\n\n const resolved = tx.Transaction ?? tx.FailedTransaction;\n if (!resolved?.status.success) {\n throw new Error('Transaction failed on-chain');\n }\n\n const payment = resolved.balanceChanges.find(\n (bc) =>\n bc.coinType === options.currency &&\n normalizeSuiAddress(bc.address) === normalizedRecipient &&\n BigInt(bc.amount) > 0n,\n );\n\n if (!payment) {\n throw new Error(\n 'Payment not found in transaction balance changes',\n );\n }\n\n const transferredRaw = BigInt(payment.amount);\n const requestedRaw = parseAmountToRaw(credential.challenge.request.amount, decimals);\n if (transferredRaw < requestedRaw) {\n throw new Error(\n `Transferred ${transferredRaw} < requested ${requestedRaw} (raw units)`,\n );\n }\n\n const receipt = Receipt.from({\n method: 'sui',\n reference: credential.payload.digest,\n status: 'success',\n timestamp: new Date().toISOString(),\n });\n\n const report: PaymentReport = {\n digest,\n sender: resolved.balanceChanges.find(\n (bc) => bc.coinType === options.currency && BigInt(bc.amount) < 0n,\n )?.address,\n recipient: options.recipient,\n amount: credential.challenge.request.amount,\n currency: options.currency,\n network,\n };\n\n if (options.onPayment) {\n try { options.onPayment(report); } catch {}\n } else if (options.registryUrl) {\n fetch(options.registryUrl, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ ...report, serverUrl: options.serverUrl }),\n }).catch(() => {});\n }\n\n return receipt;\n },\n });\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suimpp/mpp",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Sui USDC payment method for the Machine Payments Protocol (MPP)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -45,6 +45,15 @@
45
45
  "directory": "packages/mpp"
46
46
  },
47
47
  "homepage": "https://suimpp.dev",
48
+ "scripts": {
49
+ "build": "tsup",
50
+ "dev": "tsup --watch",
51
+ "test": "vitest run",
52
+ "test:watch": "vitest",
53
+ "typecheck": "tsc --noEmit",
54
+ "lint": "eslint src/",
55
+ "clean": "rm -rf dist"
56
+ },
48
57
  "dependencies": {
49
58
  "@mysten/sui": "^2",
50
59
  "mppx": "^0.4.9",
@@ -57,14 +66,5 @@
57
66
  "typescript": "^5",
58
67
  "vitest": "^3"
59
68
  },
60
- "license": "MIT",
61
- "scripts": {
62
- "build": "tsup",
63
- "dev": "tsup --watch",
64
- "test": "vitest run",
65
- "test:watch": "vitest",
66
- "typecheck": "tsc --noEmit",
67
- "lint": "eslint src/",
68
- "clean": "rm -rf dist"
69
- }
70
- }
69
+ "license": "MIT"
70
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 suimpp
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.