@infantmen-labs/harbor-sdk 0.3.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,33 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ /**
3
+ * Zero-dependency account decoders (Uint8Array in, typed structs out).
4
+ * Layouts must match programs/harbor/src/state.rs exactly; the LiteSVM
5
+ * suites and web coder test pin both sides.
6
+ */
7
+ export interface Bond {
8
+ merchant: PublicKey;
9
+ mint: PublicKey;
10
+ amount: bigint;
11
+ slaBps: number;
12
+ challengeSlots: bigint;
13
+ openDisputes: bigint;
14
+ lastChangeSlot: bigint;
15
+ reserved: bigint;
16
+ }
17
+ export interface Dispute {
18
+ binding: PublicKey;
19
+ nonce: bigint;
20
+ reason: number;
21
+ claimant: PublicKey;
22
+ deadlineSlot: bigint;
23
+ stakeLamports: bigint;
24
+ claimSpend: bigint;
25
+ }
26
+ /** MerchantBond layout: disc(8) + merchant(32) + mint(32) + amount(8) +
27
+ * slaBps(2) + challengeSlots(8) + openDisputes(8) + lastChangeSlot(8) +
28
+ * reserved(8) + bump(1). */
29
+ export declare function decodeBond(data: Uint8Array): Bond;
30
+ /** Dispute layout: disc(8) + binding(32) + nonce(8) + reason(1) +
31
+ * claimant(32) + deadlineSlot(8) + stakeLamports(8) + claimSpend(8) +
32
+ * bump(1). */
33
+ export declare function decodeDispute(data: Uint8Array): Dispute;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.decodeBond = decodeBond;
4
+ exports.decodeDispute = decodeDispute;
5
+ const web3_js_1 = require("@solana/web3.js");
6
+ function u64(d, o) {
7
+ return (BigInt(d[o]) |
8
+ (BigInt(d[o + 1]) << 8n) |
9
+ (BigInt(d[o + 2]) << 16n) |
10
+ (BigInt(d[o + 3]) << 24n) |
11
+ (BigInt(d[o + 4]) << 32n) |
12
+ (BigInt(d[o + 5]) << 40n) |
13
+ (BigInt(d[o + 6]) << 48n) |
14
+ (BigInt(d[o + 7]) << 56n));
15
+ }
16
+ function u16(d, o) {
17
+ return d[o] | (d[o + 1] << 8);
18
+ }
19
+ function pk(d, o) {
20
+ return new web3_js_1.PublicKey(d.slice(o, o + 32));
21
+ }
22
+ /** MerchantBond layout: disc(8) + merchant(32) + mint(32) + amount(8) +
23
+ * slaBps(2) + challengeSlots(8) + openDisputes(8) + lastChangeSlot(8) +
24
+ * reserved(8) + bump(1). */
25
+ function decodeBond(data) {
26
+ return {
27
+ merchant: pk(data, 8),
28
+ mint: pk(data, 40),
29
+ amount: u64(data, 72),
30
+ slaBps: u16(data, 80),
31
+ challengeSlots: u64(data, 82),
32
+ openDisputes: u64(data, 90),
33
+ lastChangeSlot: u64(data, 98),
34
+ reserved: u64(data, 106),
35
+ };
36
+ }
37
+ /** Dispute layout: disc(8) + binding(32) + nonce(8) + reason(1) +
38
+ * claimant(32) + deadlineSlot(8) + stakeLamports(8) + claimSpend(8) +
39
+ * bump(1). */
40
+ function decodeDispute(data) {
41
+ return {
42
+ binding: pk(data, 8),
43
+ nonce: u64(data, 40),
44
+ reason: data[48],
45
+ claimant: pk(data, 49),
46
+ deadlineSlot: u64(data, 81),
47
+ stakeLamports: u64(data, 89),
48
+ claimSpend: u64(data, 97),
49
+ };
50
+ }
@@ -0,0 +1,10 @@
1
+ import { PublicKey, TransactionInstruction } from "@solana/web3.js";
2
+ /**
3
+ * Canonical single-signature Ed25519 precompile ix. Offsets point into this
4
+ * ix's own data (indices u16::MAX). Layout: header(16) | pubkey(32) |
5
+ * signature(64) | message(n).
6
+ */
7
+ export declare function buildEd25519Ix(pubkey: PublicKey, signature: Uint8Array, message: Uint8Array): TransactionInstruction;
8
+ /** Upstream channel voucher payload: magic || channel || cumulative || expires. */
9
+ export declare function channelVoucherBytes(channel: PublicKey, cumulative: bigint, expiresAt: bigint): Buffer;
10
+ export declare const VOUCHER_LEN = 50;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VOUCHER_LEN = void 0;
4
+ exports.buildEd25519Ix = buildEd25519Ix;
5
+ exports.channelVoucherBytes = channelVoucherBytes;
6
+ const web3_js_1 = require("@solana/web3.js");
7
+ const ids_1 = require("./ids");
8
+ /**
9
+ * Canonical single-signature Ed25519 precompile ix. Offsets point into this
10
+ * ix's own data (indices u16::MAX). Layout: header(16) | pubkey(32) |
11
+ * signature(64) | message(n).
12
+ */
13
+ function buildEd25519Ix(pubkey, signature, message) {
14
+ if (signature.length !== 64)
15
+ throw new Error("signature must be 64 bytes");
16
+ const header = Buffer.alloc(16);
17
+ header.writeUInt8(1, 0);
18
+ header.writeUInt8(0, 1);
19
+ header.writeUInt16LE(48, 2);
20
+ header.writeUInt16LE(0xffff, 4);
21
+ header.writeUInt16LE(16, 6);
22
+ header.writeUInt16LE(0xffff, 8);
23
+ header.writeUInt16LE(112, 10);
24
+ header.writeUInt16LE(message.length, 12);
25
+ header.writeUInt16LE(0xffff, 14);
26
+ return new web3_js_1.TransactionInstruction({
27
+ programId: ids_1.ED25519_PROGRAM_ID,
28
+ keys: [],
29
+ data: Buffer.concat([
30
+ header,
31
+ pubkey.toBuffer(),
32
+ Buffer.from(signature),
33
+ Buffer.from(message),
34
+ ]),
35
+ });
36
+ }
37
+ /** Upstream channel voucher payload: magic || channel || cumulative || expires. */
38
+ function channelVoucherBytes(channel, cumulative, expiresAt) {
39
+ const out = Buffer.alloc(50);
40
+ out.writeUInt8(0x56, 0);
41
+ out.writeUInt8(0x01, 1);
42
+ channel.toBuffer().copy(out, 2);
43
+ out.writeBigUInt64LE(cumulative, 34);
44
+ out.writeBigInt64LE(expiresAt, 42);
45
+ return out;
46
+ }
47
+ exports.VOUCHER_LEN = 50;
@@ -0,0 +1,20 @@
1
+ import { PublicKey, TransactionInstruction } from "@solana/web3.js";
2
+ export declare function registerMerchantIx(programId: PublicKey, merchant: PublicKey, bond: PublicKey, mint: PublicKey, slaBps: number, challengeSlots: bigint): TransactionInstruction;
3
+ export declare function postBondIx(programId: PublicKey, merchant: PublicKey, bond: PublicKey, mint: PublicKey, merchantAta: PublicKey, vault: PublicKey, amount: bigint): TransactionInstruction;
4
+ export declare function topUpBondIx(programId: PublicKey, merchant: PublicKey, bond: PublicKey, mint: PublicKey, merchantAta: PublicKey, vault: PublicKey, amount: bigint): TransactionInstruction;
5
+ export declare function withdrawBondIx(programId: PublicKey, merchant: PublicKey, bond: PublicKey, mint: PublicKey, merchantAta: PublicKey, vault: PublicKey, amount: bigint): TransactionInstruction;
6
+ export declare function withdrawTreasuryIx(programId: PublicKey, authority: PublicKey, programdata: PublicKey, treasury: PublicKey, treasuryAta: PublicKey, destinationAta: PublicKey, mint: PublicKey, amount: bigint): TransactionInstruction;
7
+ export declare function bindChannelIx(programId: PublicKey, merchant: PublicKey, bond: PublicKey, binding: PublicKey, channel: PublicKey, channelProgram: PublicKey, maxSpend: bigint): TransactionInstruction;
8
+ export interface SubmitReceiptArgs {
9
+ cumulativeSpend: bigint;
10
+ meterHash: Uint8Array;
11
+ outputHash: Uint8Array;
12
+ status: number;
13
+ nonce: bigint;
14
+ expirySlot: bigint;
15
+ signer: PublicKey;
16
+ }
17
+ export declare function submitReceiptIx(programId: PublicKey, merchant: PublicKey, bond: PublicKey, binding: PublicKey, receipt: PublicKey, args: SubmitReceiptArgs): TransactionInstruction;
18
+ export declare function openDisputeIx(programId: PublicKey, claimant: PublicKey, bond: PublicKey, binding: PublicKey, channel: PublicKey, dispute: PublicKey, mint: PublicKey, claimantAta: PublicKey, vault: PublicKey, nonce: bigint, reason: number, claimSpend: bigint): TransactionInstruction;
19
+ export declare function haltBindingIx(programId: PublicKey, merchant: PublicKey, binding: PublicKey): TransactionInstruction;
20
+ export declare function resolveTimeoutIx(programId: PublicKey, resolver: PublicKey, bond: PublicKey, mint: PublicKey, binding: PublicKey, dispute: PublicKey, claimant: PublicKey, treasury: PublicKey, vault: PublicKey, treasuryAta: PublicKey, claimantAta: PublicKey, nonce: bigint): TransactionInstruction;
@@ -0,0 +1,176 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerMerchantIx = registerMerchantIx;
4
+ exports.postBondIx = postBondIx;
5
+ exports.topUpBondIx = topUpBondIx;
6
+ exports.withdrawBondIx = withdrawBondIx;
7
+ exports.withdrawTreasuryIx = withdrawTreasuryIx;
8
+ exports.bindChannelIx = bindChannelIx;
9
+ exports.submitReceiptIx = submitReceiptIx;
10
+ exports.openDisputeIx = openDisputeIx;
11
+ exports.haltBindingIx = haltBindingIx;
12
+ exports.resolveTimeoutIx = resolveTimeoutIx;
13
+ const web3_js_1 = require("@solana/web3.js");
14
+ const ids_1 = require("./ids");
15
+ const D = {
16
+ registerMerchant: [238, 245, 77, 132, 161, 88, 216, 248],
17
+ postBond: [168, 151, 202, 119, 163, 58, 147, 247],
18
+ topUpBond: [110, 37, 8, 119, 210, 231, 202, 197],
19
+ withdrawBond: [222, 199, 141, 31, 188, 93, 155, 40],
20
+ bindChannel: [56, 13, 116, 192, 54, 234, 8, 201],
21
+ haltBinding: [253, 79, 175, 71, 63, 49, 108, 135],
22
+ submitReceipt: [172, 84, 119, 35, 195, 154, 214, 176],
23
+ openDispute: [137, 25, 99, 119, 23, 223, 161, 42],
24
+ resolveTimeout: [149, 55, 89, 144, 121, 143, 48, 210],
25
+ refundUnused: [239, 108, 1, 110, 2, 81, 44, 174],
26
+ withdrawTreasury: [40, 63, 122, 158, 144, 216, 83, 96],
27
+ };
28
+ function u8(v) {
29
+ const b = Buffer.alloc(1);
30
+ b.writeUInt8(v);
31
+ return b;
32
+ }
33
+ function u16(v) {
34
+ const b = Buffer.alloc(2);
35
+ b.writeUInt16LE(v);
36
+ return b;
37
+ }
38
+ function u64(v) {
39
+ const b = Buffer.alloc(8);
40
+ b.writeBigUInt64LE(v);
41
+ return b;
42
+ }
43
+ function pk(k) {
44
+ return k.toBuffer();
45
+ }
46
+ function bytes32(b) {
47
+ if (b.length !== 32)
48
+ throw new Error("expected 32 bytes");
49
+ return Buffer.from(b);
50
+ }
51
+ function keys(programId, disc, data, metas) {
52
+ return new web3_js_1.TransactionInstruction({
53
+ programId,
54
+ keys: metas.map((m) => ({
55
+ pubkey: m.key,
56
+ isWritable: m.w ?? false,
57
+ isSigner: m.s ?? false,
58
+ })),
59
+ data: Buffer.concat([Buffer.from(disc), data]),
60
+ });
61
+ }
62
+ function registerMerchantIx(programId, merchant, bond, mint, slaBps, challengeSlots) {
63
+ return keys(programId, D.registerMerchant, Buffer.concat([u16(slaBps), u64(challengeSlots)]), [
64
+ { key: merchant, w: true, s: true },
65
+ { key: bond, w: true },
66
+ { key: mint },
67
+ { key: ids_1.SYSTEM_PROGRAM_ID },
68
+ ]);
69
+ }
70
+ function postBondIx(programId, merchant, bond, mint, merchantAta, vault, amount) {
71
+ return keys(programId, D.postBond, u64(amount), [
72
+ { key: merchant, w: true, s: true },
73
+ { key: bond, w: true },
74
+ { key: mint },
75
+ { key: merchantAta, w: true },
76
+ { key: vault, w: true },
77
+ { key: ids_1.TOKEN_PROGRAM_ID },
78
+ { key: ids_1.ATA_PROGRAM_ID },
79
+ { key: ids_1.SYSTEM_PROGRAM_ID },
80
+ ]);
81
+ }
82
+ function topUpBondIx(programId, merchant, bond, mint, merchantAta, vault, amount) {
83
+ return keys(programId, D.topUpBond, u64(amount), [
84
+ { key: merchant, w: true, s: true },
85
+ { key: bond, w: true },
86
+ { key: mint },
87
+ { key: merchantAta, w: true },
88
+ { key: vault, w: true },
89
+ { key: ids_1.TOKEN_PROGRAM_ID },
90
+ ]);
91
+ }
92
+ function withdrawBondIx(programId, merchant, bond, mint, merchantAta, vault, amount) {
93
+ return keys(programId, D.withdrawBond, u64(amount), [
94
+ { key: merchant, w: true, s: true },
95
+ { key: bond, w: true },
96
+ { key: mint },
97
+ { key: merchantAta, w: true },
98
+ { key: vault, w: true },
99
+ { key: ids_1.TOKEN_PROGRAM_ID },
100
+ ]);
101
+ }
102
+ function withdrawTreasuryIx(programId, authority, programdata, treasury, treasuryAta, destinationAta, mint, amount) {
103
+ return keys(programId, D.withdrawTreasury, u64(amount), [
104
+ { key: authority, s: true },
105
+ { key: programdata },
106
+ { key: treasury },
107
+ { key: treasuryAta, w: true },
108
+ { key: destinationAta, w: true },
109
+ { key: mint },
110
+ { key: ids_1.TOKEN_PROGRAM_ID },
111
+ ]);
112
+ }
113
+ function bindChannelIx(programId, merchant, bond, binding, channel, channelProgram, maxSpend) {
114
+ return keys(programId, D.bindChannel, Buffer.concat([pk(channelProgram), u64(maxSpend)]), [
115
+ { key: merchant, w: true, s: true },
116
+ { key: bond },
117
+ { key: binding, w: true },
118
+ { key: channel },
119
+ { key: ids_1.SYSTEM_PROGRAM_ID },
120
+ ]);
121
+ }
122
+ function submitReceiptIx(programId, merchant, bond, binding, receipt, args) {
123
+ return keys(programId, D.submitReceipt, Buffer.concat([
124
+ u64(args.cumulativeSpend),
125
+ bytes32(args.meterHash),
126
+ bytes32(args.outputHash),
127
+ u8(args.status),
128
+ u64(args.nonce),
129
+ u64(args.expirySlot),
130
+ pk(args.signer),
131
+ ]), [
132
+ { key: merchant, w: true, s: true },
133
+ { key: bond },
134
+ { key: binding, w: true },
135
+ { key: receipt, w: true },
136
+ { key: ids_1.IX_SYSVAR_ID },
137
+ { key: ids_1.SYSTEM_PROGRAM_ID },
138
+ ]);
139
+ }
140
+ function openDisputeIx(programId, claimant, bond, binding, channel, dispute, mint, claimantAta, vault, nonce, reason, claimSpend) {
141
+ return keys(programId, D.openDispute, Buffer.concat([u64(nonce), u8(reason), u64(claimSpend)]), [
142
+ { key: claimant, w: true, s: true },
143
+ { key: bond, w: true },
144
+ { key: binding },
145
+ { key: channel },
146
+ { key: dispute, w: true },
147
+ { key: mint },
148
+ { key: claimantAta, w: true },
149
+ { key: vault, w: true },
150
+ { key: ids_1.TOKEN_PROGRAM_ID },
151
+ { key: ids_1.SYSTEM_PROGRAM_ID },
152
+ ]);
153
+ }
154
+ function haltBindingIx(programId, merchant, binding) {
155
+ return keys(programId, D.haltBinding, Buffer.alloc(0), [
156
+ { key: merchant, w: true, s: true },
157
+ { key: binding, w: true },
158
+ ]);
159
+ }
160
+ function resolveTimeoutIx(programId, resolver, bond, mint, binding, dispute, claimant, treasury, vault, treasuryAta, claimantAta, nonce) {
161
+ return keys(programId, D.resolveTimeout, u64(nonce), [
162
+ { key: resolver, w: true, s: true },
163
+ { key: bond, w: true },
164
+ { key: mint },
165
+ { key: binding },
166
+ { key: dispute, w: true },
167
+ { key: claimant, w: true },
168
+ { key: treasury },
169
+ { key: vault, w: true },
170
+ { key: treasuryAta, w: true },
171
+ { key: claimantAta, w: true },
172
+ { key: ids_1.ATA_PROGRAM_ID },
173
+ { key: ids_1.TOKEN_PROGRAM_ID },
174
+ { key: ids_1.SYSTEM_PROGRAM_ID },
175
+ ]);
176
+ }
@@ -0,0 +1,9 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ export declare const HARBOR_PROGRAM_ID: PublicKey;
3
+ export declare const CHANNEL_PROGRAM_ID: PublicKey;
4
+ export declare const ED25519_PROGRAM_ID: PublicKey;
5
+ export declare const IX_SYSVAR_ID: PublicKey;
6
+ export declare const SYSTEM_PROGRAM_ID: PublicKey;
7
+ export declare const TOKEN_PROGRAM_ID: PublicKey;
8
+ export declare const ATA_PROGRAM_ID: PublicKey;
9
+ export declare const RENT_SYSVAR_ID: PublicKey;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RENT_SYSVAR_ID = exports.ATA_PROGRAM_ID = exports.TOKEN_PROGRAM_ID = exports.SYSTEM_PROGRAM_ID = exports.IX_SYSVAR_ID = exports.ED25519_PROGRAM_ID = exports.CHANNEL_PROGRAM_ID = exports.HARBOR_PROGRAM_ID = void 0;
4
+ const web3_js_1 = require("@solana/web3.js");
5
+ exports.HARBOR_PROGRAM_ID = new web3_js_1.PublicKey("BuRyKLqCsTLcyLVFEjxTjmF4DryCT3LmVDjwqhduvB4H");
6
+ exports.CHANNEL_PROGRAM_ID = new web3_js_1.PublicKey("CHNLxYvVA28MJP9PrFuDXccuoGXAx7jBacfLEkahyGsX");
7
+ exports.ED25519_PROGRAM_ID = new web3_js_1.PublicKey("Ed25519SigVerify111111111111111111111111111");
8
+ exports.IX_SYSVAR_ID = new web3_js_1.PublicKey("Sysvar1nstructions1111111111111111111111111");
9
+ exports.SYSTEM_PROGRAM_ID = new web3_js_1.PublicKey("11111111111111111111111111111111");
10
+ exports.TOKEN_PROGRAM_ID = new web3_js_1.PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
11
+ exports.ATA_PROGRAM_ID = new web3_js_1.PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
12
+ exports.RENT_SYSVAR_ID = new web3_js_1.PublicKey("SysvarRent111111111111111111111111111111111");
@@ -0,0 +1,7 @@
1
+ export * from "./ids";
2
+ export * from "./pda";
3
+ export * from "./accounts";
4
+ export * from "./receipt";
5
+ export * from "./ed25519";
6
+ export * from "./verify";
7
+ export * from "./harbor-ix";
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./ids"), exports);
18
+ __exportStar(require("./pda"), exports);
19
+ __exportStar(require("./accounts"), exports);
20
+ __exportStar(require("./receipt"), exports);
21
+ __exportStar(require("./ed25519"), exports);
22
+ __exportStar(require("./verify"), exports);
23
+ __exportStar(require("./harbor-ix"), exports);
@@ -0,0 +1,5 @@
1
+ export declare class JsonlLogger {
2
+ private readonly path;
3
+ constructor(path: string);
4
+ log(record: Record<string, unknown>): void;
5
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JsonlLogger = void 0;
4
+ const node_fs_1 = require("node:fs");
5
+ class JsonlLogger {
6
+ path;
7
+ constructor(path) {
8
+ this.path = path;
9
+ }
10
+ log(record) {
11
+ (0, node_fs_1.appendFileSync)(this.path, JSON.stringify(record) + "\n");
12
+ }
13
+ }
14
+ exports.JsonlLogger = JsonlLogger;
@@ -0,0 +1,8 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ export declare function bondPda(merchant: PublicKey, mint: PublicKey): [PublicKey, number];
3
+ export declare function bindingPda(channel: PublicKey): [PublicKey, number];
4
+ export declare function receiptPda(binding: PublicKey, nonce: bigint): [PublicKey, number];
5
+ export declare function disputePda(binding: PublicKey, nonce: bigint): [PublicKey, number];
6
+ export declare function treasuryPda(mint: PublicKey): [PublicKey, number];
7
+ export declare function channelPda(channelProgram: PublicKey, payer: PublicKey, payee: PublicKey, mint: PublicKey, authorizedSigner: PublicKey, salt: bigint, openSlot: bigint): [PublicKey, number];
8
+ export declare function channelAta(channel: PublicKey, tokenProgram: PublicKey, mint: PublicKey, ataProgram: PublicKey): [PublicKey, number];
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.bondPda = bondPda;
4
+ exports.bindingPda = bindingPda;
5
+ exports.receiptPda = receiptPda;
6
+ exports.disputePda = disputePda;
7
+ exports.treasuryPda = treasuryPda;
8
+ exports.channelPda = channelPda;
9
+ exports.channelAta = channelAta;
10
+ const web3_js_1 = require("@solana/web3.js");
11
+ const ids_1 = require("./ids");
12
+ function bondPda(merchant, mint) {
13
+ return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("bond"), merchant.toBuffer(), mint.toBuffer()], ids_1.HARBOR_PROGRAM_ID);
14
+ }
15
+ function bindingPda(channel) {
16
+ return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("binding"), channel.toBuffer()], ids_1.HARBOR_PROGRAM_ID);
17
+ }
18
+ function receiptPda(binding, nonce) {
19
+ const nonceBuf = Buffer.alloc(8);
20
+ nonceBuf.writeBigUInt64LE(nonce);
21
+ return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("receipt"), binding.toBuffer(), nonceBuf], ids_1.HARBOR_PROGRAM_ID);
22
+ }
23
+ function disputePda(binding, nonce) {
24
+ const nonceBuf = Buffer.alloc(8);
25
+ nonceBuf.writeBigUInt64LE(nonce);
26
+ return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("dispute"), binding.toBuffer(), nonceBuf], ids_1.HARBOR_PROGRAM_ID);
27
+ }
28
+ function treasuryPda(mint) {
29
+ return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("treasury"), mint.toBuffer()], ids_1.HARBOR_PROGRAM_ID);
30
+ }
31
+ function channelPda(channelProgram, payer, payee, mint, authorizedSigner, salt, openSlot) {
32
+ const saltBuf = Buffer.alloc(8);
33
+ saltBuf.writeBigUInt64LE(salt);
34
+ const slotBuf = Buffer.alloc(8);
35
+ slotBuf.writeBigUInt64LE(openSlot);
36
+ return web3_js_1.PublicKey.findProgramAddressSync([
37
+ Buffer.from("channel"),
38
+ payer.toBuffer(),
39
+ payee.toBuffer(),
40
+ mint.toBuffer(),
41
+ authorizedSigner.toBuffer(),
42
+ saltBuf,
43
+ slotBuf,
44
+ ], channelProgram);
45
+ }
46
+ function channelAta(channel, tokenProgram, mint, ataProgram) {
47
+ return web3_js_1.PublicKey.findProgramAddressSync([channel.toBuffer(), tokenProgram.toBuffer(), mint.toBuffer()], ataProgram);
48
+ }
@@ -0,0 +1,15 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ export interface ReceiptFields {
3
+ merchant: PublicKey;
4
+ binding: PublicKey;
5
+ cumulativeSpend: bigint;
6
+ meterHash: Uint8Array;
7
+ outputHash: Uint8Array;
8
+ status: number;
9
+ nonce: bigint;
10
+ expirySlot: bigint;
11
+ signer: PublicKey;
12
+ }
13
+ /** Borsh encoding of the onchain ReceiptMessage. Field order is consensus. */
14
+ export declare function receiptMessageBytes(f: ReceiptFields): Buffer;
15
+ export declare const RECEIPT_MESSAGE_LEN = 185;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RECEIPT_MESSAGE_LEN = void 0;
4
+ exports.receiptMessageBytes = receiptMessageBytes;
5
+ function assertLen(b, n, name) {
6
+ if (b.length !== n)
7
+ throw new Error(`${name} must be ${n} bytes`);
8
+ }
9
+ /** Borsh encoding of the onchain ReceiptMessage. Field order is consensus. */
10
+ function receiptMessageBytes(f) {
11
+ assertLen(f.meterHash, 32, "meterHash");
12
+ assertLen(f.outputHash, 32, "outputHash");
13
+ const out = Buffer.alloc(185);
14
+ let o = 0;
15
+ f.merchant.toBuffer().copy(out, o);
16
+ o += 32;
17
+ f.binding.toBuffer().copy(out, o);
18
+ o += 32;
19
+ out.writeBigUInt64LE(f.cumulativeSpend, o);
20
+ o += 8;
21
+ Buffer.from(f.meterHash).copy(out, o);
22
+ o += 32;
23
+ Buffer.from(f.outputHash).copy(out, o);
24
+ o += 32;
25
+ out.writeUInt8(f.status, o);
26
+ o += 1;
27
+ out.writeBigUInt64LE(f.nonce, o);
28
+ o += 8;
29
+ out.writeBigUInt64LE(f.expirySlot, o);
30
+ o += 8;
31
+ f.signer.toBuffer().copy(out, o);
32
+ o += 32;
33
+ return out;
34
+ }
35
+ exports.RECEIPT_MESSAGE_LEN = 185;
@@ -0,0 +1,3 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ export declare function verifyEd25519(pubkey: PublicKey, message: Uint8Array, signature: Uint8Array): boolean;
3
+ export declare function signEd25519(secretKey: Uint8Array, message: Uint8Array): Uint8Array;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.verifyEd25519 = verifyEd25519;
7
+ exports.signEd25519 = signEd25519;
8
+ const tweetnacl_1 = __importDefault(require("tweetnacl"));
9
+ function verifyEd25519(pubkey, message, signature) {
10
+ if (signature.length !== 64)
11
+ return false;
12
+ return tweetnacl_1.default.sign.detached.verify(message, signature, pubkey.toBytes());
13
+ }
14
+ function signEd25519(secretKey, message) {
15
+ return tweetnacl_1.default.sign.detached(message, secretKey);
16
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_test_1 = require("node:test");
7
+ const strict_1 = __importDefault(require("node:assert/strict"));
8
+ const web3_js_1 = require("@solana/web3.js");
9
+ const tweetnacl_1 = __importDefault(require("tweetnacl"));
10
+ const index_1 = require("../src/index");
11
+ const P = (n) => new web3_js_1.PublicKey(Buffer.alloc(32, n));
12
+ (0, node_test_1.describe)("receipt layout", () => {
13
+ (0, node_test_1.it)("encodes fields in consensus order with exact length", () => {
14
+ const f = {
15
+ merchant: P(1),
16
+ binding: P(2),
17
+ cumulativeSpend: 10000n,
18
+ meterHash: new Uint8Array(32).fill(3),
19
+ outputHash: new Uint8Array(32).fill(4),
20
+ status: 0,
21
+ nonce: 7n,
22
+ expirySlot: 999n,
23
+ signer: P(1),
24
+ };
25
+ const got = (0, index_1.receiptMessageBytes)(f);
26
+ strict_1.default.equal(got.length, index_1.RECEIPT_MESSAGE_LEN);
27
+ // Independently rebuild the expectation part by part.
28
+ const exp = Buffer.concat([
29
+ Buffer.alloc(32, 1),
30
+ Buffer.alloc(32, 2),
31
+ (() => {
32
+ const b = Buffer.alloc(8);
33
+ b.writeBigUInt64LE(10000n);
34
+ return b;
35
+ })(),
36
+ Buffer.alloc(32, 3),
37
+ Buffer.alloc(32, 4),
38
+ Buffer.from([0]),
39
+ (() => {
40
+ const b = Buffer.alloc(8);
41
+ b.writeBigUInt64LE(7n);
42
+ return b;
43
+ })(),
44
+ (() => {
45
+ const b = Buffer.alloc(8);
46
+ b.writeBigUInt64LE(999n);
47
+ return b;
48
+ })(),
49
+ Buffer.alloc(32, 1),
50
+ ]);
51
+ strict_1.default.ok(got.equals(exp));
52
+ });
53
+ (0, node_test_1.it)("round-trips through tweetnacl sign/verify", () => {
54
+ const kp = tweetnacl_1.default.sign.keyPair();
55
+ const msg = (0, index_1.receiptMessageBytes)({
56
+ merchant: new web3_js_1.PublicKey(kp.publicKey),
57
+ binding: P(2),
58
+ cumulativeSpend: 1n,
59
+ meterHash: new Uint8Array(32),
60
+ outputHash: new Uint8Array(32),
61
+ status: 0,
62
+ nonce: 1n,
63
+ expirySlot: 100n,
64
+ signer: new web3_js_1.PublicKey(kp.publicKey),
65
+ });
66
+ const sig = (0, index_1.signEd25519)(kp.secretKey, msg);
67
+ strict_1.default.ok((0, index_1.verifyEd25519)(new web3_js_1.PublicKey(kp.publicKey), msg, sig));
68
+ const tampered = Buffer.from(msg);
69
+ tampered[0] ^= 0xff;
70
+ strict_1.default.ok(!(0, index_1.verifyEd25519)(new web3_js_1.PublicKey(kp.publicKey), tampered, sig));
71
+ });
72
+ });
73
+ (0, node_test_1.describe)("ed25519 ix layout", () => {
74
+ (0, node_test_1.it)("places pubkey/sig/message at 16/48/112 with pinned indices", () => {
75
+ const ix = (0, index_1.buildEd25519Ix)(P(9), new Uint8Array(64).fill(7), new Uint8Array([1, 2, 3]));
76
+ const d = Buffer.from(ix.data);
77
+ strict_1.default.equal(d.length, 16 + 32 + 64 + 3);
78
+ strict_1.default.equal(d.readUInt8(0), 1);
79
+ strict_1.default.equal(d.readUInt16LE(2), 48);
80
+ strict_1.default.equal(d.readUInt16LE(6), 16);
81
+ strict_1.default.equal(d.readUInt16LE(10), 112);
82
+ strict_1.default.equal(d.readUInt16LE(12), 3);
83
+ strict_1.default.ok(d.subarray(16, 48).equals(Buffer.alloc(32, 9)));
84
+ });
85
+ });
86
+ (0, node_test_1.describe)("channel voucher layout", () => {
87
+ (0, node_test_1.it)("is magic || channel || cumulative || expires, 50 bytes", () => {
88
+ const v = (0, index_1.channelVoucherBytes)(P(5), 2000000n, 0n);
89
+ strict_1.default.equal(v.length, index_1.VOUCHER_LEN);
90
+ strict_1.default.equal(v.readUInt8(0), 0x56);
91
+ strict_1.default.equal(v.readUInt8(1), 0x01);
92
+ strict_1.default.ok(v.subarray(2, 34).equals(Buffer.alloc(32, 5)));
93
+ strict_1.default.equal(v.readBigUInt64LE(34), 2000000n);
94
+ strict_1.default.equal(v.readBigInt64LE(42), 0n);
95
+ });
96
+ });
97
+ (0, node_test_1.describe)("account decoders", () => {
98
+ function layout(parts, size) {
99
+ const buf = Buffer.alloc(size);
100
+ for (const [off, v] of parts) {
101
+ if (typeof v === "bigint")
102
+ buf.writeBigUInt64LE(v, off);
103
+ else
104
+ buf.writeUInt16LE(v, off);
105
+ }
106
+ return buf;
107
+ }
108
+ (0, node_test_1.it)("decodes MerchantBond with reserved", () => {
109
+ const buf = layout([
110
+ [72, 500000n],
111
+ [80, 50],
112
+ [82, 150n],
113
+ [90, 2n],
114
+ [98, 7n],
115
+ [106, 11010n],
116
+ ], 115);
117
+ P(3).toBuffer().copy(buf, 8);
118
+ P(4).toBuffer().copy(buf, 40);
119
+ const b = (0, index_1.decodeBond)(buf);
120
+ strict_1.default.ok(b.merchant.equals(P(3)));
121
+ strict_1.default.ok(b.mint.equals(P(4)));
122
+ strict_1.default.equal(b.amount, 500000n);
123
+ strict_1.default.equal(b.slaBps, 50);
124
+ strict_1.default.equal(b.openDisputes, 2n);
125
+ strict_1.default.equal(b.reserved, 11010n);
126
+ });
127
+ (0, node_test_1.it)("decodes Dispute with claimSpend", () => {
128
+ const buf = layout([
129
+ [40, 9n],
130
+ [81, 1000n],
131
+ [89, 10000000n],
132
+ [97, 3670n],
133
+ ], 106);
134
+ P(1).toBuffer().copy(buf, 8);
135
+ buf.writeUInt8(2, 48);
136
+ P(2).toBuffer().copy(buf, 49);
137
+ const d = (0, index_1.decodeDispute)(buf);
138
+ strict_1.default.equal(d.nonce, 9n);
139
+ strict_1.default.equal(d.reason, 2);
140
+ strict_1.default.ok(d.claimant.equals(P(2)));
141
+ strict_1.default.equal(d.claimSpend, 3670n);
142
+ });
143
+ });
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@infantmen-labs/harbor-sdk",
3
+ "version": "0.3.0",
4
+ "description": "TypeScript SDK for Harbor bonded refunds on Solana: PDA helpers, instruction builders, receipt codec, ed25519 verify, account decoders.",
5
+ "license": "MIT",
6
+ "keywords": [
7
+ "solana",
8
+ "payments",
9
+ "metered-api",
10
+ "bond",
11
+ "anchor"
12
+ ],
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "main": "dist/src/index.js",
17
+ "types": "dist/src/index.d.ts",
18
+ "scripts": {
19
+ "build": "tsc -p .",
20
+ "test": "node --test dist/test/vectors.test.js"
21
+ },
22
+ "dependencies": {
23
+ "@solana/web3.js": "^1.98.0",
24
+ "tweetnacl": "^1.0.3"
25
+ }
26
+ }