diff --git a/.env.anvil b/.env.anvil index ad3d43e..f6e2f8f 100644 --- a/.env.anvil +++ b/.env.anvil @@ -1,5 +1,9 @@ # Anvil default private key: PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 +PLAYER1_PRIVATE_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d +PLAYER2_PRIVATE_KEY=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a +PLAYER3_PRIVATE_KEY=0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6 +PLAYER4_PRIVATE_KEY=0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a # Anvil World address: WORLD_ADDRESS=0x8a791620dd6260079bf849dc5567adc3f2fdc318 @@ -8,4 +12,6 @@ WORLD_ADDRESS=0x8a791620dd6260079bf849dc5567adc3f2fdc318 RPC_URL=http://127.0.0.1:8545 # Enable debug logs for MUD CLI -DEBUG=mud:* \ No newline at end of file +DEBUG=mud:* + +DEFAULT_NAMESPACE=beauKode_dev \ No newline at end of file diff --git a/.env.garnet b/.env.garnet new file mode 100644 index 0000000..1e0b2c1 --- /dev/null +++ b/.env.garnet @@ -0,0 +1,12 @@ +# EVE World Address +WORLD_ADDRESS=0x7fe660995b0c59b6975d5d59973e2668af6bb9c5 + +RPC_URL=https://rpc.garnetchain.com +CHAIN_ID=17069 + +# Enable debug logs for MUD CLI +DEBUG=mud:* + +DEFAULT_NAMESPACE=beauKode_dev + + diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..cbde96a --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @beaukode \ No newline at end of file diff --git a/.github/workflows/foundry-tests.yml b/.github/workflows/foundry-tests.yml new file mode 100644 index 0000000..552b170 --- /dev/null +++ b/.github/workflows/foundry-tests.yml @@ -0,0 +1,42 @@ +name: Foundry tests + +on: + workflow_call: + +jobs: + local-deploy-and-test: + concurrency: + group: "test-${{github.ref}}" + cancel-in-progress: true + runs-on: "ubuntu-latest" + steps: + - uses: actions/checkout@v4 + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly-d14c09f15a9849fe177d097451919810e5877617 + - name: Setup Node 20.x Environment + uses: actions/setup-node@v3 + with: + node-version: 20.x + - name: "Install dependencies" + run: "npm ci" + - name: "Lint" + run: "npm run lint" + - uses: JarvusInnovations/background-action@v1 + name: Start Anvil + with: + run: | + gunzip anvil-test-state.json.gz + anvil --block-time 10 --block-base-fee-per-gas 0 --gas-limit 3000000000 --hardfork cancun --host 0.0.0.0 --port 8545 --state anvil-test-state.json & + wait-on: | + tcp:localhost:8545 + wait-for: 30s + tail: false + log-output: true + - name: "Deploy to Anvil" + run: | + npm run deploy:local + - name: "Run tests" + run: | + npm run test diff --git a/.github/workflows/on-main.yml b/.github/workflows/on-main.yml new file mode 100644 index 0000000..2513f64 --- /dev/null +++ b/.github/workflows/on-main.yml @@ -0,0 +1,31 @@ +name: Deploy staging + +on: + push: + branches: + - main + +jobs: + local-deploy-and-test: + uses: ./.github/workflows/foundry-tests.yml + deploy-staging: + concurrency: + group: "deploy-staging-${{github.ref}}" + cancel-in-progress: false + runs-on: "ubuntu-latest" + environment: "staging" + steps: + - uses: actions/checkout@v4 + - name: Setup Node 20.x Environment + uses: actions/setup-node@v3 + with: + node-version: 20.x + - name: "Install dependencies" + run: "npm ci" + - name: "Deploy corporations" + env: + PRIVATE_KEY: ${{ secrets.CORPORATIONS_PRIVATE_KEY }} + CORPORATIONS_NAMESPACE: ${{ vars.CORPORATIONS_NAMESPACE }} + run: | + cd packages/corporations + npm run deploy:garnet diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml new file mode 100644 index 0000000..cf3a184 --- /dev/null +++ b/.github/workflows/on-pr.yml @@ -0,0 +1,8 @@ +name: Pull request tests + +on: + pull_request: + +jobs: + tests: + uses: ./.github/workflows/foundry-tests.yml \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2f501c4..dc9a141 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,6 @@ bindings/ artifacts/ broadcast/ .mud/ -.env.* -!.env.anvil # Ignore MUD deploy artifacts */*/deploys diff --git a/.solhint.json b/.solhint.json index fdf55a8..a55adc7 100644 --- a/.solhint.json +++ b/.solhint.json @@ -7,7 +7,7 @@ "mud" ], "rules": { - "no-unused-import": "error", + "no-unused-import": "warn", "compiler-version": [ "error", ">=0.8.0" diff --git a/anvil-test-state.json.gz b/anvil-test-state.json.gz new file mode 100644 index 0000000..50a4658 Binary files /dev/null and b/anvil-test-state.json.gz differ diff --git a/docker-compose.yaml b/docker-compose.yaml index 6c8a65d..408f24f 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -19,7 +19,7 @@ services: # depends_on: # foundry: # condition: service_healthy - # command: "--rpc-url http://foundry:8545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --world-address 0x8a791620dd6260079bf849dc5567adc3f2fdc318" + # command: "--rpc-url http://foundry:8545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" # tty: true postgres: image: postgres diff --git a/package-lock.json b/package-lock.json index a0b2ab3..1d88e6c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "0.0.0", "license": "CC-BY-NC-4.0", "workspaces": [ - "packages/test-fixtures" + "packages/test-fixtures", + "packages/corporations" ], "dependencies": { "@eveworld/common-constants": "0.0.13", @@ -12426,6 +12427,10 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/evedatacore-corporations": { + "resolved": "packages/corporations", + "link": true + }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -18874,6 +18879,12 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "packages/corporations": { + "name": "evedatacore-corporations", + "version": "1.0.0", + "license": "CC-BY-NC-4.0", + "devDependencies": {} + }, "packages/test-fixtures": { "version": "1.0.0", "license": "CC-BY-NC-4.0", diff --git a/package.json b/package.json index dfe00ae..f0993bf 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,9 @@ "version": "0.0.0", "license": "CC-BY-NC-4.0", "scripts": { - "lint": "solhint --config .solhint.json 'packages/**/*.sol'" + "lint": "solhint --config .solhint.json 'packages/**/*.sol'", + "deploy:local": "npm run deploy:local --workspaces --if-present", + "test": "npm run test --workspaces --if-present" }, "dependencies": { "@eveworld/common-constants": "0.0.13", @@ -24,6 +26,7 @@ "solhint-plugin-mud": "2.2.9" }, "workspaces": [ - "packages/test-fixtures" + "packages/test-fixtures", + "packages/corporations" ] } \ No newline at end of file diff --git a/packages/corporations/foundry.toml b/packages/corporations/foundry.toml new file mode 100644 index 0000000..d973411 --- /dev/null +++ b/packages/corporations/foundry.toml @@ -0,0 +1,38 @@ +[profile.default] +solc = "0.8.24" +evm_version = "cancun" +ffi = false +fuzz_runs = 256 +optimizer = true +optimizer_runs = 3000 +verbosity = 2 +src = "src" +example = "test" +out = "out" +allow_paths = [ + # pnpm symlinks to the project root's node_modules + "../../node_modules", +] +remappings = [ + "ds-test/=../../node_modules/ds-test/src/", + "forge-std/=../../node_modules/forge-std/src/", + "@latticexyz/=../../node_modules/@latticexyz/", + "@eveworld/=../../node_modules/@eveworld/", +] +extra_output_files = [ + "abi", + "evm.bytecode" +] +fs_permissions = [{ access = "read", path = "./"}] + +[profile.local] +# Local Anvil Instance +eth_rpc_url = "http://127.0.0.1:8545" + +[profile.garnet] +# Public Testnet +eth_rpc_url = "https://rpc.garnetchain.com" + +[profile.redstone] +# Public Mainnet +eth_rpc_url = "https://rpc.redstonechain.com" diff --git a/packages/corporations/mud.config.ts b/packages/corporations/mud.config.ts new file mode 100644 index 0000000..2951554 --- /dev/null +++ b/packages/corporations/mud.config.ts @@ -0,0 +1,28 @@ +import { defineWorld } from "@latticexyz/world"; + +const namespace = process.env.CORPORATIONS_NAMESPACE || process.env.DEFAULT_NAMESPACE; + +export default defineWorld({ + namespace, + tables: { + CorporationsTable: { + schema: { + corpId: "uint256", + CEO: "uint256", + ticker: "bytes8", + claimedAt: "uint256", + name: "string", + homepage: "string", + description: "string", + }, + key: ["corpId"], + }, + }, + systems: { + CorporationsSystem: { + deploy: { + registerWorldFunctions: false, + }, + }, + }, +}); diff --git a/packages/corporations/package.json b/packages/corporations/package.json new file mode 100644 index 0000000..d9cc15e --- /dev/null +++ b/packages/corporations/package.json @@ -0,0 +1,17 @@ +{ + "name": "evedatacore-corporations", + "version": "1.0.0", + "description": "EVE Datacore corporations", + "main": "index.js", + "devDependencies": {}, + "scripts": { + "build": "mud build", + "clean": "forge clean && rimraf src/codegen", + "dev:contracts": "export $(cat ../../.env.anvil | grep -v '^#' | xargs) && mud dev-contracts --rpc $RPC_URL --worldAddress ${WORLD_ADDRESS}", + "deploy:local": "export $(cat ../../.env.anvil | grep -v '^#' | xargs) && npm run build && mud deploy --profile=local --worldAddress ${WORLD_ADDRESS}", + "deploy:garnet": "export $(cat ../../.env.garnet | grep -v '^#' | xargs) && npm run build && mud deploy --profile=garnet --worldAddress ${WORLD_ADDRESS}", + "test": "export $(cat ../../.env.anvil | grep -v '^#' | xargs) && forge test --fork-url $RPC_URL" + + }, + "license": "CC-BY-NC-4.0" +} \ No newline at end of file diff --git a/packages/corporations/src/codegen/index.sol b/packages/corporations/src/codegen/index.sol new file mode 100644 index 0000000..26a6c1b --- /dev/null +++ b/packages/corporations/src/codegen/index.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +import { CorporationsTable, CorporationsTableData } from "./tables/CorporationsTable.sol"; diff --git a/packages/corporations/src/codegen/tables/CorporationsTable.sol b/packages/corporations/src/codegen/tables/CorporationsTable.sol new file mode 100644 index 0000000..5cebcfb --- /dev/null +++ b/packages/corporations/src/codegen/tables/CorporationsTable.sol @@ -0,0 +1,935 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +// Import store internals +import { IStore } from "@latticexyz/store/src/IStore.sol"; +import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; +import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; +import { Bytes } from "@latticexyz/store/src/Bytes.sol"; +import { Memory } from "@latticexyz/store/src/Memory.sol"; +import { SliceLib } from "@latticexyz/store/src/Slice.sol"; +import { EncodeArray } from "@latticexyz/store/src/tightcoder/EncodeArray.sol"; +import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol"; +import { Schema } from "@latticexyz/store/src/Schema.sol"; +import { EncodedLengths, EncodedLengthsLib } from "@latticexyz/store/src/EncodedLengths.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; + +struct CorporationsTableData { + uint256 CEO; + bytes8 ticker; + uint256 claimedAt; + string name; + string homepage; + string description; +} + +library CorporationsTable { + // Hex below is the result of `WorldResourceIdLib.encode({ namespace: "beauKode_dev", name: "CorporationsTabl", typeId: RESOURCE_TABLE });` + ResourceId constant _tableId = ResourceId.wrap(0x7462626561754b6f64655f6465760000436f72706f726174696f6e735461626c); + + FieldLayout constant _fieldLayout = + FieldLayout.wrap(0x0048030320082000000000000000000000000000000000000000000000000000); + + // Hex-encoded key schema of (uint256) + Schema constant _keySchema = Schema.wrap(0x002001001f000000000000000000000000000000000000000000000000000000); + // Hex-encoded value schema of (uint256, bytes8, uint256, string, string, string) + Schema constant _valueSchema = Schema.wrap(0x004803031f471fc5c5c500000000000000000000000000000000000000000000); + + /** + * @notice Get the table's key field names. + * @return keyNames An array of strings with the names of key fields. + */ + function getKeyNames() internal pure returns (string[] memory keyNames) { + keyNames = new string[](1); + keyNames[0] = "corpId"; + } + + /** + * @notice Get the table's value field names. + * @return fieldNames An array of strings with the names of value fields. + */ + function getFieldNames() internal pure returns (string[] memory fieldNames) { + fieldNames = new string[](6); + fieldNames[0] = "CEO"; + fieldNames[1] = "ticker"; + fieldNames[2] = "claimedAt"; + fieldNames[3] = "name"; + fieldNames[4] = "homepage"; + fieldNames[5] = "description"; + } + + /** + * @notice Register the table with its config. + */ + function register() internal { + StoreSwitch.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Register the table with its config. + */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Get CEO. + */ + function getCEO(uint256 corpId) internal view returns (uint256 CEO) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** + * @notice Get CEO. + */ + function _getCEO(uint256 corpId) internal view returns (uint256 CEO) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** + * @notice Set CEO. + */ + function setCEO(uint256 corpId, uint256 CEO) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((CEO)), _fieldLayout); + } + + /** + * @notice Set CEO. + */ + function _setCEO(uint256 corpId, uint256 CEO) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((CEO)), _fieldLayout); + } + + /** + * @notice Get ticker. + */ + function getTicker(uint256 corpId) internal view returns (bytes8 ticker) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (bytes8(_blob)); + } + + /** + * @notice Get ticker. + */ + function _getTicker(uint256 corpId) internal view returns (bytes8 ticker) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (bytes8(_blob)); + } + + /** + * @notice Set ticker. + */ + function setTicker(uint256 corpId, bytes8 ticker) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreSwitch.setStaticField(_tableId, _keyTuple, 1, abi.encodePacked((ticker)), _fieldLayout); + } + + /** + * @notice Set ticker. + */ + function _setTicker(uint256 corpId, bytes8 ticker) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreCore.setStaticField(_tableId, _keyTuple, 1, abi.encodePacked((ticker)), _fieldLayout); + } + + /** + * @notice Get claimedAt. + */ + function getClaimedAt(uint256 corpId) internal view returns (uint256 claimedAt) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 2, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** + * @notice Get claimedAt. + */ + function _getClaimedAt(uint256 corpId) internal view returns (uint256 claimedAt) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 2, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** + * @notice Set claimedAt. + */ + function setClaimedAt(uint256 corpId, uint256 claimedAt) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreSwitch.setStaticField(_tableId, _keyTuple, 2, abi.encodePacked((claimedAt)), _fieldLayout); + } + + /** + * @notice Set claimedAt. + */ + function _setClaimedAt(uint256 corpId, uint256 claimedAt) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreCore.setStaticField(_tableId, _keyTuple, 2, abi.encodePacked((claimedAt)), _fieldLayout); + } + + /** + * @notice Get name. + */ + function getName(uint256 corpId) internal view returns (string memory name) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return (string(_blob)); + } + + /** + * @notice Get name. + */ + function _getName(uint256 corpId) internal view returns (string memory name) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); + return (string(_blob)); + } + + /** + * @notice Set name. + */ + function setName(uint256 corpId, string memory name) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreSwitch.setDynamicField(_tableId, _keyTuple, 0, bytes((name))); + } + + /** + * @notice Set name. + */ + function _setName(uint256 corpId, string memory name) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreCore.setDynamicField(_tableId, _keyTuple, 0, bytes((name))); + } + + /** + * @notice Get the length of name. + */ + function lengthName(uint256 corpId) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + uint256 _byteLength = StoreSwitch.getDynamicFieldLength(_tableId, _keyTuple, 0); + unchecked { + return _byteLength / 1; + } + } + + /** + * @notice Get the length of name. + */ + function _lengthName(uint256 corpId) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + uint256 _byteLength = StoreCore.getDynamicFieldLength(_tableId, _keyTuple, 0); + unchecked { + return _byteLength / 1; + } + } + + /** + * @notice Get an item of name. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function getItemName(uint256 corpId, uint256 _index) internal view returns (string memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + unchecked { + bytes memory _blob = StoreSwitch.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 1, (_index + 1) * 1); + return (string(_blob)); + } + } + + /** + * @notice Get an item of name. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function _getItemName(uint256 corpId, uint256 _index) internal view returns (string memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + unchecked { + bytes memory _blob = StoreCore.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 1, (_index + 1) * 1); + return (string(_blob)); + } + } + + /** + * @notice Push a slice to name. + */ + function pushName(uint256 corpId, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreSwitch.pushToDynamicField(_tableId, _keyTuple, 0, bytes((_slice))); + } + + /** + * @notice Push a slice to name. + */ + function _pushName(uint256 corpId, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreCore.pushToDynamicField(_tableId, _keyTuple, 0, bytes((_slice))); + } + + /** + * @notice Pop a slice from name. + */ + function popName(uint256 corpId) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreSwitch.popFromDynamicField(_tableId, _keyTuple, 0, 1); + } + + /** + * @notice Pop a slice from name. + */ + function _popName(uint256 corpId) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreCore.popFromDynamicField(_tableId, _keyTuple, 0, 1); + } + + /** + * @notice Update a slice of name at `_index`. + */ + function updateName(uint256 corpId, uint256 _index, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + unchecked { + bytes memory _encoded = bytes((_slice)); + StoreSwitch.spliceDynamicData(_tableId, _keyTuple, 0, uint40(_index * 1), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Update a slice of name at `_index`. + */ + function _updateName(uint256 corpId, uint256 _index, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + unchecked { + bytes memory _encoded = bytes((_slice)); + StoreCore.spliceDynamicData(_tableId, _keyTuple, 0, uint40(_index * 1), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Get homepage. + */ + function getHomepage(uint256 corpId) internal view returns (string memory homepage) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 1); + return (string(_blob)); + } + + /** + * @notice Get homepage. + */ + function _getHomepage(uint256 corpId) internal view returns (string memory homepage) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 1); + return (string(_blob)); + } + + /** + * @notice Set homepage. + */ + function setHomepage(uint256 corpId, string memory homepage) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreSwitch.setDynamicField(_tableId, _keyTuple, 1, bytes((homepage))); + } + + /** + * @notice Set homepage. + */ + function _setHomepage(uint256 corpId, string memory homepage) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreCore.setDynamicField(_tableId, _keyTuple, 1, bytes((homepage))); + } + + /** + * @notice Get the length of homepage. + */ + function lengthHomepage(uint256 corpId) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + uint256 _byteLength = StoreSwitch.getDynamicFieldLength(_tableId, _keyTuple, 1); + unchecked { + return _byteLength / 1; + } + } + + /** + * @notice Get the length of homepage. + */ + function _lengthHomepage(uint256 corpId) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + uint256 _byteLength = StoreCore.getDynamicFieldLength(_tableId, _keyTuple, 1); + unchecked { + return _byteLength / 1; + } + } + + /** + * @notice Get an item of homepage. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function getItemHomepage(uint256 corpId, uint256 _index) internal view returns (string memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + unchecked { + bytes memory _blob = StoreSwitch.getDynamicFieldSlice(_tableId, _keyTuple, 1, _index * 1, (_index + 1) * 1); + return (string(_blob)); + } + } + + /** + * @notice Get an item of homepage. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function _getItemHomepage(uint256 corpId, uint256 _index) internal view returns (string memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + unchecked { + bytes memory _blob = StoreCore.getDynamicFieldSlice(_tableId, _keyTuple, 1, _index * 1, (_index + 1) * 1); + return (string(_blob)); + } + } + + /** + * @notice Push a slice to homepage. + */ + function pushHomepage(uint256 corpId, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreSwitch.pushToDynamicField(_tableId, _keyTuple, 1, bytes((_slice))); + } + + /** + * @notice Push a slice to homepage. + */ + function _pushHomepage(uint256 corpId, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreCore.pushToDynamicField(_tableId, _keyTuple, 1, bytes((_slice))); + } + + /** + * @notice Pop a slice from homepage. + */ + function popHomepage(uint256 corpId) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreSwitch.popFromDynamicField(_tableId, _keyTuple, 1, 1); + } + + /** + * @notice Pop a slice from homepage. + */ + function _popHomepage(uint256 corpId) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreCore.popFromDynamicField(_tableId, _keyTuple, 1, 1); + } + + /** + * @notice Update a slice of homepage at `_index`. + */ + function updateHomepage(uint256 corpId, uint256 _index, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + unchecked { + bytes memory _encoded = bytes((_slice)); + StoreSwitch.spliceDynamicData(_tableId, _keyTuple, 1, uint40(_index * 1), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Update a slice of homepage at `_index`. + */ + function _updateHomepage(uint256 corpId, uint256 _index, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + unchecked { + bytes memory _encoded = bytes((_slice)); + StoreCore.spliceDynamicData(_tableId, _keyTuple, 1, uint40(_index * 1), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Get description. + */ + function getDescription(uint256 corpId) internal view returns (string memory description) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 2); + return (string(_blob)); + } + + /** + * @notice Get description. + */ + function _getDescription(uint256 corpId) internal view returns (string memory description) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 2); + return (string(_blob)); + } + + /** + * @notice Set description. + */ + function setDescription(uint256 corpId, string memory description) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreSwitch.setDynamicField(_tableId, _keyTuple, 2, bytes((description))); + } + + /** + * @notice Set description. + */ + function _setDescription(uint256 corpId, string memory description) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreCore.setDynamicField(_tableId, _keyTuple, 2, bytes((description))); + } + + /** + * @notice Get the length of description. + */ + function lengthDescription(uint256 corpId) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + uint256 _byteLength = StoreSwitch.getDynamicFieldLength(_tableId, _keyTuple, 2); + unchecked { + return _byteLength / 1; + } + } + + /** + * @notice Get the length of description. + */ + function _lengthDescription(uint256 corpId) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + uint256 _byteLength = StoreCore.getDynamicFieldLength(_tableId, _keyTuple, 2); + unchecked { + return _byteLength / 1; + } + } + + /** + * @notice Get an item of description. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function getItemDescription(uint256 corpId, uint256 _index) internal view returns (string memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + unchecked { + bytes memory _blob = StoreSwitch.getDynamicFieldSlice(_tableId, _keyTuple, 2, _index * 1, (_index + 1) * 1); + return (string(_blob)); + } + } + + /** + * @notice Get an item of description. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function _getItemDescription(uint256 corpId, uint256 _index) internal view returns (string memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + unchecked { + bytes memory _blob = StoreCore.getDynamicFieldSlice(_tableId, _keyTuple, 2, _index * 1, (_index + 1) * 1); + return (string(_blob)); + } + } + + /** + * @notice Push a slice to description. + */ + function pushDescription(uint256 corpId, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreSwitch.pushToDynamicField(_tableId, _keyTuple, 2, bytes((_slice))); + } + + /** + * @notice Push a slice to description. + */ + function _pushDescription(uint256 corpId, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreCore.pushToDynamicField(_tableId, _keyTuple, 2, bytes((_slice))); + } + + /** + * @notice Pop a slice from description. + */ + function popDescription(uint256 corpId) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreSwitch.popFromDynamicField(_tableId, _keyTuple, 2, 1); + } + + /** + * @notice Pop a slice from description. + */ + function _popDescription(uint256 corpId) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreCore.popFromDynamicField(_tableId, _keyTuple, 2, 1); + } + + /** + * @notice Update a slice of description at `_index`. + */ + function updateDescription(uint256 corpId, uint256 _index, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + unchecked { + bytes memory _encoded = bytes((_slice)); + StoreSwitch.spliceDynamicData(_tableId, _keyTuple, 2, uint40(_index * 1), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Update a slice of description at `_index`. + */ + function _updateDescription(uint256 corpId, uint256 _index, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + unchecked { + bytes memory _encoded = bytes((_slice)); + StoreCore.spliceDynamicData(_tableId, _keyTuple, 2, uint40(_index * 1), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Get the full data. + */ + function get(uint256 corpId) internal view returns (CorporationsTableData memory _table) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + (bytes memory _staticData, EncodedLengths _encodedLengths, bytes memory _dynamicData) = StoreSwitch.getRecord( + _tableId, + _keyTuple, + _fieldLayout + ); + return decode(_staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Get the full data. + */ + function _get(uint256 corpId) internal view returns (CorporationsTableData memory _table) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + (bytes memory _staticData, EncodedLengths _encodedLengths, bytes memory _dynamicData) = StoreCore.getRecord( + _tableId, + _keyTuple, + _fieldLayout + ); + return decode(_staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Set the full data using individual values. + */ + function set( + uint256 corpId, + uint256 CEO, + bytes8 ticker, + uint256 claimedAt, + string memory name, + string memory homepage, + string memory description + ) internal { + bytes memory _staticData = encodeStatic(CEO, ticker, claimedAt); + + EncodedLengths _encodedLengths = encodeLengths(name, homepage, description); + bytes memory _dynamicData = encodeDynamic(name, homepage, description); + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Set the full data using individual values. + */ + function _set( + uint256 corpId, + uint256 CEO, + bytes8 ticker, + uint256 claimedAt, + string memory name, + string memory homepage, + string memory description + ) internal { + bytes memory _staticData = encodeStatic(CEO, ticker, claimedAt); + + EncodedLengths _encodedLengths = encodeLengths(name, homepage, description); + bytes memory _dynamicData = encodeDynamic(name, homepage, description); + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** + * @notice Set the full data using the data struct. + */ + function set(uint256 corpId, CorporationsTableData memory _table) internal { + bytes memory _staticData = encodeStatic(_table.CEO, _table.ticker, _table.claimedAt); + + EncodedLengths _encodedLengths = encodeLengths(_table.name, _table.homepage, _table.description); + bytes memory _dynamicData = encodeDynamic(_table.name, _table.homepage, _table.description); + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Set the full data using the data struct. + */ + function _set(uint256 corpId, CorporationsTableData memory _table) internal { + bytes memory _staticData = encodeStatic(_table.CEO, _table.ticker, _table.claimedAt); + + EncodedLengths _encodedLengths = encodeLengths(_table.name, _table.homepage, _table.description); + bytes memory _dynamicData = encodeDynamic(_table.name, _table.homepage, _table.description); + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** + * @notice Decode the tightly packed blob of static data using this table's field layout. + */ + function decodeStatic(bytes memory _blob) internal pure returns (uint256 CEO, bytes8 ticker, uint256 claimedAt) { + CEO = (uint256(Bytes.getBytes32(_blob, 0))); + + ticker = (Bytes.getBytes8(_blob, 32)); + + claimedAt = (uint256(Bytes.getBytes32(_blob, 40))); + } + + /** + * @notice Decode the tightly packed blob of dynamic data using the encoded lengths. + */ + function decodeDynamic( + EncodedLengths _encodedLengths, + bytes memory _blob + ) internal pure returns (string memory name, string memory homepage, string memory description) { + uint256 _start; + uint256 _end; + unchecked { + _end = _encodedLengths.atIndex(0); + } + name = (string(SliceLib.getSubslice(_blob, _start, _end).toBytes())); + + _start = _end; + unchecked { + _end += _encodedLengths.atIndex(1); + } + homepage = (string(SliceLib.getSubslice(_blob, _start, _end).toBytes())); + + _start = _end; + unchecked { + _end += _encodedLengths.atIndex(2); + } + description = (string(SliceLib.getSubslice(_blob, _start, _end).toBytes())); + } + + /** + * @notice Decode the tightly packed blobs using this table's field layout. + * @param _staticData Tightly packed static fields. + * @param _encodedLengths Encoded lengths of dynamic fields. + * @param _dynamicData Tightly packed dynamic fields. + */ + function decode( + bytes memory _staticData, + EncodedLengths _encodedLengths, + bytes memory _dynamicData + ) internal pure returns (CorporationsTableData memory _table) { + (_table.CEO, _table.ticker, _table.claimedAt) = decodeStatic(_staticData); + + (_table.name, _table.homepage, _table.description) = decodeDynamic(_encodedLengths, _dynamicData); + } + + /** + * @notice Delete all data for given keys. + */ + function deleteRecord(uint256 corpId) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreSwitch.deleteRecord(_tableId, _keyTuple); + } + + /** + * @notice Delete all data for given keys. + */ + function _deleteRecord(uint256 corpId) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /** + * @notice Tightly pack static (fixed length) data using this table's schema. + * @return The static data, encoded into a sequence of bytes. + */ + function encodeStatic(uint256 CEO, bytes8 ticker, uint256 claimedAt) internal pure returns (bytes memory) { + return abi.encodePacked(CEO, ticker, claimedAt); + } + + /** + * @notice Tightly pack dynamic data lengths using this table's schema. + * @return _encodedLengths The lengths of the dynamic fields (packed into a single bytes32 value). + */ + function encodeLengths( + string memory name, + string memory homepage, + string memory description + ) internal pure returns (EncodedLengths _encodedLengths) { + // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits + unchecked { + _encodedLengths = EncodedLengthsLib.pack(bytes(name).length, bytes(homepage).length, bytes(description).length); + } + } + + /** + * @notice Tightly pack dynamic (variable length) data using this table's schema. + * @return The dynamic data, encoded into a sequence of bytes. + */ + function encodeDynamic( + string memory name, + string memory homepage, + string memory description + ) internal pure returns (bytes memory) { + return abi.encodePacked(bytes((name)), bytes((homepage)), bytes((description))); + } + + /** + * @notice Encode all of a record's fields. + * @return The static (fixed length) data, encoded into a sequence of bytes. + * @return The lengths of the dynamic fields (packed into a single bytes32 value). + * @return The dynamic (variable length) data, encoded into a sequence of bytes. + */ + function encode( + uint256 CEO, + bytes8 ticker, + uint256 claimedAt, + string memory name, + string memory homepage, + string memory description + ) internal pure returns (bytes memory, EncodedLengths, bytes memory) { + bytes memory _staticData = encodeStatic(CEO, ticker, claimedAt); + + EncodedLengths _encodedLengths = encodeLengths(name, homepage, description); + bytes memory _dynamicData = encodeDynamic(name, homepage, description); + + return (_staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Encode keys as a bytes32 array using this table's field layout. + */ + function encodeKeyTuple(uint256 corpId) internal pure returns (bytes32[] memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(corpId)); + + return _keyTuple; + } +} diff --git a/packages/corporations/src/codegen/world/IWorld.sol b/packages/corporations/src/codegen/world/IWorld.sol new file mode 100644 index 0000000..4761e84 --- /dev/null +++ b/packages/corporations/src/codegen/world/IWorld.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; + +/** + * @title IWorld + * @author MUD (https://mud.dev) by Lattice (https://lattice.xyz) + * @notice This interface integrates all systems and associated function selectors + * that are dynamically registered in the World during deployment. + * @dev This is an autogenerated file; do not edit manually. + */ +interface IWorld is IBaseWorld {} diff --git a/packages/corporations/src/systems/CorporationsSystem.sol b/packages/corporations/src/systems/CorporationsSystem.sol new file mode 100644 index 0000000..33e5828 --- /dev/null +++ b/packages/corporations/src/systems/CorporationsSystem.sol @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: CC-BY-NC-4.0 +pragma solidity >=0.8.24; + +import "forge-std/Test.sol"; +import { System } from "@latticexyz/world/src/System.sol"; + +import { CharactersTable } from "@eveworld/world/src/codegen/tables/CharactersTable.sol"; +import { CharactersByAddressTable } from "@eveworld/world/src/codegen/tables/CharactersByAddressTable.sol"; +import { CorporationsTable } from "../codegen/tables/CorporationsTable.sol"; +import { CorporationsSystemErrors } from "./CorporationsSystemErrors.sol"; + +contract CorporationsSystem is System { + modifier onlyCEO(uint256 corpId) { + uint256 callerId = CharactersByAddressTable.getCharacterId(_msgSender()); + uint256 ceoId = CorporationsTable.getCEO(corpId); + if (callerId != ceoId) { + revert CorporationsSystemErrors.CorporationsSystem_Unauthorized(corpId, callerId); + } + _; + } + + function claim(uint256 corpId, bytes8 ticker, string calldata name) public { + uint256 characterId = CharactersByAddressTable.getCharacterId(_msgSender()); + + // Check if the character is member of the corp + if (CharactersTable.getCorpId(characterId) != corpId) { + revert CorporationsSystemErrors.CorporationsSystem_NotMemberOfCorp(corpId, characterId); + } + + // Check if the corp is already claimed + if (isClaimValid(corpId)) { + revert CorporationsSystemErrors.CorporationsSystem_CorpAlreadyClaimed(corpId); + } + + _assertTickerFormat(ticker); + _assertStringLength(name, 1, 50); + CorporationsTable.set(corpId, characterId, ticker, block.timestamp, name, "", ""); + } + + function transfer(uint256 corpId, uint256 toCeoId) public onlyCEO(corpId) { + // Check if the new CEO is member of the corp + if (CharactersTable.getCorpId(toCeoId) != corpId) { + revert CorporationsSystemErrors.CorporationsSystem_NotMemberOfCorp(corpId, toCeoId); + } + + uint256 currentCeoId = CorporationsTable.getCEO(corpId); + // Check if the new CEO is the current CEO + if (currentCeoId == toCeoId) { + revert CorporationsSystemErrors.CorporationsSystem_IsAlreadyCeo(corpId, currentCeoId); + } + + CorporationsTable.setCEO(corpId, toCeoId); + } + + function isClaimValid(uint256 corpId) public view returns (bool) { + uint256 ceoId = CorporationsTable.getCEO(corpId); + + // Corp not claimed + if (ceoId == 0) { + return false; + } + + // Corp claimed, but the CEO is not member of the corp anymore + if (CharactersTable.getCorpId(ceoId) != corpId) { + return false; + } + + // Corp claimed, and the CEO is still member of the corp + return true; + } + + function setMetadata( + uint256 corpId, + bytes8 ticker, + string calldata name, + string calldata description, + string calldata homepage + ) public onlyCEO(corpId) { + _assertTickerFormat(ticker); + _assertStringLength(name, 1, 50); + _assertStringLength(description, 0, 4000); + _assertStringLength(homepage, 0, 255); + + CorporationsTable.setTicker(corpId, ticker); + CorporationsTable.setName(corpId, name); + CorporationsTable.setDescription(corpId, description); + CorporationsTable.setHomepage(corpId, homepage); + } + + function _assertStringLength(string calldata str, uint16 minLength, uint16 maxLength) internal pure { + uint256 length = bytes(str).length; + if (length < minLength || length > maxLength) { + revert CorporationsSystemErrors.CorporationsSystem_InvalidStringLength(str, minLength, maxLength); + } + } + + function _assertTickerFormat(bytes8 ticker) internal pure { + uint256 length = 0; + + // Count actual length (until first zero byte) + for (uint256 i = 0; i < 8; i++) { + if (ticker[i] == 0) { + break; + } + length++; + } + + // Check length + if (length < 1 || length > 5) { + revert CorporationsSystemErrors.CorporationsSystem_InvalidTickerFormat(ticker); + } + + // Check each character + for (uint256 i = 0; i < length; i++) { + bytes1 char = ticker[i]; + bool isCapitalLetter = (char >= 0x41 && char <= 0x5A); // A-Z + bool isDigit = (char >= 0x30 && char <= 0x39); // 0-9 + + if (!isCapitalLetter && !isDigit) { + revert CorporationsSystemErrors.CorporationsSystem_InvalidTickerFormat(ticker); + } + } + } +} diff --git a/packages/corporations/src/systems/CorporationsSystemErrors.sol b/packages/corporations/src/systems/CorporationsSystemErrors.sol new file mode 100644 index 0000000..78ecd06 --- /dev/null +++ b/packages/corporations/src/systems/CorporationsSystemErrors.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: CC-BY-NC-4.0 +pragma solidity >=0.8.24; + +interface CorporationsSystemErrors { + error CorporationsSystem_NotMemberOfCorp(uint256 corpId, uint256 ceoId); + error CorporationsSystem_NotCEOOfCorp(); + error CorporationsSystem_CorpAlreadyClaimed(uint256 corpId); + error CorporationsSystem_CorpNotClaimed(); + error CorporationsSystem_IsAlreadyCeo(uint256 corpId, uint256 ceoId); + error CorporationsSystem_Unauthorized(uint256 corpId, uint256 callerId); + error CorporationsSystem_InvalidStringLength(string value, uint16 minLength, uint16 maxLength); + error CorporationsSystem_InvalidTickerFormat(bytes8 ticker); +} diff --git a/packages/corporations/src/systems/Utils.sol b/packages/corporations/src/systems/Utils.sol new file mode 100644 index 0000000..3fdca2e --- /dev/null +++ b/packages/corporations/src/systems/Utils.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: CC-BY-NC-4.0 +pragma solidity >=0.8.24; + +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; +import { WorldResourceIdLib } from "@latticexyz/world/src/WorldResourceId.sol"; +import { RESOURCE_SYSTEM } from "@latticexyz/world/src/worldResourceTypes.sol"; + +import { CORPORATIONS_SYSTEM_NAME } from "./constants.sol"; + +library Utils { + function corporationsSystemId(bytes14 namespace) internal pure returns (ResourceId) { + return WorldResourceIdLib.encode({ typeId: RESOURCE_SYSTEM, namespace: namespace, name: CORPORATIONS_SYSTEM_NAME }); + } +} diff --git a/packages/corporations/src/systems/constants.sol b/packages/corporations/src/systems/constants.sol new file mode 100644 index 0000000..60d424b --- /dev/null +++ b/packages/corporations/src/systems/constants.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: CC-BY-NC-4.0 +pragma solidity >=0.8.24; + +bytes16 constant CORPORATIONS_SYSTEM_NAME = "CorporationsSyst"; diff --git a/packages/corporations/test/CorporationsTest.t.sol b/packages/corporations/test/CorporationsTest.t.sol new file mode 100644 index 0000000..53f1c37 --- /dev/null +++ b/packages/corporations/test/CorporationsTest.t.sol @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: CC-BY-NC-4.0 +pragma solidity >=0.8.24; + +import "forge-std/Test.sol"; +import { MudTest } from "@latticexyz/world/test/MudTest.t.sol"; +import { ResourceId } from "@latticexyz/world/src/WorldResourceId.sol"; + +import { FRONTIER_WORLD_DEPLOYMENT_NAMESPACE } from "@eveworld/common-constants/src/constants.sol"; +import { IBaseWorld } from "@eveworld/world/src/codegen/world/IWorld.sol"; +import { CharactersByAddressTable } from "@eveworld/world/src/codegen/tables/CharactersByAddressTable.sol"; +import { EntityRecordOffchainTableData } from "@eveworld/world/src/codegen/tables/EntityRecordOffchainTable.sol"; +import { EntityRecordData } from "@eveworld/world/src/modules/smart-character/types.sol"; +import { SmartCharacterLib } from "@eveworld/world/src/modules/smart-character/SmartCharacterLib.sol"; + +import { IWorld } from "../src/codegen/world/IWorld.sol"; +import { CorporationsTable } from "../src/codegen/tables/CorporationsTable.sol"; +import { CorporationsSystem } from "../src/systems/CorporationsSystem.sol"; +import { CorporationsSystemErrors } from "../src/systems/CorporationsSystemErrors.sol"; +import { Utils } from "../src/systems/Utils.sol"; + +contract CorporationsTest is MudTest { + using SmartCharacterLib for SmartCharacterLib.World; + + IWorld private world; + SmartCharacterLib.World private smartCharacter; + + uint256 private corp1 = 70000001; + uint256 private corp2 = 70000002; + uint256 private corp3 = 70000003; + uint256 private corp4 = 70000004; + + address private admin; + address private player1; + address private player2; + address private player3; + address private player4; + + ResourceId private systemId; + + //Setup for the tests + function setUp() public override { + super.setUp(); + world = IWorld(worldAddress); + + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + admin = vm.addr(deployerPrivateKey); + player1 = vm.addr(vm.envUint("PLAYER1_PRIVATE_KEY")); + player2 = vm.addr(vm.envUint("PLAYER2_PRIVATE_KEY")); + player3 = vm.addr(vm.envUint("PLAYER3_PRIVATE_KEY")); + player4 = vm.addr(vm.envUint("PLAYER4_PRIVATE_KEY")); + + // Convert string to bytes14 using abi.encodePacked + bytes14 namespace = bytes14(abi.encodePacked(vm.envOr("CORPORATIONS_NAMESPACE", vm.envString("DEFAULT_NAMESPACE")))); + systemId = Utils.corporationsSystemId(namespace); + + smartCharacter = SmartCharacterLib.World({ + iface: IBaseWorld(worldAddress), + namespace: FRONTIER_WORLD_DEPLOYMENT_NAMESPACE + }); + + if (CharactersByAddressTable.get(admin) == 0) { + smartCharacter.createCharacter( + 42, + admin, + corp1, + EntityRecordData({ typeId: 123, itemId: 0, volume: 0 }), + EntityRecordOffchainTableData({ + name: "beauKode", + dappURL: "https://evedataco.re", + description: "EVE Datacore website" + }), + "" + ); + } + if (CharactersByAddressTable.get(player1) == 0) { + smartCharacter.createCharacter( + 71, + player1, + corp1, + EntityRecordData({ typeId: 123, itemId: 0, volume: 0 }), + EntityRecordOffchainTableData({ name: "player1", dappURL: "", description: "" }), + "" + ); + } + if (CharactersByAddressTable.get(player2) == 0) { + smartCharacter.createCharacter( + 72, + player2, + corp2, + EntityRecordData({ typeId: 123, itemId: 0, volume: 0 }), + EntityRecordOffchainTableData({ name: "player2", dappURL: "", description: "" }), + "" + ); + } + if (CharactersByAddressTable.get(player3) == 0) { + smartCharacter.createCharacter( + 73, + player3, + corp3, + EntityRecordData({ typeId: 123, itemId: 0, volume: 0 }), + EntityRecordOffchainTableData({ name: "player3", dappURL: "", description: "" }), + "" + ); + } + if (CharactersByAddressTable.get(player4) == 0) { + smartCharacter.createCharacter( + 74, + player4, + corp4, + EntityRecordData({ typeId: 123, itemId: 0, volume: 0 }), + EntityRecordOffchainTableData({ name: "player4", dappURL: "", description: "" }), + "" + ); + } + + vm.startBroadcast(deployerPrivateKey); + // A corp claimed by the admin + CorporationsTable.set(corp1, 42, "CORP1", block.timestamp, "Corp1 Name", "", ""); + // A corp claimed by a player + CorporationsTable.set(corp2, 72, "CORP2", block.timestamp, "Corp2 Name", "", ""); + // A corp where the CEO is not a member of the corp anymore + CorporationsTable.set(corp3, 71, "CORP3", block.timestamp, "Corp3 Name", "", ""); + // Leave the corp4 unclaimed + vm.stopBroadcast(); + } + + // Test if the world exists + function testWorldExists() public { + uint256 codeSize; + address addr = worldAddress; + assembly { + codeSize := extcodesize(addr) + } + assertTrue(codeSize > 0); + } + + // Test if the admin character exists + function testAdminExists() public { + assertTrue(CharactersByAddressTable.get(admin) != 0); + } + + // Test if the corps are claimed + function testCorpsClaimedDbStatus() public { + assertTrue(CorporationsTable.getCEO(corp1) == 42); + assertTrue(CorporationsTable.getCEO(corp2) == 72); + assertTrue(CorporationsTable.getCEO(corp3) == 71); + assertTrue(CorporationsTable.getCEO(corp4) == 0); + } + + function testRevertClaimNotMemberOfCorp() public { + vm.prank(admin); + vm.expectRevert( + abi.encodeWithSelector(CorporationsSystemErrors.CorporationsSystem_NotMemberOfCorp.selector, corp2, 42) + ); + world.call(systemId, abi.encodeCall(CorporationsSystem.claim, (corp2, "TEST", "Test Corp"))); + } + + function testRevertClaimCorpAlreadyClaimed() public { + vm.prank(admin); + vm.expectRevert( + abi.encodeWithSelector(CorporationsSystemErrors.CorporationsSystem_CorpAlreadyClaimed.selector, corp1) + ); + world.call(systemId, abi.encodeCall(CorporationsSystem.claim, (corp1, "TEST", "Test Corp"))); + } + + function testClaimUnclaimedCorp() public { + vm.prank(player4); + world.call(systemId, abi.encodeCall(CorporationsSystem.claim, (corp4, "CORP4", "Corp4 Name"))); + + assertTrue(CorporationsTable.getCEO(corp4) == 74); + assertTrue(CorporationsTable.getTicker(corp4) == "CORP4"); + assertTrue( + keccak256(abi.encodePacked(CorporationsTable.getName(corp4))) == keccak256(abi.encodePacked("Corp4 Name")) + ); + assertTrue(CorporationsTable.getClaimedAt(corp4) != 0); + assertTrue(keccak256(abi.encodePacked(CorporationsTable.getDescription(corp4))) == keccak256(abi.encodePacked(""))); + assertTrue(keccak256(abi.encodePacked(CorporationsTable.getHomepage(corp4))) == keccak256(abi.encodePacked(""))); + } + + function testClaimCeoAsLeftCorp() public { + vm.prank(player3); + world.call(systemId, abi.encodeCall(CorporationsSystem.claim, (corp3, "CORP3", "Corp3 Name"))); + + assertTrue(CorporationsTable.getCEO(corp3) == 73); + assertTrue(CorporationsTable.getTicker(corp3) == "CORP3"); + assertTrue( + keccak256(abi.encodePacked(CorporationsTable.getName(corp3))) == keccak256(abi.encodePacked("Corp3 Name")) + ); + assertTrue(CorporationsTable.getClaimedAt(corp3) != 0); + assertTrue(keccak256(abi.encodePacked(CorporationsTable.getDescription(corp3))) == keccak256(abi.encodePacked(""))); + assertTrue(keccak256(abi.encodePacked(CorporationsTable.getHomepage(corp3))) == keccak256(abi.encodePacked(""))); + } + + function testRevertTransferNotMemberOfCorp() public { + vm.prank(admin); + vm.expectRevert( + abi.encodeWithSelector(CorporationsSystemErrors.CorporationsSystem_NotMemberOfCorp.selector, corp1, 74) + ); + world.call(systemId, abi.encodeCall(CorporationsSystem.transfer, (corp1, 74))); + } + + function testRevertTransferNotCEO() public { + vm.prank(player1); + vm.expectRevert( + abi.encodeWithSelector(CorporationsSystemErrors.CorporationsSystem_Unauthorized.selector, corp1, 71) + ); + world.call(systemId, abi.encodeCall(CorporationsSystem.transfer, (corp1, 42))); + } + + function testRevertTransferIsAlreadyCeo() public { + vm.prank(admin); + vm.expectRevert( + abi.encodeWithSelector(CorporationsSystemErrors.CorporationsSystem_IsAlreadyCeo.selector, corp1, 42) + ); + world.call(systemId, abi.encodeCall(CorporationsSystem.transfer, (corp1, 42))); + } + + function testTransfer() public { + assertTrue(CorporationsTable.getCEO(corp1) == 42); + + vm.prank(admin); + world.call(systemId, abi.encodeCall(CorporationsSystem.transfer, (corp1, 71))); + + assertTrue(CorporationsTable.getCEO(corp1) == 71); + } + + function testRevertClaimEmptyName() public { + vm.prank(player4); + vm.expectRevert( + abi.encodeWithSelector(CorporationsSystemErrors.CorporationsSystem_InvalidStringLength.selector, "", 1, 50) + ); + world.call(systemId, abi.encodeCall(CorporationsSystem.claim, (corp4, "CORP4", ""))); + } + + function testRevertClaimTooLongName() public { + vm.prank(player4); + string memory longName = "This corporation name is way too long and should not be accepted"; + vm.expectRevert( + abi.encodeWithSelector(CorporationsSystemErrors.CorporationsSystem_InvalidStringLength.selector, longName, 1, 50) + ); + world.call(systemId, abi.encodeCall(CorporationsSystem.claim, (corp4, "CORP4", longName))); + } + + function testRevertClaimInvalidTickerFormat() public { + vm.startBroadcast(player4); + + vm.expectRevert( + abi.encodeWithSelector(CorporationsSystemErrors.CorporationsSystem_InvalidTickerFormat.selector, bytes8(" ")) + ); + world.call(systemId, abi.encodeCall(CorporationsSystem.claim, (corp4, " ", "Corp4 Name"))); + + vm.expectRevert( + abi.encodeWithSelector( + CorporationsSystemErrors.CorporationsSystem_InvalidTickerFormat.selector, + bytes8(unicode"è") + ) + ); + world.call(systemId, abi.encodeCall(CorporationsSystem.claim, (corp4, unicode"è", "Corp4 Name"))); + + vm.expectRevert( + abi.encodeWithSelector(CorporationsSystemErrors.CorporationsSystem_InvalidTickerFormat.selector, bytes8("")) + ); + world.call(systemId, abi.encodeCall(CorporationsSystem.claim, (corp4, "", "Corp4 Name"))); + + vm.expectRevert( + abi.encodeWithSelector(CorporationsSystemErrors.CorporationsSystem_InvalidTickerFormat.selector, bytes8("ABCDEF")) + ); + world.call(systemId, abi.encodeCall(CorporationsSystem.claim, (corp4, "ABCDEF", "Corp4 Name"))); + + vm.stopBroadcast(); + } + + function testSetMetadata() public { + vm.prank(admin); + world.call( + systemId, + abi.encodeCall( + CorporationsSystem.setMetadata, + (corp1, "NEW", "New Corp Name", "New description", "https://newcorp.com") + ) + ); + + assertTrue(CorporationsTable.getTicker(corp1) == "NEW"); + assertTrue( + keccak256(abi.encodePacked(CorporationsTable.getName(corp1))) == keccak256(abi.encodePacked("New Corp Name")) + ); + assertTrue( + keccak256(abi.encodePacked(CorporationsTable.getDescription(corp1))) == + keccak256(abi.encodePacked("New description")) + ); + assertTrue( + keccak256(abi.encodePacked(CorporationsTable.getHomepage(corp1))) == + keccak256(abi.encodePacked("https://newcorp.com")) + ); + } + + function testRevertSetMetadataNotCEO() public { + vm.prank(player1); + vm.expectRevert( + abi.encodeWithSelector(CorporationsSystemErrors.CorporationsSystem_Unauthorized.selector, corp1, 71) + ); + world.call( + systemId, + abi.encodeCall( + CorporationsSystem.setMetadata, + (corp1, "NEW", "New Corp Name", "New description", "https://newcorp.com") + ) + ); + } + + function testRevertSetMetadataInvalidTickerFormat() public { + vm.startBroadcast(admin); + + vm.expectRevert( + abi.encodeWithSelector(CorporationsSystemErrors.CorporationsSystem_InvalidTickerFormat.selector, bytes8(" ")) + ); + world.call( + systemId, + abi.encodeCall( + CorporationsSystem.setMetadata, + (corp1, " ", "New Corp Name", "New description", "https://newcorp.com") + ) + ); + + vm.expectRevert( + abi.encodeWithSelector( + CorporationsSystemErrors.CorporationsSystem_InvalidTickerFormat.selector, + bytes8(unicode"è") + ) + ); + world.call( + systemId, + abi.encodeCall( + CorporationsSystem.setMetadata, + (corp1, unicode"è", "New Corp Name", "New description", "https://newcorp.com") + ) + ); + + vm.expectRevert( + abi.encodeWithSelector(CorporationsSystemErrors.CorporationsSystem_InvalidTickerFormat.selector, bytes8("")) + ); + world.call( + systemId, + abi.encodeCall( + CorporationsSystem.setMetadata, + (corp1, "", "New Corp Name", "New description", "https://newcorp.com") + ) + ); + + vm.expectRevert( + abi.encodeWithSelector(CorporationsSystemErrors.CorporationsSystem_InvalidTickerFormat.selector, bytes8("ABCDEF")) + ); + world.call( + systemId, + abi.encodeCall( + CorporationsSystem.setMetadata, + (corp1, "ABCDEF", "New Corp Name", "New description", "https://newcorp.com") + ) + ); + + vm.stopBroadcast(); + } + + function testRevertSetMetadataInvalidStringLength() public { + vm.startBroadcast(admin); + + string memory longName = "This corporation name is way too long and should not be accepted"; + vm.expectRevert( + abi.encodeWithSelector(CorporationsSystemErrors.CorporationsSystem_InvalidStringLength.selector, longName, 1, 50) + ); + world.call( + systemId, + abi.encodeCall(CorporationsSystem.setMetadata, (corp1, "NEW", longName, "New description", "https://newcorp.com")) + ); + + vm.expectRevert( + abi.encodeWithSelector(CorporationsSystemErrors.CorporationsSystem_InvalidStringLength.selector, "", 1, 50) + ); + world.call( + systemId, + abi.encodeCall(CorporationsSystem.setMetadata, (corp1, "NEW", "", "New description", "https://newcorp.com")) + ); + + string + memory longDescription = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In vitae augue laoreet, ultrices ante non, ultrices metus. Nullam lobortis, sem imperdiet tempor faucibus, mauris nisl cursus justo, quis eleifend neque nulla eu urna. Praesent tincidunt, orci dolor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In vitae augue laoreet, ultrices ante non, ultrices metus. Nullam lobortis, sem imperdiet tempor faucibus, mauris nisl cursus justo, quis eleifend neque nulla eu urna. Praesent tinciduna" + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In vitae augue laoreet, ultrices ante non, ultrices metus. Nullam lobortis, sem imperdiet tempor faucibus, mauris nisl cursus justo, quis eleifend neque nulla eu urna. Praesent tincidunt, orci dolor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In vitae augue laoreet, ultrices ante non, ultrices metus. Nullam lobortis, sem imperdiet tempor faucibus, mauris nisl cursus justo, quis eleifend neque nulla eu urna. Praesent tinciduna" + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In vitae augue laoreet, ultrices ante non, ultrices metus. Nullam lobortis, sem imperdiet tempor faucibus, mauris nisl cursus justo, quis eleifend neque nulla eu urna. Praesent tincidunt, orci dolor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In vitae augue laoreet, ultrices ante non, ultrices metus. Nullam lobortis, sem imperdiet tempor faucibus, mauris nisl cursus justo, quis eleifend neque nulla eu urna. Praesent tinciduna" + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In vitae augue laoreet, ultrices ante non, ultrices metus. Nullam lobortis, sem imperdiet tempor faucibus, mauris nisl cursus justo, quis eleifend neque nulla eu urna. Praesent tincidunt, orci dolor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In vitae augue laoreet, ultrices ante non, ultrices metus. Nullam lobortis, sem imperdiet tempor faucibus, mauris nisl cursus justo, quis eleifend neque nulla eu urna. Praesent tinciduna" + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In vitae augue laoreet, ultrices ante non, ultrices metus. Nullam lobortis, sem imperdiet tempor faucibus, mauris nisl cursus justo, quis eleifend neque nulla eu urna. Praesent tincidunt, orci dolor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In vitae augue laoreet, ultrices ante non, ultrices metus. Nullam lobortis, sem imperdiet tempor faucibus, mauris nisl cursus justo, quis eleifend neque nulla eu urna. Praesent tinciduna" + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In vitae augue laoreet, ultrices ante non, ultrices metus. Nullam lobortis, sem imperdiet tempor faucibus, mauris nisl cursus justo, quis eleifend neque nulla eu urna. Praesent tincidunt, orci dolor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In vitae augue laoreet, ultrices ante non, ultrices metus. Nullam lobortis, sem imperdiet tempor faucibus, mauris nisl cursus justo, quis eleifend neque nulla eu urna. Praesent tinciduna" + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In vitae augue laoreet, ultrices ante non, ultrices metus. Nullam lobortis, sem imperdiet tempor faucibus, mauris nisl cursus justo, quis eleifend neque nulla eu urna. Praesent tincidunt, orci dolor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In vitae augue laoreet, ultrices ante non, ultrices metus. Nullam lobortis, sem imperdiet tempor faucibus, mauris nisl cursus justo, quis eleifend neque nulla eu urna. Praesent tinciduna" + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In vitae augue laoreet, ultrices ante non, ultrices metus. Nullam lobortis, sem imperdiet tempor faucibus, mauris nisl cursus justo, quis eleifend neque nulla eu urna. Praesent tincidunt, orci dolor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In vitae augue laoreet, ultrices ante non, ultrices metus. Nullam lobortis, sem imperdiet tempor faucibus, mauris nisl cursus justo, quis eleifend neque nulla eu urna. Praesent tinciduna" + "L"; + vm.expectRevert( + abi.encodeWithSelector( + CorporationsSystemErrors.CorporationsSystem_InvalidStringLength.selector, + longDescription, + 0, + 4000 + ) + ); + world.call( + systemId, + abi.encodeCall( + CorporationsSystem.setMetadata, + (corp1, "NEW", "New Corp Name", longDescription, "https://newcorp.com") + ) + ); + + string + memory longUrl = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In vitae augue laoreet, ultrices ante non, ultrices metus. Nullam lobortis, sem imperdiet tempor faucibus, mauris nisl cursus justo, quis eleifend neque nulla eu urna. Praesent tincidunt, orci dolor."; + vm.expectRevert( + abi.encodeWithSelector(CorporationsSystemErrors.CorporationsSystem_InvalidStringLength.selector, longUrl, 0, 255) + ); + world.call( + systemId, + abi.encodeCall(CorporationsSystem.setMetadata, (corp1, "NEW", "New Corp Name", "New description", longUrl)) + ); + + vm.stopBroadcast(); + } +} diff --git a/packages/corporations/worlds.json b/packages/corporations/worlds.json new file mode 100644 index 0000000..b94f332 --- /dev/null +++ b/packages/corporations/worlds.json @@ -0,0 +1,5 @@ +{ + "31337": { + "address": "0x8a791620dd6260079bf849dc5567adc3f2fdc318" + } +} \ No newline at end of file diff --git a/packages/test-fixtures/foundry.toml b/packages/test-fixtures/foundry.toml index 0a0acb8..d973411 100644 --- a/packages/test-fixtures/foundry.toml +++ b/packages/test-fixtures/foundry.toml @@ -27,7 +27,7 @@ fs_permissions = [{ access = "read", path = "./"}] [profile.local] # Local Anvil Instance -eth_rpc_url = "http://127.0.0.1:8546" +eth_rpc_url = "http://127.0.0.1:8545" [profile.garnet] # Public Testnet diff --git a/packages/test-fixtures/package.json b/packages/test-fixtures/package.json index cad5dfc..dd91be8 100644 --- a/packages/test-fixtures/package.json +++ b/packages/test-fixtures/package.json @@ -10,7 +10,6 @@ "dev:contracts": "export $(cat ../../.env.anvil | grep -v '^#' | xargs) && mud dev-contracts --rpc $RPC_URL --worldAddress ${WORLD_ADDRESS}", "deploy:local": "export $(cat ../../.env.anvil | grep -v '^#' | xargs) && npm run build && mud deploy --profile=local --worldAddress ${WORLD_ADDRESS}", "deploy:garnet": "export $(cat ../../.env.garnet | grep -v '^#' | xargs) && npm run build && mud deploy --profile=garnet --worldAddress ${WORLD_ADDRESS}", - "test:deploy": "export $(cat ../../.env.anvil | grep -v '^#' | xargs) && tsc --noEmit && mud test", "test": "export $(cat ../../.env.anvil | grep -v '^#' | xargs) && forge test --fork-url $RPC_URL" },