UNPKG

@flarenetwork/flare-stake-tool

Version:
908 lines 42.8 kB
"use strict"; 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.networkTokenSymbol = void 0; exports.cli = cli; exports.contextFromOptions = contextFromOptions; exports.networkFromOptions = networkFromOptions; exports.getOptions = getOptions; exports.initCtxJsonFromOptions = initCtxJsonFromOptions; exports.logAddressInfo = logAddressInfo; exports.logBalanceInfo = logBalanceInfo; exports.logNetworkInfo = logNetworkInfo; exports.logValidatorInfo = logValidatorInfo; exports.logMirrorFundInfo = logMirrorFundInfo; const flarejs_1 = require("@flarenetwork/flarejs"); const context_1 = require("./context"); const utils_1 = require("./utils"); const evmTx_1 = require("./forDefi/evmTx"); const output_1 = require("./output"); const ledger = __importStar(require("./ledger")); const flare = __importStar(require("./flare")); const settings = __importStar(require("./settings")); const chain_1 = require("./flare/chain"); const ethers_1 = require("ethers"); const bn_js_1 = require("bn.js"); const transaction_1 = require("./transaction"); const transaction_2 = require("./forDefi/transaction"); const contracts_1 = require("./contracts"); const BASE_DERIVATION_PATH = "m/44'/60'/0'/0/0"; // base derivation path for ledger // mapping from network to symbol exports.networkTokenSymbol = { flare: 'FLR', songbird: 'SGB', costwo: 'C2FLR', coston: 'CFLR', localflare: 'PHT' }; function cli(program) { // global configurations program .option('--network <network>', 'Network name (flare|songbird|costwo|coston|localflare)') .option('--ledger', 'Use ledger to sign transactions') .option('--blind', 'Blind signing (used for ledger)', true) .option('--derivation-path <derivation-path>', 'Ledger address derivation path', BASE_DERIVATION_PATH) .option('--ctx-file <file>', 'Context file as returned by init-ctx', 'ctx.json') .option('--env-path <path>', 'Path to the .env file') .option('--get-hacked', 'Use the .env file with the exposed private key'); // interactive mode program .command('interactive') .description('Interactive mode') .action(async () => { // this will never run, here just for --help display }); // context setup program .command('init-ctx') .description('Initialize context file') .option('-p, --public-key <public-key>', 'Public key of the account') .action(async (options) => { options = getOptions(program, options); await initCtxJsonFromOptions(options); }); // information about the network program .command('info') .description('Relevant information') .argument('<addresses|balance|network|validators>', 'Type of information') .action(async (type) => { const options = getOptions(program, program.opts()); const ctx = await contextFromOptions(options); if (type == 'addresses') { logAddressInfo(ctx); } else if (type == 'balance') { await logBalanceInfo(ctx); } else if (type == 'network') { logNetworkInfo(ctx); } else if (type == 'validators') { await logValidatorInfo(ctx); } else if (type == 'mirror') { await logMirrorFundInfo(ctx); } else { (0, output_1.logError)(`Unknown information type ${type}`); } }); // transaction construction and sending program .command('transaction') .description('Move funds from one chain to another or to another P-chain address, stake, and delegate') .argument('<importCP|exportCP|importPC|exportPC|delegate|stake>', 'Type of a cross chain transaction') .option('-i, --transaction-id <transaction-id>', 'Id of the transaction to finalize') .option('-a, --amount <amount>', 'Amount to transfer') .option('-f, --fee <fee>', 'Transaction fee (in FLR)') .option('-n, --node-id <nodeId>', 'The id of the node to stake/delegate to') .option('-s, --start-time <start-time>', 'Start time of the staking/delegating process') .option('-e, --end-time <end-time>', 'End time of the staking/delegating process') .option('--nonce <nonce>', 'Nonce of the constructed transaction') .option('--delegation-fee <delegation-fee>', 'Delegation fee defined by the deployed validator', '10') .option('--pop-bls-public-key <popBlsPublicKey>', 'BLS Public Key') .option('--pop-bls-signature <popBlsSignature>', 'BLS Signature') .option('--transfer-address <transfer-address>', 'P-chain address to transfer funds to') .option('--threshold <threshold>', 'Threshold of the constructed transaction', '1') .action(async (type, options) => { options = getOptions(program, options); const ctx = await contextFromOptions(options); if (options.getHacked) { // for future development: users should get notified before the program gets access to their private keys await cliBuildAndSendTxUsingPrivateKey(type, ctx, options); } else if (options.ledger) { await cliBuildAndSendTxUsingLedger(type, ctx, options, options.blind, options.derivationPath); } else { await cliBuildAndSaveUnsignedTxJson(type, ctx, options.transactionId, options); } }); // signed transaction sending program .command("send") .description("Send signed transaction json to the node") .option("-i, --transaction-id <transaction-id>", "Id of the transaction to send to the network") .action(async (options) => { options = getOptions(program, options); const ctx = await contextFromOptions(options); await cliSendSignedTxJson(ctx, options.transactionId); }); // ForDefi signing program .command("forDefi") .description("Sign with ForDefi") .argument("<sign|fetch>", "Type of a forDefi transaction") .option("-i, --transaction-id <transaction-id>", "Id of the transaction to finalize") .option("--evm-tx", "Regular EVM transaction") .action(async (type, options) => { options = getOptions(program, options); if (typeof options.transactionId !== 'string') { throw new Error('Option --blind must be a string'); } if (type == "sign") { if (typeof options.ctxFile !== 'string') { throw new Error('Option --ctx-file must be a string'); } if (options.evmTx) { await signForDefi(options.transactionId, options.ctxFile, true); } else { await signForDefi(options.transactionId, options.ctxFile); } } else if (type == "fetch") { if (options.evmTx) { await fetchForDefiTx(options.transactionId, true); } else { await fetchForDefiTx(options.transactionId); } } }); // withdrawal (transfer) from C-chain (ForDefi) program .command('withdrawal') .description('Withdraw funds from C-chain') .option('-i, --transaction-id <transaction-id>', 'Id of the transaction to finalize') .option('-a, --amount <amount>', 'Amount to transfer') .option('-t, --to <to>', 'Address to send funds to') .option('--nonce <nonce>', 'Nonce of the constructed transaction') .action(async (options) => { options = getOptions(program, options); const ctx = await contextFromOptions(options); await buildUnsignedWithdrawalTxJson(ctx, options.to, options.amount, options.transactionId, options.nonce); }); // opt out (ForDefi) program .command("optOut").description("Opt out of airdrop on the c-chain") .option("-i, --transaction-id <transaction-id>", "Id of the transaction to finalize") .option("--nonce <nonce>", "Nonce of the constructed transaction") .action(async (options) => { options = getOptions(program, options); const ctx = await contextFromOptions(options); await buildUnsignedOptOutTxJson(ctx, options.transactionId, options.nonce); }); // claim staking (ValidatorRewardManager) rewards program .command('claim') .description('claim staking rewards') .option('-i, --transaction-id <transaction-id>', 'Id of the transaction to finalize') .option('-n,--nonce <nonce>', 'Nonce of the constructed transaction') .option('-a, --amount <amount>', 'Amount to claim') .option('-r, --recipient <recipient>', 'Address to send the rewards to') .option('-w, --wrap', 'Wrap the rewards', false) .action(async (options) => { options = getOptions(program, options); await processClaimTx(options, options.transactionId, options.amount, options.recipient, options.wrap, options.nonce); }); // set claim executor (ForDefi) program .command('setClaimExecutors') .description('Set claim executors for claiming FSP rewards') .option('-i, --transaction-id <transaction-id>', 'Id of the transaction to finalize') .option('--nonce <nonce>', 'Nonce of the constructed transaction') .option('--executors <executors...>', 'Addresses of the executors to set (space separated); empty string to remove all executors') .action(async (options) => { options = getOptions(program, options); const ctx = await contextFromOptions(options); await buildUnsignedSetClaimExecutorsTxJson(ctx, options.transactionId, options.executors, options.nonce); }); // set allowed claim recipients (ForDefi) program .command('setAllowedClaimRecipients') .description('Set allowed claim recipients for claiming FSP rewards') .option('-i, --transaction-id <transaction-id>', 'Id of the transaction to finalize') .option('--nonce <nonce>', 'Nonce of the constructed transaction') .option('--recipients <recipients...>', 'Addresses of the allowed claim recipients (space separated)') .action(async (options) => { options = getOptions(program, options); const ctx = await contextFromOptions(options); await buildUnsignedSetAllowedClaimRecipientsTxJson(ctx, options.transactionId, options.recipients, options.nonce); }); // custom c-chain transaction (ForDefi) program .command('customCChainTx') .description('Custom C-chain transaction') .option('-i, --transaction-id <transaction-id>', 'Id of the transaction to finalize') .option('--nonce <nonce>', 'Nonce of the constructed transaction') .option('--data <data>', 'Data to send in the transaction', '') .option('--to <to>', 'Address to send the transaction to') .option('--value <value>', 'Value to send in the transaction', '0') .action(async (options) => { options = getOptions(program, options); const ctx = await contextFromOptions(options); await buildUnsignedCustomCChainTxJson(ctx, options.transactionId, options.data, options.to, options.value, options.nonce); }); } /** * @description - returns context from the options that are passed * @param options - option to define whether its from ledger/env/ctx.file * @returns Returns the context based the source passed in the options */ async function contextFromOptions(options) { if (options.ledger) { (0, output_1.logInfo)('Fetching account from ledger...'); const publicKey = await ledger.getPublicKey(options.derivationPath, options.network); const ctx = (0, context_1.getContext)(options.network, publicKey); return ctx; } else if (options.envPath) { return (0, context_1.contextEnv)(options.envPath, options.network); } else { return (0, context_1.contextFile)(options.ctxFile); } } // Network is obtained from context file, if it exists, else from --network flag. // This is because ledger does not need a context file /** * @description Returns the network config from the options that were passed * @param options - contains the options to derive the network * @returns - network */ function networkFromOptions(options) { let network = options.network; if (network == undefined) { try { network = (0, context_1.networkFromContextFile)(options.ctxFile); } catch (e) { network = 'flare'; (0, output_1.logError)(`Error ${e}. No network was passed and no context file was found. Defaulting to flare network`); } } (0, output_1.logInfo)(`Using network: ${network}`); return network; } /** * @description - Returns the options for a command * @param program - the command * @param options - option available for the command */ function getOptions(program, options) { const allOptions = { ...program.opts(), ...options }; const network = networkFromOptions(allOptions); // amount and fee are given in FLR, transform into nanoFLR (FLR = 1e9 nanoFLR) if (allOptions.amount) { if (typeof allOptions.amount !== 'string') { throw new Error('Option --amount must be a string'); } const cleanedAmount = allOptions.amount.replace(/,/g, ''); allOptions.amount = (0, utils_1.decimalToInteger)(cleanedAmount, 9).toString(); } if (allOptions.fee) { allOptions.fee = (0, utils_1.decimalToInteger)(allOptions.fee, 9); } return { ...allOptions, network }; } ////////////////////////////////////////////////////////////////////////////////////////// // transaction-type translators async function buildUnsignedTx(transactionType, ctx, params) { if (!ctx.cAddressHex) { throw new Error('C-chain address is not set in the context file'); } if (!ctx.pAddressBech32) { throw new Error('P-chain bech32 address is not set in the context file'); } if (!ctx.cAddressBech32) { throw new Error('C-chain bech32 address is not set in the context file'); } const provider = new ethers_1.JsonRpcProvider(settings.URL[ctx.config.hrp] + '/ext/bc/C/rpc'); const evmapi = new flarejs_1.evm.EVMApi(settings.URL[ctx.config.hrp]); const pvmapi = new flarejs_1.pvm.PVMApi(settings.URL[ctx.config.hrp]); const context = await flarejs_1.Context.getContextFromURI(settings.URL[ctx.config.hrp]); const txCount = await provider.getTransactionCount(ctx.cAddressHex); function getChainIdFromContext(sourceChain, context) { return sourceChain === 'C' ? context.cBlockchainID : sourceChain === 'P' ? context.pBlockchainID : context.xBlockchainID; } switch (transactionType) { case 'exportCP': { if (!params.fee) { throw new Error(`fee is required for exportCP transaction. Use --fee <fee> to specify the fee`); } if (!params.amount) { throw new Error(`amount is required for exportCP transaction. Use --amount <amount> to specify the amount`); } const nonce = params.nonce ?? txCount; const exportTx = flarejs_1.evm.newExportTx(context, BigInt(params.amount), context.pBlockchainID, flarejs_1.utils.hexToBuffer(ctx.cAddressHex), [flarejs_1.utils.bech32ToBytes(ctx.pAddressBech32)], BigInt(params.fee), BigInt(nonce)); return exportTx; } case 'importCP': { const { utxos } = await pvmapi.getUTXOs({ sourceChain: 'C', addresses: [ctx.pAddressBech32] }); const importTx = flarejs_1.pvm.newImportTx(context, getChainIdFromContext('C', context), utxos, [flarejs_1.utils.bech32ToBytes(ctx.pAddressBech32)], [flarejs_1.utils.bech32ToBytes(ctx.cAddressBech32)]); return importTx; } case 'exportPC': { const { utxos } = await pvmapi.getUTXOs({ addresses: [ctx.pAddressBech32] }); if (!params.amount) { throw new Error(`amount is required for exportPC transaction. Use --amount <amount> to specify the amount`); } const exportTx = flarejs_1.pvm.newExportTx(context, getChainIdFromContext('C', context), [flarejs_1.utils.bech32ToBytes(ctx.pAddressBech32)], utxos, [ flarejs_1.TransferableOutput.fromNative(context.avaxAssetID, BigInt(params.amount), [ flarejs_1.utils.bech32ToBytes(ctx.pAddressBech32) ]) ]); return exportTx; } case 'importPC': { if (!params.fee) { throw new Error(`fee is required for importPC transaction. Use --fee <fee> to specify the fee`); } const { utxos } = await evmapi.getUTXOs({ sourceChain: 'P', addresses: [ctx.cAddressBech32] }); const exportTx = flarejs_1.evm.newImportTx(context, flarejs_1.utils.hexToBuffer(ctx.cAddressHex), [flarejs_1.utils.bech32ToBytes(ctx.pAddressBech32)], utxos, getChainIdFromContext('P', context), BigInt(params.fee)); return exportTx; } case 'stake': { if (!params.amount) { throw new Error(`amount is required for stake transaction. Use --amount <amount> to specify the amount`); } if (!params.nodeId) { throw new Error(`nodeId is required for stake transaction. Use --node-id <nodeId> to specify the node id`); } if (!params.endTime) { throw new Error(`endTime is required for stake transaction. Use --end-time <endTime> to specify the end time`); } if (!params.popBlsPublicKey) { throw new Error(`popBlsPublicKey is required for stake transaction. Use --pop-bls-public-key <popBlsPublicKey> to specify the BLS public key`); } if (!params.popBlsSignature) { throw new Error(`popBlsSignature is required for stake transaction. Use --pop-bls-signature <popBlsSignature> to specify the BLS signature`); } const { utxos } = await pvmapi.getUTXOs({ addresses: [ctx.pAddressBech32] }); const start = BigInt((0, utils_1.adjustStartTimeForDefi)(params.startTime)); const end = BigInt(params.endTime); const nodeID = params.nodeId; const blsPublicKey = flarejs_1.utils.hexToBuffer(params.popBlsPublicKey); const blsSignature = flarejs_1.utils.hexToBuffer(params.popBlsSignature); const stakeTx = flarejs_1.pvm.newAddPermissionlessValidatorTx(context, utxos, [flarejs_1.utils.bech32ToBytes(ctx.pAddressBech32)], nodeID, flarejs_1.networkIDs.PrimaryNetworkID.toString(), start, end, BigInt(params.amount), [flarejs_1.utils.bech32ToBytes(ctx.pAddressBech32)], [flarejs_1.utils.bech32ToBytes(ctx.pAddressBech32)], Number(params.delegationFee) * 1e4, // default fee is 10% undefined, 1, 0n, blsPublicKey, blsSignature); return stakeTx; } case 'delegate': { if (!params.amount) { throw new Error(`amount is required for stake transaction. Use --amount <amount> to specify the amount`); } if (!params.nodeId) { throw new Error(`nodeId is required for stake transaction. Use --node-id <nodeId> to specify the node id`); } if (!params.endTime) { throw new Error(`endTime is required for stake transaction. Use --end-time <endTime> to specify the end time`); } const { utxos } = await pvmapi.getUTXOs({ addresses: [ctx.pAddressBech32] }); const start = BigInt((0, utils_1.adjustStartTimeForDefi)(params.startTime)); const end = BigInt(params.endTime); const nodeID = params.nodeId; const delegateTx = flarejs_1.pvm.newAddPermissionlessDelegatorTx(context, utxos, [flarejs_1.utils.bech32ToBytes(ctx.pAddressBech32)], nodeID, flarejs_1.networkIDs.PrimaryNetworkID.toString(), start, end, BigInt(params.amount), [flarejs_1.utils.bech32ToBytes(ctx.pAddressBech32)]); return delegateTx; } case 'transfer': { if (!params.amount) { throw new Error(`amount is required for transfer transaction. Use --amount <amount> to specify the amount`); } if (!params.transferAddress) { throw new Error(`transferAddress is required for transfer transaction. Use --transfer-address <address> to specify the address`); } const { utxos } = await pvmapi.getUTXOs({ addresses: [ctx.pAddressBech32] }); const senderPAddressBytes = flarejs_1.utils.bech32ToBytes(ctx.pAddressBech32); const recipientPAddressBytes = flarejs_1.utils.bech32ToBytes(params.transferAddress); const transferTx = flarejs_1.pvm.newBaseTx(context, [senderPAddressBytes], utxos, [ flarejs_1.TransferableOutput.fromNative(context.avaxAssetID, BigInt(params.amount), [recipientPAddressBytes]) ]); return transferTx; } default: throw new Error(`Unknown transaction type: ${transactionType}`); } } async function sendSignedTxJson(ctx, signedTxJson) { const unsignedTx = flarejs_1.UnsignedTx.fromJSON(signedTxJson.serialization); const signature = Buffer.from(signedTxJson.signature, 'hex'); unsignedTx.addSignature(signature); const signedTx = unsignedTx.getSignedTx(); switch (signedTxJson.transactionType) { case 'exportCP': case 'importPC': { const evmapi = new flarejs_1.evm.EVMApi(settings.URL[ctx.config.hrp]); const resp = await evmapi.issueSignedTx(signedTx); return resp.txID; } case 'exportPC': case 'importCP': case 'stake': case 'delegate': case 'transfer': { const pvmapi = new flarejs_1.pvm.PVMApi(settings.URL[ctx.config.hrp]); const resp = await pvmapi.issueSignedTx(signedTx); return resp.txID; } default: throw new Error(`Unknown transaction type: ${signedTxJson.transactionType}`); } } function getPublicKeyFromPair(keypair) { const pk = Buffer.concat(keypair).toString('hex'); return pk; } async function buildAndSendTxUsingPrivateKey(transactionType, ctx, params) { if (transactionType === 'exportCP') { return await (0, transaction_1.exportCP)(ctx, params); } else if (transactionType === 'exportPC') { return await (0, transaction_1.exportPC)(ctx, params); } else if (transactionType === 'importCP') { return await (0, transaction_1.importCP)(ctx, params); } else if (transactionType === 'importPC') { return await (0, transaction_1.importPC)(ctx, params); } else if (transactionType === 'stake') { return await (0, transaction_1.addValidator)(ctx, params); } else if (transactionType === 'delegate') { return await (0, transaction_1.addDelegator)(ctx, params); } else if (transactionType === 'transfer') { return await (0, transaction_1.internalTransfer)(ctx, params); } else { throw new Error(`Unknown transaction type ${transactionType}`); } } ////////////////////////////////////////////////////////////////////////////////////////// // initializing ctx.json async function initCtxJsonFromOptions(options, derivationPath = BASE_DERIVATION_PATH) { let ctxFile; if (options.ledger) { const publicKey = await ledger.getPublicKey(derivationPath, options.network); const address = await ledger.verifyCAddress(derivationPath); const ethAddress = (0, utils_1.publicKeyToEthereumAddressString)(publicKey); ctxFile = { wallet: 'ledger', publicKey, ethAddress, flareAddress: address, network: options.network, derivationPath }; } else if (options.publicKey) { if (!(0, utils_1.validatePublicKey)(options.publicKey)) return (0, output_1.logError)('Invalid public key'); ctxFile = { wallet: 'publicKey', publicKey: options.publicKey, network: options.network }; if (options.vaultId) { ctxFile = { ...ctxFile, vaultId: options.vaultId }; } } else { throw new Error('Either --ledger or --public-key must be specified'); } (0, utils_1.initCtxJson)(ctxFile); (0, output_1.logSuccess)('Context file created'); } ////////////////////////////////////////////////////////////////////////////////////////// // Network info /** * @description Logs the address info * @param ctx - the context file aka ctx.json * @returns Returns the address info */ function logAddressInfo(ctx) { if (!ctx.publicKey) { (0, output_1.logError)('Public key is not set in the context file'); return; } const [pubX, pubY] = ctx.publicKey; const compressedPubKey = (0, utils_1.compressPublicKey)(pubX, pubY).toString('hex'); (0, output_1.logInfo)(`Addresses on the network "${ctx.config.hrp}"`); (0, output_1.log)(`P-chain address: ${ctx.pAddressBech32}`); (0, output_1.log)(`C-chain address hex: ${ctx.cAddressHex}`); (0, output_1.log)(`secp256k1 public key: 0x${compressedPubKey}`); } /** * @description Logs the balance info of the account * @param ctx - the context file aka ctx.json */ async function logBalanceInfo(ctx) { if (!ctx.cAddressHex) { throw new Error('C-chain address is not set in the context file'); } if (!ctx.pAddressBech32) { throw new Error('P-chain bech32 address is not set in the context file'); } let cbalance = (await ctx.web3.eth.getBalance(ctx.cAddressHex)).toString(); let pbalance = ( // NOTE: HRP seems the right string for this function await (0, chain_1.getPBalance)(ctx.config.hrp, ctx.pAddressBech32)).toString(); cbalance = (0, utils_1.integerToDecimal)(cbalance, 18); pbalance = (0, utils_1.integerToDecimal)(pbalance, 9); const symbol = exports.networkTokenSymbol[ctx.config.hrp]; (0, output_1.logInfo)(`Balances on the network "${ctx.config.hrp}"`); (0, output_1.log)(`C-chain ${ctx.cAddressHex}: ${cbalance} ${symbol}`); (0, output_1.log)(`P-chain ${ctx.pAddressBech32}: ${pbalance} ${symbol}`); } /** * @description Logs info aboout P,C and asset id * @param ctx - the context file */ function logNetworkInfo(ctx) { // TODO: necessary? //const pchainId = ctx.pchain.getBlockchainID(); //const cchainId = ctx.cchain.getBlockchainID(); (0, output_1.logInfo)(`Information about the network "${ctx.config.hrp}"`); //log(`blockchainId for P-chain: ${ctx.config.chainID}`); //log(`blockchainId for C-chain: ${ctx.config.chainID}`); //log(`assetId: ${ctx.avaxAssetID}`); } /** * @description Logs the validator information regrading current and pending validators * @param ctx - the context file */ async function logValidatorInfo(ctx) { const pvmapi = new flarejs_1.pvm.PVMApi(settings.URL[ctx.config.hrp]); const pending = await pvmapi.getPendingValidators(); const current = await pvmapi.getCurrentValidators(); //const pending = await ctx.pchain.getPendingValidators(); //const current = await ctx.pchain.getCurrentValidators(); const fpending = JSON.stringify(pending.validators, null, 2); const fpendingDel = JSON.stringify(pending.delegators, null, 2); const fcurrent = JSON.stringify(current.validators, null, 2); (0, output_1.logInfo)(`Validators on the network "${ctx.config.hrp}"`); (0, output_1.log)(`pending validators: ${fpending}`); (0, output_1.log)(`pending delegations: ${fpendingDel}`); (0, output_1.log)(`current: ${fcurrent}`); } /** * @description Logs mirror fund details * @param ctx - context */ async function logMirrorFundInfo(ctx) { const mirroFundDetails = await (0, contracts_1.fetchMirrorFunds)(ctx); (0, output_1.logInfo)(`Mirror fund details on the network "${ctx.config.hrp}"`); (0, output_1.log)(JSON.stringify(mirroFundDetails, null, 2)); } ////////////////////////////////////////////////////////////////////////////////////////// // Transaction building and execution async function cliBuildAndSendTxUsingLedger(transactionType, ctx, params, _blind, _derivationPath) { if (transactionType === 'exportCP' || transactionType === 'exportPC') { (0, output_1.logInfo)('Creating export transaction...'); } if (transactionType === 'importCP' || transactionType === 'importPC') { (0, output_1.logInfo)('Creating import transaction...'); } const sign = async (request) => { if (request.isEvmTx) { return ledger.signEvmTransaction(_derivationPath, request.unsignedTxHex); } else if (await ledger.onlyHashSign()) { // ethereum or avalanche app if (!request.unsignedTxHash) { throw new Error('unsignedTxHash is required for blind signing with ethereum or avalanche app'); } return ledger.signHash(_derivationPath, request.unsignedTxHash); } else { try { return await ledger.sign(_derivationPath, request.unsignedTxHex); } catch (e) { if (typeof e === 'object' && e && 'errorMessage' in e && typeof e.errorMessage === 'string' && e.errorMessage.includes('Data is invalid : Unrecognized error code')) { (0, output_1.logInfo)(`Non-blind signing for transaction type ${transactionType} on network ${ctx.network} is not supported by the Flare app. Blind signing with signing only the hash will be used instead.`); if (!request.unsignedTxHash) { throw new Error('unsignedTxHash is required for ledger signing'); } return ledger.signHash(_derivationPath, request.unsignedTxHash); } else { throw new Error(`Ledger signing error: ${e instanceof Error ? e.message : String(e)}`); } } } }; if (!ctx.publicKey) { throw new Error('Public key is not set in the context file'); } if (transactionType === 'exportCP') { if (!params.amount) { throw new Error(`amount is required for exportCP transaction. Use --amount <amount> to specify the amount`); } const amount = (0, utils_1.toBN)(params.amount); if (!amount) throw new Error(`Amount is invalid: ${params.amount}`); let tp = { amount: amount, exportFee: (0, utils_1.toBN)(params.fee), network: ctx.config.hrp, type: transactionType, publicKey: getPublicKeyFromPair(ctx.publicKey) }; await flare.exportCP(tp, sign); return; } else if (transactionType === 'importCP') { let tp = { network: ctx.config.hrp, type: transactionType, publicKey: getPublicKeyFromPair(ctx.publicKey) }; await flare.importCP(tp, sign); return; } else if (transactionType === 'exportPC') { let tp = { amount: (0, utils_1.toBN)(params.amount), network: ctx.config.hrp, type: transactionType, publicKey: getPublicKeyFromPair(ctx.publicKey) }; await flare.exportPC(tp, sign); return; } else if (transactionType === 'importPC') { let tp = { importFee: (0, utils_1.toBN)(params.fee), network: ctx.config.hrp, type: transactionType, publicKey: getPublicKeyFromPair(ctx.publicKey) }; await flare.importPC(tp, sign); return; } else if (transactionType === 'stake') { (0, output_1.logInfo)('Creating stake transaction...'); if (!params.amount) { throw new Error(`amount is required for stake transaction. Use --amount <amount> to specify the amount`); } if (!params.nodeId) { throw new Error(`nodeId is required for stake transaction. Use --node-id <nodeId> to specify the node id`); } if (!params.endTime) { throw new Error(`endTime is required for stake transaction. Use --end-time <endTime> to specify the end time`); } if (!params.popBlsPublicKey) { throw new Error(`popBlsPublicKey is required for stake transaction. Use --pop-bls-public-key <popBlsPublicKey> to specify the BLS public key`); } if (!params.popBlsSignature) { throw new Error(`popBlsSignature is required for stake transaction. Use --pop-bls-signature <popBlsSignature> to specify the BLS signature`); } let tp = { network: ctx.config.hrp, type: transactionType, publicKey: getPublicKeyFromPair(ctx.publicKey), delegationFee: Number(params.delegationFee) * 1e4, // default fee is 10% nodeId: params.nodeId, popBLSPublicKey: flarejs_1.utils.hexToBuffer(params.popBlsPublicKey), popBLSSignature: flarejs_1.utils.hexToBuffer(params.popBlsSignature), amount: new bn_js_1.BN(params.amount), startTime: new bn_js_1.BN((0, utils_1.adjustStartTime)(params.startTime)), endTime: new bn_js_1.BN(params.endTime), // unnecessary? useConsumableUTXOs: false, customUTXOs: [] }; await flare.addValidator(tp, sign, true); return; } else if (transactionType === 'delegate') { (0, output_1.logInfo)('Creating delegate transaction...'); if (!params.amount) { throw new Error(`amount is required for delegate transaction. Use --amount <amount> to specify the amount`); } if (!params.nodeId) { throw new Error(`nodeId is required for delegate transaction. Use --node-id <nodeId> to specify the node id`); } if (!params.endTime) { throw new Error(`endTime is required for delegate transaction. Use --end-time <endTime> to specify the end time`); } let tp = { network: ctx.config.hrp, type: transactionType, publicKey: getPublicKeyFromPair(ctx.publicKey), nodeId: params.nodeId, amount: new bn_js_1.BN(params.amount), startTime: new bn_js_1.BN((0, utils_1.adjustStartTime)(params.startTime)), endTime: new bn_js_1.BN(params.endTime), // unnecessary? useConsumableUTXOs: false, customUTXOs: [] }; // let presubmit = null (): Promise<boolean> => new Promise(() => false) await flare.addDelegator(tp, sign, true); return; } else if (transactionType === 'transfer') { (0, output_1.logInfo)('Creating transfer transaction...'); if (!params.amount) { throw new Error(`amount is required for transfer transaction. Use --amount <amount> to specify the amount`); } if (!params.transferAddress) { throw new Error(`transferAddress is required for transfer transaction. Use --transfer-address <address> to specify the address`); } const tp = { network: ctx.config.hrp, type: transactionType, publicKey: getPublicKeyFromPair(ctx.publicKey), amount: params.amount, recipientAddress: params.transferAddress, }; await flare.internalTransfer(tp, sign); return; } else { throw new Error(`Unknown transaction type ${transactionType}`); } } async function cliBuildAndSaveUnsignedTxJson(transactionType, ctx, id, params) { const unsignedTx = await buildUnsignedTx(transactionType, ctx, params); const txBuffer = Buffer.from(unsignedTx.toBytes()).toString('hex'); const txMessage = Buffer.from((0, flarejs_1.messageHashFromUnsignedTx)(unsignedTx)).toString('hex'); const unsignedTxJson = { transactionType, signatureRequests: [{ message: txMessage, signer: '' }], unsignedTransactionBuffer: txBuffer, serialization: JSON.stringify(unsignedTx.toJSON()), }; const forDefiHash = (0, utils_1.saveUnsignedTxJson)(unsignedTxJson, id); (0, output_1.logSuccess)(`Unsigned transaction ${id} constructed`); (0, output_1.logSuccess)(`ForDefi hash: ${forDefiHash}`); } async function cliSendSignedTxJson(ctx, id) { if ((0, utils_1.isAlreadySentToChain)(id)) { throw new Error("Tx already sent to chain"); } const signedTxnJson = (0, utils_1.readSignedTxJson)(id); let chainTxId; if (signedTxnJson.transactionType === "EVM") { chainTxId = await (0, evmTx_1.sendSignedEvmTransaction)(ctx, id); } else { chainTxId = await sendSignedTxJson(ctx, signedTxnJson); } (0, utils_1.addFlagForSentSignedTx)(id); (0, output_1.logSuccess)(`Signed transaction ${id} with hash ${chainTxId} sent to the node`); } async function cliBuildAndSendTxUsingPrivateKey(transactionType, ctx, params) { const { txid, usedFee } = await buildAndSendTxUsingPrivateKey(transactionType, ctx, params); const symbol = exports.networkTokenSymbol[ctx.config.hrp]; if (usedFee) (0, output_1.logInfo)(`Used fee of ${(0, utils_1.integerToDecimal)(usedFee, 9)} ${symbol}`); (0, output_1.logSuccess)(`Transaction with hash ${txid} built and sent to the network`); } ////////////////////////////////////////////////////////////////////////////////////////// // ForDefi async function signForDefi(transaction, ctx, evmTx = false) { const txid = await (0, transaction_2.sendToForDefi)(transaction, ctx, evmTx); (0, output_1.logSuccess)(`Transaction with hash ${txid} sent to the ForDefi`); } async function fetchForDefiTx(transaction, evmTx = false) { if ((0, utils_1.isAlreadySentToChain)(transaction)) { throw new Error("Tx already sent to chain"); } const signature = await (0, transaction_2.getSignature)(transaction, evmTx); (0, output_1.logSuccess)(`Success! Signature: ${signature}`); } async function buildUnsignedWithdrawalTxJson(ctx, to, amount, id, nonce) { const forDefiHash = await (0, evmTx_1.createWithdrawalTransaction)(ctx, to, amount, id, nonce); (0, output_1.logSuccess)(`Transaction ${id} constructed`); (0, output_1.logSuccess)(`ForDefi hash: ${forDefiHash}`); } async function buildUnsignedOptOutTxJson(ctx, id, nonce) { const forDefiHash = await (0, evmTx_1.createOptOutTransaction)(ctx, id, nonce); (0, output_1.logSuccess)(`Transaction ${id} constructed`); (0, output_1.logSuccess)(`ForDefi hash: ${forDefiHash}`); } async function processClaimTx(options, id, amount, recipient, wrap, nonce) { const ctx = await contextFromOptions(options); const rawTx = await (0, evmTx_1.createClaimTransaction)(ctx, amount, recipient, wrap, nonce); if (options.getHacked) { if (!ctx.cAddressHex || !ctx.privkHex) { throw new Error('cAddressHex or private key is undefined or null'); } const txId = await (0, evmTx_1.signEvmTransaction)("privateKey", ctx, rawTx); (0, output_1.logSuccess)(`Transaction with hash ${txId} built and sent to the network`); } else if (options.ledger) { const txId = await (0, evmTx_1.signEvmTransaction)("ledger", ctx, rawTx, options.derivationPath); (0, output_1.logSuccess)(`Transaction with hash ${txId} built and sent to the network`); } else { // ForDefi const forDefiHash = (0, evmTx_1.saveUnsignedClaimTx)(rawTx, id); (0, output_1.logSuccess)(`Transaction ${id} constructed`); (0, output_1.logSuccess)(`ForDefi hash: ${forDefiHash}`); } } async function buildUnsignedSetClaimExecutorsTxJson(ctx, id, executors, nonce) { const forDefiHash = await (0, evmTx_1.createSetClaimExecutorsTransaction)(ctx, id, executors, nonce); (0, output_1.logSuccess)(`Transaction ${id} constructed`); (0, output_1.logSuccess)(`ForDefi hash: ${forDefiHash}`); } async function buildUnsignedSetAllowedClaimRecipientsTxJson(ctx, id, recipients, nonce) { const forDefiHash = await (0, evmTx_1.createSetAllowedClaimRecipientsTransaction)(ctx, id, recipients, nonce); (0, output_1.logSuccess)(`Transaction ${id} constructed`); (0, output_1.logSuccess)(`ForDefi hash: ${forDefiHash}`); } async function buildUnsignedCustomCChainTxJson(ctx, id, data, to, value, nonce) { const forDefiHash = await (0, evmTx_1.createCustomCChainTransaction)(ctx, id, to, data, value, nonce); (0, output_1.logSuccess)(`Transaction ${id} constructed`); (0, output_1.logSuccess)(`ForDefi hash: ${forDefiHash}`); } //# sourceMappingURL=cli.js.map