Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[moveos_std] string_utils::parse_decimal_option and fix brc20 decimal error #1211

Merged
merged 2 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ crates/rooch-genesis/generated/

# Dev dependency
crates/rooch-indexer/.env

# Generated by rooch-sdk
sdk/typescript/dist/
sdk/typescript/src/generated/
2 changes: 1 addition & 1 deletion crates/rooch-framework/doc/bitcoin_address.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ from_script returns a BTCAddress from a ScriptBuf.



<pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="bitcoin_address.md#0x3_bitcoin_address_from_bytes">from_bytes</a>(bytes: <a href="">vector</a>&lt;u8&gt;): <a href="bitcoin_address.md#0x3_bitcoin_address_BTCAddress">bitcoin_address::BTCAddress</a>
<pre><code><b>public</b> <b>fun</b> <a href="bitcoin_address.md#0x3_bitcoin_address_from_bytes">from_bytes</a>(bytes: <a href="">vector</a>&lt;u8&gt;): <a href="bitcoin_address.md#0x3_bitcoin_address_BTCAddress">bitcoin_address::BTCAddress</a>
</code></pre>


Expand Down
2 changes: 1 addition & 1 deletion crates/rooch-framework/doc/brc20.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,5 +253,5 @@ https://domo-2.gitbook.io/brc-20-experiment/



<pre><code>entry <b>fun</b> <a href="brc20.md#0x3_brc20_progress_brc20_ops">progress_brc20_ops</a>(inscription_store_obj: &<a href="_Object">object::Object</a>&lt;<a href="ord.md#0x3_ord_InscriptionStore">ord::InscriptionStore</a>&gt;, brc20_store_obj: &<b>mut</b> <a href="_Object">object::Object</a>&lt;<a href="brc20.md#0x3_brc20_BRC20Store">brc20::BRC20Store</a>&gt;, batch_size: u64)
<pre><code>entry <b>fun</b> <a href="brc20.md#0x3_brc20_progress_brc20_ops">progress_brc20_ops</a>(ctx: &<b>mut</b> <a href="_Context">context::Context</a>, inscription_store_obj: &<a href="_Object">object::Object</a>&lt;<a href="ord.md#0x3_ord_InscriptionStore">ord::InscriptionStore</a>&gt;, brc20_store_obj: &<b>mut</b> <a href="_Object">object::Object</a>&lt;<a href="brc20.md#0x3_brc20_BRC20Store">brc20::BRC20Store</a>&gt;, batch_size: u64)
</code></pre>
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ module rooch_framework::bitcoin_address {
}
}

public(friend) fun from_bytes(bytes: vector<u8>): BTCAddress {
public fun from_bytes(bytes: vector<u8>): BTCAddress {
BTCAddress {
bytes: bytes,
}
Expand Down
164 changes: 101 additions & 63 deletions crates/rooch-framework/sources/brc20.move
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,22 @@ module rooch_framework::brc20 {
//TODO should we register the BRC20 as a CoinInfo?
struct BRC20CoinInfo has store{
tick: String,
max: u64,
lim: u64,
max: u256,
lim: u256,
dec: u64,
supply: u256,
balance: Table<BTCAddress, u256>,
}

struct BRC20Store has key {
next_inscription_index: u64,
coins: Table<String, BRC20CoinInfo>,
balance: Table<BTCAddress, u64>,
}

public(friend) fun genesis_init(ctx: &mut Context, _genesis_account: &signer){
let brc20_store = BRC20Store{
next_inscription_index: 0,
coins: context::new_table(ctx),
balance: context::new_table(ctx),
};
let obj = context::new_named_object(ctx, brc20_store);
object::to_shared(obj);
Expand All @@ -61,11 +61,11 @@ module rooch_framework::brc20 {
/// ```
struct DeployOp has store,copy,drop {
tick: String,
max: u64,
max: String,
//Mint limit: If letting users mint to themsleves, limit per ordinal
lim: u64,
lim: String,
//Decimals: set decimal precision, default to 18
dec: u64,
dec: String,
}

/// The brc20 mint operation
Expand All @@ -80,7 +80,7 @@ module rooch_framework::brc20 {
/// ```
struct MintOp has store,copy,drop {
tick: String,
amt: u64,
amt: String,
}

/// The brc20 transfer operation
Expand All @@ -94,7 +94,7 @@ module rooch_framework::brc20 {
/// }
struct TransferOp has store,copy,drop {
tick: String,
amt: u64,
amt: String,
}

public fun is_brc20(self: &Op) : bool {
Expand All @@ -113,16 +113,9 @@ module rooch_framework::brc20 {
let max_key = string::utf8(b"max");
if(simple_map::contains_key(&self.json_map, &tick_key) && simple_map::contains_key(&self.json_map,&max_key)) {
let tick = *simple_map::borrow(&self.json_map, &tick_key);
let max_str = simple_map::borrow(&self.json_map,&max_key);
let lim_str = simple_map::borrow_with_default(&self.json_map, &string::utf8(b"lim"), &string::utf8(b"0"));
let dec_str = simple_map::borrow_with_default(&self.json_map, &string::utf8(b"dec"), &string::utf8(b"18"));
let max_opt = string_utils::to_u64_option(max_str);
if(option::is_none(&max_opt)){
return option::none()
};
let max = option::destroy_some(max_opt);
let lim = option::destroy_with_default(string_utils::to_u64_option(lim_str), 0u64);
let dec = option::destroy_with_default(string_utils::to_u64_option(dec_str), 18u64);
let dec = *simple_map::borrow_with_default(&self.json_map, &string::utf8(b"dec"), &string::utf8(b"18"));
let max = *simple_map::borrow(&self.json_map,&max_key);
let lim = *simple_map::borrow_with_default(&self.json_map, &string::utf8(b"lim"), &string::utf8(b"0"));
option::some(DeployOp { tick, max, lim, dec })
} else {
option::none()
Expand All @@ -132,17 +125,22 @@ module rooch_framework::brc20 {
}
}

fun execute_deploy(brc20_store: &mut BRC20Store, deploy: DeployOp): bool{
fun execute_deploy(ctx: &mut Context, brc20_store: &mut BRC20Store, deploy: DeployOp): bool{
if(table::contains(&brc20_store.coins, deploy.tick)){
std::debug::print(&string::utf8(b"brc20 already exists"));
return false
};

let tick = deploy.tick;
let max = deploy.max;
let lim = deploy.lim;
let dec = deploy.dec;
let coin_info = BRC20CoinInfo{ tick, max, lim, dec };

let dec = option::destroy_with_default(string_utils::parse_u64_option(&deploy.dec), 18u64);
let max_opt = string_utils::parse_decimal_option(&deploy.max, dec);
if(option::is_none(&max_opt)){
return false
};
let lim = option::destroy_with_default(string_utils::parse_decimal_option(&deploy.lim, dec), 0u256);
let max = option::destroy_some(max_opt);
let coin_info = BRC20CoinInfo{ tick, max, lim, dec , supply: 0u256, balance: context::new_table(ctx)};
table::add(&mut brc20_store.coins, tick, coin_info);
true
}
Expand All @@ -158,12 +156,7 @@ module rooch_framework::brc20 {
let amt_key = string::utf8(b"amt");
if(simple_map::contains_key(&self.json_map, &tick_key) && simple_map::contains_key(&self.json_map,&amt_key)) {
let tick = *simple_map::borrow(&self.json_map, &tick_key);
let amt_str = simple_map::borrow(&self.json_map,&amt_key);
let amt_opt = string_utils::to_u64_option(amt_str);
if(option::is_none(&amt_opt)){
return option::none()
};
let amt = option::destroy_some(amt_opt);
let amt = *simple_map::borrow(&self.json_map,&amt_key);
option::some(MintOp { tick, amt })
} else {
option::none()
Expand All @@ -179,24 +172,28 @@ module rooch_framework::brc20 {
return false
};

let coin_info = table::borrow(&brc20_store.coins, mint.tick);
let max = coin_info.max;
let coin_info = table::borrow_mut(&mut brc20_store.coins, mint.tick);
let lim = coin_info.lim;
if(lim > 0 && mint.amt > lim){

let amt_opt = string_utils::parse_decimal_option(&mint.amt, coin_info.dec);
if(option::is_none(&amt_opt)){
return false
};
let amt = option::destroy_some(amt_opt);

if(lim > 0 && amt > lim){
std::debug::print(&string::utf8(b"brc20 mint lim exceeded"));
return false
};
let amt = mint.amt;
let balance = table::borrow_mut_with_default(&mut brc20_store.balance, sender, 0);
let next_balance = *balance + amt;
if(next_balance > max){
let new_total_supply = coin_info.supply + amt;
if(new_total_supply > coin_info.max){
std::debug::print(&string::utf8(b"brc20 max exceeded"));
false
}else{
*balance = next_balance;
true
}

return false
};
coin_info.supply = new_total_supply;
let balance = table::borrow_mut_with_default(&mut coin_info.balance, sender, 0);
*balance = *balance + amt;
true
}

public fun is_transfer(self: &Op) : bool {
Expand All @@ -209,13 +206,8 @@ module rooch_framework::brc20 {
let tick_key = string::utf8(b"tick");
let amt_key = string::utf8(b"amt");
if(simple_map::contains_key(&self.json_map, &tick_key) && simple_map::contains_key(&self.json_map,&amt_key)) {
let tick = *simple_map::borrow(&self.json_map, &tick_key);
let amt_str = simple_map::borrow(&self.json_map,&amt_key);
let amt_opt = string_utils::to_u64_option(amt_str);
if(option::is_none(&amt_opt)){
return option::none()
};
let amt = option::destroy_some(amt_opt);
let tick = *simple_map::borrow(&self.json_map, &tick_key);
let amt = *simple_map::borrow(&self.json_map,&amt_key);
option::some(TransferOp { tick, amt })
} else {
option::none()
Expand All @@ -230,15 +222,22 @@ module rooch_framework::brc20 {
std::debug::print(&string::utf8(b"brc20 does not exist"));
return false
};

let amt = transfer.amt;
let sender_balance = table::borrow_mut_with_default(&mut brc20_store.balance, sender, 0);

let coin_info = table::borrow_mut(&mut brc20_store.coins, transfer.tick);

let amt_opt = string_utils::parse_decimal_option(&transfer.amt, coin_info.dec);
if(option::is_none(&amt_opt)){
return false
};
let amt = option::destroy_some(amt_opt);

let sender_balance = table::borrow_mut_with_default(&mut coin_info.balance, sender, 0);
if(*sender_balance < amt){
std::debug::print(&string::utf8(b"brc20 insufficient balance"));
false
}else{
*sender_balance = *sender_balance - amt;
let receiver_balance = table::borrow_mut_with_default(&mut brc20_store.balance, receiver, 0);
let receiver_balance = table::borrow_mut_with_default(&mut coin_info.balance, receiver, 0);
*receiver_balance = *receiver_balance + amt;
true
}
Expand All @@ -257,7 +256,7 @@ module rooch_framework::brc20 {
option::some(Op { json_map })
}

fun progress_op(brc20_store: &mut BRC20Store, op: Op) {
fun progress_op(ctx: &mut Context, brc20_store: &mut BRC20Store, op: Op) {
if(!is_brc20(&op)){
std::debug::print(&string::utf8(b"not brc20 op"));
std::debug::print(&op);
Expand All @@ -271,7 +270,7 @@ module rooch_framework::brc20 {
return
};
let deploy_op = option::destroy_some(deploy_op_opt);
let result = execute_deploy(brc20_store, deploy_op);
let result = execute_deploy(ctx, brc20_store, deploy_op);
if(!result){
std::debug::print(&string::utf8(b"failed to execute deploy op"));
std::debug::print(&op);
Expand Down Expand Up @@ -315,7 +314,7 @@ module rooch_framework::brc20 {
}
}

entry fun progress_brc20_ops(inscription_store_obj: &Object<InscriptionStore>, brc20_store_obj: &mut Object<BRC20Store>, batch_size: u64){
entry fun progress_brc20_ops(ctx: &mut Context, inscription_store_obj: &Object<InscriptionStore>, brc20_store_obj: &mut Object<BRC20Store>, batch_size: u64){
let brc20_store = object::borrow_mut(brc20_store_obj);
let inscription_ids = ord::inscription_ids(inscription_store_obj);
let inscriptions = ord::inscriptions(inscription_store_obj);
Expand All @@ -332,14 +331,20 @@ module rooch_framework::brc20 {
let op_opt = from_inscription(inscription);
if(option::is_some(&op_opt)){
let op = option::destroy_some(op_opt);
progress_op(brc20_store, op);
progress_op(ctx, brc20_store, op);
};
progressed_inscription_count = progressed_inscription_count + 1;
progress_inscription_index = progress_inscription_index + 1;
};
brc20_store.next_inscription_index = progress_inscription_index;
}

#[test_only]
fun drop_brc20_store(brc20_store:BRC20Store){
let BRC20Store{ next_inscription_index:_, coins} = brc20_store;
table::drop_unchecked(coins);
}

#[test]
fun test_deploy_op(){
let deploy_op_json = b"{\"p\":\"brc-20\",\"op\":\"deploy\",\"tick\":\"ordi\",\"max\":\"21000000\",\"lim\":\"1000\"}";
Expand All @@ -350,9 +355,9 @@ module rooch_framework::brc20 {
assert!(option::is_some(&deploy_op_opt), 3);
let deploy_op = option::destroy_some(deploy_op_opt);
assert!(deploy_op.tick == string::utf8(b"ordi"), 4);
assert!(deploy_op.max == 21000000, 5);
assert!(deploy_op.lim == 1000, 6);
assert!(deploy_op.dec == 18, 7);
assert!(deploy_op.max == string::utf8(b"21000000"), 5);
assert!(deploy_op.lim == string::utf8(b"1000"), 6);
assert!(deploy_op.dec == string::utf8(b"18"), 7);
}

#[test]
Expand All @@ -365,7 +370,7 @@ module rooch_framework::brc20 {
assert!(option::is_some(&mint_op_opt), 3);
let mint_op = option::destroy_some(mint_op_opt);
assert!(mint_op.tick == string::utf8(b"ordi"), 4);
assert!(mint_op.amt == 1000, 5);
assert!(mint_op.amt == string::utf8(b"1000"), 5);
}

#[test]
Expand All @@ -378,7 +383,40 @@ module rooch_framework::brc20 {
assert!(option::is_some(&transfer_op_opt), 3);
let transfer_op = option::destroy_some(transfer_op_opt);
assert!(transfer_op.tick == string::utf8(b"ordi"), 4);
assert!(transfer_op.amt == 1000, 5);
assert!(transfer_op.amt == string::utf8(b"1000"), 5);
}

#[test]
fun test_brc20_roundtrip(){
let ctx = moveos_std::context::new_test_context(@rooch_framework);
let brc20_store = BRC20Store{
next_inscription_index: 0,
coins: context::new_table(&mut ctx),
};
let deploy_op_json = b"{\"p\":\"brc-20\",\"op\":\"deploy\",\"tick\":\"ordi\",\"max\":\"21000000\",\"lim\":\"1000\"}";
let op = Op { json_map: json::to_map(deploy_op_json) };
let deploy_op = option::destroy_some(as_deploy(&op));
assert!(execute_deploy(&mut ctx, &mut brc20_store, deploy_op), 1);

let mint_op_json = b"{\"p\":\"brc-20\",\"op\":\"mint\",\"tick\":\"ordi\",\"amt\":\"1000\"}";
let op = Op { json_map: json::to_map(mint_op_json) };
let mint_op = option::destroy_some(as_mint(&op));
let btc_address1 = rooch_framework::bitcoin_address::from_bytes(x"01");
assert!(execute_mint(&mut brc20_store, mint_op, btc_address1), 2);

let transfer_op_json = b"{\"p\":\"brc-20\",\"op\":\"transfer\",\"tick\":\"ordi\",\"amt\":\"1000\"}";
let op = Op { json_map: json::to_map(transfer_op_json) };
let transfer_op = option::destroy_some(as_transfer(&op));
let btc_address2 = rooch_framework::bitcoin_address::from_bytes(x"02");
assert!(execute_transfer(&mut brc20_store, transfer_op, btc_address1, btc_address2), 3);

let coin_info = table::borrow(&brc20_store.coins, string::utf8(b"ordi"));
assert!(coin_info.supply == 1000000000000000000000u256, 4);
let balance1 = *table::borrow(&coin_info.balance, btc_address1);
assert!(balance1 == 0u256, 5);
let balance2 = *table::borrow(&coin_info.balance, btc_address2);
assert!(balance2 == 1000000000000000000000u256, 6);
context::drop_test_context(ctx);
drop_brc20_store(brc20_store);
}
}
Loading
Loading