Skip to main content

yASTRO

Overview​

yASTRO (astroport-yastro 1.0.0, Terra only) is a transferable CW20 that is also its own staking contract. Stake ASTRO and receive the same amount of yASTRO, 1:1. Each week the Terra Maker deposits a quarter of Terra's fees, in USDC.inj, and they stream to yASTRO holders over the following week.

Every balance change (stake, unstake, transfer, send, transfer_from, send_from) first settles the rewards of both sides at their old balances, so rewards follow whoever holds the yASTRO, second by second, wherever the token moves: wallets, DAOs, pools.

  • Stake: attach ASTRO (Terra IBC ASTRO, ibc/8D8A7F7253615E5F76CB6252A1E1BD921D5EDB7BBAAF8913FB1C77FF125D9995) to stake and receive the same amount of yASTRO. The legacy ASTRO CW20 can't be staked.
  • Unstake: unstake burns yASTRO and starts an unbonding of 7 days. Unbonding ASTRO earns nothing. withdraw then sends every matured unbonding. An address can have up to 30 open unbondings.
  • Claim: rewards are never pushed. claim sends the caller's accrued rewards, and pending_rewards shows them. In the app, this is the Rewards Center at app.astroport.fi/yastro.

yASTRO has no CW20 receive hook: ASTRO is a native denom on Terra, so it's staked by attaching it to stake.

Concepts​

Epochs and streaming​

Epochs are Astroport's weeks, starting Monday 00:00 UTC (the same weeks Incentives uses). Everything deposited with deposit_rewards during an epoch is queued, then paid out evenly, per second, over the next epoch to yASTRO holders, pro rata to their balance.

  • Holding yASTRO for a minute earns a minute of rewards, so there's nothing to gain by buying just before a deposit.
  • Anything streamed while nobody holds yASTRO is queued again for the next epoch.
  • Fractions of a unit stay in an account's pending rewards until they add up to a whole unit, and so does what the streaming math rounds off. Rounding leaves dust in the contract, never a shortfall.

The APR for a reward denom is rate * 31_536_000 / total_staked, in reward units per staked ASTRO unit, with rate from reward_state and total_staked from staking_state.

Reward denoms​

Only denoms the owner allows can be deposited, at most 5 ever. ASTRO can't be a reward denom, so staked ASTRO and rewards never mix. On Terra the reward denom is USDC.inj (ibc/E8481AD838C31D4FC12A504B10F9B4E2F830F8818D2735C2FFC707579B5FA60B).

Removing a reward denom stops new deposits. What's already queued still streams and owed rewards stay claimable. Removing the Maker's reward denom would make the Maker's deposit leg fail, and with it the whole distribution, so the Maker's legs must be changed first.

claim_for​

A holder can enable claim_for for itself once (set_claim_for), for example a DAO treasury with one proposal. After that anyone, such as a bot, can send it its rewards. Holders that never enable it keep their rewards pending, so nothing lands in a contract that can't use it.

Pools​

A pool holding yASTRO earns like any holder. Anyone can call forward_pool_rewards for a pool to send its rewards to its LP stakers through Incentives:

  • The pool must answer Astroport's pair {} query for its own address, with yASTRO among its assets and an LP token it mints itself (factory/{pool}/astroport/share, or a CW20 whose minter is the pool).
  • At least min_lp_staked of the LP supply (currently 1%) must be staked in Incentives, so a tiny stake can't take a pool's rewards. Until then the rewards stay pending.
  • yASTRO must be on Incentives' fee exemption list (it is on Terra). It then isn't held to Incentives' 5-reward-token limit either. No funds are attached to the call.
  • The rewards become an Incentives schedule on the pool's LP token, from now until the next Monday plus one week, the shortest schedule Incentives allows. LPs who stake their LP tokens in Incentives earn them there. LPs who don't stake in Incentives get none of it.
  • A reward too small for Incentives' minimum of 1 unit per second (about 0.6 to 1.2 USDC per call), or one Incentives rejects, stays with the pool for the next call.
  • From a Maker deposit to an LP being fully paid takes two to three weeks.

Guarantees and owner powers​

The contract always holds at least the staked plus unbonding ASTRO (staking_state), and pays rewards only from what was deposited.

The owner (the Terra satellite, run by the "Astroport - Contributors" DAO) can change the reward denoms, the unbonding period (at most 28 days, for new unbondings only), the Incentives address and min_lp_staked. The Incentives address decides where pool rewards are forwarded, so it's as trusted as the owner. The owner can't move staked ASTRO or anyone's rewards. The wasm admin can migrate the contract.

Integration notes​

  • yASTRO is a CW20, so it can't be bridged over IBC without forfeiting its rewards to the bridge escrow.
  • Contracts holding yASTRO (vaults, staking modules, escrows) earn rewards for themselves and must claim and pass them on.
  • Stake and unstake emit stake and unstake actions rather than CW20 mint and burn.
  • ASTRO or rewards sent to the contract with a plain bank transfer can't be recovered. yASTRO can't be sent to its own contract.

InstantiateMsg​

The values below are the Terra deployment's configuration.

json
msg.rs
Copy

_21
{
_21
"owner": "terra1k9j8rcyk87v5jvfla2m9wp200azegjz0eshl7n2pwv852a7ssceqsnn7pq",
_21
"astro_denom": "ibc/8D8A7F7253615E5F76CB6252A1E1BD921D5EDB7BBAAF8913FB1C77FF125D9995",
_21
"unbonding_period": 604800,
_21
"reward_denoms": [
_21
"ibc/E8481AD838C31D4FC12A504B10F9B4E2F830F8818D2735C2FFC707579B5FA60B"
_21
],
_21
"incentives": "terra1eywh4av8sln6r45pxq45ltj798htfy0cfcf7fy3pxc2gcv6uc07se4ch9x",
_21
"min_lp_staked": "0.01",
_21
"name": "Staked ASTRO",
_21
"symbol": "yASTRO",
_21
"decimals": 6,
_21
"marketing": {
_21
"project": "https://astroport.fi",
_21
"description": "Stake ASTRO, earn protocol fees",
_21
"marketing": "terra1k9j8rcyk87v5jvfla2m9wp200azegjz0eshl7n2pwv852a7ssceqsnn7pq",
_21
"logo": {
_21
"url": "https://..."
_21
}
_21
}
_21
}

ParamsTypeDescription
ownerStringAddress allowed to change the configuration
astro_denomStringThe denom staked for yASTRO, 1:1
unbonding_periodu64Seconds between unstaking and being able to withdraw. At most 28 days
reward_denomsVec<String>Denoms deposit_rewards accepts. Can't include astro_denom
incentivesOption<String>Astroport Incentives, for forwarding pools' rewards to their LP stakers
min_lp_stakedDecimalShare of a pool's LP supply that must be staked in Incentives before its rewards are forwarded. At most 1
nameStringCW20 name
symbolStringCW20 symbol
decimalsu8CW20 decimals
marketingOption<InstantiateMarketingInfo>Project, description, logo, and the address that can update them

ExecuteMsg​

stake​

Stakes the attached ASTRO for the same amount of yASTRO, minted to recipient (default: the sender). Anyone can call it. Attach only ASTRO.

json
msg.rs
Copy

_5
{
_5
"stake": {
_5
"recipient": "terra1..."
_5
}
_5
}

ParamsTypeDescription
recipientOption<String>Receives the yASTRO. Defaults to the sender

unstake​

Burns amount of the sender's yASTRO and starts its unbonding. The ASTRO can be withdrawn after the unbonding period (7 days). Unbonding ASTRO earns nothing. Fails if the sender already has 30 open unbondings.

json
msg.rs
Copy

_5
{
_5
"unstake": {
_5
"amount": "1000000"
_5
}
_5
}

ParamsTypeDescription
amountUint128The amount of yASTRO to burn

withdraw​

Sends the sender every unbonding that has matured. Fails if nothing has matured yet.


_3
{
_3
"withdraw": {}
_3
}

deposit_rewards​

Queues the attached reward tokens to stream to yASTRO holders over the next epoch. Anyone can call it; only the accepted reward denoms can be attached. The Terra Maker calls it with its yASTRO leg when it distributes fees.


_3
{
_3
"deposit_rewards": {}
_3
}

claim​

Sends the sender's accrued rewards to recipient (default: the sender). Fails if there is nothing to claim.

json
msg.rs
Copy

_5
{
_5
"claim": {
_5
"recipient": "terra1..."
_5
}
_5
}

ParamsTypeDescription
recipientOption<String>Receives the rewards. Defaults to the sender

claim_for​

Sends address's accrued rewards to address itself. Anyone can call it, but only for holders that enabled it with set_claim_for.


_5
{
_5
"claim_for": {
_5
"address": "terra1..."
_5
}
_5
}

set_claim_for​

Lets anyone send the sender its rewards with claim_for (enabled: true), or stops it (enabled: false).


_5
{
_5
"set_claim_for": {
_5
"enabled": true
_5
}
_5
}

forward_pool_rewards​

Moves a pool's accrued rewards into Incentives for that pool's LP stakers, as a schedule lasting until the next Monday plus one week. Anyone can call it. See Pools for the requirements.


_5
{
_5
"forward_pool_rewards": {
_5
"pool": "terra1..."
_5
}
_5
}

CW20 messages​

yASTRO supports the standard CW20 messages. Every balance change settles both sides' rewards first.

MessageWho
transfer { recipient, amount }holder
send { contract, amount, msg }holder
increase_allowance { spender, amount, expires? }, decrease_allowance { spender, amount, expires? }holder
transfer_from { owner, recipient, amount }, send_from { owner, contract, amount, msg }spender with an allowance
update_marketing { project?, description?, marketing? }, upload_logomarketing address

update_config​

Owner only. Leaves unset fields as they are.

json
msg.rs
Copy

_9
{
_9
"update_config": {
_9
"add_reward_denoms": [],
_9
"remove_reward_denoms": [],
_9
"unbonding_period": 604800,
_9
"incentives": "terra1eywh4av8sln6r45pxq45ltj798htfy0cfcf7fy3pxc2gcv6uc07se4ch9x",
_9
"min_lp_staked": "0.01"
_9
}
_9
}

ParamsTypeDescription
add_reward_denomsOption<Vec<String>>Denoms to accept as rewards. At most 5 denoms ever, never ASTRO
remove_reward_denomsOption<Vec<String>>Stops accepting these. Rewards already deposited still stream and stay claimable
unbonding_periodOption<u64>Applies to unbondings started after the change. At most 28 days
incentivesOption<String>The Incentives contract pool rewards are forwarded to
min_lp_stakedOption<Decimal>At most 1

propose_new_owner, drop_ownership_proposal, claim_ownership​

The usual two-step ownership transfer: the owner proposes a new owner with an expiry ({"propose_new_owner":{"owner":"terra1...","expires_in":86400}}) or drops the proposal, and the proposed owner claims ownership.

QueryMsg​

config​

json
state.rs
Copy

_7
{
_7
"owner": "terra1k9j8rcyk87v5jvfla2m9wp200azegjz0eshl7n2pwv852a7ssceqsnn7pq",
_7
"astro_denom": "ibc/8D8A7F7253615E5F76CB6252A1E1BD921D5EDB7BBAAF8913FB1C77FF125D9995",
_7
"unbonding_period": 604800,
_7
"incentives": "terra1eywh4av8sln6r45pxq45ltj798htfy0cfcf7fy3pxc2gcv6uc07se4ch9x",
_7
"min_lp_staked": "0.01"
_7
}

Query with {"config":{}}. The response is the live Terra configuration.

pending_rewards​

Rewards an address can claim right now, as a list of coins.

json
response
Copy

_5
{
_5
"pending_rewards": {
_5
"address": "terra1..."
_5
}
_5
}

unbondings​

An address's open unbondings. release_at is the time, in seconds, after which withdraw sends it.

json
state.rs
Copy

_5
{
_5
"unbondings": {
_5
"address": "terra1..."
_5
}
_5
}

reward_state​

One entry per reward denom ever deposited.

json
msg.rs
Copy

_11
[
_11
{
_11
"denom": "ibc/E8481AD838C31D4FC12A504B10F9B4E2F830F8818D2735C2FFC707579B5FA60B",
_11
"index": "0",
_11
"rate": "0",
_11
"epoch_end": 1794182400,
_11
"queued": "3317816",
_11
"total_deposited": "3317816",
_11
"accepted": true
_11
}
_11
]

ParamsTypeDescription
denomStringThe reward denom
indexDecimal256Reward per yASTRO paid out so far
rateDecimal256Paid out per second until epoch_end, shared by all yASTRO
epoch_endu64When the current epoch ends
queuedDecimal256Streams over the next epoch
total_depositedUint128Everything ever deposited
acceptedboolWhether deposit_rewards still accepts it

Query with {"reward_state":{}}. The example is the state on 2026-11-04, after the first deposit: it is queued and starts streaming at the next epoch.

claim_for_enabled​

Whether claim_for can pay out address. Returns a bool.


_5
{
_5
"claim_for_enabled": {
_5
"address": "terra1..."
_5
}
_5
}

staking_state​

json
msg.rs
Copy

_4
{
_4
"total_staked": "49057913161684",
_4
"total_unbonding": "1011000000"
_4
}

ParamsTypeDescription
total_stakedUint128yASTRO supply, which is the ASTRO staked
total_unbondingUint128ASTRO in unbonding

Query with {"staking_state":{}}.

CW20 queries​

balance { address }, token_info {}, minter {} (always empty: yASTRO is only minted by staking), allowance { owner, spender }, all_allowances { owner, start_after?, limit? }, all_spender_allowances { spender, start_after?, limit? }, all_accounts { start_after?, limit? }, marketing_info {} and download_logo {}.


_5
{
_5
"balance": {
_5
"address": "terra1..."
_5
}
_5
}