@secux/app-eth
Version:
SecuX Hardware Wallet ETH API
18 lines (15 loc) • 7.66 kB
JavaScript
;
/*!
Copyright 2022 SecuX Technology Inc
Copyright Chen Wei-En
Copyright Wu Tsung-Yu
Licensed under the Apache License, Version 2.0 (the License);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an AS IS BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/var __createBinding=this&&this.__createBinding||(Object.create?function(o,m,k,k2){void 0===k2&&(k2=k);var desc=Object.getOwnPropertyDescriptor(m,k);desc&&!("get"in desc?!m.__esModule:desc.writable||desc.configurable)||(desc={enumerable:!0,get:function(){return m[k]}}),Object.defineProperty(o,k2,desc)}:function(o,m,k,k2){void 0===k2&&(k2=k),o[k2]=m[k]}),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?function(o,v){Object.defineProperty(o,"default",{enumerable:!0,value:v})}:function(o,v){o.default=v}),__importStar=this&&this.__importStar||function(mod){if(mod&&mod.__esModule)return mod;var result={};if(null!=mod)for(var k in mod)"default"!==k&&Object.prototype.hasOwnProperty.call(mod,k)&&__createBinding(result,mod,k);return __setModuleDefault(result,mod),result},__importDefault=this&&this.__importDefault||function(mod){return mod&&mod.__esModule?mod:{default:mod}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.EIP1559Builder=exports.ETHTransactionBuilder=exports.getBuilder=void 0;const secp256k1=require("secp256k1/elliptic"),js_sha3_1=require("js-sha3"),rlp=__importStar(require("rlp")),bignumber_js_1=require("bignumber.js"),ow_1=__importDefault(require("ow")),interface_1=require("./interface"),utility_1=require("@secux/utility"),logger=null===utility_1.Logger||void 0===utility_1.Logger?void 0:utility_1.Logger.child({id:"ethereum"}),EIP1559_TransactionType=Buffer.from([2]);exports.getBuilder=function(data){try{return(0,ow_1.default)(data,interface_1.ow_tx1559),new EIP1559Builder(data)}catch(error){}return new ETHTransactionBuilder(data)};const __tx=new WeakMap;class ETHTransactionBuilder{static deserialize(serialized){if(serialized[0]>=192){const values=rlp.decode(serialized);if(!Array.isArray(values))throw new Error("Invalid serialized tx input. Must be array");if(6!==values.length&&9!==values.length)throw new Error("Invalid transaction. Only expecting unsigned tx with 6 values (legacy) or 9 values (EIP155).");const[nonce,gasPrice,gasLimit,to,value,data,v,r,s]=values,_v=parseInt(Buffer.from(v).toString("hex"),16);let chainId;return[0,27,28].includes(_v)||(chainId=v),new ETHTransactionBuilder({nonce,gasPrice,gasLimit,to,value,data,chainId})}if(serialized[0]<=127){const values=rlp.decode(serialized.slice(1));if(!Array.isArray(values))throw new Error("Invalid serialized tx input. Must be array");if(9!==values.length&&12!==values.length)throw new Error("Invalid transaction. Only expecting unsigned tx with 9 or 12 values (EIP1559).");const[chainId,nonce,maxPriorityFeePerGas,maxFeePerGas,gasLimit,to,value,data]=values;return new EIP1559Builder({chainId,nonce,maxPriorityFeePerGas,maxFeePerGas,gasLimit,to,value,data})}throw Error(`Invalid serialized tx input, got "${serialized.toString("hex")}"`)}constructor(tx){const _tx=Object.assign({},tx);if(__tx.set(this,_tx),"string"==typeof tx.chainId){let str=tx.chainId.slice(2);str.length%2!=0&&(str=`0${str}`),_tx.chainId=Buffer.from(str,"hex")}else if("number"==typeof tx.chainId){let str=tx.chainId.toString(16);str.length%2!=0&&(str=`0${str}`),_tx.chainId=Buffer.from(str,"hex")}if(tx.gasPrice){const gasPrice=getValue(tx.gasPrice);gasPrice.lt(1)&&(null==logger||logger.warn(`Minimal gas price is 1 wei, but got ${gasPrice.toString()} wei.`))}let estimatedGas=21e3;tx.data&&("string"==typeof tx.data?estimatedGas+=68*(tx.data.length/2-1):estimatedGas+=68*tx.data.length);const gasLimit=tx.gasLimit||tx.gas;if(_tx.gasLimit=gasLimit,gasLimit){const _gasLimit=getValue(gasLimit);_gasLimit.lt(estimatedGas)&&(null==logger||logger.warn(`Minimal gas is ${estimatedGas}, but got ${_gasLimit.toString()}.`))}}serialize(toHash=!1){var _a;const transaction=[...this.prepare(),handleRLPValue(null!==(_a=this.tx.chainId)&&void 0!==_a?_a:0),0,0],encoded=rlp.encode(transaction);return toHash?Buffer.from(js_sha3_1.keccak256.update(encoded).digest()):encoded}withSignature(sig){if(!this.verify(sig))throw Error("invalid signature");sig=this.getSignature(sig);const transaction=[...this.prepare(),handleRLPValue(sig.slice(64)),trimZeroForRLP(sig.slice(0,32)),trimZeroForRLP(sig.slice(32,64))];return rlp.encode(transaction)}getSignature(sig){var _a;const chainId=parseInt(Buffer.from(null!==(_a=this.tx.chainId)&&void 0!==_a?_a:[0]).toString("hex"),16),offset=chainId>0?2*chainId+35:27;let v_hex=(sig[64]+offset).toString(16);v_hex.length%2!=0&&(v_hex=`0${v_hex}`);const v=Buffer.from(v_hex,"hex");return Buffer.concat([sig.slice(0,64),v])}prepare(){var _a;return[handleRLPValue(this.tx.nonce),handleRLPValue(this.tx.gasPrice),handleRLPValue(this.tx.gasLimit),this.tx.to,handleRLPValue(this.tx.value),null!==(_a=this.tx.data)&&void 0!==_a?_a:""]}verify(data){const sig=data.slice(0,64);try{secp256k1.ecdsaRecover(sig,data.readUint8(64),this.serialize(!0))}catch(error){return!1}return!0}get chainId(){if(this.tx.chainId)return parseInt(Buffer.from(this.tx.chainId).toString("hex"),16)}get tx(){return __tx.get(this)}}exports.ETHTransactionBuilder=ETHTransactionBuilder;class EIP1559Builder extends ETHTransactionBuilder{constructor(tx){super(tx);const _tx=__tx.get(this),priorityFee=tx.maxPriorityFeePerGas||tx.priorityFee;if(_tx.maxPriorityFeePerGas=priorityFee,priorityFee){getValue(priorityFee).lt(1)&&(null==logger||logger.warn("[maxPriorityFeePerGas] Minimal priority fee is 1 wei."))}const maxFee=tx.maxFeePerGas||tx.gasPrice;if(_tx.maxFeePerGas=maxFee,maxFee){const _maxFee=getValue(maxFee);_maxFee.lt(1)&&(null==logger||logger.warn(`[maxFeePerGas] Minimal fee is 1 wei, but got ${_maxFee.toString()} wei.`))}}serialize(toHash=!1){const transaction=this.prepare(),encoded=Buffer.concat([EIP1559_TransactionType,rlp.encode(transaction)]);return toHash?Buffer.from(js_sha3_1.keccak256.update(encoded).digest()):encoded}withSignature(sig){if(!this.verify(sig))throw Error("invalid signature");const transaction=[...this.prepare(),handleRLPValue(sig[64]),trimZeroForRLP(sig.slice(0,32)),trimZeroForRLP(sig.slice(32,64))],encoded=rlp.encode(transaction);return Buffer.concat([EIP1559_TransactionType,encoded])}getSignature(sig){return sig}prepare(){var _a,_b;return[handleRLPValue(this.tx.chainId),handleRLPValue(this.tx.nonce),handleRLPValue(this.tx.maxPriorityFeePerGas),handleRLPValue(this.tx.maxFeePerGas),handleRLPValue(this.tx.gasLimit),this.tx.to,handleRLPValue(this.tx.value),null!==(_a=this.tx.data)&&void 0!==_a?_a:"",null!==(_b=this.tx.accessList)&&void 0!==_b?_b:[]]}}function handleRLPValue(input){if("string"==typeof input){const value=(0,bignumber_js_1.BigNumber)(input);return value.isZero()?0:`0x${value.toString(16)}`}return input}function trimZeroForRLP(data){var _a;const zeros=(null!==(_a=data.toString("hex").match(/^(00)+/g))&&void 0!==_a?_a:[""])[0].length/2;return data.slice(zeros)}function getValue(data){return"number"==typeof data?new bignumber_js_1.BigNumber(data):"string"==typeof data?data.startsWith("0x")?new bignumber_js_1.BigNumber(data.slice(2),16):new bignumber_js_1.BigNumber(data):new bignumber_js_1.BigNumber(Buffer.from(data).toString("hex"),16)}exports.EIP1559Builder=EIP1559Builder;