diff --git a/src/rgb.rs b/src/rgb.rs index ccf144cc..97988fa7 100644 --- a/src/rgb.rs +++ b/src/rgb.rs @@ -65,7 +65,10 @@ use crate::{ use self::{ carbonado::{retrieve_wallets, store_wallets}, - constants::{CARBONADO_UNAVAILABLE, RGB_DEFAULT_NAME, STOCK_UNAVAILABLE}, + constants::{ + BITCOIN_DEFAULT_FETCH_LIMIT, CARBONADO_UNAVAILABLE, RGB_DEFAULT_FETCH_LIMIT, + RGB_DEFAULT_NAME, STOCK_UNAVAILABLE, + }, contract::{export_contract, ExportContractError}, import::{import_contract, ImportContractError}, prefetch::{ @@ -145,8 +148,13 @@ pub async fn issue_contract(sk: &str, request: IssueRequest) -> Result { let mut fetch_wallet = wallet.to_owned(); for contract_type in [AssetType::RGB20, AssetType::RGB21] { - prefetch_resolver_utxos(contract_type as u32, &mut fetch_wallet, &mut resolver) - .await; + prefetch_resolver_utxos( + contract_type as u32, + &mut fetch_wallet, + &mut resolver, + Some(RGB_DEFAULT_FETCH_LIMIT), + ) + .await; } Some(fetch_wallet) @@ -332,8 +340,13 @@ pub async fn reissue_contract( Some(wallet) => { let mut fetch_wallet = wallet.to_owned(); for contract_type in [AssetType::RGB20, AssetType::RGB21] { - prefetch_resolver_utxos(contract_type as u32, &mut fetch_wallet, &mut resolver) - .await; + prefetch_resolver_utxos( + contract_type as u32, + &mut fetch_wallet, + &mut resolver, + Some(RGB_DEFAULT_FETCH_LIMIT), + ) + .await; } Some(fetch_wallet) @@ -816,7 +829,13 @@ pub async fn full_transfer_asset( let mut wallet = wallet.unwrap(); let mut all_unspents = vec![]; for bitcoin_index in bitcoin_indexes { - prefetch_resolver_utxos(bitcoin_index, &mut wallet, &mut resolver).await; + prefetch_resolver_utxos( + bitcoin_index, + &mut wallet, + &mut resolver, + Some(BITCOIN_DEFAULT_FETCH_LIMIT), + ) + .await; prefetch_resolver_utxo_status(bitcoin_index, &mut wallet, &mut resolver).await; sync_wallet(bitcoin_index, &mut wallet, &mut resolver); @@ -940,8 +959,13 @@ pub async fn get_contract(sk: &str, contract_id: &str) -> Result { let mut fetch_wallet = wallet.to_owned(); for contract_type in [AssetType::RGB20, AssetType::RGB21] { - prefetch_resolver_utxos(contract_type as u32, &mut fetch_wallet, &mut resolver) - .await; + prefetch_resolver_utxos( + contract_type as u32, + &mut fetch_wallet, + &mut resolver, + Some(RGB_DEFAULT_FETCH_LIMIT), + ) + .await; } Some(fetch_wallet) @@ -977,8 +1001,13 @@ pub async fn list_contracts(sk: &str) -> Result { Some(wallet) => { let mut fetch_wallet = wallet.to_owned(); for contract_type in [AssetType::RGB20, AssetType::RGB21] { - prefetch_resolver_utxos(contract_type as u32, &mut fetch_wallet, &mut resolver) - .await; + prefetch_resolver_utxos( + contract_type as u32, + &mut fetch_wallet, + &mut resolver, + Some(RGB_DEFAULT_FETCH_LIMIT), + ) + .await; } Some(fetch_wallet) } @@ -1086,7 +1115,13 @@ pub async fn import(sk: &str, request: ImportRequest) -> Result { let mut fetch_wallet = wallet.to_owned(); - prefetch_resolver_utxos(import.clone() as u32, &mut fetch_wallet, &mut resolver).await; + prefetch_resolver_utxos( + import.clone() as u32, + &mut fetch_wallet, + &mut resolver, + Some(RGB_DEFAULT_FETCH_LIMIT), + ) + .await; Some(fetch_wallet) } _ => None, @@ -1206,7 +1241,13 @@ pub async fn watcher_details(sk: &str, name: &str) -> Result Result Result Result< ..Default::default() }; - prefetch_resolver_utxos(iface_index, &mut wallet, &mut resolver).await; + prefetch_resolver_utxos( + iface_index, + &mut wallet, + &mut resolver, + Some(RGB_DEFAULT_FETCH_LIMIT), + ) + .await; prefetch_resolver_utxo_status(iface_index, &mut wallet, &mut resolver).await; sync_wallet(iface_index, &mut wallet, &mut resolver); diff --git a/src/rgb/constants.rs b/src/rgb/constants.rs index 78b59848..66821adc 100644 --- a/src/rgb/constants.rs +++ b/src/rgb/constants.rs @@ -5,6 +5,8 @@ pub const RGB_PSBT_TAPRET: &str = "TAPRET"; pub const RGB_DEFAULT_NAME: &str = "default"; pub const RGB_OLDEST_VERSION: [u8; 8] = [0; 8]; pub const RGB_STRICT_TYPE_VERSION: [u8; 8] = *b"rgbst161"; +pub const RGB_DEFAULT_FETCH_LIMIT: u32 = 10; +pub const BITCOIN_DEFAULT_FETCH_LIMIT: u32 = 20; // General Errors #[cfg(target_arch = "wasm32")] diff --git a/src/rgb/import.rs b/src/rgb/import.rs index cfada05f..9b268458 100644 --- a/src/rgb/import.rs +++ b/src/rgb/import.rs @@ -3,11 +3,11 @@ use amplify::{ hex::FromHex, }; use bech32::{decode, FromBase32}; -use rgb_schemata::{nia_schema, uda_schema}; +use rgb_schemata::{nia_rgb20, nia_schema, uda_rgb21, uda_schema}; use rgbstd::{ containers::Contract, contract::Genesis, - interface::{rgb20, rgb21}, + interface::{rgb20, rgb21, IfacePair}, persistence::{Inventory, Stash, Stock}, resolvers::ResolveHeight, validation::ResolveTx, @@ -70,19 +70,22 @@ pub fn contract_from_genesis( asset_type: AssetType, stock: Option<&mut Stock>, ) -> Contract { - let schema = match asset_type { - AssetType::RGB20 => nia_schema(), - AssetType::RGB21 => uda_schema(), - _ => nia_schema(), + let (schema, iface, iimpl) = match asset_type { + AssetType::RGB20 => (nia_schema(), rgb20(), nia_rgb20()), + AssetType::RGB21 => (uda_schema(), rgb21(), uda_rgb21()), + _ => (nia_schema(), rgb20(), nia_rgb20()), }; if let Some(stock) = stock { - match asset_type { - AssetType::RGB20 => stock.import_iface(rgb20()), - AssetType::RGB21 => stock.import_iface(rgb21()), - _ => stock.import_iface(rgb20()), - } - .expect("import iface failed"); + stock + .import_iface(iface.clone()) + .expect("import iface failed"); } - Contract::new(schema, genesis) + let mut contract = Contract::new(schema, genesis); + contract + .ifaces + .insert(iface.iface_id(), IfacePair::with(iface, iimpl)) + .expect("import iface pair failed"); + + contract } diff --git a/src/rgb/prefetch.rs b/src/rgb/prefetch.rs index 70bf5ca6..0d0fe747 100644 --- a/src/rgb/prefetch.rs +++ b/src/rgb/prefetch.rs @@ -55,6 +55,7 @@ pub async fn prefetch_resolver_utxos( iface_index: u32, wallet: &mut RgbWallet, explorer: &mut ExplorerResolver, + limit: Option, ) { } @@ -290,14 +291,19 @@ pub async fn prefetch_resolver_utxos( iface_index: u32, wallet: &mut RgbWallet, explorer: &mut ExplorerResolver, + limit: Option, ) { use std::collections::HashSet; let esplora_client: EsploraBlockchain = EsploraBlockchain::new(&explorer.explorer_url, 1).with_concurrency(6); - let step = 100; let index = 0; + let mut step = 100; + if let Some(limit) = limit { + step = limit; + } + let mut utxos = bset![]; let scripts = wallet.descr.derive(iface_index, index..step); @@ -395,11 +401,11 @@ pub async fn prefetch_resolver_waddress( let esplora_client: EsploraBlockchain = EsploraBlockchain::new(&explorer.explorer_url, 1).with_concurrency(6); + let index = 0; let mut step = 100; if let Some(limit) = limit { step = limit; } - let index = 0; let sc = AddressCompat::from_str(address).expect("invalid address"); let script = ScriptBuf::from_hex(&sc.script_pubkey().to_hex()).expect("invalid script"); diff --git a/tests/rgb/integration/import.rs b/tests/rgb/integration/import.rs index 7991308e..1d7e5ee1 100644 --- a/tests/rgb/integration/import.rs +++ b/tests/rgb/integration/import.rs @@ -1,13 +1,32 @@ #![cfg(not(target_arch = "wasm32"))] -use crate::rgb::integration::utils::ISSUER_MNEMONIC; +use crate::rgb::integration::utils::{issuer_issue_contract, ISSUER_MNEMONIC}; use bitmask_core::{ - bitcoin::save_mnemonic, + bitcoin::{new_mnemonic, save_mnemonic}, rgb::import, structs::{AssetType, ImportRequest, SecretString}, }; #[tokio::test] async fn allow_import_fungibles_from_genesis() -> anyhow::Result<()> { + let issuer_resp = issuer_issue_contract("RGB20", 5, false, true, None).await; + assert!(issuer_resp.is_ok()); + + let another_vault = new_mnemonic(&SecretString("".to_string())).await?; + + let sk = &another_vault.private.nostr_prv; + let contract_import = ImportRequest { + import: AssetType::RGB20, + data: issuer_resp?.genesis.strict, + }; + + let import_resp = import(sk, contract_import).await; + assert!(import_resp.is_ok()); + + Ok(()) +} + +#[tokio::test] +async fn allow_import_fungibles_from_genesis_data() -> anyhow::Result<()> { let issuer_keys = save_mnemonic( &SecretString(ISSUER_MNEMONIC.to_string()), &SecretString("".to_string()), @@ -26,7 +45,7 @@ async fn allow_import_fungibles_from_genesis() -> anyhow::Result<()> { } #[tokio::test] -async fn allow_import_uda_from_genesis() -> anyhow::Result<()> { +async fn allow_import_uda_from_genesis_data() -> anyhow::Result<()> { let issuer_keys = save_mnemonic( &SecretString(ISSUER_MNEMONIC.to_string()), &SecretString("".to_string()), @@ -45,7 +64,7 @@ async fn allow_import_uda_from_genesis() -> anyhow::Result<()> { } #[tokio::test] -async fn allow_import_fungible_contract() -> anyhow::Result<()> { +async fn allow_import_fungible_contract_data() -> anyhow::Result<()> { let issuer_keys = save_mnemonic( &SecretString(ISSUER_MNEMONIC.to_string()), &SecretString("".to_string()), @@ -64,7 +83,7 @@ async fn allow_import_fungible_contract() -> anyhow::Result<()> { } #[tokio::test] -async fn allow_import_uda_contract() -> anyhow::Result<()> { +async fn allow_import_uda_contract_data() -> anyhow::Result<()> { let issuer_keys = save_mnemonic( &SecretString(ISSUER_MNEMONIC.to_string()), &SecretString("".to_string()), diff --git a/tests/rgb/integration/internal.rs b/tests/rgb/integration/internal.rs index 9beeb630..ed69d1e8 100644 --- a/tests/rgb/integration/internal.rs +++ b/tests/rgb/integration/internal.rs @@ -8,12 +8,12 @@ use bitmask_core::{ }; use crate::rgb::integration::utils::{ - create_new_invoice, issuer_issue_contract_v2, send_some_coins, UtxoFilter, ISSUER_MNEMONIC, - OWNER_MNEMONIC, + create_new_invoice, get_uda_data, issuer_issue_contract_v2, send_some_coins, UtxoFilter, + ISSUER_MNEMONIC, OWNER_MNEMONIC, }; #[tokio::test] -async fn allow_full_transfer_step() -> anyhow::Result<()> { +async fn allow_fungible_full_transfer_op() -> anyhow::Result<()> { // 1. Initial Setup let issuer_keys = save_mnemonic( &SecretString(ISSUER_MNEMONIC.to_string()), @@ -78,3 +78,71 @@ async fn allow_full_transfer_step() -> anyhow::Result<()> { assert!(resp.is_ok()); Ok(()) } + +#[tokio::test] +async fn allow_uda_full_transfer_op() -> anyhow::Result<()> { + // 1. Initial Setup + let issuer_keys = save_mnemonic( + &SecretString(ISSUER_MNEMONIC.to_string()), + &SecretString("".to_string()), + ) + .await?; + let owner_keys = save_mnemonic( + &SecretString(OWNER_MNEMONIC.to_string()), + &SecretString("".to_string()), + ) + .await?; + let meta = Some(get_uda_data()); + let issuer_resp = issuer_issue_contract_v2( + 1, + "RGB21", + 1, + false, + true, + meta, + Some("0.00000546".to_string()), + Some(UtxoFilter::with_amount_less_than(546)), + ) + .await?; + + // 2. Get Invoice + let issuer_resp = issuer_resp[0].clone(); + let owner_resp = &create_new_invoice( + &issuer_resp.contract_id, + &issuer_resp.iface, + 1, + owner_keys.clone(), + None, + None, + ) + .await?; + + // 3. Get Bitcoin UTXO + let issuer_btc_desc = &issuer_keys.public.btc_change_descriptor_xpub; + let issuer_vault = get_wallet(&SecretString(issuer_btc_desc.to_string()), None).await?; + let issuer_address = &issuer_vault + .lock() + .await + .get_address(AddressIndex::LastUnused)? + .address + .to_string(); + + send_some_coins(issuer_address, "0.001").await; + sync_wallet(&issuer_vault).await?; + + // 4. Make a Self Payment + let self_pay_req = FullRgbTransferRequest { + contract_id: issuer_resp.contract_id, + iface: issuer_resp.iface, + rgb_invoice: owner_resp.invoice.to_string(), + descriptor: SecretString(issuer_keys.public.rgb_udas_descriptor_xpub.to_string()), + change_terminal: "/1/0".to_string(), + fee: PsbtFeeRequest::Value(546), + }; + + let issue_sk = issuer_keys.private.nostr_prv.to_string(); + let resp = full_transfer_asset(&issue_sk, self_pay_req).await; + + assert!(resp.is_ok()); + Ok(()) +} diff --git a/tests/rgb/integration/states.rs b/tests/rgb/integration/states.rs index a044ac9e..936168d2 100644 --- a/tests/rgb/integration/states.rs +++ b/tests/rgb/integration/states.rs @@ -145,7 +145,6 @@ async fn check_fungible_state_after_accept_consig() -> anyhow::Result<()> { #[tokio::test] async fn check_uda_state_after_accept_consig() -> anyhow::Result<()> { // 1. Issue and Generate Trasnfer (Issuer side) - let single = Some(get_uda_data()); let issuer_keys: DecryptedWalletData = save_mnemonic( &SecretString(ISSUER_MNEMONIC.to_string()), &SecretString("".to_string()), @@ -156,7 +155,19 @@ async fn check_uda_state_after_accept_consig() -> anyhow::Result<()> { &SecretString("".to_string()), ) .await?; - let issuer_resp = issuer_issue_contract("RGB21", 1, false, true, single).await?; + let meta = Some(get_uda_data()); + let issuer_resp = issuer_issue_contract_v2( + 1, + "RGB21", + 1, + false, + true, + meta, + Some("0.1".to_string()), + Some(UtxoFilter::with_amount_equal_than(10000000)), + ) + .await?; + let issuer_resp = issuer_resp[0].clone(); let owner_resp = &create_new_invoice( &issuer_resp.contract_id, &issuer_resp.iface, diff --git a/tests/rgb/integration/udas.rs b/tests/rgb/integration/udas.rs index b4a6a450..fa467900 100644 --- a/tests/rgb/integration/udas.rs +++ b/tests/rgb/integration/udas.rs @@ -1,7 +1,7 @@ #![cfg(not(target_arch = "wasm32"))] use crate::rgb::integration::utils::{ - create_new_invoice, create_new_psbt, create_new_transfer, get_uda_data, issuer_issue_contract, - ISSUER_MNEMONIC, OWNER_MNEMONIC, + create_new_invoice, create_new_psbt, create_new_transfer, get_uda_data, + issuer_issue_contract_v2, UtxoFilter, ISSUER_MNEMONIC, OWNER_MNEMONIC, }; use bitmask_core::{ bitcoin::{save_mnemonic, sign_psbt_file}, @@ -11,7 +11,6 @@ use bitmask_core::{ #[tokio::test] async fn allow_beneficiary_accept_transfer() -> anyhow::Result<()> { - let single = Some(get_uda_data()); let issuer_keys = &save_mnemonic( &SecretString(ISSUER_MNEMONIC.to_string()), &SecretString("".to_string()), @@ -22,7 +21,19 @@ async fn allow_beneficiary_accept_transfer() -> anyhow::Result<()> { &SecretString("".to_string()), ) .await?; - let issuer_resp = &issuer_issue_contract("RGB21", 1, false, true, single).await?; + let meta = Some(get_uda_data()); + let issuer_resp = issuer_issue_contract_v2( + 1, + "RGB21", + 1, + false, + true, + meta, + Some("0.1".to_string()), + Some(UtxoFilter::with_amount_equal_than(10000000)), + ) + .await?; + let issuer_resp = issuer_resp[0].clone(); let owner_resp = create_new_invoice( &issuer_resp.contract_id, &issuer_resp.iface,