Socket
Socket
Sign inDemoInstall

@ethersproject/transactions

Package Overview
Dependencies
Maintainers
1
Versions
44
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ethersproject/transactions - npm Package Compare versions

Comparing version 5.6.0 to 6.0.0-beta.1

lib/accesslist.d.ts

2

lib/_version.d.ts

@@ -1,2 +0,2 @@

export declare const version = "transactions/5.6.0";
export declare const version = "@ethersproject/transactions@6.0.0-beta.1";
//# sourceMappingURL=_version.d.ts.map

@@ -1,5 +0,2 @@

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = void 0;
exports.version = "transactions/5.6.0";
export const version = "@ethersproject/transactions@6.0.0-beta.1";
//# sourceMappingURL=_version.js.map

@@ -1,49 +0,5 @@

import { BigNumber, BigNumberish } from "@ethersproject/bignumber";
import { BytesLike, SignatureLike } from "@ethersproject/bytes";
export declare type AccessList = Array<{
address: string;
storageKeys: Array<string>;
}>;
export declare type AccessListish = AccessList | Array<[string, Array<string>]> | Record<string, Array<string>>;
export declare enum TransactionTypes {
legacy = 0,
eip2930 = 1,
eip1559 = 2
}
export declare type UnsignedTransaction = {
to?: string;
nonce?: number;
gasLimit?: BigNumberish;
gasPrice?: BigNumberish;
data?: BytesLike;
value?: BigNumberish;
chainId?: number;
type?: number | null;
accessList?: AccessListish;
maxPriorityFeePerGas?: BigNumberish;
maxFeePerGas?: BigNumberish;
};
export interface Transaction {
hash?: string;
to?: string;
from?: string;
nonce: number;
gasLimit: BigNumber;
gasPrice?: BigNumber;
data: string;
value: BigNumber;
chainId: number;
r?: string;
s?: string;
v?: number;
type?: number | null;
accessList?: AccessList;
maxPriorityFeePerGas?: BigNumber;
maxFeePerGas?: BigNumber;
}
export declare function computeAddress(key: BytesLike | string): string;
export declare function recoverAddress(digest: BytesLike, signature: SignatureLike): string;
export declare function accessListify(value: AccessListish): AccessList;
export declare function serialize(transaction: UnsignedTransaction, signature?: SignatureLike): string;
export declare function parse(rawTransaction: BytesLike): Transaction;
export { accessListify } from "./accesslist.js";
export { Transaction } from "./transaction.js";
export type { AccessList, AccessListSet, AccessListish } from "./accesslist.js";
export type { SignedTransaction, TransactionLike } from "./transaction.js";
//# sourceMappingURL=index.d.ts.map

@@ -1,416 +0,3 @@

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.parse = exports.serialize = exports.accessListify = exports.recoverAddress = exports.computeAddress = exports.TransactionTypes = void 0;
var address_1 = require("@ethersproject/address");
var bignumber_1 = require("@ethersproject/bignumber");
var bytes_1 = require("@ethersproject/bytes");
var constants_1 = require("@ethersproject/constants");
var keccak256_1 = require("@ethersproject/keccak256");
var properties_1 = require("@ethersproject/properties");
var RLP = __importStar(require("@ethersproject/rlp"));
var signing_key_1 = require("@ethersproject/signing-key");
var logger_1 = require("@ethersproject/logger");
var _version_1 = require("./_version");
var logger = new logger_1.Logger(_version_1.version);
var TransactionTypes;
(function (TransactionTypes) {
TransactionTypes[TransactionTypes["legacy"] = 0] = "legacy";
TransactionTypes[TransactionTypes["eip2930"] = 1] = "eip2930";
TransactionTypes[TransactionTypes["eip1559"] = 2] = "eip1559";
})(TransactionTypes = exports.TransactionTypes || (exports.TransactionTypes = {}));
;
///////////////////////////////
function handleAddress(value) {
if (value === "0x") {
return null;
}
return (0, address_1.getAddress)(value);
}
function handleNumber(value) {
if (value === "0x") {
return constants_1.Zero;
}
return bignumber_1.BigNumber.from(value);
}
// Legacy Transaction Fields
var transactionFields = [
{ name: "nonce", maxLength: 32, numeric: true },
{ name: "gasPrice", maxLength: 32, numeric: true },
{ name: "gasLimit", maxLength: 32, numeric: true },
{ name: "to", length: 20 },
{ name: "value", maxLength: 32, numeric: true },
{ name: "data" },
];
var allowedTransactionKeys = {
chainId: true, data: true, gasLimit: true, gasPrice: true, nonce: true, to: true, type: true, value: true
};
function computeAddress(key) {
var publicKey = (0, signing_key_1.computePublicKey)(key);
return (0, address_1.getAddress)((0, bytes_1.hexDataSlice)((0, keccak256_1.keccak256)((0, bytes_1.hexDataSlice)(publicKey, 1)), 12));
}
exports.computeAddress = computeAddress;
function recoverAddress(digest, signature) {
return computeAddress((0, signing_key_1.recoverPublicKey)((0, bytes_1.arrayify)(digest), signature));
}
exports.recoverAddress = recoverAddress;
function formatNumber(value, name) {
var result = (0, bytes_1.stripZeros)(bignumber_1.BigNumber.from(value).toHexString());
if (result.length > 32) {
logger.throwArgumentError("invalid length for " + name, ("transaction:" + name), value);
}
return result;
}
function accessSetify(addr, storageKeys) {
return {
address: (0, address_1.getAddress)(addr),
storageKeys: (storageKeys || []).map(function (storageKey, index) {
if ((0, bytes_1.hexDataLength)(storageKey) !== 32) {
logger.throwArgumentError("invalid access list storageKey", "accessList[" + addr + ":" + index + "]", storageKey);
}
return storageKey.toLowerCase();
})
};
}
function accessListify(value) {
if (Array.isArray(value)) {
return value.map(function (set, index) {
if (Array.isArray(set)) {
if (set.length > 2) {
logger.throwArgumentError("access list expected to be [ address, storageKeys[] ]", "value[" + index + "]", set);
}
return accessSetify(set[0], set[1]);
}
return accessSetify(set.address, set.storageKeys);
});
}
var result = Object.keys(value).map(function (addr) {
var storageKeys = value[addr].reduce(function (accum, storageKey) {
accum[storageKey] = true;
return accum;
}, {});
return accessSetify(addr, Object.keys(storageKeys).sort());
});
result.sort(function (a, b) { return (a.address.localeCompare(b.address)); });
return result;
}
exports.accessListify = accessListify;
function formatAccessList(value) {
return accessListify(value).map(function (set) { return [set.address, set.storageKeys]; });
}
function _serializeEip1559(transaction, signature) {
// If there is an explicit gasPrice, make sure it matches the
// EIP-1559 fees; otherwise they may not understand what they
// think they are setting in terms of fee.
if (transaction.gasPrice != null) {
var gasPrice = bignumber_1.BigNumber.from(transaction.gasPrice);
var maxFeePerGas = bignumber_1.BigNumber.from(transaction.maxFeePerGas || 0);
if (!gasPrice.eq(maxFeePerGas)) {
logger.throwArgumentError("mismatch EIP-1559 gasPrice != maxFeePerGas", "tx", {
gasPrice: gasPrice,
maxFeePerGas: maxFeePerGas
});
}
}
var fields = [
formatNumber(transaction.chainId || 0, "chainId"),
formatNumber(transaction.nonce || 0, "nonce"),
formatNumber(transaction.maxPriorityFeePerGas || 0, "maxPriorityFeePerGas"),
formatNumber(transaction.maxFeePerGas || 0, "maxFeePerGas"),
formatNumber(transaction.gasLimit || 0, "gasLimit"),
((transaction.to != null) ? (0, address_1.getAddress)(transaction.to) : "0x"),
formatNumber(transaction.value || 0, "value"),
(transaction.data || "0x"),
(formatAccessList(transaction.accessList || []))
];
if (signature) {
var sig = (0, bytes_1.splitSignature)(signature);
fields.push(formatNumber(sig.recoveryParam, "recoveryParam"));
fields.push((0, bytes_1.stripZeros)(sig.r));
fields.push((0, bytes_1.stripZeros)(sig.s));
}
return (0, bytes_1.hexConcat)(["0x02", RLP.encode(fields)]);
}
function _serializeEip2930(transaction, signature) {
var fields = [
formatNumber(transaction.chainId || 0, "chainId"),
formatNumber(transaction.nonce || 0, "nonce"),
formatNumber(transaction.gasPrice || 0, "gasPrice"),
formatNumber(transaction.gasLimit || 0, "gasLimit"),
((transaction.to != null) ? (0, address_1.getAddress)(transaction.to) : "0x"),
formatNumber(transaction.value || 0, "value"),
(transaction.data || "0x"),
(formatAccessList(transaction.accessList || []))
];
if (signature) {
var sig = (0, bytes_1.splitSignature)(signature);
fields.push(formatNumber(sig.recoveryParam, "recoveryParam"));
fields.push((0, bytes_1.stripZeros)(sig.r));
fields.push((0, bytes_1.stripZeros)(sig.s));
}
return (0, bytes_1.hexConcat)(["0x01", RLP.encode(fields)]);
}
// Legacy Transactions and EIP-155
function _serialize(transaction, signature) {
(0, properties_1.checkProperties)(transaction, allowedTransactionKeys);
var raw = [];
transactionFields.forEach(function (fieldInfo) {
var value = transaction[fieldInfo.name] || ([]);
var options = {};
if (fieldInfo.numeric) {
options.hexPad = "left";
}
value = (0, bytes_1.arrayify)((0, bytes_1.hexlify)(value, options));
// Fixed-width field
if (fieldInfo.length && value.length !== fieldInfo.length && value.length > 0) {
logger.throwArgumentError("invalid length for " + fieldInfo.name, ("transaction:" + fieldInfo.name), value);
}
// Variable-width (with a maximum)
if (fieldInfo.maxLength) {
value = (0, bytes_1.stripZeros)(value);
if (value.length > fieldInfo.maxLength) {
logger.throwArgumentError("invalid length for " + fieldInfo.name, ("transaction:" + fieldInfo.name), value);
}
}
raw.push((0, bytes_1.hexlify)(value));
});
var chainId = 0;
if (transaction.chainId != null) {
// A chainId was provided; if non-zero we'll use EIP-155
chainId = transaction.chainId;
if (typeof (chainId) !== "number") {
logger.throwArgumentError("invalid transaction.chainId", "transaction", transaction);
}
}
else if (signature && !(0, bytes_1.isBytesLike)(signature) && signature.v > 28) {
// No chainId provided, but the signature is signing with EIP-155; derive chainId
chainId = Math.floor((signature.v - 35) / 2);
}
// We have an EIP-155 transaction (chainId was specified and non-zero)
if (chainId !== 0) {
raw.push((0, bytes_1.hexlify)(chainId)); // @TODO: hexValue?
raw.push("0x");
raw.push("0x");
}
// Requesting an unsigned transaction
if (!signature) {
return RLP.encode(raw);
}
// The splitSignature will ensure the transaction has a recoveryParam in the
// case that the signTransaction function only adds a v.
var sig = (0, bytes_1.splitSignature)(signature);
// We pushed a chainId and null r, s on for hashing only; remove those
var v = 27 + sig.recoveryParam;
if (chainId !== 0) {
raw.pop();
raw.pop();
raw.pop();
v += chainId * 2 + 8;
// If an EIP-155 v (directly or indirectly; maybe _vs) was provided, check it!
if (sig.v > 28 && sig.v !== v) {
logger.throwArgumentError("transaction.chainId/signature.v mismatch", "signature", signature);
}
}
else if (sig.v !== v) {
logger.throwArgumentError("transaction.chainId/signature.v mismatch", "signature", signature);
}
raw.push((0, bytes_1.hexlify)(v));
raw.push((0, bytes_1.stripZeros)((0, bytes_1.arrayify)(sig.r)));
raw.push((0, bytes_1.stripZeros)((0, bytes_1.arrayify)(sig.s)));
return RLP.encode(raw);
}
function serialize(transaction, signature) {
// Legacy and EIP-155 Transactions
if (transaction.type == null || transaction.type === 0) {
if (transaction.accessList != null) {
logger.throwArgumentError("untyped transactions do not support accessList; include type: 1", "transaction", transaction);
}
return _serialize(transaction, signature);
}
// Typed Transactions (EIP-2718)
switch (transaction.type) {
case 1:
return _serializeEip2930(transaction, signature);
case 2:
return _serializeEip1559(transaction, signature);
default:
break;
}
return logger.throwError("unsupported transaction type: " + transaction.type, logger_1.Logger.errors.UNSUPPORTED_OPERATION, {
operation: "serializeTransaction",
transactionType: transaction.type
});
}
exports.serialize = serialize;
function _parseEipSignature(tx, fields, serialize) {
try {
var recid = handleNumber(fields[0]).toNumber();
if (recid !== 0 && recid !== 1) {
throw new Error("bad recid");
}
tx.v = recid;
}
catch (error) {
logger.throwArgumentError("invalid v for transaction type: 1", "v", fields[0]);
}
tx.r = (0, bytes_1.hexZeroPad)(fields[1], 32);
tx.s = (0, bytes_1.hexZeroPad)(fields[2], 32);
try {
var digest = (0, keccak256_1.keccak256)(serialize(tx));
tx.from = recoverAddress(digest, { r: tx.r, s: tx.s, recoveryParam: tx.v });
}
catch (error) {
console.log(error);
}
}
function _parseEip1559(payload) {
var transaction = RLP.decode(payload.slice(1));
if (transaction.length !== 9 && transaction.length !== 12) {
logger.throwArgumentError("invalid component count for transaction type: 2", "payload", (0, bytes_1.hexlify)(payload));
}
var maxPriorityFeePerGas = handleNumber(transaction[2]);
var maxFeePerGas = handleNumber(transaction[3]);
var tx = {
type: 2,
chainId: handleNumber(transaction[0]).toNumber(),
nonce: handleNumber(transaction[1]).toNumber(),
maxPriorityFeePerGas: maxPriorityFeePerGas,
maxFeePerGas: maxFeePerGas,
gasPrice: null,
gasLimit: handleNumber(transaction[4]),
to: handleAddress(transaction[5]),
value: handleNumber(transaction[6]),
data: transaction[7],
accessList: accessListify(transaction[8]),
};
// Unsigned EIP-1559 Transaction
if (transaction.length === 9) {
return tx;
}
tx.hash = (0, keccak256_1.keccak256)(payload);
_parseEipSignature(tx, transaction.slice(9), _serializeEip1559);
return tx;
}
function _parseEip2930(payload) {
var transaction = RLP.decode(payload.slice(1));
if (transaction.length !== 8 && transaction.length !== 11) {
logger.throwArgumentError("invalid component count for transaction type: 1", "payload", (0, bytes_1.hexlify)(payload));
}
var tx = {
type: 1,
chainId: handleNumber(transaction[0]).toNumber(),
nonce: handleNumber(transaction[1]).toNumber(),
gasPrice: handleNumber(transaction[2]),
gasLimit: handleNumber(transaction[3]),
to: handleAddress(transaction[4]),
value: handleNumber(transaction[5]),
data: transaction[6],
accessList: accessListify(transaction[7])
};
// Unsigned EIP-2930 Transaction
if (transaction.length === 8) {
return tx;
}
tx.hash = (0, keccak256_1.keccak256)(payload);
_parseEipSignature(tx, transaction.slice(8), _serializeEip2930);
return tx;
}
// Legacy Transactions and EIP-155
function _parse(rawTransaction) {
var transaction = RLP.decode(rawTransaction);
if (transaction.length !== 9 && transaction.length !== 6) {
logger.throwArgumentError("invalid raw transaction", "rawTransaction", rawTransaction);
}
var tx = {
nonce: handleNumber(transaction[0]).toNumber(),
gasPrice: handleNumber(transaction[1]),
gasLimit: handleNumber(transaction[2]),
to: handleAddress(transaction[3]),
value: handleNumber(transaction[4]),
data: transaction[5],
chainId: 0
};
// Legacy unsigned transaction
if (transaction.length === 6) {
return tx;
}
try {
tx.v = bignumber_1.BigNumber.from(transaction[6]).toNumber();
}
catch (error) {
console.log(error);
return tx;
}
tx.r = (0, bytes_1.hexZeroPad)(transaction[7], 32);
tx.s = (0, bytes_1.hexZeroPad)(transaction[8], 32);
if (bignumber_1.BigNumber.from(tx.r).isZero() && bignumber_1.BigNumber.from(tx.s).isZero()) {
// EIP-155 unsigned transaction
tx.chainId = tx.v;
tx.v = 0;
}
else {
// Signed Transaction
tx.chainId = Math.floor((tx.v - 35) / 2);
if (tx.chainId < 0) {
tx.chainId = 0;
}
var recoveryParam = tx.v - 27;
var raw = transaction.slice(0, 6);
if (tx.chainId !== 0) {
raw.push((0, bytes_1.hexlify)(tx.chainId));
raw.push("0x");
raw.push("0x");
recoveryParam -= tx.chainId * 2 + 8;
}
var digest = (0, keccak256_1.keccak256)(RLP.encode(raw));
try {
tx.from = recoverAddress(digest, { r: (0, bytes_1.hexlify)(tx.r), s: (0, bytes_1.hexlify)(tx.s), recoveryParam: recoveryParam });
}
catch (error) {
console.log(error);
}
tx.hash = (0, keccak256_1.keccak256)(rawTransaction);
}
tx.type = null;
return tx;
}
function parse(rawTransaction) {
var payload = (0, bytes_1.arrayify)(rawTransaction);
// Legacy and EIP-155 Transactions
if (payload[0] > 0x7f) {
return _parse(payload);
}
// Typed Transaction (EIP-2718)
switch (payload[0]) {
case 1:
return _parseEip2930(payload);
case 2:
return _parseEip1559(payload);
default:
break;
}
return logger.throwError("unsupported transaction type: " + payload[0], logger_1.Logger.errors.UNSUPPORTED_OPERATION, {
operation: "parseTransaction",
transactionType: payload[0]
});
}
exports.parse = parse;
export { accessListify } from "./accesslist.js";
export { Transaction } from "./transaction.js";
//# sourceMappingURL=index.js.map
MIT License
Copyright (c) 2019 Richard Moore
Copyright (c) 2022 Richard Moore

@@ -5,0 +5,0 @@ Permission is hereby granted, free of charge, to any person obtaining a copy

{
"author": "Richard Moore <me@ricmoo.com>",
"dependencies": {
"@ethersproject/address": "^5.6.0",
"@ethersproject/bignumber": "^5.6.0",
"@ethersproject/bytes": "^5.6.0",
"@ethersproject/constants": "^5.6.0",
"@ethersproject/keccak256": "^5.6.0",
"@ethersproject/logger": "^5.6.0",
"@ethersproject/properties": "^5.6.0",
"@ethersproject/rlp": "^5.6.0",
"@ethersproject/signing-key": "^5.6.0"
"@ethersproject/address": "^6.0.0-beta.1",
"@ethersproject/bytes": "^6.0.0-beta.1",
"@ethersproject/crypto": "^6.0.0-beta.1",
"@ethersproject/logger": "^6.0.0-beta.1",
"@ethersproject/math": "^6.0.0-beta.1",
"@ethersproject/properties": "^6.0.0-beta.1",
"@ethersproject/rlp": "^6.0.0-beta.1",
"@ethersproject/signing-key": "^6.0.0-beta.1"
},
"description": "Utilities for decoding and encoding Ethereum transaction for ethers.",
"description": "Ethereum Providers for ethers.",
"engines": {
"node": ">=12.17.0"
},
"ethereum": "donations.ethers.eth",
"funding": [
{
"type": "individual",
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"gitHead": "b8cda5dffdcb688e38d7c6a0aec4c7b8b59c1af5",
"gitHead": "77f691b3bc3a6387a5184ec9b1779faab4bcb30d",
"keywords": [

@@ -33,9 +25,9 @@ "Ethereum",

"main": "./lib/index.js",
"module": "./lib.esm/index.js",
"name": "@ethersproject/transactions",
"publishConfig": {
"access": "public"
"access": "public",
"tag": "beta"
},
"repository": {
"directory": "packages/transactions",
"directory": "packages/providers",
"type": "git",

@@ -48,5 +40,6 @@ "url": "git://github.com/ethers-io/ethers.js.git"

"sideEffects": false,
"tarballHash": "0x33ab660469cbf8c00155842395e7bf224c3a8e6e3334a7ec73901e49b2b19594",
"tarballHash": "0x9e2724319d7734e70945de4e7922130ca177980eafd3cabb09b5307632fc9585",
"type": "module",
"types": "./lib/index.d.ts",
"version": "5.6.0"
"version": "6.0.0-beta.1"
}

@@ -1,1 +0,1 @@

export const version = "transactions/5.6.0";
export const version = "@ethersproject/transactions@6.0.0-beta.1";

@@ -1,503 +0,6 @@

"use strict";
export { accessListify } from "./accesslist.js";
export { Transaction } from "./transaction.js";
import { getAddress } from "@ethersproject/address";
import { BigNumber, BigNumberish } from "@ethersproject/bignumber";
import { arrayify, BytesLike, DataOptions, hexConcat, hexDataLength, hexDataSlice, hexlify, hexZeroPad, isBytesLike, SignatureLike, splitSignature, stripZeros, } from "@ethersproject/bytes";
import { Zero } from "@ethersproject/constants";
import { keccak256 } from "@ethersproject/keccak256";
import { checkProperties } from "@ethersproject/properties";
import * as RLP from "@ethersproject/rlp";
import { computePublicKey, recoverPublicKey } from "@ethersproject/signing-key";
export type { AccessList, AccessListSet, AccessListish } from "./accesslist.js";
import { Logger } from "@ethersproject/logger";
import { version } from "./_version";
const logger = new Logger(version);
///////////////////////////////
// Exported Types
export type AccessList = Array<{ address: string, storageKeys: Array<string> }>;
// Input allows flexibility in describing an access list
export type AccessListish = AccessList |
Array<[ string, Array<string> ]> |
Record<string, Array<string>>;
export enum TransactionTypes {
legacy = 0,
eip2930 = 1,
eip1559 = 2,
};
export type UnsignedTransaction = {
to?: string;
nonce?: number;
gasLimit?: BigNumberish;
gasPrice?: BigNumberish;
data?: BytesLike;
value?: BigNumberish;
chainId?: number;
// Typed-Transaction features
type?: number | null;
// EIP-2930; Type 1 & EIP-1559; Type 2
accessList?: AccessListish;
// EIP-1559; Type 2
maxPriorityFeePerGas?: BigNumberish;
maxFeePerGas?: BigNumberish;
}
export interface Transaction {
hash?: string;
to?: string;
from?: string;
nonce: number;
gasLimit: BigNumber;
gasPrice?: BigNumber;
data: string;
value: BigNumber;
chainId: number;
r?: string;
s?: string;
v?: number;
// Typed-Transaction features
type?: number | null;
// EIP-2930; Type 1 & EIP-1559; Type 2
accessList?: AccessList;
// EIP-1559; Type 2
maxPriorityFeePerGas?: BigNumber;
maxFeePerGas?: BigNumber;
}
///////////////////////////////
function handleAddress(value: string): string {
if (value === "0x") { return null; }
return getAddress(value);
}
function handleNumber(value: string): BigNumber {
if (value === "0x") { return Zero; }
return BigNumber.from(value);
}
// Legacy Transaction Fields
const transactionFields = [
{ name: "nonce", maxLength: 32, numeric: true },
{ name: "gasPrice", maxLength: 32, numeric: true },
{ name: "gasLimit", maxLength: 32, numeric: true },
{ name: "to", length: 20 },
{ name: "value", maxLength: 32, numeric: true },
{ name: "data" },
];
const allowedTransactionKeys: { [ key: string ]: boolean } = {
chainId: true, data: true, gasLimit: true, gasPrice:true, nonce: true, to: true, type: true, value: true
}
export function computeAddress(key: BytesLike | string): string {
const publicKey = computePublicKey(key);
return getAddress(hexDataSlice(keccak256(hexDataSlice(publicKey, 1)), 12));
}
export function recoverAddress(digest: BytesLike, signature: SignatureLike): string {
return computeAddress(recoverPublicKey(arrayify(digest), signature));
}
function formatNumber(value: BigNumberish, name: string): Uint8Array {
const result = stripZeros(BigNumber.from(value).toHexString());
if (result.length > 32) {
logger.throwArgumentError("invalid length for " + name, ("transaction:" + name), value);
}
return result;
}
function accessSetify(addr: string, storageKeys: Array<string>): { address: string,storageKeys: Array<string> } {
return {
address: getAddress(addr),
storageKeys: (storageKeys || []).map((storageKey, index) => {
if (hexDataLength(storageKey) !== 32) {
logger.throwArgumentError("invalid access list storageKey", `accessList[${ addr }:${ index }]`, storageKey)
}
return storageKey.toLowerCase();
})
};
}
export function accessListify(value: AccessListish): AccessList {
if (Array.isArray(value)) {
return (<Array<[ string, Array<string>] | { address: string, storageKeys: Array<string>}>>value).map((set, index) => {
if (Array.isArray(set)) {
if (set.length > 2) {
logger.throwArgumentError("access list expected to be [ address, storageKeys[] ]", `value[${ index }]`, set);
}
return accessSetify(set[0], set[1])
}
return accessSetify(set.address, set.storageKeys);
});
}
const result: Array<{ address: string, storageKeys: Array<string> }> = Object.keys(value).map((addr) => {
const storageKeys: Record<string, true> = value[addr].reduce((accum, storageKey) => {
accum[storageKey] = true;
return accum;
}, <Record<string, true>>{ });
return accessSetify(addr, Object.keys(storageKeys).sort())
});
result.sort((a, b) => (a.address.localeCompare(b.address)));
return result;
}
function formatAccessList(value: AccessListish): Array<[ string, Array<string> ]> {
return accessListify(value).map((set) => [ set.address, set.storageKeys ]);
}
function _serializeEip1559(transaction: UnsignedTransaction, signature?: SignatureLike): string {
// If there is an explicit gasPrice, make sure it matches the
// EIP-1559 fees; otherwise they may not understand what they
// think they are setting in terms of fee.
if (transaction.gasPrice != null) {
const gasPrice = BigNumber.from(transaction.gasPrice);
const maxFeePerGas = BigNumber.from(transaction.maxFeePerGas || 0);
if (!gasPrice.eq(maxFeePerGas)) {
logger.throwArgumentError("mismatch EIP-1559 gasPrice != maxFeePerGas", "tx", {
gasPrice, maxFeePerGas
});
}
}
const fields: any = [
formatNumber(transaction.chainId || 0, "chainId"),
formatNumber(transaction.nonce || 0, "nonce"),
formatNumber(transaction.maxPriorityFeePerGas || 0, "maxPriorityFeePerGas"),
formatNumber(transaction.maxFeePerGas || 0, "maxFeePerGas"),
formatNumber(transaction.gasLimit || 0, "gasLimit"),
((transaction.to != null) ? getAddress(transaction.to): "0x"),
formatNumber(transaction.value || 0, "value"),
(transaction.data || "0x"),
(formatAccessList(transaction.accessList || []))
];
if (signature) {
const sig = splitSignature(signature);
fields.push(formatNumber(sig.recoveryParam, "recoveryParam"));
fields.push(stripZeros(sig.r));
fields.push(stripZeros(sig.s));
}
return hexConcat([ "0x02", RLP.encode(fields)]);
}
function _serializeEip2930(transaction: UnsignedTransaction, signature?: SignatureLike): string {
const fields: any = [
formatNumber(transaction.chainId || 0, "chainId"),
formatNumber(transaction.nonce || 0, "nonce"),
formatNumber(transaction.gasPrice || 0, "gasPrice"),
formatNumber(transaction.gasLimit || 0, "gasLimit"),
((transaction.to != null) ? getAddress(transaction.to): "0x"),
formatNumber(transaction.value || 0, "value"),
(transaction.data || "0x"),
(formatAccessList(transaction.accessList || []))
];
if (signature) {
const sig = splitSignature(signature);
fields.push(formatNumber(sig.recoveryParam, "recoveryParam"));
fields.push(stripZeros(sig.r));
fields.push(stripZeros(sig.s));
}
return hexConcat([ "0x01", RLP.encode(fields)]);
}
// Legacy Transactions and EIP-155
function _serialize(transaction: UnsignedTransaction, signature?: SignatureLike): string {
checkProperties(transaction, allowedTransactionKeys);
const raw: Array<string | Uint8Array> = [];
transactionFields.forEach(function(fieldInfo) {
let value = (<any>transaction)[fieldInfo.name] || ([]);
const options: DataOptions = { };
if (fieldInfo.numeric) { options.hexPad = "left"; }
value = arrayify(hexlify(value, options));
// Fixed-width field
if (fieldInfo.length && value.length !== fieldInfo.length && value.length > 0) {
logger.throwArgumentError("invalid length for " + fieldInfo.name, ("transaction:" + fieldInfo.name), value);
}
// Variable-width (with a maximum)
if (fieldInfo.maxLength) {
value = stripZeros(value);
if (value.length > fieldInfo.maxLength) {
logger.throwArgumentError("invalid length for " + fieldInfo.name, ("transaction:" + fieldInfo.name), value );
}
}
raw.push(hexlify(value));
});
let chainId = 0;
if (transaction.chainId != null) {
// A chainId was provided; if non-zero we'll use EIP-155
chainId = transaction.chainId;
if (typeof(chainId) !== "number") {
logger.throwArgumentError("invalid transaction.chainId", "transaction", transaction);
}
} else if (signature && !isBytesLike(signature) && signature.v > 28) {
// No chainId provided, but the signature is signing with EIP-155; derive chainId
chainId = Math.floor((signature.v - 35) / 2);
}
// We have an EIP-155 transaction (chainId was specified and non-zero)
if (chainId !== 0) {
raw.push(hexlify(chainId)); // @TODO: hexValue?
raw.push("0x");
raw.push("0x");
}
// Requesting an unsigned transaction
if (!signature) {
return RLP.encode(raw);
}
// The splitSignature will ensure the transaction has a recoveryParam in the
// case that the signTransaction function only adds a v.
const sig = splitSignature(signature);
// We pushed a chainId and null r, s on for hashing only; remove those
let v = 27 + sig.recoveryParam
if (chainId !== 0) {
raw.pop();
raw.pop();
raw.pop();
v += chainId * 2 + 8;
// If an EIP-155 v (directly or indirectly; maybe _vs) was provided, check it!
if (sig.v > 28 && sig.v !== v) {
logger.throwArgumentError("transaction.chainId/signature.v mismatch", "signature", signature);
}
} else if (sig.v !== v) {
logger.throwArgumentError("transaction.chainId/signature.v mismatch", "signature", signature);
}
raw.push(hexlify(v));
raw.push(stripZeros(arrayify(sig.r)));
raw.push(stripZeros(arrayify(sig.s)));
return RLP.encode(raw);
}
export function serialize(transaction: UnsignedTransaction, signature?: SignatureLike): string {
// Legacy and EIP-155 Transactions
if (transaction.type == null || transaction.type === 0) {
if (transaction.accessList != null) {
logger.throwArgumentError("untyped transactions do not support accessList; include type: 1", "transaction", transaction);
}
return _serialize(transaction, signature);
}
// Typed Transactions (EIP-2718)
switch (transaction.type) {
case 1:
return _serializeEip2930(transaction, signature);
case 2:
return _serializeEip1559(transaction, signature);
default:
break;
}
return logger.throwError(`unsupported transaction type: ${ transaction.type }`, Logger.errors.UNSUPPORTED_OPERATION, {
operation: "serializeTransaction",
transactionType: transaction.type
});
}
function _parseEipSignature(tx: Transaction, fields: Array<string>, serialize: (tx: UnsignedTransaction) => string): void {
try {
const recid = handleNumber(fields[0]).toNumber();
if (recid !== 0 && recid !== 1) { throw new Error("bad recid"); }
tx.v = recid;
} catch (error) {
logger.throwArgumentError("invalid v for transaction type: 1", "v", fields[0]);
}
tx.r = hexZeroPad(fields[1], 32);
tx.s = hexZeroPad(fields[2], 32);
try {
const digest = keccak256(serialize(tx));
tx.from = recoverAddress(digest, { r: tx.r, s: tx.s, recoveryParam: tx.v });
} catch (error) {
console.log(error);
}
}
function _parseEip1559(payload: Uint8Array): Transaction {
const transaction = RLP.decode(payload.slice(1));
if (transaction.length !== 9 && transaction.length !== 12) {
logger.throwArgumentError("invalid component count for transaction type: 2", "payload", hexlify(payload));
}
const maxPriorityFeePerGas = handleNumber(transaction[2]);
const maxFeePerGas = handleNumber(transaction[3]);
const tx: Transaction = {
type: 2,
chainId: handleNumber(transaction[0]).toNumber(),
nonce: handleNumber(transaction[1]).toNumber(),
maxPriorityFeePerGas: maxPriorityFeePerGas,
maxFeePerGas: maxFeePerGas,
gasPrice: null,
gasLimit: handleNumber(transaction[4]),
to: handleAddress(transaction[5]),
value: handleNumber(transaction[6]),
data: transaction[7],
accessList: accessListify(transaction[8]),
};
// Unsigned EIP-1559 Transaction
if (transaction.length === 9) { return tx; }
tx.hash = keccak256(payload);
_parseEipSignature(tx, transaction.slice(9), _serializeEip1559);
return tx;
}
function _parseEip2930(payload: Uint8Array): Transaction {
const transaction = RLP.decode(payload.slice(1));
if (transaction.length !== 8 && transaction.length !== 11) {
logger.throwArgumentError("invalid component count for transaction type: 1", "payload", hexlify(payload));
}
const tx: Transaction = {
type: 1,
chainId: handleNumber(transaction[0]).toNumber(),
nonce: handleNumber(transaction[1]).toNumber(),
gasPrice: handleNumber(transaction[2]),
gasLimit: handleNumber(transaction[3]),
to: handleAddress(transaction[4]),
value: handleNumber(transaction[5]),
data: transaction[6],
accessList: accessListify(transaction[7])
};
// Unsigned EIP-2930 Transaction
if (transaction.length === 8) { return tx; }
tx.hash = keccak256(payload);
_parseEipSignature(tx, transaction.slice(8), _serializeEip2930);
return tx;
}
// Legacy Transactions and EIP-155
function _parse(rawTransaction: Uint8Array): Transaction {
const transaction = RLP.decode(rawTransaction);
if (transaction.length !== 9 && transaction.length !== 6) {
logger.throwArgumentError("invalid raw transaction", "rawTransaction", rawTransaction);
}
const tx: Transaction = {
nonce: handleNumber(transaction[0]).toNumber(),
gasPrice: handleNumber(transaction[1]),
gasLimit: handleNumber(transaction[2]),
to: handleAddress(transaction[3]),
value: handleNumber(transaction[4]),
data: transaction[5],
chainId: 0
};
// Legacy unsigned transaction
if (transaction.length === 6) { return tx; }
try {
tx.v = BigNumber.from(transaction[6]).toNumber();
} catch (error) {
console.log(error);
return tx;
}
tx.r = hexZeroPad(transaction[7], 32);
tx.s = hexZeroPad(transaction[8], 32);
if (BigNumber.from(tx.r).isZero() && BigNumber.from(tx.s).isZero()) {
// EIP-155 unsigned transaction
tx.chainId = tx.v;
tx.v = 0;
} else {
// Signed Transaction
tx.chainId = Math.floor((tx.v - 35) / 2);
if (tx.chainId < 0) { tx.chainId = 0; }
let recoveryParam = tx.v - 27;
const raw = transaction.slice(0, 6);
if (tx.chainId !== 0) {
raw.push(hexlify(tx.chainId));
raw.push("0x");
raw.push("0x");
recoveryParam -= tx.chainId * 2 + 8;
}
const digest = keccak256(RLP.encode(raw));
try {
tx.from = recoverAddress(digest, { r: hexlify(tx.r), s: hexlify(tx.s), recoveryParam: recoveryParam });
} catch (error) {
console.log(error);
}
tx.hash = keccak256(rawTransaction);
}
tx.type = null;
return tx;
}
export function parse(rawTransaction: BytesLike): Transaction {
const payload = arrayify(rawTransaction);
// Legacy and EIP-155 Transactions
if (payload[0] > 0x7f) { return _parse(payload); }
// Typed Transaction (EIP-2718)
switch (payload[0]) {
case 1:
return _parseEip2930(payload);
case 2:
return _parseEip1559(payload);
default:
break;
}
return logger.throwError(`unsupported transaction type: ${ payload[0] }`, Logger.errors.UNSUPPORTED_OPERATION, {
operation: "parseTransaction",
transactionType: payload[0]
});
}
export type { SignedTransaction, TransactionLike } from "./transaction.js";

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc