@opaquecash/relayer-client 0.2.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.
@@ -0,0 +1,66 @@
1
+ /**
2
+ * `submitGasPrivate` and the building blocks behind it (spec/relayer-market.md). The
3
+ * flow: commit the payload, fund the escrow, advertise, collect + verify bids, pick a
4
+ * stake-weighted winner, and deliver the payload sealed to the winner's key. The
5
+ * relayer then accepts and submits on-chain; gas and submitter identity are theirs,
6
+ * not the user's.
7
+ */
8
+ import { PublicKey } from "@solana/web3.js";
9
+ import type { Hex } from "viem";
10
+ import { type Advert, type Bid, type JobPayload, type PayloadEnvelope } from "./job.js";
11
+ import { type EvmTxRequest } from "./escrow.js";
12
+ import { type GatewayOptions } from "./gateway.js";
13
+ import { type RegistryReaders, type VerifiedBid } from "./select.js";
14
+ import type { TransactionInstruction } from "@solana/web3.js";
15
+ export interface PreparedJob {
16
+ jobId: Hex;
17
+ payloadHash: Hex;
18
+ deadline: number;
19
+ fee: bigint;
20
+ advert: Advert;
21
+ /** Funding artifact for the user's wallet (one of these, by chain). */
22
+ evmCreateJob?: EvmTxRequest;
23
+ solanaCreateJob?: TransactionInstruction;
24
+ }
25
+ /** Options shared by the prepare + submit flow. */
26
+ export interface PrepareOptions {
27
+ fee: bigint;
28
+ /** Absolute unix-seconds deadline; relayers must submit before it. */
29
+ deadline: number;
30
+ /** EVM `RelayerRegistry` address, or Solana `relayer-registry` program id. */
31
+ registry: string;
32
+ /** Solana only: the funding (creator) pubkey for the create_job instruction. */
33
+ creator?: PublicKey;
34
+ /** 32-byte job id; random if omitted. */
35
+ jobId?: Hex;
36
+ randomBytes?: (n: number) => Uint8Array;
37
+ }
38
+ /** Commit the payload and produce the advert + escrow funding artifact. */
39
+ export declare function prepareJob(payload: JobPayload, opts: PrepareOptions): PreparedJob;
40
+ /** Seal the payload to the winner's advertised x25519 key into a delivery envelope. */
41
+ export declare function buildPayloadEnvelope(winner: Bid, payload: JobPayload, rand?: (n: number) => Uint8Array): PayloadEnvelope;
42
+ /** End-to-end orchestration over an injected funding step and registry readers. */
43
+ export interface SubmitGasPrivateOptions extends PrepareOptions {
44
+ payload: JobPayload;
45
+ gateway: GatewayOptions;
46
+ readers: RegistryReaders;
47
+ /** Submit + confirm the escrow funding tx (the user's wallet). */
48
+ fundEscrow: (job: PreparedJob) => Promise<void>;
49
+ /** Per-chain bid signature verifier (default EVM). */
50
+ verifySig?: (bid: Bid) => Promise<boolean>;
51
+ minBids?: number;
52
+ timeoutMs?: number;
53
+ random?: () => number;
54
+ }
55
+ export interface SubmitGasPrivateResult {
56
+ jobId: Hex;
57
+ payloadHash: Hex;
58
+ winner: VerifiedBid;
59
+ }
60
+ /**
61
+ * Run the whole flow. Throws if the escrow is not funded, no valid bid arrives, or
62
+ * the gateway is unreachable. On success the winning relayer will accept + submit;
63
+ * watch the escrow's `JobSubmitted` event (or poll `jobs(jobId).submitted`) to confirm.
64
+ */
65
+ export declare function submitGasPrivate(opts: SubmitGasPrivateOptions): Promise<SubmitGasPrivateResult>;
66
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAW,GAAG,EAAE,MAAM,MAAM,CAAC;AAGzC,OAAO,EAKL,KAAK,MAAM,EACX,KAAK,GAAG,EACR,KAAK,UAAU,EACf,KAAK,eAAe,EACrB,MAAM,UAAU,CAAC;AAClB,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,WAAW,EACjB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAE9D,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,GAAG,CAAC;IACX,WAAW,EAAE,GAAG,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,eAAe,CAAC,EAAE,sBAAsB,CAAC;CAC1C;AAED,mDAAmD;AACnD,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,yCAAyC;IACzC,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,UAAU,CAAC;CACzC;AAED,2EAA2E;AAC3E,wBAAgB,UAAU,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,cAAc,GAAG,WAAW,CAwCjF;AAED,uFAAuF;AACvF,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,GAAG,EACX,OAAO,EAAE,UAAU,EACnB,IAAI,GAAE,CAAC,CAAC,EAAE,MAAM,KAAK,UAAwB,GAC5C,eAAe,CAUjB;AAED,mFAAmF;AACnF,MAAM,WAAW,uBAAwB,SAAQ,cAAc;IAC7D,OAAO,EAAE,UAAU,CAAC;IACpB,OAAO,EAAE,cAAc,CAAC;IACxB,OAAO,EAAE,eAAe,CAAC;IACzB,kEAAkE;IAClE,UAAU,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,sDAAsD;IACtD,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,GAAG,CAAC;IACX,WAAW,EAAE,GAAG,CAAC;IACjB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,uBAAuB,GAC5B,OAAO,CAAC,sBAAsB,CAAC,CAoBjC"}
package/dist/client.js ADDED
@@ -0,0 +1,101 @@
1
+ /**
2
+ * `submitGasPrivate` and the building blocks behind it (spec/relayer-market.md). The
3
+ * flow: commit the payload, fund the escrow, advertise, collect + verify bids, pick a
4
+ * stake-weighted winner, and deliver the payload sealed to the winner's key. The
5
+ * relayer then accepts and submits on-chain; gas and submitter identity are theirs,
6
+ * not the user's.
7
+ */
8
+ import { randomBytes } from "@noble/hashes/utils";
9
+ import { PublicKey } from "@solana/web3.js";
10
+ import { sealBox } from "./crypto.js";
11
+ import { bytesToHex, hexToBytes, payloadBytes, payloadHash, } from "./job.js";
12
+ import { buildEvmCreateJob, buildSolanaCreateJob, } from "./escrow.js";
13
+ import { collectBids, postAdvert, postPayload, } from "./gateway.js";
14
+ import { selectWinner, verifyBids, } from "./select.js";
15
+ /** Commit the payload and produce the advert + escrow funding artifact. */
16
+ export function prepareJob(payload, opts) {
17
+ const rand = opts.randomBytes ?? randomBytes;
18
+ const jobId = opts.jobId ?? bytesToHex(rand(32));
19
+ const hash = payloadHash(payload);
20
+ const advert = {
21
+ t: "advert",
22
+ v: 1,
23
+ jobId,
24
+ chain: payload.chain,
25
+ fee: opts.fee.toString(),
26
+ deadline: opts.deadline,
27
+ payloadHash: hash,
28
+ };
29
+ const prepared = {
30
+ jobId,
31
+ payloadHash: hash,
32
+ deadline: opts.deadline,
33
+ fee: opts.fee,
34
+ advert,
35
+ };
36
+ if (payload.chain === 2) {
37
+ prepared.evmCreateJob = buildEvmCreateJob({
38
+ registry: opts.registry,
39
+ jobId,
40
+ payloadHash: hash,
41
+ deadline: opts.deadline,
42
+ fee: opts.fee,
43
+ });
44
+ }
45
+ else {
46
+ if (!opts.creator)
47
+ throw new Error("Opaque relayer: Solana jobs need opts.creator");
48
+ prepared.solanaCreateJob = buildSolanaCreateJob({
49
+ program: new PublicKey(opts.registry),
50
+ creator: opts.creator,
51
+ jobId,
52
+ payloadHash: hash,
53
+ deadline: opts.deadline,
54
+ fee: opts.fee,
55
+ });
56
+ }
57
+ return prepared;
58
+ }
59
+ /** Seal the payload to the winner's advertised x25519 key into a delivery envelope. */
60
+ export function buildPayloadEnvelope(winner, payload, rand = randomBytes) {
61
+ const recipient = hexToBytes(winner.x25519Pk);
62
+ const sealed = sealBox(recipient, payloadBytes(payload), rand);
63
+ return {
64
+ t: "payload",
65
+ v: 1,
66
+ jobId: winner.jobId,
67
+ to: winner.x25519Pk,
68
+ box: bytesToHexBase64(sealed),
69
+ };
70
+ }
71
+ /**
72
+ * Run the whole flow. Throws if the escrow is not funded, no valid bid arrives, or
73
+ * the gateway is unreachable. On success the winning relayer will accept + submit;
74
+ * watch the escrow's `JobSubmitted` event (or poll `jobs(jobId).submitted`) to confirm.
75
+ */
76
+ export async function submitGasPrivate(opts) {
77
+ const prepared = prepareJob(opts.payload, opts);
78
+ await opts.fundEscrow(prepared);
79
+ await postAdvert(opts.gateway, prepared.advert);
80
+ const bids = await collectBids(opts.gateway, prepared.jobId, {
81
+ minBids: opts.minBids ?? 1,
82
+ timeoutMs: opts.timeoutMs ?? 15_000,
83
+ });
84
+ const verified = await verifyBids(bids, opts.fee, opts.readers, opts.verifySig);
85
+ const winner = selectWinner(verified, opts.random);
86
+ if (!winner) {
87
+ throw new Error(`Opaque relayer: no valid bid for job ${prepared.jobId} (${bids.length} raw bid(s))`);
88
+ }
89
+ const envelope = buildPayloadEnvelope(winner.bid, opts.payload, opts.randomBytes);
90
+ await postPayload(opts.gateway, envelope);
91
+ return { jobId: prepared.jobId, payloadHash: prepared.payloadHash, winner };
92
+ }
93
+ function bytesToHexBase64(b) {
94
+ // base64 without a Buffer dependency (works in browser + node).
95
+ let bin = "";
96
+ for (const x of b)
97
+ bin += String.fromCharCode(x);
98
+ // eslint-disable-next-line no-undef
99
+ return typeof btoa === "function" ? btoa(bin) : Buffer.from(b).toString("base64");
100
+ }
101
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * NaCl crypto_box (x25519-xsalsa20-poly1305) sealing, matching the relayer node's
3
+ * `crypto_box` Rust side (spec/relayer-market.md §3.3). The box wire format is
4
+ * `epk(32) ‖ nonce(24) ‖ ciphertext`; the user seals to the winning relayer's
5
+ * advertised x25519 public key.
6
+ */
7
+ /**
8
+ * Seal `plaintext` to `recipientX25519Pub`, returning `epk ‖ nonce(24) ‖ ct`.
9
+ * Interoperable with the relayer node's `crypto_box::SalsaBox`.
10
+ */
11
+ export declare function sealBox(recipientX25519Pub: Uint8Array, plaintext: Uint8Array, randomBytes: (n: number) => Uint8Array): Uint8Array;
12
+ /** Open a box addressed to `ourX25519Secret` (used in tests / read paths). */
13
+ export declare function openBox(ourX25519Secret: Uint8Array, boxed: Uint8Array): Uint8Array;
14
+ declare function hsalsa(dh: Uint8Array): Uint8Array;
15
+ /**
16
+ * Derive the box identity the relayer node advertises, from its operator seed
17
+ * (`x25519_secret = keccak256("opaque-relayer-box-v1" ‖ seed)`). Exposed for tests
18
+ * that simulate a relayer; users only ever consume the advertised public key.
19
+ */
20
+ export { hsalsa as _hsalsaForTests };
21
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH;;;GAGG;AACH,wBAAgB,OAAO,CACrB,kBAAkB,EAAE,UAAU,EAC9B,SAAS,EAAE,UAAU,EACrB,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,UAAU,GACrC,UAAU,CAYZ;AAED,8EAA8E;AAC9E,wBAAgB,OAAO,CAAC,eAAe,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,UAAU,CAOlF;AAoBD,iBAAS,MAAM,CAAC,EAAE,EAAE,UAAU,GAAG,UAAU,CAqE1C;AAED;;;;GAIG;AACH,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,CAAC"}
package/dist/crypto.js ADDED
@@ -0,0 +1,125 @@
1
+ /**
2
+ * NaCl crypto_box (x25519-xsalsa20-poly1305) sealing, matching the relayer node's
3
+ * `crypto_box` Rust side (spec/relayer-market.md §3.3). The box wire format is
4
+ * `epk(32) ‖ nonce(24) ‖ ciphertext`; the user seals to the winning relayer's
5
+ * advertised x25519 public key.
6
+ */
7
+ import { x25519 } from "@noble/curves/ed25519";
8
+ import { xsalsa20poly1305 } from "@noble/ciphers/salsa";
9
+ /**
10
+ * Seal `plaintext` to `recipientX25519Pub`, returning `epk ‖ nonce(24) ‖ ct`.
11
+ * Interoperable with the relayer node's `crypto_box::SalsaBox`.
12
+ */
13
+ export function sealBox(recipientX25519Pub, plaintext, randomBytes) {
14
+ const ephSecret = randomBytes(32);
15
+ const ephPublic = x25519.getPublicKey(ephSecret);
16
+ const shared = naclBoxKey(ephSecret, recipientX25519Pub);
17
+ const nonce = randomBytes(24);
18
+ const cipher = xsalsa20poly1305(shared, nonce);
19
+ const ct = cipher.encrypt(plaintext);
20
+ const out = new Uint8Array(32 + 24 + ct.length);
21
+ out.set(ephPublic, 0);
22
+ out.set(nonce, 32);
23
+ out.set(ct, 56);
24
+ return out;
25
+ }
26
+ /** Open a box addressed to `ourX25519Secret` (used in tests / read paths). */
27
+ export function openBox(ourX25519Secret, boxed) {
28
+ const epk = boxed.subarray(0, 32);
29
+ const nonce = boxed.subarray(32, 56);
30
+ const ct = boxed.subarray(56);
31
+ const shared = naclBoxKey(ourX25519Secret, epk);
32
+ const cipher = xsalsa20poly1305(shared, nonce);
33
+ return cipher.decrypt(ct);
34
+ }
35
+ /**
36
+ * NaCl `crypto_box_beforenm`: `HSalsa20(0^16, X25519(sk, pk))`. xsalsa20poly1305 takes
37
+ * a 32-byte key; NaCl's box derives that key by hashing the DH output with HSalsa20
38
+ * under a zero nonce. @noble/ciphers ships `hsalsa` via the salsa module.
39
+ */
40
+ function naclBoxKey(secret, peerPublic) {
41
+ const dh = x25519.getSharedSecret(secret, peerPublic);
42
+ return hsalsa(dh);
43
+ }
44
+ // HSalsa20 core with a 16-byte zero nonce and the NaCl "sigma" constant, applied to
45
+ // the 32-byte DH output to produce the box key (RFC: NaCl crypto_box_beforenm).
46
+ const SIGMA = new TextEncoder().encode("expand 32-byte k");
47
+ function rotl(x, b) {
48
+ return ((x << b) | (x >>> (32 - b))) >>> 0;
49
+ }
50
+ function hsalsa(dh) {
51
+ // Input: key = dh (32 bytes), nonce = 16 zero bytes.
52
+ const x = new Uint32Array(16);
53
+ const le = (b, i) => (b[i] | (b[i + 1] << 8) | (b[i + 2] << 16) | (b[i + 3] << 24)) >>> 0;
54
+ x[0] = le(SIGMA, 0);
55
+ x[5] = le(SIGMA, 4);
56
+ x[10] = le(SIGMA, 8);
57
+ x[15] = le(SIGMA, 12);
58
+ x[1] = le(dh, 0);
59
+ x[2] = le(dh, 4);
60
+ x[3] = le(dh, 8);
61
+ x[4] = le(dh, 12);
62
+ x[11] = le(dh, 16);
63
+ x[12] = le(dh, 20);
64
+ x[13] = le(dh, 24);
65
+ x[14] = le(dh, 28);
66
+ // nonce x[6..10] stay zero.
67
+ for (let i = 0; i < 20; i += 2) {
68
+ x[4] ^= rotl((x[0] + x[12]) >>> 0, 7);
69
+ x[8] ^= rotl((x[4] + x[0]) >>> 0, 9);
70
+ x[12] ^= rotl((x[8] + x[4]) >>> 0, 13);
71
+ x[0] ^= rotl((x[12] + x[8]) >>> 0, 18);
72
+ x[9] ^= rotl((x[5] + x[1]) >>> 0, 7);
73
+ x[13] ^= rotl((x[9] + x[5]) >>> 0, 9);
74
+ x[1] ^= rotl((x[13] + x[9]) >>> 0, 13);
75
+ x[5] ^= rotl((x[1] + x[13]) >>> 0, 18);
76
+ x[14] ^= rotl((x[10] + x[6]) >>> 0, 7);
77
+ x[2] ^= rotl((x[14] + x[10]) >>> 0, 9);
78
+ x[6] ^= rotl((x[2] + x[14]) >>> 0, 13);
79
+ x[10] ^= rotl((x[6] + x[2]) >>> 0, 18);
80
+ x[3] ^= rotl((x[15] + x[11]) >>> 0, 7);
81
+ x[7] ^= rotl((x[3] + x[15]) >>> 0, 9);
82
+ x[11] ^= rotl((x[7] + x[3]) >>> 0, 13);
83
+ x[15] ^= rotl((x[11] + x[7]) >>> 0, 18);
84
+ x[1] ^= rotl((x[0] + x[3]) >>> 0, 7);
85
+ x[2] ^= rotl((x[1] + x[0]) >>> 0, 9);
86
+ x[3] ^= rotl((x[2] + x[1]) >>> 0, 13);
87
+ x[0] ^= rotl((x[3] + x[2]) >>> 0, 18);
88
+ x[6] ^= rotl((x[5] + x[4]) >>> 0, 7);
89
+ x[7] ^= rotl((x[6] + x[5]) >>> 0, 9);
90
+ x[4] ^= rotl((x[7] + x[6]) >>> 0, 13);
91
+ x[5] ^= rotl((x[4] + x[7]) >>> 0, 18);
92
+ x[11] ^= rotl((x[10] + x[9]) >>> 0, 7);
93
+ x[8] ^= rotl((x[11] + x[10]) >>> 0, 9);
94
+ x[9] ^= rotl((x[8] + x[11]) >>> 0, 13);
95
+ x[10] ^= rotl((x[9] + x[8]) >>> 0, 18);
96
+ x[12] ^= rotl((x[15] + x[14]) >>> 0, 7);
97
+ x[13] ^= rotl((x[12] + x[15]) >>> 0, 9);
98
+ x[14] ^= rotl((x[13] + x[12]) >>> 0, 13);
99
+ x[15] ^= rotl((x[14] + x[13]) >>> 0, 18);
100
+ }
101
+ const out = new Uint8Array(32);
102
+ const wr = (v, i) => {
103
+ out[i] = v & 0xff;
104
+ out[i + 1] = (v >>> 8) & 0xff;
105
+ out[i + 2] = (v >>> 16) & 0xff;
106
+ out[i + 3] = (v >>> 24) & 0xff;
107
+ };
108
+ // HSalsa20 output words: 0,5,10,15,6,7,8,9.
109
+ wr(x[0], 0);
110
+ wr(x[5], 4);
111
+ wr(x[10], 8);
112
+ wr(x[15], 12);
113
+ wr(x[6], 16);
114
+ wr(x[7], 20);
115
+ wr(x[8], 24);
116
+ wr(x[9], 28);
117
+ return out;
118
+ }
119
+ /**
120
+ * Derive the box identity the relayer node advertises, from its operator seed
121
+ * (`x25519_secret = keccak256("opaque-relayer-box-v1" ‖ seed)`). Exposed for tests
122
+ * that simulate a relayer; users only ever consume the advertised public key.
123
+ */
124
+ export { hsalsa as _hsalsaForTests };
125
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"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AC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACvC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACvC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACtC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACvC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACtC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACvC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACvC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACxC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACxC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACzC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;IAC3C,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IAC/B,MAAM,EAAE,GAAG,CAAC,CAAS,EAAE,CAAS,EAAE,EAAE;QAClC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;QAClB,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC;QAC9B,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;QAC/B,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACjC,CAAC,CAAC;IACF,4CAA4C;IAC5C,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACZ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACZ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACb,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IACd,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACb,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACb,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACb,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACb,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Escrow funding: build the `createJob` transaction the user submits to lock the fee
3
+ * against the payload commitment (spec/relayer-market.md §2.2). The user funds this
4
+ * from an address ideally unlinked to their primary identity (§8).
5
+ */
6
+ import { type Address, type Hex } from "viem";
7
+ import { PublicKey, TransactionInstruction } from "@solana/web3.js";
8
+ /** Minimal `createJob` ABI for the EVM `RelayerRegistry`. */
9
+ export declare const relayerRegistryCreateJobAbi: readonly [{
10
+ readonly type: "function";
11
+ readonly name: "createJob";
12
+ readonly stateMutability: "payable";
13
+ readonly inputs: readonly [{
14
+ readonly name: "jobId";
15
+ readonly type: "bytes32";
16
+ }, {
17
+ readonly name: "payloadHash";
18
+ readonly type: "bytes32";
19
+ }, {
20
+ readonly name: "deadline";
21
+ readonly type: "uint64";
22
+ }];
23
+ readonly outputs: readonly [];
24
+ }];
25
+ /** An EVM transaction request the caller signs and sends with their funding wallet. */
26
+ export interface EvmTxRequest {
27
+ to: Address;
28
+ data: Hex;
29
+ value: bigint;
30
+ }
31
+ /** Build the EVM `createJob` call (value = fee). */
32
+ export declare function buildEvmCreateJob(params: {
33
+ registry: Address;
34
+ jobId: Hex;
35
+ payloadHash: Hex;
36
+ deadline: number;
37
+ fee: bigint;
38
+ }): EvmTxRequest;
39
+ /** Derive the job escrow PDA: `["job", jobId]`. */
40
+ export declare function solanaJobPda(program: PublicKey, jobId: Hex): PublicKey;
41
+ /** Build the Solana `create_job` instruction (creator signs and pays the fee). */
42
+ export declare function buildSolanaCreateJob(params: {
43
+ program: PublicKey;
44
+ creator: PublicKey;
45
+ jobId: Hex;
46
+ payloadHash: Hex;
47
+ deadline: number;
48
+ fee: bigint;
49
+ }): TransactionInstruction;
50
+ //# sourceMappingURL=escrow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escrow.d.ts","sourceRoot":"","sources":["../src/escrow.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAsB,KAAK,OAAO,EAAE,KAAK,GAAG,EAAE,MAAM,MAAM,CAAC;AAClE,OAAO,EACL,SAAS,EAET,sBAAsB,EACvB,MAAM,iBAAiB,CAAC;AAIzB,6DAA6D;AAC7D,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;EAY9B,CAAC;AAEX,uFAAuF;AACvF,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,GAAG,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;CACf;AAED,oDAAoD;AACpD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,GAAG,CAAC;IACX,WAAW,EAAE,GAAG,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb,GAAG,YAAY,CAUf;AAOD,mDAAmD;AACnD,wBAAgB,YAAY,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,GAAG,SAAS,CAKtE;AAED,kFAAkF;AAClF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE;IAC3C,OAAO,EAAE,SAAS,CAAC;IACnB,OAAO,EAAE,SAAS,CAAC;IACnB,KAAK,EAAE,GAAG,CAAC;IACX,WAAW,EAAE,GAAG,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb,GAAG,sBAAsB,CAiBzB"}
package/dist/escrow.js ADDED
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Escrow funding: build the `createJob` transaction the user submits to lock the fee
3
+ * against the payload commitment (spec/relayer-market.md §2.2). The user funds this
4
+ * from an address ideally unlinked to their primary identity (§8).
5
+ */
6
+ import { encodeFunctionData } from "viem";
7
+ import { PublicKey, SystemProgram, TransactionInstruction, } from "@solana/web3.js";
8
+ import { sha256 } from "@noble/hashes/sha256";
9
+ import { hexToBytes } from "./job.js";
10
+ /** Minimal `createJob` ABI for the EVM `RelayerRegistry`. */
11
+ export const relayerRegistryCreateJobAbi = [
12
+ {
13
+ type: "function",
14
+ name: "createJob",
15
+ stateMutability: "payable",
16
+ inputs: [
17
+ { name: "jobId", type: "bytes32" },
18
+ { name: "payloadHash", type: "bytes32" },
19
+ { name: "deadline", type: "uint64" },
20
+ ],
21
+ outputs: [],
22
+ },
23
+ ];
24
+ /** Build the EVM `createJob` call (value = fee). */
25
+ export function buildEvmCreateJob(params) {
26
+ return {
27
+ to: params.registry,
28
+ data: encodeFunctionData({
29
+ abi: relayerRegistryCreateJobAbi,
30
+ functionName: "createJob",
31
+ args: [params.jobId, params.payloadHash, BigInt(params.deadline)],
32
+ }),
33
+ value: params.fee,
34
+ };
35
+ }
36
+ /** Anchor global instruction discriminator: `sha256("global:<name>")[..8]`. */
37
+ function disc(name) {
38
+ return sha256(new TextEncoder().encode(`global:${name}`)).subarray(0, 8);
39
+ }
40
+ /** Derive the job escrow PDA: `["job", jobId]`. */
41
+ export function solanaJobPda(program, jobId) {
42
+ return PublicKey.findProgramAddressSync([new TextEncoder().encode("job"), hexToBytes(jobId)], program)[0];
43
+ }
44
+ /** Build the Solana `create_job` instruction (creator signs and pays the fee). */
45
+ export function buildSolanaCreateJob(params) {
46
+ const data = concat([
47
+ disc("create_job"),
48
+ hexToBytes(params.jobId),
49
+ hexToBytes(params.payloadHash),
50
+ i64le(BigInt(params.deadline)),
51
+ u64le(params.fee),
52
+ ]);
53
+ return new TransactionInstruction({
54
+ programId: params.program,
55
+ keys: [
56
+ { pubkey: solanaJobPda(params.program, params.jobId), isSigner: false, isWritable: true },
57
+ { pubkey: params.creator, isSigner: true, isWritable: true },
58
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
59
+ ],
60
+ data: Buffer.from(data),
61
+ });
62
+ }
63
+ function u64le(n) {
64
+ const b = new Uint8Array(8);
65
+ new DataView(b.buffer).setBigUint64(0, n, true);
66
+ return b;
67
+ }
68
+ function i64le(n) {
69
+ const b = new Uint8Array(8);
70
+ new DataView(b.buffer).setBigInt64(0, n, true);
71
+ return b;
72
+ }
73
+ function concat(parts) {
74
+ const len = parts.reduce((a, p) => a + p.length, 0);
75
+ const out = new Uint8Array(len);
76
+ let off = 0;
77
+ for (const p of parts) {
78
+ out.set(p, off);
79
+ off += p.length;
80
+ }
81
+ return out;
82
+ }
83
+ //# sourceMappingURL=escrow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escrow.js","sourceRoot":"","sources":["../src/escrow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * HTTP gateway client (spec/relayer-market.md §3.4). Any relayer node exposes this
3
+ * intake; a censoring gateway is bypassed by pointing at another node. The gateway
4
+ * re-gossips adverts/payloads and serves the bids it has seen.
5
+ */
6
+ import type { Advert, Bid, PayloadEnvelope } from "./job.js";
7
+ export interface GatewayOptions {
8
+ /** Base URL of a relayer node's gateway, e.g. `http://localhost:8787`. */
9
+ baseUrl: string;
10
+ /** Fetch implementation (default `globalThis.fetch`). */
11
+ fetchFn?: typeof fetch;
12
+ }
13
+ /** Advertise a funded job to the mesh. */
14
+ export declare function postAdvert(o: GatewayOptions, advert: Advert): Promise<void>;
15
+ /** Fetch the bids the gateway has seen for a job. */
16
+ export declare function getBids(o: GatewayOptions, jobId: string): Promise<Bid[]>;
17
+ /** Deliver the encrypted payload envelope to the winner via the gateway. */
18
+ export declare function postPayload(o: GatewayOptions, env: PayloadEnvelope): Promise<void>;
19
+ /** Poll for bids until `minBids` arrive or `timeoutMs` elapses. */
20
+ export declare function collectBids(o: GatewayOptions, jobId: string, opts?: {
21
+ minBids?: number;
22
+ timeoutMs?: number;
23
+ intervalMs?: number;
24
+ sleep?: (ms: number) => Promise<void>;
25
+ }): Promise<Bid[]>;
26
+ //# sourceMappingURL=gateway.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE7D,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AAQD,0CAA0C;AAC1C,wBAAsB,UAAU,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOjF;AAED,qDAAqD;AACrD,wBAAsB,OAAO,CAAC,CAAC,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAM9E;AAED,4EAA4E;AAC5E,wBAAsB,WAAW,CAAC,CAAC,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAUxF;AAED,mEAAmE;AACnE,wBAAsB,WAAW,CAC/B,CAAC,EAAE,cAAc,EACjB,KAAK,EAAE,MAAM,EACb,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAAO,GAC9G,OAAO,CAAC,GAAG,EAAE,CAAC,CAYhB"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * HTTP gateway client (spec/relayer-market.md §3.4). Any relayer node exposes this
3
+ * intake; a censoring gateway is bypassed by pointing at another node. The gateway
4
+ * re-gossips adverts/payloads and serves the bids it has seen.
5
+ */
6
+ function fetchOf(o) {
7
+ const f = o.fetchFn ?? globalThis.fetch;
8
+ if (!f)
9
+ throw new Error("Opaque relayer: no fetch available; pass fetchFn.");
10
+ return f;
11
+ }
12
+ /** Advertise a funded job to the mesh. */
13
+ export async function postAdvert(o, advert) {
14
+ const res = await fetchOf(o)(`${o.baseUrl.replace(/\/$/, "")}/v1/jobs`, {
15
+ method: "POST",
16
+ headers: { "content-type": "application/json" },
17
+ body: JSON.stringify(advert),
18
+ });
19
+ if (!res.ok)
20
+ throw new Error(`gateway POST /v1/jobs -> ${res.status}`);
21
+ }
22
+ /** Fetch the bids the gateway has seen for a job. */
23
+ export async function getBids(o, jobId) {
24
+ const res = await fetchOf(o)(`${o.baseUrl.replace(/\/$/, "")}/v1/jobs/${jobId}/bids`);
25
+ if (!res.ok)
26
+ throw new Error(`gateway GET bids -> ${res.status}`);
27
+ return (await res.json());
28
+ }
29
+ /** Deliver the encrypted payload envelope to the winner via the gateway. */
30
+ export async function postPayload(o, env) {
31
+ const res = await fetchOf(o)(`${o.baseUrl.replace(/\/$/, "")}/v1/jobs/${env.jobId}/payload`, {
32
+ method: "POST",
33
+ headers: { "content-type": "application/json" },
34
+ body: JSON.stringify(env),
35
+ });
36
+ if (!res.ok)
37
+ throw new Error(`gateway POST payload -> ${res.status}`);
38
+ }
39
+ /** Poll for bids until `minBids` arrive or `timeoutMs` elapses. */
40
+ export async function collectBids(o, jobId, opts = {}) {
41
+ const minBids = opts.minBids ?? 1;
42
+ const timeoutMs = opts.timeoutMs ?? 15_000;
43
+ const intervalMs = opts.intervalMs ?? 1_000;
44
+ const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
45
+ const deadline = Date.now() + timeoutMs;
46
+ let bids = [];
47
+ for (;;) {
48
+ bids = await getBids(o, jobId);
49
+ if (bids.length >= minBids || Date.now() > deadline)
50
+ return bids;
51
+ await sleep(intervalMs);
52
+ }
53
+ }
54
+ //# sourceMappingURL=gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway.js","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAWH,SAAS,OAAO,CAAC,CAAiB;IAChC,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC;IACxC,IAAI,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IAC7E,OAAO,CAAC,CAAC;AACX,CAAC;AAED,0CAA0C;AAC1C,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,CAAiB,EAAE,MAAc;IAChE,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,EAAE;QACtE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;KAC7B,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;AACzE,CAAC;AAED,qDAAqD;AACrD,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,CAAiB,EAAE,KAAa;IAC5D,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,CAAC,CAAC,CAC1B,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,KAAK,OAAO,CACxD,CAAC;IACF,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IAClE,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAU,CAAC;AACrC,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,CAAiB,EAAE,GAAoB;IACvE,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,CAAC,CAAC,CAC1B,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,GAAG,CAAC,KAAK,UAAU,EAC9D;QACE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC;KAC1B,CACF,CAAC;IACF,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;AACxE,CAAC;AAED,mEAAmE;AACnE,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,CAAiB,EACjB,KAAa,EACb,OAA6G,EAAE;IAE/G,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;IAClC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC;IAC3C,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,KAAK,CAAC;IAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5E,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;IACxC,IAAI,IAAI,GAAU,EAAE,CAAC;IACrB,SAAS,CAAC;QACR,IAAI,GAAG,MAAM,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAC/B,IAAI,IAAI,CAAC,MAAM,IAAI,OAAO,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ;YAAE,OAAO,IAAI,CAAC;QACjE,MAAM,KAAK,CAAC,UAAU,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * `@opaquecash/relayer-client` — client for the Opaque gas-private submission market
3
+ * (spec/relayer-market.md). Build a job and its payload commitment, fund the escrow,
4
+ * advertise to relayer nodes, verify + select a stake-weighted winner, and deliver the
5
+ * payload sealed to the winner's key. Used by PSR verify and (later) pool withdrawals
6
+ * to submit without linking the user's gas wallet to the action.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+ export { CHAIN_ETHEREUM, CHAIN_SOLANA, BID_DOMAIN, evmPayloadBytes, evmPayloadHash, solanaPayloadBytes, solanaPayloadHash, payloadBytes, payloadHash, bidSigningHash, hexToBytes, bytesToHex, type Advert, type Bid, type PayloadEnvelope, type MessageTag, type JobPayload, type EvmJobPayload, type SolanaJobPayload, } from "./job.js";
11
+ export { sealBox, openBox } from "./crypto.js";
12
+ export { relayerRegistryCreateJobAbi, buildEvmCreateJob, buildSolanaCreateJob, solanaJobPda, type EvmTxRequest, } from "./escrow.js";
13
+ export { postAdvert, getBids, postPayload, collectBids, type GatewayOptions, } from "./gateway.js";
14
+ export { verifyEvmBidSignature, verifyBids, selectWinner, type RegistryReaders, type VerifiedBid, } from "./select.js";
15
+ export { prepareJob, buildPayloadEnvelope, submitGasPrivate, type PreparedJob, type PrepareOptions, type SubmitGasPrivateOptions, type SubmitGasPrivateResult, } from "./client.js";
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,cAAc,EACd,YAAY,EACZ,UAAU,EACV,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,cAAc,EACd,UAAU,EACV,UAAU,EACV,KAAK,MAAM,EACX,KAAK,GAAG,EACR,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,gBAAgB,GACtB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EACL,2BAA2B,EAC3B,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,EACZ,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,UAAU,EACV,OAAO,EACP,WAAW,EACX,WAAW,EACX,KAAK,cAAc,GACpB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,qBAAqB,EACrB,UAAU,EACV,YAAY,EACZ,KAAK,eAAe,EACpB,KAAK,WAAW,GACjB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,GAC5B,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * `@opaquecash/relayer-client` — client for the Opaque gas-private submission market
3
+ * (spec/relayer-market.md). Build a job and its payload commitment, fund the escrow,
4
+ * advertise to relayer nodes, verify + select a stake-weighted winner, and deliver the
5
+ * payload sealed to the winner's key. Used by PSR verify and (later) pool withdrawals
6
+ * to submit without linking the user's gas wallet to the action.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+ export { CHAIN_ETHEREUM, CHAIN_SOLANA, BID_DOMAIN, evmPayloadBytes, evmPayloadHash, solanaPayloadBytes, solanaPayloadHash, payloadBytes, payloadHash, bidSigningHash, hexToBytes, bytesToHex, } from "./job.js";
11
+ export { sealBox, openBox } from "./crypto.js";
12
+ export { relayerRegistryCreateJobAbi, buildEvmCreateJob, buildSolanaCreateJob, solanaJobPda, } from "./escrow.js";
13
+ export { postAdvert, getBids, postPayload, collectBids, } from "./gateway.js";
14
+ export { verifyEvmBidSignature, verifyBids, selectWinner, } from "./select.js";
15
+ export { prepareJob, buildPayloadEnvelope, submitGasPrivate, } from "./client.js";
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,cAAc,EACd,YAAY,EACZ,UAAU,EACV,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,cAAc,EACd,UAAU,EACV,UAAU,GAQX,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EACL,2BAA2B,EAC3B,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,GAEb,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,UAAU,EACV,OAAO,EACP,WAAW,EACX,WAAW,GAEZ,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,qBAAqB,EACrB,UAAU,EACV,YAAY,GAGb,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,gBAAgB,GAKjB,MAAM,aAAa,CAAC"}
package/dist/job.d.ts ADDED
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Job construction and payload commitments (spec/relayer-market.md §2.3, §3). A job
3
+ * hides its payload behind a hash; the EVM payload is `abi.encode(target, calldata)`
4
+ * and the Solana payload is an instruction descriptor. These helpers compute the
5
+ * commitment the user passes to `createJob` and the box plaintext the winning relayer
6
+ * decodes.
7
+ */
8
+ import { type Address, type Hex } from "viem";
9
+ import { PublicKey, type TransactionInstruction } from "@solana/web3.js";
10
+ /** Wormhole-convention chain ids, matching spec/UAB.md and the contracts. */
11
+ export declare const CHAIN_ETHEREUM = 2;
12
+ export declare const CHAIN_SOLANA = 1;
13
+ /** Domain tag a bid signs (spec §3.2). */
14
+ export declare const BID_DOMAIN = "opaque-relayer-bid-v1";
15
+ /** A market message tag. */
16
+ export type MessageTag = "advert" | "bid" | "payload";
17
+ export interface Advert {
18
+ t: "advert";
19
+ v: 1;
20
+ jobId: Hex;
21
+ chain: number;
22
+ /** decimal string, base units (wei / lamports). */
23
+ fee: string;
24
+ deadline: number;
25
+ payloadHash: Hex;
26
+ }
27
+ export interface Bid {
28
+ t: "bid";
29
+ v: 1;
30
+ jobId: Hex;
31
+ chain: number;
32
+ operator: string;
33
+ x25519Pk: Hex;
34
+ sig: string;
35
+ }
36
+ export interface PayloadEnvelope {
37
+ t: "payload";
38
+ v: 1;
39
+ jobId: Hex;
40
+ to: Hex;
41
+ /** base64 of `epk ‖ nonce ‖ ct`. */
42
+ box: string;
43
+ }
44
+ /** An EVM job: the relayer will `target.call(calldata)` from the escrow. */
45
+ export interface EvmJobPayload {
46
+ chain: typeof CHAIN_ETHEREUM;
47
+ target: Address;
48
+ calldata: Hex;
49
+ }
50
+ /** A Solana job: the relayer will CPI this instruction (no inner signers allowed). */
51
+ export interface SolanaJobPayload {
52
+ chain: typeof CHAIN_SOLANA;
53
+ instruction: TransactionInstruction;
54
+ }
55
+ export type JobPayload = EvmJobPayload | SolanaJobPayload;
56
+ /** The box plaintext for an EVM job: `abi.encode(address target, bytes data)`. */
57
+ export declare function evmPayloadBytes(p: EvmJobPayload): Uint8Array;
58
+ /** The EVM payload commitment: `keccak256(abi.encode(target, data))`. */
59
+ export declare function evmPayloadHash(p: EvmJobPayload): Hex;
60
+ /**
61
+ * The box plaintext for a Solana job: the self-describing instruction descriptor
62
+ * `program_id(32) ‖ u32_le(n) ‖ [pubkey(32) ‖ is_writable(1)]×n ‖ u32_le(len) ‖ data`
63
+ * that the relayer node decodes (see solana.rs `decode_descriptor`).
64
+ */
65
+ export declare function solanaPayloadBytes(p: SolanaJobPayload): Uint8Array;
66
+ /** The Solana payload commitment (spec §2.3): is_signer is committed false. */
67
+ export declare function solanaPayloadHash(p: SolanaJobPayload): Hex;
68
+ /** Payload commitment for any job. */
69
+ export declare function payloadHash(p: JobPayload): Hex;
70
+ /** Box plaintext for any job. */
71
+ export declare function payloadBytes(p: JobPayload): Uint8Array;
72
+ /** The 32-byte message a bid signs (spec §3.2). */
73
+ export declare function bidSigningHash(jobId: Hex, x25519Pk: Hex): Hex;
74
+ export declare function hexToBytes(hex: string): Uint8Array;
75
+ export declare function bytesToHex(b: Uint8Array): Hex;
76
+ export { PublicKey };
77
+ //# sourceMappingURL=job.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job.d.ts","sourceRoot":"","sources":["../src/job.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAkC,KAAK,OAAO,EAAE,KAAK,GAAG,EAAE,MAAM,MAAM,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,KAAK,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAEzE,6EAA6E;AAC7E,eAAO,MAAM,cAAc,IAAI,CAAC;AAChC,eAAO,MAAM,YAAY,IAAI,CAAC;AAE9B,0CAA0C;AAC1C,eAAO,MAAM,UAAU,0BAA0B,CAAC;AAElD,4BAA4B;AAC5B,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC;AAEtD,MAAM,WAAW,MAAM;IACrB,CAAC,EAAE,QAAQ,CAAC;IACZ,CAAC,EAAE,CAAC,CAAC;IACL,KAAK,EAAE,GAAG,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,GAAG,CAAC;CAClB;AAED,MAAM,WAAW,GAAG;IAClB,CAAC,EAAE,KAAK,CAAC;IACT,CAAC,EAAE,CAAC,CAAC;IACL,KAAK,EAAE,GAAG,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,GAAG,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,eAAe;IAC9B,CAAC,EAAE,SAAS,CAAC;IACb,CAAC,EAAE,CAAC,CAAC;IACL,KAAK,EAAE,GAAG,CAAC;IACX,EAAE,EAAE,GAAG,CAAC;IACR,oCAAoC;IACpC,GAAG,EAAE,MAAM,CAAC;CACb;AAED,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,cAAc,CAAC;IAC7B,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,GAAG,CAAC;CACf;AAED,sFAAsF;AACtF,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,YAAY,CAAC;IAC3B,WAAW,EAAE,sBAAsB,CAAC;CACrC;AAED,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,gBAAgB,CAAC;AAE1D,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,CAAC,EAAE,aAAa,GAAG,UAAU,CAM5D;AAED,yEAAyE;AACzE,wBAAgB,cAAc,CAAC,CAAC,EAAE,aAAa,GAAG,GAAG,CAIpD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,gBAAgB,GAAG,UAAU,CAYlE;AAED,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,gBAAgB,GAAG,GAAG,CAQ1D;AAED,sCAAsC;AACtC,wBAAgB,WAAW,CAAC,CAAC,EAAE,UAAU,GAAG,GAAG,CAE9C;AAED,iCAAiC;AACjC,wBAAgB,YAAY,CAAC,CAAC,EAAE,UAAU,GAAG,UAAU,CAEtD;AAED,mDAAmD;AACnD,wBAAgB,cAAc,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,GAAG,GAAG,CAM7D;AAID,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAKlD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,UAAU,GAAG,GAAG,CAE7C;AAmBD,OAAO,EAAE,SAAS,EAAE,CAAC"}
package/dist/job.js ADDED
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Job construction and payload commitments (spec/relayer-market.md §2.3, §3). A job
3
+ * hides its payload behind a hash; the EVM payload is `abi.encode(target, calldata)`
4
+ * and the Solana payload is an instruction descriptor. These helpers compute the
5
+ * commitment the user passes to `createJob` and the box plaintext the winning relayer
6
+ * decodes.
7
+ */
8
+ import { encodeAbiParameters, keccak256 } from "viem";
9
+ import { PublicKey } from "@solana/web3.js";
10
+ /** Wormhole-convention chain ids, matching spec/UAB.md and the contracts. */
11
+ export const CHAIN_ETHEREUM = 2;
12
+ export const CHAIN_SOLANA = 1;
13
+ /** Domain tag a bid signs (spec §3.2). */
14
+ export const BID_DOMAIN = "opaque-relayer-bid-v1";
15
+ /** The box plaintext for an EVM job: `abi.encode(address target, bytes data)`. */
16
+ export function evmPayloadBytes(p) {
17
+ const encoded = encodeAbiParameters([{ type: "address" }, { type: "bytes" }], [p.target, p.calldata]);
18
+ return hexToBytes(encoded);
19
+ }
20
+ /** The EVM payload commitment: `keccak256(abi.encode(target, data))`. */
21
+ export function evmPayloadHash(p) {
22
+ return keccak256(encodeAbiParameters([{ type: "address" }, { type: "bytes" }], [p.target, p.calldata]));
23
+ }
24
+ /**
25
+ * The box plaintext for a Solana job: the self-describing instruction descriptor
26
+ * `program_id(32) ‖ u32_le(n) ‖ [pubkey(32) ‖ is_writable(1)]×n ‖ u32_le(len) ‖ data`
27
+ * that the relayer node decodes (see solana.rs `decode_descriptor`).
28
+ */
29
+ export function solanaPayloadBytes(p) {
30
+ const ix = p.instruction;
31
+ if (ix.keys.some((k) => k.isSigner)) {
32
+ throw new Error("Opaque relayer: inner instruction accounts must not be signers (spec §2.3)");
33
+ }
34
+ const parts = [ix.programId.toBytes(), u32le(ix.keys.length)];
35
+ for (const k of ix.keys) {
36
+ parts.push(k.pubkey.toBytes(), Uint8Array.from([k.isWritable ? 1 : 0]));
37
+ }
38
+ const data = Uint8Array.from(ix.data);
39
+ parts.push(u32le(data.length), data);
40
+ return concat(parts);
41
+ }
42
+ /** The Solana payload commitment (spec §2.3): is_signer is committed false. */
43
+ export function solanaPayloadHash(p) {
44
+ const ix = p.instruction;
45
+ const parts = [ix.programId.toBytes(), u32le(ix.keys.length)];
46
+ for (const k of ix.keys) {
47
+ parts.push(k.pubkey.toBytes(), Uint8Array.from([0]), Uint8Array.from([k.isWritable ? 1 : 0]));
48
+ }
49
+ parts.push(Uint8Array.from(ix.data));
50
+ return keccak256(concat(parts));
51
+ }
52
+ /** Payload commitment for any job. */
53
+ export function payloadHash(p) {
54
+ return p.chain === CHAIN_ETHEREUM ? evmPayloadHash(p) : solanaPayloadHash(p);
55
+ }
56
+ /** Box plaintext for any job. */
57
+ export function payloadBytes(p) {
58
+ return p.chain === CHAIN_ETHEREUM ? evmPayloadBytes(p) : solanaPayloadBytes(p);
59
+ }
60
+ /** The 32-byte message a bid signs (spec §3.2). */
61
+ export function bidSigningHash(jobId, x25519Pk) {
62
+ return keccak256(concat([
63
+ new TextEncoder().encode(BID_DOMAIN),
64
+ hexToBytes(jobId),
65
+ hexToBytes(x25519Pk),
66
+ ]));
67
+ }
68
+ // --- small byte helpers (kept local; no extra deps) ---
69
+ export function hexToBytes(hex) {
70
+ const h = hex.startsWith("0x") ? hex.slice(2) : hex;
71
+ const out = new Uint8Array(h.length / 2);
72
+ for (let i = 0; i < out.length; i++)
73
+ out[i] = parseInt(h.slice(i * 2, i * 2 + 2), 16);
74
+ return out;
75
+ }
76
+ export function bytesToHex(b) {
77
+ return `0x${Array.from(b).map((x) => x.toString(16).padStart(2, "0")).join("")}`;
78
+ }
79
+ function u32le(n) {
80
+ const b = new Uint8Array(4);
81
+ new DataView(b.buffer).setUint32(0, n, true);
82
+ return b;
83
+ }
84
+ function concat(parts) {
85
+ const len = parts.reduce((a, p) => a + p.length, 0);
86
+ const out = new Uint8Array(len);
87
+ let off = 0;
88
+ for (const p of parts) {
89
+ out.set(p, off);
90
+ off += p.length;
91
+ }
92
+ return out;
93
+ }
94
+ export { PublicKey };
95
+ //# sourceMappingURL=job.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job.js","sourceRoot":"","sources":["../src/job.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Bid verification and stake-weighted winner selection (spec/relayer-market.md §3.2,
3
+ * §6). A user MUST verify, before delivering the payload, that each candidate bid is
4
+ * signed by the registered operator, that the operator's free stake covers the fee,
5
+ * and that the advertised x25519 key matches the registry. Selection among the valid
6
+ * bids is client policy; the default weights uniformly at random by free stake.
7
+ */
8
+ import { type Hex } from "viem";
9
+ import { type Bid } from "./job.js";
10
+ /** A verified bid plus the operator's free stake (base units). */
11
+ export interface VerifiedBid {
12
+ bid: Bid;
13
+ freeStake: bigint;
14
+ }
15
+ /**
16
+ * Readers the user injects so selection can check on-chain truth without this package
17
+ * owning RPC. `freeStakeOf` returns the operator's unbonded stake; `registeredKey`
18
+ * returns the operator's registered x25519 key (0x…) or null if unregistered.
19
+ */
20
+ export interface RegistryReaders {
21
+ freeStakeOf(operator: string): Promise<bigint>;
22
+ registeredKey(operator: string): Promise<Hex | null>;
23
+ }
24
+ /** Verify an EVM bid signature recovers the claimed operator address. */
25
+ export declare function verifyEvmBidSignature(bid: Bid): Promise<boolean>;
26
+ /**
27
+ * Filter bids to those that are well-formed, signed by the registered operator, whose
28
+ * registered key matches the advertised key, and whose free stake covers `fee`.
29
+ * `verifySig` defaults to the EVM verifier; pass a Solana ed25519 verifier for `.sol`.
30
+ */
31
+ export declare function verifyBids(bids: Bid[], fee: bigint, readers: RegistryReaders, verifySig?: (bid: Bid) => Promise<boolean>): Promise<VerifiedBid[]>;
32
+ /**
33
+ * Pick a winner among verified bids, weighted by free stake (spec §6 deters Sybil
34
+ * bidding). `random` returns a float in [0, 1); injectable for determinism in tests.
35
+ */
36
+ export declare function selectWinner(verified: VerifiedBid[], random?: () => number): VerifiedBid | null;
37
+ //# sourceMappingURL=select.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../src/select.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAA+B,KAAK,GAAG,EAAE,MAAM,MAAM,CAAC;AAC7D,OAAO,EAAkC,KAAK,GAAG,EAAE,MAAM,UAAU,CAAC;AAEpE,kEAAkE;AAClE,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,GAAG,CAAC;IACT,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;CACtD;AAED,yEAAyE;AACzE,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAatE;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,GAAG,EAAE,EACX,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,eAAe,EACxB,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,OAAO,CAAC,GACzC,OAAO,CAAC,WAAW,EAAE,CAAC,CAYxB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,WAAW,EAAE,EACvB,MAAM,GAAE,MAAM,MAAoB,GACjC,WAAW,GAAG,IAAI,CAWpB"}
package/dist/select.js ADDED
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Bid verification and stake-weighted winner selection (spec/relayer-market.md §3.2,
3
+ * §6). A user MUST verify, before delivering the payload, that each candidate bid is
4
+ * signed by the registered operator, that the operator's free stake covers the fee,
5
+ * and that the advertised x25519 key matches the registry. Selection among the valid
6
+ * bids is client policy; the default weights uniformly at random by free stake.
7
+ */
8
+ import { hashMessage, recoverAddress } from "viem";
9
+ import { bidSigningHash, CHAIN_ETHEREUM } from "./job.js";
10
+ /** Verify an EVM bid signature recovers the claimed operator address. */
11
+ export async function verifyEvmBidSignature(bid) {
12
+ if (bid.chain !== CHAIN_ETHEREUM)
13
+ return false;
14
+ try {
15
+ // The operator signs the personal_sign of the bid hash; recover and compare.
16
+ const digest = bidSigningHash(bid.jobId, bid.x25519Pk);
17
+ const recovered = await recoverAddress({
18
+ hash: hashMessage({ raw: digest }),
19
+ signature: bid.sig,
20
+ });
21
+ return recovered.toLowerCase() === bid.operator.toLowerCase();
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ }
27
+ /**
28
+ * Filter bids to those that are well-formed, signed by the registered operator, whose
29
+ * registered key matches the advertised key, and whose free stake covers `fee`.
30
+ * `verifySig` defaults to the EVM verifier; pass a Solana ed25519 verifier for `.sol`.
31
+ */
32
+ export async function verifyBids(bids, fee, readers, verifySig) {
33
+ const out = [];
34
+ for (const bid of bids) {
35
+ const sigOk = verifySig ? await verifySig(bid) : await verifyEvmBidSignature(bid);
36
+ if (!sigOk)
37
+ continue;
38
+ const registered = await readers.registeredKey(bid.operator);
39
+ if (!registered || registered.toLowerCase() !== bid.x25519Pk.toLowerCase())
40
+ continue;
41
+ const freeStake = await readers.freeStakeOf(bid.operator);
42
+ if (freeStake < fee)
43
+ continue;
44
+ out.push({ bid, freeStake });
45
+ }
46
+ return out;
47
+ }
48
+ /**
49
+ * Pick a winner among verified bids, weighted by free stake (spec §6 deters Sybil
50
+ * bidding). `random` returns a float in [0, 1); injectable for determinism in tests.
51
+ */
52
+ export function selectWinner(verified, random = Math.random) {
53
+ if (verified.length === 0)
54
+ return null;
55
+ const total = verified.reduce((a, v) => a + v.freeStake, 0n);
56
+ if (total === 0n)
57
+ return verified[0];
58
+ // Weighted pick: scale the random draw into the cumulative stake range.
59
+ let target = BigInt(Math.floor(random() * 1e9)) * total / 1000000000n;
60
+ for (const v of verified) {
61
+ if (target < v.freeStake)
62
+ return v;
63
+ target -= v.freeStake;
64
+ }
65
+ return verified[verified.length - 1];
66
+ }
67
+ //# sourceMappingURL=select.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.js","sourceRoot":"","sources":["../src/select.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,cAAc,EAAY,MAAM,MAAM,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,cAAc,EAAY,MAAM,UAAU,CAAC;AAkBpE,yEAAyE;AACzE,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,GAAQ;IAClD,IAAI,GAAG,CAAC,KAAK,KAAK,cAAc;QAAE,OAAO,KAAK,CAAC;IAC/C,IAAI,CAAC;QACH,6EAA6E;QAC7E,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;QACvD,MAAM,SAAS,GAAG,MAAM,cAAc,CAAC;YACrC,IAAI,EAAE,WAAW,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;YAClC,SAAS,EAAE,GAAG,CAAC,GAAU;SAC1B,CAAC,CAAC;QACH,OAAO,SAAS,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,IAAW,EACX,GAAW,EACX,OAAwB,EACxB,SAA0C;IAE1C,MAAM,GAAG,GAAkB,EAAE,CAAC;IAC9B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,qBAAqB,CAAC,GAAG,CAAC,CAAC;QAClF,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC7D,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE;YAAE,SAAS;QACrF,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC1D,IAAI,SAAS,GAAG,GAAG;YAAE,SAAS;QAC9B,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,QAAuB,EACvB,SAAuB,IAAI,CAAC,MAAM;IAElC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACvC,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAC7D,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC;IACrC,wEAAwE;IACxE,IAAI,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,GAAG,KAAK,GAAG,WAAc,CAAC;IACzE,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,MAAM,GAAG,CAAC,CAAC,SAAS;YAAE,OAAO,CAAC,CAAC;QACnC,MAAM,IAAI,CAAC,CAAC,SAAS,CAAC;IACxB,CAAC;IACD,OAAO,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACvC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@opaquecash/relayer-client",
3
+ "version": "0.2.0",
4
+ "description": "Client for the Opaque gas-private submission market: build jobs, fund the escrow, collect relayer bids, encrypt the payload to the winner, and track settlement (spec/relayer-market.md)",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc -p tsconfig.json",
19
+ "clean": "rm -rf dist"
20
+ },
21
+ "dependencies": {
22
+ "@noble/ciphers": "^1.0.0",
23
+ "@noble/curves": "^1.6.0",
24
+ "@noble/hashes": "^1.5.0",
25
+ "@opaquecash/deployments": "0.2.0",
26
+ "@solana/web3.js": "^1.98.4",
27
+ "viem": "^2.21.0"
28
+ },
29
+ "sideEffects": false,
30
+ "license": "Apache-2.0",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/opaquecash/sdk.git",
34
+ "directory": "packages/relayer-client"
35
+ },
36
+ "homepage": "https://docs.opaque.cash",
37
+ "publishConfig": {
38
+ "access": "public"
39
+ }
40
+ }