Skip to content

Commit 657c2a2

Browse files
committed
feat(wallet): implement RGB coloring for PSBTs and on-chain swap tests
- Added `color_psbt_stage` and `color_psbt_finalize` methods to handle RGB coloring on PSBTs. - Updated `color_psbt` to utilize the new methods and return asset beneficiaries. - Introduced `generate_transfer` method for creating RGB transfers based on beneficiaries. - Refactored PSBT signing logic to be publicly accessible. - Added tests for on-chain swaps involving RGB assets, covering various scenarios for maker and taker balances. - Included assertions to verify BTC and asset balances post-swap.
1 parent 5a32c9e commit 657c2a2

11 files changed

Lines changed: 3641 additions & 43 deletions

File tree

bindings/uniffi/src/lib.rs

Lines changed: 129 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@ use rgb_lib::{
1919
InvoiceData as RgbLibInvoiceData, Media, Metadata, MultisigKeys, MultisigOnlineOptions,
2020
MultisigVotingStatus as RgbLibMultisigVotingStatus, MultisigWallet as RgbLibMultisigWallet,
2121
Online, OnlineOptions, Operation as RgbLibOperation, OperationInfo as RgbLibOperationInfo,
22-
OperationResult, Outpoint, PendingVanillaTx, ProofOfReserves, PsbtInputInfo,
23-
PsbtInspection, PsbtOutputInfo, ReceiveData, Recipient as RgbLibRecipient,
22+
OnchainSwapAssetHistory, OnchainSwapCompletion, OnchainSwapConsignment, OnchainSwapInput,
23+
OnchainSwapLeg, OnchainSwapLegKind, OnchainSwapOffer, OnchainSwapProposal,
24+
OnchainSwapReceiveResult as RgbLibOnchainSwapReceiveResult, OnchainSwapRequest,
25+
OnchainSwapRole, OperationResult, Outpoint, PendingVanillaTx, ProofOfReserves,
26+
PsbtInputInfo, PsbtInspection, PsbtOutputInfo, ReceiveData, Recipient as RgbLibRecipient,
2427
RecipientInfo as RgbLibRecipientInfo, RecipientType, RefreshFilter, RefreshTransferStatus,
2528
RefreshedTransfer, RespondToOperation as RgbLibRespondToOperation,
2629
RgbAllocation as RgbLibRgbAllocation, RgbInputInfo as RgbLibRgbInputInfo,
@@ -98,6 +101,16 @@ impl From<Assignment> for RgbLibAssignment {
98101
}
99102
}
100103
}
104+
pub struct OnchainSwapReceiveResult {
105+
pub assignments: Vec<Assignment>,
106+
}
107+
impl From<RgbLibOnchainSwapReceiveResult> for OnchainSwapReceiveResult {
108+
fn from(orig: RgbLibOnchainSwapReceiveResult) -> Self {
109+
Self {
110+
assignments: orig.assignments.into_iter().map(|a| a.into()).collect(),
111+
}
112+
}
113+
}
101114
pub struct InvoiceData {
102115
pub recipient_id: String,
103116
pub asset_schema: Option<AssetSchema>,
@@ -1390,6 +1403,86 @@ impl Wallet {
13901403
self._get_wallet().send_btc_end(online, signed_psbt)
13911404
}
13921405

1406+
fn create_swap_offer(
1407+
&self,
1408+
maker_gives: OnchainSwapLeg,
1409+
maker_receives: OnchainSwapLeg,
1410+
network_fee_sat: u64,
1411+
expiration_timestamp: Option<u64>,
1412+
proxy_url: Option<String>,
1413+
) -> Result<OnchainSwapOffer, RgbLibError> {
1414+
self._get_wallet().create_swap_offer(
1415+
maker_gives,
1416+
maker_receives,
1417+
network_fee_sat,
1418+
expiration_timestamp,
1419+
proxy_url,
1420+
)
1421+
}
1422+
1423+
fn accept_swap_offer(
1424+
&self,
1425+
online: Online,
1426+
offer: OnchainSwapOffer,
1427+
min_confirmations: u8,
1428+
skip_sync: bool,
1429+
) -> Result<OnchainSwapRequest, RgbLibError> {
1430+
self._get_wallet()
1431+
.accept_swap_offer(online, offer, min_confirmations, skip_sync)
1432+
}
1433+
1434+
fn accept_swap_request(
1435+
&self,
1436+
online: Online,
1437+
request: OnchainSwapRequest,
1438+
min_confirmations: u8,
1439+
skip_sync: bool,
1440+
) -> Result<OnchainSwapProposal, RgbLibError> {
1441+
self._get_wallet()
1442+
.accept_swap_request(online, request, min_confirmations, skip_sync)
1443+
}
1444+
1445+
fn complete_swap_proposal(
1446+
&self,
1447+
online: Online,
1448+
proposal: OnchainSwapProposal,
1449+
min_confirmations: u8,
1450+
skip_sync: bool,
1451+
) -> Result<OnchainSwapCompletion, RgbLibError> {
1452+
self._get_wallet()
1453+
.complete_swap_proposal(online, proposal, min_confirmations, skip_sync)
1454+
}
1455+
1456+
fn process_swap_completion(
1457+
&self,
1458+
online: Online,
1459+
completion: OnchainSwapCompletion,
1460+
) -> Result<OnchainSwapCompletion, RgbLibError> {
1461+
self._get_wallet().process_swap_completion(online, completion)
1462+
}
1463+
1464+
fn broadcast_swap_completion(
1465+
&self,
1466+
online: Online,
1467+
completion: OnchainSwapCompletion,
1468+
) -> Result<String, RgbLibError> {
1469+
self._get_wallet()
1470+
.broadcast_swap_completion(online, completion)
1471+
}
1472+
1473+
fn accept_swap_transfers(
1474+
&self,
1475+
online: Online,
1476+
completion: OnchainSwapCompletion,
1477+
role: OnchainSwapRole,
1478+
skip_sync: bool,
1479+
) -> Result<OnchainSwapReceiveResult, RgbLibError> {
1480+
Ok(self
1481+
._get_wallet()
1482+
.accept_swap_transfers(online, completion, role, skip_sync)?
1483+
.into())
1484+
}
1485+
13931486
fn sync(&self, online: Online, options: SyncOptions) -> Result<(), RgbLibError> {
13941487
self._get_wallet().sync(online, options.into())
13951488
}
@@ -1812,3 +1905,37 @@ impl MultisigWallet {
18121905
}
18131906

18141907
uniffi::deps::static_assertions::assert_impl_all!(MultisigWallet: Sync, Send);
1908+
1909+
#[cfg(test)]
1910+
mod tests {
1911+
const UDL: &str = include_str!("rgb-lib.udl");
1912+
1913+
#[test]
1914+
fn uniffi_exposes_swap_api_without_raw_rgb_primitives() {
1915+
for method in [
1916+
"create_swap_offer",
1917+
"accept_swap_offer",
1918+
"accept_swap_request",
1919+
"complete_swap_proposal",
1920+
"process_swap_completion",
1921+
"broadcast_swap_completion",
1922+
"accept_swap_transfers",
1923+
] {
1924+
assert!(UDL.contains(method), "missing {method} from UDL");
1925+
}
1926+
1927+
for raw_name in [
1928+
"color_psbt",
1929+
"color_psbt_and_consume",
1930+
"consume_fascia",
1931+
"Fascia",
1932+
"RgbTransfer",
1933+
"BuilderSeal",
1934+
] {
1935+
assert!(
1936+
!UDL.contains(raw_name),
1937+
"raw primitive {raw_name} must not be exposed in UDL"
1938+
);
1939+
}
1940+
}
1941+
}

bindings/uniffi/src/rgb-lib.udl

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,103 @@ dictionary RgbInspection {
768768
sequence<RgbOperationInfo> operations;
769769
};
770770

771+
[Remote]
772+
enum OnchainSwapRole {
773+
"Maker",
774+
"Taker",
775+
};
776+
777+
[Remote]
778+
enum OnchainSwapLegKind {
779+
"Btc",
780+
"Rgb",
781+
};
782+
783+
[Remote]
784+
dictionary OnchainSwapLeg {
785+
OnchainSwapLegKind kind;
786+
string? asset_id;
787+
u64 amount;
788+
};
789+
790+
[Remote]
791+
dictionary OnchainSwapInput {
792+
Outpoint outpoint;
793+
u64 amount_sat;
794+
string script_pubkey_hex;
795+
};
796+
797+
[Remote]
798+
dictionary OnchainSwapConsignment {
799+
string asset_id;
800+
string path;
801+
string? endpoint;
802+
string txid;
803+
u32 vout;
804+
u64 blinding;
805+
string recipient_id;
806+
};
807+
808+
[Remote]
809+
dictionary OnchainSwapAssetHistory {
810+
string asset_id;
811+
string path;
812+
string? endpoint;
813+
string recipient_id;
814+
};
815+
816+
[Remote]
817+
dictionary OnchainSwapOffer {
818+
string swap_id;
819+
OnchainSwapLeg maker_gives;
820+
OnchainSwapLeg maker_receives;
821+
BitcoinNetwork bitcoin_network;
822+
u64 network_fee_sat;
823+
u64 rgb_output_sat;
824+
u64? expiration_timestamp;
825+
string? maker_btc_address;
826+
string? maker_rgb_recipient_id;
827+
string? maker_rgb_script_pubkey_hex;
828+
u64? maker_rgb_blinding;
829+
string? proxy_url;
830+
};
831+
832+
[Remote]
833+
dictionary OnchainSwapRequest {
834+
OnchainSwapOffer offer;
835+
sequence<OnchainSwapInput> taker_inputs;
836+
string? taker_btc_address;
837+
string? taker_rgb_recipient_id;
838+
string? taker_rgb_script_pubkey_hex;
839+
u64? taker_rgb_blinding;
840+
string taker_change_script_pubkey_hex;
841+
};
842+
843+
[Remote]
844+
dictionary OnchainSwapProposal {
845+
OnchainSwapRequest request;
846+
sequence<OnchainSwapInput> maker_inputs;
847+
string maker_change_script_pubkey_hex;
848+
string psbt;
849+
string txid;
850+
sequence<OnchainSwapConsignment> consignments;
851+
OnchainSwapAssetHistory? maker_history;
852+
};
853+
854+
[Remote]
855+
dictionary OnchainSwapCompletion {
856+
OnchainSwapProposal proposal;
857+
string psbt;
858+
string? finalized_psbt;
859+
string txid;
860+
sequence<OnchainSwapConsignment> consignments;
861+
OnchainSwapAssetHistory? taker_history;
862+
};
863+
864+
dictionary OnchainSwapReceiveResult {
865+
sequence<Assignment> assignments;
866+
};
867+
771868
[Remote]
772869
dictionary InitOperationResult {
773870
string psbt;
@@ -979,6 +1076,39 @@ interface Wallet {
9791076
[Throws=RgbLibError]
9801077
string send_btc_end(Online online, string signed_psbt);
9811078

1079+
[Throws=RgbLibError]
1080+
OnchainSwapOffer create_swap_offer(
1081+
OnchainSwapLeg maker_gives, OnchainSwapLeg maker_receives,
1082+
u64 network_fee_sat, u64? expiration_timestamp, string? proxy_url);
1083+
1084+
[Throws=RgbLibError]
1085+
OnchainSwapRequest accept_swap_offer(
1086+
Online online, OnchainSwapOffer offer, u8 min_confirmations,
1087+
boolean skip_sync);
1088+
1089+
[Throws=RgbLibError]
1090+
OnchainSwapProposal accept_swap_request(
1091+
Online online, OnchainSwapRequest request, u8 min_confirmations,
1092+
boolean skip_sync);
1093+
1094+
[Throws=RgbLibError]
1095+
OnchainSwapCompletion complete_swap_proposal(
1096+
Online online, OnchainSwapProposal proposal, u8 min_confirmations,
1097+
boolean skip_sync);
1098+
1099+
[Throws=RgbLibError]
1100+
OnchainSwapCompletion process_swap_completion(
1101+
Online online, OnchainSwapCompletion completion);
1102+
1103+
[Throws=RgbLibError]
1104+
string broadcast_swap_completion(
1105+
Online online, OnchainSwapCompletion completion);
1106+
1107+
[Throws=RgbLibError]
1108+
OnchainSwapReceiveResult accept_swap_transfers(
1109+
Online online, OnchainSwapCompletion completion, OnchainSwapRole role,
1110+
boolean skip_sync);
1111+
9821112
[Throws=RgbLibError]
9831113
void sync(Online online, SyncOptions options);
9841114

src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,10 @@ use bdk_wallet::{
151151
#[cfg(any(feature = "electrum", feature = "esplora"))]
152152
use bdk_wallet::{
153153
Update,
154-
bitcoin::{Transaction as BdkTransaction, blockdata::fee_rate::FeeRate, hashes::HashEngine},
154+
bitcoin::{
155+
Sequence, Transaction as BdkTransaction, TxIn, Witness, absolute::LockTime,
156+
blockdata::fee_rate::FeeRate, hashes::HashEngine, transaction::Version as TxVersion,
157+
},
155158
chain::{
156159
DescriptorExt,
157160
spk_client::{FullScanRequest, FullScanResponse, SyncRequest, SyncResponse},
@@ -270,7 +273,7 @@ use crate::{
270273
error::IndexerError,
271274
utils::{
272275
INDEXER_STOP_GAP, OffchainResolver, check_proxy, get_indexer_and_resolver, hash_file,
273-
script_buf_from_recipient_id,
276+
recipient_id_from_script_buf, script_buf_from_recipient_id,
274277
},
275278
wallet::{AssignmentsCollection, Indexer, multisig::RespondToOperation},
276279
};

src/wallet/mod.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@ pub use objects::{
3535
};
3636
#[cfg(any(feature = "electrum", feature = "esplora"))]
3737
pub use objects::{
38-
BurnBeginResult, BurnDetails, InflateBeginResult, InflateDetails, OnlineOptions,
39-
OperationResult, RefreshFilter, RefreshResult, RefreshTransferStatus, RefreshedTransfer,
40-
SendBeginResult, SendDetails,
38+
BurnBeginResult, BurnDetails, InflateBeginResult, InflateDetails, OnchainSwapAssetHistory,
39+
OnchainSwapCompletion, OnchainSwapConsignment, OnchainSwapInput, OnchainSwapLeg,
40+
OnchainSwapLegKind, OnchainSwapOffer, OnchainSwapProposal, OnchainSwapReceiveResult,
41+
OnchainSwapRequest, OnchainSwapRole, OnlineOptions, OperationResult, RefreshFilter,
42+
RefreshResult, RefreshTransferStatus, RefreshedTransfer, SendBeginResult, SendDetails,
4143
};
4244
pub use offline::RgbWalletOpsOffline;
4345
#[cfg(any(feature = "electrum", feature = "esplora"))]

0 commit comments

Comments
 (0)