Skip to content

Commit

Permalink
add permanent_state for ord
Browse files Browse the repository at this point in the history
  • Loading branch information
pause125 committed Apr 23, 2024
1 parent 5e7cb44 commit 1655ece
Show file tree
Hide file tree
Showing 3 changed files with 308 additions and 44 deletions.
171 changes: 138 additions & 33 deletions examples/bitcoin_plants/sources/plants.move
Original file line number Diff line number Diff line change
Expand Up @@ -22,75 +22,180 @@
/// which has no more relationship with the seed and the plant.
module bitcoin_plants::plants {

use std::string;
use std::vector;
use moveos_std::tx_context;
use moveos_std::object::{Self, Object};
use rooch_framework::coin::{Self, Coin, CoinInfo};
use rooch_framework::account_coin_store;
use rooch_framework::timestamp;
use bitcoin_move::utxo::{Self, UTXO};
use bitcoin_move::ord::Inscription;
use bitcoin_move::ord::{Self, Inscription};

const ErrorAlreadyStaked: u64 = 1;
const ErrorAlreadyClaimed: u64 = 2;
const ErrorNotSeed: u64 = 0;
const ErrorAlreadyPlanted: u64 = 1;
const ErrorNotPlanted: u64 = 2;
const ErrorWateringTooFrequently: u64 = 3;
const ErrorPlantDead: u64 = 4;

const WATERING_INTERVAL: u64 = 60 * 60 * 24; // 1 day

/// The fruit NFT.
struct Fruit has key, store {
// TODO: maybe SFT is more suitable to represent the fruits.
struct Fruits has key, store {
variety: u64,
value: u64,
}

/// The plant.
/// Recording the plant's status and will be stored in the permanent state area of the UTXO
/// Recording the plant's status and will be stored in the permanent state area of the Inscription.
struct Plant has store {
/// The variety of the plant. The type variety is inferred from the seed Inscription.
variety: u64,
/// The growth value of the plant. The value will be updated by watering actions.
/// All status can be inferred from the growth value.
growth_value: u64,
status: u8,
/// The health of the plant. Health will decrease if the plant is not watered.
/// The health will be updated by watering actions.
/// Health value ranges in [0,100], if health is 0, the plant is dead.
health: u8,
/// The last time that the plant has been watered, in seconds.
last_watering_time: u64,
/// The number of fruits that are available to be picked.
pickable_fruits: u64,
/// The number of fruits that have been picked.
picked_fruits: u64,
}

fun init() {
let coin_info_obj = coin::register_extend<HDC>(
string::utf8(b"BTC Holder Coin"),
string::utf8(b"HDC"),
DECIMALS,
);
let coin_info_holder_obj = object::new_named_object(CoinInfoHolder { coin_info: coin_info_obj });
// Make the coin info holder object to shared, so anyone can get mutable CoinInfoHolder object
object::to_shared(coin_info_holder_obj);
}

/// Plant with seed represented by the Inscription
// Parse the Inscription and store planting info and plant states in the UTXO's permanent state area.
public fun do_plant(seed: &mut Object<Inscription>) {
public entry fun plant(seed: &mut Object<Inscription>) {
// Parse the Inscription, check if it is a seed Inscription.
// A seed inscription has protocol `bitseed` and tick name `seed`
let inscription = object::borrow(seed);
ensure_seed_inscription(inscription);

// Check if the Inscription is already planted
assert!(!ord::contains_permanent_state<Plant>(seed), ErrorAlreadyPlanted);

// Check if the UTXO is already planted
// TODO: init the Plant from seed attributes
let plant = Plant {
variety: 0,
growth_value: 0,
health: 100,
last_watering_time: timestamp::now_seconds(),
pickable_fruits: 0,
picked_fruits: 0,
};

// Store the planting info and plant status in the UTXO's permanent state area
// Store the planting info and plant status in the Inscription's permanent state area
ord::add_permanent_state(seed, plant);
}

public fun do_water(plant: &mut Object<Inscription>) {
// Check watering actions of this plant

public entry fun water(plant: &mut Object<Inscription>) {
let plant = ord::borrow_mut_permanent_state<Plant>(plant);
let watering_interval = timestamp::now_seconds() - plant.last_watering_time;
assert!(watering_interval >= WATERING_INTERVAL, ErrorWateringTooFrequently);
// Update plant status and watering actions
plant.health = calculate_health(plant.growth_value, plant.health, watering_interval);
if (plant.health == 0) {
// The plant is dead
return
};
plant.growth_value = plant.growth_value + 1;
plant.last_watering_time = timestamp::now_seconds();

// Update fruits status
if (plant.growth_value >= 10) {
plant.pickable_fruits = (plant.growth_value - 10) / 5 + 1 - plant.picked_fruits;
}
}

public fun do_harvest(plant: &mut Object<Inscription>) {
// Check the plant's status.
public fun do_harvest(plant: &mut Object<Inscription>): vector<Fruits> {
let plant = ord::borrow_mut_permanent_state<Plant>(plant);
assert!(plant.health > 0, ErrorPlantDead);

// Harvest the plant if there are fruits
if (plant.pickable_fruits > 0) {
plant.picked_fruits = plant.picked_fruits + plant.pickable_fruits;
let fruits = Fruits {
variety: plant.variety,
value: plant.pickable_fruits
};
plant.pickable_fruits = 0;
vector[fruits]
} else {
vector[]
}
}

public entry fun harvest(plant: &mut Object<Inscription>) {
let fruits = do_harvest(plant);
if (vector::length(&fruits) > 0) {
let fruit = vector::pop_back(&mut fruits);
let obj = object::new(fruit);
object::transfer(obj, tx_context::sender());
};
vector::destroy_empty(fruits);
}

public fun is_seed(_inscription: &Inscription) : bool {
// TODO: Parse the Inscription content and check if it is a seed Inscription
true
}

/// Infer the level of plant through growth value.
public fun infer_plant_level(_growth_value: u64): u8 {
// TODO
1
}

public fun is_seed(json_map: &SimpleMap<String,String>) : bool {
let protocol_key = string::utf8(b"p");
simple_map::contains_key(json_map, &protocol_key) && \
simple_map::borrow(json_map, &protocol_key) == &string::utf8(b"bitseed") && \
simple_map::borrow(json_map, &string::utf8(b"n")) == &string::utf8(b"seed")
/// Calculate the health of the plant.
/// With longer `thirst_duration`, the health will decrease more.
/// With higher growth value, the health will decrease slower.
public fun calculate_health(_growth_value: u64, health: u8, _thirst_duration: u64): u8 {
// TODO
health
}

fun ensure_seed_inscription(inscription: &Inscription) {
assert!(is_seed(inscription), ErrorNotSeed);
}

#[test_only]
use std::option;

#[test]
fun test() {
let inscription_obj = ord::new_inscription_object_for_test(
@0x3232423,
0,
0,
0,
vector[],
option::none(),
option::none(),
vector[],
option::none(),
option::none(),
option::none(),
);

plant(&mut inscription_obj);

let i = 0u8;
loop {
timestamp::fast_forward_seconds_for_test(WATERING_INTERVAL);
water(&mut inscription_obj);
i = i + 1;
if (i == 10) break;
};

let fruits = do_harvest(&mut inscription_obj);
assert!(vector::length(&fruits) == 1, 1);
let Fruits { variety: _, value: _} = vector::pop_back(&mut fruits);
vector::destroy_empty(fruits);

let plant = ord::remove_permanent_state<Plant>(&mut inscription_obj);
let Plant { variety: _, growth_value: _, health: _, last_watering_time: _, pickable_fruits: _, picked_fruits: _ } = plant;

ord::drop_inscription_object_for_test(inscription_obj);
}
}
74 changes: 74 additions & 0 deletions frameworks/bitcoin-move/doc/ord.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,25 @@
- [Function `from_transaction_bytes`](#0x4_ord_from_transaction_bytes)
- [Function `subsidy_by_height`](#0x4_ord_subsidy_by_height)
- [Function `bind_multichain_address`](#0x4_ord_bind_multichain_address)
- [Function `add_permanent_state`](#0x4_ord_add_permanent_state)
- [Function `contains_permanent_state`](#0x4_ord_contains_permanent_state)
- [Function `borrow_permanent_state`](#0x4_ord_borrow_permanent_state)
- [Function `borrow_mut_permanent_state`](#0x4_ord_borrow_mut_permanent_state)
- [Function `remove_permanent_state`](#0x4_ord_remove_permanent_state)


<pre><code><b>use</b> <a href="">0x1::option</a>;
<b>use</b> <a href="">0x1::string</a>;
<b>use</b> <a href="">0x1::vector</a>;
<b>use</b> <a href="">0x2::bag</a>;
<b>use</b> <a href="">0x2::bcs</a>;
<b>use</b> <a href="">0x2::event</a>;
<b>use</b> <a href="">0x2::json</a>;
<b>use</b> <a href="">0x2::object</a>;
<b>use</b> <a href="">0x2::signer</a>;
<b>use</b> <a href="">0x2::simple_map</a>;
<b>use</b> <a href="">0x2::table_vec</a>;
<b>use</b> <a href="">0x2::type_info</a>;
<b>use</b> <a href="">0x3::address_mapping</a>;
<b>use</b> <a href="">0x3::bitcoin_address</a>;
<b>use</b> <a href="">0x3::multichain_address</a>;
Expand Down Expand Up @@ -165,6 +172,15 @@ How many satoshis are in "one bitcoin".



<a name="0x4_ord_PERMANENT_AREA"></a>



<pre><code><b>const</b> <a href="ord.md#0x4_ord_PERMANENT_AREA">PERMANENT_AREA</a>: <a href="">vector</a>&lt;u8&gt; = [112, 101, 114, 109, 97, 110, 101, 110, 116, 95, 97, 114, 101, 97];
</code></pre>



<a name="0x4_ord_SUBSIDY_HALVING_INTERVAL"></a>

How may blocks between halvings.
Expand Down Expand Up @@ -506,3 +522,61 @@ Block Rewards

<pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="ord.md#0x4_ord_bind_multichain_address">bind_multichain_address</a>(rooch_address: <b>address</b>, bitcoin_address_opt: <a href="_Option">option::Option</a>&lt;<a href="_BitcoinAddress">bitcoin_address::BitcoinAddress</a>&gt;)
</code></pre>



<a name="0x4_ord_add_permanent_state"></a>

## Function `add_permanent_state`



<pre><code>#[private_generics(#[S])]
<b>public</b> <b>fun</b> <a href="ord.md#0x4_ord_add_permanent_state">add_permanent_state</a>&lt;S: store&gt;(inscription: &<b>mut</b> <a href="_Object">object::Object</a>&lt;<a href="ord.md#0x4_ord_Inscription">ord::Inscription</a>&gt;, state: S)
</code></pre>



<a name="0x4_ord_contains_permanent_state"></a>

## Function `contains_permanent_state`



<pre><code><b>public</b> <b>fun</b> <a href="ord.md#0x4_ord_contains_permanent_state">contains_permanent_state</a>&lt;S: store&gt;(inscription: &<a href="_Object">object::Object</a>&lt;<a href="ord.md#0x4_ord_Inscription">ord::Inscription</a>&gt;): bool
</code></pre>



<a name="0x4_ord_borrow_permanent_state"></a>

## Function `borrow_permanent_state`



<pre><code><b>public</b> <b>fun</b> <a href="ord.md#0x4_ord_borrow_permanent_state">borrow_permanent_state</a>&lt;S: store&gt;(inscription: &<a href="_Object">object::Object</a>&lt;<a href="ord.md#0x4_ord_Inscription">ord::Inscription</a>&gt;): &S
</code></pre>



<a name="0x4_ord_borrow_mut_permanent_state"></a>

## Function `borrow_mut_permanent_state`



<pre><code>#[private_generics(#[S])]
<b>public</b> <b>fun</b> <a href="ord.md#0x4_ord_borrow_mut_permanent_state">borrow_mut_permanent_state</a>&lt;S: store&gt;(inscription: &<b>mut</b> <a href="_Object">object::Object</a>&lt;<a href="ord.md#0x4_ord_Inscription">ord::Inscription</a>&gt;): &<b>mut</b> S
</code></pre>



<a name="0x4_ord_remove_permanent_state"></a>

## Function `remove_permanent_state`



<pre><code>#[private_generics(#[S])]
<b>public</b> <b>fun</b> <a href="ord.md#0x4_ord_remove_permanent_state">remove_permanent_state</a>&lt;S: store&gt;(inscription: &<b>mut</b> <a href="_Object">object::Object</a>&lt;<a href="ord.md#0x4_ord_Inscription">ord::Inscription</a>&gt;): S
</code></pre>
Loading

0 comments on commit 1655ece

Please sign in to comment.