@tokenops/sdk 2.0.0-alpha.2 → 2.0.0-alpha.4

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 (160) hide show
  1. package/CHANGELOG.md +92 -3
  2. package/README.md +76 -34
  3. package/SUPPORT.md +1 -1
  4. package/dist/{chunk-PYYSB5ZN.js → chunk-2ND7US2I.js} +1 -1
  5. package/dist/{chunk-EYDA6Q6C.js → chunk-3ZSPRHR3.js} +63 -12
  6. package/dist/{chunk-X7WBPCEU.js → chunk-67Q6KSVV.js} +4578 -3383
  7. package/dist/{chunk-UHHMVBLU.cjs → chunk-6AYJRUGP.cjs} +7 -50
  8. package/dist/{chunk-FEDX7B6T.cjs → chunk-6FUHF73F.cjs} +5 -5
  9. package/dist/{chunk-NWFMBLSQ.js → chunk-7KY4K4PH.js} +1 -1
  10. package/dist/{chunk-FHWSBGWE.cjs → chunk-ARZ5C3FS.cjs} +7 -7
  11. package/dist/{chunk-SPLNGUFF.js → chunk-AX6MQAWU.js} +2 -2
  12. package/dist/{chunk-IGO5XSPS.js → chunk-BSXEX2SH.js} +3 -3
  13. package/dist/{chunk-44YJPMQC.js → chunk-CHGY6B7L.js} +34 -81
  14. package/dist/{chunk-IUKKNJ2R.cjs → chunk-DFK5GCJP.cjs} +55 -72
  15. package/dist/{chunk-PZZK3O3S.cjs → chunk-E4IV4M4E.cjs} +4598 -3403
  16. package/dist/{chunk-A4DCEE33.js → chunk-GHGF65LN.js} +16 -12
  17. package/dist/{chunk-TWT3STIX.js → chunk-K3R27WX4.js} +8 -46
  18. package/dist/{chunk-GADGBQJO.js → chunk-KJUNBOJQ.js} +52 -71
  19. package/dist/{chunk-MM5BV5CS.cjs → chunk-PO3A2AQX.cjs} +4 -4
  20. package/dist/{chunk-EBLBPUPI.cjs → chunk-QHDMPT7I.cjs} +72 -19
  21. package/dist/{chunk-3YHBO2DL.cjs → chunk-S47IBDTA.cjs} +36 -83
  22. package/dist/{chunk-UG5RKLU2.cjs → chunk-SUYBAF27.cjs} +1 -1
  23. package/dist/{chunk-WJMEBBU7.js → chunk-TWU7L5H7.js} +5 -48
  24. package/dist/{chunk-WJLECC22.cjs → chunk-UUSSWOPR.cjs} +9 -47
  25. package/dist/{chunk-AREQJHKA.cjs → chunk-VHKMWWBA.cjs} +428 -22
  26. package/dist/{chunk-FT5H2Q7Y.cjs → chunk-YAL6JSAZ.cjs} +16 -12
  27. package/dist/{chunk-VW356KR3.js → chunk-YMWMIWBP.js} +421 -25
  28. package/dist/core/addresses.d.ts +4 -4
  29. package/dist/core/errors.d.ts +1 -1
  30. package/dist/core/index.d.ts +1 -0
  31. package/dist/core/pending.d.ts +57 -0
  32. package/dist/core/version.d.ts +1 -1
  33. package/dist/fhe/operators.d.ts +6 -5
  34. package/dist/fhe/types.d.ts +1 -1
  35. package/dist/fhe-airdrop/abis/airdrop-base.d.ts +97 -1
  36. package/dist/fhe-airdrop/abis/ecdsa.d.ts +117 -1
  37. package/dist/fhe-airdrop/abis/factory.d.ts +332 -1
  38. package/dist/fhe-airdrop/abis/merkle.d.ts +116 -1
  39. package/dist/fhe-airdrop/advanced/index.cjs +8 -4
  40. package/dist/fhe-airdrop/advanced/index.d.cts +1 -0
  41. package/dist/fhe-airdrop/advanced/index.d.ts +1 -0
  42. package/dist/fhe-airdrop/advanced/index.js +1 -1
  43. package/dist/fhe-airdrop/advanced/react/index.cjs +145 -137
  44. package/dist/fhe-airdrop/advanced/react/index.d.cts +1 -0
  45. package/dist/fhe-airdrop/advanced/react/index.d.ts +1 -0
  46. package/dist/fhe-airdrop/advanced/react/index.js +85 -78
  47. package/dist/fhe-airdrop/advanced/react/useClearCompliancePolicy.d.ts +3 -1
  48. package/dist/fhe-airdrop/advanced/react/useDisableCustomFee.d.ts +4 -1
  49. package/dist/fhe-airdrop/advanced/react/useFactoryComplianceDelegate.d.ts +2 -4
  50. package/dist/fhe-airdrop/advanced/react/useFactoryRenounceRole.d.ts +4 -3
  51. package/dist/fhe-airdrop/advanced/react/useFactoryRevokeRole.d.ts +9 -7
  52. package/dist/fhe-airdrop/advanced/react/useFactoryRoleMembers.d.ts +6 -5
  53. package/dist/fhe-airdrop/advanced/react/useSetComplianceDelegate.d.ts +7 -7
  54. package/dist/fhe-airdrop/advanced/react/useSetComplianceManagerImpl.d.ts +3 -1
  55. package/dist/fhe-airdrop/advanced/react/useSetCompliancePolicy.d.ts +2 -1
  56. package/dist/fhe-airdrop/advanced/react/useSetCustomFee.d.ts +4 -1
  57. package/dist/fhe-airdrop/advanced/react/useSetDefaultDelegateToCompliance.d.ts +5 -4
  58. package/dist/fhe-airdrop/advanced/react/useSetDefaultGasFee.d.ts +7 -3
  59. package/dist/fhe-airdrop/advanced/react/useSetMaxGasFee.d.ts +37 -0
  60. package/dist/fhe-airdrop/airdrop-base.d.ts +142 -18
  61. package/dist/fhe-airdrop/campaign.d.ts +37 -73
  62. package/dist/fhe-airdrop/compliance-clone.d.ts +21 -0
  63. package/dist/fhe-airdrop/constants.d.ts +11 -1
  64. package/dist/fhe-airdrop/ecdsa.d.ts +79 -26
  65. package/dist/fhe-airdrop/encryption.d.ts +19 -6
  66. package/dist/fhe-airdrop/errors.d.ts +136 -2
  67. package/dist/fhe-airdrop/factory-params.d.ts +2361 -0
  68. package/dist/fhe-airdrop/factory.d.ts +189 -75
  69. package/dist/fhe-airdrop/guards.d.ts +183 -19
  70. package/dist/fhe-airdrop/index.cjs +88 -56
  71. package/dist/fhe-airdrop/index.d.cts +12 -10
  72. package/dist/fhe-airdrop/index.d.ts +12 -10
  73. package/dist/fhe-airdrop/index.js +5 -5
  74. package/dist/fhe-airdrop/merkle-tree.d.ts +9 -9
  75. package/dist/fhe-airdrop/merkle.d.ts +32 -11
  76. package/dist/fhe-airdrop/react/_shared.d.ts +12 -6
  77. package/dist/fhe-airdrop/react/index.cjs +336 -240
  78. package/dist/fhe-airdrop/react/index.d.cts +19 -9
  79. package/dist/fhe-airdrop/react/index.d.ts +19 -9
  80. package/dist/fhe-airdrop/react/index.js +158 -97
  81. package/dist/fhe-airdrop/react/keys.d.ts +54 -7
  82. package/dist/fhe-airdrop/react/useAccessEcdsaClaimAmount.d.ts +6 -3
  83. package/dist/fhe-airdrop/react/useAccessMerkleClaimAmount.d.ts +2 -2
  84. package/dist/fhe-airdrop/react/useAirdropConfig.d.ts +8 -3
  85. package/dist/fhe-airdrop/react/useAirdropPause.d.ts +5 -0
  86. package/dist/fhe-airdrop/react/useBuildMerkleCampaign.d.ts +4 -6
  87. package/dist/fhe-airdrop/react/useComplianceManager.d.ts +2 -2
  88. package/dist/fhe-airdrop/react/useComplianceManagerOf.d.ts +1 -2
  89. package/dist/fhe-airdrop/react/useCreateAndFundEcdsaAirdrop.d.ts +16 -11
  90. package/dist/fhe-airdrop/react/useCreateAndFundMerkleAirdrop.d.ts +23 -7
  91. package/dist/fhe-airdrop/react/useCreateEcdsaAirdrop.d.ts +13 -8
  92. package/dist/fhe-airdrop/react/useCreateMerkleAirdrop.d.ts +27 -7
  93. package/dist/fhe-airdrop/react/useDedupMode.d.ts +16 -0
  94. package/dist/fhe-airdrop/react/useDiscloseHandleToParty.d.ts +4 -3
  95. package/dist/fhe-airdrop/react/useEcdsaClaim.d.ts +6 -4
  96. package/dist/fhe-airdrop/react/useEcdsaClaimAndUnwrap.d.ts +15 -8
  97. package/dist/fhe-airdrop/react/useEcdsaDomain.d.ts +1 -1
  98. package/dist/fhe-airdrop/react/useEncryptCampaignAmounts.d.ts +5 -7
  99. package/dist/fhe-airdrop/react/useExtendClaimWindow.d.ts +4 -1
  100. package/dist/fhe-airdrop/react/useFactoryFees.d.ts +11 -4
  101. package/dist/fhe-airdrop/react/useFundAirdrop.d.ts +3 -4
  102. package/dist/fhe-airdrop/react/useGrantInstanceRoles.d.ts +2 -1
  103. package/dist/fhe-airdrop/react/useIsAirdrop.d.ts +44 -0
  104. package/dist/fhe-airdrop/react/useIsSignatureValid.d.ts +5 -4
  105. package/dist/fhe-airdrop/react/useMerkleClaim.d.ts +4 -1
  106. package/dist/fhe-airdrop/react/useMerkleClaimAndUnwrap.d.ts +14 -3
  107. package/dist/fhe-airdrop/react/usePlanMerkleCampaign.d.ts +18 -3
  108. package/dist/fhe-airdrop/react/usePreflightClaim.d.ts +1 -1
  109. package/dist/fhe-airdrop/react/usePreflightCreateAirdrop.d.ts +34 -13
  110. package/dist/fhe-airdrop/react/useRefreshComplianceBalance.d.ts +36 -0
  111. package/dist/fhe-airdrop/react/useRescueERC20.d.ts +3 -3
  112. package/dist/fhe-airdrop/react/useRescueNativeToken.d.ts +23 -0
  113. package/dist/fhe-airdrop/react/useResolveGasFee.d.ts +30 -0
  114. package/dist/fhe-airdrop/react/useRotateMerkleRoot.d.ts +5 -2
  115. package/dist/fhe-airdrop/react/useSignClaimAuthorization.d.ts +4 -0
  116. package/dist/fhe-airdrop/react/useTokenOf.d.ts +29 -0
  117. package/dist/fhe-airdrop/react/useUnwrapRequest.d.ts +25 -0
  118. package/dist/fhe-airdrop/react/useWithdrawConfidential.d.ts +4 -0
  119. package/dist/fhe-airdrop/roles.d.ts +82 -7
  120. package/dist/fhe-airdrop/types.d.ts +122 -14
  121. package/dist/fhe-disperse/errors.d.ts +3 -3
  122. package/dist/fhe-disperse/index.cjs +22 -22
  123. package/dist/fhe-disperse/index.d.cts +2 -1
  124. package/dist/fhe-disperse/index.d.ts +2 -1
  125. package/dist/fhe-disperse/index.js +3 -3
  126. package/dist/fhe-disperse/react/index.cjs +27 -25
  127. package/dist/fhe-disperse/react/index.d.cts +2 -1
  128. package/dist/fhe-disperse/react/index.d.ts +2 -1
  129. package/dist/fhe-disperse/react/index.js +8 -6
  130. package/dist/fhe-disperse/react/useDisperse.d.ts +6 -2
  131. package/dist/fhe-disperse/react/useRegister.d.ts +9 -3
  132. package/dist/fhe-disperse/react/useSingletonWithdrawTokenFee.d.ts +7 -2
  133. package/dist/fhe-disperse/react/useWithdrawTokenFee.d.ts +7 -2
  134. package/dist/fhe-disperse/singleton.d.ts +33 -19
  135. package/dist/fhe-disperse/types.d.ts +48 -8
  136. package/dist/fhe-vesting/advanced/index.cjs +6 -6
  137. package/dist/fhe-vesting/advanced/index.js +4 -4
  138. package/dist/fhe-vesting/advanced/react/index.cjs +9 -9
  139. package/dist/fhe-vesting/advanced/react/index.js +6 -6
  140. package/dist/fhe-vesting/factory.d.ts +31 -8
  141. package/dist/fhe-vesting/index.cjs +23 -23
  142. package/dist/fhe-vesting/index.d.cts +3 -2
  143. package/dist/fhe-vesting/index.d.ts +3 -2
  144. package/dist/fhe-vesting/index.js +4 -4
  145. package/dist/fhe-vesting/manager.d.ts +27 -2
  146. package/dist/fhe-vesting/react/index.cjs +126 -126
  147. package/dist/fhe-vesting/react/index.d.cts +3 -2
  148. package/dist/fhe-vesting/react/index.d.ts +3 -2
  149. package/dist/fhe-vesting/react/index.js +5 -5
  150. package/dist/fhe-vesting/react/useCreateManager.d.ts +16 -9
  151. package/dist/fhe-vesting/react/useCreateManagerAndGetAddress.d.ts +9 -5
  152. package/dist/fhe-vesting/react/useSplitVesting.d.ts +9 -3
  153. package/dist/index.cjs +18 -18
  154. package/dist/index.js +1 -1
  155. package/dist/testnet-faucet/faucet.d.ts +3 -0
  156. package/dist/testnet-faucet/index.cjs +15 -15
  157. package/dist/testnet-faucet/index.js +3 -3
  158. package/dist/testnet-faucet/react/index.cjs +9 -9
  159. package/dist/testnet-faucet/react/index.js +4 -4
  160. package/package.json +1 -1
@@ -3,7 +3,7 @@ import type { Address } from "viem";
3
3
  import type { AirdropVariant } from "../errors.js";
4
4
  import { type AirdropInstanceHookOptions } from "./_shared.js";
5
5
  /**
6
- * Create-time-immutable instance configuration - combines five `AirdropBaseClient`
6
+ * Create-time-immutable instance configuration - combines seven `AirdropBaseClient`
7
7
  * reads into one.
8
8
  *
9
9
  * @alpha
@@ -21,13 +21,18 @@ export interface AirdropConfig {
21
21
  complianceManager: Address;
22
22
  /** Per-claim ETH fee in wei; `0` on a fee-free campaign. */
23
23
  gasFee: bigint;
24
+ /** Whether `extendClaimWindow` is permitted; frozen at create. */
25
+ canExtendClaimWindow: boolean;
26
+ /** The block the instance was initialized in (L2 block on Arbitrum). */
27
+ deploymentBlockNumber: bigint;
24
28
  }
25
29
  /** @alpha */
26
30
  export type UseAirdropConfigArgs = AirdropInstanceHookOptions<AirdropConfig>;
27
31
  /**
28
32
  * Read an airdrop instance's immutable configuration in one round trip.
29
33
  *
30
- * Bundles `token` / `airdropType` / `unwrappable` / `complianceManager` / `gasFee` -
34
+ * Bundles `token` / `airdropType` / `unwrappable` / `complianceManager` / `gasFee` /
35
+ * `canExtendClaimWindow` / `deploymentBlockNumber` -
31
36
  * what a UI needs before it can render anything else about a campaign, and how
32
37
  * `airdropType` and `unwrappable` get covered without shipping two more one-line hooks
33
38
  * nobody would call separately. `variant` is derived from the ordinal so a consumer can
@@ -38,7 +43,7 @@ export type UseAirdropConfigArgs = AirdropInstanceHookOptions<AirdropConfig>;
38
43
  *
39
44
  * The existing {@link useAirdropToken}, {@link useAirdropGasFee} and
40
45
  * {@link useComplianceManager} overlap this bundle and stay: they are one `eth_call`
41
- * rather than five, which is cheaper for a UI that needs exactly one value.
46
+ * rather than seven, which is cheaper for a UI that needs exactly one value.
42
47
  *
43
48
  * @example
44
49
  * const { data: config } = useAirdropConfig({ address: airdropAddress });
@@ -21,6 +21,11 @@ export interface AirdropPauseArgs extends WriteAccountOverride {
21
21
  * `window.isActive`, so a mounted `useAirdropWindow` consumer would otherwise
22
22
  * keep showing a stale `isActive` after a pause/unpause.
23
23
  *
24
+ * Not invalidated: {@link usePreflightClaim} (its `PausedError` blocker) and
25
+ * {@link useIsSignatureValid} (which answers `false` while paused) also move with
26
+ * the flag. Refetch them if the same session renders both the admin toggle and a
27
+ * claim form.
28
+ *
24
29
  * @example
25
30
  * const setPause = useAirdropPause({ address: airdropAddress });
26
31
  * await setPause.mutateAsync({ paused: true });
@@ -1,6 +1,5 @@
1
1
  import { type UseMutationResult } from "@tanstack/react-query";
2
2
  import type { Address } from "viem";
3
- import { type CampaignSubmitterOption } from "../campaign.js";
4
3
  import { type EncryptorSource } from "../encryption.js";
5
4
  import type { BuiltCampaign, CampaignRecipient } from "../types.js";
6
5
  /**
@@ -23,7 +22,7 @@ export interface UseBuildMerkleCampaignOptions {
23
22
  *
24
23
  * @alpha
25
24
  */
26
- export interface UseBuildMerkleCampaignArgs extends CampaignSubmitterOption {
25
+ export interface UseBuildMerkleCampaignArgs {
27
26
  /** The LIVE `MerkleConfidentialAirdrop` instance every input is bound to. Not the factory, not the implementation. */
28
27
  instance: Address;
29
28
  /** CUMULATIVE totals per account, never per-drop tranches. */
@@ -48,10 +47,9 @@ export interface UseBuildMerkleCampaignArgs extends CampaignSubmitterOption {
48
47
  * A mutation rather than a query: encryption is randomised, so the same roster
49
48
  * builds a different root every call and there is no cache identity to key.
50
49
  *
51
- * Each entry is `{ account, boundTo, handle, inputProof, merkleProof }` —
52
- * `account` selects the leaf and the cumulative accounting, `boundTo` is the
53
- * only address the coprocessor accepts as sender. They are equal unless
54
- * `submitter` was set.
50
+ * Each entry is `{ account, handle, inputProof, merkleProof }` - `account`
51
+ * selects the leaf and the cumulative accounting. One relayer request per 32
52
+ * recipients, issued concurrently; every leaf is submittable by any address.
55
53
  *
56
54
  * **Invalidates:** nothing. Off-chain work only; the root is not published here.
57
55
  *
@@ -5,8 +5,8 @@ import { type AirdropInstanceHookOptions } from "./_shared.js";
5
5
  export type UseComplianceManagerArgs = AirdropInstanceHookOptions<Address>;
6
6
  /**
7
7
  * Read an airdrop instance's own compliance-manager clone — the only
8
- * contract, besides the caller, ever granted ACL on that instance's
9
- * confidential handles.
8
+ * contract the instance itself grants persistent ACL on its pool and claim
9
+ * handles.
10
10
  *
11
11
  * @example
12
12
  * const { data: complianceManager } = useComplianceManager({ address: airdropAddress });
@@ -15,8 +15,7 @@ export interface UseComplianceManagerOfArgs extends AirdropHookOptions<Address>
15
15
  * instances it deployed, so a zero address means the instance did not come
16
16
  * from this factory - do not mount the compliance hooks on it.
17
17
  *
18
- * Cached indefinitely (`staleTime: Infinity`, overridable through `query`):
19
- * the mapping is written once, inside `createX`, and never changes.
18
+ * Caching follows the same write-once policy as {@link useTokenOf}.
20
19
  *
21
20
  * @remarks
22
21
  * {@link useComplianceManager} answers the same question from the other side,
@@ -1,7 +1,8 @@
1
1
  import { type UseMutationResult } from "@tanstack/react-query";
2
+ import type { ReceiptMode } from "../../core/pending.js";
2
3
  import type { WriteAccountOverride } from "../airdrop-base.js";
3
4
  import type { FundInput } from "../factory.js";
4
- import type { CreateAirdropArgs, CreateAirdropResult, EcdsaAirdropParams } from "../types.js";
5
+ import type { CreateAirdropArgs, CreateAirdropResult, EcdsaAirdropParams, PendingCreateAirdropResult } from "../types.js";
5
6
  import { type AirdropClientOptions } from "./_shared.js";
6
7
  /**
7
8
  * Variables for {@link useCreateAndFundEcdsaAirdrop} — the create arguments plus
@@ -9,7 +10,7 @@ import { type AirdropClientOptions } from "./_shared.js";
9
10
  *
10
11
  * @alpha
11
12
  */
12
- export type UseCreateAndFundEcdsaAirdropArgs = CreateAirdropArgs<EcdsaAirdropParams> & FundInput & WriteAccountOverride;
13
+ export type UseCreateAndFundEcdsaAirdropArgs = CreateAirdropArgs<EcdsaAirdropParams> & FundInput & WriteAccountOverride & ReceiptMode;
13
14
  /**
14
15
  * Deploy an ECDSA-authorised airdrop instance and seed its pool in one
15
16
  * transaction.
@@ -20,19 +21,23 @@ export type UseCreateAndFundEcdsaAirdropArgs = CreateAirdropArgs<EcdsaAirdropPar
20
21
  * (plaintext, encrypted against the hook-level `encryptor`) or a pre-built
21
22
  * `encryptedInput`, never both.
22
23
  *
23
- * Returns `CreateAirdropResult` unflattened — `{ hash, airdrop,
24
- * complianceManager }`; the caller needs the instance address to do anything
25
- * with the campaign afterwards.
24
+ * Returns `CreateAirdropResult` (or `PendingCreateAirdropResult` with `waitForReceipt: false`; both carry every address) unflattened - `{ hash, airdrop,
25
+ * complianceManager, managerImplementation, complianceDelegate }`; the caller
26
+ * needs the instance address to do anything with the campaign afterwards.
26
27
  *
27
28
  * **A successful receipt does not prove the pool was funded.** An ERC-7984
28
29
  * transfer moves an encrypted zero instead of reverting when the funder is
29
30
  * short, because reverting would leak the balance.
30
31
  *
31
- * **Invalidates:** the factory's registry reads (`useFactoryRegistry`) for
32
- * this factory address, whatever page was cached — a new instance moves
33
- * `airdropCount()` / `airdrops()`. Nothing else: the new instance has no
34
- * cached reads yet, and no read hook here exposes the confidential pool
35
- * balance.
32
+ * **Safe / multisig signers:** pass `waitForReceipt: false`; see
33
+ * {@link ReceiptMode}.
34
+ *
35
+ * **Invalidates:** the factory's registry pages, every cached `isAirdrop` answer, every
36
+ * cached create preflight (the salt is now consumed), and the {@link useTokenOf} /
37
+ * {@link useComplianceManagerOf} entries for the new address, which may hold a zero
38
+ * read before the create.
39
+ * No read hook here caches the confidential pool balance, so the fund leg moves nothing
40
+ * else.
36
41
  *
37
42
  * **Never log `amount`.** It is the one plaintext confidential value this
38
43
  * hook sees — keep it out of telemetry, `onError` context and console.
@@ -52,4 +57,4 @@ export type UseCreateAndFundEcdsaAirdropArgs = CreateAirdropArgs<EcdsaAirdropPar
52
57
  *
53
58
  * @alpha
54
59
  */
55
- export declare function useCreateAndFundEcdsaAirdrop(options?: AirdropClientOptions): UseMutationResult<CreateAirdropResult, Error, UseCreateAndFundEcdsaAirdropArgs>;
60
+ export declare function useCreateAndFundEcdsaAirdrop(options?: AirdropClientOptions): UseMutationResult<CreateAirdropResult | PendingCreateAirdropResult, Error, UseCreateAndFundEcdsaAirdropArgs>;
@@ -1,7 +1,8 @@
1
1
  import { type UseMutationResult } from "@tanstack/react-query";
2
+ import type { ReceiptMode } from "../../core/pending.js";
2
3
  import type { WriteAccountOverride } from "../airdrop-base.js";
3
4
  import type { FundInput } from "../factory.js";
4
- import type { CreateAirdropArgs, CreateAirdropResult, MerkleAirdropParams } from "../types.js";
5
+ import type { CreateAirdropResult, CreateMerkleAirdropArgs, PendingCreateAirdropResult } from "../types.js";
5
6
  import { type AirdropClientOptions } from "./_shared.js";
6
7
  /**
7
8
  * Variables for {@link useCreateAndFundMerkleAirdrop} — the create arguments plus
@@ -9,7 +10,7 @@ import { type AirdropClientOptions } from "./_shared.js";
9
10
  *
10
11
  * @alpha
11
12
  */
12
- export type UseCreateAndFundMerkleAirdropArgs = CreateAirdropArgs<MerkleAirdropParams> & FundInput & WriteAccountOverride;
13
+ export type UseCreateAndFundMerkleAirdropArgs = CreateMerkleAirdropArgs & FundInput & WriteAccountOverride & ReceiptMode;
13
14
  /**
14
15
  * Deploy a Merkle-proof airdrop instance and seed its pool in one transaction.
15
16
  *
@@ -20,23 +21,37 @@ export type UseCreateAndFundMerkleAirdropArgs = CreateAirdropArgs<MerkleAirdropP
20
21
  *
21
22
  * `mode: "uups"` is rejected before the wallet is even looked up, so the
22
23
  * mutation rejects without prompting for a signature. Returns
23
- * `CreateAirdropResult` unflattened — `{ hash, airdrop, complianceManager }`.
24
+ * `CreateAirdropResult` (or `PendingCreateAirdropResult` with `waitForReceipt: false`; both carry every address) unflattened - `{ hash, airdrop, complianceManager,
25
+ * managerImplementation, complianceDelegate }`.
24
26
  *
25
27
  * **A successful receipt does not prove the pool was funded.** An ERC-7984
26
28
  * transfer moves an encrypted zero instead of reverting when the funder is
27
29
  * short, because reverting would leak the balance.
28
30
  *
29
- * **Invalidates:** the factory's registry reads (`useFactoryRegistry`) for
30
- * this factory address, whatever page was cached — a new instance moves
31
- * `airdropCount()` / `airdrops()`. Nothing else.
31
+ * **Safe / multisig signers:** pass `waitForReceipt: false`; see
32
+ * {@link ReceiptMode}.
33
+ *
34
+ * **Invalidates:** the factory's registry pages, every cached `isAirdrop` answer, every
35
+ * cached create preflight (the salt is now consumed), and the {@link useTokenOf} /
36
+ * {@link useComplianceManagerOf} entries for the new address, which may hold a zero
37
+ * read before the create.
32
38
  *
33
39
  * **Never log `amount`.** It is the one plaintext confidential value this
34
40
  * hook sees — keep it out of telemetry, `onError` context and console.
35
41
  *
42
+ * **Creating from a plan.** Pass the {@link PlannedCampaign} as `plan`, with
43
+ * `merkleRoot: plan.root`. The create then pins `plan.predictedAddress`, refuses a root that
44
+ * differs from the plan's, and refuses to send once the factory's Merkle implementation has
45
+ * moved since the plan ({@link PredictionDriftError}). Both are checked before anything is
46
+ * encrypted. `expected: { airdrop: plan.predictedAddress }` is the lower-level equivalent of
47
+ * the pin alone.
48
+ *
36
49
  * @throws {@link MerkleUupsUnsupportedError} when `mode` is `"uups"`.
37
50
  * @throws {@link DeploymentAddressUnavailableError} when no factory is deployed for the connected chain.
38
51
  * @throws {@link MissingEncryptorError} when `amount` is given and no encryptor resolves.
39
52
  * @throws {@link SaltCollisionError} when this `(mode, deployer, userSalt)` tuple already deployed an instance.
53
+ * @throws {@link PredictionDriftError} when `plan` is given and the factory's Merkle implementation moved since it was taken.
54
+ * @throws {@link InvalidArgumentError} when `params.merkleRoot` or `expected.airdrop` disagrees with `plan`.
40
55
  *
41
56
  * @example
42
57
  * const createAndFund = useCreateAndFundMerkleAirdrop({ encryptor: () => sdk.relayer });
@@ -44,9 +59,10 @@ export type UseCreateAndFundMerkleAirdropArgs = CreateAirdropArgs<MerkleAirdropP
44
59
  * params: { common, merkleRoot: plan.root, isMerkleRootMutable: false },
45
60
  * mode: "clone",
46
61
  * userSalt,
62
+ * plan,
47
63
  * amount: 1_000_000n,
48
64
  * });
49
65
  *
50
66
  * @alpha
51
67
  */
52
- export declare function useCreateAndFundMerkleAirdrop(options?: AirdropClientOptions): UseMutationResult<CreateAirdropResult, Error, UseCreateAndFundMerkleAirdropArgs>;
68
+ export declare function useCreateAndFundMerkleAirdrop(options?: AirdropClientOptions): UseMutationResult<CreateAirdropResult | PendingCreateAirdropResult, Error, UseCreateAndFundMerkleAirdropArgs>;
@@ -1,19 +1,24 @@
1
1
  import { type UseMutationResult } from "@tanstack/react-query";
2
- import type { CreateAirdropArgs, CreateAirdropResult, EcdsaAirdropParams } from "../types.js";
2
+ import type { ReceiptMode } from "../../core/pending.js";
3
+ import type { CreateAirdropArgs, CreateAirdropResult, EcdsaAirdropParams, PendingCreateAirdropResult } from "../types.js";
3
4
  import type { WriteAccountOverride } from "../airdrop-base.js";
4
5
  import { type AirdropClientOptions } from "./_shared.js";
5
6
  /**
6
7
  * Deploy an ECDSA-authorised airdrop instance via the factory.
7
8
  *
8
- * Returns the headless client's `CreateAirdropResult` unchanged — `{ hash,
9
- * airdrop, complianceManager }` — never flattened to just a hash: a caller
9
+ * Returns the headless client's `CreateAirdropResult` (or `PendingCreateAirdropResult` with `waitForReceipt: false`; both carry every address) unchanged - `{ hash,
10
+ * airdrop, complianceManager, managerImplementation, complianceDelegate }` -
11
+ * never flattened to just a hash: a caller
10
12
  * needs the deployed instance address to do anything with the campaign
11
13
  * afterwards.
12
14
  *
13
- * **Invalidates:** the factory's registry reads (`useFactoryRegistry`) for
14
- * this factory address, since a new instance changes `airdropCount()` /
15
- * `airdrops()`. Nothing else — the new instance has no cached reads yet, and
16
- * no other factory or instance state changed.
15
+ * **Safe / multisig signers:** pass `waitForReceipt: false`; see
16
+ * {@link ReceiptMode}.
17
+ *
18
+ * **Invalidates:** the factory's registry pages, every cached `isAirdrop` answer, every
19
+ * cached create preflight (the salt is now consumed), and the {@link useTokenOf} /
20
+ * {@link useComplianceManagerOf} entries for the new address, which may hold a zero
21
+ * read before the create.
17
22
  *
18
23
  * @example
19
24
  * const create = useCreateEcdsaAirdrop();
@@ -25,4 +30,4 @@ import { type AirdropClientOptions } from "./_shared.js";
25
30
  *
26
31
  * @alpha
27
32
  */
28
- export declare function useCreateEcdsaAirdrop(options?: AirdropClientOptions): UseMutationResult<CreateAirdropResult, Error, CreateAirdropArgs<EcdsaAirdropParams> & WriteAccountOverride>;
33
+ export declare function useCreateEcdsaAirdrop(options?: AirdropClientOptions): UseMutationResult<CreateAirdropResult | PendingCreateAirdropResult, Error, CreateAirdropArgs<EcdsaAirdropParams> & WriteAccountOverride & ReceiptMode>;
@@ -1,16 +1,27 @@
1
1
  import { type UseMutationResult } from "@tanstack/react-query";
2
+ import type { ReceiptMode } from "../../core/pending.js";
2
3
  import type { WriteAccountOverride } from "../airdrop-base.js";
3
- import type { CreateAirdropArgs, CreateAirdropResult, MerkleAirdropParams } from "../types.js";
4
+ import type { CreateAirdropResult, CreateMerkleAirdropArgs, PendingCreateAirdropResult } from "../types.js";
4
5
  import { type AirdropClientOptions } from "./_shared.js";
5
6
  /**
6
7
  * Deploy a Merkle-proof airdrop instance via the factory.
7
8
  *
8
- * Returns the headless client's `CreateAirdropResult` unchanged — `{ hash,
9
- * airdrop, complianceManager }`.
9
+ * Returns the headless client's `CreateAirdropResult` (or `PendingCreateAirdropResult` with `waitForReceipt: false`; both carry every address) unchanged - `{ hash,
10
+ * airdrop, complianceManager, managerImplementation, complianceDelegate }`.
10
11
  *
11
- * **Invalidates:** the factory's registry reads (`useFactoryRegistry`) for
12
- * this factory address — a new instance changes `airdropCount()` /
13
- * `airdrops()`. Nothing else.
12
+ * **Safe / multisig signers:** pass `waitForReceipt: false`; see
13
+ * {@link ReceiptMode}.
14
+ *
15
+ * **Invalidates:** the factory's registry pages, every cached `isAirdrop` answer, every
16
+ * cached create preflight (the salt is now consumed), and the {@link useTokenOf} /
17
+ * {@link useComplianceManagerOf} entries for the new address, which may hold a zero
18
+ * read before the create.
19
+ *
20
+ * **Creating from a plan.** Pass the {@link PlannedCampaign} as `plan`, with
21
+ * `merkleRoot: plan.root`. The create then pins `plan.predictedAddress`, refuses a root that
22
+ * differs from the plan's, and refuses to send once the factory's Merkle implementation has
23
+ * moved since the plan ({@link PredictionDriftError}). `expected: { airdrop: plan.predictedAddress }`
24
+ * is the lower-level equivalent of the pin alone.
14
25
  *
15
26
  * @example
16
27
  * const create = useCreateMerkleAirdrop();
@@ -20,6 +31,15 @@ import { type AirdropClientOptions } from "./_shared.js";
20
31
  * userSalt,
21
32
  * });
22
33
  *
34
+ * @example
35
+ * // From a plan (immutable root):
36
+ * await create.mutateAsync({
37
+ * params: { ...params, merkleRoot: plan.root, isMerkleRootMutable: false },
38
+ * mode: "clone",
39
+ * userSalt,
40
+ * plan,
41
+ * });
42
+ *
23
43
  * @alpha
24
44
  */
25
- export declare function useCreateMerkleAirdrop(options?: AirdropClientOptions): UseMutationResult<CreateAirdropResult, Error, CreateAirdropArgs<MerkleAirdropParams> & WriteAccountOverride>;
45
+ export declare function useCreateMerkleAirdrop(options?: AirdropClientOptions): UseMutationResult<CreateAirdropResult | PendingCreateAirdropResult, Error, CreateMerkleAirdropArgs & WriteAccountOverride & ReceiptMode>;
@@ -0,0 +1,16 @@
1
+ import { type UseQueryResult } from "@tanstack/react-query";
2
+ import type { DedupMode } from "../constants.js";
3
+ import { type AirdropInstanceHookOptions } from "./_shared.js";
4
+ /** @alpha */
5
+ export type UseDedupModeArgs = AirdropInstanceHookOptions<DedupMode>;
6
+ /**
7
+ * Read an ECDSA airdrop instance's replay-protection policy from the chain.
8
+ *
9
+ * The mode is frozen at creation, so `staleTime: Infinity`.
10
+ *
11
+ * @example
12
+ * const { data: mode } = useDedupMode({ address: airdropAddress });
13
+ *
14
+ * @alpha
15
+ */
16
+ export declare function useDedupMode(args: UseDedupModeArgs): UseQueryResult<DedupMode, Error>;
@@ -18,9 +18,10 @@ export interface UseDiscloseHandleToPartyArgs extends WriteAccountOverride {
18
18
  * returns the transaction hash rather than an `EncryptedViewResult`; there is
19
19
  * no ACL event to parse.
20
20
  *
21
- * Two gates apply on-chain: the caller must itself be allowed on `handle`
22
- * (bypassed by `DISCLOSURE_ADMIN_ROLE`), and the *instance* must be allowed on
23
- * it, so a handle minted by an unrelated contract cannot be laundered through
21
+ * Two gates apply on-chain: the handle must be persistently user-decryptable by
22
+ * the caller in this instance's context (a transient allowance from a fresh FHE op
23
+ * does not count; `DISCLOSURE_ADMIN_ROLE` bypasses this gate), and the *instance*
24
+ * must be allowed on it, so a handle minted by an unrelated contract cannot be laundered through
24
25
  * this campaign. A failure of the first surfaces as `FheHandleNotAllowedError`.
25
26
  *
26
27
  * **Invalidates:** nothing. ACL is append-only and no read hook in this
@@ -6,9 +6,11 @@ import { type EcdsaAirdropInstanceClientOptions } from "./_shared.js";
6
6
  * Claim a signed encrypted allocation on an `ECDSAConfidentialAirdrop`
7
7
  * instance.
8
8
  *
9
- * `dedupMode` must be supplied — the contract has no on-chain getter for it
10
- * (see {@link EcdsaAirdropClientConfig.dedupMode}); pass the same value used
11
- * at `createEcdsaAirdrop` time.
9
+ * An empty `inputProof` is accepted only for a zero handle or one the sender and the
10
+ * instance already hold ACL on; see `EcdsaClaimArgs.encryptedInput`.
11
+ *
12
+ * `dedupMode` is deprecated - no call branches on it. Read the
13
+ * instance's actual policy with `useDedupMode`.
12
14
  *
13
15
  * **Invalidates:** the `fn "isSignatureValid"` PREFIX for this instance. A successful
14
16
  * claim consumes the EIP-712 digest, so every cached signature check on this instance
@@ -16,7 +18,7 @@ import { type EcdsaAirdropInstanceClientOptions } from "./_shared.js";
16
18
  * that can no longer be claimed. Per-claim dedup state itself remains unqueryable.
17
19
  *
18
20
  * @example
19
- * const claim = useEcdsaClaim({ address: airdropAddress, dedupMode: "perAddress" });
21
+ * const claim = useEcdsaClaim({ address: airdropAddress });
20
22
  * await claim.mutateAsync({ encryptedInput, dedupId, deadline, signer, signature });
21
23
  *
22
24
  * @alpha
@@ -8,18 +8,25 @@ import { type EcdsaAirdropInstanceClientOptions } from "./_shared.js";
8
8
  *
9
9
  * Reverts {@link FeatureDisabledError} on a non-unwrappable campaign - gate the button
10
10
  * on `useAirdropConfig().unwrappable`. Consumes the same replay guards and EIP-712
11
- * digest as {@link useEcdsaClaim}.
11
+ * digest as {@link useEcdsaClaim}, and the same empty-proof rule.
12
12
  *
13
- * `dedupMode` must be supplied - the contract has no on-chain getter for it; pass the
14
- * same value used at `createEcdsaAirdrop` time.
13
+ * **The amount is public from the claim transaction.** With the stock wrapper the unwrapped
14
+ * amount is publicly decryptable at once (the emitted `unwrapRequestId` is its handle);
15
+ * `finalizeUnwrap` only releases the ERC-20. Only a plain claim keeps it confidential.
15
16
  *
16
- * **Invalidates:** nothing, mirroring {@link useEcdsaClaim}. Replay consumption is not
17
- * queryable, so no cached read in this subpath tracks the state a successful claim
18
- * changes. `useIsSignatureValid` DOES flip to `false` once the digest is consumed, but
19
- * it is not invalidated from here - refetch it yourself after a claim.
17
+ * `dedupMode` is deprecated - no call branches on it. Read the
18
+ * instance's actual policy with `useDedupMode`.
19
+ *
20
+ * Resolves to the claim's transaction hash; the underlying ERC-20 moves only at the
21
+ * wrapper's later `finalizeUnwrap`. Pass the hash to `useUnwrapRequest` for the
22
+ * `unwrapRequestId` that call takes.
23
+ *
24
+ * **Invalidates:** the `fn "isSignatureValid"` PREFIX for this instance, exactly as
25
+ * {@link useEcdsaClaim} does: the claim consumes the EIP-712 digest, so a cached `true`
26
+ * is now a guaranteed replay. Per-claim dedup state itself remains unqueryable.
20
27
  *
21
28
  * @example
22
- * const unwrapClaim = useEcdsaClaimAndUnwrap({ address: airdropAddress, dedupMode: "perAddress" });
29
+ * const unwrapClaim = useEcdsaClaimAndUnwrap({ address: airdropAddress });
23
30
  * await unwrapClaim.mutateAsync({ encryptedInput, dedupId, deadline, signer, signature, to });
24
31
  *
25
32
  * @alpha
@@ -33,7 +33,7 @@ export type UseEcdsaDomainArgs = EcdsaAirdropInstanceHookOptions<EcdsaDomain>;
33
33
  * All three are bytecode/deploy constants, so `staleTime: Infinity`.
34
34
  *
35
35
  * @example
36
- * const { data } = useEcdsaDomain({ address: airdropAddress, dedupMode: "perAddress" });
36
+ * const { data } = useEcdsaDomain({ address: airdropAddress });
37
37
  * const built = data?.claimTypehash === CLAIM_TYPEHASH;
38
38
  *
39
39
  * @alpha
@@ -1,6 +1,6 @@
1
1
  import { type UseMutationResult } from "@tanstack/react-query";
2
2
  import type { Address } from "viem";
3
- import { type CampaignSubmitterOption, type EncryptedCampaignLeaf } from "../campaign.js";
3
+ import { type EncryptedCampaignLeaf } from "../campaign.js";
4
4
  import { type EncryptorSource } from "../encryption.js";
5
5
  import type { CampaignRecipient } from "../types.js";
6
6
  /**
@@ -22,7 +22,7 @@ export interface UseEncryptCampaignAmountsOptions {
22
22
  *
23
23
  * @alpha
24
24
  */
25
- export interface UseEncryptCampaignAmountsArgs extends CampaignSubmitterOption {
25
+ export interface UseEncryptCampaignAmountsArgs {
26
26
  /** The LIVE `MerkleConfidentialAirdrop` instance every input is bound to. Not the factory, not the implementation. */
27
27
  instance: Address;
28
28
  /** CUMULATIVE totals per account, never per-drop tranches. */
@@ -38,11 +38,9 @@ export interface UseEncryptCampaignAmountsArgs extends CampaignSubmitterOption {
38
38
  * cache identity to key. **Persist what resolves** — nothing here is
39
39
  * recoverable or reproducible.
40
40
  *
41
- * `submitter` decides the shape and the relayer cost. Omitted: one request per
42
- * recipient, issued concurrently, each input bound to that recipient, so only
43
- * they can claim — chunk the roster yourself if your relayer rate-limits. Set:
44
- * a single batched request bound to that one address, one shared `inputProof`
45
- * for the whole roster, and only that address can submit.
41
+ * One relayer request per 32 recipients, issued concurrently; every leaf is
42
+ * submittable by any address. Split the roster yourself if your relayer
43
+ * rate-limits.
46
44
  *
47
45
  * **Invalidates:** nothing. Pure off-chain relayer work — no contract state
48
46
  * moves, so no cached read goes stale.
@@ -8,7 +8,10 @@ import { type AirdropInstanceClientOptions } from "./_shared.js";
8
8
  * `canExtendClaimWindow: true`.
9
9
  *
10
10
  * **Invalidates:** only this instance's `useAirdropWindow` entry — `endTime`
11
- * is one of the five fields that hook bundles.
11
+ * is one of the five fields that hook bundles. Not invalidated: the window
12
+ * blockers of {@link usePreflightClaim} and a cached `false` from
13
+ * {@link useIsSignatureValid}, both of which a later `endTime` can move; refetch
14
+ * them if the same session renders a claim form.
12
15
  *
13
16
  * @example
14
17
  * const extend = useExtendClaimWindow({ address: airdropAddress });
@@ -11,14 +11,21 @@ export interface FactoryFees {
11
11
  feeCollector: Address;
12
12
  /** Wei charged per claim on a fee-charging campaign, absent a per-creator custom fee. */
13
13
  defaultGasFee: bigint;
14
+ /**
15
+ * The factory's own ceiling, in wei. Bounds both fee setters and the fee
16
+ * resolved at create, so it is the upper bound a `maxAcceptedGasFee` ever
17
+ * has to clear.
18
+ */
19
+ maxGasFee: bigint;
14
20
  }
15
21
  /** @alpha */
16
22
  export type UseFactoryFeesArgs = AirdropHookOptions<FactoryFees>;
17
23
  /**
18
- * Read the airdrop factory's default fee configuration — `feeCollector` and
19
- * `defaultGasFee` in one round trip. For a specific creator's override, read
20
- * `factory.getCustomFee(creator)` from the headless client directly (not
21
- * wrapped here — see the module's scope-discipline note).
24
+ * Read the airdrop factory's chain-wide fee configuration: `feeCollector`,
25
+ * `defaultGasFee` and `maxGasFee` in one round trip. For the number a specific
26
+ * creator's `maxAcceptedGasFee` is actually checked against, use
27
+ * `factory.resolveGasFee(creator)` from the headless client: it folds in that
28
+ * creator's `getCustomFee` override, which this hook does not read.
22
29
  *
23
30
  * @example
24
31
  * const { data: fees } = useFactoryFees();
@@ -15,10 +15,9 @@ import { type AirdropClientOptions } from "./_shared.js";
15
15
  * `MissingEncryptorError` only if neither resolves — no encryptor
16
16
  * indirection needs duplicating here.
17
17
  *
18
- * **Invalidates:** nothing. No read hook in this subpath's list exposes the
19
- * confidential pool balance (`adminGetCurrentBalance` is disclosure-gated
20
- * and intentionally not wrapped here), so there is no cached query this
21
- * write could leave stale.
18
+ * **Invalidates:** nothing. No read hook in this subpath caches the confidential
19
+ * pool balance (`useAdminGetCurrentBalance` is a mutation, so there is nothing to
20
+ * invalidate).
22
21
  *
23
22
  * **Never log `amount`.** It is the one plaintext confidential value this
24
23
  * hook ever sees — do not pass it to telemetry, `onError` context, or a
@@ -22,7 +22,8 @@ export interface GrantInstanceRolesArgs {
22
22
  * (regardless of which role/holder pair), since a role split can touch
23
23
  * several roles across several accounts in one call and the SDK does not
24
24
  * duplicate the on-chain role-constant lookup client-side to invalidate more
25
- * narrowly. Scoped to this instance only — other campaigns' role caches are
25
+ * narrowly. It also invalidates every `useAirdropRoleMembers` entry for this
26
+ * instance. Scoped to this instance only - other campaigns' role caches are
26
27
  * untouched.
27
28
  *
28
29
  * @example
@@ -0,0 +1,44 @@
1
+ import { type UseQueryResult } from "@tanstack/react-query";
2
+ import type { Address } from "viem";
3
+ import { type AirdropHookOptions } from "./_shared.js";
4
+ /** @alpha */
5
+ export interface UseIsAirdropArgs extends AirdropHookOptions<boolean> {
6
+ /** The address to check against the factory's registry. */
7
+ candidate?: Address;
8
+ }
9
+ /**
10
+ * Ask the canonical factory whether it created `candidate` - the genuineness
11
+ * check (audit-10).
12
+ *
13
+ * **Run this before trusting an address a user pasted, a link carried, or an
14
+ * indexer returned.** A look-alike instance with the same ABI, the same events
15
+ * and a pool the attacker controls is cheap to deploy, and nothing readable at
16
+ * the instance tells it apart. A claim against one succeeds and delivers
17
+ * nothing.
18
+ *
19
+ * The factory this asks is the one resolved from `src/core/addresses.ts` for
20
+ * the connected chain, which is what makes the answer worth anything: an
21
+ * instance that names its own factory names a fake one.
22
+ *
23
+ * **Only a `true` is cached.** The registry is append-only, so a `true` can
24
+ * never become `false` and is safe to hold forever. A `false` is not stable:
25
+ * the address can be created later by another tab, another service, or the
26
+ * headless client, none of which reaches this hook's invalidations - so caching
27
+ * it would serve a stale false negative indefinitely and, for a security check,
28
+ * in the direction that blocks a legitimate claim. Negative answers therefore
29
+ * stay fresh and refetch normally. Override either with `query.staleTime`.
30
+ *
31
+ * **The `address` override is the trust root.** Like every factory hook this
32
+ * accepts `address`, and `useMemoAirdropFactoryClient` prefers it over
33
+ * `src/core/addresses.ts`. That is what makes the hook usable on a local chain,
34
+ * but it also means an attacker-supplied factory address would answer `true`
35
+ * for its own look-alike. Pass `address` only from a source you trust at least
36
+ * as much as the registry; when in doubt, omit it.
37
+ *
38
+ * @example
39
+ * const { data: genuine } = useIsAirdrop({ candidate: fromUrl });
40
+ * if (genuine === false) return <Warning address={fromUrl} />;
41
+ *
42
+ * @alpha
43
+ */
44
+ export declare function useIsAirdrop(args?: UseIsAirdropArgs): UseQueryResult<boolean, Error>;
@@ -24,20 +24,21 @@ export interface UseIsSignatureValidArgs extends EcdsaAirdropInstanceHookOptions
24
24
  * past `deadline`, `signer` lacking `SIGNER_ROLE`, an invalid signature, a consumed
25
25
  * digest, or an already-deduped claim). `recipient` is load-bearing and part of the
26
26
  * cache key: the read is an `eth_call` FROM that address, so passing the wrong one
27
- * returns `false` for a signature that would otherwise succeed.
27
+ * returns `false` for a signature that would otherwise succeed. A `SIGNER_ROLE` key that
28
+ * has been EIP-7702-delegated makes every voucher read `false`; see
29
+ * `EcdsaAirdropClient.isSignatureValid`.
28
30
  *
29
31
  * Stays disabled until all six signature fields are defined, so a UI can call it
30
32
  * unconditionally while a form fills in.
31
33
  *
32
34
  * @remarks
33
35
  * A successful {@link useEcdsaClaim} / {@link useEcdsaClaimAndUnwrap} flips this to
34
- * `false` by consuming the digest, but neither claim hook invalidates it - refetch it
35
- * yourself after a claim rather than trusting a cached `true`.
36
+ * `false` by consuming the digest; both invalidate it on success. A claim sent outside
37
+ * these hooks (headless client, another tab) does not, so refetch in that case.
36
38
  *
37
39
  * @example
38
40
  * const { data: claimable } = useIsSignatureValid({
39
41
  * address: airdropAddress,
40
- * dedupMode: "perAddress",
41
42
  * recipient, encryptedAmountHandle, dedupId, deadline, signer, signature,
42
43
  * });
43
44
  *
@@ -13,9 +13,12 @@ import { type AirdropInstanceClientOptions } from "./_shared.js";
13
13
  * than the relayer's. Other accounts' cached reads, and every other instance's cache,
14
14
  * are left untouched.
15
15
  *
16
+ * Anyone may submit an entry; see {@link MerkleClaimArgs.entry} for what a third-party
17
+ * submission costs the recipient, and check `useClaimedAmount` first.
18
+ *
16
19
  * @example
17
20
  * const claim = useMerkleClaim({ address: airdropAddress });
18
- * await claim.mutateAsync({ entry }); // relayers pass a relayed entry unchanged
21
+ * await claim.mutateAsync({ entry }); // any address may pass the entry unchanged
19
22
  *
20
23
  * @alpha
21
24
  */