@cloakely/sdk 0.8.3 → 0.8.5

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/names.js ADDED
@@ -0,0 +1,59 @@
1
+ import { isAddress, parseAbi, zeroAddress } from "viem";
2
+ /**
3
+ * .ely names (Elysium Name Service): type `bona.ely` instead of a long address. Two read-only calls on one contract,
4
+ * no API key and no backend. Mainnet's address is published at launch; until then only testnet resolves.
5
+ */
6
+ export const ELY_NAMES = {
7
+ 99801: "0x13A130B336400409c956dF042C128238F1486ddD", // Elysium testnet
8
+ };
9
+ export const elyNamesAbi = parseAbi([
10
+ "function resolve(string label) view returns (address)",
11
+ "function primaryNameOf(address owner) view returns (string)",
12
+ "function text(string label, string key) view returns (string)",
13
+ ]);
14
+ /**
15
+ * The label of a .ely name, or null if the input isn't one: trimmed, lowercased, a trailing `.ely` removed, then
16
+ * 2 to 63 of a-z, 0-9 and hyphens, not starting or ending with a hyphen.
17
+ */
18
+ export function elyLabel(input) {
19
+ const s = input.trim().toLowerCase().replace(/\.ely$/, "");
20
+ return /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])$/.test(s) ? s : null;
21
+ }
22
+ /** True when the input looks like a .ely name (it ends in `.ely` and its label is valid). */
23
+ export const isElyName = (input) => /\.ely$/i.test(input.trim()) && elyLabel(input) !== null;
24
+ /**
25
+ * The wallet a .ely name points to now, or null if the name isn't registered, has expired, or the chain has no name
26
+ * service. Resolve at the moment of sending and don't store the result: names expire and change hands.
27
+ */
28
+ export async function resolveEly(publicClient, name) {
29
+ const label = elyLabel(name);
30
+ const at = ELY_NAMES[publicClient.chain?.id ?? (await publicClient.getChainId())];
31
+ if (!label || !at)
32
+ return null;
33
+ const a = await publicClient.readContract({ address: at, abi: elyNamesAbi, functionName: "resolve", args: [label] });
34
+ return a && a !== zeroAddress ? a : null;
35
+ }
36
+ /** A wallet's chosen .ely name (like `bona.ely`), or null if it has none. Safe to display: only the owner can set it. */
37
+ export async function elyNameOf(publicClient, address) {
38
+ const at = ELY_NAMES[publicClient.chain?.id ?? (await publicClient.getChainId())];
39
+ if (!at)
40
+ return null;
41
+ const n = await publicClient.readContract({ address: at, abi: elyNamesAbi, functionName: "primaryNameOf", args: [address] });
42
+ return n ? (n.endsWith(".ely") ? n : `${n}.ely`) : null;
43
+ }
44
+ /**
45
+ * A recipient typed by a person: a 0x address as it is, or a .ely name resolved now. Throws a readable error for
46
+ * anything else, and for a name that doesn't resolve (never send to the zero address).
47
+ */
48
+ export async function resolveRecipient(publicClient, input) {
49
+ const s = input.trim();
50
+ if (isAddress(s))
51
+ return { address: s };
52
+ if (isElyName(s)) {
53
+ const address = await resolveEly(publicClient, s);
54
+ if (!address)
55
+ throw new Error(`${elyLabel(s)}.ely isn't registered, or it has expired.`);
56
+ return { address, name: `${elyLabel(s)}.ely` };
57
+ }
58
+ throw new Error("Enter a 0x address or a .ely name.");
59
+ }
@@ -16,18 +16,25 @@ export declare function createProver(wasm: Uint8Array, zkey: Uint8Array): Prover
16
16
  /** Groth16 prover for the shared launch-token vault (`transactionMA2x2`, 8 public signals). */
17
17
  export declare function createSharedProver(wasm: Uint8Array, zkey: Uint8Array): Prover;
18
18
  /** Hosted circuit artifacts. */
19
+ /**
20
+ * Where the circuit files are served: a free CDN (jsDelivr, pinned to the commit that last changed them, so the files
21
+ * never change), then Cloak's own site if the CDN can't be reached. Every download is checked against the pinned
22
+ * hashes, so where it came from doesn't matter for safety.
23
+ */
24
+ export declare const CIRCUITS_CDN = "https://cdn.jsdelivr.net/gh/NECOKIZZ/Cloak@f55a12a40d475561ce2b4db933ab82855419eb2d/apps/web/public/circuits";
19
25
  export declare const CIRCUITS_URL = "https://cloak.boblabs.tech/circuits";
26
+ export declare const CIRCUITS_SOURCES: string[];
20
27
  /** SHA-256 of the artifacts this SDK version was built and tested against. */
21
28
  export declare const CIRCUIT_HASHES: {
22
29
  readonly wasm: "e44f07497b34f3498352da5bfd9fd7a100c2e8cb7ebddc643f1e6e29d4dfcf6a";
23
30
  readonly zkey: "5d64f2162ef439ae53f0e3c61f276245f7fa1b934907ef66ce3cf149056acad0";
24
31
  };
25
32
  /**
26
- * Fetch the circuit artifacts (12 MB, once) and build a prover. Works in browsers and Node 18+. The files are checked
33
+ * Fetch the circuit artifacts (15 MB, once; from the CDN, else Cloak's site, or the URL or URLs you pass) and build a prover. Works in browsers and Node 18+. The files are checked
27
34
  * against `CIRCUIT_HASHES`, so a tampered or mismatched download is refused; pass `hashes: null` to skip that when
28
35
  * serving your own build. In Node you can also read the files yourself and call `createProver`.
29
36
  */
30
- export declare function loadProver(baseUrl?: string, opts?: {
37
+ export declare function loadProver(baseUrl?: string | string[], opts?: {
31
38
  hashes?: {
32
39
  wasm: string;
33
40
  zkey: string;
@@ -42,7 +49,7 @@ export declare const SHARED_CIRCUIT_HASHES: {
42
49
  * Fetch the shared launch-token vault's circuit (`transactionMA2x2`, 15 MB, once) and build its prover. Checked against
43
50
  * `SHARED_CIRCUIT_HASHES` like `loadProver`; pass `hashes: null` to skip that when serving your own build.
44
51
  */
45
- export declare function loadSharedProver(baseUrl?: string, opts?: {
52
+ export declare function loadSharedProver(baseUrl?: string | string[], opts?: {
46
53
  hashes?: {
47
54
  wasm: string;
48
55
  zkey: string;
@@ -43,7 +43,40 @@ export function createSharedProver(wasm, zkey) {
43
43
  };
44
44
  }
45
45
  /** Hosted circuit artifacts. */
46
+ /**
47
+ * Where the circuit files are served: a free CDN (jsDelivr, pinned to the commit that last changed them, so the files
48
+ * never change), then Cloak's own site if the CDN can't be reached. Every download is checked against the pinned
49
+ * hashes, so where it came from doesn't matter for safety.
50
+ */
51
+ export const CIRCUITS_CDN = "https://cdn.jsdelivr.net/gh/NECOKIZZ/Cloak@f55a12a40d475561ce2b4db933ab82855419eb2d/apps/web/public/circuits";
46
52
  export const CIRCUITS_URL = "https://cloak.boblabs.tech/circuits";
53
+ export const CIRCUITS_SOURCES = [CIRCUITS_CDN, CIRCUITS_URL];
54
+ /** Download a circuit's two files from the first source that serves them with the expected hashes. */
55
+ async function fetchCircuit(baseUrl, names, want, what) {
56
+ const bases = (Array.isArray(baseUrl) ? baseUrl : [baseUrl]).map((b) => b.replace(/\/$/, ""));
57
+ let last;
58
+ for (const base of bases) {
59
+ try {
60
+ const get = async (name) => {
61
+ const res = await fetch(`${base}/${name}`);
62
+ if (!res.ok)
63
+ throw new Error(`could not download ${name} from ${base} (${res.status})`);
64
+ return new Uint8Array(await res.arrayBuffer());
65
+ };
66
+ const [wasm, zkey] = await Promise.all(names.map(get));
67
+ if (want) {
68
+ const [hw, hz] = await Promise.all([sha256(wasm), sha256(zkey)]);
69
+ if (hw !== want.wasm || hz !== want.zkey)
70
+ throw new Error(`${what} files from ${base} don't match this SDK version's hashes; refusing to prove with them`);
71
+ }
72
+ return [wasm, zkey];
73
+ }
74
+ catch (e) {
75
+ last = e;
76
+ }
77
+ }
78
+ throw last;
79
+ }
47
80
  /** SHA-256 of the artifacts this SDK version was built and tested against. */
48
81
  export const CIRCUIT_HASHES = {
49
82
  wasm: "e44f07497b34f3498352da5bfd9fd7a100c2e8cb7ebddc643f1e6e29d4dfcf6a",
@@ -54,25 +87,13 @@ async function sha256(bytes) {
54
87
  return Array.from(d, (b) => b.toString(16).padStart(2, "0")).join("");
55
88
  }
56
89
  /**
57
- * Fetch the circuit artifacts (12 MB, once) and build a prover. Works in browsers and Node 18+. The files are checked
90
+ * Fetch the circuit artifacts (15 MB, once; from the CDN, else Cloak's site, or the URL or URLs you pass) and build a prover. Works in browsers and Node 18+. The files are checked
58
91
  * against `CIRCUIT_HASHES`, so a tampered or mismatched download is refused; pass `hashes: null` to skip that when
59
92
  * serving your own build. In Node you can also read the files yourself and call `createProver`.
60
93
  */
61
- export async function loadProver(baseUrl = CIRCUITS_URL, opts = {}) {
62
- const base = baseUrl.replace(/\/$/, "");
63
- const get = async (name) => {
64
- const res = await fetch(`${base}/${name}`);
65
- if (!res.ok)
66
- throw new Error(`could not download ${name} from ${base} (${res.status})`);
67
- return new Uint8Array(await res.arrayBuffer());
68
- };
69
- const [wasm, zkey] = await Promise.all([get("transaction2x2.wasm"), get("transaction2x2_final.zkey")]);
94
+ export async function loadProver(baseUrl = CIRCUITS_SOURCES, opts = {}) {
70
95
  const want = opts.hashes === undefined ? CIRCUIT_HASHES : opts.hashes;
71
- if (want) {
72
- const [hw, hz] = await Promise.all([sha256(wasm), sha256(zkey)]);
73
- if (hw !== want.wasm || hz !== want.zkey)
74
- throw new Error("circuit files don't match this SDK version's hashes; refusing to prove with them");
75
- }
96
+ const [wasm, zkey] = await fetchCircuit(baseUrl, ["transaction2x2.wasm", "transaction2x2_final.zkey"], want, "circuit");
76
97
  return createProver(wasm, zkey);
77
98
  }
78
99
  /** SHA-256 of the shared-vault circuit artifacts this SDK version was built and tested against. */
@@ -84,20 +105,8 @@ export const SHARED_CIRCUIT_HASHES = {
84
105
  * Fetch the shared launch-token vault's circuit (`transactionMA2x2`, 15 MB, once) and build its prover. Checked against
85
106
  * `SHARED_CIRCUIT_HASHES` like `loadProver`; pass `hashes: null` to skip that when serving your own build.
86
107
  */
87
- export async function loadSharedProver(baseUrl = CIRCUITS_URL, opts = {}) {
88
- const base = baseUrl.replace(/\/$/, "");
89
- const get = async (name) => {
90
- const res = await fetch(`${base}/${name}`);
91
- if (!res.ok)
92
- throw new Error(`could not download ${name} from ${base} (${res.status})`);
93
- return new Uint8Array(await res.arrayBuffer());
94
- };
95
- const [wasm, zkey] = await Promise.all([get("transactionMA2x2.wasm"), get("transactionMA2x2_final.zkey")]);
108
+ export async function loadSharedProver(baseUrl = CIRCUITS_SOURCES, opts = {}) {
96
109
  const want = opts.hashes === undefined ? SHARED_CIRCUIT_HASHES : opts.hashes;
97
- if (want) {
98
- const [hw, hz] = await Promise.all([sha256(wasm), sha256(zkey)]);
99
- if (hw !== want.wasm || hz !== want.zkey)
100
- throw new Error("shared-vault circuit files don't match this SDK version's hashes; refusing to prove with them");
101
- }
110
+ const [wasm, zkey] = await fetchCircuit(baseUrl, ["transactionMA2x2.wasm", "transactionMA2x2_final.zkey"], want, "shared-vault circuit");
102
111
  return createSharedProver(wasm, zkey);
103
112
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloakely/sdk",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "description": "Cloak on Elysium: sealed-bid token launches, sealed NFT drops and WL spot sales, and private trading (shielded balances, private swaps, batch auctions, private sends) with on-device zero-knowledge proofs.",
5
5
  "keywords": [
6
6
  "cloak",