Add Super Testnet signing provider

This commit is contained in:
matcha committed 2026-10-05 04:39:32 +00:00
1 parent 05d6cd934d
commit acd61f11bc
1 file changed
+127
+127
View File
@@ -0,0 +1,127 @@
import { Address, Signer, Tx } from "@cmdcode/tapscript";
import { schnorr, secp256k1 } from "@noble/curves/secp256k1";
import { bytesToHex } from "@noble/curves/abstract/utils";
const NETWORK = "super-testnet-local-v0";
const KEY_STORAGE = "inscriboor:test-wallet:v1";
const pending = new Map();
async function account() {
const saved = (await chrome.storage.local.get(KEY_STORAGE))[KEY_STORAGE];
let secret = saved?.secret;
if (!secret || !/^[0-9a-f]{64}$/.test(secret) || !secp256k1.utils.isValidPrivateKey(secret)) {
secret = bytesToHex(secp256k1.utils.randomPrivateKey());
await chrome.storage.local.set({ [KEY_STORAGE]: { kind:"super-testnet-test-key", version:1, network:NETWORK, secret } });
}
const pubkey = bytesToHex(schnorr.getPublicKey(secret));
return { secret, pubkey, address: Address.p2tr.fromPubKey(pubkey, "regtest"), network: NETWORK };
}
function hydrate(hex, prevouts) {
const tx = Tx.decode(hex);
if (!Array.isArray(prevouts) || prevouts.length !== tx.vin.length) throw new Error("Prevout set does not match transaction inputs.");
tx.vin.forEach((input, i) => {
const prevout = prevouts[i];
if (!Number.isSafeInteger(prevout?.value) || prevout.value < 0 || !Array.isArray(prevout?.scriptPubKey)) throw new Error("Invalid signing prevout.");
input.prevout = { value: prevout.value, scriptPubKey: prevout.scriptPubKey };
});
return tx;
}
function assertOwnKeyPath(me, tx, inputIndex) {
if (JSON.stringify(tx.vin[inputIndex]?.prevout?.scriptPubKey) !== JSON.stringify(["OP_1", me.pubkey])) {
throw new Error("Refusing key-path signature for an input not owned by Inscriboor.");
}
}
async function signInscription(params) {
const me = await account();
if (params?.network !== NETWORK) throw new Error("Inscriboor is currently limited to Super Testnet local mode.");
const commit = hydrate(params.commit.hex, params.commit.prevouts);
const reveal = hydrate(params.reveal.hex, params.reveal.prevouts);
assertOwnKeyPath(me, commit, 0);
const revealInput = Number(params.reveal.scriptInput);
if (!Number.isInteger(revealInput) || revealInput < 0 || revealInput >= reveal.vin.length) throw new Error("Invalid reveal script input.");
if (typeof params.reveal.leaf !== "string" || !/^[0-9a-f]+$/.test(params.reveal.leaf)) throw new Error("Missing reveal leaf.");
const result = {
commit: Signer.taproot.sign(me.secret, commit, 0).hex,
revealScript: Signer.taproot.sign(me.secret, reveal, revealInput, { extension: params.reveal.leaf }).hex,
revealKey: null
};
if (revealInput === 1) {
assertOwnKeyPath(me, reveal, 0);
result.revealKey = Signer.taproot.sign(me.secret, reveal, 0).hex;
}
return result;
}
function summary(params, origin) {
const intent = params?.intent ?? {};
return {
origin, network: params?.network, contentType: intent.contentType ?? "unknown", size: intent.size ?? 0,
metadata: !!intent.metadata, metaprotocol: intent.metaprotocol || null, parent: intent.parent || null,
delegate: intent.delegate || null, pointer: intent.pointer ?? "automatic", reinscribe: intent.reinscribe || null,
postage: intent.postage ?? 1000, fees: intent.fees ?? 1000
};
}
function finish(id, error, result) {
const request = pending.get(id);
if (!request) return;
pending.delete(id);
clearTimeout(request.timer);
try { request.port.postMessage({ id, ...(error ? { error } : { result }) }); } catch {}
}
async function openApproval(message, port) {
const local = /^http:\/\/(?:localhost|127\.0\.0\.1)(?::\d+)?$/.test(message.origin);
if (message.origin !== "https://supertestnet.lol" && !local) throw new Error("This Inscriboor build only accepts Super Testnet origins.");
const id = message.id;
const timer = setTimeout(() => finish(id, "Signing request expired."), 120000);
pending.set(id, { port, params: message.params, origin: message.origin, timer });
await chrome.windows.create({ url: chrome.runtime.getURL(`approval.html?id=${encodeURIComponent(id)}`), type:"popup", width:420, height:680, focused:true });
}
chrome.runtime.onConnect.addListener(port => {
if (port.name !== "inscriboor-provider") return;
port.onMessage.addListener(async message => {
try {
if (message.method === "bitcoin_getAccounts") {
const me = await account();
port.postMessage({ id:message.id, result:[{ address:me.address, pubkey:me.pubkey, network:me.network }] });
} else if (message.method === "bitcoin_getNetwork") {
port.postMessage({ id:message.id, result:NETWORK });
} else if (message.method === "ord_signInscription") {
await openApproval(message, port);
} else {
port.postMessage({ id:message.id, error:`Unsupported Inscriboor method: ${message.method}` });
}
} catch (error) {
port.postMessage({ id:message.id, error:error.message });
}
});
port.onDisconnect.addListener(() => {
for (const [id, request] of pending) if (request.port === port) finish(id, "Page disconnected before signing completed.");
});
});
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
if (message?.type === "approval:get") {
const request = pending.get(message.id);
sendResponse(request ? { ok:true, summary:summary(request.params, request.origin) } : { ok:false, error:"Signing request no longer exists." });
return;
}
if (message?.type === "approval:reject") {
finish(message.id, "User rejected signing request.");
sendResponse({ok:true});
return;
}
if (message?.type === "approval:approve") {
const request = pending.get(message.id);
if (!request) { sendResponse({ok:false,error:"Signing request no longer exists."}); return; }
signInscription(request.params)
.then(result => { finish(message.id, null, result); sendResponse({ok:true}); })
.catch(error => { finish(message.id, error.message); sendResponse({ok:false,error:error.message}); });
return true;
}
});