@@ -33,17 +33,16 @@ use crate::framework::prelude::*;
3333// the empirical chain-time ceiling sidesteps the bug until #3040
3434// lands at the dpp layer.
3535
36- /// Gross credits the bank submits when funding `addr_1`. The bank
37- /// uses `[ReduceOutput (0)]`, so addr_1 actually receives
38- /// `FUNDING_CREDITS − bank_fee`. Sized well above the chain-time
39- /// fee (~15M empirically) so addr_1 retains enough headroom to
40- /// fund the test's own self-transfer (see #3040 comment above).
36+ /// Credits the bank delivers to `addr_1`. The bank uses
37+ /// `[DeductFromInput (0)]`, so addr_1 receives this exact amount;
38+ /// the bank's fee is absorbed by the bank's own input. Sized well
39+ /// above the chain-time fee (~15M empirically) so addr_1 has
40+ /// enough headroom for the self-transfer (see #3040 comment above).
4141const FUNDING_CREDITS : u64 = 100_000_000 ;
4242
43- /// Lower bound on what addr_1 must receive after the bank's fee
44- /// deduction before the test proceeds. Pinned well below the raw
45- /// gross so the wait isn't sensitive to fee fluctuations across
46- /// protocol versions.
43+ /// Safety floor for the addr_1 wait. Under `[DeductFromInput(0)]`
44+ /// addr_1 receives FUNDING_CREDITS exactly; the floor is kept as a
45+ /// guard against an empty/stale observation slipping through.
4746const FUNDING_FLOOR : u64 = 70_000_000 ;
4847
4948/// Gross credits the test wallet submits in its self-transfer to
@@ -82,14 +81,19 @@ async fn transfer_between_two_platform_addresses() {
8281 . await
8382 . expect ( "derive addr_1" ) ;
8483
84+ // Snapshot bank balance before funding so we can derive the fee
85+ // the bank's input actually paid (invisible to the test wallet).
86+ let bank_pre = s. ctx . bank ( ) . total_credits ( ) . await ;
87+
8588 s. ctx
8689 . bank ( )
8790 . fund_address ( & addr_1, FUNDING_CREDITS )
8891 . await
8992 . expect ( "bank.fund_address" ) ;
9093
91- // Bank uses `[ReduceOutput(0)]`, so addr_1 receives
92- // `FUNDING_CREDITS − bank_fee`. Wait on the post-fee floor.
94+ // Bank uses `[DeductFromInput(0)]`: addr_1 receives FUNDING_CREDITS
95+ // exactly. Wait on the safety floor; the exact-amount assertion
96+ // follows after the test wallet syncs.
9397 wait_for_balance ( & s. test_wallet , & addr_1, FUNDING_FLOOR , STEP_TIMEOUT )
9498 . await
9599 . expect ( "addr_1 funding never observed" ) ;
@@ -116,31 +120,38 @@ async fn transfer_between_two_platform_addresses() {
116120 . await
117121 . expect ( "addr_2 transfer never observed" ) ;
118122
119- // Re-sync so the cached view reflects post-transfer state across
120- // BOTH addresses, then derive bank- and transfer-fee shares from
121- // observed balances.
123+ // Re-sync test wallet so the cached view reflects post-transfer
124+ // state across BOTH addresses.
122125 s. test_wallet
123126 . sync_balances ( )
124127 . await
125128 . expect ( "post-transfer sync" ) ;
126129 let balances = s. test_wallet . balances ( ) . await ;
127130 let received = balances. get ( & addr_2) . copied ( ) . unwrap_or ( 0 ) ;
128131 let remaining = balances. get ( & addr_1) . copied ( ) . unwrap_or ( 0 ) ;
129- let observed_total = received. saturating_add ( remaining) ;
130- // Bank's `ReduceOutput(0)` charged its fee against addr_1's
131- // funding output: the wallet's total post-transfer is
132- // `FUNDING_CREDITS − bank_fee − transfer_fee`. Each fee is the
133- // amount each ReduceOutput step trimmed off its respective
134- // output; together they equal `FUNDING_CREDITS − observed_total`.
135- let total_fees = FUNDING_CREDITS . saturating_sub ( observed_total) ;
136132 // The transfer fee is the share TRANSFER_CREDITS lost while
137- // crossing addr_1 -> addr_2.
133+ // crossing addr_1 -> addr_2 via `[ReduceOutput(0)]` .
138134 let transfer_fee = TRANSFER_CREDITS . saturating_sub ( received) ;
139- let bank_fee = total_fees. saturating_sub ( transfer_fee) ;
135+
136+ // Resync the bank to get its post-funding balance, then derive
137+ // the fee the bank's input absorbed under `[DeductFromInput(0)]`.
138+ s. ctx
139+ . bank ( )
140+ . sync_balances ( )
141+ . await
142+ . expect ( "bank post-funding sync" ) ;
143+ let bank_post = s. ctx . bank ( ) . total_credits ( ) . await ;
144+ // bank_pre - bank_post = FUNDING_CREDITS + bank_fee
145+ let bank_fee = bank_pre
146+ . saturating_sub ( bank_post)
147+ . saturating_sub ( FUNDING_CREDITS ) ;
148+
140149 tracing:: info!(
141150 target: "platform_wallet::e2e::cases::transfer" ,
142151 ?addr_1,
143152 ?addr_2,
153+ bank_pre,
154+ bank_post,
144155 funded = FUNDING_CREDITS ,
145156 received,
146157 remaining,
@@ -149,14 +160,25 @@ async fn transfer_between_two_platform_addresses() {
149160 "post-transfer balance snapshot"
150161 ) ;
151162
152- assert ! (
153- received >= TRANSFER_FLOOR ,
154- "addr_2 must hold at least TRANSFER_FLOOR ({TRANSFER_FLOOR}); observed {received}"
163+ // Under [ReduceOutput(0)], the protocol deducts the transfer fee
164+ // from output[0] — addr_2's received amount — not from addr_1's
165+ // residual. So addr_1 retains FUNDING_CREDITS - TRANSFER_CREDITS
166+ // and addr_2 receives TRANSFER_CREDITS - transfer_fee.
167+ assert_eq ! (
168+ remaining,
169+ FUNDING_CREDITS - TRANSFER_CREDITS ,
170+ "addr_1 must retain FUNDING_CREDITS - TRANSFER_CREDITS \
171+ (transfer_fee is deducted from addr_2's amount, not addr_1's residual). \
172+ observed remaining={remaining} expected={}",
173+ FUNDING_CREDITS - TRANSFER_CREDITS ,
155174 ) ;
156- assert ! (
157- received < TRANSFER_CREDITS ,
158- "addr_2 must hold less than TRANSFER_CREDITS ({TRANSFER_CREDITS}) \
159- after `ReduceOutput(0)` fee deduction; observed {received}"
175+ assert_eq ! (
176+ received,
177+ TRANSFER_CREDITS - transfer_fee,
178+ "addr_2 must receive TRANSFER_CREDITS minus the transfer fee \
179+ (ReduceOutput(0) deducts fee from the transferred amount). \
180+ observed received={received} expected={}",
181+ TRANSFER_CREDITS - transfer_fee,
160182 ) ;
161183 assert ! (
162184 transfer_fee > 0 ,
@@ -168,7 +190,8 @@ async fn transfer_between_two_platform_addresses() {
168190 ) ;
169191 assert ! (
170192 bank_fee > 0 ,
171- "bank funding must charge a non-zero fee (observed_total={observed_total})"
193+ "bank funding must charge a non-zero fee to its own input \
194+ (bank_pre={bank_pre} bank_post={bank_post} funded={FUNDING_CREDITS})"
172195 ) ;
173196
174197 s. teardown ( ) . await . expect ( "teardown" ) ;
0 commit comments