diff --git a/.changeset/brave-needles-love.md b/.changeset/brave-needles-love.md
new file mode 100644
index 0000000000..00edffbeee
--- /dev/null
+++ b/.changeset/brave-needles-love.md
@@ -0,0 +1,5 @@
+---
+"@latticexyz/network": major
+---
+
+Removes `network` package. Please see the [changelog](https://mud.dev/changelog) for how to migrate your app to the new `store-sync` package. Or create a new project from an up-to-date template with `pnpm create mud@next your-app-name`.
diff --git a/packages/world/gas-report.json b/packages/world/gas-report.json
index dff4daad5e..00f5ec4ed6 100644
--- a/packages/world/gas-report.json
+++ b/packages/world/gas-report.json
@@ -1,4 +1,40 @@
 [
+  {
+    "file": "test/AccessControl.t.sol",
+    "test": "testAccessControl",
+    "name": "AccessControl: hasAccess (cold)",
+    "gasUsed": 12746
+  },
+  {
+    "file": "test/AccessControl.t.sol",
+    "test": "testAccessControl",
+    "name": "AccessControl: hasAccess (warm, namespace only)",
+    "gasUsed": 3451
+  },
+  {
+    "file": "test/AccessControl.t.sol",
+    "test": "testAccessControl",
+    "name": "AccessControl: hasAccess (warm)",
+    "gasUsed": 6779
+  },
+  {
+    "file": "test/AccessControl.t.sol",
+    "test": "testRequireAccess",
+    "name": "AccessControl: requireAccess (cold)",
+    "gasUsed": 12789
+  },
+  {
+    "file": "test/AccessControl.t.sol",
+    "test": "testRequireAccess",
+    "name": "AccessControl: requireAccess (warm)",
+    "gasUsed": 6795
+  },
+  {
+    "file": "test/AccessControl.t.sol",
+    "test": "testRequireAccess",
+    "name": "AccessControl: requireAccess (this address)",
+    "gasUsed": 153
+  },
   {
     "file": "test/KeysInTableModule.t.sol",
     "test": "testInstallComposite",
diff --git a/packages/world/test/AccessControl.t.sol b/packages/world/test/AccessControl.t.sol
index 39092d375c..b9f571cd39 100644
--- a/packages/world/test/AccessControl.t.sol
+++ b/packages/world/test/AccessControl.t.sol
@@ -2,8 +2,10 @@
 pragma solidity >=0.8.0;
 
 import "forge-std/Test.sol";
+import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol";
 import { StoreReadWithStubs } from "@latticexyz/store/src/StoreReadWithStubs.sol";
 
+import { IWorldErrors } from "../src/interfaces/IWorldErrors.sol";
 import { World } from "../src/World.sol";
 import { AccessControl } from "../src/AccessControl.sol";
 import { ResourceSelector } from "../src/ResourceSelector.sol";
@@ -11,34 +13,46 @@ import { ResourceSelector } from "../src/ResourceSelector.sol";
 import { ResourceAccess } from "../src/tables/ResourceAccess.sol";
 import { NamespaceOwner } from "../src/tables/NamespaceOwner.sol";
 
-contract AccessControlTest is Test, StoreReadWithStubs {
-  bytes16 namespace = "namespace";
-  bytes16 name = "name";
-  address caller = address(0x01);
+contract AccessControlTest is Test, GasReporter, StoreReadWithStubs {
+  bytes16 constant namespace = "namespace";
+  bytes16 constant name = "name";
+  address constant presetCaller = address(0x0123);
+  address constant caller = address(0x01);
 
   function setUp() public {
     ResourceAccess.register();
     NamespaceOwner.register();
 
     NamespaceOwner.set(namespace, address(this));
-    ResourceAccess.set(ResourceSelector.from(namespace), address(this), true);
+    ResourceAccess.set(ResourceSelector.from(namespace, name), presetCaller, true);
   }
 
   function testAccessControl() public {
+    bool hasAccess;
+
     // Check that the caller has no access to the namespace or name
-    assertFalse(AccessControl.hasAccess(ResourceSelector.from(namespace, name), caller));
+    startGasReport("AccessControl: hasAccess (cold)");
+    hasAccess = AccessControl.hasAccess(ResourceSelector.from(namespace, name), caller);
+    endGasReport();
+    assertFalse(hasAccess);
 
     // Grant access to the namespace
     ResourceAccess.set(ResourceSelector.from(namespace, 0), caller, true);
 
     // Check that the caller has access to the namespace or name
-    assertTrue(AccessControl.hasAccess(ResourceSelector.from(namespace, name), caller));
+    startGasReport("AccessControl: hasAccess (warm, namespace only)");
+    hasAccess = AccessControl.hasAccess(ResourceSelector.from(namespace, name), caller);
+    endGasReport();
+    assertTrue(hasAccess);
 
     // Revoke access to the namespace
     ResourceAccess.set(ResourceSelector.from(namespace, 0), caller, false);
 
     // Check that the caller has no access to the namespace or name
-    assertFalse(AccessControl.hasAccess(ResourceSelector.from(namespace, name), caller));
+    startGasReport("AccessControl: hasAccess (warm)");
+    hasAccess = AccessControl.hasAccess(ResourceSelector.from(namespace, name), caller);
+    endGasReport();
+    assertFalse(hasAccess);
 
     // Grant access to the name
     ResourceAccess.set(ResourceSelector.from(namespace, name), caller, true);
@@ -52,4 +66,27 @@ contract AccessControlTest is Test, StoreReadWithStubs {
     // Check that the caller has no access to the namespace or name
     assertFalse(AccessControl.hasAccess(ResourceSelector.from(namespace, name), caller));
   }
+
+  function testRequireAccess() public {
+    bytes32 resourceSelector = ResourceSelector.from(namespace, name);
+    startGasReport("AccessControl: requireAccess (cold)");
+    AccessControl.requireAccess(resourceSelector, presetCaller);
+    endGasReport();
+
+    startGasReport("AccessControl: requireAccess (warm)");
+    AccessControl.requireAccess(resourceSelector, presetCaller);
+    endGasReport();
+
+    startGasReport("AccessControl: requireAccess (this address)");
+    AccessControl.requireAccess(resourceSelector, address(this));
+    endGasReport();
+  }
+
+  function testRequireAccessRevert() public {
+    bytes32 resourceSelector = ResourceSelector.from(namespace, name);
+    vm.expectRevert(
+      abi.encodeWithSelector(IWorldErrors.AccessDenied.selector, ResourceSelector.toString(resourceSelector), caller)
+    );
+    AccessControl.requireAccess(resourceSelector, caller);
+  }
 }