Skip to content

Commit

Permalink
Merge pull request #176 from TokenScript/feat/auto-chain-switch
Browse files Browse the repository at this point in the history
feat: implement automatic chain switching & WC v2 chains option
  • Loading branch information
nicktaras authored Mar 6, 2023
2 parents 775b862 + efbc11a commit fc2b9d5
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 78 deletions.
2 changes: 1 addition & 1 deletion art-gallery-medium-article-website/config-overrides.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module.exports = function override(config) {
config.resolve.fallback = fallback;
config.plugins = (config.plugins || []).concat([
new webpack.ProvidePlugin({
process: "process/browser",
process: "process/browser.js",
Buffer: ["buffer", "Buffer"],
}),
]);
Expand Down
6 changes: 6 additions & 0 deletions ecommerce-store-website/src/base/utils/interact.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ export const safeMint = async ({

window.negotiator.ui.dismissLoader()

// Automatically refresh tokens after 20 seconds
window.negotiator.getTokenStore().clearCachedTokens();
setTimeout(() => {
window.negotiator.negotiate();
}, 20000);

return {
success: true,
status: "✅ Check out your transaction: " + chain + " " + tx?.hash
Expand Down
71 changes: 46 additions & 25 deletions ecommerce-store-website/src/providers/TokenContextProvider.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { createContext, useState, useEffect } from "react";
import React, {createContext, useState, useEffect, useMemo} from "react";
import { chainMap } from "src/base/utils/network";

const TokenContext = createContext({
Expand All @@ -7,7 +7,8 @@ const TokenContext = createContext({
proof: {},
negotiator: {},
walletStatus: '',
chainId: ''
chainId: '',
switchChain: undefined
});

const mumbaiIssuers = [
Expand Down Expand Up @@ -81,7 +82,12 @@ const TokenContextProvider = (props) => {
position: "bottom-right",
},
ipfsBaseUrl:
"https://smart-token-labs-demo-server.mypinata.cloud/ipfs/"
"https://smart-token-labs-demo-server.mypinata.cloud/ipfs/",
walletOptions: {
walletConnectV2: {
chains: ["eip155:5", "eip155:80001"]
}
}
});

setNegotiator(newNegotiator);
Expand Down Expand Up @@ -114,36 +120,51 @@ const TokenContextProvider = (props) => {
setWallet(walletConnection[0]);
}
});

const resetIssuers = (networkId) => {
if (!networkId) return;

setChainId(networkId);

const normalisedNetworkId = chainMap[networkId]
? chainMap[networkId]
: "";

switch (normalisedNetworkId) {
case "Goerli":
newNegotiator.negotiate(goerliIssuers);
break;
case "Mumbai":
newNegotiator.negotiate(mumbaiIssuers);
break;
default:
break;
}
};

newNegotiator.negotiate();
});

}, []);

const resetIssuers = (networkId) => {
if (!networkId) return;

if (typeof networkId === "string")
networkId = parseInt(networkId, 16);

console.log("New chain ID: ", networkId);

setChainId(networkId);

const normalisedNetworkId = chainMap[networkId]
? chainMap[networkId]
: "";

switch (normalisedNetworkId) {
case "Goerli":
window.negotiator.negotiate(goerliIssuers);
break;
case "Mumbai":
window.negotiator.negotiate(mumbaiIssuers);
break;
default:
break;
}
};

async function switchChain(nChainId){
// Try automatically switching to Goerli
await wallet.provider.send("wallet_switchEthereumChain", [{chainId: "0x" + Number(nChainId).toString(16)}]);
resetIssuers(nChainId);
}

const tokenContextProviderValue = useMemo(
() => ({ tokens, negotiator, wallet, proof, walletStatus, chainId, switchChain }),
[tokens, negotiator, wallet, proof, walletStatus, chainId, switchChain]
);

return (
<TokenContext.Provider value={{ tokens, negotiator, wallet, proof, walletStatus, chainId }}>
<TokenContext.Provider value={tokenContextProviderValue}>
{props.children}
</TokenContext.Provider>
);
Expand Down
99 changes: 61 additions & 38 deletions ecommerce-store-website/src/ui/components/minter/minter.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,13 @@ import styles from './minter.module.scss';
export default function Minter({ className }) {


const { wallet: walletInstance, walletStatus, chainId, tokens: selectedTokens } = useContext(TokenContext);
const { wallet: walletInstance, walletStatus, chainId, tokens: selectedTokens, switchChain } = useContext(TokenContext);

const [submissionStatus, setSubmissionStatus] = useState('');
const [mintedNFTs, setMintedNFTs ] = useState([]);
const [changeOfNetworkRequired, setChangeOfNetworkRequired] = useState(false);
const [popUpMsg, setPopUpMsg] = useState('')

const chain = chainMap[chainId] ? chainMap[chainId] : 'unsupported chain: ' + chainId;
const [chain, setChain] = useState();
const nftCollections = nftDataStore;

const [mintButonState, setMintButonState] = useState(nftDataStore.map(()=>false));
Expand All @@ -37,6 +36,10 @@ export default function Minter({ className }) {
if ( submissionStatus && !walletStatus ) setSubmissionStatus( walletStatus );
}, [ walletStatus ] );

useEffect(() => {
setChain(chainMap[chainId] ? chainMap[chainId] : 'unsupported chain: ' + chainId);
}, [chainId]);

const onClosePopUpEvent = () => {
setChangeOfNetworkRequired(false);
}
Expand All @@ -47,35 +50,49 @@ export default function Minter({ className }) {
return;
}

let curChain = chain;

if( !nft.contracts[chain] ) {
console.log("!nft.contracts[ chain ]", chainId);
console.log("!nft.contracts[ chain ]", chain, chainId);

chainId
? setPopUpMsg(`Please change your wallet's network to either Goerli or Mumbai.`)
: setPopUpMsg(`Please connect your wallet to continue.`)

setChangeOfNetworkRequired(true);
} else {
setChangeOfNetworkRequired(false);
const x = await walletInstance.provider.getNetwork();
// connectedWallet.provider.getNetwork

const { status, success } = await safeMint({
connectedWallet: walletInstance,
walletAddress: walletInstance.address,
sendTo: walletInstance.address,
abi: nft.contracts[chain].abi,
contract: nft.contracts[chain].contract,
chain: chain,
name: collectionItem.name,
imageURI: collectionItem.ipfs,
description: collectionItem.description,
tokenUri: collectionItem.metaUrl
});
if ( success ) setMintedNFTs( [ ...mintedNFTs, collectionItem.id ] );
if( status ) setSubmissionStatus( status );
try {
// Try automatically switching to Goerli
await switchChain(5);

if ((await walletInstance.provider.getNetwork()).chainId !== 5)
throw new Error("Chain switch failed");

curChain = "Goerli";

} catch (e) {
console.log("!nft.contracts[ chain ]", chainId);
console.log("!nft.contracts[ chain ]", curChain, chainId);

chainId
? setPopUpMsg(`Please change your wallet's network to either Goerli or Mumbai.`)
: setPopUpMsg(`Please connect your wallet to continue.`)

setChangeOfNetworkRequired(true);
return;
}
}

setChangeOfNetworkRequired(false);

// connectedWallet.provider.getNetwork

const { status, success } = await safeMint({
connectedWallet: walletInstance,
walletAddress: walletInstance.address,
sendTo: walletInstance.address,
abi: nft.contracts[curChain].abi,
contract: nft.contracts[curChain].contract,
chain: curChain,
name: collectionItem.name,
imageURI: collectionItem.ipfs,
description: collectionItem.description,
tokenUri: collectionItem.metaUrl
});
if ( success ) setMintedNFTs( [ ...mintedNFTs, collectionItem.id ] );
if( status ) setSubmissionStatus( status );
};

return (
Expand All @@ -92,14 +109,11 @@ export default function Minter({ className }) {

let current = i;

let tokenIsSelected = false;
if (typeof ethereum !== "undefined") {
const chain = ethereum.chainId === '0x13881' ? '-mumbai' : '-goerli';
tokenIsSelected = (
selectedTokens && selectedTokens[collectionItem.ref + chain] &&
selectedTokens[collectionItem.ref+chain].tokens.length > 0
);
}
const chain = chainId === 80001 ? '-mumbai' : '-goerli';
const tokenIsSelected = (
selectedTokens && selectedTokens[collectionItem.ref + chain] &&
selectedTokens[collectionItem.ref+chain].tokens.length > 0
);

let isMinted = mintedNFTs.indexOf( collectionItem.id ) > -1 || tokenIsSelected;

Expand All @@ -113,6 +127,11 @@ export default function Minter({ className }) {
onClick={ async event => {
if (mintButonState[current]) return;

if (!walletInstance){
negotiator.ui.openOverlay();
return;
}

let state = [...mintButonState];
state[current] = true;
setMintButonState(state);
Expand All @@ -131,6 +150,10 @@ export default function Minter({ className }) {
</Card>
);
})}
{ walletInstance ?
<a style={{cursor: "pointer"}} onClick={async () => { try { await switchChain(chainId !== 5 ? 5 : 80001); } catch (e) {/* no-op */}}}>
Switch to {chainId !== 5 ? "Goerli" : "Mumbai"}
</a> : ''}
</div>
<PopUp closeEvent={onClosePopUpEvent} isOpen={changeOfNetworkRequired} msg={popUpMsg} />
</div>
Expand Down
33 changes: 20 additions & 13 deletions ecommerce-store-website/src/ui/views/home/home-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,31 @@ import styles from "./home-view.module.scss";

export default function HomeView(props) {

const { wallet } = useContext(TokenContext);
const { wallet, switchChain, chainId } = useContext(TokenContext);
const [showNetworkNotification, setShowNetworkNotification] = useState(false);

let showNetworkNotification = false;

const normalisedNetworkId = wallet ? chainMap[wallet.chainId] : undefined;

if(
wallet &&
normalisedNetworkId !== "Goerli" &&
normalisedNetworkId !== "Mumbai"
){
showNetworkNotification = true;
}
useEffect(() => {
const normalisedNetworkId = wallet ? chainMap[chainId] : undefined;

if(
wallet &&
normalisedNetworkId !== "Goerli" &&
normalisedNetworkId !== "Mumbai"
){
setShowNetworkNotification(true);
} else {
setShowNetworkNotification(false);
}
}, [wallet, chainId]);

return (
<Page className={ styles[ 'v-home' ] }>

<Toast isOpen={ showNetworkNotification } msg={ "Please connect to Goerli or Mumbai network" } />
<Toast isOpen={ showNetworkNotification }
msg={ <span>Click to connect to&nbsp;
<a style={{cursor: "pointer", color: "#001AFF"}} onClick={() => switchChain(5)}>Goerli</a>&nbsp;
or <a style={{cursor: "pointer", color: "#001AFF"}} onClick={() => switchChain(80001)}>Mumbai</a> network</span> }
/>

<section className="section -ps">
<div className="grid -g-cols-1">
Expand Down
5 changes: 5 additions & 0 deletions vue-boilerplate/vue.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ const NodePolyfillPlugin = require("node-polyfill-webpack-plugin")
module.exports = defineConfig({
transpileDependencies: true,
configureWebpack: {
resolve: {
alias: {
process: "process/browser"
}
},
plugins: [
new NodePolyfillPlugin()
]
Expand Down
3 changes: 2 additions & 1 deletion vue-vite-boilerplate/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ export default defineConfig({
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
'@': fileURLToPath(new URL('./src', import.meta.url)),
process: "process/browser",
}
},
optimizeDeps: {
Expand Down

0 comments on commit fc2b9d5

Please sign in to comment.