logo
Ation - The Distrubuted DCA

Ation - The Distrubuted DCA

Subject
Web3
Type
Technical Paper
White Paper
Author
Tuck
Status
Published
Published
November 30, 2023
💡
STATUS: ALPHA PHASE Ation is currently going though alpha stage testing and building. Refining user interactions, gas costs and reinvestment systems. There will be a beta released on ARBITRUM GOERLI before public release on ARBITRUM MAINNET
 

TL;DR

Dollar Cost Averaging (DCA) is a renowned strategy for mitigating market volatility by consistently investing a fixed sum into an asset at regular intervals. This approach has been a cornerstone in traditional investment platforms, predominantly managed by centralized exchanges.
Ation introduces a groundbreaking shift to this narrative by bringing DCA to the DeFi realm. Our platform leverages the advancements in layer two technologies, which have revolutionized transaction speeds and costs.
Ation is not just an alternative; it's a pioneering move in DeFi, offering a native DCA protocol that's faster, more cost-effective, and aligned with the ethos of decentralization. Our aim is to empower users to strategically invest in selected assets with customizable intervals, starting with a USD stablecoin. We utilize innovative transaction bundling techniques to minimize upfront costs, making DCA accessible and efficient for every DeFi enthusiast.

Introduction

The finance industry, historically perceived as opaque and exclusive, is undergoing a transformation. Cryptocurrency and decentralised finance (DeFi) are at the forefront of this change, unlocking financial autonomy for a global audience. DeFi, in particular, has democratised access to financial services, enabling anyone, anywhere, to manage their finances—from saving and spending to investing and lending.
However, a significant portion of traditional financial (TradFi) products still awaits integration into DeFi. While not all TradFi products align with the DeFi philosophy, there are essential tools, like Dollar Cost Averaging (DCA), that can immensely benefit the DeFi community. Ation is committed to bridging this gap, enhancing the DeFi ecosystem with tried-and-true financial strategies, reimagined for a decentralised world.

The Problem

While DCA has gained traction in the crypto and DeFi communities, its primary accessibility remains through Centralized Exchanges (CEXs). This reliance on centralized entities poses trust and security concerns, contradicting the foundational principles of web3 and diminishing the DeFi experience for many.
Additionally, the existing on-chain implementation of DCA faces challenges like contract security and the need for reliable time-based triggers. Addressing these concerns is crucial for a truly decentralized DCA solution that aligns with the ethos of blockchain and user sovereignty.

The Solution

Ensuring that the contract system is secure, tested and opensource is vital. This will be done by creating the most simplified contract, reducing vectors of attack but also helping to increase the gas efficiency.
There also isn't a way to keep time-locked transactions scheduled all on-chain. Thankfully we have some proven ways that we can provide this. Using oracles and distributed computing we can safely trigger transactions and commands from a trusted endpoint.

User Flow

  1. Deploy a DCA Account: Users initiate their journey by creating a DCAAccount from our factory.ddd
  1. Craft a DCA Strategy: Users define their investment plan, selecting base and target currencies and setting the execution interval.
  1. Fund the Contract: Users deposit their base currency into the contract to enable their DCA strategy.
  1. Engage with Executor: Users subscribe to our DCAExecutor for automated management.
  1. Automated Execution: The DCAExecutor seamlessly handles the rest, ensuring the user's strategy is followed meticulously.
notion image

Theory

At the core of Ation lies a user-centric design where individuals can set up a DCA account, define their investment strategies, and integrate with an executor for automation. We're also exploring a 'reinvest' feature, enabling users to reinvest their earnings into reputable lending protocols. While the execution mechanics remain consistent across different implementation models, our focus is on a user-friendly and secure experience. This involves a balance of robust contract security, ease of strategy customization, and seamless integration with execution services.
Our concept centers around a simple yet effective mechanism: a user deploys a DCA account from our factory contract, selects a strategy involving two ERC20 tokens (typically starting from a stablecoin), and sets a preferred interval for asset swapping. They then subscribe to an executor, which automates the strategy execution. To further enhance the service's value, we're integrating a 'reinvest' option, allowing users to deposit their target tokens into established lending protocols for additional yield.
The strategies are stored within the DCA Account, allowing for multiple strategies with varying tokens and timings. Access control is streamlined, enabling only the owner to create, modify, and subscribe to strategies, while ensuring strategy execution can only be triggered by the appointed executor address.
The Executor is a hybrid on-chain and off-chain service. The on-chain component serves as a verifiable contract for managing account subscriptions and executing on-chain actions. Off-chain, a microservice triggers strategy executions at set intervals. This combination, while not fully decentralized, maintains the immutability and security of smart contracts, along with the efficiency and cost-effectiveness of centralized servers.
The DCA Executor contract acts as a registry and gateway, managing account subscriptions, and serving as the sole address capable of triggering the execution function on the account. This contract has no control over user funds or strategy, ensuring that all critical operations are contained within the account contract itself.
Our off-chain service is crucial for triggering strategy executions and tracking account subscriptions. Despite being the only centralised element of Ation, we've minimised its attack surface and are working towards enhancing redundancy and decentralisation. Future developments include migrating the service to decentralised computing platforms like ICP and CUDOS, which, while currently more expensive, will add credibility and reliability to our system. Additionally, securing the Executions externally only account (EOA) with a multi-signeture contract account will further fortify our platform's integrity.
In summary, Ation’s theory is based on a balance of decentralised security and centralised efficiency, aiming to provide a reliable, user-friendly, and innovative DCA experience in the DeFi ecosystem.
Building the protocol in this modular way allows a user to utilise the DCAAccount system but subscribe to alternative or their own execution service. This allows for the protocol to expand and become further more immutable.

Build Options

Our approach to developing Ation's infrastructure considers two primary methodologies: the all-in-one contract model and individual contract accounts for users. The all-in-one model centralizes logic and funds, facilitating efficient transaction management.
However, it also poses higher security risks. On the other hand, individual contract accounts offer enhanced user control and transparency, with separate logic and assets for each user, albeit at the expense of higher transaction costs. After careful consideration of these trade-offs, we've chosen to prioritise security and user autonomy by adopting the contract account model for our initial product launch.
This decision aligns with our commitment to providing a secure, decentralised, and user-centric DCA experience.

All-in-one

Creating a master contract which holds all the logic and funds to execute the onChain exchanging and management of strategies. A user creates an ‘account’ within the contract to manage their strategies. Building in this way allows for easier and more efficient bundling of transactions and management of strategies. It does create a higher chance of attack and incurs more risk, as all of the funds are in a single place.
It also does not fully align with the ethos of decentralising and putting control in the hands of the user. This method would mean that each user was totally in the hands of a select few account controllers.

Contract Accounts

Building a DCAAccount contract that can be deployed from a factory that acts as the user's DCA account, holding the logic and assets. It brings a little more control for each individual user, allowing for easy turning on and off of the service. It also stops the mingling of funds and a lot clearer overview of assets. This method also creates the ability for the user to transfer the DCAAccount between other EOAs.
This method will cause higher transaction fees as there would be more calls between contracts to bundle, or each strategy would execute separately. It does, however, open up the ability for users to host their own Execution Service, changing the allowed address in the CA and running a node, removing fees. - The fees system will be attached to the execution address, returning the set amount of fee to that address. When switching the service off and adding a custom address, this will redirect the fees to that address, thus funding it for future transactions.
This method sadly reduces the ability on bundle transactions onChain. We can bundle executions within the Executor service, but doing this in the contract can introduce wider error boundaries with the possibility of a reverted transaction across multiple account executions.

Execution

In developing the MVP, we've opted for the Contract Account approach, which aligns with our priorities of security, decentralisation, and user independence. This model not only enhances the security of individual investments but also empowers users with greater control over their strategies.
Moreover, it facilitates community-driven adaptations and configurations, fostering an ecosystem where users can contribute to and benefit from the collective development of the platform.

OnChain

To facilitate the Ation DCA we aim to keep all of the control and storage onChain, allowing for immutability past the Ation team. There are 3 main contracts that we will build and deploy along with a set of library’s witch will be shared across Accounts allowing for reinvestment interactions.
We will implement upgradability for for our library contracts and the DCAExecutor, allowing for us to continue efficiency and security upgrades. The DCAAccount, at this time, will not be upgradable but we will, if needed in the future, create migration solutions if there is the need for a V2 DCAAccount.
Once deployed the ownership of the DCAExecutor and DCAFactory will be transfered to a multi signeture wallet.

Protocol Fees

To keep the Ation DCA running we will charge fees on each execution. These fees are only applied to executions that are subscribed to out DCAExecutor. The fee will be means tested so that we can allow for covering the gas costs of executions, maintenance and running of the executor service and admin fee for maintaining the website, protocol and continue building and enhancing.
The fee data will be stored within the DCAExecutor contract, available for anyone to read. It will be passed on to the DCAAccount on each execution for the fee to be removed from the base token just prior to the DEX swap. It will be a percent, at the moment we are looking at 0.3% of each pre-swap execution.
The fees when withdrawn from the DCAExecutor will automatically split to the set fee split and for the executor service wallet the token will be exchanged for the chains native token for gas expenses.

DCAExecution

In the Executor contract will be very simple logic for a few public facing functions. Routed behind an ‘Admin’ role (will be moved to a multi), it holds the fee data, fee distribution and execution list.
This contract should only be the gateway and registry for DCAAccounts that want to utilise and subscribe to the Ation executor. It will have limited functionality to return data from the contract, any anylitics that are needed from it should use the emitted events.
Interface
/** * @notice emitted when the default Executor service address is changed * @param newExecutionEOA_ {address} the new address of the Executor Service EOA or Multi * @param changer_ {address} address of the wallet implementing change */ event ExecutionEOAAddressChange( address indexed newExecutionEOA_, address changer_ ); /** * @notice Emitted once a strategy has finished executing * @param account_ {address} Address of the DCAAccount * @param strategyId_ {uint256} ID of teh strategy executed */ event ExecutedDCA(address indexed account_, uint256 indexed strategyId_); /** * @notice Emitted when a new strategy subscribes or unsubscribes to the executor * @param DCAAccountAddress_ {address} address of the DCAAccount subscribing * @param strategyId_ {uint256} ID of the strategy to (un-)subscribe * @param strategyInterval_ {Interval} Interval state of how ofter to be executed * @param active_ {bool} wether the strategy is being subscribed (true) or unsubscribed (false) */ event DCAAccountSubscription( address indexed DCAAccountAddress_, uint256 indexed strategyId_, Interval strategyInterval_, bool indexed active_ ); /** * @notice Emitted each time the protocol fees are distributed * @param token_ {address} address of the token being distributed * @param amount_ {uint256} amount of the total token distributed */ event FeesDistributed(address indexed token_, uint256 indexed amount_); /** * @notice Called by a DCAAccount to subscribe a strategy to the DCAExecutor * @param strategy_ {Strategy} The full strategy data of the subscribing strategy * @return {bool} if the subscription was successful */ function Subscribe( Strategy calldata strategy_ ) external returns (bool sucsess); /** * @notice Called by the DCAAccount to remove itself from the executor * @param DCAAccountAddress_ {address} Address of the unsubscribing DCAAccount * @param strategyId_ {uint256} ID of the strategy being unsubscribed * @return {bool} If the unsubscribe completed */ function Unsubscribe( address DCAAccountAddress_, uint256 strategyId_ ) external returns (bool sucsess); /** * @notice Called by the external Executor service wallet only, triggers the specified strategy * @param DCAAccount_ {address} Address of the DCAAccount holding the strategy to execute * @param strategyId_ {uint256} ID of the strategy to execute */ function Execute(address DCAAccount_, uint256 strategyId_) external; /** * @notice Called by the external Executor service wallet only, triggers the specified strategy's * @dev testing for now, will execute a max of 10 strategies at a time * @param DCAAccount_ {address[]} Address of the DCAAccount holding the strategy to execute * @param strategyId_ {uint256[]} ID of the strategy to execute */ function ExecuteBatch( address[] memory DCAAccount_, uint256[] memory strategyId_ ) external; /** * @notice Distributes the acuminated fee's from the DCAExecutor * @dev will use the in-contract fee's data to split the funds and transfer to needed wallets. * @param tokenAddress {address} Address of the token in the fee's pool to be distributed */ function DistributeFees(address tokenAddress) external; /** * @notice Used by the Executor service to remove a strategy from the DCAExecutor * Used mostly for unfunded and failing accounts. * @param DCAAccount_ {address} Address of the DCAAccount to be unsubscribed * @param strategyId_ {uint256} ID of the strategy to be unsubscribed */ function ForceUnsubscribe( address DCAAccount_, uint256 strategyId_ ) external;

DCAAccount

The DCAAccount Contract is the heart of our user's DCA journey. It encapsulates the logic for executing trades and managing strategies. This personalised approach allows users to tailor their DCA strategies, including different token pairs and execution intervals.
This is the contract that will hold funds and the logic for any exchanges. Being deployed from the DCAFactory contract it will be verifiable and open allowing scrutiny.
For now we are building this account to only have a single executor, though it may be prudent to allow separate executors for each strategy, or the option to subscribe to separate executors on choose but falling back on the default executor.
The most complex part of the DCAAccount is the reinvest element. To allow for an expanding ecosystem of DeFi dApps and protocols we want to allow the access to these to grow but maintaining a safe environment for users to operate in.
Todo this we will build a selection of library’s that have defined interactions with some of the most reliable and upstanding protocols (Compound & Aave). The function set will allow the user to select a reinvest strategy that is within the library, or they can build their own from scratch. This will mean encoding the functionality into the Reinvest struct for the contract to decode and execute.
For this to run smoothly we need to ensure that these functions are not only calling the supply & withdraw functions on the reinvest protocols, but also tracking the interest bearing tokens returned to the liquidity provider.
Interface
/** * @notice Emitted when a strategy has been executed * @param strategyId_ {uint256} the id for the executed strategy * @param amountIn_ {uint256} amount received from the swap * @param reinvest_ {bool} wether the strategy reinvested or not */ event StrategyExecuted( uint256 indexed strategyId_, uint256 indexed amountIn_, bool reInvest_ ); /** * @notice Emitted when the DCAExecutor contract address is changed * @param newAddress_ {address} Address of the Executor contract */ event DCAExecutorChanged(address indexed newAddress_); /** * @notice Emitted when the Strategy is confirmed to be subscribed to an Executor * @param strategyId_ {uint256} ID of the strategy that has been subscribed * @param executor_ {address} Address of the Executor contract subscribed to */ event StrategySubscribed( uint256 indexed strategyId_, address indexed executor_ ); /** * @notice Emitted when a strategy has been unsubscribed from an Executor * @param strategyId_ {uint256} Id of the strategy being unsubscribed */ event StrategyUnsubscribed(uint256 indexed strategyId_); /** * @notice Emitted when a new strategy has been created * @param strategyId_ {uint256} Id of the newly created strategy */ event NewStrategyCreated(uint256 indexed strategyId_); /** * @notice Triggered by the assigned executor to execute the given strategy * @param strategyId_ {uint256} Id for the Strategy to be executed * @param feeAmount_ (uint16) amount of the strategy amount to be paid via fee (percent) * @return {bool} If the function was successful */ function Execute( uint256 strategyId_, uint16 feeAmount_ ) external returns (bool); /** * @notice Used by the account owner to setup a new strategy * @param newStrategy_ {Strategy} Strategy data for the new strategy to be created from * @param seedFunds_ {uin256} amount of the base token to fun the subscription with * @dev if no seed fund set to 0. Any seed funds will need to be approved before this function is called * @param subscribeToExecutor_ {bool} wether to auto subscribe to the default executor * @dev the Account needs to have 5 executions worth of funds to be subscribed */ function SetupStrategy( Strategy calldata newStrategy_, uint256 seedFunds_, bool subscribeToExecutor_ ) external; /** * @notice Used by the account owner to subscribe the strategy to the executor * @param strategyId_ {uint256} The Id of the strategy to subscribe to the executor */ function SubscribeStrategy(uint256 strategyId_) external; /** * @notice Used by the account owner to unsubscribe the strategy to the executor * @param strategyId_ {uint256} ID of the strategy to unsubscribe */ function UnsubscribeStrategy(uint256 strategyId_) external; /** * @notice Allows the account owner to fund the account for strategy's * @dev the funds are not strategy specific * @param token_ {address} Address for the base token being funded * @param amount_ {uint256} Amount of the token to be deposited * @dev Must approve the spend before calling this function */ function FundAccount(address token_, uint256 amount_) external; /** * @notice Gets Account balance of the provided base token * @param token_ {address} Address for the token to check * @returns {uint256} Amount of that token in the account */ function GetBaseBalance(address token_) external returns (uint256); /** * @notice Gets Account balance of the provided target token * @param token_ {address} Address for the token to check * @returns {uint256} Amount of that token in the account */ function GetTargetBalance(address token_) external returns (uint256); /** * @notice Removes a given amount from the Address of the given base token * @param token_ {address} Address of the base token to remove from the contract * @param amount_ {uint256} Amount of the base token to remove from the address */ function UnFundAccount(address token_, uint256 amount_) external; /** * @notice Removes a given amount from the Address of the given target token * @param token_ {address} Address of the target token to remove from the account * @param amount_ {uint256} Amount of the target token to remove from the account */ function WithdrawSavings(address token_, uint256 amount_) external; /** * @notice Ony callable by the DCAExecutor contract to remove the strategy from the executor * @dev used when a strategy runs out of funds to execute * @param strategyId_ {uint256} Id of the strategy to remove */ function ExecutorDeactivateStrategy(uint256 strategyId_) external; /** * @notice Allows the account owner to set, remove and update a strategy reinvest * @param strategyId_ {uint256} Id of the strategy * @param reinvest_ {Reinvest} Reinvest data to amend */ function SetStrategyReinvest( uint256 strategyId_, DCAReinvest.Reinvest memory reinvest_ ) external;
Librarys
library DCAReinvest { using ReinvestCodes for uint8; /** * @notice Reinvest strategy struct. * If no reinvest set active to false and zero-out other fields * If using predefined reinvest strategy zero-out the bytes fields * Check code agents the Reinvest Codes library */ struct Reinvest { bool active; uint8 investCode; bytes depositReinvestMethod; bytes withdrawReinvestMethod; address reinvestSpender; // Address of the contract that needs approval to spend (protocol entry contract) } /** * * @param reinvestData_ {Reinvest} Data of the reinvest strategy * @param tokenAddress_ {address} the underline token that will be reinvested (target token) * @param amount_ {uint256} amount of the token to be invested * @return amount {uint256} the amount of the yield baring token recived */ function _executeInvest( Reinvest memory reinvestData_, address tokenAddress_, uint256 amount_ ) internal returns (uint256 amount) { if (reinvestData_.investCode == ReinvestCodes.CUSTOM) { // execute the custom reinvest logic amount = _customReinvest(reinvestData_, tokenAddress_, amount_); } else if (reinvestData_.investCode <= ReinvestCodes.COMPOUND_ETH) { // pass to an execute Compound data amount = CompoundV3Reinvest._supplyCompound( code_, amount_, tokenAddress_ ); } else if (reinvestData_.investCode <= ReinvestCodes.AAVE_ETH) {} } function _executeWithdraw( Reinvest memory reinvestData_, address tokenAddress_, uint256 amount_ ) internal returns (bool success, uint256 amount) { if (reinvestData_.investCode == ReinvestCodes.CUSTOM) { // execute the custom reinvest logic amount = _customReinvest(reinvestData_, tokenAddress_, amount_); } else if (reinvestData_.investCode <= ReinvestCodes.COMPOUND_ETH) { // pass to an execute Compound data amount = CompoundV3Reinvest._supplyCompound( code_, amount_, tokenAddress_ ); } else if (reinvestData_.investCode <= ReinvestCodes.AAVE_ETH) {} } function _customReinvest( Reinvest memory reinvestData_, address tokenAddress_, uint256 amount_ ) internal returns (bool success, uint256 amount) {} function _customWithdraw() internal returns (bool success, uint256 amount) {} }
 
 
 
 
 
 
Copyright 2022 E_Labs
HOME
WRITING
ABOUT