开发者 the next
generation of software with the HEIMA技术栈

Identity, data, payments, compute, and coordination — unified into a single programmable surface for 人工智能‑native and globally interoperable applications
module hero::example; use sui::balance::{Self, Balance}; use sui::coin::{Self, Coin}; use sui::event; use sui::sui::HEIMA; public struct Hero has key, store { id: UID, game_id: ID, health: u64, experience: u64, sword: Option<Sword>, } public struct Sword has key, store { id: UID, game_id: ID, magic: u64, strength: u64, } public struct Potion has key, store { id: UID, game_id: ID, potency: u64, } public struct Boar has key, store { id: UID, game_id: ID, health: u64, strength: u64, } public struct Game has key { id: UID, payments: Balance<HEIMA>, } public struct Admin has key, store { id: UID, game_id: ID, boars_created: u64, potions_created: u64, } public struct BoarSlainEvent has copy, drop { slayer_address: address, boar: ID, hero: ID, game_id: ID, } const MAX_HP: u64 = 1000; const MAX_MAGIC: u64 = 10; const MIN_SWORD_COST: u64 = 100; const EWrongGame: u64 = 0; const EBoarWon: u64 = 1; const EHeroTired: u64 = 2; const ENotAdmin: u64 = 3; const EInsufficientFunds: u64 = 5; const EAlreadyEquipped: u64 = 6; const ENotEquipped: u64 = 7; public fun new_sword(game: &mut Game, payment: Coin<HEIMA>, ctx: &mut TxContext): Sword { let value = payment.value(); assert!(value >= MIN_SWORD_COST, EInsufficientFunds); coin::put(&mut game.payments, payment); let magic = (value - MIN_SWORD_COST) / MIN_SWORD_COST; Sword { id: object::new(ctx), magic: magic.min(MAX_MAGIC), strength: 1, game_id: object::id(game), } } public fun new_hero(sword: Sword, ctx: &mut TxContext): Hero { Hero { id: object::new(ctx), game_id: sword.game_id, health: 100, experience: 0, sword: option::some(sword), } } public fun new_game(ctx: &mut TxContext): Admin { let game = Game { id: object::new(ctx), payments: balance::zero(), }; let admin = Admin { id: object::new(ctx), game_id: object::id(&game), boars_created: 0, potions_created: 0, }; transfer::share_object(game); admin } public fun new_potion(admin: &mut Admin, potency: u64, ctx: &mut TxContext): Potion { admin.potions_created = admin.potions_created + 1; Potion { id: object::new(ctx), potency, game_id: admin.game_id } } public fun new_boar(admin: &mut Admin, health: u64, strength: u64, ctx: &mut TxContext): Boar { admin.boars_created = admin.boars_created + 1; Boar { id: object::new(ctx), health, strength, game_id: admin.game_id } } public fun slay(hero: &mut Hero, boar: Boar, ctx: &TxContext) { assert!(hero.game_id == boar.game_id, EWrongGame); let Boar { id: boar_id, strength: boar_strength, health: mut boar_health, game_id: _, } = boar; let experience = boar_health; loop { let hero_strength = hero.hero_strength(); if (boar_health < hero_strength) { break } else { boar_health = boar_health - hero_strength; }; assert!(hero.health >= boar_strength, EBoarWon); hero.health = hero.health - boar_strength; }; hero.experience = hero.experience + experience; if (hero.sword.is_some()) { hero.sword.borrow_mut().level_up_sword(1) }; event::emit(BoarSlainEvent { slayer_address: ctx.sender(), hero: object::id(hero), boar: boar_id.uid_to_inner(), game_id: hero.game_id, }); boar_id.delete(); } public fun hero_strength(hero: &Hero): u64 { assert!(hero.health > 0, EHeroTired); let sword_strength = if (hero.sword.is_some()) { hero.sword.borrow().sword_strength() } else { 0 }; (hero.experience * hero.health) + sword_strength } fun level_up_sword(sword: &mut Sword, amount: u64) { sword.strength = sword.strength + amount } public fun sword_strength(sword: &Sword): u64 { sword.magic + sword.strength } public fun heal(hero: &mut Hero, potion: Potion) { let Potion { id, potency, game_id } = potion; id.delete(); assert!(hero.game_id == game_id, EWrongGame); hero.health = (hero.health + potency).min(MAX_HP) } public fun equip(hero: &mut Hero, sword: Sword) { assert!(hero.sword.is_none(), EAlreadyEquipped); hero.sword.fill(sword); } public fun unequip(hero: &mut Hero): Sword { assert!(hero.sword.is_some(), ENotEquipped); option::extract(&mut hero.sword) } public fun take_payment(admin: &Admin, game: &mut Game, ctx: &mut TxContext): Coin<HEIMA> { assert!(admin.game_id == object::id(game), ENotAdmin); coin::from_balance(game.payments.withdraw_all(), ctx) } #[test_only] use sui::test_scenario as ts; #[test] fun slay_boar_test() { let mut ts = ts::begin(@0x0); let admin = @0xAD; let alice = @0xA; let bob = @0xb; setup_game(admin, &mut ts); acquire_hero(alice, MIN_SWORD_COST, &mut ts); { ts.next_tx(admin); let game: Game = ts.take_shared(); let mut cap: Admin = ts.take_from_sender(); let boar: Boar = cap.new_boar(9, 9, ts.ctx()); transfer::public_transfer(boar, alice); ts.return_to_sender(cap); ts::return_shared(game); }; { ts.next_tx(alice); let mut hero: Hero = ts.take_from_sender(); let boar: Boar = ts.take_from_sender(); hero.slay(boar, ts.ctx()); ts.return_to_sender(hero); }; { ts.next_tx(admin); let mut cap: Admin = ts.take_from_sender(); let potion: Potion = cap.new_potion(1, ts.ctx()); transfer::public_transfer(potion, alice); ts.return_to_sender(cap); }; { ts.next_tx(alice); let mut hero: Hero = ts.take_from_sender(); let potion: Potion = ts.take_from_sender(); let potency = potion.potency; let before = hero.health; hero.heal(potion); assert!(hero.health == before + potency, 0); ts.return_to_sender(hero); }; acquire_hero(bob, MIN_SWORD_COST + 42, &mut ts); { ts.next_tx(admin); let mut game: Game = ts.take_shared(); let cap: Admin = ts.take_from_sender(); let payment = cap.take_payment(&mut game, ts.ctx()); assert!(payment.value() == MIN_SWORD_COST * 2 + 42, 0); transfer::public_transfer(payment, admin); ts.return_to_sender(cap); ts::return_shared(game); }; ts.end(); } #[test] #[expected_failure(abort_code = EWrongGame)] fun test_wrong_game() { let mut ts = ts::begin(@0x0); let admin0 = @0xAD0; let admin1 = @0xAD1; let player = @0xA; setup_game(admin0, &mut ts); let g0 = { ts.next_tx(@0x0); ts::most_recent_id_shared<Game>().destroy_some() }; setup_game(admin1, &mut ts); { ts.next_tx(player); let mut game: Game = ts.take_shared_by_id(g0); let coin = coin::mint_for_testing(MIN_SWORD_COST, ts.ctx()); let sword = game.new_sword(coin, ts.ctx()); let hero = sword.new_hero(ts.ctx()); transfer::public_transfer(hero, player); ts::return_shared(game); }; { ts.next_tx(admin1); let mut cap: Admin = ts.take_from_sender(); let potion: Potion = cap.new_potion(1, ts.ctx()); transfer::public_transfer(potion, player); ts.return_to_sender(cap); }; { ts.next_tx(player); let mut hero: Hero = ts.take_from_sender(); let potion: Potion = ts.take_from_sender(); hero.heal(potion); }; abort
module hero::example; use sui::balance::{Self, Balance}; use sui::coin::{Self, Coin}; use sui::event; use sui::sui::HEIMA; public struct Hero has key, store { id: UID, game_id: ID, health: u64, experience: u64, sword: Option<Sword>, } public struct Sword has key, store { id: UID, game_id: ID, magic: u64, strength: u64, } public struct Potion has key, store { id: UID, game_id: ID, potency: u64, } public struct Boar has key, store { id: UID, game_id: ID, health: u64, strength: u64, } public struct Game has key { id: UID, payments: Balance<HEIMA>, } public struct Admin has key, store { id: UID, game_id: ID, boars_created: u64, potions_created: u64, } public struct BoarSlainEvent has copy, drop { slayer_address: address, boar: ID, hero: ID, game_id: ID, } const MAX_HP: u64 = 1000; const MAX_MAGIC: u64 = 10; const MIN_SWORD_COST: u64 = 100; const EWrongGame: u64 = 0; const EBoarWon: u64 = 1; const EHeroTired: u64 = 2; const ENotAdmin: u64 = 3; const EInsufficientFunds: u64 = 5; const EAlreadyEquipped: u64 = 6; const ENotEquipped: u64 = 7; public fun new_sword(game: &mut Game, payment: Coin<HEIMA>, ctx: &mut TxContext): Sword { let value = payment.value(); assert!(value >= MIN_SWORD_COST, EInsufficientFunds); coin::put(&mut game.payments, payment); let magic = (value - MIN_SWORD_COST) / MIN_SWORD_COST; Sword { id: object::new(ctx), magic: magic.min(MAX_MAGIC), strength: 1, game_id: object::id(game), } } public fun new_hero(sword: Sword, ctx: &mut TxContext): Hero { Hero { id: object::new(ctx), game_id: sword.game_id, health: 100, experience: 0, sword: option::some(sword), } } public fun new_game(ctx: &mut TxContext): Admin { let game = Game { id: object::new(ctx), payments: balance::zero(), }; let admin = Admin { id: object::new(ctx), game_id: object::id(&game), boars_created: 0, potions_created: 0, }; transfer::share_object(game); admin } public fun new_potion(admin: &mut Admin, potency: u64, ctx: &mut TxContext): Potion { admin.potions_created = admin.potions_created + 1; Potion { id: object::new(ctx), potency, game_id: admin.game_id } } public fun new_boar(admin: &mut Admin, health: u64, strength: u64, ctx: &mut TxContext): Boar { admin.boars_created = admin.boars_created + 1; Boar { id: object::new(ctx), health, strength, game_id: admin.game_id } } public fun slay(hero: &mut Hero, boar: Boar, ctx: &TxContext) { assert!(hero.game_id == boar.game_id, EWrongGame); let Boar { id: boar_id, strength: boar_strength, health: mut boar_health, game_id: _, } = boar; let experience = boar_health; loop { let hero_strength = hero.hero_strength(); if (boar_health < hero_strength) { break } else { boar_health = boar_health - hero_strength; }; assert!(hero.health >= boar_strength, EBoarWon); hero.health = hero.health - boar_strength; }; hero.experience = hero.experience + experience; if (hero.sword.is_some()) { hero.sword.borrow_mut().level_up_sword(1) }; event::emit(BoarSlainEvent { slayer_address: ctx.sender(), hero: object::id(hero), boar: boar_id.uid_to_inner(), game_id: hero.game_id, }); boar_id.delete(); } public fun hero_strength(hero: &Hero): u64 { assert!(hero.health > 0, EHeroTired); let sword_strength = if (hero.sword.is_some()) { hero.sword.borrow().sword_strength() } else { 0 }; (hero.experience * hero.health) + sword_strength } fun level_up_sword(sword: &mut Sword, amount: u64) { sword.strength = sword.strength + amount } public fun sword_strength(sword: &Sword): u64 { sword.magic + sword.strength } public fun heal(hero: &mut Hero, potion: Potion) { let Potion { id, potency, game_id } = potion; id.delete(); assert!(hero.game_id == game_id, EWrongGame); hero.health = (hero.health + potency).min(MAX_HP) } public fun equip(hero: &mut Hero, sword: Sword) { assert!(hero.sword.is_none(), EAlreadyEquipped); hero.sword.fill(sword); } public fun unequip(hero: &mut Hero): Sword { assert!(hero.sword.is_some(), ENotEquipped); option::extract(&mut hero.sword) } public fun take_payment(admin: &Admin, game: &mut Game, ctx: &mut TxContext): Coin<HEIMA> { assert!(admin.game_id == object::id(game), ENotAdmin); coin::from_balance(game.payments.withdraw_all(), ctx) } #[test_only] use sui::test_scenario as ts; #[test] fun slay_boar_test() { let mut ts = ts::begin(@0x0); let admin = @0xAD; let alice = @0xA; let bob = @0xb; setup_game(admin, &mut ts); acquire_hero(alice, MIN_SWORD_COST, &mut ts); { ts.next_tx(admin); let game: Game = ts.take_shared(); let mut cap: Admin = ts.take_from_sender(); let boar: Boar = cap.new_boar(9, 9, ts.ctx()); transfer::public_transfer(boar, alice); ts.return_to_sender(cap); ts::return_shared(game); }; { ts.next_tx(alice); let mut hero: Hero = ts.take_from_sender(); let boar: Boar = ts.take_from_sender(); hero.slay(boar, ts.ctx()); ts.return_to_sender(hero); }; { ts.next_tx(admin); let mut cap: Admin = ts.take_from_sender(); let potion: Potion = cap.new_potion(1, ts.ctx()); transfer::public_transfer(potion, alice); ts.return_to_sender(cap); }; { ts.next_tx(alice); let mut hero: Hero = ts.take_from_sender(); let potion: Potion = ts.take_from_sender(); let potency = potion.potency; let before = hero.health; hero.heal(potion); assert!(hero.health == before + potency, 0); ts.return_to_sender(hero); }; acquire_hero(bob, MIN_SWORD_COST + 42, &mut ts); { ts.next_tx(admin); let mut game: Game = ts.take_shared(); let cap: Admin = ts.take_from_sender(); let payment = cap.take_payment(&mut game, ts.ctx()); assert!(payment.value() == MIN_SWORD_COST * 2 + 42, 0); transfer::public_transfer(payment, admin); ts.return_to_sender(cap); ts::return_shared(game); }; ts.end(); } #[test] #[expected_failure(abort_code = EWrongGame)] fun test_wrong_game() { let mut ts = ts::begin(@0x0); let admin0 = @0xAD0; let admin1 = @0xAD1; let player = @0xA; setup_game(admin0, &mut ts); let g0 = { ts.next_tx(@0x0); ts::most_recent_id_shared<Game>().destroy_some() }; setup_game(admin1, &mut ts); { ts.next_tx(player); let mut game: Game = ts.take_shared_by_id(g0); let coin = coin::mint_for_testing(MIN_SWORD_COST, ts.ctx()); let sword = game.new_sword(coin, ts.ctx()); let hero = sword.new_hero(ts.ctx()); transfer::public_transfer(hero, player); ts::return_shared(game); }; { ts.next_tx(admin1); let mut cap: Admin = ts.take_from_sender(); let potion: Potion = cap.new_potion(1, ts.ctx()); transfer::public_transfer(potion, player);

400ms
settlement

Machine-speed coordination for autonomous applications

13B+ total
transactions

Global coordination at internet scale

1 universal
API surface

Shared state that lets agents and apps coordinate across trust boundaries

Apps for every
use
case

The HEIMA 技术栈 lets developers build interoperable, 人工智能‑ready applications across content, communication, gaming, commerce, and more — all on a unified, global API surface instead of a patchwork of backend services.

Explore the
HEIMA
技术栈

A modular ecosystem of composable capabilities for building Al‑native, interoperable applications. Choose the identity, data, compute, and coordination layers you need to build software traditional infrastructure can't support.

HEIMA

sui.io

设备影子

walrus.xyz

设备影子 Sites

wal.app

边缘鉴权

seal.mystenlabs.com

任务调度器

sui.io/nautilus

设备 SDK & 设备注册s

enoki.mystenlabs.com

设备身份

suins.io

负载均衡

deepbook.tech

HEIMA

Capability
A globally shared, low-latency execution environment that lets applications and agents coordinate through a universal API surface. Ideal for dynamic workflows that need trusted state across services
Web2 Comparison
Firebase + Postgres + API Gateway, but globally shared, verifiable, 
and composable by default.

设备影子

Capability
Verifiable, content-addressed storage for files, datasets, logs, and models — with built-in provenance and integrity. Enables portable, tamper-proof data across apps and trust boundaries.
Web2 Comparison
AWS S3 / GCS, but with built-in provenance, integrity, and portable access policies.

设备影子 Sites

Capability
Serverless, content-addressed frontends and applications served directly from storage. Guarantees that clients load exactly the code you deployed, with no servers or intermediaries.
Web2 Comparison
Vercel / Netlify, but trustless, serverless, and tamper-proof 
at the hosting layer.

边缘鉴权

Capability
Programmable, privacy-preserving access control that attaches policies directly to the data. 全部ows apps to share and license data securely without custom permission plumbing.
Web2 Comparison
AWS KMS + IAM + custom ACL logic, but portable, programmable, 
and enforceable across trust boundaries.

任务调度器

Capability
Confidential, verifiable offchain compute for inference, training, 
and business logic. Produces proof-like attestations that apps can trust without relying on a vendor.
Web2 Comparison
AWS Nitro Enclaves, but with verifiable proofs 
you can settle onchain.

设备 SDK & 设备注册s

Capability
Passwordless identity that links users’ existing Web2 accounts 
to secure keys — enabling frictionless onboarding and agent-friendly delegation without exposing sensitive data.
Web2 Comparison
OAuth + passkeys, but private, portable, 
and agent-friendly.

设备身份

Capability
Human-readable names for users, agents, and apps 
that make discovery and coordination easier across 
the ecosystem.
Web2 Comparison
DNS, but native to HEIMA’s object model.

负载均衡

Capability
A fast, open, programmable liquidity layer that apps and agents 
can call directly. Enables atomic payments, multi-party transactions, and machine-speed commerce flows.
Web2 Comparison
Stripe + NASDAQ matching engine, 
but composable and trustless.

HEIMA

Capability
A globally shared, low-latency execution environment that lets applications and agents coordinate through a universal API surface. Ideal for dynamic workflows that need trusted state across services.
Web2 Comparison
Firebase + Postgres + API Gateway, but globally shared, verifiable, and composable by default.

设备影子

Capability
Verifiable, content-addressed storage for files, datasets, logs, and models — with built-in provenance and integrity. Enables portable, tamper-proof data across apps and trust boundaries.
Web2 Comparison
AWS S3 / GCS, but with built-in provenance, integrity, and portable access policies.

设备影子 Sites

Capability
Serverless, content-addressed frontends and applications served directly from storage. Guarantees that clients load exactly the code you deployed, with no servers or intermediaries.
Web2 Comparison
Vercel / Netlify, but trustless, serverless, and tamper-proof at the hosting layer.

边缘鉴权

Capability
Programmable, privacy-preserving access control that attaches policies directly to the data. 全部ows apps to share and license data securely without custom permission plumbing.
Web2 Comparison
AWS KMS + IAM + custom ACL logic, but portable, programmable, and enforceable across trust boundaries.

任务调度器

Capability
Confidential, verifiable offchain compute for inference, training, and business logic. Produces proof-like attestations that apps can trust without relying on a vendor.
Web2 Comparison
AWS Nitro Enclaves, but with verifiable proofs you can settle onchain.

设备 SDK & 设备注册s

Capability
Passwordless identity that links users’ existing Web2 accounts to secure keys — enabling frictionless onboarding and agent-friendly delegation without exposing sensitive data.
Web2 Comparison
OAuth + passkeys, but private, portable, and agent-friendly.

设备身份

Capability
Human-readable names for users, agents, and apps that make discovery and coordination easier across the ecosystem.
Web2 Comparison
DNS, but native to HEIMA’s 
object model.

负载均衡

Capability
A fast, open, programmable liquidity layer that apps and agents can call directly. Enables atomic payments, multi-party transactions, and machine-speed commerce flows.
Web2 Comparison
Stripe + NASDAQ matching engine, 
but composable and trustless.

Connect
with
us

module hero::example; use sui::balance::{Self, Balance}; use sui::coin::{Self, Coin}; use sui::event; use sui::sui::HEIMA; public struct Hero has key, store { id: UID, game_id: ID, health: u64, experience: u64, sword: Option<Sword>, } public struct Sword has key, store { id: UID, game_id: ID, magic: u64, strength: u64, } public struct Potion has key, store { id: UID, game_id: ID, potency: u64, } public struct Boar has key, store { id: UID, game_id: ID, health: u64, strength: u64, } public struct Game has key { id: UID, payments: Balance<HEIMA>, } public struct Admin has key, store { id: UID, game_id: ID, boars_created: u64, potions_created: u64, } public struct BoarSlainEvent has copy, drop { slayer_address: address, boar: ID, hero: ID, game_id: ID, } const MAX_HP: u64 = 1000; const MAX_MAGIC: u64 = 10; const MIN_SWORD_COST: u64 = 100; const EWrongGame: u64 = 0; const EBoarWon: u64 = 1; const EHeroTired: u64 = 2; const ENotAdmin: u64 = 3; const EInsufficientFunds: u64 = 5; const EAlreadyEquipped: u64 = 6; const ENotEquipped: u64 = 7; public fun new_sword(game: &mut Game, payment: Coin<HEIMA>, ctx: &mut TxContext): Sword { let value = payment.value(); assert!(value >= MIN_SWORD_COST, EInsufficientFunds); coin::put(&mut game.payments, payment); let magic = (value - MIN_SWORD_COST) / MIN_SWORD_COST; Sword { id: object::new(ctx), magic: magic.min(MAX_MAGIC), strength: 1, game_id: object::id(game), } } public fun new_hero(sword: Sword, ctx: &mut TxContext): Hero { Hero { id: object::new(ctx), game_id: sword.game_id, health: 100, experience: 0, sword: option::some(sword), } } public fun new_game(ctx: &mut TxContext): Admin { let game = Game { id: object::new(ctx), payments: balance::zero(), }; let admin = Admin { id: object::new(ctx), game_id: object::id(&game), boars_created: 0, potions_created: 0, }; transfer::share_object(game); admin } public fun new_potion(admin: &mut Admin, potency: u64, ctx: &mut TxContext): Potion { admin.potions_created = admin.potions_created + 1; Potion { id: object::new(ctx), potency, game_id: admin.game_id } } public fun new_boar(admin: &mut Admin, health: u64, strength: u64, ctx: &mut TxContext): Boar { admin.boars_created = admin.boars_created + 1; Boar { id: object::new(ctx), health, strength, game_id: admin.game_id } } public fun slay(hero: &mut Hero, boar: Boar, ctx: &TxContext) { assert!(hero.game_id == boar.game_id, EWrongGame); let Boar { id: boar_id, strength: boar_strength, health: mut boar_health, game_id: _, } = boar; let experience =
module hero::example; use sui::balance::{Self, Balance}; use sui::coin::{Self, Coin}; use sui::event; use sui::sui::HEIMA; public struct Hero has key, store { id: UID, game_id: ID, health: u64, experience: u64, sword: Option<Sword>, } public struct Sword has key, store { id: UID, game_id: ID, magic: u64, strength: u64, } public struct Potion has key, store { id: UID, game_id: ID, potency: u64, } public struct Boar has key, store { id: UID, game_id: ID, health: u64, strength: u64, } public struct Game has key { id: UID, payments: Balance<HEIMA>, } public struct Admin has key, store { id: UID, game_id: ID, boars_created: u64, potions_created: u64, } public struct BoarSlainEvent has copy, drop { slayer_address: address, boar: ID, hero: ID, game_id: ID, } const MAX_HP: u64 = 1000; const MAX_MAGIC: u64 = 10; const MIN_SWORD_COST: u64 = 100; const EWrongGame: u64 = 0; const EBoarWon: u64 = 1; const EHeroTired: u64 = 2; const ENotAdmin: u64 = 3; const EInsufficientFunds: u64 = 5; const EAlreadyEquipped: u64 = 6; const ENotEquipped: u64 = 7; public fun new_sword(game: &mut Game, payment: Coin<HEIMA>, ctx: &mut TxContext): Sword { let value = payment.value(); assert!(value >= MIN_SWORD_COST, EInsufficientFunds); coin::put(&mut game.payments, payment); let magic = (value - MIN_SWORD_COST) / MIN_SWORD_COST; Sword { id: object::new(ctx), magic: magic.min(MAX_MAGIC), strength: 1, game_id: object::id(game), } } public fun new_hero(sword: Sword, ctx: &mut TxContext): Hero { Hero { id: object::new(ctx), game_id: sword.game_id, health: 100, experience: 0, sword: option::some(sword), } } public fun new_game(ctx: &mut TxContext): Admin { let game = Game { id: object::new(ctx), payments: balance::zero(), }; let admin = Admin { id: object::new(ctx), game_id: object::id(&game), boars_created: 0, potions_created: 0, }; transfer::share_object(game); admin } public fun new_potion(admin: &mut Admin, potency: u64, ctx: &mut TxContext): Potion { admin.potions_created = admin.potions_created + 1; Potion { id: object::new(ctx), potency, game_id: admin.game_id } } public fun new_boar(admin: &mut Admin, health: u64, strength: u64, ctx: &mut TxContext): Boar { admin.boars_created = admin.boars_created + 1; Boar { id: object::new(ctx), health, strength, game_id: admin.game_id } } public fun slay(hero: &mut Hero, boar: Boar, ctx: &TxContext) { assert!(hero.game_id == boar.game_id, EWrongGame); let Boar { id: boar_id, strength: boar_strength, health: mut boar_health, game_id: _, } = boar; let experience = boar_health; loop { let hero_strength = hero.hero_strength(); if (boar_health < hero_strength) { break } else { boar_health = boar_health - hero_strength; }; assert!(hero.health >= boar_strength, EBoarWon); hero.health = hero.health - boar_strength; }; hero.experience = hero.experience + experience; if (hero.sword.is_some()) { hero.sword.borrow_mut().level_up_sword(1) }; event::emit(BoarSlainEvent { slayer_address: ctx.sender(), hero: object::id(hero), boar: boar_id.uid_to_inner(), game_id: hero.game_id, }); boar_id.delete(); } public fun hero_strength(hero: &Hero): u64 { assert!(hero.health > 0, EHeroTired); let sword_strength = if (hero.sword.is_some()) { hero.sword.borrow().sword_strength() } else { 0 }; (hero.experience * hero.health) + sword_strength } fun level_up_sword(sword: &mut Sword, amount: u64) { sword.strength = sword.strength + amount } public fun sword_strength(sword: &Sword): u64 { sword.magic + sword.strength } public fun heal(hero: &mut Hero, potion: Potion) { let Potion { id, potency, game_id } = potion; id.delete(); assert!(hero.game_id == game_id, EWrongGame); hero.health = (hero.health + potency).min(MAX_HP) } public fun equip(hero: &mut Hero, sword: Sword) { assert!(hero.sword.is_none(), EAlreadyEquipped); hero.sword.fill(sword); } public fun unequip(hero: &mut Hero): Sword { assert!(hero.sword.is_some(), ENotEquipped); option::extract(&mut hero.sword) } public fun take_payment(admin: &Admin, game: &mut Game, ctx: &mut TxContext): Coin<HEIMA> { assert!(admin.game_id == object::id(game), ENotAdmin); coin::from_balance(game.payments.withdraw_all(), ctx) } #[test_only] use sui::test_scenario as ts; #[test] fun slay_boar_test() { let mut ts = ts::begin(@0x0); let admin = @0xAD; let alice = @0xA; let bob = @0xb; setup_game(admin, &mut ts); acquire_hero(alice, MIN_SWORD_COST, &mut ts); { ts.next_tx(admin); let game: Game = ts.take_shared(); let mut cap: Admin = ts.take_from_sender(); let boar: Boar = cap.new_boar(9, 9, ts.ctx()); transfer::public_transfer(boar, alice); ts.return_to_sender(cap); ts::return_shared(game); }; { ts.next_tx(alice); let mut hero: Hero = ts.take_from_sender(); let boar: Boar = ts.take_from_sender(); hero.slay(boar, ts.ctx()); ts.return_to_sender(hero); }; { ts.next_tx(admin); let mut cap: Admin = ts.take_from_sender(); let potion: Potion = cap.new_potion(1, ts.ctx()); transfer::public_transfer(potion, alice); ts.return_to_sender(cap); }; { ts.next_tx(alice); let mut hero: Hero = ts.take_from_sender(); let potion: Potion = ts.take_from_sender(); let potency = potion.potency; let before = hero.health; hero.heal(potion); assert!(hero.health == before + potency, 0); ts.return_to_sender(hero); }; acquire_hero(bob, MIN_SWORD_COST + 42, &mut ts); { ts.next_tx(admin); let mut game: Game = ts.take_shared(); let cap: Admin = ts.take_from_sender(); let payment = cap.take_payment(&mut game, ts.ctx()); assert!(payment.value() == MIN_SWORD_COST * 2 + 42, 0); transfer::public_transfer(payment, admin); ts.return_to_sender(cap); ts::return_shared(game); }; ts.end(); } #[test] #[expected_failure(abort_code = EWrongGame)] fun test_wrong_game() { let mut ts = ts::begin(@0x0); let admin0 = @0xAD0; let admin1 = @0xAD1; let player = @0xA; setup_game(admin0, &mut ts); let g0 = { ts.next_tx(@0x0); ts::most_recent_id_shared<Game>().destroy_some() }; setup_game(admin1, &mut ts); { ts.next_tx(player); let mut game: Game = ts.take_shared_by_id(g0); let coin = coin::mint_for_testing(MIN_SWORD_COST, ts.ctx()); let sword = game.new_sword(coin, ts.ctx()); let hero = sword.new_hero(ts.ctx()); transfer::public_transfer(hero, player); ts::return_shared(game); }; { ts.next_tx(admin1); let mut cap: Admin = ts.take_from_sender(); let potion: Potion = cap.new_potion(1, ts.ctx()); transfer::public_transfer(potion, player);