@tokenops/sdk 2.0.0-alpha.2 → 2.0.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -3
- package/README.md +11 -5
- package/dist/{chunk-PZZK3O3S.cjs → chunk-4AWQJQXY.cjs} +391 -43
- package/dist/{chunk-SPLNGUFF.js → chunk-4IB4IY5Y.js} +1 -1
- package/dist/{chunk-A4DCEE33.js → chunk-6HCT4QIX.js} +11 -10
- package/dist/{chunk-MM5BV5CS.cjs → chunk-6XOAYDC6.cjs} +4 -4
- package/dist/{chunk-FEDX7B6T.cjs → chunk-AHU2HMNE.cjs} +3 -3
- package/dist/{chunk-IGO5XSPS.js → chunk-BPIVVCSQ.js} +2 -2
- package/dist/{chunk-X7WBPCEU.js → chunk-BXX5TETM.js} +375 -27
- package/dist/{chunk-GADGBQJO.js → chunk-GCJFAY4X.js} +2 -2
- package/dist/{chunk-IUKKNJ2R.cjs → chunk-GKYZQJGP.cjs} +7 -7
- package/dist/{chunk-3YHBO2DL.cjs → chunk-IEKTGJ4J.cjs} +2 -2
- package/dist/{chunk-UHHMVBLU.cjs → chunk-JVNJSDQ4.cjs} +2 -2
- package/dist/{chunk-VW356KR3.js → chunk-LEYYG4RF.js} +126 -3
- package/dist/{chunk-PYYSB5ZN.js → chunk-NGU4JYIR.js} +1 -1
- package/dist/{chunk-FT5H2Q7Y.cjs → chunk-PG3WXT3K.cjs} +11 -10
- package/dist/{chunk-44YJPMQC.js → chunk-PK6Q6J5F.js} +1 -1
- package/dist/{chunk-EBLBPUPI.cjs → chunk-TND5NDV7.cjs} +18 -10
- package/dist/{chunk-EYDA6Q6C.js → chunk-U3TBQRT7.js} +10 -2
- package/dist/{chunk-WJMEBBU7.js → chunk-WRWRA4TN.js} +1 -1
- package/dist/{chunk-FHWSBGWE.cjs → chunk-YABHXJNH.cjs} +5 -5
- package/dist/{chunk-AREQJHKA.cjs → chunk-YTIYTV4K.cjs} +129 -2
- package/dist/core/addresses.d.ts +4 -4
- package/dist/core/errors.d.ts +1 -1
- package/dist/fhe-airdrop/abis/airdrop-base.d.ts +19 -0
- package/dist/fhe-airdrop/abis/ecdsa.d.ts +19 -0
- package/dist/fhe-airdrop/abis/factory.d.ts +113 -0
- package/dist/fhe-airdrop/abis/merkle.d.ts +19 -0
- package/dist/fhe-airdrop/advanced/index.cjs +4 -4
- package/dist/fhe-airdrop/advanced/index.js +1 -1
- package/dist/fhe-airdrop/advanced/react/index.cjs +110 -80
- package/dist/fhe-airdrop/advanced/react/index.d.cts +1 -0
- package/dist/fhe-airdrop/advanced/react/index.d.ts +1 -0
- package/dist/fhe-airdrop/advanced/react/index.js +34 -5
- package/dist/fhe-airdrop/advanced/react/useDisableCustomFee.d.ts +2 -1
- package/dist/fhe-airdrop/advanced/react/useFactoryRenounceRole.d.ts +4 -3
- package/dist/fhe-airdrop/advanced/react/useFactoryRevokeRole.d.ts +9 -7
- package/dist/fhe-airdrop/advanced/react/useFactoryRoleMembers.d.ts +6 -5
- package/dist/fhe-airdrop/advanced/react/useSetCustomFee.d.ts +2 -1
- package/dist/fhe-airdrop/advanced/react/useSetDefaultGasFee.d.ts +5 -3
- package/dist/fhe-airdrop/advanced/react/useSetMaxGasFee.d.ts +33 -0
- package/dist/fhe-airdrop/airdrop-base.d.ts +47 -8
- package/dist/fhe-airdrop/constants.d.ts +9 -1
- package/dist/fhe-airdrop/ecdsa.d.ts +13 -0
- package/dist/fhe-airdrop/errors.d.ts +59 -0
- package/dist/fhe-airdrop/factory.d.ts +62 -22
- package/dist/fhe-airdrop/guards.d.ts +87 -2
- package/dist/fhe-airdrop/index.cjs +67 -55
- package/dist/fhe-airdrop/index.d.cts +3 -3
- package/dist/fhe-airdrop/index.d.ts +3 -3
- package/dist/fhe-airdrop/index.js +4 -4
- package/dist/fhe-airdrop/react/index.cjs +237 -177
- package/dist/fhe-airdrop/react/index.d.cts +4 -1
- package/dist/fhe-airdrop/react/index.d.ts +4 -1
- package/dist/fhe-airdrop/react/index.js +72 -15
- package/dist/fhe-airdrop/react/keys.d.ts +8 -0
- package/dist/fhe-airdrop/react/useFactoryFees.d.ts +11 -4
- package/dist/fhe-airdrop/react/useIsAirdrop.d.ts +44 -0
- package/dist/fhe-airdrop/react/useRefreshComplianceBalance.d.ts +36 -0
- package/dist/fhe-airdrop/react/useResolveGasFee.d.ts +30 -0
- package/dist/fhe-airdrop/types.d.ts +13 -0
- package/dist/fhe-disperse/index.cjs +21 -21
- package/dist/fhe-disperse/index.js +2 -2
- package/dist/fhe-disperse/react/index.cjs +22 -22
- package/dist/fhe-disperse/react/index.js +3 -3
- package/dist/fhe-vesting/advanced/index.cjs +5 -5
- package/dist/fhe-vesting/advanced/index.js +3 -3
- package/dist/fhe-vesting/advanced/react/index.cjs +7 -7
- package/dist/fhe-vesting/advanced/react/index.js +4 -4
- package/dist/fhe-vesting/index.cjs +8 -8
- package/dist/fhe-vesting/index.js +2 -2
- package/dist/fhe-vesting/react/index.cjs +114 -114
- package/dist/fhe-vesting/react/index.js +3 -3
- package/dist/index.cjs +18 -18
- package/dist/index.js +1 -1
- package/dist/testnet-faucet/index.cjs +14 -14
- package/dist/testnet-faucet/index.js +2 -2
- package/dist/testnet-faucet/react/index.cjs +8 -8
- package/dist/testnet-faucet/react/index.js +3 -3
- package/package.json +1 -1
|
@@ -14,7 +14,8 @@ export interface DisableCustomFeeArgs extends WriteAccountOverride {
|
|
|
14
14
|
* Requires `FEE_MANAGER_ROLE`.
|
|
15
15
|
*
|
|
16
16
|
* **Invalidates:** `useFactoryCustomFee({ creator })` for the TARGET creator -
|
|
17
|
-
* `variables.creator`, never the sending admin.
|
|
17
|
+
* `variables.creator`, never the sending admin. Also every `useResolveGasFee`
|
|
18
|
+
* entry: dropping the override sends this creator back to the default.
|
|
18
19
|
*
|
|
19
20
|
* @example
|
|
20
21
|
* const disableCustomFee = useDisableCustomFee();
|
|
@@ -16,9 +16,10 @@ export interface FactoryRenounceRoleArgs extends WriteAccountOverride {
|
|
|
16
16
|
* resolved sending account. To remove a role from somebody else use
|
|
17
17
|
* {@link useFactoryRevokeRole}.
|
|
18
18
|
*
|
|
19
|
-
* **
|
|
20
|
-
* `LastAdmin`
|
|
21
|
-
* administration permanently
|
|
19
|
+
* **A sole `DEFAULT_ADMIN_ROLE` holder cannot renounce** (audit-14): the factory
|
|
20
|
+
* carries the same `LastAdmin` floor the instances do, so the call reverts
|
|
21
|
+
* instead of freezing role administration permanently. Grant the successor
|
|
22
|
+
* first, then renounce.
|
|
22
23
|
*
|
|
23
24
|
* **Invalidates:** every {@link useFactoryHasRole} entry for this factory
|
|
24
25
|
* (prefix, all role/holder pairs), plus its {@link useFactoryRoleMembers} entry
|
|
@@ -14,13 +14,15 @@ export interface FactoryRevokeRoleArgs extends WriteAccountOverride {
|
|
|
14
14
|
*
|
|
15
15
|
* Requires `DEFAULT_ADMIN_ROLE`, which administers all five factory roles.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
* `
|
|
19
|
-
* administration
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
17
|
+
* **`DEFAULT_ADMIN_ROLE` is floored at one live member** (audit-14). Revoking
|
|
18
|
+
* the last holder reverts `LastAdmin` rather than succeeding and freezing role
|
|
19
|
+
* administration, which is what it used to do. Granting the role to the zero
|
|
20
|
+
* address reverts `ZeroAdminGrant`, so the floor cannot be satisfied with a
|
|
21
|
+
* member nobody controls either.
|
|
22
|
+
*
|
|
23
|
+
* On the live deployment all five roles sit on one key, so a handover is two
|
|
24
|
+
* steps: grant the successor, then revoke. Show {@link useFactoryRoleMembers}'s
|
|
25
|
+
* `count` next to the button and disable it at `1n`.
|
|
24
26
|
*
|
|
25
27
|
* **Invalidates:** this factory's {@link useFactoryHasRole} entry for exactly
|
|
26
28
|
* `(role, holder)` and its {@link useFactoryRoleMembers} entry for `role`.
|
|
@@ -26,10 +26,11 @@ export interface UseFactoryRoleMembersArgs extends AirdropHookOptions<FactoryRol
|
|
|
26
26
|
* trusting a fixed string that describes one deployment at one moment.
|
|
27
27
|
*
|
|
28
28
|
* `count` is bundled with the list rather than split into its own hook because
|
|
29
|
-
* it is what a UI needs in the same render to decide whether a revoke
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
29
|
+
* it is what a UI needs in the same render to decide whether a revoke will be
|
|
30
|
+
* accepted: since audit-14 the factory floors `DEFAULT_ADMIN_ROLE` at one live
|
|
31
|
+
* member, so revoking or renouncing the last holder reverts `LastAdmin`.
|
|
32
|
+
* Disable the button at `count === 1n` rather than letting the user burn gas
|
|
33
|
+
* discovering it.
|
|
33
34
|
*
|
|
34
35
|
* Unpaginated: factory role sets are operator-sized, not user-sized.
|
|
35
36
|
*
|
|
@@ -40,7 +41,7 @@ export interface UseFactoryRoleMembersArgs extends AirdropHookOptions<FactoryRol
|
|
|
40
41
|
*
|
|
41
42
|
* @example
|
|
42
43
|
* const { data } = useFactoryRoleMembers({ role: roles?.DEFAULT_ADMIN_ROLE });
|
|
43
|
-
* const lastAdmin = data?.count === 1n;
|
|
44
|
+
* const lastAdmin = data?.count === 1n; // revoke/renounce would revert LastAdmin
|
|
44
45
|
*
|
|
45
46
|
* @alpha
|
|
46
47
|
*/
|
|
@@ -17,7 +17,8 @@ export interface SetCustomFeeArgs extends WriteAccountOverride {
|
|
|
17
17
|
* **Invalidates:** `useFactoryCustomFee({ creator })` for the TARGET creator -
|
|
18
18
|
* `variables.creator`, never the sending admin. `account` is the submitter and
|
|
19
19
|
* has no cache entry of its own here, so keying the invalidation off it would
|
|
20
|
-
* refresh the wrong row and leave the edited one stale.
|
|
20
|
+
* refresh the wrong row and leave the edited one stale. Also every
|
|
21
|
+
* `useResolveGasFee` entry, since an override is what that hook resolves.
|
|
21
22
|
*
|
|
22
23
|
* @example
|
|
23
24
|
* const setCustomFee = useSetCustomFee();
|
|
@@ -15,9 +15,11 @@ export interface SetDefaultGasFeeArgs extends WriteAccountOverride {
|
|
|
15
15
|
* instance's `gasFee()` is baked in at create time, so no deployed campaign's
|
|
16
16
|
* fee changes.
|
|
17
17
|
*
|
|
18
|
-
* **Invalidates:** this factory's `useFactoryFees` entry (fn `"fees"`)
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* **Invalidates:** this factory's `useFactoryFees` entry (fn `"fees"`) and every
|
|
19
|
+
* `useResolveGasFee` entry - the default is what resolves for every creator
|
|
20
|
+
* without an override, so all of them move at once. Deliberately NOT any
|
|
21
|
+
* instance's fn `"gasFee"` key: those are frozen at create and invalidating
|
|
22
|
+
* them would refetch unchanged values.
|
|
21
23
|
*
|
|
22
24
|
* @example
|
|
23
25
|
* const setDefaultFee = useSetDefaultGasFee();
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type UseMutationResult } from "@tanstack/react-query";
|
|
2
|
+
import type { Hex } from "viem";
|
|
3
|
+
import type { WriteAccountOverride } from "../../airdrop-base.js";
|
|
4
|
+
import { type AirdropClientOptions } from "../../react/_shared.js";
|
|
5
|
+
/** @alpha */
|
|
6
|
+
export interface SetMaxGasFeeArgs extends WriteAccountOverride {
|
|
7
|
+
/** The factory-wide ceiling in wei, `uint96` on-chain. `0n` admits only a zero fee. */
|
|
8
|
+
maxGasFee: bigint;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Set the factory's ceiling on every fee `setDefaultGasFee` / `setCustomFee` may
|
|
12
|
+
* configure, and on the fee resolved at create.
|
|
13
|
+
*
|
|
14
|
+
* Requires `DEFAULT_ADMIN_ROLE`, deliberately not `FEE_MANAGER_ROLE` (audit-09):
|
|
15
|
+
* the fee manager moves fees only within a bound the admin owns. This is the
|
|
16
|
+
* one fee control that is not a fee-manager operation.
|
|
17
|
+
*
|
|
18
|
+
* **Lowering it does not rewrite an already-configured fee.** The stale value
|
|
19
|
+
* stays in storage and reverts `GasFeeExceedsMaximum` at the next create until
|
|
20
|
+
* it is lowered too, so a reduction is two steps, not one.
|
|
21
|
+
*
|
|
22
|
+
* **Invalidates:** this factory's `useFactoryFees` entry (fn `"fees"`, which
|
|
23
|
+
* carries `maxGasFee`) and every `useResolveGasFee` entry, since the ceiling
|
|
24
|
+
* bounds what any creator's fee can resolve to. Deliberately NOT any instance's
|
|
25
|
+
* fn `"gasFee"` key - an instance's fee is frozen at create and cannot move.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* const setCeiling = useSetMaxGasFee();
|
|
29
|
+
* await setCeiling.mutateAsync({ maxGasFee: parseEther("0.5") });
|
|
30
|
+
*
|
|
31
|
+
* @alpha
|
|
32
|
+
*/
|
|
33
|
+
export declare function useSetMaxGasFee(options?: AirdropClientOptions): UseMutationResult<Hex, Error, SetMaxGasFeeArgs>;
|
|
@@ -137,7 +137,7 @@ export interface WriteAccountOverride {
|
|
|
137
137
|
* do not care about (an admin panel listing campaigns, say). The ECDSA and
|
|
138
138
|
* Merkle clients extend it and add their own claim path.
|
|
139
139
|
*
|
|
140
|
-
* What it encapsulates beyond the raw ABI is the encrypted-view protocol:
|
|
140
|
+
* What it encapsulates beyond the raw ABI is the encrypted-view protocol: six
|
|
141
141
|
* of the contract's disclosure entrypoints look like getters and are not —
|
|
142
142
|
* they mutate ACL state, so they are transactions, and their result must be
|
|
143
143
|
* recovered from the receipt. See {@link extractGrantedHandle}.
|
|
@@ -217,8 +217,12 @@ export declare class AirdropBaseClient {
|
|
|
217
217
|
* Sweep a plain ERC-20 that was sent to the instance by mistake. Requires
|
|
218
218
|
* `RESCUER_ROLE`.
|
|
219
219
|
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
220
|
+
* Passing the campaign's own token reverts `CannotRescueAirdropToken`
|
|
221
|
+
* (audit-05), matching {@link rescueOtherConfidentialToken}. ERC-7984 and
|
|
222
|
+
* ERC-20 are not disjoint in practice - a wrapper token answers both
|
|
223
|
+
* interfaces - so "the pool token is confidential, therefore out of reach
|
|
224
|
+
* here" was never a guarantee, only an assumption. Use
|
|
225
|
+
* {@link withdrawConfidential} for the pool.
|
|
222
226
|
*
|
|
223
227
|
* @returns The transaction hash.
|
|
224
228
|
*/
|
|
@@ -469,10 +473,38 @@ export declare class AirdropBaseClient {
|
|
|
469
473
|
* @alpha
|
|
470
474
|
*/
|
|
471
475
|
deploymentMode(): Promise<DeploymentMode>;
|
|
476
|
+
/**
|
|
477
|
+
* Re-grant the compliance-manager clone on the instance's current pool
|
|
478
|
+
* balance, and return the handle it was granted on.
|
|
479
|
+
*
|
|
480
|
+
* **Permissionless, and deliberately so** (audit-04). The token rotates the
|
|
481
|
+
* instance's balance handle on every incoming transfer, including one no
|
|
482
|
+
* airdrop code observes - a third party transferring in directly - and
|
|
483
|
+
* grants the fresh handle to the token and the holder only. Without this the
|
|
484
|
+
* compliance clone's grant can be stranded on a handle the token has already
|
|
485
|
+
* replaced, with no admin necessarily around to restore it. There is no
|
|
486
|
+
* argument to abuse: the only handle read is `address(this)`'s own balance,
|
|
487
|
+
* and the only grantee is the clone wired at initialization.
|
|
488
|
+
*
|
|
489
|
+
* This is therefore the normative way to obtain a readable live-balance
|
|
490
|
+
* handle - and it is a **snapshot, not a subscription**. Grants are
|
|
491
|
+
* append-only, so the handle this returns stays readable forever; but the
|
|
492
|
+
* next incoming transfer rotates the instance to a NEW handle the clone was
|
|
493
|
+
* never granted on. A compliance reader that refreshes once and stops is
|
|
494
|
+
* reading a stale balance, not a live one. Call it again after any transfer
|
|
495
|
+
* in, or before each read.
|
|
496
|
+
*
|
|
497
|
+
* @returns `{ handle, hash }` - the handle the compliance clone may now decrypt.
|
|
498
|
+
* @throws {@link ReceiptEventNotFoundError} when the transaction granted no ACL to the clone.
|
|
499
|
+
*/
|
|
500
|
+
refreshComplianceBalance(args?: WriteAccountOverride): Promise<EncryptedViewResult>;
|
|
472
501
|
/**
|
|
473
502
|
* Read the instance's own remaining pool balance as a handle the **caller**
|
|
474
503
|
* can decrypt. Requires `DISCLOSURE_ADMIN_ROLE`.
|
|
475
504
|
*
|
|
505
|
+
* Also re-grants the compliance-manager clone on the same handle (audit-04),
|
|
506
|
+
* so an admin read doubles as a {@link refreshComplianceBalance}.
|
|
507
|
+
*
|
|
476
508
|
* @returns `{ handle, hash }` — pass `handle` to the Zama relayer's `userDecrypt`.
|
|
477
509
|
* @throws {@link ReceiptEventNotFoundError} when the transaction granted no ACL to the caller.
|
|
478
510
|
*/
|
|
@@ -515,11 +547,18 @@ export declare class AirdropBaseClient {
|
|
|
515
547
|
/**
|
|
516
548
|
* Grant `party` decrypt access to a handle you already hold.
|
|
517
549
|
*
|
|
518
|
-
* Two gates apply on-chain: the
|
|
519
|
-
*
|
|
520
|
-
*
|
|
521
|
-
*
|
|
522
|
-
*
|
|
550
|
+
* Two gates apply on-chain. Gate #1: the handle must be **persistently**
|
|
551
|
+
* user-decryptable in the `(caller, instance)` context - BOTH legs, and a
|
|
552
|
+
* transient allowance does not satisfy either (audit-06). `DISCLOSURE_ADMIN_ROLE`
|
|
553
|
+
* bypasses gate #1 only. Gate #2: the instance must be allowed on the handle,
|
|
554
|
+
* so an arbitrary handle from another contract cannot be laundered through
|
|
555
|
+
* this campaign.
|
|
556
|
+
*
|
|
557
|
+
* **The transient case is the trap.** An `euint64` that just came back from
|
|
558
|
+
* an FHE op carries a transient allowance, which used to be enough here and
|
|
559
|
+
* no longer is. Handles worth disclosing come from the encrypted views on
|
|
560
|
+
* this client, whose grants are persistent. A gate #1 failure surfaces as
|
|
561
|
+
* `FheHandleNotAllowedError` carrying the offending handle.
|
|
523
562
|
*
|
|
524
563
|
* @param args.handle An existing `euint64` handle — a public pointer, not a value.
|
|
525
564
|
* @returns The transaction hash. No new handle is produced.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @alpha
|
|
5
5
|
*/
|
|
6
|
-
export declare const AIRDROP_CONTRACTS_COMMIT = "
|
|
6
|
+
export declare const AIRDROP_CONTRACTS_COMMIT = "c5c12c2087f61bcd5bd1daa802563ed3cd68f002";
|
|
7
7
|
/**
|
|
8
8
|
* Deployment shell selected at create time. Mirrors the contract's `DeploymentMode` enum
|
|
9
9
|
* (`IConfidentialAirdropTypes.sol`): `enum DeploymentMode { Clone, UUPS }`.
|
|
@@ -31,3 +31,11 @@ export declare const DEDUP_MODE: {
|
|
|
31
31
|
};
|
|
32
32
|
/** @alpha */
|
|
33
33
|
export type DedupMode = keyof typeof DEDUP_MODE;
|
|
34
|
+
/**
|
|
35
|
+
* The largest value `CommonAirdropParams.maxAcceptedGasFee` can carry - the
|
|
36
|
+
* `uint96` ceiling. Passing it accepts whatever fee the factory resolves, which
|
|
37
|
+
* is the opt-out, not the default.
|
|
38
|
+
*
|
|
39
|
+
* @alpha
|
|
40
|
+
*/
|
|
41
|
+
export declare const UINT96_MAX: bigint;
|
|
@@ -186,11 +186,24 @@ export declare class EcdsaAirdropClient extends AirdropBaseClient {
|
|
|
186
186
|
* consuming either replay guard — the same input can still be claimed for
|
|
187
187
|
* real afterwards. No fee.
|
|
188
188
|
*
|
|
189
|
+
* **It is repeatable, not unconditional** (audit-07). While the claim is
|
|
190
|
+
* still available this may be called any number of times; once it is not,
|
|
191
|
+
* this reverts with the same error the consuming claim would raise, in the
|
|
192
|
+
* same order: the mode's dedup slot first (`AddressAlreadyClaimed` /
|
|
193
|
+
* `DedupIdAlreadyClaimed`), the EIP-712 digest second (`SignatureAlreadyUsed`).
|
|
194
|
+
* A rejected preview performs no FHE op, grants no ACL and emits nothing.
|
|
195
|
+
*
|
|
196
|
+
* So this is not a probe for "has this been claimed" - use
|
|
197
|
+
* {@link isSignatureValid}, which never reverts and answers the same
|
|
198
|
+
* question for free. Reach for this when you want the AMOUNT.
|
|
199
|
+
*
|
|
189
200
|
* An encrypted view: the contract calls `FHE.allow(amount, msg.sender)`, so
|
|
190
201
|
* the result is recovered from the receipt's ACL `Allowed` event, never
|
|
191
202
|
* from a simulation. See {@link AirdropBaseClient}'s class TSDoc.
|
|
192
203
|
*
|
|
193
204
|
* @returns `{ handle, hash }` — pass `handle` to the Zama relayer's `userDecrypt`.
|
|
205
|
+
* @throws {@link AlreadyClaimedError} when this recipient's dedup slot is consumed.
|
|
206
|
+
* @throws {@link DedupIdConsumedError} when this `dedupId`'s slot is consumed.
|
|
194
207
|
*/
|
|
195
208
|
getClaimAmount(args: GetClaimAmountArgs): Promise<EncryptedViewResult>;
|
|
196
209
|
/**
|
|
@@ -315,4 +315,63 @@ export declare class ClaimWindowClosedError extends TokenOpsSdkError {
|
|
|
315
315
|
cause?: unknown;
|
|
316
316
|
});
|
|
317
317
|
}
|
|
318
|
+
/**
|
|
319
|
+
* `create*` refused the fee the factory resolved for this creator: it landed
|
|
320
|
+
* above their `CommonAirdropParams.maxAcceptedGasFee`.
|
|
321
|
+
*
|
|
322
|
+
* The fee is frozen into the instance at initialization and is immutable
|
|
323
|
+
* afterwards, while the value that resolves is set by the factory's fee manager.
|
|
324
|
+
* The bound is how a creator declines a fee raised between planning and sending
|
|
325
|
+
* - so the fix is a deliberate one: re-send with a higher bound, or wait for the
|
|
326
|
+
* fee to come down. Raising the bound to {@link UINT96_MAX} accepts whatever the
|
|
327
|
+
* factory charges, now and at every later re-plan.
|
|
328
|
+
*
|
|
329
|
+
* @alpha
|
|
330
|
+
*/
|
|
331
|
+
export declare class GasFeeNotAcceptedError extends TokenOpsSdkError {
|
|
332
|
+
readonly name = "GasFeeNotAcceptedError";
|
|
333
|
+
readonly context: {
|
|
334
|
+
method: string;
|
|
335
|
+
contractAddress: Address;
|
|
336
|
+
/** The fee the factory resolved for this creator, in wei. */
|
|
337
|
+
resolvedFee?: bigint;
|
|
338
|
+
/** The bound the creator declared, in wei. */
|
|
339
|
+
maxAcceptedGasFee?: bigint;
|
|
340
|
+
};
|
|
341
|
+
constructor(args: {
|
|
342
|
+
method: string;
|
|
343
|
+
contractAddress: Address;
|
|
344
|
+
resolvedFee?: bigint;
|
|
345
|
+
maxAcceptedGasFee?: bigint;
|
|
346
|
+
cause?: unknown;
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* The canonical factory's registry does not contain this address, so it is not
|
|
351
|
+
* an airdrop this SDK deployed.
|
|
352
|
+
*
|
|
353
|
+
* Instances are the wrong place to ask. An address with the same ABI, the same
|
|
354
|
+
* events and a pool the deployer controls is trivial to stand up, and nothing
|
|
355
|
+
* observable at the instance distinguishes it - `factory.isAirdrop` against a
|
|
356
|
+
* factory address taken from `src/core/addresses.ts` is the only check that
|
|
357
|
+
* means anything (audit-10).
|
|
358
|
+
*
|
|
359
|
+
* @alpha
|
|
360
|
+
*/
|
|
361
|
+
export declare class UnrecognisedAirdropError extends TokenOpsSdkError {
|
|
362
|
+
readonly name = "UnrecognisedAirdropError";
|
|
363
|
+
readonly context: {
|
|
364
|
+
method: string;
|
|
365
|
+
contractAddress: Address;
|
|
366
|
+
factory: Address;
|
|
367
|
+
};
|
|
368
|
+
constructor(args: {
|
|
369
|
+
method: string;
|
|
370
|
+
/** The address that failed the check. */
|
|
371
|
+
contractAddress: Address;
|
|
372
|
+
/** The factory whose registry was consulted. */
|
|
373
|
+
factory: Address;
|
|
374
|
+
cause?: unknown;
|
|
375
|
+
});
|
|
376
|
+
}
|
|
318
377
|
export declare const airdropProductMapper: RevertNameMapper;
|
|
@@ -1,19 +1,15 @@
|
|
|
1
|
+
import type { AbiParameterToPrimitiveType, ExtractAbiFunction } from "abitype";
|
|
1
2
|
import type { Account, Address, Hex, PublicClient, WalletClient } from "viem";
|
|
2
3
|
import { type SdkTelemetry } from "../core/telemetry.js";
|
|
3
4
|
import type { EncryptedInput } from "../fhe/types.js";
|
|
5
|
+
import { airdropFactoryAbi as factoryAbi } from "./abis/factory.js";
|
|
4
6
|
import type { WriteAccountOverride } from "./airdrop-base.js";
|
|
5
7
|
import { type DeploymentMode } from "./constants.js";
|
|
6
8
|
import { type EncryptorSource } from "./encryption.js";
|
|
7
9
|
import type { CommonAirdropParams, CreateAirdropArgs, CreateAirdropResult, EcdsaAirdropParams, MerkleAirdropParams } from "./types.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
startTime: number;
|
|
12
|
-
endTime: number;
|
|
13
|
-
canExtendClaimWindow: boolean;
|
|
14
|
-
unwrappable: boolean;
|
|
15
|
-
complianceAdmin: Address;
|
|
16
|
-
}
|
|
10
|
+
type ContractMerkleParams = AbiParameterToPrimitiveType<ExtractAbiFunction<typeof factoryAbi, "createMerkleConfidentialAirdrop">["inputs"][0]>;
|
|
11
|
+
/** The on-chain `CommonAirdropParams` struct, field-for-field, straight off the ABI. */
|
|
12
|
+
type ContractCommonParams = ContractMerkleParams["common"];
|
|
17
13
|
/**
|
|
18
14
|
* Convert the SDK's `"clone"` / `"uups"` name to the contract's
|
|
19
15
|
* `DeploymentMode` ordinal.
|
|
@@ -362,11 +358,26 @@ export declare class ConfidentialAirdropFactoryClient {
|
|
|
362
358
|
* @returns The page, possibly shorter than `limit`.
|
|
363
359
|
*/
|
|
364
360
|
airdrops(offset: bigint, limit: bigint): Promise<readonly Address[]>;
|
|
361
|
+
/**
|
|
362
|
+
* Whether THIS factory created `candidate` - the genuineness check (audit-10).
|
|
363
|
+
*
|
|
364
|
+
* Nothing observable at an instance proves which factory, if any, deployed
|
|
365
|
+
* it: an attacker can deploy a contract with the same ABI, the same events
|
|
366
|
+
* and a pool it controls. The only trustworthy question is whether the
|
|
367
|
+
* canonical factory's own registry contains the address, which is why this
|
|
368
|
+
* read has to be addressed to a factory taken from
|
|
369
|
+
* `src/core/addresses.ts` rather than one the instance names.
|
|
370
|
+
*
|
|
371
|
+
* @param candidate The address to check.
|
|
372
|
+
* @returns True when this factory's registry contains `candidate`.
|
|
373
|
+
*/
|
|
374
|
+
isAirdrop(candidate: Address): Promise<boolean>;
|
|
365
375
|
/**
|
|
366
376
|
* The compliance-manager clone bound to an instance.
|
|
367
377
|
*
|
|
368
|
-
* Doubles as a provenance check
|
|
369
|
-
* instances it deployed, so a zero
|
|
378
|
+
* Doubles as a provenance check, though a weaker one than {@link isAirdrop}:
|
|
379
|
+
* the factory records this mapping only for instances it deployed, so a zero
|
|
380
|
+
* address means the instance is not ours.
|
|
370
381
|
*
|
|
371
382
|
* @param airdrop Instance address.
|
|
372
383
|
* @returns The clone address, or the zero address when unknown to this factory.
|
|
@@ -403,6 +414,37 @@ export declare class ConfidentialAirdropFactoryClient {
|
|
|
403
414
|
feeCollector(): Promise<Address>;
|
|
404
415
|
/** Per-claim gas fee new instances default to absent a {@link setCustomFee} override. */
|
|
405
416
|
defaultGasFee(): Promise<bigint>;
|
|
417
|
+
/** The factory's own ceiling on every configurable fee and every create-time resolved fee. */
|
|
418
|
+
maxGasFee(): Promise<bigint>;
|
|
419
|
+
/**
|
|
420
|
+
* Set the factory's ceiling on every fee `setDefaultGasFee`/`setCustomFee`
|
|
421
|
+
* may configure, and on the fee resolved at create.
|
|
422
|
+
*
|
|
423
|
+
* Requires `DEFAULT_ADMIN_ROLE`, deliberately not `FEE_MANAGER_ROLE`: the fee
|
|
424
|
+
* manager moves fees only within a bound the admin controls. `0n` admits a
|
|
425
|
+
* zero fee and nothing else.
|
|
426
|
+
*
|
|
427
|
+
* Lowering it below an already-configured fee does not rewrite that fee - the
|
|
428
|
+
* stale value stays in storage and reverts `GasFeeExceedsMaximum` at the next
|
|
429
|
+
* create until it is lowered too.
|
|
430
|
+
*
|
|
431
|
+
* @throws {@link InvalidArgumentError} when `maxGasFee` is below a currently configured fee (`GasFeeExceedsMaximum`).
|
|
432
|
+
*/
|
|
433
|
+
setMaxGasFee(maxGasFee: bigint, account?: Account | Address): Promise<Hex>;
|
|
434
|
+
/**
|
|
435
|
+
* The per-claim fee a `create*` from `creator` would freeze into the new
|
|
436
|
+
* instance right now: their {@link getCustomFee} override when enabled, else
|
|
437
|
+
* {@link defaultGasFee}.
|
|
438
|
+
*
|
|
439
|
+
* This is the number `CommonAirdropParams.maxAcceptedGasFee` is checked
|
|
440
|
+
* against, so it is also what to show a creator before they choose a bound.
|
|
441
|
+
* It is a snapshot, not a quote: `FEE_MANAGER_ROLE` can move it between this
|
|
442
|
+
* read and the create, which is exactly what the bound exists to catch.
|
|
443
|
+
*
|
|
444
|
+
* @param creator The account that will send `create*`.
|
|
445
|
+
* @returns The resolved fee in wei.
|
|
446
|
+
*/
|
|
447
|
+
resolveGasFee(creator: Address): Promise<bigint>;
|
|
406
448
|
/**
|
|
407
449
|
* Point future `createEcdsaAirdrop*` calls at a new ECDSA implementation.
|
|
408
450
|
* Requires `IMPL_MANAGER_ROLE`. Does not affect already-deployed instances —
|
|
@@ -571,17 +613,15 @@ export declare class ConfidentialAirdropFactoryClient {
|
|
|
571
613
|
/**
|
|
572
614
|
* Revoke `role` from `holder`. Requires the role's admin role.
|
|
573
615
|
*
|
|
574
|
-
*
|
|
575
|
-
*
|
|
576
|
-
*
|
|
577
|
-
*
|
|
578
|
-
*
|
|
579
|
-
*
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
*
|
|
583
|
-
* that moment, with no way to rotate or remove them. Grant the successor
|
|
584
|
-
* first.
|
|
616
|
+
* **The factory floors `DEFAULT_ADMIN_ROLE` at one live member** (audit-14),
|
|
617
|
+
* matching the instances. Renouncing or revoking the sole holder reverts
|
|
618
|
+
* `LastAdmin`, and granting the role to the zero address reverts
|
|
619
|
+
* `ZeroAdminGrant`, so the set can neither be emptied nor satisfied by a
|
|
620
|
+
* member nobody controls. Before audit-14 this succeeded and froze role
|
|
621
|
+
* administration permanently; it no longer does.
|
|
622
|
+
*
|
|
623
|
+
* A handover is still available and is the supported way out: grant the
|
|
624
|
+
* successor first, then renounce.
|
|
585
625
|
*
|
|
586
626
|
* @returns The transaction hash.
|
|
587
627
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Address, Hex, PublicClient } from "viem";
|
|
2
2
|
import type { PreflightResult } from "../core/preflight.js";
|
|
3
|
-
import type
|
|
3
|
+
import { type DeploymentMode } from "./constants.js";
|
|
4
4
|
import { type AirdropVariant } from "./errors.js";
|
|
5
5
|
import type { ConfidentialAirdropFactoryClient } from "./factory.js";
|
|
6
6
|
import type { EcdsaAirdropParams, MerkleAirdropParams } from "./types.js";
|
|
@@ -104,6 +104,49 @@ export interface AssertSaltAvailableArgs {
|
|
|
104
104
|
* @alpha
|
|
105
105
|
*/
|
|
106
106
|
export declare function assertSaltAvailable(args: AssertSaltAvailableArgs): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* The slice of {@link ConfidentialAirdropFactoryClient} {@link assertGasFeeAccepted}
|
|
109
|
+
* reads. Structural so this module keeps its runtime-import-free shape; the real
|
|
110
|
+
* client satisfies it as-is.
|
|
111
|
+
*
|
|
112
|
+
* @alpha
|
|
113
|
+
*/
|
|
114
|
+
export interface GasFeeResolver {
|
|
115
|
+
address: Address;
|
|
116
|
+
resolveGasFee(creator: Address): Promise<bigint>;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Inputs for {@link assertGasFeeAccepted}.
|
|
120
|
+
*
|
|
121
|
+
* @alpha
|
|
122
|
+
*/
|
|
123
|
+
export interface AssertGasFeeAcceptedArgs {
|
|
124
|
+
factory: GasFeeResolver;
|
|
125
|
+
/** The account that will send `create*`; fee overrides are per-creator. */
|
|
126
|
+
creator: Address;
|
|
127
|
+
/** `CommonAirdropParams.maxAcceptedGasFee`, in wei. */
|
|
128
|
+
maxAcceptedGasFee: bigint;
|
|
129
|
+
/** Label for the thrown error. */
|
|
130
|
+
method: string;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Refuse a create whose resolved per-claim fee exceeds the creator's declared bound.
|
|
134
|
+
*
|
|
135
|
+
* The contract enforces this too. Doing it here buys the reason: the revert
|
|
136
|
+
* carries the two numbers but not which factory knob produced them, and a
|
|
137
|
+
* creator who sees `resolvedFee` next to their own bound can tell a raised
|
|
138
|
+
* default from a custom override they did not know they had.
|
|
139
|
+
*
|
|
140
|
+
* The range check is not redundant with the comparison - a bound outside
|
|
141
|
+
* `uint96` silently wraps at the ABI edge and could encode as a SMALLER number
|
|
142
|
+
* than intended, turning "accept anything" into "accept almost nothing".
|
|
143
|
+
*
|
|
144
|
+
* @throws {@link InvalidArgumentError} when `maxAcceptedGasFee` is negative or above `UINT96_MAX`.
|
|
145
|
+
* @throws {@link GasFeeNotAcceptedError} when the factory resolves a fee above the bound.
|
|
146
|
+
*
|
|
147
|
+
* @alpha
|
|
148
|
+
*/
|
|
149
|
+
export declare function assertGasFeeAccepted(args: AssertGasFeeAcceptedArgs): Promise<void>;
|
|
107
150
|
/**
|
|
108
151
|
* Inputs for {@link assertPredictionFresh}.
|
|
109
152
|
*
|
|
@@ -163,7 +206,8 @@ export type PreflightCreateArgs = PreflightCreateCommon & ({
|
|
|
163
206
|
* transaction, instead of throwing on the first problem.
|
|
164
207
|
*
|
|
165
208
|
* Same checks and the same typed errors the write path enforces: chain
|
|
166
|
-
* support, the Merkle/UUPS refusal, the creator's
|
|
209
|
+
* support, the Merkle/UUPS refusal, the creator's resolved gas fee against
|
|
210
|
+
* `common.maxAcceptedGasFee`, the creator's effective
|
|
167
211
|
* upgradeability policy when `mode: "uups"` is requested, the stock-wrapper
|
|
168
212
|
* probe when `unwrappable` is set, a salt collision on the predicted
|
|
169
213
|
* address, and - when `expectedInitCodeHash` is supplied - implementation
|
|
@@ -222,6 +266,40 @@ export interface ClaimPreflightTarget {
|
|
|
222
266
|
signature: Hex;
|
|
223
267
|
}) => Promise<boolean>;
|
|
224
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* The slice of {@link ConfidentialAirdropFactoryClient} the genuineness check reads.
|
|
271
|
+
*
|
|
272
|
+
* @alpha
|
|
273
|
+
*/
|
|
274
|
+
export interface AirdropRegistry {
|
|
275
|
+
readonly address: Address;
|
|
276
|
+
/**
|
|
277
|
+
* The chain this factory is bound to. Checked against the instance's own
|
|
278
|
+
* `chainId` before the registry is consulted - a registry answer from the
|
|
279
|
+
* wrong chain is worse than no answer, because it reads as a `true`.
|
|
280
|
+
*/
|
|
281
|
+
readonly chainId: number;
|
|
282
|
+
isAirdrop(candidate: Address): Promise<boolean>;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Refuse an instance the canonical factory's registry does not contain.
|
|
286
|
+
*
|
|
287
|
+
* The factory has to be one the caller already trusts - taken from
|
|
288
|
+
* `src/core/addresses.ts`, not read off the instance. An instance that names
|
|
289
|
+
* its own factory proves nothing: a fake instance names a fake factory, which
|
|
290
|
+
* answers `true`.
|
|
291
|
+
*
|
|
292
|
+
* @throws {@link UnrecognisedAirdropError} when the registry does not contain `airdrop`.
|
|
293
|
+
*
|
|
294
|
+
* @alpha
|
|
295
|
+
*/
|
|
296
|
+
export declare function assertKnownAirdrop(args: {
|
|
297
|
+
factory: AirdropRegistry;
|
|
298
|
+
airdrop: Address;
|
|
299
|
+
/** The chain the instance being vetted lives on. */
|
|
300
|
+
chainId: number;
|
|
301
|
+
method: string;
|
|
302
|
+
}): Promise<void>;
|
|
225
303
|
/**
|
|
226
304
|
* The signature material {@link preflightClaim} needs to check an ECDSA claim.
|
|
227
305
|
*
|
|
@@ -265,6 +343,13 @@ export interface PreflightClaimArgs {
|
|
|
265
343
|
* `isSignatureValid`, it adds the authorisation check to the report.
|
|
266
344
|
*/
|
|
267
345
|
signature?: EcdsaClaimPreflightSignature | undefined;
|
|
346
|
+
/**
|
|
347
|
+
* The canonical factory, from `src/core/addresses.ts`. Supplying it adds the
|
|
348
|
+
* genuineness check (audit-10): every other blocker here describes a claim
|
|
349
|
+
* that will fail, but a claim against a look-alike instance SUCCEEDS and
|
|
350
|
+
* delivers nothing. Omitting it skips that one check.
|
|
351
|
+
*/
|
|
352
|
+
factory?: AirdropRegistry | undefined;
|
|
268
353
|
}
|
|
269
354
|
/**
|
|
270
355
|
* The three argument-relation rules the Merkle claim path enforces before it touches the
|