diff --git a/agent-node/src/functions/delegation.ts b/agent-node/src/functions/delegation.ts index 930dadeb2..587a5ecb1 100644 --- a/agent-node/src/functions/delegation.ts +++ b/agent-node/src/functions/delegation.ts @@ -22,7 +22,17 @@ export default async function handler( return await context.wallet .buildAndSubmit(req, true) .then((v) => v) - .catch((e) => { - throw e + .catch(async (e) => { + if (e.includes('StakeKeyNotRegisteredDELEG')) { + await context.builtins.registerStake() + return context.wallet + .buildAndSubmit(req, true) + .then((v) => v) + .catch((e) => { + throw e + }) + } else { + throw e + } }) } diff --git a/api/.env.example b/api/.env.example index b33740c3f..e146cb109 100644 --- a/api/.env.example +++ b/api/.env.example @@ -2,14 +2,17 @@ # Allowed Values : development , production APP_ENV=production -DATABASE_URL=postgresql://root:root@localhost:5432/cardano_autonomous_agent_testing_db +DATABASE_URL= +AGENT_MNEMONIC="" +KAFKA_BROKERS= +KAFKA_ENABLED=true DOCS_URL=/api/docs +OPENAPI_URL=/api/openapi.json -# Service can un without Kafka but Manual Triggers are disabled. -KAFKA_BROKERS=127.0.0.1:9002 -KAFKA_ENABLED=true -KUBER_URL='https://kuber-govtool.cardanoapi.io/api/v3' +KUBER_URL= + +JWT_SECRET_KEY="" -AGENT_MNEMONIC="abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art" -JWT_SECRET_KEY="1234567890" \ No newline at end of file +METADATA_API='' +GOV_ACTION_API='' \ No newline at end of file diff --git a/api/backend/app/controllers/internal/metadata_router.py b/api/backend/app/controllers/internal/metadata_router.py new file mode 100644 index 000000000..a61541ba6 --- /dev/null +++ b/api/backend/app/controllers/internal/metadata_router.py @@ -0,0 +1,27 @@ +import aiohttp +from classy_fastapi import Routable, get + +from backend.app.exceptions import HTTPException +from backend.config.api_settings import APISettings + + +class MetadataRouter(Routable): + + def __init__(self): + super().__init__() + self.metadata_api = APISettings().METADATA_API + + @get("/metadata") + async def fetch_metadata(self, metadata_url: str): + async with aiohttp.ClientSession() as session: + async with session.get( + f"{self.metadata_api}/metadata?url={metadata_url}&hash=1111111111111111111111111111111111111111111111111111111111111112" + ) as resp: + response = await resp.json() + if resp.ok: + if response.get("hash"): + return response.get("hash") + else: + raise HTTPException(status_code=400, content=response.get("message")) + else: + raise HTTPException(status_code=400, content=response.get("message")) diff --git a/api/backend/app/router.py b/api/backend/app/router.py index 377726cc9..5ff1978f5 100644 --- a/api/backend/app/router.py +++ b/api/backend/app/router.py @@ -8,7 +8,7 @@ from fastapi import APIRouter -from backend.app.controllers.internal import proposal_router, drep_router +from backend.app.controllers.internal import proposal_router, drep_router, metadata_router from backend.app.controllers import ( ready, demo, @@ -57,6 +57,8 @@ root_api_router.include_router(auth_router.AuthRouter().router, tags=["Authentication"], prefix="/auth") -root_api_router.include_router(proposal_router.ProposalRouter().router, tags=["Porposal Router"]) +root_api_router.include_router(proposal_router.ProposalRouter().router, tags=["Proposal Router"]) root_api_router.include_router(drep_router.DrepRouter().router, tags=["Drep Router"]) + +root_api_router.include_router(metadata_router.MetadataRouter().router, tags=["Metadata Router"]) diff --git a/api/backend/config/api_settings.py b/api/backend/config/api_settings.py index 58330f56b..f809eafd8 100644 --- a/api/backend/config/api_settings.py +++ b/api/backend/config/api_settings.py @@ -7,6 +7,7 @@ class APISettings(BaseSettings): SECURE: bool = None JWT_SECRET_KEY: str = "" GOV_ACTION_API: str = "https://govtool.cardanoapi.io/api" + METADATA_API: str = "" def __init__(self, **values): super().__init__(**values) diff --git a/frontend/next.config.mjs b/frontend/next.config.mjs index 33fbd9e02..a5dd98386 100644 --- a/frontend/next.config.mjs +++ b/frontend/next.config.mjs @@ -1,9 +1,9 @@ -import nextPwa from "next-pwa"; -import runtimeCaching from "next-pwa/cache.js"; +import nextPwa from 'next-pwa'; +import runtimeCaching from 'next-pwa/cache.js'; -import { withSentryConfig } from "@sentry/nextjs"; +import { withSentryConfig } from '@sentry/nextjs'; -import i18nextConfig from "./next-i18next.config.js"; +import i18nextConfig from './next-i18next.config.js'; const i18n = i18nextConfig.i18n; diff --git a/frontend/src/app/(pages)/agents/[agentID]/page.tsx b/frontend/src/app/(pages)/agents/[agentID]/page.tsx index 4dbf3d7b1..2aa9858c6 100644 --- a/frontend/src/app/(pages)/agents/[agentID]/page.tsx +++ b/frontend/src/app/(pages)/agents/[agentID]/page.tsx @@ -42,7 +42,6 @@ export default function AgentPageById() { useEffect(() => { if (agent) { setCurrentAgentName(agent?.name || ''); - console.log(agent.userAddress, currentConnectedWallet?.address); agent.userAddress === currentConnectedWallet?.address ? setAgentOwnerIsUser(true) : setAgentOwnerIsUser(false); diff --git a/frontend/src/app/(pages)/agents/create-agent/page.tsx b/frontend/src/app/(pages)/agents/create-agent/page.tsx index bbb9b300a..35d89eccf 100644 --- a/frontend/src/app/(pages)/agents/create-agent/page.tsx +++ b/frontend/src/app/(pages)/agents/create-agent/page.tsx @@ -71,7 +71,6 @@ export default function CreateAgentForm() { router.push('/agents'); }, onError: () => { - console.log('Error Response'); setSubmittingForm(false); ErrorToast('Error while creating Agent. Try Again!'); } @@ -131,7 +130,6 @@ export default function CreateAgentForm() { ref={multiSelectorRef} onChange={(option: IOption) => { setSelected([option]); - console.log(selected[0]); form.setValue( 'agentTemplate', option.value diff --git a/frontend/src/app/(pages)/templates/create-template/page.tsx b/frontend/src/app/(pages)/templates/create-template/page.tsx index ebc91ccde..4e8134e37 100644 --- a/frontend/src/app/(pages)/templates/create-template/page.tsx +++ b/frontend/src/app/(pages)/templates/create-template/page.tsx @@ -86,7 +86,6 @@ export default function TemplateForm() { const [selected, setSelected] = useState([]); function openSelectedOption(option: ITemplateOption) { - console.log(option); setCurrentDialogForm(option); toggleDialog(); } @@ -113,10 +112,6 @@ export default function TemplateForm() { } }, [functions]); - useEffect(() => { - console.log(selected); - }, [selected]); - /* Related to saving parameter fromt the dialog popup*/ function updateSelected({ inputType, @@ -158,7 +153,6 @@ export default function TemplateForm() { const router = useRouter(); function onSubmit(formData: z.infer) { - console.log(formData); setSubmittingForm(true); templateMutation.mutateAsync(formData); } diff --git a/frontend/src/app/api/metaData.ts b/frontend/src/app/api/metaData.ts new file mode 100644 index 000000000..415000d9d --- /dev/null +++ b/frontend/src/app/api/metaData.ts @@ -0,0 +1,11 @@ +import { baseAPIurl } from './config'; + +export async function fetchMetadataHash(url: string) { + const encodedUri = encodeURIComponent(url); + const res = await fetch(`${baseAPIurl}/metadata?metadata_url=${encodedUri}`); + if (res.ok) { + return await res.json(); + } else { + throw new Error(await res.json()); + } +} diff --git a/frontend/src/app/api/templates.ts b/frontend/src/app/api/templates.ts index eabf25433..6e2ee3e85 100644 --- a/frontend/src/app/api/templates.ts +++ b/frontend/src/app/api/templates.ts @@ -130,7 +130,6 @@ export const deleteTemplatebyID = async (templateID: string) => { const response = await axios.delete(`${baseAPIurl}/templates/${templateID}`, { withCredentials: true }); - console.log(response); if (response.status === 204) { return true; } else { diff --git a/frontend/src/app/api/triggerHistory.ts b/frontend/src/app/api/triggerHistory.ts index 36e116d22..8652fb175 100644 --- a/frontend/src/app/api/triggerHistory.ts +++ b/frontend/src/app/api/triggerHistory.ts @@ -74,7 +74,6 @@ export const fetchAllTriggerHistory = async ({ const encodedURL = encodeURI(fetchURL); const res = await fetch(encodedURL); - console.log(fetchURL); if (!res.ok) { throw new Error('Trigger Fetch Failed: Network Error'); diff --git a/frontend/src/assets/css/tailwind.css b/frontend/src/assets/css/tailwind.css index caa72275a..bec85ff20 100644 --- a/frontend/src/assets/css/tailwind.css +++ b/frontend/src/assets/css/tailwind.css @@ -109,7 +109,6 @@ @apply focus-visible:!outline-none focus-visible:!ring-0 focus-visible:!ring-offset-0 active:!border-none; } - .pagination-btn-position { - @apply absolute bottom-8 right-10 -} \ No newline at end of file + @apply absolute bottom-8 right-10; +} diff --git a/frontend/src/components/Agent/AgentOverview.tsx b/frontend/src/components/Agent/AgentOverview.tsx index b79b64926..e774900d1 100644 --- a/frontend/src/components/Agent/AgentOverview.tsx +++ b/frontend/src/components/Agent/AgentOverview.tsx @@ -85,9 +85,8 @@ const AgentOverViewComponent = ({ instance: agentInstance, agentConfigurations: updatedAgentConfigs }) - .then((res) => { + .then(() => { setIsEditing(false); - console.log(res); }); }; diff --git a/frontend/src/components/Agent/FormRenderer/FormRenderer.tsx b/frontend/src/components/Agent/FormRenderer/FormRenderer.tsx index 7b4b3e809..f4ce07816 100644 --- a/frontend/src/components/Agent/FormRenderer/FormRenderer.tsx +++ b/frontend/src/components/Agent/FormRenderer/FormRenderer.tsx @@ -45,7 +45,9 @@ const FormRenderer = ({ } }); const handleTrigger = async () => { - const errorIndexes = checkIfRequiredFieldIsEmpty(selectedFunction?.parameters); + const errorIndexes = await checkIfRequiredFieldIsEmpty( + selectedFunction?.parameters + ); setErrorIndex(errorIndexes); if (!errorIndexes.length) { const params = extractAnswerFromForm(selectedFunction); @@ -61,14 +63,16 @@ const FormRenderer = ({ } }; - function checkIfRequiredFieldIsEmpty(params?: Array) { + async function checkIfRequiredFieldIsEmpty(params?: Array) { const errorIndexes: number[] = []; - params?.forEach((param, paramIndex) => { - const isParamFieldsValid = validateForDifferentType(param); - if (!isParamFieldsValid) { - errorIndexes.push(paramIndex); - } - }); + await Promise.all( + params!.map(async (param, index) => { + const isValid = await validateForDifferentType(param); + if (!isValid) { + errorIndexes.push(index); + } + }) + ); return errorIndexes; } diff --git a/frontend/src/components/Agent/FormRenderer/ObjectParameter.tsx b/frontend/src/components/Agent/FormRenderer/ObjectParameter.tsx index 3a43bd7db..55c0bbd66 100644 --- a/frontend/src/components/Agent/FormRenderer/ObjectParameter.tsx +++ b/frontend/src/components/Agent/FormRenderer/ObjectParameter.tsx @@ -23,9 +23,17 @@ const ObjectParameter = ({ useEffect(() => { if (errorIndex.includes(paramIndex)) { - const errorText = checkIfToFillAnyOneField(parameter) - ? 'Please fill any one field' - : 'Please fill all required fields.'; + let errorText = ''; + if ( + (parameter.id === 'anchor' || parameter.id === 'newConstitution') && + parameter.parameters![0].errorMsg + ) { + errorText = parameter.parameters![0].errorMsg; + } else { + errorText = checkIfToFillAnyOneField(parameter) + ? 'Please fill any one field' + : 'Please fill all required fields.'; + } setErrorMsg(errorText); } }, [errorIndex]); @@ -92,7 +100,8 @@ const ObjectParameter = ({ type={param.type === 'number' ? 'number' : 'text'} /> - {errorMsg && !param.value && ( + {(param.errorMsg || + (!param.optional && errorMsg && !param.value)) && ( {errorMsg} diff --git a/frontend/src/components/Modals/ModalViews/AgentManualTriggerModalView.tsx b/frontend/src/components/Modals/ModalViews/AgentManualTriggerModalView.tsx index 736c496c2..8e29ba80f 100644 --- a/frontend/src/components/Modals/ModalViews/AgentManualTriggerModalView.tsx +++ b/frontend/src/components/Modals/ModalViews/AgentManualTriggerModalView.tsx @@ -39,7 +39,6 @@ const AgentManualTriggerModalView = ({ closeModal(); }, onError: () => { - console.log('Error Response'); ErrorToast('Error while manually triggering Agent Function. Try Again!'); } }); diff --git a/frontend/src/components/molecules/SelectedCard.tsx b/frontend/src/components/molecules/SelectedCard.tsx index 5a65e98f8..f5cd07f04 100644 --- a/frontend/src/components/molecules/SelectedCard.tsx +++ b/frontend/src/components/molecules/SelectedCard.tsx @@ -7,7 +7,6 @@ export default function SelectedCard({ handleUnselect = () => { console.log('Handle unselect'); }, - // handleEdit = () => console.log('Handle Edit'), description }: { name: string; diff --git a/frontend/src/models/types/agent.ts b/frontend/src/models/types/agent.ts index 67834aa38..bf79e24e1 100644 --- a/frontend/src/models/types/agent.ts +++ b/frontend/src/models/types/agent.ts @@ -15,7 +15,7 @@ interface IParameter { description: string; optional: boolean; data_type: string; - value?: string; + value?: any; } export interface IAgentTrigger { function_name: AgentTriggerFunctionType; diff --git a/frontend/src/models/types/functions.ts b/frontend/src/models/types/functions.ts index 0d101e7aa..8bd183b27 100644 --- a/frontend/src/models/types/functions.ts +++ b/frontend/src/models/types/functions.ts @@ -32,6 +32,7 @@ export interface IParameter { parameters?: IParameter[]; items?: ItemObject[]; value?: any; + errorMsg?: string; } export interface IParameterOption { @@ -150,7 +151,7 @@ export const AvailableFunctions: IFunctionsDto[] = [ { id: 'dataHash', name: 'Data Hash', - optional: false, + optional: true, type: 'hash' } ] @@ -203,7 +204,8 @@ export const AvailableFunctions: IFunctionsDto[] = [ { id: 'dataHash', name: 'Data Hash', - type: 'hash' + type: 'hash', + optional: true } ] }, @@ -221,7 +223,7 @@ export const AvailableFunctions: IFunctionsDto[] = [ { id: 'dataHash', name: 'Data Hash', - optional: false, + optional: true, type: 'hash' } ] @@ -253,7 +255,8 @@ export const AvailableFunctions: IFunctionsDto[] = [ { id: 'dataHash', name: 'Data Hash', - type: 'hash' + type: 'hash', + optional: true } ] } @@ -300,7 +303,8 @@ export const AvailableFunctions: IFunctionsDto[] = [ { id: 'dataHash', name: 'Data Hash', - type: 'hash' + type: 'hash', + optional: true } ] } @@ -324,7 +328,8 @@ export const AvailableFunctions: IFunctionsDto[] = [ { id: 'dataHash', name: 'Data Hash', - type: 'hash' + type: 'hash', + optional: true } ] } @@ -349,7 +354,8 @@ export const AvailableFunctions: IFunctionsDto[] = [ { id: 'dataHash', name: 'Data Hash', - type: 'hash' + type: 'hash', + optional: true } ] }, diff --git a/frontend/src/store/atoms/formRenderer.ts b/frontend/src/store/atoms/formRenderer.ts index 9a320d96f..08376ef1c 100644 --- a/frontend/src/store/atoms/formRenderer.ts +++ b/frontend/src/store/atoms/formRenderer.ts @@ -3,3 +3,5 @@ import { atom } from 'jotai'; export const errorAtom = atom>([]); export const selectedFunctionAtom = atom(null); + +export const errorMessageAtom = atom>>([]); diff --git a/frontend/src/utils/TriggerHistoryUtils.ts b/frontend/src/utils/TriggerHistoryUtils.ts index d4b0ebf98..4c4f47b10 100644 --- a/frontend/src/utils/TriggerHistoryUtils.ts +++ b/frontend/src/utils/TriggerHistoryUtils.ts @@ -26,8 +26,6 @@ export function calculateTriggerChangeRateforLast24Hours( ((last24HourSuccessTransactionsCount - prior24HourSuccessTransactionsCount) / prior24HourSuccessTransactionsCount) * 100; - //console.log(`Last 24 hours ${last24HourSuccessTransactionsCount} , Total : ${totalTransactionsCount}`) - //console.log(`Change Rate : ${changeRate}`) return changeRate; } diff --git a/frontend/src/utils/agent.ts b/frontend/src/utils/agent.ts index c48180c65..fd89bb30a 100644 --- a/frontend/src/utils/agent.ts +++ b/frontend/src/utils/agent.ts @@ -22,8 +22,11 @@ export function getConfiguredAgentTrigger( } switch (func) { - case 'stakeDelegation': - return { ...trigger, parameters: [{ ...trigger.parameters[0], value }] }; + case 'delegation': + return { + ...trigger, + parameters: [{ ...trigger.parameters[0], value: { drep: value } }] + }; case 'voteOnProposal': return { ...trigger, parameters: [{ ...trigger.parameters[0], value }] }; diff --git a/frontend/src/utils/formRenderer.ts b/frontend/src/utils/formRenderer.ts index 48e7a67e5..1eb00e8f6 100644 --- a/frontend/src/utils/formRenderer.ts +++ b/frontend/src/utils/formRenderer.ts @@ -1,3 +1,4 @@ +import { fetchMetadataHash } from '@api/metaData'; import { IFunctionsItem, IParameter } from '@models/types/functions'; export function checkIfToFillAnyOneField(parameter: IParameter) { @@ -45,7 +46,25 @@ export function validateForListType(param: IParameter) { } } -export function validateForObjectType(param: IParameter) { +async function validateAnchorUrl(param: IParameter) { + const anchorUrl = param.parameters![0].value; + const anchorHash = param.parameters![1].value; + if (anchorUrl && !anchorHash) { + try { + param.parameters![1].value = await fetchMetadataHash(anchorUrl); + return true; + } catch (err: Error) { + param.parameters![0].errorMsg = err.message; + return false; + } + } else return true; +} + +export async function validateForObjectType(param: IParameter) { + if (param.id === 'anchor' || param.id === 'newConstitution') { + const valid = await validateAnchorUrl(param); + if (!valid) return false; + } if (param.optional) { return checkIfAllFieldsAreEmptyOrFilled(param.parameters); } else { @@ -61,12 +80,12 @@ export function validateForObjectType(param: IParameter) { } } -export function validateForOptionType(param: IParameter) { +export async function validateForOptionType(param: IParameter) { switch (param.type) { case 'list': return validateForListType(param); case 'object': - return validateForObjectType(param); + return await validateForObjectType(param); case 'string': case 'hash': case 'url': @@ -84,14 +103,14 @@ export function validateForInputType(parameter: IParameter) { ); } -export function validateForDifferentType(param: IParameter) { +export async function validateForDifferentType(param: IParameter) { switch (param.type) { case 'list': return validateForListType(param); case 'object': - return validateForObjectType(param); + return await validateForObjectType(param); case 'options': - return validateForOptionType(param.parameters![0]); + return await validateForOptionType(param.parameters![0]); case 'string': case 'hash': case 'url':