feat: add immutable protocol fee split on SME withdrawal with tests a…#590
Open
TYDev01 wants to merge 1 commit into
Open
feat: add immutable protocol fee split on SME withdrawal with tests a…#590TYDev01 wants to merge 1 commit into
TYDev01 wants to merge 1 commit into
Conversation
…nd docs Add an optional, immutable protocol_fee_bps configured at init that splits funded_amount into an SME payout and a treasury fee on withdraw. - init: new Option<i64> protocol_fee_bps param, validated 0..=10_000 (ProtocolFeeBpsOutOfRange), stored under additive DataKey::ProtocolFeeBps (default 0). - withdraw: fee = funded_amount * fee_bps / 10_000 (floor, checked) routed to DataKey::Treasury; remainder to sme_address. Treasury transfer skipped when fee == 0 (byte-identical legacy path); SME transfer skipped when net == 0. DistributedPrincipal still advances by the full gross funded_amount. - SmeWithdrew: append-only `fee` field; `amount` is now the net SME payout. - New typed errors WithdrawFeeArithmeticOverflow / WithdrawNetArithmeticUnderflow; new getter get_protocol_fee_bps. - Tests: fee split, zero-fee default, max bps, floor rounding, tiny-bps floor, large overflow-safe amount, getter, event payload, init range rejection. - Docs: escrow-numeric-model, README, ESCROW_SME_WITHDRAWAL, error-messages, EVENT_SCHEMA. Conservation invariant holds for every withdrawal: sme_payout + fee == funded_amount. Refs Liquifact#316
|
@TYDev01 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! 🚀 |
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.
close #316
Support a configurable protocol fee deducted at SME withdrawal (#316)
Implements an immutable
protocol_fee_bpsconfigured atinitthat, onwithdraw, splitsfunded_amountinto an SME payout and a treasury fee. Closes #316.What changed
escrow/src/lib.rsinit— new final paramprotocol_fee_bps: Option<i64>, validated0..=10_000(
ProtocolFeeBpsOutOfRange), stored under the new additiveDataKey::ProtocolFeeBps(default
0; always written so reads never branch on absence).withdraw— checked split:fee→DataKey::Treasury,sme_payout→sme_address, both via the existingSEP-41 balance-delta–checked transfer wrapper. The treasury transfer is skipped when
fee == 0(one transfer, identical to the pre-fee path); the SME transfer is skippedwhen
net == 0(only at10_000bps).DistributedPrincipaladvances by the fullgross
funded_amount.SmeWithdrew— append-onlyfeefield;amountis now the net SME payout.WithdrawFeeArithmeticOverflow(181),WithdrawNetArithmeticUnderflow(182),
ProtocolFeeBpsOutOfRange(180); new viewget_protocol_fee_bps.Tests (
escrow/src/tests/integration.rs) — fee split & treasury delta, zero-feedefault, max-bps (100% → treasury), floor-rounding residue to SME, tiny-bps floor-to-zero,
large overflow-safe amount, getter, event payload, init range rejection.
Docs —
escrow-numeric-model.md,README.md,ESCROW_SME_WITHDRAWAL.MD(on-chaindisbursement interaction),
escrow-error-messages.md,EVENT_SCHEMA.md.Security notes
sme_payout + fee == funded_amountfor every withdrawal — no principalcreated or destroyed.
funded_amountis not bounded byMAX_INVOICE_AMOUNT(over-fundingis allowed), so the fee multiply/divide and the net subtract use checked arithmetic with
typed errors rather than silent wrap.
0..=10_000atinit; never mutated after.protocol_fee_bps == 0(or omitted) reproduces the exact pre-feebehavior;
DataKey::ProtocolFeeBpsis additive (reads0on older instances).withdrawpath, not off-chain
settle,refund, orclaim_investor_payout— documented inESCROW_SME_WITHDRAWAL.MD.The base branch does not compile due to pre-existing errors unrelated to #316
(undeclared
PausedBlocks*variants, duplicateguard_status_*fns, a duplicate201discriminant, a
tier_lock_secsdouble-assign, and a duplicate import inintegration.rs).This PR intentionally does not touch them, so
cargo build/cargo teststill fail onthat pre-existing breakage.
The protocol-fee logic was verified by temporarily stubbing those pre-existing errors and
running the new tests through a throwaway external test target (which compiles only the
library, not the broken
#[cfg(test)]modules); the stubs were then fully reverted. Result:With the pre-existing errors stubbed,
rustfmt --checkis clean on all changed code.Out of scope / follow-up
Adding the new
initargument breaks the ~420initcall sites in the other testfiles (
coverage.rs,funding.rs, etc.). They are not updated here, since the suitecannot compile until the pre-existing breakage above is fixed regardless. Recommended
follow-up (separate PR): fix the pre-existing compile errors, then append the trailing
&Nonefee arg across the remaining test files so the full suite builds and the 95% coveragegate can be measured.