@suimpp/mpp 0.3.0 → 0.3.1

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,31 @@ 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
+ if (options.registryUrl) {
136
+ fetch(options.registryUrl, {
137
+ method: "POST",
138
+ headers: { "content-type": "application/json" },
139
+ body: JSON.stringify({
140
+ digest,
141
+ sender: resolved.balanceChanges.find(
142
+ (bc) => bc.coinType === options.currency && BigInt(bc.amount) < 0n
143
+ )?.address,
144
+ recipient: options.recipient,
145
+ amount: credential.challenge.request.amount,
146
+ currency: options.currency,
147
+ network,
148
+ serverUrl: options.serverUrl
149
+ })
150
+ }).catch(() => {
151
+ });
152
+ }
153
+ return receipt;
135
154
  }
136
155
  });
137
156
  }
@@ -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":"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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 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 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 /** URL to report verified payments to (e.g. https://suimpp.dev/api/report). Fire-and-forget POST after successful verification. */\n registryUrl?: string;\n /** Public URL of the server (e.g. https://mpp.t2000.ai). Sent with payment reports for server identification. */\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 if (options.registryUrl) {\n fetch(options.registryUrl, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\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 serverUrl: options.serverUrl,\n }),\n }).catch(() => {});\n }\n\n return receipt;\n },\n });\n}\n"]}
package/dist/index.js CHANGED
@@ -124,12 +124,31 @@ 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
+ if (options.registryUrl) {
134
+ fetch(options.registryUrl, {
135
+ method: "POST",
136
+ headers: { "content-type": "application/json" },
137
+ body: JSON.stringify({
138
+ digest,
139
+ sender: resolved.balanceChanges.find(
140
+ (bc) => bc.coinType === options.currency && BigInt(bc.amount) < 0n
141
+ )?.address,
142
+ recipient: options.recipient,
143
+ amount: credential.challenge.request.amount,
144
+ currency: options.currency,
145
+ network,
146
+ serverUrl: options.serverUrl
147
+ })
148
+ }).catch(() => {
149
+ });
150
+ }
151
+ return receipt;
133
152
  }
134
153
  });
135
154
  }
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 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 /** URL to report verified payments to (e.g. https://suimpp.dev/api/report). Fire-and-forget POST after successful verification. */\n registryUrl?: string;\n /** Public URL of the server (e.g. https://mpp.t2000.ai). Sent with payment reports for server identification. */\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 if (options.registryUrl) {\n fetch(options.registryUrl, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\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 serverUrl: options.serverUrl,\n }),\n }).catch(() => {});\n }\n\n return receipt;\n },\n });\n}\n"]}
package/dist/server.cjs CHANGED
@@ -86,12 +86,31 @@ 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
+ if (options.registryUrl) {
96
+ fetch(options.registryUrl, {
97
+ method: "POST",
98
+ headers: { "content-type": "application/json" },
99
+ body: JSON.stringify({
100
+ digest,
101
+ sender: resolved.balanceChanges.find(
102
+ (bc) => bc.coinType === options.currency && BigInt(bc.amount) < 0n
103
+ )?.address,
104
+ recipient: options.recipient,
105
+ amount: credential.challenge.request.amount,
106
+ currency: options.currency,
107
+ network,
108
+ serverUrl: options.serverUrl
109
+ })
110
+ }).catch(() => {
111
+ });
112
+ }
113
+ return receipt;
95
114
  }
96
115
  });
97
116
  }
@@ -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 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 /** URL to report verified payments to (e.g. https://suimpp.dev/api/report). Fire-and-forget POST after successful verification. */\n registryUrl?: string;\n /** Public URL of the server (e.g. https://mpp.t2000.ai). Sent with payment reports for server identification. */\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 if (options.registryUrl) {\n fetch(options.registryUrl, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\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 serverUrl: options.serverUrl,\n }),\n }).catch(() => {});\n }\n\n return receipt;\n },\n });\n}\n"]}
package/dist/server.d.cts CHANGED
@@ -10,6 +10,10 @@ interface SuiServerOptions {
10
10
  decimals?: number;
11
11
  rpcUrl?: string;
12
12
  network?: 'mainnet' | 'testnet' | 'devnet';
13
+ /** URL to report verified payments to (e.g. https://suimpp.dev/api/report). Fire-and-forget POST after successful verification. */
14
+ registryUrl?: string;
15
+ /** Public URL of the server (e.g. https://mpp.t2000.ai). Sent with payment reports for server identification. */
16
+ serverUrl?: string;
13
17
  }
14
18
  declare function sui(options: SuiServerOptions): Method.Server<{
15
19
  readonly intent: "charge";
package/dist/server.d.ts CHANGED
@@ -10,6 +10,10 @@ interface SuiServerOptions {
10
10
  decimals?: number;
11
11
  rpcUrl?: string;
12
12
  network?: 'mainnet' | 'testnet' | 'devnet';
13
+ /** URL to report verified payments to (e.g. https://suimpp.dev/api/report). Fire-and-forget POST after successful verification. */
14
+ registryUrl?: string;
15
+ /** Public URL of the server (e.g. https://mpp.t2000.ai). Sent with payment reports for server identification. */
16
+ serverUrl?: string;
13
17
  }
14
18
  declare function sui(options: SuiServerOptions): Method.Server<{
15
19
  readonly intent: "charge";
package/dist/server.js CHANGED
@@ -84,12 +84,31 @@ 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
+ if (options.registryUrl) {
94
+ fetch(options.registryUrl, {
95
+ method: "POST",
96
+ headers: { "content-type": "application/json" },
97
+ body: JSON.stringify({
98
+ digest,
99
+ sender: resolved.balanceChanges.find(
100
+ (bc) => bc.coinType === options.currency && BigInt(bc.amount) < 0n
101
+ )?.address,
102
+ recipient: options.recipient,
103
+ amount: credential.challenge.request.amount,
104
+ currency: options.currency,
105
+ network,
106
+ serverUrl: options.serverUrl
107
+ })
108
+ }).catch(() => {
109
+ });
110
+ }
111
+ return receipt;
93
112
  }
94
113
  });
95
114
  }
@@ -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":";;;;;AAEO,IAAM,SAAA,GAAY,OAAO,IAAA,CAAK;AAAA,EACnC,MAAA,EAAQ,QAAA;AAAA,EACR,IAAA,EAAM,KAAA;AAAA,EACN,MAAA,EAAQ;AAAA,IACN,UAAA,EAAY;AAAA,MACV,OAAA,EAAS,EAAE,MAAA,CAAO;AAAA,QAChB,MAAA,EAAQ,EAAE,MAAA;AAAO,OAClB;AAAA,KACH;AAAA,IACA,OAAA,EAAS,EAAE,MAAA,CAAO;AAAA,MAChB,MAAA,EAAQ,EAAE,MAAA,EAAO;AAAA,MACjB,QAAA,EAAU,EAAE,MAAA,EAAO;AAAA,MACnB,SAAA,EAAW,EAAE,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;;;ACqBK,SAAS,IAAI,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,IAAI,aAAA,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,GAAsB,mBAAA,CAAoB,OAAA,CAAQ,SAAS,CAAA;AAEjE,EAAA,OAAOA,MAAAA,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,IACxB,mBAAA,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,MAAM,OAAA,GAAU,QAAQ,IAAA,CAAK;AAAA,QAC3B,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;AAED,MAAA,IAAI,QAAQ,WAAA,EAAa;AACvB,QAAA,KAAA,CAAM,QAAQ,WAAA,EAAa;AAAA,UACzB,MAAA,EAAQ,MAAA;AAAA,UACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,UAC9C,IAAA,EAAM,KAAK,SAAA,CAAU;AAAA,YACnB,MAAA;AAAA,YACA,MAAA,EAAQ,SAAS,cAAA,CAAe,IAAA;AAAA,cAC9B,CAAC,OAAO,EAAA,CAAG,QAAA,KAAa,QAAQ,QAAA,IAAY,MAAA,CAAO,EAAA,CAAG,MAAM,CAAA,GAAI;AAAA,aAClE,EAAG,OAAA;AAAA,YACH,WAAW,OAAA,CAAQ,SAAA;AAAA,YACnB,MAAA,EAAQ,UAAA,CAAW,SAAA,CAAU,OAAA,CAAQ,MAAA;AAAA,YACrC,UAAU,OAAA,CAAQ,QAAA;AAAA,YAClB,OAAA;AAAA,YACA,WAAW,OAAA,CAAQ;AAAA,WACpB;AAAA,SACF,CAAA,CAAE,KAAA,CAAM,MAAM;AAAA,QAAC,CAAC,CAAA;AAAA,MACnB;AAEA,MAAA,OAAO,OAAA;AAAA,IACT;AAAA,GACD,CAAA;AACH","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 /** URL to report verified payments to (e.g. https://suimpp.dev/api/report). Fire-and-forget POST after successful verification. */\n registryUrl?: string;\n /** Public URL of the server (e.g. https://mpp.t2000.ai). Sent with payment reports for server identification. */\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 if (options.registryUrl) {\n fetch(options.registryUrl, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\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 serverUrl: options.serverUrl,\n }),\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.3.1",
4
4
  "description": "Sui USDC payment method for the Machine Payments Protocol (MPP)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",