Skip to content

Commit

Permalink
refactor(store,world): replace isStore with storeAddress (#1061)
Browse files Browse the repository at this point in the history
* refactor(store): replace isStore with storeAddress

* refactor(world): replace isStore with storeAddress

* rename WorldContext to WorldConsumer

* gas-report

* ignore storeAddress in deploy

* add storeAddress to MudV2Test

* fix StoreSwitch

* rebuild and simplify examples

* rebuild e2e

* refactor MudV2Test to avoid circular dependencies

* update templates

* move comment about systems from StoreConsumer to WorldConsumer

* use storeAddress in KeysWithValueHook for consistency

* some gas optimizations

* more optimizations

* refactor StoreSwitch to use storage for storeAddress, exclude MudTest from abi

* rebuild

* cleanup

* rebuild

* Update packages/store/src/StoreSwitch.sol

Co-authored-by: alvarius <[email protected]>

* Update packages/store/src/StoreCore.sol

Co-authored-by: alvarius <[email protected]>

* Create selfish-cycles-retire.md

* prettier

* Update .changeset/selfish-cycles-retire.md

Co-authored-by: alvarius <[email protected]>

* Update .changeset/selfish-cycles-retire.md

Co-authored-by: alvarius <[email protected]>

---------

Co-authored-by: alvarius <[email protected]>
  • Loading branch information
dk1a and alvrs authored Jul 18, 2023
1 parent e9258da commit a7b30c7
Show file tree
Hide file tree
Showing 57 changed files with 489 additions and 447 deletions.
23 changes: 23 additions & 0 deletions .changeset/selfish-cycles-retire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
"@latticexyz/std-contracts": minor
"@latticexyz/store": minor
"@latticexyz/world": minor
---

Rename `MudV2Test` to `MudTest` and move from `@latticexyz/std-contracts` to `@latticexyz/store`.

```solidity
// old import
import { MudV2Test } from "@latticexyz/std-contracts/src/test/MudV2Test.t.sol";
// new import
import { MudTest } from "@latticexyz/store/src/MudTest.sol";
```

Refactor `StoreSwitch` to use a storage slot instead of `function isStore()` to determine which contract is Store:

- Previously `StoreSwitch` called `isStore()` on `msg.sender` to determine if `msg.sender` is a `Store` contract. If the call succeeded, the `Store` methods were called on `msg.sender`, otherwise the data was written to the own storage.
- With this change `StoreSwitch` instead checks for an `address` in a known storage slot. If the address equals the own address, data is written to the own storage. If it is an external address, `Store` methods are called on this address. If it is unset (`address(0)`), store methods are called on `msg.sender`.
- In practice this has the same effect as before: By default the `World` contracts sets its own address in `StoreSwitch`, while `System` contracts keep the Store address undefined, so `Systems` write to their caller (`World`) if they are executed via `call` or directly to the `World` storage if they are executed via `delegatecall`.
- Besides gas savings, this change has two additional benefits:
1. it is now possible for `Systems` to explicitly set a `Store` address to make them exclusive to that `Store` and
2. table libraries can now be used in tests without having to provide an explicit `Store` argument, because the `MudTest` base contract redirects reads and writes to the internal `World` contract.
8 changes: 4 additions & 4 deletions docs/pages/world/world-101.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -256,11 +256,11 @@ Create a new file named `MySystemTest.t.sol` in the `test` folder.
pragma solidity >=0.8.0;
import "forge-std/Test.sol";
import { MudV2Test } from "@latticexyz/std-contracts/src/test/MudV2Test.t.sol";
import { MudTest } from "@latticexyz/store/src/MudTest.sol";
import { IWorld } from "../src/codegen/world/IWorld.sol";
import { Dog } from "../src/codegen/Tables.sol";
contract MySystemTest is MudV2Test {
contract MySystemTest is MudTest {
IWorld world;
function setUp() public override {
Expand All @@ -273,8 +273,8 @@ contract MySystemTest is MudV2Test {
// this will call the addEntry function on MySystem
bytes32 key = world.addEntry("bob", "blue");
// Expect the value retrieved from the Dog at the corresponding key to match "bob" and "blue"
string memory name = Dog.getName(world, key);
string memory color = Dog.getColor(world, key);
string memory name = Dog.getName(key);
string memory color = Dog.getColor(key);
assertEq(name, "bob");
assertEq(color, "blue");
}
Expand Down
1 change: 0 additions & 1 deletion e2e/packages/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
"@latticexyz/cli": "link:../../../packages/cli",
"@latticexyz/config": "link:../../../packages/config",
"@latticexyz/schema-type": "link:../../../packages/schema-type",
"@latticexyz/std-contracts": "link:../../../packages/std-contracts",
"@latticexyz/store": "link:../../../packages/store",
"@latticexyz/world": "link:../../../packages/world",
"@typechain/ethers-v5": "^10.2.0",
Expand Down
4 changes: 2 additions & 2 deletions e2e/packages/contracts/test/Worldgen.t.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import { MudV2Test } from "@latticexyz/std-contracts/src/test/MudV2Test.t.sol";
import { MudTest } from "@latticexyz/store/src/MudTest.sol";

import { ICustomErrorsSystem } from "../src/codegen/world/ICustomErrorsSystem.sol";
import { Position } from "../src/CustomTypes.sol";

contract WorldgenTest is MudV2Test {
contract WorldgenTest is MudTest {
function testError1WasGenerated() public {
vm.expectRevert();
revert ICustomErrorsSystem.TestError1();
Expand Down
14 changes: 0 additions & 14 deletions e2e/packages/contracts/types/ethers-contracts/IWorld.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ export interface IWorldInterface extends utils.Interface {
"grantAccess(bytes16,bytes16,address)": FunctionFragment;
"installModule(address,bytes)": FunctionFragment;
"installRootModule(address,bytes)": FunctionFragment;
"isStore()": FunctionFragment;
"pop()": FunctionFragment;
"popFromField(bytes16,bytes16,bytes32[],uint8,uint256)": FunctionFragment;
"popFromField(bytes32,bytes32[],uint8,uint256)": FunctionFragment;
Expand Down Expand Up @@ -93,7 +92,6 @@ export interface IWorldInterface extends utils.Interface {
| "grantAccess"
| "installModule"
| "installRootModule"
| "isStore"
| "pop"
| "popFromField(bytes16,bytes16,bytes32[],uint8,uint256)"
| "popFromField(bytes32,bytes32[],uint8,uint256)"
Expand Down Expand Up @@ -225,7 +223,6 @@ export interface IWorldInterface extends utils.Interface {
functionFragment: "installRootModule",
values: [PromiseOrValue<string>, PromiseOrValue<BytesLike>]
): string;
encodeFunctionData(functionFragment: "isStore", values?: undefined): string;
encodeFunctionData(functionFragment: "pop", values?: undefined): string;
encodeFunctionData(
functionFragment: "popFromField(bytes16,bytes16,bytes32[],uint8,uint256)",
Expand Down Expand Up @@ -491,7 +488,6 @@ export interface IWorldInterface extends utils.Interface {
functionFragment: "installRootModule",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "isStore", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "pop", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "popFromField(bytes16,bytes16,bytes32[],uint8,uint256)",
Expand Down Expand Up @@ -789,8 +785,6 @@ export interface IWorld extends BaseContract {
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;

isStore(overrides?: CallOverrides): Promise<[void]>;

pop(
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;
Expand Down Expand Up @@ -1097,8 +1091,6 @@ export interface IWorld extends BaseContract {
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;

isStore(overrides?: CallOverrides): Promise<void>;

pop(
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;
Expand Down Expand Up @@ -1405,8 +1397,6 @@ export interface IWorld extends BaseContract {
overrides?: CallOverrides
): Promise<void>;

isStore(overrides?: CallOverrides): Promise<void>;

pop(overrides?: CallOverrides): Promise<void>;

"popFromField(bytes16,bytes16,bytes32[],uint8,uint256)"(
Expand Down Expand Up @@ -1758,8 +1748,6 @@ export interface IWorld extends BaseContract {
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<BigNumber>;

isStore(overrides?: CallOverrides): Promise<BigNumber>;

pop(
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<BigNumber>;
Expand Down Expand Up @@ -2067,8 +2055,6 @@ export interface IWorld extends BaseContract {
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<PopulatedTransaction>;

isStore(overrides?: CallOverrides): Promise<PopulatedTransaction>;

pop(
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<PopulatedTransaction>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -717,13 +717,6 @@ const _abi = [
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [],
name: "isStore",
outputs: [],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "pop",
Expand Down
3 changes: 0 additions & 3 deletions e2e/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion examples/minimal/packages/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
"@latticexyz/config": "link:../../../../packages/config",
"@latticexyz/schema-type": "link:../../../../packages/schema-type",
"@latticexyz/solecs": "link:../../../../packages/solecs",
"@latticexyz/std-contracts": "link:../../../../packages/std-contracts",
"@latticexyz/store": "link:../../../../packages/store",
"@latticexyz/world": "link:../../../../packages/world",
"@solidstate/contracts": "^0.0.52",
Expand Down
4 changes: 2 additions & 2 deletions examples/minimal/packages/contracts/test/ChatNamespaced.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
pragma solidity >=0.8.0;

import "forge-std/Test.sol";
import { MudV2Test } from "@latticexyz/std-contracts/src/test/MudV2Test.t.sol";
import { MudTest } from "@latticexyz/store/src/MudTest.sol";
import { getKeysWithValue } from "@latticexyz/world/src/modules/keyswithvalue/getKeysWithValue.sol";
import { StoreCore } from "@latticexyz/store/src/StoreCore.sol";

import { IWorld } from "../src/codegen/world/IWorld.sol";
import { MessageTable, MessageTableTableId } from "../src/codegen/Tables.sol";
import { IChatNamespacedSystem } from "../src/interfaces/IChatNamespacedSystem.sol";

contract ChatNamespacedTest is MudV2Test {
contract ChatNamespacedTest is MudTest {
function testEmitEphemeral() public {
bytes32[] memory keyTuple;
vm.expectEmit(true, true, true, true);
Expand Down
12 changes: 6 additions & 6 deletions examples/minimal/packages/contracts/test/CounterTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
pragma solidity >=0.8.0;

import "forge-std/Test.sol";
import { MudV2Test } from "@latticexyz/std-contracts/src/test/MudV2Test.t.sol";
import { MudTest } from "@latticexyz/store/src/MudTest.sol";
import { getKeysWithValue } from "@latticexyz/world/src/modules/keyswithvalue/getKeysWithValue.sol";

import { IWorld } from "../src/codegen/world/IWorld.sol";
import { CounterTable, CounterTableTableId } from "../src/codegen/Tables.sol";

import { SingletonKey } from "../src/systems/IncrementSystem.sol";

contract CounterTest is MudV2Test {
contract CounterTest is MudTest {
IWorld world;

function setUp() public override {
Expand All @@ -30,19 +30,19 @@ contract CounterTest is MudV2Test {
function testCounter() public {
// Expect the counter to be 1 because it was incremented in the PostDeploy script.
bytes32 key = SingletonKey;
uint32 counter = CounterTable.get(world, key);
uint32 counter = CounterTable.get(key);
assertEq(counter, 1);

// Expect the counter to be 2 after calling increment.
world.increment();
counter = CounterTable.get(world, key);
counter = CounterTable.get(key);
assertEq(counter, 2);
}

function testKeysWithValue() public {
bytes32 key = SingletonKey;
uint32 counter = CounterTable.get(world, key);
bytes32[] memory keysWithValue = getKeysWithValue(world, CounterTableTableId, CounterTable.encode(counter));
uint32 counter = CounterTable.get(key);
bytes32[] memory keysWithValue = getKeysWithValue(CounterTableTableId, CounterTable.encode(counter));
assertEq(keysWithValue.length, 1);
}
}
4 changes: 2 additions & 2 deletions examples/minimal/packages/contracts/test/StructTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
pragma solidity >=0.8.0;

import "forge-std/Test.sol";
import { MudV2Test } from "@latticexyz/std-contracts/src/test/MudV2Test.t.sol";
import { MudTest } from "@latticexyz/store/src/MudTest.sol";
import { getKeysWithValue } from "@latticexyz/world/src/modules/keyswithvalue/getKeysWithValue.sol";

import { IWorld } from "../src/codegen/world/IWorld.sol";
import { CounterTable, CounterTableTableId } from "../src/codegen/Tables.sol";
import { BytesStruct, StringStruct } from "../src/systems/structs.sol";

contract StructTest is MudV2Test {
contract StructTest is MudTest {
IWorld world;

function setUp() public override {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export interface IWorldInterface extends utils.Interface {
"increment()": FunctionFragment;
"installModule(address,bytes)": FunctionFragment;
"installRootModule(address,bytes)": FunctionFragment;
"isStore()": FunctionFragment;
"pickUp(uint32,uint32)": FunctionFragment;
"popFromField(bytes16,bytes16,bytes32[],uint8,uint256)": FunctionFragment;
"popFromField(bytes32,bytes32[],uint8,uint256)": FunctionFragment;
Expand Down Expand Up @@ -107,7 +106,6 @@ export interface IWorldInterface extends utils.Interface {
| "increment"
| "installModule"
| "installRootModule"
| "isStore"
| "pickUp"
| "popFromField(bytes16,bytes16,bytes32[],uint8,uint256)"
| "popFromField(bytes32,bytes32[],uint8,uint256)"
Expand Down Expand Up @@ -248,7 +246,6 @@ export interface IWorldInterface extends utils.Interface {
functionFragment: "installRootModule",
values: [PromiseOrValue<string>, PromiseOrValue<BytesLike>]
): string;
encodeFunctionData(functionFragment: "isStore", values?: undefined): string;
encodeFunctionData(
functionFragment: "pickUp",
values: [PromiseOrValue<BigNumberish>, PromiseOrValue<BigNumberish>]
Expand Down Expand Up @@ -526,7 +523,6 @@ export interface IWorldInterface extends utils.Interface {
functionFragment: "installRootModule",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "isStore", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "pickUp", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "popFromField(bytes16,bytes16,bytes32[],uint8,uint256)",
Expand Down Expand Up @@ -847,8 +843,6 @@ export interface IWorld extends BaseContract {
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;

isStore(overrides?: CallOverrides): Promise<[void]>;

pickUp(
item: PromiseOrValue<BigNumberish>,
itemVariant: PromiseOrValue<BigNumberish>,
Expand Down Expand Up @@ -1169,8 +1163,6 @@ export interface IWorld extends BaseContract {
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;

isStore(overrides?: CallOverrides): Promise<void>;

pickUp(
item: PromiseOrValue<BigNumberish>,
itemVariant: PromiseOrValue<BigNumberish>,
Expand Down Expand Up @@ -1489,8 +1481,6 @@ export interface IWorld extends BaseContract {
overrides?: CallOverrides
): Promise<void>;

isStore(overrides?: CallOverrides): Promise<void>;

pickUp(
item: PromiseOrValue<BigNumberish>,
itemVariant: PromiseOrValue<BigNumberish>,
Expand Down Expand Up @@ -1856,8 +1846,6 @@ export interface IWorld extends BaseContract {
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<BigNumber>;

isStore(overrides?: CallOverrides): Promise<BigNumber>;

pickUp(
item: PromiseOrValue<BigNumberish>,
itemVariant: PromiseOrValue<BigNumberish>,
Expand Down Expand Up @@ -2179,8 +2167,6 @@ export interface IWorld extends BaseContract {
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<PopulatedTransaction>;

isStore(overrides?: CallOverrides): Promise<PopulatedTransaction>;

pickUp(
item: PromiseOrValue<BigNumberish>,
itemVariant: PromiseOrValue<BigNumberish>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -732,13 +732,6 @@ const _abi = [
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [],
name: "isStore",
outputs: [],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
Expand Down
5 changes: 1 addition & 4 deletions examples/minimal/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 0 additions & 7 deletions packages/store/abi/IStore.sol/IStore.abi.json
Original file line number Diff line number Diff line change
Expand Up @@ -428,13 +428,6 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "isStore",
"outputs": [],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
Expand Down
7 changes: 0 additions & 7 deletions packages/store/abi/IStore.sol/IStoreData.abi.json
Original file line number Diff line number Diff line change
Expand Up @@ -290,13 +290,6 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "isStore",
"outputs": [],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
Expand Down
Loading

0 comments on commit a7b30c7

Please sign in to comment.