@rainlanguage/ui-components 0.0.1-alpha.142 → 0.0.1-alpha.144

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.
@@ -1,4 +1,4 @@
1
- <script>import { formatUnits, toHex } from "viem";
1
+ <script>import { toHex } from "viem";
2
2
  import Tooltip from "./Tooltip.svelte";
3
3
  export let tokenVault;
4
4
  export let chainId;
@@ -20,7 +20,7 @@ export let orderbookAddress;
20
20
  {tokenVault.token.name} ({tokenVault.token.symbol})
21
21
  </a>
22
22
  <span class="text-sm text-gray-500 dark:text-gray-400">
23
- Balance: {formatUnits(tokenVault.balance, Number(tokenVault.token.decimals) || 18)}
23
+ Balance: {tokenVault.formattedBalance}
24
24
  </span>
25
25
  </div>
26
26
  <div>
@@ -1,5 +1,4 @@
1
1
  <script>import { timestampSecondsToUTCTimestamp } from "../../services/time";
2
- import { bigintToFloat } from "../../utils/number";
3
2
  import { createQuery } from "@tanstack/svelte-query";
4
3
  import TanstackLightweightChartLine from "../charts/TanstackLightweightChartLine.svelte";
5
4
  import { QKEY_VAULT_CHANGES } from "../../queries/keys";
@@ -22,7 +21,7 @@ const Chart = TanstackLightweightChartLine;
22
21
  priceSymbol={vault.token.symbol}
23
22
  {query}
24
23
  timeTransform={(d) => timestampSecondsToUTCTimestamp(BigInt(d.timestamp))}
25
- valueTransform={(d) => bigintToFloat(d.newBalance, Number(vault.token.decimals ?? 0))}
24
+ valueTransform={(d) => parseFloat(d.formattedNewBalance)}
26
25
  emptyMessage="No deposits or withdrawals found"
27
26
  {lightweightChartsTheme}
28
27
  />
@@ -35,6 +35,8 @@ let showAdvancedOptions = false;
35
35
  let allTokenInfos = [];
36
36
  let selectTokens = void 0;
37
37
  let checkingDeployment = false;
38
+ let availableTokens = [];
39
+ let loadingTokens = false;
38
40
  const gui = useGui();
39
41
  const registry = useRegistry();
40
42
  const raindexClient = useRaindexClient();
@@ -46,10 +48,27 @@ onMount(async () => {
46
48
  }
47
49
  selectTokens = selectTokensResult.value;
48
50
  await areAllTokensSelected();
51
+ await loadAvailableTokens();
49
52
  });
50
53
  $: if (selectTokens?.length === 0 || allTokensSelected) {
51
54
  updateFields();
52
55
  }
56
+ async function loadAvailableTokens() {
57
+ if (loadingTokens) return;
58
+ loadingTokens = true;
59
+ try {
60
+ const result = await gui.getAllTokens();
61
+ if (result.error) {
62
+ throw new Error(result.error.msg);
63
+ }
64
+ availableTokens = result.value;
65
+ } catch (error) {
66
+ DeploymentStepsError.catch(error, DeploymentStepsErrorCode.NO_AVAILABLE_TOKENS);
67
+ availableTokens = [];
68
+ } finally {
69
+ loadingTokens = false;
70
+ }
71
+ }
53
72
  function getAllGuiConfig() {
54
73
  try {
55
74
  let result = gui.getAllGuiConfig();
@@ -162,7 +181,7 @@ async function handleDeployButtonClick() {
162
181
  description="Select the tokens that you want to use in your order."
163
182
  />
164
183
  {#each selectTokens as token}
165
- <SelectToken {token} {onSelectTokenSelect} />
184
+ <SelectToken {token} {onSelectTokenSelect} {availableTokens} loading={loadingTokens} />
166
185
  {/each}
167
186
  </div>
168
187
  {/if}
@@ -3,12 +3,19 @@ import { CheckCircleSolid, CloseCircleSolid } from "flowbite-svelte-icons";
3
3
  import { Spinner } from "flowbite-svelte";
4
4
  import { onMount } from "svelte";
5
5
  import { useGui } from "../../hooks/useGui";
6
+ import ButtonSelectOption from "./ButtonSelectOption.svelte";
7
+ import TokenSelectionModal from "./TokenSelectionModal.svelte";
6
8
  export let token;
7
9
  export let onSelectTokenSelect;
10
+ export let availableTokens = [];
11
+ export let loading = false;
8
12
  let inputValue = null;
9
13
  let tokenInfo = null;
10
14
  let error = "";
11
15
  let checking = false;
16
+ let selectionMode = "dropdown";
17
+ let searchQuery = "";
18
+ let selectedToken = null;
12
19
  const gui = useGui();
13
20
  onMount(async () => {
14
21
  try {
@@ -17,12 +24,82 @@ onMount(async () => {
17
24
  throw new Error(result.error.msg);
18
25
  }
19
26
  tokenInfo = result.value;
20
- if (tokenInfo?.address) {
21
- inputValue = tokenInfo.address;
27
+ if (result.value?.address) {
28
+ inputValue = result.value.address;
22
29
  }
23
30
  } catch {
24
31
  }
25
32
  });
33
+ $: if (tokenInfo?.address && availableTokens.length > 0) {
34
+ const foundToken = availableTokens.find(
35
+ (t) => t.address.toLowerCase() === tokenInfo?.address.toLowerCase()
36
+ );
37
+ selectedToken = foundToken || null;
38
+ if (inputValue === null) {
39
+ inputValue = tokenInfo.address;
40
+ }
41
+ if (!foundToken && selectionMode === "dropdown") {
42
+ selectionMode = "custom";
43
+ }
44
+ } else if (tokenInfo?.address && inputValue === null) {
45
+ inputValue = tokenInfo.address;
46
+ }
47
+ function setMode(mode) {
48
+ selectionMode = mode;
49
+ error = "";
50
+ if (mode === "dropdown") {
51
+ searchQuery = "";
52
+ if (inputValue && tokenInfo) {
53
+ const foundToken = availableTokens.find(
54
+ (t) => t.address.toLowerCase() === inputValue?.toLowerCase()
55
+ );
56
+ if (foundToken) {
57
+ selectedToken = foundToken;
58
+ } else {
59
+ inputValue = null;
60
+ tokenInfo = null;
61
+ selectedToken = null;
62
+ clearTokenSelection();
63
+ }
64
+ } else {
65
+ inputValue = null;
66
+ tokenInfo = null;
67
+ selectedToken = null;
68
+ }
69
+ } else if (mode === "custom") {
70
+ selectedToken = null;
71
+ tokenInfo = null;
72
+ inputValue = "";
73
+ error = "";
74
+ clearTokenSelection();
75
+ }
76
+ }
77
+ function handleTokenSelect(token2) {
78
+ selectedToken = token2;
79
+ inputValue = token2.address;
80
+ saveTokenSelection(token2.address);
81
+ }
82
+ function handleSearch(query) {
83
+ searchQuery = query;
84
+ }
85
+ async function saveTokenSelection(address) {
86
+ checking = true;
87
+ error = "";
88
+ try {
89
+ await gui.setSelectToken(token.key, address);
90
+ await getInfoForSelectedToken();
91
+ } catch (e) {
92
+ const errorMessage = e.message || "Invalid token address.";
93
+ error = errorMessage;
94
+ } finally {
95
+ checking = false;
96
+ onSelectTokenSelect();
97
+ }
98
+ }
99
+ function clearTokenSelection() {
100
+ gui.unsetSelectToken(token.key);
101
+ onSelectTokenSelect();
102
+ }
26
103
  async function getInfoForSelectedToken() {
27
104
  error = "";
28
105
  try {
@@ -37,64 +114,105 @@ async function getInfoForSelectedToken() {
37
114
  }
38
115
  }
39
116
  async function handleInput(event) {
40
- tokenInfo = null;
41
117
  const currentTarget = event.currentTarget;
42
118
  if (currentTarget instanceof HTMLInputElement) {
43
119
  inputValue = currentTarget.value;
120
+ if (tokenInfo && tokenInfo.address.toLowerCase() !== inputValue.toLowerCase()) {
121
+ tokenInfo = null;
122
+ selectedToken = null;
123
+ }
44
124
  if (!inputValue) {
45
125
  error = "";
126
+ tokenInfo = null;
127
+ selectedToken = null;
128
+ return;
46
129
  }
47
- checking = true;
48
- try {
49
- await gui.setSelectToken(token.key, currentTarget.value);
50
- await getInfoForSelectedToken();
51
- } catch (e) {
52
- const errorMessage = e.message ? e.message : "Invalid token address.";
53
- error = errorMessage;
54
- }
130
+ saveTokenSelection(inputValue);
55
131
  }
56
- checking = false;
57
- onSelectTokenSelect();
58
132
  }
59
133
  </script>
60
134
 
61
- <div class="flex w-full flex-col">
62
- <div class="flex flex-col gap-2">
63
- <div class="flex flex-col justify-start gap-4 lg:flex-row lg:items-center lg:justify-between">
64
- {#if token.name || token.description}
65
- <div class="flex flex-col">
66
- {#if token.name}
67
- <h1 class="break-words text-xl font-semibold text-gray-900 lg:text-xl dark:text-white">
68
- {token.name}
69
- </h1>
70
- {/if}
71
- {#if token.description}
72
- <p class="text-sm font-light text-gray-600 lg:text-base dark:text-gray-400">
73
- {token.description}
74
- </p>
75
- {/if}
76
- </div>
77
- {/if}
78
- {#if checking}
79
- <div class="flex h-5 flex-row items-center gap-2">
80
- <Spinner class="h-5 w-5" />
81
- <span>Checking...</span>
82
- </div>
83
- {:else if tokenInfo}
84
- <div
85
- class="flex h-5 flex-row items-center gap-2"
86
- data-testid={`select-token-success-${token.key}`}
87
- >
88
- <CheckCircleSolid class="h-5 w-5" color="green" />
89
- <span>{tokenInfo.name}</span>
90
- </div>
91
- {:else if error}
92
- <div class="flex h-5 flex-row items-center gap-2" data-testid="error">
93
- <CloseCircleSolid class="h-5 w-5" color="red" />
94
- <span>{error}</span>
95
- </div>
96
- {/if}
135
+ <div class="token-selection-container flex w-full flex-col gap-4">
136
+ <div class="token-header">
137
+ {#if token.name || token.description}
138
+ <div class="flex flex-col">
139
+ {#if token.name}
140
+ <h1 class="break-words text-xl font-semibold text-gray-900 lg:text-xl dark:text-white">
141
+ {token.name}
142
+ </h1>
143
+ {/if}
144
+ {#if token.description}
145
+ <p class="text-sm font-light text-gray-600 lg:text-base dark:text-gray-400">
146
+ {token.description}
147
+ </p>
148
+ {/if}
149
+ </div>
150
+ {/if}
151
+ </div>
152
+
153
+ {#if availableTokens.length > 0 && !loading}
154
+ <div class="selection-mode flex gap-2">
155
+ <ButtonSelectOption
156
+ active={selectionMode === 'dropdown'}
157
+ buttonText="Select from list"
158
+ clickHandler={() => setMode('dropdown')}
159
+ dataTestId="dropdown-mode-button"
160
+ />
161
+ <ButtonSelectOption
162
+ active={selectionMode === 'custom'}
163
+ buttonText="Custom address"
164
+ clickHandler={() => setMode('custom')}
165
+ dataTestId="custom-mode-button"
166
+ />
167
+ </div>
168
+ {/if}
169
+
170
+ {#if selectionMode === 'dropdown' && availableTokens.length > 0 && !loading}
171
+ <TokenSelectionModal
172
+ tokens={availableTokens}
173
+ {selectedToken}
174
+ onSelect={handleTokenSelect}
175
+ searchValue={searchQuery}
176
+ onSearch={handleSearch}
177
+ />
178
+ {/if}
179
+
180
+ {#if selectionMode === 'custom' || availableTokens.length === 0}
181
+ <div class="custom-input">
182
+ <Input
183
+ type="text"
184
+ size="lg"
185
+ placeholder="Enter token address (0x...)"
186
+ bind:value={inputValue}
187
+ on:input={handleInput}
188
+ />
97
189
  </div>
98
- <Input type="text" size="lg" on:input={handleInput} bind:value={inputValue} />
190
+ {/if}
191
+
192
+ <div class="token-status">
193
+ {#if loading}
194
+ <div class="flex h-5 flex-row items-center gap-2">
195
+ <Spinner class="h-5 w-5" />
196
+ <span>Loading tokens...</span>
197
+ </div>
198
+ {:else if checking}
199
+ <div class="flex h-5 flex-row items-center gap-2">
200
+ <Spinner class="h-5 w-5" />
201
+ <span>Checking...</span>
202
+ </div>
203
+ {:else if tokenInfo}
204
+ <div
205
+ class="flex h-5 flex-row items-center gap-2"
206
+ data-testid={`select-token-success-${token.key}`}
207
+ >
208
+ <CheckCircleSolid class="h-5 w-5" color="green" />
209
+ <span>{tokenInfo.name}</span>
210
+ </div>
211
+ {:else if error}
212
+ <div class="flex h-5 flex-row items-center gap-2" data-testid="error">
213
+ <CloseCircleSolid class="h-5 w-5" color="red" />
214
+ <span>{error}</span>
215
+ </div>
216
+ {/if}
99
217
  </div>
100
218
  </div>
@@ -1,9 +1,11 @@
1
1
  import { SvelteComponent } from "svelte";
2
- import type { GuiSelectTokensCfg } from '@rainlanguage/orderbook';
2
+ import type { GuiSelectTokensCfg, TokenInfo } from '@rainlanguage/orderbook';
3
3
  declare const __propDef: {
4
4
  props: {
5
5
  token: GuiSelectTokensCfg;
6
6
  onSelectTokenSelect: () => void;
7
+ availableTokens?: TokenInfo[];
8
+ loading?: boolean;
7
9
  };
8
10
  events: {
9
11
  [evt: string]: CustomEvent<any>;
@@ -0,0 +1,101 @@
1
+ <script>import { Input, Button, Modal } from "flowbite-svelte";
2
+ import { SearchOutline, CheckCircleSolid, ChevronDownSolid } from "flowbite-svelte-icons";
3
+ export let tokens = [];
4
+ export let selectedToken = null;
5
+ export let onSelect;
6
+ export let searchValue = "";
7
+ export let onSearch;
8
+ let modalOpen = false;
9
+ $: filteredTokens = tokens.filter((token) => {
10
+ if (!searchValue) return true;
11
+ const query = searchValue.toLowerCase();
12
+ return token.name.toLowerCase().includes(query) || token.symbol.toLowerCase().includes(query) || token.address.toLowerCase().includes(query);
13
+ });
14
+ function handleSearch(event) {
15
+ const target = event.target;
16
+ onSearch(target.value);
17
+ }
18
+ function formatAddress(address) {
19
+ return `${address.slice(0, 6)}...${address.slice(-4)}`;
20
+ }
21
+ function handleTokenSelect(token) {
22
+ onSelect(token);
23
+ modalOpen = false;
24
+ }
25
+ $: displayText = selectedToken ? `${selectedToken.name} (${selectedToken.symbol})` : "Select a token...";
26
+ </script>
27
+
28
+ <div class="token-dropdown">
29
+ <div class="relative w-full">
30
+ <Button
31
+ color="alternative"
32
+ class="flex w-full justify-between overflow-hidden overflow-ellipsis pl-4 pr-2 text-left"
33
+ size="lg"
34
+ on:click={() => (modalOpen = true)}
35
+ >
36
+ <div class="flex-grow overflow-hidden">
37
+ <span class="text-gray-900 dark:text-white">{displayText}</span>
38
+ </div>
39
+ <ChevronDownSolid class="ml-2 h-4 w-4 text-black dark:text-white" />
40
+ </Button>
41
+
42
+ <Modal bind:open={modalOpen} size="md" class="w-full max-w-lg">
43
+ <div slot="header" class="flex w-full items-center justify-between">
44
+ <h3 class="text-xl font-medium text-gray-900 dark:text-white">Select a token</h3>
45
+ </div>
46
+ <div class="relative w-full border-b border-gray-200 p-2 dark:border-gray-600">
47
+ <div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-5">
48
+ <SearchOutline class="h-4 w-4 text-gray-500 dark:text-gray-400" />
49
+ </div>
50
+ <Input
51
+ type="text"
52
+ placeholder="Search tokens..."
53
+ bind:value={searchValue}
54
+ on:input={handleSearch}
55
+ class="pl-10"
56
+ />
57
+ </div>
58
+
59
+ <div class="token-list max-h-80 overflow-y-auto">
60
+ {#each filteredTokens as token (token.address)}
61
+ <div
62
+ class="token-item flex cursor-pointer items-center border-b border-gray-100 p-3 last:border-b-0 hover:bg-gray-50 dark:border-gray-600 dark:hover:bg-gray-700"
63
+ class:bg-blue-50={selectedToken?.address === token.address}
64
+ class:dark:bg-blue-900={selectedToken?.address === token.address}
65
+ class:border-l-4={selectedToken?.address === token.address}
66
+ class:border-l-blue-500={selectedToken?.address === token.address}
67
+ on:click={() => handleTokenSelect(token)}
68
+ on:keydown={(e) => e.key === 'Enter' && handleTokenSelect(token)}
69
+ role="button"
70
+ tabindex="0"
71
+ >
72
+ <div class="token-info flex-grow">
73
+ <div class="token-name font-medium text-gray-900 dark:text-white">
74
+ {token.name}
75
+ </div>
76
+ <div class="token-details flex gap-2 text-sm text-gray-500 dark:text-gray-400">
77
+ <span class="symbol font-medium">{token.symbol}</span>
78
+ <span class="address">{formatAddress(token.address)}</span>
79
+ </div>
80
+ </div>
81
+ {#if selectedToken?.address === token.address}
82
+ <CheckCircleSolid class="selected-icon h-5 w-5 text-green-500" />
83
+ {/if}
84
+ </div>
85
+ {/each}
86
+
87
+ {#if filteredTokens.length === 0}
88
+ <div class="no-results p-4 text-center text-gray-500 dark:text-gray-400">
89
+ <p>No tokens found matching your search.</p>
90
+ <button
91
+ class="mt-2 text-blue-600 underline hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300"
92
+ on:click={() => onSearch('')}
93
+ >
94
+ Clear search
95
+ </button>
96
+ </div>
97
+ {/if}
98
+ </div>
99
+ </Modal>
100
+ </div>
101
+ </div>
@@ -0,0 +1,23 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import type { TokenInfo } from '@rainlanguage/orderbook';
3
+ declare const __propDef: {
4
+ props: {
5
+ tokens?: TokenInfo[];
6
+ selectedToken?: TokenInfo | null;
7
+ onSelect: (token: TokenInfo) => void;
8
+ searchValue?: string;
9
+ onSearch: (query: string) => void;
10
+ };
11
+ events: {
12
+ [evt: string]: CustomEvent<any>;
13
+ };
14
+ slots: {};
15
+ exports?: {} | undefined;
16
+ bindings?: string | undefined;
17
+ };
18
+ export type TokenSelectionModalProps = typeof __propDef.props;
19
+ export type TokenSelectionModalEvents = typeof __propDef.events;
20
+ export type TokenSelectionModalSlots = typeof __propDef.slots;
21
+ export default class TokenSelectionModal extends SvelteComponent<TokenSelectionModalProps, TokenSelectionModalEvents, TokenSelectionModalSlots> {
22
+ }
23
+ export {};
@@ -7,7 +7,7 @@ import { QKEY_VAULT } from "../../queries/keys";
7
7
  import {
8
8
  RaindexClient
9
9
  } from "@rainlanguage/orderbook";
10
- import { formatUnits, toHex } from "viem";
10
+ import { toHex } from "viem";
11
11
  import { createQuery } from "@tanstack/svelte-query";
12
12
  import { onDestroy } from "svelte";
13
13
  import { useQueryClient } from "@tanstack/svelte-query";
@@ -119,8 +119,7 @@ const handleRefresh = async () => {
119
119
  <CardProperty data-testid="vaultDetailBalance">
120
120
  <svelte:fragment slot="key">Balance</svelte:fragment>
121
121
  <svelte:fragment slot="value"
122
- >{formatUnits(BigInt(data.balance), Number(data.token.decimals ?? 0))}
123
- {data.token.symbol}</svelte:fragment
122
+ >{`${data.formattedBalance} ${data.token.symbol}`}</svelte:fragment
124
123
  >
125
124
  </CardProperty>
126
125
 
@@ -5,7 +5,6 @@ import { DEFAULT_PAGE_SIZE } from "../../queries/constants";
5
5
  import { TableBodyCell, TableHeadCell } from "flowbite-svelte";
6
6
  import { formatTimestampSecondsAsLocal } from "../../services/time";
7
7
  import Hash, { HashType } from "../Hash.svelte";
8
- import { formatUnits } from "viem";
9
8
  import { BugOutline } from "flowbite-svelte-icons";
10
9
  import TableTimeFilters from "../charts/TableTimeFilters.svelte";
11
10
  export let order;
@@ -80,48 +79,22 @@ $: orderTradesQuery = createInfiniteQuery({
80
79
  <Hash type={HashType.Transaction} value={item.transaction.id} />
81
80
  </TableBodyCell>
82
81
  <TableBodyCell tdClass="break-all py-2">
83
- {formatUnits(
84
- BigInt(item.inputVaultBalanceChange.amount),
85
- Number(item.inputVaultBalanceChange.token.decimals ?? 0)
86
- )}
82
+ {item.inputVaultBalanceChange.formattedAmount}
87
83
  {item.inputVaultBalanceChange.token.symbol}
88
84
  </TableBodyCell>
89
85
  <TableBodyCell tdClass="break-all py-2">
90
- {formatUnits(
91
- BigInt(item.outputVaultBalanceChange.amount) * BigInt(-1),
92
- Number(item.outputVaultBalanceChange.token.decimals ?? 0)
93
- )}
86
+ {item.outputVaultBalanceChange.formattedAmount}
94
87
  {item.outputVaultBalanceChange.token.symbol}
95
88
  </TableBodyCell>
96
89
  <TableBodyCell tdClass="break-all py-2" data-testid="io-ratio">
97
90
  {Math.abs(
98
- Number(
99
- formatUnits(
100
- BigInt(item.inputVaultBalanceChange.amount),
101
- Number(item.inputVaultBalanceChange.token.decimals ?? 0)
102
- )
103
- ) /
104
- Number(
105
- formatUnits(
106
- BigInt(item.outputVaultBalanceChange.amount),
107
- Number(item.outputVaultBalanceChange.token.decimals ?? 0)
108
- )
109
- )
91
+ Number(item.inputVaultBalanceChange.formattedAmount) /
92
+ Number(item.outputVaultBalanceChange.formattedAmount)
110
93
  )}
111
94
  <span class="text-gray-400">
112
95
  ({Math.abs(
113
- Number(
114
- formatUnits(
115
- BigInt(item.outputVaultBalanceChange.amount),
116
- Number(item.outputVaultBalanceChange.token.decimals ?? 0)
117
- )
118
- ) /
119
- Number(
120
- formatUnits(
121
- BigInt(item.inputVaultBalanceChange.amount),
122
- Number(item.inputVaultBalanceChange.token.decimals ?? 0)
123
- )
124
- )
96
+ Number(item.outputVaultBalanceChange.formattedAmount) /
97
+ Number(item.inputVaultBalanceChange.formattedAmount)
125
98
  )})
126
99
  </span>
127
100
  </TableBodyCell>
@@ -1,12 +1,10 @@
1
1
  <script>import { createInfiniteQuery } from "@tanstack/svelte-query";
2
2
  import TanstackAppTable from "../TanstackAppTable.svelte";
3
3
  import { QKEY_VAULTS_VOL_LIST } from "../../queries/keys";
4
- import {} from "@rainlanguage/orderbook";
4
+ import { RaindexVaultVolume } from "@rainlanguage/orderbook";
5
5
  import { TableBodyCell, TableHeadCell } from "flowbite-svelte";
6
6
  import Hash, { HashType } from "../Hash.svelte";
7
- import { formatUnits } from "viem";
8
7
  import TableTimeFilters from "../charts/TableTimeFilters.svelte";
9
- import { bigintStringToHex } from "../../utils/hex";
10
8
  export let order;
11
9
  let startTimestamp;
12
10
  let endTimestamp;
@@ -44,7 +42,7 @@ $: vaultsVol = createInfiniteQuery({
44
42
 
45
43
  <svelte:fragment slot="bodyRow" let:item>
46
44
  <TableBodyCell tdClass="px-4 py-2">
47
- <Hash type={HashType.Identifier} shorten value={bigintStringToHex(item.id)} />
45
+ <Hash type={HashType.Identifier} shorten value={item.id} />
48
46
  </TableBodyCell>
49
47
  <TableBodyCell tdClass="break-all py-2 min-w-32">
50
48
  <div class="flex gap-x-3">
@@ -53,17 +51,16 @@ $: vaultsVol = createInfiniteQuery({
53
51
  </div>
54
52
  </TableBodyCell>
55
53
  <TableBodyCell tdClass="break-all py-2 min-w-32" data-testid="total-in">
56
- {formatUnits(BigInt(item.volDetails.totalIn), Number(item.token.decimals ?? 0))}
54
+ {item.details.formattedTotalIn}
57
55
  </TableBodyCell>
58
56
  <TableBodyCell tdClass="break-all py-2" data-testid="total-out">
59
- {formatUnits(BigInt(item.volDetails.totalOut), Number(item.token.decimals ?? 0))}
57
+ {item.details.formattedTotalOut}
60
58
  </TableBodyCell>
61
59
  <TableBodyCell tdClass="break-all py-2" data-testid="net-vol">
62
- {(BigInt(item.volDetails.totalIn) >= BigInt(item.volDetails.totalOut) ? '' : '-') +
63
- formatUnits(BigInt(item.volDetails.netVol), Number(item.token.decimals ?? 0))}
60
+ {(item.details.totalIn >= item.details.totalOut ? '' : '-') + item.details.formattedNetVol}
64
61
  </TableBodyCell>
65
62
  <TableBodyCell tdClass="break-all py-2" data-testid="total-vol">
66
- {formatUnits(BigInt(item.volDetails.totalVol), Number(item.token.decimals ?? 0))}
63
+ {item.details.formattedTotalVol}
67
64
  </TableBodyCell>
68
65
  </svelte:fragment>
69
66
  </TanstackAppTable>
@@ -1,5 +1,4 @@
1
1
  <script generics="T">import { Heading, TableHeadCell, TableBodyCell } from "flowbite-svelte";
2
- import { formatUnits } from "viem";
3
2
  import { createInfiniteQuery } from "@tanstack/svelte-query";
4
3
  import { RaindexVault } from "@rainlanguage/orderbook";
5
4
  import { formatTimestampSecondsAsLocal } from "../../services/time";
@@ -55,12 +54,10 @@ const AppTable = TanstackAppTable;
55
54
  tdClass="break-word p-0 text-left"
56
55
  data-testid="vaultBalanceChangesTableBalanceChange"
57
56
  >
58
- {formatUnits(BigInt(item.amount), Number(item.token.decimals ?? 0))}
59
- {item.token.symbol}
57
+ {`${item.formattedAmount} ${item.token.symbol}`}
60
58
  </TableBodyCell>
61
59
  <TableBodyCell tdClass="break-word p-0 text-left" data-testid="vaultBalanceChangesTableBalance">
62
- {formatUnits(item.newBalance, Number(item.token.decimals ?? 0))}
63
- {item.token.symbol}
60
+ {`${item.formattedNewBalance} ${item.token.symbol}`}
64
61
  </TableBodyCell>
65
62
  <TableBodyCell tdClass="break-word p-0 text-left" data-testid="vaultBalanceChangesTableType">
66
63
  {item.type}
@@ -9,7 +9,6 @@ import ListViewOrderbookFilters from "../ListViewOrderbookFilters.svelte";
9
9
  import OrderOrVaultHash from "../OrderOrVaultHash.svelte";
10
10
  import Hash, { HashType } from "../Hash.svelte";
11
11
  import { DEFAULT_PAGE_SIZE, DEFAULT_REFRESH_INTERVAL } from "../../queries/constants";
12
- import { vaultBalanceDisplay } from "../../utils/vault";
13
12
  import { RaindexVault } from "@rainlanguage/orderbook";
14
13
  import { QKEY_TOKENS, QKEY_VAULTS } from "../../queries/keys";
15
14
  import { useAccount } from "../../providers/wallet/useAccount";
@@ -130,8 +129,7 @@ const AppTable = TanstackAppTable;
130
129
  >{item.token.name}</TableBodyCell
131
130
  >
132
131
  <TableBodyCell tdClass="break-all p-2 min-w-48" data-testid="vault-balance">
133
- {vaultBalanceDisplay(item)}
134
- {item.token.symbol}
132
+ {`${item.formattedBalance} ${item.token.symbol}`}
135
133
  </TableBodyCell>
136
134
  <TableBodyCell tdClass="break-all p-2 min-w-48">
137
135
  {#if item.ordersAsInput.length > 0}
@@ -11,6 +11,7 @@ export declare enum DeploymentStepsErrorCode {
11
11
  NO_GUI_DETAILS = "Error getting GUI details",
12
12
  NO_CHAIN = "Unsupported chain ID",
13
13
  NO_NETWORK_KEY = "No network key found",
14
+ NO_AVAILABLE_TOKENS = "Error loading available tokens",
14
15
  SERIALIZE_ERROR = "Error serializing state",
15
16
  ADD_ORDER_FAILED = "Failed to add order",
16
17
  NO_WALLET = "No account address found",
@@ -13,6 +13,7 @@ export var DeploymentStepsErrorCode;
13
13
  DeploymentStepsErrorCode["NO_GUI_DETAILS"] = "Error getting GUI details";
14
14
  DeploymentStepsErrorCode["NO_CHAIN"] = "Unsupported chain ID";
15
15
  DeploymentStepsErrorCode["NO_NETWORK_KEY"] = "No network key found";
16
+ DeploymentStepsErrorCode["NO_AVAILABLE_TOKENS"] = "Error loading available tokens";
16
17
  DeploymentStepsErrorCode["SERIALIZE_ERROR"] = "Error serializing state";
17
18
  DeploymentStepsErrorCode["ADD_ORDER_FAILED"] = "Failed to add order";
18
19
  DeploymentStepsErrorCode["NO_WALLET"] = "No account address found";
package/dist/index.d.ts CHANGED
@@ -75,8 +75,6 @@ export type { ToastProps } from './types/toast';
75
75
  export { createResolvableQuery, createResolvableInfiniteQuery } from './__mocks__/queries';
76
76
  export { formatTimestampSecondsAsLocal, timestampSecondsToUTCTimestamp, promiseTimeout } from './services/time';
77
77
  export { bigintStringToHex, HEX_INPUT_REGEX } from './utils/hex';
78
- export { vaultBalanceDisplay } from './utils/vault';
79
- export { bigintToFloat } from './utils/number';
80
78
  export { getExplorerLink } from './services/getExplorerLink';
81
79
  export { invalidateTanstackQueries } from './queries/queryClient';
82
80
  export { getToastsContext } from './providers/toasts/context';
package/dist/index.js CHANGED
@@ -72,8 +72,6 @@ export { TransactionStatusMessage, TransactionStoreErrorMessage } from './types/
72
72
  export { createResolvableQuery, createResolvableInfiniteQuery } from './__mocks__/queries';
73
73
  export { formatTimestampSecondsAsLocal, timestampSecondsToUTCTimestamp, promiseTimeout } from './services/time';
74
74
  export { bigintStringToHex, HEX_INPUT_REGEX } from './utils/hex';
75
- export { vaultBalanceDisplay } from './utils/vault';
76
- export { bigintToFloat } from './utils/number';
77
75
  export { getExplorerLink } from './services/getExplorerLink';
78
76
  export { invalidateTanstackQueries } from './queries/queryClient';
79
77
  export { getToastsContext } from './providers/toasts/context';
@@ -5,10 +5,3 @@
5
5
  * @param decimalPoint - (optional) The number of digits to keep after "." in final result, defaults to valueDecimals
6
6
  */
7
7
  export declare function bigintStringToPercentage(value: string, valueDecimals: number, finalDecimalsDigits?: number): string;
8
- /**
9
- * Converts a bigint value to a floating point number with the specified number of decimals
10
- * @param value - The bigint value to convert
11
- * @param decimals - The number of decimal places to use in the conversion
12
- * @returns The converted floating point number
13
- */
14
- export declare function bigintToFloat(value: bigint, decimals: number): number;
@@ -14,30 +14,3 @@ export function bigintStringToPercentage(value, valueDecimals, finalDecimalsDigi
14
14
  }
15
15
  return valueString;
16
16
  }
17
- /**
18
- * Converts a bigint value to a floating point number with the specified number of decimals
19
- * @param value - The bigint value to convert
20
- * @param decimals - The number of decimal places to use in the conversion
21
- * @returns The converted floating point number
22
- */
23
- export function bigintToFloat(value, decimals) {
24
- return parseFloat(formatUnits(value, decimals));
25
- }
26
- if (import.meta.vitest) {
27
- const { it, expect } = import.meta.vitest;
28
- it('should get percentage string from bigint string', () => {
29
- const value = '123456000000000000';
30
- const decimals = 18;
31
- const finalDecimalsDigits = 4;
32
- const result = bigintStringToPercentage(value, decimals, finalDecimalsDigits);
33
- const expected = '12.3456';
34
- expect(result).toEqual(expected);
35
- });
36
- it('should convert bigint to float', () => {
37
- const value = 123456000000000000n;
38
- const decimals = 18;
39
- const result = bigintToFloat(value, decimals);
40
- const expected = 0.123456;
41
- expect(result).toEqual(expected);
42
- });
43
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rainlanguage/ui-components",
3
- "version": "0.0.1-alpha.142",
3
+ "version": "0.0.1-alpha.144",
4
4
  "description": "A component library for building Svelte applications to be used with Raindex.",
5
5
  "license": "LicenseRef-DCL-1.0",
6
6
  "author": "Rain Open Source Software Ltd",
@@ -53,7 +53,7 @@
53
53
  "@fontsource/dm-sans": "5.1.0",
54
54
  "@imask/svelte": "7.6.1",
55
55
  "@observablehq/plot": "0.6.16",
56
- "@rainlanguage/orderbook": "0.0.1-alpha.142",
56
+ "@rainlanguage/orderbook": "0.0.1-alpha.144",
57
57
  "@reown/appkit": "1.6.4",
58
58
  "@reown/appkit-adapter-wagmi": "1.6.4",
59
59
  "@sentry/sveltekit": "7.120.0",
@@ -1,2 +0,0 @@
1
- import type { RaindexVault } from '@rainlanguage/orderbook';
2
- export declare const vaultBalanceDisplay: (vault: RaindexVault) => string;
@@ -1,29 +0,0 @@
1
- import { formatUnits } from 'viem';
2
- export const vaultBalanceDisplay = (vault) => {
3
- return formatUnits(BigInt(vault.balance), Number(vault.token?.decimals || 0));
4
- };
5
- if (import.meta.vitest) {
6
- const { it, expect } = import.meta.vitest;
7
- it('formats the vault balance correctly', () => {
8
- const vault = {
9
- id: '1',
10
- balance: '1000000000000000000',
11
- token: {
12
- id: '1',
13
- decimals: '18',
14
- address: '0x00'
15
- },
16
- vaultId: '1',
17
- owner: '0x00',
18
- ordersAsInput: [],
19
- ordersAsOutput: [],
20
- balanceChanges: [],
21
- orderbook: {
22
- id: '0x00'
23
- }
24
- };
25
- expect(vaultBalanceDisplay(vault)).toEqual('1');
26
- vault.token.decimals = '6';
27
- expect(vaultBalanceDisplay(vault)).toEqual('1000000000000');
28
- });
29
- }