diff --git a/README.md b/README.md index d33862c1..43fd1961 100644 --- a/README.md +++ b/README.md @@ -218,9 +218,11 @@ The node currently exposes the following APIs: - `/getchannelid` (POST) - `/getpayment` (POST) - `/getswap` (POST) +- `/inflate` (POST) - `/init` (POST) - `/invoicestatus` (POST) - `/issueassetcfa` (POST) +- `/issueassetifa` (POST) - `/issueassetnia` (POST) - `/issueassetuda` (POST) - `/keysend` (POST) diff --git a/openapi.yaml b/openapi.yaml index 725a14b6..aaed8e8c 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -386,6 +386,24 @@ paths: application/json: schema: $ref: '#/components/schemas/GetSwapResponse' + /inflate: + post: + tags: + - RGB + summary: Inflate RGB assets + description: Inflate RGB assets on-chain + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InflateRequest' + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/InflateResponse' /init: post: tags: @@ -442,6 +460,24 @@ paths: application/json: schema: $ref: '#/components/schemas/IssueAssetCFAResponse' + /issueassetifa: + post: + tags: + - RGB + summary: Issue an RGB IFA asset + description: Issue an RGB IFA asset + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/IssueAssetIFARequest' + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/IssueAssetIFAResponse' /issueassetnia: post: tags: @@ -1056,6 +1092,63 @@ components: oneOf: - $ref: '#/components/schemas/Media' - type: 'null' + AssetIFA: + type: object + required: + - asset_id + - ticker + - name + - precision + - initial_supply + - max_supply + - known_circulating_supply + - timestamp + - added_at + - balance + properties: + asset_id: + type: string + example: rgb:CJkb4YZw-jRiz2sk-~PARPio-wtVYI1c-XAEYCqO-wTfvRZ8 + ticker: + type: string + example: USDT + name: + type: string + example: Tether + details: + type: + - string + - 'null' + example: asset details + precision: + type: integer + example: 0 + initial_supply: + type: integer + example: 777 + max_supply: + type: integer + example: 999 + known_circulating_supply: + type: integer + example: 888 + timestamp: + type: integer + example: 1691160565 + added_at: + type: integer + example: 1691161979 + balance: + $ref: '#/components/schemas/AssetBalanceResponse' + media: + oneOf: + - $ref: '#/components/schemas/Media' + - type: 'null' + reject_list_url: + type: + - string + - 'null' + example: https://some.domain/someasset/rejectlist AssetMetadataRequest: type: object required: @@ -1159,6 +1252,7 @@ components: - Nia - Uda - Cfa + - Ifa AssetUDA: type: object required: @@ -1742,6 +1836,37 @@ components: enum: - Electrum - Esplora + InflateRequest: + type: object + required: + - asset_id + - inflation_amounts + - fee_rate + - min_confirmations + properties: + asset_id: + type: string + example: rgb:CJkb4YZw-jRiz2sk-~PARPio-wtVYI1c-XAEYCqO-wTfvRZ8 + inflation_amounts: + type: array + items: + type: integer + minItems: 1 + example: [ 100, 50 ] + fee_rate: + type: integer + example: 5 + min_confirmations: + type: integer + example: 1 + InflateResponse: + type: object + required: + - txid + properties: + txid: + type: string + example: 7c2c95b9c2aa0a7d140495b664de7973b76561de833f0dd84def3efa08941664 InitRequest: type: object required: @@ -1820,6 +1945,51 @@ components: properties: asset: $ref: '#/components/schemas/AssetCFA' + IssueAssetIFARequest: + type: object + required: + - amounts + - inflation_amounts + - ticker + - name + - precision + - replace_rights_num + properties: + amounts: + type: array + items: + type: integer + minItems: 1 + example: [ 1000, 600 ] + inflation_amounts: + type: array + items: + type: integer + example: [ 100, 50 ] + ticker: + type: string + example: USDT + name: + type: string + example: Tether + precision: + type: integer + example: 0 + replace_rights_num: + type: integer + example: 0 + reject_list_url: + type: + - string + - 'null' + example: https://some.domain/someasset/rejectlist + IssueAssetIFAResponse: + type: object + required: + - asset + properties: + asset: + $ref: '#/components/schemas/AssetIFA' IssueAssetNIARequest: type: object required: @@ -1934,13 +2104,14 @@ components: type: array items: $ref: '#/components/schemas/AssetSchema' - example: [ Nia, Uda, Cfa ] + example: [ Nia, Uda, Cfa, Ifa ] ListAssetsResponse: type: object required: - nia - uda - cfa + - ifa properties: nia: type: @@ -1960,6 +2131,12 @@ components: - 'null' items: $ref: '#/components/schemas/AssetCFA' + ifa: + type: + - array + - 'null' + items: + $ref: '#/components/schemas/AssetIFA' ListChannelsResponse: type: object required: diff --git a/rust-lightning b/rust-lightning index ea151249..00b44166 160000 --- a/rust-lightning +++ b/rust-lightning @@ -1 +1 @@ -Subproject commit ea151249bc794bc38b1bc054c09c6f6463c05a10 +Subproject commit 00b44166467dd1f7478a19c7a225cd7abbffbe81 diff --git a/src/error.rs b/src/error.rs index 28b58372..b3c89bc8 100644 --- a/src/error.rs +++ b/src/error.rs @@ -293,6 +293,9 @@ pub enum APIError { #[error("The provided backup has an unsupported version: {version}")] UnsupportedBackupVersion { version: String }, + #[error("Inflation is not supported by schema {0}")] + UnsupportedInflation(String), + #[error("Layer 1 {0} is not supported")] UnsupportedLayer1(String), @@ -386,6 +389,9 @@ impl From for APIError { RgbLibError::MaxFeeExceeded { txid } => APIError::MaxFeeExceeded(txid), RgbLibError::MinFeeNotMet { txid } => APIError::MinFeeNotMet(txid), RgbLibError::Network { details } => APIError::Network(details), + RgbLibError::NoInflationAmounts => { + APIError::InvalidAmount(s!("inflation request with no amounts or zero amounts")) + } RgbLibError::NoIssuanceAmounts => { APIError::InvalidAmount(s!("issuance request with no provided amounts")) } @@ -393,9 +399,15 @@ impl From for APIError { RgbLibError::OutputBelowDustLimit => APIError::OutputBelowDustLimit, RgbLibError::Proxy { details } => APIError::Network(format!("proxy err: {details}")), RgbLibError::RecipientIDAlreadyUsed => APIError::RecipientIDAlreadyUsed, + RgbLibError::TooHighInflationAmounts => { + APIError::InvalidAmount(s!("inflation amount exceeds the max possible supply")) + } RgbLibError::TooHighIssuanceAmounts => { APIError::InvalidAmount(s!("trying to issue too many assets")) } + RgbLibError::UnsupportedInflation { asset_schema } => { + APIError::UnsupportedInflation(format!("{asset_schema}")) + } RgbLibError::UnsupportedLayer1 { layer_1 } => APIError::UnsupportedLayer1(layer_1), RgbLibError::UnsupportedTransportType => APIError::UnsupportedTransportType, _ => { @@ -506,6 +518,7 @@ impl IntoResponse for APIError { | APIError::UnknownLNInvoice | APIError::UnknownTemporaryChannelId | APIError::UnlockedNode + | APIError::UnsupportedInflation(_) | APIError::UnsupportedLayer1(_) | APIError::UnsupportedTransportType => { (StatusCode::FORBIDDEN, self.to_string(), self.name()) diff --git a/src/ldk.rs b/src/ldk.rs index 34639e2d..4251f02d 100644 --- a/src/ldk.rs +++ b/src/ldk.rs @@ -593,9 +593,10 @@ async fn handle_ldk_events( let channel_rgb_amount = rgb_info.local_rgb_amount + rgb_info.remote_rgb_amount; let asset_id = rgb_info.contract_id.to_string(); let assignment = match rgb_info.schema { - AssetSchema::Nia | AssetSchema::Cfa => Assignment::Fungible(channel_rgb_amount), + AssetSchema::Nia | AssetSchema::Cfa | AssetSchema::Ifa => { + Assignment::Fungible(channel_rgb_amount) + } AssetSchema::Uda => Assignment::NonFungible, - AssetSchema::Ifa => todo!(), }; let recipient_id = recipient_id_from_script_buf(script_buf, static_state.network); @@ -1788,7 +1789,12 @@ pub(crate) async fn start_ldk( master_fingerprint: master_fingerprint.to_string(), mnemonic: Some(mnemonic.to_string()), vanilla_keychain: None, - supported_schemas: vec![AssetSchema::Nia, AssetSchema::Cfa, AssetSchema::Uda], + supported_schemas: vec![ + AssetSchema::Nia, + AssetSchema::Cfa, + AssetSchema::Uda, + AssetSchema::Ifa, + ], }) .expect("valid rgb-lib wallet") }) diff --git a/src/main.rs b/src/main.rs index 60dc4287..4fcc5bbd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,12 +45,13 @@ use crate::routes::{ address, asset_balance, asset_metadata, backup, btc_balance, change_password, check_indexer_url, check_proxy_endpoint, close_channel, connect_peer, create_utxos, decode_ln_invoice, decode_rgb_invoice, disconnect_peer, estimate_fee, fail_transfers, - get_asset_media, get_channel_id, get_payment, get_swap, init, invoice_status, issue_asset_cfa, - issue_asset_nia, issue_asset_uda, keysend, list_assets, list_channels, list_payments, - list_peers, list_swaps, list_transactions, list_transfers, list_unspents, ln_invoice, lock, - maker_execute, maker_init, network_info, node_info, open_channel, post_asset_media, - refresh_transfers, restore, revoke_token, rgb_invoice, send_btc, send_onion_message, - send_payment, send_rgb, shutdown, sign_message, sync, taker, unlock, + get_asset_media, get_channel_id, get_payment, get_swap, inflate, init, invoice_status, + issue_asset_cfa, issue_asset_ifa, issue_asset_nia, issue_asset_uda, keysend, list_assets, + list_channels, list_payments, list_peers, list_swaps, list_transactions, list_transfers, + list_unspents, ln_invoice, lock, maker_execute, maker_init, network_info, node_info, + open_channel, post_asset_media, refresh_transfers, restore, revoke_token, rgb_invoice, + send_btc, send_onion_message, send_payment, send_rgb, shutdown, sign_message, sync, taker, + unlock, }; use crate::utils::{start_daemon, AppState, LOGS_DIR}; @@ -124,9 +125,11 @@ pub(crate) async fn app(args: UserArgs) -> Result<(Router, Arc), AppEr .route("/getchannelid", post(get_channel_id)) .route("/getpayment", post(get_payment)) .route("/getswap", post(get_swap)) + .route("/inflate", post(inflate)) .route("/init", post(init)) .route("/invoicestatus", post(invoice_status)) .route("/issueassetcfa", post(issue_asset_cfa)) + .route("/issueassetifa", post(issue_asset_ifa)) .route("/issueassetnia", post(issue_asset_nia)) .route("/issueassetuda", post(issue_asset_uda)) .route("/keysend", post(keysend)) diff --git a/src/rgb.rs b/src/rgb.rs index 3400487a..8a1a294c 100644 --- a/src/rgb.rs +++ b/src/rgb.rs @@ -17,7 +17,7 @@ use rgb_lib::{ bitcoin::psbt::Psbt as BitcoinPsbt, wallet::{ rust_only::{check_proxy_url, ColoringInfo}, - AssetCFA, AssetNIA, AssetUDA, Assets, Balance, BtcBalance, Metadata, Online, + AssetCFA, AssetIFA, AssetNIA, AssetUDA, Assets, Balance, BtcBalance, Metadata, Online, OperationResult, ReceiveData, Recipient, RefreshResult, Transaction as RgbLibTransaction, Transfer, TransportEndpoint, Unspent, WalletData, }, @@ -114,6 +114,17 @@ impl UnlockedAppState { self.rgb_wallet_wrapper.get_wallet_data() } + pub(crate) fn rgb_inflate( + &self, + asset_id: String, + inflation_amounts: Vec, + fee_rate: u64, + min_confirmations: u8, + ) -> Result { + self.rgb_wallet_wrapper + .inflate(asset_id, inflation_amounts, fee_rate, min_confirmations) + } + pub(crate) fn rgb_issue_asset_cfa( &self, name: String, @@ -126,6 +137,28 @@ impl UnlockedAppState { .issue_asset_cfa(name, details, precision, amounts, file_path) } + #[allow(clippy::too_many_arguments)] + pub(crate) fn rgb_issue_asset_ifa( + &self, + ticker: String, + name: String, + precision: u8, + amounts: Vec, + inflation_amounts: Vec, + replace_rights_num: u8, + reject_list_url: Option, + ) -> Result { + self.rgb_wallet_wrapper.issue_asset_ifa( + ticker, + name, + precision, + amounts, + inflation_amounts, + replace_rights_num, + reject_list_url, + ) + } + pub(crate) fn rgb_issue_asset_nia( &self, ticker: String, @@ -430,6 +463,22 @@ impl RgbLibWalletWrapper { self.get_rgb_wallet().get_wallet_data() } + pub(crate) fn inflate( + &self, + asset_id: String, + inflation_amounts: Vec, + fee_rate: u64, + min_confirmations: u8, + ) -> Result { + self.get_rgb_wallet().inflate( + self.online.clone(), + asset_id, + inflation_amounts, + fee_rate, + min_confirmations, + ) + } + pub(crate) fn issue_asset_cfa( &self, name: String, @@ -442,6 +491,28 @@ impl RgbLibWalletWrapper { .issue_asset_cfa(name, details, precision, amounts, file_path) } + #[allow(clippy::too_many_arguments)] + pub(crate) fn issue_asset_ifa( + &self, + ticker: String, + name: String, + precision: u8, + amounts: Vec, + inflation_amounts: Vec, + replace_rights_num: u8, + reject_list_url: Option, + ) -> Result { + self.get_rgb_wallet().issue_asset_ifa( + ticker, + name, + precision, + amounts, + inflation_amounts, + replace_rights_num, + reject_list_url, + ) + } + pub(crate) fn issue_asset_nia( &self, ticker: String, diff --git a/src/routes.rs b/src/routes.rs index e405325d..6a4765e6 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -51,9 +51,9 @@ use rgb_lib::{ check_indexer_url as rgb_lib_check_indexer_url, IndexerProtocol as RgbLibIndexerProtocol, }, - AssetCFA as RgbLibAssetCFA, AssetNIA as RgbLibAssetNIA, AssetUDA as RgbLibAssetUDA, - Balance as RgbLibBalance, EmbeddedMedia as RgbLibEmbeddedMedia, Invoice as RgbLibInvoice, - Media as RgbLibMedia, ProofOfReserves as RgbLibProofOfReserves, + AssetCFA as RgbLibAssetCFA, AssetIFA as RgbLibAssetIFA, AssetNIA as RgbLibAssetNIA, + AssetUDA as RgbLibAssetUDA, Balance as RgbLibBalance, EmbeddedMedia as RgbLibEmbeddedMedia, + Invoice as RgbLibInvoice, Media as RgbLibMedia, ProofOfReserves as RgbLibProofOfReserves, Recipient as RgbLibRecipient, RecipientInfo, RecipientType as RgbLibRecipientType, Token as RgbLibToken, TokenLight as RgbLibTokenLight, WitnessData as RgbLibWitnessData, }, @@ -171,6 +171,43 @@ impl From for AssetCFA { } } +#[derive(Deserialize, Serialize)] +pub(crate) struct AssetIFA { + pub(crate) asset_id: String, + pub(crate) ticker: String, + pub(crate) name: String, + pub(crate) details: Option, + pub(crate) precision: u8, + pub(crate) initial_supply: u64, + pub(crate) max_supply: u64, + pub(crate) known_circulating_supply: u64, + pub(crate) timestamp: i64, + pub(crate) added_at: i64, + pub(crate) balance: AssetBalanceResponse, + pub(crate) media: Option, + pub(crate) reject_list_url: Option, +} + +impl From for AssetIFA { + fn from(value: RgbLibAssetIFA) -> Self { + Self { + asset_id: value.asset_id, + ticker: value.ticker, + name: value.name, + details: value.details, + precision: value.precision, + initial_supply: value.initial_supply, + max_supply: value.max_supply, + known_circulating_supply: value.known_circulating_supply, + timestamp: value.timestamp, + added_at: value.added_at, + balance: value.balance.into(), + media: value.media.map(|m| m.into()), + reject_list_url: value.reject_list_url, + } + } +} + #[derive(Deserialize, Serialize)] pub(crate) struct AssetMetadataRequest { pub(crate) asset_id: String, @@ -226,6 +263,7 @@ pub(crate) enum AssetSchema { Nia, Uda, Cfa, + Ifa, } impl From for RgbLibAssetSchema { @@ -234,6 +272,7 @@ impl From for RgbLibAssetSchema { AssetSchema::Nia => Self::Nia, AssetSchema::Uda => Self::Uda, AssetSchema::Cfa => Self::Cfa, + AssetSchema::Ifa => Self::Ifa, } } } @@ -244,7 +283,7 @@ impl From for AssetSchema { RgbLibAssetSchema::Nia => Self::Nia, RgbLibAssetSchema::Uda => Self::Uda, RgbLibAssetSchema::Cfa => Self::Cfa, - RgbLibAssetSchema::Ifa => todo!(), + RgbLibAssetSchema::Ifa => Self::Ifa, } } } @@ -598,6 +637,19 @@ impl From for IndexerProtocol { } } +#[derive(Deserialize, Serialize)] +pub(crate) struct InflateRequest { + pub(crate) asset_id: String, + pub(crate) inflation_amounts: Vec, + pub(crate) fee_rate: u64, + pub(crate) min_confirmations: u8, +} + +#[derive(Deserialize, Serialize)] +pub(crate) struct InflateResponse { + pub(crate) txid: String, +} + #[derive(Deserialize, Serialize)] pub(crate) struct InitRequest { pub(crate) password: String, @@ -641,6 +693,22 @@ pub(crate) struct IssueAssetCFAResponse { pub(crate) asset: AssetCFA, } +#[derive(Deserialize, Serialize)] +pub(crate) struct IssueAssetIFARequest { + pub(crate) amounts: Vec, + pub(crate) inflation_amounts: Vec, + pub(crate) ticker: String, + pub(crate) name: String, + pub(crate) precision: u8, + pub(crate) replace_rights_num: u8, + pub(crate) reject_list_url: Option, +} + +#[derive(Deserialize, Serialize)] +pub(crate) struct IssueAssetIFAResponse { + pub(crate) asset: AssetIFA, +} + #[derive(Deserialize, Serialize)] pub(crate) struct IssueAssetNIARequest { pub(crate) amounts: Vec, @@ -694,6 +762,7 @@ pub(crate) struct ListAssetsResponse { pub(crate) nia: Option>, pub(crate) uda: Option>, pub(crate) cfa: Option>, + pub(crate) ifa: Option>, } #[derive(Deserialize, Serialize)] @@ -1876,6 +1945,37 @@ pub(crate) async fn get_swap( Err(APIError::SwapNotFound(payload.payment_hash)) } +pub(crate) async fn inflate( + State(state): State>, + WithRejection(Json(payload), _): WithRejection, APIError>, +) -> Result, APIError> { + no_cancel(async move { + let guard = state.check_unlocked().await?; + let unlocked_state = guard.as_ref().unwrap(); + + if *unlocked_state.rgb_send_lock.lock().unwrap() { + return Err(APIError::OpenChannelInProgress); + } + + let unlocked_state_copy = unlocked_state.clone(); + let inflate_result = tokio::task::spawn_blocking(move || { + unlocked_state_copy.rgb_inflate( + payload.asset_id, + payload.inflation_amounts, + payload.fee_rate, + payload.min_confirmations, + ) + }) + .await + .unwrap()?; + + Ok(Json(InflateResponse { + txid: inflate_result.txid, + })) + }) + .await +} + pub(crate) async fn init( State(state): State>, WithRejection(Json(payload), _): WithRejection, APIError>, @@ -1963,6 +2063,35 @@ pub(crate) async fn issue_asset_cfa( .await } +pub(crate) async fn issue_asset_ifa( + State(state): State>, + WithRejection(Json(payload), _): WithRejection, APIError>, +) -> Result, APIError> { + no_cancel(async move { + let guard = state.check_unlocked().await?; + let unlocked_state = guard.as_ref().unwrap(); + + if *unlocked_state.rgb_send_lock.lock().unwrap() { + return Err(APIError::OpenChannelInProgress); + } + + let asset = unlocked_state.rgb_issue_asset_ifa( + payload.ticker, + payload.name, + payload.precision, + payload.amounts, + payload.inflation_amounts, + payload.replace_rights_num, + payload.reject_list_url, + )?; + + Ok(Json(IssueAssetIFAResponse { + asset: asset.into(), + })) + }) + .await +} + pub(crate) async fn issue_asset_nia( State(state): State>, WithRejection(Json(payload), _): WithRejection, APIError>, @@ -2204,8 +2333,21 @@ pub(crate) async fn list_assets( }) .collect() }); + let ifa = rgb_assets.ifa.map(|assets| { + assets + .into_iter() + .map(|a| { + let mut asset: AssetIFA = a.into(); + ( + asset.balance.offchain_outbound, + asset.balance.offchain_inbound, + ) = *offchain_balances.get(&asset.asset_id).unwrap_or(&(0, 0)); + asset + }) + .collect() + }); - Ok(Json(ListAssetsResponse { nia, uda, cfa })) + Ok(Json(ListAssetsResponse { nia, uda, cfa, ifa })) } pub(crate) async fn list_channels( @@ -3149,11 +3291,10 @@ pub(crate) async fn open_channel( .rgb_get_asset_metadata(*contract_id)? .asset_schema; let assignment = match schema { - RgbLibAssetSchema::Nia | RgbLibAssetSchema::Cfa => { + RgbLibAssetSchema::Nia | RgbLibAssetSchema::Cfa | RgbLibAssetSchema::Ifa => { Assignment::Fungible(*asset_amount) } RgbLibAssetSchema::Uda => Assignment::NonFungible, - RgbLibAssetSchema::Ifa => todo!(), }; let recipient_map = map! { diff --git a/src/test/inflate.rs b/src/test/inflate.rs new file mode 100644 index 00000000..a06c3b1c --- /dev/null +++ b/src/test/inflate.rs @@ -0,0 +1,24 @@ +use super::*; + +const TEST_DIR_BASE: &str = "tmp/inflate/"; + +#[serial_test::serial] +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +#[traced_test] +async fn success() { + initialize(); + + let test_dir_base = format!("{TEST_DIR_BASE}success/"); + let test_dir_node1 = format!("{test_dir_base}node1"); + let (node1_addr, _) = start_node(&test_dir_node1, NODE1_PEER_PORT, false).await; + + fund_and_create_utxos(node1_addr, None).await; + + let asset_id = issue_asset_ifa(node1_addr).await.asset_id; + assert_eq!(asset_balance_spendable(node1_addr, &asset_id).await, 1000); + + inflate(node1_addr, &asset_id, 500).await; + mine(false); + refresh_transfers(node1_addr).await; + assert_eq!(asset_balance_spendable(node1_addr, &asset_id).await, 1500); +} diff --git a/src/test/issue.rs b/src/test/issue.rs index 8a9592c2..f95560c3 100644 --- a/src/test/issue.rs +++ b/src/test/issue.rs @@ -26,21 +26,26 @@ async fn issue() { // issue assets let asset_cfa = issue_asset_cfa(node1_addr, Some(file_path)).await; + let asset_ifa = issue_asset_ifa(node1_addr).await; let asset_nia = issue_asset_nia(node1_addr).await; let asset_uda = issue_asset_uda(node1_addr, Some(file_path)).await; // check /listassets let assets = list_assets(node1_addr).await; let assets_cfa = assets.cfa.unwrap(); + let assets_ifa = assets.ifa.unwrap(); let assets_nia = assets.nia.unwrap(); let assets_uda = assets.uda.unwrap(); assert_eq!(assets_cfa.len(), 1); + assert_eq!(assets_ifa.len(), 1); assert_eq!(assets_nia.len(), 1); assert_eq!(assets_uda.len(), 1); let cfa_asset = assets_cfa.first().unwrap(); + let ifa_asset = assets_ifa.first().unwrap(); let nia_asset = assets_nia.first().unwrap(); let uda_asset = assets_uda.first().unwrap(); assert_eq!(cfa_asset.asset_id, asset_cfa.asset_id); + assert_eq!(ifa_asset.asset_id, asset_ifa.asset_id); assert_eq!(nia_asset.asset_id, asset_nia.asset_id); assert_eq!(uda_asset.asset_id, asset_uda.asset_id); diff --git a/src/test/mod.rs b/src/test/mod.rs index 7b29c35a..7b57051a 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -23,15 +23,16 @@ use tracing_test::traced_test; use crate::error::APIErrorResponse; use crate::ldk::FEE_RATE; use crate::routes::{ - AddressResponse, AssetBalanceRequest, AssetBalanceResponse, AssetCFA, AssetNIA, AssetUDA, - Assignment, BackupRequest, BtcBalanceRequest, BtcBalanceResponse, ChangePasswordRequest, - Channel, CloseChannelRequest, ConnectPeerRequest, CreateUtxosRequest, DecodeLNInvoiceRequest, - DecodeLNInvoiceResponse, DecodeRGBInvoiceRequest, DecodeRGBInvoiceResponse, - DisconnectPeerRequest, EmptyResponse, FailTransfersRequest, FailTransfersResponse, - GetAssetMediaRequest, GetAssetMediaResponse, GetChannelIdRequest, GetChannelIdResponse, - GetPaymentRequest, GetPaymentResponse, GetSwapRequest, GetSwapResponse, HTLCStatus, - InitRequest, InitResponse, InvoiceStatus, InvoiceStatusRequest, InvoiceStatusResponse, - IssueAssetCFARequest, IssueAssetCFAResponse, IssueAssetNIARequest, IssueAssetNIAResponse, + AddressResponse, AssetBalanceRequest, AssetBalanceResponse, AssetCFA, AssetIFA, AssetNIA, + AssetUDA, Assignment, BackupRequest, BtcBalanceRequest, BtcBalanceResponse, + ChangePasswordRequest, Channel, CloseChannelRequest, ConnectPeerRequest, CreateUtxosRequest, + DecodeLNInvoiceRequest, DecodeLNInvoiceResponse, DecodeRGBInvoiceRequest, + DecodeRGBInvoiceResponse, DisconnectPeerRequest, EmptyResponse, FailTransfersRequest, + FailTransfersResponse, GetAssetMediaRequest, GetAssetMediaResponse, GetChannelIdRequest, + GetChannelIdResponse, GetPaymentRequest, GetPaymentResponse, GetSwapRequest, GetSwapResponse, + HTLCStatus, InflateRequest, InflateResponse, InitRequest, InitResponse, InvoiceStatus, + InvoiceStatusRequest, InvoiceStatusResponse, IssueAssetCFARequest, IssueAssetCFAResponse, + IssueAssetIFARequest, IssueAssetIFAResponse, IssueAssetNIARequest, IssueAssetNIAResponse, IssueAssetUDARequest, IssueAssetUDAResponse, KeysendRequest, KeysendResponse, LNInvoiceRequest, LNInvoiceResponse, ListAssetsRequest, ListAssetsResponse, ListChannelsResponse, ListPaymentsResponse, ListPeersResponse, ListSwapsResponse, ListTransactionsRequest, @@ -574,6 +575,27 @@ async fn get_channel_id(node_address: SocketAddr, temp_chan_id: &str) -> String .channel_id } +async fn inflate(node_address: SocketAddr, asset_id: &str, inflation_amount: u64) { + println!("inflating asset {asset_id} by {inflation_amount}"); + let payload = InflateRequest { + asset_id: asset_id.to_string(), + inflation_amounts: vec![inflation_amount], + fee_rate: FEE_RATE, + min_confirmations: 1, + }; + let res = reqwest::Client::new() + .post(format!("http://{node_address}/inflate")) + .json(&payload) + .send() + .await + .unwrap(); + _check_response_is_ok(res) + .await + .json::() + .await + .unwrap(); +} + async fn invoice_status(node_address: SocketAddr, invoice: &str) -> InvoiceStatus { println!("getting status of invoice {invoice} for node {node_address}"); let payload = InvoiceStatusRequest { @@ -620,6 +642,31 @@ async fn issue_asset_cfa(node_address: SocketAddr, file_path: Option<&str>) -> A .asset } +async fn issue_asset_ifa(node_address: SocketAddr) -> AssetIFA { + println!("issuing IFA asset on node {node_address}"); + let payload = IssueAssetIFARequest { + amounts: vec![1000], + inflation_amounts: vec![2000], + ticker: s!("USDT"), + name: s!("Tether"), + precision: 0, + replace_rights_num: 0, + reject_list_url: None, + }; + let res = reqwest::Client::new() + .post(format!("http://{node_address}/issueassetifa")) + .json(&payload) + .send() + .await + .unwrap(); + _check_response_is_ok(res) + .await + .json::() + .await + .unwrap() + .asset +} + async fn issue_asset_nia(node_address: SocketAddr) -> AssetNIA { println!("issuing NIA asset on node {node_address}"); let payload = IssueAssetNIARequest { @@ -1885,6 +1932,7 @@ mod concurrent_openchannel; mod fail_transfers; mod getchannelid; mod htlc_amount_checks; +mod inflate; mod init; mod invoice; mod issue;