@chipi-stack/backend 14.7.0 → 14.9.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.js CHANGED
@@ -392,6 +392,9 @@ function ed25519PubkeyFelts(publicKey) {
392
392
  const { low, high } = bytesToLeU256(publicKey);
393
393
  return [low, high];
394
394
  }
395
+ function ed25519OwnerPubkeyParam(publicKey) {
396
+ return ed25519PubkeyFelts(publicKey).map((f) => "0x" + f.toString(16)).join(",");
397
+ }
395
398
  async function buildEd25519Envelope(params) {
396
399
  const ownerIndex = params.ownerIndex ?? 0n;
397
400
  if (ownerIndex < 0n || ownerIndex >= 0x100000000n) {
@@ -502,6 +505,14 @@ var init_encryption2 = __esm({
502
505
  init_encryption();
503
506
  }
504
507
  });
508
+
509
+ // src/shhh/signers/stark.ts
510
+ var stark_exports = {};
511
+ __export(stark_exports, {
512
+ buildStarkEnvelope: () => buildStarkEnvelope,
513
+ starkOwnerId: () => starkOwnerId,
514
+ starkPubkeyFelts: () => starkPubkeyFelts
515
+ });
505
516
  function buildStarkEnvelope(params) {
506
517
  const pkHex = "0x" + (typeof params.privateKey === "bigint" ? params.privateKey : BigInt(params.privateKey)).toString(16).padStart(64, "0");
507
518
  const sig = starknet.ec.starkCurve.sign("0x" + params.messageHash.toString(16), pkHex);
@@ -952,7 +963,15 @@ var executePaymasterTransaction = async ({
952
963
  usePasskey ? "encryptKey is required when using passkey. The passkey authentication should have provided the encryptKey." : "encryptKey is required for transaction execution"
953
964
  );
954
965
  }
955
- const walletType = wallet.walletType ?? "READY";
966
+ let walletType = wallet.walletType ?? "READY";
967
+ if (!wallet.walletType) {
968
+ try {
969
+ const resolveProvider = new starknet.RpcProvider({ nodeUrl: shared.WALLET_RPC_ENDPOINTS.CHIPI });
970
+ const onChainClassHash = await resolveProvider.getClassHashAt(wallet.publicKey, "latest");
971
+ walletType = shared.getWalletTypeFromClassHash(onChainClassHash) ?? "READY";
972
+ } catch {
973
+ }
974
+ }
956
975
  if (walletType === "SHHH") {
957
976
  const { executeShhhSponsoredRaw: executeShhhSponsoredRaw2 } = await Promise.resolve().then(() => (init_execute_paymaster_raw(), execute_paymaster_raw_exports));
958
977
  const shhhSignerKind = (wallet.signerKind ?? "STARK") || "STARK";
@@ -2826,6 +2845,79 @@ var ChipiSessions = class {
2826
2845
  }
2827
2846
  }
2828
2847
  };
2848
+ var ChipiTreasury = class {
2849
+ constructor(client, apiSecretKey) {
2850
+ this.client = client;
2851
+ this.apiSecretKey = apiSecretKey;
2852
+ }
2853
+ base(treasuryId) {
2854
+ return `${shared.API_ENDPOINTS.TREASURIES}/${treasuryId}/transactions`;
2855
+ }
2856
+ /** Resolve a `sk_` for the secret-gated reads/execute, or fail loudly. */
2857
+ secret(bearerToken) {
2858
+ const token = bearerToken ?? this.apiSecretKey;
2859
+ if (!token) {
2860
+ throw new Error(
2861
+ "A secret key (sk_) is required for treasury execute / list / allowance. Pass `bearerToken` to forTreasury(...) or configure the SDK with apiSecretKey. Call these from a server, never the browser."
2862
+ );
2863
+ }
2864
+ return token;
2865
+ }
2866
+ /**
2867
+ * Build a {@link TreasuryTransport} for one treasury + owner. Wire the
2868
+ * returned object directly into chipi-react's `useProposeAction` /
2869
+ * `useTreasuryProposals` `transport` prop, or call its methods yourself.
2870
+ */
2871
+ forTreasury(config) {
2872
+ const { treasuryId, ownerPubkey } = config;
2873
+ const base = this.base(treasuryId);
2874
+ return {
2875
+ // sk_ — read the proposal list. chipi-back returns
2876
+ // { items, total, page, limit, hasMore }; the hook only reads `items`.
2877
+ list: async () => {
2878
+ const res = await this.client.get({
2879
+ endpoint: base,
2880
+ bearerToken: this.secret(config.bearerToken)
2881
+ });
2882
+ return { ...res, items: res.items ?? [] };
2883
+ },
2884
+ // pk_ + owner sig — no secret. `ownerPubkey` lets the backend verify the
2885
+ // proposer envelope. `meta` (if present) is stripped server-side.
2886
+ propose: async (body) => {
2887
+ return this.client.post({
2888
+ endpoint: base,
2889
+ // Empty string → no Authorization header; the pk_ travels in
2890
+ // x-api-key, which ApiKeyGuard accepts for propose.
2891
+ bearerToken: "",
2892
+ body: { ...body, ownerPubkey }
2893
+ });
2894
+ },
2895
+ // pk_ + owner sig — approve an existing proposal.
2896
+ sign: async (proposalId, body) => {
2897
+ return this.client.post({
2898
+ endpoint: `${base}/${proposalId}/sign`,
2899
+ bearerToken: "",
2900
+ body: { ...body, ownerPubkey }
2901
+ });
2902
+ },
2903
+ // sk_ — relay the assembled threshold calldata through the paymaster.
2904
+ execute: async (proposalId, calldata) => {
2905
+ return this.client.post({
2906
+ endpoint: `${base}/${proposalId}/execute`,
2907
+ bearerToken: this.secret(config.bearerToken),
2908
+ body: { calldata }
2909
+ });
2910
+ },
2911
+ // sk_ — the free-execute meter (org-scoped).
2912
+ getMultisigAllowance: async () => {
2913
+ return this.client.get({
2914
+ endpoint: `${shared.API_ENDPOINTS.TREASURIES}/${treasuryId}/multisig-allowance`,
2915
+ bearerToken: this.secret(config.bearerToken)
2916
+ });
2917
+ }
2918
+ };
2919
+ }
2920
+ };
2829
2921
  var InMemoryNonceStore = class {
2830
2922
  constructor() {
2831
2923
  this.used = /* @__PURE__ */ new Set();
@@ -3092,6 +3184,43 @@ var signTypedData = async ({
3092
3184
  throw error;
3093
3185
  }
3094
3186
  };
3187
+ function toFeltHex(x) {
3188
+ return "0x" + x.toString(16).padStart(64, "0");
3189
+ }
3190
+ var signTypedDataArray = async ({
3191
+ params
3192
+ }) => {
3193
+ const { encryptKey, wallet, typedData: typedData2 } = params;
3194
+ if (!encryptKey) throw new Error("encryptKey is required for signing typed data");
3195
+ if (!wallet || !wallet.encryptedPrivateKey || !wallet.publicKey) {
3196
+ throw new Error("wallet with publicKey and encryptedPrivateKey is required");
3197
+ }
3198
+ if (!typedData2) throw new Error("typedData is required for signing");
3199
+ const walletType = wallet.walletType ?? "READY";
3200
+ if (!Object.prototype.hasOwnProperty.call(shared.WALLET_RPC_ENDPOINTS, walletType)) {
3201
+ throw new Error(`Unsupported wallet type: "${String(walletType)}"`);
3202
+ }
3203
+ let privateKeyDecrypted;
3204
+ try {
3205
+ privateKeyDecrypted = exports.decryptPrivateKey(wallet.encryptedPrivateKey, encryptKey);
3206
+ } catch {
3207
+ throw new Error("Failed to decrypt private key. Verify the encryptKey is correct.");
3208
+ }
3209
+ const messageHash = BigInt(starknet.typedData.getMessageHash(typedData2, wallet.publicKey));
3210
+ if (walletType === "SHHH") {
3211
+ const { buildStarkEnvelope: buildStarkEnvelope2 } = await Promise.resolve().then(() => (init_stark(), stark_exports));
3212
+ const envelope = buildStarkEnvelope2({
3213
+ privateKey: privateKeyDecrypted,
3214
+ messageHash,
3215
+ ownerIndex: 0n
3216
+ });
3217
+ return envelope.map(toFeltHex);
3218
+ }
3219
+ const provider = new starknet.RpcProvider({ nodeUrl: shared.WALLET_RPC_ENDPOINTS[walletType] });
3220
+ const account = new starknet.Account({ provider, address: wallet.publicKey, signer: privateKeyDecrypted });
3221
+ const { r, s } = normalizeSignature(await account.signMessage(typedData2));
3222
+ return [r, s];
3223
+ };
3095
3224
 
3096
3225
  // src/chipi-sdk.ts
3097
3226
  var ChipiSDK = class {
@@ -3105,6 +3234,7 @@ var ChipiSDK = class {
3105
3234
  this.skus = new ChipiSkus(this.client);
3106
3235
  this.sessions = new ChipiSessions(this.client);
3107
3236
  this.exchanges = new ChipiExchanges(this.client);
3237
+ this.treasury = new ChipiTreasury(this.client, config.apiSecretKey);
3108
3238
  this.executeTransaction = this.executeTransaction.bind(this);
3109
3239
  this.executeTransactionWithSession = this.executeTransactionWithSession.bind(this);
3110
3240
  this.transfer = this.transfer.bind(this);
@@ -3115,6 +3245,7 @@ var ChipiSDK = class {
3115
3245
  this.purchaseSku = this.purchaseSku.bind(this);
3116
3246
  this.getUsdAmount = this.getUsdAmount.bind(this);
3117
3247
  this.signTypedData = this.signTypedData.bind(this);
3248
+ this.signTypedDataArray = this.signTypedDataArray.bind(this);
3118
3249
  this.createPaymasterAdapter = this.createPaymasterAdapter.bind(this);
3119
3250
  this.createX402Facilitator = this.createX402Facilitator.bind(this);
3120
3251
  }
@@ -3243,6 +3374,14 @@ var ChipiSDK = class {
3243
3374
  async signTypedData({ params }) {
3244
3375
  return signTypedData({ params });
3245
3376
  }
3377
+ /**
3378
+ * Sign SNIP-12 typed data and return the full signature span (felt strings) —
3379
+ * the shape a dApp's `wallet_signTypedData` expects. Wallet-type aware:
3380
+ * SHHH returns the V2_SNIP12 owner envelope; standard accounts return [r, s].
3381
+ */
3382
+ async signTypedDataArray({ params }) {
3383
+ return signTypedDataArray({ params });
3384
+ }
3246
3385
  /**
3247
3386
  * Transfer tokens
3248
3387
  */
@@ -3765,6 +3904,12 @@ function eip191PubkeyFelts(pubkey) {
3765
3904
  function eip191OwnerId(pubkey) {
3766
3905
  return ownerCommitment(EIP191_KIND_TAG_FELT, eip191PubkeyFelts(pubkey));
3767
3906
  }
3907
+ function eip191OwnerPubkeyParam(pubkey) {
3908
+ assertBytes("secp256k1 x coordinate", pubkey.x, 32);
3909
+ assertBytes("secp256k1 y coordinate", pubkey.y, 32);
3910
+ const toHex = (b) => "0x" + Array.from(b, (v) => v.toString(16).padStart(2, "0")).join("");
3911
+ return `${toHex(pubkey.x)},${toHex(pubkey.y)}`;
3912
+ }
3768
3913
  function parseEip191Signature(signatureHex) {
3769
3914
  const hex = signatureHex.startsWith("0x") ? signatureHex.slice(2) : signatureHex;
3770
3915
  if (hex.length !== 130) {
@@ -3987,6 +4132,12 @@ function webauthnPubkeyFelts(pub) {
3987
4132
  function webauthnOwnerId(pub) {
3988
4133
  return ownerCommitment(WEBAUTHN_KIND_TAG_FELT, webauthnPubkeyFelts(pub));
3989
4134
  }
4135
+ function webauthnOwnerPubkeyParam(pub) {
4136
+ assertBytes2("P-256 x coordinate", pub.x, 32);
4137
+ assertBytes2("P-256 y coordinate", pub.y, 32);
4138
+ const toHex = (b) => "0x" + Array.from(b, (v) => v.toString(16).padStart(2, "0")).join("");
4139
+ return `${toHex(pub.x)},${toHex(pub.y)}`;
4140
+ }
3990
4141
  function buildWebAuthnEnvelopeFromAssertion(params) {
3991
4142
  const ownerIndex = params.ownerIndex ?? 0n;
3992
4143
  if (ownerIndex < 0n || ownerIndex >= 0x100000000n) {
@@ -4542,6 +4693,82 @@ function secondsUntilExecutable(validAfterSecs, nowSecs) {
4542
4693
  init_threshold();
4543
4694
  init_execute_paymaster_raw();
4544
4695
 
4696
+ // src/shhh/account.ts
4697
+ init_oe();
4698
+ init_stark();
4699
+ init_execute_paymaster_raw();
4700
+ function generateNonce2() {
4701
+ return BigInt(Date.now()) << 24n | BigInt(Math.floor(Math.random() * 16777216));
4702
+ }
4703
+ var ShhhAccount = class {
4704
+ constructor(provider, address, privateKey, options = {}) {
4705
+ this.provider = provider;
4706
+ this.address = address;
4707
+ this.privateKey = privateKey;
4708
+ this.ownerIndex = options.ownerIndex ?? 0n;
4709
+ this.chainId = options.chainId ?? resolveShhhChainId({});
4710
+ this.submitFn = options.submit;
4711
+ }
4712
+ /**
4713
+ * Build + sign an OutsideExecution for `calls` and return the
4714
+ * serialized `execute_from_outside_v2` calldata (plus the underlying
4715
+ * pieces). Use this when you want to broadcast the calldata yourself.
4716
+ */
4717
+ async signExecution(calls, options = {}) {
4718
+ if (options.validForSeconds !== void 0) {
4719
+ const maxValid = exports.MAX_OE_VALIDITY_WINDOW_SECONDS - OE_EXECUTE_AFTER_CLOCK_SKEW_BUFFER;
4720
+ if (!Number.isFinite(options.validForSeconds) || options.validForSeconds <= 0) {
4721
+ throw new Error(
4722
+ "ShhhAccount: validForSeconds must be a positive finite number of seconds."
4723
+ );
4724
+ }
4725
+ if (options.validForSeconds > maxValid) {
4726
+ throw new Error(
4727
+ `ShhhAccount: validForSeconds (${options.validForSeconds}) exceeds the max ${maxValid}s (the account caps the OE window at ${exports.MAX_OE_VALIDITY_WINDOW_SECONDS}s and ${OE_EXECUTE_AFTER_CLOCK_SKEW_BUFFER}s is reserved for clock skew).`
4728
+ );
4729
+ }
4730
+ }
4731
+ const outsideExecution = buildOutsideExecution({
4732
+ nonce: options.nonce ?? generateNonce2(),
4733
+ calls,
4734
+ ...options.validForSeconds !== void 0 ? { executeBefore: BigInt(Math.floor(Date.now() / 1e3) + options.validForSeconds) } : {}
4735
+ });
4736
+ const messageHash = computeOeHash(outsideExecution, this.address, this.chainId);
4737
+ const signature = buildStarkEnvelope({
4738
+ privateKey: this.privateKey,
4739
+ messageHash,
4740
+ ownerIndex: this.ownerIndex
4741
+ });
4742
+ const calldata = serializeExecuteFromOutsideV2Calldata({ outsideExecution, signature });
4743
+ return { outsideExecution, signature, messageHash, calldata };
4744
+ }
4745
+ /**
4746
+ * Sign `calls` and broadcast them via the configured `submit` strategy.
4747
+ * Mirrors starknet.js `Account.execute` ergonomically. Returns the
4748
+ * transaction hash without waiting for inclusion — use
4749
+ * `provider.waitForTransaction(transactionHash)` if you need to block.
4750
+ */
4751
+ async execute(calls, options = {}) {
4752
+ if (!this.submitFn) {
4753
+ throw new Error(
4754
+ "ShhhAccount.execute: no submit strategy configured. Pass `submit` in the ShhhAccount constructor options (e.g. submitViaAccount(fundedAccount) for self-sovereign, or a paymaster relay for gasless), or call signExecution() and broadcast the calldata yourself."
4755
+ );
4756
+ }
4757
+ const { calldata } = await this.signExecution(calls, options);
4758
+ return this.submitFn({ accountAddress: this.address, calldata });
4759
+ }
4760
+ };
4761
+ function submitViaAccount(funded) {
4762
+ return async ({ accountAddress, calldata }) => {
4763
+ const { transaction_hash } = await funded.execute({
4764
+ contractAddress: accountAddress,
4765
+ entrypoint: "execute_from_outside_v2",
4766
+ calldata
4767
+ });
4768
+ return { transactionHash: transaction_hash };
4769
+ };
4770
+ }
4771
+
4545
4772
  Object.defineProperty(exports, "Chain", {
4546
4773
  enumerable: true,
4547
4774
  get: function () { return types.Chain; }
@@ -4565,6 +4792,7 @@ exports.ChipiSessions = ChipiSessions;
4565
4792
  exports.ChipiSkuPurchases = ChipiSkuPurchases;
4566
4793
  exports.ChipiSkus = ChipiSkus;
4567
4794
  exports.ChipiTransactions = ChipiTransactions;
4795
+ exports.ChipiTreasury = ChipiTreasury;
4568
4796
  exports.ChipiWallets = ChipiWallets;
4569
4797
  exports.DEFAULT_OP_EXPIRY_SECONDS = DEFAULT_OP_EXPIRY_SECONDS;
4570
4798
  exports.OP_ADD_OWNER = OP_ADD_OWNER;
@@ -4573,6 +4801,7 @@ exports.OP_ROTATE_OWNER = OP_ROTATE_OWNER;
4573
4801
  exports.OP_SET_THRESHOLD = OP_SET_THRESHOLD;
4574
4802
  exports.ROLE_GUARDIAN = ROLE_GUARDIAN;
4575
4803
  exports.ROLE_OWNER = ROLE_OWNER;
4804
+ exports.ShhhAccount = ShhhAccount;
4576
4805
  exports.TIMELOCK_SECONDS = TIMELOCK_SECONDS;
4577
4806
  exports.VIEW_SELECTORS = VIEW_SELECTORS;
4578
4807
  exports.X402Facilitator = X402Facilitator;
@@ -4611,9 +4840,11 @@ exports.computeOeHash = computeOeHash;
4611
4840
  exports.computeShhhAddress = computeShhhAddress;
4612
4841
  exports.computeSnip12Hash = computeSnip12Hash;
4613
4842
  exports.ed25519OwnerId = ed25519OwnerId;
4843
+ exports.ed25519OwnerPubkeyParam = ed25519OwnerPubkeyParam;
4614
4844
  exports.ed25519PubkeyFelts = ed25519PubkeyFelts;
4615
4845
  exports.ed25519SignedBytes = ed25519SignedBytes;
4616
4846
  exports.eip191OwnerId = eip191OwnerId;
4847
+ exports.eip191OwnerPubkeyParam = eip191OwnerPubkeyParam;
4617
4848
  exports.eip191PubkeyFelts = eip191PubkeyFelts;
4618
4849
  exports.estimateThresholdGasOverhead = estimateThresholdGasOverhead;
4619
4850
  exports.executeShhhSponsoredRaw = executeShhhSponsoredRaw;
@@ -4639,10 +4870,13 @@ exports.secondsUntilExecutable = secondsUntilExecutable;
4639
4870
  exports.serializeExecuteFromOutsideV2Calldata = serializeExecuteFromOutsideV2Calldata;
4640
4871
  exports.signBootstrapMessage = signBootstrapMessage;
4641
4872
  exports.signTypedData = signTypedData;
4873
+ exports.signTypedDataArray = signTypedDataArray;
4642
4874
  exports.snip12HashToHexAsciiBytes = snip12HashToHexAsciiBytes;
4643
4875
  exports.starkOwnerId = starkOwnerId;
4644
4876
  exports.starkPubkeyFelts = starkPubkeyFelts;
4877
+ exports.submitViaAccount = submitViaAccount;
4645
4878
  exports.webauthnOwnerId = webauthnOwnerId;
4879
+ exports.webauthnOwnerPubkeyParam = webauthnOwnerPubkeyParam;
4646
4880
  exports.webauthnPubkeyFelts = webauthnPubkeyFelts;
4647
4881
  exports.wrapV2Snip12Envelope = wrapV2Snip12Envelope;
4648
4882
  exports.wrapV2ThresholdEnvelope = wrapV2ThresholdEnvelope;