diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8788640da9..628a150884 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,6 +6,7 @@ on: push: branches: - master + - develop jobs: start-runner: diff --git a/CODEOWNERS b/CODEOWNERS index 403f5b5949..222ebfae9b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -4,5 +4,6 @@ src/hw @ledgerhq/live-devices src/manager @ledgerhq/live-devices src/apps @ledgerhq/live-devices src/notifications @ledgerhq/live-hub +src/featureFlags @ledgerhq/live-hub src/exchange @ledgerhq/live-platform-and-services src/platform @ledgerhq/live-platform-and-services \ No newline at end of file diff --git a/cli/package.json b/cli/package.json index 70a355692f..b4d7dba292 100644 --- a/cli/package.json +++ b/cli/package.json @@ -27,13 +27,15 @@ "peerDependencies": { "@ledgerhq/hw-transport-node-ble": "5.7.0" }, + "optionalDependencies": { + "@ledgerhq/hw-transport-node-ble": "^6.24.1" + }, "dependencies": { "@ledgerhq/cryptoassets": "6.24.1", "@ledgerhq/errors": "6.10.0", "@ledgerhq/hw-app-btc": "6.24.1", "@ledgerhq/hw-transport-http": "6.24.1", "@ledgerhq/hw-transport-mocker": "6.24.1", - "@ledgerhq/hw-transport-node-ble": "^6.24.1", "@ledgerhq/hw-transport-node-hid": "6.24.1", "@ledgerhq/hw-transport-node-speculos": "6.24.1", "@ledgerhq/ledger-core": "6.14.5", diff --git a/cli/src/live-common-setup.ts b/cli/src/live-common-setup.ts index 4793c72f17..26f8b31f05 100644 --- a/cli/src/live-common-setup.ts +++ b/cli/src/live-common-setup.ts @@ -32,7 +32,9 @@ checkLibs({ connect, }); import implementLibcore from "@ledgerhq/live-common/lib/libcore/platforms/nodejs"; -import BluetoothTransport from "@ledgerhq/hw-transport-node-ble"; + +type BluetoothTransport = any; + implementLibcore({ lib: () => require("@ledgerhq/ledger-core"), // eslint-disable-line global-require @@ -78,6 +80,7 @@ if (process.env.DEVICE_PROXY_URL) { registerTransportModule({ id: "http", open: () => + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore retry(() => Tr.create(3000, 5000), { context: "open-http-proxy", @@ -118,6 +121,8 @@ async function init() { const getTransport = async (): Promise => { if (!TransportNodeBle) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore const { default: mod } = await import("@ledgerhq/hw-transport-node-ble"); TransportNodeBle = mod; } @@ -131,13 +136,9 @@ async function init() { const [, q] = m; if (cacheBle[query]) return cacheBle[query]; const t = await (!q - ? ( - (await getTransport().constructor) as typeof BluetoothTransport - ).create() + ? ((await getTransport().constructor) as typeof TransportNodeBle).create() : new Observable( - ( - (await getTransport().constructor) as typeof BluetoothTransport - ).listen + ((await getTransport().constructor) as typeof TransportNodeBle).listen ) .pipe( first( @@ -166,7 +167,7 @@ async function init() { let s: any; getTransport().then((module) => { - (module.constructor as typeof BluetoothTransport).listen(o); + (module.constructor as typeof TransportNodeBle).listen(o); s = module; }); @@ -182,7 +183,7 @@ async function init() { query.startsWith("ble") ? cacheBle[query] ? ( - (await getTransport().constructor) as typeof BluetoothTransport + (await getTransport().constructor) as typeof TransportNodeBle ).disconnect(cacheBle[query].id) : Promise.resolve() : undefined, diff --git a/cli/yarn.lock b/cli/yarn.lock index dc0189ac1c..2a99841c7c 100644 --- a/cli/yarn.lock +++ b/cli/yarn.lock @@ -6935,9 +6935,9 @@ uri-js@^4.2.2: punycode "^2.1.0" urijs@^1.19.1: - version "1.19.7" - resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.7.tgz#4f594e59113928fea63c00ce688fb395b1168ab9" - integrity sha512-Id+IKjdU0Hx+7Zx717jwLPsPeUqz7rAtuVBRLLs+qn+J2nf9NGITWVCxcijgYxBqe83C7sqsQPs6H1pyz3x9gA== + version "1.19.8" + resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.8.tgz#ee0407a18528934d3c383e691912f47043a58feb" + integrity sha512-iIXHrjomQ0ZCuDRy44wRbyTZVnfVNLVo3Ksz1yxNyE5wV1IDZW2S5Jszy45DTlw/UdsnRT7DyDhIz7Gy+vJumw== url-parse-lax@^1.0.0: version "1.0.0" @@ -6954,9 +6954,9 @@ url-parse-lax@^3.0.0: prepend-http "^2.0.0" url-parse@^1.4.3, url-parse@^1.5.1: - version "1.5.7" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.7.tgz#00780f60dbdae90181f51ed85fb24109422c932a" - integrity sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA== + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== dependencies: querystringify "^2.1.1" requires-port "^1.0.0" diff --git a/dependencies.md b/dependencies.md new file mode 100644 index 0000000000..ef60d29818 --- /dev/null +++ b/dependencies.md @@ -0,0 +1,100 @@ +This document allows to track, explain and maintain the dependencies we have defined in package.json. + +### Direct Dependencies + +| library name (what). | description of its usage (why) | ideal frequency of update (when) / status | +|------------------------|--------------------------------|----------------------------------| +|@celo/contractkit | Celo coin integration | monthly | +|@celo/wallet-base | Celo coin integration | monthly | +|@celo/wallet-ledger | Celo coin integration | monthly | +|@cosmjs/crypto | Cosmos coin integration | monthly | +|@cosmjs/ledger-amino | Cosmos coin integration | monthly | +|@cosmjs/proto-signing | Cosmos coin integration | monthly | +|@cosmjs/stargate | Cosmos coin integration | monthly | +|@crypto-com/chain-jslib | Crypto.com coin integration | monthly | +|@ethereumjs/common | Ethereum coin integration | monthly | +|@ethereumjs/tx | Ethereum coin integration | monthly | +|@ledgerhq/compressjs | used for LiveQR feature | stable | +|@ledgerhq/cryptoassets | crypto currencies and tokens | weekly | +|@ledgerhq/devices | devices data | weekly | +|@ledgerhq/errors | errors defintion | weekly | +|@ledgerhq/hw-app-algorand| Algorand coin integration | weekly | +|@ledgerhq/hw-app-btc | Bitcoin coin integration | weekly | +|@ledgerhq/hw-app-cosmos | Cosmos coin integration | weekly | +|@ledgerhq/hw-app-eth | Ethereum coin integration | weekly | +|@ledgerhq/hw-app-polkadot| Polkadot coin integration | weekly | +|@ledgerhq/hw-app-solana | Solana coin integration | weekly | +|@ledgerhq/hw-app-str | Ethereum coin integration | weekly | +|@ledgerhq/hw-app-tezos | Tezos coin integration | weekly | +|@ledgerhq/hw-app-trx | TRON coin integration | weekly | +|@ledgerhq/hw-app-xrp | XRP coin integration | weekly | +|@ledgerhq/hw-transport | device communication | weekly | +|@ledgerhq/hw-transport-mocker| used by tests | weekly | +|@ledgerhq/hw-transport-node-speculos| used by bot tests | weekly | +|@ledgerhq/json-bignumber| workaround for Ledger explorers who don't give String in some API | stable | +|@ledgerhq/live-app-sdk | utils for live apps feature | ??? | +|@ledgerhq/logs | logs | weekly | +|@polkadot/types | Polkadot coin integration | **BLOCKED BY LLM (ticket missing)** | +|@polkadot/types-known | Polkadot coin integration | **BLOCKED BY LLM (ticket missing)** | +|@solana/spl-token | Solana coin integration | monthly | +|@solana/web3.js | Solana coin integration | monthly | +|@taquito/ledger-signer | Tezos coin integration | **BLOCKED BY LLM (ticket missing)** | +|@taquito/taquito | Tezos coin integration | **BLOCKED BY LLM (ticket missing)** | +|@types/bchaddrjs | Bitcoin coin integration | monthly | +|@types/bs58check | Bitcoin coin integration | monthly | +|@walletconnect/client | Wallet connect feature | monthly | +|@xstate/react | used for some components | TBD DEPRECATE? | +|@zondax/ledger-filecoin | Filecoin coin integration | monthly | +|algosdk | Algorand coin integration | monthly | +|async | ??? | UNCLEAR IF USED | +|axios | network | monthly | +|axios-retry | network | monthly | +|base32-decode | Filecoin coin integration | monthly | +|bchaddrjs | Bitcoin coin integration | monthly | +|bech32 | Bitcoin coin integration | BLOCKED? TBD | +|bignumber.js | many parts involving amounts | monthly | +|bip32 | coin integrations | monthly | +|bip32-path | coin integrations | monthly | +|bip39 | needed for bot | monthly | +|bitcoinjs-lib | Bitcoin coin integration | monthly | +|blake-hash | Bitcoin coin integration | monthly | +|bs58 | Bitcoin coin integration | monthly | +|bs58check | Bitcoin coin integration | monthly | +|buffer | many parts for bytes ops | monthly | +|cashaddrjs | Bitcoin coin integration | monthly | +|cbor | Filecoin coin integration | monthly | +|coininfo | Bitcoin coin integration | monthly | +|crypto-js | NEO coin integration | monthly | +|eip55 | Ethereum coin integration | monthly | +|eth-sig-util | Ethereum coin integration | monthly | +|ethereumjs-abi | Ethereum coin integration | monthly | +|ethereumjs-util | Ethereum coin integration | monthly | +|expect | Tests | monthly | +|generic-pool | Bitcoin coin integration | monthly | +|invariant | generic helper | monthly | +|isomorphic-ws | WebSocket helper | monthly | +|json-rpc-2.0 | Ethereum coin integration | monthly | +|leb128 | Filecoin coin integration | monthly | +|lodash | generic helper | monthly | +|lru-cache | generic helper | monthly | +|numeral | for very concise amount display (on graph) | monthly – **TBD if can be dropped** | +|object-hash | Solana coin integration | monthly | +|performance-now | bot | monthly – may not strongly need | +|prando | account mocks | stable – try not to upgrade to not change the mock data too often | +|redux | general react helper | monthly | +|reselect | general react helper | monthly | +|ripemd160 | Bitcoin coin integration | monthly | +|ripple-binary-codec | XRP coin integration | monthly | +|ripple-bs58check | XRP coin integration | monthly | +|ripple-lib | XRP coin integration | monthly | +|rlp | Ethereum coin integration | monthly | +|rxjs | generic helper | BLOCKED by issue revealed when trying to upgrade. **ticket missing** | +|rxjs-compat | generic helper | BLOCKED by issue revealed when trying to upgrade. **ticket missing** | +|secp256k1 | Bitcoin coin integration | monthly | +|semver | generic helper | monthly | +|sha.js | generic helper for crypto | monthly | +|stellar-sdk | Stellar coin integration | monthly | +|triple-beam | logs | monthly | +|winston | logs | monthly | +|xstate | generic helper for React | **TBD why it's needed.** | +|zcash-bitcore-lib | Bitcoin coin integration | monthly | diff --git a/mobile-test-app/yarn.lock b/mobile-test-app/yarn.lock index 2a7cb54fe7..6a8fe444a0 100644 --- a/mobile-test-app/yarn.lock +++ b/mobile-test-app/yarn.lock @@ -6860,9 +6860,9 @@ unset-value@^1.0.0: isobject "^3.0.0" urijs@^1.19.1: - version "1.19.7" - resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.7.tgz#4f594e59113928fea63c00ce688fb395b1168ab9" - integrity sha512-Id+IKjdU0Hx+7Zx717jwLPsPeUqz7rAtuVBRLLs+qn+J2nf9NGITWVCxcijgYxBqe83C7sqsQPs6H1pyz3x9gA== + version "1.19.8" + resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.8.tgz#ee0407a18528934d3c383e691912f47043a58feb" + integrity sha512-iIXHrjomQ0ZCuDRy44wRbyTZVnfVNLVo3Ksz1yxNyE5wV1IDZW2S5Jszy45DTlw/UdsnRT7DyDhIz7Gy+vJumw== urix@^0.1.0: version "0.1.0" @@ -6870,9 +6870,9 @@ urix@^0.1.0: integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= url-parse@^1.4.3: - version "1.5.7" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.7.tgz#00780f60dbdae90181f51ed85fb24109422c932a" - integrity sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA== + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== dependencies: querystringify "^2.1.1" requires-port "^1.0.0" diff --git a/package.json b/package.json index 1430a9e8e7..de9438dff0 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "type": "git", "url": "https://github.com/LedgerHQ/ledger-live-common" }, - "version": "21.33.0", + "version": "21.34.0", "main": "lib/index.js", "types": "lib/index.d.ts", "license": "Apache-2.0", diff --git a/src/families/bitcoin/getAccountNetworkInfo.ts b/src/families/bitcoin/getAccountNetworkInfo.ts index b5ec7976f9..d13cb34c91 100644 --- a/src/families/bitcoin/getAccountNetworkInfo.ts +++ b/src/families/bitcoin/getAccountNetworkInfo.ts @@ -45,14 +45,16 @@ export async function getAccountNetworkInfo( if (feesPerByte.length !== 3) { throw new Error("cardinality of feesPerByte should be exactly 3"); } - // Fix fees if suggested fee is too low, this is only for viacoin because the viacoin fees backend endpoint is broken + // Fix fees if suggested fee is too low, this is only for viacoin/decred because the fees backend endpoint is broken if ( - account.currency.id === "viacoin" && + (account.currency.id === "viacoin" || account.currency.id === "decred") && feesPerByte[2].toNumber() < Math.ceil(relayFee * 100000) ) { feesPerByte[2] = new BigNumber(Math.ceil(relayFee * 100000)).plus(1); feesPerByte[1] = feesPerByte[2].plus(1); - feesPerByte[0] = feesPerByte[1].plus(1); + if (feesPerByte[1].plus(1).gt(feesPerByte[0])) { + feesPerByte[0] = feesPerByte[1].plus(1); + } } const feeItems = { items: feesPerByte.map((feePerByte, i) => ({ diff --git a/src/families/bitcoin/wallet-btc/utils.ts b/src/families/bitcoin/wallet-btc/utils.ts index da354f5deb..ef411662a9 100644 --- a/src/families/bitcoin/wallet-btc/utils.ts +++ b/src/families/bitcoin/wallet-btc/utils.ts @@ -200,7 +200,10 @@ export function maxTxSize( // Input: 32 PrevTxHash + 4 Index + 1 scriptSigLength + 4 sequence let inputsWeight = byteSize(inputCount) * baseByte; // Number of inputs inputsWeight += inputWeight(derivationMode) * inputCount; - + // More bytes for decred, refer to https://github.com/LedgerHQ/lib-ledger-core/blob/fc9d762b83fc2b269d072b662065747a64ab2816/core/src/wallet/bitcoin/api_impl/BitcoinLikeTransactionApi.cpp#L162 + if (currency.network.name === "Decred") { + inputsWeight += 64 * inputCount; + } const txWeight = fixed + inputsWeight + outputsWeight; return txWeight / 4; } diff --git a/src/families/elrond/speculos-deviceActions.ts b/src/families/elrond/speculos-deviceActions.ts index d3c604b3fd..67d8b7ce95 100644 --- a/src/families/elrond/speculos-deviceActions.ts +++ b/src/families/elrond/speculos-deviceActions.ts @@ -6,25 +6,20 @@ const acceptTransaction: DeviceAction = deviceActionFlow({ steps: [ { title: "Receiver", - button: "RRRRrrrr", + button: "Rr", expectedValue: ({ transaction }) => transaction.recipient, }, { title: "Amount", button: "Rr", - expectedValue: ({ account, transaction }) => { - const formattedValue = - formatCurrencyUnit(account.unit, transaction.amount, { - disableRounding: true, - }) + " eGLD"; - - if (!formattedValue.includes(".")) { - // if the value is pure integer, in the app it will automatically add an .0 - return formattedValue + ".0"; - } - - return formattedValue; - }, + expectedValue: ({ account, transaction }) => + formatCurrencyUnit(account.unit, transaction.amount, { + showCode: true, + disableRounding: true, + joinFragmentsSeparator: " ", + }) + .replace(/\s+/g, " ") + .replace("egld", "EGLD"), // FIXME }, { title: "Fee", @@ -39,6 +34,11 @@ const acceptTransaction: DeviceAction = deviceActionFlow({ title: "Sign", button: "LRlr", }, + { + title: "Network", + button: "Rr", + expectedValue: () => "Mainnet", + }, ], }); export default { diff --git a/src/families/ethereum/specs.ts b/src/families/ethereum/specs.ts index 19d3c0af35..83a5c507bc 100644 --- a/src/families/ethereum/specs.ts +++ b/src/families/ethereum/specs.ts @@ -20,6 +20,8 @@ import { DeviceModelId } from "@ledgerhq/devices"; import { TokenCurrency } from "@ledgerhq/cryptoassets"; import { CompoundAccountSummary } from "../../compound/types"; +const testTimeout = 5 * 60 * 1000; + const ethereumBasicMutations = ({ maxAccount }) => [ { name: "move 50%", @@ -119,7 +121,7 @@ const ethereum: AppSpec = { model: DeviceModelId.nanoS, appName: "Ethereum", }, - testTimeout: 4 * 60 * 1000, + testTimeout, transactionCheck: ({ maxSpendable }) => { invariant( maxSpendable.gt( @@ -357,7 +359,7 @@ const ethereumClassic: AppSpec = { appName: "Ethereum Classic", }, dependency: "Ethereum", - testTimeout: 2 * 60 * 1000, + testTimeout, transactionCheck: ({ maxSpendable }) => { invariant( maxSpendable.gt( @@ -380,7 +382,7 @@ const ethereumRopsten: AppSpec = { model: DeviceModelId.nanoS, appName: "Ethereum", }, - testTimeout: 2 * 60 * 1000, + testTimeout, transactionCheck: ({ maxSpendable }) => { invariant( maxSpendable.gt( @@ -405,7 +407,7 @@ const bsc: AppSpec = { appName: "Binance Smart Chain", }, dependency: "Ethereum", - testTimeout: 2 * 60 * 1000, + testTimeout, transactionCheck: ({ maxSpendable }) => { invariant( maxSpendable.gt( @@ -452,7 +454,7 @@ const polygon: AppSpec = { appName: "Polygon", }, dependency: "Ethereum", - testTimeout: 2 * 60 * 1000, + testTimeout, transactionCheck: ({ maxSpendable }) => { invariant( maxSpendable.gt( diff --git a/src/families/filecoin/account.ts b/src/families/filecoin/account.ts new file mode 100644 index 0000000000..4397d7c064 --- /dev/null +++ b/src/families/filecoin/account.ts @@ -0,0 +1,44 @@ +import BigNumber from "bignumber.js"; + +export function fromOperationExtraRaw( + extra: Record | null | undefined +): Record | null | undefined { + if (!extra) return extra; + + const { gasLimit, gasPremium, gasFeeCap } = extra; + + if (gasLimit !== undefined) + extra = { ...extra, gasLimit: new BigNumber(gasLimit) }; + + if (gasPremium !== undefined) + extra = { ...extra, gasPremium: new BigNumber(gasPremium) }; + + if (gasFeeCap !== undefined) + extra = { ...extra, gasFeeCap: new BigNumber(gasFeeCap) }; + + return extra; +} + +export function toOperationExtraRaw( + extra: Record | null | undefined +): Record | null | undefined { + if (!extra) return extra; + + const { gasLimit, gasPremium, gasFeeCap } = extra; + + if (gasLimit !== undefined) + extra = { ...extra, gasLimit: gasLimit.toNumber() }; + + if (gasPremium !== undefined) + extra = { ...extra, gasPremium: gasPremium.toFixed() }; + + if (gasFeeCap !== undefined) + extra = { ...extra, gasFeeCap: gasFeeCap.toFixed() }; + + return extra; +} + +export default { + fromOperationExtraRaw, + toOperationExtraRaw, +}; diff --git a/src/families/filecoin/bridge/account.ts b/src/families/filecoin/bridge/account.ts index 3b4fe5bb0d..f5b53cecd9 100644 --- a/src/families/filecoin/bridge/account.ts +++ b/src/families/filecoin/bridge/account.ts @@ -66,8 +66,10 @@ const getTransactionStatus = async ( const errors: TransactionStatus["errors"] = {}; const warnings: TransactionStatus["warnings"] = {}; + const { balance } = a; const { address } = getAddress(a); - const { recipient, amount, gasPremium, gasFeeCap, gasLimit } = t; + const { recipient, useAllAmount, gasPremium, gasFeeCap, gasLimit } = t; + let { amount } = t; if (!recipient) errors.recipient = new RecipientRequired(); else if (!validateAddress(recipient).isValid) @@ -81,8 +83,8 @@ const getTransactionStatus = async ( // This is the worst case scenario (the tx won't cost more than this value) const estimatedFees = calculateEstimatedFees(gasFeeCap, gasLimit); - // Add the estimated fees to the tx amount - const totalSpent = amount.plus(estimatedFees); + const totalSpent = useAllAmount ? balance : amount.plus(estimatedFees); + amount = useAllAmount ? balance.minus(estimatedFees) : amount; if (amount.lte(0)) errors.amount = new AmountRequired(); if (totalSpent.gt(a.spendableBalance)) errors.amount = new NotEnoughBalance(); @@ -174,11 +176,13 @@ const prepareTransaction = async ( const sync = makeSync(getAccountShape); const broadcast: BroadcastFnSignature = async ({ - signedOperation: { operation }, + signedOperation: { operation, signature }, }) => { // log("debug", "[broadcast] start fn"); - const resp = await broadcastTx(operation.extra.reqToBroadcast); + const tx = getTxToBroadcast(operation, signature); + + const resp = await broadcastTx(tx); const { hash } = resp; const result = patchOperationWithHash(operation, hash); @@ -199,8 +203,18 @@ const signOperation: SignOperationFnSignature = ({ async function main() { // log("debug", "[signOperation] start fn"); - const { recipient, amount, gasFeeCap, gasLimit } = transaction; - const { id: accountId } = account; + const { + recipient, + method, + version, + nonce, + gasFeeCap, + gasLimit, + gasPremium, + useAllAmount, + } = transaction; + let { amount } = transaction; + const { id: accountId, balance } = account; const { address, derivationPath } = getAddress(account); if (!gasFeeCap.gt(0) || !gasLimit.gt(0)) { @@ -218,6 +232,11 @@ const signOperation: SignOperationFnSignature = ({ type: "device-signature-requested", }); + const fee = calculateEstimatedFees(gasFeeCap, gasLimit); + if (useAllAmount) amount = balance.minus(fee); + + transaction = { ...transaction, amount }; + // Serialize tx const serializedTx = toCBOR( getAddressRaw(address), @@ -243,21 +262,12 @@ const signOperation: SignOperationFnSignature = ({ type: "device-signature-granted", }); - const fee = calculateEstimatedFees(gasFeeCap, gasLimit); - const value = amount.plus(fee); - // resolved at broadcast time const txHash = ""; // build signature on the correct format const signature = `${result.signature_compact.toString("base64")}`; - const reqToBroadcast = getTxToBroadcast( - account, - transaction, - signature - ); - const operation: Operation = { id: `${accountId}-${txHash}-OUT`, hash: txHash, @@ -265,13 +275,19 @@ const signOperation: SignOperationFnSignature = ({ senders: [address], recipients: [recipient], accountId, - value, + value: amount, fee, blockHash: null, blockHeight: null, date: new Date(), extra: { - reqToBroadcast, + gasLimit, + gasFeeCap, + gasPremium, + method, + version, + nonce, + signatureType: 1, }, }; diff --git a/src/families/filecoin/bridge/utils/utils.ts b/src/families/filecoin/bridge/utils/utils.ts index c2e626db74..7b4ba9b953 100644 --- a/src/families/filecoin/bridge/utils/utils.ts +++ b/src/families/filecoin/bridge/utils/utils.ts @@ -12,7 +12,6 @@ import { import { fetchBalances, fetchBlockHeight, fetchTxs } from "./api"; import { encodeAccountId } from "../../../../account"; import flatMap from "lodash/flatMap"; -import { Transaction } from "../../types"; type TxsById = { [id: string]: { @@ -103,21 +102,19 @@ export const getAddress = (a: Account): Address => : { address: a.freshAddress, derivationPath: a.freshAddressPath }; export const getTxToBroadcast = ( - account: Account, - transaction: Transaction, + operation: Operation, signature: string ): BroadcastTransactionRequest => { - const { address } = getAddress(account); + const { extra, senders, recipients, value } = operation; const { - recipient, - amount, gasLimit, gasFeeCap, gasPremium, method, version, nonce, - } = transaction; + signatureType, + } = extra; return { message: { @@ -125,15 +122,15 @@ export const getTxToBroadcast = ( method, nonce, params: "", - to: recipient, - from: address, + to: recipients[0], + from: senders[0], gaslimit: gasLimit.toNumber(), gaspremium: gasPremium.toString(), gasfeecap: gasFeeCap.toString(), - value: amount.toFixed(), + value: value.toFixed(), }, signature: { - type: 1, + type: signatureType, data: signature, }, }; diff --git a/src/families/filecoin/deviceTransactionConfig.ts b/src/families/filecoin/deviceTransactionConfig.ts new file mode 100644 index 0000000000..e452d19b64 --- /dev/null +++ b/src/families/filecoin/deviceTransactionConfig.ts @@ -0,0 +1,73 @@ +import type { DeviceTransactionField } from "../../transaction"; +import type { Account, AccountLike, TransactionStatus } from "../../types"; +import type { Transaction } from "./types"; +import { formatCurrencyUnit, getCryptoCurrencyById } from "../../currencies"; +import { methodToString } from "./utils"; + +const currency = getCryptoCurrencyById("filecoin"); + +export type ExtraDeviceTransactionField = + | { + type: "filecoin.gasFeeCap"; + label: string; + value: string; + } + | { + type: "filecoin.gasPremium"; + label: string; + value: string; + } + | { + type: "filecoin.gasLimit"; + label: string; + value: string; + } + | { + type: "filecoin.method"; + label: string; + value: string; + }; + +function getDeviceTransactionConfig(input: { + account: AccountLike; + parentAccount: Account | null | undefined; + transaction: Transaction; + status: TransactionStatus; +}): Array { + const fields: Array = []; + + fields.push({ + type: "amount", + label: "Value", + }); + fields.push({ + type: "filecoin.gasLimit", + label: "Gas Limit", + value: input.transaction.gasLimit.toFixed(), + }); + fields.push({ + type: "filecoin.gasPremium", + label: "Gas Premium", + value: formatCurrencyUnit(currency.units[0], input.transaction.gasPremium, { + showCode: false, + disableRounding: true, + }), + }); + fields.push({ + type: "filecoin.gasFeeCap", + label: "Gas Fee Cap", + value: formatCurrencyUnit(currency.units[0], input.transaction.gasFeeCap, { + showCode: false, + disableRounding: true, + }), + }); + fields.push({ + type: "filecoin.method", + label: "Method", + value: methodToString(input.transaction.method), + }); + + return fields; +} + +export default getDeviceTransactionConfig; diff --git a/src/families/filecoin/specs.ts b/src/families/filecoin/specs.ts index bd29867601..0a0d810697 100644 --- a/src/families/filecoin/specs.ts +++ b/src/families/filecoin/specs.ts @@ -14,8 +14,6 @@ const filecoinSpecs: AppSpec = { appQuery: { model: DeviceModelId.nanoS, appName: "Filecoin", - firmware: "2.0.0", - appVersion: "0.18.3", }, testTimeout: 2 * 60 * 1000, diff --git a/src/families/filecoin/transaction.ts b/src/families/filecoin/transaction.ts index 9ddb3dd8ed..e0d81b2b85 100644 --- a/src/families/filecoin/transaction.ts +++ b/src/families/filecoin/transaction.ts @@ -8,12 +8,22 @@ import { getAccountUnit } from "../../account"; import { formatCurrencyUnit } from "../../currencies"; import BigNumber from "bignumber.js"; -export const formatTransaction = (t: Transaction, account: Account): string => ` -SEND ${formatCurrencyUnit(getAccountUnit(account), t.amount, { - showCode: true, - disableRounding: true, -})} -TO ${t.recipient}`; +export const formatTransaction = ( + { recipient, useAllAmount, amount }: Transaction, + account: Account +): string => ` +SEND ${ + useAllAmount + ? "MAX" + : amount.isZero() + ? "" + : " " + + formatCurrencyUnit(getAccountUnit(account), amount, { + showCode: true, + disableRounding: true, + }) +} +TO ${recipient}`; export const fromTransactionRaw = (tr: TransactionRaw): Transaction => { const common = fromTransactionCommonRaw(tr); diff --git a/src/families/filecoin/utils.ts b/src/families/filecoin/utils.ts index 831da319e1..3fe719a3d2 100644 --- a/src/families/filecoin/utils.ts +++ b/src/families/filecoin/utils.ts @@ -19,6 +19,15 @@ export const isError = (r: { return_code: number; error_message: string }) => { throw new Error(`${r.return_code} - ${r.error_message}`); }; +export const methodToString = (method: number): string => { + switch (method) { + case 0: + return "Transfer"; + default: + return "Unknown"; + } +}; + export const getBufferFromString = (message: string): Buffer => isValidHex(message) ? Buffer.from(message, "hex") diff --git a/src/families/ripple/specs.ts b/src/families/ripple/specs.ts index 491c63dc14..30d384249c 100644 --- a/src/families/ripple/specs.ts +++ b/src/families/ripple/specs.ts @@ -13,7 +13,6 @@ const ripple: AppSpec = { currency, appQuery: { model: DeviceModelId.nanoS, - firmware: "<2", appName: "XRP", }, mutations: [ diff --git a/src/families/tezos/api/tzkt.ts b/src/families/tezos/api/tzkt.ts index c314c2d706..8e697b67f7 100644 --- a/src/families/tezos/api/tzkt.ts +++ b/src/families/tezos/api/tzkt.ts @@ -36,6 +36,8 @@ type CommonOperationType = { timestamp: string; level: number; block: string; + gasLimit?: number; + storageLimit?: number; status?: "applied" | "failed" | "backtracked" | "skipped"; }; diff --git a/src/families/tezos/bridge/js.ts b/src/families/tezos/bridge/js.ts index 28af4b652f..58691d6513 100644 --- a/src/families/tezos/bridge/js.ts +++ b/src/families/tezos/bridge/js.ts @@ -331,13 +331,11 @@ const estimateMaxSpendable = async ({ return s.amount; }; -const broadcast = async ({ signedOperation: { operation } }) => { +const broadcast = async ({ signedOperation: { operation, signature } }) => { const tezos = new TezosToolkit(getEnv("API_TEZOS_NODE")); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - const hash = await tezos.contract.context.injector.inject( - operation.extra.opbytes - ); + const hash = await tezos.contract.context.injector.inject(signature); return patchOperationWithHash(operation, hash); }; diff --git a/src/families/tezos/signOperation.ts b/src/families/tezos/signOperation.ts index 98bf631a51..a857a7d930 100644 --- a/src/families/tezos/signOperation.ts +++ b/src/families/tezos/signOperation.ts @@ -49,7 +49,7 @@ export const signOperation = ({ let type = "OUT"; - let res, signature, opbytes; + let res, opbytes; const params = { fee: transaction.fees?.toNumber() || 0, storageLimit: transaction.storageLimit?.toNumber() || 0, @@ -73,7 +73,6 @@ export const signOperation = ({ amount: transaction.amount.toNumber(), ...params, }); - signature = res.raw.opOb.signature; opbytes = res.raw.opbytes; break; case "delegate": @@ -119,7 +118,6 @@ export const signOperation = ({ extra: { storageLimit: transaction.storageLimit, gasLimit: transaction.gasLimit, - opbytes, }, blockHash: null, blockHeight: null, @@ -133,7 +131,7 @@ export const signOperation = ({ type: "signed", signedOperation: { operation, - signature, + signature: opbytes, expirationDate: null, }, }); diff --git a/src/families/tezos/synchronisation.ts b/src/families/tezos/synchronisation.ts index 6e37618397..0328c12b30 100644 --- a/src/families/tezos/synchronisation.ts +++ b/src/families/tezos/synchronisation.ts @@ -219,6 +219,8 @@ const txToOp = level: blockHeight, block: blockHash, timestamp, + storageLimit, + gasLimit, } = tx; if (!hash) { @@ -253,7 +255,7 @@ const txToOp = blockHash, accountId, date: new Date(timestamp), - extra: { id }, + extra: { gasLimit: gasLimit, storageLimit: storageLimit, id }, hasFailed, }; }; diff --git a/src/featureFlags/FeatureToggle.tsx b/src/featureFlags/FeatureToggle.tsx index 0fbe8ca6ac..c9ce7f6835 100644 --- a/src/featureFlags/FeatureToggle.tsx +++ b/src/featureFlags/FeatureToggle.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from "react"; +import React, { ReactNode } from "react"; import useFeature from "./useFeature"; import { FeatureId } from "./types"; @@ -13,12 +13,12 @@ export const FeatureToggle = ({ feature: featureId, fallback, children, -}: Props): ReactNode => { +}: Props): JSX.Element => { const feature = useFeature(featureId); if (!feature || !feature.enabled) { - return fallback ?? null; + return <>{fallback || null}; } - return children ?? null; + return <>{children || null}; }; diff --git a/src/generated/account.ts b/src/generated/account.ts index ece6073d38..e4f6314b1a 100644 --- a/src/generated/account.ts +++ b/src/generated/account.ts @@ -8,6 +8,8 @@ import crypto_org from "../families/crypto_org/account"; import elrond from "../families/elrond/account"; +import filecoin from "../families/filecoin/account"; + import polkadot from "../families/polkadot/account"; @@ -17,5 +19,6 @@ export default { cosmos, crypto_org, elrond, + filecoin, polkadot, }; diff --git a/src/generated/deviceTransactionConfig.ts b/src/generated/deviceTransactionConfig.ts index 014ca10c3f..1e371d2df9 100644 --- a/src/generated/deviceTransactionConfig.ts +++ b/src/generated/deviceTransactionConfig.ts @@ -12,6 +12,8 @@ import elrond from "../families/elrond/deviceTransactionConfig"; import ethereum from "../families/ethereum/deviceTransactionConfig"; +import filecoin from "../families/filecoin/deviceTransactionConfig"; + import polkadot from "../families/polkadot/deviceTransactionConfig"; import ripple from "../families/ripple/deviceTransactionConfig"; @@ -33,6 +35,7 @@ export default { crypto_org, elrond, ethereum, + filecoin, polkadot, ripple, solana, @@ -41,12 +44,14 @@ export default { tron, }; import { ExtraDeviceTransactionField as ExtraDeviceTransactionField_cosmos } from "../families/cosmos/deviceTransactionConfig"; +import { ExtraDeviceTransactionField as ExtraDeviceTransactionField_filecoin } from "../families/filecoin/deviceTransactionConfig"; import { ExtraDeviceTransactionField as ExtraDeviceTransactionField_polkadot } from "../families/polkadot/deviceTransactionConfig"; import { ExtraDeviceTransactionField as ExtraDeviceTransactionField_stellar } from "../families/stellar/deviceTransactionConfig"; import { ExtraDeviceTransactionField as ExtraDeviceTransactionField_tezos } from "../families/tezos/deviceTransactionConfig"; import { ExtraDeviceTransactionField as ExtraDeviceTransactionField_tron } from "../families/tron/deviceTransactionConfig"; export type ExtraDeviceTransactionField = | ExtraDeviceTransactionField_cosmos +| ExtraDeviceTransactionField_filecoin | ExtraDeviceTransactionField_polkadot | ExtraDeviceTransactionField_stellar | ExtraDeviceTransactionField_tezos diff --git a/src/load/speculos.ts b/src/load/speculos.ts index 2e26d95c5a..84590bacd1 100644 --- a/src/load/speculos.ts +++ b/src/load/speculos.ts @@ -299,10 +299,6 @@ export type AppSearch = { appVersion?: string; }; -function semverSatisfies(a, b) { - return semver.satisfies(a, b) || semver.satisfies(semver.coerce(a), b); -} - export function appCandidatesMatches( appCandidate: AppCandidate, search: AppSearch @@ -316,12 +312,12 @@ export function appCandidatesMatches( ((!searchFirmware && !appCandidate.firmware.includes("rc")) || appCandidate.firmware === searchFirmware || (searchFirmware && - semverSatisfies( + semver.satisfies( hackBadSemver(appCandidate.firmware), searchFirmware ))) && (!search.appVersion || - semverSatisfies(appCandidate.appVersion, search.appVersion)) + semver.satisfies(appCandidate.appVersion, search.appVersion)) ); } export const findAppCandidate = ( diff --git a/src/market/api/api.mock.ts b/src/market/api/api.mock.ts index 91d754e6c4..76859b7603 100644 --- a/src/market/api/api.mock.ts +++ b/src/market/api/api.mock.ts @@ -1,4 +1,9 @@ -import { CurrencyData, SupportedCoins } from "../types"; +import { + CurrencyData, + MarketCoin, + MarketListRequestParams, + SupportedCoins, +} from "../types"; import { listCryptoCurrencies, listTokens } from "../../currencies"; const cryptoCurrenciesList = [...listCryptoCurrencies(), ...listTokens()]; @@ -14,8 +19,21 @@ async function setSupportedCoinsList(): Promise { return response; } +const matchSearch = + (search: string) => + (currency: MarketCoin): boolean => { + if (!search) return false; + const match = `${currency.symbol}|${currency.name}`; + return match.toLowerCase().includes(search.toLowerCase()); + }; + // fetches currencies data for selected currencies ids -async function listPaginated(): Promise { +async function listPaginated({ + search = "", + starred = [], + order = "desc", + range = "24h", +}: MarketListRequestParams): Promise { const response = await Promise.resolve([ { id: "bitcoin", @@ -233,8 +251,26 @@ async function listPaginated(): Promise { }, ]); + let filteredResponse = response; + + if (order !== "desc") { + filteredResponse = filteredResponse.sort( + (x, y) => y.market_cap_rank - x.market_cap_rank + ); + } + + if (search) { + filteredResponse = filteredResponse.filter(matchSearch(search)); + } + + if (starred.length > 0) { + filteredResponse = filteredResponse.filter((currency) => + starred.includes(currency.id) + ); + } + // @ts-expect-error issue in typing - return response.map((currency: any) => ({ + return filteredResponse.map((currency: any) => ({ id: currency.id, name: currency.name, image: currency.image, @@ -248,7 +284,10 @@ async function listPaginated(): Promise { low24h: currency.low_24h, ticker: currency.symbol, price: currency.current_price, - priceChangePercentage: currency.price_change_percentage_24h_in_currency, + priceChangePercentage: + range !== "24h" + ? currency.price_change_percentage_24h_in_currency * 7 + : currency.price_change_percentage_24h_in_currency, marketCapChangePercentage24h: currency.market_cap_change_percentage_24h, circulatingSupply: currency.circulating_supply, totalSupply: currency.total_supply, diff --git a/tools/yarn.lock b/tools/yarn.lock index 292d1cc8a9..16f865d583 100644 --- a/tools/yarn.lock +++ b/tools/yarn.lock @@ -13926,9 +13926,9 @@ uri-js@^4.2.2: punycode "^2.1.0" urijs@^1.19.1: - version "1.19.7" - resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.7.tgz#4f594e59113928fea63c00ce688fb395b1168ab9" - integrity sha512-Id+IKjdU0Hx+7Zx717jwLPsPeUqz7rAtuVBRLLs+qn+J2nf9NGITWVCxcijgYxBqe83C7sqsQPs6H1pyz3x9gA== + version "1.19.8" + resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.8.tgz#ee0407a18528934d3c383e691912f47043a58feb" + integrity sha512-iIXHrjomQ0ZCuDRy44wRbyTZVnfVNLVo3Ksz1yxNyE5wV1IDZW2S5Jszy45DTlw/UdsnRT7DyDhIz7Gy+vJumw== urix@^0.1.0: version "0.1.0" @@ -13945,9 +13945,9 @@ url-loader@2.3.0: schema-utils "^2.5.0" url-parse@^1.4.3: - version "1.5.7" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.7.tgz#00780f60dbdae90181f51ed85fb24109422c932a" - integrity sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA== + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== dependencies: querystringify "^2.1.1" requires-port "^1.0.0" diff --git a/yarn.lock b/yarn.lock index 5850ca1022..d6e22124f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9215,9 +9215,9 @@ uri-js@^4.2.2: punycode "^2.1.0" urijs@^1.19.1: - version "1.19.7" - resolved "https://registry.npmjs.org/urijs/-/urijs-1.19.7.tgz" - integrity sha512-Id+IKjdU0Hx+7Zx717jwLPsPeUqz7rAtuVBRLLs+qn+J2nf9NGITWVCxcijgYxBqe83C7sqsQPs6H1pyz3x9gA== + version "1.19.8" + resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.8.tgz#ee0407a18528934d3c383e691912f47043a58feb" + integrity sha512-iIXHrjomQ0ZCuDRy44wRbyTZVnfVNLVo3Ksz1yxNyE5wV1IDZW2S5Jszy45DTlw/UdsnRT7DyDhIz7Gy+vJumw== url-parse-lax@^1.0.0: version "1.0.0" @@ -9234,9 +9234,9 @@ url-parse-lax@^3.0.0: prepend-http "^2.0.0" url-parse@^1.4.3, url-parse@^1.5.1: - version "1.5.7" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.7.tgz#00780f60dbdae90181f51ed85fb24109422c932a" - integrity sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA== + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== dependencies: querystringify "^2.1.1" requires-port "^1.0.0"