@altiuslabs/tx-sdk 0.1.21 → 0.1.23
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/package.json +1 -1
- package/src/transaction.js +33 -11
- package/src/wallet.js +19 -6
package/package.json
CHANGED
package/src/transaction.js
CHANGED
|
@@ -23,10 +23,10 @@ export class TxBuilder {
|
|
|
23
23
|
this._data = '0x';
|
|
24
24
|
this._max_priority_fee_per_gas = 0;
|
|
25
25
|
this._max_fee_per_gas = 0;
|
|
26
|
-
this._max_fee_per_gas_usd_attodollars = 0
|
|
26
|
+
this._max_fee_per_gas_usd_attodollars = 40000000000n; // $0.04/gas default
|
|
27
27
|
this._fee_token = null;
|
|
28
|
-
this._fee_payer =
|
|
29
|
-
this._fee_payer_signature =
|
|
28
|
+
this._fee_payer = null;
|
|
29
|
+
this._fee_payer_signature = null;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/**
|
|
@@ -158,6 +158,9 @@ export class TxBuilder {
|
|
|
158
158
|
if (this._nonce === null) throw new Error('nonce is required');
|
|
159
159
|
if (!this._fee_token) throw new Error('fee_token is required');
|
|
160
160
|
|
|
161
|
+
// Default fee_payer to zero address (sender-pays)
|
|
162
|
+
const fee_payer = this._fee_payer || '0x0000000000000000000000000000000000000000';
|
|
163
|
+
|
|
161
164
|
return {
|
|
162
165
|
chain_id: this._chain_id,
|
|
163
166
|
nonce: this._nonce,
|
|
@@ -169,7 +172,7 @@ export class TxBuilder {
|
|
|
169
172
|
max_fee_per_gas: this._max_fee_per_gas,
|
|
170
173
|
max_fee_per_gas_usd_attodollars: this._max_fee_per_gas_usd_attodollars,
|
|
171
174
|
fee_token: this._fee_token,
|
|
172
|
-
fee_payer
|
|
175
|
+
fee_payer,
|
|
173
176
|
fee_payer_signature: this._fee_payer_signature,
|
|
174
177
|
};
|
|
175
178
|
}
|
|
@@ -220,16 +223,33 @@ export class TxBuilder {
|
|
|
220
223
|
|
|
221
224
|
const tx = this.build();
|
|
222
225
|
|
|
223
|
-
// Use sender as fee_payer if not specified
|
|
224
|
-
const feePayer = tx.fee_payer ||
|
|
226
|
+
// Use sender as fee_payer if not specified (null means use sender)
|
|
227
|
+
const feePayer = tx.fee_payer === '0x0000000000000000000000000000000000000000' || !tx.fee_payer
|
|
228
|
+
? wallet.address
|
|
229
|
+
: tx.fee_payer;
|
|
230
|
+
|
|
231
|
+
// Auto-generate fee_payer_signature if not set and fee_payer equals sender
|
|
232
|
+
let feePayerSignature = tx.fee_payer_signature;
|
|
233
|
+
if (!feePayerSignature && feePayer.toLowerCase() === wallet.address.toLowerCase()) {
|
|
234
|
+
// Compute fee_payer signature hash using sender as the sender
|
|
235
|
+
const feePayerSigHash = fee_payer_signature_hash(tx, wallet.address);
|
|
236
|
+
const feePayerSig = await wallet.sign_hash(feePayerSigHash);
|
|
237
|
+
// Format: 0x + v (1 byte) + r (32 bytes) + s (32 bytes)
|
|
238
|
+
feePayerSignature = '0x' + feePayerSig.v.toString(16).padStart(2, '0') + feePayerSig.r.slice(2) + feePayerSig.s.slice(2);
|
|
239
|
+
}
|
|
225
240
|
|
|
226
241
|
// Convert r, s to 32-byte buffers
|
|
227
242
|
const rBuffer = Buffer.from(sig.r.slice(2), 'hex');
|
|
228
243
|
const sBuffer = Buffer.from(sig.s.slice(2), 'hex');
|
|
229
244
|
|
|
245
|
+
// Note: y_parity is used directly as a number in signature_bytes below
|
|
246
|
+
|
|
230
247
|
// Build signed transaction list (for EIP-2718 encoding)
|
|
231
248
|
// Format matches node's rlp_encode_fields: [chainId, nonce, ..., feePayerSignature, signature]
|
|
232
249
|
// IMPORTANT: Use Buffer for addresses and data to match Rust alloy-rlp encoding
|
|
250
|
+
//
|
|
251
|
+
// NOTE: For signature encoding, we use RLP encoding to match Rust alloy's behavior.
|
|
252
|
+
// The signature (y_parity, r, s) is added to the RLP list and encoded together.
|
|
233
253
|
const signed_fields = [
|
|
234
254
|
BigInt(tx.chain_id),
|
|
235
255
|
BigInt(tx.nonce),
|
|
@@ -243,15 +263,16 @@ export class TxBuilder {
|
|
|
243
263
|
Buffer.from(tx.fee_token.slice(2), 'hex'), // fee_token as buffer
|
|
244
264
|
Buffer.from(feePayer.slice(2), 'hex'), // fee_payer as buffer
|
|
245
265
|
BigInt(tx.max_fee_per_gas_usd_attodollars),
|
|
246
|
-
Buffer.from(
|
|
247
|
-
// Signature: y_parity
|
|
248
|
-
y_parity,
|
|
249
|
-
|
|
250
|
-
|
|
266
|
+
Buffer.from(feePayerSignature?.slice(2) || '', 'hex') || Buffer.alloc(0),
|
|
267
|
+
// Signature: y_parity, r, s - RLP encoded as part of the list
|
|
268
|
+
BigInt(y_parity),
|
|
269
|
+
Buffer.from(sig.r.slice(2), 'hex'), // r as 32-byte buffer
|
|
270
|
+
Buffer.from(sig.s.slice(2), 'hex'), // s as 32-byte buffer
|
|
251
271
|
];
|
|
252
272
|
|
|
253
273
|
// EIP-2718: first byte is type (0x7a), then RLP encoded fields
|
|
254
274
|
const rlp_fields_buf = RLP.encode(signed_fields);
|
|
275
|
+
|
|
255
276
|
const raw_transaction = '0x7a' + rlp_fields_buf.toString('hex');
|
|
256
277
|
|
|
257
278
|
const transaction_hash = keccak256(raw_transaction);
|
|
@@ -259,6 +280,7 @@ export class TxBuilder {
|
|
|
259
280
|
return {
|
|
260
281
|
...tx,
|
|
261
282
|
fee_payer: feePayer,
|
|
283
|
+
fee_payer_signature: feePayerSignature,
|
|
262
284
|
v: y_parity,
|
|
263
285
|
r: sig.r,
|
|
264
286
|
s: sig.s,
|
package/src/wallet.js
CHANGED
|
@@ -23,9 +23,12 @@ export function generate_private_key() {
|
|
|
23
23
|
*/
|
|
24
24
|
export function private_key_to_address(private_key) {
|
|
25
25
|
const pk = private_key.slice(2);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
// false = uncompressed key (65 bytes starting with 0x04)
|
|
27
|
+
const publicKey = secp256k1.getPublicKey(pk, false);
|
|
28
|
+
// Convert hex string to Buffer
|
|
29
|
+
const publicKeyBuffer = Buffer.from(publicKey, 'hex');
|
|
30
|
+
// Remove first byte (prefix 0x04 for uncompressed key) and hash
|
|
31
|
+
const hash = keccak256(publicKeyBuffer.slice(1));
|
|
29
32
|
// Take last 20 bytes
|
|
30
33
|
return '0x' + hash.slice(-40);
|
|
31
34
|
}
|
|
@@ -56,7 +59,13 @@ function parse_der(der) {
|
|
|
56
59
|
offset += 2;
|
|
57
60
|
|
|
58
61
|
// Get r (may need padding for leading zeros)
|
|
59
|
-
|
|
62
|
+
// DER uses signed integers, so if MSB >= 0x80, a 0x00 prefix is added
|
|
63
|
+
// We need to strip that prefix if present, then pad to 32 bytes
|
|
64
|
+
let rHex = der.slice(offset, offset + rLen * 2);
|
|
65
|
+
if (rHex.startsWith('00') && rLen === 33) {
|
|
66
|
+
// Strip the leading 0x00 added by DER encoding
|
|
67
|
+
rHex = rHex.slice(2);
|
|
68
|
+
}
|
|
60
69
|
const r = '0x' + rHex.padStart(64, '0');
|
|
61
70
|
offset += rLen * 2;
|
|
62
71
|
|
|
@@ -68,8 +77,12 @@ function parse_der(der) {
|
|
|
68
77
|
const sLen = parseInt(der.slice(offset, offset + 2), 16);
|
|
69
78
|
offset += 2;
|
|
70
79
|
|
|
71
|
-
// Get s
|
|
72
|
-
|
|
80
|
+
// Get s (may need to strip leading 0x00 added by DER encoding)
|
|
81
|
+
let sHex = der.slice(offset, offset + sLen * 2);
|
|
82
|
+
if (sHex.startsWith('00') && sLen === 33) {
|
|
83
|
+
// Strip the leading 0x00 added by DER encoding
|
|
84
|
+
sHex = sHex.slice(2);
|
|
85
|
+
}
|
|
73
86
|
const s = '0x' + sHex.padStart(64, '0');
|
|
74
87
|
|
|
75
88
|
return { r, s };
|