Repository navigation
Conversation
|
@Ejirosam Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
b102908 to
f8ef699
Compare
0bbc2cc to
c376305
Compare
c376305 to
0d6d0b4
Compare
|
Warning Possible malicious code: please review before merging. An automated security scan flagged files in this pull request that appear to contain malicious code. Code like this can run automatically when a project is opened in an editor or its dependencies are installed, so it could compromise the machine of anyone who checks out this branch. Automated scans can be wrong, so this may be a false positive. Maintainers: please review the flagged files carefully before merging, on GitHub in your browser rather than by checking out the branch. The malicious part is often hidden far off to the right of an ordinary-looking line, so scroll sideways through each file. @Ejirosam: this pull request adds or changes these files, possibly without your knowledge. Malware like this spreads by adding itself to repositories on machines it has infected, so your machine might be compromised. Please undo these changes in this pull request, and don't open or run other projects on that machine until you've checked it. Flagged files at commit
Only merge once you're confident these files are safe. ℹ️ Posted automatically by Drips Wave. Questions? Contact support@drips.network. |
0d6d0b4 to
9457aee
Compare
Overview
lock()promised a yield computed from the APY table at lock time andunlock()paidamount + projected_yieldout of the contract's pooled token balance. That balance is every user's principal, so an unfunded pool paid the first withdrawer's "yield" out of the second user's money.This change replaces the promise with shares. A lock now holds
sharesof the pooled vault (crates/loktin-yield), andunlock()pays whatever those shares redeem for at that moment. If the pool earned nothing, a share is worth exactly what was paid for it, so two users who each lock 100 with no pool set both withdraw 100 and the contract is never short.projected_yieldstays onLockas a display estimate only, and is never paid as a promise.The same file's two smaller defects are fixed: the duration range check now runs before the tier lookup (so
InvalidDurationfires for 0 and 61 instead ofDurationTierMissing), and one month is365 / 12days so a 12-month lock earns the advertised APY over a full 365-day year rather than 360 days.Related Issue
#40 — "fix(locked_in): pay real yield instead of yield promised at lock time".
Changes
[ADD]
crates/loktin-yield/Cargo.tomlrlib) holding the share math and the pool interface.[ADD]
crates/loktin-yield/src/lib.rsshares_for_deposit,assets_for_shares,position_yield(ERC-4626-style, rounds down) and thePoolclient trait matchingmock_pool'ssupply/withdraw/get_position.[ADD]
crates/loktin-yield/src/test.rs[MODIFY]
Cargo.toml/Cargo.lock["contracts/*", "crates/*"]; lockfile gains theloktin-yieldpackage and thelocked_independency edge.[MODIFY]
contracts/locked_in/Cargo.tomlloktin-yieldvia path.[MODIFY]
contracts/locked_in/src/types.rsLockgainsshares;projected_yieldis documented as a display-only estimate.DataKeygainsTotalShares.[MODIFY]
contracts/locked_in/src/lib.rslock()prices the deposit against the vault's pre-deposit value, rejects a zero-share mint (Error::ZeroShares), recordsshares, and incrementsTotalShares. Duration range check moved ahead of the tier lookup. One month is now2_628_000s (365 / 12days).unlock()paysassets_for_shares(shares, total_shares, vault value), pulling the shortfall from the pool first and failing withError::InsufficientLiquidityrather than underpaying. LocalPooltrait removed in favour ofloktin_yield::PoolClient.total_shares()/total_assets()reads and extends the instance TTL alongside persistent TTLs.[MODIFY]
contracts/locked_in/src/error.rsZeroShares = 18andInsufficientLiquidity = 19.[MODIFY]
contracts/locked_in/src/events.rsLockedevent carriessharesalongside the display estimate.[MODIFY]
contracts/locked_in/src/test.rsassert_solventclaim check, mirroring test(contracts): add multi-user test harness #36) plus the new regression tests below.Verification Results
test_two_users_no_pool_both_withdraw_in_full. Adds a local harness (funded_user,assert_solvent); #36 is still unmerged, so the helpers live in this test module and move to the shared crate when it lands.test_user_cannot_withdraw_more_than_shares_redeem_forasserts a non-zeroprojected_yieldbut pays only the shares' real value.InvalidDurationfires for duration 0 and for 61test_invalid_duration_fires_for_zero_and_sixty_oneassertsErr(Ok(Error::InvalidDuration))for both; the range check now precedes the tier lookup.shares <= 0→Error::ZeroSharesbefore the token transfer;test_zero_share_mint_is_rejectedalso asserts the balance is untouched and a whole-share deposit still works.loktin-commonextend_ttl_instance, called from the constructor and every state-changing entry point), butloktin-common(#38) is not onstaging, so a local mirror with the same name is used.loktin-commontest_lock_ids_are_one_indexed(they already started at 1 vianext_id); the sharedloktin-common::next_idis not onstagingyet.2_628_000s =365 / 12days, so 12 months is exactlySECONDS_PER_YEAR(365 days) and a "10% APY" tier pays 10%; documented at the constant and asserted intest_lock_records_shares_and_display_estimate.cargo test --workspacepassesCloses #40