Some checks failed
check / check (push) Failing after 13s
- Content script sends UUID via location.origin instead of "*" - Inpage UUID listener removes itself after first message to prevent malicious pages from overriding the persisted UUID
193 lines
6.0 KiB
JavaScript
193 lines
6.0 KiB
JavaScript
// AutistMask inpage script — injected into the page's JS context.
|
|
// Creates window.ethereum (EIP-1193 provider) and announces via EIP-6963.
|
|
|
|
(function () {
|
|
const CHAIN_ID = "0x1"; // Ethereum mainnet
|
|
|
|
const listeners = {};
|
|
let nextId = 1;
|
|
const pending = {};
|
|
|
|
// Listen for responses from the content script
|
|
window.addEventListener("message", function onUuid(event) {
|
|
if (event.source !== window) return;
|
|
if (event.data?.type !== "AUTISTMASK_RESPONSE") return;
|
|
const { id, result, error } = event.data;
|
|
const p = pending[id];
|
|
if (!p) return;
|
|
delete pending[id];
|
|
if (error) {
|
|
p.reject(new Error(error.message || "Request failed"));
|
|
} else {
|
|
p.resolve(result);
|
|
}
|
|
});
|
|
|
|
// Listen for events pushed from the extension
|
|
window.addEventListener("message", function onUuid(event) {
|
|
if (event.source !== window) return;
|
|
if (event.data?.type !== "AUTISTMASK_EVENT") return;
|
|
const { eventName, data } = event.data;
|
|
emit(eventName, data);
|
|
});
|
|
|
|
function emit(eventName, data) {
|
|
const cbs = listeners[eventName];
|
|
if (!cbs) return;
|
|
for (const cb of cbs) {
|
|
try {
|
|
cb(data);
|
|
} catch (e) {
|
|
// ignore listener errors
|
|
}
|
|
}
|
|
}
|
|
|
|
function sendRequest(args) {
|
|
return new Promise((resolve, reject) => {
|
|
const id = nextId++;
|
|
pending[id] = { resolve, reject };
|
|
window.postMessage(
|
|
{ type: "AUTISTMASK_REQUEST", id, ...args },
|
|
"*",
|
|
);
|
|
});
|
|
}
|
|
|
|
const provider = {
|
|
isAutistMask: true,
|
|
isMetaMask: true, // compatibility — many dApps check this
|
|
chainId: CHAIN_ID,
|
|
networkVersion: "1",
|
|
selectedAddress: null,
|
|
|
|
async request(args) {
|
|
const result = await sendRequest({
|
|
method: args.method,
|
|
params: args.params || [],
|
|
});
|
|
if (
|
|
args.method === "eth_requestAccounts" ||
|
|
args.method === "eth_accounts"
|
|
) {
|
|
provider.selectedAddress =
|
|
Array.isArray(result) && result.length > 0
|
|
? result[0]
|
|
: null;
|
|
}
|
|
return result;
|
|
},
|
|
|
|
// Legacy methods (still used by some dApps)
|
|
enable() {
|
|
return this.request({ method: "eth_requestAccounts" });
|
|
},
|
|
|
|
send(methodOrPayload, paramsOrCallback) {
|
|
// Handle both send(method, params) and send({method, params})
|
|
if (typeof methodOrPayload === "string") {
|
|
return this.request({
|
|
method: methodOrPayload,
|
|
params: paramsOrCallback || [],
|
|
});
|
|
}
|
|
return this.request({
|
|
method: methodOrPayload.method,
|
|
params: methodOrPayload.params || [],
|
|
});
|
|
},
|
|
|
|
sendAsync(payload, callback) {
|
|
this.request({
|
|
method: payload.method,
|
|
params: payload.params || [],
|
|
})
|
|
.then((result) =>
|
|
callback(null, { id: payload.id, jsonrpc: "2.0", result }),
|
|
)
|
|
.catch((err) => callback(err));
|
|
},
|
|
|
|
on(event, cb) {
|
|
if (!listeners[event]) listeners[event] = [];
|
|
listeners[event].push(cb);
|
|
return this;
|
|
},
|
|
|
|
removeListener(event, cb) {
|
|
if (!listeners[event]) return this;
|
|
listeners[event] = listeners[event].filter((c) => c !== cb);
|
|
return this;
|
|
},
|
|
|
|
removeAllListeners(event) {
|
|
if (event) {
|
|
delete listeners[event];
|
|
} else {
|
|
for (const key of Object.keys(listeners)) {
|
|
delete listeners[key];
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// Some dApps (wagmi) check this to confirm MetaMask-like behavior
|
|
_metamask: {
|
|
isUnlocked() {
|
|
return Promise.resolve(provider.selectedAddress !== null);
|
|
},
|
|
},
|
|
};
|
|
|
|
// Set window.ethereum if no other wallet has claimed it
|
|
if (typeof window.ethereum === "undefined") {
|
|
window.ethereum = provider;
|
|
}
|
|
window.dispatchEvent(new Event("ethereum#initialized"));
|
|
|
|
// EIP-6963: Multi Injected Provider Discovery
|
|
const ICON_SVG =
|
|
"data:image/svg+xml," +
|
|
encodeURIComponent(
|
|
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">' +
|
|
'<rect width="32" height="32" rx="6" fill="#000"/>' +
|
|
'<text x="16" y="23" text-anchor="middle" font-family="monospace" font-size="20" font-weight="bold" fill="#fff">A</text>' +
|
|
"</svg>",
|
|
);
|
|
|
|
let providerUuid = crypto.randomUUID(); // fallback until real UUID arrives
|
|
|
|
function buildProviderInfo() {
|
|
return {
|
|
uuid: providerUuid,
|
|
name: "AutistMask",
|
|
icon: ICON_SVG,
|
|
rdns: "berlin.sneak.autistmask",
|
|
};
|
|
}
|
|
|
|
function announceProvider() {
|
|
window.dispatchEvent(
|
|
new CustomEvent("eip6963:announceProvider", {
|
|
detail: Object.freeze({
|
|
info: buildProviderInfo(),
|
|
provider,
|
|
}),
|
|
}),
|
|
);
|
|
}
|
|
|
|
// Listen for the persisted UUID from the content script
|
|
function onProviderUuid(event) {
|
|
if (event.source !== window) return;
|
|
if (event.data?.type !== "AUTISTMASK_PROVIDER_UUID") return;
|
|
window.removeEventListener("message", onProviderUuid);
|
|
providerUuid = event.data.uuid;
|
|
announceProvider();
|
|
}
|
|
window.addEventListener("message", onProviderUuid);
|
|
|
|
window.addEventListener("eip6963:requestProvider", announceProvider);
|
|
announceProvider();
|
|
})();
|