Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/StdCheats.sol
Original file line number Diff line number Diff line change
Expand Up @@ -827,8 +827,8 @@ abstract contract StdCheats is StdCheatsSafe {
(, bytes memory balData) = token.staticcall(abi.encodeWithSelector(0x70a08231, to));
uint256 prevBal = abi.decode(balData, (uint256));

// update balance
_stdstore.target(token).sig(0x70a08231).with_key(to).checked_write(give);
// update balance (packed-aware for tokens like USDC / sUSD that pack balance into a slot)
_stdstore.enable_packed_slots().target(token).sig(0x70a08231).with_key(to).checked_write(give);

// update total supply
if (adjust) {
Expand All @@ -850,7 +850,7 @@ abstract contract StdCheats is StdCheatsSafe {
uint256 prevBal = abi.decode(balData, (uint256));

// update balance
_stdstore.target(token).sig(0x00fdd58e).with_key(to).with_key(id).checked_write(give);
_stdstore.enable_packed_slots().target(token).sig(0x00fdd58e).with_key(to).with_key(id).checked_write(give);

// update total supply
if (adjust) {
Expand Down Expand Up @@ -884,9 +884,9 @@ abstract contract StdCheats is StdCheatsSafe {
(, bytes memory toBalData) = token.staticcall(abi.encodeWithSelector(0x70a08231, to));
uint256 toPrevBal = abi.decode(toBalData, (uint256));

// update balances
_stdstore.target(token).sig(0x70a08231).with_key(abi.decode(ownerData, (address))).checked_write(--fromPrevBal);
_stdstore.target(token).sig(0x70a08231).with_key(to).checked_write(++toPrevBal);
// update balances (packed-aware)
_stdstore.enable_packed_slots().target(token).sig(0x70a08231).with_key(abi.decode(ownerData, (address))).checked_write(--fromPrevBal);
_stdstore.enable_packed_slots().target(token).sig(0x70a08231).with_key(to).checked_write(++toPrevBal);

// update owner
_stdstore.target(token).sig(0x6352211e).with_key(id).checked_write(to);
Expand Down
34 changes: 34 additions & 0 deletions test/StdCheats.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,21 @@ contract StdCheatsTest is Test {
assertEq(barToken.totalSupply(), 10000e18);
}

function test_DealTokenPackedBalance() public {
// Balance packed in the low 128 bits of a storage word (USDC / sUSD class).
PackedBalanceToken token = new PackedBalanceToken();
address other = address(0xBEEF);
token.seed(address(this), 100, 0xdead);
token.seed(other, 50, 0xbeef);

deal(address(token), address(this), 999);
assertEq(token.balanceOf(address(this)), 999);
// high bits must be preserved
assertEq(token.other(address(this)), 0xdead);
assertEq(token.balanceOf(other), 50);
assertEq(token.other(other), 0xbeef);
}

function test_DealERC1155Token() public {
BarERC1155 barToken = new BarERC1155();
address bar = address(barToken);
Expand Down Expand Up @@ -672,6 +687,25 @@ contract BarERC721 {
mapping(address => uint256) private _balances;
}


/// ERC20-like balance packed with adjacent metadata in the same slot (low 128 = balance).
contract PackedBalanceToken {
// layout per address: [other:128][balance:128]
mapping(address => uint256) private _slot;

function seed(address account, uint128 bal, uint128 meta) external {
_slot[account] = uint256(bal) | (uint256(meta) << 128);
}

function balanceOf(address account) external view returns (uint256) {
return uint128(_slot[account]);
}

function other(address account) external view returns (uint256) {
return uint256(uint128(_slot[account] >> 128));
}
}

contract RevertingContract {
constructor() {
revert();
Expand Down