Balance
ETH is stored in the World contract and owned by namespaces.
Any ETH sent with a call to a System is kept on the World, and credited to the namescape to which the System belongs.
This has two important effects:
- The ETH balance is shared between the different
Systemcontracts in the same namespace of the sameWorld. - The same
Systemcan be used in multipleWorldcontracts and the ETH balances remain separate.
To know how much ETH a call has included a System can check _msgValue() (opens in a new tab).
To know how much ETH a specific namespace has you can look in world:Balances (the Balances table of the world namespace).
import { Balances } from "@latticexyz/world/src/codegen/tables/Balances.sol";
uint256 balance = Balances.get(<namespace>);See this in action
-
Have a MUD application running. The easiest way to do this is to run the template locally.
-
Here we are not concerned with the client, so change to the
contractspage. Because we are not concerned with the client, all the file names will be relative to.../packages/contracts.cd packages/contracts -
Create a
.envfile with:PRIVATE_KEY- the private key of an account that has ETH on the blockchain.WORLD_ADDRESS- the address of theWorldto which you add the namespace.
If you are using the template with a fresh
pnpm dev, then you can use this.env:.env# Anvil default private key for the second account # (NOT the account that deployed the World) PRIVATE_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d # Address for the world we are extending WORLD_ADDRESS=0x6e9474e9c83676b9a71133ff96db43e7aa0a4342 -
Create this file as
scripts/GetBalance.s.sol.GetBalance.s.sol// SPDX-License-Identifier: MIT pragma solidity >=0.8.21; import { Script } from "forge-std/Script.sol"; import { console } from "forge-std/console.sol"; import { Balances } from "@latticexyz/world/src/codegen/tables/Balances.sol"; import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; import { WorldResourceIdLib } from "@latticexyz/world/src/WorldResourceId.sol"; contract GetBalance is Script { function run() external { address worldAddress = vm.envAddress("WORLD_ADDRESS"); StoreSwitch.setStoreAddress(worldAddress); console.log("World at:", worldAddress); ResourceId namespaceResource = WorldResourceIdLib.encodeNamespace(bytes14("")); console.log("Namespace ID: %x", uint256(ResourceId.unwrap(namespaceResource))); uint256 balance = Balances.get(namespaceResource); console.log("Balance: %d wei", balance); } }Explanation
// SPDX-License-Identifier: MIT pragma solidity >=0.8.21;Standard Solidity boilerplate
import { Script } from "forge-std/Script.sol"; import { console } from "forge-std/console.sol";Standard
forgescript (opens in a new tab) boilerplate.import { Balances } from "@latticexyz/world/src/codegen/tables/Balances.sol";The
Balancestable contains namespace balances. Note that while this table's Solidity code is available as part of the library, it is a standard MUD table and as such gets created from amud.config.tsfile (opens in a new tab).import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol";We need this file (opens in a new tab) to specify the
World's address.import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; import { WorldResourceIdLib } from "@latticexyz/world/src/WorldResourceId.sol";We need the namespace's
ResourceId.contract GetBalance is Script { function run() external { address worldAddress = vm.envAddress("WORLD_ADDRESS");Get
WORLD_ADDRESSfrom.env.StoreSwitch.setStoreAddress(worldAddress); console.log("World at:", worldAddress);Set the address of the
World(opens in a new tab) so future data calls to go to the correct location.ResourceId namespaceResource = WorldResourceIdLib.encodeNamespace(bytes14("")); console.log("Namespace ID: %x", uint256(ResourceId.unwrap(namespaceResource)));Create the
ResourceIdfor the root namespace.uint256 balance = Balances.get(namespaceResource); console.log("Balance: %d wei", balance); } }Finally, get the balance from the
world:Balancestable. -
Source the
.envfile (we are going to use the variables there to transfer ETH to the root namespace's account).source .env -
Run the script.
forge script script/GetBalance.s.sol --rpc-url http://localhost:8545The balance should be zero for now.
-
To allow
incrementto accept wei, editsrc/systems/IncrementSystem.solto make itpayable:IncrementSystem.sol// SPDX-License-Identifier: MIT pragma solidity >=0.8.21; import { System } from "@latticexyz/world/src/System.sol"; import { Counter } from "../codegen/index.sol"; contract IncrementSystem is System { function increment() public payable returns (uint32) { uint32 counter = Counter.get(); uint32 newValue = counter + 1; Counter.set(newValue); return newValue; } } -
pnpm devdeploys the new code, but it deploys it to a newWorld. Restartpnpm devto have aWorldin the same address as before. -
Call
incrementwith ETH.cast send --private-key $PRIVATE_KEY 0x6E9474e9c83676B9A71133FF96Db43E7AA0a4342 --value 1ether "increment()" -
Run the script again to see that the balance is higher.
forge script script/GetBalance.s.sol --rpc-url http://localhost:8545
To transfer ETH out of the World you need to have the access permission level to the namespace itself.
Note that System contracts within the namespace do have that permission.
You use either transferBalanceToNamespace (opens in a new tab), if you want to transfer between namespaces in the same World, or transferBalanceToAddress (opens in a new tab), to transfer to a different address.
See this in action
These steps assume you already transferred 1 ether to the root namespace.
-
Run this command to transfer 10 wei to the zero address.
cast send --private-key $PRIVATE_KEY 0x6E9474e9c83676B9A71133FF96Db43E7AA0a4342 "transferBalanceToAddress(bytes32,address,uint256)" 0x6e73000000000000000000000000000000000000000000000000000000000000 `cast address-zero` 10 -
See that the namespace's balance is lower by 10 wei.
forge script script/GetBalance.s.sol --rpc-url http://localhost:8545 -
See that 10 wei have been deducted from the balance of the
World.cast balance $WORLD_ADDRESS