Storage, by name
The contract
Arcade, the scoreboard of a multiplayer game: each player's record
is one packed slot and a name, kept in a mapping; playerList
lists who joined. Click a byte to find its variable, or a variable to
find its bytes.
Arcade.sol — the contract ( lines)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Arcade {
struct Player {
uint64 score; // ┐
uint32 combo; // │
uint32 bestCombo; // │ one full slot: 8+4+4+4+4+8 = 32 bytes
uint32 plays; // │
uint32 hits; // │
uint64 lastBlock; // ┘ when they last played
string name; // the next slot
}
address[] public playerList; // who joined, in order (a mapping can't list its keys)
string public motd; // the message of the day
uint128 public totalScore; // ┐ one slot: every player's points
uint64 public totalHits; // ┘ and every player's hits
mapping(address => Player) public players;
constructor(string memory initialMotd) { motd = initialMotd; }
function join(string calldata name) external { // pick a name, once
require(bytes(name).length > 0 && bytes(players[msg.sender].name).length == 0);
players[msg.sender].name = name;
playerList.push(msg.sender);
}
function play() external {
Player storage player = players[msg.sender];
require(bytes(player.name).length > 0); // joined
player.plays += 1;
player.lastBlock = uint64(block.number);
if (!_rolledHit()) { _resetCombo(player); return; } // a miss
player.combo += 1;
player.hits += 1;
if (player.combo > player.bestCombo) player.bestCombo = player.combo;
uint64 gained = _applyCombo(10, player.combo);
player.score += gained; totalScore += gained; totalHits += 1;
}
function setMotd(string calldata text) external { motd = text; } // anyone may
// a toy roll (not safe randomness): 2 in 3 is a hit
function _rolledHit() internal view returns (bool) {
return uint256(keccak256(abi.encode(block.prevrandao, msg.sender))) % 3 != 0;
}
function _resetCombo(Player storage player) internal { player.combo = 0; }
function _applyCombo(uint64 points, uint32 combo) internal pure returns (uint64) {
return points * (combo < 5 ? combo : 5); // the multiplier stops at 5
}
}
Scene
Halfway: alice, bob and carol have joined and played.
alice's third hit: her record before and after.
setMotd("gl hf"): a 50-byte string becomes a short one.
The same game compiled by Vyper: Solidity's rule finds zeros.
One 32-byte word per row; "⋯" skips addresses.
Ethdebug data from the compiler (as YAML;
template names shortened; solc writes $, shown as
~)
Step through the pointer's dereference to see how this compiler output finds these bytes
Storage changed byte no value shown
Variables changed unchanged
About this demo · part of the ethdebug post appendix, with ethdebug in depth and Step through a transaction