@dexterai/vault 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +24 -0
- package/README.md +61 -0
- package/dist/constants/index.cjs +92 -0
- package/dist/constants/index.d.cts +34 -0
- package/dist/constants/index.d.ts +34 -0
- package/dist/constants/index.js +57 -0
- package/dist/counterfactual.cjs +138 -0
- package/dist/counterfactual.d.cts +17 -0
- package/dist/counterfactual.d.ts +17 -0
- package/dist/counterfactual.js +113 -0
- package/dist/index.cjs +140 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +113 -0
- package/dist/instructions/index.cjs +5016 -0
- package/dist/instructions/index.d.cts +290 -0
- package/dist/instructions/index.d.ts +290 -0
- package/dist/instructions/index.js +4989 -0
- package/dist/messages/index.cjs +156 -0
- package/dist/messages/index.d.cts +89 -0
- package/dist/messages/index.d.ts +89 -0
- package/dist/messages/index.js +125 -0
- package/dist/precompile/index.cjs +192 -0
- package/dist/precompile/index.d.cts +45 -0
- package/dist/precompile/index.d.ts +45 -0
- package/dist/precompile/index.js +149 -0
- package/dist/reader/index.cjs +121 -0
- package/dist/reader/index.d.cts +41 -0
- package/dist/reader/index.d.ts +41 -0
- package/dist/reader/index.js +93 -0
- package/dist/signers/node/index.cjs +62 -0
- package/dist/signers/node/index.d.cts +21 -0
- package/dist/signers/node/index.d.ts +21 -0
- package/dist/signers/node/index.js +27 -0
- package/dist/signers/types.cjs +18 -0
- package/dist/signers/types.d.cts +34 -0
- package/dist/signers/types.d.ts +34 -0
- package/dist/signers/types.js +0 -0
- package/dist/types.cjs +18 -0
- package/dist/types.d.cts +104 -0
- package/dist/types.d.ts +104 -0
- package/dist/types.js +0 -0
- package/package.json +53 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { TransactionInstruction } from '@solana/web3.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SIMD-0075 secp256r1 precompile builder + WebAuthn precompile message
|
|
5
|
+
* assembler.
|
|
6
|
+
*
|
|
7
|
+
* Place a secp256r1 verify instruction IMMEDIATELY before any vault
|
|
8
|
+
* instruction that takes a passkey-signed op (set_swig, register_session_key,
|
|
9
|
+
* revoke_session_key, request_withdrawal, finalize_withdrawal, force_release,
|
|
10
|
+
* rotate_passkey, prove_passkey). The vault program reads
|
|
11
|
+
* SYSVAR_INSTRUCTIONS to introspect the sibling and rejects unless it
|
|
12
|
+
* verifies.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
declare const SIGNATURE_OFFSETS_SERIALIZED_SIZE = 14;
|
|
16
|
+
declare const SIGNATURE_SERIALIZED_SIZE = 64;
|
|
17
|
+
declare const COMPRESSED_PUBKEY_SERIALIZED_SIZE = 33;
|
|
18
|
+
declare const PRECOMPILE_DATA_START = 2;
|
|
19
|
+
declare function buildSecp256r1VerifyInstruction(publicKey: Uint8Array, // 33-byte compressed P-256
|
|
20
|
+
signature: Uint8Array, // 64-byte (r||s)
|
|
21
|
+
message: Uint8Array): TransactionInstruction;
|
|
22
|
+
/**
|
|
23
|
+
* Build the bytes the precompile verifies against the WebAuthn signature:
|
|
24
|
+
* authenticatorData || SHA-256(clientDataJSON)
|
|
25
|
+
*
|
|
26
|
+
* Works in Node (via `node:crypto`) and the browser (via SubtleCrypto).
|
|
27
|
+
*/
|
|
28
|
+
declare function buildPrecompileMessage(clientDataJSON: Uint8Array, authenticatorData: Uint8Array): Promise<Uint8Array>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Solana Ed25519 sigverify precompile builder.
|
|
32
|
+
*
|
|
33
|
+
* Layout matches solana-sdk/sdk/src/ed25519_instruction.rs byte-for-byte:
|
|
34
|
+
* u8 numSigs + u8 padding + 14-byte SignatureOffsets +
|
|
35
|
+
* contiguous pubkey(32) || signature(64) || message
|
|
36
|
+
*
|
|
37
|
+
* Place this BEFORE vault::settle_tab_voucher (or any future ix needing
|
|
38
|
+
* session-key verification) in the same tx.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
declare function buildEd25519VerifyInstruction(pubkey: Uint8Array, // 32 bytes
|
|
42
|
+
signature: Uint8Array, // 64 bytes
|
|
43
|
+
message: Uint8Array): TransactionInstruction;
|
|
44
|
+
|
|
45
|
+
export { COMPRESSED_PUBKEY_SERIALIZED_SIZE, PRECOMPILE_DATA_START, SIGNATURE_OFFSETS_SERIALIZED_SIZE, SIGNATURE_SERIALIZED_SIZE, buildEd25519VerifyInstruction, buildPrecompileMessage, buildSecp256r1VerifyInstruction };
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// src/precompile/secp256r1.ts
|
|
2
|
+
import { TransactionInstruction } from "@solana/web3.js";
|
|
3
|
+
|
|
4
|
+
// src/constants/index.ts
|
|
5
|
+
import { PublicKey } from "@solana/web3.js";
|
|
6
|
+
var DEXTER_VAULT_PROGRAM_ID = new PublicKey(
|
|
7
|
+
"Hg3wRaydFtJhYrdvYrKECacpJYDsC9Px7yKmpncj2fhc"
|
|
8
|
+
);
|
|
9
|
+
var SWIG_PROGRAM_ID = new PublicKey(
|
|
10
|
+
"swigypWHEksbC64pWKwah1WTeh9JXwx8H1rJHLdbQMB"
|
|
11
|
+
);
|
|
12
|
+
var SECP256R1_PROGRAM_ID = new PublicKey(
|
|
13
|
+
"Secp256r1SigVerify1111111111111111111111111"
|
|
14
|
+
);
|
|
15
|
+
var ED25519_PROGRAM_ID = new PublicKey(
|
|
16
|
+
"Ed25519SigVerify111111111111111111111111111"
|
|
17
|
+
);
|
|
18
|
+
var INSTRUCTIONS_SYSVAR_ID = new PublicKey(
|
|
19
|
+
"Sysvar1nstructions1111111111111111111111111"
|
|
20
|
+
);
|
|
21
|
+
var VAULT_SEED_PREFIX = Buffer.from("vault");
|
|
22
|
+
var DISCRIMINATORS = Object.freeze({
|
|
23
|
+
initialize_vault: Uint8Array.from([48, 191, 163, 44, 71, 129, 63, 164]),
|
|
24
|
+
set_swig: Uint8Array.from([253, 229, 89, 206, 192, 118, 137, 165]),
|
|
25
|
+
settle_voucher: Uint8Array.from([144, 176, 128, 220, 156, 79, 41, 54]),
|
|
26
|
+
request_withdrawal: Uint8Array.from([251, 85, 121, 205, 56, 201, 12, 177]),
|
|
27
|
+
finalize_withdrawal: Uint8Array.from([178, 87, 206, 68, 201, 186, 164, 232]),
|
|
28
|
+
force_release: Uint8Array.from([122, 190, 243, 252, 54, 202, 208, 234]),
|
|
29
|
+
rotate_passkey: Uint8Array.from([28, 134, 49, 89, 196, 34, 58, 174]),
|
|
30
|
+
rotate_dexter_authority: Uint8Array.from([145, 60, 4, 119, 180, 205, 236, 134]),
|
|
31
|
+
prove_passkey: Uint8Array.from([35, 175, 41, 143, 201, 118, 49, 184]),
|
|
32
|
+
settle_tab_voucher: Uint8Array.from([173, 22, 98, 31, 110, 129, 59, 161]),
|
|
33
|
+
register_session_key: Uint8Array.from([69, 94, 60, 44, 49, 199, 183, 233]),
|
|
34
|
+
revoke_session_key: Uint8Array.from([81, 192, 32, 110, 104, 116, 144, 151])
|
|
35
|
+
});
|
|
36
|
+
var OTS_SESSION_REGISTER_V1_DOMAIN = (() => {
|
|
37
|
+
const buf = new Uint8Array(32);
|
|
38
|
+
buf.set(new TextEncoder().encode("OTS_SESSION_REGISTER_V1"), 0);
|
|
39
|
+
return buf;
|
|
40
|
+
})();
|
|
41
|
+
var OTS_SESSION_REVOKE_V1_DOMAIN = (() => {
|
|
42
|
+
const buf = new Uint8Array(32);
|
|
43
|
+
buf.set(new TextEncoder().encode("OTS_SESSION_REVOKE_V1"), 0);
|
|
44
|
+
return buf;
|
|
45
|
+
})();
|
|
46
|
+
|
|
47
|
+
// src/precompile/secp256r1.ts
|
|
48
|
+
var SIGNATURE_OFFSETS_SERIALIZED_SIZE = 14;
|
|
49
|
+
var SIGNATURE_SERIALIZED_SIZE = 64;
|
|
50
|
+
var COMPRESSED_PUBKEY_SERIALIZED_SIZE = 33;
|
|
51
|
+
var PRECOMPILE_DATA_START = 2;
|
|
52
|
+
function buildSecp256r1VerifyInstruction(publicKey, signature, message) {
|
|
53
|
+
if (publicKey.length !== COMPRESSED_PUBKEY_SERIALIZED_SIZE) {
|
|
54
|
+
throw new Error(`expected ${COMPRESSED_PUBKEY_SERIALIZED_SIZE}-byte pubkey`);
|
|
55
|
+
}
|
|
56
|
+
if (signature.length !== SIGNATURE_SERIALIZED_SIZE) {
|
|
57
|
+
throw new Error(`expected ${SIGNATURE_SERIALIZED_SIZE}-byte signature`);
|
|
58
|
+
}
|
|
59
|
+
const signatureOffset = PRECOMPILE_DATA_START + SIGNATURE_OFFSETS_SERIALIZED_SIZE;
|
|
60
|
+
const publicKeyOffset = signatureOffset + SIGNATURE_SERIALIZED_SIZE;
|
|
61
|
+
const messageOffset = publicKeyOffset + COMPRESSED_PUBKEY_SERIALIZED_SIZE;
|
|
62
|
+
const messageSize = message.length;
|
|
63
|
+
const totalLen = messageOffset + messageSize;
|
|
64
|
+
const data = Buffer.alloc(totalLen);
|
|
65
|
+
data[0] = 1;
|
|
66
|
+
data[1] = 0;
|
|
67
|
+
data.writeUInt16LE(signatureOffset, PRECOMPILE_DATA_START + 0);
|
|
68
|
+
data.writeUInt16LE(65535, PRECOMPILE_DATA_START + 2);
|
|
69
|
+
data.writeUInt16LE(publicKeyOffset, PRECOMPILE_DATA_START + 4);
|
|
70
|
+
data.writeUInt16LE(65535, PRECOMPILE_DATA_START + 6);
|
|
71
|
+
data.writeUInt16LE(messageOffset, PRECOMPILE_DATA_START + 8);
|
|
72
|
+
data.writeUInt16LE(messageSize, PRECOMPILE_DATA_START + 10);
|
|
73
|
+
data.writeUInt16LE(65535, PRECOMPILE_DATA_START + 12);
|
|
74
|
+
Buffer.from(signature).copy(data, signatureOffset);
|
|
75
|
+
Buffer.from(publicKey).copy(data, publicKeyOffset);
|
|
76
|
+
Buffer.from(message).copy(data, messageOffset);
|
|
77
|
+
return new TransactionInstruction({
|
|
78
|
+
keys: [],
|
|
79
|
+
programId: SECP256R1_PROGRAM_ID,
|
|
80
|
+
data
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
async function buildPrecompileMessage(clientDataJSON, authenticatorData) {
|
|
84
|
+
const subtle = globalThis.crypto?.subtle;
|
|
85
|
+
let clientDataHash;
|
|
86
|
+
if (subtle) {
|
|
87
|
+
const buf = await subtle.digest("SHA-256", clientDataJSON);
|
|
88
|
+
clientDataHash = new Uint8Array(buf);
|
|
89
|
+
} else {
|
|
90
|
+
const { createHash } = await import("crypto");
|
|
91
|
+
clientDataHash = createHash("sha256").update(clientDataJSON).digest();
|
|
92
|
+
}
|
|
93
|
+
const out = new Uint8Array(authenticatorData.length + 32);
|
|
94
|
+
out.set(authenticatorData, 0);
|
|
95
|
+
out.set(clientDataHash, authenticatorData.length);
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/precompile/ed25519.ts
|
|
100
|
+
import { TransactionInstruction as TransactionInstruction2 } from "@solana/web3.js";
|
|
101
|
+
function buildEd25519VerifyInstruction(pubkey, signature, message) {
|
|
102
|
+
if (pubkey.length !== 32) throw new Error("pubkey must be 32 bytes");
|
|
103
|
+
if (signature.length !== 64) throw new Error("signature must be 64 bytes");
|
|
104
|
+
const NUM_SIG = 1;
|
|
105
|
+
const PADDING = 0;
|
|
106
|
+
const HEADER_LEN = 2;
|
|
107
|
+
const OFFSETS_LEN = 14;
|
|
108
|
+
const DATA_START = HEADER_LEN + OFFSETS_LEN;
|
|
109
|
+
const data = Buffer.alloc(DATA_START + pubkey.length + signature.length + message.length);
|
|
110
|
+
let off = 0;
|
|
111
|
+
data.writeUInt8(NUM_SIG, off);
|
|
112
|
+
off += 1;
|
|
113
|
+
data.writeUInt8(PADDING, off);
|
|
114
|
+
off += 1;
|
|
115
|
+
const pubkeyOffset = DATA_START;
|
|
116
|
+
const signatureOffset = pubkeyOffset + pubkey.length;
|
|
117
|
+
const messageOffset = signatureOffset + signature.length;
|
|
118
|
+
data.writeUInt16LE(signatureOffset, off);
|
|
119
|
+
off += 2;
|
|
120
|
+
data.writeUInt16LE(65535, off);
|
|
121
|
+
off += 2;
|
|
122
|
+
data.writeUInt16LE(pubkeyOffset, off);
|
|
123
|
+
off += 2;
|
|
124
|
+
data.writeUInt16LE(65535, off);
|
|
125
|
+
off += 2;
|
|
126
|
+
data.writeUInt16LE(messageOffset, off);
|
|
127
|
+
off += 2;
|
|
128
|
+
data.writeUInt16LE(message.length, off);
|
|
129
|
+
off += 2;
|
|
130
|
+
data.writeUInt16LE(65535, off);
|
|
131
|
+
off += 2;
|
|
132
|
+
Buffer.from(pubkey).copy(data, pubkeyOffset);
|
|
133
|
+
Buffer.from(signature).copy(data, signatureOffset);
|
|
134
|
+
Buffer.from(message).copy(data, messageOffset);
|
|
135
|
+
return new TransactionInstruction2({
|
|
136
|
+
programId: ED25519_PROGRAM_ID,
|
|
137
|
+
keys: [],
|
|
138
|
+
data
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
export {
|
|
142
|
+
COMPRESSED_PUBKEY_SERIALIZED_SIZE,
|
|
143
|
+
PRECOMPILE_DATA_START,
|
|
144
|
+
SIGNATURE_OFFSETS_SERIALIZED_SIZE,
|
|
145
|
+
SIGNATURE_SERIALIZED_SIZE,
|
|
146
|
+
buildEd25519VerifyInstruction,
|
|
147
|
+
buildPrecompileMessage,
|
|
148
|
+
buildSecp256r1VerifyInstruction
|
|
149
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/reader/index.ts
|
|
21
|
+
var reader_exports = {};
|
|
22
|
+
__export(reader_exports, {
|
|
23
|
+
readVaultFull: () => readVaultFull,
|
|
24
|
+
readVaultOnchain: () => readVaultOnchain
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(reader_exports);
|
|
27
|
+
|
|
28
|
+
// src/reader/accountReader.ts
|
|
29
|
+
var import_web3 = require("@solana/web3.js");
|
|
30
|
+
var VERSION_OFFSET = 8;
|
|
31
|
+
var SWIG_ADDRESS_OFFSET = 43;
|
|
32
|
+
var PENDING_VOUCHER_COUNT_OFFSET = 79;
|
|
33
|
+
var PENDING_WITHDRAWAL_TAG_OFFSET = 83;
|
|
34
|
+
var PENDING_WITHDRAWAL_AMOUNT_OFFSET = 84;
|
|
35
|
+
var PENDING_WITHDRAWAL_DESTINATION_OFFSET = 92;
|
|
36
|
+
var PENDING_WITHDRAWAL_REQUESTED_AT_OFFSET = 124;
|
|
37
|
+
var PENDING_WITHDRAWAL_BODY_LEN = 48;
|
|
38
|
+
var PENDING_WITHDRAWAL_BODY_START = PENDING_WITHDRAWAL_TAG_OFFSET + 1;
|
|
39
|
+
var IDENTITY_CLAIM_LEN = 32;
|
|
40
|
+
var PUBKEY_LEN = 32;
|
|
41
|
+
var ACTIVE_SESSION_BODY_LEN = 92;
|
|
42
|
+
var EMPTY_FULL = {
|
|
43
|
+
exists: false,
|
|
44
|
+
version: 0,
|
|
45
|
+
swigAddress: null,
|
|
46
|
+
dexterAuthority: null,
|
|
47
|
+
pendingVoucherCount: 0,
|
|
48
|
+
activeSession: null
|
|
49
|
+
};
|
|
50
|
+
async function readVaultOnchain(conn, vaultPda) {
|
|
51
|
+
const account = await conn.getAccountInfo(vaultPda, "confirmed");
|
|
52
|
+
if (!account) {
|
|
53
|
+
return { exists: false, pendingVoucherCount: 0, pendingWithdrawal: null };
|
|
54
|
+
}
|
|
55
|
+
const data = account.data;
|
|
56
|
+
const pendingVoucherCount = data.readUInt32LE(PENDING_VOUCHER_COUNT_OFFSET);
|
|
57
|
+
let pendingWithdrawal = null;
|
|
58
|
+
if (data[PENDING_WITHDRAWAL_TAG_OFFSET] === 1) {
|
|
59
|
+
pendingWithdrawal = {
|
|
60
|
+
amount: data.readBigUInt64LE(PENDING_WITHDRAWAL_AMOUNT_OFFSET).toString(),
|
|
61
|
+
destination: new import_web3.PublicKey(
|
|
62
|
+
data.subarray(
|
|
63
|
+
PENDING_WITHDRAWAL_DESTINATION_OFFSET,
|
|
64
|
+
PENDING_WITHDRAWAL_DESTINATION_OFFSET + 32
|
|
65
|
+
)
|
|
66
|
+
).toBase58(),
|
|
67
|
+
requestedAt: Number(data.readBigInt64LE(PENDING_WITHDRAWAL_REQUESTED_AT_OFFSET))
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return { exists: true, pendingVoucherCount, pendingWithdrawal };
|
|
71
|
+
}
|
|
72
|
+
async function readVaultFull(conn, vaultPda) {
|
|
73
|
+
const account = await conn.getAccountInfo(vaultPda, "confirmed");
|
|
74
|
+
if (!account) return EMPTY_FULL;
|
|
75
|
+
const data = account.data;
|
|
76
|
+
if (data.length < SWIG_ADDRESS_OFFSET + PUBKEY_LEN) return EMPTY_FULL;
|
|
77
|
+
const version = data.readUInt8(VERSION_OFFSET);
|
|
78
|
+
const swigAddress = new import_web3.PublicKey(
|
|
79
|
+
data.subarray(SWIG_ADDRESS_OFFSET, SWIG_ADDRESS_OFFSET + PUBKEY_LEN)
|
|
80
|
+
).toBase58();
|
|
81
|
+
const pendingVoucherCount = data.readUInt32LE(PENDING_VOUCHER_COUNT_OFFSET);
|
|
82
|
+
const withdrawalTag = data[PENDING_WITHDRAWAL_TAG_OFFSET];
|
|
83
|
+
const afterWithdrawal = PENDING_WITHDRAWAL_BODY_START + (withdrawalTag === 1 ? PENDING_WITHDRAWAL_BODY_LEN : 0);
|
|
84
|
+
const dexterAuthorityOffset = afterWithdrawal + IDENTITY_CLAIM_LEN;
|
|
85
|
+
if (data.length < dexterAuthorityOffset + PUBKEY_LEN) {
|
|
86
|
+
return { ...EMPTY_FULL, exists: true, version, swigAddress, pendingVoucherCount };
|
|
87
|
+
}
|
|
88
|
+
const dexterAuthority = new import_web3.PublicKey(
|
|
89
|
+
data.subarray(dexterAuthorityOffset, dexterAuthorityOffset + PUBKEY_LEN)
|
|
90
|
+
).toBase58();
|
|
91
|
+
const activeSessionTagOffset = dexterAuthorityOffset + PUBKEY_LEN;
|
|
92
|
+
let activeSession = null;
|
|
93
|
+
if (data.length > activeSessionTagOffset && data[activeSessionTagOffset] === 1) {
|
|
94
|
+
const bodyStart = activeSessionTagOffset + 1;
|
|
95
|
+
if (data.length >= bodyStart + ACTIVE_SESSION_BODY_LEN) {
|
|
96
|
+
activeSession = {
|
|
97
|
+
sessionPubkey: new Uint8Array(data.subarray(bodyStart, bodyStart + 32)),
|
|
98
|
+
maxAmount: data.readBigUInt64LE(bodyStart + 32),
|
|
99
|
+
expiresAt: Number(data.readBigInt64LE(bodyStart + 40)),
|
|
100
|
+
allowedCounterparty: new import_web3.PublicKey(
|
|
101
|
+
data.subarray(bodyStart + 48, bodyStart + 80)
|
|
102
|
+
).toBase58(),
|
|
103
|
+
nonce: data.readUInt32LE(bodyStart + 80),
|
|
104
|
+
spent: data.readBigUInt64LE(bodyStart + 84)
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
exists: true,
|
|
110
|
+
version,
|
|
111
|
+
swigAddress,
|
|
112
|
+
dexterAuthority,
|
|
113
|
+
pendingVoucherCount,
|
|
114
|
+
activeSession
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
118
|
+
0 && (module.exports = {
|
|
119
|
+
readVaultFull,
|
|
120
|
+
readVaultOnchain
|
|
121
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Connection, PublicKey } from '@solana/web3.js';
|
|
2
|
+
import { VaultStateFull, VaultOnchainState } from '../types.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Vault account decoders (Anchor v2 layout).
|
|
6
|
+
*
|
|
7
|
+
* Two entry points sharing one byte layout:
|
|
8
|
+
* - readVaultOnchain → slim {exists, pendingVoucherCount, pendingWithdrawal}
|
|
9
|
+
* - readVaultFull → full incl. swigAddress + dexterAuthority + activeSession
|
|
10
|
+
*
|
|
11
|
+
* v2 layout (programs/dexter-vault/src/state.rs::Vault):
|
|
12
|
+
* 0 8 discriminator
|
|
13
|
+
* 8 1 version u8 (= 2)
|
|
14
|
+
* 9 1 bump u8
|
|
15
|
+
* 10 33 passkey_pubkey
|
|
16
|
+
* 43 32 swig_address
|
|
17
|
+
* 75 4 cooling_off_seconds u32
|
|
18
|
+
* 79 4 pending_voucher_count u32
|
|
19
|
+
* 83 1 pending_withdrawal Option tag
|
|
20
|
+
* 48 pending_withdrawal body (if tag==1):
|
|
21
|
+
* 8 amount u64
|
|
22
|
+
* 32 destination
|
|
23
|
+
* 8 requested_at i64
|
|
24
|
+
* 132/84 32 identity_claim (132 if withdrawal present, else 84)
|
|
25
|
+
* 164/116 32 dexter_authority (164 if withdrawal present, else 116)
|
|
26
|
+
* 196/148 1 active_session Option tag
|
|
27
|
+
* 92 session body (if tag==1):
|
|
28
|
+
* 32 session_pubkey
|
|
29
|
+
* 8 max_amount u64
|
|
30
|
+
* 8 expires_at i64
|
|
31
|
+
* 32 allowed_counterparty
|
|
32
|
+
* 4 nonce u32
|
|
33
|
+
* 8 spent u64
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/** Slim read — the shape dexter-api's existing /status routes return. */
|
|
37
|
+
declare function readVaultOnchain(conn: Connection, vaultPda: PublicKey): Promise<VaultOnchainState>;
|
|
38
|
+
/** Full read — adds swigAddress, dexterAuthority, activeSession. The /tab/settle path. */
|
|
39
|
+
declare function readVaultFull(conn: Connection, vaultPda: PublicKey): Promise<VaultStateFull>;
|
|
40
|
+
|
|
41
|
+
export { readVaultFull, readVaultOnchain };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Connection, PublicKey } from '@solana/web3.js';
|
|
2
|
+
import { VaultStateFull, VaultOnchainState } from '../types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Vault account decoders (Anchor v2 layout).
|
|
6
|
+
*
|
|
7
|
+
* Two entry points sharing one byte layout:
|
|
8
|
+
* - readVaultOnchain → slim {exists, pendingVoucherCount, pendingWithdrawal}
|
|
9
|
+
* - readVaultFull → full incl. swigAddress + dexterAuthority + activeSession
|
|
10
|
+
*
|
|
11
|
+
* v2 layout (programs/dexter-vault/src/state.rs::Vault):
|
|
12
|
+
* 0 8 discriminator
|
|
13
|
+
* 8 1 version u8 (= 2)
|
|
14
|
+
* 9 1 bump u8
|
|
15
|
+
* 10 33 passkey_pubkey
|
|
16
|
+
* 43 32 swig_address
|
|
17
|
+
* 75 4 cooling_off_seconds u32
|
|
18
|
+
* 79 4 pending_voucher_count u32
|
|
19
|
+
* 83 1 pending_withdrawal Option tag
|
|
20
|
+
* 48 pending_withdrawal body (if tag==1):
|
|
21
|
+
* 8 amount u64
|
|
22
|
+
* 32 destination
|
|
23
|
+
* 8 requested_at i64
|
|
24
|
+
* 132/84 32 identity_claim (132 if withdrawal present, else 84)
|
|
25
|
+
* 164/116 32 dexter_authority (164 if withdrawal present, else 116)
|
|
26
|
+
* 196/148 1 active_session Option tag
|
|
27
|
+
* 92 session body (if tag==1):
|
|
28
|
+
* 32 session_pubkey
|
|
29
|
+
* 8 max_amount u64
|
|
30
|
+
* 8 expires_at i64
|
|
31
|
+
* 32 allowed_counterparty
|
|
32
|
+
* 4 nonce u32
|
|
33
|
+
* 8 spent u64
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/** Slim read — the shape dexter-api's existing /status routes return. */
|
|
37
|
+
declare function readVaultOnchain(conn: Connection, vaultPda: PublicKey): Promise<VaultOnchainState>;
|
|
38
|
+
/** Full read — adds swigAddress, dexterAuthority, activeSession. The /tab/settle path. */
|
|
39
|
+
declare function readVaultFull(conn: Connection, vaultPda: PublicKey): Promise<VaultStateFull>;
|
|
40
|
+
|
|
41
|
+
export { readVaultFull, readVaultOnchain };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// src/reader/accountReader.ts
|
|
2
|
+
import { PublicKey } from "@solana/web3.js";
|
|
3
|
+
var VERSION_OFFSET = 8;
|
|
4
|
+
var SWIG_ADDRESS_OFFSET = 43;
|
|
5
|
+
var PENDING_VOUCHER_COUNT_OFFSET = 79;
|
|
6
|
+
var PENDING_WITHDRAWAL_TAG_OFFSET = 83;
|
|
7
|
+
var PENDING_WITHDRAWAL_AMOUNT_OFFSET = 84;
|
|
8
|
+
var PENDING_WITHDRAWAL_DESTINATION_OFFSET = 92;
|
|
9
|
+
var PENDING_WITHDRAWAL_REQUESTED_AT_OFFSET = 124;
|
|
10
|
+
var PENDING_WITHDRAWAL_BODY_LEN = 48;
|
|
11
|
+
var PENDING_WITHDRAWAL_BODY_START = PENDING_WITHDRAWAL_TAG_OFFSET + 1;
|
|
12
|
+
var IDENTITY_CLAIM_LEN = 32;
|
|
13
|
+
var PUBKEY_LEN = 32;
|
|
14
|
+
var ACTIVE_SESSION_BODY_LEN = 92;
|
|
15
|
+
var EMPTY_FULL = {
|
|
16
|
+
exists: false,
|
|
17
|
+
version: 0,
|
|
18
|
+
swigAddress: null,
|
|
19
|
+
dexterAuthority: null,
|
|
20
|
+
pendingVoucherCount: 0,
|
|
21
|
+
activeSession: null
|
|
22
|
+
};
|
|
23
|
+
async function readVaultOnchain(conn, vaultPda) {
|
|
24
|
+
const account = await conn.getAccountInfo(vaultPda, "confirmed");
|
|
25
|
+
if (!account) {
|
|
26
|
+
return { exists: false, pendingVoucherCount: 0, pendingWithdrawal: null };
|
|
27
|
+
}
|
|
28
|
+
const data = account.data;
|
|
29
|
+
const pendingVoucherCount = data.readUInt32LE(PENDING_VOUCHER_COUNT_OFFSET);
|
|
30
|
+
let pendingWithdrawal = null;
|
|
31
|
+
if (data[PENDING_WITHDRAWAL_TAG_OFFSET] === 1) {
|
|
32
|
+
pendingWithdrawal = {
|
|
33
|
+
amount: data.readBigUInt64LE(PENDING_WITHDRAWAL_AMOUNT_OFFSET).toString(),
|
|
34
|
+
destination: new PublicKey(
|
|
35
|
+
data.subarray(
|
|
36
|
+
PENDING_WITHDRAWAL_DESTINATION_OFFSET,
|
|
37
|
+
PENDING_WITHDRAWAL_DESTINATION_OFFSET + 32
|
|
38
|
+
)
|
|
39
|
+
).toBase58(),
|
|
40
|
+
requestedAt: Number(data.readBigInt64LE(PENDING_WITHDRAWAL_REQUESTED_AT_OFFSET))
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return { exists: true, pendingVoucherCount, pendingWithdrawal };
|
|
44
|
+
}
|
|
45
|
+
async function readVaultFull(conn, vaultPda) {
|
|
46
|
+
const account = await conn.getAccountInfo(vaultPda, "confirmed");
|
|
47
|
+
if (!account) return EMPTY_FULL;
|
|
48
|
+
const data = account.data;
|
|
49
|
+
if (data.length < SWIG_ADDRESS_OFFSET + PUBKEY_LEN) return EMPTY_FULL;
|
|
50
|
+
const version = data.readUInt8(VERSION_OFFSET);
|
|
51
|
+
const swigAddress = new PublicKey(
|
|
52
|
+
data.subarray(SWIG_ADDRESS_OFFSET, SWIG_ADDRESS_OFFSET + PUBKEY_LEN)
|
|
53
|
+
).toBase58();
|
|
54
|
+
const pendingVoucherCount = data.readUInt32LE(PENDING_VOUCHER_COUNT_OFFSET);
|
|
55
|
+
const withdrawalTag = data[PENDING_WITHDRAWAL_TAG_OFFSET];
|
|
56
|
+
const afterWithdrawal = PENDING_WITHDRAWAL_BODY_START + (withdrawalTag === 1 ? PENDING_WITHDRAWAL_BODY_LEN : 0);
|
|
57
|
+
const dexterAuthorityOffset = afterWithdrawal + IDENTITY_CLAIM_LEN;
|
|
58
|
+
if (data.length < dexterAuthorityOffset + PUBKEY_LEN) {
|
|
59
|
+
return { ...EMPTY_FULL, exists: true, version, swigAddress, pendingVoucherCount };
|
|
60
|
+
}
|
|
61
|
+
const dexterAuthority = new PublicKey(
|
|
62
|
+
data.subarray(dexterAuthorityOffset, dexterAuthorityOffset + PUBKEY_LEN)
|
|
63
|
+
).toBase58();
|
|
64
|
+
const activeSessionTagOffset = dexterAuthorityOffset + PUBKEY_LEN;
|
|
65
|
+
let activeSession = null;
|
|
66
|
+
if (data.length > activeSessionTagOffset && data[activeSessionTagOffset] === 1) {
|
|
67
|
+
const bodyStart = activeSessionTagOffset + 1;
|
|
68
|
+
if (data.length >= bodyStart + ACTIVE_SESSION_BODY_LEN) {
|
|
69
|
+
activeSession = {
|
|
70
|
+
sessionPubkey: new Uint8Array(data.subarray(bodyStart, bodyStart + 32)),
|
|
71
|
+
maxAmount: data.readBigUInt64LE(bodyStart + 32),
|
|
72
|
+
expiresAt: Number(data.readBigInt64LE(bodyStart + 40)),
|
|
73
|
+
allowedCounterparty: new PublicKey(
|
|
74
|
+
data.subarray(bodyStart + 48, bodyStart + 80)
|
|
75
|
+
).toBase58(),
|
|
76
|
+
nonce: data.readUInt32LE(bodyStart + 80),
|
|
77
|
+
spent: data.readBigUInt64LE(bodyStart + 84)
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
exists: true,
|
|
83
|
+
version,
|
|
84
|
+
swigAddress,
|
|
85
|
+
dexterAuthority,
|
|
86
|
+
pendingVoucherCount,
|
|
87
|
+
activeSession
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
export {
|
|
91
|
+
readVaultFull,
|
|
92
|
+
readVaultOnchain
|
|
93
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/signers/node/index.ts
|
|
31
|
+
var node_exports = {};
|
|
32
|
+
__export(node_exports, {
|
|
33
|
+
NodeEd25519Signer: () => NodeEd25519Signer
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(node_exports);
|
|
36
|
+
var import_tweetnacl = __toESM(require("tweetnacl"), 1);
|
|
37
|
+
var NodeEd25519Signer = class {
|
|
38
|
+
keypair;
|
|
39
|
+
/**
|
|
40
|
+
* @param secretKey 32-byte seed OR 64-byte nacl secret-key buffer
|
|
41
|
+
* (seed || pubkey). Both shapes are accepted.
|
|
42
|
+
*/
|
|
43
|
+
constructor(secretKey) {
|
|
44
|
+
if (secretKey.length === 32) {
|
|
45
|
+
this.keypair = import_tweetnacl.default.sign.keyPair.fromSeed(secretKey);
|
|
46
|
+
} else if (secretKey.length === 64) {
|
|
47
|
+
this.keypair = import_tweetnacl.default.sign.keyPair.fromSecretKey(secretKey);
|
|
48
|
+
} else {
|
|
49
|
+
throw new Error(`NodeEd25519Signer: secretKey must be 32 or 64 bytes, got ${secretKey.length}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
get publicKey() {
|
|
53
|
+
return this.keypair.publicKey;
|
|
54
|
+
}
|
|
55
|
+
async sign(message) {
|
|
56
|
+
return import_tweetnacl.default.sign.detached(message, this.keypair.secretKey);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
60
|
+
0 && (module.exports = {
|
|
61
|
+
NodeEd25519Signer
|
|
62
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Ed25519Signer } from '../types.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Node Ed25519 signer — wraps a secret-key seed with tweetnacl.
|
|
5
|
+
*
|
|
6
|
+
* Used by dexter-api as its dexter-authority signer (the server-held
|
|
7
|
+
* session master) and by dexter-vault tests as a deterministic stand-in.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
declare class NodeEd25519Signer implements Ed25519Signer {
|
|
11
|
+
private readonly keypair;
|
|
12
|
+
/**
|
|
13
|
+
* @param secretKey 32-byte seed OR 64-byte nacl secret-key buffer
|
|
14
|
+
* (seed || pubkey). Both shapes are accepted.
|
|
15
|
+
*/
|
|
16
|
+
constructor(secretKey: Uint8Array);
|
|
17
|
+
get publicKey(): Uint8Array;
|
|
18
|
+
sign(message: Uint8Array): Promise<Uint8Array>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { NodeEd25519Signer };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Ed25519Signer } from '../types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Node Ed25519 signer — wraps a secret-key seed with tweetnacl.
|
|
5
|
+
*
|
|
6
|
+
* Used by dexter-api as its dexter-authority signer (the server-held
|
|
7
|
+
* session master) and by dexter-vault tests as a deterministic stand-in.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
declare class NodeEd25519Signer implements Ed25519Signer {
|
|
11
|
+
private readonly keypair;
|
|
12
|
+
/**
|
|
13
|
+
* @param secretKey 32-byte seed OR 64-byte nacl secret-key buffer
|
|
14
|
+
* (seed || pubkey). Both shapes are accepted.
|
|
15
|
+
*/
|
|
16
|
+
constructor(secretKey: Uint8Array);
|
|
17
|
+
get publicKey(): Uint8Array;
|
|
18
|
+
sign(message: Uint8Array): Promise<Uint8Array>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { NodeEd25519Signer };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// src/signers/node/index.ts
|
|
2
|
+
import nacl from "tweetnacl";
|
|
3
|
+
var NodeEd25519Signer = class {
|
|
4
|
+
keypair;
|
|
5
|
+
/**
|
|
6
|
+
* @param secretKey 32-byte seed OR 64-byte nacl secret-key buffer
|
|
7
|
+
* (seed || pubkey). Both shapes are accepted.
|
|
8
|
+
*/
|
|
9
|
+
constructor(secretKey) {
|
|
10
|
+
if (secretKey.length === 32) {
|
|
11
|
+
this.keypair = nacl.sign.keyPair.fromSeed(secretKey);
|
|
12
|
+
} else if (secretKey.length === 64) {
|
|
13
|
+
this.keypair = nacl.sign.keyPair.fromSecretKey(secretKey);
|
|
14
|
+
} else {
|
|
15
|
+
throw new Error(`NodeEd25519Signer: secretKey must be 32 or 64 bytes, got ${secretKey.length}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
get publicKey() {
|
|
19
|
+
return this.keypair.publicKey;
|
|
20
|
+
}
|
|
21
|
+
async sign(message) {
|
|
22
|
+
return nacl.sign.detached(message, this.keypair.secretKey);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
export {
|
|
26
|
+
NodeEd25519Signer
|
|
27
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
|
|
16
|
+
// src/signers/types.ts
|
|
17
|
+
var types_exports = {};
|
|
18
|
+
module.exports = __toCommonJS(types_exports);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signer abstractions.
|
|
3
|
+
*
|
|
4
|
+
* Ed25519Signer is shipped with a Node implementation in v0.1.
|
|
5
|
+
* PasskeySigner is shipped as INTERFACE ONLY in v0.1 — the WebAuthn
|
|
6
|
+
* implementation is tracked as task #235 ("@dexterai/vault v0.2 —
|
|
7
|
+
* BrowserPasskeySigner") and must lift from dexter-fe/app/lib/passkey.ts +
|
|
8
|
+
* dexter-fe/app/lib/passkey-anon.ts. Until v0.2 ships, dexter-fe stays on
|
|
9
|
+
* its hand-rolled passkey ceremony.
|
|
10
|
+
*/
|
|
11
|
+
interface Ed25519Signer {
|
|
12
|
+
/** 32-byte public key. */
|
|
13
|
+
readonly publicKey: Uint8Array;
|
|
14
|
+
/** Produce a 64-byte detached signature over `message`. */
|
|
15
|
+
sign(message: Uint8Array): Promise<Uint8Array>;
|
|
16
|
+
}
|
|
17
|
+
interface PasskeySigner {
|
|
18
|
+
/** Opaque credential ID handed back to the platform authenticator. */
|
|
19
|
+
readonly credentialId: Uint8Array;
|
|
20
|
+
/**
|
|
21
|
+
* Run the WebAuthn assertion ceremony over `challenge` and return the
|
|
22
|
+
* three things the precompile needs.
|
|
23
|
+
*
|
|
24
|
+
* Implementation: TASK #235. dexter-fe currently ships an equivalent
|
|
25
|
+
* function in app/lib/passkey.ts (`signOperation`) — the lift target.
|
|
26
|
+
*/
|
|
27
|
+
sign(challenge: Uint8Array): Promise<{
|
|
28
|
+
signature: Uint8Array;
|
|
29
|
+
clientDataJSON: Uint8Array;
|
|
30
|
+
authenticatorData: Uint8Array;
|
|
31
|
+
}>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type { Ed25519Signer, PasskeySigner };
|