feat: UI fetch mint price on contract (#166)

* chore: update deployment

* feat: add fleekERC721 redux state and billing states

* feat: add billing price to mint flow
This commit is contained in:
Felipe Mendes 2023-03-13 14:07:37 -03:00 committed by GitHub
parent 1eed510527
commit c3fe09c1a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 1165 additions and 108 deletions

View File

@ -34,6 +34,11 @@
"address": "0x550Ee47Fa9E0B81c1b9C394FeE62Fe699a955519",
"txHash": "0x7076aaf31e50c5f9ddc4aeb1025c8b41e753ee99cc0d15ac5ac26395f04326e3",
"kind": "transparent"
},
{
"address": "0x37150709cFf366DeEaB836d05CAf49F4DA46Bb2E",
"txHash": "0x808546aa8bbc4e36c54d955970d8cfe8c4dc925eb5f65ff7b25203dd312bad4c",
"kind": "transparent"
}
],
"impls": {
@ -6988,6 +6993,458 @@
}
}
}
},
"eb08503319f5be721686b3aa1a477f4936b7bd581264aec1f5567149b8e15ff5": {
"address": "0xc1eEFa5035898B7D33572397A263D262b449886D",
"txHash": "0x61f5b9f485cf414e467dfd2077cbe785412639c51c7631f34628d5a43f8c371e",
"layout": {
"solcVersion": "0.8.12",
"storage": [
{
"label": "_initialized",
"offset": 0,
"slot": "0",
"type": "t_uint8",
"contract": "Initializable",
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62",
"retypedFrom": "bool"
},
{
"label": "_initializing",
"offset": 1,
"slot": "0",
"type": "t_bool",
"contract": "Initializable",
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67"
},
{
"label": "__gap",
"offset": 0,
"slot": "1",
"type": "t_array(t_uint256)50_storage",
"contract": "ContextUpgradeable",
"src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36"
},
{
"label": "__gap",
"offset": 0,
"slot": "51",
"type": "t_array(t_uint256)50_storage",
"contract": "ERC165Upgradeable",
"src": "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol:41"
},
{
"label": "_name",
"offset": 0,
"slot": "101",
"type": "t_string_storage",
"contract": "ERC721Upgradeable",
"src": "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol:25"
},
{
"label": "_symbol",
"offset": 0,
"slot": "102",
"type": "t_string_storage",
"contract": "ERC721Upgradeable",
"src": "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol:28"
},
{
"label": "_owners",
"offset": 0,
"slot": "103",
"type": "t_mapping(t_uint256,t_address)",
"contract": "ERC721Upgradeable",
"src": "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol:31"
},
{
"label": "_balances",
"offset": 0,
"slot": "104",
"type": "t_mapping(t_address,t_uint256)",
"contract": "ERC721Upgradeable",
"src": "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol:34"
},
{
"label": "_tokenApprovals",
"offset": 0,
"slot": "105",
"type": "t_mapping(t_uint256,t_address)",
"contract": "ERC721Upgradeable",
"src": "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol:37"
},
{
"label": "_operatorApprovals",
"offset": 0,
"slot": "106",
"type": "t_mapping(t_address,t_mapping(t_address,t_bool))",
"contract": "ERC721Upgradeable",
"src": "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol:40"
},
{
"label": "__gap",
"offset": 0,
"slot": "107",
"type": "t_array(t_uint256)44_storage",
"contract": "ERC721Upgradeable",
"src": "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol:514"
},
{
"label": "_collectionRolesCounter",
"offset": 0,
"slot": "151",
"type": "t_mapping(t_enum(CollectionRoles)3829,t_uint256)",
"contract": "FleekAccessControl",
"src": "contracts/FleekAccessControl.sol:57"
},
{
"label": "_collectionRoles",
"offset": 0,
"slot": "152",
"type": "t_mapping(t_enum(CollectionRoles)3829,t_mapping(t_address,t_bool))",
"contract": "FleekAccessControl",
"src": "contracts/FleekAccessControl.sol:62"
},
{
"label": "_tokenRolesVersion",
"offset": 0,
"slot": "153",
"type": "t_mapping(t_uint256,t_uint256)",
"contract": "FleekAccessControl",
"src": "contracts/FleekAccessControl.sol:69"
},
{
"label": "_tokenRoles",
"offset": 0,
"slot": "154",
"type": "t_mapping(t_uint256,t_mapping(t_uint256,t_mapping(t_enum(TokenRoles)3831,t_mapping(t_address,t_bool))))",
"contract": "FleekAccessControl",
"src": "contracts/FleekAccessControl.sol:74"
},
{
"label": "__gap",
"offset": 0,
"slot": "155",
"type": "t_array(t_uint256)49_storage",
"contract": "FleekAccessControl",
"src": "contracts/FleekAccessControl.sol:176"
},
{
"label": "_paused",
"offset": 0,
"slot": "204",
"type": "t_bool",
"contract": "FleekPausable",
"src": "contracts/FleekPausable.sol:23"
},
{
"label": "_canPause",
"offset": 1,
"slot": "204",
"type": "t_bool",
"contract": "FleekPausable",
"src": "contracts/FleekPausable.sol:24"
},
{
"label": "__gap",
"offset": 0,
"slot": "205",
"type": "t_array(t_uint256)49_storage",
"contract": "FleekPausable",
"src": "contracts/FleekPausable.sol:133"
},
{
"label": "_billings",
"offset": 0,
"slot": "254",
"type": "t_mapping(t_enum(Billing)4234,t_uint256)",
"contract": "FleekBilling",
"src": "contracts/FleekBilling.sol:31"
},
{
"label": "__gap",
"offset": 0,
"slot": "255",
"type": "t_array(t_uint256)49_storage",
"contract": "FleekBilling",
"src": "contracts/FleekBilling.sol:81"
},
{
"label": "_appIds",
"offset": 0,
"slot": "304",
"type": "t_uint256",
"contract": "FleekERC721",
"src": "contracts/FleekERC721.sol:119"
},
{
"label": "_apps",
"offset": 0,
"slot": "305",
"type": "t_mapping(t_uint256,t_struct(App)4585_storage)",
"contract": "FleekERC721",
"src": "contracts/FleekERC721.sol:120"
},
{
"label": "_accessPoints",
"offset": 0,
"slot": "306",
"type": "t_mapping(t_string_memory_ptr,t_struct(AccessPoint)4609_storage)",
"contract": "FleekERC721",
"src": "contracts/FleekERC721.sol:121"
}
],
"types": {
"t_address": {
"label": "address",
"numberOfBytes": "20"
},
"t_array(t_uint256)44_storage": {
"label": "uint256[44]",
"numberOfBytes": "1408"
},
"t_array(t_uint256)49_storage": {
"label": "uint256[49]",
"numberOfBytes": "1568"
},
"t_array(t_uint256)50_storage": {
"label": "uint256[50]",
"numberOfBytes": "1600"
},
"t_bool": {
"label": "bool",
"numberOfBytes": "1"
},
"t_enum(AccessPointCreationStatus)4595": {
"label": "enum FleekERC721.AccessPointCreationStatus",
"members": [
"DRAFT",
"APPROVED",
"REJECTED",
"REMOVED"
],
"numberOfBytes": "1"
},
"t_enum(Billing)4234": {
"label": "enum FleekBilling.Billing",
"members": [
"Mint",
"AddAccessPoint"
],
"numberOfBytes": "1"
},
"t_enum(CollectionRoles)3829": {
"label": "enum FleekAccessControl.CollectionRoles",
"members": [
"Owner"
],
"numberOfBytes": "1"
},
"t_enum(TokenRoles)3831": {
"label": "enum FleekAccessControl.TokenRoles",
"members": [
"Controller"
],
"numberOfBytes": "1"
},
"t_mapping(t_address,t_bool)": {
"label": "mapping(address => bool)",
"numberOfBytes": "32"
},
"t_mapping(t_address,t_mapping(t_address,t_bool))": {
"label": "mapping(address => mapping(address => bool))",
"numberOfBytes": "32"
},
"t_mapping(t_address,t_uint256)": {
"label": "mapping(address => uint256)",
"numberOfBytes": "32"
},
"t_mapping(t_enum(Billing)4234,t_uint256)": {
"label": "mapping(enum FleekBilling.Billing => uint256)",
"numberOfBytes": "32"
},
"t_mapping(t_enum(CollectionRoles)3829,t_mapping(t_address,t_bool))": {
"label": "mapping(enum FleekAccessControl.CollectionRoles => mapping(address => bool))",
"numberOfBytes": "32"
},
"t_mapping(t_enum(CollectionRoles)3829,t_uint256)": {
"label": "mapping(enum FleekAccessControl.CollectionRoles => uint256)",
"numberOfBytes": "32"
},
"t_mapping(t_enum(TokenRoles)3831,t_mapping(t_address,t_bool))": {
"label": "mapping(enum FleekAccessControl.TokenRoles => mapping(address => bool))",
"numberOfBytes": "32"
},
"t_mapping(t_string_memory_ptr,t_struct(AccessPoint)4609_storage)": {
"label": "mapping(string => struct FleekERC721.AccessPoint)",
"numberOfBytes": "32"
},
"t_mapping(t_uint256,t_address)": {
"label": "mapping(uint256 => address)",
"numberOfBytes": "32"
},
"t_mapping(t_uint256,t_mapping(t_enum(TokenRoles)3831,t_mapping(t_address,t_bool)))": {
"label": "mapping(uint256 => mapping(enum FleekAccessControl.TokenRoles => mapping(address => bool)))",
"numberOfBytes": "32"
},
"t_mapping(t_uint256,t_mapping(t_uint256,t_mapping(t_enum(TokenRoles)3831,t_mapping(t_address,t_bool))))": {
"label": "mapping(uint256 => mapping(uint256 => mapping(enum FleekAccessControl.TokenRoles => mapping(address => bool))))",
"numberOfBytes": "32"
},
"t_mapping(t_uint256,t_struct(App)4585_storage)": {
"label": "mapping(uint256 => struct FleekERC721.App)",
"numberOfBytes": "32"
},
"t_mapping(t_uint256,t_struct(Build)4590_storage)": {
"label": "mapping(uint256 => struct FleekERC721.Build)",
"numberOfBytes": "32"
},
"t_mapping(t_uint256,t_uint256)": {
"label": "mapping(uint256 => uint256)",
"numberOfBytes": "32"
},
"t_string_memory_ptr": {
"label": "string",
"numberOfBytes": "32"
},
"t_string_storage": {
"label": "string",
"numberOfBytes": "32"
},
"t_struct(AccessPoint)4609_storage": {
"label": "struct FleekERC721.AccessPoint",
"members": [
{
"label": "tokenId",
"type": "t_uint256",
"offset": 0,
"slot": "0"
},
{
"label": "score",
"type": "t_uint256",
"offset": 0,
"slot": "1"
},
{
"label": "contentVerified",
"type": "t_bool",
"offset": 0,
"slot": "2"
},
{
"label": "nameVerified",
"type": "t_bool",
"offset": 1,
"slot": "2"
},
{
"label": "owner",
"type": "t_address",
"offset": 2,
"slot": "2"
},
{
"label": "status",
"type": "t_enum(AccessPointCreationStatus)4595",
"offset": 22,
"slot": "2"
}
],
"numberOfBytes": "96"
},
"t_struct(App)4585_storage": {
"label": "struct FleekERC721.App",
"members": [
{
"label": "name",
"type": "t_string_storage",
"offset": 0,
"slot": "0"
},
{
"label": "description",
"type": "t_string_storage",
"offset": 0,
"slot": "1"
},
{
"label": "externalURL",
"type": "t_string_storage",
"offset": 0,
"slot": "2"
},
{
"label": "ENS",
"type": "t_string_storage",
"offset": 0,
"slot": "3"
},
{
"label": "currentBuild",
"type": "t_uint256",
"offset": 0,
"slot": "4"
},
{
"label": "builds",
"type": "t_mapping(t_uint256,t_struct(Build)4590_storage)",
"offset": 0,
"slot": "5"
},
{
"label": "logo",
"type": "t_string_storage",
"offset": 0,
"slot": "6"
},
{
"label": "color",
"type": "t_uint24",
"offset": 0,
"slot": "7"
},
{
"label": "accessPointAutoApproval",
"type": "t_bool",
"offset": 3,
"slot": "7"
}
],
"numberOfBytes": "256"
},
"t_struct(Build)4590_storage": {
"label": "struct FleekERC721.Build",
"members": [
{
"label": "commitHash",
"type": "t_string_storage",
"offset": 0,
"slot": "0"
},
{
"label": "gitRepository",
"type": "t_string_storage",
"offset": 0,
"slot": "1"
}
],
"numberOfBytes": "64"
},
"t_uint24": {
"label": "uint24",
"numberOfBytes": "3"
},
"t_uint256": {
"label": "uint256",
"numberOfBytes": "32"
},
"t_uint8": {
"label": "uint8",
"numberOfBytes": "1"
}
}
}
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,9 @@
{
"FleekERC721": [
{
"address": "0x37150709cFf366DeEaB836d05CAf49F4DA46Bb2E",
"timestamp": "3/3/2023, 4:43:25 PM"
},
{
"address": "0x550Ee47Fa9E0B81c1b9C394FeE62Fe699a955519",
"timestamp": "2/24/2023, 5:28:44PM"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -19,7 +19,10 @@ const createWriteContractContext = <
TAbi extends EthereumHooks.Abi,
TArgumentsMap extends EthereumHooks.WriteContext.ArgumentsMap,
TFunctionName extends keyof TArgumentsMap & string,
TFunctionArguments extends TArgumentsMap[TFunctionName]
TFunctionArguments extends [
...TArgumentsMap[TFunctionName],
EthereumHooks.WriteContext.SettingsParam
]
>(
address: string,
abi: TAbi,
@ -89,7 +92,10 @@ export const EthereumHooks = {
*/
createFleekERC721WriteContext: <
TFunctionName extends keyof ArgumentsMaps.FleekERC721 & string,
TFunctionArguments extends ArgumentsMaps.FleekERC721[TFunctionName]
TFunctionArguments extends [
...ArgumentsMaps.FleekERC721[TFunctionName],
EthereumHooks.WriteContext.SettingsParam
]
>(
functionName: TFunctionName
) => {
@ -115,7 +121,10 @@ export namespace EthereumHooks {
TAbi extends Abi,
TArgumentsMap extends ArgumentsMap,
TFunctionName extends keyof TArgumentsMap & string,
TFunctionArguments extends TArgumentsMap[TFunctionName]
TFunctionArguments extends [
...TArgumentsMap[TFunctionName],
EthereumHooks.WriteContext.SettingsParam
]
> {
functionName: TFunctionName;
prepare: ReturnType<
@ -147,6 +156,8 @@ export namespace EthereumHooks {
children?: React.ReactNode | React.ReactNode[];
config?: ProviderConfig<TFunctionName>;
}
export type SettingsParam = { value?: string };
}
}

View File

@ -5,6 +5,11 @@ import {
import { BytesLike } from 'ethers';
import { Ethereum } from '../ethereum';
enum Billing {
Mint,
AddAccessPoint,
}
enum CollectionRoles {
Owner,
Verifier,
@ -17,6 +22,12 @@ enum TokenRoles {
export const FleekERC721 = {
contract: Ethereum.getContract('FleekERC721'),
Enums: {
Billing,
CollectionRoles,
TokenRoles,
},
async mint(
params: FleekERC721.MintParams,
provider: Ethereum.Providers
@ -36,9 +47,7 @@ export const FleekERC721 = {
},
async tokenMetadata(tokenId: number): Promise<FleekERC721.Metadata> {
const contract = Ethereum.getContract('FleekERC721');
const response = await contract.tokenURI(Number(tokenId));
const response = await this.contract.tokenURI(Number(tokenId));
const parsed = JSON.parse(
Buffer.from(response.slice(29), 'base64')
@ -50,8 +59,15 @@ export const FleekERC721 = {
},
async lastTokenId(): Promise<number> {
// TODO: fetch last token id
return 7;
const contract = Ethereum.getContract('FleekERC721');
return contract.getLastTokenId();
},
async getBilling(key: keyof typeof Billing): Promise<string> {
const contract = Ethereum.getContract('FleekERC721');
return (await contract.getBilling(this.Enums.Billing[key])).toString();
},
parseError(error: BytesLike): FleekERC721.TransactionError {

View File

@ -0,0 +1,25 @@
import { FleekERC721 } from '@/integrations';
import { FleekERC721State, fleekERC721Actions, RootState } from '@/store';
import { createAsyncThunk } from '@reduxjs/toolkit';
type FetchBilling = FleekERC721State.BillingKeys;
export const fetchBilling = createAsyncThunk<void, FetchBilling>(
'fleekERC721/fetchBilling',
async (key, { dispatch, getState }) => {
const { billingState } = (getState() as RootState).fleekERC721;
if (billingState[key] === 'loading') return;
try {
dispatch(fleekERC721Actions.setBillingState({ key, value: 'loading' }));
const value = await FleekERC721.getBilling(key);
dispatch(fleekERC721Actions.setBilling({ key, value }));
} catch (error) {
console.log(error);
dispatch(fleekERC721Actions.setBillingState({ key, value: 'failed' }));
}
}
);

View File

@ -0,0 +1 @@
export * from './fetch-billing';

View File

@ -0,0 +1,68 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { RootState } from '@/store';
import { useAppSelector } from '@/store/hooks';
import * as asyncThunk from './async-thunk';
import { FleekERC721 } from '@/integrations';
export namespace FleekERC721State {
export type QueryState = undefined | 'loading' | 'failed' | 'success';
export type BillingKeys = keyof typeof FleekERC721.Enums.Billing;
export type Billing = {
[key in BillingKeys]?: string;
};
export type BillingState = {
[key in BillingKeys]?: QueryState;
};
}
export interface FleekERC721State {
billing: FleekERC721State.Billing;
billingState: FleekERC721State.BillingState;
}
const initialState: FleekERC721State = {
billing: {},
billingState: {},
};
export const fleekERC721Slice = createSlice({
name: 'fleekERC721',
initialState,
reducers: {
setBilling: (
state,
action: PayloadAction<{
key: FleekERC721State.BillingKeys;
value: string;
}>
) => {
state.billing[action.payload.key] = action.payload.value;
state.billingState[action.payload.key] = 'success';
},
setBillingState: (
state,
action: PayloadAction<{
key: FleekERC721State.BillingKeys;
value: Exclude<FleekERC721State.QueryState, 'success' | undefined>;
}>
) => {
state.billingState[action.payload.key] = action.payload.value;
},
},
});
export const fleekERC721Actions = {
...fleekERC721Slice.actions,
...asyncThunk,
};
const selectFleekERC721State = (state: RootState): FleekERC721State =>
state.fleekERC721;
export const useFleekERC721Store = (): FleekERC721State =>
useAppSelector(selectFleekERC721State);
export default fleekERC721Slice.reducer;

View File

@ -0,0 +1 @@
export * from './use-fleek-erc721-billing';

View File

@ -0,0 +1,24 @@
import { useAppDispatch } from '@/store/hooks';
import { useEffect } from 'react';
import {
fleekERC721Actions,
FleekERC721State,
useFleekERC721Store,
} from '../fleek-erc721-slice';
export const useFleekERC721Billing = (key: FleekERC721State.BillingKeys) => {
const { billing, billingState } = useFleekERC721Store();
const dispatch = useAppDispatch();
const refresh = (): void => {
dispatch(fleekERC721Actions.fetchBilling(key));
};
useEffect(() => {
if (typeof billingState[key] !== 'undefined') return;
refresh();
}, []);
return [billing[key], billingState[key], refresh] as const;
};

View File

@ -0,0 +1,2 @@
export * from './fleek-erc721-slice';
export * from './hooks';

View File

@ -1,3 +1,4 @@
export * from './ens';
export * from './fleek-erc721';
export * from './github';
export * from './toasts';

View File

@ -2,10 +2,12 @@ import { configureStore } from '@reduxjs/toolkit';
import githubReducer from './features/github/github-slice';
import toastsReducer from './features/toasts/toasts-slice';
import ensReducer from './features/ens/ens-slice';
import fleekERC721Reducer from './features/fleek-erc721/fleek-erc721-slice';
export const store = configureStore({
reducer: {
ens: ensReducer,
fleekERC721: fleekERC721Reducer,
github: githubReducer,
toasts: toastsReducer,
},

View File

@ -1,11 +1,12 @@
import { useState } from 'react';
import { ComboboxItem, DropdownItem } from '@/components';
import { Ethereum, EthereumHooks } from '@/integrations';
import { GithubState } from '@/store';
import { EthereumHooks } from '@/integrations';
import { GithubState, useFleekERC721Billing } from '@/store';
import { createContext } from '@/utils';
export type MintContext = {
billing: string | undefined;
selectedUserOrg: ComboboxItem;
repositoryName: GithubState.Repository;
branchName: DropdownItem; //get value from DropdownItem to mint
@ -68,6 +69,7 @@ export abstract class Mint {
const [ens, setEns] = useState({} as ComboboxItem);
const [domain, setDomain] = useState('');
const [verifyNFA, setVerifyNFA] = useState(true);
const [billing] = useFleekERC721Billing('Mint');
//Field validations
const [ensError, setEnsError] = useState<string>('');
@ -81,6 +83,7 @@ export abstract class Mint {
return (
<MintProvider
value={{
billing,
selectedUserOrg,
repositoryName,
branchName,

View File

@ -14,6 +14,7 @@ export const MintFormStep = () => {
const { address } = useAccount();
const { nextStep } = Stepper.useContext();
const {
billing,
appName,
appDescription,
domain,
@ -43,6 +44,7 @@ export const MintFormStep = () => {
appLogo,
parseColorToNumber(logoColor),
verifyNFA,
{ value: billing },
]);
nextStep();