# 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
```