@suimpp/mpp 0.3.1 → 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 +17 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +17 -12
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +17 -12
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +17 -3
- package/dist/server.d.ts +17 -3
- package/dist/server.js +17 -12
- package/dist/server.js.map +1 -1
- package/package.json +12 -12
- package/LICENSE +0 -21
package/dist/index.cjs
CHANGED
|
@@ -132,21 +132,26 @@ function sui2(options) {
|
|
|
132
132
|
status: "success",
|
|
133
133
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
134
134
|
});
|
|
135
|
-
|
|
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) {
|
|
136
151
|
fetch(options.registryUrl, {
|
|
137
152
|
method: "POST",
|
|
138
153
|
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
|
-
})
|
|
154
|
+
body: JSON.stringify({ ...report, serverUrl: options.serverUrl })
|
|
150
155
|
}).catch(() => {
|
|
151
156
|
});
|
|
152
157
|
}
|
package/dist/index.cjs.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","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"]}
|
|
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
|
@@ -130,21 +130,26 @@ function sui2(options) {
|
|
|
130
130
|
status: "success",
|
|
131
131
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
132
132
|
});
|
|
133
|
-
|
|
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) {
|
|
134
149
|
fetch(options.registryUrl, {
|
|
135
150
|
method: "POST",
|
|
136
151
|
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
|
-
})
|
|
152
|
+
body: JSON.stringify({ ...report, serverUrl: options.serverUrl })
|
|
148
153
|
}).catch(() => {
|
|
149
154
|
});
|
|
150
155
|
}
|
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 /** 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"]}
|
|
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
|
@@ -92,21 +92,26 @@ function sui(options) {
|
|
|
92
92
|
status: "success",
|
|
93
93
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
94
94
|
});
|
|
95
|
-
|
|
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) {
|
|
96
111
|
fetch(options.registryUrl, {
|
|
97
112
|
method: "POST",
|
|
98
113
|
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
|
-
})
|
|
114
|
+
body: JSON.stringify({ ...report, serverUrl: options.serverUrl })
|
|
110
115
|
}).catch(() => {
|
|
111
116
|
});
|
|
112
117
|
}
|
package/dist/server.cjs.map
CHANGED
|
@@ -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":";;;;;;;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;;;
|
|
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,9 +18,15 @@ interface SuiServerOptions {
|
|
|
10
18
|
decimals?: number;
|
|
11
19
|
rpcUrl?: string;
|
|
12
20
|
network?: 'mainnet' | 'testnet' | 'devnet';
|
|
13
|
-
/**
|
|
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. */
|
|
14
28
|
registryUrl?: string;
|
|
15
|
-
/**
|
|
29
|
+
/** @deprecated Use `onPayment` instead. Public URL of the server. */
|
|
16
30
|
serverUrl?: string;
|
|
17
31
|
}
|
|
18
32
|
declare function sui(options: SuiServerOptions): Method.Server<{
|
|
@@ -35,4 +49,4 @@ declare function sui(options: SuiServerOptions): Method.Server<{
|
|
|
35
49
|
readonly recipient: string;
|
|
36
50
|
}, undefined>;
|
|
37
51
|
|
|
38
|
-
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,9 +18,15 @@ interface SuiServerOptions {
|
|
|
10
18
|
decimals?: number;
|
|
11
19
|
rpcUrl?: string;
|
|
12
20
|
network?: 'mainnet' | 'testnet' | 'devnet';
|
|
13
|
-
/**
|
|
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. */
|
|
14
28
|
registryUrl?: string;
|
|
15
|
-
/**
|
|
29
|
+
/** @deprecated Use `onPayment` instead. Public URL of the server. */
|
|
16
30
|
serverUrl?: string;
|
|
17
31
|
}
|
|
18
32
|
declare function sui(options: SuiServerOptions): Method.Server<{
|
|
@@ -35,4 +49,4 @@ declare function sui(options: SuiServerOptions): Method.Server<{
|
|
|
35
49
|
readonly recipient: string;
|
|
36
50
|
}, undefined>;
|
|
37
51
|
|
|
38
|
-
export { type SuiServerOptions, sui };
|
|
52
|
+
export { type PaymentReport, type SuiServerOptions, sui };
|
package/dist/server.js
CHANGED
|
@@ -90,21 +90,26 @@ function sui(options) {
|
|
|
90
90
|
status: "success",
|
|
91
91
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
92
92
|
});
|
|
93
|
-
|
|
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) {
|
|
94
109
|
fetch(options.registryUrl, {
|
|
95
110
|
method: "POST",
|
|
96
111
|
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
|
-
})
|
|
112
|
+
body: JSON.stringify({ ...report, serverUrl: options.serverUrl })
|
|
108
113
|
}).catch(() => {
|
|
109
114
|
});
|
|
110
115
|
}
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;;;
|
|
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
|
+
"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
|
-
|
|
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.
|