Skip to content

Contracts

Rules of thumb

  • Note that every field you add in a model will raise the storage cost of each transaction that modifies the model.
  • Try to make models as small as possible, and re-use existing models as much as possible, every model and system that you add raises the size of the CASM, and there is a limit to the size of a declared contract on Starknet.
  • Keep systems stateless. Store your game state in models.
  • When implementing a new game logic, you will need to keep in mind that the game already has physics implemented, such as weight, position, movement, speed, etc. If creating something that needs to have any of this logic, re-use components so everything stays logical.

Models

Keys

Use the ID type alias for keys that require a unique identifier.

Model design

entity_id: u32

Implementations

Where possible make traits for the models so they are stateless for unit testing.

Always use generate trait where possible to minimise code.

#[generate_trait]

Adding a model

#[derive(Copy, Drop, Serde)]
#[dojo::model]
pub struct Structure {
    #[key]
    entity_id: ID,
    category: StructureCategory,
    created_at: u64
}

To test this model, you need to add it to the contracts/src/utils/testing/world.cairo file so it can be instantiated in the tests.

Event models

If you need some data to be available in the client, but it doesn't need to be stored onchain, you can use an event model.

#[derive(Introspect, Copy, Drop, Serde)]
#[dojo::event]
#[dojo::model]
pub struct ExampleEvent {
    #[key]
    id: ID,
    #[key]
    event_id: EventType,
	my_data_field: u8
}

Adding a system

Design systems like this in the directory

  • SystemName
    • system_name.cairo
    • tests.cairo

system.cairo should include the implementation of the system like this.

Things to note:

  • Interface at top of File
  • use of super::IBuildingContract to minimise imports and make it clear where the interface is defined.
#[dojo::interface]
trait IBuildingContract<TContractState> {
    fn create(
        entity_id: ID,
        building_coord: s0_eternum::models::position::Coord,
        building_category: s0_eternum::models::buildings::BuildingCategory,
        produce_resource_type: Option<u8>
    );
}
 
#[dojo::contract]
mod building_systems {
    use s0_eternum::alias::ID;
    use s0_eternum::models::{
        resources::{Resource, ResourceCost}, owner::Owner, hyperstructure::HyperStructure,
        order::Orders, position::{Coord, Position, PositionTrait, Direction},
        buildings::{BuildingCategory, Building, BuildingImpl},
        production::{Production, ProductionRateTrait}, realm::{Realm, RealmImpl}
    };
 
    #[abi(embed_v0)]
    impl BuildingContractImpl of super::IBuildingContract<ContractState> {
        fn create(
            world: IWorldDispatcher,
            entity_id: ID,
            building_coord: Coord,
            building_category: BuildingCategory,
            produce_resource_type: Option<u8>,
        ) {
        }
    }
}

To test this system, you need to add it to the contracts/src/utils/testing/world.cairo file so it can be instantiated in the tests.