Skip to content

Instantly share code, notes, and snippets.

@al-f4lc0n
Created February 27, 2025 09:26
Show Gist options
  • Select an option

  • Save al-f4lc0n/25bd18b41484fab5e3a121df236bbc7d to your computer and use it in GitHub Desktop.

Select an option

Save al-f4lc0n/25bd18b41484fab5e3a121df236bbc7d to your computer and use it in GitHub Desktop.
use std::{thread, time::Duration};
use bitcoin::{ absolute, transaction::Version, Amount, OutPoint, ScriptBuf, Sequence, Transaction, TxIn, TxOut };
use bitcoin::consensus::encode::serialize_hex;
use bitcoincore_rpc::{json, Client as BitcoinClient, RpcApi as _};
use clarity::types::{chainstate::StacksAddress, Address as _};
use clarity::vm::types::{PrincipalData, StandardPrincipalData};
use emily_client::apis::configuration::{ApiKey, Configuration};
use emily_client::apis::deposit_api;
use emily_client::models::CreateDepositRequestBody;
use sbtc::deposits::{DepositScriptInputs, ReclaimScriptInputs};
use signer::config::Settings;
use signer::keys::{PrivateKey, PublicKey, SignerScriptPubKey};
use signer::stacks::api::{StacksClient, StacksInteract};
/* secret key:
spawn knee orchard patrol merge forget dust position daring short bridge elevator attitude leopard opera appear auction limit magic hover tunnel museum quantum manual
*/
const TEST_AUDITOR_STACKS_ADDR: &str = "ST2BEV097EV2R9ZMFRMRT904QB5RFYMA0683TC111";
const TEST_CONFIG_PATH: &str = "signer/src/config/default.toml";
const TEST_DEPLOYER_STACKS_ADDR: &str = "SN3R84XZYA63QS28932XQF3G1J8R9PC3W76P9CSQS";
const TEST_RECIPIENT_STACKS_ADDR: &str = "ST3497E9JFQ7KB9VEHAZRWYKF3296WQZEXBPXG193";
const TEST_DEPOSIT_MAX_FEE: u64 = 100000;
const TEST_DEPOSIT_LOCK_TIME: u32 = 10;
const TEST_BTC_TX_FEE: u64 = 200;
const SATOSHI_PER_BTC: u64 = 100000000;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
wait_sbtc_deployment().await?;
println!("┏ donation 10 BTC to aggregate signer BTC wallet");
donation_to_aggregate_key(10 * SATOSHI_PER_BTC).await?;
println!("┗ end");
println!("┏ deposit 1 sBTC to aggregate signer Stacks wallet");
deposit_sbtc_to(
TEST_AUDITOR_STACKS_ADDR,
SATOSHI_PER_BTC,
).await?;
println!("┗ end");
println!("┏ deposit 1 sBTC to test auditor Stacks wallet");
deposit_sbtc_to(
TEST_AUDITOR_STACKS_ADDR,
SATOSHI_PER_BTC,
).await?;
println!("┗ end");
Ok(())
}
async fn wait_sbtc_deployment() -> Result<(), Box<dyn std::error::Error>> {
println!("┏ waiting for sBTC contract deployment");
loop {
println!("┃ ...");
thread::sleep(Duration::from_secs(5));
match get_current_aggregate_key().await {
Err(_) => {}
Ok(_) => break
}
}
println!("┗ sBTC contract has been deployed");
Ok(())
}
fn get_emily_client_config() -> Result<Configuration, Box<dyn std::error::Error>> {
let settings = Settings::new(Some(TEST_CONFIG_PATH))?;
let emily_api_endpoint = settings.emily.endpoints.first().unwrap().clone();
Ok(Configuration {
base_path: emily_api_endpoint.to_string().trim_end_matches("/").to_string(),
api_key: Some(ApiKey {
prefix: None,
key: emily_api_endpoint.username().to_string(),
}),
..Default::default()
})
}
fn get_stacks_client() -> Result<StacksClient, Box<dyn std::error::Error>> {
let settings = Settings::new(Some(TEST_CONFIG_PATH))?;
let url = settings
.stacks
.endpoints
.first()
.expect("No Stacks endpoints configured");
Ok(StacksClient::new(url.clone()).expect("Failed to create Stacks client"))
}
fn get_btc_client() -> Result<BitcoinClient, Box<dyn std::error::Error>> {
let settings = Settings::new(Some(TEST_CONFIG_PATH))?;
let bitcoin_url = format!(
"{}wallet/depositor",
settings
.bitcoin
.rpc_endpoints
.first()
.expect("No Bitcoin RPC endpoints configured")
);
let btc_client = BitcoinClient::new(
&bitcoin_url,
bitcoincore_rpc::Auth::UserPass("devnet".into(), "devnet".into()),
).expect("Failed to create Bitcoin RPC client");
Ok(btc_client)
}
async fn get_current_aggregate_key() -> Result<PublicKey, Box<dyn std::error::Error>> {
let stacks_client = get_stacks_client()?;
let deployer = PrincipalData::parse_standard_principal(&TEST_DEPLOYER_STACKS_ADDR)
.map(StacksAddress::from)?;
match stacks_client.get_current_signers_aggregate_key(&deployer).await? {
Some(aggregate_key) => Ok(aggregate_key),
_ => Err(Box::from("no aggregate key")),
}
}
async fn donation_to_aggregate_key(
amount: u64,
) -> Result<(), Box<dyn std::error::Error>> {
let aggregate_key = get_current_aggregate_key().await?;
let btc_client = get_btc_client()?;
let unsigned_tx = get_transaction_from_btc_wallet(
&btc_client,
TxOut {
value: Amount::from_sat(amount),
script_pubkey: aggregate_key.signers_script_pubkey(),
},
Amount::from_sat(TEST_BTC_TX_FEE),
)?;
let signed_tx = btc_client.sign_raw_transaction_with_wallet(&unsigned_tx, None, None)?;
let txid = btc_client.send_raw_transaction(&signed_tx.hex)?;
println!("┃ btc tx sent: {:?}", txid);
Ok(())
}
async fn deposit_sbtc_to(
recipient: &str,
amount: u64,
) -> Result<(), Box<dyn std::error::Error>> {
let aggregate_key = get_current_aggregate_key().await?;
let deposit_script = DepositScriptInputs {
signers_public_key: aggregate_key.into(),
max_fee: TEST_DEPOSIT_MAX_FEE,
recipient: PrincipalData::Standard(StandardPrincipalData::from(
StacksAddress::from_string(recipient).unwrap(),
)),
};
let reclaim_script = ReclaimScriptInputs::try_new(TEST_DEPOSIT_LOCK_TIME, ScriptBuf::new())?;
let btc_client = get_btc_client()?;
let unsigned_tx = get_transaction_from_btc_wallet(
&btc_client,
TxOut {
value: Amount::from_sat(amount),
script_pubkey: sbtc::deposits::to_script_pubkey(
deposit_script.deposit_script(),
reclaim_script.reclaim_script(),
),
},
Amount::from_sat(TEST_BTC_TX_FEE),
)?;
let signed_tx = btc_client.sign_raw_transaction_with_wallet(&unsigned_tx, None, None)?;
let txid = btc_client.send_raw_transaction(&signed_tx.hex)?;
println!("┃ btc tx sent: {:?}", txid);
let emily_client_config = get_emily_client_config()?;
let emily_deposit = deposit_api::create_deposit(
&emily_client_config,
CreateDepositRequestBody {
bitcoin_tx_output_index: 0,
bitcoin_txid: txid.to_string(),
deposit_script: deposit_script.deposit_script().to_hex_string(),
reclaim_script: reclaim_script.reclaim_script().to_hex_string(),
transaction_hex: serialize_hex(&unsigned_tx),
},
).await?;
println!("┃ deposit request created: {:?}", emily_deposit);
Ok(())
}
fn get_transaction_from_btc_wallet(
bitcoin_client: &BitcoinClient,
tx_out: TxOut,
fee: Amount,
) -> Result<Transaction, Box<dyn std::error::Error>> {
let amount = tx_out.value;
let opts = json::ListUnspentQueryOptions {
minimum_amount: Some(amount + fee),
..Default::default()
};
let unspent = bitcoin_client
.list_unspent(Some(1), None, None, None, Some(opts))?
.into_iter()
.next()
.unwrap();
Ok(Transaction {
input: vec![TxIn {
previous_output: OutPoint {
txid: unspent.txid,
vout: unspent.vout,
},
script_sig: Default::default(),
sequence: Sequence::ZERO,
witness: Default::default(),
}],
output: vec![
tx_out,
TxOut {
value: unspent.amount - amount - fee,
script_pubkey: unspent.script_pub_key,
},
],
version: Version::TWO,
lock_time: absolute::LockTime::ZERO,
})
}
@al-f4lc0n
Copy link
Author

An insignificant patch.

--- a/signer/src/bin/pocinit.rs
+++ b/signer/src/bin/pocinit.rs
@@ -38,7 +38,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

     println!("┏ deposit 1 sBTC to aggregate signer Stacks wallet");
     deposit_sbtc_to(
-        TEST_AUDITOR_STACKS_ADDR,
+        TEST_DEPLOYER_STACKS_ADDR,
         SATOSHI_PER_BTC,
     ).await?;
     println!("┗ end");

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment