Skip to main content
Glama

Sensei MCP

by dojoengine
token.txt14.1 kB
An example of a ERC20 token contract in dojo // SPDX-License-Identifier: MIT // Compatible with OpenZeppelin Contracts for Cairo ^0.20.0 #[starknet::contract] mod ERC20Token { use openzeppelin::access::ownable::OwnableComponent; use openzeppelin::token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; use starknet::ContractAddress; component!(path: ERC20Component, storage: erc20, event: ERC20Event); component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); // External #[abi(embed_v0)] impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl<ContractState>; #[abi(embed_v0)] impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl<ContractState>; // Internal impl ERC20InternalImpl = ERC20Component::InternalImpl<ContractState>; impl OwnableInternalImpl = OwnableComponent::InternalImpl<ContractState>; #[storage] struct Storage { #[substorage(v0)] erc20: ERC20Component::Storage, #[substorage(v0)] ownable: OwnableComponent::Storage, } #[event] #[derive(Drop, starknet::Event)] enum Event { #[flat] ERC20Event: ERC20Component::Event, #[flat] OwnableEvent: OwnableComponent::Event, } #[constructor] fn constructor( ref self: ContractState, owner: ContractAddress, name: ByteArray, symbol: ByteArray, initial_supply: u256, recipient: ContractAddress, ) { self.ownable.initializer(owner); self.erc20.initializer(name, symbol); self.erc20.mint(recipient, initial_supply); } /// This implementation is not secure, only for testing purposes and quick minting. #[generate_trait] #[abi(per_item)] impl ExternalImpl of ExternalTrait { #[external(v0)] fn mint(ref self: ContractState, amount: u256) { self.erc20.mint(starknet::get_caller_address(), amount); } } } An example of a ERC721 contract in dojo // SPDX-License-Identifier: MIT // Compatible with OpenZeppelin Contracts for Cairo ^0.20.0 #[starknet::contract] mod ERC721Token { use openzeppelin::access::ownable::OwnableComponent; use openzeppelin::introspection::src5::SRC5Component; use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; use starknet::ContractAddress; use crate::externals::components::erc4906::ERC4906Component; component!(path: ERC721Component, storage: erc721, event: ERC721Event); component!(path: SRC5Component, storage: src5, event: SRC5Event); component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); component!(path: ERC4906Component, storage: erc4906, event: ERC4906Event); // External #[abi(embed_v0)] impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl<ContractState>; #[abi(embed_v0)] impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl<ContractState>; #[abi(embed_v0)] impl ERC4906MixinImpl = ERC4906Component::ERC4906Implementation<ContractState>; // Internal impl ERC721InternalImpl = ERC721Component::InternalImpl<ContractState>; impl OwnableInternalImpl = OwnableComponent::InternalImpl<ContractState>; #[storage] struct Storage { #[substorage(v0)] erc721: ERC721Component::Storage, #[substorage(v0)] src5: SRC5Component::Storage, #[substorage(v0)] ownable: OwnableComponent::Storage, #[substorage(v0)] erc4906: ERC4906Component::Storage, } #[event] #[derive(Drop, starknet::Event)] enum Event { #[flat] ERC721Event: ERC721Component::Event, #[flat] SRC5Event: SRC5Component::Event, #[flat] OwnableEvent: OwnableComponent::Event, #[flat] ERC4906Event: ERC4906Component::Event, } #[constructor] fn constructor( ref self: ContractState, owner: ContractAddress, name: ByteArray, symbol: ByteArray, base_uri: ByteArray, ) { self.ownable.initializer(owner); self.erc721.initializer(name, symbol, base_uri); } /// This implementation is not secure, only for testing purposes and quick minting. #[generate_trait] #[abi(per_item)] impl ERC721Demo of ERC721DemoTrait { #[external(v0)] fn mint(ref self: ContractState, token_id: u256) { self.erc721.mint(starknet::get_caller_address(), token_id); } #[external(v0)] fn update_token_metadata(ref self: ContractState, token_id: u256) { // Only owner can update metadata self.ownable.assert_only_owner(); // Emit metadata update event self.erc4906.emit_metadata_update(token_id); } #[external(v0)] fn update_batch_token_metadata( ref self: ContractState, from_token_id: u256, to_token_id: u256, ) { // Only owner can update metadata self.ownable.assert_only_owner(); // Emit batch metadata update event self.erc4906.emit_batch_metadata_update(from_token_id, to_token_id); } #[external(v0)] fn update_tokens_metadata(ref self: ContractState, token_ids: Span<u256>) { // Only owner can update metadata self.ownable.assert_only_owner(); // Emit metadata update event for each token let mut i: usize = 0; loop { if i >= token_ids.len() { break; } self.erc4906.emit_metadata_update(*token_ids.at(i)); i += 1; } } } } An example of an ERC1155 token in dojo // SPDX-License-Identifier: MIT // Compatible with OpenZeppelin Contracts for Cairo ^0.20.0 #[starknet::contract] mod ERC1155Token { use openzeppelin::access::ownable::OwnableComponent; use openzeppelin::introspection::src5::SRC5Component; use openzeppelin::token::erc1155::{ERC1155Component, ERC1155HooksEmptyImpl}; use starknet::ContractAddress; use crate::externals::components::erc4906::ERC4906Component; component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event); component!(path: SRC5Component, storage: src5, event: SRC5Event); component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); component!(path: ERC4906Component, storage: erc4906, event: ERC4906Event); // External #[abi(embed_v0)] impl ERC1155MixinImpl = ERC1155Component::ERC1155MixinImpl<ContractState>; #[abi(embed_v0)] impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl<ContractState>; #[abi(embed_v0)] impl ERC4906MixinImpl = ERC4906Component::ERC4906Implementation<ContractState>; // Internal impl ERC1155InternalImpl = ERC1155Component::InternalImpl<ContractState>; impl OwnableInternalImpl = OwnableComponent::InternalImpl<ContractState>; #[storage] struct Storage { #[substorage(v0)] erc1155: ERC1155Component::Storage, #[substorage(v0)] src5: SRC5Component::Storage, #[substorage(v0)] ownable: OwnableComponent::Storage, #[substorage(v0)] erc4906: ERC4906Component::Storage, } #[event] #[derive(Drop, starknet::Event)] enum Event { #[flat] ERC1155Event: ERC1155Component::Event, #[flat] SRC5Event: SRC5Component::Event, #[flat] OwnableEvent: OwnableComponent::Event, #[flat] ERC4906Event: ERC4906Component::Event, } #[constructor] fn constructor(ref self: ContractState, owner: ContractAddress, base_uri: ByteArray) { self.ownable.initializer(owner); self.erc1155.initializer(base_uri); } /// This implementation is not secure, only for testing purposes and quick minting. #[generate_trait] #[abi(per_item)] impl ExternalImpl of ExternalTrait { #[external(v0)] fn token_uri(ref self: ContractState, token_id: u256) -> ByteArray { let seed = starknet::get_execution_info().block_info.block_number; format!( "data:application/json,{{ \"image\": \"https://api.dicebear.com/9.x/lorelei-neutral/png?seed={}\" }}", seed, ) } #[external(v0)] fn mint(ref self: ContractState, token_id: u256, value: u256) { self .erc1155 .update( starknet::contract_address_const::<0x0>(), starknet::get_caller_address(), array![token_id].span(), array![value].span(), ); // Seems to not be supported by default dojo account. // self.erc1155.mint_with_acceptance_check(account, token_id, value, data); } #[external(v0)] fn transfer_from( ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, value: u256, ) { self.erc1155.update(from, to, array![token_id].span(), array![value].span()); // safe transfer from does not support default account since they dont implement // receiver. } #[external(v0)] fn batch_mint(ref self: ContractState, token_ids: Span<u256>, values: Span<u256>) { self .erc1155 .update( starknet::contract_address_const::<0x0>(), starknet::get_caller_address(), token_ids, values, ); // Seems to not be supported by default dojo account. // self.erc1155.batch_mint_with_acceptance_check(account, token_ids, values, data); } #[external(v0)] fn update_token_metadata(ref self: ContractState, token_id: u256) { // Only owner can update metadata self.ownable.assert_only_owner(); // Emit metadata update event self.erc4906.emit_metadata_update(token_id); } #[external(v0)] fn update_batch_token_metadata( ref self: ContractState, from_token_id: u256, to_token_id: u256, ) { // Only owner can update metadata self.ownable.assert_only_owner(); // Emit batch metadata update event self.erc4906.emit_batch_metadata_update(from_token_id, to_token_id); } // Optional: Batch update specific token IDs #[external(v0)] fn update_tokens_metadata(ref self: ContractState, token_ids: Span<u256>) { // Only owner can update metadata self.ownable.assert_only_owner(); // Emit metadata update event for each token let mut i: usize = 0; loop { if i >= token_ids.len() { break; } self.erc4906.emit_metadata_update(*token_ids.at(i)); i += 1; } } } } An example dojo_dev.toml profile with those external contracts declared [world] description = "example world" name = "example" seed = "dojo_examples" [[models]] tag = "ns-Message" description = "Message sent by a player" [[models]] tag = "ns-Position" description = "position of a player in the world" [[models]] tag = "ns-Moves" description = "move of a player in the world" [[events]] tag = "ns-Moved" description = "when a player has moved" [[contracts]] tag = "ns-actions" description = "set of actions for a player" [[external_contracts]] contract_name = "ERC20Token" instance_name = "GoldToken" salt = "1" constructor_data = ["0x2af9427c5a277474c079a1283c880ee8a6f0f8fbf73ce969c08d88befec1bba", "str:Gold", "str:GOLD", "u256:0x10000000000000", "0x2af9427c5a277474c079a1283c880ee8a6f0f8fbf73ce969c08d88befec1bba"] [[external_contracts]] contract_name = "ERC20Token" instance_name = "WoodToken" salt = "1" constructor_data = ["0x2af9427c5a277474c079a1283c880ee8a6f0f8fbf73ce969c08d88befec1bba", "str:Wood", "str:WOOD", "u256:0x10000000000000", "0x2af9427c5a277474c079a1283c880ee8a6f0f8fbf73ce969c08d88befec1bba"] [[external_contracts]] contract_name = "ERC721Token" instance_name = "Badge" salt = "1" constructor_data = ["0x2af9427c5a277474c079a1283c880ee8a6f0f8fbf73ce969c08d88befec1bba", "str:Badge", "str:BDG", "str:https://badge.com/" ] [[external_contracts]] contract_name = "ERC1155Token" instance_name = "Rewards" salt = "1" constructor_data = ["0x2af9427c5a277474c079a1283c880ee8a6f0f8fbf73ce969c08d88befec1bba", "str:https://rewards.com/" ] [[external_contracts]] contract_name = "Bank" salt = "1" constructor_data = ["0x2af9427c5a277474c079a1283c880ee8a6f0f8fbf73ce969c08d88befec1bba"] [[external_contracts]] contract_name = "Saloon" constructor_data = [] salt = "1" [lib_versions] "ns-simple_math" = "0_1_0" [namespace] default = "ns" [env] rpc_url = "http://localhost:5050/" # Default account for katana with seed = 0 account_address = "0x2af9427c5a277474c079a1283c880ee8a6f0f8fbf73ce969c08d88befec1bba" private_key = "0x1800000000300000180000000000030000000000003006001800006600" world_address = "0x24334e79a3c56e5374c5bdd148c22ff2f0de3b4dc6e734e22ea49795f367221" ipfs_config.url = "https://ipfs.infura.io:5001" ipfs_config.username = "2EBrzr7ZASQZKH32sl2xWauXPSA" ipfs_config.password = "12290b883db9138a8ae3363b6739d220" [init_call_args] "ns-others" = ["0xff"] [writers] "ns" = [ "ns-mock_token", "ns-actions", "ns-others" ] You should define them like this for each one of the contracts [[external_contracts]] contract_name = "ERC20Token" instance_name = "GoldToken" salt = "1" constructor_data = ["0x2af9427c5a277474c079a1283c880ee8a6f0f8fbf73ce969c08d88befec1bba", "str:Gold", "str:GOLD", "u256:0x10000000000000", "0x2af9427c5a277474c079a1283c880ee8a6f0f8fbf73ce969c08d88befec1bba"] and always pass in the correct constructor_data while referring to the token contract implementation to know what is the constructor data.

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dojoengine/sensei-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server