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数量极少,估计由于任务难度大,很多小伙伴直接放弃,这样的项目若是空投,绝对是大毛。
从git官网下载windows版本的git:http://git-scm.com/downloads
一般使用默认设置即可:一路next,最后install,git安装完毕!
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.sol
和 App.jsx
.这两个文件的代码编写。
注意,接下来用到三个单独的控制台,我开了三个控制台。
1.在控制台输入yarn chain,启动您的安全帽后端。
2.再开一个控制台,cd进入那个文件夹,在控制台输入yarn deploy。
3.再开一个控制台,cd进入那个文件夹,在控制台输入yarn start ,启动你的 React 前端。
然后我们就可以看到网页跳出了这个页面!!!
在我们的默认视图中,我们有两个选项卡—— Staker UI
& Debug Contracts
.
1.往本地钱包发送测试币,先不要点那个连接!复制右上角的地址(这就是本地地址),然后粘贴到右下角,点击小飞机。
2.可以看到下面有提示,并且左上角余额发生变化。
3.或者这里也可以,点击send。充值本地钱包后,您将能够与您的合约进行交互!
1.打开vscode,点击open folder,找到你创建的那个文件夹,点击选择文件夹。
2.找到packages——hardhat——contracts——Staker.sol
3.将下面的代码直接复制粘贴进去。(官方链接对每一步都做了解释,很有意思,感兴趣的可以看一下每段代码对应的模块功能, 而不是单纯的粘贴最终代码哈哈,但是用官方代码的话,第78行的rewardRatePerBlock要改成rewardRatePerSecond,下面这个是改好的)
// SPDX-License-Identifier: MIT
pragma 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);
}
}
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 logging
const DEBUG = true;
const NETWORKCHECK = true;
// 🛰 providers
if (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/901
const 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 blockchain
const localProviderUrl = targetNetwork.rpcUrl;
// as you deploy to other networks you can set REACT_APP_PROVIDER=https://dai.poa.network in packages/react-app/.env
const 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 URL
const blockExplorer = targetNetwork.blockExplorer;
// Coinbase walletLink init
const walletLink = new WalletLink({
appName: "coinbase",
});
// WalletLink provider
const 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 -> Settings -> Networks -> Chain ID -> 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,测试一下,据说正常部署都会提示取款时间已过。。。
1.登录github,点击New。
2.这三个地方填一下,然后直接拉到最下面,点击Create repository。
3.点击code,复制仓库地址备用。
4..接下来就到本地操作了,首先确保你已经成功安装Git这个软件,在电脑上找到你要上传到Github上面的那个项目文件夹,进入项目文件夹,单击鼠标右键,选择Git Bash Here,如下图所示。
5.接下来输入如下代码(关键步骤),把github上面的仓库克隆到本地
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.复制这个链接即可。
提交链接
前几周的内容在推特@greta0086看哦