diff --git a/apps/ledger-live-mobile/package.json b/apps/ledger-live-mobile/package.json index 50a6a342e0a5..10fc7bb4af57 100644 --- a/apps/ledger-live-mobile/package.json +++ b/apps/ledger-live-mobile/package.json @@ -76,6 +76,7 @@ "@formatjs/intl-locale": "^3.0.0", "@formatjs/intl-pluralrules": "^5.0.0", "@formatjs/intl-relativetimeformat": "^11.1.8", + "@ledgerhq/coin-elrond": "workspace:^", "@ledgerhq/coin-evm": "workspace:^", "@ledgerhq/coin-framework": "workspace:^", "@ledgerhq/devices": "workspace:*", diff --git a/apps/ledger-live-mobile/src/families/elrond/accountActions.tsx b/apps/ledger-live-mobile/src/families/elrond/accountActions.tsx index ed979cdf78e7..0d47a823a134 100644 --- a/apps/ledger-live-mobile/src/families/elrond/accountActions.tsx +++ b/apps/ledger-live-mobile/src/families/elrond/accountActions.tsx @@ -1,15 +1,18 @@ -import React from "react"; +import { + hasMinimumDelegableBalance, + randomizeProviders, +} from "@ledgerhq/live-common/families/elrond/helpers"; import type { ElrondAccount } from "@ledgerhq/live-common/families/elrond/types"; -import { hasMinimumDelegableBalance } from "@ledgerhq/live-common/families/elrond/helpers"; import { IconsLegacy } from "@ledgerhq/native-ui"; +import React from "react"; import { Trans } from "react-i18next"; import type { Account } from "@ledgerhq/types-live"; import type { ActionButtonEvent, NavigationParamsType } from "~/components/FabActions"; -import { NavigatorName, ScreenName } from "~/const"; +import { getCurrentElrondPreloadData } from "@ledgerhq/coin-elrond/preload"; import { ParamListBase, RouteProp } from "@react-navigation/native"; -import { useElrondRandomizedValidators } from "@ledgerhq/live-common/families/elrond/react"; +import { NavigatorName, ScreenName } from "~/const"; /* * Declare the types for the properties and return payload. @@ -36,7 +39,9 @@ const getMainActions = ({ /* * Get a list of all the providers, randomize, and also the screen, conditionally, based on existing amount of delegations. */ - const validators = useElrondRandomizedValidators(); + const preloaded = getCurrentElrondPreloadData(); + const validators = randomizeProviders(preloaded.validators); + const isFirstTimeFlow = account.elrondResources && account.elrondResources.delegations.length === 0; const screen = isFirstTimeFlow diff --git a/apps/ledger-live-mobile/src/families/elrond/components/Flows/Delegate/components/PickAmount/PickAmount.tsx b/apps/ledger-live-mobile/src/families/elrond/components/Flows/Delegate/components/PickAmount/PickAmount.tsx index fab37b56d4e2..7f4a509176a9 100644 --- a/apps/ledger-live-mobile/src/families/elrond/components/Flows/Delegate/components/PickAmount/PickAmount.tsx +++ b/apps/ledger-live-mobile/src/families/elrond/components/Flows/Delegate/components/PickAmount/PickAmount.tsx @@ -58,7 +58,7 @@ const PickAmount = (props: PickAmountPropsType) => { }; fetchMaxSpendable(); - }, [transaction, account]); + }, [bridge, account, transaction]); /* * Handle the ration selection callback. diff --git a/libs/coin-modules/coin-elrond/.unimportedrc.json b/libs/coin-modules/coin-elrond/.unimportedrc.json index b08a32980d2d..05c16333d82d 100644 --- a/libs/coin-modules/coin-elrond/.unimportedrc.json +++ b/libs/coin-modules/coin-elrond/.unimportedrc.json @@ -1,4 +1,17 @@ { - "entry": [], - "ignoreUnimported": [] + "entry": ["src/index.ts", "src/bridge/js.ts", "src/network/index.ts", "src/signer.ts"], + "ignoreUnimported": [ + "src/cli-transaction.ts", + "src/datasets/elrond1.ts", + "src/deviceTransactionConfig.ts", + "src/formatters.ts", + "src/helpers/denominate.ts", + "src/helpers/handleTransactionStatus.ts", + "src/helpers/hasMinimumDelegableBalance.ts", + "src/helpers/randomizeProviders.ts", + "src/specs.ts", + "src/speculos-deviceActions.ts", + "src/transaction.ts" + ], + "ignoreUnused": ["@ledgerhq/devices", "@ledgerhq/live-promise", "lodash", "expect", "invariant"] } diff --git a/libs/coin-modules/coin-elrond/jest.config.js b/libs/coin-modules/coin-elrond/jest.config.js index 6afec776eac2..e35fe8eda7f8 100644 --- a/libs/coin-modules/coin-elrond/jest.config.js +++ b/libs/coin-modules/coin-elrond/jest.config.js @@ -4,5 +4,5 @@ module.exports = { coverageDirectory: "coverage", collectCoverageFrom: ["src/**/*.ts"], testEnvironment: "node", - testPathIgnorePatterns: ["lib/", "lib-es/", ".*\\.integ\\.test\\.[tj]s"], + testPathIgnorePatterns: ["lib/", "lib-es/", ".integration.test.ts"], }; diff --git a/libs/coin-modules/coin-elrond/src/bridge/js.ts b/libs/coin-modules/coin-elrond/src/bridge/js.ts index 62133cc72a1d..cff348d88c4c 100644 --- a/libs/coin-modules/coin-elrond/src/bridge/js.ts +++ b/libs/coin-modules/coin-elrond/src/bridge/js.ts @@ -1,19 +1,18 @@ -import type { AccountBridge, CurrencyBridge } from "@ledgerhq/types-live"; +import getAddressWrapper from "@ledgerhq/coin-framework/bridge/getAddressWrapper"; import { defaultUpdateTransaction, makeAccountBridgeReceive, makeScanAccounts, } from "@ledgerhq/coin-framework/bridge/jsHelpers"; -import type { ElrondAccount, Transaction, TransactionStatus } from "../types"; -import { getPreloadStrategy, preload, hydrate } from "../preload"; -import { getTransactionStatus } from "../getTransactionStatus"; +import { SignerContext } from "@ledgerhq/coin-framework/lib/signer"; +import type { AccountBridge, CurrencyBridge } from "@ledgerhq/types-live"; +import { broadcast } from "../broadcast"; +import { createTransaction } from "../createTransaction"; import { estimateMaxSpendable } from "../estimateMaxSpendable"; +import { getTransactionStatus } from "../getTransactionStatus"; +import resolver from "../hw-getAddress"; +import { getPreloadStrategy, hydrate, preload } from "../preload"; import { prepareTransaction } from "../prepareTransaction"; -import { sync } from "../synchronisation"; -import { createTransaction } from "../createTransaction"; -import { buildSignOperation } from "../signOperation"; -import { broadcast } from "../broadcast"; -import { getAccountShape } from "../synchronisation"; import { assignFromAccountRaw, assignToAccountRaw, @@ -21,9 +20,9 @@ import { toOperationExtraRaw, } from "../serialization"; import { ElrondSigner } from "../signer"; -import { SignerContext } from "@ledgerhq/coin-framework/lib/signer"; -import resolver from "../hw-getAddress"; -import getAddressWrapper from "@ledgerhq/coin-framework/bridge/getAddressWrapper"; +import { buildSignOperation } from "../signOperation"; +import { getAccountShape, sync } from "../synchronisation"; +import type { ElrondAccount, Transaction, TransactionStatus } from "../types"; export function buildCurrencyBridge(signerContext: SignerContext): CurrencyBridge { const getAddress = resolver(signerContext); diff --git a/libs/ledger-live-common/.unimportedrc.json b/libs/ledger-live-common/.unimportedrc.json index 14bf370ae0f3..47cae36d46c4 100644 --- a/libs/ledger-live-common/.unimportedrc.json +++ b/libs/ledger-live-common/.unimportedrc.json @@ -98,12 +98,8 @@ "src/families/crypto_org/types.ts", "src/families/elrond/banner.ts", "src/families/elrond/constants.ts", - "src/families/elrond/helpers/denominate.ts", - "src/families/elrond/helpers/handleTransactionStatus.ts", - "src/families/elrond/helpers/hasMinimumDelegableBalance.ts", - "src/families/elrond/helpers/randomizeProviders.ts", - "src/families/elrond/js-estimateMaxSpendable.ts", - "src/families/elrond/preload.ts", + "src/families/elrond/helpers.ts", + "src/families/elrond/errors.ts", "src/families/elrond/react.ts", "src/families/elrond/types.ts", "src/families/evm/banner.ts", diff --git a/libs/ledger-live-common/package.json b/libs/ledger-live-common/package.json index 48474b8895d7..70ce517ca496 100644 --- a/libs/ledger-live-common/package.json +++ b/libs/ledger-live-common/package.json @@ -138,6 +138,7 @@ "@ledgerhq/coin-algorand": "workspace:^", "@ledgerhq/coin-bitcoin": "workspace:^", "@ledgerhq/coin-cardano": "workspace:^", + "@ledgerhq/coin-elrond": "workspace:^", "@ledgerhq/coin-evm": "workspace:^", "@ledgerhq/coin-framework": "workspace:^", "@ledgerhq/coin-near": "workspace:^", @@ -149,7 +150,6 @@ "@ledgerhq/coin-tezos": "workspace:^", "@ledgerhq/coin-tron": "workspace:^", "@ledgerhq/coin-xrp": "workspace:^", - "@ledgerhq/coin-elrond": "workspace:^", "@ledgerhq/crypto-icons-ui": "workspace:^", "@ledgerhq/cryptoassets": "workspace:^", "@ledgerhq/device-core": "workspace:^", diff --git a/libs/ledgerjs/packages/hw-app-elrond/README.md b/libs/ledgerjs/packages/hw-app-elrond/README.md new file mode 100644 index 000000000000..922bab4f6665 --- /dev/null +++ b/libs/ledgerjs/packages/hw-app-elrond/README.md @@ -0,0 +1,105 @@ + + +[GitHub](https://github.com/LedgerHQ/ledger-live/), +[Ledger Devs Discord](https://developers.ledger.com/discord-pro), +[Developer Portal](https://developers.ledger.com/) + +## @ledgerhq/hw-app-elrond + +Ledger Hardware Wallet Elrond JavaScript bindings. + +*** + +## Are you adding Ledger support to your software wallet? + +You may be using this package to communicate with the Elrond Nano App. + +For a smooth and quick integration: + +* See the developers’ documentation on the [Developer Portal](https://developers.ledger.com/docs/transport/overview/) and +* Go on [Discord](https://developers.ledger.com/discord-pro/) to chat with developer support and the developer community. + +*** + +## API + + + +#### Table of Contents + +* [Elrond](#elrond) + * [Parameters](#parameters) + * [Examples](#examples) + * [getAppConfiguration](#getappconfiguration) + * [Examples](#examples-1) + * [getAddress](#getaddress) + * [Parameters](#parameters-1) + * [Examples](#examples-2) + * [setAddress](#setaddress) + * [Parameters](#parameters-2) + * [Examples](#examples-3) + +### Elrond + +Elrond API + +#### Parameters + +* `transport` **Transport** +* `scrambleKey` (optional, default `"eGLD"`) + +#### Examples + +```javascript +import Elrond from "@ledgerhq/hw-app-elrond"; +const elrond = new Elrond(transport) +``` + +#### getAppConfiguration + +Get Elrond app configuration. + +##### Examples + +```javascript +const result = await elrond.getAppConfiguration(); +const { contractData, accountIndex, addressIndex, version } = result; +``` + +Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\** an object with a contractData, accountIndex, addressIndex, version + +#### getAddress + +Get Elrond address for a given BIP 32 path. + +##### Parameters + +* `path` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** a path in BIP 32 format +* `boolDisplay` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** optionally enable or not the display + +##### Examples + +```javascript +const result = await elrond.getAddress("44'/508'/0'/0'/0'"); +const { publicKey, address } = result; +``` + +Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<{publicKey: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), address: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)}>** an object with a address + +#### setAddress + +Set Elrond address for a given BIP 32 path. + +##### Parameters + +* `path` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** a path in BIP 32 format +* `display` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** optionally enable or not the display + +##### Examples + +```javascript +const result = await elrond.setAddress("44'/508'/0'/0/0"); +result : Buffer; +``` + +Returns **any** an object with a address diff --git a/libs/ledgerjs/packages/hw-app-elrond/src/Elrond.ts b/libs/ledgerjs/packages/hw-app-elrond/src/Elrond.ts index 9d7b0324ce10..85a2b03b5a87 100644 --- a/libs/ledgerjs/packages/hw-app-elrond/src/Elrond.ts +++ b/libs/ledgerjs/packages/hw-app-elrond/src/Elrond.ts @@ -1,6 +1,6 @@ import type Transport from "@ledgerhq/hw-transport"; -import BIPPath from "bip32-path"; import { Address } from "@multiversx/sdk-core"; +import BIPPath from "bip32-path"; const CHUNK_SIZE = 150; const CURVE_MASK = 0x80; @@ -15,6 +15,13 @@ const SIGN_HASH_TX_INS = 0x07; const SW_OK = 0x9000; const SW_CANCEL = 0x6986; +/** + * Elrond API + * + * @example + * import Elrond from "@ledgerhq/hw-app-elrond"; + * const elrond = new Elrond(transport) + */ export default class Elrond { transport: Transport; diff --git a/package.json b/package.json index f75332708469..fa6f58fa587d 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "coin:algorand": "pnpm --filter coin-algorand", "coin:bitcoin": "pnpm --filter coin-bitcoin", "coin:cardano": "pnpm --filter coin-cardano", + "coin:elrond": "pnpm --filter coin-elrond", "coin:evm": "pnpm --filter coin-evm", "coin:framework": "pnpm --filter coin-framework", "coin:tester": "pnpm --filter coin-tester", @@ -76,7 +77,6 @@ "coin:tezos": "pnpm --filter coin-tezos", "coin:tron": "pnpm --filter coin-tron", "coin:xrp": "pnpm --filter coin-xrp", - "coin:elrond": "pnpm --filter coin-elrond", "evm-tools": "pnpm --filter evm-tools", "domain": "pnpm --filter domain-service", "doc": "pnpm --filter docs", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9d123dc7a7bd..4cd446cf18cd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -799,6 +799,9 @@ importers: '@formatjs/intl-relativetimeformat': specifier: ^11.1.8 version: 11.2.12 + '@ledgerhq/coin-elrond': + specifier: workspace:^ + version: link:../../libs/coin-modules/coin-elrond '@ledgerhq/coin-evm': specifier: workspace:^ version: link:../../libs/coin-modules/coin-evm @@ -46585,7 +46588,7 @@ snapshots: postcss-modules-scope: 3.1.1(postcss@8.4.38) postcss-modules-values: 4.0.0(postcss@8.4.38) postcss-value-parser: 4.2.0 - semver: 7.6.2 + semver: 7.6.3 optionalDependencies: webpack: 5.94.0(@swc/core@1.4.11)(esbuild@0.19.12) @@ -46598,7 +46601,7 @@ snapshots: postcss-modules-scope: 3.1.1(postcss@8.4.38) postcss-modules-values: 4.0.0(postcss@8.4.38) postcss-value-parser: 4.2.0 - semver: 7.6.2 + semver: 7.6.3 optionalDependencies: webpack: 5.94.0(@swc/core@1.4.11)(metro@0.80.8) @@ -46611,7 +46614,7 @@ snapshots: postcss-modules-scope: 3.1.1(postcss@8.4.38) postcss-modules-values: 4.0.0(postcss@8.4.38) postcss-value-parser: 4.2.0 - semver: 7.6.2 + semver: 7.6.3 optionalDependencies: webpack: 5.94.0