@otoplo/wallet-common 0.1.15 → 0.2.0

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 (56) hide show
  1. package/dist/index.js +555 -579
  2. package/dist/index.js.map +1 -1
  3. package/dist/types/persistence/datastore/db.d.ts +5 -3
  4. package/dist/types/persistence/datastore/db.d.ts.map +1 -1
  5. package/dist/types/persistence/wallet-db.d.ts +10 -18
  6. package/dist/types/persistence/wallet-db.d.ts.map +1 -1
  7. package/dist/types/services/asset.d.ts +5 -9
  8. package/dist/types/services/asset.d.ts.map +1 -1
  9. package/dist/types/services/cache.d.ts +4 -2
  10. package/dist/types/services/cache.d.ts.map +1 -1
  11. package/dist/types/services/wallet.d.ts +2 -1
  12. package/dist/types/services/wallet.d.ts.map +1 -1
  13. package/dist/types/state/hooks.d.ts +2 -1
  14. package/dist/types/state/hooks.d.ts.map +1 -1
  15. package/dist/types/state/slices/market.d.ts +54 -0
  16. package/dist/types/state/slices/market.d.ts.map +1 -0
  17. package/dist/types/state/slices/status.d.ts +0 -27
  18. package/dist/types/state/slices/status.d.ts.map +1 -1
  19. package/dist/types/state/slices/wallet.d.ts +21 -220
  20. package/dist/types/state/slices/wallet.d.ts.map +1 -1
  21. package/dist/types/state/store.d.ts +3 -0
  22. package/dist/types/state/store.d.ts.map +1 -1
  23. package/dist/types/types/db.types.d.ts +16 -11
  24. package/dist/types/types/db.types.d.ts.map +1 -1
  25. package/dist/types/types/wallet.types.d.ts +39 -14
  26. package/dist/types/types/wallet.types.d.ts.map +1 -1
  27. package/dist/types/utils/asset.d.ts +3 -6
  28. package/dist/types/utils/asset.d.ts.map +1 -1
  29. package/dist/types/utils/common.d.ts +0 -1
  30. package/dist/types/utils/common.d.ts.map +1 -1
  31. package/dist/types/utils/enums.d.ts +0 -4
  32. package/dist/types/utils/enums.d.ts.map +1 -1
  33. package/dist/types/utils/index.d.ts +1 -1
  34. package/dist/types/utils/index.d.ts.map +1 -1
  35. package/dist/types/utils/{price.d.ts → market.d.ts} +19 -13
  36. package/dist/types/utils/market.d.ts.map +1 -0
  37. package/package.json +4 -5
  38. package/src/persistence/datastore/db.ts +5 -3
  39. package/src/persistence/wallet-db.ts +28 -28
  40. package/src/services/asset.ts +69 -162
  41. package/src/services/cache.ts +12 -2
  42. package/src/services/wallet.ts +23 -21
  43. package/src/state/hooks.ts +3 -1
  44. package/src/state/slices/market.ts +47 -0
  45. package/src/state/slices/status.ts +2 -31
  46. package/src/state/slices/wallet.ts +8 -13
  47. package/src/state/store.ts +3 -0
  48. package/src/types/db.types.ts +17 -12
  49. package/src/types/wallet.types.ts +40 -16
  50. package/src/utils/asset.ts +12 -52
  51. package/src/utils/common.ts +0 -6
  52. package/src/utils/enums.ts +0 -5
  53. package/src/utils/index.ts +1 -1
  54. package/src/utils/market.ts +97 -0
  55. package/dist/types/utils/price.d.ts.map +0 -1
  56. package/src/utils/price.ts +0 -46
@@ -193,11 +193,10 @@ export class WalletManager {
193
193
  await this.subscribeAddresses(this.getAllAddresses(), true);
194
194
 
195
195
  const tokensLoadPromises: Promise<void>[] = [];
196
- tokensLoadPromises.push(this.assetService.fetchAndSaveTokens(MAIN_WALLET_ID, sumTokensBalance(this.getMainAddresses().map(a => a.tokensBalance))));
196
+ tokensLoadPromises.push(this.assetService.syncTokens(MAIN_WALLET_ID, sumTokensBalance(this.getMainAddresses().map(a => a.tokensBalance))));
197
197
  for (const account of this.accounts.values()) {
198
- tokensLoadPromises.push(this.assetService.fetchAndSaveTokens(account.id, account.tokensBalance));
198
+ tokensLoadPromises.push(this.assetService.syncTokens(account.id, account.tokensBalance));
199
199
  }
200
-
201
200
  await Promise.all(tokensLoadPromises);
202
201
  }
203
202
 
@@ -219,7 +218,6 @@ export class WalletManager {
219
218
  address: this.getReceiveAddress(),
220
219
  balance: sumBalance(walletAddresses.map(a => a.balance)),
221
220
  tokensBalance: sumTokensBalance(walletAddresses.map(a => a.tokensBalance)),
222
- tokens: [],
223
221
  sessions: {}
224
222
  }
225
223
  });
@@ -233,7 +231,6 @@ export class WalletManager {
233
231
  address: account.address,
234
232
  balance: account.balance,
235
233
  tokensBalance: account.tokensBalance,
236
- tokens: [],
237
234
  sessions: {}
238
235
  }
239
236
  });
@@ -401,7 +398,7 @@ export class WalletManager {
401
398
  }
402
399
  }
403
400
 
404
- await Promise.all([this.postProcessWalletUpdate(walletTxs), this.postProcessAccountUpdate(accountsTxs)]);
401
+ await Promise.all([this.postProcessWalletUpdate(walletTxs), this.postProcessAccountsUpdate(accountsTxs)]);
405
402
  } catch (e) {
406
403
  console.error('Error processing pending updates:', e);
407
404
  } finally {
@@ -431,29 +428,35 @@ export class WalletManager {
431
428
  this.notify({ type: 'account_balance_updated', accountId: MAIN_WALLET_ID, balance: walletBalance, tokensBalance: walletTokenBalances });
432
429
  this.notify({ type: 'main_address_updated', address: this.getReceiveAddress() });
433
430
 
434
- const nftPromise = this.assetService.syncNfts(MAIN_WALLET_ID, walletTokenBalances);
435
- for (const tx of txHistoryMap.values()) {
436
- await this.transactionService.classifyAndSaveTransaction(MAIN_WALLET_ID, tx.tx_hash, walletAddresses.map(a => a.address));
437
- }
438
- await nftPromise;
431
+ await this.postProcessActivity(MAIN_WALLET_ID, Array.from(txHistoryMap.values()), walletAddresses.map(a => a.address), walletTokenBalances);
439
432
  }
440
433
 
441
- private async postProcessAccountUpdate(history: AddressHistory[]): Promise<void> {
434
+ private async postProcessAccountsUpdate(history: AddressHistory[]): Promise<void> {
442
435
  if (history.length === 0) {
443
436
  return;
444
437
  }
445
438
 
439
+ const activityPromises: Promise<void>[] = [];
446
440
  for (const { address, txs } of history) {
447
441
  const account = address as AccountDTO;
448
442
 
449
443
  this.notify({ type: 'account_balance_updated', accountId: account.id, balance: account.balance, tokensBalance: account.tokensBalance });
450
444
 
451
- const nftPromise = this.assetService.syncNfts(account.id, account.tokensBalance);
452
- for (const tx of txs) {
453
- await this.transactionService.classifyAndSaveTransaction(account.id, tx.tx_hash, [account.address]);
454
- }
455
- await nftPromise;
445
+ const p = this.postProcessActivity(account.id, txs, [account.address], account.tokensBalance);
446
+ activityPromises.push(p);
447
+ }
448
+ await Promise.all(activityPromises);
449
+ }
450
+
451
+ private async postProcessActivity(accountId: number, txHistory: ITXHistory[], addresses: string[], tokensBalance: Record<string, Balance>): Promise<void> {
452
+ const postPromises: Promise<void>[] = [];
453
+ const nftPromise = this.assetService.syncNfts(accountId, tokensBalance);
454
+ for (const tx of txHistory) {
455
+ const p = this.transactionService.classifyAndSaveTransaction(accountId, tx.tx_hash, addresses);
456
+ postPromises.push(p);
456
457
  }
458
+ postPromises.push(nftPromise);
459
+ await Promise.all(postPromises);
457
460
  }
458
461
 
459
462
  private async fetchAndUpdateAddress(addrDTO: AddressDTO, hash: string): Promise<AddressHistory> {
@@ -544,7 +547,6 @@ export class WalletManager {
544
547
  this.accounts.set(id, account);
545
548
  this.accountsAddressToId.set(address, id);
546
549
  await this.walletDb.saveAccount(account);
547
- await this.subscribeAddresses([account]);
548
550
 
549
551
  this.notify({
550
552
  type: 'new_account',
@@ -554,10 +556,10 @@ export class WalletManager {
554
556
  address: account.address,
555
557
  balance: account.balance,
556
558
  tokensBalance: account.tokensBalance,
557
- tokens: [],
558
559
  sessions: {}
559
560
  }
560
561
  });
562
+ await this.subscribeAddresses([account]);
561
563
  }
562
564
 
563
565
  public async updateAccountName(id: number, name: string): Promise<void> {
@@ -609,8 +611,7 @@ export class WalletManager {
609
611
  private async addVault(vault: VaultDTO): Promise<void> {
610
612
  this.vaults.set(vault.address, vault);
611
613
  await this.walletDb.saveVault(vault);
612
- await this.subscribeAddresses([vault]);
613
-
614
+
614
615
  this.notify({
615
616
  type: 'new_vault',
616
617
  vault: {
@@ -620,6 +621,7 @@ export class WalletManager {
620
621
  index: vault.idx
621
622
  }
622
623
  });
624
+ await this.subscribeAddresses([vault]);
623
625
  }
624
626
 
625
627
  public async rescanVaults(): Promise<boolean> {
@@ -2,12 +2,14 @@ import { useSelector } from "react-redux";
2
2
  import type { SharedState } from "./store";
3
3
  import { createSelector } from "@reduxjs/toolkit";
4
4
  import type { AuthState } from "./slices";
5
- import type { Account, AppNotification, Balance, SessionInfo, VaultInfo } from "../types";
5
+ import type { Account, AppNotification, Balance, MarketData, SessionInfo, VaultInfo } from "../types";
6
6
 
7
7
  const useSharedSelector = useSelector.withTypes<SharedState>();
8
8
 
9
9
  export const useAuth = (): AuthState => useSharedSelector(state => state.auth);
10
10
 
11
+ export const useMarketData = (tokenId: string): MarketData => useSharedSelector(state => state.market.data[tokenId]);
12
+
11
13
  export const useBlockHeight = (): number => useSharedSelector(state => state.status.height);
12
14
 
13
15
  export const useAccount = (id: number): Account => useSharedSelector(state => state.wallet.accounts[id]);
@@ -0,0 +1,47 @@
1
+ import type { PayloadAction } from "@reduxjs/toolkit";
2
+ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
3
+ import type { MarketData } from "../../types";
4
+ import type { CurrencyCode} from "../../utils";
5
+ import { getMarketData } from "../../utils";
6
+ import type { SharedState } from "../store";
7
+
8
+ export interface MarketState {
9
+ currency: CurrencyCode;
10
+ data: Record<string, MarketData>;
11
+ }
12
+
13
+ const initialState: MarketState = {
14
+ currency: 'usd',
15
+ data: {}
16
+ };
17
+
18
+ const updateMarketData = createAsyncThunk('market/updateMarketData', async (_, thunkAPI) => {
19
+ const state = thunkAPI.getState() as SharedState;
20
+ return await getMarketData(state.market.currency);
21
+ });
22
+
23
+ const marketSlice = createSlice({
24
+ name: 'market',
25
+ initialState,
26
+ reducers: {
27
+ setCurrency: (state, action: PayloadAction<CurrencyCode>) => {
28
+ state.currency = action.payload;
29
+ },
30
+ setMarketData: (state, action: PayloadAction<Record<string, MarketData>>) => {
31
+ state.data = action.payload;
32
+ }
33
+ },
34
+ extraReducers: (builder) => {
35
+ builder
36
+ .addCase(updateMarketData.fulfilled, (state, action) => {
37
+ state.data = action.payload;
38
+ })
39
+ .addCase(updateMarketData.rejected, (_, action) => {
40
+ console.error(action.error.message);
41
+ });
42
+ }
43
+ });
44
+
45
+ export const marketThunks = { updateMarketData };
46
+ export const marketActions = marketSlice.actions;
47
+ export const marketReducer = marketSlice.reducer;
@@ -1,12 +1,9 @@
1
- import type { PayloadAction } from "@reduxjs/toolkit";
2
- import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
3
- import type { Price } from "../../types";
4
- import { getNexaPrices, initializePrices } from "../../utils";
1
+ import type { PayloadAction } from "@reduxjs/toolkit";
2
+ import { createSlice } from "@reduxjs/toolkit";
5
3
 
6
4
  export interface StatusState {
7
5
  status: "Online" | "Offline";
8
6
  height: number;
9
- price: Record<string, Price>;
10
7
  hasNetwork: boolean;
11
8
  isSuspended: boolean;
12
9
  }
@@ -14,27 +11,10 @@ export interface StatusState {
14
11
  const initialState: StatusState = {
15
12
  status: "Offline",
16
13
  height: 0,
17
- price: initializePrices(),
18
14
  hasNetwork: true,
19
15
  isSuspended: false,
20
16
  };
21
17
 
22
- export const fetchPrice = createAsyncThunk('status/fetchPrice', async () => {
23
- const prices = initializePrices();
24
- try {
25
- const p = await getNexaPrices();
26
- Object.keys(p).forEach(currency => {
27
- prices[currency] = {
28
- value: p[currency],
29
- change: p[`${currency}_24h_change`]
30
- };
31
- });
32
- } catch {
33
- // prices remain at 0
34
- }
35
- return prices ;
36
- });
37
-
38
18
  const statusSlice = createSlice({
39
19
  name: 'status',
40
20
  initialState,
@@ -54,15 +34,6 @@ const statusSlice = createSlice({
54
34
  setIsSuspended: (state, action: PayloadAction<boolean>) => {
55
35
  state.isSuspended = action.payload;
56
36
  }
57
- },
58
- extraReducers: (builder) => {
59
- builder
60
- .addCase(fetchPrice.fulfilled, (state, action) => {
61
- state.price = action.payload;
62
- })
63
- .addCase(fetchPrice.rejected, (_, action) => {
64
- console.error(action.error.message);
65
- });
66
37
  }
67
38
  });
68
39
 
@@ -1,6 +1,6 @@
1
- import type { PayloadAction } from "@reduxjs/toolkit";
1
+ import type { PayloadAction } from "@reduxjs/toolkit";
2
2
  import { createSlice } from "@reduxjs/toolkit";
3
- import type { Account, AssetEntity, Balance, SessionInfo, VaultInfo } from "../../types";
3
+ import type { Account, Balance, SessionInfo, VaultInfo } from "../../types";
4
4
  import { MAIN_WALLET_ID } from "../../utils";
5
5
 
6
6
  interface AddSessionPayload {
@@ -22,6 +22,7 @@ export interface WalletState {
22
22
  initLoad: boolean;
23
23
  txUpdateTrigger: number;
24
24
  nftsUpdateTrigger: number;
25
+ tokensUpdateTrigger: number;
25
26
  }
26
27
 
27
28
  const initialState: WalletState = {
@@ -34,7 +35,6 @@ const initialState: WalletState = {
34
35
  address: '',
35
36
  balance: {confirmed: "0", unconfirmed: "0"},
36
37
  tokensBalance: {},
37
- tokens: [],
38
38
  sessions: {}
39
39
  }
40
40
  },
@@ -42,7 +42,8 @@ const initialState: WalletState = {
42
42
  sync: false,
43
43
  initLoad: true,
44
44
  txUpdateTrigger: 0,
45
- nftsUpdateTrigger: 0
45
+ nftsUpdateTrigger: 0,
46
+ tokensUpdateTrigger: 0
46
47
  }
47
48
 
48
49
  const walletSlice = createSlice({
@@ -77,21 +78,15 @@ const walletSlice = createSlice({
77
78
  setAccountName: (state, action: PayloadAction<{ id: number, name: string }>) => {
78
79
  state.accounts[action.payload.id].name = action.payload.name;
79
80
  },
80
- setTokens: (state, action: PayloadAction<{ id: number, assets: AssetEntity[] }>) => {
81
- state.accounts[action.payload.id].tokens = action.payload.assets;
82
- },
83
- addToken: (state, action: PayloadAction<{ id: number, asset: AssetEntity }>) => {
84
- state.accounts[action.payload.id].tokens.unshift(action.payload.asset);
85
- },
86
- removeToken: (state, action: PayloadAction<{ id: number, tokenId: string }>) => {
87
- state.accounts[action.payload.id].tokens = state.accounts[action.payload.id].tokens.filter(n => n.tokenIdHex !== action.payload.tokenId);
88
- },
89
81
  refreshTxs: (state) => {
90
82
  state.txUpdateTrigger++;
91
83
  },
92
84
  refreshNfts: (state) => {
93
85
  state.nftsUpdateTrigger++;
94
86
  },
87
+ refreshTokens: (state) => {
88
+ state.tokensUpdateTrigger++;
89
+ },
95
90
  addSession(state, action: PayloadAction<AddSessionPayload>) {
96
91
  const { accountId, sessionInfo } = action.payload;
97
92
  state.accounts[accountId].sessions[sessionInfo.details.sessionId] = sessionInfo;
@@ -1,6 +1,7 @@
1
1
  import { authReducer } from "./slices/auth";
2
2
  import { dappModalReducer } from "./slices/dapp";
3
3
  import { loaderReducer } from "./slices/loader";
4
+ import { marketReducer } from "./slices/market";
4
5
  import { notificationsReducer } from "./slices/notifications";
5
6
  import { statusReducer } from "./slices/status";
6
7
  import { walletReducer } from "./slices/wallet";
@@ -12,6 +13,7 @@ export const sharedReducers = {
12
13
  wallet: walletReducer,
13
14
  auth: authReducer,
14
15
  notifications: notificationsReducer,
16
+ market: marketReducer,
15
17
  };
16
18
 
17
19
  export type SharedState = {
@@ -21,4 +23,5 @@ export type SharedState = {
21
23
  wallet: ReturnType<typeof walletReducer>;
22
24
  auth: ReturnType<typeof authReducer>;
23
25
  notifications: ReturnType<typeof notificationsReducer>;
26
+ market: ReturnType<typeof marketReducer>;
24
27
  };
@@ -1,5 +1,5 @@
1
- import type { AccountType, AssetType } from "../utils/enums";
2
- import type { Balance } from "./wallet.types";
1
+ import type { AccountType } from "../utils/enums";
2
+ import type { Balance, AssetType } from "./wallet.types";
3
3
 
4
4
  export interface AssetMovement {
5
5
  address: string;
@@ -43,6 +43,13 @@ export interface AssetTransactionEntity {
43
43
  time: number;
44
44
  }
45
45
 
46
+ export interface AssetEntity {
47
+ accountId: number;
48
+ tokenIdHex: string;
49
+ type: AssetType;
50
+ addedTime: number;
51
+ }
52
+
46
53
  export interface TokenEntity {
47
54
  token: string;
48
55
  tokenIdHex: string;
@@ -50,15 +57,20 @@ export interface TokenEntity {
50
57
  ticker: string;
51
58
  iconUrl: string;
52
59
  decimals: number;
53
- parentGroup: string;
60
+ parent: string;
54
61
  }
55
62
 
56
63
  export interface NftEntity {
57
64
  token: string;
58
65
  tokenIdHex: string;
59
- parentGroup: string;
60
- source: string;
66
+ name: string;
67
+ parent: string;
61
68
  collection: string;
69
+ series: string;
70
+ author: string;
71
+ public: string;
72
+ front: string;
73
+ back: string;
62
74
  }
63
75
 
64
76
  export interface SessionEntity {
@@ -90,13 +102,6 @@ export interface AccountEntity {
90
102
  tokensBalance: string;
91
103
  }
92
104
 
93
- export interface AssetEntity {
94
- accountId: number;
95
- tokenIdHex: string;
96
- type: AssetType;
97
- addedTime: number;
98
- }
99
-
100
105
  export interface AddressDTO {
101
106
  address: string;
102
107
  space: number;
@@ -1,5 +1,4 @@
1
1
  import type { DAppInfo, SessionDetails } from "wallet-comms-sdk";
2
- import type { AssetEntity } from "./db.types";
3
2
  import type { AccountType, KeySpace } from "../utils/enums";
4
3
 
5
4
  export interface KeyPath {
@@ -20,18 +19,12 @@ export interface Balance {
20
19
  unconfirmed: string | number;
21
20
  }
22
21
 
23
- export interface Price {
24
- value: number;
25
- change: number;
26
- }
27
-
28
22
  export interface Account {
29
23
  id: number;
30
24
  name: string;
31
25
  address: string;
32
26
  balance: Balance;
33
27
  tokensBalance: Record<string, Balance>;
34
- tokens: AssetEntity[];
35
28
  sessions: Record<string, SessionInfo>
36
29
  }
37
30
 
@@ -42,21 +35,52 @@ export interface VaultInfo {
42
35
  index: number;
43
36
  }
44
37
 
45
- export interface NftPreview {
46
- infoJson: string;
47
- image: string;
48
- }
49
-
50
38
  export interface SessionInfo {
51
39
  details: SessionDetails;
52
40
  appInfo: DAppInfo;
53
41
  };
54
42
 
55
- export interface AssetInfo {
43
+ export interface AssetBase {
56
44
  token: string;
57
45
  tokenIdHex: string;
46
+ parent: string;
47
+ }
48
+
49
+ export interface TokenAsset {
50
+ name: string;
51
+ ticker: string;
52
+ iconUrl: string;
58
53
  decimals: number;
59
- name?: string;
60
- ticker?: string;
61
- iconUrl?: string;
54
+ }
55
+
56
+ export interface NFTAsset {
57
+ name: string;
58
+ series: string;
59
+ collection: string;
60
+ author: string;
61
+ public: string;
62
+ front: string;
63
+ back: string;
64
+ }
65
+
66
+ export type AssetType = 'token' | 'nft';
67
+
68
+ export type AssetInfo =
69
+ | (AssetBase & { type: 'token'; data: TokenAsset })
70
+ | (AssetBase & { type: 'nft'; data: NFTAsset });
71
+
72
+ export interface MarketData {
73
+ id: string;
74
+ name: string;
75
+ symbol: string;
76
+ image: string;
77
+ price: number;
78
+ priceChange?: number | null;
79
+ priceChangePercentage?: number | null;
80
+ marketCap?: number | null;
81
+ fdv?: number | null;
82
+ circSupply?: number | null;
83
+ totalSupply?: number | null;
84
+ maxSupply?: number | null;
85
+ totalVolume?: number | null;
62
86
  }
@@ -1,53 +1,5 @@
1
- import { BufferUtils, GroupToken } from "libnexa-ts";
2
- import type { TokenEntity } from "../types/db.types";
3
- import { getAddressBuffer, isTestnet } from "./common";
4
-
5
- export function getNiftyToken(): TokenEntity {
6
- return {
7
- name: 'NiftyArt',
8
- ticker: 'NIFTY',
9
- iconUrl: 'https://niftyart.cash/td/niftyicon.svg',
10
- parentGroup: '',
11
- token: isTestnet()
12
- ? 'nexatest:tq8r37lcjlqazz7vuvug84q2ev50573hesrnxkv9y6hvhhl5k5qqqnmyf79mx'
13
- : 'nexa:tr9v70v4s9s6jfwz32ts60zqmmkp50lqv7t0ux620d50xa7dhyqqqcg6kdm6f',
14
- tokenIdHex: isTestnet()
15
- ? '0e38fbf897c1d10bcce33883d40acb28fa7a37cc0733598526aecbdff4b50000'
16
- : 'cacf3d958161a925c28a970d3c40deec1a3fe06796fe1b4a7b68f377cdb90000',
17
- decimals: 0
18
- };
19
- }
20
-
21
- export function fetchNiftyNFT(id: string): Promise<Uint8Array> {
22
- return performGet(getNiftyEndpoint() + id, 'raw');
23
- }
24
-
25
- export function isNiftySubgroup(group: string): boolean {
26
- try {
27
- const addrBuf = getAddressBuffer(group);
28
- if (!GroupToken.isSubgroup(addrBuf)) {
29
- return false;
30
- }
31
-
32
- return BufferUtils.bufferToHex(addrBuf.subarray(0, 32)) === getNiftyToken().tokenIdHex;
33
- } catch {
34
- return false;
35
- }
36
- }
37
-
38
- export function fetchAssetDoc(url: string): Promise<any> {
39
- // it is possible that legacy token description is stored on IPFS, check for this
40
- // example: ipfs://bafkvmicdm2vdjgieqvk3s5ykqlddtzd42yuy7voum4cifpknjvhv3uarwu
41
- url = translateIfIpfsUrl(url);
42
- return performGet(url, 'json');
43
- }
44
-
45
- export function fetchAssetBlob(url: string): Promise<Uint8Array> {
46
- // it is possible that NRC (Token / NFT) description is stored on IPFS, check for this
47
- // example: ipfs://bafkvmicdm2vdjgieqvk3s5ykqlddtzd42yuy7voum4cifpknjvhv3uarwu
48
- url = translateIfIpfsUrl(url);
49
- return performGet(url, 'raw');
50
- }
1
+ import { isTestnet } from "./common";
2
+ import type { AssetInfo } from "../types";
51
3
 
52
4
  export function transformTokenIconUrl(icon: string, documentUrl: string): string {
53
5
  if (icon && typeof icon === 'string') {
@@ -80,8 +32,16 @@ function translateIfIpfsUrl(url: string, gateway = "https://ipfs.nebula.markets/
80
32
  return url;
81
33
  }
82
34
 
83
- function getNiftyEndpoint(): string {
84
- return `https://${isTestnet() ? 'testnet.' : ''}niftyart.cash/_public/`;
35
+ export function getAssetMetadata(token: string): Promise<AssetInfo> {
36
+ return performGet(`${getAssetsEndpoint()}/metadata/${token}`, 'json');
37
+ }
38
+
39
+ export function getAssetFileUrl(token:string, asset: string): string {
40
+ return `${getAssetsEndpoint()}/assets/${token}/${asset}`;
41
+ }
42
+
43
+ function getAssetsEndpoint(): string {
44
+ return `https://${isTestnet() ? 'testapi' : 'api'}.otoplo.com`;
85
45
  }
86
46
 
87
47
  async function performGet(url: string, responseType?: 'json' | 'raw'): Promise<any> {
@@ -170,9 +170,3 @@ export function prettifyAmount(amount: string | number): string {
170
170
  }
171
171
  return val;
172
172
  }
173
-
174
- export function calcAmountValue(amount: string | number, price: number, prettify = false): string {
175
- const value = bigDecimal.multiply(amount, price);
176
- const rounded = bigDecimal.round(value, 2, bigDecimal.RoundingModes.HALF_DOWN);
177
- return prettify ? prettifyAmount(rounded) : rounded;
178
- }
@@ -15,8 +15,3 @@ export enum SessionRequestType {
15
15
  SignTransaction = 'signTransaction',
16
16
  SendTransaction = 'sendTransaction',
17
17
  }
18
-
19
- export enum AssetType {
20
- TOKEN = 'token',
21
- NFT = 'nft'
22
- }
@@ -2,6 +2,6 @@ export * from './asset';
2
2
  export * from './common';
3
3
  export * from './enums';
4
4
  export * from './seed';
5
- export * from './price';
5
+ export * from './market';
6
6
  export * from './vault';
7
7
  export * from './keypath';
@@ -0,0 +1,97 @@
1
+ import bigDecimal from "js-big-decimal";
2
+ import type { MarketData } from "../types/wallet.types";
3
+
4
+ interface MarketDataResponse {
5
+ id: string;
6
+ circulating_supply?: number | null;
7
+ current_price?: number | null;
8
+ fully_diluted_valuation?: number | null;
9
+ image?: string;
10
+ last_updated?: string;
11
+ market_cap?: number | null;
12
+ market_cap_change_24h?: number | null;
13
+ market_cap_change_percentage_24h?: number | null;
14
+ max_supply?: number | null;
15
+ name?: string;
16
+ price_change_24h?: number | null;
17
+ price_change_percentage_24h?: number | null;
18
+ symbol?: string;
19
+ total_supply?: number | null;
20
+ total_volume?: number | null;
21
+ }
22
+
23
+ export const CURRENCIES = [
24
+ { code: 'usd', symbol: '$', name: 'US Dollar' },
25
+ { code: 'eur', symbol: '€', name: 'Euro' },
26
+ { code: 'gbp', symbol: '£', name: 'British Pound' },
27
+ { code: 'aud', symbol: 'A$', name: 'Australian Dollar' },
28
+ { code: 'cad', symbol: 'C$', name: 'Canadian Dollar' },
29
+ { code: 'chf', symbol: 'Fr', name: 'Swiss Franc' },
30
+ { code: 'inr', symbol: '₹', name: 'Indian Rupee' },
31
+ { code: 'cny', symbol: '¥', name: 'Chinese Yuan' },
32
+ { code: 'jpy', symbol: '¥', name: 'Japanese Yen' },
33
+ ] as const;
34
+
35
+ export type CurrencyCode = typeof CURRENCIES[number]['code'];
36
+
37
+ export type CurrencySymbol = typeof CURRENCIES[number]['symbol'];
38
+
39
+ export const currencySymbols = Object.fromEntries(
40
+ CURRENCIES.map(c => [c.code, c.symbol])
41
+ ) as Record<CurrencyCode, CurrencySymbol>;
42
+
43
+ export const currencyCodes = CURRENCIES.map(c => c.code);
44
+
45
+ export const SUPPORTED_TOKENS: Record<string, string> = {
46
+ nexacoin: 'nexa'
47
+ };
48
+
49
+ export async function getMarketData(currency: CurrencyCode): Promise<Record<string, MarketData>> {
50
+ const ids = Object.keys(SUPPORTED_TOKENS).join(",");
51
+ const res = await fetch(`https://api.coingecko.com/api/v3/coins/markets?ids=${ids}&vs_currency=${currency}`);
52
+ if (!res.ok) {
53
+ throw new Error("Failed to fetch market data");
54
+ }
55
+
56
+ const payload: MarketDataResponse[] = await res.json();
57
+ const data: Record<string, MarketData> = {};
58
+ payload.forEach(entry => {
59
+ data[SUPPORTED_TOKENS[entry.id]] = {
60
+ id: entry.id,
61
+ name: entry.name ?? '',
62
+ symbol: entry.symbol ?? '',
63
+ image: entry.image ?? '',
64
+ price: entry.current_price ?? 0,
65
+ priceChange: entry.price_change_24h,
66
+ priceChangePercentage: entry.price_change_percentage_24h,
67
+ marketCap: entry.market_cap,
68
+ fdv: entry.fully_diluted_valuation,
69
+ circSupply: entry.circulating_supply,
70
+ totalSupply: entry.total_supply,
71
+ maxSupply: entry.max_supply,
72
+ totalVolume: entry.total_volume
73
+ }
74
+ });
75
+
76
+ return data;
77
+ }
78
+
79
+ export function getCurrencySymbol(currency: CurrencyCode): string {
80
+ return currencySymbols[currency] || currency;
81
+ }
82
+
83
+
84
+ export function calculateFiatValue(amount: string | number, price: number, currency?: CurrencyCode): string {
85
+ const value = bigDecimal.multiply(amount, price);
86
+ const rounded = bigDecimal.round(value, 2, bigDecimal.RoundingModes.HALF_DOWN);
87
+ return currency ? formatMoney(rounded, currency) : rounded;
88
+ }
89
+
90
+ export function formatMoney(value: string | number, currency: CurrencyCode, locale = 'en-US'): string {
91
+ return new Intl.NumberFormat(locale, {
92
+ style: 'currency',
93
+ currency,
94
+ maximumFractionDigits: 2,
95
+ minimumFractionDigits: 2
96
+ }).format(Number(value));
97
+ }
@@ -1 +0,0 @@
1
- {"version":3,"file":"price.d.ts","sourceRoot":"","sources":["../../../src/utils/price.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEnD,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EASb,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,MAAM,MAAM,cAAc,GAAG,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC;AAEjE,eAAO,MAAM,eAAe,EAEvB,MAAM,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;AAE1C,eAAO,MAAM,aAAa,mEAA8B,CAAC;AAEzD,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAUrE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CAEhE;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAMxD"}