Skip to content

Instantly share code, notes, and snippets.

@andsilver
Last active July 21, 2023 10:30
Show Gist options
  • Save andsilver/ea2b5bed69cd36b094a9085eb4633940 to your computer and use it in GitHub Desktop.
Save andsilver/ea2b5bed69cd36b094a9085eb4633940 to your computer and use it in GitHub Desktop.
Code Examples
:root {
--primary-color: #123456;
--secondary-color: #654321;
}
.component {
color: var(--primary-color);
background-color: var(--secondary-color);
}
@media screen and (max-width: 960px) {
.component {
font-size: 0.8rem;
}
}
.component-overlay-enter {
animation: component-overlay-enter-animation 150ms forwards
}
@keyframes component-overlay-enter-animation {
from {
background-color: transparent
}
to {
background-color: var(--maskbg)
}
}
require 'rails_helper'
RSpec.feature "User Registration", type: :feature do
scenario "User can register with valid information" do
visit new_user_registration_path
fill_in "Email", with: "test@example.com"
fill_in "Password", with: "password"
fill_in "Confirm Password", with: "password"
click_button "Sign up"
expect(page).to have_content("Welcome! You have signed up successfully.")
end
scenario "User cannot register with invalid information" do
visit new_user_registration_path
fill_in "Email", with: "invalid_email"
fill_in "Password", with: "password"
fill_in "Confirm Password", with: "password"
click_button "Sign up"
expect(page).to have_content("Email is invalid")
end
end
FactoryBot.define do
factory :user do
email { "test@example.com" }
password { "password" }
end
end
# The code ensures usability by simulating user interactions and ensuring the expected behavior and reliability by mocking external dependencies and controlling their behavior.
export default function App() {
const posts = [
{
id: 1,
title: "Post 1"
},
{
id: 2,
title: "Post 2"
}
];
return (
<main>
<div>
<h1>Nav Bar</h1>
</div>
<ul>
{posts.map(post => (
<li key={post.id}>
{post.title}
</li>
))}
</ul>
</main>
);
}
// The above code is an App component that renders a Navbar and Posts on a page.
// To make it cleaner:
const Post = (post) => export default function App() {
return (
<main>
<Navbar title="Nav Bar" />
<FeaturedPosts />
</main>
);
}
function Navbar({ title }) {
return (
<div>
<h1>{title}</h1>
</div>
);
}
function Post({ post }) {
return <li key={post.id}>{post.title}</li>;
}
function FeaturedPosts() {
const posts = [
{
id: 1,
title: "How to Build YouTube with React",
},
{
id: 2,
title: "How to Write Your First React Hook",
},
];
return (
<ul>
{posts.map((post) => (
<Post key={post.id} post={post} />
))}
</ul>
);
}
/*
Now it’s obvious that the App component is rendering a nav bar and some featured posts on the page.
Also, by doing this, the Post component can be reused in another component for displaying posts.
And every component is self-explanatory on their functionalities without a single comment
It’s better to keep each component in separate files as well.
*/
import { createContext, useContext, useEffect, useState } from "react";
import Web3Modal from "web3modal";
import WalletConnectProvider from "@walletconnect/web3-provider";
import Web3 from "web3";
import { ethers } from "ethers";
import { config } from "../config";
import { getAuthToken, getMe, getNonce } from "../api/api";
import { getWeb3 } from "../helper/utils";
export const AuthContext = createContext({
address: null,
connect: () => null,
loading: false,
disconnect: () => null,
chainId: null,
isAdmin: false,
provider: null,
user: null,
balance: 0,
});
const providerOptions = {
walletconnect: {
package: WalletConnectProvider, // required
options: {
infuraId: "2277777c3280436f956e6a3bc011bf6f",
rpc: {
[config.chainId]: config.chainRPC,
},
},
},
};
const web3Modal = new Web3Modal({
cacheProvider: true, // optional
providerOptions, // required
});
export const AuthProvider = ({ children }) => {
const [chainId, setChainId] = useState(null);
const [address, setAddress] = useState("");
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(false);
const [balance, setBalance] = useState(0);
const [provider, setProvider] = useState(null);
const subscribeProvider = (provider) => {
provider.on("disconnect", (error) => {
console.log("Disconnected:", error);
setChainId(null);
setAddress(null);
});
provider.on("accountsChanged", (accounts) => {
setAddress(accounts[0]);
});
// Subscribe to chainId change
provider.on("chainChanged", (chainId) => {
window.location.reload();
});
};
const getBalance = async () => {
if (!user) {
setBalance(0);
return;
}
const web3 = getWeb3();
const value = await web3.eth.getBalance(user.pubKey);
setBalance(parseFloat(Web3.utils.fromWei(value)).toFixed(4));
};
const login = async () => {
if (user) {
return;
}
try {
const provider = await web3Modal.connect();
const web3 = new Web3(provider);
subscribeProvider(provider);
const accounts = await web3.eth.getAccounts();
const chain = await web3.eth.getChainId();
setAddress(accounts[0]);
setChainId(chain);
setProvider(provider);
} catch (err) {
web3Modal.clearCachedProvider();
console.error(err);
}
};
const logout = () => {
setUser(null);
setAddress(null);
web3Modal.clearCachedProvider();
localStorage.removeItem("token");
};
const fetchUser = async () => {
setLoading(true);
try {
const res = await getMe();
setUser(res);
} catch (err) {
console.error(err);
logout();
}
setLoading(false);
};
useEffect(() => {
getBalance();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [user, provider]);
useEffect(() => {
const handleSignMessage = async (nonce) => {
try {
const p = new ethers.providers.Web3Provider(provider);
const signer = p.getSigner();
const domain = {
name: "Zunaverse",
version: "1",
};
return await signer._signTypedData(
domain,
{
Message: [
{
name: "nonce",
type: "uint256",
},
],
},
{
nonce,
}
);
} catch (err) {
console.error(err);
web3Modal.clearCachedProvider();
throw new Error("You need to sign the message to be able to log in.");
}
};
if (!address || !provider) {
setUser(null);
return;
}
const accessToken = localStorage.getItem("token");
if ((!user || user.address === address) && accessToken) {
fetchUser();
return;
}
setLoading(true);
getNonce(address)
.then(({ nonce }) => handleSignMessage(nonce))
.then((signature) => getAuthToken(address, signature))
.then(({ accessToken, user }) => {
localStorage.setItem("token", accessToken);
setUser(user);
setLoading(false);
})
.catch((err) => {
console.error(err);
setUser(null);
setLoading(false);
web3Modal.clearCachedProvider();
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [address, provider]);
useEffect(() => {
if (web3Modal.cachedProvider) {
login();
}
// eslint-disable-next-line
}, []);
return (
<AuthContext.Provider
value={{
address,
loading,
connect: login,
disconnect: logout,
chainId,
provider,
user,
balance,
fetchUser,
}}
>
{children}
</AuthContext.Provider>
);
};
export const useAuthContext = () => useContext(AuthContext);
/* eslint-disable react-hooks/exhaustive-deps */
import { createContext, useContext, useEffect, useMemo, useState } from "react";
import { useChainId, useProvider, useSigner, useSignTypedData } from "wagmi";
import { Contract } from "ethers";
import mediaABI from "../contracts/abis/Zuna.json";
import marketABI from "../contracts/abis/Market.json";
import erc20ABI from "../contracts/abis/erc20.json";
import market2ABI from "../contracts/abis/Market2.json";
import erc721ABI from "../contracts/abis/erc721.json";
import { config } from "../config";
import { useAuthContext } from "./AuthContext";
import { useSnackbar } from "./Snackbar";
import { fromWei } from "../helper/utils";
import { useConfirm } from "./Confirm";
import { useCurrency } from "./CurrencyContext";
export const Web3Context = createContext({
wrongNetwork: false,
getErc20Contract: (address) => undefined,
getErc721Contract: (address) => undefined,
signEIP712: async (types, data, contract) => undefined,
getErc20Balance: async (currency, address) => {},
serviceFee: 0,
approveMarket: async () => {},
approveNFT: async () => {},
marketContract: null,
mediaContract: null,
market2Contract: null,
});
export const Web3Provider = ({ children }) => {
const { user } = useAuthContext();
const [serviceFee, setServiceFee] = useState(0);
const { showSnackbar } = useSnackbar();
const confirm = useConfirm();
const { getCoinByAddress } = useCurrency();
const chainId = useChainId();
const { signTypedDataAsync } = useSignTypedData();
const { data: signer } = useSigner();
const provider = useProvider({
chainId: config.chainId,
});
const mediaContract = useMemo(
() => new Contract(config.nftContractAddress, mediaABI, signer || provider),
[signer, provider]
);
const marketContract = useMemo(
() =>
new Contract(config.marketContractAddress, marketABI, signer || provider),
[signer, provider]
);
const market2Contract = useMemo(
() =>
new Contract(
config.market2ContractAddress,
market2ABI,
signer || provider
),
[signer, provider]
);
const wrongNetwork = useMemo(() => {
if (!chainId) {
return true;
}
if (parseInt(chainId) !== config.chainId) {
return true;
}
return false;
}, [chainId]);
const approveMarket = async (erc20Address, marketAddress) => {
const erc20 = getErc20Contract(erc20Address);
const allowance = await erc20.allowance(
user.pubKey,
marketAddress || config.marketContractAddress
);
const balance = await erc20.balanceOf(user.pubKey);
const coin = getCoinByAddress(erc20Address);
if (!coin) {
throw new Error("Unspported coin");
}
const decimals = coin.decimals;
const amount = +fromWei(allowance.toString(), decimals);
const bAmount = +fromWei(balance.toString(), decimals);
if (amount > 100 || bAmount === 0) {
return;
}
await erc20.approve(
marketAddress || config.marketContractAddress,
""
);
};
const approveNFT = async (erc721Address, marketAddress) => {
const erc721Contract = getErc721Contract(erc721Address);
const marketplaceApproved = await erc721Contract.isApprovedForAll(
user.pubKey,
marketAddress || config.marketContractAddress
);
if (!marketplaceApproved) {
await confirm({
title: "APPROVE MARKETPLACE",
text: "One-time Approval for further transactions",
cancelText: "",
okText: "Approve",
});
await erc721Contract.setApprovalForAll(
marketAddress || config.marketContractAddress,
true
);
}
};
const getErc20Balance = async (currency, userAddress) => {
const coin = getCoinByAddress(currency);
if (!coin) {
return "";
}
const erc20 = getErc20Contract(currency);
const balance = await erc20.balanceOf(userAddress);
return fromWei(balance.toString(), coin.decimals);
};
const getServiceFee = async () => {
const value = await marketContract.fee();
setServiceFee(value / 1000);
};
const signEIP712 = async (types, data, contract) => {
if (!user || wrongNetwork) {
return;
}
const domain = {
...(contract === "market"
? config.sign.market
: contract === "market2"
? config.sign.market2
: config.sign.zuna),
};
return await signTypedDataAsync({
domain,
types,
value: data,
});
// return await signer._signTypedData(domain, types, data);
};
const showWrongNetworkWarning = () => {
showSnackbar({
severity: "warning",
message: `Please switch to ${config.networkName}`,
});
};
const getErc20Contract = (address) => {
return new Contract(address, erc20ABI, signer || provider);
};
const getErc721Contract = (address) => {
return new Contract(address, erc721ABI, signer || provider);
};
useEffect(() => {
if (!marketContract) {
return;
}
getServiceFee();
}, [marketContract]);
return (
<Web3Context.Provider
value={{
provider,
wrongNetwork,
getErc20Contract,
signEIP712,
showWrongNetworkWarning,
getErc20Balance,
getErc721Contract,
serviceFee,
approveMarket,
approveNFT,
mediaContract,
marketContract,
market2Contract,
}}
>
{children}
</Web3Context.Provider>
);
};
export const useWeb3 = () => useContext(Web3Context);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment