# Not A Bird ## Interfaces ### PSP-22 ```plantuml @startuml interface PSP22 { + total_supply() -> u128 + balance_of(owner: AccountId) -> u128 + allowance(owner: AccountId, spender: AccountId) -> u128 + transfer(to: AccountId, value: u128, data: Vec<u8>) -> (), PSP22Error + transfer_from(from: AccountId, to: AccountId, value: u128, data: Vec<u8>) -> (), PSP22Error + approve(spender: AccountId, value: u128) -> (), PSP22Error + increase_allowance(spender: AccountId, delta_value: u128) -> (), PSP22Error + decrease_allowance(spender: AccountId, delta_value: u128) -> (), PSP22Error } ``` ### PSP-22 Metadata ```plantuml @startuml interface PSP22Metadata { + token_name() -> Option<String> + token_symbol() -> Option<String> + token_decimals() -> u8 } ``` _Implementation notes_ * `token_decimals()` should return `0` in all cases * `token_symbol()` should return `MWATER` for _water_, `MWIND` for _wind_, etc. * `token_name()` should return the name of the element ### Element Contract _Extends_ `PSP-22`, `PSP-22 Metadata` ```plantuml interface PSP22 { + total_supply() -> u128 + balance_of(owner: AccountId) -> u128 + allowance(owner: AccountId, spender: AccountId) -> u128 + transfer(to: AccountId, value: u128, data: Vec<u8>) -> (), PSP22Error + transfer_from(from: AccountId, to: AccountId, value: u128, data: Vec<u8>) -> (), PSP22Error + approve(spender: AccountId, value: u128) -> (), PSP22Error + increase_allowance(spender: AccountId, delta_value: u128) -> (), PSP22Error + decrease_allowance(spender: AccountId, delta_value: u128) -> (), PSP22Error } interface PSP22Mintable { + mint(owner: AccountId, value: u128) -> (), PSP22Error } interface PSP22Burnable { + burn(owner: AccountId, value: u128) -> (), PSP22Error } interface PSP22Metadata { + token_name() -> Option<String> + token_symbol() -> Option<String> + token_decimals() -> u8 } interface ElementContract { + lock_game_contract(contract: AccountId) -> (), ElementContractError + claim_ownership() -> () } PSP22 <|.. ElementContract : <<extends>> PSP22Mintable <|.. ElementContract : <<extends>> PSP22Burnable <|.. ElementContract : <<extends>> PSP22Metadata <|.. ElementContract : <<extends>> ``` _Implementation notes_ * By calling `lock_game_contract` we set the address of the game contract for calls * Should we investigate taking this as constructor parameter? * In order the call `lock_game_contract` the caller has to be the **contract owner** (which is claimed _once_ by anybody right after deployment via `claim_ownership()`) * `mint` and `burn` may only be called by the game contract * When calling `mint`, we emit a `PSP22 Transfer` event with `None` sender, and also increase total supply * When calling `burn`, we emit a `PSP22 Transfer` event with `None` recipient, and also decrease total supply ### Game Contract ```plantuml interface GameContract { + num_elements() -> u32 + num_recipes() -> u32 + recipe_name(recipe_index: u32) -> Option<String> + buy_offer() -> (u128, u128) + buy(element_index: u32) -> (), GameContractError + sacrifice(element_index: u32, value: u128) -> (), GameContractError + claim_ownership() -> (), GameContractError + lock_element_contract(element_index: u32, contract: AccountId) -> (), GameContractError + set_buy_offer(native_tokens: u128, reward_tier_points: u128) -> (), GameContractError + craft(element_a: u32, element_b: u32) -> (), GameContractError } class GameContractImpl {} GameContract <|-- GameContractImpl : <<extends>> ``` ## Contracts ### Crafting a new item ```plantuml @startuml actor "Alice" as AliceUser actor "Water : PSP22" as WaterContract actor "Fire : PSP22" as FireContract actor "Steam : PSP22" as SteamContract actor "Game" as GameContract AliceUser -> GameContract : craft(recipe = create_steam) activate GameContract GameContract -> WaterContract : burn(account = /Alice, amount = 1) activate WaterContract return OK GameContract -> FireContract : burn(account = /Alice, amount = 1) activate FireContract return OK GameContract -> SteamContract : mint(account = /Alice, amount = 1) activate SteamContract return OK return OK @enduml ``` ### Buying a new item ```plantuml @startuml actor "Alice" as AliceUser actor "Water : PSP22" as WaterContract actor "Game : PSP22" as GameContract AliceUser -> WaterContract : balance_of(account = /Alice) activate WaterContract return 13 AliceUser -> GameContract : buy_price_of(item = water) activate GameContract return (1, 100) AliceUser -> GameContract : buy(item = water), transfered_value = 1 activate GameContract GameContract -> WaterContract : mint(account = /Alice, amount = 100) activate WaterContract return OK return OK AliceUser -> WaterContract : balance_of(account = /Alice) activate WaterContract return 113 @enduml ``` ### Alice starts the game ```plantuml @startuml actor "Alice" as AliceUser actor "Water : PSP22" as WaterContract actor "Fire : PSP22" as FireContract actor "Earth : PSP22" as EarthContract actor "Wind : PSP22" as WindContract actor "Game" as GameContract AliceUser -> GameContract : buy(item) activate GameContract return (1, 4) Alice -> GameContract : start_game(), transfered_value = 4 activate GameContract GameContract -> WaterContract : mint(account = /Alice, amount = 4) activate WaterContract return OK GameContract -> FireContract : mint(account = /Alice, amount = 5) activate FireContract return OK GameContract -> EarthContract : mint(account = /Alice, amount = 6) activate EarthContract return OK GameContract -> WindContract : mint(account = /Alice, amount = 7) activate WindContract return OK return OK @enduml ``` ### Alice contributes to the common pot ```plantuml @startuml actor "Alice" as AliceUser actor "Bob" as BobUser actor "Game" as GameContract actor "Steam : PSP22" as SteamContract AliceUser -> GameContract : contribute(resource = steam, amount = 2) activate GameContract GameContract -> SteamContract : burn(account = /Alice, amount = 2) activate SteamContract return OK alt pot full GameContract -> GameContract : distribute_rewards() activate GameContract GameContract -> BobUser : send_native_tokens(123) activate BobUser return OK GameContract -> GameContract : new_turn() activate GameContract return OK end return OK @enduml ``` ### Alice tries to craft a new item with insufficient tokens ```plantuml @startuml actor "Alice" as AliceUser actor "Water : PSP22" as WaterContract actor "Fire : PSP22" as FireContract actor "Steam : PSP22" as SteamContract actor "Game" as GameContract AliceUser -> WaterContract : balance_of(account = /Alice) activate WaterContract return 0 AliceUser -> FireContract : balance_of(account = /Alice) activate FireContract return 1 AliceUser -> GameContract : craft(recipe = create_steam) activate GameContract return GameError::InsufficientItemTokensForCraftingError return OK @enduml ``` ### Alice buys, but does not transfer exact amount ```plantuml @startuml actor "Alice" as AliceUser actor "Water : PSP22" as WaterContract actor "Game" as GameContract AliceUser -> WaterContract : balance_of(account = /Alice) activate WaterContract return 13 AliceUser -> GameContract : buy_price_of(item = water) activate GameContract return (0.1, 4) AliceUser -> GameContract : buy(item = water), transfered_value = 0.298315 activate GameContract GameContract -> WaterContract : mint(account = /Alice, amount = 8) activate WaterContract return OK return OK AliceUser -> WaterContract : balance_of(account = /Alice) activate WaterContract return 21 @enduml ``` ### Alice buys for less than price ```plantuml @startuml actor "Alice" as AliceUser actor "Water : PSP22" as WaterContract actor "Game" as GameContract AliceUser -> WaterContract : balance_of(account = /Alice) activate WaterContract return 13 AliceUser -> GameContract : buy_price_of(item = water) activate GameContract return (0.1, 4) AliceUser -> GameContract : buy(item = water), transfered_value = 0.0999999 activate GameContract return GameError::InsufficientTransferedValueError AliceUser -> WaterContract : balance_of(account = /Alice) activate WaterContract return 13 @enduml ``` ### Alice tries to directly mint ```plantuml @startuml actor "Alice" as AliceUser actor "Water : PSP22" as WaterContract AliceUser -> WaterContract : mint(account = /Alice, amount = 99999) activate WaterContract return GameError::OnlyGameContractMayMintError @enduml ``` ### Alice tries to directly burn ```plantuml @startuml actor "Alice" as AliceUser actor "Water : PSP22" as WaterContract AliceUser -> WaterContract : burn(account = /Bob, amount = 99999) activate WaterContract return GameError::OnlyGameContractMayBurnError @enduml ```