feat(token): Integrate Stellar Asset Contract (SAC) Standard for Yield-Bearing Pool Shares#181
Open
senmalong wants to merge 1 commit into
Open
Conversation
Implements issue BETAIL-BOYS#171 — Stellar Asset Contract (SAC) standard for yield-bearing pool shares in the TradeFlow factoring pools. ## What changed Added contracts/liquidity_vault — a new Soroban contract that acts as the yield-bearing vault for liquidity providers. When an LP deposits USDC (or any SAC-compatible token) they receive tfUSDC shares in return. As the pool earns revenue (factoring discounts, interest, flash-loan fees) the underlying value of each share grows, so LPs automatically accrue yield without any extra claim transaction. ## SAC Token Interface The vault implements the full standard Soroban token interface: balance, total_supply, decimals, name, symbol (read) transfer, transfer_from, approve, allowance (write) This makes tfUSDC indistinguishable from any other SAC token, so any wallet, DEX aggregator or lending protocol that understands the Stellar Asset Contract standard can interact with it out of the box. ## Vault / ERC-4626 mechanics deposit(from, assets, min_shares) -> shares Pulls underlying from the caller, mints shares at the current exchange rate (assets * total_shares / total_assets). Returns shares minted. min_shares is a slippage guard. withdraw(from, shares, min_assets_out) -> assets Burns the caller's shares and releases proportional underlying. min_assets_out is a slippage guard. preview_deposit / preview_redeem Read-only helpers so frontends can quote before submitting. total_assets — physical SAC balance held by the vault total_supply — shares outstanding ## Inflation-attack mitigation On the very first deposit MINIMUM_LIQUIDITY (1 000) shares are permanently locked by crediting them to total_shares without issuing a balance entry. This is the standard ERC-4626 / Uniswap V2 technique: the locked shares force the share-price denominator to always be >= 1 000, making the classic first-depositor rounding attack economically infeasible regardless of how much an attacker donates to the vault. ## Admin & compliance set_paused(bool) — emergency circuit breaker set_frozen(address, bool) — per-address compliance freeze (mirrors the freeze pattern already in amm_pool) is_paused / is_frozen — read helpers Closes BETAIL-BOYS#171
Contributor
|
@senmalong could you please check back and fix the rust CI build that failed |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #171
Implements the Stellar Asset Contract (SAC) standard for yield-bearing LP share tokens in the TradeFlow factoring pools, as specified in issue #171.
What was built
New contract:
contracts/liquidity_vaultA standalone Soroban vault contract (
LiquidityVault) that LPs interact with instead of depositing into a raw pool. When a user deposits USDC they receive tfUSDC — a fully transferable, yield-bearing share token that is indistinguishable from any other SAC token on Stellar.SAC Token Interface (acceptance criterion 1)
All standard Soroban token read/write functions are implemented:
balance(id)total_supply()decimals()name()symbol()transfer(from, to, amount)transfer_from(spender, from, to, amount)approve(owner, spender, amount, expiration_ledger)allowance(owner, spender)Minting & Burning Logic (acceptance criterion 2)
deposit(from, assets, min_shares) -> sharesOn first deposit:
shares = assets(1:1 before inflation lock).On subsequent deposits:
shares = (assets * total_shares) / total_assetsTransfers underlying from caller to vault, mints shares at the current exchange rate.
min_sharesis a slippage guard.withdraw(from, shares, min_assets_out) -> assetsassets = (shares * total_assets) / total_sharesBurns caller's shares (checks-effects-interactions), releases proportional underlying.
min_assets_outis a slippage guard.As the pool earns revenue (factoring discounts, loan interest, flash-loan fees)
total_assetsgrows whiletotal_sharesstays the same — every share is now worth more underlying.Inflation-Attack Protection (acceptance criterion 3)
On the very first deposit
MINIMUM_LIQUIDITY = 1_000shares are permanently locked by incrementingtotal_shareswithout issuing any balance entry. These shares can never be redeemed.This is the same technique used by Uniswap V2 and recommended in ERC-4626 audit literature:
MINIMUM_LIQUIDITY; otherwise the transaction reverts.Storage design
VaultStateAdminPausedFrozen(Address)Balance(Address)Allowance(owner, spender)AllowanceLedger(owner, spender)All persistent entries extend their own TTL on every access (~30 days / 535_680 ledgers), matching the pattern used across the existing contracts.
Tests (12 unit tests)
transfer,transfer_from,approve/allowanceContract compiles to
wasm32v1-nonewith zero errors.Checklist
depositmints shares at correct exchange ratewithdrawburns shares and releases proportional assetsMINIMUM_LIQUIDITYlockdepositandwithdrawpreview_deposit/preview_redeemread helpers for frontendswasm32v1-nonewith no errors