|
3 | 3 | import { onMount } from 'svelte' |
4 | 4 | import { createForm } from "svelte-forms-lib"; |
5 | 5 | import { Contract, Web3 } from 'web3' |
6 | | - import { serviceInstanceABI, tokenContractABI } from "$lib/contracts.js" |
| 6 | + import { serviceInstanceABI, tokenContractABI, token_symbol, token_decimals, calculate_user_balance, calculate_provider_balance } from "$lib/contracts.js" |
7 | 7 | import { WalletInformation, reset_warning, get_wallet_addr, wallet_logout } from '$lib/wallet' |
8 | 8 | import { number } from "zod"; |
9 | 9 |
|
|
14 | 14 | onMount ( async () => { |
15 | 15 | if (window.ethereum) { |
16 | 16 | window.web3 = new Web3(window.ethereum); |
| 17 | + local_get_wallet_addr({target: undefined}) |
17 | 18 | } else { |
18 | 19 | wallet.warning = "no web3 wallet attached!" |
19 | 20 | } |
20 | 21 | }) |
21 | 22 |
|
| 23 | + $: has_wallet = !!wallet.walletaddr |
| 24 | +
|
22 | 25 | const is_provider = $page.data.session?.user?.role == "Provider" |
23 | 26 |
|
24 | 27 | async function local_get_wallet_addr(ev) { |
|
31 | 34 | reset_warning(wallet); |
32 | 35 | } |
33 | 36 |
|
| 37 | + const locale = 'en' // better get this from the browser |
| 38 | + const options: Intl.DateTimeFormatOptions = { |
| 39 | + weekday: undefined, |
| 40 | + year: 'numeric', |
| 41 | + month: 'numeric', |
| 42 | + day: 'numeric', |
| 43 | + hour: '2-digit', |
| 44 | + minute: '2-digit', |
| 45 | + second: '2-digit' |
| 46 | + } |
| 47 | + const date_formatter = new Intl.DateTimeFormat(locale, options) |
| 48 | +
|
34 | 49 | let details: { dayPrice: number; deposit: number; retracted: number; startTime: number; endTime: number; userAddress: string; providerAddress: string; tokenAddress: string } | undefined = undefined |
35 | 50 | async function read_contract(contractAddress: string) { |
36 | 51 | if (window.web3 && wallet.walletaddr && contractAddress) { |
|
55 | 70 | return this.toString(); |
56 | 71 | }; |
57 | 72 |
|
| 73 | + let contractevents = undefined //: { event: string, blockNumber: string, blockHash: string, transactionHash: string, address: string, returnValues: Record<string,string>, topics: string[] }[] | undefined = undefined |
| 74 | + async function list_events() { |
| 75 | + if (window.web3 && wallet.walletaddr !== undefined && data !== undefined && data.addr) { |
| 76 | + let contract: Contract<typeof serviceInstanceABI> = new window.web3.eth.Contract(serviceInstanceABI, data.addr) |
| 77 | + contract.getPastEvents('ALLEVENTS', { fromBlock: 0, toBlock: 'latest'}).then(function (events) { |
| 78 | + if (events.length) { |
| 79 | + contractevents = events |
| 80 | + // console.log(JSON.stringify(events,null,2)) |
| 81 | + } |
| 82 | + }) |
| 83 | + } else { |
| 84 | + contractevents = undefined |
| 85 | + } |
| 86 | + } |
| 87 | + async function get_transaction(txhash: string) { |
| 88 | + if (window.web3 && txhash) { |
| 89 | + window.web3.eth.getTransaction(txhash).then((tx) => console.log(JSON.stringify(tx,null,2))) |
| 90 | + } |
| 91 | + } |
| 92 | + async function get_block_time(blockHash: string): Promise<bigint> { |
| 93 | + if (window.web3 && blockHash) { |
| 94 | + // console.log(`get_block_time ${blockHash}`) |
| 95 | + const bdata = await window.web3.eth.getBlock(blockHash, false) |
| 96 | + if (bdata && bdata.timestamp) { |
| 97 | + // console.log(`block data: ${JSON.stringify(bdata,null,2)}`) |
| 98 | + return BigInt(bdata.timestamp) |
| 99 | + } |
| 100 | + return 0n |
| 101 | + } |
| 102 | + return 0n |
| 103 | + } |
58 | 104 | async function withdraw_user(amount: number, useGas: number) { |
59 | 105 | if (window.web3 && wallet.walletaddr !== undefined && data !== undefined && data.addr) { |
60 | 106 | const contract = new window.web3.eth.Contract(serviceInstanceABI, data.addr); |
61 | 107 | contract.setConfig({ "defaultNetworkId": wallet.walletnetwork }); |
62 | 108 | try { |
63 | | - const one_token: number = 10**18; |
| 109 | + const one_token: number = 10**(token_decimals); |
64 | 110 | const gasPrice = await window.web3.eth.getGasPrice(); |
65 | 111 | let estimatedGas = useGas; |
66 | 112 | if (wallet.walletnetwork === "0x89") { // Polygon |
|
87 | 133 | const contract = new window.web3.eth.Contract(serviceInstanceABI, data.addr); |
88 | 134 | contract.setConfig({ "defaultNetworkId": wallet.walletnetwork }); |
89 | 135 | try { |
90 | | - const one_token: number = 10**18; |
| 136 | + const one_token: number = 10**(token_decimals); |
91 | 137 | const gasPrice = await window.web3.eth.getGasPrice(); |
92 | 138 | let estimatedGas = useGas; |
93 | 139 | if (wallet.walletnetwork === "0x89") { // Polygon |
|
187 | 233 | <div class="w3-container w3-padding-32"> |
188 | 234 |
|
189 | 235 | {#if $page.data.session} |
| 236 | + <span id="details"></span> |
| 237 | + <p> </p> |
| 238 | + |
| 239 | + <h2 class="{is_provider ? 'w3-green' : 'w3-gray'}">Service Instance</h2> |
| 240 | + |
| 241 | + <h3>Details</h3> |
190 | 242 |
|
191 | | - <h2 class="{is_provider ? 'w3-green' : 'w3-gray'}">Service Instance - {data.addr}</h2> |
| 243 | + <div class="w3-bar w3-theme"> |
| 244 | + <a href="#details" class="w3-bar-item w3-button w3-hover-white">Details</a> |
| 245 | + <a href="#contract" class="w3-bar-item w3-button {has_wallet ? "w3-hover-white" : 'w3-disabled'}">Contract</a> |
| 246 | + <a href="#events" class="w3-bar-item w3-button {has_wallet ? "w3-hover-white" : 'w3-disabled'}">Events</a> |
| 247 | + <a href="#transactions" class="w3-bar-item w3-button {has_wallet ? "w3-hover-white" : 'w3-disabled'}">Transactions</a> |
| 248 | + </div> |
| 249 | + |
| 250 | + <section class="addr-section"> |
| 251 | + <ul> |
| 252 | + <li>Contract address: {data.addr}</li> |
| 253 | + {#if wallet.walletnetwork === "0x89"} |
| 254 | + <li>PolygonScan: <a href={"https://polygonscan.com/address/"+data.addr}>view</a></li> |
| 255 | + {:else if wallet.walletnetwork === "0x80002"} |
| 256 | + <li>PolygonScan: <a href={"https://polygonscan.com/address/"+data.addr}>view</a></li> |
| 257 | + {/if} |
| 258 | + </ul> |
| 259 | + </section> |
192 | 260 |
|
193 | 261 | <section class="login-section"> |
194 | | - {#if wallet.walletaddr == undefined} |
| 262 | + {#if !has_wallet} |
195 | 263 | <p><button type="button" class="login-btn" on:click={ (ev) => local_get_wallet_addr(ev) }>🔓 Log in with Web3</button></p> |
196 | 264 | <span class="instruction"> |
197 | 265 | Ensure to have an Ethereum based wallet installed i.e MetaMask. Change the network and account in the wallet and |
198 | 266 | click the button again to update the app's state. |
199 | 267 | </span> |
200 | 268 | {:else} |
201 | | - <p><button type="button" class="logout-btn" on:click={ () => local_wallet_logout() }>🔐 Log out</button></p> |
| 269 | + <p>connected: |
202 | 270 | <span> network: {wallet.walletnetwork} </span> |
203 | 271 | <span> address: {wallet.walletaddr} </span> |
| 272 | + <button type="button" class="logout-btn" on:click={ () => local_wallet_logout() }>🔐 Log out</button></p> |
204 | 273 | {/if} |
205 | 274 | </section> |
| 275 | + |
| 276 | + <p><br/></p> |
| 277 | + <p><br/></p> |
| 278 | + <p><br/></p> |
| 279 | + <p><br/></p> |
| 280 | + <p><br/></p> |
| 281 | + <p><br/></p> |
| 282 | + <p><br/></p> |
| 283 | + <p><br/></p> |
| 284 | + <span id="contract"></span> |
| 285 | + <p> </p> |
| 286 | + |
| 287 | + <h2 class="{is_provider ? 'w3-green' : 'w3-gray'}">Service Instance</h2> |
| 288 | + |
| 289 | + <h3>Contract</h3> |
206 | 290 |
|
207 | | - <button type="button" on:click={() => read_contract(data.addr)}>show</button> |
| 291 | + <div class="w3-bar w3-theme"> |
| 292 | + <a href="#details" class="w3-bar-item w3-button w3-hover-white">Details</a> |
| 293 | + <a href="#contract" class="w3-bar-item w3-button {has_wallet ? "w3-hover-white" : 'w3-disabled'}">Contract</a> |
| 294 | + <a href="#events" class="w3-bar-item w3-button {has_wallet ? "w3-hover-white" : 'w3-disabled'}">Events</a> |
| 295 | + <a href="#transactions" class="w3-bar-item w3-button {has_wallet ? "w3-hover-white" : 'w3-disabled'}">Transactions</a> |
| 296 | + </div> |
| 297 | + |
| 298 | + <div> |
| 299 | + <p><button type="button" class="{has_wallet ? "w3-theme" : 'w3-disabled'}" on:click={() => has_wallet && read_contract(data.addr)}><i class="fa fa-refresh"></i></button></p> |
208 | 300 |
|
209 | 301 | {#if details !== undefined && wallet.walletaddr !== undefined} |
210 | | - <p>daily price: {details.dayPrice / 10**18}</p> |
211 | | - <p>user deposit: {details.deposit / 10**18}</p> |
212 | | - <p>retracted: {details.retracted / 10**18}</p> |
213 | | - <p>start time: {details.startTime}</p> |
214 | | - <p>end time: {details.endTime}</p> |
215 | | - <p>provider address: {details.providerAddress}</p> |
216 | | - <p>user address: {details.userAddress}</p> |
| 302 | + <ul> |
| 303 | + <li>daily price: {details.dayPrice / 10**(token_decimals)} {token_symbol}</li> |
| 304 | + <li>user deposit: {details.deposit / 10**(token_decimals)} {token_symbol}</li> |
| 305 | + <li>retracted: {details.retracted / 10**(token_decimals)} {token_symbol}</li> |
| 306 | + <li>start time: { details.startTime > 0 ? date_formatter.format(details.startTime * 1000) : '' }</li> |
| 307 | + <li>end time: {details.endTime > 0 ? date_formatter.format(details.endTime * 1000) : (details.startTime > 0 ? "(" + date_formatter.format(details.startTime * 1000 + (details.deposit * 1000 * 24 * 3600 / details.dayPrice)) + ") estimated" : '') }</li> |
| 308 | + <li>provider address: {details.providerAddress}</li> |
| 309 | + <li>user address: {details.userAddress}</li> |
| 310 | + <li>estimated user balance: { calculate_user_balance(details.deposit, details.startTime, details.dayPrice) / 10**(token_decimals)} {token_symbol}</li> |
| 311 | + <li>estimated provider balance: { calculate_provider_balance(details.deposit, details.retracted, details.startTime, details.dayPrice) / 10**(token_decimals)} {token_symbol}</li> |
| 312 | + </ul> |
217 | 313 | {/if} |
218 | 314 |
|
219 | 315 | <section class="withdrawal"> |
220 | 316 | <h3>Withdrawal from contract</h3> |
221 | 317 | <form on:submit={w_handleSubmit}> |
222 | | - <label for="amount">amount:</label> |
| 318 | + <label for="amount">amount {token_symbol}:</label> |
223 | 319 | <input |
224 | 320 | id="amount" |
225 | 321 | name="amount" |
|
262 | 358 | </form> |
263 | 359 | </section> |
264 | 360 | {/if} |
| 361 | + </div> |
| 362 | + |
| 363 | + <p><br/></p> |
| 364 | + <p><br/></p> |
| 365 | + <p><br/></p> |
| 366 | + <p><br/></p> |
| 367 | + <p><br/></p> |
| 368 | + <p><br/></p> |
| 369 | + <p><br/></p> |
| 370 | + <p><br/></p> |
| 371 | + <span id="events"></span> |
| 372 | + <p> </p> |
| 373 | + |
| 374 | + <h2 class="{is_provider ? 'w3-green' : 'w3-gray'}">Service Instance</h2> |
| 375 | + <h3>Events</h3> |
| 376 | + |
| 377 | + <div class="w3-bar w3-theme"> |
| 378 | + <a href="#details" class="w3-bar-item w3-button w3-hover-white">Details</a> |
| 379 | + <a href="#contract" class="w3-bar-item w3-button {has_wallet ? "w3-hover-white" : 'w3-disabled'}">Contract</a> |
| 380 | + <a href="#events" class="w3-bar-item w3-button {has_wallet ? "w3-hover-white" : 'w3-disabled'}">Events</a> |
| 381 | + <a href="#transactions" class="w3-bar-item w3-button {has_wallet ? "w3-hover-white" : 'w3-disabled'}">Transactions</a> |
| 382 | + </div> |
| 383 | + |
| 384 | + <div> |
| 385 | + <p><button type="button" class="{has_wallet ? "w3-theme" : 'w3-disabled'}" on:click={() => has_wallet && list_events()}><i class="fa fa-refresh"></i></button></p> |
| 386 | + |
| 387 | + {#if contractevents && contractevents.length} |
| 388 | + {#each contractevents as cev} |
| 389 | + <h4>{cev.event ? cev.event : "some event"}</h4> |
| 390 | + <table class="w3-table w3-bordered"> |
| 391 | + <tr><td>Timestamp:</td><td> |
| 392 | + {#await get_block_time(cev.blockHash) then timestamp} |
| 393 | + {date_formatter.format(Number(timestamp * 1000n))} |
| 394 | + {:catch} |
| 395 | + <i class="fa fa-thumbs-down"></i> error |
| 396 | + {/await} |
| 397 | + </td></tr> |
| 398 | + <tr><td>Block:</td><td>#{cev.blockNumber ? cev.blockNumber : "#?"} {cev.blockHash ? cev.blockHash : "0x.."}</td></tr> |
| 399 | + <tr><td>Transaction:</td><td> |
| 400 | + {#if cev.transactionHash} |
| 401 | + <button type="button" class="w3-btn" on:click={() => has_wallet && get_transaction(cev.transactionHash)}>{cev.transactionHash}</button> |
| 402 | + {:else} |
| 403 | + "no transaction info" |
| 404 | + {/if} |
| 405 | + </td></tr> |
| 406 | + <tr><td>Address:</td><td>{cev.address ? cev.address : "0x.."}</td></tr> |
| 407 | + <tr><td>Topics:</td><td>{cev.topics ? JSON.stringify(cev.topics,null,2) : "[ ]"}</td></tr> |
| 408 | + <tr><td>Values:</td><td>{cev.returnValues ? JSON.stringify(cev.returnValues,null,2) : "{ }"}</td></tr> |
| 409 | + </table> |
| 410 | + {/each} |
| 411 | + {/if} </div> |
| 412 | + |
| 413 | + <p><br/></p> |
| 414 | + <p><br/></p> |
| 415 | + <p><br/></p> |
| 416 | + <p><br/></p> |
| 417 | + <p><br/></p> |
| 418 | + <p><br/></p> |
| 419 | + <p><br/></p> |
| 420 | + <p><br/></p> |
| 421 | + <span id="transactions"></span> |
| 422 | + <p> </p> |
| 423 | + |
| 424 | + <h2 class="{is_provider ? 'w3-green' : 'w3-gray'}">Service Instance</h2> |
| 425 | + <h3>Transactions</h3> |
| 426 | + |
| 427 | + <div class="w3-bar w3-theme"> |
| 428 | + <a href="#details" class="w3-bar-item w3-button w3-hover-white">Details</a> |
| 429 | + <a href="#contract" class="w3-bar-item w3-button {has_wallet ? "w3-hover-white" : 'w3-disabled'}">Contract</a> |
| 430 | + <a href="#events" class="w3-bar-item w3-button {has_wallet ? "w3-hover-white" : 'w3-disabled'}">Events</a> |
| 431 | + <a href="#transactions" class="w3-bar-item w3-button {has_wallet ? "w3-hover-white" : 'w3-disabled'}">Transactions</a> |
| 432 | + </div> |
| 433 | + |
| 434 | + <div> |
| 435 | + <p><br/></p> |
| 436 | + <p><br/></p> |
| 437 | + <p><br/></p> |
| 438 | + </div> |
265 | 439 |
|
266 | 440 | {/if} |
267 | 441 | </div> |
|
0 commit comments