@chipi-stack/backend 14.3.1 → 14.6.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.mts 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
- export { Chain, ChainToken } from '@chipi-stack/types';
5
- import { ChipiWallets } from './wallets.mjs';
4
+ export { Chain, ChainToken, Currency } from '@chipi-stack/types';
5
+ import { C as ChipiWallets } from './wallets-CYCLyyXY.mjs';
6
+ export { E as ExecuteMigrateResponse, M as MigrateChipiWalletToShhhParams, a as MigrationOutcome, P as PrepareMigrateResponse, m as migrateChipiWalletToShhh } from './wallets-CYCLyyXY.mjs';
6
7
  import { ChipiTransactions } from './transactions.mjs';
7
8
  import { ChipiSkuPurchases } from './skuPurchases.mjs';
8
9
  import { ChipiSkus } from './skus.mjs';
@@ -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
  *
@@ -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,1391 @@ 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
+ /**
1147
+ * EIP-191 secp256k1 signer for SHHH V8.4 ShhhAccount.
1148
+ *
1149
+ * The signer kind for MetaMask + any EVM wallet that exposes the
1150
+ * `personal_sign` RPC. The wallet computes
1151
+ * keccak256("\x19Ethereum Signed Message:\n32" || msg_be32)
1152
+ * internally and signs with secp256k1; the V8.4 EIP191Secp256k1Verifier
1153
+ * (class `0x03a75997…084e6`) reproduces the same prefix-and-hash and
1154
+ * recovers the signing key from (r, s, y_parity) to compare to the
1155
+ * stored pubkey.
1156
+ *
1157
+ * Reference: shhh-wallet-cairo/scripts/ts/gen-eip191-fixture.mjs
1158
+ *
1159
+ * Verifier overhead: ~10M l2_gas (per SHHH_KIND_GAS_OVERHEAD).
1160
+ *
1161
+ * Two builder entry points:
1162
+ * - buildEip191EnvelopeFromSignature: production / MetaMask flow.
1163
+ * Caller already has the 65-byte signature back from `personal_sign`
1164
+ * and the wallet's pubkey (either pre-known or recovered).
1165
+ * - buildEip191EnvelopeFromPrivateKey: test / Chipi-held-key flow.
1166
+ * Sign locally with a known secp256k1 private key.
1167
+ *
1168
+ * Both produce the identical V2_SNIP12 envelope shape:
1169
+ * [V2_SNIP12, owner_id, EIP191_SECP256K1_tag,
1170
+ * r.low, r.high, s.low, s.high, y_parity]
1171
+ */
1172
+ /**
1173
+ * Uncompressed secp256k1 public key, split into 4 felt252-sized halves
1174
+ * matching what Cairo's EIP191 verifier expects in
1175
+ * `validate_pubkey` — `[x.low, x.high, y.low, y.high]` (big-endian
1176
+ * scalar, low_u128 first).
1177
+ */
1178
+ interface Eip191Pubkey {
1179
+ /** 32-byte big-endian X coordinate. */
1180
+ x: Uint8Array;
1181
+ /** 32-byte big-endian Y coordinate. */
1182
+ y: Uint8Array;
1183
+ }
1184
+ /**
1185
+ * Pubkey felt layout for the V8.4 constructor / propose_add_owner.
1186
+ * Returns `[x.low, x.high, y.low, y.high]` (each u128).
1187
+ */
1188
+ declare function eip191PubkeyFelts(pubkey: Eip191Pubkey): bigint[];
1189
+ /**
1190
+ * Compute the V8.4 owner_id for an EIP-191 owner.
1191
+ * owner_id = owner_commitment(EIP191_tag, [x.low, x.high, y.low, y.high]).
1192
+ */
1193
+ declare function eip191OwnerId(pubkey: Eip191Pubkey): bigint;
1194
+ /**
1195
+ * Parse a 65-byte `personal_sign`/`eth_sign` signature hex string into
1196
+ * the Cairo-shaped envelope payload `[r.low, r.high, s.low, s.high, y_parity]`.
1197
+ *
1198
+ * Accepts both `v ∈ {27, 28}` (EIP-191 wallet convention) and
1199
+ * `y_parity ∈ {0, 1}` raw forms.
1200
+ */
1201
+ declare function parseEip191Signature(signatureHex: string): bigint[];
1202
+ /**
1203
+ * Production / MetaMask flow. Caller already has the 65-byte signature
1204
+ * from `personal_sign` (e.g., `await provider.send("personal_sign", [...])`)
1205
+ * and the wallet's public key.
1206
+ */
1207
+ declare function buildEip191EnvelopeFromSignature(params: {
1208
+ /** 65-byte `personal_sign` signature, 0x-prefixed hex. */
1209
+ signatureHex: string;
1210
+ /** Uncompressed secp256k1 pubkey (x, y) as 32 BE bytes each. */
1211
+ pubkey: Eip191Pubkey;
1212
+ /**
1213
+ * Owner index in the wallet's owner_set. **Defaults to 0n (primary).**
1214
+ * MUST fit in u32. Passing `eip191OwnerId(pubkey)` (the commitment)
1215
+ * here will revert on chain with `'SHHH: bad owner_id'`.
1216
+ */
1217
+ ownerIndex?: bigint;
1218
+ }): bigint[];
1219
+ /**
1220
+ * Test / Chipi-held-key flow. Sign locally with a known secp256k1
1221
+ * private key using the exact `personal_sign` recipe MetaMask uses
1222
+ * (keccak256("\x19Ethereum Signed Message:\n32" || msg_be32),
1223
+ * secp256k1 sign of that hash).
1224
+ *
1225
+ * Async because @noble/secp256k1 v3 ships ESM and we dynamic-import.
1226
+ */
1227
+ declare function buildEip191EnvelopeFromPrivateKey(params: {
1228
+ /** 32-byte secp256k1 private key (Uint8Array OR 0x-prefixed hex). */
1229
+ privateKey: Uint8Array | string;
1230
+ /** SNIP-12 hash of the OutsideExecution — see oe.ts::computeOeHash. */
1231
+ messageHash: bigint;
1232
+ /**
1233
+ * Owner index in the wallet's owner_set. **Defaults to 0n (primary).**
1234
+ * MUST fit in u32. Same semantics as `buildEip191EnvelopeFromSignature`.
1235
+ */
1236
+ ownerIndex?: bigint;
1237
+ }): Promise<bigint[]>;
1238
+
1239
+ /**
1240
+ * WebAuthn P-256 signer for SHHH V8.4 ShhhAccount.
1241
+ *
1242
+ * The SHHH default signer kind in the browser. Wraps a WebAuthn
1243
+ * `navigator.credentials.get()` assertion (or a synthesized equivalent
1244
+ * in tests) into the V2_SNIP12 envelope the on-chain
1245
+ * WebAuthnP256Verifier (class `0x074f6efd…44657`) consumes.
1246
+ *
1247
+ * The verifier reproduces the WebAuthn assertion check on-chain:
1248
+ * 1. Parse `authData` (rpIdHash 32B || flags 1B || signCount 4B || …).
1249
+ * Require UP flag (bit 0) set.
1250
+ * 2. Compute `outer = sha256(authData ‖ sha256(clientDataJSON))`.
1251
+ * 3. Verify P-256 ECDSA `(r, s, y_parity)` against `outer` and the
1252
+ * stored pubkey.
1253
+ * 4. At `challenge_offset` in `clientDataJSON`, base64url-decode the
1254
+ * next 43 chars to 32 bytes and compare equal to the SNIP-12 hash.
1255
+ * 5. Sanity-check the `clientDataJSON` prefix
1256
+ * `{"type":"webauthn.get",…` (rejects webauthn.create / missing
1257
+ * type).
1258
+ *
1259
+ * Reference: shhh-wallet-cairo/scripts/ts/gen-webauthn-fixture.mjs
1260
+ * and tests/signer_webauthn_fixture.cairo
1261
+ *
1262
+ * Verifier overhead: ~35M l2_gas (per SHHH_KIND_GAS_OVERHEAD).
1263
+ *
1264
+ * Envelope shape (after the V2_SNIP12 + owner_id + kind_tag wrap):
1265
+ * ByteArray(authData)
1266
+ * ByteArray(clientDataJSON)
1267
+ * challenge_offset: u32
1268
+ * r.low, r.high, s.low, s.high, y_parity
1269
+ *
1270
+ * Cairo ByteArray serde:
1271
+ * num_full_words: u32
1272
+ * full_words: felt252[num_full_words] // 31 bytes packed MSB-first
1273
+ * pending_word: felt252 // <31 remaining bytes
1274
+ * pending_word_len: u32
1275
+ */
1276
+ interface WebAuthnPubkey {
1277
+ /** 32-byte big-endian P-256 X coordinate. */
1278
+ x: Uint8Array;
1279
+ /** 32-byte big-endian P-256 Y coordinate. */
1280
+ y: Uint8Array;
1281
+ }
1282
+ /**
1283
+ * Serialize a byte string as Cairo's `ByteArray` serde — must match
1284
+ * what the Cairo runtime would produce when this same bytes value is
1285
+ * passed as a `ByteArray` parameter.
1286
+ *
1287
+ * Layout: `[num_full_words, ...full_words, pending_word, pending_word_len]`.
1288
+ * Each full word packs 31 bytes MSB-first (network order) into a felt252.
1289
+ */
1290
+ declare function byteArrayToFelts(bytes: Uint8Array): bigint[];
1291
+ /**
1292
+ * Parse a DER-encoded ECDSA signature into raw (r, s) 32-byte BE values.
1293
+ * WebAuthn assertions return DER per spec; we strip it to the raw
1294
+ * P-256 components the Cairo verifier expects.
1295
+ */
1296
+ declare function parseDerEcdsaSignature(der: Uint8Array): {
1297
+ r: Uint8Array<ArrayBuffer>;
1298
+ s: Uint8Array<ArrayBuffer>;
1299
+ };
1300
+ declare function webauthnPubkeyFelts(pub: WebAuthnPubkey): bigint[];
1301
+ declare function webauthnOwnerId(pub: WebAuthnPubkey): bigint;
1302
+ /**
1303
+ * Production / browser flow. The caller has run
1304
+ * `navigator.credentials.get({ publicKey: { challenge: msgBe32, ... } })`
1305
+ * and now has the assertion's `authenticatorData`, `clientDataJSON`,
1306
+ * and DER-encoded `signature` (plus the wallet's stored P-256 pubkey).
1307
+ *
1308
+ * This builder:
1309
+ * 1. Parses the DER signature into raw r, s.
1310
+ * 2. Recovers y_parity from the signature (always 0; WebAuthn does
1311
+ * not expose recovery — the on-chain verifier compares the recovered
1312
+ * pubkey against the stored one and tries both parities if needed).
1313
+ * 3. Locates the challenge in clientDataJSON to compute challenge_offset.
1314
+ * 4. Encodes authData + clientDataJSON as Cairo ByteArrays.
1315
+ * 5. Wraps everything in the V2_SNIP12 envelope.
1316
+ */
1317
+ declare function buildWebAuthnEnvelopeFromAssertion(params: {
1318
+ authenticatorData: Uint8Array;
1319
+ clientDataJSON: Uint8Array;
1320
+ /** DER-encoded ECDSA signature from `assertion.response.signature`. */
1321
+ signatureDer: Uint8Array;
1322
+ /** P-256 pubkey stored at registration time. */
1323
+ pubkey: WebAuthnPubkey;
1324
+ /** SNIP-12 hash the challenge was constructed from. */
1325
+ messageHash: bigint;
1326
+ /**
1327
+ * y_parity recovery bit. WebAuthn assertions don't include this — pass
1328
+ * 0 (the verifier tries both parities internally when needed). Override
1329
+ * only if you know the bit independently.
1330
+ */
1331
+ yParity?: 0n | 1n;
1332
+ /**
1333
+ * Owner index in the wallet's owner_set. Defaults to 0n (primary).
1334
+ * MUST fit in u32. Passing `webauthnOwnerId(pubkey)` (the commitment)
1335
+ * here will revert on chain with `'SHHH: bad owner_id'`.
1336
+ */
1337
+ ownerIndex?: bigint;
1338
+ }): bigint[];
1339
+ /**
1340
+ * Test / Chipi-held-key flow. Synthesizes a WebAuthn assertion under
1341
+ * a known P-256 private key, then builds the envelope. Matches the
1342
+ * exact recipe in `gen-webauthn-fixture.mjs` so the resulting envelope
1343
+ * byte-for-byte matches `signer_webauthn_fixture.cairo`.
1344
+ *
1345
+ * Defaults match the canonical Shhh fixture: rpId="cifra.mx",
1346
+ * origin="https://cifra.mx", flags=0x05 (UP+UV), signCount=42.
1347
+ */
1348
+ declare function buildWebAuthnEnvelopeFromPrivateKey(params: {
1349
+ /** 32-byte P-256 private key (Uint8Array or 0x-prefixed hex). */
1350
+ privateKey: Uint8Array | string;
1351
+ /** SNIP-12 hash of the OutsideExecution. */
1352
+ messageHash: bigint;
1353
+ /** WebAuthn relying-party ID (the rpId field). Default: "cifra.mx". */
1354
+ rpId?: string;
1355
+ /** WebAuthn origin string. Default: "https://cifra.mx". */
1356
+ origin?: string;
1357
+ /** WebAuthn authData flags byte. Default 0x05 (UP+UV). */
1358
+ flags?: number;
1359
+ /** 32-bit sign count. Default 42. */
1360
+ signCount?: number;
1361
+ /**
1362
+ * Owner index in the wallet's owner_set. Defaults to 0n (primary).
1363
+ * MUST fit in u32. Same semantics as `buildWebAuthnEnvelopeFromAssertion`.
1364
+ */
1365
+ ownerIndex?: bigint;
1366
+ }): Promise<bigint[]>;
1367
+
1368
+ /**
1369
+ * JWT ES256 "Sign in with Apple" sub-bound signer for SHHH V8.4.
1370
+ *
1371
+ * Wave B PR B3. Adds the Apple Sign In signer kind
1372
+ * (`JWT_ES256_APPLE_SUB`, verifier class `0x06b67762...`). The headline
1373
+ * UX value is iOS-native onboarding: users without MetaMask but with
1374
+ * an iPhone authenticate with Apple, and the resulting JWT becomes a
1375
+ * SHHH owner. Multi-tenant safe via the `sub` claim binding.
1376
+ *
1377
+ * Cairo source of truth:
1378
+ * shhh-wallet-cairo/src/signer/jwt_es256_apple_sub/verifier.cairo
1379
+ *
1380
+ * Envelope layout (Cairo Serde over Span<felt252>):
1381
+ * header_b64 ByteArray // base64url JOSE header bytes
1382
+ * payload_decoded ByteArray // raw JSON claims (NOT base64url)
1383
+ * nonce_offset u32 // index of the 43-byte b64url nonce
1384
+ * iss_offset u32 // index of "https://appleid.apple.com"
1385
+ * sub_offset u32 // index of the sub claim VALUE
1386
+ * sub_len u32 // byte length of the sub claim VALUE
1387
+ * r u256 // low | high
1388
+ * s u256 // low | high
1389
+ * y_parity felt252 // 0 | 1
1390
+ *
1391
+ * Pubkey layout (5 felts):
1392
+ * [x_low, x_high, y_low, y_high, sub_hash]
1393
+ *
1394
+ * where sub_hash = poseidon_hash_span over the user's stable Apple
1395
+ * `sub` identifier, one felt per byte. The 5th felt binds the
1396
+ * on-chain owner to a SPECIFIC end-user — so multiple Chipi accounts
1397
+ * sharing Apple's same signing key can't be co-controlled by an
1398
+ * arbitrary Apple-authenticated user.
1399
+ *
1400
+ * Two flows:
1401
+ * - `buildJwtAppleSubEnvelopeFromTokenResponse` — production. Caller
1402
+ * drops in the Apple-issued `id_token` + the SNIP-12 message hash
1403
+ * that was committed to via the OE nonce.
1404
+ * - `buildJwtAppleSubEnvelopeFromTestKey` — synthesized test path.
1405
+ * Generates an ES256 keypair, constructs a JWT shaped like
1406
+ * Apple's, and signs it locally. Used for unit tests + the Wave
1407
+ * B B9 mainnet smoke. Real Apple-issued fixture upgrade comes
1408
+ * post-Wave-B (per plan D18).
1409
+ */
1410
+ /** The hardcoded `iss` claim every Apple JWT carries. 25 ASCII bytes. */
1411
+ declare const APPLE_ISSUER = "https://appleid.apple.com";
1412
+ /**
1413
+ * Uncompressed P-256 public key for Apple's current ES256 signing key.
1414
+ * Apple rotates this periodically — see the JWKS endpoint at
1415
+ * `https://appleid.apple.com/auth/keys`.
1416
+ */
1417
+ interface JwtAppleSubPubkey {
1418
+ /** 32-byte big-endian X coordinate. */
1419
+ x: Uint8Array;
1420
+ /** 32-byte big-endian Y coordinate. */
1421
+ y: Uint8Array;
1422
+ /** The user's stable Apple `sub` identifier (ASCII), e.g. "001234.abc.5678". */
1423
+ sub: string;
1424
+ }
1425
+ /**
1426
+ * What you get back from `https://appleid.apple.com/auth/token` after
1427
+ * a Sign-in-with-Apple OAuth flow. Only `id_token` is signature-bearing;
1428
+ * `access_token` and friends are out of scope here.
1429
+ */
1430
+ interface AppleTokenResponse {
1431
+ id_token: string;
1432
+ }
1433
+ /**
1434
+ * Compute the sub_hash that the Cairo verifier checks against.
1435
+ * Equals `poseidon_hash_span` over the bytes of the sub string, one
1436
+ * felt per byte. The SDK and the verifier must agree byte-for-byte.
1437
+ */
1438
+ declare function jwtAppleSubHash(sub: string): bigint;
1439
+ /**
1440
+ * Pubkey felt layout for the V8.4 constructor / propose_add_owner.
1441
+ * Returns `[x.low, x.high, y.low, y.high, sub_hash]` (5 felts).
1442
+ */
1443
+ declare function jwtAppleSubPubkeyFelts(pubkey: JwtAppleSubPubkey): bigint[];
1444
+ /** owner_commitment for an Apple JWT owner. */
1445
+ declare function jwtAppleSubOwnerId(pubkey: JwtAppleSubPubkey): bigint;
1446
+ /**
1447
+ * base64url-no-padding encoding of a 32 BE-byte SNIP-12 hash. The
1448
+ * Cairo verifier asserts the 43 ASCII bytes at
1449
+ * `payload_decoded[nonce_offset .. nonce_offset+43]` equal exactly
1450
+ * this string. Format must match RFC 4648 §5 (no padding `=`).
1451
+ */
1452
+ declare function messageHashToNonceB64url(messageHash: bigint): string;
1453
+ /**
1454
+ * Split an Apple-issued `id_token` into its three base64url parts.
1455
+ * Returns the decoded payload bytes + the header b64url + the
1456
+ * 64-byte raw ECDSA signature (r || s).
1457
+ */
1458
+ declare function parseAppleIdToken(idToken: string): {
1459
+ headerB64: string;
1460
+ payloadDecoded: Uint8Array;
1461
+ rawSignature: Uint8Array;
1462
+ };
1463
+ /**
1464
+ * Locate the sub claim's VALUE bytes in a decoded JSON payload.
1465
+ * Searches for the literal `"sub":"<value>"` pattern. JSON allows
1466
+ * whitespace, but Apple's id_token always uses canonical
1467
+ * no-whitespace form, so we don't try to handle pretty-printed JSON.
1468
+ *
1469
+ * Returns `{ offset, length }` of the value bytes (not including the
1470
+ * surrounding quotes), suitable for `sub_offset` + `sub_len` in the
1471
+ * envelope.
1472
+ */
1473
+ declare function locateSubClaim(payload: Uint8Array): {
1474
+ offset: number;
1475
+ length: number;
1476
+ };
1477
+ /**
1478
+ * Production flow. Build a V2_SNIP12 envelope wrapping an actual
1479
+ * Apple-issued JWT. Caller has invoked the OAuth flow client-side
1480
+ * (e.g. via `appleid.com` JS SDK or AuthenticationServices on iOS)
1481
+ * and received an `id_token` whose `nonce` claim equals
1482
+ * `messageHashToNonceB64url(oeMessageHash)`.
1483
+ *
1484
+ * Returns the `signature` felt span you pass to
1485
+ * `paymaster_executeSponsoredRaw.calldata`.
1486
+ */
1487
+ declare function buildJwtAppleSubEnvelopeFromTokenResponse(params: {
1488
+ /** Apple OAuth token endpoint response (we only need `id_token`). */
1489
+ idToken: string;
1490
+ /** Owner's full pubkey including sub — same as registered on-chain. */
1491
+ pubkey: JwtAppleSubPubkey;
1492
+ /** SNIP-12 hash of the OE — the nonce in the id_token MUST encode this. */
1493
+ messageHash: bigint;
1494
+ /**
1495
+ * Owner index in the wallet's owner_set. Defaults to 0n (primary).
1496
+ * MUST fit in u32. Passing `jwtAppleSubOwnerId(pubkey)` (the commitment)
1497
+ * here will revert on chain with `'SHHH: bad owner_id'`.
1498
+ */
1499
+ ownerIndex?: bigint;
1500
+ }): bigint[];
1501
+ /**
1502
+ * Test / synthesized-fixture flow. Generate an ES256 P-256 keypair,
1503
+ * construct a JWT shaped like an Apple-issued one, sign it locally,
1504
+ * and wrap into a SHHH envelope.
1505
+ *
1506
+ * Used by unit tests + the Wave B B9 mainnet smoke. The wallet
1507
+ * deployed for the smoke must be registered with the SAME synthesized
1508
+ * pubkey returned here (`{ x, y, sub }`); production wallets register
1509
+ * Apple's actual JWKS pubkey + the user's real sub.
1510
+ *
1511
+ * Async because `@noble/curves` ESM imports.
1512
+ *
1513
+ * Real-Apple-Developer-issued fixture upgrade is tracked as a separate
1514
+ * post-Wave-B item (plan D18).
1515
+ */
1516
+ declare function buildJwtAppleSubEnvelopeFromTestKey(params: {
1517
+ /** 32-byte raw P-256 private key. */
1518
+ privateKey: Uint8Array;
1519
+ /** Stable Apple-style sub identifier (e.g. "001234.synthesized.5678"). */
1520
+ sub: string;
1521
+ /** Audience claim — typically the integrator's Apple Developer team ID + bundle ID. */
1522
+ aud: string;
1523
+ /** SNIP-12 hash of the OE; encoded as the nonce claim. */
1524
+ messageHash: bigint;
1525
+ /**
1526
+ * Owner index in the wallet's owner_set. Defaults to 0n (primary).
1527
+ * MUST fit in u32. Same semantics as `buildJwtAppleSubEnvelopeFromTokenResponse`.
1528
+ */
1529
+ ownerIndex?: bigint;
1530
+ }): Promise<{
1531
+ envelope: bigint[];
1532
+ pubkey: JwtAppleSubPubkey;
1533
+ idToken: string;
1534
+ }>;
1535
+
1536
+ /**
1537
+ * SHHH V8.4 governance + recovery call builders.
1538
+ *
1539
+ * Wave B PR B1. The V8.4 ShhhAccount gates every owner-set mutation
1540
+ * through a propose → wait-timelock → execute flow (`@external` fns
1541
+ * in `src/account.cairo`). Each entrypoint is `_assert_self_call`-gated,
1542
+ * which means callers wrap these `Call`s inside an OutsideExecution and
1543
+ * submit through the paymaster.
1544
+ *
1545
+ * This module produces the `Call` (`{ contractAddress, entrypoint,
1546
+ * calldata }`) shape that `oe.ts:buildOutsideExecution` expects. It
1547
+ * does NOT sign — that's the integrator's choice of signer kind. For
1548
+ * recovery initiation specifically, the signer MUST be a guardian owner
1549
+ * (`role == ROLE_GUARDIAN` on-chain).
1550
+ *
1551
+ * Sources of truth on the Cairo side:
1552
+ *
1553
+ * src/account.cairo:616 propose_add_owner
1554
+ * src/account.cairo:634 propose_remove_owner
1555
+ * src/account.cairo:649 propose_rotate_owner
1556
+ * src/account.cairo:670 propose_set_threshold
1557
+ * src/account.cairo:731 execute_add_owner
1558
+ * src/account.cairo:760 execute_remove_owner
1559
+ * src/account.cairo:769 execute_rotate_owner
1560
+ * src/account.cairo:786 execute_set_threshold
1561
+ * src/account.cairo:828 cancel_pending_op
1562
+ * src/account.cairo:864 initiate_recovery
1563
+ * src/account.cairo:888 cancel_recovery
1564
+ * src/account.cairo:897 finalize_recovery
1565
+ * src/governance/pending_ops.cairo OP_* + TIMELOCK_* felt constants
1566
+ * src/owner_set/interface.cairo ROLE_* felt constants
1567
+ *
1568
+ * Timelock semantics
1569
+
1570
+ * propose_add_owner 48h
1571
+ * propose_remove_owner 24h
1572
+ * propose_rotate_owner 24h
1573
+ * propose_set_threshold 48h
1574
+ * initiate_recovery 7d (longest — the headline UX gate)
1575
+ *
1576
+ * Recovery is ADDITIVE on V8.4: `finalize_recovery` adds the new owner
1577
+ * but does not remove existing ones. The integrator's UI is expected
1578
+ * to follow with `propose_remove_owner` for stale devices.
1579
+ *
1580
+ * finalize_recovery is the only entrypoint here that is NOT
1581
+ * `_assert_self_call`-gated — it's permissionless after the 7d window,
1582
+ * so anyone can call it directly without an OE wrapper. The function
1583
+ * still returns a Call so the integrator can dispatch via their
1584
+ * normal account abstraction layer.
1585
+ */
1586
+
1587
+ /** `src/owner_set/interface.cairo:10` — `pub const ROLE_OWNER: felt252 = 'OWNER'`. */
1588
+ declare const ROLE_OWNER: bigint;
1589
+ /** `src/owner_set/interface.cairo:11` — `pub const ROLE_GUARDIAN: felt252 = 'GUARDIAN'`. */
1590
+ declare const ROLE_GUARDIAN: bigint;
1591
+ /** `src/governance/pending_ops.cairo:19` — `OP_ADD_OWNER: felt252 = 'ADD_OWNER'`. */
1592
+ declare const OP_ADD_OWNER: bigint;
1593
+ declare const OP_REMOVE_OWNER: bigint;
1594
+ declare const OP_ROTATE_OWNER: bigint;
1595
+ declare const OP_SET_THRESHOLD: bigint;
1596
+ /**
1597
+ * Per-op timelocks in seconds. Match `src/governance/pending_ops.cairo:42-50`
1598
+ * exactly. Exposed so SDK consumers can predict timelock expiry without
1599
+ * an extra on-chain call after `proposeAddOwner` succeeds.
1600
+ */
1601
+ declare const TIMELOCK_SECONDS: {
1602
+ readonly ADD_OWNER: 172800;
1603
+ readonly REMOVE_OWNER: 86400;
1604
+ readonly ROTATE_OWNER: 86400;
1605
+ readonly SET_THRESHOLD: 172800;
1606
+ readonly ADD_VERIFIER: 172800;
1607
+ readonly REMOVE_VERIFIER: 86400;
1608
+ readonly ADD_GUARDIAN: 86400;
1609
+ readonly REMOVE_GUARDIAN: 86400;
1610
+ readonly RECOVERY: 604800;
1611
+ };
1612
+ /** `DEFAULT_OP_EXPIRY_SECONDS` from `pending_ops.cairo:78`. After this
1613
+ * window from propose-time, the pending op auto-expires even if no one
1614
+ * calls cancel. UI should reflect this so users don't sit on a stale op. */
1615
+ declare const DEFAULT_OP_EXPIRY_SECONDS = 1209600;
1616
+ /** Owner role on-chain. ROLE_OWNER can sign OEs; ROLE_GUARDIAN can
1617
+ * only `initiate_recovery` (no general signing power). */
1618
+ type OwnerRole = "OWNER" | "GUARDIAN";
1619
+ /**
1620
+ * Inputs shared across add-owner / rotate / initiate-recovery — they
1621
+ * all encode a (kind, pubkey, role, weight, label) tuple.
1622
+ *
1623
+ * `pubkeyBytes` is the per-kind felt-array layout exposed by the
1624
+ * matching `*PubkeyFelts` helper in `signers/{stark,ed25519,eip191,
1625
+ * webauthn}.ts`. The Cairo verifier calls `owner_commitment(kind,
1626
+ * pubkeyBytes)` to derive the on-chain owner_id; if the bytes
1627
+ * disagree with the kind's verifier expectation,
1628
+ * `_validate_pubkey_via_verifier` panics at execute time (audit M-1).
1629
+ */
1630
+ interface NewOwnerInput {
1631
+ kind: string;
1632
+ pubkeyBytes: bigint[];
1633
+ role: OwnerRole;
1634
+ weight: number;
1635
+ label: string;
1636
+ }
1637
+ /**
1638
+ * Build a `propose_add_owner(proposer, kind, pubkey_bytes, role,
1639
+ * weight, label)` call. Wrap in an OE signed by `proposer` and submit
1640
+ * through the paymaster. After confirmation, watch for the
1641
+ * `OpProposed` event to learn the `op_id`; then wait
1642
+ * `TIMELOCK_SECONDS.ADD_OWNER` and call `buildExecuteAddOwnerCall`.
1643
+ */
1644
+ declare function buildProposeAddOwnerCall(args: {
1645
+ walletAddress: bigint | string;
1646
+ proposer: number;
1647
+ newOwner: NewOwnerInput;
1648
+ }): Call;
1649
+ /**
1650
+ * Build an `execute_add_owner(op_id, kind, pubkey_bytes, role, weight,
1651
+ * label)` call. MUST be called after the 48h timelock from the matching
1652
+ * propose. The wallet recomputes the payload and asserts equality
1653
+ * before mutating `owner_set`.
1654
+ */
1655
+ declare function buildExecuteAddOwnerCall(args: {
1656
+ walletAddress: bigint | string;
1657
+ opId: bigint | string;
1658
+ newOwner: NewOwnerInput;
1659
+ }): Call;
1660
+ declare function buildProposeRemoveOwnerCall(args: {
1661
+ walletAddress: bigint | string;
1662
+ proposer: number;
1663
+ ownerId: number;
1664
+ }): Call;
1665
+ declare function buildExecuteRemoveOwnerCall(args: {
1666
+ walletAddress: bigint | string;
1667
+ opId: bigint | string;
1668
+ ownerId: number;
1669
+ }): Call;
1670
+ declare function buildProposeRotateOwnerCall(args: {
1671
+ walletAddress: bigint | string;
1672
+ proposer: number;
1673
+ ownerId: number;
1674
+ newPubkeyBytes: bigint[];
1675
+ }): Call;
1676
+ declare function buildExecuteRotateOwnerCall(args: {
1677
+ walletAddress: bigint | string;
1678
+ opId: bigint | string;
1679
+ ownerId: number;
1680
+ newPubkeyBytes: bigint[];
1681
+ }): Call;
1682
+ declare function buildProposeSetThresholdCall(args: {
1683
+ walletAddress: bigint | string;
1684
+ proposer: number;
1685
+ newThreshold: number;
1686
+ }): Call;
1687
+ declare function buildExecuteSetThresholdCall(args: {
1688
+ walletAddress: bigint | string;
1689
+ opId: bigint | string;
1690
+ newThreshold: number;
1691
+ }): Call;
1692
+ /**
1693
+ * Build a `cancel_pending_op(op_id)` call. Valid any time before the
1694
+ * op is executed; signed by any current owner via OE.
1695
+ */
1696
+ declare function buildCancelPendingOpCall(args: {
1697
+ walletAddress: bigint | string;
1698
+ opId: bigint | string;
1699
+ }): Call;
1700
+ /**
1701
+ * Build an `initiate_recovery(proposer, new_owner_kind, new_pubkey_bytes,
1702
+ * new_role, new_weight, new_label)` call.
1703
+ *
1704
+ * IMPORTANT: `proposer` MUST be an owner with `role == ROLE_GUARDIAN`.
1705
+ * If it's a regular owner, the wallet panics with
1706
+ * `'RECOVERY: not a guardian'`. The new owner being recovered to is
1707
+ * typically `role: "OWNER"` so it can sign after the timelock.
1708
+ *
1709
+ * After confirmation, the wallet starts a 7-day timelock
1710
+ * (`TIMELOCK_SECONDS.RECOVERY`). During the window:
1711
+ * - Any current ROLE_OWNER can call `cancel_recovery` to abort.
1712
+ * - After the window, anyone can call `finalize_recovery`.
1713
+ */
1714
+ declare function buildInitiateRecoveryCall(args: {
1715
+ walletAddress: bigint | string;
1716
+ /** owner_id of the guardian that's initiating */
1717
+ proposer: number;
1718
+ newOwner: NewOwnerInput;
1719
+ }): Call;
1720
+ /**
1721
+ * Build a `cancel_recovery(owner_id)` call. Signed by any current
1722
+ * ROLE_OWNER via OE — the canceler must hold a working signing key.
1723
+ * The owner_id arg identifies which owner is canceling (used by the
1724
+ * wallet for event attribution and the not-revoked / role-OWNER check).
1725
+ */
1726
+ declare function buildCancelRecoveryCall(args: {
1727
+ walletAddress: bigint | string;
1728
+ ownerId: number;
1729
+ }): Call;
1730
+ /**
1731
+ * Build a `finalize_recovery(new_owner_kind, new_pubkey_bytes, new_role,
1732
+ * new_weight, new_label)` call.
1733
+ *
1734
+ * Permissionless after the 7-day timelock. The wallet recomputes the
1735
+ * pubkey commitment and asserts equality against the stored value
1736
+ * from `initiate_recovery`. Per audit H-1, the recovery path also
1737
+ * library-calls the per-kind verifier so a poison-pill pubkey from a
1738
+ * compromised guardian still fails at finalize time, not silently.
1739
+ *
1740
+ * Recovery is additive — existing owners stay. Follow up with
1741
+ * `buildProposeRemoveOwnerCall` for the stale devices.
1742
+ *
1743
+ * Because this call is NOT `_assert_self_call`-gated, the integrator
1744
+ * can either wrap it in an OE (signed by anyone) or dispatch directly
1745
+ * via a regular invoke. The Call shape is the same.
1746
+ */
1747
+ declare function buildFinalizeRecoveryCall(args: {
1748
+ walletAddress: bigint | string;
1749
+ newOwner: NewOwnerInput;
1750
+ }): Call;
1751
+ /**
1752
+ * Compute the selector for a view; convenient for `provider.callContract`.
1753
+ * Exposed so SDK consumers can build their own `{ contractAddress,
1754
+ * entryPointSelector, calldata }` `Call`-shaped views without
1755
+ * re-deriving selectors.
1756
+ */
1757
+ declare const VIEW_SELECTORS: {
1758
+ readonly get_pending_op: string;
1759
+ readonly get_pending_recovery: string;
1760
+ readonly get_owner: string;
1761
+ };
1762
+ /**
1763
+ * Given a `valid_after` block timestamp (seconds) returned from
1764
+ * `get_pending_op` or `get_pending_recovery`, compute whether the
1765
+ * timelock has elapsed against a given clock reading. Cheap UI helper
1766
+ * so consumers don't reach for a date library. Accepts fractional
1767
+ * `number` seconds (e.g., `Date.now() / 1000`) — truncates via floor.
1768
+ */
1769
+ declare function isReadyToExecute(validAfterSecs: number | bigint, nowSecs: number): boolean;
1770
+ /**
1771
+ * Seconds remaining until a pending op / recovery becomes executable.
1772
+ * Returns 0 when the timelock has already elapsed. Useful for showing
1773
+ * a countdown in UI without a separate clock import. Accepts fractional
1774
+ * `number` seconds — truncates via floor.
1775
+ */
1776
+ declare function secondsUntilExecutable(validAfterSecs: number | bigint, nowSecs: number): number;
1777
+
1778
+ /**
1779
+ * SHHH V8.4 threshold envelope assembly + gas estimation.
1780
+ *
1781
+ * Wave B PR B2. The single-owner OE flow already ships via
1782
+ * `wrap_v2_snip12_envelope` in `oe.ts`. This module adds the
1783
+ * multi-owner threshold flow: N owners (possibly with mixed signer
1784
+ * kinds) co-sign a single OE, and the wallet's
1785
+ * `execute_from_outside_v2` walks the envelope to verify each owner
1786
+ * independently before checking total weight against the configured
1787
+ * threshold.
1788
+ *
1789
+ * Cairo dispatch shape (`src/account.cairo:418-460`):
1790
+ *
1791
+ * signature = [
1792
+ * V2_THRESHOLD, // tag
1793
+ * n, // u32, MUST be >= 2 and <= owner_count
1794
+ * env_1_len, // u32
1795
+ * ...env_1 (env_1_len felts),
1796
+ * env_2_len,
1797
+ * ...env_2,
1798
+ * ...
1799
+ * env_n_len,
1800
+ * ...env_n,
1801
+ * ]
1802
+ *
1803
+ * Each inner envelope is a V2_SNIP12 single-owner envelope
1804
+ * (`wrap_v2_snip12_envelope` output) from a different owner. Nested
1805
+ * threshold envelopes are NOT permitted — the Cairo `_verify_sub_envelope`
1806
+ * recursively checks the inner tag and rejects anything except
1807
+ * V2_SNIP12.
1808
+ *
1809
+ * Cairo-enforced invariants this module replicates client-side so
1810
+ * mis-shaped envelopes fail at the SDK boundary with a clear message
1811
+ * instead of with an opaque "THRESH: …" Starknet revert:
1812
+ *
1813
+ * - signature.len() >= 2
1814
+ * - n >= 2 (n=1 is invalid — use V2_SNIP12 directly for single-owner)
1815
+ * - each inner env_len >= 2
1816
+ * - no buffer overflow / truncation
1817
+ * - no trailing bytes after the last inner envelope
1818
+ * - no duplicate owner_id across inner envelopes (extracted from
1819
+ * envelope[1] of each inner)
1820
+ *
1821
+ * Total-weight vs threshold is enforced ON-CHAIN (the SDK can't know
1822
+ * the configured threshold without reading on-chain state). Use
1823
+ * `estimateThresholdGasOverhead` to predict paymaster fees.
1824
+ */
1825
+
1826
+ /** Minimum number of inner envelopes in a threshold (Cairo enforces >= 2). */
1827
+ declare const MIN_THRESHOLD_OWNERS = 2;
1828
+ /**
1829
+ * Validate that an envelope is a well-formed V2_SNIP12 single-owner
1830
+ * envelope suitable for inclusion in a threshold envelope. Returns
1831
+ * the extracted `owner_id` (envelope[1]) so the caller can dedupe.
1832
+ *
1833
+ * Rejects nested threshold envelopes (`signature[0] == V2_THRESHOLD`)
1834
+ * — V8.4 does not allow nesting; the Cairo `_verify_sub_envelope`
1835
+ * would revert with `'SRC9: bad tag'`.
1836
+ */
1837
+ declare function assertValidV2Snip12Envelope(envelope: bigint[]): bigint;
1838
+ /**
1839
+ * Build a V2_THRESHOLD envelope from a list of pre-signed V2_SNIP12
1840
+ * inner envelopes. Each inner envelope must come from a DIFFERENT
1841
+ * owner — duplicates fail loudly here instead of on-chain.
1842
+ *
1843
+ * The typical UI flow:
1844
+ * 1. Compute the OE message hash once (computeOeHash from oe.ts)
1845
+ * 2. Distribute the hash to N owners (push notification, QR, etc.)
1846
+ * 3. Each owner signs with their kind's envelope builder and
1847
+ * returns the resulting `bigint[]`
1848
+ * 4. UI collects the envelopes and calls this function
1849
+ * 5. UI submits the resulting threshold envelope as the
1850
+ * `signature` field of `paymaster_executeSponsoredRaw.calldata`
1851
+ *
1852
+ * `buildThresholdEnvelope` does NOT submit anything. It returns the
1853
+ * felt span that goes into the OE calldata.
1854
+ */
1855
+ declare function buildThresholdEnvelope(args: {
1856
+ innerEnvelopes: bigint[][];
1857
+ }): bigint[];
1858
+ /**
1859
+ * Split a V2_THRESHOLD envelope back into its constituent inner
1860
+ * V2_SNIP12 envelopes. Inverse of `buildThresholdEnvelope`.
1861
+ *
1862
+ * Useful for UI flows that need to display "N of M signers signed
1863
+ * so far" status without re-deriving from individual envelopes. Also
1864
+ * useful in tests for round-trip validation.
1865
+ *
1866
+ * Throws if the envelope is malformed (tag mismatch, n < 2, length
1867
+ * mismatch, trailing bytes). Mirrors the Cairo dispatch's structural
1868
+ * checks (`src/account.cairo:418-456`) so the same envelope that
1869
+ * would parse-fail here would also revert on-chain.
1870
+ */
1871
+ declare function parseThresholdEnvelope(envelope: bigint[]): {
1872
+ innerEnvelopes: bigint[][];
1873
+ };
1874
+ /**
1875
+ * Predict the on-chain L2 gas overhead a threshold envelope will
1876
+ * consume. Each inner envelope dispatches to its kind's verifier
1877
+ * class via `library_call`, so total overhead is the sum of
1878
+ * per-kind overheads.
1879
+ *
1880
+ * Returned value is the same unit as `SHHH_KIND_GAS_OVERHEAD` in
1881
+ * `@chipi-stack/shared` (raw L2 gas units). Use to predict paymaster
1882
+ * fees:
1883
+ * estimatedFee = baselineOEGas + estimateThresholdGasOverhead(owners)
1884
+ *
1885
+ * Does NOT include the wallet's threshold-validation loop overhead
1886
+ * (the deduplication walk and weight summation) — that's a constant
1887
+ * ~50K L2 gas regardless of N, and the paymaster's fee predictor
1888
+ * already accounts for it.
1889
+ */
1890
+ declare function estimateThresholdGasOverhead(owners: Array<{
1891
+ signerKind: ShhhSignerKind;
1892
+ }>): number;
1893
+
1894
+ /**
1895
+ * SHHH V8.4 migration builders — sessions / OZ → V8.4.
1896
+ *
1897
+ * Wave B PR B4. Covers two paths:
1898
+ *
1899
+ * 1. Happy-path atomic multicall (`buildMigrationCalls`)
1900
+ * Used when the legacy wallet is still operational and can sign
1901
+ * an OutsideExecution. The wallet emits one OE containing two
1902
+ * calls in order:
1903
+ *
1904
+ * upgrade(SHHH_ACCOUNT_CLASS_HASH)
1905
+ * bootstrap_from_sessions(public_key, STARK_VERIFIER_CLASS_HASH, label)
1906
+ *
1907
+ * Atomicity is the security property — between `upgrade` and
1908
+ * `bootstrap_from_sessions` an attacker watching the mempool
1909
+ * could call `bootstrap_from_sessions(attacker_pk, ...)` first
1910
+ * and seize the account. The OE bundles them so that race
1911
+ * window doesn't exist.
1912
+ *
1913
+ * 2. Stranded-state recovery (`buildBootstrapSignedCall` +
1914
+ * `computeBootstrapMessage` + `signBootstrapMessage`)
1915
+ * Used when the legacy multicall ran but `bootstrap_from_sessions`
1916
+ * silently reverted (the old chipi-pay/sessions-smart-contract's
1917
+ * `_execute_calls` swallowed subcall errors at a
1918
+ * `Result::Err(_) => res.append(array![].span())` site). The
1919
+ * account is at V8.4 class but `primary_kind == 0`.
1920
+ *
1921
+ * Recovery: anyone calls
1922
+ *
1923
+ * bootstrap_from_sessions_signed(
1924
+ * public_key, stark_verifier_class, label, signature_r, signature_s
1925
+ * )
1926
+ *
1927
+ * where (r, s) is a STARK ECDSA signature over
1928
+ *
1929
+ * bootstrap_msg = poseidon([
1930
+ * 'SHHH_BOOTSTRAP_V8_4',
1931
+ * account_address,
1932
+ * public_key,
1933
+ * stark_verifier_class,
1934
+ * label,
1935
+ * ])
1936
+ *
1937
+ * Authorization comes from two independent checks on-chain:
1938
+ * - the preserved `LEGACY_OZ_ACCOUNT_PUBKEY_SLOT` storage slot
1939
+ * must equal `public_key` (audit C-1 binding to the original
1940
+ * owner)
1941
+ * - the signature must verify under `public_key`
1942
+ *
1943
+ * So this entrypoint is callable from any sponsor / relayer
1944
+ * without a self-call channel, but is non-transferable: only the
1945
+ * original owner who controls the STARK private key can produce
1946
+ * a valid signature.
1947
+ *
1948
+ * Cairo source of truth:
1949
+ * shhh-wallet-cairo/src/account.cairo:1024 bootstrap_from_sessions
1950
+ * shhh-wallet-cairo/src/account.cairo:1113 bootstrap_from_sessions_signed
1951
+ * shhh-wallet-cairo/scripts/ts/migrate-sessions-wallet.ts (happy-path ref)
1952
+ *
1953
+ * What this module does NOT cover (out of scope for B4):
1954
+ * - The upstream OE-build + sign step. Caller wraps these `Call`s
1955
+ * with `buildOutsideExecution` + `buildStarkEnvelope` and submits
1956
+ * via the paymaster. Same flow as B1's governance builders.
1957
+ * - Verifying on-chain whether a wallet is stranded. Done by the
1958
+ * dashboard / UI; here we just produce the recovery call.
1959
+ */
1960
+
1961
+ /**
1962
+ * Domain separator the on-chain signed-bootstrap message uses as its
1963
+ * first felt. Mirrors `'SHHH_BOOTSTRAP_V8_4'` literal at
1964
+ * `src/account.cairo:1170`.
1965
+ */
1966
+ declare const BOOTSTRAP_DOMAIN_SEPARATOR: bigint;
1967
+ /**
1968
+ * Encode a label string as a Starknet short-string felt252.
1969
+ *
1970
+ * starknet.js `shortString.encodeShortString` accepts only ASCII
1971
+ * characters and rejects strings longer than 31 characters (it
1972
+ * stuffs one character per byte, and felt252 has 251 useful bits).
1973
+ * We pre-validate here so callers get a clear error instead of the
1974
+ * generic encoder revert; the previous "≤31 bytes" message was
1975
+ * misleading because non-ASCII strings would silently slip past a
1976
+ * UTF-16 `.length` check.
1977
+ */
1978
+ declare function labelToFelt(label: string): bigint;
1979
+ interface BuildMigrationCallsParams {
1980
+ /** Address of the legacy CHIPI v29 / sessions wallet being migrated. */
1981
+ legacyAccountAddress: bigint | string;
1982
+ /** STARK public key currently stored in the legacy wallet's OZ slot. */
1983
+ legacyPublicKey: bigint | string;
1984
+ /** Target SHHH ShhhAccount class hash (e.g. `WALLET_CLASS_HASHES.SHHH`). */
1985
+ shhhAccountClassHash: bigint | string;
1986
+ /** STARK verifier class hash (`SHHH_VERIFIER_CLASS_HASHES.STARK`). */
1987
+ starkVerifierClassHash: bigint | string;
1988
+ /** Optional user-facing tag bound into the V8.4 owner record (≤31 ASCII characters). */
1989
+ label?: string;
1990
+ }
1991
+ /**
1992
+ * Build the two-call atomic multicall that migrates a legacy
1993
+ * CHIPI v29 (chipi-pay/sessions-smart-contract) wallet to SHHH V8.4.
1994
+ *
1995
+ * Caller's responsibility:
1996
+ * 1. Wrap the returned `Call[]` in a single `OutsideExecution`
1997
+ * against the legacy wallet's address.
1998
+ * 2. Sign the OE with the legacy wallet's STARK key (the SDK's
1999
+ * legacy CHIPI flow already does this; use that signer here).
2000
+ * 3. Submit via the paymaster.
2001
+ *
2002
+ * The multicall MUST be atomic — if any call between the two could
2003
+ * land independently on chain (e.g. via mempool front-running), the
2004
+ * resulting `upgrade`-but-not-`bootstrap` state is exploitable.
2005
+ * Bundling them in a single OE makes both calls land atomically.
2006
+ *
2007
+ * Returns the calls in order: [upgrade, bootstrap_from_sessions].
2008
+ */
2009
+ declare function buildMigrationCalls(params: BuildMigrationCallsParams): Call[];
2010
+ interface BootstrapMessageParams {
2011
+ /** The (already-upgraded) SHHH wallet's contract address. */
2012
+ accountAddress: bigint | string;
2013
+ /** STARK public key originally stored in the legacy OZ slot. */
2014
+ publicKey: bigint | string;
2015
+ /** STARK verifier class hash to set as primary. */
2016
+ starkVerifierClassHash: bigint | string;
2017
+ /** User-facing tag bound into the V8.4 owner record (≤31 ASCII characters). */
2018
+ label?: string;
2019
+ }
2020
+ /**
2021
+ * Compute the canonical bootstrap message the on-chain
2022
+ * `bootstrap_from_sessions_signed` verifier will check the STARK
2023
+ * ECDSA signature against:
2024
+ *
2025
+ * poseidon([
2026
+ * 'SHHH_BOOTSTRAP_V8_4',
2027
+ * account_address,
2028
+ * public_key,
2029
+ * stark_verifier_class,
2030
+ * label,
2031
+ * ])
2032
+ *
2033
+ * Domain separation via the literal prefix means a signature produced
2034
+ * for this message can never be replayed as a different intent
2035
+ * (audit point — covered in `audit_poc_attacker_can_seize_any_stranded_wallet`).
2036
+ */
2037
+ declare function computeBootstrapMessage(params: BootstrapMessageParams): bigint;
2038
+ /**
2039
+ * Sign the canonical bootstrap message with a STARK private key.
2040
+ *
2041
+ * Used in two situations:
2042
+ * - Server-side test fixtures + the Wave B B9 mainnet smoke
2043
+ * - "Recovery as a service" flows where the SDK is allowed to hold
2044
+ * a temporary STARK key custodied for the user
2045
+ *
2046
+ * Most production flows will instead sign via the user's wallet
2047
+ * (e.g. by exposing the message hash to the legacy wallet's STARK
2048
+ * signing endpoint) and pass `(r, s)` directly to
2049
+ * `buildBootstrapSignedCall`.
2050
+ */
2051
+ declare function signBootstrapMessage(params: {
2052
+ privateKey: bigint | string;
2053
+ message: BootstrapMessageParams;
2054
+ }): {
2055
+ r: bigint;
2056
+ s: bigint;
2057
+ };
2058
+ interface BuildBootstrapSignedCallParams {
2059
+ /** The stranded SHHH wallet's contract address. */
2060
+ accountAddress: bigint | string;
2061
+ /** STARK public key originally stored in the legacy OZ slot. */
2062
+ publicKey: bigint | string;
2063
+ /** STARK verifier class hash. Must match the value the original migration intended. */
2064
+ starkVerifierClassHash: bigint | string;
2065
+ /** User-facing tag (≤31 ASCII characters). Must match the original migration intent. */
2066
+ label?: string;
2067
+ /** STARK ECDSA signature r component over `computeBootstrapMessage(...)`. */
2068
+ signatureR: bigint | string;
2069
+ /** STARK ECDSA signature s component. */
2070
+ signatureS: bigint | string;
2071
+ }
2072
+ /**
2073
+ * Build a `bootstrap_from_sessions_signed(public_key, stark_verifier_class,
2074
+ * label, signature_r, signature_s)` call against a stranded SHHH wallet.
2075
+ *
2076
+ * Permissionless on-chain — anyone (typically a sponsor / relayer)
2077
+ * can dispatch this call. The signature is the auth. So the integrator
2078
+ * does NOT need to wrap it in an OE from the wallet itself; it goes
2079
+ * out as a regular invoke from any STARK account.
2080
+ *
2081
+ * Returns the `Call` for the integrator's account-abstraction layer
2082
+ * (e.g. a chipi-back service account, or a one-off operator wallet).
2083
+ */
2084
+ declare function buildBootstrapSignedCall(params: BuildBootstrapSignedCallParams): Call;
2085
+
2086
+ /**
2087
+ * High-level SHHH V8.4 OE execution path for the backend SDK.
2088
+ *
2089
+ * This module is the glue between the low-level OE builders in
2090
+ * `oe.ts` + `signers/*.ts` and chipi-back's
2091
+ * `POST /transactions/execute-sponsored-raw` endpoint.
2092
+ *
2093
+ * Why this exists separately from the legacy
2094
+ * `executePaymasterTransaction` flow:
2095
+ *
2096
+ * - The Chipi paymaster cannot synthesize SHHH envelopes server-side
2097
+ * (each of the 10 V8.4 signer kinds has a different envelope
2098
+ * shape; the paymaster's design — see chipi-pay/paymaster#16 — is
2099
+ * to pass the signature span verbatim to `execute_from_outside_v2`).
2100
+ * - Therefore SHHH clients MUST build + sign the OE locally and
2101
+ * submit it pre-signed to a forwarding endpoint
2102
+ * (`/transactions/execute-sponsored-raw`).
2103
+ * - The legacy flow (`prepare-typed-data` → sign → `execute-sponsored-
2104
+ * transaction`) is unchanged and continues to serve CHIPI / READY
2105
+ * wallets.
2106
+ *
2107
+ * Scope (Wave B, this PR): STARK + ED25519 signer kinds.
2108
+ * EIP-191 / WEBAUTHN_P256 / JWT_ES256_APPLE_SUB still throw the
2109
+ * deferred-feature error until the external-rooted wallet adapter
2110
+ * flow lands.
2111
+ */
2112
+
2113
+ /**
2114
+ * Pick the chain ID for SNIP-12 hashing. If the caller did not provide
2115
+ * an explicit override, infer from the chipi-back base URL (sepolia
2116
+ * hostnames map to SN_SEPOLIA; everything else maps to SN_MAIN per
2117
+ * the project's mainnet-only invariant).
2118
+ *
2119
+ * Exported for unit-testing the resolver in isolation.
2120
+ */
2121
+ declare function resolveShhhChainId(params: {
2122
+ override?: bigint | string;
2123
+ baseUrl?: string;
2124
+ }): string;
2125
+ /**
2126
+ * Serialize an OutsideExecution struct + signature span into the
2127
+ * flat felt-array calldata that `execute_from_outside_v2` expects.
2128
+ *
2129
+ * Cairo's serde order (per shhh-wallet-cairo v8.4
2130
+ * `src/account.cairo::execute_from_outside_v2`):
2131
+ *
2132
+ * caller
2133
+ * nonce
2134
+ * execute_after (u64, 1 felt)
2135
+ * execute_before (u64, 1 felt)
2136
+ * calls.len()
2137
+ * for each Call:
2138
+ * to (felt)
2139
+ * selector (felt)
2140
+ * calldata.len()
2141
+ * ...calldata felts
2142
+ * signature.len()
2143
+ * ...signature felts
2144
+ *
2145
+ * Returns 0x-prefixed lowercase hex strings ready for direct
2146
+ * JSON-RPC submission via the chipi-back forwarder.
2147
+ */
2148
+ declare function serializeExecuteFromOutsideV2Calldata(params: {
2149
+ outsideExecution: {
2150
+ caller: bigint | string;
2151
+ nonce: bigint | string;
2152
+ execute_after: bigint;
2153
+ execute_before: bigint;
2154
+ calls: Call[];
2155
+ };
2156
+ signature: bigint[];
2157
+ }): string[];
2158
+ /**
2159
+ * Decrypt the wallet's STARK private key from its
2160
+ * `encryptedPrivateKey` ciphertext + the per-wallet `encryptKey` (PIN
2161
+ * or passkey-derived secret), build a SHHH V8.4 OE around the given
2162
+ * inner calls, sign it with the wallet's STARK key, serialize the
2163
+ * calldata for `execute_from_outside_v2`, and forward to chipi-back's
2164
+ * `/transactions/execute-sponsored-raw` endpoint.
2165
+ *
2166
+ * Returns the on-chain transaction hash.
2167
+ *
2168
+ * Pre-conditions:
2169
+ * - `wallet.walletType === "SHHH"`.
2170
+ * - `wallet.publicKey` is the deployed account address (NOT the raw
2171
+ * STARK pubkey; V8.4 deployment derives address from kind-bound
2172
+ * salt + verifier).
2173
+ * - `wallet.encryptedPrivateKey` decrypts cleanly with `encryptKey`.
2174
+ * - The wallet is already deployed (no deploy_and_invoke variant
2175
+ * here; that flow stays inside `createWallet`).
2176
+ */
2177
+ declare function executeShhhSponsoredRaw(params: {
2178
+ client: ChipiClient;
2179
+ bearerToken: string;
2180
+ wallet: {
2181
+ publicKey: string;
2182
+ encryptedPrivateKey: string;
2183
+ walletType?: string;
2184
+ };
2185
+ encryptKey: string;
2186
+ calls: Call[];
2187
+ /**
2188
+ * Defaults to `"STARK"`. Wave B ships STARK + ED25519. EIP-191 /
2189
+ * WEBAUTHN_P256 / JWT_ES256_APPLE_SUB are deferred to the
2190
+ * external-rooted wallet adapter PRs.
2191
+ */
2192
+ signerKind?: "STARK" | "ED25519";
2193
+ /** Override the OE nonce; default = `Date.now() << 24 | random24bit`. */
2194
+ nonce?: bigint;
2195
+ /** Optional externalUserId for the audit-log hint. */
2196
+ externalUserId?: string;
2197
+ /**
2198
+ * Override the chain ID used for SNIP-12 hashing. Defaults to
2199
+ * mainnet per CLAUDE.md's mainnet-only invariant; sepolia is auto-
2200
+ * inferred from the chipi-back base URL when present. Drift here
2201
+ * = the V8.4 account rejects the signature on chain.
2202
+ */
2203
+ chainId?: bigint | string;
2204
+ }): Promise<string>;
2205
+
2206
+ 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, 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 };