diff --git a/src/outgoing.rs b/src/outgoing.rs index fef42322b8..c9ae6de2b1 100644 --- a/src/outgoing.rs +++ b/src/outgoing.rs @@ -5,6 +5,7 @@ pub enum Outgoing { Amount(Amount), InscriptionId(InscriptionId), SatPoint(SatPoint), + Sat(Sat), Rune { decimal: Decimal, rune: SpacedRune }, } @@ -13,6 +14,7 @@ impl Display for Outgoing { match self { Self::Amount(amount) => write!(f, "{}", amount.to_string().to_lowercase()), Self::InscriptionId(inscription_id) => inscription_id.fmt(f), + Self::Sat(sat) => sat.fmt(f), Self::SatPoint(satpoint) => satpoint.fmt(f), Self::Rune { decimal, rune } => write!(f, "{decimal} {rune}"), } @@ -61,7 +63,9 @@ impl FromStr for Outgoing { .unwrap(); } - Ok(if re::SATPOINT.is_match(s) { + Ok(if s.parse::().is_ok() { + Self::Sat(s.parse()?) + } else if re::SATPOINT.is_match(s) { Self::SatPoint(s.parse()?) } else if re::INSCRIPTION_ID.is_match(s) { Self::InscriptionId(s.parse()?) @@ -107,6 +111,12 @@ mod tests { assert_eq!(s.parse::().unwrap(), outgoing); } + case("0", Outgoing::Sat("0".parse().unwrap())); + case( + "2099999997689999", + Outgoing::Sat("2099999997689999".parse().unwrap()), + ); + case( "0000000000000000000000000000000000000000000000000000000000000000i0", Outgoing::InscriptionId( @@ -177,8 +187,6 @@ mod tests { decimal: "1.1".parse().unwrap(), }, ); - - assert!("0".parse::().is_err()); } #[test] @@ -189,6 +197,12 @@ mod tests { assert_eq!(s, outgoing.to_string()); } + case("0", Outgoing::Sat("0".parse().unwrap())); + case( + "2099999997689999", + Outgoing::Sat("2099999997689999".parse().unwrap()), + ); + case( "0000000000000000000000000000000000000000000000000000000000000000i0", Outgoing::InscriptionId( @@ -236,6 +250,13 @@ mod tests { assert_eq!(serde_json::from_str::(j).unwrap(), o); } + case("0", "\"0\"", Outgoing::Sat("0".parse().unwrap())); + case( + "2099999997689999", + "\"2099999997689999\"", + Outgoing::Sat("2099999997689999".parse().unwrap()), + ); + case( "0000000000000000000000000000000000000000000000000000000000000000i0", "\"0000000000000000000000000000000000000000000000000000000000000000i0\"", diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index ca930d8e87..72bd5fe399 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -66,6 +66,14 @@ impl Send { self.fee_rate, false, )?, + Outgoing::Sat(sat) => Self::create_unsigned_send_satpoint_transaction( + &wallet, + address, + wallet.find_sat_in_outputs(sat)?, + self.postage, + self.fee_rate, + true, + )?, }; let unspent_outputs = wallet.utxos(); diff --git a/tests/lib.rs b/tests/lib.rs index fb0c7a96b2..16842f5545 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -111,6 +111,19 @@ fn receive( .unwrap() } +fn sats( + bitcoin_rpc_server: &test_bitcoincore_rpc::Handle, + ord_rpc_server: &TestServer, +) -> Vec { + CommandBuilder::new(format!( + "--chain {} wallet sats", + bitcoin_rpc_server.network() + )) + .bitcoin_rpc_server(bitcoin_rpc_server) + .ord_rpc_server(ord_rpc_server) + .run_and_deserialize_output::>() +} + fn inscribe( bitcoin_rpc_server: &test_bitcoincore_rpc::Handle, ord_rpc_server: &TestServer, diff --git a/tests/wallet/send.rs b/tests/wallet/send.rs index 4b6dd95b7f..b99bb9de2a 100644 --- a/tests/wallet/send.rs +++ b/tests/wallet/send.rs @@ -68,7 +68,7 @@ fn send_unknown_inscription() { } #[test] -fn send_inscribed_sat() { +fn send_inscribed_inscription() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); @@ -100,6 +100,67 @@ fn send_inscribed_sat() { ); } +#[test] +fn send_uninscribed_sat() { + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &[]); + + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); + + let sat = 1; + + CommandBuilder::new(format!( + "wallet send --fee-rate 1 bc1qcqgs2pps4u4yedfyl5pysdjjncs8et5utseepv {sat}" + )) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) + .expected_stderr(format!( + "error: could not find sat `{sat}` in wallet outputs\n" + )) + .expected_exit_code(1) + .run_and_extract_stdout(); +} + +#[test] +fn send_inscription_by_sat() { + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &[]); + + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); + + let (inscription, txid) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); + + let sat_list = sats(&bitcoin_rpc_server, &ord_rpc_server); + + let sat = sat_list.iter().find(|s| s.output.txid == txid).unwrap().sat; + + let address = "bc1qcqgs2pps4u4yedfyl5pysdjjncs8et5utseepv"; + + let output = CommandBuilder::new(format!("wallet send --fee-rate 1 {address} {sat}")) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) + .run_and_deserialize_output::(); + + bitcoin_rpc_server.mine_blocks(1); + + let send_txid = output.txid; + + ord_rpc_server.assert_response_regex( + format!("/inscription/{inscription}"), + format!( + ".*

Inscription 0

.*
address
.*
{address}
.*
location
.*
{send_txid}:0:0
.*", + ), + ); +} + #[test] fn send_on_mainnnet_works_with_wallet_named_foo() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn();