Skip to content

Commit

Permalink
Create group chat comp contract verify (#772)
Browse files Browse the repository at this point in the history
* feat: contract fectch done

* feat: contract fectch done

* feat: token validation done

* feat: token symbol display done

* feat: duplicate rules fixed

* fix: fixed ui issues

* feat: added token truncation

* feat: token default value done

* fix: fixed minor issues

---------

Co-authored-by: Monalisha Mishra <[email protected]>
  • Loading branch information
MdTeach and mishramonalisha76 authored Oct 13, 2023
1 parent a21227a commit 03a2851
Show file tree
Hide file tree
Showing 13 changed files with 398 additions and 58 deletions.
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

0 comments on commit 03a2851

Please sign in to comment.