@thru/thru-sdk 0.1.19 → 0.1.21
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/README.md +19 -0
- package/dist/{chunk-PH7P5EEU.js → chunk-SHMREHP5.js} +13 -9
- package/dist/chunk-SHMREHP5.js.map +1 -0
- package/dist/client.d.ts +2 -1
- package/dist/client.js +3 -2
- package/dist/client.js.map +1 -1
- package/dist/metafile-esm.json +1 -1
- package/dist/sdk.d.ts +3 -3
- package/dist/sdk.js +1 -1
- package/dist/{transactions-BzD9hYlc.d.ts → transactions-CLezIeXO.d.ts} +20 -18
- package/package.json +7 -3
- package/buf.gen.yaml +0 -12
- package/buf.lock +0 -9
- package/buf.yaml +0 -15
- package/dist/chunk-PH7P5EEU.js.map +0 -1
- package/proto/thru/common/v1/consensus.proto +0 -73
- package/proto/thru/common/v1/errors.proto +0 -65
- package/proto/thru/common/v1/filters.proto +0 -60
- package/proto/thru/common/v1/pagination.proto +0 -45
- package/proto/thru/core/v1/account.proto +0 -137
- package/proto/thru/core/v1/block.proto +0 -80
- package/proto/thru/core/v1/state.proto +0 -35
- package/proto/thru/core/v1/transaction.proto +0 -136
- package/proto/thru/core/v1/types.proto +0 -50
- package/proto/thru/services/v1/command_service.proto +0 -70
- package/proto/thru/services/v1/query_service.proto +0 -344
- package/proto/thru/services/v1/streaming_service.proto +0 -126
- package/thru-ts-client-sdk/__tests__/helpers/test-utils.ts +0 -228
- package/thru-ts-client-sdk/client.ts +0 -10
- package/thru-ts-client-sdk/core/__tests__/bound-client.test.ts +0 -354
- package/thru-ts-client-sdk/core/__tests__/client.test.ts +0 -53
- package/thru-ts-client-sdk/core/bound-client.ts +0 -156
- package/thru-ts-client-sdk/core/client.ts +0 -38
- package/thru-ts-client-sdk/defaults.ts +0 -26
- package/thru-ts-client-sdk/modules/__tests__/accounts.test.ts +0 -406
- package/thru-ts-client-sdk/modules/__tests__/blocks.test.ts +0 -199
- package/thru-ts-client-sdk/modules/__tests__/events.test.ts +0 -74
- package/thru-ts-client-sdk/modules/__tests__/height.test.ts +0 -39
- package/thru-ts-client-sdk/modules/__tests__/helpers.test.ts +0 -288
- package/thru-ts-client-sdk/modules/__tests__/keys.test.ts +0 -55
- package/thru-ts-client-sdk/modules/__tests__/proofs.test.ts +0 -119
- package/thru-ts-client-sdk/modules/__tests__/streaming.test.ts +0 -152
- package/thru-ts-client-sdk/modules/__tests__/transactions.test.ts +0 -730
- package/thru-ts-client-sdk/modules/__tests__/version.test.ts +0 -40
- package/thru-ts-client-sdk/modules/accounts.ts +0 -141
- package/thru-ts-client-sdk/modules/blocks.ts +0 -75
- package/thru-ts-client-sdk/modules/events.ts +0 -20
- package/thru-ts-client-sdk/modules/height.ts +0 -9
- package/thru-ts-client-sdk/modules/helpers.ts +0 -131
- package/thru-ts-client-sdk/modules/keys.ts +0 -29
- package/thru-ts-client-sdk/modules/proofs.ts +0 -20
- package/thru-ts-client-sdk/modules/streaming.ts +0 -133
- package/thru-ts-client-sdk/modules/transactions.ts +0 -374
- package/thru-ts-client-sdk/modules/version.ts +0 -10
- package/thru-ts-client-sdk/proto/buf/validate/validate_pb.ts +0 -4761
- package/thru-ts-client-sdk/proto/google/api/annotations_pb.ts +0 -39
- package/thru-ts-client-sdk/proto/google/api/client_pb.ts +0 -953
- package/thru-ts-client-sdk/proto/google/api/field_behavior_pb.ts +0 -157
- package/thru-ts-client-sdk/proto/google/api/http_pb.ts +0 -474
- package/thru-ts-client-sdk/proto/google/api/launch_stage_pb.ts +0 -118
- package/thru-ts-client-sdk/proto/thru/common/v1/consensus_pb.ts +0 -162
- package/thru-ts-client-sdk/proto/thru/common/v1/errors_pb.ts +0 -129
- package/thru-ts-client-sdk/proto/thru/common/v1/filters_pb.ts +0 -129
- package/thru-ts-client-sdk/proto/thru/common/v1/pagination_pb.ts +0 -79
- package/thru-ts-client-sdk/proto/thru/core/v1/account_pb.ts +0 -359
- package/thru-ts-client-sdk/proto/thru/core/v1/block_pb.ts +0 -259
- package/thru-ts-client-sdk/proto/thru/core/v1/state_pb.ts +0 -103
- package/thru-ts-client-sdk/proto/thru/core/v1/transaction_pb.ts +0 -528
- package/thru-ts-client-sdk/proto/thru/core/v1/types_pb.ts +0 -100
- package/thru-ts-client-sdk/proto/thru/services/v1/command_service_pb.ts +0 -146
- package/thru-ts-client-sdk/proto/thru/services/v1/query_service_pb.ts +0 -819
- package/thru-ts-client-sdk/proto/thru/services/v1/streaming_service_pb.ts +0 -390
- package/thru-ts-client-sdk/sdk.ts +0 -42
- package/thru-ts-client-sdk/test-scripts/counter.ts +0 -469
- package/thru-ts-client-sdk/test-scripts/create-account.ts +0 -74
- package/thru-ts-client-sdk/test-scripts/get-height.ts +0 -52
- package/thru-ts-client-sdk/transactions/Transaction.ts +0 -240
- package/thru-ts-client-sdk/transactions/TransactionBuilder.ts +0 -53
- package/thru-ts-client-sdk/transactions/__tests__/TransactionBuilder.test.ts +0 -411
- package/thru-ts-client-sdk/transactions/__tests__/utils.test.ts +0 -214
- package/thru-ts-client-sdk/transactions/index.ts +0 -3
- package/thru-ts-client-sdk/transactions/types.ts +0 -74
- package/thru-ts-client-sdk/transactions/utils.ts +0 -132
- package/thru-ts-client-sdk/types/types.ts +0 -8
- package/thru-ts-client-sdk/utils/utils.ts +0 -27
- package/tsconfig.json +0 -9
- package/tsup.config.ts +0 -14
- package/vitest.config.ts +0 -31
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
import { signAsync } from "@noble/ed25519";
|
|
2
|
-
import type { AccountAddress, Bytes64, OptionalProofs, TransactionAccountsInput, TransactionHeaderInput } from "./types";
|
|
3
|
-
|
|
4
|
-
const DEFAULT_FLAGS = 0;
|
|
5
|
-
const TRANSACTION_VERSION = 1;
|
|
6
|
-
const SIGNATURE_LENGTH = 64;
|
|
7
|
-
const PUBKEY_LENGTH = 32;
|
|
8
|
-
const HEADER_SIZE = 176;
|
|
9
|
-
const SIGNATURE_PREFIX_SIZE = 64;
|
|
10
|
-
|
|
11
|
-
export class Transaction {
|
|
12
|
-
readonly feePayer: AccountAddress;
|
|
13
|
-
readonly program: AccountAddress;
|
|
14
|
-
|
|
15
|
-
readonly fee: bigint;
|
|
16
|
-
readonly nonce: bigint;
|
|
17
|
-
readonly startSlot: bigint;
|
|
18
|
-
readonly expiryAfter: number;
|
|
19
|
-
|
|
20
|
-
readonly requestedComputeUnits: number;
|
|
21
|
-
readonly requestedStateUnits: number;
|
|
22
|
-
readonly requestedMemoryUnits: number;
|
|
23
|
-
readonly flags: number;
|
|
24
|
-
|
|
25
|
-
readonly readWriteAccounts: AccountAddress[];
|
|
26
|
-
readonly readOnlyAccounts: AccountAddress[];
|
|
27
|
-
|
|
28
|
-
readonly instructionData?: Uint8Array;
|
|
29
|
-
readonly feePayerStateProof?: Uint8Array;
|
|
30
|
-
readonly feePayerAccountMetaRaw?: Uint8Array;
|
|
31
|
-
|
|
32
|
-
private signature?: Bytes64;
|
|
33
|
-
|
|
34
|
-
constructor(params: {
|
|
35
|
-
feePayer: AccountAddress;
|
|
36
|
-
program: AccountAddress;
|
|
37
|
-
header: TransactionHeaderInput;
|
|
38
|
-
accounts?: TransactionAccountsInput;
|
|
39
|
-
instructionData?: Uint8Array;
|
|
40
|
-
proofs?: OptionalProofs;
|
|
41
|
-
}) {
|
|
42
|
-
this.feePayer = copyKey(params.feePayer);
|
|
43
|
-
this.program = copyKey(params.program);
|
|
44
|
-
|
|
45
|
-
this.fee = params.header.fee;
|
|
46
|
-
this.nonce = params.header.nonce;
|
|
47
|
-
this.startSlot = params.header.startSlot;
|
|
48
|
-
this.expiryAfter = params.header.expiryAfter ?? 0;
|
|
49
|
-
|
|
50
|
-
this.requestedComputeUnits = params.header.computeUnits ?? 0;
|
|
51
|
-
this.requestedStateUnits = params.header.stateUnits ?? 0;
|
|
52
|
-
this.requestedMemoryUnits = params.header.memoryUnits ?? 0;
|
|
53
|
-
this.flags = params.header.flags ?? DEFAULT_FLAGS;
|
|
54
|
-
|
|
55
|
-
this.readWriteAccounts = params.accounts?.readWriteAccounts
|
|
56
|
-
? params.accounts.readWriteAccounts.map(copyKey)
|
|
57
|
-
: [];
|
|
58
|
-
this.readOnlyAccounts = params.accounts?.readOnlyAccounts
|
|
59
|
-
? params.accounts.readOnlyAccounts.map(copyKey)
|
|
60
|
-
: [];
|
|
61
|
-
|
|
62
|
-
this.instructionData = params.instructionData ? new Uint8Array(params.instructionData) : undefined;
|
|
63
|
-
this.feePayerStateProof = params.proofs?.feePayerStateProof
|
|
64
|
-
? new Uint8Array(params.proofs.feePayerStateProof)
|
|
65
|
-
: undefined;
|
|
66
|
-
this.feePayerAccountMetaRaw = params.proofs?.feePayerAccountMetaRaw
|
|
67
|
-
? new Uint8Array(params.proofs.feePayerAccountMetaRaw)
|
|
68
|
-
: undefined;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
getSignature(): Bytes64 | undefined {
|
|
72
|
-
return this.signature ? new Uint8Array(this.signature) : undefined;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
setSignature(signature: Bytes64): void {
|
|
76
|
-
if (signature.length !== SIGNATURE_LENGTH) {
|
|
77
|
-
throw new Error(`Signature must contain ${SIGNATURE_LENGTH} bytes`);
|
|
78
|
-
}
|
|
79
|
-
this.signature = new Uint8Array(signature);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async sign(privateKey: Uint8Array): Promise<Bytes64> {
|
|
83
|
-
if (privateKey.length !== 32) {
|
|
84
|
-
throw new Error("Fee payer private key must contain 32 bytes");
|
|
85
|
-
}
|
|
86
|
-
const payload = this.toWireForSigning();
|
|
87
|
-
const signature = await signAsync(payload, privateKey);
|
|
88
|
-
if (signature.length !== SIGNATURE_LENGTH) {
|
|
89
|
-
throw new Error("ed25519 signing produced an invalid signature");
|
|
90
|
-
}
|
|
91
|
-
this.signature = signature;
|
|
92
|
-
return new Uint8Array(signature);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
toWireForSigning(): Uint8Array {
|
|
96
|
-
const header = this.createHeader(undefined);
|
|
97
|
-
const view = new Uint8Array(header);
|
|
98
|
-
return this.buildWirePayload(view.subarray(SIGNATURE_PREFIX_SIZE));
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
toWire(): Uint8Array {
|
|
102
|
-
const header = this.createHeader(this.signature);
|
|
103
|
-
const payload = this.buildWirePayload(new Uint8Array(header));
|
|
104
|
-
return payload;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
private createHeader(signature: Uint8Array | undefined): ArrayBufferLike {
|
|
108
|
-
const buffer = new ArrayBuffer(HEADER_SIZE);
|
|
109
|
-
const headerBytes = new Uint8Array(buffer);
|
|
110
|
-
const view = new DataView(buffer);
|
|
111
|
-
|
|
112
|
-
// Fee payer signature (64 bytes)
|
|
113
|
-
if (signature) {
|
|
114
|
-
if (signature.length !== SIGNATURE_LENGTH) {
|
|
115
|
-
throw new Error(`Signature must contain ${SIGNATURE_LENGTH} bytes`);
|
|
116
|
-
}
|
|
117
|
-
headerBytes.set(signature, 0);
|
|
118
|
-
} else {
|
|
119
|
-
headerBytes.fill(0, 0, SIGNATURE_LENGTH);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
let offset = SIGNATURE_PREFIX_SIZE;
|
|
123
|
-
view.setUint8(offset, TRANSACTION_VERSION);
|
|
124
|
-
offset += 1;
|
|
125
|
-
|
|
126
|
-
view.setUint8(offset, this.flags & 0xff);
|
|
127
|
-
offset += 1;
|
|
128
|
-
|
|
129
|
-
view.setUint16(offset, this.readWriteAccounts.length, true);
|
|
130
|
-
offset += 2;
|
|
131
|
-
|
|
132
|
-
view.setUint16(offset, this.readOnlyAccounts.length, true);
|
|
133
|
-
offset += 2;
|
|
134
|
-
|
|
135
|
-
const instructionDataLength = this.instructionData?.length ?? 0;
|
|
136
|
-
if (instructionDataLength > 0xffff) {
|
|
137
|
-
throw new Error("Instruction data exceeds maximum length (65535 bytes)");
|
|
138
|
-
}
|
|
139
|
-
view.setUint16(offset, instructionDataLength, true);
|
|
140
|
-
offset += 2;
|
|
141
|
-
|
|
142
|
-
view.setUint32(offset, ensureUint32(this.requestedComputeUnits), true);
|
|
143
|
-
offset += 4;
|
|
144
|
-
|
|
145
|
-
view.setUint16(offset, ensureUint16(this.requestedStateUnits), true);
|
|
146
|
-
offset += 2;
|
|
147
|
-
|
|
148
|
-
view.setUint16(offset, ensureUint16(this.requestedMemoryUnits), true);
|
|
149
|
-
offset += 2;
|
|
150
|
-
|
|
151
|
-
view.setBigUint64(offset, ensureBigUint64(this.fee), true);
|
|
152
|
-
offset += 8;
|
|
153
|
-
|
|
154
|
-
view.setBigUint64(offset, ensureBigUint64(this.nonce), true);
|
|
155
|
-
offset += 8;
|
|
156
|
-
|
|
157
|
-
view.setBigUint64(offset, ensureBigUint64(this.startSlot), true);
|
|
158
|
-
offset += 8;
|
|
159
|
-
|
|
160
|
-
view.setUint32(offset, ensureUint32(this.expiryAfter), true);
|
|
161
|
-
offset += 4;
|
|
162
|
-
|
|
163
|
-
// padding_0 (4 bytes) zeroed by default
|
|
164
|
-
offset += 4;
|
|
165
|
-
|
|
166
|
-
headerBytes.set(this.feePayer, offset);
|
|
167
|
-
offset += PUBKEY_LENGTH;
|
|
168
|
-
|
|
169
|
-
headerBytes.set(this.program, offset);
|
|
170
|
-
|
|
171
|
-
return buffer;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
private buildWirePayload(headerWithoutSignature: Uint8Array): Uint8Array {
|
|
175
|
-
const dynamicLength =
|
|
176
|
-
this.readWriteAccounts.length * PUBKEY_LENGTH +
|
|
177
|
-
this.readOnlyAccounts.length * PUBKEY_LENGTH +
|
|
178
|
-
(this.instructionData?.length ?? 0) +
|
|
179
|
-
(this.feePayerStateProof?.length ?? 0) +
|
|
180
|
-
(this.feePayerAccountMetaRaw?.length ?? 0);
|
|
181
|
-
|
|
182
|
-
const result = new Uint8Array(headerWithoutSignature.length + dynamicLength);
|
|
183
|
-
result.set(headerWithoutSignature, 0);
|
|
184
|
-
|
|
185
|
-
let offset = headerWithoutSignature.length;
|
|
186
|
-
offset = appendAccountList(result, offset, this.readWriteAccounts);
|
|
187
|
-
offset = appendAccountList(result, offset, this.readOnlyAccounts);
|
|
188
|
-
if (this.instructionData) {
|
|
189
|
-
result.set(this.instructionData, offset);
|
|
190
|
-
offset += this.instructionData.length;
|
|
191
|
-
}
|
|
192
|
-
if (this.feePayerStateProof) {
|
|
193
|
-
result.set(this.feePayerStateProof, offset);
|
|
194
|
-
offset += this.feePayerStateProof.length;
|
|
195
|
-
}
|
|
196
|
-
if (this.feePayerAccountMetaRaw) {
|
|
197
|
-
result.set(this.feePayerAccountMetaRaw, offset);
|
|
198
|
-
offset += this.feePayerAccountMetaRaw.length;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return result;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
function appendAccountList(target: Uint8Array, start: number, accounts: AccountAddress[]): number {
|
|
206
|
-
let offset = start;
|
|
207
|
-
for (const account of accounts) {
|
|
208
|
-
target.set(account, offset);
|
|
209
|
-
offset += PUBKEY_LENGTH;
|
|
210
|
-
}
|
|
211
|
-
return offset;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
function ensureUint16(value: number): number {
|
|
215
|
-
if (!Number.isInteger(value) || value < 0 || value > 0xffff) {
|
|
216
|
-
throw new Error("Value must fit within uint16 range");
|
|
217
|
-
}
|
|
218
|
-
return value;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function ensureUint32(value: number): number {
|
|
222
|
-
if (!Number.isInteger(value) || value < 0 || value > 0xffffffff) {
|
|
223
|
-
throw new Error("Value must fit within uint32 range");
|
|
224
|
-
}
|
|
225
|
-
return value;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function ensureBigUint64(value: bigint): bigint {
|
|
229
|
-
if (value < 0n || value > 0xffff_ffff_ffff_ffffn) {
|
|
230
|
-
throw new Error("Value must fit within uint64 range");
|
|
231
|
-
}
|
|
232
|
-
return value;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function copyKey(source: AccountAddress): AccountAddress {
|
|
236
|
-
if (source.length !== PUBKEY_LENGTH) {
|
|
237
|
-
throw new Error("Public keys must contain 32 bytes");
|
|
238
|
-
}
|
|
239
|
-
return new Uint8Array(source);
|
|
240
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { Transaction } from "./Transaction";
|
|
2
|
-
import type {
|
|
3
|
-
BuildTransactionParams,
|
|
4
|
-
SignedTransactionResult,
|
|
5
|
-
TransactionAccountsInput
|
|
6
|
-
} from "./types";
|
|
7
|
-
import { normalizeAccountList, parseInstructionData, resolveProgramIdentifier } from "./utils";
|
|
8
|
-
|
|
9
|
-
const FLAG_HAS_FEE_PAYER_PROOF = 1 << 0;
|
|
10
|
-
|
|
11
|
-
export class TransactionBuilder {
|
|
12
|
-
build(params: BuildTransactionParams): Transaction {
|
|
13
|
-
const program = resolveProgramIdentifier(params.program);
|
|
14
|
-
const accounts = this.normalizeAccounts(params.accounts);
|
|
15
|
-
const baseFlags = params.header.flags ?? 0;
|
|
16
|
-
const flags = params.proofs?.feePayerStateProof ? baseFlags | FLAG_HAS_FEE_PAYER_PROOF : baseFlags;
|
|
17
|
-
|
|
18
|
-
/* Parse instruction data from BytesLike */
|
|
19
|
-
const instructionData = parseInstructionData(params.instructionData);
|
|
20
|
-
|
|
21
|
-
return new Transaction({
|
|
22
|
-
feePayer: params.feePayer.publicKey,
|
|
23
|
-
program,
|
|
24
|
-
header: {
|
|
25
|
-
...params.header,
|
|
26
|
-
flags,
|
|
27
|
-
},
|
|
28
|
-
accounts,
|
|
29
|
-
instructionData,
|
|
30
|
-
proofs: params.proofs,
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async buildAndSign(params: BuildTransactionParams): Promise<SignedTransactionResult> {
|
|
35
|
-
if (!params.feePayer.privateKey) {
|
|
36
|
-
throw new Error("Fee payer private key is required to sign the transaction");
|
|
37
|
-
}
|
|
38
|
-
const transaction = this.build(params);
|
|
39
|
-
const signature = await transaction.sign(params.feePayer.privateKey);
|
|
40
|
-
const rawTransaction = transaction.toWire();
|
|
41
|
-
return { transaction, signature, rawTransaction };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
private normalizeAccounts(accounts?: TransactionAccountsInput): TransactionAccountsInput | undefined {
|
|
45
|
-
if (!accounts) {
|
|
46
|
-
return undefined;
|
|
47
|
-
}
|
|
48
|
-
return {
|
|
49
|
-
readWriteAccounts: normalizeAccountList(accounts.readWriteAccounts ?? []),
|
|
50
|
-
readOnlyAccounts: normalizeAccountList(accounts.readOnlyAccounts ?? []),
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
}
|
|
@@ -1,411 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { TransactionBuilder } from "../TransactionBuilder";
|
|
3
|
-
import { Transaction } from "../Transaction";
|
|
4
|
-
import { generateTestPubkey, generateTestAddress } from "../../__tests__/helpers/test-utils";
|
|
5
|
-
import type { BuildTransactionParams } from "../types";
|
|
6
|
-
|
|
7
|
-
describe("TransactionBuilder", () => {
|
|
8
|
-
const createBuilder = () => new TransactionBuilder();
|
|
9
|
-
|
|
10
|
-
const createMinimalParams = (): BuildTransactionParams => {
|
|
11
|
-
return {
|
|
12
|
-
feePayer: {
|
|
13
|
-
publicKey: generateTestPubkey(0x01),
|
|
14
|
-
},
|
|
15
|
-
program: generateTestPubkey(0x02),
|
|
16
|
-
header: {
|
|
17
|
-
fee: 1n,
|
|
18
|
-
nonce: 2n,
|
|
19
|
-
startSlot: 3n,
|
|
20
|
-
},
|
|
21
|
-
};
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const createPrivateKey = (): Uint8Array => {
|
|
25
|
-
// Ed25519 private key is 32 bytes (seed), but we need to generate a valid one
|
|
26
|
-
// For testing, we'll use a 32-byte array
|
|
27
|
-
const privateKey = new Uint8Array(32);
|
|
28
|
-
privateKey.fill(0x42);
|
|
29
|
-
return privateKey;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
describe("build", () => {
|
|
33
|
-
it("should build transaction with minimal params", () => {
|
|
34
|
-
const builder = createBuilder();
|
|
35
|
-
const params = createMinimalParams();
|
|
36
|
-
|
|
37
|
-
const transaction = builder.build(params);
|
|
38
|
-
|
|
39
|
-
expect(transaction).toBeInstanceOf(Transaction);
|
|
40
|
-
expect(transaction.feePayer).toEqual(params.feePayer.publicKey);
|
|
41
|
-
expect(transaction.program).toEqual(params.program);
|
|
42
|
-
expect(transaction.fee).toBe(params.header.fee);
|
|
43
|
-
expect(transaction.nonce).toBe(params.header.nonce);
|
|
44
|
-
expect(transaction.startSlot).toBe(params.header.startSlot);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("should build transaction with all header fields", () => {
|
|
48
|
-
const builder = createBuilder();
|
|
49
|
-
const params: BuildTransactionParams = {
|
|
50
|
-
...createMinimalParams(),
|
|
51
|
-
header: {
|
|
52
|
-
fee: 10n,
|
|
53
|
-
nonce: 20n,
|
|
54
|
-
startSlot: 30n,
|
|
55
|
-
expiryAfter: 100,
|
|
56
|
-
computeUnits: 300_000_000,
|
|
57
|
-
stateUnits: 10_000,
|
|
58
|
-
memoryUnits: 10_000,
|
|
59
|
-
flags: 1,
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const transaction = builder.build(params);
|
|
64
|
-
|
|
65
|
-
expect(transaction.fee).toBe(10n);
|
|
66
|
-
expect(transaction.nonce).toBe(20n);
|
|
67
|
-
expect(transaction.startSlot).toBe(30n);
|
|
68
|
-
expect(transaction.expiryAfter).toBe(100);
|
|
69
|
-
expect(transaction.requestedComputeUnits).toBe(300_000_000);
|
|
70
|
-
expect(transaction.requestedStateUnits).toBe(10_000);
|
|
71
|
-
expect(transaction.requestedMemoryUnits).toBe(10_000);
|
|
72
|
-
expect(transaction.flags).toBe(1);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it("should accept program as Uint8Array", () => {
|
|
76
|
-
const builder = createBuilder();
|
|
77
|
-
const program = generateTestPubkey(0x02);
|
|
78
|
-
const params: BuildTransactionParams = {
|
|
79
|
-
...createMinimalParams(),
|
|
80
|
-
program,
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const transaction = builder.build(params);
|
|
84
|
-
|
|
85
|
-
expect(transaction.program).toEqual(program);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("should accept program as ta- address string", () => {
|
|
89
|
-
const builder = createBuilder();
|
|
90
|
-
const programAddress = generateTestAddress(0x02);
|
|
91
|
-
const params: BuildTransactionParams = {
|
|
92
|
-
...createMinimalParams(),
|
|
93
|
-
program: programAddress,
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
const transaction = builder.build(params);
|
|
97
|
-
|
|
98
|
-
expect(transaction.program.length).toBe(32);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("should accept program as hex string", () => {
|
|
102
|
-
const builder = createBuilder();
|
|
103
|
-
const programBytes = generateTestPubkey(0x02);
|
|
104
|
-
const hexString = Array.from(programBytes)
|
|
105
|
-
.map(b => b.toString(16).padStart(2, "0"))
|
|
106
|
-
.join("");
|
|
107
|
-
const params: BuildTransactionParams = {
|
|
108
|
-
...createMinimalParams(),
|
|
109
|
-
program: hexString,
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const transaction = builder.build(params);
|
|
113
|
-
|
|
114
|
-
expect(transaction.program).toEqual(programBytes);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it("should normalize and sort accounts", () => {
|
|
118
|
-
const builder = createBuilder();
|
|
119
|
-
const account1 = generateTestPubkey(0x03);
|
|
120
|
-
const account2 = generateTestPubkey(0x04);
|
|
121
|
-
const account3 = generateTestPubkey(0x02);
|
|
122
|
-
|
|
123
|
-
const params: BuildTransactionParams = {
|
|
124
|
-
...createMinimalParams(),
|
|
125
|
-
accounts: {
|
|
126
|
-
readWriteAccounts: [account1, account2, account3],
|
|
127
|
-
},
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
const transaction = builder.build(params);
|
|
131
|
-
|
|
132
|
-
expect(transaction.readWriteAccounts).toHaveLength(3);
|
|
133
|
-
// Should be sorted
|
|
134
|
-
expect(transaction.readWriteAccounts[0]).toEqual(account3); // 0x02
|
|
135
|
-
expect(transaction.readWriteAccounts[1]).toEqual(account1); // 0x03
|
|
136
|
-
expect(transaction.readWriteAccounts[2]).toEqual(account2); // 0x04
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it("should deduplicate accounts", () => {
|
|
140
|
-
const builder = createBuilder();
|
|
141
|
-
const account1 = generateTestPubkey(0x03);
|
|
142
|
-
const account1Duplicate = new Uint8Array(account1);
|
|
143
|
-
|
|
144
|
-
const params: BuildTransactionParams = {
|
|
145
|
-
...createMinimalParams(),
|
|
146
|
-
accounts: {
|
|
147
|
-
readWriteAccounts: [account1, account1Duplicate],
|
|
148
|
-
},
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
const transaction = builder.build(params);
|
|
152
|
-
|
|
153
|
-
expect(transaction.readWriteAccounts).toHaveLength(1);
|
|
154
|
-
expect(transaction.readWriteAccounts[0]).toEqual(account1);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it("should handle read-only accounts", () => {
|
|
158
|
-
const builder = createBuilder();
|
|
159
|
-
const readOnlyAccount = generateTestPubkey(0x05);
|
|
160
|
-
|
|
161
|
-
const params: BuildTransactionParams = {
|
|
162
|
-
...createMinimalParams(),
|
|
163
|
-
accounts: {
|
|
164
|
-
readOnlyAccounts: [readOnlyAccount],
|
|
165
|
-
},
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
const transaction = builder.build(params);
|
|
169
|
-
|
|
170
|
-
expect(transaction.readOnlyAccounts).toHaveLength(1);
|
|
171
|
-
expect(transaction.readOnlyAccounts[0]).toEqual(readOnlyAccount);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it("should handle both read-write and read-only accounts", () => {
|
|
175
|
-
const builder = createBuilder();
|
|
176
|
-
const rwAccount = generateTestPubkey(0x03);
|
|
177
|
-
const roAccount = generateTestPubkey(0x04);
|
|
178
|
-
|
|
179
|
-
const params: BuildTransactionParams = {
|
|
180
|
-
...createMinimalParams(),
|
|
181
|
-
accounts: {
|
|
182
|
-
readWriteAccounts: [rwAccount],
|
|
183
|
-
readOnlyAccounts: [roAccount],
|
|
184
|
-
},
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
const transaction = builder.build(params);
|
|
188
|
-
|
|
189
|
-
expect(transaction.readWriteAccounts).toHaveLength(1);
|
|
190
|
-
expect(transaction.readOnlyAccounts).toHaveLength(1);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it("should handle instruction data as Uint8Array", () => {
|
|
194
|
-
const builder = createBuilder();
|
|
195
|
-
const instructionData = new Uint8Array([0x01, 0x02, 0x03]);
|
|
196
|
-
|
|
197
|
-
const params: BuildTransactionParams = {
|
|
198
|
-
...createMinimalParams(),
|
|
199
|
-
instructionData,
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
const transaction = builder.build(params);
|
|
203
|
-
|
|
204
|
-
expect(transaction.instructionData).toEqual(instructionData);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it("should handle instruction data as hex string", () => {
|
|
208
|
-
const builder = createBuilder();
|
|
209
|
-
const instructionBytes = new Uint8Array([0x01, 0x02, 0x03]);
|
|
210
|
-
const hexString = Array.from(instructionBytes)
|
|
211
|
-
.map(b => b.toString(16).padStart(2, "0"))
|
|
212
|
-
.join("");
|
|
213
|
-
|
|
214
|
-
const params: BuildTransactionParams = {
|
|
215
|
-
...createMinimalParams(),
|
|
216
|
-
instructionData: hexString,
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
const transaction = builder.build(params);
|
|
220
|
-
|
|
221
|
-
expect(transaction.instructionData).toEqual(instructionBytes);
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
it("should set fee payer proof flag when proof is provided", () => {
|
|
225
|
-
const builder = createBuilder();
|
|
226
|
-
const proof = new Uint8Array(64);
|
|
227
|
-
proof.fill(0x42);
|
|
228
|
-
|
|
229
|
-
const params: BuildTransactionParams = {
|
|
230
|
-
...createMinimalParams(),
|
|
231
|
-
header: {
|
|
232
|
-
fee: 1n,
|
|
233
|
-
nonce: 2n,
|
|
234
|
-
startSlot: 3n,
|
|
235
|
-
flags: 0,
|
|
236
|
-
},
|
|
237
|
-
proofs: {
|
|
238
|
-
feePayerStateProof: proof,
|
|
239
|
-
},
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
const transaction = builder.build(params);
|
|
243
|
-
|
|
244
|
-
// Flag should be set (FLAG_HAS_FEE_PAYER_PROOF = 1 << 0 = 1)
|
|
245
|
-
expect(transaction.flags & 1).toBe(1);
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
it("should not set fee payer proof flag when proof is not provided", () => {
|
|
249
|
-
const builder = createBuilder();
|
|
250
|
-
const params: BuildTransactionParams = {
|
|
251
|
-
...createMinimalParams(),
|
|
252
|
-
header: {
|
|
253
|
-
fee: 1n,
|
|
254
|
-
nonce: 2n,
|
|
255
|
-
startSlot: 3n,
|
|
256
|
-
flags: 0,
|
|
257
|
-
},
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
const transaction = builder.build(params);
|
|
261
|
-
|
|
262
|
-
expect(transaction.flags & 1).toBe(0);
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
it("should preserve custom flags when proof is provided", () => {
|
|
266
|
-
const builder = createBuilder();
|
|
267
|
-
const proof = new Uint8Array(64);
|
|
268
|
-
proof.fill(0x42);
|
|
269
|
-
|
|
270
|
-
const params: BuildTransactionParams = {
|
|
271
|
-
...createMinimalParams(),
|
|
272
|
-
header: {
|
|
273
|
-
fee: 1n,
|
|
274
|
-
nonce: 2n,
|
|
275
|
-
startSlot: 3n,
|
|
276
|
-
flags: 2, // Custom flag
|
|
277
|
-
},
|
|
278
|
-
proofs: {
|
|
279
|
-
feePayerStateProof: proof,
|
|
280
|
-
},
|
|
281
|
-
};
|
|
282
|
-
|
|
283
|
-
const transaction = builder.build(params);
|
|
284
|
-
|
|
285
|
-
// Should have both custom flag (2) and proof flag (1) = 3
|
|
286
|
-
expect(transaction.flags).toBe(3);
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
it("should store fee payer state proof", () => {
|
|
290
|
-
const builder = createBuilder();
|
|
291
|
-
const proof = new Uint8Array(64);
|
|
292
|
-
proof.fill(0x42);
|
|
293
|
-
|
|
294
|
-
const params: BuildTransactionParams = {
|
|
295
|
-
...createMinimalParams(),
|
|
296
|
-
proofs: {
|
|
297
|
-
feePayerStateProof: proof,
|
|
298
|
-
},
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
const transaction = builder.build(params);
|
|
302
|
-
|
|
303
|
-
expect(transaction.feePayerStateProof).toEqual(proof);
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
it("should store fee payer account meta raw", () => {
|
|
307
|
-
const builder = createBuilder();
|
|
308
|
-
const metaRaw = new Uint8Array(32);
|
|
309
|
-
metaRaw.fill(0x43);
|
|
310
|
-
|
|
311
|
-
const params: BuildTransactionParams = {
|
|
312
|
-
...createMinimalParams(),
|
|
313
|
-
proofs: {
|
|
314
|
-
feePayerAccountMetaRaw: metaRaw,
|
|
315
|
-
},
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
const transaction = builder.build(params);
|
|
319
|
-
|
|
320
|
-
expect(transaction.feePayerAccountMetaRaw).toEqual(metaRaw);
|
|
321
|
-
});
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
describe("buildAndSign", () => {
|
|
325
|
-
it("should build and sign transaction", async () => {
|
|
326
|
-
const builder = createBuilder();
|
|
327
|
-
const privateKey = createPrivateKey();
|
|
328
|
-
const publicKey = generateTestPubkey(0x01);
|
|
329
|
-
const params: BuildTransactionParams = {
|
|
330
|
-
feePayer: {
|
|
331
|
-
publicKey,
|
|
332
|
-
privateKey,
|
|
333
|
-
},
|
|
334
|
-
program: generateTestPubkey(0x02),
|
|
335
|
-
header: {
|
|
336
|
-
fee: 1n,
|
|
337
|
-
nonce: 2n,
|
|
338
|
-
startSlot: 3n,
|
|
339
|
-
},
|
|
340
|
-
};
|
|
341
|
-
|
|
342
|
-
const result = await builder.buildAndSign(params);
|
|
343
|
-
|
|
344
|
-
expect(result.transaction).toBeInstanceOf(Transaction);
|
|
345
|
-
expect(result.signature).toBeInstanceOf(Uint8Array);
|
|
346
|
-
expect(result.signature.length).toBe(64);
|
|
347
|
-
expect(result.rawTransaction).toBeInstanceOf(Uint8Array);
|
|
348
|
-
expect(result.rawTransaction.length).toBeGreaterThan(0);
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
it("should throw error when private key is missing", async () => {
|
|
352
|
-
const builder = createBuilder();
|
|
353
|
-
const params: BuildTransactionParams = {
|
|
354
|
-
...createMinimalParams(),
|
|
355
|
-
// No privateKey
|
|
356
|
-
};
|
|
357
|
-
|
|
358
|
-
await expect(builder.buildAndSign(params)).rejects.toThrow(
|
|
359
|
-
"Fee payer private key is required to sign the transaction"
|
|
360
|
-
);
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
it("should include signature in raw transaction", async () => {
|
|
364
|
-
const builder = createBuilder();
|
|
365
|
-
const privateKey = createPrivateKey();
|
|
366
|
-
const publicKey = generateTestPubkey(0x01);
|
|
367
|
-
const params: BuildTransactionParams = {
|
|
368
|
-
feePayer: {
|
|
369
|
-
publicKey,
|
|
370
|
-
privateKey,
|
|
371
|
-
},
|
|
372
|
-
program: generateTestPubkey(0x02),
|
|
373
|
-
header: {
|
|
374
|
-
fee: 1n,
|
|
375
|
-
nonce: 2n,
|
|
376
|
-
startSlot: 3n,
|
|
377
|
-
},
|
|
378
|
-
};
|
|
379
|
-
|
|
380
|
-
const result = await builder.buildAndSign(params);
|
|
381
|
-
|
|
382
|
-
// Signature should be in first 64 bytes of raw transaction
|
|
383
|
-
const signatureInRaw = result.rawTransaction.slice(0, 64);
|
|
384
|
-
expect(signatureInRaw).toEqual(result.signature);
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
it("should create valid wire format", async () => {
|
|
388
|
-
const builder = createBuilder();
|
|
389
|
-
const privateKey = createPrivateKey();
|
|
390
|
-
const publicKey = generateTestPubkey(0x01);
|
|
391
|
-
const params: BuildTransactionParams = {
|
|
392
|
-
feePayer: {
|
|
393
|
-
publicKey,
|
|
394
|
-
privateKey,
|
|
395
|
-
},
|
|
396
|
-
program: generateTestPubkey(0x02),
|
|
397
|
-
header: {
|
|
398
|
-
fee: 1n,
|
|
399
|
-
nonce: 2n,
|
|
400
|
-
startSlot: 3n,
|
|
401
|
-
},
|
|
402
|
-
};
|
|
403
|
-
|
|
404
|
-
const result = await builder.buildAndSign(params);
|
|
405
|
-
|
|
406
|
-
// Should have at least header size (176 bytes)
|
|
407
|
-
expect(result.rawTransaction.length).toBeGreaterThanOrEqual(176);
|
|
408
|
-
});
|
|
409
|
-
});
|
|
410
|
-
});
|
|
411
|
-
|