From 83071abb47fdc74833c2a99002c903ec42421992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Thu, 24 Mar 2022 10:18:23 +0100 Subject: [PATCH] Remove all libcore codebase --- .eslintignore | 2 - .gitignore | 2 - README.md | 4 +- cli/README.md | 5 - cli/package.json | 5 - cli/scripts/createTest.sh | 18 - cli/scripts/test.template/test.sh | 3 - cli/scripts/testOne.sh | 67 -- cli/scripts/tests.sh | 3 - cli/src/commands-index.ts | 4 - cli/src/commands/getAccountNetworkInfo.ts | 40 -- cli/src/commands/libcoreReset.ts | 8 - cli/src/commands/libcoreSetPassword.ts | 21 - cli/src/commands/validRecipient.ts | 46 -- cli/src/commands/version.ts | 11 +- cli/src/live-common-setup-base.ts | 13 +- cli/src/live-common-setup.ts | 7 - cli/src/scan.ts | 3 +- docs/account.md | 2 +- docs/adding-libcore-bindings.md | 144 ---- docs/bot.md | 2 +- docs/ci-intro.md | 25 +- docs/gist-tx.md | 11 +- docs/intro.md | 5 +- docs/push_libcore_bump.md | 52 -- package.json | 2 +- scripts/sync-families-dispatch.sh | 52 -- src/__tests__/all.libcore.ts | 48 +- .../implement-react-native-libcore/index.ts | 6 - .../specific.android.ts | 4 - .../specific.ios.ts | 4 - .../test-helpers/libcore-setup.native.ts | 1 - src/__tests__/test-helpers/libcore-setup.ts | 8 +- src/account/support.ts | 21 +- src/api/Ledger.ts | 13 +- src/bot/index.ts | 5 - src/bridge/impl.ts | 22 +- src/bridge/shared.ts | 38 -- src/cross.ts | 4 +- src/derivation.ts | 86 +-- src/env.ts | 23 +- src/families/algorand/bridge/libcore.ts | 345 ---------- src/families/algorand/buildASAOperation.ts | 66 -- src/families/algorand/libcore-broadcast.ts | 18 - .../algorand/libcore-buildOperation.ts | 69 -- .../algorand/libcore-buildSubAccounts.ts | 132 ---- .../algorand/libcore-buildTransaction.ts | 115 ---- .../algorand/libcore-getFeesForTransaction.ts | 25 - .../algorand/libcore-postBuildAccount.ts | 42 -- .../algorand/libcore-signOperation.ts | 104 --- src/families/algorand/types.ts | 257 +------ src/families/bitcoin/bridge/libcore.ts | 297 -------- .../bitcoin/customAddressValidation.ts | 30 - src/families/bitcoin/js-signOperation.ts | 5 - src/families/bitcoin/libcore-broadcast.ts | 15 - .../bitcoin/libcore-buildOperation.ts | 38 -- .../bitcoin/libcore-buildTransaction.ts | 118 ---- .../libcore-getAccountNetworkInfo.test.ts | 27 - .../bitcoin/libcore-getAccountNetworkInfo.ts | 50 -- .../bitcoin/libcore-getFeesForTransaction.ts | 67 -- .../bitcoin/libcore-postBuildAccount.ts | 70 -- src/families/bitcoin/libcore-signOperation.ts | 227 ------ src/families/bitcoin/logic.ts | 68 -- src/families/bitcoin/networks.ts | 2 +- src/families/bitcoin/transaction.ts | 62 -- src/families/bitcoin/types.ts | 223 ------ src/families/bitcoin/wallet-btc/utils.ts | 2 +- src/families/celo/types.ts | 6 - src/families/cosmos/bridge/libcore.ts | 177 ----- src/families/cosmos/libcore-broadcast.ts | 24 - src/families/cosmos/libcore-buildOperation.ts | 121 ---- .../cosmos/libcore-buildTransaction.ts | 212 ------ .../cosmos/libcore-getFeesForTransaction.ts | 27 - .../cosmos/libcore-getTransactionStatus.ts | 299 -------- .../cosmos/libcore-mergeOperations.ts | 43 -- .../cosmos/libcore-postBuildAccount.ts | 177 ----- src/families/cosmos/libcore-signOperation.ts | 96 --- src/families/cosmos/logic.ts | 41 +- src/families/cosmos/message.ts | 155 ----- src/families/cosmos/test-specifics.ts | 225 ------ src/families/cosmos/types.ts | 620 +---------------- src/families/crypto_org/types.ts | 6 +- src/families/elrond/types.ts | 6 +- src/families/ethereum/types.ts | 7 +- .../filecoin/customAddressValidation.ts | 19 - src/families/filecoin/types.ts | 6 - src/families/neo/types.ts | 6 +- src/families/polkadot/types.ts | 6 +- src/families/ripple/types.ts | 143 ---- src/families/solana/types.ts | 8 - src/families/stellar/api/horizon.ts | 3 +- src/families/stellar/test-dataset.ts | 6 +- src/families/stellar/types.ts | 5 - src/families/tezos/bridge/js.ts | 17 +- src/families/tezos/bridge/libcore.ts | 312 --------- src/families/tezos/libcore-broadcast.ts | 16 - src/families/tezos/libcore-buildOperation.ts | 52 -- .../tezos/libcore-buildSubAccounts.ts | 137 ---- .../tezos/libcore-buildTransaction.ts | 154 ----- .../tezos/libcore-getAccountNetworkInfo.ts | 25 - .../tezos/libcore-getFeesForTransaction.ts | 27 - src/families/tezos/libcore-signOperation.ts | 122 ---- src/families/tezos/types.ts | 224 ------ src/families/tron/types.ts | 7 +- src/generated/bridge/libcore.ts | 15 - src/generated/customAddressValidation.ts | 6 - src/generated/libcore-buildOperation.ts | 15 - src/generated/libcore-buildSubAccounts.ts | 9 - .../libcore-getAccountNetworkInfo.ts | 9 - .../libcore-getFeesForTransaction.ts | 15 - src/generated/libcore-mergeOperations.ts | 6 - src/generated/libcore-postBuildAccount.ts | 12 - src/generated/libcore-postSyncPatch.ts | 3 - src/generated/test-specifics.ts | 3 - src/generated/types.ts | 202 ------ src/libcore/access.ts | 135 ---- src/libcore/broadcast.ts | 34 - src/libcore/buildAccount/buildOperation.ts | 96 --- src/libcore/buildAccount/buildSubAccounts.ts | 24 - src/libcore/buildAccount/index.ts | 259 ------- src/libcore/buildBigNumber.ts | 28 - src/libcore/createAccountFromDevice.ts | 108 --- src/libcore/errors.ts | 20 - src/libcore/getAccountBalanceHistory.ts | 56 -- src/libcore/getAccountNetworkInfo.ts | 46 -- src/libcore/getCoreAccount.ts | 37 - src/libcore/getFeesForTransaction.ts | 46 -- src/libcore/getOrCreateAccount.ts | 93 --- src/libcore/getOrCreateWallet.ts | 91 --- src/libcore/isValidRecipient.ts | 36 - src/libcore/nativeSegwitAppsVersionsMap.ts | 4 - src/libcore/platforms/nodejs.ts | 627 ----------------- src/libcore/platforms/react-native.ts | 242 ------- src/libcore/scanAccounts.ts | 280 -------- src/libcore/signOperation.ts | 141 ---- src/libcore/syncAccount.ts | 152 ----- src/libcore/types/index.ts | 645 ------------------ src/reconciliation.ts | 9 +- 138 files changed, 64 insertions(+), 10291 deletions(-) delete mode 100755 cli/scripts/createTest.sh delete mode 100644 cli/scripts/test.template/test.sh delete mode 100755 cli/scripts/testOne.sh delete mode 100755 cli/scripts/tests.sh delete mode 100644 cli/src/commands/getAccountNetworkInfo.ts delete mode 100644 cli/src/commands/libcoreReset.ts delete mode 100644 cli/src/commands/libcoreSetPassword.ts delete mode 100644 cli/src/commands/validRecipient.ts delete mode 100644 docs/adding-libcore-bindings.md delete mode 100644 docs/push_libcore_bump.md delete mode 100644 src/__tests__/test-helpers/implement-react-native-libcore/index.ts delete mode 100644 src/__tests__/test-helpers/implement-react-native-libcore/specific.android.ts delete mode 100644 src/__tests__/test-helpers/implement-react-native-libcore/specific.ios.ts delete mode 100644 src/bridge/shared.ts delete mode 100644 src/families/algorand/bridge/libcore.ts delete mode 100644 src/families/algorand/buildASAOperation.ts delete mode 100644 src/families/algorand/libcore-broadcast.ts delete mode 100644 src/families/algorand/libcore-buildOperation.ts delete mode 100644 src/families/algorand/libcore-buildSubAccounts.ts delete mode 100644 src/families/algorand/libcore-buildTransaction.ts delete mode 100644 src/families/algorand/libcore-getFeesForTransaction.ts delete mode 100644 src/families/algorand/libcore-postBuildAccount.ts delete mode 100644 src/families/algorand/libcore-signOperation.ts delete mode 100644 src/families/bitcoin/bridge/libcore.ts delete mode 100644 src/families/bitcoin/customAddressValidation.ts delete mode 100644 src/families/bitcoin/libcore-broadcast.ts delete mode 100644 src/families/bitcoin/libcore-buildOperation.ts delete mode 100644 src/families/bitcoin/libcore-buildTransaction.ts delete mode 100644 src/families/bitcoin/libcore-getAccountNetworkInfo.test.ts delete mode 100644 src/families/bitcoin/libcore-getAccountNetworkInfo.ts delete mode 100644 src/families/bitcoin/libcore-getFeesForTransaction.ts delete mode 100644 src/families/bitcoin/libcore-postBuildAccount.ts delete mode 100644 src/families/bitcoin/libcore-signOperation.ts delete mode 100644 src/families/cosmos/bridge/libcore.ts delete mode 100644 src/families/cosmos/libcore-broadcast.ts delete mode 100644 src/families/cosmos/libcore-buildOperation.ts delete mode 100644 src/families/cosmos/libcore-buildTransaction.ts delete mode 100644 src/families/cosmos/libcore-getFeesForTransaction.ts delete mode 100644 src/families/cosmos/libcore-getTransactionStatus.ts delete mode 100644 src/families/cosmos/libcore-mergeOperations.ts delete mode 100644 src/families/cosmos/libcore-postBuildAccount.ts delete mode 100644 src/families/cosmos/libcore-signOperation.ts delete mode 100644 src/families/cosmos/message.ts delete mode 100644 src/families/cosmos/test-specifics.ts delete mode 100644 src/families/filecoin/customAddressValidation.ts delete mode 100644 src/families/tezos/bridge/libcore.ts delete mode 100644 src/families/tezos/libcore-broadcast.ts delete mode 100644 src/families/tezos/libcore-buildOperation.ts delete mode 100644 src/families/tezos/libcore-buildSubAccounts.ts delete mode 100644 src/families/tezos/libcore-buildTransaction.ts delete mode 100644 src/families/tezos/libcore-getAccountNetworkInfo.ts delete mode 100644 src/families/tezos/libcore-getFeesForTransaction.ts delete mode 100644 src/families/tezos/libcore-signOperation.ts delete mode 100644 src/generated/bridge/libcore.ts delete mode 100644 src/generated/libcore-buildOperation.ts delete mode 100644 src/generated/libcore-buildSubAccounts.ts delete mode 100644 src/generated/libcore-getAccountNetworkInfo.ts delete mode 100644 src/generated/libcore-getFeesForTransaction.ts delete mode 100644 src/generated/libcore-mergeOperations.ts delete mode 100644 src/generated/libcore-postBuildAccount.ts delete mode 100644 src/generated/libcore-postSyncPatch.ts delete mode 100644 src/libcore/access.ts delete mode 100644 src/libcore/broadcast.ts delete mode 100644 src/libcore/buildAccount/buildOperation.ts delete mode 100644 src/libcore/buildAccount/buildSubAccounts.ts delete mode 100644 src/libcore/buildAccount/index.ts delete mode 100644 src/libcore/buildBigNumber.ts delete mode 100644 src/libcore/createAccountFromDevice.ts delete mode 100644 src/libcore/errors.ts delete mode 100644 src/libcore/getAccountBalanceHistory.ts delete mode 100644 src/libcore/getAccountNetworkInfo.ts delete mode 100644 src/libcore/getCoreAccount.ts delete mode 100644 src/libcore/getFeesForTransaction.ts delete mode 100644 src/libcore/getOrCreateAccount.ts delete mode 100644 src/libcore/getOrCreateWallet.ts delete mode 100644 src/libcore/isValidRecipient.ts delete mode 100644 src/libcore/nativeSegwitAppsVersionsMap.ts delete mode 100644 src/libcore/platforms/nodejs.ts delete mode 100644 src/libcore/platforms/react-native.ts delete mode 100644 src/libcore/scanAccounts.ts delete mode 100644 src/libcore/signOperation.ts delete mode 100644 src/libcore/syncAccount.ts delete mode 100644 src/libcore/types/index.ts diff --git a/.eslintignore b/.eslintignore index 03e188de05..4ab6ee4701 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,9 +1,7 @@ src/data/icons src/data/flags -src/libcore/types/*.js src/families/*/types.js -src/libcore/platforms/*.js src/generated src/load/tokens/ mobile-test-app/ diff --git a/.gitignore b/.gitignore index 6cc32432bd..6144515d5d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ # See https://help.github.com/ignore-files/ for more about ignoring files. -libcoredb/ - dbdata tmp diff --git a/README.md b/README.md index 09db4a8570..13367ef0be 100644 --- a/README.md +++ b/README.md @@ -51,18 +51,16 @@ The stack is pretty standard for a ES6 and FlowType library. The notable depende - [Countervalues logic](./docs/countervalues.md) - Coin integration specifics - [Introduction](./docs/ci-intro.md) - - Bridge implementations, where to start? (JS, Libcore, Mock) + - Bridge implementations, where to start? (JS, Mock) - Implementing the hardware wallet logic of a new coin - [The account derivation (BIP44 and exceptions)](./docs/derivation.md) - Advanced - [api/socket `createDeviceSocket` and script runner](./docs/socket.md) - env.js: live-common configuration system - Serialization and reconciliation - - libcore typing and wrapping - cross.js and "LiveQR" protocol - cache.js helpers - Tokens management and ERC20 - - [Developing with lib-ledger-core bindings](./docs/adding-libcore-bindings.md) ### Developing with Ledger Live Common diff --git a/cli/README.md b/cli/README.md index 97e5a6a460..7337871016 100644 --- a/cli/README.md +++ b/cli/README.md @@ -167,11 +167,6 @@ Usage: ledger-live getTransactionStatus # Prepare a transaction and returns 'Tra --fees : how much fees -f, --format : how to display the data -Usage: ledger-live libcoreReset - -Usage: ledger-live libcoreSetPassword - --password : the new password - Usage: ledger-live liveData # utility for Ledger Live app.json file --device : provide a specific HID path of a device --xpub : use an xpub (alternatively to --device) diff --git a/cli/package.json b/cli/package.json index 8c575d6b6d..d29806b912 100644 --- a/cli/package.json +++ b/cli/package.json @@ -18,10 +18,6 @@ "build": "tsc --project src/tsconfig.json", "prepublishOnly": "rm -rf lib && tsc --project src/tsconfig.json", "watch": "tsc --watch --project src/tsconfig.json", - "test-e2e": "./scripts/tests.sh", - "test": "yarn run test-e2e", - "testOne": "./scripts/testOne.sh", - "createTest": "./scripts/createTest.sh", "type-check": "tsc --noEmit" }, "peerDependencies": { @@ -38,7 +34,6 @@ "@ledgerhq/hw-transport-mocker": "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", "@ledgerhq/live-common": "^21.34.0", "@ledgerhq/logs": "6.10.0", "@walletconnect/client": "^1.7.1", diff --git a/cli/scripts/createTest.sh b/cli/scripts/createTest.sh deleted file mode 100755 index 4543aa7030..0000000000 --- a/cli/scripts/createTest.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -set -e - -name=$1 -if [ "$name" == "" ]; then - echo "Usage: yarn createTest " - exit 1 -fi - -cd $(dirname $0)/../tests - -if [ -x "$name" ]; then - echo "test $name already exists" - exit 1 -fi - -cp -R ../scripts/test.template $name \ No newline at end of file diff --git a/cli/scripts/test.template/test.sh b/cli/scripts/test.template/test.sh deleted file mode 100644 index ac6b7bb5e6..0000000000 --- a/cli/scripts/test.template/test.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -ledger-live libcoreVersion > output/version diff --git a/cli/scripts/testOne.sh b/cli/scripts/testOne.sh deleted file mode 100755 index 5a8c276eb1..0000000000 --- a/cli/scripts/testOne.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/bash - -START_TIME=$SECONDS - -# all verbose logs are logs to stdout but not recorded by testOne. -export VERBOSE_FILE=`mktemp` - -name=$1 -shift -opt=$@ -cwd=`dirname $0` - -# ledger-live () { -# $cwd/../cli.js "$@" -# } - -set -e -cd $(dirname $0)/../tests/$name - -touch apdu.snapshot.log -curl -XPOST http://localhost:8435/end 2> /dev/null || true # make sure all is killed -ledger-live proxy -f apdu.snapshot.log $opt >> $VERBOSE_FILE & -PID=$! -rm -rf output/ dbdata/ -if [ "$opt" == "--record" ]; then - rm -rf expected/ -fi -mkdir output - -echo "Running test $name..." -export DISABLE_TRANSACTION_BROADCAST=1 -export DEVICE_PROXY_URL=ws://localhost:8435 - -set +e -source ./test.sh -time=$(($SECONDS - $START_TIME)) -if [ $? -eq 0 ]; then - echo "$name: done in ${time}s." -else - echo "--- recent logs ---" - tail $VERBOSE_FILE - echo "$name: FAILED." - exit 1 -fi -set -e - -sleep 2 - -if kill -0 $PID 2> /dev/null; then - curl -XPOST http://localhost:8435/end -fi -wait -if [ "$opt" == "--record" ]; then - mkdir -p expected - cp -r ./output/* ./expected/ -fi -if [ -d "./expected" ]; then - diff ./output ./expected - if [ $? -ne 0 ]; then - echo "$name: Unexpected result." - echo "--- recent logs ---" - tail $VERBOSE_FILE - echo "$name: FAILED." - exit 1 - fi -fi -echo diff --git a/cli/scripts/tests.sh b/cli/scripts/tests.sh deleted file mode 100755 index d9628ae71d..0000000000 --- a/cli/scripts/tests.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -set -e diff --git a/cli/src/commands-index.ts b/cli/src/commands-index.ts index 3bd068da70..a3d4089d08 100644 --- a/cli/src/commands-index.ts +++ b/cli/src/commands-index.ts @@ -28,8 +28,6 @@ import genuineCheck from "./commands/genuineCheck"; import getAccountNetworkInfo from "./commands/getAccountNetworkInfo"; import getAddress from "./commands/getAddress"; import getTransactionStatus from "./commands/getTransactionStatus"; -import libcoreReset from "./commands/libcoreReset"; -import libcoreSetPassword from "./commands/libcoreSetPassword"; import liveData from "./commands/liveData"; import makeCompoundSummary from "./commands/makeCompoundSummary"; import managerListApps from "./commands/managerListApps"; @@ -83,8 +81,6 @@ export default { getAccountNetworkInfo, getAddress, getTransactionStatus, - libcoreReset, - libcoreSetPassword, liveData, makeCompoundSummary, managerListApps, diff --git a/cli/src/commands/getAccountNetworkInfo.ts b/cli/src/commands/getAccountNetworkInfo.ts deleted file mode 100644 index a28da46f52..0000000000 --- a/cli/src/commands/getAccountNetworkInfo.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { from } from "rxjs"; -import { map, mergeMap } from "rxjs/operators"; -import { getAccountNetworkInfo } from "@ledgerhq/live-common/lib/libcore/getAccountNetworkInfo"; -import { scan, scanCommonOpts } from "../scan"; -import type { ScanCommonOpts } from "../scan"; -const getAccountNetworkInfoFormatters = { - json: (e) => JSON.stringify(e), -}; -export default { - description: "Get the currency network info for accounts", - args: [ - ...scanCommonOpts, - { - name: "format", - alias: "f", - type: String, - typeDesc: Object.keys(getAccountNetworkInfoFormatters).join(" | "), - desc: "how to display the data", - }, - ], - job: ( - opts: ScanCommonOpts & { - format: string; - } - ) => - scan(opts).pipe( - mergeMap((account) => from(getAccountNetworkInfo(account))), - map((e) => { - const f = getAccountNetworkInfoFormatters[opts.format || "json"]; - - if (!f) { - throw new Error( - "getAccountNetworkInfo: no such formatter '" + opts.format + "'" - ); - } - - return f(e); - }) - ), -}; diff --git a/cli/src/commands/libcoreReset.ts b/cli/src/commands/libcoreReset.ts deleted file mode 100644 index 5a0c2ca5a8..0000000000 --- a/cli/src/commands/libcoreReset.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { withLibcore } from "@ledgerhq/live-common/lib/libcore/access"; -export default { - args: [], - job: () => - withLibcore(async (core) => { - await core.getPoolInstance().freshResetAll(); - }), -}; diff --git a/cli/src/commands/libcoreSetPassword.ts b/cli/src/commands/libcoreSetPassword.ts deleted file mode 100644 index 183271e3b2..0000000000 --- a/cli/src/commands/libcoreSetPassword.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { getEnv } from "@ledgerhq/live-common/lib/env"; -import { withLibcore } from "@ledgerhq/live-common/lib/libcore/access"; -export default { - args: [ - { - name: "password", - type: String, - desc: "the new password", - }, - ], - job: ({ - password, - }: Partial<{ - password: string; - }>) => - withLibcore((core) => - core - .getPoolInstance() - .changePassword(getEnv("LIBCORE_PASSWORD"), password || "") - ), -}; diff --git a/cli/src/commands/validRecipient.ts b/cli/src/commands/validRecipient.ts deleted file mode 100644 index 6ed8509343..0000000000 --- a/cli/src/commands/validRecipient.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { CryptoCurrency } from "@ledgerhq/cryptoassets"; -import { isValidRecipient } from "@ledgerhq/live-common/lib/libcore/isValidRecipient"; -import { currencyOpt, deviceOpt, inferCurrency } from "../scan"; -export default { - description: "Validate a recipient address", - args: [ - { - name: "recipient", - alias: "r", - type: String, - desc: "the address to validate", - }, - currencyOpt, - deviceOpt, - ], - job: ( - arg: Partial<{ - recipient: string; - currency: string; - device: string; - }> - ) => - inferCurrency(arg) - .toPromise() - .then((currency) => - isValidRecipient({ - currency: currency as CryptoCurrency, - recipient: arg.recipient as string, - }) - ) - .then( - (warning) => - warning - ? { - type: "warning", - warning, - } - : { - type: "success", - }, - (error) => ({ - type: "error", - error: error.message, - }) - ), -}; diff --git a/cli/src/commands/version.ts b/cli/src/commands/version.ts index 6d04c1d68f..7f9b7d9a75 100644 --- a/cli/src/commands/version.ts +++ b/cli/src/commands/version.ts @@ -1,7 +1,5 @@ /* eslint-disable global-require, @typescript-eslint/no-var-requires */ -import { from, of, concat, Observable } from "rxjs"; -import { map } from "rxjs/operators"; -import { withLibcore } from "@ledgerhq/live-common/lib/libcore/access"; +import { of, concat, Observable } from "rxjs"; export default { args: [], @@ -11,13 +9,6 @@ export default { of( "@ledgerhq/live-common: " + require("@ledgerhq/live-common/package.json").version - ), - of( - "@ledgerhq/ledger-core: " + - require("@ledgerhq/ledger-core/package.json").version - ), - from(withLibcore((core) => core.LedgerCore.getStringVersion())).pipe( - map((v) => "libcore: " + v) ) ), }; diff --git a/cli/src/live-common-setup-base.ts b/cli/src/live-common-setup-base.ts index 9e005bf009..4d77fc3133 100644 --- a/cli/src/live-common-setup-base.ts +++ b/cli/src/live-common-setup-base.ts @@ -3,7 +3,6 @@ import winston from "winston"; import { EnvName, setEnvUnsafe } from "@ledgerhq/live-common/lib/env"; import simple from "@ledgerhq/live-common/lib/logs/simple"; import { listen } from "@ledgerhq/logs"; -import implementLibcore from "@ledgerhq/live-common/lib/libcore/platforms/nodejs"; import { setSupportedCurrencies } from "@ledgerhq/live-common/lib/currencies"; import { setPlatformVersion } from "@ledgerhq/live-common/lib/platform/version"; @@ -106,14 +105,11 @@ listen((log) => { const { type } = log; let level = "info"; - if (type === "libcore-call" || type === "libcore-result") { - level = "silly"; - } else if ( + if ( type === "apdu" || type === "hw" || type === "speculos" || - type.includes("debug") || - type.startsWith("libcore") + type.includes("debug") ) { level = "debug"; } else if (type.includes("warn")) { @@ -128,8 +124,3 @@ listen((log) => { // @ts-ignore logger.log(level, log); }); -implementLibcore({ - lib: () => require("@ledgerhq/ledger-core"), - // eslint-disable-line global-require - dbPath: process.env.LIBCORE_DB_PATH || "./dbdata", -}); diff --git a/cli/src/live-common-setup.ts b/cli/src/live-common-setup.ts index 26f8b31f05..45e84eb9b6 100644 --- a/cli/src/live-common-setup.ts +++ b/cli/src/live-common-setup.ts @@ -31,16 +31,9 @@ checkLibs({ Transport, connect, }); -import implementLibcore from "@ledgerhq/live-common/lib/libcore/platforms/nodejs"; type BluetoothTransport = any; -implementLibcore({ - lib: () => require("@ledgerhq/ledger-core"), - // eslint-disable-line global-require - dbPath: process.env.LIBCORE_DB_PATH || "./dbdata", -}); - let idCounter = 0; const mockTransports = {}; const recordStores = {}; diff --git a/cli/src/scan.ts b/cli/src/scan.ts index 1e806c0c81..a207f216e0 100644 --- a/cli/src/scan.ts +++ b/cli/src/scan.ts @@ -146,7 +146,6 @@ const implTypePerFamily = { }; const possibleImpls = { js: 1, - libcore: 1, mock: 1, }; export const inferCurrency = < @@ -294,7 +293,7 @@ export function scan(arg: ScanCommonOpts): Observable { const type = findAndEat((s) => possibleImpls[s]) || implTypePerFamily[currency.family] || - "libcore"; + "js"; const version = findAndEat((s) => s.match(/^\d+$/)) || "1"; const derivationMode = asDerivationMode( findAndEat((s) => { diff --git a/docs/account.md b/docs/account.md index c78764d873..0d7f66979a 100644 --- a/docs/account.md +++ b/docs/account.md @@ -61,7 +61,7 @@ type Account = { ``` - `id` is a unique account identifier that we build up with many pieces of information. It's generally composed of 5 parts split by a `:` with: - - `implementation` the implementation that the account is using (typically `js` or `libcore`). It directly maps the account to use the corresponding [AccountBridge](./AccountBridge.md). + - `implementation` the implementation that the account is using (typically `js`). It directly maps the account to use the corresponding [AccountBridge](./AccountBridge.md). - `version` a version number required to allow a migration system (changing it usually will force a recalculation of operations). - `currencyId` tracks the unique id of currency. - `xpubOrAddress` is the "restore key" which is either an xpub or an address. Basically a way (combined with some other fields) to restore the account information. diff --git a/docs/adding-libcore-bindings.md b/docs/adding-libcore-bindings.md deleted file mode 100644 index 0b60a3cd65..0000000000 --- a/docs/adding-libcore-bindings.md +++ /dev/null @@ -1,144 +0,0 @@ -## Developing with lib-ledger-core bindings - -The main C++ libcore library source code is on [LedgerHQ/lib-ledger-core](https://github.com/LedgerHQ/lib-ledger-core). - -There are two existing bindings at the moment: [Node.js bindings](https://github.com/LedgerHQ/lib-ledger-core-node-bindings) (NPM: `@ledgerhq/ledger-core`) and [React Native bindings](https://github.com/LedgerHQ/lib-ledger-core-react-native-bindings) (NPM: `@ledgerhq/react-native-ledger-core`). - -### 2 bindings, 1 abstraction - -The particularity is each bindings expose the libcore through a different API. Node.js bindings have a OO style API and is synchronous where the React Native is asynchronous (defacto because React Native modules architecture) and functional style (where you pass some sort of pointer around to functions instead of having an object with methods). - -```js -// Node.js bindings style: -const coreAcc = coreWallet.getAccount(index); -const balance = coreAcc.getBalance(); -// React Native bindings style: -const coreAcc = await RNCore.CoreWallet.getAccount(coreWallet, index); -const balance = await RNCore.CoreAccount.getBalance(coreAcc); -``` - -**To reconciliate the bindings to a same API, live-common implements an abstraction that wrap them into a higher level OO-style asynchronous API:** - -```js -// live-common wrapping: -const coreAcc = await coreWallet.getAccount(index); -const balance = await coreAcc.getBalance(); -``` - -Interestingly, it's almost like the Node.js bindings but with awaits. - -#### How the wrapping works - -The tradeoff to this idea to abstract lib-ledger-core is we need to maintain ourself the typing of that wrapping. - -The wrapping are done for each platform (nodejs and react-native) and are implemented in `src/libcore/platforms/*`. - -It works by reading some static declaration done in `src/libcore/types.ts` as well as each family specific types in `src/families/*/types.ts` and rebuilding an API with it (proxying each methods into the actual bindings). - -### What you need to do to add a new libcore methods or classes? - -First of all, the way we can see the libcore API is by looking into this generated pseudo-types: [ledgercore_doc.js](https://github.com/LedgerHQ/lib-ledger-core-node-bindings/blob/master/src/ledgercore_doc.js). - -Now, there are two categories of API to add: it's a coin specifics or it's not. If it's a coin specific you will have to modify the bindings of `src/families//types.ts` (see Bitcoin family for example). If it's not, it's done in `src/libcore/types/index.ts`. - -Now, for each of them there are two things you need to essentially do to make the methods/classes available to live-common: - -- You need to add it in the FlowType. -- You need to add it in the _reflect_ / `declare()` wrapping logic. - -#### Example - -Let's look at a generic example, let's say we want to add the method `getAccounts` on the `CoreWallet`. - -When looking on [ledgercore_doc.js](https://github.com/LedgerHQ/lib-ledger-core-node-bindings/blob/master/src/ledgercore_doc.js), it looks like this: - -```js -declare class NJSWallet { - declare function getAccounts(offset: number, count: number, callback: NJSAccountListCallback); -} -``` - -where `NJSAccountListCallback` is actually - -```js -declare class NJSAccountListCallback { - declare function onCallback(result: ?Array, error: ?Error); -} -``` - -essentially you need to rethink this as being: - -```js -function getAccounts(offset: number, count: number): NJSAccount[] -``` - -a function that returns an array of accounts. - -Now, to target what our abstraction is, we need to make this API asynchronous as well as rewording things by replacing "NJS" by "Core": - -```js -declare class CoreWallet { - getAccounts(offset: number, count: number): Promise; -} -``` - -Well, that's exactly the FlowType what we have defined in `libcore/types/index.ts`! - -Now, that we did the FlowType part, we also need to define the methods in the CoreWallet class: - -```js -// NB we don't have the "Core" part of the name. -declare("Wallet", { - methods: { - getAccounts: { - returns: ["Account"] // DSL that means "array of accounts" - }, - - ...other methods... - } -}); -``` - -If the methods accept any parameter that are NOT primitive types (like string or arrays), we also need to describe them. **You can look at other example in the codebase for this**. - -Advanced Example: - -```js -declare("WalletPool", { - statics: { - newInstance: { - params: [ - null, // it's a primitive type so we don't need to "unwrap" the param - null, - "HttpClient", - "WebSocketClient", - "PathResolver", - "LogPrinter", - "ThreadDispatcher", - "RandomNumberGenerator", - "DatabaseBackend", - "DynamicObject" - ], - returns: "WalletPool" - } - }, - methods: { - freshResetAll: {}, - changePassword: {}, - getName: {}, - updateWalletConfig: { - params: [null, "DynamicObject"] - }, - getWallet: { - returns: "Wallet" - }, - getCurrency: { - returns: "Currency" - }, - createWallet: { - params: [null, "Currency", "DynamicObject"], - returns: "Wallet" - } - } -}); -``` diff --git a/docs/bot.md b/docs/bot.md index af3920535b..2e3ceb04ea 100644 --- a/docs/bot.md +++ b/docs/bot.md @@ -164,7 +164,7 @@ What the test engine is simply doing is scanning accounts with the seed and for We can see here that for EACH account of my seed, it tries to do a transaction. At this current stage, we only have defined 2 possible transactions. This is highly driven by defining these "mutations" for all coins (what are all the possible actions that can be performed on these actions). Once they are all defined, we can assume the accounts will transition over and "see" all the possible states we support. -Essentially, 95% of the focus of Ledger-Live Bot is about maintaining and defining all possible scenarios one can do with Ledger Live and the bot autonomously do them with the whole Ledger stack (libcore, live-common, nano app running in speculos). +Essentially, 95% of the focus of Ledger-Live Bot is about maintaining and defining all possible scenarios one can do with Ledger Live and the bot autonomously do them with the whole Ledger stack (live-common, nano app running in speculos). > Note: in the example above, we can see some errors are happening, which actually made the test fails. This is because we are too optimistic on the minimal account required for the fees. In the future, when we fix this, everything should fall into the case `🤷‍♂️ couldn't find a mutation to do!` which is not an error. diff --git a/docs/ci-intro.md b/docs/ci-intro.md index ad34266cd7..b3c5bac3a4 100644 --- a/docs/ci-intro.md +++ b/docs/ci-intro.md @@ -1,23 +1,11 @@ # Coin Integration Introduction -Ledger Live Common is designed to have very generic models (for currencies, accounts) but to also facilitate new coin integrations and via different ways (pure JS implementation, lib-ledger-core integrations,...). +Ledger Live Common is designed to have very generic models (for currencies, accounts) but to also facilitate new coin integrations and via different ways (pure JS implementation,...). The way this is structured is the folder `src/families` would contain the only specific bits a coin family will have and the rest is factorized and generic. The goal is to minimize the amount of effort to implement while guaranteeing the general library integrity (we want to avoid if logics in the generic parts). -## JS or Libcore bridge? - -There are typically two possibilities in how a coin gets integrated: either we develop the integration in [lib-ledger-core](https://github.com/LedgerHQ/lib-ledger-core) (our in-house C++ core library – it's the case today for Bitcoin and Ethereum) or we integrate it in JavaScript to leverage on JavaScript ecosystem. - -There are pros and cons in the different approach. - -Typically we saw the more complex a coin is, the more likely we might have it solved by lib-ledger-core and avoid JS maintenance of the logic and crypto libraries to distribute on all targets (Node, React Native,..). Typically, ripple-lib has been challenging to get it work on React Native. - -On the other hand, having it solved in JS is generally easier and the "developer experience" iteration is faster. - -Interestingly, both implementation can exist at the same time, this is case for Ethereum and Ripple today and usually happens for legacy/evolution reason. - ## A typical families folder For each new coin integration, define a family for it (potentially one family of coin can contain many coins, for instance _Bitcoin Cash_ is in `bitcoin` family, _Ethereum Classic_ is in `ethereum` family). A family is a ledger specific technical grouping that put together the similar coins, typically those issued by a fork. @@ -31,22 +19,13 @@ The folder will have this kind of structure: ``` . ├── bridge -│   ├── js.ts # if relevant -│   ├── libcore.ts # if relevant +│   ├── js.ts │   └── mock.ts ├── cli-transaction.ts # for the CLI ├── hw-getAddress.ts ├── hw-signMessage.ts # if possible ├── transaction.ts # transaction specific fields ├── types.ts # family specific types -# if libcore is used -├── libcore-buildOperation.ts -├── libcore-buildSubAccounts.ts # if this concept even exists -├── libcore-buildTransaction.ts -├── libcore-getAccountNetworkInfo.ts -├── libcore-getFeesForTransaction.ts -├── libcore-hw-signTransaction.ts -├── libcore-signAndBroadcast.ts # for tests ├── test-dataset.ts └── test-specifics.ts diff --git a/docs/gist-tx.md b/docs/gist-tx.md index a9062b2684..3d5d85436b 100644 --- a/docs/gist-tx.md +++ b/docs/gist-tx.md @@ -7,10 +7,9 @@ yarn add @ledgerhq/live-common yarn add rxjs # for Observable ``` -Now we need a concrete implementation of libcore and a Transport to use the ledger device with. _In our example we're going to do a Node.js script that works with USB_, so we're just going to install these: +Now we need an actual implementation of a Transport to use the ledger device with. _In our example we're going to do a Node.js script that works with USB_, so we're just going to install these: ```bash -yarn add @ledgerhq/ledger-core yarn add @ledgerhq/hw-transport-node-hid-noevents ``` @@ -46,8 +45,6 @@ const deviceId = ""; // in HID case const { registerTransportModule } = require("@ledgerhq/live-common/lib/hw"); const TransportNodeHid = require("@ledgerhq/hw-transport-node-hid-noevents") .default; -const implementLibcore = require("@ledgerhq/live-common/lib/libcore/platforms/nodejs") - .default; const { setSupportedCurrencies, } = require("@ledgerhq/live-common/lib/currencies"); @@ -55,12 +52,6 @@ const { // configure which coins to enable setSupportedCurrencies([currencyId]); -// provide a libcore implementation -implementLibcore({ - lib: () => require("@ledgerhq/ledger-core"), - dbPath: "./dbdata", -}); - // configure which transport are available registerTransportModule({ id: "hid", diff --git a/docs/intro.md b/docs/intro.md index d28a1f8638..f779e83669 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -22,7 +22,6 @@ We can highlight this library have: This library does not have/is agnostic of: - The actual HW transport you use. But you can easily find one on [ledgerjs libraries](https://github.com/LedgerHQ/ledgerjs) (or implement your own). _There are many communication channel possible: webusb, u2f, webhid, webble, node-hid, react-native-hid, react-native bluetooth,...)_ -- A [lib-ledger-core](https://github.com/LedgerHQ/lib-ledger-core) implementation (but you can attach a binding easily) ### One library, multiple usecases @@ -32,11 +31,9 @@ This works by having multiple entry points. For instance, the Manager apps logic is imported from `@ledgerhq/live-common/lib/apps` and the currencies logic is in `@ledgerhq/live-common/lib/currencies`. -> Some of the bricks have different requirements. For instance `@ledgerhq/live-common/lib/bridge` will require you to provide a [lib-ledger-core](https://github.com/LedgerHQ/lib-ledger-core) implementation (Node bindings or React Native bindings) while other utility like `@ledgerhq/live-common/lib/countervalues` just requires networking and can run on the web. - ### Modular coins support architecture -Ledger Live Common is designed to have very generic models (for currencies, accounts) but to also facilitate new coin integrations and via different ways (pure JS implementation, lib-ledger-core integrations,...). +Ledger Live Common is designed to have very generic models (for currencies, accounts) but to also facilitate new coin integrations and via different ways (pure JS implementation,...). [More information in Coin Integration Introduction...](./ci-intro.md) diff --git a/docs/push_libcore_bump.md b/docs/push_libcore_bump.md deleted file mode 100644 index ccbaef5ed5..0000000000 --- a/docs/push_libcore_bump.md +++ /dev/null @@ -1,52 +0,0 @@ -## A new lib-ledger-core version is out, what now? - -### Prerequisite - -- Make sure that the version landed on develop. https://github.com/LedgerHQ/lib-ledger-core/commits/develop -- Make sure that on that commit, the CI is passing for at least all the `build_*_release` task as well as the `Release Visual Studio 15 2017` build inside `appveyor` -- figure out the libcore version. For instance `3.5.0-rc-8796ba` - -### bump on lib-ledger-core-node-bindings - -**You must have access to NPM and Github right to push to lib-ledger-core-node-bindings** - -- clone https://github.com/LedgerHQ/lib-ledger-core-node-bindings and go to master, make sure to be in sync with upstream -- Confirm with libcore team that there is no diff of libcore interface since the last changes, otherwise they must provide a bindings PR and update the libcoreVersion in package.json. -- Otherwise, edit package.json to change the libcoreVersion to the correct one that you figured out in Prerequisite. -- commit that change and push it to upstream -- now, run this to publish it: - -``` -yarn publish -# replace upstream by the remote name of upstream repo -git push upstream master -git push upstream master --tags -``` - -- Now, wait that our CI is building the prebuild, you can see the status on the Github commit -- When the prebuild are pushed. Go to https://github.com/LedgerHQ/lib-ledger-core-node-bindings/releases and edit the tag to RELEASE it - -(It is very important for the prebuild to be accessible) - -### bump on lib-ledger-core-react-native-bindings - -- clone https://github.com/LedgerHQ/lib-ledger-core-react-native-bindings and go to master, make sure to be in sync with upstream -- Confirm with libcore team that there is no diff of libcore interface since the last changes, otherwise they must provide a bindings PR and update the libcoreVersion in preinstall.js. -- Otherwise, edit preinstall.js to change the LIB_CORE_VERSION to the correct one that you figured out in Prerequisite. -- commit that change and push it to upstream -- now, run this to publish it: - -``` -yarn publish -# replace upstream by the remote name of upstream repo -git push upstream master -git push upstream master --tags -``` - -That's all. - -### Bump on LLD and LLM - -Once the work above is solved, we can independently update on both ledger-live-desktop and ledger-live-mobile project that new lib core bindings version. This is in scope of Ledger Live team. -A good practice is also to update it in the ledger-live-common CLI project so we can have latest libcore tested against the CI and the bot. - diff --git a/package.json b/package.json index 539c5908dc..81531f341e 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "updateAppSupportsQuitApp": "node scripts/updateAppSupportsQuitApp.js", "prettier": "prettier --write 'src/**/*.?s' 'cli/src/**/*.?s'", "lint": "eslint src", - "jest": "rimraf libcoredb && mkdir libcoredb && cross-env TZ=America/New_York jest", + "jest": "cross-env TZ=America/New_York jest", "test": "yarn jest", "ci-lint": "yarn lint", "ci-test-common": "env-cmd -f ./.ci.env yarn test --ci --updateSnapshot && git diff --exit-code src", diff --git a/scripts/sync-families-dispatch.sh b/scripts/sync-families-dispatch.sh index 7cf6a40916..3ffa0632bb 100755 --- a/scripts/sync-families-dispatch.sh +++ b/scripts/sync-families-dispatch.sh @@ -7,16 +7,8 @@ targets="\ customAddressValidation.ts \ hw-getAddress.ts \ hw-signMessage.ts \ -libcore-buildOperation.ts \ -libcore-buildSubAccounts.ts \ -libcore-getFeesForTransaction.ts \ -libcore-postSyncPatch.ts \ -libcore-postBuildAccount.ts \ -libcore-getAccountNetworkInfo.ts \ -libcore-mergeOperations.ts \ transaction.ts \ bridge/js.ts \ -bridge/libcore.ts \ bridge/mock.ts \ cli-transaction.ts \ specs.ts \ @@ -31,8 +23,6 @@ presync.ts \ platformAdapter.ts \ " -withoutNetworkInfo=("algorand polkadot solana celo") - cd ../src rm -rf generated @@ -106,35 +96,10 @@ genDeviceTransactionConfig () { genTypesFile () { for family in $families; do - echo 'import { reflect as '$family'Reflect } from "../families/'$family'/types";' - echo 'import { CoreStatics as CoreStatics_'$family' } from "../families/'$family'/types";' - echo 'import { CoreAccountSpecifics as CoreAccountSpecifics_'$family' } from "../families/'$family'/types";' - echo 'import { CoreOperationSpecifics as CoreOperationSpecifics_'$family' } from "../families/'$family'/types";' - echo 'import { CoreCurrencySpecifics as CoreCurrencySpecifics_'$family' } from "../families/'$family'/types";' echo 'import { Transaction as '$family'Transaction } from "../families/'$family'/types";' echo 'import { TransactionRaw as '$family'TransactionRaw } from "../families/'$family'/types";' - if [[ ! " ${withoutNetworkInfo[@]} " =~ " ${family} " ]]; then - echo 'import { NetworkInfo as '$family'NetworkInfo } from "../families/'$family'/types";' - echo 'import { NetworkInfoRaw as '$family'NetworkInfoRaw } from "../families/'$family'/types";' - fi done echo - echo 'export type SpecificStatics = {}' - for family in $families; do - echo '& CoreStatics_'$family - done - echo 'export type CoreAccountSpecifics = {}' - for family in $families; do - echo '& CoreAccountSpecifics_'$family - done - echo 'export type CoreOperationSpecifics = {}' - for family in $families; do - echo '& CoreOperationSpecifics_'$family - done - echo 'export type CoreCurrencySpecifics = {}' - for family in $families; do - echo '& CoreCurrencySpecifics_'$family - done echo 'export type Transaction =' for family in $families; do echo ' | '$family'Transaction' @@ -143,23 +108,6 @@ genTypesFile () { for family in $families; do echo ' | '$family'TransactionRaw' done - echo 'export type NetworkInfo =' - for family in $families; do - if [[ ! " ${withoutNetworkInfo[@]} " =~ " ${family} " ]]; then - echo ' | '$family'NetworkInfo' - fi - done - echo 'export type NetworkInfoRaw =' - for family in $families; do - if [[ ! " ${withoutNetworkInfo[@]} " =~ " ${family} " ]]; then - echo ' | '$family'NetworkInfoRaw' - fi - done - echo 'export const reflectSpecifics = (declare: any): Array<{ OperationMethods: Record, AccountMethods: Record }> => [' - for family in $families; do - echo ' '$family'Reflect(declare),' - done - echo '] as Array<{ OperationMethods: Record, AccountMethods: Record }>;' } genTypesFile > ../generated/types.ts diff --git a/src/__tests__/all.libcore.ts b/src/__tests__/all.libcore.ts index ca65a129d9..e9c8008671 100644 --- a/src/__tests__/all.libcore.ts +++ b/src/__tests__/all.libcore.ts @@ -3,10 +3,7 @@ import { from } from "rxjs"; import { mergeAll } from "rxjs/operators"; import { flatMap } from "lodash"; */ -import { log } from "@ledgerhq/logs"; import { setup } from "./test-helpers/libcore-setup"; -import { withLibcore, afterLibcoreGC } from "../libcore/access"; -import { delay } from "../promise"; import { testBridge } from "./test-helpers/bridge"; import dataset from "../generated/test-dataset"; import specifics from "../generated/test-specifics"; @@ -17,11 +14,7 @@ afterAll(async () => { await disconnectAll(); }); setup("libcore"); -test("libcore version", async () => { - const v = await withLibcore((core) => core.LedgerCore.getStringVersion()); - expect(typeof v).toBe("string"); - log("libcoreVersion", v as string); -}); + const families = Object.keys(dataset); const maybeFamilyToOnlyRun = process.env.BRANCH && process.env.BRANCH.split("/")[0]; @@ -49,42 +42,3 @@ from(flatMap(all, r => r.preloadObservables)) Object.values(specifics).forEach((specific: (...args: Array) => any) => { specific(); }); -describe("libcore access", () => { - test("withLibcore", async () => { - const res = await withLibcore(async (core) => { - expect(core).toBeDefined(); - await delay(100); - return 42; - }); - expect(res).toBe(42); - }); - test("afterLibcoreGC", async () => { - let count = 0; - let gcjob = 0; - withLibcore(async () => { - await delay(100); - ++count; - }); - withLibcore(async () => { - await delay(100); - ++count; - }); - let p3; - await delay(20); - await afterLibcoreGC(async () => { - expect(count).toBe(2); - await delay(100); - p3 = withLibcore(async () => { - await delay(400); - ++count; - }); - expect(count).toBe(2); - await delay(100); - expect(count).toBe(2); - gcjob++; - }); - await p3; - expect(count).toBe(3); - expect(gcjob).toBe(1); - }); -}); diff --git a/src/__tests__/test-helpers/implement-react-native-libcore/index.ts b/src/__tests__/test-helpers/implement-react-native-libcore/index.ts deleted file mode 100644 index 9438ec3700..0000000000 --- a/src/__tests__/test-helpers/implement-react-native-libcore/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import implementLibcore from "../../../libcore/platforms/react-native"; -// @ts-expect-error react native specific platform import -import { getNativeModule } from "./specific"; -implementLibcore({ - getNativeModule, -}); diff --git a/src/__tests__/test-helpers/implement-react-native-libcore/specific.android.ts b/src/__tests__/test-helpers/implement-react-native-libcore/specific.android.ts deleted file mode 100644 index 739ee96504..0000000000 --- a/src/__tests__/test-helpers/implement-react-native-libcore/specific.android.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-expect-error react native import -import { NativeModules } from "react-native"; -export const getNativeModule = (name: string): any => - NativeModules[`Core${name}`]; diff --git a/src/__tests__/test-helpers/implement-react-native-libcore/specific.ios.ts b/src/__tests__/test-helpers/implement-react-native-libcore/specific.ios.ts deleted file mode 100644 index 949b6ebb30..0000000000 --- a/src/__tests__/test-helpers/implement-react-native-libcore/specific.ios.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-expect-error react native import -import { NativeModules } from "react-native"; -export const getNativeModule = (name: string): any => - NativeModules[`CoreLG${name}`]; diff --git a/src/__tests__/test-helpers/libcore-setup.native.ts b/src/__tests__/test-helpers/libcore-setup.native.ts index 11166bec75..2f855f496e 100644 --- a/src/__tests__/test-helpers/libcore-setup.native.ts +++ b/src/__tests__/test-helpers/libcore-setup.native.ts @@ -1,7 +1,6 @@ /* eslint-disable no-console */ import { listen } from "@ledgerhq/logs"; import "./setup"; -import "./implement-react-native-libcore"; declare global { namespace NodeJS { diff --git a/src/__tests__/test-helpers/libcore-setup.ts b/src/__tests__/test-helpers/libcore-setup.ts index 08eee9d918..322703fd7e 100644 --- a/src/__tests__/test-helpers/libcore-setup.ts +++ b/src/__tests__/test-helpers/libcore-setup.ts @@ -3,7 +3,7 @@ import winston from "winston"; import { listen } from "@ledgerhq/logs"; import "./setup"; import { EnvName, setEnvUnsafe } from "../../env"; -import implementLibcore from "../../libcore/platforms/nodejs"; + let setupCalled = null; export const setup = (testId) => { if (setupCalled) { @@ -11,13 +11,7 @@ export const setup = (testId) => { "setup(" + testId + "): was already called with " + setupCalled ); } - setupCalled = testId; - implementLibcore({ - lib: () => require("@ledgerhq/ledger-core"), - // eslint-disable-line global-require - dbPath: "./libcoredb/" + testId, - }); }; for (const k in process.env) setEnvUnsafe(k as EnvName, process.env[k]); diff --git a/src/account/support.ts b/src/account/support.ts index e9cea50790..c03c5dea02 100644 --- a/src/account/support.ts +++ b/src/account/support.ts @@ -18,28 +18,11 @@ import { import { isCurrencySupported } from "../currencies"; import { getMainAccount } from "../account"; import { getAccountBridge } from "../bridge"; -import jsBridges from "../generated/bridge/js"; -const experimentalIntegrations = ["tezos"]; - -export function shouldUseJS(currency: CryptoCurrency) { - const jsBridge = jsBridges[currency.family]; - if (!jsBridge) return false; - - if (experimentalIntegrations.includes(currency.id)) { - return getEnv("EXPERIMENTAL_CURRENCIES_JS_BRIDGE") - .split(",") - .includes(currency.id); - } - - return true; -} -export const libcoreNoGoBalanceHistory = () => - getEnv("LIBCORE_BALANCE_HISTORY_NOGO").split(","); export const shouldShowNewAccount = ( currency: CryptoCurrency, derivationMode: DerivationMode -) => { +): boolean => { const modes = getDerivationModesForCurrency(currency); // last mode is always creatable by convention if (modes[modes.length - 1] === derivationMode) return true; @@ -79,7 +62,7 @@ export function canSend( return false; } } -export function canBeMigrated(account: Account) { +export function canBeMigrated(account: Account): boolean { try { const { version } = decodeAccountId(account.id); diff --git a/src/api/Ledger.ts b/src/api/Ledger.ts index 3026d271a4..59d392a337 100644 --- a/src/api/Ledger.ts +++ b/src/api/Ledger.ts @@ -15,7 +15,6 @@ export const findCurrencyExplorer = ( const config = getExplorerConfig()[currency.id]; if (!config) return; const { id } = config; - if (getEnv("SATSTACK") && currency.id === "bitcoin") { return { endpoint: getEnv("EXPLORER_SATSTACK"), @@ -23,19 +22,9 @@ export const findCurrencyExplorer = ( version: "v3", }; } - if (config.experimental && getEnv("EXPERIMENTAL_EXPLORERS")) { const base = config.experimental.base; - let version = config.experimental.version; - const CURRENCIES_JS = getEnv("EXPERIMENTAL_CURRENCIES_JS_BRIDGE"); - let useJS = false; - if (CURRENCIES_JS) { - useJS = CURRENCIES_JS.split(",").includes(currency.id); - } - //V2 explorer for doge and bitcoin cash when libcore is used - if ((currency.id === "bitcoin_cash" || currency.id === "doge") && !useJS) { - version = "v2"; - } + const version = config.experimental.version; return { endpoint: getEnv(base), id, diff --git a/src/bot/index.ts b/src/bot/index.ts index bf0f8f9f38..be7774f8ec 100644 --- a/src/bot/index.ts +++ b/src/bot/index.ts @@ -7,7 +7,6 @@ import flatMap from "lodash/flatMap"; import { getEnv } from "../env"; import allSpecs from "../generated/specs"; import network from "../network"; -import { withLibcore } from "../libcore/access"; import type { MutationReport, SpecReport } from "./types"; import { promiseAllBatched } from "../promise"; import { @@ -35,10 +34,6 @@ const usd = getFiatCurrencyByTicker("USD"); export async function bot({ currency, family, mutation }: Arg = {}) { const SEED = getEnv("SEED"); invariant(SEED, "SEED required"); - const libcoreVersion = await withLibcore((core) => - core.LedgerCore.getStringVersion() - ); - log("libcoreVersion", "libcore version " + libcoreVersion); const specs: any[] = []; const specsLogs: string[][] = []; const maybeCurrency = currency diff --git a/src/bridge/impl.ts b/src/bridge/impl.ts index 97b279c2dc..ce4be985a7 100644 --- a/src/bridge/impl.ts +++ b/src/bridge/impl.ts @@ -8,10 +8,10 @@ import type { } from "../types"; import { decodeAccountId, getMainAccount } from "../account"; import { getEnv } from "../env"; -import { checkAccountSupported, shouldUseJS } from "../account/support"; +import { checkAccountSupported } from "../account/support"; import jsBridges from "../generated/bridge/js"; import mockBridges from "../generated/bridge/mock"; -import libcoreBridges from "../generated/bridge/libcore"; + export const getCurrencyBridge = (currency: CryptoCurrency): CurrencyBridge => { if (getEnv("MOCK")) { const mockBridge = mockBridges[currency.family]; @@ -25,16 +25,10 @@ export const getCurrencyBridge = (currency: CryptoCurrency): CurrencyBridge => { } const jsBridge = jsBridges[currency.family]; - const libcoreBridge = libcoreBridges[currency.family]; - - if (jsBridge && (!libcoreBridge || shouldUseJS(currency))) { + if (jsBridge) { return jsBridge.currencyBridge; } - if (libcoreBridge) { - return libcoreBridge.currencyBridge; - } - throw new CurrencyNotSupported( "no implementation available for currency " + currency.id, { @@ -68,17 +62,11 @@ export const getAccountBridge = ( } const jsBridge = jsBridges[family]; - if (type === "libcore") { - if (jsBridge && shouldUseJS(currency)) { + // migrate from libcore via JS + if (jsBridge) { return jsBridge.accountBridge; } - - // TODO at this point, we might want to check if an impl in JS exists - // and if it's not flagged as experimental, we make an implicit migration that would happen to change ids and change bridge implementation - // FIXME: how will addAccount reconciliate accounts? - const libcoreBridge = libcoreBridges[family]; - if (libcoreBridge) return libcoreBridge.accountBridge; throw new CurrencyNotSupported( "no libcore implementation available for currency " + currency.id, { diff --git a/src/bridge/shared.ts b/src/bridge/shared.ts deleted file mode 100644 index ffc6026960..0000000000 --- a/src/bridge/shared.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { RecipientRequired } from "@ledgerhq/errors"; -import type { CryptoCurrency } from "../types"; -import { isValidRecipient } from "../libcore/isValidRecipient"; -import { makeLRUCache } from "../cache"; -// TODO drop this file. move back to families -export const validateRecipient: ( - arg0: CryptoCurrency, - arg1: string | null | undefined -) => Promise<{ - recipientError: Error | null | undefined; - recipientWarning: Error | null | undefined; -}> = makeLRUCache( - async (currency, recipient) => { - if (!recipient) { - return { - recipientError: new RecipientRequired(""), - recipientWarning: null, - }; - } - - try { - const recipientWarning = await isValidRecipient({ - currency, - recipient, - }); - return { - recipientError: null, - recipientWarning, - }; - } catch (recipientError) { - return { - recipientError, - recipientWarning: null, - }; - } - }, - (currency, recipient) => `${currency.id}_${recipient || ""}` -); diff --git a/src/cross.ts b/src/cross.ts index 7970a0eb0a..fa6a8a49a3 100644 --- a/src/cross.ts +++ b/src/cross.ts @@ -304,13 +304,11 @@ export const accountDataToAccount = ({ let freshAddressPath = ""; if ( - type === "libcore" || // FIXME Dirty hack, since we have no way here to know if "xpubOrAddress" is one or the other. // Proposed fix: https://ledgerhq.atlassian.net/browse/LL-7437 currency.family === "bitcoin" ) { - // In libcore implementation, xpubOrAddress field always go in the xpub - // In JS implementation, only Bitcoin-like currencies store the xpub + // In bitcoin implementation, xpubOrAddress field always go in the xpub xpub = xpubOrAddress; } else { if (currency.family === "tezos") { diff --git a/src/derivation.ts b/src/derivation.ts index ab432842dd..a8e004b0d0 100644 --- a/src/derivation.ts +++ b/src/derivation.ts @@ -13,13 +13,11 @@ import type { CryptoCurrency, CryptoCurrencyIds } from "./types"; import { getCryptoCurrencyById } from "./currencies"; import { getEnv } from "./env"; import type { GetAddressOptions, Result } from "./hw/getAddress/types"; -type LibcoreConfig = Record; export type ModeSpec = { mandatoryEmptyAccountSkip?: number; isNonIterable?: boolean; startsAt?: number; overridesDerivation?: string; - libcoreConfig?: LibcoreConfig; isSegwit?: boolean; isNativeSegwit?: boolean; isTaproot?: boolean; @@ -36,31 +34,7 @@ export type ModeSpec = { addressFormat?: string; }; export type DerivationMode = keyof typeof modes; -const extraConfigPerCurrency: Record = { - tezos: { - BLOCKCHAIN_EXPLORER_ENGINE: "TZSTATS_API", - BLOCKCHAIN_EXPLORER_API_ENDPOINT: () => - getEnv("API_TEZOS_BLOCKCHAIN_EXPLORER_API_ENDPOINT"), - TEZOS_PROTOCOL_UPDATE: "TEZOS_PROTOCOL_UPDATE_BABYLON", - TEZOS_NODE: () => getEnv("API_TEZOS_NODE"), - }, - cosmos: { - BLOCKCHAIN_EXPLORER_ENGINE: () => getEnv("API_COSMOS_NODE"), - BLOCKCHAIN_OBSERVER_ENGINE: () => getEnv("API_COSMOS_NODE"), - BLOCKCHAIN_EXPLORER_API_ENDPOINT: () => - getEnv("API_COSMOS_BLOCKCHAIN_EXPLORER_API_ENDPOINT"), - }, - cosmos_testnet: { - BLOCKCHAIN_EXPLORER_ENGINE: () => getEnv("API_COSMOS_TESTNET_NODE"), - BLOCKCHAIN_OBSERVER_ENGINE: () => getEnv("API_COSMOS_TESTNET_NODE"), - BLOCKCHAIN_EXPLORER_API_ENDPOINT: () => - getEnv("API_COSMOS_TESTNET_BLOCKCHAIN_EXPLORER_API_ENDPOINT"), - }, - algorand: { - BLOCKCHAIN_EXPLORER_API_ENDPOINT: () => - getEnv("API_ALGORAND_BLOCKCHAIN_EXPLORER_API_ENDPOINT"), - }, -}; + const modes = Object.freeze({ // this is "default" by convention "": {}, @@ -83,9 +57,6 @@ const modes = Object.freeze({ isInvalid: true, isSegwit: true, purpose: 49, - libcoreConfig: { - KEYCHAIN_ENGINE: "BIP49_P2SH", - }, addressFormat: "p2sh", }, // many users have wrongly sent BTC on BCH paths @@ -103,9 +74,6 @@ const modes = Object.freeze({ overridesCoinType: 128, isSegwit: true, purpose: 49, - libcoreConfig: { - KEYCHAIN_ENGINE: "BIP49_P2SH", - }, addressFormat: "p2sh", }, // MEW legacy derivation for eth @@ -120,32 +88,20 @@ const modes = Object.freeze({ // default derivation of tezbox offerred to users tezbox: { overridesDerivation: "44'/1729'/'/0'", - libcoreConfig: { - TEZOS_XPUB_CURVE: "ED25519", - }, }, tezosbip44h: { tag: "galleon", overridesDerivation: "44'/1729'/'/0'/0'", - libcoreConfig: { - TEZOS_XPUB_CURVE: "ED25519", - }, }, galleonL: { tag: "legacy", startsAt: 1, overridesDerivation: "44'/1729'/0'/0'/'", - libcoreConfig: { - TEZOS_XPUB_CURVE: "ED25519", - }, }, tezboxL: { tag: "legacy", startsAt: 1, overridesDerivation: "44'/1729'/0'/'", - libcoreConfig: { - TEZOS_XPUB_CURVE: "ED25519", - }, }, taproot: { purpose: 86, @@ -156,9 +112,6 @@ const modes = Object.freeze({ }, native_segwit: { purpose: 84, - libcoreConfig: { - KEYCHAIN_ENGINE: "BIP173_P2WPKH", - }, addressFormat: "bech32", tag: "native segwit", isSegwit: true, @@ -167,41 +120,26 @@ const modes = Object.freeze({ segwit: { isSegwit: true, purpose: 49, - libcoreConfig: { - KEYCHAIN_ENGINE: "BIP49_P2SH", - }, tag: "segwit", addressFormat: "p2sh", }, segwit_on_legacy: { isSegwit: true, purpose: 44, - libcoreConfig: { - KEYCHAIN_ENGINE: "BIP49_P2SH", - }, addressFormat: "p2sh", isInvalid: true, }, legacy_on_segwit: { purpose: 49, - libcoreConfig: { - KEYCHAIN_ENGINE: "BIP32_P2PKH", - }, isInvalid: true, }, legacy_on_native_segwit: { purpose: 84, - libcoreConfig: { - KEYCHAIN_ENGINE: "BIP32_P2PKH", - }, isInvalid: true, }, segwit_unsplit: { isSegwit: true, purpose: 49, - libcoreConfig: { - KEYCHAIN_ENGINE: "BIP49_P2SH", - }, addressFormat: "p2sh", isUnsplit: true, tag: "segwit unsplit", @@ -279,29 +217,7 @@ export const isTaprootDerivationMode = ( derivationMode: DerivationMode ): boolean => (modes[derivationMode] as { isTaproot: boolean }).isTaproot || false; -export const getLibcoreConfig = ( - currency: CryptoCurrency, - derivationMode: DerivationMode -): Record => { - const obj = {}; - const extra = { - ...extraConfigPerCurrency[currency.id], - ...(modes[derivationMode] as { libcoreConfig: LibcoreConfig }) - .libcoreConfig, - }; - for (const k in extra) { - const v = extra[k]; - - if (typeof v === "function") { - obj[k] = v(); - } else { - obj[k] = v; - } - } - - return obj; -}; export const isUnsplitDerivationMode = ( derivationMode: DerivationMode ): boolean => diff --git a/src/env.ts b/src/env.ts index 78dc63ebaa..5599c86363 100644 --- a/src/env.ts +++ b/src/env.ts @@ -235,11 +235,6 @@ const envDefinitions = { parser: stringParser, desc: "enable experimental support of currencies (comma separated)", }, - EXPERIMENTAL_CURRENCIES_JS_BRIDGE: { - def: "", - parser: stringParser, - desc: "enable JS integration of currencies (comma separated)", - }, EXPERIMENTAL_EXPLORERS: { def: false, parser: boolParser, @@ -255,11 +250,6 @@ const envDefinitions = { parser: boolParser, desc: "enable experimental languages", }, - EXPERIMENTAL_LIBCORE: { - def: false, - parser: boolParser, - desc: "enable experimental libcore implementation of a currency (affects scan accounts)", - }, EXPERIMENTAL_MANAGER: { def: false, parser: boolParser, @@ -345,17 +335,6 @@ const envDefinitions = { parser: boolParser, desc: "enable sending to KT accounts. Not tested.", }, - LIBCORE_BALANCE_HISTORY_NOGO: { - def: "ripple,ethereum,tezos,stellar", - // LLC-475 - parser: stringParser, - desc: "comma-separated list of currencies which does not properly support balance history libcore implementation", - }, - LIBCORE_PASSWORD: { - def: "", - parser: stringParser, - desc: "libcore encryption password", - }, MANAGER_API_BASE: { def: "https://manager.api.live.ledger.com/api", parser: stringParser, @@ -413,7 +392,7 @@ const envDefinitions = { OPERATION_OPTIMISTIC_RETENTION: { def: 30 * 60 * 1000, parser: intParser, - desc: "timeout to keep an optimistic operation that was broadcasted but not yet visible from libcore or the API", + desc: "timeout to keep an optimistic operation that was broadcasted but not yet visible from the coin implementation or the API", }, OPERATION_PAGE_SIZE_INITIAL: { def: 100, diff --git a/src/families/algorand/bridge/libcore.ts b/src/families/algorand/bridge/libcore.ts deleted file mode 100644 index d471f6ba36..0000000000 --- a/src/families/algorand/bridge/libcore.ts +++ /dev/null @@ -1,345 +0,0 @@ -import { getAbandonSeedAddress } from "@ledgerhq/cryptoassets"; -import { BigNumber } from "bignumber.js"; -import invariant from "invariant"; -import { - AmountRequired, - InvalidAddressBecauseDestinationIsAlsoSource, - NotEnoughBalance, - FeeNotLoaded, - FeeTooHigh, - NotEnoughBalanceBecauseDestinationNotCreated, - NotEnoughBalanceInParentAccount, -} from "@ledgerhq/errors"; -import { AlgorandASANotOptInInRecipient } from "../../../errors"; -import { validateRecipient } from "../../../bridge/shared"; -import type { AccountBridge, CurrencyBridge, Account } from "../../../types"; -import type { AlgorandResources, AlgorandTransaction } from "../types"; -import { AlgorandOperationTypeEnum } from "../types"; -import { scanAccounts } from "../../../libcore/scanAccounts"; -import { sync } from "../../../libcore/syncAccount"; -import type { CacheRes } from "../../../cache"; -import { makeLRUCache } from "../../../cache"; -import { getMainAccount } from "../../../account"; -import broadcast from "../libcore-broadcast"; -import signOperation from "../libcore-signOperation"; -import { getFeesForTransaction } from "../../../libcore/getFeesForTransaction"; -import { withLibcore } from "../../../libcore/access"; -import { getCoreAccount } from "../../../libcore/getCoreAccount"; -import { libcoreAmountToBigNumber } from "../../../libcore/buildBigNumber"; -import { extractTokenId } from "../tokens"; -import { ALGORAND_MAX_MEMO_SIZE } from "../logic"; -import { makeAccountBridgeReceive } from "../../../bridge/jsHelpers"; -import { ClaimRewardsFeesWarning } from "../../../errors"; -const receive = makeAccountBridgeReceive(); -export const calculateFees: CacheRes< - Array<{ - a: Account; - t: AlgorandTransaction; - }>, - { - estimatedFees: BigNumber; - estimatedGas: BigNumber | null | undefined; - } -> = makeLRUCache( - async ({ - a, - t, - }): Promise<{ - estimatedFees: BigNumber; - estimatedGas: BigNumber | null | undefined; - }> => { - return await getFeesForTransaction({ - account: a, - transaction: t, - }); - }, - ({ a, t }) => - `${a.id}_${t.amount.toString()}_${t.recipient}_${String(t.useAllAmount)}_${ - t.memo ? t.memo.toString() : "" - }_${t.mode}_${t.assetId || ""}` -); - -const getSpendableMaxForOptIn = async (account) => - await withLibcore(async (core) => { - const { coreAccount } = await getCoreAccount(core, account); - const algorandAccount = await coreAccount.asAlgorandAccount(); - const spendableBalanceBigInt = await algorandAccount.getSpendableBalance( - AlgorandOperationTypeEnum.ASSET_OPT_IN - ); - const spendableBalance = await libcoreAmountToBigNumber( - spendableBalanceBigInt - ); - return spendableBalance; - }); - -const createTransaction = (): AlgorandTransaction => ({ - family: "algorand", - amount: new BigNumber(0), - fees: null, - recipient: "", - useAllAmount: false, - memo: null, - mode: "send", - assetId: null, -}); - -const updateTransaction = (t, patch) => { - return { ...t, ...patch }; -}; - -const recipientHasAsset = async (assetId, recipient, account) => - await withLibcore(async (core) => { - const { coreAccount } = await getCoreAccount(core, account); - const algorandAccount = await coreAccount.asAlgorandAccount(); - const hasAsset = await algorandAccount.hasAsset(recipient, assetId); - return hasAsset; - }); - -const getAmountValid = async (recipient, amount, account) => - await withLibcore(async (core) => { - const { coreAccount } = await getCoreAccount(core, account); - const algorandAccount = await coreAccount.asAlgorandAccount(); - const isValid = await algorandAccount.isAmountValid( - recipient, - amount.toString() - ); - return isValid; - }); - -/* - * Here are the list of the differents things we check - * - Check if recipient is the same in case of send - * - Check if recipient is valid - * - Check if amounts are set - * - Check if fees are loaded - * - Check if is a send Max and set the amount - * - Check if Token is already optin at the recipient - * - Check if memo is too long - */ -const getTransactionStatus = async (a: Account, t) => { - const errors: any = {}; - const warnings: any = {}; - const tokenAccount = !t.subAccountId - ? null - : a.subAccounts && a.subAccounts.find((ta) => ta.id === t.subAccountId); - - if (t.mode === "send" && a.freshAddress === t.recipient) { - errors.recipient = new InvalidAddressBecauseDestinationIsAlsoSource(); - } else { - const { recipientError, recipientWarning } = await validateRecipient( - a.currency, - t.recipient - ); - - if (recipientError) { - errors.recipient = recipientError; - } - - if (recipientWarning) { - warnings.recipient = recipientWarning; - } - } - - const estimatedFees = t.fees || new BigNumber(0); - let amount = t.amount; - let totalSpent = estimatedFees; - - switch (t.mode) { - case "send": { - if (amount.lte(0) && !t.useAllAmount) { - errors.amount = new AmountRequired(); - } - - if (!t.fees || !t.fees.gt(0)) { - errors.fees = new FeeNotLoaded(); - } - - if ( - tokenAccount && - tokenAccount.type === "TokenAccount" && - !errors.recipient && - !(await recipientHasAsset( - extractTokenId(tokenAccount.token.id), - t.recipient, - a - )) - ) { - errors.recipient = new AlgorandASANotOptInInRecipient(); - } - - amount = t.useAllAmount - ? tokenAccount - ? tokenAccount.balance - : a.spendableBalance.minus(estimatedFees) - : amount; - - if (amount.lt(0)) { - amount = new BigNumber(0); - } - - totalSpent = tokenAccount ? amount : amount.plus(estimatedFees); - - if ( - !errors.recipient && - !(await getAmountValid(t.recipient, amount, a)) - ) { - errors.amount = new NotEnoughBalanceBecauseDestinationNotCreated("", { - minimalAmount: "0.1 ALGO", - }); - } - - if (!tokenAccount && amount.gt(0) && estimatedFees.times(10).gt(amount)) { - warnings.feeTooHigh = new FeeTooHigh(); - } - - if ( - (amount.lte(0) && t.useAllAmount) || // if use all Amount sets an amount at 0 - (!errors.recipient && !errors.amount && tokenAccount - ? totalSpent.gt(tokenAccount.balance) - : totalSpent.gt(a.spendableBalance)) // if spendable balance lower than total - ) { - errors.amount = new NotEnoughBalance(); - } - - // if spendable balance lower than fees for token - if ( - !errors.amount && - tokenAccount && - a.spendableBalance.lt(estimatedFees) - ) { - errors.amount = new NotEnoughBalanceInParentAccount(); - } - - break; - } - - case "optIn": { - if (!t.fees || !t.fees.gt(0)) { - errors.fees = new FeeNotLoaded(); - } - - // This error doesn't need to be translate, - // it will use to block until the user choose an assetId - if (!t.assetId) { - errors.assetId = new Error("Asset Id is not set"); - } - - const spendableBalance = await getSpendableMaxForOptIn(a); - - if (spendableBalance.lt(estimatedFees)) { - errors.amount = new NotEnoughBalance(); - } - - break; - } - - case "claimReward": { - if (a.spendableBalance.lt(totalSpent)) { - errors.amount = new NotEnoughBalance(); - } - - invariant(a.algorandResources, "Algorand family"); - - if ( - estimatedFees.gt((a.algorandResources as AlgorandResources).rewards) - ) { - warnings.claimReward = new ClaimRewardsFeesWarning(); - } - - break; - } - } - - if (t.memo && t.memo.length > ALGORAND_MAX_MEMO_SIZE) { - throw new Error("Memo is too long"); - } - - return Promise.resolve({ - errors, - warnings, - estimatedFees, - amount, - totalSpent, - }); -}; - -const sameFees = (a, b) => (!a || !b ? a === b : a.eq(b)); - -const prepareTransaction = async (a, t) => { - let fees = t.fees; - let amount = t.amount; - let recipient = t.recipient; - - if (t.mode === "optIn" || t.mode === "claimReward") { - recipient = a.freshAddress; - amount = new BigNumber(0); - } - - if (recipient || t.mode !== "send") { - let errors: Error | undefined | null | boolean = ( - await validateRecipient(a.currency, recipient) - ).recipientError; - errors = errors || (t.mode === "optIn" && !t.assetId); - - if (!errors) { - const res = await calculateFees({ - a, - t, - }); - fees = res.estimatedFees; - } - } - - if ( - !sameFees(t.fees, fees) || - !sameFees(t.amount, amount) || - t.recipient !== recipient - ) { - return { ...t, fees, amount, recipient }; - } - - return t; -}; - -const estimateMaxSpendable = async ({ - account, - parentAccount, - transaction, -}) => { - const mainAccount = getMainAccount(account, parentAccount); - const t = await prepareTransaction(mainAccount, { - ...createTransaction(), - subAccountId: account.type === "Account" ? null : account.id, - ...transaction, - recipient: - transaction?.recipient || getAbandonSeedAddress(mainAccount.currency.id), - useAllAmount: true, - }); - const s = await getTransactionStatus(mainAccount, t); - return s.amount; -}; - -const preload = async () => Promise.resolve({}); - -const hydrate = () => {}; - -const currencyBridge: CurrencyBridge = { - preload, - hydrate, - scanAccounts, -}; -const accountBridge: AccountBridge = { - createTransaction, - updateTransaction, - prepareTransaction, - getTransactionStatus, - sync, - receive, - signOperation, - broadcast, - estimateMaxSpendable, -}; -export default { - currencyBridge, - accountBridge, -}; diff --git a/src/families/algorand/buildASAOperation.ts b/src/families/algorand/buildASAOperation.ts deleted file mode 100644 index 2c7784ac1f..0000000000 --- a/src/families/algorand/buildASAOperation.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { BigNumber } from "bignumber.js"; -import { AlgorandOperationTypeEnum } from "./types"; -import type { Operation, OperationType } from "../../types"; -import type { CoreOperation } from "../../libcore/types"; -import { encodeOperationId } from "../../operation"; -const OperationTypeMap = { - "0": "OUT", - "1": "IN", -}; -export async function buildASAOperation(arg: { - coreOperation: CoreOperation; - accountId: string; - tokenId: string; -}) { - const { coreOperation, accountId, tokenId } = arg; - const algorandOperation = await coreOperation.asAlgorandOperation(); - const transaction = await algorandOperation.getTransaction(); - const hash = await transaction.getId(); - const algoOpeType = await algorandOperation.getAlgorandOperationType(); - - if ( - ![ - AlgorandOperationTypeEnum.ASSET_OPT_OUT, - AlgorandOperationTypeEnum.ASSET_TRANSFER, - ].includes(algoOpeType) - ) { - return null; - } - - const transferInfo = await transaction.getAssetTransferInfo(); - - if ((await transferInfo.getAssetId()) !== tokenId) { - return null; - } - - const operationType = await coreOperation.getOperationType(); - const type = OperationTypeMap[operationType]; - const id = encodeOperationId(accountId, hash, type); - const blockHeight = parseInt(await transaction.getRound()); - const value = await algorandOperation.getAssetAmount(); - const receiver = - algoOpeType === 8 - ? [ - await transferInfo.getRecipientAddress(), - await transferInfo.getCloseAddress(), - ] - : [await transferInfo.getRecipientAddress()]; - const fee = await transaction.getFee(); - const sender = await transaction.getSender(); - const date = new Date(await coreOperation.getDate()); - const op: Operation = { - id, - type: type as OperationType, - value: new BigNumber(value), - hash, - fee: new BigNumber(fee), - senders: [sender], - recipients: receiver, - blockHeight, - blockHash: null, - accountId, - date, - extra: {}, - }; - return op; -} diff --git a/src/families/algorand/libcore-broadcast.ts b/src/families/algorand/libcore-broadcast.ts deleted file mode 100644 index 02ad25eadc..0000000000 --- a/src/families/algorand/libcore-broadcast.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { Operation } from "../../types"; -import { makeBroadcast } from "../../libcore/broadcast"; -import { patchOperationWithHash } from "../../operation"; - -async function broadcast({ - coreAccount, - signedOperation: { operation, signature }, -}): Promise { - const algorandAccount = await coreAccount.asAlgorandAccount(); - let hash = ""; - hash = await algorandAccount.broadcastRawTransaction(signature); - const op = patchOperationWithHash(operation, hash); - return op; -} - -export default makeBroadcast({ - broadcast, -}); diff --git a/src/families/algorand/libcore-buildOperation.ts b/src/families/algorand/libcore-buildOperation.ts deleted file mode 100644 index 4dcf0b42d0..0000000000 --- a/src/families/algorand/libcore-buildOperation.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { BigNumber } from "bignumber.js"; -import type { CoreOperation } from "../../libcore/types"; -import type { Operation } from "../../types"; -import { AlgorandOperationTypeEnum } from "./types"; - -const getAssetId = async (transaction) => { - if ((await transaction.getType()) === "axfer") { - const assetInfo = await transaction.getAssetTransferInfo(); - return assetInfo.getAssetId(); - } - - return null; -}; - -const getOperationType = async (algorandOperation, transaction) => { - const operationType = await algorandOperation.getAlgorandOperationType(); - let type; - - if ((await transaction.getType()) === "axfer") { - type = "FEES"; - } - - if (operationType === AlgorandOperationTypeEnum.ASSET_OPT_IN) { - type = "OPT_IN"; - } - - if (operationType === AlgorandOperationTypeEnum.ASSET_OPT_OUT) { - type = "OPT_OUT"; - } - - return type; -}; - -async function algorandBuildOperation({ - coreOperation, -}: { - coreOperation: CoreOperation; -}) { - const algorandLikeOperation = await coreOperation.asAlgorandOperation(); - const algorandLikeTransaction = await algorandLikeOperation.getTransaction(); - const hash = await algorandLikeTransaction.getId(); - const out: Partial = { - hash, - }; - const type = await getOperationType( - algorandLikeOperation, - algorandLikeTransaction - ); - - if (type) { - out.type = type; - } - - const assetId = await getAssetId(algorandLikeTransaction); - - if (assetId) { - out.extra = { ...out.extra, assetId: assetId }; - } - - const rewards = await algorandLikeOperation.getRewards(); - - if (rewards) { - out.extra = { ...out.extra, rewards: new BigNumber(rewards) }; - } - - return out; -} - -export default algorandBuildOperation; diff --git a/src/families/algorand/libcore-buildSubAccounts.ts b/src/families/algorand/libcore-buildSubAccounts.ts deleted file mode 100644 index d1ab036911..0000000000 --- a/src/families/algorand/libcore-buildSubAccounts.ts +++ /dev/null @@ -1,132 +0,0 @@ -import type { - CryptoCurrency, - TokenAccount, - Account, - SyncConfig, -} from "../../types"; -import type { CoreAccount, CoreOperation } from "../../libcore/types"; -import { minimalOperationsBuilder } from "../../reconciliation"; -import { buildASAOperation } from "./buildASAOperation"; -import { BigNumber } from "bignumber.js"; -import { findTokenById, listTokensForCryptoCurrency } from "../../currencies"; -import { emptyHistoryCache } from "../../account"; -import { promiseAllBatched } from "../../promise"; -import { extractTokenId, addPrefixToken } from "./tokens"; -const OperationOrderKey = { - date: 0, -}; - -async function buildAlgorandTokenAccount({ - parentAccountId, - token, - coreAccount, - existingTokenAccount, - balance, -}) { - const extractedId = extractTokenId(token.id); - const id = parentAccountId + "+" + extractedId; - - const getAllOperations = async () => { - const query = await coreAccount.queryOperations(); - await query.complete(); - await query.addOrder(OperationOrderKey.date, false); - const coreOperations = await query.execute(); - const operations = await minimalOperationsBuilder( - (existingTokenAccount && existingTokenAccount.operations) || [], - coreOperations, - (coreOperation: CoreOperation) => - buildASAOperation({ - coreOperation, - accountId: id, - tokenId: extractedId, - }) - ); - return operations; - }; - - const operations = await getAllOperations(); - const tokenAccount: TokenAccount = { - type: "TokenAccount", - id, - parentId: parentAccountId, - starred: false, - token, - operationsCount: operations.length, - operations, - pendingOperations: [], - balance, - spendableBalance: balance, - swapHistory: [], - creationDate: - operations.length > 0 - ? operations[operations.length - 1].date - : new Date(), - balanceHistoryCache: emptyHistoryCache, // calculated in the jsHelpers - }; - return tokenAccount; -} - -async function algorandBuildTokenAccounts({ - currency, - coreAccount, - accountId, - existingAccount, - syncConfig, -}: { - currency: CryptoCurrency; - coreAccount: CoreAccount; - accountId: string; - existingAccount: Account | null | undefined; - syncConfig: SyncConfig; -}): Promise { - const { blacklistedTokenIds = [] } = syncConfig; - if (listTokensForCryptoCurrency(currency).length === 0) return undefined; - const tokenAccounts: TokenAccount[] = []; - const algorandAccount = await coreAccount.asAlgorandAccount(); - const accountASA = await algorandAccount.getAssetsBalances(); - const existingAccountByTicker = {}; // used for fast lookup - - const existingAccountTickers: string[] = []; // used to keep track of ordering - - if (existingAccount && existingAccount.subAccounts) { - for (const existingSubAccount of existingAccount.subAccounts) { - if (existingSubAccount.type === "TokenAccount") { - const { ticker, id } = existingSubAccount.token; - - if (!blacklistedTokenIds.includes(id)) { - existingAccountTickers.push(ticker); - existingAccountByTicker[ticker] = existingSubAccount; - } - } - } - } - - // filter by token existence - await promiseAllBatched(3, accountASA, async (asa) => { - const token = findTokenById(addPrefixToken(await asa.getAssetId())); - - if (token && !blacklistedTokenIds.includes(token.id)) { - const existingTokenAccount = existingAccountByTicker[token.ticker]; - const tokenAccount = await buildAlgorandTokenAccount({ - parentAccountId: accountId, - existingTokenAccount, - token, - coreAccount, - balance: new BigNumber(await asa.getAmount()), - }); - if (tokenAccount) tokenAccounts.push(tokenAccount); - } - }); - // Preserve order of tokenAccounts from the existing token accounts - tokenAccounts.sort((a, b) => { - const i = existingAccountTickers.indexOf(a.token.ticker); - const j = existingAccountTickers.indexOf(b.token.ticker); - if (i === j) return 0; - if (i < 0) return 1; - if (j < 0) return -1; - return i - j; - }); - return tokenAccounts; -} - -export default algorandBuildTokenAccounts; diff --git a/src/families/algorand/libcore-buildTransaction.ts b/src/families/algorand/libcore-buildTransaction.ts deleted file mode 100644 index a4cd7bfa14..0000000000 --- a/src/families/algorand/libcore-buildTransaction.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { FeeNotLoaded } from "@ledgerhq/errors"; -import type { Account } from "../../types"; -import { libcoreAmountToBigNumber } from "../../libcore/buildBigNumber"; -import type { Core, CoreAccount } from "../../libcore/types"; -import type { CoreAlgorandTransaction, AlgorandTransaction } from "./types"; -import { extractTokenId } from "./tokens"; - -const setInfo = async ( - core, - buildedTransaction, - transaction, - subAccount, - account, - isPartial -) => { - const { amount, recipient, assetId, mode, useAllAmount } = transaction; - - if (subAccount || (assetId && mode === "optIn")) { - const targetAssetId = - subAccount && subAccount.type === "TokenAccount" - ? extractTokenId(subAccount.token.id) - : assetId - ? extractTokenId(assetId) - : ""; - - if (targetAssetId === "") { - throw new Error("Token Asset Id not found"); - } - - const asaTransferInfo = await core.AlgorandAssetTransferInfo.init( - targetAssetId, - useAllAmount && subAccount - ? subAccount.balance.toString() - : amount.toString(), - recipient, - null, - null, - null - ); - await buildedTransaction.setAssetTransferInfo(asaTransferInfo); - } else { - const paymentinfo = await core.AlgorandPaymentInfo.init( - recipient, - useAllAmount - ? isPartial - ? account.spendableBalance.toString() - : account.spendableBalance.minus(transaction.fees || 0).toString() - : amount.toString(), - null, - null - ); - await buildedTransaction.setPaymentInfo(paymentinfo); - } -}; - -export async function algorandBuildTransaction({ - account, - core, - coreAccount, - transaction, - isCancelled, - isPartial, -}: { - account: Account; - core: Core; - coreAccount: CoreAccount; - transaction: AlgorandTransaction; - isPartial: boolean; - isCancelled: () => boolean; -}): Promise { - const { fees, memo, subAccountId } = transaction; - const subAccount = subAccountId - ? account.subAccounts && - account.subAccounts.find((t) => t.id === subAccountId) - : null; - - if (isPartial === false && !fees) { - throw new FeeNotLoaded(); - } - - const algorandAccount = await coreAccount.asAlgorandAccount(); - if (isCancelled()) return; - const buildedTransaction = await algorandAccount.createTransaction(); - if (isCancelled()) return; - // set Payment or Asset if token - await setInfo( - core, - buildedTransaction, - transaction, - subAccount, - account, - isPartial - ); - - // Note, maybe not available - if (memo) { - await buildedTransaction.setNote(memo); - } - - // if Partial getEstimateFees here - const feesToSet = isPartial - ? await libcoreAmountToBigNumber( - await algorandAccount.getFeeEstimate(buildedTransaction) - ) - : fees; - - // then setFees here in any case - if (feesToSet) { - await buildedTransaction.setFee(feesToSet.toString()); - } - - // return transaction - return buildedTransaction; -} -export default algorandBuildTransaction; diff --git a/src/families/algorand/libcore-getFeesForTransaction.ts b/src/families/algorand/libcore-getFeesForTransaction.ts deleted file mode 100644 index 232b5af3aa..0000000000 --- a/src/families/algorand/libcore-getFeesForTransaction.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { Account } from "../../types"; -import type { Core, CoreCurrency, CoreAccount } from "../../libcore/types"; -import type { AlgorandTransaction } from "./types"; -import buildTransaction from "./libcore-buildTransaction"; -import { BigNumber } from "bignumber.js"; - -async function algorand(args: { - account: Account; - core: Core; - coreAccount: CoreAccount; - coreCurrency: CoreCurrency; - transaction: AlgorandTransaction; - isPartial: boolean; - isCancelled: () => boolean; -}) { - const builded = await buildTransaction({ ...args, isPartial: true }); - if (!builded) return; - const fees = await builded.getFee(); - const estimatedFees = new BigNumber(fees); - return { - estimatedFees, - }; -} - -export default algorand; diff --git a/src/families/algorand/libcore-postBuildAccount.ts b/src/families/algorand/libcore-postBuildAccount.ts deleted file mode 100644 index 6786e16487..0000000000 --- a/src/families/algorand/libcore-postBuildAccount.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { Account } from "../../types"; -import type { CoreAccount } from "../../libcore/types"; -import { libcoreAmountToBigNumber } from "../../libcore/buildBigNumber"; -import type { AlgorandResources } from "./types"; -import { AlgorandOperationTypeEnum } from "./types"; - -const getAlgorandResources = async ( - coreAccount -): Promise => { - const algorandAccount = await coreAccount.asAlgorandAccount(); - const rewardsBigInt = await algorandAccount.getPendingRewards(); - const rewards = await libcoreAmountToBigNumber(rewardsBigInt); - return { - rewards, - nbAssets: 0, // unused in libcore implem - }; -}; - -const getAlgorandSpendableBalance = async (coreAccount) => { - const algorandAccount = await coreAccount.asAlgorandAccount(); - const spendableBalanceBigInt = await algorandAccount.getSpendableBalance( - AlgorandOperationTypeEnum.PAYMENT - ); - const spendableBalance = await libcoreAmountToBigNumber( - spendableBalanceBigInt - ); - return spendableBalance; -}; - -const postBuildAccount = async ({ - account, - coreAccount, -}: { - account: Account; - coreAccount: CoreAccount; -}): Promise => { - account.algorandResources = await getAlgorandResources(coreAccount); - account.spendableBalance = await getAlgorandSpendableBalance(coreAccount); - return account; -}; - -export default postBuildAccount; diff --git a/src/families/algorand/libcore-signOperation.ts b/src/families/algorand/libcore-signOperation.ts deleted file mode 100644 index b3f0f43616..0000000000 --- a/src/families/algorand/libcore-signOperation.ts +++ /dev/null @@ -1,104 +0,0 @@ -import Algorand from "@ledgerhq/hw-app-algorand"; -import { makeSignOperation } from "../../libcore/signOperation"; -import buildTransaction from "./libcore-buildTransaction"; -import type { AlgorandTransaction, CoreAlgorandTransaction } from "./types"; -import type { Operation } from "../../types"; -import { BigNumber } from "bignumber.js"; - -async function signTransaction({ - account, - transport, - transaction, - coreTransaction, - isCancelled, - onDeviceSignatureGranted, - onDeviceSignatureRequested, -}) { - const { freshAddressPath, spendableBalance, id, freshAddress, subAccounts } = - account; - const hwApp = new Algorand(transport); - const serialized = await coreTransaction.serialize(); - onDeviceSignatureRequested(); - // Call the hw-app signature - const { signature } = await hwApp.sign(freshAddressPath, serialized); - onDeviceSignatureGranted(); - - if (!signature) { - throw new Error("No signature"); - } - - // Set signature here - await coreTransaction.setSignature(signature.toString("hex")); - if (isCancelled()) return; - // Get the serialization after signature to send it to broadcast - const hex = await coreTransaction.serialize(); - if (isCancelled()) return; - // Add fees, senders (= account.freshAddress) and recipients. - const senders = [freshAddress]; - const recipients = [transaction.recipient]; - const fee = await coreTransaction.getFee(); - const { subAccountId } = transaction; - - const getType = () => { - return subAccountId - ? "FEES" - : transaction.mode === "optIn" - ? "OPT_IN" - : "OUT"; - }; - - const type = getType(); - const tokenAccount = !subAccountId - ? null - : subAccounts && subAccounts.find((ta) => ta.id === subAccountId); - const op: Operation = { - id: `${id}--${type}`, - hash: "", - type, - value: subAccountId - ? new BigNumber(fee) - : transaction.useAllAmount - ? spendableBalance - : transaction.amount.plus(fee), - fee: new BigNumber(fee), - blockHash: null, - blockHeight: null, - senders, - recipients, - accountId: id, - date: new Date(), - extra: {}, - }; - - if (tokenAccount && subAccountId) { - op.subOperations = [ - { - id: `${subAccountId}--OUT`, - hash: "", - type: "OUT", - value: transaction.useAllAmount - ? tokenAccount.balance - : transaction.amount, - fee: new BigNumber(0), - blockHash: null, - blockHeight: null, - senders, - recipients, - accountId: subAccountId, - date: new Date(), - extra: {}, - }, - ]; - } - - return { - operation: op, - expirationDate: null, - signature: hex, - }; -} - -export default makeSignOperation({ - buildTransaction, - signTransaction, -}); diff --git a/src/families/algorand/types.ts b/src/families/algorand/types.ts index 8f17c005f8..1e3e9e13c1 100644 --- a/src/families/algorand/types.ts +++ b/src/families/algorand/types.ts @@ -4,97 +4,13 @@ import type { TransactionCommonRaw, } from "../../types/transaction"; import type { Operation, OperationRaw } from "../../types/operation"; -import type { CoreAmount, Spec } from "../../libcore/types"; + export const AlgorandOperationTypeEnum = { PAYMENT: 0, ASSET_OPT_IN: 7, ASSET_OPT_OUT: 8, ASSET_TRANSFER: 9, }; -export type CoreStatics = { - AlgorandPaymentInfo: AlgorandPaymentInfo; - AlgorandAssetTransferInfo: AlgorandAssetTransferInfo; - AlgorandAddress: AlgorandAddress; -}; -export type CoreAccountSpecifics = { - asAlgorandAccount(): Promise; -}; -export type CoreOperationSpecifics = { - asAlgorandOperation(): Promise; -}; - -declare class AlgorandAddress { - fromPublicKey(pubkey: string): Promise; -} - -declare class AlgorandPaymentInfo { - init( - amount: string, - recipientAddress: string, - closeAddress: string | null | undefined, - closeAmount: string | null | undefined - ): Promise; -} - -declare class AlgorandAssetTransferInfo { - init( - assetId: string, - amount: string, - recipientAddress: string, - closeAddress: string | null | undefined, - clawedBackAddress: string | null | undefined, - closeAmount: string | null | undefined - ): Promise; - getAmount(): Promise; - getAssetId(): Promise; - getRecipientAddress(): Promise; - getCloseAddress(): Promise; - getCloseAmount(): Promise; -} - -declare class CoreAlgorandTransaction { - getId(): Promise; - getType(): Promise; - getSender(): Promise; - getFee(): Promise; - getNote(): Promise; - getRound(): Promise; - setSender(sender: string): void; - setFee(fee: string): void; - setNote(note: string): void; - setPaymentInfo(info: AlgorandPaymentInfo): void; - setAssetTransferInfo(info: AlgorandAssetTransferInfo): void; - serialize(): Promise; - setSignature(signature: string): void; - getAssetTransferInfo(): Promise; -} - -declare class AlgorandAssetAmount { - init(amount: string, assetId: string): Promise; - getAmount(): Promise; - getAssetId(): Promise; -} - -declare class CoreAlgorandAccount { - getAssetsBalances(): Promise; - createTransaction(): Promise; - getFeeEstimate(transaction: CoreAlgorandTransaction): Promise; - getPendingRewards(): Promise; - getTotalRewards(): Promise; - broadcastRawTransaction(transaction: string): Promise; - getSpendableBalance(operationType: number): Promise; - hasAsset(address: string, assetId: string): Promise; - isAmountValid(address: string, amount: string): Promise; -} - -declare class CoreAlgorandOperation { - getTransaction(): Promise; - getAlgorandOperationType(): Promise; - getAssetAmount(): Promise; - getRewards(): Promise; -} - -export type CoreCurrencySpecifics = Record; export type AlgorandResources = { rewards: BigNumber; @@ -107,11 +23,7 @@ export type AlgorandResourcesRaw = { nbAssets: number; }; export type AlgorandOperationMode = "send" | "optIn" | "claimReward"; -export type { - CoreAlgorandOperation, - CoreAlgorandAccount, - CoreAlgorandTransaction, -}; + export type AlgorandTransaction = TransactionCommon & { family: "algorand"; mode: AlgorandOperationMode; @@ -139,168 +51,3 @@ export type AlgorandExtraTxInfo = { memo?: string; assetId?: string; }; -export const reflect = ( - declare: (arg0: string, arg1: Spec) => void -): { - OperationMethods: { - asAlgorandOperation: { - returns: "AlgorandOperation"; - }; - }; - AccountMethods: { - asAlgorandAccount: { - returns: "AlgorandAccount"; - }; - }; -} => { - declare("AlgorandAccount", { - methods: { - createTransaction: { - returns: "AlgorandTransaction", - }, - broadcastRawTransaction: { - params: ["hex"], - }, - getFeeEstimate: { - params: ["AlgorandTransaction"], - returns: "Amount", - }, - getAssetsBalances: { - returns: ["AlgorandAssetAmount"], - }, - getPendingRewards: { - returns: "Amount", - }, - getTotalRewards: { - returns: "Amount", - }, - getSpendableBalance: { - returns: "Amount", - }, - isAmountValid: {}, - hasAsset: {}, - }, - }); - declare("AlgorandAssetAmount", { - njsUsesPlainObject: true, - methods: { - getAssetId: { - njsField: "assetId", - }, - getAmount: { - njsField: "amount", - }, - }, - }); - declare("AlgorandOperation", { - methods: { - getTransaction: { - returns: "AlgorandTransaction", - }, - getAlgorandOperationType: {}, - getAssetAmount: {}, - getRewards: {}, - }, - }); - declare("AlgorandTransaction", { - methods: { - getId: {}, - setSender: {}, - setFee: {}, - setNote: {}, - getType: {}, - setPaymentInfo: { - params: ["AlgorandPaymentInfo"], - }, - setAssetTransferInfo: { - params: ["AlgorandAssetTransferInfo"], - }, - serialize: { - returns: "hex", - }, - setSignature: { - params: ["hex"], - }, - getFee: {}, - getRound: {}, - getSender: {}, - getAssetTransferInfo: { - returns: "AlgorandAssetTransferInfo", - }, - getNote: {}, - }, - }); - declare("AlgorandPaymentInfo", { - njsUsesPlainObject: true, - statics: { - init: { - params: [null, null, null, null], - returns: "AlgorandPaymentInfo", - njsInstanciateClass: [ - { - recipientAddress: 0, - amount: 1, - closeAddress: 2, - closeAmount: 3, - }, - ], - }, - }, - }); - declare("AlgorandAddress", { - statics: { - fromPublicKey: { - params: ["hex"], - njsBuggyMethodIsNotStatic: true, - }, - }, - }); - declare("AlgorandAssetTransferInfo", { - njsUsesPlainObject: true, - statics: { - init: { - params: [null, null, null, null, null, null], - returns: "AlgorandAssetTransferInfo", - njsInstanciateClass: [ - { - assetId: 0, - amount: 1, - recipientAddress: 2, - closeAddress: 3, - clawedBackAddress: 4, - closeAmount: 5, - }, - ], - }, - }, - methods: { - getAssetId: { - njsField: "assetId", - }, - getAmount: { - njsField: "amount", - }, - getRecipientAddress: { - njsField: "recipientAddress", - }, - getCloseAddress: { - njsField: "closeAddress", - }, - getCloseAmount: { - njsField: "closeAmount", - }, - }, - }); - return { - OperationMethods: { - asAlgorandOperation: { - returns: "AlgorandOperation", - }, - }, - AccountMethods: { - asAlgorandAccount: { - returns: "AlgorandAccount", - }, - }, - }; -}; diff --git a/src/families/bitcoin/bridge/libcore.ts b/src/families/bitcoin/bridge/libcore.ts deleted file mode 100644 index 3634337365..0000000000 --- a/src/families/bitcoin/bridge/libcore.ts +++ /dev/null @@ -1,297 +0,0 @@ -import { getAbandonSeedAddress } from "@ledgerhq/cryptoassets"; -import { log } from "@ledgerhq/logs"; -import { BigNumber } from "bignumber.js"; -import invariant from "invariant"; -import { - AmountRequired, - FeeNotLoaded, - FeeRequired, - FeeTooHigh, - NotEnoughBalance, -} from "@ledgerhq/errors"; -import { LowerThanMinimumRelayFee } from "../../../errors"; -import { validateRecipient } from "../../../bridge/shared"; -import type { AccountBridge, CurrencyBridge } from "../../../types/bridge"; -import type { Account } from "../../../types/account"; -import type { Transaction, NetworkInfo } from "../types"; -import { sync } from "../../../libcore/syncAccount"; -import { scanAccounts } from "../../../libcore/scanAccounts"; -import { getAccountNetworkInfo } from "../../../libcore/getAccountNetworkInfo"; -import { getFeesForTransaction } from "../../../libcore/getFeesForTransaction"; -import { makeLRUCache } from "../../../cache"; -import broadcast from "../libcore-broadcast"; -import signOperation from "../libcore-signOperation"; -import { getMainAccount } from "../../../account"; -import { getMinRelayFee } from "../fees"; -import { isChangeOutput, perCoinLogic } from "../transaction"; -import { makeAccountBridgeReceive } from "../../../bridge/jsHelpers"; -import { requiresSatStackReady } from "../satstack"; -import * as explorerConfigAPI from "../../../api/explorerConfig"; -import Btc from "@ledgerhq/hw-app-btc/lib/BtcOld"; // use old implementation of Btc for libcore -import { getAddressWithBtcInstance } from "../hw-getAddress"; - -const receive = makeAccountBridgeReceive({ - injectGetAddressParams: (account) => { - const perCoin = perCoinLogic[account.currency.id]; - - if (perCoin && perCoin.injectGetAddressParams) { - return perCoin.injectGetAddressParams(account); - } - }, -}); - -const getCacheKey = (a, t) => - `${a.id}_${a.blockHeight || 0}_${t.amount.toString()}_${String( - t.useAllAmount - )}_${t.recipient}_${t.feePerByte ? t.feePerByte.toString() : ""}_${ - t.utxoStrategy.pickUnconfirmedRBF ? 1 : 0 - }_${t.utxoStrategy.strategy}_${String(t.rbf)}_${t.utxoStrategy.excludeUTXOs - .map(({ hash, outputIndex }) => `${hash}@${outputIndex}`) - .join("+")}`; - -const calculateFees = makeLRUCache(async (a, t) => { - return getFeesForTransaction({ - account: a, - transaction: t, - }); -}, getCacheKey); - -const createTransaction = (): Transaction => ({ - family: "bitcoin", - amount: new BigNumber(0), - utxoStrategy: { - strategy: 0, - pickUnconfirmedRBF: false, - excludeUTXOs: [], - }, - recipient: "", - rbf: false, - feePerByte: null, - networkInfo: null, - useAllAmount: false, - feesStrategy: "medium", -}); - -const updateTransaction = (t, patch) => { - const updatedT = { ...t, ...patch }; - - if (updatedT.recipient.toLowerCase().indexOf("bc1") === 0) { - updatedT.recipient = updatedT.recipient.toLowerCase(); - } - - return updatedT; -}; - -const estimateMaxSpendable = async ({ - account, - parentAccount, - transaction, -}) => { - const mainAccount = getMainAccount(account, parentAccount); - const t = await prepareTransaction(mainAccount, { - // worse case scenario using a legacy address - ...createTransaction(), - ...transaction, - recipient: - transaction?.recipient || getAbandonSeedAddress(mainAccount.currency.id), - useAllAmount: true, - }); - const s = await getTransactionStatus(mainAccount, t); - return s.amount; -}; - -const getTransactionStatus = async (a, t) => { - const errors: { - recipient?: Error; - feePerByte?: Error; - amount?: Error; - } = {}; - const warnings: { - recipient?: Error; - feePerByte?: Error; - feeTooHigh?: Error; - } = {}; - const useAllAmount = !!t.useAllAmount; - const { recipientError, recipientWarning } = await validateRecipient( - a.currency, - t.recipient - ); - - if (recipientError) { - errors.recipient = recipientError; - } - - if (recipientWarning) { - warnings.recipient = recipientWarning; - } - - let txInputs; - let txOutputs; - let estimatedFees = new BigNumber(0); - - if (!t.feePerByte) { - errors.feePerByte = new FeeNotLoaded(); - } else if (t.feePerByte.eq(0)) { - errors.feePerByte = new FeeRequired(); - } else if (!errors.recipient) { - await calculateFees(a, t).then( - (res) => { - txInputs = res.txInputs; - txOutputs = res.txOutputs; - estimatedFees = res.estimatedFees; - }, - (error) => { - if (error.name === "NotEnoughBalance") { - errors.amount = error; - } else { - throw error; - } - } - ); - } - - const sumOfInputs = (txInputs || []).reduce( - (sum, input) => sum.plus(input.value), - new BigNumber(0) - ); - const sumOfChanges = (txOutputs || []) - .filter(isChangeOutput) - .reduce((sum, output) => sum.plus(output.value), new BigNumber(0)); - - if (txInputs) { - log("bitcoin", `${txInputs.length} inputs, sum: ${sumOfInputs.toString()}`); - } - - if (txOutputs) { - log( - "bitcoin", - `${txOutputs.length} outputs, sum of changes: ${sumOfChanges.toString()}` - ); - } - - const totalSpent = sumOfInputs.minus(sumOfChanges); - const amount = useAllAmount ? totalSpent.minus(estimatedFees) : t.amount; - log( - "bitcoin", - `totalSpent ${totalSpent.toString()} amount ${amount.toString()}` - ); - - if (!errors.amount && !amount.gt(0)) { - errors.amount = useAllAmount - ? new NotEnoughBalance() - : new AmountRequired(); - } - - if ( - process.env.EXPERIMENTAL_MIN_RELAY_FEE && - estimatedFees.gt(0) && - estimatedFees.lt(getMinRelayFee(a.currency)) - ) { - warnings.feePerByte = new LowerThanMinimumRelayFee(); - } else if (amount.gt(0) && estimatedFees.times(10).gt(amount)) { - warnings.feeTooHigh = new FeeTooHigh(); - } - - return Promise.resolve({ - errors, - warnings, - estimatedFees, - amount, - totalSpent, - txInputs, - txOutputs, - }); -}; - -const inferFeePerByte = (t: Transaction, networkInfo: NetworkInfo) => { - if (t.feesStrategy) { - const speed = networkInfo.feeItems.items.find( - (item) => t.feesStrategy === item.speed - ); - - if (!speed) { - return networkInfo.feeItems.defaultFeePerByte; - } - - return speed.feePerByte; - } - - return t.feePerByte || networkInfo.feeItems.defaultFeePerByte; -}; - -const prepareTransaction = async ( - a: Account, - t: Transaction -): Promise => { - if (a.currency.id === "bitcoin") { - await requiresSatStackReady(); - } - - let networkInfo = t.networkInfo; - - if (!networkInfo) { - networkInfo = await getAccountNetworkInfo(a); - invariant( - networkInfo && networkInfo.family === "bitcoin", - "bitcoin networkInfo expected" - ); - } - - const feePerByte = inferFeePerByte(t, networkInfo as NetworkInfo); - - if ( - t.networkInfo === networkInfo && - (feePerByte === t.feePerByte || feePerByte.eq(t.feePerByte || 0)) // nothing changed - ) { - return t; - } - - return { ...t, networkInfo, feePerByte }; -}; - -const preload = async () => { - const explorerConfig = await explorerConfigAPI.preload(); - return { - explorerConfig, - }; -}; - -const hydrate = (maybeConfig: any) => { - if (maybeConfig && maybeConfig.explorerConfig) { - explorerConfigAPI.hydrate(maybeConfig.explorerConfig); - } -}; - -const currencyBridge: CurrencyBridge = { - scanAccounts: (arg) => - scanAccounts({ - ...arg, - getAddressFn: (transport) => { - const btc = new Btc(transport); - return (opts) => getAddressWithBtcInstance(transport, btc, opts); - }, - }), - preload, - hydrate, -}; -const accountBridge: AccountBridge = { - estimateMaxSpendable, - createTransaction, - updateTransaction, - prepareTransaction, - getTransactionStatus, - receive, - sync, - signOperation, - broadcast: async ({ account, signedOperation }) => { - calculateFees.reset(); - return broadcast({ - account, - signedOperation, - }); - }, -}; -export default { - currencyBridge, - accountBridge, -}; diff --git a/src/families/bitcoin/customAddressValidation.ts b/src/families/bitcoin/customAddressValidation.ts deleted file mode 100644 index 211d12cf68..0000000000 --- a/src/families/bitcoin/customAddressValidation.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { InvalidAddress } from "@ledgerhq/errors"; -import type { Core } from "../../libcore/types"; -import type { CryptoCurrency } from "../../types"; -import { perCoinLogic } from "./transaction"; -export default async ( - core: Core, - arg: { - currency: CryptoCurrency; - recipient: string; - } -): Promise => { - const { currency } = arg; - const perCoin = perCoinLogic[currency.id]; - const recipient = perCoin?.asLibcoreTransactionRecipient - ? perCoin.asLibcoreTransactionRecipient(arg.recipient) - : arg.recipient; - const poolInstance = core.getPoolInstance(); - const currencyCore = await poolInstance.getCurrency(currency.id); - const value = await core.Address.isValid(recipient, currencyCore); - - if (value) { - return Promise.resolve(null); - } - - return Promise.reject( - new InvalidAddress(undefined, { - currencyName: currency.name, - }) - ); -}; diff --git a/src/families/bitcoin/js-signOperation.ts b/src/families/bitcoin/js-signOperation.ts index f4f3939f74..aebcfc5b98 100644 --- a/src/families/bitcoin/js-signOperation.ts +++ b/src/families/bitcoin/js-signOperation.ts @@ -48,11 +48,6 @@ const signOperation = ({ fee = res.fees; }); - // FIXME (legacy) - // should be `transaction.getLockTime()` as soon as lock time is - // handled by libcore (actually: it always returns a default value - // and that caused issue with zcash (see #904)) - // cf. https://github.com/LedgerHQ/lib-ledger-core/blob/fc9d762b83fc2b269d072b662065747a64ab2816/core/src/wallet/bitcoin/transaction_builders/BitcoinLikeUtxoPicker.cpp#L156-L159 let lockTime; // (legacy) Set lockTime for Komodo to enable reward claiming on UTXOs created by diff --git a/src/families/bitcoin/libcore-broadcast.ts b/src/families/bitcoin/libcore-broadcast.ts deleted file mode 100644 index ff28736d1a..0000000000 --- a/src/families/bitcoin/libcore-broadcast.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { patchOperationWithHash } from "../../operation"; -import { makeBroadcast } from "../../libcore/broadcast"; - -async function broadcast({ - coreAccount, - signedOperation: { signature, operation }, -}) { - const bitcoinLikeAccount = await coreAccount.asBitcoinLikeAccount(); - const txHash = await bitcoinLikeAccount.broadcastRawTransaction(signature); - return patchOperationWithHash(operation, txHash); -} - -export default makeBroadcast({ - broadcast, -}); diff --git a/src/families/bitcoin/libcore-buildOperation.ts b/src/families/bitcoin/libcore-buildOperation.ts deleted file mode 100644 index 0f470b45c0..0000000000 --- a/src/families/bitcoin/libcore-buildOperation.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { CryptoCurrency, Account, Operation } from "../../types"; -import type { CoreOperation } from "../../libcore/types"; -import { perCoinLogic } from "./transaction"; - -async function bitcoinBuildOperation( - { - coreOperation, - existingAccount, - currency, - }: { - coreOperation: CoreOperation; - existingAccount: Account | null | undefined; - currency: CryptoCurrency; - }, - partialOp: Operation -): Promise> { - const bitcoinLikeOperation = await coreOperation.asBitcoinLikeOperation(); - const bitcoinLikeTransaction = await bitcoinLikeOperation.getTransaction(); - const hash = await bitcoinLikeTransaction.getHash(); - const shape: Partial = { - hash, - }; - const perCoin = perCoinLogic[currency.id]; - - if (perCoin && perCoin.syncReplaceAddress) { - const { syncReplaceAddress } = perCoin; - shape.senders = partialOp.senders.map((addr) => - syncReplaceAddress(existingAccount, addr) - ); - shape.recipients = partialOp.recipients.map((addr) => - syncReplaceAddress(existingAccount, addr) - ); - } - - return shape; -} - -export default bitcoinBuildOperation; diff --git a/src/families/bitcoin/libcore-buildTransaction.ts b/src/families/bitcoin/libcore-buildTransaction.ts deleted file mode 100644 index 07e7f57eea..0000000000 --- a/src/families/bitcoin/libcore-buildTransaction.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { log } from "@ledgerhq/logs"; -import { BigNumber } from "bignumber.js"; -import { FeeNotLoaded, InvalidAddress } from "@ledgerhq/errors"; -import type { Account } from "../../types"; -import { isValidRecipient } from "../../libcore/isValidRecipient"; -import { bigNumberToLibcoreAmount } from "../../libcore/buildBigNumber"; -import type { Core, CoreCurrency, CoreAccount } from "../../libcore/types"; -import type { CoreBitcoinLikeTransaction, Transaction } from "./types"; -import { getUTXOStatus } from "./transaction"; -import { promiseAllBatched } from "../../promise"; -import { parseBitcoinUTXO, perCoinLogic } from "./transaction"; - -async function bitcoinBuildTransaction({ - account, - core, - coreAccount, - coreCurrency, - transaction, - isPartial, - isCancelled, -}: { - account: Account; - core: Core; - coreAccount: CoreAccount; - coreCurrency: CoreCurrency; - transaction: Transaction; - isPartial: boolean; - isCancelled: () => boolean; -}): Promise { - const { currency } = account; - const perCoin = perCoinLogic[currency.id]; - const recipient = perCoin?.asLibcoreTransactionRecipient - ? perCoin.asLibcoreTransactionRecipient(transaction.recipient) - : transaction.recipient; - const bitcoinLikeAccount = await coreAccount.asBitcoinLikeAccount(); - const isValid = await isValidRecipient({ - currency, - recipient, - }); - - if (isValid !== null) { - throw new InvalidAddress("", { - currencyName: currency.name, - }); - } - - const { feePerByte } = transaction; - if (!feePerByte) throw new FeeNotLoaded(); - const fees = await bigNumberToLibcoreAmount( - core, - coreCurrency, - new BigNumber(feePerByte) - ); - if (isCancelled()) return; - const transactionBuilder = await bitcoinLikeAccount.buildTransaction( - isPartial - ); - if (isCancelled()) return; - const { utxoStrategy } = transaction; - - if (transaction.useAllAmount) { - await transactionBuilder.wipeToAddress(recipient); - if (isCancelled()) return; - } - - const count = await bitcoinLikeAccount.getUTXOCount(); - const objects = await bitcoinLikeAccount.getUTXO(0, count); - let utxos = await promiseAllBatched(6, objects, parseBitcoinUTXO); - - if (perCoin) { - const { syncReplaceAddress } = perCoin; - - if (syncReplaceAddress) { - utxos = utxos.map((u) => ({ - ...u, - address: u.address && syncReplaceAddress(account, u.address), - })); - } - } - - for (const utxo of utxos) { - const s = getUTXOStatus(utxo, utxoStrategy); - - if (s.excluded) { - log( - "bitcoin", - `excludeUTXO ${utxo.hash}@${utxo.outputIndex} (${s.reason})` - ); - await transactionBuilder.excludeUtxo(utxo.hash, utxo.outputIndex); - } - } - - if (!transaction.useAllAmount) { - if (!transaction.amount) throw new Error("amount is missing"); - const amount = await bigNumberToLibcoreAmount( - core, - coreCurrency, - new BigNumber(transaction.amount) - ); - if (isCancelled()) return; - await transactionBuilder.sendToAddress(amount, recipient); - if (isCancelled()) return; - } - - await transactionBuilder.pickInputs( - utxoStrategy.strategy, - 0 - /* not used, out of int32 range issue. patched in signature time. */ - ); - if (isCancelled()) return; - await transactionBuilder.setFeesPerByte(fees); - if (isCancelled()) return; - const builded = await transactionBuilder.build(); - if (isCancelled()) return; - return builded; -} - -export default bitcoinBuildTransaction; diff --git a/src/families/bitcoin/libcore-getAccountNetworkInfo.test.ts b/src/families/bitcoin/libcore-getAccountNetworkInfo.test.ts deleted file mode 100644 index 653fc91fdf..0000000000 --- a/src/families/bitcoin/libcore-getAccountNetworkInfo.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { avoidDups } from "./libcore-getAccountNetworkInfo"; -import { BigNumber } from "bignumber.js"; -test("avoidFeeDupsInFeePerByte [3,2,2] => [4,3,2]", async () => { - expect( - avoidDups([new BigNumber(3), new BigNumber(3), new BigNumber(2)]) - ).toEqual([new BigNumber(4), new BigNumber(3), new BigNumber(2)]); -}); -test("avoidFeeDupsInFeePerByte [2,2,1] => [3,2,1]", async () => { - expect( - avoidDups([new BigNumber(2), new BigNumber(2), new BigNumber(1)]) - ).toEqual([new BigNumber(3), new BigNumber(2), new BigNumber(1)]); -}); -test("avoidFeeDupsInFeePerByte [3,3,3] => [5,4,3]", async () => { - expect( - avoidDups([new BigNumber(3), new BigNumber(3), new BigNumber(3)]) - ).toEqual([new BigNumber(5), new BigNumber(4), new BigNumber(3)]); -}); -test("avoidFeeDupsInFeePerByte [3,2,1] => [3,2,1]", async () => { - expect( - avoidDups([new BigNumber(3), new BigNumber(2), new BigNumber(1)]) - ).toEqual([new BigNumber(3), new BigNumber(2), new BigNumber(1)]); -}); -test("avoidFeeDupsInFeePerByte [100,100,1] => [101,100,1]", async () => { - expect( - avoidDups([new BigNumber(100), new BigNumber(100), new BigNumber(1)]) - ).toEqual([new BigNumber(101), new BigNumber(100), new BigNumber(1)]); -}); diff --git a/src/families/bitcoin/libcore-getAccountNetworkInfo.ts b/src/families/bitcoin/libcore-getAccountNetworkInfo.ts deleted file mode 100644 index 71876d221f..0000000000 --- a/src/families/bitcoin/libcore-getAccountNetworkInfo.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { BigNumber } from "bignumber.js"; -import type { NetworkInfo } from "./types"; -import type { Account } from "../../types"; -import type { CoreAccount } from "../../libcore/types"; -import { promiseAllBatched } from "../../promise"; -import { libcoreBigIntToBigNumber } from "../../libcore/buildBigNumber"; -type Input = { - coreAccount: CoreAccount; - account: Account; -}; -const speeds = ["fast", "medium", "slow"]; -export function avoidDups(nums: Array): Array { - nums = nums.slice(0); - - for (let i = nums.length - 2; i >= 0; i--) { - if (nums[i + 1].gte(nums[i])) { - nums[i] = nums[i + 1].plus(1); - } - } - - return nums; -} - -async function bitcoin({ coreAccount }: Input): Promise { - const bitcoinLikeAccount = await coreAccount.asBitcoinLikeAccount(); - const bigInts = await bitcoinLikeAccount.getFees(); - const bigNumbers = await promiseAllBatched( - 10, - bigInts, - libcoreBigIntToBigNumber - ); - const normalized = avoidDups( - bigNumbers.map((bn) => bn.div(1000).integerValue(BigNumber.ROUND_CEIL)) - ); - const feeItems = { - items: normalized.map((feePerByte, i) => ({ - key: String(i), - speed: speeds[i], - feePerByte, - })), - defaultFeePerByte: - normalized[Math.floor(normalized.length / 2)] || new BigNumber(0), - }; - return { - family: "bitcoin", - feeItems, - }; -} - -export default bitcoin; diff --git a/src/families/bitcoin/libcore-getFeesForTransaction.ts b/src/families/bitcoin/libcore-getFeesForTransaction.ts deleted file mode 100644 index cd83d5f71a..0000000000 --- a/src/families/bitcoin/libcore-getFeesForTransaction.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { BigNumber } from "bignumber.js"; -import { libcoreAmountToBigNumber } from "../../libcore/buildBigNumber"; -import buildTransaction from "./libcore-buildTransaction"; -import { - parseBitcoinOutput, - parseBitcoinInput, - perCoinLogic, -} from "./transaction"; -import { promiseAllBatched } from "../../promise"; -import type { BitcoinInput, BitcoinOutput } from "./types"; - -async function bitcoin(arg: any): Promise<{ - estimatedFees: BigNumber; - value: BigNumber; - txInputs: BitcoinInput[]; - txOutputs: BitcoinOutput[]; -} | void> { - const builded = await buildTransaction(arg); - if (!builded) return; - const feesAmount = await builded.getFees(); - - if (!feesAmount) { - throw new Error("getFeesForTransaction: fees should not be undefined"); - } - - const estimatedFees = await libcoreAmountToBigNumber(feesAmount); - // TODO we don't have a getValue on bitcoin - const value = new BigNumber(0); - const inputs = await builded.getInputs(); - let txInputs: BitcoinInput[] = await promiseAllBatched( - 4, - inputs, - parseBitcoinInput - ); - const outputs = await builded.getOutputs(); - let txOutputs: BitcoinOutput[] = await promiseAllBatched( - 4, - outputs, - parseBitcoinOutput - ); - const { account } = arg; - const perCoin = perCoinLogic[account.currency.id]; - - if (perCoin) { - const { syncReplaceAddress } = perCoin; - - if (syncReplaceAddress) { - txInputs = txInputs.map((i) => ({ - ...i, - address: syncReplaceAddress(account, i.address as string), - })); - txOutputs = txOutputs.map((o) => ({ - ...o, - address: o.address && syncReplaceAddress(account, o.address), - })); - } - } - - return { - estimatedFees, - value, - txInputs, - txOutputs, - }; -} - -export default bitcoin; diff --git a/src/families/bitcoin/libcore-postBuildAccount.ts b/src/families/bitcoin/libcore-postBuildAccount.ts deleted file mode 100644 index 9c3ead1ff1..0000000000 --- a/src/families/bitcoin/libcore-postBuildAccount.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { log } from "@ledgerhq/logs"; -import type { Account } from "../../types"; -import type { BitcoinResources } from "./types"; -import type { CoreAccount } from "../../libcore/types"; -import { SatStackDescriptorNotImported } from "../../errors"; -import { promiseAllBatched } from "../../promise"; -import { parseBitcoinUTXO, perCoinLogic } from "./transaction"; -import { isSatStackEnabled, checkDescriptorExists } from "./satstack"; -import { inferDescriptorFromAccount } from "./descriptor"; - -const postBuildAccount = async ({ - account, - coreAccount, -}: { - account: Account; - coreAccount: CoreAccount; -}): Promise => { - if (isSatStackEnabled() && account.currency.id === "bitcoin") { - const inferred = inferDescriptorFromAccount(account); - - if (inferred) { - const exists = await checkDescriptorExists(inferred.internal); - - if (!exists) { - throw new SatStackDescriptorNotImported(); - } - } - } - - log("bitcoin/post-buildAccount", "bitcoinResources"); - const bitcoinLikeAccount = await coreAccount.asBitcoinLikeAccount(); - const count = await bitcoinLikeAccount.getUTXOCount(); - const objects = await bitcoinLikeAccount.getUTXO(0, count); - const utxos = await promiseAllBatched(6, objects, parseBitcoinUTXO); - const perCoin = perCoinLogic[account.currency.id]; - let bitcoinResources: BitcoinResources = { - ...account.bitcoinResources, - utxos, - walletAccount: undefined, - }; - - if (perCoin) { - if (perCoin.postBuildBitcoinResources) { - bitcoinResources = perCoin.postBuildBitcoinResources( - account, - bitcoinResources - ); - } - - const { syncReplaceAddress } = perCoin; - - if (syncReplaceAddress) { - account.freshAddress = syncReplaceAddress(account, account.freshAddress); - account.freshAddresses = account.freshAddresses.map((a) => ({ - ...a, - address: syncReplaceAddress(account, a.address), - })); - bitcoinResources.utxos = bitcoinResources.utxos.map((u) => ({ - ...u, - address: u.address && syncReplaceAddress(account, u.address), - })); - } - } - - account.bitcoinResources = bitcoinResources; - log("bitcoin/post-buildAccount", "bitcoinResources DONE"); - return account; -}; - -export default postBuildAccount; diff --git a/src/families/bitcoin/libcore-signOperation.ts b/src/families/bitcoin/libcore-signOperation.ts deleted file mode 100644 index 9a03dbaec1..0000000000 --- a/src/families/bitcoin/libcore-signOperation.ts +++ /dev/null @@ -1,227 +0,0 @@ -import { BigNumber } from "bignumber.js"; -import Btc from "@ledgerhq/hw-app-btc"; -import { log } from "@ledgerhq/logs"; -import { isSegwitDerivationMode } from "../../derivation"; -import type { - CoreBitcoinLikeTransaction, - CoreBitcoinLikeInput, - CoreBitcoinLikeOutput, - Transaction, -} from "./types"; -import type { Operation } from "../../types"; -import { promiseAllBatched } from "../../promise"; -import { libcoreAmountToBigNumber } from "../../libcore/buildBigNumber"; -import { makeSignOperation } from "../../libcore/signOperation"; -import buildTransaction from "./libcore-buildTransaction"; -import { - parseBitcoinOutput, - isChangeOutput, - perCoinLogic, -} from "./transaction"; - -async function signTransaction({ - account, - isCancelled, - transport, - currency, - coreCurrency, - transaction, - coreTransaction, - derivationMode, - onDeviceStreaming, - onDeviceSignatureRequested, - onDeviceSignatureGranted, -}) { - log("hw", `signTransaction ${currency.id} for account ${account.id}`); - const perCoin = perCoinLogic[currency.id]; - const networkParams = await coreCurrency.getBitcoinLikeNetworkParameters(); - if (isCancelled()) return; - const sigHashTypeHex = await networkParams.getSigHash(); - const sigHashType = parseInt(sigHashTypeHex, 16); - - if (isNaN(sigHashType)) { - throw new Error("sigHashType should not be NaN"); - } - - if (isCancelled()) return; - const hasTimestamp = await networkParams.getUsesTimestampedTransaction(); - if (isCancelled()) return; - const hwApp = new Btc(transport); - let additionals = [currency.id]; - - if (account.derivationMode === "native_segwit") { - additionals.push("bech32"); - } - - if (perCoin?.getAdditionals) { - additionals = additionals.concat( - perCoin.getAdditionals({ - transaction, - }) - ); - } - - const expiryHeight = perCoin?.hasExpiryHeight - ? Buffer.from([0x00, 0x00, 0x00, 0x00]) - : undefined; - const hasExtraData = perCoin?.hasExtraData || false; - const rawInputs: CoreBitcoinLikeInput[] = await coreTransaction.getInputs(); - if (isCancelled()) return; - const inputs: any[] = await promiseAllBatched( - 5, - rawInputs, - async (input, i) => { - const hexPreviousTransaction = await input.getPreviousTransaction(); - log("libcore", "splitTransaction " + String(hexPreviousTransaction)); - // v1 of XST txs have timestamp but not v2 - const inputHasTimestamp = - (currency.id === "stealthcoin" && - hexPreviousTransaction.slice(0, 2) === "01") || - hasTimestamp; - log("hw", `splitTransaction`, { - hexPreviousTransaction, - supportsSegwit: currency.supportsSegwit, - inputHasTimestamp, - hasExtraData, - additionals, - }); - const previousTransaction = hwApp.splitTransaction( - hexPreviousTransaction, - currency.supportsSegwit, - inputHasTimestamp, - hasExtraData, - additionals - ); - const outputIndex = await input.getPreviousOutputIndex(); - // NB libcore's sequence is not used because int32 limit issue - const sequence = transaction.rbf ? 0 : 0xffffffff; - log("libcore", "inputs[" + i + "]", { - previousTransaction: JSON.stringify(previousTransaction), - outputIndex, - sequence, - }); - return [ - previousTransaction, - outputIndex, - undefined, // we don't use that TODO: document - sequence, - ]; - } - ); - if (isCancelled()) return; - const associatedKeysets = await promiseAllBatched( - 5, - rawInputs, - async (input) => { - const derivationPaths = await input.getDerivationPath(); - const [first] = derivationPaths; - if (!first) throw new Error("unexpected empty derivationPaths"); - const r = await first.toString(); - return r; - } - ); - if (isCancelled()) return; - const outputs: CoreBitcoinLikeOutput[] = await coreTransaction.getOutputs(); - if (isCancelled()) return; - let changePath; - - for (const o of outputs) { - const output = await parseBitcoinOutput(o); - - if (isChangeOutput(output)) { - changePath = output.path || undefined; - } - } - - const outputScriptHex = await coreTransaction.serializeOutputs(); - if (isCancelled()) return; - const initialTimestamp = hasTimestamp - ? await coreTransaction.getTimestamp() - : undefined; - if (isCancelled()) return; - // FIXME - // should be `transaction.getLockTime()` as soon as lock time is - // handled by libcore (actually: it always returns a default value - // and that caused issue with zcash (see #904)) - let lockTime; - - // Set lockTime for Komodo to enable reward claiming on UTXOs created by - // Ledger Live. We should only set this if the currency is Komodo and - // lockTime isn't already defined. - if (currency.id === "komodo" && lockTime === undefined) { - const unixtime = Math.floor(Date.now() / 1000); - lockTime = unixtime - 777; - } - - log("hw", `createPaymentTransactionNew`, { - associatedKeysets, - changePath, - outputScriptHex, - lockTime, - sigHashType, - segwit: isSegwitDerivationMode(derivationMode), - initialTimestamp: initialTimestamp || undefined, - additionals, - expiryHeight: expiryHeight && expiryHeight.toString("hex"), - }); - const signature = await hwApp.createPaymentTransactionNew({ - inputs, - associatedKeysets, - changePath, - outputScriptHex, - lockTime, - sigHashType, - segwit: isSegwitDerivationMode(derivationMode), - initialTimestamp: initialTimestamp || undefined, - additionals, - expiryHeight, - onDeviceSignatureGranted, - onDeviceSignatureRequested, - onDeviceStreaming, - }); - const sendersInput = await coreTransaction.getInputs(); - const senders = ( - await promiseAllBatched(5, sendersInput, (senderInput: any) => - senderInput.getAddress() - ) - ).filter(Boolean) as string[]; - const recipientsOutput = await coreTransaction.getOutputs(); - const recipients = ( - await promiseAllBatched(5, recipientsOutput, (recipientOutput: any) => - recipientOutput.getAddress() - ) - ).filter(Boolean) as string[]; - const coreAmountFees = await coreTransaction.getFees(); - - if (!coreAmountFees) { - throw new Error("signAndBroadcast: fees should not be undefined"); - } - - const fee = await libcoreAmountToBigNumber(coreAmountFees); - const txHash = ""; // will be resolved in broadcast() - - const operation: Operation = { - id: `${account.id}-${txHash}-OUT`, - hash: txHash, - type: "OUT", - value: new BigNumber(transaction.amount).plus(fee), - fee, - blockHash: null, - blockHeight: null, - senders, - recipients, - accountId: account.id, - date: new Date(), - extra: {}, - }; - return { - operation, - expirationDate: null, - signature, - }; -} - -export default makeSignOperation({ - buildTransaction, - signTransaction, -}); diff --git a/src/families/bitcoin/logic.ts b/src/families/bitcoin/logic.ts index d7c682e2fc..d504bd69ff 100644 --- a/src/families/bitcoin/logic.ts +++ b/src/families/bitcoin/logic.ts @@ -9,11 +9,7 @@ import type { Transaction, NetworkInfo, UtxoStrategy, - CoreBitcoinLikeOutput, - CoreBitcoinLikeInput, - BitcoinInput, } from "./types"; -import { libcoreAmountToBigNumber } from "../../libcore/buildBigNumber"; // correspond ~ to min relay fees but determined empirically for a tx to be accepted by network const minFees = { @@ -192,67 +188,3 @@ export const perCoinLogic: Record< }), }, }; - -// vvvvv DEPRECATED - used only by legacy libcore implementation vvvvv - -export async function parseBitcoinInput( - input: CoreBitcoinLikeInput -): Promise { - const address = await input.getAddress(); - const rawValue = await input.getValue(); - const value = rawValue ? await libcoreAmountToBigNumber(rawValue) : null; - const previousTxHash = await input.getPreviousTxHash(); - const previousOutputIndex = await input.getPreviousOutputIndex(); - return { - address, - value, - previousTxHash, - previousOutputIndex, - }; -} - -export async function parseBitcoinOutput( - output: CoreBitcoinLikeOutput -): Promise { - let blockHeight = await output.getBlockHeight(); - - if (!blockHeight || blockHeight < 0) { - blockHeight = undefined; - } - - const hash = await output.getTransactionHash(); - const outputIndex = await output.getOutputIndex(); - const address = await output.getAddress(); - const derivationPath = await output.getDerivationPath(); - let path; - - if (derivationPath) { - const isDerivationPathNull = await derivationPath.isNull(); - - if (!isDerivationPathNull) { - path = await derivationPath.toString(); - } - } - - const value = await libcoreAmountToBigNumber(await output.getValue()); - const rbf = false; // this is unsafe to generically call this at the moment. libcore segfault. - - return { - hash, - outputIndex, - blockHeight, - address, - isChange: false, - path, - value, - rbf, - }; -} - -export async function parseBitcoinUTXO( - output: CoreBitcoinLikeOutput -): Promise { - const utxo = await parseBitcoinOutput(output); - utxo.rbf = await output.isReplaceable(); - return utxo; -} diff --git a/src/families/bitcoin/networks.ts b/src/families/bitcoin/networks.ts index 89678ebab8..a928403acd 100644 --- a/src/families/bitcoin/networks.ts +++ b/src/families/bitcoin/networks.ts @@ -173,7 +173,7 @@ export const getNetworkParameters = ( dustAmount: new BigNumber(10000), messagePrefix: "StealthCoin Signed Message:\n", usesTimestampedTransaction: false, - // Used to depend on "version", cf. https://github.com/LedgerHQ/lib-ledger-core/blob/fc9d762b83fc2b269d072b662065747a64ab2816/core/src/wallet/bitcoin/networks.cpp#L250 + // Used to depend on "version" timestampDelay: new BigNumber(15), sigHash: BitcoinLikeSigHashType.SIGHASH_ALL, additionalBIPs: [], diff --git a/src/families/bitcoin/transaction.ts b/src/families/bitcoin/transaction.ts index 37e1d0134e..d9a824dc7e 100644 --- a/src/families/bitcoin/transaction.ts +++ b/src/families/bitcoin/transaction.ts @@ -8,9 +8,6 @@ import type { FeeItemsRaw, BitcoinOutput, UtxoStrategy, - CoreBitcoinLikeOutput, - CoreBitcoinLikeInput, - BitcoinInput, BitcoinResources, } from "./types"; import type { Account } from "../../types"; @@ -23,7 +20,6 @@ import { import { getAccountUnit } from "../../account"; import { formatCurrencyUnit } from "../../currencies"; import type { CryptoCurrencyIds } from "../../types"; -import { libcoreAmountToBigNumber } from "../../libcore/buildBigNumber"; const fromFeeItemsRaw = (fir: FeeItemsRaw): FeeItems => ({ items: fir.items.map((fi) => ({ @@ -208,65 +204,7 @@ export type UTXOStatus = | { excluded: false; }; -export async function parseBitcoinInput( - input: CoreBitcoinLikeInput -): Promise { - const address = await input.getAddress(); - const rawValue = await input.getValue(); - const value = rawValue ? await libcoreAmountToBigNumber(rawValue) : null; - const previousTxHash = await input.getPreviousTxHash(); - const previousOutputIndex = await input.getPreviousOutputIndex(); - return { - address, - value, - previousTxHash, - previousOutputIndex, - }; -} -export async function parseBitcoinOutput( - output: CoreBitcoinLikeOutput -): Promise { - let blockHeight = await output.getBlockHeight(); - - if (!blockHeight || blockHeight < 0) { - blockHeight = undefined; - } - - const hash = await output.getTransactionHash(); - const outputIndex = await output.getOutputIndex(); - const address = await output.getAddress(); - const derivationPath = await output.getDerivationPath(); - let path; - if (derivationPath) { - const isDerivationPathNull = await derivationPath.isNull(); - - if (!isDerivationPathNull) { - path = await derivationPath.toString(); - } - } - - const value = await libcoreAmountToBigNumber(await output.getValue()); - const rbf = false; // this is unsafe to generically call this at the moment. libcore segfault. - - return { - hash, - outputIndex, - blockHeight, - address, - isChange: false, - path, - value, - rbf, - }; -} -export async function parseBitcoinUTXO( - output: CoreBitcoinLikeOutput -): Promise { - const utxo = await parseBitcoinOutput(output); - utxo.rbf = await output.isReplaceable(); - return utxo; -} export function getUTXOStatus( utxo: BitcoinOutput, utxoStrategy: UtxoStrategy diff --git a/src/families/bitcoin/types.ts b/src/families/bitcoin/types.ts index c2f66ba991..a993669e9b 100644 --- a/src/families/bitcoin/types.ts +++ b/src/families/bitcoin/types.ts @@ -8,12 +8,6 @@ import type { TransactionCommon, TransactionCommonRaw, } from "../../types/transaction"; -import type { - CoreBigInt, - CoreAmount, - CoreDerivationPath, - Spec, -} from "../../libcore/types"; export type BitcoinInput = { address: string | null | undefined; @@ -154,220 +148,3 @@ export type TransactionRaw = TransactionCommonRaw & { feePerByte: string | null | undefined; networkInfo: NetworkInfoRaw | null | undefined; }; - -// vvvvv DEPRECATED - used only by legacy libcore implementation vvvvv - -declare class CoreBitcoinLikeInput { - getPreviousTransaction(): Promise; - getPreviousTxHash(): Promise; - getPreviousOutputIndex(): Promise; - getValue(): Promise; - getSequence(): Promise; - getDerivationPath(): Promise; - getAddress(): Promise; -} - -declare class CoreBitcoinLikeOutput { - getTransactionHash(): Promise; - getOutputIndex(): Promise; - getValue(): Promise; - getBlockHeight(): Promise; - getDerivationPath(): Promise; - getAddress(): Promise; - isReplaceable(): Promise; -} - -declare class CoreBitcoinLikeTransaction { - getHash(): Promise; - getFees(): Promise; - getInputs(): Promise; - getOutputs(): Promise; - serializeOutputs(): Promise; - getTimestamp(): Promise; -} - -declare class CoreBitcoinLikeOperation { - getTransaction(): Promise; -} - -declare class CoreBitcoinLikeTransactionBuilder { - wipeToAddress(address: string): Promise; - sendToAddress(amount: CoreAmount, recipient: string): Promise; - excludeUtxo(transactionHash: string, outputIndex: number): Promise; - pickInputs(arg0: number, arg1: number): Promise; - setFeesPerByte(feesPerByte: CoreAmount): Promise; - build(): Promise; -} - -declare class CoreBitcoinLikeAccount { - getUTXO(from: number, to: number): Promise; - getUTXOCount(): Promise; - buildTransaction( - isPartial: boolean - ): Promise; - broadcastRawTransaction(signed: string): Promise; - getFees(): Promise; -} - -declare class CoreBitcoinLikeNetworkParameters { - getSigHash(): Promise; - getUsesTimestampedTransaction(): Promise; -} - -export type CoreStatics = { - BitcoinLikeAccount: CoreBitcoinLikeAccount; - BitcoinLikeInput: CoreBitcoinLikeInput; - BitcoinLikeNetworkParameters: CoreBitcoinLikeNetworkParameters; - BitcoinLikeOperation: CoreBitcoinLikeOperation; - BitcoinLikeOutput: CoreBitcoinLikeOutput; - BitcoinLikeTransaction: CoreBitcoinLikeTransaction; - BitcoinLikeTransactionBuilder: CoreBitcoinLikeTransactionBuilder; -}; -export type { - CoreBitcoinLikeAccount, - CoreBitcoinLikeInput, - CoreBitcoinLikeNetworkParameters, - CoreBitcoinLikeOperation, - CoreBitcoinLikeOutput, - CoreBitcoinLikeTransaction, - CoreBitcoinLikeTransactionBuilder, -}; -export type CoreAccountSpecifics = { - asBitcoinLikeAccount(): Promise; -}; -export type CoreOperationSpecifics = { - asBitcoinLikeOperation(): Promise; -}; -export type CoreCurrencySpecifics = { - getBitcoinLikeNetworkParameters(): Promise; -}; - -export const reflect = ( - declare: (arg0: string, arg1: Spec) => void -): { - OperationMethods: { - asBitcoinLikeOperation: { - returns: "BitcoinLikeOperation"; - }; - }; - AccountMethods: { - asBitcoinLikeAccount: { - returns: "BitcoinLikeAccount"; - }; - }; -} => { - declare("BitcoinLikeInput", { - methods: { - getPreviousTransaction: { - returns: "hex", - }, - getPreviousTxHash: {}, - getValue: { - returns: "Amount", - }, - getPreviousOutputIndex: {}, - getSequence: {}, - getDerivationPath: { - returns: ["DerivationPath"], - }, - getAddress: {}, - }, - }); - declare("BitcoinLikeOutput", { - methods: { - getTransactionHash: {}, - getOutputIndex: {}, - getValue: { - returns: "Amount", - }, - getBlockHeight: {}, - getDerivationPath: { - returns: "DerivationPath", - }, - getAddress: {}, - isReplaceable: {}, - }, - }); - declare("BitcoinLikeTransaction", { - methods: { - getHash: {}, - getFees: { - returns: "Amount", - }, - getInputs: { - returns: ["BitcoinLikeInput"], - }, - getOutputs: { - returns: ["BitcoinLikeOutput"], - }, - serializeOutputs: { - returns: "hex", - }, - getTimestamp: {}, - }, - }); - declare("BitcoinLikeOperation", { - methods: { - getTransaction: { - returns: "BitcoinLikeTransaction", - }, - }, - }); - declare("BitcoinLikeTransactionBuilder", { - methods: { - wipeToAddress: {}, - sendToAddress: { - params: ["Amount"], - }, - excludeUtxo: {}, - pickInputs: {}, - setFeesPerByte: { - params: ["Amount"], - }, - build: { - returns: "BitcoinLikeTransaction", - }, - }, - }); - declare("BitcoinLikeAccount", { - methods: { - getUTXO: { - returns: ["BitcoinLikeOutput"], - }, - getUTXOCount: {}, - buildTransaction: { - returns: "BitcoinLikeTransactionBuilder", - }, - broadcastRawTransaction: { - params: ["hex"], - }, - getFees: { - returns: ["BigInt"], - }, - }, - }); - declare("BitcoinLikeNetworkParameters", { - njsUsesPlainObject: true, - methods: { - getSigHash: { - returns: "hex", - njsField: "SigHash", - }, - getUsesTimestampedTransaction: { - njsField: "UsesTimestampedTransaction", - }, - }, - }); - return { - OperationMethods: { - asBitcoinLikeOperation: { - returns: "BitcoinLikeOperation", - }, - }, - AccountMethods: { - asBitcoinLikeAccount: { - returns: "BitcoinLikeAccount", - }, - }, - }; -}; diff --git a/src/families/bitcoin/wallet-btc/utils.ts b/src/families/bitcoin/wallet-btc/utils.ts index ef411662a9..972ca92e95 100644 --- a/src/families/bitcoin/wallet-btc/utils.ts +++ b/src/families/bitcoin/wallet-btc/utils.ts @@ -138,7 +138,7 @@ function outputWeight(derivationMode: string): number { export function outputSize(currency: ICrypto, addr: string): number { const scriptLen = currency.toOutputScript(addr).length; let size = 1 + 8 + scriptLen; - // More bytes for decred, refer to https://github.com/LedgerHQ/lib-ledger-core/blob/fc9d762b83fc2b269d072b662065747a64ab2816/core/src/wallet/bitcoin/api_impl/BitcoinLikeTransactionApi.cpp#L478 + // More bytes for decred if (currency.network.name === "Decred") { size += 8; } diff --git a/src/families/celo/types.ts b/src/families/celo/types.ts index cabba09bcd..98d5b42138 100644 --- a/src/families/celo/types.ts +++ b/src/families/celo/types.ts @@ -12,9 +12,3 @@ export type TransactionRaw = TransactionCommonRaw & { family: "celo"; fees: string | null | undefined; }; - -export type CoreStatics = Record; -export type CoreAccountSpecifics = Record; -export type CoreOperationSpecifics = Record; -export type CoreCurrencySpecifics = Record; -export const reflect = (_declare: any) => {}; diff --git a/src/families/cosmos/bridge/libcore.ts b/src/families/cosmos/bridge/libcore.ts deleted file mode 100644 index 5b135c2fb7..0000000000 --- a/src/families/cosmos/bridge/libcore.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { BigNumber } from "bignumber.js"; -import { getAbandonSeedAddress } from "@ledgerhq/cryptoassets"; -import { scanAccounts } from "../../../libcore/scanAccounts"; -import { sync } from "../../../libcore/syncAccount"; -import type { - Account, - AccountBridge, - CurrencyBridge, - CryptoCurrency, - AccountLike, -} from "../../../types"; -import type { CosmosValidatorItem, Transaction } from "../types"; -import getTransactionStatus from "../libcore-getTransactionStatus"; -import signOperation from "../libcore-signOperation"; -import broadcast from "../libcore-broadcast"; -import { getMainAccount } from "../../../account"; -import { validateRecipient } from "../../../bridge/shared"; -import { - setCosmosPreloadData, - asSafeCosmosPreloadData, -} from "../preloadedData"; -import { getValidators, hydrateValidators } from "../validators"; -import { calculateFees, getMaxEstimatedBalance } from "../logic"; -import { makeAccountBridgeReceive } from "../../../bridge/jsHelpers"; -const receive = makeAccountBridgeReceive(); - -const createTransaction = (): Transaction => ({ - family: "cosmos", - mode: "send", - amount: new BigNumber(0), - fees: null, - gas: null, - recipient: "", - useAllAmount: false, - networkInfo: null, - memo: null, - cosmosSourceValidator: null, - validators: [], -}); - -const updateTransaction = (t, patch) => { - if ("mode" in patch && patch.mode !== t.mode) { - return { ...t, ...patch, gas: null, fees: null }; - } - - if ( - "validators" in patch && - patch.validators.length !== t.validators.length - ) { - return { ...t, ...patch, gas: null, fees: null }; - } - - return { ...t, ...patch }; -}; - -const isTransactionValidForEstimatedFees = async (a, t) => { - let errors: Error | undefined | null = null; - - if (t.mode === "send" && (t.amount.gt(0) || t.useAllAmount)) { - errors = (await validateRecipient(a.currency, t.recipient)).recipientError; - } else { - errors = - t.validators.some( - (v) => !v.address || !v.address.includes("cosmosvaloper") - ) || - (t.mode !== "claimReward" && - t.validators - .reduce((old, current) => old.plus(current.amount), new BigNumber(0)) - .eq(0)); - } - - return errors; -}; - -const sameFees = (a, b) => (!a || !b ? a === b : a.eq(b)); - -const prepareTransaction = async ( - a: Account, - t: Transaction -): Promise => { - let memo = t.memo; - let fees = t.fees; - let gas = t.gas; - - if (t.recipient || t.mode !== "send") { - const errors = await isTransactionValidForEstimatedFees(a, t); - - if (!errors) { - let amount; - - if (t.useAllAmount) { - amount = getMaxEstimatedBalance(a, new BigNumber(0)); - } - - if ((amount && amount.gt(0)) || !amount) { - const res = await calculateFees({ - a, - t: { ...t, amount: amount || t.amount }, - }); - fees = res.estimatedFees; - gas = res.estimatedGas; - } - } - } - - if (t.mode !== "send" && !memo) { - memo = "Ledger Live"; - } - - if (t.memo !== memo || !sameFees(t.fees, fees)) { - return { ...t, memo, fees, gas }; - } - - return t; -}; - -const currencyBridge: CurrencyBridge = { - preload: async (currency: CryptoCurrency) => { - const validators = await getValidators(currency); - setCosmosPreloadData({ - validators, - }); - return Promise.resolve({ - validators, - }); - }, - hydrate: (data: { validators?: CosmosValidatorItem[] }) => { - if (!data || typeof data !== "object") return; - const { validators } = data; - if ( - !validators || - typeof validators !== "object" || - !Array.isArray(validators) - ) - return; - hydrateValidators(validators); - setCosmosPreloadData(asSafeCosmosPreloadData(data)); - }, - scanAccounts, -}; - -const estimateMaxSpendable = async ({ - account, - parentAccount, - transaction, -}: { - account: AccountLike; - parentAccount: Account; - transaction: Transaction; -}): Promise => { - const mainAccount = getMainAccount(account, parentAccount); - const t = await prepareTransaction(mainAccount, { - ...createTransaction(), - ...transaction, - recipient: - transaction?.recipient || getAbandonSeedAddress(mainAccount.currency.id), - useAllAmount: true, - }); - const s = await getTransactionStatus(mainAccount, t); - return s.amount; -}; - -const accountBridge: AccountBridge = { - createTransaction, - updateTransaction, - prepareTransaction, - getTransactionStatus, - estimateMaxSpendable, - sync, - receive, - signOperation, - broadcast, -}; -export default { - currencyBridge, - accountBridge, -}; diff --git a/src/families/cosmos/libcore-broadcast.ts b/src/families/cosmos/libcore-broadcast.ts deleted file mode 100644 index 3b2baf8975..0000000000 --- a/src/families/cosmos/libcore-broadcast.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { Operation } from "../../types"; -import type { CosmosBroadcastResponse } from "./types"; -import { makeBroadcast } from "../../libcore/broadcast"; -import { CosmosBroadcastError } from "../../errors"; -import { patchOperationWithHash } from "../../operation"; - -async function broadcast({ - coreAccount, - signedOperation: { operation, signature }, -}): Promise { - const cosmosLikeAccount = await coreAccount.asCosmosLikeAccount(); - const res = await cosmosLikeAccount.broadcastRawTransaction(signature); - const parsed: CosmosBroadcastResponse = JSON.parse(res); - - if (parsed.code && parsed.code > 0) { - throw new CosmosBroadcastError[parsed.code](); - } - - return patchOperationWithHash(operation, parsed.txhash); -} - -export default makeBroadcast({ - broadcast, -}); diff --git a/src/families/cosmos/libcore-buildOperation.ts b/src/families/cosmos/libcore-buildOperation.ts deleted file mode 100644 index 6b34f02d67..0000000000 --- a/src/families/cosmos/libcore-buildOperation.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { $Shape } from "utility-types"; -import type { Operation, OperationType } from "../../types"; -import type { Core, CoreOperation } from "../../libcore/types"; -import { BigNumber } from "bignumber.js"; -import { CosmosMessage } from "./types"; - -const translateExtraInfo = async ( - core: Core, - msg: CosmosMessage, - type: OperationType -) => { - let unwrapped: any; - let amount: BigNumber | undefined; - let address: string | undefined; - let cosmosSourceValidator: string; - - switch (type) { - case "DELEGATE": { - unwrapped = await core.CosmosLikeMessage.unwrapMsgDelegate(msg); - const cosmosAmount = await unwrapped.getAmount(); - amount = await cosmosAmount.getAmount(); - address = await unwrapped.getValidatorAddress(); - break; - } - - case "UNDELEGATE": { - unwrapped = await core.CosmosLikeMessage.unwrapMsgUndelegate(msg); - const cosmosAmount = await unwrapped.getAmount(); - amount = await cosmosAmount.getAmount(); - address = await unwrapped.getValidatorAddress(); - break; - } - - case "REWARD": { - unwrapped = - await core.CosmosLikeMessage.unwrapMsgWithdrawDelegationReward(msg); - address = await unwrapped.getValidatorAddress(); - amount = new BigNumber(0); - break; - } - - case "REDELEGATE": { - unwrapped = await core.CosmosLikeMessage.unwrapMsgBeginRedelegate(msg); - const cosmosAmount = await unwrapped.getAmount(); - amount = await cosmosAmount.getAmount(); - address = await unwrapped.getValidatorDestinationAddress(); - cosmosSourceValidator = await unwrapped.getValidatorSourceAddress(); - return { - validators: [ - { - address, - amount, - }, - ], - cosmosSourceValidator, - }; - } - } - - const validator = { - address, - amount, - }; - - return { - validators: [validator], - }; -}; - -async function cosmosBuildOperation({ - core, - coreOperation, -}: { - core: Core; - coreOperation: CoreOperation; -}): Promise> { - const cosmosLikeOperation = await coreOperation.asCosmosLikeOperation(); - const cosmosLikeTransaction = await cosmosLikeOperation.getTransaction(); - const hash = await cosmosLikeTransaction.getHash(); - const memo = await cosmosLikeTransaction.getMemo(); - const message = await cosmosLikeOperation.getMessage(); - const out: $Shape = { - hash, - }; - - switch (await message.getRawMessageType()) { - case "internal/MsgFees": - out.type = "FEES"; - break; - - case "cosmos-sdk/MsgDelegate": - out.type = "DELEGATE"; - out.extra = await translateExtraInfo(core, message, out.type); - break; - - case "cosmos-sdk/MsgUndelegate": - out.type = "UNDELEGATE"; - out.extra = await translateExtraInfo(core, message, out.type); - break; - - case "cosmos-sdk/MsgWithdrawDelegationReward": - out.type = "REWARD"; - out.extra = await translateExtraInfo(core, message, out.type); - break; - - case "cosmos-sdk/MsgBeginRedelegate": - out.type = "REDELEGATE"; - out.extra = await translateExtraInfo(core, message, out.type); - break; - } - - out.extra = { ...out.extra, id: await message.getIndex() }; - - if (memo) { - out.extra = { ...out.extra, memo }; - } - - return out; -} - -export default cosmosBuildOperation; diff --git a/src/families/cosmos/libcore-buildTransaction.ts b/src/families/cosmos/libcore-buildTransaction.ts deleted file mode 100644 index 926d851e9e..0000000000 --- a/src/families/cosmos/libcore-buildTransaction.ts +++ /dev/null @@ -1,212 +0,0 @@ -import axios, { AxiosRequestConfig } from "axios"; -import { retry } from "../../promise"; -import { FeeNotLoaded } from "@ledgerhq/errors"; -import type { Transaction, CoreCosmosLikeTransaction } from "./types"; -import type { Account, CryptoCurrency } from "../../types"; -import type { Core, CoreAccount, CoreCurrency } from "../../libcore/types"; -import getTransactionStatus from "./libcore-getTransactionStatus"; -import { - bigNumberToLibcoreAmount, - libcoreBigIntToBigNumber, -} from "../../libcore/buildBigNumber"; -import { BigNumber } from "bignumber.js"; -import { cosmosCreateMessage } from "./message"; -import { getEnv } from "../../env"; -import { promiseAllBatched } from "../../promise"; -import { getMaxEstimatedBalance } from "./logic"; -import network from "../../network"; - -const getBaseApiUrl = (currency: CryptoCurrency) => { - if (currency.id === "cosmos_testnet") { - return getEnv("API_COSMOS_TESTNET_BLOCKCHAIN_EXPLORER_API_ENDPOINT"); - } else { - return getEnv("API_COSMOS_BLOCKCHAIN_EXPLORER_API_ENDPOINT"); - } -}; - -const isStargate = (currency: CryptoCurrency) => { - if (currency.id === "cosmos_testnet") { - return getEnv("API_COSMOS_TESTNET_NODE") == "STARGATE_NODE"; - } else { - return getEnv("API_COSMOS_NODE") == "STARGATE_NODE"; - } -}; - -async function fetch_sequence(address: string, currency: CryptoCurrency) { - const namespace = "cosmos"; - const version = "v1beta1"; - - if (isStargate(currency)) { - const url = `${getBaseApiUrl( - currency - )}/${namespace}/auth/${version}/accounts/${address}`; - const { data } = await network({ - method: "GET", - url, - }); - return data.account.sequence; - } else { - const url = `${getBaseApiUrl(currency)}/auth/accounts/${address}`; - const { data } = await network({ - method: "GET", - url, - }); - return data.result.value.sequence; - } -} - -/// Return true if this address can be used to estimate gas. -/// Stargate API will refuse to estimate gas for a sender that does not -/// exist in the state, so we check the account endpoint for a non-error response -async function canEstimateGas(account: Account, transaction: Transaction) { - const namespace = "cosmos"; - const version = "v1beta1"; - - if (isStargate(account.currency)) { - const url = `${getBaseApiUrl( - account.currency - )}/${namespace}/auth/${version}/accounts/${account.freshAddress}`; - const request: AxiosRequestConfig = { - method: "GET", - url, - timeout: getEnv("GET_CALLS_TIMEOUT"), - }; - const retriable = retry(() => axios(request), { - maxRetry: getEnv("GET_CALLS_RETRY"), - }); - // FIXME: 1 - Stargate also refuses to estimate gas for a variety of other reasons, - // eg. insufficient amount (LL-4667), redelegating to same validator, maybe others... - // So getTransactionStatus called here as "pre-validation" - // FIXME: 2 - For redelegation getTransactionStatus doesn't check that - // the old and new validator exist, which also causes this to fail - const status = await getTransactionStatus(account, transaction, true); - return await retriable - .then((_response) => { - return !status.errors || Object.entries(status.errors).length === 0; - }) - .catch(() => { - return false; - }); - } else { - return true; - } -} - -export async function cosmosBuildTransaction({ - account, - core, - coreAccount, - coreCurrency, - transaction, - isCancelled, - isPartial, // is true if we just want to estimate fees and gas -}: { - account: Account; - core: Core; - coreAccount: CoreAccount; - coreCurrency: CoreCurrency; - transaction: Transaction; - isPartial: boolean; - isCancelled: () => boolean; -}): Promise { - const { gas, memo } = transaction; - const cosmosLikeAccount = await coreAccount.asCosmosLikeAccount(); - if (isCancelled()) return; - const transactionBuilder = await cosmosLikeAccount.buildTransaction(); - if (isCancelled()) return; - const accountCanEstimateGas = await canEstimateGas(account, transaction); - if (isCancelled()) return; - let messages = await cosmosCreateMessage( - account.freshAddress, - { - ...transaction, - amount: transaction.useAllAmount - ? getMaxEstimatedBalance(account, new BigNumber(0)) - : transaction.amount, - }, - core, - account.currency - ); - const memoTransaction = memo || ""; - await transactionBuilder.setMemo(memoTransaction); - // Gas - let estimatedGas: BigNumber; - - if (isPartial && accountCanEstimateGas) { - const gasRequest = await core.CosmosGasLimitRequest.init( - memoTransaction, - messages, - // COSMOS_GAS_AMPLIFIER env use by JS implementation - // Set as 4 int in order to not break libcore implementation - // String(getEnv("COSMOS_GAS_AMPLIFIER")) - String(4) - ); - estimatedGas = await libcoreBigIntToBigNumber( - // NOTE: With new cosmos code, this call might fail if the account hasn't been synchronized - // and missed a new transaction. This is because now the account sequence needs to be exact, - // and can't be a dummy 0 like pre-Stargate. - // - // LibCore internally calls for sequence number synchronization here, but this is a data race between the - // last time we read the sequence number and the instant we send the gas estimation request - await cosmosLikeAccount.estimateGas(gasRequest) - ); - } else { - // 60000 is the default gas here. - estimatedGas = gas || new BigNumber(60000); - } - - if (!estimatedGas.gt(0)) { - throw new FeeNotLoaded(); - } - - const gasAmount = await bigNumberToLibcoreAmount( - core, - coreCurrency, - estimatedGas - ); - if (isCancelled()) return; - await transactionBuilder.setGas(gasAmount); - const gasPrice = getEnv("COSMOS_GAS_PRICE"); - const feesBigNumber = estimatedGas - .multipliedBy(gasPrice) - .integerValue(BigNumber.ROUND_CEIL); - const feesAmount = await bigNumberToLibcoreAmount( - core, - coreCurrency, - feesBigNumber - ); - if (isCancelled()) return; - await transactionBuilder.setFee(feesAmount); - - if (transaction.useAllAmount && transaction.amount) { - messages = await cosmosCreateMessage( - account.freshAddress, - { - ...transaction, - amount: getMaxEstimatedBalance(account, feesBigNumber), - }, - core, - account.currency - ); - } - - promiseAllBatched( - 3, - messages, - async (message) => await transactionBuilder.addMessage(message) - ); - // Signature information - const accNum = await cosmosLikeAccount.getAccountNumber(); - await transactionBuilder.setAccountNumber(accNum); - - if (accountCanEstimateGas) { - const seq = await fetch_sequence(account.freshAddress, account.currency); - await transactionBuilder.setSequence(seq); - } else { - await transactionBuilder.setSequence("0"); - } - - const tx = await transactionBuilder.build(); - return tx; -} -export default cosmosBuildTransaction; diff --git a/src/families/cosmos/libcore-getFeesForTransaction.ts b/src/families/cosmos/libcore-getFeesForTransaction.ts deleted file mode 100644 index 567e75f33d..0000000000 --- a/src/families/cosmos/libcore-getFeesForTransaction.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { Account } from "../../types"; -import type { Core, CoreCurrency, CoreAccount } from "../../libcore/types"; -import type { Transaction } from "./types"; -import { libcoreAmountToBigNumber } from "../../libcore/buildBigNumber"; -import buildTransaction from "./libcore-buildTransaction"; -import BigNumber from "bignumber.js"; - -async function cosmos(args: { - account: Account; - core: Core; - coreAccount: CoreAccount; - coreCurrency: CoreCurrency; - transaction: Transaction; - isPartial: boolean; - isCancelled: () => boolean; -}): Promise<{ estimatedFees: BigNumber; estimatedGas: BigNumber } | undefined> { - const builded = await buildTransaction({ ...args, isPartial: true }); - if (!builded) return; - const estimatedFees = await libcoreAmountToBigNumber(await builded.getFee()); - const estimatedGas = await libcoreAmountToBigNumber(await builded.getGas()); - return { - estimatedFees, - estimatedGas, - }; -} - -export default cosmos; diff --git a/src/families/cosmos/libcore-getTransactionStatus.ts b/src/families/cosmos/libcore-getTransactionStatus.ts deleted file mode 100644 index 505f68bf53..0000000000 --- a/src/families/cosmos/libcore-getTransactionStatus.ts +++ /dev/null @@ -1,299 +0,0 @@ -import { BigNumber } from "bignumber.js"; -import invariant from "invariant"; -import { - NotEnoughBalance, - InvalidAddressBecauseDestinationIsAlsoSource, - InvalidAddress, - AmountRequired, - RecommendUndelegation, - FeeNotLoaded, -} from "@ledgerhq/errors"; -import { - CosmosRedelegationInProgress, - ClaimRewardsFeesWarning, - CosmosDelegateAllFundsWarning, - CosmosTooManyValidators, - NotEnoughDelegationBalance, -} from "../../errors"; -import { - getMaxEstimatedBalance, - COSMOS_MAX_REDELEGATIONS, - COSMOS_MAX_UNBONDINGS, - COSMOS_MAX_DELEGATIONS, -} from "./logic"; -import { validateRecipient } from "../../bridge/shared"; -import { Transaction, TransactionStatus, StatusErrorMap } from "./types"; -import { Account } from "../../types"; - -const isDelegable = ( - a: Account, - address: string | undefined | null, - amount: BigNumber -) => { - const { cosmosResources } = a; - invariant(cosmosResources, "cosmosResources should exist"); - - if ( - cosmosResources && - cosmosResources.delegations.some( - (delegation) => - delegation.validatorAddress === address && delegation.amount.lt(amount) - ) - ) { - return new NotEnoughDelegationBalance(); - } - - return null; -}; - -const redelegationStatusError = (a: Account, t: Transaction) => { - if (a.cosmosResources) { - const redelegations = a.cosmosResources.redelegations; - invariant( - redelegations.length < COSMOS_MAX_REDELEGATIONS, - "redelegation should not have more than 6 entries" - ); - if ( - redelegations.some((redelegation) => { - const dstValidator = redelegation.validatorDstAddress; - return ( - dstValidator === t.cosmosSourceValidator && - redelegation.completionDate > new Date() - ); - }) - ) - return new CosmosRedelegationInProgress(); - if (t.cosmosSourceValidator === t.validators[0].address) - return new InvalidAddressBecauseDestinationIsAlsoSource(); - } - - return isDelegable(a, t.cosmosSourceValidator, t.validators[0].amount); -}; - -const getSendTransactionStatus = async ( - a: Account, - t: Transaction, - isPreValidation = false -): Promise => { - const errors: StatusErrorMap = {}; - const warnings: StatusErrorMap = {}; - - if (a.freshAddress === t.recipient) { - errors.recipient = new InvalidAddressBecauseDestinationIsAlsoSource(); - } else { - const { recipientError, recipientWarning } = await validateRecipient( - a.currency, - t.recipient - ); - - if (recipientError) { - errors.recipient = recipientError; - } - - if (recipientWarning) { - warnings.recipient = recipientWarning; - } - } - - let amount = t.amount; - - if (amount.lte(0) && !t.useAllAmount) { - errors.amount = new AmountRequired(); - } - - const estimatedFees = t.fees || new BigNumber(0); - - if (!isPreValidation && (!t.fees || !t.fees.gt(0))) { - errors.fees = new FeeNotLoaded(); - } - - amount = t.useAllAmount ? getMaxEstimatedBalance(a, estimatedFees) : amount; - const totalSpent = amount.plus(estimatedFees); - - if ( - (amount.lte(0) && t.useAllAmount) || // if use all Amount sets an amount at 0 - (!errors.recipient && !errors.amount && totalSpent.gt(a.spendableBalance)) // if spendable balance lower than total - ) { - errors.amount = new NotEnoughBalance(); - } - - if ( - a.cosmosResources && - a.cosmosResources.delegations.length > 0 && - t.useAllAmount - ) { - warnings.amount = new RecommendUndelegation(); - } - - return Promise.resolve({ - errors, - warnings, - estimatedFees, - amount, - totalSpent, - }); -}; - -const getDelegateTransactionStatus = async ( - a: Account, - t: Transaction, - isPreValidation = false -): Promise => { - const errors: StatusErrorMap = {}; - const warnings: StatusErrorMap = {}; - if ( - t.validators.some( - (v) => !v.address || !v.address.includes("cosmosvaloper") - ) || - t.validators.length === 0 - ) - errors.recipient = new InvalidAddress(undefined, { - currencyName: a.currency.name, - }); - - if (t.validators.length > COSMOS_MAX_DELEGATIONS) { - errors.validators = new CosmosTooManyValidators(); - } - - let amount = t.validators.reduce( - (old, current) => old.plus(current.amount), - new BigNumber(0) - ); - - if (amount.eq(0)) { - errors.amount = new AmountRequired(); - } - - const estimatedFees = t.fees || new BigNumber(0); - - if (!isPreValidation && !t.fees) { - errors.fees = new FeeNotLoaded(); - } - - let totalSpent = amount.plus(estimatedFees); - - if (totalSpent.eq(a.spendableBalance)) { - warnings.delegate = new CosmosDelegateAllFundsWarning(); - } - - if ( - !errors.recipient && - !errors.amount && - (amount.lt(0) || totalSpent.gt(a.spendableBalance)) - ) { - errors.amount = new NotEnoughBalance(); - amount = new BigNumber(0); - totalSpent = new BigNumber(0); - } - - return Promise.resolve({ - errors, - warnings, - estimatedFees, - amount, - totalSpent, - }); -}; - -const getTransactionStatus = async ( - a: Account, - t: Transaction, - isPreValidation = false -): Promise => { - if (t.mode === "send") { - // We isolate the send transaction that it's a little bit different from the rest - return await getSendTransactionStatus(a, t, isPreValidation); - } else if (t.mode === "delegate") { - return await getDelegateTransactionStatus(a, t, isPreValidation); - } - - const errors: StatusErrorMap = {}; - const warnings: StatusErrorMap = {}; - // here we only treat about all other mode than delegate and send - if ( - t.validators.some( - (v) => !v.address || !v.address.includes("cosmosvaloper") - ) || - t.validators.length === 0 - ) - errors.recipient = new InvalidAddress(undefined, { - currencyName: a.currency.name, - }); - - if (t.mode === "redelegate") { - const redelegationError = redelegationStatusError(a, t); - - if (redelegationError) { - // Note : note sure if I have to put this error on this field - errors.redelegation = redelegationError; - } - } else if (t.mode === "undelegate") { - invariant( - a.cosmosResources && - a.cosmosResources.unbondings.length < COSMOS_MAX_UNBONDINGS, - "unbondings should not have more than 6 entries" - ); - if (t.validators.length === 0) - errors.recipient = new InvalidAddress(undefined, { - currencyName: a.currency.name, - }); - const [first] = t.validators; - const unbondingError = first && isDelegable(a, first.address, first.amount); - - if (unbondingError) { - errors.unbonding = unbondingError; - } - } - - const validatorAmount = t.validators.reduce( - (old, current) => old.plus(current.amount), - new BigNumber(0) - ); - - if (t.mode !== "claimReward" && validatorAmount.lte(0)) { - errors.amount = new AmountRequired(); - } - - const estimatedFees = t.fees || new BigNumber(0); - - if (!isPreValidation && !t.fees) { - errors.fees = new FeeNotLoaded(); - } - - let totalSpent = estimatedFees; - - if (["claimReward", "claimRewardCompound"].includes(t.mode)) { - const { cosmosResources } = a; - invariant(cosmosResources, "cosmosResources should exist"); - const claimReward = - t.validators.length && cosmosResources - ? cosmosResources.delegations.find( - (delegation) => - delegation.validatorAddress === t.validators[0].address - ) - : null; - - if (claimReward && estimatedFees.gt(claimReward.pendingRewards)) { - warnings.claimReward = new ClaimRewardsFeesWarning(); - } - } - - if ( - !errors.recipient && - !errors.amount && - (validatorAmount.lt(0) || totalSpent.gt(a.spendableBalance)) - ) { - errors.amount = new NotEnoughBalance(); - totalSpent = new BigNumber(0); - } - - return Promise.resolve({ - errors, - warnings, - estimatedFees, - amount: new BigNumber(0), - totalSpent, - }); -}; - -export default getTransactionStatus; diff --git a/src/families/cosmos/libcore-mergeOperations.ts b/src/families/cosmos/libcore-mergeOperations.ts deleted file mode 100644 index ed9f3c0459..0000000000 --- a/src/families/cosmos/libcore-mergeOperations.ts +++ /dev/null @@ -1,43 +0,0 @@ -import invariant from "invariant"; -import type { Operation } from "../../types"; - -const mergeOperations = (operations: Operation[]): Operation => { - const feeOp = operations.find((op) => op.type === "FEES"); - const otherOps = operations.filter((op) => op !== feeOp); - - if (otherOps.length === 0) { - invariant(feeOp, "fee op is necessary if no other ops exists"); - return feeOp as Operation; - } - - const union: Operation = { ...otherOps[0] }; - // remove the "-0" part - union.id = union.id.split("-").slice(0, 3).join("-"); - - if (feeOp) { - union.fee = feeOp.value; - - if (union.type === "OUT") { - union.value = union.value.plus(feeOp.value); - } else { - union.value = feeOp.value; - } - } - - const extra = { ...union.extra }; - delete extra.id; - - // accumulate validators - if (extra.validators) { - extra.validators = otherOps.reduce( - (validators, op) => - validators.concat((op.extra && op.extra.validators) || []), - [] - ); - } - - union.extra = extra; - return union; -}; - -export default mergeOperations; diff --git a/src/families/cosmos/libcore-postBuildAccount.ts b/src/families/cosmos/libcore-postBuildAccount.ts deleted file mode 100644 index ceeb1fa815..0000000000 --- a/src/families/cosmos/libcore-postBuildAccount.ts +++ /dev/null @@ -1,177 +0,0 @@ -import type { Account } from "../../types"; -import type { CoreAccount } from "../../libcore/types"; -import type { - CosmosResources, - CoreCosmosLikeAccount, - CosmosUnbonding, - CosmosRedelegation, - CosmosDelegation, -} from "./types"; -import { BigNumber } from "bignumber.js"; -import { log } from "@ledgerhq/logs"; -import { - libcoreAmountToBigNumber, - libcoreBigIntToBigNumber, -} from "../../libcore/buildBigNumber"; -import { promiseAllBatched } from "../../promise"; -import { getMaxEstimatedBalance } from "./logic"; - -const getValidatorStatus = async ( - cosmosAccount: CoreCosmosLikeAccount, - address -) => { - const status = ["unbonded", "unbonding", "bonded"]; - const validatorInfo = await cosmosAccount.getValidatorInfo(address); - const rawStatus = await validatorInfo.getActiveStatus(); - - // Pre stargate - if (["0", "1", "2"].includes(rawStatus)) { - return status[parseInt(rawStatus)]; - } - - // Stargate - const stargateStatusMap = { - BOND_STATUS_UNBONDED: "unbonded", - BOND_STATUS_UNBONDING: "unbonding", - BOND_STATUS_BONDED: "bonded", - }; - return stargateStatusMap[rawStatus] || "unbonded"; -}; - -const getFlattenDelegation = async ( - cosmosAccount: CoreCosmosLikeAccount -): Promise => { - const delegations = await cosmosAccount.getDelegations(); - const pendingRewards = await cosmosAccount.getPendingRewards(); - return await promiseAllBatched(10, delegations, async (delegation) => { - const validatorAddress = await delegation.getValidatorAddress(); - let reward; - - for (let i = 0; i < pendingRewards.length; i++) { - if ( - (await pendingRewards[i].getValidatorAddress()) === validatorAddress - ) { - reward = await pendingRewards[i].getRewardAmount(); - break; - } - } - - return { - amount: await libcoreAmountToBigNumber( - await delegation.getDelegatedAmount() - ), - validatorAddress, - pendingRewards: reward - ? await libcoreAmountToBigNumber(reward) - : new BigNumber(0), - status: await getValidatorStatus(cosmosAccount, validatorAddress), - }; - }); -}; - -const getFlattenRedelegations = async ( - cosmosAccount: CoreCosmosLikeAccount -): Promise => { - const redelegations = await cosmosAccount.getRedelegations(); - const toFlatten = await promiseAllBatched( - 3, - redelegations, - async (redelegation) => - await promiseAllBatched( - 3, - await redelegation.getEntries(), - async (entry) => ({ - validatorSrcAddress: await redelegation.getSrcValidatorAddress(), - validatorDstAddress: await redelegation.getDstValidatorAddress(), - amount: await libcoreBigIntToBigNumber( - await entry.getInitialBalance() - ), - completionDate: await entry.getCompletionTime(), - }) - ) - ); - return toFlatten.reduce((old, current) => [...old, ...current], []); -}; - -const getFlattenUnbonding = async ( - cosmosAccount: CoreCosmosLikeAccount -): Promise => { - const unbondings = await cosmosAccount.getUnbondings(); - const toFlatten = await promiseAllBatched( - 3, - unbondings, - async (unbonding) => - await promiseAllBatched( - 3, - await unbonding.getEntries(), - async (entry) => ({ - validatorAddress: await unbonding.getValidatorAddress(), - amount: await libcoreBigIntToBigNumber( - await entry.getInitialBalance() - ), - completionDate: await entry.getCompletionTime(), - }) - ) - ); - return toFlatten.reduce((old, current) => [...old, ...current], []); -}; - -const filterDelegation = (delegations) => { - return delegations.filter((delegation) => delegation.amount.gt(0)); -}; - -const getCosmosResources = async ( - account: Account, - coreAccount -): Promise => { - const cosmosAccount = await coreAccount.asCosmosLikeAccount(); - const flattenDelegation = await getFlattenDelegation(cosmosAccount); - const flattenUnbonding = await getFlattenUnbonding(cosmosAccount); - const flattenRedelegation = await getFlattenRedelegations(cosmosAccount); - const res = { - delegations: filterDelegation(flattenDelegation), - redelegations: flattenRedelegation, - unbondings: flattenUnbonding, - delegatedBalance: flattenDelegation.reduce( - (old, current) => old.plus(current.amount), - new BigNumber(0) - ), - pendingRewardsBalance: flattenDelegation.reduce( - (old, current) => old.plus(current.pendingRewards), - new BigNumber(0) - ), - unbondingBalance: flattenUnbonding.reduce( - (old, current) => old.plus(current.amount), - new BigNumber(0) - ), - withdrawAddress: "", - }; - return res; -}; - -const postBuildAccount = async ({ - account, - coreAccount, -}: { - account: Account; - coreAccount: CoreAccount; -}): Promise => { - log("cosmos/post-buildAccount", "getCosmosResources"); - account.cosmosResources = await getCosmosResources(account, coreAccount); - log("cosmos/post-buildAccount", "getCosmosResources DONE"); - account.spendableBalance = getMaxEstimatedBalance(account, new BigNumber(0)); - - if (account.spendableBalance.lt(0)) { - account.spendableBalance = new BigNumber(0); - } - - if (!account.used) { - const cosmosAccount = await coreAccount.asCosmosLikeAccount(); - const seq = await cosmosAccount.getSequence(); - account.used = seq != ""; - } - - return account; -}; - -export default postBuildAccount; diff --git a/src/families/cosmos/libcore-signOperation.ts b/src/families/cosmos/libcore-signOperation.ts deleted file mode 100644 index f8397e6e15..0000000000 --- a/src/families/cosmos/libcore-signOperation.ts +++ /dev/null @@ -1,96 +0,0 @@ -import CosmosApp from "@ledgerhq/hw-app-cosmos"; -import { makeSignOperation } from "../../libcore/signOperation"; -import buildTransaction from "./libcore-buildTransaction"; -import type { Transaction, CoreCosmosLikeTransaction } from "./types"; -import { libcoreAmountToBigNumber } from "../../libcore/buildBigNumber"; -import { OperationType } from "../../types"; - -async function signTransaction({ - account, - transport, - transaction, - coreTransaction, - isCancelled, - onDeviceSignatureGranted, - onDeviceSignatureRequested, -}) { - const { freshAddressPath, spendableBalance, id, freshAddress } = account; - const hwApp = new CosmosApp(transport); - const serialized = await coreTransaction.serializeForSignature(); - onDeviceSignatureRequested(); - const { signature } = await hwApp.sign(freshAddressPath, serialized); - onDeviceSignatureGranted(); - - if (signature) { - await coreTransaction.setDERSignature(signature.toString("hex")); - } else { - throw new Error("Cosmos: no Signature Found"); - } - - if (isCancelled()) return; - // Serialize the transaction to be broadcast - // @param mode The supported broadcast modes include - // "block"(return after tx commit), (https://docs.cosmos.network/master/basics/tx-lifecycle.html#commit) - // "sync"(return afer CheckTx), (https://docs.cosmos.network/master/basics/tx-lifecycle.html#types-of-checks) and - // "async"(return right away). - const hex = await coreTransaction.serializeForBroadcast("sync"); - if (isCancelled()) return; - const feesRaw = await coreTransaction.getFee(); - if (isCancelled()) return; - const fee = await libcoreAmountToBigNumber(feesRaw); - if (isCancelled()) return; - const recipients = [transaction.recipient]; - if (isCancelled()) return; - const senders = [freshAddress]; - if (isCancelled()) return; - const type: OperationType = - transaction.mode === "undelegate" - ? "UNDELEGATE" - : transaction.mode === "delegate" - ? "DELEGATE" - : transaction.mode === "redelegate" - ? "REDELEGATE" - : ["claimReward", "claimRewardCompound"].includes(transaction.mode) - ? "REWARD" - : "OUT"; - const extra = {}; - - if (transaction.mode === "redelegate") { - Object.assign(extra, { - cosmosSourceValidator: transaction.cosmosSourceValidator, - }); - } - - if (transaction.mode !== "send") { - Object.assign(extra, { - validators: transaction.validators, - }); - } - - const op = { - id: `${id}--${type}`, - hash: "", - type, - value: transaction.useAllAmount - ? spendableBalance - : transaction.amount.plus(fee), - fee, - blockHash: null, - blockHeight: null, - senders, - recipients, - accountId: id, - date: new Date(), - extra, - }; - return { - operation: op, - expirationDate: null, - signature: hex, - }; -} - -export default makeSignOperation({ - buildTransaction, - signTransaction, -}); diff --git a/src/families/cosmos/logic.ts b/src/families/cosmos/logic.ts index 1a8a11c473..42cb4b1cf6 100644 --- a/src/families/cosmos/logic.ts +++ b/src/families/cosmos/logic.ts @@ -13,11 +13,8 @@ import type { CosmosRedelegation, CosmosMappedRedelegation, } from "./types"; -import type { CacheRes } from "../../cache"; import type { Unit, Account } from "../../types"; -import type { Transaction } from "./types"; -import { getFeesForTransaction } from "../../libcore/getFeesForTransaction"; -import { makeLRUCache } from "../../cache"; +import { calculateFees } from "./js-prepareTransaction"; export const COSMOS_MAX_REDELEGATIONS = 7; export const COSMOS_MAX_UNBONDINGS = 7; @@ -163,37 +160,7 @@ export const getMaxEstimatedBalance = ( return amount; }; -export const calculateFees: CacheRes< - Array<{ - a: Account; - t: Transaction; - }>, - { - estimatedFees: BigNumber; - estimatedGas: BigNumber | null | undefined; - } -> = makeLRUCache( - async ({ - a, - t, - }): Promise<{ - estimatedFees: BigNumber; - estimatedGas: BigNumber | null | undefined; - }> => { - return getFeesForTransaction({ - account: a, - transaction: t, - }); - }, - ({ a, t }) => - `${a.id}_${a.currency.id}_${t.amount.toString()}_${t.recipient}_${String( - t.useAllAmount - )}_${t.mode}_${ - t.validators ? t.validators.map((v) => v.address).join("-") : "" - }_${t.memo ? t.memo.toString() : ""}_${ - t.cosmosSourceValidator ? t.cosmosSourceValidator : "" - }` -); + export function canUndelegate(account: Account): boolean { const { cosmosResources } = account; invariant(cosmosResources, "cosmosResources should exist"); @@ -230,8 +197,8 @@ export async function canClaimRewards( const { cosmosResources } = account; invariant(cosmosResources, "cosmosResources should exist"); const res = await calculateFees({ - a: account, - t: { + account, + transaction: { family: "cosmos", mode: "claimReward", amount: new BigNumber(0), diff --git a/src/families/cosmos/message.ts b/src/families/cosmos/message.ts deleted file mode 100644 index a2d704c6e7..0000000000 --- a/src/families/cosmos/message.ts +++ /dev/null @@ -1,155 +0,0 @@ -import type { Transaction, CosmosMessage } from "./types"; -import type { Core } from "../../libcore/types"; -import { promiseAllBatched } from "../../promise"; -import type { CryptoCurrency } from "../../types"; - -const getAmount = async ( - core: Core, - currency: CryptoCurrency, - amount: string -) => { - return await core.CosmosLikeAmount.init( - amount, - currency.id === "cosmos_testnet" ? "umuon" : "uatom" - ); -}; - -export const cosmosCreateMessage = async ( - freshAddress: string, - transaction: Transaction, - core: Core, - currency: CryptoCurrency -): Promise => { - const { recipient } = transaction; - - switch (transaction.mode) { - case "send": - return [ - await core.CosmosLikeMessage.wrapMsgSend( - await core.CosmosLikeMsgSend.init(freshAddress, recipient, [ - await getAmount(core, currency, transaction.amount.toString()), - ]) - ), - ]; - - case "delegate": { - const { validators } = transaction; - - if (!validators || validators.length === 0) { - throw new Error("no validators"); - } - - return await promiseAllBatched( - 2, - validators, - async (validator) => - await core.CosmosLikeMessage.wrapMsgDelegate( - await core.CosmosLikeMsgDelegate.init( - freshAddress, - validator.address, - await getAmount(core, currency, validator.amount.toString()) - ) - ) - ); - } - - case "undelegate": { - const { validators } = transaction; - - if (!validators || validators.length === 0) { - throw new Error("no validators"); - } - - return await promiseAllBatched( - 2, - validators, - async (validator) => - await core.CosmosLikeMessage.wrapMsgUndelegate( - await core.CosmosLikeMsgUndelegate.init( - freshAddress, - validator.address, - await getAmount(core, currency, validator.amount.toString()) - ) - ) - ); - } - - case "redelegate": { - const { cosmosSourceValidator } = transaction; - - if (!cosmosSourceValidator) { - throw new Error("source validator is empty"); - } - - const { validators } = transaction; - - if (!validators || validators.length === 0) { - throw new Error("no validators"); - } - - return await promiseAllBatched( - 2, - validators, - async (validator) => - await core.CosmosLikeMessage.wrapMsgBeginRedelegate( - await core.CosmosLikeMsgBeginRedelegate.init( - freshAddress, - cosmosSourceValidator, - validator.address, - await getAmount(core, currency, validator.amount.toString()) - ) - ) - ); - } - - case "claimReward": { - const { validators } = transaction; - - if (!validators || validators.length === 0) { - throw new Error("no validators"); - } - - return await promiseAllBatched( - 2, - validators, - async (validator) => - await core.CosmosLikeMessage.wrapMsgWithdrawDelegationReward( - await core.CosmosLikeMsgWithdrawDelegationReward.init( - freshAddress, - validator.address - ) - ) - ); - } - - case "claimRewardCompound": { - const { validators } = transaction; - - if (!validators || validators.length === 0) { - throw new Error("no validators"); - } - - return [ - ...(await promiseAllBatched(2, validators, async (validator) => { - return await core.CosmosLikeMessage.wrapMsgWithdrawDelegationReward( - await core.CosmosLikeMsgWithdrawDelegationReward.init( - freshAddress, - validator.address - ) - ); - })), - ...(await promiseAllBatched(2, validators, async (validator) => { - return await core.CosmosLikeMessage.wrapMsgDelegate( - await core.CosmosLikeMsgDelegate.init( - freshAddress, - validator.address, - await getAmount(core, currency, validator.amount.toString()) - ) - ); - })), - ]; - } - } - - throw new Error(`unknown message : ${transaction.mode}`); -}; diff --git a/src/families/cosmos/test-specifics.ts b/src/families/cosmos/test-specifics.ts deleted file mode 100644 index ad86ee96d4..0000000000 --- a/src/families/cosmos/test-specifics.ts +++ /dev/null @@ -1,225 +0,0 @@ -import { BigNumber } from "bignumber.js"; -import { cosmosCreateMessage } from "./message"; -import { withLibcore } from "../../libcore/access"; -import { getCryptoCurrencyById } from "../../currencies"; -import { Transaction } from "./types"; -export default () => { - describe("cosmosCreateMessage", () => { - const commonTransaction = { - family: "cosmos", - fees: null, - gas: null, - recipient: "", - useAllAmount: false, - networkInfo: null, - memo: null, - cosmosSourceValidator: null, - validators: [], - }; - const sourceAddresss = "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl"; - const currency = getCryptoCurrencyById("cosmos"); - test("create a message send", async () => { - const messages = await withLibcore(async (core) => { - return await cosmosCreateMessage( - sourceAddresss, - { - ...commonTransaction, - amount: new BigNumber(3000), - mode: "send", - } as Transaction, - core, - currency - ); - }); - expect(messages[0].constructor.name).toBe("NJSCosmosLikeMessage"); - expect(messages.length).toBe(1); - }); - test("create a message delegate that throw and error", async () => { - await withLibcore(async (core) => { - try { - return await cosmosCreateMessage( - sourceAddresss, - { - ...commonTransaction, - amount: new BigNumber(3000), - mode: "delegate", - } as Transaction, - core, - currency - ); - } catch (e: any) { - expect(e.message).toBe("no validators"); - } - }); - }); - test("create a message delegate with multiples validators", async () => { - const messages = await withLibcore(async (core) => { - return await cosmosCreateMessage( - sourceAddresss, - { - ...commonTransaction, - amount: new BigNumber(3000), - mode: "delegate", - validators: [ - { - amount: new BigNumber(3000), - address: "", - }, - { - amount: new BigNumber(3000), - address: "", - }, - { - amount: new BigNumber(3000), - address: "", - }, - ], - } as Transaction, - core, - currency - ); - }); - expect(messages[0].constructor.name).toBe("NJSCosmosLikeMessage"); - expect(messages[1].constructor.name).toBe("NJSCosmosLikeMessage"); - expect(messages[2].constructor.name).toBe("NJSCosmosLikeMessage"); - expect(messages.length).toBe(3); - }); - test("create a message delegate", async () => { - const messages = await withLibcore(async (core) => { - return await cosmosCreateMessage( - sourceAddresss, - { - ...commonTransaction, - amount: new BigNumber(3000), - mode: "delegate", - validators: [ - { - amount: new BigNumber(3000), - address: "", - }, - ], - } as Transaction, - core, - currency - ); - }); - expect(messages[0].constructor.name).toBe("NJSCosmosLikeMessage"); - expect(messages.length).toBe(1); - }); - test("create a message undelegate", async () => { - const messages = await withLibcore(async (core) => { - return await cosmosCreateMessage( - sourceAddresss, - { - ...commonTransaction, - amount: new BigNumber(3000), - mode: "undelegate", - validators: [ - { - amount: new BigNumber(3000), - address: "", - }, - ], - } as Transaction, - core, - currency - ); - }); - expect(messages[0].constructor.name).toBe("NJSCosmosLikeMessage"); - expect(messages.length).toBe(1); - }); - test("create a message redelegate - without cosmosSourceValidator", async () => { - await withLibcore(async (core) => { - try { - return await cosmosCreateMessage( - sourceAddresss, - { - ...commonTransaction, - amount: new BigNumber(0), - mode: "redelegate", - cosmosSourceValidator: null, - validators: [ - { - amount: new BigNumber(3000), - address: "", - }, - ], - } as Transaction, - core, - currency - ); - } catch (e: any) { - expect(e.message).toBe("source validator is empty"); - } - }); - }); - test("create a message redelegate", async () => { - const messages = await withLibcore(async (core) => { - return await cosmosCreateMessage( - sourceAddresss, - { - ...commonTransaction, - amount: new BigNumber(3000), - mode: "redelegate", - cosmosSourceValidator: "source", - validators: [ - { - amount: new BigNumber(3000), - address: "", - }, - ], - } as Transaction, - core, - currency - ); - }); - expect(messages[0].constructor.name).toBe("NJSCosmosLikeMessage"); - expect(messages.length).toBe(1); - }); - test("create a message claimReward", async () => { - const messages = await withLibcore(async (core) => { - return await cosmosCreateMessage( - sourceAddresss, - { - ...commonTransaction, - amount: new BigNumber(0), - mode: "claimReward", - validators: [ - { - amount: new BigNumber(0), - address: "", - }, - ], - } as Transaction, - core, - currency - ); - }); - expect(messages[0].constructor.name).toBe("NJSCosmosLikeMessage"); - expect(messages.length).toBe(1); - }); - test("create a message claimRewardCompound", async () => { - const messages = await withLibcore(async (core) => { - return await cosmosCreateMessage( - sourceAddresss, - { - ...commonTransaction, - amount: new BigNumber(0), - mode: "claimRewardCompound", - validators: [ - { - amount: new BigNumber(0), - address: "", - }, - ], - } as Transaction, - core, - currency - ); - }); - expect(messages[0].constructor.name).toBe("NJSCosmosLikeMessage"); - expect(messages[1].constructor.name).toBe("NJSCosmosLikeMessage"); - expect(messages.length).toBe(2); - }); - }); -}; diff --git a/src/families/cosmos/types.ts b/src/families/cosmos/types.ts index 4d5df12fbc..3a3d82b775 100644 --- a/src/families/cosmos/types.ts +++ b/src/families/cosmos/types.ts @@ -4,28 +4,7 @@ import type { TransactionCommonRaw, } from "../../types/transaction"; import type { Operation, OperationRaw } from "../../types/operation"; -import type { CoreAmount, CoreBigInt, Spec } from "../../libcore/types"; -export type CoreStatics = { - CosmosLikeOperation: CoreCosmosLikeOperation; - CosmosLikeAddress: CoreCosmosLikeAddress; - CosmosLikeTransactionBuilder: CoreCosmosLikeTransactionBuilder; - CosmosLikeTransaction: CoreCosmosLikeTransaction; - CosmosLikeMessage: CoreCosmosLikeMessage; - CosmosLikeMsgWithdrawDelegationReward: CosmosMsgWithdrawDelegationReward; - CosmosLikeAmount: CoreCosmosLikeAmount; - CosmosLikeMsgSend: CosmosMsgSend; - CosmosLikeMsgDelegate: CosmosMsgDelegate; - CosmosLikeMsgUndelegate: CosmosMsgUndelegate; - CosmosLikeMsgBeginRedelegate: CosmosMsgRedelegate; - CosmosGasLimitRequest: CoreCosmosGasLimitRequest; -}; -export type CoreAccountSpecifics = { - asCosmosLikeAccount(): Promise; -}; -export type CoreOperationSpecifics = { - asCosmosLikeOperation(): Promise; -}; -export type CoreCurrencySpecifics = Record; + export type CosmosDelegationStatus = | "bonded" // in the active set that generates rewards | "unbonding" // doesn't generate rewards. means the validator has been removed from the active set, but has its voting power "frozen" in case they misbehaved (just like a delegator undelegating). This last 21 days @@ -137,19 +116,6 @@ export type CosmosExtraTxInfo = { validator?: CosmosDelegationInfo; }; -export type CosmosDelegateTxInfo = { - validators: CosmosDelegationInfo[]; -}; -export type CosmosUndelegateTxInfo = { - validators: CosmosDelegationInfo[]; -}; -export type CosmosRedelegateTxInfo = { - validators: CosmosDelegationInfo[]; - cosmosSourceValidator: string | null | undefined; -}; -export type CosmosClaimRewardsTxInfo = { - validator: CosmosDelegationInfo; -}; export type CosmosDelegationInfo = { address: string; amount: BigNumber; @@ -158,210 +124,7 @@ export type CosmosDelegationInfoRaw = { address: string; amount: string; }; -export type CosmosMessage = CoreCosmosLikeMessage; - -declare class CosmosMsgSend { - init( - fromAddress: string, - toAddress: string, - amount: CoreCosmosLikeAmount[] - ): Promise; - fromAddress: string; - toAddress: string; - amount: Array; -} - -declare class CosmosMsgDelegate { - init( - delegatorAddress: string, - validatorAddress: string, - amount: CoreCosmosLikeAmount - ): Promise; - getValidatorAddress(): Promise; - getAmount(): Promise; - delegatorAddress: string; - validatorAddress: string; - amount: CoreCosmosLikeAmount; -} - -export type CosmosMsgUndelegate = CosmosMsgDelegate; -type CosmosAmount = { - getAmount(): Promise; - amount: string; - denom: string; -}; - -declare class CoreCosmosGasLimitRequest { - init( - memo: string, - messages: CoreCosmosLikeMessage[], - amplifier: string - ): Promise; -} - -declare class CosmosMsgRedelegate { - init( - delegatorAddress: string, - validatorSourceAddress: string, - validatorDestinationAddress: string, - amount: CoreCosmosLikeAmount - ): Promise; - getValidatorDestinationAddress(): Promise; - getValidatorSourceAddress(): Promise; - getAmount(): Promise; - delegatorAddress: string; - validatorSourceAddress: string; - validatorDestinationAddress: string; - amount: CoreCosmosLikeAmount; -} - -declare class CoreCosmosLikeAmount { - init(amount: string, denom: string): Promise; - getAmount(): Promise; - amount: string; - denom: string; -} - -declare class CosmosMsgWithdrawDelegationReward { - init( - delegatorAddress: string, - validatorAddress: string - ): Promise; - getValidatorAddress(): Promise; - delegatorAddress: string; - validatorAddress: string; -} - -type CosmosLikeEntry = { - // Block height of the begin redelegate request - getCreationHeight(): Promise; - // Timestamp of the redelegation completion - getCompletionTime(): Date; - // Balance requested to redelegate - getInitialBalance(): Promise; - // Current amount being redelegated (i.e. less than initialBalance if slashed) - getBalance(): Promise; -}; -export type CosmosLikeRedelegation = { - getDelegatorAddress(): string; - getSrcValidatorAddress(): string; - getDstValidatorAddress(): string; - getEntries(): CosmosLikeEntry[]; -}; -export type CosmosLikeUnbonding = { - getDelegatorAddress(): string; - getValidatorAddress(): string; - getEntries(): CosmosLikeEntry[]; -}; -export type CosmosLikeDelegation = { - getDelegatorAddress(): string; - getValidatorAddress(): string; - getDelegatedAmount(): CoreAmount; -}; - -declare class CoreCosmosLikeAddress { - toBech32(): Promise; -} - -declare class CoreCosmosLikeOperation { - getTransaction(): Promise; - getMessage(): Promise; -} - -declare class CoreCosmosLikeMsgType {} -declare class CoreCosmosLikeMessage { - getIndex(): Promise; - getMessageType(): Promise; - getRawMessageType(): Promise; - wrapMsgSend(message: CosmosMsgSend): Promise; - wrapMsgDelegate(message: CosmosMsgDelegate): Promise; - wrapMsgUndelegate( - message: CosmosMsgUndelegate - ): Promise; - wrapMsgBeginRedelegate( - message: CosmosMsgRedelegate - ): Promise; - wrapMsgWithdrawDelegationReward( - message: CosmosMsgWithdrawDelegationReward - ): Promise; - unwrapMsgDelegate(msg: CosmosMessage): Promise; - unwrapMsgBeginRedelegate(msg: CosmosMessage): Promise; - unwrapMsgUndelegate(msg: CosmosMessage): Promise; - unwrapMsgWithdrawDelegationReward( - msg: CosmosMessage - ): Promise; -} - -declare class CoreCosmosLikeTransactionBuilder { - setMemo(memo: string): Promise; - setSequence(sequence: string): Promise; - setAccountNumber( - accountNumber: string - ): Promise; - addMessage( - message: CoreCosmosLikeMessage - ): Promise; - setFee(fees: CoreAmount): Promise; - setGas(gas: CoreAmount): Promise; - build(): Promise; -} - -declare class CoreCosmosLikeTransaction { - toRawTransaction(): string; - toSignatureBase(): Promise; - getHash(): Promise; - getMemo(): Promise; - getFee(): Promise; - getGas(): Promise; - serializeForSignature(): Promise; - serializeForBroadcast(type: "block" | "async" | "sync"): Promise; - setSignature(arg0: string, arg1: string): Promise; - setDERSignature(arg0: string): Promise; -} - -declare class CosmosLikeReward { - getDelegatorAddress(): string; - getValidatorAddress(): string; - getRewardAmount(): CoreAmount; -} - -export type CosmosLikeValidator = { - activeStatus: string; - getActiveStatus(): Promise; -}; -export type CosmosBroadcastResponse = { - code: number; - raw_log: string; - txhash: string; -}; - -declare class CoreCosmosLikeAccount { - buildTransaction(): Promise; - broadcastRawTransaction(signed: string): Promise; - broadcastTransaction(signed: string): Promise; - getEstimatedGasLimit( - transaction: CoreCosmosLikeTransaction - ): Promise; - estimateGas(request: CoreCosmosGasLimitRequest): Promise; - getBaseReserve(): Promise; - isAddressActivated(address: string): Promise; - getSequence(): Promise; - getAccountNumber(): Promise; - getPendingRewards(): Promise; - getRedelegations(): Promise; - getUnbondings(): Promise; - getDelegations(): Promise; - getValidatorInfo(validatorAddress: string): Promise; -} - -export type { - CoreCosmosLikeAccount, - CoreCosmosLikeAddress, - CoreCosmosLikeOperation, - CoreCosmosLikeTransaction, - CoreCosmosLikeTransactionBuilder, -}; export type Transaction = TransactionCommon & { family: "cosmos"; mode: CosmosOperationMode; @@ -429,384 +192,3 @@ export type CosmosMappedValidator = { export type CosmosSearchFilter = ( query: string ) => (delegation: CosmosMappedDelegation | CosmosMappedValidator) => boolean; -export const reflect = ( - declare: (arg0: string, arg1: Spec) => void -): { - OperationMethods: { - asCosmosLikeOperation: { - returns: "CosmosLikeOperation"; - }; - }; - AccountMethods: { - asCosmosLikeAccount: { - returns: "CosmosLikeAccount"; - }; - }; -} => { - declare("CosmosLikeTransactionBuilder", { - methods: { - addMessage: { - params: ["CosmosLikeMessage"], - }, - build: { - returns: "CosmosLikeTransaction", - }, - setMemo: {}, - setSequence: {}, - setAccountNumber: {}, - setFee: { - params: ["Amount"], - }, - setGas: { - params: ["Amount"], - }, - }, - }); - declare("CosmosGasLimitRequest", { - njsUsesPlainObject: true, - statics: { - init: { - params: [null, ["CosmosLikeMessage"], null], - returns: "CosmosGasLimitRequest", - njsInstanciateClass: [ - { - memo: 0, - messages: 1, - amplifier: 2, - }, - ], - }, - }, - }); - declare("CosmosLikeAccount", { - methods: { - estimateGas: { - params: ["CosmosGasLimitRequest"], - returns: "BigInt", - }, - buildTransaction: { - returns: "CosmosLikeTransactionBuilder", - }, - broadcastRawTransaction: {}, - broadcastTransaction: {}, - getEstimatedGasLimit: { - params: ["CosmosLikeTransaction"], - }, - getSequence: {}, - getAccountNumber: {}, - getPendingRewards: { - returns: ["CosmosLikeReward"], - }, - getRedelegations: { - returns: ["CosmosLikeRedelegation"], - }, - getUnbondings: { - returns: ["CosmosLikeUnbonding"], - }, - getDelegations: { - returns: ["CosmosLikeDelegation"], - }, - getValidatorInfo: { - returns: "CosmosLikeValidator", - }, - }, - }); - declare("CosmosLikeValidator", { - njsUsesPlainObject: true, - methods: { - getActiveStatus: { - njsField: "activeStatus", - }, - }, - }); - declare("CosmosLikeReward", { - methods: { - getDelegatorAddress: {}, - getValidatorAddress: {}, - getRewardAmount: { - returns: "Amount", - }, - }, - }); - declare("CosmosLikeUnbonding", { - methods: { - getDelegatorAddress: {}, - getValidatorAddress: {}, - getEntries: { - returns: ["CosmosLikeUnbondingEntry"], - }, - }, - }); - declare("CosmosLikeTransaction", { - methods: { - getHash: {}, - getMemo: {}, - setDERSignature: { - params: ["hex"], - }, - getFee: { - returns: "Amount", - }, - getGas: { - returns: "Amount", - }, - serializeForSignature: {}, - serializeForBroadcast: {}, - }, - }); - declare("CosmosLikeOperation", { - methods: { - getTransaction: { - returns: "CosmosLikeTransaction", - }, - getMessage: { - returns: "CosmosLikeMessage", - }, - }, - }); - declare("CosmosLikeRedelegationEntry", { - methods: { - getInitialBalance: { - returns: "BigInt", - }, - getCompletionTime: {}, - }, - }); - declare("CosmosLikeUnbondingEntry", { - methods: { - getInitialBalance: { - returns: "BigInt", - }, - getCompletionTime: {}, - }, - }); - declare("CosmosLikeRedelegation", { - methods: { - getDelegatorAddress: { - returns: "string", - }, - getSrcValidatorAddress: { - returns: "string", - }, - getDstValidatorAddress: { - returns: "string", - }, - getEntries: { - returns: ["CosmosLikeRedelegationEntry"], - }, - }, - }); - declare("CosmosLikeDelegation", { - methods: { - getDelegatorAddress: {}, - getValidatorAddress: {}, - getDelegatedAmount: { - returns: "Amount", - }, - }, - }); - declare("CosmosLikeMessage", { - statics: { - wrapMsgSend: { - params: ["CosmosLikeMsgSend"], - returns: "CosmosLikeMessage", - njsBuggyMethodIsNotStatic: true, - }, - wrapMsgDelegate: { - params: ["CosmosLikeMsgDelegate"], - returns: "CosmosLikeMessage", - njsBuggyMethodIsNotStatic: true, - }, - wrapMsgUndelegate: { - params: ["CosmosLikeMsgUndelegate"], - returns: "CosmosLikeMessage", - njsBuggyMethodIsNotStatic: true, - }, - wrapMsgBeginRedelegate: { - params: ["CosmosLikeMsgBeginRedelegate"], - returns: "CosmosLikeMessage", - njsBuggyMethodIsNotStatic: true, - }, - wrapMsgWithdrawDelegationReward: { - params: ["CosmosLikeMsgWithdrawDelegationReward"], - returns: "CosmosLikeMessage", - njsBuggyMethodIsNotStatic: true, - }, - unwrapMsgDelegate: { - params: ["CosmosLikeMessage"], - returns: "CosmosLikeMsgDelegate", - njsBuggyMethodIsNotStatic: true, - }, - unwrapMsgBeginRedelegate: { - params: ["CosmosLikeMessage"], - returns: "CosmosLikeMsgBeginRedelegate", - njsBuggyMethodIsNotStatic: true, - }, - unwrapMsgUndelegate: { - params: ["CosmosLikeMessage"], - returns: "CosmosLikeMsgUndelegate", - njsBuggyMethodIsNotStatic: true, - }, - unwrapMsgWithdrawDelegationReward: { - params: ["CosmosLikeMessage"], - returns: "CosmosLikeMsgWithdrawDelegationReward", - njsBuggyMethodIsNotStatic: true, - }, - }, - methods: { - getMessageType: {}, - getRawMessageType: {}, - getIndex: {}, - }, - }); - declare("CosmosLikeAmount", { - njsUsesPlainObject: true, - statics: { - init: { - params: [null, null], - returns: "CosmosLikeAmount", - njsInstanciateClass: [ - { - amount: 0, - denom: 1, - }, - ], - }, - }, - methods: { - getAmount: { - njsField: "amount", - }, - }, - }); - declare("CosmosLikeMsgSend", { - njsUsesPlainObject: true, - statics: { - init: { - params: [null, null, ["CosmosLikeAmount"]], - returns: "CosmosLikeMsgSend", - njsInstanciateClass: [ - { - fromAddress: 0, - toAddress: 1, - amount: 2, - }, - ], - }, - }, - }); - declare("CosmosLikeMsgDelegate", { - njsUsesPlainObject: true, - statics: { - init: { - params: [null, null, "CosmosLikeAmount"], - returns: "CosmosLikeMsgDelegate", - njsInstanciateClass: [ - { - delegatorAddress: 0, - validatorAddress: 1, - amount: 2, - }, - ], - }, - }, - methods: { - getValidatorAddress: { - njsField: "validatorAddress", - }, - getAmount: { - njsField: "amount", - returns: "CosmosLikeAmount", - }, - }, - }); - declare("CosmosLikeMsgBeginRedelegate", { - njsUsesPlainObject: true, - statics: { - init: { - params: [null, null, null, "CosmosLikeAmount"], - returns: "CosmosLikeMsgBeginRedelegate", - njsInstanciateClass: [ - { - delegatorAddress: 0, - validatorSourceAddress: 1, - validatorDestinationAddress: 2, - amount: 3, - }, - ], - }, - }, - methods: { - getValidatorDestinationAddress: { - njsField: "validatorDestinationAddress", - }, - getValidatorSourceAddress: { - njsField: "validatorSourceAddress", - }, - getAmount: { - njsField: "amount", - returns: "CosmosLikeAmount", - }, - }, - }); - declare("CosmosLikeMsgUndelegate", { - njsUsesPlainObject: true, - statics: { - init: { - params: [null, null, "CosmosLikeAmount"], - returns: "CosmosLikeMsgUndelegate", - njsInstanciateClass: [ - { - delegatorAddress: 0, - validatorAddress: 1, - amount: 2, - }, - ], - }, - }, - methods: { - getValidatorAddress: { - njsField: "validatorAddress", - }, - getAmount: { - njsField: "amount", - returns: "CosmosLikeAmount", - }, - }, - }); - declare("CosmosLikeMsgWithdrawDelegationReward", { - njsUsesPlainObject: true, - statics: { - init: { - params: [null, null], - returns: "CosmosLikeMsgWithdrawDelegationReward", - njsInstanciateClass: [ - { - delegatorAddress: 0, - validatorAddress: 1, - }, - ], - }, - }, - methods: { - getDelegatorAddress: { - njsField: "delegatorAddress", - }, - getValidatorAddress: { - njsField: "validatorAddress", - }, - }, - }); - return { - OperationMethods: { - asCosmosLikeOperation: { - returns: "CosmosLikeOperation", - }, - }, - AccountMethods: { - asCosmosLikeAccount: { - returns: "CosmosLikeAccount", - }, - }, - }; -}; diff --git a/src/families/crypto_org/types.ts b/src/families/crypto_org/types.ts index 9bfe2d1b98..1a49b7ce13 100644 --- a/src/families/crypto_org/types.ts +++ b/src/families/crypto_org/types.ts @@ -3,10 +3,7 @@ import type { TransactionCommon, TransactionCommonRaw, } from "../../types/transaction"; -export type CoreStatics = Record; -export type CoreAccountSpecifics = Record; -export type CoreOperationSpecifics = Record; -export type CoreCurrencySpecifics = Record; + export type CryptoOrgResources = { bondedBalance: BigNumber; redelegatingBalance: BigNumber; @@ -38,4 +35,3 @@ export type NetworkInfo = { export type NetworkInfoRaw = { family: "crypto_org"; }; -export const reflect = (_declare: any) => {}; diff --git a/src/families/elrond/types.ts b/src/families/elrond/types.ts index 9e3bd2ace6..71c8f9e99b 100644 --- a/src/families/elrond/types.ts +++ b/src/families/elrond/types.ts @@ -4,10 +4,7 @@ import type { TransactionCommon, TransactionCommonRaw, } from "../../types/transaction"; -export type CoreStatics = Record; -export type CoreAccountSpecifics = Record; -export type CoreOperationSpecifics = Record; -export type CoreCurrencySpecifics = Record; + export type ElrondResources = { nonce: number; }; @@ -74,4 +71,3 @@ export type NetworkInfoRaw = { export type ElrondPreloadData = { validators: Record; }; -export const reflect = (_declare: any) => {}; diff --git a/src/families/ethereum/types.ts b/src/families/ethereum/types.ts index 3d358c0c31..5702dfbe76 100644 --- a/src/families/ethereum/types.ts +++ b/src/families/ethereum/types.ts @@ -8,6 +8,7 @@ import type { TransactionMode, ModeModule } from "./modules"; import type { Range, RangeRaw } from "../../range"; import type { CryptoCurrency } from "../../types"; import type { DerivationMode } from "../../derivation"; + export type EthereumGasLimitRequest = { from?: string; to?: string; @@ -91,9 +92,3 @@ export type TypedMessageData = { stringHash: string; }; }; -// -export type CoreStatics = Record; -export type CoreAccountSpecifics = Record; -export type CoreOperationSpecifics = Record; -export type CoreCurrencySpecifics = Record; -export const reflect = (_declare: any) => {}; diff --git a/src/families/filecoin/customAddressValidation.ts b/src/families/filecoin/customAddressValidation.ts deleted file mode 100644 index c3021b8aa7..0000000000 --- a/src/families/filecoin/customAddressValidation.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { Core } from "../../libcore/types"; -import type { CryptoCurrency } from "../../types"; -import { InvalidAddress } from "@ledgerhq/errors"; -import { validateAddress } from "./bridge/utils/addresses"; -export default async ( - core: Core, - arg: { - currency: CryptoCurrency; - recipient: string; - } -): Promise => { - if (validateAddress(arg.recipient).isValid) return Promise.resolve(null); - - return Promise.reject( - new InvalidAddress(undefined, { - currencyName: arg.currency.name, - }) - ); -}; diff --git a/src/families/filecoin/types.ts b/src/families/filecoin/types.ts index 12512728f6..c3e9c8c69b 100644 --- a/src/families/filecoin/types.ts +++ b/src/families/filecoin/types.ts @@ -38,9 +38,3 @@ export type TransactionRaw = TransactionCommonRaw & { }; export type BroadcastFnSignature = (arg0: BroadcastArg0) => Promise; - -export type CoreStatics = Record; -export type CoreAccountSpecifics = Record; -export type CoreOperationSpecifics = Record; -export type CoreCurrencySpecifics = Record; -export const reflect = (_declare: any) => {}; diff --git a/src/families/neo/types.ts b/src/families/neo/types.ts index 238875cd03..130b2d4f12 100644 --- a/src/families/neo/types.ts +++ b/src/families/neo/types.ts @@ -2,10 +2,7 @@ import type { TransactionCommon, TransactionCommonRaw, } from "../../types/transaction"; -export type CoreStatics = Record; -export type CoreAccountSpecifics = Record; -export type CoreOperationSpecifics = Record; -export type CoreCurrencySpecifics = Record; + export type NetworkInfo = { family: "neo"; }; @@ -18,4 +15,3 @@ export type Transaction = TransactionCommon & { export type TransactionRaw = TransactionCommonRaw & { family: "neo"; }; -export const reflect = (_declare: any) => {}; diff --git a/src/families/polkadot/types.ts b/src/families/polkadot/types.ts index 034cab2db5..dc9f099733 100644 --- a/src/families/polkadot/types.ts +++ b/src/families/polkadot/types.ts @@ -3,10 +3,7 @@ import type { TransactionCommon, TransactionCommonRaw, } from "../../types/transaction"; -export type CoreStatics = Record; -export type CoreAccountSpecifics = Record; -export type CoreOperationSpecifics = Record; -export type CoreCurrencySpecifics = Record; + export type RewardDestinationType = | "Staked" | "Stash" @@ -107,4 +104,3 @@ export type PolkadotPreloadData = { export type PolkadotSearchFilter = ( query: string ) => (validator: PolkadotValidator) => boolean; -export const reflect = (_declare: any): void => {}; diff --git a/src/families/ripple/types.ts b/src/families/ripple/types.ts index d86cfaf08b..fa53ce8291 100644 --- a/src/families/ripple/types.ts +++ b/src/families/ripple/types.ts @@ -4,65 +4,7 @@ import type { TransactionCommon, TransactionCommonRaw, } from "../../types/transaction"; -import type { CoreAmount, CoreBigInt, Spec } from "../../libcore/types"; -declare class CoreRippleLikeAddress { - toBase58(): Promise; -} - -declare class CoreRippleLikeTransaction { - getHash(): Promise; - getFees(): Promise; - getReceiver(): Promise; - getSender(): Promise; - serialize(): Promise; - setSignature(arg0: string, arg1: string): Promise; - setDERSignature(arg0: string): Promise; - getDestinationTag(): Promise; - getSequence(): Promise; -} - -declare class CoreRippleLikeOperation { - getTransaction(): Promise; -} - -declare class CoreRippleLikeTransactionBuilder { - wipeToAddress(address: string): Promise; - sendToAddress(amount: CoreAmount, recipient: string): Promise; - setDestinationTag(tag: number): Promise; - setFees(fees: CoreAmount): Promise; - build(): Promise; -} - -declare class CoreRippleLikeAccount { - buildTransaction(): Promise; - broadcastRawTransaction(signed: string): Promise; - getFees(): Promise; - getBaseReserve(): Promise; - isAddressActivated(address: string): Promise; -} - -export type CoreStatics = { - RippleLikeOperation: CoreRippleLikeOperation; - RippleLikeAddress: CoreRippleLikeAddress; - RippleLikeTransaction: CoreRippleLikeTransaction; - RippleLikeAccount: CoreRippleLikeAccount; - RippleLikeTransactionBuilder: CoreRippleLikeTransactionBuilder; -}; -export type { - CoreRippleLikeAccount, - CoreRippleLikeAddress, - CoreRippleLikeOperation, - CoreRippleLikeTransaction, - CoreRippleLikeTransactionBuilder, -}; -export type CoreAccountSpecifics = { - asRippleLikeAccount(): Promise; -}; -export type CoreOperationSpecifics = { - asRippleLikeOperation(): Promise; -}; -export type CoreCurrencySpecifics = Record; export type NetworkInfo = { family: "ripple"; serverFee: BigNumber; @@ -87,88 +29,3 @@ export type TransactionRaw = TransactionCommonRaw & { tag: number | null | undefined; feeCustomUnit: Unit | null | undefined; }; -export const reflect = (declare: (arg0: string, arg1: Spec) => void) => { - declare("RippleLikeAddress", { - methods: { - toBase58: {}, - }, - }); - declare("RippleLikeOperation", { - methods: { - getTransaction: { - returns: "RippleLikeTransaction", - }, - }, - }); - declare("RippleLikeTransaction", { - methods: { - getHash: {}, - getDestinationTag: {}, - getSequence: { - returns: "BigInt", - }, - getFees: { - returns: "Amount", - }, - getReceiver: { - returns: "RippleLikeAddress", - }, - getSender: { - returns: "RippleLikeAddress", - }, - serialize: { - returns: "hex", - }, - setSignature: { - params: ["hex", "hex"], - }, - setDERSignature: { - params: ["hex"], - }, - }, - }); - declare("RippleLikeTransactionBuilder", { - methods: { - wipeToAddress: {}, - sendToAddress: { - params: ["Amount"], - }, - setFees: { - params: ["Amount"], - }, - setDestinationTag: {}, - build: { - returns: "RippleLikeTransaction", - }, - }, - }); - declare("RippleLikeAccount", { - methods: { - buildTransaction: { - returns: "RippleLikeTransactionBuilder", - }, - broadcastRawTransaction: { - params: ["hex"], - }, - getFees: { - returns: "Amount", - }, - getBaseReserve: { - returns: "Amount", - }, - isAddressActivated: {}, - }, - }); - return { - OperationMethods: { - asRippleLikeOperation: { - returns: "RippleLikeOperation", - }, - }, - AccountMethods: { - asRippleLikeAccount: { - returns: "RippleLikeAccount", - }, - }, - }; -}; diff --git a/src/families/solana/types.ts b/src/families/solana/types.ts index 34659ecd55..9d9f22027c 100644 --- a/src/families/solana/types.ts +++ b/src/families/solana/types.ts @@ -3,12 +3,6 @@ import type { TransactionCommonRaw, } from "../../types/transaction"; -// for legacy reasons export the types -export type CoreStatics = Record; -export type CoreAccountSpecifics = Record; -export type CoreOperationSpecifics = Record; -export type CoreCurrencySpecifics = Record; - export type TransferCommand = { kind: "transfer"; sender: string; @@ -106,5 +100,3 @@ export type TransactionRaw = TransactionCommonRaw & { lamportsPerSignature: number; }; }; - -export const reflect = (_declare: unknown): void => {}; diff --git a/src/families/stellar/api/horizon.ts b/src/families/stellar/api/horizon.ts index a5e797f8c4..f8f32ba8c7 100644 --- a/src/families/stellar/api/horizon.ts +++ b/src/families/stellar/api/horizon.ts @@ -7,7 +7,8 @@ import StellarSdk, { } from "stellar-sdk"; import { getEnv } from "../../../env"; import { getCryptoCurrencyById, parseCurrencyUnit } from "../../../currencies"; -import type { Account, NetworkInfo, Operation } from "../../../types"; +import type { Account, Operation } from "../../../types"; +import type { NetworkInfo } from "../types"; import { getAccountSpendableBalance, rawOperationsToOperations, diff --git a/src/families/stellar/test-dataset.ts b/src/families/stellar/test-dataset.ts index 89bdb67043..e9435231cf 100644 --- a/src/families/stellar/test-dataset.ts +++ b/src/families/stellar/test-dataset.ts @@ -322,11 +322,9 @@ const dataset: DatasetTest = { ], }, { - FIXME_tests: [ - "balance is sum of ops", // https://github.com/LedgerHQ/lib-ledger-core/pull/580 - ], + FIXME_tests: ["balance is sum of ops"], raw: { - id: "libcore:1:stellar:f30b743cb3a8bc8c3ea8fe8455c6a52221cc6cf867f7f1f5861dd52aba1d0b8a:sep5", + id: "js:1:stellar:f30b743cb3a8bc8c3ea8fe8455c6a52221cc6cf867f7f1f5861dd52aba1d0b8a:sep5", seedIdentifier: "gre", name: "GRE's Stellar 2", xpub: "f30b743cb3a8bc8c3ea8fe8455c6a52221cc6cf867f7f1f5861dd52aba1d0b8a", diff --git a/src/families/stellar/types.ts b/src/families/stellar/types.ts index 414211b12c..299982b1cd 100644 --- a/src/families/stellar/types.ts +++ b/src/families/stellar/types.ts @@ -3,10 +3,6 @@ import type { TransactionCommon, TransactionCommonRaw, } from "../../types/transaction"; -export type CoreStatics = Record; -export type CoreAccountSpecifics = Record; -export type CoreOperationSpecifics = Record; -export type CoreCurrencySpecifics = Record; export type NetworkInfo = { family: "stellar"; @@ -43,4 +39,3 @@ export type TransactionRaw = TransactionCommonRaw & { memoType: string | null | undefined; memoValue: string | null | undefined; }; -export const reflect = (_declare: any): void => {}; diff --git a/src/families/tezos/bridge/js.ts b/src/families/tezos/bridge/js.ts index 153112e434..e301d72061 100644 --- a/src/families/tezos/bridge/js.ts +++ b/src/families/tezos/bridge/js.ts @@ -16,8 +16,10 @@ import { InvalidAddressBecauseDestinationIsAlsoSource, RecommendUndelegation, NotEnoughBalanceBecauseDestinationNotCreated, + RecipientRequired, + InvalidAddress, } from "@ledgerhq/errors"; -import { validateRecipient } from "../../../bridge/shared"; +import { validateAddress, ValidationResult } from "@taquito/utils"; import type { CurrencyBridge, AccountBridge, @@ -41,6 +43,19 @@ import { log } from "@ledgerhq/logs"; import { InvalidAddressBecauseAlreadyDelegated } from "../../../errors"; import api from "../api/tzkt"; +const validateRecipient = (currency, recipient) => { + let recipientError = null; + const recipientWarning = null; + if (!recipient) { + recipientError = new RecipientRequired(""); + } else if (validateAddress(recipient) !== ValidationResult.VALID) { + recipientError = new InvalidAddress(undefined, { + currencyName: currency.name, + }); + } + return Promise.resolve({ recipientError, recipientWarning }); +}; + const receive = makeAccountBridgeReceive(); const EXISTENTIAL_DEPOSIT = new BigNumber(275000); diff --git a/src/families/tezos/bridge/libcore.ts b/src/families/tezos/bridge/libcore.ts deleted file mode 100644 index 1d9a4b20da..0000000000 --- a/src/families/tezos/bridge/libcore.ts +++ /dev/null @@ -1,312 +0,0 @@ -import invariant from "invariant"; -import { BigNumber } from "bignumber.js"; -import { - AmountRequired, - NotEnoughBalance, - NotEnoughBalanceToDelegate, - NotEnoughBalanceInParentAccount, - FeeNotLoaded, - FeeTooHigh, - NotSupportedLegacyAddress, - InvalidAddressBecauseDestinationIsAlsoSource, - RecommendSubAccountsToEmpty, - RecommendUndelegation, -} from "@ledgerhq/errors"; -import { validateRecipient } from "../../../bridge/shared"; -import type { Account, AccountBridge, CurrencyBridge } from "../../../types"; -import type { Transaction } from "../types"; -import { scanAccounts } from "../../../libcore/scanAccounts"; -import { getAccountNetworkInfo } from "../../../libcore/getAccountNetworkInfo"; -import { sync } from "../../../libcore/syncAccount"; -import { getFeesForTransaction } from "../../../libcore/getFeesForTransaction"; -import broadcast from "../libcore-broadcast"; -import signOperation from "../libcore-signOperation"; -import { makeLRUCache } from "../../../cache"; -import { isAccountBalanceSignificant, getMainAccount } from "../../../account"; -import { withLibcore } from "../../../libcore/access"; -import { libcoreBigIntToBigNumber } from "../../../libcore/buildBigNumber"; -import { getCoreAccount } from "../../../libcore/getCoreAccount"; -import { fetchAllBakers, hydrateBakers, isAccountDelegating } from "../bakers"; -import { getEnv } from "../../../env"; -import { makeAccountBridgeReceive } from "../../../bridge/jsHelpers"; - -const receive = makeAccountBridgeReceive(); - -type EstimateGasLimitAndStorage = ( - arg0: Account, - arg1: string -) => Promise<{ - gasLimit: BigNumber; - storage: BigNumber; -}>; - -export const estimateGasLimitAndStorage: EstimateGasLimitAndStorage = - makeLRUCache( - (account, addr) => - withLibcore(async (core) => { - const { coreAccount } = await getCoreAccount(core, account); - const tezosLikeAccount = await coreAccount.asTezosLikeAccount(); - const gasLimit = await libcoreBigIntToBigNumber( - await tezosLikeAccount.getEstimatedGasLimit(addr) - ); - // for babylon network 257 is the current cost of sending to new account. - const storage = new BigNumber(257); - - /* - const storage = await libcoreBigIntToBigNumber( - await tezosLikeAccount.getStorage(addr) - ); - */ - return { - gasLimit, - storage, - }; - }), - (a, addr) => a.id + "|" + addr - ); -const calculateFees = makeLRUCache( - async (a, t) => { - return getFeesForTransaction({ - account: a, - transaction: t, - }); - }, - (a, t) => - `${a.id}_${t.amount.toString()}_${t.recipient}_${ - t.gasLimit ? t.gasLimit.toString() : "" - }_${t.fees ? t.fees.toString() : ""}_${ - t.storageLimit ? t.storageLimit.toString() : "" - }_${String(t.useAllAmount)}_${String(t.subAccountId)}` -); - -const createTransaction = (): Transaction => ({ - family: "tezos", - mode: "send", - amount: new BigNumber(0), - fees: null, - gasLimit: null, - storageLimit: null, - recipient: "", - networkInfo: null, - useAllAmount: false, - taquitoError: null, - estimatedFees: null, -}); - -const updateTransaction = (t, patch) => ({ ...t, ...patch }); - -const getTransactionStatus = async (a, t) => { - const errors: { - recipient?: Error; - amount?: Error; - fees?: Error; - } = {}; - const warnings: { - amount?: Error; - feeTooHigh?: Error; - recipient?: Error; - } = {}; - const subAcc = !t.subAccountId - ? null - : a.subAccounts && a.subAccounts.find((ta) => ta.id === t.subAccountId); - invariant( - t.mode === "send" || !subAcc, - "delegation features not supported for sub accounts" - ); - const account = subAcc || a; - - if (t.mode !== "undelegate") { - if (account.freshAddress === t.recipient) { - errors.recipient = new InvalidAddressBecauseDestinationIsAlsoSource(); - } else { - const { recipientError, recipientWarning } = await validateRecipient( - a.currency, - t.recipient - ); - - if (recipientError) { - errors.recipient = recipientError; - } - - if (recipientWarning) { - warnings.recipient = recipientWarning; - } - } - } - - if ( - !getEnv("LEGACY_KT_SUPPORT_TO_YOUR_OWN_RISK") && - t.recipient.startsWith("KT") && - !errors.recipient - ) { - errors.recipient = new NotSupportedLegacyAddress(); - } - - let estimatedFees = new BigNumber(0); - let amount = t.amount; - - if (!t.fees) { - errors.fees = new FeeNotLoaded(); - } else if (!errors.recipient) { - await calculateFees(a, t).then( - (res) => { - estimatedFees = res.estimatedFees; - amount = res.value; - }, - (error) => { - if (error.name === "NotEnoughBalance") { - errors.amount = error; - } else { - throw error; - } - } - ); - } - - if (!errors.amount && subAcc && estimatedFees.gt(a.balance)) { - errors.amount = new NotEnoughBalanceInParentAccount(); - } - - let totalSpent = !t.useAllAmount - ? t.amount.plus(estimatedFees) - : account.balance; - - if ( - !errors.recipient && - !errors.amount && - (amount.lt(0) || totalSpent.gt(account.balance)) - ) { - errors.amount = new NotEnoughBalance(); - totalSpent = new BigNumber(0); - amount = new BigNumber(0); - } - - if (t.mode === "send") { - if (!errors.amount && amount.eq(0)) { - errors.amount = new AmountRequired(); - } else if (amount.gt(0) && estimatedFees.times(10).gt(amount)) { - warnings.feeTooHigh = new FeeTooHigh(); - } - - const thresholdWarning = 0.5 * 10 ** a.currency.units[0].magnitude; - - if ( - !subAcc && - !errors.amount && - account.balance.minus(totalSpent).lt(thresholdWarning) - ) { - if (isAccountDelegating(account)) { - warnings.amount = new RecommendUndelegation(); - } else if ((a.subAccounts || []).some(isAccountBalanceSignificant)) { - warnings.amount = new RecommendSubAccountsToEmpty(); - } - } - } else { - // delegation case, we remap NotEnoughBalance to a more precise error - if (errors.amount instanceof NotEnoughBalance) { - errors.amount = new NotEnoughBalanceToDelegate(); - } - } - - return Promise.resolve({ - errors, - warnings, - estimatedFees, - amount, - totalSpent, - }); -}; - -const prepareTransaction = async (a, t) => { - let networkInfo = t.networkInfo; - - if (!networkInfo) { - const ni = await getAccountNetworkInfo(a); - invariant(ni.family === "tezos", "tezos networkInfo expected"); - networkInfo = ni; - } - - let gasLimit = t.gasLimit; - let storageLimit = t.storageLimit; - - if (!gasLimit || !storageLimit) { - const { recipientError } = - t.mode === "undelegate" - ? { recipientError: undefined } - : await validateRecipient(a.currency, t.recipient); - - if (!recipientError) { - const r = await estimateGasLimitAndStorage(a, t.recipient); - gasLimit = r.gasLimit; - storageLimit = r.storage; - } - } - - const fees = t.fees || networkInfo.fees; - - if ( - t.networkInfo !== networkInfo || - t.gasLimit !== gasLimit || - t.storageLimit !== storageLimit || - t.fees !== fees - ) { - return { ...t, networkInfo, storageLimit, gasLimit, fees }; - } - - return t; -}; - -const estimateMaxSpendable = async ({ - account, - parentAccount, - transaction, -}) => { - const mainAccount = getMainAccount(account, parentAccount); - const t = await prepareTransaction(mainAccount, { - ...createTransaction(), - subAccountId: account.type === "Account" ? null : account.id, - ...transaction, - // this seed is empty (worse case scenario is to send to new). addr from: 1. eyebrow 2. odor 3. rice 4. attack 5. loyal 6. tray 7. letter 8. harbor 9. resemble 10. sphere 11. system 12. forward 13. onion 14. buffalo 15. crumble - recipient: transaction?.recipient || "tz1VJitLYB31fEC82efFkLRU4AQUH9QgH3q6", - useAllAmount: true, - }); - const s = await getTransactionStatus(mainAccount, t); - return s.amount; -}; - -const preload = async () => { - const bakers = await fetchAllBakers(); - return { - bakers, - }; -}; - -const hydrate = (data: any) => { - if (!data || typeof data !== "object") return; - const { bakers } = data; - if (!bakers || typeof bakers !== "object" || !Array.isArray(bakers)) return; - hydrateBakers(bakers); -}; - -const currencyBridge: CurrencyBridge = { - preload, - hydrate, - scanAccounts, -}; - -const accountBridge: AccountBridge = { - createTransaction, - updateTransaction, - prepareTransaction, - getTransactionStatus, - estimateMaxSpendable, - sync, - receive, - signOperation, - broadcast, -}; - -export default { - currencyBridge, - accountBridge, -}; diff --git a/src/families/tezos/libcore-broadcast.ts b/src/families/tezos/libcore-broadcast.ts deleted file mode 100644 index c6a9359dca..0000000000 --- a/src/families/tezos/libcore-broadcast.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { Operation } from "../../types"; -import { makeBroadcast } from "../../libcore/broadcast"; -import { patchOperationWithHash } from "../../operation"; - -async function broadcast({ - coreAccount, - signedOperation: { operation, signature }, -}): Promise { - const tezosLikeAccount = await coreAccount.asTezosLikeAccount(); - const hash = await tezosLikeAccount.broadcastRawTransaction(signature); - return patchOperationWithHash(operation, hash); -} - -export default makeBroadcast({ - broadcast, -}); diff --git a/src/families/tezos/libcore-buildOperation.ts b/src/families/tezos/libcore-buildOperation.ts deleted file mode 100644 index 6bf0a6a95e..0000000000 --- a/src/families/tezos/libcore-buildOperation.ts +++ /dev/null @@ -1,52 +0,0 @@ -import type { Operation, OperationType } from "../../types"; -import type { CoreOperation } from "../../libcore/types"; -import { tezosOperationTag } from "./types"; -const opTagToType = { - [tezosOperationTag.OPERATION_TAG_REVEAL]: "REVEAL", - [tezosOperationTag.OPERATION_TAG_ORIGINATION]: "CREATE", - [tezosOperationTag.OPERATION_TAG_DELEGATION]: "DELEGATE", -}; - -async function tezosBuildOperation( - { - coreOperation, - }: { - coreOperation: CoreOperation; - }, - partialOp: Partial -) { - const tezosLikeOperation = await coreOperation.asTezosLikeOperation(); - const tezosLikeTransaction = await tezosLikeOperation.getTransaction(); - const status = await tezosLikeTransaction.getStatus(); - const tezosType = await tezosLikeTransaction.getType(); - const hash = await tezosLikeTransaction.getHash(); - const out: Partial = { - hash, - }; - let maybeCustomType = opTagToType[tezosType]; - - if ( - maybeCustomType === "DELEGATE" && - partialOp && - partialOp.recipients && - !partialOp.recipients[0] - ) { - maybeCustomType = "UNDELEGATE"; - } - - if (maybeCustomType === "IN" && partialOp?.value?.eq(0)) { - maybeCustomType = "NONE"; - } - - if (maybeCustomType) { - out.type = maybeCustomType as OperationType; - } - - if (status === 0) { - out.hasFailed = true; - } - - return out; -} - -export default tezosBuildOperation; diff --git a/src/families/tezos/libcore-buildSubAccounts.ts b/src/families/tezos/libcore-buildSubAccounts.ts deleted file mode 100644 index 889ea1385f..0000000000 --- a/src/families/tezos/libcore-buildSubAccounts.ts +++ /dev/null @@ -1,137 +0,0 @@ -import type { - CryptoCurrency, - ChildAccount, - Account, - BalanceHistoryCache, -} from "../../types"; -import type { CoreAccount, Core } from "../../libcore/types"; -import type { - CoreTezosLikeOriginatedAccount, - CoreTezosLikeAccount, -} from "./types"; -import { libcoreAmountToBigNumber } from "../../libcore/buildBigNumber"; -import { buildOperation } from "../../libcore/buildAccount/buildOperation"; -import { minimalOperationsBuilder } from "../../reconciliation"; -import { - shortAddressPreview, - generateHistoryFromOperations, -} from "../../account"; -const OperationOrderKey = { - date: 0, -}; - -async function buildOriginatedAccount({ - core, - parentAccountId, - currency, - coreOriginatedAccount, - existingOriginatedAccount, -}: { - core: Core; - parentAccountId: string; - currency: CryptoCurrency; - coreOriginatedAccount: CoreTezosLikeOriginatedAccount; - existingOriginatedAccount: ChildAccount; -}) { - const balance = await libcoreAmountToBigNumber( - await coreOriginatedAccount.getBalance() - ); - const address = await coreOriginatedAccount.getAddress(); - const query = await coreOriginatedAccount.queryOperations(); - await query.complete(); - await query.addOrder(OperationOrderKey.date, false); - const coreOperations = await query.execute(); - const id = `${parentAccountId}+${address}`; - const operations = await minimalOperationsBuilder( - (existingOriginatedAccount && existingOriginatedAccount.operations) || [], - coreOperations, - (coreOperation) => - buildOperation({ - core, - coreOperation, - accountId: id, - currency, - existingAccount: existingOriginatedAccount, - }) - ); - const swapHistory = existingOriginatedAccount?.swapHistory || []; - const originatedAccount: ChildAccount = { - type: "ChildAccount", - id, - name: shortAddressPreview(address), - starred: false, - parentId: parentAccountId, - currency, - address, - balance, - operationsCount: operations.length, - operations, - pendingOperations: [], - creationDate: - operations.length > 0 - ? operations[operations.length - 1].date - : new Date(), - swapHistory, - balanceHistoryCache: {} as BalanceHistoryCache, - }; - originatedAccount.balanceHistoryCache = - generateHistoryFromOperations(originatedAccount); - return originatedAccount; -} - -async function tezosBuildOriginatedAccount({ - core, - currency, - coreAccount, - accountId, - existingAccount, -}: { - core: Core; - currency: CryptoCurrency; - coreAccount: CoreAccount; - accountId: string; - existingAccount: Account | null | undefined; -}): Promise { - const originatedAccounts: ChildAccount[] = []; - const xtzAccount: CoreTezosLikeAccount = - await coreAccount.asTezosLikeAccount(); - const coreOAS: CoreTezosLikeOriginatedAccount[] = - await xtzAccount.getOriginatedAccounts(); - const existingAccountByAddress = {}; - const existingAccountAddresses: string[] = []; - - if (existingAccount && existingAccount.subAccounts) { - for (const existingSubAccount of existingAccount.subAccounts) { - if (existingSubAccount.type === "ChildAccount") { - const { address } = existingSubAccount; - existingAccountAddresses.push(address); - existingAccountByAddress[address] = existingSubAccount; - } - } - } - - for (const coreOA of coreOAS) { - const address = await coreOA.getAddress(); - const existingOriginatedAccount = existingAccountByAddress[address]; - const originatedAccount = await buildOriginatedAccount({ - core, - parentAccountId: accountId, - currency, - coreOriginatedAccount: coreOA, - existingOriginatedAccount, - }); - if (originatedAccount) originatedAccounts.push(originatedAccount); - } - - originatedAccounts.sort((a: any, b: any) => { - const i = existingAccountAddresses.indexOf(a.address); - const j = existingAccountAddresses.indexOf(b.address); - if (i === j) return 0; - if (i < 0) return 1; - if (j < 0) return -1; - return i - j; - }); - return originatedAccounts; -} - -export default tezosBuildOriginatedAccount; diff --git a/src/families/tezos/libcore-buildTransaction.ts b/src/families/tezos/libcore-buildTransaction.ts deleted file mode 100644 index 52aa874b62..0000000000 --- a/src/families/tezos/libcore-buildTransaction.ts +++ /dev/null @@ -1,154 +0,0 @@ -import invariant from "invariant"; -import { BigNumber } from "bignumber.js"; -import { FeeNotLoaded } from "@ledgerhq/errors"; -import type { Account } from "../../types"; -import { isValidRecipient } from "../../libcore/isValidRecipient"; -import { - bigNumberToLibcoreAmount, - bigNumberToLibcoreBigInt, -} from "../../libcore/buildBigNumber"; -import type { Core, CoreCurrency, CoreAccount } from "../../libcore/types"; -import type { - CoreTezosLikeOriginatedAccount, - CoreTezosLikeAccount, - CoreTezosLikeTransaction, - Transaction, -} from "./types"; -import { tezosOperationTag } from "./types"; -import { upperModulo } from "../../modulo"; - -export async function tezosBuildTransaction({ - account, - core, - coreAccount, - coreCurrency, - transaction, - isCancelled, -}: { - account: Account; - core: Core; - coreAccount: CoreAccount; - coreCurrency: CoreCurrency; - transaction: Transaction; - isPartial: boolean; - isCancelled: () => boolean; -}): Promise { - const { currency } = account; - const { recipient, fees, gasLimit, storageLimit, subAccountId } = transaction; - const subAccount = subAccountId - ? account.subAccounts && - account.subAccounts.find((t) => t.id === subAccountId) - : null; - let tezosAccount: - | (CoreTezosLikeAccount | null | undefined) - | (CoreTezosLikeOriginatedAccount | null | undefined); - const tezosLikeAccount = await coreAccount.asTezosLikeAccount(); - if (isCancelled()) return; - - if (subAccount && subAccount.type === "ChildAccount") { - const accounts = await tezosLikeAccount.getOriginatedAccounts(); - - for (const a of accounts) { - const addr = await a.getAddress(); - - if (addr === subAccount.address) { - tezosAccount = a; - break; - } - } - - invariant(tezosAccount, "sub account not found " + subAccount.id); - } else { - tezosAccount = tezosLikeAccount; - } - - if (transaction.mode !== "undelegate") { - await isValidRecipient({ - currency, - recipient, - }); - if (isCancelled()) return; - } - - if (!fees || !gasLimit || !storageLimit) { - throw new FeeNotLoaded(); - } - - const feesAmount = await bigNumberToLibcoreAmount(core, coreCurrency, fees); - if (isCancelled()) return; - let gasLimitRounded = gasLimit; - - if (["delegate", "undelegate"].includes(transaction.mode)) { - gasLimitRounded = upperModulo( - gasLimit, - new BigNumber(136), - new BigNumber(1000) - ); - } - - const gasLimitAmount = await bigNumberToLibcoreAmount( - core, - coreCurrency, - gasLimitRounded - ); - if (isCancelled()) return; - const storageBigInt = await bigNumberToLibcoreBigInt(core, storageLimit); - if (isCancelled()) return; - const transactionBuilder = await ( - tezosAccount as CoreTezosLikeAccount - ).buildTransaction(); - if (isCancelled()) return; - let type; - - switch (transaction.mode) { - case "send": - type = tezosOperationTag.OPERATION_TAG_TRANSACTION; - break; - - case "delegate": - case "undelegate": - invariant( - !transaction.useAllAmount, - "send max can't be used in delegation context" - ); - invariant( - transaction.amount.isZero(), - "amount must be ZERO in delegation context" - ); - type = tezosOperationTag.OPERATION_TAG_DELEGATION; - break; - - default: - throw new Error("Unsupported transaction.mode = " + transaction.mode); - } - - await transactionBuilder.setType(type); - if (isCancelled()) return; - - if (transaction.useAllAmount) { - await transactionBuilder.wipeToAddress(recipient); - if (isCancelled()) return; - } else { - if (!transaction.amount) throw new Error("amount is missing"); - const amount = await bigNumberToLibcoreAmount( - core, - coreCurrency, - new BigNumber(transaction.amount) - ); - if (isCancelled()) return; - await transactionBuilder.sendToAddress(amount, recipient); - if (isCancelled()) return; - } - - await transactionBuilder.setGasLimit(gasLimitAmount); - if (isCancelled()) return; - await transactionBuilder.setFees(feesAmount); - if (isCancelled()) return; - await transactionBuilder.setStorageLimit(storageBigInt); - if (isCancelled()) return; - const builded = await transactionBuilder.build(); - if (isCancelled()) return; - return builded; -} - -export default tezosBuildTransaction; diff --git a/src/families/tezos/libcore-getAccountNetworkInfo.ts b/src/families/tezos/libcore-getAccountNetworkInfo.ts deleted file mode 100644 index d245a38ef4..0000000000 --- a/src/families/tezos/libcore-getAccountNetworkInfo.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { BigNumber } from "bignumber.js"; -import type { Account } from "../../types"; -import type { NetworkInfo, CoreTezosLikeAccount } from "./types"; -import type { CoreAccount } from "../../libcore/types"; -import { libcoreBigIntToBigNumber } from "../../libcore/buildBigNumber"; -type Input = { - coreAccount: CoreAccount; - account: Account; -}; -type Output = NetworkInfo; - -async function tezos({ coreAccount }: Input): Promise { - const tezosLikeAccount: CoreTezosLikeAccount = - await coreAccount.asTezosLikeAccount(); - const bigInt = await tezosLikeAccount.getFees(); - const networkFees = await libcoreBigIntToBigNumber(bigInt); - // workaround of a bug on server side. set some boundaries. - const fees = BigNumber.min(BigNumber.max(2500, networkFees), 30000); - return { - family: "tezos", - fees, - }; -} - -export default tezos; diff --git a/src/families/tezos/libcore-getFeesForTransaction.ts b/src/families/tezos/libcore-getFeesForTransaction.ts deleted file mode 100644 index dab9bc6919..0000000000 --- a/src/families/tezos/libcore-getFeesForTransaction.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { Account } from "../../types"; -import type { Core, CoreCurrency, CoreAccount } from "../../libcore/types"; -import type { Transaction } from "./types"; -import { libcoreAmountToBigNumber } from "../../libcore/buildBigNumber"; -import buildTransaction from "./libcore-buildTransaction"; -import BigNumber from "bignumber.js"; - -async function tezos(args: { - account: Account; - core: Core; - coreAccount: CoreAccount; - coreCurrency: CoreCurrency; - transaction: Transaction; - isPartial: boolean; - isCancelled: () => boolean; -}): Promise<{ estimatedFees: BigNumber; value: BigNumber } | void> { - const builded = await buildTransaction(args); - if (!builded) return; - const value = await libcoreAmountToBigNumber(await builded.getValue()); - const estimatedFees = await libcoreAmountToBigNumber(await builded.getFees()); - return { - estimatedFees, - value, - }; -} - -export default tezos; diff --git a/src/families/tezos/libcore-signOperation.ts b/src/families/tezos/libcore-signOperation.ts deleted file mode 100644 index 6b991e9c06..0000000000 --- a/src/families/tezos/libcore-signOperation.ts +++ /dev/null @@ -1,122 +0,0 @@ -import invariant from "invariant"; -import Xtz from "@ledgerhq/hw-app-tezos"; -import type { CoreTezosLikeTransaction, Transaction } from "./types"; -import { makeSignOperation } from "../../libcore/signOperation"; -import { libcoreAmountToBigNumber } from "../../libcore/buildBigNumber"; -import buildTransaction from "./libcore-buildTransaction"; - -async function signTransaction({ - account, - transport, - transaction, - coreTransaction, - isCancelled, - onDeviceSignatureGranted, - onDeviceSignatureRequested, -}) { - const { freshAddressPath, balance, id, subAccounts } = account; - // Sign with the device - const hwApp = new Xtz(transport); - const serialized = await coreTransaction.serialize(); - onDeviceSignatureRequested(); - const { signature } = await hwApp.signOperation(freshAddressPath, serialized); - onDeviceSignatureGranted(); - if (isCancelled()) return; - await coreTransaction.setSignature(signature); - if (isCancelled()) return; - const hex = await coreTransaction.serialize(); - if (isCancelled()) return; - const receiver = await coreTransaction.getReceiver(); - if (isCancelled()) return; - const sender = await coreTransaction.getSender(); - if (isCancelled()) return; - const recipients = [ - transaction.mode === "undelegate" ? "" : await receiver.toBase58(), - ]; - if (isCancelled()) return; - const senders = [await sender.toBase58()]; - if (isCancelled()) return; - const feesRaw = await coreTransaction.getFees(); - if (isCancelled()) return; - const fee = await libcoreAmountToBigNumber(feesRaw); - if (isCancelled()) return; - // Make an optimistic response - let op; - const txHash = ""; // resolved in broadcast() - - const type = - transaction.mode === "undelegate" - ? "UNDELEGATE" - : transaction.mode === "delegate" - ? "DELEGATE" - : "OUT"; - const subAccount = - transaction.subAccountId && subAccounts - ? subAccounts.find((a) => a.id === transaction.subAccountId) - : null; - - if (!subAccount) { - op = { - id: `${id}-${txHash}-${type}`, - hash: txHash, - type, - value: transaction.useAllAmount ? balance : transaction.amount.plus(fee), - fee, - blockHash: null, - blockHeight: null, - senders, - recipients, - accountId: id, - date: new Date(), - extra: {}, - }; - } else { - invariant( - subAccount.type === "ChildAccount", - "tezos child account is ChildAccount" - ); - op = { - id: `${id}-${txHash}-OUT`, - hash: txHash, - type: "OUT", - value: fee, - fee, - blockHash: null, - blockHeight: null, - senders, - recipients: [subAccount.address], - accountId: id, - date: new Date(), - extra: {}, - subOperations: [ - { - id: `${subAccount.id}-${txHash}-${type}`, - hash: txHash, - type, - value: transaction.useAllAmount - ? subAccount.balance - : transaction.amount, - fee, - blockHash: null, - blockHeight: null, - senders, - recipients: [transaction.recipient], - accountId: subAccount.id, - date: new Date(), - extra: {}, - }, - ], - }; - } - - return { - operation: op, - expirationDate: null, - signature: hex, - }; -} - -export default makeSignOperation({ - buildTransaction, - signTransaction, -}); diff --git a/src/families/tezos/types.ts b/src/families/tezos/types.ts index 1473c87fe3..433638d5b4 100644 --- a/src/families/tezos/types.ts +++ b/src/families/tezos/types.ts @@ -1,10 +1,4 @@ import type { BigNumber } from "bignumber.js"; -import type { - CoreAmount, - CoreBigInt, - Spec, - CoreOperationQuery, -} from "../../libcore/types"; import type { TransactionCommon, TransactionCommonRaw, @@ -20,104 +14,8 @@ export type TezosResourcesRaw = { counter: number; }; -// WILL BE DROPPED => - -export const tezosOperationTag = { - OPERATION_TAG_NONE: 0, - OPERATION_TAG_NONE1: 1, - OPERATION_TAG_NONE2: 2, - OPERATION_TAG_GENERIC: 3, - OPERATION_TAG_NONE4: 4, - OPERATION_TAG_PROPOSAL: 5, - OPERATION_TAG_BALLOT: 6, - OPERATION_TAG_REVEAL: 7, - OPERATION_TAG_TRANSACTION: 8, - OPERATION_TAG_ORIGINATION: 9, - OPERATION_TAG_DELEGATION: 10, -}; - export type TezosOperationMode = "send" | "delegate" | "undelegate"; -export type TezosOperationTag = - typeof tezosOperationTag[keyof typeof tezosOperationTag]; - -declare class CoreTezosLikeAddress { - toBase58(): Promise; -} - -declare class CoreTezosLikeTransaction { - getType(): Promise; - getHash(): Promise; - getFees(): Promise; - getValue(): Promise; - getReceiver(): Promise; - getSender(): Promise; - getGasLimit(): Promise; - serialize(): Promise; - setSignature(arg0: string): Promise; - getStatus(): Promise; -} - -declare class CoreTezosLikeOperation { - getTransaction(): Promise; -} - -declare class CoreTezosLikeTransactionBuilder { - setType(type: TezosOperationTag): Promise; - sendToAddress( - amount: CoreAmount, - address: string - ): Promise; - wipeToAddress(address: string): Promise; - setFees(fees: CoreAmount): Promise; - setGasLimit(gasLimit: CoreAmount): Promise; - build(): Promise; - setStorageLimit( - storageLimit: CoreBigInt - ): Promise; -} - -declare class CoreTezosLikeAccount { - broadcastRawTransaction(signed: string): Promise; - buildTransaction(): Promise; - getStorage(address: string): Promise; - getEstimatedGasLimit(address: string): Promise; - getFees(): Promise; - getOriginatedAccounts(): Promise; -} - -declare class CoreTezosLikeOriginatedAccount { - getAddress(): Promise; - getPublicKey(): Promise; - getBalance(): Promise; - isSpendable(): Promise; - isDelegatable(): Promise; - buildTransaction(): Promise; - queryOperations(): Promise; -} - -export type CoreStatics = { - TezosLikeOperation: CoreTezosLikeOperation; - TezosLikeAddress: CoreTezosLikeAddress; - TezosLikeAccount: CoreTezosLikeAccount; - TezosLikeTransaction: CoreTezosLikeTransaction; - TezosLikeTransactionBuilder: CoreTezosLikeTransactionBuilder; -}; -export type { - CoreTezosLikeOperation, - CoreTezosLikeAddress, - CoreTezosLikeAccount, - CoreTezosLikeOriginatedAccount, - CoreTezosLikeTransaction, - CoreTezosLikeTransactionBuilder, -}; -export type CoreAccountSpecifics = { - asTezosLikeAccount(): Promise; -}; -export type CoreOperationSpecifics = { - asTezosLikeOperation(): Promise; -}; -export type CoreCurrencySpecifics = Record; export type NetworkInfo = { family: "tezos"; fees: BigNumber; @@ -147,125 +45,3 @@ export type TransactionRaw = TransactionCommonRaw & { estimatedFees: string | null | undefined; taquitoError: string | null | undefined; }; -export const reflect = (declare: (arg0: string, arg1: Spec) => void) => { - declare("TezosLikeAddress", { - methods: { - toBase58: {}, - }, - }); - declare("TezosLikeTransaction", { - methods: { - getType: {}, - getHash: {}, - getStatus: {}, - getFees: { - returns: "Amount", - }, - getValue: { - returns: "Amount", - }, - getGasLimit: { - returns: "Amount", - }, - getReceiver: { - returns: "TezosLikeAddress", - }, - getSender: { - returns: "TezosLikeAddress", - }, - serialize: { - returns: "hex", - }, - setSignature: { - params: ["hex"], - }, - }, - }); - declare("TezosLikeOperation", { - methods: { - getTransaction: { - returns: "TezosLikeTransaction", - }, - }, - }); - declare("TezosLikeTransactionBuilder", { - methods: { - setType: { - returns: "TezosLikeTransactionBuilder", - }, - sendToAddress: { - params: ["Amount"], - returns: "TezosLikeTransactionBuilder", - }, - wipeToAddress: { - returns: "TezosLikeTransactionBuilder", - }, - setFees: { - params: ["Amount"], - returns: "TezosLikeTransactionBuilder", - }, - setGasLimit: { - params: ["Amount"], - returns: "TezosLikeTransactionBuilder", - }, - setStorageLimit: { - params: ["BigInt"], - returns: "TezosLikeTransactionBuilder", - }, - build: { - returns: "TezosLikeTransaction", - }, - }, - }); - declare("TezosLikeAccount", { - methods: { - broadcastRawTransaction: { - params: ["hex"], - }, - buildTransaction: { - returns: "TezosLikeTransactionBuilder", - }, - getStorage: { - returns: "BigInt", - }, - getEstimatedGasLimit: { - returns: "BigInt", - }, - getFees: { - returns: "BigInt", - }, - getOriginatedAccounts: { - returns: ["TezosLikeOriginatedAccount"], - }, - }, - }); - declare("TezosLikeOriginatedAccount", { - methods: { - getAddress: {}, - getPublicKey: {}, - getBalance: { - returns: "Amount", - }, - isSpendable: {}, - isDelegatable: {}, - buildTransaction: { - returns: "TezosLikeTransactionBuilder", - }, - queryOperations: { - returns: "OperationQuery", - }, - }, - }); - return { - OperationMethods: { - asTezosLikeOperation: { - returns: "TezosLikeOperation", - }, - }, - AccountMethods: { - asTezosLikeAccount: { - returns: "TezosLikeAccount", - }, - }, - }; -}; diff --git a/src/families/tron/types.ts b/src/families/tron/types.ts index 71e5084bbf..6e2dbd3268 100644 --- a/src/families/tron/types.ts +++ b/src/families/tron/types.ts @@ -3,10 +3,7 @@ import type { TransactionCommon, TransactionCommonRaw, } from "../../types/transaction"; -export type CoreStatics = Record; -export type CoreAccountSpecifics = Record; -export type CoreOperationSpecifics = Record; -export type CoreCurrencySpecifics = Record; + export type TronOperationMode = | "send" | "freeze" @@ -120,8 +117,6 @@ export type SendTransactionDataSuccess = { signature: string[] | null | undefined; }; -/** */ -export const reflect = (_declare: any): void => {}; export type SuperRepresentativeData = { list: SuperRepresentative[]; totalVotes: number; diff --git a/src/generated/bridge/libcore.ts b/src/generated/bridge/libcore.ts deleted file mode 100644 index c7000bc09d..0000000000 --- a/src/generated/bridge/libcore.ts +++ /dev/null @@ -1,15 +0,0 @@ -import algorand from "../../families/algorand/bridge/libcore"; - -import bitcoin from "../../families/bitcoin/bridge/libcore"; - -import cosmos from "../../families/cosmos/bridge/libcore"; - -import tezos from "../../families/tezos/bridge/libcore"; - - -export default { - algorand, - bitcoin, - cosmos, - tezos, -}; diff --git a/src/generated/customAddressValidation.ts b/src/generated/customAddressValidation.ts index 4fcb4d7b29..2554dd8def 100644 --- a/src/generated/customAddressValidation.ts +++ b/src/generated/customAddressValidation.ts @@ -1,9 +1,3 @@ -import bitcoin from "../families/bitcoin/customAddressValidation"; - -import filecoin from "../families/filecoin/customAddressValidation"; - export default { - bitcoin, - filecoin, }; diff --git a/src/generated/libcore-buildOperation.ts b/src/generated/libcore-buildOperation.ts deleted file mode 100644 index 4650180dee..0000000000 --- a/src/generated/libcore-buildOperation.ts +++ /dev/null @@ -1,15 +0,0 @@ -import algorand from "../families/algorand/libcore-buildOperation"; - -import bitcoin from "../families/bitcoin/libcore-buildOperation"; - -import cosmos from "../families/cosmos/libcore-buildOperation"; - -import tezos from "../families/tezos/libcore-buildOperation"; - - -export default { - algorand, - bitcoin, - cosmos, - tezos, -}; diff --git a/src/generated/libcore-buildSubAccounts.ts b/src/generated/libcore-buildSubAccounts.ts deleted file mode 100644 index 7aaf26f982..0000000000 --- a/src/generated/libcore-buildSubAccounts.ts +++ /dev/null @@ -1,9 +0,0 @@ -import algorand from "../families/algorand/libcore-buildSubAccounts"; - -import tezos from "../families/tezos/libcore-buildSubAccounts"; - - -export default { - algorand, - tezos, -}; diff --git a/src/generated/libcore-getAccountNetworkInfo.ts b/src/generated/libcore-getAccountNetworkInfo.ts deleted file mode 100644 index f077b415a1..0000000000 --- a/src/generated/libcore-getAccountNetworkInfo.ts +++ /dev/null @@ -1,9 +0,0 @@ -import bitcoin from "../families/bitcoin/libcore-getAccountNetworkInfo"; - -import tezos from "../families/tezos/libcore-getAccountNetworkInfo"; - - -export default { - bitcoin, - tezos, -}; diff --git a/src/generated/libcore-getFeesForTransaction.ts b/src/generated/libcore-getFeesForTransaction.ts deleted file mode 100644 index 30e3d7ae2d..0000000000 --- a/src/generated/libcore-getFeesForTransaction.ts +++ /dev/null @@ -1,15 +0,0 @@ -import algorand from "../families/algorand/libcore-getFeesForTransaction"; - -import bitcoin from "../families/bitcoin/libcore-getFeesForTransaction"; - -import cosmos from "../families/cosmos/libcore-getFeesForTransaction"; - -import tezos from "../families/tezos/libcore-getFeesForTransaction"; - - -export default { - algorand, - bitcoin, - cosmos, - tezos, -}; diff --git a/src/generated/libcore-mergeOperations.ts b/src/generated/libcore-mergeOperations.ts deleted file mode 100644 index b713d8e2ff..0000000000 --- a/src/generated/libcore-mergeOperations.ts +++ /dev/null @@ -1,6 +0,0 @@ -import cosmos from "../families/cosmos/libcore-mergeOperations"; - - -export default { - cosmos, -}; diff --git a/src/generated/libcore-postBuildAccount.ts b/src/generated/libcore-postBuildAccount.ts deleted file mode 100644 index 44bc4ec098..0000000000 --- a/src/generated/libcore-postBuildAccount.ts +++ /dev/null @@ -1,12 +0,0 @@ -import algorand from "../families/algorand/libcore-postBuildAccount"; - -import bitcoin from "../families/bitcoin/libcore-postBuildAccount"; - -import cosmos from "../families/cosmos/libcore-postBuildAccount"; - - -export default { - algorand, - bitcoin, - cosmos, -}; diff --git a/src/generated/libcore-postSyncPatch.ts b/src/generated/libcore-postSyncPatch.ts deleted file mode 100644 index 2554dd8def..0000000000 --- a/src/generated/libcore-postSyncPatch.ts +++ /dev/null @@ -1,3 +0,0 @@ - -export default { -}; diff --git a/src/generated/test-specifics.ts b/src/generated/test-specifics.ts index f539a85210..2d5194b335 100644 --- a/src/generated/test-specifics.ts +++ b/src/generated/test-specifics.ts @@ -1,5 +1,3 @@ -import cosmos from "../families/cosmos/test-specifics"; - import polkadot from "../families/polkadot/test-specifics"; import tezos from "../families/tezos/test-specifics"; @@ -8,7 +6,6 @@ import tron from "../families/tron/test-specifics"; export default { - cosmos, polkadot, tezos, tron, diff --git a/src/generated/types.ts b/src/generated/types.ts index bd04931886..d1ee5a8d03 100644 --- a/src/generated/types.ts +++ b/src/generated/types.ts @@ -1,195 +1,34 @@ -import { reflect as algorandReflect } from "../families/algorand/types"; -import { CoreStatics as CoreStatics_algorand } from "../families/algorand/types"; -import { CoreAccountSpecifics as CoreAccountSpecifics_algorand } from "../families/algorand/types"; -import { CoreOperationSpecifics as CoreOperationSpecifics_algorand } from "../families/algorand/types"; -import { CoreCurrencySpecifics as CoreCurrencySpecifics_algorand } from "../families/algorand/types"; import { Transaction as algorandTransaction } from "../families/algorand/types"; import { TransactionRaw as algorandTransactionRaw } from "../families/algorand/types"; -import { reflect as bitcoinReflect } from "../families/bitcoin/types"; -import { CoreStatics as CoreStatics_bitcoin } from "../families/bitcoin/types"; -import { CoreAccountSpecifics as CoreAccountSpecifics_bitcoin } from "../families/bitcoin/types"; -import { CoreOperationSpecifics as CoreOperationSpecifics_bitcoin } from "../families/bitcoin/types"; -import { CoreCurrencySpecifics as CoreCurrencySpecifics_bitcoin } from "../families/bitcoin/types"; import { Transaction as bitcoinTransaction } from "../families/bitcoin/types"; import { TransactionRaw as bitcoinTransactionRaw } from "../families/bitcoin/types"; -import { NetworkInfo as bitcoinNetworkInfo } from "../families/bitcoin/types"; -import { NetworkInfoRaw as bitcoinNetworkInfoRaw } from "../families/bitcoin/types"; -import { reflect as celoReflect } from "../families/celo/types"; -import { CoreStatics as CoreStatics_celo } from "../families/celo/types"; -import { CoreAccountSpecifics as CoreAccountSpecifics_celo } from "../families/celo/types"; -import { CoreOperationSpecifics as CoreOperationSpecifics_celo } from "../families/celo/types"; -import { CoreCurrencySpecifics as CoreCurrencySpecifics_celo } from "../families/celo/types"; import { Transaction as celoTransaction } from "../families/celo/types"; import { TransactionRaw as celoTransactionRaw } from "../families/celo/types"; -import { reflect as cosmosReflect } from "../families/cosmos/types"; -import { CoreStatics as CoreStatics_cosmos } from "../families/cosmos/types"; -import { CoreAccountSpecifics as CoreAccountSpecifics_cosmos } from "../families/cosmos/types"; -import { CoreOperationSpecifics as CoreOperationSpecifics_cosmos } from "../families/cosmos/types"; -import { CoreCurrencySpecifics as CoreCurrencySpecifics_cosmos } from "../families/cosmos/types"; import { Transaction as cosmosTransaction } from "../families/cosmos/types"; import { TransactionRaw as cosmosTransactionRaw } from "../families/cosmos/types"; -import { NetworkInfo as cosmosNetworkInfo } from "../families/cosmos/types"; -import { NetworkInfoRaw as cosmosNetworkInfoRaw } from "../families/cosmos/types"; -import { reflect as crypto_orgReflect } from "../families/crypto_org/types"; -import { CoreStatics as CoreStatics_crypto_org } from "../families/crypto_org/types"; -import { CoreAccountSpecifics as CoreAccountSpecifics_crypto_org } from "../families/crypto_org/types"; -import { CoreOperationSpecifics as CoreOperationSpecifics_crypto_org } from "../families/crypto_org/types"; -import { CoreCurrencySpecifics as CoreCurrencySpecifics_crypto_org } from "../families/crypto_org/types"; import { Transaction as crypto_orgTransaction } from "../families/crypto_org/types"; import { TransactionRaw as crypto_orgTransactionRaw } from "../families/crypto_org/types"; -import { NetworkInfo as crypto_orgNetworkInfo } from "../families/crypto_org/types"; -import { NetworkInfoRaw as crypto_orgNetworkInfoRaw } from "../families/crypto_org/types"; -import { reflect as elrondReflect } from "../families/elrond/types"; -import { CoreStatics as CoreStatics_elrond } from "../families/elrond/types"; -import { CoreAccountSpecifics as CoreAccountSpecifics_elrond } from "../families/elrond/types"; -import { CoreOperationSpecifics as CoreOperationSpecifics_elrond } from "../families/elrond/types"; -import { CoreCurrencySpecifics as CoreCurrencySpecifics_elrond } from "../families/elrond/types"; import { Transaction as elrondTransaction } from "../families/elrond/types"; import { TransactionRaw as elrondTransactionRaw } from "../families/elrond/types"; -import { NetworkInfo as elrondNetworkInfo } from "../families/elrond/types"; -import { NetworkInfoRaw as elrondNetworkInfoRaw } from "../families/elrond/types"; -import { reflect as ethereumReflect } from "../families/ethereum/types"; -import { CoreStatics as CoreStatics_ethereum } from "../families/ethereum/types"; -import { CoreAccountSpecifics as CoreAccountSpecifics_ethereum } from "../families/ethereum/types"; -import { CoreOperationSpecifics as CoreOperationSpecifics_ethereum } from "../families/ethereum/types"; -import { CoreCurrencySpecifics as CoreCurrencySpecifics_ethereum } from "../families/ethereum/types"; import { Transaction as ethereumTransaction } from "../families/ethereum/types"; import { TransactionRaw as ethereumTransactionRaw } from "../families/ethereum/types"; -import { NetworkInfo as ethereumNetworkInfo } from "../families/ethereum/types"; -import { NetworkInfoRaw as ethereumNetworkInfoRaw } from "../families/ethereum/types"; -import { reflect as filecoinReflect } from "../families/filecoin/types"; -import { CoreStatics as CoreStatics_filecoin } from "../families/filecoin/types"; -import { CoreAccountSpecifics as CoreAccountSpecifics_filecoin } from "../families/filecoin/types"; -import { CoreOperationSpecifics as CoreOperationSpecifics_filecoin } from "../families/filecoin/types"; -import { CoreCurrencySpecifics as CoreCurrencySpecifics_filecoin } from "../families/filecoin/types"; import { Transaction as filecoinTransaction } from "../families/filecoin/types"; import { TransactionRaw as filecoinTransactionRaw } from "../families/filecoin/types"; -import { NetworkInfo as filecoinNetworkInfo } from "../families/filecoin/types"; -import { NetworkInfoRaw as filecoinNetworkInfoRaw } from "../families/filecoin/types"; -import { reflect as neoReflect } from "../families/neo/types"; -import { CoreStatics as CoreStatics_neo } from "../families/neo/types"; -import { CoreAccountSpecifics as CoreAccountSpecifics_neo } from "../families/neo/types"; -import { CoreOperationSpecifics as CoreOperationSpecifics_neo } from "../families/neo/types"; -import { CoreCurrencySpecifics as CoreCurrencySpecifics_neo } from "../families/neo/types"; import { Transaction as neoTransaction } from "../families/neo/types"; import { TransactionRaw as neoTransactionRaw } from "../families/neo/types"; -import { NetworkInfo as neoNetworkInfo } from "../families/neo/types"; -import { NetworkInfoRaw as neoNetworkInfoRaw } from "../families/neo/types"; -import { reflect as polkadotReflect } from "../families/polkadot/types"; -import { CoreStatics as CoreStatics_polkadot } from "../families/polkadot/types"; -import { CoreAccountSpecifics as CoreAccountSpecifics_polkadot } from "../families/polkadot/types"; -import { CoreOperationSpecifics as CoreOperationSpecifics_polkadot } from "../families/polkadot/types"; -import { CoreCurrencySpecifics as CoreCurrencySpecifics_polkadot } from "../families/polkadot/types"; import { Transaction as polkadotTransaction } from "../families/polkadot/types"; import { TransactionRaw as polkadotTransactionRaw } from "../families/polkadot/types"; -import { reflect as rippleReflect } from "../families/ripple/types"; -import { CoreStatics as CoreStatics_ripple } from "../families/ripple/types"; -import { CoreAccountSpecifics as CoreAccountSpecifics_ripple } from "../families/ripple/types"; -import { CoreOperationSpecifics as CoreOperationSpecifics_ripple } from "../families/ripple/types"; -import { CoreCurrencySpecifics as CoreCurrencySpecifics_ripple } from "../families/ripple/types"; import { Transaction as rippleTransaction } from "../families/ripple/types"; import { TransactionRaw as rippleTransactionRaw } from "../families/ripple/types"; -import { NetworkInfo as rippleNetworkInfo } from "../families/ripple/types"; -import { NetworkInfoRaw as rippleNetworkInfoRaw } from "../families/ripple/types"; -import { reflect as solanaReflect } from "../families/solana/types"; -import { CoreStatics as CoreStatics_solana } from "../families/solana/types"; -import { CoreAccountSpecifics as CoreAccountSpecifics_solana } from "../families/solana/types"; -import { CoreOperationSpecifics as CoreOperationSpecifics_solana } from "../families/solana/types"; -import { CoreCurrencySpecifics as CoreCurrencySpecifics_solana } from "../families/solana/types"; import { Transaction as solanaTransaction } from "../families/solana/types"; import { TransactionRaw as solanaTransactionRaw } from "../families/solana/types"; -import { reflect as stellarReflect } from "../families/stellar/types"; -import { CoreStatics as CoreStatics_stellar } from "../families/stellar/types"; -import { CoreAccountSpecifics as CoreAccountSpecifics_stellar } from "../families/stellar/types"; -import { CoreOperationSpecifics as CoreOperationSpecifics_stellar } from "../families/stellar/types"; -import { CoreCurrencySpecifics as CoreCurrencySpecifics_stellar } from "../families/stellar/types"; import { Transaction as stellarTransaction } from "../families/stellar/types"; import { TransactionRaw as stellarTransactionRaw } from "../families/stellar/types"; -import { NetworkInfo as stellarNetworkInfo } from "../families/stellar/types"; -import { NetworkInfoRaw as stellarNetworkInfoRaw } from "../families/stellar/types"; -import { reflect as tezosReflect } from "../families/tezos/types"; -import { CoreStatics as CoreStatics_tezos } from "../families/tezos/types"; -import { CoreAccountSpecifics as CoreAccountSpecifics_tezos } from "../families/tezos/types"; -import { CoreOperationSpecifics as CoreOperationSpecifics_tezos } from "../families/tezos/types"; -import { CoreCurrencySpecifics as CoreCurrencySpecifics_tezos } from "../families/tezos/types"; import { Transaction as tezosTransaction } from "../families/tezos/types"; import { TransactionRaw as tezosTransactionRaw } from "../families/tezos/types"; -import { NetworkInfo as tezosNetworkInfo } from "../families/tezos/types"; -import { NetworkInfoRaw as tezosNetworkInfoRaw } from "../families/tezos/types"; -import { reflect as tronReflect } from "../families/tron/types"; -import { CoreStatics as CoreStatics_tron } from "../families/tron/types"; -import { CoreAccountSpecifics as CoreAccountSpecifics_tron } from "../families/tron/types"; -import { CoreOperationSpecifics as CoreOperationSpecifics_tron } from "../families/tron/types"; -import { CoreCurrencySpecifics as CoreCurrencySpecifics_tron } from "../families/tron/types"; import { Transaction as tronTransaction } from "../families/tron/types"; import { TransactionRaw as tronTransactionRaw } from "../families/tron/types"; -import { NetworkInfo as tronNetworkInfo } from "../families/tron/types"; -import { NetworkInfoRaw as tronNetworkInfoRaw } from "../families/tron/types"; -export type SpecificStatics = {} -& CoreStatics_algorand -& CoreStatics_bitcoin -& CoreStatics_celo -& CoreStatics_cosmos -& CoreStatics_crypto_org -& CoreStatics_elrond -& CoreStatics_ethereum -& CoreStatics_filecoin -& CoreStatics_neo -& CoreStatics_polkadot -& CoreStatics_ripple -& CoreStatics_solana -& CoreStatics_stellar -& CoreStatics_tezos -& CoreStatics_tron -export type CoreAccountSpecifics = {} -& CoreAccountSpecifics_algorand -& CoreAccountSpecifics_bitcoin -& CoreAccountSpecifics_celo -& CoreAccountSpecifics_cosmos -& CoreAccountSpecifics_crypto_org -& CoreAccountSpecifics_elrond -& CoreAccountSpecifics_ethereum -& CoreAccountSpecifics_filecoin -& CoreAccountSpecifics_neo -& CoreAccountSpecifics_polkadot -& CoreAccountSpecifics_ripple -& CoreAccountSpecifics_solana -& CoreAccountSpecifics_stellar -& CoreAccountSpecifics_tezos -& CoreAccountSpecifics_tron -export type CoreOperationSpecifics = {} -& CoreOperationSpecifics_algorand -& CoreOperationSpecifics_bitcoin -& CoreOperationSpecifics_celo -& CoreOperationSpecifics_cosmos -& CoreOperationSpecifics_crypto_org -& CoreOperationSpecifics_elrond -& CoreOperationSpecifics_ethereum -& CoreOperationSpecifics_filecoin -& CoreOperationSpecifics_neo -& CoreOperationSpecifics_polkadot -& CoreOperationSpecifics_ripple -& CoreOperationSpecifics_solana -& CoreOperationSpecifics_stellar -& CoreOperationSpecifics_tezos -& CoreOperationSpecifics_tron -export type CoreCurrencySpecifics = {} -& CoreCurrencySpecifics_algorand -& CoreCurrencySpecifics_bitcoin -& CoreCurrencySpecifics_celo -& CoreCurrencySpecifics_cosmos -& CoreCurrencySpecifics_crypto_org -& CoreCurrencySpecifics_elrond -& CoreCurrencySpecifics_ethereum -& CoreCurrencySpecifics_filecoin -& CoreCurrencySpecifics_neo -& CoreCurrencySpecifics_polkadot -& CoreCurrencySpecifics_ripple -& CoreCurrencySpecifics_solana -& CoreCurrencySpecifics_stellar -& CoreCurrencySpecifics_tezos -& CoreCurrencySpecifics_tron export type Transaction = | algorandTransaction | bitcoinTransaction @@ -222,44 +61,3 @@ export type TransactionRaw = | stellarTransactionRaw | tezosTransactionRaw | tronTransactionRaw -export type NetworkInfo = - | bitcoinNetworkInfo - | cosmosNetworkInfo - | crypto_orgNetworkInfo - | elrondNetworkInfo - | ethereumNetworkInfo - | filecoinNetworkInfo - | neoNetworkInfo - | rippleNetworkInfo - | stellarNetworkInfo - | tezosNetworkInfo - | tronNetworkInfo -export type NetworkInfoRaw = - | bitcoinNetworkInfoRaw - | cosmosNetworkInfoRaw - | crypto_orgNetworkInfoRaw - | elrondNetworkInfoRaw - | ethereumNetworkInfoRaw - | filecoinNetworkInfoRaw - | neoNetworkInfoRaw - | rippleNetworkInfoRaw - | stellarNetworkInfoRaw - | tezosNetworkInfoRaw - | tronNetworkInfoRaw -export const reflectSpecifics = (declare: any): Array<{ OperationMethods: Record, AccountMethods: Record }> => [ - algorandReflect(declare), - bitcoinReflect(declare), - celoReflect(declare), - cosmosReflect(declare), - crypto_orgReflect(declare), - elrondReflect(declare), - ethereumReflect(declare), - filecoinReflect(declare), - neoReflect(declare), - polkadotReflect(declare), - rippleReflect(declare), - solanaReflect(declare), - stellarReflect(declare), - tezosReflect(declare), - tronReflect(declare), -] as Array<{ OperationMethods: Record, AccountMethods: Record }>; diff --git a/src/libcore/access.ts b/src/libcore/access.ts deleted file mode 100644 index 8a89e7ceef..0000000000 --- a/src/libcore/access.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { log } from "@ledgerhq/logs"; -import invariant from "invariant"; -import { Subject, Observable } from "rxjs"; -import { map, distinctUntilChanged } from "rxjs/operators"; -import type { Core } from "./types"; - -const GC_DELAY = 1000; -let core: Core | null | undefined; -let corePromise: Promise | null | undefined; -let libcoreJobsCounter = 0; -let lastFlush: Promise = Promise.resolve(); -let flushTimeout: NodeJS.Timeout | null | number = null; -const libcoreJobsCounterSubject: Subject = new Subject(); - -export const libcoreJobBusy: Observable = - libcoreJobsCounterSubject.pipe( - map((v) => v > 0), - distinctUntilChanged() - ); -type AfterGCJob = { - job: (arg0: Core) => Promise; - resolve: (arg0: R) => void; -}; -const afterLibcoreFlushes: Array> = []; - -function flush(c: Core) { - log("libcore/access", "flush"); - lastFlush = c - .flush() - .then(async () => { - let item; - - while ((item = afterLibcoreFlushes.shift())) { - item.resolve(await item.job(c)); - } - - log("libcore/access", "flush end"); - }) - .catch((e) => { - log("libcore/access", "flush error " + String(e)); - console.error(e); - }); -} - -export async function afterLibcoreGC( - job: (core: Core) => Promise -): Promise { - return new Promise((resolve) => { - if (!core) return; - log("libcore/access", "new after gc job"); - afterLibcoreFlushes.push({ - job, - resolve, - }); - - if (libcoreJobsCounter === 0) { - log("libcore/access", "after gc job exec now"); - if (flushTimeout) { - clearTimeout(flushTimeout as number); - } - flushTimeout = setTimeout(flush.bind(null, core), GC_DELAY); - } - }); -} - -export async function withLibcore( - job: (core: Core) => Promise -): Promise { - libcoreJobsCounter++; - libcoreJobsCounterSubject.next(libcoreJobsCounter); - let c: Core | null | undefined; - - try { - if (flushTimeout) { - // there is a new job so we must not do the GC yet. - clearTimeout(flushTimeout as number); - flushTimeout = null; - } - - c = await load(); - await lastFlush; // wait previous flush before starting anything - - const res = await job(c); - return res; - } finally { - libcoreJobsCounter--; - libcoreJobsCounterSubject.next(libcoreJobsCounter); - - if (c && libcoreJobsCounter === 0) { - flushTimeout = setTimeout(flush.bind(null, c), GC_DELAY); - } - } -} - -type Fn, R> = (...args: A) => Promise; - -export const withLibcoreF = - , R>(job: (core: Core) => Fn): Fn => - (...args) => - withLibcore((c) => job(c)(...args)); - -let loadCoreImpl: (() => Promise) | null | undefined; - -// reset the libcore data -export async function reset(): Promise { - log("libcore/access", "reset"); - if (!core) return; - invariant(libcoreJobsCounter === 0, "some libcore jobs are still running"); - await core.getPoolInstance().freshResetAll(); - core = null; - corePromise = null; -} - -async function load(): Promise { - if (core) { - return core; - } - - if (!corePromise) { - if (!loadCoreImpl) { - console.warn("loadCore implementation is missing"); - throw new Error("loadCoreImpl missing"); - } - - log("libcore/access", "load core impl"); - corePromise = loadCoreImpl(); - } - - core = await corePromise; - return core; -} - -export function setLoadCoreImplementation(loadCore: () => Promise) { - loadCoreImpl = loadCore; -} diff --git a/src/libcore/broadcast.ts b/src/libcore/broadcast.ts deleted file mode 100644 index 89102cb5f5..0000000000 --- a/src/libcore/broadcast.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { - AccountBridge, - Operation, - Account, - SignedOperation, -} from "../types"; -import type { CoreAccount } from "./types"; -import { withLibcore } from "./access"; -import { remapLibcoreErrors } from "./errors"; -import { getCoreAccount } from "./getCoreAccount"; -import { Transaction } from "../types"; - -export type Arg = { - broadcast: (arg0: { - account: Account; - coreAccount: CoreAccount; - signedOperation: SignedOperation; - }) => Promise; -}; - -type Broadcast = AccountBridge["broadcast"]; - -export const makeBroadcast = - ({ broadcast }: Arg): Broadcast => - ({ account, signedOperation }) => - withLibcore(async (core) => { - const { coreAccount } = await getCoreAccount(core, account); - const res = await broadcast({ - account, - coreAccount, - signedOperation, - }); - return res; - }).catch((e) => Promise.reject(remapLibcoreErrors(e))); diff --git a/src/libcore/buildAccount/buildOperation.ts b/src/libcore/buildAccount/buildOperation.ts deleted file mode 100644 index 21a72b34ca..0000000000 --- a/src/libcore/buildAccount/buildOperation.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { log } from "@ledgerhq/logs"; -import type { - Operation, - CryptoCurrency, - SubAccount, - AccountLike, -} from "../../types"; -import { libcoreAmountToBigNumber } from "../buildBigNumber"; -import { inferSubOperations } from "../../account"; -import type { Core, CoreOperation } from "../types"; -import perFamily from "../../generated/libcore-buildOperation"; -import { getEnv } from "../../env"; -export const OperationTypeMap = { - "0": "OUT", - "1": "IN", -}; -export async function buildOperation(arg: { - core: Core; - coreOperation: CoreOperation; - accountId: string; - currency: CryptoCurrency; - contextualSubAccounts?: SubAccount[] | null | undefined; - existingAccount: AccountLike | null | undefined; -}): Promise { - const { coreOperation, accountId, currency, contextualSubAccounts } = arg; - const buildOp = perFamily[currency.family]; - - if (!buildOp) { - throw new Error(currency.family + " family not supported"); - } - - const operationType = await coreOperation.getOperationType(); - const type = OperationTypeMap[operationType] || "NONE"; - const coreValue = await coreOperation.getAmount(); - let value = await libcoreAmountToBigNumber(coreValue); - const coreFee = await coreOperation.getFees(); - if (!coreFee) throw new Error("fees should not be null"); - const fee = await libcoreAmountToBigNumber(coreFee); - - if (type === "OUT") { - value = value.plus(fee); - } - - const blockHeight = await coreOperation.getBlockHeight(); - const [recipients, senders] = await Promise.all([ - type === "IN" && currency.family === "bitcoin" - ? coreOperation.getSelfRecipients() - : coreOperation.getRecipients(), - coreOperation.getSenders(), - ]); - const date = new Date(await coreOperation.getDate()); - const partialOp = { - type, - value, - fee, - senders, - recipients, - blockHeight, - blockHash: null, - accountId, - date, - extra: {}, - }; - const rest = await buildOp(arg, partialOp); - if (!rest) return null; - const id = `${accountId}-${rest.hash}-${rest.type || type}${ - rest.extra && rest.extra.id ? "-" + rest.extra.id : "" - }`; - const op: Operation = { - id, - subOperations: contextualSubAccounts - ? inferSubOperations(rest.hash, contextualSubAccounts) - : undefined, - ...partialOp, - ...rest, - }; - const OPERATION_ADDRESSES_LIMIT = getEnv("OPERATION_ADDRESSES_LIMIT"); - - if (op.recipients.length > OPERATION_ADDRESSES_LIMIT) { - log( - "warning", - `operation.recipients too big (${op.recipients.length} > ${OPERATION_ADDRESSES_LIMIT}) – ${id}` - ); - op.recipients.splice(OPERATION_ADDRESSES_LIMIT); - } - - if (op.senders.length > OPERATION_ADDRESSES_LIMIT) { - log( - "warning", - `operation.senders too big (${op.senders.length} > ${OPERATION_ADDRESSES_LIMIT}) – ${id}` - ); - op.senders.splice(OPERATION_ADDRESSES_LIMIT); - } - - return op; -} diff --git a/src/libcore/buildAccount/buildSubAccounts.ts b/src/libcore/buildAccount/buildSubAccounts.ts deleted file mode 100644 index 6a0d3a15b3..0000000000 --- a/src/libcore/buildAccount/buildSubAccounts.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { - SubAccount, - Account, - CryptoCurrency, - SyncConfig, -} from "../../types"; -import type { Core, CoreAccount } from "../types"; -import byFamily from "../../generated/libcore-buildSubAccounts"; -export async function buildSubAccounts(arg: { - core: Core; - currency: CryptoCurrency; - coreAccount: CoreAccount; - accountId: string; - existingAccount: Account | null | undefined; - syncConfig: SyncConfig; - logId?: number; -}): Promise { - const f = byFamily[arg.currency.family]; - - if (f) { - const res = await f(arg); - return res; - } -} diff --git a/src/libcore/buildAccount/index.ts b/src/libcore/buildAccount/index.ts deleted file mode 100644 index b4854c7b72..0000000000 --- a/src/libcore/buildAccount/index.ts +++ /dev/null @@ -1,259 +0,0 @@ -import { log } from "@ledgerhq/logs"; -import last from "lodash/last"; -import { - encodeAccountId, - isAccountEmpty, - getAccountPlaceholderName, - getNewAccountPlaceholderName, - libcoreNoGoBalanceHistory, - emptyHistoryCache, -} from "../../account"; -import type { - SyncConfig, - Account, - CryptoCurrency, - DerivationMode, -} from "../../types"; -import { libcoreAmountToBigNumber } from "../buildBigNumber"; -import type { CoreWallet, CoreAccount, Core } from "../types"; -import { buildOperation } from "./buildOperation"; -import { buildSubAccounts } from "./buildSubAccounts"; -import { minimalOperationsBuilder } from "../../reconciliation"; -import { getOperationsPageSize } from "../../pagination"; -import getAccountBalanceHistory from "../getAccountBalanceHistory"; -import { getRanges } from "../../portfolio"; -import mergeOperationsByFamily from "../../generated/libcore-mergeOperations"; -import byFamily from "../../generated/libcore-postBuildAccount"; -// FIXME how to get that -const OperationOrderKey = { - date: 0, -}; -type F = (arg0: { - account: Account; - coreAccount: CoreAccount; -}) => Promise; - -async function queryOps(coreAccount) { - const query = await coreAccount.queryOperations(); - await query.addOrder(OperationOrderKey.date, false); - return query; -} - -export async function buildAccount({ - core, - coreWallet, - coreAccount, - currency, - accountIndex, - derivationMode, - seedIdentifier, - existingAccount, - logId, - syncConfig, -}: { - core: Core; - coreWallet: CoreWallet; - coreAccount: CoreAccount; - currency: CryptoCurrency; - accountIndex: number; - derivationMode: DerivationMode; - seedIdentifier: string; - existingAccount: Account | null | undefined; - logId: number; - syncConfig: SyncConfig; -}): Promise { - log("libcore", `sync(${logId}) start buildAccount`); - const restoreKey = await coreAccount.getRestoreKey(); - const accountId = encodeAccountId({ - type: "libcore", - version: "1", - currencyId: currency.id, - xpubOrAddress: restoreKey, - derivationMode, - }); - const query = await queryOps(coreAccount); - await query.partial(); - const partialOperations = await query.execute(); - const operationsPageSize = getOperationsPageSize(accountId, syncConfig); - const paginatedPartialOperations = isFinite(operationsPageSize) - ? partialOperations.slice(partialOperations.length - operationsPageSize) - : partialOperations; - log("libcore", `sync(${logId}) DONE partial query ops`); - const nativeBalance = await coreAccount.getBalance(); - const balance = await libcoreAmountToBigNumber(nativeBalance); - log("libcore", `sync(${logId}) DONE balance`); - const coreAccountCreationInfo = await coreWallet.getAccountCreationInfo( - accountIndex - ); - const derivations = await coreAccountCreationInfo.getDerivations(); - const accountPath: string = last(derivations); - const coreBlock = await coreAccount.getLastBlock(); - const blockHeight = await coreBlock.getHeight(); - const coreFreshAddresses = await coreAccount.getFreshPublicAddresses(); - if (coreFreshAddresses.length === 0) - throw new Error("expected at least one fresh address"); - const freshAddresses = await Promise.all( - coreFreshAddresses.map(async (item) => { - const [address, path] = await Promise.all([ - item.toString(), - item.getDerivationPath(), - ]); - const derivationPath: string = path - ? `${accountPath}/${path}` - : accountPath; - return { - address, - derivationPath, - }; - }) - ); - log("libcore", `sync(${logId}) DONE coreAccount addresses`); - const name = - partialOperations.length === 0 - ? getNewAccountPlaceholderName({ - currency, - index: accountIndex, - derivationMode, - }) - : getAccountPlaceholderName({ - currency, - index: accountIndex, - derivationMode, - }); - const subAccounts = await buildSubAccounts({ - core, - currency, - coreAccount, - accountId, - existingAccount, - logId, - syncConfig, - }); - - // We have pre-fetched the operations in "partial" mode - // now we will need to complete them lazily - const inferCoreOperation = async (corePartialOperation) => { - const query = await queryOps(coreAccount); - await query.limit(1); - await query.offset(partialOperations.indexOf(corePartialOperation)); - await query.complete(); - const [coreOperation] = await query.execute(); - return coreOperation; - }; - - const operations = await minimalOperationsBuilder( - (existingAccount && existingAccount.operations) || [], - paginatedPartialOperations, - async (corePartialOperation) => - buildOperation({ - core, - coreOperation: await inferCoreOperation(corePartialOperation), - accountId, - currency, - contextualSubAccounts: subAccounts, - existingAccount, - }), - mergeOperationsByFamily[currency.family] - ); - let lastOperation; - - if (partialOperations.length > 0) { - if (operations.length === partialOperations.length) { - // we already have lastOperation - lastOperation = operations[operations.length - 1]; - } else { - // we need to fetch lastOperation. partialOperations is older first - const coreOperation = await inferCoreOperation(partialOperations[0]); - lastOperation = await buildOperation({ - core, - coreOperation, - accountId, - currency, - contextualSubAccounts: subAccounts, - existingAccount, - }); - } - } - - log("libcore", `sync(${logId}) DONE operations`); - const balanceHistory = {}; - - if (!libcoreNoGoBalanceHistory().includes(currency.id)) { - await Promise.all( - getRanges().map(async (range) => { - // NB if we find this not optimized, we can implement this cache strategy: - // if for this range a balanceHistory exists in "existingAccount" - // compare the last data point {value} with `balance`, re-calc if differ - // also compare the last data point {date} with current date to force a re-calc every X hours. - const h = await getAccountBalanceHistory(coreAccount, range); - - if (!h[h.length - 1].value.isEqualTo(balance)) { - log("libcore", "last data point DOES NOT match the balance!"); - return; - } - - balanceHistory[range] = h; - }) - ); - } - - log("libcore", `sync(${logId}) DONE balanceHistory`); - let creationDate = new Date(); - - if (lastOperation) { - creationDate = lastOperation.date; - } - - if (subAccounts) { - subAccounts.forEach((a) => { - if (a.creationDate < creationDate) { - creationDate = a.creationDate; - } - }); - } - - const swapHistory = existingAccount?.swapHistory || []; - const account: Account = { - type: "Account", - id: accountId, - seedIdentifier, - xpub: restoreKey, - derivationMode, - index: accountIndex, - freshAddress: freshAddresses[0].address, - freshAddressPath: freshAddresses[0].derivationPath, - freshAddresses, - name, - starred: false, - used: false, - balance, - balanceHistory, - spendableBalance: balance, // FIXME need libcore concept - blockHeight, - currency, - unit: currency.units[0], - operationsCount: partialOperations.length, - operations, - pendingOperations: [], - lastSyncDate: new Date(), - creationDate, - swapHistory, - balanceHistoryCache: emptyHistoryCache, // calculated in the syncAccount function - }; - - if (subAccounts) { - account.subAccounts = subAccounts; - } - - account.used = !isAccountEmpty(account); - const f: F = byFamily[currency.family]; - - if (f) { - return await f({ - account, - coreAccount, - }); - } - - return account; -} diff --git a/src/libcore/buildBigNumber.ts b/src/libcore/buildBigNumber.ts deleted file mode 100644 index de4d268f00..0000000000 --- a/src/libcore/buildBigNumber.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { BigNumber } from "bignumber.js"; -import type { Core, CoreCurrency, CoreAmount, CoreBigInt } from "./types"; -export function bigNumberToLibcoreAmount( - core: Core, - walletCurrency: CoreCurrency, - amount: BigNumber -): Promise { - return core.Amount.fromHex(walletCurrency, amount.toString(16)); -} -export function bigNumberToLibcoreBigInt( - core: Core, - n: BigNumber -): Promise { - return core.BigInt.fromIntegerString(n.toString(10), 10); -} -export async function libcoreBigIntToBigNumber( - coreBigInt: CoreBigInt -): Promise { - const value = await coreBigInt.toString(10); - return new BigNumber(value); -} -export async function libcoreAmountToBigNumber( - amountInstance: CoreAmount -): Promise { - const coreBigInt = await amountInstance.toBigInt(); - const res = await libcoreBigIntToBigNumber(coreBigInt); - return res; -} diff --git a/src/libcore/createAccountFromDevice.ts b/src/libcore/createAccountFromDevice.ts deleted file mode 100644 index e8287515d7..0000000000 --- a/src/libcore/createAccountFromDevice.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { log } from "@ledgerhq/logs"; -import type { Core, CoreWallet, CoreAccount } from "./types"; -import type { CryptoCurrency } from "../types"; -import type { DerivationMode } from "../derivation"; -import { GetAddressOptions, Result } from "../hw/getAddress/types"; - -// In order to not re-query the same path, we use a temporary cache -export class DerivationsCache { - store: Record< - string, - { - publicKey: string; - chainCode?: string; - } - > = {}; -} - -type F = (arg0: { - core: Core; - wallet: CoreWallet; - currency: CryptoCurrency; - index: number; - derivationMode: DerivationMode; - isUnsubscribed: () => boolean; - derivationsCache: DerivationsCache; - getAddress: (opts: GetAddressOptions) => Promise; -}) => Promise; - -export const createAccountFromDevice: F = async ({ - core, - wallet, - currency, - derivationMode, - isUnsubscribed, - derivationsCache, - getAddress, -}) => { - log( - "libcore", - "createAccountFromDevice " + currency.id + " " + derivationMode - ); - - const accountCreationInfos = await wallet.getNextAccountCreationInfo(); - if (isUnsubscribed()) return; - - const chainCodes = await accountCreationInfos.getChainCodes(); - if (isUnsubscribed()) return; - - const publicKeys = await accountCreationInfos.getPublicKeys(); - if (isUnsubscribed()) return; - - const index = await accountCreationInfos.getIndex(); - if (isUnsubscribed()) return; - - const derivations = await accountCreationInfos.getDerivations(); - if (isUnsubscribed()) return; - - const owners = await accountCreationInfos.getOwners(); - if (isUnsubscribed()) return; - - await derivations.reduce( - (promise, derivation) => - promise.then(async () => { - if (isUnsubscribed()) return; - let cache = derivationsCache.store[derivation]; - - if (!cache) { - cache = await getAddress({ - currency, - path: derivation, - derivationMode, - askChainCode: true, - skipAppFailSafeCheck: true, - }); - derivationsCache.store[derivation] = cache; - } - - const { publicKey, chainCode } = cache; - publicKeys.push(publicKey); - if (chainCode) chainCodes.push(chainCode); - }), - Promise.resolve() - ); - - if (isUnsubscribed()) return; - - log("libcore", "AccountCreationInfo.init", { - index, - owners, - derivations, - publicKeys, - chainCodes, - }); - - const newAccountCreationInfos = await core.AccountCreationInfo.init( - index, - owners, - derivations, - publicKeys, - chainCodes - ); - - if (isUnsubscribed()) return; - - const account = await wallet.newAccountWithInfo(newAccountCreationInfos); - - return account; -}; diff --git a/src/libcore/errors.ts b/src/libcore/errors.ts deleted file mode 100644 index 66947b6972..0000000000 --- a/src/libcore/errors.ts +++ /dev/null @@ -1,20 +0,0 @@ -let impl = (e: unknown): Error => - e && e instanceof Error ? e : new Error(String(e)); - -export function setRemapLibcoreErrorsImplementation( - remap: (error: unknown) => Error -): void { - impl = remap; -} -export function remapLibcoreErrors(error: unknown): Error { - return impl(error); -} -export function isNonExistingAccountError(error: Error): boolean { - return error.message.includes("doesn't exist"); -} -export function isNonExistingWalletError(error: Error): boolean { - return error.message.includes("doesn't exist"); -} -export function isAlreadyExistingWalletError(error: Error): boolean { - return error.message.includes("already exists"); -} diff --git a/src/libcore/getAccountBalanceHistory.ts b/src/libcore/getAccountBalanceHistory.ts deleted file mode 100644 index 9daa83e0aa..0000000000 --- a/src/libcore/getAccountBalanceHistory.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { log } from "@ledgerhq/logs"; -import type { PortfolioRange, BalanceHistory } from "../types"; -import type { CoreAccount } from "./types"; -import { TimePeriod } from "./types"; -import { libcoreAmountToBigNumber } from "./buildBigNumber"; -import { promiseAllBatched } from "../promise"; -import { getDates, getPortfolioRangeConfig } from "../portfolio"; -import invariant from "invariant"; - -const getAccountBalanceHistory = async ( - coreA: CoreAccount, - range: PortfolioRange -): Promise => { - const dates = getDates(range); - const conf = getPortfolioRangeConfig(range); - const period = TimePeriod[conf.granularityId]; - log( - "getAccountBalanceHistory", - "calc for range=" + range + " with " + dates.length + " datapoint" - ); - // FIXME @gre the weird date adjustment we are doing - const to = new Date(conf.startOf(new Date()).getTime() + conf.increment - 1); - const fromISO = new Date(dates[0].valueOf() - conf.increment).toISOString(); - const toISO = to.toISOString(); - const rawBalances = await coreA.getBalanceHistory(fromISO, toISO, period); - const balances = await promiseAllBatched( - 5, - rawBalances, - libcoreAmountToBigNumber - ); - invariant( - balances.length === dates.length, - "Mismatch in sizes dates/balance" - ); - const balanceHistory = balances.map((value, i) => ({ - date: dates[i], - value, - })); - log( - "getAccountBalanceHistory", - "DONE. calc for range=" + - range + - ". " + - balanceHistory.length + - " datapoint. period=" + - period + - " range: [" + - fromISO + - ", " + - toISO + - "]" - ); - return balanceHistory; -}; - -export default getAccountBalanceHistory; diff --git a/src/libcore/getAccountNetworkInfo.ts b/src/libcore/getAccountNetworkInfo.ts deleted file mode 100644 index 92811865bf..0000000000 --- a/src/libcore/getAccountNetworkInfo.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { getWalletName } from "../account"; -import type { NetworkInfo } from "../generated/types"; -import type { Account } from "../types"; -import { withLibcoreF } from "./access"; -import { remapLibcoreErrors } from "./errors"; -import { getOrCreateWallet } from "./getOrCreateWallet"; -import { getOrCreateAccount } from "./getOrCreateAccount"; -import byFamily from "../generated/libcore-getAccountNetworkInfo"; - -type F = (arg0: Account) => Promise; - -export const getAccountNetworkInfo: F = withLibcoreF( - (core) => async (account) => { - try { - const { derivationMode, currency } = account; - const f = byFamily[currency.family]; - - if (!f) { - throw new Error( - "getAccountNetworkInfo is not implemented by family " + - currency.family - ); - } - - const walletName = getWalletName(account); - const coreWallet = await getOrCreateWallet({ - core, - walletName, - currency, - derivationMode, - }); - const coreAccount = await getOrCreateAccount({ - core, - coreWallet, - account, - }); - const res = await f({ - account, - coreAccount, - }); - return res; - } catch (error) { - throw remapLibcoreErrors(error); - } - } -); diff --git a/src/libcore/getCoreAccount.ts b/src/libcore/getCoreAccount.ts deleted file mode 100644 index 1cb22146bb..0000000000 --- a/src/libcore/getCoreAccount.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { Account } from "../types"; -import type { Core, CoreWallet, CoreAccount } from "./types"; -import { getWalletName } from "../account"; -import { getOrCreateWallet } from "./getOrCreateWallet"; -import { getOrCreateAccount } from "./getOrCreateAccount"; - -export const getCoreAccount = async ( - core: Core, - account: Account -): Promise<{ - coreWallet: CoreWallet; - coreAccount: CoreAccount; - walletName: string; -}> => { - const { currency, derivationMode, seedIdentifier } = account; - const walletName = getWalletName({ - currency, - seedIdentifier, - derivationMode, - }); - const coreWallet = await getOrCreateWallet({ - core, - walletName, - currency, - derivationMode, - }); - const coreAccount = await getOrCreateAccount({ - core, - coreWallet, - account, - }); - return { - walletName, - coreWallet, - coreAccount, - }; -}; diff --git a/src/libcore/getFeesForTransaction.ts b/src/libcore/getFeesForTransaction.ts deleted file mode 100644 index a481f62c61..0000000000 --- a/src/libcore/getFeesForTransaction.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { BigNumber } from "bignumber.js"; -import type { Account, Transaction } from "../types"; -import { withLibcoreF } from "./access"; -import { remapLibcoreErrors } from "./errors"; -import { getCoreAccount } from "./getCoreAccount"; -import byFamily from "../generated/libcore-getFeesForTransaction"; -import type { BitcoinInput, BitcoinOutput } from "../families/bitcoin/types"; - -export type Input = { - account: Account; - transaction: Transaction; -}; - -type F = (arg0: Input) => Promise<{ - estimatedFees: BigNumber; - estimatedGas: BigNumber | null | undefined; - // Note: Use in Cosmos - value: BigNumber; - txInputs?: BitcoinInput[]; - txOutputs?: BitcoinOutput[]; -}>; - -export const getFeesForTransaction: F = withLibcoreF( - (core) => - async ({ account, transaction }) => { - try { - const { currency } = account; - const { coreWallet, coreAccount } = await getCoreAccount(core, account); - const coreCurrency = await coreWallet.getCurrency(); - const f = byFamily[currency.family]; - if (!f) throw new Error("currency " + currency.id + " not supported"); - const fees = await f({ - account, - core, - coreAccount, - coreCurrency, - transaction, - isPartial: true, - isCancelled: () => false, - }); - return fees; - } catch (error) { - throw remapLibcoreErrors(error); - } - } -); diff --git a/src/libcore/getOrCreateAccount.ts b/src/libcore/getOrCreateAccount.ts deleted file mode 100644 index 5b06cef492..0000000000 --- a/src/libcore/getOrCreateAccount.ts +++ /dev/null @@ -1,93 +0,0 @@ -import invariant from "invariant"; -import { log } from "@ledgerhq/logs"; -import type { Account } from "../types"; -import { atomicQueue } from "../promise"; -import type { Core, CoreWallet, CoreAccount } from "./types"; -import { isNonExistingAccountError } from "./errors"; - -type Param = { - core: Core; - coreWallet: CoreWallet; - account: Account; -}; - -type F = (arg0: Param) => Promise; - -const restoreWithAccountCreationInfo = { - tezos: true, - stellar: true, - algorand: true, -}; - -export const getOrCreateAccount: F = atomicQueue( - async ({ core, coreWallet, account: { xpub, index, currency } }) => { - log("libcore", "getOrCreateAccount", { - xpub, - index, - }); - let coreAccount; - - try { - coreAccount = await coreWallet.getAccount(index); - } catch (err: any) { - if (!isNonExistingAccountError(err)) { - throw err; - } - - log("libcore", "no account existed. restoring..."); - invariant(xpub, "xpub is missing. Please reimport the account."); - - if (!xpub) return; - - if (restoreWithAccountCreationInfo[currency.id]) { - const accountCreationInfos = await coreWallet.getAccountCreationInfo( - index - ); - const chainCodes = await accountCreationInfos.getChainCodes(); - const publicKeys = await accountCreationInfos.getPublicKeys(); - const derivations = await accountCreationInfos.getDerivations(); - const owners = await accountCreationInfos.getOwners(); - publicKeys.push(xpub); - log("libcore", "AccountCreationInfo.init", { - index, - owners, - derivations, - publicKeys, - chainCodes, - }); - const newAccountCreationInfos = await core.AccountCreationInfo.init( - index, - owners, - derivations, - publicKeys, - chainCodes - ); - const account = await coreWallet.newAccountWithInfo( - newAccountCreationInfos - ); - return account; - } else { - const extendedInfos = - await coreWallet.getExtendedKeyAccountCreationInfo(index); - const infosIndex = await extendedInfos.getIndex(); - const extendedKeys = await extendedInfos.getExtendedKeys(); - const owners = await extendedInfos.getOwners(); - const derivations = await extendedInfos.getDerivations(); - extendedKeys.push(xpub); - const newExtendedKeys = await core.ExtendedKeyAccountCreationInfo.init( - infosIndex, - owners, - derivations, - extendedKeys - ); - const account = await coreWallet.newAccountWithExtendedKeyInfo( - newExtendedKeys - ); - return account; - } - } - - return coreAccount; - }, - ({ account }) => account.id || "" -); diff --git a/src/libcore/getOrCreateWallet.ts b/src/libcore/getOrCreateWallet.ts deleted file mode 100644 index fc6194c4be..0000000000 --- a/src/libcore/getOrCreateWallet.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { getLibcoreConfig, getDerivationScheme } from "../derivation"; -import type { CryptoCurrency, DerivationMode } from "../types"; -import { atomicQueue } from "../promise"; -import type { Core, CoreWallet } from "./types"; -import { findCurrencyExplorer } from "../api/Ledger"; -import { getEnv } from "../env"; -import { isAlreadyExistingWalletError } from "./errors"; - -type Param = { - core: Core; - walletName: string; - currency: CryptoCurrency; - derivationMode: DerivationMode; -}; - -type F = (arg0: Param) => Promise; - -export const getOrCreateWallet: F = atomicQueue( - async ({ core, walletName, currency, derivationMode }: Param) => { - const poolInstance = core.getPoolInstance(); - let wallet; - const config = await core.DynamicObject.newInstance(); - const configExtra = getLibcoreConfig(currency, derivationMode); - - if (configExtra) { - for (const k in configExtra) { - const v = configExtra[k]; - - if (typeof v === "string") { - await config.putString(k, v); - } - } - } - - const derivationScheme = getDerivationScheme({ - currency, - derivationMode, - }); - await config.putString("KEYCHAIN_DERIVATION_SCHEME", derivationScheme); - await config.putBoolean( - "DEACTIVATE_SYNC_TOKEN", - getEnv("DISABLE_SYNC_TOKEN") - ); - const KEYCHAIN_OBSERVABLE_RANGE = getEnv("KEYCHAIN_OBSERVABLE_RANGE"); - - if (KEYCHAIN_OBSERVABLE_RANGE) { - await config.putInt( - "KEYCHAIN_OBSERVABLE_RANGE", - KEYCHAIN_OBSERVABLE_RANGE - ); - } - - const ledgerExplorer = findCurrencyExplorer(currency); - - if (ledgerExplorer) { - const endpoint = ledgerExplorer.endpoint; - - if (endpoint) { - await config.putString("BLOCKCHAIN_EXPLORER_API_ENDPOINT", endpoint); - } - - await config.putString( - "BLOCKCHAIN_EXPLORER_VERSION", - ledgerExplorer.version - ); - } - - try { - // create it with the config - const currencyCore = await poolInstance.getCurrency(currency.id); - wallet = await poolInstance.createWallet( - walletName, - currencyCore, - config - ); - return wallet; - } catch (err: any) { - if (!isAlreadyExistingWalletError(err)) { - throw err; - } - - // actually wallet was existing... - // we need to sync the config in case it changed - await poolInstance.updateWalletConfig(walletName, config); - } - - wallet = await poolInstance.getWallet(walletName); - return wallet; - }, - ({ walletName }: { walletName: string }) => walletName -); diff --git a/src/libcore/isValidRecipient.ts b/src/libcore/isValidRecipient.ts deleted file mode 100644 index 6e597f7051..0000000000 --- a/src/libcore/isValidRecipient.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { InvalidAddress, RecipientRequired } from "@ledgerhq/errors"; -import type { CryptoCurrency } from "../types"; -import { withLibcoreF } from "./access"; -import customAddressValidationByFamily from "../generated/customAddressValidation"; -type F = (arg0: { - currency: CryptoCurrency; - recipient: string; -}) => Promise; -export const isValidRecipient: F = withLibcoreF((core) => async (arg) => { - const { currency, recipient } = arg; - - if (!recipient) { - return Promise.reject(new RecipientRequired("")); - } - - const custom = customAddressValidationByFamily[arg.currency.family]; - - if (custom) { - const res = await custom(core, arg); - return res; - } - - const poolInstance = core.getPoolInstance(); - const currencyCore = await poolInstance.getCurrency(currency.id); - const value = await core.Address.isValid(recipient, currencyCore); - - if (value) { - return Promise.resolve(null); - } - - return Promise.reject( - new InvalidAddress(undefined, { - currencyName: currency.name, - }) - ); -}); diff --git a/src/libcore/nativeSegwitAppsVersionsMap.ts b/src/libcore/nativeSegwitAppsVersionsMap.ts deleted file mode 100644 index f60409d6a4..0000000000 --- a/src/libcore/nativeSegwitAppsVersionsMap.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default { - DigiByte: "1.3.19", - Litecoin: "1.3.19", -}; diff --git a/src/libcore/platforms/nodejs.ts b/src/libcore/platforms/nodejs.ts deleted file mode 100644 index 25346af5fd..0000000000 --- a/src/libcore/platforms/nodejs.ts +++ /dev/null @@ -1,627 +0,0 @@ -/* eslint-disable new-cap */ -import invariant from "invariant"; -import { log } from "@ledgerhq/logs"; -import { NotEnoughBalance } from "@ledgerhq/errors"; -import { deserializeError, serializeError } from "@ledgerhq/errors"; -import { CoreWalletPool, reflect } from "../types"; -import type { Core, CoreStatics } from "../types"; -import { setLoadCoreImplementation } from "../access"; -import { setRemapLibcoreErrorsImplementation } from "../errors"; -import { getEnv } from "../../env"; -import network from "../../network"; - -import crypto from "crypto"; - -import path from "path"; - -import fs from "fs"; - -const prefixHex0x = (str) => (str.startsWith("0x") ? str : "0x" + str); - -const unprefixHex0x = (str) => (str.startsWith("0x") ? str.slice(2) : str); - -const hexToBytes = (str) => Array.from(Buffer.from(unprefixHex0x(str), "hex")); - -const bytesToHex = (buf) => Buffer.from(buf).toString("hex"); - -const bytesArrayToString = (bytesArray: string[] = []) => - Buffer.from(bytesArray as any).toString(); - -const stringToBytesArray = (str) => Array.from(Buffer.from(str)); - -export default (arg: { - // the actual @ledgerhq/ledger-core lib or a function that returns it - lib: any; - dbPath: string; - dbPassword?: string; -}): void => { - let lib; - - const lazyLoad = () => { - if (lib) return; - - if (typeof arg.lib === "function") { - lib = arg.lib(); - } else { - lib = arg.lib; - } - }; - - const { dbPath } = arg; - const dbPassword = - typeof arg.dbPassword === "undefined" - ? getEnv("LIBCORE_PASSWORD") - : arg.dbPassword; - - const loadCore = (): Promise => { - lazyLoad(); - // feature detect if the bindings uses hex or array bytes - const isUsingArrayOfBytes = - "object" === typeof new lib.NJSDynamicArray().serialize(); - log("libcore", "using array of bytes = " + String(isUsingArrayOfBytes)); - const wrappers = { - hex: isUsingArrayOfBytes ? hexToBytes : prefixHex0x, - }; - const unwrappers = { - hex: isUsingArrayOfBytes ? bytesToHex : unprefixHex0x, - }; - const MAX_RANDOM = 2684869021; - const lcore = new lib.NJSLedgerCore(); - const stringVersion = lcore.getStringVersion(); - const sqlitePrefix = `v${stringVersion.split(".")[0]}`; - const NJSExecutionContextImpl = { - execute: (runnable) => { - try { - const runFunction = () => runnable.run(); - - setImmediate(runFunction); - } catch (e) { - log("libcore-Error", String(e)); - } - }, - delay: (runnable, ms) => setTimeout(() => runnable.run(), ms), - }; - const ThreadContexts = {}; - - const getSerialExecutionContext = (name) => { - let currentContext = ThreadContexts[name]; - - if (!currentContext) { - currentContext = new lib.NJSExecutionContext(NJSExecutionContextImpl); - ThreadContexts[name] = currentContext; - } - - return currentContext; - }; - - const getMainExecutionContext = () => getSerialExecutionContext("main"); - - const NJSThreadDispatcher = new lib.NJSThreadDispatcher({ - contexts: ThreadContexts, - getThreadPoolExecutionContext: (name) => getSerialExecutionContext(name), - getMainExecutionContext, - getSerialExecutionContext, - newLock: () => { - log( - "libcore-Warn", - "libcore NJSThreadDispatcher: newLock: Not implemented" - ); - }, - }); - NJSThreadDispatcher.getMainExecutionContext = getMainExecutionContext; - - function createHttpConnection(res, libcoreError) { - if (!res) { - return null; - } - - const headersMap = new Map(); - Object.keys(res.headers).forEach((key) => { - if (typeof res.headers[key] === "string") { - headersMap.set(key, res.headers[key]); - } - }); - const NJSHttpUrlConnectionImpl = { - getStatusCode: () => Number(res.status), - getStatusText: () => res.statusText, - getHeaders: () => headersMap, - readBody: () => ({ - error: libcoreError, - data: isUsingArrayOfBytes ? stringToBytesArray(res.data) : res.data, - }), - }; - return new lib.NJSHttpUrlConnection(NJSHttpUrlConnectionImpl); - } - - const NJSHttpClient = new lib.NJSHttpClient({ - execute: async (r) => { - const method = r.getMethod(); - const headersMap = r.getHeaders(); - const url = r.getUrl(); - let data = r.getBody(); - const headers = {}; - headersMap.forEach((v, k) => { - headers[k] = v; - }); - let res; - const param: Record = { - method: lib.METHODS[method], - url, - headers, - validateStatus: ( - status // FIXME in future, everything should passthrough libcore - ) => - // for now as we need to have the server error we will only pass-in 2xx and 404 - // FIXME for the FIXME: Stargate nodes return 500 when an account has no delegations - // or no unbondings or no redelegations. So for cosmos, status 500 need to go to libcore for proper handling - { - const isCosmosRequest = - url.includes("/cosmos/") || url.includes("cosmos.coin"); - return ( - (status >= 200 && status < 300) || - status === 404 || - (isCosmosRequest && status === 500) - ); - }, - // the default would parse the request, we want to preserve the string - transformResponse: (data) => data, - }; - - if (isUsingArrayOfBytes) { - if (Array.isArray(data)) { - if (data.length === 0) { - data = null; - } else { - // we transform back to a string - data = bytesArrayToString(data); - } - } - } else if ( - headers["Content-Type"] && - headers["Content-Type"] === "application/x-binary" - ) { - data = Buffer.from(unprefixHex0x(data), "hex"); - } else { - if (typeof data === "string" && data) { - data = Buffer.from(unprefixHex0x(data), "hex").toString(); - } - } - - if (data) { - param.data = data; - - if (!headers["Content-Type"]) { - headers["Content-Type"] = "application/json"; - } - } - - try { - res = await network(param); - const urlConnection = createHttpConnection(res, null); - r.complete(urlConnection, null); - } catch (err: any) { - const libcoreError = { - code: lib.ERROR_CODE.HTTP_ERROR, - message: JSON.stringify( - serializeError({ - message: err.message, - name: err.name, - }) - ), - stack: err.stack, - }; - const urlConnection = createHttpConnection(res, libcoreError); - r.complete(urlConnection, libcoreError); - } - }, - }); - const NJSWebSocketClient = new lib.NJSWebSocketClient({ - connect: (url, connection) => { - connection.OnConnect(); - }, - send: (connection, data) => { - connection.OnMessage(data); - }, - disconnect: (connection) => { - connection.OnClose(); - }, - }); - const NJSLogPrinter = new lib.NJSLogPrinter({ - context: {}, - printError: (message) => log("libcore-Error", message), - printInfo: (message) => log("libcore-Info", message), - printDebug: (message) => log("libcore-Debug", message), - printWarning: (message) => log("libcore-Warning", message), - printApdu: (message) => log("libcore-Apdu", message), - printCriticalError: (message) => log("libcore-CriticalError", message), - getContext: () => new lib.NJSExecutionContext(NJSExecutionContextImpl), - }); - const NJSRandomNumberGenerator = new lib.NJSRandomNumberGenerator({ - getRandomBytes: isUsingArrayOfBytes - ? (size) => - // @ts-expect-error Buffer lib ts definition missmatch - Array.from(Buffer.from(crypto.randomBytes(size), "hex")) - : (size) => "0x" + crypto.randomBytes(size).toString("hex"), - getRandomInt: () => Math.random() * MAX_RANDOM, - getRandomLong: () => Math.random() * MAX_RANDOM * MAX_RANDOM, - }); - const NJSDatabaseBackend = new lib.NJSDatabaseBackend(); - const config = new lib.NJSDynamicObject(); - // We handle logs ourself with the logger - // still overridable by env - config.putBoolean( - "ENABLE_INTERNAL_LOGGING", - !!process.env.LIBCORE_ENABLE_INTERNAL_LOGGING - ); - let walletPoolInstance: CoreWalletPool | null = null; - - const instanciateWalletPool = (): CoreWalletPool => { - try { - fs.mkdirSync(dbPath); - } catch (err: any) { - if (err.code !== "EEXIST") { - throw err; - } - } - - const NJSPathResolver = new lib.NJSPathResolver({ - resolveLogFilePath: (pathToResolve) => { - const hash = pathToResolve.replace(/\//g, "__"); - return path.resolve(dbPath, `./log_file_${sqlitePrefix}_${hash}`); - }, - resolvePreferencesPath: (pathToResolve) => { - const hash = pathToResolve.replace(/\//g, "__"); - return path.resolve(dbPath, `./preferences_${sqlitePrefix}_${hash}`); - }, - resolveDatabasePath: (pathToResolve) => { - const hash = pathToResolve.replace(/\//g, "__"); - return path.resolve(dbPath, `./database_${sqlitePrefix}_${hash}`); - }, - }); - walletPoolInstance = new lib.NJSWalletPool( - "ledgerlive", - dbPassword, - NJSHttpClient, - NJSWebSocketClient, - NJSPathResolver, - NJSLogPrinter, - NJSThreadDispatcher, - NJSRandomNumberGenerator, - NJSDatabaseBackend, - config - ); - return walletPoolInstance; - }; - - const getPoolInstance = (): CoreWalletPool => { - if (!walletPoolInstance) { - instanciateWalletPool(); - } - - invariant(walletPoolInstance, "can't initialize walletPoolInstance"); - return walletPoolInstance; - }; - - const mappings: Record | CoreStatics = {}; - Object.keys(lib).forEach((k) => { - if (k.startsWith("NJS")) { - mappings[k.slice(3)] = lib[k]; - } - }); - - function wrapResult(id, value) { - if (!value || !id) return value; - - if (Array.isArray(id)) { - const [actualId] = id; - return value.map((a) => wrapResult(actualId, a)); - } - - if (id in unwrappers) { - return unwrappers[id](value); - } - - const Clz = mappings[id]; - - if (!Clz) { - return value; - } - - if (value instanceof Clz) return value; - return new Clz(value); - } - - function unwrapArg(id, value) { - if (!value || !id) return value; - - if (Array.isArray(id)) { - const [actualId] = id; - return value.map((v) => unwrapArg(actualId, v)); - } - - if (id in wrappers) { - return wrappers[id](value); - } - - return value; - } - - reflect((id, spec) => { - const { methods, statics } = spec; - let m; - - if (spec.njsUsesPlainObject) { - // In that case we need to create a class and abstract out the methods - m = function constructor(data) { - Object.assign(this, data); - }; - - mappings[id] = m; - } else { - m = mappings[id]; - - if (!m) { - return; - } - } - - if (statics) { - Object.keys(statics).forEach((method) => { - const { - njsInstanciateClass, - njsBuggyMethodIsNotStatic, - params, - returns, - } = statics[method]; - - if (njsInstanciateClass) { - m[method] = function met(...vargs) { - if (process.env.VERBOSE) { - log("libcore-call", id + "." + method, vargs); - } - - const args = njsInstanciateClass.map((arg) => { - if (typeof arg === "object") { - const o = {}; - - for (const k in arg) { - const index = arg[k]; - o[k] = unwrapArg(params && params[index], vargs[index]); - } - - return o; - } - - return arg; - }); - // @ts-expect-error ts must target es5 or higher - const value = new m(...args); - - if (process.env.VERBOSE) { - log("libcore-result", id + "." + method, { - value, - }); - } - - return value; - }; - } else if (njsBuggyMethodIsNotStatic) { - // There is a bug in the node bindings that don't expose the static functions - m[method] = (...args) => { - if (process.env.VERBOSE) { - log("libcore-call", id + "." + method, args); - } - - let value; - - if (params) { - // it's seems statics method until now doesn't need to be unwrap - const hexArgs = unwrapArg(params, args); - // @ts-expect-error ts must target es5 or higher - value = new m(...hexArgs)[method](...hexArgs); - } else { - const constructorArgs = - typeof njsBuggyMethodIsNotStatic === "function" - ? njsBuggyMethodIsNotStatic(args) - : args; - // @ts-expect-error ts must target es5 or higher - value = new m(...constructorArgs)[method](...args); - } - - if (process.env.VERBOSE) { - log("libcore-result", id + "." + method, { - value, - }); - } - - if (returns) { - return wrapResult(returns, value); - } - - return value; - }; - } - }); - } - - if (methods) { - Object.keys(methods).forEach((method) => { - const { njsField, params, returns, nodejsNotAvailable } = - methods[method]; - if (nodejsNotAvailable) return; - - if (njsField) { - m.prototype[method] = function met() { - if (process.env.VERBOSE) { - log("libcore-call", id + "#" + method); - } - - const value = this[njsField]; - - if (process.env.VERBOSE) { - log("libcore-result", id + "#" + method, { - value, - }); - } - - const Cls = - typeof returns === "string" && returns in mappings - ? mappings[returns] - : null; - - if (Cls && !(value instanceof Cls)) { - const inst = new Cls(value); - return inst; - } - - return wrapResult(returns, value); - }; - } else { - const f = m.prototype[method]; - - if (!f) { - console.warn(`no such method '${method}' in ${id}`); - return; - } - - m.prototype[method] = async function met(...a) { - if (process.env.VERBOSE) { - log("libcore-call", id + "#" + method, a); - } - - const args = params - ? a.map((value, i) => unwrapArg(params[i], value)) - : a; - const value = await f.apply(this, args); - - if (process.env.VERBOSE) { - log("libcore-result", id + "#" + method, { - value, - }); - } - - return wrapResult(returns, value); - }; - } - }); - } - }); - // other NodeJS bindings specific code - const eventBusSubscribe = mappings.EventBus.prototype.subscribe; - - mappings.EventBus.prototype.subscribe = function subscribe( - executionContext, - receiver_ - ) { - const receiver = receiver_; - return new Promise((resolve, reject) => { - receiver._resolve = resolve; - receiver._reject = reject; - eventBusSubscribe.call(this, executionContext, receiver); - }); - }; - - mappings.EventReceiver.newInstance = () => { - const receiver = new mappings.EventReceiver({ - onEvent: (e) => { - const code = e.getCode(); - - if ( - code === lib.EVENT_CODE.UNDEFINED || - code === lib.EVENT_CODE.SYNCHRONIZATION_FAILED - ) { - const payload = e.getPayload(); - const message = ( - (payload && payload.getString("EV_SYNC_ERROR_MESSAGE")) || - "Sync failed" - ).replace(" (EC_PRIV_KEY_INVALID_FORMAT)", ""); - - try { - receiver._reject(deserializeError(JSON.parse(message))); - } catch (_e) { - receiver._reject(message); - } - - return; - } - - if ( - code === lib.EVENT_CODE.SYNCHRONIZATION_SUCCEED || - code === - lib.EVENT_CODE.SYNCHRONIZATION_SUCCEED_ON_PREVIOUSLY_EMPTY_ACCOUNT - ) { - receiver._resolve(); - } - }, - }); - return receiver; - }; - - const cs: CoreStatics = mappings; - // @ts-expect-error i tried... - const core: Core = { - ...cs, - flush: () => Promise.resolve(), - getPoolInstance, - getThreadDispatcher: () => NJSThreadDispatcher, - }; - return Promise.resolve(core); - }; - - function parseError(error: string): Error { - const m = error.match(/[^{]*({.*}).*/); - - if (m) { - const json = JSON.parse(m[1]); - - if (json.name) { - return deserializeError(json); - } - } - - return new Error(String(error)); - } - - const remapLibcoreErrors = (e: unknown): Error => { - lazyLoad(); - - if (typeof e === "string") { - try { - return parseError(e); - } catch (e2: any) { - return e2; - } - } - - if (e && typeof e === "object") { - if ( - typeof (<{ code?: number }>e).code === "number" && - (<{ code: number }>e).code === lib.ERROR_CODE.NOT_ENOUGH_FUNDS - ) { - return new NotEnoughBalance(); - } else { - // re-deserialize error if it was a serialized error - if ((<{ message?: string }>e).message === "string") { - try { - return parseError((<{ message: string }>e).message); - } catch (_e2) { - console.error(_e2); - } - } - } - } - - if (e instanceof Error) { - return e; - } - - if (e && typeof (<{ message?: string }>e).message === "string") { - return new Error((<{ message: string }>e).message); - } - - return new Error(String(e)); - }; - - setLoadCoreImplementation(loadCore); - setRemapLibcoreErrorsImplementation(remapLibcoreErrors); -}; diff --git a/src/libcore/platforms/react-native.ts b/src/libcore/platforms/react-native.ts deleted file mode 100644 index 4aa43e4062..0000000000 --- a/src/libcore/platforms/react-native.ts +++ /dev/null @@ -1,242 +0,0 @@ -import { NotEnoughBalance, NetworkDown } from "@ledgerhq/errors"; -import type { Core, CoreStatics, Spec } from "../types"; -import { reflect } from "../types"; -import { setLoadCoreImplementation } from "../access"; -import { setRemapLibcoreErrorsImplementation } from "../errors"; - -export default (arg: { getNativeModule: (id: string) => any }): void => { - const { getNativeModule } = arg; - - async function loadCore(): Promise { - // Abstract out the libcore interface into a higher level API. - const mappings: CoreStatics | Record = {}; - const wrappers = { - // FIXME need to fix it in RN bindings - hex: (value) => value.replace(/[< >]/g, ""), - }; - - function wrap(id, ref) { - if (!ref) return ref; - - if (Array.isArray(id)) { - const [actualId] = id; - return ref.map((r) => wrap(actualId, r)); - } - - if (id in wrappers) { - return wrappers[id](ref); - } - - const Clz = mappings[id]; - - if (!Clz) { - console.warn(`Unknown wrapping '${id}'`); - return ref; - } - - return new Clz(ref); - } - - function unwrap(id, instance, ctx) { - if (!instance) return instance; - - if (Array.isArray(id)) { - const [actualId] = id; - return instance.map((inst) => unwrap(actualId, inst, undefined)); - } - - const Clz = mappings[id]; - - if (!Clz) { - return instance; - } - - if (!(instance instanceof Clz)) { - throw new Error(`Expected '${String(id)}' instance in ${String(ctx)}`); - } - - return instance.ref; - } - - const flushes: any[] = []; - const blacklistFlushes = [ - "ThreadDispatcher", - "HttpClient", - "WebSocketClient", - "PathResolver", - "LogPrinter", - "RandomNumberGenerator", - "DatabaseBackend", - "DynamicObject", - "WalletPool", - "SerialContext", - "", - ]; - - function declare(id: string, { methods, statics }: Spec) { - const native = getNativeModule(id); - - if (!blacklistFlushes.includes(id)) { - flushes.push(() => { - try { - return native.flush(); - } catch (e) { - console.warn("no flush for " + id); - } - }); - } - - // There are lot of decoration done to abstract out libcore api. - // The plan is to make it converge to this API in the future - // so we have less runtime wrapping. - const unwrapArgs = (args, params, ctx) => - params - ? [...args].map((value, i) => { - const spec = params[i]; - return spec ? unwrap(spec, value, ctx) : value; - }) - : args; - - const wrapResult = (r, returns) => { - let res = r; - - if (res && typeof res === "object") { - if (res.then) { - return res.then((re) => wrapResult(re, returns)); - } - - if ("value" in res) { - res = res.value; - } - } - - if (!returns) return res; - return wrap(returns, res); - }; - - const proto = {}; - - if (methods) { - Object.keys(methods).forEach((method) => { - const { returns, params } = methods[method]; - const f = native && native[method]; - - if (!f) { - console.warn( - `LibCore: module '${id}' method '${method}' is missing in native side` - ); - } - - const ctx = `${id}#${method}`; - - proto[method] = function fn(...args) { - const unwrapped = unwrapArgs(args, params, ctx); - const r = f.call(native, unwrap(id, this, ctx), ...unwrapped); - return wrapResult(r, returns); - }; - }); - } - - function constructor(ref) { - this.ref = ref; - } - - constructor.prototype = proto; - - if (statics) { - Object.keys(statics).forEach((method) => { - const { returns, params } = statics[method]; - const f = native && native[method]; - - if (!f) { - console.warn( - `LibCore: module '${id}' static method '${method}' is missing in native side` - ); - } - - const ctx = `${id}.${method}`; - - constructor[method] = (...args) => { - const unwrapped = unwrapArgs(args, params, ctx); - const r = f.call(native, ...unwrapped); - return wrapResult(r, returns); - }; - }); - } - - if (id in mappings) { - console.warn("LibCore: Already declared " + id); - } - - mappings[id] = constructor; - } - - reflect(declare); - const cs: CoreStatics = mappings; - const threadDispatcher = await cs.ThreadDispatcher.newInstance(); - const httpClient = await cs.HttpClient.newInstance(); - const webSocket = await cs.WebSocketClient.newInstance(); - const pathResolver = await cs.PathResolver.newInstance(); - const logPrinter = await cs.LogPrinter.newInstance(); - const rng = await cs.RandomNumberGenerator.newInstance(); - const backend = await cs.DatabaseBackend.getSqlite3Backend(); - const walletDynObject = await cs.DynamicObject.newInstance(); - const walletPoolInstance = await cs.WalletPool.newInstance( - "ledgerlive", - "", - httpClient, - webSocket, - pathResolver, - logPrinter, - threadDispatcher, - rng, - backend, - walletDynObject - ); - const core = { - ...cs, - flush: () => Promise.all(flushes.map((f) => f())).then(() => undefined), - getPoolInstance: () => walletPoolInstance, - getThreadDispatcher: () => threadDispatcher, - } as Core; - return core; - } - - function remapLibcoreErrors(error: unknown): Error { - if (!(error && error instanceof Error)) { - return new Error(String(error)); - } - - if (!error || !error.message) return error; - const msg = error.message; - - if ( - msg.includes("Cannot gather enough funds") || - msg.includes("There is no UTXO on this account.") - ) { - return new NotEnoughBalance(); - } - - if ( - msg.includes("The Internet connection appears to be offline") || - msg.includes( - '"explorers.api.live.ledger.com": No address associated with hostname' - ) - ) { - return new NetworkDown(); - } - - // Attempt to recover the human readable error from a verbose iOS trace - const pattern = /NS[\w]+Error.+Code.+"([\w .]+)"/; - const match = pattern.exec(msg); - - if (match && match[1] !== "(null)") { - return new Error(match[1]); - } - - return error; - } - - setLoadCoreImplementation(loadCore); - setRemapLibcoreErrorsImplementation(remapLibcoreErrors); -}; diff --git a/src/libcore/scanAccounts.ts b/src/libcore/scanAccounts.ts deleted file mode 100644 index e26c199e59..0000000000 --- a/src/libcore/scanAccounts.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { Observable } from "rxjs"; -import Transport from "@ledgerhq/hw-transport"; -import { log } from "@ledgerhq/logs"; -import { TransportStatusError, UserRefusedAddress } from "@ledgerhq/errors"; -import semver from "semver"; -import { - getDerivationModesForCurrency, - getSeedIdentifierDerivation, - derivationModeSupportsIndex, - isIterableDerivationMode, - getMandatoryEmptyAccountSkip, - getDerivationModeStartsAt, -} from "../derivation"; -import { getWalletName, shouldShowNewAccount } from "../account"; -import type { - Account, - CryptoCurrency, - DerivationMode, - ScanAccountEvent, - SyncConfig, -} from "../types"; -import { withDevice } from "../hw/deviceAccess"; -import getAddress from "../hw/getAddress"; -import getAppAndVersion from "../hw/getAppAndVersion"; -import { withLibcoreF } from "./access"; -import { syncCoreAccount, newSyncLogId } from "./syncAccount"; -import { getOrCreateWallet } from "./getOrCreateWallet"; -import { - DerivationsCache, - createAccountFromDevice, -} from "./createAccountFromDevice"; -import { remapLibcoreErrors, isNonExistingAccountError } from "./errors"; -import { GetAppAndVersionUnsupportedFormat } from "../errors"; -import nativeSegwitAppsVersionsMap from "./nativeSegwitAppsVersionsMap"; -import type { Core, CoreWallet } from "./types"; -import { GetAddressOptions, Result } from "../hw/getAddress/types"; - -async function scanNextAccount(props: { - core: Core; - wallet: CoreWallet; - currency: CryptoCurrency; - accountIndex: number; - onAccountScanned: (arg0: Account) => any; - seedIdentifier: string; - derivationMode: DerivationMode; - showNewAccount: boolean; - isUnsubscribed: () => boolean; - emptyCount?: number; - syncConfig: SyncConfig; - derivationsCache: DerivationsCache; - getAddr: (opts: GetAddressOptions) => Promise; -}) { - const logId = newSyncLogId(); - const { - core, - wallet, - currency, - accountIndex, - onAccountScanned, - seedIdentifier, - derivationMode, - showNewAccount, - isUnsubscribed, - syncConfig, - derivationsCache, - getAddr, - } = props; - log( - "libcore", - `sync(${logId}) scanNextAccount started. ${currency.id}|${derivationMode}|${accountIndex}` - ); - let coreAccount; - - try { - coreAccount = await wallet.getAccount(accountIndex); - } catch (err: any) { - if (!isNonExistingAccountError(err)) { - throw err; - } - - if (isUnsubscribed()) return; - coreAccount = await createAccountFromDevice({ - core, - wallet, - currency, - index: accountIndex, - derivationMode, - isUnsubscribed, - derivationsCache, - getAddress: getAddr, - }); - } - - if (isUnsubscribed() || !coreAccount) return; - const account = await syncCoreAccount({ - core, - coreWallet: wallet, - coreAccount, - currency, - accountIndex, - derivationMode, - seedIdentifier, - logId, - syncConfig, - }); - if (isUnsubscribed()) return; - const shouldSkip = - accountIndex < getDerivationModeStartsAt(derivationMode) || - (!account.used && !showNewAccount) || - !derivationModeSupportsIndex(derivationMode, accountIndex); - log( - "libcore", - `scanning ${currency.id} ${ - derivationMode || "default" - }@${accountIndex}: resulted of ${ - account && !shouldSkip - ? `Account with ${account.operationsCount} txs (xpub ${String( - account.xpub - )}, fresh ${account.freshAddressPath} ${account.freshAddress})` - : "no account" - }. ${!account.used ? "ALL SCANNED" : ""}` - ); - - if (!shouldSkip) { - onAccountScanned(account); - } - - const emptyCount = props.emptyCount || 0; - const shouldIter = !account.used - ? emptyCount < getMandatoryEmptyAccountSkip(derivationMode) - : isIterableDerivationMode(derivationMode); - - if (shouldIter) { - await scanNextAccount({ - ...props, - accountIndex: accountIndex + 1, - emptyCount: !account.used ? emptyCount + 1 : 0, - }); - } -} - -const libcoreBlacklist = ["taproot"]; - -export const scanAccounts = ({ - currency, - deviceId, - scheme, - syncConfig, - getAddressFn, -}: { - currency: CryptoCurrency; - deviceId: string; - scheme?: DerivationMode | null | undefined; - syncConfig: SyncConfig; - getAddressFn?: ( - transport: Transport - ) => (opts: GetAddressOptions) => Promise; -}): Observable => - withDevice(deviceId)((transport) => - Observable.create((o) => { - let finished = false; - - const unsubscribe = () => { - finished = true; - }; - - const isUnsubscribed = () => finished; - - const main = withLibcoreF((core) => async () => { - try { - let derivationModes = getDerivationModesForCurrency(currency); - - if (scheme !== undefined) { - derivationModes = derivationModes.filter((mode) => mode === scheme); - } - - derivationModes = derivationModes.filter( - (m) => !libcoreBlacklist.includes(m) - ); - - for (let i = 0; i < derivationModes.length; i++) { - const derivationMode = derivationModes[i]; - const path = getSeedIdentifierDerivation(currency, derivationMode); - let result; - - if (derivationMode === "native_segwit") { - if (nativeSegwitAppsVersionsMap[currency.managerAppName]) { - try { - const { version } = await getAppAndVersion(transport); - - if ( - !semver.gte( - version, - nativeSegwitAppsVersionsMap[currency.managerAppName] - ) - ) { - continue; - } - } catch (e) { - // in case the apdu is not even supported, we assume to not do native_segwit - if ( - (e instanceof TransportStatusError && - // @ts-expect-error TransportStatusError to be typed on ledgerjs - e.statusCode === 0x6d00) || - e instanceof GetAppAndVersionUnsupportedFormat - ) { - continue; - } - - throw e; - } - } - } - - const getAddr = getAddressFn - ? getAddressFn(transport) - : (opts) => getAddress(transport, opts); - - try { - result = await getAddr({ currency, path, derivationMode }); - } catch (e) { - // feature detection: some old app will specifically returns this code for segwit case and we ignore it - // we also feature detect any denying case that could happen - if ( - e instanceof TransportStatusError || - e instanceof UserRefusedAddress - ) { - log("scanAccounts", "ignore derivationMode=" + derivationMode); - } - } - - if (!result) continue; - const seedIdentifier = result.publicKey; - if (isUnsubscribed()) return; - const walletName = getWalletName({ - seedIdentifier, - currency, - derivationMode, - }); - const wallet = await getOrCreateWallet({ - core, - walletName, - currency, - derivationMode, - }); - if (isUnsubscribed()) return; - - const onAccountScanned = (account) => - o.next({ - type: "discovered", - account, - }); - - // recursively scan all accounts on device on the given app - // new accounts will be created in sqlite, existing ones will be updated - await scanNextAccount({ - core, - wallet, - currency, - accountIndex: 0, - onAccountScanned, - seedIdentifier, - derivationMode, - showNewAccount: shouldShowNewAccount(currency, derivationMode), - isUnsubscribed, - syncConfig, - derivationsCache: new DerivationsCache(), - getAddr, - }); - } - - o.complete(); - } catch (e) { - o.error(remapLibcoreErrors(e)); - } - }); - main(); - return unsubscribe; - }) - ); diff --git a/src/libcore/signOperation.ts b/src/libcore/signOperation.ts deleted file mode 100644 index 097c598585..0000000000 --- a/src/libcore/signOperation.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { Observable, from } from "rxjs"; -import { tap } from "rxjs/operators"; -import Transport from "@ledgerhq/hw-transport"; -import { StatusCodes } from "@ledgerhq/hw-transport"; -import { UpdateYourApp } from "@ledgerhq/errors"; -import { log } from "@ledgerhq/logs"; -import type { - AccountBridge, - Account, - CryptoCurrency, - SignedOperation, - Transaction, - DerivationMode, - SignOperationEvent, -} from "../types"; -import type { Core, CoreAccount, CoreCurrency, CoreWallet } from "./types"; -import { withDevice } from "../hw/deviceAccess"; -import { toTransactionRaw, toSignOperationEventRaw } from "../transaction"; -import { getCoreAccount } from "./getCoreAccount"; -import { remapLibcoreErrors } from "./errors"; -import { withLibcore } from "./access"; - -export type Arg = { - buildTransaction: (arg0: { - account: Account; - core: Core; - coreCurrency: CoreCurrency; - coreAccount: CoreAccount; - coreWallet: CoreWallet; - transaction: T; - isPartial: boolean; - isCancelled: () => boolean; - }) => Promise; - signTransaction: (arg0: { - account: Account; - subAccountId: string | null | undefined; - transport: Transport; - currency: CryptoCurrency; - derivationMode: DerivationMode; - coreCurrency: CoreCurrency; - transaction: T; - coreTransaction: CT; - coreAccount: CoreAccount; - isCancelled: () => boolean; - onDeviceStreaming: (arg0: { - progress: number; - index: number; - total: number; - }) => void; - onDeviceSignatureRequested: () => void; - onDeviceSignatureGranted: () => void; - }) => Promise; -}; - -type SignOperation = AccountBridge["signOperation"]; - -export const makeSignOperation = - ({ - buildTransaction, - signTransaction, - }: Arg): SignOperation => - ({ account, transaction, deviceId }) => - Observable.create((o) => { - let cancelled = false; - withLibcore(async (core) => { - if (cancelled) return; - const { currency, derivationMode } = account; - const { coreAccount, coreWallet } = await getCoreAccount(core, account); - if (cancelled) return; - const coreCurrency = await coreWallet.getCurrency(); - if (cancelled) return; - log("libcore", "buildTransaction", toTransactionRaw(transaction)); - const builded = await buildTransaction({ - account, - core, - coreCurrency, - coreAccount, - transaction, - coreWallet, - isPartial: false, - isCancelled: () => cancelled, - }); - if (cancelled || !builded) return; - const signedOperation = await withDevice(deviceId)((transport) => - from( - signTransaction({ - account, - subAccountId: transaction.subAccountId, - transport, - currency, - derivationMode, - coreCurrency, - coreAccount, - transaction, - coreTransaction: builded, - isCancelled: () => cancelled, - onDeviceStreaming: ({ progress, index, total }) => - o.next({ - type: "device-streaming", - progress, - index, - total, - }), - onDeviceSignatureRequested: () => - o.next({ - type: "device-signature-requested", - }), - onDeviceSignatureGranted: () => - o.next({ - type: "device-signature-granted", - }), - }).catch((e) => { - // TODO where to remap this generically??? - if (e && e.statusCode === StatusCodes.INCORRECT_P1_P2) { - throw new UpdateYourApp( - `UpdateYourApp ${currency.id}`, - currency - ); - } - - throw e; - }) - ) - ).toPromise(); - if (cancelled || !signedOperation) return; - o.next({ - type: "signed", - signedOperation, - }); - }).then( - () => o.complete(), - (e) => o.error(remapLibcoreErrors(e)) - ); - return () => { - cancelled = true; - }; - }).pipe( - tap((e: SignOperationEvent) => - log("signOperation", "event " + e.type, toSignOperationEventRaw(e)) - ) - ); diff --git a/src/libcore/syncAccount.ts b/src/libcore/syncAccount.ts deleted file mode 100644 index 7e7183d946..0000000000 --- a/src/libcore/syncAccount.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { Observable, from, defer } from "rxjs"; -import { map } from "rxjs/operators"; -import { log } from "@ledgerhq/logs"; -import { SyncError } from "@ledgerhq/errors"; -import type { - SyncConfig, - Account, - CryptoCurrency, - DerivationMode, -} from "../types"; -import { withLibcore } from "./access"; -import { buildAccount } from "./buildAccount"; -import { getCoreAccount } from "./getCoreAccount"; -import { remapLibcoreErrors } from "./errors"; -import { - shouldRetainPendingOperation, - recalculateAccountBalanceHistories, -} from "../account"; -import postSyncPatchPerFamily from "../generated/libcore-postSyncPatch"; -import perFamilyPresync from "../generated/presync"; - -let coreSyncCounter = 0; - -export const newSyncLogId = (): number => ++coreSyncCounter; - -export async function syncCoreAccount({ - core, - coreWallet, - coreAccount, - currency, - accountIndex, - derivationMode, - seedIdentifier, - existingAccount, - logId, - syncConfig, -}: { - core: any; - coreWallet: any; - coreAccount: any; - currency: CryptoCurrency; - accountIndex: number; - derivationMode: DerivationMode; - seedIdentifier: string; - existingAccount?: Account | null | undefined; - logId: number; - syncConfig: SyncConfig; - walletName?: string; // @ts-check this is unused somehow -}): Promise { - const presync = perFamilyPresync[currency.family]; - - if (presync) { - await presync(currency); - } - - try { - if (!syncConfig.withoutSynchronize) { - log("libcore", `sync(${logId}) syncCoreAccount`); - const eventReceiver = await core.EventReceiver.newInstance(); - const eventBus = await coreAccount.synchronize(); - log("libcore", `sync(${logId}) DONE coreAccount.synchronize`); - const serialContext = await core - .getThreadDispatcher() - .getMainExecutionContext(); - await eventBus.subscribe(serialContext, eventReceiver); - log("libcore", `sync(${logId}) DONE eventBus.subscribe`); - } - - const account = await buildAccount({ - core, - coreWallet, - coreAccount, - currency, - accountIndex, - derivationMode, - seedIdentifier, - existingAccount, - logId, - syncConfig, - }); - return account; - } catch (e) { - const specificError = remapLibcoreErrors(e); - if (specificError.name !== "Error") throw specificError; - throw new SyncError(specificError.message); - } -} - -const defaultPostSyncPatch = (initial: Account, synced: Account): Account => - synced; - -export function sync( - existingAccount: Account, - syncConfig: SyncConfig -): Observable<(arg0: Account) => Account> { - const logId = newSyncLogId(); - const { derivationMode, seedIdentifier, currency } = existingAccount; - const postSyncPatch = - postSyncPatchPerFamily[currency.family] || defaultPostSyncPatch; - return defer(() => - from( - withLibcore((core) => { - log("libcore", `sync(${logId}) started. ${existingAccount.id}`); - return getCoreAccount(core, existingAccount).then( - ({ coreWallet, coreAccount, walletName }) => - syncCoreAccount({ - core, - coreWallet, - coreAccount, - walletName, - currency, - accountIndex: existingAccount.index, - derivationMode, - seedIdentifier, - existingAccount, - logId, - syncConfig, - }) - ); - }) - ) - ).pipe( - map( - (syncedAccount) => (initialAccount) => - recalculateAccountBalanceHistories( - postSyncPatch(initialAccount, { - ...initialAccount, - // FIXME, the "patching" logic should be somewhere else, especially that it's also in jsHelpers - id: syncedAccount.id, - freshAddress: syncedAccount.freshAddress, - freshAddressPath: syncedAccount.freshAddressPath, - balance: syncedAccount.balance, - balanceHistory: syncedAccount.balanceHistory, - spendableBalance: syncedAccount.spendableBalance, - blockHeight: syncedAccount.blockHeight, - lastSyncDate: new Date(), - creationDate: syncedAccount.creationDate, - operations: syncedAccount.operations, - operationsCount: syncedAccount.operations.length, - subAccounts: syncedAccount.subAccounts, - pendingOperations: initialAccount.pendingOperations.filter((op) => - shouldRetainPendingOperation(syncedAccount, op) - ), - cosmosResources: syncedAccount.cosmosResources, - algorandResources: syncedAccount.algorandResources, - bitcoinResources: syncedAccount.bitcoinResources, - }), - initialAccount - ) - ) - ); -} diff --git a/src/libcore/types/index.ts b/src/libcore/types/index.ts deleted file mode 100644 index 6629bcc262..0000000000 --- a/src/libcore/types/index.ts +++ /dev/null @@ -1,645 +0,0 @@ -// NB this is a tradeoff that for now, we maintain ourself the types interface AND the JS declaration of them. -// this allow to do wrapping on top of the different libcore interfaces -import { reflectSpecifics } from "../../generated/types"; -import type { - SpecificStatics, - CoreAccountSpecifics, - CoreOperationSpecifics, - CoreCurrencySpecifics, -} from "../../generated/types"; - -declare class CoreWalletPool { - newInstance( - name: string, - pwd: string, - httpClient: CoreHttpClient, - webSocket: CoreWebSocketClient, - pathResolver: CorePathResolver, - logPrinter: CoreLogPrinter, - threadDispatcher: CoreThreadDispatcher, - rng: CoreRandomNumberGenerator, - backend: CoreDatabaseBackend, - walletDynObject: CoreDynamicObject - ): Promise; - getWallet(name: string): Promise; - getCurrency(id: string): Promise; - updateWalletConfig( - walletName: string, - config: CoreDynamicObject - ): Promise; - createWallet( - walletName: string, - currency: CoreCurrency, - config: CoreDynamicObject - ): Promise; - freshResetAll(): Promise; - changePassword(oldPassword: string, newPassword: string): Promise; - getName(): Promise; -} - -declare class CoreWallet { - getAccountCreationInfo( - accountIndex: number - ): Promise; - getNextAccountCreationInfo(): Promise; - newAccountWithInfo(info: CoreAccountCreationInfo): Promise; - getCurrency(): Promise; - getAccount(index: number): Promise; - getExtendedKeyAccountCreationInfo( - index: number - ): Promise; - newAccountWithExtendedKeyInfo( - keys?: CoreExtendedKeyAccountCreationInfo - ): Promise; -} - -export const TimePeriod = { - HOUR: 0, - DAY: 1, - WEEK: 2, - MONTH: 3, -}; -declare type CoreAccount = { - getBalance(): Promise; - getBalanceHistory( - from: string, - to: string, - timePeriod: typeof TimePeriod[keyof typeof TimePeriod] - ): Promise; - getLastBlock(): Promise; - getFreshPublicAddresses(): Promise; - getRestoreKey(): Promise; - synchronize(): Promise; - queryOperations(): Promise; -} & CoreAccountSpecifics; -declare type CoreOperation = { - getDate(): Promise; - getOperationType(): Promise; - getAmount(): Promise; - getFees(): Promise; - getBlockHeight(): Promise; - getRecipients(): Promise; - getSelfRecipients(): Promise; - getSenders(): Promise; -} & CoreOperationSpecifics; -declare type CoreCurrency = Record & CoreCurrencySpecifics; - -declare class CoreLedgerCore { - getStringVersion(): Promise; - getIntVersion(): Promise; -} - -declare class CoreDatabaseBackend { - getSqlite3Backend(): Promise; - flush(): Promise; -} - -declare class CoreHttpClient { - newInstance(): Promise; - flush(): Promise; -} - -declare class CoreWebSocketClient { - newInstance(): Promise; - flush(): Promise; -} - -declare class CorePathResolver { - newInstance(): Promise; - flush(): Promise; -} - -declare class CoreLogPrinter { - newInstance(): Promise; - flush(): Promise; -} - -declare class CoreRandomNumberGenerator { - newInstance(): Promise; - flush(): Promise; -} - -declare class CoreBigInt { - fromIntegerString(s: string, radix: number): Promise; - toString(base: number): Promise; -} - -declare class CoreAmount { - fromHex(arg0: CoreCurrency, arg1: string): Promise; - toBigInt(): Promise; -} - -declare class CoreBlock { - getHeight(): Promise; -} - -declare class CoreDerivationPath { - toString(): Promise; - isNull(): Promise; -} - -export type OperationType = 0 | 1; - -declare class CoreAddress { - isValid(recipient: string, currency: CoreCurrency): Promise; - toString(): Promise; - getDerivationPath(): Promise; -} - -declare class CoreOperationQuery { - offset(arg0: number): Promise; - limit(arg0: number): Promise; - partial(): Promise; - complete(): Promise; - addOrder(arg0: number, arg1: boolean): Promise; - execute(): Promise; -} - -declare class CoreAccountCreationInfo { - init( - index: number, - owners: string[], - derivations: string[], - publicKeys: string[], - chainCodes: string[] - ): Promise; - getDerivations(): Promise; - getChainCodes(): Promise; - getPublicKeys(): Promise; - getOwners(): Promise; - getIndex(): Promise; -} - -declare class CoreExtendedKeyAccountCreationInfo { - init( - index: number, - owners: string[], - derivations: string[], - extendedKeys: string[] - ): Promise; - getIndex(): Promise; - getExtendedKeys(): Promise; - getOwners(): Promise; - getDerivations(): Promise; -} - -declare class CoreDynamicObject { - newInstance(): Promise; - flush(): Promise; - putBoolean(arg0: string, arg1: boolean): Promise; - putString(arg0: string, arg1: string): Promise; - putInt(arg0: string, arg1: number): Promise; -} - -declare class CoreSerialContext {} - -declare class CoreThreadDispatcher { - newInstance(): Promise; - getMainExecutionContext(): Promise; -} - -declare class CoreEventBus { - subscribe( - serialContext: CoreSerialContext, - eventReceiver: CoreEventReceiver - ): Promise; -} - -declare class CoreEventReceiver { - newInstance(): Promise; -} - -export type CoreStatics = { - Account: CoreAccount; - AccountCreationInfo: CoreAccountCreationInfo; - Address: CoreAddress; - Amount: CoreAmount; - BigInt: CoreBigInt; - Block: CoreBlock; - Currency: CoreCurrency; - DatabaseBackend: CoreDatabaseBackend; - DerivationPath: CoreDerivationPath; - DynamicObject: CoreDynamicObject; - EventBus: CoreEventBus; - EventReceiver: CoreEventReceiver; - ExtendedKeyAccountCreationInfo: CoreExtendedKeyAccountCreationInfo; - HttpClient: CoreHttpClient; - LedgerCore: CoreLedgerCore; - LogPrinter: CoreLogPrinter; - Operation: CoreOperation; - OperationQuery: CoreOperationQuery; - PathResolver: CorePathResolver; - RandomNumberGenerator: CoreRandomNumberGenerator; - SerialContext: CoreSerialContext; - ThreadDispatcher: CoreThreadDispatcher; - Wallet: CoreWallet; - WalletPool: CoreWalletPool; - WebSocketClient: CoreWebSocketClient; -} & SpecificStatics; -export type Core = CoreStatics & { - flush: () => Promise; - getPoolInstance: () => CoreWalletPool; - getThreadDispatcher: () => CoreThreadDispatcher; -}; -export type { - CoreAccount, - CoreAccountCreationInfo, - CoreAddress, - CoreAmount, - CoreBigInt, - CoreBlock, - CoreCurrency, - CoreDatabaseBackend, - CoreDerivationPath, - CoreDynamicObject, - CoreEventBus, - CoreEventReceiver, - CoreExtendedKeyAccountCreationInfo, - CoreHttpClient, - CoreLedgerCore, - CoreLogPrinter, - CoreOperation, - CoreOperationQuery, - CorePathResolver, - CoreRandomNumberGenerator, - CoreSerialContext, - CoreThreadDispatcher, - CoreWallet, - CoreWalletPool, - CoreWebSocketClient, -}; -type SpecMapF = { - params?: Array<(string | null | undefined) | string[]>; - returns?: string | string[]; - njsField?: string; - njsInstanciateClass?: Array>; - njsBuggyMethodIsNotStatic?: boolean | ((...args: Array) => any); - nodejsNotAvailable?: boolean; -}; -export type Spec = { - njsUsesPlainObject?: boolean; - statics?: Record; - methods?: Record; -}; -// To make the above contract possible with current libcore bindings, -// we need to define the code below and build-up abstraction wrappings on top of the lower level bindings. -// We do this at runtime but ideally in the future, it will be at build time (generated code). -export const reflect = (declare: (arg0: string, arg1: Spec) => void): void => { - const { AccountMethods, OperationMethods } = reflectSpecifics(declare).reduce( - (all, extra) => ({ - AccountMethods: { - ...all.AccountMethods, - ...(extra && extra.AccountMethods), - }, - OperationMethods: { - ...all.OperationMethods, - ...(extra && extra.OperationMethods), - }, - }), - { AccountMethods: {}, OperationMethods: {} } - ); - declare("WalletPool", { - statics: { - newInstance: { - params: [ - null, - null, - "HttpClient", - "WebSocketClient", - "PathResolver", - "LogPrinter", - "ThreadDispatcher", - "RandomNumberGenerator", - "DatabaseBackend", - "DynamicObject", - ], - returns: "WalletPool", - }, - }, - methods: { - freshResetAll: {}, - changePassword: {}, - getName: {}, - updateWalletConfig: { - params: [null, "DynamicObject"], - }, - getWallet: { - returns: "Wallet", - }, - getCurrency: { - returns: "Currency", - }, - createWallet: { - params: [null, "Currency", "DynamicObject"], - returns: "Wallet", - }, - }, - }); - declare("Wallet", { - methods: { - getAccountCreationInfo: { - returns: "AccountCreationInfo", - }, - getNextAccountCreationInfo: { - returns: "AccountCreationInfo", - }, - newAccountWithInfo: { - params: ["AccountCreationInfo"], - returns: "Account", - }, - getCurrency: { - returns: "Currency", - }, - getAccount: { - returns: "Account", - }, - getExtendedKeyAccountCreationInfo: { - returns: "ExtendedKeyAccountCreationInfo", - }, - newAccountWithExtendedKeyInfo: { - params: ["ExtendedKeyAccountCreationInfo"], - returns: "Account", - }, - }, - }); - declare("Account", { - methods: { - ...AccountMethods, - getBalance: { - returns: "Amount", - }, - getBalanceHistory: { - returns: ["Amount"], - }, - getLastBlock: { - returns: "Block", - }, - getFreshPublicAddresses: { - returns: ["Address"], - }, - getRestoreKey: {}, - synchronize: { - returns: "EventBus", - }, - queryOperations: { - returns: "OperationQuery", - }, - }, - }); - declare("Operation", { - methods: { - ...OperationMethods, - getDate: {}, - getOperationType: {}, - getAmount: { - returns: "Amount", - }, - getFees: { - returns: "Amount", - }, - getBlockHeight: {}, - getRecipients: {}, - getSelfRecipients: {}, - getSenders: {}, - }, - }); - declare("Currency", { - njsUsesPlainObject: true, - methods: { - getBitcoinLikeNetworkParameters: { - returns: "BitcoinLikeNetworkParameters", - njsField: "bitcoinLikeNetworkParameters", - }, - }, - }); - declare("BigInt", { - statics: { - fromIntegerString: { - njsBuggyMethodIsNotStatic: () => ["", 0, "."], - returns: "BigInt", - }, - }, - methods: { - toString: {}, - }, - }); - declare("Amount", { - statics: { - fromHex: { - params: ["Currency"], - returns: "Amount", - njsBuggyMethodIsNotStatic: true, - }, - }, - methods: { - toBigInt: { - returns: "BigInt", - }, - }, - }); - declare("Block", { - njsUsesPlainObject: true, - methods: { - getHeight: { - njsField: "height", - }, - }, - }); - declare("DerivationPath", { - methods: { - toString: {}, - isNull: {}, - }, - }); - declare("Address", { - statics: { - isValid: { - params: [null, "Currency"], - njsBuggyMethodIsNotStatic: true, - }, - }, - methods: { - toString: {}, - getDerivationPath: {}, - }, - }); - declare("OperationQuery", { - methods: { - limit: {}, - offset: {}, - partial: {}, - complete: {}, - addOrder: {}, - execute: { - returns: ["Operation"], - }, - }, - }); - declare("AccountCreationInfo", { - njsUsesPlainObject: true, - statics: { - init: { - params: [null, null, null, ["hex"], ["hex"]], - returns: "AccountCreationInfo", - njsInstanciateClass: [ - { - index: 0, - owners: 1, - derivations: 2, - publicKeys: 3, - chainCodes: 4, - }, - ], - }, - }, - methods: { - getDerivations: { - njsField: "derivations", - }, - getChainCodes: { - njsField: "chainCodes", - returns: ["hex"], - }, - getPublicKeys: { - njsField: "publicKeys", - returns: ["hex"], - }, - getOwners: { - njsField: "owners", - }, - getIndex: { - njsField: "index", - }, - }, - }); - declare("ExtendedKeyAccountCreationInfo", { - njsUsesPlainObject: true, - statics: { - init: { - returns: "ExtendedKeyAccountCreationInfo", - njsInstanciateClass: [ - { - index: 0, - owners: 1, - derivations: 2, - extendedKeys: 3, - }, - ], - }, - }, - methods: { - getIndex: { - njsField: "index", - }, - getExtendedKeys: { - njsField: "extendedKeys", - }, - getOwners: { - njsField: "owners", - }, - getDerivations: { - njsField: "derivations", - }, - }, - }); - declare("DynamicObject", { - statics: { - flush: {}, - newInstance: { - returns: "DynamicObject", - njsInstanciateClass: [], - }, - }, - methods: { - putBoolean: {}, - putString: {}, - putInt: {}, - }, - }); - declare("SerialContext", {}); - declare("ThreadDispatcher", { - statics: { - newInstance: { - returns: "ThreadDispatcher", - }, - }, - methods: { - getMainExecutionContext: { - nodejsNotAvailable: true, - returns: "SerialContext", - }, - }, - }); - declare("EventBus", { - methods: { - subscribe: { - params: ["SerialContext", "EventReceiver"], - }, - }, - }); - declare("EventReceiver", { - statics: { - newInstance: { - returns: "EventReceiver", - }, - }, - }); - declare("HttpClient", { - statics: { - newInstance: { - returns: "HttpClient", - }, - flush: {}, - }, - }); - declare("WebSocketClient", { - statics: { - newInstance: { - returns: "WebSocketClient", - }, - flush: {}, - }, - }); - declare("PathResolver", { - statics: { - newInstance: { - returns: "PathResolver", - }, - flush: {}, - }, - }); - declare("LogPrinter", { - statics: { - newInstance: { - returns: "LogPrinter", - }, - flush: {}, - }, - }); - declare("RandomNumberGenerator", { - statics: { - newInstance: { - returns: "RandomNumberGenerator", - }, - flush: {}, - }, - }); - declare("DatabaseBackend", { - statics: { - flush: {}, - getSqlite3Backend: { - returns: "DatabaseBackend", - }, - }, - }); - declare("LedgerCore", { - statics: { - getStringVersion: { - njsBuggyMethodIsNotStatic: true, - }, - getIntVersion: { - njsBuggyMethodIsNotStatic: true, - }, - }, - }); -}; diff --git a/src/reconciliation.ts b/src/reconciliation.ts index d7895114a1..9a5de9f2aa 100644 --- a/src/reconciliation.ts +++ b/src/reconciliation.ts @@ -1,5 +1,4 @@ -// libcore reconciliation by the React definition. https://reactjs.org/docs/reconciliation.html -// TODO move to account/ +// reconciliation by the React definition. https://reactjs.org/docs/reconciliation.html import isEqual from "lodash/isEqual"; import { BigNumber } from "bignumber.js"; import { sameOp } from "./bridge/jsHelpers"; @@ -31,7 +30,7 @@ import { } from "./account"; import consoleWarnExpectToEqual from "./consoleWarnExpectToEqual"; -// aim to build operations with the minimal diff & call to libcore possible +// aim to build operations with the minimal diff & call to coin implementation possible export async function minimalOperationsBuilder( existingOperations: Operation[], coreOperations: CO[], @@ -558,12 +557,12 @@ function stepBuilder(state, newOp, i) { const rest = state.existingOps.slice(j); if (rest.length > i + 1) { - // if libcore happen to have less ops that what we had, + // if coin implementation happen to have less ops that what we had, // we actually need to continue because we don't know where hole will be, // but we can keep existingOp state.operations.push(existingOp); } else { - // otherwise we stop the libcore iteration and continue with previous data + // otherwise we stop the coin implementation iteration and continue with previous data // and we're done on the iteration if (state.operations.length === 0 && j === 0) { // special case: we preserve the operations array as much as possible