UNPKG

@kaiachain/web3js-ext

Version:
197 lines (196 loc) 9.16 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (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.KlaytnTypedTransaction = exports.secp256k1 = void 0; const rlp_1 = require("@ethereumjs/rlp"); const js_ext_core_1 = require("@kaiachain/js-ext-core"); const keccak_js_1 = require("ethereum-cryptography/keccak.js"); const ethereumCryptography = __importStar(require("ethereum-cryptography/secp256k1.js")); const web3_eth_accounts_1 = require("web3-eth-accounts"); const web3_utils_1 = require("web3-utils"); exports.secp256k1 = ethereumCryptography.secp256k1 ?? ethereumCryptography; // Mimics the LegacyTransaction. // See web3-eth-accounts/src/tx/legacyTransaction.ts // and web3-eth-accounts/src/tx/baseTransaction.ts class KlaytnTypedTransaction extends web3_eth_accounts_1.Transaction { get type() { return this._klaytnType; } // This constructor creates a frozen read-only transaction object out of TxData. // Any modifications to the fields (e.g. adding the signature) should involve // the construction of a new object, rather than modifying the fields directly. constructor(txData, txOptions = {}) { // Allow adding new fields inside constructor. const savedFreeze = txOptions.freeze; txOptions.freeze = false; // Construct LegacyTransaction and parse TxData fields super(txData, txOptions); // Parse KlaytnTxData fields if (!txData.type) { // Should not reach here because KlaytnTx is selected via explicit type field. throw new Error("Missing 'type' field"); } this._klaytnType = (0, web3_utils_1.toNumber)(txData.type); this.from = txData.from; this.chainId = txData.chainId; this.key = txData.key; this.feePayer = txData.feePayer; this.feePayer_v = txData.feePayer_v; this.feePayer_r = txData.feePayer_r; this.feePayer_s = txData.feePayer_s; this.txSignatures = txData.txSignatures; this.feePayerSignatures = txData.feePayerSignatures; this.feeRatio = txData.feeRatio; // Build the inner CoreKlaytnTx object. // Convert to type understood by CoreKlaytnTx. const klaytnTxObject = { type: (0, web3_utils_1.toHex)(this.type || 0), nonce: (0, web3_utils_1.toHex)(this.nonce), gasPrice: (0, web3_utils_1.toHex)(this.gasPrice), gasLimit: (0, web3_utils_1.toHex)(this.gasLimit), to: this.to ? (0, web3_utils_1.bytesToHex)(this.to.toString()) : undefined, value: (0, web3_utils_1.toHex)(this.value), from: this.from, data: (0, web3_utils_1.bytesToHex)(this.data), input: (0, web3_utils_1.bytesToHex)(this.data), chainId: this.chainId ? (0, web3_utils_1.toHex)(this.chainId) : undefined, humanReadable: false, codeFormat: 0x00, key: this.key, feePayer: this.feePayer, txSignatures: this.txSignatures, feePayerSignatures: this.feePayerSignatures, feeRatio: this.feeRatio, }; if (txData.type == js_ext_core_1.TxType.SmartContractDeploy || txData.type == js_ext_core_1.TxType.FeeDelegatedSmartContractDeploy || txData.type == js_ext_core_1.TxType.FeeDelegatedSmartContractDeployWithRatio) { klaytnTxObject.to = "0x0000000000000000000000000000000000000000"; } this.coreKlaytnTx = js_ext_core_1.KlaytnTxFactory.fromObject(klaytnTxObject); if (this.v && this.r && this.s) { this.coreKlaytnTx.addSenderSig({ v: Number(this.v), r: (0, web3_utils_1.numberToHex)(this.r), s: (0, web3_utils_1.numberToHex)(this.s), }); } if (this.feePayer_v && this.feePayer_r && this.feePayer_s) { this.coreKlaytnTx.addFeePayerSig({ v: Number(this.feePayer_v), r: (0, web3_utils_1.numberToHex)(this.feePayer_r), s: (0, web3_utils_1.numberToHex)(this.feePayer_s), }); } // Resume the freeze behavior at the end of LegacyTransaction.constructor(). this.txOptions.freeze = savedFreeze; if (this.txOptions.freeze ?? true) { Object.freeze(this); } } getMessageToSign(hashMessage = true) { const rlp = (0, web3_utils_1.hexToBytes)(this.coreKlaytnTx.sigRLP()); if (hashMessage) { return (0, keccak_js_1.keccak256)(rlp); // Hashed Uint8Array } else { return rlp_1.RLP.decode(rlp); // RLP-decoded Uint8Array[] } } getMessageToSignAsFeePayer(hashMessage = true) { const rlp = (0, web3_utils_1.hexToBytes)(this.coreKlaytnTx.sigFeePayerRLP()); if (hashMessage) { return (0, keccak_js_1.keccak256)(rlp); // Hashed Uint8Array } else { return rlp_1.RLP.decode(rlp); // RLP-decoded Uint8Array[] } } // Return a new KlaytnTx object with the (v,r,s) signature added. sign(privateKey) { if (privateKey.length !== 32) { const msg = this._errorMsg("Private key must be 32 bytes in length."); throw new Error(msg); } if (!this.chainId) { // shouldn't reach here because chainId is required in every Klaytn TxType. // The chainId should have been supplied by user or filled at prepareTransaction(). throw new Error("Missing 'chainId' field"); } const msgHash = this.getMessageToSign(true); const { r, s, v } = this._eip155sign(msgHash, privateKey, this.chainId); return new KlaytnTypedTransaction({ ...this, type: this.type, v: v, r: (0, web3_utils_1.toBigInt)((0, web3_utils_1.bytesToHex)(r)), s: (0, web3_utils_1.toBigInt)((0, web3_utils_1.bytesToHex)(s)), }, this.txOptions); } // Analogous to sign(), but uses *AsFeePayer methods. // See web3-eth-accounts/src/tx/baseTransaction.ts:sign() signAsFeePayer(privateKey) { if (privateKey.length !== 32) { const msg = this._errorMsg("Private key must be 32 bytes in length."); throw new Error(msg); } if (!this.chainId) { // shouldn't reach here because chainId is required in every Klaytn TxType. // The chainId should have been supplied by user or filled at prepareTransaction(). throw new Error("Missing 'chainId' field"); } const msgHash = this.getMessageToSignAsFeePayer(true); const { v, r, s } = this._eip155sign(msgHash, privateKey, this.chainId); return new KlaytnTypedTransaction({ ...this, type: this.type, feePayer_v: v, feePayer_r: (0, web3_utils_1.toBigInt)((0, web3_utils_1.bytesToHex)(r)), feePayer_s: (0, web3_utils_1.toBigInt)((0, web3_utils_1.bytesToHex)(s)), }, this.txOptions); } // Return the sender-signed raw transaction. i.e. SenderTxHashRLP or TxHashRLP serialize() { if ((0, js_ext_core_1.isFeePayerSigTxType)(this.type)) { return (0, web3_utils_1.hexToBytes)(this.coreKlaytnTx.senderTxHashRLP()); } return (0, web3_utils_1.hexToBytes)(this.coreKlaytnTx.txHashRLP()); } // Return the feePayer-signed raw transaction. i.e. TxHashRLP serializeAsFeePayer() { return (0, web3_utils_1.hexToBytes)(this.coreKlaytnTx.txHashRLP()); } // Recreating BaseTransaction._ecsign because that's private. _eip155sign(msgHash, privateKey, chainId) { const signature = exports.secp256k1.sign(msgHash, privateKey); const signatureBytes = signature.toCompactRawBytes(); const r = signatureBytes.subarray(0, 32); const s = signatureBytes.subarray(32, 64); const recoveryParam = signature.recovery; const v = BigInt(recoveryParam + 35) + chainId * BigInt(2); return { v, r, s }; } } exports.KlaytnTypedTransaction = KlaytnTypedTransaction;