293 lines
12 KiB
JavaScript
293 lines
12 KiB
JavaScript
"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;
|
|
};
|
|
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 assert_1 = __importDefault(require("assert"));
|
|
const bn_js_1 = __importDefault(require("bn.js"));
|
|
const rlp = __importStar(require("rlp"));
|
|
const secp256k1_1 = require("ethereum-cryptography/secp256k1");
|
|
const internal_1 = require("./internal");
|
|
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((0, bytes_1.toBuffer)(nonce)) : undefined, balance ? new bn_js_1.default((0, bytes_1.toBuffer)(balance)) : undefined, stateRoot ? (0, bytes_1.toBuffer)(stateRoot) : undefined, codeHash ? (0, 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 [
|
|
(0, types_1.bnToUnpaddedBuffer)(this.nonce),
|
|
(0, 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.
|
|
*/
|
|
const isValidAddress = function (hexAddress) {
|
|
try {
|
|
(0, helpers_1.assertIsString)(hexAddress);
|
|
}
|
|
catch (e) {
|
|
return false;
|
|
}
|
|
return /^0x[0-9a-fA-F]{40}$/.test(hexAddress);
|
|
};
|
|
exports.isValidAddress = isValidAddress;
|
|
/**
|
|
* Returns a checksummed address.
|
|
*
|
|
* If an 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 and the EIP-1191 checksum is not
|
|
* backwards compatible to the original widely adopted checksum format standard introduced in
|
|
* [EIP-55](https://eips.ethereum.org/EIPS/eip-55), so this will break in existing applications.
|
|
* Usage of this EIP is therefore discouraged unless you have a very targeted use case.
|
|
*/
|
|
const toChecksumAddress = function (hexAddress, eip1191ChainId) {
|
|
(0, helpers_1.assertIsHexString)(hexAddress);
|
|
const address = (0, internal_1.stripHexPrefix)(hexAddress).toLowerCase();
|
|
let prefix = '';
|
|
if (eip1191ChainId) {
|
|
const chainId = (0, types_1.toType)(eip1191ChainId, types_1.TypeOutput.BN);
|
|
prefix = chainId.toString() + '0x';
|
|
}
|
|
const hash = (0, 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;
|
|
};
|
|
exports.toChecksumAddress = toChecksumAddress;
|
|
/**
|
|
* Checks if the address is a valid checksummed address.
|
|
*
|
|
* See toChecksumAddress' documentation for details about the eip1191ChainId parameter.
|
|
*/
|
|
const isValidChecksumAddress = function (hexAddress, eip1191ChainId) {
|
|
return (0, exports.isValidAddress)(hexAddress) && (0, exports.toChecksumAddress)(hexAddress, eip1191ChainId) === hexAddress;
|
|
};
|
|
exports.isValidChecksumAddress = isValidChecksumAddress;
|
|
/**
|
|
* 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
|
|
*/
|
|
const generateAddress = function (from, nonce) {
|
|
(0, helpers_1.assertIsBuffer)(from);
|
|
(0, 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 (0, hash_1.rlphash)([from, null]).slice(-20);
|
|
}
|
|
// Only take the lower 160bits of the hash
|
|
return (0, hash_1.rlphash)([from, Buffer.from(nonceBN.toArray())]).slice(-20);
|
|
};
|
|
exports.generateAddress = generateAddress;
|
|
/**
|
|
* 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
|
|
*/
|
|
const generateAddress2 = function (from, salt, initCode) {
|
|
(0, helpers_1.assertIsBuffer)(from);
|
|
(0, helpers_1.assertIsBuffer)(salt);
|
|
(0, helpers_1.assertIsBuffer)(initCode);
|
|
(0, assert_1.default)(from.length === 20);
|
|
(0, assert_1.default)(salt.length === 32);
|
|
const address = (0, hash_1.keccak256)(Buffer.concat([Buffer.from('ff', 'hex'), from, salt, (0, hash_1.keccak256)(initCode)]));
|
|
return address.slice(-20);
|
|
};
|
|
exports.generateAddress2 = generateAddress2;
|
|
/**
|
|
* Checks if the private key satisfies the rules of the curve secp256k1.
|
|
*/
|
|
const isValidPrivate = function (privateKey) {
|
|
return (0, secp256k1_1.privateKeyVerify)(privateKey);
|
|
};
|
|
exports.isValidPrivate = isValidPrivate;
|
|
/**
|
|
* 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
|
|
*/
|
|
const isValidPublic = function (publicKey, sanitize = false) {
|
|
(0, helpers_1.assertIsBuffer)(publicKey);
|
|
if (publicKey.length === 64) {
|
|
// Convert to SEC1 for secp256k1
|
|
return (0, secp256k1_1.publicKeyVerify)(Buffer.concat([Buffer.from([4]), publicKey]));
|
|
}
|
|
if (!sanitize) {
|
|
return false;
|
|
}
|
|
return (0, secp256k1_1.publicKeyVerify)(publicKey);
|
|
};
|
|
exports.isValidPublic = isValidPublic;
|
|
/**
|
|
* 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
|
|
*/
|
|
const pubToAddress = function (pubKey, sanitize = false) {
|
|
(0, helpers_1.assertIsBuffer)(pubKey);
|
|
if (sanitize && pubKey.length !== 64) {
|
|
pubKey = Buffer.from((0, secp256k1_1.publicKeyConvert)(pubKey, false).slice(1));
|
|
}
|
|
(0, assert_1.default)(pubKey.length === 64);
|
|
// Only take the lower 160bits of the hash
|
|
return (0, hash_1.keccak)(pubKey).slice(-20);
|
|
};
|
|
exports.pubToAddress = pubToAddress;
|
|
exports.publicToAddress = exports.pubToAddress;
|
|
/**
|
|
* Returns the ethereum public key of a given private key.
|
|
* @param privateKey A private key must be 256 bits wide
|
|
*/
|
|
const privateToPublic = function (privateKey) {
|
|
(0, helpers_1.assertIsBuffer)(privateKey);
|
|
// skip the type flag and use the X, Y points
|
|
return Buffer.from((0, secp256k1_1.publicKeyCreate)(privateKey, false)).slice(1);
|
|
};
|
|
exports.privateToPublic = privateToPublic;
|
|
/**
|
|
* Returns the ethereum address of a given private key.
|
|
* @param privateKey A private key must be 256 bits wide
|
|
*/
|
|
const privateToAddress = function (privateKey) {
|
|
return (0, exports.publicToAddress)((0, exports.privateToPublic)(privateKey));
|
|
};
|
|
exports.privateToAddress = privateToAddress;
|
|
/**
|
|
* Converts a public key to the Ethereum format.
|
|
*/
|
|
const importPublic = function (publicKey) {
|
|
(0, helpers_1.assertIsBuffer)(publicKey);
|
|
if (publicKey.length !== 64) {
|
|
publicKey = Buffer.from((0, secp256k1_1.publicKeyConvert)(publicKey, false).slice(1));
|
|
}
|
|
return publicKey;
|
|
};
|
|
exports.importPublic = importPublic;
|
|
/**
|
|
* Returns the zero address.
|
|
*/
|
|
const zeroAddress = function () {
|
|
const addressLength = 20;
|
|
const addr = (0, bytes_1.zeros)(addressLength);
|
|
return (0, bytes_1.bufferToHex)(addr);
|
|
};
|
|
exports.zeroAddress = zeroAddress;
|
|
/**
|
|
* Checks if a given address is the zero address.
|
|
*/
|
|
const isZeroAddress = function (hexAddress) {
|
|
try {
|
|
(0, helpers_1.assertIsString)(hexAddress);
|
|
}
|
|
catch (e) {
|
|
return false;
|
|
}
|
|
const zeroAddr = (0, exports.zeroAddress)();
|
|
return zeroAddr === hexAddress;
|
|
};
|
|
exports.isZeroAddress = isZeroAddress;
|
|
//# sourceMappingURL=account.js.map
|