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,101 @@
|
|
|
1
|
+
// Copyright (c) 2025-2026 zkLicensing project developers
|
|
2
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* releaseClient.ts
|
|
6
|
+
*
|
|
7
|
+
* Vendor-signed release path — call POST /prove/release-vendor to get an
|
|
8
|
+
* unsigned proved tx, then broadcast it through the vendor's wallet
|
|
9
|
+
* (`provider.sendTransaction`). The vendor pays the transaction fee and
|
|
10
|
+
* signs the fee-payer AU themselves — the licensing service never needs
|
|
11
|
+
* its own feepayer key for this path.
|
|
12
|
+
*
|
|
13
|
+
* `releaseFunds` is permissionless on-chain, so anyone willing to pay gas
|
|
14
|
+
* may call this. There is no server-side identity check — the client's
|
|
15
|
+
* wallet holds full authority via the fee-payer signature.
|
|
16
|
+
*
|
|
17
|
+
* On-chain settlement is observed by the licensing service's event-sync
|
|
18
|
+
* loop. That sweep runs every 30 minutes, so callers who want the
|
|
19
|
+
* `released` flag to flip within seconds of inclusion should call
|
|
20
|
+
* `confirmVendorRelease` with the wallet's returned tx hash — the service
|
|
21
|
+
* then watches bestChain and mirrors the fundsReleased event optimistically.
|
|
22
|
+
* The event-sync sweep is still authoritative; the confirm/watch path is a
|
|
23
|
+
* latency shortcut and is idempotent.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { readErrorBody, keeperError } from './proveErrors.js';
|
|
27
|
+
|
|
28
|
+
export type ProveVendorReleaseRequest = {
|
|
29
|
+
keeperUrl: string;
|
|
30
|
+
licenseHash: string;
|
|
31
|
+
zkAppAddress: string;
|
|
32
|
+
senderPublicKey: string;
|
|
33
|
+
// Optional — the service will resolve the network from the zkAppAddress
|
|
34
|
+
// when omitted. Set explicitly when the caller already knows which
|
|
35
|
+
// network the deployment lives on.
|
|
36
|
+
network?: 'devnet' | 'mainnet';
|
|
37
|
+
fetcher?: typeof fetch;
|
|
38
|
+
signal?: AbortSignal;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type ProveVendorReleaseResponse = {
|
|
42
|
+
// Serialized Mina.transaction JSON, proved but with an unsigned fee-payer
|
|
43
|
+
// AU. Pass verbatim to `provider.sendTransaction({ transaction })`.
|
|
44
|
+
provenTxJson: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
function baseUrl(url: string): string {
|
|
48
|
+
return url.replace(/\/+$/, '');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function proveVendorRelease(req: ProveVendorReleaseRequest): Promise<ProveVendorReleaseResponse> {
|
|
52
|
+
const fetcher = req.fetcher ?? fetch;
|
|
53
|
+
const resp = await fetcher(`${baseUrl(req.keeperUrl)}/prove/release-vendor`, {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
headers: { 'Content-Type': 'application/json' },
|
|
56
|
+
signal: req.signal,
|
|
57
|
+
body: JSON.stringify({
|
|
58
|
+
licenseHash: req.licenseHash,
|
|
59
|
+
zkAppAddress: req.zkAppAddress,
|
|
60
|
+
senderPublicKey: req.senderPublicKey,
|
|
61
|
+
...(req.network ? { network: req.network } : {}),
|
|
62
|
+
}),
|
|
63
|
+
});
|
|
64
|
+
if (!resp.ok) {
|
|
65
|
+
const parsed = await readErrorBody(resp);
|
|
66
|
+
throw keeperError(parsed);
|
|
67
|
+
}
|
|
68
|
+
return await resp.json() as ProveVendorReleaseResponse;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type ConfirmVendorReleaseRequest = {
|
|
72
|
+
keeperUrl: string;
|
|
73
|
+
licenseHash: string;
|
|
74
|
+
zkAppAddress: string;
|
|
75
|
+
// Tx hash returned by the wallet after broadcast. The service polls
|
|
76
|
+
// bestChain for this hash and flips `record.released` on sight.
|
|
77
|
+
txHash: string;
|
|
78
|
+
fetcher?: typeof fetch;
|
|
79
|
+
signal?: AbortSignal;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export type ConfirmVendorReleaseResponse = { ok: true };
|
|
83
|
+
|
|
84
|
+
export async function confirmVendorRelease(req: ConfirmVendorReleaseRequest): Promise<ConfirmVendorReleaseResponse> {
|
|
85
|
+
const fetcher = req.fetcher ?? fetch;
|
|
86
|
+
const resp = await fetcher(`${baseUrl(req.keeperUrl)}/confirm/release-vendor`, {
|
|
87
|
+
method: 'POST',
|
|
88
|
+
headers: { 'Content-Type': 'application/json' },
|
|
89
|
+
signal: req.signal,
|
|
90
|
+
body: JSON.stringify({
|
|
91
|
+
zkAppAddress: req.zkAppAddress,
|
|
92
|
+
licenseHash: req.licenseHash,
|
|
93
|
+
txHash: req.txHash,
|
|
94
|
+
}),
|
|
95
|
+
});
|
|
96
|
+
if (!resp.ok) {
|
|
97
|
+
const parsed = await readErrorBody(resp);
|
|
98
|
+
throw keeperError(parsed);
|
|
99
|
+
}
|
|
100
|
+
return await resp.json() as ConfirmVendorReleaseResponse;
|
|
101
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
// Copyright (c) 2025-2026 zkLicensing project developers
|
|
2
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* renewClient.ts
|
|
6
|
+
*
|
|
7
|
+
* Browser-safe client for the renew flow. Mirrors `buyClient.ts` in shape:
|
|
8
|
+
* the caller drives the wallet step between `proveRenew` and `confirmRenew`
|
|
9
|
+
* so the SDK stays wallet-agnostic.
|
|
10
|
+
*
|
|
11
|
+
* Unlike buy, renew carries a legally load-bearing `waiverAccepted` flag —
|
|
12
|
+
* EU/UK CRD Article 16(m) extinguishes the 14-day right of withdrawal only
|
|
13
|
+
* on the buyer's express affirmative act (consent to immediate delivery +
|
|
14
|
+
* acknowledgment of loss of withdrawal). We refuse the /prove/renew call
|
|
15
|
+
* when the flag is false to keep third-party integrators from silently
|
|
16
|
+
* shipping a UI that omits the waiver gate.
|
|
17
|
+
*
|
|
18
|
+
* Also refused pre-network when the backend's sales-freeze schedule has
|
|
19
|
+
* fired (salesFrozen === true) — the backend would 423 the request anyway,
|
|
20
|
+
* so surfacing the specific message client-side avoids a wasted round-trip.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import type { PurchaseReceipt } from './receipt.js';
|
|
24
|
+
import { resolveFreezeState } from './freezeClient.js';
|
|
25
|
+
import { readErrorBody, tryQueueBusyFromBody, keeperError } from './proveErrors.js';
|
|
26
|
+
|
|
27
|
+
export type ProveRenewRequest = {
|
|
28
|
+
// Standing consent (zklicensing/agent/actions) when an AI agent gives the
|
|
29
|
+
// withdrawal waiver for its owner.
|
|
30
|
+
agentConsent?: unknown;
|
|
31
|
+
keeperUrl: string;
|
|
32
|
+
licenseHash: string;
|
|
33
|
+
buyerAddress: string;
|
|
34
|
+
zkAppAddress: string;
|
|
35
|
+
// Mina network this renewal targets. Pull from your persisted ProofFile
|
|
36
|
+
// so it always matches the network the original buy was proved against.
|
|
37
|
+
network: 'devnet' | 'mainnet';
|
|
38
|
+
duration: 0 | 1 | 2 | 3; // 0 = Monthly, 1 = Yearly, 2 = 5 Years, 3 = Perpetual
|
|
39
|
+
// Set from the UI's Article 16(m) consent gate. Missing/false raises
|
|
40
|
+
// before the network call — the SDK never proves a renew without it.
|
|
41
|
+
waiverAccepted: boolean;
|
|
42
|
+
// Optional freeze-slot override. When omitted the SDK reads
|
|
43
|
+
// `process.env.FREEZE_AFTER_SLOT` (Node only), else GET /health.
|
|
44
|
+
freezeAfterSlot?: number | null;
|
|
45
|
+
fetcher?: typeof fetch;
|
|
46
|
+
signal?: AbortSignal;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type ProveRenewResponse = {
|
|
50
|
+
provenTxJson: string;
|
|
51
|
+
newExpirySlot: number;
|
|
52
|
+
licenseHash: string;
|
|
53
|
+
renewAmount: string; // nanomina
|
|
54
|
+
// Deployment generation the backend proved against. If the buyer's stored
|
|
55
|
+
// ProofFile.generation is smaller, renew.ts walks ancestors and calls
|
|
56
|
+
// migrateLicense on each hop before submitting the renew.
|
|
57
|
+
generation: number;
|
|
58
|
+
// Key the backend stored the per-app in-flight slot under. Passed back to
|
|
59
|
+
// POST /tx-broadcast after the wallet returns a hash. See ProveBuyResponse.
|
|
60
|
+
appKey: string;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type ConfirmRenewRequest = {
|
|
64
|
+
keeperUrl: string;
|
|
65
|
+
zkAppAddress: string;
|
|
66
|
+
licenseHash: string;
|
|
67
|
+
txHash: string;
|
|
68
|
+
fetcher?: typeof fetch;
|
|
69
|
+
signal?: AbortSignal;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
function baseUrl(url: string): string {
|
|
73
|
+
return url.replace(/\/+$/, '');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function readJsonError(resp: Response): Promise<string> {
|
|
77
|
+
try {
|
|
78
|
+
const body = await resp.json() as Record<string, unknown>;
|
|
79
|
+
if (typeof body.error === 'string') return body.error;
|
|
80
|
+
} catch { /* body wasn't JSON */ }
|
|
81
|
+
return `HTTP ${resp.status}`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// POST /prove/renew — the backend compiles + proves the renew tx.
|
|
85
|
+
// Rejects locally when `waiverAccepted` is false; a network call in that
|
|
86
|
+
// state would be wasted work and would give the caller no signal that the
|
|
87
|
+
// legal precondition was missing. Also rejects when the backend's freeze
|
|
88
|
+
// has fired — same reasoning.
|
|
89
|
+
export async function proveRenew(req: ProveRenewRequest): Promise<ProveRenewResponse> {
|
|
90
|
+
if (req.waiverAccepted !== true) {
|
|
91
|
+
throw new Error('waiverAccepted must be true — Article 16(m) waiver required to renew');
|
|
92
|
+
}
|
|
93
|
+
const fetcher = req.fetcher ?? fetch;
|
|
94
|
+
const freeze = await resolveFreezeState({
|
|
95
|
+
keeperUrl: req.keeperUrl,
|
|
96
|
+
network: req.network,
|
|
97
|
+
freezeAfterSlot: req.freezeAfterSlot,
|
|
98
|
+
fetcher,
|
|
99
|
+
signal: req.signal,
|
|
100
|
+
});
|
|
101
|
+
if (freeze.salesFrozen) {
|
|
102
|
+
throw new Error('Sales frozen — new renewals refused ahead of scheduled hardfork migration');
|
|
103
|
+
}
|
|
104
|
+
const resp = await fetcher(`${baseUrl(req.keeperUrl)}/prove/renew`, {
|
|
105
|
+
method: 'POST',
|
|
106
|
+
headers: { 'Content-Type': 'application/json' },
|
|
107
|
+
signal: req.signal,
|
|
108
|
+
body: JSON.stringify({
|
|
109
|
+
licenseHash: req.licenseHash,
|
|
110
|
+
buyerAddress: req.buyerAddress,
|
|
111
|
+
zkAppAddress: req.zkAppAddress,
|
|
112
|
+
network: req.network,
|
|
113
|
+
duration: req.duration,
|
|
114
|
+
...(req.agentConsent ? { agentConsent: req.agentConsent } : {}),
|
|
115
|
+
}),
|
|
116
|
+
});
|
|
117
|
+
if (!resp.ok) {
|
|
118
|
+
const parsed = await readErrorBody(resp);
|
|
119
|
+
const queueBusy = tryQueueBusyFromBody(parsed);
|
|
120
|
+
if (queueBusy) throw queueBusy;
|
|
121
|
+
throw keeperError(parsed);
|
|
122
|
+
}
|
|
123
|
+
return await resp.json() as ProveRenewResponse;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// POST /confirm/renew — promotes the in-memory renew intent to a pending
|
|
127
|
+
// LicenseRecord. Call this only after the wallet returns a txHash.
|
|
128
|
+
/** Returns the buyer's receipt for the renewal when the keeper issued one (put it in the license file as `receipt`). */
|
|
129
|
+
export async function confirmRenew(req: ConfirmRenewRequest): Promise<{ ok: true; receipt?: PurchaseReceipt }> {
|
|
130
|
+
const fetcher = req.fetcher ?? fetch;
|
|
131
|
+
const resp = await fetcher(`${baseUrl(req.keeperUrl)}/confirm/renew`, {
|
|
132
|
+
method: 'POST',
|
|
133
|
+
headers: { 'Content-Type': 'application/json' },
|
|
134
|
+
signal: req.signal,
|
|
135
|
+
body: JSON.stringify({
|
|
136
|
+
zkAppAddress: req.zkAppAddress,
|
|
137
|
+
licenseHash: req.licenseHash,
|
|
138
|
+
txHash: req.txHash,
|
|
139
|
+
}),
|
|
140
|
+
});
|
|
141
|
+
if (!resp.ok) throw new Error(await readJsonError(resp));
|
|
142
|
+
const body = await resp.json().catch(() => ({})) as { receipt?: PurchaseReceipt };
|
|
143
|
+
return { ok: true, ...(body?.receipt ? { receipt: body.receipt } : {}) };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export type RenewStatus = {
|
|
147
|
+
// 'confirmed' once the eventSync sweep has observed the licenseRenewed event
|
|
148
|
+
// on-chain and cleared the pending flag with the new expiry in place.
|
|
149
|
+
// 'pending' while the tx is in flight. 'unknown' if the license record
|
|
150
|
+
// hasn't been persisted yet (confirmRenew hasn't run or the intent expired).
|
|
151
|
+
// 'failed' if the keeper saw the transaction included but failed on chain
|
|
152
|
+
// (failureReason says why): the license is as it was; terminal.
|
|
153
|
+
status: 'confirmed' | 'pending' | 'unknown' | 'failed';
|
|
154
|
+
failureReason?: string;
|
|
155
|
+
expirySlot?: number;
|
|
156
|
+
txHash?: string | null;
|
|
157
|
+
elapsedMs?: number;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export type PollRenewStatusOptions = {
|
|
161
|
+
keeperUrl: string;
|
|
162
|
+
network: 'devnet' | 'mainnet';
|
|
163
|
+
zkAppAddress: string;
|
|
164
|
+
licenseHash: string;
|
|
165
|
+
// The expected new expiry (from ProveRenewResponse). Confirmation requires
|
|
166
|
+
// BOTH pending=false AND the on-chain record's expirySlot matching or
|
|
167
|
+
// exceeding this — the pending flag alone flips before the sweep, so
|
|
168
|
+
// matching against the expected slot avoids reporting confirmed on the
|
|
169
|
+
// stale pre-renew record.
|
|
170
|
+
newExpirySlot: number;
|
|
171
|
+
fetcher?: typeof fetch;
|
|
172
|
+
// Mina slots are ~90 s and a tx usually lands within a few slots, so a
|
|
173
|
+
// 10 s tick keeps the UI live without hammering the backend.
|
|
174
|
+
intervalMs?: number; // default 10 000
|
|
175
|
+
timeoutMs?: number; // default 20 min
|
|
176
|
+
signal?: AbortSignal;
|
|
177
|
+
onTick?: (status: RenewStatus) => void;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// Polls GET /license/:zkAppAddress/:licenseHash until the renew has landed
|
|
181
|
+
// on-chain (pending flipped and new expiry recorded) or the timeout fires.
|
|
182
|
+
// Mirrors pollBuyStatus, except the terminal condition is Mina-side rather
|
|
183
|
+
// than a backend-side buy-watch — renew has no dedicated watch map.
|
|
184
|
+
export async function pollRenewStatus(opts: PollRenewStatusOptions): Promise<RenewStatus> {
|
|
185
|
+
const fetcher = opts.fetcher ?? fetch;
|
|
186
|
+
const interval = opts.intervalMs ?? 10_000;
|
|
187
|
+
const timeout = opts.timeoutMs ?? 20 * 60 * 1000;
|
|
188
|
+
const startedAt = Date.now();
|
|
189
|
+
const deadline = startedAt + timeout;
|
|
190
|
+
const url = `${baseUrl(opts.keeperUrl)}/license/${encodeURIComponent(opts.zkAppAddress)}/${encodeURIComponent(opts.licenseHash)}?network=${opts.network}`;
|
|
191
|
+
|
|
192
|
+
while (true) {
|
|
193
|
+
if (opts.signal?.aborted) throw new Error('pollRenewStatus aborted');
|
|
194
|
+
let status: RenewStatus = { status: 'unknown', elapsedMs: Date.now() - startedAt };
|
|
195
|
+
try {
|
|
196
|
+
const resp = await fetcher(url, { signal: opts.signal });
|
|
197
|
+
if (resp.ok) {
|
|
198
|
+
const body = await resp.json() as {
|
|
199
|
+
found?: boolean; expirySlot?: number; pending?: boolean; txHash?: string | null;
|
|
200
|
+
};
|
|
201
|
+
if (!body.found) {
|
|
202
|
+
status = { status: 'unknown', elapsedMs: Date.now() - startedAt };
|
|
203
|
+
} else if (body.pending === false && (body.expirySlot ?? 0) >= opts.newExpirySlot) {
|
|
204
|
+
status = {
|
|
205
|
+
status: 'confirmed',
|
|
206
|
+
expirySlot: body.expirySlot,
|
|
207
|
+
txHash: body.txHash ?? null,
|
|
208
|
+
elapsedMs: Date.now() - startedAt,
|
|
209
|
+
};
|
|
210
|
+
} else {
|
|
211
|
+
status = {
|
|
212
|
+
status: 'pending',
|
|
213
|
+
expirySlot: body.expirySlot,
|
|
214
|
+
txHash: body.txHash ?? null,
|
|
215
|
+
elapsedMs: Date.now() - startedAt,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
} catch { /* transient network — try again on next tick */ }
|
|
220
|
+
// Not confirmed yet: the keeper's landing watch says whether the
|
|
221
|
+
// transaction failed on chain (a keeper without the route: 404, ignored).
|
|
222
|
+
if (status.status !== 'confirmed') {
|
|
223
|
+
try {
|
|
224
|
+
const w = await fetcher(`${baseUrl(opts.keeperUrl)}/renew/status?licenseHash=${encodeURIComponent(opts.licenseHash)}`, { signal: opts.signal });
|
|
225
|
+
const watch = w.ok ? await w.json() as { status?: string; failureReason?: string } : null;
|
|
226
|
+
if (watch?.status === 'failed') status = { ...status, status: 'failed', ...(watch.failureReason ? { failureReason: watch.failureReason } : {}) };
|
|
227
|
+
} catch { /* transient — try again on next tick */ }
|
|
228
|
+
}
|
|
229
|
+
opts.onTick?.(status);
|
|
230
|
+
if (status.status === 'confirmed' || status.status === 'failed') return status;
|
|
231
|
+
if (Date.now() >= deadline) return status;
|
|
232
|
+
await new Promise<void>((r) => setTimeout(r, interval));
|
|
233
|
+
}
|
|
234
|
+
}
|
package/src/seatName.ts
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// Copyright (c) 2025-2026 zkLicensing project developers
|
|
2
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* seatName.ts
|
|
6
|
+
*
|
|
7
|
+
* Human-readable device names for the device-binding slot list
|
|
8
|
+
* ("Silent Otter", "Curious Panda"). Meant for the SDK UI so a buyer
|
|
9
|
+
* looking at their bound-devices panel sees "the laptop I left in the
|
|
10
|
+
* office" instead of an opaque device commitment.
|
|
11
|
+
*
|
|
12
|
+
* The name is purely cosmetic — the on-chain circuit never gates on it,
|
|
13
|
+
* verifyOffline never inspects it, and unbind targets `slotIndex`, not
|
|
14
|
+
* name. It exists solely so the SDK's device-manager UI has something
|
|
15
|
+
* readable to render next to each slot.
|
|
16
|
+
*
|
|
17
|
+
* Names are NOT persisted on-chain and NOT persisted off-chain either —
|
|
18
|
+
* `seatNameFor` derives the label deterministically from three on-chain
|
|
19
|
+
* fields (`licenseHash`, `slotIndex`, `boundAtSlot`), so every viewer of
|
|
20
|
+
* the same license computes the same name for a given occupied slot with
|
|
21
|
+
* no naming sidecar and no stored state.
|
|
22
|
+
*
|
|
23
|
+
* The wordlists are intentionally boring: no scary animals, no negative
|
|
24
|
+
* adjectives, no cultural landmines, no words with awkward secondary
|
|
25
|
+
* meanings. Order and word choice are stable — changing them shifts every
|
|
26
|
+
* slot's name on the next mint, which would be jarring for users looking
|
|
27
|
+
* at their device list.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
// 250 friendly adjectives. Short, common, unambiguously positive or
|
|
31
|
+
// neutral. Ordering is alphabetical for maintainability — the derivation
|
|
32
|
+
// is index-based so order does not affect behavior.
|
|
33
|
+
const ADJECTIVES: readonly string[] = [
|
|
34
|
+
'Agile', 'Alert', 'Amber', 'Ample', 'Ancient', 'Apricot', 'Arctic', 'Ashen',
|
|
35
|
+
'Aspen', 'Autumn', 'Azure', 'Balmy', 'Bashful', 'Beaming', 'Beige', 'Bold',
|
|
36
|
+
'Bouncy', 'Brave', 'Breezy', 'Bright', 'Brisk', 'Bronze', 'Bubbly', 'Busy',
|
|
37
|
+
'Calm', 'Candid', 'Cedar', 'Cheery', 'Chic', 'Chipper', 'Chunky', 'Classic',
|
|
38
|
+
'Clean', 'Clever', 'Cloudy', 'Clumsy', 'Coastal', 'Comet', 'Cool', 'Copper',
|
|
39
|
+
'Coral', 'Cosmic', 'Cosy', 'Crafty', 'Creamy', 'Crimson', 'Crisp', 'Cuddly',
|
|
40
|
+
'Curious', 'Daisy', 'Dandy', 'Dapper', 'Daring', 'Dawn', 'Deft', 'Delta',
|
|
41
|
+
'Devout', 'Dewy', 'Diamond', 'Doughty', 'Dreamy', 'Dusky', 'Dusty', 'Eager',
|
|
42
|
+
'Earnest', 'Earthy', 'Easy', 'Ebony', 'Elder', 'Elm', 'Ember', 'Emerald',
|
|
43
|
+
'Epic', 'Fabled', 'Faint', 'Fair', 'Fancy', 'Fearless', 'Feisty', 'Fern',
|
|
44
|
+
'Fiery', 'Fine', 'Firm', 'Flaxen', 'Fleet', 'Floral', 'Fluffy', 'Foggy',
|
|
45
|
+
'Fond', 'Forest', 'Frank', 'Freckled', 'Friendly', 'Frosty', 'Frugal', 'Furry',
|
|
46
|
+
'Fuzzy', 'Gallant', 'Genial', 'Gentle', 'Gilded', 'Ginger', 'Glad', 'Glassy',
|
|
47
|
+
'Gleaming', 'Glossy', 'Glowing', 'Golden', 'Graceful', 'Grand', 'Grassy',
|
|
48
|
+
'Green', 'Handy', 'Happy', 'Hardy', 'Hazel', 'Hearty', 'Helpful', 'Honest',
|
|
49
|
+
'Humble', 'Icy', 'Indigo', 'Inky', 'Iron', 'Ivory', 'Jade', 'Jaunty', 'Jazzy',
|
|
50
|
+
'Jolly', 'Jovial', 'Joyful', 'Juniper', 'Keen', 'Kind', 'Lanky', 'Lark',
|
|
51
|
+
'Lavender', 'Lazy', 'Leaf', 'Lilac', 'Limber', 'Lively', 'Lofty', 'Loyal',
|
|
52
|
+
'Lucky', 'Lush', 'Marble', 'Matte', 'Meadow', 'Mellow', 'Merry', 'Mighty',
|
|
53
|
+
'Milky', 'Mindful', 'Minty', 'Misty', 'Modest', 'Mossy', 'Nautical', 'Nectar',
|
|
54
|
+
'Nifty', 'Nimble', 'Noble', 'Nutmeg', 'Oaken', 'Ochre', 'Olive', 'Onyx',
|
|
55
|
+
'Opal', 'Orange', 'Orchid', 'Pale', 'Patient', 'Peachy', 'Pearl', 'Pebble',
|
|
56
|
+
'Perky', 'Piney', 'Placid', 'Playful', 'Plucky', 'Plum', 'Plush', 'Polar',
|
|
57
|
+
'Polite', 'Prairie', 'Proud', 'Prudent', 'Punchy', 'Purple', 'Quaint', 'Quick',
|
|
58
|
+
'Quiet', 'Radiant', 'Rainy', 'Rapid', 'Rare', 'Ready', 'Ripe', 'River',
|
|
59
|
+
'Rocky', 'Rosy', 'Royal', 'Ruby', 'Rugged', 'Rustic', 'Saffron', 'Sage',
|
|
60
|
+
'Sandy', 'Sapphire', 'Scarlet', 'Serene', 'Shady', 'Sharp', 'Shy', 'Silent',
|
|
61
|
+
'Silken', 'Silky', 'Silver', 'Sincere', 'Sleek', 'Sleepy', 'Slender', 'Small',
|
|
62
|
+
'Smart', 'Smiling', 'Snowy', 'Solar', 'Solid', 'Sonic', 'Soulful', 'Speedy',
|
|
63
|
+
'Spirited', 'Splendid', 'Sprightly', 'Spry', 'Starry', 'Steady', 'Stellar',
|
|
64
|
+
'Sterling', 'Stormy', 'Stout', 'Sunny', 'Sunset', 'Swift', 'Tangy', 'Teal',
|
|
65
|
+
'Tender', 'Thankful', 'Thoughtful', 'Thrifty', 'Tidy', 'Timber', 'Tiny',
|
|
66
|
+
'Topaz', 'Tranquil', 'Trusty', 'Turquoise', 'Twilight', 'Upright', 'Valiant',
|
|
67
|
+
'Velvet', 'Verdant', 'Violet', 'Vivid', 'Warm', 'Whimsical', 'Willow',
|
|
68
|
+
'Windy', 'Winsome', 'Wise', 'Witty', 'Woodland', 'Woolly', 'Zealous', 'Zesty',
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
// 250 friendly animals. Common, recognisable, unambiguously non-threatening
|
|
72
|
+
// (no predators-of-humans, no venomous ones, no polarising taxidermy). Two-
|
|
73
|
+
// word species (mountain goat, sea otter) are collapsed to the base name so
|
|
74
|
+
// the final "Adjective Animal" reads cleanly. Ordering is alphabetical.
|
|
75
|
+
const ANIMALS: readonly string[] = [
|
|
76
|
+
'Alpaca', 'Anteater', 'Antelope', 'Ape', 'Armadillo', 'Auk', 'Aurochs',
|
|
77
|
+
'Axolotl', 'Baboon', 'Badger', 'Barnacle', 'Bat', 'Beagle', 'Bear', 'Beaver',
|
|
78
|
+
'Bee', 'Beetle', 'Bison', 'Blackbird', 'Bluebird', 'Boar', 'Bobcat', 'Bonobo',
|
|
79
|
+
'Booby', 'Buffalo', 'Bulldog', 'Bullfinch', 'Bumblebee', 'Butterfly', 'Camel',
|
|
80
|
+
'Capybara', 'Cardinal', 'Caribou', 'Cassowary', 'Cat', 'Caterpillar',
|
|
81
|
+
'Catfish', 'Chameleon', 'Chamois', 'Cheetah', 'Chickadee', 'Chinchilla',
|
|
82
|
+
'Chipmunk', 'Clam', 'Clownfish', 'Coati', 'Cobra', 'Cockatoo', 'Codfish',
|
|
83
|
+
'Colt', 'Condor', 'Coot', 'Cormorant', 'Corgi', 'Cougar', 'Cow', 'Coyote',
|
|
84
|
+
'Crab', 'Crane', 'Cricket', 'Crow', 'Cub', 'Cuckoo', 'Curlew', 'Deer',
|
|
85
|
+
'Dingo', 'Dolphin', 'Donkey', 'Dormouse', 'Dove', 'Dragonfly', 'Drake',
|
|
86
|
+
'Duck', 'Duckling', 'Eagle', 'Echidna', 'Eel', 'Egret', 'Elephant', 'Elk',
|
|
87
|
+
'Emu', 'Falcon', 'Fawn', 'Ferret', 'Finch', 'Firefly', 'Fish', 'Flamingo',
|
|
88
|
+
'Fox', 'Frog', 'Gecko', 'Gerbil', 'Gibbon', 'Giraffe', 'Gnu', 'Goat',
|
|
89
|
+
'Godwit', 'Goldfinch', 'Goldfish', 'Goose', 'Gorilla', 'Grebe', 'Griffon',
|
|
90
|
+
'Grouse', 'Guanaco', 'Guineafowl', 'Gull', 'Guppy', 'Hamster', 'Hare',
|
|
91
|
+
'Harrier', 'Hawk', 'Hedgehog', 'Heron', 'Herring', 'Hippo', 'Hoatzin',
|
|
92
|
+
'Hoopoe', 'Hornbill', 'Horse', 'Hound', 'Hummingbird', 'Ibex', 'Ibis',
|
|
93
|
+
'Iguana', 'Impala', 'Jackal', 'Jackrabbit', 'Jaguar', 'Jay', 'Jellyfish',
|
|
94
|
+
'Kakapo', 'Kangaroo', 'Kestrel', 'Kingfisher', 'Kinkajou', 'Kite', 'Kitten',
|
|
95
|
+
'Kiwi', 'Koala', 'Kookaburra', 'Ladybug', 'Lamb', 'Lapwing', 'Lark',
|
|
96
|
+
'Lemming', 'Lemur', 'Leopard', 'Lion', 'Lizard', 'Llama', 'Lobster',
|
|
97
|
+
'Lorikeet', 'Loris', 'Lynx', 'Macaque', 'Macaw', 'Magpie', 'Mallard',
|
|
98
|
+
'Manatee', 'Mandrill', 'Mantis', 'Marmoset', 'Marmot', 'Marten', 'Meerkat',
|
|
99
|
+
'Mink', 'Minnow', 'Mole', 'Mongoose', 'Monkey', 'Moose', 'Moth', 'Mouse',
|
|
100
|
+
'Mule', 'Muntjac', 'Muskrat', 'Narwhal', 'Newt', 'Nightingale', 'Numbat',
|
|
101
|
+
'Nuthatch', 'Ocelot', 'Octopus', 'Okapi', 'Opossum', 'Orangutan', 'Oriole',
|
|
102
|
+
'Ostrich', 'Otter', 'Owl', 'Ox', 'Oyster', 'Panda', 'Pangolin', 'Panther',
|
|
103
|
+
'Parakeet', 'Parrot', 'Partridge', 'Peacock', 'Pelican', 'Penguin', 'Perch',
|
|
104
|
+
'Pheasant', 'Pig', 'Pigeon', 'Piglet', 'Pika', 'Pipit', 'Platypus', 'Plover',
|
|
105
|
+
'Pointer', 'Polecat', 'Pony', 'Porcupine', 'Porpoise', 'Possum', 'Prawn',
|
|
106
|
+
'Ptarmigan', 'Puffin', 'Puma', 'Pup', 'Puppy', 'Quail', 'Quokka', 'Rabbit',
|
|
107
|
+
'Raccoon', 'Ram', 'Rat', 'Raven', 'Reindeer', 'Rhino', 'Roadrunner', 'Robin',
|
|
108
|
+
'Rook', 'Salamander', 'Salmon', 'Sandpiper', 'Sardine', 'Scallop', 'Seahorse',
|
|
109
|
+
'Seal', 'Serval', 'Shrew', 'Shrimp', 'Skunk', 'Sloth', 'Snail', 'Snake',
|
|
110
|
+
'Snipe', 'Sparrow', 'Spider', 'Squid', 'Squirrel', 'Starling', 'Stoat',
|
|
111
|
+
'Stork', 'Sturgeon', 'Swallow', 'Swan', 'Tamarin', 'Tanager', 'Tapir',
|
|
112
|
+
'Tarsier', 'Teal', 'Tern', 'Terrier', 'Thrush', 'Tiger', 'Toad', 'Toucan',
|
|
113
|
+
'Trout', 'Tuna', 'Turkey', 'Turtle', 'Vole', 'Wagtail', 'Walrus', 'Wapiti',
|
|
114
|
+
'Warbler', 'Wasp', 'Weasel', 'Whale', 'Whippet', 'Wolf', 'Wolverine',
|
|
115
|
+
'Wombat', 'Woodchuck', 'Woodpecker', 'Wren', 'Yak', 'Zebra',
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
// Total combinations = ADJECTIVES.length * ANIMALS.length. Exposed for
|
|
119
|
+
// diagnostics and for tests that want to assert the name space is large
|
|
120
|
+
// enough to cover the expected ceiling of licenses × cap.
|
|
121
|
+
export const SEAT_NAME_COMBINATIONS: number = ADJECTIVES.length * ANIMALS.length;
|
|
122
|
+
|
|
123
|
+
// FNV-1a — a fast, non-cryptographic 32-bit hash. Uniform enough over 250
|
|
124
|
+
// buckets that the modulo bias (~1 in 17M) is invisible in practice. Not
|
|
125
|
+
// used for security; just to derive a stable pair of small indices from
|
|
126
|
+
// the seat's on-chain identity fields.
|
|
127
|
+
function fnv1a(s: string, seed: number): number {
|
|
128
|
+
let h = seed >>> 0;
|
|
129
|
+
for (let i = 0; i < s.length; i++) {
|
|
130
|
+
h ^= s.charCodeAt(i);
|
|
131
|
+
h = Math.imul(h, 0x01000193);
|
|
132
|
+
}
|
|
133
|
+
return h >>> 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Deterministic seat-name derivation. Two viewers holding the same
|
|
137
|
+
// `LicenseLeaf` compute the same name for a given occupied slot, without
|
|
138
|
+
// a naming sidecar or any persisted state — the function is a pure fold
|
|
139
|
+
// over three on-chain fields.
|
|
140
|
+
//
|
|
141
|
+
// Key composition — `licenseHash + slotIndex + boundAtSlot`:
|
|
142
|
+
// - `licenseHash` scopes names to a single license, so slot 0 on
|
|
143
|
+
// license A doesn't collide with slot 0 on license B.
|
|
144
|
+
// - `slotIndex` separates concurrent occupants of the same license.
|
|
145
|
+
// - `boundAtSlot` binds the name to the device-instance in that slot at
|
|
146
|
+
// that bind moment. Unbind + rebind at the same slotIndex produces
|
|
147
|
+
// a fresh name, matching the mental model of "the laptop I bound in
|
|
148
|
+
// March" rather than "seat 0 forever".
|
|
149
|
+
//
|
|
150
|
+
// Empty slots (`boundAtSlot === 0`) are the caller's responsibility to
|
|
151
|
+
// filter — the function returns a valid string for any input rather than
|
|
152
|
+
// throwing, to keep the surface pure. Match on `deviceCommitment !== '0'`
|
|
153
|
+
// or `boundAtSlot > 0` before calling.
|
|
154
|
+
export type SeatNameInput = {
|
|
155
|
+
licenseHash: string; // Poseidon-hash decimal string from ProofFile / LicenseRecord
|
|
156
|
+
slotIndex: number; // 0..deviceCap-1
|
|
157
|
+
boundAtSlot: number; // Mina slot the bind() tx landed at
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export function seatNameFor(input: SeatNameInput): string {
|
|
161
|
+
const key = `${input.licenseHash}|${input.slotIndex}|${input.boundAtSlot}`;
|
|
162
|
+
const adj = ADJECTIVES[fnv1a(key, 0x811c9dc5) % ADJECTIVES.length];
|
|
163
|
+
const animal = ANIMALS[ fnv1a(key, 0xdeadbeef) % ANIMALS.length];
|
|
164
|
+
return `${adj} ${animal}`;
|
|
165
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Copyright (c) 2025-2026 zkLicensing project developers
|
|
2
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Slot math of the licensing contract (90 s per slot after the Mesa
|
|
6
|
+
* upgrade), as plain numbers. No o1js: the package root imports these, and
|
|
7
|
+
* o1js is an optional peer dependency (README, Publishing to npm).
|
|
8
|
+
* contractInterface re-exports them.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const MONTHLY_SLOTS_N = 28_800; // 30 days × 24 × 40
|
|
12
|
+
export const ONE_YEAR_SLOTS_N = 350_640; // 365.25 days × 24 × 40 (Julian year)
|
|
13
|
+
export const FIVE_YEAR_SLOTS_N = 1_753_200; // 5 × ONE_YEAR_SLOTS_N
|
|
14
|
+
export const PERPETUAL_SLOTS_N = 350_640_000; // 1000 × ONE_YEAR_SLOTS_N ("perpetual — until vendor sunset")
|
|
15
|
+
export const REFUND_WINDOW_N = 13_440;
|
|
16
|
+
export const GRACE_PERIOD_N = 6_720;
|
|
17
|
+
export const MS_PER_SLOT_N = 90_000; // Mesa slot duration in ms
|
|
18
|
+
export const SLOTS_PER_DAY_N = 960; // 86_400_000 / MS_PER_SLOT_N
|