diff --git a/.eslintrc b/.eslintrc
deleted file mode 100644
index d1179aa..0000000
--- a/.eslintrc
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "parser": "@typescript-eslint/parser",
- "env": {
- "browser": true,
- "es2021": true
- },
- "extends": [
- "plugin:react/recommended",
- "plugin:@typescript-eslint/eslint-recommended",
- "plugin:@typescript-eslint/recommended",
- "prettier",
- "next/core-web-vitals"
- ],
- "parserOptions": {
- "ecmaFeatures": {
- "jsx": true
- },
- "ecmaVersion": 12,
- "sourceType": "module"
- },
- "plugins": ["react","@typescript-eslint"],
- "rules": {
- "@next/next/no-img-element": "off"
- }
-}
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..7ce406a
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,10 @@
+{
+ "extends": "next/core-web-vitals",
+ "rules": {
+ "semi": [2, "never"],
+ "space-before-blocks": "error",
+ "space-before-function-paren": [2, "never"],
+ "object-curly-spacing": ["error", "always"],
+ "indent": ["error", 2]
+ }
+}
diff --git a/components/Navigation.tsx b/components/Navigation.tsx
index c423c18..4cfc346 100644
--- a/components/Navigation.tsx
+++ b/components/Navigation.tsx
@@ -1,4 +1,4 @@
-import Link from "next/link";
+import Link from "next/link"
import { useRouter } from "next/router"
const config = [
@@ -34,10 +34,9 @@ const config = [
export const Navigation = () => {
const router = useRouter()
- console.log(router.asPath);
const isActivePath = (path: string) => router.asPath === path
return (
-
+
{ config.map((item) => {
return (
diff --git a/constants/index.ts b/constants/index.ts
new file mode 100644
index 0000000..14b2ab3
--- /dev/null
+++ b/constants/index.ts
@@ -0,0 +1,20 @@
+export const RPC_URLS = [
+ "https://speedy-nodes-nyc.moralis.io/cebf590f4bcd4f12d78ee1d4/polygon/mumbai",
+]
+
+export const BLOCK_EXPLORER_URLS = ["https://explorer-mumbai.maticvigil.com/"]
+export const SUPPORT_NETWORKS = [80001]
+export const STORAGE_KEY_ACCOUNT = 'ethAccount'
+export const STORAGE_KEY_ACCOUNT_SIG = 'sig_login'
+export const DOMAIN = {
+ name: "DwebLab Alpha",
+ version: "1",
+ chainId: 80001,
+}
+
+export const signInfo = {
+ types: {
+ Message: [{name: "content", type: "string"}],
+ },
+ message: {content: "Sign this msg to login"}
+}
diff --git a/context/web3Context.ts b/context/web3Context.ts
new file mode 100644
index 0000000..fb1d16b
--- /dev/null
+++ b/context/web3Context.ts
@@ -0,0 +1,68 @@
+import {createContext, Dispatch, useContext} from "react"
+
+type StateType = {
+ provider?: any
+ web3Provider?: any
+ account?: string
+ chainId?: number
+}
+
+export const initialWeb3State: StateType = {
+ provider: null,
+ web3Provider: null,
+ account: null,
+ chainId: null,
+}
+
+type ActionType =
+ | {
+ type: 'SET_WEB3_PROVIDER'
+ provider?: StateType['provider']
+ web3Provider?: StateType['web3Provider']
+ account?: StateType['account']
+ chainId?: StateType['chainId']
+}
+ | {
+ type: 'SET_ACCOUNT'
+ account?: StateType['account']
+}
+ | {
+ type: 'SET_CHAIN_ID'
+ chainId?: StateType['chainId']
+}
+ | {
+ type: 'RESET_WEB3_PROVIDER'
+}
+
+export function web3Reducer(state: StateType, action: ActionType): StateType {
+ switch (action.type) {
+ case 'SET_WEB3_PROVIDER':
+ return {
+ ...state,
+ provider: action.provider,
+ web3Provider: action.web3Provider,
+ account: action.account,
+ chainId: action.chainId,
+ }
+ case 'SET_ACCOUNT':
+ return {
+ ...state,
+ account: action.account,
+ }
+ case 'SET_CHAIN_ID':
+ return {
+ ...state,
+ chainId: action.chainId,
+ }
+ case 'RESET_WEB3_PROVIDER':
+ return initialWeb3State
+ default:
+ throw new Error()
+ }
+}
+
+
+export const Web3Context = createContext<{ state: StateType, dispatch: Dispatch
| undefined}>({ state: initialWeb3State, dispatch: undefined});
+export function useWeb3Context() {
+ return useContext(Web3Context);
+}
\ No newline at end of file
diff --git a/hooks/useAccount.ts b/hooks/useAccount.ts
new file mode 100644
index 0000000..4877d32
--- /dev/null
+++ b/hooks/useAccount.ts
@@ -0,0 +1,6 @@
+import {useWeb3Context} from "../context/web3Context";
+
+export const useAccount = () => {
+ const { state} = useWeb3Context()
+ return state.account
+}
\ No newline at end of file
diff --git a/hooks/useChainId.ts b/hooks/useChainId.ts
new file mode 100644
index 0000000..3685b3a
--- /dev/null
+++ b/hooks/useChainId.ts
@@ -0,0 +1,6 @@
+import {useWeb3Context} from "../context/web3Context";
+
+export const useChainId = () => {
+ const { state: { chainId }} = useWeb3Context()
+ return chainId
+}
\ No newline at end of file
diff --git a/hooks/useProvider.ts b/hooks/useProvider.ts
new file mode 100644
index 0000000..0bf9e54
--- /dev/null
+++ b/hooks/useProvider.ts
@@ -0,0 +1,6 @@
+import {useWeb3Context} from "../context/web3Context";
+
+export const useProvider = () => {
+ const { state: { provider }} = useWeb3Context()
+ return provider
+}
diff --git a/hooks/useWeb3.ts b/hooks/useWeb3.ts
new file mode 100644
index 0000000..3eb1f70
--- /dev/null
+++ b/hooks/useWeb3.ts
@@ -0,0 +1,6 @@
+import {useWeb3Context} from "../context/web3Context";
+
+export const useWeb3 = () => {
+ const { state: { web3Provider }} = useWeb3Context()
+ return web3Provider
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 161fad3..5992706 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
"@react-hookz/web": "^12.0.4",
"@textile/eth-storage": "^1.0.0",
"axios": "^0.24.0",
+ "bignumber.js": "^9.0.2",
"ethers": "^5.5.2",
"graphql-tag": "^2.12.6",
"ipfs-http-client": "^55.0.0",
@@ -27,6 +28,7 @@
"react-dom": "^17.0.2",
"react-hook-form": "^7.22.5",
"react-markdown": "^7.1.1",
+ "web3": "^1.6.1",
"web3modal": "^1.9.4",
"yup": "^0.32.11"
},
diff --git a/pages/_app.js b/pages/_app.js
deleted file mode 100644
index e9f4b7a..0000000
--- a/pages/_app.js
+++ /dev/null
@@ -1,263 +0,0 @@
-import "../styles/globals.css"
-import "../styles/markdown.css"
-import { FrontendVersion } from "../version.js"
-import Head from "next/head"
-import { useState } from "react"
-import { ethers } from "ethers"
-import { Menu, Transition } from "@headlessui/react"
-import { Fragment, useEffect } from "react"
-import { ChevronDownIcon } from "@heroicons/react/solid"
-import {Navigation} from "../components/Navigation";
-import axios from "axios"
-
-// On production, you should use something like web3Modal
-// to support additional wallet providers, like WalletConnect
-
-let provider
-
-function Marketplace({ Component, pageProps }) {
- const [ethAccount, setethAccount] = useState(null)
- const [Logined, setLogined] = useState(false)
- const [loadingState, setLoadingState] = useState("not-loaded")
- const [BackendVersion, setBackendVersion] = useState("err")
-
- useEffect(() => {
- if (typeof window !== "undefined") {
- const aethAccount = localStorage.getItem("ethAccount")
- if (aethAccount) {
- setethAccount(aethAccount)
- loginSig()
- setLogined(true)
- }
- async function listenMMAccount() {
- window.ethereum.on("accountsChanged", async function () {
- const accounts = await window.ethereum.request({
- method: "eth_requestAccounts",
- })
- if (accounts[0] != localStorage.getItem("ethAccount")) {
- console.log("Got new ethAccount", accounts[0])
- localStorage.setItem("ethAccount", accounts[0])
- localStorage.removeItem("sig_login")
- setethAccount(aethAccount)
- loginSig()
- }
- })
- }
- listenMMAccount()
- getBackendVersion()
- }
- }, [])
-
- async function getBackendVersion() {
- try {
- const dweb_search_ver_api = "https://dweb-search-api.anwen.cc/version"
- const ret = await axios.get(dweb_search_ver_api)
- if (ret.status == 200 && 'version' in ret.data) {
- setBackendVersion(ret.data['version'])
- }
- } catch (error) {
- console.log(error)
- }
- }
-
- async function loginSig() {
- // change network and sig login
- const sig_login = localStorage.getItem("sig_login")
- if (sig_login) {
- console.log("sig_login already done", sig_login)
- return
- }
- await addPolygonTestnetNetwork()
- const provider = new ethers.providers.Web3Provider(window.ethereum)
- const signer = provider.getSigner()
- const types = {
- Message: [{ name: "content", type: "string" }],
- }
- const domain = {
- name: "DwebLab Alpha",
- version: "1",
- chainId: 80001,
- }
- const message = {
- content: "Sign this msg to login",
- }
- signer.getAddress().then((walletAddress) => {
- signer._signTypedData(domain, types, message).then((signature) => {
- let verifiedAddress = ethers.utils.verifyTypedData(
- domain,
- types,
- message,
- signature,
- )
- if (verifiedAddress !== walletAddress) {
- alert(`Signed by: ${verifiedAddress}\r\nExpected: ${walletAddress}`)
- } else {
- localStorage.setItem("sig_login", signature)
- }
- console.log("signature", signature)
- })
- })
- setLoadingState("loaded")
- }
-
- async function addPolygonTestnetNetwork() {
- try {
- await ethereum.request({
- method: "wallet_switchEthereumChain",
- params: [{ chainId: "0x13881" }], // Hexadecimal version of 80001, prefixed with 0x
- })
- } catch (error) {
- if (error.code === 4902) {
- try {
- await ethereum.request({
- method: "wallet_addEthereumChain",
- params: [
- {
- chainId: "0x13881", // Hexadecimal version of 80001, prefixed with 0x
- chainName: "POLYGON Mumbai",
- nativeCurrency: {
- name: "MATIC",
- symbol: "MATIC",
- decimals: 18,
- },
- rpcUrls: [
- "https://speedy-nodes-nyc.moralis.io/cebf590f4bcd4f12d78ee1d4/polygon/mumbai",
- ],
- blockExplorerUrls: ["https://explorer-mumbai.maticvigil.com/"],
- iconUrls: [""],
- },
- ],
- })
- } catch (addError) {
- console.log("Did not add network")
- }
- }
- }
- }
-
- async function ConnectWallet() {
- // if (window.ethereum)
- await window.ethereum.enable()
- const accounts = await window.ethereum.request({
- method: "eth_requestAccounts",
- })
- if (accounts.length > 0) {
- setethAccount(accounts[0])
- console.log("Got ethAccount", accounts[0])
- if (typeof window !== "undefined") {
- localStorage.setItem("ethAccount", accounts[0])
- }
- loginSig()
- setLogined(true)
- }
- }
-
- async function DisconnectWallet() {
- setLogined(false)
- setethAccount(null)
- console.log("Killing the wallet connection", provider)
- if (provider && provider.close) {
- await provider.close()
- provider = null
- }
- if (typeof window !== "undefined") {
- localStorage.removeItem("ethAccount")
- localStorage.removeItem("sig_login")
- }
- }
-
- function getBrief(astr) {
- if (!astr) return ""
- return astr.substring(0, 6) + "..." + astr.substr(astr.length - 4)
- }
-
- return (
-
-
-
Creative Comomons NFT Playground
-
-
-
-
-
-
-
-
-
-
- )
-}
-
-export default Marketplace
diff --git a/pages/_app.tsx b/pages/_app.tsx
new file mode 100644
index 0000000..9587541
--- /dev/null
+++ b/pages/_app.tsx
@@ -0,0 +1,234 @@
+import "../styles/globals.css"
+import "../styles/markdown.css"
+import Head from "next/head"
+import { ethers, providers } from "ethers"
+import { Menu, Transition } from "@headlessui/react"
+import { Fragment, useCallback, useMemo, useReducer, useState } from "react"
+import { ChevronDownIcon } from "@heroicons/react/solid"
+import { Navigation } from "../components/Navigation"
+import {
+ DOMAIN,
+ signInfo,
+ STORAGE_KEY_ACCOUNT,
+ STORAGE_KEY_ACCOUNT_SIG,
+ SUPPORT_NETWORKS
+} from "../constants"
+import { useAsync, useLocalStorageValue, useMountEffect } from "@react-hookz/web"
+import { initialWeb3State, Web3Context, web3Reducer } from "../context/web3Context"
+import { createProvider, switchNetwork } from "../web3"
+import { getBrief } from "../web3/utils"
+import axios from "axios"
+import { FrontendVersion } from "../version.js"
+
+
+function App({ Component, pageProps }) {
+ const [accountInLocal, setLocalAccount, removeLocalAccount] = useLocalStorageValue(STORAGE_KEY_ACCOUNT)
+ const [sigInLocal, setLocalSig, removeLocalSig] = useLocalStorageValue(STORAGE_KEY_ACCOUNT_SIG)
+ const [state, dispatch] = useReducer(web3Reducer, { ...initialWeb3State, account: accountInLocal })
+ const { account, provider, chainId } = state
+ const isSupportCurrentNetwork = SUPPORT_NETWORKS.includes(chainId)
+ const [backendVersion, setBackendVersion] = useState("err")
+
+ const [, actions] = useAsync(async() => {
+ if (!sigInLocal || !accountInLocal) return
+ const cachedProvider = await createProvider(undefined, (id) => dispatch({ type: "SET_CHAIN_ID", chainId: id }))
+ if (!cachedProvider) return
+ dispatch({ type: 'SET_WEB3_PROVIDER', provider: cachedProvider })
+ const web3Provider = new providers.Web3Provider(cachedProvider)
+ const signer = web3Provider.getSigner()
+ const address = await signer.getAddress()
+ const network = await web3Provider.getNetwork()
+ if (address !== accountInLocal) return
+ dispatch({
+ type: 'SET_WEB3_PROVIDER',
+ provider: cachedProvider,
+ web3Provider,
+ account: address,
+ chainId: network.chainId,
+ })
+ })
+
+ useMountEffect(actions.execute)
+ useMountEffect(getBackendVersion)
+
+ async function getBackendVersion() {
+ try {
+ const dweb_search_ver_api = "https://dweb-search-api.anwen.cc/version"
+ const ret = await axios.get(dweb_search_ver_api)
+ if (ret.status == 200 && 'version' in ret.data) {
+ setBackendVersion(ret.data['version'])
+ }
+ } catch (error) {
+ console.log(error)
+ }
+ }
+
+ const web3ContextValue = useMemo(() => {
+ return { state, dispatch }
+ }, [state, dispatch])
+
+ const connectWallet = useCallback(async function() {
+ const provider = await createProvider(undefined, (id) => dispatch({ type: "SET_CHAIN_ID", chainId: id }))
+ if (provider.chainId !== '0x13881') {
+ await switchNetwork(provider)
+ }
+ if (!provider) return
+ dispatch({ type: 'SET_WEB3_PROVIDER', provider })
+
+ const web3Provider = new providers.Web3Provider(provider)
+ const signer = web3Provider.getSigner()
+ const address = await signer.getAddress()
+
+ if (!sigInLocal) {
+ const signature = await signer._signTypedData(DOMAIN, signInfo.types, signInfo.message)
+ const verifiedAddress = ethers.utils.verifyTypedData(
+ DOMAIN,
+ signInfo.types,
+ signInfo.message,
+ signature,
+ )
+ if (verifiedAddress !== address) return
+ setLocalSig(signature)
+ }
+
+ const network = await web3Provider.getNetwork()
+ setLocalAccount(address)
+
+ dispatch({
+ type: 'SET_WEB3_PROVIDER',
+ provider,
+ web3Provider,
+ account: address,
+ chainId: network.chainId,
+ })
+ }, [])
+
+ const disconnectWallet = async() => {
+ dispatch({
+ type: 'SET_WEB3_PROVIDER',
+ provider: undefined,
+ web3Provider: undefined,
+ account: undefined,
+ chainId: undefined,
+ })
+ removeLocalAccount()
+ removeLocalSig()
+ }
+
+
+ const renderActionButton = () => {
+ if (!sigInLocal || !accountInLocal) {
+ return (
+
+ )
+ }
+
+ if (!isSupportCurrentNetwork) {
+ return (
+
+ )
+
+ }
+ return null
+ }
+ return (
+
+
+
Creative Comomons NFT Playground
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default App
diff --git a/pages/all-assets.js b/pages/all-assets.js
index cc6625f..44fd498 100644
--- a/pages/all-assets.js
+++ b/pages/all-assets.js
@@ -7,22 +7,17 @@ import { nftaddress, nftmarketaddress } from "../config"
import NFT from "../artifacts/contracts/NFT.sol/NFT.json"
import Market from "../artifacts/contracts/Market.sol/NFTMarket.json"
+import { useWeb3 } from "../hooks/useWeb3"
export default function AllAssets() {
const [nfts, setNfts] = useState([])
const [sold, setSold] = useState([])
const [loadingState, setLoadingState] = useState("not-loaded")
+ const provider = useWeb3()
useEffect(() => {
loadNFTs()
}, [])
async function loadNFTs() {
- const web3Modal = new Web3Modal({
- network: "mainnet",
- cacheProvider: true,
- })
- const connection = await web3Modal.connect()
- // const provider = new ethers.providers.JsonRpcProvider()
- const provider = new ethers.providers.Web3Provider(connection)
const signer = provider.getSigner()
// const marketContract = new ethers.Contract(nftmarketaddress, Market.abi, provider)
@@ -36,7 +31,7 @@ export default function AllAssets() {
// const data = await marketContract.fetchItemsCreated()
const items = await Promise.all(
- data.map(async (i) => {
+ data.map(async(i) => {
const tokenUri = await tokenContract.tokenURI(i.tokenId)
const meta = await axios.get(tokenUri)
let price = ethers.utils.formatUnits(i.price.toString(), "ether")
diff --git a/pages/article.js b/pages/article.js
index bf60c6b..97034ff 100644
--- a/pages/article.js
+++ b/pages/article.js
@@ -15,6 +15,7 @@ import Market from "../artifacts/contracts/Market.sol/NFTMarket.json"
import { providers } from "ethers"
import { init } from "@textile/eth-storage"
+import { useWeb3 } from "../hooks/useWeb3"
let ethAccount
let myethAccount
@@ -22,6 +23,7 @@ let cid
let nft = {}
export default function MyAssets() {
// const [nft, setNft] = useState({})
+ const provider = useWeb3()
const [loadingState, setLoadingState] = useState("not-loaded")
const router = useRouter()
@@ -45,10 +47,7 @@ export default function MyAssets() {
}
async function storeNFTtoFilecoin() {
- await window.ethereum.enable()
- const provider = new providers.Web3Provider(window.ethereum)
const wallet = provider.getSigner()
-
const storage = await init(wallet)
// const blob = new Blob(["Hello, world!"], { type: "text/plain" });
const jsonse = JSON.stringify(nft)
@@ -75,9 +74,6 @@ export default function MyAssets() {
}
async function createSale(url) {
- const web3Modal = new Web3Modal()
- const connection = await web3Modal.connect()
- const provider = new ethers.providers.Web3Provider(connection)
const signer = provider.getSigner()
/* next, create the item */
diff --git a/pages/articles-my.js b/pages/articles-my.js
index 50a9f78..9577ec8 100644
--- a/pages/articles-my.js
+++ b/pages/articles-my.js
@@ -1,6 +1,7 @@
import { ethers } from "ethers"
import { useEffect, useState } from "react"
import axios from "axios"
+import { useAccount } from "../hooks/useAccount"
import { nftmarketaddress, nftaddress } from "../config"
@@ -8,10 +9,7 @@ let ethAccount
export default function MyAssets() {
const [nfts, setNfts] = useState([])
const [loadingState, setLoadingState] = useState("not-loaded")
-
- if (typeof window !== "undefined") {
- ethAccount = localStorage.getItem("ethAccount")
- }
+ const ethAccount = useAccount()
useEffect(() => {
loadNFTs()
diff --git a/pages/articles.js b/pages/articles.js
index 7ed1af6..f0fdb60 100644
--- a/pages/articles.js
+++ b/pages/articles.js
@@ -4,9 +4,11 @@ import axios from "axios"
import { useRouter } from "next/router"
import { nftmarketaddress, nftaddress } from "../config"
+import { useAccount } from "../hooks/useAccount"
let ethAccount
export default function MyAssets() {
+ const account = useAccount()
const [nfts, setNfts] = useState([])
const [loadingState, setLoadingState] = useState("not-loaded")
@@ -14,7 +16,6 @@ export default function MyAssets() {
// },[]);
const router = useRouter()
- console.log(router.query)
if ("author" in router.query) {
ethAccount = router.query.author
console.log("ethAccount", ethAccount)
diff --git a/pages/ccmarket.js b/pages/ccmarket.js
index 29065db..d2f30a0 100644
--- a/pages/ccmarket.js
+++ b/pages/ccmarket.js
@@ -29,7 +29,7 @@ export default function Home() {
const data = await marketContract.fetchMarketItems()
const items = await Promise.all(
- data.map(async (i) => {
+ data.map(async(i) => {
const tokenUri = await tokenContract.tokenURI(i.tokenId)
const meta = await axios.get(tokenUri)
console.log(i.price.toString(), "raw price")
diff --git a/pages/create.tsx b/pages/create.tsx
index 6934c7f..f6a570f 100644
--- a/pages/create.tsx
+++ b/pages/create.tsx
@@ -1,16 +1,17 @@
-import {useRouter} from "next/router"
+import { useRouter } from "next/router"
import axios from "axios"
-import {useForm} from "react-hook-form";
-import {yupResolver} from '@hookform/resolvers/yup';
-import * as yup from "yup";
-import {addNFTToNFTStorage} from "../services/NFTStorage";
-import {addToIPFS} from "../services/IPFSHttpClient";
+import { useForm } from "react-hook-form"
+import { yupResolver } from '@hookform/resolvers/yup'
+import * as yup from "yup"
+import { addNFTToNFTStorage } from "../services/NFTStorage"
+import { addToIPFS } from "../services/IPFSHttpClient"
-import {nftaddress, nftmarketaddress} from "../config"
+import { nftaddress, nftmarketaddress } from "../config"
import NFT from "../artifacts/contracts/NFT.sol/NFT.json"
import Market from "../artifacts/contracts/Market.sol/NFTMarket.json"
-import {useEffect, useState} from "react";
-import {InputFieldError} from "../components/InputFieldError";
+import { useEffect, useState } from "react"
+import { InputFieldError } from "../components/InputFieldError"
+import { useAccount } from "../hooks/useAccount"
interface IFormInputs {
price: string
@@ -27,31 +28,26 @@ const schema = yup.object({
description: yup.string().required("Content is not optional"),
s_tags: yup.string().required("Tags is not optional"),
author: yup.string().required("Authors Name is not optional"),
- files: yup.mixed().test({ test: (value) => value.length, message: "Feature Image is not optional"}),
-}).required();
+ files: yup.mixed().test({ test: (value) => value.length, message: "Feature Image is not optional" }),
+}).required()
export default function CreateItem() {
const router = useRouter()
- // TODO: create useAccount Hook and listen account change
- const [ethAccount, setEthAccount] = useState()
+ const account = useAccount()
const [preview, setPreview] = useState()
useEffect(() => {
- if (typeof window !== "undefined") {
- const account = localStorage.getItem("ethAccount")
- if (!account) {
- alert("No ETH Account, Please login")
- router.push("/articles-all")
- return
- }
- setEthAccount(account)
+ if (!account) {
+ alert("No ETH Account, Please login")
+ router.push("/articles-all")
+ return
}
}, [])
- const {register, handleSubmit, formState: {errors, isSubmitting}, watch} = useForm({
+ const { register, handleSubmit, formState: { errors, isSubmitting }, watch } = useForm({
resolver: yupResolver(schema)
- });
- const watchedFiles = watch("files", null);
+ })
+ const watchedFiles = watch("files", null)
useEffect(() => {
if (!watchedFiles) return
@@ -61,9 +57,9 @@ export default function CreateItem() {
setPreview(url)
}, [watchedFiles])
- const onSubmit = async (data: IFormInputs) => {
+ const onSubmit = async(data: IFormInputs) => {
const file = data.files[0]
- const {type: filetype, size: filesize, name: filename} = file
+ const { type: filetype, size: filesize, name: filename } = file
const addedImage = await addToIPFS(file)
const imageURL = `https://ipfs.infura.io/ipfs/${addedImage.path}`
const license = "CC-BY-SA"
@@ -72,7 +68,7 @@ export default function CreateItem() {
const authors = [{
name: data.author,
wallet: {
- eth: ethAccount,
+ eth: account,
},
}]
const nftData = JSON.stringify({
@@ -99,7 +95,7 @@ export default function CreateItem() {
axios.defaults.headers.common['address'] = aethAccount
const ret = await axios.post(dweb_search_url, {
path: addedNFT.path,
- eth: ethAccount,
+ eth: account,
name: data.name,
image: imageURL,
tags: data.s_tags,
@@ -145,15 +141,15 @@ export default function CreateItem() {
-
-- Markdown Tips:
- 参考1
- 参考2
-
-
+
-- Markdown Tips:
+ 参考1
+ 参考2
+
+
diff --git a/pages/edit.tsx b/pages/edit.tsx
index 8603abb..37d4e1b 100644
--- a/pages/edit.tsx
+++ b/pages/edit.tsx
@@ -1,18 +1,19 @@
-import {useRouter} from "next/router"
+import { useRouter } from "next/router"
import axios from "axios"
-import {useForm} from "react-hook-form";
-import {yupResolver} from '@hookform/resolvers/yup';
-import * as yup from "yup";
-import {useEffect, useState} from "react";
+import { useForm } from "react-hook-form"
+import { yupResolver } from '@hookform/resolvers/yup'
+import * as yup from "yup"
+import { useEffect, useState } from "react"
-import {addNFTToNFTStorage} from "../services/NFTStorage";
-import {addToIPFS} from "../services/IPFSHttpClient";
-import {loadNFT} from "../services/backend";
+import { addNFTToNFTStorage } from "../services/NFTStorage"
+import { addToIPFS } from "../services/IPFSHttpClient"
+import { loadNFT } from "../services/backend"
-import {nftaddress, nftmarketaddress} from "../config"
+import { nftaddress, nftmarketaddress } from "../config"
import NFT from "../artifacts/contracts/NFT.sol/NFT.json"
import Market from "../artifacts/contracts/Market.sol/NFTMarket.json"
-import {InputFieldError} from "../components/InputFieldError";
+import { InputFieldError } from "../components/InputFieldError"
+import { useAccount } from "../hooks/useAccount"
let nft // TODO: use useState?
@@ -33,25 +34,20 @@ const schema = yup.object({
s_tags: yup.string().required("Tags is not optional"),
author: yup.string().required("Authors Name is not optional"),
files: yup.mixed(), // TODO: special case, not a img?
-}).required();
+}).required()
export default function EditItem() {
const router = useRouter()
- // TODO: create useAccount Hook and listen account change
- const [ethAccount, setEthAccount] = useState()
+ const account = useAccount()
const [preview, setPreview] = useState()
const [cid, setCid] = useState()
const [loadingState, setLoadingState] = useState("not-loaded")
useEffect(() => {
- if (typeof window !== "undefined") {
- const account = localStorage.getItem("ethAccount")
- if (!account) {
- alert("No ETH Account, Please login")
- router.push("/articles-all")
- return
- }
- setEthAccount(account)
+ if (!account) {
+ alert("No ETH Account, Please login")
+ router.push("/articles-all")
+ return
}
}, [])
@@ -69,10 +65,10 @@ export default function EditItem() {
}
}, [router.query])
- const {register, handleSubmit, formState: {errors, isSubmitting}, watch} = useForm({
+ const { register, handleSubmit, formState: { errors, isSubmitting }, watch } = useForm({
resolver: yupResolver(schema)
- });
- const watchedFiles = watch("files", null);
+ })
+ const watchedFiles = watch("files", null)
useEffect(() => {
if (!watchedFiles) return
@@ -82,7 +78,7 @@ export default function EditItem() {
setPreview(url)
}, [watchedFiles])
- const onSubmit = async (data: IFormInputs) => {
+ const onSubmit = async(data: IFormInputs) => {
// upload new file is optional
let imageURL
let filesize
@@ -90,7 +86,7 @@ export default function EditItem() {
let filetype
if (data.files.length>0) {
const file = data.files[0]
- const {type: filetype, size: filesize, name: filename} = file
+ const { type: filetype, size: filesize, name: filename } = file
const addedImage = await addToIPFS(file)
imageURL = `https://ipfs.infura.io/ipfs/${addedImage.path}`
} else {
@@ -105,7 +101,7 @@ export default function EditItem() {
const authors = [{
name: data.author,
wallet: {
- eth: ethAccount,
+ eth: account,
},
}]
const nftData = JSON.stringify({
@@ -135,7 +131,7 @@ export default function EditItem() {
// headers: {"Authorization" : `Bearer ${tokenStr}`},
previous_path: cid,
path: addedNFT.path,
- eth: ethAccount,
+ eth: account,
name: data.name,
image: imageURL,
tags: data.s_tags,
@@ -187,16 +183,16 @@ export default function EditItem() {
-
-- Markdown Tips:
- 参考1
- 参考2
-
-
+
-- Markdown Tips:
+ 参考1
+ 参考2
+
+
diff --git a/pages/index.js b/pages/index.js
index 604f2cc..a394d2b 100644
--- a/pages/index.js
+++ b/pages/index.js
@@ -1,17 +1,12 @@
import { ethers } from "ethers"
import { useEffect, useState } from "react"
-import axios from "axios"
import { nftaddress, nftmarketaddress } from "../config"
import NFT from "../artifacts/contracts/NFT.sol/NFT.json"
import Market from "../artifacts/contracts/Market.sol/NFTMarket.json"
-let provider
-
export default function Home() {
- const [nfts, setNfts] = useState([])
-
return (
📢Tips
diff --git a/pages/my-collections.js b/pages/my-collections.js
index 9a35a5e..ff078d1 100644
--- a/pages/my-collections.js
+++ b/pages/my-collections.js
@@ -6,22 +6,22 @@ import { nftmarketaddress, nftaddress } from "../config"
import Market from "../artifacts/contracts/Market.sol/NFTMarket.json"
import NFT from "../artifacts/contracts/NFT.sol/NFT.json"
+import { useAccount } from "../hooks/useAccount"
+import { useWeb3Context } from "../context/web3Context"
+import { useWeb3 } from "../hooks/useWeb3"
export default function MyAssets() {
const [nfts, setNfts] = useState([])
const [loadingState, setLoadingState] = useState("not-loaded")
+ const provider = useWeb3()
+
useEffect(() => {
+ if (!provider) return
loadNFTs()
- }, [])
+ }, [provider])
+
async function loadNFTs() {
- const web3Modal = new Web3Modal({
- // network: "mainnet",
- // network: "mumbai",
- cacheProvider: true,
- })
- const connection = await web3Modal.connect()
- const provider = new ethers.providers.Web3Provider(connection)
const signer = provider.getSigner()
const marketContract = new ethers.Contract(
@@ -33,7 +33,7 @@ export default function MyAssets() {
const data = await marketContract.fetchMyNFTs()
const items = await Promise.all(
- data.map(async (i) => {
+ data.map(async(i) => {
const tokenUri = await tokenContract.tokenURI(i.tokenId)
console.log(tokenUri)
const meta = await axios.get(tokenUri)
@@ -64,7 +64,7 @@ export default function MyAssets() {
{nfts.map((nft, i) => (
-

+
diff --git a/pages/my-nfts.js b/pages/my-nfts.js
index 312fc02..75dd019 100644
--- a/pages/my-nfts.js
+++ b/pages/my-nfts.js
@@ -6,24 +6,19 @@ import { nftmarketaddress, nftaddress } from "../config"
import Market from "../artifacts/contracts/Market.sol/NFTMarket.json"
import NFT from "../artifacts/contracts/NFT.sol/NFT.json"
+import { useWeb3 } from "../hooks/useWeb3"
export default function CreatorDashboard() {
const [nfts, setNfts] = useState([])
const [sold, setSold] = useState([])
const [loadingState, setLoadingState] = useState("not-loaded")
+ const provider = useWeb3()
useEffect(() => {
+ if (!provider) return
loadNFTs()
- }, [])
+ }, [provider])
async function loadNFTs() {
- const web3Modal = new Web3Modal({
- // network: "mainnet",
- // network: "mumbai",
- cacheProvider: true,
- })
- // const connection = await web3Modal.connect("https://rpc-mumbai.matic.today")
- const connection = await web3Modal.connect()
- const provider = new ethers.providers.Web3Provider(connection)
const signer = provider.getSigner()
const marketContract = new ethers.Contract(
@@ -35,7 +30,7 @@ export default function CreatorDashboard() {
const data = await marketContract.fetchItemsCreated()
const items = await Promise.all(
- data.map(async (i) => {
+ data.map(async(i) => {
const tokenUri = await tokenContract.tokenURI(i.tokenId)
const meta = await axios.get(tokenUri)
let price = ethers.utils.formatUnits(i.price.toString(), "ether")
diff --git a/pages/nft-market.js b/pages/nft-market.js
index 1a964bb..8896f37 100644
--- a/pages/nft-market.js
+++ b/pages/nft-market.js
@@ -32,7 +32,7 @@ export default function Home() {
console.log("data", data)
const items = await Promise.all(
- data.map(async (i) => {
+ data.map(async(i) => {
const tokenUri = await tokenContract.tokenURI(i.tokenId)
const meta = await axios.get(tokenUri)
console.log("tokenUri", tokenUri)
diff --git a/web3/index.ts b/web3/index.ts
new file mode 100644
index 0000000..54517d0
--- /dev/null
+++ b/web3/index.ts
@@ -0,0 +1,57 @@
+import Web3Modal from "web3modal";
+import BigNumber from "bignumber.js";
+import {BLOCK_EXPLORER_URLS, RPC_URLS} from "../constants";
+
+export const createProvider = async (network?: string, onChainChange?: (chainId: number) => void) => {
+ try {
+ const web3Modal = new Web3Modal({
+ network: network ?? '0x13881',
+ cacheProvider: true,
+ })
+ const provider = await web3Modal.connect()
+ if (!provider) return null
+ onChainChange((new BigNumber(provider.chainId)).toNumber())
+ if (onChainChange) {
+ provider.on('chainChanged', (id) => {
+ onChainChange((new BigNumber(id)).toNumber())
+ })
+ }
+ return provider
+ } catch {
+ return null
+ }
+}
+
+export const switchNetwork = async (provider: any) => {
+ if (!provider) return
+ try {
+ await provider.request({
+ method: "wallet_switchEthereumChain",
+ params: [{chainId: "0x13881"}],
+ })
+ } catch (error) {
+ if (error.code === 4902) {
+ try {
+ await provider.request({
+ method: "wallet_addEthereumChain",
+ params: [
+ {
+ chainId: "0x13881",
+ chainName: "POLYGON Mumbai",
+ nativeCurrency: {
+ name: "MATIC",
+ symbol: "MATIC",
+ decimals: 18,
+ },
+ rpcUrls: RPC_URLS,
+ blockExplorerUrls: BLOCK_EXPLORER_URLS,
+ iconUrls: [""],
+ },
+ ],
+ })
+ } catch (addError) {
+ console.log("Did not add network")
+ }
+ }
+ }
+}
diff --git a/web3/utils.ts b/web3/utils.ts
new file mode 100644
index 0000000..774b6aa
--- /dev/null
+++ b/web3/utils.ts
@@ -0,0 +1,4 @@
+export function getBrief(astr) {
+ if (!astr) return ""
+ return astr.substring(0, 6) + "..." + astr.substr(astr.length - 4)
+}