Skip to main content

Auto-Earn / Standardized Yield (SY)

Auto-earn is implemented as a thin SY adapter on top of Blend (Stellar's primary lending protocol). When a user does Router.auto_deposit(user, asset, amount):

  1. The Router transfers the underlying asset from the user into the SY adapter.
  2. The SY adapter deposits into Blend, receiving yield-bearing Blend shares.
  3. The SY adapter mints SY tokens to the user proportional to the share value.
  4. Over time, Blend accrues interest → SY's exchange_rate() grows.

Withdrawal is the inverse: auto_withdraw(user, asset, amount) burns SY, redeems from Blend, transfers the underlying back.

SY exchange rate

fn exchange_rate(env: Env) -> i128 {
// total_underlying_held_by_adapter * WAD / total_sy_supply
}

exchange_rate() is in WAD (1e18) and monotonically non-decreasing — every block, the underlying grows due to Blend yield while the SY supply stays constant.

A 1 USDC deposit at exchange rate 1.0 mints 1.0 SY. A year later when exchange rate is 1.10, that same 1.0 SY redeems for 1.10 USDC.

Why this matters

SY is a "yield-bearing" token. Holding SY = holding the underlying AND auto-compounding the interest. The mobile UI surfaces this as the Auto-Earn APY pill — the displayed rate is the realized growth rate of exchange_rate().

Auth model

The user signs the outer auto_deposit call. The Router pre-authorizes the inner sy.deposit(...) and token.transfer(...) sub-invocations using env.authorize_as_current_contract, so the smart wallet's signer policy only sees one outer auth entry.

env.authorize_as_current_contract(vec![
&env,
InvokerContractAuthEntry::Contract(SubContractInvocation {
context: ContractContext { contract: sy_id, fn_name: symbol!("deposit"), args: ... },
sub_invocations: vec![
&env,
InvokerContractAuthEntry::Contract(SubContractInvocation {
context: ContractContext { contract: asset_id, fn_name: symbol!("transfer"), args: ... },
sub_invocations: vec![&env],
}),
],
}),
]);

Without this pre-declaration, the nested token.transfer(from=Router, ...) would fail with Error(Auth, InvalidAction) because the user's signature only covers the outer call.

Contracts and SY tokens

AssetUnderlying contractSY contract
USDCCC6K5SDGTDYPZDDHM6GV4OV3DAQPHKIMROIVE2LGLQN7PF4HEYSJWYYJCAACQ5OGFP7ZPS6U3XDO6T67SQMSXNRO4FABWFV3EMWEUUM6DU3XFREK
EURCCCFU2STUWKUTHDNG2IS57BS2WAPGO75KG55H4JLPILSKISU5DHX26F4HCBGE2IFCBCWPH4OJR4EZTVTMKV5ATL3WH4QGDYEIXP2LRGZBDP6UI7GB
XLM(native)CDXECW7NPIVDVOEV2D5EKTXPABBC4EJPACZ6LRLQSCC3F5KEHRABPO5C