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

feat (48829): agent stuff updates #94

Merged
merged 3 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/components/manage-registry/ManageRegistry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,16 @@ import { useTokenIdentityRegistry } from "@/hooks/useTokenIdentityRegistry";
import { useWalletInterface } from "@/services/wallets/useWalletInterface";
import { ManageAgents } from "./manage-agents/ManageAgents";
import { ManageIdentities } from "./manage-identities/ManageIdentities";
import { encodeLogToIdentity } from "@/services/util/helpers";
import { LogDescription } from "ethers";

export const ManageRegistry = ({ isAgents }: { isAgents: boolean }) => {
export const ManageRegistry = ({
isAgents,
identities,
}: {
isAgents: boolean;
identities: LogDescription[];
}) => {
const [ownTokens, setOwnTokens] = useState<Array<TokenNameItem>>([]);
const [selectedIdentity, setSelectedIdentity] = useState<{
walletAddress: EvmAddress;
Expand All @@ -52,6 +60,8 @@ export const ManageRegistry = ({ isAgents }: { isAgents: boolean }) => {

const { isOpen, onOpen, onClose } = useDisclosure();

const identityItems = identities.map((id) => encodeLogToIdentity(id));
maryjanyes marked this conversation as resolved.
Show resolved Hide resolved

useEffect(() => {
(deployedTokens as any).map((item: any) => {
const tokenAddress = item["args"]?.[0];
Expand Down Expand Up @@ -112,6 +122,7 @@ export const ManageRegistry = ({ isAgents }: { isAgents: boolean }) => {
setUpdateTxError={setUpdateTxError}
setUpdateTxResult={setUpdateTxResult}
registry={registry}
identityItems={identityItems}
/>
) : (
<ManageIdentities
Expand Down
158 changes: 137 additions & 21 deletions src/components/manage-registry/manage-agents/ManageAgents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import {
Input,
Alert,
AlertDescription,
AlertIcon,
} from "@chakra-ui/react";
import { GroupBase } from "react-select";
import { useState } from "react";
import { useRef, useState } from "react";
import { useMutation } from "@tanstack/react-query";
import { ethers } from "ethers";
import { useQueryClient } from "@tanstack/react-query";
import { EvmAddress } from "@/types/types";
import { EvmAddress, IdentityItem, InputRefProps } from "@/types/types";
import { QueryKeys } from "@/hooks/types";
import { MenuSelect } from "@/components/MenuSelect";
import {
Expand All @@ -21,24 +21,41 @@ import {
import { useWalletInterface } from "@/services/wallets/useWalletInterface";
import { WalletInterface } from "@/services/wallets/walletInterface";
import { useTokenIdentityRegistryAgents } from "@/hooks/useTokenIdentityRegistryAgents";
import { ethers } from "ethers";

type ManageAgentsProps = {
setUpdateTxResult: (res?: string) => void;
setUpdateTxError: (err?: string) => void;
onClose: () => void;
registry?: EvmAddress;
identityItems: IdentityItem[];
};

export function ManageAgents({
setUpdateTxResult,
setUpdateTxError,
registry,
identityItems,
onClose,
}: ManageAgentsProps) {
const [selectedAgentToAdd, setSelectedAgentToAdd] = useState<EvmAddress>();
const [selectedAgent, setSelectedAgent] = useState<EvmAddress>();
const [newUserAgentAddress, setNewUserAgentAddress] = useState<string>();
const { walletInterface } = useWalletInterface();
const { filteredAgents } = useTokenIdentityRegistryAgents(registry);
const inputRef = useRef<HTMLInputElement>(null);
const [agentManualError, setAgentManualError] = useState<string>();
const [agentSelectError, setAgentSelectError] = useState<string>();
maryjanyes marked this conversation as resolved.
Show resolved Hide resolved
const { walletInterface, accountEvm } = useWalletInterface();
const { filteredAgents, filteredNotAgentsYet } =
useTokenIdentityRegistryAgents(
registry,
identityItems.map((agent) => agent.identity as EvmAddress),
);
const accountIdentity = identityItems.find(
(item) => item.wallet === accountEvm,
);
const isAccountAgentAdmin = filteredNotAgentsYet?.includes(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we sure that having a record here makes a user an Admin? Maybe user was removed afterwards? Is there a reliable way to detect Agent vs Admin?

Copy link
Collaborator Author

@maryjanyes maryjanyes Oct 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we sure that having a record here makes a user an Admin? Maybe user was removed afterwards? Is there a reliable way to detect Agent vs Admin?

Admin is the one who can perform edit operations, because of it Agent became Admin and Admin also became Agent. Correct me if i wrong @dchernokur.

accountIdentity?.identity as EvmAddress,
);
maryjanyes marked this conversation as resolved.
Show resolved Hide resolved

const queryClient = useQueryClient();

const {
Expand All @@ -58,7 +75,7 @@ export function ManageAgents({
mutateAsync: mutateIdentityRegistryAddAgent,
isPending: isAddAgentPending,
} = useMutation({
mutationFn: async () => {
mutationFn: async (newUserAgentAddress: string) => {
dchernokur marked this conversation as resolved.
Show resolved Hide resolved
return writeIdentityRegistryAddAgent(
walletInterface as WalletInterface,
{ args: [newUserAgentAddress as EvmAddress] },
Expand All @@ -67,10 +84,53 @@ export function ManageAgents({
},
});

const addNewUserAgentAsAdmin = async () => {
if (filteredAgents.includes(selectedAgentToAdd)) {
setAgentSelectError("Agent already exists");

return;
} else {
setAgentSelectError(undefined);
}

if (selectedAgentToAdd && ethers.isAddress(selectedAgentToAdd)) {
try {
const txHash = await mutateIdentityRegistryAddAgent(selectedAgentToAdd);
setUpdateTxResult(txHash);
setUpdateTxError(undefined);
setTimeout(() => {
maryjanyes marked this conversation as resolved.
Show resolved Hide resolved
queryClient.invalidateQueries({
queryKey: [QueryKeys.ReadAgentInRegistry],
});
}, 1000);
} catch (err: any) {
setUpdateTxResult(undefined);
setUpdateTxError(err);
}

onClose();
(inputRef.current as unknown as InputRefProps)?.setValue(undefined);
} else {
setAgentSelectError("Address is empty or not an EVM address");
}
};

const addNewUserAgent = async () => {
if (ethers.isAddress(newUserAgentAddress)) {
const newUserAgentAddress = (inputRef.current as unknown as InputRefProps)
?.value;

if (filteredAgents.includes(newUserAgentAddress)) {
setAgentManualError("Agent already exists");

return;
} else {
setAgentManualError(undefined);
}

if (newUserAgentAddress && ethers.isAddress(newUserAgentAddress)) {
try {
const txHash = await mutateIdentityRegistryAddAgent();
const txHash =
await mutateIdentityRegistryAddAgent(newUserAgentAddress);
setUpdateTxResult(txHash);
setUpdateTxError(undefined);
setTimeout(() => {
Expand All @@ -82,12 +142,12 @@ export function ManageAgents({
setUpdateTxResult(undefined);
setUpdateTxError(err);
}

onClose();
(inputRef.current as unknown as InputRefProps)?.setValue(undefined);
} else {
setUpdateTxError("User agent address is incorrect");
setUpdateTxResult(undefined);
setAgentManualError("Address is empty or not an EVM address");
}
onClose();
setNewUserAgentAddress(undefined);
};

const handleAgentSelect = (agent: string) => {
Expand All @@ -108,6 +168,7 @@ export function ManageAgents({
setUpdateTxResult(undefined);
setUpdateTxError(err);
}

onClose();
setSelectedAgent(undefined);
};
Expand All @@ -118,9 +179,9 @@ export function ManageAgents({
<Flex direction="row" gap="2">
<Input
width="70%"
value={newUserAgentAddress}
placeholder="User agent address"
onChange={(e) => setNewUserAgentAddress(e.target.value)}
size="md"
ref={inputRef}
maryjanyes marked this conversation as resolved.
Show resolved Hide resolved
placeholder="Paste user agent address manually"
/>
<Button
width="28%"
Expand All @@ -130,12 +191,67 @@ export function ManageAgents({
Add new agent
</Button>
</Flex>
<Alert status="success" mt="2">
<AlertDescription fontSize="12">
Somebody who can perform update / delete operations on {"identity"}{" "}
wallet.
</AlertDescription>
<Alert status={agentManualError ? "error" : "success"} mt="2">
{agentManualError ? (
<>
<AlertIcon />
<AlertDescription fontSize={12}>
{agentManualError}
</AlertDescription>
</>
) : (
<AlertDescription fontSize="12">
Somebody who can perform update / delete operations on{" "}
maryjanyes marked this conversation as resolved.
Show resolved Hide resolved
{"identity"} wallet.
</AlertDescription>
)}
</Alert>
{isAccountAgentAdmin && !!filteredNotAgentsYet && (
maryjanyes marked this conversation as resolved.
Show resolved Hide resolved
<Flex direction="column" mt="7">
<MenuSelect
data={
filteredNotAgentsYet.map((agent) => ({
value: agent,
label: agent,
})) as unknown as GroupBase<string | number>[]
}
label="Select agent to add (only for admin)"
onTokenSelect={(value) =>
setSelectedAgentToAdd(value as EvmAddress)
}
/>
<Alert status={agentSelectError ? "error" : "success"} mt="2">
{agentSelectError ? (
<>
<AlertIcon />
<AlertDescription fontSize={12}>
{agentSelectError}
</AlertDescription>
</>
) : (
<AlertDescription fontSize="12">
Somebody who can perform update / delete operations on{" "}
maryjanyes marked this conversation as resolved.
Show resolved Hide resolved
{"identity"} wallet.
</AlertDescription>
)}
</Alert>
{selectedAgentToAdd && (
<>
<Text fontWeight="bold" style={{ fontSize: 14 }} mt="2">
Selected agent: {selectedAgentToAdd}
</Text>
<Button
width="50%"
onClick={addNewUserAgentAsAdmin}
mt="2"
isLoading={isAddAgentPending}
>
Add selected agent
</Button>
</>
)}
</Flex>
)}
{!!filteredAgents && (
<Flex direction="column" mt="7">
<MenuSelect
Expand Down
37 changes: 35 additions & 2 deletions src/hooks/useTokenIdentityRegistryAgents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import { useEffect, useState } from "react";
import { WatchContractEventReturnType } from "viem";
import { QueryKeys } from "./types";

export function useTokenIdentityRegistryAgents(registry?: EvmAddress) {
export function useTokenIdentityRegistryAgents(
registry?: EvmAddress,
identityItems?: EvmAddress[],
) {
const [uniqueAgents, setUniqueAgents] = useState<Array<string>>([]);

useEffect(() => {
Expand All @@ -34,6 +37,33 @@ export function useTokenIdentityRegistryAgents(registry?: EvmAddress) {
}
}, [registry]);

const filteredNotAgentsYetQueryResult = useQueries({
maryjanyes marked this conversation as resolved.
Show resolved Hide resolved
queries: (identityItems ?? []).map((agent) => ({
maryjanyes marked this conversation as resolved.
Show resolved Hide resolved
queryKey: [QueryKeys.ReadAgentInRegistry, agent],
enabled: !!identityItems?.length && !!registry,
maryjanyes marked this conversation as resolved.
Show resolved Hide resolved
queryFn: async () => {
const isAgent = await readIdentityRegistryIsAgent(
{ args: [agent] },
registry,
);

return {
agent,
isAgent,
};
},
staleTime: Infinity,
})),
combine: (result) => {
maryjanyes marked this conversation as resolved.
Show resolved Hide resolved
return result
.filter(
(agent) =>
!(agent.data?.isAgent as unknown as { "0": boolean })?.["0"],
)
.map((agent) => agent.data?.agent);
},
});

const filteredAgentsQueryResult = useQueries({
queries: uniqueAgents.map((agent) => ({
queryKey: [QueryKeys.ReadAgentInRegistry, agent],
Expand Down Expand Up @@ -61,5 +91,8 @@ export function useTokenIdentityRegistryAgents(registry?: EvmAddress) {
},
});

return { filteredAgents: filteredAgentsQueryResult };
return {
filteredAgents: filteredAgentsQueryResult,
filteredNotAgentsYet: filteredNotAgentsYetQueryResult,
};
}
10 changes: 9 additions & 1 deletion src/services/util/helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { isNil } from "lodash";
import { VAULT_TOKEN_PRECISION_VALUE } from "@/config/constants";
import BigNumber from "bignumber.js";
import { formatUnits, parseUnits } from "viem";
import { EvmAddress } from "@/types/types";
import { EvmAddress, IdentityItem } from "@/types/types";
import { LogDescription } from "ethers";

const COINGECKO_API_ENDPOINT = "https://api.coingecko.com/api/v3";

Expand Down Expand Up @@ -83,3 +84,10 @@ export function getFiatCurrencyRate(
`${COINGECKO_API_ENDPOINT}/simple/price?ids=${coinName}&vs_currencies=${fiatName}`,
);
}

export function encodeLogToIdentity(log: LogDescription): IdentityItem {
return {
wallet: log.args[0],
identity: log.args[1],
};
}
10 changes: 10 additions & 0 deletions src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ export type TransferTokenFromRequest = {

export type EvmAddress = `0x${string}`;

export type IdentityItem = {
wallet: string;
identity: string;
};

export type InputRefProps = {
value?: string;
setValue: (value?: string) => void;
};

export type VaultInfoProps = {
vaultAddress: EvmAddress;
};
Expand Down
7 changes: 4 additions & 3 deletions src/views/eip3643/EIP3643.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import { ManageRegistry } from "@/components/manage-registry/ManageRegistry";

export default function EIP3643() {
const { accountId } = useWalletInterface();
const { setDeployedTokens, setIdentities } = useContext(Eip3643Context);
const { setDeployedTokens, setIdentities, identities } =
useContext(Eip3643Context);

useEffect(() => {
const unsubTokens: WatchContractEventReturnType =
Expand Down Expand Up @@ -63,10 +64,10 @@ export default function EIP3643() {
<NFT />
</TabPanel>
<TabPanel>
<ManageRegistry isAgents={false} />
<ManageRegistry isAgents={false} identities={identities} />
maryjanyes marked this conversation as resolved.
Show resolved Hide resolved
</TabPanel>
<TabPanel>
<ManageRegistry isAgents />
<ManageRegistry isAgents identities={identities} />
</TabPanel>
</TabPanels>
</Tabs>
Expand Down