Add delegate recursion and reinscription UI
This commit is contained in:
1 parent
9f27897967
commit
5d1a1ae866
10 files changed
+327
-31
No files matched your search
+29
-9
@@ -1,3 +1,4 @@
|
||||
import { RECURSIVE_DEMO } from './recursion.js';
|
||||
import './style.css';
|
||||
import { encodeMetadata, parseMetadataJSON } from './adapters/metadata.js';
|
||||
import { MetadataIndex, deriveProtocol } from './adapters/metadata-index.js';
|
||||
@@ -57,10 +58,18 @@ const composerBody = () => ({ contentType: $('mime-type').value, bytes: fileByte
|
||||
function updateCount() { $('byte-count').textContent = `${composerBody().bytes.length} / ${MAX_BYTES} bytes`; }
|
||||
function resetComposer() {
|
||||
fileRevision++; fileBytes = null; $('body-file').value = ''; $('payload').value = '';
|
||||
$('metadata').value = ''; updateMetadataCount(); $('parent').value = ''; closeReview(); updateCount();
|
||||
$('delegate').value = ''; $('pointer').value = ''; $('reinscribe').value = ''; $('metadata').value = ''; updateMetadataCount(); $('parent').value = ''; closeReview(); updateCount();
|
||||
}
|
||||
function lineage(item, chain) {
|
||||
const p = el('p', undefined, 'tx-links');
|
||||
if (item.delegate) p.append(link(`Delegate ${short(item.delegate)}`, `#inscription/${item.delegate}`));
|
||||
for (const record of chain.transactions.values()) if (record.inscription?.delegate === item.id)
|
||||
p.append(link(`Delegated by ${short(record.inscription.id)}`, `#inscription/${record.inscription.id}`));
|
||||
if (item.sat !== undefined) {
|
||||
p.append(el('span', `Synthetic sat ${item.sat} · stack position ${item.reinscriptionPosition} · ${JSON.stringify(chain.satLocation(item.sat))}`));
|
||||
for (const record of chain.transactions.values()) if (record.inscription?.sat === item.sat)
|
||||
p.append(link(`Sat history #${record.inscription.reinscriptionPosition}`, `#inscription/${record.inscription.id}`));
|
||||
}
|
||||
if (item.parent) p.append(link(`Parent ${short(item.parent)}`, `#inscription/${item.parent}`));
|
||||
for (const record of chain.transactions.values()) if (record.inscription?.parent === item.id)
|
||||
p.append(link(`Child ${short(record.inscription.id)}`, `#inscription/${record.inscription.id}`));
|
||||
@@ -163,7 +172,7 @@ function render() {
|
||||
$('miner-interval').value = String(lab?.miner?.interval ?? 0);
|
||||
$('miner-status').textContent = minerStatus(lab);
|
||||
$('payload').disabled = disabled || !wallet || !!fileBytes || fileLoading || $('mime-type').value.startsWith('image/') && $('mime-type').value !== 'image/svg+xml';
|
||||
for (const id of ['mime-type', 'body-file', 'parent', 'metadata']) $(id).disabled = disabled || !wallet || fileLoading;
|
||||
for (const id of ['mime-type', 'body-file', 'parent', 'metadata', 'delegate', 'pointer', 'reinscribe', 'recursive-demo']) $(id).disabled = disabled || !wallet || fileLoading;
|
||||
$('review').disabled = disabled || !wallet || !chain?.coins(wallet.script).some(c => !c.inscription) || fileLoading || !composerBody().bytes.length;
|
||||
$('sign').disabled = disabled;
|
||||
$('export-wallet').disabled = disabled; $('export-lab').disabled = busy;
|
||||
@@ -180,6 +189,10 @@ function render() {
|
||||
$('parent').replaceChildren(new Option('No parent', ''));
|
||||
items.forEach(item => $('parent').append(new Option(short(item.id), item.id)));
|
||||
$('parent').value = selectedParent;
|
||||
const selectedSat = $('reinscribe').value;
|
||||
$('reinscribe').replaceChildren(new Option('New sat', ''));
|
||||
items.forEach(item => $('reinscribe').append(new Option(`Sat ${item.sat}: ${short(item.id)}`, item.id)));
|
||||
$('reinscribe').value = selectedSat;
|
||||
$('collection-count').textContent = items.length;
|
||||
clearPreviews($('collectibles'));
|
||||
$('collectibles').replaceChildren();
|
||||
@@ -190,7 +203,7 @@ function render() {
|
||||
}
|
||||
for (const item of items.toReversed()) {
|
||||
const card = el('article', undefined, 'collectible');
|
||||
card.append(el('span', item.height ? `CONFIRMED · BLOCK ${item.height}` : 'PENDING · MINE TO CONFIRM', 'tag'), preview(item));
|
||||
card.append(el('span', item.height ? `CONFIRMED · BLOCK ${item.height}` : 'PENDING · MINE TO CONFIRM', 'tag'), preview(item, new LocalOrdAPI(chain)));
|
||||
card.append(link(`Inscription ${short(item.id)}`, `#inscription/${item.id}`));
|
||||
const trail = el('p', undefined, 'tx-links');
|
||||
trail.append(link('View reveal transaction ↗', `#tx/${item.txid}`));
|
||||
@@ -231,7 +244,12 @@ setInterval(() => {
|
||||
$('metadata').oninput = () => { closeReview(); updateMetadataCount(); };
|
||||
MIME_TYPES.forEach(type => $('mime-type').append(new Option(type, type)));
|
||||
$('payload').oninput = () => { closeReview(); updateCount(); render(); };
|
||||
$('parent').onchange = closeReview;
|
||||
$('parent').onchange = $('reinscribe').onchange = closeReview;
|
||||
$('delegate').oninput = $('pointer').oninput = closeReview;
|
||||
$('recursive-demo').onclick = () => {
|
||||
fileRevision++; fileBytes = null; $('body-file').value = ''; $('mime-type').value = 'text/html';
|
||||
$('payload').value = RECURSIVE_DEMO; closeReview(); updateCount(); render();
|
||||
};
|
||||
$('mime-type').onchange = () => {
|
||||
fileRevision++; fileBytes = null; $('body-file').value = ''; closeReview(); updateCount(); render();
|
||||
};
|
||||
@@ -253,12 +271,14 @@ $('body-file').onchange = async event => {
|
||||
$('review').onclick = () => {
|
||||
try {
|
||||
const payload = composerBody(), parent = $('parent').value || null, metadata = parseMetadataJSON($('metadata').value);
|
||||
new InscriptionAdapter(lab.chain, lab.wallet).prepare(payload, parent, metadata);
|
||||
const options = { delegate: $('delegate').value.trim() || null, reinscribe: $('reinscribe').value || null, ...($('pointer').value.trim() ? { pointer: $('pointer').value.trim() } : {}) };
|
||||
new InscriptionAdapter(lab.chain, lab.wallet).prepare(payload, parent, metadata, options);
|
||||
closeReview();
|
||||
reviewed = { payload, parent, metadata, address: lab.wallet.address };
|
||||
reviewed = { payload, parent, metadata, options, address: lab.wallet.address };
|
||||
$('review-text').textContent = ['text/plain', 'application/json'].includes(payload.contentType) ? new TextDecoder().decode(payload.bytes) : `${payload.contentType} · ${payload.bytes.length} bytes`;
|
||||
if (!['text/plain', 'application/json'].includes(payload.contentType)) $('review-preview').append(preview(payload));
|
||||
$('review-parent').textContent = parent ? `Parent: ${parent}. Its output will be spent and its full value returned to this wallet. This relationship is immutable.` : 'No parent.';
|
||||
$('review-parent').textContent += ` Delegate: ${options.delegate ?? 'none'}. Reinscribe: ${options.reinscribe ?? 'none'}. Pointer: ${options.pointer ?? 'automatic'}.`;
|
||||
$('review-metadata').textContent = metadata === undefined ? 'No metadata.' : `Metadata (CBOR, tag 5): ${JSON.stringify(metadata, null, 2)}`;
|
||||
$('review-panel').hidden = false;
|
||||
$('sign').focus(); message('Review the payload and fake-sat costs. Nothing has been signed yet.');
|
||||
@@ -270,7 +290,7 @@ $('sign').onclick = () => {
|
||||
if (!consent) return;
|
||||
mutate(next => {
|
||||
if (next.wallet?.address !== consent.address) throw new Error('Wallet changed. Review again before signing.');
|
||||
new InscriptionAdapter(next.chain, next.wallet).inscribe(consent.payload, consent.parent, consent.metadata);
|
||||
new InscriptionAdapter(next.chain, next.wallet).inscribe(consent.payload, consent.parent, consent.metadata, consent.options);
|
||||
}, 'Inscription submitted. Its commit + reveal are now visible in the mempool; mine a block to confirm them.', resetComposer);
|
||||
};
|
||||
$('export-wallet').onclick = () => download('super-testnet-wallet.json', JSON.stringify(lab.wallet.backup(), null, 2));
|
||||
@@ -336,7 +356,7 @@ function inspect(scroll = true) {
|
||||
if (record.inscription) {
|
||||
add('Inscription ID', record.inscription.id); add('Content type', record.inscription.contentType);
|
||||
add('Body size', `${record.inscription.bytes.length} bytes`);
|
||||
pane.append(preview(record.inscription), metadataView(record.inscription), lineage(record.inscription, chain));
|
||||
pane.append(preview(record.inscription, new LocalOrdAPI(chain)), metadataView(record.inscription), lineage(record.inscription, chain));
|
||||
pane.append(link('Inspect commit transaction ↗', `#tx/${record.inscription.commitTxid}`));
|
||||
}
|
||||
pane.append(el('h3', 'Inputs → outputs'));
|
||||
@@ -350,7 +370,7 @@ function inspect(scroll = true) {
|
||||
tx.vout.forEach((out, i) => add(`Output ${i}`, `${Number(out.value).toLocaleString()} sats`));
|
||||
add('Fee', record.coinbase ? '0 (local reward)' : '500 fake sats');
|
||||
pane.append(el('h3', 'Raw Bitcoin transaction'), el('pre', rpc.call('getrawtransaction', [txid])));
|
||||
if (!record.coinbase) { pane.append(el('h3', 'Witness'), el('pre', JSON.stringify(tx.vin[0].witness, null, 2))); }
|
||||
if (!record.coinbase) { pane.append(el('h3', 'Witness'), el('pre', JSON.stringify(tx.vin.map(input => input.witness), null, 2))); }
|
||||
} else throw new Error('Unknown explorer view.');
|
||||
} catch (error) { pane.append(el('p', error.message)); }
|
||||
if (scroll) { pane.scrollIntoView({ behavior: 'smooth', block: 'start' }); pane.focus({ preventScroll: true }); }
|
||||
|
||||
Reference in new issue
Block a user