@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
@@ -7,11 +7,22 @@ import { type AirdropInstanceClientOptions } from "./_shared.js";
7
7
  * wrapper's unwrap, making `to` the underlying ERC-20 beneficiary.
8
8
  *
9
9
  * **Self-only, unlike {@link useMerkleClaim}.** This entrypoint takes no claim identity:
10
- * the leaf, the accounting and the input proof are all `msg.sender`, so an entry whose
11
- * `account` is not the submitter is refused before the write. Gate the button on
12
- * `submitter === entry.account` and fall back to `useMerkleClaim` for relayed claims.
10
+ * the leaf and the accounting are both `msg.sender`, so an entry whose `account` is not
11
+ * the sender is refused before the write. Gate the button on `sender === entry.account`
12
+ * and fall back to `useMerkleClaim` for third-party claims.
13
13
  * Also requires an unwrappable campaign - see `useAirdropConfig().unwrappable`.
14
14
  *
15
+ * A third party can still settle the same entry first through `useMerkleClaim`, after
16
+ * which an unwrap pays an encrypted zero; see {@link MerkleClaimArgs.entry}.
17
+ *
18
+ * **The amount is public from the claim transaction.** With the stock wrapper the unwrapped
19
+ * amount is publicly decryptable at once (the emitted `unwrapRequestId` is its handle);
20
+ * `finalizeUnwrap` only releases the ERC-20. Only a plain claim keeps it confidential.
21
+ *
22
+ * Resolves to the claim's transaction hash; the underlying ERC-20 moves only at the
23
+ * wrapper's later `finalizeUnwrap`. Pass the hash to `useUnwrapRequest` for the
24
+ * `unwrapRequestId` that call takes.
25
+ *
15
26
  * **Invalidates:** `useClaimedAmount` for the SENDING account on this instance. The
16
27
  * contract advances `claimedAmount[msg.sender]` here, so the key is built from the
17
28
  * sender resolved in `onMutate` (`variables.account ?? walletClient.account`) and NOT
@@ -19,8 +19,13 @@ export type UsePlanMerkleCampaignArgs = Omit<PlanMerkleCampaignArgs, "factory" |
19
19
  * hook breaks it with the factory's CREATE2 prediction oracle, then feeds
20
20
  * `root` to {@link useCreateMerkleAirdrop} (or
21
21
  * {@link useCreateAndFundMerkleAirdrop}) with the SAME `mode`, `creator` and
22
- * `userSalt`. `initCodeHashAfter` is what `preflightCreateAirdrop`'s
23
- * `expectedInitCodeHash` wants.
22
+ * `userSalt`.
23
+ *
24
+ * Pass the returned plan to that create as `plan` (and to `usePreflightCreateAirdrop`). The
25
+ * entries are bound to `plan.predictedAddress`, so the create pins it, checks the root, and
26
+ * refuses to send once the factory's Merkle implementation has moved since planning
27
+ * ({@link PredictionDriftError}). `expected: { airdrop: plan.predictedAddress }` is the
28
+ * lower-level equivalent of the pin alone.
24
29
  *
25
30
  * **Surface {@link PredictionDriftError}, never swallow it.** It means the
26
31
  * factory's Merkle implementation pointer moved mid-build, so every entry is
@@ -34,6 +39,9 @@ export type UsePlanMerkleCampaignArgs = Omit<PlanMerkleCampaignArgs, "factory" |
34
39
  * `(variant, mode, creator, userSalt)` predict the same address however
35
40
  * different their params.
36
41
  *
42
+ * One relayer request per 32 recipients, issued concurrently; every leaf is
43
+ * submittable by any address.
44
+ *
37
45
  * **Invalidates:** nothing. Two init-code-hash reads and a prediction — no
38
46
  * state moves until the create lands.
39
47
  *
@@ -44,9 +52,16 @@ export type UsePlanMerkleCampaignArgs = Omit<PlanMerkleCampaignArgs, "factory" |
44
52
  *
45
53
  * @example
46
54
  * const plan = usePlanMerkleCampaign({ encryptor: () => sdk.relayer });
47
- * const { root, entries, predictedAddress } = await plan.mutateAsync({
55
+ * const create = useCreateMerkleAirdrop();
56
+ * const planned = await plan.mutateAsync({
48
57
  * params, mode: "clone", creator, userSalt, recipients,
49
58
  * });
59
+ * await create.mutateAsync({
60
+ * params: { ...params, merkleRoot: planned.root, isMerkleRootMutable: false },
61
+ * mode: "clone",
62
+ * userSalt,
63
+ * plan: planned,
64
+ * });
50
65
  *
51
66
  * @alpha
52
67
  */
@@ -13,7 +13,7 @@ export interface UsePreflightClaimArgs extends AirdropInstanceHookOptions<Prefli
13
13
  dedupMode?: DedupMode;
14
14
  /** The account that will SEND the claim and therefore pays the exact-equality gas fee. On a relayed Merkle claim this is the relayer. */
15
15
  claimant?: Address;
16
- /** Merkle campaigns only: the claim identity, when it is not the submitter. Defaults to `claimant`. */
16
+ /** Merkle campaigns only: the claim identity, when it is not the sender. Defaults to `claimant`. Any address may send a Merkle claim; only the account itself may redirect with `to`. */
17
17
  account?: Address;
18
18
  /** The payout destination, if one will be passed. */
19
19
  to?: Address;
@@ -1,21 +1,30 @@
1
1
  import { type UseQueryResult } from "@tanstack/react-query";
2
2
  import type { Address, Hex } from "viem";
3
- import type { PreflightResult } from "../../core/preflight.js";
4
3
  import type { DeploymentMode } from "../constants.js";
5
- import type { EcdsaAirdropParams, MerkleAirdropParams } from "../types.js";
4
+ import { type PreflightCreateResult } from "../guards.js";
5
+ import type { CreateCommitments, EcdsaAirdropParams, MerkleAirdropParams, PlannedCampaign } from "../types.js";
6
6
  import { type AirdropHookOptions } from "./_shared.js";
7
7
  /** The variant-independent half of {@link UsePreflightCreateAirdropArgs}. */
8
8
  /** @alpha */
9
- export interface PreflightCreateAirdropCommon extends AirdropHookOptions<PreflightResult> {
9
+ export interface PreflightCreateAirdropCommon extends AirdropHookOptions<PreflightCreateResult> {
10
10
  mode?: DeploymentMode;
11
11
  /** The account that will send `create*`. CREATE2 salts are per-deployer. */
12
12
  creator?: Address;
13
13
  userSalt?: Hex;
14
14
  /**
15
- * Pass `plan.initCodeHashAfter` to turn the implementation-drift check on.
16
- * Omitting it skips that one check.
15
+ * The init-code hash the address was predicted against. Supplying it turns
16
+ * the implementation-drift check on; omitting it skips that one check. A
17
+ * Merkle `plan` supplies it for you.
17
18
  */
18
19
  expectedInitCodeHash?: Hex;
20
+ /**
21
+ * Commitments to pin instead of quoting fresh at send time. Anything omitted is read
22
+ * from the factory just before the transaction. For a {@link PlannedCampaign}, pass
23
+ * `plan` on the Merkle arm instead; `{ airdrop: plan.predictedAddress }` here is the
24
+ * lower-level equivalent of its pin. A pinned field that differs from the factory's
25
+ * fresh quote is reported as a `CreateCommitmentMismatchError` blocker.
26
+ */
27
+ expected?: Partial<CreateCommitments> | undefined;
19
28
  }
20
29
  /**
21
30
  * `variant` and `params` are correlated, mirroring the headless
@@ -34,22 +43,35 @@ export type UsePreflightCreateAirdropArgs = PreflightCreateAirdropCommon & ({
34
43
  } | {
35
44
  variant: "merkle";
36
45
  params?: MerkleAirdropParams;
46
+ /**
47
+ * The {@link PlannedCampaign} the create will realise: pins its address and
48
+ * checks drift against `plan.initCodeHashAfter`. A disagreeing root, pin or
49
+ * `expectedInitCodeHash` is reported as an `InvalidArgumentError` blocker.
50
+ */
51
+ plan?: PlannedCampaign;
37
52
  });
38
53
  /**
39
54
  * Read-only preflight for `useCreateEcdsaAirdrop` / `useCreateMerkleAirdrop`.
40
55
  * Runs every create-time guardrail the write path enforces - chain support,
41
- * the Merkle/UUPS refusal, the creator's effective upgradeability policy for
56
+ * the Merkle/UUPS refusal, params the new instance's initializer would revert
57
+ * on (zero token, an `endTime` less than a minute away, a window that does not
58
+ * open before it closes, a zero immutable Merkle root, a zero ECDSA signer),
59
+ * the resolved fee against the factory's ceiling and the creator's bound,
60
+ * the creator's effective upgradeability policy for
42
61
  * `mode: "uups"`, the stock-wrapper probe when `params.common.unwrappable` is
43
- * set, a salt collision on the predicted address, and implementation drift
44
- * when `expectedInitCodeHash` is supplied.
62
+ * set, a salt collision on the predicted address, a pinned `expected`
63
+ * commitment that drifted from the fresh quote, and implementation drift
64
+ * when `expectedInitCodeHash` (or a Merkle `plan`) is supplied.
45
65
  *
46
66
  * **The point is disabling the submit button, not catching after the fact.**
47
67
  * `blockers` carries the same typed errors `create*` would have thrown, so a
48
68
  * UI branches on `error.code` here exactly as it does in `onError`.
69
+ * `commitments` is what the create would be sent with, for showing the creator
70
+ * before they sign.
49
71
  *
50
72
  * Disabled until `variant`, `params`, `mode`, `creator` and `userSalt` are all
51
- * set. Refetch after the user changes their target mode or the factory's
52
- * implementation pointer moves.
73
+ * set. Factory writes made through this SDK's admin hooks invalidate it;
74
+ * refetch after a factory change made anywhere else.
53
75
  *
54
76
  * @remarks
55
77
  * Named `…Airdrop` rather than `…Create` so a consumer importing both this and
@@ -57,11 +79,10 @@ export type UsePreflightCreateAirdropArgs = PreflightCreateAirdropCommon & ({
57
79
  *
58
80
  * @example
59
81
  * const { data: report } = usePreflightCreateAirdrop({
60
- * variant: "merkle", params, mode: "clone", creator, userSalt,
61
- * expectedInitCodeHash: plan.initCodeHashAfter,
82
+ * variant: "merkle", params, mode: "clone", creator, userSalt, plan,
62
83
  * });
63
84
  * const canSubmit = report?.ready === true;
64
85
  *
65
86
  * @alpha
66
87
  */
67
- export declare function usePreflightCreateAirdrop(args?: UsePreflightCreateAirdropArgs): UseQueryResult<PreflightResult, Error>;
88
+ export declare function usePreflightCreateAirdrop(args?: UsePreflightCreateAirdropArgs): UseQueryResult<PreflightCreateResult, Error>;
@@ -0,0 +1,36 @@
1
+ import { type UseMutationResult } from "@tanstack/react-query";
2
+ import type { EncryptedViewResult } from "../../fhe/types.js";
3
+ import type { WriteAccountOverride } from "../airdrop-base.js";
4
+ import { type AirdropInstanceClientOptions } from "./_shared.js";
5
+ /**
6
+ * **Submits a transaction.** Re-grants the compliance-manager clone on the
7
+ * instance's current pool balance and returns the handle it was granted on.
8
+ *
9
+ * **Permissionless** (audit-04), unlike every other disclosure hook here. The
10
+ * token rotates the instance's balance handle on every incoming transfer,
11
+ * including a direct one no airdrop code observes, and the clone's existing
12
+ * grant does not follow. Anyone may restore it because there is no argument to
13
+ * abuse: the only handle read is the instance's own balance and the only
14
+ * grantee is the clone wired at initialization.
15
+ *
16
+ * The handle comes from the receipt's ACL `Allowed` event naming the CLONE, not
17
+ * the caller - so unless the connected account is the clone, it cannot decrypt
18
+ * what this returns. That is also why it is a mutation: the entrypoint calls
19
+ * `FHE.allow`, so its handle can never come from a simulation (CLAUDE.md
20
+ * Pitfall #1).
21
+ *
22
+ * **A snapshot, not a subscription.** The handle stays readable forever (ACL is
23
+ * append-only), but the next incoming transfer rotates the instance onto a new
24
+ * handle the clone was never granted on. Re-run this after any transfer in.
25
+ *
26
+ * **Invalidates:** nothing, and nothing invalidates it - a mutation result is
27
+ * not a cache entry, so no write can refresh it. Re-run it yourself after
28
+ * `useFundAirdrop` or `useWithdrawConfidential`.
29
+ *
30
+ * @example
31
+ * const refresh = useRefreshComplianceBalance({ address: airdropAddress });
32
+ * const { handle } = await refresh.mutateAsync();
33
+ *
34
+ * @alpha
35
+ */
36
+ export declare function useRefreshComplianceBalance(options: AirdropInstanceClientOptions): UseMutationResult<EncryptedViewResult, Error, WriteAccountOverride | void>;
@@ -13,9 +13,9 @@ export interface RescueERC20Args extends WriteAccountOverride {
13
13
  * Sweep a plain ERC-20 that was sent to an airdrop instance by mistake.
14
14
  * Requires `RESCUER_ROLE`.
15
15
  *
16
- * The campaign's own token is ERC-7984, not ERC-20, so the distribution pool
17
- * cannot be reached through this path at all — use
18
- * {@link useWithdrawConfidential} (`TREASURY_ROLE`) for the pool, and
16
+ * Passing the campaign's own token reverts `CannotRescueAirdropToken` (a wrapper token
17
+ * answers both interfaces, so this is a check, not an assumption); use
18
+ * {@link useWithdrawConfidential} for the pool and
19
19
  * {@link useRescueOtherConfidentialToken} for a stranded ERC-7984.
20
20
  *
21
21
  * **Invalidates:** nothing. This subpath has no read hook over arbitrary
@@ -0,0 +1,23 @@
1
+ import { type UseMutationResult } from "@tanstack/react-query";
2
+ import type { Address, Hex } from "viem";
3
+ import type { WriteAccountOverride } from "../airdrop-base.js";
4
+ import { type AirdropInstanceClientOptions } from "./_shared.js";
5
+ /**
6
+ * Sweep ETH stranded on a zero-fee airdrop instance (a forced send, or a transfer to the
7
+ * predicted address before deploy) to `recipient`. Requires `RESCUER_ROLE`.
8
+ *
9
+ * Refused on a fee-charging campaign (`NativeRescueRequiresZeroFeeError`): there the ETH is
10
+ * fee revenue and belongs to `withdrawGasFee`.
11
+ *
12
+ * **Invalidates:** nothing. No read hook in this subpath exposes the instance's ETH balance,
13
+ * so there is no cached query left stale by the sweep.
14
+ *
15
+ * @example
16
+ * const rescue = useRescueNativeToken({ address: airdropAddress });
17
+ * await rescue.mutateAsync({ recipient: treasuryAddress });
18
+ *
19
+ * @alpha
20
+ */
21
+ export declare function useRescueNativeToken(options: AirdropInstanceClientOptions): UseMutationResult<Hex, Error, {
22
+ recipient: Address;
23
+ } & WriteAccountOverride>;
@@ -0,0 +1,30 @@
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 UseResolveGasFeeArgs extends AirdropHookOptions<bigint> {
6
+ /** The account that will send `create*`. Fee overrides are per-creator. */
7
+ creator?: Address;
8
+ }
9
+ /**
10
+ * The per-claim fee a `create*` from `creator` would freeze into the new
11
+ * instance right now: their `setCustomFee` override when enabled, else the
12
+ * factory default.
13
+ *
14
+ * **This is the number `common.maxAcceptedGasFee` is checked against**, so it is
15
+ * what a create form should show before the creator picks a bound. Composing it
16
+ * from `useFactoryFees` and `useFactoryCustomFee` by hand means re-implementing
17
+ * the override-over-default rule in every consumer, and getting it wrong reads
18
+ * as "no fee" on exactly the accounts that have one.
19
+ *
20
+ * **A snapshot, not a quote.** `FEE_MANAGER_ROLE` can move it between this read
21
+ * and the create landing, which is the case `maxAcceptedGasFee` exists to catch -
22
+ * so treat this as what to *show*, never as a reason to skip the bound.
23
+ *
24
+ * @example
25
+ * const { data: fee } = useResolveGasFee({ creator: address });
26
+ * // then: params.common.maxAcceptedGasFee = fee ?? 0n
27
+ *
28
+ * @alpha
29
+ */
30
+ export declare function useResolveGasFee(args?: UseResolveGasFeeArgs): UseQueryResult<bigint, Error>;
@@ -1,6 +1,6 @@
1
1
  import { type UseMutationResult } from "@tanstack/react-query";
2
2
  import type { WriteAccountOverride } from "../airdrop-base.js";
3
- import { type CampaignSubmitterOption, type RotatedCampaign } from "../campaign.js";
3
+ import { type RotatedCampaign } from "../campaign.js";
4
4
  import type { CampaignRecipient } from "../types.js";
5
5
  import { type AirdropInstanceClientOptions } from "./_shared.js";
6
6
  /**
@@ -8,7 +8,7 @@ import { type AirdropInstanceClientOptions } from "./_shared.js";
8
8
  *
9
9
  * @alpha
10
10
  */
11
- export interface UseRotateMerkleRootArgs extends CampaignSubmitterOption, WriteAccountOverride {
11
+ export interface UseRotateMerkleRootArgs extends WriteAccountOverride {
12
12
  /** The FULL updated roster, carrying each account's new CUMULATIVE total — never the delta since the last root. */
13
13
  recipients: readonly CampaignRecipient[];
14
14
  }
@@ -23,6 +23,9 @@ export interface UseRotateMerkleRootArgs extends CampaignSubmitterOption, WriteA
23
23
  * rosters. A total below what an account already received pays nothing; a
24
24
  * rotation cannot claw back.
25
25
  *
26
+ * One relayer request per 32 recipients, issued concurrently; every leaf is
27
+ * submittable by any address.
28
+ *
26
29
  * The root is published LAST, so a failed rebuild leaves the previous campaign
27
30
  * intact and claimable. Once it lands the old proofs stop verifying:
28
31
  * **distribute the new entries to every recipient**, including those whose
@@ -19,6 +19,10 @@ export interface SignClaimAuthorizationArgs extends WriteAccountOverride {
19
19
  * dedupId, uint256 deadline)` authorization for an `ECDSAConfidentialAirdrop`
20
20
  * instance. The signer must hold `SIGNER_ROLE` on that instance.
21
21
  *
22
+ * **Never sign with an EIP-7702-delegated key.** A delegation switches verification to
23
+ * ERC-1271 against the delegate, so every outstanding voucher fails although the key
24
+ * keeps the role. See {@link signClaimAuthorization}.
25
+ *
22
26
  * `airdrop` and `chainId` come from the hook's own options, so a component
23
27
  * supplies only the claim fields.
24
28
  *
@@ -0,0 +1,29 @@
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 UseTokenOfArgs extends AirdropHookOptions<Address> {
6
+ /** The airdrop instance whose recorded token to resolve. */
7
+ airdrop?: Address;
8
+ }
9
+ /**
10
+ * Resolve the token the factory recorded for an airdrop instance at create - what
11
+ * `fundAirdrop` pulls, regardless of what the instance reports today.
12
+ *
13
+ * A zero address means the instance did not come from this factory.
14
+ *
15
+ * Cached indefinitely once non-zero: the mapping is written once, inside `createX`.
16
+ * A zero answer (not created yet, or not from this factory) is not cached forever: it
17
+ * stays stale and refetches on the next mount, focus or `refetch`, because the address
18
+ * can be created later by another tab, a server or the headless client, none of which
19
+ * reaches a hook invalidation. The create hooks in this subpath also invalidate this
20
+ * key for the address they deploy, so a zero already on screen updates immediately;
21
+ * the per-result `staleTime` covers every other create path, the same policy as
22
+ * {@link useIsAirdrop}. Override through `query.staleTime`.
23
+ *
24
+ * @example
25
+ * const { data: token } = useTokenOf({ airdrop: airdropAddress });
26
+ *
27
+ * @alpha
28
+ */
29
+ export declare function useTokenOf(args?: UseTokenOfArgs): UseQueryResult<Address, Error>;
@@ -0,0 +1,25 @@
1
+ import { type UseQueryResult } from "@tanstack/react-query";
2
+ import type { Hex } from "viem";
3
+ import type { UnwrapRequest } from "../airdrop-base.js";
4
+ import { type AirdropInstanceHookOptions } from "./_shared.js";
5
+ /** @alpha */
6
+ export type UseUnwrapRequestArgs = AirdropInstanceHookOptions<UnwrapRequest> & {
7
+ /** The `claimAndUnwrap` transaction hash. The query stays idle until it is set. */
8
+ hash: Hex | undefined;
9
+ };
10
+ /**
11
+ * The unwrap request a `claimAndUnwrap` started: waits for `hash` to be mined and
12
+ * decodes the instance's `ClaimedAndUnwrapInitiated` event.
13
+ *
14
+ * Hand `unwrapRequestId` to the wrapper's `finalizeUnwrap` to release the underlying
15
+ * ERC-20; with Zama's SDK, `sdk.createToken(wrapper).finalizeUnwrap(unwrapRequestId)`.
16
+ * The amount was already public from the claim transaction; finalizing discloses nothing new.
17
+ * A mined receipt does not change, so the result never goes stale.
18
+ *
19
+ * @example
20
+ * const unwrap = useEcdsaClaimAndUnwrap({ address: airdropAddress });
21
+ * const { data: request } = useUnwrapRequest({ address: airdropAddress, hash: unwrap.data });
22
+ *
23
+ * @alpha
24
+ */
25
+ export declare function useUnwrapRequest(args: UseUnwrapRequestArgs): UseQueryResult<UnwrapRequest, Error>;
@@ -8,6 +8,10 @@ import { type AirdropInstanceClientOptions } from "./_shared.js";
8
8
  * never appears in plaintext anywhere — the contract reads its own encrypted
9
9
  * balance and moves all of it.
10
10
  *
11
+ * Refused while the campaign is unpaused inside its claim window
12
+ * (`ClawbackRequiresPauseError`): pause with {@link useAirdropPause} first, or run it
13
+ * outside the window.
14
+ *
11
15
  * **Invalidates:** nothing. No read hook in this subpath's list exposes the
12
16
  * confidential pool balance (see {@link useFundAirdrop}'s TSDoc for the same
13
17
  * reasoning), so there is no cached query left stale by a withdrawal.
@@ -26,6 +26,10 @@ export interface InstanceRoleAssignment {
26
26
  disclosureAdmin?: Address;
27
27
  upgrader?: Address;
28
28
  merkleAdmin?: Address;
29
+ /**
30
+ * `SIGNER_ROLE`. A dedicated key that is never EIP-7702-delegated (or an ERC-1271 contract
31
+ * signer): a delegation invalidates every outstanding voucher.
32
+ */
29
33
  signer?: Address;
30
34
  /** Granting this always fails from DEFAULT_ADMIN — see planInstanceRoleSplit. */
31
35
  feeCollector?: Address;
@@ -66,20 +70,18 @@ export interface RoleGrantOutcome {
66
70
  * would only revert on-chain:
67
71
  *
68
72
  * 1. **`feeCollector`.** `FEE_COLLECTOR_ROLE` is self-administered
69
- * (`_setRoleAdmin(FEE_COLLECTOR_ROLE, FEE_COLLECTOR_ROLE)`,
70
- * `ConfidentialAirdropBase.sol:142`) — `DEFAULT_ADMIN_ROLE` cannot grant
71
- * it, only an existing member can, and the campaign creator never is one
72
- * (the factory's collector is seeded instead, `:143`).
73
+ * (`_setRoleAdmin(FEE_COLLECTOR_ROLE, FEE_COLLECTOR_ROLE)` at initialization)
74
+ * - `DEFAULT_ADMIN_ROLE` cannot grant it, only an existing member can, and
75
+ * the campaign creator never is one (the factory's collector is seeded instead).
73
76
  * 2. **`admin` set to the zero address.** Mirrors the contract's
74
- * unconditional `ZeroAdminGrant` guard (`_grantRole`, `:281`).
77
+ * unconditional `ZeroAdminGrant` guard in `_grantRole`.
75
78
  * 3. **A variant-only role on the wrong campaign** — `merkleAdmin` on an
76
79
  * ECDSA instance, or `signer` on a Merkle one. Neither role exists there;
77
80
  * the getter itself would revert.
78
81
  *
79
82
  * `admin`, when present, always produces two steps — grant the new admin,
80
83
  * then revoke `args.caller` — and the revoke is always ORDERED LAST, after
81
- * every other step in the plan. `_revokeRole`'s `LastAdmin` floor
82
- * (`ConfidentialAirdropBase.sol:261`) blocks removing the sole
84
+ * every other step in the plan. `_revokeRole`'s `LastAdmin` floor blocks removing the sole
83
85
  * `DEFAULT_ADMIN_ROLE` member, so revoking the creator first would strand
84
86
  * every later grant with no admin left to authorize it.
85
87
  *
@@ -96,6 +98,73 @@ export declare function planInstanceRoleSplit(args: {
96
98
  caller: Address;
97
99
  airdropType: "ecdsa" | "merkle";
98
100
  }): RoleGrantStep[];
101
+ /**
102
+ * One call of a Safe MultiSend batch: a zero-value `grantRole` / `revokeRole`
103
+ * on the instance.
104
+ *
105
+ * @alpha
106
+ */
107
+ export interface InstanceRoleCall {
108
+ to: Address;
109
+ data: Hex;
110
+ value: 0n;
111
+ }
112
+ /**
113
+ * The `bytes32` constants {@link encodeInstanceRoleSplit} encodes, keyed by
114
+ * {@link RoleName}. Read them off the instance (the `*_ROLE()` getters on
115
+ * `AirdropBaseClient` and its subclasses, or `useAirdropRoleConstants`);
116
+ * hashing the names yourself would silently grant a role nothing checks if a
117
+ * constant is ever renamed. `DEFAULT_ADMIN_ROLE` may be omitted: it is
118
+ * OpenZeppelin's fixed `bytes32(0)`.
119
+ *
120
+ * @alpha
121
+ */
122
+ export type InstanceRoleConstants = Partial<Record<RoleName, Hex>>;
123
+ /**
124
+ * Encode a {@link planInstanceRoleSplit} plan as the ordered calls a Safe
125
+ * executes in one MultiSend batch - the multisig counterpart of
126
+ * {@link grantInstanceRoles}.
127
+ *
128
+ * {@link grantInstanceRoles} must mine each step before simulating the next,
129
+ * which a threshold Safe cannot do in one session. A batch does not need to:
130
+ * its calls run in order inside one transaction, so the plan's
131
+ * grant-before-revoke ordering holds and the `LastAdmin` floor never sees the
132
+ * new admin missing. Plan with `caller` set to the Safe address, since the Safe
133
+ * is the account whose `DEFAULT_ADMIN_ROLE` the handoff revokes.
134
+ *
135
+ * The batch is atomic: one reverting call reverts the whole split, unlike the
136
+ * per-step outcomes of {@link grantInstanceRoles}.
137
+ *
138
+ * To keep the Safe as co-admin (the `revokeFromCreator: false` of
139
+ * {@link grantInstanceRoles}), drop the `revoke` `DEFAULT_ADMIN_ROLE` step
140
+ * from the plan before encoding.
141
+ *
142
+ * Each call is a plain CALL, so `operation` is omitted. Safe SDKs type `value`
143
+ * as a decimal string; map it when handing the calls over.
144
+ *
145
+ * @param args.airdrop The instance the calls target.
146
+ * @param args.steps The plan, in the order {@link planInstanceRoleSplit} returned it.
147
+ * @param args.roleConstants The role constants read off the instance.
148
+ * @returns One {@link InstanceRoleCall} per step, in plan order.
149
+ * @throws {@link InvalidArgumentError} when a step names a role whose constant is missing.
150
+ *
151
+ * @example
152
+ * const steps = planInstanceRoleSplit({ assignment, caller: safeAddress, airdropType: "merkle" });
153
+ * const calls = encodeInstanceRoleSplit({
154
+ * airdrop,
155
+ * steps,
156
+ * roleConstants: { PAUSER_ROLE: await client.PAUSER_ROLE() },
157
+ * });
158
+ * // One MultiSend transaction; Safe SDKs take `value` as a string.
159
+ * const transactions = calls.map((c) => ({ ...c, value: "0" }));
160
+ *
161
+ * @alpha
162
+ */
163
+ export declare function encodeInstanceRoleSplit(args: {
164
+ airdrop: Address;
165
+ steps: readonly RoleGrantStep[];
166
+ roleConstants: InstanceRoleConstants;
167
+ }): InstanceRoleCall[];
99
168
  /**
100
169
  * Execute an {@link InstanceRoleAssignment} against a live instance:
101
170
  * {@link planInstanceRoleSplit} the steps, then submit them one at a time,
@@ -113,6 +182,12 @@ export declare function planInstanceRoleSplit(args: {
113
182
  * intentionally: correctness of the ordering guarantee in
114
183
  * {@link planInstanceRoleSplit} depends on it.
115
184
  *
185
+ * **Safe / multisig signers cannot use this.** A threshold Safe cannot mine
186
+ * one step before proposing the next, so there is deliberately no
187
+ * `waitForReceipt: false` here. Plan with {@link planInstanceRoleSplit}
188
+ * (`caller` = the Safe) and submit {@link encodeInstanceRoleSplit}'s calls as
189
+ * one MultiSend batch instead.
190
+ *
116
191
  * **This is several transactions, not one — never atomic.** Each step is
117
192
  * caught individually, so one failing grant or revoke does not abort the
118
193
  * rest; inspect each {@link RoleGrantOutcome} rather than assuming