Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create group chat comp contract verify #772

Merged
2 changes: 1 addition & 1 deletion packages/examples/sdk-frontend-react/src/app/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ export function App() {
<Web3Context.Provider value={{ account, active, library, chainId }}>
<SocketContext.Provider value={socketData}>
<AccountContext.Provider value={{ pgpPrivateKey, setSpaceId }}>
<ChatUIProvider env={env} theme={darkChatTheme} account={account} pgpPrivateKey={pgpPrivateKey} signer={signer}>
<ChatUIProvider env={env} theme={lightChatTheme} account={account} pgpPrivateKey={pgpPrivateKey} signer={signer}>
<SpacesUIProvider spaceUI={spaceUI} theme={customDarkTheme}>
<Routes>
<Route
Expand Down
126 changes: 96 additions & 30 deletions packages/uiweb/src/lib/components/chat/CreateGroup/AddCriteria.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { useContext, useEffect, useState } from 'react';

import styled from 'styled-components';
import { MdError } from 'react-icons/md';

import {
Button,
DropDownInput,
Expand Down Expand Up @@ -32,6 +35,8 @@ import {
TypeKeys,
ReadonlyInputType,
} from '../types';
import useToast from '../reusables/NewToast';
import { tokenFetchHandler } from '../helpers/tokenHelpers';
import {
CriteriaValidationErrorType,
Data,
Expand All @@ -40,7 +45,7 @@ import {
Rule,
} from '../types/tokenGatedGroupCreationType';
import { validationCriteria } from '../helpers';
import styled from 'styled-components';


const AddCriteria = ({
handlePrevious,
Expand All @@ -65,13 +70,16 @@ const AddCriteria = ({
const [url, setUrl] = useState<string>('');
const [guildId, setGuildId] = useState<string>('');
const [specificRoleId, setSpecificRoleId] = useState<string>('');
const [unit, setUnit] = useState('TOKEN');
const [decimals, setDecimals] = useState(18);

const [quantity, setQuantity] = useState<{ value: number; range: number }>({
value: 0,
range: 0,
});
const { env } = useChatData();
const theme = useContext(ThemeContext);
const groupInfoToast = useToast();

const isMobile = useMediaQuery(device.mobileL);

Expand Down Expand Up @@ -160,23 +168,15 @@ const AddCriteria = ({
},
};

const tokenCategoryValues = [
{
id: 0,
const dropdownSubCategoryValues: DropdownSubCategoryValuesType = {
ERC20: {
value: SUBCATEGORY.HOLDER,
title: 'Holder',
function: () => setSelectedSubCategoryValue(0),
},
{
id: 1,
ERC721:{
value: SUBCATEGORY.OWENER,
title: 'Owner',
function: () => setSelectedSubCategoryValue(1),
},
];
const dropdownSubCategoryValues: DropdownSubCategoryValuesType = {
ERC20: tokenCategoryValues,
ERC721: tokenCategoryValues,
INVITE: {
value: SUBCATEGORY.DEFAULT,
title: 'Default',
Expand Down Expand Up @@ -308,7 +308,7 @@ const AddCriteria = ({
let subCategory = 'DEFAULT';
if (_type === 'PUSH') {
if (category === CATEGORY.ERC20 || category === CATEGORY.ERC721) {
subCategory = tokenCategoryValues[selectedSubCategoryValue].value;
subCategory = category === CATEGORY.ERC20 ? SUBCATEGORY.HOLDER : SUBCATEGORY.OWENER
} else if (category === CATEGORY.CustomEndpoint) {
subCategory = 'GET';
}
Expand All @@ -323,7 +323,8 @@ const AddCriteria = ({
contract: `${selectedChain}:${contract}`,
amount: quantity.value,
comparison: dropdownQuantityRangeValues[quantity.range].value,
decimals: 18,
decimals: category === CATEGORY.ERC20 ? decimals : undefined,
token: unit,
};
} else if (category === CATEGORY.INVITE) {
const _inviteRoles = [];
Expand Down Expand Up @@ -364,7 +365,11 @@ const AddCriteria = ({
if (Object.keys(errors).length) {
setValidationErrors(errors);
} else {
criteriaState.addNewRule(rule);
const isSuccess = criteriaState.addNewRule(rule);
if (!isSuccess) {
showError('Selected Criteria was already added');
return;
}
if (handlePrevious) {
handlePrevious();
}
Expand Down Expand Up @@ -395,12 +400,16 @@ const AddCriteria = ({
oldValue.category === CATEGORY.ERC20 ||
oldValue.category === CATEGORY.ERC721
) {
setSelectedSubCategoryValue(
tokenCategoryValues.findIndex(
(obj) => obj.value === oldValue.subcategory
)
);

if(pushData.token){
setUnit(pushData.token)
}

if (pushData.decimals) {
setDecimals(decimals);
}

// TODO: make helper function for this
const contractAndChain: string[] = (
pushData.contract || 'eip155:1:0x'
).split(':');
Expand Down Expand Up @@ -439,6 +448,53 @@ const AddCriteria = ({
}
}, []);

const getSeletedType = () => {
return dropdownTypeValues[selectedTypeValue].value || 'PUSH';
};

const getSelectedCategory = () => {
const category: string =
(dropdownCategoryValues['PUSH'] as DropdownValueType[])[
selectedCategoryValue
].value || CATEGORY.ERC20;

return category;
};

const getSelectedChain = () => {
return dropdownChainsValues[selectedChainValue].value || 'eip155:1';
};

// Fetch the contract info
useEffect(() => {
// TODO: optimize to reduce this call call when user is typing
(async()=>{
setValidationErrors(prev => ({...prev, tokenError:undefined}))

const _type = getSeletedType();
const _category: string = getSelectedCategory();
const _chainInfo = getSelectedChain();

await tokenFetchHandler(
contract,
_type,
_category,
_chainInfo,
setUnit,
setDecimals
);
})();
}, [contract, selectedCategoryValue, selectedChainValue]);

const showError = (errorMessage: string) => {
groupInfoToast.showMessageToast({
toastTitle: 'Error',
toastMessage: errorMessage,
toastType: 'ERROR',
getToastIcon: (size) => <MdError size={size} color="red" />,
});
};

return (
<Section
flexDirection="column"
Expand Down Expand Up @@ -504,20 +560,32 @@ const AddCriteria = ({
selectedValue={selectedChainValue}
dropdownValues={dropdownChainsValues}
/>
<TextInput
labelName="Contract"
inputValue={contract}
onInputChange={(e: any) => setContract(e.target.value)}
placeholder="e.g. 0x123..."
/>
<Section gap="10px" flexDirection="column" alignItems="start">
<TextInput
labelName="Contract"
inputValue={contract}
onInputChange={(e: any) => setContract(e.target.value)}
placeholder="e.g. 0x123..."
error={!!validationErrors?.tokenError}
/>
{!!validationErrors?.tokenError && (
<ErrorSpan>{validationErrors?.tokenError}</ErrorSpan>
)}
</Section>
<Section gap="10px" flexDirection="column" alignItems="start">
<QuantityInput
dropDownValues={dropdownQuantityRangeValues}
labelName="Quantity"
inputValue={quantity}
error={!!validationErrors?.tokenAmount}
onInputChange={onQuantityChange}
placeholder="e.g. 1.45678"
unit={'TOKEN'}
unit={unit}
/>
{!!validationErrors?.tokenAmount && (
<ErrorSpan>{validationErrors?.tokenAmount}</ErrorSpan>
)}
</Section>
</>
)}

Expand Down Expand Up @@ -574,9 +642,7 @@ const AddCriteria = ({
<OptionButtons
options={GUILD_COMPARISON_OPTIONS}
totalWidth="410px"
selectedValue={
guildComparison
}
selectedValue={guildComparison}
error={!!validationErrors?.guildComparison}
handleClick={(newEl: string) => {
setGuildComparison(newEl);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ThemeContext } from '../theme/ThemeProvider';
import Dropdown, { DropdownValueType } from '../reusables/DropDown';
import { ConditionArray, ConditionData, IChatTheme } from '../exportedTypes';
import { useClickAway } from '../../../hooks';
import { CATEGORY, CRITERIA_TYPE, CriteriaType, TOKEN_NFT_COMPARISION, TokenNftComparision } from '../types';
import { CATEGORY, CRITERIA_TYPE, CriteriaType, PushData, TOKEN_NFT_COMPARISION, TokenNftComparision } from '../types';

import EditSvg from '../../../icons/EditSvg.svg';
import RemoveSvg from '../../../icons/RemoveSvg.svg';
Expand Down Expand Up @@ -94,11 +94,23 @@ const CriteriaSection = ({ criteria }: { criteria: ConditionData }) => {
return (GUILD_COMPARISON_OPTIONS.find(option => option.value === criteria?.data?.['comparison']))?.heading;

}

const getTokenSymbol = (conditionData:ConditionData)=>{
if(conditionData.data){
const data:PushData = conditionData.data;
if(data.token){
return shortenText(data.token, 15)
}
}

return conditionData.category
}

return (
<Section gap="8px">
<Span
alignSelf="center"
background="#657795"
background={theme.backgroundColor?.criteriaLabelBackground}
borderRadius="4px"
fontSize="10px"
color={theme.textColor?.buttonText}
Expand All @@ -112,7 +124,7 @@ const CriteriaSection = ({ criteria }: { criteria: ConditionData }) => {
{getTokenNftComparisionLabel()}{' '}
</Span>
{/* need to fetch token symbol */}
{criteria?.data?.['amount']} {criteria.category}
{criteria?.data?.['amount']} {getTokenSymbol(criteria)}
</Span>
)}
{criteria.category === CATEGORY.INVITE && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,12 @@ export const CreateGroupModal: React.FC<CreateGroupModalProps> = ({
}
}, [activeComponent]);

const [groupInputDetails, setGroupInputDetails] =
useState<GroupInputDetailsType>({
groupName: '',
groupDescription: '',
groupImage: '',
});
const useDummyGroupInfo = true;
const [groupInputDetails, setGroupInputDetails] = useState<GroupInputDetailsType>({
groupName: useDummyGroupInfo ? 'This is duumy group name' : '',
groupDescription: useDummyGroupInfo ? 'This is dummy group description for testing' : '',
groupImage: useDummyGroupInfo ? ProfilePicture : ''
})

const renderComponent = () => {
switch (activeComponent) {
Expand Down
53 changes: 44 additions & 9 deletions packages/uiweb/src/lib/components/chat/helpers/tokenGatedGroup.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import axios from 'axios';
import { ethers } from "ethers";

import { fetchERC20Info, fetchERC721nfo } from './tokenHelpers';
import {
CATEGORY,
CriteriaStateType,
Expand Down Expand Up @@ -121,17 +124,49 @@ const validateGUILDData = async (
return {};
};

const validationCriteria = async (
condition: Rule
): Promise<CriteriaValidationErrorType> => {
if (condition.type === TYPE.GUILD) {
return validateGUILDData(condition);
const validateTokenData = async (condition:Rule):Promise<CriteriaValidationErrorType> =>{
const data:PushData = condition.data;
const _contract = data.contract || ""
const _eip155Format = _contract.split(":")

if(_eip155Format.length !==3){
return {tokenError:"Invalid contract address"}
}
if (condition.category === CATEGORY.CustomEndpoint)
return validateCustomEndpointData(condition);

return {};
};
const [chainId, address] = [parseInt(_eip155Format[1]), _eip155Format[2]]

if(!ethers.utils.isAddress(address)){
return {tokenError:`Invalid contract address`}
}

const [err] = condition.category === CATEGORY.ERC721 ?
await fetchERC721nfo(address, chainId) : await fetchERC20Info(address, chainId);

if(err){
return {tokenError:`Invalid ${condition.category} contract`}
}
if(!data.amount){
return {tokenAmount:`Amount cannot be 0`}
}
return {}
}

const validationCriteria = async (condition: Rule):Promise<CriteriaValidationErrorType> => {
if(condition.type === TYPE.GUILD)
{
return validateGUILDData(condition);
}else{
if(condition.category === CATEGORY.INVITE){
return {}
}else if (condition.category === CATEGORY.CustomEndpoint){
return validateCustomEndpointData(condition);
}else{
return validateTokenData(condition)
}
}

}

export {
handleDefineCondition,
validationCriteria,
Expand Down
Loading