Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DRAFT: Jl/1193 multichain example dapp #372

Draft
wants to merge 20 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
209 changes: 209 additions & 0 deletions src/eip1193_index.html
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2024-11-01 at 2 04 28 PM

simple example eip1193 dapp to illustrate chain switching friction

Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ethereum EIP-1193 API Dapp</title>
</head>
<body>
<h1>Ethereum EIP-1193 API Dapp</h1>
<button id="connectButton">Connect Wallet</button>
<br>
<div id="connectedAccounts">Connected Accounts: Not available</div>
<br><br>
<label for="chainSelect">Select Chain:</label>
<select id="chainSelect">
<option value="0x1">Ethereum Mainnet</option>
<option value="0xe708">Linea Mainnet</option>
<option value="0xaa36a7">Sepolia Testnet</option>
<option value="0xe705">Linea Sepolia</option>
</select>
<br><br>
<button id="sendTxButton">Send 0 Ether to Null Address</button>
<br><br>
<div id="currentChain">Current Chain: Not available</div>
<div id="blockHead">Current Block Head: Not available</div>
<script>
let accounts = [];

// Dapp <-> Wallet Connection Initialization
console.log(window.ethereum); // this is handled for us already

// Dapp Initialization
checkWalletConnection();

// Subscription Events
window.ethereum.on("message", (message) => {
if (message.type === "eth_subscription") {
handleEthSubscriptionDisplay(message.data.result.number);
}
console.log(message.data);
});

// Permission Events/Handling
window.ethereum.on("chainChanged", async (chainId) => {
const selectedChainId = document.getElementById("chainSelect").value;
if (chainId !== selectedChainId) {
alert(`Wallet has changed the current chain to ${chainId}. Will try to change it back to ${selectedChainId} now.`);
await switchChain(selectedChainId);
}
document.getElementById("blockHead").innerText = "Current Block Head: Not available";
await updateCurrentChainDisplay();
await subscribeToBlockHeaders();
});

window.ethereum.on("accountsChanged", async (newAccounts) => {
accounts = newAccounts;
updateConnectedAccountsDisplay();
});

// Permission Initialization
async function connectWallet() {
try {
accounts = await window.ethereum.request({
method: "eth_requestAccounts",
});

updateConnectedAccountsDisplay();

const selectedChainId = document.getElementById("chainSelect").value;
await switchChain(selectedChainId);
await updateCurrentChainDisplay();
await subscribeToBlockHeaders();
} catch (error) {
console.error(error);
alert("Failed to connect wallet!");
}
}

async function checkWalletConnection() {
try {
accounts = await window.ethereum.request({
method: "eth_accounts",
});
if (accounts.length > 0) {
updateConnectedAccountsDisplay();
await updateCurrentChainDisplay();
await subscribeToBlockHeaders();
}
} catch (error) {
console.error("Failed to check wallet connection:", error);
alert("Failed to check wallet connection");
}
}

//
// Example Usage
//

// Transaction
async function sendTransaction() {
if (accounts.length === 0) {
alert("Please connect your wallet first!");
return;
}

const selectedChainId = document.getElementById("chainSelect").value;
const currentChainId = await getCurrentChainId();

// Switch to the selected chain if not already on it
if (currentChainId !== selectedChainId) {
alert(
`The wallet's current chain ${currentChainId} does not match the dapp expectations. Will try to change to the expected chain ${selectedChainId} now.`,
);
await switchChain(selectedChainId);
}

const transactionParameters = {
to: "0x0000000000000000000000000000000000000000",
from: accounts[0],
value: "0x0",
};

try {
alert(
`Will make eth_sendTransaction request intended for ${selectedChainId} after this alert is dismissed.`,
);
await window.ethereum.request({
method: "eth_sendTransaction",
params: [transactionParameters],
});
alert("Transaction sent!");
} catch (error) {
console.error(error);
alert("Transaction failed!");
}
}

// Subscription
async function subscribeToBlockHeaders() {
try {
await window.ethereum.request({
method: "eth_subscribe",
params: ["newHeads"],
});
} catch (error) {
console.error("Failed to subscribe to new block headers:", error);
alert("Failed to subscribe to new block headers");
}
}

//
// EIP-1193 specific chain helpers
//

async function switchChain(chainId) {
try {
await window.ethereum.request({
method: "wallet_switchEthereumChain",
params: [
{
chainId: chainId,
},
],
});
} catch (switchError) {
console.error(switchError);
}
}

async function getCurrentChainId() {
return await window.ethereum.request({
method: "eth_chainId",
});
}

//
// Everything below is ignorable boilerplate
//

// DOM
document.getElementById("connectButton").addEventListener("click", connectWallet);
document.getElementById("chainSelect").addEventListener("change", async (event) => {
const chainId = event.target.value;
await switchChain(chainId);
await subscribeToBlockHeaders();
await updateCurrentChainDisplay();
});
document.getElementById("sendTxButton").addEventListener("click", sendTransaction);

async function updateCurrentChainDisplay() {
const currentChainId = await getCurrentChainId();
document.getElementById("currentChain").innerText = `Current Chain: ${currentChainId}`;
}

function updateConnectedAccountsDisplay() {
document.getElementById("connectedAccounts").innerText = `Connected Accounts: ${accounts.join(", ")}`;
}

async function handleEthSubscriptionDisplay(value = "Not available") {
const blockHeadDisplay = document.getElementById("blockHead");
blockHeadDisplay.innerText = `Current Block Head: ${value}`;
blockHeadDisplay.style.color = "red";
setTimeout(() => {
blockHeadDisplay.style.color = "black";
}, 1500);
}
</script>
</body>
</html>
88 changes: 88 additions & 0 deletions src/multichain_demo.html
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2024-11-01 at 2 05 56 PM

More complicated bridging demo dapp to highlight some of the features enabled by the Multichain API.

Note this code is garbage spaghetti nonsense because I don't know how to use d3 and relied on chatgpt to start the project which was probably a mistake. And then I just kept bandaging things on in an effort to finish this before I am OOO. So... sorry lol

Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Multichain API EVM Bridging Demo Dapp</title>
<style>
body {
margin: 0;
overflow: hidden;
}
svg {
width: 100vw;
height: 100vh;
}
.modal {
display: none;
position: fixed;
z-index: 2;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgb(0,0,0);
background-color: rgba(0,0,0,0.4);
padding-top: 60px;
}
.modal-content {
background-color: #fefefe;
margin: 5% auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
}
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
.floating-div {
position: absolute;
top: 0;
right: 0;
width: 150px;
background-color: lightgray;
z-index: 1;
padding: 5px;
}
</style>
</head>
<body>
<!-- Floating Div -->
<div class="floating-div">
<input type="text" id="connectExtensionInput" value="nonfpcflonapegmnfeafnddgdniflbnk" />
<br>
<button id="connectExtensionButton">Connect Extension</button>
<button id="connectButton">Connect Wallet</button>
<br><br>
<a href="https://chainlist.org/chain/11155111" target="_blank">Sepolia</a>
<br>
<a href="https://chainlist.org/chain/59141" target="_blank">Linea Sepolia</a>
<br>
<a href="https://chainlist.org/chain/421614" target="_blank">Arbitrum Sepolia</a>
<br>
<a href="https://chainlist.org/chain/11155420" target="_blank">OP Sepolia</a>
<br>
<a href="https://chainlist.org/chain/168587773" target="_blank">Blast Sepolia</a>
</div>

<!-- Modal Structure -->
<div id="myModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<div id="modal-text"></div>
</div>
</div>

<script src="multichain_demo.js" defer></script>
</body>
</html>
Loading
Loading