Skip to content

Commit

Permalink
feat: etch example
Browse files Browse the repository at this point in the history
  • Loading branch information
veeso committed Jul 29, 2024
1 parent 1ec300f commit 184f02a
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 7 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,8 @@ path = "examples/print_script.rs"
name = "edict"
path = "examples/edict.rs"
required-features = ["rune"]

[[example]]
name = "etch"
path = "examples/etch.rs"
required-features = ["rune"]
188 changes: 188 additions & 0 deletions examples/etch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
mod utils;

use std::str::FromStr as _;

use argh::FromArgs;
use bitcoin::secp256k1::Secp256k1;
use bitcoin::{Address, Network, PrivateKey};
use log::{debug, info};
use ord_rs::wallet::{
CreateCommitTransactionArgsV2, RevealTransactionArgs, Runestone, SignCommitTransactionArgs,
};
use ord_rs::{Nft, OrdTransactionBuilder};
use ordinals::{Etching, Rune, Terms};
use utils::rpc_client;

use crate::utils::{calc_fees, Fees};

#[derive(FromArgs, Debug)]
#[argh(description = "Etch a runestone")]
struct Args {
#[argh(option, short = 'a')]
/// amount
amount: u64,

#[argh(option, short = 'l')]
/// amount
limit: u64,

#[argh(option, short = 'p')]
/// private key
private_key: String,

#[argh(positional)]
/// tx inputs
inputs: Vec<String>,

#[argh(option, short = 'n')]
/// network
network: String,

#[argh(option, short = 'N')]
/// rune name
rune_name: String,

#[argh(option, short = 's', default = "String::from(\"p2tr\")")]
/// script type (p2tr, p2wsh)
script_type: String,

#[argh(switch, short = 'd')]
/// dry run, don't send any transaction
dry_run: bool,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
env_logger::init();
let args: Args = argh::from_env();

let inputs = utils::parse_inputs(args.inputs);

let network = match args.network.as_str() {
"testnet" | "test" => Network::Testnet,
"mainnet" | "prod" => Network::Bitcoin,
_ => panic!("invalid network"),
};

let amount = args.amount;
let limit = args.limit;
let private_key = PrivateKey::from_wif(&args.private_key)?;
let public_key = private_key.public_key(&Secp256k1::new());
let sender_address = Address::p2wpkh(&public_key, network).unwrap();
debug!("sender address: {sender_address}");

let Fees {
commit_fee,
reveal_fee,
..
} = calc_fees(network);
info!("Commit fee: {commit_fee}, reveal fee: {reveal_fee}",);

let inputs = rpc_client::sats_amount_from_tx_inputs(&inputs, network).await?;

debug!("getting commit transaction...");
let mut builder = match args.script_type.as_str() {
"p2tr" | "P2TR" => OrdTransactionBuilder::p2tr(private_key),
"p2wsh" | "P2WSH" => OrdTransactionBuilder::p2wsh(private_key),
_ => panic!("invalid script type"),
};

let etching = Etching {
rune: Some(Rune::from_str(&args.rune_name).unwrap()),
divisibility: Some(2),
premine: Some(1_000_000),
spacers: None,
symbol: Some('$'),
terms: Some(Terms {
amount: Some(amount as u128),
cap: Some(limit as u128),
height: (None, None),
offset: (None, None),
}),
turbo: true,
};

let mut inscription = Nft::new(
Some("text/plain;charset=utf-8".as_bytes().to_vec()),
Some(etching.rune.unwrap().to_string().as_bytes().to_vec()),
);
inscription.pointer = Some(vec![]);
inscription.rune = Some(
etching
.rune
.ok_or(anyhow::anyhow!("Invalid etching data; rune is missing"))?
.commitment(),
);

let commit_tx = builder.build_commit_transaction_with_fixed_fees(
network,
CreateCommitTransactionArgsV2 {
inputs: inputs.clone(),
inscription,
txin_script_pubkey: sender_address.script_pubkey(),
leftovers_recipient: sender_address.clone(),
commit_fee,
reveal_fee,
},
)?;
let signed_commit_tx = builder
.sign_commit_transaction(
commit_tx.unsigned_tx,
SignCommitTransactionArgs {
inputs,
txin_script_pubkey: sender_address.script_pubkey(),
},
)
.await?;

debug!("commit transaction: {signed_commit_tx:?}");

let commit_txid = if args.dry_run {
signed_commit_tx.txid()
} else {
info!(
"broadcasting Commit transaction: {}",
signed_commit_tx.txid()
);
rpc_client::broadcast_transaction(&signed_commit_tx, network).await?
};
info!("Commit transaction broadcasted: {}", commit_txid);

// make runestone
let runestone = Runestone {
etching: Some(etching),
edicts: vec![],
mint: None,
pointer: Some(1),
};

debug!("getting reveal transaction...");
let reveal_transaction = builder
.build_reveal_transaction(RevealTransactionArgs {
input: ord_rs::wallet::Utxo {
id: commit_txid,
index: 0,
amount: commit_tx.reveal_balance,
},
recipient_address: sender_address,
redeem_script: commit_tx.redeem_script,
#[cfg(feature = "rune")]
runestone: Some(runestone),
})
.await?;
debug!("reveal transaction: {reveal_transaction:?}");

if !args.dry_run {
// wait for commit transaction to be inserted
rpc_client::wait_for_tx(&commit_txid, network).await?;

info!(
"commit transaction confirmed; broadcasting reveal transaction: {}",
reveal_transaction.txid()
);
let txid = rpc_client::broadcast_transaction(&reveal_transaction, network).await?;
info!("Reveal transaction broadcasted: {}", txid);
}

Ok(())
}
8 changes: 1 addition & 7 deletions src/wallet/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,13 +491,7 @@ mod tests {
let nft = OrdParser::parse_all(&tx)
.unwrap()
.into_iter()
.find(|(_, ins)| {
if let OrdParser::Ordinal(_) = ins {
true
} else {
false
}
})
.find(|(_, ins)| matches!(ins, OrdParser::Ordinal(_)))
.unwrap()
.1;
let nft = Nft::try_from(nft).unwrap();
Expand Down

0 comments on commit 184f02a

Please sign in to comment.