Skip to content

Commit

Permalink
World deployments with deterministic addresses
Browse files Browse the repository at this point in the history
  • Loading branch information
tarrencev committed Oct 3, 2023
1 parent 8360af8 commit b7a4dd1
Show file tree
Hide file tree
Showing 15 changed files with 488 additions and 78 deletions.
43 changes: 43 additions & 0 deletions crates/dojo-core/src/base.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use starknet::{ClassHash, SyscallResult, SyscallResultTrait};

use dojo::world::IWorldDispatcher;

#[starknet::interface]
trait IBase<T> {
fn world(self: @T) -> IWorldDispatcher;
}

#[starknet::contract]
mod base {
use starknet::{ClassHash, get_caller_address};

use dojo::upgradable::{IUpgradeable, UpgradeableTrait};
use dojo::world::IWorldDispatcher;

#[storage]
struct Storage {
world_dispatcher: IWorldDispatcher,
}

#[constructor]
fn constructor(ref self: ContractState) {
self.world_dispatcher.write(IWorldDispatcher { contract_address: get_caller_address() });
}

#[external(v0)]
fn world(self: @ContractState) -> IWorldDispatcher {
self.world_dispatcher.read()
}

#[external(v0)]
impl Upgradeable of IUpgradeable<ContractState> {
/// Upgrade contract implementation to new_class_hash
///
/// # Arguments
///
/// * `new_class_hash` - The new implementation class hahs.
fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {
UpgradeableTrait::upgrade(new_class_hash);
}
}
}
43 changes: 43 additions & 0 deletions crates/dojo-core/src/base_test.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use option::OptionTrait;
use starknet::ClassHash;
use traits::TryInto;

use dojo::base::base;
use dojo::upgradable::{IUpgradeableDispatcher, IUpgradeableDispatcherTrait};
use dojo::test_utils::deploy_contract;

#[starknet::contract]
mod contract_upgrade {
#[storage]
struct Storage {}

#[starknet::interface]
trait IQuantumLeap<TState> {
fn plz_more_tps(self: @TState) -> felt252;
}

#[constructor]
fn constructor(ref self: ContractState) {}

#[external(v0)]
impl QuantumLeap of IQuantumLeap<ContractState> {
fn plz_more_tps(self: @ContractState) -> felt252 {
'daddy'
}
}
}

use contract_upgrade::{IQuantumLeapDispatcher, IQuantumLeapDispatcherTrait};

#[test]
#[available_gas(6000000)]
fn test_upgrade() {
let base_address = deploy_contract(base::TEST_CLASS_HASH, array![].span());
let upgradable_dispatcher = IUpgradeableDispatcher { contract_address: base_address };

let new_class_hash: ClassHash = contract_upgrade::TEST_CLASS_HASH.try_into().unwrap();
upgradable_dispatcher.upgrade(new_class_hash);

let quantum_dispatcher = IQuantumLeapDispatcher { contract_address: base_address };
assert(quantum_dispatcher.plz_more_tps() == 'daddy', 'quantum leap failed');
}
4 changes: 4 additions & 0 deletions crates/dojo-core/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
mod base;
#[cfg(test)]
mod base_test;
mod database;
#[cfg(test)]
mod database_test;
Expand All @@ -11,6 +14,7 @@ mod packing_test;
mod world;
#[cfg(test)]
mod world_test;
mod upgradable;

#[cfg(test)]
mod test_utils;
8 changes: 3 additions & 5 deletions crates/dojo-core/src/test_utils.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ use dojo::world::{world, IWorldDispatcher, IWorldDispatcherTrait};
/// # Returns
/// * address of contract deployed
fn deploy_contract(class_hash: felt252, calldata: Span<felt252>) -> ContractAddress {
let (system_contract, _) = starknet::deploy_syscall(
let (contract, _) = starknet::deploy_syscall(
class_hash.try_into().unwrap(), 0, calldata, false
)
.unwrap();
system_contract
contract
}

/// Deploy classhash and passes in world address to constructor
Expand All @@ -49,10 +49,8 @@ fn spawn_test_world(models: Array<felt252>) -> IWorldDispatcher {
)
.unwrap();
// deploy world
let mut world_constructor_calldata = array::ArrayTrait::new();
world_constructor_calldata.append(executor_address.into());
let (world_address, _) = deploy_syscall(
world::TEST_CLASS_HASH.try_into().unwrap(), 0, world_constructor_calldata.span(), false
world::TEST_CLASS_HASH.try_into().unwrap(), 0, array![executor_address.into(), dojo::base::base::TEST_CLASS_HASH].span(), false
)
.unwrap();
let world = IWorldDispatcher { contract_address: world_address };
Expand Down
19 changes: 19 additions & 0 deletions crates/dojo-core/src/upgradable.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use starknet::{ClassHash, SyscallResult, SyscallResultTrait};
use zeroable::Zeroable;
use result::ResultTrait;

#[starknet::interface]
trait IUpgradeable<T> {
fn upgrade(ref self: T, new_class_hash: ClassHash);
}

trait UpgradeableTrait {
fn upgrade(new_class_hash: ClassHash);
}

impl UpgradeableTraitImpl of UpgradeableTrait {
fn upgrade(new_class_hash: ClassHash) {
assert(new_class_hash.is_non_zero(), 'class_hash cannot be zero');
starknet::replace_class_syscall(new_class_hash).unwrap_syscall();
}
}
41 changes: 35 additions & 6 deletions crates/dojo-core/src/world.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use option::OptionTrait;
trait IWorld<T> {
fn model(self: @T, name: felt252) -> ClassHash;
fn register_model(ref self: T, class_hash: ClassHash);
fn deploy_contract(self: @T, salt: felt252, class_hash: ClassHash) -> ContractAddress;
fn uuid(ref self: T) -> usize;
fn emit(self: @T, keys: Array<felt252>, values: Span<felt252>);
fn entity(
Expand All @@ -29,6 +30,7 @@ trait IWorld<T> {
) -> (Span<felt252>, Span<Span<felt252>>);
fn set_executor(ref self: T, contract_address: ContractAddress);
fn executor(self: @T) -> ContractAddress;
fn base(self: @T) -> ClassHash;
fn delete_entity(ref self: T, model: felt252, keys: Span<felt252>);
fn is_owner(self: @T, address: ContractAddress, target: felt252) -> bool;
fn grant_owner(ref self: T, address: ContractAddress, target: felt252);
Expand All @@ -49,12 +51,13 @@ mod world {
use starknet::{
get_caller_address, get_contract_address, get_tx_info,
contract_address::ContractAddressIntoFelt252, ClassHash, Zeroable, ContractAddress,
syscalls::emit_event_syscall, SyscallResultTrait, SyscallResultTraitImpl
syscalls::{deploy_syscall, emit_event_syscall}, SyscallResult, SyscallResultTrait, SyscallResultTraitImpl
};

use dojo::database;
use dojo::database::index::WhereCondition;
use dojo::executor::{IExecutorDispatcher, IExecutorDispatcherTrait};
use dojo::upgradable::{IUpgradeableDispatcher, IUpgradeableDispatcherTrait};
use dojo::world::{IWorldDispatcher, IWorld};


Expand Down Expand Up @@ -101,18 +104,18 @@ mod world {
#[storage]
struct Storage {
executor_dispatcher: IExecutorDispatcher,
models: LegacyMap::<felt252, ClassHash>,
contract_base: ClassHash,
nonce: usize,
models: LegacyMap::<felt252, ClassHash>,
owners: LegacyMap::<(felt252, ContractAddress), bool>,
writers: LegacyMap::<(felt252, ContractAddress), bool>,
// Tracks the calling systems name for auth purposes.
call_stack_len: felt252,
call_stack: LegacyMap::<felt252, felt252>,
}

#[constructor]
fn constructor(ref self: ContractState, executor: ContractAddress) {
fn constructor(ref self: ContractState, executor: ContractAddress, contract_base: ClassHash) {
self.executor_dispatcher.write(IExecutorDispatcher { contract_address: executor });
self.contract_base.write(contract_base);

self
.owners
.write(
Expand Down Expand Up @@ -273,6 +276,23 @@ mod world {
self.models.read(name)
}

/// Deploys a contract associated with the world.
///
/// # Arguments
///
/// * `name` - The name of the contract.
/// * `class_hash` - The class_hash of the contract.
///
/// # Returns
///
/// * `ClassHash` - The class hash of the model.
fn deploy_contract(self: @ContractState, salt: felt252, class_hash: ClassHash) -> ContractAddress {
let (contract_address, _) = deploy_syscall(self.contract_base.read(), salt, array![].span(), false).unwrap_syscall();
let upgradable_dispatcher = IUpgradeableDispatcher { contract_address };
upgradable_dispatcher.upgrade(class_hash);
contract_address
}

/// Issues an autoincremented id to the caller.
///
/// # Returns
Expand Down Expand Up @@ -408,6 +428,15 @@ mod world {
fn executor(self: @ContractState) -> ContractAddress {
self.executor_dispatcher.read().contract_address
}

/// Gets the base contract class hash.
///
/// # Returns
///
/// * `ContractAddress` - The address of the contract_base contract.
fn base(self: @ContractState) -> ClassHash {
self.contract_base.read()
}
}

/// Asserts that the current caller can write to the model.
Expand Down
1 change: 1 addition & 0 deletions crates/dojo-lang/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ fn find_project_contracts(

pub fn collect_core_crate_ids(db: &RootDatabase) -> Vec<CrateId> {
[
ContractSelector("dojo::base::base".to_string()),
ContractSelector("dojo::executor::executor".to_string()),
ContractSelector("dojo::world::world".to_string()),
]
Expand Down
17 changes: 15 additions & 2 deletions crates/dojo-lang/src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use cairo_lang_semantic::db::SemanticGroup;
use cairo_lang_starknet::abi;
use cairo_lang_starknet::plugin::aux_data::StarkNetContractAuxData;
use convert_case::{Case, Casing};
use dojo_world::manifest::{Contract, EXECUTOR_CONTRACT_NAME, WORLD_CONTRACT_NAME};
use dojo_world::manifest::{
Class, Contract, BASE_CONTRACT_NAME, EXECUTOR_CONTRACT_NAME, WORLD_CONTRACT_NAME,
};
use serde::Serialize;
use smol_str::SmolStr;
use starknet::core::types::FieldElement;
Expand Down Expand Up @@ -43,13 +45,24 @@ impl Manifest {
)
);
});
let (base, base_abi) = compiled_classes.get(BASE_CONTRACT_NAME).unwrap_or_else(|| {
panic!(
"{}",
format!(
"Contract `{}` not found. Did you include `dojo` as a dependency?",
BASE_CONTRACT_NAME
)
);
});

manifest.0.world = Contract {
name: WORLD_CONTRACT_NAME.into(),
address: None,
class_hash: *world,
abi: world_abi.clone(),
};
manifest.0.base =
Class { name: BASE_CONTRACT_NAME.into(), class_hash: *base, abi: base_abi.clone() };
manifest.0.executor = Contract {
name: EXECUTOR_CONTRACT_NAME.into(),
address: None,
Expand Down Expand Up @@ -137,7 +150,7 @@ impl Manifest {
compiled_classes: &HashMap<SmolStr, (FieldElement, Option<abi::Contract>)>,
) {
for name in &aux_data.contracts {
if "world" == name.as_str() || "executor" == name.as_str() {
if "world" == name.as_str() || "executor" == name.as_str() || "base" == name.as_str() {
return;
}

Expand Down
Loading

0 comments on commit b7a4dd1

Please sign in to comment.