zklicensing 0.1.0-beta.2 → 0.1.0-beta.21
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 +417 -201
- package/build/agent.d.ts +371 -0
- package/build/agent.d.ts.map +1 -0
- package/build/agent.js +553 -0
- package/build/agent.js.map +1 -0
- package/build/agentActions.d.ts +841 -0
- package/build/agentActions.d.ts.map +1 -0
- package/build/agentActions.js +581 -0
- package/build/agentActions.js.map +1 -0
- package/build/agentRecord.d.ts +83 -0
- package/build/agentRecord.d.ts.map +1 -0
- package/build/agentRecord.js +69 -0
- package/build/agentRecord.js.map +1 -0
- package/build/agentVendor.d.ts +220 -0
- package/build/agentVendor.d.ts.map +1 -0
- package/build/agentVendor.js +306 -0
- package/build/agentVendor.js.map +1 -0
- package/build/appsLookup.d.ts +14 -0
- package/build/appsLookup.d.ts.map +1 -0
- package/build/appsLookup.js +12 -0
- package/build/appsLookup.js.map +1 -0
- package/build/archiveBootstrap.d.ts +31 -5
- package/build/archiveBootstrap.d.ts.map +1 -0
- package/build/archiveBootstrap.js +140 -47
- package/build/archiveBootstrap.js.map +1 -0
- package/build/atomicJson.d.ts +5 -0
- package/build/atomicJson.d.ts.map +1 -0
- package/build/atomicJson.js +55 -0
- package/build/atomicJson.js.map +1 -0
- package/build/bindClient.d.ts +85 -0
- package/build/bindClient.d.ts.map +1 -0
- package/build/bindClient.js +187 -0
- package/build/bindClient.js.map +1 -0
- package/build/buyClient.d.ts +27 -4
- package/build/buyClient.d.ts.map +1 -0
- package/build/buyClient.js +72 -26
- package/build/buyClient.js.map +1 -0
- package/build/chainHead.d.ts +61 -0
- package/build/chainHead.d.ts.map +1 -0
- package/build/chainHead.js +88 -0
- package/build/chainHead.js.map +1 -0
- package/build/compactMerkleMap.d.ts +72 -0
- package/build/compactMerkleMap.d.ts.map +1 -0
- package/build/compactMerkleMap.js +224 -0
- package/build/compactMerkleMap.js.map +1 -0
- package/build/contractInterface.d.ts +191 -51
- package/build/contractInterface.d.ts.map +1 -0
- package/build/contractInterface.js +234 -43
- package/build/contractInterface.js.map +1 -0
- package/build/contractTypes.d.ts +9 -0
- package/build/contractTypes.d.ts.map +1 -0
- package/build/contractTypes.js +28 -0
- package/build/contractTypes.js.map +1 -0
- package/build/countryStats.d.ts +12 -3
- package/build/countryStats.d.ts.map +1 -0
- package/build/countryStats.js +87 -23
- package/build/countryStats.js.map +1 -0
- package/build/deployClient.d.ts +138 -24
- package/build/deployClient.d.ts.map +1 -0
- package/build/deployClient.js +91 -31
- package/build/deployClient.js.map +1 -0
- package/build/deviceIdentity.d.ts +68 -0
- package/build/deviceIdentity.d.ts.map +1 -0
- package/build/deviceIdentity.js +209 -0
- package/build/deviceIdentity.js.map +1 -0
- package/build/freezeClient.d.ts +26 -6
- package/build/freezeClient.d.ts.map +1 -0
- package/build/freezeClient.js +30 -6
- package/build/freezeClient.js.map +1 -0
- package/build/generationClient.d.ts +6 -3
- package/build/generationClient.d.ts.map +1 -0
- package/build/generationClient.js +25 -38
- package/build/generationClient.js.map +1 -0
- package/build/holderSecret.d.ts +70 -0
- package/build/holderSecret.d.ts.map +1 -0
- package/build/holderSecret.js +76 -0
- package/build/holderSecret.js.map +1 -0
- package/build/index.d.ts +38 -38
- package/build/index.d.ts.map +1 -0
- package/build/index.js +97 -121
- package/build/index.js.map +1 -0
- package/build/keysClient.d.ts +142 -0
- package/build/keysClient.d.ts.map +1 -0
- package/build/keysClient.js +109 -0
- package/build/keysClient.js.map +1 -0
- package/build/licenseLeaf.d.ts +76 -0
- package/build/licenseLeaf.d.ts.map +1 -0
- package/build/licenseLeaf.js +189 -0
- package/build/licenseLeaf.js.map +1 -0
- package/build/licenseRecord.d.ts +68 -5
- package/build/licenseRecord.d.ts.map +1 -0
- package/build/licenseRecord.js +26 -1
- package/build/licenseRecord.js.map +1 -0
- package/build/licenseStore.d.ts +81 -4
- package/build/licenseStore.d.ts.map +1 -0
- package/build/licenseStore.js +400 -30
- package/build/licenseStore.js.map +1 -0
- package/build/licenseTrustless.d.ts +43 -0
- package/build/licenseTrustless.d.ts.map +1 -0
- package/build/licenseTrustless.js +180 -0
- package/build/licenseTrustless.js.map +1 -0
- package/build/manifestVerify.d.ts +29 -1
- package/build/manifestVerify.d.ts.map +1 -0
- package/build/manifestVerify.js +46 -4
- package/build/manifestVerify.js.map +1 -0
- package/build/minaPriceHistory.d.ts +9 -0
- package/build/minaPriceHistory.d.ts.map +1 -0
- package/build/minaPriceHistory.js +152 -0
- package/build/minaPriceHistory.js.map +1 -0
- package/build/onChainRoot.d.ts +60 -0
- package/build/onChainRoot.d.ts.map +1 -0
- package/build/onChainRoot.js +74 -0
- package/build/onChainRoot.js.map +1 -0
- package/build/ownershipLicenseHash.d.ts +25 -0
- package/build/ownershipLicenseHash.d.ts.map +1 -0
- package/build/ownershipLicenseHash.js +29 -0
- package/build/ownershipLicenseHash.js.map +1 -0
- package/build/ownershipSignature.d.ts +153 -0
- package/build/ownershipSignature.d.ts.map +1 -0
- package/build/ownershipSignature.js +297 -0
- package/build/ownershipSignature.js.map +1 -0
- package/build/permitCheckFlow.d.ts +155 -0
- package/build/permitCheckFlow.d.ts.map +1 -0
- package/build/permitCheckFlow.js +196 -0
- package/build/permitCheckFlow.js.map +1 -0
- package/build/permitDescribe.d.ts +124 -0
- package/build/permitDescribe.d.ts.map +1 -0
- package/build/permitDescribe.js +324 -0
- package/build/permitDescribe.js.map +1 -0
- package/build/permitInterface.d.ts +85 -0
- package/build/permitInterface.d.ts.map +1 -0
- package/build/permitInterface.js +121 -0
- package/build/permitInterface.js.map +1 -0
- package/build/permitMessages.d.ts +313 -0
- package/build/permitMessages.d.ts.map +1 -0
- package/build/permitMessages.js +462 -0
- package/build/permitMessages.js.map +1 -0
- package/build/permitProof.d.ts +108 -0
- package/build/permitProof.d.ts.map +1 -0
- package/build/permitProof.js +202 -0
- package/build/permitProof.js.map +1 -0
- package/build/permitRegistryRebuild.d.ts +166 -0
- package/build/permitRegistryRebuild.d.ts.map +1 -0
- package/build/permitRegistryRebuild.js +347 -0
- package/build/permitRegistryRebuild.js.map +1 -0
- package/build/permitTree.d.ts +88 -0
- package/build/permitTree.d.ts.map +1 -0
- package/build/permitTree.js +230 -0
- package/build/permitTree.js.map +1 -0
- package/build/permitsClient.d.ts +680 -0
- package/build/permitsClient.d.ts.map +1 -0
- package/build/permitsClient.js +597 -0
- package/build/permitsClient.js.map +1 -0
- package/build/proveErrors.d.ts +50 -0
- package/build/proveErrors.d.ts.map +1 -0
- package/build/proveErrors.js +76 -0
- package/build/proveErrors.js.map +1 -0
- package/build/receipt.d.ts +95 -0
- package/build/receipt.d.ts.map +1 -0
- package/build/receipt.js +84 -0
- package/build/receipt.js.map +1 -0
- package/build/refundClient.d.ts +7 -12
- package/build/refundClient.d.ts.map +1 -0
- package/build/refundClient.js +52 -21
- package/build/refundClient.js.map +1 -0
- package/build/registryTrustVerify.d.ts +78 -0
- package/build/registryTrustVerify.d.ts.map +1 -0
- package/build/registryTrustVerify.js +129 -0
- package/build/registryTrustVerify.js.map +1 -0
- package/build/releaseClient.d.ts +26 -0
- package/build/releaseClient.d.ts.map +1 -0
- package/build/releaseClient.js +65 -0
- package/build/releaseClient.js.map +1 -0
- package/build/renewClient.d.ts +28 -2
- package/build/renewClient.d.ts.map +1 -0
- package/build/renewClient.js +31 -27
- package/build/renewClient.js.map +1 -0
- package/build/seatName.d.ts +8 -0
- package/build/seatName.d.ts.map +1 -0
- package/build/seatName.js +136 -0
- package/build/seatName.js.map +1 -0
- package/build/slotConstants.d.ts +15 -0
- package/build/slotConstants.d.ts.map +1 -0
- package/build/slotConstants.js +17 -0
- package/build/slotConstants.js.map +1 -0
- package/build/statsClient.d.ts +61 -7
- package/build/statsClient.d.ts.map +1 -0
- package/build/statsClient.js +44 -5
- package/build/statsClient.js.map +1 -0
- package/build/successorChain.d.ts +35 -0
- package/build/successorChain.d.ts.map +1 -0
- package/build/successorChain.js +69 -0
- package/build/successorChain.js.map +1 -0
- package/build/taxId.d.ts +44 -0
- package/build/taxId.d.ts.map +1 -0
- package/build/taxId.js +212 -0
- package/build/taxId.js.map +1 -0
- package/build/trustAnchorVerify.d.ts +49 -0
- package/build/trustAnchorVerify.d.ts.map +1 -0
- package/build/trustAnchorVerify.js +432 -0
- package/build/trustAnchorVerify.js.map +1 -0
- package/build/trustAnchorVerifyCli.d.ts +45 -0
- package/build/trustAnchorVerifyCli.d.ts.map +1 -0
- package/build/trustAnchorVerifyCli.js +235 -0
- package/build/trustAnchorVerifyCli.js.map +1 -0
- package/build/txBroadcastClient.d.ts +26 -0
- package/build/txBroadcastClient.d.ts.map +1 -0
- package/build/txBroadcastClient.js +20 -0
- package/build/txBroadcastClient.js.map +1 -0
- package/build/unbindClient.d.ts +77 -0
- package/build/unbindClient.d.ts.map +1 -0
- package/build/unbindClient.js +169 -0
- package/build/unbindClient.js.map +1 -0
- package/build/units.d.ts +28 -0
- package/build/units.d.ts.map +1 -0
- package/build/units.js +65 -0
- package/build/units.js.map +1 -0
- package/build/vatRates.d.ts +54 -0
- package/build/vatRates.d.ts.map +1 -0
- package/build/vatRates.js +82 -0
- package/build/vatRates.js.map +1 -0
- package/build/vatRatesData.d.ts +5 -0
- package/build/vatRatesData.d.ts.map +1 -0
- package/build/vatRatesData.js +42 -0
- package/build/vatRatesData.js.map +1 -0
- package/build/vendorAccountClient.d.ts +66 -0
- package/build/vendorAccountClient.d.ts.map +1 -0
- package/build/vendorAccountClient.js +47 -0
- package/build/vendorAccountClient.js.map +1 -0
- package/build/verifyCore.d.ts +4 -1
- package/build/verifyCore.d.ts.map +1 -0
- package/build/verifyCore.js +21 -10
- package/build/verifyCore.js.map +1 -0
- package/build/verifyOffline.d.ts +85 -0
- package/build/verifyOffline.d.ts.map +1 -0
- package/build/verifyOffline.js +139 -0
- package/build/verifyOffline.js.map +1 -0
- package/build/verifyService.d.ts.map +1 -0
- package/build/verifyService.js +571 -501
- package/build/verifyService.js.map +1 -0
- package/build/verifyWithAncestors.d.ts +19 -0
- package/build/verifyWithAncestors.d.ts.map +1 -0
- package/build/verifyWithAncestors.js +127 -0
- package/build/verifyWithAncestors.js.map +1 -0
- package/build/zipToState.d.ts +2 -0
- package/build/zipToState.d.ts.map +1 -0
- package/build/zipToState.js +94 -0
- package/build/zipToState.js.map +1 -0
- package/package.json +141 -16
- package/src/agent.ts +682 -0
- package/src/agentActions.ts +637 -0
- package/src/agentRecord.ts +121 -0
- package/src/agentVendor.ts +356 -0
- package/src/appsLookup.ts +48 -0
- package/src/archiveBootstrap.ts +386 -0
- package/src/atomicJson.ts +53 -0
- package/src/bindClient.ts +297 -0
- package/src/buyClient.ts +344 -0
- package/src/chainHead.ts +106 -0
- package/src/compactMerkleMap.ts +222 -0
- package/src/contractInterface.ts +465 -0
- package/src/contractTypes.ts +31 -0
- package/src/countryStats.ts +200 -0
- package/src/deployClient.ts +517 -0
- package/src/deviceIdentity.ts +249 -0
- package/src/freezeClient.ts +162 -0
- package/src/generationClient.ts +145 -0
- package/src/holderSecret.ts +91 -0
- package/src/index.ts +362 -0
- package/src/keysClient.ts +135 -0
- package/src/licenseLeaf.ts +221 -0
- package/src/licenseRecord.ts +190 -0
- package/src/licenseStore.ts +767 -0
- package/src/licenseTrustless.ts +207 -0
- package/src/manifestVerify.ts +164 -0
- package/src/minaPriceHistory.ts +166 -0
- package/src/onChainRoot.ts +90 -0
- package/src/ownershipLicenseHash.ts +31 -0
- package/src/ownershipSignature.ts +421 -0
- package/src/permitCheckFlow.ts +227 -0
- package/src/permitDescribe.ts +362 -0
- package/src/permitInterface.ts +146 -0
- package/src/permitMessages.ts +572 -0
- package/src/permitProof.ts +230 -0
- package/src/permitRegistryRebuild.ts +423 -0
- package/src/permitTree.ts +232 -0
- package/src/permitsClient.ts +746 -0
- package/src/proveErrors.ts +98 -0
- package/src/receipt.ts +156 -0
- package/src/refundClient.ts +177 -0
- package/src/registryTrustVerify.ts +192 -0
- package/src/releaseClient.ts +101 -0
- package/src/renewClient.ts +234 -0
- package/src/seatName.ts +165 -0
- package/src/slotConstants.ts +18 -0
- package/src/statsClient.ts +249 -0
- package/src/successorChain.ts +87 -0
- package/src/taxId.ts +227 -0
- package/src/trustAnchorVerify.ts +550 -0
- package/src/trustAnchorVerifyCli.ts +234 -0
- package/src/txBroadcastClient.ts +48 -0
- package/src/unbindClient.ts +233 -0
- package/src/units.ts +74 -0
- package/src/vatRates.ts +100 -0
- package/src/vatRatesData.ts +43 -0
- package/src/vendorAccountClient.ts +120 -0
- package/src/verifyCore.ts +100 -0
- package/src/verifyOffline.ts +212 -0
- package/src/verifyService.ts +982 -0
- package/src/verifyWithAncestors.ts +163 -0
- package/src/zipToState.ts +94 -0
- package/build/licenseProof.d.ts +0 -26
- package/build/licenseProof.js +0 -41
- package/build/migrateClient.d.ts +0 -43
- package/build/migrateClient.js +0 -47
- package/build/ownership.d.ts +0 -34
- package/build/ownership.js +0 -147
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// Copyright (c) 2025-2026 zkLicensing project developers
|
|
2
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* countryStats.ts
|
|
6
|
+
*
|
|
7
|
+
* Buyer-country breakdown of a vendor's revenue, bucketed by UTC calendar year.
|
|
8
|
+
* Structured for tax reporting: one row per (year, appId, countryCode) with
|
|
9
|
+
* license count + the vendor's cut in MINA. Both the backend (source of
|
|
10
|
+
* truth for /stats) and the dashboard UI import from here so the numbers can
|
|
11
|
+
* never drift between them.
|
|
12
|
+
*
|
|
13
|
+
* Purchases attribute to the year the buyer confirmed on-chain (LicenseRecord
|
|
14
|
+
* `createdAt` ISO timestamp). Legacy records that predate `createdAt` fall
|
|
15
|
+
* back to slot-derived time. Renewals attribute to the renewal year via
|
|
16
|
+
* `renewals[i].slot → time`. Refunded purchases contribute zero revenue but
|
|
17
|
+
* still count the license (a purchase happened even if the money was
|
|
18
|
+
* returned) — remove `count` from refunded rows client-side if you want a
|
|
19
|
+
* strictly net view.
|
|
20
|
+
*
|
|
21
|
+
* Browser-safe: pure computation, no fs, no fetch.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { vendorShareNano, type LicenseRecord } from './licenseRecord.js';
|
|
25
|
+
import { standardVatRate, vatIncludedNano } from './vatRates.js';
|
|
26
|
+
import { zipToState } from './zipToState.js';
|
|
27
|
+
|
|
28
|
+
export { zipToState } from './zipToState.js';
|
|
29
|
+
|
|
30
|
+
// LicensingApp charges a 2 % platform fee (200 basis points); the vendor
|
|
31
|
+
// receives the remaining 9 800 bps. Keep this in sync with the on-chain
|
|
32
|
+
// constant in LicensingApp.
|
|
33
|
+
|
|
34
|
+
// 90 s/slot post-Mesa. Used only for slot→time fallback attribution.
|
|
35
|
+
const SLOT_DURATION_MS = 90_000;
|
|
36
|
+
|
|
37
|
+
export type CountryBucket = {
|
|
38
|
+
count: number; // licenses purchased by buyers from this country (renewals don't bump)
|
|
39
|
+
revenueMina: number; // vendor cut in MINA — purchase + renewals combined
|
|
40
|
+
// What buyers paid (the VAT-inclusive price), before the platform fee —
|
|
41
|
+
// the VAT base. Refunded purchases count zero, as for revenue.
|
|
42
|
+
grossMina: number;
|
|
43
|
+
// Estimated VAT included in grossMina, at the buyer country's standard
|
|
44
|
+
// rate on each sale's date (zklicensing/vat). Absent for countries
|
|
45
|
+
// without VAT in the table. An estimate for consumer sales, not tax
|
|
46
|
+
// advice: reverse-charged business sales and thresholds aren't applied.
|
|
47
|
+
estimatedVatMina?: number;
|
|
48
|
+
// The distinct standard rates (percent) applied, sorted; more than one
|
|
49
|
+
// when the rate changed during the year.
|
|
50
|
+
vatRates?: number[];
|
|
51
|
+
// State sub-buckets — populated only for US buyers, derived from
|
|
52
|
+
// `buyerZip` via zipToState. States are the jurisdictional unit that US
|
|
53
|
+
// sales-tax filings care about; the raw ZIP stays on the LicenseRecord
|
|
54
|
+
// for audit trail but isn't rolled up here. Missing entirely on non-US
|
|
55
|
+
// rows; may be missing on US rows for legacy records that predate ZIP
|
|
56
|
+
// capture or whose ZIP prefix doesn't resolve to a state.
|
|
57
|
+
states?: Record<string, { count: number; revenueMina: number; grossMina: number }>;
|
|
58
|
+
};
|
|
59
|
+
export type CountryYearBreakdown = {
|
|
60
|
+
aggregate: Record<string, CountryBucket>; // keyed by ISO-alpha-2 (or '??' when missing)
|
|
61
|
+
byApp: Record<string, Record<string, CountryBucket>>; // appId → country → bucket
|
|
62
|
+
};
|
|
63
|
+
export type CountryBreakdown = {
|
|
64
|
+
years: number[]; // UTC calendar years with activity, most-recent first
|
|
65
|
+
byYear: Record<number, CountryYearBreakdown>;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// tzOffsetMinutes: signed minutes east of UTC (e.g. +60 for CET, -480 for PST).
|
|
69
|
+
// Purchase/renewal timestamps are shifted by this offset before extracting the
|
|
70
|
+
// calendar year, so the "tax year" cut-over aligns with the operator's local
|
|
71
|
+
// clock instead of hardcoded UTC. Defaults to 0 (UTC) for callers who don't
|
|
72
|
+
// pass it — preserves the pre-tz behavior.
|
|
73
|
+
export function computeCountryBreakdown(
|
|
74
|
+
apps: { appId: string; zkAppAddress: string }[],
|
|
75
|
+
records: LicenseRecord[],
|
|
76
|
+
currentSlot: number,
|
|
77
|
+
nowMs: number,
|
|
78
|
+
tzOffsetMinutes: number = 0,
|
|
79
|
+
): CountryBreakdown {
|
|
80
|
+
const appByAddress = new Map(apps.map(a => [a.zkAppAddress, a.appId] as const));
|
|
81
|
+
const byYear: Record<number, CountryYearBreakdown> = {};
|
|
82
|
+
const yearSet = new Set<number>();
|
|
83
|
+
const tzShiftMs = tzOffsetMinutes * 60_000;
|
|
84
|
+
|
|
85
|
+
const ensureYear = (year: number): CountryYearBreakdown => {
|
|
86
|
+
let yb = byYear[year];
|
|
87
|
+
if (!yb) {
|
|
88
|
+
yb = { aggregate: {}, byApp: {} };
|
|
89
|
+
byYear[year] = yb;
|
|
90
|
+
yearSet.add(year);
|
|
91
|
+
}
|
|
92
|
+
return yb;
|
|
93
|
+
};
|
|
94
|
+
const ensureAppBucket = (yb: CountryYearBreakdown, appId: string): Record<string, CountryBucket> => {
|
|
95
|
+
let ab = yb.byApp[appId];
|
|
96
|
+
if (!ab) {
|
|
97
|
+
ab = {};
|
|
98
|
+
yb.byApp[appId] = ab;
|
|
99
|
+
}
|
|
100
|
+
return ab;
|
|
101
|
+
};
|
|
102
|
+
const add = (
|
|
103
|
+
bucket: Record<string, CountryBucket>,
|
|
104
|
+
code: string,
|
|
105
|
+
count: number,
|
|
106
|
+
revenueNano: bigint,
|
|
107
|
+
grossNano: bigint,
|
|
108
|
+
saleMs: number | null,
|
|
109
|
+
state?: string,
|
|
110
|
+
): void => {
|
|
111
|
+
let b = bucket[code];
|
|
112
|
+
if (!b) {
|
|
113
|
+
b = { count: 0, revenueMina: 0, grossMina: 0 };
|
|
114
|
+
bucket[code] = b;
|
|
115
|
+
}
|
|
116
|
+
b.count += count;
|
|
117
|
+
b.revenueMina += Number(revenueNano) / 1e9;
|
|
118
|
+
b.grossMina += Number(grossNano) / 1e9;
|
|
119
|
+
// VAT included at the rate in force on the sale date.
|
|
120
|
+
const rate = code !== '??' && saleMs !== null ? standardVatRate(code, saleMs) : null;
|
|
121
|
+
if (rate !== null) {
|
|
122
|
+
b.estimatedVatMina = (b.estimatedVatMina ?? 0) + Number(vatIncludedNano(grossNano, rate).vatNano) / 1e9;
|
|
123
|
+
if (grossNano > 0n) {
|
|
124
|
+
const rates = (b.vatRates ??= []);
|
|
125
|
+
if (!rates.includes(rate)) { rates.push(rate); rates.sort((x, y) => x - y); }
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// State sub-bucket only for US rows whose ZIP resolved to a state.
|
|
129
|
+
// Non-US or unresolvable rows just add to the country total.
|
|
130
|
+
if (code === 'US' && state) {
|
|
131
|
+
const states = b.states ?? (b.states = {});
|
|
132
|
+
let sb = states[state];
|
|
133
|
+
if (!sb) {
|
|
134
|
+
sb = { count: 0, revenueMina: 0, grossMina: 0 };
|
|
135
|
+
states[state] = sb;
|
|
136
|
+
}
|
|
137
|
+
sb.count += count;
|
|
138
|
+
sb.revenueMina += Number(revenueNano) / 1e9;
|
|
139
|
+
sb.grossMina += Number(grossNano) / 1e9;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const slotToMs = (slot: number): number | null => {
|
|
143
|
+
if (currentSlot <= 0) return null;
|
|
144
|
+
const ms = nowMs - (currentSlot - slot) * SLOT_DURATION_MS;
|
|
145
|
+
return Number.isFinite(ms) ? ms : null;
|
|
146
|
+
};
|
|
147
|
+
const msToYear = (ms: number | null): number | null => {
|
|
148
|
+
if (ms === null) return null;
|
|
149
|
+
const y = new Date(ms + tzShiftMs).getUTCFullYear();
|
|
150
|
+
return Number.isFinite(y) ? y : null;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
for (const r of records) {
|
|
154
|
+
const appId = appByAddress.get(r.zkAppAddress);
|
|
155
|
+
if (!appId) continue;
|
|
156
|
+
const country = r.buyerCountry && /^[A-Z]{2}$/.test(r.buyerCountry) ? r.buyerCountry : '??';
|
|
157
|
+
// Only US rows carry a meaningful ZIP for sales-tax purposes; derive
|
|
158
|
+
// the state jurisdiction here and let `add` roll it up. Non-US ZIPs
|
|
159
|
+
// and unresolvable prefixes fall through to country-total only.
|
|
160
|
+
const state = country === 'US' && r.buyerZip ? zipToState(r.buyerZip) : undefined;
|
|
161
|
+
|
|
162
|
+
// Purchase — attribute to the year of confirm-on-chain (createdAt).
|
|
163
|
+
// Fallback: slot-derived time for legacy records that lack createdAt.
|
|
164
|
+
let purchaseMs: number | null = null;
|
|
165
|
+
if (r.createdAt) {
|
|
166
|
+
const t = new Date(r.createdAt).getTime();
|
|
167
|
+
if (Number.isFinite(t)) purchaseMs = t;
|
|
168
|
+
}
|
|
169
|
+
if (purchaseMs === null && r.purchaseSlot > 0) {
|
|
170
|
+
purchaseMs = slotToMs(r.purchaseSlot);
|
|
171
|
+
}
|
|
172
|
+
const purchaseYear = msToYear(purchaseMs);
|
|
173
|
+
if (purchaseYear !== null) {
|
|
174
|
+
const yb = ensureYear(purchaseYear);
|
|
175
|
+
const ab = ensureAppBucket(yb, appId);
|
|
176
|
+
const purchaseGross = (!r.refunded && r.purchaseAmount && r.purchaseAmount !== '0') ? BigInt(r.purchaseAmount) : 0n;
|
|
177
|
+
const purchaseCut = purchaseGross > 0n ? vendorShareNano(purchaseGross) : 0n;
|
|
178
|
+
add(ab, country, 1, purchaseCut, purchaseGross, purchaseMs, state);
|
|
179
|
+
add(yb.aggregate, country, 1, purchaseCut, purchaseGross, purchaseMs, state);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Renewals — revenue only (no license count), attributed to renewal year.
|
|
183
|
+
for (const rn of r.renewals ?? []) {
|
|
184
|
+
const ms = slotToMs(rn.slot);
|
|
185
|
+
const y = msToYear(ms);
|
|
186
|
+
if (y === null) continue;
|
|
187
|
+
const yb = ensureYear(y);
|
|
188
|
+
const ab = ensureAppBucket(yb, appId);
|
|
189
|
+
const gross = BigInt(rn.amount);
|
|
190
|
+
const cut = vendorShareNano(gross);
|
|
191
|
+
add(ab, country, 0, cut, gross, ms, state);
|
|
192
|
+
add(yb.aggregate, country, 0, cut, gross, ms, state);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
years: Array.from(yearSet).sort((a, b) => b - a),
|
|
198
|
+
byYear,
|
|
199
|
+
};
|
|
200
|
+
}
|