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,517 @@
|
|
|
1
|
+
// Copyright (c) 2025-2026 zkLicensing project developers
|
|
2
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* deployClient.ts
|
|
6
|
+
*
|
|
7
|
+
* Browser-safe client for the deploy flow. Mirrors the buy / renew / refund
|
|
8
|
+
* client shape: `proveDeploy` posts to the backend's `/prove/deploy` endpoint
|
|
9
|
+
* and returns the atomic deploy+initialize tx already signed with the
|
|
10
|
+
* throwaway zkApp key. The vendor's wallet then adds the fee-payer signature
|
|
11
|
+
* and broadcasts.
|
|
12
|
+
*
|
|
13
|
+
* The atomic bundling is what makes single-shot initialization safe:
|
|
14
|
+
* `provenTxJson` holds both `zkApp.deploy()` and `zkApp.initialize()`
|
|
15
|
+
* AccountUpdates in the same transaction, and the backend generates a
|
|
16
|
+
* throwaway zkApp keypair per request that is discarded before the tx is
|
|
17
|
+
* returned — so the target address is not observable to an attacker until
|
|
18
|
+
* the initialize AU inside is already signed and locked to `(vendor,
|
|
19
|
+
* prices)`. Combined with the in-circuit `vendor.requireEquals(empty())`
|
|
20
|
+
* precondition on `initialize()`, this yields a one-shot init per account.
|
|
21
|
+
*
|
|
22
|
+
* Also exports admin-side `redeployApp` / `redeployAll` for the platform
|
|
23
|
+
* operator to trigger post-hardfork v1→v2 migrations via the backend's
|
|
24
|
+
* `/admin/redeploy*` endpoints (bearer-token auth).
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
export type ProveDeployRequest = {
|
|
28
|
+
keeperUrl: string;
|
|
29
|
+
senderPublicKey: string; // vendor's fee-payer wallet
|
|
30
|
+
vendorAddress: string; // owner slot; may equal senderPublicKey
|
|
31
|
+
priceMonthlyMina: number;
|
|
32
|
+
priceYearlyMina: number;
|
|
33
|
+
priceFiveYearMina: number;
|
|
34
|
+
pricePerpetualMina: number;
|
|
35
|
+
// Per-license device cap, 0..5 (0 = binding disabled). Stored immutably in
|
|
36
|
+
// the contract's state at deploy; must match the cap the app is registered
|
|
37
|
+
// with (checked at approval).
|
|
38
|
+
deviceCap: number;
|
|
39
|
+
// Successor-generation deploys pass the announced fork activation slot so
|
|
40
|
+
// migrated licenses whose pre-fork expiry falls before activation get their
|
|
41
|
+
// expiry bumped forward at migration time (7-day grace then runs from
|
|
42
|
+
// activation, preserving renewal stacking). Omit or 0 for first-generation
|
|
43
|
+
// deploys — no ancestor tree, no bump.
|
|
44
|
+
forkActivationSlot?: number;
|
|
45
|
+
// The network to deploy on (the keeper defaults to devnet without it).
|
|
46
|
+
network?: 'devnet' | 'mainnet';
|
|
47
|
+
// The app's https:// website, written into the contract's zkappUri (the
|
|
48
|
+
// fixed half of the vendor's /.well-known trust anchor).
|
|
49
|
+
website?: string;
|
|
50
|
+
// Where the vendor's 98% goes (default: vendorAddress). It never signs;
|
|
51
|
+
// the vendor can change it later, effective after a 48 h delay.
|
|
52
|
+
payoutAddress?: string;
|
|
53
|
+
fetcher?: typeof fetch;
|
|
54
|
+
signal?: AbortSignal;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type ProveDeployResponse = {
|
|
58
|
+
provenTxJson: string;
|
|
59
|
+
verificationKeyHash: string;
|
|
60
|
+
zkAppAddress: string;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The keeper's deploy watch (GET /deploy/status): `landed` once the zkApp
|
|
65
|
+
* account is on chain, `timeout` if the watch gave up waiting, `unknown` for
|
|
66
|
+
* an address it isn't watching.
|
|
67
|
+
*/
|
|
68
|
+
export type DeployStatus = {
|
|
69
|
+
status: 'unknown' | 'pending' | 'landed' | 'timeout';
|
|
70
|
+
landedAtSlot?: number;
|
|
71
|
+
landedAtBlockHeight?: number;
|
|
72
|
+
elapsedMs?: number;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
function baseUrl(url: string): string {
|
|
76
|
+
return url.replace(/\/+$/, '');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function readJsonError(resp: Response): Promise<string> {
|
|
80
|
+
try {
|
|
81
|
+
const body = await resp.json() as Record<string, unknown>;
|
|
82
|
+
if (typeof body.error === 'string') return body.error;
|
|
83
|
+
} catch { /* body wasn't JSON */ }
|
|
84
|
+
return `HTTP ${resp.status}`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// POST /prove/deploy — the backend generates the throwaway zkApp keypair,
|
|
88
|
+
// assembles the deploy+initialize tx, proves it, and signs the zkApp
|
|
89
|
+
// AccountUpdate with the throwaway key. The caller forwards `provenTxJson` to
|
|
90
|
+
// the vendor's wallet for the fee-payer signature.
|
|
91
|
+
//
|
|
92
|
+
// At least one of the four tier prices must be > 0 (enforced by the backend
|
|
93
|
+
// *and* by the circuit's `initialize()`).
|
|
94
|
+
export async function proveDeploy(req: ProveDeployRequest): Promise<ProveDeployResponse> {
|
|
95
|
+
const fetcher = req.fetcher ?? fetch;
|
|
96
|
+
const resp = await fetcher(`${baseUrl(req.keeperUrl)}/prove/deploy`, {
|
|
97
|
+
method: 'POST',
|
|
98
|
+
headers: { 'Content-Type': 'application/json' },
|
|
99
|
+
signal: req.signal,
|
|
100
|
+
body: JSON.stringify({
|
|
101
|
+
senderPublicKey: req.senderPublicKey,
|
|
102
|
+
vendorAddress: req.vendorAddress,
|
|
103
|
+
priceMonthlyMina: req.priceMonthlyMina,
|
|
104
|
+
priceYearlyMina: req.priceYearlyMina,
|
|
105
|
+
priceFiveYearMina: req.priceFiveYearMina,
|
|
106
|
+
pricePerpetualMina: req.pricePerpetualMina,
|
|
107
|
+
deviceCap: req.deviceCap,
|
|
108
|
+
...(req.forkActivationSlot !== undefined ? { forkActivationSlot: req.forkActivationSlot } : {}),
|
|
109
|
+
...(req.network ? { network: req.network } : {}),
|
|
110
|
+
...(req.website ? { website: req.website } : {}),
|
|
111
|
+
...(req.payoutAddress ? { payoutAddress: req.payoutAddress } : {}),
|
|
112
|
+
}),
|
|
113
|
+
});
|
|
114
|
+
if (!resp.ok) throw new Error(await readJsonError(resp));
|
|
115
|
+
return await resp.json() as ProveDeployResponse;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export type PollDeployStatusOptions = {
|
|
119
|
+
keeperUrl: string;
|
|
120
|
+
zkAppAddress: string;
|
|
121
|
+
fetcher?: typeof fetch;
|
|
122
|
+
intervalMs?: number; // default 3 000
|
|
123
|
+
timeoutMs?: number; // default 10 min
|
|
124
|
+
signal?: AbortSignal;
|
|
125
|
+
onTick?: (status: DeployStatus) => void;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Polls GET /deploy/status until it returns a terminal state ('landed' or
|
|
129
|
+
// 'timeout') or the timeout fires. 'unknown' means the backend hasn't seen the
|
|
130
|
+
// tx yet (register step not called or watch not armed).
|
|
131
|
+
export async function pollDeployStatus(opts: PollDeployStatusOptions): Promise<DeployStatus> {
|
|
132
|
+
const fetcher = opts.fetcher ?? fetch;
|
|
133
|
+
const interval = opts.intervalMs ?? 3_000;
|
|
134
|
+
const timeout = opts.timeoutMs ?? 10 * 60 * 1000;
|
|
135
|
+
const deadline = Date.now() + timeout;
|
|
136
|
+
const url = `${baseUrl(opts.keeperUrl)}/deploy/status?zkAppAddress=${encodeURIComponent(opts.zkAppAddress)}`;
|
|
137
|
+
|
|
138
|
+
while (true) {
|
|
139
|
+
if (opts.signal?.aborted) throw new Error('pollDeployStatus aborted');
|
|
140
|
+
let status: DeployStatus = { status: 'unknown' };
|
|
141
|
+
try {
|
|
142
|
+
const resp = await fetcher(url, { signal: opts.signal });
|
|
143
|
+
if (resp.ok) status = await resp.json() as DeployStatus;
|
|
144
|
+
} catch { /* transient network — try again on next tick */ }
|
|
145
|
+
opts.onTick?.(status);
|
|
146
|
+
if (status.status === 'landed' || status.status === 'timeout') return status;
|
|
147
|
+
if (Date.now() >= deadline) return status;
|
|
148
|
+
await new Promise<void>((r) => setTimeout(r, interval));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ─── Admin redeploy ──────────────────────────────────────────────────────────
|
|
153
|
+
//
|
|
154
|
+
// Wraps the backend's `/admin/redeploy*` endpoints. These are platform-operator
|
|
155
|
+
// tools; the backend reads the authoritative vendor/prices from chain. The
|
|
156
|
+
// admin wallet (the platform key; the keeper holds no key) signs the retire
|
|
157
|
+
// and pays: a call answers { step: 'sign-retire', retireMessage } until
|
|
158
|
+
// `retireSignature` is given; with it and `feePayer` (the admin wallet's
|
|
159
|
+
// address) it answers the proved transaction of the app's next step, which
|
|
160
|
+
// the wallet signs and sends, then registerAdminRedeploy. Before a hard fork
|
|
161
|
+
// the step retires the app (`step: 'retire'`); after it, it deploys the
|
|
162
|
+
// successor, which starts with the retired app's licenses (`step: 'deploy'`).
|
|
163
|
+
// Guards enforced by the backend: refund window closed, escrow empty,
|
|
164
|
+
// on-chain version still in supported range. Auth is a platform-admin bearer
|
|
165
|
+
// token.
|
|
166
|
+
|
|
167
|
+
export type RedeployAppRequest = {
|
|
168
|
+
keeperUrl: string;
|
|
169
|
+
appId: string;
|
|
170
|
+
adminToken: string;
|
|
171
|
+
network?: 'devnet' | 'mainnet';
|
|
172
|
+
// The platform key's base58 signature over `retireMessage` from a
|
|
173
|
+
// sign-retire answer, made in the admin wallet (the keeper holds no key).
|
|
174
|
+
retireSignature?: string;
|
|
175
|
+
// The admin wallet's address: it signs, sends and pays for the step.
|
|
176
|
+
feePayer?: string;
|
|
177
|
+
// Retire with purchases still in escrow (their funds stay on the old
|
|
178
|
+
// contract for good); the platform key signs it. Last resort ahead of a fork.
|
|
179
|
+
force?: boolean;
|
|
180
|
+
fetcher?: typeof fetch;
|
|
181
|
+
signal?: AbortSignal;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* A proved redeploy step for the signer's wallet to sign, send and pay
|
|
186
|
+
* (vendor or admin wallet); then register its hash with `redeployId`.
|
|
187
|
+
*/
|
|
188
|
+
export type RedeployTx =
|
|
189
|
+
| { step: 'retire'; provenTxJson: string; redeployId: string; retiredAddress: string; successor: string }
|
|
190
|
+
| { step: 'retireAndDeploy' | 'deploy'; provenTxJson: string; redeployId: string; retiredAddress: string; zkAppAddress: string; verificationKeyHash: string };
|
|
191
|
+
|
|
192
|
+
/** The step a redeploy call took. */
|
|
193
|
+
export type RedeployStepResult =
|
|
194
|
+
| { step: 'sign-retire'; retireMessage: string[]; licensesRoot: string; successor: string; then: 'retire' | 'retireAndDeploy' | 'deploy'; force: boolean }
|
|
195
|
+
| RedeployTx;
|
|
196
|
+
|
|
197
|
+
export type RedeployAppResponse = { ok: true } & RedeployStepResult;
|
|
198
|
+
|
|
199
|
+
export type RedeployAllRequest = {
|
|
200
|
+
keeperUrl: string;
|
|
201
|
+
adminToken: string;
|
|
202
|
+
network?: 'devnet' | 'mainnet';
|
|
203
|
+
fetcher?: typeof fetch;
|
|
204
|
+
signal?: AbortSignal;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
export type RedeployAllResponse = {
|
|
208
|
+
ok: true;
|
|
209
|
+
count: number;
|
|
210
|
+
/** Apps whose retirement gate is up, ready for their next step. */
|
|
211
|
+
ready: number;
|
|
212
|
+
results: Array<
|
|
213
|
+
| { appId: string; ok: true; step: 'retire' | 'retireAndDeploy' | 'deploy'; readyAt: number }
|
|
214
|
+
| { appId: string; ok: false; error: string }
|
|
215
|
+
>;
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
function adminHeaders(token: string): Record<string, string> {
|
|
219
|
+
return {
|
|
220
|
+
'Content-Type': 'application/json',
|
|
221
|
+
'Authorization': `Bearer ${token}`,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// POST /admin/redeploy/:appId — the next redeploy step for a single app.
|
|
226
|
+
// Rejects with a descriptive error if any backend guard fails (409) or the
|
|
227
|
+
// transaction itself throws (500). After a deploy step the returned
|
|
228
|
+
// `zkAppAddress` is the successor's; the old one stays on chain, retired.
|
|
229
|
+
export async function redeployApp(req: RedeployAppRequest): Promise<RedeployAppResponse> {
|
|
230
|
+
const fetcher = req.fetcher ?? fetch;
|
|
231
|
+
const resp = await fetcher(`${baseUrl(req.keeperUrl)}/admin/redeploy/${encodeURIComponent(req.appId)}`, {
|
|
232
|
+
method: 'POST',
|
|
233
|
+
headers: adminHeaders(req.adminToken),
|
|
234
|
+
signal: req.signal,
|
|
235
|
+
body: JSON.stringify({
|
|
236
|
+
...(req.network ? { network: req.network } : {}),
|
|
237
|
+
...(req.retireSignature ? { retireSignature: req.retireSignature } : {}),
|
|
238
|
+
...(req.feePayer ? { feePayer: req.feePayer } : {}),
|
|
239
|
+
...(req.force ? { force: true } : {}),
|
|
240
|
+
}),
|
|
241
|
+
});
|
|
242
|
+
if (!resp.ok) throw new Error(await readJsonError(resp));
|
|
243
|
+
return await resp.json() as RedeployAppResponse;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// POST /admin/redeploy/:appId/register — the admin wallet sent the proved
|
|
247
|
+
// step: a retire is marked on the app; a deploy is watched until it lands,
|
|
248
|
+
// then the app moves to its successor.
|
|
249
|
+
export async function registerAdminRedeploy(req: { keeperUrl: string; appId: string; adminToken: string; redeployId: string; txHash: string; fetcher?: typeof fetch; signal?: AbortSignal }): Promise<{ ok: true; step: 'retire' | 'deploy' }> {
|
|
250
|
+
const resp = await (req.fetcher ?? fetch)(`${baseUrl(req.keeperUrl)}/admin/redeploy/${encodeURIComponent(req.appId)}/register`, {
|
|
251
|
+
method: 'POST', headers: adminHeaders(req.adminToken), signal: req.signal,
|
|
252
|
+
body: JSON.stringify({ redeployId: req.redeployId, txHash: req.txHash }),
|
|
253
|
+
});
|
|
254
|
+
if (!resp.ok) throw new Error(await readJsonError(resp));
|
|
255
|
+
return await resp.json() as { ok: true; step: 'retire' | 'deploy' };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// POST /admin/redeploy-all — raises every eligible app's retirement gate at
|
|
259
|
+
// once (one shared drain) and reports each app's next step; the admin wallet
|
|
260
|
+
// then takes them one app at a time with redeployApp (it signs and pays).
|
|
261
|
+
// Apps that can't be redeployed surface as ok:false with the reason.
|
|
262
|
+
export async function redeployAll(req: RedeployAllRequest): Promise<RedeployAllResponse> {
|
|
263
|
+
const fetcher = req.fetcher ?? fetch;
|
|
264
|
+
const resp = await fetcher(`${baseUrl(req.keeperUrl)}/admin/redeploy-all`, {
|
|
265
|
+
method: 'POST',
|
|
266
|
+
headers: adminHeaders(req.adminToken),
|
|
267
|
+
signal: req.signal,
|
|
268
|
+
body: JSON.stringify(req.network ? { network: req.network } : {}),
|
|
269
|
+
});
|
|
270
|
+
if (!resp.ok) throw new Error(await readJsonError(resp));
|
|
271
|
+
return await resp.json() as RedeployAllResponse;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ─── Vendor-triggered redeploy ───────────────────────────────────────────────
|
|
275
|
+
//
|
|
276
|
+
// Same steps as the admin path, but authenticated by a Mina signature against
|
|
277
|
+
// the on-chain vendor of the target app (no admin token required), and the
|
|
278
|
+
// vendor signs the retire. The vendor's wallet pays for every step (retire
|
|
279
|
+
// and successor). Flow, mirroring first-time /prove/deploy →
|
|
280
|
+
// wallet-broadcast → /apps register:
|
|
281
|
+
//
|
|
282
|
+
// 1. getVendorRedeployChallenge → { nonce, expiresAt }
|
|
283
|
+
// 2. vendor's wallet signs `nonce` → { field, scalar }
|
|
284
|
+
// 3. proveVendorRedeploy → 202 draining (come back after readyAt),
|
|
285
|
+
// 202 sign-retire (sign retireMessage with
|
|
286
|
+
// signFields, call again with
|
|
287
|
+
// retireSignature), then
|
|
288
|
+
// { step, provenTxJson, redeployId, … }
|
|
289
|
+
// (step 'retire' before a fork)
|
|
290
|
+
// 4. wallet adds fee-payer sig + broadcasts (mirrors proveDeploy flow)
|
|
291
|
+
// 5. registerVendorRedeploy → a retire is marked on the app; a
|
|
292
|
+
// deploy arms the backend's
|
|
293
|
+
// deploy-watcher on the new address. On
|
|
294
|
+
// landing, the backend promotes the new
|
|
295
|
+
// deployment onto the AppRecord
|
|
296
|
+
// (deploymentHistory[] etc).
|
|
297
|
+
//
|
|
298
|
+
// Each call to step 3 needs a fresh challenge. Guards (freeze slot
|
|
299
|
+
// configured, refund window closed, escrow empty, active status, supported
|
|
300
|
+
// version) all apply; an app retires only with an empty escrow.
|
|
301
|
+
//
|
|
302
|
+
// ⚠ Vendor-embedded buy links — after redeploy, `proveVendorRedeploy` returns
|
|
303
|
+
// the successor's `zkAppAddress`. Platform-hosted marketplace pages handle this
|
|
304
|
+
// automatically: the marketplace stores your registered `buyUrl`/`renewUrl`/
|
|
305
|
+
// `refundUrl` as base URLs and appends `?app=<zkAppAddress>&network=<net>`
|
|
306
|
+
// at render time, so the "Get" button routes to the new address the
|
|
307
|
+
// moment `finalizeRedeploy` promotes it onto the AppRecord — no vendor
|
|
308
|
+
// action required. Only vendor-hosted surfaces where the raw `zkAppAddress`
|
|
309
|
+
// is hand-embedded (docs, storefront, emails) need manual updating. Existing
|
|
310
|
+
// licenses carry on in the successor under the same hash; the retired
|
|
311
|
+
// address refuses new buys.
|
|
312
|
+
|
|
313
|
+
export type VendorRedeployPreflight = {
|
|
314
|
+
// True iff every guard passes and the redeploy can proceed.
|
|
315
|
+
ok: boolean;
|
|
316
|
+
freezeSlotConfigured: boolean;
|
|
317
|
+
appExists: boolean;
|
|
318
|
+
status?: 'active' | 'inactive' | 'pending';
|
|
319
|
+
refundWindow: { closed: boolean; opensAtSlot: number; currentSlot: number };
|
|
320
|
+
onChainAccount: { exists: boolean };
|
|
321
|
+
// The app retires only with an empty escrow: wait for refunds and releases.
|
|
322
|
+
escrow: { empty: boolean };
|
|
323
|
+
version: { current: number; supported: boolean; supportedList: readonly number[] };
|
|
324
|
+
// Live deployment already runs the keeper's current circuit.
|
|
325
|
+
alreadyCurrent: boolean;
|
|
326
|
+
// The live deployment is retired on chain: its successor is next.
|
|
327
|
+
retired: boolean;
|
|
328
|
+
// The platform is draining the app: new purchases are paused until the escrow is empty.
|
|
329
|
+
draining: { startedAt: string } | null;
|
|
330
|
+
// What the redeploy would do next (null when a guard blocks it).
|
|
331
|
+
step: 'retire' | 'retireAndDeploy' | 'deploy' | null;
|
|
332
|
+
// Set while a redeploy is in progress: the current deployment is read-only
|
|
333
|
+
// (no buys, renewals or device changes) until the successor lands. Before
|
|
334
|
+
// `readyAt` the prove call answers `draining`.
|
|
335
|
+
retirement: { readyAt: number; expiresAt: number } | null;
|
|
336
|
+
// Human-readable reasons for every failing guard, in evaluation order.
|
|
337
|
+
reasons: string[];
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
// GET /vendor/redeploy/:appId/preflight — read-only guard status. No nonce
|
|
341
|
+
// consumed, no signature required. UI decides button state from the returned
|
|
342
|
+
// fields; 404 means the app itself is unknown (a distinct failure mode from a
|
|
343
|
+
// known app failing individual guards).
|
|
344
|
+
export async function fetchVendorRedeployPreflight(opts: {
|
|
345
|
+
keeperUrl: string;
|
|
346
|
+
appId: string;
|
|
347
|
+
network: 'devnet' | 'mainnet';
|
|
348
|
+
fetcher?: typeof fetch;
|
|
349
|
+
signal?: AbortSignal;
|
|
350
|
+
}): Promise<VendorRedeployPreflight> {
|
|
351
|
+
const fetcher = opts.fetcher ?? fetch;
|
|
352
|
+
const resp = await fetcher(`${baseUrl(opts.keeperUrl)}/vendor/redeploy/${encodeURIComponent(opts.appId)}/preflight?network=${opts.network}`, {
|
|
353
|
+
signal: opts.signal,
|
|
354
|
+
});
|
|
355
|
+
if (!resp.ok) throw new Error(await readJsonError(resp));
|
|
356
|
+
return await resp.json() as VendorRedeployPreflight;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export type VendorRedeployChallenge = {
|
|
360
|
+
nonce: string;
|
|
361
|
+
expiresAt: number; // ms epoch
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
// GET /vendor/redeploy/:appId/challenge — mint a one-shot nonce that the
|
|
365
|
+
// vendor's wallet signs. The nonce is bound to the appId and expires in 60s;
|
|
366
|
+
// consumed on /prove.
|
|
367
|
+
export async function getVendorRedeployChallenge(opts: {
|
|
368
|
+
keeperUrl: string;
|
|
369
|
+
appId: string;
|
|
370
|
+
/** The deployment to redeploy (the keeper requires it). */
|
|
371
|
+
network: 'devnet' | 'mainnet';
|
|
372
|
+
fetcher?: typeof fetch;
|
|
373
|
+
signal?: AbortSignal;
|
|
374
|
+
}): Promise<VendorRedeployChallenge> {
|
|
375
|
+
const fetcher = opts.fetcher ?? fetch;
|
|
376
|
+
const resp = await fetcher(`${baseUrl(opts.keeperUrl)}/vendor/redeploy/${encodeURIComponent(opts.appId)}/challenge?network=${opts.network}`, {
|
|
377
|
+
signal: opts.signal,
|
|
378
|
+
});
|
|
379
|
+
if (!resp.ok) throw new Error(await readJsonError(resp));
|
|
380
|
+
return await resp.json() as VendorRedeployChallenge;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
export type ProveVendorRedeployRequest = {
|
|
384
|
+
keeperUrl: string;
|
|
385
|
+
appId: string;
|
|
386
|
+
nonce: string;
|
|
387
|
+
signature: { field: string; scalar: string };
|
|
388
|
+
senderPublicKey: string; // vendor's fee-payer wallet
|
|
389
|
+
// The vendor's base58 signature over `retireMessage` (from the sign-retire
|
|
390
|
+
// answer), made with the wallet's signFields.
|
|
391
|
+
retireSignature?: string;
|
|
392
|
+
fetcher?: typeof fetch;
|
|
393
|
+
signal?: AbortSignal;
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
// The successor's zkApp address (on a deploy step) is where the app moves.
|
|
397
|
+
// Marketplace links auto-update: the platform appends
|
|
398
|
+
// `?app=<zkAppAddress>&network=<net>` to your registered `buyUrl`/`renewUrl`/
|
|
399
|
+
// `refundUrl` at render time, so the "Get" button routes to this new address
|
|
400
|
+
// once `finalizeRedeploy` promotes it onto the AppRecord — no vendor action
|
|
401
|
+
// required. Only vendor-hosted surfaces where the raw address is embedded by
|
|
402
|
+
// hand need manual updating. `redeployId` is the capability token for
|
|
403
|
+
// registerVendorRedeploy.
|
|
404
|
+
export type ProveVendorRedeployResponse = RedeployTx;
|
|
405
|
+
|
|
406
|
+
// Returned (HTTP 202) by the first prove call: the keeper has made the current
|
|
407
|
+
// deployment read-only and is waiting out in-flight proofs before the app can
|
|
408
|
+
// retire. Call proveVendorRedeploy again (fresh challenge + signature) after
|
|
409
|
+
// `readyAt`, before `expiresAt`.
|
|
410
|
+
export type VendorRedeployDraining = {
|
|
411
|
+
status: 'draining';
|
|
412
|
+
readyAt: number; // epoch ms
|
|
413
|
+
expiresAt: number; // epoch ms — the pause lifts if the redeploy isn't finished
|
|
414
|
+
message: string;
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
// Returned (HTTP 202) when the app must retire and no retireSignature was
|
|
418
|
+
// given: sign `retireMessage` (field strings) with the vendor key and call
|
|
419
|
+
// again with `retireSignature`.
|
|
420
|
+
export type VendorRedeploySignRetire = {
|
|
421
|
+
status: 'sign-retire';
|
|
422
|
+
retireMessage: string[];
|
|
423
|
+
licensesRoot: string;
|
|
424
|
+
/** The new zkApp address the retirement names: only it can start from these licenses. */
|
|
425
|
+
successor: string;
|
|
426
|
+
message: string;
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
// POST /vendor/redeploy/:appId/prove — the backend verifies the nonce +
|
|
430
|
+
// signature against the on-chain vendor of the app and runs all redeploy
|
|
431
|
+
// guards, then answers with the next step (see the flow above).
|
|
432
|
+
export async function proveVendorRedeploy(req: ProveVendorRedeployRequest): Promise<
|
|
433
|
+
ProveVendorRedeployResponse | VendorRedeployDraining | VendorRedeploySignRetire
|
|
434
|
+
> {
|
|
435
|
+
const fetcher = req.fetcher ?? fetch;
|
|
436
|
+
const resp = await fetcher(`${baseUrl(req.keeperUrl)}/vendor/redeploy/${encodeURIComponent(req.appId)}/prove`, {
|
|
437
|
+
method: 'POST',
|
|
438
|
+
headers: { 'Content-Type': 'application/json' },
|
|
439
|
+
signal: req.signal,
|
|
440
|
+
body: JSON.stringify({
|
|
441
|
+
nonce: req.nonce,
|
|
442
|
+
signature: req.signature,
|
|
443
|
+
senderPublicKey: req.senderPublicKey,
|
|
444
|
+
...(req.retireSignature ? { retireSignature: req.retireSignature } : {}),
|
|
445
|
+
}),
|
|
446
|
+
});
|
|
447
|
+
if (!resp.ok) throw new Error(await readJsonError(resp));
|
|
448
|
+
return await resp.json() as ProveVendorRedeployResponse | VendorRedeployDraining | VendorRedeploySignRetire;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
export type RegisterVendorRedeployRequest = {
|
|
452
|
+
keeperUrl: string;
|
|
453
|
+
appId: string;
|
|
454
|
+
redeployId: string; // capability token from proveVendorRedeploy
|
|
455
|
+
txHash: string; // returned by the vendor's wallet after broadcast
|
|
456
|
+
fetcher?: typeof fetch;
|
|
457
|
+
signal?: AbortSignal;
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
// POST /vendor/redeploy/:appId/register — arm the backend's deploy-watcher on
|
|
461
|
+
// the new address. On confirmation the backend mutates AppRecord to promote
|
|
462
|
+
// the new deployment (push old to deploymentHistory[], overwrite live fields,
|
|
463
|
+
// clear event-sync cursors). Idempotent to duplicate calls (the watcher
|
|
464
|
+
// checks whether one is already armed for the address). Poll GET
|
|
465
|
+
// /deploy/status?zkAppAddress=<new> — or use pollDeployStatus — for landing
|
|
466
|
+
// progress, same as first-time deploys.
|
|
467
|
+
// POST /vendor/redeploy/:appId/cancel — calls off the vendor's redeploy
|
|
468
|
+
// (gate, drain) before the app is retired on chain. A fresh signed challenge.
|
|
469
|
+
export async function cancelVendorRedeploy(req: { keeperUrl: string; appId: string; nonce: string; signature: { field: string; scalar: string }; fetcher?: typeof fetch; signal?: AbortSignal }): Promise<{ ok: true }> {
|
|
470
|
+
const fetcher = req.fetcher ?? fetch;
|
|
471
|
+
const resp = await fetcher(`${baseUrl(req.keeperUrl)}/vendor/redeploy/${encodeURIComponent(req.appId)}/cancel`, {
|
|
472
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' }, signal: req.signal,
|
|
473
|
+
body: JSON.stringify({ nonce: req.nonce, signature: req.signature }),
|
|
474
|
+
});
|
|
475
|
+
if (!resp.ok) throw new Error(await readJsonError(resp));
|
|
476
|
+
return await resp.json() as { ok: true };
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// POST /admin/redeploy/:appId/drain — the platform starts a redeploy of an
|
|
480
|
+
// app with purchases in escrow: new purchases pause until it is empty.
|
|
481
|
+
export async function startRedeployDrain(req: { keeperUrl: string; appId: string; adminToken: string; network: 'devnet' | 'mainnet'; fetcher?: typeof fetch }): Promise<{ ok: true; draining: { startedAt: string }; alreadyEmpty: boolean }> {
|
|
482
|
+
const resp = await (req.fetcher ?? fetch)(`${baseUrl(req.keeperUrl)}/admin/redeploy/${encodeURIComponent(req.appId)}/drain`, {
|
|
483
|
+
method: 'POST', headers: adminHeaders(req.adminToken), body: JSON.stringify({ network: req.network }),
|
|
484
|
+
});
|
|
485
|
+
if (!resp.ok) throw new Error(await readJsonError(resp));
|
|
486
|
+
return await resp.json() as { ok: true; draining: { startedAt: string }; alreadyEmpty: boolean };
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// POST /admin/redeploy/:appId/cancel — calls off a redeploy or drain not yet retired on chain.
|
|
490
|
+
export async function cancelRedeploy(req: { keeperUrl: string; appId: string; adminToken: string; network: 'devnet' | 'mainnet'; fetcher?: typeof fetch }): Promise<{ ok: true }> {
|
|
491
|
+
const resp = await (req.fetcher ?? fetch)(`${baseUrl(req.keeperUrl)}/admin/redeploy/${encodeURIComponent(req.appId)}/cancel`, {
|
|
492
|
+
method: 'POST', headers: adminHeaders(req.adminToken), body: JSON.stringify({ network: req.network }),
|
|
493
|
+
});
|
|
494
|
+
if (!resp.ok) throw new Error(await readJsonError(resp));
|
|
495
|
+
return await resp.json() as { ok: true };
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// POST /admin/redeploy-notice — ahead of an announced hard fork, emails the vendors of live apps.
|
|
499
|
+
export async function sendRedeployNotices(req: { keeperUrl: string; adminToken: string; network: 'devnet' | 'mainnet'; fetcher?: typeof fetch }): Promise<{ ok: true; notified: string[]; forkActivationSlot: number }> {
|
|
500
|
+
const resp = await (req.fetcher ?? fetch)(`${baseUrl(req.keeperUrl)}/admin/redeploy-notice`, {
|
|
501
|
+
method: 'POST', headers: adminHeaders(req.adminToken), body: JSON.stringify({ network: req.network }),
|
|
502
|
+
});
|
|
503
|
+
if (!resp.ok) throw new Error(await readJsonError(resp));
|
|
504
|
+
return await resp.json() as { ok: true; notified: string[]; forkActivationSlot: number };
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
export async function registerVendorRedeploy(req: RegisterVendorRedeployRequest): Promise<{ ok: true; step: 'retire' | 'deploy' }> {
|
|
508
|
+
const fetcher = req.fetcher ?? fetch;
|
|
509
|
+
const resp = await fetcher(`${baseUrl(req.keeperUrl)}/vendor/redeploy/${encodeURIComponent(req.appId)}/register`, {
|
|
510
|
+
method: 'POST',
|
|
511
|
+
headers: { 'Content-Type': 'application/json' },
|
|
512
|
+
signal: req.signal,
|
|
513
|
+
body: JSON.stringify({ redeployId: req.redeployId, txHash: req.txHash }),
|
|
514
|
+
});
|
|
515
|
+
if (!resp.ok) throw new Error(await readJsonError(resp));
|
|
516
|
+
return await resp.json() as { ok: true; step: 'retire' | 'deploy' };
|
|
517
|
+
}
|