@tokenops/sdk 2.0.0-alpha.1 → 2.0.0-alpha.3

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.
Files changed (155) hide show
  1. package/CHANGELOG.md +67 -2
  2. package/README.md +14 -8
  3. package/dist/{chunk-PUX5VXDB.cjs → chunk-4AWQJQXY.cjs} +1415 -69
  4. package/dist/{chunk-74QFZ5MK.js → chunk-4IB4IY5Y.js} +1 -1
  5. package/dist/{chunk-DZHYSUGY.js → chunk-6HCT4QIX.js} +12 -5
  6. package/dist/{chunk-FGEB7RMY.cjs → chunk-6XOAYDC6.cjs} +4 -4
  7. package/dist/{chunk-XAYGD4E4.cjs → chunk-AHU2HMNE.cjs} +3 -3
  8. package/dist/{chunk-6NAVMWZQ.js → chunk-BPIVVCSQ.js} +2 -2
  9. package/dist/{chunk-GKKDAW44.js → chunk-BXX5TETM.js} +1401 -59
  10. package/dist/{chunk-456VDDA3.js → chunk-GCJFAY4X.js} +78 -25
  11. package/dist/{chunk-CTED3MTR.cjs → chunk-GKYZQJGP.cjs} +82 -29
  12. package/dist/{chunk-JZLGCTGK.cjs → chunk-IEKTGJ4J.cjs} +2 -2
  13. package/dist/{chunk-6PXXGACR.cjs → chunk-JVNJSDQ4.cjs} +2 -2
  14. package/dist/{chunk-O5676ZWY.js → chunk-LEYYG4RF.js} +232 -6
  15. package/dist/{chunk-335Z2W67.js → chunk-NGU4JYIR.js} +1 -1
  16. package/dist/{chunk-5576FRT3.cjs → chunk-PG3WXT3K.cjs} +12 -5
  17. package/dist/{chunk-H5POPGOZ.js → chunk-PK6Q6J5F.js} +1 -1
  18. package/dist/chunk-TND5NDV7.cjs +263 -0
  19. package/dist/chunk-U3TBQRT7.js +253 -0
  20. package/dist/{chunk-EAJ7SYHE.js → chunk-WRWRA4TN.js} +1 -1
  21. package/dist/{chunk-JQYC66TO.cjs → chunk-YABHXJNH.cjs} +5 -5
  22. package/dist/{chunk-M4IT6DNJ.cjs → chunk-YTIYTV4K.cjs} +236 -4
  23. package/dist/core/addresses.d.ts +4 -4
  24. package/dist/core/errors.d.ts +1 -1
  25. package/dist/fhe/types.d.ts +2 -2
  26. package/dist/fhe-airdrop/abis/airdrop-base.d.ts +19 -0
  27. package/dist/fhe-airdrop/abis/ecdsa.d.ts +19 -0
  28. package/dist/fhe-airdrop/abis/factory.d.ts +113 -0
  29. package/dist/fhe-airdrop/abis/merkle.d.ts +43 -12
  30. package/dist/fhe-airdrop/advanced/index.cjs +4 -4
  31. package/dist/fhe-airdrop/advanced/index.js +1 -1
  32. package/dist/fhe-airdrop/advanced/react/index.cjs +620 -9
  33. package/dist/fhe-airdrop/advanced/react/index.d.cts +35 -0
  34. package/dist/fhe-airdrop/advanced/react/index.d.ts +35 -0
  35. package/dist/fhe-airdrop/advanced/react/index.js +590 -7
  36. package/dist/fhe-airdrop/advanced/react/useClearCompliancePolicy.d.ts +28 -0
  37. package/dist/fhe-airdrop/advanced/react/useClearUpgradeabilityPolicy.d.ts +26 -0
  38. package/dist/fhe-airdrop/advanced/react/useDisableCustomFee.d.ts +26 -0
  39. package/dist/fhe-airdrop/advanced/react/useEcdsaInitCodeHash.d.ts +30 -0
  40. package/dist/fhe-airdrop/advanced/react/useFactoryComplianceDelegate.d.ts +25 -0
  41. package/dist/fhe-airdrop/advanced/react/useFactoryCompliancePolicy.d.ts +38 -0
  42. package/dist/fhe-airdrop/advanced/react/useFactoryCustomFee.d.ts +24 -0
  43. package/dist/fhe-airdrop/advanced/react/useFactoryGrantRole.d.ts +32 -0
  44. package/dist/fhe-airdrop/advanced/react/useFactoryHasRole.d.ts +27 -0
  45. package/dist/fhe-airdrop/advanced/react/useFactoryImplementations.d.ts +33 -0
  46. package/dist/fhe-airdrop/advanced/react/useFactoryRenounceRole.d.ts +37 -0
  47. package/dist/fhe-airdrop/advanced/react/useFactoryRevokeRole.d.ts +37 -0
  48. package/dist/fhe-airdrop/advanced/react/useFactoryRoleConstants.d.ts +46 -0
  49. package/dist/fhe-airdrop/advanced/react/useFactoryRoleMembers.d.ts +48 -0
  50. package/dist/fhe-airdrop/advanced/react/useFactoryUpgradeabilityPolicy.d.ts +39 -0
  51. package/dist/fhe-airdrop/advanced/react/useMerkleInitCodeHash.d.ts +24 -0
  52. package/dist/fhe-airdrop/advanced/react/useSetComplianceDelegate.d.ts +33 -0
  53. package/dist/fhe-airdrop/advanced/react/useSetComplianceManagerImpl.d.ts +27 -0
  54. package/dist/fhe-airdrop/advanced/react/useSetCompliancePolicy.d.ts +28 -0
  55. package/dist/fhe-airdrop/advanced/react/useSetCustomFee.d.ts +29 -0
  56. package/dist/fhe-airdrop/advanced/react/useSetDefaultDelegateToCompliance.d.ts +31 -0
  57. package/dist/fhe-airdrop/advanced/react/useSetDefaultGasFee.d.ts +30 -0
  58. package/dist/fhe-airdrop/advanced/react/useSetDefaultUpgradeable.d.ts +27 -0
  59. package/dist/fhe-airdrop/advanced/react/useSetEcdsaImplementation.d.ts +34 -0
  60. package/dist/fhe-airdrop/advanced/react/useSetFeeCollector.d.ts +27 -0
  61. package/dist/fhe-airdrop/advanced/react/useSetMaxGasFee.d.ts +33 -0
  62. package/dist/fhe-airdrop/advanced/react/useSetMerkleImplementation.d.ts +30 -0
  63. package/dist/fhe-airdrop/advanced/react/useSetUpgradeabilityPolicy.d.ts +31 -0
  64. package/dist/fhe-airdrop/airdrop-base.d.ts +55 -8
  65. package/dist/fhe-airdrop/campaign.d.ts +61 -20
  66. package/dist/fhe-airdrop/constants.d.ts +9 -1
  67. package/dist/fhe-airdrop/ecdsa.d.ts +13 -0
  68. package/dist/fhe-airdrop/errors.d.ts +87 -0
  69. package/dist/fhe-airdrop/factory.d.ts +62 -22
  70. package/dist/fhe-airdrop/guards.d.ts +133 -4
  71. package/dist/fhe-airdrop/index.cjs +86 -971
  72. package/dist/fhe-airdrop/index.d.cts +4 -4
  73. package/dist/fhe-airdrop/index.d.ts +4 -4
  74. package/dist/fhe-airdrop/index.js +8 -927
  75. package/dist/fhe-airdrop/merkle-tree.d.ts +32 -38
  76. package/dist/fhe-airdrop/merkle.d.ts +52 -31
  77. package/dist/fhe-airdrop/react/_shared.d.ts +13 -0
  78. package/dist/fhe-airdrop/react/index.cjs +997 -87
  79. package/dist/fhe-airdrop/react/index.d.cts +53 -4
  80. package/dist/fhe-airdrop/react/index.d.ts +53 -4
  81. package/dist/fhe-airdrop/react/index.js +887 -27
  82. package/dist/fhe-airdrop/react/keys.d.ts +98 -0
  83. package/dist/fhe-airdrop/react/useAccessEcdsaClaimAmount.d.ts +26 -0
  84. package/dist/fhe-airdrop/react/useAccessMerkleClaimAmount.d.ts +27 -0
  85. package/dist/fhe-airdrop/react/useAddComplianceDelegate.d.ts +37 -0
  86. package/dist/fhe-airdrop/react/useAdminBatchDiscloseBalanceToParties.d.ts +36 -0
  87. package/dist/fhe-airdrop/react/useAdminDiscloseBalanceToParty.d.ts +31 -0
  88. package/dist/fhe-airdrop/react/useAdminGetCurrentBalance.d.ts +27 -0
  89. package/dist/fhe-airdrop/react/useAirdropConfig.d.ts +49 -0
  90. package/dist/fhe-airdrop/react/useAirdropDeploymentMode.d.ts +28 -0
  91. package/dist/fhe-airdrop/react/useAirdropGrantRole.d.ts +35 -0
  92. package/dist/fhe-airdrop/react/useAirdropRenounceRole.d.ts +36 -0
  93. package/dist/fhe-airdrop/react/useAirdropRevokeRole.d.ts +33 -0
  94. package/dist/fhe-airdrop/react/useAirdropRoleAdmin.d.ts +28 -0
  95. package/dist/fhe-airdrop/react/useAirdropRoleConstants.d.ts +67 -0
  96. package/dist/fhe-airdrop/react/useAirdropRoleMembers.d.ts +49 -0
  97. package/dist/fhe-airdrop/react/useAirdropUpgradeToAndCall.d.ts +45 -0
  98. package/dist/fhe-airdrop/react/useBatchDiscloseHandlesToParty.d.ts +32 -0
  99. package/dist/fhe-airdrop/react/useBuildMerkleCampaign.d.ts +68 -0
  100. package/dist/fhe-airdrop/react/useComplianceGrantRole.d.ts +32 -0
  101. package/dist/fhe-airdrop/react/useComplianceHasRole.d.ts +37 -0
  102. package/dist/fhe-airdrop/react/useComplianceInfo.d.ts +50 -0
  103. package/dist/fhe-airdrop/react/useComplianceManagerOf.d.ts +32 -0
  104. package/dist/fhe-airdrop/react/useComplianceRenounceRole.d.ts +38 -0
  105. package/dist/fhe-airdrop/react/useComplianceRevokeRole.d.ts +36 -0
  106. package/dist/fhe-airdrop/react/useComplianceRoleConstants.d.ts +41 -0
  107. package/dist/fhe-airdrop/react/useComplianceRoleMembers.d.ts +45 -0
  108. package/dist/fhe-airdrop/react/useCreateAndFundEcdsaAirdrop.d.ts +55 -0
  109. package/dist/fhe-airdrop/react/useCreateAndFundMerkleAirdrop.d.ts +52 -0
  110. package/dist/fhe-airdrop/react/useDiscloseHandleToParty.d.ts +35 -0
  111. package/dist/fhe-airdrop/react/useEcdsaClaim.d.ts +4 -3
  112. package/dist/fhe-airdrop/react/useEcdsaClaimAndUnwrap.d.ts +27 -0
  113. package/dist/fhe-airdrop/react/useEcdsaDomain.d.ts +41 -0
  114. package/dist/fhe-airdrop/react/useEffectiveDelegateToCompliance.d.ts +26 -0
  115. package/dist/fhe-airdrop/react/useEncryptCampaignAmounts.d.ts +59 -0
  116. package/dist/fhe-airdrop/react/useFactoryFees.d.ts +11 -4
  117. package/dist/fhe-airdrop/react/useIsActiveDelegate.d.ts +29 -0
  118. package/dist/fhe-airdrop/react/useIsAirdrop.d.ts +44 -0
  119. package/dist/fhe-airdrop/react/useIsMerkleRootMutable.d.ts +21 -0
  120. package/dist/fhe-airdrop/react/useIsSignatureValid.d.ts +46 -0
  121. package/dist/fhe-airdrop/react/useMerkleClaim.d.ts +7 -19
  122. package/dist/fhe-airdrop/react/useMerkleClaimAndUnwrap.d.ts +29 -0
  123. package/dist/fhe-airdrop/react/usePlanMerkleCampaign.d.ts +53 -0
  124. package/dist/fhe-airdrop/react/usePreflightClaim.d.ts +46 -0
  125. package/dist/fhe-airdrop/react/usePreflightCreateAirdrop.d.ts +67 -0
  126. package/dist/fhe-airdrop/react/useRefreshComplianceBalance.d.ts +36 -0
  127. package/dist/fhe-airdrop/react/useRescueERC20.d.ts +30 -0
  128. package/dist/fhe-airdrop/react/useRescueOtherConfidentialToken.d.ts +34 -0
  129. package/dist/fhe-airdrop/react/useResolveGasFee.d.ts +30 -0
  130. package/dist/fhe-airdrop/react/useRevokeComplianceDelegate.d.ts +37 -0
  131. package/dist/fhe-airdrop/react/useRotateMerkleRoot.d.ts +52 -0
  132. package/dist/fhe-airdrop/react/useSignClaimAuthorization.d.ts +42 -0
  133. package/dist/fhe-airdrop/react/useWithdrawGasFee.d.ts +37 -0
  134. package/dist/fhe-airdrop/types.d.ts +27 -1
  135. package/dist/fhe-disperse/index.cjs +21 -21
  136. package/dist/fhe-disperse/index.js +2 -2
  137. package/dist/fhe-disperse/react/index.cjs +22 -22
  138. package/dist/fhe-disperse/react/index.js +3 -3
  139. package/dist/fhe-vesting/advanced/index.cjs +5 -5
  140. package/dist/fhe-vesting/advanced/index.js +3 -3
  141. package/dist/fhe-vesting/advanced/react/index.cjs +7 -7
  142. package/dist/fhe-vesting/advanced/react/index.js +4 -4
  143. package/dist/fhe-vesting/index.cjs +8 -8
  144. package/dist/fhe-vesting/index.js +2 -2
  145. package/dist/fhe-vesting/react/index.cjs +114 -114
  146. package/dist/fhe-vesting/react/index.js +3 -3
  147. package/dist/index.cjs +18 -18
  148. package/dist/index.js +1 -1
  149. package/dist/testnet-faucet/index.cjs +14 -14
  150. package/dist/testnet-faucet/index.js +2 -2
  151. package/dist/testnet-faucet/react/index.cjs +8 -8
  152. package/dist/testnet-faucet/react/index.js +3 -3
  153. package/package.json +1 -1
  154. package/dist/chunk-NCVX3K2N.cjs +0 -161
  155. package/dist/chunk-VP2B4WM2.js +0 -154
@@ -65,6 +65,34 @@ export declare class NonStockWrapperError extends TokenOpsSdkError {
65
65
  cause?: unknown;
66
66
  });
67
67
  }
68
+ /**
69
+ * A third party tried to redirect someone else's Merkle payout.
70
+ *
71
+ * `to` may differ from the claim identity only when the submitter IS that identity, so a
72
+ * relayer must leave `to` at `account` (or omit it). Raised by the SDK before the write
73
+ * and mapped from the contract's `UnauthorizedRedirect` revert, since the rule is decidable
74
+ * from the arguments alone — same two-producer shape as {@link NonStockWrapperError}.
75
+ *
76
+ * Carries all three addresses because the relation is the error: `to` on its own is a
77
+ * perfectly good address, and blaming it alone would be misleading.
78
+ *
79
+ * @alpha
80
+ */
81
+ export declare class UnauthorizedRedirectError extends TokenOpsSdkError {
82
+ readonly name = "UnauthorizedRedirectError";
83
+ readonly context: {
84
+ constraint: "merkle-third-party-redirect";
85
+ account?: Address;
86
+ to?: Address;
87
+ submitter?: Address;
88
+ };
89
+ constructor(args?: {
90
+ account?: Address;
91
+ to?: Address;
92
+ submitter?: Address;
93
+ cause?: unknown;
94
+ });
95
+ }
68
96
  /**
69
97
  * The two campaign variants, as used to label a prediction guard's context.
70
98
  *
@@ -287,4 +315,63 @@ export declare class ClaimWindowClosedError extends TokenOpsSdkError {
287
315
  cause?: unknown;
288
316
  });
289
317
  }
318
+ /**
319
+ * `create*` refused the fee the factory resolved for this creator: it landed
320
+ * above their `CommonAirdropParams.maxAcceptedGasFee`.
321
+ *
322
+ * The fee is frozen into the instance at initialization and is immutable
323
+ * afterwards, while the value that resolves is set by the factory's fee manager.
324
+ * The bound is how a creator declines a fee raised between planning and sending
325
+ * - so the fix is a deliberate one: re-send with a higher bound, or wait for the
326
+ * fee to come down. Raising the bound to {@link UINT96_MAX} accepts whatever the
327
+ * factory charges, now and at every later re-plan.
328
+ *
329
+ * @alpha
330
+ */
331
+ export declare class GasFeeNotAcceptedError extends TokenOpsSdkError {
332
+ readonly name = "GasFeeNotAcceptedError";
333
+ readonly context: {
334
+ method: string;
335
+ contractAddress: Address;
336
+ /** The fee the factory resolved for this creator, in wei. */
337
+ resolvedFee?: bigint;
338
+ /** The bound the creator declared, in wei. */
339
+ maxAcceptedGasFee?: bigint;
340
+ };
341
+ constructor(args: {
342
+ method: string;
343
+ contractAddress: Address;
344
+ resolvedFee?: bigint;
345
+ maxAcceptedGasFee?: bigint;
346
+ cause?: unknown;
347
+ });
348
+ }
349
+ /**
350
+ * The canonical factory's registry does not contain this address, so it is not
351
+ * an airdrop this SDK deployed.
352
+ *
353
+ * Instances are the wrong place to ask. An address with the same ABI, the same
354
+ * events and a pool the deployer controls is trivial to stand up, and nothing
355
+ * observable at the instance distinguishes it - `factory.isAirdrop` against a
356
+ * factory address taken from `src/core/addresses.ts` is the only check that
357
+ * means anything (audit-10).
358
+ *
359
+ * @alpha
360
+ */
361
+ export declare class UnrecognisedAirdropError extends TokenOpsSdkError {
362
+ readonly name = "UnrecognisedAirdropError";
363
+ readonly context: {
364
+ method: string;
365
+ contractAddress: Address;
366
+ factory: Address;
367
+ };
368
+ constructor(args: {
369
+ method: string;
370
+ /** The address that failed the check. */
371
+ contractAddress: Address;
372
+ /** The factory whose registry was consulted. */
373
+ factory: Address;
374
+ cause?: unknown;
375
+ });
376
+ }
290
377
  export declare const airdropProductMapper: RevertNameMapper;
@@ -1,19 +1,15 @@
1
+ import type { AbiParameterToPrimitiveType, ExtractAbiFunction } from "abitype";
1
2
  import type { Account, Address, Hex, PublicClient, WalletClient } from "viem";
2
3
  import { type SdkTelemetry } from "../core/telemetry.js";
3
4
  import type { EncryptedInput } from "../fhe/types.js";
5
+ import { airdropFactoryAbi as factoryAbi } from "./abis/factory.js";
4
6
  import type { WriteAccountOverride } from "./airdrop-base.js";
5
7
  import { type DeploymentMode } from "./constants.js";
6
8
  import { type EncryptorSource } from "./encryption.js";
7
9
  import type { CommonAirdropParams, CreateAirdropArgs, CreateAirdropResult, EcdsaAirdropParams, MerkleAirdropParams } from "./types.js";
8
- /** The on-chain `CommonAirdropParams` struct, field-for-field. */
9
- interface ContractCommonParams {
10
- token: Address;
11
- startTime: number;
12
- endTime: number;
13
- canExtendClaimWindow: boolean;
14
- unwrappable: boolean;
15
- complianceAdmin: Address;
16
- }
10
+ type ContractMerkleParams = AbiParameterToPrimitiveType<ExtractAbiFunction<typeof factoryAbi, "createMerkleConfidentialAirdrop">["inputs"][0]>;
11
+ /** The on-chain `CommonAirdropParams` struct, field-for-field, straight off the ABI. */
12
+ type ContractCommonParams = ContractMerkleParams["common"];
17
13
  /**
18
14
  * Convert the SDK's `"clone"` / `"uups"` name to the contract's
19
15
  * `DeploymentMode` ordinal.
@@ -362,11 +358,26 @@ export declare class ConfidentialAirdropFactoryClient {
362
358
  * @returns The page, possibly shorter than `limit`.
363
359
  */
364
360
  airdrops(offset: bigint, limit: bigint): Promise<readonly Address[]>;
361
+ /**
362
+ * Whether THIS factory created `candidate` - the genuineness check (audit-10).
363
+ *
364
+ * Nothing observable at an instance proves which factory, if any, deployed
365
+ * it: an attacker can deploy a contract with the same ABI, the same events
366
+ * and a pool it controls. The only trustworthy question is whether the
367
+ * canonical factory's own registry contains the address, which is why this
368
+ * read has to be addressed to a factory taken from
369
+ * `src/core/addresses.ts` rather than one the instance names.
370
+ *
371
+ * @param candidate The address to check.
372
+ * @returns True when this factory's registry contains `candidate`.
373
+ */
374
+ isAirdrop(candidate: Address): Promise<boolean>;
365
375
  /**
366
376
  * The compliance-manager clone bound to an instance.
367
377
  *
368
- * Doubles as a provenance check: the factory records this mapping only for
369
- * instances it deployed, so a zero address means the instance is not ours.
378
+ * Doubles as a provenance check, though a weaker one than {@link isAirdrop}:
379
+ * the factory records this mapping only for instances it deployed, so a zero
380
+ * address means the instance is not ours.
370
381
  *
371
382
  * @param airdrop Instance address.
372
383
  * @returns The clone address, or the zero address when unknown to this factory.
@@ -403,6 +414,37 @@ export declare class ConfidentialAirdropFactoryClient {
403
414
  feeCollector(): Promise<Address>;
404
415
  /** Per-claim gas fee new instances default to absent a {@link setCustomFee} override. */
405
416
  defaultGasFee(): Promise<bigint>;
417
+ /** The factory's own ceiling on every configurable fee and every create-time resolved fee. */
418
+ maxGasFee(): Promise<bigint>;
419
+ /**
420
+ * Set the factory's ceiling on every fee `setDefaultGasFee`/`setCustomFee`
421
+ * may configure, and on the fee resolved at create.
422
+ *
423
+ * Requires `DEFAULT_ADMIN_ROLE`, deliberately not `FEE_MANAGER_ROLE`: the fee
424
+ * manager moves fees only within a bound the admin controls. `0n` admits a
425
+ * zero fee and nothing else.
426
+ *
427
+ * Lowering it below an already-configured fee does not rewrite that fee - the
428
+ * stale value stays in storage and reverts `GasFeeExceedsMaximum` at the next
429
+ * create until it is lowered too.
430
+ *
431
+ * @throws {@link InvalidArgumentError} when `maxGasFee` is below a currently configured fee (`GasFeeExceedsMaximum`).
432
+ */
433
+ setMaxGasFee(maxGasFee: bigint, account?: Account | Address): Promise<Hex>;
434
+ /**
435
+ * The per-claim fee a `create*` from `creator` would freeze into the new
436
+ * instance right now: their {@link getCustomFee} override when enabled, else
437
+ * {@link defaultGasFee}.
438
+ *
439
+ * This is the number `CommonAirdropParams.maxAcceptedGasFee` is checked
440
+ * against, so it is also what to show a creator before they choose a bound.
441
+ * It is a snapshot, not a quote: `FEE_MANAGER_ROLE` can move it between this
442
+ * read and the create, which is exactly what the bound exists to catch.
443
+ *
444
+ * @param creator The account that will send `create*`.
445
+ * @returns The resolved fee in wei.
446
+ */
447
+ resolveGasFee(creator: Address): Promise<bigint>;
406
448
  /**
407
449
  * Point future `createEcdsaAirdrop*` calls at a new ECDSA implementation.
408
450
  * Requires `IMPL_MANAGER_ROLE`. Does not affect already-deployed instances —
@@ -571,17 +613,15 @@ export declare class ConfidentialAirdropFactoryClient {
571
613
  /**
572
614
  * Revoke `role` from `holder`. Requires the role's admin role.
573
615
  *
574
- * Unlike an airdrop instance, the factory declares no "never empty" floor:
575
- * revoking the last `DEFAULT_ADMIN_ROLE` member succeeds, and what it
576
- * freezes is **role administration**, permanently. `DEFAULT_ADMIN_ROLE`
577
- * administers all four operational roles and itself, so with the set empty
578
- * no role can ever be granted or revoked again - including putting an admin
579
- * back. The four operational roles keep working for whoever already holds
580
- * them: a `FEE_MANAGER_ROLE` holder can still call {@link setDefaultGasFee},
581
- * an `IMPL_MANAGER_ROLE` holder can still rotate an implementation. The
582
- * factory therefore keeps running with exactly the role holders it had at
583
- * that moment, with no way to rotate or remove them. Grant the successor
584
- * first.
616
+ * **The factory floors `DEFAULT_ADMIN_ROLE` at one live member** (audit-14),
617
+ * matching the instances. Renouncing or revoking the sole holder reverts
618
+ * `LastAdmin`, and granting the role to the zero address reverts
619
+ * `ZeroAdminGrant`, so the set can neither be emptied nor satisfied by a
620
+ * member nobody controls. Before audit-14 this succeeded and froze role
621
+ * administration permanently; it no longer does.
622
+ *
623
+ * A handover is still available and is the supported way out: grant the
624
+ * successor first, then renounce.
585
625
  *
586
626
  * @returns The transaction hash.
587
627
  */
@@ -1,6 +1,6 @@
1
1
  import type { Address, Hex, PublicClient } from "viem";
2
2
  import type { PreflightResult } from "../core/preflight.js";
3
- import type { DeploymentMode } from "./constants.js";
3
+ import { type DeploymentMode } from "./constants.js";
4
4
  import { type AirdropVariant } from "./errors.js";
5
5
  import type { ConfidentialAirdropFactoryClient } from "./factory.js";
6
6
  import type { EcdsaAirdropParams, MerkleAirdropParams } from "./types.js";
@@ -104,6 +104,49 @@ export interface AssertSaltAvailableArgs {
104
104
  * @alpha
105
105
  */
106
106
  export declare function assertSaltAvailable(args: AssertSaltAvailableArgs): Promise<void>;
107
+ /**
108
+ * The slice of {@link ConfidentialAirdropFactoryClient} {@link assertGasFeeAccepted}
109
+ * reads. Structural so this module keeps its runtime-import-free shape; the real
110
+ * client satisfies it as-is.
111
+ *
112
+ * @alpha
113
+ */
114
+ export interface GasFeeResolver {
115
+ address: Address;
116
+ resolveGasFee(creator: Address): Promise<bigint>;
117
+ }
118
+ /**
119
+ * Inputs for {@link assertGasFeeAccepted}.
120
+ *
121
+ * @alpha
122
+ */
123
+ export interface AssertGasFeeAcceptedArgs {
124
+ factory: GasFeeResolver;
125
+ /** The account that will send `create*`; fee overrides are per-creator. */
126
+ creator: Address;
127
+ /** `CommonAirdropParams.maxAcceptedGasFee`, in wei. */
128
+ maxAcceptedGasFee: bigint;
129
+ /** Label for the thrown error. */
130
+ method: string;
131
+ }
132
+ /**
133
+ * Refuse a create whose resolved per-claim fee exceeds the creator's declared bound.
134
+ *
135
+ * The contract enforces this too. Doing it here buys the reason: the revert
136
+ * carries the two numbers but not which factory knob produced them, and a
137
+ * creator who sees `resolvedFee` next to their own bound can tell a raised
138
+ * default from a custom override they did not know they had.
139
+ *
140
+ * The range check is not redundant with the comparison - a bound outside
141
+ * `uint96` silently wraps at the ABI edge and could encode as a SMALLER number
142
+ * than intended, turning "accept anything" into "accept almost nothing".
143
+ *
144
+ * @throws {@link InvalidArgumentError} when `maxAcceptedGasFee` is negative or above `UINT96_MAX`.
145
+ * @throws {@link GasFeeNotAcceptedError} when the factory resolves a fee above the bound.
146
+ *
147
+ * @alpha
148
+ */
149
+ export declare function assertGasFeeAccepted(args: AssertGasFeeAcceptedArgs): Promise<void>;
107
150
  /**
108
151
  * Inputs for {@link assertPredictionFresh}.
109
152
  *
@@ -163,7 +206,8 @@ export type PreflightCreateArgs = PreflightCreateCommon & ({
163
206
  * transaction, instead of throwing on the first problem.
164
207
  *
165
208
  * Same checks and the same typed errors the write path enforces: chain
166
- * support, the Merkle/UUPS refusal, the creator's effective
209
+ * support, the Merkle/UUPS refusal, the creator's resolved gas fee against
210
+ * `common.maxAcceptedGasFee`, the creator's effective
167
211
  * upgradeability policy when `mode: "uups"` is requested, the stock-wrapper
168
212
  * probe when `unwrappable` is set, a salt collision on the predicted
169
213
  * address, and - when `expectedInitCodeHash` is supplied - implementation
@@ -207,6 +251,11 @@ export interface ClaimPreflightTarget {
207
251
  * the two blocked the claim.
208
252
  */
209
253
  paused(): Promise<boolean>;
254
+ /**
255
+ * Create-time flag; `claimAndUnwrap` reverts `TokenNotUnwrappable` when false. Optional
256
+ * so a hand-rolled target stays valid — the unwrap blocker is simply not reported then.
257
+ */
258
+ unwrappable?: () => Promise<boolean>;
210
259
  /** Present on `EcdsaAirdropClient` only. */
211
260
  isSignatureValid?: (args: {
212
261
  recipient: Address;
@@ -217,6 +266,40 @@ export interface ClaimPreflightTarget {
217
266
  signature: Hex;
218
267
  }) => Promise<boolean>;
219
268
  }
269
+ /**
270
+ * The slice of {@link ConfidentialAirdropFactoryClient} the genuineness check reads.
271
+ *
272
+ * @alpha
273
+ */
274
+ export interface AirdropRegistry {
275
+ readonly address: Address;
276
+ /**
277
+ * The chain this factory is bound to. Checked against the instance's own
278
+ * `chainId` before the registry is consulted - a registry answer from the
279
+ * wrong chain is worse than no answer, because it reads as a `true`.
280
+ */
281
+ readonly chainId: number;
282
+ isAirdrop(candidate: Address): Promise<boolean>;
283
+ }
284
+ /**
285
+ * Refuse an instance the canonical factory's registry does not contain.
286
+ *
287
+ * The factory has to be one the caller already trusts - taken from
288
+ * `src/core/addresses.ts`, not read off the instance. An instance that names
289
+ * its own factory proves nothing: a fake instance names a fake factory, which
290
+ * answers `true`.
291
+ *
292
+ * @throws {@link UnrecognisedAirdropError} when the registry does not contain `airdrop`.
293
+ *
294
+ * @alpha
295
+ */
296
+ export declare function assertKnownAirdrop(args: {
297
+ factory: AirdropRegistry;
298
+ airdrop: Address;
299
+ /** The chain the instance being vetted lives on. */
300
+ chainId: number;
301
+ method: string;
302
+ }): Promise<void>;
220
303
  /**
221
304
  * The signature material {@link preflightClaim} needs to check an ECDSA claim.
222
305
  *
@@ -237,14 +320,58 @@ export interface EcdsaClaimPreflightSignature {
237
320
  */
238
321
  export interface PreflightClaimArgs {
239
322
  airdrop: ClaimPreflightTarget;
240
- /** The account that will send the claim - not the payout destination. */
323
+ /**
324
+ * The account that will SEND the claim, and therefore pays the exact-equality gas fee.
325
+ * On a relayed Merkle claim this is the relayer, not the entitled account.
326
+ */
241
327
  claimant: Address;
328
+ /**
329
+ * Merkle campaigns only: the claim identity, when it is not the submitter. Defaults to
330
+ * `claimant`. Supplying it enables the two argument-relation checks the contract makes
331
+ * before any FHE work — the zero identity and the third-party redirect rule.
332
+ */
333
+ account?: Address | undefined;
334
+ /** The payout destination, if one will be passed. Omitted means "defaults to `account`". */
335
+ to?: Address | undefined;
336
+ /**
337
+ * Set for `claimAndUnwrap`, which has no `account` parameter and is always submitted for
338
+ * the caller — so a claim identity that is not the submitter can never settle through it.
339
+ */
340
+ entrypoint?: "claim" | "claimAndUnwrap" | undefined;
242
341
  /**
243
342
  * ECDSA campaigns only. Supplied together with a client that exposes
244
343
  * `isSignatureValid`, it adds the authorisation check to the report.
245
344
  */
246
345
  signature?: EcdsaClaimPreflightSignature | undefined;
346
+ /**
347
+ * The canonical factory, from `src/core/addresses.ts`. Supplying it adds the
348
+ * genuineness check (audit-10): every other blocker here describes a claim
349
+ * that will fail, but a claim against a look-alike instance SUCCEEDS and
350
+ * delivers nothing. Omitting it skips that one check.
351
+ */
352
+ factory?: AirdropRegistry | undefined;
247
353
  }
354
+ /**
355
+ * The three argument-relation rules the Merkle claim path enforces before it touches the
356
+ * coprocessor. All are decidable from the arguments alone, so both `preflightClaim` and
357
+ * the client's own write path run them — no RPC, no chance of a wasted fee.
358
+ *
359
+ * @param account The claim identity (`claim`'s leading argument).
360
+ * @param submitter The address that will send the transaction.
361
+ * @param boundTo The address the entry's input proof is bound to, when known.
362
+ * @param to The payout destination, if one will be passed.
363
+ * @param entrypoint `claimAndUnwrap` is self-only; `claim` accepts a third-party submitter.
364
+ * @throws {@link InvalidArgumentError} on a zero `account`, a proof bound to another address, or a relayed `claimAndUnwrap`.
365
+ * @throws {@link UnauthorizedRedirectError} when a third party tries to redirect payout.
366
+ */
367
+ export declare function assertMerkleClaimIdentity(args: {
368
+ method: string;
369
+ account: Address;
370
+ submitter: Address;
371
+ boundTo?: Address | undefined;
372
+ to?: Address | undefined;
373
+ entrypoint?: "claim" | "claimAndUnwrap" | undefined;
374
+ }): void;
248
375
  /**
249
376
  * Report what would stop `claim` / `claimAndUnwrap` right now, without sending
250
377
  * anything.
@@ -252,7 +379,9 @@ export interface PreflightClaimArgs {
252
379
  * Checks the chain, the pause flag, the claim window on both ends, whether the
253
380
  * claimant can cover the instance's exact-equality gas fee, and - for an ECDSA
254
381
  * campaign given `signature` - whether the authorisation is currently valid and
255
- * unconsumed.
382
+ * unconsumed. Given `account` or `entrypoint`, it also checks the Merkle claim path's
383
+ * three argument relations: the zero identity, the third-party redirect rule, and
384
+ * `claimAndUnwrap`'s self-only constraint.
256
385
  *
257
386
  * The pause is read on its own rather than through `isClaimWindowActive()`:
258
387
  * that read ANDs the pause and the window together, so a `false` from it