Reproduced Exploit
Ruyui — a shop purchase without signup mints 100 CREDIT for 0
purchaseShopItem assumes the caller already signed up. It does not check hasSignedUp. A caller who never signed up, and who pays nothing, still gets item 1 recorded and 100 CREDIT minted.
Chain
Other
Category
access-control
Date
Jan 2026
Source
AuditVault
EVM Playground
Source-level debugger — step opcodes and Solidity in sync
The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.
Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 64703-h-01-it-is-possible-to-purchase-in-game-items-without-signin. Standalone Foundry PoC and full write-up: 64703-h-01-it-is-possible-to-purchase-in-game-items-without-signin_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/access-control/missing-check · vuln/logic/missing-validation
Reproduction: a self-contained Foundry PoC that compiles and runs offline with only
forge-std— no fork and no RPC. Trace: output.txt. PoC: test/64703-h-01-it-is-possible-to-purchase-in-game-items-without-signin_exp.sol.
Key info#
| Impact | HIGH — a caller who never signed up receives the item and 100 CREDIT for 0 |
| Protocol | Roots of Embervault, Ruyui shop |
| Vulnerable code | purchaseShopItem updates userShopItemPurchases and mints credit with no hasSignedUp check |
| Bug class | Purchase does not require signup |
| Finding | Shieldify — Roots of Embervault / Ruyui, January 2026 · id 64703, H-01 |
| Report | Ruyui security review |
| Source | AuditVault finding |
| Status | Reproduced here as a local synthetic. |
| Compiler | solc 0.8.27, cancun, via IR |
TL;DR#
purchaseShopItem assumes the caller already signed up. It does not check hasSignedUp. A caller who never signed up, and who pays nothing, still gets item 1 recorded and 100 CREDIT minted.
Control requires hasSignedUp. The same purchase reverts. After signUp(), the purchase mints the 100 CREDIT.
The vulnerable code#
function purchaseShopItem(uint256 itemId) external {
// @> VULN: no hasSignedUp check
userShopItemPurchases[msg.sender][itemId] += 1;
credit.mint(msg.sender, CREDIT_OUT);
}
The fix is require(hasSignedUp[msg.sender], "signup") before those writes.
Root cause#
Signup is what records the user and the referee. The purchase path updates per-user item state and pays credit as if that had already happened. Skipping it grants the item and the credit without the signup fee and without a referee.
Preconditions#
hasSignedUpfor the caller is false.- The caller sends no value.
Attack walkthrough#
- Confirm the caller has not signed up.
- Call
purchaseShopItem(1). - The item counter becomes 1 and the shop mints 100 CREDIT.
- The caller's credit balance is 100.
hasSignedUpis still false.
Control reverts with signup until signUp() is called. The signed-up purchase then mints 100 CREDIT.
Diagrams#
Impact#
The caller receives item 1 and 100 CREDIT without signing up and without paying. The referee who would have been set at signup is never paid.
Remediation#
Require hasSignedUp(msg.sender) at the start of purchaseShopItem and purchaseBattlePass.
Sources#
Taxonomy#
severity/highsector/gamingplatform/shieldify
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 64703-h-01-it-is-possible-to-purchase-in-game-items-without-signin_exp (evm-hack-registry mirror).
- AuditVault finding: 64703-h-01-it-is-possible-to-purchase-in-game-items-without-signin.
Alerts & third-party analyses
These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.