Loading...
Loading...
Implement, deploy, and index ERC20 and ERC721 tokens in Dojo. Use when adding token contracts, deploying them, or configuring Torii to index balances and transfers.
npx skill4agent add dojoengine/book dojo-tokensozo migrateScarb.toml[dependencies]
origami_token = { git = "https://github.com/dojoengine/origami", tag = "v1.0.0" }#[derive(Copy, Drop, Serde)]
#[dojo::model]
pub struct Gold {
#[key]
pub player: ContractAddress,
pub amount: u256,
}
#[starknet::interface]
trait IGoldToken<T> {
fn mint(ref self: T, to: ContractAddress, amount: u256);
fn transfer(ref self: T, to: ContractAddress, amount: u256);
fn balance_of(self: @T, account: ContractAddress) -> u256;
}
#[dojo::contract]
mod gold_token {
use super::{IGoldToken, Gold};
use starknet::{ContractAddress, get_caller_address};
use dojo::model::ModelStorage;
#[abi(embed_v0)]
impl GoldTokenImpl of IGoldToken<ContractState> {
fn mint(ref self: ContractState, to: ContractAddress, amount: u256) {
let mut world = self.world_default();
let mut balance: Gold = world.read_model(to);
balance.amount += amount;
world.write_model(@balance);
}
fn transfer(ref self: ContractState, to: ContractAddress, amount: u256) {
let mut world = self.world_default();
let from = get_caller_address();
let mut from_balance: Gold = world.read_model(from);
let mut to_balance: Gold = world.read_model(to);
assert(from_balance.amount >= amount, 'insufficient balance');
from_balance.amount -= amount;
to_balance.amount += amount;
world.write_model(@from_balance);
world.write_model(@to_balance);
}
fn balance_of(self: @ContractState, account: ContractAddress) -> u256 {
let world = self.world_default();
let balance: Gold = world.read_model(account);
balance.amount
}
}
#[generate_trait]
impl InternalImpl of InternalTrait {
fn world_default(self: @ContractState) -> dojo::world::WorldStorage {
self.world(@"my_game")
}
}
}#[derive(Copy, Drop, Serde)]
#[dojo::model]
pub struct Weapon {
#[key]
pub token_id: u256,
pub owner: ContractAddress,
pub damage: u32,
pub rarity: u8,
}
#[starknet::interface]
trait IWeaponNFT<T> {
fn mint(ref self: T, to: ContractAddress, damage: u32) -> u256;
fn transfer(ref self: T, to: ContractAddress, token_id: u256);
fn owner_of(self: @T, token_id: u256) -> ContractAddress;
}
#[dojo::contract]
mod weapon_nft {
use super::{IWeaponNFT, Weapon};
use starknet::{ContractAddress, get_caller_address};
use dojo::model::ModelStorage;
#[abi(embed_v0)]
impl WeaponNFTImpl of IWeaponNFT<ContractState> {
fn mint(ref self: ContractState, to: ContractAddress, damage: u32) -> u256 {
let mut world = self.world_default();
let token_id: u256 = world.uuid().into();
let weapon = Weapon {
token_id,
owner: to,
damage,
rarity: 1,
};
world.write_model(@weapon);
token_id
}
fn transfer(ref self: ContractState, to: ContractAddress, token_id: u256) {
let mut world = self.world_default();
let from = get_caller_address();
let mut weapon: Weapon = world.read_model(token_id);
assert(weapon.owner == from, 'not owner');
weapon.owner = to;
world.write_model(@weapon);
}
fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress {
let world = self.world_default();
let weapon: Weapon = world.read_model(token_id);
weapon.owner
}
}
#[generate_trait]
impl InternalImpl of InternalTrait {
fn world_default(self: @ContractState) -> dojo::world::WorldStorage {
self.world(@"my_game")
}
}
}#[derive(Copy, Drop, Serde)]
#[dojo::event]
pub struct TokenTransferred {
#[key]
pub from: ContractAddress,
#[key]
pub to: ContractAddress,
pub amount: u256,
}
// Emit in your functions
world.emit_event(@TokenTransferred { from, to, amount });dojo-deploy[[target.starknet-contract]]
build-external-contracts = [
"dojo::world::world_contract::world",
"tokens::models::m_ERC20Token"
]dojo_dev.toml[[external_contracts]]
contract_name = "ERC20Token"
instance_name = "GoldToken"
salt = "1"
constructor_data = [
"str:Gold Coin", # Token name
"sstr:GOLD", # Symbol
"u256:1000000000000000000", # Total supply (1e18)
"0x1234567890abcdef..." # Owner address
][[external_contracts]]sozo build && sozo migratedojo-indexer[indexing]ERC20:ERC721:# torii.toml
[indexing]
contracts = [
"ERC20:0xYOUR_GOLD_TOKEN_ADDRESS",
"ERC721:0xYOUR_WEAPON_NFT_ADDRESS",
]torii --world 0xYOUR_WORLD \
--indexing.contracts "ERC20:0xGOLD_TOKEN" \
--indexing.contracts "ERC721:0xWEAPON_NFT"[indexing]
contracts = [
"ERC20:0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", # ETH
"ERC20:0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", # STRK
]tokensbalanceserc_transfersSELECT * FROM tokens;
SELECT * FROM balances WHERE account_address = '0xPLAYER';
SELECT * FROM erc_transfers ORDER BY rowid DESC LIMIT 20;import { useTokens } from "@dojoengine/sdk/react";
function TokenBalance({ address }: { address: string }) {
const { tokens, getBalance, toDecimal } = useTokens({
accountAddresses: [address],
});
return (
<div>
{tokens.map((token, idx) => (
<div key={idx}>
{token.symbol}: {toDecimal(token, getBalance(token))}
</div>
))}
</div>
);
}ERC20:ERC721: