@chipi-stack/backend 14.4.0 → 14.7.0

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/dist/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import * as _chipi_stack_types from '@chipi-stack/types';
2
- import { CreateSessionKeyParams, SessionKeyData, AddSessionKeyParams, RevokeSessionKeyParams, GetSessionDataParams, SessionDataResponse, ExecuteWithSessionParams, SetSpendingPolicyParams, GetSpendingPolicyParams, SpendingPolicyData, RemoveSpendingPolicyParams, Currency, PaymasterAdapter, PaymasterAccount, X402FacilitatorConfig, NonceStore, PaymentPayload, VerifyResponse, SettleResponse, ChipiSDKConfig, ExecuteTransactionParams, SignTypedDataParams, SignTypedDataResult, TransferParams, ApproveParams, CallAnyContractParams, CreateWalletParams, CreateWalletResponse, RecordSendTransactionParams, Transaction, GetTransactionListQuery, PaginatedResponse, GetWalletParams, GetTokenBalanceParams, GetTokenBalanceResponse, CreateSkuPurchaseParams, GetSkuListQuery, Sku, TransactionStatusResponse, PrepareWalletUpgradeParams, PrepareWalletUpgradeResponse, ExecuteWalletUpgradeParams, ExecuteWalletUpgradeResponse, ChipiServerSDKConfig, ChipiBrowserSDKConfig, X402PaymentConfig } from '@chipi-stack/types';
2
+ import { CreateSessionKeyParams, SessionKeyData, AddSessionKeyParams, RevokeSessionKeyParams, GetSessionDataParams, SessionDataResponse, ExecuteWithSessionParams, SetSpendingPolicyParams, GetSpendingPolicyParams, SpendingPolicyData, RemoveSpendingPolicyParams, Currency, PaymasterAdapter, PaymasterAccount, X402FacilitatorConfig, NonceStore, PaymentPayload, VerifyResponse, SettleResponse, ChipiSDKConfig, ExecuteTransactionParams, SignTypedDataParams, SignTypedDataResult, TransferParams, ApproveParams, CallAnyContractParams, CreateWalletParams, CreateWalletResponse, RecordSendTransactionParams, Transaction, GetTransactionListQuery, PaginatedResponse, GetWalletParams, GetTokenBalanceParams, GetTokenBalanceResponse, CreateSkuPurchaseParams, GetSkuListQuery, Sku, TransactionStatusResponse, PrepareWalletUpgradeParams, PrepareWalletUpgradeResponse, ExecuteWalletUpgradeParams, ExecuteWalletUpgradeResponse, ChipiServerSDKConfig, ChipiBrowserSDKConfig, X402ClientConfig, PaymentRequirement, X402PaymentConfig, ShhhSignerKind } from '@chipi-stack/types';
3
3
  export * from '@chipi-stack/types';
4
4
  export { Chain, ChainToken, Currency } from '@chipi-stack/types';
5
- import { ChipiWallets } from './wallets.js';
5
+ import { C as ChipiWallets } from './wallets-DxKnh21Q.js';
6
+ export { E as ExecuteMigrateResponse, M as MigrateChipiWalletToShhhParams, a as MigrationOutcome, P as PrepareMigrateResponse, m as migrateChipiWalletToShhh } from './wallets-DxKnh21Q.js';
6
7
  import { ChipiTransactions } from './transactions.js';
7
8
  import { ChipiSkuPurchases } from './skuPurchases.js';
8
9
  import { ChipiSkus } from './skus.js';
@@ -21,10 +22,22 @@ declare class ChipiSessions {
21
22
  private client;
22
23
  constructor(client: ChipiClient);
23
24
  /**
24
- * Validate that the wallet is a CHIPI wallet (required for session operations).
25
- * Throws ChipiSessionError if wallet type is not CHIPI.
25
+ * Validate that the wallet supports SNIP-163 session keys.
26
+ *
27
+ * Both CHIPI v29 (sessions-smart-contract) and SHHH V8.4
28
+ * (ShhhAccount) embed the same SNIP-163 SessionKeyComponent, so
29
+ * `add_or_update_session_key` / `revoke_session_key` /
30
+ * `get_session_data` selectors exist on both. The 4-felt session
31
+ * signature shape (`[session_pubkey, r, s, valid_until]`) is the
32
+ * same on both — V8.4's `execute_from_outside_v2` checks
33
+ * `signature.len() == 4` at account.cairo:389 BEFORE the
34
+ * V2_SNIP12 envelope dispatch, so sessions land through the
35
+ * legacy fast path on V8.4 wallets too.
36
+ *
37
+ * READY (Argent X v0.4.0) does NOT implement SNIP-163; reject with
38
+ * a clear error.
26
39
  */
27
- private validateChipiWallet;
40
+ private validateWalletSupportsSessions;
28
41
  /**
29
42
  * Convert a Uint8Array to hex string with 0x prefix.
30
43
  */
@@ -38,6 +51,15 @@ declare class ChipiSessions {
38
51
  *
39
52
  * After generating, call `addSessionKeyToContract()` to register the session on-chain.
40
53
  *
54
+ * **PIN is weak — not recommended for production.** `encryptKey` here
55
+ * is the symmetric key the SDK uses to encrypt the session's private
56
+ * key at rest. If you're passing a user-typed PIN, anyone who
57
+ * observes that PIN can decrypt the session. Derive `encryptKey`
58
+ * from a platform passkey (`@chipi-stack/chipi-passkey`) — for SHHH
59
+ * V8.4 wallets prefer `createShhhPasskey` + `signShhhMessage`, where
60
+ * the wallet's primary key never needs to be decrypted client-side
61
+ * at all.
62
+ *
41
63
  * @param params - Session creation parameters
42
64
  * @returns SessionKeyData for external storage
43
65
  *
@@ -459,7 +481,7 @@ declare class ChipiSDK {
459
481
  * });
460
482
  * ```
461
483
  */
462
- signTypedData({ params, }: {
484
+ signTypedData({ params }: {
463
485
  params: SignTypedDataParams;
464
486
  }): Promise<SignTypedDataResult>;
465
487
  /**
@@ -625,6 +647,126 @@ declare const signTypedData: ({ params, }: {
625
647
  declare const encryptPrivateKey: (privateKey: string, password: string) => string;
626
648
  declare const decryptPrivateKey: (encryptedPrivateKey: string, password: string) => string;
627
649
 
650
+ /**
651
+ * SHHH V8.4 buyer client for the x402 payment protocol.
652
+ *
653
+ * The CHIPI v29 / Argent `X402Client` (in `@chipi-stack/core`) signs
654
+ * a SNIP-12 typed-data message with `account.signMessage()` and lets
655
+ * the facilitator build the on-chain USDC transfer. That path does
656
+ * not work for SHHH V8.4 wallets: V8.4's
657
+ * `execute_from_outside_v2` at `account.cairo:389` rejects raw
658
+ * `{r, s}` (2-felt) signatures and dispatches based on
659
+ * `signature[0]` to either the V2_SNIP12 envelope path (single-owner
660
+ * primary verifier) or the 4-felt session-sig fast path.
661
+ *
662
+ * `X402ShhhClient` builds the full execute_from_outside_v2 calldata
663
+ * + signs the V8.4 SNIP-12 hash locally with the primary STARK key,
664
+ * then ships the pre-signed span in the `payload.shhh.oeCalldata`
665
+ * field. The facilitator (`X402Facilitator.settle`) detects
666
+ * `payload.shhh` and forwards verbatim through
667
+ * `/transactions/execute-sponsored-raw`. Self-custodial:
668
+ * `encryptKey` decrypts the wallet's STARK key client-side; chipi-
669
+ * back never sees the cleartext.
670
+ *
671
+ * Same `fetch(url, init)` surface as `X402Client` so an integrator
672
+ * can pick the right client at construction time based on their
673
+ * wallet type:
674
+ *
675
+ * const client = wallet.walletType === "SHHH"
676
+ * ? new X402ShhhClient({ wallet, encryptKey, chipiClient, bearerToken })
677
+ * : new X402Client(account);
678
+ *
679
+ * Wave B ships STARK + ED25519 primary signers; the SDK rejects
680
+ * other kinds with a clear error so a future external-rooted SHHH
681
+ * wallet (MetaMask, Phantom, passkey, Apple) doesn't silently
682
+ * produce a sig that reverts on chain.
683
+ */
684
+
685
+ interface FetchResponse {
686
+ status: number;
687
+ headers: {
688
+ get(name: string): string | null;
689
+ };
690
+ }
691
+ /**
692
+ * The minimal wallet shape the SHHH client needs to sign payments.
693
+ * Matches the `WalletData`-derived row callers already pass to the
694
+ * rest of the SDK.
695
+ */
696
+ interface ShhhX402Wallet {
697
+ /** Deployed account address (NOT the raw STARK pubkey). */
698
+ publicKey: string;
699
+ /** Wallet's PIN/passkey-encrypted STARK private key (ciphertext). */
700
+ encryptedPrivateKey: string;
701
+ /**
702
+ * Defaults to `"STARK"`. Future external-rooted kinds will need
703
+ * their own signing path; the client throws when handed an
704
+ * unsupported kind so silent on-chain reverts are impossible.
705
+ */
706
+ signerKind?: "STARK" | "ED25519";
707
+ }
708
+ interface X402ShhhClientConfig extends X402ClientConfig {
709
+ /**
710
+ * Override the chain id used for SHHH SNIP-12 hashing. Defaults to
711
+ * Starknet mainnet per CLAUDE.md; sepolia is auto-inferred from
712
+ * the chipi-back base URL when present. Drift here = the on-chain
713
+ * V8.4 wallet rejects the signature.
714
+ */
715
+ chainId?: bigint | string;
716
+ }
717
+ /**
718
+ * SHHH-aware buyer client.
719
+ */
720
+ declare class X402ShhhClient {
721
+ private readonly wallet;
722
+ private readonly encryptKey;
723
+ private readonly chipiClient;
724
+ private readonly bearerToken;
725
+ private readonly config;
726
+ private readonly fetchFn;
727
+ constructor(opts: {
728
+ /** The SHHH wallet making the payment. */
729
+ wallet: ShhhX402Wallet;
730
+ /** PIN / passkey-derived encryption key for the wallet's STARK key. */
731
+ encryptKey: string;
732
+ /** Chipi HTTP client; only used by the facilitator side. The client side
733
+ * keeps it for forward-compat if a future verifier flow needs to
734
+ * query chipi-back's chain-id helper at sign-time. */
735
+ chipiClient: ChipiClient;
736
+ /** Bearer token for chipi-back. Server-side only; never crosses the wire
737
+ * to an x402 seller. */
738
+ bearerToken: string;
739
+ config?: X402ShhhClientConfig;
740
+ });
741
+ /**
742
+ * Drop-in `fetch` replacement. If the seller returns 402, signs a
743
+ * payment + retries with the X-PAYMENT header. Same surface as
744
+ * `X402Client.fetch`.
745
+ */
746
+ fetch(url: string, init?: Record<string, any>): Promise<FetchResponse>;
747
+ parsePaymentRequired(response: FetchResponse): PaymentRequirement;
748
+ private validateRequirement;
749
+ /**
750
+ * Sign an x402 payment as a SHHH V8.4 buyer. Builds the V8.4 OE
751
+ * containing the single USDC.transfer call, computes the SNIP-12
752
+ * hash against the wallet address + chain id, signs locally with
753
+ * the decrypted STARK private key, wraps the result in a V2_SNIP12
754
+ * envelope, and serializes the full execute_from_outside_v2
755
+ * calldata. Returns a PaymentPayload with the `shhh` extension
756
+ * populated; the facilitator's settle branch forwards
757
+ * `payload.shhh.oeCalldata` verbatim to
758
+ * `/transactions/execute-sponsored-raw`.
759
+ *
760
+ * The `signature: {r, s}` field is also populated with the inner
761
+ * `(r, s)` pair so the wire format stays valid for receivers that
762
+ * only inspect the legacy field. The facilitator uses the SHHH
763
+ * branch when `payload.shhh` is present, so the redundant `{r, s}`
764
+ * is informational, not load-bearing.
765
+ */
766
+ signPayment(requirement: PaymentRequirement): Promise<PaymentPayload>;
767
+ private retryWithPayment;
768
+ }
769
+
628
770
  interface X402MiddlewareConfig {
629
771
  /** x402 facilitator instance for verify + settle */
630
772
  facilitator: X402Facilitator;
@@ -674,4 +816,1436 @@ interface X402MiddlewareFlatConfig {
674
816
  */
675
817
  declare function x402Middleware(config: X402MiddlewareConfig | X402MiddlewareFlatConfig): (req: any, res: any, next: any) => Promise<any>;
676
818
 
677
- export { ChipiBrowserSDK, ChipiClient, ChipiPaymasterAdapter, ChipiSDK, ChipiServerSDK, ChipiSessions, ChipiSkuPurchases, ChipiSkus, ChipiTransactions, ChipiWallets, X402Facilitator, type X402FacilitatorCreateOptions, type X402MiddlewareConfig, type X402MiddlewareFlatConfig, decryptPrivateKey, encryptPrivateKey, signTypedData, x402Middleware };
819
+ /**
820
+ * SNIP-12 typed-data hash for `OutsideExecution` — TypeScript reference.
821
+ *
822
+ * Vendored from haycarlitos/shhh-wallet-cairo @ v8.4 commit 3975532
823
+ * source: scripts/ts/snip12-hash.ts
824
+ * license: MIT (Copyright (c) 2026 Carlos Castillo and contributors)
825
+ *
826
+ * MUST produce byte-identical output to `compute_snip12_hash` in
827
+ * `src/outside_execution.cairo` at the same commit. If the canonical
828
+ * Cairo source ships a breaking change, the upstream Shhh maintainer
829
+ * will open a PR against this file (per Q3 of SHHH_INTEGRATION_ANSWERS.md).
830
+ */
831
+
832
+ declare const STARKNET_MESSAGE_PREFIX: string;
833
+ declare const OE_DOMAIN_NAME: string;
834
+ declare const OE_DOMAIN_VERSION = 2n;
835
+ declare const OE_DOMAIN_REVISION = 1n;
836
+ declare const SIG_VERSION_V1_HEX_ASCII: string;
837
+ declare const SIG_VERSION_V2_SNIP12: string;
838
+ declare const SIG_VERSION_V2_THRESHOLD: string;
839
+ declare const ISRC9_V2_ID: bigint;
840
+ declare const ISIGNER_ID: bigint;
841
+ declare const ISIGNER_CANONICAL_LABEL = "ISigner_V1";
842
+ /** Recomputes the canonical `ISIGNER_ID` at runtime. */
843
+ declare function computeISignerId(): bigint;
844
+ type OutsideExecution = {
845
+ caller: bigint | string;
846
+ nonce: bigint | string;
847
+ execute_after: bigint;
848
+ execute_before: bigint;
849
+ calls: Call[];
850
+ };
851
+ declare function hashCall(call: Call): bigint;
852
+ declare function hashCalls(calls: Call[]): bigint;
853
+ declare function hashStarknetDomain(chainIdFelt: bigint | string): bigint;
854
+ declare function hashOutsideExecutionStruct(oe: OutsideExecution): bigint;
855
+ /**
856
+ * The authoritative SNIP-12 message hash for an OutsideExecution.
857
+ * Signers sign over this felt252 (or its hex-ASCII re-encoding for
858
+ * Phantom UX compatibility — see snip12HashToHexAsciiBytes).
859
+ */
860
+ declare function computeSnip12Hash(oe: OutsideExecution, contractAddress: bigint | string, chainIdFelt: bigint | string): bigint;
861
+ /**
862
+ * Phantom hex-ASCII envelope helper. Phantom signs a string; we give it
863
+ * a 64-byte hex-ASCII representation of the SNIP-12 hash, and the Cairo
864
+ * verifier re-computes the hash and compares byte-by-byte.
865
+ */
866
+ declare function snip12HashToHexAsciiBytes(h: bigint): Uint8Array;
867
+
868
+ /**
869
+ * Deterministic ShhhAccount (V8.4) address derivation.
870
+ *
871
+ * Vendored from haycarlitos/shhh-wallet-cairo @ v8.4 commit 3975532
872
+ * source: scripts/ts/compute-wallet-address.ts
873
+ * license: MIT (Copyright (c) 2026 Carlos Castillo and contributors)
874
+ *
875
+ * Given the primary owner (kind + raw pubkey bytes) and the
876
+ * ShhhAccount class hash, returns the Starknet address the account
877
+ * will deploy to.
878
+ *
879
+ * MUST match `src/account.cairo::constructor` at the same commit. If
880
+ * the upstream constructor changes shape, the Shhh maintainer will
881
+ * open a PR against this file.
882
+ */
883
+ type ComputeAddressInput = {
884
+ /** SHHH ShhhAccount class hash — see WALLET_CLASS_HASHES.SHHH in @chipi-stack/shared. */
885
+ classHash: bigint;
886
+ /** Primary signer kind, e.g. 'STARK' / 'ED25519' / 'WEBAUTHN_P256'. */
887
+ primaryKind: string;
888
+ /** Raw pubkey bytes interpreted as felt252-sized halves (kind-specific layout). */
889
+ pubkey: bigint[];
890
+ /** Verifier class hash for primaryKind — see SHHH_VERIFIER_CLASS_HASHES. */
891
+ verifierClassHash: bigint;
892
+ /** Optional user-facing tag bound into the constructor (default 'primary'). */
893
+ label?: string;
894
+ /** Factory / UDC address; default 0 for self-deploy. */
895
+ deployerAddress?: bigint;
896
+ };
897
+ /** Mirrors `src/signer/interface.cairo::owner_commitment` = poseidon(kind_tag, pubkey...). */
898
+ declare function ownerCommitment(kindTag: bigint, pubkey: bigint[]): bigint;
899
+ /**
900
+ * Mirrors the V8.4 constructor's salt derivation:
901
+ * salt = poseidon(primary_kind, primary_pubkey_hash)
902
+ * where primary_pubkey_hash = owner_commitment(primary_kind, pubkey).
903
+ * Binding kind into the salt ensures the same raw key under two
904
+ * different kinds produces two distinct addresses.
905
+ */
906
+ declare function computeAddressSalt(primaryKind: string, pubkey: bigint[]): bigint;
907
+ /** Computes the deterministic deployment address for a SHHH ShhhAccount. */
908
+ declare function computeShhhAddress(input: ComputeAddressInput): bigint;
909
+
910
+ /**
911
+ * Outside-Execution construction for SHHH V8.4 ShhhAccount.
912
+ *
913
+ * Wraps the canonical SNIP-12 hash + envelope assembly so SDK callers
914
+ * never touch the underlying felt-array shapes directly. Implements
915
+ * the envelope routing per V8.4 handover §5:
916
+ *
917
+ * [ signature[0] ]
918
+ * ├─ 'V2_SNIP12' → [owner_id, kind_tag, ...payload] (single-owner)
919
+ * ├─ 'V2_THRESHOLD' → [n, env_1_len, ..., env_n_len, ...] (multisig)
920
+ * └─ (else, len==4) → SNIP-163 session-key envelope
921
+ *
922
+ * Source of truth: `src/account.cairo::execute_from_outside_v2` at
923
+ * shhh-wallet-cairo v8.4 commit 3975532.
924
+ */
925
+
926
+ /**
927
+ * The `caller` sentinel used for paymaster-sponsored OE flows. Any
928
+ * forwarder may submit the OE; binding `caller = ANY_CALLER` lifts the
929
+ * usual caller-equality check inside the account.
930
+ */
931
+ declare const ANY_CALLER_FELT: bigint;
932
+ /**
933
+ * Per audit M-2, OE validity windows must not exceed 2 hours. The
934
+ * V8.4 account enforces this on-chain; we expose the constant so SDK
935
+ * callers can default sensibly.
936
+ */
937
+ declare const MAX_OE_VALIDITY_WINDOW_SECONDS: number;
938
+ /**
939
+ * Build a standard paymaster-sponsored OutsideExecution with sensible
940
+ * defaults. Callers can override any field; the typical happy path is
941
+ * to pass only `nonce` + `calls`.
942
+ */
943
+ declare function buildOutsideExecution(params: {
944
+ /** Unique per-account replay nonce. The account stores these in `oe_nonces`. */
945
+ nonce: bigint | string;
946
+ /** The calls the account should execute on success. */
947
+ calls: Call[];
948
+ /** Override `caller`; defaults to ANY_CALLER for paymaster flows. */
949
+ caller?: bigint | string;
950
+ /**
951
+ * UNIX seconds. Defaults to `now - OE_EXECUTE_AFTER_CLOCK_SKEW_BUFFER`
952
+ * (60s in the past) so the OE is valid by the time the next block is
953
+ * minted regardless of small clock drifts between the SDK host and
954
+ * the sequencer. The wallet's check is strict `<` (`account.cairo:377`
955
+ * `'SRC9: now <= execute_after'`) — without the buffer, a fast
956
+ * SDK→paymaster round trip can land in the SAME block as the OE
957
+ * was built in and revert with `'SRC9: too early'`. PR sdks#289
958
+ * (this builder fix) made the buffer the default after the first
959
+ * session smoke run reproduced the race.
960
+ */
961
+ executeAfter?: bigint;
962
+ /** UNIX seconds; defaults to now + 2h (the maximum window per audit M-2). */
963
+ executeBefore?: bigint;
964
+ }): OutsideExecution;
965
+ /**
966
+ * Compute the SNIP-12 hash for the given OutsideExecution against
967
+ * a specific account address and chain. This is what the signer
968
+ * (STARK / Ed25519 / EIP-191 / WebAuthn / etc.) commits to.
969
+ *
970
+ * For Phantom UX where the user signs a string, hex-encode the
971
+ * resulting hash via `snip12HashToHexAsciiBytes` from snip12-hash.
972
+ */
973
+ declare function computeOeHash(oe: OutsideExecution, accountAddress: bigint | string, chainId: bigint | string): bigint;
974
+ /**
975
+ * Single-owner V2_SNIP12 envelope.
976
+ *
977
+ * The account strips signature[0..2] = ['V2_SNIP12', owner_id, kind_tag]
978
+ * and library_calls the matching verifier with `payload`. Chipi
979
+ * paymaster passes the entire envelope verbatim to
980
+ * `execute_from_outside_v2`; it does NOT need to know the kind.
981
+ *
982
+ * **`ownerId` is a u32 INDEX into the wallet's owner_set**, NOT the
983
+ * poseidon `owner_commitment`. Primary owner = `0n`; subsequent owners
984
+ * added via `propose_add_owner` get `1n`, `2n`, etc. The V8.4
985
+ * dispatcher (`src/account.cairo:480`) panics with `'SHHH: bad
986
+ * owner_id'` if signature[1] does not fit in u32 — which happens
987
+ * silently at the wallet level (outer paymaster tx still reports
988
+ * SUCCEEDED). Discovered 2026-05-27 in a production trace audit.
989
+ *
990
+ * Returns the `signature: felt252[]` span you pass to
991
+ * `paymaster_executeSponsoredRaw.calldata`.
992
+ */
993
+ declare function wrapV2Snip12Envelope(params: {
994
+ /** u32 INDEX into owner_set (0n for primary). NOT the commitment. */
995
+ ownerId: bigint;
996
+ kindTag: string | bigint;
997
+ payload: Array<bigint | string>;
998
+ }): bigint[];
999
+ /**
1000
+ * @deprecated since Wave B B2 — use `buildThresholdEnvelope` from
1001
+ * `@chipi-stack/backend/shhh/threshold` instead. This wrapper exists
1002
+ * for backward compat with v13.x and will be removed one minor
1003
+ * release after `chipi-stack@14.5.0` ships.
1004
+ *
1005
+ * Forwards to the validated builder. Note that callers passing
1006
+ * fewer than 2 envelopes (which the old version silently accepted
1007
+ * with a bogus layout) will now correctly throw — Cairo rejects
1008
+ * `n < 2` thresholds.
1009
+ */
1010
+ declare function wrapV2ThresholdEnvelope(innerEnvelopes: bigint[][]): bigint[];
1011
+
1012
+ /**
1013
+ * STARK ECDSA signer for SHHH V8.4 ShhhAccount.
1014
+ *
1015
+ * Cheapest verifier path (~2M l2_gas). Native Stark-curve ECDSA over
1016
+ * the SNIP-12 hash; the verifier class is a thin syscall wrapper.
1017
+ *
1018
+ * Reference: shhh-wallet-cairo/scripts/ts/gen-phase3-account-fixture.mjs
1019
+ * (same Stark signature shape, wrapped in V2_SNIP12).
1020
+ *
1021
+ * For non-STARK kinds (Ed25519, EIP-191, WebAuthn, JWT, BLS) the
1022
+ * envelope payload is per-kind — see sibling files in this directory.
1023
+ */
1024
+ /**
1025
+ * Build a V2_SNIP12 envelope for the STARK kind from a private key +
1026
+ * the OE's SNIP-12 hash. The envelope is the `signature` span you
1027
+ * pass to `paymaster_executeSponsoredRaw.calldata`.
1028
+ *
1029
+ * Payload for STARK is exactly `[r, s]` — the verifier reads these
1030
+ * two felts and invokes the chain's `ec_op` syscall.
1031
+ *
1032
+ * **Envelope layout (V8.4):** `[V2_SNIP12, owner_id, kind_tag, ...payload]`
1033
+ *
1034
+ * `owner_id` at position [1] is a **u32 INDEX into the owner_set**,
1035
+ * NOT the poseidon `owner_commitment`. For the primary owner of a
1036
+ * fresh deploy that index is `0n`; for additional owners added via
1037
+ * `propose_add_owner` it's `1n`, `2n`, etc. The V8.4 dispatcher
1038
+ * (`src/account.cairo:480`) does `(*signature.at(1)).try_into::<u32>()`
1039
+ * and panics with `'SHHH: bad owner_id'` if the felt exceeds u32 MAX —
1040
+ * which is exactly what happens if a poseidon commitment is passed
1041
+ * here instead of the index. (Discovered 2026-05-27 trace audit on
1042
+ * tx 0x1d1ee02e0dd1ffdbc6f0dddb426098d0dc87800eee3d0d0201e74d97ddc0a55.)
1043
+ */
1044
+ declare function buildStarkEnvelope(params: {
1045
+ /** STARK private key as bigint or 0x-prefixed hex. */
1046
+ privateKey: bigint | string;
1047
+ /** SNIP-12 hash of the OutsideExecution — see oe.ts::computeOeHash. */
1048
+ messageHash: bigint;
1049
+ /**
1050
+ * Owner index in the wallet's owner_set. **Defaults to 0n (primary).**
1051
+ * Override for additional owners added via `propose_add_owner`.
1052
+ * MUST fit in u32 (i.e., < 2^32). Passing the poseidon
1053
+ * `owner_commitment` here will revert on chain with
1054
+ * `'SHHH: bad owner_id'`.
1055
+ */
1056
+ ownerIndex?: bigint;
1057
+ }): bigint[];
1058
+ /**
1059
+ * Compute the V8.4 owner_id for a STARK-kind owner from its public
1060
+ * key. owner_id = owner_commitment(kind_tag, [pubkey]) per
1061
+ * `src/signer/interface.cairo::owner_commitment`. The account uses
1062
+ * this to dedupe owners and select the verifier class.
1063
+ */
1064
+ declare function starkOwnerId(starkPubKey: bigint): bigint;
1065
+ /**
1066
+ * STARK pubkey layout for the V8.4 constructor / propose_add_owner.
1067
+ * The Stark curve pubkey fits in one felt252, so this is a 1-element
1068
+ * array. Kept as a function for parity with kinds that split the
1069
+ * pubkey into multiple felts (Ed25519, secp256k1, P-256, BLS).
1070
+ */
1071
+ declare function starkPubkeyFelts(starkPubKey: bigint | string): bigint[];
1072
+
1073
+ /**
1074
+ * Ed25519 signer for SHHH V8.4 ShhhAccount.
1075
+ *
1076
+ * Used by Phantom (Solana) flows and any generic Ed25519 signer. The
1077
+ * V8.4 verifier (class `0x030a7dfc…949f74`) consumes a Cairo-ready
1078
+ * calldata array produced by Garaga's `eddsaCalldataBuilder` — it
1079
+ * contains the signature halves plus elliptic-curve hints the Cairo
1080
+ * verifier needs to keep gas bounded.
1081
+ *
1082
+ * Reference: shhh-wallet-cairo/scripts/ts/regen-ed25519-fixtures.mjs
1083
+ *
1084
+ * Verifier overhead: ~33M l2_gas (per SHHH_KIND_GAS_OVERHEAD).
1085
+ *
1086
+ * IMPORTANT — dynamic imports
1087
+ *
1088
+ * Garaga ships a ~1.2MB WASM bundle. We dynamic-import it so callers
1089
+ * who never touch Ed25519 don't pay the bundle cost. The first call
1090
+ * to `buildEd25519Envelope` will lazy-load + initialize Garaga; this
1091
+ * is irreversible and cached for the process lifetime. Cold-start
1092
+ * cost is ~200-400ms on Android, ~80-150ms on iPhone 13+.
1093
+ *
1094
+ * `@noble/ed25519` is also dynamic-imported for symmetry, though it
1095
+ * tree-shakes cleanly as a static import too.
1096
+ */
1097
+ /**
1098
+ * The Cairo Ed25519 verifier reads the SNIP-12 hash via its hex-ASCII
1099
+ * 64-byte encoding so the signature commits to a string that Phantom
1100
+ * (and any "sign-this-string" UX) can display. This helper is exposed
1101
+ * here for parity with the Cairo-side decoder; it's the same function
1102
+ * as `snip12HashToHexAsciiBytes` in snip12-hash.ts, re-exported under
1103
+ * a kind-specific name for discoverability.
1104
+ */
1105
+ declare function ed25519SignedBytes(messageHash: bigint): Uint8Array;
1106
+ /**
1107
+ * Compute the V8.4 owner_id for an Ed25519-kind owner from its public
1108
+ * key. owner_id = owner_commitment(ED25519_tag, [pub_low, pub_high])
1109
+ * — the same shape as `src/signer/ed25519/verifier.cairo` expects in
1110
+ * `validate_pubkey`.
1111
+ */
1112
+ declare function ed25519OwnerId(publicKey: Uint8Array): bigint;
1113
+ /**
1114
+ * Ed25519 pubkey felt layout for the V8.4 constructor / propose_add_owner.
1115
+ * Returns the low/high u128 halves of the 32-byte public key in that
1116
+ * order — what the Cairo `kind == ED25519` branch reads.
1117
+ */
1118
+ declare function ed25519PubkeyFelts(publicKey: Uint8Array): bigint[];
1119
+ /**
1120
+ * Build a V2_SNIP12 envelope for the Ed25519 kind. Async because the
1121
+ * underlying Garaga WASM must be initialized; subsequent calls reuse
1122
+ * the cached init.
1123
+ *
1124
+ * Inputs:
1125
+ * privateKey — 32-byte Ed25519 secret (Uint8Array, NOT hex/bigint:
1126
+ * the noble lib refuses anything else).
1127
+ * messageHash — the SNIP-12 hash of the OE (see oe.ts::computeOeHash).
1128
+ *
1129
+ * Output: the `signature` span you pass to
1130
+ * `paymaster_executeSponsoredRaw.calldata`.
1131
+ */
1132
+ declare function buildEd25519Envelope(params: {
1133
+ privateKey: Uint8Array;
1134
+ messageHash: bigint;
1135
+ /**
1136
+ * Owner index in the wallet's owner_set. **Defaults to 0n (primary).**
1137
+ * Override for additional owners added via `propose_add_owner`.
1138
+ * MUST fit in u32 (i.e., < 2^32). Passing the poseidon
1139
+ * `owner_commitment` (e.g. via `ed25519OwnerId`) here will revert
1140
+ * on chain with `'SHHH: bad owner_id'` — the bug Shhh team caught
1141
+ * 2026-05-27 + audit-trail 2026-05-28.
1142
+ */
1143
+ ownerIndex?: bigint;
1144
+ }): Promise<bigint[]>;
1145
+ /**
1146
+ * Build a V2_SNIP12 Ed25519 envelope from an **externally-produced**
1147
+ * signature — the Phantom / Solana flow. Phantom (and every external
1148
+ * Ed25519 wallet) never exposes the private key: the user signs via
1149
+ * `window.solana.signMessage(...)`, which returns a 64-byte signature
1150
+ * (R‖s). This is the from-signature counterpart of
1151
+ * `buildEd25519Envelope`, mirroring `buildEip191EnvelopeFromSignature`
1152
+ * for MetaMask.
1153
+ *
1154
+ * IMPORTANT — what the wallet MUST sign:
1155
+ * The wallet must sign the EXACT bytes returned by
1156
+ * `ed25519SignedBytes(messageHash)` — the 64-byte hex-ASCII encoding
1157
+ * of the SNIP-12 OE hash — NOT the raw 32-byte `messageHash`. The
1158
+ * Cairo verifier reconstructs those same bytes from `messageHash`, so
1159
+ * a mismatch fails on chain. The dashboard adapter does:
1160
+ * await window.solana.connect();
1161
+ * // Phantom returns the 32-byte Ed25519 pubkey as a Solana PublicKey;
1162
+ * // `.toBytes()` is the Uint8Array this API expects.
1163
+ * const publicKey = window.solana.publicKey.toBytes();
1164
+ * const msg = ed25519SignedBytes(messageHash);
1165
+ * // Phantom's signMessage returns { signature: Uint8Array (64B), publicKey }.
1166
+ * const { signature } = await window.solana.signMessage(msg);
1167
+ * const env = await buildEd25519EnvelopeFromSignature({
1168
+ * signature, publicKey, messageHash,
1169
+ * });
1170
+ *
1171
+ * Async (unlike the synchronous EIP-191 from-signature path): the
1172
+ * Garaga WASM hint builder must be initialized to produce the EC hints
1173
+ * the Cairo Ed25519 verifier needs. The issue sketch (#311) showed a
1174
+ * sync return; Ed25519 cannot avoid the WASM init that secp256k1 does.
1175
+ */
1176
+ declare function buildEd25519EnvelopeFromSignature(params: {
1177
+ /** 64-byte Ed25519 signature R(32)‖s(32) from the wallet's signMessage. */
1178
+ signature: Uint8Array;
1179
+ /** 32-byte Ed25519 public key (as Phantom returns it). */
1180
+ publicKey: Uint8Array;
1181
+ /** SNIP-12 hash of the OutsideExecution — see oe.ts::computeOeHash. */
1182
+ messageHash: bigint;
1183
+ /**
1184
+ * Owner index in the wallet's owner_set. **Defaults to 0n (primary).**
1185
+ * MUST fit in u32. Same semantics + footgun as `buildEd25519Envelope`:
1186
+ * pass the INDEX, never an `ed25519OwnerId` commitment.
1187
+ */
1188
+ ownerIndex?: bigint;
1189
+ }): Promise<bigint[]>;
1190
+
1191
+ /**
1192
+ * EIP-191 secp256k1 signer for SHHH V8.4 ShhhAccount.
1193
+ *
1194
+ * The signer kind for MetaMask + any EVM wallet that exposes the
1195
+ * `personal_sign` RPC. The wallet computes
1196
+ * keccak256("\x19Ethereum Signed Message:\n32" || msg_be32)
1197
+ * internally and signs with secp256k1; the V8.4 EIP191Secp256k1Verifier
1198
+ * (class `0x03a75997…084e6`) reproduces the same prefix-and-hash and
1199
+ * recovers the signing key from (r, s, y_parity) to compare to the
1200
+ * stored pubkey.
1201
+ *
1202
+ * Reference: shhh-wallet-cairo/scripts/ts/gen-eip191-fixture.mjs
1203
+ *
1204
+ * Verifier overhead: ~10M l2_gas (per SHHH_KIND_GAS_OVERHEAD).
1205
+ *
1206
+ * Two builder entry points:
1207
+ * - buildEip191EnvelopeFromSignature: production / MetaMask flow.
1208
+ * Caller already has the 65-byte signature back from `personal_sign`
1209
+ * and the wallet's pubkey (either pre-known or recovered).
1210
+ * - buildEip191EnvelopeFromPrivateKey: test / Chipi-held-key flow.
1211
+ * Sign locally with a known secp256k1 private key.
1212
+ *
1213
+ * Both produce the identical V2_SNIP12 envelope shape:
1214
+ * [V2_SNIP12, owner_id, EIP191_SECP256K1_tag,
1215
+ * r.low, r.high, s.low, s.high, y_parity]
1216
+ */
1217
+ /**
1218
+ * Uncompressed secp256k1 public key, split into 4 felt252-sized halves
1219
+ * matching what Cairo's EIP191 verifier expects in
1220
+ * `validate_pubkey` — `[x.low, x.high, y.low, y.high]` (big-endian
1221
+ * scalar, low_u128 first).
1222
+ */
1223
+ interface Eip191Pubkey {
1224
+ /** 32-byte big-endian X coordinate. */
1225
+ x: Uint8Array;
1226
+ /** 32-byte big-endian Y coordinate. */
1227
+ y: Uint8Array;
1228
+ }
1229
+ /**
1230
+ * Pubkey felt layout for the V8.4 constructor / propose_add_owner.
1231
+ * Returns `[x.low, x.high, y.low, y.high]` (each u128).
1232
+ */
1233
+ declare function eip191PubkeyFelts(pubkey: Eip191Pubkey): bigint[];
1234
+ /**
1235
+ * Compute the V8.4 owner_id for an EIP-191 owner.
1236
+ * owner_id = owner_commitment(EIP191_tag, [x.low, x.high, y.low, y.high]).
1237
+ */
1238
+ declare function eip191OwnerId(pubkey: Eip191Pubkey): bigint;
1239
+ /**
1240
+ * Parse a 65-byte `personal_sign`/`eth_sign` signature hex string into
1241
+ * the Cairo-shaped envelope payload `[r.low, r.high, s.low, s.high, y_parity]`.
1242
+ *
1243
+ * Accepts both `v ∈ {27, 28}` (EIP-191 wallet convention) and
1244
+ * `y_parity ∈ {0, 1}` raw forms.
1245
+ */
1246
+ declare function parseEip191Signature(signatureHex: string): bigint[];
1247
+ /**
1248
+ * Production / MetaMask flow. Caller already has the 65-byte signature
1249
+ * from `personal_sign` (e.g., `await provider.send("personal_sign", [...])`)
1250
+ * and the wallet's public key.
1251
+ */
1252
+ declare function buildEip191EnvelopeFromSignature(params: {
1253
+ /** 65-byte `personal_sign` signature, 0x-prefixed hex. */
1254
+ signatureHex: string;
1255
+ /** Uncompressed secp256k1 pubkey (x, y) as 32 BE bytes each. */
1256
+ pubkey: Eip191Pubkey;
1257
+ /**
1258
+ * Owner index in the wallet's owner_set. **Defaults to 0n (primary).**
1259
+ * MUST fit in u32. Passing `eip191OwnerId(pubkey)` (the commitment)
1260
+ * here will revert on chain with `'SHHH: bad owner_id'`.
1261
+ */
1262
+ ownerIndex?: bigint;
1263
+ }): bigint[];
1264
+ /**
1265
+ * Test / Chipi-held-key flow. Sign locally with a known secp256k1
1266
+ * private key using the exact `personal_sign` recipe MetaMask uses
1267
+ * (keccak256("\x19Ethereum Signed Message:\n32" || msg_be32),
1268
+ * secp256k1 sign of that hash).
1269
+ *
1270
+ * Async because @noble/secp256k1 v3 ships ESM and we dynamic-import.
1271
+ */
1272
+ declare function buildEip191EnvelopeFromPrivateKey(params: {
1273
+ /** 32-byte secp256k1 private key (Uint8Array OR 0x-prefixed hex). */
1274
+ privateKey: Uint8Array | string;
1275
+ /** SNIP-12 hash of the OutsideExecution — see oe.ts::computeOeHash. */
1276
+ messageHash: bigint;
1277
+ /**
1278
+ * Owner index in the wallet's owner_set. **Defaults to 0n (primary).**
1279
+ * MUST fit in u32. Same semantics as `buildEip191EnvelopeFromSignature`.
1280
+ */
1281
+ ownerIndex?: bigint;
1282
+ }): Promise<bigint[]>;
1283
+
1284
+ /**
1285
+ * WebAuthn P-256 signer for SHHH V8.4 ShhhAccount.
1286
+ *
1287
+ * The SHHH default signer kind in the browser. Wraps a WebAuthn
1288
+ * `navigator.credentials.get()` assertion (or a synthesized equivalent
1289
+ * in tests) into the V2_SNIP12 envelope the on-chain
1290
+ * WebAuthnP256Verifier (class `0x074f6efd…44657`) consumes.
1291
+ *
1292
+ * The verifier reproduces the WebAuthn assertion check on-chain:
1293
+ * 1. Parse `authData` (rpIdHash 32B || flags 1B || signCount 4B || …).
1294
+ * Require UP flag (bit 0) set.
1295
+ * 2. Compute `outer = sha256(authData ‖ sha256(clientDataJSON))`.
1296
+ * 3. Verify P-256 ECDSA `(r, s, y_parity)` against `outer` and the
1297
+ * stored pubkey.
1298
+ * 4. At `challenge_offset` in `clientDataJSON`, base64url-decode the
1299
+ * next 43 chars to 32 bytes and compare equal to the SNIP-12 hash.
1300
+ * 5. Sanity-check the `clientDataJSON` prefix
1301
+ * `{"type":"webauthn.get",…` (rejects webauthn.create / missing
1302
+ * type).
1303
+ *
1304
+ * Reference: shhh-wallet-cairo/scripts/ts/gen-webauthn-fixture.mjs
1305
+ * and tests/signer_webauthn_fixture.cairo
1306
+ *
1307
+ * Verifier overhead: ~35M l2_gas (per SHHH_KIND_GAS_OVERHEAD).
1308
+ *
1309
+ * Envelope shape (after the V2_SNIP12 + owner_id + kind_tag wrap):
1310
+ * ByteArray(authData)
1311
+ * ByteArray(clientDataJSON)
1312
+ * challenge_offset: u32
1313
+ * r.low, r.high, s.low, s.high, y_parity
1314
+ *
1315
+ * Cairo ByteArray serde:
1316
+ * num_full_words: u32
1317
+ * full_words: felt252[num_full_words] // 31 bytes packed MSB-first
1318
+ * pending_word: felt252 // <31 remaining bytes
1319
+ * pending_word_len: u32
1320
+ */
1321
+ interface WebAuthnPubkey {
1322
+ /** 32-byte big-endian P-256 X coordinate. */
1323
+ x: Uint8Array;
1324
+ /** 32-byte big-endian P-256 Y coordinate. */
1325
+ y: Uint8Array;
1326
+ }
1327
+ /**
1328
+ * Serialize a byte string as Cairo's `ByteArray` serde — must match
1329
+ * what the Cairo runtime would produce when this same bytes value is
1330
+ * passed as a `ByteArray` parameter.
1331
+ *
1332
+ * Layout: `[num_full_words, ...full_words, pending_word, pending_word_len]`.
1333
+ * Each full word packs 31 bytes MSB-first (network order) into a felt252.
1334
+ */
1335
+ declare function byteArrayToFelts(bytes: Uint8Array): bigint[];
1336
+ /**
1337
+ * Parse a DER-encoded ECDSA signature into raw (r, s) 32-byte BE values.
1338
+ * WebAuthn assertions return DER per spec; we strip it to the raw
1339
+ * P-256 components the Cairo verifier expects.
1340
+ */
1341
+ declare function parseDerEcdsaSignature(der: Uint8Array): {
1342
+ r: Uint8Array<ArrayBuffer>;
1343
+ s: Uint8Array<ArrayBuffer>;
1344
+ };
1345
+ declare function webauthnPubkeyFelts(pub: WebAuthnPubkey): bigint[];
1346
+ declare function webauthnOwnerId(pub: WebAuthnPubkey): bigint;
1347
+ /**
1348
+ * Production / browser flow. The caller has run
1349
+ * `navigator.credentials.get({ publicKey: { challenge: msgBe32, ... } })`
1350
+ * and now has the assertion's `authenticatorData`, `clientDataJSON`,
1351
+ * and DER-encoded `signature` (plus the wallet's stored P-256 pubkey).
1352
+ *
1353
+ * This builder:
1354
+ * 1. Parses the DER signature into raw r, s.
1355
+ * 2. Recovers y_parity from the signature (always 0; WebAuthn does
1356
+ * not expose recovery — the on-chain verifier compares the recovered
1357
+ * pubkey against the stored one and tries both parities if needed).
1358
+ * 3. Locates the challenge in clientDataJSON to compute challenge_offset.
1359
+ * 4. Encodes authData + clientDataJSON as Cairo ByteArrays.
1360
+ * 5. Wraps everything in the V2_SNIP12 envelope.
1361
+ */
1362
+ declare function buildWebAuthnEnvelopeFromAssertion(params: {
1363
+ authenticatorData: Uint8Array;
1364
+ clientDataJSON: Uint8Array;
1365
+ /** DER-encoded ECDSA signature from `assertion.response.signature`. */
1366
+ signatureDer: Uint8Array;
1367
+ /** P-256 pubkey stored at registration time. */
1368
+ pubkey: WebAuthnPubkey;
1369
+ /** SNIP-12 hash the challenge was constructed from. */
1370
+ messageHash: bigint;
1371
+ /**
1372
+ * y_parity recovery bit. WebAuthn assertions don't include this — pass
1373
+ * 0 (the verifier tries both parities internally when needed). Override
1374
+ * only if you know the bit independently.
1375
+ */
1376
+ yParity?: 0n | 1n;
1377
+ /**
1378
+ * Owner index in the wallet's owner_set. Defaults to 0n (primary).
1379
+ * MUST fit in u32. Passing `webauthnOwnerId(pubkey)` (the commitment)
1380
+ * here will revert on chain with `'SHHH: bad owner_id'`.
1381
+ */
1382
+ ownerIndex?: bigint;
1383
+ }): bigint[];
1384
+ /**
1385
+ * Test / Chipi-held-key flow. Synthesizes a WebAuthn assertion under
1386
+ * a known P-256 private key, then builds the envelope. Matches the
1387
+ * exact recipe in `gen-webauthn-fixture.mjs` so the resulting envelope
1388
+ * byte-for-byte matches `signer_webauthn_fixture.cairo`.
1389
+ *
1390
+ * Defaults match the canonical Shhh fixture: rpId="cifra.mx",
1391
+ * origin="https://cifra.mx", flags=0x05 (UP+UV), signCount=42.
1392
+ */
1393
+ declare function buildWebAuthnEnvelopeFromPrivateKey(params: {
1394
+ /** 32-byte P-256 private key (Uint8Array or 0x-prefixed hex). */
1395
+ privateKey: Uint8Array | string;
1396
+ /** SNIP-12 hash of the OutsideExecution. */
1397
+ messageHash: bigint;
1398
+ /** WebAuthn relying-party ID (the rpId field). Default: "cifra.mx". */
1399
+ rpId?: string;
1400
+ /** WebAuthn origin string. Default: "https://cifra.mx". */
1401
+ origin?: string;
1402
+ /** WebAuthn authData flags byte. Default 0x05 (UP+UV). */
1403
+ flags?: number;
1404
+ /** 32-bit sign count. Default 42. */
1405
+ signCount?: number;
1406
+ /**
1407
+ * Owner index in the wallet's owner_set. Defaults to 0n (primary).
1408
+ * MUST fit in u32. Same semantics as `buildWebAuthnEnvelopeFromAssertion`.
1409
+ */
1410
+ ownerIndex?: bigint;
1411
+ }): Promise<bigint[]>;
1412
+
1413
+ /**
1414
+ * JWT ES256 "Sign in with Apple" sub-bound signer for SHHH V8.4.
1415
+ *
1416
+ * Wave B PR B3. Adds the Apple Sign In signer kind
1417
+ * (`JWT_ES256_APPLE_SUB`, verifier class `0x06b67762...`). The headline
1418
+ * UX value is iOS-native onboarding: users without MetaMask but with
1419
+ * an iPhone authenticate with Apple, and the resulting JWT becomes a
1420
+ * SHHH owner. Multi-tenant safe via the `sub` claim binding.
1421
+ *
1422
+ * Cairo source of truth:
1423
+ * shhh-wallet-cairo/src/signer/jwt_es256_apple_sub/verifier.cairo
1424
+ *
1425
+ * Envelope layout (Cairo Serde over Span<felt252>):
1426
+ * header_b64 ByteArray // base64url JOSE header bytes
1427
+ * payload_decoded ByteArray // raw JSON claims (NOT base64url)
1428
+ * nonce_offset u32 // index of the 43-byte b64url nonce
1429
+ * iss_offset u32 // index of "https://appleid.apple.com"
1430
+ * sub_offset u32 // index of the sub claim VALUE
1431
+ * sub_len u32 // byte length of the sub claim VALUE
1432
+ * r u256 // low | high
1433
+ * s u256 // low | high
1434
+ * y_parity felt252 // 0 | 1
1435
+ *
1436
+ * Pubkey layout (5 felts):
1437
+ * [x_low, x_high, y_low, y_high, sub_hash]
1438
+ *
1439
+ * where sub_hash = poseidon_hash_span over the user's stable Apple
1440
+ * `sub` identifier, one felt per byte. The 5th felt binds the
1441
+ * on-chain owner to a SPECIFIC end-user — so multiple Chipi accounts
1442
+ * sharing Apple's same signing key can't be co-controlled by an
1443
+ * arbitrary Apple-authenticated user.
1444
+ *
1445
+ * Two flows:
1446
+ * - `buildJwtAppleSubEnvelopeFromTokenResponse` — production. Caller
1447
+ * drops in the Apple-issued `id_token` + the SNIP-12 message hash
1448
+ * that was committed to via the OE nonce.
1449
+ * - `buildJwtAppleSubEnvelopeFromTestKey` — synthesized test path.
1450
+ * Generates an ES256 keypair, constructs a JWT shaped like
1451
+ * Apple's, and signs it locally. Used for unit tests + the Wave
1452
+ * B B9 mainnet smoke. Real Apple-issued fixture upgrade comes
1453
+ * post-Wave-B (per plan D18).
1454
+ */
1455
+ /** The hardcoded `iss` claim every Apple JWT carries. 25 ASCII bytes. */
1456
+ declare const APPLE_ISSUER = "https://appleid.apple.com";
1457
+ /**
1458
+ * Uncompressed P-256 public key for Apple's current ES256 signing key.
1459
+ * Apple rotates this periodically — see the JWKS endpoint at
1460
+ * `https://appleid.apple.com/auth/keys`.
1461
+ */
1462
+ interface JwtAppleSubPubkey {
1463
+ /** 32-byte big-endian X coordinate. */
1464
+ x: Uint8Array;
1465
+ /** 32-byte big-endian Y coordinate. */
1466
+ y: Uint8Array;
1467
+ /** The user's stable Apple `sub` identifier (ASCII), e.g. "001234.abc.5678". */
1468
+ sub: string;
1469
+ }
1470
+ /**
1471
+ * What you get back from `https://appleid.apple.com/auth/token` after
1472
+ * a Sign-in-with-Apple OAuth flow. Only `id_token` is signature-bearing;
1473
+ * `access_token` and friends are out of scope here.
1474
+ */
1475
+ interface AppleTokenResponse {
1476
+ id_token: string;
1477
+ }
1478
+ /**
1479
+ * Compute the sub_hash that the Cairo verifier checks against.
1480
+ * Equals `poseidon_hash_span` over the bytes of the sub string, one
1481
+ * felt per byte. The SDK and the verifier must agree byte-for-byte.
1482
+ */
1483
+ declare function jwtAppleSubHash(sub: string): bigint;
1484
+ /**
1485
+ * Pubkey felt layout for the V8.4 constructor / propose_add_owner.
1486
+ * Returns `[x.low, x.high, y.low, y.high, sub_hash]` (5 felts).
1487
+ */
1488
+ declare function jwtAppleSubPubkeyFelts(pubkey: JwtAppleSubPubkey): bigint[];
1489
+ /** owner_commitment for an Apple JWT owner. */
1490
+ declare function jwtAppleSubOwnerId(pubkey: JwtAppleSubPubkey): bigint;
1491
+ /**
1492
+ * base64url-no-padding encoding of a 32 BE-byte SNIP-12 hash. The
1493
+ * Cairo verifier asserts the 43 ASCII bytes at
1494
+ * `payload_decoded[nonce_offset .. nonce_offset+43]` equal exactly
1495
+ * this string. Format must match RFC 4648 §5 (no padding `=`).
1496
+ */
1497
+ declare function messageHashToNonceB64url(messageHash: bigint): string;
1498
+ /**
1499
+ * Split an Apple-issued `id_token` into its three base64url parts.
1500
+ * Returns the decoded payload bytes + the header b64url + the
1501
+ * 64-byte raw ECDSA signature (r || s).
1502
+ */
1503
+ declare function parseAppleIdToken(idToken: string): {
1504
+ headerB64: string;
1505
+ payloadDecoded: Uint8Array;
1506
+ rawSignature: Uint8Array;
1507
+ };
1508
+ /**
1509
+ * Locate the sub claim's VALUE bytes in a decoded JSON payload.
1510
+ * Searches for the literal `"sub":"<value>"` pattern. JSON allows
1511
+ * whitespace, but Apple's id_token always uses canonical
1512
+ * no-whitespace form, so we don't try to handle pretty-printed JSON.
1513
+ *
1514
+ * Returns `{ offset, length }` of the value bytes (not including the
1515
+ * surrounding quotes), suitable for `sub_offset` + `sub_len` in the
1516
+ * envelope.
1517
+ */
1518
+ declare function locateSubClaim(payload: Uint8Array): {
1519
+ offset: number;
1520
+ length: number;
1521
+ };
1522
+ /**
1523
+ * Production flow. Build a V2_SNIP12 envelope wrapping an actual
1524
+ * Apple-issued JWT. Caller has invoked the OAuth flow client-side
1525
+ * (e.g. via `appleid.com` JS SDK or AuthenticationServices on iOS)
1526
+ * and received an `id_token` whose `nonce` claim equals
1527
+ * `messageHashToNonceB64url(oeMessageHash)`.
1528
+ *
1529
+ * Returns the `signature` felt span you pass to
1530
+ * `paymaster_executeSponsoredRaw.calldata`.
1531
+ */
1532
+ declare function buildJwtAppleSubEnvelopeFromTokenResponse(params: {
1533
+ /** Apple OAuth token endpoint response (we only need `id_token`). */
1534
+ idToken: string;
1535
+ /** Owner's full pubkey including sub — same as registered on-chain. */
1536
+ pubkey: JwtAppleSubPubkey;
1537
+ /** SNIP-12 hash of the OE — the nonce in the id_token MUST encode this. */
1538
+ messageHash: bigint;
1539
+ /**
1540
+ * Owner index in the wallet's owner_set. Defaults to 0n (primary).
1541
+ * MUST fit in u32. Passing `jwtAppleSubOwnerId(pubkey)` (the commitment)
1542
+ * here will revert on chain with `'SHHH: bad owner_id'`.
1543
+ */
1544
+ ownerIndex?: bigint;
1545
+ }): bigint[];
1546
+ /**
1547
+ * Test / synthesized-fixture flow. Generate an ES256 P-256 keypair,
1548
+ * construct a JWT shaped like an Apple-issued one, sign it locally,
1549
+ * and wrap into a SHHH envelope.
1550
+ *
1551
+ * Used by unit tests + the Wave B B9 mainnet smoke. The wallet
1552
+ * deployed for the smoke must be registered with the SAME synthesized
1553
+ * pubkey returned here (`{ x, y, sub }`); production wallets register
1554
+ * Apple's actual JWKS pubkey + the user's real sub.
1555
+ *
1556
+ * Async because `@noble/curves` ESM imports.
1557
+ *
1558
+ * Real-Apple-Developer-issued fixture upgrade is tracked as a separate
1559
+ * post-Wave-B item (plan D18).
1560
+ */
1561
+ declare function buildJwtAppleSubEnvelopeFromTestKey(params: {
1562
+ /** 32-byte raw P-256 private key. */
1563
+ privateKey: Uint8Array;
1564
+ /** Stable Apple-style sub identifier (e.g. "001234.synthesized.5678"). */
1565
+ sub: string;
1566
+ /** Audience claim — typically the integrator's Apple Developer team ID + bundle ID. */
1567
+ aud: string;
1568
+ /** SNIP-12 hash of the OE; encoded as the nonce claim. */
1569
+ messageHash: bigint;
1570
+ /**
1571
+ * Owner index in the wallet's owner_set. Defaults to 0n (primary).
1572
+ * MUST fit in u32. Same semantics as `buildJwtAppleSubEnvelopeFromTokenResponse`.
1573
+ */
1574
+ ownerIndex?: bigint;
1575
+ }): Promise<{
1576
+ envelope: bigint[];
1577
+ pubkey: JwtAppleSubPubkey;
1578
+ idToken: string;
1579
+ }>;
1580
+
1581
+ /**
1582
+ * SHHH V8.4 governance + recovery call builders.
1583
+ *
1584
+ * Wave B PR B1. The V8.4 ShhhAccount gates every owner-set mutation
1585
+ * through a propose → wait-timelock → execute flow (`@external` fns
1586
+ * in `src/account.cairo`). Each entrypoint is `_assert_self_call`-gated,
1587
+ * which means callers wrap these `Call`s inside an OutsideExecution and
1588
+ * submit through the paymaster.
1589
+ *
1590
+ * This module produces the `Call` (`{ contractAddress, entrypoint,
1591
+ * calldata }`) shape that `oe.ts:buildOutsideExecution` expects. It
1592
+ * does NOT sign — that's the integrator's choice of signer kind. For
1593
+ * recovery initiation specifically, the signer MUST be a guardian owner
1594
+ * (`role == ROLE_GUARDIAN` on-chain).
1595
+ *
1596
+ * Sources of truth on the Cairo side:
1597
+ *
1598
+ * src/account.cairo:616 propose_add_owner
1599
+ * src/account.cairo:634 propose_remove_owner
1600
+ * src/account.cairo:649 propose_rotate_owner
1601
+ * src/account.cairo:670 propose_set_threshold
1602
+ * src/account.cairo:731 execute_add_owner
1603
+ * src/account.cairo:760 execute_remove_owner
1604
+ * src/account.cairo:769 execute_rotate_owner
1605
+ * src/account.cairo:786 execute_set_threshold
1606
+ * src/account.cairo:828 cancel_pending_op
1607
+ * src/account.cairo:864 initiate_recovery
1608
+ * src/account.cairo:888 cancel_recovery
1609
+ * src/account.cairo:897 finalize_recovery
1610
+ * src/governance/pending_ops.cairo OP_* + TIMELOCK_* felt constants
1611
+ * src/owner_set/interface.cairo ROLE_* felt constants
1612
+ *
1613
+ * Timelock semantics
1614
+
1615
+ * propose_add_owner 48h
1616
+ * propose_remove_owner 24h
1617
+ * propose_rotate_owner 24h
1618
+ * propose_set_threshold 48h
1619
+ * initiate_recovery 7d (longest — the headline UX gate)
1620
+ *
1621
+ * Recovery is ADDITIVE on V8.4: `finalize_recovery` adds the new owner
1622
+ * but does not remove existing ones. The integrator's UI is expected
1623
+ * to follow with `propose_remove_owner` for stale devices.
1624
+ *
1625
+ * finalize_recovery is the only entrypoint here that is NOT
1626
+ * `_assert_self_call`-gated — it's permissionless after the 7d window,
1627
+ * so anyone can call it directly without an OE wrapper. The function
1628
+ * still returns a Call so the integrator can dispatch via their
1629
+ * normal account abstraction layer.
1630
+ */
1631
+
1632
+ /** `src/owner_set/interface.cairo:10` — `pub const ROLE_OWNER: felt252 = 'OWNER'`. */
1633
+ declare const ROLE_OWNER: bigint;
1634
+ /** `src/owner_set/interface.cairo:11` — `pub const ROLE_GUARDIAN: felt252 = 'GUARDIAN'`. */
1635
+ declare const ROLE_GUARDIAN: bigint;
1636
+ /** `src/governance/pending_ops.cairo:19` — `OP_ADD_OWNER: felt252 = 'ADD_OWNER'`. */
1637
+ declare const OP_ADD_OWNER: bigint;
1638
+ declare const OP_REMOVE_OWNER: bigint;
1639
+ declare const OP_ROTATE_OWNER: bigint;
1640
+ declare const OP_SET_THRESHOLD: bigint;
1641
+ /**
1642
+ * Per-op timelocks in seconds. Match `src/governance/pending_ops.cairo:42-50`
1643
+ * exactly. Exposed so SDK consumers can predict timelock expiry without
1644
+ * an extra on-chain call after `proposeAddOwner` succeeds.
1645
+ */
1646
+ declare const TIMELOCK_SECONDS: {
1647
+ readonly ADD_OWNER: 172800;
1648
+ readonly REMOVE_OWNER: 86400;
1649
+ readonly ROTATE_OWNER: 86400;
1650
+ readonly SET_THRESHOLD: 172800;
1651
+ readonly ADD_VERIFIER: 172800;
1652
+ readonly REMOVE_VERIFIER: 86400;
1653
+ readonly ADD_GUARDIAN: 86400;
1654
+ readonly REMOVE_GUARDIAN: 86400;
1655
+ readonly RECOVERY: 604800;
1656
+ };
1657
+ /** `DEFAULT_OP_EXPIRY_SECONDS` from `pending_ops.cairo:78`. After this
1658
+ * window from propose-time, the pending op auto-expires even if no one
1659
+ * calls cancel. UI should reflect this so users don't sit on a stale op. */
1660
+ declare const DEFAULT_OP_EXPIRY_SECONDS = 1209600;
1661
+ /** Owner role on-chain. ROLE_OWNER can sign OEs; ROLE_GUARDIAN can
1662
+ * only `initiate_recovery` (no general signing power). */
1663
+ type OwnerRole = "OWNER" | "GUARDIAN";
1664
+ /**
1665
+ * Inputs shared across add-owner / rotate / initiate-recovery — they
1666
+ * all encode a (kind, pubkey, role, weight, label) tuple.
1667
+ *
1668
+ * `pubkeyBytes` is the per-kind felt-array layout exposed by the
1669
+ * matching `*PubkeyFelts` helper in `signers/{stark,ed25519,eip191,
1670
+ * webauthn}.ts`. The Cairo verifier calls `owner_commitment(kind,
1671
+ * pubkeyBytes)` to derive the on-chain owner_id; if the bytes
1672
+ * disagree with the kind's verifier expectation,
1673
+ * `_validate_pubkey_via_verifier` panics at execute time (audit M-1).
1674
+ */
1675
+ interface NewOwnerInput {
1676
+ kind: string;
1677
+ pubkeyBytes: bigint[];
1678
+ role: OwnerRole;
1679
+ weight: number;
1680
+ label: string;
1681
+ }
1682
+ /**
1683
+ * Build a `propose_add_owner(proposer, kind, pubkey_bytes, role,
1684
+ * weight, label)` call. Wrap in an OE signed by `proposer` and submit
1685
+ * through the paymaster. After confirmation, watch for the
1686
+ * `OpProposed` event to learn the `op_id`; then wait
1687
+ * `TIMELOCK_SECONDS.ADD_OWNER` and call `buildExecuteAddOwnerCall`.
1688
+ */
1689
+ declare function buildProposeAddOwnerCall(args: {
1690
+ walletAddress: bigint | string;
1691
+ proposer: number;
1692
+ newOwner: NewOwnerInput;
1693
+ }): Call;
1694
+ /**
1695
+ * Build an `execute_add_owner(op_id, kind, pubkey_bytes, role, weight,
1696
+ * label)` call. MUST be called after the 48h timelock from the matching
1697
+ * propose. The wallet recomputes the payload and asserts equality
1698
+ * before mutating `owner_set`.
1699
+ */
1700
+ declare function buildExecuteAddOwnerCall(args: {
1701
+ walletAddress: bigint | string;
1702
+ opId: bigint | string;
1703
+ newOwner: NewOwnerInput;
1704
+ }): Call;
1705
+ declare function buildProposeRemoveOwnerCall(args: {
1706
+ walletAddress: bigint | string;
1707
+ proposer: number;
1708
+ ownerId: number;
1709
+ }): Call;
1710
+ declare function buildExecuteRemoveOwnerCall(args: {
1711
+ walletAddress: bigint | string;
1712
+ opId: bigint | string;
1713
+ ownerId: number;
1714
+ }): Call;
1715
+ declare function buildProposeRotateOwnerCall(args: {
1716
+ walletAddress: bigint | string;
1717
+ proposer: number;
1718
+ ownerId: number;
1719
+ newPubkeyBytes: bigint[];
1720
+ }): Call;
1721
+ declare function buildExecuteRotateOwnerCall(args: {
1722
+ walletAddress: bigint | string;
1723
+ opId: bigint | string;
1724
+ ownerId: number;
1725
+ newPubkeyBytes: bigint[];
1726
+ }): Call;
1727
+ declare function buildProposeSetThresholdCall(args: {
1728
+ walletAddress: bigint | string;
1729
+ proposer: number;
1730
+ newThreshold: number;
1731
+ }): Call;
1732
+ declare function buildExecuteSetThresholdCall(args: {
1733
+ walletAddress: bigint | string;
1734
+ opId: bigint | string;
1735
+ newThreshold: number;
1736
+ }): Call;
1737
+ /**
1738
+ * Build a `cancel_pending_op(op_id)` call. Valid any time before the
1739
+ * op is executed; signed by any current owner via OE.
1740
+ */
1741
+ declare function buildCancelPendingOpCall(args: {
1742
+ walletAddress: bigint | string;
1743
+ opId: bigint | string;
1744
+ }): Call;
1745
+ /**
1746
+ * Build an `initiate_recovery(proposer, new_owner_kind, new_pubkey_bytes,
1747
+ * new_role, new_weight, new_label)` call.
1748
+ *
1749
+ * IMPORTANT: `proposer` MUST be an owner with `role == ROLE_GUARDIAN`.
1750
+ * If it's a regular owner, the wallet panics with
1751
+ * `'RECOVERY: not a guardian'`. The new owner being recovered to is
1752
+ * typically `role: "OWNER"` so it can sign after the timelock.
1753
+ *
1754
+ * After confirmation, the wallet starts a 7-day timelock
1755
+ * (`TIMELOCK_SECONDS.RECOVERY`). During the window:
1756
+ * - Any current ROLE_OWNER can call `cancel_recovery` to abort.
1757
+ * - After the window, anyone can call `finalize_recovery`.
1758
+ */
1759
+ declare function buildInitiateRecoveryCall(args: {
1760
+ walletAddress: bigint | string;
1761
+ /** owner_id of the guardian that's initiating */
1762
+ proposer: number;
1763
+ newOwner: NewOwnerInput;
1764
+ }): Call;
1765
+ /**
1766
+ * Build a `cancel_recovery(owner_id)` call. Signed by any current
1767
+ * ROLE_OWNER via OE — the canceler must hold a working signing key.
1768
+ * The owner_id arg identifies which owner is canceling (used by the
1769
+ * wallet for event attribution and the not-revoked / role-OWNER check).
1770
+ */
1771
+ declare function buildCancelRecoveryCall(args: {
1772
+ walletAddress: bigint | string;
1773
+ ownerId: number;
1774
+ }): Call;
1775
+ /**
1776
+ * Build a `finalize_recovery(new_owner_kind, new_pubkey_bytes, new_role,
1777
+ * new_weight, new_label)` call.
1778
+ *
1779
+ * Permissionless after the 7-day timelock. The wallet recomputes the
1780
+ * pubkey commitment and asserts equality against the stored value
1781
+ * from `initiate_recovery`. Per audit H-1, the recovery path also
1782
+ * library-calls the per-kind verifier so a poison-pill pubkey from a
1783
+ * compromised guardian still fails at finalize time, not silently.
1784
+ *
1785
+ * Recovery is additive — existing owners stay. Follow up with
1786
+ * `buildProposeRemoveOwnerCall` for the stale devices.
1787
+ *
1788
+ * Because this call is NOT `_assert_self_call`-gated, the integrator
1789
+ * can either wrap it in an OE (signed by anyone) or dispatch directly
1790
+ * via a regular invoke. The Call shape is the same.
1791
+ */
1792
+ declare function buildFinalizeRecoveryCall(args: {
1793
+ walletAddress: bigint | string;
1794
+ newOwner: NewOwnerInput;
1795
+ }): Call;
1796
+ /**
1797
+ * Compute the selector for a view; convenient for `provider.callContract`.
1798
+ * Exposed so SDK consumers can build their own `{ contractAddress,
1799
+ * entryPointSelector, calldata }` `Call`-shaped views without
1800
+ * re-deriving selectors.
1801
+ */
1802
+ declare const VIEW_SELECTORS: {
1803
+ readonly get_pending_op: string;
1804
+ readonly get_pending_recovery: string;
1805
+ readonly get_owner: string;
1806
+ };
1807
+ /**
1808
+ * Given a `valid_after` block timestamp (seconds) returned from
1809
+ * `get_pending_op` or `get_pending_recovery`, compute whether the
1810
+ * timelock has elapsed against a given clock reading. Cheap UI helper
1811
+ * so consumers don't reach for a date library. Accepts fractional
1812
+ * `number` seconds (e.g., `Date.now() / 1000`) — truncates via floor.
1813
+ */
1814
+ declare function isReadyToExecute(validAfterSecs: number | bigint, nowSecs: number): boolean;
1815
+ /**
1816
+ * Seconds remaining until a pending op / recovery becomes executable.
1817
+ * Returns 0 when the timelock has already elapsed. Useful for showing
1818
+ * a countdown in UI without a separate clock import. Accepts fractional
1819
+ * `number` seconds — truncates via floor.
1820
+ */
1821
+ declare function secondsUntilExecutable(validAfterSecs: number | bigint, nowSecs: number): number;
1822
+
1823
+ /**
1824
+ * SHHH V8.4 threshold envelope assembly + gas estimation.
1825
+ *
1826
+ * Wave B PR B2. The single-owner OE flow already ships via
1827
+ * `wrap_v2_snip12_envelope` in `oe.ts`. This module adds the
1828
+ * multi-owner threshold flow: N owners (possibly with mixed signer
1829
+ * kinds) co-sign a single OE, and the wallet's
1830
+ * `execute_from_outside_v2` walks the envelope to verify each owner
1831
+ * independently before checking total weight against the configured
1832
+ * threshold.
1833
+ *
1834
+ * Cairo dispatch shape (`src/account.cairo:418-460`):
1835
+ *
1836
+ * signature = [
1837
+ * V2_THRESHOLD, // tag
1838
+ * n, // u32, MUST be >= 2 and <= owner_count
1839
+ * env_1_len, // u32
1840
+ * ...env_1 (env_1_len felts),
1841
+ * env_2_len,
1842
+ * ...env_2,
1843
+ * ...
1844
+ * env_n_len,
1845
+ * ...env_n,
1846
+ * ]
1847
+ *
1848
+ * Each inner envelope is a V2_SNIP12 single-owner envelope
1849
+ * (`wrap_v2_snip12_envelope` output) from a different owner. Nested
1850
+ * threshold envelopes are NOT permitted — the Cairo `_verify_sub_envelope`
1851
+ * recursively checks the inner tag and rejects anything except
1852
+ * V2_SNIP12.
1853
+ *
1854
+ * Cairo-enforced invariants this module replicates client-side so
1855
+ * mis-shaped envelopes fail at the SDK boundary with a clear message
1856
+ * instead of with an opaque "THRESH: …" Starknet revert:
1857
+ *
1858
+ * - signature.len() >= 2
1859
+ * - n >= 2 (n=1 is invalid — use V2_SNIP12 directly for single-owner)
1860
+ * - each inner env_len >= 2
1861
+ * - no buffer overflow / truncation
1862
+ * - no trailing bytes after the last inner envelope
1863
+ * - no duplicate owner_id across inner envelopes (extracted from
1864
+ * envelope[1] of each inner)
1865
+ *
1866
+ * Total-weight vs threshold is enforced ON-CHAIN (the SDK can't know
1867
+ * the configured threshold without reading on-chain state). Use
1868
+ * `estimateThresholdGasOverhead` to predict paymaster fees.
1869
+ */
1870
+
1871
+ /** Minimum number of inner envelopes in a threshold (Cairo enforces >= 2). */
1872
+ declare const MIN_THRESHOLD_OWNERS = 2;
1873
+ /**
1874
+ * Validate that an envelope is a well-formed V2_SNIP12 single-owner
1875
+ * envelope suitable for inclusion in a threshold envelope. Returns
1876
+ * the extracted `owner_id` (envelope[1]) so the caller can dedupe.
1877
+ *
1878
+ * Rejects nested threshold envelopes (`signature[0] == V2_THRESHOLD`)
1879
+ * — V8.4 does not allow nesting; the Cairo `_verify_sub_envelope`
1880
+ * would revert with `'SRC9: bad tag'`.
1881
+ */
1882
+ declare function assertValidV2Snip12Envelope(envelope: bigint[]): bigint;
1883
+ /**
1884
+ * Build a V2_THRESHOLD envelope from a list of pre-signed V2_SNIP12
1885
+ * inner envelopes. Each inner envelope must come from a DIFFERENT
1886
+ * owner — duplicates fail loudly here instead of on-chain.
1887
+ *
1888
+ * The typical UI flow:
1889
+ * 1. Compute the OE message hash once (computeOeHash from oe.ts)
1890
+ * 2. Distribute the hash to N owners (push notification, QR, etc.)
1891
+ * 3. Each owner signs with their kind's envelope builder and
1892
+ * returns the resulting `bigint[]`
1893
+ * 4. UI collects the envelopes and calls this function
1894
+ * 5. UI submits the resulting threshold envelope as the
1895
+ * `signature` field of `paymaster_executeSponsoredRaw.calldata`
1896
+ *
1897
+ * `buildThresholdEnvelope` does NOT submit anything. It returns the
1898
+ * felt span that goes into the OE calldata.
1899
+ */
1900
+ declare function buildThresholdEnvelope(args: {
1901
+ innerEnvelopes: bigint[][];
1902
+ }): bigint[];
1903
+ /**
1904
+ * Split a V2_THRESHOLD envelope back into its constituent inner
1905
+ * V2_SNIP12 envelopes. Inverse of `buildThresholdEnvelope`.
1906
+ *
1907
+ * Useful for UI flows that need to display "N of M signers signed
1908
+ * so far" status without re-deriving from individual envelopes. Also
1909
+ * useful in tests for round-trip validation.
1910
+ *
1911
+ * Throws if the envelope is malformed (tag mismatch, n < 2, length
1912
+ * mismatch, trailing bytes). Mirrors the Cairo dispatch's structural
1913
+ * checks (`src/account.cairo:418-456`) so the same envelope that
1914
+ * would parse-fail here would also revert on-chain.
1915
+ */
1916
+ declare function parseThresholdEnvelope(envelope: bigint[]): {
1917
+ innerEnvelopes: bigint[][];
1918
+ };
1919
+ /**
1920
+ * Predict the on-chain L2 gas overhead a threshold envelope will
1921
+ * consume. Each inner envelope dispatches to its kind's verifier
1922
+ * class via `library_call`, so total overhead is the sum of
1923
+ * per-kind overheads.
1924
+ *
1925
+ * Returned value is the same unit as `SHHH_KIND_GAS_OVERHEAD` in
1926
+ * `@chipi-stack/shared` (raw L2 gas units). Use to predict paymaster
1927
+ * fees:
1928
+ * estimatedFee = baselineOEGas + estimateThresholdGasOverhead(owners)
1929
+ *
1930
+ * Does NOT include the wallet's threshold-validation loop overhead
1931
+ * (the deduplication walk and weight summation) — that's a constant
1932
+ * ~50K L2 gas regardless of N, and the paymaster's fee predictor
1933
+ * already accounts for it.
1934
+ */
1935
+ declare function estimateThresholdGasOverhead(owners: Array<{
1936
+ signerKind: ShhhSignerKind;
1937
+ }>): number;
1938
+
1939
+ /**
1940
+ * SHHH V8.4 migration builders — sessions / OZ → V8.4.
1941
+ *
1942
+ * Wave B PR B4. Covers two paths:
1943
+ *
1944
+ * 1. Happy-path atomic multicall (`buildMigrationCalls`)
1945
+ * Used when the legacy wallet is still operational and can sign
1946
+ * an OutsideExecution. The wallet emits one OE containing two
1947
+ * calls in order:
1948
+ *
1949
+ * upgrade(SHHH_ACCOUNT_CLASS_HASH)
1950
+ * bootstrap_from_sessions(public_key, STARK_VERIFIER_CLASS_HASH, label)
1951
+ *
1952
+ * Atomicity is the security property — between `upgrade` and
1953
+ * `bootstrap_from_sessions` an attacker watching the mempool
1954
+ * could call `bootstrap_from_sessions(attacker_pk, ...)` first
1955
+ * and seize the account. The OE bundles them so that race
1956
+ * window doesn't exist.
1957
+ *
1958
+ * 2. Stranded-state recovery (`buildBootstrapSignedCall` +
1959
+ * `computeBootstrapMessage` + `signBootstrapMessage`)
1960
+ * Used when the legacy multicall ran but `bootstrap_from_sessions`
1961
+ * silently reverted (the old chipi-pay/sessions-smart-contract's
1962
+ * `_execute_calls` swallowed subcall errors at a
1963
+ * `Result::Err(_) => res.append(array![].span())` site). The
1964
+ * account is at V8.4 class but `primary_kind == 0`.
1965
+ *
1966
+ * Recovery: anyone calls
1967
+ *
1968
+ * bootstrap_from_sessions_signed(
1969
+ * public_key, stark_verifier_class, label, signature_r, signature_s
1970
+ * )
1971
+ *
1972
+ * where (r, s) is a STARK ECDSA signature over
1973
+ *
1974
+ * bootstrap_msg = poseidon([
1975
+ * 'SHHH_BOOTSTRAP_V8_4',
1976
+ * account_address,
1977
+ * public_key,
1978
+ * stark_verifier_class,
1979
+ * label,
1980
+ * ])
1981
+ *
1982
+ * Authorization comes from two independent checks on-chain:
1983
+ * - the preserved `LEGACY_OZ_ACCOUNT_PUBKEY_SLOT` storage slot
1984
+ * must equal `public_key` (audit C-1 binding to the original
1985
+ * owner)
1986
+ * - the signature must verify under `public_key`
1987
+ *
1988
+ * So this entrypoint is callable from any sponsor / relayer
1989
+ * without a self-call channel, but is non-transferable: only the
1990
+ * original owner who controls the STARK private key can produce
1991
+ * a valid signature.
1992
+ *
1993
+ * Cairo source of truth:
1994
+ * shhh-wallet-cairo/src/account.cairo:1024 bootstrap_from_sessions
1995
+ * shhh-wallet-cairo/src/account.cairo:1113 bootstrap_from_sessions_signed
1996
+ * shhh-wallet-cairo/scripts/ts/migrate-sessions-wallet.ts (happy-path ref)
1997
+ *
1998
+ * What this module does NOT cover (out of scope for B4):
1999
+ * - The upstream OE-build + sign step. Caller wraps these `Call`s
2000
+ * with `buildOutsideExecution` + `buildStarkEnvelope` and submits
2001
+ * via the paymaster. Same flow as B1's governance builders.
2002
+ * - Verifying on-chain whether a wallet is stranded. Done by the
2003
+ * dashboard / UI; here we just produce the recovery call.
2004
+ */
2005
+
2006
+ /**
2007
+ * Domain separator the on-chain signed-bootstrap message uses as its
2008
+ * first felt. Mirrors `'SHHH_BOOTSTRAP_V8_4'` literal at
2009
+ * `src/account.cairo:1170`.
2010
+ */
2011
+ declare const BOOTSTRAP_DOMAIN_SEPARATOR: bigint;
2012
+ /**
2013
+ * Encode a label string as a Starknet short-string felt252.
2014
+ *
2015
+ * starknet.js `shortString.encodeShortString` accepts only ASCII
2016
+ * characters and rejects strings longer than 31 characters (it
2017
+ * stuffs one character per byte, and felt252 has 251 useful bits).
2018
+ * We pre-validate here so callers get a clear error instead of the
2019
+ * generic encoder revert; the previous "≤31 bytes" message was
2020
+ * misleading because non-ASCII strings would silently slip past a
2021
+ * UTF-16 `.length` check.
2022
+ */
2023
+ declare function labelToFelt(label: string): bigint;
2024
+ interface BuildMigrationCallsParams {
2025
+ /** Address of the legacy CHIPI v29 / sessions wallet being migrated. */
2026
+ legacyAccountAddress: bigint | string;
2027
+ /** STARK public key currently stored in the legacy wallet's OZ slot. */
2028
+ legacyPublicKey: bigint | string;
2029
+ /** Target SHHH ShhhAccount class hash (e.g. `WALLET_CLASS_HASHES.SHHH`). */
2030
+ shhhAccountClassHash: bigint | string;
2031
+ /** STARK verifier class hash (`SHHH_VERIFIER_CLASS_HASHES.STARK`). */
2032
+ starkVerifierClassHash: bigint | string;
2033
+ /** Optional user-facing tag bound into the V8.4 owner record (≤31 ASCII characters). */
2034
+ label?: string;
2035
+ }
2036
+ /**
2037
+ * Build the two-call atomic multicall that migrates a legacy
2038
+ * CHIPI v29 (chipi-pay/sessions-smart-contract) wallet to SHHH V8.4.
2039
+ *
2040
+ * Caller's responsibility:
2041
+ * 1. Wrap the returned `Call[]` in a single `OutsideExecution`
2042
+ * against the legacy wallet's address.
2043
+ * 2. Sign the OE with the legacy wallet's STARK key (the SDK's
2044
+ * legacy CHIPI flow already does this; use that signer here).
2045
+ * 3. Submit via the paymaster.
2046
+ *
2047
+ * The multicall MUST be atomic — if any call between the two could
2048
+ * land independently on chain (e.g. via mempool front-running), the
2049
+ * resulting `upgrade`-but-not-`bootstrap` state is exploitable.
2050
+ * Bundling them in a single OE makes both calls land atomically.
2051
+ *
2052
+ * Returns the calls in order: [upgrade, bootstrap_from_sessions].
2053
+ */
2054
+ declare function buildMigrationCalls(params: BuildMigrationCallsParams): Call[];
2055
+ interface BootstrapMessageParams {
2056
+ /** The (already-upgraded) SHHH wallet's contract address. */
2057
+ accountAddress: bigint | string;
2058
+ /** STARK public key originally stored in the legacy OZ slot. */
2059
+ publicKey: bigint | string;
2060
+ /** STARK verifier class hash to set as primary. */
2061
+ starkVerifierClassHash: bigint | string;
2062
+ /** User-facing tag bound into the V8.4 owner record (≤31 ASCII characters). */
2063
+ label?: string;
2064
+ }
2065
+ /**
2066
+ * Compute the canonical bootstrap message the on-chain
2067
+ * `bootstrap_from_sessions_signed` verifier will check the STARK
2068
+ * ECDSA signature against:
2069
+ *
2070
+ * poseidon([
2071
+ * 'SHHH_BOOTSTRAP_V8_4',
2072
+ * account_address,
2073
+ * public_key,
2074
+ * stark_verifier_class,
2075
+ * label,
2076
+ * ])
2077
+ *
2078
+ * Domain separation via the literal prefix means a signature produced
2079
+ * for this message can never be replayed as a different intent
2080
+ * (audit point — covered in `audit_poc_attacker_can_seize_any_stranded_wallet`).
2081
+ */
2082
+ declare function computeBootstrapMessage(params: BootstrapMessageParams): bigint;
2083
+ /**
2084
+ * Sign the canonical bootstrap message with a STARK private key.
2085
+ *
2086
+ * Used in two situations:
2087
+ * - Server-side test fixtures + the Wave B B9 mainnet smoke
2088
+ * - "Recovery as a service" flows where the SDK is allowed to hold
2089
+ * a temporary STARK key custodied for the user
2090
+ *
2091
+ * Most production flows will instead sign via the user's wallet
2092
+ * (e.g. by exposing the message hash to the legacy wallet's STARK
2093
+ * signing endpoint) and pass `(r, s)` directly to
2094
+ * `buildBootstrapSignedCall`.
2095
+ */
2096
+ declare function signBootstrapMessage(params: {
2097
+ privateKey: bigint | string;
2098
+ message: BootstrapMessageParams;
2099
+ }): {
2100
+ r: bigint;
2101
+ s: bigint;
2102
+ };
2103
+ interface BuildBootstrapSignedCallParams {
2104
+ /** The stranded SHHH wallet's contract address. */
2105
+ accountAddress: bigint | string;
2106
+ /** STARK public key originally stored in the legacy OZ slot. */
2107
+ publicKey: bigint | string;
2108
+ /** STARK verifier class hash. Must match the value the original migration intended. */
2109
+ starkVerifierClassHash: bigint | string;
2110
+ /** User-facing tag (≤31 ASCII characters). Must match the original migration intent. */
2111
+ label?: string;
2112
+ /** STARK ECDSA signature r component over `computeBootstrapMessage(...)`. */
2113
+ signatureR: bigint | string;
2114
+ /** STARK ECDSA signature s component. */
2115
+ signatureS: bigint | string;
2116
+ }
2117
+ /**
2118
+ * Build a `bootstrap_from_sessions_signed(public_key, stark_verifier_class,
2119
+ * label, signature_r, signature_s)` call against a stranded SHHH wallet.
2120
+ *
2121
+ * Permissionless on-chain — anyone (typically a sponsor / relayer)
2122
+ * can dispatch this call. The signature is the auth. So the integrator
2123
+ * does NOT need to wrap it in an OE from the wallet itself; it goes
2124
+ * out as a regular invoke from any STARK account.
2125
+ *
2126
+ * Returns the `Call` for the integrator's account-abstraction layer
2127
+ * (e.g. a chipi-back service account, or a one-off operator wallet).
2128
+ */
2129
+ declare function buildBootstrapSignedCall(params: BuildBootstrapSignedCallParams): Call;
2130
+
2131
+ /**
2132
+ * High-level SHHH V8.4 OE execution path for the backend SDK.
2133
+ *
2134
+ * This module is the glue between the low-level OE builders in
2135
+ * `oe.ts` + `signers/*.ts` and chipi-back's
2136
+ * `POST /transactions/execute-sponsored-raw` endpoint.
2137
+ *
2138
+ * Why this exists separately from the legacy
2139
+ * `executePaymasterTransaction` flow:
2140
+ *
2141
+ * - The Chipi paymaster cannot synthesize SHHH envelopes server-side
2142
+ * (each of the 10 V8.4 signer kinds has a different envelope
2143
+ * shape; the paymaster's design — see chipi-pay/paymaster#16 — is
2144
+ * to pass the signature span verbatim to `execute_from_outside_v2`).
2145
+ * - Therefore SHHH clients MUST build + sign the OE locally and
2146
+ * submit it pre-signed to a forwarding endpoint
2147
+ * (`/transactions/execute-sponsored-raw`).
2148
+ * - The legacy flow (`prepare-typed-data` → sign → `execute-sponsored-
2149
+ * transaction`) is unchanged and continues to serve CHIPI / READY
2150
+ * wallets.
2151
+ *
2152
+ * Scope (Wave B, this PR): STARK + ED25519 signer kinds.
2153
+ * EIP-191 / WEBAUTHN_P256 / JWT_ES256_APPLE_SUB still throw the
2154
+ * deferred-feature error until the external-rooted wallet adapter
2155
+ * flow lands.
2156
+ */
2157
+
2158
+ /**
2159
+ * Pick the chain ID for SNIP-12 hashing. If the caller did not provide
2160
+ * an explicit override, infer from the chipi-back base URL (sepolia
2161
+ * hostnames map to SN_SEPOLIA; everything else maps to SN_MAIN per
2162
+ * the project's mainnet-only invariant).
2163
+ *
2164
+ * Exported for unit-testing the resolver in isolation.
2165
+ */
2166
+ declare function resolveShhhChainId(params: {
2167
+ override?: bigint | string;
2168
+ baseUrl?: string;
2169
+ }): string;
2170
+ /**
2171
+ * Serialize an OutsideExecution struct + signature span into the
2172
+ * flat felt-array calldata that `execute_from_outside_v2` expects.
2173
+ *
2174
+ * Cairo's serde order (per shhh-wallet-cairo v8.4
2175
+ * `src/account.cairo::execute_from_outside_v2`):
2176
+ *
2177
+ * caller
2178
+ * nonce
2179
+ * execute_after (u64, 1 felt)
2180
+ * execute_before (u64, 1 felt)
2181
+ * calls.len()
2182
+ * for each Call:
2183
+ * to (felt)
2184
+ * selector (felt)
2185
+ * calldata.len()
2186
+ * ...calldata felts
2187
+ * signature.len()
2188
+ * ...signature felts
2189
+ *
2190
+ * Returns 0x-prefixed lowercase hex strings ready for direct
2191
+ * JSON-RPC submission via the chipi-back forwarder.
2192
+ */
2193
+ declare function serializeExecuteFromOutsideV2Calldata(params: {
2194
+ outsideExecution: {
2195
+ caller: bigint | string;
2196
+ nonce: bigint | string;
2197
+ execute_after: bigint;
2198
+ execute_before: bigint;
2199
+ calls: Call[];
2200
+ };
2201
+ signature: bigint[];
2202
+ }): string[];
2203
+ /**
2204
+ * Decrypt the wallet's STARK private key from its
2205
+ * `encryptedPrivateKey` ciphertext + the per-wallet `encryptKey` (PIN
2206
+ * or passkey-derived secret), build a SHHH V8.4 OE around the given
2207
+ * inner calls, sign it with the wallet's STARK key, serialize the
2208
+ * calldata for `execute_from_outside_v2`, and forward to chipi-back's
2209
+ * `/transactions/execute-sponsored-raw` endpoint.
2210
+ *
2211
+ * Returns the on-chain transaction hash.
2212
+ *
2213
+ * Pre-conditions:
2214
+ * - `wallet.walletType === "SHHH"`.
2215
+ * - `wallet.publicKey` is the deployed account address (NOT the raw
2216
+ * STARK pubkey; V8.4 deployment derives address from kind-bound
2217
+ * salt + verifier).
2218
+ * - `wallet.encryptedPrivateKey` decrypts cleanly with `encryptKey`.
2219
+ * - The wallet is already deployed (no deploy_and_invoke variant
2220
+ * here; that flow stays inside `createWallet`).
2221
+ */
2222
+ declare function executeShhhSponsoredRaw(params: {
2223
+ client: ChipiClient;
2224
+ bearerToken: string;
2225
+ wallet: {
2226
+ publicKey: string;
2227
+ encryptedPrivateKey: string;
2228
+ walletType?: string;
2229
+ };
2230
+ encryptKey: string;
2231
+ calls: Call[];
2232
+ /**
2233
+ * Defaults to `"STARK"`. Wave B ships STARK + ED25519. EIP-191 /
2234
+ * WEBAUTHN_P256 / JWT_ES256_APPLE_SUB are deferred to the
2235
+ * external-rooted wallet adapter PRs.
2236
+ */
2237
+ signerKind?: "STARK" | "ED25519";
2238
+ /** Override the OE nonce; default = `Date.now() << 24 | random24bit`. */
2239
+ nonce?: bigint;
2240
+ /** Optional externalUserId for the audit-log hint. */
2241
+ externalUserId?: string;
2242
+ /**
2243
+ * Override the chain ID used for SNIP-12 hashing. Defaults to
2244
+ * mainnet per CLAUDE.md's mainnet-only invariant; sepolia is auto-
2245
+ * inferred from the chipi-back base URL when present. Drift here
2246
+ * = the V8.4 account rejects the signature on chain.
2247
+ */
2248
+ chainId?: bigint | string;
2249
+ }): Promise<string>;
2250
+
2251
+ export { ANY_CALLER_FELT, APPLE_ISSUER, type AppleTokenResponse, BOOTSTRAP_DOMAIN_SEPARATOR, type BootstrapMessageParams, type BuildBootstrapSignedCallParams, type BuildMigrationCallsParams, ChipiBrowserSDK, ChipiClient, ChipiPaymasterAdapter, ChipiSDK, ChipiServerSDK, ChipiSessions, ChipiSkuPurchases, ChipiSkus, ChipiTransactions, ChipiWallets, type ComputeAddressInput, DEFAULT_OP_EXPIRY_SECONDS, type Eip191Pubkey, ISIGNER_CANONICAL_LABEL, ISIGNER_ID, ISRC9_V2_ID, type JwtAppleSubPubkey, MAX_OE_VALIDITY_WINDOW_SECONDS, MIN_THRESHOLD_OWNERS, type NewOwnerInput, OE_DOMAIN_NAME, OE_DOMAIN_REVISION, OE_DOMAIN_VERSION, OP_ADD_OWNER, OP_REMOVE_OWNER, OP_ROTATE_OWNER, OP_SET_THRESHOLD, type OutsideExecution, type OwnerRole, ROLE_GUARDIAN, ROLE_OWNER, SIG_VERSION_V1_HEX_ASCII, SIG_VERSION_V2_SNIP12, SIG_VERSION_V2_THRESHOLD, STARKNET_MESSAGE_PREFIX, type ShhhX402Wallet, TIMELOCK_SECONDS, VIEW_SELECTORS, type WebAuthnPubkey, X402Facilitator, type X402FacilitatorCreateOptions, type X402MiddlewareConfig, type X402MiddlewareFlatConfig, X402ShhhClient, type X402ShhhClientConfig, assertValidV2Snip12Envelope, buildBootstrapSignedCall, buildCancelPendingOpCall, buildCancelRecoveryCall, buildEd25519Envelope, buildEd25519EnvelopeFromSignature, buildEip191EnvelopeFromPrivateKey, buildEip191EnvelopeFromSignature, buildExecuteAddOwnerCall, buildExecuteRemoveOwnerCall, buildExecuteRotateOwnerCall, buildExecuteSetThresholdCall, buildFinalizeRecoveryCall, buildInitiateRecoveryCall, buildJwtAppleSubEnvelopeFromTestKey, buildJwtAppleSubEnvelopeFromTokenResponse, buildMigrationCalls, buildOutsideExecution, buildProposeAddOwnerCall, buildProposeRemoveOwnerCall, buildProposeRotateOwnerCall, buildProposeSetThresholdCall, buildStarkEnvelope, buildThresholdEnvelope, buildWebAuthnEnvelopeFromAssertion, buildWebAuthnEnvelopeFromPrivateKey, byteArrayToFelts, computeAddressSalt, computeBootstrapMessage, computeISignerId, computeOeHash, computeShhhAddress, computeSnip12Hash, decryptPrivateKey, ed25519OwnerId, ed25519PubkeyFelts, ed25519SignedBytes, eip191OwnerId, eip191PubkeyFelts, encryptPrivateKey, estimateThresholdGasOverhead, executeShhhSponsoredRaw, hashCall, hashCalls, hashOutsideExecutionStruct, hashStarknetDomain, isReadyToExecute, jwtAppleSubHash, jwtAppleSubOwnerId, jwtAppleSubPubkeyFelts, labelToFelt, locateSubClaim, messageHashToNonceB64url, ownerCommitment, parseAppleIdToken, parseDerEcdsaSignature, parseEip191Signature, parseThresholdEnvelope, resolveShhhChainId, secondsUntilExecutable, serializeExecuteFromOutsideV2Calldata, signBootstrapMessage, signTypedData, snip12HashToHexAsciiBytes, starkOwnerId, starkPubkeyFelts, webauthnOwnerId, webauthnPubkeyFelts, wrapV2Snip12Envelope, wrapV2ThresholdEnvelope, x402Middleware };