<100 subscribers
เมื่อไม่นานมานี้โปรเจค Alchemy ได้เข้าสู่ web3 ด้วยมูลค่า 10.2 พันล้านดอลลาร์ และได้รับการระดมทุน 545 ล้านดอลลาร์.
Alchemy คือโปรเจคอะไร?
Alchemy คือแพลตฟอร์มสำหรับนักพัฒนา dApp บน web3. เป็นรากฐานที่อยู่เบื้องหลังตลาด NFT ยอดนิยมอย่าง OpenSea, Nifty และคอลเล็กชั่นระดับโลกที่สำคัญมากมาย.
12/2019, Alchemy เสร็จสิ้นการจัดหาเงินทุน Series A มูลค่า 15 ล้านดอลลาร์จาก Pantera Capital, Stanford University, Coinbase, Samsung,…
4/2021, Alchemy ได้เสร็จสิ้นการจัดหาเงินทุน Series B มูลค่า 80 ล้านดอลลาร์ โดยมีมูลค่า 505 ล้านดอลลาร์ นำโดย Coatue and Addition โดยมีส่วนร่วมจาก DFJ Growth, K5 Global, Chainsmokers, นักแสดง Jared Leto และครอบครัว Glazer.
10/2021, Alchemy ได้เสร็จสิ้นการจัดหาเงินทุน Series C มูลค่า 250 ล้านดอลลาร์ โดยมีมูลค่า 3.5 พันล้านดอลลาร์ นำโดย a16z.
2/2022, Alchemy ได้เสร็จสิ้นการจัดหาเงินทุน 200 ล้านดอลลาร์โดยมีมูลค่า 10.2 พันล้านดอลลาร์ซึ่งนำโดย Lightspeed และ Silver Lake.
Alchemy เป็นทีมที่มีพื้นฐานที่แข็งแกร่ง, เงินทุนที่เพียงพอ, ทำงานจริง, และยังไม่ได้ออก token.
และ Alchemy วางแผนที่จะใช้เงินทุนใหม่นี้เพื่อกระตุ้นการใช้งาน Web3, บางส่วนรวมถึงการเปิดตัว Web3 University, ซึ่งปัจจุบันเป็นงาน Road to Web3 เป็นเวลา 10 สัปดาห์ด้วยหนึ่ง NFT ต่อสัปดาห์. ผมเห็นว่าจำนวน nfts ที่มิ้นออกมานั้นน้อยมาก คาดว่าเนื่องจากความยากของงาน คนจำนวนมากจึงยอมแพ้ที่จะเข้าร่วม, หากโปรเจคนี้มี Airdrop ส่วนตัวผมมองว่าน่าจะได้เยอะอย่างแน่นอน.
ดาวน์โหลด git เวอร์ชั่น windows จากเว็บไซต์ทางการของ git: http://git-scm.com/downloads

โดยทั่วไป, คุณสามารถใช้การตั้งค่าเริ่มต้นได้: กด next ไปจนเรื่อยๆ และสุดท้ายกด install.
1.ป้อน git clone https://github.com/scaffold-eth/scaffold-eth-challenges.git challenge-1-decentralized-staking ใน console กด Enter และรอสักครู่เพื่อให้การดาวน์โหลดเสร็จสิ้น.

2. ป้อน cd challenge-1-decentralized-staking ใน console แล้วกด Enter.

3. ป้อน git checkout challenge-1-decentralized-staking ใน console แล้วกด Enter.

4. ป้อน yarn install ใน console แล้วกด Enter เพื่อติดตั้ง yarn และรอสักครู่จนกว่าจะติดตั้งเสร็จ.

5. หลังจากติดตั้งทุกอย่างแล้ว คุณควรเห็นโฟลเดอร์ challenge-1-decentralized-staking.

หมายเหตุ: ในบทช่วยสอนนี้ เราจะเน้นเขียน code ไปที่สองไฟล์ Staker.sol และ App.jsx เป็นหลัก


โปรดทราบว่าต่อไปจะใช้ console แยกกันสามตัว, และผมได้เปิด console สามตัวไว้.
1. พิมพ์ yarn chain ใน console เพื่อเริ่ม back end hardhat ของคุณ.

2. เปิด console อื่น, ป้อน cd ลงในโฟลเดอร์นั้น และป้อน yarn deploy ต่อใน console.

3. เปิด console อื่น, ป้อน cd ลงในโฟลเดอร์นั้น และพิมพ์ yarn start ใน console เพื่อเริ่มส่วนหน้า React ของคุณ.

แล้วเราจะเห็นหน้านี้.

ในมุมมองเริ่มต้นของเรา, เรามีสองแท็บ - Staker UI & Debug Contracts.

1. เอา public address จากมุมขวาบนมาใส่ตรงช่องข้างล่าง, จากนั้นคลิกส่ง token test ไปที่ public wallet (อย่ากด connect wallet).

2. คุณจะเห็นยอดคงเหลือที่มุมขวาบนมีการเปลี่ยนแปลง.

3. หลังจากเติมเงินใน public wallet ของคุณแล้วคลิก กด send.

1. เปิด vscode คลิก เปิดโฟลเดอร์, ค้นหาโฟลเดอร์ที่คุณสร้าง แล้วคลิกเพื่อเลือกโฟลเดอร์.


2.เปิด packages—hardhat——contracts——Staker.sol.

3. คัดลอกและวางโค้ดต่อไปนี้ลงในไฟล์โดยตรง (ลิงค์อย่างเป็นทางการจะอธิบายแต่ละ step ถ้าสนใจก็สามารถเข้าไปดู module function ที่ตรงกับโค้ดแต่ละส่วนแทนการ paste รวดเดียวแบบนี้ได้เลยครับ, แต่ถ้าจะใช้โค้ดของ official ต้องเปลี่ยน rewardRatePerBlock ในบรรทัดที่ 78 เป็น rewardRatePerSecond ด้วย).
1. ค้นหา packages——react-app——src——App.jsx.

2. คัดลอกและวางโค้ดต่อไปนี้ลงในไฟล์โดยตรง (ลิงค์อย่างเป็นทางการจะอธิบายแต่ละ step ถ้าสนใจก็สามารถเข้าไปดู module function ที่ตรงกับโค้ดแต่ละส่วนแทนการ paste รวดเดียวแบบนี้ได้เลยครับ).
3. เปิด console อีกครั้ง, ป้อน yarn deploy --reset แล้วคุณจะเห็นว่าหน้าเว็บแตกต่างจากก่อนหน้านี้เล็กน้อย.

4. ทดสอบฟังก์ชั่น stake คลิก stake ถ้าแจ้งว่าหมดเวลาการถอนแล้วคือปกติ.

ขั้นตอนอัปโฟลเดอร์โปรเจคลง github มีมากมายไม่ว่าจะเป็นเปิด git bash แล้วอัปลงด้วย code, อัปตรงจาก vscode ลองไปหาใน Google, youtube ดู. หรือสามารถอัปแบบ github desktop เหมือนสัปดาร์ก่อนหน้านี้ได้ครับ.
https://docs.google.com/forms/d/e/1FAIpQLSdNNLXMYZmIhjcWoT-UedS3AoGpRiPDRaNARUPGXLbX1TVvSg/viewform
เราสามารถเช็ด nft ที่เรายังไม่ได้เคลมได้นะครับโดยเข้าไปที่ mintkudos จากนั้นต่อกระเป๋ามุมขวามือลองกดเช็ดดูถ้ามีปลุ่ม claim แปลว่าเรามี nft ที่เราสามารถเคลมได้แต่เรายังไม่ได้เคลม.

เท่านี้ก็จบกันไปแล้วนะครับกับ Alchemy Road to web3 week6 ใครมีคำถาม หรือสงสัยยังไงสามารถถามได้. ส่วนใครที่อยากจะสนับสนุนค่ากาแฟให้ผู้เขียนสามารถกดปุ่ม Collect Entry ได้ที่ด้านล่างนี้จากนั้นเราจะได้ nft บทความของ mirror.xyz chain optimism และสามารถเช็ด nft ที่เรา collect มาได้ที่ QxProfile.
collect://
เมื่อไม่นานมานี้โปรเจค Alchemy ได้เข้าสู่ web3 ด้วยมูลค่า 10.2 พันล้านดอลลาร์ และได้รับการระดมทุน 545 ล้านดอลลาร์.
Alchemy คือโปรเจคอะไร?
Alchemy คือแพลตฟอร์มสำหรับนักพัฒนา dApp บน web3. เป็นรากฐานที่อยู่เบื้องหลังตลาด NFT ยอดนิยมอย่าง OpenSea, Nifty และคอลเล็กชั่นระดับโลกที่สำคัญมากมาย.
12/2019, Alchemy เสร็จสิ้นการจัดหาเงินทุน Series A มูลค่า 15 ล้านดอลลาร์จาก Pantera Capital, Stanford University, Coinbase, Samsung,…
4/2021, Alchemy ได้เสร็จสิ้นการจัดหาเงินทุน Series B มูลค่า 80 ล้านดอลลาร์ โดยมีมูลค่า 505 ล้านดอลลาร์ นำโดย Coatue and Addition โดยมีส่วนร่วมจาก DFJ Growth, K5 Global, Chainsmokers, นักแสดง Jared Leto และครอบครัว Glazer.
10/2021, Alchemy ได้เสร็จสิ้นการจัดหาเงินทุน Series C มูลค่า 250 ล้านดอลลาร์ โดยมีมูลค่า 3.5 พันล้านดอลลาร์ นำโดย a16z.
2/2022, Alchemy ได้เสร็จสิ้นการจัดหาเงินทุน 200 ล้านดอลลาร์โดยมีมูลค่า 10.2 พันล้านดอลลาร์ซึ่งนำโดย Lightspeed และ Silver Lake.
Alchemy เป็นทีมที่มีพื้นฐานที่แข็งแกร่ง, เงินทุนที่เพียงพอ, ทำงานจริง, และยังไม่ได้ออก token.
และ Alchemy วางแผนที่จะใช้เงินทุนใหม่นี้เพื่อกระตุ้นการใช้งาน Web3, บางส่วนรวมถึงการเปิดตัว Web3 University, ซึ่งปัจจุบันเป็นงาน Road to Web3 เป็นเวลา 10 สัปดาห์ด้วยหนึ่ง NFT ต่อสัปดาห์. ผมเห็นว่าจำนวน nfts ที่มิ้นออกมานั้นน้อยมาก คาดว่าเนื่องจากความยากของงาน คนจำนวนมากจึงยอมแพ้ที่จะเข้าร่วม, หากโปรเจคนี้มี Airdrop ส่วนตัวผมมองว่าน่าจะได้เยอะอย่างแน่นอน.
ดาวน์โหลด git เวอร์ชั่น windows จากเว็บไซต์ทางการของ git: http://git-scm.com/downloads

โดยทั่วไป, คุณสามารถใช้การตั้งค่าเริ่มต้นได้: กด next ไปจนเรื่อยๆ และสุดท้ายกด install.
1.ป้อน git clone https://github.com/scaffold-eth/scaffold-eth-challenges.git challenge-1-decentralized-staking ใน console กด Enter และรอสักครู่เพื่อให้การดาวน์โหลดเสร็จสิ้น.

2. ป้อน cd challenge-1-decentralized-staking ใน console แล้วกด Enter.

3. ป้อน git checkout challenge-1-decentralized-staking ใน console แล้วกด Enter.

4. ป้อน yarn install ใน console แล้วกด Enter เพื่อติดตั้ง yarn และรอสักครู่จนกว่าจะติดตั้งเสร็จ.

5. หลังจากติดตั้งทุกอย่างแล้ว คุณควรเห็นโฟลเดอร์ challenge-1-decentralized-staking.

หมายเหตุ: ในบทช่วยสอนนี้ เราจะเน้นเขียน code ไปที่สองไฟล์ Staker.sol และ App.jsx เป็นหลัก


โปรดทราบว่าต่อไปจะใช้ console แยกกันสามตัว, และผมได้เปิด console สามตัวไว้.
1. พิมพ์ yarn chain ใน console เพื่อเริ่ม back end hardhat ของคุณ.

2. เปิด console อื่น, ป้อน cd ลงในโฟลเดอร์นั้น และป้อน yarn deploy ต่อใน console.

3. เปิด console อื่น, ป้อน cd ลงในโฟลเดอร์นั้น และพิมพ์ yarn start ใน console เพื่อเริ่มส่วนหน้า React ของคุณ.

แล้วเราจะเห็นหน้านี้.

ในมุมมองเริ่มต้นของเรา, เรามีสองแท็บ - Staker UI & Debug Contracts.

1. เอา public address จากมุมขวาบนมาใส่ตรงช่องข้างล่าง, จากนั้นคลิกส่ง token test ไปที่ public wallet (อย่ากด connect wallet).

2. คุณจะเห็นยอดคงเหลือที่มุมขวาบนมีการเปลี่ยนแปลง.

3. หลังจากเติมเงินใน public wallet ของคุณแล้วคลิก กด send.

1. เปิด vscode คลิก เปิดโฟลเดอร์, ค้นหาโฟลเดอร์ที่คุณสร้าง แล้วคลิกเพื่อเลือกโฟลเดอร์.


2.เปิด packages—hardhat——contracts——Staker.sol.

3. คัดลอกและวางโค้ดต่อไปนี้ลงในไฟล์โดยตรง (ลิงค์อย่างเป็นทางการจะอธิบายแต่ละ step ถ้าสนใจก็สามารถเข้าไปดู module function ที่ตรงกับโค้ดแต่ละส่วนแทนการ paste รวดเดียวแบบนี้ได้เลยครับ, แต่ถ้าจะใช้โค้ดของ official ต้องเปลี่ยน rewardRatePerBlock ในบรรทัดที่ 78 เป็น rewardRatePerSecond ด้วย).
1. ค้นหา packages——react-app——src——App.jsx.

2. คัดลอกและวางโค้ดต่อไปนี้ลงในไฟล์โดยตรง (ลิงค์อย่างเป็นทางการจะอธิบายแต่ละ step ถ้าสนใจก็สามารถเข้าไปดู module function ที่ตรงกับโค้ดแต่ละส่วนแทนการ paste รวดเดียวแบบนี้ได้เลยครับ).
3. เปิด console อีกครั้ง, ป้อน yarn deploy --reset แล้วคุณจะเห็นว่าหน้าเว็บแตกต่างจากก่อนหน้านี้เล็กน้อย.

4. ทดสอบฟังก์ชั่น stake คลิก stake ถ้าแจ้งว่าหมดเวลาการถอนแล้วคือปกติ.

ขั้นตอนอัปโฟลเดอร์โปรเจคลง github มีมากมายไม่ว่าจะเป็นเปิด git bash แล้วอัปลงด้วย code, อัปตรงจาก vscode ลองไปหาใน Google, youtube ดู. หรือสามารถอัปแบบ github desktop เหมือนสัปดาร์ก่อนหน้านี้ได้ครับ.
https://docs.google.com/forms/d/e/1FAIpQLSdNNLXMYZmIhjcWoT-UedS3AoGpRiPDRaNARUPGXLbX1TVvSg/viewform
เราสามารถเช็ด nft ที่เรายังไม่ได้เคลมได้นะครับโดยเข้าไปที่ mintkudos จากนั้นต่อกระเป๋ามุมขวามือลองกดเช็ดดูถ้ามีปลุ่ม claim แปลว่าเรามี nft ที่เราสามารถเคลมได้แต่เรายังไม่ได้เคลม.

เท่านี้ก็จบกันไปแล้วนะครับกับ Alchemy Road to web3 week6 ใครมีคำถาม หรือสงสัยยังไงสามารถถามได้. ส่วนใครที่อยากจะสนับสนุนค่ากาแฟให้ผู้เขียนสามารถกดปุ่ม Collect Entry ได้ที่ด้านล่างนี้จากนั้นเราจะได้ nft บทความของ mirror.xyz chain optimism และสามารถเช็ด nft ที่เรา collect มาได้ที่ QxProfile.
collect://
// SPDX-License-Identifier: MIT
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);
}
}
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;
// SPDX-License-Identifier: MIT
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);
}
}
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;


Share Dialog
Share Dialog
No comments yet