@pgchain/blockchain-libs
Version:
PGWallet Blockchain Libs
299 lines • 13.9 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.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.Provider = void 0;
const abi_1 = require("@ethersproject/abi");
const address_1 = require("@ethersproject/address");
const bytes_1 = require("@ethersproject/bytes");
const keccak256_1 = require("@ethersproject/keccak256");
const transactions_1 = require("@ethersproject/transactions");
const eth_sig_util_1 = require("@metamask/eth-sig-util");
const js_sdk_1 = __importDefault(require("@onekeyfe/js-sdk"));
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const ethUtil = __importStar(require("ethereumjs-util"));
const bignumber_plus_1 = require("../../../basic/bignumber-plus");
const precondtion_1 = require("../../../basic/precondtion");
const abc_1 = require("../../abc");
const geth_1 = require("./geth");
const message_1 = require("./sdk/message");
class Provider extends abc_1.BaseProvider {
get geth() {
return this.clientSelector((i) => i instanceof geth_1.Geth);
}
get chainId() {
var _a, _b;
return Number((_b = (_a = this.chainInfo) === null || _a === void 0 ? void 0 : _a.implOptions) === null || _b === void 0 ? void 0 : _b.chainId);
}
verifyAddress(address) {
let isValid = false;
let checksumAddress = '';
try {
checksumAddress = (0, address_1.getAddress)(address);
isValid = checksumAddress.length === 42;
}
catch {
// pass
}
return Promise.resolve({
normalizedAddress: checksumAddress.toLowerCase() || undefined,
displayAddress: checksumAddress || undefined,
isValid,
});
}
async pubkeyToAddress(verifier, encoding) {
const pubkey = await verifier.getPubkey(false);
return '0x' + (0, keccak256_1.keccak256)(pubkey.slice(-64)).slice(-40);
}
async buildUnsignedTx(unsignedTx) {
const input = unsignedTx.inputs[0];
const output = unsignedTx.outputs[0];
const payload = unsignedTx.payload || {};
const nonce = unsignedTx.nonce;
let feeLimit = unsignedTx.feeLimit;
(0, precondtion_1.check)(typeof nonce === 'number' && nonce >= 0, 'nonce is required');
if (input && output) {
const fromAddress = input.address;
const tokenAddress = output.tokenAddress;
let toAddress = output.address;
let value = (0, bignumber_plus_1.toBigIntHex)(output.value);
let data;
if (tokenAddress) {
data =
'0xa9059cbb' +
abi_1.defaultAbiCoder
.encode(['address', 'uint256'], [toAddress, value])
.slice(2); // method_selector(transfer) + byte32_pad(address) + byte32_pad(value)
value = '0x0';
toAddress = tokenAddress;
}
else {
data = payload.data;
}
if (typeof data === 'string' && data) {
payload.data = data;
}
if (!feeLimit) {
const estimatedGasLimit = await this.geth.then((client) => client.estimateGasLimit(fromAddress, toAddress, value, data));
const estimatedGasLimitBN = (0, bignumber_plus_1.fromBigIntHex)(estimatedGasLimit);
const multiplier = this.chainInfo.implOptions['contract_gaslimit_multiplier'] || 1.2;
feeLimit =
tokenAddress ||
((await this.verifyAddress(toAddress)).isValid &&
(await this.geth.then((client) => client.isContract(toAddress))))
? estimatedGasLimitBN.multipliedBy(multiplier).integerValue()
: estimatedGasLimitBN;
}
}
feeLimit = feeLimit || new bignumber_js_1.default(21000);
let feePricePerUnit = unsignedTx.feePricePerUnit;
if (!feePricePerUnit) {
const feePrice = await this.geth.then((i) => i.getFeePricePerUnit());
const normal = feePrice.normal;
feePricePerUnit = normal.price;
if (normal.payload) {
Object.assign(payload, normal.payload);
}
}
return Object.assign(unsignedTx, {
inputs: input ? [input] : [],
outputs: output ? [output] : [],
nonce,
feeLimit,
feePricePerUnit,
payload,
});
}
async signTransaction(unsignedTx, signers) {
var _a;
const fromAddress = (_a = unsignedTx.inputs[0]) === null || _a === void 0 ? void 0 : _a.address;
(0, precondtion_1.check)(fromAddress && signers[fromAddress], 'Signer not found');
const tx = this.buildEtherUnSignedTx(unsignedTx);
const digest = (0, keccak256_1.keccak256)((0, transactions_1.serialize)(tx));
const [sig, recoveryParam] = await signers[fromAddress].sign(Buffer.from(digest.slice(2), 'hex'));
const [r, s] = [sig.slice(0, 32), sig.slice(32)];
const signature = (0, bytes_1.splitSignature)({
recoveryParam: recoveryParam,
r: (0, bytes_1.hexZeroPad)('0x' + r.toString('hex'), 32),
s: (0, bytes_1.hexZeroPad)('0x' + s.toString('hex'), 32),
});
const rawTx = (0, transactions_1.serialize)(tx, signature);
const txid = (0, keccak256_1.keccak256)(rawTx);
return { txid, rawTx };
}
buildEtherUnSignedTx(unsignedTx) {
var _a, _b, _c, _d;
const output = unsignedTx.outputs[0];
const isERC20Transfer = !!output.tokenAddress;
const toAddress = isERC20Transfer ? output.tokenAddress : output.address;
const value = isERC20Transfer ? '0x0' : (0, bignumber_plus_1.toBigIntHex)(output.value);
const baseTx = {
to: toAddress || undefined,
value,
gasLimit: (0, bignumber_plus_1.toBigIntHex)((0, precondtion_1.checkIsDefined)(unsignedTx.feeLimit)),
nonce: (0, precondtion_1.checkIsDefined)(unsignedTx.nonce),
data: ((_a = unsignedTx.payload) === null || _a === void 0 ? void 0 : _a.data) || '0x',
chainId: parseInt((0, precondtion_1.checkIsDefined)(this.chainInfo.implOptions.chainId)),
};
if (((_b = unsignedTx.payload) === null || _b === void 0 ? void 0 : _b.EIP1559Enabled) === true) {
Object.assign(baseTx, {
type: 2,
maxFeePerGas: (0, bignumber_plus_1.toBigIntHex)(new bignumber_js_1.default((0, precondtion_1.checkIsDefined)((_c = unsignedTx.payload) === null || _c === void 0 ? void 0 : _c.maxFeePerGas))),
maxPriorityFeePerGas: (0, bignumber_plus_1.toBigIntHex)(new bignumber_js_1.default((0, precondtion_1.checkIsDefined)((_d = unsignedTx.payload) === null || _d === void 0 ? void 0 : _d.maxPriorityFeePerGas))),
});
}
else {
Object.assign(baseTx, {
gasPrice: (0, bignumber_plus_1.toBigIntHex)((0, precondtion_1.checkIsDefined)(unsignedTx.feePricePerUnit)),
});
}
return baseTx;
}
async signMessage(message, signer, address) {
const messageHash = (0, message_1.hashMessage)(message.type, message.message);
const [sig, recId] = await signer.sign(ethUtil.toBuffer(messageHash));
return ethUtil.addHexPrefix(Buffer.concat([sig, Buffer.from([recId + 27])]).toString('hex'));
}
async verifyMessage(address, message, signature) {
const recoveredAddress = await this.ecRecover(message, signature);
return address.toLowerCase() === recoveredAddress.toLowerCase();
}
async ecRecover(message, signature) {
const messageHash = (0, message_1.hashMessage)(message.type, message.message);
const hashBuffer = ethUtil.toBuffer(messageHash);
const sigBuffer = ethUtil.toBuffer(signature);
(0, precondtion_1.check)(hashBuffer.length === 32, 'Invalid message hash length');
(0, precondtion_1.check)(sigBuffer.length === 65, 'Invalid signature length');
const [r, s, v] = [
sigBuffer.slice(0, 32),
sigBuffer.slice(32, 64),
sigBuffer[64],
];
const publicKey = ethUtil.ecrecover(hashBuffer, v, r, s);
return ethUtil.addHexPrefix(ethUtil.pubToAddress(publicKey).toString('hex'));
}
// The below two mm- methods are very specific functions needed to mimic a
// metamask provider.
async mmDecrypt(serializedMessage, signer) {
const encryptedData = JSON.parse(ethUtil.toBuffer(serializedMessage).toString());
return (0, eth_sig_util_1.decrypt)({
encryptedData,
privateKey: (await signer.getPrvkey()).toString('hex'),
});
}
async mmGetPublicKey(signer) {
return (0, eth_sig_util_1.getEncryptionPublicKey)((await signer.getPrvkey()).toString('hex'));
}
async hardwareGetXpubs(paths, showOnDevice) {
const resp = await this.wrapHardwareCall(() => js_sdk_1.default.ethereumGetPublicKey({
bundle: paths.map((path) => ({ path, showOnTrezor: showOnDevice })),
}));
return resp.map((i) => ({
path: i.serializedPath,
xpub: i.xpub,
}));
}
async hardwareGetAddress(path, showOnDevice, addressToVerify) {
const params = {
path,
showOnTrezor: showOnDevice,
};
if (typeof addressToVerify === 'string') {
Object.assign(params, {
address: addressToVerify,
});
}
const { address } = await this.wrapHardwareCall(() => js_sdk_1.default.ethereumGetAddress(params));
return address;
}
async hardwareSignTransaction(unsignedTx, signers) {
const { inputs: [{ address: fromAddress }], } = unsignedTx;
const signer = signers[fromAddress];
(0, precondtion_1.check)(signer, 'Signer not found');
const tx = this.buildEtherUnSignedTx(unsignedTx);
const { r, s, v } = await this.wrapHardwareCall(() => js_sdk_1.default.ethereumSignTransaction({
path: signer,
transaction: {
to: tx.to,
value: tx.value,
gasPrice: tx.gasPrice,
gasLimit: tx.gasLimit,
nonce: ethUtil.addHexPrefix(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
ethUtil.padToEven(tx.nonce.toString(16))),
data: tx.data,
chainId: tx.chainId,
},
}));
const signature = (0, bytes_1.splitSignature)({
v: Number(v),
r,
s,
});
const rawTx = (0, transactions_1.serialize)(tx, signature);
const txid = (0, keccak256_1.keccak256)(rawTx);
return { txid, rawTx };
}
async hardwareSignMessage(message, signer) {
const { type: messageType, message: strMessage } = message;
switch (messageType) {
case message_1.MessageTypes.ETH_SIGN:
throw new Error('eth_sign is not supported for hardware');
case message_1.MessageTypes.TYPE_DATA_V1:
throw new Error('signTypedData_v1 is not supported for hardware');
case message_1.MessageTypes.PERSONAL_SIGN: {
const { signature } = await this.wrapHardwareCall(() => js_sdk_1.default.ethereumSignMessage({
path: signer,
message: strMessage,
}));
return ethUtil.addHexPrefix(signature);
}
case message_1.MessageTypes.TYPE_DATA_V3:
case message_1.MessageTypes.TYPE_DATA_V4: {
const { signature } = await this.wrapHardwareCall(() =>
// @ts-ignore
js_sdk_1.default.ethereumSignMessageEIP712({
path: signer,
version: messageType === message_1.MessageTypes.TYPE_DATA_V3 ? 'V3' : 'V4',
data: JSON.parse(strMessage),
}));
return ethUtil.addHexPrefix(signature);
}
}
throw new Error(`Not supported`);
}
async hardwareVerifyMessage(address, message, signature) {
const { type: messageType, message: strMessage } = message;
if (messageType === message_1.MessageTypes.PERSONAL_SIGN) {
const { message: resp } = await this.wrapHardwareCall(() => js_sdk_1.default.ethereumVerifyMessage({
address,
message: strMessage,
signature,
}));
return resp === 'Message verified';
}
throw new Error('Not supported');
}
}
exports.Provider = Provider;
//# sourceMappingURL=provider.js.map