@heraldprotocol/mpp 0.0.5 → 0.0.6

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.
@@ -306,7 +306,7 @@ function charge2(parameters = {}) {
306
306
  })
307
307
  });
308
308
  if (waitForConfirmation) {
309
- const receipt = await (0, import_actions.getTransactionReceipt)(client, {
309
+ const receipt = await (0, import_actions.waitForTransactionReceipt)(client, {
310
310
  hash
311
311
  });
312
312
  return toReceipt(receipt);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/index.ts","../../src/server/Charge.ts","../../src/defaults.ts","../../src/Methods.ts","../../src/server/Methods.ts"],"sourcesContent":["export { charge } from \"./Charge.js\";\nexport { zerog } from \"./Methods.js\";\n","import type { Account, Address, Chain, Client, TransactionReceipt } from \"viem\";\nimport { Method, Store } from \"mppx\";\nimport {\n encodeFunctionData,\n erc20Abi,\n isAddressEqual,\n keccak256,\n parseEventLogs,\n} from \"viem\";\nimport { parseAccount } from \"viem/accounts\";\nimport { getTransactionReceipt, sendTransaction } from \"viem/actions\";\n\nimport type { MaybePromise } from \"../types.js\";\nimport * as defaults from \"../defaults.js\";\nimport * as Methods from \"../Methods.js\";\n\n/**\n * Creates a 0G charge method intent for usage on the server.\n *\n * @example\n * ```ts\n * import { zerog } from \"@heraldprotocol/mpp/server\";\n *\n * const charge = zerog.charge({\n * recipient: \"0x...\",\n * currency: \"0x...\",\n * account: privateKeyToAccount(\"0x...\"),\n * });\n * ```\n */\nexport function charge(parameters: charge.Parameters = {}): Method.AnyServer {\n const {\n amount,\n currency = defaults.resolveCurrency(parameters),\n decimals = defaults.decimals,\n description,\n externalId,\n recipient,\n waitForConfirmation = true,\n } = parameters;\n const store = (parameters.store ??\n Store.memory()) as Store.Store<charge.StoreItemMap>;\n\n if (currency.toLowerCase() in defaults.erc3009Tokens && !parameters.account) {\n throw new Error(\n \"ERC-3009 requires an `account` parameter so the server can sign and broadcast \" +\n \"the transferWithAuthorization transaction.\"\n );\n }\n\n const serverAccount = parameters.account\n ? typeof parameters.account === \"string\"\n ? parseAccount(parameters.account)\n : parameters.account\n : undefined;\n\n const resolveClient = async (\n chainId?: number | undefined\n ): Promise<Client> => {\n if (parameters.getClient) return parameters.getClient({ chainId });\n const id = chainId ?? defaults.chainId.mainnet;\n const url = defaults.rpcUrl[id];\n if (!url) throw new Error(`No RPC URL configured for chainId ${id}.`);\n const { createClient, http } = await import(\"viem\");\n return createClient({ chain: { id } as Chain, transport: http(url) });\n };\n\n return Method.toServer(Methods.charge, {\n defaults: {\n amount,\n currency,\n decimals,\n description,\n externalId,\n recipient,\n } as never,\n\n async request({ request }) {\n const chainId = await (async () => {\n if (request.chainId) return request.chainId;\n if (parameters.testnet) return defaults.chainId.testnet;\n return (await resolveClient(undefined)).chain?.id;\n })();\n\n const client = await (async () => {\n try {\n return await resolveClient(chainId);\n } catch {\n throw new Error(`No client configured with chainId ${chainId}.`);\n }\n })();\n if (client.chain?.id !== chainId)\n throw new Error(`Client not configured with chainId ${chainId}.`);\n\n return { ...request, chainId };\n },\n\n async verify({ credential, request }) {\n const { challenge } = credential;\n const { chainId } = request;\n\n const client = await resolveClient(chainId);\n\n const { request: challengeRequest } = challenge;\n const challengeAmount = challengeRequest.amount as string;\n const challengeCurrency = challengeRequest.currency as Address;\n const challengeRecipient = challengeRequest.recipient as Address;\n const expires = challenge.expires;\n\n if (expires && new Date(expires) < new Date()) {\n throw new Error(`Payment expired at ${expires}.`);\n }\n\n const payload = credential.payload;\n\n switch (payload.type) {\n case \"hash\": {\n const hash = payload.hash as `0x${string}`;\n await assertHashUnused(store, hash);\n\n const sender = extractDidAddress(credential.source);\n if (!sender)\n throw new Error(\n \"Hash credential is missing a valid `source` DID — cannot verify sender.\"\n );\n\n const receipt = await getTransactionReceipt(client, { hash });\n\n const transferLogs = parseEventLogs({\n abi: erc20Abi,\n eventName: \"Transfer\",\n logs: receipt.logs,\n });\n\n const match = transferLogs.find(\n (log) =>\n isAddressEqual(log.address, challengeCurrency) &&\n isAddressEqual(log.args.from, sender) &&\n isAddressEqual(log.args.to, challengeRecipient) &&\n log.args.value.toString() === challengeAmount\n );\n\n if (!match)\n throw new MismatchError(\n \"Payment verification failed: no matching ERC-20 transfer found.\",\n {\n sender,\n amount: challengeAmount,\n currency: challengeCurrency,\n recipient: challengeRecipient,\n }\n );\n\n await markHashUsed(store, hash);\n\n return toReceipt(receipt);\n }\n\n case \"authorization\": {\n if (!serverAccount) {\n throw new Error(\n \"Received ERC-3009 authorization credential but no server `account` is configured. \" +\n \"Set `account` in charge parameters to broadcast transferWithAuthorization.\"\n );\n }\n\n const { from, to, value, validAfter, validBefore, nonce, signature } =\n payload as {\n from: string;\n to: string;\n value: string;\n validAfter: string;\n validBefore: string;\n nonce: string;\n signature: string;\n };\n\n // Split signature into v, r, s for the contract call\n const r = `0x${signature.slice(2, 66)}` as `0x${string}`;\n const s = `0x${signature.slice(66, 130)}` as `0x${string}`;\n const v = parseInt(signature.slice(130, 132), 16);\n\n // Validate authorization parameters match the challenge\n if (!isAddressEqual(to as Address, challengeRecipient))\n throw new MismatchError(\n \"Authorization recipient does not match challenge.\",\n { expected: challengeRecipient, actual: to }\n );\n\n if (value !== challengeAmount)\n throw new MismatchError(\n \"Authorization amount does not match challenge.\",\n { expected: challengeAmount, actual: value }\n );\n\n // Check expiry from the authorization itself\n const validBeforeTs = Number(validBefore);\n if (\n validBeforeTs > 0 &&\n validBeforeTs < Math.floor(Date.now() / 1000)\n ) {\n throw new Error(\n `ERC-3009 authorization expired (validBefore: ${validBefore}).`\n );\n }\n\n const signatureHash = keccak256(signature as `0x${string}`);\n await assertHashUnused(store, signatureHash);\n await markHashUsed(store, signatureHash);\n\n const hash = await sendTransaction(client, {\n account: serverAccount,\n chain: client.chain,\n to: challengeCurrency,\n data: encodeFunctionData({\n abi: defaults.erc3009Abi,\n functionName: \"transferWithAuthorization\",\n args: [\n from as Address,\n to as Address,\n BigInt(value),\n BigInt(validAfter),\n BigInt(validBefore),\n nonce as `0x${string}`,\n v,\n r as `0x${string}`,\n s as `0x${string}`,\n ],\n }),\n } as never);\n\n if (waitForConfirmation) {\n const receipt = await getTransactionReceipt(client, {\n hash,\n });\n\n return toReceipt(receipt);\n }\n\n return {\n method: \"zerog\" as const,\n status: \"success\" as const,\n timestamp: new Date().toISOString(),\n reference: hash,\n };\n }\n\n default:\n throw new Error(\n `Unsupported credential type \"${(payload as { type: string }).type}\".`\n );\n }\n },\n });\n}\n\nexport declare namespace charge {\n type StoreItemMap = {\n [key: `mppx:charge:${string}`]: number;\n };\n\n type Parameters = {\n /** Default payment amount (human-readable, e.g. \"1.50\"). */\n amount?: string | undefined;\n /** ERC-20 token contract address. */\n currency?: string | undefined;\n /** Token decimals. @default 6 */\n decimals?: number | undefined;\n /** Human-readable description. */\n description?: string | undefined;\n /** External identifier to echo back in receipt. */\n externalId?: string | undefined;\n /** Recipient address for payments. */\n recipient?: string | undefined;\n /** Testnet mode. */\n testnet?: boolean | undefined;\n /**\n * Whether to wait for the charge transaction to confirm on-chain.\n * @default true\n */\n waitForConfirmation?: boolean | undefined;\n /** Function that returns a viem Client for the given chain ID. */\n getClient?:\n | ((parameters: { chainId?: number | undefined }) => MaybePromise<Client>)\n | undefined;\n /**\n * Server account used to broadcast `transferWithAuthorization` transactions.\n * Required when accepting `authorization` payloads. The server pays gas\n * from this account.\n */\n account?: Account | Address | undefined;\n /**\n * Store for transaction hash replay protection.\n *\n * Use a shared store in multi-instance deployments so consumed hashes are\n * visible across all server instances.\n */\n store?: Store.Store | undefined;\n };\n}\n\n/** @internal */\nfunction getHashStoreKey(hash: `0x${string}`): `mppx:charge:${string}` {\n return `mppx:charge:${hash.toLowerCase()}`;\n}\n\n/** @internal */\nasync function assertHashUnused(\n store: Store.Store<charge.StoreItemMap>,\n hash: `0x${string}`\n): Promise<void> {\n const seen = await store.get(getHashStoreKey(hash));\n if (seen !== null) throw new Error(\"Transaction hash has already been used.\");\n}\n\n/** @internal */\nasync function markHashUsed(\n store: Store.Store<charge.StoreItemMap>,\n hash: `0x${string}`\n): Promise<void> {\n await store.put(getHashStoreKey(hash), Date.now());\n}\n\n/** @internal */\nfunction toReceipt(receipt: TransactionReceipt) {\n const { status, transactionHash } = receipt;\n if (status !== \"success\") {\n throw new Error(`Transaction reverted: ${transactionHash}`);\n }\n return {\n method: \"zerog\" as const,\n status: \"success\" as const,\n timestamp: new Date().toISOString(),\n reference: transactionHash,\n };\n}\n\n/**\n * Extracts an Ethereum address from a `did:pkh:eip155:<chainId>:<address>` DID.\n * Returns `undefined` if the source is missing or malformed.\n * @internal\n */\nfunction extractDidAddress(source: string | undefined): Address | undefined {\n if (!source) return undefined;\n const match = /^did:pkh:eip155:\\d+:(0x[0-9a-fA-F]{40})$/.exec(source);\n return match ? (match[1] as Address) : undefined;\n}\n\n/** @internal */\nclass MismatchError extends Error {\n override readonly name = \"MismatchError\";\n\n constructor(reason: string, details: Record<string, string>) {\n super(\n [\n reason,\n ...Object.entries(details).map(([k, v]) => ` - ${k}: ${v}`),\n ].join(\"\\n\")\n );\n }\n}\n","export const chainId = {\n mainnet: 16661,\n testnet: 16602,\n} as const;\n\nexport type ChainId = (typeof chainId)[keyof typeof chainId];\n\nconst USDC_E = \"0x1f3aa82227281ca364bfb3d253b0f1af1da6473e\";\n\n/** Chain ID → default currency. */\nexport const currency: Partial<Record<ChainId, string>> = {\n [chainId.mainnet]: USDC_E,\n};\n\n/** Default token decimals for USDC.e. */\nexport const decimals = 6;\n\n/** Default RPC URLs per chain. */\nexport const rpcUrl: Record<number, string> = {\n [chainId.mainnet]: \"https://evmrpc.0g.ai\",\n [chainId.testnet]: \"https://evmrpc-testnet.0g.ai\",\n};\n\n/** ERC-3009 ABI (`transferWithAuthorization`, `receiveWithAuthorization`). */\nexport const erc3009Abi = [\n {\n type: \"function\",\n name: \"transferWithAuthorization\",\n inputs: [\n { name: \"from\", type: \"address\" },\n { name: \"to\", type: \"address\" },\n { name: \"value\", type: \"uint256\" },\n { name: \"validAfter\", type: \"uint256\" },\n { name: \"validBefore\", type: \"uint256\" },\n { name: \"nonce\", type: \"bytes32\" },\n { name: \"v\", type: \"uint8\" },\n { name: \"r\", type: \"bytes32\" },\n { name: \"s\", type: \"bytes32\" },\n ],\n outputs: [],\n stateMutability: \"nonpayable\",\n },\n {\n type: \"function\",\n name: \"receiveWithAuthorization\",\n inputs: [\n { name: \"from\", type: \"address\" },\n { name: \"to\", type: \"address\" },\n { name: \"value\", type: \"uint256\" },\n { name: \"validAfter\", type: \"uint256\" },\n { name: \"validBefore\", type: \"uint256\" },\n { name: \"nonce\", type: \"bytes32\" },\n { name: \"v\", type: \"uint8\" },\n { name: \"r\", type: \"bytes32\" },\n { name: \"s\", type: \"bytes32\" },\n ],\n outputs: [],\n stateMutability: \"nonpayable\",\n },\n {\n type: \"function\",\n name: \"name\",\n inputs: [],\n outputs: [{ name: \"\", type: \"string\" }],\n stateMutability: \"view\",\n },\n {\n type: \"function\",\n name: \"version\",\n inputs: [],\n outputs: [{ name: \"\", type: \"string\" }],\n stateMutability: \"view\",\n },\n] as const;\n\n/**\n * Known tokens that support ERC-3009 (TransferWithAuthorization).\n * Keyed by lowercase address for case-insensitive lookup.\n */\nexport const erc3009Tokens: Record<string, { name: string; version: string }> =\n {\n [USDC_E.toLowerCase()]: {\n name: \"Bridged USDC\",\n version: \"2\",\n },\n };\n\n/** Resolves the default currency for a given chain. */\nexport function resolveCurrency(parameters: {\n chainId?: number | undefined;\n testnet?: boolean | undefined;\n}): string {\n const id =\n parameters.chainId ??\n (parameters.testnet ? chainId.testnet : chainId.mainnet);\n const resolved = currency[id as ChainId];\n if (!resolved)\n throw new Error(`No default currency configured for chainId ${id}.`);\n return resolved;\n}\n","import { Method, z } from \"mppx\";\nimport { parseUnits } from \"viem\";\n\n/**\n * 0G charge intent for one-time ERC-20 token transfers.\n */\nexport const charge = Method.from({\n name: \"zerog\",\n intent: \"charge\",\n schema: {\n credential: {\n payload: z.discriminatedUnion(\"type\", [\n z.object({ hash: z.hash(), type: z.literal(\"hash\") }),\n z.object({\n type: z.literal(\"authorization\"),\n from: z.string(),\n to: z.string(),\n value: z.string(),\n validAfter: z.string(),\n validBefore: z.string(),\n nonce: z.string(),\n signature: z.string(),\n }),\n ]),\n },\n request: z.pipe(\n z.object({\n amount: z.amount(),\n chainId: z.optional(z.number()),\n currency: z.string(),\n decimals: z.number(),\n description: z.optional(z.string()),\n externalId: z.optional(z.string()),\n recipient: z.optional(z.string()),\n }),\n z.transform(({ amount, chainId, decimals, ...rest }) => ({\n ...rest,\n amount: parseUnits(amount, decimals).toString(),\n ...(chainId !== undefined ? { methodDetails: { chainId } } : {}),\n }))\n ),\n },\n});\n","import type { Method } from \"mppx\";\n\nimport { charge as charge_ } from \"./Charge.js\";\n\n/**\n * Creates a 0G `charge` server method.\n *\n * @example\n * ```ts\n * import { Mppx } from \"mppx/server\";\n * import { zerog } from \"@heraldprotocol/mpp/server\";\n *\n * const mppx = Mppx.create({\n * methods: [zerog({ recipient: \"0x...\", currency: \"0x...\" })],\n * });\n * ```\n */\nexport function zerog(\n parameters?: zerog.Parameters\n): readonly [Method.AnyServer] {\n return [zerog.charge(parameters)] as const;\n}\n\nexport namespace zerog {\n export type Parameters = charge_.Parameters;\n\n /** Creates a 0G `charge` method for one-time ERC-20 token transfers. */\n export const charge = charge_;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA,gBAAAA;AAAA,EAAA;AAAA;AAAA;;;ACCA,IAAAC,eAA8B;AAC9B,IAAAC,eAMO;AACP,sBAA6B;AAC7B,qBAAuD;;;ACVhD,IAAM,UAAU;AAAA,EACrB,SAAS;AAAA,EACT,SAAS;AACX;AAIA,IAAM,SAAS;AAGR,IAAM,WAA6C;AAAA,EACxD,CAAC,QAAQ,OAAO,GAAG;AACrB;AAGO,IAAM,WAAW;AAGjB,IAAM,SAAiC;AAAA,EAC5C,CAAC,QAAQ,OAAO,GAAG;AAAA,EACnB,CAAC,QAAQ,OAAO,GAAG;AACrB;AAGO,IAAM,aAAa;AAAA,EACxB;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,MACN,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,MAChC,EAAE,MAAM,MAAM,MAAM,UAAU;AAAA,MAC9B,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,MACjC,EAAE,MAAM,cAAc,MAAM,UAAU;AAAA,MACtC,EAAE,MAAM,eAAe,MAAM,UAAU;AAAA,MACvC,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,MACjC,EAAE,MAAM,KAAK,MAAM,QAAQ;AAAA,MAC3B,EAAE,MAAM,KAAK,MAAM,UAAU;AAAA,MAC7B,EAAE,MAAM,KAAK,MAAM,UAAU;AAAA,IAC/B;AAAA,IACA,SAAS,CAAC;AAAA,IACV,iBAAiB;AAAA,EACnB;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,MACN,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,MAChC,EAAE,MAAM,MAAM,MAAM,UAAU;AAAA,MAC9B,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,MACjC,EAAE,MAAM,cAAc,MAAM,UAAU;AAAA,MACtC,EAAE,MAAM,eAAe,MAAM,UAAU;AAAA,MACvC,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,MACjC,EAAE,MAAM,KAAK,MAAM,QAAQ;AAAA,MAC3B,EAAE,MAAM,KAAK,MAAM,UAAU;AAAA,MAC7B,EAAE,MAAM,KAAK,MAAM,UAAU;AAAA,IAC/B;AAAA,IACA,SAAS,CAAC;AAAA,IACV,iBAAiB;AAAA,EACnB;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ,CAAC;AAAA,IACT,SAAS,CAAC,EAAE,MAAM,IAAI,MAAM,SAAS,CAAC;AAAA,IACtC,iBAAiB;AAAA,EACnB;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ,CAAC;AAAA,IACT,SAAS,CAAC,EAAE,MAAM,IAAI,MAAM,SAAS,CAAC;AAAA,IACtC,iBAAiB;AAAA,EACnB;AACF;AAMO,IAAM,gBACX;AAAA,EACE,CAAC,OAAO,YAAY,CAAC,GAAG;AAAA,IACtB,MAAM;AAAA,IACN,SAAS;AAAA,EACX;AACF;AAGK,SAAS,gBAAgB,YAGrB;AACT,QAAM,KACJ,WAAW,YACV,WAAW,UAAU,QAAQ,UAAU,QAAQ;AAClD,QAAM,WAAW,SAAS,EAAa;AACvC,MAAI,CAAC;AACH,UAAM,IAAI,MAAM,8CAA8C,EAAE,GAAG;AACrE,SAAO;AACT;;;ACnGA,kBAA0B;AAC1B,kBAA2B;AAKpB,IAAM,SAAS,mBAAO,KAAK;AAAA,EAChC,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AAAA,IACN,YAAY;AAAA,MACV,SAAS,cAAE,mBAAmB,QAAQ;AAAA,QACpC,cAAE,OAAO,EAAE,MAAM,cAAE,KAAK,GAAG,MAAM,cAAE,QAAQ,MAAM,EAAE,CAAC;AAAA,QACpD,cAAE,OAAO;AAAA,UACP,MAAM,cAAE,QAAQ,eAAe;AAAA,UAC/B,MAAM,cAAE,OAAO;AAAA,UACf,IAAI,cAAE,OAAO;AAAA,UACb,OAAO,cAAE,OAAO;AAAA,UAChB,YAAY,cAAE,OAAO;AAAA,UACrB,aAAa,cAAE,OAAO;AAAA,UACtB,OAAO,cAAE,OAAO;AAAA,UAChB,WAAW,cAAE,OAAO;AAAA,QACtB,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAAA,IACA,SAAS,cAAE;AAAA,MACT,cAAE,OAAO;AAAA,QACP,QAAQ,cAAE,OAAO;AAAA,QACjB,SAAS,cAAE,SAAS,cAAE,OAAO,CAAC;AAAA,QAC9B,UAAU,cAAE,OAAO;AAAA,QACnB,UAAU,cAAE,OAAO;AAAA,QACnB,aAAa,cAAE,SAAS,cAAE,OAAO,CAAC;AAAA,QAClC,YAAY,cAAE,SAAS,cAAE,OAAO,CAAC;AAAA,QACjC,WAAW,cAAE,SAAS,cAAE,OAAO,CAAC;AAAA,MAClC,CAAC;AAAA,MACD,cAAE,UAAU,CAAC,EAAE,QAAQ,SAAAC,UAAS,UAAAC,WAAU,GAAG,KAAK,OAAO;AAAA,QACvD,GAAG;AAAA,QACH,YAAQ,wBAAW,QAAQA,SAAQ,EAAE,SAAS;AAAA,QAC9C,GAAID,aAAY,SAAY,EAAE,eAAe,EAAE,SAAAA,SAAQ,EAAE,IAAI,CAAC;AAAA,MAChE,EAAE;AAAA,IACJ;AAAA,EACF;AACF,CAAC;;;AFZM,SAASE,QAAO,aAAgC,CAAC,GAAqB;AAC3E,QAAM;AAAA,IACJ;AAAA,IACA,UAAAC,YAAoB,gBAAgB,UAAU;AAAA,IAC9C,UAAAC,YAAoB;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,IACA,sBAAsB;AAAA,EACxB,IAAI;AACJ,QAAM,QAAS,WAAW,SACxB,mBAAM,OAAO;AAEf,MAAID,UAAS,YAAY,KAAc,iBAAiB,CAAC,WAAW,SAAS;AAC3E,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAEA,QAAM,gBAAgB,WAAW,UAC7B,OAAO,WAAW,YAAY,eAC5B,8BAAa,WAAW,OAAO,IAC/B,WAAW,UACb;AAEJ,QAAM,gBAAgB,OACpBE,aACoB;AACpB,QAAI,WAAW,UAAW,QAAO,WAAW,UAAU,EAAE,SAAAA,SAAQ,CAAC;AACjE,UAAM,KAAKA,YAAoB,QAAQ;AACvC,UAAM,MAAe,OAAO,EAAE;AAC9B,QAAI,CAAC,IAAK,OAAM,IAAI,MAAM,qCAAqC,EAAE,GAAG;AACpE,UAAM,EAAE,cAAc,KAAK,IAAI,MAAM,OAAO,MAAM;AAClD,WAAO,aAAa,EAAE,OAAO,EAAE,GAAG,GAAY,WAAW,KAAK,GAAG,EAAE,CAAC;AAAA,EACtE;AAEA,SAAO,oBAAO,SAAiB,QAAQ;AAAA,IACrC,UAAU;AAAA,MACR;AAAA,MACA,UAAAF;AAAA,MACA,UAAAC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IAEA,MAAM,QAAQ,EAAE,QAAQ,GAAG;AACzB,YAAMC,WAAU,OAAO,YAAY;AACjC,YAAI,QAAQ,QAAS,QAAO,QAAQ;AACpC,YAAI,WAAW,QAAS,QAAgB,QAAQ;AAChD,gBAAQ,MAAM,cAAc,MAAS,GAAG,OAAO;AAAA,MACjD,GAAG;AAEH,YAAM,SAAS,OAAO,YAAY;AAChC,YAAI;AACF,iBAAO,MAAM,cAAcA,QAAO;AAAA,QACpC,QAAQ;AACN,gBAAM,IAAI,MAAM,qCAAqCA,QAAO,GAAG;AAAA,QACjE;AAAA,MACF,GAAG;AACH,UAAI,OAAO,OAAO,OAAOA;AACvB,cAAM,IAAI,MAAM,sCAAsCA,QAAO,GAAG;AAElE,aAAO,EAAE,GAAG,SAAS,SAAAA,SAAQ;AAAA,IAC/B;AAAA,IAEA,MAAM,OAAO,EAAE,YAAY,QAAQ,GAAG;AACpC,YAAM,EAAE,UAAU,IAAI;AACtB,YAAM,EAAE,SAAAA,SAAQ,IAAI;AAEpB,YAAM,SAAS,MAAM,cAAcA,QAAO;AAE1C,YAAM,EAAE,SAAS,iBAAiB,IAAI;AACtC,YAAM,kBAAkB,iBAAiB;AACzC,YAAM,oBAAoB,iBAAiB;AAC3C,YAAM,qBAAqB,iBAAiB;AAC5C,YAAM,UAAU,UAAU;AAE1B,UAAI,WAAW,IAAI,KAAK,OAAO,IAAI,oBAAI,KAAK,GAAG;AAC7C,cAAM,IAAI,MAAM,sBAAsB,OAAO,GAAG;AAAA,MAClD;AAEA,YAAM,UAAU,WAAW;AAE3B,cAAQ,QAAQ,MAAM;AAAA,QACpB,KAAK,QAAQ;AACX,gBAAM,OAAO,QAAQ;AACrB,gBAAM,iBAAiB,OAAO,IAAI;AAElC,gBAAM,SAAS,kBAAkB,WAAW,MAAM;AAClD,cAAI,CAAC;AACH,kBAAM,IAAI;AAAA,cACR;AAAA,YACF;AAEF,gBAAM,UAAU,UAAM,sCAAsB,QAAQ,EAAE,KAAK,CAAC;AAE5D,gBAAM,mBAAe,6BAAe;AAAA,YAClC,KAAK;AAAA,YACL,WAAW;AAAA,YACX,MAAM,QAAQ;AAAA,UAChB,CAAC;AAED,gBAAM,QAAQ,aAAa;AAAA,YACzB,CAAC,YACC,6BAAe,IAAI,SAAS,iBAAiB,SAC7C,6BAAe,IAAI,KAAK,MAAM,MAAM,SACpC,6BAAe,IAAI,KAAK,IAAI,kBAAkB,KAC9C,IAAI,KAAK,MAAM,SAAS,MAAM;AAAA,UAClC;AAEA,cAAI,CAAC;AACH,kBAAM,IAAI;AAAA,cACR;AAAA,cACA;AAAA,gBACE;AAAA,gBACA,QAAQ;AAAA,gBACR,UAAU;AAAA,gBACV,WAAW;AAAA,cACb;AAAA,YACF;AAEF,gBAAM,aAAa,OAAO,IAAI;AAE9B,iBAAO,UAAU,OAAO;AAAA,QAC1B;AAAA,QAEA,KAAK,iBAAiB;AACpB,cAAI,CAAC,eAAe;AAClB,kBAAM,IAAI;AAAA,cACR;AAAA,YAEF;AAAA,UACF;AAEA,gBAAM,EAAE,MAAM,IAAI,OAAO,YAAY,aAAa,OAAO,UAAU,IACjE;AAWF,gBAAM,IAAI,KAAK,UAAU,MAAM,GAAG,EAAE,CAAC;AACrC,gBAAM,IAAI,KAAK,UAAU,MAAM,IAAI,GAAG,CAAC;AACvC,gBAAM,IAAI,SAAS,UAAU,MAAM,KAAK,GAAG,GAAG,EAAE;AAGhD,cAAI,KAAC,6BAAe,IAAe,kBAAkB;AACnD,kBAAM,IAAI;AAAA,cACR;AAAA,cACA,EAAE,UAAU,oBAAoB,QAAQ,GAAG;AAAA,YAC7C;AAEF,cAAI,UAAU;AACZ,kBAAM,IAAI;AAAA,cACR;AAAA,cACA,EAAE,UAAU,iBAAiB,QAAQ,MAAM;AAAA,YAC7C;AAGF,gBAAM,gBAAgB,OAAO,WAAW;AACxC,cACE,gBAAgB,KAChB,gBAAgB,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,GAC5C;AACA,kBAAM,IAAI;AAAA,cACR,gDAAgD,WAAW;AAAA,YAC7D;AAAA,UACF;AAEA,gBAAM,oBAAgB,wBAAU,SAA0B;AAC1D,gBAAM,iBAAiB,OAAO,aAAa;AAC3C,gBAAM,aAAa,OAAO,aAAa;AAEvC,gBAAM,OAAO,UAAM,gCAAgB,QAAQ;AAAA,YACzC,SAAS;AAAA,YACT,OAAO,OAAO;AAAA,YACd,IAAI;AAAA,YACJ,UAAM,iCAAmB;AAAA,cACvB,KAAc;AAAA,cACd,cAAc;AAAA,cACd,MAAM;AAAA,gBACJ;AAAA,gBACA;AAAA,gBACA,OAAO,KAAK;AAAA,gBACZ,OAAO,UAAU;AAAA,gBACjB,OAAO,WAAW;AAAA,gBAClB;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,cACF;AAAA,YACF,CAAC;AAAA,UACH,CAAU;AAEV,cAAI,qBAAqB;AACvB,kBAAM,UAAU,UAAM,sCAAsB,QAAQ;AAAA,cAClD;AAAA,YACF,CAAC;AAED,mBAAO,UAAU,OAAO;AAAA,UAC1B;AAEA,iBAAO;AAAA,YACL,QAAQ;AAAA,YACR,QAAQ;AAAA,YACR,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,YAClC,WAAW;AAAA,UACb;AAAA,QACF;AAAA,QAEA;AACE,gBAAM,IAAI;AAAA,YACR,gCAAiC,QAA6B,IAAI;AAAA,UACpE;AAAA,MACJ;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAgDA,SAAS,gBAAgB,MAA8C;AACrE,SAAO,eAAe,KAAK,YAAY,CAAC;AAC1C;AAGA,eAAe,iBACb,OACA,MACe;AACf,QAAM,OAAO,MAAM,MAAM,IAAI,gBAAgB,IAAI,CAAC;AAClD,MAAI,SAAS,KAAM,OAAM,IAAI,MAAM,yCAAyC;AAC9E;AAGA,eAAe,aACb,OACA,MACe;AACf,QAAM,MAAM,IAAI,gBAAgB,IAAI,GAAG,KAAK,IAAI,CAAC;AACnD;AAGA,SAAS,UAAU,SAA6B;AAC9C,QAAM,EAAE,QAAQ,gBAAgB,IAAI;AACpC,MAAI,WAAW,WAAW;AACxB,UAAM,IAAI,MAAM,yBAAyB,eAAe,EAAE;AAAA,EAC5D;AACA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC,WAAW;AAAA,EACb;AACF;AAOA,SAAS,kBAAkB,QAAiD;AAC1E,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,QAAQ,2CAA2C,KAAK,MAAM;AACpE,SAAO,QAAS,MAAM,CAAC,IAAgB;AACzC;AAGA,IAAM,gBAAN,cAA4B,MAAM;AAAA,EAGhC,YAAY,QAAgB,SAAiC;AAC3D;AAAA,MACE;AAAA,QACE;AAAA,QACA,GAAG,OAAO,QAAQ,OAAO,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,KAAK,CAAC,EAAE;AAAA,MAC7D,EAAE,KAAK,IAAI;AAAA,IACb;AARF,SAAkB,OAAO;AAAA,EASzB;AACF;;;AGvVO,SAAS,MACd,YAC6B;AAC7B,SAAO,CAAC,MAAM,OAAO,UAAU,CAAC;AAClC;AAAA,CAEO,CAAUC,WAAV;AAIE,EAAMA,OAAA,SAASC;AAAA,GAJP;","names":["charge","import_mppx","import_viem","chainId","decimals","charge","currency","decimals","chainId","zerog","charge"]}
1
+ {"version":3,"sources":["../../src/server/index.ts","../../src/server/Charge.ts","../../src/defaults.ts","../../src/Methods.ts","../../src/server/Methods.ts"],"sourcesContent":["export { charge } from \"./Charge.js\";\nexport { zerog } from \"./Methods.js\";\n","import type { Account, Address, Chain, Client, TransactionReceipt } from \"viem\";\nimport { Method, Store } from \"mppx\";\nimport {\n encodeFunctionData,\n erc20Abi,\n isAddressEqual,\n keccak256,\n parseEventLogs,\n} from \"viem\";\nimport { parseAccount } from \"viem/accounts\";\nimport {\n getTransactionReceipt,\n sendTransaction,\n waitForTransactionReceipt,\n} from \"viem/actions\";\n\nimport type { MaybePromise } from \"../types.js\";\nimport * as defaults from \"../defaults.js\";\nimport * as Methods from \"../Methods.js\";\n\n/**\n * Creates a 0G charge method intent for usage on the server.\n *\n * @example\n * ```ts\n * import { zerog } from \"@heraldprotocol/mpp/server\";\n *\n * const charge = zerog.charge({\n * recipient: \"0x...\",\n * currency: \"0x...\",\n * account: privateKeyToAccount(\"0x...\"),\n * });\n * ```\n */\nexport function charge(parameters: charge.Parameters = {}): Method.AnyServer {\n const {\n amount,\n currency = defaults.resolveCurrency(parameters),\n decimals = defaults.decimals,\n description,\n externalId,\n recipient,\n waitForConfirmation = true,\n } = parameters;\n const store = (parameters.store ??\n Store.memory()) as Store.Store<charge.StoreItemMap>;\n\n if (currency.toLowerCase() in defaults.erc3009Tokens && !parameters.account) {\n throw new Error(\n \"ERC-3009 requires an `account` parameter so the server can sign and broadcast \" +\n \"the transferWithAuthorization transaction.\"\n );\n }\n\n const serverAccount = parameters.account\n ? typeof parameters.account === \"string\"\n ? parseAccount(parameters.account)\n : parameters.account\n : undefined;\n\n const resolveClient = async (\n chainId?: number | undefined\n ): Promise<Client> => {\n if (parameters.getClient) return parameters.getClient({ chainId });\n const id = chainId ?? defaults.chainId.mainnet;\n const url = defaults.rpcUrl[id];\n if (!url) throw new Error(`No RPC URL configured for chainId ${id}.`);\n const { createClient, http } = await import(\"viem\");\n return createClient({ chain: { id } as Chain, transport: http(url) });\n };\n\n return Method.toServer(Methods.charge, {\n defaults: {\n amount,\n currency,\n decimals,\n description,\n externalId,\n recipient,\n } as never,\n\n async request({ request }) {\n const chainId = await (async () => {\n if (request.chainId) return request.chainId;\n if (parameters.testnet) return defaults.chainId.testnet;\n return (await resolveClient(undefined)).chain?.id;\n })();\n\n const client = await (async () => {\n try {\n return await resolveClient(chainId);\n } catch {\n throw new Error(`No client configured with chainId ${chainId}.`);\n }\n })();\n if (client.chain?.id !== chainId)\n throw new Error(`Client not configured with chainId ${chainId}.`);\n\n return { ...request, chainId };\n },\n\n async verify({ credential, request }) {\n const { challenge } = credential;\n const { chainId } = request;\n\n const client = await resolveClient(chainId);\n\n const { request: challengeRequest } = challenge;\n const challengeAmount = challengeRequest.amount as string;\n const challengeCurrency = challengeRequest.currency as Address;\n const challengeRecipient = challengeRequest.recipient as Address;\n const expires = challenge.expires;\n\n if (expires && new Date(expires) < new Date()) {\n throw new Error(`Payment expired at ${expires}.`);\n }\n\n const payload = credential.payload;\n\n switch (payload.type) {\n case \"hash\": {\n const hash = payload.hash as `0x${string}`;\n await assertHashUnused(store, hash);\n\n const sender = extractDidAddress(credential.source);\n if (!sender)\n throw new Error(\n \"Hash credential is missing a valid `source` DID — cannot verify sender.\"\n );\n\n const receipt = await getTransactionReceipt(client, { hash });\n\n const transferLogs = parseEventLogs({\n abi: erc20Abi,\n eventName: \"Transfer\",\n logs: receipt.logs,\n });\n\n const match = transferLogs.find(\n (log) =>\n isAddressEqual(log.address, challengeCurrency) &&\n isAddressEqual(log.args.from, sender) &&\n isAddressEqual(log.args.to, challengeRecipient) &&\n log.args.value.toString() === challengeAmount\n );\n\n if (!match)\n throw new MismatchError(\n \"Payment verification failed: no matching ERC-20 transfer found.\",\n {\n sender,\n amount: challengeAmount,\n currency: challengeCurrency,\n recipient: challengeRecipient,\n }\n );\n\n await markHashUsed(store, hash);\n\n return toReceipt(receipt);\n }\n\n case \"authorization\": {\n if (!serverAccount) {\n throw new Error(\n \"Received ERC-3009 authorization credential but no server `account` is configured. \" +\n \"Set `account` in charge parameters to broadcast transferWithAuthorization.\"\n );\n }\n\n const { from, to, value, validAfter, validBefore, nonce, signature } =\n payload as {\n from: string;\n to: string;\n value: string;\n validAfter: string;\n validBefore: string;\n nonce: string;\n signature: string;\n };\n\n // Split signature into v, r, s for the contract call\n const r = `0x${signature.slice(2, 66)}` as `0x${string}`;\n const s = `0x${signature.slice(66, 130)}` as `0x${string}`;\n const v = parseInt(signature.slice(130, 132), 16);\n\n // Validate authorization parameters match the challenge\n if (!isAddressEqual(to as Address, challengeRecipient))\n throw new MismatchError(\n \"Authorization recipient does not match challenge.\",\n { expected: challengeRecipient, actual: to }\n );\n\n if (value !== challengeAmount)\n throw new MismatchError(\n \"Authorization amount does not match challenge.\",\n { expected: challengeAmount, actual: value }\n );\n\n // Check expiry from the authorization itself\n const validBeforeTs = Number(validBefore);\n if (\n validBeforeTs > 0 &&\n validBeforeTs < Math.floor(Date.now() / 1000)\n ) {\n throw new Error(\n `ERC-3009 authorization expired (validBefore: ${validBefore}).`\n );\n }\n\n const signatureHash = keccak256(signature as `0x${string}`);\n await assertHashUnused(store, signatureHash);\n await markHashUsed(store, signatureHash);\n\n const hash = await sendTransaction(client, {\n account: serverAccount,\n chain: client.chain,\n to: challengeCurrency,\n data: encodeFunctionData({\n abi: defaults.erc3009Abi,\n functionName: \"transferWithAuthorization\",\n args: [\n from as Address,\n to as Address,\n BigInt(value),\n BigInt(validAfter),\n BigInt(validBefore),\n nonce as `0x${string}`,\n v,\n r as `0x${string}`,\n s as `0x${string}`,\n ],\n }),\n } as never);\n\n if (waitForConfirmation) {\n const receipt = await waitForTransactionReceipt(client, {\n hash,\n });\n\n return toReceipt(receipt);\n }\n\n return {\n method: \"zerog\" as const,\n status: \"success\" as const,\n timestamp: new Date().toISOString(),\n reference: hash,\n };\n }\n\n default:\n throw new Error(\n `Unsupported credential type \"${(payload as { type: string }).type}\".`\n );\n }\n },\n });\n}\n\nexport declare namespace charge {\n type StoreItemMap = {\n [key: `mppx:charge:${string}`]: number;\n };\n\n type Parameters = {\n /** Default payment amount (human-readable, e.g. \"1.50\"). */\n amount?: string | undefined;\n /** ERC-20 token contract address. */\n currency?: string | undefined;\n /** Token decimals. @default 6 */\n decimals?: number | undefined;\n /** Human-readable description. */\n description?: string | undefined;\n /** External identifier to echo back in receipt. */\n externalId?: string | undefined;\n /** Recipient address for payments. */\n recipient?: string | undefined;\n /** Testnet mode. */\n testnet?: boolean | undefined;\n /**\n * Whether to wait for the charge transaction to confirm on-chain.\n * @default true\n */\n waitForConfirmation?: boolean | undefined;\n /** Function that returns a viem Client for the given chain ID. */\n getClient?:\n | ((parameters: { chainId?: number | undefined }) => MaybePromise<Client>)\n | undefined;\n /**\n * Server account used to broadcast `transferWithAuthorization` transactions.\n * Required when accepting `authorization` payloads. The server pays gas\n * from this account.\n */\n account?: Account | Address | undefined;\n /**\n * Store for transaction hash replay protection.\n *\n * Use a shared store in multi-instance deployments so consumed hashes are\n * visible across all server instances.\n */\n store?: Store.Store | undefined;\n };\n}\n\n/** @internal */\nfunction getHashStoreKey(hash: `0x${string}`): `mppx:charge:${string}` {\n return `mppx:charge:${hash.toLowerCase()}`;\n}\n\n/** @internal */\nasync function assertHashUnused(\n store: Store.Store<charge.StoreItemMap>,\n hash: `0x${string}`\n): Promise<void> {\n const seen = await store.get(getHashStoreKey(hash));\n if (seen !== null) throw new Error(\"Transaction hash has already been used.\");\n}\n\n/** @internal */\nasync function markHashUsed(\n store: Store.Store<charge.StoreItemMap>,\n hash: `0x${string}`\n): Promise<void> {\n await store.put(getHashStoreKey(hash), Date.now());\n}\n\n/** @internal */\nfunction toReceipt(receipt: TransactionReceipt) {\n const { status, transactionHash } = receipt;\n if (status !== \"success\") {\n throw new Error(`Transaction reverted: ${transactionHash}`);\n }\n return {\n method: \"zerog\" as const,\n status: \"success\" as const,\n timestamp: new Date().toISOString(),\n reference: transactionHash,\n };\n}\n\n/**\n * Extracts an Ethereum address from a `did:pkh:eip155:<chainId>:<address>` DID.\n * Returns `undefined` if the source is missing or malformed.\n * @internal\n */\nfunction extractDidAddress(source: string | undefined): Address | undefined {\n if (!source) return undefined;\n const match = /^did:pkh:eip155:\\d+:(0x[0-9a-fA-F]{40})$/.exec(source);\n return match ? (match[1] as Address) : undefined;\n}\n\n/** @internal */\nclass MismatchError extends Error {\n override readonly name = \"MismatchError\";\n\n constructor(reason: string, details: Record<string, string>) {\n super(\n [\n reason,\n ...Object.entries(details).map(([k, v]) => ` - ${k}: ${v}`),\n ].join(\"\\n\")\n );\n }\n}\n","export const chainId = {\n mainnet: 16661,\n testnet: 16602,\n} as const;\n\nexport type ChainId = (typeof chainId)[keyof typeof chainId];\n\nconst USDC_E = \"0x1f3aa82227281ca364bfb3d253b0f1af1da6473e\";\n\n/** Chain ID → default currency. */\nexport const currency: Partial<Record<ChainId, string>> = {\n [chainId.mainnet]: USDC_E,\n};\n\n/** Default token decimals for USDC.e. */\nexport const decimals = 6;\n\n/** Default RPC URLs per chain. */\nexport const rpcUrl: Record<number, string> = {\n [chainId.mainnet]: \"https://evmrpc.0g.ai\",\n [chainId.testnet]: \"https://evmrpc-testnet.0g.ai\",\n};\n\n/** ERC-3009 ABI (`transferWithAuthorization`, `receiveWithAuthorization`). */\nexport const erc3009Abi = [\n {\n type: \"function\",\n name: \"transferWithAuthorization\",\n inputs: [\n { name: \"from\", type: \"address\" },\n { name: \"to\", type: \"address\" },\n { name: \"value\", type: \"uint256\" },\n { name: \"validAfter\", type: \"uint256\" },\n { name: \"validBefore\", type: \"uint256\" },\n { name: \"nonce\", type: \"bytes32\" },\n { name: \"v\", type: \"uint8\" },\n { name: \"r\", type: \"bytes32\" },\n { name: \"s\", type: \"bytes32\" },\n ],\n outputs: [],\n stateMutability: \"nonpayable\",\n },\n {\n type: \"function\",\n name: \"receiveWithAuthorization\",\n inputs: [\n { name: \"from\", type: \"address\" },\n { name: \"to\", type: \"address\" },\n { name: \"value\", type: \"uint256\" },\n { name: \"validAfter\", type: \"uint256\" },\n { name: \"validBefore\", type: \"uint256\" },\n { name: \"nonce\", type: \"bytes32\" },\n { name: \"v\", type: \"uint8\" },\n { name: \"r\", type: \"bytes32\" },\n { name: \"s\", type: \"bytes32\" },\n ],\n outputs: [],\n stateMutability: \"nonpayable\",\n },\n {\n type: \"function\",\n name: \"name\",\n inputs: [],\n outputs: [{ name: \"\", type: \"string\" }],\n stateMutability: \"view\",\n },\n {\n type: \"function\",\n name: \"version\",\n inputs: [],\n outputs: [{ name: \"\", type: \"string\" }],\n stateMutability: \"view\",\n },\n] as const;\n\n/**\n * Known tokens that support ERC-3009 (TransferWithAuthorization).\n * Keyed by lowercase address for case-insensitive lookup.\n */\nexport const erc3009Tokens: Record<string, { name: string; version: string }> =\n {\n [USDC_E.toLowerCase()]: {\n name: \"Bridged USDC\",\n version: \"2\",\n },\n };\n\n/** Resolves the default currency for a given chain. */\nexport function resolveCurrency(parameters: {\n chainId?: number | undefined;\n testnet?: boolean | undefined;\n}): string {\n const id =\n parameters.chainId ??\n (parameters.testnet ? chainId.testnet : chainId.mainnet);\n const resolved = currency[id as ChainId];\n if (!resolved)\n throw new Error(`No default currency configured for chainId ${id}.`);\n return resolved;\n}\n","import { Method, z } from \"mppx\";\nimport { parseUnits } from \"viem\";\n\n/**\n * 0G charge intent for one-time ERC-20 token transfers.\n */\nexport const charge = Method.from({\n name: \"zerog\",\n intent: \"charge\",\n schema: {\n credential: {\n payload: z.discriminatedUnion(\"type\", [\n z.object({ hash: z.hash(), type: z.literal(\"hash\") }),\n z.object({\n type: z.literal(\"authorization\"),\n from: z.string(),\n to: z.string(),\n value: z.string(),\n validAfter: z.string(),\n validBefore: z.string(),\n nonce: z.string(),\n signature: z.string(),\n }),\n ]),\n },\n request: z.pipe(\n z.object({\n amount: z.amount(),\n chainId: z.optional(z.number()),\n currency: z.string(),\n decimals: z.number(),\n description: z.optional(z.string()),\n externalId: z.optional(z.string()),\n recipient: z.optional(z.string()),\n }),\n z.transform(({ amount, chainId, decimals, ...rest }) => ({\n ...rest,\n amount: parseUnits(amount, decimals).toString(),\n ...(chainId !== undefined ? { methodDetails: { chainId } } : {}),\n }))\n ),\n },\n});\n","import type { Method } from \"mppx\";\n\nimport { charge as charge_ } from \"./Charge.js\";\n\n/**\n * Creates a 0G `charge` server method.\n *\n * @example\n * ```ts\n * import { Mppx } from \"mppx/server\";\n * import { zerog } from \"@heraldprotocol/mpp/server\";\n *\n * const mppx = Mppx.create({\n * methods: [zerog({ recipient: \"0x...\", currency: \"0x...\" })],\n * });\n * ```\n */\nexport function zerog(\n parameters?: zerog.Parameters\n): readonly [Method.AnyServer] {\n return [zerog.charge(parameters)] as const;\n}\n\nexport namespace zerog {\n export type Parameters = charge_.Parameters;\n\n /** Creates a 0G `charge` method for one-time ERC-20 token transfers. */\n export const charge = charge_;\n}\n"],"mappings":"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","names":["charge","import_mppx","import_viem","chainId","decimals","charge","currency","decimals","chainId","zerog","charge"]}
@@ -20,7 +20,11 @@ import {
20
20
  parseEventLogs
21
21
  } from "viem";
22
22
  import { parseAccount } from "viem/accounts";
23
- import { getTransactionReceipt, sendTransaction } from "viem/actions";
23
+ import {
24
+ getTransactionReceipt,
25
+ sendTransaction,
26
+ waitForTransactionReceipt
27
+ } from "viem/actions";
24
28
  function charge2(parameters = {}) {
25
29
  const {
26
30
  amount,
@@ -166,7 +170,7 @@ function charge2(parameters = {}) {
166
170
  })
167
171
  });
168
172
  if (waitForConfirmation) {
169
- const receipt = await getTransactionReceipt(client, {
173
+ const receipt = await waitForTransactionReceipt(client, {
170
174
  hash
171
175
  });
172
176
  return toReceipt(receipt);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/Charge.ts","../../src/server/Methods.ts"],"sourcesContent":["import type { Account, Address, Chain, Client, TransactionReceipt } from \"viem\";\nimport { Method, Store } from \"mppx\";\nimport {\n encodeFunctionData,\n erc20Abi,\n isAddressEqual,\n keccak256,\n parseEventLogs,\n} from \"viem\";\nimport { parseAccount } from \"viem/accounts\";\nimport { getTransactionReceipt, sendTransaction } from \"viem/actions\";\n\nimport type { MaybePromise } from \"../types.js\";\nimport * as defaults from \"../defaults.js\";\nimport * as Methods from \"../Methods.js\";\n\n/**\n * Creates a 0G charge method intent for usage on the server.\n *\n * @example\n * ```ts\n * import { zerog } from \"@heraldprotocol/mpp/server\";\n *\n * const charge = zerog.charge({\n * recipient: \"0x...\",\n * currency: \"0x...\",\n * account: privateKeyToAccount(\"0x...\"),\n * });\n * ```\n */\nexport function charge(parameters: charge.Parameters = {}): Method.AnyServer {\n const {\n amount,\n currency = defaults.resolveCurrency(parameters),\n decimals = defaults.decimals,\n description,\n externalId,\n recipient,\n waitForConfirmation = true,\n } = parameters;\n const store = (parameters.store ??\n Store.memory()) as Store.Store<charge.StoreItemMap>;\n\n if (currency.toLowerCase() in defaults.erc3009Tokens && !parameters.account) {\n throw new Error(\n \"ERC-3009 requires an `account` parameter so the server can sign and broadcast \" +\n \"the transferWithAuthorization transaction.\"\n );\n }\n\n const serverAccount = parameters.account\n ? typeof parameters.account === \"string\"\n ? parseAccount(parameters.account)\n : parameters.account\n : undefined;\n\n const resolveClient = async (\n chainId?: number | undefined\n ): Promise<Client> => {\n if (parameters.getClient) return parameters.getClient({ chainId });\n const id = chainId ?? defaults.chainId.mainnet;\n const url = defaults.rpcUrl[id];\n if (!url) throw new Error(`No RPC URL configured for chainId ${id}.`);\n const { createClient, http } = await import(\"viem\");\n return createClient({ chain: { id } as Chain, transport: http(url) });\n };\n\n return Method.toServer(Methods.charge, {\n defaults: {\n amount,\n currency,\n decimals,\n description,\n externalId,\n recipient,\n } as never,\n\n async request({ request }) {\n const chainId = await (async () => {\n if (request.chainId) return request.chainId;\n if (parameters.testnet) return defaults.chainId.testnet;\n return (await resolveClient(undefined)).chain?.id;\n })();\n\n const client = await (async () => {\n try {\n return await resolveClient(chainId);\n } catch {\n throw new Error(`No client configured with chainId ${chainId}.`);\n }\n })();\n if (client.chain?.id !== chainId)\n throw new Error(`Client not configured with chainId ${chainId}.`);\n\n return { ...request, chainId };\n },\n\n async verify({ credential, request }) {\n const { challenge } = credential;\n const { chainId } = request;\n\n const client = await resolveClient(chainId);\n\n const { request: challengeRequest } = challenge;\n const challengeAmount = challengeRequest.amount as string;\n const challengeCurrency = challengeRequest.currency as Address;\n const challengeRecipient = challengeRequest.recipient as Address;\n const expires = challenge.expires;\n\n if (expires && new Date(expires) < new Date()) {\n throw new Error(`Payment expired at ${expires}.`);\n }\n\n const payload = credential.payload;\n\n switch (payload.type) {\n case \"hash\": {\n const hash = payload.hash as `0x${string}`;\n await assertHashUnused(store, hash);\n\n const sender = extractDidAddress(credential.source);\n if (!sender)\n throw new Error(\n \"Hash credential is missing a valid `source` DID — cannot verify sender.\"\n );\n\n const receipt = await getTransactionReceipt(client, { hash });\n\n const transferLogs = parseEventLogs({\n abi: erc20Abi,\n eventName: \"Transfer\",\n logs: receipt.logs,\n });\n\n const match = transferLogs.find(\n (log) =>\n isAddressEqual(log.address, challengeCurrency) &&\n isAddressEqual(log.args.from, sender) &&\n isAddressEqual(log.args.to, challengeRecipient) &&\n log.args.value.toString() === challengeAmount\n );\n\n if (!match)\n throw new MismatchError(\n \"Payment verification failed: no matching ERC-20 transfer found.\",\n {\n sender,\n amount: challengeAmount,\n currency: challengeCurrency,\n recipient: challengeRecipient,\n }\n );\n\n await markHashUsed(store, hash);\n\n return toReceipt(receipt);\n }\n\n case \"authorization\": {\n if (!serverAccount) {\n throw new Error(\n \"Received ERC-3009 authorization credential but no server `account` is configured. \" +\n \"Set `account` in charge parameters to broadcast transferWithAuthorization.\"\n );\n }\n\n const { from, to, value, validAfter, validBefore, nonce, signature } =\n payload as {\n from: string;\n to: string;\n value: string;\n validAfter: string;\n validBefore: string;\n nonce: string;\n signature: string;\n };\n\n // Split signature into v, r, s for the contract call\n const r = `0x${signature.slice(2, 66)}` as `0x${string}`;\n const s = `0x${signature.slice(66, 130)}` as `0x${string}`;\n const v = parseInt(signature.slice(130, 132), 16);\n\n // Validate authorization parameters match the challenge\n if (!isAddressEqual(to as Address, challengeRecipient))\n throw new MismatchError(\n \"Authorization recipient does not match challenge.\",\n { expected: challengeRecipient, actual: to }\n );\n\n if (value !== challengeAmount)\n throw new MismatchError(\n \"Authorization amount does not match challenge.\",\n { expected: challengeAmount, actual: value }\n );\n\n // Check expiry from the authorization itself\n const validBeforeTs = Number(validBefore);\n if (\n validBeforeTs > 0 &&\n validBeforeTs < Math.floor(Date.now() / 1000)\n ) {\n throw new Error(\n `ERC-3009 authorization expired (validBefore: ${validBefore}).`\n );\n }\n\n const signatureHash = keccak256(signature as `0x${string}`);\n await assertHashUnused(store, signatureHash);\n await markHashUsed(store, signatureHash);\n\n const hash = await sendTransaction(client, {\n account: serverAccount,\n chain: client.chain,\n to: challengeCurrency,\n data: encodeFunctionData({\n abi: defaults.erc3009Abi,\n functionName: \"transferWithAuthorization\",\n args: [\n from as Address,\n to as Address,\n BigInt(value),\n BigInt(validAfter),\n BigInt(validBefore),\n nonce as `0x${string}`,\n v,\n r as `0x${string}`,\n s as `0x${string}`,\n ],\n }),\n } as never);\n\n if (waitForConfirmation) {\n const receipt = await getTransactionReceipt(client, {\n hash,\n });\n\n return toReceipt(receipt);\n }\n\n return {\n method: \"zerog\" as const,\n status: \"success\" as const,\n timestamp: new Date().toISOString(),\n reference: hash,\n };\n }\n\n default:\n throw new Error(\n `Unsupported credential type \"${(payload as { type: string }).type}\".`\n );\n }\n },\n });\n}\n\nexport declare namespace charge {\n type StoreItemMap = {\n [key: `mppx:charge:${string}`]: number;\n };\n\n type Parameters = {\n /** Default payment amount (human-readable, e.g. \"1.50\"). */\n amount?: string | undefined;\n /** ERC-20 token contract address. */\n currency?: string | undefined;\n /** Token decimals. @default 6 */\n decimals?: number | undefined;\n /** Human-readable description. */\n description?: string | undefined;\n /** External identifier to echo back in receipt. */\n externalId?: string | undefined;\n /** Recipient address for payments. */\n recipient?: string | undefined;\n /** Testnet mode. */\n testnet?: boolean | undefined;\n /**\n * Whether to wait for the charge transaction to confirm on-chain.\n * @default true\n */\n waitForConfirmation?: boolean | undefined;\n /** Function that returns a viem Client for the given chain ID. */\n getClient?:\n | ((parameters: { chainId?: number | undefined }) => MaybePromise<Client>)\n | undefined;\n /**\n * Server account used to broadcast `transferWithAuthorization` transactions.\n * Required when accepting `authorization` payloads. The server pays gas\n * from this account.\n */\n account?: Account | Address | undefined;\n /**\n * Store for transaction hash replay protection.\n *\n * Use a shared store in multi-instance deployments so consumed hashes are\n * visible across all server instances.\n */\n store?: Store.Store | undefined;\n };\n}\n\n/** @internal */\nfunction getHashStoreKey(hash: `0x${string}`): `mppx:charge:${string}` {\n return `mppx:charge:${hash.toLowerCase()}`;\n}\n\n/** @internal */\nasync function assertHashUnused(\n store: Store.Store<charge.StoreItemMap>,\n hash: `0x${string}`\n): Promise<void> {\n const seen = await store.get(getHashStoreKey(hash));\n if (seen !== null) throw new Error(\"Transaction hash has already been used.\");\n}\n\n/** @internal */\nasync function markHashUsed(\n store: Store.Store<charge.StoreItemMap>,\n hash: `0x${string}`\n): Promise<void> {\n await store.put(getHashStoreKey(hash), Date.now());\n}\n\n/** @internal */\nfunction toReceipt(receipt: TransactionReceipt) {\n const { status, transactionHash } = receipt;\n if (status !== \"success\") {\n throw new Error(`Transaction reverted: ${transactionHash}`);\n }\n return {\n method: \"zerog\" as const,\n status: \"success\" as const,\n timestamp: new Date().toISOString(),\n reference: transactionHash,\n };\n}\n\n/**\n * Extracts an Ethereum address from a `did:pkh:eip155:<chainId>:<address>` DID.\n * Returns `undefined` if the source is missing or malformed.\n * @internal\n */\nfunction extractDidAddress(source: string | undefined): Address | undefined {\n if (!source) return undefined;\n const match = /^did:pkh:eip155:\\d+:(0x[0-9a-fA-F]{40})$/.exec(source);\n return match ? (match[1] as Address) : undefined;\n}\n\n/** @internal */\nclass MismatchError extends Error {\n override readonly name = \"MismatchError\";\n\n constructor(reason: string, details: Record<string, string>) {\n super(\n [\n reason,\n ...Object.entries(details).map(([k, v]) => ` - ${k}: ${v}`),\n ].join(\"\\n\")\n );\n }\n}\n","import type { Method } from \"mppx\";\n\nimport { charge as charge_ } from \"./Charge.js\";\n\n/**\n * Creates a 0G `charge` server method.\n *\n * @example\n * ```ts\n * import { Mppx } from \"mppx/server\";\n * import { zerog } from \"@heraldprotocol/mpp/server\";\n *\n * const mppx = Mppx.create({\n * methods: [zerog({ recipient: \"0x...\", currency: \"0x...\" })],\n * });\n * ```\n */\nexport function zerog(\n parameters?: zerog.Parameters\n): readonly [Method.AnyServer] {\n return [zerog.charge(parameters)] as const;\n}\n\nexport namespace zerog {\n export type Parameters = charge_.Parameters;\n\n /** Creates a 0G `charge` method for one-time ERC-20 token transfers. */\n export const charge = charge_;\n}\n"],"mappings":"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","names":["charge","decimals","chainId","zerog","charge"]}
1
+ {"version":3,"sources":["../../src/server/Charge.ts","../../src/server/Methods.ts"],"sourcesContent":["import type { Account, Address, Chain, Client, TransactionReceipt } from \"viem\";\nimport { Method, Store } from \"mppx\";\nimport {\n encodeFunctionData,\n erc20Abi,\n isAddressEqual,\n keccak256,\n parseEventLogs,\n} from \"viem\";\nimport { parseAccount } from \"viem/accounts\";\nimport {\n getTransactionReceipt,\n sendTransaction,\n waitForTransactionReceipt,\n} from \"viem/actions\";\n\nimport type { MaybePromise } from \"../types.js\";\nimport * as defaults from \"../defaults.js\";\nimport * as Methods from \"../Methods.js\";\n\n/**\n * Creates a 0G charge method intent for usage on the server.\n *\n * @example\n * ```ts\n * import { zerog } from \"@heraldprotocol/mpp/server\";\n *\n * const charge = zerog.charge({\n * recipient: \"0x...\",\n * currency: \"0x...\",\n * account: privateKeyToAccount(\"0x...\"),\n * });\n * ```\n */\nexport function charge(parameters: charge.Parameters = {}): Method.AnyServer {\n const {\n amount,\n currency = defaults.resolveCurrency(parameters),\n decimals = defaults.decimals,\n description,\n externalId,\n recipient,\n waitForConfirmation = true,\n } = parameters;\n const store = (parameters.store ??\n Store.memory()) as Store.Store<charge.StoreItemMap>;\n\n if (currency.toLowerCase() in defaults.erc3009Tokens && !parameters.account) {\n throw new Error(\n \"ERC-3009 requires an `account` parameter so the server can sign and broadcast \" +\n \"the transferWithAuthorization transaction.\"\n );\n }\n\n const serverAccount = parameters.account\n ? typeof parameters.account === \"string\"\n ? parseAccount(parameters.account)\n : parameters.account\n : undefined;\n\n const resolveClient = async (\n chainId?: number | undefined\n ): Promise<Client> => {\n if (parameters.getClient) return parameters.getClient({ chainId });\n const id = chainId ?? defaults.chainId.mainnet;\n const url = defaults.rpcUrl[id];\n if (!url) throw new Error(`No RPC URL configured for chainId ${id}.`);\n const { createClient, http } = await import(\"viem\");\n return createClient({ chain: { id } as Chain, transport: http(url) });\n };\n\n return Method.toServer(Methods.charge, {\n defaults: {\n amount,\n currency,\n decimals,\n description,\n externalId,\n recipient,\n } as never,\n\n async request({ request }) {\n const chainId = await (async () => {\n if (request.chainId) return request.chainId;\n if (parameters.testnet) return defaults.chainId.testnet;\n return (await resolveClient(undefined)).chain?.id;\n })();\n\n const client = await (async () => {\n try {\n return await resolveClient(chainId);\n } catch {\n throw new Error(`No client configured with chainId ${chainId}.`);\n }\n })();\n if (client.chain?.id !== chainId)\n throw new Error(`Client not configured with chainId ${chainId}.`);\n\n return { ...request, chainId };\n },\n\n async verify({ credential, request }) {\n const { challenge } = credential;\n const { chainId } = request;\n\n const client = await resolveClient(chainId);\n\n const { request: challengeRequest } = challenge;\n const challengeAmount = challengeRequest.amount as string;\n const challengeCurrency = challengeRequest.currency as Address;\n const challengeRecipient = challengeRequest.recipient as Address;\n const expires = challenge.expires;\n\n if (expires && new Date(expires) < new Date()) {\n throw new Error(`Payment expired at ${expires}.`);\n }\n\n const payload = credential.payload;\n\n switch (payload.type) {\n case \"hash\": {\n const hash = payload.hash as `0x${string}`;\n await assertHashUnused(store, hash);\n\n const sender = extractDidAddress(credential.source);\n if (!sender)\n throw new Error(\n \"Hash credential is missing a valid `source` DID — cannot verify sender.\"\n );\n\n const receipt = await getTransactionReceipt(client, { hash });\n\n const transferLogs = parseEventLogs({\n abi: erc20Abi,\n eventName: \"Transfer\",\n logs: receipt.logs,\n });\n\n const match = transferLogs.find(\n (log) =>\n isAddressEqual(log.address, challengeCurrency) &&\n isAddressEqual(log.args.from, sender) &&\n isAddressEqual(log.args.to, challengeRecipient) &&\n log.args.value.toString() === challengeAmount\n );\n\n if (!match)\n throw new MismatchError(\n \"Payment verification failed: no matching ERC-20 transfer found.\",\n {\n sender,\n amount: challengeAmount,\n currency: challengeCurrency,\n recipient: challengeRecipient,\n }\n );\n\n await markHashUsed(store, hash);\n\n return toReceipt(receipt);\n }\n\n case \"authorization\": {\n if (!serverAccount) {\n throw new Error(\n \"Received ERC-3009 authorization credential but no server `account` is configured. \" +\n \"Set `account` in charge parameters to broadcast transferWithAuthorization.\"\n );\n }\n\n const { from, to, value, validAfter, validBefore, nonce, signature } =\n payload as {\n from: string;\n to: string;\n value: string;\n validAfter: string;\n validBefore: string;\n nonce: string;\n signature: string;\n };\n\n // Split signature into v, r, s for the contract call\n const r = `0x${signature.slice(2, 66)}` as `0x${string}`;\n const s = `0x${signature.slice(66, 130)}` as `0x${string}`;\n const v = parseInt(signature.slice(130, 132), 16);\n\n // Validate authorization parameters match the challenge\n if (!isAddressEqual(to as Address, challengeRecipient))\n throw new MismatchError(\n \"Authorization recipient does not match challenge.\",\n { expected: challengeRecipient, actual: to }\n );\n\n if (value !== challengeAmount)\n throw new MismatchError(\n \"Authorization amount does not match challenge.\",\n { expected: challengeAmount, actual: value }\n );\n\n // Check expiry from the authorization itself\n const validBeforeTs = Number(validBefore);\n if (\n validBeforeTs > 0 &&\n validBeforeTs < Math.floor(Date.now() / 1000)\n ) {\n throw new Error(\n `ERC-3009 authorization expired (validBefore: ${validBefore}).`\n );\n }\n\n const signatureHash = keccak256(signature as `0x${string}`);\n await assertHashUnused(store, signatureHash);\n await markHashUsed(store, signatureHash);\n\n const hash = await sendTransaction(client, {\n account: serverAccount,\n chain: client.chain,\n to: challengeCurrency,\n data: encodeFunctionData({\n abi: defaults.erc3009Abi,\n functionName: \"transferWithAuthorization\",\n args: [\n from as Address,\n to as Address,\n BigInt(value),\n BigInt(validAfter),\n BigInt(validBefore),\n nonce as `0x${string}`,\n v,\n r as `0x${string}`,\n s as `0x${string}`,\n ],\n }),\n } as never);\n\n if (waitForConfirmation) {\n const receipt = await waitForTransactionReceipt(client, {\n hash,\n });\n\n return toReceipt(receipt);\n }\n\n return {\n method: \"zerog\" as const,\n status: \"success\" as const,\n timestamp: new Date().toISOString(),\n reference: hash,\n };\n }\n\n default:\n throw new Error(\n `Unsupported credential type \"${(payload as { type: string }).type}\".`\n );\n }\n },\n });\n}\n\nexport declare namespace charge {\n type StoreItemMap = {\n [key: `mppx:charge:${string}`]: number;\n };\n\n type Parameters = {\n /** Default payment amount (human-readable, e.g. \"1.50\"). */\n amount?: string | undefined;\n /** ERC-20 token contract address. */\n currency?: string | undefined;\n /** Token decimals. @default 6 */\n decimals?: number | undefined;\n /** Human-readable description. */\n description?: string | undefined;\n /** External identifier to echo back in receipt. */\n externalId?: string | undefined;\n /** Recipient address for payments. */\n recipient?: string | undefined;\n /** Testnet mode. */\n testnet?: boolean | undefined;\n /**\n * Whether to wait for the charge transaction to confirm on-chain.\n * @default true\n */\n waitForConfirmation?: boolean | undefined;\n /** Function that returns a viem Client for the given chain ID. */\n getClient?:\n | ((parameters: { chainId?: number | undefined }) => MaybePromise<Client>)\n | undefined;\n /**\n * Server account used to broadcast `transferWithAuthorization` transactions.\n * Required when accepting `authorization` payloads. The server pays gas\n * from this account.\n */\n account?: Account | Address | undefined;\n /**\n * Store for transaction hash replay protection.\n *\n * Use a shared store in multi-instance deployments so consumed hashes are\n * visible across all server instances.\n */\n store?: Store.Store | undefined;\n };\n}\n\n/** @internal */\nfunction getHashStoreKey(hash: `0x${string}`): `mppx:charge:${string}` {\n return `mppx:charge:${hash.toLowerCase()}`;\n}\n\n/** @internal */\nasync function assertHashUnused(\n store: Store.Store<charge.StoreItemMap>,\n hash: `0x${string}`\n): Promise<void> {\n const seen = await store.get(getHashStoreKey(hash));\n if (seen !== null) throw new Error(\"Transaction hash has already been used.\");\n}\n\n/** @internal */\nasync function markHashUsed(\n store: Store.Store<charge.StoreItemMap>,\n hash: `0x${string}`\n): Promise<void> {\n await store.put(getHashStoreKey(hash), Date.now());\n}\n\n/** @internal */\nfunction toReceipt(receipt: TransactionReceipt) {\n const { status, transactionHash } = receipt;\n if (status !== \"success\") {\n throw new Error(`Transaction reverted: ${transactionHash}`);\n }\n return {\n method: \"zerog\" as const,\n status: \"success\" as const,\n timestamp: new Date().toISOString(),\n reference: transactionHash,\n };\n}\n\n/**\n * Extracts an Ethereum address from a `did:pkh:eip155:<chainId>:<address>` DID.\n * Returns `undefined` if the source is missing or malformed.\n * @internal\n */\nfunction extractDidAddress(source: string | undefined): Address | undefined {\n if (!source) return undefined;\n const match = /^did:pkh:eip155:\\d+:(0x[0-9a-fA-F]{40})$/.exec(source);\n return match ? (match[1] as Address) : undefined;\n}\n\n/** @internal */\nclass MismatchError extends Error {\n override readonly name = \"MismatchError\";\n\n constructor(reason: string, details: Record<string, string>) {\n super(\n [\n reason,\n ...Object.entries(details).map(([k, v]) => ` - ${k}: ${v}`),\n ].join(\"\\n\")\n );\n }\n}\n","import type { Method } from \"mppx\";\n\nimport { charge as charge_ } from \"./Charge.js\";\n\n/**\n * Creates a 0G `charge` server method.\n *\n * @example\n * ```ts\n * import { Mppx } from \"mppx/server\";\n * import { zerog } from \"@heraldprotocol/mpp/server\";\n *\n * const mppx = Mppx.create({\n * methods: [zerog({ recipient: \"0x...\", currency: \"0x...\" })],\n * });\n * ```\n */\nexport function zerog(\n parameters?: zerog.Parameters\n): readonly [Method.AnyServer] {\n return [zerog.charge(parameters)] as const;\n}\n\nexport namespace zerog {\n export type Parameters = charge_.Parameters;\n\n /** Creates a 0G `charge` method for one-time ERC-20 token transfers. */\n export const charge = charge_;\n}\n"],"mappings":"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","names":["charge","decimals","chainId","zerog","charge"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@heraldprotocol/mpp",
4
- "version": "0.0.5",
4
+ "version": "0.0.6",
5
5
  "description": "0G Chain payment method for the Machine Payments Protocol",
6
6
  "repository": {
7
7
  "type": "git",