mirror of
https://github.com/FlipsideCrypto/badger.git
synced 2026-02-06 10:57:46 +00:00
fix: chain localization
* sleep listeners until processes spin up * makes use of providers DRY * refactor primary chain to chain id * resolves #116 & #134 * integrates with #189 * implements frontend contract architecture for #181
This commit is contained in:
parent
06d805ef66
commit
ee5f381bb9
@ -1,4 +1,5 @@
|
||||
import os
|
||||
import json
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from pathlib import Path
|
||||
@ -147,35 +148,22 @@ REST_FRAMEWORK = {
|
||||
CORS_ALLOW_CREDENTIALS = True
|
||||
CORS_ALLOW_ALL_ORIGINS = True
|
||||
|
||||
# Web3 settings
|
||||
# Provider settings
|
||||
NODE_IP = os.getenv("NODE_IP", "0.0.0.0")
|
||||
|
||||
ALCHEMY_API_KEY = os.getenv("REACT_APP_ALCHEMY_API_KEY")
|
||||
|
||||
DEFAULT_NETWORK = os.getenv("REACT_APP_DEFAULT_NETWORK", "LOCAL")
|
||||
CHAIN_ID = int(os.getenv("REACT_APP_CHAIN_ID", 1337))
|
||||
PROVIDERS = {
|
||||
'ETHEREUM': os.getenv("PROVIDER", f"https://eth-mainnet.g.alchemy.com/v2/{ALCHEMY_API_KEY}"),
|
||||
'POLYGON': os.getenv("POLYGON_PROVIDER", f"https://polygon-mainnet.g.alchemy.com/v2/{ALCHEMY_API_KEY}"),
|
||||
'LOCAL': os.getenv("LOCAL_PROVIDER", f"http://{NODE_IP}:8545/"),
|
||||
1: os.getenv("PROVIDER", f"https://eth-mainnet.g.alchemy.com/v2/{ALCHEMY_API_KEY}"),
|
||||
137: os.getenv("POLYGON_PROVIDER", f"https://polygon-mainnet.g.alchemy.com/v2/{ALCHEMY_API_KEY}"),
|
||||
1337: os.getenv("LOCAL_PROVIDER", f"http://{NODE_IP}:8545/"),
|
||||
}
|
||||
PROVIDERS['DEFAULT'] = os.getenv("PROVIDER", PROVIDERS[DEFAULT_NETWORK])
|
||||
PROVIDER = PROVIDERS['ETHEREUM']
|
||||
|
||||
FACTORY_ADDRESSES = {
|
||||
"ETHEREUM": "0x0",
|
||||
"POLYGON": "0x72b03C649953CA95B920f60A5687e4d2DACf45c0",
|
||||
"LOCAL": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512"
|
||||
}
|
||||
FACTORY_ADDRESS = os.getenv("FACTORY_ADDRESS", FACTORY_ADDRESSES[DEFAULT_NETWORK])
|
||||
CHAIN_PROVIDER = PROVIDERS[CHAIN_ID]
|
||||
PROVIDER = PROVIDERS[1]
|
||||
|
||||
# Blockchain based authentication
|
||||
AUTHENTICATION_BACKENDS = ["siwe_auth.backend.SiweBackend"]
|
||||
|
||||
PINATA_API_KEY = os.getenv("API_PINATA_API_KEY")
|
||||
PINATA_API_SECRET_KEY = os.getenv("API_PINATA_API_SECRET_KEY")
|
||||
PINATA_INDEXER_URL = os.getenv(
|
||||
"API_PINATA_INDEXER_URL", "https://badger.mypinata.cloud/ipfs/"
|
||||
)
|
||||
|
||||
# Onchain schema settings
|
||||
FACTORY_ABI = FACTORY_ABI
|
||||
FACTORY_ABI_FULL = FACTORY_ABI_FULL
|
||||
FACTORY_EVENTS = FACTORY_EVENTS
|
||||
@ -186,7 +174,28 @@ ORGANIZATION_ABI_FULL = ORGANIZATION_ABI_FULL
|
||||
ORGANIZATION_EVENTS = ORGANIZATION_EVENTS
|
||||
ORGANIZATION_TOPIC_SIGNATURES = ORGANIZATION_TOPIC_SIGNATURES
|
||||
|
||||
# Listener settings
|
||||
LISTENER_INIT_BLOCK = os.getenv("LISTENER_INIT_BLOCK", 0)
|
||||
LISTENER_INIT_BLOCK_BUFFER = os.getenv("LISTENER_INIT_BLOCK_BUFFER", 20)
|
||||
LISTENER_CHAIN_ID = os.getenv("LISTENER_CHAIN_ID", 1337)
|
||||
LISTENER_POLL_INTERVAL = os.getenv("POLL_INTERVAL", 5)
|
||||
LISTENER_POLL_INTERVAL = os.getenv("LISTENER_POLL_INTERVAL", 5)
|
||||
|
||||
# Onchain reference data
|
||||
BADGER_ADDRESSES = os.getenv("REACT_APP_BADGER_ADDRESSES", None)
|
||||
if BADGER_ADDRESSES is None:
|
||||
raise Exception("BADGER_ADDRESSES not set")
|
||||
|
||||
BADGER_ADDRESSES = {int(k): v for k, v in json.loads(BADGER_ADDRESSES).items()}
|
||||
|
||||
if CHAIN_ID not in BADGER_ADDRESSES:
|
||||
raise Exception(f"Chain ID {CHAIN_ID} not found in BADGER_ADDRESSES")
|
||||
|
||||
FACTORY_ADDRESS = BADGER_ADDRESSES[CHAIN_ID]
|
||||
|
||||
# IPFS settings
|
||||
PINATA_API_KEY = os.getenv("API_PINATA_API_KEY")
|
||||
PINATA_API_SECRET_KEY = os.getenv("API_PINATA_API_SECRET_KEY")
|
||||
PINATA_INDEXER_URL = os.getenv(
|
||||
"API_PINATA_INDEXER_URL", "https://badger.mypinata.cloud/ipfs/"
|
||||
)
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ from django.conf import settings
|
||||
from web3 import Web3
|
||||
from ens import ENS
|
||||
|
||||
w3 = Web3(Web3.HTTPProvider(settings.PROVIDERS['DEFAULT']))
|
||||
w3 = Web3(Web3.HTTPProvider(settings.CHAIN_PROVIDER))
|
||||
ns = ENS.fromWeb3(w3)
|
||||
|
||||
def get_ens_name(address):
|
||||
|
||||
@ -65,9 +65,10 @@ services:
|
||||
- badger_redis
|
||||
command: >
|
||||
sh -c "
|
||||
sleep 10 &&
|
||||
python manage.py migrate &&
|
||||
python manage.py runserver
|
||||
"
|
||||
python manage.py runserver 0.0.0.0:8000
|
||||
"
|
||||
# Run the onchain listener for Badger Factories in a separate worker
|
||||
# that is loading data into the same database as the API.
|
||||
badger_factory_listener:
|
||||
@ -89,7 +90,11 @@ services:
|
||||
links:
|
||||
- badger_node:badger_node
|
||||
- badger_db:badger_db
|
||||
command: python manage.py listen_for_factories
|
||||
command: >
|
||||
sh -c "
|
||||
sleep 25 &&
|
||||
python manage.py listen_for_factories
|
||||
"
|
||||
# Run the onchain listener for Badger Organizations in a separate worker
|
||||
# that is loading data into the same database as the API.
|
||||
badger_organization_listener:
|
||||
@ -111,7 +116,11 @@ services:
|
||||
links:
|
||||
- badger_node:badger_node
|
||||
- badger_db:badger_db
|
||||
command: python manage.py listen_for_organizations
|
||||
command: >
|
||||
sh -c "
|
||||
sleep 25 &&
|
||||
python manage.py listen_for_organizations
|
||||
"
|
||||
# Run the React frontend for Badger. This will run the frontend
|
||||
# on port 3000 and will be accessible at http://localhost:3000
|
||||
# while consuming the state of all services apriori.
|
||||
|
||||
14
example.env
14
example.env
@ -15,12 +15,12 @@ API_PINATA_API_SECRET_KEY=""
|
||||
# Must provide this to declare which chain the dapp will run on when running.
|
||||
# By using this variable to control the default network, the backend will automatically
|
||||
# configure the correct network for the frontend.
|
||||
# - ETHEREUM
|
||||
# - POLYGON
|
||||
# - LOCAL
|
||||
# - Ethereum: 1
|
||||
# - Polygon: 137
|
||||
# - Local: 1337
|
||||
# ✅ WE PROVIDE A DEFAULT, BUT LOCAL SETTING. ✅
|
||||
REACT_APP_DEFAULT_NETWORK = "LOCAL"
|
||||
|
||||
REACT_APP_CHAIN_ID=1337
|
||||
|
||||
# =======================================================================================
|
||||
# Must provide this in order to run the needed blockchain calls.
|
||||
# This is not just needed locally, the Badger system subsidizes the use of an RPC in preference
|
||||
@ -52,9 +52,7 @@ PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
|
||||
# the hardhat deployer on localhost which means you will not need to change anything. If you are deploying
|
||||
# to a different network, you will need to change this.
|
||||
# ✅ WE PROVIDE A DEFAULT, BUT A RATE LIMITED ONE. ✅
|
||||
REACT_APP_BADGER_SINGLETON="0x5FbDB2315678afecb367f032d93F642f64180aa3"
|
||||
REACT_APP_PRODUCTION_CHAIN="Localhost"
|
||||
REACT_APP_BADGER_ADDRESSES={"Localhost":"0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512"}
|
||||
REACT_APP_BADGER_ADDRESSES={"137":"0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512","1337":"0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512"}
|
||||
|
||||
# =======================================================================================
|
||||
# Must provide an API key to CoinMarketCap to see the cost of the transactions when running Hardhat tests.
|
||||
|
||||
@ -4,8 +4,6 @@ import { useConnectModal } from "@rainbow-me/rainbowkit";
|
||||
|
||||
import { useAuthentication, useAuthenticationModal } from "@hooks";
|
||||
|
||||
const PRIMARY_PRODUCTION_CHAIN = process.env.REACT_APP_PRODUCTION_CHAIN;
|
||||
|
||||
const ConnectButton = () => {
|
||||
const { switchNetwork } = useSwitchNetwork();
|
||||
|
||||
@ -13,8 +11,7 @@ const ConnectButton = () => {
|
||||
|
||||
const { openConnectModal } = useConnectModal();
|
||||
|
||||
const { primaryChain, isAuthenticating, isWrongNetwork } =
|
||||
useAuthentication();
|
||||
const { primaryChain, isAuthenticating, isWrongNetwork } = useAuthentication();
|
||||
|
||||
const { openAuthenticationModal } = useAuthenticationModal();
|
||||
|
||||
@ -31,7 +28,7 @@ const ConnectButton = () => {
|
||||
disabled={!switchNetwork}
|
||||
onClick={switchNetwork.bind(null, primaryChain.id)}
|
||||
>
|
||||
Switch to {PRIMARY_PRODUCTION_CHAIN}
|
||||
Switch to {primaryChain.name}
|
||||
</button>
|
||||
);
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { createContext, useEffect, useState } from "react";
|
||||
import { useAccount, useNetwork, useSwitchNetwork } from "wagmi";
|
||||
|
||||
const PRIMARY_PRODUCTION_CHAIN = process.env.REACT_APP_PRODUCTION_CHAIN
|
||||
const CHAIN_ID = process.env.REACT_APP_CHAIN_ID
|
||||
|
||||
const AuthenticationContext = createContext();
|
||||
|
||||
@ -18,16 +18,17 @@ const AuthenticationContextProvider = ({ children }) => {
|
||||
const [authenticatedAddress, setAuthenticatedAddress] = useState(getAuthenticatedAddress());
|
||||
const [isAuthenticating, setIsAuthenticating] = useState(false);
|
||||
|
||||
const primaryChain = chains.find(c => c.name === PRIMARY_PRODUCTION_CHAIN)
|
||||
const primaryChain = chains.find(c => c.id === CHAIN_ID);
|
||||
|
||||
const isWrongNetwork = isConnected && chain && primaryChain && chains && chain.id !== primaryChain.id;
|
||||
|
||||
const isAuthenticated = isConnected && !isWrongNetwork && address === authenticatedAddress;
|
||||
|
||||
const isReadyToSwitch = !isError && switchNetwork && isWrongNetwork;
|
||||
|
||||
useEffect(() => {
|
||||
/// Using isError here allows us to not prompt another switchNetwork if the user has already rejected the switch.
|
||||
if (isWrongNetwork && switchNetwork && !isError) switchNetwork(primaryChain.id)
|
||||
}, [isWrongNetwork, switchNetwork, isError, primaryChain]);
|
||||
if (isReadyToSwitch) switchNetwork(primaryChain.id)
|
||||
}, [isReadyToSwitch, primaryChain]);
|
||||
|
||||
return (
|
||||
<AuthenticationContext.Provider value={{
|
||||
|
||||
@ -1,41 +1,30 @@
|
||||
import { ethers } from "ethers";
|
||||
|
||||
const PRIMARY_IMPLEMENTATION = process.env.REACT_APP_BADGER_SINGLETON;
|
||||
const PRIMARY_PRODUCTION_CHAIN = process.env.REACT_APP_PRODUCTION_CHAIN;
|
||||
const BADGER_ADDRESSES_DICT = JSON.parse(process.env.REACT_APP_BADGER_ADDRESSES);
|
||||
const BADGER_ADDRESSES = Object.keys(BADGER_ADDRESSES_DICT).reduce((acc, key) => {
|
||||
acc[key] = BADGER_ADDRESSES_DICT[key][0];
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// Gets the Badger implementation to clone based on the version.
|
||||
// TODO: Add versioning
|
||||
function getPrimaryImplementation() {
|
||||
return PRIMARY_IMPLEMENTATION;
|
||||
}
|
||||
|
||||
// Putting the parse into a try catch block to account for missing env var breaking the app.
|
||||
function getBadgerAddress(chainName) {
|
||||
function getBadgerAddress(chainID) {
|
||||
try {
|
||||
const BADGER_ADDRESSES = JSON.parse(process.env.REACT_APP_BADGER_ADDRESSES);
|
||||
const address = BADGER_ADDRESSES[chainName] ? BADGER_ADDRESSES[chainName] : BADGER_ADDRESSES[PRIMARY_PRODUCTION_CHAIN];
|
||||
return address;
|
||||
}
|
||||
catch {
|
||||
return BADGER_ADDRESSES[chainID];
|
||||
} catch {
|
||||
console.error(`Badger contract address not found in .env.`)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Gets the ABI for sash contracts.
|
||||
// TODO: Add versioning
|
||||
function getBadgerOrganizationAbi() {
|
||||
try {
|
||||
const abi = require('@abis/BadgerOrganization.json');
|
||||
return { abi: new ethers.utils.Interface(abi) }
|
||||
}
|
||||
catch (err) {
|
||||
} catch (err) {
|
||||
console.error('Error importing BadgerOrganization:', err);
|
||||
return { error: err }
|
||||
}
|
||||
}
|
||||
|
||||
// Gets the abi and chain specific address for the Badger contract.
|
||||
function getBadgerAbi(chainName) {
|
||||
try {
|
||||
const abi = require('@abis/Badger.json');
|
||||
@ -44,15 +33,13 @@ function getBadgerAbi(chainName) {
|
||||
abi: new ethers.utils.Interface(abi),
|
||||
address: address
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
} catch (err) {
|
||||
console.error('Error importing Badger:', err);
|
||||
return { error: err }
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
getPrimaryImplementation,
|
||||
getBadgerAddress,
|
||||
getBadgerOrganizationAbi,
|
||||
getBadgerAbi
|
||||
|
||||
@ -1,4 +1,14 @@
|
||||
export { getPrimaryImplementation, getBadgerAddress, getBadgerOrganizationAbi, getBadgerAbi } from './contractVersions';
|
||||
export {
|
||||
useCreateOrg, useEditOrg, useOrgForm, useSetBadge, useManageBadgeOwnership, useSetDelegates, useTransferOwnership, useRenounceOwnership
|
||||
getBadgerAddress,
|
||||
getBadgerOrganizationAbi,
|
||||
getBadgerAbi
|
||||
} from './contractVersions';
|
||||
|
||||
export {
|
||||
useOrgForm,
|
||||
useSetBadge,
|
||||
useManageBadgeOwnership,
|
||||
useSetDelegates,
|
||||
useTransferOwnership,
|
||||
useRenounceOwnership
|
||||
} from './useContracts';
|
||||
@ -1,11 +1,9 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { ethers } from "ethers";
|
||||
import { usePrepareContractWrite, useContractWrite } from "wagmi"
|
||||
|
||||
import {
|
||||
getPrimaryImplementation,
|
||||
getBadgerOrganizationAbi,
|
||||
getBadgerAbi,
|
||||
useFees,
|
||||
@ -15,24 +13,8 @@ import {
|
||||
useUser
|
||||
} from "@hooks";
|
||||
|
||||
import { postOrgRequest } from "@utils";
|
||||
|
||||
import { IPFS_GATEWAY_URL } from "@static";
|
||||
|
||||
// TODO: Refactor the image field to be stored as a hash on the obj
|
||||
// TODO: Do the same for the contract hash
|
||||
|
||||
// IPFS has been the major pain point of this project so let's fix that.
|
||||
// on success of transaction, should pin the image through the calling of a hook
|
||||
|
||||
// sometimes though, we will know the hash when calling the transaction without using an image so having to pass the image in the hooks is wrong and was has been causing a lot of issues
|
||||
// Basically the only thing that changes between all the code in this file is the args, the function name and whether or not it is using ipfs or not
|
||||
// I see no reason we couldn't easily have a useOrg and useBadge hook that takes in the args and function name and then does the rest of the work
|
||||
|
||||
// The real architecture I want is useCreateOrg, useEditOrg and then inside each of those have logic for openOrgTx
|
||||
// The initial hooks were written in a way that required a lot of logic to be above the component any time it was used
|
||||
// This is a step in the right direction, but I want to get to a point where the hooks are the only thing that needs to be imported
|
||||
|
||||
const getOrgFormTxArgs = ({ functionName, authenticatedAddress, name, symbol, imageHash, contractHash }) => {
|
||||
if (functionName === "setOrganizationURI") {
|
||||
return [IPFS_GATEWAY_URL + contractHash]
|
||||
@ -49,68 +31,6 @@ const getOrgFormTxArgs = ({ functionName, authenticatedAddress, name, symbol, im
|
||||
}
|
||||
}
|
||||
|
||||
const useOrg = ({ obj, functionName }) => {
|
||||
const fees = useFees();
|
||||
|
||||
const { authenticatedAddress, chain } = useUser();
|
||||
|
||||
const Badger = useMemo(() => {
|
||||
if (obj.ethereum_address) return getBadgerOrganizationAbi();
|
||||
|
||||
return getBadgerAbi(chain.name);
|
||||
}, [functionName, chain.name]);
|
||||
|
||||
const isReady = Badger && fees && authenticatedAddress;
|
||||
|
||||
const overrides = { gasPrice: fees?.gasPrice };
|
||||
|
||||
// TODO: Args
|
||||
const args = []
|
||||
|
||||
const { config, isSuccess: isPrepared } = usePrepareContractWrite({
|
||||
enabled: isReady,
|
||||
addressOrName: obj.ethereum_address || Badger.address,
|
||||
contractInterface: Badger.abi,
|
||||
functionName,
|
||||
args,
|
||||
overrides,
|
||||
onError: (e) => {
|
||||
const err = e?.error?.message || e?.data?.message || e
|
||||
|
||||
throw new Error(err);
|
||||
}
|
||||
})
|
||||
|
||||
const { writeAsync } = useContractWrite(config);
|
||||
|
||||
// We are separating the openOrg logic because every function called through this needs to have a post request following
|
||||
const openOrgTx = async ({
|
||||
onError = (e) => { console.error(e) },
|
||||
onLoading = () => { },
|
||||
onSuccess = ({ tx, org, response }) => { }
|
||||
}) => {
|
||||
try {
|
||||
onLoading()
|
||||
|
||||
const tx = await writeAsync()
|
||||
|
||||
const txReceipt = await tx.wait()
|
||||
|
||||
if (txReceipt.status === 0) throw new Error("Error submitting transaction.");
|
||||
|
||||
const response = await postOrgRequest(obj)
|
||||
|
||||
if (!response.ok) throw new Error("Error submitting Organization request.")
|
||||
|
||||
onSuccess({ tx, obj, response })
|
||||
} catch (e) {
|
||||
onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
return { openOrgTx }
|
||||
}
|
||||
|
||||
const useOrgForm = ({ obj, image }) => {
|
||||
const fees = useFees();
|
||||
|
||||
@ -208,176 +128,6 @@ const useOrgForm = ({ obj, image }) => {
|
||||
return { openOrgFormTx, isPrepared, isLoading, isSuccess };
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Hook to trigger and handle a transaction that calls `createOrganization` on the Badger contract.
|
||||
*/
|
||||
const useCreateOrg = ({ enabled, name, symbol, imageHash, contractHash }) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const fees = useFees();
|
||||
|
||||
const { authenticatedAddress, chain } = useUser();
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isSuccess, setIsSuccess] = useState(false);
|
||||
|
||||
const Badger = useMemo(() => getBadgerAbi(chain.name), [chain.name]);
|
||||
|
||||
const orgCreatedTopic = Badger.abi.getEventTopic("OrganizationCreated");
|
||||
|
||||
const isReady = Boolean(enabled && fees && Badger);
|
||||
|
||||
const args = getOrgFormTxArgs({ functionName: "createOrganization", authenticatedAddress, name, symbol, imageHash, contractHash });
|
||||
|
||||
const { config, isSuccess: isPrepared } = usePrepareContractWrite({
|
||||
addressOrName: Badger.address,
|
||||
contractInterface: Badger.abi,
|
||||
functionName: "createOrganization",
|
||||
args: args,
|
||||
enabled: isReady,
|
||||
overrides: { gasPrice: fees?.gasPrice },
|
||||
onError(e) {
|
||||
const err = e?.error?.message || e?.data?.message || e
|
||||
|
||||
throw new Error(err);
|
||||
}
|
||||
})
|
||||
|
||||
const { writeAsync } = useContractWrite(config);
|
||||
|
||||
const openCreateOrgTx = async ({
|
||||
onError = (e) => { console.error(e) },
|
||||
onLoading = () => { },
|
||||
onSuccess = ({ tx, org, response }) => { }
|
||||
}) => {
|
||||
try {
|
||||
setIsLoading(true) && onLoading();
|
||||
|
||||
const tx = await writeAsync();
|
||||
|
||||
const [txReceipt, imageHash, metadataHash] = await Promise.all([
|
||||
tx.wait(),
|
||||
null,
|
||||
null
|
||||
// pinImage(objImage),
|
||||
// pinMetadata(objMetadata),
|
||||
]);
|
||||
|
||||
if (txReceipt.status === 0) throw new Error("Error submitting Organization transaction.");
|
||||
|
||||
const orgCreatedEvent = txReceipt.logs.find((log) => log.topics[0] === orgCreatedTopic);
|
||||
const orgEvent = Badger.abi.decodeEventLog("OrganizationCreated", orgCreatedEvent.data, orgCreatedEvent.topics);
|
||||
const contractAddress = orgEvent.organization;
|
||||
|
||||
if (!contractAddress) throw new Error("Could not find event emission from Organization creation.");
|
||||
|
||||
const org = {
|
||||
...org,
|
||||
ethereum_address: contractAddress,
|
||||
contract_uri_hash: metadataHash,
|
||||
image_hash: imageHash,
|
||||
chain: chain.name,
|
||||
owner: authenticatedAddress, // don't do it like this
|
||||
is_active: true
|
||||
}
|
||||
|
||||
const response = await postOrgRequest(org);
|
||||
|
||||
if (!response.ok) throw new Error("Error creating Organization in database.");
|
||||
|
||||
navigate(`/dashboard/organization/${response.id}/`);
|
||||
|
||||
setIsSuccess(true) && onSuccess({ tx, org, response });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
setIsSuccess(false) && onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
return { openCreateOrgTx, isPrepared, isLoading, isSuccess };
|
||||
}
|
||||
|
||||
// Edit the contract URI of an organization and update the image, description, and name.
|
||||
const useEditOrg = ({ enabled, contractAddress, contractUriHash }) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const fees = useFees();
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isSuccess, setIsSuccess] = useState(false);
|
||||
|
||||
const BadgerOrganization = useMemo(() => getBadgerOrganizationAbi(), []);
|
||||
|
||||
const isReady = Boolean(enabled && fees && BadgerOrganization);
|
||||
|
||||
const args = [
|
||||
IPFS_GATEWAY_URL + contractUriHash
|
||||
]
|
||||
|
||||
const { config, isSuccess: isPrepared } = usePrepareContractWrite({
|
||||
addressOrName: contractAddress,
|
||||
contractInterface: BadgerOrganization.abi,
|
||||
functionName: "setOrganizationURI",
|
||||
args: args,
|
||||
enabled: isReady,
|
||||
overrides: { gasPrice: fees?.gasPrice },
|
||||
onError(e) {
|
||||
const err = e?.error?.message || e?.data?.message || e
|
||||
|
||||
throw new Error(err);
|
||||
}
|
||||
})
|
||||
|
||||
const { writeAsync } = useContractWrite(config);
|
||||
|
||||
const openCreateOrgTx = async ({
|
||||
onError = (e) => { console.error(e) },
|
||||
onLoading = () => { },
|
||||
onSuccess = ({ tx, org, response }) => { }
|
||||
}) => {
|
||||
try {
|
||||
setIsLoading(true) && onLoading();
|
||||
|
||||
const tx = await writeAsync();
|
||||
|
||||
const [txReceipt, imageHash, metadataHash] = await Promise.all([
|
||||
tx.wait(),
|
||||
null,
|
||||
null
|
||||
// pinImage(objImage),
|
||||
// pinMetadata({
|
||||
// name: obj.name,
|
||||
// description: obj.description,
|
||||
// imageHash: objImage,
|
||||
// })
|
||||
])
|
||||
|
||||
if (txReceipt.status === 0) throw new Error("Error submitting Organization transaction.");
|
||||
|
||||
const org = {
|
||||
...org,
|
||||
contract_uri_hash: metadataHash,
|
||||
image_hash: imageHash,
|
||||
}
|
||||
|
||||
const response = await postOrgRequest(org);
|
||||
|
||||
if (!response.ok) throw new Error("Error creating Organization in database.");
|
||||
|
||||
navigate(`/dashboard/organization/${response.id}/`);
|
||||
|
||||
setIsSuccess(true) && onSuccess({ tx, org, response });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
setIsSuccess(false) && onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
return { openCreateOrgTx, isPrepared, isLoading, isSuccess };
|
||||
}
|
||||
|
||||
// Creates a badge from a cloned sash contract.
|
||||
const useSetBadge = (isTxReady, contractAddress, tokenUri, badge) => {
|
||||
const BadgerOrganization = useMemo(() => getBadgerOrganizationAbi(), []);
|
||||
@ -594,8 +344,6 @@ const useRenounceOwnership = (isTxReady, orgAddress) => {
|
||||
}
|
||||
|
||||
export {
|
||||
useCreateOrg,
|
||||
useEditOrg,
|
||||
useOrgForm,
|
||||
useSetBadge,
|
||||
useManageBadgeOwnership,
|
||||
|
||||
@ -1,15 +1,12 @@
|
||||
export {
|
||||
getPrimaryImplementation,
|
||||
getBadgerAddress,
|
||||
getBadgerOrganizationAbi,
|
||||
getBadgerAbi,
|
||||
useCreateOrg,
|
||||
useEditOrg,
|
||||
useSetBadge,
|
||||
useOrgForm,
|
||||
useManageBadgeOwnership,
|
||||
useSetDelegates,
|
||||
useTransferOwnership,
|
||||
getBadgerAddress,
|
||||
getBadgerOrganizationAbi,
|
||||
getBadgerAbi,
|
||||
useSetBadge,
|
||||
useOrgForm,
|
||||
useManageBadgeOwnership,
|
||||
useSetDelegates,
|
||||
useTransferOwnership,
|
||||
useRenounceOwnership
|
||||
} from './contracts';
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user