@trezor/connect
Version:
High-level javascript interface for Trezor hardware wallet.
225 lines • 13.2 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.fetchAccountOwnerAndTokenInfoForAddress = exports.buildTokenTransferTransaction = exports.getMinimumRequiredTokenAccountsForTransfer = exports.buildCreateAssociatedTokenAccountInstruction = exports.getAssociatedTokenAccountAddress = exports.buildTokenTransferInstruction = exports.buildTransferTransaction = exports.dummyPriorityFeesForFeeEstimation = exports.getLamportsFromSol = exports.SOLANA_BASE_FEE = void 0;
exports.createTransactionShim = createTransactionShim;
exports.createTransactionShimFromHex = createTransactionShimFromHex;
const ts_belt_1 = require("@mobily/ts-belt");
const blockchain_link_utils_1 = require("@trezor/blockchain-link-utils");
const bigNumber_1 = require("@trezor/utils/lib/bigNumber");
const { SYSTEM_PROGRAM_PUBLIC_KEY, tokenProgramsInfo } = blockchain_link_utils_1.solanaUtils;
const loadSolanaLib = async () => await Promise.resolve().then(() => __importStar(require('@solana/kit')));
const loadSolanaComputeBudgetProgramLib = async () => await Promise.resolve().then(() => __importStar(require('@solana-program/compute-budget')));
const loadSolanaSystemProgramLib = async () => await Promise.resolve().then(() => __importStar(require('@solana-program/system')));
const loadSolanaTokenProgramLib = async (tokenProgramName) => {
switch (tokenProgramName) {
case 'spl-token':
return await Promise.resolve().then(() => __importStar(require('@solana-program/token')));
case 'spl-token-2022':
return await Promise.resolve().then(() => __importStar(require('@solana-program/token-2022')));
default:
throw new Error(`Unsupported token program: ${tokenProgramName}`);
}
};
exports.SOLANA_BASE_FEE = 5000;
const getLamportsFromSol = (amountInSol) => BigInt(new bigNumber_1.BigNumber(amountInSol).times(10 ** 9).toString());
exports.getLamportsFromSol = getLamportsFromSol;
exports.dummyPriorityFeesForFeeEstimation = {
computeUnitPrice: '100000',
computeUnitLimit: '200000',
};
async function createTransactionShimCommon(transaction) {
const { getBase16Codec, getTransactionEncoder } = await loadSolanaLib();
return {
addSignature(signerPubKey, signatureHex) {
if (signerPubKey in transaction.signatures) {
const signatureBytes = getBase16Codec().encode(signatureHex);
transaction = Object.freeze({
...transaction,
signatures: Object.freeze({
...transaction.signatures,
[signerPubKey]: signatureBytes,
}),
});
}
},
serializeMessage() {
return getBase16Codec().decode(transaction.messageBytes);
},
serialize() {
return (0, ts_belt_1.pipe)(transaction, getTransactionEncoder().encode, getBase16Codec().decode);
},
};
}
async function createTransactionShim(message) {
const { compileTransaction } = await loadSolanaLib();
const transaction = compileTransaction(message);
return createTransactionShimCommon(transaction);
}
async function createTransactionShimFromHex(rawTx) {
const { getBase16Encoder, getTransactionDecoder } = await loadSolanaLib();
const txByteArray = getBase16Encoder().encode(rawTx);
const transaction = getTransactionDecoder().decode(txByteArray);
return createTransactionShimCommon(transaction);
}
const addPriorityFees = async (message, priorityFees) => {
const [{ prependTransactionMessageInstructions }, { getSetComputeUnitLimitInstruction, getSetComputeUnitPriceInstruction },] = await Promise.all([loadSolanaLib(), loadSolanaComputeBudgetProgramLib()]);
return (0, ts_belt_1.pipe)(message, m => prependTransactionMessageInstructions([
getSetComputeUnitLimitInstruction({
units: parseInt(priorityFees.computeUnitLimit, 10),
}),
getSetComputeUnitPriceInstruction({
microLamports: parseInt(priorityFees.computeUnitPrice, 10),
}),
], m));
};
const buildTransferTransaction = async (fromAddress, toAddress, amountInSol, blockhash, lastValidBlockHeight, priorityFees) => {
const [{ address, appendTransactionMessageInstruction, createTransactionMessage, lamports, setTransactionMessageLifetimeUsingBlockhash, setTransactionMessageFeePayer, createNoopSigner, }, { getTransferSolInstruction },] = await Promise.all([loadSolanaLib(), loadSolanaSystemProgramLib()]);
const message = await (0, ts_belt_1.pipe)(createTransactionMessage({ version: 'legacy' }), m => setTransactionMessageFeePayer(address(fromAddress), m), m => setTransactionMessageLifetimeUsingBlockhash({
blockhash: blockhash,
lastValidBlockHeight: BigInt(lastValidBlockHeight ?? '0xFFFFFFFFFFFFFFFF'),
}, m), m => appendTransactionMessageInstruction(getTransferSolInstruction({
amount: lamports((0, exports.getLamportsFromSol)(amountInSol)),
destination: address(toAddress),
source: createNoopSigner(address(fromAddress)),
}), m), m => addPriorityFees(m, priorityFees));
return await createTransactionShim(message);
};
exports.buildTransferTransaction = buildTransferTransaction;
const buildTokenTransferInstruction = async (from, to, owner, amount, mint, decimals, tokenProgramName) => {
const [{ address, createNoopSigner }, { getTransferCheckedInstruction },] = await Promise.all([loadSolanaLib(), loadSolanaTokenProgramLib(tokenProgramName)]);
return getTransferCheckedInstruction({
amount: BigInt(amount.toString()),
authority: createNoopSigner(address(owner)),
decimals,
destination: address(to),
mint: address(mint),
source: address(from),
});
};
exports.buildTokenTransferInstruction = buildTokenTransferInstruction;
const getAssociatedTokenAccountAddress = async (baseAddress, tokenMintAddress, tokenProgramName) => {
const [{ address }, { findAssociatedTokenPda },] = await Promise.all([loadSolanaLib(), loadSolanaTokenProgramLib(tokenProgramName)]);
const [pdaAddress] = await findAssociatedTokenPda({
mint: address(tokenMintAddress),
owner: address(baseAddress),
tokenProgram: address(tokenProgramsInfo[tokenProgramName].publicKey),
});
return pdaAddress;
};
exports.getAssociatedTokenAccountAddress = getAssociatedTokenAccountAddress;
const buildCreateAssociatedTokenAccountInstruction = async (funderAddress, newOwnerAddress, tokenMintAddress, tokenProgramName) => {
const [{ address, createNoopSigner }, { getCreateAssociatedTokenInstruction },] = await Promise.all([loadSolanaLib(), loadSolanaTokenProgramLib(tokenProgramName)]);
const associatedTokenAccountAddress = await (0, exports.getAssociatedTokenAccountAddress)(newOwnerAddress, tokenMintAddress, tokenProgramName);
const txInstruction = getCreateAssociatedTokenInstruction({
ata: associatedTokenAccountAddress,
mint: address(tokenMintAddress),
owner: address(newOwnerAddress),
payer: createNoopSigner(address(funderAddress)),
});
txInstruction.data = new Uint8Array([]);
return [txInstruction, associatedTokenAccountAddress];
};
exports.buildCreateAssociatedTokenAccountInstruction = buildCreateAssociatedTokenAccountInstruction;
const getMinimumRequiredTokenAccountsForTransfer = (tokenAccounts, requiredAmount) => {
let accumulatedBalance = new bigNumber_1.BigNumber('0');
const requiredAccounts = ts_belt_1.F.toMutable((0, ts_belt_1.pipe)(tokenAccounts, ts_belt_1.A.sort((a, b) => new bigNumber_1.BigNumber(b.balance).comparedTo(new bigNumber_1.BigNumber(a.balance)) ?? 0), ts_belt_1.A.takeWhile(tokenAccount => {
const needMoreAccounts = accumulatedBalance.lt(requiredAmount);
accumulatedBalance = accumulatedBalance.plus(tokenAccount.balance);
return needMoreAccounts;
})));
return requiredAccounts;
};
exports.getMinimumRequiredTokenAccountsForTransfer = getMinimumRequiredTokenAccountsForTransfer;
const buildTokenTransferTransaction = async (fromAddress, toAddress, toAddressOwner, tokenMint, tokenUiAmount, tokenDecimals, fromTokenAccounts, toTokenAccount, blockhash, lastValidBlockHeight, priorityFees, tokenProgramName) => {
const { address, appendTransactionMessageInstruction, appendTransactionMessageInstructions, createTransactionMessage, setTransactionMessageFeePayer, setTransactionMessageLifetimeUsingBlockhash, } = await loadSolanaLib();
let message = await (0, ts_belt_1.pipe)(createTransactionMessage({ version: 'legacy' }), m => setTransactionMessageFeePayer(address(fromAddress), m), m => setTransactionMessageLifetimeUsingBlockhash({
blockhash: blockhash,
lastValidBlockHeight: BigInt(lastValidBlockHeight ?? '0xFFFFFFFFFFFFFFFF'),
}, m), m => addPriorityFees(m, priorityFees));
const tokenAmount = new bigNumber_1.BigNumber(tokenUiAmount).times(10 ** tokenDecimals);
const requiredAccounts = (0, exports.getMinimumRequiredTokenAccountsForTransfer)(fromTokenAccounts, tokenAmount.toString());
const isReceiverAddressSystemAccount = toAddressOwner === SYSTEM_PROGRAM_PUBLIC_KEY;
let finalReceiverAddress = toAddress;
if (isReceiverAddressSystemAccount) {
if (toTokenAccount) {
finalReceiverAddress = toTokenAccount.publicKey;
}
else {
const [createAccountInstruction, associatedTokenAccountAddress] = await (0, exports.buildCreateAssociatedTokenAccountInstruction)(fromAddress, toAddress, tokenMint, tokenProgramName);
message = appendTransactionMessageInstruction(createAccountInstruction, message);
finalReceiverAddress = associatedTokenAccountAddress;
}
}
let remainingAmount = tokenAmount;
const instructionPromises = requiredAccounts.map(async (tokenAccount) => {
const transferAmount = bigNumber_1.BigNumber.min(remainingAmount, new bigNumber_1.BigNumber(tokenAccount.balance));
const transferInstruction = await (0, exports.buildTokenTransferInstruction)(tokenAccount.publicKey, finalReceiverAddress, fromAddress, transferAmount, tokenMint, tokenDecimals, tokenProgramName);
remainingAmount = remainingAmount.minus(transferAmount);
return transferInstruction;
});
message = appendTransactionMessageInstructions(await Promise.all(instructionPromises), message);
return {
transaction: await createTransactionShim(message),
destinationAddress: finalReceiverAddress,
tokenAccountInfo: isReceiverAddressSystemAccount
? {
baseAddress: toAddress,
tokenProgram: tokenProgramsInfo[tokenProgramName].publicKey,
tokenMint,
tokenAccount: finalReceiverAddress,
}
: undefined,
};
};
exports.buildTokenTransferTransaction = buildTokenTransferTransaction;
const fetchAccountOwnerAndTokenInfoForAddress = async (blockchain, address, mint, tokenProgram) => {
let accountOwner;
let tokenInfo;
const accountInfoResponse = await blockchain.getAccountInfo({
descriptor: address,
details: 'tokens',
});
if (accountInfoResponse) {
const associatedTokenAccount = await (0, exports.getAssociatedTokenAccountAddress)(address, mint, tokenProgram);
accountOwner = accountInfoResponse?.misc?.owner;
tokenInfo = accountInfoResponse?.tokens
?.find(token => token.contract === mint)
?.accounts?.find(account => associatedTokenAccount.toString() === account.publicKey);
}
return [accountOwner, tokenInfo];
};
exports.fetchAccountOwnerAndTokenInfoForAddress = fetchAccountOwnerAndTokenInfoForAddress;
//# sourceMappingURL=solanaUtils.js.map