Skip to content

Commit

Permalink
Merge pull request #752 from OpenZeppelin/751-consider-avoiding-auto-…
Browse files Browse the repository at this point in the history
…connect-to-metamask

➕ Add connect MetaMask modal
  • Loading branch information
GianfrancoBazzani authored Sep 3, 2024
2 parents 68ad817 + 22d4ee3 commit 06dc0b4
Show file tree
Hide file tree
Showing 18 changed files with 294 additions and 106 deletions.
3 changes: 3 additions & 0 deletions client/src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ export const setNetworkId = id => ({ type: SET_NETWORK_ID, id })
export const SET_PLAYER_ADDRESS = "SET_PLAYER_ADDRESS";
export const setPlayerAddress = address => ({ type: SET_PLAYER_ADDRESS, address })

export const SET_GAME_READ_ONLY = "SET_GAME_READ_ONLY";
export const setGameReadOnly = readOnly => ({ type: SET_GAME_READ_ONLY, readOnly })

export const LOAD_GAME_DATA = "LOAD_GAME_DATA";
export const loadGamedata = () => ({ type: LOAD_GAME_DATA, levels: undefined })

Expand Down
75 changes: 66 additions & 9 deletions client/src/containers/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import {
deprecationDate,
} from "../utils/networkDeprecation";
import { Helmet } from "react-helmet";

import { store } from "./../store";
import * as actions from "../../src/actions";

class App extends React.Component {
constructor() {
Expand Down Expand Up @@ -61,6 +62,35 @@ class App extends React.Component {
this.props.navigate(`${constants.PATH_LEVEL_ROOT}${target}`);
}

async continueAnyway() {
const deployWindow = document.querySelectorAll(".deploy-window-bg");
deployWindow[0].style.display = "none";
}

async continueInReadOnly() {
store.dispatch(actions.loadGamedata());
store.dispatch(actions.setGameReadOnly(true));
const accountConnectionWindow = document.querySelectorAll(
".account-connection-window-bg"
);
accountConnectionWindow[0].style.display = "none";
}

async displayConnectionWindow() {
const accountConnectionWindow = document.querySelectorAll(
".account-connection-window-bg"
);
accountConnectionWindow[0].style.display = "block";
}

async requestAccounts() {
await window.ethereum.request({ method: "eth_requestAccounts" });
const accountConnectionWindow = document.querySelectorAll(
".account-connection-window-bg"
);
accountConnectionWindow[0].style.display = "none";
}

render() {
let language = localStorage.getItem("lang");
let strings = loadTranslations(language);
Expand Down Expand Up @@ -117,11 +147,6 @@ class App extends React.Component {
}
}

async function continueAnyway() {
const deployWindow = document.querySelectorAll(".deploy-window-bg");
deployWindow[0].style.display = "none";
}

return (
<div className="appcontainer">
<Helmet>
Expand Down Expand Up @@ -180,21 +205,53 @@ class App extends React.Component {
/>
<ul>
<button
onClick={() => this.navigateToFirstIncompleteLevel()}
onClick={() => {
if (!store.getState().gamedata.readOnly) {
this.navigateToFirstIncompleteLevel();
} else {
this.displayConnectionWindow();
}
}}
className="buttons"
>
{strings.playNow}
</button>
</ul>
</section>
{/*not Account Connected window*/}
<div className="account-connection-window-bg">
<div className="account-connection-window">
<button
className="account-connection-close-x fas fa-x "
onClick={this.continueInReadOnly}
>
</button>
<h1>{randBadIcon()}</h1>
<br />
<h2>{strings.accountNotConnectedTitle}</h2>
<br />
<p>{strings.accountNotConnectedMessage}</p>
<br />
<div className="choice-buttons">
<button
className="buttons"
onClick={async () => {
await this.requestAccounts();
window.location.reload();
}}
>
{strings.connectAccount}
</button>
</div>
</div>
</div>
{/*not Deployed window*/}
<div className="deploy-window-bg">
{!networkOnDeprecationOrDeprecated(this.state.chainId) ? (
<div className="deploy-window">
{/*deploy window*/}
<h1>{randGoodIcon()}</h1>
<h2>{strings.deployMessageTitle}</h2>
<br />
{strings.deployMessage}
{supportedNetworksList(supportedNetworks)}
<p className="deploy-note">{strings.deployConfirmation}</p>
Expand Down Expand Up @@ -225,7 +282,7 @@ class App extends React.Component {
{strings.switchToSepolia}
</button>
{!isDeprecatedNetwork(this.state.chainId) && (
<button className="buttons" onClick={continueAnyway}>
<button className="buttons" onClick={this.continueAnyway}>
{strings.continueAnyway}
</button>
)}
Expand Down
93 changes: 54 additions & 39 deletions client/src/containers/Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from "react";
import onClickOutside from "react-onclickoutside";
import { connect } from "react-redux";
import { withRouter } from "../hoc/withRouter";
import { Link } from "react-router-dom";
import { Link, Navigate } from "react-router-dom";
import { bindActionCreators } from "redux";
import * as actions from "../actions";
import * as constants from "../constants";
Expand All @@ -11,6 +11,7 @@ import PropTypes from "prop-types";
import { ProgressBar } from "react-loader-spinner";
import { svgFilter } from "../utils/svg";
import LeaderIcon from "../components/leaderboard/LeaderIcon";
import { store } from "../store";
// import parse from "html-react-parser";

class Header extends React.Component {
Expand All @@ -24,7 +25,7 @@ class Header extends React.Component {
multiDDOpen: false,
};

if (this.props.web3) {
if (this.props.web3 && !store.getState().gamedata.readOnly) {
window.ethereum.request({ method: "eth_chainId" }).then((id) => {
this.setState({ chainId: Number(id) });
});
Expand Down Expand Up @@ -325,7 +326,8 @@ class Header extends React.Component {
</Link>
</div>
{window.location.pathname === constants.PATH_ROOT &&
!!this.props.web3 && (
!!this.props.web3 &&
!store.getState().gamedata.readOnly && (
<Link
onClick={() => this.toggleDropdownState()}
to={constants.PATH_LEADERBOARD}
Expand All @@ -342,45 +344,58 @@ class Header extends React.Component {
className="element-in-row toggle --small"
type="checkbox"
/>
<div className="connect-button">
{store.getState().gamedata.readOnly && (
<button
className="buttons"
onClick={async () => {
await window.ethereum.request({ method: "eth_requestAccounts" });
window.location.reload();
}}
>
{strings.connectAccount}
</button>
)}
</div>
</div>
</div>

<div
className={`single-dropdown --${
this.props.web3 && "--hidden"
}`}
>
<p onClick={() => this.setActiveTab(2)}>
<i className="fas fa-network-wired"></i>
<span>{strings.Networks}</span>
</p>
<div className={this.getDDClassName(2)}>
{Object.values(constants.NETWORKS_INGAME).map(
(network, index) => {
if (network && network.name !== "local") {
if (Number(network.id) === this.state.chainId)
return false; // filter out current network
return (
<div
key={index}
onClick={(e) => {
e.preventDefault();
this.changeNetwork(network);
}}
className="dropdown-pill"
>
<a id={network.name} key={network.name} href="/">
{network.name}
</a>
</div>
);
{this.props.web3 && !store.getState().gamedata.readOnly && (
<div className={`single-dropdown`}>
<p onClick={() => this.setActiveTab(2)}>
<i className="fas fa-network-wired"></i>
<span>{strings.Networks}</span>
</p>
<div className={this.getDDClassName(2)}>
{Object.values(constants.NETWORKS_INGAME).map(
(network, index) => {
if (network && network.name !== "local") {
if (Number(network.id) === this.state.chainId)
return false; // filter out current network
return (
<div
key={index}
onClick={(e) => {
e.preventDefault();
this.changeNetwork(network);
}}
className="dropdown-pill"
>
<a
id={network.name}
key={network.name}
href="/"
>
{network.name}
</a>
</div>
);
}
return null;
}
return null;
}
)}
)}
</div>
</div>
</div>

)}
<div className="single-dropdown">
<p onClick={() => this.setActiveTab(1)}>
<i className="fas fa-globe-americas"></i>
Expand Down Expand Up @@ -438,7 +453,7 @@ class Header extends React.Component {
wrapperClass="progress-bar-wrapper"
visible={true}
/>
{!this.props.web3 && (
{!this.props.web3 && !store.getState().gamedata.readOnly && (
<div
style={{ backgroundColor: "#eddfd6", border: "none" }}
className="alert alert-warning"
Expand Down
3 changes: 2 additions & 1 deletion client/src/containers/Level.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { getLevelKey } from "../utils/contractutil";
import { deployAndRegisterLevel } from "../utils/deploycontract";
import { svgFilter } from "../utils/svg";
import { Helmet } from 'react-helmet';
import { store } from "../store";

class Level extends React.Component {
constructor(props) {
Expand Down Expand Up @@ -331,7 +332,7 @@ class Level extends React.Component {
)}

{/* DEPLOY OR CREATE */}
{this.props.web3 && <button
{ !store.getState().gamedata.readOnly && this.props.web3 && <button
type="button"
className="button-actions"
onClick={
Expand Down
3 changes: 3 additions & 0 deletions client/src/gamedata/ar/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"sources": "مصادر",
"submitInstance": "تسليم النسخة",
"getNewInstance": "احصل على نسخة جديدة",
"accountNotConnectedTitle": "حساب MetaMask غير متصل",
"accountNotConnectedMessage": "لبدء مغامرتك، قم بتوصيل محفظة MetaMask الخاصة بك! تتفاعل لعبتنا بشكل مباشر مع عقود Ethernaut على السلسلة، مما يعني أن أفعالك في اللعبة مسجلة على blockchain. من خلال ربط حساب MetaMask الخاص بك، يمكنك التفاعل بأمان مع هذه العقود الذكية، مما يسمح لك بحل التحديات والتقدم في اللعبة.",
"connectAccount": "يتصل",
"deployMessageTitle": "لم يتم نشر اللعبة",
"deployMessage": "تدُعم اللُعبة حاليًا هذه الشبكات فقط:",
"deprecatedNetwork": "شبكة عفا عليها الزمن",
Expand Down
3 changes: 3 additions & 0 deletions client/src/gamedata/en/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"sources": "Sources",
"submitInstance": "Submit instance",
"getNewInstance": "Get new instance",
"accountNotConnectedTitle": "MetaMask account not connected",
"accountNotConnectedMessage": "To begin your adventure, connect your MetaMask wallet! Our game interacts directly with Ethernaut on-chain contracts, which means that your actions in the game are recorded on the blockchain. By linking your MetaMask account, you can securely engage with these smart contracts, allowing you to solve challenges, and progress in the game.",
"connectAccount": "Connect",
"deployMessageTitle": "Game not deployed",
"deprecatedNetwork": "Network deprecated",
"networkBeingDeprecated": "Network being deprecated",
Expand Down
3 changes: 3 additions & 0 deletions client/src/gamedata/es/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"sources": "Código",
"submitInstance": "Comprueba instancia",
"getNewInstance": "Nueva instancia",
"accountNotConnectedTitle": "Cuenta MetaMask no conectada",
"accountNotConnectedMessage": "Para comenzar tu aventura, conecta tu billetera MetaMask. Nuestro juego interactúa directamente con los contratos en cadena de Ethernaut, lo que significa que tus acciones en el juego quedan registradas en la cadena de bloques. Al vincular tu cuenta MetaMask, puedes interactuar de forma segura con estos contratos inteligentes, lo que te permite resolver desafíos y progresar en el juego.",
"connectAccount": "Conectar",
"deployMessageTitle": "Juego no desplegado",
"deprecatedNetwork": "Red obsoleta",
"networkBeingDeprecated": "Red en desuso",
Expand Down
3 changes: 3 additions & 0 deletions client/src/gamedata/fr/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"sources": "Sources",
"submitInstance": "Soumettre une instance",
"getNewInstance": "Créer une nouvelle instance",
"accountNotConnectedTitle": "Compte MetaMask non connecté",
"accountNotConnectedMessage": "Pour commencer votre aventure, connectez votre portefeuille MetaMask ! Notre jeu interagit directement avec les contrats on-chain d'Ethernaut, ce qui signifie que vos actions dans le jeu sont enregistrées sur la blockchain. En liant votre compte MetaMask, vous pouvez interagir en toute sécurité avec ces contrats intelligents, vous permettant de résoudre des défis et de progresser dans le jeu.",
"connectAccount": "Connecter",
"deployMessageTitle": "Jeu non déployé",
"deprecatedNetwork": "Réseau obsolète",
"networkBeingDeprecated": "Réseau désaffecté",
Expand Down
3 changes: 3 additions & 0 deletions client/src/gamedata/ja/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"sources": "ソース",
"submitInstance": "インスタンスの提出",
"getNewInstance": "インスタンスの生成",
"accountNotConnectedTitle": "MetaMask アカウントが接続されていません",
"accountNotConnectedMessage": "冒険を始めるには、MetaMask ウォレットを接続してください。私たちのゲームは Ethernaut オンチェーン コントラクトと直接やり取りします。つまり、ゲーム内でのアクションはブロックチェーンに記録されます。MetaMask アカウントをリンクすることで、これらのスマート コントラクトに安全に関与し、課題を解決してゲームを進めることができます。",
"connectAccount": "接続する",
"deployMessageTitle": "ゲームが展開されていません",
"deployMessage": "現在、ゲームはこれらのネットワークのみをサポートしています:",
"deprecatedNetwork": "ネットワークは放棄されています",
Expand Down
3 changes: 3 additions & 0 deletions client/src/gamedata/pt_br/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"sources": "Código",
"submitInstance": "Enviar instância",
"getNewInstance": "Obter nova instância",
"accountNotConnectedTitle": "Conta MetaMask não ligada",
"accountNotConnectedMessage": "Para começar a sua aventura, ligue a sua carteira MetaMask! O nosso jogo interage diretamente com os contratos on-chain do Ethernaut, o que significa que as suas ações no jogo são registadas na blockchain. Ao ligar a sua conta MetaMask, pode interagir com segurança com estes contratos inteligentes, permitindo-lhe resolver desafios e progredir no jogo.",
"connectAccount": "Ligar",
"deployMessageTitle": "Jogo não implantado",
"deprecatedNetwork": "Rede obsoleta",
"networkBeingDeprecated": "Rede sendo obsoleta",
Expand Down
3 changes: 3 additions & 0 deletions client/src/gamedata/ru/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"sources": "Исходный код",
"submitInstance": "Отправить инстанс на проверку",
"getNewInstance": "Создать новый инстанс",
"accountNotConnectedTitle": "Учетная запись MetaMask не подключена",
"accountNotConnectedMessage": "Чтобы начать приключение, подключите свой кошелек MetaMask! Наша игра напрямую взаимодействует с ончейн-контрактами Ethernaut, что означает, что ваши действия в игре записываются в блокчейн. Подключив свой аккаунт MetaMask, вы можете безопасно взаимодействовать с этими смарт-контрактами, что позволит вам решать задачи и продвигаться в игре.",
"connectAccount": "Bağlamak",
"deployMessageTitle": "Игра не развернута",
"deprecatedNetwork": "заброшенная сеть",
"networkBeingDeprecated": "сеть в процессе закрытия",
Expand Down
3 changes: 3 additions & 0 deletions client/src/gamedata/tr/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"sources": "Kaynaklar",
"submitInstance": "Durum gönder",
"getNewInstance": "Yeni durum al",
"accountNotConnectedTitle": "MetaMask hesabı bağlı değil",
"accountNotConnectedMessage": "Maceranıza başlamak için MetaMask cüzdanınızı bağlayın! Oyunumuz doğrudan Ethernaut zincir üstü sözleşmeleriyle etkileşime girer, bu da oyundaki eylemlerinizin blok zincirinde kaydedildiği anlamına gelir. MetaMask hesabınızı bağlayarak bu akıllı sözleşmelerle güvenli bir şekilde etkileşime girebilir, zorlukları çözebilir ve oyunda ilerleyebilirsiniz.",
"connectAccount": "Bağlamak",
"deployMessageTitle": "Oyun dağıtılmadı",
"deprecatedNetwork": "Ağ kullanımdan kaldırıldı",
"networkBeingDeprecated": "Ağ kullanımdan kaldırılıyor",
Expand Down
3 changes: 3 additions & 0 deletions client/src/gamedata/zh_cn/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"sources": "源码",
"submitInstance": "提交该实例",
"getNewInstance": "生成新实例",
"accountNotConnectedTitle": "MetaMask 帐户未连接",
"accountNotConnectedMessage": "要开始您的冒险,请连接您的 MetaMask 钱包!我们的游戏直接与 Ethernaut 链上合约交互,这意味着您在游戏中的行为会记录在区块链上。通过链接您的 MetaMask 帐户,您可以安全地与这些智能合约互动,从而让您解决​​挑战并在游戏中取得进展。",
"connectAccount": "连接",
"deployMessageTitle": "Game not deployed",
"deprecatedNetwork": "Network deprecated",
"networkBeingDeprecated": "Network being deprecated",
Expand Down
Loading

0 comments on commit 06dc0b4

Please sign in to comment.