diff --git a/agent-manager/package.json b/agent-manager/package.json index 45404d040..565da1f35 100644 --- a/agent-manager/package.json +++ b/agent-manager/package.json @@ -27,7 +27,7 @@ "express": "^4.19.2", "kafkajs": "^2.2.4", "kuber-client": "^3.0.3", - "libcardano": "1.4.3", + "libcardano": "1.4.11", "luxon": "^3.4.4", "prisma": "^5.13.0", "swagger-jsdoc": "^6.2.8", diff --git a/agent-manager/src/config/tracer.ts b/agent-manager/src/config/tracer.ts index 9a5b245f2..3976ec6a4 100644 --- a/agent-manager/src/config/tracer.ts +++ b/agent-manager/src/config/tracer.ts @@ -1,25 +1,26 @@ -import { ParameterizedMessageObject, Span, Transaction } from "elastic-apm-node"; +import { ParameterizedMessageObject, Span, Transaction } from 'elastic-apm-node' -export const apm = require('elastic-apm-node') +import apmAgent from 'elastic-apm-node' +export const apm = apmAgent -function readConfig(){ - if(process.env.ELASTIC_APM_SERVER_URL && process.env.ELASTIC_APM_API_KEY && process.env.ELASTIC_APM_ENVIRONMENT){ - return { - ELASTIC_APM_SERVER_URL: process.env.ELASTIC_APM_SERVER_URL, - ELASTIC_APM_API_KEY: process.env.ELASTIC_APM_API_KEY, - ELASTIC_APM_ENVIRONMENT: process.env.ELASTIC_APM_ENVIRONMENT - } - } +function readConfig() { + if (process.env.ELASTIC_APM_SERVER_URL && process.env.ELASTIC_APM_API_KEY && process.env.ELASTIC_APM_ENVIRONMENT) { + return { + ELASTIC_APM_SERVER_URL: process.env.ELASTIC_APM_SERVER_URL, + ELASTIC_APM_API_KEY: process.env.ELASTIC_APM_API_KEY, + ELASTIC_APM_ENVIRONMENT: process.env.ELASTIC_APM_ENVIRONMENT, + } + } } -export const apmConfig=readConfig() +export const apmConfig = readConfig() -export const startTransaction= (...args: any[]): Transaction=>{ - return apm.startTransaction(...args); +export const startTransaction = (...args: any[]): Transaction => { + return apm.startTransaction(...args) } -export const captureError= (err: Error | string | ParameterizedMessageObject)=>{ - return apm.captureError(err) +export const captureError = (err: Error | string | ParameterizedMessageObject) => { + return apm.captureError(err) +} +export const startSpan = (...args: any[]): Span | any => { + return apm.startSpan(...args) } -export const startSpan = (...args: any[]) : Span | any =>{ - return apm.startSpan(...args); -} \ No newline at end of file diff --git a/agent-manager/src/controller/blockfrostHealth.ts b/agent-manager/src/controller/blockfrostHealth.ts new file mode 100644 index 000000000..9191bbf34 --- /dev/null +++ b/agent-manager/src/controller/blockfrostHealth.ts @@ -0,0 +1,20 @@ +import { Router, Request, Response } from 'express' +import { handlerWrapper } from '../utils/asyncWrapper' +import environments from '../config/environments' + +const router = Router() + +async function blockfrostHealthCheck(req: Request, res: Response) { + const url = `https://cardano-${environments.networkName}.blockfrost.io/api/v0/health` + const response = await fetch(url, { + method: 'GET', + headers: { + project_id: environments.blockFrostApiKey, + }, + }) + return res.status(response.status).send(await response.json()) +} + +router.get('/', handlerWrapper(blockfrostHealthCheck)) + +export default router diff --git a/agent-manager/src/controller/health.ts b/agent-manager/src/controller/health.ts new file mode 100644 index 000000000..dd6abc45c --- /dev/null +++ b/agent-manager/src/controller/health.ts @@ -0,0 +1,32 @@ +import { Router, Request, Response } from 'express' +import { handlerWrapper } from '../utils/asyncWrapper' +import { checkKafkaStatus } from '../service/healthCheck/kafka' +import { cardanoNodeStatus } from '../service/healthCheck/cardanoNode' + +const router = Router() + +async function healthCheck(req: Request, res: Response) { + try { + const isKafkaHealthy = await checkKafkaStatus() + const nodeStatus = cardanoNodeStatus.checkStatus() + return res.status(isKafkaHealthy && nodeStatus.isHealthy ? 200 : 503).send({ + isHealthy: isKafkaHealthy && nodeStatus.isHealthy, + details: { + kafka: { + isHealthy: isKafkaHealthy, + }, + cardanoNode: { + isHealthy: nodeStatus.isHealthy, + secsSinceLastBlock: cardanoNodeStatus.lastTimeStamp, + lastBlock: nodeStatus.block, + }, + }, + }) + } catch (err: any) { + return res.status(500).send(err.message ? err.message : err) + } +} + +router.get('/', handlerWrapper(healthCheck)) + +export default router diff --git a/agent-manager/src/index.ts b/agent-manager/src/index.ts index 9647f1e31..be05f133d 100644 --- a/agent-manager/src/index.ts +++ b/agent-manager/src/index.ts @@ -1,21 +1,14 @@ -process.env.ELASTIC_APM_LOG_LEVEL='warning' -process.env.ELASTIC_APM_SERVICE_NAME='autonomous-agents-manager' -process.env.ELASTIC_APM_ENVIRONMENT=process.env.ELASTIC_APM_ENVIRONMENT || 'local' +process.env.ELASTIC_APM_LOG_LEVEL = 'warning' +process.env.ELASTIC_APM_SERVICE_NAME = 'autonomous-agents-manager' +process.env.ELASTIC_APM_ENVIRONMENT = process.env.ELASTIC_APM_ENVIRONMENT || 'local' import * as dotenv from 'dotenv' dotenv.config() -import * as agent from 'elastic-apm-node/start'; agent; -import { startTransaction } from "./config/tracer"; -// agent.start({ -// serviceName: 'autonomous-agents-manager', -// environment: 'local', -// // Use if APM Server requires a token -// -// // Use if APM Server uses API keys for authentication -// apiKey: process.env.ELASTIC_APM_API_KEY, -// -// // Set custom APM Server URL (default: http://127.0.0.1:8200) -// serverUrl: process.env.ELASTIC_APM_SERVER_URL, -// }) +// import * as agent from 'elastic-apm-node/start' +// agent +import { startTransaction } from './config/tracer' +import { cardanoNodeStatus } from './service/healthCheck/cardanoNode' +import healthCheck from './controller/health' +import blockfrostHealth from './controller/blockfrostHealth' import express from 'express' import { WebSocket } from 'ws' import { initKafkaConsumers } from './service/Listeners/KafkaMessageConsumer' @@ -24,10 +17,13 @@ import { createBlockchainInstance } from './service/Listeners/BlockchainService' import { ManagerWalletService } from './service/Manager/ManagerWallet' import { TxListener } from './service/Listeners/TxListener' import { parseRawBlockBody } from 'libcardano/cardano/ledger-serialization/transaction' -import environments from "./config/environments"; +import environments from './config/environments' const app = express() const port = environments.serverPort +app.use('/health', healthCheck) +app.use('/blockfrost/health', blockfrostHealth) + const server = app.listen(port, async () => { console.log(`Server is running on http://localhost:${port}`) @@ -43,7 +39,9 @@ const server = app.listen(port, async () => { blockchain.start() blockchain.blockChain.on('extendBlock', (block) => { - const transaction = startTransaction('extendBlock','node',) + const transaction = startTransaction('extendBlock', 'node') + cardanoNodeStatus.onBlockTimeStamp(Date.now(), block) + console.log( `[Blockchain] RollForward blockNo=${block.blockNo} hash=${block.headerHash.toString('hex')} slot=${block.slotNo}` ) diff --git a/agent-manager/src/repository/agent_manager_repository.ts b/agent-manager/src/repository/agent_manager_repository.ts index 806691cff..8173d6198 100644 --- a/agent-manager/src/repository/agent_manager_repository.ts +++ b/agent-manager/src/repository/agent_manager_repository.ts @@ -1,6 +1,5 @@ import { JsonValue } from '@prisma/client/runtime/library' -import { prisma } from "./dbClient"; - +import { prisma } from './dbClient' export async function getAgentIdBySecret(agentSecret: Buffer): Promise { return prisma.agent diff --git a/agent-manager/src/repository/dbClient.ts b/agent-manager/src/repository/dbClient.ts index 8a372bb94..f9edd2170 100644 --- a/agent-manager/src/repository/dbClient.ts +++ b/agent-manager/src/repository/dbClient.ts @@ -1,24 +1,24 @@ -import { PrismaClient } from "@prisma/client"; -import { apm } from "../config/tracer"; +import { PrismaClient } from '@prisma/client' +import { apm } from '../config/tracer' export const prisma = new PrismaClient() -if(process.env.ELASTIC_APM_SERVER_URL && process.env.ELASTIC_APM_API_KEY){ - prisma.$use(async (params: any, next:any) => { - const spanName=params.model?`prisma.${params.model}.${params.action}`:`prisma.${params.action}` - const span = apm.startSpan(spanName); - if (span) { - span.type = "DB"; - span.subtype = "prisma"; - span.action = "query"; - } - try { - const result = await next(params); - span?.end(); - return result; - } catch (e) { - span?.end(); - throw e; - } - }); -} \ No newline at end of file +if (process.env.ELASTIC_APM_SERVER_URL && process.env.ELASTIC_APM_API_KEY) { + prisma.$use(async (params: any, next: any) => { + const spanName = params.model ? `prisma.${params.model}.${params.action}` : `prisma.${params.action}` + const span = apm.startSpan(spanName) + if (span) { + span.type = 'DB' + span.subtype = 'prisma' + span.action = 'query' + } + try { + const result = await next(params) + span?.end() + return result + } catch (e) { + span?.end() + throw e + } + }) +} diff --git a/agent-manager/src/repository/trigger_history_repository.ts b/agent-manager/src/repository/trigger_history_repository.ts index bf8864cca..0f338abef 100644 --- a/agent-manager/src/repository/trigger_history_repository.ts +++ b/agent-manager/src/repository/trigger_history_repository.ts @@ -1,8 +1,6 @@ -import { PrismaClient } from '@prisma/client' import { DateTime } from 'luxon' import { v4 as uuidv4 } from 'uuid' -import { prisma } from "./dbClient"; - +import { prisma } from './dbClient' export type TriggerType = 'CRON' | 'MANUAL' | 'EVENT' | 'INTERNAL' diff --git a/agent-manager/src/service/Listeners/KafkaMessageConsumer.ts b/agent-manager/src/service/Listeners/KafkaMessageConsumer.ts index e16b8a578..481beab70 100644 --- a/agent-manager/src/service/Listeners/KafkaMessageConsumer.ts +++ b/agent-manager/src/service/Listeners/KafkaMessageConsumer.ts @@ -7,7 +7,7 @@ import environments from '../../config/environments' const config = environments.kafka const configTopic = `${config.topicPrefix || config.prefix || 'agent'}-updates` const triggerTopic = `${config.topicPrefix || config.prefix || 'agent'}-triggers` -const topicList = [configTopic, triggerTopic] +export const topicList = [configTopic, triggerTopic] const brokers = config.brokers .split(',') .map((x) => x.trim()) @@ -19,7 +19,9 @@ const kafka = new Kafka({ brokers, // Update with your Kafka broker address }) -const consumer = kafka.consumer({ groupId }) +export const consumer = kafka.consumer({ + groupId, +}) export async function initKafkaConsumers(manager: AgentManagerRPC) { console.log('[Kafka]', `brokers:${brokers}, groupId:${groupId}, topics:${topicList}`) @@ -74,3 +76,10 @@ export async function initKafkaConsumers(manager: AgentManagerRPC) { }, }) } + +const { HEARTBEAT } = consumer.events +let lastHeartbeat: number = 0 +consumer.on(HEARTBEAT, ({ timestamp }) => { + lastHeartbeat = timestamp +}) +export const fetchConsumerLatestHeartbeat = () => lastHeartbeat diff --git a/agent-manager/src/service/Manager/AgentManagerRPC.ts b/agent-manager/src/service/Manager/AgentManagerRPC.ts index 30a37659f..596406e13 100644 --- a/agent-manager/src/service/Manager/AgentManagerRPC.ts +++ b/agent-manager/src/service/Manager/AgentManagerRPC.ts @@ -1,18 +1,17 @@ -import {IncomingMessage} from 'http' +import { IncomingMessage } from 'http' import { fetchAgentConfiguration, getAgentIdBySecret, updateLastActiveTimestamp, } from '../../repository/agent_manager_repository' -import {WsRpcServer} from '../../lib/WsRpcServer' -import {RpcV1} from 'libcardano/network/Rpc' -import {TriggerType} from '../../repository/trigger_history_repository' -import {ManagerWalletService} from './ManagerWallet' -import {Server} from 'ws' -import {generateRootKey} from '../../utils/cardano' -import {RPCTopicHandler} from "../RPCTopicHandler"; -import { apmConfig, captureError, startTransaction } from "../../config/tracer"; - +import { WsRpcServer } from '../../lib/WsRpcServer' +import { RpcV1 } from 'libcardano/network/Rpc' +import { TriggerType } from '../../repository/trigger_history_repository' +import { ManagerWalletService } from './ManagerWallet' +import { Server } from 'ws' +import { generateRootKey } from '../../utils/cardano' +import { RPCTopicHandler } from '../RPCTopicHandler' +import { apmConfig, captureError, startTransaction } from '../../config/tracer' export interface ILog { function_name: string @@ -38,24 +37,24 @@ export class AgentManagerRPC extends WsRpcServer { } protected async validateConnection(req: IncomingMessage): Promise { - let tx = _tx = startTransaction('connect','request') + const tx = (_tx = startTransaction('connect', 'request')) const agentSecretKey = req.url?.slice(1) console.log('new connection from', req.socket.remoteAddress) tx.addLabels({ - 'request_ip': req.socket.remoteAddress, // Request IP address (note: underscore instead of dot) - }); + request_ip: req.socket.remoteAddress, // Request IP address (note: underscore instead of dot) + }) // Loop through all request headers and add them as labels - for (let [headerKey, headerValue] of Object.entries(req.headers)) { + for (const [headerKey, headerValue] of Object.entries(req.headers)) { // Replace dots in header keys with underscores - const labelKey = `request_headers_${headerKey.replace(/\./g, '_')}`; + const labelKey = `request_headers_${headerKey.replace(/\./g, '_')}` // Make sure the header value is a string and add as a label if (typeof headerValue === 'string') { - tx.addLabels({ [labelKey]: headerValue }); + tx.addLabels({ [labelKey]: headerValue }) } else { // If the value is not a string (like an array), we can stringify it - tx.addLabels({ [labelKey]: JSON.stringify(headerValue) }); + tx.addLabels({ [labelKey]: JSON.stringify(headerValue) }) } } @@ -77,30 +76,34 @@ export class AgentManagerRPC extends WsRpcServer { protected async handleMethodCall(connection_id: string, method: string, args: any[]) { console.log('Method call from client', connection_id, method, args) - const tx=startTransaction("RPC "+method,'request') - const labels: Record={ - 'connectionId': connection_id, - 'method': method - } - args.map((x,index)=>{ - labels['arg'+index] = x - }) - tx.addLabels(labels) - let _tx=tx as any - _tx.context = {'connectionId': connection_id} - - return this.rpcTopicHandler.handleEvent(method, connection_id, args).then(v=>{ - tx.setOutcome('success') - tx.addLabels({'result': v}) - return v + const tx = startTransaction('RPC ' + method, 'request') + const labels: Record = { + connectionId: connection_id, + method: method, + } + args.map((x, index) => { + labels['arg' + index] = x }) - .catch(e=>{ - captureError(e) - throw e - }).finally(()=>tx.end()) + tx.addLabels(labels) + const _tx = tx as any + _tx.context = { connectionId: connection_id } + + return this.rpcTopicHandler + .handleEvent(method, connection_id, args) + .then((v) => { + tx.setOutcome('success') + tx.addLabels({ result: v }) + return v + }) + .catch((e) => { + captureError(e) + throw e + }) + .finally(() => tx.end()) } protected async validateEventBroadcast(connection_id: string, topic: string, message: any): Promise { + console.debug("message : ",message) // TODO: handle the event emitted if (topic === 'active_connection') { await updateLastActiveTimestamp(connection_id) @@ -110,8 +113,8 @@ export class AgentManagerRPC extends WsRpcServer { } protected onReady(client: RpcV1): void { - const thisTx=_tx; - _tx=undefined + const thisTx = _tx + _tx = undefined const agentConfigsPromise = fetchAgentConfiguration(client.getId()) .then(async (config) => { if (!config) { @@ -119,12 +122,11 @@ export class AgentManagerRPC extends WsRpcServer { return } - const {instanceCount, agentIndex, agentName} = config + const { instanceCount, agentIndex, agentName } = config const rootKeyBuffer = await generateRootKey(agentIndex || 0) - client.emit('instance_count', {instanceCount, rootKeyBuffer, agentName}) - if(apmConfig) - { - (config as any).apm=apmConfig + client.emit('instance_count', { instanceCount, rootKeyBuffer, agentName }) + if (apmConfig) { + ;(config as any).apm = apmConfig } client.emit('initial_config', config) @@ -132,20 +134,23 @@ export class AgentManagerRPC extends WsRpcServer { .catch((error) => { throw error }) - Promise.all([agentConfigsPromise]).catch((err: Error) => { - console.error('AgentManagerRPC onReady Error:', err) - this.disconnect(client.getId(), err.message) - }).then(()=>{ - if(thisTx){ - thisTx.setOutcome('success') - } - }).catch((e)=>{ - captureError(e) - }).finally(()=>{ - if(thisTx){ - thisTx.end() - } - }) - + Promise.all([agentConfigsPromise]) + .catch((err: Error) => { + console.error('AgentManagerRPC onReady Error:', err) + this.disconnect(client.getId(), err.message) + }) + .then(() => { + if (thisTx) { + thisTx.setOutcome('success') + } + }) + .catch((e) => { + captureError(e) + }) + .finally(() => { + if (thisTx) { + thisTx.end() + } + }) } -} \ No newline at end of file +} diff --git a/agent-manager/src/service/RPCTopicHandler.ts b/agent-manager/src/service/RPCTopicHandler.ts index a0684d59a..47af8b1bb 100644 --- a/agent-manager/src/service/RPCTopicHandler.ts +++ b/agent-manager/src/service/RPCTopicHandler.ts @@ -1,10 +1,10 @@ -import {ManagerWalletService} from "./Manager/ManagerWallet"; -import {txSubmitter} from "./TxBuildAndSubmit/TxSubmitterService"; -import {kuber} from "./TxBuildAndSubmit/KuberService"; -import {saveTriggerHistory, updateAgentDrepRegistration} from "../repository/trigger_history_repository"; -import {ILog} from "./Manager/AgentManagerRPC"; -import {metaDataService} from "./MetadataService"; -import {dbSync} from "./DbSyncService"; +import { ManagerWalletService } from './Manager/ManagerWallet' +import { txSubmitter } from './TxBuildAndSubmit/TxSubmitterService' +import { kuber } from './TxBuildAndSubmit/KuberService' +import { saveTriggerHistory, updateAgentDrepRegistration } from '../repository/trigger_history_repository' +import { ILog } from './Manager/AgentManagerRPC' +import { metaDataService } from './MetadataService' +import { dbSync } from './DbSyncService' export class RPCTopicHandler { managerWallet @@ -13,11 +13,7 @@ export class RPCTopicHandler { this.managerWallet = managerWallet } - handleEvent( - eventName: string, - connection_id: string, - args: any, - ) : Promise { + handleEvent(eventName: string, connection_id: string, args: any): Promise { const handler = (this as any)[eventName] if (handler === undefined || eventName === 'constructor') { console.error('Unknown event type', eventName, 'received') @@ -90,4 +86,4 @@ export class RPCTopicHandler { const [url, hash] = args return metaDataService.fetchMetadata(url, hash) } -} \ No newline at end of file +} diff --git a/agent-manager/src/service/healthCheck/cardanoNode.ts b/agent-manager/src/service/healthCheck/cardanoNode.ts new file mode 100644 index 000000000..fbc501248 --- /dev/null +++ b/agent-manager/src/service/healthCheck/cardanoNode.ts @@ -0,0 +1,27 @@ +import { BlockEvent } from 'libcardano/types' + +interface IBlock { + blockNo: number + hash: string + slot: number +} + +class CardanoNodeStatus { + lastTimeStamp: number = 0 + block: IBlock | null = null + + onBlockTimeStamp(timeStamp: number, block: BlockEvent) { + this.lastTimeStamp = timeStamp + this.block = { + hash: block.headerHash.toString('hex'), + blockNo: block.blockNo, + slot: block.slotNo, + } + } + + checkStatus() { + return { isHealthy: Date.now() - this.lastTimeStamp < 6 * 60 * 1000, block: this.block } + } +} + +export const cardanoNodeStatus = new CardanoNodeStatus() diff --git a/agent-manager/src/service/healthCheck/kafka.ts b/agent-manager/src/service/healthCheck/kafka.ts new file mode 100644 index 000000000..6645f00ac --- /dev/null +++ b/agent-manager/src/service/healthCheck/kafka.ts @@ -0,0 +1,23 @@ +import { consumer, fetchConsumerLatestHeartbeat } from '../Listeners/KafkaMessageConsumer' + +export async function checkKafkaStatus() { + try { + const lastHeartbeat = fetchConsumerLatestHeartbeat() + const isHealthy = async () => { + // so it is healthy + if (Date.now() - lastHeartbeat < 10000) { + return true + } + + try { + const { state } = await consumer.describeGroup() + return ['CompletingRebalance', 'PreparingRebalance'].includes(state) + } catch (err) { + return false + } + } + return isHealthy() + } catch (error: any) { + throw new Error('Error connecting to Kafka:', error.message ? error.message : error) + } +} diff --git a/agent-manager/src/utils/asyncWrapper.ts b/agent-manager/src/utils/asyncWrapper.ts new file mode 100644 index 000000000..2ad1feefd --- /dev/null +++ b/agent-manager/src/utils/asyncWrapper.ts @@ -0,0 +1,15 @@ +import { NextFunction, Request, Response } from 'express' + +export const handlerWrapper = (fn: any) => (req: Request, res: Response, next: NextFunction) => { + try { + const result = fn(req, res, next) + if (result instanceof Promise) { + result.catch((e) => { + next(e) + }) + } + } catch (error) { + // Handle sync errors + next(error) + } +} diff --git a/agent-manager/yarn.lock b/agent-manager/yarn.lock index 8720cd305..0686cd097 100644 --- a/agent-manager/yarn.lock +++ b/agent-manager/yarn.lock @@ -2951,10 +2951,10 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -libcardano@1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/libcardano/-/libcardano-1.4.3.tgz#a3dc20452e1520b65fa32621337de62413a3cd5c" - integrity sha512-5R5aE+WHwC1wxFLH4fVjXeQh0WbOLCBZKcFmbVYvGwWy2TMaqspncy8QMclqD03x8GtdK7yWEFTp9buGP9/eTw== +libcardano@1.4.11: + version "1.4.11" + resolved "https://registry.yarnpkg.com/libcardano/-/libcardano-1.4.11.tgz#ffe0f1f63a4a9bb940e87a5b5b68284cbbbaa1a7" + integrity sha512-rl/okJH27NBGwOx7VTgBkOJe4q6vBbBE4FbPrXXDhgLgI55004K0ODK0R4+3HCHAL8TtnXPqPRJXE5vvO++lIQ== dependencies: "@cardano-sdk/crypto" "^0.1.30" "@emurgo/cardano-serialization-lib-nodejs" "^11.5.0" diff --git a/agent-node/.prettierrc.json b/agent-node/.prettierrc.json index e74ed9ff3..f0db82f11 100644 --- a/agent-node/.prettierrc.json +++ b/agent-node/.prettierrc.json @@ -2,5 +2,6 @@ "trailingComma": "es5", "tabWidth": 4, "semi": false, - "singleQuote": true + "singleQuote": true, + "printWidth": 120 } diff --git a/agent-node/package.json b/agent-node/package.json index b13772a11..57e14cd21 100644 --- a/agent-node/package.json +++ b/agent-node/package.json @@ -23,7 +23,7 @@ "bech32": "^2.0.0", "dotenv": "^16.4.5", "kuber-client": "^2.0.0", - "libcardano": "1.4.2", + "libcardano": "1.4.19", "luxon": "^3.4.4", "node-cron": "^3.0.3", "ws": "^8.18.0" diff --git a/agent-node/src/constants/global.ts b/agent-node/src/constants/global.ts index 148f37a89..b66858947 100644 --- a/agent-node/src/constants/global.ts +++ b/agent-node/src/constants/global.ts @@ -1,13 +1,10 @@ -import { EventTriggerTypeDetails } from '../types/types' +import { IEventBasedAction } from '../types/eventTriger' export const globalState: { - eventTriggerTypeDetails: EventTriggerTypeDetails + eventTriggerTypeDetails: IEventBasedAction[] agentName: string } = { - eventTriggerTypeDetails: { - eventType: false, - function_name: '', - }, + eventTriggerTypeDetails: [], agentName: '', } diff --git a/agent-node/src/executor/AgentFunctions.ts b/agent-node/src/executor/AgentFunctions.ts index 468d7e5fe..21e690f0c 100644 --- a/agent-node/src/executor/AgentFunctions.ts +++ b/agent-node/src/executor/AgentFunctions.ts @@ -7,6 +7,7 @@ export interface FunctionHolder { export interface FunctionGroup { functions: FunctionHolder builtins: FunctionHolder + filters: FunctionHolder } // Helper function to require modules with extensions automatically function requireModule(filePath: string) { @@ -27,6 +28,7 @@ function requireModule(filePath: string) { function loadHandlersSync(directory: string): FunctionGroup { const handlers: FunctionHolder = {} const builtins: FunctionHolder = {} + const filters: FunctionHolder = {} // Get the list of files in the directory const files = fs.readdirSync(directory) @@ -44,17 +46,18 @@ function loadHandlersSync(directory: string): FunctionGroup { const module: any = requireModule(filePath) // Check if the module exports a `handler` function const handler = module.handler || module.default + const filter = module.filter if (typeof handler === 'function') { handlers[baseFileName] = handler } if (module.builtin || handler.name == 'builtin') { builtins[baseFileName] = module.builtin || module.default } + if (filter && typeof filter == 'function') { + filters[baseFileName] = filter + } } catch (error) { - console.error( - `Failed to load handler from file ${filePath}:`, - error - ) + console.error(`Failed to load handler from file ${filePath}:`, error) } } }) @@ -62,6 +65,7 @@ function loadHandlersSync(directory: string): FunctionGroup { return { functions: handlers, builtins: builtins, + filters: filters, } } diff --git a/agent-node/src/executor/AgentRunner.ts b/agent-node/src/executor/AgentRunner.ts index cd8e16dc6..b19ab090d 100644 --- a/agent-node/src/executor/AgentRunner.ts +++ b/agent-node/src/executor/AgentRunner.ts @@ -7,24 +7,45 @@ import { loadRootKeyFromBuffer } from '../utils/cardano' import { HdWallet } from 'libcardano' import { AgentWalletDetails } from '../types/types' import { globalState } from '../constants/global' +import { EventContext } from './BaseFunction' export class AgentRunner { executor: Executor managerInterface: ManagerInterface + constructor(managerInterface: ManagerInterface, txListener: TxListener) { this.managerInterface = managerInterface this.executor = new Executor(null, managerInterface, txListener) } - invokeFunction( + invokeFunction(triggerType: TriggerType, instanceIndex: number, method: string, ...args: any) { + this.executor.invokeFunction(method, ...args).then((result) => { + saveTxLog(result, this.managerInterface, triggerType, instanceIndex) + }) + } + + async invokeFunctionWithEventContext( + eventFilterContext: any, + context: EventContext, triggerType: TriggerType, instanceIndex: number, method: string, - ...args: any + parameters: any[] ) { - this.executor.invokeFunction(method, ...args).then((result) => { - saveTxLog(result, this.managerInterface, triggerType, instanceIndex) - }) + const eventContext = { + event: context, + filter: eventFilterContext, + parameters, + } + const params = await this.executor + .filterFunctionParams(method, eventContext) + .catch((err) => console.error('Function Invocation Error: ', err)) + + if (params) { + this.executor.invokeFunctionWithContext(eventContext, method, ...params).then((result) => { + saveTxLog(result, this.managerInterface, triggerType, instanceIndex) + }) + } } async remakeContext(index: number) { @@ -33,20 +54,13 @@ export class AgentRunner { const account = await hdWallet.getAccount(index) const accountWallet = await account.singleAddressWallet() const agentInstanceWallet: AgentWalletDetails = { - payment_signing_key: - accountWallet.paymentKey.private.toString('hex'), + payment_signing_key: accountWallet.paymentKey.private.toString('hex'), stake_signing_key: accountWallet.stakeKey!.private.toString('hex'), - payment_verification_key_hash: - accountWallet.paymentKey.pkh.toString('hex'), - stake_verification_key_hash: - accountWallet.stakeKey!.pkh.toString('hex'), + payment_verification_key_hash: accountWallet.paymentKey.pkh.toString('hex'), + stake_verification_key_hash: accountWallet.stakeKey!.pkh.toString('hex'), agent_address: accountWallet.getAddress(0).toBech32(), drep_id: accountWallet.stakeKey!.pkh.toString('hex'), } - this.executor.remakeContext( - agentInstanceWallet, - this.managerInterface, - `${globalState.agentName}#${index}` - ) + this.executor.remakeContext(agentInstanceWallet, this.managerInterface, `${globalState.agentName}#${index}`) } } diff --git a/agent-node/src/executor/BaseFunction.ts b/agent-node/src/executor/BaseFunction.ts index 034d8133c..6e0f677af 100644 --- a/agent-node/src/executor/BaseFunction.ts +++ b/agent-node/src/executor/BaseFunction.ts @@ -1,39 +1,48 @@ +import { Transaction } from 'libcardano/cardano/ledger-serialization/transaction' +import { DecodedBlock } from './TxListener' + export interface Key { private: string public: string pubKeyHash: string + signRaw(data: Buffer): Buffer + verify(signature: Buffer): Buffer } + export interface Wallet { address: string paymentKey: Key stakeKey: Key rewardAddress: string drepId: string - buildAndSubmit( - spec: any, - stakeSigning?: boolean, - saveDrepStatus?: boolean - ): Promise + + buildAndSubmit(spec: any, stakeSigning?: boolean, saveDrepStatus?: boolean): Promise + signTx(txRaw: Buffer, stakeSigning?: boolean): Buffer } export interface KuberApi { buildTx(spec: any): Promise + buildAndSubmit(spec: any): Promise } + // types.ts export interface TxInfo { txId: string cborHex: string + [key: string]: any // Allows for additional properties } + export interface OffchainData { url: string hash: string } + export type DelegationTarget = | 'abstain' | 'no-confidence' @@ -43,31 +52,17 @@ export type DelegationTarget = export interface Builtins { stakeDeRegistration: () => Promise - transferADA: ( - address: string, - amount: string | number | Record - ) => Promise - waitTxConfirmation: ( - txId: string, - confirmation: number, - timeoutMs: number - ) => Promise + transferADA: (address: string, amount: string | number | Record) => Promise + waitTxConfirmation: (txId: string, confirmation: number, timeoutMs: number) => Promise dRepRegistration: (anchor?: OffchainData) => Promise dRepDeRegistration: () => Promise registerStake: () => Promise - voteOnProposal: ( - proposal: string, - vote: boolean | string | undefined, - anchor?: OffchainData - ) => Promise + voteOnProposal: (proposal: string, vote: boolean | string | undefined, anchor?: OffchainData) => Promise abstainDelegation: (target: DelegationTarget) => Promise - callWebhook: ( - url: string, - data: Record | any[] | string - ) => Promise + callWebhook: (url: string, data: Record | any[] | string) => Promise loadFunds: (amount: number) => Promise saveMetadata: (content: string) => Promise - fetchMetadata: (url: string,hash:string) => Promise + fetchMetadata: (url: string, hash: string) => Promise } export interface Helpers { @@ -76,19 +71,25 @@ export interface Helpers { generateVoteMetadataContent: () => string } +export interface EventContext { + tx: Transaction + block: DecodedBlock + confirmation: number +} + export interface FunctionContext { wallet: Wallet kuber: KuberApi builtins: Builtins agentName: string helpers: Helpers + event?: EventContext + filter?: any + parameters?: { name: string; value: string }[] } // Create a restricted execution environment -export const restrictedExecution = function ( - functionString: string, - context: Record -) { +export const restrictedExecution = function (functionString: string, context: Record) { // Provide only necessary globals and variables within a sandbox const restrictedGlobals = { Math, diff --git a/agent-node/src/executor/Executor.ts b/agent-node/src/executor/Executor.ts index fcb6e1599..dbb2a6189 100644 --- a/agent-node/src/executor/Executor.ts +++ b/agent-node/src/executor/Executor.ts @@ -1,12 +1,12 @@ -import {AgentWalletDetails} from '../types/types' -import {ManagerInterface} from '../service/ManagerInterfaceService' -import {FunctionGroup, getHandlers} from './AgentFunctions' -import {Builtins, FunctionContext, Key, Wallet} from './BaseFunction' -import {TxListener} from './TxListener' -import {generateProposalMetadataContent} from '../utils/metadataContent/proposalMetadataContent' -import {generateRegisterDrepMetadataContent} from '../utils/metadataContent/drepMetadataContent' -import {generateVoteMetadataContent} from '../utils/metadataContent/voteMetadataContent' -import {rewardAddressBech32} from '../utils/cardano' +import { AgentWalletDetails } from '../types/types' +import { ManagerInterface } from '../service/ManagerInterfaceService' +import { FunctionGroup, getHandlers } from './AgentFunctions' +import { Builtins, FunctionContext, Key, Wallet } from './BaseFunction' +import { TxListener } from './TxListener' +import { generateProposalMetadataContent } from '../utils/metadataContent/proposalMetadataContent' +import { generateRegisterDrepMetadataContent } from '../utils/metadataContent/drepMetadataContent' +import { generateVoteMetadataContent } from '../utils/metadataContent/voteMetadataContent' +import { rewardAddressBech32 } from '../utils/cardano' export interface CallLog { function: string @@ -22,11 +22,7 @@ export class Executor { readonly functionContext: FunctionContext txListener: TxListener - constructor( - wallet: any, - rpcInterface: ManagerInterface, - txListener: TxListener - ) { + constructor(wallet: any, rpcInterface: ManagerInterface, txListener: TxListener) { this.wallet = wallet this.functions = getHandlers() this.rpcInterface = rpcInterface @@ -49,10 +45,8 @@ export class Executor { getHelpers(wallet: any, agentName: string) { return { - generateProposalMetadataContent: () => - generateProposalMetadataContent(agentName), - generateDrepMetadataContent: () => - generateRegisterDrepMetadataContent(agentName, wallet.address), + generateProposalMetadataContent: () => generateProposalMetadataContent(agentName), + generateDrepMetadataContent: () => generateRegisterDrepMetadataContent(agentName, wallet.address), generateVoteMetadataContent: () => generateVoteMetadataContent(), } } @@ -82,10 +76,7 @@ export class Executor { throw new Error('Key.signRaw is not implemented') }, } - const rewardAddress = rewardAddressBech32( - 0, - walletDetails.stake_verification_key_hash - ) + const rewardAddress = rewardAddressBech32(0, walletDetails.stake_verification_key_hash) console.log( 'Account keys received :', '\n\tAddress: ' + walletDetails.agent_address, @@ -97,11 +88,7 @@ export class Executor { stakeKey: stakeKey, drepId: walletDetails.drep_id, rewardAddress: rewardAddress, - buildAndSubmit: ( - spec: any, - stakeSigning?: boolean, - saveDrepStatus?: boolean - ) => { + buildAndSubmit: (spec: any, stakeSigning?: boolean, saveDrepStatus?: boolean) => { spec.selections = [ walletDetails.agent_address, { @@ -118,40 +105,27 @@ export class Executor { }) } return new Promise((resolve, reject) => { - txSubmissionHold.push({request: spec, resolve, reject}) + txSubmissionHold.push({ request: spec, resolve, reject }) console.log('Queue is : ', txSubmissionHold) if (!isProcessing) { processQueue(this.rpcInterface, this.txListener) } }) - async function processQueue( - rpcInterface: ManagerInterface, - txListener: TxListener - ) { + async function processQueue(rpcInterface: ManagerInterface, txListener: TxListener) { isProcessing = true while (txSubmissionHold.length > 0) { - const {request, resolve, reject} = - txSubmissionHold.shift() + const { request, resolve, reject } = txSubmissionHold.shift() try { - const res = await rpcInterface.buildTx( - request, - true - ) + const res = await rpcInterface.buildTx(request, true) resolve(res) await txListener .addListener(res.hash, 0, 300000) .then(() => { if (saveDrepStatus) { - rpcInterface.checkAndSaveDrepRegistration( - walletDetails.drep_id - ) + rpcInterface.checkAndSaveDrepRegistration(walletDetails.drep_id) } - console.log( - 'Tx matched :', - res.hash, - txSubmissionHold - ) + console.log('Tx matched :', res.hash, txSubmissionHold) }) .catch((e) => { console.error('TXListener Error: ', e) @@ -159,9 +133,7 @@ export class Executor { }) } catch (error) { if (saveDrepStatus) { - rpcInterface.checkAndSaveDrepRegistration( - walletDetails.drep_id - ) + rpcInterface.checkAndSaveDrepRegistration(walletDetails.drep_id) } reject(error) } @@ -176,20 +148,11 @@ export class Executor { } } - remakeContext( - agent_wallet: AgentWalletDetails, - managerInterface: ManagerInterface, - agentName: string - ) { + remakeContext(agent_wallet: AgentWalletDetails, managerInterface: ManagerInterface, agentName: string) { this.functionContext.wallet = this.makeWallet(agent_wallet) - this.functionContext.builtins = this.getBuiltins( - this.functionContext.wallet - ) + this.functionContext.builtins = this.getBuiltins(this.functionContext.wallet) this.functionContext.agentName = agentName - this.functionContext.helpers = this.getHelpers( - this.functionContext.wallet, - agentName - ) + this.functionContext.helpers = this.getHelpers(this.functionContext.wallet, agentName) managerInterface .getFaucetBalance(this.functionContext.wallet.address) .then((balance) => { @@ -203,9 +166,7 @@ export class Executor { .catch((err) => { console.error('GetBalance : ', err) }) - managerInterface.checkAndSaveDrepRegistration( - this.functionContext.wallet.drepId - ) + managerInterface.checkAndSaveDrepRegistration(this.functionContext.wallet.drepId) } makeProxy(context: T): { proxy: T; callLog: CallLog[] } { @@ -267,16 +228,8 @@ export class Executor { updatedBuiltins[key] = (...args: any) => f(context, ...args) }) return { - waitTxConfirmation: async ( - txId: string, - confirmation: number, - timeout: number - ): Promise => { - return await this.txListener.addListener( - txId, - confirmation, - timeout - ) + waitTxConfirmation: async (txId: string, confirmation: number, timeout: number): Promise => { + return await this.txListener.addListener(txId, confirmation, timeout) }, loadFunds: async (amount: number): Promise => { return await this.rpcInterface.loadFunds(wallet.address, amount) @@ -291,7 +244,25 @@ export class Executor { } as Builtins } - invokeFunction(name: string, ...args: any): Promise { + async invokeFunction(name: string, ...args: any): Promise { + return await this.invokeFunctionWithContext({}, name, ...args) + } + + filterFunctionParams(name: string, context: any): Promise { + const f: any | undefined = this.functions.filters[name] + if (f === undefined) { + return Promise.reject(new Error('Filter not defined')) + } + const newContext = { ...this.functionContext, ...context } + try { + const result: any = f(newContext) + return Promise.resolve(result) + } catch (err) { + return Promise.reject(err) + } + } + + invokeFunctionWithContext(context: any, name: string, ...args: any): Promise { const f: any | undefined = this.functions.functions[name] const log: CallLog = { function: name, @@ -302,16 +273,13 @@ export class Executor { return Promise.resolve([log]) } - const newContext = {...this.functionContext} + const newContext = { ...this.functionContext, ...context } const builtinsProxy = this.makeProxy(newContext.builtins) newContext.builtins = builtinsProxy.proxy builtinsProxy.callLog.push(log) try { - const result: any = f( - newContext, - ...args.map((arg: any) => arg.value) - ) + const result: any = f(newContext, ...args.map((arg: any) => arg.value)) if (result instanceof Promise) { return result .then((v) => { diff --git a/agent-node/src/executor/TxListener.ts b/agent-node/src/executor/TxListener.ts index 6a260721b..6f0a009a8 100644 --- a/agent-node/src/executor/TxListener.ts +++ b/agent-node/src/executor/TxListener.ts @@ -12,9 +12,7 @@ interface IPendingTx { block: DecodedBlock | null } -function checkAndResolvePendingTx( - pendingTxs: Record> -) { +function checkAndResolvePendingTx(pendingTxs: Record>) { Object.keys(pendingTxs).forEach((key) => { const txs = pendingTxs[key] txs.map((tx) => { @@ -75,15 +73,11 @@ export class TxListener { return tx.hash.toString('hex') === txId }) }) - if ( - txMatchedIndex + confirmation_count > - this.MAX_BLOCK_COUNT - ) { + if (txMatchedIndex + confirmation_count > this.MAX_BLOCK_COUNT) { this.pendingTxs[txId].push({ resolve, index: 0, - destinationIndex: - confirmation_count - (10 - txMatchedIndex), + destinationIndex: confirmation_count - (10 - txMatchedIndex), block: this.blocks[txMatchedIndex], }) } else { diff --git a/agent-node/src/functions/callWebhook.ts b/agent-node/src/functions/callWebhook.ts index 28c4cfd48..2308205e4 100644 --- a/agent-node/src/functions/callWebhook.ts +++ b/agent-node/src/functions/callWebhook.ts @@ -1,13 +1,6 @@ import { FunctionContext } from '../executor/BaseFunction' import axios from 'axios' -export default async function builtin( - context: FunctionContext, - url: string, - data: Record | any[] | string -) { - return axios.post( - url, - typeof data == 'string' ? data : JSON.stringify(data) - ) +export default async function builtin(context: FunctionContext, url: string, data: Record | any[] | string) { + return axios.post(url, typeof data == 'string' ? data : JSON.stringify(data)) } diff --git a/agent-node/src/functions/createInfoGovAction.ts b/agent-node/src/functions/createInfoGovAction.ts index 3ad64db5c..d49a72f18 100644 --- a/agent-node/src/functions/createInfoGovAction.ts +++ b/agent-node/src/functions/createInfoGovAction.ts @@ -1,16 +1,8 @@ import { FunctionContext } from '../executor/BaseFunction' -export default async function handler( - context: FunctionContext, - anchor: Record -) { - const { dataHash, url } = await context.builtins.saveMetadata( - context.helpers.generateProposalMetadataContent() - ) - const anchorData = - anchor && anchor['url'] && anchor['dataHash'] - ? anchor - : { url, dataHash } +export default async function handler(context: FunctionContext, anchor: Record) { + const { dataHash, url } = await context.builtins.saveMetadata(context.helpers.generateProposalMetadataContent()) + const anchorData = anchor && anchor['url'] && anchor['dataHash'] ? anchor : { url, dataHash } const req = { proposals: [ { diff --git a/agent-node/src/functions/dRepRegistration.ts b/agent-node/src/functions/dRepRegistration.ts index 2b0aecccc..71dc73cdc 100644 --- a/agent-node/src/functions/dRepRegistration.ts +++ b/agent-node/src/functions/dRepRegistration.ts @@ -1,13 +1,8 @@ import { FunctionContext } from '../executor/BaseFunction' export default async function builtin(context: FunctionContext, anchor: any) { - const { dataHash, url } = await context.builtins.saveMetadata( - context.helpers.generateDrepMetadataContent() - ) - const anchorData = - anchor && anchor['url'] && anchor['dataHash'] - ? anchor - : { url, dataHash } + const { dataHash, url } = await context.builtins.saveMetadata(context.helpers.generateDrepMetadataContent()) + const anchorData = anchor && anchor['url'] && anchor['dataHash'] ? anchor : { url, dataHash } const req = { certificates: [ { diff --git a/agent-node/src/functions/delegation.ts b/agent-node/src/functions/delegation.ts index 587a5ecb1..a105cafcc 100644 --- a/agent-node/src/functions/delegation.ts +++ b/agent-node/src/functions/delegation.ts @@ -1,9 +1,6 @@ import { FunctionContext } from '../executor/BaseFunction' -export default async function handler( - context: FunctionContext, - delegation: any -) { +export default async function handler(context: FunctionContext, delegation: any) { let drep = '' if (typeof delegation === 'string') { drep = delegation diff --git a/agent-node/src/functions/noConfidence.ts b/agent-node/src/functions/noConfidence.ts index 46b043fe2..d67307b2a 100644 --- a/agent-node/src/functions/noConfidence.ts +++ b/agent-node/src/functions/noConfidence.ts @@ -1,13 +1,8 @@ import { FunctionContext } from '../executor/BaseFunction' export default async function handler(context: FunctionContext, anchor: any) { - const { dataHash, url } = await context.builtins.saveMetadata( - context.helpers.generateProposalMetadataContent() - ) - const anchorData = - anchor && anchor['url'] && anchor['dataHash'] - ? anchor - : { url, dataHash } + const { dataHash, url } = await context.builtins.saveMetadata(context.helpers.generateProposalMetadataContent()) + const anchorData = anchor && anchor['url'] && anchor['dataHash'] ? anchor : { url, dataHash } const req = { proposals: [ { diff --git a/agent-node/src/functions/proposalNewConstitution.ts b/agent-node/src/functions/proposalNewConstitution.ts index eb53b6c13..48546242b 100644 --- a/agent-node/src/functions/proposalNewConstitution.ts +++ b/agent-node/src/functions/proposalNewConstitution.ts @@ -6,25 +6,17 @@ export default async function handler( newConstitution: any, guardRailScript: any ) { - const { dataHash, url } = await context.builtins.saveMetadata( - context.helpers.generateProposalMetadataContent() - ) - const anchorData = - anchor && anchor['url'] && anchor['dataHash'] - ? anchor - : { url, dataHash } + const { dataHash, url } = await context.builtins.saveMetadata(context.helpers.generateProposalMetadataContent()) + const anchorData = anchor && anchor['url'] && anchor['dataHash'] ? anchor : { url, dataHash } const newConstitutionData = - newConstitution['url'] && newConstitution['dataHash'] - ? newConstitution - : { url, dataHash } + newConstitution['url'] && newConstitution['dataHash'] ? newConstitution : { url, dataHash } const req = { proposals: [ { anchor: anchorData, newconstitution: { ...newConstitutionData, - scriptHash: - 'db1bc3c3f99ce68977ceaf27ab4dd917123ef9e73f85c304236eab23', + scriptHash: 'db1bc3c3f99ce68977ceaf27ab4dd917123ef9e73f85c304236eab23', }, refundAccount: context.wallet.rewardAddress, }, diff --git a/agent-node/src/functions/transferADA.ts b/agent-node/src/functions/transferADA.ts index 27d2c77f3..de606c5b5 100644 --- a/agent-node/src/functions/transferADA.ts +++ b/agent-node/src/functions/transferADA.ts @@ -1,10 +1,6 @@ import { FunctionContext } from '../executor/BaseFunction' -export default async function handler( - context: FunctionContext, - receiverAddress: string, - receivingAda: number -) { +export default async function handler(context: FunctionContext, receiverAddress: string, receivingAda: number) { const req = { outputs: { address: receiverAddress, diff --git a/agent-node/src/functions/treasuryWithdrawal.ts b/agent-node/src/functions/treasuryWithdrawal.ts index dbda5b810..b984355f5 100644 --- a/agent-node/src/functions/treasuryWithdrawal.ts +++ b/agent-node/src/functions/treasuryWithdrawal.ts @@ -6,23 +6,18 @@ export default async function handler( withdrawal: Record, anchor: Record ) { - const { dataHash, url } = await context.builtins.saveMetadata( - context.helpers.generateProposalMetadataContent() - ) + const { dataHash, url } = await context.builtins.saveMetadata(context.helpers.generateProposalMetadataContent()) let updatedWithdrawal = withdrawal if (withdrawal) { const stakeKey = Object.keys(withdrawal)[0] if (stakeKey.startsWith('stake')) { const statkeKeyHex = bech32toHex(stakeKey) updatedWithdrawal = { - ['e0'+statkeKeyHex]: withdrawal[stakeKey], + ['e0' + statkeKeyHex]: withdrawal[stakeKey], } } } - const anchorData = - anchor && anchor['url'] && anchor['dataHash'] - ? anchor - : { url, dataHash } + const anchorData = anchor && anchor['url'] && anchor['dataHash'] ? anchor : { url, dataHash } const req = { proposals: [ { diff --git a/agent-node/src/functions/updateCommittee.ts b/agent-node/src/functions/updateCommittee.ts index bc9832d83..1f731e40e 100644 --- a/agent-node/src/functions/updateCommittee.ts +++ b/agent-node/src/functions/updateCommittee.ts @@ -7,13 +7,8 @@ export default async function handler( add: Record, remove: Array ) { - const { dataHash, url } = await context.builtins.saveMetadata( - context.helpers.generateProposalMetadataContent() - ) - const anchorData = - anchor && anchor['url'] && anchor['dataHash'] - ? anchor - : { url, dataHash } + const { dataHash, url } = await context.builtins.saveMetadata(context.helpers.generateProposalMetadataContent()) + const anchorData = anchor && anchor['url'] && anchor['dataHash'] ? anchor : { url, dataHash } const req = { proposals: [ { diff --git a/agent-node/src/functions/voteOnProposal.ts b/agent-node/src/functions/voteOnProposal.ts index 204b9e4b3..9b278fa66 100644 --- a/agent-node/src/functions/voteOnProposal.ts +++ b/agent-node/src/functions/voteOnProposal.ts @@ -3,24 +3,29 @@ import { FunctionContext } from '../executor/BaseFunction' export default async function handler( context: FunctionContext, proposal: Record, - anchor: Record + anchor: Record, + voteType: 'yes' | 'no' | 'abstain' ) { - const { dataHash, url } = await context.builtins.saveMetadata( - context.helpers.generateVoteMetadataContent() - ) - const anchorData = - anchor && anchor['url'] && anchor['dataHash'] - ? anchor - : { url, dataHash } + const { dataHash, url } = await context.builtins.saveMetadata(context.helpers.generateVoteMetadataContent()) + const anchorData = anchor && anchor['url'] && anchor['dataHash'] ? anchor : { url, dataHash } const req = { - vote: { - voter: context.wallet.drepId, - role: 'drep', - proposal: proposal, - vote: true, - anchor: anchorData, - }, + vote: Array.isArray(proposal) + ? proposal.map((individualProposal) => ({ + voter: context.wallet.drepId, + role: 'drep', + proposal: individualProposal, + vote: voteType, + anchor: anchorData, + })) + : { + voter: context.wallet.drepId, + role: 'drep', + proposal: proposal, + vote: voteType, + anchor: anchorData, + }, } + return await context.wallet .buildAndSubmit(req, true) .then((v) => v) @@ -38,3 +43,37 @@ export default async function handler( } }) } + +export function filter(context: FunctionContext) { + if (!context.filter || !context.event) return null + const tx = context.event.tx + try { + const filters = context.filter['proposalProcedures'] || context.filter['tx']['proposalProcedures'] + const voteTypeParam = context.parameters?.find((param) => param.name === 'voteType') + const matchedFilterIndexes = filters.filter((i: any) => 'matchedIndex' in i) + const ProposalIds = matchedFilterIndexes.length + ? matchedFilterIndexes.map((p: any) => `${tx.hash.toString('hex')}#${p.matchedIndex ? p.matchedIndex : 0}`) + : null + return [ + { + name: 'proposalIds', + value: ProposalIds, + }, + { + name: 'anchor', + value: {}, + }, + { + name: 'voteType', + value: voteTypeParam && voteTypeParam.value ? voteTypeParam.value : 'yes', + }, + ] + } catch (err) { + if (!context.filter['proposalProcedures'] || !context.filter['tx']['proposalProcedures']) { + console.error('VoteOnProposalFilterError : ' + 'proposalProcedure does not exist') + } else { + console.error('VoteOnProposalFilterError : ', err) + } + return null + } +} diff --git a/agent-node/src/index.ts b/agent-node/src/index.ts index 56aea19b1..64f843f20 100644 --- a/agent-node/src/index.ts +++ b/agent-node/src/index.ts @@ -56,9 +56,7 @@ function connectToManagerWebSocket() { const scheduledTasks: ScheduledTask[] = [] ws = new WebSocket(`${wsUrl}/${agentSecret}`) const clientPipe = new WsClientPipe(ws) - const rpcChannel = new AgentRpc( - new CborDuplex(clientPipe, cborxBackend(true)) - ) + const rpcChannel = new AgentRpc(new CborDuplex(clientPipe, cborxBackend(true))) const managerInterface = new ManagerInterface(rpcChannel) const txListener = new TxListener() @@ -101,9 +99,7 @@ function connectToManagerWebSocket() { attemptReconnect() clearInterval(interval) } - console.log( - `Disconnected from the server (code: ${code}, reason: ${reason}).` - ) + console.log(`Disconnected from the server (code: ${code}, reason: ${reason}).`) }) ws.on('error', (err: any) => { @@ -129,17 +125,12 @@ function attemptReconnect() { connectToManagerWebSocket() isReconnecting = false if (reconnectAttempts <= maxReconnectAttempts) { - console.log( - `Attempting to reconnect... (${reconnectAttempts}/${maxReconnectAttempts})` - ) + console.log(`Attempting to reconnect... (${reconnectAttempts}/${maxReconnectAttempts})`) } }, 10000) - maxReconnectAttempts >= reconnectAttempts && - console.log('Waiting for 10 seconds before reconnecting') + maxReconnectAttempts >= reconnectAttempts && console.log('Waiting for 10 seconds before reconnecting') } else { - console.error( - 'Max reconnect attempts reached. Exiting application.' - ) + console.error('Max reconnect attempts reached. Exiting application.') process.exit(1) } } diff --git a/agent-node/src/service/AgentRpc.ts b/agent-node/src/service/AgentRpc.ts index a877d028d..eefc4f639 100644 --- a/agent-node/src/service/AgentRpc.ts +++ b/agent-node/src/service/AgentRpc.ts @@ -27,10 +27,7 @@ export class AgentRpc extends RpcV1 { handleMethodCall(method: string, args: any[]) { this.handlers['methodCall'](method, args) } - on( - eventType: 'methodCall', - handler: (method: string, args: any[]) => any - ): void + on(eventType: 'methodCall', handler: (method: string, args: any[]) => any): void on(eventType: 'event', handler: (topic: string, message: any) => void): void on(eventType: AgentEventType, handler: any): void { this.handlers[eventType] = handler diff --git a/agent-node/src/service/EventTriggerHandler.ts b/agent-node/src/service/EventTriggerHandler.ts new file mode 100644 index 000000000..2f0de4a35 --- /dev/null +++ b/agent-node/src/service/EventTriggerHandler.ts @@ -0,0 +1,157 @@ +import { ManagerInterface } from './ManagerInterfaceService' +import { Transaction } from 'libcardano/cardano/ledger-serialization/transaction' +import { AgentRunner } from '../executor/AgentRunner' +import { compareValue } from '../utils/validator' +import { IBooleanNode, IEventBasedAction, IFieldNode, IFilterNode } from '../types/eventTriger' +import { DecodedBlock } from '../executor/TxListener' +import { reconstructTxFromPaths } from '../utils/event/eventFilterFormatter' + +export class EventTriggerHandler { + eventBasedActions: IEventBasedAction[] = [] + managerInterface: ManagerInterface + + constructor(managerInterface: ManagerInterface) { + this.managerInterface = managerInterface + } + + onBlock(block: DecodedBlock, agentRunners: AgentRunner[]) { + if (this.eventBasedActions.length) { + block.body.forEach((tx: Transaction) => { + this.eventBasedActions.forEach((eventBasedAction) => { + const handler = (this as any)['transactionHandler'] + if (handler !== undefined && handler !== 'constructor') { + handler.bind(this)(tx, eventBasedAction, block, agentRunners) + } + }) + }) + } + } + + transactionHandler( + tx: Transaction, + eventBasedAction: IEventBasedAction, + block: DecodedBlock, + agentRunners?: AgentRunner[] + ) { + let result + const matchedTxPath: any = [] + try { + result = this.solveNode( + { tx: tx.body, transaction: tx.body }, + eventBasedAction.eventTrigger, + [], + matchedTxPath + ) + console.debug('tx=', tx.hash.toString('hex'), 'solution=', result) + } catch (e) { + console.error('Error handling event', e) + return + } + + const matchedEventContext = reconstructTxFromPaths({ tx: tx.body }, matchedTxPath) + + const { function_name, parameters } = eventBasedAction.triggeringFunction + if (result && agentRunners) { + const eventContext = { + tx, + block, + confirmation: 1, + } + agentRunners.forEach((runner, index) => { + runner.invokeFunctionWithEventContext( + matchedEventContext, + eventContext, + 'EVENT', + index, + function_name, + parameters + ) + }) + } + } + + solveNode(targetObject: any, filterNode: IFilterNode, parentNodes: string[], matchedTxPath: any) { + return this.solveNodeInternal(targetObject, filterNode, filterNode.id, parentNodes, matchedTxPath) + } + + solveNodeInternal( + targetObject: any, + filterNode: IFilterNode, + nodes: string[] | string | undefined, + parent_nodes: string[], + matchedTxPath: any + ): boolean { + if (nodes === undefined || nodes.length == 0) { + const result = + 'children' in filterNode + ? this.solveBooleanNode(targetObject, filterNode, parent_nodes, matchedTxPath) + : this.solveFieldNode(targetObject, filterNode, parent_nodes, matchedTxPath) + return filterNode.negate ? !result : result + } else if (typeof nodes === 'string') { + nodes = [nodes] + } + + if (targetObject == null && filterNode.operator === 'exists') { + return false + } + + let result + let propertyValue = targetObject[nodes[0]] + if (!propertyValue) { + propertyValue = targetObject[nodes[0] + 's'] + if (!propertyValue) return false + } + parent_nodes.push(nodes[0]) + + const subArray = nodes.slice(1) + if (Array.isArray(propertyValue)) { + const node_pos = parent_nodes.length + parent_nodes.push('') + result = propertyValue.reduce((acc, node, index: number) => { + if (acc) { + return acc + } + parent_nodes[node_pos] = index.toString() + const result = this.solveNodeInternal(node, filterNode, subArray, parent_nodes, matchedTxPath) + console.log(parent_nodes.join('.'), ': result=', result) + return result + }, false) + parent_nodes.pop() + } else { + result = this.solveNodeInternal(propertyValue, filterNode, subArray, parent_nodes, matchedTxPath) + } + parent_nodes.pop() + return result + } + + solveBooleanNode( + targetObject: any, + filterNode: IBooleanNode, + parent_nodes: string[] = [], + matchedTxPath: any + ): boolean { + const orOperator = (a: boolean, b: boolean) => a || b + let operator = orOperator + if (filterNode.operator === 'AND') { + operator = (a: boolean, b: boolean) => a && b + } + const result = filterNode.children.reduce((acc, node) => { + return operator(acc, this.solveNodeInternal(targetObject, node, node.id, parent_nodes, matchedTxPath)) + }, operator !== orOperator) + console.log('id=', filterNode.id, 'operator=', filterNode.operator, 'result=', result) + return result + } + + solveFieldNode(targetObject: any, filterNode: IFieldNode, parent_nodes: string[], matchedTxPath: any): boolean { + const result = compareValue(filterNode.operator, filterNode.value, targetObject, parent_nodes) + if (result) { + const matchKey = parent_nodes.join('.') || 'root' + matchedTxPath.push(matchKey) + } + return result + } + + addEventActions(actions: IEventBasedAction[]) { + this.eventBasedActions = actions + } +} diff --git a/agent-node/src/service/KuberService.ts b/agent-node/src/service/KuberService.ts index 5a7ade341..83cf1aa28 100644 --- a/agent-node/src/service/KuberService.ts +++ b/agent-node/src/service/KuberService.ts @@ -1,11 +1,6 @@ import { bech32 } from 'bech32' -type CertificateType = - | 'registerstake' - | 'registerdrep' - | 'deregisterdrep' - | 'delegate' - | 'deregisterstake' +type CertificateType = 'registerstake' | 'registerdrep' | 'deregisterdrep' | 'delegate' | 'deregisterstake' export type TxSubmitResponse = { cborHex: string @@ -28,11 +23,7 @@ export class Kuber { } static generateCert(type: CertificateType, key: string, dRep: string = '') { - if ( - type === 'registerstake' || - type === 'deregisterdrep' || - type === 'deregisterstake' - ) { + if (type === 'registerstake' || type === 'deregisterdrep' || type === 'deregisterstake') { return { type: type, key: key, @@ -43,8 +34,7 @@ export class Kuber { key: key, anchor: { url: 'https://bit.ly/3zCH2HL', - dataHash: - '1111111111111111111111111111111111111111111111111111111111111111', + dataHash: '1111111111111111111111111111111111111111111111111111111111111111', }, } } else if (type === 'delegate' && dRep) { @@ -91,12 +81,6 @@ export class Kuber { rewardAddressBech32(networkId: number, stakevkh: string): string { const prefix = networkId == 0 ? 'stake_test' : 'stake' - return bech32.encode( - prefix, - bech32.toWords( - Buffer.from(this.rewardAddressRawBytes(networkId, stakevkh)) - ), - 200 - ) + return bech32.encode(prefix, bech32.toWords(Buffer.from(this.rewardAddressRawBytes(networkId, stakevkh))), 200) } } diff --git a/agent-node/src/service/ManagerInterfaceService.ts b/agent-node/src/service/ManagerInterfaceService.ts index 1bccdf48b..99d12f2ad 100644 --- a/agent-node/src/service/ManagerInterfaceService.ts +++ b/agent-node/src/service/ManagerInterfaceService.ts @@ -1,5 +1,5 @@ -import {RpcV1} from 'libcardano/network/Rpc' -import {ILog} from './TriggerActionHandler' +import { RpcV1 } from 'libcardano/network/Rpc' +import { ILog } from './TriggerActionHandler' export class ManagerInterface { rpc: RpcV1 diff --git a/agent-node/src/service/RpcTopicHandler.ts b/agent-node/src/service/RpcTopicHandler.ts index 1603ad50b..1898a397d 100644 --- a/agent-node/src/service/RpcTopicHandler.ts +++ b/agent-node/src/service/RpcTopicHandler.ts @@ -2,22 +2,24 @@ import { ManagerInterface } from './ManagerInterfaceService' import { TxListener } from '../executor/TxListener' import { BlockEvent } from 'libcardano/types' import { parseRawBlockBody } from 'libcardano/cardano/ledger-serialization/transaction' -import { globalState } from '../constants/global' import { clearScheduledTasks, scheduleFunctions } from '../utils/scheduler' -import { - checkIfAgentWithEventTriggerTypeExists, - createActionDtoForEventTrigger, -} from '../utils/agent' +import { checkIfAgentWithEventTriggerTypeExists } from '../utils/agent' import { ScheduledTask } from 'node-cron' import { AgentRunner } from '../executor/AgentRunner' +import { EventTriggerHandler } from './EventTriggerHandler' +import { formatEventFilter } from '../utils/event/eventFilterFormatter' export class RpcTopicHandler { managerInterface: ManagerInterface txListener: TxListener + eventTriggerHandlers: EventTriggerHandler + constructor(managerInterface: ManagerInterface, txListener: TxListener) { this.managerInterface = managerInterface this.txListener = txListener + this.eventTriggerHandlers = new EventTriggerHandler(this.managerInterface) } + handleEvent( eventName: string, message: any, @@ -42,52 +44,21 @@ export class RpcTopicHandler { 'txCount=' + transactions.length ) this.txListener.onBlock({ ...block, body: transactions }) - if ( - globalState.eventTriggerTypeDetails.eventType && - transactions.length - ) { - transactions.forEach((tx: any) => { - if (Array.isArray(tx.body.proposalProcedures)) { - tx.body.proposalProcedures.forEach( - (proposal: any, index: number) => { - const { function_name, parameters } = - createActionDtoForEventTrigger(tx, index) - agentRunners.forEach((runner, index) => { - runner.invokeFunction( - 'EVENT', - index, - function_name, - ...(parameters as any) - ) - }) - } - ) - } - }) - } + this.eventTriggerHandlers.onBlock({ ...block, body: transactions }, agentRunners) } - initial_config( - message: any, - agentRunners: Array, - scheduledTasks: ScheduledTask[] - ) { + + initial_config(message: any, agentRunners: Array, scheduledTasks: ScheduledTask[]) { const { configurations } = message - checkIfAgentWithEventTriggerTypeExists(configurations) + const eventBasedActions = formatEventFilter(checkIfAgentWithEventTriggerTypeExists(configurations)) + if (eventBasedActions) { + this.eventTriggerHandlers.addEventActions(eventBasedActions) + } agentRunners.forEach((runner, index) => { - scheduleFunctions( - this.managerInterface, - runner, - configurations, - index, - scheduledTasks - ) + scheduleFunctions(this.managerInterface, runner, configurations, index, scheduledTasks) }) } - config_updated( - message: any, - agentRunners: Array, - scheduledTasks: ScheduledTask[] - ) { + + config_updated(message: any, agentRunners: Array, scheduledTasks: ScheduledTask[]) { const { instanceCount, configurations } = message if (instanceCount != agentRunners.length) { if (instanceCount < agentRunners.length) { @@ -97,28 +68,23 @@ export class RpcTopicHandler { Array(increasedRunner) .fill('') .forEach(async (item, index) => { - const runner = new AgentRunner( - this.managerInterface, - this.txListener - ) + const runner = new AgentRunner(this.managerInterface, this.txListener) await runner.remakeContext(agentRunners.length + index) agentRunners.push(runner) }) } } - checkIfAgentWithEventTriggerTypeExists(configurations) + const eventBasedActions = formatEventFilter(checkIfAgentWithEventTriggerTypeExists(configurations)) + if (eventBasedActions) { + this.eventTriggerHandlers.addEventActions(eventBasedActions) + } clearScheduledTasks(scheduledTasks) agentRunners.forEach((runner, index) => { - scheduleFunctions( - this.managerInterface, - runner, - configurations, - index, - scheduledTasks - ) + scheduleFunctions(this.managerInterface, runner, configurations, index, scheduledTasks) }) } agent_keys(message: any) {} + instance_count(args: any) {} } diff --git a/agent-node/src/service/TriggerActionHandler.ts b/agent-node/src/service/TriggerActionHandler.ts index 4656ae158..7ef6bdf8d 100644 --- a/agent-node/src/service/TriggerActionHandler.ts +++ b/agent-node/src/service/TriggerActionHandler.ts @@ -48,10 +48,7 @@ export class TriggerActionHandler { } setTimeOut() { - console.log( - 'Timout initialized of 80 second... , TriggerQueue ', - this.triggerQueue - ) + console.log('Timout initialized of 80 second... , TriggerQueue ', this.triggerQueue) this.timeOut = setTimeout(() => { if (this.triggerQueue.length) { this.triggerQueue = removeRedundantTrigger(this.triggerQueue) @@ -88,13 +85,7 @@ export class TriggerActionHandler { success: true, instanceIndex: 0, } - triggerAction( - this, - this.managerInterface, - action['function_name'], - action['parameters'], - triggerType - ) + triggerAction(this, this.managerInterface, action['function_name'], action['parameters'], triggerType) .then((res) => { console.log('Tx Hash of res is : ', res) if (res) { @@ -133,8 +124,7 @@ function removeRedundantTrigger(triggerQueue: Array) { const secondTrigger = triggerQueue.at(1) if ( firstTrigger?.triggerType === secondTrigger?.triggerType && - firstTrigger?.action.function_name === - secondTrigger?.action.function_name + firstTrigger?.action.function_name === secondTrigger?.action.function_name ) { triggerQueue.shift() } diff --git a/agent-node/src/service/WsClientPipe.ts b/agent-node/src/service/WsClientPipe.ts index d8fd221a7..b178d0718 100644 --- a/agent-node/src/service/WsClientPipe.ts +++ b/agent-node/src/service/WsClientPipe.ts @@ -18,10 +18,7 @@ export class WsClientPipe extends Pipe { }) } - write( - chunk: any, - cb?: ((error?: Error | undefined) => void) | undefined - ): boolean { + write(chunk: any, cb?: ((error?: Error | undefined) => void) | undefined): boolean { this.ws.send(chunk, { binary: true }, cb) return true } diff --git a/agent-node/src/service/transactionBuilder.ts b/agent-node/src/service/transactionBuilder.ts index a76e1e5d0..0352b835e 100644 --- a/agent-node/src/service/transactionBuilder.ts +++ b/agent-node/src/service/transactionBuilder.ts @@ -9,21 +9,15 @@ export class AgentTransactionBuilder { constructor(agentWalletDetails: AgentWalletDetails) { this.agentWalletDetails = agentWalletDetails - this.kuber = new Kuber( - this.agentWalletDetails.agent_address, - this.agentWalletDetails.payment_signing_key - ) + this.kuber = new Kuber(this.agentWalletDetails.agent_address, this.agentWalletDetails.payment_signing_key) } public static setInstance(agentWalletDetails: AgentWalletDetails) { - AgentTransactionBuilder.agentTxBuilderInstance = - new AgentTransactionBuilder(agentWalletDetails) + AgentTransactionBuilder.agentTxBuilderInstance = new AgentTransactionBuilder(agentWalletDetails) } public static getInstance() { - return AgentTransactionBuilder.agentTxBuilderInstance - ? AgentTransactionBuilder.agentTxBuilderInstance - : null + return AgentTransactionBuilder.agentTxBuilderInstance ? AgentTransactionBuilder.agentTxBuilderInstance : null } transferADA(receiverAddress: string, ADA = 10) { @@ -39,12 +33,7 @@ export class AgentTransactionBuilder { dRepRegistration() { const req = { inputs: this.agentWalletDetails.agent_address, - certificates: [ - Kuber.generateCert( - 'registerdrep', - this.agentWalletDetails.stake_verification_key_hash - ), - ], + certificates: [Kuber.generateCert('registerdrep', this.agentWalletDetails.stake_verification_key_hash)], } return this.kuber.signTx(req, this.agentWalletDetails.stake_signing_key) } @@ -52,12 +41,7 @@ export class AgentTransactionBuilder { dRepDeRegistration() { const req = { inputs: this.agentWalletDetails.agent_address, - certificates: [ - Kuber.generateCert( - 'deregisterdrep', - this.agentWalletDetails.stake_verification_key_hash - ), - ], + certificates: [Kuber.generateCert('deregisterdrep', this.agentWalletDetails.stake_verification_key_hash)], } return this.kuber.signTx(req, this.agentWalletDetails.stake_signing_key) } @@ -65,28 +49,19 @@ export class AgentTransactionBuilder { stakeDelegation(drepId: string) { const req = { certificates: [ - Kuber.generateCert( - 'delegate', - this.agentWalletDetails.payment_verification_key_hash, - drepId - ), + Kuber.generateCert('delegate', this.agentWalletDetails.payment_verification_key_hash, drepId), ], } return this.kuber.signTx(req, this.agentWalletDetails.stake_signing_key) } noConfidence(anchorUrl: string, anchorDataHash: string) { - const rewardAddress = this.kuber.rewardAddressBech32( - 0, - this.agentWalletDetails.stake_verification_key_hash - ) + const rewardAddress = this.kuber.rewardAddressBech32(0, this.agentWalletDetails.stake_verification_key_hash) const infoProposal = { refundAccount: rewardAddress, anchor: { url: anchorUrl || 'https://bit.ly/3zCH2HL', - dataHash: - anchorDataHash || - '1111111111111111111111111111111111111111111111111111111111111111', + dataHash: anchorDataHash || '1111111111111111111111111111111111111111111111111111111111111111', }, } return this.kuber.signTx({ @@ -94,22 +69,13 @@ export class AgentTransactionBuilder { }) } - treasuryWithdrawal( - anchorUrl: string, - anchorDataHash: string, - withdrawal: Record - ) { - const rewardAddress = this.kuber.rewardAddressBech32( - 0, - this.agentWalletDetails.stake_verification_key_hash - ) + treasuryWithdrawal(anchorUrl: string, anchorDataHash: string, withdrawal: Record) { + const rewardAddress = this.kuber.rewardAddressBech32(0, this.agentWalletDetails.stake_verification_key_hash) const infoProposal = { refundAccount: rewardAddress, anchor: { url: anchorUrl || 'https://bit.ly/3zCH2HL', - dataHash: - anchorDataHash || - '1111111111111111111111111111111111111111111111111111111111111111', + dataHash: anchorDataHash || '1111111111111111111111111111111111111111111111111111111111111111', }, withdraw: withdrawal, } @@ -126,17 +92,12 @@ export class AgentTransactionBuilder { addingCommittee: Record, removingCommittee: Array ) { - const rewardAddress = this.kuber.rewardAddressBech32( - 0, - this.agentWalletDetails.stake_verification_key_hash - ) + const rewardAddress = this.kuber.rewardAddressBech32(0, this.agentWalletDetails.stake_verification_key_hash) const infoProposal = { refundAccount: rewardAddress, anchor: { url: anchorUrl || 'https://bit.ly/3zCH2HL', - dataHash: - anchorDataHash || - '1111111111111111111111111111111111111111111111111111111111111111', + dataHash: anchorDataHash || '1111111111111111111111111111111111111111111111111111111111111111', }, updatecommittee: { add: addingCommittee, @@ -159,10 +120,7 @@ export class AgentTransactionBuilder { newConstitutionDataHash: string, guardRailScript: string | undefined ) { - const rewardAddress = this.kuber.rewardAddressBech32( - 0, - this.agentWalletDetails.stake_verification_key_hash - ) + const rewardAddress = this.kuber.rewardAddressBech32(0, this.agentWalletDetails.stake_verification_key_hash) const req = { proposals: [ { @@ -184,12 +142,7 @@ export class AgentTransactionBuilder { registerStake() { const req = { - certificates: [ - Kuber.generateCert( - 'registerstake', - this.agentWalletDetails.stake_verification_key_hash - ), - ], + certificates: [Kuber.generateCert('registerstake', this.agentWalletDetails.stake_verification_key_hash)], } return this.kuber.signTx(req, this.agentWalletDetails.stake_signing_key) } @@ -202,12 +155,7 @@ export class AgentTransactionBuilder { value: '10A', addChange: true, }, - certificates: [ - Kuber.generateCert( - 'deregisterstake', - this.agentWalletDetails.stake_verification_key_hash - ), - ], + certificates: [Kuber.generateCert('deregisterstake', this.agentWalletDetails.stake_verification_key_hash)], } return this.kuber.signTx(req, this.agentWalletDetails.stake_signing_key) } @@ -224,9 +172,7 @@ export class AgentTransactionBuilder { }, anchor: { url: anchorUrl || 'https://bit.ly/3zCH2HL', - dataHash: - anchorDataHash || - '1111111111111111111111111111111111111111111111111111111111111111', + dataHash: anchorDataHash || '1111111111111111111111111111111111111111111111111111111111111111', }, } return this.kuber.signTx({ @@ -255,11 +201,7 @@ export class AgentTransactionBuilder { abstainDelegation() { const req = { certificates: [ - Kuber.generateCert( - 'delegate', - this.agentWalletDetails.stake_verification_key_hash, - 'abstain' - ), + Kuber.generateCert('delegate', this.agentWalletDetails.stake_verification_key_hash, 'abstain'), ], } return this.kuber.signTx(req, this.agentWalletDetails.stake_signing_key) diff --git a/agent-node/src/service/triggerService.ts b/agent-node/src/service/triggerService.ts index af64e34a1..d49c87cb2 100644 --- a/agent-node/src/service/triggerService.ts +++ b/agent-node/src/service/triggerService.ts @@ -34,12 +34,7 @@ function mergeObjectsValueIntoSingleObject( ) { const objectsMap = new Map() values.forEach((val) => - objectsMap.set( - val[recordString1], - recordString2Type === 'number' - ? +val[recordString2] - : val[recordString2] - ) + objectsMap.set(val[recordString1], recordString2Type === 'number' ? +val[recordString2] : val[recordString2]) ) return Object.fromEntries(objectsMap) } @@ -87,15 +82,10 @@ export async function triggerAction( throw err }) case 'stakeDelegation': - body = transactionBuilder.stakeDelegation( - getParameterValue(parameters, 'drep') || 'abstain' - ) + body = transactionBuilder.stakeDelegation(getParameterValue(parameters, 'drep') || 'abstain') return managerInterface.buildTx(body, true).catch((err: Error) => { if (err && err.message.includes('StakeKeyNotRegisteredDELEG')) { - triggerHandler.setTriggerOnQueue( - { function_name: 'registerStake', parameters: [] }, - 'INTERNAL' - ) + triggerHandler.setTriggerOnQueue({ function_name: 'registerStake', parameters: [] }, 'INTERNAL') triggerHandler.setTriggerOnQueue( { function_name: 'stakeDelegation', @@ -113,19 +103,12 @@ export async function triggerAction( anchor = getParameterValue(parameters, 'anchor') || [] anchorUrl = getParameterValue(anchor, 'url') || '' anchorDataHash = getParameterValue(anchor, 'dataHash') || '' - body = transactionBuilder.voteOnProposal( - proposal, - anchorUrl, - anchorDataHash - ) + body = transactionBuilder.voteOnProposal(proposal, anchorUrl, anchorDataHash) return managerInterface.buildTx(body, true).catch((err: Error) => { if (err && err.message.includes('GovActionsDoNotExist')) { throw new Error('Governance Action Proposal doesnot exist') } else if (err && err.message.includes('VotersDoNotExist')) { - triggerHandler.setTriggerOnQueue( - { function_name: 'dRepRegistration', parameters: [] }, - 'INTERNAL' - ) + triggerHandler.setTriggerOnQueue({ function_name: 'dRepRegistration', parameters: [] }, 'INTERNAL') triggerHandler.setTriggerOnQueue( { function_name: 'voteOnProposal', @@ -150,15 +133,10 @@ export async function triggerAction( anchor = getParameterValue(parameters, 'anchor') || [] anchorUrl = getParameterValue(anchor, 'url') anchorDataHash = getParameterValue(anchor, 'dataHash') - newConstitution = - getParameterValue(parameters, 'newConstitution') || [] + newConstitution = getParameterValue(parameters, 'newConstitution') || [] newConstitutionUrl = getParameterValue(newConstitution, 'url') - newConstitutionDataHAsh = getParameterValue( - newConstitution, - 'dataHash' - ) - guardRailScript = - getParameterValue(parameters, 'guardrailScript') || undefined + newConstitutionDataHAsh = getParameterValue(newConstitution, 'dataHash') + guardRailScript = getParameterValue(parameters, 'guardrailScript') || undefined body = transactionBuilder.proposalNewConstitution( anchorUrl, anchorDataHash, @@ -189,11 +167,7 @@ export async function triggerAction( 'amount', 'number' ) - body = transactionBuilder.treasuryWithdrawal( - anchorUrl, - anchorDataHash, - withdrawal - ) + body = transactionBuilder.treasuryWithdrawal(anchorUrl, anchorDataHash, withdrawal) return managerInterface.buildTx(body, true).catch((err: Error) => { throw err }) @@ -226,10 +200,7 @@ export async function triggerAction( case 'dRepRegistration': body = transactionBuilder.dRepRegistration() return managerInterface.buildTx(body, true).catch((err: Error) => { - if ( - err && - err.message.includes('ConwayDRepAlreadyRegistered') - ) { + if (err && err.message.includes('ConwayDRepAlreadyRegistered')) { throw new Error(`Drep is already registered`) } else { throw err @@ -256,10 +227,7 @@ export async function triggerAction( case 'stakeDeRegistration': body = transactionBuilder.stakeDeRegistration() return managerInterface.buildTx(body, true).catch((err: Error) => { - if ( - err && - err.message.includes('Stake address is not registered') - ) { + if (err && err.message.includes('Stake address is not registered')) { throw new Error('Stake Address is not registered.') } else { throw err diff --git a/agent-node/src/types/eventTriger.ts b/agent-node/src/types/eventTriger.ts new file mode 100644 index 000000000..3c31e2594 --- /dev/null +++ b/agent-node/src/types/eventTriger.ts @@ -0,0 +1,25 @@ +import { Action } from '../service/triggerService' + +export type BooleanOperator = 'AND' | 'OR' +export type ComparisonOperator = 'equals' | 'greaterThan' | 'lessThan' | 'in' | 'exists' + +export interface IEventBasedAction { + eventTrigger: IBooleanNode | IFieldNode + triggeringFunction: Action +} + +export interface IFieldNode { + id: string | string[] + value: any + negate: boolean + operator: ComparisonOperator +} + +export interface IBooleanNode { + id?: string | string[] + children: IFilterNode[] + negate: boolean + operator: BooleanOperator +} + +export type IFilterNode = IFieldNode | IBooleanNode diff --git a/agent-node/src/types/types.ts b/agent-node/src/types/types.ts index a96dce239..7b7c9ceff 100644 --- a/agent-node/src/types/types.ts +++ b/agent-node/src/types/types.ts @@ -6,8 +6,3 @@ export type AgentWalletDetails = { payment_verification_key_hash: string drep_id: string } - -export type EventTriggerTypeDetails = { - eventType: boolean - function_name: string -} diff --git a/agent-node/src/utils/agent.ts b/agent-node/src/utils/agent.ts index e164696b0..9ac3fa389 100644 --- a/agent-node/src/utils/agent.ts +++ b/agent-node/src/utils/agent.ts @@ -1,39 +1,31 @@ -import { - Action, - ActionParameter, - Configuration, - TriggerType, -} from '../service/triggerService' -import { globalState } from '../constants/global' +import { Action, ActionParameter, Configuration, TriggerType } from '../service/triggerService' import { ManagerInterface } from '../service/ManagerInterfaceService' import { CallLog } from '../executor/Executor' import { ILog, InternalLog } from '../service/TriggerActionHandler' import { DateTime } from 'luxon' +import { IEventBasedAction, IFilterNode } from '../types/eventTriger' -export function getParameterValue( - parameters: ActionParameter[] = [], - name: string -): any { +export function getParameterValue(parameters: ActionParameter[] = [], name: string): any { const param = parameters.find((param) => param && param.name === name) return param ? param.value : '' } -export function checkIfAgentWithEventTriggerTypeExists( - configurations: Configuration[] -) { +export function checkIfAgentWithEventTriggerTypeExists(configurations: Configuration[]) { + const eventBasedAction: IEventBasedAction[] = [] configurations.forEach((config) => { if (config.type === 'EVENT') { - globalState.eventTriggerTypeDetails = { - eventType: true, - function_name: config.action.function_name, - } + eventBasedAction.push({ + eventTrigger: config.data as unknown as IFilterNode, + triggeringFunction: config.action, + }) } }) + return eventBasedAction.flat() } export function createActionDtoForEventTrigger(tx: any, index: number): Action { return { - function_name: globalState.eventTriggerTypeDetails.function_name, + function_name: 'voteOnProposal', parameters: [ { name: 'proposal', @@ -67,9 +59,7 @@ export function saveTxLog( txLog.txHash = mainLog.return.hash } else if (mainLog.error) { txLog.result = mainLog.error - txLog.message = - mainLog.error && - ((mainLog.error as Error).message ?? mainLog.error) + txLog.message = mainLog.error && ((mainLog.error as Error).message ?? mainLog.error) txLog.success = false } callLogs.length && @@ -85,8 +75,7 @@ export function saveTxLog( internalLog.txHash = log.return.hash } else if (log.error) { internalLog.result = log.error - internalLog.message = - log.error && (log.error.message ?? log.error) + internalLog.message = log.error && (log.error.message ?? log.error) internalLog.success = false } internalLog.timeStamp = DateTime.utc().toISO() diff --git a/agent-node/src/utils/cardano.ts b/agent-node/src/utils/cardano.ts index c84c4feca..2f87e9b12 100644 --- a/agent-node/src/utils/cardano.ts +++ b/agent-node/src/utils/cardano.ts @@ -13,16 +13,9 @@ export function rewardAddressRawBytes(network: number, stakevkh: string) { return result } -export function rewardAddressBech32( - networkId: number, - stakevkh: string -): string { +export function rewardAddressBech32(networkId: number, stakevkh: string): string { const prefix = networkId == 0 ? 'stake_test' : 'stake' - return bech32.encode( - prefix, - bech32.toWords(Buffer.from(rewardAddressRawBytes(networkId, stakevkh))), - 200 - ) + return bech32.encode(prefix, bech32.toWords(Buffer.from(rewardAddressRawBytes(networkId, stakevkh))), 200) } export function loadRootKeyFromBuffer() { diff --git a/agent-node/src/utils/event/eventFilterFormatter.ts b/agent-node/src/utils/event/eventFilterFormatter.ts new file mode 100644 index 000000000..8101e7c2f --- /dev/null +++ b/agent-node/src/utils/event/eventFilterFormatter.ts @@ -0,0 +1,112 @@ +import { IBooleanNode, IEventBasedAction } from '../../types/eventTriger' +import { customReplacer } from '../validator' + +function flattenIds(obj: any) { + const ids: any = [] + + function flattenChildren(node: any) { + if (node.id) { + ids.push(node.id) // Push id to ids array + } + + if (Array.isArray(node.children)) { + let flatChildren: any = [] + for (const child of node.children) { + if (child.children) { + flatChildren = flatChildren.concat(flattenChildren(child)) + } else { + flatChildren.push(child) + } + } + return flatChildren + } + return [] + } + + const flatChildren = flattenChildren(obj) + + return { + id: ids, + children: flatChildren, + negate: obj.negate, + operator: obj.operator, + } +} + +function flattenFilterChildWithParentId(data: IBooleanNode) { + data.children = data.children.map((child) => { + if ('children' in child && child.children.length) { + const similarChild = child.children.find((c) => c.id === child.id) + if (similarChild) { + return similarChild + } + return child + } + return child + }) + return data +} + +export function formatEventFilter(data: IEventBasedAction[]) { + return data.map((item) => { + return { + ...item, + eventTrigger: { + ...('children' in item.eventTrigger + ? item.eventTrigger.children.length == 1 + ? flattenIds(flattenFilterChildWithParentId(item.eventTrigger)) + : flattenFilterChildWithParentId(item.eventTrigger) + : item.eventTrigger), + }, + } + }) +} + +export function reconstructTxFromPaths(tx: any, matchingPaths: any) { + const newTx: any = {} + + matchingPaths.forEach((path: any) => { + const keys = path.split('.') + let source = tx + const target = newTx + + keys.forEach((key: any, index: number) => { + const isLastKey = index === keys.length - 1 + const arrayMatch = key.match(/^(\d+)$/) // Check if it's an array index + + if (arrayMatch) { + // Handle array index + const arrayIndex = parseInt(keys[index]) // Previous key is the array name + const targetArrayId = keys[index - 1] + + if (!Array.isArray(target[targetArrayId])) { + target[targetArrayId] = [] + } + + source = source[arrayIndex] + target[targetArrayId] = [ + ...target[targetArrayId].filter((o: any) => !!o.matchedIndex), + { ...source, matchedIndex: arrayIndex }, + ] + } else { + // Regular key handling + if (isLastKey) { + if (typeof source[key] === 'object' && source[key] !== null) { + target[key] = JSON.parse(JSON.stringify(source[key], customReplacer, 2)) // Deep copy + } else { + target[key] = source[key] + } + } else { + if (!source[key]) { + source = source[key + 's'] + } else { + source = source[key] + } + target[key] = source + } + } + }) + }) + + return newTx +} diff --git a/agent-node/src/utils/metadataContent/drepMetadataContent.ts b/agent-node/src/utils/metadataContent/drepMetadataContent.ts index a4f882bb9..5a9c8c4d5 100644 --- a/agent-node/src/utils/metadataContent/drepMetadataContent.ts +++ b/agent-node/src/utils/metadataContent/drepMetadataContent.ts @@ -1,7 +1,4 @@ -export function generateRegisterDrepMetadataContent( - drepName: string, - paymentAddress: string -) { +export function generateRegisterDrepMetadataContent(drepName: string, paymentAddress: string) { return JSON.stringify({ '@context': { '@language': 'en-us', @@ -15,8 +12,7 @@ export function generateRegisterDrepMetadataContent( '@id': 'CIP119:references', '@container': '@set', '@context': { - GovernanceMetadata: - 'CIP100:GovernanceMetadataReference', + GovernanceMetadata: 'CIP100:GovernanceMetadataReference', Identity: 'CIP100:IdentityReference', Link: 'CIP100:LinkReference', Other: 'CIP100:OtherReference', @@ -60,8 +56,7 @@ export function generateRegisterDrepMetadataContent( hashAlgorithm: 'blake2b-256', body: { givenName: `${drepName}`, - motivations: - 'This Drep is generated automatically by autonomous-agent-testing', + motivations: 'This Drep is generated automatically by autonomous-agent-testing', objectives: 'This is the automatically generated DRep for testing.', paymentAddress: `${paymentAddress}`, qualifications: 'Known for running test in cardano blockchain.', diff --git a/agent-node/src/utils/metadataContent/proposalMetadataContent.ts b/agent-node/src/utils/metadataContent/proposalMetadataContent.ts index 47a3df856..40820adc6 100644 --- a/agent-node/src/utils/metadataContent/proposalMetadataContent.ts +++ b/agent-node/src/utils/metadataContent/proposalMetadataContent.ts @@ -12,8 +12,7 @@ export function generateProposalMetadataContent(agentName: string) { '@id': 'CIP108:references', '@container': '@set', '@context': { - GovernanceMetadata: - 'CIP100:GovernanceMetadataReference', + GovernanceMetadata: 'CIP100:GovernanceMetadataReference', Other: 'CIP100:OtherReference', label: 'CIP100:reference-label', uri: 'CIP100:reference-uri', @@ -52,8 +51,7 @@ export function generateProposalMetadataContent(agentName: string) { hashAlgorithm: 'blake2b-256', body: { abstract: `This proposal is created automatically by agent: ${agentName}`, - motivation: - 'This proposal is automatically generated by autonomous-agent-testing.', + motivation: 'This proposal is automatically generated by autonomous-agent-testing.', references: [ { '@type': 'Other', @@ -62,8 +60,7 @@ export function generateProposalMetadataContent(agentName: string) { }, ], title: `Proposal By Agent ${agentName}`, - rationale: - 'This proposal was created as a part of testing for autonomous-agent-testing', + rationale: 'This proposal was created as a part of testing for autonomous-agent-testing', }, }) } diff --git a/agent-node/src/utils/metadataContent/voteMetadataContent.ts b/agent-node/src/utils/metadataContent/voteMetadataContent.ts index 13b2b0429..3df04d7a9 100644 --- a/agent-node/src/utils/metadataContent/voteMetadataContent.ts +++ b/agent-node/src/utils/metadataContent/voteMetadataContent.ts @@ -11,8 +11,7 @@ export function generateVoteMetadataContent() { '@id': 'CIP100:references', '@container': '@set', '@context': { - GovernanceMetadata: - 'CIP100:GovernanceMetadataReference', + GovernanceMetadata: 'CIP100:GovernanceMetadataReference', Other: 'CIP100:OtherReference', label: 'CIP100:reference-label', uri: 'CIP100:reference-uri', @@ -53,8 +52,7 @@ export function generateVoteMetadataContent() { }, authors: [], body: { - comment: - 'This is the automated vote generated by autonomous-agent-testing', + comment: 'This is the automated vote generated by autonomous-agent-testing', }, hashAlgorithm: 'blake2b-256', }) diff --git a/agent-node/src/utils/operatorSupport.ts b/agent-node/src/utils/operatorSupport.ts new file mode 100644 index 000000000..a5c418363 --- /dev/null +++ b/agent-node/src/utils/operatorSupport.ts @@ -0,0 +1,175 @@ +import { ShelleyAddress, Value } from 'libcardano' + +type TypeHandler = (a: any, b: any) => boolean + +const operatorNameToSymbol: Record = { + // Text-based comparison operators + greaterthan: '>', + greaterthanorequal: '>=', + lessthan: '<', + lessthanorequal: '<=', + equal: '==', + equals: '==', + notequal: '!=', + notequals: '!=', + + // Shorthand comparison operators + gt: '>', + gte: '>=', + lt: '<', + lte: '<=', + ne: '!=', + eq: '==', + + // js suport + contains: 'contains', +} + +class LogicalFunctions { + private typeHandlers: Map> + + constructor() { + // Initialize a map to hold handlers for different types/classes + this.typeHandlers = new Map() + } + + // Register operator for a specific class or type (e.g., Person, Animal, Number, etc.) + register(type: string, operator: string, handler: TypeHandler): void { + if (!this.typeHandlers.has(type)) { + this.typeHandlers.set(type, {}) + } + const typeRegistry = this.typeHandlers.get(type)! + + if (typeRegistry[operator]) { + throw new Error(`Operator "${operator}" for type "${type}" is already registered.`) + } + + typeRegistry[operator] = handler + } + + // Execute the logical operation, checking the types/classes of the operands + execute(operator: string, a: any, b: any): boolean { + if (operator === 'exists') { + return !!a + } + + const aClass = a.constructor ? a.constructor.name : typeof a + const bClass = b.constructor ? b.constructor.name : typeof b + + if (operatorNameToSymbol[operator.toLowerCase()]) { + operator = operatorNameToSymbol[operator.toLowerCase()] + } + // Try class-specific operator registration first + if (this.typeHandlers.has(aClass)) { + const aHandlers = this.typeHandlers.get(aClass)! + if (aHandlers[operator]) { + if (aClass === 'buffer' && !Buffer.isBuffer(b)) { + const convertedBuffer = Buffer.from(b) + return aHandlers[operator](a, convertedBuffer) + } else if (aClass === 'Value' && bClass !== 'Value') { + const convertedTypeValueOperand = a.constructor.fromString(b) + return aHandlers[operator](a, convertedTypeValueOperand) + } else if (aClass === 'ShelleyAddress' && bClass !== 'ShelleyAddress') { + const convertedTypeValueOperand = a.constructor.fromAny(b) + return aHandlers[operator](a, convertedTypeValueOperand) + } + return aHandlers[operator](a, b) + } + } + + // If class-specific handler not found, check for type (primitive) operators + if (this.typeHandlers.has(aClass.toLowerCase())) { + const aHandlers = this.typeHandlers.get(aClass.toLowerCase())! + if (aHandlers[operator]) { + if (aClass.toLowerCase() === 'buffer' && !Buffer.isBuffer(b)) { + const convertedBuffer = Buffer.from(b, 'hex') + return aHandlers[operator](a, convertedBuffer) + } else if (aClass === 'Value' && bClass !== 'Value') { + const convertedTypeValueOperand = a.constructor.fromString(b) + return aHandlers[operator](a, convertedTypeValueOperand) + } else if (aClass === 'ShelleyAddress' && bClass !== 'ShelleyAddress') { + const convertedTypeValueOperand = a.constructor.fromAny(b) + return aHandlers[operator](a, convertedTypeValueOperand) + } + return aHandlers[operator](a, b) + } + } + + // Try class-specific operator registration for the second operand + if (this.typeHandlers.has(bClass)) { + const bHandlers = this.typeHandlers.get(bClass)! + if (bHandlers[operator]) { + return bHandlers[operator](a, b) + } + } + + // check for static method fromString() + if (a.constructor.fromString && a.constructor.compare) { + try { + const convertedOperandB = a.constructor.fromString(b) + return a.constructor.compare(convertedOperandB) + } catch (err) { + console.error(`Operand B of type ${bClass} could not be converted to class ${aClass}`) + return false + } + } + + // check for static method fromAny() + if (a.constructor.fromAny && a.constructor.compare) { + try { + const convertedOperandB = a.constructor.fromAny(b) + return a.constructor.compare(convertedOperandB) + } catch (err) { + console.error(`Operand B of type ${bClass} could not be converted to class ${aClass}`) + return false + } + } + + console.log('typeHandlers', JSON.stringify(Array.from(this.typeHandlers.keys()), undefined, 2)) + console.log('typeHandlersvalues', JSON.stringify(Array.from(this.typeHandlers.values()), undefined, 2)) + + // If operator is not found for the types of operands, throw an error + throw new Error(`Operator "${operator}" is not supported for operands of types "${aClass}" and "${bClass}".`) + } +} + +// Initialize the _logicalFunctions instance +const _logicalFunctions = new LogicalFunctions() + +// Register handlers for numbers +_logicalFunctions.register('number', '==', (a: number, b: number) => a == b) +_logicalFunctions.register('number', '!=', (a: number, b: number) => a !== b) +_logicalFunctions.register('number', '>', (a: number, b: number) => a > b) +_logicalFunctions.register('number', '<', (a: number, b: number) => a < b) +_logicalFunctions.register('number', '>=', (a: number, b: number) => a >= b) +_logicalFunctions.register('number', '<=', (a: number, b: number) => a <= b) + +// Register handlers for strings +_logicalFunctions.register('string', '==', (a: string, b: string) => a == b) +_logicalFunctions.register('string', '!=', (a: string, b: string) => a !== b) +_logicalFunctions.register('string', '>', (a: string, b: string) => a > b) +_logicalFunctions.register('string', '<', (a: string, b: string) => a < b) +_logicalFunctions.register('string', '>=', (a: string, b: string) => a >= b) +_logicalFunctions.register('string', '<=', (a: string, b: string) => a <= b) +_logicalFunctions.register('string', 'contains', (a: string, b: string) => a.includes(b) || b.includes(a)) + +// Register handlers for buffers +_logicalFunctions.register('buffer', '==', (a: Buffer, b: Buffer) => a.equals(b)) +_logicalFunctions.register('buffer', '!=', (a: Buffer, b: Buffer) => !a.equals(b)) +_logicalFunctions.register('buffer', '>', (a: Buffer, b: Buffer) => a.compare(b) > 0) +_logicalFunctions.register('buffer', '<', (a: Buffer, b: Buffer) => a.compare(b) < 0) +_logicalFunctions.register('buffer', '>=', (a: Buffer, b: Buffer) => a.compare(b) >= 0) +_logicalFunctions.register('buffer', '<=', (a: Buffer, b: Buffer) => a.compare(b) <= 0) + +// Register handlers for type Value +_logicalFunctions.register('Value', '==', (a: Value, b: Value) => a.equals(b)) +_logicalFunctions.register('Value', '!=', (a: Value, b: Value) => !a.equals(b)) +_logicalFunctions.register('Value', '>', (a: Value, b: Value) => a.greaterThan(b)) +_logicalFunctions.register('Value', '<', (a: Value, b: Value) => a.lessThan(b)) +_logicalFunctions.register('Value', '>=', (a: Value, b: Value) => a.greaterThanOrEqualsTo(b)) +_logicalFunctions.register('Value', '<=', (a: Value, b: Value) => a.lessThanOrEqualsTo(b)) + +// Register handlers for type ShelleyAddress +_logicalFunctions.register('ShelleyAddress', '==', (a: ShelleyAddress, b: ShelleyAddress) => a.equals(b)) + +export const logicalFunctions = _logicalFunctions diff --git a/agent-node/src/utils/scheduler.ts b/agent-node/src/utils/scheduler.ts index 664c81cc7..140e32aea 100644 --- a/agent-node/src/utils/scheduler.ts +++ b/agent-node/src/utils/scheduler.ts @@ -31,12 +31,7 @@ function createTask( instanceIndex: instanceIndex, }) } else { - agentRunner.invokeFunction( - 'CRON', - instanceIndex, - action.function_name, - ...(action.parameters as any) - ) + agentRunner.invokeFunction('CRON', instanceIndex, action.function_name, ...(action.parameters as any)) } }, { @@ -56,14 +51,7 @@ export function scheduleFunctions( const { data, action, type } = config if (action && type === 'CRON') { const { frequency, probability } = data - const task = createTask( - runner, - manager, - action, - frequency, - probability, - instanceIndex - ) + const task = createTask(runner, manager, action as Action, frequency, probability, instanceIndex) scheduledTasks.push(task) } scheduledTasks.forEach((task) => task.start()) diff --git a/agent-node/src/utils/validator.ts b/agent-node/src/utils/validator.ts index 32d9c31e9..fbee26e78 100644 --- a/agent-node/src/utils/validator.ts +++ b/agent-node/src/utils/validator.ts @@ -1,3 +1,6 @@ +import { BooleanOperator, ComparisonOperator } from '../types/eventTriger' +import { logicalFunctions } from './operatorSupport' + const NetworkName = ['preview', 'preprod', 'sanchonet'] export function validateToken(token: string) { @@ -7,7 +10,43 @@ export function validateToken(token: string) { if (token.split('_')[1].includes('undefined')) { return 'Not a valid token. Missing secret key' } - if (!NetworkName.includes(token.split('_')[0])) - return 'Not a valid network name' + if (!NetworkName.includes(token.split('_')[0])) return 'Not a valid network name' return '' } + +export function reduceBooleanArray(bools: Array, reducer: BooleanOperator, negate: boolean): boolean { + let reducedBooleanLogic + if (reducer === 'AND') { + reducedBooleanLogic = bools.reduce((acc, bool) => acc && bool, true) + } else if (reducer === 'OR') { + reducedBooleanLogic = bools.reduce((acc, bool) => acc || bool, false) + } + return negate ? !reducedBooleanLogic : !!reducedBooleanLogic +} + +export function compareValue( + operator: ComparisonOperator, + objectProperty: any, + txPropertyVal: any, + property_path: string[] +) { + console.debug('filterValue=' + objectProperty + ', txPropertyVal= ' + txPropertyVal) + const result = logicalFunctions.execute(operator, txPropertyVal, objectProperty) + + if (txPropertyVal.constructor.name == 'Buffer') { + console.debug( + `compareValue[${property_path.join('.')}] (${operator},0x${txPropertyVal.toString('hex')}, ${objectProperty}) = ${result}` + ) + } else + console.debug( + `compareValue[${property_path.join('.')}] (${operator},${txPropertyVal}, ${objectProperty}) = ${result}` + ) + return result +} + +export function customReplacer(key: any, value: any) { + if (typeof value === 'bigint') { + return value.toString() // Convert BigInt to string + } + return value // Return the value unchanged for other types +} diff --git a/agent-node/yarn.lock b/agent-node/yarn.lock index d10466de9..d334ef003 100644 --- a/agent-node/yarn.lock +++ b/agent-node/yarn.lock @@ -51,11 +51,6 @@ resolved "https://registry.yarnpkg.com/@emurgo/cardano-serialization-lib-asmjs/-/cardano-serialization-lib-asmjs-11.5.0.tgz#0c71fc8fedce8ae8304180d40dfb5ca76f76e8f0" integrity sha512-QQkavjEug/EmBFg02bmSg0eLYiOaRa1lmRG8q6fGRkx3O9BX1iZQDZJmirPDBTkTO3CpNB0q9se8DQFFcjREIw== -"@emurgo/cardano-serialization-lib-nodejs@^11.5.0": - version "11.5.0" - resolved "https://registry.yarnpkg.com/@emurgo/cardano-serialization-lib-nodejs/-/cardano-serialization-lib-nodejs-11.5.0.tgz#0662e2a17d7d1e944f8cdb86396133c8edaec059" - integrity sha512-IlVABlRgo9XaTR1NunwZpWcxnfEv04ba2l1vkUz4S1W7Jt36F4CtffP+jPeqBZGnAe+fnUwo0XjIJC3ZTNToNQ== - "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -623,11 +618,6 @@ ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ansi-colors@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" - integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== - ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -655,14 +645,6 @@ ansi-styles@^6.1.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== -anymatch@~3.1.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" - integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -714,11 +696,6 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -assertion-error@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" - integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== - async@^2.6.2: version "2.6.4" resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" @@ -765,7 +742,7 @@ bin-links@^4.0.1: read-cmd-shim "^4.0.0" write-file-atomic "^5.0.0" -binary-extensions@^2.0.0, binary-extensions@^2.2.0: +binary-extensions@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== @@ -844,18 +821,13 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.3, braces@~3.0.2: +braces@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: fill-range "^7.1.1" -browser-stdout@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" - integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== - buffer-alloc-unsafe@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" @@ -892,9 +864,9 @@ buffers@~0.1.1: integrity sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ== bufferutil@^4.0.1: - version "4.0.8" - resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.8.tgz#1de6a71092d65d7766c4d8a522b261a6e787e8ea" - integrity sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw== + version "4.0.9" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.9.tgz#6e81739ad48a95cad45a279588e13e95e24a800a" + integrity sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw== dependencies: node-gyp-build "^4.3.0" @@ -945,11 +917,6 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camelcase@^6.0.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - cbor@^9.0.0: version "9.0.2" resolved "https://registry.yarnpkg.com/cbor/-/cbor-9.0.2.tgz#536b4f2d544411e70ec2b19a2453f10f83cd9fdb" @@ -957,17 +924,6 @@ cbor@^9.0.0: dependencies: nofilter "^3.1.0" -chai@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/chai/-/chai-5.1.1.tgz#f035d9792a22b481ead1c65908d14bb62ec1c82c" - integrity sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA== - dependencies: - assertion-error "^2.0.1" - check-error "^2.1.1" - deep-eql "^5.0.1" - loupe "^3.1.0" - pathval "^2.0.0" - chainsaw@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" @@ -975,7 +931,7 @@ chainsaw@~0.1.0: dependencies: traverse ">=0.3.0 <0.4" -chalk@^4.0.0, chalk@^4.1.0: +chalk@^4.0.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -988,26 +944,6 @@ chalk@^5.3.0: resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== -check-error@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc" - integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw== - -chokidar@^3.5.3: - version "3.6.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" - integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - chownr@^1.0.1: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" @@ -1060,15 +996,6 @@ cli-table3@^0.6.3: optionalDependencies: "@colors/colors" "1.5.0" -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" @@ -1186,7 +1113,7 @@ d@1, d@^1.0.1, d@^1.0.2: es5-ext "^0.10.64" type "^2.7.2" -debug@4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5: +debug@4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.7" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== @@ -1200,11 +1127,6 @@ debug@^2.1.3, debug@^2.2.0: dependencies: ms "2.0.0" -decamelize@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" - integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== - decompress-response@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" @@ -1212,11 +1134,6 @@ decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" -deep-eql@^5.0.1: - version "5.0.2" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" - integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q== - deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -1259,7 +1176,7 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -diff@^5.1.0, diff@^5.2.0: +diff@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== @@ -1349,11 +1266,6 @@ es6-symbol@^3.1.1, es6-symbol@^3.1.3: d "^1.0.2" ext "^1.7.0" -escalade@^3.1.1: - version "3.2.0" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" - integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== - escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" @@ -1563,11 +1475,6 @@ flat-cache@^3.0.4: keyv "^4.5.3" rimraf "^3.0.2" -flat@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" - integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== - flatted@^3.2.9: version "3.3.1" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" @@ -1619,16 +1526,6 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fs@^0.0.1-security: - version "0.0.1-security" - resolved "https://registry.yarnpkg.com/fs/-/fs-0.0.1-security.tgz#8a7bd37186b6dddf3813f23858b57ecaaf5e41d4" - integrity sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w== - -fsevents@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - function-bind@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" @@ -1676,22 +1573,12 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-func-name@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" - integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== - github-from-package@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== -glob-parent@^5.1.2, glob-parent@~5.1.2: +glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -1729,7 +1616,7 @@ glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.0.1, glob@^8.1.0: +glob@^8.0.1: version "8.1.0" resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== @@ -1800,11 +1687,6 @@ hasown@^2.0.2: dependencies: function-bind "^1.1.2" -he@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - hosted-git-info@^6.0.0, hosted-git-info@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-6.1.1.tgz#629442c7889a69c05de604d52996b74fe6f26d58" @@ -1937,13 +1819,6 @@ ip-regex@^4.1.0: resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - is-cidr@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/is-cidr/-/is-cidr-4.0.2.tgz#94c7585e4c6c77ceabf920f8cde51b8c0fda8814" @@ -1975,7 +1850,7 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -1997,21 +1872,11 @@ is-path-inside@^3.0.3: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== -is-plain-obj@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== - is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -2145,13 +2010,12 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -libcardano@1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/libcardano/-/libcardano-1.4.2.tgz#5ef6ae1ff1e1b44812e33c4a720ae2ffd816a3d0" - integrity sha512-+ubt/FI1Cks9AN5LfhgirbzEMTOxGJw6AFkBLTLsFvl+0iTDLihOrTQALtqE8m8dInanXcJE2OnsxtZ7VlB42w== +libcardano@1.4.19: + version "1.4.19" + resolved "https://registry.yarnpkg.com/libcardano/-/libcardano-1.4.19.tgz#9859f65ca9dd40c2ba0b5a13b525efbd03f0143c" + integrity sha512-xe4/2I1rDiYekUv09qWNgNbdmlTUJrQvgqBnQ2DlrmoU5jHp97pizWatL2U2Js1C+FibWqUXIT2h+kIvh4WPLA== dependencies: "@cardano-sdk/crypto" "^0.1.30" - "@emurgo/cardano-serialization-lib-nodejs" "^11.5.0" "@noble/curves" "^1.6.0" "@prisma/client" "4.15.0" bech32 "^2.0.0" @@ -2159,12 +2023,9 @@ libcardano@1.4.2: blake2b "^2.1.4" blakejs "^1.2.1" cbor "^9.0.0" - chai "^5.1.1" - fs "^0.0.1-security" kafka "^0.2.3" kafka-node "^5.0.0" kafkajs "^2.2.4" - mocha "^10.5.2" prisma "^4.15.0" readline "^1.3.0" tsconfig-paths "^4.2.0" @@ -2312,26 +2173,11 @@ lodash@^4.17.14, lodash@^4.17.21, lodash@^4.17.4: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - long@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/long/-/long-1.1.2.tgz#eaef5951ca7551d96926b82da242db9d6b28fb53" integrity sha512-pjR3OP1X2VVQhCQlrq3s8UxugQsuoucwMOn9Yj/kN/61HMc+lDFJS5bvpNEHneZ9NVaSm8gNWxZvtGS7lqHb3Q== -loupe@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.1.tgz#71d038d59007d890e3247c5db97c1ec5a92edc54" - integrity sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw== - dependencies: - get-func-name "^2.0.1" - lru-cache@^10.2.0: version "10.4.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" @@ -2441,7 +2287,7 @@ minimatch@^3.0.2, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -minimatch@^5.0.1, minimatch@^5.1.6: +minimatch@^5.0.1: version "5.1.6" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== @@ -2555,32 +2401,6 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mocha@^10.5.2: - version "10.7.3" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.7.3.tgz#ae32003cabbd52b59aece17846056a68eb4b0752" - integrity sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A== - dependencies: - ansi-colors "^4.1.3" - browser-stdout "^1.3.1" - chokidar "^3.5.3" - debug "^4.3.5" - diff "^5.2.0" - escape-string-regexp "^4.0.0" - find-up "^5.0.0" - glob "^8.1.0" - he "^1.2.0" - js-yaml "^4.1.0" - log-symbols "^4.1.0" - minimatch "^5.1.6" - ms "^2.1.3" - serialize-javascript "^6.0.2" - strip-json-comments "^3.1.1" - supports-color "^8.1.1" - workerpool "^6.5.1" - yargs "^16.2.0" - yargs-parser "^20.2.9" - yargs-unparser "^2.0.0" - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -2597,9 +2417,9 @@ mute-stream@^1.0.0, mute-stream@~1.0.0: integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== nan@^2.14.1: - version "2.20.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.20.0.tgz#08c5ea813dd54ed16e5bd6505bf42af4f7838ca3" - integrity sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw== + version "2.22.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.0.tgz#31bc433fc33213c97bad36404bb68063de604de3" + integrity sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw== nanoassert@^2.0.0: version "2.0.0" @@ -2646,9 +2466,9 @@ node-cron@^3.0.3: uuid "8.3.2" node-gyp-build@^4.3.0: - version "4.8.2" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.2.tgz#4f802b71c1ab2ca16af830e6c1ea7dd1ad9496fa" - integrity sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw== + version "4.8.4" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.4.tgz#8a70ee85464ae52327772a90d66c6077a900cfc8" + integrity sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ== node-gyp@^9.0.0, node-gyp@^9.4.1: version "9.4.1" @@ -2701,11 +2521,6 @@ normalize-package-data@^5.0.0: semver "^7.3.5" validate-npm-package-license "^3.0.4" -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - npm-audit-report@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/npm-audit-report/-/npm-audit-report-5.0.0.tgz#83ac14aeff249484bde81eff53c3771d5048cf95" @@ -3022,11 +2837,6 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -pathval@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25" - integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA== - pbkdf2@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" @@ -3038,7 +2848,7 @@ pbkdf2@^3.1.2: safe-buffer "^5.0.1" sha.js "^2.4.8" -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: +picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -3166,13 +2976,6 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -3242,23 +3045,11 @@ readable-stream@^3.5.0, readable-stream@^3.6.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - readline@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/readline/-/readline-1.3.0.tgz#c580d77ef2cfc8752b132498060dc9793a7ac01c" integrity sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg== -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== - resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -3301,7 +3092,7 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: +safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -3333,13 +3124,6 @@ serialize-error@^8: dependencies: type-fest "^0.20.2" -serialize-javascript@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" - integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== - dependencies: - randombytes "^2.1.0" - set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -3578,13 +3362,6 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-color@^8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - supports-color@^9.4.0: version "9.4.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.4.0.tgz#17bfcf686288f531db3dea3215510621ccb55954" @@ -3894,12 +3671,7 @@ word-wrap@^1.2.5: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -workerpool@^6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" - integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== - -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -3940,11 +3712,6 @@ xtend@^4.0.0: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - yaeti@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" @@ -3955,34 +3722,6 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yargs-parser@^20.2.2, yargs-parser@^20.2.9: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - -yargs-unparser@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" - integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== - dependencies: - camelcase "^6.0.0" - decamelize "^4.0.0" - flat "^5.0.2" - is-plain-obj "^2.1.0" - -yargs@^16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" diff --git a/api/backend/app/models/trigger/resposne_dto.py b/api/backend/app/models/trigger/resposne_dto.py index 81ed708b5..5f072d734 100644 --- a/api/backend/app/models/trigger/resposne_dto.py +++ b/api/backend/app/models/trigger/resposne_dto.py @@ -1,10 +1,10 @@ +from typing import Union, Optional, Any + from pydantic import BaseModel -from typing import Union, Optional from backend.app.models.trigger.trigger_dto import ( Action, CronTriggerDTO, - EventTriggerDTO, ) @@ -12,8 +12,8 @@ class TriggerResponse(BaseModel): id: str agent_id: str type: str - action: Optional[Action] = None - data: Optional[Union[CronTriggerDTO, EventTriggerDTO]] = None + action: Optional[Action | Any] = None + data: Optional[Union[CronTriggerDTO, Any]] = None # class TriggerResponse_agent_id(BaseModel): diff --git a/api/backend/app/models/trigger/trigger_dto.py b/api/backend/app/models/trigger/trigger_dto.py index f3dfca54e..3a43bcd41 100644 --- a/api/backend/app/models/trigger/trigger_dto.py +++ b/api/backend/app/models/trigger/trigger_dto.py @@ -1,8 +1,6 @@ -from typing import Union, Optional, Any -from pydantic import BaseModel, json -from croniter import croniter +from typing import Union, Optional, Any, Literal -from backend.app.exceptions import HTTPException +from pydantic import BaseModel class CronTriggerDTO(BaseModel): @@ -15,9 +13,9 @@ class SubParameter(BaseModel): value: Any -class EventTriggerDTO(BaseModel): - event: str - parameters: Optional[list[SubParameter]] +# class EventTriggerDTO(BaseModel): +# event: str +# parameters: Optional[list[SubParameter]] class Action(BaseModel): @@ -25,28 +23,44 @@ class Action(BaseModel): parameters: list[SubParameter] -class TriggerCreateDTO(BaseModel): - type: str - action: Optional[Action] = None - data: Optional[Union[CronTriggerDTO, EventTriggerDTO]] = None +# For Event Based Trigger + +BooleanOperator = Literal["AND", "OR"] +ComparisonOperator = Literal[ + "equals", "greaterthan", "lessthan", "in", "gt", "gte", "lt", "lte", "ne", "eq", "contains", "exists" +] + + +class Field(BaseModel): + id: Union[str, list[str]] + value: Any + negate: bool + operator: ComparisonOperator + operators: list[ComparisonOperator] + +class ChildrenFields(BaseModel): + id: Optional[Union[str, list[str]]] + children: list["FilterNode"] + negate: bool + operator: BooleanOperator -# validation for cron expression -async def validate_type_CRON(cron_expression: str, probability: float): - try: - croniter(cron_expression) - except ValueError as e: - raise HTTPException(400, f"Invalid CRON expression") - # Validate probability - if probability < 0 or probability > 1: - raise HTTPException(400, "Probability must be between 0 and 1 (inclusive)") +# FilterNode - recursive model +FilterNode = Union[Field, ChildrenFields] -# validation for Topic -async def validate_type_EVENT(value: str): - try: - if value.isnumeric(): - raise HTTPException(400, f"Invalid topic :") - except ValueError as e: - return f"Validation error: {e}" +class EventTriggerDTO(BaseModel): + id: Optional[Union[str, list[str]]] + children: Optional[list[FilterNode]] + negate: Optional[bool] + operator: Optional[BooleanOperator] + + +ChildrenFields.update_forward_refs() # To allow model to reference another model that has not been defined yet + + +class TriggerCreateDTO(BaseModel): + type: str + action: Optional[Action] = None + data: Optional[Union["CronTriggerDTO", "EventTriggerDTO"]] = None diff --git a/api/backend/app/models/trigger/utils.py b/api/backend/app/models/trigger/utils.py new file mode 100644 index 000000000..dd306fe85 --- /dev/null +++ b/api/backend/app/models/trigger/utils.py @@ -0,0 +1,22 @@ +from backend.app.models.trigger.trigger_dto import EventTriggerDTO +from backend.app.exceptions import HTTPException +from croniter import croniter + + +def validate_type_EVENT(data: any): + try: + EventTriggerDTO.parse_raw(data.json()) + except Exception as e: + raise HTTPException( + status_code=402, content="Unprocessable Entity , Data is not an intatnce of Event Trigger Dto" + ) + + +def validate_type_CRON(cron_expression: str, probability: float): + try: + croniter(cron_expression) + except ValueError as e: + raise HTTPException(400, f"Invalid CRON expression") + # Validate probability + if probability < 0 or probability > 1: + raise HTTPException(400, "Probability must be between 0 and 1 (inclusive)") diff --git a/api/backend/app/repositories/template_trigger_repository.py b/api/backend/app/repositories/template_trigger_repository.py index 85c22a277..4641cacd7 100644 --- a/api/backend/app/repositories/template_trigger_repository.py +++ b/api/backend/app/repositories/template_trigger_repository.py @@ -8,15 +8,14 @@ from prisma import Prisma from backend.app.models import ( - validate_type_CRON, CronTriggerDTO, EventTriggerDTO, TemplateTriggerResponse, TemplateTriggerCreateDto, TriggerCreateDTO, - validate_type_EVENT, ) from backend.config.database import prisma_connection +from backend.app.models.trigger.utils import validate_type_EVENT, validate_type_CRON class TemplateTriggerRepository: @@ -28,9 +27,9 @@ async def save_template_trigger(self, transaction: Prisma, template_id: str, tem template_data_dict = template_data.dict() if template_data.type == "CRON": - await validate_type_CRON(template_data.data.frequency, template_data.data.probability) + validate_type_CRON(template_data.data.frequency, template_data.data.probability) elif template_data.type == "EVENT": - await validate_type_EVENT(template_data.data.event) + validate_type_EVENT(template_data.data.event) else: raise HTTPException(status_code=400, content=f"Invalid Trigger Type {template_data.type}") diff --git a/api/backend/app/repositories/trigger_repository.py b/api/backend/app/repositories/trigger_repository.py index f5fc6fa34..a25d9b4d4 100644 --- a/api/backend/app/repositories/trigger_repository.py +++ b/api/backend/app/repositories/trigger_repository.py @@ -9,10 +9,11 @@ TriggerCreateDTO, CronTriggerDTO, EventTriggerDTO, - validate_type_CRON, - validate_type_EVENT, ) from backend.config.database import prisma_connection +from backend.app.models.trigger.trigger_dto import EventTriggerDTO +from backend.config.logger import logger +from backend.app.models.trigger.utils import validate_type_CRON, validate_type_EVENT class TriggerRepository: @@ -21,9 +22,9 @@ def __init__(self, db_connection=None): async def save_trigger(self, agent_id: str, trigger_data: TriggerCreateDTO): if trigger_data.type == "CRON": - await validate_type_CRON(trigger_data.data.frequency, trigger_data.data.probability) + validate_type_CRON(trigger_data.data.frequency, trigger_data.data.probability) elif trigger_data.type == "EVENT": - await validate_type_EVENT(trigger_data.data.event) + validate_type_EVENT(trigger_data.data) else: raise HTTPException(status_code=400, content=f"Invalid Trigger Type {trigger_data.type}") trigger_data_dict = trigger_data.dict() @@ -93,10 +94,10 @@ async def modify_trigger_by_id(self, trigger_id: str, trigger_data: TriggerCreat # validation for CRON nad TOPIC if trigger_data.type == "CRON": - await validate_type_CRON(trigger_data.data.frequency, trigger_data.data.probability) + validate_type_CRON(trigger_data.data.frequency, trigger_data.data.probability) if trigger_data.type == "EVENT": - await validate_type_EVENT(trigger_data.data.event) + validate_type_EVENT(trigger_data.data) # for config data data_dict = updated_data_dict.pop("data") diff --git a/dbsync-api/.prettierrc.json b/dbsync-api/.prettierrc.json new file mode 100644 index 000000000..4e534aad2 --- /dev/null +++ b/dbsync-api/.prettierrc.json @@ -0,0 +1,7 @@ +{ + "trailingComma": "es5", + "tabWidth": 4, + "semi": false, + "singleQuote": true, + "printWidth": 120 +} diff --git a/dbsync-api/src/repository/address.ts b/dbsync-api/src/repository/address.ts index 608f8a960..fb19420ad 100644 --- a/dbsync-api/src/repository/address.ts +++ b/dbsync-api/src/repository/address.ts @@ -11,9 +11,4 @@ export const fetchFaucetBalance= async (address:string)=>{ GROUP BY stake_address.hash_raw ` as Array> return result.length?+result[0].total_value:0 -} - -// UTXO SUM -// + REWARD_REST -// + REWARD -// (aLSO MAKE SURE THAT IT IS NOT ALREADY WITHEDRAWAN BY SEEING THE WITHDRAWALS TABLE) \ No newline at end of file +} \ No newline at end of file diff --git a/frontend/next.config.mjs b/frontend/next.config.mjs index cc4f92401..0c9126191 100644 --- a/frontend/next.config.mjs +++ b/frontend/next.config.mjs @@ -7,9 +7,7 @@ import i18nextConfig from './next-i18next.config.js'; const i18n = i18nextConfig.i18n; -const imageDomains = process.env.IMAGE_DOMAINS - ? process.env.IMAGE_DOMAINS.split(',') - : null; +const imageDomains = process.env.IMAGE_DOMAINS ? process.env.IMAGE_DOMAINS.split(',') : null; const imageDomainsWithOnlyHostname = []; function getHostnameFromRegex(url) { @@ -143,10 +141,7 @@ const nextConfigWithSentryIfEnabled = !!process.env.SENTRY_ORG && !!process.env.SENTRY_PROJECT && !!process.env.SENTRY_RELEASE - ? withSentryConfig( - { ...nextConfigWithPWA, devtool: 'source-map' }, - sentryWebpackPluginOptions - ) + ? withSentryConfig({ ...nextConfigWithPWA, devtool: 'source-map' }, sentryWebpackPluginOptions) : nextConfigWithPWA; export default nextConfigWithSentryIfEnabled; diff --git a/frontend/package.json b/frontend/package.json index 0b6dda568..08fb66602 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,6 +22,7 @@ "@emotion/styled": "^11.11.0", "@headlessui/react": "^1.7.18", "@hookform/resolvers": "^3.3.4", + "@monaco-editor/react": "^4.6.0", "@mswjs/interceptors": "^0.25.14", "@mui/material": "^5.15.6", "@mui/x-data-grid": "^6.19.1", @@ -35,7 +36,7 @@ "@radix-ui/react-select": "^2.1.1", "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.1.0", - "@radix-ui/react-switch": "^1.0.3", + "@radix-ui/react-switch": "^1.1.2", "@radix-ui/react-tabs": "^1.1.0", "@reduxjs/toolkit": "^2.0.1", "@sentry/nextjs": "^7.94.1", @@ -66,6 +67,7 @@ "js-cookie": "^3.0.5", "katex": "^0.16.9", "kuber-client": "^2.0.3", + "libcardano": "^1.4.1-7.3-browser", "libphonenumber-js": "^1.10.54", "little-state-machine": "^4.8.0", "lodash": "^4.17.21", @@ -102,6 +104,7 @@ "react-share": "^5.0.3", "react-toastify": "^10.0.4", "react-use": "^17.5.0", + "reaflow": "^5.3.1", "recharts": "^2.12.7", "redux-logger": "^3.0.6", "redux-persist": "^6.0.0", @@ -119,7 +122,6 @@ "zod": "^3.23.4" }, "devDependencies": { - "storybook": "^8.0.8", "@chromatic-com/storybook": "^1.3.2", "@redux-devtools/core": "^4.0.0", "@storybook/addon-essentials": "^8.0.8", @@ -166,6 +168,7 @@ "prettier": "^3.2.4", "prettier-plugin-tailwindcss": "^0.5.11", "rimraf": "^5.0.5", + "storybook": "^8.0.8", "tailwind-scrollbar": "^3.0.5", "tailwindcss": "^3.3.0", "ts-jest": "^29.1.2", diff --git a/frontend/prettier.config.js b/frontend/prettier.config.js index 20b181fb8..c741d7e87 100644 --- a/frontend/prettier.config.js +++ b/frontend/prettier.config.js @@ -1,5 +1,5 @@ module.exports = { - printWidth: 88, + printWidth: 120, singleQuote: true, tabWidth: 4, trailingComma: 'none', diff --git a/frontend/src/api/agents.ts b/frontend/src/api/agents.ts index 484d1fa84..47c20ecba 100644 --- a/frontend/src/api/agents.ts +++ b/frontend/src/api/agents.ts @@ -9,6 +9,34 @@ import { convertToQueryStr } from '@app/utils/common/extra'; import { agentFormSchema } from '../app/(pages)/agents/create-agent/_form/schema'; import { baseAPIurl } from './config'; +export type BooleanOperator = 'AND' | 'OR'; +export type ComparisonOperator = 'equals' | 'greaterThan' | 'lessThan' | 'in'; + +export interface IFieldNode { + id: string | Array; + value: any; + negate: boolean; + operators: string[]; + operator: string; + validator: (...args: any) => any; +} + +export interface IBooleanNode { + id?: string | string[]; + children: IFilterNode[]; + negate: boolean; + operator: BooleanOperator; +} + +export interface IEventTrigger { + id: string | string[]; + children: IFilterNode[]; + negate: boolean; + operator: BooleanOperator; +} + +export type IFilterNode = IFieldNode | IBooleanNode; + export type TriggerType = 'CRON' | 'MANUAL' | 'EVENT'; export interface ISubParameter { @@ -26,11 +54,6 @@ export interface ICronTrigger { probability: number; } -export interface IEventTrigger { - event: 'VoteEvent'; - parameters?: Array; -} - export interface IAgentConfiguration { id: string; agent_id: string; @@ -77,11 +100,7 @@ export interface IAgent { secret_key?: string; } -export const fetchAgents = async (params: { - page: number; - size: number; - search: string; -}): Promise => { +export const fetchAgents = async (params: { page: number; size: number; search: string }): Promise => { const { page, size, search } = params; const queryString = convertToQueryStr(page, size, search); @@ -182,10 +201,7 @@ export interface AgentTriggerRequestData { parameters: Array; } -export const manualTriggerForAgent = async ( - agentId: string, - data: AgentTriggerRequestData -) => { +export const manualTriggerForAgent = async (agentId: string, data: AgentTriggerRequestData) => { const url = `${baseAPIurl}/agents/${agentId}/trigger`; try { const response = await axios.post(url, data, { diff --git a/frontend/src/api/auth.ts b/frontend/src/api/auth.ts index c398f8d33..e014d75f7 100644 --- a/frontend/src/api/auth.ts +++ b/frontend/src/api/auth.ts @@ -15,9 +15,7 @@ export interface IUserResponse { isSuperUser: boolean; } -export const SendLoginRequest = async ( - signedData: ISignedData -): Promise => { +export const SendLoginRequest = async (signedData: ISignedData): Promise => { if (!signedData) { throw new Error('Signed data is null'); } diff --git a/frontend/src/app/(pages)/templates/create-template/components/TriggerForm.tsx b/frontend/src/api/healthStatus/route.ts similarity index 100% rename from frontend/src/app/(pages)/templates/create-template/components/TriggerForm.tsx rename to frontend/src/api/healthStatus/route.ts diff --git a/frontend/src/api/templates.ts b/frontend/src/api/templates.ts index 4f277bdd0..3024f605e 100644 --- a/frontend/src/api/templates.ts +++ b/frontend/src/api/templates.ts @@ -39,11 +39,7 @@ export interface ICreateTemplateRequestDTO { template_triggers: ITriggerCreateDto[]; } -export const fetchTemplates = async (params: { - page: number; - size: number; - search: string; -}): Promise => { +export const fetchTemplates = async (params: { page: number; size: number; search: string }): Promise => { const { page, size, search } = params; const queryString = convertToQueryStr(page, size, search); @@ -63,11 +59,7 @@ export const fetchTemplatebyID = async (templateID: string): Promise return await res.json(); }; -export const postTemplateData = async ({ - data -}: { - data: ICreateTemplateRequestDTO; -}) => { +export const postTemplateData = async ({ data }: { data: ICreateTemplateRequestDTO }) => { try { const response = await axios.post( `${baseAPIurl}/templates`, diff --git a/frontend/src/api/trigger.ts b/frontend/src/api/trigger.ts index 6d984499e..9df1e0ea1 100644 --- a/frontend/src/api/trigger.ts +++ b/frontend/src/api/trigger.ts @@ -1,11 +1,6 @@ import axios from 'axios'; -import { - IAgentAction, - IAgentConfiguration, - ICronTrigger, - IEventTrigger -} from './agents'; +import { IAgentAction, IAgentConfiguration, ICronTrigger, IEventTrigger } from './agents'; import { baseAPIurl } from './config'; export interface ITrigger { @@ -46,9 +41,7 @@ export const fetchtriggersbyTemplateID = async (templateID: string) => { return await res.json(); }; -export const fetchTransactionsCountByAgentID = async ( - agentID: string -): Promise => { +export const fetchTransactionsCountByAgentID = async (agentID: string): Promise => { const res = await fetch(`${baseAPIurl}/transaction-counts/${agentID}`); if (!res.ok) { throw new Error('Trigger Fetch failed: Network Error'); @@ -94,16 +87,13 @@ export interface ITriggerCreateDto { } export const postTrigger = async (agentID: string, triggerData: ITriggerCreateDto) => { - const response = await axios.post( - `${baseAPIurl}/agents/${agentID}/triggers`, - triggerData, - { - headers: { - 'Content-Type': 'application/json' - }, - withCredentials: true - } - ); + console.log('Req body: ', triggerData); + const response = await axios.post(`${baseAPIurl}/agents/${agentID}/triggers`, triggerData, { + headers: { + 'Content-Type': 'application/json' + }, + withCredentials: true + }); return response.data; }; diff --git a/frontend/src/api/triggerHistory.ts b/frontend/src/api/triggerHistory.ts index e3bc27c16..8b108ccaf 100644 --- a/frontend/src/api/triggerHistory.ts +++ b/frontend/src/api/triggerHistory.ts @@ -18,13 +18,8 @@ export interface IAgentTriggerHistory { result?: any; } -export const fetchAgentTriggerHistoryById = async ( - agentId: string, - pageItemsSize: number -) => { - const res = await fetch( - `${baseAPIurl}/trigger-history?agent_id=${agentId}&size=${pageItemsSize}` - ); +export const fetchAgentTriggerHistoryById = async (agentId: string, pageItemsSize: number) => { + const res = await fetch(`${baseAPIurl}/trigger-history?agent_id=${agentId}&size=${pageItemsSize}`); if (!res.ok) { throw new Error('Trigger Fetch failed: Network Error'); } @@ -41,9 +36,7 @@ export const fetchTransactionCountOfAgentById = async (agentId: string) => { export const fetchTriggerHistoryByFunctionName = async (functionName: string) => { const encodedFunctionName = encodeURI(functionName); - const res = await fetch( - `${baseAPIurl}/trigger-history?function_name=${encodedFunctionName}` - ); + const res = await fetch(`${baseAPIurl}/trigger-history?function_name=${encodedFunctionName}`); if (!res.ok) { throw new Error('Trigger Fetch Failed: Network Error'); } @@ -53,9 +46,7 @@ export const fetchTriggerHistoryByFunctionName = async (functionName: string) => export const fetchAllTriggerHistory = async ({ queryKey -}: QueryFunctionContext< - [string, number?, number?, string?, string?, string?, string?] ->) => { +}: QueryFunctionContext<[string, number?, number?, string?, string?, string?, string?]>) => { const [, page, size = 50, agentID, functionName, status, success] = queryKey; let fetchURL = `${baseAPIurl}/trigger-history?page=${page}&size=${size}`; diff --git a/frontend/src/api/triggerHistoryMetric.ts b/frontend/src/api/triggerHistoryMetric.ts index 8f531d37b..06c44d11b 100644 --- a/frontend/src/api/triggerHistoryMetric.ts +++ b/frontend/src/api/triggerHistoryMetric.ts @@ -20,14 +20,11 @@ export const fecthTriggerHistoryMetric = async ( ): Promise => { let fetchURL = `${baseAPIurl}/trigger-metric`; if (functions.length > 0) { - const queryParams = functions - .map((func) => `function_name=${encodeURIComponent(func)}`) - .join('&'); + const queryParams = functions.map((func) => `function_name=${encodeURIComponent(func)}`).join('&'); fetchURL += `?${queryParams}`; } if (agent_id && agent_id?.length >= 0) { - fetchURL += - functions.length == 0 ? `?agent_id=${agent_id}` : `&agent_id=${agent_id}`; + fetchURL += functions.length == 0 ? `?agent_id=${agent_id}` : `&agent_id=${agent_id}`; } const res = fetch(fetchURL); const data = (await res).json(); diff --git a/frontend/src/app/(pages)/agents/[agentID]/page.tsx b/frontend/src/app/(pages)/agents/[agentID]/page.tsx index 2f2a4aafe..07fbdfbf5 100644 --- a/frontend/src/app/(pages)/agents/[agentID]/page.tsx +++ b/frontend/src/app/(pages)/agents/[agentID]/page.tsx @@ -10,17 +10,8 @@ import { useAtom } from 'jotai'; import AgentTabSection from '@app/components/Agent/AgentTab/AgentTab'; import AgentTabContent from '@app/components/Agent/AgentTab/AgentTabContent'; -import { - Breadcrumb, - BreadcrumbItem, - BreadcrumbList, - BreadcrumbSeparator -} from '@app/components/atoms/Breadcrumb'; -import { - adminAccessAtom, - currentAgentNameAtom, - currentConnectedWalletAtom -} from '@app/store/localStore'; +import { Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbSeparator } from '@app/components/atoms/Breadcrumb'; +import { adminAccessAtom, currentAgentNameAtom, currentConnectedWalletAtom } from '@app/store/localStore'; export default function AgentPageById() { const [, setCurrentAgentName] = useAtom(currentAgentNameAtom); diff --git a/frontend/src/app/(pages)/agents/components/AgentsContainer.tsx b/frontend/src/app/(pages)/agents/components/AgentsContainer.tsx index fd6d391b8..833f00d71 100644 --- a/frontend/src/app/(pages)/agents/components/AgentsContainer.tsx +++ b/frontend/src/app/(pages)/agents/components/AgentsContainer.tsx @@ -8,11 +8,7 @@ export interface AgentsContainerProps { enableDelete: boolean; } -const AgentsContainer: React.FC = ({ - agentsList, - enableEdit, - enableDelete -}) => { +const AgentsContainer: React.FC = ({ agentsList, enableEdit, enableDelete }) => { return (
{agentsList.map((agent) => ( diff --git a/frontend/src/app/(pages)/agents/components/AgentsTopNav.tsx b/frontend/src/app/(pages)/agents/components/AgentsTopNav.tsx index 36f144a0b..d0ed4640f 100644 --- a/frontend/src/app/(pages)/agents/components/AgentsTopNav.tsx +++ b/frontend/src/app/(pages)/agents/components/AgentsTopNav.tsx @@ -5,13 +5,7 @@ import { Button } from '@app/components/atoms/Button'; import { cn } from '@app/components/lib/utils'; import { Skeleton } from '@app/components/shadcn/ui/skeleton'; -const AgentsTopNav = ({ - createAgentAccess, - onSearch = () => {} -}: { - createAgentAccess: boolean; - onSearch?: any; -}) => { +const AgentsTopNav = ({ createAgentAccess, onSearch = () => {} }: { createAgentAccess: boolean; onSearch?: any }) => { return (
diff --git a/frontend/src/app/(pages)/agents/create-agent/page.tsx b/frontend/src/app/(pages)/agents/create-agent/page.tsx index 3a9b930f2..b27e31fc5 100644 --- a/frontend/src/app/(pages)/agents/create-agent/page.tsx +++ b/frontend/src/app/(pages)/agents/create-agent/page.tsx @@ -32,9 +32,7 @@ export default function CreateAgentForm() { const [, setAgentCreated] = useAtom(agentCreatedAtom); const [submittingForm, setSubmittingForm] = useState(false); - const [agentTemplateOptions, setAgentTemplateOptions] = useState( - [] - ); + const [agentTemplateOptions, setAgentTemplateOptions] = useState([]); const { data: templates } = useQuery({ queryKey: ['templates'], queryFn: () => fetchTemplates({ page: 1, size: 50, search: '' }) @@ -118,9 +116,7 @@ export default function CreateAgentForm() { name="agentTemplate" render={({}) => ( - +
{ setSelected([option]); - form.setValue( - 'agentTemplate', - option.value - ); + form.setValue('agentTemplate', option.value); }} onUnselect={unselectOption} /> @@ -152,14 +145,10 @@ export default function CreateAgentForm() { description={option.description} key={index} handleEdit={() => { - toast.error( - 'Template Editing is currently unavailable.' - ); + toast.error('Template Editing is currently unavailable.'); }} handleUnselect={() => { - multiSelectorRef.current.handleUnselect( - option - ); + multiSelectorRef.current.handleUnselect(option); form.resetField('agentTemplate'); }} /> @@ -172,17 +161,13 @@ export default function CreateAgentForm() { name="numberOfAgents" render={({ field }) => ( - + - field.onChange(parseInt(e.target.value)) - } + onChange={(e) => field.onChange(parseInt(e.target.value))} min={1} /> diff --git a/frontend/src/app/(pages)/agents/page.tsx b/frontend/src/app/(pages)/agents/page.tsx index 20756ecfa..c27c0b56b 100644 --- a/frontend/src/app/(pages)/agents/page.tsx +++ b/frontend/src/app/(pages)/agents/page.tsx @@ -11,11 +11,7 @@ import { cn } from '@app/components/lib/utils'; import { SuccessToast } from '@app/components/molecules/CustomToasts'; import EmptyScreen from '@app/components/molecules/EmptyScreen'; import { Skeleton } from '@app/components/shadcn/ui/skeleton'; -import { - adminAccessAtom, - agentCreatedAtom, - currentConnectedWalletAtom -} from '@app/store/localStore'; +import { adminAccessAtom, agentCreatedAtom, currentConnectedWalletAtom } from '@app/store/localStore'; import { IQueryParams } from '@app/utils/query'; import AgentsContainer from './components/AgentsContainer'; @@ -55,26 +51,15 @@ export default function AgentsPage() { }, [agentCreated]); const otherAgents = useMemo(() => { - return ( - agents?.filter( - (agent) => agent.userAddress !== currentConnectedWallet?.address - ) || [] - ); + return agents?.filter((agent) => agent.userAddress !== currentConnectedWallet?.address) || []; }, [agents, currentConnectedWallet?.address]); const myAgents = useMemo(() => { - return ( - agents?.filter( - (agent) => agent.userAddress === currentConnectedWallet?.address - ) || [] - ); + return agents?.filter((agent) => agent.userAddress === currentConnectedWallet?.address) || []; }, [agents, currentConnectedWallet?.address]); return ( <> - handleSearch(value)} - /> + handleSearch(value)} /> {isLoading && } {!isLoading && agents && @@ -96,18 +81,9 @@ export default function AgentsPage() { enableDelete={adminAccess} /> {currentConnectedWallet && !isLoading && myAgents.length > 0 && ( -
0 && 'my-8', - 'h-full w-full' - )} - > +
0 && 'my-8', 'h-full w-full')}> My Agents - +
)}
diff --git a/frontend/src/app/(pages)/dRep-directory/components/AgentsDelegationDialogContent.tsx b/frontend/src/app/(pages)/dRep-directory/components/AgentsDelegationDialogContent.tsx index f615455cb..d67dc7ceb 100644 --- a/frontend/src/app/(pages)/dRep-directory/components/AgentsDelegationDialogContent.tsx +++ b/frontend/src/app/(pages)/dRep-directory/components/AgentsDelegationDialogContent.tsx @@ -5,10 +5,7 @@ interface AgentsDelegationDialogContentProps { handleClose: () => void; } -export default function AgentsDelegationDialogContent({ - dRepId, - handleClose -}: AgentsDelegationDialogContentProps) { +export default function AgentsDelegationDialogContent({ dRepId, handleClose }: AgentsDelegationDialogContentProps) { return ( = ({ dRep }) => { )}
- - {dRep.status} - + {dRep.status}

DrepID : {hexToBech32(dRep.drepId)}

- +
@@ -120,13 +114,8 @@ const DRepCard: React.FC = ({ dRep }) => {
-

- Status -

- +

Status

+ {dRep.status}
@@ -163,10 +152,7 @@ const DRepCard: React.FC = ({ dRep }) => { {/* Dialogs */} - + @@ -190,32 +176,18 @@ const AgentDetails = ({ return (
- +

- + {' '} {agentName || ''}

-

- AgentID : {agentId || ''} -

- +

AgentID : {agentId || ''}

+
@@ -230,52 +202,39 @@ export const DRepCardSkeleton = ({ className?: string; }) => { return ( -
+
- {' '} - {/* Placeholder for dRepName */} + {/* Placeholder for dRepName */} {/* Placeholder for Badge */}
{' '} {/* Placeholder for dRepId */} - {' '} - {/* Placeholder for CopyIcon */} + {/* Placeholder for CopyIcon */}
- {' '} - {/* Placeholder for "Voting Power" label */} - {' '} - {/* Placeholder for Voting Power value */} + {/* Placeholder for "Voting Power" label */} + {/* Placeholder for Voting Power value */}
{internalDRep && (
- {' '} - {/* Placeholder for Agent Name */} + {/* Placeholder for Agent Name */}
{' '} {/* Placeholder for AgentID */} - {' '} - {/* Placeholder for CopyIcon */} + {/* Placeholder for CopyIcon */}
)}
- {' '} - {/* Placeholder for View details button */} - {' '} - {/* Placeholder for Delegate button */} + {/* Placeholder for View details button */} + {/* Placeholder for Delegate button */}
); diff --git a/frontend/src/app/(pages)/dRep-directory/components/DrepDetailDialogContent.tsx b/frontend/src/app/(pages)/dRep-directory/components/DrepDetailDialogContent.tsx index a544e33a1..1d3a53e98 100644 --- a/frontend/src/app/(pages)/dRep-directory/components/DrepDetailDialogContent.tsx +++ b/frontend/src/app/(pages)/dRep-directory/components/DrepDetailDialogContent.tsx @@ -16,27 +16,17 @@ export default function DrepDetailDialogContent({ dRep: IDRepInternal; onClose?: (value: boolean) => void; }) { - const formattedVotingPower = convertLovelaceToAda(dRep.votingPower).toLocaleString( - 'en-US' - ); + const formattedVotingPower = convertLovelaceToAda(dRep.votingPower).toLocaleString('en-US'); const redirectToGovTool = () => { - window.open( - `https://govtool.cardanoapi.io/connected/drep_directory/${dRep.drepId}`, - '_blank' - ); + window.open(`https://govtool.cardanoapi.io/connected/drep_directory/${dRep.drepId}`, '_blank'); }; return (
- - {getDrepGivedName(dRep) || 'Data Missing'} - - + {getDrepGivedName(dRep) || 'Data Missing'} +
@@ -57,36 +47,25 @@ export default function DrepDetailDialogContent({
- - Voting Power: {formattedVotingPower} ADA - + Voting Power: {formattedVotingPower} ADA {dRep.references && dRep.references.length > 0 && ( - - References: {dRep.references.join(', ')} - + References: {dRep.references.join(', ')} )} - - {dRep.bio || 'No bio available'} - + {dRep.bio || 'No bio available'} {dRep.agentId && dRep.agentName && (
- - Agent ID: {dRep.agentId} - - - Agent Name: {dRep.agentName} - + Agent ID: {dRep.agentId} + Agent Name: {dRep.agentName}
)} {!dRep.givenName && ( - The data used when this DRep was created has been formatted - incorrectly. + The data used when this DRep was created has been formatted incorrectly. )}
@@ -110,10 +89,7 @@ const DRepDetails = ({ dRep }: { dRep: IDRepInternal }) => { External URL: {dRep.url ? ( - + {Truncate(dRep.url, 10)} ) : ( @@ -127,14 +103,8 @@ const DRepDetails = ({ dRep }: { dRep: IDRepInternal }) => { Latest Tx Hash: {dRep.latestTxHash ? (
- - {dRep.latestTxHash} - - - copyToClipboard(dRep.latestTxHash || '', 'Tx Hash copied') - } - /> + {dRep.latestTxHash} + copyToClipboard(dRep.latestTxHash || '', 'Tx Hash copied')} />
) : ( No latest tx hash available @@ -147,17 +117,8 @@ const DRepDetails = ({ dRep }: { dRep: IDRepInternal }) => { MetaData Hash: {dRep.metadataHash ? (
- - {dRep.metadataHash} - - - copyToClipboard( - dRep.metadataHash || '', - 'Metadata Hash copied' - ) - } - /> + {dRep.metadataHash} + copyToClipboard(dRep.metadataHash || '', 'Metadata Hash copied')} />
) : ( MetaData Hash not available @@ -169,9 +130,7 @@ const DRepDetails = ({ dRep }: { dRep: IDRepInternal }) => { Latest Registration Date: {dRep.latestRegistrationDate ? ( - - {new Date(dRep.latestRegistrationDate).toLocaleString()} - + {new Date(dRep.latestRegistrationDate).toLocaleString()} ) : ( No latest registration date available )} diff --git a/frontend/src/app/(pages)/dRep-directory/page.tsx b/frontend/src/app/(pages)/dRep-directory/page.tsx index e11e66d29..a97faaad0 100644 --- a/frontend/src/app/(pages)/dRep-directory/page.tsx +++ b/frontend/src/app/(pages)/dRep-directory/page.tsx @@ -81,33 +81,22 @@ export default function DRepDirectory() { /> {!isLoading && data && data?.items?.length > 0 && (
- {data?.items?.map((dRep, index) => ( - - ))} + {data?.items?.map((dRep, index) => )}
)} - {!isLoading && - data?.items?.length === 0 && - queryParams.drep_type === 'internal' && ( - handleFilterChange('all')} - /> - )} - - {!isLoading && - data?.items?.length === 0 && - queryParams.drep_type !== 'internal' && ( - - )} + {!isLoading && data?.items?.length === 0 && queryParams.drep_type === 'internal' && ( + handleFilterChange('all')} + /> + )} + + {!isLoading && data?.items?.length === 0 && queryParams.drep_type !== 'internal' && ( + + )} {isLoading && } -
+
- + { +const AgentsVoteDialogContent = ({ handleClose, proposalId }: AgentsVoteDialogContentProps) => { return ( +
- - No Gov Actions found at the moment! - + No Gov Actions found at the moment!
); diff --git a/frontend/src/app/(pages)/governance-actions/components/proposalCard.tsx b/frontend/src/app/(pages)/governance-actions/components/proposalCard.tsx index 79c701d57..2c0f9e178 100644 --- a/frontend/src/app/(pages)/governance-actions/components/proposalCard.tsx +++ b/frontend/src/app/(pages)/governance-actions/components/proposalCard.tsx @@ -48,10 +48,7 @@ const ProposalCard: React.FC = ({ proposal }) => { }; const handleProposalExternalRedirect = () => { - window.open( - `https://govtool.cardanoapi.io/connected/governance_actions/${proposal.txHash}`, - '_blank' - ); + window.open(`https://govtool.cardanoapi.io/connected/governance_actions/${proposal.txHash}`, '_blank'); }; const [currentConnectedWallet] = useAtom(currentConnectedWalletAtom); @@ -77,29 +74,16 @@ const ProposalCard: React.FC = ({ proposal }) => {
{proposal.abstract !== null && (
-

- Abstract -

-

- {proposal.abstract} -

+

Abstract

+

{proposal.abstract}

)} -
+
{proposal.agentName && (

Agent Name :{' '} - + {proposal.agentName}

@@ -116,9 +100,7 @@ const ProposalCard: React.FC = ({ proposal }) => {
)} -

- Governance Action Type -

+

Governance Action Type

@@ -138,32 +120,25 @@ const ProposalCard: React.FC = ({ proposal }) => {
Submitted: - {proposal.createdDate && - formatDisplayDate(proposal.createdDate)} + {proposal.createdDate && formatDisplayDate(proposal.createdDate)} (Epoch {proposal.createdEpochNo})

Expires: - {proposal.createdDate && - formatDisplayDate(proposal?.expiryDate)} + {proposal.createdDate && formatDisplayDate(proposal?.expiryDate)} (Epoch {proposal.expiryEpochNo})

-

- Governance Action Id -

+

Governance Action Id

{proposalId} -
+
@@ -182,10 +157,7 @@ const ProposalCard: React.FC = ({ proposal }) => {
{/* Dialogs */} - + ); diff --git a/frontend/src/app/(pages)/governance-actions/page.tsx b/frontend/src/app/(pages)/governance-actions/page.tsx index de4438366..c92d7e535 100644 --- a/frontend/src/app/(pages)/governance-actions/page.tsx +++ b/frontend/src/app/(pages)/governance-actions/page.tsx @@ -34,10 +34,7 @@ export default function GovernanceAction() { search: '' }); - const queryKey = useMemo( - () => [QUERY_KEYS.useGetInfoProposalListKey, searchParams], - [searchParams] - ); + const queryKey = useMemo(() => [QUERY_KEYS.useGetInfoProposalListKey, searchParams], [searchParams]); const { data, isLoading } = useQuery({ queryKey, @@ -79,32 +76,21 @@ export default function GovernanceAction() { {!isLoading && data?.items && data?.items?.length > 0 && (
- {data?.items?.map((proposal: any) => ( - - ))} + {data?.items?.map((proposal: any) => )}
)} - {!isLoading && - data?.items?.length === 0 && - searchParams.proposal_type === 'internal' && ( - handleFilterChange('all')} - /> - )} - {!isLoading && - data?.items?.length === 0 && - searchParams.proposal_type !== 'internal' && ( - - )} + {!isLoading && data?.items?.length === 0 && searchParams.proposal_type === 'internal' && ( + handleFilterChange('all')} + /> + )} + {!isLoading && data?.items?.length === 0 && searchParams.proposal_type !== 'internal' && ( + + )} {isLoading && } -
+
- +
{statusOptions.map((status: string, index) => ( handleStatusChange(status)} > @@ -163,29 +155,20 @@ export default function LogsPage() { <> {loadingLogs && } - {!loadingLogs && LogsHistory?.items.length === 0 && ( - - )} + {!loadingLogs && LogsHistory?.items.length === 0 && } {!loadingLogs && LogsHistory?.items.length > 0 && (
- {LogsHistory.items.map( - (history: IAgentTriggerHistory, index: number) => ( - - ) - )} + {LogsHistory.items.map((history: IAgentTriggerHistory, index: number) => ( + + ))}
)} -
+
{ diff --git a/frontend/src/app/(pages)/templates/[templateId]/page.tsx b/frontend/src/app/(pages)/templates/[templateId]/page.tsx index 764e502f3..958e43447 100644 --- a/frontend/src/app/(pages)/templates/[templateId]/page.tsx +++ b/frontend/src/app/(pages)/templates/[templateId]/page.tsx @@ -10,12 +10,7 @@ import { useQuery } from '@tanstack/react-query'; import { useAtom } from 'jotai'; import TemplateOverview from '@app/components/Template/Content/TemplateOverview'; -import { - Breadcrumb, - BreadcrumbItem, - BreadcrumbList, - BreadcrumbSeparator -} from '@app/components/atoms/Breadcrumb'; +import { Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbSeparator } from '@app/components/atoms/Breadcrumb'; import { Skeleton } from '@app/components/shadcn/ui/skeleton'; import { templateAtom } from '@app/store/atoms/template'; import { adminAccessAtom } from '@app/store/localStore'; @@ -44,9 +39,7 @@ const EditTemplateCard = () => {
{templateLoading && } - {template && ( - - )} + {template && }
@@ -65,9 +58,7 @@ const TemplateBreadCrumb = ({ templateName }: { templateName?: string }) => { Templates - - {Truncate(templateName || '', 30) || 'Template Name'} - + {Truncate(templateName || '', 30) || 'Template Name'} ); diff --git a/frontend/src/app/(pages)/templates/components/TemplateList.tsx b/frontend/src/app/(pages)/templates/components/TemplateList.tsx index e29c7b396..11e677cb4 100644 --- a/frontend/src/app/(pages)/templates/components/TemplateList.tsx +++ b/frontend/src/app/(pages)/templates/components/TemplateList.tsx @@ -11,8 +11,7 @@ const TemplateList = ({ adminAccess: boolean; currentConnectedWallet: any; }) => { - const containerClass = - 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 2xl:grid-cols-5 3xl:grid-cols-6 gap-4'; + const containerClass = 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 2xl:grid-cols-5 3xl:grid-cols-6 gap-4'; return (
@@ -22,9 +21,7 @@ const TemplateList = ({ ))}
diff --git a/frontend/src/app/(pages)/templates/components/TemplatesTopNav.tsx b/frontend/src/app/(pages)/templates/components/TemplatesTopNav.tsx index 5ea574966..95e096d23 100644 --- a/frontend/src/app/(pages)/templates/components/TemplatesTopNav.tsx +++ b/frontend/src/app/(pages)/templates/components/TemplatesTopNav.tsx @@ -5,21 +5,11 @@ import { Button } from '@app/components/atoms/Button'; import { cn } from '@app/components/lib/utils'; import { Skeleton } from '@app/components/shadcn/ui/skeleton'; -const TemplatesTopNav = ({ - onSearch, - adminAccess -}: { - onSearch: any; - adminAccess: boolean; -}) => { +const TemplatesTopNav = ({ onSearch, adminAccess }: { onSearch: any; adminAccess: boolean }) => { return (
- +
+
+ +
); }; diff --git a/frontend/src/app/(pages)/templates/create-template/components/trigger/ParameterRenderers.tsx b/frontend/src/app/(pages)/templates/create-template/components/ParameterRenderers.tsx similarity index 80% rename from frontend/src/app/(pages)/templates/create-template/components/trigger/ParameterRenderers.tsx rename to frontend/src/app/(pages)/templates/create-template/components/ParameterRenderers.tsx index a51cef665..1ca1d0cd6 100644 --- a/frontend/src/app/(pages)/templates/create-template/components/trigger/ParameterRenderers.tsx +++ b/frontend/src/app/(pages)/templates/create-template/components/ParameterRenderers.tsx @@ -6,10 +6,7 @@ import { Select, SelectContent, SelectItem } from '@app/components/atoms/Select' import { Label } from '@app/components/atoms/label'; import { NumberInput } from '@app/components/molecules/NumberInput'; -export const RenderStringParameter = ( - param: IParameter, - onValueChange: (id: string, value: any) => void -) => ( +export const RenderStringParameter = (param: IParameter, onValueChange: (id: string, value: any) => void) => (
); -export const RenderNumberParameter = ( - param: IParameter, - onValueChange: (id: string, value: any) => void -) => ( +export const RenderNumberParameter = (param: IParameter, onValueChange: (id: string, value: any) => void) => (
); }; @@ -142,21 +127,14 @@ export const renderParameters = (
{parameters.map((param, index) => (
- {param.type === 'string' || - param.type === 'hash' || - param.type === 'url' + {param.type === 'string' || param.type === 'hash' || param.type === 'url' ? RenderStringParameter(param, onValueChange) : param.type === 'number' ? RenderNumberParameter(param, onValueChange) - : (param.type === 'object' || param.type === 'list') && - param.parameters + : (param.type === 'object' || param.type === 'list') && param.parameters ? RenderObjectParameter(param, onNestedValueChange!) : param.type === 'options' && param.options - ? RenderOptionsParameter( - param, - handleOptionChange, - optionValue || null - ) + ? RenderOptionsParameter(param, handleOptionChange, optionValue || null) : null}
))} diff --git a/frontend/src/app/(pages)/templates/create-template/components/trigger/TriggerTab.tsx b/frontend/src/app/(pages)/templates/create-template/components/TriggerTab.tsx similarity index 85% rename from frontend/src/app/(pages)/templates/create-template/components/trigger/TriggerTab.tsx rename to frontend/src/app/(pages)/templates/create-template/components/TriggerTab.tsx index 77e947c96..a9a0af1f0 100644 --- a/frontend/src/app/(pages)/templates/create-template/components/trigger/TriggerTab.tsx +++ b/frontend/src/app/(pages)/templates/create-template/components/TriggerTab.tsx @@ -3,15 +3,10 @@ import React, { useEffect, useState } from 'react'; import { Card } from '@app/components/atoms/Card'; -import { - Tabs, - TabsContent, - TabsList, - TabsTrigger -} from '@app/components/molecules/Tabs'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@app/components/molecules/Tabs'; -import CustomCron from './CustomCron'; -import DefaultCron from './DefaultCron'; +import CustomCron from './cron/CustomCron'; +import DefaultCron from './cron/DefaultCron'; export interface ICronSetting { placeholder: string; @@ -57,11 +52,7 @@ export default function TriggerTab({ onlyCronTriggerTab, defaultToCustomTab = false }: { - onChange?: ( - cronExpression: string[], - configuredSettings: string, - selectedTab: any - ) => void; + onChange?: (cronExpression: string[], configuredSettings: string, selectedTab: any) => void; defaultCron?: any; previousSelectedOption?: string; previousConfiguredSettings?: IInputSetting[]; @@ -71,9 +62,7 @@ export default function TriggerTab({ defaultToCustomTab?: boolean; }) { const [cron, setCron] = useState( - typeof defaultCron === 'string' - ? defaultCron.split(' ') - : defaultCron || ['*', '*', '*', '*', '*'] + typeof defaultCron === 'string' ? defaultCron.split(' ') : defaultCron || ['*', '*', '*', '*', '*'] ); /* State for persisiting custom cron settings when switching tabs*/ @@ -81,14 +70,10 @@ export default function TriggerTab({ /* state for persisiting user cron settings when switching tabs*/ - const [defaultSelected, setDefaultSelected] = useState( - previousSelectedOption || 'Minute-option-one' - ); + const [defaultSelected, setDefaultSelected] = useState(previousSelectedOption || 'Minute-option-one'); const defaultSelectedCronTab = - (defaultToCustomTab && 'Custom') || - previousSelectedOption?.split('-')[0] || - 'Minute'; + (defaultToCustomTab && 'Custom') || previousSelectedOption?.split('-')[0] || 'Minute'; const initialSettings: IInputSetting[] = [ { name: 'Minute-option-two', value: 1 }, @@ -143,10 +128,7 @@ export default function TriggerTab({ setSelectedTab?.('CRON')}> Cron Trigger - setSelectedTab?.('EVENT')} - > + setSelectedTab?.('EVENT')}> Event Trigger @@ -175,10 +157,7 @@ export default function TriggerTab({ ))} - +
diff --git a/frontend/src/app/(pages)/templates/create-template/components/FunctionCards.tsx b/frontend/src/app/(pages)/templates/create-template/components/cards/FunctionCards.tsx similarity index 78% rename from frontend/src/app/(pages)/templates/create-template/components/FunctionCards.tsx rename to frontend/src/app/(pages)/templates/create-template/components/cards/FunctionCards.tsx index c36d82663..179216fab 100644 --- a/frontend/src/app/(pages)/templates/create-template/components/FunctionCards.tsx +++ b/frontend/src/app/(pages)/templates/create-template/components/cards/FunctionCards.tsx @@ -3,7 +3,7 @@ import { Edit } from 'lucide-react'; import { Card } from '@app/components/atoms/Card'; -import { IFormFunctionInstance } from '../page'; +import { IFormFunctionInstance } from '../../page'; interface IFunctionCards { functions: IFormFunctionInstance[]; @@ -21,11 +21,7 @@ export const FunctionCards = ({ functions, onUnselect, onEdit }: IFunctionCards)
{functionItem.name}
- onEdit(functionItem)} - /> + onEdit(functionItem)} />
- - {functionItem.description} - + {functionItem.description} ))} diff --git a/frontend/src/app/(pages)/templates/create-template/components/cards/InfoCard.tsx b/frontend/src/app/(pages)/templates/create-template/components/cards/InfoCard.tsx new file mode 100644 index 000000000..fb57257e4 --- /dev/null +++ b/frontend/src/app/(pages)/templates/create-template/components/cards/InfoCard.tsx @@ -0,0 +1,30 @@ +import { Info } from 'lucide-react'; + +import { cn } from '@app/components/lib/utils'; + +export default function InfoCard({ + onMouseEnter, + onMouseLeave, + visible +}: { + onMouseEnter: any; + onMouseLeave: any; + visible: boolean; +}) { + return ( +
+ + +
+ ); +} diff --git a/frontend/src/app/(pages)/templates/create-template/components/trigger/CustomCron.tsx b/frontend/src/app/(pages)/templates/create-template/components/cron/CustomCron.tsx similarity index 84% rename from frontend/src/app/(pages)/templates/create-template/components/trigger/CustomCron.tsx rename to frontend/src/app/(pages)/templates/create-template/components/cron/CustomCron.tsx index d4ea7d2f8..748bd71ae 100644 --- a/frontend/src/app/(pages)/templates/create-template/components/trigger/CustomCron.tsx +++ b/frontend/src/app/(pages)/templates/create-template/components/cron/CustomCron.tsx @@ -2,26 +2,14 @@ import { useEffect, useRef, useState } from 'react'; import { Input } from '@app/components/atoms/Input'; -export default function CustomCron({ - customCron, - onChange -}: { - customCron?: string[]; - onChange?: any; -}) { +export default function CustomCron({ customCron, onChange }: { customCron?: string[]; onChange?: any }) { const [seconds, setSeconds] = useState(customCron ? customCron[0] : '*'); const [minutes, setMinutes] = useState(customCron ? customCron[1] : '*'); const [hours, setHours] = useState(customCron ? customCron[2] : '*'); const [months, setMonths] = useState(customCron ? customCron[3] : '*'); const [years, setYears] = useState(customCron ? customCron[4] : '*'); - const [, setCron] = useState([ - `${seconds}`, - `${minutes}`, - `${hours}`, - `${months}`, - `${years}` - ]); + const [, setCron] = useState([`${seconds}`, `${minutes}`, `${hours}`, `${months}`, `${years}`]); const isFirstRender = useRef(true); @@ -31,13 +19,7 @@ export default function CustomCron({ return; } - const newCron = [ - `${seconds}`, - `${minutes}`, - `${hours}`, - `${months}`, - `${years}` - ]; + const newCron = [`${seconds}`, `${minutes}`, `${hours}`, `${months}`, `${years}`]; setCron(newCron); onChange?.(newCron); }, [seconds, minutes, hours, months, years]); diff --git a/frontend/src/app/(pages)/templates/create-template/components/trigger/DefaultCron.tsx b/frontend/src/app/(pages)/templates/create-template/components/cron/DefaultCron.tsx similarity index 88% rename from frontend/src/app/(pages)/templates/create-template/components/trigger/DefaultCron.tsx rename to frontend/src/app/(pages)/templates/create-template/components/cron/DefaultCron.tsx index 872b80b3b..b7b607c5f 100644 --- a/frontend/src/app/(pages)/templates/create-template/components/trigger/DefaultCron.tsx +++ b/frontend/src/app/(pages)/templates/create-template/components/cron/DefaultCron.tsx @@ -5,7 +5,7 @@ import { Label } from '@app/components/atoms/label'; import { cn } from '@app/components/lib/utils'; import { NumberInput } from '@app/components/molecules/NumberInput'; -import { ICronSetting, IInputSetting } from './TriggerTab'; +import { ICronSetting, IInputSetting } from '../TriggerTab'; export default function DefaultCron({ cronSetting, @@ -85,11 +85,8 @@ export default function DefaultCron({ }); }} defaultValue={ - configuredSettings?.find( - (item) => - item.name === - `${cronSetting.placeholder}-option-two` - )?.value || '1' + configuredSettings?.find((item) => item.name === `${cronSetting.placeholder}-option-two`) + ?.value || '1' } disabled={selected !== `${cronSetting.placeholder}-option-two`} />{' '} @@ -136,14 +133,10 @@ export default function DefaultCron({ }} defaultValue={ configuredSettings?.find( - (item) => - item.name === - `${cronSetting.placeholder}-option-three-start` + (item) => item.name === `${cronSetting.placeholder}-option-three-start` )?.value || '1' } - disabled={ - selected !== `${cronSetting.placeholder}-option-three` - } + disabled={selected !== `${cronSetting.placeholder}-option-three`} /> {cronSetting.placeholder} and - item.name === - `${cronSetting.placeholder}-option-three-end` + (item) => item.name === `${cronSetting.placeholder}-option-three-end` )?.value || '1' } - disabled={ - selected !== `${cronSetting.placeholder}-option-three` - } + disabled={selected !== `${cronSetting.placeholder}-option-three`} /> {cronSetting.placeholder} diff --git a/frontend/src/app/(pages)/templates/create-template/components/event/CustomEditor.tsx b/frontend/src/app/(pages)/templates/create-template/components/event/CustomEditor.tsx new file mode 100644 index 000000000..1a98bc58c --- /dev/null +++ b/frontend/src/app/(pages)/templates/create-template/components/event/CustomEditor.tsx @@ -0,0 +1,29 @@ +import { IEventTrigger } from '@api/agents'; +import Editor from '@monaco-editor/react'; + +export default function CustomEditor({ + defaultValue, + onValueChange +}: { + defaultValue: IEventTrigger | null; + onValueChange: (data: string) => void; +}) { + return ( + <> + data && onValueChange(data)} + > + + ); +} diff --git a/frontend/src/app/(pages)/templates/create-template/components/event/EventTab.tsx b/frontend/src/app/(pages)/templates/create-template/components/event/EventTab.tsx new file mode 100644 index 000000000..0330f719b --- /dev/null +++ b/frontend/src/app/(pages)/templates/create-template/components/event/EventTab.tsx @@ -0,0 +1,436 @@ +import React, { useEffect, useMemo, useState } from 'react'; + +import { IBooleanNode, IEventTrigger, IFieldNode } from '@api/agents'; +import { getNestedTxProperties } from '@utils'; +import { Events as transactionSchema } from 'libcardano/spec/properties'; + +import EventTabRenderer from '@app/app/(pages)/templates/create-template/components/event/EventTabRenderer'; +import RenderEventChildForm from '@app/app/(pages)/templates/create-template/components/event/RenderEventChildForm'; +import { checkIfStringOrArrayOfStringIdsAreEqual } from '@app/app/(pages)/templates/create-template/components/utils/array'; +import { Button } from '@app/components/atoms/Button'; +import { Card } from '@app/components/atoms/Card'; +import { Label } from '@app/components/atoms/label'; +import { cn } from '@app/components/lib/utils'; +import { CustomCombobox } from '@app/components/molecules/CustomCombobox'; +import { CustomSelect } from '@app/components/molecules/CustomDropDown'; +import { Switch } from '@app/components/shadcn/ui/switch'; +import { areArraysEqual } from '@app/utils/common/array'; + +import InfoCard from '../cards/InfoCard'; +import { ISchema } from './EventTrigger'; + +const EventTab = ({ + className, + savedEventTrigger, + onChange = () => {} +}: { + className?: string; + savedEventTrigger?: IEventTrigger; + onChange?: (value: IEventTrigger) => void; +}) => { + const [currentEvent, setCurrentEvent] = useState(null); + + const [currentEventFilter, setCurrentEventFilter] = useState(null); + + const [isInfoVisible, setIsInfoVisible] = useState(false); + + const [formData, setFormData] = useState(savedEventTrigger || null); + + const [proMode, setProMode] = useState(false); + + useEffect(() => { + if (formData) { + onChange(formData); + } + }, [formData]); + + useEffect(() => { + if (savedEventTrigger) { + const savedEvent = transactionSchema.find((event) => event.id === savedEventTrigger?.id); + if (savedEvent) { + setCurrentEvent(savedEvent); + } + const lastEventFilter = savedEvent?.properties?.find( + (prop) => prop.id === savedEventTrigger?.children[savedEventTrigger?.children.length - 1]?.id + ); + if (lastEventFilter) { + setCurrentEventFilter(lastEventFilter); + } + } + }, []); + + const handleSelectEvent = (eventId: string) => { + const selectedEvent = transactionSchema.find((tx) => tx.id === eventId); + if (selectedEvent) { + setCurrentEvent(selectedEvent); + setFormData({ + id: selectedEvent?.id, + children: [], + negate: false, + operator: 'AND' + }); + } + }; + + const handleSelectEventFilter = (eventFilterId: string) => { + const selectedEventFilter = currentEvent?.properties?.find((prop) => prop.id === eventFilterId); + if (selectedEventFilter) { + if (!selectedEventFilter.properties) { + const { id, label, type, validator } = selectedEventFilter; + selectedEventFilter.properties = [{ id, label, type, validator }]; + } else { + const { id, label, type, validator, properties } = selectedEventFilter; + const idExists = selectedEventFilter.properties.find((p) => p.id === id); + if (!idExists) { + selectedEventFilter.properties = [ + ...properties, + { + id, + label, + type, + validator, + operators: ['exists'] + } + ]; + } + } + setCurrentEventFilter(selectedEventFilter); + return selectedEventFilter; + } + }; + + const handleAddEventFilter = (eventFilterId: string) => { + const selectedEventFilter = handleSelectEventFilter(eventFilterId); + if (selectedEventFilter) { + formData && + setFormData({ + ...formData, + children: [ + ...formData.children, + { + id: selectedEventFilter.id, + operator: 'AND', + negate: false, + children: [] + } + ] + }); + } + }; + + function getSelectedParameter(parameterId: string | string[]) { + const selectedProperty = transactionSchema + .find((tx) => tx.id === formData!.id) + ?.properties?.find((filter) => filter.id === currentEventFilter?.id); + let selectedParameter: any = selectedProperty?.properties?.find((prop) => { + if (Array.isArray(parameterId)) { + return prop.id === parameterId[0]; + } else { + return prop.id === parameterId; + } + }); + if (selectedParameter && 'properties' in selectedParameter) { + selectedParameter = getNestedTxProperties(selectedParameter)?.find((prop) => + areArraysEqual(prop.id as string[], parameterId as string[]) + ); + } + return selectedParameter; + } + + const handleAddParameter = (parameterId: string | string[]) => { + if (formData && currentEventFilter) { + const selectedParameter = getSelectedParameter(parameterId); + if (selectedParameter) { + const data = { + ...formData, + children: formData.children.map((eventFilter) => + eventFilter.id === currentEventFilter?.id + ? { + ...eventFilter, + children: [ + //@ts-ignore + ...(eventFilter.children || []), + { + id: selectedParameter.id, + operators: selectedParameter.operators + ? selectedParameter.operators + : ['exists'], + validator: selectedParameter.validator, + value: '', + negate: false, + operator: 'exists' + } + ] + } + : eventFilter + ) + }; + setFormData(data); + } + } + }; + + const handleDeleteParameter = (parameterId: string | string[]) => { + if (!formData) return; + const data = { + ...formData, + children: formData.children.map((eventFilter) => { + return 'children' in eventFilter + ? { + ...eventFilter, + children: eventFilter.children.filter((child) => { + if (Array.isArray(child.id)) { + return !areArraysEqual(child.id as string[], parameterId as string[]); + } else return child.id !== parameterId; + }) + } + : eventFilter; + }) + }; + setFormData({ ...data }); + }; + + const handleParamValueChange = (parameterId: string, value: any) => { + if (formData && currentEventFilter) { + const data = { + ...formData, + children: formData.children.map((eventFilter) => + eventFilter.id === currentEventFilter?.id + ? { + ...eventFilter, + //@ts-ignore + children: eventFilter.children.map((child) => + child.id === parameterId ? { ...child, value } : child + ) + } + : eventFilter + ) + }; + setFormData(data); + } + }; + + const handleParamOperatorChange = (parameterId: string, operator: string) => { + if (formData && currentEventFilter) { + const data = { + ...formData, + children: formData.children.map((eventFilter) => + eventFilter.id === currentEventFilter?.id + ? { + ...eventFilter, + //@ts-ignore + children: eventFilter.children.map((child) => + child.id === parameterId ? { ...child, operator: operator } : child + ) + } + : eventFilter + ) + }; + setFormData(data); + } + }; + + const handleParamNegationChange = (parameterId: string, negate: boolean) => { + if (formData && currentEventFilter) { + const data = { + ...formData, + children: formData.children.map((eventFilter) => + eventFilter.id === currentEventFilter?.id + ? { + ...eventFilter, + //@ts-ignore + children: eventFilter.children.map((child) => + child.id === parameterId ? { ...child, negate: negate } : child + ) + } + : eventFilter + ) + }; + setFormData(data); + } + }; + + const getNotSelectedEventFilters = () => { + return transactionSchema + .find((tx) => tx.id === formData?.id) + ?.properties?.filter((prop) => !formData?.children.find((child) => child.id === prop.id)); + }; + + const getFlattenEventFilterParams = () => { + return currentEventFilter?.properties?.map((prop) => getNestedTxProperties(prop)).flat(); + }; + + const getNotSelectedEventFilterParameters = () => { + return ( + currentEventFilter && + getFlattenEventFilterParams()?.filter( + (prop: ISchema) => + !formData?.children + .find((eventFilter) => eventFilter.id === currentEventFilter?.id) + //@ts-ignore + ?.children.find( + (child: IFieldNode) => !checkIfStringOrArrayOfStringIdsAreEqual(child.id, prop.id) + ) + ) + ); + }; + + const removeEventFilter = (filterId?: string | string[]) => { + if (!filterId || !formData?.children) return; + formData.children = formData?.children.filter( + (child) => child.id && checkIfStringOrArrayOfStringIdsAreEqual(filterId, child.id) + ); + setFormData({ ...formData }); + formData && formData.children && formData.children.length + ? handleSelectEventFilter(formData.children.slice(-1)[0].id as string) + : setCurrentEventFilter(null); + }; + + const memoizedEventFilterParams = useMemo( + () => + getNotSelectedEventFilterParameters()?.map((item: ISchema) => { + return { id: item.id, label: item.label }; + }) || [], + [formData, currentEventFilter] + ); + + const memoizedEventFilters = useMemo( + () => + getNotSelectedEventFilters()?.map((item) => ({ + id: item.id, + label: item.label + })) || [], + [formData] + ); + + return ( +
+
+ + setProMode(!proMode)} id="airplane-mode" /> +
+ {proMode ? ( + <> + ) : ( + <> +
+ ({ + label: txProp.label, + value: txProp.id + }))} + defaultValue={currentEvent?.label || 'Event'} + label="Event" + onSelect={handleSelectEvent} + disabled={currentEvent !== null} + /> +
+

Event Filter Parameters

+ { + handleAddEventFilter(value); + }} + defaultValue={{ id: 'label', label: 'Event Filter' }} + /> +
+
+

Event Filter Parameters

+ { + // const splittedParamArray = + // dotSeperatedParamString.split('.'); + handleAddParameter(value); + }} + defaultValue={{ + id: 'label', + label: 'Search Parameter' + }} + /> +
+ { + setIsInfoVisible(true); + }} + onMouseLeave={() => { + setIsInfoVisible(false); + }} + visible={isInfoVisible} + /> +
+ +
+ + +
+ {formData?.children.map((param, index) => { + if (param.id === currentEventFilter?.id) { + return ( +
+ {(param as IBooleanNode).children?.map((child, childIndex) => ( +
+ { + { + handleParamValueChange(child.id as string, value); + }} + onOperatorChange={(value) => { + handleParamOperatorChange(child.id as string, value); + }} + onNegateChange={(value) => { + handleParamNegationChange(child.id as string, value); + }} + onDeleteParameter={handleDeleteParameter} + /> + } +
+ ))} +
+ ); + } + })} +
+ + )} + { + setFormData(value)} + /> + } +
+ ); +}; + +export default EventTab; diff --git a/frontend/src/app/(pages)/templates/create-template/components/event/EventTabRenderer.tsx b/frontend/src/app/(pages)/templates/create-template/components/event/EventTabRenderer.tsx new file mode 100644 index 000000000..1272e6d93 --- /dev/null +++ b/frontend/src/app/(pages)/templates/create-template/components/event/EventTabRenderer.tsx @@ -0,0 +1,47 @@ +import React, { useEffect, useState } from 'react'; + +import { IEventTrigger } from '@api/agents'; +import { useDebounceValue } from 'usehooks-ts'; + +import CustomEditor from '@app/app/(pages)/templates/create-template/components/event/CustomEditor'; +import NodeGraph from '@app/app/(pages)/templates/create-template/components/event/EventTriggerGraph'; +import { ErrorToast } from '@app/components/molecules/CustomToasts'; + +const EventTabRenderer = ({ + displayMonacoEditor, + formData, + onEditorValueChange +}: { + displayMonacoEditor: boolean; + formData: IEventTrigger | null; + onEditorValueChange: (value: IEventTrigger) => void; +}) => { + const [editorValue, setEditorValue] = useState(formData ? JSON.stringify(formData) : ''); + const handleOnEditorValueChange = (value: string) => { + setEditorValue(value); + }; + + const [debouncedValue] = useDebounceValue(editorValue, 800); + + useEffect(() => { + try { + const parsedData = JSON.parse(debouncedValue); + onEditorValueChange(parsedData); + } catch (err) { + if (!debouncedValue) return; + ErrorToast('Invalid JSON'); + } + }, [debouncedValue]); + + return ( +
+ {displayMonacoEditor ? ( + + ) : ( + + )} +
+ ); +}; + +export default EventTabRenderer; diff --git a/frontend/src/app/(pages)/templates/create-template/components/event/EventTrigger.ts b/frontend/src/app/(pages)/templates/create-template/components/event/EventTrigger.ts new file mode 100644 index 000000000..06ff6138c --- /dev/null +++ b/frontend/src/app/(pages)/templates/create-template/components/event/EventTrigger.ts @@ -0,0 +1,12 @@ +interface IEventFilterAtribute { + id: string; + label: string; + type: string; + operators?: string[]; + validator?: (arg: any) => any | undefined; + properties?: IEventFilterAtribute[]; +} + +export interface ISchema extends Omit { + id: string | string[]; +} diff --git a/frontend/src/app/(pages)/templates/create-template/components/event/EventTriggerGraph.tsx b/frontend/src/app/(pages)/templates/create-template/components/event/EventTriggerGraph.tsx new file mode 100644 index 000000000..2199d0e6b --- /dev/null +++ b/frontend/src/app/(pages)/templates/create-template/components/event/EventTriggerGraph.tsx @@ -0,0 +1,147 @@ +import { useEffect, useState } from 'react'; + +import { IEventTrigger, IFilterNode } from '@api/agents'; +import { Canvas, EdgeData, NodeData } from 'reaflow'; + +interface NodeGraphProps { + data: IEventTrigger | null; + className?: string; + maxHeight?: number; + maxWidth?: number; +} + +export default function EventTriggerGraph({ data, className, maxHeight, maxWidth }: NodeGraphProps) { + const [nodes, setNodes] = useState([]); + const [edges, setEdges] = useState([]); + + const getNodeWidth = (value: string) => { + return Math.min(Math.max(value.length * 15, 60), 350); + }; + + const reduceString = (strList: string[] | string): string => { + if (typeof strList === 'string') return strList; + return strList.join('.'); + }; + + const shouldDisplayBooleanOperator = (operator: string, obj: IFilterNode) => { + if (['AND', 'OR'].includes(operator)) { + if ('children' in obj && obj.children.length <= 1) return ''; + } + return operator; + }; + + useEffect(() => { + const newNodes: NodeData[] = []; + const newEdges: EdgeData[] = []; + + const truncate = (text: string) => { + if (text.length > 50) { + return text.slice(0, 50) + '...'; + } + return text; + }; + + const addNode = (id: string, text?: string) => { + newNodes.push({ + id: id, + text: truncate(text ? text : id), + width: getNodeWidth(text ? text : id) + }); + }; + + const addEdge = (parentId: string, childId: string) => { + newEdges.push({ + id: `${parentId}-${childId}`, + from: `${parentId}`, + to: `${parentId}.${childId}` + }); + }; + + const reduceStr = (text: string | string[]) => { + if (typeof text === 'string') { + return text; + } + return text.join('.'); + }; + + const addNodesRecursively = (obj: IFilterNode, parentId: string) => { + if (obj.id) { + const currentId = parentId + '.' + reduceStr(obj.id); + + let nodeText = + reduceStr(obj.id) + + ` ${shouldDisplayBooleanOperator(obj.operator, obj) ? ` ( ${obj.operator} )` : ''} `; + if ('value' in obj) { + nodeText = nodeText + obj.value; + } + + if (obj.negate) { + nodeText = 'NOT ( ' + nodeText + ')'; + } + + addNode(currentId, nodeText); + addEdge(parentId, reduceStr(obj.id)); + + if ('children' in obj) { + obj.children.forEach((childObj) => { + addNodesRecursively(childObj, currentId); + }); + } + } + if (!('id' in obj) && 'children' in obj) { + obj.children.forEach((childObj) => { + addNodesRecursively(childObj, parentId); + }); + } + }; + + // Initalize Root node and edge + if (data) { + const rootId = reduceString(data.id); + const text = data.negate + ? 'NOT ( ' + + data.id + + ` ${shouldDisplayBooleanOperator(data.operator, data) ? ` ( ${data.operator} )` : ''} ` + + ')' + : data.id + `${shouldDisplayBooleanOperator(data.operator, data) ? ` ( ${data.operator} )` : ''}`; + newNodes.push({ + id: rootId, + text: text, + width: getNodeWidth(text) + }); + + data.children.forEach((param) => { + // recursivelyAddNode(param, operatorId); + addNodesRecursively(param, rootId); + }); + } + + setNodes(newNodes); + setEdges(newEdges); + }, [data]); + + if (!data) { + return
; + } + return ( + JSON.stringify(node)))).map((node) => + JSON.parse(node) + ) as NodeData[] + } //ensure nodes are unique + edges={ + Array.from(new Set(edges.map((edge) => JSON.stringify(edge)))).map((edge) => + JSON.parse(edge) + ) as EdgeData[] + } // ensure edges are unique + pannable={true} + panType="scroll" + maxHeight={maxHeight || 800} + maxWidth={maxWidth || 1600} + className={`w-full bg-[#D2E0FB] ${className || ''}`} + direction="RIGHT" + zoomable={true} + /> + ); +} diff --git a/frontend/src/app/(pages)/templates/create-template/components/event/RenderEventChildForm.tsx b/frontend/src/app/(pages)/templates/create-template/components/event/RenderEventChildForm.tsx new file mode 100644 index 000000000..0e87639f6 --- /dev/null +++ b/frontend/src/app/(pages)/templates/create-template/components/event/RenderEventChildForm.tsx @@ -0,0 +1,139 @@ +import React, { useEffect, useState } from 'react'; + +import { IFieldNode } from '@api/agents'; +import { ChevronDown } from 'lucide-react'; +import { useDebounceValue } from 'usehooks-ts'; + +import { Checkbox } from '@app/components/atoms/Checkbox'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger +} from '@app/components/atoms/DropDownMenu'; +import { Input } from '@app/components/atoms/Input'; +import { cn } from '@app/components/lib/utils'; +import { Close } from '@app/views/atoms/Icons/Close'; + +import { transformLabelForOperators } from '../utils/MapperHelper'; + +const RenderEventChildForm = ({ + eventFilterParam, + onValueChange, + onOperatorChange, + onNegateChange, + onDeleteParameter +}: { + eventFilterParam: IFieldNode; + onValueChange?: (value: any) => void; + onOperatorChange?: (value: any) => void; + onNegateChange?: (value: boolean) => void; + onDeleteParameter?: (...args: any) => void; +}) => { + const [localOperator, setLocalOperator] = useState(eventFilterParam.operator || 'eq'); + const [value, setValue] = useState(eventFilterParam.value); + const [deleteBtnClicked,setDeleteBtnClicked] = useState(false) + + useEffect(() => { + if (deleteBtnClicked) { + setLocalOperator(eventFilterParam.operator || 'eq'); + setValue(eventFilterParam.value); + } + setDeleteBtnClicked(false) + }, [deleteBtnClicked]); + + const paramId = Array.isArray(eventFilterParam.id) + ? (eventFilterParam.id as string[]).join('.') + : eventFilterParam.id; + + useEffect(() => { + localOperator && onOperatorChange?.(localOperator); + }, [localOperator]); + + const [errMsg, setErrMsg] = useState(''); + + const [debouncedVal] = useDebounceValue(value, 500); + + useEffect(() => { + if (debouncedVal && eventFilterParam?.validator && !eventFilterParam?.validator(debouncedVal)) { + return setErrMsg(`Error occured on ${paramId}`); + } + onValueChange?.(debouncedVal); + }, [debouncedVal]); + + useEffect(() => { + const clearErrMsg = setTimeout(() => { + setErrMsg(''); + }, 3000); + return () => { + clearTimeout(clearErrMsg); + }; + }, [errMsg]); + + const handleOperatorChange = (operator: string) => { + setLocalOperator(operator); + onOperatorChange?.(operator); + }; + + const handleOnDeleteParam = (paramId: string | string[]) => { + onDeleteParameter && onDeleteParameter(paramId); + setDeleteBtnClicked(true) + }; + + const handleInputChange = (e: React.ChangeEvent) => { + const inputVal = e.target.value; + setValue(inputVal); + setErrMsg(''); + }; + + return ( +
+
+ {paramId} +
+ negate + +
+ + + + {transformLabelForOperators(localOperator)} + + + + + {eventFilterParam.operators.map((operator, index) => ( + handleOperatorChange(operator)} + > + {transformLabelForOperators(operator)} + + ))} + + + {eventFilterParam.value} + {localOperator !== 'exists' ? ( + + ) : ( + <> + )} + handleOnDeleteParam(eventFilterParam.id)} + className={cn( + 'invisible cursor-pointer group-hover:visible', + localOperator === 'exists' ? 'h-5 w-5' : 'h-10 w-10 ' + )} + /> +
+ {errMsg &&
{errMsg}
} +
+ ); +}; + +export default RenderEventChildForm; diff --git a/frontend/src/app/(pages)/templates/create-template/components/event/demo.ts b/frontend/src/app/(pages)/templates/create-template/components/event/demo.ts new file mode 100644 index 000000000..8d1a3b3ab --- /dev/null +++ b/frontend/src/app/(pages)/templates/create-template/components/event/demo.ts @@ -0,0 +1,66 @@ +// import { IEventTrigger } from "@api/agents"; + +// const demoData: IEventTrigger = { +// id: 'tx', +// negate: false, +// operator: 'AND', +// parameters: [ +// { +// negate: false, +// id: ['inputs', 'address'], +// value: 'addr_test1qqgn33e8vc40948u4crwsq7dxk9e6kuf3amp6gf4k57n97prsqy2pgnvcwzlutpc6a2ttwnuggl36hy4vkm227zdy0gq9mj2yv', +// operator: 'equals' +// }, +// { +// negate: false, +// children: [ +// { +// id: ['outputs', 'value'], +// value: 999, +// negate: true, +// operator: 'greaterThan' +// }, +// { +// id: ['outputs', 'address'], +// value: 'addr.....', +// negate: false, +// operator: 'equals' +// } +// ], +// operator: 'AND' +// } +// ] +// }; + +// const nestedData: IEventTrigger = { +// id: 'tx', +// negate: false, +// operator: 'AND', +// parameters: [ +// { +// negate: false, +// id: ['inputs', 'address'], +// value: 'addr_test1qqgn33e8vc40948u4crwsq7dxk9e6kuf3amp6gf4k57n97prsqy2pgnvcwzlutpc6a2ttwnuggl36hy4vkm227zdy0gq9mj2yv', +// operator: 'equals' +// }, +// { +// id: 'outputs', +// negate: false, +// children: [ +// { +// id: ['value'], +// value: 999, +// negate: true, +// operator: 'greaterThan' +// }, +// { +// id: ['address'], +// value: 'addr.....', +// negate: false, +// operator: 'equals' +// } +// ], +// operator: 'AND' +// } +// ] +// }; diff --git a/frontend/src/app/(pages)/templates/create-template/components/utils/FunctionMapper.ts b/frontend/src/app/(pages)/templates/create-template/components/utils/FunctionMapper.ts index 1bcd045ac..1c9318f7a 100644 --- a/frontend/src/app/(pages)/templates/create-template/components/utils/FunctionMapper.ts +++ b/frontend/src/app/(pages)/templates/create-template/components/utils/FunctionMapper.ts @@ -1,42 +1,35 @@ -import { IAgentConfiguration, ISubParameter } from '@api/agents'; +import { IAgentConfiguration, IEventTrigger, ISubParameter } from '@api/agents'; import { TriggerType } from '@api/agents'; import { ICronTrigger } from '@api/agents'; import { ITemplateConfiguration } from '@api/templates'; import { ITriggerCreateDto } from '@api/trigger'; -import { - IParameter, - IParameterOption, - TemplateFunctions -} from '@models/types/functions'; +import { IParameter, IParameterOption, TemplateFunctions } from '@models/types/functions'; import { IFormFunctionInstance } from '../../page'; import { - mapKeyValuePairToParamValue, + mapKeyValuePairToParamValue, // mapKeyValuePairToParamValue, mapOptionToKeyValuePair, mapParamValueToKeyValuePair } from './MapperHelper'; // For converting function data from form to Trigger Create DTO Format. -export const mapFormFunctionToTriggerConfiguration = ( - item: IFormFunctionInstance -): ITriggerCreateDto => { +export const mapFormFunctionToTriggerConfiguration = (item: IFormFunctionInstance): ITriggerCreateDto => { const commonData = { probability: (item.cronValue?.probability || 100) / 100, frequency: item.cronValue?.frequency || '* * * * *' }; if (item.type === 'EVENT') { - return { - type: item.type, - action: { - function_name: item.id, - parameters: mapParamValueToKeyValuePair(item.eventParameters || []) - }, - data: { - event: 'VoteEvent', - parameters: mapParamValueToKeyValuePair(item.eventParameters || []) - } - }; + if (item.eventValue) { + return { + type: item.type, + action: { + function_name: item.id, + parameters: item.optionValue ? [mapOptionToKeyValuePair(item.optionValue, 'voteType')] : [] + }, + data: item.eventValue + }; + } } if (item.id === 'delegation') { @@ -44,14 +37,28 @@ export const mapFormFunctionToTriggerConfiguration = ( type: item.type, action: { function_name: item.id, - parameters: item.optionValue - ? [mapOptionToKeyValuePair(item.optionValue, 'delegation_params')] - : [] + parameters: item.optionValue ? [mapOptionToKeyValuePair(item.optionValue, 'delegation_params')] : [] }, data: commonData }; } + if (item.id == 'voteOnProposal' && item.type == 'CRON' && item.optionValue) { + alert(item.optionValue.id); + item.parameters = + item.parameters?.map((param) => { + if (param.type === 'options') { + return { + name: param.name, + id: param.id || 'vote', + type: 'string', + value: item.optionValue?.id || 'yes' + }; + } + return param; + }) || item.parameters; + } + return { type: item.type, action: { @@ -66,9 +73,7 @@ export const mapFormFunctionToTriggerConfiguration = ( export const mapTriggerConfigurationToFormFunction = ( trigger: ITriggerCreateDto | IAgentConfiguration ): IFormFunctionInstance => { - const baseFunction = TemplateFunctions.find( - (f) => f.id === trigger.action.function_name - ); + const baseFunction = TemplateFunctions.find((f) => f.id === trigger.action.function_name); if (!baseFunction) { throw new Error( @@ -76,21 +81,12 @@ export const mapTriggerConfigurationToFormFunction = ( ); } - const populateParameters = ( - baseParams: IParameter[], - triggerParams: ISubParameter[] - ): IParameter[] => { + const populateParameters = (baseParams: IParameter[], triggerParams: ISubParameter[]): IParameter[] => { const obj = baseParams.map((baseParam) => { - const matchingTriggerParam = triggerParams.find( - (param) => param.name == baseParam.id - ); + const matchingTriggerParam = triggerParams.find((param) => param.name == baseParam.id); // populate upper layer - if ( - matchingTriggerParam && - baseParam.type !== 'options' && - baseParam.type !== 'object' - ) { + if (matchingTriggerParam && baseParam.type !== 'options' && baseParam.type !== 'object') { return { ...baseParam, value: matchingTriggerParam.value @@ -99,19 +95,14 @@ export const mapTriggerConfigurationToFormFunction = ( // for nested object type if (matchingTriggerParam && baseParam.type === 'object') { - const combinedSubTriggerParams = Object.entries( - matchingTriggerParam.value - ).map(([key, value]) => ({ + const combinedSubTriggerParams = Object.entries(matchingTriggerParam.value).map(([key, value]) => ({ name: key, value: value })); const nested_obj = { ...baseParam, - parameters: populateParameters( - baseParam.parameters || [], - combinedSubTriggerParams - ) + parameters: populateParameters(baseParam.parameters || [], combinedSubTriggerParams) }; return nested_obj; @@ -159,8 +150,7 @@ export const mapTriggerConfigurationToFormFunction = ( }; }; - const cronValue: ICronTrigger | undefined = (trigger.data as ICronTrigger) - ?.probability + const cronValue: ICronTrigger | undefined = (trigger.data as ICronTrigger)?.probability ? { probability: (trigger.data as ICronTrigger).probability * 100, frequency: (trigger.data as ICronTrigger).frequency @@ -168,43 +158,33 @@ export const mapTriggerConfigurationToFormFunction = ( : undefined; const eventParameters = - trigger.type === 'EVENT' - ? mapKeyValuePairToParamValue(trigger.action.parameters || []) - : undefined; + trigger.type === 'EVENT' ? mapKeyValuePairToParamValue(trigger.action.parameters || []) : undefined; const final_obj = { id: trigger.action.function_name, index: '', name: baseFunction.name, description: baseFunction.description, - parameters: populateParameters( - baseFunction.parameters || [], - trigger.action.parameters || [] - ), + parameters: populateParameters(baseFunction.parameters || [], trigger.action.parameters || []), type: trigger.type as TriggerType, cronValue, - eventValue: - trigger.type === 'EVENT' - ? { - event: (trigger.data as any).event, - parameters: eventParameters - } - : undefined, + eventValue: trigger.type === 'EVENT' ? (trigger.data as IEventTrigger) : undefined, optionValue: baseFunction?.parameters && baseFunction?.parameters[0].type === 'options' ? populateOptionValue(trigger.action.parameters[0]) - : undefined, + : (trigger.type === 'EVENT' ? eventParameters && populateOptionValue(eventParameters[0]) : undefined) || + undefined, selectedCronOption: undefined, congifuredCronSettings: undefined, - agent_id: (trigger as IAgentConfiguration).agent_id || undefined + agent_id: (trigger as IAgentConfiguration).agent_id || undefined, + eventDescription: baseFunction.eventDescription, + eventParameters: baseFunction.eventParameters }; return final_obj; }; -export const mapAgentConfigurationToFormFunction = ( - agentConfiguration: IAgentConfiguration -): IFormFunctionInstance => { +export const mapAgentConfigurationToFormFunction = (agentConfiguration: IAgentConfiguration): IFormFunctionInstance => { const convertedObj = mapTriggerConfigurationToFormFunction({ action: agentConfiguration.action, data: agentConfiguration.data, @@ -215,9 +195,7 @@ export const mapAgentConfigurationToFormFunction = ( return convertedObj; }; -export const mapFormFunctionToAgentConfiguration = ( - formFuncion: IFormFunctionInstance -): IAgentConfiguration => { +export const mapFormFunctionToAgentConfiguration = (formFuncion: IFormFunctionInstance): IAgentConfiguration => { const functionMetaData = TemplateFunctions.find((f) => f.name === formFuncion.name); // Ids are different. Meta data id and actual trigger id @@ -238,9 +216,7 @@ export const mapFormFunctionToTemplateConfiguration = ( formFunction: IFormFunctionInstance, template_id: string ): ITemplateConfiguration => { - const functionMetaData = TemplateFunctions.find( - (f) => f.name === formFunction.name - ); + const functionMetaData = TemplateFunctions.find((f) => f.name === formFunction.name); const convertedObj = mapFormFunctionToTriggerConfiguration({ ...formFunction, diff --git a/frontend/src/app/(pages)/templates/create-template/components/utils/MapperHelper.ts b/frontend/src/app/(pages)/templates/create-template/components/utils/MapperHelper.ts index 8fece9d52..feaf17e8c 100644 --- a/frontend/src/app/(pages)/templates/create-template/components/utils/MapperHelper.ts +++ b/frontend/src/app/(pages)/templates/create-template/components/utils/MapperHelper.ts @@ -4,15 +4,11 @@ import { IParameter, IParameterOption } from '@models/types/functions'; export const mapParamValueToKeyValuePair = (params: IParameter[]): ISubParameter[] => { return params.map((param) => ({ name: param.id || param.name, - value: param.parameters - ? convertToKeyValuePairObject(param.parameters) - : param.value + value: param.parameters ? convertToKeyValuePairObject(param.parameters) : param.value })); }; -export const convertToKeyValuePairObject = ( - params: IParameter[] -): Record => { +export const convertToKeyValuePairObject = (params: IParameter[]): Record => { return params.reduce( (obj, param) => { obj[param.id] = param.value; @@ -21,10 +17,7 @@ export const convertToKeyValuePairObject = ( {} as Record ); }; -export const mapOptionToKeyValuePair = ( - option: IParameterOption, - name = '' -): ISubParameter => { +export const mapOptionToKeyValuePair = (option: IParameterOption, name = ''): ISubParameter => { if (option.parameters) { return { name: name || option.name, @@ -37,9 +30,7 @@ export const mapOptionToKeyValuePair = ( }; }; -export const mapKeyValuePairToParamValue = ( - subParams: ISubParameter[] -): IParameter[] => { +export const mapKeyValuePairToParamValue = (subParams: ISubParameter[]): IParameter[] => { return subParams.map((subParam) => ({ id: subParam.name, type: 'string', @@ -55,3 +46,20 @@ export const mapKeyValuePairToParamValue = ( : subParam.value })); }; + +export function transformLabelForOperators(operator: string) { + switch (operator) { + case 'lt': + return '<'; + case 'gt': + return '>'; + case 'gte': + return '>='; + case 'lte': + return '<='; + case 'eq': + return '=='; + default: + return operator; + } +} diff --git a/frontend/src/app/(pages)/templates/create-template/components/utils/array.ts b/frontend/src/app/(pages)/templates/create-template/components/utils/array.ts new file mode 100644 index 000000000..3a393529a --- /dev/null +++ b/frontend/src/app/(pages)/templates/create-template/components/utils/array.ts @@ -0,0 +1,9 @@ +import { areArraysEqual } from '@app/utils/common/array'; + +export function checkIfStringOrArrayOfStringIdsAreEqual(id1: string | string[], id2: string | string[]) { + if (Array.isArray(id1)) { + return !Array.isArray(id2) || !areArraysEqual(id1, id2); + } else { + return Array.isArray(id2) || id1 !== id2; + } +} diff --git a/frontend/src/app/(pages)/templates/create-template/page.tsx b/frontend/src/app/(pages)/templates/create-template/page.tsx index 7f0445681..0640edf60 100644 --- a/frontend/src/app/(pages)/templates/create-template/page.tsx +++ b/frontend/src/app/(pages)/templates/create-template/page.tsx @@ -7,13 +7,8 @@ import { useRouter } from 'next/navigation'; import { ICronTrigger, IEventTrigger, TriggerType } from '@api/agents'; import { ICreateTemplateRequestDTO, postTemplateData } from '@api/templates'; -import { - IFunctionsItem, - IParameterOption, - TemplateFunctions -} from '@models/types/functions'; +import { IFunctionsItem, IParameterOption, TemplateFunctions } from '@models/types/functions'; import { Dialog, DialogContent } from '@mui/material'; -import useMediaQuery from '@mui/material/useMediaQuery'; import { useMutation } from '@tanstack/react-query'; import { useAtom } from 'jotai'; import { v4 } from 'uuid'; @@ -33,8 +28,8 @@ import { ErrorToast } from '@app/components/molecules/CustomToasts'; import { templateCreatedAtom } from '@app/store/localStore'; import { queryClient } from '@app/utils/providers/ReactQueryProvider'; -import { FunctionCards } from './components/FunctionCards'; import { FunctionForm } from './components/FunctionForm'; +import { FunctionCards } from './components/cards/FunctionCards'; import { mapFormFunctionToTriggerConfiguration } from './components/utils/FunctionMapper'; export interface IFormFunctionInstance extends IFunctionsItem { @@ -45,7 +40,7 @@ export interface IFormFunctionInstance extends IFunctionsItem { optionValue?: IParameterOption; //for saving cron settings selectedCronOption?: string; - congifuredCronSettings?: any; + configuredCronSettings?: any; //To be compatible with IAgentConfiuration agent_id?: string; } @@ -58,7 +53,6 @@ interface IFormData { export default function CreateTemplatePage() { const router = useRouter(); - const isMobile = useMediaQuery('(max-width:600px)'); const [submittingForm, setSubmittingForm] = useState(false); const [, setTemplateCreated] = useAtom(templateCreatedAtom); @@ -69,8 +63,7 @@ export default function CreateTemplatePage() { }); const templateMutation = useMutation({ - mutationFn: (data: ICreateTemplateRequestDTO) => - postTemplateData({ data: data }), + mutationFn: (data: ICreateTemplateRequestDTO) => postTemplateData({ data: data }), onSuccess: () => { new Promise((resolve) => setTimeout(resolve, 1000)); queryClient.refetchQueries({ queryKey: ['templates'] }); @@ -83,8 +76,7 @@ export default function CreateTemplatePage() { } }); - const [currentSelectedFunction, setCurrentSelectedFunction] = - useState(null); + const [currentSelectedFunction, setCurrentSelectedFunction] = useState(null); const [isDialogOpen, setIsDialogOpen] = useState(false); @@ -107,9 +99,7 @@ export default function CreateTemplatePage() { }; const handleDeleteFunction = (inputFunction: IFormFunctionInstance) => { - const newFunctions = mainState.functions.filter( - (functionItem) => functionItem.index !== inputFunction.index - ); + const newFunctions = mainState.functions.filter((functionItem) => functionItem.index !== inputFunction.index); setMainState({ ...mainState, functions: newFunctions @@ -122,9 +112,7 @@ export default function CreateTemplatePage() { const newFunctions = mainState.functions.map((functionItem) => functionItem.index === item.index ? item : functionItem ); - const indexExists = newFunctions.some( - (functionItem) => functionItem.index === item.index - ); + const indexExists = newFunctions.some((functionItem) => functionItem.index === item.index); if (!indexExists) { newFunctions.push(item); } @@ -151,9 +139,7 @@ export default function CreateTemplatePage() { const templateRequest: ICreateTemplateRequestDTO = { name: mainState.name, description: mainState.description, - template_triggers: mainState.functions.map((func) => - mapFormFunctionToTriggerConfiguration(func) - ) + template_triggers: mainState.functions.map((func) => mapFormFunctionToTriggerConfiguration(func)) }; setSubmittingForm(true); templateMutation.mutate(templateRequest); @@ -177,9 +163,7 @@ export default function CreateTemplatePage() { placeholder="Text..." className="mt-3 h-[123px] w-full" value={mainState.description} - onChange={(e) => - handleUpdateMainState('description', e.target.value) - } + onChange={(e) => handleUpdateMainState('description', e.target.value)} />
@@ -189,18 +173,11 @@ export default function CreateTemplatePage() { Add Function - {TemplateFunctions.map( - (templateFunction, index: number) => ( - - handleAddFunction(templateFunction) - } - key={index} - > - {templateFunction.name} - - ) - )} + {TemplateFunctions.map((templateFunction, index: number) => ( + handleAddFunction(templateFunction)} key={index}> + {templateFunction.name} + + ))}
@@ -222,10 +199,7 @@ export default function CreateTemplatePage() { handleTemplateSubmit(); }} disabled={ - submittingForm || - !mainState.functions.length || - !mainState.name || - !mainState.description + submittingForm || !mainState.functions.length || !mainState.name || !mainState.description } > Create Template @@ -233,7 +207,7 @@ export default function CreateTemplatePage() {
{/*Dialog for function form*/} - + {currentSelectedFunction && ( , React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); AccordionItem.displayName = 'AccordionItem'; diff --git a/frontend/src/app/components/AgentActionDialogContent.tsx b/frontend/src/app/components/AgentActionDialogContent.tsx index 472647288..c7a04e88b 100644 --- a/frontend/src/app/components/AgentActionDialogContent.tsx +++ b/frontend/src/app/components/AgentActionDialogContent.tsx @@ -28,18 +28,17 @@ const AgentsActionDialogContent = ({ triggerType, functionId }: AgentsActionDialogContentProps) => { - const { activeAgents, isSubmitting, handleSelect, handleAction, selectedAgentIds } = - useAgentsAction(triggerType, handleClose, functionId); + const { activeAgents, isSubmitting, handleSelect, handleAction, selectedAgentIds } = useAgentsAction( + triggerType, + handleClose, + functionId + ); const [adminAccess] = useAtom(adminAccessAtom); const [currentConnectedWallet] = useAtom(currentConnectedWalletAtom); function filterUserOwnedAgents(agents: IAgent[]) { - return adminAccess - ? agents - : agents.filter( - (agent) => agent.userAddress === currentConnectedWallet?.address - ); + return adminAccess ? agents : agents.filter((agent) => agent.userAddress === currentConnectedWallet?.address); } const userOwnedAgents = filterUserOwnedAgents(activeAgents); @@ -52,11 +51,7 @@ const AgentsActionDialogContent = ({ ) : (
{userOwnedAgents.map((agent) => ( - + ))}
)} diff --git a/frontend/src/app/components/AgentSelector.tsx b/frontend/src/app/components/AgentSelector.tsx index 240adadc6..c7110b908 100644 --- a/frontend/src/app/components/AgentSelector.tsx +++ b/frontend/src/app/components/AgentSelector.tsx @@ -1,24 +1,46 @@ +import { useState } from 'react'; + import { IAgent } from '@api/agents'; -import { ITemplate, fetchTemplatebyID } from '@api/templates'; -import { useQuery } from '@tanstack/react-query'; import { Checkbox } from '@app/components/atoms/Checkbox'; +import { CustomCombobox } from '@app/components/molecules/CustomCombobox'; +import { IAgentVote, VoteType } from '@app/lib/hooks/useAgentActions'; interface AgentSelectorProps { agent: IAgent; - handleSelect: (checked: string | boolean, agent: IAgent) => void; + handleSelect: (checked: string | boolean, agent: IAgentVote) => void; } + export default function AgentSelector({ agent, handleSelect }: AgentSelectorProps) { - const { data: template } = useQuery({ - queryKey: [`template${agent.template_id}`], - queryFn: () => fetchTemplatebyID(agent.template_id || '') - }); + const [voteType, setVoteType] = useState(''); + const voteTypes = [ + { id: 'Yes', label: 'Yes' }, + { id: 'No', label: 'No' }, + { id: 'Abstain', label: 'Abstain' } + ]; + const handleCheckChange = (checked: string | boolean) => { + handleSelect(checked, { + agentId: agent.id, + voteType: voteType as VoteType + }); + }; return ( -
- handleSelect(checked, agent)} /> -
-

{agent.name}

-

{template?.name}

+
+ vote.id === voteType)} + onCheckedChange={(checked) => handleCheckChange(checked)} + /> +
+

{agent.name}

+
+ Vote : + setVoteType(voteType)} + /> +
); diff --git a/frontend/src/app/components/AppDialog.tsx b/frontend/src/app/components/AppDialog.tsx index 5ae33a03c..ed3dcbaad 100644 --- a/frontend/src/app/components/AppDialog.tsx +++ b/frontend/src/app/components/AppDialog.tsx @@ -2,13 +2,7 @@ import React from 'react'; import { X } from 'lucide-react'; -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle -} from '@app/components/atoms/Dialog'; +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@app/components/atoms/Dialog'; interface AppDialogProps extends React.PropsWithChildren { isOpen?: boolean; @@ -27,10 +21,7 @@ export const AppDialogContent: React.FC = (props) => { return ( - onClose?.(false)} - /> + onClose?.(false)} /> {title && {title}} {description && {description}} diff --git a/frontend/src/app/components/DashboardCards.tsx b/frontend/src/app/components/DashboardCards.tsx index ca242c2d2..83d5d05ea 100644 --- a/frontend/src/app/components/DashboardCards.tsx +++ b/frontend/src/app/components/DashboardCards.tsx @@ -54,11 +54,7 @@ const DashboardCards = ({ className }: { className?: string }) => { const { data: proposalMetric } = useQuery({ queryKey: ['proposalMetric'], - queryFn: async () => - fecthTriggerHistoryMetric([ - 'createInfoGovAction', - 'proposalNewConstitution' - ]) + queryFn: async () => fecthTriggerHistoryMetric(['createInfoGovAction', 'proposalNewConstitution']) }); const { data: voteMetric } = useQuery({ @@ -76,10 +72,7 @@ const DashboardCards = ({ className }: { className?: string }) => { title="No of Agents" totalAgents={agents.length || 'NA'} activeAgents={activeAgents?.online_agents_count || 0} - inactiveAgents={Math.max( - 0, - agents.length - activeAgents?.online_agents_count || 0 - )} + inactiveAgents={Math.max(0, agents.length - activeAgents?.online_agents_count || 0)} /> { /> { /> ) => { onSearch && onSearch(event.target.value); }, 500); @@ -26,9 +22,7 @@ export default function DataActionBar({ variant="secondary" placeholder={placeholder} onChange={handleSearch} - className={ - 'h-10 w-full min-w-[50px] rounded-lg px-4 py-3 text-sm xl:w-[400px] 2xl:w-[500px]' - } + className={'h-10 w-full min-w-[50px] rounded-lg px-4 py-3 text-sm xl:w-[400px] 2xl:w-[500px]'} />
); diff --git a/frontend/src/app/components/EmptyAgent.tsx b/frontend/src/app/components/EmptyAgent.tsx index 51fd1fafa..64c02ef2f 100644 --- a/frontend/src/app/components/EmptyAgent.tsx +++ b/frontend/src/app/components/EmptyAgent.tsx @@ -2,9 +2,7 @@ export default function EmptyAgent() { return (

No agents are active.

-

- Please ensure at least one of your agent is active -

+

Please ensure at least one of your agent is active

); } diff --git a/frontend/src/app/components/OverViewAgentsCard.stories.tsx b/frontend/src/app/components/OverViewAgentsCard.stories.tsx index 5dbff4ced..e6880c52c 100644 --- a/frontend/src/app/components/OverViewAgentsCard.stories.tsx +++ b/frontend/src/app/components/OverViewAgentsCard.stories.tsx @@ -20,13 +20,6 @@ type Story = StoryObj; export const Primary: Story = { args: { className: 'w-[269px] h-[143px]', - children: ( - - ) + children: } }; diff --git a/frontend/src/app/components/OverViewAgentsCard.tsx b/frontend/src/app/components/OverViewAgentsCard.tsx index 3c94d874b..53e67fb8c 100644 --- a/frontend/src/app/components/OverViewAgentsCard.tsx +++ b/frontend/src/app/components/OverViewAgentsCard.tsx @@ -7,20 +7,13 @@ export interface IOverViewAgentsCard { inactiveAgents: number | string; } -export default function OverViewAgentsCard({ - title, - totalAgents, - activeAgents, - inactiveAgents -}: IOverViewAgentsCard) { +export default function OverViewAgentsCard({ title, totalAgents, activeAgents, inactiveAgents }: IOverViewAgentsCard) { return (
-
- {activeAgents} Running -
+
{activeAgents} Running
diff --git a/frontend/src/app/components/OverViewCard.tsx b/frontend/src/app/components/OverViewCard.tsx index 2437f3247..9354e1366 100644 --- a/frontend/src/app/components/OverViewCard.tsx +++ b/frontend/src/app/components/OverViewCard.tsx @@ -10,12 +10,7 @@ export interface IOverViewCard { className?: string; } -const OverViewCard: React.FC = ({ - title, - value, - children, - className -}) => { +const OverViewCard: React.FC = ({ title, value, children, className }) => { return ( @@ -51,8 +43,7 @@ export default function OverViewGraphCard({ )}
- {changeRate} %{' '} - (24 hours) + {changeRate} % (24 hours)
diff --git a/frontend/src/app/components/OverViewTriggerCard.tsx b/frontend/src/app/components/OverViewTriggerCard.tsx index da20784dd..1c754eba2 100644 --- a/frontend/src/app/components/OverViewTriggerCard.tsx +++ b/frontend/src/app/components/OverViewTriggerCard.tsx @@ -11,12 +11,7 @@ export interface IOverViewAgentsCard { inactive: number | string; } -export default function OverViewTriggerCard({ - title, - total, - active, - inactive -}: IOverViewAgentsCard) { +export default function OverViewTriggerCard({ title, total, active, inactive }: IOverViewAgentsCard) { return ( diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 56112914f..bf39fc898 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -51,11 +51,7 @@ export default function RootLayout({ {process.env.NEXT_PUBLIC_UMAMI_ENABLED ? ( - + ) : ( <> )} @@ -86,11 +82,7 @@ export default function RootLayout({
- +
{/*