@tokenops/sdk 1.6.0 → 2.0.0-alpha.1

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 (194) hide show
  1. package/CHANGELOG.md +48 -8
  2. package/CONTRIBUTING.md +4 -2
  3. package/README.md +146 -90
  4. package/SUPPORT.md +47 -0
  5. package/dist/{chunk-HETNKZEE.js → chunk-335Z2W67.js} +1 -1
  6. package/dist/{chunk-BD4LZBVF.cjs → chunk-3QHNYEQD.cjs} +18 -17
  7. package/dist/chunk-456VDDA3.js +370 -0
  8. package/dist/{chunk-6WSNS3UV.js → chunk-4RAAATVY.js} +2 -4
  9. package/dist/{chunk-4KJ66YRH.js → chunk-4TSDNZQ3.js} +2 -2
  10. package/dist/{chunk-WUXUWTFW.cjs → chunk-5576FRT3.cjs} +31 -7
  11. package/dist/chunk-66IHPTOK.cjs +20 -0
  12. package/dist/{chunk-2RNW4MIJ.cjs → chunk-67OV5CX2.cjs} +0 -18
  13. package/dist/{chunk-PD7ME2BT.js → chunk-6GNR22OV.js} +2 -1
  14. package/dist/{chunk-VHSNYUYV.js → chunk-6NAVMWZQ.js} +5 -5
  15. package/dist/{chunk-EUKPOXWR.cjs → chunk-6PXXGACR.cjs} +17 -15
  16. package/dist/{chunk-WTK6JDSI.js → chunk-74QFZ5MK.js} +6 -4
  17. package/dist/{chunk-WOEETTH7.cjs → chunk-7AFPOE7N.cjs} +6 -6
  18. package/dist/{chunk-U57COLUE.js → chunk-AXIPOR3C.js} +2 -2
  19. package/dist/chunk-CTED3MTR.cjs +380 -0
  20. package/dist/{chunk-QKKBBH7I.js → chunk-DZHYSUGY.js} +28 -7
  21. package/dist/{chunk-V5D7BHW3.js → chunk-EAJ7SYHE.js} +6 -4
  22. package/dist/{chunk-SYHNZSZZ.cjs → chunk-FAXIIH5R.cjs} +3 -3
  23. package/dist/{chunk-7G7UOQV4.cjs → chunk-FGEB7RMY.cjs} +4 -4
  24. package/dist/chunk-GKKDAW44.js +8823 -0
  25. package/dist/{chunk-QET3Q4JP.js → chunk-H5POPGOZ.js} +8 -6
  26. package/dist/{chunk-VR3FREBX.cjs → chunk-JK4XMMKM.cjs} +3 -3
  27. package/dist/{chunk-CBRL2PJA.cjs → chunk-JQYC66TO.cjs} +12 -12
  28. package/dist/{chunk-YFIWCYHC.cjs → chunk-JZLGCTGK.cjs} +56 -54
  29. package/dist/{chunk-4ZCXK4VI.cjs → chunk-KFRDLKMA.cjs} +14 -14
  30. package/dist/chunk-KWFFIJYX.js +11 -0
  31. package/dist/chunk-M4IT6DNJ.cjs +608 -0
  32. package/dist/{chunk-43RFBQ73.cjs → chunk-MVDQMV25.cjs} +29 -32
  33. package/dist/chunk-NCVX3K2N.cjs +161 -0
  34. package/dist/chunk-NWFMBLSQ.js +4 -0
  35. package/dist/chunk-O5676ZWY.js +585 -0
  36. package/dist/chunk-PUX5VXDB.cjs +8845 -0
  37. package/dist/{chunk-DRSPMIZ7.js → chunk-QBJ7O2B4.js} +1 -11
  38. package/dist/{chunk-UJS4N2XM.cjs → chunk-TEBM66WA.cjs} +12 -12
  39. package/dist/{chunk-TN65XNTI.js → chunk-TWT3STIX.js} +8 -6
  40. package/dist/chunk-UG5RKLU2.cjs +6 -0
  41. package/dist/chunk-VP2B4WM2.js +154 -0
  42. package/dist/{chunk-BK7YIVLK.cjs → chunk-WJLECC22.cjs} +75 -73
  43. package/dist/{chunk-NFW7AUEX.js → chunk-WMACINXO.js} +1 -1
  44. package/dist/{chunk-AYGRYDBX.cjs → chunk-XAYGD4E4.cjs} +37 -35
  45. package/dist/{chunk-46T67CE2.js → chunk-ZA673I3O.js} +1 -1
  46. package/dist/{chunk-YPYCWLYP.js → chunk-ZWXJTBZO.js} +1 -1
  47. package/dist/core/addresses.d.ts +14 -2
  48. package/dist/core/brands.d.ts +8 -2
  49. package/dist/core/errors.d.ts +71 -10
  50. package/dist/core/preflight.d.ts +1 -0
  51. package/dist/fhe/erc7984-abi.d.ts +1 -0
  52. package/dist/fhe/index.cjs +64 -63
  53. package/dist/fhe/index.js +7 -6
  54. package/dist/fhe/operators.d.ts +22 -5
  55. package/dist/fhe/react/index.cjs +13 -12
  56. package/dist/fhe/react/index.js +6 -5
  57. package/dist/fhe/types.d.ts +6 -0
  58. package/dist/fhe-airdrop/abis/{cloneable.d.ts → airdrop-base.d.ts} +254 -396
  59. package/dist/fhe-airdrop/abis/compliance.d.ts +422 -0
  60. package/dist/fhe-airdrop/abis/ecdsa.d.ts +1316 -0
  61. package/dist/fhe-airdrop/abis/factory.d.ts +1319 -190
  62. package/dist/fhe-airdrop/abis/index.d.ts +5 -2
  63. package/dist/fhe-airdrop/abis/merkle.d.ts +1238 -0
  64. package/dist/fhe-airdrop/advanced/index.cjs +11 -12
  65. package/dist/fhe-airdrop/advanced/index.d.cts +34 -11
  66. package/dist/fhe-airdrop/advanced/index.d.ts +34 -11
  67. package/dist/fhe-airdrop/advanced/index.js +3 -8
  68. package/dist/fhe-airdrop/advanced/react/index.cjs +37 -50
  69. package/dist/fhe-airdrop/advanced/react/index.d.cts +9 -3
  70. package/dist/fhe-airdrop/advanced/react/index.d.ts +9 -3
  71. package/dist/fhe-airdrop/advanced/react/index.js +36 -50
  72. package/dist/fhe-airdrop/advanced/react/usePredictEcdsaAirdropAddress.d.ts +33 -0
  73. package/dist/fhe-airdrop/advanced/react/usePredictMerkleAirdropAddress.d.ts +27 -0
  74. package/dist/fhe-airdrop/airdrop-base.d.ts +600 -0
  75. package/dist/fhe-airdrop/campaign.d.ts +297 -0
  76. package/dist/fhe-airdrop/compliance.d.ts +302 -0
  77. package/dist/fhe-airdrop/constants.d.ts +33 -0
  78. package/dist/fhe-airdrop/ecdsa.d.ts +263 -0
  79. package/dist/fhe-airdrop/encryption.d.ts +92 -14
  80. package/dist/fhe-airdrop/errors.d.ts +245 -15
  81. package/dist/fhe-airdrop/factory.d.ts +533 -274
  82. package/dist/fhe-airdrop/guards.d.ts +273 -0
  83. package/dist/fhe-airdrop/index.cjs +1131 -74
  84. package/dist/fhe-airdrop/index.d.cts +46 -8
  85. package/dist/fhe-airdrop/index.d.ts +46 -8
  86. package/dist/fhe-airdrop/index.js +931 -8
  87. package/dist/fhe-airdrop/merkle-tree.d.ts +103 -0
  88. package/dist/fhe-airdrop/merkle.d.ts +180 -0
  89. package/dist/fhe-airdrop/react/_shared.d.ts +144 -71
  90. package/dist/fhe-airdrop/react/index.cjs +399 -459
  91. package/dist/fhe-airdrop/react/index.d.cts +53 -74
  92. package/dist/fhe-airdrop/react/index.d.ts +53 -74
  93. package/dist/fhe-airdrop/react/index.js +261 -374
  94. package/dist/fhe-airdrop/react/useAirdropGasFee.d.ts +10 -5
  95. package/dist/fhe-airdrop/react/useAirdropHasRole.d.ts +15 -6
  96. package/dist/fhe-airdrop/react/useAirdropPause.d.ts +30 -0
  97. package/dist/fhe-airdrop/react/useAirdropPaused.d.ts +17 -0
  98. package/dist/fhe-airdrop/react/useAirdropToken.d.ts +11 -4
  99. package/dist/fhe-airdrop/react/useAirdropWindow.d.ts +37 -0
  100. package/dist/fhe-airdrop/react/useClaimedAmount.d.ts +29 -0
  101. package/dist/fhe-airdrop/react/useComplianceManager.d.ts +16 -0
  102. package/dist/fhe-airdrop/react/useCreateEcdsaAirdrop.d.ts +28 -0
  103. package/dist/fhe-airdrop/react/useCreateMerkleAirdrop.d.ts +25 -0
  104. package/dist/fhe-airdrop/react/useEcdsaClaim.d.ts +23 -0
  105. package/dist/fhe-airdrop/react/useEffectiveUpgradeable.d.ts +20 -0
  106. package/dist/fhe-airdrop/react/useExtendClaimWindow.d.ts +17 -12
  107. package/dist/fhe-airdrop/react/useFactoryFees.d.ts +28 -0
  108. package/dist/fhe-airdrop/react/useFactoryRegistry.d.ts +35 -0
  109. package/dist/fhe-airdrop/react/useFundAirdrop.d.ts +35 -0
  110. package/dist/fhe-airdrop/react/useGrantInstanceRoles.d.ts +35 -0
  111. package/dist/fhe-airdrop/react/useMerkleClaim.d.ts +34 -0
  112. package/dist/fhe-airdrop/react/useMerkleRoot.d.ts +15 -0
  113. package/dist/fhe-airdrop/react/useSetMerkleRoot.d.ts +22 -0
  114. package/dist/fhe-airdrop/react/useWithdrawConfidential.d.ts +23 -0
  115. package/dist/fhe-airdrop/roles.d.ts +147 -0
  116. package/dist/fhe-airdrop/types.d.ts +67 -65
  117. package/dist/fhe-disperse/index.cjs +70 -68
  118. package/dist/fhe-disperse/index.js +10 -8
  119. package/dist/fhe-disperse/react/index.cjs +94 -92
  120. package/dist/fhe-disperse/react/index.js +13 -11
  121. package/dist/fhe-vesting/advanced/index.cjs +10 -8
  122. package/dist/fhe-vesting/advanced/index.js +8 -6
  123. package/dist/fhe-vesting/advanced/react/index.cjs +16 -14
  124. package/dist/fhe-vesting/advanced/react/index.js +13 -11
  125. package/dist/fhe-vesting/index.cjs +74 -72
  126. package/dist/fhe-vesting/index.js +11 -9
  127. package/dist/fhe-vesting/react/index.cjs +228 -226
  128. package/dist/fhe-vesting/react/index.js +15 -13
  129. package/dist/index.cjs +100 -87
  130. package/dist/index.js +3 -2
  131. package/dist/testnet-faucet/index.cjs +57 -55
  132. package/dist/testnet-faucet/index.js +7 -5
  133. package/dist/testnet-faucet/react/index.cjs +55 -53
  134. package/dist/testnet-faucet/react/index.js +9 -7
  135. package/package.json +6 -2
  136. package/dist/chunk-CNP4L3GF.js +0 -105
  137. package/dist/chunk-KSDSXJ34.js +0 -41
  138. package/dist/chunk-LBWRFZR3.cjs +0 -1681
  139. package/dist/chunk-OL6SHN3D.cjs +0 -44
  140. package/dist/chunk-S2XD75JM.js +0 -1637
  141. package/dist/chunk-SPGSO5SO.cjs +0 -1644
  142. package/dist/chunk-UE5XK2SY.js +0 -1673
  143. package/dist/chunk-WO72UBQD.cjs +0 -109
  144. package/dist/fhe-airdrop/advanced/factory-advanced.d.ts +0 -53
  145. package/dist/fhe-airdrop/advanced/react/usePredictAirdropAddress.d.ts +0 -49
  146. package/dist/fhe-airdrop/airdrop.d.ts +0 -317
  147. package/dist/fhe-airdrop/react/useAccessClaimAmount.d.ts +0 -37
  148. package/dist/fhe-airdrop/react/useAirdropCanExtendClaimWindow.d.ts +0 -7
  149. package/dist/fhe-airdrop/react/useAirdropClaimTypehash.d.ts +0 -10
  150. package/dist/fhe-airdrop/react/useAirdropClaimedSignatures.d.ts +0 -16
  151. package/dist/fhe-airdrop/react/useAirdropDeploymentBlockNumber.d.ts +0 -6
  152. package/dist/fhe-airdrop/react/useAirdropDomainSeparator.d.ts +0 -8
  153. package/dist/fhe-airdrop/react/useAirdropEndTime.d.ts +0 -10
  154. package/dist/fhe-airdrop/react/useAirdropFactoryCustomFee.d.ts +0 -17
  155. package/dist/fhe-airdrop/react/useAirdropFactoryDefaultGasFee.d.ts +0 -10
  156. package/dist/fhe-airdrop/react/useAirdropFactoryDisableCustomFee.d.ts +0 -15
  157. package/dist/fhe-airdrop/react/useAirdropFactoryFeeCollector.d.ts +0 -11
  158. package/dist/fhe-airdrop/react/useAirdropFactoryInitCodeHash.d.ts +0 -17
  159. package/dist/fhe-airdrop/react/useAirdropFactorySetCustomFee.d.ts +0 -17
  160. package/dist/fhe-airdrop/react/useAirdropFactorySetDefaultGasFee.d.ts +0 -16
  161. package/dist/fhe-airdrop/react/useAirdropFactorySetFeeCollector.d.ts +0 -15
  162. package/dist/fhe-airdrop/react/useAirdropGrantRole.d.ts +0 -16
  163. package/dist/fhe-airdrop/react/useAirdropHasClaimEnded.d.ts +0 -7
  164. package/dist/fhe-airdrop/react/useAirdropHasClaimStarted.d.ts +0 -7
  165. package/dist/fhe-airdrop/react/useAirdropIsClaimWindowActive.d.ts +0 -9
  166. package/dist/fhe-airdrop/react/useAirdropIsPaused.d.ts +0 -8
  167. package/dist/fhe-airdrop/react/useAirdropIsSignatureClaimed.d.ts +0 -22
  168. package/dist/fhe-airdrop/react/useAirdropIsSignatureValid.d.ts +0 -51
  169. package/dist/fhe-airdrop/react/useAirdropRevokeRole.d.ts +0 -15
  170. package/dist/fhe-airdrop/react/useAirdropStartTime.d.ts +0 -10
  171. package/dist/fhe-airdrop/react/useAirdropWithdrawGasFee.d.ts +0 -15
  172. package/dist/fhe-airdrop/react/useAirdropWithdrawOtherConfidentialToken.d.ts +0 -14
  173. package/dist/fhe-airdrop/react/useAirdropWithdrawOtherToken.d.ts +0 -14
  174. package/dist/fhe-airdrop/react/useClaim.d.ts +0 -39
  175. package/dist/fhe-airdrop/react/useConfidentialAirdropFactoryImplementation.d.ts +0 -11
  176. package/dist/fhe-airdrop/react/useCreateAndFundConfidentialAirdrop.d.ts +0 -41
  177. package/dist/fhe-airdrop/react/useCreateAndFundConfidentialAirdropAndGetAddress.d.ts +0 -48
  178. package/dist/fhe-airdrop/react/useCreateConfidentialAirdrop.d.ts +0 -29
  179. package/dist/fhe-airdrop/react/useCreateConfidentialAirdropAndGetAddress.d.ts +0 -31
  180. package/dist/fhe-airdrop/react/useDisableCustomFee.d.ts +0 -14
  181. package/dist/fhe-airdrop/react/useFactoryCustomFee.d.ts +0 -17
  182. package/dist/fhe-airdrop/react/useFactoryDefaultGasFee.d.ts +0 -10
  183. package/dist/fhe-airdrop/react/useFactoryFeeCollector.d.ts +0 -11
  184. package/dist/fhe-airdrop/react/useFactoryInitCodeHash.d.ts +0 -17
  185. package/dist/fhe-airdrop/react/useFundConfidentialAirdrop.d.ts +0 -33
  186. package/dist/fhe-airdrop/react/usePreflightCreateAirdrop.d.ts +0 -51
  187. package/dist/fhe-airdrop/react/useSetCustomFee.d.ts +0 -16
  188. package/dist/fhe-airdrop/react/useSetDefaultGasFee.d.ts +0 -15
  189. package/dist/fhe-airdrop/react/useSetFeeCollector.d.ts +0 -14
  190. package/dist/fhe-airdrop/react/useSetPaused.d.ts +0 -14
  191. package/dist/fhe-airdrop/react/useSignClaimAuthorization.d.ts +0 -31
  192. package/dist/fhe-airdrop/react/useWithdraw.d.ts +0 -12
  193. package/dist/fhe-airdrop/react/useWithdrawOtherConfidentialToken.d.ts +0 -13
  194. package/dist/fhe-airdrop/react/useWithdrawOtherToken.d.ts +0 -13
@@ -1,364 +1,623 @@
1
1
  import type { Account, Address, Hex, PublicClient, WalletClient } from "viem";
2
- import { type TxHash } from "../core/brands.js";
3
2
  import { type SdkTelemetry } from "../core/telemetry.js";
4
- import { type Encryptor, type EncryptorSource } from "./encryption.js";
5
- import type { AirdropParams, CreateAirdropPreflightReport, CustomFee, EncryptedInput } from "./types.js";
3
+ import type { EncryptedInput } from "../fhe/types.js";
4
+ import type { WriteAccountOverride } from "./airdrop-base.js";
5
+ import { type DeploymentMode } from "./constants.js";
6
+ import { type EncryptorSource } from "./encryption.js";
7
+ 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
+ }
17
+ /**
18
+ * Convert the SDK's `"clone"` / `"uups"` name to the contract's
19
+ * `DeploymentMode` ordinal.
20
+ *
21
+ * The string union is the SDK's currency everywhere else; the ordinal exists
22
+ * only at the ABI edge. Keeping the conversion in one exported function makes
23
+ * an inverted mapping a one-line test failure rather than a silently
24
+ * upgradeable instance.
25
+ *
26
+ * @param mode Deployment shell to select.
27
+ * @returns The `uint8` the factory expects.
28
+ */
29
+ export declare function toDeploymentModeOrdinal(mode: DeploymentMode): number;
30
+ /**
31
+ * Project {@link CommonAirdropParams} onto the contract's struct.
32
+ *
33
+ * The two shapes are already identical, so this is a re-assembly rather than a
34
+ * translation — and that is the point: it pins the field set and their order to
35
+ * the vendored ABI, so a struct change in a future contract commit surfaces
36
+ * here instead of as a silently mis-encoded tuple.
37
+ *
38
+ * @param common Create-time fields shared by both airdrop variants.
39
+ * @returns The struct in the ABI's declared field order.
40
+ */
41
+ export declare function toContractCommonParams(common: CommonAirdropParams): ContractCommonParams;
42
+ /** @alpha */
6
43
  export interface ConfidentialAirdropFactoryClientConfig {
7
44
  publicClient: PublicClient;
8
45
  walletClient?: WalletClient;
9
- /** Override the on-chain factory address. Falls back to `getFheAirdropFactoryAddress(chainId)`. */
46
+ /** Override the on-chain factory address. Falls back to {@link requireFheAirdropFactoryAddress} keyed by chain id. */
10
47
  address?: Address;
11
- /** Explicit chain id used to look up the default factory address. Defaults to `publicClient.chain?.id`. */
48
+ /**
49
+ * Chain id used for the support check and the default address lookup.
50
+ * Defaults to `publicClient.chain?.id`; one of the two must resolve, because
51
+ * the client refuses to talk to a chain it cannot name.
52
+ */
12
53
  chainId?: number | undefined;
13
54
  /**
14
- * Default encryptor used by fund methods. Optional at construction time but
15
- * required at call time for `createAndFundConfidentialAirdrop` and `fundConfidentialAirdrop`.
55
+ * Default encryptor for the fund methods. Optional here and required only at
56
+ * call time, so a React host can hand over a lazy factory that resolves
57
+ * against whatever relayer the provider currently holds.
16
58
  */
17
59
  encryptor?: EncryptorSource | undefined;
18
60
  /**
19
61
  * Optional telemetry sink. When provided, the SDK emits a `fhe-airdrop.client.init`
20
62
  * event on construction and brackets public write methods with named spans
21
- * (`fhe-airdrop.factory.createConfidentialAirdrop`, `…createAndFundConfidentialAirdrop`, …).
22
- * Defaults to a no-op — zero overhead, zero leakage of consumer-side identifiers.
63
+ * (`fhe-airdrop.factory.createEcdsaAirdrop`, …). Defaults to a no-op — zero
64
+ * overhead, zero leakage of consumer-side identifiers.
23
65
  *
24
66
  * The shape is structurally compatible with `NoopTelemetry` / `ConsoleTelemetry` /
25
67
  * `TokenOpsTelemetry` exported from `@tokenops/sdk/telemetry`.
26
68
  */
27
69
  telemetry?: SdkTelemetry | undefined;
28
70
  }
29
- export interface CreateAirdropArgs {
30
- params: AirdropParams;
31
- /** Any unique 32-byte salt. Combined with msg.sender to derive the CREATE2 salt. */
32
- userSalt: Hex;
33
- account?: Account | Address | undefined;
34
- }
35
- /** Common fields for {@link CreateAndFundAirdropArgs} — discriminated below. */
36
- interface CreateAndFundAirdropArgsCommon {
37
- params: AirdropParams;
38
- userSalt: Hex;
39
- /**
40
- * Plaintext token amount. The SDK encrypts via `encryptor`. Mutually
41
- * exclusive with `encryptedInput` — supply exactly one.
42
- */
43
- amount?: bigint;
44
- encryptor?: Encryptor;
45
- /** Pre-encrypted input — mutually exclusive with `amount`. */
46
- encryptedInput?: EncryptedInput;
47
- account?: Account | Address;
48
- }
49
71
  /**
50
- * Discriminated union: provide EITHER an encryptor (the SDK encrypts `amount`)
51
- * OR a pre-built `encryptedInput`. Passing both is a compile error.
72
+ * The encrypted pool amount for a fund-style write, as exactly one of a
73
+ * plaintext the SDK encrypts or a ciphertext the caller already built.
74
+ *
75
+ * The union is discriminated with `never` rather than left as two optional
76
+ * fields so that passing both is a compile error: a stale plaintext sitting
77
+ * next to a pre-built handle is a silent divergence from what actually moves
78
+ * on-chain.
79
+ *
80
+ * @alpha
52
81
  */
53
- export type CreateAndFundAirdropArgs = (CreateAndFundAirdropArgsCommon & {
54
- /** Plaintext token amount. The SDK encrypts via `encryptor`. */
82
+ export type FundInput = {
83
+ /** Base units of the ERC-7984 token (6 decimals on the TokenOps tokens). */
55
84
  amount: bigint;
56
- encryptor?: Encryptor | undefined;
85
+ /** Overrides the client's default encryptor for this call. */
86
+ encryptor?: EncryptorSource | undefined;
57
87
  encryptedInput?: never;
58
- }) | (CreateAndFundAirdropArgsCommon & {
59
- /** Pre-encrypted input — bypasses `encryptor`. */
88
+ } | {
89
+ /** Must be bound to `(factoryAddress, funderAddress)` — the factory is what calls `FHE.fromExternal`. */
60
90
  encryptedInput: EncryptedInput;
61
91
  amount?: never;
62
92
  encryptor?: never;
63
- });
64
- /** Common fields for {@link FundAirdropArgs} — discriminated below. */
65
- interface FundAirdropArgsCommon {
66
- token: Address;
67
- params: AirdropParams;
68
- userSalt: Hex;
93
+ };
94
+ /**
95
+ * Inputs for the address-prediction oracles.
96
+ *
97
+ * `params` is required because the ABI declares it, but the factory ignores
98
+ * it: an instance address commits only to `(implementation-for-variant, mode,
99
+ * deployer, userSalt)`. Changing the token, window, signer or root does NOT
100
+ * move the predicted address — only `mode`, `deployer` and `userSalt` do.
101
+ *
102
+ * @alpha
103
+ */
104
+ export interface PredictArgs<P> {
105
+ params: P;
106
+ mode: DeploymentMode;
107
+ /** The account that will send `create*` — CREATE2 salts are per-deployer. */
69
108
  deployer: Address;
70
- /**
71
- * Gas fee in wei that was active for the deployer at airdrop creation time.
72
- * Must match the value used when the clone was created (custom fee if one was
73
- * set for that deployer, otherwise `defaultGasFee`). Determines the CREATE2
74
- * salt and the predicted clone address — a mismatch will target the wrong address.
75
- *
76
- * Obtain via `factory.getCustomFee(deployer)` or `factory.defaultGasFee()`.
77
- */
78
- gasFee: bigint;
79
- /**
80
- * Plaintext token amount. The SDK encrypts via `encryptor`. Mutually
81
- * exclusive with `encryptedInput` — supply exactly one.
82
- */
83
- amount?: bigint;
84
- encryptor?: Encryptor;
85
- /** Pre-encrypted input — mutually exclusive with `amount`. */
86
- encryptedInput?: EncryptedInput;
87
- account?: Account | Address;
109
+ userSalt: Hex;
88
110
  }
89
111
  /**
90
- * Discriminated union: provide EITHER an encryptor (the SDK encrypts `amount`)
91
- * OR a pre-built `encryptedInput`. Passing both is a compile error.
112
+ * Inputs for the init-code-hash reads.
113
+ *
114
+ * Narrower than {@link PredictArgs} on purpose: the hash covers the proxy
115
+ * init-code only, so `deployer` and `userSalt` cannot affect it and are not
116
+ * worth making a caller invent.
117
+ *
118
+ * @alpha
92
119
  */
93
- export type FundAirdropArgs = (FundAirdropArgsCommon & {
94
- /** Plaintext token amount. The SDK encrypts via `encryptor`. */
95
- amount: bigint;
96
- encryptor?: Encryptor | undefined;
97
- encryptedInput?: never;
98
- }) | (FundAirdropArgsCommon & {
99
- /** Pre-encrypted input — bypasses `encryptor`. */
100
- encryptedInput: EncryptedInput;
101
- amount?: never;
102
- encryptor?: never;
103
- });
120
+ export type InitCodeHashArgs<P> = Pick<PredictArgs<P>, "params" | "mode">;
104
121
  /**
105
- * Rich-return shape from {@link ConfidentialAirdropFactoryClient.createConfidentialAirdrop}
106
- * and {@link ConfidentialAirdropFactoryClient.createAndFundConfidentialAirdrop}.
122
+ * The on-chain `CustomFee` struct returned by `getCustomFee`.
107
123
  *
108
- * Branded `hash: TxHash` and `airdrop: Address` so consumers can't accidentally
109
- * swap them at the destructure site.
124
+ * @alpha
110
125
  */
111
- export interface CreateAirdropResult {
112
- /** Tx hash that deployed the clone. */
113
- hash: TxHash;
114
- /** Address of the deployed clone, parsed from the `ConfidentialAirdropCreated` event. */
115
- airdrop: Address;
126
+ export interface CustomFee {
127
+ enabled: boolean;
128
+ /** Wei, `uint96` on-chain. */
129
+ gasFee: bigint;
116
130
  }
117
- /** Arguments for {@link ConfidentialAirdropFactoryClient.preflightCreateAirdrop}. */
118
- export interface PreflightCreateAirdropArgs {
119
- /** The airdrop parameters that will be passed to the create call. */
120
- params: AirdropParams;
121
- /** The address that will send the create transaction (`msg.sender`). */
122
- creator: Address;
123
- /**
124
- * The `userSalt` planned for the create call. Optional — when supplied, the
125
- * preflight also predicts the CREATE2 clone address and reports whether a
126
- * clone already exists there (a repeat create with the same salt reverts).
127
- */
128
- userSalt?: Hex;
129
- /**
130
- * Set `true` when the plan is `createAndFundConfidentialAirdrop` (or a
131
- * follow-up `fundConfidentialAirdrop`). Funding pulls tokens from the
132
- * creator via `confidentialTransferFrom`, so a missing factory-operator
133
- * approval becomes a blocker. When `false`/omitted, the operator state is
134
- * still reported (`isFactoryOperator`) but does not block a create-only tx.
135
- */
136
- funding?: boolean;
131
+ /**
132
+ * The on-chain `CompliancePolicy` struct returned by `getCompliancePolicy`.
133
+ *
134
+ * @alpha
135
+ */
136
+ export interface CompliancePolicy {
137
+ /** Whether this creator has a policy distinct from `effectiveDelegateToCompliance`'s factory default. */
138
+ overridden: boolean;
139
+ delegateToCompliance: boolean;
137
140
  }
138
- export interface PredictAirdropArgs {
139
- params: AirdropParams;
140
- userSalt: Hex;
141
- deployer: Address;
142
- /**
143
- * Gas fee in wei to embed into the CREATE2 salt computation.
144
- * Must match the fee that will be (or was) active for `deployer` at deploy time:
145
- * custom fee if `getCustomFee(deployer).enabled`, otherwise `defaultGasFee`.
146
- *
147
- * Passing the wrong value produces a different address than the deployed clone.
148
- */
149
- gasFee: bigint;
141
+ /**
142
+ * The on-chain `UpgradeabilityPolicy` struct returned by `getUpgradeabilityPolicy`.
143
+ *
144
+ * @alpha
145
+ */
146
+ export interface UpgradeabilityPolicy {
147
+ /** Whether this creator has a policy distinct from `defaultUpgradeable`'s factory default. */
148
+ overridden: boolean;
149
+ allowed: boolean;
150
150
  }
151
151
  /**
152
- * Typed wrapper around the deployed `ConfidentialAirdropFactory`. Resolves the
153
- * factory address per chain and encapsulates FHE encryption for fund methods.
152
+ * States the live role-concentration fact plainly: this management surface
153
+ * wraps every fee, implementation, compliance and upgradeability admin entry
154
+ * point the factory exposes, and on the deployed Sepolia factory all of them
155
+ * are reachable from one key. See the class TSDoc for the full context —
156
+ * this string exists so a consumer can surface the same fact in their own UI
157
+ * (e.g. an admin panel warning) without re-deriving it.
158
+ *
159
+ * **Check it rather than trust it.** This is a fixed string describing one
160
+ * deployment at one point in time; role membership is mutable. The claim is
161
+ * verifiable on whatever chain you are actually connected to by reading the
162
+ * five role constants ({@link ConfidentialAirdropFactoryClient.DEFAULT_ADMIN_ROLE},
163
+ * `FEE_MANAGER_ROLE`, `IMPL_MANAGER_ROLE`, `COMPLIANCE_WIRING_ROLE`,
164
+ * `UPGRADE_MANAGER_ROLE`) and passing each to
165
+ * {@link ConfidentialAirdropFactoryClient.getRoleMembers} - see that method's
166
+ * example for the whole check in six lines. A UI that shows this warning
167
+ * should prefer the live reads and fall back to this string only when it has
168
+ * no client to read with.
169
+ *
170
+ * @alpha
171
+ */
172
+ export declare const FACTORY_ROLE_CONCENTRATION_NOTE = "The live factory holds all five factory roles on the deployer account.";
173
+ /**
174
+ * Typed wrapper around the deployed airdrop v2 `AirdropFactory`.
175
+ *
176
+ * Encapsulates the three things a raw ABI call leaves to the caller: FHE
177
+ * encryption of the pool amount (bound to the factory, which is the contract
178
+ * that calls `FHE.fromExternal`), the `DeploymentMode` / `DedupMode` ordinal
179
+ * mapping, and resolving the deployed instance plus its compliance-manager
180
+ * clone out of the receipt.
181
+ *
182
+ * Also exposes the factory's fee, implementation, compliance and
183
+ * upgradeability admin surface. On the live Sepolia deployment all five
184
+ * factory roles (admin, feeManager, implManager, complianceWiring,
185
+ * upgradeManager) and the fee collector are held by the deploying account —
186
+ * the deploy script has no env var to split them. See
187
+ * {@link FACTORY_ROLE_CONCENTRATION_NOTE}. Nothing below implies separation
188
+ * exists; splitting roles is a contracts-repo decision, not something this
189
+ * client can arrange.
154
190
  *
155
191
  * @example
156
- * const factory = createConfidentialAirdropFactoryClient({ publicClient, walletClient, encryptor });
157
- * const hash = await factory.createConfidentialAirdrop({ params, userSalt });
192
+ * const factory = createConfidentialAirdropFactoryClient({ publicClient, walletClient });
193
+ * const { airdrop, complianceManager } = await factory.createMerkleAirdrop({
194
+ * params, mode: "clone", userSalt,
195
+ * });
196
+ *
197
+ * @alpha
158
198
  */
159
199
  export declare class ConfidentialAirdropFactoryClient {
160
200
  #private;
161
201
  readonly publicClient: PublicClient;
162
202
  readonly walletClient?: WalletClient;
163
203
  readonly address: Address;
204
+ readonly chainId: number;
164
205
  readonly encryptor?: EncryptorSource;
165
206
  constructor(config: ConfidentialAirdropFactoryClientConfig);
166
207
  /**
167
- * Deploy a new airdrop clone via the factory and return its address, parsed
168
- * from the `ConfidentialAirdropCreated` event in the tx receipt.
208
+ * Deploy an ECDSA-authorised airdrop instance.
169
209
  *
170
- * **This is the headline factory entry point.** It returns one rich-return
171
- * shape: most consumers want the deployed address back, and the SDK already
172
- * waits for and parses the event, so there's no reason to make consumers do
173
- * it themselves.
210
+ * The factory injects `admin = msg.sender`; there is no admin field to set.
211
+ * Split roles afterwards rather than at create time.
174
212
  *
175
- * Internally: `writeContract` → {@link PublicClient.waitForTransactionReceipt} →
176
- * `parseEventLogs({ eventName: "ConfidentialAirdropCreated" })`.
213
+ * Guardrailed before the send: the stock-wrapper probe when
214
+ * `params.common.unwrappable` is set, and a salt-collision
215
+ * check on the predicted address. See {@link preflightCreate} for the
216
+ * read-only form of the same checks.
177
217
  *
178
- * For genuine pre-mine prediction (rare), import
179
- * `ConfidentialAirdropFactoryAdvancedClient` from
180
- * `@tokenops/sdk/fhe-airdrop/advanced` and call `predictAirdropAddress`.
218
+ * @param args Instance parameters, deployment shell and the caller's salt.
219
+ * @returns The tx hash, the deployed instance and its compliance-manager clone.
220
+ * @throws {@link NonStockWrapperError} when `unwrappable` is set on a token that is not a stock wrapper.
221
+ * @throws {@link SaltCollisionError} when this `(mode, deployer, userSalt)` tuple already deployed an instance.
222
+ * @throws {@link UpgradeabilityNotAllowedError} when `mode: "uups"` is not permitted for the creator.
223
+ * @throws {@link InvalidArgumentError} when `userSalt` was already consumed by this creator and mode.
181
224
  *
182
225
  * @example
183
- * const { hash, airdrop } = await factory.createConfidentialAirdrop({
184
- * params, userSalt,
226
+ * const { airdrop } = await factory.createEcdsaAirdrop({
227
+ * params: { common, signer, dedupMode: "perAddress" },
228
+ * mode: "clone",
229
+ * userSalt: keccak256(toBytes("campaign-42")),
185
230
  * });
186
231
  */
187
- createConfidentialAirdrop(args: CreateAirdropArgs): Promise<CreateAirdropResult>;
232
+ createEcdsaAirdrop(args: CreateAirdropArgs<EcdsaAirdropParams> & WriteAccountOverride): Promise<CreateAirdropResult>;
188
233
  /**
189
- * Deploy and fund a new airdrop in a single transaction, and return its
190
- * address. The SDK encrypts `amount` into an `externalEuint64` before
191
- * passing to the contract.
234
+ * Deploy a Merkle-proof airdrop instance.
192
235
  *
193
- * Prerequisites: caller must have set this factory as an operator on the
194
- * token: `token.setOperator(factoryAddress, deadline)`.
236
+ * `mode: "uups"` is refused outright, before any RPC: the Merkle
237
+ * implementation's per-account claim state occupies an ERC-7201 namespaced
238
+ * slot that an earlier revision typed differently, so an in-place upgrade
239
+ * across that change reinterprets live storage. See
240
+ * {@link MerkleUupsUnsupportedError}.
195
241
  *
196
- * **Silent-zero transfers.** ERC-7984 transfers do not revert when the sender's encrypted balance is insufficient — the transfer succeeds and moves an encrypted zero instead. This is by design: reverting would leak balance information. The transaction receipt alone cannot tell you whether value actually moved.
242
+ * @param args Instance parameters, deployment shell and the caller's salt.
243
+ * @returns The tx hash, the deployed instance and its compliance-manager clone.
244
+ * @throws {@link MerkleUupsUnsupportedError} when `mode` is `"uups"`.
245
+ * @throws {@link NonStockWrapperError} when `unwrappable` is set on a token that is not a stock wrapper.
246
+ * @throws {@link SaltCollisionError} when this `(mode, deployer, userSalt)` tuple already deployed an instance.
247
+ * @throws {@link InvalidArgumentError} when the root is zero and `isMerkleRootMutable` is false, or the salt was already used.
248
+ */
249
+ createMerkleAirdrop(args: CreateAirdropArgs<MerkleAirdropParams> & WriteAccountOverride): Promise<CreateAirdropResult>;
250
+ /**
251
+ * Deploy an ECDSA airdrop and seed its pool in the same transaction.
197
252
  *
198
- * **Amount units:** TokenOps confidential (ERC-7984) tokens use a 6-decimals
199
- * convention (1 token = 1_000_000 base units), not the 18 decimals typical of
200
- * ERC-20. Amount parameters are base units of the token's actual decimals:
201
- * for the CTTT test token (6 decimals) `1_000_000n` = 1 CTTT, while the
202
- * transparent TTT test token uses 18 decimals.
253
+ * Prerequisite: the funder must have approved this factory as an ERC-7984
254
+ * operator on the token (`token.setOperator(factoryAddress, deadline)`) —
255
+ * the factory pulls the pool via `confidentialTransferFrom`.
203
256
  *
204
- * Symmetric with {@link ConfidentialAirdropFactoryClient.createConfidentialAirdrop}
205
- * — both return `{ hash, airdrop }` parsed from the
206
- * `ConfidentialAirdropCreated` event.
257
+ * **Silent-zero transfers.** An ERC-7984 transfer does not revert when the
258
+ * sender's encrypted balance is short; it moves an encrypted zero instead,
259
+ * because reverting would leak the balance. A successful receipt therefore
260
+ * does not prove the pool was funded — confirm the funder's balance first.
207
261
  *
208
- * @example
209
- * const { hash, airdrop } = await factory.createAndFundConfidentialAirdrop({
210
- * params, userSalt, amount: 1_000_000n,
211
- * });
262
+ * @param args Create arguments plus exactly one of `amount` / `encryptedInput`.
263
+ * @returns The tx hash, the deployed instance and its compliance-manager clone.
264
+ * @throws {@link NonStockWrapperError} when `unwrappable` is set on a token that is not a stock wrapper.
265
+ * @throws {@link SaltCollisionError} when this `(mode, deployer, userSalt)` tuple already deployed an instance.
266
+ * @throws {@link MissingEncryptorError} when `amount` is given and no encryptor is resolvable.
212
267
  */
213
- createAndFundConfidentialAirdrop(args: CreateAndFundAirdropArgs): Promise<CreateAirdropResult>;
268
+ createAndFundEcdsaAirdrop(args: CreateAirdropArgs<EcdsaAirdropParams> & FundInput & WriteAccountOverride): Promise<CreateAirdropResult>;
214
269
  /**
215
- * Fund an existing (or not-yet-deployed) airdrop with encrypted tokens.
270
+ * Deploy a Merkle airdrop and seed its pool in the same transaction.
271
+ *
272
+ * Prerequisite: the funder must have approved this factory as an ERC-7984
273
+ * operator on the token (`token.setOperator(factoryAddress, deadline)`).
216
274
  *
217
- * Prerequisites: caller must have set this factory as an operator on the token:
218
- * `token.setOperator(factoryAddress, deadline)`
275
+ * **Silent-zero transfers.** An ERC-7984 transfer does not revert when the
276
+ * sender's encrypted balance is short; it moves an encrypted zero instead,
277
+ * because reverting would leak the balance. A successful receipt therefore
278
+ * does not prove the pool was funded.
219
279
  *
220
- * **Silent-zero transfers.** ERC-7984 transfers do not revert when the sender's encrypted balance is insufficient — the transfer succeeds and moves an encrypted zero instead. This is by design: reverting would leak balance information. The transaction receipt alone cannot tell you whether value actually moved.
280
+ * @param args Create arguments plus exactly one of `amount` / `encryptedInput`.
281
+ * @returns The tx hash, the deployed instance and its compliance-manager clone.
282
+ * @throws {@link MerkleUupsUnsupportedError} when `mode` is `"uups"`.
283
+ * @throws {@link NonStockWrapperError} when `unwrappable` is set on a token that is not a stock wrapper.
284
+ * @throws {@link SaltCollisionError} when this `(mode, deployer, userSalt)` tuple already deployed an instance.
285
+ * @throws {@link MissingEncryptorError} when `amount` is given and no encryptor is resolvable.
286
+ */
287
+ createAndFundMerkleAirdrop(args: CreateAirdropArgs<MerkleAirdropParams> & FundInput & WriteAccountOverride): Promise<CreateAirdropResult>;
288
+ /**
289
+ * Top up the pool of an instance this factory already deployed.
221
290
  *
222
- * **Amount units:** TokenOps confidential (ERC-7984) tokens use a 6-decimals
223
- * convention (1 token = 1_000_000 base units), not the 18 decimals typical of
224
- * ERC-20. Amount parameters are base units of the token's actual decimals:
225
- * for the CTTT test token (6 decimals) `1_000_000n` = 1 CTTT, while the
226
- * transparent TTT test token uses 18 decimals.
291
+ * Funding always routes through the factory rather than the instance: the
292
+ * factory is what holds the operator approval, calls `FHE.fromExternal`, and
293
+ * grants the compliance-manager clone its ACL on the amount. Sending tokens
294
+ * to the instance directly leaves the pool unusable for compliance reads.
227
295
  *
228
- * @returns Transaction hash.
296
+ * **Silent-zero transfers.** A short encrypted balance moves an encrypted
297
+ * zero instead of reverting — the receipt cannot tell you value arrived.
298
+ *
299
+ * @param args The instance address plus exactly one of `amount` / `encryptedInput`.
300
+ * @returns The transaction hash.
301
+ * @throws {@link InvalidArgumentError} when `airdrop` was not created by this factory (`UnknownAirdrop`).
229
302
  */
230
- fundConfidentialAirdrop(args: FundAirdropArgs): Promise<Hex>;
303
+ fundAirdrop(args: {
304
+ airdrop: Address;
305
+ } & FundInput & WriteAccountOverride): Promise<Hex>;
231
306
  /**
232
- * Deploy a new airdrop clone and return its address, parsed from the
233
- * `ConfidentialAirdropCreated` event in the tx receipt.
307
+ * Where an ECDSA `create*` from `deployer` with this `(mode, userSalt)` pair
308
+ * would land.
234
309
  *
235
- * Symmetry helper with the fhe-vesting client. Use this when you want a
236
- * one-call "deploy and use" shape; otherwise call
237
- * {@link ConfidentialAirdropFactoryClient.createConfidentialAirdrop} and parse
238
- * the receipt yourself, or call
239
- * {@link ConfidentialAirdropFactoryClient.predictAirdropAddress} before mining.
310
+ * Prediction is a pure address oracle — it answers even for a deployer that
311
+ * is not currently allowed to deploy the requested `mode`, so a returned
312
+ * address is not evidence the create will succeed.
240
313
  *
241
- * Internally: {@link ConfidentialAirdropFactoryClient.createConfidentialAirdrop} →
242
- * {@link PublicClient.waitForTransactionReceipt} →
243
- * `parseEventLogs({ eventName: "ConfidentialAirdropCreated" })`.
314
+ * @param args Prediction inputs; only `mode`, `deployer` and `userSalt` affect the result.
315
+ * @returns The CREATE2 address of the instance.
316
+ */
317
+ predictEcdsaAirdropAddress(args: PredictArgs<EcdsaAirdropParams>): Promise<Address>;
318
+ /**
319
+ * Where a Merkle `create*` from `deployer` with this `(mode, userSalt)` pair
320
+ * would land.
244
321
  *
245
- * @example
246
- * const { hash, airdrop } = await factory.createConfidentialAirdropAndGetAddress({
247
- * params, userSalt,
248
- * });
322
+ * @param args Prediction inputs; only `mode`, `deployer` and `userSalt` affect the result.
323
+ * @returns The CREATE2 address of the instance.
324
+ */
325
+ predictMerkleAirdropAddress(args: PredictArgs<MerkleAirdropParams>): Promise<Address>;
326
+ /**
327
+ * Init-code hash of the ECDSA proxy the factory would deploy for `mode`.
328
+ *
329
+ * Read it once before a campaign build and again after: the hash tracks the
330
+ * factory's current implementation pointer, so a change between the two
331
+ * reads means an implementation swap landed mid-flight and any address you
332
+ * derived from the first read is stale.
333
+ *
334
+ * @param args The variant params (unused on-chain) and the deployment shell.
335
+ * @returns The `bytes32` init-code hash.
249
336
  */
250
- createConfidentialAirdropAndGetAddress(args: CreateAirdropArgs): Promise<CreateAirdropResult>;
337
+ getEcdsaInitCodeHash(args: InitCodeHashArgs<EcdsaAirdropParams>): Promise<Hex>;
251
338
  /**
252
- * Deploy AND fund a new airdrop clone in one tx, returning its address parsed
253
- * from the `ConfidentialAirdropCreated` event in the receipt.
339
+ * Init-code hash of the Merkle proxy the factory would deploy for `mode`.
254
340
  *
255
- * Symmetry with {@link ConfidentialAirdropFactoryClient.createConfidentialAirdropAndGetAddress}
256
- * — use this when you also need to seed the pool in the same transaction.
341
+ * @param args The variant params (unused on-chain) and the deployment shell.
342
+ * @returns The `bytes32` init-code hash.
343
+ */
344
+ getMerkleInitCodeHash(args: InitCodeHashArgs<MerkleAirdropParams>): Promise<Hex>;
345
+ /** How many instances this factory has deployed. Also the upper bound for {@link airdropAt}. */
346
+ airdropCount(): Promise<bigint>;
347
+ /**
348
+ * @param index Zero-based position in deployment order.
349
+ * @returns The instance address.
350
+ * @throws {@link InvalidArgumentError} when `index >= airdropCount()` (`IndexOutOfBounds`).
351
+ */
352
+ airdropAt(index: bigint): Promise<Address>;
353
+ /**
354
+ * Page through deployed instances in deployment order.
257
355
  *
258
- * **Silent-zero transfers.** ERC-7984 transfers do not revert when the sender's encrypted balance is insufficient — the transfer succeeds and moves an encrypted zero instead. This is by design: reverting would leak balance information. The transaction receipt alone cannot tell you whether value actually moved.
356
+ * The contract clamps to the tail rather than reverting, so an `offset` past
357
+ * the end returns an empty page — page until the result is short, not until
358
+ * it throws.
259
359
  *
260
- * **Amount units:** TokenOps confidential (ERC-7984) tokens use a 6-decimals
261
- * convention (1 token = 1_000_000 base units), not the 18 decimals typical of
262
- * ERC-20. Amount parameters are base units of the token's actual decimals:
263
- * for the CTTT test token (6 decimals) `1_000_000n` = 1 CTTT, while the
264
- * transparent TTT test token uses 18 decimals.
360
+ * @param offset Index to start at.
361
+ * @param limit Maximum entries to return.
362
+ * @returns The page, possibly shorter than `limit`.
363
+ */
364
+ airdrops(offset: bigint, limit: bigint): Promise<readonly Address[]>;
365
+ /**
366
+ * The compliance-manager clone bound to an instance.
265
367
  *
266
- * Internally: {@link ConfidentialAirdropFactoryClient.createAndFundConfidentialAirdrop} →
267
- * {@link PublicClient.waitForTransactionReceipt} →
268
- * `parseEventLogs({ eventName: "ConfidentialAirdropCreated" })`.
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.
269
370
  *
270
- * @example
271
- * const { hash, airdrop } = await factory.createAndFundConfidentialAirdropAndGetAddress({
272
- * params, userSalt, amount, encryptor,
273
- * });
371
+ * @param airdrop Instance address.
372
+ * @returns The clone address, or the zero address when unknown to this factory.
274
373
  */
275
- createAndFundConfidentialAirdropAndGetAddress(args: CreateAndFundAirdropArgs): Promise<CreateAirdropResult>;
276
- /** Change the fee collector address. FEE_MANAGER_ROLE only. */
277
- setFeeCollector(newFeeCollector: Address, account?: Account | Address): Promise<TxHash>;
278
- /** Change the default per-claim gas fee in wei. FEE_MANAGER_ROLE only. */
279
- setDefaultGasFee(newGasFee: bigint, account?: Account | Address): Promise<TxHash>;
280
- /** Set a per-creator gas fee override. FEE_MANAGER_ROLE only. */
281
- setCustomFee(campaignCreator: Address, gasFee: bigint, account?: Account | Address): Promise<TxHash>;
282
- /** Remove a per-creator fee override, reverting to `defaultGasFee`. FEE_MANAGER_ROLE only. */
283
- disableCustomFee(campaignCreator: Address, account?: Account | Address): Promise<TxHash>;
284
- /** Current LibClone implementation address. */
285
- implementation(): Promise<Address>;
286
- /**
287
- * Alias for {@link ConfidentialAirdropFactoryClient.implementation} that matches
288
- * the contract's storage field name (`_airdropImplementation`).
289
- *
290
- * Provided for parity with `fhe-vesting`'s `managerImplementation()` alias.
291
- * The airdrop factory has the same
292
- * LibClone shape mirroring `_airdropImplementation` storage, so server
293
- * devs reading the Solidity first reach for `factory.airdropImplementation()`
294
- * via autocomplete and would otherwise hit a TS2551 on the SDK's
295
- * `implementation()` shape. The canonical `implementation()` method is
296
- * retained for backwards-compat and cross-factory uniformity.
297
- */
298
- airdropImplementation(): Promise<Address>;
299
- /** Default per-claim gas fee in wei. Used when no custom fee is set for the campaign creator. */
300
- defaultGasFee(): Promise<bigint>;
301
- /** Address authorized to collect gas fees from airdrop clones. */
374
+ complianceManagerOf(airdrop: Address): Promise<Address>;
375
+ /**
376
+ * Redirect where accrued claim-fee ETH withdraws to. Requires
377
+ * `FEE_MANAGER_ROLE`.
378
+ *
379
+ * @throws {@link InvalidArgumentError} when `feeCollector` is the zero address (`ZeroFeeCollector`).
380
+ */
381
+ setFeeCollector(feeCollector: Address, account?: Account | Address): Promise<Hex>;
382
+ /**
383
+ * Set the per-claim gas fee new instances default to when their creator has
384
+ * no {@link setCustomFee} override. Requires `FEE_MANAGER_ROLE`.
385
+ *
386
+ * @param gasFee Wei, fits `uint96` on-chain.
387
+ */
388
+ setDefaultGasFee(gasFee: bigint, account?: Account | Address): Promise<Hex>;
389
+ /**
390
+ * Override the default gas fee for one creator. Requires `FEE_MANAGER_ROLE`.
391
+ *
392
+ * @param gasFee Wei, fits `uint96` on-chain.
393
+ */
394
+ setCustomFee(creator: Address, gasFee: bigint, account?: Account | Address): Promise<Hex>;
395
+ /**
396
+ * Drop a creator's {@link setCustomFee} override, reverting them to
397
+ * {@link defaultGasFee}. Requires `FEE_MANAGER_ROLE`.
398
+ */
399
+ disableCustomFee(creator: Address, account?: Account | Address): Promise<Hex>;
400
+ /** A creator's fee override, if any — `enabled: false` means {@link defaultGasFee} applies instead. */
401
+ getCustomFee(creator: Address): Promise<CustomFee>;
402
+ /** Current destination for accrued claim-fee ETH withdrawals. */
302
403
  feeCollector(): Promise<Address>;
303
- /** Per-creator custom fee override. `enabled` is `false` when no override is set. */
304
- getCustomFee(campaignCreator: Address): Promise<CustomFee>;
305
- /**
306
- * Compute the CREATE2 init-code hash for a given set of params and gas fee.
307
- * Useful for deriving the clone address off-chain. Use `predictAirdropAddress` from
308
- * `@tokenops/sdk/fhe-airdrop/advanced` for the full address derivation.
309
- * @param args `{ params, gasFee }` — must match the values used at deploy time.
310
- * @returns `bytes32` init-code hash.
311
- */
312
- getInitCodeHash(args: {
313
- params: AirdropParams;
314
- gasFee: bigint;
315
- }): Promise<Hex>;
316
- /**
317
- * Read-only preflight for {@link createConfidentialAirdrop} /
318
- * {@link createAndFundConfidentialAirdrop}. Runs the
319
- * deterministic-from-off-chain checks and reports blockers both as
320
- * human-readable strings (`blockers`) and as typed {@link TokenOpsSdkError}s
321
- * (`blockerErrors`), index-aligned.
322
- *
323
- * Preflight is **opt-in**. Consumers who have already validated state can
324
- * skip this call and invoke the write directly — they'll receive the same
325
- * typed errors at write time via the revert mapper.
326
- *
327
- * Report fields: `isFactoryOperator`, `gasFee`, `predictedAirdrop`,
328
- * `alreadyDeployed`, `ready`, `blockers`, `blockerErrors` — see
329
- * {@link CreateAirdropPreflightReport} for the meaning of each.
330
- *
331
- * Checks performed (all read-only, no gas):
332
- * - Structural `params` validation mirroring the factory + clone
333
- * `initialize` requirements (`InvalidArgumentError` blockers): `token` and
334
- * `admin` are checksummed non-zero addresses,
335
- * `startTimestamp < endTimestamp`, and `endTimestamp` is in the future
336
- * (the clone's `initialize` reverts with `EndTimeInPast`).
337
- * - Operator: creator has approved this factory as an ERC-7984 operator on
338
- * `params.token` — a blocker only when `funding: true`
339
- * ({@link OperatorNotApprovedError}); otherwise informational via
340
- * `isFactoryOperator`.
341
- * - Collision (only when `userSalt` is supplied): bytecode already at the
342
- * predicted CREATE2 address means a repeat create with the same
343
- * `(params, userSalt, gasFee)` would revert
344
- * ({@link AlreadyInitializedError} blocker).
345
- *
346
- * **Not preflight-detectable — read before trusting `ready: true`:** the
347
- * creator's confidential token balance is an encrypted `euint64` the SDK
348
- * cannot read in plaintext, and an underfunded `createAndFund` does **not**
349
- * revert — the ERC-7984 transfer silently moves an encrypted zero. Confirm
350
- * the creator's balance covers the pool before submitting.
351
- *
352
- * @param args See {@link PreflightCreateAirdropArgs}.
353
- * @returns A {@link CreateAirdropPreflightReport}.
354
- */
355
- preflightCreateAirdrop(args: PreflightCreateAirdropArgs): Promise<CreateAirdropPreflightReport>;
404
+ /** Per-claim gas fee new instances default to absent a {@link setCustomFee} override. */
405
+ defaultGasFee(): Promise<bigint>;
406
+ /**
407
+ * Point future `createEcdsaAirdrop*` calls at a new ECDSA implementation.
408
+ * Requires `IMPL_MANAGER_ROLE`. Does not affect already-deployed instances —
409
+ * clones point at the implementation live when they were created.
410
+ *
411
+ * @throws {@link InvalidArgumentError} when `implementation` is the zero address (`ZeroImplementation`).
412
+ */
413
+ setEcdsaImplementation(implementation: Address, account?: Account | Address): Promise<Hex>;
414
+ /**
415
+ * Point future `createMerkleAirdrop*` calls at a new Merkle implementation.
416
+ * Requires `IMPL_MANAGER_ROLE`. Does not affect already-deployed instances.
417
+ *
418
+ * @throws {@link InvalidArgumentError} when `implementation` is the zero address (`ZeroImplementation`).
419
+ */
420
+ setMerkleImplementation(implementation: Address, account?: Account | Address): Promise<Hex>;
421
+ /** The implementation address `createEcdsaAirdrop*` currently clones. */
422
+ ecdsaImplementation(): Promise<Address>;
423
+ /** The implementation address `createMerkleAirdrop*` currently clones. */
424
+ merkleImplementation(): Promise<Address>;
425
+ /**
426
+ * Point future compliance-manager clones at a new implementation. Requires
427
+ * `COMPLIANCE_WIRING_ROLE`. Does not affect already-deployed clones.
428
+ *
429
+ * @throws {@link InvalidArgumentError} when `implementation` is the zero address (`ZeroComplianceManagerImpl`).
430
+ */
431
+ setComplianceManagerImpl(implementation: Address, account?: Account | Address): Promise<Hex>;
432
+ /**
433
+ * Set the platform-designated compliance delegate every compliance-manager
434
+ * clone wires in. Requires `COMPLIANCE_WIRING_ROLE`.
435
+ *
436
+ * **Ordering:** call this before {@link setDefaultDelegateToCompliance}`(true)`
437
+ * — the contracts' own deploy script does it in this order, and the reverse
438
+ * reverts `ZeroComplianceDelegate` (turning delegation on with no delegate
439
+ * set has nothing to delegate to).
440
+ *
441
+ * @throws {@link InvalidArgumentError} when `delegate` is the zero address (`ZeroComplianceDelegate`).
442
+ */
443
+ setComplianceDelegate(delegate: Address, account?: Account | Address): Promise<Hex>;
444
+ /**
445
+ * Set whether new instances default to delegating compliance disclosure to
446
+ * {@link complianceDelegate}. Requires `COMPLIANCE_WIRING_ROLE`.
447
+ *
448
+ * **Ordering:** call {@link setComplianceDelegate} first when turning this
449
+ * on — passing `true` while no delegate is set reverts `ZeroComplianceDelegate`.
450
+ *
451
+ * @throws {@link InvalidArgumentError} when `on` is `true` and no delegate is set yet (`ZeroComplianceDelegate`).
452
+ */
453
+ setDefaultDelegateToCompliance(on: boolean, account?: Account | Address): Promise<Hex>;
454
+ /**
455
+ * Override the default delegate-to-compliance policy for one creator.
456
+ * Requires `COMPLIANCE_WIRING_ROLE`.
457
+ */
458
+ setCompliancePolicy(creator: Address, delegateToCompliance: boolean, account?: Account | Address): Promise<Hex>;
459
+ /**
460
+ * Drop a creator's {@link setCompliancePolicy} override, reverting them to
461
+ * the factory's default. Requires `COMPLIANCE_WIRING_ROLE`.
462
+ */
463
+ clearCompliancePolicy(creator: Address, account?: Account | Address): Promise<Hex>;
464
+ /** The compliance-manager implementation new clones currently point at. */
465
+ complianceManagerImpl(): Promise<Address>;
466
+ /** The platform-designated compliance delegate wired into new compliance-manager clones. */
467
+ complianceDelegate(): Promise<Address>;
468
+ /** A creator's delegate-to-compliance override, if any — `overridden: false` means the factory default applies instead. */
469
+ getCompliancePolicy(creator: Address): Promise<CompliancePolicy>;
470
+ /**
471
+ * The delegate-to-compliance policy that actually applies to `creator` —
472
+ * their override if `getCompliancePolicy(creator).overridden`, else the
473
+ * factory default. Prefer this over composing the two reads yourself.
474
+ */
475
+ effectiveDelegateToCompliance(creator: Address): Promise<boolean>;
476
+ /**
477
+ * Set whether new instances default to permitting `mode: "uups"`. Requires
478
+ * `UPGRADE_MANAGER_ROLE`.
479
+ */
480
+ setDefaultUpgradeable(allowed: boolean, account?: Account | Address): Promise<Hex>;
481
+ /**
482
+ * Override the default upgradeability policy for one creator. Requires
483
+ * `UPGRADE_MANAGER_ROLE`.
484
+ */
485
+ setUpgradeabilityPolicy(creator: Address, allowed: boolean, account?: Account | Address): Promise<Hex>;
486
+ /**
487
+ * Drop a creator's {@link setUpgradeabilityPolicy} override, reverting them
488
+ * to the factory default. Requires `UPGRADE_MANAGER_ROLE`.
489
+ */
490
+ clearUpgradeabilityPolicy(creator: Address, account?: Account | Address): Promise<Hex>;
491
+ /** Whether new instances default to permitting `mode: "uups"` absent a per-creator override. */
492
+ defaultUpgradeable(): Promise<boolean>;
493
+ /** A creator's upgradeability override, if any — `overridden: false` means {@link defaultUpgradeable} applies instead. */
494
+ getUpgradeabilityPolicy(creator: Address): Promise<UpgradeabilityPolicy>;
495
+ /**
496
+ * The upgradeability policy that actually applies to `creator` — this is a
497
+ * plain read of the same value `createEcdsaAirdrop`/`createMerkleAirdrop`
498
+ * consult when validating `mode: "uups"`. This getter does not itself block
499
+ * a create; `preflightCreate` (`./guards.js`) is what reads it and reports
500
+ * an {@link UpgradeabilityNotAllowedError} before the transaction is sent.
501
+ */
502
+ effectiveUpgradeable(creator: Address): Promise<boolean>;
503
+ /** `bytes32` identifier for the role gating {@link setFeeCollector}, {@link setDefaultGasFee}, {@link setCustomFee} and {@link disableCustomFee}. */
504
+ FEE_MANAGER_ROLE(): Promise<Hex>;
505
+ /** `bytes32` identifier for the role gating {@link setEcdsaImplementation} and {@link setMerkleImplementation}. */
506
+ IMPL_MANAGER_ROLE(): Promise<Hex>;
507
+ /** `bytes32` identifier for the role gating the compliance admin methods. */
508
+ COMPLIANCE_WIRING_ROLE(): Promise<Hex>;
509
+ /** `bytes32` identifier for the role gating the upgradeability admin methods. */
510
+ UPGRADE_MANAGER_ROLE(): Promise<Hex>;
511
+ /**
512
+ * OpenZeppelin's root role (`bytes32(0)`), read from the factory rather than
513
+ * hard-coded. It administers all four roles above.
514
+ */
515
+ DEFAULT_ADMIN_ROLE(): Promise<Hex>;
516
+ /**
517
+ * Whether `holder` holds `role`.
518
+ *
519
+ * The role subject is called `holder`, not `account`: on the writes below
520
+ * `account` already means "who sends this transaction", and one key cannot
521
+ * be both - an admin granting a role to somebody else is the normal case.
522
+ *
523
+ * @param args.role A `bytes32` from one of the role getters above - not a hand-hashed string.
524
+ */
525
+ hasRole(args: {
526
+ role: Hex;
527
+ holder: Address;
528
+ }): Promise<boolean>;
529
+ /** The role whose holders may {@link grantRole} and {@link revokeRole} `role` - `DEFAULT_ADMIN_ROLE` for all five factory roles. */
530
+ getRoleAdmin(role: Hex): Promise<Hex>;
531
+ /** How many accounts hold `role`. */
532
+ getRoleMemberCount(role: Hex): Promise<bigint>;
533
+ /**
534
+ * The `index`-th holder of `role`, in `EnumerableSet` order - which is not
535
+ * stable across grants and revokes. Prefer {@link getRoleMembers} unless you
536
+ * genuinely want one entry.
537
+ */
538
+ getRoleMember(args: {
539
+ role: Hex;
540
+ index: bigint;
541
+ }): Promise<Address>;
542
+ /**
543
+ * Every account currently holding `role`.
544
+ *
545
+ * Run it over the five role getters to check
546
+ * {@link FACTORY_ROLE_CONCENTRATION_NOTE} against the chain you are actually
547
+ * pointed at, rather than taking the note's word for it.
548
+ *
549
+ * @example
550
+ * const roles = await Promise.all([
551
+ * factory.DEFAULT_ADMIN_ROLE(),
552
+ * factory.FEE_MANAGER_ROLE(),
553
+ * factory.IMPL_MANAGER_ROLE(),
554
+ * factory.COMPLIANCE_WIRING_ROLE(),
555
+ * factory.UPGRADE_MANAGER_ROLE(),
556
+ * ]);
557
+ * const holders = await Promise.all(roles.map((role) => factory.getRoleMembers(role)));
558
+ * const concentrated = new Set(holders.flat().map((a) => a.toLowerCase())).size === 1;
559
+ */
560
+ getRoleMembers(role: Hex): Promise<readonly Address[]>;
561
+ /**
562
+ * Grant `role` to `holder`. Requires the role's admin role
563
+ * ({@link getRoleAdmin}).
564
+ *
565
+ * @returns The transaction hash.
566
+ */
567
+ grantRole(args: {
568
+ role: Hex;
569
+ holder: Address;
570
+ } & WriteAccountOverride): Promise<Hex>;
571
+ /**
572
+ * Revoke `role` from `holder`. Requires the role's admin role.
573
+ *
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.
585
+ *
586
+ * @returns The transaction hash.
587
+ */
588
+ revokeRole(args: {
589
+ role: Hex;
590
+ holder: Address;
591
+ } & WriteAccountOverride): Promise<Hex>;
592
+ /**
593
+ * Give up `role` yourself.
594
+ *
595
+ * The contract's signature is `renounceRole(role, callerConfirmation)` and
596
+ * it reverts `AccessControlBadConfirmation` unless `callerConfirmation` is
597
+ * the sender, so this method takes no holder and fills the confirmation from
598
+ * the resolved sending account. Renouncing for somebody else is not a call
599
+ * that can succeed; use {@link revokeRole} to remove another account's role.
600
+ *
601
+ * The same frozen-role-administration warning as {@link revokeRole} applies,
602
+ * and more sharply - a sole admin renouncing `DEFAULT_ADMIN_ROLE` is not
603
+ * blocked here the way it is on an instance. Read that warning for what
604
+ * actually stops working: role grants and revokes, permanently; not the
605
+ * operational setters, which keep answering to whoever already holds the
606
+ * role that gates them.
607
+ *
608
+ * @returns The transaction hash.
609
+ */
610
+ renounceRole(args: {
611
+ role: Hex;
612
+ } & WriteAccountOverride): Promise<Hex>;
356
613
  }
357
614
  /**
358
615
  * Create a {@link ConfidentialAirdropFactoryClient}. Mirrors viem's `create*` convention.
359
616
  *
360
617
  * @example
361
618
  * const factory = createConfidentialAirdropFactoryClient({ publicClient, walletClient, encryptor });
619
+ *
620
+ * @alpha
362
621
  */
363
622
  export declare function createConfidentialAirdropFactoryClient(config: ConfidentialAirdropFactoryClientConfig): ConfidentialAirdropFactoryClient;
364
623
  export {};