SKIP TO CONTENT
ETHEREUM STANDARDS6 minStandards That Ship / Ep. 9 · Stage 9-2

Grouped Transfer Bug: How the Proposed Fix Works

The proposed fix adds up the entries that share a token id, so 40 + 40 counts as 80. Was it merged, and what else could you do?

XIN
Pixel-art night scene of a vault checkpoint. A red lamp at the gate refuses one large crate, while a conveyor belt carries two identical smaller crates through the same gate under a green lamp, and a padlocked shelf in the vault behind stands empty.

IN 30 SECONDS

A cumulative per-id check is a fix that adds up every batch entry sharing a token id, then compares that running total with both the raw balance and the unfrozen amount. PR #2045 proposes it for the ERC-7943 reference code; it was unmerged on 7 October 2026.

KEY TAKEAWAYS · 3

  • With PR #2045 applied, the batch of 40 + 40 on one id is refused at the second entry, before any debit.
  • On 7 October 2026, the PR was open, unmerged and unreviewed, and issue #1814 was still open.
  • Any fix must make the check see the total per id. A balance check after the debit is a backstop, not a replacement.
On this page

A worked example: the same batch, before and after the fix

IN SHORT With PR #2045 applied, the same batch is refused at the second entry: the check now adds up entries that share an id.

WORDS TO KNOW

Regression test
A test that replays a bug once it is fixed, so the bug can never come back unnoticed.
Pull request
A proposed change to a project’s code, open for review. It changes nothing until someone merges it.
Self-transfer
A transfer where the sender and the receiver are the same account.

A WORKED EXAMPLE

REFUSEDPR #2045 applied: id 1 twice, 40 each

INPUT

Balance
100
Frozen
60
Batch
id 1 twice: 40 + 40

WHAT HAPPENS

  1. First entry: the running total for id 1 is 40, within the 40 unfrozen.
  2. Second entry: the running total for id 1 is now 80, over the 40 unfrozen.
  3. The call fails (it reverts) before any debit.

OUTPUT

Moved
0
Balance / frozen
100 / 60

BYPASSEDReference contract before the fix, same batch

INPUT

Batch
id 1 twice: 40 + 40

WHAT HAPPENS

  1. Each entry is checked against the same 40 unfrozen: both pass.
  2. Only then are both entries debited: 80 in total.

OUTPUT

Moved
80
Balance / frozen
20 / 60

ITEM GET

Add up per id

The fix doesn’t hunt for this one trick. Entries that share an id simply add up, like any other split.

These are the PR’s reproduction numbers, not made-up ones. Unfixed, the bypass is bounded: toward another account, never more than the raw balance, and each entry must fit the 40 unfrozen. But any whitelisted holder can use it, with no special role.

A Regression test A test that replays a bug once it is fixed, so the bug can never come back unnoticed. See the glossary replays this exact batch. It passes once the PR’s changes are applied.

Coming up next: So has this fix been merged yet?

Where issue #1814 and PR #2045 stand

IN SHORT On 7 October 2026, the fix sat in an open pull request with no reviews, and the issue was still open.

STEP BY STEP

Flow diagram: 5 stepsFlow diagram: 5 steps. 5 May 2026: ERC-7943 is Final, then 13 June 2026: issue #1814. 13 June 2026: issue #1814, then 19 June 2026: forum post suggested. 19 June 2026: forum post suggested, then 3 October 2026: PR #2045. 3 October 2026: PR #2045, then 7 October 2026: still open.15 May 2026: ERC-7943 isFinal213 June 2026: issue #1814319 June 2026: forum postsuggested43 October 2026: PR #204557 October 2026: stillopen

Step 1. Final. The commit “Update ERC-7943: Move to Final” lands on the ERC’s text.

Pick a step to see it move
  1. Final The commit “Update ERC-7943: Move to Final” lands on the ERC’s text.
  2. Reported @MavenRain opens an issue, a public bug report, on the repeated-id bypass in uRWA1155, with a proof of concept.
  3. Redirected A contributor recommends posting on the ERC’s thread on Ethereum Magicians, the standards forum.
  4. Fix proposed @Kshot3000 opens PR #2045: it fixes the transfer and burn branches (a burn destroys tokens) and adds three regression tests.
  5. Still open The PR is open and unmerged, with no reviews. The bot lists one author review and two editor reviews as required. The issue is still open too.
Dates from the ERCs repository on GitHub. Status as of 7 October 2026.

IN PLAIN WORDS

The Pull request A proposed change to a project’s code, open for review. It changes nothing until someone merges it. See the glossary keeps a running total per id. It refuses once that total exceeds the raw balance or the unfrozen amount. Burns get the same fix: there, repeated ids lowered the frozen amount too little. Its description reports 222 passing tests for the assets/erc-7943 folder.

Four ways to close the gap

IN SHORT Any fix has to make the check see the total per id, and every option has a trade-off.

Add up per id (PR #2045)
At each entry, check its id’s running total against the raw balance and the unfrozen amount
Cost grows with the square of batch length: fine for short batches. The PR also offers a seen-ids accumulator
Reject repeated ids
Fail if an id appears twice in the batch
Simplest; the issue notes it’s stricter than ERC-1155 requires
Check after the debit
Debit first, then, on holder transfers starting with balance ≥ frozen, require that inequality for each id that moved
Must run before the receiver hook. Misses self-transfers, so it doesn’t replace the aggregate check
One transfer at a time
Each entry is checked and debited before the next
One event per entry instead of one TransferBatch event, which likely uses more gas (the unit that measures computing work on Ethereum). A self-transfer can still execute a total the view refused

I like the check after the debit as a second line of defense. On holder transfers that start at or above the frozen amount, it asserts directly that frozen tokens stay put.

WATCH OUT

A freeze may exceed the balance: then holder transfers must not widen that gap. Forced transfers and privileged burns follow their own rule. If they spend frozen tokens, they update the frozen amount and emit Frozen before the transfer event.

In OpenZeppelin v5, ERC-1155 runs _update before the receiver check, the call that asks a receiving contract to accept the tokens. So a check at the end of _update comes first.

WATCH OUT

But it misses a Self-transfer A transfer where the sender and the receiver are the same account. See the glossary. On the unpatched contract with the post-check added, holder to holder, [40, 40] on one id still executes while canTransfer refuses 80. The balance is debited and credited back: nothing leaves, yet view and execution disagree.

I ran the stage 4 test for this post, not on client code. The post-check version passed two of its three tests. PR #2045 passed all three, self-transfers included.

So keep the aggregate check and treat the post-check as a backstop.

Sources: ERCs issue #1814, ERCs PR #2045 (open as of 7 Oct 2026).

STAGE CLEAR

What you now know

  • Where the fix stood on 7 October 2026: open, unmerged, with no reviews.
  • Why a check after the debit can back the fix up but can’t replace it.

Check yourself

Tap or click a question to see the answer.

You reject every batch that repeats an id. Is the 40 + 40 trick closed, and what does it cost you?

Closed: each entry is then its id’s total. The cost is refusing batches that ERC-1155 itself allows.

Next stage:Which two rules catch this kind of bug, and where else should you check them?6 min
Adam Boudjemaa
Adam Boudjemaa

Former CTO of Integra. Named author (1 of 5) of ERC-3643, first author of ERC-6960, co-author of ERC-7410, and co-author of ERC-8203, which is still a draft. Building production AI and regulated Web3 systems.

Have a project to ship or a role to fill?

Discuss your project or role
Follow the engineering notes

Get new articles on production AI and smart contracts by email.