Skip to content

Commit 20bf2a5

Browse files
committed
Add sending NFTs
1 parent 31d341f commit 20bf2a5

File tree

4 files changed

+144
-35
lines changed

4 files changed

+144
-35
lines changed

src/components/withdraw/Withdraw.svelte

+122-31
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import {
2121
getNativeTokenMetaData,
2222
type INativeToken,
23+
type INFT,
2324
} from '../../lib/native_token';
2425
import { onDestroy, onMount } from 'svelte';
2526
import { toast } from '@zerodevx/svelte-toast';
@@ -28,6 +29,7 @@
2829
const state: WithdrawState = {
2930
availableBaseTokens: 0,
3031
availableNativeTokens: [],
32+
availableNFTs: [],
3133
contract: undefined,
3234
evmChainID: 0,
3335
@@ -44,11 +46,13 @@
4446
4547
$: formattedBalance = (state.availableBaseTokens / 1e6).toFixed(2);
4648
$: formattedAmountToSend = (formInput.baseTokensToSend / 1e6).toFixed(2);
47-
$: canSendFunds =
49+
$: isValidAddress = formInput.receiverAddress.length == Bech32AddressLength;
50+
$: canWithdraw =
4851
state.availableBaseTokens > 0 &&
4952
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;
5256
$: state.isMetamaskConnected = window.ethereum
5357
? window.ethereum.isConnected()
5458
: false;
@@ -89,13 +93,14 @@
8993
9094
let parameters = getBalanceParameters(agentID);
9195
92-
const result = await state.contract.methods
96+
const nativeTokenResult = await state.contract.methods
9397
.callView(accountsCoreContract, getBalanceFunc, parameters)
9498
.call();
9599
100+
console.log('nativeToken', nativeTokenResult);
96101
const nativeTokens: INativeToken[] = [];
97102
98-
for (let item of result.items) {
103+
for (let item of nativeTokenResult.items) {
99104
const id = item.key;
100105
const idBytes = Converter.hexToBytes(id);
101106
@@ -122,8 +127,36 @@
122127
}
123128
}
124129
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+
125158
async function pollAccount() {
126-
await Promise.all([pollBalance(), pollNativeTokens()]);
159+
await Promise.all([pollBalance(), pollNativeTokens(), pollNFTs()]);
127160
}
128161
129162
async function subscribeBalance() {
@@ -164,32 +197,22 @@
164197
state.isLoading = false;
165198
}
166199
167-
async function onWithdrawClick() {
200+
async function withdraw(
201+
baseTokens: number,
202+
nativeTokens: INativeToken[],
203+
nft: INFT,
204+
) {
168205
if (!$selectedAccount) {
169206
return;
170207
}
171208
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-
187209
let parameters = await withdrawParameters(
188210
$nodeClient,
189211
formInput.receiverAddress,
190212
gasFee,
191-
formInput.baseTokensToSend,
192-
nativeTokensToSend,
213+
baseTokens,
214+
nativeTokens,
215+
nft,
193216
);
194217
195218
let result: any;
@@ -203,10 +226,12 @@
203226
duration: 8000,
204227
},
205228
);
206-
229+
console.log(ex);
207230
return;
208231
}
209232
233+
console.log(result);
234+
210235
if (result.status) {
211236
toast.push(`Withdraw request sent. BlockIndex: ${result.blockNumber}`, {
212237
duration: 4000,
@@ -220,6 +245,39 @@
220245
);
221246
}
222247
}
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+
}
223281
</script>
224282

225283
<component>
@@ -235,12 +293,12 @@
235293
</div>
236294
<div class="balance_container">
237295
<div>Balance</div>
238-
<div class="balance">{formattedBalance}Mi</div>
296+
<div class="balance">{formattedBalance}</div>
239297
</div>
240298
</div>
241299

242300
<div class="input_container">
243-
<span class="header">Receiver address </span>
301+
<span class="header">Receiver address</span>
244302
<input
245303
type="text"
246304
placeholder="L1 address starting with (rms/tst/...)"
@@ -259,7 +317,7 @@
259317

260318
<input
261319
type="range"
262-
disabled={!canSetAmountToSend}
320+
disabled={!canSetAmountToWithdraw}
263321
min="0"
264322
max={state.availableBaseTokens}
265323
bind:value={formInput.baseTokensToSend}
@@ -285,14 +343,47 @@
285343
</div>
286344

287345
<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>
291372
</div>
292373
{/if}
293374
</component>
294375

295376
<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+
296387
.token_list {
297388
display: flex;
298389
flex-direction: column;

src/components/withdraw/component_types.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { SetIntervalAsyncTimer } from 'set-interval-async';
2-
import type { INativeToken } from '../../lib/native_token';
2+
import type { INFT, INativeToken } from '../../lib/native_token';
33
import type { Contract } from 'web3-eth-contract';
44

55
export interface WithdrawState {
@@ -13,6 +13,11 @@ export interface WithdrawState {
1313
*/
1414
availableNativeTokens: INativeToken[];
1515

16+
/**
17+
* The current available NFTs of the user.
18+
*/
19+
availableNFTs: INFT[];
20+
1621
/**
1722
* The reference to the ISC magic contract used for contract invocations.
1823
*/

src/components/withdraw/parameters.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Converter } from '@iota/util.js';
22
import { waspAddrBinaryFromBech32 } from './../../lib/bech32';
33
import type { SingleNodeClient } from '@iota/iota.js';
4-
import type { INativeToken } from '../../lib/native_token';
4+
import type { INFT, INativeToken } from '../../lib/native_token';
55

66
export function getBalanceParameters(agentID: Uint8Array) {
77
return {
@@ -31,6 +31,7 @@ export async function withdrawParameters(
3131
gasFee: number,
3232
baseTokensToWithdraw: number,
3333
nativeTokens: INativeToken[],
34+
nft: INFT,
3435
) {
3536
const binaryAddress = await waspAddrBinaryFromBech32(
3637
nodeClient,
@@ -47,6 +48,11 @@ export async function withdrawParameters(
4748
amount: x.amount,
4849
}));
4950

51+
const nftID = []
52+
if (nft) {
53+
nftID.push(nft.id);
54+
}
55+
5056
const parameters = [
5157
{
5258
// Receiver
@@ -56,7 +62,7 @@ export async function withdrawParameters(
5662
// Fungible Tokens
5763
baseTokens: baseTokensToWithdraw - gasFee,
5864
nativeTokens: nativeTokenTuple,
59-
nfts: [],
65+
nfts: nftID,
6066
},
6167
false,
6268
{
@@ -84,6 +90,6 @@ export async function withdrawParameters(
8490
},
8591
},
8692
];
87-
93+
console.log(parameters)
8894
return parameters;
8995
}

src/lib/native_token.ts

+7
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ export interface INativeToken {
3434
metadata?: INativeTokenIRC30;
3535
}
3636

37+
export interface INFT {
38+
/**
39+
* Identifier of the NFT
40+
*/
41+
id: string;
42+
}
43+
3744
interface INativeTokenMetaDataCacheMap {
3845
[Key: string]: INativeTokenIRC30;
3946
}

0 commit comments

Comments
 (0)