Last active
July 21, 2023 10:30
-
-
Save andsilver/ea2b5bed69cd36b094a9085eb4633940 to your computer and use it in GitHub Desktop.
Code Examples
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
: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) | |
} | |
} |
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
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: "[email protected]" | |
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 { "[email protected]" } | |
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. |
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
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. | |
*/ |
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
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); |
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
/* 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