@onekeyfe/blockchain-libs
Version:
OneKey Blockchain Libs
289 lines • 13.5 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 = exports.buildSignedTx = exports.buildUnsignedRawTx = void 0;
const bytes_1 = require("@ethersproject/bytes");
const js_sdk_1 = __importDefault(require("@onekeyfe/js-sdk"));
const starcoin_1 = require("@starcoin/starcoin");
const ethUtil = __importStar(require("ethereumjs-util"));
const precondtion_1 = require("../../../basic/precondtion");
const abc_1 = require("../../abc");
const starcoin_2 = require("./starcoin");
class Provider extends abc_1.BaseProvider {
get starcoin() {
return this.clientSelector((i) => i instanceof starcoin_2.StcClient);
}
async buildUnsignedTx(unsignedTx) {
var _a;
const feePricePerUnit = unsignedTx.feePricePerUnit
? unsignedTx.feePricePerUnit
: (await (await this.starcoin).getFeePricePerUnit()).normal.price;
const txInput = unsignedTx.inputs[0];
const txOutput = unsignedTx.outputs[0];
const payload = unsignedTx.payload || {};
const nonce = unsignedTx.nonce;
let feeLimit = unsignedTx.feeLimit;
const fromAddr = txInput.address;
let txPayload;
if (txInput && txOutput) {
let toAddr = txOutput.address;
const amount = txOutput.value;
const tokenAddress = txOutput.tokenAddress;
if (toAddr.startsWith('stc')) {
const riv = starcoin_1.encoding.decodeReceiptIdentifier(toAddr);
toAddr = riv.accountAddress.startsWith('0x')
? riv.accountAddress
: '0x' + riv.accountAddress;
}
const typeArgs = [tokenAddress !== null && tokenAddress !== void 0 ? tokenAddress : '0x1::STC::STC'];
const functionId = '0x1::TransferScripts::peer_to_peer_v2';
const args = [toAddr, BigInt(amount.toNumber())];
const nodeUrl = (_a = (await this.starcoin).rpc) === null || _a === void 0 ? void 0 : _a.url;
const scriptFunction = (await starcoin_1.utils.tx.encodeScriptFunctionByResolve(functionId, typeArgs, args, nodeUrl));
payload.scriptFn = scriptFunction;
txPayload = scriptFunction;
}
else if (payload.data) {
txPayload = starcoin_1.encoding.bcsDecode(starcoin_1.starcoin_types.TransactionPayload, payload.data);
}
else {
// should not be here
throw new Error('invalid unsignedTx payload');
}
const senderPublicKey = txInput.publicKey || '';
if (!feeLimit) {
(0, precondtion_1.check)(senderPublicKey, 'senderPublicKey is required');
}
if (typeof nonce === 'undefined') {
throw new Error('nonce is not available');
}
payload.expirationTime =
payload.expirationTime || Math.floor(Date.now() / 1000) + 60 * 60;
const maxGasAmount = 40000000;
const chainId = this.chainInfo.implOptions.chainId;
const gasUnitPrice = feePricePerUnit.toNumber();
const expirationTimestampSecs = payload.expirationTime || Math.floor(Date.now() / 1000) + 60 * 60;
const rawUserTransaction = starcoin_1.utils.tx.generateRawUserTransaction(fromAddr, txPayload, maxGasAmount, gasUnitPrice, nonce, expirationTimestampSecs, chainId);
const rawUserTransactionHex = starcoin_1.encoding.bcsEncode(rawUserTransaction);
let tokensChangedTo;
if (!feeLimit) {
const result = await (await this.starcoin).estimateGasLimitAndTokensChangedTo(rawUserTransactionHex, senderPublicKey);
feeLimit = result.feeLimit;
tokensChangedTo = result.tokensChangedTo;
}
return {
inputs: txInput ? [txInput] : [],
outputs: txOutput ? [txOutput] : [],
feeLimit,
tokensChangedTo,
feePricePerUnit,
nonce,
payload,
};
}
async pubkeyToAddress(verifier, encoding = 'hex') {
let address = '';
const pubkeyBytes = await verifier.getPubkey();
if (encoding === 'hex') {
address = starcoin_1.encoding.publicKeyToAddress(pubkeyBytes.toString('hex'));
}
else if (encoding === 'bech32') {
address = starcoin_1.encoding.publicKeyToReceiptIdentifier(pubkeyBytes.toString('hex'));
}
else {
throw new Error('invalid encoding');
}
return address;
}
async signTransaction(unsignedTx, signers) {
const [rawTxn, rawUserTransactionBytes] = (0, exports.buildUnsignedRawTx)(unsignedTx, this.chainInfo.implOptions.chainId);
const msgBytes = hashRawTx(rawUserTransactionBytes);
const { inputs: [{ address: fromAddr, publicKey: senderPublicKey }], } = unsignedTx;
(0, precondtion_1.check)(typeof senderPublicKey !== 'undefined', 'senderPublicKey is required');
const [signature] = await signers[fromAddr].sign(Buffer.from(msgBytes));
return (0, exports.buildSignedTx)(senderPublicKey, signature, rawTxn);
}
async verifyAddress(address) {
if (address.startsWith('stc')) {
try {
const riv = starcoin_1.encoding.decodeReceiptIdentifier(address);
return {
normalizedAddress: '0x' + riv.accountAddress,
displayAddress: address,
isValid: true,
encoding: 'bech32',
};
}
catch (error) {
// pass
}
}
else {
try {
const normalizedAddress = address.startsWith('0x')
? address.toLowerCase()
: '0x' + address.toLowerCase();
const accountAddress = starcoin_1.encoding.addressToSCS(normalizedAddress);
// in order to check invalid address length, because the auto padding 0 at head of address
if (starcoin_1.encoding.addressFromSCS(accountAddress) === normalizedAddress) {
return {
normalizedAddress,
displayAddress: normalizedAddress,
isValid: true,
encoding: 'hex',
};
}
}
catch (error) {
// pass
}
}
return {
isValid: false,
};
}
async signMessage({ type, message: messageHex }, signer, address) {
const privateKey = await signer.getPrvkey();
const originMessage = Buffer.from(ethUtil.stripHexPrefix(messageHex), 'hex').toString('utf8');
const { publicKey, signature } = await starcoin_1.utils.signedMessage.signMessage(originMessage, privateKey.toString('hex'));
if (type === 1) {
const chainId = parseInt(this.chainInfo.implOptions.chainId);
const msgBytes = (0, bytes_1.arrayify)(messageHex);
const signingMessage = new starcoin_1.starcoin_types.SigningMessage(msgBytes);
const signedMessageHex = await starcoin_1.utils.signedMessage.generateSignedMessage(signingMessage, chainId, publicKey, signature);
return signedMessageHex;
}
return signature;
}
async verifyMessage(publicKey, // require pubkey here!!
{ message }, signature) {
// starcoin sdk doesn't provide a direct method to verify message, need to
// build up a signedMessage ourselves.
const messageBytes = new Uint8Array(Buffer.from(message, 'utf8'));
const signedMessageHex = await starcoin_1.utils.signedMessage.generateSignedMessage(new starcoin_1.starcoin_types.SigningMessage(messageBytes), parseInt(this.chainInfo.implOptions.chainId), publicKey, signature);
try {
const address = await starcoin_1.utils.signedMessage.recoverSignedMessageAddress(signedMessageHex);
return address === starcoin_1.encoding.publicKeyToAddress(publicKey);
}
catch (e) {
console.error(e);
return false;
}
}
async hardwareGetXpubs(paths, showOnDevice) {
const resp = await this.wrapHardwareCall(() => js_sdk_1.default.starcoinGetPublicKey({
bundle: paths.map((path) => ({ path, showOnDevice })),
}));
return resp.map((i) => ({
path: i.serializedPath,
xpub: i.publicKey,
}));
}
async hardwareGetAddress(path, showOnDevice, addressToVerify) {
const params = {
path,
showOnDevice,
};
if (typeof addressToVerify === 'string') {
Object.assign(params, {
address: addressToVerify,
});
}
const { address } = await this.wrapHardwareCall(() => js_sdk_1.default.starcoinGetAddress(params));
return address;
}
async hardwareSignTransaction(unsignedTx, signers) {
const [rawTxn, rawUserTransactionBytes] = (0, exports.buildUnsignedRawTx)(unsignedTx, this.chainInfo.implOptions.chainId);
const { inputs: [{ address: fromAddr, publicKey: senderPublicKey }], } = unsignedTx;
(0, precondtion_1.check)(typeof senderPublicKey !== 'undefined', 'senderPublicKey is required');
const { signature } = await this.wrapHardwareCall(() => js_sdk_1.default.starcoinSignTransaction({
path: signers[fromAddr],
rawTx: Buffer.from(rawUserTransactionBytes).toString('hex'),
}));
return (0, exports.buildSignedTx)(senderPublicKey, Buffer.from(signature, 'hex'), rawTxn);
}
async hardwareSignMessage({ message }, signer) {
const { signature } = await this.wrapHardwareCall(() => js_sdk_1.default.starcoinSignMessage({
path: signer,
message,
}));
return ethUtil.addHexPrefix(signature);
}
async hardwareVerifyMessage(publicKey, // require pubkey here!!
{ message }, signature) {
const { message: resp } = await this.wrapHardwareCall(() => js_sdk_1.default.starcoinVerifyMessage({
publicKey,
message,
signature,
}));
return resp === 'Message verified';
}
}
exports.Provider = Provider;
const buildUnsignedRawTx = (unsignedTx, chainId) => {
const fromAddr = unsignedTx.inputs[0].address;
const { scriptFn, data } = unsignedTx.payload;
const gasLimit = unsignedTx.feeLimit;
const gasPrice = unsignedTx.feePricePerUnit;
const nonce = unsignedTx.nonce;
const expirationTime = unsignedTx.payload.expirationTime;
if (!fromAddr ||
!(scriptFn || data) ||
!gasLimit ||
!gasPrice ||
typeof nonce === 'undefined') {
throw new Error('invalid unsignedTx');
}
let txPayload;
if (scriptFn) {
txPayload = scriptFn;
}
else {
txPayload = starcoin_1.encoding.bcsDecode(starcoin_1.starcoin_types.TransactionPayload, data);
}
const rawTxn = starcoin_1.utils.tx.generateRawUserTransaction(fromAddr, txPayload, gasLimit.toNumber(), gasPrice.toNumber(), nonce, expirationTime, Number(chainId));
const serializer = new starcoin_1.bcs.BcsSerializer();
rawTxn.serialize(serializer);
return [rawTxn, serializer.getBytes()];
};
exports.buildUnsignedRawTx = buildUnsignedRawTx;
const hashRawTx = (rawUserTransactionBytes) => {
const hashSeedBytes = starcoin_1.crypto_hash.createRawUserTransactionHasher().get_salt();
return Uint8Array.of(...hashSeedBytes, ...rawUserTransactionBytes);
};
const buildSignedTx = (senderPublicKey, rawSignature, rawTxn) => {
const publicKey = new starcoin_1.starcoin_types.Ed25519PublicKey(Buffer.from(senderPublicKey, 'hex'));
const signature = new starcoin_1.starcoin_types.Ed25519Signature(rawSignature);
const transactionAuthenticatorVariantEd25519 = new starcoin_1.starcoin_types.TransactionAuthenticatorVariantEd25519(publicKey, signature);
const signedUserTransaction = new starcoin_1.starcoin_types.SignedUserTransaction(rawTxn, transactionAuthenticatorVariantEd25519);
const se = new starcoin_1.bcs.BcsSerializer();
signedUserTransaction.serialize(se);
const txid = starcoin_1.crypto_hash
.createUserTransactionHasher()
.crypto_hash(se.getBytes());
const rawTx = (0, bytes_1.hexlify)(se.getBytes());
return { txid, rawTx };
};
exports.buildSignedTx = buildSignedTx;
//# sourceMappingURL=provider.js.map