Skip to content
Back to skills

Dojo Token

ASecurity

Implement ERC20 and ERC721 token standards in Dojo using Origami library. Use when adding fungible tokens, NFTs, or token-based game mechanics.

  • 53 stars
  • 0 votes
  • 0 copies
  • 5 views
  • Added February 10, 2026
developmentgogitapidocumentation

Works with

  • cli
  • api

Security analysis

A100/100

Scanned February 12, 2026

npx -y skills add dojoengine/book --skill dojo-token --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Dojo Token?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Dojo Token
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/dojoengine-dojo-token/badge)](https://www.skillsdirectory.com/skills/dojoengine-dojo-token)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: dojo-token
description: Implement ERC20 and ERC721 token standards in Dojo using Origami library. Use when adding fungible tokens, NFTs, or token-based game mechanics.
allowed-tools: Read, Write, Edit, Glob, Grep
---

# Dojo Token Standards

Implement ERC20 fungible tokens and ERC721 NFTs in your Dojo game using the Origami library.

## When to Use This Skill

- "Implement ERC20 token for game currency"
- "Create NFT items with ERC721"
- "Add token standard to my game"
- "Use Origami for tokens"

## What This Skill Does

Implements token standards:
- ERC20 fungible tokens (currency, resources)
- ERC721 non-fungible tokens (items, characters)
- Token minting and burning
- Transfer mechanics
- Balance tracking

## Token Standards

### ERC20 - Fungible Tokens

For interchangeable assets:
- Game currency (gold, gems)
- Resources (wood, stone)
- Experience points

**Properties:**
- Divisible (can have fractions)
- Interchangeable (any token = any other)
- Track balances per account

### ERC721 - Non-Fungible Tokens

For unique assets:
- Character NFTs
- Equipment/items
- Land plots
- Achievements

**Properties:**
- Unique (each has token ID)
- Indivisible (whole units only)
- Individual ownership tracking

## Using Origami Library

### Installation

Add to `Scarb.toml`:
```toml
[dependencies]
origami_token = { git = "https://github.com/dojoengine/origami", tag = "v1.0.0" }
```

Origami provides reusable token components following standard interfaces.
Refer to the [Origami documentation](https://github.com/dojoengine/origami) for the latest API.

## Simple Token Implementation

You can implement tokens using standard Dojo models without Origami:

### ERC20-like Currency

```cairo
#[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")
        }
    }
}
```

### ERC721-like NFT

```cairo
#[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")
        }
    }
}
```

## Game Patterns

### In-Game Currency

```cairo
// Award gold for completing quest
fn complete_quest(ref self: ContractState, quest_id: u32) {
    let mut world = self.world_default();
    let player = get_caller_address();

    // Check quest is completed
    // ...

    // Award gold
    let mut gold: Gold = world.read_model(player);
    gold.amount += 100;
    world.write_model(@gold);
}

// Spend gold to buy item
fn buy_item(ref self: ContractState, item_id: u32, price: u256) {
    let mut world = self.world_default();
    let player = get_caller_address();

    let mut gold: Gold = world.read_model(player);
    assert(gold.amount >= price, 'insufficient gold');

    gold.amount -= price;
    world.write_model(@gold);

    // Give item to player
    // ...
}
```

### Multiple Resource Types

```cairo
#[derive(Copy, Drop, Serde)]
#[dojo::model]
pub struct Resources {
    #[key]
    pub player: ContractAddress,
    pub wood: u256,
    pub stone: u256,
    pub iron: u256,
}
```

### Equipment NFTs

```cairo
fn equip_weapon(ref self: ContractState, token_id: u256) {
    let mut world = self.world_default();
    let player = get_caller_address();

    // Check ownership
    let weapon: Weapon = world.read_model(token_id);
    assert(weapon.owner == player, 'not owner');

    // Equip
    let mut equipment: Equipment = world.read_model(player);
    equipment.weapon_id = token_id;
    world.write_model(@equipment);
}
```

## Token Events

```cairo
#[derive(Copy, Drop, Serde)]
#[dojo::event]
pub struct TokenTransferred {
    #[key]
    pub from: ContractAddress,
    #[key]
    pub to: ContractAddress,
    pub amount: u256,
}

#[derive(Copy, Drop, Serde)]
#[dojo::event]
pub struct NFTTransferred {
    #[key]
    pub token_id: u256,
    pub from: ContractAddress,
    pub to: ContractAddress,
}

// Emit in your functions
world.emit_event(@TokenTransferred { from, to, amount });
```

## Considerations

- **Balance checks**: Always verify sufficient balance before transfers
- **Ownership**: Always verify ownership before NFT operations
- **Overflow**: Use u256 for token amounts to avoid overflow
- **Events**: Emit events for all token operations for indexing
- **Permissions**: Grant writer permission to token contracts

## Next Steps

After implementing tokens:
1. Test thoroughly with `dojo-test` skill
2. Deploy with `dojo-deploy` skill
3. Integrate with client (`dojo-client` skill)
4. Set up permissions (`dojo-world` skill)

## Related Skills

- **dojo-model**: Token models extend Dojo models
- **dojo-system**: Token logic in systems
- **dojo-test**: Test token operations
- **dojo-review**: Audit token implementation

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.