HomeAboutResearchProducts

Autonomous DCA Agent

Autonomous-Finance
AO
AgentFi
AIA
DCA

May 9, 2024

Autonomous DCA Agent

1. DCA Agent by Autonomous Finance

Introduction

This software functions as an Autonomous Investment Agent (AIA) designed to execute trades using a dynamic dollar-cost-average (DCA) investment strategy across various liquidity pools within the AO ecosystem. The agent automatically buys a base token at predetermined, user-configurable intervals using a consistent amount of a quote token for each transaction.

A standout feature is its functional autonomy, unique to the AO network, which is essential for autonomous functionality. Once initiated, it runs autonomously on the AO platform without the need for off-chain signals or human intervention.

The management interface of the DCA agent is facilitated through a frontend hosted on the Arweave's Permaweb and operates without the need for trusted intermediaries.

With its design and technology stack, the DCA Agent is the first fully decentralized and uncensorable AgentFi application of its type. Once deployed, agents will operate as programmed and can only be deactivated by their creators.

How to use

Visit the latest DCA Agent on Permaweb or fork the code on GitHub.

Begin by configuring your agent upon creation:

Screenshot of Agent Configuration

Configuration involves setting DCA parameters, defining an acceptable slippage range, and selecting the DEX (AMM liquidity pool) for the swaps.

Your agent will execute DCA buys provided there are sufficient quote token funds deposited:

Screenshot of DCA Buy Execution

The dashboard offers agent owners:

  • Detailed configuration and asset information
  • Options to withdraw and liquidate assets
  • Insights into the history and performance of the agent
  • Lifecycle management actions such as pausing, retiring, or transferring the agent

Owner Control

  • Your assets are securely held by the agent and remain 100% liquid, permitting withdrawals at any time.
  • As a proof-of-concept, the agent's ownership can be transferred to another party.
    • Owners may be regular users or other AO processes, enhancing the DCA agents' compatibility with multisig setups and their composability with other agent types.

Our Vision

The DCA agent acts as a basic blueprint for further development, aimed at facilitating more sophisticated multi-agent configurations. While in its initial stages and continuously improving, it performs most of the application-specific heavy lifting, providing visionary builders with a significant head start.

As the AO's Autonomous Finance ecosystem evolves, we anticipate the development of more advanced and intricate designs for the DCA agent and AIAs in general. With its high configurability and composability, the DCA agent is designed to be easily customized and integrated into multi-agent configurations. Imagine an AIA that includes a DCA component as part of its strategy by simply integrating a DCA agent.

Below are some thoughts on the future capabilities and composability of the DCA agent.

Advanced Trading Capabilities

  • The DCA agent is capable of executing advanced trading strategies based on highly customizable parameters:

    • Price-action-based buys (% deviation).
    • Fixed intervals and time-weighted actions (buy more when the price is lower).
    • Data-driven profit-taking and profit-reinvestment signals.
  • The current iteration of the DCA agent operates using a single liquidity pool for swaps. However, plans for diversifying liquidity sources and developing a global RFQ system are underway. Such enhancements will not only automate the buying process but also ensure the aggregation of the best possible swap prices across DEXs and private market makers.

Enhanced Composability

The DCA agent is already highly composable, but it's possible to explore alternative designs for even greater flexibility. One such approach involves agents that never take custody of user funds. In this model, multiple specialized agents are granted permission to access user funds only when profitable opportunities arise. For example, a DCA agent might access permissioned funds, execute a profitable transaction, and then return the funds to the user's self-custodied wallet.

This approach significantly enhances capital efficiency. Users aren't required to lock their capital into a specific strategy or pool; instead, they authorize agents to use a predefined amount of funds, which remain accessible to the users until utilized for a profitable action by any of the agents.

Technical Insights and Lessons Learned

Through the initial development of the DCA agent, we have gained a deeper understanding of the AO platform, encompassing both its capabilities and limitations. This process has also led to significant insights into effective design patterns suitable for DCA Agent development.

We will elaborate on these insights in an upcoming article.

Functional Scope

This project serves as a foundational example within the emerging AgentFI domain, illustrating how autonomous investments can be managed by a trustless, fully on-chain process without the need for third-party tools or infrastructure. It aims to provide a starting point for the community to build more complex agents.

Our intention is to iterate upon this base with more robust designs, using advanced patterns that will be described in further articles and blueprints.

The current iteration of the project includes:

  1. A minimum viable DCA Agent.
  2. A Backend Process on AO that facilitates convenient aggregation and management of DCA agents.
  3. A basic Web Interface for managing agents, enabling deployment as a static website on the permaweb.

Looking forward, we plan to expand the functionality to include tighter integration with DEXI - our application that allows DeFi users to navigate and discover the AO ecosystem.

Extendability

Future iterations of the agent could include capabilities for:

  1. Extended Configurations at fixed time intervals, such as liquidity provision strategies and rebalancing of a top-5-tokens index.
  2. Complex tasks triggered by specific external signals at irregular times; these would efficiently reuse the basic functionalities like deposit, withdrawal, liquidation, and lifecycle management (pause, retire, transfer ownership).
  3. Operating as an investment fund, utilizing various strategies and catering to a group of stakers rather than a single owner.

Providing a visualization of the DCA agent's activity history, complete with links to AOLINK, would allow owners to inspect each swap via its unique ID.

  • Implementing this feature would involve persisting IDs of final credit notices associated with DCA buys and swap backs, as the agent notifies the backend of completed swaps.

Essential Automation - AO Cron

The AO platform uniquely supports automation through native crons, distinguishing itself in the realm of dApp development. Developers can set up cron jobs that operate entirely on-chain, a crucial feature for achieving full autonomy in financial agents.

Given the ongoing efforts to enhance the power and reliability of cron jobs, it's premature to finalize best practices. However, we can share one insight that seems pertinent at this current stage.

Direct vs. Indirect Trigger

We have devised a pattern known as Direct Trigger for utilizing crons in straightforward scenarios where the process requires only one action that needs regular execution. In such cases, the process is tagged at the time of creation (or spawning) to receive cron ticks. The frequency of these ticks is determined by a single parameter.

direct_trigger

Direct Trigger

// spawning cron process from @permaweb/ao-connect

const signer = createDataItemSigner(window.arweaveWallet)

const process = await ao.spawn({
  module: "SBNb1qPQ1TDwpD_mboxm2YllmMLXpWw4U8P9Ff8W9vk",
  scheduler: "_GQ33BkPtZrqxA84vM8Zk-N2aO0toNNu_C-l-rawrBA",
  signer,

  // -- configure cron
  tags: [
    { name: "Cron-Interval", value: "1-minute" },
    { name: "Cron-Tag-Action", value: "TriggerSwap" },
  ],
})

// Turn on monitoring to activate cron triggering
await monitor({
  process,
  signer,
})

/*
 * As a result, our process starts receiving cron ticks once per minute.
 * If it has a handler that matches { "Action" : "TriggerSwap" }
 * these ticks will execute said handler.
 */
-- process.lua

Handlers.add(
  "triggerSwap",
  function(msg)
      -- ensure only authentic cron ticks are matched
      return Handlers.utils.hasMatchingTag("Action", "TriggerSwap")(msg)
          and msg.Cron
  end,
  function(msg)
    -- ... perform swap
  end
)

In scenarios that demand multiple types of actions to be executed regularly, we employ an Indirect Trigger pattern. This approach uses separate processes that function as trigger-notifiers. These are simple cron-powered agents that remind the main process to perform its tasks.

A practical use case for this is a DCA agent designed to manage retrying failed message deliveries. This agent:

  • Executes DCA buys at regular intervals.
  • Monitors its own state at these intervals to handle any requests it made that did not receive responses within a predetermined timeout.
direct_trigger

Indirect Trigger

-- swap_notifier.lua

Agent = Agent or nil -- set after creation via dedicated handler

Handlers.add(
  "cron",
  function(msg)
      return Handlers.utils.hasMatchingTag("Action", "Cron")(msg)
          and msg.Cron
  end,
  function(msg)
    ao.send({Target = Agent})
  end
)

-- retry_notifier.lua

-- analogous to swap_notifier.lua, but handler execution is
-- ...
    ao.send({Target = Agent})
-- ...


-- process.lua

SwapNotifier = SwapNotifier or nil -- set via handler after notifier is spawned
RetryNotifier = SwapNotifier or nil -- set via handler after notifier is spawned

Handlers.add(
  "triggerSwap",
  function(msg)
      return msg.From == SwapNotifier
  end,
  function(msg)
    -- ... perform swap
  end
)

Handlers.add(
  "checkQueue",
  function(msg)
      return msg.From == RetryNotifier
  end,
  function(msg)
    -- ... perform check on retry queue
  end
)

When implementing the Indirect Trigger pattern, it becomes crucial for the main process to recognize the identities of its trigger-notifiers, ensuring it cannot be activated by impostors.

Additionally, this pattern offers a strategic advantage in updating cron tick intervals, which is not natively possible within a single-cron-action scenario. Although cron intervals are set as process tags at inception and cannot be changed afterward, the use of Indirect Triggers allows for flexibility. By simply spawning a new trigger-notifier with a different interval, it becomes trivial to adjust the timing of the tasks as needed.

Other Cron Takeaways

  • Cron processes need active monitoring to function correctly and ensure real-time triggering. The capability to monitor and unmonitor can effectively toggle real-time cron triggering on and off. Currently, monitoring can only be initiated or stopped through a process from AO Connect, not from within a Lua handler.

  • Occasionally, messages triggered by cron execution might slip past the MU, causing slight inconsistencies or delays in how triggers are executed. We anticipate improvements will lead to greater stability as we approach the mainnet launch.

AO Application Architecture

In the following sections, we will primarily explore the AO processes integral to our project. We may also highlight Frontend features or delve into implementation details where they add technical value.

Our project is structured around two critical AO processes essential for the DCA Agent application:

  1. Agent Process with Cron Trigger

    • Facilitates user-triggered and automated DCA buys, including:
      • Deposits
      • Withdrawals
      • DCA swaps
      • Liquidations (swap back and withdraw)
    • Isolates actions on assets through a state machine
    • Tracks fund balances in both tokens
    • Updates the AO Process (backend) about completed actions
  2. **AO Process (Backend) **

    • Manages registration and tracking of owner-associated agents
    • Tracks historical data for each agent through integration with the agent

These processes interact with the following components:

  • Quote Token
  • Base Token
  • AMM Pool (specific to the selected Quote and Base Token)

DCA Agent, Backend and related processes DCA Agent, Backend and related processes

Swaps

The diagram below illustrates the architectural flow for a DCA buy, where a swap from quote to base token occurs. This visualization helps to detail the steps and interactions within such transactions.

Steps for a DCA buy Steps for a DCA buy

A similar sequence of messages would be involved for swaps in the opposite direction (base to quote), which plays a crucial role in the liquidation of an agent’s assets.

The Agent Process securely holds funds, requiring deposits to facilitate DCA buys. Upon executing swaps, it retains the resulting assets, ensuring that all assets remain liquid (i.e., available for withdrawal by the user at any time).

Agent & Backend

Currently, the ao bakcend processand & the agent are tightly coupled. We envision standardizing both components such that they could be easily swapped with others while maintaining system compatibility. However, given our specific goal to integrate these agents with DEXI, we have decided to defer the standardization of the DCA agent and backend to future iterations.

Frontend Architecture

The frontend leverages Arweave Gateway for interactions with AO processes. It engages with:

  • The agent to perform actions.
  • The agent backend process to retrieve all associated agents.
  • DEXI to access information on available AMM pools and obtain the latest prices.

Overview of Frontend Architecture Overview of the Frontend Architecture

The web frontend facilitates basic agent management and interaction, supporting multiple agents per user. To maintain independence from traditional web2 backends that and enhance decentralization, the UI is designed to be deployed as a static website on the permaweb. All persisted data resides on AO, maintained within the state of AO processes.

Simple & Secure Backend Registration

For simplicity and to streamline operations, agents are spawned independently and their owners can register them with the backend without any access control. This setup is secure because the backend requires no access control in its current configuration; it cannot be effectively abused. Registered agents are associated with their owner's account, preventing the possibility of polluting other users' data with unauthorized registrations.

An alternative approach could involve a factory setup where agents are directly spawned from the backend. For more details on our decision against this method, see the discussion here.

-- backend.lua

Handlers.add(
  'registerAgent',
  Handlers.utils.hasMatchingTag('Action', 'RegisterAgent'),
  registration.registerAgent
)

-- registration.lua

mod.registerAgent = function(msg)
  local agent = msg.Tags.Agent
  -- ...
  local sender = msg.From

  -- 👇 registration only affects sender-related data
  -- => ok to do without access control
  RegisteredAgents[agent] = msg.From
  AgentsPerUser[sender] = AgentsPerUser[sender] or {}
  AgentInfosPerUser[sender] = AgentInfosPerUser[sender] or {}

  table.insert(AgentsPerUser[sender], agent)
  table.insert(AgentInfosPerUser[sender], {
     -- ...
  })
  response.success("RegisterAgent")(msg)
end

The Choice Against a Factory

While it's feasible to design the backend as a factory that spawns agents, we found this approach to be more cumbersome for users. Consequently, we opted for a non-factory setup. However, if the need arises for access-controlled registration, reverting to a factory-based approach could be a viable solution.

Here is how agent creation and registration would function in the factory version:

  1. Creation: The creator (user) spawns an agent via the factory, including a unique creation-nonce.
    • The factory attempts to pre-register the agent process. This registration should fail if the creation-nonce is not unique.
  2. Confirmation: The creator polls the backend process until it confirms that creation with the specified nonce was successful.
    • Upon confirmation, the creator obtains the spawned agent ID.
  3. Initialization: The creator initializes the agent, thereby becoming its owner.
    • The agent is fully initialized, has its owner set, and performs complete registration with the backend, which now recognizes the agent as a trusted process.

Changes on the frontend:

  • The user interacts with the backend to create a new agent.
  • The user polls the backend for confirmation of the agent's pre-registration.
  • Registration and initialization processes are completed and confirmed in a single step.

This approach ensures a streamlined process that integrates security and user convenience, should there be a future shift towards a factory setup.

Process Ownership, Custody & Composability

In our implementation, users typically start as the owners of an agent, but ownership can be transferred at the initialization phase, allowing the owner to also be another process. This flexibility supports various operational scenarios, such as:

  • An autonomous agent utilizing this one to perform DCA buys.
  • A multisig wallet process employing this agent for DCA transactions, effectively serving multiple co-owners simultaneously.

Alternative Ownership Model: An interesting alternative ownership model involves an agent that does not hold funds itself but is authorized to access funds from another source, such as a different process that maintains these funds.

Use Case Example: As an owner, I could utilize multiple agents to manage investments without needing to distribute funds among them. This is particularly useful if the agents do not invest at fixed intervals but respond to unpredictable signals. Such a setup not only reduces operational overhead but also leads to a more efficient usage of available funds, enhancing the overall capital efficiency.

Ownership & Initialization - AO vs. Ethereum

In AO, every process has a global variable Owner, which is automatically assigned when the process is created. The owner of a process can be either a wallet or another process.

  • When a process is spawned through a message signed by a wallet, as is the case with our DCA Agent via the frontend, the wallet ID becomes the owner of the process.
  • If a process is initiated by another process, then the initiating process automatically assumes ownership.

Ownership can be transferred by updating the value of Owner. We incorporate this mechanism into our application by enhancing our Handlers with functionality similar to the onlyOwner modifier commonly used in Solidity smart contracts, ensuring actions are restricted to the process owner.

-- process.lua

Handlers.add(
  "retire",
  Handlers.utils.hasMatchingTag("Action", "Retire"),
  function(msg)
    permissions.onlyOwner(msg)
    lifeCycle.retire(msg)
  end
)

...

-- permissions.lua

mod.onlyOwner = function(msg)
  assert(msg.From == Owner, "Only the owner is allowed")
end

Initialization Differences: AO Processes vs. Ethereum Smart Contracts

Unlike Ethereum smart contracts, AO Processes do not have a constructor. Therefore, any initialization necessary for the operation of an agent must be performed through a subsequent message. This message functions like a regular message but is specifically designed to update the agent's state.

To manage initialization status, we employ an IsInitialized flag. Processes that are not yet initialized will refuse to handle (almost) any message. They are configured to only allow:

  • Querying the current owner.
  • Checking the current process status.
  • Executing the initialization itself.

This selective message handling is facilitated by placing a "catch-all" message handler at the top of the Handlers list. This setup acts as a gatekeeper, ensuring that the process cannot perform or respond to any actions until it has been properly initialized.

-- msg to be sent by end user or another process
Handlers.add(
  "initialize",
  Handlers.utils.hasMatchingTag("Action", "Initialize"),
  lifeCycle.initialize
)

-- ! every handler below is gated on Initialized == true
Handlers.add(
  "checkInit",
  function(msg)
    return not Initialized  -- the match is positive if we're not initialized yet
  end,
  response.errorMessage("error - process is not initialized")
)

Owner Initialization and Security in DCA Agent Application

In our DCA agent application, while the user who creates the agent is typically intended to be the owner, we aim for maximum composability and setup flexibility. This approach allows for other processes to be designated as DCA agent owners. Moreover, we are exploring future integrations with DEXI, enabling users to create agents through the DEXI UI potentially using trusted agent factories. This setup would ideally separate the roles of who spawns the process, who becomes its proper owner, and who performs the initialization.

In smart contract development, it's often crucial to have strict access control over initialization, ensuring only the designated owner can perform it. However, we chose a more relaxed approach to maintain simplicity.

To simplify our design, we've merged the "proper owner initialization" and "DCA config initialization" into a single "Initialize" action. Until this initialization is performed, the agent remains "useless." The sender of the "Initialize" message automatically becomes the proper owner of the process.

This design choice does theoretically open up the possibility for malicious sabotage. If someone else initializes the process before the intended owner, they could take control. Nevertheless, we assess that this risk of front running is significantly lower on AO compared to other global state VM platforms. For more on the security aspects and why this concern is less prominent on AO, see our detailed security discussion.

State Management

In our system, all crucial data is stored in the process state directly on-chain, eliminating the need for queries via Arweave gateways.

From the backend process, we can effortlessly retrieve a list of all available agents per user, including essential information such as a history of buys and sells.

To ensure atomicity and isolation for the multi-step actions performed on assets (such as DCA buys, withdrawals, and liquidations), our agent loosely employs a state machine model, functioning similarly to a mutex. This model also keeps track of related successes and failures.

IsSwapping = IsSwapping or false
IsWithdrawing = IsWithdrawing or false
IsDepositing = IsDepositing or false
IsLiquidating = IsLiquidating or false

LastWithdrawalNoticeId = LastWithdrawalNoticeId or nil
LastDepositNoticeId = LastDepositNoticeId or nil
LastLiquidationNoticeId = LastLiquidationNoticeId or nil
LastSwapNoticeId = LastSwapNoticeId or nil

LastWithdrawalError = LastWithdrawalError or nil
LastLiquidationError = LastLiquidationError or nil
LastSwapError = LastSwapError or nil

A more advanced state machine would facilitate tracking the progress of each specific multi-step action. This enhancement would be particularly beneficial for developing a UI that provides users with accurate progress updates on any particular action.

Currently, the isolation provided by our agent is quite basic. If an attempt is made to perform an asset action while another is already in progress, the agent simply returns an error.

mod.checkNotBusy = function()
  local flags = json.encode({
    IsSwapping = IsSwapping,
    IsDepositing = IsDepositing,
    IsWithdrawing = IsWithdrawing,
    IsLiquidating = IsLiquidating
  })
  if IsDepositing or IsWithdrawing or IsLiquidating or IsSwapping then
    response.errorMessage(
      "error - process is busy with another action on funds" .. flags
    )()
  end
end

Here is an example of how the check is used to achieve the mutex-like behaviour:

Handlers.add(
  "withdrawQuoteToken",
  Handlers.utils.hasMatchingTag("Action", "WithdrawQuoteToken"),
  function(msg)
    permissions.onlyOwner(msg)
    status.checkNotBusy()
    progress.startWithdrawal(msg)
    withdrawals.withdrawQuoteToken(msg)
  end
)

A more optimal behavior would be to queue incoming messages that trigger asset actions, rather than rejecting them. This change would alleviate the need for the triggering entity to retry the action, enhancing user experience and system efficiency.

Keeping The Frontend in Sync

While developing with Javascript for the AO platform, it's important to note the absence of straightforward methods to subscribe to AO events—a feature commonly utilized in EVM development via smart contract events and Javascript libraries like web3 and ethers.

In the case of our DCA Agent UI, an approach involving polling the Arweave Gateway endpoints for AO messages with specific tags could have been possible. However, given our agent's substantial state management within its process memory, we opted to poll updates directly from our agent process via dryRun calls. This choice allows our UI to more accurately reflect the real-time state of the agent, despite requiring additional overhead in managing React hooks to detect state changes. This complexity is currently manageable given the complexity of the agent.

Mirroring a Remote Source of Truth

For the DCA Agent, and particularly for more sophisticated agents, it's crucial to maintain a real-time awareness of data pertinent to their operation that is persisted externally. For instance, an agent needs to know its own balance of a specific token to make timely decisions based on incoming signals.

While an agent might track such state locally, it must also be vigilant about potential inconsistencies due to the absence of ordering guarantees inherent in distributed computing platforms. Having this data readily available helps in making effective real-time decisions, though the agent must continually check for and reconcile any discrepancies.

Motivation & Example

Consider an example where an agent is programmed to respond to two types of signals, A and B. Signal A represents exceptional, highly profitable opportunities, while Signal B occurs regularly. When Signal A is detected, the agent should execute a specific swap using its funds. However, these funds are also earmarked for activities triggered by Signal B. The challenge lies in maximizing fund usage for Signal A while ensuring sufficient reserves remain for Signal B.

This scenario underscores the necessity for the agent to maintain an accurate awareness of its token balance, which might fluctuate due to unrecorded deposits or potential withdrawals by third parties, should AO token processes ever support approvals similar to ERC20 tokens.

Although this specific challenge has only arisen once, it represents a real risk of loss of opportunity and could impact the agent's competitive edge. Therefore, addressing it is crucial.

Solution: To manage this, the agent listens for any Credit-Notice or Debit-Notice from the token process, responding with a {"Action" : "Balance"} message to update its balance. It then updates its internal balance based on the response tagged with "Balance."

However, due to the lack of guaranteed message ordering, there's a risk that responses may not correspond to the most recent balance inquiries. This could erroneously affect the locally mirrored balance.

Our approach mitigates this by tracking the timestamp of each balance update. If a new update's timestamp is not later than the last, the agent disregards this update, ensuring the integrity and accuracy of the balance information.

mod.latestBalanceUpdateBaseToken = function(msg)
  -- balance responses may come in any order, so we disregard delayed ones (possibly stale values)
  if (msg.Timestamp > LatestBaseTokenBalTimestamp) then
    LatestBaseTokenBal = msg.Balance
    LatestBaseTokenBalTimestamp = msg.Timestamp
    ao.send({ Target = Backend, Action = "UpdateBaseTokenBalance", Balance = msg.Balance })
  end
end

AMM Related Security

1. Front Running

In typical decentralized finance platforms, the predictable nature of swap timings and amounts by a DCA agent might lead to front running concerns, especially within global state VMs where transactions can be precisely ordered to the benefit of an attacker, enabling tactics like sandwich attacks. However, on AO, the scenario is quite different.

Front running is considered unlikely on AO due to the lack of guarantees around transaction ordering. While an attacker might attempt to execute a sandwich attack by timing their transactions around the DCA agent's scheduled swaps, such maneuvers are visible to others who could potentially front run the attacker themselves, thus negating the profitability of the attack. This inherent risk makes front running a less viable strategy on AO compared to traditional DeFi settings.

2. Price without an Oracle

When determining the expected output of a swap, the AMM pool itself can serve as a reliable source for pricing on AO. This contrasts with EVM-based systems, where relying on the pool's price during swap executions could expose users to exploitation by front runners.

On platforms with global state VMs, DeFi applications typically rely on external oracles to mitigate risks associated with manipulated pricing. These oracles themselves then become potential attack vectors. Given the reduced feasibility of front running on AO, the necessity for external price oracles diminishes, allowing us to rely directly on AMM pool prices without adopting the external oracle best practices common in other DeFi environments.

Challenges and Solutions

One of the key challenges in designing the agent process revolves around the coding and handling of incoming messages. Correctly identifying the purpose of each message is crucial for appropriate response actions.

Example of Message Handling Complexity:

A Credit-Notice received from the Quote Token process might indicate several scenarios:

  • A successful deposit.
  • A successful swap from base to quote.
  • A refund due to a failed swap from quote to base.

To address this complexity, we have opted to enhance the granularity of our matching functions within the handlers. This approach allows us to:

  • Precisely identify the intent of each message.
  • Maintain cleaner and more efficient execution within the handler functions.

This differentiation in message handling ensures that each action is processed accurately, enhancing the overall robustness and clarity of the agent's operations.

-- process.lua

--[[
    This file defines all the handlers.
    It should give a clear overview for understanding
    everything the process can do in terms of handling messages
--]]

--[[
    The handler below matches Credit-Notice messages from the BaseToken,
    but is not the only one doing so ==>> we use the patterns.continue()
    wrapper to allow for matching of other handlers, too
]]

Handlers.add(
  "balanceUpdateCreditBaseToken",
  patterns.continue(function(msg)
    return Handlers.utils.hasMatchingTag("Action", "Credit-Notice")(msg)
        and msg.From == BaseToken
  end),
  balances.balanceUpdateCreditBaseToken
)

--[[
    The handler below is another Credit-Notice matcher for messages from
    BaseToken. It uses an imported function to perform a match,
    but keeps the matching of the 'Action' Tag right here in explicit form,
    so that we can rapidly find all the 'Credit-Notice' handlers of the process.
    Furthermore, using the patterns.continue() wrapper has the effect that this
--]]

Handlers.add(
  'swapBackErrorByPool',
  function(msg)
    return Handlers.utils.hasMatchingTag('Action', 'Credit-Notice')(msg)
        and liquidation.isSwapBackErrorByRefundCreditNotice(msg)
  end,
  progress.concludeLiquidationOnErrorByRefundCreditNotice
)

-- liquidation.lua

mod.isSwapBackErrorByRefundCreditNotice = function(msg)
  return msg.From == BaseToken
      and msg.Sender == Pool
      and msg.Tags["X-Refunded-Transfer"] ~= nil
end

Focus on the Essential

In developing our agent, we strategically decided to omit some best practices that are typically involved in creating a production-grade agent on AO. This was done to concentrate our efforts on addressing the more challenging issues effectively.

Areas for Future Enhancement

  1. Input Validation:

    • Currently, the agent performs minimal validation on inputs, such as checking that address inputs are non-empty strings. Implementing more advanced validation could enhance security and reliability.
  2. Type Support for Messages and Handlers:

    • The agent and backend process currently lack robust type support for Messages and certain Handler utilities. Enhancing type support could eliminate false warnings and improve code reliability (msg.From, PatternFunction, etc.).
  3. User Experience on the Web Frontend:

    • There is significant potential to improve the user experience, particularly in:
      • Optimizing the polling for balances and current prices.
      • Implementing optimistic updates for balances following successful asset actions.
  4. Start/Pause Functionality and State Transitions:

    • The Start/Pause functionality and transitions from 'No Funds' to 'Active' do not trigger immediate actions, which means "missed" swaps due to insufficient funds are not compensated with delayed swaps. While potentially desirable, this functionality could alter the DCA nature of the agent:
      • Users could manipulate the frequency of swaps by frequently stopping and starting the agent, affecting performance metrics based on the assumption of equidistant swaps.
      • Although there are potential solutions to these issues, they were deemed too ambitious for inclusion in the current blueprint.
  5. Defensive Programming:

    • Enhancing the robustness of the agent process by catching execution errors and properly finalizing actions could maintain a consistent state across the state machine flags, thereby increasing the resilience of the system.

These omissions were made with a focus on delivering a functional prototype that addresses the core functionalities while acknowledging the areas where future enhancements could be made.

Final Note

Thank you for taking the time to explore the intricacies of our DCA agent development on AO. This article aims to provide transparency in our development process, sharing both our current achievements and the areas we're looking to enhance. We hope this insight fosters a deeper understanding of the challenges and potential solutions in decentralized Agent development, pushing the AgentFi domain.

Stay tuned for more updates as we continue to refine our technology and explore new possibilities. We're committed to advancing the field and sharing our learnings along the way.

For further information, to contribute, or to stay connected with our progress, please visit the following resources:

  • Read more on our Blog
  • Contribute or explore our projects on GitHub
  • Follow us on Twitter for real-time updates

We're excited about what the future holds. Expect more detailed articles and updates as we push the boundaries of what's possible with decentralized finance!

HomeAboutResearchProducts

© 2024 Autonomous Finance. All rights reserved.