UNPKG

@pgchain/blockchain-libs

Version:
212 lines 10.3 kB
"use strict"; 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