@pgchain/blockchain-libs
Version:
PGWallet Blockchain Libs
212 lines • 10.3 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 js_sdk_1 = __importDefault(require("@onekeyfe/js-sdk"));
const splToken = __importStar(require("@solana/spl-token"));
const web3_js_1 = require("@solana/web3.js");
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const bs58_1 = __importDefault(require("bs58"));
const precondtion_1 = require("../../../basic/precondtion");
const abc_1 = require("../../abc");
const solana_1 = require("./solana");
class Provider extends abc_1.BaseProvider {
get solana() {
return this.clientSelector((i) => i instanceof solana_1.Solana);
}
async buildUnsignedTx(unsignedTx) {
const feePricePerUnit = unsignedTx.feePricePerUnit
? unsignedTx.feePricePerUnit
: (await (await this.solana).getFeePricePerUnit()).normal.price;
const txInput = unsignedTx.inputs[0];
const txOutput = unsignedTx.outputs[0];
const payload = unsignedTx.payload || {};
const feeLimit = new bignumber_js_1.default(1); // len(transfer_tx.signatures)
if (txInput && txOutput) {
const tokenAddress = txInput.tokenAddress;
const receiver = txOutput.address;
// Note: method `is_on_curve` may has risk. see https://github.com/solana-labs/solana/issues/17106
const isValidSystemAccount = web3_js_1.PublicKey.isOnCurve(bs58_1.default.decode(receiver));
// spl-token transfer
if (tokenAddress) {
payload.isTokenAccount = false;
const accountInfo = await (await this.solana).getAccountInfo(receiver);
// account not funded: only support system account
if (accountInfo === null) {
(0, precondtion_1.check)(isValidSystemAccount, 'only not_funded system account allowed');
}
else {
if (isValidSystemAccount) {
(0, precondtion_1.check)(accountInfo.owner == web3_js_1.SystemProgram.programId.toString(), `system account with invalid owner ${accountInfo.owner}`);
}
else {
// token account
(0, precondtion_1.check)(accountInfo.owner == splToken.TOKEN_PROGRAM_ID.toString(), 'invalid account owner');
(0, precondtion_1.check)(accountInfo.data.parsed.info.mint == tokenAddress, `invalid token account with ${tokenAddress}`);
payload.accountFunded = true;
payload.isTokenAccount = true;
}
}
if (isValidSystemAccount) {
const tokenReceiver = await this.getAssociatedTokenAddress(new web3_js_1.PublicKey(receiver), new web3_js_1.PublicKey(tokenAddress));
const accountInfo = await (await this.solana).getAccountInfo(tokenReceiver.toString());
if (accountInfo === null) {
payload.accountFunded = false;
}
else {
payload.accountFunded = true;
}
}
}
else {
// sol transfer only support system accounts
(0, precondtion_1.check)(isValidSystemAccount, 'fall off curve pubkey is not allowed');
}
// something like nonce
const [_, recentBlockhash] = await (await this.solana).getFees();
payload.recentBlockhash = recentBlockhash;
}
return {
inputs: txInput ? [txInput] : [],
outputs: txOutput ? [txOutput] : [],
feeLimit,
feePricePerUnit,
payload,
};
}
async pubkeyToAddress(verifier, encoding) {
const pubkeyBytes = await verifier.getPubkey();
const address = new web3_js_1.PublicKey(pubkeyBytes).toBase58();
return address;
}
async signTransaction(unsignedTx, signers) {
const sender = unsignedTx.inputs[0].address;
const receiver = unsignedTx.outputs[0].address;
const amount = unsignedTx.outputs[0].value;
const tokenAddress = unsignedTx.outputs[0].tokenAddress;
const transferTx = await this.buildTx(sender, receiver, amount, unsignedTx.payload, tokenAddress);
transferTx.feePayer = new web3_js_1.PublicKey(sender);
const signData = transferTx.serializeMessage();
const [sig, _] = await signers[sender].sign(signData);
(0, precondtion_1.check)(sig.length === 64, 'signature has invalid length');
transferTx.addSignature(new web3_js_1.PublicKey(sender), sig);
const txid = bs58_1.default.encode(sig);
const rawTx = transferTx.serialize().toString('base64');
return { txid, rawTx };
}
async getAssociatedTokenAddress(owner, mint) {
const associatedTokenAddress = await splToken.Token.getAssociatedTokenAddress(splToken.ASSOCIATED_TOKEN_PROGRAM_ID, splToken.TOKEN_PROGRAM_ID, mint, owner);
return associatedTokenAddress;
}
async buildTx(fromAddr, toAddr, amount, payload, mintAddress) {
const transferTx = new web3_js_1.Transaction();
const sender = new web3_js_1.PublicKey(fromAddr);
const receiver = new web3_js_1.PublicKey(toAddr);
if (mintAddress) {
// SPL-Token transfer
const tokenSender = await this.getAssociatedTokenAddress(sender, new web3_js_1.PublicKey(mintAddress));
const accountFunded = payload.accountFunded;
const isTokenAccount = payload.isTokenAccount;
const tokenReceiver = isTokenAccount
? receiver
: await this.getAssociatedTokenAddress(receiver, new web3_js_1.PublicKey(mintAddress));
// account not funded means system account
if (!accountFunded) {
transferTx.add(splToken.Token.createAssociatedTokenAccountInstruction(splToken.ASSOCIATED_TOKEN_PROGRAM_ID, splToken.TOKEN_PROGRAM_ID, new web3_js_1.PublicKey(mintAddress), tokenReceiver, receiver, sender));
}
transferTx.add(splToken.Token.createTransferInstruction(splToken.TOKEN_PROGRAM_ID, tokenSender, tokenReceiver, sender, [], amount.toNumber()));
}
else {
// SOL transfer
transferTx.add(web3_js_1.SystemProgram.transfer({
fromPubkey: sender,
toPubkey: receiver,
lamports: amount.toNumber(),
}));
}
transferTx.recentBlockhash = payload.recentBlockhash;
return transferTx;
}
async verifyAddress(address) {
let isValid = true;
try {
new web3_js_1.PublicKey(address);
}
catch (error) {
isValid = false;
}
return {
normalizedAddress: isValid ? address : undefined,
displayAddress: isValid ? address : undefined,
isValid,
};
}
async verifyAssociatedTokenAddress(address) {
const result = await this.verifyAddress(address);
if (result.isValid) {
const isOnCurve = web3_js_1.PublicKey.isOnCurve(bs58_1.default.decode(address));
// validate program address is off ed25519 curve
result.isValid = !isOnCurve;
}
return result;
}
async hardwareGetXpubs(paths, showOnDevice) {
const resp = await this.wrapHardwareCall(() => js_sdk_1.default.solanaGetAddress({
bundle: paths.map((path) => ({ path, showOnDevice })),
}));
return resp.map((i) => ({
path: i.serializedPath,
xpub: new web3_js_1.PublicKey(i.address).toBuffer().toString('hex'), // public key here, not xpub
}));
}
async hardwareGetAddress(path, showOnDevice, addressToVerify) {
const payload = {
path,
showOnDevice,
};
if (typeof addressToVerify === 'string') {
Object.assign(payload, { address: addressToVerify });
}
const { address } = await this.wrapHardwareCall(() => js_sdk_1.default.solanaGetAddress(payload));
return address;
}
async hardwareSignTransaction(unsignedTx, signers) {
const { inputs: [{ address: sender }], outputs: [{ address: receiver, value: amount, tokenAddress }], } = unsignedTx;
const transferTx = await this.buildTx(sender, receiver, amount, unsignedTx.payload, tokenAddress);
transferTx.feePayer = new web3_js_1.PublicKey(sender);
const signData = transferTx.serializeMessage();
const { signature } = await this.wrapHardwareCall(() => js_sdk_1.default.solanaSignTransaction({
path: signers[sender],
rawTx: bs58_1.default.encode(signData),
}));
const signatureBuffer = bs58_1.default.decode(signature);
(0, precondtion_1.check)(signatureBuffer.length === 64, 'signature has invalid length');
transferTx.addSignature(new web3_js_1.PublicKey(sender), signatureBuffer);
const txid = signature;
const rawTx = transferTx.serialize().toString('base64');
return { txid, rawTx };
}
}
exports.Provider = Provider;
//# sourceMappingURL=provider.js.map