中文版 · Blockchain learning guide · Previous article
Blockchain learning · Extension BCX010

A transfer request for twenty units does not by itself prove that twenty units arrived. A completed call, an acceptable return value, a change in the receiver’s holdings and a new internal credit are separate observations. An integration can handle the call convention correctly while recording the wrong quantity.
This lesson reconciles those layers through a fixed, fictional ledger. You will compare requested-amount crediting with actual-receipt crediting, distinguish an outer revert from a non-reverting rejection, and test whether later balance changes still cover fixed-unit claims.
Every quantity is an inert raw integer with no monetary value, price, interest, redemption promise or legal entitlement. There are no real tokens, wallets, approvals, deposits or financial recommendations. The examples are evidence worksheets, not production adapter code or a guarantee that a particular token can be supported safely.
Choose your reading route
In this guide
- 1. Fix the ledger and the claim being tested
- 2. Separate four layers of evidence
- 3. Declare the acceptance policy before calculating
- 4. Case A: ordinary transfer and matching credit
- 5. Case B: a deducted fee delivers eighteen
- 6. Case C: exact-receipt rejection with and without rollback
- 7. Case D: false, empty and true returns need separate effects
- 8. Case E: false after movement and wrapper failure paths
- 9. Case F: later balance adjustment leaves fixed credits unchanged
- 10. Case G: a wider observation window mixes two receipts
- 11. Case H: the balance observation itself is untrusted
- 12. Build a reconciliation record, not a one-word verdict
- Exercise with worked answers
- Sources and further reading
Sections 1–3 define the observations and policies. Sections 4–8 compute ordinary, fee-deducted and unusual-return cases. Sections 9–11 examine later balance changes, measurement attribution and untrusted observations. The exercise supplies exact answers. For a different question, how much permission remains after a transfer, see Token Allowances.
Back to contents · Next section
1. Fix the ledger and the claim being tested
Unless a case explicitly continues an earlier state, begin with sender S holding 100 units, receiver adapter D holding 50, fee bucket F holding 0, and aggregate internal credits C equal to 50. The requested transfer quantity is 20. The credits are fixed-unit bookkeeping claims in the toy, not proportional shares.
For the main cases, code and configuration are fixed, all required permissions are supplied, and there are no callbacks, unrelated balance adjustments or concurrent observations inside the call. Balance observations are truthful unless the defective-observation case explicitly removes that premise.
The tracked token holdings initially sum to 150 across S, D and F. Credits are a separate ledger and must not be added to token holdings as if they were extra tokens. The initial quantity coverage comparison is D=50 versus C=50.
We define a coverage gap as the amount by which credits exceed holdings, when positive. Equality can be useful, but does not alone prove correct attribution: crediting the wrong participant could preserve aggregate equality while violating the application policy.
Back to contents · Next section
2. Separate four layers of evidence
First ask whether the low-level call completed without reverting. Next ask whether its returned data satisfies the caller’s declared compatibility rule. Then establish the quantity attributable to the transfer. Only after that should the adapter calculate the permitted internal credit.
ERC-20, marked Final and created on 19 November 2015, defines a boolean-return interface and requires callers to handle false. Returning false is not itself the same operation as reverting the EVM call. What state changed depends on the supplied implementation and the surrounding call’s response.
Our deliberately defective mocks are not described as compliant ERC-20 implementations. Their purpose is to test reasoning when a return convention and an observed effect disagree. A true return with no movement does not become a conforming transfer because the lesson can model it.
For a broader identity and interface check, see Native Assets and Token Interface Evidence. Here the identities are supplied so that we can focus on the missing receipt-to-credit calculation.
Back to contents · Next section
3. Declare the acceptance policy before calculating
The requested-credit policy issues twenty credits whenever its supplied return-acceptance test passes, without checking delivered quantity. We will test why this is insufficient for some behaviours.
The actual-receipt policy accepts only the declared return convention and trustworthy, isolated evidence of attributable receipt. It issues a credit equal to that receipt. In the positive examples this is either 20 or 18. No positive receipt means no new credit.
The exact-receipt policy additionally requires receipt to equal the request of 20. A mismatch triggers a propagated outer revert across every modeled token, fee-bucket and adapter-credit write in that call tree. This rollback boundary is an explicit premise, not an inference from the word “reject.”
A fourth comparison path reports rejection without reverting. It issues no credit but leaves already completed token effects intact. That path may require further reconciliation; the exercise does not invent an automatic return or a permission to allocate the retained units.
Back to contents · Next section
4. Case A: ordinary transfer and matching credit
Start from the baseline. The transfer completes, returns true, debits S by 20 and increases D by 20. F is unchanged. Token balances become S=80, D=70 and F=0, still totaling 150.
Under either the actual-receipt or exact-receipt policy, the attributable receipt is 20 and the new credit is 20. Aggregate credits become C=70. Holdings 70 cover fixed credits 70 with no quantity gap.
This is the straightforward case that can make requested-credit accounting appear adequate. It is nevertheless one behaviour under declared assumptions, not proof that the same arithmetic applies to every other implementation.
A proper result records both token changes and credit changes. Merely writing “success” would hide which observations were tested and make the following variants harder to distinguish.
Back to contents · Next section
5. Case B: a deducted fee delivers eighteen
Reset to the baseline. The supplied mock deducts 20 from S, delivers 18 to D and allocates 2 to F, then returns true without reverting. The balances are S=80, D=68 and F=2. The total remains 150; the fee is inside the modeled token ledger, not an extra debit beyond the requested twenty.
If the adapter credits the requested 20, C becomes 70 while D holds 68. The quantity coverage gap is 2. Correct conservation across token balances has not prevented over-crediting in the separate internal ledger.
Under the actual-receipt policy, credit 18 instead. C becomes 68 and D holds 68. This satisfies that policy’s quantity reconciliation under the supplied truthful, isolated observations.
It does not satisfy a different promise that twenty units must arrive. An adapter cannot silently change an exact-delivery promise into an actual-receipt promise just because the latter avoids a coverage gap. State which service rule is being evaluated.
The example stipulates this fee behaviour; it does not claim every unusual transfer is standards-compliant or that supporting it is automatically appropriate. Compatibility remains a declared application decision.
Back to contents · Next section
6. Case C: exact-receipt rejection with and without rollback
Use the fee-deducted transfer again under the exact-receipt policy. Before the outer decision, the modeled token effects are S=80, D=68 and F=2. The observed receipt 18 differs from requested 20, so the outer operation propagates a revert.
By the explicitly supplied rollback boundary, all token and fee-bucket changes in that call tree are undone, and any modeled adapter-credit writes in it are also undone. The final tracked state is S=100, D=50, F=0 and C=50. No eighteen-unit transfer remains from this rejected operation.
Now reset and take the independent non-reverting rejection path. The same token movement occurs, but the caller reports failure and continues without issuing credits. Its final state is S=80, D=68, F=2 and C=50. The eighteen units are still held by D, and the fee bucket still has two.
These are different outcomes. A failure status is not proof that custody returned to the sender. The second case leaves an uncredited receipt that needs a separately specified resolution policy; no such action is performed here.
The rollback claim excludes earlier separate transactions, transaction fees, account counters and external observations. Do not use the toy’s restored four values to claim that every consequence of a rejected transaction disappears.
Back to contents · Next section
7. Case D: false, empty and true returns need separate effects
Consider three independent mocks from the baseline. In the first, the low-level call completes without reverting, returns false and makes no token movement. The supplied acceptance rule refuses to credit. S=100, D=50, F=0 and C=50 remain unchanged.
In the second, a contract call completes with empty return data, and the dossier independently establishes an exact twenty-unit transfer. A compatibility rule explicitly accepting that empty-return convention can proceed to the quantity check. If it credits the verified twenty, the final state is S=80, D=70, F=0 and C=70.
That does not mean arbitrary empty responses prove movement. Both acceptable call convention and the separately supplied effect evidence matter. An unsupported target or malformed return would require its own rule rather than being folded into this successful fixture.
In the third, a deliberately defective mock returns true but moves nothing. Truthful before-and-after observations show receipt 0. The actual-receipt policy issues no new credit, leaving the baseline unchanged. A requested-credit implementation that trusted only true would instead create C=70 against D=50, a gap of 20.
These cases show why a boolean is not a quantity measurement. They also prevent the opposite mistake: assuming every nonstandard return convention necessarily means no movement occurred.
Back to contents · Next section
8. Case E: false after movement and wrapper failure paths
A further independent mock completes an exact transfer of twenty but returns false without reverting. Before the adapter decides what to do, S=80, D=70 and F=0. This intentionally inconsistent mock is not asserted to conform to the standard.
If the caller handles false by returning failure without an outer revert, the completed transfer remains. With no new credit, C stays 50 and D stays 70. If instead a reverting wrapper path propagates an outer revert across the modeled call tree, the state returns to S=100, D=50, F=0 and C=50.
The pinned OpenZeppelin SafeERC20 source at v5.4.0 contains both reverting safe paths and try-style boolean-return paths. Its optional-return handling addresses call and return conventions; it does not measure the quantity delivered to the recipient.
Therefore a false result from a try-style path should not be described as automatic rollback of a token call that itself completed. The caller’s subsequent action matters. Conversely, a propagated outer revert is a state-reversion mechanism, not just a different label on the same retained transfer.
This is a version-pinned source observation, not a claim about the latest release or every wrapper. The lesson supplies no integration code and no blanket compatibility guarantee.
Back to contents · Next section
9. Case F: later balance adjustment leaves fixed credits unchanged
This case continues the successful ordinary Case A, where S=80, D=70, F=0 and C=70. At a later distinct step, the fictional token applies a declared negative-rebase rule: each modeled token balance is multiplied by four-fifths. These particular values produce integers, so no rounding rule is needed.
The new balances are S=64, D=56 and F=0. Their total is 120 rather than 150, as the supplied balance-adjustment rule changes the modeled supply. It is not a conserved transfer from D to another account.
The adapter’s fixed-unit credit ledger does not participate in that rebase. C remains 70. Holdings 56 therefore leave a quantity coverage gap of 14, even though the original twenty-unit receipt and credit were reconciled correctly.
This does not decide who bears the difference, whether claims may be modified, or how a real service should respond. No loss-allocation or redemption rule has been supplied. The finding is the mismatch between the current quantities under the stated model.
The pinned OpenZeppelin ERC20Wrapper v5.4.0 source warns that underlying balances changing without explicit transfers can desynchronize holdings and wrapper supply. Our fixed-credit calculation illustrates that kind of compatibility boundary without alleging that all wrappers have the same design or outcome.
Back to contents · Next section
10. Case G: a wider observation window mixes two receipts
Use a fresh off-chain measurement exercise. At the first snapshot, S=100, D=50, F=0, C=50, and a separate donor J holds 10. One completed transaction performs the fee-deducted requested transfer: S becomes 80, D becomes 68 and F becomes 2.
Later, a separate transaction moves 5 from J to D without a fee. At the second snapshot J holds 5 and D holds 73. The observed receiver delta across the whole window is 73−50=23, but only 18 of those units came from S’s requested transfer.
Crediting all 23 to S would misattribute J’s five units. Aggregate D and C could both become 73 and still conceal that attribution error. Under the stipulated sender-receipt rule, S receives only 18 new credits, so C becomes 68. The extra five held units are not assigned by this worksheet.
There is no independent transaction interleaved between two reads inside one serial EVM call in this example. The snapshots span two separate completed transactions off chain. The contamination comes from using a wider observation window than the operation being credited.
Before-and-after subtraction can be useful when the observation scope is trustworthy and attributable. It is not a universal substitute for defining that scope. Preserve transaction and state boundaries in the evidence record.
Back to contents · Next section
11. Case H: the balance observation itself is untrusted
Return to the baseline and use a defective mock that makes no actual movement. Its first balance query reports D=50 and its later query reports D=70 despite actual holdings remaining 50. The apparent delta is 20, but the premise of truthful observations is explicitly false.
A subtraction-only procedure cannot establish actual receipt from those reports. A reviewer with this supplied dossier should reject the compatibility claim or mark receipt evidence unsupported; the mock’s reported values do not override the stated actual ledger.
This is not repaired by treating the same token’s return true as independent corroboration. Both reports come from the behaviour under question. The exercise does not claim there is a universal on-chain observer that can recover every malicious token’s hidden intended meaning.
Unsupported rebasing, callbacks, mutable implementations or ambiguous measurements similarly require their own specification and review. Rejecting unsupported behaviour is a legitimate compatibility policy; accepting every possible token is not a requirement of this worksheet.
Back to contents · Next section
12. Build a reconciliation record, not a one-word verdict
Record the initial sender, receiver, fee and credit quantities; the request; the low-level outcome; the return convention; attributable movement; chosen policy; and final outer outcome. Then compute holdings, credits and any gap. Label sequential changes separately from independent test resets.
The fee case exposes quantity over-crediting. The false-after-movement case exposes mistaken rollback assumptions. The rebase case exposes later divergence. The wider-window case exposes wrong attribution even when totals can be made equal. These are different defects and need different evidence.
A bounded model verdict is: “Under truthful isolated observations, the actual-receipt policy credits eighteen for the fee-deducted transfer. The exact-receipt policy rejects and restores baseline only because the outer revert is explicitly propagated. A non-reverting failure retains the modeled transfer effects. Later balance adjustment can still make fixed claims exceed holdings.”
For how already-established claims interact across systems, see Nested Claims and Dependencies. This lesson establishes the earlier accounting link rather than assuming a supplied backing ratio.
Back to contents · Next section
Exercise with worked answers
- A request debits 20, delivers 18 and assigns 2 to the fee bucket. Where are the twenty units? Eighteen at D and two at F; S has decreased by twenty.
- What gap follows crediting 20 after that receipt? D=68, C=70, so the gap is 2.
- What follows crediting the actual eighteen? D=68 and C=68, under the stated receipt policy.
- Does exact-receipt rejection always restore the sender? Only under the explicitly propagated rollback path here. Non-reverting rejection leaves S=80, D=68 and F=2.
- Does false imply that no movement happened? No. Compare the separate no-movement and completed-movement mocks.
- After the supplied four-fifths rebase, what covers the seventy fixed credits? Only 56 held units; the gap is 14.
- May a window delta of 23 all be assigned to S? No. The dossier attributes 18 to S and 5 to J.
- Does an apparent delta from a lying observation prove receipt? No. The trustworthy-observation premise is missing.
For an extra check, suppose the fee is 3 rather than 2, with the same total sender debit 20 and no other changes. Receipt is 17. Requested-credit accounting gives D=67, C=70 and gap 3; actual-receipt credit gives D=C=67. This independent variation changes quantity, not the underlying distinction between the two policies.
Back to contents · Next section
Sources and further reading
- ERC-20: Final, created 19 November 2015; boolean interface and false-return handling, checked 12 October 2026.
- SafeERC20 v5.4.0 source: optional-return checks and reverting versus try-style paths; checked 12 October 2026, not presented as the latest release.
- ERC20Wrapper v5.4.0 source: warning about balance-change desynchronization; checked 12 October 2026.
The useful accounting question is not just whether a call looked successful. It is what actually arrived, what the application promised to credit, and whether the later held quantity still supports that promise under its stated rules.
