Skip to content

Commit

Permalink
Enable packed slots (#1369)
Browse files Browse the repository at this point in the history
  • Loading branch information
DrakeEvans authored Dec 21, 2024
1 parent 8fd59a6 commit 573479a
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 3 deletions.
25 changes: 25 additions & 0 deletions src/reference/forge-std/enable_packed_slots.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## `enable_packed_slots`

### Signature

```solidity
function enable_packed_slots(StdStorage storage self) internal returns (StdStorage storage);
```

### Description

Enables the usage of packed storage slots

### Examples

```solidity
// Write arbitrary balances even on gas-optimized contracts like AUSD
stdstore
.enable_packed_slots()
.target(_tokenAddress)
.sig("balanceOf(address)")
.with_key(_to)
.checked_write(
_amount
);
```
67 changes: 64 additions & 3 deletions src/reference/forge-std/std-storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ Query functions:
- [`sig`](./sig.md): Set the 4-byte selector of the function to static call
- [`with_key`](./with_key.md): Pass an argument to the function (can be used multiple times)
- [`depth`](./depth.md): Set the position of the value in the `tuple` (e.g. inside a `struct`)
- [`enable_packed_slots`](./enable_packed_slots.md): Allow the use of packed storage slots

Terminator functions:

- [`find`](./find.md): Return the slot number
- [`checked_write`](./checked_write.md): Set the data to be written to the storage slot(s)
- [`read_<type>`](./read.md): Read the value from the storage slot as `<type>`

### Example
### Simple Example

`playerToCharacter` tracks info about players' characters.

Expand Down Expand Up @@ -59,9 +60,69 @@ stdstore
.checked_write(120);
```

### Limitations

- Accessing packed slots is not supported
### Packed-Slot Example

`balanceOf()` returns the balance of a user in a gas-optimized ERC20 implementation.
`enable_packed_slots()` also works with [ERC7201 Namespaced Storage Slots](https://eips.ethereum.org/EIPS/eip-7201), proxy patterns, and packed slots as shown in the example below:

```solidity
// AgoraDollar.sol
contract AgoraDollar {
/// @notice The Erc20AccountData struct
/// @param isFrozen A boolean to indicate if the account is frozen
/// @param balance A uint248 to store the balance of the account
struct Erc20AccountData {
bool isFrozen;
uint248 balance;
}
/// @notice The Erc20CoreStorage struct
/// @param accountData A mapping of address to Erc20AccountData to store account data
/// @custom:storage-location erc7201:AgoraDollarErc1967Proxy.Erc20CoreStorage
struct Erc20CoreStorage {
/// @dev _account The account whose data we are accessing
/// @dev _accountData The account data for the account
mapping(address _account => Erc20AccountData _accountData) accountData;
}
/// @notice The ```ERC20_CORE_STORAGE_SLOT_``` is the storage slot for the Erc20CoreStorage struct
/// @dev keccak256(abi.encode(uint256(keccak256("AgoraDollarErc1967Proxy.Erc20CoreStorage")) - 1)) & ~bytes32(uint256(0xff))
bytes32 internal constant ERC20_CORE_STORAGE_SLOT_ =
0x455730fed596673e69db1907be2e521374ba893f1a04cc5f5dd931616cd6b700;
/// @notice The ```getPointerToErc20CoreStorage``` function returns a pointer to the Erc20CoreStorage struct
/// @return $ A pointer to the Erc20CoreStorage struct
function getPointerToErc20CoreStorage() internal pure returns (Erc20CoreStorage storage $) {
/// @solidity memory-safe-assembly
assembly {
$.slot := ERC20_CORE_STORAGE_SLOT_
}
}
/// @notice The ```balanceOf``` function returns the token balance of a given account
/// @param _account The account to check the balance of
/// @return The balance of the account
function balanceOf(address _account) external view returns (uint256) {
return getPointerToErc20CoreStorage().accountData[_account].balance;
}
}
```

Let's say we want a function to set the balance of an address to some amount on the AUSD contract.

```solidity
// TestAgoraDollar.t.sol
function seedUserWithAusd(address _to, uint248 _amount) internal {
stdstore
.enable_packed_slots()
.target(address(Constants.Mainnet.AUSD_ERC20)) //0x00000000eFE302BEAA2b3e6e1b18d08D69a9012a
.sig("balanceOf(address)")
.with_key(_to)
.checked_write(_amount);
}
```

### Known issues

Expand Down

0 comments on commit 573479a

Please sign in to comment.