craft-nft

A standalone NFT implementation for real-world arts and crafts assets
Log | Files | Refs | README

commit 4d3568c4b5b9c68562fcdad8ec767abb6da9ef9d
parent ce63038cf2e88ea42056d67af57c0ed16e0d7fc1
Author: lash <dev@holbrook.no>
Date:   Tue, 21 Feb 2023 14:21:55 +0000

Implement metadata endpoint

Diffstat:
Mjs/qrread.html | 27++++++++++++++++++++++-----
Mjs/qrread.js | 53++++++++++++++++++++++++++++++++++++++++++++++++++---
Mjs/qrread_ui.js | 2+-
3 files changed, 73 insertions(+), 9 deletions(-)

diff --git a/js/qrread.html b/js/qrread.html @@ -3,6 +3,7 @@ <title>webcam</title> <script src="node_modules/jsqr/dist/jsQR.js"></script> <script src="node_modules/ethers/dist/ethers.umd.min.js"></script> + <script src="src/wala.js"></script> <script src="qrread.js"></script> <script src="qrread_ui.js"></script> <link rel="stylesheet" href="style.css"></link> @@ -25,18 +26,28 @@ window.addEventListener('load', () => { return keyFileHandler(keyFile, keyFilePassword); }); document.getElementById('chainSubmit').addEventListener("click", (o) => { - const chainId = document.getElementById("chainId").value; - const chainRpcUrl = document.getElementById("chainRpcUrl").value; const submit = document.getElementById('chainSubmit'); submit.setAttribute('disabled', 1); - return chainHandler(chainRpcUrl, chainId); + const chainId = document.getElementById("chainId").value; + const chainRpcUrl = document.getElementById("chainRpcUrl").value; + chainHandler(chainRpcUrl, chainId); + let metaUrl = document.getElementById("metaUrl").value; + if (metaUrl != '') { + metaHandler(metaUrl); + } }); document.getElementById('contractSubmit').addEventListener("click", (o) => { const tokenAddress = document.getElementById("contractAddress").value; return contractHandler(tokenAddress); }); document.getElementById('requestSubmit').addEventListener("click", (o) => { - const tokenBatch = document.getElementById("tokenBatch").value; + let tokenBatch = undefined; + const batches = document.getElementsByName('tokenBatch'); + for(let i = 0; i < batches.length; i++){ + if(batches[i].checked){ + tokenBatch = batches[i].value; + } + } const amount = document.getElementById("requestAmount").value; return requestHandler(tokenBatch, amount); }); @@ -95,6 +106,8 @@ window.addEventListener('load', () => { <input type="text" id="chainRpcUrl" value="http://localhost:8545" /> <label for="chainId">Chain ID</label> <input type="text" id="chainId" /> + <label for="metaUrl">Data URL (optional)</label> + <input type="text" id="metaUrl" value="" /> <button id="chainSubmit">connect to network</button> </div> <div class="pane" id="contract"> @@ -110,10 +123,14 @@ window.addEventListener('load', () => { <button id="requestSubmit">create request</button> </div> <div class="pane" id="read"> - <h2>Scan QR code</h2> + <h2>Mint token(s)</h2> <dl> <dt>Token Id</dt> <dd id="scanTokenId"></dd> + <dt>Token Name</dt> + <dd id="scanTokenMetaName">(unavailable)</dd> + <dt>Token Description</dt> + <dd id="scanTokenMetaDescription">(unavailable)</dd> <dt>Batch</dt> <dd id="scanTokenBatch"></dd> <dt>Amount</dt> diff --git a/js/qrread.js b/js/qrread.js @@ -21,6 +21,8 @@ var settings = { wallet: undefined, chainId: undefined, dataPost: undefined, + metaInterface: undefined, + metaCanWrite: false, mintAmount: 1, minedAmount: 0, failedAmount: 0, @@ -167,13 +169,29 @@ async function scanContract(addr) { setTimeout(scanContractTokens, 0, addr); } -function contractHandler(addr) { +async function contractHandler(addr) { checkState(STATE.WALLET_SETTINGS | STATE.NETWORK_SETTINGS, true); setStatus('check if wallet can mint...', STATUS_BUSY); setTimeout(checkContractOwner, 0, addr); } +async function metaHandler(url) { + settings.metaInterface = new Wala(url); + try { + settings.metaInterface.put('foo'); + settings.metaCanWrite = true; + } catch { + console.warn('cannot write to data URL', url); + } + //try { + // settings.metaInterface.get(''); + //} catch(e) { + // console.warn('cannot read from data URL', url, e); + // settings.metaInterace = undefined; + //} +} + async function scanContractTokens(contractAddress) { const contract = new ethers.Contract(contractAddress, nftAbi, settings.provider); let i = 0; @@ -237,12 +255,41 @@ async function scanContractTokens(contractAddress) { setStatus('found ' + c + ' available token batches in contract', STATUS_OK); } -function requestHandler(tokenBatch, amount) { - setStatus('scan QR code or manually enter address...', STATUS_BUSY); +async function scanTokenMetadata(tokenId) { + if (settings.metaInterface === undefined) { + console.debug('skip metadata lookup since interface is not available'); + return; + } + setStatus('scan token metadata...', STATUS_BUSY); + if (tokenId.length < 64) { + setStatus('invalid token id length', STATUS_ERROR); + throw 'invalid token id length'; + } else if (tokenId.substring(0, 2) == '0x') { + tokenId = tokenId.substring(2); + } + let r = undefined; + try { + r = await settings.metaInterface.get(tokenId); + } catch(e) { + setStatus('metadata lookup failed', STATUS_ERROR); + document.getElementById('scanTokenMetaName').innerHTML = '(unavailable)'; + document.getElementById('scanTokenMetaDescription').innerHTML = '(unavailable)'; + return; + } + const o = JSON.parse(r); + setStatus('found token metadata', STATUS_OK); + console.debug('metadata token', tokenId, o); + document.getElementById('scanTokenMetaName').innerHTML = o['name']; + document.getElementById('scanTokenMetaDescription').innerHTML = o['description']; +} + +async function requestHandler(tokenBatch, amount) { const v = tokenBatch.split('.'); let batchNumberHex = "0000000000000000000000000000000000000000000000000000000000000000" + v[1].toString(16); batchNumberHex = batchNumberHex.slice(-64); let tokenId = v[0].substring(2); + await scanTokenMetadata(tokenId); + setStatus('scan QR code or manually enter address...', STATUS_BUSY); settings.dataPost = tokenId + batchNumberHex; settings.tokenId = tokenId; settings.batchNumber = v[1]; diff --git a/js/qrread_ui.js b/js/qrread_ui.js @@ -98,7 +98,7 @@ window.addEventListener('token', (e) => { const ls = document.getElementById('tokenChooser'); const v = e.detail.tokenId + '.' + e.detail.batch; const input = document.createElement('input'); - input.setAttribute('id', 'tokenBatch'); + input.setAttribute('id', 'tokenBatch.' + v); input.setAttribute('name', 'tokenBatch'); input.setAttribute('type', 'radio'); input.setAttribute('value', v);