@obelyzk/sdk 1.0.0 → 1.1.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.
@@ -4,11 +4,12 @@ import {
4
4
  GENERATOR_G,
5
5
  GENERATOR_H,
6
6
  ObelyskPrivacy,
7
+ __require,
7
8
  ecAdd,
8
9
  ecMul,
9
10
  invMod,
10
11
  randomScalar
11
- } from "../chunk-O2PF7VJA.mjs";
12
+ } from "../chunk-CGPFHXUF.mjs";
12
13
 
13
14
  // src/obelysk/client.ts
14
15
  import { RpcProvider } from "starknet";
@@ -1788,6 +1789,225 @@ var ShieldedSwapClient = class {
1788
1789
  }
1789
1790
  };
1790
1791
 
1792
+ // src/obelysk/ecies.ts
1793
+ var HKDF_INFO = "obelysk-ecies-v1";
1794
+ var ECIES_VERSION = 1;
1795
+ async function eciesEncrypt(payload, relayerPubkeyHex) {
1796
+ const subtle = getCrypto();
1797
+ const relayerPubkeyBytes = hexToBytes(relayerPubkeyHex);
1798
+ if (relayerPubkeyBytes.length !== 32) {
1799
+ throw new Error(`Invalid relayer public key length: ${relayerPubkeyBytes.length} (expected 32)`);
1800
+ }
1801
+ const relayerPubkey = await subtle.importKey(
1802
+ "raw",
1803
+ relayerPubkeyBytes.buffer,
1804
+ { name: "X25519" },
1805
+ false,
1806
+ []
1807
+ );
1808
+ const ephemeralKeyPair = await subtle.generateKey(
1809
+ { name: "X25519" },
1810
+ true,
1811
+ ["deriveBits"]
1812
+ );
1813
+ const sharedBits = await subtle.deriveBits(
1814
+ { name: "X25519", public: relayerPubkey },
1815
+ ephemeralKeyPair.privateKey,
1816
+ 256
1817
+ );
1818
+ const sharedKey = await subtle.importKey(
1819
+ "raw",
1820
+ sharedBits,
1821
+ { name: "HKDF" },
1822
+ false,
1823
+ ["deriveKey"]
1824
+ );
1825
+ const aesKey = await subtle.deriveKey(
1826
+ {
1827
+ name: "HKDF",
1828
+ hash: "SHA-256",
1829
+ salt: new ArrayBuffer(0),
1830
+ info: new TextEncoder().encode(HKDF_INFO)
1831
+ },
1832
+ sharedKey,
1833
+ { name: "AES-GCM", length: 256 },
1834
+ false,
1835
+ ["encrypt"]
1836
+ );
1837
+ const nonce = getRandomBytes(12);
1838
+ const plaintext = new TextEncoder().encode(JSON.stringify(payload));
1839
+ const ciphertext = await subtle.encrypt(
1840
+ { name: "AES-GCM", iv: nonce },
1841
+ aesKey,
1842
+ plaintext
1843
+ );
1844
+ const ephPubRaw = await subtle.exportKey("raw", ephemeralKeyPair.publicKey);
1845
+ return {
1846
+ ephemeral_pubkey: bytesToHex(new Uint8Array(ephPubRaw)),
1847
+ ciphertext: bytesToBase64(new Uint8Array(ciphertext)),
1848
+ nonce: bytesToHex(nonce),
1849
+ version: ECIES_VERSION
1850
+ };
1851
+ }
1852
+ function getCrypto() {
1853
+ if (typeof globalThis.crypto?.subtle !== "undefined") {
1854
+ return globalThis.crypto.subtle;
1855
+ }
1856
+ try {
1857
+ const { webcrypto } = __require("crypto");
1858
+ return webcrypto.subtle;
1859
+ } catch {
1860
+ throw new Error("ECIES requires Web Crypto API (Node 20+ or a modern browser)");
1861
+ }
1862
+ }
1863
+ function getRandomBytes(n) {
1864
+ if (typeof globalThis.crypto?.getRandomValues !== "undefined") {
1865
+ return globalThis.crypto.getRandomValues(new Uint8Array(n));
1866
+ }
1867
+ const { randomBytes } = __require("crypto");
1868
+ return new Uint8Array(randomBytes(n));
1869
+ }
1870
+ function hexToBytes(hex) {
1871
+ const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
1872
+ const bytes = new Uint8Array(clean.length / 2);
1873
+ for (let i = 0; i < bytes.length; i++) {
1874
+ bytes[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);
1875
+ }
1876
+ return bytes;
1877
+ }
1878
+ function bytesToHex(bytes) {
1879
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
1880
+ }
1881
+ function bytesToBase64(bytes) {
1882
+ if (typeof btoa === "function") {
1883
+ return btoa(String.fromCharCode(...bytes));
1884
+ }
1885
+ return Buffer.from(bytes).toString("base64");
1886
+ }
1887
+
1888
+ // src/obelysk/denominations.ts
1889
+ var WBTC_DENOMINATIONS = [
1890
+ 50000n,
1891
+ // 0.0005 BTC
1892
+ 100000n,
1893
+ // 0.001 BTC
1894
+ 500000n,
1895
+ // 0.005 BTC
1896
+ 1000000n,
1897
+ // 0.01 BTC
1898
+ 5000000n,
1899
+ // 0.05 BTC
1900
+ 10000000n
1901
+ // 0.1 BTC
1902
+ ];
1903
+ var SAGE_DENOMINATIONS = [
1904
+ 10000000000000000n,
1905
+ // 0.01 SAGE
1906
+ 50000000000000000n,
1907
+ // 0.05 SAGE
1908
+ 100000000000000000n,
1909
+ // 0.1 SAGE
1910
+ 500000000000000000n,
1911
+ // 0.5 SAGE
1912
+ 1000000000000000000n,
1913
+ // 1 SAGE
1914
+ 5000000000000000000n
1915
+ // 5 SAGE
1916
+ ];
1917
+ var ETH_DENOMINATIONS = [
1918
+ 1000000000000000n,
1919
+ // 0.001 ETH
1920
+ 5000000000000000n,
1921
+ // 0.005 ETH
1922
+ 10000000000000000n,
1923
+ // 0.01 ETH
1924
+ 50000000000000000n,
1925
+ // 0.05 ETH
1926
+ 100000000000000000n,
1927
+ // 0.1 ETH
1928
+ 500000000000000000n
1929
+ // 0.5 ETH
1930
+ ];
1931
+ var STRK_DENOMINATIONS = [
1932
+ 50000000000000000n,
1933
+ // 0.05 STRK
1934
+ 100000000000000000n,
1935
+ // 0.1 STRK
1936
+ 500000000000000000n,
1937
+ // 0.5 STRK
1938
+ 1000000000000000000n,
1939
+ // 1 STRK
1940
+ 5000000000000000000n
1941
+ // 5 STRK
1942
+ ];
1943
+ var USDC_DENOMINATIONS = [
1944
+ 1000000n,
1945
+ // 1 USDC
1946
+ 5000000n,
1947
+ // 5 USDC
1948
+ 10000000n,
1949
+ // 10 USDC
1950
+ 50000000n,
1951
+ // 50 USDC
1952
+ 100000000n,
1953
+ // 100 USDC
1954
+ 500000000n
1955
+ // 500 USDC
1956
+ ];
1957
+ var VM31_DENOMINATIONS = {
1958
+ 0: WBTC_DENOMINATIONS,
1959
+ 1: SAGE_DENOMINATIONS,
1960
+ 2: ETH_DENOMINATIONS,
1961
+ 3: STRK_DENOMINATIONS,
1962
+ 4: USDC_DENOMINATIONS
1963
+ };
1964
+ var DENOMINATION_BY_SYMBOL = {
1965
+ wbtc: WBTC_DENOMINATIONS,
1966
+ sage: SAGE_DENOMINATIONS,
1967
+ eth: ETH_DENOMINATIONS,
1968
+ strk: STRK_DENOMINATIONS,
1969
+ usdc: USDC_DENOMINATIONS
1970
+ };
1971
+ function validateDenomination(amount, assetIdOrSymbol) {
1972
+ let denoms;
1973
+ if (typeof assetIdOrSymbol === "number") {
1974
+ denoms = VM31_DENOMINATIONS[assetIdOrSymbol];
1975
+ } else {
1976
+ denoms = DENOMINATION_BY_SYMBOL[assetIdOrSymbol.toLowerCase()];
1977
+ }
1978
+ if (!denoms) return;
1979
+ if (!denoms.includes(amount)) {
1980
+ throw new Error(
1981
+ `Deposit must use a standard denomination for asset ${assetIdOrSymbol}. Got ${amount}. Valid: [${denoms.join(", ")}]`
1982
+ );
1983
+ }
1984
+ }
1985
+ function splitIntoDenominations(amount, assetIdOrSymbol) {
1986
+ let denoms;
1987
+ if (typeof assetIdOrSymbol === "number") {
1988
+ denoms = VM31_DENOMINATIONS[assetIdOrSymbol];
1989
+ } else {
1990
+ denoms = DENOMINATION_BY_SYMBOL[assetIdOrSymbol.toLowerCase()];
1991
+ }
1992
+ if (!denoms) return { denominations: [amount], remainder: 0n };
1993
+ const sorted = [...denoms].sort((a, b) => b > a ? 1 : b < a ? -1 : 0);
1994
+ const result = [];
1995
+ let remaining = amount;
1996
+ for (const denom of sorted) {
1997
+ while (remaining >= denom) {
1998
+ result.push(denom);
1999
+ remaining -= denom;
2000
+ }
2001
+ }
2002
+ return { denominations: result, remainder: remaining };
2003
+ }
2004
+ function getDenominations(assetIdOrSymbol) {
2005
+ if (typeof assetIdOrSymbol === "number") {
2006
+ return VM31_DENOMINATIONS[assetIdOrSymbol];
2007
+ }
2008
+ return DENOMINATION_BY_SYMBOL[assetIdOrSymbol.toLowerCase()];
2009
+ }
2010
+
1791
2011
  // src/obelysk/vm31Vault.ts
1792
2012
  var VM31VaultClient = class {
1793
2013
  constructor(obelysk) {
@@ -1802,6 +2022,8 @@ var VM31VaultClient = class {
1802
2022
  get relayerApiKey() {
1803
2023
  return this.obelysk.relayerApiKey;
1804
2024
  }
2025
+ /** Cached relayer X25519 public key (fetched on first encrypted submit) */
2026
+ _relayerPubkey = null;
1805
2027
  async relayerFetch(path, init) {
1806
2028
  const url = `${this.relayerUrl}${path}`;
1807
2029
  const headers = { "Content-Type": "application/json" };
@@ -1813,6 +2035,27 @@ var VM31VaultClient = class {
1813
2035
  }
1814
2036
  return res.json();
1815
2037
  }
2038
+ /**
2039
+ * Submit a payload to the relayer, encrypted with ECIES.
2040
+ * Falls back to plaintext if `encrypt: false` is passed.
2041
+ */
2042
+ async relayerSubmit(payload, encrypt = true) {
2043
+ if (!encrypt) {
2044
+ return this.relayerFetch("/submit", {
2045
+ method: "POST",
2046
+ body: JSON.stringify(payload)
2047
+ });
2048
+ }
2049
+ if (!this._relayerPubkey) {
2050
+ const keyInfo = await this.getRelayerPublicKey();
2051
+ this._relayerPubkey = keyInfo.publicKey;
2052
+ }
2053
+ const envelope = await eciesEncrypt(payload, this._relayerPubkey);
2054
+ return this.relayerFetch("/submit", {
2055
+ method: "POST",
2056
+ body: JSON.stringify(envelope)
2057
+ });
2058
+ }
1816
2059
  // --------------------------------------------------------------------------
1817
2060
  // On-chain reads (callContract to vm31_pool)
1818
2061
  // --------------------------------------------------------------------------
@@ -1976,61 +2219,65 @@ var VM31VaultClient = class {
1976
2219
  algorithm: data.algorithm
1977
2220
  };
1978
2221
  }
1979
- /** Submit a deposit transaction to the relayer */
1980
- async submitDeposit(params) {
1981
- return this.relayerFetch("/submit", {
1982
- method: "POST",
1983
- body: JSON.stringify({
1984
- type: "deposit",
1985
- amount: Number(params.amount),
1986
- asset_id: params.assetId,
1987
- recipient_pubkey: params.recipientPubkey,
1988
- recipient_viewing_key: params.recipientViewingKey
1989
- })
1990
- });
2222
+ /**
2223
+ * Submit a deposit transaction to the relayer.
2224
+ * Validates denomination (privacy gap #7) and encrypts with ECIES by default.
2225
+ * @param encrypt - Set to false for legacy plaintext mode (default: true)
2226
+ */
2227
+ async submitDeposit(params, encrypt = true) {
2228
+ validateDenomination(params.amount, params.assetId);
2229
+ return this.relayerSubmit({
2230
+ type: "deposit",
2231
+ amount: Number(params.amount),
2232
+ asset_id: params.assetId,
2233
+ recipient_pubkey: params.recipientPubkey,
2234
+ recipient_viewing_key: params.recipientViewingKey
2235
+ }, encrypt);
1991
2236
  }
1992
- /** Submit a withdrawal transaction to the relayer */
1993
- async submitWithdraw(params) {
1994
- return this.relayerFetch("/submit", {
1995
- method: "POST",
1996
- body: JSON.stringify({
1997
- type: "withdraw",
1998
- amount: Number(params.amount),
1999
- asset_id: params.assetId,
2000
- note: {
2001
- owner_pubkey: params.note.owner_pubkey,
2002
- asset_id: params.note.asset_id,
2003
- amount_lo: params.note.amount_lo,
2004
- amount_hi: params.note.amount_hi,
2005
- blinding: params.note.blinding
2006
- },
2007
- spending_key: params.spendingKey,
2008
- merkle_path: params.merklePath,
2009
- merkle_root: params.merkleRoot,
2010
- withdrawal_binding: params.withdrawalBinding,
2011
- binding_salt: params.bindingSalt
2012
- })
2013
- });
2237
+ /**
2238
+ * Submit a withdrawal transaction to the relayer.
2239
+ * Withdrawals are not denomination-restricted.
2240
+ * @param encrypt - Set to false for legacy plaintext mode (default: true)
2241
+ */
2242
+ async submitWithdraw(params, encrypt = true) {
2243
+ return this.relayerSubmit({
2244
+ type: "withdraw",
2245
+ amount: Number(params.amount),
2246
+ asset_id: params.assetId,
2247
+ note: {
2248
+ owner_pubkey: params.note.owner_pubkey,
2249
+ asset_id: params.note.asset_id,
2250
+ amount_lo: params.note.amount_lo,
2251
+ amount_hi: params.note.amount_hi,
2252
+ blinding: params.note.blinding
2253
+ },
2254
+ spending_key: params.spendingKey,
2255
+ merkle_path: params.merklePath,
2256
+ merkle_root: params.merkleRoot,
2257
+ withdrawal_binding: params.withdrawalBinding,
2258
+ binding_salt: params.bindingSalt
2259
+ }, encrypt);
2014
2260
  }
2015
- /** Submit a private transfer transaction to the relayer */
2016
- async submitTransfer(params) {
2017
- return this.relayerFetch("/submit", {
2018
- method: "POST",
2019
- body: JSON.stringify({
2020
- type: "transfer",
2021
- amount: Number(params.amount),
2022
- asset_id: params.assetId,
2023
- recipient_pubkey: params.recipientPubkey,
2024
- recipient_viewing_key: params.recipientViewingKey,
2025
- sender_viewing_key: params.senderViewingKey,
2026
- input_notes: params.inputNotes.map((n) => ({
2027
- note: n.note,
2028
- spending_key: n.spendingKey,
2029
- merkle_path: n.merklePath
2030
- })),
2031
- merkle_root: params.merkleRoot
2032
- })
2033
- });
2261
+ /**
2262
+ * Submit a private transfer transaction to the relayer.
2263
+ * Transfers are not denomination-restricted.
2264
+ * @param encrypt - Set to false for legacy plaintext mode (default: true)
2265
+ */
2266
+ async submitTransfer(params, encrypt = true) {
2267
+ return this.relayerSubmit({
2268
+ type: "transfer",
2269
+ amount: Number(params.amount),
2270
+ asset_id: params.assetId,
2271
+ recipient_pubkey: params.recipientPubkey,
2272
+ recipient_viewing_key: params.recipientViewingKey,
2273
+ sender_viewing_key: params.senderViewingKey,
2274
+ input_notes: params.inputNotes.map((n) => ({
2275
+ note: n.note,
2276
+ spending_key: n.spendingKey,
2277
+ merkle_path: n.merklePath
2278
+ })),
2279
+ merkle_root: params.merkleRoot
2280
+ }, encrypt);
2034
2281
  }
2035
2282
  /** Query batch info from the relayer */
2036
2283
  async queryBatch(batchId) {
@@ -2747,7 +2994,9 @@ export {
2747
2994
  CURVE_ORDER,
2748
2995
  ConfidentialTransferClient,
2749
2996
  DARKPOOL_ASSET_IDS,
2997
+ DENOMINATION_BY_SYMBOL,
2750
2998
  DarkPoolClient,
2999
+ ETH_DENOMINATIONS,
2751
3000
  FIELD_PRIME,
2752
3001
  GENERATOR_G,
2753
3002
  GENERATOR_H,
@@ -2762,25 +3011,34 @@ export {
2762
3011
  PrivacyPoolClient,
2763
3012
  PrivacyRouterClient,
2764
3013
  ProverStakingClient,
3014
+ SAGE_DENOMINATIONS,
3015
+ STRK_DENOMINATIONS,
2765
3016
  ShieldedSwapClient,
2766
3017
  StealthClient,
2767
3018
  TOKEN_DECIMALS,
3019
+ USDC_DENOMINATIONS,
2768
3020
  VM31BridgeClient,
2769
3021
  VM31VaultClient,
2770
3022
  VM31_ASSET_IDS,
3023
+ VM31_DENOMINATIONS,
3024
+ WBTC_DENOMINATIONS,
2771
3025
  commitmentToHash,
2772
3026
  createAEHint,
2773
3027
  createEncryptionProof,
2774
3028
  deriveNullifier,
2775
3029
  ecAdd2 as ecAdd,
2776
3030
  ecMul2 as ecMul,
3031
+ eciesEncrypt,
2777
3032
  elgamalEncrypt,
2778
3033
  formatAmount,
2779
3034
  getContracts,
3035
+ getDenominations,
2780
3036
  getRpcUrl,
2781
3037
  mod,
2782
3038
  modInverse,
2783
3039
  parseAmount,
2784
3040
  pedersenCommit,
2785
- randomScalar2 as randomScalar
3041
+ randomScalar2 as randomScalar,
3042
+ splitIntoDenominations,
3043
+ validateDenomination
2786
3044
  };
@@ -19,7 +19,7 @@ import {
19
19
  randomBytes,
20
20
  randomScalar,
21
21
  subMod
22
- } from "../chunk-O2PF7VJA.mjs";
22
+ } from "../chunk-CGPFHXUF.mjs";
23
23
  export {
24
24
  AssetId,
25
25
  CURVE_ORDER,
@@ -16,7 +16,7 @@ import {
16
16
  WorkersClient,
17
17
  getContractsForNetwork
18
18
  } from "../chunk-LXJT3QK6.mjs";
19
- import "../chunk-O2PF7VJA.mjs";
19
+ import "../chunk-CGPFHXUF.mjs";
20
20
 
21
21
  // src/react/providers/BitSageProvider.tsx
22
22
  import {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@obelyzk/sdk",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "BitSage Network SDK - Client library for distributed compute, privacy swaps, and Obelysk encryption",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -97,5 +97,9 @@
97
97
  "homepage": "https://bitsage.network",
98
98
  "bugs": {
99
99
  "url": "https://github.com/Bitsage-Network/bitsage-network/issues"
100
+ },
101
+ "optionalDependencies": {
102
+ "@modelcontextprotocol/sdk": "^1.27.1",
103
+ "zod": "^4.3.6"
100
104
  }
101
105
  }