百亿项目 Alchemy Road to Web3 第六周 NFT 获取教程
2022-08-18 11:26
Greta
2022-08-18 11:26
订阅此专栏
收藏此文章

Alchemy 是什么项目?

2019 年 12 月,Alchemy 完成1500 万美元 A 轮融资,资方为 Pantera Capital,斯坦福大学,Coinbase,三星等。

2021 年 4 月,Alchemy 以 5.05 亿美元估值完成8000 万美元 B 轮融资,Coatue 和 Addition 领投,DFJ Growth、K5 Global、Chainsmokers(烟鬼组合)、演员 Jared Leto 和 Glazer 家族参投。

2021 年 10 月,Alchemy 以 35 亿美元估值完成2.5 亿美元 C 轮融资,由 a16z 领投的。

2022 年 2 月,Alchemy 以 102 亿美元估值完成2 亿美元融资,Lightspeed 与 Silver Lake 领投。

Alchemy 是一个背景强大、经费充足、踏实做事、没有发币的团队,这样的项目不刷,难道去刷土狗吗?

并且,Alchemy 计划将新资金用于推广 Web3 采用,这方面的一些举措包括推出 Web3 University,就是现在的 Road to Web3 活动,活动为期 10 周,每周一个 NFT。看了下 nft 数量极少,估计由于任务难度大,很多小伙伴直接放弃,这样的项目若是空投,绝对是大毛。

手把手第六周教程开始:. 如何构建 Staking Dapp

step1 下载 git 并安装

从 git 官网下载 windows 版本的 git:http://git-scm.com/downloads

一般使用默认设置即可:一路 next,最后 install,git 安装完毕!

step2 下载 Scaffold-Eth

1.在控制台输入 git clone https://github.com/scaffold-eth/scaffold-eth-challenges.git challenge-1-decentralized-staking ,按回车,等一会下载完毕。

2.控制台输入 cd challenge-1-decentralized-staking,再按回车。

3.控制台输入 git checkout challenge-1-decentralized-staking ,按回车。

4.控制台输入 yarn install ,按回车,多等一会。

5.都装好后,在 challenge-1-decentralized-staking 这个文件夹中应该能看到这样的目录。

注意:在本教程中,我们将主要致力于 Staker.solApp.jsx.这两个文件的代码编写。

step3 设置环境

注意,接下来用到三个单独的控制台,我开了三个控制台。

1.在控制台输入 yarn chain,启动您的安全帽后端。

2.再开一个控制台,cd 进入那个文件夹,在控制台输入 yarn deploy。

3.再开一个控制台,cd 进入那个文件夹,在控制台输入 yarn start ,启动你的 React 前端。

然后我们就可以看到网页跳出了这个页面!!!

step4 熟悉 Scaffold-Eth(只拿 NFT,这步可以不管)

在我们的默认视图中,我们有两个选项卡—— Staker UI& Debug Contracts.

1.往本地钱包发送测试币,先不要点那个连接!复制右上角的地址(这就是本地地址),然后粘贴到右下角,点击小飞机。

2.可以看到下面有提示,并且左上角余额发生变化。

3.或者这里也可以,点击 send。充值本地钱包后,您将能够与您的合约进行交互!

step5 深入研究 Solidity

1.打开 vscode,点击 open folder,找到你创建的那个文件夹,点击选择文件夹。

2.找到 packages——hardhat——contracts——Staker.sol

3.将下面的代码直接复制粘贴进去。(官方链接对每一步都做了解释,很有意思,感兴趣的可以看一下每段代码对应的模块功能, 而不是单纯的粘贴最终代码哈哈,但是用官方代码的话,第 78 行的rewardRatePerBlock要改成rewardRatePerSecond,下面这个是改好的)

// SPDX-License-Identifier: MITpragma solidity 0.8.4;import "hardhat/console.sol";import "./ExampleExternalContract.sol";contract Staker {  ExampleExternalContract public exampleExternalContract;  mapping(address => uint256) public balances;  mapping(address => uint256) public depositTimestamps;  uint256 public constant rewardRatePerSecond = 0.1 ether;  uint256 public withdrawalDeadline = block.timestamp + 120 seconds;  uint256 public claimDeadline = block.timestamp + 240 seconds;  uint256 public currentBlock = 0;  // Events  event Stake(address indexed sender, uint256 amount);  event Received(address, uint);  event Execute(address indexed sender, uint256 amount);  // Modifiers  /*  Checks if the withdrawal period has been reached or not  */  modifier withdrawalDeadlineReached( bool requireReached ) {    uint256 timeRemaining = withdrawalTimeLeft();    if( requireReached ) {      require(timeRemaining == 0, "Withdrawal period is not reached yet");    } else {      require(timeRemaining > 0, "Withdrawal period has been reached");    }    _;  }  /*  Checks if the claim period has ended or not  */  modifier claimDeadlineReached( bool requireReached ) {    uint256 timeRemaining = claimPeriodLeft();    if( requireReached ) {      require(timeRemaining == 0, "Claim deadline is not reached yet");    } else {      require(timeRemaining > 0, "Claim deadline has been reached");    }    _;  }  /*  Requires that the contract only be completed once!  */  modifier notCompleted() {    bool completed = exampleExternalContract.completed();    require(!completed, "Stake already completed!");    _;  }  constructor(address exampleExternalContractAddress){      exampleExternalContract = ExampleExternalContract(exampleExternalContractAddress);  }  // Stake function for a user to stake ETH in our contract  function stake() public payable withdrawalDeadlineReached(false) claimDeadlineReached(false){    balances[msg.sender] = balances[msg.sender] + msg.value;    depositTimestamps[msg.sender] = block.timestamp;    emit Stake(msg.sender, msg.value);  }  /*  Withdraw function for a user to remove their staked ETH inclusive  of both principal and any accrued interest  */  function withdraw() public withdrawalDeadlineReached(true) claimDeadlineReached(false) notCompleted{    require(balances[msg.sender] > 0, "You have no balance to withdraw!");    uint256 individualBalance = balances[msg.sender];    uint256 indBalanceRewards = individualBalance + ((block.timestamp-depositTimestamps[msg.sender])*rewardRatePerSecond);    balances[msg.sender] = 0;    // Transfer all ETH via call! (not transfer) cc: https://solidity-by-example.org/sending-ether    (bool sent, bytes memory data) = msg.sender.call{value: indBalanceRewards}("");    require(sent, "RIP; withdrawal failed :( ");  }  /*  Allows any user to repatriate "unproductive" funds that are left in the staking contract  past the defined withdrawal period  */  function execute() public claimDeadlineReached(true) notCompleted {    uint256 contractBalance = address(this).balance;    exampleExternalContract.complete{value: address(this).balance}();  }  /*  READ-ONLY function to calculate the time remaining before the minimum staking period has passed  */  function withdrawalTimeLeft() public view returns (uint256 withdrawalTimeLeft) {    if( block.timestamp >= withdrawalDeadline) {      return (0);    } else {      return (withdrawalDeadline - block.timestamp);    }  }  /*  READ-ONLY function to calculate the time remaining before the minimum staking period has passed  */  function claimPeriodLeft() public view returns (uint256 claimPeriodLeft) {    if( block.timestamp >= claimDeadline) {      return (0);    } else {      return (claimDeadline - block.timestamp);    }  }  /*  Time to "kill-time" on our local testnet  */  function killTime() public {    currentBlock = block.timestamp;  }  /*  \Function for our smart contract to receive ETH  cc: https://docs.soliditylang.org/en/latest/contracts.html#receive-ether-function  */  receive() external payable {      emit Received(msg.sender, msg.value);  }}

step6 进军前端

1.找到 packages——react-app——src——App.jsx 。官方链接对每一步都做了解释,很有意思,感兴趣的可以看一下每段代码对应的模块功能, 而不是单纯的粘贴最终代码哈哈

2.将如下代码复制粘贴进去。

import WalletConnectProvider from "@walletconnect/web3-provider";//import Torus from "@toruslabs/torus-embed"import WalletLink from "walletlink";import { Alert, Button, Col, Menu, Row, List, Divider } from "antd";import "antd/dist/antd.css";import React, { useCallback, useEffect, useState } from "react";import { BrowserRouter, Link, Route, Switch } from "react-router-dom";import Web3Modal from "web3modal";import "./App.css";import { Account, Address, Balance, Contract, Faucet, GasGauge, Header, Ramp, ThemeSwitch } from "./components";import { INFURA_ID, NETWORK, NETWORKS } from "./constants";import { Transactor } from "./helpers";import {  useBalance,  useContractLoader,  useContractReader,  useGasPrice,  useOnBlock,  useUserProviderAndSigner,} from "eth-hooks";import { useEventListener } from "eth-hooks/events/useEventListener";import { useExchangeEthPrice } from "eth-hooks/dapps/dex";// import Hints from "./Hints";import { ExampleUI, Hints, Subgraph } from "./views";import { useContractConfig } from "./hooks";import Portis from "@portis/web3";import Fortmatic from "fortmatic";import Authereum from "authereum";import humanizeDuration from "humanize-duration";const { ethers } = require("ethers");/*    Welcome to 🏗 scaffold-eth !    Code:    https://github.com/austintgriffith/scaffold-eth    Support:    https://t.me/joinchat/KByvmRe5wkR-8F_zz6AjpA    or DM @austingriffith on Twitter or Telegram    You should get your own Infura.io ID and put it in `constants.js`    (this is your connection to the main Ethereum network for ENS etc.)    🌏 EXTERNAL CONTRACTS:    You can also bring in contract artifacts in `constants.js`    (and then use the `useExternalContractLoader()` hook!)*//// 📡 What chain are your contracts deployed to?const targetNetwork = NETWORKS.localhost; // <------- select your target frontend network (localhost, rinkeby, xdai, mainnet)// 😬 Sorry for all the console loggingconst DEBUG = true;const NETWORKCHECK = true;// 🛰 providersif (DEBUG) console.log("📡 Connecting to Mainnet Ethereum");// const mainnetProvider = getDefaultProvider("mainnet", { infura: INFURA_ID, etherscan: ETHERSCAN_KEY, quorum: 1 });// const mainnetProvider = new InfuraProvider("mainnet",INFURA_ID);//// attempt to connect to our own scaffold eth rpc and if that fails fall back to infura...// Using StaticJsonRpcProvider as the chainId won't change see https://github.com/ethers-io/ethers.js/issues/901const scaffoldEthProvider = navigator.onLine  ? new ethers.providers.StaticJsonRpcProvider("https://rpc.scaffoldeth.io:48544")  : null;const poktMainnetProvider = navigator.onLine  ? new ethers.providers.StaticJsonRpcProvider(      "https://eth-mainnet.gateway.pokt.network/v1/lb/611156b4a585a20035148406",    )  : null;const mainnetInfura = navigator.onLine  ? new ethers.providers.StaticJsonRpcProvider("https://mainnet.infura.io/v3/" + INFURA_ID)  : null;// ( ⚠️ Getting "failed to meet quorum" errors? Check your INFURA_ID// 🏠 Your local provider is usually pointed at your local blockchainconst localProviderUrl = targetNetwork.rpcUrl;// as you deploy to other networks you can set REACT_APP_PROVIDER=https://dai.poa.network in packages/react-app/.envconst localProviderUrlFromEnv = process.env.REACT_APP_PROVIDER ? process.env.REACT_APP_PROVIDER : localProviderUrl;if (DEBUG) console.log("🏠 Connecting to provider:", localProviderUrlFromEnv);const localProvider = new ethers.providers.StaticJsonRpcProvider(localProviderUrlFromEnv);// 🔭 block explorer URLconst blockExplorer = targetNetwork.blockExplorer;// Coinbase walletLink initconst walletLink = new WalletLink({  appName: "coinbase",});// WalletLink providerconst walletLinkProvider = walletLink.makeWeb3Provider(`https://mainnet.infura.io/v3/${INFURA_ID}`, 1);// Portis ID: 6255fb2b-58c8-433b-a2c9-62098c05ddc9/*  Web3 modal helps us "connect" external wallets:*/const web3Modal = new Web3Modal({  network: "mainnet", // Optional. If using WalletConnect on xDai, change network to "xdai" and add RPC info below for xDai chain.  cacheProvider: true, // optional  theme: "light", // optional. Change to "dark" for a dark theme.  providerOptions: {    walletconnect: {      package: WalletConnectProvider, // required      options: {        bridge: "https://polygon.bridge.walletconnect.org",        infuraId: INFURA_ID,        rpc: {          1: `https://mainnet.infura.io/v3/${INFURA_ID}`, // mainnet // For more WalletConnect providers: https://docs.walletconnect.org/quick-start/dapps/web3-provider#required          42: `https://kovan.infura.io/v3/${INFURA_ID}`,          100: "https://dai.poa.network", // xDai        },      },    },    portis: {      display: {        logo: "https://user-images.githubusercontent.com/9419140/128913641-d025bc0c-e059-42de-a57b-422f196867ce.png",        name: "Portis",        description: "Connect to Portis App",      },      package: Portis,      options: {        id: "6255fb2b-58c8-433b-a2c9-62098c05ddc9",      },    },    fortmatic: {      package: Fortmatic, // required      options: {        key: "pk_live_5A7C91B2FC585A17", // required      },    },    // torus: {    //   package: Torus,    //   options: {    //     networkParams: {    //       host: "https://localhost:8545", // optional    //       chainId: 1337, // optional    //       networkId: 1337 // optional    //     },    //     config: {    //       buildEnv: "development" // optional    //     },    //   },    // },    "custom-walletlink": {      display: {        logo: "https://play-lh.googleusercontent.com/PjoJoG27miSglVBXoXrxBSLveV6e3EeBPpNY55aiUUBM9Q1RCETKCOqdOkX2ZydqVf0",        name: "Coinbase",        description: "Connect to Coinbase Wallet (not Coinbase App)",      },      package: walletLinkProvider,      connector: async (provider, _options) => {        await provider.enable();        return provider;      },    },    authereum: {      package: Authereum, // required    },  },});function App(props) {  const mainnetProvider =    poktMainnetProvider && poktMainnetProvider._isProvider      ? poktMainnetProvider      : scaffoldEthProvider && scaffoldEthProvider._network      ? scaffoldEthProvider      : mainnetInfura;  const [injectedProvider, setInjectedProvider] = useState();  const [address, setAddress] = useState();  const logoutOfWeb3Modal = async () => {    await web3Modal.clearCachedProvider();    if (injectedProvider && injectedProvider.provider && typeof injectedProvider.provider.disconnect == "function") {      await injectedProvider.provider.disconnect();    }    setTimeout(() => {      window.location.reload();    }, 1);  };  /* 💵 This hook will get the price of ETH from 🦄 Uniswap: */  const price = useExchangeEthPrice(targetNetwork, mainnetProvider);  /* 🔥 This hook will get the price of Gas from ⛽️ EtherGasStation */  const gasPrice = useGasPrice(targetNetwork, "fast");  // Use your injected provider from 🦊 Metamask or if you don't have it then instantly generate a 🔥 burner wallet.  const userProviderAndSigner = useUserProviderAndSigner(injectedProvider, localProvider);  const userSigner = userProviderAndSigner.signer;  useEffect(() => {    async function getAddress() {      if (userSigner) {        const newAddress = await userSigner.getAddress();        setAddress(newAddress);      }    }    getAddress();  }, [userSigner]);  // You can warn the user if you would like them to be on a specific network  const localChainId = localProvider && localProvider._network && localProvider._network.chainId;  const selectedChainId =    userSigner && userSigner.provider && userSigner.provider._network && userSigner.provider._network.chainId;  // For more hooks, check out 🔗eth-hooks at: https://www.npmjs.com/package/eth-hooks  // The transactor wraps transactions and provides notificiations  const tx = Transactor(userSigner, gasPrice);  // Faucet Tx can be used to send funds from the faucet  const faucetTx = Transactor(localProvider, gasPrice);  // 🏗 scaffold-eth is full of handy hooks like this one to get your balance:  const yourLocalBalance = useBalance(localProvider, address);  // Just plug in different 🛰 providers to get your balance on different chains:  const yourMainnetBalance = useBalance(mainnetProvider, address);  const contractConfig = useContractConfig();  // Load in your local 📝 contract and read a value from it:  const readContracts = useContractLoader(localProvider, contractConfig);  // If you want to make 🔐 write transactions to your contracts, use the userSigner:  const writeContracts = useContractLoader(userSigner, contractConfig, localChainId);  // EXTERNAL CONTRACT EXAMPLE:  //  // If you want to bring in the mainnet DAI contract it would look like:  const mainnetContracts = useContractLoader(mainnetProvider, contractConfig);  // If you want to call a function on a new block  useOnBlock(mainnetProvider, () => {    console.log(`⛓ A new mainnet block is here: ${mainnetProvider._lastBlockNumber}`);  });  // Then read your DAI balance like:  const myMainnetDAIBalance = useContractReader(mainnetContracts, "DAI", "balanceOf", [    "0x34aA3F359A9D614239015126635CE7732c18fDF3",  ]);  //keep track of contract balance to know how much has been staked total:  const stakerContractBalance = useBalance(    localProvider,    readContracts && readContracts.Staker ? readContracts.Staker.address : null,  );  if (DEBUG) console.log("💵 stakerContractBalance", stakerContractBalance);  const rewardRatePerSecond = useContractReader(readContracts, "Staker", "rewardRatePerSecond");  console.log("💵 Reward Rate:", rewardRatePerSecond);  // ** keep track of a variable from the contract in the local React state:  const balanceStaked = useContractReader(readContracts, "Staker", "balances", [address]);  console.log("💸 balanceStaked:", balanceStaked);  // ** 📟 Listen for broadcast events  const stakeEvents = useEventListener(readContracts, "Staker", "Stake", localProvider, 1);  console.log("📟 stake events:", stakeEvents);  const receiveEvents = useEventListener(readContracts, "Staker", "Received", localProvider, 1);  console.log("📟 receive events:", receiveEvents);  // ** keep track of a variable from the contract in the local React state:  const claimPeriodLeft = useContractReader(readContracts, "Staker", "claimPeriodLeft");  console.log("⏳ Claim Period Left:", claimPeriodLeft);  const withdrawalTimeLeft = useContractReader(readContracts, "Staker", "withdrawalTimeLeft");  console.log("⏳ Withdrawal Time Left:", withdrawalTimeLeft);  // ** Listen for when the contract has been 'completed'  const complete = useContractReader(readContracts, "ExampleExternalContract", "completed");  console.log("✅ complete:", complete);  const exampleExternalContractBalance = useBalance(    localProvider,    readContracts && readContracts.ExampleExternalContract ? readContracts.ExampleExternalContract.address : null,  );  if (DEBUG) console.log("💵 exampleExternalContractBalance", exampleExternalContractBalance);  let completeDisplay = "";  if (complete) {    completeDisplay = (      <div style={{padding: 64, backgroundColor: "#eeffef", fontWeight: "bold", color: "rgba(0, 0, 0, 0.85)" }} >        -- 💀 Staking App Fund Repatriation Executed 🪦 --        <Balance balance={exampleExternalContractBalance} fontSize={32} /> ETH locked!      </div>    );  }  /*  const addressFromENS = useResolveName(mainnetProvider, "austingriffith.eth");  console.log("🏷 Resolved austingriffith.eth as:", addressFromENS)  */  //  // 🧫 DEBUG 👨🏻‍🔬  //  useEffect(() => {    if (      DEBUG &&      mainnetProvider &&      address &&      selectedChainId &&      yourLocalBalance &&      yourMainnetBalance &&      readContracts &&      writeContracts &&      mainnetContracts    ) {      console.log("_____________________________________ 🏗 scaffold-eth _____________________________________");      console.log("🌎 mainnetProvider", mainnetProvider);      console.log("🏠 localChainId", localChainId);      console.log("👩‍💼 selected address:", address);      console.log("🕵🏻‍♂️ selectedChainId:", selectedChainId);      console.log("💵 yourLocalBalance", yourLocalBalance ? ethers.utils.formatEther(yourLocalBalance) : "...");      console.log("💵 yourMainnetBalance", yourMainnetBalance ? ethers.utils.formatEther(yourMainnetBalance) : "...");      console.log("📝 readContracts", readContracts);      console.log("🌍 DAI contract on mainnet:", mainnetContracts);      console.log("💵 yourMainnetDAIBalance", myMainnetDAIBalance);      console.log("🔐 writeContracts", writeContracts);    }  }, [    mainnetProvider,    address,    selectedChainId,    yourLocalBalance,    yourMainnetBalance,    readContracts,    writeContracts,    mainnetContracts,  ]);  let networkDisplay = "";  if (NETWORKCHECK && localChainId && selectedChainId && localChainId !== selectedChainId) {    const networkSelected = NETWORK(selectedChainId);    const networkLocal = NETWORK(localChainId);    if (selectedChainId === 1337 && localChainId === 31337) {      networkDisplay = (        <div style={{ zIndex: 2, position: "absolute", right: 0, top: 60, padding: 16 }}>          <Alert            message="⚠️ Wrong Network ID"            description={              <div>                You have <b>chain id 1337</b> for localhost and you need to change it to <b>31337</b> to work with                HardHat.                <div>(MetaMask -&gt; Settings -&gt; Networks -&gt; Chain ID -&gt; 31337)</div>              </div>            }            type="error"            closable={false}          />        </div>      );    } else {      networkDisplay = (        <div style={{ zIndex: 2, position: "absolute", right: 0, top: 60, padding: 16 }}>          <Alert            message="⚠️ Wrong Network"            description={              <div>                You have <b>{networkSelected && networkSelected.name}</b> selected and you need to be on{" "}                <Button                  onClick={async () => {                    const ethereum = window.ethereum;                    const data = [                      {                        chainId: "0x" + targetNetwork.chainId.toString(16),                        chainName: targetNetwork.name,                        nativeCurrency: targetNetwork.nativeCurrency,                        rpcUrls: [targetNetwork.rpcUrl],                        blockExplorerUrls: [targetNetwork.blockExplorer],                      },                    ];                    console.log("data", data);                    let switchTx;                    // https://docs.metamask.io/guide/rpc-api.html#other-rpc-methods                    try {                      switchTx = await ethereum.request({                        method: "wallet_switchEthereumChain",                        params: [{ chainId: data[0].chainId }],                      });                    } catch (switchError) {                      // not checking specific error code, because maybe we're not using MetaMask                      try {                        switchTx = await ethereum.request({                          method: "wallet_addEthereumChain",                          params: data,                        });                      } catch (addError) {                        // handle "add" error                      }                    }                    if (switchTx) {                      console.log(switchTx);                    }                  }}                >                  <b>{networkLocal && networkLocal.name}</b>                </Button>              </div>            }            type="error"            closable={false}          />        </div>      );    }  } else {    networkDisplay = (      <div style={{ zIndex: -1, position: "absolute", right: 154, top: 28, padding: 16, color: targetNetwork.color }}>        {targetNetwork.name}      </div>    );  }  const loadWeb3Modal = useCallback(async () => {    const provider = await web3Modal.connect();    setInjectedProvider(new ethers.providers.Web3Provider(provider));    provider.on("chainChanged", chainId => {      console.log(`chain changed to ${chainId}! updating providers`);      setInjectedProvider(new ethers.providers.Web3Provider(provider));    });    provider.on("accountsChanged", () => {      console.log(`account changed!`);      setInjectedProvider(new ethers.providers.Web3Provider(provider));    });    // Subscribe to session disconnection    provider.on("disconnect", (code, reason) => {      console.log(code, reason);      logoutOfWeb3Modal();    });  }, [setInjectedProvider]);  useEffect(() => {    if (web3Modal.cachedProvider) {      loadWeb3Modal();    }  }, [loadWeb3Modal]);  const [route, setRoute] = useState();  useEffect(() => {    setRoute(window.location.pathname);  }, [setRoute]);  let faucetHint = "";  const faucetAvailable = localProvider && localProvider.connection && targetNetwork.name.indexOf("local") !== -1;  const [faucetClicked, setFaucetClicked] = useState(false);  if (    !faucetClicked &&    localProvider &&    localProvider._network &&    localProvider._network.chainId === 31337 &&    yourLocalBalance &&    ethers.utils.formatEther(yourLocalBalance) <= 0  ) {    faucetHint = (      <div style={{ padding: 16 }}>        <Button          type="primary"          onClick={() => {            faucetTx({              to: address,              value: ethers.utils.parseEther("0.01"),            });            setFaucetClicked(true);          }}        >          💰 Grab funds from the faucet ⛽️        </Button>      </div>    );  }  return (    <div className="App">      {/* ✏️ Edit the header and change the title to your project name */}      <Header />      {networkDisplay}      <BrowserRouter>        <Menu style={{ textAlign: "center" }} selectedKeys={[route]} mode="horizontal">          <Menu.Item key="/">            <Link              onClick={() => {                setRoute("/");              }}              to="/"            >              Staker UI            </Link>          </Menu.Item>          <Menu.Item key="/contracts">            <Link              onClick={() => {                setRoute("/contracts");              }}              to="/contracts"            >              Debug Contracts            </Link>          </Menu.Item>        </Menu>        <Switch>          <Route exact path="/">            {completeDisplay}            <div style={{ padding: 8, marginTop: 16 }}>              <div>Staker Contract:</div>              <Address value={readContracts && readContracts.Staker && readContracts.Staker.address} />            </div>            <Divider />            <div style={{ padding: 8, marginTop: 16 }}>              <div>Reward Rate Per Second:</div>              <Balance balance={rewardRatePerSecond} fontSize={64} /> ETH            </div>            <Divider />            <div style={{ padding: 8, marginTop: 16, fontWeight: "bold" }}>              <div>Claim Period Left:</div>              {claimPeriodLeft && humanizeDuration(claimPeriodLeft.toNumber() * 1000)}            </div>            <div style={{ padding: 8, marginTop: 16, fontWeight: "bold"}}>              <div>Withdrawal Period Left:</div>              {withdrawalTimeLeft && humanizeDuration(withdrawalTimeLeft.toNumber() * 1000)}            </div>            <Divider />            <div style={{ padding: 8, fontWeight: "bold"}}>              <div>Total Available ETH in Contract:</div>              <Balance balance={stakerContractBalance} fontSize={64} />            </div>            <Divider />            <div style={{ padding: 8,fontWeight: "bold" }}>              <div>ETH Locked 🔒 in Staker Contract:</div>              <Balance balance={balanceStaked} fontSize={64} />            </div>            <div style={{ padding: 8 }}>              <Button                type={"default"}                onClick={() => {                  tx(writeContracts.Staker.execute());                }}              >                📡 Execute!              </Button>            </div>            <div style={{ padding: 8 }}>              <Button                type={"default"}                onClick={() => {                  tx(writeContracts.Staker.withdraw());                }}              >                🏧 Withdraw              </Button>            </div>            <div style={{ padding: 8 }}>              <Button                type={balanceStaked ? "success" : "primary"}                onClick={() => {                  tx(writeContracts.Staker.stake({ value: ethers.utils.parseEther("0.5") }));                }}              >                🥩 Stake 0.5 ether!              </Button>            </div>            {/*                🎛 this scaffolding is full of commonly used components                this <Contract/> component will automatically parse your ABI                and give you a form to interact with it locally            */}            {/* uncomment for a second contract:            <Contract              name="SecondContract"              signer={userProvider.getSigner()}              provider={localProvider}              address={address}              blockExplorer={blockExplorer}              contractConfig={contractConfig}            />            */}          </Route>          <Route path="/contracts">            <Contract              name="Staker"              signer={userSigner}              provider={localProvider}              address={address}              blockExplorer={blockExplorer}              contractConfig={contractConfig}            />            <Contract              name="ExampleExternalContract"              signer={userSigner}              provider={localProvider}              address={address}              blockExplorer={blockExplorer}              contractConfig={contractConfig}            />          </Route>        </Switch>      </BrowserRouter>      <ThemeSwitch />      {/* 👨‍💼 Your account is in the top right with a wallet at connect options */}      <div style={{ position: "fixed", textAlign: "right", right: 0, top: 0, padding: 10 }}>        <Account          address={address}          localProvider={localProvider}          userSigner={userSigner}          mainnetProvider={mainnetProvider}          price={price}          web3Modal={web3Modal}          loadWeb3Modal={loadWeb3Modal}          logoutOfWeb3Modal={logoutOfWeb3Modal}          blockExplorer={blockExplorer}        />        {faucetHint}      </div>      <div style={{ marginTop: 32, opacity: 0.5 }}>        {/* Add your address here */}        Created by <Address value={"Your...address"} ensProvider={mainnetProvider} fontSize={16} />      </div>      <div style={{ marginTop: 32, opacity: 0.5 }}>        <a target="_blank" style={{ padding: 32, color: "#000" }} href="https://github.com/scaffold-eth/scaffold-eth">          🍴 Fork me!        </a>      </div>      {/* 🗺 Extra UI like gas price, eth price, faucet, and support: */}      <div style={{ position: "fixed", textAlign: "left", left: 0, bottom: 20, padding: 10 }}>        <Row align="middle" gutter={[4, 4]}>          <Col span={8}>            <Ramp price={price} address={address} networks={NETWORKS} />          </Col>          <Col span={8} style={{ textAlign: "center", opacity: 0.8 }}>            <GasGauge gasPrice={gasPrice} />          </Col>          <Col span={8} style={{ textAlign: "center", opacity: 1 }}>            <Button              onClick={() => {                window.open("https://t.me/joinchat/KByvmRe5wkR-8F_zz6AjpA");              }}              size="large"              shape="round"            >              <span style={{ marginRight: 8 }} role="img" aria-label="support">                💬              </span>              Support            </Button>          </Col>        </Row>        <Row align="middle" gutter={[4, 4]}>          <Col span={24}>            {              /*  if the local provider has a signer, let's show the faucet:  */              faucetAvailable ? (                <Faucet localProvider={localProvider} price={price} ensProvider={mainnetProvider} />              ) : (                ""              )            }          </Col>        </Row>      </div>    </div>  );}export default App;

3.再打开控制台,输入 yarn deploy –reset,可以看到前端界面和刚才有细微差别。

4.测试 stake 功能,点击 stake,测试一下,据说正常部署都会提示取款时间已过。。。

step7 上传至 github

1.登录 github,点击 New。

2.这三个地方填一下,然后直接拉到最下面,点击 Create repository。

3.点击 code,复制仓库地址备用。

4..接下来就到本地操作了,首先确保你已经成功安装 Git 这个软件,在电脑上找到你要上传到 Github 上面的那个项目文件夹,进入项目文件夹,单击鼠标右键,选择 Git Bash Here,如下图所示。

5.接下来输入如下代码(关键步骤),把 github 上面的仓库克隆到本地

git clone https://github.com/qpc666/road-to-web3-06.git(https://github.com/qpc666/road-to-web3-06.git 替换成你之前复制的地址),如图即可。

6.这个步骤以后你的本地项目文件夹下面就会多出个文件夹,该文件夹名即为你 github 上面的项目名,如图我多出了个 road-to-web3-06 文件夹,我们把本地项目文件夹下的所有文件(除了新多出的那个文件夹不用),其余都复制到那个新多出的文件夹下。(扔那里不管,去打把王者再回来差不多就复制完成了,电脑慢的去打把 dota 回来估计差不多了,毕竟时间要用在刀刃上)

7.接着继续输入命令 cd road-to-web3-06,进入 road-to-web3-06 文件夹(road-to-web3-06 是我建的仓库,这里应该改成你自己的仓库名字)

 8.接下来依次输入以下代码即可完成其他剩余操作:

git add .  (注:别忘记后面的 . ,此操作是把 Test 文件夹下面的文件都添加进来)

git commit  -m  ”提交信息”  (注:“提交信息”里面换成你需要,也可以不管,这里可能会出现如下第一张图提示,按照提示输入 git config –global user.email “you@example.com”git config –global user.name “Your Name” 就可以了 )

git push -u origin main   (注:此操作目的是把本地仓库 push 到 github 上面,此步骤需要你输入帐号和密码)

9.第一次使用 Git,会弹框要求登录,把这个码复制下来。

10.点第九步码下面那个链接,进去登录自己的 git 账号,然后把把复制粘贴在这里,授权就可以了。

11.复制这个链接即可。

step8 提交项目

提交链接

step9 领取 NFT

https://mintkudos.xyz/

前几周的内容在推特@greta0086 看哦

相关Wiki

【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。

Greta
数据请求中
查看更多

推荐专栏

数据请求中
在 App 打开