@pgchain/blockchain-libs
Version:
PGWallet Blockchain Libs
270 lines • 12.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 || {};
let nonce = unsignedTx.nonce;
let feeLimit = unsignedTx.feeLimit;
if (txInput && txOutput) {
const senderPublicKey = txInput.publicKey;
if (!feeLimit) {
(0, precondtion_1.check)(senderPublicKey, 'senderPublicKey is required');
}
const fromAddr = txInput.address;
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;
}
if (typeof nonce === 'undefined') {
nonce = (_a = (await (await this.starcoin).getAddresses([fromAddr]))[0]) === null || _a === void 0 ? void 0 : _a.nonce;
(0, precondtion_1.check)(typeof nonce !== 'undefined', 'nonce is not available');
}
payload.expirationTime =
payload.expirationTime || Math.floor(Date.now() / 1000) + 60 * 60;
const strTypeArgs = [tokenAddress !== null && tokenAddress !== void 0 ? tokenAddress : '0x1::STC::STC'];
const tyArgs = starcoin_1.utils.tx.encodeStructTypeTags(strTypeArgs);
const functionId = '0x1::TransferScripts::peer_to_peer_v2';
const amountSCSHex = (function () {
const se = new starcoin_1.bcs.BcsSerializer();
se.serializeU128(BigInt(amount.toNumber()));
return (0, bytes_1.hexlify)(se.getBytes());
})();
const args = [(0, bytes_1.arrayify)(toAddr), (0, bytes_1.arrayify)(amountSCSHex)];
payload.scriptFn = starcoin_1.utils.tx.encodeScriptFunction(functionId, tyArgs, args);
feeLimit =
feeLimit ||
(await (await this.starcoin).estimateGasLimit({
chain_id: this.chainInfo.implOptions.chainId,
gas_unit_price: feePricePerUnit.toNumber(),
sender: fromAddr,
sender_public_key: senderPublicKey,
sequence_number: nonce,
max_gas_amount: 1000000,
script: {
code: functionId,
type_args: strTypeArgs,
args: [toAddr, '19950u128'],
},
}));
}
return {
inputs: txInput ? [txInput] : [],
outputs: txOutput ? [txOutput] : [],
feeLimit,
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) {
let isValid = true;
let encoding = undefined;
let normalizeAddress = address;
if (address.startsWith('stc')) {
try {
const riv = starcoin_1.encoding.decodeReceiptIdentifier(address);
normalizeAddress = '0x' + riv.accountAddress;
encoding = 'bech32';
}
catch (error) {
isValid = false;
}
}
else {
try {
const accountAddress = starcoin_1.encoding.addressToSCS(address);
// in order to check invalid address length, because the auto padding 0 at head of address
if (starcoin_1.encoding.addressFromSCS(accountAddress) ===
(address.startsWith('0x') ? address : '0x' + address)) {
encoding = 'hex';
}
else {
isValid = false;
}
}
catch (error) {
isValid = false;
}
}
return {
normalizedAddress: isValid ? normalizeAddress : undefined,
displayAddress: isValid ? address : undefined,
isValid,
encoding,
};
}
async signMessage({ message }, signer, address) {
const privateKey = await signer.getPrvkey();
const { signature } = await starcoin_1.utils.signedMessage.signMessage(message, privateKey.toString('hex'));
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 = unsignedTx.payload.scriptFn;
const gasLimit = unsignedTx.feeLimit;
const gasPrice = unsignedTx.feePricePerUnit;
const nonce = unsignedTx.nonce;
const expirationTime = unsignedTx.payload.expirationTime;
if (!fromAddr ||
!scriptFn ||
!gasLimit ||
!gasPrice ||
typeof nonce === 'undefined') {
throw new Error('invalid unsignedTx');
}
const rawTxn = starcoin_1.utils.tx.generateRawUserTransaction(fromAddr, scriptFn, 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