This repository is a complete starter for developing games on Starknet using Cairo/Dojo as backend. It includes achievements integration, player system, and is production ready in Sepolia.
contract/
├── src/
│ ├── achievements/ # Achievements/achievements system
│ │ └── achievement.cairo # Enum and configuration of achievements
│ ├── helpers/ # Aux functions
│ │ └── timestamp.cairo # Timestamps
│ ├── models/ # Models
│ │ └── player.cairo # Player model
│ ├── systems/ # Main contracts (business logic)
│ │ └── game.cairo # Main system
│ ├── tests/ # Integration tests
│ │ ├── test_game.cairo # System tests
│ │ └── utils.cairo # Testing utilities
│ ├── constants.cairo # Global constants
│ ├── store.cairo # Layer of data access
│ └── lib.cairo # Main module
├── Scarb.toml # Project settings
├── dojo_dev.toml # Configs for local development
├── dojo_sepolia.toml # Configs for Sepolia
└── torii_config.toml # Indexer configs
Models define the data structures that are stored in the Dojo world:
Player: Main entity that represents a player.- owner: Address of the owner
- experience`: Experience points
- health`: Health points
- coins
: Players coins - creation_day`: Day of creation
The store acts as an intermediate layer between models and systems:
- Getters:
read_player(),read_player_from_address(). - Setters:
write_player(),write_player_from_address(). - Creators:
create_player(),create_player(),create_player(). - Game Actions:
train_player(),mine_coins(),rest_player(),rest_player().
Systems contain the business logic and are the methods exposed to the client:
spawn_player(): Create new player.train(): Train player (+10 experience)mine(): Mine coins (+5 coins, -5 health)rest(): Rest (+20 health)
Complete integrated achievements system:
pub enum Achievement {
MiniGamer, // 1 action
MasterGamer, // 10 action
LegendGamer, // 20 action
AllStarGamer, // 30 action
SenseiGamer, // 50 action
}The next three steps assume you are in the
contract/directory.
katana --config katana.tomlsozo build
sozo migratetorii --world <WORLD_ADDRESS> --http.cors_origins "*"In the client/ directory, create an .env.development.local file with the following contents:
VITE_PUBLIC_DEPLOY_TYPE=localhost
VITE_PUBLIC_NODE_URL=http://localhost:5050
VITE_PUBLIC_TORII=http://localhost:8080Now run npm run dev:https and you should be ready to go!
- Create Argent or Braavos account on Sepolia testnet.
- Deploy the account and enable it.
- Fund with STRK tokens using faucets
- Obtain
account_addressandprivate_key.
export STARKNET_RPC_URL="https://api.cartridge.gg/x/starknet/sepolia"
export DEPLOYER_ACCOUNT_ADDRESS="<tu_direccion_de_cuenta>"
export DEPLOYER_PRIVATE_KEY="<tu_clave_privada>"In dojo_sepolia.toml, set a new seed:
seed = "seed456" # Update the seed to create a new deployment# Delete old manifest
rm manifest_sepolia.jsonIn torii_config.toml, clear the previous world address:
world_address = ""cd contract
scarb run sepolia✅ The deploy will return the world_address you will need for the client.
Note: if you are using a new account and receive an "account does not exist error" please ensure your account has been fully deployed
Torii is the indexer that allows you to query the state of the world efficiently.
slot auth login
# Authenticate with controller usernameslot deployments create <instance_name> torii \
--sql.historical "universe-TrophyProgression" \
--world <world_address> \
--rpc https://api.cartridge.gg/x/starknet/sepolia📝 The instance_name is used later on in the client to connect to this specific instance.
Achievements are defined in src/achievements/achievement.cairo:
impl AchievementImpl of AchievementTrait {
fn identifier(self: Achievement) -> felt252 { /* ... */ }
fn title(self: Achievement) -> felt252 { /* ... */ }
fn description(self: Achievement) -> ByteArray { /* ... */ }
fn tasks(self: Achievement) -> Span<Task> { /* ... */ }
// ... more methods
}The achievements are automatically initialized in dojo_init():
fn dojo_init(ref self: ContractState) {
let mut achievement_id: u8 = 1;
while achievement_id <= constants::ACHIEVEMENTS_COUNT {
let achievement: Achievement = achievement_id.into();
self.achievable.create(world, /* parámetros del achievement */);
achievement_id += 1;
}
}Each game action (train, mine, rest) emits progress events:
// In each action of the system
let mut achievement_id = constants::ACHIEVEMENTS_INITIAL_ID;
while achievement_id <= constants::ACHIEVEMENTS_COUNT {
let task: Achievement = achievement_id.into();
achievement_store.progress(
player.owner.into(),
task.identifier(),
1,
get_block_timestamp()
);
achievement_id += 1;
}cd contract
sozo testtest_spawn_player(): Player creationtest_train_player(): Training systemtest_mine_coins(): Mining systemtest_rest_player(): Rest systemtest_complete_game_flow(): Complete flow of the game
- Cairo project configuration
- Dependencies (Dojo, Achievement)
- Deployment scripts
- External contracts
- Dojo World Configuration
- RPC URLs
- Write permissions
- Project Namespace
- Indexer configuration
- Events to index
- CORS and network options