@ledgerhq/live-common 25.0.0-next.2 → 25.0.0-next.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/lib/__tests__/accounts/importAccounts.js +1 -1
  2. package/lib/__tests__/test-helpers/setup.js +1 -1
  3. package/lib/account/helpers.d.ts +5 -1
  4. package/lib/account/helpers.d.ts.map +1 -1
  5. package/lib/account/helpers.js +28 -1
  6. package/lib/account/helpers.js.map +1 -1
  7. package/lib/apps/filtering.test.js +1 -1
  8. package/lib/apps/support.d.ts.map +1 -1
  9. package/lib/apps/support.js +2 -1
  10. package/lib/apps/support.js.map +1 -1
  11. package/lib/currencies/helpers.d.ts +9 -0
  12. package/lib/currencies/helpers.d.ts.map +1 -0
  13. package/lib/currencies/helpers.js +78 -0
  14. package/lib/currencies/helpers.js.map +1 -0
  15. package/lib/currencies/helpers.test.d.ts +2 -0
  16. package/lib/currencies/helpers.test.d.ts.map +1 -0
  17. package/lib/currencies/helpers.test.js +20 -0
  18. package/lib/currencies/helpers.test.js.map +1 -0
  19. package/lib/currencies/index.d.ts +1 -0
  20. package/lib/currencies/index.d.ts.map +1 -1
  21. package/lib/currencies/index.js +1 -0
  22. package/lib/currencies/index.js.map +1 -1
  23. package/lib/currencies/react.d.ts +4 -0
  24. package/lib/currencies/react.d.ts.map +1 -0
  25. package/lib/currencies/react.js +16 -0
  26. package/lib/currencies/react.js.map +1 -0
  27. package/lib/families/cardano/bridge.integration.test.d.ts.map +1 -1
  28. package/lib/families/cardano/bridge.integration.test.js +36 -2
  29. package/lib/families/cardano/bridge.integration.test.js.map +1 -1
  30. package/lib/families/cardano/buildSubAccounts.d.ts.map +1 -1
  31. package/lib/families/cardano/buildSubAccounts.js +5 -3
  32. package/lib/families/cardano/buildSubAccounts.js.map +1 -1
  33. package/lib/families/cardano/deviceTransactionConfig.d.ts +11 -0
  34. package/lib/families/cardano/deviceTransactionConfig.d.ts.map +1 -0
  35. package/lib/families/cardano/deviceTransactionConfig.js +91 -0
  36. package/lib/families/cardano/deviceTransactionConfig.js.map +1 -0
  37. package/lib/families/cardano/errors.d.ts +4 -0
  38. package/lib/families/cardano/errors.d.ts.map +1 -1
  39. package/lib/families/cardano/errors.js +5 -1
  40. package/lib/families/cardano/errors.js.map +1 -1
  41. package/lib/families/cardano/js-estimateMaxSpendable.d.ts.map +1 -1
  42. package/lib/families/cardano/js-estimateMaxSpendable.js +3 -2
  43. package/lib/families/cardano/js-estimateMaxSpendable.js.map +1 -1
  44. package/lib/families/cardano/js-getTransactionStatus.d.ts.map +1 -1
  45. package/lib/families/cardano/js-getTransactionStatus.js +6 -5
  46. package/lib/families/cardano/js-getTransactionStatus.js.map +1 -1
  47. package/lib/families/cardano/js-signOperation.d.ts.map +1 -1
  48. package/lib/families/cardano/js-signOperation.js +10 -5
  49. package/lib/families/cardano/js-signOperation.js.map +1 -1
  50. package/lib/families/cardano/js-synchronisation.d.ts.map +1 -1
  51. package/lib/families/cardano/js-synchronisation.js +126 -98
  52. package/lib/families/cardano/js-synchronisation.js.map +1 -1
  53. package/lib/families/cardano/js-transaction.js +1 -1
  54. package/lib/families/cardano/js-transaction.js.map +1 -1
  55. package/lib/families/cardano/logic.d.ts +1 -0
  56. package/lib/families/cardano/logic.d.ts.map +1 -1
  57. package/lib/families/cardano/logic.js +11 -1
  58. package/lib/families/cardano/logic.js.map +1 -1
  59. package/lib/families/cardano/specs.d.ts.map +1 -1
  60. package/lib/families/cardano/specs.js +39 -1
  61. package/lib/families/cardano/specs.js.map +1 -1
  62. package/lib/families/cardano/speculos-deviceActions.d.ts.map +1 -1
  63. package/lib/families/cardano/speculos-deviceActions.js +15 -0
  64. package/lib/families/cardano/speculos-deviceActions.js.map +1 -1
  65. package/lib/families/hedera/api/mirror.js +1 -1
  66. package/lib/families/hedera/api/mirror.js.map +1 -1
  67. package/lib/families/hedera/api/network.d.ts.map +1 -1
  68. package/lib/families/hedera/api/network.js +3 -1
  69. package/lib/families/hedera/api/network.js.map +1 -1
  70. package/lib/families/hedera/deviceTransactionConfig.d.ts.map +1 -1
  71. package/lib/families/hedera/deviceTransactionConfig.js +7 -0
  72. package/lib/families/hedera/deviceTransactionConfig.js.map +1 -1
  73. package/lib/families/hedera/js-synchronisation.d.ts.map +1 -1
  74. package/lib/families/hedera/js-synchronisation.js +10 -7
  75. package/lib/families/hedera/js-synchronisation.js.map +1 -1
  76. package/lib/families/hedera/specs.d.ts.map +1 -1
  77. package/lib/families/hedera/specs.js +23 -0
  78. package/lib/families/hedera/specs.js.map +1 -1
  79. package/lib/families/hedera/speculos-deviceActions.d.ts.map +1 -1
  80. package/lib/families/hedera/speculos-deviceActions.js +5 -1
  81. package/lib/families/hedera/speculos-deviceActions.js.map +1 -1
  82. package/lib/families/hedera/transaction.d.ts.map +1 -1
  83. package/lib/families/hedera/transaction.js +2 -2
  84. package/lib/families/hedera/transaction.js.map +1 -1
  85. package/lib/families/hedera/types.d.ts +2 -5
  86. package/lib/families/hedera/types.d.ts.map +1 -1
  87. package/lib/families/hedera/types.js +0 -4
  88. package/lib/families/hedera/types.js.map +1 -1
  89. package/lib/families/hedera/utils.d.ts.map +1 -1
  90. package/lib/featureFlags/types.d.ts +1 -1
  91. package/lib/featureFlags/types.d.ts.map +1 -1
  92. package/lib/generated/deviceTransactionConfig.d.ts +2 -0
  93. package/lib/generated/deviceTransactionConfig.d.ts.map +1 -1
  94. package/lib/generated/deviceTransactionConfig.js +28 -26
  95. package/lib/generated/deviceTransactionConfig.js.map +1 -1
  96. package/lib/platform/PlatformAppProvider/api/api.mock.js +11 -11
  97. package/lib/platform/PlatformAppProvider/api/api.mock.js.map +1 -1
  98. package/lib/platform/PlatformAppProvider/helpers.d.ts +0 -7
  99. package/lib/platform/PlatformAppProvider/helpers.d.ts.map +1 -1
  100. package/lib/platform/PlatformAppProvider/helpers.js +1 -29
  101. package/lib/platform/PlatformAppProvider/helpers.js.map +1 -1
  102. package/lib/platform/converters.d.ts +4 -4
  103. package/lib/platform/converters.d.ts.map +1 -1
  104. package/lib/platform/converters.js +40 -2
  105. package/lib/platform/converters.js.map +1 -1
  106. package/lib/platform/filters.d.ts +18 -0
  107. package/lib/platform/filters.d.ts.map +1 -0
  108. package/lib/platform/filters.js +63 -0
  109. package/lib/platform/filters.js.map +1 -0
  110. package/lib/platform/helpers.d.ts +7 -0
  111. package/lib/platform/helpers.d.ts.map +1 -0
  112. package/lib/platform/helpers.js +22 -0
  113. package/lib/platform/helpers.js.map +1 -0
  114. package/lib/platform/logic.d.ts +1 -5
  115. package/lib/platform/logic.d.ts.map +1 -1
  116. package/lib/platform/logic.js +1 -18
  117. package/lib/platform/logic.js.map +1 -1
  118. package/lib/platform/providers/RemoteLiveAppProvider/api/mock.json +87 -246
  119. package/lib/platform/react.d.ts +11 -0
  120. package/lib/platform/react.d.ts.map +1 -0
  121. package/lib/platform/react.js +61 -0
  122. package/lib/platform/react.js.map +1 -0
  123. package/lib/platform/types.d.ts +8 -2
  124. package/lib/platform/types.d.ts.map +1 -1
  125. package/lib/platform/types.js +15 -0
  126. package/lib/platform/types.js.map +1 -1
  127. package/lib/platform/version.test.js +1 -1
  128. package/lib/walletconnect/walletconnect.test.js +0 -2
  129. package/lib/walletconnect/walletconnect.test.js.map +1 -1
  130. package/package.json +5 -4
  131. package/src/__tests__/accounts/importAccounts.ts +1 -1
  132. package/src/__tests__/test-helpers/setup.ts +1 -1
  133. package/src/account/helpers.ts +51 -0
  134. package/src/apps/filtering.test.ts +1 -1
  135. package/src/apps/support.ts +1 -0
  136. package/src/currencies/__snapshots__/sortByMarketcap.test.ts.snap +101 -0
  137. package/src/currencies/helpers.test.ts +21 -0
  138. package/src/currencies/helpers.ts +69 -0
  139. package/src/currencies/index.ts +1 -0
  140. package/src/currencies/react.ts +16 -0
  141. package/src/families/cardano/__snapshots__/bridge.integration.test.ts.snap +41 -1
  142. package/src/families/cardano/bridge.integration.test.ts +38 -0
  143. package/src/families/cardano/buildSubAccounts.ts +5 -3
  144. package/src/families/cardano/deviceTransactionConfig.ts +109 -0
  145. package/src/families/cardano/errors.ts +7 -0
  146. package/src/families/cardano/js-estimateMaxSpendable.ts +4 -1
  147. package/src/families/cardano/js-getTransactionStatus.ts +7 -7
  148. package/src/families/cardano/js-signOperation.ts +10 -5
  149. package/src/families/cardano/js-synchronisation.ts +52 -21
  150. package/src/families/cardano/js-transaction.ts +1 -1
  151. package/src/families/cardano/logic.ts +10 -0
  152. package/src/families/cardano/specs.ts +53 -1
  153. package/src/families/cardano/speculos-deviceActions.ts +15 -0
  154. package/src/families/hedera/api/mirror.ts +1 -1
  155. package/src/families/hedera/api/network.ts +1 -0
  156. package/src/families/hedera/deviceTransactionConfig.ts +8 -0
  157. package/src/families/hedera/js-synchronisation.ts +5 -3
  158. package/src/families/hedera/specs.ts +56 -5
  159. package/src/families/hedera/speculos-deviceActions.ts +1 -0
  160. package/src/families/hedera/transaction.ts +2 -0
  161. package/src/families/hedera/types.ts +2 -8
  162. package/src/families/hedera/utils.ts +1 -1
  163. package/src/featureFlags/types.ts +1 -0
  164. package/src/generated/deviceTransactionConfig.ts +3 -0
  165. package/src/platform/PlatformAppProvider/api/api.mock.ts +11 -11
  166. package/src/platform/PlatformAppProvider/helpers.ts +0 -48
  167. package/src/platform/converters.ts +60 -6
  168. package/src/platform/filters.ts +102 -0
  169. package/src/platform/helpers.ts +33 -0
  170. package/src/platform/logic.ts +1 -20
  171. package/src/platform/providers/RemoteLiveAppProvider/api/mock.json +87 -246
  172. package/src/platform/react.ts +93 -0
  173. package/src/platform/types.ts +23 -1
  174. package/src/platform/version.test.ts +1 -1
  175. package/src/walletconnect/walletconnect.test.ts +0 -3
@@ -0,0 +1,16 @@
1
+ import { useMemo } from "react";
2
+ import { listAndFilterCurrencies } from "./helpers";
3
+ import { Currency } from "../types";
4
+ import { CurrencyFilters } from "../platform/filters";
5
+
6
+ export function useFilteredCurrencies({
7
+ includeTokens,
8
+ currencies,
9
+ }: CurrencyFilters): Currency[] {
10
+ return useMemo(() => {
11
+ return listAndFilterCurrencies({
12
+ includeTokens,
13
+ currencies,
14
+ });
15
+ }, [currencies, includeTokens]);
16
+ }
@@ -820,7 +820,7 @@ Array [
820
820
  "operationsCount": 4,
821
821
  "pendingOperations": Array [],
822
822
  "seedIdentifier": "21a6a622d47969ef0b2b89ef7c2e8428f955bae3e52f211e651ccffd",
823
- "spendableBalance": "1021211139",
823
+ "spendableBalance": "1019831859",
824
824
  "starred": false,
825
825
  "subAccounts": Array [],
826
826
  "swapHistory": Array [],
@@ -829,6 +829,20 @@ Array [
829
829
  "used": true,
830
830
  "xpub": "a902bc61a79256e1d5858d2ba49cab7011a26ad49580daace7412e255e818b1c1eba2defa321ff2937deba397f639f5bbabb0f0064516301fa4d08663c83f5b2",
831
831
  },
832
+ Object {
833
+ "approvals": undefined,
834
+ "balance": "5000000000",
835
+ "compoundBalance": undefined,
836
+ "id": "js:2:cardano_testnet:a902bc61a79256e1d5858d2ba49cab7011a26ad49580daace7412e255e818b1c1eba2defa321ff2937deba397f639f5bbabb0f0064516301fa4d08663c83f5b2:cardano+cardano_testnet%2Fnative%2F581684861359b6d4d00594073a9aa4223c6fc24d24da05e1b34fb865544553545f544f4b454e",
837
+ "operationsCount": 1,
838
+ "parentId": "js:2:cardano_testnet:a902bc61a79256e1d5858d2ba49cab7011a26ad49580daace7412e255e818b1c1eba2defa321ff2937deba397f639f5bbabb0f0064516301fa4d08663c83f5b2:cardano",
839
+ "pendingOperations": Array [],
840
+ "spendableBalance": "5000000000",
841
+ "starred": false,
842
+ "swapHistory": Array [],
843
+ "tokenId": "cardano_testnet/native/581684861359b6d4d00594073a9aa4223c6fc24d24da05e1b34fb865544553545f544f4b454e",
844
+ "type": "TokenAccountRaw",
845
+ },
832
846
  Object {
833
847
  "balance": "0",
834
848
  "cardanoResources": Object {
@@ -1662,6 +1676,32 @@ Array [
1662
1676
  "value": "1000000000",
1663
1677
  },
1664
1678
  ],
1679
+ Array [
1680
+ Object {
1681
+ "accountId": "js:2:cardano_testnet:a902bc61a79256e1d5858d2ba49cab7011a26ad49580daace7412e255e818b1c1eba2defa321ff2937deba397f639f5bbabb0f0064516301fa4d08663c83f5b2:cardano+cardano_testnet%2Fnative%2F581684861359b6d4d00594073a9aa4223c6fc24d24da05e1b34fb865544553545f544f4b454e",
1682
+ "blockHash": undefined,
1683
+ "blockHeight": 3568569,
1684
+ "contract": undefined,
1685
+ "extra": Object {
1686
+ "memo": undefined,
1687
+ },
1688
+ "fee": "215441",
1689
+ "hash": "2c8106d296fe10a8e8bf37a0202df048265dc17f2aa6c184bd132960fbb18bae",
1690
+ "id": "js:2:cardano_testnet:a902bc61a79256e1d5858d2ba49cab7011a26ad49580daace7412e255e818b1c1eba2defa321ff2937deba397f639f5bbabb0f0064516301fa4d08663c83f5b2:cardano+cardano_testnet%2Fnative%2F581684861359b6d4d00594073a9aa4223c6fc24d24da05e1b34fb865544553545f544f4b454e-2c8106d296fe10a8e8bf37a0202df048265dc17f2aa6c184bd132960fbb18bae-IN",
1691
+ "operator": undefined,
1692
+ "recipients": Array [
1693
+ "addr_test1qqs6df3z63uknmct9wy77lpwss50j4d6u0jj7gg7v5wvll23krjmh2m86v92yu0my7rqydx3h5erqwty82kwgjuc4elsr6hdhy",
1694
+ "addr_test1qzpdaf0vr0gdsjncllv7cejmpwmwu6m86c3epd8uadv99ktra2x9gp8eakdwsrv4k42ys4ajqy0r7f4k8hwrhcdt6sks7k7alz",
1695
+ ],
1696
+ "senders": Array [
1697
+ "addr_test1qr4qq9s7cf28zse9t4c46l3utj6ajphx654l8ddcv6rhketra2x9gp8eakdwsrv4k42ys4ajqy0r7f4k8hwrhcdt6sksjh37nz",
1698
+ ],
1699
+ "standard": undefined,
1700
+ "tokenId": undefined,
1701
+ "type": "IN",
1702
+ "value": "5000000000",
1703
+ },
1704
+ ],
1665
1705
  Array [],
1666
1706
  ]
1667
1707
  `;
@@ -1,3 +1,4 @@
1
+ import { NotEnoughBalance } from "@ledgerhq/errors";
1
2
  import "../../__tests__/test-helpers/setup";
2
3
  import { testBridge } from "../../__tests__/test-helpers/bridge";
3
4
  import BigNumber from "bignumber.js";
@@ -33,6 +34,43 @@ const dataset: DatasetTest<Transaction> = {
33
34
  },
34
35
  },
35
36
  },
37
+ {
38
+ name: "token amount more than balance",
39
+ transaction: fromTransactionRaw({
40
+ family: "cardano",
41
+ recipient:
42
+ "addr_test1qpl90kc2jl5kr9tev0s7vays9yhwcdnq8nlylyk4dqsdq3g466elxnxwrzwq72pvp5akenj30t5s9et7frfvrxxx8xcsxrzs87",
43
+ amount: "5000000001",
44
+ subAccountId:
45
+ "js:2:cardano_testnet:a902bc61a79256e1d5858d2ba49cab7011a26ad49580daace7412e255e818b1c1eba2defa321ff2937deba397f639f5bbabb0f0064516301fa4d08663c83f5b2:+cardano_testnet%2Fnative%2F581684861359b6d4d00594073a9aa4223c6fc24d24da05e1b34fb865544553545f544f4b454e",
46
+ mode: "send",
47
+ }),
48
+ expectedStatus: {
49
+ amount: new BigNumber("5000000001"),
50
+ errors: {
51
+ amount: new NotEnoughBalance(),
52
+ },
53
+ },
54
+ },
55
+ {
56
+ name: "send max token",
57
+ transaction: fromTransactionRaw({
58
+ family: "cardano",
59
+ recipient:
60
+ "addr_test1qpl90kc2jl5kr9tev0s7vays9yhwcdnq8nlylyk4dqsdq3g466elxnxwrzwq72pvp5akenj30t5s9et7frfvrxxx8xcsxrzs87",
61
+ amount: "0",
62
+ subAccountId:
63
+ "js:2:cardano_testnet:a902bc61a79256e1d5858d2ba49cab7011a26ad49580daace7412e255e818b1c1eba2defa321ff2937deba397f639f5bbabb0f0064516301fa4d08663c83f5b2:+cardano_testnet%2Fnative%2F581684861359b6d4d00594073a9aa4223c6fc24d24da05e1b34fb865544553545f544f4b454e",
64
+ mode: "send",
65
+ useAllAmount: true,
66
+ }),
67
+ expectedStatus: {
68
+ amount: new BigNumber("5000000000"),
69
+ totalSpent: new BigNumber("5000000000"),
70
+ errors: {},
71
+ warnings: {},
72
+ },
73
+ },
36
74
  ],
37
75
  },
38
76
  ],
@@ -83,6 +83,10 @@ const mapTxToTokenAccountOperation = ({
83
83
 
84
84
  const tokenOperationType = token.amount.lt(0) ? "OUT" : "IN";
85
85
  const memo = getMemoFromTx(tx);
86
+ const extra = {};
87
+ if (memo) {
88
+ extra["memo"] = memo;
89
+ }
86
90
  const operation: Operation = {
87
91
  accountId: tokenAccountId,
88
92
  id: encodeOperationId(tokenAccountId, tx.hash, tokenOperationType),
@@ -102,9 +106,7 @@ const mapTxToTokenAccountOperation = ({
102
106
  ),
103
107
  blockHeight: tx.blockHeight,
104
108
  date: new Date(tx.timestamp),
105
- extra: {
106
- memo,
107
- },
109
+ extra: extra,
108
110
  blockHash: undefined,
109
111
  };
110
112
  operations.push(operation);
@@ -0,0 +1,109 @@
1
+ import BigNumber from "bignumber.js";
2
+ import { getAccountUnit, getMainAccount } from "../../account";
3
+ import { formatCurrencyUnit } from "../../currencies";
4
+ import { DeviceTransactionField } from "../../transaction";
5
+ import { Account, AccountLike, TransactionStatus } from "../../types";
6
+ import { decodeTokenAssetId, decodeTokenCurrencyId } from "./buildSubAccounts";
7
+ import { CardanoResources, Transaction } from "./types";
8
+ import { utils as TyphonUtils } from "@stricahq/typhonjs";
9
+ import { decodeTokenName } from "./logic";
10
+
11
+ function getDeviceTransactionConfig({
12
+ account,
13
+ transaction,
14
+ parentAccount,
15
+ }: {
16
+ account: AccountLike;
17
+ parentAccount: Account | null | undefined;
18
+ transaction: Transaction;
19
+ status: TransactionStatus;
20
+ }): Array<DeviceTransactionField> {
21
+ const { mode } = transaction;
22
+ const fields: DeviceTransactionField[] = [];
23
+ const mainAccount = getMainAccount(account, parentAccount);
24
+
25
+ if (mode === "send") {
26
+ if (account.type === "TokenAccount") {
27
+ const cardanoResources = mainAccount.cardanoResources as CardanoResources;
28
+
29
+ const { assetId } = decodeTokenCurrencyId(account.token.id);
30
+ const { policyId, assetName } = decodeTokenAssetId(assetId);
31
+ const transactionAmount = transaction.useAllAmount
32
+ ? account.balance
33
+ : transaction.amount;
34
+ const { fees } = transaction;
35
+
36
+ const tokensToSend = [
37
+ {
38
+ policyId,
39
+ assetName,
40
+ amount: transactionAmount,
41
+ },
42
+ ];
43
+
44
+ const requiredMinAdaForTokens = TyphonUtils.calculateMinUtxoAmount(
45
+ tokensToSend,
46
+ new BigNumber(cardanoResources.protocolParams.lovelacePerUtxoWord)
47
+ );
48
+ fields.push({
49
+ type: "text",
50
+ label: "ADA",
51
+ value: formatCurrencyUnit(
52
+ getAccountUnit(mainAccount),
53
+ requiredMinAdaForTokens,
54
+ { showCode: true, disableRounding: true }
55
+ ),
56
+ });
57
+ fields.push({
58
+ type: "text",
59
+ label: "Token Name",
60
+ value: decodeTokenName(assetName),
61
+ });
62
+ fields.push({
63
+ type: "text",
64
+ label: "Amount",
65
+ value: formatCurrencyUnit(getAccountUnit(account), transactionAmount, {
66
+ showCode: true,
67
+ disableRounding: true,
68
+ }),
69
+ });
70
+ if (fees) {
71
+ fields.push({
72
+ type: "text",
73
+ label: "Fees",
74
+ value: formatCurrencyUnit(getAccountUnit(mainAccount), fees, {
75
+ showCode: true,
76
+ disableRounding: true,
77
+ }),
78
+ });
79
+ }
80
+ } else if (account.type === "Account") {
81
+ const { fees } = transaction;
82
+ const transactionAmount = transaction.useAllAmount
83
+ ? account.balance
84
+ : transaction.amount;
85
+ fields.push({
86
+ type: "text",
87
+ label: "Amount",
88
+ value: formatCurrencyUnit(getAccountUnit(account), transactionAmount, {
89
+ showCode: true,
90
+ disableRounding: true,
91
+ }),
92
+ });
93
+ if (fees) {
94
+ fields.push({
95
+ type: "text",
96
+ label: "Fees",
97
+ value: formatCurrencyUnit(getAccountUnit(account), fees, {
98
+ showCode: true,
99
+ disableRounding: true,
100
+ }),
101
+ });
102
+ }
103
+ }
104
+ }
105
+
106
+ return fields;
107
+ }
108
+
109
+ export default getDeviceTransactionConfig;
@@ -6,3 +6,10 @@ import { createCustomErrorClass } from "@ledgerhq/errors";
6
6
  export const CardanoMinAmountError = createCustomErrorClass(
7
7
  "CardanoMinAmountError"
8
8
  );
9
+
10
+ /**
11
+ * Cardano error thrown when not enough funds to perform transaction
12
+ */
13
+ export const CardanoNotEnoughFunds = createCustomErrorClass(
14
+ "CardanoNotEnoughFunds"
15
+ );
@@ -30,10 +30,13 @@ const estimateMaxSpendable = async ({
30
30
  return account.balance;
31
31
  }
32
32
 
33
+ const dummyRecipient =
34
+ "addr_test1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq0uk53y";
33
35
  const a = getMainAccount(account, parentAccount);
34
- const t = {
36
+ const t: Transaction = {
35
37
  ...createTransaction(),
36
38
  ...transaction,
39
+ recipient: dummyRecipient,
37
40
  // amount field will not be used to build a transaction when useAllAmount is true
38
41
  amount: new BigNumber(0),
39
42
  useAllAmount: true,
@@ -1,6 +1,5 @@
1
1
  import { BigNumber } from "bignumber.js";
2
2
  import {
3
- NotEnoughBalance,
4
3
  RecipientRequired,
5
4
  FeeNotLoaded,
6
5
  InvalidAddress,
@@ -10,7 +9,7 @@ import type { Account, TransactionStatus } from "../../types";
10
9
  import type { CardanoResources, Token, Transaction } from "./types";
11
10
  import { isValidAddress } from "./logic";
12
11
  import { utils as TyphonUtils } from "@stricahq/typhonjs";
13
- import { CardanoMinAmountError } from "./errors";
12
+ import { CardanoMinAmountError, CardanoNotEnoughFunds } from "./errors";
14
13
  import { AccountAwaitingSendPendingOperations } from "../../errors";
15
14
  import { getNetworkParameters } from "./networks";
16
15
  import { decodeTokenAssetId, decodeTokenCurrencyId } from "./buildSubAccounts";
@@ -86,17 +85,18 @@ const getTransactionStatus = async (
86
85
 
87
86
  if (!amount.gt(0)) {
88
87
  errors.amount = useAllAmount
89
- ? new NotEnoughBalance()
88
+ ? new CardanoNotEnoughFunds()
90
89
  : new AmountRequired();
91
90
  } else if (!t.subAccountId && amount.lt(minTransactionAmount)) {
92
91
  errors.amount = new CardanoMinAmountError("", {
93
92
  amount: minTransactionAmount.div(1e6).toString(),
94
93
  });
95
94
  } else if (
96
- (tokenAccount && totalSpent.gt(tokenAccount.balance)) ||
97
- totalSpent.gt(a.balance)
95
+ tokenAccount
96
+ ? totalSpent.gt(tokenAccount.balance)
97
+ : totalSpent.gt(a.balance)
98
98
  ) {
99
- errors.amount = new NotEnoughBalance();
99
+ errors.amount = new CardanoNotEnoughFunds();
100
100
  } else {
101
101
  try {
102
102
  await buildTransaction(a, t);
@@ -105,7 +105,7 @@ const getTransactionStatus = async (
105
105
  e.message.toLowerCase() === "not enough ada" ||
106
106
  e.message.toLowerCase() === "not enough tokens"
107
107
  ) {
108
- errors.amount = new NotEnoughBalance();
108
+ errors.amount = new CardanoNotEnoughFunds();
109
109
  }
110
110
  throw e;
111
111
  }
@@ -83,11 +83,18 @@ const buildOptimisticOperation = (
83
83
  const memoMetadata = auxiliaryData.metadata.find(
84
84
  (m) => m.label === MEMO_LABEL
85
85
  );
86
- if (memoMetadata && Array.isArray(memoMetadata.data)) {
87
- memo = memoMetadata.data.join(", ");
86
+ if (memoMetadata && memoMetadata.data instanceof Map) {
87
+ const msg = memoMetadata.data.get("msg");
88
+ if (Array.isArray(msg) && msg.length) {
89
+ memo = msg.join(", ");
90
+ }
88
91
  }
89
92
  }
90
93
 
94
+ const extra = {};
95
+ if (memo) {
96
+ extra["memo"] = memo;
97
+ }
91
98
  const op: Operation = {
92
99
  id: encodeOperationId(account.id, transactionHash, opType),
93
100
  hash: transactionHash,
@@ -100,9 +107,7 @@ const buildOptimisticOperation = (
100
107
  recipients: transaction.getOutputs().map((o) => o.address.getBech32()),
101
108
  accountId: account.id,
102
109
  date: new Date(),
103
- extra: {
104
- memo,
105
- },
110
+ extra: extra,
106
111
  };
107
112
 
108
113
  const tokenAccount = t.subAccountId
@@ -21,6 +21,7 @@ import {
21
21
  getMemoFromTx,
22
22
  getOperationType,
23
23
  isHexString,
24
+ mergeTokens,
24
25
  } from "./logic";
25
26
  import { encodeOperationId } from "../../operation";
26
27
  import { getNetworkParameters } from "./networks";
@@ -28,6 +29,9 @@ import { getNetworkInfo } from "./api/getNetworkInfo";
28
29
  import uniqBy from "lodash/uniqBy";
29
30
  import postSyncPatch from "./postSyncPatch";
30
31
  import { getTransactions } from "./api/getTransactions";
32
+ import { buildSubAccounts } from "./buildSubAccounts";
33
+ import { calculateMinUtxoAmount } from "@stricahq/typhonjs/dist/utils/utils";
34
+ import { listTokensForCryptoCurrency } from "../../currencies";
31
35
 
32
36
  function mapTxToAccountOperation(
33
37
  tx: APITransaction,
@@ -43,6 +47,10 @@ function mapTxToAccountOperation(
43
47
 
44
48
  const subOperations = inferSubOperations(tx.hash, subAccounts);
45
49
  const memo = getMemoFromTx(tx);
50
+ const extra = {};
51
+ if (memo) {
52
+ extra["memo"] = memo;
53
+ }
46
54
 
47
55
  return {
48
56
  accountId,
@@ -64,14 +72,12 @@ function mapTxToAccountOperation(
64
72
  subOperations,
65
73
  blockHeight: tx.blockHeight,
66
74
  date: new Date(tx.timestamp),
67
- extra: {
68
- memo,
69
- },
75
+ extra: extra,
70
76
  blockHash: undefined,
71
77
  };
72
78
  }
73
79
 
74
- function syncUtxos(
80
+ function prepareUtxos(
75
81
  newTransactions: Array<APITransaction>,
76
82
  stableUtxos: Array<CardanoOutput>,
77
83
  accountCredentialsMap: Record<string, PaymentCredential>
@@ -116,7 +122,10 @@ function syncUtxos(
116
122
  return utxos;
117
123
  }
118
124
 
119
- export const getAccountShape: GetAccountShape = async (info) => {
125
+ export const getAccountShape: GetAccountShape = async (
126
+ info,
127
+ { blacklistedTokenIds }
128
+ ) => {
120
129
  const {
121
130
  transport,
122
131
  currency,
@@ -152,16 +161,27 @@ export const getAccountShape: GetAccountShape = async (info) => {
152
161
  derivationMode,
153
162
  });
154
163
 
164
+ // when new tokens are added / blacklist changes, we need to sync again because we need to go through all operations again
165
+ const syncHash =
166
+ JSON.stringify(blacklistedTokenIds || []) +
167
+ "_" +
168
+ listTokensForCryptoCurrency(currency, {
169
+ withDelisted: true,
170
+ }).length;
171
+ const outdatedSyncHash = initialAccount?.syncHash !== syncHash;
172
+
155
173
  const requiredConfirmations = 90;
156
174
 
157
175
  const oldOperations = initialAccount?.operations || [];
158
176
  const lastBlockHeight = oldOperations.length
159
177
  ? (oldOperations[0].blockHeight || 0) + 1
160
178
  : 0;
161
- const syncFromBlockHeight =
162
- lastBlockHeight > requiredConfirmations
163
- ? lastBlockHeight - requiredConfirmations
164
- : 0;
179
+
180
+ const syncFromBlockHeight = outdatedSyncHash
181
+ ? 0
182
+ : lastBlockHeight > requiredConfirmations
183
+ ? lastBlockHeight - requiredConfirmations
184
+ : 0;
165
185
 
166
186
  const {
167
187
  transactions: newTransactions,
@@ -195,21 +215,24 @@ export const getAccountShape: GetAccountShape = async (info) => {
195
215
  (u) => stableOperationsByIds[u.hash]
196
216
  );
197
217
 
198
- const utxos = syncUtxos(newTransactions, stableUtxos, accountCredentialsMap);
218
+ const utxos = prepareUtxos(
219
+ newTransactions,
220
+ stableUtxos,
221
+ accountCredentialsMap
222
+ );
199
223
  const accountBalance = utxos.reduce(
200
224
  (total, u) => total.plus(u.amount),
201
225
  new BigNumber(0)
202
226
  );
203
- // const tokenBalance = mergeTokens(utxos.map((u) => u.tokens).flat());
204
- // const subAccounts = buildSubAccounts({
205
- // initialAccount,
206
- // parentAccountId: accountId,
207
- // parentCurrency: currency,
208
- // newTransactions,
209
- // tokens: tokenBalance,
210
- // accountCredentialsMap,
211
- // });
212
- const subAccounts = [];
227
+ const tokenBalance = mergeTokens(utxos.map((u) => u.tokens).flat());
228
+ const subAccounts = buildSubAccounts({
229
+ initialAccount,
230
+ parentAccountId: accountId,
231
+ parentCurrency: currency,
232
+ newTransactions,
233
+ tokens: tokenBalance,
234
+ accountCredentialsMap,
235
+ });
213
236
 
214
237
  const newOperations = newTransactions.map((t) =>
215
238
  mapTxToAccountOperation(t, accountId, accountCredentialsMap, subAccounts)
@@ -233,13 +256,21 @@ export const getAccountShape: GetAccountShape = async (info) => {
233
256
  }).getBech32(),
234
257
  }));
235
258
  const cardanoNetworkInfo = await getNetworkInfo(initialAccount, currency);
259
+ const minAdaBalanceForTokens = tokenBalance.length
260
+ ? calculateMinUtxoAmount(
261
+ tokenBalance,
262
+ new BigNumber(cardanoNetworkInfo.protocolParams.lovelacePerUtxoWord),
263
+ false
264
+ )
265
+ : new BigNumber(0);
236
266
 
237
267
  return {
238
268
  id: accountId,
239
269
  xpub,
240
270
  balance: accountBalance,
241
- spendableBalance: accountBalance,
271
+ spendableBalance: accountBalance.minus(minAdaBalanceForTokens),
242
272
  operations: operations,
273
+ syncHash,
243
274
  subAccounts,
244
275
  freshAddresses,
245
276
  freshAddress: freshAddresses[0].address,
@@ -47,7 +47,7 @@ export const prepareTransaction = async (
47
47
  try {
48
48
  transaction = await buildTransaction(a, t);
49
49
  } catch (error) {
50
- return { ...t, fees: new BigNumber(0), amount: new BigNumber(0) };
50
+ return { ...t, fees: new BigNumber(0), amount: t.amount };
51
51
  }
52
52
 
53
53
  const transactionFees = transaction.getFee();
@@ -448,3 +448,13 @@ export function isHexString(value: string): boolean {
448
448
  const regExp = /^[0-9a-fA-F]+$/;
449
449
  return regExp.test(value);
450
450
  }
451
+
452
+ export function decodeTokenName(assetName: string): string {
453
+ if (assetName.length > 0) {
454
+ const bytes = [...Buffer.from(assetName, "hex")];
455
+ if (bytes.filter((byte) => byte <= 32 || byte >= 127).length === 0) {
456
+ return String.fromCharCode(...bytes);
457
+ }
458
+ }
459
+ return assetName;
460
+ }
@@ -9,10 +9,15 @@ import invariant from "invariant";
9
9
  import { utils as TyphonUtils } from "@stricahq/typhonjs";
10
10
  import { mergeTokens } from "./logic";
11
11
  import { parseCurrencyUnit } from "../../currencies";
12
+ import { SubAccount } from "../../types";
12
13
 
13
14
  const currency = getCryptoCurrencyById("cardano");
14
15
  const minBalanceRequired = parseCurrencyUnit(currency.units[0], "2.2");
15
16
  const minBalanceRequiredForMaxSend = parseCurrencyUnit(currency.units[0], "1");
17
+ const minSpendableRequiredForTokenTx = parseCurrencyUnit(
18
+ currency.units[0],
19
+ "3"
20
+ );
16
21
 
17
22
  const cardano: AppSpec<Transaction> = {
18
23
  name: "cardano",
@@ -34,7 +39,12 @@ const cardano: AppSpec<Transaction> = {
34
39
 
35
40
  const updates = [
36
41
  { recipient },
37
- { amount: new BigNumber(account.balance.dividedBy(2)).dp(0, 1) },
42
+ {
43
+ amount: new BigNumber(account.balance.dividedBy(2)).dp(
44
+ 0,
45
+ BigNumber.ROUND_CEIL
46
+ ),
47
+ },
38
48
  { memo: "LedgerLiveBot" },
39
49
  ];
40
50
 
@@ -90,6 +100,48 @@ const cardano: AppSpec<Transaction> = {
90
100
  );
91
101
  },
92
102
  },
103
+ {
104
+ name: "move ~10% token",
105
+ maxRun: 1,
106
+ transaction: ({ account, siblings, bridge, maxSpendable }) => {
107
+ invariant(
108
+ maxSpendable.gte(minSpendableRequiredForTokenTx),
109
+ "balance is too low"
110
+ );
111
+ const sibling = pickSiblings(siblings, 3);
112
+ const recipient = sibling.freshAddress;
113
+ const transaction = bridge.createTransaction(account);
114
+
115
+ const subAccount = account.subAccounts?.find((subAccount) =>
116
+ subAccount.balance.gt(1)
117
+ ) as SubAccount;
118
+ invariant(subAccount, "No token account with balance");
119
+
120
+ const updates = [
121
+ { subAccountId: subAccount.id },
122
+ { recipient },
123
+ {
124
+ amount: new BigNumber(subAccount.balance.dividedBy(10)).dp(
125
+ 0,
126
+ BigNumber.ROUND_CEIL
127
+ ),
128
+ },
129
+ ];
130
+
131
+ return {
132
+ transaction,
133
+ updates,
134
+ };
135
+ },
136
+ test: ({ operation, transaction }): void => {
137
+ expect(operation.subOperations).toBeTruthy();
138
+ expect(operation.subOperations?.length).toEqual(1);
139
+
140
+ const subOperation =
141
+ operation.subOperations && operation.subOperations[0];
142
+ expect(subOperation?.value).toEqual(transaction.amount);
143
+ },
144
+ },
93
145
  ],
94
146
  };
95
147
 
@@ -39,6 +39,21 @@ const acceptTransaction: DeviceAction<Transaction, any> = deviceActionFlow({
39
39
  expectedValue: ({ account, status }) =>
40
40
  expectedValue(account.unit, status.amount),
41
41
  },
42
+ {
43
+ title: "Asset fingerprint",
44
+ button: "LRlr",
45
+ },
46
+ {
47
+ title: "Token amount",
48
+ button: "LRlr",
49
+ },
50
+ {
51
+ title: "Confirm",
52
+ },
53
+ {
54
+ title: "output?",
55
+ button: "Rr",
56
+ },
42
57
  {
43
58
  title: "Transaction fee",
44
59
  button: "LRlr",
@@ -136,7 +136,7 @@ export async function getOperationsForAccount(
136
136
  // Set a value just so that it's considered confirmed according to isConfirmedOperation
137
137
  blockHeight: 5,
138
138
  blockHash: null,
139
- extra: { consensus_timestamp },
139
+ extra: {},
140
140
  fee,
141
141
  hash,
142
142
  recipients,
@@ -23,6 +23,7 @@ export async function buildUnsignedTransaction({
23
23
  return new hedera.TransferTransaction()
24
24
  .setNodeAccountIds([new AccountId(3)])
25
25
  .setTransactionId(hedera.TransactionId.generate(accountId))
26
+ .setTransactionMemo(transaction.memo ?? "")
26
27
  .addHbarTransfer(accountId, hbarAmount.negated())
27
28
  .addHbarTransfer(transaction.recipient, hbarAmount)
28
29
  .freeze();
@@ -39,6 +39,14 @@ function getDeviceTransactionConfig({
39
39
  });
40
40
  }
41
41
 
42
+ if (transaction.memo) {
43
+ fields.push({
44
+ type: "text",
45
+ label: "Memo",
46
+ value: transaction.memo,
47
+ });
48
+ }
49
+
42
50
  return fields;
43
51
  }
44
52