@hbar-kit/payments 0.1.2 → 0.2.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/README.md +41 -0
- package/dist/index.cjs +42 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +60 -1
- package/dist/index.d.ts +60 -1
- package/dist/index.js +38 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +9 -1
- package/src/types.ts +1 -1
- package/src/usdc.ts +97 -0
- package/src/wait.ts +4 -0
package/README.md
CHANGED
|
@@ -21,3 +21,44 @@ if (result.matched) {
|
|
|
21
21
|
Statuses: `confirmed | pending | underpaid | overpaid | duplicate | mismatch | expired | failed`.
|
|
22
22
|
A non-match is a result (with `reason`), not a thrown error. See the
|
|
23
23
|
[docs](https://github.com/devwhodevs/hbar-kit).
|
|
24
|
+
|
|
25
|
+
## USDC
|
|
26
|
+
|
|
27
|
+
USDC on Hedera is an HTS token, so `verifyUsdcPayment` / `waitForUsdcPayment` are thin wrappers over
|
|
28
|
+
`verifyHtsPayment` / `waitForHtsPayment` that fill in the **verified USDC token id** for the network
|
|
29
|
+
and parse amounts at **6 decimals**.
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { verifyUsdcPayment } from "@hbar-kit/payments"
|
|
33
|
+
|
|
34
|
+
const result = await verifyUsdcPayment({
|
|
35
|
+
network: "mainnet", // required — USDC token ids are network-specific (no default)
|
|
36
|
+
receiver: "0.0.12345",
|
|
37
|
+
amount: "25.00", // decimal string, 6 decimals; ">6 dp" throws InvalidAmountError
|
|
38
|
+
memo: "invoice_123",
|
|
39
|
+
after: new Date(Date.now() - 30 * 60 * 1000),
|
|
40
|
+
})
|
|
41
|
+
// result.asset === { tokenId: "0.0.456858", decimals: 6, symbol: "USDC" }
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- **When to use the USDC helper vs `verifyHtsPayment`:** use `verifyUsdcPayment` when you're
|
|
45
|
+
accepting USDC and want the token id + 6-decimal parsing handled for you; use `verifyHtsPayment`
|
|
46
|
+
for any other token or when you need full control over `tokenId`/`decimals`.
|
|
47
|
+
- **Verified token ids:** mainnet `0.0.456858`, testnet `0.0.429274` (verified against the Mirror
|
|
48
|
+
Node and Circle's official docs); `previewnet` throws `UnsupportedAssetError`. Also exported as
|
|
49
|
+
`USDC_TOKEN_IDS` / `getUsdcTokenId(network)`.
|
|
50
|
+
- **Precision:** amounts are decimal strings, never floats; USDC is always 6 decimals.
|
|
51
|
+
- **Custom / mock token:** pass an explicit `tokenId` (still parsed at 6 decimals) for dev/testnet:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
await verifyUsdcPayment({
|
|
55
|
+
network: "testnet",
|
|
56
|
+
tokenId: process.env.TESTNET_USDC_TOKEN_ID!,
|
|
57
|
+
receiver: "0.0.12345",
|
|
58
|
+
amount: "10.00",
|
|
59
|
+
memo: "test_invoice_1",
|
|
60
|
+
})
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Use `isUsdcPaymentResult(result)` to detect USDC results. Full guide:
|
|
64
|
+
[Verify a USDC payment](https://devwhodevs.github.io/hbar-kit/guide/verify-usdc-payment).
|
package/dist/index.cjs
CHANGED
|
@@ -148,6 +148,39 @@ async function verifyHtsPayment(p) {
|
|
|
148
148
|
}
|
|
149
149
|
return runVerify(p, { tokenId: p.tokenId, decimals }, core.parseUnits(p.amount, decimals), decimals);
|
|
150
150
|
}
|
|
151
|
+
var USDC_DECIMALS = 6;
|
|
152
|
+
var USDC_SYMBOL = "USDC";
|
|
153
|
+
var USDC_TOKEN_IDS = {
|
|
154
|
+
mainnet: "0.0.456858",
|
|
155
|
+
testnet: "0.0.429274",
|
|
156
|
+
previewnet: void 0
|
|
157
|
+
};
|
|
158
|
+
function getUsdcTokenId(network) {
|
|
159
|
+
const tokenId = USDC_TOKEN_IDS[network];
|
|
160
|
+
if (tokenId === void 0) {
|
|
161
|
+
throw new core.UnsupportedAssetError(
|
|
162
|
+
`USDC has no verified token id on Hedera ${network}. Pass an explicit \`tokenId\` to verify a custom token, or use "mainnet" or "testnet".`,
|
|
163
|
+
{ docsPath: "/guide/verify-usdc-payment" }
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
return tokenId;
|
|
167
|
+
}
|
|
168
|
+
async function verifyUsdcPayment(p) {
|
|
169
|
+
if (!p.network) {
|
|
170
|
+
throw new core.InvalidParamsError(
|
|
171
|
+
"`network` is required for USDC verification \u2014 USDC token ids and explorer URLs are network-specific",
|
|
172
|
+
{ docsPath: "/guide/verify-usdc-payment" }
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
const tokenId = p.tokenId ?? getUsdcTokenId(p.network);
|
|
176
|
+
core.assertEntityId(tokenId);
|
|
177
|
+
const result = await verifyHtsPayment({ ...p, tokenId, decimals: USDC_DECIMALS });
|
|
178
|
+
const asset = result.asset === "HBAR" ? result.asset : { ...result.asset, symbol: USDC_SYMBOL };
|
|
179
|
+
return { ...result, asset };
|
|
180
|
+
}
|
|
181
|
+
function isUsdcPaymentResult(result) {
|
|
182
|
+
return typeof result.asset === "object" && result.asset.symbol === USDC_SYMBOL;
|
|
183
|
+
}
|
|
151
184
|
|
|
152
185
|
// src/wait.ts
|
|
153
186
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
@@ -178,14 +211,23 @@ function waitForHbarPayment(p) {
|
|
|
178
211
|
function waitForHtsPayment(p) {
|
|
179
212
|
return poll(() => verifyHtsPayment(p), p);
|
|
180
213
|
}
|
|
214
|
+
function waitForUsdcPayment(p) {
|
|
215
|
+
return poll(() => verifyUsdcPayment(p), p);
|
|
216
|
+
}
|
|
181
217
|
|
|
218
|
+
exports.USDC_DECIMALS = USDC_DECIMALS;
|
|
219
|
+
exports.USDC_TOKEN_IDS = USDC_TOKEN_IDS;
|
|
182
220
|
exports.classifyAmount = classifyAmount;
|
|
221
|
+
exports.getUsdcTokenId = getUsdcTokenId;
|
|
183
222
|
exports.hashscanTxUrl = hashscanTxUrl;
|
|
223
|
+
exports.isUsdcPaymentResult = isUsdcPaymentResult;
|
|
184
224
|
exports.memoMatches = memoMatches;
|
|
185
225
|
exports.netToReceiver = netToReceiver;
|
|
186
226
|
exports.verifyHbarPayment = verifyHbarPayment;
|
|
187
227
|
exports.verifyHtsPayment = verifyHtsPayment;
|
|
228
|
+
exports.verifyUsdcPayment = verifyUsdcPayment;
|
|
188
229
|
exports.waitForHbarPayment = waitForHbarPayment;
|
|
189
230
|
exports.waitForHtsPayment = waitForHtsPayment;
|
|
231
|
+
exports.waitForUsdcPayment = waitForUsdcPayment;
|
|
190
232
|
//# sourceMappingURL=index.cjs.map
|
|
191
233
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/match.ts","../src/explorer.ts","../src/verify.ts","../src/wait.ts"],"names":["NETWORKS","txIdToMirror","createMirrorClient","InvalidParamsError","formatUnits","normalizeTransaction","parseHbar","parseUnits"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Transaction } from \"@hbar-kit/mirror\"\nimport type { MemoComparison, PaymentAsset } from \"./types.js\"\n\n/** Signed sum of ALL the receiver's legs (HBAR transfers or a specific token's transfers). */\nexport function netToReceiver(tx: Transaction, receiver: string, asset: PaymentAsset): bigint {\n if (asset === \"HBAR\") {\n return tx.transfers.filter((t) => t.account === receiver).reduce((s, t) => s + t.amount, 0n)\n }\n return tx.tokenTransfers\n .filter((t) => t.tokenId === asset.tokenId && t.account === receiver)\n .reduce((s, t) => s + t.amount, 0n)\n}\n\nexport function memoMatches(actual: string, expected: string, cmp: MemoComparison = {}): boolean {\n const mode = cmp.mode ?? \"exact\"\n if (mode === \"trim\") return actual.trim() === expected.trim()\n if (mode === \"caseInsensitive\") return actual.toLowerCase() === expected.toLowerCase()\n return actual === expected\n}\n\nexport type AmountClass = \"exact\" | \"underpaid\" | \"overpaid\"\nexport function classifyAmount(net: bigint, expected: bigint): AmountClass {\n if (net === expected) return \"exact\"\n return net < expected ? \"underpaid\" : \"overpaid\"\n}\n","import { NETWORKS, txIdToMirror, type HederaNetwork } from \"@hbar-kit/core\"\n\n/** HashScan transaction link: consensus timestamp in path, tx id as ?tid query param. */\nexport function hashscanTxUrl(\n network: HederaNetwork,\n consensusTimestamp: string,\n transactionId: string,\n): string {\n return `${NETWORKS[network].hashscan}/transaction/${consensusTimestamp}?tid=${txIdToMirror(transactionId)}`\n}\n","import {\n parseUnits,\n parseHbar,\n formatUnits,\n type HederaNetwork,\n type NetworkInput,\n InvalidParamsError,\n} from \"@hbar-kit/core\"\nimport {\n createMirrorClient,\n normalizeTransaction,\n type MirrorClient,\n type RawTransaction,\n type Transaction,\n} from \"@hbar-kit/mirror\"\nimport { netToReceiver, memoMatches, classifyAmount } from \"./match.js\"\nimport { hashscanTxUrl } from \"./explorer.js\"\nimport type { MemoComparison, PaymentAsset, PaymentMatch, PaymentResult } from \"./types.js\"\n\nexport interface VerifyBaseParams extends NetworkInput {\n client?: MirrorClient\n receiver: string\n amount: string\n memo?: string\n memoComparison?: MemoComparison\n comparison?: \"exact\" | \"atLeast\"\n after?: Date | string\n before?: Date | string\n}\nexport type VerifyHbarParams = VerifyBaseParams\nexport interface VerifyHtsParams extends VerifyBaseParams {\n tokenId: string\n decimals?: number\n}\n\nconst tokenDecimalsCache = new Map<string, number>()\n\nfunction resolveClient(p: VerifyBaseParams): { client: MirrorClient; network: HederaNetwork } {\n return {\n client: p.client ?? createMirrorClient(p),\n network: (p.network ?? \"mainnet\") as HederaNetwork,\n }\n}\n\nasync function runVerify(\n p: VerifyBaseParams,\n asset: PaymentAsset,\n expectedBase: bigint,\n decimals: number,\n): Promise<PaymentResult> {\n if (p.after && p.before && new Date(p.after) > new Date(p.before)) {\n throw new InvalidParamsError(\"`after` must be before `before`\")\n }\n const { client, network } = resolveClient(p)\n const tokenId = asset === \"HBAR\" ? undefined : asset.tokenId\n\n const candidates: PaymentMatch[] = []\n const collect = (items: Transaction[]) => {\n for (const tx of items) {\n if (tx.result !== \"SUCCESS\") continue\n const net = netToReceiver(tx, p.receiver, asset)\n if (net <= 0n) continue\n if (tokenId && !tx.tokenTransfers.some((t) => t.tokenId === tokenId)) continue\n const payer =\n tx.transfers.find((t) => t.amount < 0n)?.account ??\n tx.tokenTransfers.find((t) => t.amount < 0n)?.account\n const match: PaymentMatch = {\n transactionId: tx.transactionId,\n consensusTimestamp: tx.consensusTimestamp.raw,\n netBase: net,\n net: formatUnits(net, decimals),\n memo: tx.memo,\n transaction: tx,\n }\n if (payer !== undefined) match.payer = payer\n candidates.push(match)\n }\n }\n\n const findParams: Parameters<typeof client.transactions.find>[0] = {\n accountId: p.receiver,\n transactionType: \"cryptotransfer\",\n result: \"success\",\n order: \"desc\",\n }\n if (p.after !== undefined) findParams.after = p.after\n if (p.before !== undefined) findParams.before = p.before\n let page = await client.transactions.find(findParams)\n collect(page.items)\n while (page.next) {\n const body = (await client.transport.get(page.next)) as {\n transactions?: RawTransaction[]\n links?: { next: string | null }\n }\n collect((body.transactions ?? []).map(normalizeTransaction))\n page = { items: [], next: body.links?.next ?? null }\n }\n\n const memoFiltered = p.memo\n ? candidates.filter((c) => memoMatches(c.memo, p.memo!, p.memoComparison))\n : candidates\n\n const fail = (status: PaymentResult[\"status\"], reason: string): PaymentResult => ({\n matched: false,\n status,\n receiver: p.receiver,\n asset,\n matches: memoFiltered,\n reason,\n })\n if (candidates.length === 0)\n return fail(\"pending\", \"no matching transactions for receiver in window\")\n if (p.memo && memoFiltered.length === 0)\n return fail(\"mismatch\", \"no transaction matched the expected memo\")\n\n const wantAtLeast = p.comparison === \"atLeast\"\n const satisfying = memoFiltered.filter((c) =>\n wantAtLeast ? c.netBase >= expectedBase : c.netBase === expectedBase,\n )\n\n const resultFrom = (\n m: PaymentMatch,\n status: PaymentResult[\"status\"],\n matched: boolean,\n matches: PaymentMatch[],\n reason?: string,\n ): PaymentResult => {\n const result: PaymentResult = {\n matched,\n status,\n receiver: p.receiver,\n asset,\n transactionId: m.transactionId,\n amountBase: m.netBase,\n amount: m.net,\n memo: m.memo,\n consensusTimestamp: m.consensusTimestamp,\n explorerUrl: hashscanTxUrl(network, m.consensusTimestamp, m.transactionId),\n matches,\n }\n if (m.payer !== undefined) result.payer = m.payer\n if (reason !== undefined) result.reason = reason\n return result\n }\n\n if (satisfying.length === 0) {\n const best = memoFiltered[0]!\n const cls = classifyAmount(best.netBase, expectedBase)\n return resultFrom(\n best,\n cls === \"exact\" ? \"confirmed\" : cls,\n false,\n memoFiltered,\n `amount ${cls}`,\n )\n }\n if (satisfying.length > 1) {\n return resultFrom(\n satisfying[0]!,\n \"duplicate\",\n false,\n satisfying,\n `${satisfying.length} transactions satisfy this request`,\n )\n }\n return resultFrom(satisfying[0]!, \"confirmed\", true, satisfying)\n}\n\nexport async function verifyHbarPayment(p: VerifyHbarParams): Promise<PaymentResult> {\n return runVerify(p, \"HBAR\", parseHbar(p.amount), 8)\n}\n\nexport async function verifyHtsPayment(p: VerifyHtsParams): Promise<PaymentResult> {\n let decimals = p.decimals\n if (decimals === undefined) {\n const cached = tokenDecimalsCache.get(p.tokenId)\n if (cached !== undefined) decimals = cached\n else {\n const { client } = resolveClient(p)\n decimals = (await client.tokens.get(p.tokenId)).decimals\n tokenDecimalsCache.set(p.tokenId, decimals)\n }\n }\n return runVerify(p, { tokenId: p.tokenId, decimals }, parseUnits(p.amount, decimals), decimals)\n}\n","import {\n verifyHbarPayment,\n verifyHtsPayment,\n type VerifyHbarParams,\n type VerifyHtsParams,\n} from \"./verify.js\"\nimport type { PaymentResult } from \"./types.js\"\n\nexport interface WaitOptions {\n timeoutMs?: number\n pollIntervalMs?: number\n signal?: AbortSignal\n}\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))\n\nasync function poll(\n verify: () => Promise<PaymentResult>,\n opts: WaitOptions,\n): Promise<PaymentResult> {\n const timeoutMs = opts.timeoutMs ?? 10 * 60 * 1000\n const pollIntervalMs = opts.pollIntervalMs ?? 3000\n const deadline = Date.now() + timeoutMs\n let last: PaymentResult | undefined\n while (Date.now() < deadline) {\n if (opts.signal?.aborted) break\n last = await verify()\n if (last.matched) return last\n if (last.status === \"duplicate\" || last.status === \"overpaid\") return last\n await sleep(pollIntervalMs)\n }\n return last && last.status !== \"pending\"\n ? { ...last, status: \"expired\" }\n : {\n matched: false,\n status: \"expired\",\n receiver: last?.receiver ?? \"\",\n asset: last?.asset ?? \"HBAR\",\n matches: [],\n reason: \"timed out waiting for payment\",\n }\n}\n\nexport function waitForHbarPayment(p: VerifyHbarParams & WaitOptions): Promise<PaymentResult> {\n return poll(() => verifyHbarPayment(p), p)\n}\nexport function waitForHtsPayment(p: VerifyHtsParams & WaitOptions): Promise<PaymentResult> {\n return poll(() => verifyHtsPayment(p), p)\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/match.ts","../src/explorer.ts","../src/verify.ts","../src/usdc.ts","../src/wait.ts"],"names":["NETWORKS","txIdToMirror","createMirrorClient","InvalidParamsError","formatUnits","normalizeTransaction","parseHbar","parseUnits","UnsupportedAssetError","assertEntityId"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Transaction } from \"@hbar-kit/mirror\"\nimport type { MemoComparison, PaymentAsset } from \"./types.js\"\n\n/** Signed sum of ALL the receiver's legs (HBAR transfers or a specific token's transfers). */\nexport function netToReceiver(tx: Transaction, receiver: string, asset: PaymentAsset): bigint {\n if (asset === \"HBAR\") {\n return tx.transfers.filter((t) => t.account === receiver).reduce((s, t) => s + t.amount, 0n)\n }\n return tx.tokenTransfers\n .filter((t) => t.tokenId === asset.tokenId && t.account === receiver)\n .reduce((s, t) => s + t.amount, 0n)\n}\n\nexport function memoMatches(actual: string, expected: string, cmp: MemoComparison = {}): boolean {\n const mode = cmp.mode ?? \"exact\"\n if (mode === \"trim\") return actual.trim() === expected.trim()\n if (mode === \"caseInsensitive\") return actual.toLowerCase() === expected.toLowerCase()\n return actual === expected\n}\n\nexport type AmountClass = \"exact\" | \"underpaid\" | \"overpaid\"\nexport function classifyAmount(net: bigint, expected: bigint): AmountClass {\n if (net === expected) return \"exact\"\n return net < expected ? \"underpaid\" : \"overpaid\"\n}\n","import { NETWORKS, txIdToMirror, type HederaNetwork } from \"@hbar-kit/core\"\n\n/** HashScan transaction link: consensus timestamp in path, tx id as ?tid query param. */\nexport function hashscanTxUrl(\n network: HederaNetwork,\n consensusTimestamp: string,\n transactionId: string,\n): string {\n return `${NETWORKS[network].hashscan}/transaction/${consensusTimestamp}?tid=${txIdToMirror(transactionId)}`\n}\n","import {\n parseUnits,\n parseHbar,\n formatUnits,\n type HederaNetwork,\n type NetworkInput,\n InvalidParamsError,\n} from \"@hbar-kit/core\"\nimport {\n createMirrorClient,\n normalizeTransaction,\n type MirrorClient,\n type RawTransaction,\n type Transaction,\n} from \"@hbar-kit/mirror\"\nimport { netToReceiver, memoMatches, classifyAmount } from \"./match.js\"\nimport { hashscanTxUrl } from \"./explorer.js\"\nimport type { MemoComparison, PaymentAsset, PaymentMatch, PaymentResult } from \"./types.js\"\n\nexport interface VerifyBaseParams extends NetworkInput {\n client?: MirrorClient\n receiver: string\n amount: string\n memo?: string\n memoComparison?: MemoComparison\n comparison?: \"exact\" | \"atLeast\"\n after?: Date | string\n before?: Date | string\n}\nexport type VerifyHbarParams = VerifyBaseParams\nexport interface VerifyHtsParams extends VerifyBaseParams {\n tokenId: string\n decimals?: number\n}\n\nconst tokenDecimalsCache = new Map<string, number>()\n\nfunction resolveClient(p: VerifyBaseParams): { client: MirrorClient; network: HederaNetwork } {\n return {\n client: p.client ?? createMirrorClient(p),\n network: (p.network ?? \"mainnet\") as HederaNetwork,\n }\n}\n\nasync function runVerify(\n p: VerifyBaseParams,\n asset: PaymentAsset,\n expectedBase: bigint,\n decimals: number,\n): Promise<PaymentResult> {\n if (p.after && p.before && new Date(p.after) > new Date(p.before)) {\n throw new InvalidParamsError(\"`after` must be before `before`\")\n }\n const { client, network } = resolveClient(p)\n const tokenId = asset === \"HBAR\" ? undefined : asset.tokenId\n\n const candidates: PaymentMatch[] = []\n const collect = (items: Transaction[]) => {\n for (const tx of items) {\n if (tx.result !== \"SUCCESS\") continue\n const net = netToReceiver(tx, p.receiver, asset)\n if (net <= 0n) continue\n if (tokenId && !tx.tokenTransfers.some((t) => t.tokenId === tokenId)) continue\n const payer =\n tx.transfers.find((t) => t.amount < 0n)?.account ??\n tx.tokenTransfers.find((t) => t.amount < 0n)?.account\n const match: PaymentMatch = {\n transactionId: tx.transactionId,\n consensusTimestamp: tx.consensusTimestamp.raw,\n netBase: net,\n net: formatUnits(net, decimals),\n memo: tx.memo,\n transaction: tx,\n }\n if (payer !== undefined) match.payer = payer\n candidates.push(match)\n }\n }\n\n const findParams: Parameters<typeof client.transactions.find>[0] = {\n accountId: p.receiver,\n transactionType: \"cryptotransfer\",\n result: \"success\",\n order: \"desc\",\n }\n if (p.after !== undefined) findParams.after = p.after\n if (p.before !== undefined) findParams.before = p.before\n let page = await client.transactions.find(findParams)\n collect(page.items)\n while (page.next) {\n const body = (await client.transport.get(page.next)) as {\n transactions?: RawTransaction[]\n links?: { next: string | null }\n }\n collect((body.transactions ?? []).map(normalizeTransaction))\n page = { items: [], next: body.links?.next ?? null }\n }\n\n const memoFiltered = p.memo\n ? candidates.filter((c) => memoMatches(c.memo, p.memo!, p.memoComparison))\n : candidates\n\n const fail = (status: PaymentResult[\"status\"], reason: string): PaymentResult => ({\n matched: false,\n status,\n receiver: p.receiver,\n asset,\n matches: memoFiltered,\n reason,\n })\n if (candidates.length === 0)\n return fail(\"pending\", \"no matching transactions for receiver in window\")\n if (p.memo && memoFiltered.length === 0)\n return fail(\"mismatch\", \"no transaction matched the expected memo\")\n\n const wantAtLeast = p.comparison === \"atLeast\"\n const satisfying = memoFiltered.filter((c) =>\n wantAtLeast ? c.netBase >= expectedBase : c.netBase === expectedBase,\n )\n\n const resultFrom = (\n m: PaymentMatch,\n status: PaymentResult[\"status\"],\n matched: boolean,\n matches: PaymentMatch[],\n reason?: string,\n ): PaymentResult => {\n const result: PaymentResult = {\n matched,\n status,\n receiver: p.receiver,\n asset,\n transactionId: m.transactionId,\n amountBase: m.netBase,\n amount: m.net,\n memo: m.memo,\n consensusTimestamp: m.consensusTimestamp,\n explorerUrl: hashscanTxUrl(network, m.consensusTimestamp, m.transactionId),\n matches,\n }\n if (m.payer !== undefined) result.payer = m.payer\n if (reason !== undefined) result.reason = reason\n return result\n }\n\n if (satisfying.length === 0) {\n const best = memoFiltered[0]!\n const cls = classifyAmount(best.netBase, expectedBase)\n return resultFrom(\n best,\n cls === \"exact\" ? \"confirmed\" : cls,\n false,\n memoFiltered,\n `amount ${cls}`,\n )\n }\n if (satisfying.length > 1) {\n return resultFrom(\n satisfying[0]!,\n \"duplicate\",\n false,\n satisfying,\n `${satisfying.length} transactions satisfy this request`,\n )\n }\n return resultFrom(satisfying[0]!, \"confirmed\", true, satisfying)\n}\n\nexport async function verifyHbarPayment(p: VerifyHbarParams): Promise<PaymentResult> {\n return runVerify(p, \"HBAR\", parseHbar(p.amount), 8)\n}\n\nexport async function verifyHtsPayment(p: VerifyHtsParams): Promise<PaymentResult> {\n let decimals = p.decimals\n if (decimals === undefined) {\n const cached = tokenDecimalsCache.get(p.tokenId)\n if (cached !== undefined) decimals = cached\n else {\n const { client } = resolveClient(p)\n decimals = (await client.tokens.get(p.tokenId)).decimals\n tokenDecimalsCache.set(p.tokenId, decimals)\n }\n }\n return runVerify(p, { tokenId: p.tokenId, decimals }, parseUnits(p.amount, decimals), decimals)\n}\n","import {\n assertEntityId,\n InvalidParamsError,\n UnsupportedAssetError,\n type HederaNetwork,\n} from \"@hbar-kit/core\"\nimport { verifyHtsPayment, type VerifyHtsParams } from \"./verify.js\"\nimport type { PaymentAsset, PaymentResult } from \"./types.js\"\n\n/** USDC uses 6 decimals on every network Circle issues it on, including Hedera. */\nexport const USDC_DECIMALS = 6\nconst USDC_SYMBOL = \"USDC\"\n\n/**\n * Canonical Circle-issued USDC token ids on Hedera, per network.\n *\n * Verified two independent ways before hardcoding — do NOT change without re-verifying both:\n *\n * 1. Live Hedera Mirror Node token metadata (on-chain ground truth):\n * - mainnet `GET https://mainnet-public.mirrornode.hedera.com/api/v1/tokens/0.0.456858`\n * → { symbol: \"USDC\", name: \"USD Coin\", decimals: \"6\", type: \"FUNGIBLE_COMMON\",\n * treasury_account_id: \"0.0.439909\" }\n * - testnet `GET https://testnet.mirrornode.hedera.com/api/v1/tokens/0.0.429274`\n * → { symbol: \"USDC\", name: \"USD Coin\", decimals: \"6\", type: \"FUNGIBLE_COMMON\",\n * treasury_account_id: \"0.0.5176\" }\n *\n * 2. Circle's official \"USDC Contract Addresses\" documentation:\n * https://developers.circle.com/stablecoins/usdc-contract-addresses\n * (Hedera row links to hashscan.io/mainnet/token/0.0.456858; Hedera Testnet row links to\n * hashscan.io/testnet/token/0.0.429274). Circle publishes the native 0.0.x form, not an EVM\n * address.\n *\n * Previewnet: Circle does not issue USDC there, so there is no verified id. `getUsdcTokenId`\n * throws rather than silently falling back to a mainnet/testnet id.\n */\nexport const USDC_TOKEN_IDS = {\n mainnet: \"0.0.456858\",\n testnet: \"0.0.429274\",\n previewnet: undefined,\n} as const satisfies Record<HederaNetwork, string | undefined>\n\n/** Resolve the verified USDC token id for a network, or throw if none is known for it. */\nexport function getUsdcTokenId(network: HederaNetwork): string {\n const tokenId = USDC_TOKEN_IDS[network]\n if (tokenId === undefined) {\n throw new UnsupportedAssetError(\n `USDC has no verified token id on Hedera ${network}. ` +\n `Pass an explicit \\`tokenId\\` to verify a custom token, or use \"mainnet\" or \"testnet\".`,\n { docsPath: \"/guide/verify-usdc-payment\" },\n )\n }\n return tokenId\n}\n\n/**\n * Params for {@link verifyUsdcPayment}. Same as {@link VerifyHtsParams} minus `tokenId`/`decimals`\n * (USDC is always 6 decimals) and with `network` **required** — USDC token ids and HashScan URLs are\n * network-specific, so there is no implicit default network for the USDC helper.\n */\nexport interface VerifyUsdcParams extends Omit<VerifyHtsParams, \"tokenId\" | \"decimals\" | \"network\"> {\n network: HederaNetwork\n /**\n * Override the canonical USDC token id — useful for a dev/testnet mock token. The amount is still\n * parsed at 6 decimals. Production mainnet flows should omit this and use the verified token id.\n */\n tokenId?: string\n}\n\n/**\n * Verify a USDC payment on Hedera. A convenience wrapper over {@link verifyHtsPayment} that resolves\n * the canonical USDC token id for `network`, forces 6-decimal amount parsing, and tags the result\n * asset as USDC. Every other semantic — `confirmed`/`pending`/`underpaid`/`overpaid`/`duplicate`/\n * `mismatch`/`expired`/`failed`, memo/amount/time-window matching — is identical to HTS verification.\n */\nexport async function verifyUsdcPayment(p: VerifyUsdcParams): Promise<PaymentResult> {\n if (!p.network) {\n throw new InvalidParamsError(\n \"`network` is required for USDC verification — USDC token ids and explorer URLs are network-specific\",\n { docsPath: \"/guide/verify-usdc-payment\" },\n )\n }\n const tokenId = p.tokenId ?? getUsdcTokenId(p.network)\n assertEntityId(tokenId)\n const result = await verifyHtsPayment({ ...p, tokenId, decimals: USDC_DECIMALS })\n // verifyHtsPayment always yields a token asset (never \"HBAR\"); tag it so consumers can show USDC.\n const asset: PaymentAsset =\n result.asset === \"HBAR\" ? result.asset : { ...result.asset, symbol: USDC_SYMBOL }\n return { ...result, asset }\n}\n\n/**\n * True when a PaymentResult was produced by {@link verifyUsdcPayment} (its asset is tagged\n * `symbol: \"USDC\"`). Useful for narrowing/displaying results from a mixed payment pipeline.\n */\nexport function isUsdcPaymentResult(result: PaymentResult): boolean {\n return typeof result.asset === \"object\" && result.asset.symbol === USDC_SYMBOL\n}\n","import {\n verifyHbarPayment,\n verifyHtsPayment,\n type VerifyHbarParams,\n type VerifyHtsParams,\n} from \"./verify.js\"\nimport { verifyUsdcPayment, type VerifyUsdcParams } from \"./usdc.js\"\nimport type { PaymentResult } from \"./types.js\"\n\nexport interface WaitOptions {\n timeoutMs?: number\n pollIntervalMs?: number\n signal?: AbortSignal\n}\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))\n\nasync function poll(\n verify: () => Promise<PaymentResult>,\n opts: WaitOptions,\n): Promise<PaymentResult> {\n const timeoutMs = opts.timeoutMs ?? 10 * 60 * 1000\n const pollIntervalMs = opts.pollIntervalMs ?? 3000\n const deadline = Date.now() + timeoutMs\n let last: PaymentResult | undefined\n while (Date.now() < deadline) {\n if (opts.signal?.aborted) break\n last = await verify()\n if (last.matched) return last\n if (last.status === \"duplicate\" || last.status === \"overpaid\") return last\n await sleep(pollIntervalMs)\n }\n return last && last.status !== \"pending\"\n ? { ...last, status: \"expired\" }\n : {\n matched: false,\n status: \"expired\",\n receiver: last?.receiver ?? \"\",\n asset: last?.asset ?? \"HBAR\",\n matches: [],\n reason: \"timed out waiting for payment\",\n }\n}\n\nexport function waitForHbarPayment(p: VerifyHbarParams & WaitOptions): Promise<PaymentResult> {\n return poll(() => verifyHbarPayment(p), p)\n}\nexport function waitForHtsPayment(p: VerifyHtsParams & WaitOptions): Promise<PaymentResult> {\n return poll(() => verifyHtsPayment(p), p)\n}\nexport function waitForUsdcPayment(p: VerifyUsdcParams & WaitOptions): Promise<PaymentResult> {\n return poll(() => verifyUsdcPayment(p), p)\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -5,6 +5,7 @@ type PaymentStatus = "confirmed" | "pending" | "underpaid" | "overpaid" | "dupli
|
|
|
5
5
|
type PaymentAsset = "HBAR" | {
|
|
6
6
|
tokenId: string;
|
|
7
7
|
decimals: number;
|
|
8
|
+
symbol?: string;
|
|
8
9
|
};
|
|
9
10
|
interface PaymentMatch {
|
|
10
11
|
transactionId: string;
|
|
@@ -52,6 +53,63 @@ interface VerifyHtsParams extends VerifyBaseParams {
|
|
|
52
53
|
declare function verifyHbarPayment(p: VerifyHbarParams): Promise<PaymentResult>;
|
|
53
54
|
declare function verifyHtsPayment(p: VerifyHtsParams): Promise<PaymentResult>;
|
|
54
55
|
|
|
56
|
+
/** USDC uses 6 decimals on every network Circle issues it on, including Hedera. */
|
|
57
|
+
declare const USDC_DECIMALS = 6;
|
|
58
|
+
/**
|
|
59
|
+
* Canonical Circle-issued USDC token ids on Hedera, per network.
|
|
60
|
+
*
|
|
61
|
+
* Verified two independent ways before hardcoding — do NOT change without re-verifying both:
|
|
62
|
+
*
|
|
63
|
+
* 1. Live Hedera Mirror Node token metadata (on-chain ground truth):
|
|
64
|
+
* - mainnet `GET https://mainnet-public.mirrornode.hedera.com/api/v1/tokens/0.0.456858`
|
|
65
|
+
* → { symbol: "USDC", name: "USD Coin", decimals: "6", type: "FUNGIBLE_COMMON",
|
|
66
|
+
* treasury_account_id: "0.0.439909" }
|
|
67
|
+
* - testnet `GET https://testnet.mirrornode.hedera.com/api/v1/tokens/0.0.429274`
|
|
68
|
+
* → { symbol: "USDC", name: "USD Coin", decimals: "6", type: "FUNGIBLE_COMMON",
|
|
69
|
+
* treasury_account_id: "0.0.5176" }
|
|
70
|
+
*
|
|
71
|
+
* 2. Circle's official "USDC Contract Addresses" documentation:
|
|
72
|
+
* https://developers.circle.com/stablecoins/usdc-contract-addresses
|
|
73
|
+
* (Hedera row links to hashscan.io/mainnet/token/0.0.456858; Hedera Testnet row links to
|
|
74
|
+
* hashscan.io/testnet/token/0.0.429274). Circle publishes the native 0.0.x form, not an EVM
|
|
75
|
+
* address.
|
|
76
|
+
*
|
|
77
|
+
* Previewnet: Circle does not issue USDC there, so there is no verified id. `getUsdcTokenId`
|
|
78
|
+
* throws rather than silently falling back to a mainnet/testnet id.
|
|
79
|
+
*/
|
|
80
|
+
declare const USDC_TOKEN_IDS: {
|
|
81
|
+
readonly mainnet: "0.0.456858";
|
|
82
|
+
readonly testnet: "0.0.429274";
|
|
83
|
+
readonly previewnet: undefined;
|
|
84
|
+
};
|
|
85
|
+
/** Resolve the verified USDC token id for a network, or throw if none is known for it. */
|
|
86
|
+
declare function getUsdcTokenId(network: HederaNetwork): string;
|
|
87
|
+
/**
|
|
88
|
+
* Params for {@link verifyUsdcPayment}. Same as {@link VerifyHtsParams} minus `tokenId`/`decimals`
|
|
89
|
+
* (USDC is always 6 decimals) and with `network` **required** — USDC token ids and HashScan URLs are
|
|
90
|
+
* network-specific, so there is no implicit default network for the USDC helper.
|
|
91
|
+
*/
|
|
92
|
+
interface VerifyUsdcParams extends Omit<VerifyHtsParams, "tokenId" | "decimals" | "network"> {
|
|
93
|
+
network: HederaNetwork;
|
|
94
|
+
/**
|
|
95
|
+
* Override the canonical USDC token id — useful for a dev/testnet mock token. The amount is still
|
|
96
|
+
* parsed at 6 decimals. Production mainnet flows should omit this and use the verified token id.
|
|
97
|
+
*/
|
|
98
|
+
tokenId?: string;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Verify a USDC payment on Hedera. A convenience wrapper over {@link verifyHtsPayment} that resolves
|
|
102
|
+
* the canonical USDC token id for `network`, forces 6-decimal amount parsing, and tags the result
|
|
103
|
+
* asset as USDC. Every other semantic — `confirmed`/`pending`/`underpaid`/`overpaid`/`duplicate`/
|
|
104
|
+
* `mismatch`/`expired`/`failed`, memo/amount/time-window matching — is identical to HTS verification.
|
|
105
|
+
*/
|
|
106
|
+
declare function verifyUsdcPayment(p: VerifyUsdcParams): Promise<PaymentResult>;
|
|
107
|
+
/**
|
|
108
|
+
* True when a PaymentResult was produced by {@link verifyUsdcPayment} (its asset is tagged
|
|
109
|
+
* `symbol: "USDC"`). Useful for narrowing/displaying results from a mixed payment pipeline.
|
|
110
|
+
*/
|
|
111
|
+
declare function isUsdcPaymentResult(result: PaymentResult): boolean;
|
|
112
|
+
|
|
55
113
|
interface WaitOptions {
|
|
56
114
|
timeoutMs?: number;
|
|
57
115
|
pollIntervalMs?: number;
|
|
@@ -59,6 +117,7 @@ interface WaitOptions {
|
|
|
59
117
|
}
|
|
60
118
|
declare function waitForHbarPayment(p: VerifyHbarParams & WaitOptions): Promise<PaymentResult>;
|
|
61
119
|
declare function waitForHtsPayment(p: VerifyHtsParams & WaitOptions): Promise<PaymentResult>;
|
|
120
|
+
declare function waitForUsdcPayment(p: VerifyUsdcParams & WaitOptions): Promise<PaymentResult>;
|
|
62
121
|
|
|
63
122
|
/** HashScan transaction link: consensus timestamp in path, tx id as ?tid query param. */
|
|
64
123
|
declare function hashscanTxUrl(network: HederaNetwork, consensusTimestamp: string, transactionId: string): string;
|
|
@@ -69,4 +128,4 @@ declare function memoMatches(actual: string, expected: string, cmp?: MemoCompari
|
|
|
69
128
|
type AmountClass = "exact" | "underpaid" | "overpaid";
|
|
70
129
|
declare function classifyAmount(net: bigint, expected: bigint): AmountClass;
|
|
71
130
|
|
|
72
|
-
export { type MemoComparison, type PaymentAsset, type PaymentMatch, type PaymentResult, type PaymentStatus, type VerifyBaseParams, type VerifyHbarParams, type VerifyHtsParams, type WaitOptions, classifyAmount, hashscanTxUrl, memoMatches, netToReceiver, verifyHbarPayment, verifyHtsPayment, waitForHbarPayment, waitForHtsPayment };
|
|
131
|
+
export { type MemoComparison, type PaymentAsset, type PaymentMatch, type PaymentResult, type PaymentStatus, USDC_DECIMALS, USDC_TOKEN_IDS, type VerifyBaseParams, type VerifyHbarParams, type VerifyHtsParams, type VerifyUsdcParams, type WaitOptions, classifyAmount, getUsdcTokenId, hashscanTxUrl, isUsdcPaymentResult, memoMatches, netToReceiver, verifyHbarPayment, verifyHtsPayment, verifyUsdcPayment, waitForHbarPayment, waitForHtsPayment, waitForUsdcPayment };
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ type PaymentStatus = "confirmed" | "pending" | "underpaid" | "overpaid" | "dupli
|
|
|
5
5
|
type PaymentAsset = "HBAR" | {
|
|
6
6
|
tokenId: string;
|
|
7
7
|
decimals: number;
|
|
8
|
+
symbol?: string;
|
|
8
9
|
};
|
|
9
10
|
interface PaymentMatch {
|
|
10
11
|
transactionId: string;
|
|
@@ -52,6 +53,63 @@ interface VerifyHtsParams extends VerifyBaseParams {
|
|
|
52
53
|
declare function verifyHbarPayment(p: VerifyHbarParams): Promise<PaymentResult>;
|
|
53
54
|
declare function verifyHtsPayment(p: VerifyHtsParams): Promise<PaymentResult>;
|
|
54
55
|
|
|
56
|
+
/** USDC uses 6 decimals on every network Circle issues it on, including Hedera. */
|
|
57
|
+
declare const USDC_DECIMALS = 6;
|
|
58
|
+
/**
|
|
59
|
+
* Canonical Circle-issued USDC token ids on Hedera, per network.
|
|
60
|
+
*
|
|
61
|
+
* Verified two independent ways before hardcoding — do NOT change without re-verifying both:
|
|
62
|
+
*
|
|
63
|
+
* 1. Live Hedera Mirror Node token metadata (on-chain ground truth):
|
|
64
|
+
* - mainnet `GET https://mainnet-public.mirrornode.hedera.com/api/v1/tokens/0.0.456858`
|
|
65
|
+
* → { symbol: "USDC", name: "USD Coin", decimals: "6", type: "FUNGIBLE_COMMON",
|
|
66
|
+
* treasury_account_id: "0.0.439909" }
|
|
67
|
+
* - testnet `GET https://testnet.mirrornode.hedera.com/api/v1/tokens/0.0.429274`
|
|
68
|
+
* → { symbol: "USDC", name: "USD Coin", decimals: "6", type: "FUNGIBLE_COMMON",
|
|
69
|
+
* treasury_account_id: "0.0.5176" }
|
|
70
|
+
*
|
|
71
|
+
* 2. Circle's official "USDC Contract Addresses" documentation:
|
|
72
|
+
* https://developers.circle.com/stablecoins/usdc-contract-addresses
|
|
73
|
+
* (Hedera row links to hashscan.io/mainnet/token/0.0.456858; Hedera Testnet row links to
|
|
74
|
+
* hashscan.io/testnet/token/0.0.429274). Circle publishes the native 0.0.x form, not an EVM
|
|
75
|
+
* address.
|
|
76
|
+
*
|
|
77
|
+
* Previewnet: Circle does not issue USDC there, so there is no verified id. `getUsdcTokenId`
|
|
78
|
+
* throws rather than silently falling back to a mainnet/testnet id.
|
|
79
|
+
*/
|
|
80
|
+
declare const USDC_TOKEN_IDS: {
|
|
81
|
+
readonly mainnet: "0.0.456858";
|
|
82
|
+
readonly testnet: "0.0.429274";
|
|
83
|
+
readonly previewnet: undefined;
|
|
84
|
+
};
|
|
85
|
+
/** Resolve the verified USDC token id for a network, or throw if none is known for it. */
|
|
86
|
+
declare function getUsdcTokenId(network: HederaNetwork): string;
|
|
87
|
+
/**
|
|
88
|
+
* Params for {@link verifyUsdcPayment}. Same as {@link VerifyHtsParams} minus `tokenId`/`decimals`
|
|
89
|
+
* (USDC is always 6 decimals) and with `network` **required** — USDC token ids and HashScan URLs are
|
|
90
|
+
* network-specific, so there is no implicit default network for the USDC helper.
|
|
91
|
+
*/
|
|
92
|
+
interface VerifyUsdcParams extends Omit<VerifyHtsParams, "tokenId" | "decimals" | "network"> {
|
|
93
|
+
network: HederaNetwork;
|
|
94
|
+
/**
|
|
95
|
+
* Override the canonical USDC token id — useful for a dev/testnet mock token. The amount is still
|
|
96
|
+
* parsed at 6 decimals. Production mainnet flows should omit this and use the verified token id.
|
|
97
|
+
*/
|
|
98
|
+
tokenId?: string;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Verify a USDC payment on Hedera. A convenience wrapper over {@link verifyHtsPayment} that resolves
|
|
102
|
+
* the canonical USDC token id for `network`, forces 6-decimal amount parsing, and tags the result
|
|
103
|
+
* asset as USDC. Every other semantic — `confirmed`/`pending`/`underpaid`/`overpaid`/`duplicate`/
|
|
104
|
+
* `mismatch`/`expired`/`failed`, memo/amount/time-window matching — is identical to HTS verification.
|
|
105
|
+
*/
|
|
106
|
+
declare function verifyUsdcPayment(p: VerifyUsdcParams): Promise<PaymentResult>;
|
|
107
|
+
/**
|
|
108
|
+
* True when a PaymentResult was produced by {@link verifyUsdcPayment} (its asset is tagged
|
|
109
|
+
* `symbol: "USDC"`). Useful for narrowing/displaying results from a mixed payment pipeline.
|
|
110
|
+
*/
|
|
111
|
+
declare function isUsdcPaymentResult(result: PaymentResult): boolean;
|
|
112
|
+
|
|
55
113
|
interface WaitOptions {
|
|
56
114
|
timeoutMs?: number;
|
|
57
115
|
pollIntervalMs?: number;
|
|
@@ -59,6 +117,7 @@ interface WaitOptions {
|
|
|
59
117
|
}
|
|
60
118
|
declare function waitForHbarPayment(p: VerifyHbarParams & WaitOptions): Promise<PaymentResult>;
|
|
61
119
|
declare function waitForHtsPayment(p: VerifyHtsParams & WaitOptions): Promise<PaymentResult>;
|
|
120
|
+
declare function waitForUsdcPayment(p: VerifyUsdcParams & WaitOptions): Promise<PaymentResult>;
|
|
62
121
|
|
|
63
122
|
/** HashScan transaction link: consensus timestamp in path, tx id as ?tid query param. */
|
|
64
123
|
declare function hashscanTxUrl(network: HederaNetwork, consensusTimestamp: string, transactionId: string): string;
|
|
@@ -69,4 +128,4 @@ declare function memoMatches(actual: string, expected: string, cmp?: MemoCompari
|
|
|
69
128
|
type AmountClass = "exact" | "underpaid" | "overpaid";
|
|
70
129
|
declare function classifyAmount(net: bigint, expected: bigint): AmountClass;
|
|
71
130
|
|
|
72
|
-
export { type MemoComparison, type PaymentAsset, type PaymentMatch, type PaymentResult, type PaymentStatus, type VerifyBaseParams, type VerifyHbarParams, type VerifyHtsParams, type WaitOptions, classifyAmount, hashscanTxUrl, memoMatches, netToReceiver, verifyHbarPayment, verifyHtsPayment, waitForHbarPayment, waitForHtsPayment };
|
|
131
|
+
export { type MemoComparison, type PaymentAsset, type PaymentMatch, type PaymentResult, type PaymentStatus, USDC_DECIMALS, USDC_TOKEN_IDS, type VerifyBaseParams, type VerifyHbarParams, type VerifyHtsParams, type VerifyUsdcParams, type WaitOptions, classifyAmount, getUsdcTokenId, hashscanTxUrl, isUsdcPaymentResult, memoMatches, netToReceiver, verifyHbarPayment, verifyHtsPayment, verifyUsdcPayment, waitForHbarPayment, waitForHtsPayment, waitForUsdcPayment };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NETWORKS, txIdToMirror, parseHbar, parseUnits, InvalidParamsError, formatUnits } from '@hbar-kit/core';
|
|
1
|
+
import { NETWORKS, txIdToMirror, parseHbar, parseUnits, UnsupportedAssetError, InvalidParamsError, assertEntityId, formatUnits } from '@hbar-kit/core';
|
|
2
2
|
import { normalizeTransaction, createMirrorClient } from '@hbar-kit/mirror';
|
|
3
3
|
|
|
4
4
|
// src/verify.ts
|
|
@@ -146,6 +146,39 @@ async function verifyHtsPayment(p) {
|
|
|
146
146
|
}
|
|
147
147
|
return runVerify(p, { tokenId: p.tokenId, decimals }, parseUnits(p.amount, decimals), decimals);
|
|
148
148
|
}
|
|
149
|
+
var USDC_DECIMALS = 6;
|
|
150
|
+
var USDC_SYMBOL = "USDC";
|
|
151
|
+
var USDC_TOKEN_IDS = {
|
|
152
|
+
mainnet: "0.0.456858",
|
|
153
|
+
testnet: "0.0.429274",
|
|
154
|
+
previewnet: void 0
|
|
155
|
+
};
|
|
156
|
+
function getUsdcTokenId(network) {
|
|
157
|
+
const tokenId = USDC_TOKEN_IDS[network];
|
|
158
|
+
if (tokenId === void 0) {
|
|
159
|
+
throw new UnsupportedAssetError(
|
|
160
|
+
`USDC has no verified token id on Hedera ${network}. Pass an explicit \`tokenId\` to verify a custom token, or use "mainnet" or "testnet".`,
|
|
161
|
+
{ docsPath: "/guide/verify-usdc-payment" }
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
return tokenId;
|
|
165
|
+
}
|
|
166
|
+
async function verifyUsdcPayment(p) {
|
|
167
|
+
if (!p.network) {
|
|
168
|
+
throw new InvalidParamsError(
|
|
169
|
+
"`network` is required for USDC verification \u2014 USDC token ids and explorer URLs are network-specific",
|
|
170
|
+
{ docsPath: "/guide/verify-usdc-payment" }
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
const tokenId = p.tokenId ?? getUsdcTokenId(p.network);
|
|
174
|
+
assertEntityId(tokenId);
|
|
175
|
+
const result = await verifyHtsPayment({ ...p, tokenId, decimals: USDC_DECIMALS });
|
|
176
|
+
const asset = result.asset === "HBAR" ? result.asset : { ...result.asset, symbol: USDC_SYMBOL };
|
|
177
|
+
return { ...result, asset };
|
|
178
|
+
}
|
|
179
|
+
function isUsdcPaymentResult(result) {
|
|
180
|
+
return typeof result.asset === "object" && result.asset.symbol === USDC_SYMBOL;
|
|
181
|
+
}
|
|
149
182
|
|
|
150
183
|
// src/wait.ts
|
|
151
184
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
@@ -176,7 +209,10 @@ function waitForHbarPayment(p) {
|
|
|
176
209
|
function waitForHtsPayment(p) {
|
|
177
210
|
return poll(() => verifyHtsPayment(p), p);
|
|
178
211
|
}
|
|
212
|
+
function waitForUsdcPayment(p) {
|
|
213
|
+
return poll(() => verifyUsdcPayment(p), p);
|
|
214
|
+
}
|
|
179
215
|
|
|
180
|
-
export { classifyAmount, hashscanTxUrl, memoMatches, netToReceiver, verifyHbarPayment, verifyHtsPayment, waitForHbarPayment, waitForHtsPayment };
|
|
216
|
+
export { USDC_DECIMALS, USDC_TOKEN_IDS, classifyAmount, getUsdcTokenId, hashscanTxUrl, isUsdcPaymentResult, memoMatches, netToReceiver, verifyHbarPayment, verifyHtsPayment, verifyUsdcPayment, waitForHbarPayment, waitForHtsPayment, waitForUsdcPayment };
|
|
181
217
|
//# sourceMappingURL=index.js.map
|
|
182
218
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/match.ts","../src/explorer.ts","../src/verify.ts","../src/wait.ts"],"names":[],"mappings":";;;;;;AAIO,SAAS,aAAA,CAAc,EAAA,EAAiB,QAAA,EAAkB,KAAA,EAA6B;AAC5F,EAAA,IAAI,UAAU,MAAA,EAAQ;AACpB,IAAA,OAAO,GAAG,SAAA,CAAU,MAAA,CAAO,CAAC,CAAA,KAAM,EAAE,OAAA,KAAY,QAAQ,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,CAAA,CAAE,QAAQ,EAAE,CAAA;AAAA,EAC7F;AACA,EAAA,OAAO,EAAA,CAAG,eACP,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,OAAA,KAAY,MAAM,OAAA,IAAW,CAAA,CAAE,YAAY,QAAQ,CAAA,CACnE,OAAO,CAAC,CAAA,EAAG,MAAM,CAAA,GAAI,CAAA,CAAE,QAAQ,EAAE,CAAA;AACtC;AAEO,SAAS,WAAA,CAAY,MAAA,EAAgB,QAAA,EAAkB,GAAA,GAAsB,EAAC,EAAY;AAC/F,EAAA,MAAM,IAAA,GAAO,IAAI,IAAA,IAAQ,OAAA;AACzB,EAAA,IAAI,SAAS,MAAA,EAAQ,OAAO,OAAO,IAAA,EAAK,KAAM,SAAS,IAAA,EAAK;AAC5D,EAAA,IAAI,SAAS,iBAAA,EAAmB,OAAO,OAAO,WAAA,EAAY,KAAM,SAAS,WAAA,EAAY;AACrF,EAAA,OAAO,MAAA,KAAW,QAAA;AACpB;AAGO,SAAS,cAAA,CAAe,KAAa,QAAA,EAA+B;AACzE,EAAA,IAAI,GAAA,KAAQ,UAAU,OAAO,OAAA;AAC7B,EAAA,OAAO,GAAA,GAAM,WAAW,WAAA,GAAc,UAAA;AACxC;ACrBO,SAAS,aAAA,CACd,OAAA,EACA,kBAAA,EACA,aAAA,EACQ;AACR,EAAA,OAAO,CAAA,EAAG,QAAA,CAAS,OAAO,CAAA,CAAE,QAAQ,gBAAgB,kBAAkB,CAAA,KAAA,EAAQ,YAAA,CAAa,aAAa,CAAC,CAAA,CAAA;AAC3G;;;AC0BA,IAAM,kBAAA,uBAAyB,GAAA,EAAoB;AAEnD,SAAS,cAAc,CAAA,EAAuE;AAC5F,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,CAAA,CAAE,MAAA,IAAU,kBAAA,CAAmB,CAAC,CAAA;AAAA,IACxC,OAAA,EAAU,EAAE,OAAA,IAAW;AAAA,GACzB;AACF;AAEA,eAAe,SAAA,CACb,CAAA,EACA,KAAA,EACA,YAAA,EACA,QAAA,EACwB;AACxB,EAAA,IAAI,CAAA,CAAE,KAAA,IAAS,CAAA,CAAE,MAAA,IAAU,IAAI,IAAA,CAAK,CAAA,CAAE,KAAK,CAAA,GAAI,IAAI,IAAA,CAAK,CAAA,CAAE,MAAM,CAAA,EAAG;AACjE,IAAA,MAAM,IAAI,mBAAmB,iCAAiC,CAAA;AAAA,EAChE;AACA,EAAA,MAAM,EAAE,MAAA,EAAQ,OAAA,EAAQ,GAAI,cAAc,CAAC,CAAA;AAC3C,EAAA,MAAM,OAAA,GAAU,KAAA,KAAU,MAAA,GAAS,MAAA,GAAY,KAAA,CAAM,OAAA;AAErD,EAAA,MAAM,aAA6B,EAAC;AACpC,EAAA,MAAM,OAAA,GAAU,CAAC,KAAA,KAAyB;AACxC,IAAA,KAAA,MAAW,MAAM,KAAA,EAAO;AACtB,MAAA,IAAI,EAAA,CAAG,WAAW,SAAA,EAAW;AAC7B,MAAA,MAAM,GAAA,GAAM,aAAA,CAAc,EAAA,EAAI,CAAA,CAAE,UAAU,KAAK,CAAA;AAC/C,MAAA,IAAI,OAAO,EAAA,EAAI;AACf,MAAA,IAAI,OAAA,IAAW,CAAC,EAAA,CAAG,cAAA,CAAe,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAA,KAAY,OAAO,CAAA,EAAG;AACtE,MAAA,MAAM,QACJ,EAAA,CAAG,SAAA,CAAU,KAAK,CAAC,CAAA,KAAM,EAAE,MAAA,GAAS,EAAE,GAAG,OAAA,IACzC,EAAA,CAAG,eAAe,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,MAAA,GAAS,EAAE,CAAA,EAAG,OAAA;AAChD,MAAA,MAAM,KAAA,GAAsB;AAAA,QAC1B,eAAe,EAAA,CAAG,aAAA;AAAA,QAClB,kBAAA,EAAoB,GAAG,kBAAA,CAAmB,GAAA;AAAA,QAC1C,OAAA,EAAS,GAAA;AAAA,QACT,GAAA,EAAK,WAAA,CAAY,GAAA,EAAK,QAAQ,CAAA;AAAA,QAC9B,MAAM,EAAA,CAAG,IAAA;AAAA,QACT,WAAA,EAAa;AAAA,OACf;AACA,MAAA,IAAI,KAAA,KAAU,MAAA,EAAW,KAAA,CAAM,KAAA,GAAQ,KAAA;AACvC,MAAA,UAAA,CAAW,KAAK,KAAK,CAAA;AAAA,IACvB;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,UAAA,GAA6D;AAAA,IACjE,WAAW,CAAA,CAAE,QAAA;AAAA,IACb,eAAA,EAAiB,gBAAA;AAAA,IACjB,MAAA,EAAQ,SAAA;AAAA,IACR,KAAA,EAAO;AAAA,GACT;AACA,EAAA,IAAI,CAAA,CAAE,KAAA,KAAU,MAAA,EAAW,UAAA,CAAW,QAAQ,CAAA,CAAE,KAAA;AAChD,EAAA,IAAI,CAAA,CAAE,MAAA,KAAW,MAAA,EAAW,UAAA,CAAW,SAAS,CAAA,CAAE,MAAA;AAClD,EAAA,IAAI,IAAA,GAAO,MAAM,MAAA,CAAO,YAAA,CAAa,KAAK,UAAU,CAAA;AACpD,EAAA,OAAA,CAAQ,KAAK,KAAK,CAAA;AAClB,EAAA,OAAO,KAAK,IAAA,EAAM;AAChB,IAAA,MAAM,OAAQ,MAAM,MAAA,CAAO,SAAA,CAAU,GAAA,CAAI,KAAK,IAAI,CAAA;AAIlD,IAAA,OAAA,CAAA,CAAS,KAAK,YAAA,IAAgB,EAAC,EAAG,GAAA,CAAI,oBAAoB,CAAC,CAAA;AAC3D,IAAA,IAAA,GAAO,EAAE,OAAO,EAAC,EAAG,MAAM,IAAA,CAAK,KAAA,EAAO,QAAQ,IAAA,EAAK;AAAA,EACrD;AAEA,EAAA,MAAM,YAAA,GAAe,CAAA,CAAE,IAAA,GACnB,UAAA,CAAW,OAAO,CAAC,CAAA,KAAM,WAAA,CAAY,CAAA,CAAE,MAAM,CAAA,CAAE,IAAA,EAAO,CAAA,CAAE,cAAc,CAAC,CAAA,GACvE,UAAA;AAEJ,EAAA,MAAM,IAAA,GAAO,CAAC,MAAA,EAAiC,MAAA,MAAmC;AAAA,IAChF,OAAA,EAAS,KAAA;AAAA,IACT,MAAA;AAAA,IACA,UAAU,CAAA,CAAE,QAAA;AAAA,IACZ,KAAA;AAAA,IACA,OAAA,EAAS,YAAA;AAAA,IACT;AAAA,GACF,CAAA;AACA,EAAA,IAAI,WAAW,MAAA,KAAW,CAAA;AACxB,IAAA,OAAO,IAAA,CAAK,WAAW,iDAAiD,CAAA;AAC1E,EAAA,IAAI,CAAA,CAAE,IAAA,IAAQ,YAAA,CAAa,MAAA,KAAW,CAAA;AACpC,IAAA,OAAO,IAAA,CAAK,YAAY,0CAA0C,CAAA;AAEpE,EAAA,MAAM,WAAA,GAAc,EAAE,UAAA,KAAe,SAAA;AACrC,EAAA,MAAM,aAAa,YAAA,CAAa,MAAA;AAAA,IAAO,CAAC,CAAA,KACtC,WAAA,GAAc,EAAE,OAAA,IAAW,YAAA,GAAe,EAAE,OAAA,KAAY;AAAA,GAC1D;AAEA,EAAA,MAAM,aAAa,CACjB,CAAA,EACA,MAAA,EACA,OAAA,EACA,SACA,MAAA,KACkB;AAClB,IAAA,MAAM,MAAA,GAAwB;AAAA,MAC5B,OAAA;AAAA,MACA,MAAA;AAAA,MACA,UAAU,CAAA,CAAE,QAAA;AAAA,MACZ,KAAA;AAAA,MACA,eAAe,CAAA,CAAE,aAAA;AAAA,MACjB,YAAY,CAAA,CAAE,OAAA;AAAA,MACd,QAAQ,CAAA,CAAE,GAAA;AAAA,MACV,MAAM,CAAA,CAAE,IAAA;AAAA,MACR,oBAAoB,CAAA,CAAE,kBAAA;AAAA,MACtB,aAAa,aAAA,CAAc,OAAA,EAAS,CAAA,CAAE,kBAAA,EAAoB,EAAE,aAAa,CAAA;AAAA,MACzE;AAAA,KACF;AACA,IAAA,IAAI,CAAA,CAAE,KAAA,KAAU,MAAA,EAAW,MAAA,CAAO,QAAQ,CAAA,CAAE,KAAA;AAC5C,IAAA,IAAI,MAAA,KAAW,MAAA,EAAW,MAAA,CAAO,MAAA,GAAS,MAAA;AAC1C,IAAA,OAAO,MAAA;AAAA,EACT,CAAA;AAEA,EAAA,IAAI,UAAA,CAAW,WAAW,CAAA,EAAG;AAC3B,IAAA,MAAM,IAAA,GAAO,aAAa,CAAC,CAAA;AAC3B,IAAA,MAAM,GAAA,GAAM,cAAA,CAAe,IAAA,CAAK,OAAA,EAAS,YAAY,CAAA;AACrD,IAAA,OAAO,UAAA;AAAA,MACL,IAAA;AAAA,MACA,GAAA,KAAQ,UAAU,WAAA,GAAc,GAAA;AAAA,MAChC,KAAA;AAAA,MACA,YAAA;AAAA,MACA,UAAU,GAAG,CAAA;AAAA,KACf;AAAA,EACF;AACA,EAAA,IAAI,UAAA,CAAW,SAAS,CAAA,EAAG;AACzB,IAAA,OAAO,UAAA;AAAA,MACL,WAAW,CAAC,CAAA;AAAA,MACZ,WAAA;AAAA,MACA,KAAA;AAAA,MACA,UAAA;AAAA,MACA,CAAA,EAAG,WAAW,MAAM,CAAA,kCAAA;AAAA,KACtB;AAAA,EACF;AACA,EAAA,OAAO,WAAW,UAAA,CAAW,CAAC,CAAA,EAAI,WAAA,EAAa,MAAM,UAAU,CAAA;AACjE;AAEA,eAAsB,kBAAkB,CAAA,EAA6C;AACnF,EAAA,OAAO,UAAU,CAAA,EAAG,MAAA,EAAQ,UAAU,CAAA,CAAE,MAAM,GAAG,CAAC,CAAA;AACpD;AAEA,eAAsB,iBAAiB,CAAA,EAA4C;AACjF,EAAA,IAAI,WAAW,CAAA,CAAE,QAAA;AACjB,EAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,IAAA,MAAM,MAAA,GAAS,kBAAA,CAAmB,GAAA,CAAI,CAAA,CAAE,OAAO,CAAA;AAC/C,IAAA,IAAI,MAAA,KAAW,QAAW,QAAA,GAAW,MAAA;AAAA,SAChC;AACH,MAAA,MAAM,EAAE,MAAA,EAAO,GAAI,aAAA,CAAc,CAAC,CAAA;AAClC,MAAA,QAAA,GAAA,CAAY,MAAM,MAAA,CAAO,MAAA,CAAO,GAAA,CAAI,CAAA,CAAE,OAAO,CAAA,EAAG,QAAA;AAChD,MAAA,kBAAA,CAAmB,GAAA,CAAI,CAAA,CAAE,OAAA,EAAS,QAAQ,CAAA;AAAA,IAC5C;AAAA,EACF;AACA,EAAA,OAAO,SAAA,CAAU,CAAA,EAAG,EAAE,OAAA,EAAS,CAAA,CAAE,OAAA,EAAS,QAAA,EAAS,EAAG,UAAA,CAAW,CAAA,CAAE,MAAA,EAAQ,QAAQ,GAAG,QAAQ,CAAA;AAChG;;;AC3KA,IAAM,KAAA,GAAQ,CAAC,EAAA,KAAe,IAAI,OAAA,CAAQ,CAAC,CAAA,KAAM,UAAA,CAAW,CAAA,EAAG,EAAE,CAAC,CAAA;AAElE,eAAe,IAAA,CACb,QACA,IAAA,EACwB;AACxB,EAAA,MAAM,SAAA,GAAY,IAAA,CAAK,SAAA,IAAa,EAAA,GAAK,EAAA,GAAK,GAAA;AAC9C,EAAA,MAAM,cAAA,GAAiB,KAAK,cAAA,IAAkB,GAAA;AAC9C,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAC9B,EAAA,IAAI,IAAA;AACJ,EAAA,OAAO,IAAA,CAAK,GAAA,EAAI,GAAI,QAAA,EAAU;AAC5B,IAAA,IAAI,IAAA,CAAK,QAAQ,OAAA,EAAS;AAC1B,IAAA,IAAA,GAAO,MAAM,MAAA,EAAO;AACpB,IAAA,IAAI,IAAA,CAAK,SAAS,OAAO,IAAA;AACzB,IAAA,IAAI,KAAK,MAAA,KAAW,WAAA,IAAe,IAAA,CAAK,MAAA,KAAW,YAAY,OAAO,IAAA;AACtE,IAAA,MAAM,MAAM,cAAc,CAAA;AAAA,EAC5B;AACA,EAAA,OAAO,IAAA,IAAQ,KAAK,MAAA,KAAW,SAAA,GAC3B,EAAE,GAAG,IAAA,EAAM,MAAA,EAAQ,SAAA,EAAU,GAC7B;AAAA,IACE,OAAA,EAAS,KAAA;AAAA,IACT,MAAA,EAAQ,SAAA;AAAA,IACR,QAAA,EAAU,MAAM,QAAA,IAAY,EAAA;AAAA,IAC5B,KAAA,EAAO,MAAM,KAAA,IAAS,MAAA;AAAA,IACtB,SAAS,EAAC;AAAA,IACV,MAAA,EAAQ;AAAA,GACV;AACN;AAEO,SAAS,mBAAmB,CAAA,EAA2D;AAC5F,EAAA,OAAO,IAAA,CAAK,MAAM,iBAAA,CAAkB,CAAC,GAAG,CAAC,CAAA;AAC3C;AACO,SAAS,kBAAkB,CAAA,EAA0D;AAC1F,EAAA,OAAO,IAAA,CAAK,MAAM,gBAAA,CAAiB,CAAC,GAAG,CAAC,CAAA;AAC1C","file":"index.js","sourcesContent":["import type { Transaction } from \"@hbar-kit/mirror\"\nimport type { MemoComparison, PaymentAsset } from \"./types.js\"\n\n/** Signed sum of ALL the receiver's legs (HBAR transfers or a specific token's transfers). */\nexport function netToReceiver(tx: Transaction, receiver: string, asset: PaymentAsset): bigint {\n if (asset === \"HBAR\") {\n return tx.transfers.filter((t) => t.account === receiver).reduce((s, t) => s + t.amount, 0n)\n }\n return tx.tokenTransfers\n .filter((t) => t.tokenId === asset.tokenId && t.account === receiver)\n .reduce((s, t) => s + t.amount, 0n)\n}\n\nexport function memoMatches(actual: string, expected: string, cmp: MemoComparison = {}): boolean {\n const mode = cmp.mode ?? \"exact\"\n if (mode === \"trim\") return actual.trim() === expected.trim()\n if (mode === \"caseInsensitive\") return actual.toLowerCase() === expected.toLowerCase()\n return actual === expected\n}\n\nexport type AmountClass = \"exact\" | \"underpaid\" | \"overpaid\"\nexport function classifyAmount(net: bigint, expected: bigint): AmountClass {\n if (net === expected) return \"exact\"\n return net < expected ? \"underpaid\" : \"overpaid\"\n}\n","import { NETWORKS, txIdToMirror, type HederaNetwork } from \"@hbar-kit/core\"\n\n/** HashScan transaction link: consensus timestamp in path, tx id as ?tid query param. */\nexport function hashscanTxUrl(\n network: HederaNetwork,\n consensusTimestamp: string,\n transactionId: string,\n): string {\n return `${NETWORKS[network].hashscan}/transaction/${consensusTimestamp}?tid=${txIdToMirror(transactionId)}`\n}\n","import {\n parseUnits,\n parseHbar,\n formatUnits,\n type HederaNetwork,\n type NetworkInput,\n InvalidParamsError,\n} from \"@hbar-kit/core\"\nimport {\n createMirrorClient,\n normalizeTransaction,\n type MirrorClient,\n type RawTransaction,\n type Transaction,\n} from \"@hbar-kit/mirror\"\nimport { netToReceiver, memoMatches, classifyAmount } from \"./match.js\"\nimport { hashscanTxUrl } from \"./explorer.js\"\nimport type { MemoComparison, PaymentAsset, PaymentMatch, PaymentResult } from \"./types.js\"\n\nexport interface VerifyBaseParams extends NetworkInput {\n client?: MirrorClient\n receiver: string\n amount: string\n memo?: string\n memoComparison?: MemoComparison\n comparison?: \"exact\" | \"atLeast\"\n after?: Date | string\n before?: Date | string\n}\nexport type VerifyHbarParams = VerifyBaseParams\nexport interface VerifyHtsParams extends VerifyBaseParams {\n tokenId: string\n decimals?: number\n}\n\nconst tokenDecimalsCache = new Map<string, number>()\n\nfunction resolveClient(p: VerifyBaseParams): { client: MirrorClient; network: HederaNetwork } {\n return {\n client: p.client ?? createMirrorClient(p),\n network: (p.network ?? \"mainnet\") as HederaNetwork,\n }\n}\n\nasync function runVerify(\n p: VerifyBaseParams,\n asset: PaymentAsset,\n expectedBase: bigint,\n decimals: number,\n): Promise<PaymentResult> {\n if (p.after && p.before && new Date(p.after) > new Date(p.before)) {\n throw new InvalidParamsError(\"`after` must be before `before`\")\n }\n const { client, network } = resolveClient(p)\n const tokenId = asset === \"HBAR\" ? undefined : asset.tokenId\n\n const candidates: PaymentMatch[] = []\n const collect = (items: Transaction[]) => {\n for (const tx of items) {\n if (tx.result !== \"SUCCESS\") continue\n const net = netToReceiver(tx, p.receiver, asset)\n if (net <= 0n) continue\n if (tokenId && !tx.tokenTransfers.some((t) => t.tokenId === tokenId)) continue\n const payer =\n tx.transfers.find((t) => t.amount < 0n)?.account ??\n tx.tokenTransfers.find((t) => t.amount < 0n)?.account\n const match: PaymentMatch = {\n transactionId: tx.transactionId,\n consensusTimestamp: tx.consensusTimestamp.raw,\n netBase: net,\n net: formatUnits(net, decimals),\n memo: tx.memo,\n transaction: tx,\n }\n if (payer !== undefined) match.payer = payer\n candidates.push(match)\n }\n }\n\n const findParams: Parameters<typeof client.transactions.find>[0] = {\n accountId: p.receiver,\n transactionType: \"cryptotransfer\",\n result: \"success\",\n order: \"desc\",\n }\n if (p.after !== undefined) findParams.after = p.after\n if (p.before !== undefined) findParams.before = p.before\n let page = await client.transactions.find(findParams)\n collect(page.items)\n while (page.next) {\n const body = (await client.transport.get(page.next)) as {\n transactions?: RawTransaction[]\n links?: { next: string | null }\n }\n collect((body.transactions ?? []).map(normalizeTransaction))\n page = { items: [], next: body.links?.next ?? null }\n }\n\n const memoFiltered = p.memo\n ? candidates.filter((c) => memoMatches(c.memo, p.memo!, p.memoComparison))\n : candidates\n\n const fail = (status: PaymentResult[\"status\"], reason: string): PaymentResult => ({\n matched: false,\n status,\n receiver: p.receiver,\n asset,\n matches: memoFiltered,\n reason,\n })\n if (candidates.length === 0)\n return fail(\"pending\", \"no matching transactions for receiver in window\")\n if (p.memo && memoFiltered.length === 0)\n return fail(\"mismatch\", \"no transaction matched the expected memo\")\n\n const wantAtLeast = p.comparison === \"atLeast\"\n const satisfying = memoFiltered.filter((c) =>\n wantAtLeast ? c.netBase >= expectedBase : c.netBase === expectedBase,\n )\n\n const resultFrom = (\n m: PaymentMatch,\n status: PaymentResult[\"status\"],\n matched: boolean,\n matches: PaymentMatch[],\n reason?: string,\n ): PaymentResult => {\n const result: PaymentResult = {\n matched,\n status,\n receiver: p.receiver,\n asset,\n transactionId: m.transactionId,\n amountBase: m.netBase,\n amount: m.net,\n memo: m.memo,\n consensusTimestamp: m.consensusTimestamp,\n explorerUrl: hashscanTxUrl(network, m.consensusTimestamp, m.transactionId),\n matches,\n }\n if (m.payer !== undefined) result.payer = m.payer\n if (reason !== undefined) result.reason = reason\n return result\n }\n\n if (satisfying.length === 0) {\n const best = memoFiltered[0]!\n const cls = classifyAmount(best.netBase, expectedBase)\n return resultFrom(\n best,\n cls === \"exact\" ? \"confirmed\" : cls,\n false,\n memoFiltered,\n `amount ${cls}`,\n )\n }\n if (satisfying.length > 1) {\n return resultFrom(\n satisfying[0]!,\n \"duplicate\",\n false,\n satisfying,\n `${satisfying.length} transactions satisfy this request`,\n )\n }\n return resultFrom(satisfying[0]!, \"confirmed\", true, satisfying)\n}\n\nexport async function verifyHbarPayment(p: VerifyHbarParams): Promise<PaymentResult> {\n return runVerify(p, \"HBAR\", parseHbar(p.amount), 8)\n}\n\nexport async function verifyHtsPayment(p: VerifyHtsParams): Promise<PaymentResult> {\n let decimals = p.decimals\n if (decimals === undefined) {\n const cached = tokenDecimalsCache.get(p.tokenId)\n if (cached !== undefined) decimals = cached\n else {\n const { client } = resolveClient(p)\n decimals = (await client.tokens.get(p.tokenId)).decimals\n tokenDecimalsCache.set(p.tokenId, decimals)\n }\n }\n return runVerify(p, { tokenId: p.tokenId, decimals }, parseUnits(p.amount, decimals), decimals)\n}\n","import {\n verifyHbarPayment,\n verifyHtsPayment,\n type VerifyHbarParams,\n type VerifyHtsParams,\n} from \"./verify.js\"\nimport type { PaymentResult } from \"./types.js\"\n\nexport interface WaitOptions {\n timeoutMs?: number\n pollIntervalMs?: number\n signal?: AbortSignal\n}\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))\n\nasync function poll(\n verify: () => Promise<PaymentResult>,\n opts: WaitOptions,\n): Promise<PaymentResult> {\n const timeoutMs = opts.timeoutMs ?? 10 * 60 * 1000\n const pollIntervalMs = opts.pollIntervalMs ?? 3000\n const deadline = Date.now() + timeoutMs\n let last: PaymentResult | undefined\n while (Date.now() < deadline) {\n if (opts.signal?.aborted) break\n last = await verify()\n if (last.matched) return last\n if (last.status === \"duplicate\" || last.status === \"overpaid\") return last\n await sleep(pollIntervalMs)\n }\n return last && last.status !== \"pending\"\n ? { ...last, status: \"expired\" }\n : {\n matched: false,\n status: \"expired\",\n receiver: last?.receiver ?? \"\",\n asset: last?.asset ?? \"HBAR\",\n matches: [],\n reason: \"timed out waiting for payment\",\n }\n}\n\nexport function waitForHbarPayment(p: VerifyHbarParams & WaitOptions): Promise<PaymentResult> {\n return poll(() => verifyHbarPayment(p), p)\n}\nexport function waitForHtsPayment(p: VerifyHtsParams & WaitOptions): Promise<PaymentResult> {\n return poll(() => verifyHtsPayment(p), p)\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/match.ts","../src/explorer.ts","../src/verify.ts","../src/usdc.ts","../src/wait.ts"],"names":["InvalidParamsError"],"mappings":";;;;;;AAIO,SAAS,aAAA,CAAc,EAAA,EAAiB,QAAA,EAAkB,KAAA,EAA6B;AAC5F,EAAA,IAAI,UAAU,MAAA,EAAQ;AACpB,IAAA,OAAO,GAAG,SAAA,CAAU,MAAA,CAAO,CAAC,CAAA,KAAM,EAAE,OAAA,KAAY,QAAQ,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,CAAA,CAAE,QAAQ,EAAE,CAAA;AAAA,EAC7F;AACA,EAAA,OAAO,EAAA,CAAG,eACP,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,OAAA,KAAY,MAAM,OAAA,IAAW,CAAA,CAAE,YAAY,QAAQ,CAAA,CACnE,OAAO,CAAC,CAAA,EAAG,MAAM,CAAA,GAAI,CAAA,CAAE,QAAQ,EAAE,CAAA;AACtC;AAEO,SAAS,WAAA,CAAY,MAAA,EAAgB,QAAA,EAAkB,GAAA,GAAsB,EAAC,EAAY;AAC/F,EAAA,MAAM,IAAA,GAAO,IAAI,IAAA,IAAQ,OAAA;AACzB,EAAA,IAAI,SAAS,MAAA,EAAQ,OAAO,OAAO,IAAA,EAAK,KAAM,SAAS,IAAA,EAAK;AAC5D,EAAA,IAAI,SAAS,iBAAA,EAAmB,OAAO,OAAO,WAAA,EAAY,KAAM,SAAS,WAAA,EAAY;AACrF,EAAA,OAAO,MAAA,KAAW,QAAA;AACpB;AAGO,SAAS,cAAA,CAAe,KAAa,QAAA,EAA+B;AACzE,EAAA,IAAI,GAAA,KAAQ,UAAU,OAAO,OAAA;AAC7B,EAAA,OAAO,GAAA,GAAM,WAAW,WAAA,GAAc,UAAA;AACxC;ACrBO,SAAS,aAAA,CACd,OAAA,EACA,kBAAA,EACA,aAAA,EACQ;AACR,EAAA,OAAO,CAAA,EAAG,QAAA,CAAS,OAAO,CAAA,CAAE,QAAQ,gBAAgB,kBAAkB,CAAA,KAAA,EAAQ,YAAA,CAAa,aAAa,CAAC,CAAA,CAAA;AAC3G;;;AC0BA,IAAM,kBAAA,uBAAyB,GAAA,EAAoB;AAEnD,SAAS,cAAc,CAAA,EAAuE;AAC5F,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,CAAA,CAAE,MAAA,IAAU,kBAAA,CAAmB,CAAC,CAAA;AAAA,IACxC,OAAA,EAAU,EAAE,OAAA,IAAW;AAAA,GACzB;AACF;AAEA,eAAe,SAAA,CACb,CAAA,EACA,KAAA,EACA,YAAA,EACA,QAAA,EACwB;AACxB,EAAA,IAAI,CAAA,CAAE,KAAA,IAAS,CAAA,CAAE,MAAA,IAAU,IAAI,IAAA,CAAK,CAAA,CAAE,KAAK,CAAA,GAAI,IAAI,IAAA,CAAK,CAAA,CAAE,MAAM,CAAA,EAAG;AACjE,IAAA,MAAM,IAAI,mBAAmB,iCAAiC,CAAA;AAAA,EAChE;AACA,EAAA,MAAM,EAAE,MAAA,EAAQ,OAAA,EAAQ,GAAI,cAAc,CAAC,CAAA;AAC3C,EAAA,MAAM,OAAA,GAAU,KAAA,KAAU,MAAA,GAAS,MAAA,GAAY,KAAA,CAAM,OAAA;AAErD,EAAA,MAAM,aAA6B,EAAC;AACpC,EAAA,MAAM,OAAA,GAAU,CAAC,KAAA,KAAyB;AACxC,IAAA,KAAA,MAAW,MAAM,KAAA,EAAO;AACtB,MAAA,IAAI,EAAA,CAAG,WAAW,SAAA,EAAW;AAC7B,MAAA,MAAM,GAAA,GAAM,aAAA,CAAc,EAAA,EAAI,CAAA,CAAE,UAAU,KAAK,CAAA;AAC/C,MAAA,IAAI,OAAO,EAAA,EAAI;AACf,MAAA,IAAI,OAAA,IAAW,CAAC,EAAA,CAAG,cAAA,CAAe,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAA,KAAY,OAAO,CAAA,EAAG;AACtE,MAAA,MAAM,QACJ,EAAA,CAAG,SAAA,CAAU,KAAK,CAAC,CAAA,KAAM,EAAE,MAAA,GAAS,EAAE,GAAG,OAAA,IACzC,EAAA,CAAG,eAAe,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,MAAA,GAAS,EAAE,CAAA,EAAG,OAAA;AAChD,MAAA,MAAM,KAAA,GAAsB;AAAA,QAC1B,eAAe,EAAA,CAAG,aAAA;AAAA,QAClB,kBAAA,EAAoB,GAAG,kBAAA,CAAmB,GAAA;AAAA,QAC1C,OAAA,EAAS,GAAA;AAAA,QACT,GAAA,EAAK,WAAA,CAAY,GAAA,EAAK,QAAQ,CAAA;AAAA,QAC9B,MAAM,EAAA,CAAG,IAAA;AAAA,QACT,WAAA,EAAa;AAAA,OACf;AACA,MAAA,IAAI,KAAA,KAAU,MAAA,EAAW,KAAA,CAAM,KAAA,GAAQ,KAAA;AACvC,MAAA,UAAA,CAAW,KAAK,KAAK,CAAA;AAAA,IACvB;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,UAAA,GAA6D;AAAA,IACjE,WAAW,CAAA,CAAE,QAAA;AAAA,IACb,eAAA,EAAiB,gBAAA;AAAA,IACjB,MAAA,EAAQ,SAAA;AAAA,IACR,KAAA,EAAO;AAAA,GACT;AACA,EAAA,IAAI,CAAA,CAAE,KAAA,KAAU,MAAA,EAAW,UAAA,CAAW,QAAQ,CAAA,CAAE,KAAA;AAChD,EAAA,IAAI,CAAA,CAAE,MAAA,KAAW,MAAA,EAAW,UAAA,CAAW,SAAS,CAAA,CAAE,MAAA;AAClD,EAAA,IAAI,IAAA,GAAO,MAAM,MAAA,CAAO,YAAA,CAAa,KAAK,UAAU,CAAA;AACpD,EAAA,OAAA,CAAQ,KAAK,KAAK,CAAA;AAClB,EAAA,OAAO,KAAK,IAAA,EAAM;AAChB,IAAA,MAAM,OAAQ,MAAM,MAAA,CAAO,SAAA,CAAU,GAAA,CAAI,KAAK,IAAI,CAAA;AAIlD,IAAA,OAAA,CAAA,CAAS,KAAK,YAAA,IAAgB,EAAC,EAAG,GAAA,CAAI,oBAAoB,CAAC,CAAA;AAC3D,IAAA,IAAA,GAAO,EAAE,OAAO,EAAC,EAAG,MAAM,IAAA,CAAK,KAAA,EAAO,QAAQ,IAAA,EAAK;AAAA,EACrD;AAEA,EAAA,MAAM,YAAA,GAAe,CAAA,CAAE,IAAA,GACnB,UAAA,CAAW,OAAO,CAAC,CAAA,KAAM,WAAA,CAAY,CAAA,CAAE,MAAM,CAAA,CAAE,IAAA,EAAO,CAAA,CAAE,cAAc,CAAC,CAAA,GACvE,UAAA;AAEJ,EAAA,MAAM,IAAA,GAAO,CAAC,MAAA,EAAiC,MAAA,MAAmC;AAAA,IAChF,OAAA,EAAS,KAAA;AAAA,IACT,MAAA;AAAA,IACA,UAAU,CAAA,CAAE,QAAA;AAAA,IACZ,KAAA;AAAA,IACA,OAAA,EAAS,YAAA;AAAA,IACT;AAAA,GACF,CAAA;AACA,EAAA,IAAI,WAAW,MAAA,KAAW,CAAA;AACxB,IAAA,OAAO,IAAA,CAAK,WAAW,iDAAiD,CAAA;AAC1E,EAAA,IAAI,CAAA,CAAE,IAAA,IAAQ,YAAA,CAAa,MAAA,KAAW,CAAA;AACpC,IAAA,OAAO,IAAA,CAAK,YAAY,0CAA0C,CAAA;AAEpE,EAAA,MAAM,WAAA,GAAc,EAAE,UAAA,KAAe,SAAA;AACrC,EAAA,MAAM,aAAa,YAAA,CAAa,MAAA;AAAA,IAAO,CAAC,CAAA,KACtC,WAAA,GAAc,EAAE,OAAA,IAAW,YAAA,GAAe,EAAE,OAAA,KAAY;AAAA,GAC1D;AAEA,EAAA,MAAM,aAAa,CACjB,CAAA,EACA,MAAA,EACA,OAAA,EACA,SACA,MAAA,KACkB;AAClB,IAAA,MAAM,MAAA,GAAwB;AAAA,MAC5B,OAAA;AAAA,MACA,MAAA;AAAA,MACA,UAAU,CAAA,CAAE,QAAA;AAAA,MACZ,KAAA;AAAA,MACA,eAAe,CAAA,CAAE,aAAA;AAAA,MACjB,YAAY,CAAA,CAAE,OAAA;AAAA,MACd,QAAQ,CAAA,CAAE,GAAA;AAAA,MACV,MAAM,CAAA,CAAE,IAAA;AAAA,MACR,oBAAoB,CAAA,CAAE,kBAAA;AAAA,MACtB,aAAa,aAAA,CAAc,OAAA,EAAS,CAAA,CAAE,kBAAA,EAAoB,EAAE,aAAa,CAAA;AAAA,MACzE;AAAA,KACF;AACA,IAAA,IAAI,CAAA,CAAE,KAAA,KAAU,MAAA,EAAW,MAAA,CAAO,QAAQ,CAAA,CAAE,KAAA;AAC5C,IAAA,IAAI,MAAA,KAAW,MAAA,EAAW,MAAA,CAAO,MAAA,GAAS,MAAA;AAC1C,IAAA,OAAO,MAAA;AAAA,EACT,CAAA;AAEA,EAAA,IAAI,UAAA,CAAW,WAAW,CAAA,EAAG;AAC3B,IAAA,MAAM,IAAA,GAAO,aAAa,CAAC,CAAA;AAC3B,IAAA,MAAM,GAAA,GAAM,cAAA,CAAe,IAAA,CAAK,OAAA,EAAS,YAAY,CAAA;AACrD,IAAA,OAAO,UAAA;AAAA,MACL,IAAA;AAAA,MACA,GAAA,KAAQ,UAAU,WAAA,GAAc,GAAA;AAAA,MAChC,KAAA;AAAA,MACA,YAAA;AAAA,MACA,UAAU,GAAG,CAAA;AAAA,KACf;AAAA,EACF;AACA,EAAA,IAAI,UAAA,CAAW,SAAS,CAAA,EAAG;AACzB,IAAA,OAAO,UAAA;AAAA,MACL,WAAW,CAAC,CAAA;AAAA,MACZ,WAAA;AAAA,MACA,KAAA;AAAA,MACA,UAAA;AAAA,MACA,CAAA,EAAG,WAAW,MAAM,CAAA,kCAAA;AAAA,KACtB;AAAA,EACF;AACA,EAAA,OAAO,WAAW,UAAA,CAAW,CAAC,CAAA,EAAI,WAAA,EAAa,MAAM,UAAU,CAAA;AACjE;AAEA,eAAsB,kBAAkB,CAAA,EAA6C;AACnF,EAAA,OAAO,UAAU,CAAA,EAAG,MAAA,EAAQ,UAAU,CAAA,CAAE,MAAM,GAAG,CAAC,CAAA;AACpD;AAEA,eAAsB,iBAAiB,CAAA,EAA4C;AACjF,EAAA,IAAI,WAAW,CAAA,CAAE,QAAA;AACjB,EAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,IAAA,MAAM,MAAA,GAAS,kBAAA,CAAmB,GAAA,CAAI,CAAA,CAAE,OAAO,CAAA;AAC/C,IAAA,IAAI,MAAA,KAAW,QAAW,QAAA,GAAW,MAAA;AAAA,SAChC;AACH,MAAA,MAAM,EAAE,MAAA,EAAO,GAAI,aAAA,CAAc,CAAC,CAAA;AAClC,MAAA,QAAA,GAAA,CAAY,MAAM,MAAA,CAAO,MAAA,CAAO,GAAA,CAAI,CAAA,CAAE,OAAO,CAAA,EAAG,QAAA;AAChD,MAAA,kBAAA,CAAmB,GAAA,CAAI,CAAA,CAAE,OAAA,EAAS,QAAQ,CAAA;AAAA,IAC5C;AAAA,EACF;AACA,EAAA,OAAO,SAAA,CAAU,CAAA,EAAG,EAAE,OAAA,EAAS,CAAA,CAAE,OAAA,EAAS,QAAA,EAAS,EAAG,UAAA,CAAW,CAAA,CAAE,MAAA,EAAQ,QAAQ,GAAG,QAAQ,CAAA;AAChG;AC9KO,IAAM,aAAA,GAAgB;AAC7B,IAAM,WAAA,GAAc,MAAA;AAwBb,IAAM,cAAA,GAAiB;AAAA,EAC5B,OAAA,EAAS,YAAA;AAAA,EACT,OAAA,EAAS,YAAA;AAAA,EACT,UAAA,EAAY;AACd;AAGO,SAAS,eAAe,OAAA,EAAgC;AAC7D,EAAA,MAAM,OAAA,GAAU,eAAe,OAAO,CAAA;AACtC,EAAA,IAAI,YAAY,MAAA,EAAW;AACzB,IAAA,MAAM,IAAI,qBAAA;AAAA,MACR,2CAA2C,OAAO,CAAA,uFAAA,CAAA;AAAA,MAElD,EAAE,UAAU,4BAAA;AAA6B,KAC3C;AAAA,EACF;AACA,EAAA,OAAO,OAAA;AACT;AAsBA,eAAsB,kBAAkB,CAAA,EAA6C;AACnF,EAAA,IAAI,CAAC,EAAE,OAAA,EAAS;AACd,IAAA,MAAM,IAAIA,kBAAAA;AAAA,MACR,0GAAA;AAAA,MACA,EAAE,UAAU,4BAAA;AAA6B,KAC3C;AAAA,EACF;AACA,EAAA,MAAM,OAAA,GAAU,CAAA,CAAE,OAAA,IAAW,cAAA,CAAe,EAAE,OAAO,CAAA;AACrD,EAAA,cAAA,CAAe,OAAO,CAAA;AACtB,EAAA,MAAM,MAAA,GAAS,MAAM,gBAAA,CAAiB,EAAE,GAAG,CAAA,EAAG,OAAA,EAAS,QAAA,EAAU,aAAA,EAAe,CAAA;AAEhF,EAAA,MAAM,KAAA,GACJ,MAAA,CAAO,KAAA,KAAU,MAAA,GAAS,MAAA,CAAO,KAAA,GAAQ,EAAE,GAAG,MAAA,CAAO,KAAA,EAAO,MAAA,EAAQ,WAAA,EAAY;AAClF,EAAA,OAAO,EAAE,GAAG,MAAA,EAAQ,KAAA,EAAM;AAC5B;AAMO,SAAS,oBAAoB,MAAA,EAAgC;AAClE,EAAA,OAAO,OAAO,MAAA,CAAO,KAAA,KAAU,QAAA,IAAY,MAAA,CAAO,MAAM,MAAA,KAAW,WAAA;AACrE;;;AClFA,IAAM,KAAA,GAAQ,CAAC,EAAA,KAAe,IAAI,OAAA,CAAQ,CAAC,CAAA,KAAM,UAAA,CAAW,CAAA,EAAG,EAAE,CAAC,CAAA;AAElE,eAAe,IAAA,CACb,QACA,IAAA,EACwB;AACxB,EAAA,MAAM,SAAA,GAAY,IAAA,CAAK,SAAA,IAAa,EAAA,GAAK,EAAA,GAAK,GAAA;AAC9C,EAAA,MAAM,cAAA,GAAiB,KAAK,cAAA,IAAkB,GAAA;AAC9C,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAC9B,EAAA,IAAI,IAAA;AACJ,EAAA,OAAO,IAAA,CAAK,GAAA,EAAI,GAAI,QAAA,EAAU;AAC5B,IAAA,IAAI,IAAA,CAAK,QAAQ,OAAA,EAAS;AAC1B,IAAA,IAAA,GAAO,MAAM,MAAA,EAAO;AACpB,IAAA,IAAI,IAAA,CAAK,SAAS,OAAO,IAAA;AACzB,IAAA,IAAI,KAAK,MAAA,KAAW,WAAA,IAAe,IAAA,CAAK,MAAA,KAAW,YAAY,OAAO,IAAA;AACtE,IAAA,MAAM,MAAM,cAAc,CAAA;AAAA,EAC5B;AACA,EAAA,OAAO,IAAA,IAAQ,KAAK,MAAA,KAAW,SAAA,GAC3B,EAAE,GAAG,IAAA,EAAM,MAAA,EAAQ,SAAA,EAAU,GAC7B;AAAA,IACE,OAAA,EAAS,KAAA;AAAA,IACT,MAAA,EAAQ,SAAA;AAAA,IACR,QAAA,EAAU,MAAM,QAAA,IAAY,EAAA;AAAA,IAC5B,KAAA,EAAO,MAAM,KAAA,IAAS,MAAA;AAAA,IACtB,SAAS,EAAC;AAAA,IACV,MAAA,EAAQ;AAAA,GACV;AACN;AAEO,SAAS,mBAAmB,CAAA,EAA2D;AAC5F,EAAA,OAAO,IAAA,CAAK,MAAM,iBAAA,CAAkB,CAAC,GAAG,CAAC,CAAA;AAC3C;AACO,SAAS,kBAAkB,CAAA,EAA0D;AAC1F,EAAA,OAAO,IAAA,CAAK,MAAM,gBAAA,CAAiB,CAAC,GAAG,CAAC,CAAA;AAC1C;AACO,SAAS,mBAAmB,CAAA,EAA2D;AAC5F,EAAA,OAAO,IAAA,CAAK,MAAM,iBAAA,CAAkB,CAAC,GAAG,CAAC,CAAA;AAC3C","file":"index.js","sourcesContent":["import type { Transaction } from \"@hbar-kit/mirror\"\nimport type { MemoComparison, PaymentAsset } from \"./types.js\"\n\n/** Signed sum of ALL the receiver's legs (HBAR transfers or a specific token's transfers). */\nexport function netToReceiver(tx: Transaction, receiver: string, asset: PaymentAsset): bigint {\n if (asset === \"HBAR\") {\n return tx.transfers.filter((t) => t.account === receiver).reduce((s, t) => s + t.amount, 0n)\n }\n return tx.tokenTransfers\n .filter((t) => t.tokenId === asset.tokenId && t.account === receiver)\n .reduce((s, t) => s + t.amount, 0n)\n}\n\nexport function memoMatches(actual: string, expected: string, cmp: MemoComparison = {}): boolean {\n const mode = cmp.mode ?? \"exact\"\n if (mode === \"trim\") return actual.trim() === expected.trim()\n if (mode === \"caseInsensitive\") return actual.toLowerCase() === expected.toLowerCase()\n return actual === expected\n}\n\nexport type AmountClass = \"exact\" | \"underpaid\" | \"overpaid\"\nexport function classifyAmount(net: bigint, expected: bigint): AmountClass {\n if (net === expected) return \"exact\"\n return net < expected ? \"underpaid\" : \"overpaid\"\n}\n","import { NETWORKS, txIdToMirror, type HederaNetwork } from \"@hbar-kit/core\"\n\n/** HashScan transaction link: consensus timestamp in path, tx id as ?tid query param. */\nexport function hashscanTxUrl(\n network: HederaNetwork,\n consensusTimestamp: string,\n transactionId: string,\n): string {\n return `${NETWORKS[network].hashscan}/transaction/${consensusTimestamp}?tid=${txIdToMirror(transactionId)}`\n}\n","import {\n parseUnits,\n parseHbar,\n formatUnits,\n type HederaNetwork,\n type NetworkInput,\n InvalidParamsError,\n} from \"@hbar-kit/core\"\nimport {\n createMirrorClient,\n normalizeTransaction,\n type MirrorClient,\n type RawTransaction,\n type Transaction,\n} from \"@hbar-kit/mirror\"\nimport { netToReceiver, memoMatches, classifyAmount } from \"./match.js\"\nimport { hashscanTxUrl } from \"./explorer.js\"\nimport type { MemoComparison, PaymentAsset, PaymentMatch, PaymentResult } from \"./types.js\"\n\nexport interface VerifyBaseParams extends NetworkInput {\n client?: MirrorClient\n receiver: string\n amount: string\n memo?: string\n memoComparison?: MemoComparison\n comparison?: \"exact\" | \"atLeast\"\n after?: Date | string\n before?: Date | string\n}\nexport type VerifyHbarParams = VerifyBaseParams\nexport interface VerifyHtsParams extends VerifyBaseParams {\n tokenId: string\n decimals?: number\n}\n\nconst tokenDecimalsCache = new Map<string, number>()\n\nfunction resolveClient(p: VerifyBaseParams): { client: MirrorClient; network: HederaNetwork } {\n return {\n client: p.client ?? createMirrorClient(p),\n network: (p.network ?? \"mainnet\") as HederaNetwork,\n }\n}\n\nasync function runVerify(\n p: VerifyBaseParams,\n asset: PaymentAsset,\n expectedBase: bigint,\n decimals: number,\n): Promise<PaymentResult> {\n if (p.after && p.before && new Date(p.after) > new Date(p.before)) {\n throw new InvalidParamsError(\"`after` must be before `before`\")\n }\n const { client, network } = resolveClient(p)\n const tokenId = asset === \"HBAR\" ? undefined : asset.tokenId\n\n const candidates: PaymentMatch[] = []\n const collect = (items: Transaction[]) => {\n for (const tx of items) {\n if (tx.result !== \"SUCCESS\") continue\n const net = netToReceiver(tx, p.receiver, asset)\n if (net <= 0n) continue\n if (tokenId && !tx.tokenTransfers.some((t) => t.tokenId === tokenId)) continue\n const payer =\n tx.transfers.find((t) => t.amount < 0n)?.account ??\n tx.tokenTransfers.find((t) => t.amount < 0n)?.account\n const match: PaymentMatch = {\n transactionId: tx.transactionId,\n consensusTimestamp: tx.consensusTimestamp.raw,\n netBase: net,\n net: formatUnits(net, decimals),\n memo: tx.memo,\n transaction: tx,\n }\n if (payer !== undefined) match.payer = payer\n candidates.push(match)\n }\n }\n\n const findParams: Parameters<typeof client.transactions.find>[0] = {\n accountId: p.receiver,\n transactionType: \"cryptotransfer\",\n result: \"success\",\n order: \"desc\",\n }\n if (p.after !== undefined) findParams.after = p.after\n if (p.before !== undefined) findParams.before = p.before\n let page = await client.transactions.find(findParams)\n collect(page.items)\n while (page.next) {\n const body = (await client.transport.get(page.next)) as {\n transactions?: RawTransaction[]\n links?: { next: string | null }\n }\n collect((body.transactions ?? []).map(normalizeTransaction))\n page = { items: [], next: body.links?.next ?? null }\n }\n\n const memoFiltered = p.memo\n ? candidates.filter((c) => memoMatches(c.memo, p.memo!, p.memoComparison))\n : candidates\n\n const fail = (status: PaymentResult[\"status\"], reason: string): PaymentResult => ({\n matched: false,\n status,\n receiver: p.receiver,\n asset,\n matches: memoFiltered,\n reason,\n })\n if (candidates.length === 0)\n return fail(\"pending\", \"no matching transactions for receiver in window\")\n if (p.memo && memoFiltered.length === 0)\n return fail(\"mismatch\", \"no transaction matched the expected memo\")\n\n const wantAtLeast = p.comparison === \"atLeast\"\n const satisfying = memoFiltered.filter((c) =>\n wantAtLeast ? c.netBase >= expectedBase : c.netBase === expectedBase,\n )\n\n const resultFrom = (\n m: PaymentMatch,\n status: PaymentResult[\"status\"],\n matched: boolean,\n matches: PaymentMatch[],\n reason?: string,\n ): PaymentResult => {\n const result: PaymentResult = {\n matched,\n status,\n receiver: p.receiver,\n asset,\n transactionId: m.transactionId,\n amountBase: m.netBase,\n amount: m.net,\n memo: m.memo,\n consensusTimestamp: m.consensusTimestamp,\n explorerUrl: hashscanTxUrl(network, m.consensusTimestamp, m.transactionId),\n matches,\n }\n if (m.payer !== undefined) result.payer = m.payer\n if (reason !== undefined) result.reason = reason\n return result\n }\n\n if (satisfying.length === 0) {\n const best = memoFiltered[0]!\n const cls = classifyAmount(best.netBase, expectedBase)\n return resultFrom(\n best,\n cls === \"exact\" ? \"confirmed\" : cls,\n false,\n memoFiltered,\n `amount ${cls}`,\n )\n }\n if (satisfying.length > 1) {\n return resultFrom(\n satisfying[0]!,\n \"duplicate\",\n false,\n satisfying,\n `${satisfying.length} transactions satisfy this request`,\n )\n }\n return resultFrom(satisfying[0]!, \"confirmed\", true, satisfying)\n}\n\nexport async function verifyHbarPayment(p: VerifyHbarParams): Promise<PaymentResult> {\n return runVerify(p, \"HBAR\", parseHbar(p.amount), 8)\n}\n\nexport async function verifyHtsPayment(p: VerifyHtsParams): Promise<PaymentResult> {\n let decimals = p.decimals\n if (decimals === undefined) {\n const cached = tokenDecimalsCache.get(p.tokenId)\n if (cached !== undefined) decimals = cached\n else {\n const { client } = resolveClient(p)\n decimals = (await client.tokens.get(p.tokenId)).decimals\n tokenDecimalsCache.set(p.tokenId, decimals)\n }\n }\n return runVerify(p, { tokenId: p.tokenId, decimals }, parseUnits(p.amount, decimals), decimals)\n}\n","import {\n assertEntityId,\n InvalidParamsError,\n UnsupportedAssetError,\n type HederaNetwork,\n} from \"@hbar-kit/core\"\nimport { verifyHtsPayment, type VerifyHtsParams } from \"./verify.js\"\nimport type { PaymentAsset, PaymentResult } from \"./types.js\"\n\n/** USDC uses 6 decimals on every network Circle issues it on, including Hedera. */\nexport const USDC_DECIMALS = 6\nconst USDC_SYMBOL = \"USDC\"\n\n/**\n * Canonical Circle-issued USDC token ids on Hedera, per network.\n *\n * Verified two independent ways before hardcoding — do NOT change without re-verifying both:\n *\n * 1. Live Hedera Mirror Node token metadata (on-chain ground truth):\n * - mainnet `GET https://mainnet-public.mirrornode.hedera.com/api/v1/tokens/0.0.456858`\n * → { symbol: \"USDC\", name: \"USD Coin\", decimals: \"6\", type: \"FUNGIBLE_COMMON\",\n * treasury_account_id: \"0.0.439909\" }\n * - testnet `GET https://testnet.mirrornode.hedera.com/api/v1/tokens/0.0.429274`\n * → { symbol: \"USDC\", name: \"USD Coin\", decimals: \"6\", type: \"FUNGIBLE_COMMON\",\n * treasury_account_id: \"0.0.5176\" }\n *\n * 2. Circle's official \"USDC Contract Addresses\" documentation:\n * https://developers.circle.com/stablecoins/usdc-contract-addresses\n * (Hedera row links to hashscan.io/mainnet/token/0.0.456858; Hedera Testnet row links to\n * hashscan.io/testnet/token/0.0.429274). Circle publishes the native 0.0.x form, not an EVM\n * address.\n *\n * Previewnet: Circle does not issue USDC there, so there is no verified id. `getUsdcTokenId`\n * throws rather than silently falling back to a mainnet/testnet id.\n */\nexport const USDC_TOKEN_IDS = {\n mainnet: \"0.0.456858\",\n testnet: \"0.0.429274\",\n previewnet: undefined,\n} as const satisfies Record<HederaNetwork, string | undefined>\n\n/** Resolve the verified USDC token id for a network, or throw if none is known for it. */\nexport function getUsdcTokenId(network: HederaNetwork): string {\n const tokenId = USDC_TOKEN_IDS[network]\n if (tokenId === undefined) {\n throw new UnsupportedAssetError(\n `USDC has no verified token id on Hedera ${network}. ` +\n `Pass an explicit \\`tokenId\\` to verify a custom token, or use \"mainnet\" or \"testnet\".`,\n { docsPath: \"/guide/verify-usdc-payment\" },\n )\n }\n return tokenId\n}\n\n/**\n * Params for {@link verifyUsdcPayment}. Same as {@link VerifyHtsParams} minus `tokenId`/`decimals`\n * (USDC is always 6 decimals) and with `network` **required** — USDC token ids and HashScan URLs are\n * network-specific, so there is no implicit default network for the USDC helper.\n */\nexport interface VerifyUsdcParams extends Omit<VerifyHtsParams, \"tokenId\" | \"decimals\" | \"network\"> {\n network: HederaNetwork\n /**\n * Override the canonical USDC token id — useful for a dev/testnet mock token. The amount is still\n * parsed at 6 decimals. Production mainnet flows should omit this and use the verified token id.\n */\n tokenId?: string\n}\n\n/**\n * Verify a USDC payment on Hedera. A convenience wrapper over {@link verifyHtsPayment} that resolves\n * the canonical USDC token id for `network`, forces 6-decimal amount parsing, and tags the result\n * asset as USDC. Every other semantic — `confirmed`/`pending`/`underpaid`/`overpaid`/`duplicate`/\n * `mismatch`/`expired`/`failed`, memo/amount/time-window matching — is identical to HTS verification.\n */\nexport async function verifyUsdcPayment(p: VerifyUsdcParams): Promise<PaymentResult> {\n if (!p.network) {\n throw new InvalidParamsError(\n \"`network` is required for USDC verification — USDC token ids and explorer URLs are network-specific\",\n { docsPath: \"/guide/verify-usdc-payment\" },\n )\n }\n const tokenId = p.tokenId ?? getUsdcTokenId(p.network)\n assertEntityId(tokenId)\n const result = await verifyHtsPayment({ ...p, tokenId, decimals: USDC_DECIMALS })\n // verifyHtsPayment always yields a token asset (never \"HBAR\"); tag it so consumers can show USDC.\n const asset: PaymentAsset =\n result.asset === \"HBAR\" ? result.asset : { ...result.asset, symbol: USDC_SYMBOL }\n return { ...result, asset }\n}\n\n/**\n * True when a PaymentResult was produced by {@link verifyUsdcPayment} (its asset is tagged\n * `symbol: \"USDC\"`). Useful for narrowing/displaying results from a mixed payment pipeline.\n */\nexport function isUsdcPaymentResult(result: PaymentResult): boolean {\n return typeof result.asset === \"object\" && result.asset.symbol === USDC_SYMBOL\n}\n","import {\n verifyHbarPayment,\n verifyHtsPayment,\n type VerifyHbarParams,\n type VerifyHtsParams,\n} from \"./verify.js\"\nimport { verifyUsdcPayment, type VerifyUsdcParams } from \"./usdc.js\"\nimport type { PaymentResult } from \"./types.js\"\n\nexport interface WaitOptions {\n timeoutMs?: number\n pollIntervalMs?: number\n signal?: AbortSignal\n}\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))\n\nasync function poll(\n verify: () => Promise<PaymentResult>,\n opts: WaitOptions,\n): Promise<PaymentResult> {\n const timeoutMs = opts.timeoutMs ?? 10 * 60 * 1000\n const pollIntervalMs = opts.pollIntervalMs ?? 3000\n const deadline = Date.now() + timeoutMs\n let last: PaymentResult | undefined\n while (Date.now() < deadline) {\n if (opts.signal?.aborted) break\n last = await verify()\n if (last.matched) return last\n if (last.status === \"duplicate\" || last.status === \"overpaid\") return last\n await sleep(pollIntervalMs)\n }\n return last && last.status !== \"pending\"\n ? { ...last, status: \"expired\" }\n : {\n matched: false,\n status: \"expired\",\n receiver: last?.receiver ?? \"\",\n asset: last?.asset ?? \"HBAR\",\n matches: [],\n reason: \"timed out waiting for payment\",\n }\n}\n\nexport function waitForHbarPayment(p: VerifyHbarParams & WaitOptions): Promise<PaymentResult> {\n return poll(() => verifyHbarPayment(p), p)\n}\nexport function waitForHtsPayment(p: VerifyHtsParams & WaitOptions): Promise<PaymentResult> {\n return poll(() => verifyHtsPayment(p), p)\n}\nexport function waitForUsdcPayment(p: VerifyUsdcParams & WaitOptions): Promise<PaymentResult> {\n return poll(() => verifyUsdcPayment(p), p)\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hbar-kit/payments",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Verify native Hedera payments (HBAR & HTS) by receiver, amount, memo, and time window via the Mirror Node — read-only, non-custodial, bigint-safe.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://devwhodevs.github.io/hbar-kit/",
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
"module": "./dist/index.js",
|
|
48
48
|
"types": "./dist/index.d.ts",
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@hbar-kit/core": "0.
|
|
51
|
-
"@hbar-kit/mirror": "0.1.
|
|
50
|
+
"@hbar-kit/core": "0.2.0",
|
|
51
|
+
"@hbar-kit/mirror": "0.1.4"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"typescript": "^5.6.0",
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
export { verifyHbarPayment, verifyHtsPayment } from "./verify.js"
|
|
2
2
|
export type { VerifyHbarParams, VerifyHtsParams, VerifyBaseParams } from "./verify.js"
|
|
3
|
-
export { waitForHbarPayment, waitForHtsPayment } from "./wait.js"
|
|
3
|
+
export { waitForHbarPayment, waitForHtsPayment, waitForUsdcPayment } from "./wait.js"
|
|
4
4
|
export type { WaitOptions } from "./wait.js"
|
|
5
|
+
export {
|
|
6
|
+
verifyUsdcPayment,
|
|
7
|
+
getUsdcTokenId,
|
|
8
|
+
isUsdcPaymentResult,
|
|
9
|
+
USDC_TOKEN_IDS,
|
|
10
|
+
USDC_DECIMALS,
|
|
11
|
+
} from "./usdc.js"
|
|
12
|
+
export type { VerifyUsdcParams } from "./usdc.js"
|
|
5
13
|
export { hashscanTxUrl } from "./explorer.js"
|
|
6
14
|
export { netToReceiver, memoMatches, classifyAmount } from "./match.js"
|
|
7
15
|
export type {
|
package/src/types.ts
CHANGED
|
@@ -10,7 +10,7 @@ export type PaymentStatus =
|
|
|
10
10
|
| "expired"
|
|
11
11
|
| "failed"
|
|
12
12
|
|
|
13
|
-
export type PaymentAsset = "HBAR" | { tokenId: string; decimals: number }
|
|
13
|
+
export type PaymentAsset = "HBAR" | { tokenId: string; decimals: number; symbol?: string }
|
|
14
14
|
|
|
15
15
|
export interface PaymentMatch {
|
|
16
16
|
transactionId: string
|
package/src/usdc.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assertEntityId,
|
|
3
|
+
InvalidParamsError,
|
|
4
|
+
UnsupportedAssetError,
|
|
5
|
+
type HederaNetwork,
|
|
6
|
+
} from "@hbar-kit/core"
|
|
7
|
+
import { verifyHtsPayment, type VerifyHtsParams } from "./verify.js"
|
|
8
|
+
import type { PaymentAsset, PaymentResult } from "./types.js"
|
|
9
|
+
|
|
10
|
+
/** USDC uses 6 decimals on every network Circle issues it on, including Hedera. */
|
|
11
|
+
export const USDC_DECIMALS = 6
|
|
12
|
+
const USDC_SYMBOL = "USDC"
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Canonical Circle-issued USDC token ids on Hedera, per network.
|
|
16
|
+
*
|
|
17
|
+
* Verified two independent ways before hardcoding — do NOT change without re-verifying both:
|
|
18
|
+
*
|
|
19
|
+
* 1. Live Hedera Mirror Node token metadata (on-chain ground truth):
|
|
20
|
+
* - mainnet `GET https://mainnet-public.mirrornode.hedera.com/api/v1/tokens/0.0.456858`
|
|
21
|
+
* → { symbol: "USDC", name: "USD Coin", decimals: "6", type: "FUNGIBLE_COMMON",
|
|
22
|
+
* treasury_account_id: "0.0.439909" }
|
|
23
|
+
* - testnet `GET https://testnet.mirrornode.hedera.com/api/v1/tokens/0.0.429274`
|
|
24
|
+
* → { symbol: "USDC", name: "USD Coin", decimals: "6", type: "FUNGIBLE_COMMON",
|
|
25
|
+
* treasury_account_id: "0.0.5176" }
|
|
26
|
+
*
|
|
27
|
+
* 2. Circle's official "USDC Contract Addresses" documentation:
|
|
28
|
+
* https://developers.circle.com/stablecoins/usdc-contract-addresses
|
|
29
|
+
* (Hedera row links to hashscan.io/mainnet/token/0.0.456858; Hedera Testnet row links to
|
|
30
|
+
* hashscan.io/testnet/token/0.0.429274). Circle publishes the native 0.0.x form, not an EVM
|
|
31
|
+
* address.
|
|
32
|
+
*
|
|
33
|
+
* Previewnet: Circle does not issue USDC there, so there is no verified id. `getUsdcTokenId`
|
|
34
|
+
* throws rather than silently falling back to a mainnet/testnet id.
|
|
35
|
+
*/
|
|
36
|
+
export const USDC_TOKEN_IDS = {
|
|
37
|
+
mainnet: "0.0.456858",
|
|
38
|
+
testnet: "0.0.429274",
|
|
39
|
+
previewnet: undefined,
|
|
40
|
+
} as const satisfies Record<HederaNetwork, string | undefined>
|
|
41
|
+
|
|
42
|
+
/** Resolve the verified USDC token id for a network, or throw if none is known for it. */
|
|
43
|
+
export function getUsdcTokenId(network: HederaNetwork): string {
|
|
44
|
+
const tokenId = USDC_TOKEN_IDS[network]
|
|
45
|
+
if (tokenId === undefined) {
|
|
46
|
+
throw new UnsupportedAssetError(
|
|
47
|
+
`USDC has no verified token id on Hedera ${network}. ` +
|
|
48
|
+
`Pass an explicit \`tokenId\` to verify a custom token, or use "mainnet" or "testnet".`,
|
|
49
|
+
{ docsPath: "/guide/verify-usdc-payment" },
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
return tokenId
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Params for {@link verifyUsdcPayment}. Same as {@link VerifyHtsParams} minus `tokenId`/`decimals`
|
|
57
|
+
* (USDC is always 6 decimals) and with `network` **required** — USDC token ids and HashScan URLs are
|
|
58
|
+
* network-specific, so there is no implicit default network for the USDC helper.
|
|
59
|
+
*/
|
|
60
|
+
export interface VerifyUsdcParams extends Omit<VerifyHtsParams, "tokenId" | "decimals" | "network"> {
|
|
61
|
+
network: HederaNetwork
|
|
62
|
+
/**
|
|
63
|
+
* Override the canonical USDC token id — useful for a dev/testnet mock token. The amount is still
|
|
64
|
+
* parsed at 6 decimals. Production mainnet flows should omit this and use the verified token id.
|
|
65
|
+
*/
|
|
66
|
+
tokenId?: string
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Verify a USDC payment on Hedera. A convenience wrapper over {@link verifyHtsPayment} that resolves
|
|
71
|
+
* the canonical USDC token id for `network`, forces 6-decimal amount parsing, and tags the result
|
|
72
|
+
* asset as USDC. Every other semantic — `confirmed`/`pending`/`underpaid`/`overpaid`/`duplicate`/
|
|
73
|
+
* `mismatch`/`expired`/`failed`, memo/amount/time-window matching — is identical to HTS verification.
|
|
74
|
+
*/
|
|
75
|
+
export async function verifyUsdcPayment(p: VerifyUsdcParams): Promise<PaymentResult> {
|
|
76
|
+
if (!p.network) {
|
|
77
|
+
throw new InvalidParamsError(
|
|
78
|
+
"`network` is required for USDC verification — USDC token ids and explorer URLs are network-specific",
|
|
79
|
+
{ docsPath: "/guide/verify-usdc-payment" },
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
const tokenId = p.tokenId ?? getUsdcTokenId(p.network)
|
|
83
|
+
assertEntityId(tokenId)
|
|
84
|
+
const result = await verifyHtsPayment({ ...p, tokenId, decimals: USDC_DECIMALS })
|
|
85
|
+
// verifyHtsPayment always yields a token asset (never "HBAR"); tag it so consumers can show USDC.
|
|
86
|
+
const asset: PaymentAsset =
|
|
87
|
+
result.asset === "HBAR" ? result.asset : { ...result.asset, symbol: USDC_SYMBOL }
|
|
88
|
+
return { ...result, asset }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* True when a PaymentResult was produced by {@link verifyUsdcPayment} (its asset is tagged
|
|
93
|
+
* `symbol: "USDC"`). Useful for narrowing/displaying results from a mixed payment pipeline.
|
|
94
|
+
*/
|
|
95
|
+
export function isUsdcPaymentResult(result: PaymentResult): boolean {
|
|
96
|
+
return typeof result.asset === "object" && result.asset.symbol === USDC_SYMBOL
|
|
97
|
+
}
|
package/src/wait.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
type VerifyHbarParams,
|
|
5
5
|
type VerifyHtsParams,
|
|
6
6
|
} from "./verify.js"
|
|
7
|
+
import { verifyUsdcPayment, type VerifyUsdcParams } from "./usdc.js"
|
|
7
8
|
import type { PaymentResult } from "./types.js"
|
|
8
9
|
|
|
9
10
|
export interface WaitOptions {
|
|
@@ -46,3 +47,6 @@ export function waitForHbarPayment(p: VerifyHbarParams & WaitOptions): Promise<P
|
|
|
46
47
|
export function waitForHtsPayment(p: VerifyHtsParams & WaitOptions): Promise<PaymentResult> {
|
|
47
48
|
return poll(() => verifyHtsPayment(p), p)
|
|
48
49
|
}
|
|
50
|
+
export function waitForUsdcPayment(p: VerifyUsdcParams & WaitOptions): Promise<PaymentResult> {
|
|
51
|
+
return poll(() => verifyUsdcPayment(p), p)
|
|
52
|
+
}
|