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 2 commits
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
12 changes: 10 additions & 2 deletions src/components/manage-registry/ManageRegistry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
useDisclosure,
} from "@chakra-ui/react";
import { GroupBase } from "react-select";
import { useContext, useState, useEffect } from "react";
import { useContext, useState, useEffect, useMemo } from "react";
import { EvmAddress, TokenNameItem } from "@/types/types";
import { Eip3643Context } from "@/contexts/Eip3643Context";
import { MenuSelect } from "@/components/MenuSelect";
Expand All @@ -35,6 +35,7 @@ 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";

export const ManageRegistry = ({ isAgents }: { isAgents: boolean }) => {
const [ownTokens, setOwnTokens] = useState<Array<TokenNameItem>>([]);
Expand All @@ -47,11 +48,16 @@ export const ManageRegistry = ({ isAgents }: { isAgents: boolean }) => {
useTokenIdentityRegistry(selectedToken);
const [updateTxResult, setUpdateTxResult] = useState<string>();
const [updateTxError, setUpdateTxError] = useState<string>();
const { deployedTokens } = useContext(Eip3643Context);
const { deployedTokens, identities } = useContext(Eip3643Context);
const { accountEvm } = useWalletInterface();

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

const identityItems = useMemo(
() => identities.map((id) => encodeLogToIdentity(id)),
[identities],
);

useEffect(() => {
(deployedTokens as any).map((item: any) => {
const tokenAddress = item["args"]?.[0];
Expand Down Expand Up @@ -112,6 +118,8 @@ export const ManageRegistry = ({ isAgents }: { isAgents: boolean }) => {
setUpdateTxError={setUpdateTxError}
setUpdateTxResult={setUpdateTxResult}
registry={registry}
identityItems={identityItems}
selectedIdentity={selectedIdentity?.identityAddress}
/>
) : (
<ManageIdentities
Expand Down
174 changes: 143 additions & 31 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 { useMemo, 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 } from "@/types/types";
import { QueryKeys } from "@/hooks/types";
import { MenuSelect } from "@/components/MenuSelect";
import {
Expand All @@ -21,30 +21,56 @@ 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;
identityItems: IdentityItem[];
registry?: EvmAddress;
selectedIdentity?: EvmAddress;
};

export function ManageAgents({
setUpdateTxResult,
setUpdateTxError,
registry,
identityItems,
selectedIdentity,
onClose,
}: ManageAgentsProps) {
const [selectedAgentToAdd, setSelectedAgentToAdd] = useState<EvmAddress>();
const [agentToAdd, setAgentToAdd] = useState<EvmAddress>();
const [selectedAgent, setSelectedAgent] = useState<EvmAddress>();
const [newUserAgentAddress, setNewUserAgentAddress] = useState<string>();
const { walletInterface } = useWalletInterface();
const [agentManualError, setAgentManualError] = useState("");
const [agentSelectError, setAgentSelectError] = useState("");
const { walletInterface, accountEvm } = useWalletInterface();
const { filteredAgents } = useTokenIdentityRegistryAgents(registry);
const identityItemsToAddAsAgents = identityItems.filter(
maryjanyes marked this conversation as resolved.
Show resolved Hide resolved
(item) => !filteredAgents.find((agent) => agent === item.identity),
);
const accountIdentity = identityItems.find(
(item) => item.wallet?.toLowerCase() === accountEvm,
);
const isAccountAgentAdmin = useMemo(
() =>
accountIdentity?.identity === selectedIdentity ||
filteredAgents?.includes(accountIdentity?.identity as EvmAddress),
[filteredAgents, accountIdentity?.identity, selectedIdentity],
);

const queryClient = useQueryClient();

const {
mutateAsync: mutateIdentityRegistryRemoveAgent,
isPending: isRemoveAgentPending,
} = useMutation({
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [QueryKeys.ReadAgentInRegistry],
});
},
mutationFn: async () => {
return writeIdentityRegistryRemoveAgent(
walletInterface as WalletInterface,
Expand All @@ -58,7 +84,12 @@ export function ManageAgents({
mutateAsync: mutateIdentityRegistryAddAgent,
isPending: isAddAgentPending,
} = useMutation({
mutationFn: async () => {
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [QueryKeys.ReadAgentInRegistry],
});
},
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,27 +98,55 @@ export function ManageAgents({
},
});

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

return;
} else {
setAgentSelectError("");
}

if (selectedAgentToAdd && ethers.isAddress(selectedAgentToAdd)) {
try {
const txHash = await mutateIdentityRegistryAddAgent(selectedAgentToAdd);
setUpdateTxResult(txHash);
setUpdateTxError(undefined);
} catch (err: any) {
setUpdateTxResult(undefined);
setUpdateTxError(err);
}

onClose();
} else {
setAgentSelectError("Address is empty or not an EVM address");
}
};

const addNewUserAgent = async () => {
if (ethers.isAddress(newUserAgentAddress)) {
if (filteredAgents.includes(agentToAdd as EvmAddress)) {
setAgentManualError("Agent already exists");

return;
} else {
setAgentManualError("");
}

if (agentToAdd && ethers.isAddress(agentToAdd)) {
try {
const txHash = await mutateIdentityRegistryAddAgent();
const txHash = await mutateIdentityRegistryAddAgent(agentToAdd);
setUpdateTxResult(txHash);
setUpdateTxError(undefined);
setTimeout(() => {
queryClient.invalidateQueries({
queryKey: [QueryKeys.ReadAgentInRegistry],
});
}, 1000);
} catch (err: any) {
setUpdateTxResult(undefined);
setUpdateTxError(err);
}

onClose();
setAgentToAdd(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 @@ -96,18 +155,14 @@ export function ManageAgents({

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

onClose();
setSelectedAgent(undefined);
};
Expand All @@ -118,9 +173,11 @@ 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"
onInput={(e) => {
maryjanyes marked this conversation as resolved.
Show resolved Hide resolved
setAgentToAdd((e.target as any).value);
}}
placeholder="Paste user agent address manually"
/>
<Button
width="28%"
Expand All @@ -130,12 +187,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">
User which can perform update / delete operations on identity
wallet.
</AlertDescription>
)}
</Alert>
{isAccountAgentAdmin && (
<Flex direction="column" mt="7">
<MenuSelect
data={
identityItemsToAddAsAgents.map((item) => ({
value: item.identity,
label: item.identity,
})) 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">
User which can perform update / delete operations on 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
22 changes: 13 additions & 9 deletions src/hooks/useTokenIdentityRegistryAgents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ import { useEffect, useState } from "react";
import { WatchContractEventReturnType } from "viem";
import { QueryKeys } from "./types";

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

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

Expand Down Expand Up @@ -51,15 +60,10 @@ export function useTokenIdentityRegistryAgents(registry?: EvmAddress) {
},
staleTime: Infinity,
})),
combine: (result) => {
return result
.filter(
(agent) =>
!!(agent.data?.isAgent as unknown as { "0": boolean })?.["0"],
)
.map((agent) => agent.data?.agent);
},
combine: combineIdentityRegistryAgentsOnlyAgentsResult,
});

return { filteredAgents: filteredAgentsQueryResult };
return {
filteredAgents: filteredAgentsQueryResult,
};
}
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],
};
}
5 changes: 5 additions & 0 deletions src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ export type TransferTokenFromRequest = {

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

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

export type VaultInfoProps = {
vaultAddress: EvmAddress;
};
Expand Down
3 changes: 2 additions & 1 deletion 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