EVM patterns
ERC1363 Callback Security: Receipt, Approval and Reentry
A token callback does not by itself prove that an application received an authorized payment. ERC1363 callback security requires authenticating the token, separating approval from receipt and checking the exact callback delivery path.

Key facts
- Original source census
- 6 public callback entrypoints mapped to 3 effective mutation bodies in OpenZeppelin v5.4.0
- Approval boundary
- 2 approval overloads set permission before the spender callback, not a transfer by the approval entrypoint itself
- Relaxed delivery
- 3 helpers with 6 code-presence branches, including 3 that request plain ERC20 operations
- Evidence limit
- Complete fixed-source inspection, no EVM execution or deployed-token security rate
Separate token receipt from spending permission
An approval is permission to spend. It is not a receipt for tokens.
That distinction matters when an application supports ERC1363, an extension that calls recipient or spender code after a token operation. A receiver can react to a completed transfer. A spender can react to a newly granted allowance. Both callbacks arrive from a token contract, but they describe different changes to state. Crediting a deposit merely because either callback ran confuses those changes.
Consider a proposed subscription integration. A customer approves a service through approveAndCall. The service sees an owner and an amount, then marks the subscription paid. Unless its callback also collects the required tokens successfully under the intended policy, the service has recorded payment from permission alone. This is an illustrative design error, not a claim about a deployed subscription or a documented exploit.
The ERC1363 specification defines separate interfaces for transfer reception and approval reception. Its familiar function names retain the underlying ERC20 meaning. The additional callback gives the application a place to act; it does not define that application's invoice, beneficiary, price or settlement policy.
- Token receipt
- A transfer changes token balances before the receiver callback in the pinned implementation.
- Spending permission
- An approval sets an allowance before the spender callback; collection can be a later action within that callback.
- Application completion
- The service's own authorized state transition, supported by the intended token movement and its acceptance criteria.
A successful callback also does not identify a trusted token automatically. Another contract can expose the same function names and supply plausible arguments. At the callback boundary, authenticate the immediate caller before using those arguments to mint credit, release an asset or update an order.
Amount policy belongs to the application too. ERC20 treats zero-value transfers as normal transfers, with the required event behavior. A payment receiver still needs to compare the supplied amount and resulting effect with what the service actually owes. Interface acceptance must not turn a technically valid zero-value operation into a paid entitlement. Decide how underpayment, excess payment and a changed invoice price are handled, then preserve those decisions in the receiver's acceptance conditions.
Our ERC20 token audit guide covers the broader token review. This article narrows the question to what the callback establishes, which authority it carries and what evidence still separates permission from payment. The useful outcome is a reviewable integration contract between the token and the application.
Original census of callback routes
Method
We enumerated the complete ERC1363 interface and implementation in OpenZeppelin Contracts v5.4.0, then classified every relaxed ERC1363 helper in the same release's SafeERC20 library. The unit of observation is a declared source entrypoint or an explicitly selected helper branch. It is not a token deployment, a transaction or an attack.
- Source and retrieval
- Fixed tagged source files fetched through the authenticated GitHub API on ; complete responses and hashes retained.
- Entrypoint population
- The implementation declares
7functions. ExcludingsupportsInterfaceleaves all6callback entrypoints, matching the interface signatures. - Helper selection
- Every function named
transferAndCallRelaxed,transferFromAndCallRelaxedorapproveAndCallRelaxedin the complete fixed library; other library functions are retained in the exclusion ledger. - Classification
- Brace-balanced source bodies identify empty-data wrappers, underlying operations and callback utilities. A manual read checks that the mutation call precedes the callback call.
- Artifacts
seo/research/erc1363-callback-security-2026-10-28/measurement.py,measurement.jsonandroutes.csv. These repository artifacts are not public downloads.
Results
| Entrypoint | Pre-callback operation | Callback family | Data handling |
|---|---|---|---|
transferAndCall(to,value) | transfer | Receiver | Empty-data wrapper |
transferAndCall(to,value,data) | transfer | Receiver | Supplied bytes |
transferFromAndCall(from,to,value) | transferFrom | Receiver | Empty-data wrapper |
transferFromAndCall(from,to,value,data) | transferFrom | Receiver | Supplied bytes |
approveAndCall(spender,value) | approve | Spender | Empty-data wrapper |
approveAndCall(spender,value,data) | approve | Spender | Supplied bytes |
Four entrypoints reach a transfer callback and two reach an approval callback. Three are empty-data wrappers, leaving three effective mutation bodies. Counting every overload as a separate payment mechanism would overstate what the interface contains. Conversely, treating the approval family as a transfer would misclassify its effect before the callback begins.
The helper census found 3 relaxed helpers with 6 code-presence branches: 3 select an ERC1363 callback route and 3 select a plain ERC20 operation. The delivery-policy table below identifies those branches. These counts describe the inspected release, not how often an application uses each route.
Limits
- No Solidity compiler or EVM executed. The result is a static source census with explicit classification.
- Only the pinned implementation and helper files were measured. Overrides and deployed configurations can change behavior.
- An approval callback can itself collect tokens. The classification concerns the operation before the callback, not every effect of the whole transaction.
- Source ordering does not prove that a receiver's accounting, authorization or reentry behavior is correct.
To reproduce the classification, inspect the resolved data-bearing body behind each empty-data wrapper. Preserve both signatures in the inventory while attributing the underlying operation once. That keeps interface coverage separate from implementation-path coverage.
Authenticate the token and the action
Begin with the callback caller. In the standard flow, msg.sender at the receiver is the token contract. The operator argument names the address that invoked the transfer-and-call operation, while from names the source of the tokens. They are not interchangeable identities.
The pinned direct-transfer path passes its context sender as both operator and source. Its delegated-transfer path passes the context sender as operator while retaining the supplied source address. A router can therefore be the operator for tokens belonging to someone else. A policy that intends to reward the source but credits the operator has changed the intended beneficiary.
Write down which identity controls each application effect. A payer can fund an invoice for another beneficiary if the product intentionally permits that arrangement. It can also be forbidden. ERC1363 does not choose between those policies, and a correctly recovered address cannot answer the business question on its own.
- Accept callbacks only from the token or explicitly approved token set. Define who can change that set and how existing integrations are rechecked.
- Associate the callback with the intended payer and beneficiary. Inspect the delegated route with different operator and source addresses rather than using the same address for every actor.
- Decode
dataunder an explicit application format. Reject malformed values and bind every field that changes the resulting order or credit. - Check whether the operation is allowed in the current application state. A well-formed callback for an already completed invoice must not create another entitlement.
- Retain an accepted control case beside each rejection test. An unrelated token failure does not establish that the intended authorization check worked.
Additional data has no standard-specified format. An application might encode an invoice identifier or beneficiary there, but those bytes are supplied through a public token operation. Their presence is not evidence of a private approval. If the design requires an independently signed authorization, its validation and consumption rules belong to a separate review.
Interface discovery is a compatibility signal. The token's advertised 0xb0202a11 interface identifier can help select an integration path, but it does not authorize the token as a source of application credit. The pinned callback utility directly invokes the relevant hook and compares its response. Keep a discovery result, the configured token identity and the actual callback outcome as separate fields in the review record. A matching interface label should never silently replace the token allowlist.
Empty data deserves its own acceptance decision. The no-data overloads are real entrypoints in the census, not malformed versions of a payment request. A receiver can reject empty data when its application requires an invoice. It should do so deliberately, with an error that distinguishes unsupported application input from a token compatibility failure.
Do not use the callback's expected return value as a substitute for these checks. The receiver returns 0x88a7ca5c and the spender returns 0x7b04a2d0 to signal interface-level acceptance. Those values identify the callback response; they do not attest that a price, payer or beneficiary was appropriate.
Review callback state before reentry
The token operation happens before the external callback in the pinned source. Application code therefore runs inside a larger transaction whose token state has already changed. Review what the application exposes while its own accounting transition is incomplete. A callback can call other contracts, including the token, so inspecting only the first entrypoint misses possible nested effects.
- Transfer path
- Check the received tokens and authorize the application credit without counting the same effect twice.
- Approval path
- Check the approved collection policy and establish successful collection before recording payment when collection is required.
- Final evidence
- Reconcile the token effect with the application's intended completed state.
The reentrancy analysis explains the wider state-transition problem. For this integration, enumerate every route that can spend, withdraw or mint against an intermediate credit. A guard on one callback does not automatically cover a different function that reads the same application state.
Approval reception also creates a timing question about allowance. The pinned base ERC20 implementation leaves an allowance equal to the maximum uint256 unchanged when spending. Finite allowances are reduced. A collection test that always expects a lower allowance can reject correct base behavior; an unlimited allowance does not show that no payment occurred.
Keep approval-race analysis separate from callback authenticity. The standard warns that changing an allowance can allow the old and new values to be spent through unfortunate ordering. Reducing an allowance before changing it addresses that particular transition, but it does not authenticate application data or make a collection path safe against every nested call.
Callback rejection has a precise implementation boundary. The pinned utilities reject a target without code or a response with the wrong selector. Empty revert data produces a utility-specific error, while nonempty revert data is propagated. Those failures revert the operation rather than providing a successful receipt. Rehearse the resulting application and token state; this census did not execute that rollback.
Treat relaxed helpers as a delivery policy
A relaxed helper can choose an operation that makes no callback. That may be appropriate for a general transfer utility, but it is a material policy difference for an application that depends on callback execution. Review the selected path instead of inferring its guarantee from the word safe in a library name.
| Helper | Target without code | Target with code |
|---|---|---|
transferAndCallRelaxed | safeTransfer | transferAndCall |
transferFromAndCallRelaxed | safeTransferFrom | transferFromAndCall |
approveAndCallRelaxed | forceApprove | approveAndCall |
For a target without code, a successful relaxed transfer does not establish that an invoice handler ran. The product must decide whether transferring tokens alone is an acceptable completion state. If callback execution is mandatory, a fallback that silently weakens that condition is incompatible with the application requirement.
Code presence is a routing observation, not proof of an address's identity or capabilities. A code-bearing target can lack the required hook and reject the operation. Review actual deployment and call conditions rather than describing the branch as a universal distinction between people and contracts.
Approval has another important asymmetry. In the no-code branch, forceApprove can retry an approval through a zero value. In the code-bearing branch, the helper attempts approveAndCall directly and relies on its true result. The zero-first retry behavior must not be assumed for that callback branch.
Our transaction simulation guide helps frame preflight evidence. A simulation should exercise the same helper, target code and token implementation that the application will use. Success on the plain ERC20 branch cannot stand in for a demonstrated callback path.
Record delivery policy as part of the integration configuration. A change from a strict operation to a relaxed helper can alter the meaning of success even if the public user action still has the same label. That change needs a review of the application's postcondition, not merely an update to an imported function name.
Reconcile payment from the resulting evidence
Define the completion evidence before launch. The question is which tokens moved under which authority and which application state was legitimately created. A transaction's successful status, a callback response and a displayed payment label are useful observations, but each has a narrower meaning than that full conclusion.
| Scenario | Required evidence | Interpretation limit |
|---|---|---|
| Expected direct transfer | Trusted token, intended receiver balance effect and authorized credit | A response selector alone does not identify the invoice |
| Delegated payment | Different operator and source identities mapped to the intended beneficiary | Operator is not automatically the payer |
| Approval-triggered collection | Allowance authorization plus successful required collection | Approval notification alone does not establish receipt |
| Nested callback attempt | Intermediate state cannot create an unauthorized second effect | A rejected first route does not cover every neighboring function |
| Wrong hook response | Revert outcome and expected application/token postconditions | The source census does not execute rollback |
| Relaxed no-code delivery | Explicitly accepted transfer-only or approval-only completion policy | Helper success does not prove callback execution |
Where token customization changes transfer amounts, derive the credit rule from that implementation and the product's acceptance policy. The callback's declared value should not be promoted into a universal balance-delta guarantee. The measured base source does not test custom fees, rebasing or overridden behavior.
Event-based monitoring needs the same distinction. In the pinned base token, allowance spending through transferFrom does not emit an Approval update by default. A missing approval-change event is therefore not proof that a finite allowance stayed unchanged. Compare the relevant state observation with the reviewed implementation's event behavior. Record unavailable observations as unknown rather than replacing them with a zero balance or a guessed remaining allowance. This matters when investigating why collection failed after an earlier approval notification.
Preserve the reviewed token and application versions alongside the chosen routes. Record accepted token configuration, actor mapping, data schema and the expected collection outcome. Include tests for malformed data and failed collection as well as the successful path. These are proposed acceptance checks, not executions performed for this article.
Use the security provider directory and Pharos Production member profile to identify a reviewer whose engagement can name the receiver, spender and payment logic. A token review that omits the consuming application cannot establish its invoice or credit semantics.
The census supplies a bounded map: six public callback entrypoints, three effective mutation bodies and distinct relaxed delivery branches. An integration is ready for its own release decision when the actual implementation's tests and resulting evidence support the promised application effect. Keep that evidence tied to the deployed revision and revisit it when collection or delivery policy changes.
Frequently asked questions
Does the payable token name mean the callback receives native ether?
The standard describes ERC20 token operations and passes a token amount to the callback. Native currency handling is a separate application design; the name does not make every callback a payable native-currency entrypoint.
Does ERC1363 define gasless signatures or a relay protocol?
Its callback interface does not define a permit-signature or relay protocol. Such authorization and delivery extensions need separate implementation review even when the same token also supports ERC1363.
Does the standard require every callback amount to use 18 decimals?
No fixed decimal scale is imposed by the callback interface. It passes a uint256 token quantity. Use the particular token and application unit conventions rather than treating a familiar display precision as part of callback authorization.