Airswap staking recalculates unlock timing when more AST enters an existing stake
Updated
Airswap staking deposits AST into a governance staking contract, where additional deposits recalculate the unlock schedule for the combined balance. In the Staking 5.0.1 implementation with
stakeDuration
and
available()
, a top-up uses the amount already withdrawable to credit elapsed progress. Each underlying staker has one recorded start, finish, and balance. When the global unlock override is off, the updated timestamps determine when the remaining principal becomes available. Governance rewards follow their own participation and claim rules.
Adding AST to an existing stake recalculates one combined schedule, using the amount already available to withdraw.
Governance rewards and trading bonuses
Depositing AST establishes a staked balance for governance participation. Voter rewards use points allocated after a voting period, based on the staked AST used in proposal votes. Those points support reward claims, while contributor tips reward contributions through a different allocation system. RFQ and other swap activity generate protocol fees that support these mechanisms. When configured to read AST staking balances, SwapERC20 with EIP-712 domain version 4.3 can pay the transaction sender a bonus from a nonzero protocol fee in the signer's token. The bonus depends on that sender's staking balance and the contract's adjustable settings. swap() and swapAnySender() include this mechanism; swapLight() does not. The staked principal and any rewards remain distinct balances throughout this arrangement.
The rewards Pool validates claims against enabled Merkle trees and records whether each claimant has already used them. A growing stake alone does not supply a claim proof or establish a reward amount. Likewise, claiming a pool reward does not withdraw the underlying stake. A reward allocation therefore cannot tell you when deposited AST becomes spendable. That timing comes from the stake record and the withdrawal calculation.
The stake balance and its withdrawal clock
When the global unlock override is off, the stored start and finish determine how much AST is currently withdrawable. Before finish,
available()
multiplies the balance by the elapsed fraction of that interval. At finish, it returns the full remaining balance. The calculation uses the blockchain timestamp, so the stored deadline refers to chain time. Leaving unlocked AST in the contract keeps that amount within the recorded stake until a withdrawal executes.
The stake record contains
start
,
finish
, and
balance
.
balanceOf()
reports the full recorded stake;
available()
reports the amount that
unstake()
currently permits.
How does adding AST change the unlock schedule?
Adding AST recalculates one combined schedule from the enlarged balance, the active duration, and the amount already withdrawable. Both the original AST and the addition share the new finish timestamp. Deposits do not retain individual release dates inside that record.
The top-up calculation
Let B denote the existing balance, A the added AST, U the amount available immediately beforehand, and D the active
stakeDuration
. Use the same raw token units for B, A, and U. Let t be the deposit transaction's block timestamp, with D measured in seconds.
The contract computes
start = t - floor(D * U / (B + A))
.
Integer division rounds the credited time down to whole seconds.
Existing progress credits the combined balance
The fraction
U / (B + A)
determines how much of D counts as elapsed progress. When U is zero, the calculation credits no elapsed time. When U is positive, that credit can prevent a complete restart from the transaction timestamp. Rounding means the immediately withdrawable amount need not preserve every previously available atomic unit. The actual
available()
result expresses the limit after the deposit.
The finish applies to all remaining AST
The contract then sets
finish = start + D
. For otherwise identical B, U, and D, a larger addition can move that finish later. Even a previously fully available stake receives a recalculated schedule when more AST enters. With a positive stakeDuration and the global unlock override off, the enlarged balance does not become immediately withdrawable in full. If the active duration has changed, the new finish also reflects that changed input.
What happens to the schedule after a partial withdrawal?
An accepted partial withdrawal reduces the recorded balance and recalculates start, while retaining the stake's stored finish timestamp. The amount must exceed zero and cannot exceed
available()
when the transaction executes. The contract adjusts the remaining release curve around the smaller balance. Withdrawing the currently unlocked portion can therefore leave AST that continues unlocking toward the existing finish. With the global unlock override off, withdrawing all currently available AST before finish moves start to the withdrawal timestamp without moving finish.
If a withdrawal removes the entire recorded balance, the contract deletes that stake record. A later deposit follows the fresh-stake branch, starting at its transaction timestamp and using the duration then active.
Duration settings and early unlocking
A completed change to
stakeDuration
changes the duration input for later deposits. Existing stakes retain their stored timestamps. The owner first schedules a proposed duration with a delay that meets the contract's minimum timelock. Only after that delay can the owner complete the change. The contract also permits cancellation of a pending duration change. A later top-up uses the duration that actually took effect, rather than a proposal that remains pending.
The owner can override the schedule with
setUnlocked(true)
, making the full balance available. Turning that override off restores the stored schedule while tokens remain staked.
Confirming a top-up before relying on its schedule
A successful top-up transaction transfers AST into the matching
staking contract
and updates the intended stake. Compatibility requires the correct network, underlying
stakingToken
, and staking contract. The AST allowance must authorize that contract to transfer the deposit amount. Reading a balance from another deployment cannot establish what happened to this position, even if both interfaces display the same token symbol.
| Stage | Deposit or recorded result | Schedule or withdrawal limit |
|---|---|---|
| Before submission | The network, stakingToken, and AST allowance match the intended staking contract. | The existing stake has stored start and finish timestamps; stakeDuration supplies the top-up duration. |
| Accepted top-up | A successful stake() or stakeFor() execution transfers AST and updates the target stake. | The combined balance shares a recalculated finish timestamp. |
| Availability after the deposit | available() reads the withdrawable amount for the underlying staker. | The full remaining balance becomes available at finish, or while the global unlock override applies. |
For an active delegate,
stakeFor()
redirects a deposit targeting that delegate to its underlying staker. Reading only the delegate's own stake can therefore miss the credited balance. A stakeFor() deposit without that delegation relationship updates the named staker's record. If the stake receives no further changes, waiting follows its existing schedule. A top-up applies a new calculation to the combined balance.
Airswap staking questions worth asking
Can someone else add AST to my existing stake?
Another wallet can add AST through stakeFor(), using its own tokens and an allowance granted to the staking contract. The function does not require the recipient to initiate the deposit. If the deposit reaches an existing stake, the same combined-schedule calculation applies, so another wallet's top-up can affect that stake's timing.
Does proposing a delegate activate the delegation immediately?
Proposing a delegate does not activate the relationship; the proposed wallet must accept it. The contract rejects acceptance when that wallet already acts as a delegate or has its own nonzero stake. Until acceptance, the proposal alone does not establish the redirection that affects stakeFor() and unstake().
Where does AST go when a delegate calls unstake()?
A delegate's accepted unstaking call sends AST to the underlying staker, not to the delegate's wallet. The contract resolves the delegate to that staker before checking availability. Delegation therefore permits a withdrawal action within the existing limit without giving the delegate a choice of withdrawal recipient.
Is the recorded staked balance transferable like an ERC-20 token?
The recorded stake stays with its staker because this Staking implementation provides balance queries and Transfer events without the ordinary ERC-20 transfer functions that would move that balance to another wallet.
Why does a direct AST transfer to the staking address not create a stake?
A direct AST transfer does not execute the staking functions that update the recipient's stake record. stake() and stakeFor() perform both the token movement and the accounting update. Tokens arriving at the contract address alone therefore do not establish a credited balance, start timestamp, or finish timestamp.
Which decimal precision applies to staking amounts?
The staking contract's decimals() function returns the precision of its underlying staking token. Deposit and withdrawal arguments use raw token units, while an interface may display a decimal AST quantity. The same unit conversion must apply when comparing an entered amount with balanceOf() or available().