Skip to content

Commit 87694a1

Browse files
feat: flrp validators and delegator
TICKET: WIN-7084
1 parent 5658080 commit 87694a1

15 files changed

+585
-273
lines changed

modules/sdk-coin-flrp/src/lib/atomicTransactionBuilder.ts

Lines changed: 64 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,38 @@ import { Credential, Signature, TransferableInput, TransferableOutput } from '@f
44
import { TransactionExplanation, DecodedUtxoObj } from './iface';
55
import {
66
ASSET_ID_LENGTH,
7-
SECP256K1_SIGNATURE_LENGTH,
87
TRANSACTION_ID_HEX_LENGTH,
98
PRIVATE_KEY_HEX_LENGTH,
10-
createFlexibleHexRegex,
9+
SECP256K1_SIGNATURE_LENGTH,
10+
TRANSACTION_ID_PREFIX,
11+
DEFAULT_NETWORK_ID,
12+
EMPTY_BUFFER_SIZE,
13+
HEX_PREFIX,
14+
HEX_PREFIX_LENGTH,
15+
DECIMAL_RADIX,
16+
SIGNING_METHOD,
17+
AMOUNT_STRING_ZERO,
18+
DEFAULT_LOCKTIME,
19+
DEFAULT_THRESHOLD,
20+
ZERO_BIGINT,
21+
ZERO_NUMBER,
22+
ERROR_AMOUNT_POSITIVE,
23+
ERROR_CREDENTIALS_ARRAY,
24+
ERROR_UTXOS_REQUIRED,
25+
ERROR_SIGNATURES_ARRAY,
26+
ERROR_SIGNATURES_EMPTY,
27+
ERROR_INVALID_PRIVATE_KEY,
28+
ERROR_UTXOS_REQUIRED_BUILD,
29+
ERROR_ENHANCED_BUILD_FAILED,
30+
ERROR_ENHANCED_PARSE_FAILED,
31+
ERROR_FLAREJS_SIGNING_FAILED,
32+
ERROR_CREATE_CREDENTIAL_FAILED,
33+
ERROR_UNKNOWN,
34+
FLARE_ATOMIC_PREFIX,
35+
FLARE_ATOMIC_PARSED_PREFIX,
36+
HEX_ENCODING,
1137
} from './constants';
38+
import { createFlexibleHexRegex } from './utils';
1239

1340
/**
1441
* Flare P-chain atomic transaction builder with FlareJS credential support.
@@ -38,14 +65,14 @@ export abstract class AtomicTransactionBuilder {
3865
setTransaction: (tx: unknown) => void;
3966
} = {
4067
_network: {},
41-
_networkID: 0,
42-
_blockchainID: Buffer.alloc(0),
43-
_assetId: Buffer.alloc(0),
68+
_networkID: DEFAULT_NETWORK_ID,
69+
_blockchainID: Buffer.alloc(EMPTY_BUFFER_SIZE),
70+
_assetId: Buffer.alloc(EMPTY_BUFFER_SIZE),
4471
_fromAddresses: [],
4572
_to: [],
46-
_locktime: 0n,
47-
_threshold: 1,
48-
_fee: { fee: '0' },
73+
_locktime: DEFAULT_LOCKTIME,
74+
_threshold: DEFAULT_THRESHOLD,
75+
_fee: { fee: AMOUNT_STRING_ZERO },
4976
hasCredentials: false,
5077
setTransaction: function (_tx: unknown) {
5178
this._tx = _tx;
@@ -75,8 +102,8 @@ export abstract class AtomicTransactionBuilder {
75102
}
76103

77104
validateAmount(amount: bigint): void {
78-
if (amount <= 0n) {
79-
throw new BuildTransactionError('Amount must be positive');
105+
if (amount <= ZERO_BIGINT) {
106+
throw new BuildTransactionError(ERROR_AMOUNT_POSITIVE);
80107
}
81108
}
82109

@@ -86,7 +113,7 @@ export abstract class AtomicTransactionBuilder {
86113
*/
87114
protected validateCredentials(credentials: Credential[]): void {
88115
if (!Array.isArray(credentials)) {
89-
throw new BuildTransactionError('Credentials must be an array');
116+
throw new BuildTransactionError(ERROR_CREDENTIALS_ARRAY);
90117
}
91118

92119
credentials.forEach((credential, index) => {
@@ -111,8 +138,8 @@ export abstract class AtomicTransactionBuilder {
111138
outputs: TransferableOutput[];
112139
credentials: Credential[];
113140
} {
114-
if (!this._utxos || this._utxos.length === 0) {
115-
throw new BuildTransactionError('UTXOs are required for creating inputs and outputs');
141+
if (!this._utxos || this._utxos.length === ZERO_NUMBER) {
142+
throw new BuildTransactionError(ERROR_UTXOS_REQUIRED);
116143
}
117144

118145
const inputs: TransferableInput[] = [];
@@ -157,8 +184,8 @@ export abstract class AtomicTransactionBuilder {
157184

158185
// Create TransferableInput for atomic transactions
159186
const transferableInput = {
160-
txID: Buffer.from(utxo.txid || '0'.repeat(TRANSACTION_ID_HEX_LENGTH), 'hex'),
161-
outputIndex: parseInt(utxo.outputidx || '0', 10),
187+
txID: Buffer.from(utxo.txid || AMOUNT_STRING_ZERO.repeat(TRANSACTION_ID_HEX_LENGTH), HEX_ENCODING),
188+
outputIndex: parseInt(utxo.outputidx || AMOUNT_STRING_ZERO, DECIMAL_RADIX),
162189
assetID: this.getAssetId(),
163190
input: {
164191
amount: utxoAmount,
@@ -225,11 +252,11 @@ export abstract class AtomicTransactionBuilder {
225252
*/
226253
protected createFlareCredential(_credentialId: number, signatures: string[]): Credential {
227254
if (!Array.isArray(signatures)) {
228-
throw new BuildTransactionError('Signatures must be an array');
255+
throw new BuildTransactionError(ERROR_SIGNATURES_ARRAY);
229256
}
230257

231-
if (signatures.length === 0) {
232-
throw new BuildTransactionError('Signatures array cannot be empty');
258+
if (signatures.length === ZERO_NUMBER) {
259+
throw new BuildTransactionError(ERROR_SIGNATURES_EMPTY);
233260
}
234261

235262
const sigs = signatures.map((sig, index) => {
@@ -239,13 +266,13 @@ export abstract class AtomicTransactionBuilder {
239266
}
240267

241268
// Validate hex string format
242-
const cleanSig = sig.startsWith('0x') ? sig.slice(2) : sig;
269+
const cleanSig = sig.startsWith(HEX_PREFIX) ? sig.slice(HEX_PREFIX_LENGTH) : sig;
243270
if (!createFlexibleHexRegex().test(cleanSig)) {
244271
throw new BuildTransactionError(`Invalid hex signature at index ${index}: contains non-hex characters`);
245272
}
246273

247274
// Convert to buffer and validate length
248-
const sigBuffer = Buffer.from(cleanSig, 'hex');
275+
const sigBuffer = Buffer.from(cleanSig, HEX_ENCODING);
249276
if (sigBuffer.length > SECP256K1_SIGNATURE_LENGTH) {
250277
throw new BuildTransactionError(
251278
`Signature too long at index ${index}: ${sigBuffer.length} bytes (max ${SECP256K1_SIGNATURE_LENGTH})`
@@ -260,7 +287,7 @@ export abstract class AtomicTransactionBuilder {
260287
return new Signature(new Uint8Array(fixedLengthBuffer));
261288
} catch (error) {
262289
throw new BuildTransactionError(
263-
`Failed to create signature at index ${index}: ${error instanceof Error ? error.message : 'unknown error'}`
290+
`Failed to create signature at index ${index}: ${error instanceof Error ? error.message : ERROR_UNKNOWN}`
264291
);
265292
}
266293
});
@@ -269,7 +296,7 @@ export abstract class AtomicTransactionBuilder {
269296
return new Credential(sigs);
270297
} catch (error) {
271298
throw new BuildTransactionError(
272-
`Failed to create credential: ${error instanceof Error ? error.message : 'unknown error'}`
299+
`${ERROR_CREATE_CREDENTIAL_FAILED}: ${error instanceof Error ? error.message : ERROR_UNKNOWN}`
273300
);
274301
}
275302
}
@@ -289,13 +316,13 @@ export abstract class AtomicTransactionBuilder {
289316
try {
290317
// Validate private key format (placeholder implementation)
291318
if (!params.key || params.key.length < PRIVATE_KEY_HEX_LENGTH) {
292-
throw new BuildTransactionError('Invalid private key format');
319+
throw new BuildTransactionError(ERROR_INVALID_PRIVATE_KEY);
293320
}
294321

295322
// Create signature structure
296323
const signature = {
297324
privateKey: params.key,
298-
signingMethod: 'secp256k1',
325+
signingMethod: SIGNING_METHOD,
299326
};
300327

301328
// Store signature for FlareJS compatibility
@@ -305,7 +332,7 @@ export abstract class AtomicTransactionBuilder {
305332
return this;
306333
} catch (error) {
307334
throw new BuildTransactionError(
308-
`FlareJS signing failed: ${error instanceof Error ? error.message : 'unknown error'}`
335+
`${ERROR_FLAREJS_SIGNING_FAILED}: ${error instanceof Error ? error.message : ERROR_UNKNOWN}`
309336
);
310337
}
311338
}
@@ -318,12 +345,12 @@ export abstract class AtomicTransactionBuilder {
318345
try {
319346
// Validate transaction requirements
320347
if (!this._utxos || this._utxos.length === 0) {
321-
throw new BuildTransactionError('UTXOs are required for transaction building');
348+
throw new BuildTransactionError(ERROR_UTXOS_REQUIRED_BUILD);
322349
}
323350

324351
// Create FlareJS transaction structure with atomic support
325352
const transaction = {
326-
_id: `flare-atomic-tx-${Date.now()}`,
353+
_id: `${TRANSACTION_ID_PREFIX}${Date.now()}`,
327354
_inputs: [],
328355
_outputs: [],
329356
_type: this.transactionType,
@@ -333,7 +360,7 @@ export abstract class AtomicTransactionBuilder {
333360
validationErrors: [],
334361

335362
// FlareJS methods with atomic support
336-
toBroadcastFormat: () => `flare-atomic-tx-${Date.now()}`,
363+
toBroadcastFormat: () => `${TRANSACTION_ID_PREFIX}${Date.now()}`,
337364
toJson: () => ({
338365
type: this.transactionType,
339366
}),
@@ -342,11 +369,11 @@ export abstract class AtomicTransactionBuilder {
342369
type: this.transactionType,
343370
inputs: [],
344371
outputs: [],
345-
outputAmount: '0',
372+
outputAmount: AMOUNT_STRING_ZERO,
346373
rewardAddresses: [],
347-
id: `flare-atomic-${Date.now()}`,
374+
id: `${FLARE_ATOMIC_PREFIX}${Date.now()}`,
348375
changeOutputs: [],
349-
changeAmount: '0',
376+
changeAmount: AMOUNT_STRING_ZERO,
350377
fee: { fee: this.transaction._fee.fee },
351378
}),
352379

@@ -358,14 +385,14 @@ export abstract class AtomicTransactionBuilder {
358385
outputs: () => [],
359386
fee: () => ({ fee: this.transaction._fee.fee }),
360387
feeRate: () => 0,
361-
id: () => `flare-atomic-${Date.now()}`,
388+
id: () => `${FLARE_ATOMIC_PREFIX}${Date.now()}`,
362389
type: this.transactionType,
363390
} as unknown as BaseTransaction;
364391

365392
return transaction;
366393
} catch (error) {
367394
throw new BuildTransactionError(
368-
`Enhanced FlareJS transaction building failed: ${error instanceof Error ? error.message : 'unknown error'}`
395+
`${ERROR_ENHANCED_BUILD_FAILED}: ${error instanceof Error ? error.message : ERROR_UNKNOWN}`
369396
);
370397
}
371398
}
@@ -380,16 +407,16 @@ export abstract class AtomicTransactionBuilder {
380407
type: this.transactionType,
381408
inputs: [],
382409
outputs: [],
383-
outputAmount: '0',
410+
outputAmount: AMOUNT_STRING_ZERO,
384411
rewardAddresses: [],
385-
id: `flare-atomic-parsed-${Date.now()}`,
412+
id: `${FLARE_ATOMIC_PARSED_PREFIX}${Date.now()}`,
386413
changeOutputs: [],
387-
changeAmount: '0',
414+
changeAmount: AMOUNT_STRING_ZERO,
388415
fee: { fee: this.transaction._fee.fee },
389416
};
390417
} catch (error) {
391418
throw new BuildTransactionError(
392-
`Enhanced FlareJS transaction parsing failed: ${error instanceof Error ? error.message : 'unknown error'}`
419+
`${ERROR_ENHANCED_PARSE_FAILED}: ${error instanceof Error ? error.message : ERROR_UNKNOWN}`
393420
);
394421
}
395422
}

0 commit comments

Comments
 (0)