UNPKG

@ledgerhq/live-common

Version:
236 lines • 7.76 kB
/* eslint-disable no-console */ import { LiveConfig } from "@ledgerhq/live-config/LiveConfig"; import BigNumber from "bignumber.js"; import childProcess from "child_process"; import { existsSync, readFileSync, writeFileSync } from "fs"; import { promisify } from "util"; import { argv } from "yargs"; import { firstValueFrom, reduce } from "rxjs"; import { encodeAccountId, fromAccountRaw, toAccountRaw } from "../../account"; import { getAccountBridgeByFamily, getCurrencyBridge } from "../../bridge/impl"; import { liveConfig } from "../../config/sharedConfig"; import { findCryptoCurrencyById, getCryptoCurrencyById, setSupportedCurrencies, } from "../../currencies"; import { migrationAddresses as defaultAddresses } from "./addresses"; // mandatory to run the script setSupportedCurrencies([ "bitcoin", "ethereum", "bsc", "polkadot", "ripple", "litecoin", "polygon", "bitcoin_cash", "stellar", "dogecoin", "cosmos", "dash", "tron", "tezos", "elrond", "ethereum_classic", "zcash", "decred", "digibyte", "algorand", "avalanche_c_chain", "qtum", "bitcoin_gold", "komodo", "zencash", "crypto_org", "crypto_org_croeseid", "celo", "hedera", "cardano", "solana", "osmosis", "fantom", "moonbeam", "cronos", "songbird", "flare", "near", "optimism", "arbitrum", "rsk", "bittorrent", "energy_web", "astar", "metis", "boba", "moonriver", "velas_evm", "syscoin", "axelar", "stargaze", "secret_network", "umee", "desmos", "dydx", "onomy", "sei_network", "persistence", "quicksilver", "vechain", "internet_computer", "klaytn", "polygon_zk_evm", "base", "base_sepolia", "stacks", "telos_evm", "sei_network_evm", "berachain", "hyperevm", "coreum", "injective", "casper", "neon_evm", "lukso", "filecoin", "linea", "ton", "mina", ]); LiveConfig.setConfig(liveConfig); LiveConfig.setAppinfo({ environment: "ci", platform: "headless-linux", }); const args = argv; const { currencies, inputFile, noEmit, outputFolderPath } = args; const getMockAccount = (currencyId, address) => { const currency = getCryptoCurrencyById(currencyId); return { type: "Account", id: encodeAccountId({ type: "js", version: "", currencyId: currencyId, xpubOrAddress: address, derivationMode: "", }), freshAddress: address, xpub: address, derivationMode: "", operations: [], currency, creationDate: new Date(0), balance: new BigNumber(0), spendableBalance: new BigNumber(0), blockHeight: 0, freshAddressPath: "", seedIdentifier: "", index: 0, used: true, operationsCount: 0, pendingOperations: [], lastSyncDate: new Date(0), balanceHistoryCache: { HOUR: { latestDate: 0, balances: [] }, DAY: { latestDate: 0, balances: [] }, WEEK: { latestDate: 0, balances: [] }, }, swapHistory: [], }; }; const testSync = async (currencyId, xpubOrAddress) => { console.log("starting sync on", currencyId, xpubOrAddress); const mockAccount = getMockAccount(currencyId, xpubOrAddress); const currency = getCryptoCurrencyById(currencyId); const currencyBrige = getCurrencyBridge(currency); const data = await currencyBrige.preload(currency); currencyBrige.hydrate(data, currency); const accountBrige = getAccountBridgeByFamily(mockAccount.currency.family, mockAccount.id); const syncedAccount = await firstValueFrom(accountBrige .sync(mockAccount, { paginationConfig: {}, blacklistedTokenIds: [] }) .pipe(reduce((acc, f) => f(acc), mockAccount))); const accountRaw = toAccountRaw(syncedAccount); console.log("finishing sync on", currencyId, xpubOrAddress); return accountRaw; }; const testSyncAccount = async (account) => { console.log("starting sync on", account.currency.id, account.xpub ?? account.freshAddress); const currency = getCryptoCurrencyById(account.currency.id); const currencyBrige = getCurrencyBridge(currency); const data = await currencyBrige.preload(currency); currencyBrige.hydrate(data, currency); const accountBrige = getAccountBridgeByFamily(account.currency.family, account.id); const syncedAccount = await firstValueFrom(accountBrige .sync(account, { paginationConfig: {}, blacklistedTokenIds: [] }) .pipe(reduce((acc, f) => f(acc), account))); const accountRaw = toAccountRaw(syncedAccount); console.log("finishing sync on", account.currency.id, account.xpub ?? account.freshAddress); return accountRaw; }; (async () => { // list of accounts from input file const inputAccounts = []; if (inputFile) { if (!existsSync(inputFile)) { console.warn(`Incorrect file path: ${inputFile} does not exist`, "Will use default addresses instead"); } else { // if there's a input file we parse it inputAccounts.push(JSON.parse(readFileSync(inputFile, "utf8"))); } } const currencyIds = currencies?.split(","); for (const currencyId of currencyIds || []) { if (!findCryptoCurrencyById(currencyId)) { continue; } } // Basically the inputAccounts only exist after the second run // So we first try to sync the default addresses // And in the 2nd run we use the input file (which is the ouput of the first sync run) const migrationAddresses = inputAccounts.length ? inputAccounts : defaultAddresses; const filteredAddresses = migrationAddresses.filter(({ currencyId }) => { if (currencyIds) { return currencyIds.includes(currencyId); } return true; }); let syncedAccounts = []; // if --inputFile we use the addresses from the input file otherwise from addresses.ts if (inputAccounts.length) { syncedAccounts = await Promise.allSettled(filteredAddresses.map(rawAccount => { const account = fromAccountRaw(rawAccount); return testSyncAccount(account); })); } else { syncedAccounts = await Promise.allSettled(filteredAddresses.map(migrationAddress => { return testSync(migrationAddress.currencyId, migrationAddress.xpub ?? migrationAddress.address); })); } const errors = []; const response = syncedAccounts.map((account, i) => { if (account.status === "fulfilled") { return account.value; } else { // Promise.allSettled preserve order so it's safe to use filteredAddresses[i] errors.push(`${filteredAddresses[i].currencyId} ${account.reason.stack}`); } }); if (errors.length) { throw new Error(errors.map(err => `${err}`).join("\n\n")); } const exec = promisify(childProcess.exec); const outputContent = JSON.stringify(response, null, 3); if (!noEmit) { // also used in account-migration.yml to know which input file to use const { stdout } = await exec("git rev-parse --short HEAD"); const outputFilePath = outputFolderPath ? `${outputFolderPath}/${stdout.trim()}.json` : `${stdout.trim()}.json`; console.log("Writing output...."); writeFileSync(outputFilePath, outputContent); console.log(`File created: ${outputFilePath}`); } console.log("Done"); return response; })(); //# sourceMappingURL=account-migration.js.map