@exodus/ethereumjs-util
Version:
A collection of utility functions for Ethereum
290 lines • 11.4 kB
JavaScript
;
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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isZeroAddress = exports.zeroAddress = exports.importPublic = exports.privateToAddress = exports.privateToPublic = exports.publicToAddress = exports.pubToAddress = exports.isValidPublic = exports.isValidPrivate = exports.generateAddress2 = exports.generateAddress = exports.isValidChecksumAddress = exports.toChecksumAddress = exports.isValidAddress = exports.Account = void 0;
const minimalistic_assert_1 = __importDefault(require("minimalistic-assert"));
const bn_js_1 = __importDefault(require("bn.js"));
const rlp = __importStar(require("rlp"));
const secp256k1 = __importStar(require("@noble/secp256k1"));
const ethjs_util_1 = require("ethjs-util");
const constants_1 = require("./constants");
const bytes_1 = require("./bytes");
const hash_1 = require("./hash");
const helpers_1 = require("./helpers");
const types_1 = require("./types");
class Account {
/**
* This constructor assigns and validates the values.
* Use the static factory methods to assist in creating an Account from varying data types.
*/
constructor(nonce = new bn_js_1.default(0), balance = new bn_js_1.default(0), stateRoot = constants_1.KECCAK256_RLP, codeHash = constants_1.KECCAK256_NULL) {
this.nonce = nonce;
this.balance = balance;
this.stateRoot = stateRoot;
this.codeHash = codeHash;
this._validate();
}
static fromAccountData(accountData) {
const { nonce, balance, stateRoot, codeHash } = accountData;
return new Account(nonce ? new bn_js_1.default(bytes_1.toBuffer(nonce)) : undefined, balance ? new bn_js_1.default(bytes_1.toBuffer(balance)) : undefined, stateRoot ? bytes_1.toBuffer(stateRoot) : undefined, codeHash ? bytes_1.toBuffer(codeHash) : undefined);
}
static fromRlpSerializedAccount(serialized) {
const values = rlp.decode(serialized);
if (!Array.isArray(values)) {
throw new Error('Invalid serialized account input. Must be array');
}
return this.fromValuesArray(values);
}
static fromValuesArray(values) {
const [nonce, balance, stateRoot, codeHash] = values;
return new Account(new bn_js_1.default(nonce), new bn_js_1.default(balance), stateRoot, codeHash);
}
_validate() {
if (this.nonce.lt(new bn_js_1.default(0))) {
throw new Error('nonce must be greater than zero');
}
if (this.balance.lt(new bn_js_1.default(0))) {
throw new Error('balance must be greater than zero');
}
if (this.stateRoot.length !== 32) {
throw new Error('stateRoot must have a length of 32');
}
if (this.codeHash.length !== 32) {
throw new Error('codeHash must have a length of 32');
}
}
/**
* Returns a Buffer Array of the raw Buffers for the account, in order.
*/
raw() {
return [
types_1.bnToUnpaddedBuffer(this.nonce),
types_1.bnToUnpaddedBuffer(this.balance),
this.stateRoot,
this.codeHash,
];
}
/**
* Returns the RLP serialization of the account as a `Buffer`.
*/
serialize() {
return rlp.encode(this.raw());
}
/**
* Returns a `Boolean` determining if the account is a contract.
*/
isContract() {
return !this.codeHash.equals(constants_1.KECCAK256_NULL);
}
/**
* Returns a `Boolean` determining if the account is empty complying to the definition of
* account emptiness in [EIP-161](https://eips.ethereum.org/EIPS/eip-161):
* "An account is considered empty when it has no code and zero nonce and zero balance."
*/
isEmpty() {
return this.balance.isZero() && this.nonce.isZero() && this.codeHash.equals(constants_1.KECCAK256_NULL);
}
}
exports.Account = Account;
/**
* Checks if the address is a valid. Accepts checksummed addresses too.
*/
exports.isValidAddress = function (hexAddress) {
try {
helpers_1.assertIsString(hexAddress);
}
catch (e) {
return false;
}
return /^0x[0-9a-fA-F]{40}$/.test(hexAddress);
};
/**
* Returns a checksummed address.
*
* If a eip1191ChainId is provided, the chainId will be included in the checksum calculation. This
* has the effect of checksummed addresses for one chain having invalid checksums for others.
* For more details see [EIP-1191](https://eips.ethereum.org/EIPS/eip-1191).
*
* WARNING: Checksums with and without the chainId will differ. As of 2019-06-26, the most commonly
* used variation in Ethereum was without the chainId. This may change in the future.
*/
exports.toChecksumAddress = function (hexAddress, eip1191ChainId) {
helpers_1.assertIsHexString(hexAddress);
const address = ethjs_util_1.stripHexPrefix(hexAddress).toLowerCase();
let prefix = '';
if (eip1191ChainId) {
const chainId = types_1.toType(eip1191ChainId, types_1.TypeOutput.BN);
prefix = chainId.toString() + '0x';
}
const hash = hash_1.keccakFromString(prefix + address).toString('hex');
let ret = '0x';
for (let i = 0; i < address.length; i++) {
if (parseInt(hash[i], 16) >= 8) {
ret += address[i].toUpperCase();
}
else {
ret += address[i];
}
}
return ret;
};
/**
* Checks if the address is a valid checksummed address.
*
* See toChecksumAddress' documentation for details about the eip1191ChainId parameter.
*/
exports.isValidChecksumAddress = function (hexAddress, eip1191ChainId) {
return exports.isValidAddress(hexAddress) && exports.toChecksumAddress(hexAddress, eip1191ChainId) === hexAddress;
};
/**
* Generates an address of a newly created contract.
* @param from The address which is creating this new address
* @param nonce The nonce of the from account
*/
exports.generateAddress = function (from, nonce) {
helpers_1.assertIsBuffer(from);
helpers_1.assertIsBuffer(nonce);
const nonceBN = new bn_js_1.default(nonce);
if (nonceBN.isZero()) {
// in RLP we want to encode null in the case of zero nonce
// read the RLP documentation for an answer if you dare
return hash_1.rlphash([from, null]).slice(-20);
}
// Only take the lower 160bits of the hash
return hash_1.rlphash([from, Buffer.from(nonceBN.toArray())]).slice(-20);
};
/**
* Generates an address for a contract created using CREATE2.
* @param from The address which is creating this new address
* @param salt A salt
* @param initCode The init code of the contract being created
*/
exports.generateAddress2 = function (from, salt, initCode) {
helpers_1.assertIsBuffer(from);
helpers_1.assertIsBuffer(salt);
helpers_1.assertIsBuffer(initCode);
minimalistic_assert_1.default(from.length === 20);
minimalistic_assert_1.default(salt.length === 32);
const address = hash_1.keccak256(Buffer.concat([Buffer.from('ff', 'hex'), from, salt, hash_1.keccak256(initCode)]));
return address.slice(-20);
};
function publicKeyVerify(publicKey) {
helpers_1.assertIsBuffer(publicKey);
minimalistic_assert_1.default(publicKey.length === 33 || publicKey.length === 65);
try {
return Boolean(secp256k1.Point.fromHex(publicKey));
}
catch (_a) {
return false;
}
}
/**
* Checks if the private key satisfies the rules of the curve secp256k1.
*/
exports.isValidPrivate = function (privateKey) {
helpers_1.assertIsBuffer(privateKey);
minimalistic_assert_1.default(privateKey.length === 32);
return secp256k1.utils.isValidPrivateKey(privateKey);
};
/**
* Checks if the public key satisfies the rules of the curve secp256k1
* and the requirements of Ethereum.
* @param publicKey The two points of an uncompressed key, unless sanitize is enabled
* @param sanitize Accept public keys in other formats
*/
exports.isValidPublic = function (publicKey, sanitize = false) {
helpers_1.assertIsBuffer(publicKey);
if (publicKey.length === 64) {
// Convert to SEC1 for secp256k1
return publicKeyVerify(Buffer.concat([Buffer.from([4]), publicKey]));
}
if (!sanitize) {
return false;
}
return publicKeyVerify(publicKey);
};
/**
* Returns the ethereum address of a given public key.
* Accepts "Ethereum public keys" and SEC1 encoded keys.
* @param pubKey The two points of an uncompressed key, unless sanitize is enabled
* @param sanitize Accept public keys in other formats
*/
exports.pubToAddress = function (pubKey, sanitize = false) {
helpers_1.assertIsBuffer(pubKey);
if (sanitize && pubKey.length !== 64) {
pubKey = Buffer.from(secp256k1.Point.fromHex(pubKey).toRawBytes(false).slice(1));
}
minimalistic_assert_1.default(pubKey.length === 64);
// Only take the lower 160bits of the hash
return hash_1.keccak(pubKey).slice(-20);
};
exports.publicToAddress = exports.pubToAddress;
/**
* Returns the ethereum public key of a given private key.
* @param privateKey A private key must be 256 bits wide
*/
exports.privateToPublic = function (privateKey) {
helpers_1.assertIsBuffer(privateKey);
// skip the type flag and use the X, Y points
return Buffer.from(secp256k1.getPublicKey(privateKey, false)).slice(1);
};
/**
* Returns the ethereum address of a given private key.
* @param privateKey A private key must be 256 bits wide
*/
exports.privateToAddress = function (privateKey) {
return exports.publicToAddress(exports.privateToPublic(privateKey));
};
/**
* Converts a public key to the Ethereum format.
*/
exports.importPublic = function (publicKey) {
helpers_1.assertIsBuffer(publicKey);
if (publicKey.length !== 64) {
publicKey = Buffer.from(secp256k1.Point.fromHex(publicKey).toRawBytes(false).slice(1));
}
return publicKey;
};
/**
* Returns the zero address.
*/
exports.zeroAddress = function () {
const addressLength = 20;
const addr = bytes_1.zeros(addressLength);
return bytes_1.bufferToHex(addr);
};
/**
* Checks if a given address is the zero address.
*/
exports.isZeroAddress = function (hexAddress) {
try {
helpers_1.assertIsString(hexAddress);
}
catch (e) {
return false;
}
const zeroAddr = exports.zeroAddress();
return zeroAddr === hexAddress;
};
//# sourceMappingURL=account.js.map