Created
January 11, 2022 15:56
-
-
Save rashgaroth/af7b99eef284ccfdfa37e35309964ae1 to your computer and use it in GitHub Desktop.
Web3 hooks for communicate with smart contract
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ============================================= | |
// Dwiyan Putra (nefti.dwiyan@gmail.com) | |
// Jan 11 2022 | |
// ============================================= | |
import * as _aP from 'storage/profile/actions' //your actions (redux) | |
import * as _aW from 'storage/wallet/walletActions' //your actions (redux) | |
import React, { useCallback, useEffect, useMemo, useReducer, useState } from 'react' | |
import { useDispatch, useSelector } from 'react-redux' // im using redux for state management | |
import Web3 from 'web3' | |
import Web3Modal from 'web3modal' | |
import { providerOptions } from 'common/provider/Web3Connectors' // your providerOptions for web3 connect :) | |
import { toast } from 'react-hot-toast' // for giving informations for users | |
// ============================================= | |
// Smart contract address list, please use your own smart contract address for this | |
// ============================================= | |
let web3Modal | |
const scMarketplace = process.env.CONTRACT_ADDRESS_MARKETPLACE | |
const scWallet = process.env.CONTRACT_ADDRESS_WALLET | |
const scGallery = process.env.CONTRACT_ADDRESS_OWN_GALLERY | |
const abiWallet = process.env.ABI_WALLET | |
const abiGallery = process.env.ABI_GALLERY | |
const abiMarketplace = process.env.ABI_MARKETPLACE | |
// ============================================= | |
// Done | |
// ============================================= | |
if (typeof window !== 'undefined') { | |
web3Modal = new Web3Modal({ | |
cacheProvider: true, | |
providerOptions: providerOptions, | |
}) | |
} | |
const initialWeb3State = { | |
provider: null, | |
web3Provider: null, | |
address: '', | |
balance: '0', | |
decimal: '' | |
} | |
const web3Reducer = (state = initialWeb3State, action) => { | |
switch(action.type){ | |
case 'SET_PROVIDER': | |
return { | |
...state, | |
provider: action.provider.provider, | |
address: action.provider.address, | |
balance: action.provider.balance, | |
web3Provider: action.provider.web3Provider, | |
decimal: action.provider.decimal, | |
} | |
case 'SET_ADDR': | |
return { | |
...state, | |
address: action.data, | |
} | |
} | |
} | |
export const Web3Context = React.createContext(null) | |
export const Web3Provider = ({ children }) => { | |
const dispatcherRedux = useDispatch() | |
const [state, dispatch] = useReducer(web3Reducer, initialWeb3State) | |
const [provider, setProvider] = useState(null) | |
const [account, setAccount] = useState(null) | |
const [walletContract, setWalletContract] = useState(null) | |
const [galleryContract, setGalleryContract] = useState(null) | |
const [marketplaceContract, setMarketplaceContract] = useState(null) | |
const [isConnected, setIsConnected] = useState(false) | |
const [withWeb3, setWithWeb3] = useState(null) | |
// Connect wallet function | |
const web3Connect = () => new Promise(async(resolve, reject) => { | |
try{ | |
const prov = await web3Modal.connect() | |
if(prov){ | |
console.log(prov, "@prov") | |
resolve({ | |
provider: prov | |
}) | |
}else{ | |
resolve({ | |
provider: null | |
}) | |
} | |
}catch(e){ | |
if (typeof e !== 'undefined') { | |
if (e.message === 'Already processing eth_requestAccounts. Please wait.') { | |
toast.error('Already processing. Please go to your wallet provider and sync with Neftipedia') | |
resolve({ | |
provider: null | |
}) | |
} else { | |
if (e.message.includes("Returned values aren't valid")) { | |
toast.error("Please change your network") | |
resolve({ | |
provider: null | |
}) | |
} else { | |
toast.error(e.message) | |
resolve({ | |
provider: null | |
}) | |
} | |
} | |
} | |
resolve({ | |
provider: null | |
}) | |
} | |
}) | |
// Connect wallet function | |
const connect = async () => { | |
console.log("@connecting") | |
try { | |
web3Connect().then(async(data) => { | |
if(data.provider){ | |
const prov = data.provider | |
const web3 = new Web3(prov) | |
const account = await web3.eth.getAccounts() | |
const walletContract = new web3.eth.Contract(abiWallet, scWallet) | |
const galleryContract = new web3.eth.Contract(abiGallery, scGallery) | |
const marketplaceContract = new web3.eth.Contract(abiMarketplace, scMarketplace) | |
const tokenBalance = await walletContract.methods.balanceOf(account[0]).call() | |
console.info("@Connecting to web3... ") | |
await setWithWeb3(web3) | |
await setProvider(prov) | |
setWalletContract(walletContract) | |
setGalleryContract(galleryContract) | |
setMarketplaceContract(marketplaceContract) | |
setAccount(account[0]) | |
setIsConnected(true) | |
console.info("@Connected") | |
dispatcherRedux(_aP.auth({ | |
wallet: account[0].toLocaleLowerCase() | |
})) | |
await dispatch( | |
{ | |
type: 'SET_PROVIDER', | |
provider: { | |
...state, | |
address: account[0], | |
provider: prov, | |
web3Provider: web3, | |
balance: tokenBalance, | |
} | |
}, | |
) | |
await dispatch( | |
{ | |
type: 'SET_ADDR', | |
data: account[0] | |
} | |
) | |
} | |
}).catch((e) => { | |
toast.error(e.message) | |
}) | |
} catch (e) { | |
if (typeof e !== 'undefined') { | |
toast.error(e.message) | |
} | |
} | |
} | |
// Disconnect wallet function | |
const disconnect = async () => { | |
console.log(provider, state.provider, "@state.provider") | |
await web3Modal.clearCachedProvider() | |
if (provider.disconnect && typeof provider.disconnect === 'function') { | |
await provider.disconnect() | |
} | |
setIsConnected(false) | |
setProvider(null) | |
setWithWeb3(null) | |
await dispatch( | |
{ | |
type: 'SET_PROVIDER', | |
provider: { | |
...state, | |
provider: null, | |
web3Provider: null, | |
address: '', | |
balance: '0', | |
} | |
} | |
) | |
await dispatcherRedux( | |
_aP.successLogin({ | |
jwt: '', | |
name: 'No Account', | |
profile_cover: '', | |
profile_picture: '', | |
status: '', | |
verified_at: '', | |
wallet_address: '', | |
id: '', | |
email: '', | |
badge: '', | |
}), | |
) | |
} | |
// React lifecycle | |
let temp = true | |
useEffect(async () => { | |
if (typeof state.provider !== 'undefined' && state.provider !== null) { | |
if (state.provider.on) { | |
const handleAccountsChanged = async (accountCallback = []) => { | |
temp = true | |
try{ | |
if(accountCallback){ | |
if(temp){ | |
console.info("@fromAccountChanged") | |
const web3 = new Web3(state.provider) | |
const tokenBalance = await walletContract.methods.balanceOf(accountCallback[0]).call() | |
await setWithWeb3(web3) | |
await setProvider(state.provider) | |
setAccount(accountCallback[0]) | |
setIsConnected(true) | |
dispatcherRedux(_aP.auth({ | |
wallet: accountCallback[0] | |
})) | |
dispatch({ | |
type: 'SET_PROVIDER', | |
provider: { | |
...state, | |
web3Provider: web3, | |
balance: tokenBalance, | |
address: accountCallback[0], | |
} | |
}) | |
}else{ | |
console.info("@account changed with:", accountCallback[0]) | |
} | |
temp = true | |
} | |
}catch(e){ | |
toast.error(e.message) | |
} | |
} | |
//https://docs.ethers.io/v5/concepts/best-practices/#best-practices--network-changes | |
const handleChainChanged = (_hexChainId = '') => { | |
console.log("on chain changed") | |
window.location.reload() | |
} | |
const handleDisconnect = () => { | |
disconnect() | |
} | |
state.provider.on('accountsChanged', handleAccountsChanged) | |
state.provider.on('chainChanged', handleChainChanged) | |
state.provider.on('disconnect', handleDisconnect) | |
} | |
} | |
return async () => { | |
console.info("@on cleaning handler") | |
dispatch( | |
{ | |
type: 'SET_PROVIDER', | |
provider: { | |
...state, | |
provider: null, | |
web3Provider: null, | |
address: '', | |
balance: '0', | |
} | |
} | |
) | |
await dispatcherRedux( | |
_aP.successLogin({ | |
jwt: '', | |
name: 'Neftipedia Account', | |
profile_cover: '', | |
profile_picture: '', | |
status: '', | |
verified_at: '', | |
wallet_address: '', | |
id: '', | |
email: '', | |
badge: '', | |
}), | |
) | |
} | |
}, [state.provider]) | |
useEffect(() => { | |
if (web3Modal.cachedProvider) { | |
connect() | |
setIsConnected(true) | |
}else{ | |
setIsConnected(false) | |
} | |
}, []) | |
const toTokenHex = (num) => { | |
const { toBN, toHex } = withWeb3.utils | |
const isFloat = n => { | |
return Number(n) === n && n % 1 !== 0 | |
} | |
const toToken = val => { | |
let numb = isFloat(val) ? val * 100 : val | |
let res = toBN(10).pow(toBN(16)).mul(toBN(numb)) | |
if (isFloat(val)) res = res.div(toBN(100)) | |
return res | |
} | |
return toHex(toToken(num)) | |
} | |
// used for global approval :) | |
// another function for communicate with smart contract | |
const approval = (sc, amount) => new Promise((resolve, reject) => { | |
try{ | |
walletContract.methods.approve(sc, amount) | |
.send({ from: account }) | |
.on('confirmation', (confirmationNumber, receipt) => { | |
console.info(receipt, "@approvalReceipt") | |
resolve({ | |
success: true, | |
data: receipt | |
}) | |
}) | |
.on('error', (error, receipt) => { | |
console.error(error, receipt) | |
reject({ | |
success: false, | |
data: error.message | |
}) | |
}) | |
}catch(e){ | |
reject({ | |
success: false, | |
data: e.message | |
}) | |
} | |
}) | |
const allowance = (sc) => new Promise((resolve, reject) => { | |
try{ | |
walletContract.methods.allowance(account, sc) | |
.send({ from: account }) | |
.on('confirmation', (confirmationNumber, receipt) => { | |
console.info(receipt, "@allowedReceipt") | |
resolve({ | |
success: true, | |
data: receipt | |
}) | |
}) | |
.on('error', (error, receipt) => { | |
console.error(error, receipt) | |
reject({ | |
success: false, | |
data: error.message | |
}) | |
}) | |
}catch(e){ | |
reject({ | |
success: false, | |
data: e.message | |
}) | |
} | |
}) | |
const txDirectBuy = (saleId, purchaseId, amount) => new Promise((resolve, reject) => { | |
try{ | |
marketplaceContract.methods.txDirectBuy(saleId, purchaseId, amount) | |
.send({ from: account }) | |
.on('confirmation', (confirmationNumber, receipt) => { | |
console.info(receipt, "@txDirectBuyReceipt") | |
resolve({ | |
success: true, | |
data: receipt | |
}) | |
}) | |
.on('error', (error, receipt) => { | |
console.error(error, receipt) | |
reject({ | |
success: false, | |
data: error.message | |
}) | |
}) | |
}catch(e){ | |
resolve({ | |
success: false, | |
data: { | |
message: e.message | |
} | |
}) | |
} | |
}) | |
const refreshBalance = () => new Promise(async(resolve, reject) => { | |
try{ | |
if(state.provider !== null){ | |
const web3 = new Web3(state.provider) | |
web3.eth.getAccounts() | |
.then(async(account) => { | |
if(account[0]){ | |
await walletContract.methods.balanceOf(account[0]).call({ from: account[0] }, (error, res) => { | |
if(res){ | |
console.info(res, "@balanceOfResult") | |
dispatch({ | |
type: 'SET_PROVIDER', | |
provider: { | |
...state, | |
web3Provider: web3, | |
balance: res, | |
address: account[0], | |
} | |
}) | |
resolve({ | |
success: true, | |
data: res | |
}) | |
}else{ | |
console.error(error, "@errorgetBalance") | |
reject({ | |
success: false, | |
data: error.message | |
}) | |
} | |
}) | |
} | |
}) | |
} | |
}catch(e){ | |
reject({ | |
success: false, | |
data: e.message | |
}) | |
} | |
}) | |
const values = useMemo( | |
() => ({ | |
connect, | |
disconnect, | |
toTokenHex, | |
approval, | |
allowance, | |
txDirectBuy, | |
refreshBalance, | |
isConnected, | |
provider, | |
account, | |
walletContract, | |
galleryContract, | |
marketplaceContract, | |
state, | |
withWeb3, | |
}), | |
[ | |
isConnected, | |
account, | |
provider, | |
state, | |
withWeb3 | |
], | |
) | |
return <Web3Context.Provider value={values}>{children}</Web3Context.Provider> | |
} | |
export default function useWeb3() { | |
const context = React.useContext(Web3Context) | |
if (context === undefined) { | |
throw new Error('useWeb3 hook must be with a useWeb3Provider') | |
} | |
return context | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment