diff --git a/package.json b/package.json index 24240ed3..e0908dbc 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,10 @@ "url": "https://github.com/MetaMask/test-dapp/issues" }, "homepage": "https://metamask.github.io/test-dapp", - "dependencies": {}, + "dependencies": { + "d3": "^7.9.0", + "web3-eth-contract": "^4.7.0" + }, "devDependencies": { "@lavamoat/allow-scripts": "^2.5.1", "@lavamoat/preinstall-always-fail": "^2.0.0", diff --git a/src/eip1193_index.html b/src/eip1193_index.html new file mode 100644 index 00000000..7ce9c8d5 --- /dev/null +++ b/src/eip1193_index.html @@ -0,0 +1,209 @@ + + + + + + Ethereum EIP-1193 API Dapp + + +

Ethereum EIP-1193 API Dapp

+ +
+
Connected Accounts: Not available
+

+ + +

+ +

+
Current Chain: Not available
+
Current Block Head: Not available
+ + + diff --git a/src/multichain_demo.html b/src/multichain_demo.html new file mode 100644 index 00000000..46f6c933 --- /dev/null +++ b/src/multichain_demo.html @@ -0,0 +1,88 @@ + + + + + + Multichain API EVM Bridging Demo Dapp + + + + +
+ +
+ + +

+ Sepolia +
+ Linea Sepolia +
+ Arbitrum Sepolia +
+ OP Sepolia +
+ Blast Sepolia +
+ + + + + + + diff --git a/src/multichain_demo.js b/src/multichain_demo.js new file mode 100644 index 00000000..74bc3953 --- /dev/null +++ b/src/multichain_demo.js @@ -0,0 +1,1151 @@ +import * as d3 from "d3"; +import { Contract } from 'web3-eth-contract'; +import abi from './multichain_demo_contract_abi.json' +const bridgeContract = new Contract(abi) + +// +// Constants +// +const WeiPerEth = 1000000000000000000 + +const BridgeableScopes = { + // Sepolia + "eip155:11155111": { + name: "Sepolia", + contractAddress: '0x2e2512fd69cba059DFf557cD6f683a3279402e91', + blockExplorerUrl: 'https://sepolia.etherscan.io', + supports: ["eip155:59141", "eip155:421614", "eip155:11155420", "eip155:168587773"] + }, + // Linea Sepolia (no Arbitrium) + "eip155:59141": { + name: "Linea Sepolia", + contractAddress: '0x786Cb4C684F9D4bA4aBEbddE2c8a4D4ec80a9b78', + blockExplorerUrl: 'https://sepolia.lineascan.build/', + supports: ["eip155:11155111", "eip155:11155420", "eip155:168587773"] + }, + // Arbitrum Sepolia (no Linea) + "eip155:421614": { + name: "Arbitrum Sepolia", + contractAddress: '0x697230B7c217F4F45C460d5d181F792AB0aC6549', + blockExplorerUrl: 'https://sepolia.arbiscan.io/', + supports: ["eip155:11155111", "eip155:11155420", "eip155:168587773"] + }, + // OP Sepolia (no Blast) + "eip155:11155420": { + name: "OP Sepolia", + contractAddress: '0x697230B7c217F4F45C460d5d181F792AB0aC6549', + blockExplorerUrl: 'https://sepolia-optimism.etherscan.io/', + supports: ["eip155:11155111", "eip155:59141", "eip155:421614"] + }, + // Blast Sepolia (no OP) + "eip155:168587773": { + name: "Blast Sepolia", + contractAddress: '0x697230B7c217F4F45C460d5d181F792AB0aC6549', + blockExplorerUrl: 'https://sepolia.blastscan.io/', + supports: ["eip155:11155111", "eip155:59141", "eip155:421614"] + }, +} + +const pastelColors = [ + "#e8b6ae", "#f0c1ad", "#eed0ba", "#ebdec6", "#cdc0d6", + "#b8b1cf", "#959bad", "#97aeb8", "#a5bcc1", "#b2c9c9", + "#9ac7a8", "#b8dbc9", "#c5e3d2", "#d1ebdb", "#d6e1c8", + "#e5c28c", "#e8d097", "#edd9aa", "#f2e6bb", "#f9f3dd" +]; +const NODE_MIN_RADIUS = 130; +const NODE_PADDING = 20; +const NODE_SCALE = 1.3; +const NODE_TEXT_START = "-2.4em"; +const NODE_TEXT_SPACING = "1.2em"; +const NODE_FLASH_COLOR = "gray" +const EDGE_COLOR = "#999"; +const EDGE_STROKE_WIDTH = 3; +const FLASH_DURATION = 3000; + +const ETH_VALUE_PRECISION = 4; + +const USE_SUBSCRIPTIONS = true; // use false for polling +// subscriptions +const SUBSCRIPTION_STAGGER = 5000; +const SUBSCRIPTION_DEBOUNCE = 2500; +const SUBSCRIPTION_REQUEST_DELAY = 666; // just to avoid rate limiting a bit better +// polling +const POLLING_RATE = 12500; // mainnet block time -ish + +// +// State +// +let extensionPort; +let jsonRpcId; +let currentSessionScopes = {}; +let accounts = []; +let scopeStrings = []; + +let balances = {}; +let transactions = []; +let subscriptionDebounce = {}; + +let nodes = []; +let links = []; + +// +// Helpers +// + +// Dapp <-> Wallet Connection Initialization +async function connectExtension() { + const extensionId = document.getElementById("connectExtensionInput").value; + try { + extensionPort = chrome.runtime.connect(extensionId); // externally_connectable + extensionPort.onMessage.addListener((msg) => { + const { data: { method, params } } = msg; + // Subscription Events + if (method === "wallet_notify") { + handleEthSubscription( + params.scope, + params.notification.params.result.number, + ); + // Permission Events + } else if (method === "wallet_sessionChanged") { + onNewSessionScopes(params.sessionScopes); + } + // console.log(msg.data); + }); + + // Dapp Initialization + checkWalletConnection(); + } catch (error) { + console.error(error); + alert("Failed to connect to extension!"); + } +} + +// Permission Handling +async function onNewSessionScopes(sessionScopes) { + const oldScopeStrings = scopeStrings; + const oldAccounts = accounts; + const eip155ScopeStrings = Object.keys(sessionScopes).filter((scopeString) => { + // return /eip155:[0-9]+/u.test(scopeString) + if (!BridgeableScopes[scopeString]) { + // console.log(`ignoring ${scopeString} since it is not defined in BridgeableScopes`) + return false + } + return true + }); + + const eip155AccountsSet = new Set(); + + eip155ScopeStrings.forEach((scopeString) => { + + const scopeObject = sessionScopes[scopeString]; + + scopeObject.accounts.forEach((account) => { + const address = account.split(":")[2]; + eip155AccountsSet.add(address); + }); + }); + + currentSessionScopes = sessionScopes; + accounts = [...eip155AccountsSet]; + scopeStrings = [...eip155ScopeStrings]; + + const accountsDidChange = oldAccounts.length !== accounts.length || accounts.some(account => !oldAccounts.includes(account)) + + oldScopeStrings.forEach((oldScopeString) => { + if (!scopeStrings.includes(oldScopeString)) { + removeNode(oldScopeString) + } + }); + scopeStrings.forEach(newScopeString => { + if (!oldScopeStrings.includes(newScopeString)) { + addNode(newScopeString) + if (USE_SUBSCRIPTIONS) { + setTimeout(() => { + subscribeToBlockHeaders(newScopeString) + }, Math.floor(Math.random() * (SUBSCRIPTION_STAGGER + 1))) + } + } else if (accountsDidChange) { + refreshNodeContent(newScopeString) + } + }) + + if (!USE_SUBSCRIPTIONS) { + startPolling() + } +} + +async function subscribeToBlockHeaders(scopeString) { + try { + await extensionPortRequest({ + method: "wallet_invokeMethod", + params: { + scope: scopeString, + request: { + method: "eth_subscribe", + params: ["newHeads"], + }, + }, + }); + } catch (error) { + console.error(error); + alert("Subscription failed!"); + } +} + +function getShortAddress(address) { + return `${address.slice(0,7)}...${address.slice(37,42)}` +} + +function getNodeLabelText(scopeString) { + const {name, contractAddress} = BridgeableScopes[scopeString]; + const accountsText = accounts.map(account => { + const balance = balances[scopeString]?.[account]?.toFixed(ETH_VALUE_PRECISION) ?? '------' + return `${getShortAddress(account)}: ${balance} ETH` + }).join('\n') + + const contractBalance = balances[scopeString]?.[contractAddress] || 0 + // return`${name}\nBridge Contract: ${contractBalance.toFixed(ETH_VALUE_PRECISION)} ETH\n--------------------------\n${accountsText}` + return`${name}\n--------------------------\n${accountsText}` +} + +// Permission Initialization +async function connectWallet() { + if (!extensionPort) { + alert("Connect to extension first."); + return; + } + try { + + const { sessionScopes } = await extensionPortRequest({ + method: "wallet_createSession", + params: { + eip155: { + references: ["11155111", "59141", "421614", "11155420", "168587773"], + methods: [ + "eth_getTransactionReceipt", + "eth_getBalance", + "eth_sendTransaction", + "eth_subscribe", + ], + notifications: ["eth_subscription"], + }, + }, + }); + + await onNewSessionScopes(sessionScopes); + } catch (error) { + console.error(error); + alert("Failed to connect wallet!"); + } +} + +async function checkWalletConnection() { + try { + const { sessionScopes } = await extensionPortRequest({ + method: "wallet_getSession", + }); + await onNewSessionScopes(sessionScopes); + } catch (error) { + console.error("Failed to check wallet connection:", error); + alert("Failed to check wallet connection"); + } +} + +// +// externally_connectable helpers: +// normally this would be abstracted away +// by a helper library +// + +function generateJsonRpcId(opts) { + let max = Number.MAX_SAFE_INTEGER; + jsonRpcId = jsonRpcId ?? Math.floor(Math.random() * max); + + jsonRpcId = jsonRpcId % max; + jsonRpcId += 1; + return jsonRpcId; +} + +async function extensionPortRequest(request) { + const id = generateJsonRpcId(); + + extensionPort.postMessage({ + type: "caip-x", + data: { + jsonrpc: "2.0", + id, + ...request, + }, + }); + + return new Promise((resolve, reject) => { + const listener = (msg) => { + if (msg.type === "caip-x" && msg.data.id === id) { + const { result, error } = msg.data; + if (result) { + resolve(result); + } else { + reject(error); + } + extensionPort.onMessage.removeListener(listener); + } + }; + + extensionPort.onMessage.addListener(listener); + }); +} + +// +// D3 +// + +const svg = d3.select("body").append("svg") + .attr("width", window.innerWidth) + .attr("height", window.innerHeight); + +const simulation = d3.forceSimulation(nodes) + .force("link", d3.forceLink(links).id(d => d.id).distance(400)) // Further increased distance + .force("charge", d3.forceManyBody().strength(-1600)) // Further increased negative strength + .force("center", d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2)) + .force("collide", d3.forceCollide().radius(d => getRadius(d.label) + 40)); // Further increased radius + +let link = svg.append("g") + .attr("class", "links") + .selectAll("line") + .data(links) + .enter().append("line") + .attr("stroke-width", EDGE_STROKE_WIDTH) + .attr("stroke", EDGE_COLOR) + .on("click", function(event, d) { + showEdgeModal(d); + }); + +let node = svg.append("g") + .attr("class", "nodes") + .selectAll("g") + .data(nodes) + .enter().append("g") + .on("click", function(event, d) { + showNodeModal(d.id); + }); + +node.append("circle") + .attr("r", d => getRadius(d.label)) + .attr("fill", () => pastelColors[Math.floor(Math.random() * pastelColors.length)]); + +let text = node.append("text") + .attr("text-anchor", "middle") + .attr("dominant-baseline", "top"); // this may need to be added elsewhere + +text.selectAll("tspan") + .data(d => d.label.split('\n')) + .enter() + .append("tspan") + .attr("x", 0) + .attr("dy", (d, i) => i === 0 ? NODE_TEXT_START : NODE_TEXT_SPACING) + .text(d => d); + + +function updateGraph() { + d3.selectAll("g > *").remove() + + link = svg.append("g") + .attr("class", "links") + .selectAll("line") + .data(links) + .enter().append("line") + .attr("stroke-width", EDGE_STROKE_WIDTH) + .attr("stroke", EDGE_COLOR) + .on("click", function(event, d) { + showEdgeModal(d); + }); + + node = svg.append("g") + .attr("class", "nodes") + .selectAll("g") + .data(nodes) + .enter().append("g") + .on("click", function(event, d) { + showNodeModal(d.id); + }); + + node.append("circle") + .attr("r", d => getRadius(d.label)) + .attr("fill", () => pastelColors[Math.floor(Math.random() * pastelColors.length)]); + + text = node.append("text") + .attr("text-anchor", "middle") + .attr("dominant-baseline", "top"); // this may need to be added elsewhere + + text.selectAll("tspan") + .data(d => d.label.split('\n')) + .enter() + .append("tspan") + .attr("x", 0) + .attr("dy", (d, i) => i === 0 ? NODE_TEXT_START : NODE_TEXT_SPACING) + .text(d => d); +} + +simulation.on("tick", () => { + link + .attr("x1", d => d.source.x) + .attr("y1", d => d.source.y) + .attr("x2", d => d.target.x) + .attr("y2", d => d.target.y); + + node + .attr("transform", d => `translate(${d.x},${d.y})`); +}); + + +function getRadius(text) { + const context = document.createElement("canvas").getContext("2d"); + context.font = "10px sans-serif"; + const lines = text.split("\n"); + const maxWidth = Math.max(...lines.map(line => context.measureText(line).width)); + const lineHeight = 10; // Approximate line height + const textHeight = lines.length * lineHeight; + const original = Math.sqrt(maxWidth * maxWidth + textHeight * textHeight) / 2 + 10 + return Math.max(NODE_MIN_RADIUS, original * NODE_SCALE + NODE_PADDING); +} + +window.addEventListener("resize", () => { + svg.attr("width", window.innerWidth) + .attr("height", window.innerHeight); + simulation.force("center", d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2)); + simulation.alpha(1).restart(); // Restart simulation to adjust positions +}); + +// Modal functionality +const modal = document.getElementById("myModal"); +const modalText = document.getElementById("modal-text"); +const span = document.getElementsByClassName("close")[0]; + + +function generateBlockExplorerAddressLink(blockExplorerUrl, address, label) { + return `${label || address}` +} + +function showEdgeModal(edge) { + const source = BridgeableScopes[edge.source.id] + const target = BridgeableScopes[edge.target.id] + + let maxAmount = Math.min( + balances[edge.source.id]?.[source.contractAddress] || 0, + balances[edge.target.id]?.[target.contractAddress] || 0, + balances[edge.source.id]?.[accounts[0]] || 0 + ) + + modalText.innerHTML = ` +

Bridge ${source.name} <-> ${target.name}

+

${source.name} Bridge Contract: ${generateBlockExplorerAddressLink(source.blockExplorerUrl, source.contractAddress)} (${(balances[edge.source.id]?.[source.contractAddress] || 0).toFixed(ETH_VALUE_PRECISION)} ETH)

+

${target.name} Bridge Contract: ${generateBlockExplorerAddressLink(target.blockExplorerUrl, target.contractAddress)} (${(balances[edge.target.id]?.[target.contractAddress] || 0).toFixed(ETH_VALUE_PRECISION)} ETH)

+
+ + + + +
+ + +
+ + + Max: ${maxAmount.toFixed(ETH_VALUE_PRECISION)} ETH +
+ + `; + + modal.style.display = "block"; + + document.getElementById("fromScopeSelect").addEventListener("change", (event) => { + const fromScope = event.target.value; + const toScope = fromScope === edge.source.id ? edge.target.id : edge.source.id; + document.getElementById("accountSelect").innerHTML = accounts.map(account => { + const balance = balances[fromScope]?.[account] || 0 + return `` + }).join('') + + document.getElementById("toScopeSelect").value = toScope + + const account = document.getElementById("accountSelect").value + maxAmount = Math.min( + balances[edge.source.id]?.[source.contractAddress] || 0, + balances[edge.target.id]?.[target.contractAddress] || 0, + balances[fromScope]?.[account] || 0 + ) + document.getElementById("maxAmountDisplay").innerText = maxAmount.toFixed(ETH_VALUE_PRECISION) + }); + + document.getElementById("toScopeSelect").addEventListener("change", (event) => { + const toScope = event.target.value; + const fromScope = toScope === edge.source.id ? edge.target.id : edge.source.id; + document.getElementById("fromScopeSelect").value = fromScope + }); + + document.getElementById("accountSelect").addEventListener("change", (event) => { + const account = event.target.value; + const fromScope = document.getElementById("fromScopeSelect").value + + maxAmount = Math.min( + balances[edge.source.id]?.[source.contractAddress] || 0, + balances[edge.target.id]?.[target.contractAddress] || 0, + balances[fromScope]?.[account] || 0 + ) + document.getElementById("maxAmountDisplay").innerText = maxAmount.toFixed(ETH_VALUE_PRECISION) + }); + + document.getElementById("bridgeButton").addEventListener("click", async () => { + const account = document.getElementById("accountSelect").value + const fromScope = document.getElementById("fromScopeSelect").value + const toScope = document.getElementById("toScopeSelect").value + + const { contractAddress } = BridgeableScopes[fromScope] + + try { + const amount = Number(document.getElementById("amountText").value) + if (amount > maxAmount) { + alert(`Amount must be less than ${maxAmount}`) + return; + } + if (amount <= 0 || Number.isNaN(amount)) { + alert(`Amount must be greater than 0`) + return; + } + + + const transactionHash = await extensionPortRequest({ + method: "wallet_invokeMethod", + params: { + scope: fromScope, + request: { + "method": "eth_sendTransaction", + "params": [{ + to: contractAddress, + from: account, + data: "0x", + value: `0x${(amount * WeiPerEth).toString(16)}` + }] + } + }, + }) + + transactions.push({ + account, + toScope, + fromScope, + amount, + transactionHash, + type: 'bridge' + }) + updateEdgeColor(fromScope, toScope, "orange") + updateNodeBorderColor(fromScope, "orange") + modal.style.display = "none"; + } catch (error) { + console.log(error) + alert('failed to bridge!') + } + }); +} + + +function showNodeModal(id) { + const {name, contractAddress, supports, blockExplorerUrl} = BridgeableScopes[id] + modalText.innerHTML = ` +

${name}

+

Block Explorer

+

Bridge Contract: ${generateBlockExplorerAddressLink(blockExplorerUrl, contractAddress)} (${(balances[id]?.[contractAddress] || 0).toFixed(ETH_VALUE_PRECISION)} ETH)

+

Bridgeable Scopes: ${supports.map(scopeString => BridgeableScopes[scopeString].name).join(', ')}

+
+ Accounts: +
+ + + `; + modal.style.display = "block"; +} + +span.onclick = function() { + modal.style.display = "none"; +} + +window.onclick = function(event) { + if (event.target == modal) { + modal.style.display = "none"; + } +} + +function updateEdgeColor(sourceId, targetId, color = EDGE_COLOR) { + const targetLink = links.find(link => + (link.source.id === sourceId && link.target.id === targetId) || + (link.source.id === targetId && link.target.id === sourceId) + ) + if (!targetLink) { + console.log('couldnt find link', {sourceId, targetId, color}) + return + } + + const linkSelection = d3.select(link.nodes()[links.indexOf(targetLink)]) + + linkSelection.attr("stroke", color); + + // const sourceNode = nodes.find(node => node.id === randomLink.source.id); + // const targetNode = nodes.find(node => node.id === randomLink.target.id); + + // const edgeLabel = svg.append("text") + // .attr("x", (sourceNode.x + targetNode.x) / 2) + // .attr("y", (sourceNode.y + targetNode.y) / 2) + // .attr("dy", -5) + // .attr("text-anchor", "middle") + // .attr("fill", "green") + // .text("sending..."); + + // setTimeout(() => { + // edgeLabel.remove(); + // }, 3000); +} + +function addNode(id) { + // console.log('adding node', id) + const newNode = { id, label: getNodeLabelText(id) }; + nodes.push(newNode); + + const supportedScopes = BridgeableScopes[id]?.supports || [] + + supportedScopes.forEach((supportedScope) => { + const targetNode = nodes.find(node => node.id === supportedScope) + const targetLink = links.find(link => + (link.source.id === id && link.target.id === supportedScope) || + (link.source.id === supportedScope && link.target.id === id) + ) + if (targetNode && !targetLink) { + links.push({ source: id, target: targetNode.id }); + } + }) + + // Update the simulation with the new node and link + simulation.nodes(nodes); + simulation.force("link").links(links); + + // Add the new node to the SVG + // const newNodeSelection = svg.select(".nodes") + // .selectAll("g") + // .data(nodes) + // .enter() + // .append("g") + // .on("click", function(event, d) { + // showModal(d.label); + // }); + + // newNodeSelection.append("circle") + // .attr("r", d => getRadius(d.label)) + // .attr("fill", () => pastelColors[Math.floor(Math.random() * pastelColors.length)]); + + // const newText = newNodeSelection.append("text") + // .attr("text-anchor", "middle"); + + // newText.selectAll("tspan") + // .data(d => d.label.split('\n')) + // .enter() + // .append("tspan") + // .attr("x", 0) + // .attr("dy", (d, i) => i === 0 ? NODE_TEXT_START : NODE_TEXT_SPACING) + // .text(d => d); + + + // Restart the simulation + updateGraph() + simulation.alpha(1).restart(); +} + +function removeNode(id) { + // console.log('removing node', id) + nodes = nodes.filter(node => node.id !== id); + links = links.filter(link => link.source.id !== id && link.target.id !== id); + + // Update the simulation with the removed node and links + simulation.nodes(nodes); + simulation.force("link").links(links); + + // Remove the node and its edges from the SVG + svg.select(".nodes").selectAll("g") + .data(nodes, d => d.id) + .exit().remove(); + + svg.select(".links").selectAll("line") + .data(links, d => `${d.source.id}-${d.target.id}`) + .exit().remove(); + + updateGraph() + // Restart the simulation + simulation.alpha(1).restart(); +} + +function refreshNodeContent(id) { + const targetNode = nodes.find(node => node.id === id) + targetNode.label = getNodeLabelText(id) + text.selectAll("tspan").remove(); + text.selectAll("tspan") + .data(d => d.label.split('\n')) + .enter() + .append("tspan") + .attr("x", 0) + .attr("dy", (d, i) => i === 0 ? NODE_TEXT_START : NODE_TEXT_SPACING) + .text(d => d); + node.select("circle").attr("r", d => getRadius(d.label)); + + const originalColor = svg.selectAll(".nodes circle") + .filter(d => d.id === id) + .attr("fill"); + + if(originalColor !== NODE_FLASH_COLOR) { + svg.selectAll(".nodes circle") + .filter(d => d.id === id) + .attr("fill", NODE_FLASH_COLOR); + + setTimeout(() => { + svg.selectAll(".nodes circle") + .filter(d => d.id === id) + .attr("fill", originalColor); + }, FLASH_DURATION / 4) + } + + + // Restart simulation to adjust positions + // simulation.alpha(1).restart(); +} + +function updateNodeBorderColor(id, color) { + const targetNode = nodes.find(node => node.id === id) + + const targetNodeCircle = d3.select(node.nodes()[nodes.indexOf(targetNode)]).select("circle"); + if (color) { + targetNodeCircle.attr("stroke", color).attr("stroke-width", 3); + } else { + targetNodeCircle.attr("stroke", null).attr("stroke-width", null); + } + + // Restart simulation to adjust positions + // simulation.alpha(1).restart(); +} + +// DOM +document.getElementById("connectExtensionButton").addEventListener("click", connectExtension); +document.getElementById("connectButton").addEventListener("click", connectWallet); + +// Claim +async function claimBridgedEth(transaction) { + const { toScope, fromScope, account, amount } = transaction + const { contractAddress } = BridgeableScopes[toScope] + + if (!confirm(`Initial bridge tranasction on ${BridgeableScopes[fromScope].name} was successful. Finish by claiming the ${amount} ETH now waiting on ${BridgeableScopes[toScope].name}?`)) { + console.log(`User chose not to finish claiming ${amount} ETH that has already been bridged from ${BridgeableScopes[fromScope].name} to ${BridgeableScopes[toScope].name}`); + return + } + + const data = await bridgeContract.methods.withdraw(`0x${(amount * WeiPerEth).toString(16)}`).encodeABI(); + + const transactionHash = await extensionPortRequest({ + method: "wallet_invokeMethod", + params: { + scope: toScope, + request: { + "method": "eth_sendTransaction", + "params": [{ + to: contractAddress, + from: account, + data + }] + } + }, + }) + + transactions.push({ + account, + toScope, + fromScope, + amount, + transactionHash, + type: 'claim' + }) + updateEdgeColor(fromScope, toScope, "orange") + updateNodeBorderColor(toScope, "orange") +} + +// Events/Loops +async function handleEthSubscription(scopeString, _blockHead) { + if (!scopeStrings.includes(scopeString)) { + // Not sure how this would happen + return + } + if(subscriptionDebounce[scopeString]) { + return + } + subscriptionDebounce[scopeString] = true + + console.log(`Subscription: updating account balances, contract balance, tx statuses for scope ${scopeString}`) + await updateTransactionStatusesForScope(scopeString) + await updateContractBalanceForScope(scopeString) + await updateAccountBalancesForScope(scopeString) + + setTimeout(() => { + subscriptionDebounce[scopeString] = false + }, SUBSCRIPTION_DEBOUNCE) +} + +async function updateAccountBalancesForScope(scopeString) { + if(!extensionPort) { + return + } + if(!accounts.length || !scopeStrings.includes(scopeString)) { + return + } + + for (let account of accounts) { + // console.log(`updating balance for account ${account} on scope ${scopeString}`) + try { + const balance = await extensionPortRequest({ + method: "wallet_invokeMethod", + params: { + scope: scopeString, + request: { + "method": "eth_getBalance", + "params": [ + account, + "latest" + ], + }, + }, + }) + balances[scopeString] = balances[scopeString] || {} + const oldBalance = balances[scopeString][account] || 0 + balances[scopeString][account] = parseInt(balance, 16) / Math.pow(10, 18); + + if (oldBalance !== balances[scopeString][account]) { + // console.log(`updating node for account ${account} on scope ${scopeString} with ${balances[scopeString][account]}`) + // refreshNodeContent(scopeString, !oldBalance || balances[scopeString][account] > oldBalance ? "green" : "red") + refreshNodeContent(scopeString) + } + + } catch (error) { + console.error(`failed updating balance for account ${account} on scope ${scopeString}`, error) + } + await new Promise((resolve) => setTimeout(resolve, SUBSCRIPTION_REQUEST_DELAY)) + } +} + +async function updateAccountBalances() { + if(!extensionPort) { + return + } + if(!accounts.length || !scopeStrings.length) { + return + } + + for (let account of accounts) { + console.log(`Polling: updating account balances for account ${account}`) + // Notice how we hit multiple chains at once + await Promise.allSettled(scopeStrings.map(async scopeString => { + // console.log(`updating balance for account ${account} on scope ${scopeString}`) + try { + const balance = await extensionPortRequest({ + method: "wallet_invokeMethod", + params: { + scope: scopeString, + request: { + "method": "eth_getBalance", + "params": [ + account, + "latest" + ], + }, + }, + }) + balances[scopeString] = balances[scopeString] || {} + const oldBalance = balances[scopeString][account] || 0 + balances[scopeString][account] = parseInt(balance, 16) / Math.pow(10, 18); + + if (oldBalance !== balances[scopeString][account]) { + // console.log(`updating node for account ${account} on scope ${scopeString} with ${balances[scopeString][account]}`) + // refreshNodeContent(scopeString, !oldBalance || balances[scopeString][account] > oldBalance ? "green" : "red") + refreshNodeContent(scopeString) + } + + } catch (error) { + console.error(`failed updating balance for account ${account} on scope ${scopeString}`, error) + } + + })) + } +} +async function updateAccountBalancesPoll() { + await updateAccountBalances() + setTimeout(() => { + updateAccountBalancesPoll() + }, POLLING_RATE) +} + + +async function updateContractBalanceForScope(scopeString) { + if(!extensionPort) { + return + } + if(!accounts.length || !scopeStrings.includes(scopeString)) { + return + } + + const {contractAddress} = BridgeableScopes[scopeString] + // console.log(`updating balance for contract ${contractAddress} on scope ${scopeString}`) + try { + const balance = await extensionPortRequest({ + method: "wallet_invokeMethod", + params: { + scope: scopeString, + request: { + "method": "eth_getBalance", + "params": [ + contractAddress, + "latest" + ], + }, + }, + }) + balances[scopeString] = balances[scopeString] || {} + const oldBalance = balances[scopeString][contractAddress] || 0 + balances[scopeString][contractAddress] = parseInt(balance, 16) / Math.pow(10, 18); + + if (oldBalance !== balances[scopeString][contractAddress]) { + // console.log(`updating node for contract ${contractAddress} on scope ${scopeString} with ${balances[scopeString][contractAddress]}`) + refreshNodeContent(scopeString) + } + + } catch (error) { + console.error(`failed updating balance for contract ${contractAddress} on scope ${scopeString}`, error) + } +} + +async function updateContractBalances() { + if(!extensionPort) { + return + } + if(!accounts.length || !scopeStrings.length) { + return + } + + + await Promise.allSettled(scopeStrings.map(async scopeString => { + const {contractAddress} = BridgeableScopes[scopeString] + // console.log(`updating balance for contract ${contractAddress} on scope ${scopeString}`) + try { + const balance = await extensionPortRequest({ + method: "wallet_invokeMethod", + params: { + scope: scopeString, + request: { + "method": "eth_getBalance", + "params": [ + contractAddress, + "latest" + ], + }, + }, + }) + balances[scopeString] = balances[scopeString] || {} + const oldBalance = balances[scopeString][contractAddress] || 0 + balances[scopeString][contractAddress] = parseInt(balance, 16) / Math.pow(10, 18); + + if (oldBalance !== balances[scopeString][contractAddress]) { + // console.log(`updating node for contract ${contractAddress} on scope ${scopeString} with ${balances[scopeString][contractAddress]}`) + refreshNodeContent(scopeString) + } + + } catch (error) { + console.error(`failed updating balance for contract ${contractAddress} on scope ${scopeString}`, error) + } + })) +} +async function updateContractBalancesPoll() { + console.log("Polling: updating all contract balances") + await updateContractBalances() + setTimeout(() => { + updateContractBalancesPoll() + }, POLLING_RATE) +} + +async function updateTransactionStatusesForScope(scopeString) { + if(!extensionPort) { + return + } + if(!accounts.length || !scopeStrings.length) { + return + } + + const pendingTransactions = transactions.filter(transaction => !transaction.status) + + await Promise.allSettled(pendingTransactions.map(async pendingTransaction => { + const {fromScope, toScope, type, transactionHash } = pendingTransaction + + const isBridge = type === 'bridge' + const targetScope = isBridge ? fromScope : toScope + + if(targetScope !== scopeString) { + return + } + + console.log(`getting tx receipt for tx hash ${transactionHash} on scope ${targetScope}`) + try { + const receipt = await extensionPortRequest({ + method: "wallet_invokeMethod", + params: { + scope: targetScope, + request: { + "method": "eth_getTransactionReceipt", + "params": [ + transactionHash + ], + }, + }, + }) + + if(!receipt) { + return; + } + + pendingTransaction.status = receipt.status + const color = receipt.status === '0x1' ? 'green' : 'red' + updateEdgeColor(fromScope, toScope, color) + updateNodeBorderColor(isBridge ? fromScope : toScope, color) + setTimeout(() => { + updateEdgeColor(fromScope, toScope) + updateNodeBorderColor(isBridge ? fromScope : toScope) + }, FLASH_DURATION) + if (isBridge) { + setTimeout(() => { + claimBridgedEth(pendingTransaction) + }, FLASH_DURATION + 500) + } + console.log(`got tx receipt for tx hash ${transactionHash} on scope ${targetScope}`, receipt.status) + } catch (error) { + console.error(`failed getting tx receipt for tx hash ${transactionHash} on scope ${targetScope}`) + } + })) +} + +async function updateTransactionStatuses() { + if(!extensionPort) { + return + } + if(!accounts.length || !scopeStrings.length) { + return + } + + const pendingTransactions = transactions.filter(transaction => !transaction.status) + + await Promise.allSettled(pendingTransactions.map(async pendingTransaction => { + const {fromScope, toScope, type, transactionHash } = pendingTransaction + + const targetScope = type === 'bridge' ? fromScope : toScope + + console.log(`getting tx receipt for tx hash ${transactionHash} on scope ${targetScope}`) + try { + const receipt = await extensionPortRequest({ + method: "wallet_invokeMethod", + params: { + scope: targetScope, + request: { + "method": "eth_getTransactionReceipt", + "params": [ + transactionHash + ], + }, + }, + }) + + if(!receipt) { + return; + } + + pendingTransaction.status = receipt.status + const color = receipt.status === '0x1' ? 'green' : 'red' + updateEdgeColor(fromScope, toScope, color) + updateNodeBorderColor(isBridge ? fromScope : toScope, color) + setTimeout(() => { + updateEdgeColor(fromScope, toScope) + updateNodeBorderColor(isBridge ? fromScope : toScope) + }, FLASH_DURATION) + if (isBridge) { + setTimeout(() => { + claimBridgedEth(pendingTransaction) + }, FLASH_DURATION + 500) + } + console.log(`got tx receipt for tx hash ${transactionHash} on scope ${targetScope}`, receipt.status) + } catch (error) { + console.error(`failed getting tx receipt for tx hash ${transactionHash} on scope ${targetScope}`) + } + })) +} +async function updateTransactionStatusesPoll() { + console.log("Polling: updating all tx statuses") + await updateTransactionStatuses() + setTimeout(() => { + updateTransactionStatusesPoll() + }, POLLING_RATE) +} + +let isPolling; +async function startPolling() { + if (!isPolling) { + isPolling = true + updateTransactionStatusesPoll() + updateContractBalancesPoll() + updateAccountBalancesPoll() + } +} + +// Flash an edge +// setTimeout(() => { +// const randomLink = d3.select(link.nodes()[Math.floor(Math.random() * links.length)]); +// const originalColor = randomLink.attr("stroke"); +// randomLink.attr("stroke", "green"); +// setTimeout(() => { +// randomLink.attr("stroke", originalColor); +// }, 3000); +// }, 5000); + + +// setTimeout(() => { +// const randomLink = links[Math.floor(Math.random() * links.length)]; +// const linkSelection = d3.select(link.nodes()[links.indexOf(randomLink)]); +// const sourceNode = nodes.find(node => node.id === randomLink.source.id); +// const targetNode = nodes.find(node => node.id === randomLink.target.id); + +// const edgeLabel = svg.append("text") +// .attr("x", (sourceNode.x + targetNode.x) / 2) +// .attr("y", (sourceNode.y + targetNode.y) / 2) +// .attr("dy", -5) +// .attr("text-anchor", "middle") +// .attr("fill", "green") +// .text("sending..."); + +// setTimeout(() => { +// edgeLabel.remove(); +// }, 3000); +// }, 5000); diff --git a/src/multichain_demo_contract.sol b/src/multichain_demo_contract.sol new file mode 100644 index 00000000..9e84a446 --- /dev/null +++ b/src/multichain_demo_contract.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract RestrictedWithdrawal { + address public owner; + mapping(address => bool) public allowedAddresses; + uint256 public allowedCount; + + event Deposit(address indexed sender, uint256 amount); + event Withdrawal(address indexed recipient, uint256 amount); + event AllowedAddressUpdated(address indexed addr, bool allowed); + + modifier onlyOwner() { + require(msg.sender == owner, "Only owner can call this function"); + _; + } + + modifier onlyAllowed() { + require(allowedAddresses[msg.sender], "Not allowed to withdraw"); + _; + } + + constructor(address[] memory initialAllowedAddresses) { + owner = msg.sender; + for (uint256 i = 0; i < initialAllowedAddresses.length; i++) { + allowedAddresses[initialAllowedAddresses[i]] = true; + } + allowedCount = initialAllowedAddresses.length; + } + + receive() external payable { + emit Deposit(msg.sender, msg.value); + } + + function withdraw(uint256 amount) external onlyAllowed { + require(address(this).balance >= amount, "Insufficient balance"); + payable(msg.sender).transfer(amount); + emit Withdrawal(msg.sender, amount); + } + + function updateAllowedAddress(address addr, bool allowed) external onlyOwner { + if (allowed && !allowedAddresses[addr]) { + allowedCount++; + } else if (!allowed && allowedAddresses[addr]) { + allowedCount--; + } + allowedAddresses[addr] = allowed; + emit AllowedAddressUpdated(addr, allowed); + } + + function getContractBalance() external view returns (uint256) { + return address(this).balance; + } +} diff --git a/src/multichain_demo_contract_abi.json b/src/multichain_demo_contract_abi.json new file mode 100644 index 00000000..48b9f99a --- /dev/null +++ b/src/multichain_demo_contract_abi.json @@ -0,0 +1,163 @@ +[ + { + "inputs": [ + { + "internalType": "address[]", + "name": "initialAllowedAddresses", + "type": "address[]" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "addr", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "allowed", + "type": "bool" + } + ], + "name": "AllowedAddressUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Withdrawal", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "allowedAddresses", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "allowedCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getContractBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + }, + { + "internalType": "bool", + "name": "allowed", + "type": "bool" + } + ], + "name": "updateAllowedAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/src/multichain_demo_contract_bytecode.txt b/src/multichain_demo_contract_bytecode.txt new file mode 100644 index 00000000..2d0daa1c --- /dev/null +++ b/src/multichain_demo_contract_bytecode.txt @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50604051610d39380380610d39833981810160405281019061003291906102d6565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060005b81518110156100fc5760018060008484815181106100965761009561031f565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508080600101915050610075565b5080516002819055505061034e565b6000604051905090565b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61016d82610124565b810181811067ffffffffffffffff8211171561018c5761018b610135565b5b80604052505050565b600061019f61010b565b90506101ab8282610164565b919050565b600067ffffffffffffffff8211156101cb576101ca610135565b5b602082029050602081019050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061020c826101e1565b9050919050565b61021c81610201565b811461022757600080fd5b50565b60008151905061023981610213565b92915050565b600061025261024d846101b0565b610195565b90508083825260208201905060208402830185811115610275576102746101dc565b5b835b8181101561029e578061028a888261022a565b845260208401935050602081019050610277565b5050509392505050565b600082601f8301126102bd576102bc61011f565b5b81516102cd84826020860161023f565b91505092915050565b6000602082840312156102ec576102eb610115565b5b600082015167ffffffffffffffff81111561030a5761030961011a565b5b610316848285016102a8565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6109dc8061035d6000396000f3fe6080604052600436106100595760003560e01c80630e73684e146100b35780632e1a7d4d146100dc5780634120657a146101055780636f9fb98a1461014257806389d34e4b1461016d5780638da5cb5b14610198576100ae565b366100ae573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040516100a491906105bb565b60405180910390a2005b600080fd5b3480156100bf57600080fd5b506100da60048036038101906100d59190610671565b6101c3565b005b3480156100e857600080fd5b5061010360048036038101906100fe91906106dd565b6103e9565b005b34801561011157600080fd5b5061012c6004803603810190610127919061070a565b610550565b6040516101399190610746565b60405180910390f35b34801561014e57600080fd5b50610157610570565b60405161016491906105bb565b60405180910390f35b34801561017957600080fd5b50610182610578565b60405161018f91906105bb565b60405180910390f35b3480156101a457600080fd5b506101ad61057e565b6040516101ba9190610770565b60405180910390f35b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610251576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102489061080e565b60405180910390fd5b8080156102a85750600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16155b156102ca57600260008154809291906102c09061085d565b9190505550610340565b801580156103215750600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff165b1561033f5760026000815480929190610339906108a5565b91905055505b5b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff167f14fcc60af8440546881d552eb01644aa747a1106cb912daf1a3b52e9932ed189826040516103dd9190610746565b60405180910390a25050565b600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16610475576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161046c9061091a565b60405180910390fd5b804710156104b8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104af90610986565b60405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501580156104fe573d6000803e3d6000fd5b503373ffffffffffffffffffffffffffffffffffffffff167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b658260405161054591906105bb565b60405180910390a250565b60016020528060005260406000206000915054906101000a900460ff1681565b600047905090565b60025481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000819050919050565b6105b5816105a2565b82525050565b60006020820190506105d060008301846105ac565b92915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610606826105db565b9050919050565b610616816105fb565b811461062157600080fd5b50565b6000813590506106338161060d565b92915050565b60008115159050919050565b61064e81610639565b811461065957600080fd5b50565b60008135905061066b81610645565b92915050565b60008060408385031215610688576106876105d6565b5b600061069685828601610624565b92505060206106a78582860161065c565b9150509250929050565b6106ba816105a2565b81146106c557600080fd5b50565b6000813590506106d7816106b1565b92915050565b6000602082840312156106f3576106f26105d6565b5b6000610701848285016106c8565b91505092915050565b6000602082840312156107205761071f6105d6565b5b600061072e84828501610624565b91505092915050565b61074081610639565b82525050565b600060208201905061075b6000830184610737565b92915050565b61076a816105fb565b82525050565b60006020820190506107856000830184610761565b92915050565b600082825260208201905092915050565b7f4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f60008201527f6e00000000000000000000000000000000000000000000000000000000000000602082015250565b60006107f860218361078b565b91506108038261079c565b604082019050919050565b60006020820190508181036000830152610827816107eb565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610868826105a2565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361089a5761089961082e565b5b600182019050919050565b60006108b0826105a2565b9150600082036108c3576108c261082e565b5b600182039050919050565b7f4e6f7420616c6c6f77656420746f207769746864726177000000000000000000600082015250565b600061090460178361078b565b915061090f826108ce565b602082019050919050565b60006020820190508181036000830152610933816108f7565b9050919050565b7f496e73756666696369656e742062616c616e6365000000000000000000000000600082015250565b600061097060148361078b565b915061097b8261093a565b602082019050919050565b6000602082019050818103600083015261099f81610963565b905091905056fea26469706673582212204c5c5bc49f588ae9c18b9af6ec048481d900a81336d2241578883700d803512164736f6c634300081a0033 diff --git a/src/multichain_index.html b/src/multichain_index.html new file mode 100644 index 00000000..8342884c --- /dev/null +++ b/src/multichain_index.html @@ -0,0 +1,417 @@ + + + + + + Ethereum Multichain API Dapp + + +

Ethereum Multichain API Dapp

+ +
+ +
+
Connected Extension: Not available
+

+ + + + +
+ +
+ +

+
Connected Accounts: Not available
+

+ + +

+ +

+ +

+
+ + + diff --git a/webpack.config.js b/webpack.config.js index b6bfe381..7f3da68c 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -20,6 +20,7 @@ module.exports = { devtool: 'eval-source-map', mode: 'development', entry: { + multichain_demo: './src/multichain_demo.js', main: './src/index.js', request: './src/request.js', }, diff --git a/yarn.lock b/yarn.lock index 853e8563..eebac236 100644 --- a/yarn.lock +++ b/yarn.lock @@ -110,6 +110,11 @@ resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== +"@ethereumjs/rlp@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-5.0.2.tgz#c89bd82f2f3bec248ab2d517ae25f5bbc4aac842" + integrity sha512-DziebCdg4JpGlEqEdGgXmjqcFoJi+JGulUXwEjsZGAscAQ7MyD/7LE/GVCP29vEQxKc7AAwjT3A2ywHp2xfoCA== + "@ethereumjs/tx@^4.2.0": version "4.2.0" resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-4.2.0.tgz#5988ae15daf5a3b3c815493bc6b495e76009e853" @@ -1303,6 +1308,13 @@ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== +"@types/ws@8.5.3": + version "8.5.3" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" + integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w== + dependencies: + "@types/node" "*" + "@types/ws@^7.4.4": version "7.4.7" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" @@ -1904,6 +1916,11 @@ abbrev@^1.0.0: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abitype@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.7.1.tgz#16db20abe67de80f6183cf75f3de1ff86453b745" + integrity sha512-VBkRHTDZf9Myaek/dO3yMmOzB/y2s3Zo6nVU7yaw1G+TvCHAjwaJzNGN9yo4K5D8bU/VZXKP1EJpRhFr862PlQ== + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -2547,6 +2564,11 @@ colorette@^2.0.10, colorette@^2.0.14: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== +commander@7: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + commander@^10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" @@ -2641,7 +2663,7 @@ core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -crc-32@^1.2.0: +crc-32@^1.2.0, crc-32@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== @@ -2697,6 +2719,250 @@ csstype@^3.1.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== +"d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@3, d3-array@^3.2.0: + version "3.2.4" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5" + integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg== + dependencies: + internmap "1 - 2" + +d3-axis@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-3.0.0.tgz#c42a4a13e8131d637b745fc2973824cfeaf93322" + integrity sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw== + +d3-brush@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-3.0.0.tgz#6f767c4ed8dcb79de7ede3e1c0f89e63ef64d31c" + integrity sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ== + dependencies: + d3-dispatch "1 - 3" + d3-drag "2 - 3" + d3-interpolate "1 - 3" + d3-selection "3" + d3-transition "3" + +d3-chord@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-3.0.1.tgz#d156d61f485fce8327e6abf339cb41d8cbba6966" + integrity sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g== + dependencies: + d3-path "1 - 3" + +"d3-color@1 - 3", d3-color@3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2" + integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== + +d3-contour@4: + version "4.0.2" + resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-4.0.2.tgz#bb92063bc8c5663acb2422f99c73cbb6c6ae3bcc" + integrity sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA== + dependencies: + d3-array "^3.2.0" + +d3-delaunay@6: + version "6.0.4" + resolved "https://registry.yarnpkg.com/d3-delaunay/-/d3-delaunay-6.0.4.tgz#98169038733a0a5babbeda55054f795bb9e4a58b" + integrity sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A== + dependencies: + delaunator "5" + +"d3-dispatch@1 - 3", d3-dispatch@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e" + integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg== + +"d3-drag@2 - 3", d3-drag@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba" + integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg== + dependencies: + d3-dispatch "1 - 3" + d3-selection "3" + +"d3-dsv@1 - 3", d3-dsv@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-3.0.1.tgz#c63af978f4d6a0d084a52a673922be2160789b73" + integrity sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q== + dependencies: + commander "7" + iconv-lite "0.6" + rw "1" + +"d3-ease@1 - 3", d3-ease@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4" + integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w== + +d3-fetch@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-fetch/-/d3-fetch-3.0.1.tgz#83141bff9856a0edb5e38de89cdcfe63d0a60a22" + integrity sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw== + dependencies: + d3-dsv "1 - 3" + +d3-force@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-3.0.0.tgz#3e2ba1a61e70888fe3d9194e30d6d14eece155c4" + integrity sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg== + dependencies: + d3-dispatch "1 - 3" + d3-quadtree "1 - 3" + d3-timer "1 - 3" + +"d3-format@1 - 3", d3-format@3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641" + integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== + +d3-geo@3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-3.1.1.tgz#6027cf51246f9b2ebd64f99e01dc7c3364033a4d" + integrity sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q== + dependencies: + d3-array "2.5.0 - 3" + +d3-hierarchy@3: + version "3.1.2" + resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz#b01cd42c1eed3d46db77a5966cf726f8c09160c6" + integrity sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA== + +"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d" + integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== + dependencies: + d3-color "1 - 3" + +"d3-path@1 - 3", d3-path@3, d3-path@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.1.0.tgz#22df939032fb5a71ae8b1800d61ddb7851c42526" + integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ== + +d3-polygon@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-3.0.1.tgz#0b45d3dd1c48a29c8e057e6135693ec80bf16398" + integrity sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg== + +"d3-quadtree@1 - 3", d3-quadtree@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-3.0.1.tgz#6dca3e8be2b393c9a9d514dabbd80a92deef1a4f" + integrity sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw== + +d3-random@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-3.0.1.tgz#d4926378d333d9c0bfd1e6fa0194d30aebaa20f4" + integrity sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ== + +d3-scale-chromatic@3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz#34c39da298b23c20e02f1a4b239bd0f22e7f1314" + integrity sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ== + dependencies: + d3-color "1 - 3" + d3-interpolate "1 - 3" + +d3-scale@4: + version "4.0.2" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396" + integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ== + dependencies: + d3-array "2.10.0 - 3" + d3-format "1 - 3" + d3-interpolate "1.2.0 - 3" + d3-time "2.1.1 - 3" + d3-time-format "2 - 4" + +"d3-selection@2 - 3", d3-selection@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31" + integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ== + +d3-shape@3: + version "3.2.0" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.2.0.tgz#a1a839cbd9ba45f28674c69d7f855bcf91dfc6a5" + integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA== + dependencies: + d3-path "^3.1.0" + +"d3-time-format@2 - 4", d3-time-format@4: + version "4.1.0" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a" + integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg== + dependencies: + d3-time "1 - 3" + +"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7" + integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q== + dependencies: + d3-array "2 - 3" + +"d3-timer@1 - 3", d3-timer@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0" + integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== + +"d3-transition@2 - 3", d3-transition@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f" + integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w== + dependencies: + d3-color "1 - 3" + d3-dispatch "1 - 3" + d3-ease "1 - 3" + d3-interpolate "1 - 3" + d3-timer "1 - 3" + +d3-zoom@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3" + integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw== + dependencies: + d3-dispatch "1 - 3" + d3-drag "2 - 3" + d3-interpolate "1 - 3" + d3-selection "2 - 3" + d3-transition "2 - 3" + +d3@^7.9.0: + version "7.9.0" + resolved "https://registry.yarnpkg.com/d3/-/d3-7.9.0.tgz#579e7acb3d749caf8860bd1741ae8d371070cd5d" + integrity sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA== + dependencies: + d3-array "3" + d3-axis "3" + d3-brush "3" + d3-chord "3" + d3-color "3" + d3-contour "4" + d3-delaunay "6" + d3-dispatch "3" + d3-drag "3" + d3-dsv "3" + d3-ease "3" + d3-fetch "3" + d3-force "3" + d3-format "3" + d3-geo "3" + d3-hierarchy "3" + d3-interpolate "3" + d3-path "3" + d3-polygon "3" + d3-quadtree "3" + d3-random "3" + d3-scale "4" + d3-scale-chromatic "3" + d3-selection "3" + d3-shape "3" + d3-time "3" + d3-time-format "4" + d3-timer "3" + d3-transition "3" + d3-zoom "3" + date-fns@^2.29.3: version "2.30.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" @@ -2783,6 +3049,13 @@ del@^4.1.1: pify "^4.0.1" rimraf "^2.6.3" +delaunator@5: + version "5.0.1" + resolved "https://registry.yarnpkg.com/delaunator/-/delaunator-5.0.1.tgz#39032b08053923e924d6094fe2cde1a99cc51278" + integrity sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw== + dependencies: + robust-predicates "^3.0.2" + delay@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" @@ -4092,7 +4365,7 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.6.2: +iconv-lite@0.6, iconv-lite@^0.6.2: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -4158,6 +4431,11 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +"internmap@1 - 2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009" + integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== + interpret@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" @@ -4386,6 +4664,11 @@ isomorphic-ws@^4.0.1: resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== +isomorphic-ws@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" + integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== + jackspeak@^2.3.5: version "2.3.6" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" @@ -5763,6 +6046,11 @@ rlp@^2.0.0: dependencies: bn.js "^4.11.1" +robust-predicates@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.2.tgz#d5b28528c4824d20fc48df1928d41d9efa1ad771" + integrity sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg== + rpc-websockets@^9.0.2: version "9.0.4" resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-9.0.4.tgz#9d8ee82533b5d1e13d9ded729e3e38d0d8fa083f" @@ -5784,6 +6072,11 @@ run-parallel@^1.1.9: resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== +rw@1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" + integrity sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ== + rxjs@^6.6.3: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" @@ -5956,6 +6249,11 @@ set-function-length@^1.2.1: gopd "^1.0.1" has-property-descriptors "^1.0.2" +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + setprototypeof@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" @@ -6655,6 +6953,171 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" +web3-core@^4.4.0, web3-core@^4.5.1, web3-core@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-4.7.0.tgz#a109ed079e5f98a968487f4bb18c7aaf845f768a" + integrity sha512-skP4P56fhlrE+rIuS4WY9fTdja1DPml2xrrDmv+vQhPtmSFBs7CqesycIRLQh4dK1D4de/a23tkX6DLYdUt3nA== + dependencies: + web3-errors "^1.3.0" + web3-eth-accounts "^4.2.1" + web3-eth-iban "^4.0.7" + web3-providers-http "^4.2.0" + web3-providers-ws "^4.0.8" + web3-types "^1.8.1" + web3-utils "^4.3.2" + web3-validator "^2.0.6" + optionalDependencies: + web3-providers-ipc "^4.0.7" + +web3-errors@^1.1.3, web3-errors@^1.2.0, web3-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/web3-errors/-/web3-errors-1.3.0.tgz#504e4d3218899df108856940087a8022d6688d74" + integrity sha512-j5JkAKCtuVMbY3F5PYXBqg1vWrtF4jcyyMY1rlw8a4PV67AkqlepjGgpzWJZd56Mt+TvHy6DA1F/3Id8LatDSQ== + dependencies: + web3-types "^1.7.0" + +web3-eth-abi@^4.2.3, web3-eth-abi@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-4.3.0.tgz#9036541206418b9db6f1db295ad87a6f0fa0ed7d" + integrity sha512-OqZPGGxHmfKJt33BfpclEMmWvnnLJ/B+jVTnVagd2OIU1kIv09xf/E60ei0eGeG612uFy/pPq31u4RidF/gf6g== + dependencies: + abitype "0.7.1" + web3-errors "^1.3.0" + web3-types "^1.8.1" + web3-utils "^4.3.2" + web3-validator "^2.0.6" + +web3-eth-accounts@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-4.2.1.tgz#db27399137e1a26f9d467b9500019a70771f5724" + integrity sha512-aOlEZFzqAgKprKs7+DGArU4r9b+ILBjThpeq42aY7LAQcP+mSpsWcQgbIRK3r/n3OwTYZ3aLPk0Ih70O/LwnYA== + dependencies: + "@ethereumjs/rlp" "^4.0.1" + crc-32 "^1.2.2" + ethereum-cryptography "^2.0.0" + web3-errors "^1.3.0" + web3-types "^1.7.0" + web3-utils "^4.3.1" + web3-validator "^2.0.6" + +web3-eth-contract@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-4.7.0.tgz#119a744e8a35f60fd7bc3e4f8637f0380a3d0e85" + integrity sha512-fdStoBOjFyMHwlyJmSUt/BTDL1ATwKGmG3zDXQ/zTKlkkW/F/074ut0Vry4GuwSBg9acMHc0ycOiZx9ZKjNHsw== + dependencies: + "@ethereumjs/rlp" "^5.0.2" + web3-core "^4.5.1" + web3-errors "^1.3.0" + web3-eth "^4.8.2" + web3-eth-abi "^4.2.3" + web3-types "^1.7.0" + web3-utils "^4.3.1" + web3-validator "^2.0.6" + +web3-eth-iban@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-4.0.7.tgz#ee504f845d7b6315f0be78fcf070ccd5d38e4aaf" + integrity sha512-8weKLa9KuKRzibC87vNLdkinpUE30gn0IGY027F8doeJdcPUfsa4IlBgNC4k4HLBembBB2CTU0Kr/HAOqMeYVQ== + dependencies: + web3-errors "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" + web3-validator "^2.0.3" + +web3-eth@^4.8.2: + version "4.10.0" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-4.10.0.tgz#a62017908e543fe1bae398e2384bd29d1ebbbacd" + integrity sha512-8d7epCOm1hv/xGnOW8pWNkO5Ze9b+LKl81Pa1VUdRi2xZKtBaQsk+4qg6EnqeDF6SPpL502wNmX6TAB69vGBWw== + dependencies: + setimmediate "^1.0.5" + web3-core "^4.7.0" + web3-errors "^1.3.0" + web3-eth-abi "^4.3.0" + web3-eth-accounts "^4.2.1" + web3-net "^4.1.0" + web3-providers-ws "^4.0.8" + web3-rpc-methods "^1.3.0" + web3-types "^1.8.1" + web3-utils "^4.3.2" + web3-validator "^2.0.6" + +web3-net@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-4.1.0.tgz#db7bde675e58b153339e4f149f29ec0410d6bab2" + integrity sha512-WWmfvHVIXWEoBDWdgKNYKN8rAy6SgluZ0abyRyXOL3ESr7ym7pKWbfP4fjApIHlYTh8tNqkrdPfM4Dyi6CA0SA== + dependencies: + web3-core "^4.4.0" + web3-rpc-methods "^1.3.0" + web3-types "^1.6.0" + web3-utils "^4.3.0" + +web3-providers-http@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-4.2.0.tgz#0f4bf424681a068d49994aa7fabc69bed45ac50b" + integrity sha512-IPMnDtHB7dVwaB7/mMxAZzyq7d5ezfO1+Vw0bNfAeIi7gaDlJiggp85SdyAfOgov8AMUA/dyiY72kQ0KmjXKvQ== + dependencies: + cross-fetch "^4.0.0" + web3-errors "^1.3.0" + web3-types "^1.7.0" + web3-utils "^4.3.1" + +web3-providers-ipc@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-4.0.7.tgz#9ec4c8565053af005a5170ba80cddeb40ff3e3d3" + integrity sha512-YbNqY4zUvIaK2MHr1lQFE53/8t/ejHtJchrWn9zVbFMGXlTsOAbNoIoZWROrg1v+hCBvT2c9z8xt7e/+uz5p1g== + dependencies: + web3-errors "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" + +web3-providers-ws@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-4.0.8.tgz#6de7b262f7ec6df1a2dff466ba91d7ebdac2c45e" + integrity sha512-goJdgata7v4pyzHRsg9fSegUG4gVnHZSHODhNnn6J93ykHkBI1nz4fjlGpcQLUMi4jAMz6SHl9Ibzs2jj9xqPw== + dependencies: + "@types/ws" "8.5.3" + isomorphic-ws "^5.0.0" + web3-errors "^1.2.0" + web3-types "^1.7.0" + web3-utils "^4.3.1" + ws "^8.17.1" + +web3-rpc-methods@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/web3-rpc-methods/-/web3-rpc-methods-1.3.0.tgz#d5ee299a69389d63822d354ddee2c6a121a6f670" + integrity sha512-/CHmzGN+IYgdBOme7PdqzF+FNeMleefzqs0LVOduncSaqsppeOEoskLXb2anSpzmQAP3xZJPaTrkQPWSJMORig== + dependencies: + web3-core "^4.4.0" + web3-types "^1.6.0" + web3-validator "^2.0.6" + +web3-types@^1.3.0, web3-types@^1.6.0, web3-types@^1.7.0, web3-types@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/web3-types/-/web3-types-1.8.1.tgz#6379aca0f99330eb0f8f6ca80a3de93129b58339" + integrity sha512-isspsvQbBJFUkJYz2Badb7dz/BrLLLpOop/WmnL5InyYMr7kYYc8038NYO7Vkp1M7Bupa/wg+yALvBm7EGbyoQ== + +web3-utils@^4.0.7, web3-utils@^4.3.0, web3-utils@^4.3.1, web3-utils@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-4.3.2.tgz#a952428d677b635fd0c16044ae4c534807a39639" + integrity sha512-bEFpYEBMf6ER78Uvj2mdsCbaLGLK9kABOsa3TtXOEEhuaMy/RK0KlRkKoZ2vmf/p3hB8e1q5erknZ6Hy7AVp7A== + dependencies: + ethereum-cryptography "^2.0.0" + eventemitter3 "^5.0.1" + web3-errors "^1.3.0" + web3-types "^1.8.1" + web3-validator "^2.0.6" + +web3-validator@^2.0.3, web3-validator@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/web3-validator/-/web3-validator-2.0.6.tgz#a0cdaa39e1d1708ece5fae155b034e29d6a19248" + integrity sha512-qn9id0/l1bWmvH4XfnG/JtGKKwut2Vokl6YXP5Kfg424npysmtRLe9DgiNBM9Op7QL/aSiaA0TVXibuIuWcizg== + dependencies: + ethereum-cryptography "^2.0.0" + util "^0.12.5" + web3-errors "^1.2.0" + web3-types "^1.6.0" + zod "^3.21.4" + "webextension-polyfill@>=0.10.0 <1.0": version "0.11.0" resolved "https://registry.yarnpkg.com/webextension-polyfill/-/webextension-polyfill-0.11.0.tgz#1640c0d27192424fd5b420237acbe453f88c8246" @@ -6931,6 +7394,11 @@ ws@^8.13.0, ws@^8.5.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== +ws@^8.17.1: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== + ws@~8.11.0: version "8.11.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" @@ -7021,3 +7489,8 @@ yargs@^17.0.1: string-width "^4.2.3" y18n "^5.0.5" yargs-parser "^21.0.0" + +zod@^3.21.4: + version "3.23.8" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" + integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==