Native Yield
Overview

Native Yield on ApeChain

Native yield allows specific assets bridged into the ApeChain ecosystem to automatically generate yield by forwarding them to vaulted sources of yield on the Ethereum mainnet. The initial target assets and yield sources are outlined in the table below:

Source TokenYield SourceDestination Token
USDC, USDT, DAIsDAI (DSR) (opens in a new tab)apeUSD
ETH, WETHwstETH (opens in a new tab)apeETH
ApeCoinApeStake (opens in a new tab)ApeCoin (gas token)

This innovative concept enables assets to generate yield automatically without manual intervention on Layer 2 and Layer 3 chains. By offering native yield, ApeChain aims to incentivize bridging and enhance the consumer experience by allowing users' wallet balances to rebase at frequent intervals.

Implementation Approach

ApeChain's Multi-Hop Bridging and Cross-Chain Execution

ApeChain supports multi-hop bridging and cross-chain execution by preparing call data that instructs the Gateway to fulfill specific actions. These instructions are passed between Gateway endpoints on different chains using arbitrary messaging bridges like LayerZero and rollup frameworks’ canonical bridges.

Architecture | Yield Routes

To enable the creation of tokens with native yield on ApeChain, we have deployed a new contract called the Yield Route Wrapper. This contract abstracts the process of depositing assets into ERC4626-compliant vaults on Ethereum and creating bridgeable tokens into a single transaction.

Component Overview

Deposit Process

  1. User selects apeUSD as the destination token, triggering a Yield Route where sDAI on Ethereum is the yield source.

  2. User deposits their source token into Decent’s Gateway.

  3. Decent swaps the source token into DAI on Ethereum and transfers the DAI to the Yield Route Wrapper.

  4. The Wrapper deposits the DAI into the sDAI contract, creating sDAI, and transfers it to the OFT adapter.

  5. The Wrapper then mints an OFT version of sDAI on ApeChain, named apeUSD.

Withdraw Process

  1. User selects apeUSD as their source token.
  2. User transfers apeUSD to the Decent Gateway contract on ApeChain.
  3. The Decent Gateway burns apeUSD on ApeChain and mints apeUSD on Ethereum.
  4. The Gateway routes the request to the Yield Route Wrapper, which unrolls the apeUSD into sDAI, swaps it for DAI, and transfers the DAI to the user on Ethereum.

For most rollups today, assets held as collateral in the bridge sit idly without being put to work (with over $15B worth of ETH locked up across leading rollups at the moment). ApeChain instead allows users to earn yield on those locked assets!

EOAs automatically receive $APE yield via rebasing -- users will automatically see their wallet balance tick up.

Yield Modes

ApeChain supports three different yield modes: automatic, void, and delegate. The ArbInfo contract (opens in a new tab) can be used to update the yield mode for an address. By default, all addresses start in automatic mode where yield is automatically earned each block. Whenever a contract is deployed, the yield mode is switched to void where yield is disabled to maximize compatibility with existing contracts. The contract can modify its yield mode by calling the ArbInfo contract (opens in a new tab).

For a more detailed walkthrough, check out this video demo (opens in a new tab)!

Automatic Yield Mode (default for EOAs)

In automatic yield mode, yield is automatically earned each block. This is the default mode for all EOAs (ie wallet addresses).

In automatic mode, the address' $APE balance is incremented by the amount of yield earned each block, without the user needing to manually "claim" the yield.

Void Yield Mode (default for contracts)

In void yield mode, yield is disabled for the totality of $APE held in the address. This is the default mode for all contracts, as native yield can introduce incompatibilities with common smart contracts if not properly accounted for.

Changing from "void" to "automatic" or "delegate" mode will not grant the user any $APE that would have accrued in the past, were the contract not in void mode. For this reason, it is recommended that contract developers who wish to take advantage of native yield switch to another mode shortly after deployment-time.

Delegate Yield Mode

In delegate yield mode, the address has a fixed balance and yield is sent to a delegate address instead. Note that delegating yield to an address in delegate yield mode does not chain and delegating yield to an address that is not in automatic yield mode does not increase balance until the delegate address switches to automatic yield mode.

Changing Yield Mode

The "ArbInfo" contract (opens in a new tab), located at address 0x0000000000000000000000000000000000000065, a system contract built into the ApeChain protocol, can be used to update the yield mode for an address. For native yield, three functions are available:

interface ArbInfo {
    function configureAutomaticYield() external;
    function configureVoidYield() external;
    function configureDelegateYield(address delegate) external;
}

A contract can call these methods directly to update the yield mode for itself. Note that only the contract itself can call these methods, and not any other address.

For example, a contract might add the ability for an owner to configure the yield mode for itself by adding the following code to its implementation:

 
enum YieldMode {
    AUTOMATIC,
    VOID,
    DELEGATE
}
 
function configureYieldMode(YieldMode yieldMode, address delegate) external onlyOwner {
    if(yieldMode == YieldMode.AUTOMATIC) {
        ArbInfo(0x0000000000000000000000000000000000000065).configureAutomaticYield();
    } else if(yieldMode == YieldMode.VOID) {
        ArbInfo(0x0000000000000000000000000000000000000065).configureVoidYield();
    } else if(yieldMode == YieldMode.DELEGATE) {
        ArbInfo(0x0000000000000000000000000000000000000065).configureDelegateYield(delegate);
    }
}

Balance Values

ApeChain Mainnet

The following fields are added to the StateAccount object to track yield: flags, fixed, shares, debt, and delegate.

Balance is computed as follows:

balance = account.shares * global.sharePrice + account.fixed - account.debt

eth_getBalanceValues can be used to get all the balance values for an address:

cast rpc --rpc-url https://rpc.apechain.com eth_getBalanceValues 0x0000000000000000000000000000000000000000 latest

eth_getSharePrice can be used to get the global share price:

cast rpc --rpc-url https://rpc.apechain.com eth_getSharePrice latest

Yield Rate

The ArbOwnerPublic contract (opens in a new tab) may be used to get the share price and annual percentage yield.



Curtis Testnet

The following fields are added to the StateAccount object to track yield: flags, fixed, shares, debt, and delegate.

Balance is computed as follows:

balance = account.shares * global.sharePrice + account.fixed - account.debt

eth_getBalanceValues can be used to get all the balance values for an address:

cast rpc --rpc-url https://rpc.curtis.apechain.com eth_getBalanceValues 0x0000000000000000000000000000000000000000 latest

eth_getSharePrice can be used to get the global share price:

cast rpc --rpc-url https://rpc.curtis.apechain.com eth_getSharePrice latest

Yield Rate

The ArbOwnerPublic contract (opens in a new tab) may be used to get the share price and annual percentage yield.

Querying the annual percentage yield

Any users can determine the annual percentage yield by viewing the getApy function on the ArbOwnerPublic contract (opens in a new tab) on the ApeChain Explorer.

Note that this value is stored as an integer representing 10^(-9)ths of a percent, so a value of 10000000000 represents 10% APY.

APY Query

Using the APY value in smart contracts

Smart contracts can also query this value onchain.

interface IArbOwnerPublic {
    function getApy() external view returns (uint256);
}
 
contract APYExample {
    IArbOwnerPublic constant arbOwnerPublic = IArbOwnerPublic(0x000000000000000000000000000000000000006b);
 
    function getCurrentAPY() public view returns (uint256) {
        // Remember, the value returned by getApy is in 10^(-9)ths of a percent.
        uint256 apyValue = arbOwnerPublic.getApy();
    }
}