|
20 | 20 | import {
|
21 | 21 | getNativeTokenMetaData,
|
22 | 22 | type INativeToken,
|
| 23 | + type INFT, |
23 | 24 | } from '../../lib/native_token';
|
24 | 25 | import { onDestroy, onMount } from 'svelte';
|
25 | 26 | import { toast } from '@zerodevx/svelte-toast';
|
|
28 | 29 | const state: WithdrawState = {
|
29 | 30 | availableBaseTokens: 0,
|
30 | 31 | availableNativeTokens: [],
|
| 32 | + availableNFTs: [], |
31 | 33 | contract: undefined,
|
32 | 34 | evmChainID: 0,
|
33 | 35 |
|
|
44 | 46 |
|
45 | 47 | $: formattedBalance = (state.availableBaseTokens / 1e6).toFixed(2);
|
46 | 48 | $: formattedAmountToSend = (formInput.baseTokensToSend / 1e6).toFixed(2);
|
47 |
| - $: canSendFunds = |
| 49 | + $: isValidAddress = formInput.receiverAddress.length == Bech32AddressLength; |
| 50 | + $: canWithdraw = |
48 | 51 | state.availableBaseTokens > 0 &&
|
49 | 52 | formInput.baseTokensToSend > 0 &&
|
50 |
| - formInput.receiverAddress.length == Bech32AddressLength; |
51 |
| - $: canSetAmountToSend = state.availableBaseTokens > gasFee + 1; |
| 53 | + isValidAddress; |
| 54 | + $: canWithdrawEverything = isValidAddress; |
| 55 | + $: canSetAmountToWithdraw = state.availableBaseTokens > gasFee + 1; |
52 | 56 | $: state.isMetamaskConnected = window.ethereum
|
53 | 57 | ? window.ethereum.isConnected()
|
54 | 58 | : false;
|
|
89 | 93 |
|
90 | 94 | let parameters = getBalanceParameters(agentID);
|
91 | 95 |
|
92 |
| - const result = await state.contract.methods |
| 96 | + const nativeTokenResult = await state.contract.methods |
93 | 97 | .callView(accountsCoreContract, getBalanceFunc, parameters)
|
94 | 98 | .call();
|
95 | 99 |
|
| 100 | + console.log('nativeToken', nativeTokenResult); |
96 | 101 | const nativeTokens: INativeToken[] = [];
|
97 | 102 |
|
98 |
| - for (let item of result.items) { |
| 103 | + for (let item of nativeTokenResult.items) { |
99 | 104 | const id = item.key;
|
100 | 105 | const idBytes = Converter.hexToBytes(id);
|
101 | 106 |
|
|
122 | 127 | }
|
123 | 128 | }
|
124 | 129 |
|
| 130 | + type Dict = [string, string][]; |
| 131 | +
|
| 132 | + async function pollNFTs() { |
| 133 | + if (!$selectedAccount) { |
| 134 | + return; |
| 135 | + } |
| 136 | +
|
| 137 | + console.log('pollNFT'); |
| 138 | +
|
| 139 | + const accountsCoreContract = hNameFromString('accounts'); |
| 140 | + const getAccountNFTsFunc = hNameFromString('accountNFTs'); |
| 141 | + const agentID = evmAddressToAgentID($selectedAccount); |
| 142 | +
|
| 143 | + let parameters = getBalanceParameters(agentID); |
| 144 | +
|
| 145 | + const NFTsResult = await state.contract.methods |
| 146 | + .callView(accountsCoreContract, getAccountNFTsFunc, parameters) |
| 147 | + .call(); |
| 148 | +
|
| 149 | + const nfts = NFTsResult.items as Dict; |
| 150 | +
|
| 151 | + // The 'i' parameter returns the length of the nft id array, but we can just filter that out |
| 152 | + // and go through the list dynamically. |
| 153 | + const nftIds = nfts.filter(x => Converter.hexToUtf8(x[0]) != 'i'); |
| 154 | +
|
| 155 | + state.availableNFTs = nftIds.map(x => <INFT>{ id: x[1] }); |
| 156 | + } |
| 157 | +
|
125 | 158 | async function pollAccount() {
|
126 |
| - await Promise.all([pollBalance(), pollNativeTokens()]); |
| 159 | + await Promise.all([pollBalance(), pollNativeTokens(), pollNFTs()]); |
127 | 160 | }
|
128 | 161 |
|
129 | 162 | async function subscribeBalance() {
|
|
164 | 197 | state.isLoading = false;
|
165 | 198 | }
|
166 | 199 |
|
167 |
| - async function onWithdrawClick() { |
| 200 | + async function withdraw( |
| 201 | + baseTokens: number, |
| 202 | + nativeTokens: INativeToken[], |
| 203 | + nft: INFT, |
| 204 | + ) { |
168 | 205 | if (!$selectedAccount) {
|
169 | 206 | return;
|
170 | 207 | }
|
171 | 208 |
|
172 |
| - const nativeTokensToSend: INativeToken[] = []; |
173 |
| -
|
174 |
| - for (const tokenID of Object.keys(formInput.nativeTokensToSend)) { |
175 |
| - const amount = formInput.nativeTokensToSend[tokenID]; |
176 |
| -
|
177 |
| - if (amount > 0) { |
178 |
| - nativeTokensToSend.push({ |
179 |
| - // TODO: BigInt is required for native tokens, but it causes problems with the range slider. This needs to be adressed before shipping. |
180 |
| - // In this function the amount is actually of type "number" not bigint, so we lose precision at 53bits which is a problem that needs to be solved. |
181 |
| - amount: BigInt(amount), |
182 |
| - id: tokenID, |
183 |
| - }); |
184 |
| - } |
185 |
| - } |
186 |
| -
|
187 | 209 | let parameters = await withdrawParameters(
|
188 | 210 | $nodeClient,
|
189 | 211 | formInput.receiverAddress,
|
190 | 212 | gasFee,
|
191 |
| - formInput.baseTokensToSend, |
192 |
| - nativeTokensToSend, |
| 213 | + baseTokens, |
| 214 | + nativeTokens, |
| 215 | + nft, |
193 | 216 | );
|
194 | 217 |
|
195 | 218 | let result: any;
|
|
203 | 226 | duration: 8000,
|
204 | 227 | },
|
205 | 228 | );
|
206 |
| -
|
| 229 | + console.log(ex); |
207 | 230 | return;
|
208 | 231 | }
|
209 | 232 |
|
| 233 | + console.log(result); |
| 234 | +
|
210 | 235 | if (result.status) {
|
211 | 236 | toast.push(`Withdraw request sent. BlockIndex: ${result.blockNumber}`, {
|
212 | 237 | duration: 4000,
|
|
220 | 245 | );
|
221 | 246 | }
|
222 | 247 | }
|
| 248 | +
|
| 249 | + async function onWithdrawClick() { |
| 250 | + const nativeTokensToSend: INativeToken[] = []; |
| 251 | +
|
| 252 | + for (const tokenID of Object.keys(formInput.nativeTokensToSend)) { |
| 253 | + const amount = formInput.nativeTokensToSend[tokenID]; |
| 254 | +
|
| 255 | + if (amount > 0) { |
| 256 | + nativeTokensToSend.push({ |
| 257 | + // TODO: BigInt is required for native tokens, but it causes problems with the range slider. This needs to be adressed before shipping. |
| 258 | + // In this function the amount is actually of type "number" not bigint, so we lose precision at 53bits which is a problem that needs to be solved. |
| 259 | + amount: BigInt(amount), |
| 260 | + id: tokenID, |
| 261 | + }); |
| 262 | + } |
| 263 | + } |
| 264 | +
|
| 265 | + await withdraw(formInput.baseTokensToSend, nativeTokensToSend, undefined); |
| 266 | + } |
| 267 | +
|
| 268 | + async function onWithdrawEverythingClick() { |
| 269 | + for (let nft of state.availableNFTs) { |
| 270 | + await pollBalance(); |
| 271 | + await withdraw(900000, [], nft); |
| 272 | + } |
| 273 | +
|
| 274 | + await pollBalance(); |
| 275 | + await withdraw( |
| 276 | + state.availableBaseTokens, |
| 277 | + state.availableNativeTokens, |
| 278 | + null, |
| 279 | + ); |
| 280 | + } |
223 | 281 | </script>
|
224 | 282 |
|
225 | 283 | <component>
|
|
235 | 293 | </div>
|
236 | 294 | <div class="balance_container">
|
237 | 295 | <div>Balance</div>
|
238 |
| - <div class="balance">{formattedBalance}Mi</div> |
| 296 | + <div class="balance">{formattedBalance}</div> |
239 | 297 | </div>
|
240 | 298 | </div>
|
241 | 299 |
|
242 | 300 | <div class="input_container">
|
243 |
| - <span class="header">Receiver address </span> |
| 301 | + <span class="header">Receiver address</span> |
244 | 302 | <input
|
245 | 303 | type="text"
|
246 | 304 | placeholder="L1 address starting with (rms/tst/...)"
|
|
259 | 317 |
|
260 | 318 | <input
|
261 | 319 | type="range"
|
262 |
| - disabled={!canSetAmountToSend} |
| 320 | + disabled={!canSetAmountToWithdraw} |
263 | 321 | min="0"
|
264 | 322 | max={state.availableBaseTokens}
|
265 | 323 | bind:value={formInput.baseTokensToSend}
|
|
285 | 343 | </div>
|
286 | 344 |
|
287 | 345 | <div class="input_container">
|
288 |
| - <button disabled={!canSendFunds} on:click={onWithdrawClick} |
289 |
| - >Withdraw</button |
290 |
| - ><br /> |
| 346 | + <div class="header">NFTs</div> |
| 347 | + |
| 348 | + <div class="token_list"> |
| 349 | + {#each state.availableNFTs as nft} |
| 350 | + <div class="token_list-item"> |
| 351 | + <div class="header"> |
| 352 | + {nft.id} |
| 353 | + </div> |
| 354 | + </div> |
| 355 | + {/each} |
| 356 | + </div> |
| 357 | + </div> |
| 358 | + |
| 359 | + <div class="input_container"> |
| 360 | + <button disabled={!canWithdraw} on:click={onWithdrawClick}> |
| 361 | + Withdraw |
| 362 | + </button> |
| 363 | + </div> |
| 364 | + <div class="input_container"> |
| 365 | + <button |
| 366 | + class="warning" |
| 367 | + disabled={!canWithdrawEverything} |
| 368 | + on:click={onWithdrawEverythingClick} |
| 369 | + > |
| 370 | + Withdraw everything at once |
| 371 | + </button> |
291 | 372 | </div>
|
292 | 373 | {/if}
|
293 | 374 | </component>
|
294 | 375 |
|
295 | 376 | <style>
|
| 377 | + .warning:disabled { |
| 378 | + background-color: #6a1b1e; |
| 379 | + } |
| 380 | +
|
| 381 | + .warning { |
| 382 | + background-color: #b92e34; |
| 383 | + border-color: red; |
| 384 | + color: white; |
| 385 | + } |
| 386 | +
|
296 | 387 | .token_list {
|
297 | 388 | display: flex;
|
298 | 389 | flex-direction: column;
|
|
0 commit comments