System Design

Lucidly Yield Product Specifications

Lucidly Yields is built on the boring-vault architecture built and pioneered by Veda Labs.

  • Users can mint syAssets in a single atomic transaction using whitelisted tokens. Withdrawals are asynchronous and take 6-24 hours to process in the current production setup.

  • Underlying deposits are allocated to select strategies by strategist roles and are segregated by risk and liquidity profiles, i.e., a strategist whitelisted to supply/withdraw USDC from AaveV3 can only do that and nothing else.

  • syAssets are deployed on multiple networks and can use generic interop protocols - ccip, layerzero, wormhole etc.

The protocol stack consists of 2 components -

  • onchain - The smart contracts powering syTokens are built on the boring-vault architecture built and pioneered by Veda Labs.

  • offchain - Asset management and relayer services for transaction creation and handling used to interact with the smart contract layer.

Onchain Stack

Flow of funds

deposit flow
allocation flow
redemption flow

Offchain stack

  • AUM service - Handles accounting for global assets and liabilities using oracles and updates exchangeRate on the Accountant smart contract.

  • Solver service - Monitors pending withdrawal requests on the Queue smart contract and processes them.

  • Manager service - The core and the most important part of the stack, is used to create transactions based on offchain algorithms and restricted calldata, prepares merkle proofs and interacts with the Manager smart contract.

example_manager_script.py
for leaf in all_leafs:
    if (leaf.target.lower() == from_token.lower()
        and leaf.signature == "approve(address,uint256)"
        and leaf.argument_addresses[0].lower() == swap_tx["to"].lower()):
        approve_leaf = leaf

    elif (leaf.signature == "swapCompact()"
          and leaf.argument_addresses[1].lower() == to_token.lower()
          and leaf.argument_addresses[2].lower() == self.boring_vault_address.lower()
          and leaf.argument_addresses[3].lower() == odos_executor.lower()):
        swap_leaf = leaf

if not approve_leaf or not swap_leaf:
    raise ValueError("Valid leafs not found in Merkle tree.")

proofs = _get_proofs_using_tree([approve_leaf, swap_leaf], tree)

tx = await self.manager.functions.manageVaultWithMerkleVerification(
    proofs,
    [self.sanitizer_address] * 2,
    [from_token, swap_tx["to"]],
    [self.encode_approve(swap_tx["to"], 2**256 - 1), swap_tx["data"]],
    [0, 0]
).build_transaction({
    "from": self.account.address,
    "nonce": await self.w3.eth.get_transaction_count(self.account.address),
})

A sanitizer parameter is used when creating transactions that restricts addresses included in calldata based on an ADMIN_ROLE set preset.

Github link - https://github.com/lucidlylabs/boring-vault

We welcome new strategists to ideate on novel onchain yield strategies, DM us on X or send an email to [email protected] for support.

Last updated