diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 22b630f3..97c8b922 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -98,7 +98,7 @@ jobs: run: deno cache ./yarpc/js/stacks/transactions.ts - run: cargo install cargo-tarpaulin # - run: cargo tarpaulin - - run: cargo tarpaulin --verbose --all-features --workspace --timeout 120 --out Xml --avoid-cfg-tarpaulin + - run: cargo tarpaulin --verbose --all-features --workspace --timeout 120 --out xml --avoid-cfg-tarpaulin - uses: codecov/codecov-action@v3 with: fail_ci_if_error: true diff --git a/.gitignore b/.gitignore index 7f3ac0b7..e2ff569d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ Cargo.lock cobertura.xml tarpaulin-report.html *~ +.DS_Store +.idea diff --git a/Cargo.toml b/Cargo.toml index c032d61a..b43a1c15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,13 @@ [workspace] members = [ "relay-server", - "frost-test", - "frost-signer", - "frost-coordinator", - "sbtc-cli", - "stacks-coordinator", - "stacks-signer", - "stacks-signer-api", - "stacks-doctor", - "test-utils", - "test-vectors", - "yarpc"] + "degen-base-signer", + "degen-base-coordinator", + "degen-superior-coordinator", + "degen-superior-signer", + "yarpc", + "test-utils" +] [workspace.dependencies] anyhow = "1.0" @@ -19,11 +15,11 @@ array-bytes = "6.1.0" bdk = "0.28.0" bs58 = "0.5" bitcoin = { version = "0.29.2", features = ["rand", "bitcoinconsensus"] } -blockstack-core = { git = "https://github.com/stacks-network/stacks-blockchain/", branch = "next" } +stackslib = { git = "https://github.com/stacks-network/stacks-core/", branch = "next" } clap = { version = "4.1.1", features = ["derive", "env"] } hex = "0.4.3" p256k1 = "5.4.1" -wsts = "2.0" +wsts = "3.0" rusqlite = "0.24.2" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["preserve_order"] } diff --git a/Flow b/Flow new file mode 100644 index 00000000..ab72b07b --- /dev/null +++ b/Flow @@ -0,0 +1,53 @@ +M3 flow: + +When a signer runs his code: +The signer makes his config, creating nodes, wallets, addresses, keys, fees etc. +An infinite loop is made until the smart contract read-only call returns that the signer is a miner in pool (hence preventing him to run the signer code). +We then check if the amount to send to the script that he chose in config is enough to cover at least 1 transaction. + - If the check fails, the code panics and informs him to choose a higher amount. +Then we spawn a separate thread in order to continuously run some commands (auto-exchange, accept non-blacklisted users in pool) - every 5 minutes. +The thread doesn't block the signer at all, so he continues to receive requests and send responses to coordinator. + +Coordinator: +Stacks Coordinator -> Requests data about signers from Frost Coordinator +Frost Coordinator -> Requests data about signers from signers + +Signers: +Frost Signer -> Creates refund and unspendable scripts which are used in order to create a script address and taproot spend info +Frost Signer -> Checks the amount required to send to pox for the current block +Frost Signer -> Calls listunspent command on his script address to retreive the UTXOs +Frost Signer -> Checks if the amount on his script is enough to cover the transaction + - if he has enough money, he then proceeds to create a response containing his script UTXO along with his stacks address + - if he doesn't, then: + - he runs a refund phase on his script, returning the money to him + - he then tries to send money to the script, then creating a response - the utxo field is a result containing either a valid utxo, or an error +Frost Signer -> Constructs a response and sends it to Frost Coordinator + +Coordinator: +Frost Coordinator -> Awaits the response from all signers, checking if it is the correct type, and puts the data in a vector +Frost Coordinator -> Returns the signer's data to Stacks Coordinator +Stacks Coordinator -> Checks if the UTXO is valid and not an error, the amount on the utxo is correct, and that the returned stacks addresses are correct + - if there is a bad actor, the transaction is canceled, notifying the signers that there is a bad actor + - if not, the transaction is created, signed and broadcasted + +Bad actor case: +Stacks Coordinator -> Checks every address from bad actors: + - warns it if it has less than 2 warnings + - proposes it for removal and votes positive for removal if it has 2 warnings, adding it to a list of users to be voted out of pool +Stacks Coordinator -> Sends the list of users to be voted out of pool to Frost Coordinator +Frost Coordinator -> Sends a request to signers to vote out all users from the list +Frost Signer -> Creates a separate thread, checking for each user: + - if the user is still in mining pool means he was't yet voted out + - if the user is proposed for removal (in order to vote only after the propose for removal call is made) + - if the above conditions are true, the user makes a call to vote positive for removal, no longer checking the voted address + +No bad actor case: +Stacks Coordinator -> Creates the transaction containing signer's UTXOs as inputs, and the 2 PoX addresses + the rest of money back to each script as outputs +Stacks Coordinator -> Sends a request to Frost Coordinator for signed transaction +Frost Coordinator -> Iterates through each input, sending a request to signers to sign it, including the transaction in the request +Frost Signer -> Checks if the outputs contain the 2 PoX addresses and the correct amount sent to them +Frost Signer -> Each signer checks if his UTXO is contained in inputs, and if the script address is included in the outputs and has the correct amount back +Frost Signer -> If any of the checks above fail, they don't sign the transaction and write a malicious coordinator log to a local file +Frost Signer -> If the checks succeed, they sign the inputs and return them to Frost Coordinator +Frost Coordinator -> Returns the signed transaction to Stacks Coordinator +Stacks Coordinator -> Broadcasts the transaction \ No newline at end of file diff --git a/README.md b/README.md index 29476d18..42a5faad 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Documentation: https://trust-machines.github.io/stacks-sbtc ## Projects - [relay-server](./relay-server/) is a simple HTTP relay server. -- [stacks-signer-api](./stacks-signer-api) is an API server for interacting with a Stacks signer binary. +- [stacks-signer-api](utils/stacks-signer-api) is an API server for interacting with a Stacks signer binary. ## Prerequisites diff --git a/frost-coordinator/Cargo.toml b/degen-base-coordinator/Cargo.toml similarity index 81% rename from frost-coordinator/Cargo.toml rename to degen-base-coordinator/Cargo.toml index a09283b9..07da175b 100644 --- a/frost-coordinator/Cargo.toml +++ b/degen-base-coordinator/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "frost-coordinator" +name = "degen-base-coordinator" version = "0.0.1" license = "GPLv3" homepage = "https://github.com/Trust-Machines/core-eng" @@ -10,6 +10,7 @@ edition = "2021" [dependencies] p256k1 = { workspace = true } +stackslib = { workspace = true } wsts = { workspace = true } backoff = { workspace = true } clap = { workspace = true } @@ -17,8 +18,10 @@ hashbrown = { workspace = true } thiserror = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } -frost-signer = { path = "../frost-signer" } +degen-base-signer = { path = "../degen-base-signer" } serde = { version = "1.0", features = ["serde_derive"] } +bitcoin.workspace = true +url = { workspace = true } [dev-dependencies] rand_core = { workspace = true } @@ -31,5 +34,5 @@ path = "src/lib.rs" # The source file of the target crate-type = ["lib"] # The crate types to generate [[bin]] -name = "frost-coordinator" +name = "degen-base-coordinator" path = "src/main.rs" diff --git a/frost-coordinator/README.md b/degen-base-coordinator/README.md similarity index 100% rename from frost-coordinator/README.md rename to degen-base-coordinator/README.md diff --git a/frost-coordinator/conf/signer.toml b/degen-base-coordinator/conf/signer.toml similarity index 100% rename from frost-coordinator/conf/signer.toml rename to degen-base-coordinator/conf/signer.toml diff --git a/frost-coordinator/src/coordinator.rs b/degen-base-coordinator/src/coordinator.rs similarity index 64% rename from frost-coordinator/src/coordinator.rs rename to degen-base-coordinator/src/coordinator.rs index f96dbe5f..64e71eb0 100644 --- a/frost-coordinator/src/coordinator.rs +++ b/degen-base-coordinator/src/coordinator.rs @@ -1,13 +1,18 @@ use std::any::Any; use std::collections::BTreeMap; use std::time::Duration; - -use frost_signer::config::{Config, Error as ConfigError}; -use frost_signer::{ +use bitcoin::{Address, Txid}; +use bitcoin::util::taproot::TapBranchHash; +use stackslib::burnchains::bitcoin::address::BitcoinAddress; +use stackslib::types::chainstate::StacksAddress; +use degen_base_signer::signing_round::UtxoError; + +use degen_base_signer::config::{Config, Error as ConfigError}; +use degen_base_signer::{ net::{Error as HttpNetError, Message, NetListen}, signing_round::{ DkgBegin, DkgPublicShare, MessageTypes, NonceRequest, NonceResponse, Signable, - SignatureShareRequest, + SignatureShareRequest, SigShareRequestPox, POXTxidResponse, }, }; use hashbrown::HashSet; @@ -17,9 +22,11 @@ use wsts::{ common::{PolyCommitment, PublicNonce, Signature, SignatureShare}, compute, errors::AggregatorError, - taproot::{Error as TaprootError, SchnorrProof}, + taproot::SchnorrProof, v1, Point, Scalar, }; +use degen_base_signer::bitcoin_node::{BitcoinTransaction, UTXO}; +use degen_base_signer::signing_round::{DegensScriptRequest, VoteOutActorRequest}; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -29,8 +36,6 @@ pub enum Error { NoAggregatePublicKey, #[error("Aggregator failed to sign: {0}")] Aggregator(#[from] AggregatorError), - #[error("Taproot error")] - Taproot(TaprootError), #[error("SchnorrProof failed to verify")] SchnorrProofFailed, #[error("Operation timed out")] @@ -47,6 +52,8 @@ pub enum Command { Sign { msg: Vec }, DkgSign { msg: Vec }, GetAggregatePublicKey, + CreateScripts, + SpendScripts, // TODO degens: has to sign the msg as in Sign and DkgSign } pub struct Coordinator { @@ -65,6 +72,9 @@ pub struct Coordinator { aggregate_public_key: Point, network_private_key: Scalar, public_key: PublicKey, + fee_to_pox: u64, + // from user public key to script bitcoin address + script_addresses: BTreeMap, } impl Coordinator { @@ -85,6 +95,8 @@ impl Coordinator { signature_shares: Default::default(), network_private_key: config.network_private_key, public_key: config.coordinator_public_key, + fee_to_pox: config.fee_to_pox, + script_addresses: Default::default(), }) } @@ -134,6 +146,16 @@ where info!("aggregate public key {}", key); Ok(()) } + Command::CreateScripts => { + info!("create scripts and fund them by signers"); + self.run_create_scripts_generation(); + Ok(()) + } + Command::SpendScripts => { + info!("spend scripts"); + // self. + Ok(()) + } } } @@ -147,6 +169,72 @@ where Ok(public_key) } + fn start_scripts(&mut self) -> Result<(), Error> { + let create_script = DegensScriptRequest { + dkg_id: self.current_dkg_id, + fee_to_pox: self.fee_to_pox, + aggregate_public_key: self.get_aggregate_public_key().unwrap_or(Point::default()), + }; + let create_scripts_message = Message { + sig: create_script.sign(&self.network_private_key).expect(""), + msg: MessageTypes::DegensCreateScriptsRequest(create_script), + }; + self.network.send_message(create_scripts_message)?; + Ok(()) + } + fn wait_for_create_scripts(&mut self) -> (Vec>, Vec, Vec) { + let mut ids_to_await: HashSet = (1..=self.total_signers).collect(); + let (mut utxo, mut stacks_address, mut merkle_root) = (vec![], vec![], vec![]); + while !ids_to_await.is_empty() { + if let MessageTypes::DegensCreateScriptsResponse(response) = self.wait_for_next_message().unwrap().msg { + ids_to_await.remove(&response.signer_id); + utxo.push(response.utxo); + stacks_address.push(response.stacks_address); + merkle_root.push(response.merkle_root); + } + } + (utxo, stacks_address, merkle_root) + } + pub fn run_create_scripts_generation(&mut self) -> (Vec>, Vec, Vec) { + // things to be done by coordinator + info!("Starting to create scripts"); + self.start_scripts().unwrap(); + let (utxo, stacks_address, merkle_root) = self.wait_for_create_scripts(); + (utxo, stacks_address, merkle_root) + } + + fn start_voting_out(&mut self, actors: Vec) -> Result<(), Error> { + let vote_out = VoteOutActorRequest { + dkg_id: self.current_dkg_id, + aggregate_public_key: self.get_aggregate_public_key().unwrap_or(Point::default()), + actors_to_be_voted_out: actors, + }; + let vote_out_message = Message { + sig: vote_out.sign(&self.network_private_key).expect(""), + msg: MessageTypes::VoteOutActorRequest(vote_out), + }; + self.network.send_message(vote_out_message)?; + Ok(()) + } + + pub fn run_voting_actors_out(&mut self, actors: Vec) -> Result<(), Error> { + self.start_voting_out(actors).unwrap(); + Ok(()) + } + + pub fn send_txid_to_signers(&mut self, txid: Txid) -> Result<(), Error> { + let txid = POXTxidResponse { + dkg_id: self.current_dkg_id, + txid, + }; + let txid_message = Message { + sig: txid.sign(&self.network_private_key).expect(""), + msg: MessageTypes::POXTxidResponse(txid), + }; + self.network.send_message(txid_message)?; + Ok(()) + } + fn start_public_shares(&mut self) -> Result<(), Error> { self.dkg_public_shares.clear(); info!( @@ -306,6 +394,66 @@ where Ok(()) } + fn request_sigshares_pox( + &self, + nonce_responses: &[NonceResponse], + msg: &[u8], + tx: &BitcoinTransaction, + ) -> Result<(), Error> { + let signature_share_request = SigShareRequestPox { + dkg_id: self.current_dkg_id, + sign_id: self.current_sign_id, + correlation_id: 0, + nonce_responses: nonce_responses.to_vec(), + message: msg.to_vec(), + transaction: tx.clone(), + }; + + info!( + "Sending SigShareRequestPox dkg_id #{} sign_id #{} to signers", + signature_share_request.dkg_id, signature_share_request.sign_id + ); + + let signature_share_request_message = Message { + sig: signature_share_request + .sign(&self.network_private_key) + .expect("Failed to sign SigShareRequestPox"), + msg: MessageTypes::SigShareRequestPox(signature_share_request), + }; + + self.network.send_message(signature_share_request_message)?; + + Ok(()) + } + + fn collect_sigshares_pox(&mut self) -> Result<(), Error> { + // get the parties who responded with a nonce + let mut signers: HashSet = HashSet::from_iter(self.public_nonces.keys().cloned()); + while !signers.is_empty() { + match self.wait_for_next_message()?.msg { + MessageTypes::SigShareResponsePox(response) => { + if let Some(_party_id) = signers.take(&response.signer_id) { + info!( + "Insert signature shares for signer_id {}", + &response.signer_id + ); + self.signature_shares + .insert(response.signer_id, response.signature_shares.clone()); + } + debug!( + "signature shares for {} received. left to receive: {:?}", + response.signer_id, signers + ); + } + MessageTypes::SigShareRequestPox(_) => {} + msg => { + warn!("SigSharePox loop got unexpected msg {:?}", msg.type_id()); + } + } + } + Ok(()) + } + #[allow(non_snake_case)] pub fn sign_message(&mut self, msg: &[u8]) -> Result<(Signature, SchnorrProof), Error> { debug!("Attempting to Sign Message"); @@ -372,7 +520,85 @@ where info!("Signature ({}, {})", sig.R, sig.z); - let proof = SchnorrProof::new(&sig).map_err(Error::Taproot)?; + let proof = SchnorrProof::new(&sig); + + info!("SchnorrProof ({}, {})", proof.r, proof.s); + + if !proof.verify(&self.aggregate_public_key.x(), msg) { + warn!("SchnorrProof failed to verify!"); + return Err(Error::SchnorrProofFailed); + } + + Ok((sig, proof)) + } + + #[allow(non_snake_case)] + pub fn sign_pox_transaction(&mut self, msg: &[u8], tx: &BitcoinTransaction) -> Result<(Signature, SchnorrProof), Error> { + debug!("Attempting to Sign Message"); + if self.aggregate_public_key == Point::default() { + return Err(Error::NoAggregatePublicKey); + } + + //Continually compute a new aggregate nonce until we have a valid even R + loop { + let R = self.compute_aggregate_nonce(msg)?; + if R.has_even_y() { + debug!("Success: R has even y coord: {}", &R); + break; + } else { + warn!("Failure: R does not have even y coord: {}", R); + } + } + + // make an array of dkg public share polys for SignatureAggregator + debug!( + "collecting commitments from 1..{} in {:?}", + self.total_keys, + self.dkg_public_shares.keys().collect::>() + ); + let polys: Vec = self + .dkg_public_shares + .values() + .map(|ps| ps.public_share.clone()) + .collect(); + + debug!( + "SignatureAggregator::new total_keys: {} threshold: {} commitments: {}", + self.total_keys, + self.threshold, + polys.len() + ); + + let mut aggregator = v1::SignatureAggregator::new(self.total_keys, self.threshold, polys)?; + + let nonce_responses: Vec = self.public_nonces.values().cloned().collect(); + + // request signature shares + self.request_sigshares_pox(&nonce_responses, msg, tx)?; + self.collect_sigshares_pox()?; + + let nonces = nonce_responses + .iter() + .flat_map(|nr| nr.nonces.clone()) + .collect::>(); + let shares = &self + .public_nonces + .iter() + .flat_map(|(i, _)| self.signature_shares[i].clone()) + .collect::>(); + + info!( + "aggregator.sign({:?}, {:?}, {:?})", + msg, + nonces.len(), + shares.len() + ); + + let sig = aggregator.sign(msg, &nonces, shares)?; + + info!("Signature ({}, {})", sig.R, sig.z); + + let proof = SchnorrProof::new(&sig); info!("SchnorrProof ({}, {})", proof.r, proof.s); @@ -486,8 +712,9 @@ where mod test { use super::*; use crate::DEVNET_COORDINATOR_ID; + use bitcoin::secp256k1::{Secp256k1, SecretKey}; - use frost_signer::{ + use degen_base_signer::{ config::{Config, PublicKeys, SignerKeyIds}, net::{HttpNet, HttpNetListen}, signer::Signer, @@ -500,6 +727,14 @@ mod test { use relay_server::Server as RelayServer; use std::{env, thread}; use test_utils::parse_env; + use std::str::FromStr; + use bitcoin::{KeyPair, Network, PrivateKey, XOnlyPublicKey}; + use stackslib::address::AddressHashMode; + use stackslib::chainstate::stacks::{StacksPrivateKey, TransactionVersion}; + use stackslib::types::chainstate::{StacksAddress, StacksPublicKey}; + use serde::Deserialize; + use degen_base_signer::util_versioning::address_version; + use url::Url; fn create_signer_key_ids(signer_id: u32, keys_per_signer: u32) -> Vec { (0..keys_per_signer) @@ -599,6 +834,41 @@ mod test { schnorr_proof.verify(&public_key.x(), &msg); } + #[derive(Clone, Deserialize, Default, Debug)] + #[serde(rename_all = "lowercase")] + pub enum NetworkVersion { + Mainnet, + Testnet, + #[default] + Regtest, + } + fn parse_version(network: NetworkVersion) -> (TransactionVersion, bitcoin::Network) { + // Determine what network we are running on + match network { + NetworkVersion::Mainnet => (TransactionVersion::Mainnet, Network::Bitcoin), + NetworkVersion::Testnet => (TransactionVersion::Testnet, Network::Testnet), + NetworkVersion::Regtest => (TransactionVersion::Testnet, Network::Regtest), + } + } + fn parse_stacks_private_key(stacks_private_key: String, network: NetworkVersion) -> Result<(StacksPrivateKey, StacksAddress), Error> { + let sender_key = StacksPrivateKey::from_hex(&stacks_private_key).unwrap(); + let pk = StacksPublicKey::from_private(&sender_key); + let address = StacksAddress::from_public_keys( + address_version(&parse_version(network).0), + &AddressHashMode::SerializeP2PKH, + 1, + &vec![pk], + ).unwrap(); + Ok((sender_key, address)) + } + fn parse_bitcoin_private_key(bitcoin_private_key: String) -> Result<(SecretKey, XOnlyPublicKey), Error> { + let secp = Secp256k1::new(); + let sender_key = SecretKey::from_str(&bitcoin_private_key).unwrap(); + let key_pair_source = KeyPair::from_secret_key(&secp, &sender_key); + let (xonly_public_key, _) = key_pair_source.x_only_public_key(); + Ok((sender_key, xonly_public_key)) + } + fn spawn_processes_and_get_config(relay_url: String) -> (Config, HttpNetListen) { env::set_var("RUST_LOG", "info"); @@ -620,7 +890,27 @@ mod test { .map(|i| (i + 1, create_signer_key_ids(i, keys_per_signer))) .collect::(); let public_keys = create_public_keys(&signer_private_keys, keys_per_signer); + let stacks_private_key_str = "7287ba251d44a4d3fd9276c88ce34c5c52a038955511cccaf77e61068649c17801"; + let (stacks_private_key, stacks_address) = parse_stacks_private_key(stacks_private_key_str.to_string(), NetworkVersion::Regtest) + .unwrap(); + let stacks_node_rpc_url = Url::try_from("http://localhost:20443").unwrap(); + let stacks_version = TransactionVersion::Testnet; + let bitcoin_private_key_str = "2bd806c97f0e00af1a1fc3328fa763a9269723c8db8fac4f93af71db186d6e90"; + let (bitcoin_private_key, bitcoin_xonly_pubkey) = parse_bitcoin_private_key(bitcoin_private_key_str.to_string()).unwrap(); + let bitcoin_node_rpc_url = Url::try_from("http://devnet:devnet@localhost:18443").unwrap(); + let transaction_fee = 2000; + let bitcoin_network = Network::Regtest; + let coordinator_config = Config::new( + stacks_private_key, + stacks_address, + stacks_node_rpc_url.clone(), + stacks_version, + bitcoin_private_key, + bitcoin_xonly_pubkey, + bitcoin_node_rpc_url.clone(), + transaction_fee, + bitcoin_network, keys_threshold, coordinator_public_key, public_keys.clone(), @@ -631,7 +921,20 @@ mod test { let signer_configs = signer_private_keys .iter() .map(|k| { + let (stacks_private_key, stacks_address) = parse_stacks_private_key( + StacksPrivateKey::new().to_hex(), + NetworkVersion::Regtest + ).unwrap(); Config::new( + stacks_private_key, + stacks_address, + stacks_node_rpc_url.clone(), + stacks_version, + bitcoin_private_key, + bitcoin_xonly_pubkey, + bitcoin_node_rpc_url.clone(), + transaction_fee, + bitcoin_network, keys_threshold, coordinator_public_key, public_keys.clone(), diff --git a/frost-coordinator/src/lib.rs b/degen-base-coordinator/src/lib.rs similarity index 96% rename from frost-coordinator/src/lib.rs rename to degen-base-coordinator/src/lib.rs index 538431ec..6a3edece 100644 --- a/frost-coordinator/src/lib.rs +++ b/degen-base-coordinator/src/lib.rs @@ -1,7 +1,7 @@ pub mod coordinator; use coordinator::{Coordinator, Error}; -use frost_signer::{ +use degen_base_signer::{ config::Config, net::{HttpNet, HttpNetListen}, }; diff --git a/frost-coordinator/src/main.rs b/degen-base-coordinator/src/main.rs similarity index 86% rename from frost-coordinator/src/main.rs rename to degen-base-coordinator/src/main.rs index 0c2504fb..46430093 100644 --- a/frost-coordinator/src/main.rs +++ b/degen-base-coordinator/src/main.rs @@ -1,7 +1,7 @@ use clap::Parser; -use frost_coordinator::{coordinator::Command, create_coordinator_from_path}; -use frost_signer::logging; +use degen_base_coordinator::{coordinator::Command, create_coordinator_from_path}; +use degen_base_signer::logging; use tracing::{error, warn}; #[derive(Parser, Debug)] diff --git a/frost-signer/Cargo.toml b/degen-base-signer/Cargo.toml similarity index 63% rename from frost-signer/Cargo.toml rename to degen-base-signer/Cargo.toml index b2075989..c84b1ab7 100644 --- a/frost-signer/Cargo.toml +++ b/degen-base-signer/Cargo.toml @@ -1,9 +1,9 @@ [package] -name = "frost-signer" +name = "degen-base-signer" version = "0.0.1" license = "GPLv3" -homepage = "https://github.com/Trust-Machines/core-eng" -repository = "https://github.com/Trust-Machines/core-eng" +homepage = "https://degenlab.io" +repository = "https://github.com/stacks-degens/core-eng" edition = "2021" [lib] @@ -11,6 +11,9 @@ path = "src/lib.rs" # The source file of the target. crate-type = ["lib"] # The crate types to generate. [dependencies] +stackslib = { workspace = true } +rusqlite = { workspace = true } +serde_json = { workspace = true } aes-gcm = { workspace = true } backoff = { workspace = true } bincode = { workspace = true } @@ -19,6 +22,7 @@ p256k1 = { workspace = true } wsts = { workspace = true } hashbrown = { workspace = true } itertools = { workspace = true } +mockall = "0.11.3" rand_core = { workspace = true } serde = { workspace = true } sha2 = { workspace = true } @@ -28,3 +32,9 @@ tracing = { workspace = true } tracing-subscriber = { workspace = true } ureq = { workspace = true } rand = { workspace = true } +bdk.workspace = true +bitcoin.workspace = true +url = { workspace = true } +hex.workspae = true +reqwest = { version = "0.11.14", features = ["blocking", "json"] } +chrono = "0.4.29" \ No newline at end of file diff --git a/frost-signer/conf/signer.toml b/degen-base-signer/conf/signer.toml similarity index 100% rename from frost-signer/conf/signer.toml rename to degen-base-signer/conf/signer.toml diff --git a/frost-signer/src/mod.rs b/degen-base-signer/logs/malicious_coordinator_no_pox_transaction.txt similarity index 100% rename from frost-signer/src/mod.rs rename to degen-base-signer/logs/malicious_coordinator_no_pox_transaction.txt diff --git a/degen-base-signer/logs/malicious_coordinator_signer_1.txt b/degen-base-signer/logs/malicious_coordinator_signer_1.txt new file mode 100644 index 00000000..1cc474d8 --- /dev/null +++ b/degen-base-signer/logs/malicious_coordinator_signer_1.txt @@ -0,0 +1,579 @@ +Date and time: "11-10-2023 - 17:49:34" +Block height: 720 +Transaction { + version: 2, + lock_time: PackedLockTime( + 100, + ), + input: [ + TxIn { + previous_output: OutPoint { + txid: 3e3f94d02c3ddbd04d1725501fbb17c1be4bb2bbd99a91fc7e1d799240993fef, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 44b74a9cf1be66698a6ee70251d91b5d3e17da100af17743ae14ef56227081a8, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: cccd2b96aba999e3155d6684bb3941359adbcb58dcf70cc0e48be8bf1011257e, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + ], + output: [ + TxOut { + value: 2756666767, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 4c5563d0186d987b70e9598039af4afbd8aa1f8b9684226ac416e77d21768982), + }, + TxOut { + value: 2756666767, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + TxOut { + value: 2756666767, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 ea899a5ed2ef9d3180ac505da90b1a227f30c0095b8968d4b39525a638f5ae6b), + }, + TxOut { + value: 1865000100, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 bb1745a455bfecbbbedefc0ad93b3c6146e653af8521d2bcf4e6f814ae64efcc), + }, + TxOut { + value: 1865000100, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 6c3acc5483681f06ee58da7c5d7b39ced53269821d527df9a4cef0c9e20f32dd), + }, + ], +} + +============================================ + +Date and time: "17-10-2023 - 19:11:12" +Cause: "The transaction did not contain the correct PoX addresses!" +Block height: 622 +Transaction { + version: 2, + lock_time: PackedLockTime( + 100, + ), + input: [ + TxIn { + previous_output: OutPoint { + txid: af2be73279b6f04331451f445935b283648d57e6002d95e48b6a12230b1ba175, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 056314ffea8e3c2ee12e07f7bc5c7430e8c745f2154ee4891ee92e2fa75f3b60, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 5d23b06cddedeb17120ee4c0055632ecda83a432ebcf01c71f0dde618769e686, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + ], + output: [ + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 ea899a5ed2ef9d3180ac505da90b1a227f30c0095b8968d4b39525a638f5ae6b), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 4c5563d0186d987b70e9598039af4afbd8aa1f8b9684226ac416e77d21768982), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + ], +} + +============================================ + +Date and time: "17-10-2023 - 19:29:21" +Cause: "The transaction did not contain the correct amount!" +Block height: 622 +Transaction { + version: 2, + lock_time: PackedLockTime( + 100, + ), + input: [ + TxIn { + previous_output: OutPoint { + txid: af2be73279b6f04331451f445935b283648d57e6002d95e48b6a12230b1ba175, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 056314ffea8e3c2ee12e07f7bc5c7430e8c745f2154ee4891ee92e2fa75f3b60, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 5d23b06cddedeb17120ee4c0055632ecda83a432ebcf01c71f0dde618769e686, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + ], + output: [ + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 ea899a5ed2ef9d3180ac505da90b1a227f30c0095b8968d4b39525a638f5ae6b), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 4c5563d0186d987b70e9598039af4afbd8aa1f8b9684226ac416e77d21768982), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + TxOut { + value: 1864999900, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 bb1745a455bfecbbbedefc0ad93b3c6146e653af8521d2bcf4e6f814ae64efcc), + }, + TxOut { + value: 1864999900, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 6c3acc5483681f06ee58da7c5d7b39ced53269821d527df9a4cef0c9e20f32dd), + }, + ], +} + +============================================ + +Date and time: "17-10-2023 - 19:30:12" +Cause: "Could not find a good output back to your script in the transaction!" +Block height: 622 +Transaction { + version: 2, + lock_time: PackedLockTime( + 100, + ), + input: [ + TxIn { + previous_output: OutPoint { + txid: 5d23b06cddedeb17120ee4c0055632ecda83a432ebcf01c71f0dde618769e686, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: af2be73279b6f04331451f445935b283648d57e6002d95e48b6a12230b1ba175, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 056314ffea8e3c2ee12e07f7bc5c7430e8c745f2154ee4891ee92e2fa75f3b60, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + ], + output: [ + TxOut { + value: 2756665934, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + TxOut { + value: 2756665934, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 ea899a5ed2ef9d3180ac505da90b1a227f30c0095b8968d4b39525a638f5ae6b), + }, + TxOut { + value: 2756665934, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 4c5563d0186d987b70e9598039af4afbd8aa1f8b9684226ac416e77d21768982), + }, + TxOut { + value: 1865000000, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 bb1745a455bfecbbbedefc0ad93b3c6146e653af8521d2bcf4e6f814ae64efcc), + }, + TxOut { + value: 1865000000, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 6c3acc5483681f06ee58da7c5d7b39ced53269821d527df9a4cef0c9e20f32dd), + }, + ], +} + +============================================ + +Date and time: "17-10-2023 - 19:31:06" +Cause: "Could not find a good output back to your script in the transaction!" +Block height: 622 +Transaction { + version: 2, + lock_time: PackedLockTime( + 100, + ), + input: [ + TxIn { + previous_output: OutPoint { + txid: 5d23b06cddedeb17120ee4c0055632ecda83a432ebcf01c71f0dde618769e686, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: af2be73279b6f04331451f445935b283648d57e6002d95e48b6a12230b1ba175, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 056314ffea8e3c2ee12e07f7bc5c7430e8c745f2154ee4891ee92e2fa75f3b60, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + ], + output: [ + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + TxOut { + value: 1865000000, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 bb1745a455bfecbbbedefc0ad93b3c6146e653af8521d2bcf4e6f814ae64efcc), + }, + TxOut { + value: 1865000000, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 6c3acc5483681f06ee58da7c5d7b39ced53269821d527df9a4cef0c9e20f32dd), + }, + ], +} + +============================================ + +Date and time: "17-10-2023 - 19:32:36" +Cause: "Could not find your script's UTXO as input in the transaction!" +Block height: 622 +Transaction { + version: 2, + lock_time: PackedLockTime( + 100, + ), + input: [ + TxIn { + previous_output: OutPoint { + txid: 056314ffea8e3c2ee12e07f7bc5c7430e8c745f2154ee4891ee92e2fa75f3b60, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 056314ffea8e3c2ee12e07f7bc5c7430e8c745f2154ee4891ee92e2fa75f3b60, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 056314ffea8e3c2ee12e07f7bc5c7430e8c745f2154ee4891ee92e2fa75f3b60, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + ], + output: [ + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 4c5563d0186d987b70e9598039af4afbd8aa1f8b9684226ac416e77d21768982), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 ea899a5ed2ef9d3180ac505da90b1a227f30c0095b8968d4b39525a638f5ae6b), + }, + TxOut { + value: 1865000000, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 bb1745a455bfecbbbedefc0ad93b3c6146e653af8521d2bcf4e6f814ae64efcc), + }, + TxOut { + value: 1865000000, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 6c3acc5483681f06ee58da7c5d7b39ced53269821d527df9a4cef0c9e20f32dd), + }, + ], +} + +============================================ + +Date and time: "17-10-2023 - 19:42:45" +Cause: "Could not find your script's UTXO as input in the transaction!" +Block height: 622 +Transaction { + version: 2, + lock_time: PackedLockTime( + 100, + ), + input: [ + TxIn { + previous_output: OutPoint { + txid: 5d23b06cddedeb17120ee4c0055632ecda83a432ebcf01c71f0dde618769e686, + vout: 1, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 056314ffea8e3c2ee12e07f7bc5c7430e8c745f2154ee4891ee92e2fa75f3b60, + vout: 1, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: af2be73279b6f04331451f445935b283648d57e6002d95e48b6a12230b1ba175, + vout: 1, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + ], + output: [ + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 4c5563d0186d987b70e9598039af4afbd8aa1f8b9684226ac416e77d21768982), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 ea899a5ed2ef9d3180ac505da90b1a227f30c0095b8968d4b39525a638f5ae6b), + }, + TxOut { + value: 1865000000, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 bb1745a455bfecbbbedefc0ad93b3c6146e653af8521d2bcf4e6f814ae64efcc), + }, + TxOut { + value: 1865000000, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 6c3acc5483681f06ee58da7c5d7b39ced53269821d527df9a4cef0c9e20f32dd), + }, + ], +} + +============================================ + diff --git a/degen-base-signer/logs/malicious_coordinator_signer_2.txt b/degen-base-signer/logs/malicious_coordinator_signer_2.txt new file mode 100644 index 00000000..6da900eb --- /dev/null +++ b/degen-base-signer/logs/malicious_coordinator_signer_2.txt @@ -0,0 +1,495 @@ +Date and time: "11-10-2023 - 17:49:34" +Block height: 720 +Transaction { + version: 2, + lock_time: PackedLockTime( + 100, + ), + input: [ + TxIn { + previous_output: OutPoint { + txid: 3e3f94d02c3ddbd04d1725501fbb17c1be4bb2bbd99a91fc7e1d799240993fef, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 44b74a9cf1be66698a6ee70251d91b5d3e17da100af17743ae14ef56227081a8, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: cccd2b96aba999e3155d6684bb3941359adbcb58dcf70cc0e48be8bf1011257e, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + ], + output: [ + TxOut { + value: 2756666767, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 4c5563d0186d987b70e9598039af4afbd8aa1f8b9684226ac416e77d21768982), + }, + TxOut { + value: 2756666767, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + TxOut { + value: 2756666767, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 ea899a5ed2ef9d3180ac505da90b1a227f30c0095b8968d4b39525a638f5ae6b), + }, + TxOut { + value: 1865000100, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 bb1745a455bfecbbbedefc0ad93b3c6146e653af8521d2bcf4e6f814ae64efcc), + }, + TxOut { + value: 1865000100, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 6c3acc5483681f06ee58da7c5d7b39ced53269821d527df9a4cef0c9e20f32dd), + }, + ], +} + +============================================ + +Date and time: "17-10-2023 - 19:11:12" +Cause: "The transaction did not contain the correct PoX addresses!" +Block height: 622 +Transaction { + version: 2, + lock_time: PackedLockTime( + 100, + ), + input: [ + TxIn { + previous_output: OutPoint { + txid: af2be73279b6f04331451f445935b283648d57e6002d95e48b6a12230b1ba175, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 056314ffea8e3c2ee12e07f7bc5c7430e8c745f2154ee4891ee92e2fa75f3b60, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 5d23b06cddedeb17120ee4c0055632ecda83a432ebcf01c71f0dde618769e686, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + ], + output: [ + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 ea899a5ed2ef9d3180ac505da90b1a227f30c0095b8968d4b39525a638f5ae6b), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 4c5563d0186d987b70e9598039af4afbd8aa1f8b9684226ac416e77d21768982), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + ], +} + +============================================ + +Date and time: "17-10-2023 - 19:29:21" +Cause: "The transaction did not contain the correct amount!" +Block height: 622 +Transaction { + version: 2, + lock_time: PackedLockTime( + 100, + ), + input: [ + TxIn { + previous_output: OutPoint { + txid: af2be73279b6f04331451f445935b283648d57e6002d95e48b6a12230b1ba175, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 056314ffea8e3c2ee12e07f7bc5c7430e8c745f2154ee4891ee92e2fa75f3b60, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 5d23b06cddedeb17120ee4c0055632ecda83a432ebcf01c71f0dde618769e686, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + ], + output: [ + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 ea899a5ed2ef9d3180ac505da90b1a227f30c0095b8968d4b39525a638f5ae6b), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 4c5563d0186d987b70e9598039af4afbd8aa1f8b9684226ac416e77d21768982), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + TxOut { + value: 1864999900, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 bb1745a455bfecbbbedefc0ad93b3c6146e653af8521d2bcf4e6f814ae64efcc), + }, + TxOut { + value: 1864999900, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 6c3acc5483681f06ee58da7c5d7b39ced53269821d527df9a4cef0c9e20f32dd), + }, + ], +} + +============================================ + +Date and time: "17-10-2023 - 19:30:12" +Cause: "Could not find a good output back to your script in the transaction!" +Block height: 622 +Transaction { + version: 2, + lock_time: PackedLockTime( + 100, + ), + input: [ + TxIn { + previous_output: OutPoint { + txid: 5d23b06cddedeb17120ee4c0055632ecda83a432ebcf01c71f0dde618769e686, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: af2be73279b6f04331451f445935b283648d57e6002d95e48b6a12230b1ba175, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 056314ffea8e3c2ee12e07f7bc5c7430e8c745f2154ee4891ee92e2fa75f3b60, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + ], + output: [ + TxOut { + value: 2756665934, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + TxOut { + value: 2756665934, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 ea899a5ed2ef9d3180ac505da90b1a227f30c0095b8968d4b39525a638f5ae6b), + }, + TxOut { + value: 2756665934, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 4c5563d0186d987b70e9598039af4afbd8aa1f8b9684226ac416e77d21768982), + }, + TxOut { + value: 1865000000, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 bb1745a455bfecbbbedefc0ad93b3c6146e653af8521d2bcf4e6f814ae64efcc), + }, + TxOut { + value: 1865000000, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 6c3acc5483681f06ee58da7c5d7b39ced53269821d527df9a4cef0c9e20f32dd), + }, + ], +} + +============================================ + +Date and time: "17-10-2023 - 19:32:36" +Cause: "Could not find your script's UTXO as input in the transaction!" +Block height: 622 +Transaction { + version: 2, + lock_time: PackedLockTime( + 100, + ), + input: [ + TxIn { + previous_output: OutPoint { + txid: 056314ffea8e3c2ee12e07f7bc5c7430e8c745f2154ee4891ee92e2fa75f3b60, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 056314ffea8e3c2ee12e07f7bc5c7430e8c745f2154ee4891ee92e2fa75f3b60, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 056314ffea8e3c2ee12e07f7bc5c7430e8c745f2154ee4891ee92e2fa75f3b60, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + ], + output: [ + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 4c5563d0186d987b70e9598039af4afbd8aa1f8b9684226ac416e77d21768982), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 ea899a5ed2ef9d3180ac505da90b1a227f30c0095b8968d4b39525a638f5ae6b), + }, + TxOut { + value: 1865000000, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 bb1745a455bfecbbbedefc0ad93b3c6146e653af8521d2bcf4e6f814ae64efcc), + }, + TxOut { + value: 1865000000, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 6c3acc5483681f06ee58da7c5d7b39ced53269821d527df9a4cef0c9e20f32dd), + }, + ], +} + +============================================ + +Date and time: "17-10-2023 - 19:42:45" +Cause: "Could not find your script's UTXO as input in the transaction!" +Block height: 622 +Transaction { + version: 2, + lock_time: PackedLockTime( + 100, + ), + input: [ + TxIn { + previous_output: OutPoint { + txid: 5d23b06cddedeb17120ee4c0055632ecda83a432ebcf01c71f0dde618769e686, + vout: 1, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 056314ffea8e3c2ee12e07f7bc5c7430e8c745f2154ee4891ee92e2fa75f3b60, + vout: 1, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: af2be73279b6f04331451f445935b283648d57e6002d95e48b6a12230b1ba175, + vout: 1, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + ], + output: [ + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 4c5563d0186d987b70e9598039af4afbd8aa1f8b9684226ac416e77d21768982), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 ea899a5ed2ef9d3180ac505da90b1a227f30c0095b8968d4b39525a638f5ae6b), + }, + TxOut { + value: 1865000000, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 bb1745a455bfecbbbedefc0ad93b3c6146e653af8521d2bcf4e6f814ae64efcc), + }, + TxOut { + value: 1865000000, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 6c3acc5483681f06ee58da7c5d7b39ced53269821d527df9a4cef0c9e20f32dd), + }, + ], +} + +============================================ + diff --git a/degen-base-signer/logs/malicious_coordinator_signer_3.txt b/degen-base-signer/logs/malicious_coordinator_signer_3.txt new file mode 100644 index 00000000..7b244320 --- /dev/null +++ b/degen-base-signer/logs/malicious_coordinator_signer_3.txt @@ -0,0 +1,495 @@ +Date and time: "11-10-2023 - 17:49:34" +Block height: 720 +Transaction { + version: 2, + lock_time: PackedLockTime( + 100, + ), + input: [ + TxIn { + previous_output: OutPoint { + txid: 3e3f94d02c3ddbd04d1725501fbb17c1be4bb2bbd99a91fc7e1d799240993fef, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 44b74a9cf1be66698a6ee70251d91b5d3e17da100af17743ae14ef56227081a8, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: cccd2b96aba999e3155d6684bb3941359adbcb58dcf70cc0e48be8bf1011257e, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + ], + output: [ + TxOut { + value: 2756666767, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 4c5563d0186d987b70e9598039af4afbd8aa1f8b9684226ac416e77d21768982), + }, + TxOut { + value: 2756666767, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + TxOut { + value: 2756666767, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 ea899a5ed2ef9d3180ac505da90b1a227f30c0095b8968d4b39525a638f5ae6b), + }, + TxOut { + value: 1865000100, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 bb1745a455bfecbbbedefc0ad93b3c6146e653af8521d2bcf4e6f814ae64efcc), + }, + TxOut { + value: 1865000100, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 6c3acc5483681f06ee58da7c5d7b39ced53269821d527df9a4cef0c9e20f32dd), + }, + ], +} + +============================================ + +Date and time: "17-10-2023 - 19:11:12" +Cause: "The transaction did not contain the correct PoX addresses!" +Block height: 622 +Transaction { + version: 2, + lock_time: PackedLockTime( + 100, + ), + input: [ + TxIn { + previous_output: OutPoint { + txid: af2be73279b6f04331451f445935b283648d57e6002d95e48b6a12230b1ba175, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 056314ffea8e3c2ee12e07f7bc5c7430e8c745f2154ee4891ee92e2fa75f3b60, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 5d23b06cddedeb17120ee4c0055632ecda83a432ebcf01c71f0dde618769e686, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + ], + output: [ + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 ea899a5ed2ef9d3180ac505da90b1a227f30c0095b8968d4b39525a638f5ae6b), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 4c5563d0186d987b70e9598039af4afbd8aa1f8b9684226ac416e77d21768982), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + ], +} + +============================================ + +Date and time: "17-10-2023 - 19:29:21" +Cause: "The transaction did not contain the correct amount!" +Block height: 622 +Transaction { + version: 2, + lock_time: PackedLockTime( + 100, + ), + input: [ + TxIn { + previous_output: OutPoint { + txid: af2be73279b6f04331451f445935b283648d57e6002d95e48b6a12230b1ba175, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 056314ffea8e3c2ee12e07f7bc5c7430e8c745f2154ee4891ee92e2fa75f3b60, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 5d23b06cddedeb17120ee4c0055632ecda83a432ebcf01c71f0dde618769e686, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + ], + output: [ + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 ea899a5ed2ef9d3180ac505da90b1a227f30c0095b8968d4b39525a638f5ae6b), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 4c5563d0186d987b70e9598039af4afbd8aa1f8b9684226ac416e77d21768982), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + TxOut { + value: 1864999900, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 bb1745a455bfecbbbedefc0ad93b3c6146e653af8521d2bcf4e6f814ae64efcc), + }, + TxOut { + value: 1864999900, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 6c3acc5483681f06ee58da7c5d7b39ced53269821d527df9a4cef0c9e20f32dd), + }, + ], +} + +============================================ + +Date and time: "17-10-2023 - 19:30:12" +Cause: "Could not find a good output back to your script in the transaction!" +Block height: 622 +Transaction { + version: 2, + lock_time: PackedLockTime( + 100, + ), + input: [ + TxIn { + previous_output: OutPoint { + txid: 5d23b06cddedeb17120ee4c0055632ecda83a432ebcf01c71f0dde618769e686, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: af2be73279b6f04331451f445935b283648d57e6002d95e48b6a12230b1ba175, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 056314ffea8e3c2ee12e07f7bc5c7430e8c745f2154ee4891ee92e2fa75f3b60, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + ], + output: [ + TxOut { + value: 2756665934, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + TxOut { + value: 2756665934, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 ea899a5ed2ef9d3180ac505da90b1a227f30c0095b8968d4b39525a638f5ae6b), + }, + TxOut { + value: 2756665934, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 4c5563d0186d987b70e9598039af4afbd8aa1f8b9684226ac416e77d21768982), + }, + TxOut { + value: 1865000000, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 bb1745a455bfecbbbedefc0ad93b3c6146e653af8521d2bcf4e6f814ae64efcc), + }, + TxOut { + value: 1865000000, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 6c3acc5483681f06ee58da7c5d7b39ced53269821d527df9a4cef0c9e20f32dd), + }, + ], +} + +============================================ + +Date and time: "17-10-2023 - 19:31:06" +Cause: "Could not find a good output back to your script in the transaction!" +Block height: 622 +Transaction { + version: 2, + lock_time: PackedLockTime( + 100, + ), + input: [ + TxIn { + previous_output: OutPoint { + txid: 5d23b06cddedeb17120ee4c0055632ecda83a432ebcf01c71f0dde618769e686, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: af2be73279b6f04331451f445935b283648d57e6002d95e48b6a12230b1ba175, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 056314ffea8e3c2ee12e07f7bc5c7430e8c745f2154ee4891ee92e2fa75f3b60, + vout: 0, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + ], + output: [ + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + TxOut { + value: 1865000000, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 bb1745a455bfecbbbedefc0ad93b3c6146e653af8521d2bcf4e6f814ae64efcc), + }, + TxOut { + value: 1865000000, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 6c3acc5483681f06ee58da7c5d7b39ced53269821d527df9a4cef0c9e20f32dd), + }, + ], +} + +============================================ + +Date and time: "17-10-2023 - 19:42:45" +Cause: "Could not find your script's UTXO as input in the transaction!" +Block height: 622 +Transaction { + version: 2, + lock_time: PackedLockTime( + 100, + ), + input: [ + TxIn { + previous_output: OutPoint { + txid: 5d23b06cddedeb17120ee4c0055632ecda83a432ebcf01c71f0dde618769e686, + vout: 1, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: 056314ffea8e3c2ee12e07f7bc5c7430e8c745f2154ee4891ee92e2fa75f3b60, + vout: 1, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + TxIn { + previous_output: OutPoint { + txid: af2be73279b6f04331451f445935b283648d57e6002d95e48b6a12230b1ba175, + vout: 1, + }, + script_sig: Script(), + sequence: Sequence( + 2150694911, + ), + witness: Witness { + content: [], + witness_elements: 0, + last: 0, + second_to_last: 0, + }, + }, + ], + output: [ + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 a5d1e9fb38ad8678b3b76c73e8f2315ae3f08c02523644ddcbc0d2c93261e998), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 4c5563d0186d987b70e9598039af4afbd8aa1f8b9684226ac416e77d21768982), + }, + TxOut { + value: 2756666034, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 ea899a5ed2ef9d3180ac505da90b1a227f30c0095b8968d4b39525a638f5ae6b), + }, + TxOut { + value: 1865000000, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 bb1745a455bfecbbbedefc0ad93b3c6146e653af8521d2bcf4e6f814ae64efcc), + }, + TxOut { + value: 1865000000, + script_pubkey: Script(OP_PUSHNUM_1 OP_PUSHBYTES_32 6c3acc5483681f06ee58da7c5d7b39ced53269821d527df9a4cef0c9e20f32dd), + }, + ], +} + +============================================ + diff --git a/stacks-coordinator/src/bitcoin_node.rs b/degen-base-signer/src/bitcoin_node.rs similarity index 76% rename from stacks-coordinator/src/bitcoin_node.rs rename to degen-base-signer/src/bitcoin_node.rs index abba99b1..9c3d53eb 100644 --- a/stacks-coordinator/src/bitcoin_node.rs +++ b/degen-base-signer/src/bitcoin_node.rs @@ -1,19 +1,24 @@ use std::{borrow::Cow, str::FromStr}; use bdk::descriptor::calc_checksum; -use bitcoin::{consensus::Encodable, hashes::sha256d::Hash, util::amount::Amount, Txid}; +use bitcoin::{consensus::Encodable, hashes::sha256d::Hash, util::amount::Amount, Txid, Address}; use serde::{Deserialize, Serialize}; -use serde_json::Value; +use serde_json::{Number, Value}; use tracing::{debug, info, warn}; +use ureq::serde_json; use url::Url; pub trait BitcoinNode { /// Broadcast the BTC transaction to the bitcoin node fn broadcast_transaction(&self, tx: &BitcoinTransaction) -> Result; /// Load the Bitcoin wallet from the given address - fn load_wallet(&self, address: &bitcoin::Address) -> Result<(), Error>; + fn load_wallet(&self, address: &Address) -> Result<(), Error>; /// Get all utxos from the given address - fn list_unspent(&self, address: &bitcoin::Address) -> Result, Error>; + fn list_unspent(&self, address: &Address) -> Result, Error>; + /// Get all imported descriptors + fn list_descriptors(&self) -> Result, Error>; + /// Get a transaction based on a parsed txid + fn get_transacion(&self, txid: &Txid) -> Result, Error>; } pub type BitcoinTransaction = bitcoin::Transaction; @@ -61,11 +66,13 @@ struct Wallet { warning: String, } +#[derive(Debug, Clone)] pub struct LocalhostBitcoinNode { bitcoind_api: Url, wallet_name: String, } + impl BitcoinNode for LocalhostBitcoinNode { fn broadcast_transaction(&self, tx: &BitcoinTransaction) -> Result { let mut tx_bytes: Vec = vec![]; @@ -124,6 +131,76 @@ impl BitcoinNode for LocalhostBitcoinNode { result } + + fn list_descriptors(&self) -> Result, Error> { + let private = false; + + let response = self.call_wallet("listdescriptors", [private])?; + + let descriptors = response + .as_object() + .ok_or(Error::InvalidResponseJSON("Listdescriptors response is not an object".to_string()))? + .get("descriptors") + .ok_or(Error::InvalidResponseJSON("Missing 'descriptors' field".to_string()))? + .as_array() + .ok_or(Error::InvalidResponseJSON("'descriptors' is not an array".to_string()))?; + + let result: Result, Error> = descriptors + .iter() + .map(|desc| { + desc.as_object() + .and_then(|obj| obj.get("desc")) + .and_then(|desc_value| desc_value.as_str()) + .ok_or(Error::InvalidResponseJSON("Missing or invalid 'desc' field".to_string())) + .map(|s| s[5..69].to_string()) + }) + .collect(); + + let mut address_list = vec![]; + result.unwrap().iter().for_each(|address| address_list.push(Address::from_str(address.as_str()).unwrap())); + + Ok(address_list) + } + + fn get_transacion(&self, txid: &Txid) -> Result, Error> { + debug!("Retrieving transaction..."); + let include_watchonly = true; + let verbose = false; + let params = (txid, include_watchonly, verbose); + + let response = self.call_wallet("gettransaction", params)?; + + let details = response + .as_object() + .ok_or(Error::InvalidResponseJSON("Gettransaction response is not an object".to_string()))? + .get("details") + .ok_or(Error::InvalidResponseJSON("Missing 'details' field".to_string()))? + .as_array() + .ok_or(Error::InvalidResponseJSON("'details' is not an array".to_string()))?; + + let result: Result, Error> = details + .iter() + .filter(|detail| { + detail.as_object() + .and_then(|obj| obj.get("category")) + .map(|category| category.as_str()) + .map(|category_str| category_str == Some("receive")) + .unwrap_or(false) + }) + .map(|detail| { + detail.as_object() + .and_then(|obj| { + let address = obj.get("address")?; + let amount = obj.get("amount")?; + Some((address.as_str()?.to_string(), (amount.as_f64()? * 100000000.0) as u64)) + }) + .ok_or(Error::InvalidResponseJSON("Missing or invalid 'address' or 'amount' fields.".to_string())) + .map(|fields| fields) + }) + .collect(); + + result + } } impl LocalhostBitcoinNode { @@ -214,6 +291,14 @@ impl LocalhostBitcoinNode { Ok(()) } + pub fn get_block_count(&self) -> Result { + debug!("Getting block count..."); + let block_count = serde_json::from_value::(self.call("getblockcount", ())?) + .map_err(|e| Error::InvalidResponseJSON(e.to_string()))? + .as_u64().expect("block count not a number"); + Ok(block_count) + } + fn import_address(&self, address: &bitcoin::Address) -> Result<(), Error> { debug!("Importing address {}...", address); diff --git a/degen-base-signer/src/bitcoin_scripting.rs b/degen-base-signer/src/bitcoin_scripting.rs new file mode 100644 index 00000000..3a30f3cd --- /dev/null +++ b/degen-base-signer/src/bitcoin_scripting.rs @@ -0,0 +1,249 @@ +use std::str::FromStr; +use bitcoin::blockdata::opcodes::all; +use bitcoin::blockdata::script::Builder; +use bitcoin::secp256k1::{All, Message, Secp256k1}; +use bitcoin::{Address, KeyPair, Network, OutPoint, PackedLockTime, SchnorrSig, SchnorrSighashType, Script, Sequence, Transaction, Txid, TxIn, TxOut, Witness, XOnlyPublicKey}; +use bitcoin::psbt::Prevouts; +use bitcoin::psbt::serialize::Serialize; +use bitcoin::schnorr::{TapTweak, TweakedPublicKey}; +use bitcoin::util::sighash::{ScriptPath, SighashCache}; +use bitcoin::util::taproot; +use bitcoin::util::taproot::{ControlBlock, LeafVersion, TaprootSpendInfo}; +use crate::bitcoin_node::{LocalhostBitcoinNode, UTXO}; +use crate::signing_round::UtxoError::InvalidUTXO; + +pub fn create_script_refund( + user_public_key: &XOnlyPublicKey, + unlock_block: usize, +) -> Script { + Builder::new() + .push_int(unlock_block as i64) + .push_opcode(all::OP_CLTV) + .push_opcode(all::OP_DROP) + .push_x_only_key(user_public_key) + .push_opcode(all::OP_CHECKSIG) + .into_script() +} + +pub fn create_script_unspendable() -> Script { + Builder::new().push_opcode(all::OP_RETURN).into_script() +} + +pub fn create_tree( + secp: &Secp256k1, + aggregate_x_only: XOnlyPublicKey, + network: Network, + script_1: &Script, + script_2: &Script, +) -> (TaprootSpendInfo, Address) { + let builder = taproot::TaprootBuilder::with_huffman_tree(vec![ + (1, script_1.clone()), + (1, script_2.clone()), + ]).unwrap(); + + let tap_info = builder.finalize(secp, aggregate_x_only).unwrap(); + + // let tweaked_public_key = TweakedPublicKey::dangerous_assume_tweaked(aggregate_x_only); + // let address_tweaked = Address::p2tr_tweaked(tweaked_public_key, network); + + let address = Address::p2tr( + secp, + tap_info.internal_key(), + tap_info.merkle_root(), + network, + ); + + (tap_info, address) +} + +pub fn get_current_block_height(client: &LocalhostBitcoinNode) -> u64 { + client.get_block_count().unwrap() +} + +pub fn create_tx_from_user_to_script( + previous_outputs_vec: &Vec, + user_address: &Address, + script_address: &Address, + amount: u64, + fee_to_script: u64, + fee_to_pox: u64, +) -> Transaction { + let mut inputs = vec![]; + let mut total_utxo_amount: u64 = 0; + + for position in 0..previous_outputs_vec.len() { + let outpoint = OutPoint::new( + Txid::from_str(&previous_outputs_vec[position].txid.as_str()).unwrap(), + previous_outputs_vec[position].vout.clone() + ); + + total_utxo_amount = total_utxo_amount + previous_outputs_vec[position].amount; + + inputs.push( + TxIn { + previous_output: outpoint, + script_sig: Script::new(), + sequence: Sequence(0x8030FFFF), + witness: Witness::default(), + } + ) + } + + let amount_back_to_user = total_utxo_amount - amount - fee_to_script - fee_to_pox; + + if amount_back_to_user != 0 { + Transaction { + version: 2, + lock_time: PackedLockTime(0), + input: inputs, + output: vec![ + TxOut { + value: amount + fee_to_pox, + script_pubkey: script_address.script_pubkey(), + }, + TxOut { + value: amount_back_to_user, + script_pubkey: user_address.script_pubkey(), + } + ], + } + } + else { + Transaction { + version: 2, + lock_time: PackedLockTime(0), + input: inputs, + output: vec![ + TxOut { + value: amount + fee_to_pox, + script_pubkey: script_address.script_pubkey(), + } + ], + } + } +} + +/// uses script sign +pub fn sign_tx_script_refund( + secp: &Secp256k1, + tx_ref: &Transaction, + txout_vec: &Vec, + script: &Script, + key_pair_user: &KeyPair, + tap_info: &TaprootSpendInfo, +) -> Transaction { + let mut tx = tx_ref.clone(); + + let prevouts = Prevouts::All(txout_vec); + + for position in 0..txout_vec.len() { + let sighash_sig = SighashCache::new(&mut tx.clone()) + .taproot_script_spend_signature_hash( + position, + &prevouts, + ScriptPath::with_defaults(script), + SchnorrSighashType::AllPlusAnyoneCanPay, + ) + .unwrap(); + // println!("sighash_sig: {}", sighash_sig); + // println!("message: {}", Message::from_slice(&sighash_sig).unwrap()); + let msg = Message::from_slice(&sighash_sig).unwrap(); + let sig = secp.sign_schnorr(&msg, key_pair_user); + // println!("sig: {}", sig); + + let actual_control = tap_info + .control_block(&(script.clone(), LeafVersion::TapScript)) + .unwrap(); + + // TODO: degens - verify_p2tr_commitment works with key_pair_internal but not with key_pair_from_script + // we don't have private/secret key for aggregated key in refund path + // modifiy it to work with it or remove it + // verify_p2tr_commitment(secp, script, key_pair_user, tap_info, &actual_control); + + let schnorr_sig = SchnorrSig { + sig, + hash_ty: SchnorrSighashType::AllPlusAnyoneCanPay, + }; + + let wit = Witness::from_vec(vec![ + schnorr_sig.to_vec(), + script.to_bytes(), + actual_control.serialize(), + ]); + + tx.input[position].witness = wit; + } + + tx +} + +pub fn create_refund_tx( + utxos: &Vec, + user_address: &Address, + amount: u64, +) -> Transaction { + let mut inputs = vec![]; + + for utxo in utxos { + let prev_output_txid_string = &utxo.txid; + let prev_output_txid = Txid::from_str(prev_output_txid_string.as_str()).unwrap(); + let prev_output_vout = utxo.vout.clone(); + let outpoint = OutPoint::new(prev_output_txid, prev_output_vout); + + inputs.push( + TxIn { + previous_output: outpoint, + script_sig: Script::new(), + sequence: Sequence(0x8030FFFF), + witness: Witness::default(), + } + ) + } + + Transaction { + version: 2, + lock_time: PackedLockTime(100), + input: inputs, + output: vec![ + TxOut { + value: amount, + script_pubkey: user_address.script_pubkey(), + }, + ], + } +} + +pub fn sign_tx_user_to_script( + secp: &Secp256k1, + tx_ref: &Transaction, + prevouts: &Prevouts, + key_pair_internal: &KeyPair, +) -> Transaction { + let mut tx = tx_ref.clone(); + + for position in 0..tx_ref.input.len() { + let sighash_sig = SighashCache::new(&mut tx.clone()) + .taproot_key_spend_signature_hash(position, prevouts, SchnorrSighashType::AllPlusAnyoneCanPay) // or All + .unwrap(); + + let tweak_key_pair = key_pair_internal.tap_tweak(secp, None); + // then sig + let msg = Message::from_slice(&sighash_sig).unwrap(); + + let sig = secp.sign_schnorr(&msg, &tweak_key_pair.to_inner()); + + //verify sig + secp.verify_schnorr(&sig, &msg, &tweak_key_pair.to_inner().x_only_public_key().0) + .unwrap(); + + // then witness + let schnorr_sig = SchnorrSig { + sig, + hash_ty: SchnorrSighashType::AllPlusAnyoneCanPay, // or All + }; + + tx.input[position].witness.push(schnorr_sig.serialize()); + } + + tx +} \ No newline at end of file diff --git a/stacks-coordinator/src/bitcoin_wallet.rs b/degen-base-signer/src/bitcoin_wallet.rs similarity index 78% rename from stacks-coordinator/src/bitcoin_wallet.rs rename to degen-base-signer/src/bitcoin_wallet.rs index 32f4c0e5..81a3e200 100644 --- a/stacks-coordinator/src/bitcoin_wallet.rs +++ b/degen-base-signer/src/bitcoin_wallet.rs @@ -9,6 +9,8 @@ use bitcoin::{ blockdata::script, hashes::hex::FromHex, schnorr::TweakedPublicKey, Address, Network, OutPoint, Script, Transaction, TxIn, XOnlyPublicKey, }; +use bitcoin::schnorr::UntweakedPublicKey; +use bitcoin::secp256k1::Secp256k1; use tracing::{debug, warn}; #[derive(thiserror::Error, Debug, PartialEq)] @@ -25,6 +27,7 @@ pub enum Error { MismatchedFulfillmentFee, } +#[derive(Debug, Clone)] pub struct BitcoinWallet { address: Address, public_key: XOnlyPublicKey, @@ -32,8 +35,9 @@ pub struct BitcoinWallet { impl BitcoinWallet { pub fn new(public_key: XOnlyPublicKey, network: Network) -> Self { - let tweaked_public_key = TweakedPublicKey::dangerous_assume_tweaked(public_key); - let address = Address::p2tr_tweaked(tweaked_public_key, network); + // let tweaked_public_key = TweakedPublicKey::dangerous_assume_tweaked(public_key); + let address = Address::p2tr(&Secp256k1::new(), public_key, None, network); + // let address = Address::p2tr_tweaked(tweaked_public_key, network); Self { address, public_key, @@ -127,6 +131,73 @@ impl BitcoinWalletTrait for BitcoinWallet { Ok((tx, prevouts)) } + + fn script_peg_out( + &self, + op: &PegOutRequestOp, + available_utxos: Vec, + ) -> Result<(Transaction, Vec), PegWalletError> { + // Create an empty transaction + let mut tx = Transaction { + version: 2, + lock_time: bitcoin::PackedLockTime(0), + input: vec![], + output: vec![], + }; + // Consume UTXOs until we have enough to cover the total spend (fulfillment fee and peg out amount) + let mut total_consumed = 0; + let mut prevouts = vec![]; + for utxo in available_utxos.into_iter() { + if total_consumed < op.amount { + total_consumed += utxo.amount; + tx.input.push(utxo_to_input(&utxo)?); + prevouts.push(utxo_to_output(&utxo)?); + } else { + // We have consumed enough to cover the total spend + // i.e. have covered the peg out amount + break; + } + } + // Sanity check all the things! + // Check that we have sufficient funds and didn't just run out of available utxos. + if total_consumed < op.amount { + warn!( + "Consumed total {} is less than intended spend: {}", + total_consumed, op.amount + ); + return Err(PegWalletError::from(Error::InsufficientFunds)); + } + // Get the transaction change amount + let change_amount = total_consumed - op.amount; + debug!( + "change_amount: {:?}, total_consumed: {:?}, op.amount: {:?}", + change_amount, total_consumed, op.amount + ); + // Do not want to use Script::new_v1_p2tr because it will tweak our key when we don't want it to + // TODO: degens - update this + let public_key_tweaked = TweakedPublicKey::dangerous_assume_tweaked(self.public_key); + let script_pubkey = Script::new_v1_p2tr_tweaked(public_key_tweaked); + let fee = 300; + tx.output.push(withdrawal_data_output()); + let withdrawal_output = bitcoin::TxOut { + value: op.amount, + script_pubkey: script_pubkey.clone(), + }; + tx.output.push(withdrawal_output); + if change_amount >= script_pubkey.dust_value().to_sat() { + let change_output = bitcoin::TxOut { + value: change_amount - fee, + script_pubkey, + }; + tx.output.push(change_output); + } else { + // Instead of leaving that change to the BTC miner, we could / should bump the sortition fee + debug!("Not enough change to clear dust limit. Not adding change address."); + } + Ok((tx, prevouts)) + } + + fn address(&self) -> &Address { &self.address } @@ -148,7 +219,7 @@ fn withdrawal_data_output() -> TxOut { .push_slice(&data) .into_script(); - bitcoin::TxOut { + TxOut { value: 0, script_pubkey, } @@ -186,7 +257,7 @@ mod tests { use super::{BitcoinWallet, Error}; use crate::bitcoin_node::UTXO; use crate::peg_wallet::{BitcoinWallet as BitcoinWalletTrait, Error as PegWalletError}; - use crate::util::test::{build_peg_out_request_op, PRIVATE_KEY_HEX}; + use crate::util_versioning::test::{build_peg_out_request_op, PRIVATE_KEY_HEX}; use bitcoin::XOnlyPublicKey; use hex::encode; use rand::Rng; diff --git a/degen-base-signer/src/config.rs b/degen-base-signer/src/config.rs new file mode 100644 index 00000000..b7542e47 --- /dev/null +++ b/degen-base-signer/src/config.rs @@ -0,0 +1,773 @@ +use clap::Parser; +use hashbrown::HashMap; +use p256k1::{ + ecdsa::{self, KeyError}, + scalar::{Error as ScalarError, Scalar}, +}; + +use serde::Deserialize; +use std::{fs, thread, time}; +use std::io::Write; +use std::str::FromStr; +use std::sync::{Arc, Mutex}; +use std::thread::sleep; +use bincode::config; +use bitcoin::{KeyPair, XOnlyPublicKey}; +use bitcoin::secp256k1::{Secp256k1, SecretKey}; +use chrono::Local; +use stackslib::address::AddressHashMode; +use stackslib::burnchains::Address; +use stackslib::chainstate::stacks::{StacksPrivateKey, StacksTransaction, TransactionVersion}; +use stackslib::types::chainstate::{StacksAddress, StacksPublicKey}; +use stackslib::vm::ContractName; +use stackslib::vm::types::PrincipalData; +use p256k1::field::P; +use toml; +use tracing::info; +use url::Url; +use stackslib::burnchains::bitcoin::address::BitcoinAddress; +use crate::bitcoin_node::{BitcoinNode, LocalhostBitcoinNode}; +use crate::bitcoin_wallet::BitcoinWallet; +use crate::peg_wallet::BitcoinWallet as BitcoinWalletTrait; +use crate::stacks_node::client::NodeClient; +use crate::stacks_node::StacksNode; +use crate::stacks_wallet::StacksWallet; +use crate::peg_wallet::StacksWallet as PegWallet; + +// import type Bitcoin PrivateKey +// import type Bitcoin xOnlyPubKey and address +use crate::util_versioning::address_version; +use crate::util::parse_public_key; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("{0}")] + IO(#[from] std::io::Error), + #[error("{0}")] + Toml(#[from] toml::de::Error), + #[error("Invalid Public Key: {0}")] + InvalidPublicKey(KeyError), + #[error("Failed to parse network_private_key: {0}")] + InvalidPrivateKey(ScalarError), + #[error("Invalid Key ID. Must specify Key IDs greater than 0.")] + InvalidKeyID, + #[error("Failed to parse stacks_private_key: {0}")] + InvalidStacksPrivateKey(String), + #[error("Failed to parse bitcoin_private_key: {0}")] + InvalidBitcoinPrivateKey(String), + #[error("Invalid config url. {0}")] + InvalidConfigUrl(String), + #[error("Invalid contract. {0}")] + InvalidContract(String), + #[error("Invalid amount. {0}")] + AmountTooLow(String), +} + +// status enum: "is-miner") || (ok "is-waiting") || (ok "is-pending") || (ok "is-none"))))) +// 'Miner' | 'Pending' | 'Waiting' | 'NormalUser'; +#[derive(Clone, Debug, PartialEq)] +pub enum MinerStatus { + Miner, + Pending, + Waiting, + NormalUser, + NoStatus, +} + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +pub struct Cli { + /// Turn debugging information on + #[arg(short, long, action = clap::ArgAction::Count)] + debug: u8, + + /// Config file path + #[arg(short, long)] + pub config: String, + + /// Start a signing round + #[arg(short, long)] + pub start: bool, + + /// ID associated with signer + #[arg(short, long)] + pub id: u32, +} + +#[derive(Clone, Deserialize, Default, Debug)] +struct RawSigners { + pub public_key: String, + pub key_ids: Vec, +} + +#[derive(Clone, Deserialize, Default, Debug)] +#[serde(rename_all = "lowercase")] +pub enum Network { + Mainnet, + Testnet, + #[default] + Regtest, +} +#[derive(Clone, Deserialize, Default, Debug)] +struct RawConfig { + pub mining_contract: String, + pub exchange_contract: String, + pub stacks_private_key: String, + pub stacks_node_rpc_url: String, + pub bitcoin_private_key: String, + pub bitcoin_node_rpc_url: String, + /// The transaction fee in Satoshis used to broadcast transactions to the stacks node + pub transaction_fee: u64, + pub amount_to_script: u64, + pub fee_to_script: u64, + pub fee_to_pox: u64, + pub network: Network, + pub http_relay_url: String, + pub keys_threshold: u32, + pub network_private_key: String, + signers: Vec, + coordinator_public_key: String, +} + +pub type SignerKeyIds = HashMap>; + +impl RawConfig { + pub fn from_path(path: impl AsRef) -> Result { + let content = fs::read_to_string(path)?; + Ok(toml::from_str(&content)?) + } + + pub fn public_keys(&self) -> Result { + let mut public_keys = PublicKeys::default(); + for (i, s) in self.signers.iter().enumerate() { + let signer_public_key = + parse_public_key(&s.public_key).map_err(Error::InvalidPublicKey)?; + for key_id in &s.key_ids { + //We do not allow a key id of 0. + if *key_id == 0 { + return Err(Error::InvalidKeyID); + } + public_keys.key_ids.insert(*key_id, signer_public_key); + } + //We start our signer and key IDs from 1 hence the + 1; + let signer_key = u32::try_from(i).unwrap() + 1; + public_keys.signers.insert(signer_key, signer_public_key); + } + Ok(public_keys) + } + + pub fn signer_key_ids(&self) -> SignerKeyIds { + let mut signer_key_ids = SignerKeyIds::default(); + for (i, s) in self.signers.iter().enumerate() { + signer_key_ids.insert((i + 1).try_into().unwrap(), s.key_ids.clone()); + } + signer_key_ids + } + + pub fn coordinator_public_key(&self) -> Result { + parse_public_key(&self.coordinator_public_key).map_err(Error::InvalidPublicKey) + } + + pub fn network_private_key(&self) -> Result { + let network_private_key = Scalar::try_from(self.network_private_key.as_str()) + .map_err(Error::InvalidPrivateKey)?; + Ok(network_private_key) + } + + pub fn parse_stacks_private_key(&self) -> Result<(StacksPrivateKey, StacksAddress), Error> { + let sender_key = StacksPrivateKey::from_hex(&self.stacks_private_key) + .map_err(|e| Error::InvalidStacksPrivateKey(e.to_string()))?; + let pk = StacksPublicKey::from_private(&sender_key); + let address = StacksAddress::from_public_keys( + address_version(&self.parse_version().0), + &AddressHashMode::SerializeP2PKH, + 1, + &vec![pk], + ) + .ok_or(Error::InvalidStacksPrivateKey( + "Failed to generate stacks address from private key".to_string(), + ))?; + Ok((sender_key, address)) + } + pub fn parse_bitcoin_private_key(&self) -> Result<(SecretKey, XOnlyPublicKey), Error> { + let secp = Secp256k1::new(); + let sender_key = SecretKey::from_str(&self.bitcoin_private_key) + .map_err(|e| Error::InvalidBitcoinPrivateKey(e.to_string()))?; + let key_pair_source = KeyPair::from_secret_key(&secp, &sender_key); + let (xonly_public_key, _) = key_pair_source.x_only_public_key(); + Ok((sender_key, xonly_public_key)) + } + pub fn parse_version(&self) -> (TransactionVersion, bitcoin::Network) { + // Determine what network we are running on + match self.network { + Network::Mainnet => (TransactionVersion::Mainnet, bitcoin::Network::Bitcoin), + // TODO: change the network back to Testnet + Network::Testnet => (TransactionVersion::Testnet, bitcoin::Network::Regtest), + Network::Regtest => (TransactionVersion::Testnet, bitcoin::Network::Regtest), + } + } + pub fn parse_contract(&self) -> Result<((ContractName, StacksAddress), (ContractName, StacksAddress)), Error> { + let mut split = self.mining_contract.split('.'); + let mut split2 = self.exchange_contract.split('.'); + let mining_address = split + .next() + .ok_or(Error::InvalidContract("Missing address".to_string()))?; + let mining_name = split + .next() + .ok_or(Error::InvalidContract("Missing name.".to_string()))? + .to_owned(); + let exchange_address = split2 + .next() + .ok_or(Error::InvalidContract("Missing address".to_string()))?; + let exchange_name = split2 + .next() + .ok_or(Error::InvalidContract("Missing name.".to_string()))? + .to_owned(); + let mining_address = StacksAddress::from_string(mining_address) + .ok_or(Error::InvalidContract("Bad contract address.".to_string()))?; + let mining_name = ContractName::try_from(mining_name) + .map_err(|e| Error::InvalidContract(format!("Bad contract name: {}.", e)))?; + let exchange_address = StacksAddress::from_string(exchange_address) + .ok_or(Error::InvalidContract("Bad contract address.".to_string()))?; + let exchange_name = ContractName::try_from(exchange_name) + .map_err(|e| Error::InvalidContract(format!("Bad contract name: {}.", e)))?; + Ok(((mining_name, mining_address), (exchange_name, exchange_address))) + } + +} + +#[derive(Default, Clone, Debug)] +pub struct PublicKeys { + pub signers: HashMap, + pub key_ids: HashMap, +} + +#[derive(Clone, Debug)] +pub struct Config { + pub contract_name: ContractName, + pub contract_address: StacksAddress, + pub stacks_private_key: StacksPrivateKey, + pub stacks_address: StacksAddress, + pub stacks_node_rpc_url: Url, + pub local_stacks_node: NodeClient, + pub stacks_wallet: StacksWallet, + pub stacks_version: TransactionVersion, + pub bitcoin_private_key: SecretKey, + pub bitcoin_xonly_public_key: XOnlyPublicKey, + pub bitcoin_node_rpc_url: Url, + pub local_bitcoin_node: LocalhostBitcoinNode, + pub bitcoin_wallet: BitcoinWallet, + pub transaction_fee: u64, + pub bitcoin_network: bitcoin::Network, + pub http_relay_url: String, + pub keys_threshold: u32, + pub amount_to_script: u64, + pub fee_to_script: u64, + pub fee_to_pox: u64, + pub network_private_key: Scalar, + pub public_keys: PublicKeys, + pub signer_key_ids: SignerKeyIds, + pub coordinator_public_key: ecdsa::PublicKey, + pub total_signers: u32, + pub total_keys: u32, + pub status: MinerStatus, + pub pox_transactions_block_heights: Arc>>, + pub fund_each_block: bool, +} + +impl Config { + pub fn new( + contract_name: ContractName, + contract_address: StacksAddress, + stacks_private_key: StacksPrivateKey, + stacks_address: StacksAddress, + stacks_node_rpc_url: Url, + local_stacks_node: NodeClient, + stacks_wallet: StacksWallet, + stacks_version: TransactionVersion, + bitcoin_private_key: SecretKey, + bitcoin_xonly_public_key: XOnlyPublicKey, + bitcoin_node_rpc_url: Url, + local_bitcoin_node: LocalhostBitcoinNode, + bitcoin_wallet: BitcoinWallet, + transaction_fee: u64, + bitcoin_network: bitcoin::Network, + keys_threshold: u32, + amount_to_script: u64, + fee_to_script: u64, + fee_to_pox: u64, + coordinator_public_key: ecdsa::PublicKey, + public_keys: PublicKeys, + signer_key_ids: SignerKeyIds, + network_private_key: Scalar, + http_relay_url: String, + status: MinerStatus, + pox_transactions_block_heights: Arc>>, + fund_each_block: bool, + ) -> Config { + Self { + contract_name, + contract_address, + stacks_private_key, + stacks_address, + stacks_node_rpc_url, + local_stacks_node, + stacks_wallet, + stacks_version, + bitcoin_private_key, + bitcoin_xonly_public_key, + bitcoin_node_rpc_url, + local_bitcoin_node, + bitcoin_wallet, + transaction_fee, + bitcoin_network, + keys_threshold, + coordinator_public_key, + network_private_key, + http_relay_url, + amount_to_script, + fee_to_script, + fee_to_pox, + total_signers: public_keys.signers.len().try_into().unwrap(), + total_keys: public_keys.key_ids.len().try_into().unwrap(), + public_keys, + signer_key_ids, + status, + pox_transactions_block_heights, + fund_each_block, + } + } + + pub fn from_path(path: impl AsRef) -> Result { + let raw_config = RawConfig::from_path(path)?; + Config::try_from(&raw_config) + } +} + +fn operate_address_status_non_miner( + stacks_wallet: &StacksWallet, + stacks_node: &mut NodeClient, + stacks_address: &StacksAddress, + bitcoin_pubkey: &XOnlyPublicKey, +) -> MinerStatus { + let mut current_status = stacks_node.get_status(stacks_address).unwrap_or(MinerStatus::NoStatus); + + // TODO: degens - delete these after querying for mempool done + let mut not_in_waiting_mempool = true; + let mut not_in_pending_mempool = true; + let mut not_in_mempool_to_be_miner = true; + + let mut nonce = stacks_node.next_nonce(stacks_address).unwrap_or(0); + + // hashbytes for the bitcoin p2pkh address + // types.tuple({ + // version - link // https://github.com/stacksgov/sips/blob/feat/sip-015/sips/sip-015/sip-015-network-upgrade.md#new-method-get-burn-block-info + // version: types.buff(hash160(buffer_from('00'))), + // hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + // }) + while current_status != MinerStatus::Miner { + match current_status { + MinerStatus::NormalUser => { + // TODO: degens - query the mempool + if not_in_waiting_mempool { + match stacks_wallet.ask_to_join(nonce, bitcoin_pubkey.serialize().to_vec().clone()) + { + Ok(tx) => { + match stacks_node.broadcast_transaction(&tx) { + Ok(()) => { + info!("The tx for ask-to-join: {:#?}", tx); + not_in_waiting_mempool = false; + nonce += 1; + } + Err(e) => { + info!("Failed to broadcast ask-to-jon transaction: {:?}", e); + } + } + } + Err(e) => { + info!("Failed to constuct ask-to-join transaction: {:?}", e); + } + } + } + } + MinerStatus::Waiting => { + let enough_voted = stacks_node.is_enough_voted_to_enter(stacks_address).unwrap_or(false); + if enough_voted { + if not_in_pending_mempool { + match stacks_wallet.call_try_enter(nonce) { + Ok(tx) => { + match stacks_node.broadcast_transaction(&tx) { + Ok(()) => { + info!("The tx for try-enter: {:#?}", tx); + not_in_pending_mempool = false; + nonce += 1; + } + Err(e) => { + info!("Failed to broadcast try-enter transaction: {:?}", e); + } + } + } + Err(e) => { + info!("Failed to constuct try-enter transaction: {:?}", e); + } + } + } + } + } + MinerStatus::Pending => { + let enough_blocks_passed = stacks_node.is_enough_blocks_passed_for_pending_miners(stacks_address).unwrap_or(false); + if enough_blocks_passed { + if not_in_mempool_to_be_miner { + match stacks_wallet.add_pending_miners_to_pool(nonce) { + Ok(tx) => { + match stacks_node.broadcast_transaction(&tx) { + Ok(()) => { + info!("The tx for add-pending-miners-to-pool: {:#?}", tx); + not_in_mempool_to_be_miner = false; + nonce += 1; + } + Err(e) => { + info!("Failed to broadcast add-pending-miners-to-pool transaction: {:?}", e); + } + } + } + Err(e) => { + info!("Failed to constuct add-pending-miners-to-pool transaction: {:?}", e); + } + } + } + } + } + MinerStatus::Miner => { + break + } + MinerStatus::NoStatus => {} + } + sleep(time::Duration::from_secs(60)); + current_status = stacks_node.get_status(stacks_address).unwrap_or(MinerStatus::NoStatus); + } + current_status +} + + +impl TryFrom<&RawConfig> for Config { + type Error = Error; + fn try_from(raw_config: &RawConfig) -> Result { + let (stacks_private_key, stacks_address) = raw_config.parse_stacks_private_key()?; + let (bitcoin_private_key, bitcoin_xonly_public_key) = raw_config.parse_bitcoin_private_key()?; + let (stacks_version, bitcoin_network) = raw_config.parse_version(); + let ((mining_name, mining_address), (exchange_name, exchange_address)) = raw_config.parse_contract().unwrap(); + // TODO: degens - use exchange contract as well + let stacks_node_rpc_url = Url::parse(raw_config.stacks_node_rpc_url.as_str()) + .map_err(|e| + Error::InvalidConfigUrl(format!("Invalid stacks_node_rpc_url: {}", e)) + )?; + let bitcoin_node_rpc_url = Url::parse(raw_config.bitcoin_node_rpc_url.as_str()) + .map_err(|e| + Error::InvalidConfigUrl(format!("Invalid bitcoin_node_rpc_url: {}", e)) + )?; + let mut local_stacks_node = NodeClient::new( + stacks_node_rpc_url.clone(), + mining_name.clone(), + mining_address, + ); + let stacks_wallet = StacksWallet::new( + mining_name.clone(), + mining_address, + stacks_private_key, + stacks_address, + stacks_version, + raw_config.transaction_fee.clone(), + ); + + let status = operate_address_status_non_miner(&stacks_wallet, &mut local_stacks_node, &stacks_address, &bitcoin_xonly_public_key); + + let mut stacks_node_clone = local_stacks_node.clone(); + let mut stacks_wallet_clone = stacks_wallet.clone(); + let mut nonce = local_stacks_node.next_nonce(&stacks_address).unwrap_or(0); + + thread::spawn(move || { + loop { + match stacks_node_clone.is_auto_exchange(&stacks_address) { + Ok(auto_exchange) => { + if auto_exchange == true && stacks_node_clone.get_user_balance(&stacks_address).unwrap_or(0) > 1000 * 1000000 { + // TODO: degens - build the auto-exchange flow + } + } + Err(e) => { + info!("Couldn't get auto exchange value: {:#?}", e); + } + } + + let waiting_list = stacks_node_clone.get_waiting_list(&stacks_address).unwrap_or(vec![]); + + for waiting_miner in waiting_list { + match stacks_node_clone.is_blacklisted(&stacks_address, &waiting_miner) { + Ok(is_blacklisted) => { + if is_blacklisted { + info!("{:#?} is blacklisted, skipping.", &waiting_miner.to_string()) + } + else { + match stacks_wallet_clone.vote_positive_join_request(nonce, waiting_miner) { + Ok(tx) => { + match stacks_node_clone.broadcast_transaction(&tx) { + Ok(()) => { + info!("Voted to accept {:#?} in pool.", &waiting_miner.to_string()); + nonce += 1; + } + Err(e) => { + info!("Couldn't accept miner in pool: {:#?}", e); + } + } + } + Err(e) => { + info!("Error creating vote-positive-join-request transaction: {:?}", e); + } + } + } + } + Err(e) => { + info!("Error retreiving value for is-blacklisted: {:?}", e); + } + } + } + + sleep(time::Duration::from_secs(600)); + } + }); + + let bitcoin_wallet = BitcoinWallet::new(bitcoin_xonly_public_key, bitcoin_network); + let local_bitcoin_node = LocalhostBitcoinNode::new(bitcoin_node_rpc_url.clone()); + if let Err(e) = local_bitcoin_node.load_wallet(bitcoin_wallet.address()) { + info!("Couldn't load script address: {:?}", e) + } + + let number_of_signers = local_stacks_node.get_miners_list(stacks_wallet.address()).unwrap_or(vec![stacks_wallet.address().clone()]).len() as u64 - 1; + let amount_to_pox = local_stacks_node.get_pool_total_spend_per_block(stacks_wallet.address()).unwrap_or(0) as u64 / number_of_signers; + let fee_to_script = raw_config.fee_to_script; + let fee_to_pox = raw_config.fee_to_pox / number_of_signers; + let total_fees = fee_to_script + fee_to_pox; + let mut fund_each_block = false; + + if raw_config.amount_to_script == 0 { + fund_each_block = true; + } else if raw_config.amount_to_script < amount_to_pox + total_fees { + return Err(Error::AmountTooLow(format!("The amount you specified is too low in order to send to PoX and cover the fees: {} < {}", raw_config.amount_to_script, amount_to_pox + total_fees))); + } + + let pox_transactions_block_heights = Arc::new(Mutex::new(Vec::::new())); + let pox_tx_block_heights_thread = Arc::clone(&pox_transactions_block_heights); + + thread::spawn(move || { + loop { + if let Ok(mut block_heights) = pox_tx_block_heights_thread.lock() { + while block_heights.len() > 1 { + if block_heights[1] - block_heights[0] > 1 { + info!("The coordinator didn't fund the scripts for {} block(s)!", block_heights[1] - block_heights[0] - 1); + + let mut missing_block_heights = String::new(); + + for i in block_heights[0] + 1..block_heights[1] - 1 { + missing_block_heights.push_str(&format!("{}, ", i)) + }; + + missing_block_heights.push_str((block_heights[1] - 1).to_string().as_str()); + + let log_directory = std::path::Path::new("../degen-base-signer/logs/"); + + if !log_directory.exists() { + if let Err(e) = fs::create_dir_all(&log_directory) { + info!("Failed to create directory: {:?}", e); + } + } + + let file_path = log_directory.join(format!("malicious_coordinator_no_pox_transaction.txt", )); + + let formatted_date_time = Local::now().format("%d-%m-%Y - %H:%M:%S").to_string(); + + match fs::OpenOptions::new() + .create(true) + .append(true) + .open(&file_path) { + Ok(mut file) => { + let log_message = format!("\ + Date and time: {:?}\n\ + Cause: The coordinator didn't fund the scripts for {} block(s)!\n\ + Block heights: {:#?}\n\n\ + ============================================\n\n", formatted_date_time, block_heights[1] - block_heights[0] - 1, missing_block_heights); + + if let Err(e) = file.write_all(log_message.as_bytes()) { + info!("Couldn't write to file: {:?}", e) + } + + if let Err(e) = file.flush() { + info!("Couldn't flush the file: {:?}", e) + } + } + Err(e) => { + info!("Couldn't create log file: {:?}", e); + } + } + } + block_heights.remove(0); + } + } + sleep(time::Duration::from_secs(60)); + } + }); + + Ok(Config::new( + mining_name, + mining_address, + stacks_private_key, + stacks_address, + stacks_node_rpc_url, + local_stacks_node, + stacks_wallet, + stacks_version, + bitcoin_private_key, + bitcoin_xonly_public_key, + bitcoin_node_rpc_url, + local_bitcoin_node, + bitcoin_wallet, + raw_config.transaction_fee, + bitcoin_network, + raw_config.keys_threshold, + raw_config.amount_to_script, + raw_config.fee_to_script, + raw_config.fee_to_pox, + raw_config.coordinator_public_key()?, + raw_config.public_keys()?, + raw_config.signer_key_ids(), + raw_config.network_private_key()?, + raw_config.http_relay_url.clone(), + status, + pox_transactions_block_heights, + fund_each_block, + )) + } +} + +#[cfg(test)] +mod test { + use super::{Config, Error, RawConfig, RawSigners}; + + #[test] + fn try_from_raw_config_test() { + let mut raw_config = RawConfig::default(); + + // Should fail with the default config (require valid private and public keys...) + assert!(matches!( + Config::try_from(&raw_config), + Err(Error::InvalidPublicKey(_)) + )); + + raw_config.coordinator_public_key = + "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj".to_string(); + assert!(matches!( + Config::try_from(&raw_config), + Err(Error::InvalidPrivateKey(_)) + )); + + raw_config.network_private_key = "9aSCCR6eirt1NAHwJtSz4HMwBHTyMo62SyPMvVDt5DQn".to_string(); + assert!(Config::try_from(&raw_config).is_ok()); + } + + #[test] + fn coordinator_public_key_test() { + let mut config = RawConfig::default(); + // Should fail with an empty public key + assert!(matches!( + config.coordinator_public_key(), + Err(Error::InvalidPublicKey(_)) + )); + // Should fail with an invalid public key + config.coordinator_public_key = "Invalid Public Key".to_string(); + assert!(matches!( + config.coordinator_public_key(), + Err(Error::InvalidPublicKey(_)) + )); + // Should succeed with a valid public key + config.coordinator_public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj".to_string(); + assert!(config.coordinator_public_key().is_ok()); + } + + #[test] + fn public_keys_test() { + let mut config = RawConfig::default(); + let public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj".to_string(); + // Should succeed with an empty vector + let public_keys = config.public_keys().unwrap(); + assert!(public_keys.key_ids.is_empty()); + assert!(public_keys.signers.is_empty()); + + // Should fail with an empty public key + let raw_signer_keys = RawSigners { + key_ids: vec![1, 2], + public_key: "".to_string(), + }; + config.signers = vec![raw_signer_keys]; + assert!(matches!( + config.public_keys(), + Err(Error::InvalidPublicKey(_)) + )); + + // Should fail with an invalid public key + let raw_signer_keys = RawSigners { + key_ids: vec![1, 2], + public_key: "Invalid public key".to_string(), + }; + config.signers = vec![raw_signer_keys]; + assert!(matches!( + config.public_keys(), + Err(Error::InvalidPublicKey(_)) + )); + + // Should fail with an invalid key ID + let raw_signer_keys = RawSigners { + key_ids: vec![0, 1], + public_key: public_key.clone(), + }; + config.signers = vec![raw_signer_keys]; + assert!(matches!(config.public_keys(), Err(Error::InvalidKeyID))); + + // Should succeed with a valid public keys + let raw_signer_keys1 = RawSigners { + key_ids: vec![1, 2], + public_key: public_key.clone(), + }; + let raw_signer_keys2 = RawSigners { + key_ids: vec![3, 4], + public_key, + }; + config.signers = vec![raw_signer_keys1, raw_signer_keys2]; + let public_keys = config.public_keys().unwrap(); + assert_eq!(public_keys.signers.len(), 2); + assert_eq!(public_keys.key_ids.len(), 4); + } + + // test private keys stacks and bitcoin + #[test] + fn parse_stacks_private_key_test() { + let mut config = RawConfig::default(); + // An empty private key should fail + assert!(matches!( + config.parse_stacks_private_key(), + Err(Error::InvalidStacksPrivateKey(_)) + )); + // An invalid key shoudl fail + config.stacks_private_key = "This is an invalid private key...".to_string(); + assert!(matches!( + config.parse_stacks_private_key(), + Err(Error::InvalidStacksPrivateKey(_)) + )); + // A valid key should succeed + config.stacks_private_key = + "d655b2523bcd65e34889725c73064feb17ceb796831c0e111ba1a552b0f31b3901".to_string(); + assert_eq!( + config.parse_stacks_private_key().unwrap().0.to_hex(), + config.stacks_private_key + ); + } + +} diff --git a/frost-signer/src/lib.rs b/degen-base-signer/src/lib.rs similarity index 76% rename from frost-signer/src/lib.rs rename to degen-base-signer/src/lib.rs index 72fa6e70..98d50257 100644 --- a/frost-signer/src/lib.rs +++ b/degen-base-signer/src/lib.rs @@ -5,6 +5,15 @@ pub mod signer; pub mod signing_round; pub mod state_machine; pub mod util; +pub mod peg_queue; +pub mod stacks_node; +pub mod bitcoin_node; +pub mod bitcoin_wallet; + +pub mod bitcoin_scripting; +pub mod peg_wallet; +pub mod stacks_wallet; +pub mod util_versioning; // set via _compile-time_ envars const GIT_BRANCH: Option<&'static str> = option_env!("GIT_BRANCH"); diff --git a/frost-signer/src/logging.rs b/degen-base-signer/src/logging.rs similarity index 100% rename from frost-signer/src/logging.rs rename to degen-base-signer/src/logging.rs diff --git a/frost-signer/src/main.rs b/degen-base-signer/src/main.rs similarity index 81% rename from frost-signer/src/main.rs rename to degen-base-signer/src/main.rs index c04d6183..bfae303a 100644 --- a/frost-signer/src/main.rs +++ b/degen-base-signer/src/main.rs @@ -1,9 +1,9 @@ use clap::Parser; use tracing::{error, info, warn}; -use frost_signer::config::{Cli, Config}; -use frost_signer::logging; -use frost_signer::signer::Signer; +use degen_base_signer::config::{Cli, Config}; +use degen_base_signer::logging; +use degen_base_signer::signer::Signer; fn main() { logging::initiate_tracing_subscriber(); @@ -15,7 +15,7 @@ fn main() { let mut signer = Signer::new(config, cli.id); info!( "{} signer id #{}", - frost_signer::version(), + degen_base_signer::version(), signer.signer_id ); // sign-on message diff --git a/frost-signer/src/net.rs b/degen-base-signer/src/net.rs similarity index 100% rename from frost-signer/src/net.rs rename to degen-base-signer/src/net.rs diff --git a/stacks-coordinator/src/peg_queue/mod.rs b/degen-base-signer/src/peg_queue/mod.rs similarity index 92% rename from stacks-coordinator/src/peg_queue/mod.rs rename to degen-base-signer/src/peg_queue/mod.rs index 19e0f190..ccb68d21 100644 --- a/stacks-coordinator/src/peg_queue/mod.rs +++ b/degen-base-signer/src/peg_queue/mod.rs @@ -1,5 +1,5 @@ -use blockstack_lib::burnchains::Txid; -use blockstack_lib::types::chainstate::BurnchainHeaderHash; +use stackslib::burnchains::Txid; +use stackslib::types::chainstate::BurnchainHeaderHash; use crate::stacks_node; use crate::stacks_node::Error as StacksNodeError; diff --git a/stacks-coordinator/src/peg_queue/sqlite_peg_queue.rs b/degen-base-signer/src/peg_queue/sqlite_peg_queue.rs similarity index 99% rename from stacks-coordinator/src/peg_queue/sqlite_peg_queue.rs rename to degen-base-signer/src/peg_queue/sqlite_peg_queue.rs index 20769bab..9c1512d8 100644 --- a/stacks-coordinator/src/peg_queue/sqlite_peg_queue.rs +++ b/degen-base-signer/src/peg_queue/sqlite_peg_queue.rs @@ -2,9 +2,9 @@ use rusqlite::{Connection as RusqliteConnection, Error as RusqliteError, Row as use std::path::Path; use std::str::FromStr; -use blockstack_lib::burnchains::Txid; -use blockstack_lib::types::chainstate::BurnchainHeaderHash; -use blockstack_lib::util::HexError; +use stackslib::burnchains::Txid; +use stackslib::types::chainstate::BurnchainHeaderHash; +use stackslib::util::HexError; use crate::peg_queue::{Error as PegQueueError, PegQueue, SbtcOp}; use crate::stacks_node::{Error as StacksNodeError, PegInOp, PegOutRequestOp, StacksNode}; @@ -370,7 +370,7 @@ impl FromStr for Status { mod tests { use crate::stacks_node; - use blockstack_lib::{ + use stackslib::{ chainstate::stacks::address::PoxAddress, types::chainstate::StacksAddress, util::{hash::Hash160, secp256k1::MessageSignature}, diff --git a/stacks-coordinator/src/peg_wallet.rs b/degen-base-signer/src/peg_wallet.rs similarity index 88% rename from stacks-coordinator/src/peg_wallet.rs rename to degen-base-signer/src/peg_wallet.rs index bd74cdb2..409e31b6 100644 --- a/stacks-coordinator/src/peg_wallet.rs +++ b/degen-base-signer/src/peg_wallet.rs @@ -6,7 +6,7 @@ use crate::stacks_wallet::{ }; use bitcoin::XOnlyPublicKey; use bitcoin::{Address as BitcoinAddress, TxOut}; -use blockstack_lib::{ +use stackslib::{ chainstate::stacks::StacksTransaction, types::chainstate::{StacksAddress, StacksPublicKey}, }; @@ -58,6 +58,13 @@ pub trait BitcoinWallet { txouts: Vec, ) -> Result<(bitcoin_node::BitcoinTransaction, Vec), Error>; + // Builds an unsigned transaction using the provided utxos to cover the spend amount for the script + fn script_peg_out( + &self, + op: &PegOutRequestOp, + txouts: Vec, + ) -> Result<(bitcoin_node::BitcoinTransaction, Vec), Error>; + /// Returns the BTC address for the wallet fn address(&self) -> &BitcoinAddress; @@ -73,8 +80,8 @@ pub trait PegWallet { } pub struct WrapPegWallet { - pub(crate) bitcoin_wallet: BitcoinWalletStruct, - pub(crate) stacks_wallet: StacksWalletStruct, + pub bitcoin_wallet: BitcoinWalletStruct, + pub stacks_wallet: StacksWalletStruct, } impl PegWallet for WrapPegWallet { diff --git a/frost-signer/src/signer.rs b/degen-base-signer/src/signer.rs similarity index 85% rename from frost-signer/src/signer.rs rename to degen-base-signer/src/signer.rs index a1bad69f..1a6b9711 100644 --- a/frost-signer/src/signer.rs +++ b/degen-base-signer/src/signer.rs @@ -42,7 +42,7 @@ impl Signer { let network_private_key = self.config.network_private_key; let mut round = SigningRound::from(self); loop { - // Retreive a message from coordinator + // Retrieve a message from coordinator let inbound = rx.recv()?; // blocking let outbounds = round.process(inbound.msg)?; for out in outbounds { @@ -81,6 +81,34 @@ impl Signer { .sign(&network_private_key) .expect("failed to sign SignShareResponse") .to_vec(), + MessageTypes::SigShareRequestPox(msg) => msg + .sign(&network_private_key) + .expect("failed to sign SigShareRequestPox") + .to_vec(), + MessageTypes::SigShareResponsePox(msg) => msg + .sign(&network_private_key) + .expect("failed to sign SigShareResponsePox") + .to_vec(), + MessageTypes::VoteOutActorRequest(msg) => msg + .sign(&network_private_key) + .expect("failed to sign VoteOutActorRequest") + .to_vec(), + MessageTypes::POXTxidResponse(msg) => msg + .sign(&network_private_key) + .expect("failed to sign POXTxidResponse") + .to_vec(), + MessageTypes::DegensCreateScriptsRequest(msg) => msg + .sign(&network_private_key) + .expect("failed to sign DegensCreateScriptsRequest") + .to_vec(), + MessageTypes::DegensCreateScriptsResponse(msg) => msg + .sign(&network_private_key) + .expect("failed to sign DegensCreateScriptsResponse") + .to_vec(), + MessageTypes::DegensSpendScripts(msg) => msg + .sign(&network_private_key) + .expect("failed to sign DegensSpendScripts") + .to_vec(), }, }; net.send_message(msg)?; @@ -245,6 +273,56 @@ fn verify_msg( return false; } } + MessageTypes::SigShareRequestPox(msg) => { + if !msg.verify(&m.sig, coordinator_public_key) { + warn!("Received a SigShareRequestPox message with an invalid signature."); + return false; + } + } + MessageTypes::SigShareResponsePox(msg) => { + if let Some(public_key) = public_keys.signers.get(&msg.signer_id) { + if !msg.verify(&m.sig, public_key) { + warn!("Received a SigShareResponsePox message with an invalid signature."); + return false; + } + } else { + warn!( + "Received a SigShareResponsePox message with an unknown id: {}", + msg.signer_id + ); + return false; + } + } + MessageTypes::VoteOutActorRequest(msg) => { + if !msg.verify(&m.sig, coordinator_public_key) { + warn!("Received a VoteOutActorRequest message with an invalid signature."); + return false; + } + } + MessageTypes::POXTxidResponse(msg) => { + if !msg.verify(&m.sig, coordinator_public_key) { + warn!("Received a POXTxidResponse message with an invalid signature."); + return false; + } + } + MessageTypes::DegensCreateScriptsRequest(msg) => { + if !msg.verify(&m.sig, coordinator_public_key) { + warn!("Received a DegensCreateScriptsRequest message with an invalid signature."); + return false; + } + } + MessageTypes::DegensCreateScriptsResponse(msg) => { + if !msg.verify(&m.sig, coordinator_public_key) { + warn!("Received a DegensCreateScriptsResponse message with an invalid signature."); + return false; + } + } + MessageTypes::DegensSpendScripts(msg) => { + if !msg.verify(&m.sig, coordinator_public_key) { + warn!("Received a DegensSpendScripts message with an invalid signature."); + return false; + } + } } true } diff --git a/degen-base-signer/src/signing_round.rs b/degen-base-signer/src/signing_round.rs new file mode 100644 index 00000000..ec3f488e --- /dev/null +++ b/degen-base-signer/src/signing_round.rs @@ -0,0 +1,1796 @@ +use bdk::miniscript::psbt::SighashError; +use chrono::Local; +use bitcoin::blockdata::opcodes::all; +use bitcoin::blockdata::script::Builder; +use bitcoin::consensus::serialize; +use bitcoin::hashes::Hash; +use bitcoin::psbt::{PartiallySignedTransaction, Prevouts}; +use bitcoin::secp256k1::{All, Secp256k1, SecretKey}; +use bitcoin::util::sighash::SighashCache; +use bitcoin::util::{base58, taproot}; +use bitcoin::{Transaction, Txid}; +use bitcoin::{ + hashes::hex::FromHex, + EcdsaSighashType, KeyPair, Network, OutPoint, PrivateKey, PublicKey, SchnorrSighashType, + Script, TxOut, Witness, XOnlyPublicKey, +}; +use stackslib::burnchains::bitcoin::address::{BitcoinAddress, SegwitBitcoinAddress}; +use stackslib::burnchains::bitcoin::{ + BitcoinNetworkType, BitcoinTransaction, BitcoinTxOutput, +}; +use stackslib::burnchains::{ + BurnchainBlockHeader, BurnchainTransaction, PrivateKey as PrivateKeyTrait, +}; +use stackslib::chainstate::burn::operations::PegOutRequestOp; +use stackslib::chainstate::burn::Opcodes; +use stackslib::chainstate::stacks::address::PoxAddress; +use bitcoin::util::address::Address; +use stackslib::chainstate::stacks::{StacksPrivateKey, StacksTransaction, TransactionVersion}; +use stackslib::types::chainstate::{BurnchainHeaderHash, StacksAddress}; +use stackslib::util::hash::{Hash160, Sha256Sum}; +use stackslib::vm::ContractName; +use hashbrown::{HashMap, HashSet}; +use p256k1::{ + ecdsa, + point::{Compressed, Point}, + scalar::Scalar, +}; +use rand::{random, Rng}; +use rand_core::{CryptoRng, OsRng, RngCore}; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use std::collections::BTreeMap; +use std::str::FromStr; +use std::thread; +use std::thread::sleep; +use std::time::Duration; +use bdk::miniscript::ToPublicKey; +use bitcoin::psbt::serialize::Serialize as TransactionSerializer; +use bitcoin::util::taproot::{TapBranchHash, TaprootSpendInfo, TapSighashHash}; +use itertools::Itertools; +use tracing::{debug, info, warn}; +use url::Url; +use wsts::{ + common::{PolyCommitment, PublicNonce, SignatureShare}, + traits::Signer as SignerTrait, + v1, +}; +use std::io::Write; +use std::sync::{Arc, Mutex}; +use crate::bitcoin_node::{BitcoinNode, LocalhostBitcoinNode, UTXO}; +use crate::bitcoin_scripting::{create_refund_tx, create_script_refund, create_script_unspendable, create_tree, create_tx_from_user_to_script, get_current_block_height, sign_tx_script_refund, sign_tx_user_to_script}; +use crate::bitcoin_wallet::BitcoinWallet; +use crate::peg_wallet::{BitcoinWallet as BitcoinWalletTrait, StacksWallet as PegWallet}; +use crate::stacks_node::client::NodeClient; +use crate::stacks_wallet::StacksWallet; +use crate::{ + config::PublicKeys, + signer::Signer as FrostSigner, + state_machine::{Error as StateMachineError, StateMachine, States}, + util::{decrypt, encrypt, make_shared_secret}, +}; +use crate::signing_round::UtxoError::{InvalidUTXO, UTXOAmount}; +use crate::stacks_node::StacksNode; +use stackslib::burnchains::Address as BitcoinAddressTrait; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("InvalidPartyID")] + InvalidPartyID, + #[error("InvalidDkgPublicShare")] + InvalidDkgPublicShare, + #[error("InvalidDkgPrivateShares")] + InvalidDkgPrivateShares(Vec), + #[error("InvalidNonceResponse")] + InvalidNonceResponse, + #[error("InvalidSignatureShare")] + InvalidSignatureShare, + #[error("State Machine Error: {0}")] + StateMachineError(#[from] StateMachineError), + #[error("Error occured during signing: {0}")] + SigningError(#[from] SighashError), + #[error("The amount you're sending is smaller than the fee")] + FeeError, +} + +#[derive(thiserror::Error, Debug, Clone, Serialize, Deserialize)] +pub enum UtxoError { + #[error("Invalid UTXO.")] + InvalidUTXO, + #[error("UTXO amount too low")] + UTXOAmount, +} + +pub trait Signable { + fn hash(&self, hasher: &mut Sha256); + + fn sign(&self, private_key: &Scalar) -> Result, ecdsa::Error> { + let mut hasher = Sha256::new(); + + self.hash(&mut hasher); + + let hash = hasher.finalize(); + match ecdsa::Signature::new(hash.as_slice(), private_key) { + Ok(sig) => Ok(sig.to_bytes().to_vec()), + Err(e) => Err(e), + } + } + + fn verify(&self, signature: &[u8], public_key: &ecdsa::PublicKey) -> bool { + let mut hasher = Sha256::new(); + + self.hash(&mut hasher); + + let hash = hasher.finalize(); + let sig = match ecdsa::Signature::try_from(signature) { + Ok(sig) => sig, + Err(_) => return false, + }; + + sig.verify(hash.as_slice(), public_key) + } +} + +pub struct SigningRound { + pub dkg_id: u64, + pub dkg_public_id: u64, + pub sign_id: u64, + pub sign_nonce_id: u64, + pub threshold: u32, + pub total_signers: u32, + pub total_keys: u32, + pub signer: Signer, + pub state: States, + pub commitments: BTreeMap, + pub shares: HashMap>>, + pub public_nonces: Vec, + pub network_private_key: Scalar, + pub public_keys: PublicKeys, + // TODO: should be encrypted, i guess? + pub contract_name: ContractName, + pub contract_address: StacksAddress, + pub aggregate_public_key: Point, + pub stacks_private_key: StacksPrivateKey, + pub stacks_address: StacksAddress, + pub stacks_node_rpc_url: Url, + pub local_stacks_node: NodeClient, + pub stacks_wallet: StacksWallet, + pub stacks_version: TransactionVersion, + pub bitcoin_private_key: SecretKey, + pub bitcoin_xonly_public_key: XOnlyPublicKey, + pub bitcoin_node_rpc_url: Url, + pub local_bitcoin_node: LocalhostBitcoinNode, + pub bitcoin_wallet: BitcoinWallet, + pub transaction_fee: u64, + pub amount_to_script: u64, + pub fee_to_script: u64, + pub bitcoin_network: Network, + pub previous_transactions: Vec<(u64, Txid, Vec
, u64)>, + pub amount_back_to_script: Vec<(u64, u64)>, + pub script_addresses: BTreeMap, + pub pox_transactions_block_heights: Arc>>, + pub fund_each_block: bool, +} + +pub struct Signer { + pub frost_signer: v1::Signer, + pub signer_id: u32, +} + +impl StateMachine for SigningRound { + fn move_to(&mut self, state: States) -> Result<(), StateMachineError> { + self.can_move_to(&state)?; + self.state = state; + Ok(()) + } + + fn can_move_to(&self, state: &States) -> Result<(), StateMachineError> { + let prev_state = &self.state; + let accepted = match state { + States::Idle => true, + States::DkgPublicDistribute => { + prev_state == &States::Idle + || prev_state == &States::DkgPublicGather + || prev_state == &States::DkgPrivateDistribute + } + States::DkgPublicGather => prev_state == &States::DkgPublicDistribute, + States::DkgPrivateDistribute => prev_state == &States::DkgPublicGather, + States::DkgPrivateGather => prev_state == &States::DkgPrivateDistribute, + States::SignGather => prev_state == &States::Idle, + States::Signed => prev_state == &States::SignGather, + States::DegensScriptDistribute => prev_state == &States::Idle, + States::DegensScriptGather => prev_state == &States::DegensScriptDistribute, + // TODO degens: add states for scripts + }; + if accepted { + info!("state change from {:?} to {:?}", prev_state, state); + Ok(()) + } else { + Err(StateMachineError::BadStateChange(format!( + "{:?} to {:?}", + prev_state, state + ))) + } + } +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub enum DkgStatus { + Success, + Failure(String), +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub enum MessageTypes { + DkgBegin(DkgBegin), + DkgPrivateBegin(DkgBegin), + DkgEnd(DkgEnd), + DkgPublicEnd(DkgEnd), + DkgPublicShare(DkgPublicShare), + DkgPrivateShares(DkgPrivateShares), + NonceRequest(NonceRequest), + NonceResponse(NonceResponse), + SignShareRequest(SignatureShareRequest), + SignShareResponse(SignatureShareResponse), + SigShareRequestPox(SigShareRequestPox), + SigShareResponsePox(SigShareResponsePox), + VoteOutActorRequest(VoteOutActorRequest), + POXTxidResponse(POXTxidResponse), + DegensCreateScriptsRequest(DegensScriptRequest), + DegensCreateScriptsResponse(DegensScriptResponse), + DegensSpendScripts(DegensSpendScript), +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct DkgPublicShare { + pub dkg_id: u64, + pub dkg_public_id: u64, + pub party_id: u32, + pub public_share: PolyCommitment, +} + +impl Signable for DkgPublicShare { + fn hash(&self, hasher: &mut Sha256) { + hasher.update("DKG_PUBLIC_SHARE".as_bytes()); + hasher.update(self.dkg_id.to_be_bytes()); + hasher.update(self.dkg_public_id.to_be_bytes()); + hasher.update(self.party_id.to_be_bytes()); + for a in &self.public_share.A { + hasher.update(a.compress().as_bytes()); + } + } +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct DkgPrivateShares { + pub dkg_id: u64, + pub key_id: u32, + /// Encrypt the shares using AES-GCM with a key derived from ECDH + pub private_shares: HashMap>, +} + +impl Signable for DkgPrivateShares { + fn hash(&self, hasher: &mut Sha256) { + hasher.update("DKG_PRIVATE_SHARES".as_bytes()); + hasher.update(self.dkg_id.to_be_bytes()); + hasher.update(self.key_id.to_be_bytes()); + // make sure we iterate sequentially + // TODO: change this once WSTS goes to 1 based indexing for key_ids, or change to BTreeMap + for id in 0..self.private_shares.len() as u32 { + hasher.update(id.to_be_bytes()); + hasher.update(&self.private_shares[&id]); + } + } +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct DkgBegin { + pub dkg_id: u64, //TODO: Strong typing for this, alternatively introduce a type alias +} + +impl Signable for DkgBegin { + fn hash(&self, hasher: &mut Sha256) { + hasher.update("DKG_BEGIN".as_bytes()); + hasher.update(self.dkg_id.to_be_bytes()); + } +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct DkgEnd { + pub dkg_id: u64, + pub signer_id: u32, + pub status: DkgStatus, +} + +impl Signable for DkgEnd { + fn hash(&self, hasher: &mut Sha256) { + hasher.update("DKG_END".as_bytes()); + hasher.update(self.dkg_id.to_be_bytes()); + hasher.update(self.signer_id.to_be_bytes()); + } +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct NonceRequest { + pub dkg_id: u64, + pub sign_id: u64, + pub sign_nonce_id: u64, +} + +impl Signable for NonceRequest { + fn hash(&self, hasher: &mut Sha256) { + hasher.update("NONCE_REQUEST".as_bytes()); + hasher.update(self.dkg_id.to_be_bytes()); + hasher.update(self.sign_id.to_be_bytes()); + hasher.update(self.sign_nonce_id.to_be_bytes()); + } +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct NonceResponse { + pub dkg_id: u64, + pub sign_id: u64, + pub sign_nonce_id: u64, + pub signer_id: u32, + pub key_ids: Vec, + pub nonces: Vec, +} + +impl Signable for NonceResponse { + fn hash(&self, hasher: &mut Sha256) { + hasher.update("NONCE_RESPONSE".as_bytes()); + hasher.update(self.dkg_id.to_be_bytes()); + hasher.update(self.sign_id.to_be_bytes()); + hasher.update(self.sign_nonce_id.to_be_bytes()); + hasher.update(self.signer_id.to_be_bytes()); + + for key_id in &self.key_ids { + hasher.update(key_id.to_be_bytes()); + } + + for nonce in &self.nonces { + hasher.update(nonce.D.compress().as_bytes()); + hasher.update(nonce.E.compress().as_bytes()); + } + } +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct SignatureShareRequest { + pub dkg_id: u64, + pub sign_id: u64, + pub correlation_id: u64, + pub nonce_responses: Vec, + pub message: Vec, +} + +impl Signable for SignatureShareRequest { + fn hash(&self, hasher: &mut Sha256) { + hasher.update("SIGNATURE_SHARE_REQUEST".as_bytes()); + hasher.update(self.dkg_id.to_be_bytes()); + hasher.update(self.sign_id.to_be_bytes()); + hasher.update(self.correlation_id.to_be_bytes()); + + for nonce_response in &self.nonce_responses { + nonce_response.hash(hasher); + } + + hasher.update(self.message.as_slice()); + } +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct SignatureShareResponse { + pub dkg_id: u64, + pub sign_id: u64, + pub correlation_id: u64, + pub signer_id: u32, + pub signature_shares: Vec, +} + +impl Signable for SignatureShareResponse { + fn hash(&self, hasher: &mut Sha256) { + hasher.update("SIGNATURE_SHARE_RESPONSE".as_bytes()); + hasher.update(self.dkg_id.to_be_bytes()); + hasher.update(self.sign_id.to_be_bytes()); + hasher.update(self.correlation_id.to_be_bytes()); + hasher.update(self.signer_id.to_be_bytes()); + + for signature_share in &self.signature_shares { + hasher.update(signature_share.id.to_be_bytes()); + hasher.update(signature_share.z_i.to_bytes()); + } + } +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct SigShareRequestPox { + pub dkg_id: u64, + pub sign_id: u64, + pub correlation_id: u64, + pub nonce_responses: Vec, + pub message: Vec, + pub transaction: Transaction, +} + +impl Signable for SigShareRequestPox { + fn hash(&self, hasher: &mut Sha256) { + hasher.update("SIGSHARE_REQUEST_POX".as_bytes()); + hasher.update(self.dkg_id.to_be_bytes()); + hasher.update(self.sign_id.to_be_bytes()); + hasher.update(self.correlation_id.to_be_bytes()); + + for nonce_response in &self.nonce_responses { + nonce_response.hash(hasher); + } + + hasher.update(self.message.as_slice()); + hasher.update(bitcoin::psbt::serialize::Serialize::serialize(&self.transaction).as_slice()); + } +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct SigShareResponsePox { + pub dkg_id: u64, + pub sign_id: u64, + pub correlation_id: u64, + pub signer_id: u32, + pub signature_shares: Vec, +} + +impl Signable for SigShareResponsePox { + fn hash(&self, hasher: &mut Sha256) { + hasher.update("SIGSHARE_RESPONSE_POX".as_bytes()); + hasher.update(self.dkg_id.to_be_bytes()); + hasher.update(self.sign_id.to_be_bytes()); + hasher.update(self.correlation_id.to_be_bytes()); + hasher.update(self.signer_id.to_be_bytes()); + + for signature_share in &self.signature_shares { + hasher.update(signature_share.id.to_be_bytes()); + hasher.update(signature_share.z_i.to_bytes()); + } + } +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct VoteOutActorRequest { + pub dkg_id: u64, + pub aggregate_public_key: Point, + pub actors_to_be_voted_out: Vec, +} + +impl Signable for VoteOutActorRequest { + fn hash(&self, hasher: &mut Sha256) { + hasher.update("DEGENS_CREATE_SCRIPT_REQUEST".as_bytes()); + hasher.update(self.dkg_id.to_be_bytes()); + hasher.update(self.aggregate_public_key.to_string().as_bytes()); + for actor in &self.actors_to_be_voted_out { + hasher.update(actor.to_string().as_bytes()); + } + } +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct POXTxidResponse { + pub dkg_id: u64, + pub txid: Txid, +} + +impl Signable for POXTxidResponse { + fn hash(&self, hasher: &mut Sha256) { + hasher.update("DEGENS_CREATE_SCRIPT_REQUEST".as_bytes()); + hasher.update(self.dkg_id.to_be_bytes()); + hasher.update(self.txid.to_vec().as_slice()); + } +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct DegensScriptRequest { + pub dkg_id: u64, + pub fee_to_pox: u64, + pub aggregate_public_key: Point, +} + +impl Signable for DegensScriptRequest { + fn hash(&self, hasher: &mut Sha256) { + hasher.update("DEGENS_CREATE_SCRIPT_REQUEST".as_bytes()); + hasher.update(self.dkg_id.to_be_bytes()); + hasher.update(self.fee_to_pox.to_be_bytes()); + hasher.update(self.aggregate_public_key.to_string().as_bytes()); + } +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct DegensScriptResponse { + pub signer_id: u32, + pub stacks_address: StacksAddress, + pub merkle_root: TapBranchHash, + pub utxo: Result, +} + +impl Signable for DegensScriptResponse { + fn hash(&self, hasher: &mut Sha256) { + hasher.update("DEGENS_CREATE_SCRIPT_RESPONSE".as_bytes()); + hasher.update(self.signer_id.to_be_bytes()); + + hasher.update(self.stacks_address.bytes.as_bytes()); + hasher.update(self.stacks_address.version.to_be_bytes()); + + hasher.update(self.merkle_root.to_vec().as_slice()); + + match &self.utxo { + Ok(utxo) => { + hasher.update(utxo.address.as_bytes()); + hasher.update(utxo.txid.as_bytes()); + hasher.update(utxo.amount.to_be_bytes()); + hasher.update(utxo.desc.as_bytes()); + hasher.update(utxo.confirmations.to_be_bytes()); + hasher.update(utxo.label.as_bytes()); + hasher.update(utxo.redeemScript.as_bytes()); + hasher.update(utxo.reused.to_string().as_bytes()); + hasher.update(utxo.safe.to_string().as_bytes()); + hasher.update(utxo.scriptPubKey.as_bytes()); + hasher.update(utxo.solvable.to_string().as_bytes()); + hasher.update(utxo.spendable.to_string().as_bytes()); + hasher.update(utxo.vout.to_be_bytes()); + hasher.update(utxo.witnessScript.as_bytes()); + } + Err(_) => { + hasher.update("No good UTXO in the list.".as_bytes()); + } + } + } +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct DegensSpendScript { + pub dkg_id: u64, + pub addresses: Vec, // TODO degens: update to address type/alias +} + +impl Signable for DegensSpendScript { + fn hash(&self, hasher: &mut Sha256) { + hasher.update("DEGENS_SPEND_SCRIPT".as_bytes()); + hasher.update(self.dkg_id.to_be_bytes()); + for address in &self.addresses { + hasher.update(address.as_bytes()); + } + } +} + +impl SigningRound { + pub fn new( + threshold: u32, + total_signers: u32, + total_keys: u32, + signer_id: u32, + key_ids: Vec, + network_private_key: Scalar, + public_keys: PublicKeys, + network: Network, + ) -> SigningRound { + assert!(threshold <= total_keys); + let mut rng = OsRng; + let frost_signer = v1::Signer::new(signer_id, &key_ids, total_keys, threshold, &mut rng); + let signer = Signer { + frost_signer, + signer_id, + }; + + SigningRound { + dkg_id: 0, + dkg_public_id: 0, + sign_id: 1, + sign_nonce_id: 1, + threshold, + total_signers, + total_keys, + signer, + state: States::Idle, + commitments: BTreeMap::new(), + shares: HashMap::new(), + public_nonces: vec![], + network_private_key, + public_keys, + contract_name: ContractName::from(""), + contract_address: StacksAddress::new(26, Hash160([0; 20])), + aggregate_public_key: Point::new(), + stacks_private_key: StacksPrivateKey::new(), + stacks_address: StacksAddress::new(26, Hash160([0; 20])), + stacks_node_rpc_url: Url::from_str("").unwrap(), + local_stacks_node: NodeClient::new( + Url::from_str("").unwrap(), + ContractName::from(""), + StacksAddress::new(26, Hash160([0; 20])), + ), + stacks_wallet: StacksWallet::new( + ContractName::from(""), + StacksAddress::from_string("").unwrap(), + StacksPrivateKey::new(), + StacksAddress::new(26, Hash160([0; 20])), + TransactionVersion::Testnet, + 0, + ), + stacks_version: TransactionVersion::Testnet, + bitcoin_private_key: SecretKey::new(&mut rng), + bitcoin_xonly_public_key: SecretKey::new(&mut rng) + .x_only_public_key(&Secp256k1::new()) + .0, + bitcoin_node_rpc_url: Url::from_str("").unwrap(), + local_bitcoin_node: LocalhostBitcoinNode::new(Url::from_str("").unwrap()), + bitcoin_wallet: BitcoinWallet::new( + XOnlyPublicKey::from_str( + "cc8a4bc64d897bddc5fbc2f670f7a8ba0b386779106cf1223c6fc5d7cd6fc115", + ) + .unwrap(), + network, + ), + transaction_fee: 0, + amount_to_script: 0, + bitcoin_network: network, + previous_transactions: vec![], + amount_back_to_script: vec![], + fee_to_script: 0, + script_addresses: BTreeMap::new(), + pox_transactions_block_heights: Arc::new(Mutex::new(vec![])), + fund_each_block: true, + } + } + + fn reset(&mut self, dkg_id: u64, rng: &mut T) { + self.dkg_id = dkg_id; + self.dkg_public_id = 0; + self.commitments.clear(); + self.shares.clear(); + self.public_nonces.clear(); + self.signer.frost_signer.reset_polys(rng); + } + + pub fn process(&mut self, message: MessageTypes) -> Result, Error> { + let out_msgs = match message { + MessageTypes::DkgBegin(dkg_begin) => self.dkg_begin(dkg_begin), + MessageTypes::DkgPrivateBegin(_) => self.dkg_private_begin(), + MessageTypes::DkgPublicShare(dkg_public_shares) => { + self.dkg_public_share(dkg_public_shares) + } + MessageTypes::DkgPrivateShares(dkg_private_shares) => { + self.dkg_private_shares(dkg_private_shares) + } + MessageTypes::SignShareRequest(sign_share_request) => { + self.sign_share_request(sign_share_request) + } + MessageTypes::SigShareRequestPox(sigshare_request_pox) => { + self.sigshare_request_pox(sigshare_request_pox) + } + MessageTypes::NonceRequest(nonce_request) => { + self.nonce_request(nonce_request) + }, + MessageTypes::VoteOutActorRequest(vote_out_request) => { + self.vote_miners_out_of_pool(vote_out_request) + } + MessageTypes::POXTxidResponse(txid_response) => { + self.add_txid_to_list(txid_response) + } + MessageTypes::DegensCreateScriptsRequest(degens_create_script) => { + self.degen_create_script(degens_create_script) + } + + _ => Ok(vec![]), // TODO + }; + + match out_msgs { + Ok(mut out) => { + if self.public_shares_done() { + debug!( + "public_shares_done==true. commitments {}", + self.commitments.len() + ); + let dkg_end_msgs = self.dkg_public_ended()?; + out.push(dkg_end_msgs); + self.move_to(States::DkgPrivateDistribute)?; + } else if self.can_dkg_end() { + debug!( + "can_dkg_end==true. shares {} commitments {}", + self.shares.len(), + self.commitments.len() + ); + let dkg_end_msgs = self.dkg_ended()?; + out.push(dkg_end_msgs); + self.move_to(States::Idle)?; + } + Ok(out) + } + Err(e) => Err(e), + } + } + + fn dkg_public_ended(&mut self) -> Result { + let dkg_end = DkgEnd { + dkg_id: self.dkg_id, + signer_id: self.signer.signer_id, + status: DkgStatus::Success, + }; + let dkg_end = MessageTypes::DkgPublicEnd(dkg_end); + info!( + "DKG_END round #{} signer_id {}", + self.dkg_id, self.signer.signer_id + ); + Ok(dkg_end) + } + + fn dkg_ended(&mut self) -> Result { + let polys: Vec = self.commitments.clone().into_values().collect(); + + let mut decrypted_shares = HashMap::new(); + + // go through private shares, and decrypt any for owned keys, leaving the rest as zero scalars + let key_ids: HashSet = self.signer.frost_signer.get_key_ids().into_iter().collect(); + let mut invalid_dkg_private_shares = Vec::new(); + + for (src_key_id, encrypted_shares) in &self.shares { + let mut decrypted_key_shares = HashMap::new(); + + for (dst_key_id, private_share) in encrypted_shares { + if key_ids.contains(dst_key_id) { + debug!( + "decrypting dkg private share for key_id #{}", + dst_key_id + 1 + ); + let compressed = + Compressed::from(self.public_keys.key_ids[&(src_key_id + 1)].to_bytes()); + let src_public_key = Point::try_from(&compressed).unwrap(); + let shared_secret = + make_shared_secret(&self.network_private_key, &src_public_key); + + match decrypt(&shared_secret, private_share) { + Ok(plain) => match Scalar::try_from(&plain[..]) { + Ok(s) => { + decrypted_key_shares.insert(*dst_key_id, s); + } + Err(e) => { + warn!("Failed to parse Scalar for dkg private share from key_id {} to key_id {}: {:?}", src_key_id, dst_key_id, e); + invalid_dkg_private_shares.push(*src_key_id); + } + }, + Err(e) => { + warn!("Failed to decrypt dkg private share from key_id {} to key_id {}: {:?}", src_key_id, dst_key_id, e); + invalid_dkg_private_shares.push(*src_key_id); + } + } + } else { + decrypted_key_shares.insert(*dst_key_id, Scalar::new()); + } + } + + decrypted_shares.insert(*src_key_id, decrypted_key_shares); + } + + let dkg_end = if invalid_dkg_private_shares.is_empty() { + match self + .signer + .frost_signer + .compute_secrets(&decrypted_shares, &polys) + { + Ok(()) => DkgEnd { + dkg_id: self.dkg_id, + signer_id: self.signer.signer_id, + status: DkgStatus::Success, + }, + Err(dkg_error_map) => DkgEnd { + dkg_id: self.dkg_id, + signer_id: self.signer.signer_id, + status: DkgStatus::Failure(format!("{:?}", dkg_error_map)), + }, + } + } else { + DkgEnd { + dkg_id: self.dkg_id, + signer_id: self.signer.signer_id, + status: DkgStatus::Failure(format!("{:?}", invalid_dkg_private_shares)), + } + }; + + let dkg_end = MessageTypes::DkgEnd(dkg_end); + info!( + "DKG_END round #{} signer_id {}", + self.dkg_id, self.signer.signer_id + ); + Ok(dkg_end) + } + + fn public_shares_done(&self) -> bool { + debug!( + "public_shares_done state {:?} commitments {}", + self.state, + self.commitments.len(), + ); + self.state == States::DkgPublicGather + && self.commitments.len() == usize::try_from(self.total_keys).unwrap() + } + + fn can_dkg_end(&self) -> bool { + debug!( + "can_dkg_end state {:?} commitments {} shares {}", + self.state, + self.commitments.len(), + self.shares.len() + ); + self.state == States::DkgPrivateGather + && self.commitments.len() == usize::try_from(self.total_keys).unwrap() + && self.shares.len() == usize::try_from(self.total_keys).unwrap() + } + + fn nonce_request(&mut self, nonce_request: NonceRequest) -> Result, Error> { + let mut rng = OsRng; + let mut msgs = vec![]; + let signer_id = self.signer.signer_id; + let key_ids = self.signer.frost_signer.get_key_ids(); + let nonces = self.signer.frost_signer.gen_nonces(&mut rng); + + let response = NonceResponse { + dkg_id: nonce_request.dkg_id, + sign_id: nonce_request.sign_id, + sign_nonce_id: nonce_request.sign_nonce_id, + signer_id, + key_ids, + nonces, + }; + + let response = MessageTypes::NonceResponse(response); + + info!( + "nonce request with dkg_id {:?}. response sent from signer_id {}", + nonce_request.dkg_id, signer_id + ); + msgs.push(response); + + Ok(msgs) + } + + fn sign_share_request( + &mut self, + sign_request: SignatureShareRequest, + ) -> Result, Error> { + let mut msgs = vec![]; + + let signer_ids = sign_request + .nonce_responses + .iter() + .map(|nr| nr.signer_id) + .collect::>(); + + info!("Got SignatureShareRequest for signer_ids {:?}", signer_ids); + + for signer_id in &signer_ids { + if *signer_id == self.signer.signer_id { + let key_ids: Vec = sign_request + .nonce_responses + .iter() + .flat_map(|nr| nr.key_ids.iter().copied()) + .collect::>(); + let nonces = sign_request + .nonce_responses + .iter() + .flat_map(|nr| nr.nonces.clone()) + .collect::>(); + let signature_shares = self.signer.frost_signer.sign( + &sign_request.message, + &signer_ids, + &key_ids, + &nonces, + ); + + let response = SignatureShareResponse { + dkg_id: sign_request.dkg_id, + sign_id: sign_request.sign_id, + correlation_id: sign_request.correlation_id, + signer_id: *signer_id, + signature_shares, + }; + + info!( + "Sending SignatureShareResponse for signer_id {:?}", + signer_id + ); + + let response = MessageTypes::SignShareResponse(response); + + msgs.push(response); + } else { + debug!("SignShareRequest for {} dropped.", signer_id); + } + } + Ok(msgs) + } + + fn sigshare_request_pox( + &mut self, + sign_request: SigShareRequestPox, + ) -> Result, Error> { + let mut msgs = vec![]; + + let secp = Secp256k1::new(); + let keypair = KeyPair::from_secret_key(&secp, &self.bitcoin_private_key); + + // let aggregate_compressed = degens_create_script.aggregate_public_key.compress(); + // let aggregate_x_only = PublicKey::from_slice(aggregate_compressed.as_bytes()).unwrap().to_x_only_pubkey(); + + let script_1 = create_script_refund(&self.bitcoin_xonly_public_key, 100); + let script_2 = create_script_unspendable(); + + // TODO: degens - change keypair xonly back to aggregate_x_only after done with testing + let (_, script_address) = create_tree(&secp, keypair.x_only_public_key().0, self.bitcoin_network, &script_1, &script_2); + + let transaction_clone = sign_request.transaction; + let transaction_outputs = &transaction_clone.output; + let transaction_inputs = &transaction_clone.input; + + let mut pox_addresses = vec![ + Address::from_str("bcrt1phvt5tfz4hlkth0k7ls9djweuv9rwv5a0s5sa9085umupftnyalxq0zx28d").unwrap(), + Address::from_str("bcrt1pdsavc4yrdq0sdmjcmf7967eeem2ny6vzr4f8m7dyemcvncs0xtwsc85zdq").unwrap() + ]; + let mut pox_total_amount: u64 = 0; + let pox_sc_amount = self.local_stacks_node.get_pool_total_spend_per_block(self.stacks_wallet.address()).unwrap_or(0) as u64; + + transaction_outputs.iter().for_each(|output| { + match Address::from_script(&output.script_pubkey, self.bitcoin_network) { + Ok(address) => { + if pox_addresses.contains(&address) { + pox_addresses.retain(|user| user != &address); + pox_total_amount = pox_total_amount + output.value; + } + } + Err(e) => { + info!("Couldn't retreive address from UTXO: {:?}", e); + } + } + }); + + let script_utxos = self.local_bitcoin_node.list_unspent(&script_address).unwrap_or(vec![]); + let number_of_signers = transaction_inputs.len() as u64; + let total_amount = self.local_stacks_node.get_pool_total_spend_per_block(self.stacks_wallet.address()).unwrap_or(0) as u64; + let fee = 1000; + let mut found_utxo_in_inputs = false; + let mut found_correct_output = self.fund_each_block; + + for utxo in script_utxos { + for input in transaction_inputs { + if input.previous_output.txid.to_string() == utxo.txid && input.previous_output.vout == utxo.vout { + found_utxo_in_inputs = true; + break + } + } + + if utxo.amount > (total_amount + fee) / number_of_signers { + let amount_back = utxo.amount - ((total_amount + fee) / number_of_signers); + + for output in transaction_outputs { + if Script::from_str(&utxo.scriptPubKey).unwrap_or(Script::new()).eq(&output.script_pubkey) && amount_back == output.value { + found_correct_output = true; + break + } + } + } + + if found_correct_output && found_utxo_in_inputs { + break + } + } + + if pox_addresses.len() == 0 && pox_total_amount == pox_sc_amount && found_correct_output && found_utxo_in_inputs { + let signer_ids = sign_request + .nonce_responses + .iter() + .map(|nr| nr.signer_id) + .collect::>(); + + info!("Got SigShareRequestPox for signer_ids {:?}", signer_ids); + + for signer_id in &signer_ids { + if *signer_id == self.signer.signer_id { + let key_ids: Vec = sign_request + .nonce_responses + .iter() + .flat_map(|nr| nr.key_ids.iter().copied()) + .collect::>(); + let nonces = sign_request + .nonce_responses + .iter() + .flat_map(|nr| nr.nonces.clone()) + .collect::>(); + let signature_shares = self.signer.frost_signer.sign( + &sign_request.message, + &signer_ids, + &key_ids, + &nonces, + ); + + let response = SigShareResponsePox { + dkg_id: sign_request.dkg_id, + sign_id: sign_request.sign_id, + correlation_id: sign_request.correlation_id, + signer_id: *signer_id, + signature_shares, + }; + + info!( + "Sending SigShareResponsePox for signer_id {:?}", + signer_id + ); + + let response = MessageTypes::SigShareResponsePox(response); + + msgs.push(response); + } else { + debug!("SigShareRequestPox for {} dropped.", signer_id); + } + } + } + else { + let mut cause = ""; + + if pox_addresses.len() != 0 { + cause = "The transaction did not contain the correct PoX addresses!" + } + else if pox_total_amount != pox_sc_amount { + cause = "The transaction did not contain the correct amount!" + } + else if !found_correct_output { + cause = "Could not find a good output back to your script in the transaction!" + } + else if !found_utxo_in_inputs { + cause = "Could not find your script's UTXO as input in the transaction!" + } + + info!("The signing failed: {:?}", cause); + + let log_directory = std::path::Path::new("../degen-base-signer/logs/"); + + if !log_directory.exists() { + if let Err(e) = std::fs::create_dir_all(&log_directory) { + info!("Failed to create directory: {:?}", e); + } + } + + let file_path = log_directory.join(format!("malicious_coordinator_signer_{:?}.txt", self.signer.signer_id)); + + let formatted_date_time = Local::now().format("%d-%m-%Y - %H:%M:%S").to_string(); + + match std::fs::OpenOptions::new() + .create(true) + .append(true) + .open(&file_path) { + Ok(mut file) => { + let log_message = format!("\ + Date and time: {:?}\n\ + Cause: {:#?}\n\ + Block height: {:#?}\n\ + {:#?}\n\n\ + ============================================\n\n", formatted_date_time, cause, get_current_block_height(&self.local_bitcoin_node.clone()), transaction_clone); + + if let Err(e) = file.write_all(log_message.as_bytes()) { + info!("Couldn't write to file: {:?}", e) + } + + if let Err(e) = file.flush() { + info!("Couldn't flush the file: {:?}", e) + } + } + Err(e) => { + info!("Couldn't create log file: {:?}", e); + } + } + } + Ok(msgs) + } + + fn dkg_begin(&mut self, dkg_begin: DkgBegin) -> Result, Error> { + let mut rng = OsRng; + + self.reset(dkg_begin.dkg_id, &mut rng); + self.move_to(States::DkgPublicDistribute)?; + + let _party_state = self.signer.frost_signer.save(); + + self.dkg_public_begin() + } + + fn dkg_public_begin(&mut self) -> Result, Error> { + let mut rng = OsRng; + let mut msgs = vec![]; + let polys = self.signer.frost_signer.get_poly_commitments(&mut rng); + + info!( + "sending DkgPublicShares for round #{}, {} poly commitments for signer #{}", + self.dkg_id, + polys.len(), + self.signer.frost_signer.get_id(), + ); + + for poly in &polys { + let public_share = DkgPublicShare { + dkg_id: self.dkg_id, + dkg_public_id: self.dkg_public_id, + party_id: poly.id.id.get_u32(), + public_share: poly.clone(), + }; + + let public_share = MessageTypes::DkgPublicShare(public_share); + msgs.push(public_share); + } + + self.move_to(States::DkgPublicGather)?; + Ok(msgs) + } + + fn dkg_private_begin(&mut self) -> Result, Error> { + let mut rng = OsRng; + let mut msgs = vec![]; + for (key_id, private_shares) in &self.signer.frost_signer.get_shares() { + info!( + "signer {} sending dkg private share for key_id #{}", + self.signer.signer_id, key_id + ); + // encrypt each share for the recipient + let mut encrypted_shares = HashMap::new(); + + for (dst_key_id, private_share) in private_shares { + debug!( + "encrypting dkg private share for key_id #{}", + dst_key_id + 1 + ); + let compressed = + Compressed::from(self.public_keys.key_ids[&(dst_key_id + 1)].to_bytes()); + let dst_public_key = Point::try_from(&compressed).unwrap(); + let shared_secret = make_shared_secret(&self.network_private_key, &dst_public_key); + let encrypted_share = + encrypt(&shared_secret, &private_share.to_bytes(), &mut rng).unwrap(); + + encrypted_shares.insert(*dst_key_id, encrypted_share); + } + + let private_shares = DkgPrivateShares { + dkg_id: self.dkg_id, + key_id: *key_id, + private_shares: encrypted_shares, + }; + + let private_shares = MessageTypes::DkgPrivateShares(private_shares); + msgs.push(private_shares); + } + + self.move_to(States::DkgPrivateGather)?; + Ok(msgs) + } + + fn dkg_public_share( + &mut self, + dkg_public_share: DkgPublicShare, + ) -> Result, Error> { + self.commitments + .insert(dkg_public_share.party_id, dkg_public_share.public_share); + info!( + "received DkgPublicShare from key #{} {}/{}", + dkg_public_share.party_id, + self.commitments.len(), + self.total_keys + ); + Ok(vec![]) + } + + fn dkg_private_shares( + &mut self, + dkg_private_shares: DkgPrivateShares, + ) -> Result, Error> { + let shares_clone = dkg_private_shares.private_shares.clone(); + self.shares + .insert(dkg_private_shares.key_id, dkg_private_shares.private_shares); + info!( + "received DkgPrivateShares from key #{} {}/{} {:?}", + dkg_private_shares.key_id, + self.shares.len(), + self.total_keys, + shares_clone.keys(), + ); + Ok(vec![]) + } + + fn vote_miners_out_of_pool( + &mut self, + vote_out_request: VoteOutActorRequest, + ) -> Result, Error> { + let mut node_clone = self.local_stacks_node.clone(); + let mut wallet_clone = self.stacks_wallet.clone(); + let mut nonce = self.local_stacks_node.next_nonce(&self.stacks_wallet.address()).unwrap_or(0); + + thread::spawn(move || { + let mut actors_to_be_voted_out = vote_out_request.actors_to_be_voted_out.clone(); + loop { + for actor in actors_to_be_voted_out.clone() { + match node_clone.clone().get_miners_list(wallet_clone.address()) { + Ok(miners_list) => { + if miners_list.contains(&actor) { + match node_clone.clone().is_proposed_for_removal(wallet_clone.address(), &actor) { + Ok(value) => { + if value == true { + match wallet_clone.vote_positive_remove_request(nonce, actor) { + Ok(tx) => { + match node_clone.broadcast_transaction(&tx) { + Ok(()) => { + info!("Successfully voted out {:?}", actor.to_string()); + actors_to_be_voted_out.retain(|user| user != &actor); + nonce += 1; + } + Err(e) => { + info!("Error broadcasting the voting out transaction for {:?}: {:?}", actor.to_string(), e); + } + } + } + Err(e) => { + info!("Couldn't make vote out transaction for {:?}: {:?}", &actor.to_string(), e); + } + } + } + } + Err(e) => { + info!("Could not check if {:?} is proposed for removal.", actor.to_string()); + } + } + } + else { + actors_to_be_voted_out.retain(|user| user != &actor); + } + } + Err(e) => { + info!("Couldn't get miner's list: {:?}", e) + } + } + } + if actors_to_be_voted_out.len() == 0 { + break + } + sleep(Duration::from_secs(300)); + } + }); + + Ok(vec![]) + } + + fn add_txid_to_list( + &mut self, + txid_response: POXTxidResponse, + ) -> Result, Error> { + let pox_addresses = vec![ + Address::from_str("bcrt1phvt5tfz4hlkth0k7ls9djweuv9rwv5a0s5sa9085umupftnyalxq0zx28d").unwrap(), + Address::from_str("bcrt1pdsavc4yrdq0sdmjcmf7967eeem2ny6vzr4f8m7dyemcvncs0xtwsc85zdq").unwrap() + ]; + let pox_sc_amount = self.local_stacks_node.get_pool_total_spend_per_block(self.stacks_wallet.address()).unwrap_or(0) as u64; + self.previous_transactions.push((get_current_block_height(&self.local_bitcoin_node), txid_response.txid, pox_addresses, pox_sc_amount)); + + Ok(vec![]) + } + + fn check_if_transaction_is_good( + &mut self, + script_address: &Address, + ) -> bool { + // once current block height is greater than tuple's block height (1st field) check if: + // - call bitcoin_node.get_transacion(txid) returns Vec<(address, amount)> + // - all (both) pox addresses from this list (2nd field) are contained in the txid call (3rd field) + // - their total amount (4th field) match the sum of the amount for them in the txid call (3rd field) + // - each signer's output back to script is contained in the outputs from the txid call (3rd field) + + let prev_transactions = &self.previous_transactions; + let current_block = get_current_block_height(&self.local_bitcoin_node); + let mut remaining_transactions = vec![]; + let mut are_transactions_good = true; + + for (block_height, txid, pox_addresses, pox_amount) in prev_transactions.iter() { + let amount_back: Vec<(u64, u64)> = self.amount_back_to_script.clone() + .iter() + .filter(|&(first, _)| first != block_height) + .cloned() + .collect(); + self.amount_back_to_script = self.amount_back_to_script.iter() + .filter(|&(height, _)| height > block_height) + .cloned() + .collect(); + let can_verify_script_output = if let Some((block_height_back, _)) = amount_back.first() { + if block_height_back == block_height { + true + } + else { + info!("No data found for block height found {} in your amount back to script list.", block_height); + false + } + } + else { + info!("Failed to fetch amount back to script for block height {} from your list.", block_height); + false + }; + + if *block_height < current_block { + match &self.local_bitcoin_node.get_transacion(&txid) { + Ok(data) => { + let address_set: HashSet = pox_addresses.iter().map(|address| address.to_string()).collect(); + let mut sum_of_matched_amounts = 0; + let mut pox_addresses_matched = vec![]; + let mut found_address_in_outputs = false; + + for (address, amount) in data.iter() { + if address_set.contains(address) { + sum_of_matched_amounts += amount; + pox_addresses_matched.push(address); + } + if can_verify_script_output { + if address == &script_address.to_string() { + found_address_in_outputs = true; + if *amount != amount_back[0].1 { + info!("The amount back to your script did not match the expected value: {} != {}", amount, amount_back[0].1); + are_transactions_good = false; + } + } + } + } + + if can_verify_script_output && !found_address_in_outputs { + info!("Could not find your script address in the outputs!"); + are_transactions_good = false; + } + + if sum_of_matched_amounts != *pox_amount { + info!("The sum of the amount to PoX of the transaction included in block {} did not match the expected value: {} != {}. Txid: {:?}", block_height, sum_of_matched_amounts, pox_amount, txid); + are_transactions_good = false; + } else if pox_addresses_matched.len() != address_set.len() { + info!("PoX addresses mismatch in transaction from block {}. Expected:\n{:#?}\nActual:\n{:#?}", block_height, pox_addresses.iter().map(|address| address.to_string()), pox_addresses_matched); + are_transactions_good = false; + } + } + Err(e) => { + info!("Couldn't fetch transaction from txid {:?}: {:?}", &txid.to_string(), e); + are_transactions_good = false; + } + } + } + else { + remaining_transactions.push((*block_height, *txid, pox_addresses.clone(), *pox_amount)); + } + } + + self.previous_transactions = remaining_transactions; + are_transactions_good + } + + fn degen_create_script( + &mut self, + degens_create_script: DegensScriptRequest, + ) -> Result, Error> { + let current_block_height = get_current_block_height(&self.local_bitcoin_node); + + if let Ok(mut array) = self.pox_transactions_block_heights.lock() { + array.push(current_block_height); + } + + let secp = Secp256k1::new(); + let keypair = KeyPair::from_secret_key(&secp, &self.bitcoin_private_key); + + let aggregate_compressed = degens_create_script.aggregate_public_key.compress(); + let aggregate_x_only = PublicKey::from_slice(aggregate_compressed.as_bytes()).unwrap().to_x_only_pubkey(); + + let script_1 = create_script_refund(&self.bitcoin_xonly_public_key, 100); + let script_2 = create_script_unspendable(); + + // TODO: degens - change keypair xonly back to aggregate_x_only after done with testing and remove the match + let (tap_info, script_address) = create_tree(&secp, keypair.x_only_public_key().0, self.bitcoin_network, &script_1, &script_2); + + // TODO: check this, if it's true all good, if it's not then there was an issue in one of the previous transactions + let are_previous_transactions_good = self.check_if_transaction_is_good(&script_address); + + let number_of_signers = self.local_stacks_node.get_miners_list(&self.stacks_wallet.address()).unwrap_or(vec![self.stacks_wallet.address().clone()]).len() as u64 - 1; + let fee_to_script = self.fee_to_script; + let fee_to_pox = degens_create_script.fee_to_pox / number_of_signers; + let total_fees = fee_to_script + fee_to_pox; + let amount_to_script = match self.fund_each_block { + true => self.local_stacks_node.get_pool_total_spend_per_block(self.stacks_wallet.address()).expect("Failed to retrieve amount to script") as u64 / number_of_signers, + false => self.amount_to_script, + }; + + let script_utxo = self.run_script_funding_phase( + current_block_height, + secp, + keypair, + script_1, + script_address, + &tap_info, + amount_to_script, + fee_to_script, + fee_to_pox, + total_fees, + number_of_signers, + ); + + let mut msgs = vec![]; + + let response = DegensScriptResponse { + signer_id: self.signer.signer_id, + stacks_address: self.stacks_address, + merkle_root: tap_info.merkle_root().unwrap(), + utxo: script_utxo, + }; + + let response = MessageTypes::DegensCreateScriptsResponse(response); + msgs.push(response); + + Ok(msgs) + } + + fn run_script_funding_phase( + &mut self, + current_block_height: u64, + secp: Secp256k1, + keypair: KeyPair, + script_1: Script, + script_address: Address, + tap_info: &TaprootSpendInfo, + amount_to_script: u64, + fee_to_script: u64, + fee_to_pox: u64, + total_fees: u64, + number_of_signers: u64, + ) -> Result { + let total_amount = self.local_stacks_node.get_pool_total_spend_per_block(self.stacks_wallet.address()).unwrap_or(0) as u64; + + let mut script_utxo: Result = Err(InvalidUTXO); + let mut script_address_needs_funds = true; + + // Check if the script address is already loaded into the wallet. If not, try loading it until the wallet stops refreshing. + if self.local_bitcoin_node.list_descriptors().unwrap_or(vec![]).contains(&script_address) == false { + let mut number_of_iterations = 0; + loop { + match self.local_bitcoin_node.load_wallet(&script_address) { + Ok(()) => { + break + } + Err(e) => { + if number_of_iterations % 10 == 0 { + info!("Couldn't load script address: {:?}", e) + } + // How many times to try loading the script address + if number_of_iterations == 100 { + break + } + } + } + number_of_iterations += 1; + sleep(Duration::from_secs(1)); + } + } + + let script_utxos = self.local_bitcoin_node.list_unspent(&script_address).unwrap_or(vec![]); + let mut run_refund_phase = false; + + // Check if the script address contains enough money to send to PoX. If yes, we found the UTXO, otherwise run refund and fund phases. + for utxo in &script_utxos { + if amount_to_script <= utxo.amount { + script_utxo = Ok(utxo.clone()); + script_address_needs_funds = false; + + if !self.fund_each_block && utxo.amount > (total_amount + fee_to_pox) / number_of_signers { + let amount_back = utxo.amount - ((total_amount + fee_to_pox) / number_of_signers); + self.amount_back_to_script.push((current_block_height, amount_back)); + } + } + else { + script_utxo = Err(InvalidUTXO); + run_refund_phase = true; + } + } + + // Run the refund phase + if run_refund_phase { + let mut amount_to_refund: u64 = 0; + + for utxo in &script_utxos { + amount_to_refund += utxo.amount; + } + + if amount_to_refund > fee_to_script { + amount_to_refund -= fee_to_script; + + let refund_tx = create_refund_tx(&script_utxos, self.bitcoin_wallet.address(), amount_to_refund); + + let mut txout_vec: Vec = vec![]; + script_utxos.iter().for_each(|utxo| { + txout_vec.push(TxOut { + value: utxo.amount, + script_pubkey: Script::from_str(utxo.scriptPubKey.as_str()).unwrap(), + }); + }); + + let signed_refund_tx = sign_tx_script_refund(&secp, &refund_tx, &txout_vec, &script_1, &keypair, tap_info); + + match self.local_bitcoin_node.broadcast_transaction(&signed_refund_tx) { + Ok(txid) => { + info!("Successfully refunded from script. Txid: {:?}", txid) + } + Err(e) => { + info!("Couldn't broadcast refund transaction: {:?}", e) + } + }; + } + else { + info!("You don't have enough money to refund from script address!") + } + } + + // Run the funding phase + if script_address_needs_funds == true { + info!("Not enough money on script. Running the funding phase!"); + + let mut unspent_list_signer = self + .local_bitcoin_node + .list_unspent(&self.bitcoin_wallet.address()) + .unwrap_or(vec![]); + + let mut valid_utxos = vec![]; + let mut total_amount: u64 = 0; + + // This works, but we have no confirmations because of the node not mining blocks + // Switch back to it once the issue is fixed + // unspent_list_signer.sort_by(|a, b| b.confirmations.partial_cmp(&a.confirmations).unwrap()); + + if &unspent_list_signer.len() > &2 { + unspent_list_signer.sort_by(|a, b| b.amount.partial_cmp(&a.amount).unwrap()); + } + + for utxo in &unspent_list_signer { + if total_amount < amount_to_script + total_fees { + total_amount += utxo.amount; + valid_utxos.push(utxo.clone()); + continue + } + break + } + + if total_amount < amount_to_script + total_fees { + info!("You don't have enough money to fund the script!"); + script_utxo = Err(UTXOAmount); + } + else { + let mut unspent_list_txout: Vec = vec![]; + valid_utxos.iter().for_each(|utxo| { + unspent_list_txout.push(TxOut { + value: utxo.amount, + script_pubkey: Script::from_str(utxo.scriptPubKey.as_str()).unwrap(), + }); + }); + + let prevouts_signer = Prevouts::All(unspent_list_txout.as_slice()); + + let user_to_script_unsigned = create_tx_from_user_to_script( + &valid_utxos, + &self.bitcoin_wallet.address(), + &script_address, + amount_to_script, + fee_to_script, + fee_to_pox, + ); + + let user_to_script_signed = + sign_tx_user_to_script(&secp, &user_to_script_unsigned, &prevouts_signer, &keypair); + + match self.local_bitcoin_node.broadcast_transaction(&user_to_script_signed) { + Ok(txid) => { + info!("Successfully funded script. Txid: {:?}", txid) + } + Err(e) => { + info!("Couldn't broadcast fund script transaction: {:?}", e) + } + }; + + for utxo in self.local_bitcoin_node.list_unspent(&script_address).unwrap_or(vec![]) { + if amount_to_script <= utxo.amount { + if !self.fund_each_block && utxo.amount > (total_amount + fee_to_pox) / number_of_signers { + let amount_back = utxo.amount - ((total_amount + fee_to_pox) / number_of_signers); + self.amount_back_to_script.push((current_block_height, amount_back)); + } + script_utxo = Ok(utxo); + } else { + script_utxo = Err(InvalidUTXO); + } + } + } + } + + if script_utxo.is_err() { + info!("Couldn't get any valid transaction from script!"); + } + else { + info!("Found a good UTXO on your script, proceeding with it!"); + } + + script_utxo + } +} + +impl From<&FrostSigner> for SigningRound { + fn from(signer: &FrostSigner) -> Self { + let signer_id = signer.signer_id; + let signer_private_key = signer.config.stacks_private_key; + assert!(signer_id > 0 && signer_id <= signer.config.total_signers); + let key_ids = signer.config.signer_key_ids[&signer_id] + .iter() + .map(|i| i - 1) + .collect::>(); + + assert!(signer.config.keys_threshold <= signer.config.total_keys); + let mut rng = OsRng; + let frost_signer = v1::Signer::new( + signer_id, + &key_ids, + signer.config.total_keys, + signer.config.keys_threshold, + &mut rng, + ); + + let network_private_key = signer.config.network_private_key; + let public_keys = signer.config.public_keys.clone(); + + SigningRound { + dkg_id: 1, + dkg_public_id: 1, + sign_id: 1, + sign_nonce_id: 1, + threshold: signer.config.keys_threshold, + total_keys: signer.config.total_keys, + total_signers: signer.config.total_signers, + signer: Signer { + frost_signer, + signer_id, + }, + state: States::Idle, + commitments: BTreeMap::new(), + shares: HashMap::new(), + public_nonces: vec![], + network_private_key, + public_keys, + contract_name: signer.config.contract_name.clone(), + contract_address: signer.config.contract_address, + aggregate_public_key: Point::new(), + stacks_private_key: signer.config.stacks_private_key, + stacks_address: signer.config.stacks_address, + stacks_node_rpc_url: signer.config.stacks_node_rpc_url.clone(), + local_stacks_node: signer.config.local_stacks_node.clone(), + stacks_wallet: signer.config.stacks_wallet.clone(), + stacks_version: signer.config.stacks_version, + bitcoin_private_key: signer.config.bitcoin_private_key, + bitcoin_xonly_public_key: signer.config.bitcoin_xonly_public_key, + bitcoin_node_rpc_url: signer.config.bitcoin_node_rpc_url.clone(), + local_bitcoin_node: signer.config.local_bitcoin_node.clone(), + bitcoin_wallet: signer.config.bitcoin_wallet.clone(), + transaction_fee: signer.config.transaction_fee, + amount_to_script: signer.config.amount_to_script, + fee_to_script: signer.config.fee_to_script, + bitcoin_network: signer.config.bitcoin_network, + previous_transactions: vec![], + amount_back_to_script: vec![], + script_addresses: BTreeMap::new(), + pox_transactions_block_heights: Arc::clone(&signer.config.pox_transactions_block_heights), + fund_each_block: signer.config.fund_each_block, + } + } +} + +#[cfg(test)] +mod test { + use bitcoin::Network; + use hashbrown::HashMap; + use rand_core::{CryptoRng, OsRng, RngCore}; + use wsts::{common::PolyCommitment, schnorr::ID, Scalar}; + + use crate::signing_round::{ + DkgPrivateShares, DkgPublicShare, DkgStatus, MessageTypes, SigningRound, + }; + use crate::state_machine::States; + + fn get_rng() -> impl RngCore + CryptoRng { + let rnd = OsRng; + //rand::rngs::StdRng::seed_from_u64(rnd.next_u64()) // todo: fix trait `rand_core::RngCore` is not implemented for `StdRng` + rnd + } + + #[test] + fn dkg_public_share() { + let mut rnd = get_rng(); + let mut signing_round = + SigningRound::new(1, 1, 1, 1, vec![1], Default::default(), Default::default(), Network::Regtest); + let public_share = DkgPublicShare { + dkg_id: 0, + party_id: 0, + public_share: PolyCommitment { + id: ID::new(&Scalar::new(), &Scalar::new(), &mut rnd), + A: vec![], + }, + dkg_public_id: 0, + }; + signing_round.dkg_public_share(public_share).unwrap(); + assert_eq!(1, signing_round.commitments.len()) + } + + #[test] + fn dkg_private_shares() { + let mut signing_round = + SigningRound::new(1, 1, 1, 1, vec![1], Default::default(), Default::default(), Network::Regtest); + let mut private_shares = DkgPrivateShares { + dkg_id: 0, + key_id: 0, + private_shares: HashMap::new(), + }; + private_shares.private_shares.insert(1, Vec::new()); + signing_round.dkg_private_shares(private_shares).unwrap(); + assert_eq!(1, signing_round.shares.len()) + } + + #[test] + fn public_shares_done() { + let mut rnd = get_rng(); + let mut signing_round = + SigningRound::new(1, 1, 1, 1, vec![1], Default::default(), Default::default(), Network::Regtest); + // publich_shares_done starts out as false + assert_eq!(false, signing_round.public_shares_done()); + + // meet the conditions for all public keys received + signing_round.state = States::DkgPublicGather; + signing_round.commitments.insert( + 1, + PolyCommitment { + id: ID::new(&Scalar::new(), &Scalar::new(), &mut rnd), + A: vec![], + }, + ); + + // public_shares_done should be true + assert!(signing_round.public_shares_done()); + } + + #[test] + fn can_dkg_end() { + let mut rnd = get_rng(); + let mut signing_round = + SigningRound::new(1, 1, 1, 1, vec![1], Default::default(), Default::default(), Network::Regtest); + // can_dkg_end starts out as false + assert_eq!(false, signing_round.can_dkg_end()); + + // meet the conditions for DKG_END + signing_round.state = States::DkgPrivateGather; + signing_round.commitments.insert( + 1, + PolyCommitment { + id: ID::new(&Scalar::new(), &Scalar::new(), &mut rnd), + A: vec![], + }, + ); + let shares: HashMap> = HashMap::new(); + signing_round.shares.insert(1, shares); + + // can_dkg_end should be true + assert!(signing_round.can_dkg_end()); + } + + #[test] + fn dkg_ended() { + let mut signing_round = + SigningRound::new(1, 1, 1, 1, vec![1], Default::default(), Default::default(), Network::Regtest); + match signing_round.dkg_ended() { + Ok(dkg_end) => match dkg_end { + MessageTypes::DkgEnd(dkg_end) => match dkg_end.status { + DkgStatus::Failure(_) => assert!(true), + _ => assert!(false), + }, + _ => assert!(false), + }, + _ => assert!(false), + } + } +} diff --git a/degen-base-signer/src/stacks_node/client.rs b/degen-base-signer/src/stacks_node/client.rs new file mode 100644 index 00000000..fc700ad2 --- /dev/null +++ b/degen-base-signer/src/stacks_node/client.rs @@ -0,0 +1,1702 @@ +use std::time::{Duration, Instant}; + +use crate::stacks_node::{Error as StacksNodeError, PegInOp, PegOutRequestOp, StacksNode}; +use bitcoin::XOnlyPublicKey; +use stackslib::{ + chainstate::stacks::StacksTransaction, + codec::StacksMessageCodec, + types::chainstate::StacksAddress, + vm::{types::SequenceData, ClarityName, ContractName, Value as ClarityValue}, +}; +use stackslib::util::hash::Hash160; +use stackslib::vm::types::{PrincipalData, StandardPrincipalData}; +use crate::config::{MinerStatus, PublicKeys, SignerKeyIds}; +use reqwest::{ + blocking::{Client, Response}, + StatusCode, +}; +use serde_json::{json, Value}; +use tracing::{debug, info}; +use url::Url; +use wsts::ecdsa::PublicKey; + +/// Kinds of stacks node broadcast errors that can occur +#[derive(Debug, thiserror::Error)] +pub enum BroadcastError { + #[error("Fee too low. Expected: {0}, Actual: {1}")] + FeeTooLow(u64, u64), + #[error("Not enough funds: {0}")] + NotEnoughFunds(String), + #[error("Conflicting nonce in mempool")] + ConflictingNonceInMempool, + #[error("{0}")] + Other(String), +} + +impl From<&serde_json::Value> for BroadcastError { + fn from(value: &serde_json::Value) -> Self { + let reason = value + .get("reason") + .and_then(|reason| reason.as_str()) + .unwrap_or("Unknown Reason"); + let reason_data = value.get("reason_data"); + match reason { + "FeeTooLow" => { + let expected = value + .get("expected") + .and_then(|expected| expected.as_u64()) + .unwrap_or(0); + let actual = value + .get("actual") + .and_then(|actual| actual.as_u64()) + .unwrap_or(0); + BroadcastError::FeeTooLow(expected, actual) + } + "NotEnoughFunds" => BroadcastError::NotEnoughFunds( + reason_data.unwrap_or(&json!("No Reason Data")).to_string(), + ), + "ConflictingNonceInMempool" => BroadcastError::ConflictingNonceInMempool, + _ => BroadcastError::Other(reason.to_string()), + } + } +} + +#[derive(Clone, Debug)] +pub struct NodeClient { + node_url: Url, + client: Client, + contract_name: ContractName, + contract_address: StacksAddress, + next_nonce: Option, +} + +impl NodeClient { + pub fn new( + node_url: Url, + contract_name: ContractName, + contract_address: StacksAddress, + ) -> Self { + Self { + node_url, + client: Client::new(), + contract_name, + contract_address, + next_nonce: None, + } + } + + fn build_url(&self, route: &str) -> Result { + Ok(self.node_url.join(route)?) + } + + fn get_response(&self, route: &str) -> Result { + let url = self.build_url(route)?; + debug!("Sending Request to Stacks Node: {}", &url); + let now = Instant::now(); + let notify = |_err, dur| { + debug!("Failed to connect to {}. Next attempt in {:?}", &url, dur); + }; + + let send_request = || { + if now.elapsed().as_secs() > 5 { + debug!("Timeout exceeded."); + return Err(backoff::Error::Permanent(StacksNodeError::Timeout)); + } + let request = self.client.get(url.as_str()); + let response = request.send().map_err(StacksNodeError::ReqwestError)?; + Ok(response) + }; + let backoff_timer = backoff::ExponentialBackoffBuilder::new() + .with_initial_interval(Duration::from_millis(2)) + .with_max_interval(Duration::from_millis(128)) + .build(); + + let response = backoff::retry_notify(backoff_timer, send_request, notify) + .map_err(|_| StacksNodeError::Timeout)?; + + Ok(response) + } + + fn get_burn_ops(&self, block_height: u64, op: &str) -> Result, StacksNodeError> + where + T: serde::de::DeserializeOwned, + { + let json = self + .get_response(&format!("/v2/burn_ops/{block_height}/{op}"))? + .json::() + .map_err(|_| StacksNodeError::UnknownBlockHeight(block_height))?; + Ok(serde_json::from_value(json[op].clone())?) + } + + fn num_signers(&self, sender: &StacksAddress) -> Result { + let function_name = "get-num-signers"; + let total_signers_hex = self.call_read(sender, function_name, &[])?; + let total_signers = ClarityValue::try_deserialize_hex_untyped(&total_signers_hex)?; + if let ClarityValue::UInt(total_signers) = total_signers { + Ok(total_signers) + } else { + Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + total_signers, + )) + } + } + + fn signer_data( + &self, + sender: &StacksAddress, + id: u128, + public_keys: &mut PublicKeys, + signer_key_ids: &mut SignerKeyIds, + ) -> Result<(), StacksNodeError> { + let function_name = "get-signer-data"; + let signer_data_hex = self.call_read( + sender, + function_name, + &[&ClarityValue::UInt(id).to_string()], + )?; + let signer_data = ClarityValue::try_deserialize_hex_untyped(&signer_data_hex)?; + if let ClarityValue::Optional(optional_data) = signer_data.clone() { + if let Some(ClarityValue::Tuple(tuple_data)) = optional_data.data.map(|boxed| *boxed) { + let public_key = + if let Some(ClarityValue::Sequence(SequenceData::Buffer(public_key))) = + tuple_data.data_map.get(&ClarityName::from("public-key")) + { + PublicKey::try_from(public_key.data.as_slice()).map_err(|_| { + StacksNodeError::MalformedClarityValue( + function_name.to_string(), + signer_data.clone(), + ) + })? + } else { + return Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + signer_data, + )); + }; + public_keys + .signers + .insert(id.try_into().unwrap(), public_key); + if let Some(ClarityValue::Sequence(SequenceData::List(keys_ids))) = + tuple_data.data_map.get(&ClarityName::from("key-ids")) + { + let mut this_signer_key_ids = Vec::new(); + for key_id in &keys_ids.data { + if let ClarityValue::UInt(key_id) = key_id { + public_keys + .key_ids + .insert((*key_id).try_into().unwrap(), public_key); + this_signer_key_ids.push((*key_id).try_into().unwrap()); + } else { + return Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + signer_data, + )); + } + } + signer_key_ids.insert(id.try_into().unwrap(), this_signer_key_ids); + } + } else { + return Err(StacksNodeError::NoSignerData(id)); + } + } + Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + signer_data, + )) + } + + fn call_read( + &self, + sender: &StacksAddress, + function_name: &str, + function_args: &[&str], + ) -> Result { + debug!("Calling read-only function {}...", function_name); + let body = json!({"sender": sender.to_string(), "arguments": function_args}).to_string(); + let url = self.build_url(&format!( + "/v2/contracts/call-read/{}/{}/{function_name}", + self.contract_address, + self.contract_name.as_str() + ))?; + + let response = self + .client + .post(url) + .header("content-type", "application/json") + .body(body) + .send()? + .json::()?; + + debug!("response: {:?}", response); + if !response + .get("okay") + .map(|val| val.as_bool().unwrap_or(false)) + .unwrap_or(false) + { + let cause = response + .get("cause") + .ok_or(StacksNodeError::InvalidJsonEntry("cause".to_string()))?; + return Err(StacksNodeError::ReadOnlyFailure(format!( + "{}: {}", + function_name, cause + ))); + } + let result = response + .get("result") + .ok_or(StacksNodeError::InvalidJsonEntry("result".to_string()))? + .as_str() + .ok_or_else(|| StacksNodeError::ReadOnlyFailure("Expected string result.".to_string()))? + .to_string(); + Ok(result) + } +} + +impl StacksNode for NodeClient { + fn get_peg_in_ops(&self, block_height: u64) -> Result, StacksNodeError> { + debug!("Retrieving peg-in ops..."); + self.get_burn_ops::(block_height, "peg_in") + } + + fn get_peg_out_request_ops( + &self, + block_height: u64, + ) -> Result, StacksNodeError> { + debug!("Retrieving peg-out request ops..."); + self.get_burn_ops::(block_height, "peg_out_request") + } + + fn burn_block_height(&self) -> Result { + debug!("Retrieving burn block height..."); + let json = self.get_response("/v2/info")?.json::()?; + let entry = "burn_block_height"; + json[entry] + .as_u64() + .ok_or_else(|| StacksNodeError::InvalidJsonEntry(entry.to_string())) + } + + fn next_nonce(&mut self, address: &StacksAddress) -> Result { + debug!("Retrieving next nonce..."); + let address = address.to_string(); + let entry = "possible_next_nonce"; + let route = format!("/extended/v1/address/{}/nonces", address); + let response = self.get_response(&route)?; + if response.status() == StatusCode::NOT_FOUND { + return Err(StacksNodeError::UnknownAddress(address)); + } + let json = response + .json::() + .map_err(|_| StacksNodeError::BehindChainTip)?; + let nonce = json + .get(entry) + .and_then(|nonce| nonce.as_u64()) + .ok_or_else(|| StacksNodeError::InvalidJsonEntry(entry.to_string()))?; + self.next_nonce = Some(nonce); + Ok(nonce) + } + + fn get_user_balance(&mut self, address: &StacksAddress) -> Result { + debug!("Retrieving account balance..."); + let address = address.to_string(); + let entry = "balance"; + let route = format!("/v2/accounts/{}", address); + let response = self.get_response(&route)?; + if response.status() == StatusCode::NOT_FOUND { + return Err(StacksNodeError::UnknownAddress(address)); + } + let json = response + .json::() + .map_err(|_| StacksNodeError::BehindChainTip)?; + info!("{:#?}", json + .get(entry)); + let balance = json + .get(entry) + .and_then(|balance| Some(u64::from_str_radix(&balance.as_str().unwrap()[2..], 16).unwrap())) + .ok_or_else(|| StacksNodeError::InvalidJsonEntry(entry.to_string()))?; + + Ok(balance) + } + + fn get_mempool_transactions(&mut self) -> Result { + debug!("Retrieving mempool transactions..."); + let entry = "results"; + let route = format!("/extended/v1/tx/mempool?limit=50"); + let response = self.get_response(&route)?; + // if response.status() == StatusCode::NOT_FOUND { + // return Err(StacksNodeError::UnknownAddress(address)); + // } + let json = response + .json::() + .map_err(|_| StacksNodeError::BehindChainTip)?; + let transactions = json + .get(entry) + .and_then(|transaction| transaction.as_array()) + .ok_or_else(|| StacksNodeError::InvalidJsonEntry(entry.to_string()))?; + let contract_call_transactions: Vec<&Value> = transactions + .iter() + .filter(|tx| tx["tx_type"] == "contract_call") + .collect(); + + println!("{:#?}\n{:#?}", contract_call_transactions, contract_call_transactions.len()); + Ok(0) + } + + fn broadcast_transaction(&self, tx: &StacksTransaction) -> Result<(), StacksNodeError> { + debug!("Broadcasting transaction..."); + debug!("Transaction: {:?}", tx); + let url = self.build_url("/v2/transactions")?; + let mut buffer = vec![]; + + tx.consensus_serialize(&mut buffer)?; + + let response = self + .client + .post(url) + .header("content-type", "application/octet-stream") + .body(buffer) + .send()?; + + // TODO: degens - fix broadcast stx transaction + if response.status() != StatusCode::OK { + let json_response = response.json::()?; + return Err(StacksNodeError::from(BroadcastError::from(&json_response))); + } + + Ok(()) + } + + fn keys_threshold(&self, sender: &StacksAddress) -> Result { + let function_name = "get-threshold"; + let threshold_hex = self.call_read(sender, function_name, &[])?; + let threshold = ClarityValue::try_deserialize_hex_untyped(&threshold_hex)?; + if let ClarityValue::UInt(keys_threshold) = threshold { + Ok(keys_threshold) + } else { + Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + threshold, + )) + } + } + + fn public_keys(&self, sender: &StacksAddress) -> Result { + let total_signers = self.num_signers(sender)?; + // Retrieve all the signers + let mut public_keys = PublicKeys::default(); + let mut signer_key_ids = SignerKeyIds::default(); + for id in 1..=total_signers { + self.signer_data(sender, id, &mut public_keys, &mut signer_key_ids)?; + } + Ok(public_keys) + } + + fn signer_key_ids(&self, sender: &StacksAddress) -> Result { + let total_signers = self.num_signers(sender)?; + // Retrieve all the signers + let mut public_keys = PublicKeys::default(); + let mut signer_key_ids = SignerKeyIds::default(); + for id in 1..=total_signers { + self.signer_data(sender, id, &mut public_keys, &mut signer_key_ids)?; + } + Ok(signer_key_ids) + } + + fn coordinator_public_key( + &self, + sender: &StacksAddress, + ) -> Result, StacksNodeError> { + Ok(None) + // let function_name = "get-coordinator-data"; + // let coordinator_data_hex = self.call_read(sender, function_name, &[])?; + // let coordinator_data = ClarityValue::try_deserialize_hex_untyped(&coordinator_data_hex)?; + // if let ClarityValue::Optional(optional_data) = coordinator_data.clone() { + // if let Some(ClarityValue::Tuple(tuple_data)) = optional_data.data.map(|boxed| *boxed) { + // let value = tuple_data + // .data_map + // .get(&ClarityName::from("key")) + // .ok_or_else(|| { + // StacksNodeError::MalformedClarityValue( + // function_name.to_string(), + // coordinator_data.clone(), + // ) + // })?; + // if let ClarityValue::Sequence(SequenceData::Buffer(coordinator_public_key)) = value + // { + // let public_key = PublicKey::try_from(coordinator_public_key.data.as_slice()) + // .map_err(|_| { + // StacksNodeError::MalformedClarityValue( + // function_name.to_string(), + // coordinator_data, + // ) + // })?; + // return Ok(Some(public_key)); + // } else { + // return Err(StacksNodeError::MalformedClarityValue( + // function_name.to_string(), + // coordinator_data, + // )); + // } + // } + // return Ok(None); + // // Err(StacksNodeError::MalformedClarityValue( + // // function_name.to_string(), + // // coordinator_data, + // // )) + // } else { + // Ok(None) + // } + } + + fn bitcoin_wallet_public_key( + &self, + sender: &StacksAddress, + ) -> Result, StacksNodeError> { + Ok(None) + // let function_name = "get-bitcoin-wallet-public-key"; + // let bitcoin_wallet_public_key_hex = self.call_read(sender, function_name, &[])?; + // let bitcoin_wallet_public_key = + // ClarityValue::try_deserialize_hex_untyped(&bitcoin_wallet_public_key_hex)?; + // if let ClarityValue::Optional(optional_data) = bitcoin_wallet_public_key.clone() { + // if let Some(ClarityValue::Sequence(SequenceData::Buffer(public_key))) = + // optional_data.data.map(|boxed| *boxed) + // { + // let xonly_pubkey = XOnlyPublicKey::from_slice(&public_key.data).map_err(|_| { + // StacksNodeError::MalformedClarityValue( + // function_name.to_string(), + // bitcoin_wallet_public_key, + // ) + // })?; + // return Ok(Some(xonly_pubkey)); + // } else { + // return Ok(None); + // } + // } + // Err(StacksNodeError::MalformedClarityValue( + // function_name.to_string(), + // bitcoin_wallet_public_key, + // )) + } + + fn get_status(&self, sender: &StacksAddress) -> Result { + let function_name = "get-address-status"; + + let data_hex = self.call_read(sender, function_name, &[("0x".to_owned() + &(hex::encode(ClarityValue::Principal(PrincipalData::from(*sender)).serialize_to_vec()))).as_str()])?; + // response and string + // match string based on message + let data = ClarityValue::try_deserialize_hex_untyped(&data_hex)?; + if let ClarityValue::Response(optional_data) = data.clone() { + let display_value: String = optional_data.data.clone().expect_ascii(); + // if let Ok(ClarityValue::Sequence(SequenceData::String(local_status))) = optional_data.data.expect_ascii() { + match display_value.as_str() { + "is-miner" => Ok(MinerStatus::Miner), + "is-pending" => Ok(MinerStatus::Pending), + "is-waiting" => Ok(MinerStatus::Waiting), + "is-none" => Ok(MinerStatus::NormalUser), + _ => Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + data, + )) + } + } else { + Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + data, + )) + } + } + + fn get_warn_number_user( + &self, + sender: &StacksAddress, + warned_address: &StacksAddress, + ) -> Result { + let function_name = "get-warnings-user"; + + let total_warnings_hex = self.call_read(sender, function_name, &[("0x".to_owned() + &(hex::encode(ClarityValue::Principal(PrincipalData::from(*warned_address)).serialize_to_vec()))).as_str()])?; + let total_warnings = ClarityValue::try_deserialize_hex_untyped(&total_warnings_hex)?; + if let ClarityValue::UInt(total_signers) = total_warnings { + Ok(total_signers) + } else { + Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + total_warnings, + )) + } + } + + fn get_notifier(&self, sender: &StacksAddress) -> Result { + let function_name = "get-notifier"; + let notifier_hex = self.call_read(sender, function_name, &[])?; + let notifier = ClarityValue::try_deserialize_hex_untyped(¬ifier_hex)?; + + if let ClarityValue::Principal(notifier) = notifier { + Ok(notifier) + } else { + Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + notifier, + )) + } + } + + fn is_blacklisted( + &self, + sender: &StacksAddress, + address: &StacksAddress + ) -> Result { + let function_name = "is-blacklisted"; + + let is_blacklisted_hex = self.call_read(sender, function_name, &[("0x".to_owned() + &(hex::encode(ClarityValue::Principal(PrincipalData::from(*address)).serialize_to_vec()))).as_str()])?; + let is_blacklisted = ClarityValue::try_deserialize_hex_untyped(&is_blacklisted_hex)?; + if let ClarityValue::Bool(is_blacklisted) = is_blacklisted { + Ok(is_blacklisted) + } else { + Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + is_blacklisted, + )) + } + } + + fn is_block_claimed( + &self, + sender: &StacksAddress, + block_height: u128 + ) -> Result { + let function_name = "is-claimed"; + + let is_claimed_hex = self.call_read(sender, function_name, &[("0x".to_owned() + &(hex::encode(ClarityValue::UInt(block_height).serialize_to_vec()))).as_str()])?; + let is_claimed = ClarityValue::try_deserialize_hex_untyped(&is_claimed_hex)?; + if let ClarityValue::Bool(is_claimed) = is_claimed { + Ok(is_claimed) + } else { + Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + is_claimed, + )) + } + } + + fn is_enough_voted_to_enter( + &self, + sender: &StacksAddress, + ) -> Result { + let function_name = "is-user-accepted"; + + let data_hex = self.call_read(sender, function_name, &[])?; + let data = ClarityValue::try_deserialize_hex_untyped(&data_hex)?; + if let ClarityValue::Bool(is_enough) = data { + Ok(is_enough) + } else { + Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + data, + )) + } + } + + fn is_enough_blocks_passed_for_pending_miners( + &self, + sender: &StacksAddress, + ) -> Result { + let function_name = "blocks-passed-for-pending-miners"; + + let data_hex = self.call_read(sender, function_name, &[])?; + let data = ClarityValue::try_deserialize_hex_untyped(&data_hex)?; + if let ClarityValue::Bool(is_enough) = data { + Ok(is_enough) + } else { + Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + data, + )) + } + } + + fn is_auto_exchange(&self, sender: &StacksAddress) -> Result { + let function_name = "get-auto-exchange"; + + let is_auto_exchange_hex = self.call_read(sender, function_name, &[("0x".to_owned() + &(hex::encode(ClarityValue::Principal(PrincipalData::from(*sender)).serialize_to_vec()))).as_str()])?; + let is_auto_exchange = ClarityValue::try_deserialize_hex_untyped(&is_auto_exchange_hex)?; + + if let ClarityValue::Bool(is_auto_exchange) = is_auto_exchange { + Ok(is_auto_exchange) + } else if let ClarityValue::Optional(is_auto_exchange) = is_auto_exchange.clone() { + Ok(false) + } else { + Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + is_auto_exchange, + )) + } + } + + fn get_reward_info_for_block_height( + &self, + sender: &StacksAddress, + block_height: u128, + ) -> Result<(u128, PrincipalData), StacksNodeError> { + let mut final_reward: u128 = 0; + let mut final_claimer: PrincipalData = PrincipalData::from(*sender); + // TODO: degens - this should not be the sender address in the end + + let function_name = "get-reward-at-block-read"; + let reward_data_hex = self.call_read( + sender, + function_name, + &[("0x".to_owned() + &(hex::encode(ClarityValue::UInt(block_height).serialize_to_vec()))).as_str()], + )?; + let reward_data = ClarityValue::try_deserialize_hex_untyped(&reward_data_hex)?; + // we have directly a tuple for those values, should not have optional + // it can be a value or null + + if let ClarityValue::Tuple(tuple_data) = reward_data.clone() { + if let Some(ClarityValue::Optional(local_reward)) = tuple_data.data_map.get(&ClarityName::from("reward")) { + if let ClarityValue::UInt(reward) = &*local_reward.data.clone().unwrap_or(Box::new(ClarityValue::Bool(false))) { + final_reward = reward.clone(); + } else { + return Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + reward_data + )); + } + } else { + return Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + reward_data + )); + } + + if let Some(ClarityValue::Optional(local_claimer)) = tuple_data.data_map.get(&ClarityName::from("claimer")) { + if let ClarityValue::Principal(claimer) = &*local_claimer.data.clone().unwrap_or(Box::new(ClarityValue::Bool(false))) { + final_claimer = claimer.clone(); + } else { + return Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + reward_data + )); + } + } else { + return Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + reward_data + )); + } + } else { + return Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + reward_data + )); + } + // TODO: check if this is the case here + // TODO: what if the sender actually is the one who claimed the block? this will return error for no reason + // if StacksAddress::from(final_claimer.clone()) == *sender { + // return Err(StacksNodeError::MalformedClarityValue( + // function_name.to_string(), + // reward_data + // )); + // } + + Ok((final_reward, final_claimer)) + } + + fn get_miners_list(&self, sender: &StacksAddress) -> Result, StacksNodeError> { + // input: no arguments + // output: list(Principal) + let mut miners:Vec = Vec::new(); + let function_name = "get-miners-list"; + let miners_data_hex = self.call_read(sender, function_name, &[])?; + let miners_data = ClarityValue::try_deserialize_hex_untyped(&miners_data_hex)?; + if let ClarityValue::Sequence(SequenceData::List(miners_clarity)) = miners_data.clone() { + for miner_clarity in miners_clarity.data { + if let ClarityValue::Principal(miner_address) = miner_clarity { + miners.push(StacksAddress::from(miner_address)); + } else { + return Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + miners_data + )); + } + } + } else { + return Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + miners_data, + )); + } + return Ok(miners); + } + + fn get_waiting_list(&self, sender: &StacksAddress) -> Result, StacksNodeError> { + // input: no arguments + // output: list(Principal) + let mut waiting_list: Vec = Vec::new(); + let function_name = "get-waiting-list"; + let waiting_list_data_hex = self.call_read(sender, function_name, &[])?; + let waiting_list_data = ClarityValue::try_deserialize_hex_untyped(&waiting_list_data_hex)?; + if let ClarityValue::Sequence(SequenceData::List(waiting_list_clarity)) = waiting_list_data.clone() { + for waiting_user in waiting_list_clarity.data { + if let ClarityValue::Principal(waiting_user_address) = waiting_user { + waiting_list.push(StacksAddress::from(waiting_user_address)); + } else { + return Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + waiting_list_data + )); + } + } + } else { + return Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + waiting_list_data, + )); + } + return Ok(waiting_list); + } + + fn get_pool_total_spend_per_block(&self, sender: &StacksAddress) -> Result { + // input: no arguments + // output: uint + let function_name = "get-pool-total-spend-per-block"; + let amount_data_hex = self.call_read(sender, function_name, &[])?; + let amount_data = ClarityValue::try_deserialize_hex_untyped(&amount_data_hex)?; + if let ClarityValue::UInt(amount) = amount_data { + Ok(amount) + } else { + Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + amount_data, + )) + } + } + + fn is_proposed_for_removal( + &self, + sender: &StacksAddress, + address: &StacksAddress + ) -> Result { + let function_name = "get-proposed-removal-list"; + + let mut proposed_for_removal_list:Vec = Vec::new(); + let proposed_removal_list_hex = self.call_read(sender, function_name, &[])?; + let proposed_for_removal_data = ClarityValue::try_deserialize_hex_untyped(&proposed_removal_list_hex)?; + + if let ClarityValue::Sequence(SequenceData::List(proposed_for_removal_clarity)) = proposed_for_removal_data.clone() { + for proposed_miner in proposed_for_removal_clarity.data { + if let ClarityValue::Principal(proposed_address) = proposed_miner { + proposed_for_removal_list.push(StacksAddress::from(proposed_address)); + } else { + return Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + proposed_for_removal_data + )); + } + } + } else { + return Err(StacksNodeError::MalformedClarityValue( + function_name.to_string(), + proposed_for_removal_data, + )); + } + + return Ok(proposed_for_removal_list.contains(address)) + } +} + +#[cfg(test)] +mod tests { + use std::{ + io::{BufWriter, Read, Write}, + net::{SocketAddr, TcpListener}, + thread::spawn, + }; + use bincode::config; + + use stackslib::{ + address::{AddressHashMode, C32_ADDRESS_VERSION_TESTNET_SINGLESIG}, + burnchains::Address, + chainstate::stacks::{ + CoinbasePayload, SinglesigHashMode, SinglesigSpendingCondition, TransactionAnchorMode, + TransactionAuth, TransactionPayload, TransactionPostConditionMode, + TransactionPublicKeyEncoding, TransactionSpendingCondition, TransactionVersion, + }, + types::chainstate::{StacksPrivateKey, StacksPublicKey}, + util::{hash::Hash160, secp256k1::MessageSignature}, + }; + use stackslib::util::secp256k1::Secp256k1PrivateKey; + use crate::peg_wallet::StacksWallet as PegWallet; + use crate::stacks_node::Error; + use crate::stacks_wallet::StacksWallet; + + use crate::util_versioning::test::PRIVATE_KEY_HEX; + + use super::*; + + struct ReadOnlyConfig { + stacks_node: NodeClient, + coordinator_wallet: StacksWallet, + signer_1_wallet: StacksWallet, + signer_2_wallet: StacksWallet, + } + + impl ReadOnlyConfig { + pub fn new() -> Self { + let mut stacks_node = NodeClient::new( + Url::parse("https://stacks-node-api.testnet.stacks.co/").unwrap(), + ContractName::from( "mining-m3-testing-v2"), + StacksAddress::from_string("ST02D2KP0630FS1BCJ7YM4TYMDH6NS9QKR0B57R3").unwrap(), + ); + let coordinator_wallet = StacksWallet::new( + ContractName::from("mining-m3-testing-v2"), + StacksAddress::from_string("ST02D2KP0630FS1BCJ7YM4TYMDH6NS9QKR0B57R3").unwrap(), + StacksPrivateKey::from_hex("c2eae79ad466a0a98d64e24fc27d0a8eaf75891c9029d5f821a67743affa874201").unwrap(), + StacksAddress::from_string("ST02D2KP0630FS1BCJ7YM4TYMDH6NS9QKR0B57R3").unwrap(), + TransactionVersion::Testnet, + 20000); + let signer_1_wallet = StacksWallet::new( + ContractName::from("mining-m3-testing-v2"), + StacksAddress::from_string("ST02D2KP0630FS1BCJ7YM4TYMDH6NS9QKR0B57R3").unwrap(), + StacksPrivateKey::from_hex("811ad2e8f9bafb837c6f7df8521d71a2782b19701715b511018eaa93c3ed84da01").unwrap(), + StacksAddress::from_string("ST109H2F95ZKHDKW4G7DQSCV6ZRXJD9EA126HH4E1").unwrap(), + TransactionVersion::Testnet, + 20000); + let signer_2_wallet = StacksWallet::new( + ContractName::from("mining-m3-testing-v2"), + StacksAddress::from_string("ST02D2KP0630FS1BCJ7YM4TYMDH6NS9QKR0B57R3").unwrap(), + StacksPrivateKey::from_hex("5bfaefc764eb822296d21fc1ff36ff88c59c62419baa5c5cfb90194cd84af8e801").unwrap(), + StacksAddress::from_string("ST1T1XCR5RJ9NBFVMVAKWJDZ14M4RT9WJNNAK1A8Z").unwrap(), + TransactionVersion::Testnet, + 20000); + + Self { + stacks_node, + coordinator_wallet, + signer_1_wallet, + signer_2_wallet, + } + } + } + + struct TestConfig { + sender: StacksAddress, + mock_server: TcpListener, + client: NodeClient, + coordinator_wallet: StacksWallet, + signer_wallet: StacksWallet, + } + + impl TestConfig { + pub fn new() -> Self { + let sender_key = StacksPrivateKey::from_hex("eb92abc7cd7ab8d7590763d6aee37c60fd5bafa6048d4f0760a27dcded7d11c501") + .expect("Unable to generate stacks private key from hex string"); + + let pk = StacksPublicKey::from_private(&sender_key); + + let sender = StacksAddress::from_public_keys( + C32_ADDRESS_VERSION_TESTNET_SINGLESIG, + &AddressHashMode::SerializeP2PKH, + 1, + &vec![pk], + ) + .expect("Failed to generate address from private key"); + + let mut mock_server_addr = SocketAddr::from(([127, 0, 0, 1], 0)); + let mock_server = TcpListener::bind(mock_server_addr).unwrap(); + + mock_server_addr.set_port(mock_server.local_addr().unwrap().port()); + let client = NodeClient::new( + Url::parse(&format!("http://{}", mock_server_addr)) + .expect("Failed to parse mock server address"), + ContractName::from("mining-pool"), + StacksAddress::from_string("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM").unwrap(), + ); + let coordinator_wallet = StacksWallet::new( + ContractName::from("mining-pool"), + StacksAddress::from_string("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM").unwrap(), + StacksPrivateKey::from_hex("753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601").unwrap(), + StacksAddress::from_string("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM").unwrap(), + TransactionVersion::Testnet, + 2000); + let signer_wallet = StacksWallet::new( + ContractName::from("mining-pool"), + StacksAddress::from_string("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM").unwrap(), + StacksPrivateKey::from_hex("eb92abc7cd7ab8d7590763d6aee37c60fd5bafa6048d4f0760a27dcded7d11c501").unwrap(), + StacksAddress::from_string("ST2XK3JZ0RYKPS38N9HHMYVHGTSABPNF98RPCJDQS").unwrap(), + TransactionVersion::Testnet, + 2000); + Self { + sender, + mock_server, + client, + coordinator_wallet, + signer_wallet, + } + } + } + + #[test] + fn smart_contract_flow() { + let mut config = ReadOnlyConfig::new(); + let signer_1_address = config.signer_1_wallet.address().clone(); + let signer_2_address = config.signer_2_wallet.address().clone(); + let coordinator_address = config.coordinator_wallet.address().clone(); + let mut stacks_node_clone = config.stacks_node.clone(); + + // Function: get pool total spend per block -> u3730000000 + // + // let h = spawn(move || config.stacks_node.get_pool_total_spend_per_block(&coordinator_address)); + // let status = h.join().unwrap().unwrap(); + // + // assert_eq!(status, 3730000000); + + // Function: get address status -> Coordinator is a miner + // + // let h = spawn(move || config.stacks_node.get_status(&coordinator_address)); + // let status = h.join().unwrap().unwrap(); + // + // assert_eq!(status, MinerStatus::Miner); + + + // Function: get address status -> Signer 2 is a normal user + // + // let h = spawn(move || config.stacks_node.get_status(&signer_2_address)); + // let status = h.join().unwrap().unwrap(); + // + // assert_eq!(status, MinerStatus::NormalUser); + + + // Function: get miners list -> Coordinator + Signer 1 + // + // let h = spawn(move || config.stacks_node.get_miners_list(&signer_1_address)); + // let status = h.join().unwrap().unwrap(); + // + // assert_eq!(status, vec![coordinator_address, signer_1_address]); + + + // Function: get auto exchange -> Coordinator - false + // + // let h = spawn(move || config.stacks_node.is_auto_exchange(&coordinator_address)); + // let status = h.join().unwrap().unwrap(); + // + // assert_eq!(status, false); + + + // Function: ask to join -> Signer 2 - response (ok true) + // Example: 0x09002f8da81226a3a5cdccd0cc56b68d30070fea83547164f0c37ce1b1851dbd + // + // let h = spawn(move || config.signer_2_wallet.ask_to_join(stacks_node_clone.next_nonce(&signer_2_address).unwrap(), [0u8; 32].serialize_to_vec())); + // let tx = h.join().unwrap().unwrap(); + // + // let h = spawn(move || config.stacks_node.broadcast_transaction(&tx)); + // let broadcasted = h.join().unwrap(); + // + // match broadcasted { + // Ok(()) => {} + // Err(e) => {panic!("{}", e)} + // } + + + // Function: get address status -> Signer 2 is waiting + // + // let h = spawn(move || config.stacks_node.get_status(&signer_2_address)); + // let status = h.join().unwrap().unwrap(); + // + // assert_eq!(status, MinerStatus::Waiting); + + + // Function: is blacklisted -> Signer 2 - false + // + // let h = spawn(move || config.stacks_node.is_blacklisted(&coordinator_address, &signer_2_address)); + // let status = h.join().unwrap().unwrap(); + // + // assert_eq!(status, false); + + + // Function: vote positive join request - Coordinator -> Signer 2 - response (ok true) + // Example: 0x25389e6706b2ebc79f8c2930fc26f89f092b9df838a98d64d821b710e36b454e + // + // let h = spawn(move || config.coordinator_wallet.vote_positive_join_request(stacks_node_clone.next_nonce(&coordinator_address).unwrap(), signer_2_address)); + // let tx = h.join().unwrap().unwrap(); + // + // let h = spawn(move || config.stacks_node.broadcast_transaction(&tx)); + // let broadcasted = h.join().unwrap(); + // + // match broadcasted { + // Ok(()) => {} + // Err(e) => {panic!("{}", e)} + // } + + + // Function: vote negative join request - Coordinator -> Signer 2 - response (err u108) - already voted + // Example: 0xf963742accbb6b218bcdebfac41bd7b32b0ddf7cc041c89b84e7a83dbb8cb94e + // + // let h = spawn(move || config.coordinator_wallet.vote_negative_join_request(stacks_node_clone.next_nonce(&coordinator_address).unwrap(), signer_2_address)); + // let tx = h.join().unwrap().unwrap(); + // + // let h = spawn(move || config.stacks_node.broadcast_transaction(&tx)); + // let broadcasted = h.join().unwrap(); + // + // match broadcasted { + // Ok(()) => {} + // Err(e) => {panic!("{}", e)} + // } + + + // Function: is enough voted to enter -> Signer 2 - true + // + // let h = spawn(move || config.stacks_node.is_enough_voted_to_enter(&signer_2_address)); + // let status = h.join().unwrap().unwrap(); + // + // assert_eq!(status, true); + + + // Function: try enter pool - Signer 2 - response (ok true) + // Example: 0xca92cf119414a2fa484b787f1680ce0df3d403b3eebd6ff0b16af5589a6796bc + // + // let h = spawn(move || config.signer_2_wallet.call_try_enter(stacks_node_clone.next_nonce(&signer_2_address).unwrap())); + // let tx = h.join().unwrap().unwrap(); + // + // let h = spawn(move || config.stacks_node.broadcast_transaction(&tx)); + // let broadcasted = h.join().unwrap(); + // + // match broadcasted { + // Ok(()) => {} + // Err(e) => {panic!("{}", e)} + // } + + + // Function: get address status -> Signer 2 is pending + // + // let h = spawn(move || config.stacks_node.get_status(&signer_2_address)); + // let status = h.join().unwrap().unwrap(); + // + // assert_eq!(status, MinerStatus::Pending); + + + // Function: can enter pending miners -> true + // + // let h = spawn(move || config.stacks_node.is_enough_blocks_passed_for_pending_miners(&signer_2_address)); + // let status = h.join().unwrap().unwrap(); + // + // assert_eq!(status, true); + + + // Function: add pending miners to pool - Signer 2 - response (ok true) + // Example: 0x72d1b3938cbcb868710d509ba1fccbd00113e7db790c6509ab5b7419b5fe2db2 + // + // let h = spawn(move || config.signer_2_wallet.add_pending_miners_to_pool(stacks_node_clone.next_nonce(&signer_2_address).unwrap())); + // let tx = h.join().unwrap().unwrap(); + // + // let h = spawn(move || config.stacks_node.broadcast_transaction(&tx)); + // let broadcasted = h.join().unwrap(); + // + // match broadcasted { + // Ok(()) => {} + // Err(e) => {panic!("{}", e)} + // } + + + // Function: get address status -> Signer 2 is miner + // + // let h = spawn(move || config.stacks_node.get_status(&signer_2_address)); + // let status = h.join().unwrap().unwrap(); + // + // assert_eq!(status, MinerStatus::Miner); + + + // Function: get miners list -> Coordinator + Signer 1 + Signer 2 + // + // let h = spawn(move || config.stacks_node.get_miners_list(&signer_1_address)); + // let status = h.join().unwrap().unwrap(); + // + // assert_eq!(status, vec![coordinator_address, signer_1_address, signer_2_address]); + + + // Function: get notifier -> Coordinator + // + // let h = spawn(move || config.stacks_node.get_notifier(&signer_1_address)); + // let status = h.join().unwrap().unwrap(); + // + // assert_eq!(status, PrincipalData::from(coordinator_address)); + + + // Function: get warnings -> Signer 2 - 0 + // + // let h = spawn(move || config.stacks_node.get_warn_number_user(&coordinator_address, &signer_2_address)); + // let status = h.join().unwrap().unwrap(); + // + // assert_eq!(status, 0); + + + // Function: warn - Signer 2 - response (ok true) + // Example: 0x1c9b21c037a71cca0008d5546cdfece10aaeaca4de6abd56259fb0b043f08e3a + // + // let h = spawn(move || config.coordinator_wallet.warn_miner(stacks_node_clone.next_nonce(&coordinator_address).unwrap(), signer_2_address)); + // let tx = h.join().unwrap().unwrap(); + // + // let h = spawn(move || config.stacks_node.broadcast_transaction(&tx)); + // let broadcasted = h.join().unwrap(); + // + // match broadcasted { + // Ok(()) => {} + // Err(e) => {panic!("{}", e)} + // } + + + // Function: get warnings -> Signer 2 - 1 + // + // let h = spawn(move || config.stacks_node.get_warn_number_user(&coordinator_address, &signer_2_address)); + // let status = h.join().unwrap().unwrap(); + // + // assert_eq!(status, 1); + + + // Function: warn - Signer 2 - response (ok true) + // Example: 0xda326274b176c008c37b87e263134e6f2856f8cd002c9c5f12e408bb827776c3 + // + // let h = spawn(move || config.coordinator_wallet.warn_miner(stacks_node_clone.next_nonce(&coordinator_address).unwrap(), signer_2_address)); + // let tx = h.join().unwrap().unwrap(); + // + // let h = spawn(move || config.stacks_node.broadcast_transaction(&tx)); + // let broadcasted = h.join().unwrap(); + // + // match broadcasted { + // Ok(()) => {} + // Err(e) => {panic!("{}", e)} + // } + + + // Function: get warnings -> Signer 2 - 2 + // + // let h = spawn(move || config.stacks_node.get_warn_number_user(&coordinator_address, &signer_2_address)); + // let status = h.join().unwrap().unwrap(); + // + // assert_eq!(status, 2); + + + // Function: propose removal - Coordinator -> Signer 2 - response (ok true) + // Example: 0x4b92dc1314da17a3156599215686133f6af6e4311dd54c058e35a4d8dc9c915atrue + // + // let h = spawn(move || config.coordinator_wallet.propose_removal(stacks_node_clone.next_nonce(&coordinator_address).unwrap(), signer_2_address)); + // let tx = h.join().unwrap().unwrap(); + // + // let h = spawn(move || config.stacks_node.broadcast_transaction(&tx)); + // let broadcasted = h.join().unwrap(); + // + // match broadcasted { + // Ok(()) => {} + // Err(e) => {panic!("{}", e)} + // } + + + // Function: vote negative remove request - Coordinator -> Signer 2 - response (ok true) + // Example: 0xf0d0e101bb024dd02b04dc8646f99b7a83d064c6ef684189bb5f94e1b313f2a6 + // + // let h = spawn(move || config.coordinator_wallet.vote_negative_remove_request(stacks_node_clone.next_nonce(&coordinator_address).unwrap(), signer_2_address)); + // let tx = h.join().unwrap().unwrap(); + // + // let h = spawn(move || config.stacks_node.broadcast_transaction(&tx)); + // let broadcasted = h.join().unwrap(); + // + // match broadcasted { + // Ok(()) => {} + // Err(e) => {panic!("{}", e)} + // } + + + // Function: propose removal - Coordinator -> Signer 2 - response (err u116) - signer 2 already proposed for removal (also need signer 1 negative vote) + // Example: 0xf68bff6a1be0d0ef59146bc1ff4d0cd08e8acd1228c79a20b7c013be48b9892a + // + // let h = spawn(move || config.coordinator_wallet.propose_removal(stacks_node_clone.next_nonce(&coordinator_address).unwrap(), signer_2_address)); + // let tx = h.join().unwrap().unwrap(); + // + // let h = spawn(move || config.stacks_node.broadcast_transaction(&tx)); + // let broadcasted = h.join().unwrap(); + // + // match broadcasted { + // Ok(()) => {}true + // Err(e) => {panic!("{}", e)} + // } + + + // Function: vote negative remove request - Signer 1 -> Signer 2 - response (ok true) + // Example: 0xa9f8ac532ba37bc4538427f0b22aa6d611ea5bd8a852fd85062ad7d195315688 + // + // let h = spawn(move || config.signer_1_wallet.vote_negative_remove_request(stacks_node_clone.next_nonce(&signer_1_address).unwrap(), signer_2_address)); + // let tx = h.join().unwrap().unwrap(); + // + // let h = spawn(move || config.stacks_node.broadcast_transaction(&tx)); + // let broadcasted = h.join().unwrap(); + // + // match broadcasted { + // Ok(()) => {} + // Err(e) => {panic!("{}", e)} + // } + + + // Function: propose removal - Coordinator -> Signer 2 - response (ok true) + // Example: 0x6060697283c4570e73cd51cac142ced7c3019253b0f5bf8e82d17fb89325e63c + // + // let h = spawn(move || config.coordinator_wallet.propose_removal(stacks_node_clone.next_nonce(&coordinator_address).unwrap(), signer_2_address)); + // let tx = h.join().unwrap().unwrap(); + // + // let h = spawn(move || config.stacks_node.broadcast_transaction(&tx)); + // let broadcasted = h.join().unwrap(); + // + // match broadcasted { + // Ok(()) => {} + // Err(e) => {panic!("{}", e)} + // } + + + // Function: vote positive remove request - Coordinator -> Signer 2 - response (ok true) + // Example: 0xfcb091e97b84d32cfa0056a9adb0db1ff1593f95ceaadb520e679d23671e4f2a + // + // let h = spawn(move || config.coordinator_wallet.vote_positive_remove_request(stacks_node_clone.next_nonce(&coordinator_address).unwrap(), signer_2_address)); + // let tx = h.join().unwrap().unwrap(); + // + // let h = spawn(move || config.stacks_node.broadcast_transaction(&tx)); + // let broadcasted = h.join().unwrap(); + // + // match broadcasted { + // Ok(()) => {} + // Err(e) => {panic!("{}", e)} + // } + + + // Function: is blacklisted -> Signer 2 - true + // + // let h = spawn(move || config.stacks_node.is_blacklisted(&coordinator_address, &signer_2_address)); + // let status = h.join().unwrap().unwrap(); + // + // assert_eq!(status, true); + + + // Function: get miners list -> Coordinator + Signer 1 + // + // let h = spawn(move || config.stacks_node.get_miners_list(&signer_1_address)); + // let status = h.join().unwrap().unwrap(); + // + // assert_eq!(status, vec![coordinator_address, signer_1_address]); + } + + #[test] + fn get_address_status() { + let config = TestConfig::new(); + let address = config.signer_wallet.address().clone(); + + let h = spawn(move || config.client.get_status(&address)); + + write_response( + config.mock_server, + b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x070d0000000769732d6e6f6e65\"}" + ); + let result = h.join().unwrap().unwrap(); + + assert_eq!(result, MinerStatus::NormalUser); + } + + #[test] + fn get_warn_number_user() { + let config = TestConfig::new(); + let address = config.client.contract_address; + + let h = spawn(move || config.client.get_warn_number_user( + &address, + &StacksAddress::from_string("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM").unwrap() + )); + + write_response( + config.mock_server, + b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x0100000000000000000000000000000000\"}" + ); + let result = h.join().unwrap().unwrap(); + + assert_eq!(result, 0); + } + + #[test] + fn get_notifier() { + let config = TestConfig::new(); + let address = config.client.contract_address; + + let h = spawn(move || config.client.get_notifier(&address)); + + write_response( + config.mock_server, + b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x051a6d78de7b0625dfbfc16c3a8a5735f6dc3dc3f2ce\"}" + ); + let result = h.join().unwrap().unwrap(); + + assert_eq!(result, PrincipalData::from(StacksAddress::from_string("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM").unwrap())); + } + + #[test] + fn is_blacklisted() { + let config = TestConfig::new(); + let address = config.client.contract_address; + + let h = spawn(move || config.client.is_blacklisted( + &address, + &StacksAddress::from_string("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM").unwrap() + )); + + write_response( + config.mock_server, + b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x04\"}" + ); + let result = h.join().unwrap().unwrap(); + + assert_eq!(result, false); + } + + #[test] + fn is_block_claimed() { + let config = TestConfig::new(); + let address = config.client.contract_address; + + let h = spawn(move || config.client.is_block_claimed( + &address, + 10, + )); + + write_response( + config.mock_server, + b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x04\"}" + ); + let result = h.join().unwrap().unwrap(); + + assert_eq!(result, false); + } + + #[test] + fn is_enough_voted_to_enter() { + let config = TestConfig::new(); + let address = config.client.contract_address; + + let h = spawn(move || config.client.is_enough_voted_to_enter(&address)); + + write_response( + config.mock_server, + b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x04\"}" + ); + let result = h.join().unwrap().unwrap(); + + assert_eq!(result, false); + } + + #[test] + fn is_enough_blocks_passed_for_pending_miners() { + let config = TestConfig::new(); + let address = config.client.contract_address; + + let h = spawn(move || config.client.is_enough_blocks_passed_for_pending_miners(&address)); + + write_response( + config.mock_server, + b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x04\"}" + ); + let result = h.join().unwrap().unwrap(); + + assert_eq!(result, false); + } + + #[test] + fn is_auto_exchange() { + let config = TestConfig::new(); + let address = config.client.contract_address; + + let h = spawn(move || config.client.is_blacklisted( + &address, + &StacksAddress::from_string("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM").unwrap() + )); + + write_response( + config.mock_server, + b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x04\"}" + ); + let result = h.join().unwrap().unwrap(); + + assert_eq!(result, false); + } + + #[test] + fn get_reward_info_for_block_height() { + let config = TestConfig::new(); + let address = config.client.contract_address; + + let h = spawn(move || config.client.get_reward_info_for_block_height( + &address, + 10, + )); + + write_response( + config.mock_server, + b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x0c0000000207636c61696d65720a051aee9369fb719c0ba43ddf4d94638a970b84775f47067265776172640a010000000000000000000000003b9f5de0\"}" + ); + let result = h.join().unwrap().unwrap(); + + assert_eq!(result, (1000300000, PrincipalData::from(StacksAddress::from_string("ST3Q96TFVE6E0Q91XVX6S8RWAJW5R8XTZ8YEBM8RQ").unwrap()))); + } + + #[test] + fn get_miners_list() { + let config = TestConfig::new(); + let address = config.client.contract_address; + + let h = spawn(move || config.client.get_miners_list( + &address, + )); + + write_response( + config.mock_server, + b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x0b00000001051a6d78de7b0625dfbfc16c3a8a5735f6dc3dc3f2ce\"}" + ); + let result = h.join().unwrap().unwrap(); + + assert_eq!(result, [StacksAddress::from_string("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM").unwrap()]); + } + + #[test] + fn get_waiting_list() { + let config = TestConfig::new(); + let address = config.client.contract_address; + + let h = spawn(move || config.client.get_waiting_list( + &address, + )); + + write_response( + config.mock_server, + b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x0b00000000\"}" + ); + let result = h.join().unwrap().unwrap(); + info!("{result:?}"); + + assert_eq!(result, []); + } + + fn write_response(mock_server: TcpListener, bytes: &[u8]) -> [u8; 1024] { + let mut request_bytes = [0u8; 1024]; + { + let mut stream = mock_server.accept().unwrap().0; + + stream.read(&mut request_bytes).unwrap(); + stream.write(bytes).unwrap(); + } + request_bytes + } + + #[test] + fn call_read_success_test() { + let config = TestConfig::new(); + let h = spawn(move || { + config + .client + .call_read(&config.sender, "function-name", &[]) + }); + write_response( + config.mock_server, + b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x070d0000000473425443\"}", + ); + let result = h.join().unwrap().unwrap(); + assert_eq!(result, "0x070d0000000473425443"); + } + + #[test] + fn call_read_failure_test() { + let config = TestConfig::new(); + let h = spawn(move || { + config + .client + .call_read(&config.sender, "function-name", &[]) + }); + write_response( + config.mock_server, + b"HTTP/1.1 200 OK\n\n{\"okay\":false,\"cause\":\"Some reason\"}", + ); + let result = h.join().unwrap(); + assert!(matches!(result, Err(StacksNodeError::ReadOnlyFailure(_)))); + } + + #[test] + fn signer_data_none_test() { + let config = TestConfig::new(); + + let h = spawn(move || { + let mut public_keys = PublicKeys::default(); + let mut signer_key_ids = SignerKeyIds::default(); + config + .client + .signer_data(&config.sender, 1u128, &mut public_keys, &mut signer_key_ids) + }); + write_response( + config.mock_server, + b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x09\"}", + ); + let result = h.join().unwrap(); + assert!(matches!(result, Err(StacksNodeError::NoSignerData(_)))); + } + + #[test] + fn keys_threshold_test() { + let config = TestConfig::new(); + + let h = spawn(move || config.client.keys_threshold(&config.sender)); + + write_response(config.mock_server, b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x0100000000000000000000000000000af0\"}"); + let result = h.join().unwrap().unwrap(); + assert_eq!(result, 2800); + } + + #[test] + fn keys_threshold_invalid_test() { + let config = TestConfig::new(); + + let h = spawn(move || config.client.keys_threshold(&config.sender)); + write_response( + config.mock_server, + b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x09\"}", + ); + let result = h.join().unwrap(); + assert!(matches!( + result, + Err(StacksNodeError::MalformedClarityValue(..)) + )); + } + + #[test] + fn num_signers_test() { + let config = TestConfig::new(); + + let h = spawn(move || config.client.num_signers(&config.sender)); + write_response(config.mock_server, + b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x0100000000000000000000000000000fa0\"}" + ); + let result = h.join().unwrap().unwrap(); + assert_eq!(result, 4000); + } + + #[test] + fn num_signers_invalid_test() { + let config = TestConfig::new(); + + let h = spawn(move || config.client.num_signers(&config.sender)); + write_response( + config.mock_server, + b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x09\"}", + ); + let result = h.join().unwrap(); + assert!(matches!( + result, + Err(StacksNodeError::MalformedClarityValue(..)) + )); + } + + #[test] + fn next_nonce_success_test() { + let mut config = TestConfig::new(); + + let h = spawn(move || { + let nonce = config.client.next_nonce(&config.sender).unwrap(); + let next_nonce = config.client.next_nonce(&config.sender).unwrap(); + (nonce, next_nonce) + }); + write_response(config.mock_server, + b"HTTP/1.1 200 OK\n\n{\"balance\":\"0x00000000000000000000000000000000\",\"locked\":\"0x00000000000000000000000000000000\",\"unlock_height\":0,\"nonce\":20,\"balance_proof\":\"\",\"nonce_proof\":\"\"}" + ); + let (nonce, next_nonce) = h.join().unwrap(); + assert_eq!(nonce, 20); + assert_eq!(next_nonce, 21); + } + + #[test] + fn next_nonce_failure_test() { + let mut config = TestConfig::new(); + + let h = spawn(move || config.client.next_nonce(&config.sender)); + write_response( + config.mock_server, + b"HTTP/1.1 404 Not Found\n\n/v2/accounts/SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE", + ); + let result = h.join().unwrap(); + assert!(matches!(result, Err(StacksNodeError::UnknownAddress(_)))); + } + + #[test] + fn burn_block_height_success_test() { + let config = TestConfig::new(); + + let h = spawn(move || config.client.burn_block_height()); + write_response( + config.mock_server, + b"HTTP/1.1 200 OK\n\n{\"peer_version\":420759911,\"burn_block_height\":2430220}", + ); + let result = h.join().unwrap().unwrap(); + assert_eq!(result, 2430220); + } + + #[test] + fn burn_block_height_failure_test() { + let config = TestConfig::new(); + + let h = spawn(move || config.client.burn_block_height()); + write_response( + config.mock_server, + b"HTTP/1.1 200 OK\n\n{\"peer_version\":420759911,\"burn_block_height2\":2430220}", + ); + let result = h.join().unwrap(); + assert!(matches!(result, Err(StacksNodeError::InvalidJsonEntry(_)))); + } + + #[test] + fn should_send_tx_bytes_to_node() { + let config = TestConfig::new(); + let tx = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: 0, + auth: TransactionAuth::Standard(TransactionSpendingCondition::Singlesig( + SinglesigSpendingCondition { + hash_mode: SinglesigHashMode::P2PKH, + signer: Hash160([0; 20]), + nonce: 0, + tx_fee: 0, + key_encoding: TransactionPublicKeyEncoding::Uncompressed, + signature: MessageSignature([0; 65]), + }, + )), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::Coinbase(CoinbasePayload([0; 32]), None), + }; + + let mut tx_bytes = [0u8; 1024]; + { + let mut tx_bytes_writer = BufWriter::new(&mut tx_bytes[..]); + tx.consensus_serialize(&mut tx_bytes_writer).unwrap(); + tx_bytes_writer.flush().unwrap(); + } + + let bytes_len = tx_bytes + .iter() + .enumerate() + .rev() + .find(|(_, &x)| x != 0) + .unwrap() + .0 + + 1; + + let h = spawn(move || config.client.broadcast_transaction(&tx)); + + let request_bytes = write_response(config.mock_server, b"HTTP/1.1 200 OK\n\n"); + h.join().unwrap().unwrap(); + + assert!( + request_bytes + .windows(bytes_len) + .any(|window| window == &tx_bytes[..bytes_len]), + "Request bytes did not contain the transaction bytes" + ); + } +} diff --git a/stacks-coordinator/src/stacks_node/mod.rs b/degen-base-signer/src/stacks_node/mod.rs similarity index 60% rename from stacks-coordinator/src/stacks_node/mod.rs rename to degen-base-signer/src/stacks_node/mod.rs index 70909b63..690ccd33 100644 --- a/stacks-coordinator/src/stacks_node/mod.rs +++ b/degen-base-signer/src/stacks_node/mod.rs @@ -1,13 +1,16 @@ pub mod client; use bitcoin::XOnlyPublicKey; -use blockstack_lib::{ +use stackslib::{ chainstate::{burn::operations as burn_ops, stacks::StacksTransaction}, codec::Error as CodecError, types::chainstate::StacksAddress, vm::{types::serialization::SerializationError, Value as ClarityValue}, }; -use frost_signer::config::{PublicKeys, SignerKeyIds}; +use stackslib::vm::types::{PrincipalData, SequenceData}; +use stackslib::vm::{ClarityName, Value}; +use tracing::info; +use crate::config::{MinerStatus, PublicKeys, SignerKeyIds}; use wsts::ecdsa::PublicKey; use self::client::BroadcastError; @@ -55,15 +58,27 @@ pub trait StacksNode { fn get_peg_out_request_ops(&self, block_height: u64) -> Result, Error>; fn burn_block_height(&self) -> Result; fn next_nonce(&mut self, addr: &StacksAddress) -> Result; + fn get_user_balance(&mut self, address: &StacksAddress) -> Result; + fn get_mempool_transactions(&mut self) -> Result; fn broadcast_transaction(&self, tx: &StacksTransaction) -> Result<(), Error>; fn keys_threshold(&self, sender: &StacksAddress) -> Result; fn public_keys(&self, sender: &StacksAddress) -> Result; fn signer_key_ids(&self, sender: &StacksAddress) -> Result; fn coordinator_public_key(&self, sender: &StacksAddress) -> Result, Error>; - fn bitcoin_wallet_public_key( - &self, - sender: &StacksAddress, - ) -> Result, Error>; + fn bitcoin_wallet_public_key(&self, sender: &StacksAddress) -> Result, Error>; + fn get_status(&self, sender: &StacksAddress) -> Result; + fn get_warn_number_user(&self, sender: &StacksAddress, warned_address: &StacksAddress) -> Result; + fn get_notifier(&self, sender: &StacksAddress) -> Result; + fn is_blacklisted(&self, sender: &StacksAddress, address: &StacksAddress) -> Result; + fn is_block_claimed(&self, sender: &StacksAddress, block_height: u128) -> Result; + fn is_enough_voted_to_enter(&self, sender: &StacksAddress) -> Result; + fn is_enough_blocks_passed_for_pending_miners(&self, sender: &StacksAddress) -> Result; + fn is_auto_exchange(&self, sender: &StacksAddress) -> Result; + fn get_reward_info_for_block_height(&self, sender: &StacksAddress, block_height: u128) -> Result<(u128, PrincipalData), Error>; + fn get_miners_list(&self, sender: &StacksAddress) -> Result, Error>; + fn get_waiting_list(&self, sender: &StacksAddress) -> Result, Error>; + fn get_pool_total_spend_per_block(&self, sender: &StacksAddress) -> Result; + fn is_proposed_for_removal(&self, sender: &StacksAddress, address: &StacksAddress) -> Result; } pub type PegInOp = burn_ops::PegInOp; diff --git a/stacks-coordinator/src/stacks_wallet.rs b/degen-base-signer/src/stacks_wallet.rs similarity index 72% rename from stacks-coordinator/src/stacks_wallet.rs rename to degen-base-signer/src/stacks_wallet.rs index 7897b410..935b97d0 100644 --- a/stacks-coordinator/src/stacks_wallet.rs +++ b/degen-base-signer/src/stacks_wallet.rs @@ -1,10 +1,10 @@ use crate::{ peg_wallet::{Error as PegWalletError, StacksWallet as StacksWalletTrait}, stacks_node::{PegInOp, PegOutRequestOp}, - util::address_version, + util_versioning::address_version, }; use bitcoin::XOnlyPublicKey; -use blockstack_lib::{ +use stackslib::{ chainstate::stacks::{ StacksTransaction, StacksTransactionSigner, TransactionAnchorMode, TransactionAuth, TransactionContractCall, TransactionPayload, TransactionPostConditionMode, @@ -18,6 +18,8 @@ use blockstack_lib::{ ClarityName, ContractName, Value, }, }; +use stackslib::vm::types::PrincipalData; +use tracing::info; #[derive(thiserror::Error, Debug, PartialEq)] pub enum Error { @@ -35,9 +37,10 @@ pub enum Error { ClarityRuntimeError(#[from] RuntimeErrorType), ///Blockstack error #[error("Clarity runtime error ocurred: {0}")] - BlockstackError(#[from] blockstack_lib::vm::errors::Error), + BlockstackError(#[from] stackslib::vm::errors::Error), } +#[derive(Debug, Clone)] pub struct StacksWallet { contract_address: StacksAddress, contract_name: ContractName, @@ -140,6 +143,102 @@ impl StacksWallet { }; Ok(payload.into()) } + + /// COORDINATOR' FUNCTIONS + // TODO: degens - add the functions directly here or add them same way as peg-out/in for operation operability + pub fn propose_removal(&self, nonce: u64, miner_address: StacksAddress) -> Result { + // only coordinator can call this + let function_name = "propose-removal"; + let principal = Value::Principal(PrincipalData::from(miner_address)); + // stacks wallet is self + let function_args: Vec = vec![principal]; + let tx = self.build_transaction_signed(function_name, function_args, nonce)?; + Ok(tx) + } + + pub fn warn_miner(&self, nonce: u64, miner_address: StacksAddress) -> Result { + // only coordinator can call this + let function_name = "warn-miner"; + let principal = Value::Principal(PrincipalData::from(miner_address)); + let function_args: Vec = vec![principal]; + let tx = self.build_transaction_signed(function_name, function_args, nonce)?; + Ok(tx) + } + + /// MINERS' FUNCTIONS + pub fn vote_positive_join_request(&self, nonce: u64, miner_address: StacksAddress) -> Result { + let function_name = "vote-positive-join-request"; + let principal = Value::Principal(PrincipalData::from(miner_address)); + let function_args: Vec = vec![principal]; + let tx = self.build_transaction_signed(function_name, function_args, nonce)?; + Ok(tx) + } + + // TODO: degens - find if needed or delete + pub fn vote_negative_join_request(&self, nonce: u64, miner_address: StacksAddress) -> Result { + let function_name = "vote-negative-join-request"; + let principal = Value::Principal(PrincipalData::from(miner_address)); + let function_args: Vec = vec![principal]; + let tx = self.build_transaction_signed(function_name, function_args, nonce)?; + Ok(tx) + } + + pub fn vote_positive_remove_request(&self, nonce: u64, miner_address: StacksAddress) -> Result { + let function_name = "vote-positive-remove-request"; + let principal = Value::Principal(PrincipalData::from(miner_address)); + let function_args: Vec = vec![principal]; + let tx = self.build_transaction_signed(function_name, function_args, nonce)?; + Ok(tx) + } + + // TODO: degens - find if needed or delete + pub fn vote_negative_remove_request(&self, nonce: u64, miner_address: StacksAddress) -> Result { + let function_name = "vote-negative-remove-request"; + let principal = Value::Principal(PrincipalData::from(miner_address)); + let function_args: Vec = vec![principal]; + let tx = self.build_transaction_signed(function_name, function_args, nonce)?; + Ok(tx) + } + + /// PENDING MINERS' FUNCTIONS + pub fn add_pending_miners_to_pool(&self, nonce: u64) -> Result { + let function_name = "add-pending-miners-to-pool"; + let function_args: Vec = vec![]; + let tx = self.build_transaction_signed(function_name, function_args, nonce)?; + Ok(tx) + } + + /// WAITING MINERS' FUNCTIONS + pub fn call_try_enter(&self, nonce: u64) -> Result { + let function_name = "try-enter-pool"; + let function_args: Vec = vec![]; + let tx = self.build_transaction_signed(function_name, function_args, nonce)?; + Ok(tx) + } + + /// NORMAL USERS' FUNCTIONS + /// TODO: degens - check if works: hashbytes parsed and version hardcoded + /// (ask-to-join (my-btc-address {hashbytes: (buff 20), version: (buff 1)})) + pub fn ask_to_join(&self, nonce: u64, hashbytes_btc: Vec) -> Result { + let function_name = "ask-to-join"; + + // directly legacy address for the bitcoin private key provided + let hashbytes = Value::Sequence(SequenceData::Buffer(BuffData { + data: hashbytes_btc + })); + let version = Value::Sequence(SequenceData::Buffer(BuffData { + data: vec![06] + })); + + let btc_address = Value::Tuple(TupleData::from_data(vec![ + (ClarityName::from("hashbytes"), hashbytes), + (ClarityName::from("version"), version) + ]).map_err(Error::from)?); + ; + let function_args = vec![btc_address]; + let tx = self.build_transaction_signed(function_name, function_args, nonce)?; + Ok(tx) + } } /// Build a StacksTransaction using the provided wallet and nonce @@ -241,7 +340,7 @@ impl StacksWalletTrait for StacksWallet { (ClarityName::from("addr"), principal), (ClarityName::from("key"), key), ]) - .map_err(Error::from)?; + .map_err(Error::from)?; let function_args = vec![data.into()]; let tx = self.build_transaction_signed(function_name, function_args, nonce)?; Ok(tx) @@ -265,13 +364,13 @@ mod tests { use crate::{ peg_wallet::StacksWallet as StacksWalletTrait, stacks_wallet::StacksWallet, - util::{ + util_versioning::{ address_version, test::{build_peg_out_request_op, PRIVATE_KEY_HEX, PUBLIC_KEY_HEX}, }, }; use bitcoin::XOnlyPublicKey; - use blockstack_lib::{ + use stackslib::{ address::{AddressHashMode, C32_ADDRESS_VERSION_TESTNET_SINGLESIG}, burnchains::{Address, Txid}, chainstate::{ @@ -312,11 +411,11 @@ mod tests { 1, &vec![pk], ) - .expect("Failed to create stacks address"); - let contract_name = ContractName::from("sbtc-alpha"); + .expect("Failed to create stacks address"); + let contract_name = ContractName::from("mining-contract"); let contract_address = - StacksAddress::from_string("SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE") + StacksAddress::from_string("ST1SCEXE6PMGPAC6B4N5P2MDKX8V4GF9QDEBN8YF5") .expect("Failed to parse contract address"); StacksWallet::new( contract_name, @@ -327,6 +426,7 @@ mod tests { 10, ) } + // TODO: degens - test accordingly #[test] fn build_mint_transaction_test() { diff --git a/frost-signer/src/state_machine.rs b/degen-base-signer/src/state_machine.rs similarity index 89% rename from frost-signer/src/state_machine.rs rename to degen-base-signer/src/state_machine.rs index 46b35e68..ca9543b9 100644 --- a/frost-signer/src/state_machine.rs +++ b/degen-base-signer/src/state_machine.rs @@ -7,6 +7,8 @@ pub enum States { DkgPrivateGather, SignGather, Signed, + DegensScriptGather, + DegensScriptDistribute, } pub trait StateMachine { diff --git a/frost-signer/src/util.rs b/degen-base-signer/src/util.rs similarity index 100% rename from frost-signer/src/util.rs rename to degen-base-signer/src/util.rs diff --git a/degen-base-signer/src/util_versioning.rs b/degen-base-signer/src/util_versioning.rs new file mode 100644 index 00000000..cebfa5dc --- /dev/null +++ b/degen-base-signer/src/util_versioning.rs @@ -0,0 +1,115 @@ +use stackslib::{ + address::{C32_ADDRESS_VERSION_MAINNET_SINGLESIG, C32_ADDRESS_VERSION_TESTNET_SINGLESIG}, + chainstate::stacks::TransactionVersion, +}; + +pub fn address_version(version: &TransactionVersion) -> u8 { + match version { + TransactionVersion::Mainnet => C32_ADDRESS_VERSION_MAINNET_SINGLESIG, + TransactionVersion::Testnet => C32_ADDRESS_VERSION_TESTNET_SINGLESIG, + } +} + +#[cfg(test)] +pub mod test { + use stackslib::{ + burnchains::{ + bitcoin::{ + address::{BitcoinAddress, SegwitBitcoinAddress}, + BitcoinTransaction, BitcoinTxInput, BitcoinTxOutput, + }, + BurnchainBlockHeader, BurnchainTransaction, PrivateKey, Txid, + }, + chainstate::burn::{operations::PegOutRequestOp, Opcodes}, + util::{hash::Sha256Sum, secp256k1::Secp256k1PrivateKey}, + }; + use rand::Rng; + + pub const PRIVATE_KEY_HEX: &str = + "b244296d5907de9864c0b0d51f98a13c52890be0404e83f273144cd5b9960eed01"; + pub const PUBLIC_KEY_HEX: &str = + "cc8a4bc64d897bddc5fbc2f670f7a8ba0b386779106cf1223c6fc5d7cd6fc115"; + + /// Helper function to construct a valid signed peg out request op + pub fn build_peg_out_request_op( + key_hex: &str, + amount: u64, + dust_amount: u64, + fulfillment_fee: u64, + ) -> PegOutRequestOp { + let mut rng = rand::thread_rng(); + let private_key = Secp256k1PrivateKey::from_hex(key_hex) + .expect("Failed to construct a valid private key."); + + // Build a dust txo + let recipient_address_bytes = rng.gen::<[u8; 32]>(); + let output2 = BitcoinTxOutput { + units: dust_amount, + address: BitcoinAddress::Segwit(SegwitBitcoinAddress::P2TR( + true, + recipient_address_bytes, + )), + }; + + // Build a fulfillment fee txo + let peg_wallet_address = rng.gen::<[u8; 32]>(); + let output3 = BitcoinTxOutput { + units: fulfillment_fee, + address: BitcoinAddress::Segwit(SegwitBitcoinAddress::P2TR(true, peg_wallet_address)), + }; + + // Generate the message signature by signing the amount and recipient fields + let mut script_pubkey = vec![81, 32]; // OP_1 OP_PUSHBYTES_32 + script_pubkey.extend_from_slice(&recipient_address_bytes); + + let mut msg = amount.to_be_bytes().to_vec(); + msg.extend_from_slice(&script_pubkey); + + let signature = private_key + .sign(Sha256Sum::from_data(&msg).as_bytes()) + .expect("Failed to sign amount and recipient fields."); + + let mut data = vec![]; + data.extend_from_slice(&amount.to_be_bytes()); + data.extend_from_slice(signature.as_bytes()); + + let outputs = vec![output2, output3]; + let inputs = vec![]; + + // Build the burnchain tx using the above generated data + let tx = build_burnchain_transaction(Opcodes::PegOutRequest as u8, data, inputs, outputs); + + // Build an empty block header + let header = build_empty_block_header(); + + // use the header and tx to generate a peg out request + PegOutRequestOp::from_tx(&header, &tx).expect("Failed to construct peg-out request op") + } + + fn build_empty_block_header() -> BurnchainBlockHeader { + BurnchainBlockHeader { + block_height: 0, + block_hash: [0; 32].into(), + parent_block_hash: [0; 32].into(), + num_txs: 0, + timestamp: 0, + } + } + + fn build_burnchain_transaction( + opcode: u8, + data: Vec, + inputs: Vec, + outputs: Vec, + ) -> BurnchainTransaction { + BurnchainTransaction::Bitcoin(BitcoinTransaction { + txid: Txid([0; 32]), + vtxindex: 0, + opcode, + data, + data_amt: 0, + inputs, + outputs, + }) + } +} diff --git a/frost-signer/tests/messages.rs b/degen-base-signer/tests/messages.rs similarity index 97% rename from frost-signer/tests/messages.rs rename to degen-base-signer/tests/messages.rs index e9f37078..151e4402 100644 --- a/frost-signer/tests/messages.rs +++ b/degen-base-signer/tests/messages.rs @@ -1,4 +1,4 @@ -use frost_signer::signing_round::{ +use degen_base_signer::signing_round::{ DkgBegin, MessageTypes, NonceResponse, SignatureShareRequest, SigningRound, }; use wsts::common::PublicNonce; diff --git a/frost-signer/tests/net.rs b/degen-base-signer/tests/net.rs similarity index 77% rename from frost-signer/tests/net.rs rename to degen-base-signer/tests/net.rs index 2dd43653..4d2dd5bd 100644 --- a/frost-signer/tests/net.rs +++ b/degen-base-signer/tests/net.rs @@ -1,5 +1,5 @@ -use frost_signer::net::{HttpNet, HttpNetListen, Message, NetListen}; -use frost_signer::signing_round::{DkgBegin, MessageTypes}; +use degen_base_signer::net::{HttpNet, HttpNetListen, Message, NetListen}; +use degen_base_signer::signing_round::{DkgBegin, MessageTypes}; #[test] fn receive_msg() { diff --git a/stacks-coordinator/Cargo.toml b/degen-superior-coordinator/Cargo.toml similarity index 79% rename from stacks-coordinator/Cargo.toml rename to degen-superior-coordinator/Cargo.toml index fad93661..75b57665 100644 --- a/stacks-coordinator/Cargo.toml +++ b/degen-superior-coordinator/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "stacks-coordinator" +name = "degen-superior-coordinator" version = "0.0.1" license = "GPLv3" homepage = "https://github.com/Trust-Machines/core-eng" @@ -10,14 +10,14 @@ edition = "2021" [dependencies] bs58 = { workspace = true } -blockstack-core = { workspace = true } +stackslib = { workspace = true } clap = { workspace = true } -frost-coordinator = { path = "../frost-coordinator" } -frost-signer = { path = "../frost-signer" } +degen-base-coordinator = { path = "../degen-base-coordinator" } +degen-base-signer = { path = "../degen-base-signer" } rusqlite = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } -stacks-signer = { path = "../stacks-signer" } +degen-superior-signer = { path = "../degen-superior-signer" } tracing = { workspace = true } tracing-subscriber = { workspace = true } thiserror = { workspace = true } diff --git a/stacks-coordinator/README.md b/degen-superior-coordinator/README.md similarity index 100% rename from stacks-coordinator/README.md rename to degen-superior-coordinator/README.md diff --git a/degen-superior-coordinator/conf/coordinator.toml b/degen-superior-coordinator/conf/coordinator.toml new file mode 100644 index 00000000..73d99b86 --- /dev/null +++ b/degen-superior-coordinator/conf/coordinator.toml @@ -0,0 +1,16 @@ +mining_contract = "ST02D2KP0630FS1BCJ7YM4TYMDH6NS9QKR0B57R3.mining-m3-testing-v3" +exchange_contract = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.bridge-contract" +stacks_private_key = "c2eae79ad466a0a98d64e24fc27d0a8eaf75891c9029d5f821a67743affa874201" +stacks_node_rpc_url = "https://stacks-node-api.testnet.stacks.co/" +bitcoin_node_rpc_url = "http://devnet:devnet@localhost:18443" +bitcoin_private_key = "2bd806c97f0e00af1a1fc3328fa763a9269723c8db8fac4f93af71db186d6e90" +frost_dkg_round_id = 0 +transaction_fee = 2000 +# Insert the amount you want to send to script, in satoshi - 1 BTC = 100,000,000 SATS (if you want to fund the script each block, insert 0) +amount_to_script = 4_000_000_000 +fee_to_script = 300 +fee_to_pox = 1000 +network = "testnet" +http_relay_url = "http://localhost:9776" +frost_state_file = "frost.state.bin" +network_private_key = "9aSCCR6eirt1NAHwJtSz4HMwBHTyMo62SyPMvVDt5DQn" \ No newline at end of file diff --git a/degen-superior-coordinator/conf/signer.toml b/degen-superior-coordinator/conf/signer.toml new file mode 100644 index 00000000..773a8ec6 --- /dev/null +++ b/degen-superior-coordinator/conf/signer.toml @@ -0,0 +1,34 @@ +mining_contract = "ST02D2KP0630FS1BCJ7YM4TYMDH6NS9QKR0B57R3.mining-m3-testing-v3" +exchange_contract = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.bridge-contract" +stacks_private_key = "c2eae79ad466a0a98d64e24fc27d0a8eaf75891c9029d5f821a67743affa874201" +bitcoin_private_key = "2bd806c97f0e00af1a1fc3328fa763a9269723c8db8fac4f93af71db186d6e90" +stacks_node_rpc_url = "https://stacks-node-api.testnet.stacks.co/" +bitcoin_node_rpc_url = "http://devnet:devnet@localhost:18443" +transaction_fee = 2000 +network = "testnet" +http_relay_url = "http://localhost:9776" +keys_threshold = 4 +# Insert the amount you want to send to script, in satoshi - 1 BTC = 100,000,000 SATS (if you want to fund the script each block, insert 0) +amount_to_script = 4_000_000_000 +fee_to_script = 300 +fee_to_pox = 1000 +frost_state_file = "frost.state.bin" +network_private_key = "9aSCCR6eirt1NAHwJtSz4HMwBHTyMo62SyPMvVDt5DQn" +signers = [ + {public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj", key_ids = [1, 2]}, + {public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj", key_ids = [3, 4]}, + {public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj", key_ids = [5, 6]} +] +coordinator_public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj" + + +#http_relay_url = "http://localhost:9776" +#keys_threshold = 4 +#frost_state_file = "frost.state.bin" +#network_private_key = "9aSCCR6eirt1NAHwJtSz4HMwBHTyMo62SyPMvVDt5DQn" +#signers = [ +# {public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj", key_ids = [1, 2]}, +# {public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj", key_ids = [3, 4]}, +# {public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj", key_ids = [5, 6]} +#] +#coordinator_public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj" \ No newline at end of file diff --git a/stacks-coordinator/db.sqlite b/degen-superior-coordinator/db.sqlite similarity index 100% rename from stacks-coordinator/db.sqlite rename to degen-superior-coordinator/db.sqlite diff --git a/stacks-coordinator/src/cli.rs b/degen-superior-coordinator/src/cli.rs similarity index 100% rename from stacks-coordinator/src/cli.rs rename to degen-superior-coordinator/src/cli.rs diff --git a/stacks-coordinator/src/config.rs b/degen-superior-coordinator/src/config.rs similarity index 73% rename from stacks-coordinator/src/config.rs rename to degen-superior-coordinator/src/config.rs index fc597b8a..c059e914 100644 --- a/stacks-coordinator/src/config.rs +++ b/degen-superior-coordinator/src/config.rs @@ -1,4 +1,8 @@ -use blockstack_lib::{ +use std::str::FromStr; +use bitcoin::secp256k1::{Secp256k1, SecretKey}; +use bitcoin::{KeyPair, XOnlyPublicKey}; +use tracing::info; +use stackslib::{ address::AddressHashMode, burnchains::Address, chainstate::stacks::TransactionVersion, @@ -6,6 +10,11 @@ use blockstack_lib::{ vm::ContractName, }; use url::Url; +use degen_base_signer::bitcoin_node::{BitcoinNode, LocalhostBitcoinNode}; +use degen_base_signer::bitcoin_wallet::BitcoinWallet; +use degen_base_signer::peg_wallet::BitcoinWallet as BitcoinWalletTrait; +use degen_base_signer::stacks_node::client::NodeClient; +use degen_base_signer::stacks_wallet::StacksWallet; use crate::util::address_version; @@ -21,10 +30,12 @@ pub enum Error { TomlError(#[from] toml::de::Error), #[error("Invalid config file. {0}")] InvalidConfig(String), - #[error("Invalid sbtc_contract. {0}")] + #[error("Invalid mining_contract. {0}")] InvalidContract(String), #[error("Failed to parse stacks_private_key: {0}")] InvalidPrivateKey(String), + #[error("Failed to parse bitcoin_private_key: {0}")] + InvalidBitcoinPrivateKey(String), } #[derive(serde::Deserialize)] @@ -32,14 +43,16 @@ pub enum Error { pub enum Network { Mainnet, Testnet, + Regtest, } #[derive(serde::Deserialize, Default)] pub struct RawConfig { - pub sbtc_contract: String, + pub mining_contract: String, pub stacks_private_key: String, pub stacks_node_rpc_url: String, pub bitcoin_node_rpc_url: String, + pub bitcoin_private_key: String, pub frost_dkg_round_id: u64, pub signer_config_path: Option, pub start_block_height: Option, @@ -48,6 +61,9 @@ pub struct RawConfig { pub network: Option, /// The transaction fee in Satoshis used to broadcast transactions to the stacks node pub transaction_fee: u64, + pub amount_to_script: u64, + pub fee_to_script: u64, + pub fee_to_pox: u64, /// Frost specific config options. Must be specified if signer_config_path is not used pub http_relay_url: Option, pub frost_state_file: Option, @@ -62,8 +78,9 @@ impl RawConfig { Ok(config) } + // TODO will use: implement this when necessary pub fn parse_contract(&self) -> Result<(ContractName, StacksAddress), Error> { - let mut split = self.sbtc_contract.split('.'); + let mut split = self.mining_contract.split('.'); let contract_address = split .next() .ok_or(Error::InvalidContract("Missing address".to_string()))?; @@ -91,18 +108,30 @@ impl RawConfig { 1, &vec![pk], ) - .ok_or(Error::InvalidPrivateKey( - "Failed to generate stacks address from private key.".to_string(), - ))?; + .ok_or(Error::InvalidPrivateKey( + "Failed to generate stacks address from private key.".to_string(), + ))?; Ok((sender_key, address)) } + pub fn parse_bitcoin_private_key(&self) -> Result<(SecretKey, XOnlyPublicKey), Error> { + let secp = Secp256k1::new(); + let sender_key = SecretKey::from_str(&self.bitcoin_private_key) + .map_err(|e| Error::InvalidBitcoinPrivateKey(e.to_string()))?; + let key_pair_source = KeyPair::from_secret_key(&secp, &sender_key); + let (xonly_public_key, _) = key_pair_source.x_only_public_key(); + + Ok((sender_key, xonly_public_key)) + } + pub fn parse_version(&self) -> (TransactionVersion, bitcoin::Network) { // Determine what network we are running on match self.network.as_ref().unwrap_or(&Network::Mainnet) { Network::Mainnet => (TransactionVersion::Mainnet, bitcoin::Network::Bitcoin), - Network::Testnet => (TransactionVersion::Testnet, bitcoin::Network::Testnet), + // TODO: change the network back to Testnet + Network::Testnet => (TransactionVersion::Testnet, bitcoin::Network::Regtest), + Network::Regtest => (TransactionVersion::Testnet, bitcoin::Network::Regtest), } } } @@ -114,6 +143,12 @@ pub struct Config { pub stacks_address: StacksAddress, pub stacks_node_rpc_url: Url, pub bitcoin_node_rpc_url: Url, + pub local_stacks_node: NodeClient, + pub stacks_wallet: StacksWallet, + pub local_bitcoin_node: LocalhostBitcoinNode, + pub bitcoin_wallet: BitcoinWallet, + pub bitcoin_private_key: SecretKey, + pub bitcoin_xpub: XOnlyPublicKey, pub frost_dkg_round_id: u64, pub signer_config_path: Option, pub start_block_height: Option, @@ -122,6 +157,9 @@ pub struct Config { pub stacks_version: TransactionVersion, /// The transaction fee in Satoshis used to broadcast transactions to the stacks node pub transaction_fee: u64, + pub amount_to_script: u64, + pub fee_to_script: u64, + pub fee_to_pox: u64, /// Frost specific config options. Must be specified if signer_config_path is not used pub http_relay_url: Option, pub network_private_key: Option, @@ -154,17 +192,47 @@ impl TryFrom for Config { let (contract_name, contract_address) = config.parse_contract()?; let (stacks_version, bitcoin_network) = config.parse_version(); let (stacks_private_key, stacks_address) = config.parse_stacks_private_key()?; + let (bitcoin_private_key, bitcoin_address) = config.parse_bitcoin_private_key()?; + + let stacks_node_rpc_url = Url::parse(&config.stacks_node_rpc_url) + .map_err(|e| Error::InvalidConfig(format!("Invalid stacks_node_rpc_url: {}", e)))?; + + let bitcoin_node_rpc_url = Url::parse(&config.bitcoin_node_rpc_url) + .map_err(|e| Error::InvalidConfig(format!("Invalid bitcoin_node_rpc_url: {}", e)))?; + + let local_stacks_node = NodeClient::new( + stacks_node_rpc_url.clone(), + contract_name.clone(), + contract_address, + ); + + let stacks_wallet = StacksWallet::new( + contract_name.clone(), + contract_address, + stacks_private_key, + stacks_address, + stacks_version, + config.transaction_fee.clone(), + ); + + let bitcoin_wallet = BitcoinWallet::new(bitcoin_address, bitcoin_network); + + let local_bitcoin_node = LocalhostBitcoinNode::new(bitcoin_node_rpc_url.clone()); + local_bitcoin_node.load_wallet(bitcoin_wallet.address()).unwrap(); Ok(Self { contract_name, contract_address, stacks_private_key, stacks_address, - stacks_node_rpc_url: Url::parse(&config.stacks_node_rpc_url) - .map_err(|e| Error::InvalidConfig(format!("Invalid stacks_node_rpc_url: {}", e)))?, - bitcoin_node_rpc_url: Url::parse(&config.bitcoin_node_rpc_url).map_err(|e| { - Error::InvalidConfig(format!("Invalid bitcoin_node_rpc_url: {}", e)) - })?, + stacks_node_rpc_url, + bitcoin_node_rpc_url, + local_stacks_node, + stacks_wallet, + local_bitcoin_node, + bitcoin_wallet, + bitcoin_private_key, + bitcoin_xpub: bitcoin_address, frost_dkg_round_id: config.frost_dkg_round_id, signer_config_path: config.signer_config_path, start_block_height: config.start_block_height, @@ -172,6 +240,9 @@ impl TryFrom for Config { bitcoin_network, stacks_version, transaction_fee: config.transaction_fee, + amount_to_script: config.amount_to_script, + fee_to_script: config.fee_to_script, + fee_to_pox: config.fee_to_pox, http_relay_url: config.http_relay_url, network_private_key: config.network_private_key, polling_interval: config.polling_interval.unwrap_or(DEFAULT_POLLING_INTERVAL), @@ -193,12 +264,13 @@ mod tests { use super::*; use bitcoin::Network as BitcoinNetwork; - use blockstack_lib::chainstate::stacks::TransactionVersion; + use stackslib::chainstate::stacks::TransactionVersion; use std::io::Write; use tempdir::TempDir; + // TODO: degens - update accordingly with mining fn write_new_config( - sbtc_contract: String, + mining_contract: String, stacks_private_key: String, signer_config_file: Option, http_relay_url: Option, @@ -210,7 +282,7 @@ mod tests { let mut coord_file = std::fs::File::create(&file_path).unwrap(); let mut coord_contents = format!( r#" -sbtc_contract = "{sbtc_contract}" +mining_contract = "{mining_contract}" stacks_private_key = "{stacks_private_key}" stacks_node_rpc_url = "http://localhost:20443" bitcoin_node_rpc_url = "http://localhost:9776" @@ -239,45 +311,45 @@ transaction_fee = 2000 #[test] fn config_from_raw() { - let sbtc_contract_address = "ST3N4AJFZZYC4BK99H53XP8KDGXFGQ2PRSPNET8TN"; - let sbtc_contract_name = "sbtc-alpha"; + let mining_contract_address = "ST3N4AJFZZYC4BK99H53XP8KDGXFGQ2PRSPNET8TN"; + let mining_contract_name = "sbtc-alpha"; let stacks_private_key = PRIVATE_KEY_HEX; // Test config with signer_config_file let config = write_new_config( - format!("{sbtc_contract_address}.{sbtc_contract_name}"), + format!("{mining_contract_address}.{mining_contract_name}"), stacks_private_key.to_string(), Some(String::new()), None, None, None, ) - .unwrap(); + .unwrap(); assert_eq!(config.bitcoin_network, BitcoinNetwork::Bitcoin); assert_eq!(config.stacks_version, TransactionVersion::Mainnet); - assert_eq!(config.contract_name.to_string(), sbtc_contract_name); - assert_eq!(config.contract_address.to_string(), sbtc_contract_address); + assert_eq!(config.contract_name.to_string(), mining_contract_name); + assert_eq!(config.contract_address.to_string(), mining_contract_address); assert_eq!(config.stacks_private_key.to_hex(), stacks_private_key); // Test config with no signer_config_file let config = write_new_config( - format!("{sbtc_contract_address}.{sbtc_contract_name}"), + format!("{mining_contract_address}.{mining_contract_name}"), stacks_private_key.to_string(), None, Some(String::new()), Some(String::new()), Some(String::new()), ) - .unwrap(); + .unwrap(); assert_eq!(config.bitcoin_network, BitcoinNetwork::Bitcoin); assert_eq!(config.stacks_version, TransactionVersion::Mainnet); - assert_eq!(config.contract_name.to_string(), sbtc_contract_name); - assert_eq!(config.contract_address.to_string(), sbtc_contract_address); + assert_eq!(config.contract_name.to_string(), mining_contract_name); + assert_eq!(config.contract_address.to_string(), mining_contract_address); assert_eq!(config.stacks_private_key.to_hex(), stacks_private_key); // Test config with no signer_config_file or http_relay_url let config = write_new_config( - format!("{sbtc_contract_address}.{sbtc_contract_name}"), + format!("{mining_contract_address}.{mining_contract_name}"), stacks_private_key.to_string(), None, None, @@ -288,7 +360,7 @@ transaction_fee = 2000 // Test config with no signer_config_file or network_private_key let config = write_new_config( - format!("{sbtc_contract_address}.{sbtc_contract_name}"), + format!("{mining_contract_address}.{mining_contract_name}"), stacks_private_key.to_string(), None, Some(String::new()), @@ -299,7 +371,7 @@ transaction_fee = 2000 // Test config with no signer_config_file or frost_state_file let config = write_new_config( - format!("{sbtc_contract_address}.{sbtc_contract_name}"), + format!("{mining_contract_address}.{mining_contract_name}"), stacks_private_key.to_string(), None, Some(String::new()), @@ -310,7 +382,7 @@ transaction_fee = 2000 // Test config with invalid contract name let config = write_new_config( - format!("garbage.{sbtc_contract_name}"), + format!("garbage.{mining_contract_name}"), stacks_private_key.to_string(), None, Some(String::new()), @@ -321,7 +393,7 @@ transaction_fee = 2000 // Test config with missing "." let config = write_new_config( - format!("{sbtc_contract_address}"), + format!("{mining_contract_address}"), stacks_private_key.to_string(), None, Some(String::new()), @@ -332,7 +404,7 @@ transaction_fee = 2000 // Test config with no contract name let config = write_new_config( - format!("{sbtc_contract_address}."), + format!("{mining_contract_address}."), stacks_private_key.to_string(), None, Some(String::new()), @@ -343,7 +415,7 @@ transaction_fee = 2000 // Test config with garbage contract name let config = write_new_config( - format!("{sbtc_contract_address}.12"), + format!("{mining_contract_address}.12"), stacks_private_key.to_string(), None, Some(String::new()), @@ -354,7 +426,7 @@ transaction_fee = 2000 // Test config with no garbage contract address name let config = write_new_config( - format!("garbage.{sbtc_contract_name}"), + format!("garbage.{mining_contract_name}"), stacks_private_key.to_string(), None, Some(String::new()), @@ -365,7 +437,7 @@ transaction_fee = 2000 // Test config with an invalid private key let config = write_new_config( - format!("{sbtc_contract_address}.{sbtc_contract_name}"), + format!("{mining_contract_address}.{mining_contract_name}"), String::from("Garbage"), None, Some(String::new()), @@ -420,10 +492,11 @@ transaction_fee = 2000 assert_eq!(stacks_version, TransactionVersion::Mainnet); assert_eq!(bitcoin_network, BitcoinNetwork::Bitcoin); } + #[test] fn parse_contract_test() { let mut config = RawConfig::default(); - config.sbtc_contract = "SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sbtc-alpha".to_string(); + config.mining_contract = "SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sbtc-alpha".to_string(); let (parsed_contract_name, parsed_contract_address) = config.parse_contract().unwrap(); assert_eq!( parsed_contract_address.to_string(), @@ -432,21 +505,21 @@ transaction_fee = 2000 assert_eq!(parsed_contract_name.to_string(), "sbtc-alpha".to_string()); // Invalid contract - config.sbtc_contract = "SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTEsbtc-alpha".to_string(); + config.mining_contract = "SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTEsbtc-alpha".to_string(); assert!(matches!( config.parse_contract(), Err(Error::InvalidContract(_)) )); // Invalid contract address - config.sbtc_contract = "SP3FBR2AGK5H9QBDH3EEN6DF8E.sbtc-alpha".to_string(); + config.mining_contract = "SP3FBR2AGK5H9QBDH3EEN6DF8E.sbtc-alpha".to_string(); assert!(matches!( config.parse_contract(), Err(Error::InvalidContract(_)) )); // Invalid contract name - config.sbtc_contract = "SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.12".to_string(); + config.mining_contract = "SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.12".to_string(); assert!(matches!( config.parse_contract(), Err(Error::InvalidContract(_)) diff --git a/stacks-coordinator/src/coordinator.rs b/degen-superior-coordinator/src/coordinator.rs similarity index 59% rename from stacks-coordinator/src/coordinator.rs rename to degen-superior-coordinator/src/coordinator.rs index ecbfa7b2..0ca38aeb 100644 --- a/stacks-coordinator/src/coordinator.rs +++ b/degen-superior-coordinator/src/coordinator.rs @@ -1,21 +1,18 @@ -use bitcoin::{ - psbt::Prevouts, - util::{ - base58, - sighash::{Error as SighashError, SighashCache}, - }, - SchnorrSighashType, XOnlyPublicKey, -}; -use blockstack_lib::{types::chainstate::StacksAddress, util::secp256k1::Secp256k1PublicKey}; -use frost_coordinator::{ +use bitcoin::{psbt::Prevouts, util::{ + base58, + sighash::{Error as SighashError, SighashCache}, +}, hashes::Hash, SchnorrSighashType, XOnlyPublicKey, Address, Txid, PackedLockTime, TxIn, Script, Sequence, Witness, TxOut, OutPoint}; +use stackslib::{types::chainstate::StacksAddress, util::secp256k1::Secp256k1PublicKey}; +use degen_base_coordinator::{ coordinator::Error as FrostCoordinatorError, create_coordinator, create_coordinator_from_path, }; -use frost_signer::{ +use degen_base_signer::{ config::Config as SignerConfig, net::{Error as HttpNetError, HttpNetListen}, - signing_round::DkgPublicShare, + signing_round::{DkgPublicShare, UtxoError}, }; use std::{ + str::FromStr, collections::BTreeMap, fs::File, path::{Path, PathBuf}, @@ -23,14 +20,17 @@ use std::{ thread::sleep, time::Duration, }; +use std::sync::{Arc, Mutex}; use tracing::{debug, info, warn}; use wsts::{common::Signature, field::Element, taproot::SchnorrProof, Point, Scalar}; - -use crate::bitcoin_wallet::BitcoinWallet; -use crate::stacks_node::{self, Error as StacksNodeError}; -use crate::stacks_wallet::StacksWallet; -use crate::{config::Config, stacks_node::client::BroadcastError}; -use crate::{ +use stackslib::vm::types::PrincipalData; + +use degen_base_signer::bitcoin_wallet::BitcoinWallet; +use degen_base_signer::stacks_node::{self, Error as StacksNodeError}; +use degen_base_signer::stacks_wallet::StacksWallet; +use crate::{config::Config}; +use degen_base_signer::stacks_node::client::BroadcastError; +use degen_base_signer::{ peg_wallet::{ BitcoinWallet as BitcoinWalletTrait, Error as PegWalletError, PegWallet, StacksWallet as StacksWalletTrait, WrapPegWallet, @@ -39,15 +39,15 @@ use crate::{ }; // Traits in scope -use crate::bitcoin_node::{ - BitcoinNode, BitcoinTransaction, Error as BitcoinNodeError, LocalhostBitcoinNode, +use degen_base_signer::bitcoin_node::{ + BitcoinNode, BitcoinTransaction, Error as BitcoinNodeError, LocalhostBitcoinNode, UTXO }; -use crate::peg_queue::{ +use degen_base_signer::peg_queue::{ Error as PegQueueError, PegQueue, SbtcOp, SqlitePegQueue, SqlitePegQueueError, }; -use crate::stacks_node::{client::NodeClient, StacksNode}; +use degen_base_signer::stacks_node::{client::NodeClient, StacksNode}; -type FrostCoordinator = frost_coordinator::coordinator::Coordinator; +type FrostCoordinator = degen_base_coordinator::coordinator::Coordinator; /// Helper that uses this module's error type pub type Result = std::result::Result; @@ -251,6 +251,49 @@ trait CoordinatorHelpers: Coordinator { } } } + + fn sign_tx_from_script( + &mut self, + utxos: Vec, + // op: &stacks_node::PegOutRequestOp, + tx: BitcoinTransaction, + ) -> Result { + // Build unsigned fulfilled peg out transaction + // let (mut tx, prevouts) = self.fee_wallet().bitcoin().fulfill_peg_out(op, utxos)?; + let mut prevouts: Vec = vec![]; + for utxo in utxos { + prevouts.push(TxOut { + value: utxo.amount, + script_pubkey: Script::from_str(utxo.scriptPubKey.as_str()).unwrap(), + }); + }; + let mut signed_tx = tx.clone(); + let sighash_tx = tx.clone(); + let mut sighash_cache = SighashCache::new(&sighash_tx); + // Sign the transaction + for index in 0..sighash_tx.input.len() { + let taproot_sighash = sighash_cache + .taproot_key_spend_signature_hash( + index, + &Prevouts::All(&prevouts), + SchnorrSighashType::Default, + ) + .map_err(Error::SigningError)?; + let (_frost_sig, schnorr_proof) = self + .frost_coordinator_mut() + .sign_pox_transaction(&taproot_sighash.as_hash(), &sighash_tx)?; + debug!( + "Fulfill Tx {:?} SchnorrProof ({},{})", + &tx, schnorr_proof.r, schnorr_proof.s + ); + let finalized = schnorr_proof.to_bytes(); + let finalized_b58 = base58::encode_slice(&finalized); + debug!("CALC SIG ({}) {}", finalized.len(), finalized_b58); + signed_tx.input[index].witness.push(finalized); + } + //Return the signed transaction + Ok(signed_tx) + } } impl CoordinatorHelpers for T {} @@ -265,6 +308,7 @@ pub struct StacksCoordinator { local_peg_queue: SqlitePegQueue, local_stacks_node: NodeClient, local_bitcoin_node: LocalhostBitcoinNode, + fee_to_pox: u64, pub local_fee_wallet: WrapPegWallet, } @@ -278,6 +322,230 @@ impl StacksCoordinator { pub fn sign_message(&mut self, message: &str) -> Result<(Signature, SchnorrProof)> { Ok(self.frost_coordinator.sign_message(message.as_bytes())?) } + + pub fn run_create_script(&mut self) -> Result { + let (response_utxos, response_stacks_addresses, response_merkle_roots) = self.frost_coordinator.run_create_scripts_generation(); + // check if signers sent correct details to coordinator + let mut utxos= vec![]; + let mut bad_actors = vec![]; + let mut good_actors = vec![]; + let mut impersonators_positions = vec![]; + let mut to_be_voted_out = vec![]; + let mut all_miners: Vec = self.local_stacks_node.get_miners_list(&self.local_fee_wallet.stacks_wallet.address()).unwrap_or(vec![self.local_fee_wallet.stacks_wallet.address().clone()]); + let coordinator = StacksAddress::from(self.local_stacks_node.get_notifier(&self.local_fee_wallet.stacks_wallet.address()).unwrap_or(PrincipalData::from(self.local_fee_wallet.stacks_wallet.address().clone()))); + let amount_to_pox = self.local_stacks_node.get_pool_total_spend_per_block(self.local_fee_wallet.stacks_wallet.address()).unwrap_or(0) / all_miners.len() as u128; + let mut can_create_tx = true; + all_miners.retain(|signer| signer != &coordinator); + + // Divide the addresses by the types of their response. If an error came through, add him to bad actors list. + for position in 0..response_utxos.len() { + if response_utxos[position].clone().unwrap_or(UTXO::default()) != UTXO::default() { + if response_utxos[position].clone().unwrap().amount as u128 >= amount_to_pox + self.fee_to_pox as u128 / (all_miners.len() + 1) as u128 { + good_actors.push(response_stacks_addresses[position]); + utxos.push(response_utxos[position].clone().unwrap()); + } + else { + can_create_tx = false; + bad_actors.push(response_stacks_addresses[position]); + } + } + else { + can_create_tx = false; + bad_actors.push(response_stacks_addresses[position]); + } + } + + // If there is a certain address in both bad and good list, it means someone is trying to impersonate others. + // Add the 'bad actor's position - len of list' (because later when we remove them, the index will decrease by 1 every removal) to a list + for position in 0..bad_actors.len() { + if good_actors.contains(&bad_actors[position]) { + impersonators_positions.push(position - impersonators_positions.len()); + } + } + + // For every impersonating actor, remove him from the bad actors list + for position in &impersonators_positions { + bad_actors.remove(*position); + } + + // Only keep people that are not bad actors in the miner's list + for bad_actor in &bad_actors { + all_miners.retain(|actor| &actor != &bad_actor); + } + + // Also remove the good actors from the miner's list - so now, only impersonators are left (they didn't appear in any list) + for good_actor in &good_actors { + all_miners.retain(|actor| &actor != &good_actor); + } + + // Add impersonators in bad miners list - delete this if we decide to take another action for them + for impersonator in all_miners { + can_create_tx = false; + bad_actors.push(impersonator); + } + + // Make a temporary nonce in order to avoid ConflictingNonceInMempool errors + let mut nonce = self.local_stacks_node.next_nonce(&self.local_fee_wallet.stacks_wallet.address()).unwrap_or(0); + + // TODO: Remove comments once testing done + // Check for warnings and warn or propose for removal the actors + // for actor in bad_actors { + // match self.local_stacks_node.get_warn_number_user(&self.local_fee_wallet.stacks_wallet.address(), &actor) { + // Ok(warnings_number) => { + // if warnings_number < 2 { + // match self.local_fee_wallet.stacks_wallet.warn_miner(nonce, actor) { + // Ok(tx) => { + // match self.local_stacks_node.broadcast_transaction(&tx) { + // Ok(()) => { + // info!("Successfully warned {:?}.", &actor.to_string()); + // nonce += 1; + // } + // Err(e) => { + // info!("Couldn't broadcast warning transaction for {:?}: {:?}", &actor.to_string(), e); + // } + // } + // } + // Err(e) => { + // info!("Couldn't warn {:?}: {:?}", &actor.to_string(), e); + // } + // } + // } else { + // match self.local_fee_wallet.stacks_wallet.propose_removal(nonce, actor) { + // Ok(tx) => { + // match self.local_stacks_node.broadcast_transaction(&tx) { + // Ok(()) => { + // info!("Proposed {:?} for removal.", &actor.to_string()); + // to_be_voted_out.push(actor); + // nonce += 1; + // } + // Err(e) => { + // info!("Failed to broadcast propose for removal transaction for {:?}: {:?}", &actor.to_string(), e); + // } + // } + + // match self.local_fee_wallet.stacks_wallet.vote_positive_remove_request(nonce, actor) { + // Ok(tx) => { + // match self.local_stacks_node.broadcast_transaction(&tx) { + // Ok(()) => { + // info!("Successfully voted out {:?}", &actor.to_string()); + // nonce += 1; + // } + // Err(e) => { + // info!("Failed to broadcast vote positive removal transaction for {:?}: {:?}", &actor.to_string(), e); + // } + // } + // } + // Err(e) => { + // info!("Couldn't vote positive for kicking {:?} out of pool: {:?}", &actor.to_string(), e); + // } + // } + // } + // Err(e) => { + // info!("Couldn't propose for removal {:?}: {:?}", &actor.to_string(), e) + // } + // } + // } + // } + // Err(e) => { + // info!("Couldn't get warnings number for {:#?}: {:?}", &actor.to_string(), e); + // } + // } + // } + + if to_be_voted_out.len() > 0 { + self.frost_coordinator.run_voting_actors_out(to_be_voted_out).unwrap(); + } + + if can_create_tx { + let tx = create_tx_from_txids( + vec![ + &Address::from_str("bcrt1phvt5tfz4hlkth0k7ls9djweuv9rwv5a0s5sa9085umupftnyalxq0zx28d").unwrap(), + &Address::from_str("bcrt1pdsavc4yrdq0sdmjcmf7967eeem2ny6vzr4f8m7dyemcvncs0xtwsc85zdq").unwrap() + ], + &utxos, + 1000, + self.local_stacks_node.get_pool_total_spend_per_block(self.local_fee_wallet.stacks_wallet.address()).unwrap_or(0) as u64, + ); + match self.sign_tx_from_script(utxos, tx) { + Ok(signed_tx) => { + info!("{:#?}", signed_tx); + match self.local_bitcoin_node.broadcast_transaction(&signed_tx) { + Ok(txid) => { + self.frost_coordinator.send_txid_to_signers(txid)?; + info!("Successfully broadcasted transaction from scripts to PoX. Txid: {:?}", txid); + } + Err(e) => { + info!("Couldn't broadcast the scripts to PoX transaction: {:?}", e); + } + } + } + Err(e) => { + info!("Couldn't sign transaction from scripts to PoX: {:?}", e); + } + } + } + else { + self.frost_coordinator.send_txid_to_signers(Txid::all_zeros())?; + info!("There was a bad actor in the pool, transaction creation aborted.") + } + + Ok(0) + } +} + +fn create_tx_from_txids( + user_addresses: Vec<&Address>, + utxos: &Vec, + fee: u64, + total_amount: u64, +) -> BitcoinTransaction { + let mut inputs = vec![]; + let mut outputs = vec![]; + let amount_to_each_pox_address = total_amount / user_addresses.len() as u64; + let number_of_signers = utxos.len() as u64; + + for utxo in utxos { + let outpoint = OutPoint::new( + Txid::from_str(utxo.txid.as_str()).unwrap(), + utxo.vout.clone() + ); + + inputs.push( + TxIn { + previous_output: outpoint, + script_sig: Script::new(), + sequence: Sequence(0x8030FFFF), + witness: Witness::default(), + } + ); + + if utxo.amount > (total_amount + fee) / number_of_signers { + let amount_back = utxo.amount - ((total_amount + fee) / number_of_signers); + + outputs.push( + TxOut { + value: amount_back, + script_pubkey: Script::from_str(&utxo.scriptPubKey).unwrap(), + } + ) + } + } + + for address in user_addresses { + outputs.push( + TxOut { + value: amount_to_each_pox_address, + script_pubkey: address.script_pubkey(), + } + ) + } + + BitcoinTransaction { + version: 2, + lock_time: PackedLockTime(100), + input: inputs, + output: outputs, + } } fn create_frost_coordinator_from_path( @@ -316,7 +584,7 @@ fn create_frost_coordinator_from_path( &coordinator_public_key, nonce, )?; - stacks_node.broadcast_transaction(&coordinator_tx)?; + // stacks_node.broadcast_transaction(&coordinator_tx)?; } Ok(coordinator) } @@ -341,13 +609,35 @@ fn create_frost_coordinator_from_contract( ) .map_err(|_| Error::ConfigError("Invalid network_private_key.".to_string()))?; let http_relay_url = config.http_relay_url.clone().unwrap_or(String::new()); + let miner_status = stacks_node.get_status(&config.stacks_address).unwrap(); create_coordinator(&SignerConfig::new( + config.contract_name.clone(), + config.contract_address.clone(), + config.stacks_private_key.clone(), + config.stacks_address.clone(), + config.stacks_node_rpc_url.clone(), + config.local_stacks_node.clone(), + config.stacks_wallet.clone(), + config.stacks_version.clone(), + config.bitcoin_private_key.clone(), + config.bitcoin_xpub.clone(), + config.bitcoin_node_rpc_url.clone(), + config.local_bitcoin_node.clone(), + config.bitcoin_wallet.clone(), + config.transaction_fee.clone(), + config.bitcoin_network.clone(), keys_threshold.try_into().unwrap(), + config.amount_to_script, + config.fee_to_script, + config.fee_to_pox, coordinator_public_key, public_keys, signer_key_ids, network_private_key, http_relay_url, + miner_status, + Arc::new(Mutex::new(Vec::::new())), + true, )) .map_err(|e| Error::ConfigError(e.to_string())) } @@ -442,7 +732,7 @@ fn load_dkg_data( let nonce = stacks_node.next_nonce(address)?; let tx = stacks_wallet.build_set_bitcoin_wallet_public_key_transaction(&xonly_pubkey, nonce)?; - stacks_node.broadcast_transaction(&tx)?; + // stacks_node.broadcast_transaction(&tx)?; Ok(xonly_pubkey) } } @@ -492,12 +782,14 @@ impl TryFrom<&Config> for StacksCoordinator { } else { SqlitePegQueue::in_memory(start_block_height, current_block_height) }?; + let fee_to_pox = config.fee_to_pox; Ok(Self { local_peg_queue, local_stacks_node, local_bitcoin_node, frost_coordinator, + fee_to_pox, local_fee_wallet: WrapPegWallet { bitcoin_wallet, stacks_wallet, @@ -549,11 +841,11 @@ impl Coordinator for StacksCoordinator { mod tests { use crate::config::{Config, RawConfig}; use crate::coordinator::{CoordinatorHelpers, StacksCoordinator}; - use crate::stacks_node::PegOutRequestOp; + use degen_base_signer::stacks_node::PegOutRequestOp; use bitcoin::consensus::Encodable; - use blockstack_lib::burnchains::Txid; - use blockstack_lib::chainstate::stacks::address::{PoxAddress, PoxAddressType20}; - use blockstack_lib::types::chainstate::BurnchainHeaderHash; + use stackslib::burnchains::Txid; + use stackslib::chainstate::stacks::address::{PoxAddress, PoxAddressType20}; + use stackslib::types::chainstate::BurnchainHeaderHash; #[ignore] #[test] @@ -571,7 +863,7 @@ mod tests { let op = PegOutRequestOp { amount: 0, recipient: recipient, - signature: blockstack_lib::util::secp256k1::MessageSignature([0; 65]), + signature: stackslib::util::secp256k1::MessageSignature([0; 65]), peg_wallet_address: peg_wallet_address, fulfillment_fee: 0, memo: vec![], diff --git a/degen-superior-coordinator/src/lib.rs b/degen-superior-coordinator/src/lib.rs new file mode 100644 index 00000000..78ea37b9 --- /dev/null +++ b/degen-superior-coordinator/src/lib.rs @@ -0,0 +1,4 @@ +pub mod cli; +pub mod config; +pub mod coordinator; +mod util; diff --git a/stacks-coordinator/src/main.rs b/degen-superior-coordinator/src/main.rs similarity index 89% rename from stacks-coordinator/src/main.rs rename to degen-superior-coordinator/src/main.rs index b5ac0239..79b21700 100644 --- a/stacks-coordinator/src/main.rs +++ b/degen-superior-coordinator/src/main.rs @@ -1,8 +1,8 @@ use clap::Parser; -use frost_signer::logging; -use stacks_coordinator::cli::{Cli, Command}; -use stacks_coordinator::config::Config; -use stacks_coordinator::coordinator::{Coordinator, StacksCoordinator}; +use degen_base_signer::logging; +use degen_superior_coordinator::cli::{Cli, Command}; +use degen_superior_coordinator::config::Config; +use degen_superior_coordinator::coordinator::{Coordinator, StacksCoordinator}; use tracing::{error, info, warn}; fn main() { @@ -41,6 +41,8 @@ fn main() { if let Err(e) = coordinator.run_dkg_round() { warn!("An error occurred during DKG round: {}", e); }; + // TODO: degens - move separately + coordinator.run_create_script(); info!("Running Signing Round"); let (signature, schnorr_proof) = match coordinator.sign_message("Hello, world!") { diff --git a/stacks-coordinator/src/util.rs b/degen-superior-coordinator/src/util.rs similarity index 98% rename from stacks-coordinator/src/util.rs rename to degen-superior-coordinator/src/util.rs index 8b820efc..3b94bd88 100644 --- a/stacks-coordinator/src/util.rs +++ b/degen-superior-coordinator/src/util.rs @@ -1,4 +1,4 @@ -use blockstack_lib::{ +use stackslib::{ address::{C32_ADDRESS_VERSION_MAINNET_SINGLESIG, C32_ADDRESS_VERSION_TESTNET_SINGLESIG}, chainstate::stacks::TransactionVersion, }; @@ -12,7 +12,7 @@ pub fn address_version(version: &TransactionVersion) -> u8 { #[cfg(test)] pub mod test { - use blockstack_lib::{ + use stackslib::{ burnchains::{ bitcoin::{ address::{BitcoinAddress, SegwitBitcoinAddress}, @@ -25,6 +25,7 @@ pub mod test { }; use rand::Rng; + // TODO: degens - modify accordingly pub const PRIVATE_KEY_HEX: &str = "b244296d5907de9864c0b0d51f98a13c52890be0404e83f273144cd5b9960eed01"; pub const PUBLIC_KEY_HEX: &str = diff --git a/stacks-coordinator/tests/local_bitcoin_node.rs b/degen-superior-coordinator/tests/local_bitcoin_node.rs similarity index 99% rename from stacks-coordinator/tests/local_bitcoin_node.rs rename to degen-superior-coordinator/tests/local_bitcoin_node.rs index 3d459423..56c93f4d 100644 --- a/stacks-coordinator/tests/local_bitcoin_node.rs +++ b/degen-superior-coordinator/tests/local_bitcoin_node.rs @@ -1,7 +1,7 @@ use std::str::FromStr; use bitcoin::{Address, Network, OutPoint}; -use stacks_coordinator::{ +use degen_base_signer::{ bitcoin_node::{BitcoinNode, LocalhostBitcoinNode}, bitcoin_wallet::BitcoinWallet, peg_wallet::BitcoinWallet as BitcoinWalletTrait, diff --git a/stacks-signer/Cargo.toml b/degen-superior-signer/Cargo.toml similarity index 84% rename from stacks-signer/Cargo.toml rename to degen-superior-signer/Cargo.toml index b1d60323..7cb32eca 100644 --- a/stacks-signer/Cargo.toml +++ b/degen-superior-signer/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "stacks-signer" +name = "degen-superior-signer" version = "0.1.0" edition = "2021" @@ -7,7 +7,7 @@ edition = "2021" [dependencies] clap = { workspace = true } -frost-signer = { path = "../frost-signer" } +degen-base-signer = { path = "../degen-base-signer" } rand_core = "0.6" serde = { workspace = true } thiserror = { workspace = true } diff --git a/stacks-signer/README.md b/degen-superior-signer/README.md similarity index 97% rename from stacks-signer/README.md rename to degen-superior-signer/README.md index 4fbca050..3fcce715 100644 --- a/stacks-signer/README.md +++ b/degen-superior-signer/README.md @@ -20,7 +20,7 @@ communicates with a signer entity. ```rust pub trait SignerService { type RelayServer; - type StacksNode; // Shared implementation with stacks-coordinator + type StacksNode; // Shared implementation with degen-superior-coordinator type Signer: Signer; fn relay(&self) -> &Self::RelayServer; diff --git a/stacks-coordinator/conf/signer.toml b/degen-superior-signer/conf/signer.toml similarity index 100% rename from stacks-coordinator/conf/signer.toml rename to degen-superior-signer/conf/signer.toml diff --git a/degen-superior-signer/conf/signer1.toml b/degen-superior-signer/conf/signer1.toml new file mode 100644 index 00000000..f0210c36 --- /dev/null +++ b/degen-superior-signer/conf/signer1.toml @@ -0,0 +1,32 @@ +mining_contract = "ST02D2KP0630FS1BCJ7YM4TYMDH6NS9QKR0B57R3.mining-m3-testing-v3" +exchange_contract = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.bridge-contract" +stacks_private_key = "811ad2e8f9bafb837c6f7df8521d71a2782b19701715b511018eaa93c3ed84da01" +bitcoin_private_key = "1d799760009ca66cb0ad05a80e5b781deabf0550923fad7ad4417f61702f6353" +stacks_node_rpc_url = "https://stacks-node-api.testnet.stacks.co/" +bitcoin_node_rpc_url = "http://devnet:devnet@localhost:18443" +transaction_fee = 2000 +network = "testnet" +http_relay_url = "http://localhost:9776" +keys_threshold = 4 +# Insert the amount you want to send to script, in satoshi - 1 BTC = 100,000,000 SATS (if you want to fund the script each block, insert 0) +amount_to_script = 0 +fee_to_script = 300 +fee_to_pox = 1000 +#frost_state_file = "frost.state.bin" +#network_private_key = "GGJL5xL1o8U5TJNMtQiT8YK6sdYgpTrN1TZCeFrcYxeV" +#signers = [ +# {public_key = "xvwd4f8L4oAmAXNhqMvQvbMiniCSeNH1rD9ZwCEEf7b6", key_ids = [1, 2]}, +# {public_key = "cLuu4PSQLxi6bjFYGyTvyAFRUe3XfJL8mQ99Bd9A8mv9", key_ids = [3, 4]}, +# {public_key = "gKCpZS42qdyVh9qQLtjbJYg8reEhjgyho6z6ghmLXZUx", key_ids = [5, 6]} +#] +#coordinator_public_key = "2A1tcY8RFby6pTNk8wQ3qGPCgWRXTL24yMwmcXiG7tKCZ" + +# We had these, which worked: +frost_state_file = "frost.state.bin" +network_private_key = "9aSCCR6eirt1NAHwJtSz4HMwBHTyMo62SyPMvVDt5DQn" +signers = [ + {public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj", key_ids = [1, 2]}, + {public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj", key_ids = [3, 4]}, + {public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj", key_ids = [5, 6]} +] +coordinator_public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj" \ No newline at end of file diff --git a/degen-superior-signer/conf/signer2.toml b/degen-superior-signer/conf/signer2.toml new file mode 100644 index 00000000..d6a19634 --- /dev/null +++ b/degen-superior-signer/conf/signer2.toml @@ -0,0 +1,32 @@ +mining_contract = "ST02D2KP0630FS1BCJ7YM4TYMDH6NS9QKR0B57R3.mining-m3-testing-v3" +exchange_contract = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.bridge-contract" +stacks_private_key = "5bfaefc764eb822296d21fc1ff36ff88c59c62419baa5c5cfb90194cd84af8e801" +bitcoin_private_key = "91436bd90d9cde7ba3162375b7692ae3f22ad01586cb4520bffae48d3a480f6a" +stacks_node_rpc_url = "https://stacks-node-api.testnet.stacks.co/" +bitcoin_node_rpc_url = "http://devnet:devnet@localhost:18443" +transaction_fee = 2000 +network = "testnet" +http_relay_url = "http://localhost:9776" +keys_threshold = 4 +# Insert the amount you want to send to script, in satoshi - 1 BTC = 100,000,000 SATS (if you want to fund the script each block, insert 0) +amount_to_script = 100_000 +fee_to_script = 300 +fee_to_pox = 1000 +#frost_state_file = "frost.state.bin" +#network_private_key = "F2BmyfiCuuC7665kwCFw5SgZ8GA6j9wpssn6sqvHBbpX" +#signers = [ +# {public_key = "xvwd4f8L4oAmAXNhqMvQvbMiniCSeNH1rD9ZwCEEf7b6", key_ids = [1, 2]}, +# {public_key = "cLuu4PSQLxi6bjFYGyTvyAFRUe3XfJL8mQ99Bd9A8mv9", key_ids = [3, 4]}, +# {public_key = "gKCpZS42qdyVh9qQLtjbJYg8reEhjgyho6z6ghmLXZUx", key_ids = [5, 6]} +#] +#coordinator_public_key = "2A1tcY8RFby6pTNk8wQ3qGPCgWRXTL24yMwmcXiG7tKCZ" + +# We had these, which worked: +frost_state_file = "frost.state.bin" +network_private_key = "9aSCCR6eirt1NAHwJtSz4HMwBHTyMo62SyPMvVDt5DQn" +signers = [ + {public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj", key_ids = [1, 2]}, + {public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj", key_ids = [3, 4]}, + {public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj", key_ids = [5, 6]} +] +coordinator_public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj" \ No newline at end of file diff --git a/degen-superior-signer/conf/signer3.toml b/degen-superior-signer/conf/signer3.toml new file mode 100644 index 00000000..650aaace --- /dev/null +++ b/degen-superior-signer/conf/signer3.toml @@ -0,0 +1,32 @@ +mining_contract = "ST02D2KP0630FS1BCJ7YM4TYMDH6NS9QKR0B57R3.mining-m3-testing-v3" +exchange_contract = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.bridge-contract" +stacks_private_key = "17ebd9a02291e3c4f54a953d16bc14442ea5ed82a06de290d44c9580394e5d4a01" +bitcoin_private_key = "25c69df27df3f630daa05a344679474f383bf49e5c577354169f4858484abfe3" +stacks_node_rpc_url = "https://stacks-node-api.testnet.stacks.co/" +bitcoin_node_rpc_url = "http://devnet:devnet@localhost:18443" +transaction_fee = 2000 +network = "testnet" +http_relay_url = "http://localhost:9776" +keys_threshold = 4 +# Insert the amount you want to send to script, in satoshi - 1 BTC = 100,000,000 SATS (if you want to fund the script each block, insert 0) +amount_to_script = 0 +fee_to_script = 300 +fee_to_pox = 1000 +#frost_state_file = "frost.state.bin" +#network_private_key = "hkBLK7JXHv14yCH7UDzx3bxaur9ixPFopmdLRvfP4Kr" +#signers = [ +# {public_key = "xvwd4f8L4oAmAXNhqMvQvbMiniCSeNH1rD9ZwCEEf7b6", key_ids = [1, 2]}, +# {public_key = "cLuu4PSQLxi6bjFYGyTvyAFRUe3XfJL8mQ99Bd9A8mv9", key_ids = [3, 4]}, +# {public_key = "gKCpZS42qdyVh9qQLtjbJYg8reEhjgyho6z6ghmLXZUx", key_ids = [5, 6]} +#] +#coordinator_public_key = "2A1tcY8RFby6pTNk8wQ3qGPCgWRXTL24yMwmcXiG7tKCZ" + +# We had these, which worked: +frost_state_file = "frost.state.bin" +network_private_key = "9aSCCR6eirt1NAHwJtSz4HMwBHTyMo62SyPMvVDt5DQn" +signers = [ + {public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj", key_ids = [1, 2]}, + {public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj", key_ids = [3, 4]}, + {public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj", key_ids = [5, 6]} +] +coordinator_public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj" \ No newline at end of file diff --git a/stacks-signer/src/cli.rs b/degen-superior-signer/src/cli.rs similarity index 100% rename from stacks-signer/src/cli.rs rename to degen-superior-signer/src/cli.rs diff --git a/stacks-signer/src/lib.rs b/degen-superior-signer/src/lib.rs similarity index 93% rename from stacks-signer/src/lib.rs rename to degen-superior-signer/src/lib.rs index b19f5d60..3600913e 100644 --- a/stacks-signer/src/lib.rs +++ b/degen-superior-signer/src/lib.rs @@ -16,7 +16,7 @@ const BUILD_TYPE: &'static str = "release"; pub fn version() -> String { format!( - "stacks-signer {} {} {}", + "degen-superior-signer {} {} {}", BUILD_TYPE, GIT_BRANCH.unwrap_or(""), GIT_COMMIT.unwrap_or("") diff --git a/stacks-signer/src/main.rs b/degen-superior-signer/src/main.rs similarity index 84% rename from stacks-signer/src/main.rs rename to degen-superior-signer/src/main.rs index 5526066d..26f868c5 100644 --- a/stacks-signer/src/main.rs +++ b/degen-superior-signer/src/main.rs @@ -1,8 +1,8 @@ use clap::Parser; -use frost_signer::config::Config; -use frost_signer::logging; -use stacks_signer::cli::{Cli, Command}; -use stacks_signer::signer::Signer; +use degen_base_signer::config::Config; +use degen_base_signer::logging; +use degen_superior_signer::cli::{Cli, Command}; +use degen_superior_signer::signer::Signer; use tracing::info; use wsts::Point; @@ -19,7 +19,7 @@ fn main() { match Config::from_path(&config) { Ok(config) => { let mut signer = Signer::new(config, id); - info!("{} signer id #{}", stacks_signer::version(), id); // sign-on message + info!("{} signer id #{}", degen_superior_signer::version(), id); // sign-on message if let Err(e) = signer.start_p2p_sync() { panic!("An error occurred on the P2P Network: {}", e); } diff --git a/stacks-signer/src/secp256k1.rs b/degen-superior-signer/src/secp256k1.rs similarity index 100% rename from stacks-signer/src/secp256k1.rs rename to degen-superior-signer/src/secp256k1.rs diff --git a/stacks-signer/src/signer.rs b/degen-superior-signer/src/signer.rs similarity index 80% rename from stacks-signer/src/signer.rs rename to degen-superior-signer/src/signer.rs index c5a84cb4..28effc1b 100644 --- a/stacks-signer/src/signer.rs +++ b/degen-superior-signer/src/signer.rs @@ -1,5 +1,5 @@ -use frost_signer::config::Config; -use frost_signer::signer::{Error as SignerError, Signer as FrostSigner}; +use degen_base_signer::config::Config; +use degen_base_signer::signer::{Error as SignerError, Signer as FrostSigner}; #[derive(Clone)] pub struct Signer { diff --git a/stacks-signer/tests/cli.rs b/degen-superior-signer/tests/cli.rs similarity index 85% rename from stacks-signer/tests/cli.rs rename to degen-superior-signer/tests/cli.rs index 832acbdc..1dca8a39 100644 --- a/stacks-signer/tests/cli.rs +++ b/degen-superior-signer/tests/cli.rs @@ -5,7 +5,7 @@ use testdir::testdir; #[test] fn secp256k1_to_stdout() { - let mut cmd = Command::cargo_bin("stacks-signer").unwrap(); + let mut cmd = Command::cargo_bin("degen-superior-signer").unwrap(); cmd.arg("private-key"); cmd.env("RUST_LOG", "info"); @@ -20,7 +20,7 @@ fn secp256k1_to_file() { output_path.push(".priv_key"); assert!(!output_path.exists()); - let mut cmd = Command::cargo_bin("stacks-signer").unwrap(); + let mut cmd = Command::cargo_bin("degen-superior-signer").unwrap(); cmd.arg("private-key").arg("-f"); cmd.env("RUST_LOG", "info"); //Test with no filename specified. diff --git a/frost-signer/conf/signer1.toml b/frost-signer/conf/signer1.toml deleted file mode 100644 index 2dd6003a..00000000 --- a/frost-signer/conf/signer1.toml +++ /dev/null @@ -1,10 +0,0 @@ -http_relay_url = "http://localhost:9776" -keys_threshold = 4 -frost_state_file = "frost.signer1.state.bin" -network_private_key = "GGJL5xL1o8U5TJNMtQiT8YK6sdYgpTrN1TZCeFrcYxeV" -signers = [ - {public_key = "xvwd4f8L4oAmAXNhqMvQvbMiniCSeNH1rD9ZwCEEf7b6", key_ids = [1, 2]}, - {public_key = "cLuu4PSQLxi6bjFYGyTvyAFRUe3XfJL8mQ99Bd9A8mv9", key_ids = [3, 4]}, - {public_key = "gKCpZS42qdyVh9qQLtjbJYg8reEhjgyho6z6ghmLXZUx", key_ids = [5, 6]} -] -coordinator_public_key = "2A1tcY8RFby6pTNk8wQ3qGPCgWRXTL24yMwmcXiG7tKCZ" \ No newline at end of file diff --git a/frost-signer/conf/signer2.toml b/frost-signer/conf/signer2.toml deleted file mode 100644 index 04d8fc25..00000000 --- a/frost-signer/conf/signer2.toml +++ /dev/null @@ -1,10 +0,0 @@ -http_relay_url = "http://localhost:9776" -keys_threshold = 4 -frost_state_file = "frost.signer2.state.bin" -network_private_key = "F2BmyfiCuuC7665kwCFw5SgZ8GA6j9wpssn6sqvHBbpX" -signers = [ - {public_key = "xvwd4f8L4oAmAXNhqMvQvbMiniCSeNH1rD9ZwCEEf7b6", key_ids = [1, 2]}, - {public_key = "cLuu4PSQLxi6bjFYGyTvyAFRUe3XfJL8mQ99Bd9A8mv9", key_ids = [3, 4]}, - {public_key = "gKCpZS42qdyVh9qQLtjbJYg8reEhjgyho6z6ghmLXZUx", key_ids = [5, 6]} -] -coordinator_public_key = "2A1tcY8RFby6pTNk8wQ3qGPCgWRXTL24yMwmcXiG7tKCZ" \ No newline at end of file diff --git a/frost-signer/conf/signer3.toml b/frost-signer/conf/signer3.toml deleted file mode 100644 index 4264533c..00000000 --- a/frost-signer/conf/signer3.toml +++ /dev/null @@ -1,10 +0,0 @@ -http_relay_url = "http://localhost:9776" -keys_threshold = 4 -frost_state_file = "frost.signer3.state.bin" -network_private_key = "hkBLK7JXHv14yCH7UDzx3bxaur9ixPFopmdLRvfP4Kr" -signers = [ - {public_key = "xvwd4f8L4oAmAXNhqMvQvbMiniCSeNH1rD9ZwCEEf7b6", key_ids = [1, 2]}, - {public_key = "cLuu4PSQLxi6bjFYGyTvyAFRUe3XfJL8mQ99Bd9A8mv9", key_ids = [3, 4]}, - {public_key = "gKCpZS42qdyVh9qQLtjbJYg8reEhjgyho6z6ghmLXZUx", key_ids = [5, 6]} -] -coordinator_public_key = "2A1tcY8RFby6pTNk8wQ3qGPCgWRXTL24yMwmcXiG7tKCZ" \ No newline at end of file diff --git a/frost-signer/src/config.rs b/frost-signer/src/config.rs deleted file mode 100644 index ee0b9e0f..00000000 --- a/frost-signer/src/config.rs +++ /dev/null @@ -1,264 +0,0 @@ -use clap::Parser; -use hashbrown::HashMap; -use p256k1::{ - ecdsa::{self, KeyError}, - scalar::{Error as ScalarError, Scalar}, -}; -use serde::Deserialize; -use std::fs; -use toml; - -use crate::util::parse_public_key; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("{0}")] - IO(#[from] std::io::Error), - #[error("{0}")] - Toml(#[from] toml::de::Error), - #[error("Invalid Public Key: {0}")] - InvalidPublicKey(KeyError), - #[error("Failed to parse network_private_key: {0}")] - InvalidPrivateKey(ScalarError), - #[error("Invalid Key ID. Must specify Key IDs greater than 0.")] - InvalidKeyID, -} - -#[derive(Parser)] -#[command(author, version, about, long_about = None)] -pub struct Cli { - /// Turn debugging information on - #[arg(short, long, action = clap::ArgAction::Count)] - debug: u8, - - /// Config file path - #[arg(short, long)] - pub config: String, - - /// Start a signing round - #[arg(short, long)] - pub start: bool, - - /// ID associated with signer - #[arg(short, long)] - pub id: u32, -} - -#[derive(Clone, Deserialize, Default, Debug)] -struct RawSigners { - pub public_key: String, - pub key_ids: Vec, -} - -#[derive(Clone, Deserialize, Default, Debug)] -struct RawConfig { - pub http_relay_url: String, - pub keys_threshold: u32, - pub network_private_key: String, - signers: Vec, - coordinator_public_key: String, -} - -pub type SignerKeyIds = HashMap>; - -impl RawConfig { - pub fn from_path(path: impl AsRef) -> Result { - let content = fs::read_to_string(path)?; - Ok(toml::from_str(&content)?) - } - - pub fn public_keys(&self) -> Result { - let mut public_keys = PublicKeys::default(); - for (i, s) in self.signers.iter().enumerate() { - let signer_public_key = - parse_public_key(&s.public_key).map_err(Error::InvalidPublicKey)?; - for key_id in &s.key_ids { - //We do not allow a key id of 0. - if *key_id == 0 { - return Err(Error::InvalidKeyID); - } - public_keys.key_ids.insert(*key_id, signer_public_key); - } - //We start our signer and key IDs from 1 hence the + 1; - let signer_key = u32::try_from(i).unwrap() + 1; - public_keys.signers.insert(signer_key, signer_public_key); - } - Ok(public_keys) - } - - pub fn signer_key_ids(&self) -> SignerKeyIds { - let mut signer_key_ids = SignerKeyIds::default(); - for (i, s) in self.signers.iter().enumerate() { - signer_key_ids.insert((i + 1).try_into().unwrap(), s.key_ids.clone()); - } - signer_key_ids - } - - pub fn coordinator_public_key(&self) -> Result { - parse_public_key(&self.coordinator_public_key).map_err(Error::InvalidPublicKey) - } - - pub fn network_private_key(&self) -> Result { - let network_private_key = Scalar::try_from(self.network_private_key.as_str()) - .map_err(Error::InvalidPrivateKey)?; - Ok(network_private_key) - } -} - -#[derive(Default, Clone, Debug)] -pub struct PublicKeys { - pub signers: HashMap, - pub key_ids: HashMap, -} - -#[derive(Clone, Debug)] -pub struct Config { - pub http_relay_url: String, - pub keys_threshold: u32, - pub network_private_key: Scalar, - pub public_keys: PublicKeys, - pub signer_key_ids: SignerKeyIds, - pub coordinator_public_key: ecdsa::PublicKey, - pub total_signers: u32, - pub total_keys: u32, -} - -impl Config { - pub fn new( - keys_threshold: u32, - coordinator_public_key: ecdsa::PublicKey, - public_keys: PublicKeys, - signer_key_ids: SignerKeyIds, - network_private_key: Scalar, - http_relay_url: String, - ) -> Config { - Self { - keys_threshold, - coordinator_public_key, - network_private_key, - http_relay_url, - total_signers: public_keys.signers.len().try_into().unwrap(), - total_keys: public_keys.key_ids.len().try_into().unwrap(), - public_keys, - signer_key_ids, - } - } - - pub fn from_path(path: impl AsRef) -> Result { - let raw_config = RawConfig::from_path(path)?; - Config::try_from(&raw_config) - } -} - -impl TryFrom<&RawConfig> for Config { - type Error = Error; - fn try_from(raw_config: &RawConfig) -> Result { - Ok(Config::new( - raw_config.keys_threshold, - raw_config.coordinator_public_key()?, - raw_config.public_keys()?, - raw_config.signer_key_ids(), - raw_config.network_private_key()?, - raw_config.http_relay_url.clone(), - )) - } -} - -#[cfg(test)] -mod test { - use super::{Config, Error, RawConfig, RawSigners}; - - #[test] - fn try_from_raw_config_test() { - let mut raw_config = RawConfig::default(); - - // Should fail with the default config (require valid private and public keys...) - assert!(matches!( - Config::try_from(&raw_config), - Err(Error::InvalidPublicKey(_)) - )); - - raw_config.coordinator_public_key = - "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj".to_string(); - assert!(matches!( - Config::try_from(&raw_config), - Err(Error::InvalidPrivateKey(_)) - )); - - raw_config.network_private_key = "9aSCCR6eirt1NAHwJtSz4HMwBHTyMo62SyPMvVDt5DQn".to_string(); - assert!(Config::try_from(&raw_config).is_ok()); - } - - #[test] - fn coordinator_public_key_test() { - let mut config = RawConfig::default(); - // Should fail with an empty public key - assert!(matches!( - config.coordinator_public_key(), - Err(Error::InvalidPublicKey(_)) - )); - // Should fail with an invalid public key - config.coordinator_public_key = "Invalid Public Key".to_string(); - assert!(matches!( - config.coordinator_public_key(), - Err(Error::InvalidPublicKey(_)) - )); - // Should succeed with a valid public key - config.coordinator_public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj".to_string(); - assert!(config.coordinator_public_key().is_ok()); - } - - #[test] - fn public_keys_test() { - let mut config = RawConfig::default(); - let public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj".to_string(); - // Should succeed with an empty vector - let public_keys = config.public_keys().unwrap(); - assert!(public_keys.key_ids.is_empty()); - assert!(public_keys.signers.is_empty()); - - // Should fail with an empty public key - let raw_signer_keys = RawSigners { - key_ids: vec![1, 2], - public_key: "".to_string(), - }; - config.signers = vec![raw_signer_keys]; - assert!(matches!( - config.public_keys(), - Err(Error::InvalidPublicKey(_)) - )); - - // Should fail with an invalid public key - let raw_signer_keys = RawSigners { - key_ids: vec![1, 2], - public_key: "Invalid public key".to_string(), - }; - config.signers = vec![raw_signer_keys]; - assert!(matches!( - config.public_keys(), - Err(Error::InvalidPublicKey(_)) - )); - - // Should fail with an invalid key ID - let raw_signer_keys = RawSigners { - key_ids: vec![0, 1], - public_key: public_key.clone(), - }; - config.signers = vec![raw_signer_keys]; - assert!(matches!(config.public_keys(), Err(Error::InvalidKeyID))); - - // Should succeed with a valid public keys - let raw_signer_keys1 = RawSigners { - key_ids: vec![1, 2], - public_key: public_key.clone(), - }; - let raw_signer_keys2 = RawSigners { - key_ids: vec![3, 4], - public_key, - }; - config.signers = vec![raw_signer_keys1, raw_signer_keys2]; - let public_keys = config.public_keys().unwrap(); - assert_eq!(public_keys.signers.len(), 2); - assert_eq!(public_keys.key_ids.len(), 4); - } -} diff --git a/frost-signer/src/signing_round.rs b/frost-signer/src/signing_round.rs deleted file mode 100644 index b9d11254..00000000 --- a/frost-signer/src/signing_round.rs +++ /dev/null @@ -1,863 +0,0 @@ -use hashbrown::{HashMap, HashSet}; -use p256k1::{ - ecdsa, - point::{Compressed, Point}, - scalar::Scalar, -}; -use rand_core::{CryptoRng, OsRng, RngCore}; -use serde::{Deserialize, Serialize}; -use sha2::{Digest, Sha256}; -use std::collections::BTreeMap; -use tracing::{debug, info, warn}; -pub use wsts; -use wsts::{ - common::{PolyCommitment, PublicNonce, SignatureShare}, - traits::Signer as SignerTrait, - v1, -}; - -use crate::{ - config::PublicKeys, - signer::Signer as FrostSigner, - state_machine::{Error as StateMachineError, StateMachine, States}, - util::{decrypt, encrypt, make_shared_secret}, -}; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("InvalidPartyID")] - InvalidPartyID, - #[error("InvalidDkgPublicShare")] - InvalidDkgPublicShare, - #[error("InvalidDkgPrivateShares")] - InvalidDkgPrivateShares(Vec), - #[error("InvalidNonceResponse")] - InvalidNonceResponse, - #[error("InvalidSignatureShare")] - InvalidSignatureShare, - #[error("State Machine Error: {0}")] - StateMachineError(#[from] StateMachineError), -} - -pub trait Signable { - fn hash(&self, hasher: &mut Sha256); - - fn sign(&self, private_key: &Scalar) -> Result, ecdsa::Error> { - let mut hasher = Sha256::new(); - - self.hash(&mut hasher); - - let hash = hasher.finalize(); - match ecdsa::Signature::new(hash.as_slice(), private_key) { - Ok(sig) => Ok(sig.to_bytes().to_vec()), - Err(e) => Err(e), - } - } - - fn verify(&self, signature: &[u8], public_key: &ecdsa::PublicKey) -> bool { - let mut hasher = Sha256::new(); - - self.hash(&mut hasher); - - let hash = hasher.finalize(); - let sig = match ecdsa::Signature::try_from(signature) { - Ok(sig) => sig, - Err(_) => return false, - }; - - sig.verify(hash.as_slice(), public_key) - } -} - -pub struct SigningRound { - pub dkg_id: u64, - pub dkg_public_id: u64, - pub sign_id: u64, - pub sign_nonce_id: u64, - pub threshold: u32, - pub total_signers: u32, - pub total_keys: u32, - pub signer: Signer, - pub state: States, - pub commitments: BTreeMap, - pub shares: HashMap>>, - pub public_nonces: Vec, - pub network_private_key: Scalar, - pub public_keys: PublicKeys, -} - -pub struct Signer { - pub frost_signer: wsts::v1::Signer, - pub signer_id: u32, -} - -impl StateMachine for SigningRound { - fn move_to(&mut self, state: States) -> Result<(), StateMachineError> { - self.can_move_to(&state)?; - self.state = state; - Ok(()) - } - - fn can_move_to(&self, state: &States) -> Result<(), StateMachineError> { - let prev_state = &self.state; - let accepted = match state { - States::Idle => true, - States::DkgPublicDistribute => { - prev_state == &States::Idle - || prev_state == &States::DkgPublicGather - || prev_state == &States::DkgPrivateDistribute - } - States::DkgPublicGather => prev_state == &States::DkgPublicDistribute, - States::DkgPrivateDistribute => prev_state == &States::DkgPublicGather, - States::DkgPrivateGather => prev_state == &States::DkgPrivateDistribute, - States::SignGather => prev_state == &States::Idle, - States::Signed => prev_state == &States::SignGather, - }; - if accepted { - info!("state change from {:?} to {:?}", prev_state, state); - Ok(()) - } else { - Err(StateMachineError::BadStateChange(format!( - "{:?} to {:?}", - prev_state, state - ))) - } - } -} - -#[derive(Clone, Serialize, Deserialize, Debug)] -pub enum DkgStatus { - Success, - Failure(String), -} - -#[derive(Clone, Serialize, Deserialize, Debug)] -pub enum MessageTypes { - DkgBegin(DkgBegin), - DkgPrivateBegin(DkgBegin), - DkgEnd(DkgEnd), - DkgPublicEnd(DkgEnd), - DkgPublicShare(DkgPublicShare), - DkgPrivateShares(DkgPrivateShares), - NonceRequest(NonceRequest), - NonceResponse(NonceResponse), - SignShareRequest(SignatureShareRequest), - SignShareResponse(SignatureShareResponse), -} - -#[derive(Clone, Serialize, Deserialize, Debug)] -pub struct DkgPublicShare { - pub dkg_id: u64, - pub dkg_public_id: u64, - pub party_id: u32, - pub public_share: PolyCommitment, -} - -impl Signable for DkgPublicShare { - fn hash(&self, hasher: &mut Sha256) { - hasher.update("DKG_PUBLIC_SHARE".as_bytes()); - hasher.update(self.dkg_id.to_be_bytes()); - hasher.update(self.dkg_public_id.to_be_bytes()); - hasher.update(self.party_id.to_be_bytes()); - for a in &self.public_share.A { - hasher.update(a.compress().as_bytes()); - } - } -} - -#[derive(Clone, Serialize, Deserialize, Debug)] -pub struct DkgPrivateShares { - pub dkg_id: u64, - pub key_id: u32, - /// Encrypt the shares using AES-GCM with a key derived from ECDH - pub private_shares: HashMap>, -} - -impl Signable for DkgPrivateShares { - fn hash(&self, hasher: &mut Sha256) { - hasher.update("DKG_PRIVATE_SHARES".as_bytes()); - hasher.update(self.dkg_id.to_be_bytes()); - hasher.update(self.key_id.to_be_bytes()); - // make sure we iterate sequentially - // TODO: change this once WSTS goes to 1 based indexing for key_ids, or change to BTreeMap - for id in 0..self.private_shares.len() as u32 { - hasher.update(id.to_be_bytes()); - hasher.update(&self.private_shares[&id]); - } - } -} - -#[derive(Clone, Serialize, Deserialize, Debug)] -pub struct DkgBegin { - pub dkg_id: u64, //TODO: Strong typing for this, alternatively introduce a type alias -} - -impl Signable for DkgBegin { - fn hash(&self, hasher: &mut Sha256) { - hasher.update("DKG_BEGIN".as_bytes()); - hasher.update(self.dkg_id.to_be_bytes()); - } -} - -#[derive(Clone, Serialize, Deserialize, Debug)] -pub struct DkgEnd { - pub dkg_id: u64, - pub signer_id: u32, - pub status: DkgStatus, -} - -impl Signable for DkgEnd { - fn hash(&self, hasher: &mut Sha256) { - hasher.update("DKG_END".as_bytes()); - hasher.update(self.dkg_id.to_be_bytes()); - hasher.update(self.signer_id.to_be_bytes()); - } -} - -#[derive(Clone, Serialize, Deserialize, Debug)] -pub struct NonceRequest { - pub dkg_id: u64, - pub sign_id: u64, - pub sign_nonce_id: u64, -} - -impl Signable for NonceRequest { - fn hash(&self, hasher: &mut Sha256) { - hasher.update("NONCE_REQUEST".as_bytes()); - hasher.update(self.dkg_id.to_be_bytes()); - hasher.update(self.sign_id.to_be_bytes()); - hasher.update(self.sign_nonce_id.to_be_bytes()); - } -} - -#[derive(Clone, Serialize, Deserialize, Debug)] -pub struct NonceResponse { - pub dkg_id: u64, - pub sign_id: u64, - pub sign_nonce_id: u64, - pub signer_id: u32, - pub key_ids: Vec, - pub nonces: Vec, -} - -impl Signable for NonceResponse { - fn hash(&self, hasher: &mut Sha256) { - hasher.update("NONCE_RESPONSE".as_bytes()); - hasher.update(self.dkg_id.to_be_bytes()); - hasher.update(self.sign_id.to_be_bytes()); - hasher.update(self.sign_nonce_id.to_be_bytes()); - hasher.update(self.signer_id.to_be_bytes()); - - for key_id in &self.key_ids { - hasher.update(key_id.to_be_bytes()); - } - - for nonce in &self.nonces { - hasher.update(nonce.D.compress().as_bytes()); - hasher.update(nonce.E.compress().as_bytes()); - } - } -} - -#[derive(Clone, Serialize, Deserialize, Debug)] -pub struct SignatureShareRequest { - pub dkg_id: u64, - pub sign_id: u64, - pub correlation_id: u64, - pub nonce_responses: Vec, - pub message: Vec, -} - -impl Signable for SignatureShareRequest { - fn hash(&self, hasher: &mut Sha256) { - hasher.update("SIGNATURE_SHARE_REQUEST".as_bytes()); - hasher.update(self.dkg_id.to_be_bytes()); - hasher.update(self.sign_id.to_be_bytes()); - hasher.update(self.correlation_id.to_be_bytes()); - - for nonce_response in &self.nonce_responses { - nonce_response.hash(hasher); - } - - hasher.update(self.message.as_slice()); - } -} - -#[derive(Clone, Serialize, Deserialize, Debug)] -pub struct SignatureShareResponse { - pub dkg_id: u64, - pub sign_id: u64, - pub correlation_id: u64, - pub signer_id: u32, - pub signature_shares: Vec, -} - -impl Signable for SignatureShareResponse { - fn hash(&self, hasher: &mut Sha256) { - hasher.update("SIGNATURE_SHARE_RESPONSE".as_bytes()); - hasher.update(self.dkg_id.to_be_bytes()); - hasher.update(self.sign_id.to_be_bytes()); - hasher.update(self.correlation_id.to_be_bytes()); - hasher.update(self.signer_id.to_be_bytes()); - - for signature_share in &self.signature_shares { - hasher.update(signature_share.id.to_be_bytes()); - hasher.update(signature_share.z_i.to_bytes()); - } - } -} - -impl SigningRound { - pub fn new( - threshold: u32, - total_signers: u32, - total_keys: u32, - signer_id: u32, - key_ids: Vec, - network_private_key: Scalar, - public_keys: PublicKeys, - ) -> SigningRound { - assert!(threshold <= total_keys); - let mut rng = OsRng; - let frost_signer = v1::Signer::new(signer_id, &key_ids, total_keys, threshold, &mut rng); - let signer = Signer { - frost_signer, - signer_id, - }; - - SigningRound { - dkg_id: 0, - dkg_public_id: 0, - sign_id: 1, - sign_nonce_id: 1, - threshold, - total_signers, - total_keys, - signer, - state: States::Idle, - commitments: BTreeMap::new(), - shares: HashMap::new(), - public_nonces: vec![], - network_private_key, - public_keys, - } - } - - fn reset(&mut self, dkg_id: u64, rng: &mut T) { - self.dkg_id = dkg_id; - self.dkg_public_id = 0; - self.commitments.clear(); - self.shares.clear(); - self.public_nonces.clear(); - self.signer.frost_signer.reset_polys(rng); - } - - pub fn process(&mut self, message: MessageTypes) -> Result, Error> { - let out_msgs = match message { - MessageTypes::DkgBegin(dkg_begin) => self.dkg_begin(dkg_begin), - MessageTypes::DkgPrivateBegin(_) => self.dkg_private_begin(), - MessageTypes::DkgPublicShare(dkg_public_shares) => { - self.dkg_public_share(dkg_public_shares) - } - MessageTypes::DkgPrivateShares(dkg_private_shares) => { - self.dkg_private_shares(dkg_private_shares) - } - MessageTypes::SignShareRequest(sign_share_request) => { - self.sign_share_request(sign_share_request) - } - MessageTypes::NonceRequest(nonce_request) => self.nonce_request(nonce_request), - _ => Ok(vec![]), // TODO - }; - - match out_msgs { - Ok(mut out) => { - if self.public_shares_done() { - debug!( - "public_shares_done==true. commitments {}", - self.commitments.len() - ); - let dkg_end_msgs = self.dkg_public_ended()?; - out.push(dkg_end_msgs); - self.move_to(States::DkgPrivateDistribute)?; - } else if self.can_dkg_end() { - debug!( - "can_dkg_end==true. shares {} commitments {}", - self.shares.len(), - self.commitments.len() - ); - let dkg_end_msgs = self.dkg_ended()?; - out.push(dkg_end_msgs); - self.move_to(States::Idle)?; - } - Ok(out) - } - Err(e) => Err(e), - } - } - - fn dkg_public_ended(&mut self) -> Result { - let dkg_end = DkgEnd { - dkg_id: self.dkg_id, - signer_id: self.signer.signer_id, - status: DkgStatus::Success, - }; - let dkg_end = MessageTypes::DkgPublicEnd(dkg_end); - info!( - "DKG_END round #{} signer_id {}", - self.dkg_id, self.signer.signer_id - ); - Ok(dkg_end) - } - - fn dkg_ended(&mut self) -> Result { - let polys: Vec = self.commitments.clone().into_values().collect(); - - let mut decrypted_shares = HashMap::new(); - - // go through private shares, and decrypt any for owned keys, leaving the rest as zero scalars - let key_ids: HashSet = self.signer.frost_signer.get_key_ids().into_iter().collect(); - let mut invalid_dkg_private_shares = Vec::new(); - - for (src_key_id, encrypted_shares) in &self.shares { - let mut decrypted_key_shares = HashMap::new(); - - for (dst_key_id, private_share) in encrypted_shares { - if key_ids.contains(dst_key_id) { - debug!( - "decrypting dkg private share for key_id #{}", - dst_key_id + 1 - ); - let compressed = - Compressed::from(self.public_keys.key_ids[&(src_key_id + 1)].to_bytes()); - let src_public_key = Point::try_from(&compressed).unwrap(); - let shared_secret = - make_shared_secret(&self.network_private_key, &src_public_key); - - match decrypt(&shared_secret, private_share) { - Ok(plain) => match Scalar::try_from(&plain[..]) { - Ok(s) => { - decrypted_key_shares.insert(*dst_key_id, s); - } - Err(e) => { - warn!("Failed to parse Scalar for dkg private share from key_id {} to key_id {}: {:?}", src_key_id, dst_key_id, e); - invalid_dkg_private_shares.push(*src_key_id); - } - }, - Err(e) => { - warn!("Failed to decrypt dkg private share from key_id {} to key_id {}: {:?}", src_key_id, dst_key_id, e); - invalid_dkg_private_shares.push(*src_key_id); - } - } - } else { - decrypted_key_shares.insert(*dst_key_id, Scalar::new()); - } - } - - decrypted_shares.insert(*src_key_id, decrypted_key_shares); - } - - let dkg_end = if invalid_dkg_private_shares.is_empty() { - match self - .signer - .frost_signer - .compute_secrets(&decrypted_shares, &polys) - { - Ok(()) => DkgEnd { - dkg_id: self.dkg_id, - signer_id: self.signer.signer_id, - status: DkgStatus::Success, - }, - Err(dkg_error_map) => DkgEnd { - dkg_id: self.dkg_id, - signer_id: self.signer.signer_id, - status: DkgStatus::Failure(format!("{:?}", dkg_error_map)), - }, - } - } else { - DkgEnd { - dkg_id: self.dkg_id, - signer_id: self.signer.signer_id, - status: DkgStatus::Failure(format!("{:?}", invalid_dkg_private_shares)), - } - }; - - let dkg_end = MessageTypes::DkgEnd(dkg_end); - info!( - "DKG_END round #{} signer_id {}", - self.dkg_id, self.signer.signer_id - ); - Ok(dkg_end) - } - - fn public_shares_done(&self) -> bool { - debug!( - "public_shares_done state {:?} commitments {}", - self.state, - self.commitments.len(), - ); - self.state == States::DkgPublicGather - && self.commitments.len() == usize::try_from(self.total_keys).unwrap() - } - - fn can_dkg_end(&self) -> bool { - debug!( - "can_dkg_end state {:?} commitments {} shares {}", - self.state, - self.commitments.len(), - self.shares.len() - ); - self.state == States::DkgPrivateGather - && self.commitments.len() == usize::try_from(self.total_keys).unwrap() - && self.shares.len() == usize::try_from(self.total_keys).unwrap() - } - - fn nonce_request(&mut self, nonce_request: NonceRequest) -> Result, Error> { - let mut rng = OsRng; - let mut msgs = vec![]; - let signer_id = self.signer.signer_id; - let key_ids = self.signer.frost_signer.get_key_ids(); - let nonces = self.signer.frost_signer.gen_nonces(&mut rng); - - let response = NonceResponse { - dkg_id: nonce_request.dkg_id, - sign_id: nonce_request.sign_id, - sign_nonce_id: nonce_request.sign_nonce_id, - signer_id, - key_ids, - nonces, - }; - - let response = MessageTypes::NonceResponse(response); - - info!( - "nonce request with dkg_id {:?}. response sent from signer_id {}", - nonce_request.dkg_id, signer_id - ); - msgs.push(response); - - Ok(msgs) - } - - fn sign_share_request( - &mut self, - sign_request: SignatureShareRequest, - ) -> Result, Error> { - let mut msgs = vec![]; - - let signer_ids = sign_request - .nonce_responses - .iter() - .map(|nr| nr.signer_id) - .collect::>(); - - info!("Got SignatureShareRequest for signer_ids {:?}", signer_ids); - - for signer_id in &signer_ids { - if *signer_id == self.signer.signer_id { - let key_ids: Vec = sign_request - .nonce_responses - .iter() - .flat_map(|nr| nr.key_ids.iter().copied()) - .collect::>(); - let nonces = sign_request - .nonce_responses - .iter() - .flat_map(|nr| nr.nonces.clone()) - .collect::>(); - let signature_shares = self.signer.frost_signer.sign( - &sign_request.message, - &signer_ids, - &key_ids, - &nonces, - ); - - let response = SignatureShareResponse { - dkg_id: sign_request.dkg_id, - sign_id: sign_request.sign_id, - correlation_id: sign_request.correlation_id, - signer_id: *signer_id, - signature_shares, - }; - - info!( - "Sending SignatureShareResponse for signer_id {:?}", - signer_id - ); - - let response = MessageTypes::SignShareResponse(response); - - msgs.push(response); - } else { - debug!("SignShareRequest for {} dropped.", signer_id); - } - } - Ok(msgs) - } - - fn dkg_begin(&mut self, dkg_begin: DkgBegin) -> Result, Error> { - let mut rng = OsRng; - - self.reset(dkg_begin.dkg_id, &mut rng); - self.move_to(States::DkgPublicDistribute)?; - - let _party_state = self.signer.frost_signer.save(); - - self.dkg_public_begin() - } - - fn dkg_public_begin(&mut self) -> Result, Error> { - let mut rng = OsRng; - let mut msgs = vec![]; - let polys = self.signer.frost_signer.get_poly_commitments(&mut rng); - - info!( - "sending DkgPublicShares for round #{}, {} poly commitments for signer #{}", - self.dkg_id, - polys.len(), - self.signer.frost_signer.get_id(), - ); - - for poly in &polys { - let public_share = DkgPublicShare { - dkg_id: self.dkg_id, - dkg_public_id: self.dkg_public_id, - party_id: poly.id.id.get_u32(), - public_share: poly.clone(), - }; - - let public_share = MessageTypes::DkgPublicShare(public_share); - msgs.push(public_share); - } - - self.move_to(States::DkgPublicGather)?; - Ok(msgs) - } - - fn dkg_private_begin(&mut self) -> Result, Error> { - let mut rng = OsRng; - let mut msgs = vec![]; - for (key_id, private_shares) in &self.signer.frost_signer.get_shares() { - info!( - "signer {} sending dkg private share for key_id #{}", - self.signer.signer_id, key_id - ); - // encrypt each share for the recipient - let mut encrypted_shares = HashMap::new(); - - for (dst_key_id, private_share) in private_shares { - debug!( - "encrypting dkg private share for key_id #{}", - dst_key_id + 1 - ); - let compressed = - Compressed::from(self.public_keys.key_ids[&(dst_key_id + 1)].to_bytes()); - let dst_public_key = Point::try_from(&compressed).unwrap(); - let shared_secret = make_shared_secret(&self.network_private_key, &dst_public_key); - let encrypted_share = - encrypt(&shared_secret, &private_share.to_bytes(), &mut rng).unwrap(); - - encrypted_shares.insert(*dst_key_id, encrypted_share); - } - - let private_shares = DkgPrivateShares { - dkg_id: self.dkg_id, - key_id: *key_id, - private_shares: encrypted_shares, - }; - - let private_shares = MessageTypes::DkgPrivateShares(private_shares); - msgs.push(private_shares); - } - - self.move_to(States::DkgPrivateGather)?; - Ok(msgs) - } - - fn dkg_public_share( - &mut self, - dkg_public_share: DkgPublicShare, - ) -> Result, Error> { - self.commitments - .insert(dkg_public_share.party_id, dkg_public_share.public_share); - info!( - "received DkgPublicShare from key #{} {}/{}", - dkg_public_share.party_id, - self.commitments.len(), - self.total_keys - ); - Ok(vec![]) - } - - fn dkg_private_shares( - &mut self, - dkg_private_shares: DkgPrivateShares, - ) -> Result, Error> { - let shares_clone = dkg_private_shares.private_shares.clone(); - self.shares - .insert(dkg_private_shares.key_id, dkg_private_shares.private_shares); - info!( - "received DkgPrivateShares from key #{} {}/{} {:?}", - dkg_private_shares.key_id, - self.shares.len(), - self.total_keys, - shares_clone.keys(), - ); - Ok(vec![]) - } -} - -impl From<&FrostSigner> for SigningRound { - fn from(signer: &FrostSigner) -> Self { - let signer_id = signer.signer_id; - assert!(signer_id > 0 && signer_id <= signer.config.total_signers); - let key_ids = signer.config.signer_key_ids[&signer_id] - .iter() - .map(|i| i - 1) - .collect::>(); - - assert!(signer.config.keys_threshold <= signer.config.total_keys); - let mut rng = OsRng; - let frost_signer = v1::Signer::new( - signer_id, - &key_ids, - signer.config.total_keys, - signer.config.keys_threshold, - &mut rng, - ); - - let network_private_key = signer.config.network_private_key; - let public_keys = signer.config.public_keys.clone(); - - SigningRound { - dkg_id: 1, - dkg_public_id: 1, - sign_id: 1, - sign_nonce_id: 1, - threshold: signer.config.keys_threshold, - total_keys: signer.config.total_keys, - total_signers: signer.config.total_signers, - signer: Signer { - frost_signer, - signer_id, - }, - state: States::Idle, - commitments: BTreeMap::new(), - shares: HashMap::new(), - public_nonces: vec![], - network_private_key, - public_keys, - } - } -} - -#[cfg(test)] -mod test { - use hashbrown::HashMap; - use rand_core::{CryptoRng, OsRng, RngCore}; - use wsts::{common::PolyCommitment, schnorr::ID, Scalar}; - - use crate::signing_round::{ - DkgPrivateShares, DkgPublicShare, DkgStatus, MessageTypes, SigningRound, - }; - use crate::state_machine::States; - - fn get_rng() -> impl RngCore + CryptoRng { - let rnd = OsRng; - //rand::rngs::StdRng::seed_from_u64(rnd.next_u64()) // todo: fix trait `rand_core::RngCore` is not implemented for `StdRng` - rnd - } - - #[test] - fn dkg_public_share() { - let mut rnd = get_rng(); - let mut signing_round = - SigningRound::new(1, 1, 1, 1, vec![1], Default::default(), Default::default()); - let public_share = DkgPublicShare { - dkg_id: 0, - party_id: 0, - public_share: PolyCommitment { - id: ID::new(&Scalar::new(), &Scalar::new(), &mut rnd), - A: vec![], - }, - dkg_public_id: 0, - }; - signing_round.dkg_public_share(public_share).unwrap(); - assert_eq!(1, signing_round.commitments.len()) - } - - #[test] - fn dkg_private_shares() { - let mut signing_round = - SigningRound::new(1, 1, 1, 1, vec![1], Default::default(), Default::default()); - let mut private_shares = DkgPrivateShares { - dkg_id: 0, - key_id: 0, - private_shares: HashMap::new(), - }; - private_shares.private_shares.insert(1, Vec::new()); - signing_round.dkg_private_shares(private_shares).unwrap(); - assert_eq!(1, signing_round.shares.len()) - } - - #[test] - fn public_shares_done() { - let mut rnd = get_rng(); - let mut signing_round = - SigningRound::new(1, 1, 1, 1, vec![1], Default::default(), Default::default()); - // publich_shares_done starts out as false - assert_eq!(false, signing_round.public_shares_done()); - - // meet the conditions for all public keys received - signing_round.state = States::DkgPublicGather; - signing_round.commitments.insert( - 1, - PolyCommitment { - id: ID::new(&Scalar::new(), &Scalar::new(), &mut rnd), - A: vec![], - }, - ); - - // public_shares_done should be true - assert!(signing_round.public_shares_done()); - } - - #[test] - fn can_dkg_end() { - let mut rnd = get_rng(); - let mut signing_round = - SigningRound::new(1, 1, 1, 1, vec![1], Default::default(), Default::default()); - // can_dkg_end starts out as false - assert_eq!(false, signing_round.can_dkg_end()); - - // meet the conditions for DKG_END - signing_round.state = States::DkgPrivateGather; - signing_round.commitments.insert( - 1, - PolyCommitment { - id: ID::new(&Scalar::new(), &Scalar::new(), &mut rnd), - A: vec![], - }, - ); - let shares: HashMap> = HashMap::new(); - signing_round.shares.insert(1, shares); - - // can_dkg_end should be true - assert!(signing_round.can_dkg_end()); - } - - #[test] - fn dkg_ended() { - let mut signing_round = - SigningRound::new(1, 1, 1, 1, vec![1], Default::default(), Default::default()); - match signing_round.dkg_ended() { - Ok(dkg_end) => match dkg_end { - MessageTypes::DkgEnd(dkg_end) => match dkg_end.status { - DkgStatus::Failure(_) => assert!(true), - _ => assert!(false), - }, - _ => assert!(false), - }, - _ => assert!(false), - } - } -} diff --git a/frost-test/tests/pure_frost.rs b/frost-test/tests/pure_frost.rs deleted file mode 100644 index d8027b69..00000000 --- a/frost-test/tests/pure_frost.rs +++ /dev/null @@ -1,37 +0,0 @@ -use rand_core::OsRng; -use wsts::taproot::test_helpers::{dkg, sign}; -use wsts::taproot::SchnorrProof; -use wsts::v1::{self, SignatureAggregator}; - -#[test] -#[allow(non_snake_case)] -fn pure_frost_test() { - let T = 3; - let N = 4; - let mut rng = OsRng; - let mut signers = [ - v1::Signer::new(1, &[0, 1], N, T, &mut rng), - v1::Signer::new(2, &[2], N, T, &mut rng), - v1::Signer::new(3, &[3], N, T, &mut rng), - ]; - - // DKG (Distributed Key Generation) - let A = dkg(&mut signers[..], &mut rng, None).unwrap(); - - // signing. Signers: 0 (parties: 0, 1) and 1 (parties: 2) - let result = { - // decide which signers will be used - let mut signers = [signers[0].clone(), signers[1].clone()]; - - const MSG: &[u8] = "It was many and many a year ago".as_bytes(); - - // get nonces and shares - let (nonces, shares) = sign(MSG, &mut signers, &mut rng, None); - - SignatureAggregator::new(N, T, A.clone()) - .unwrap() - .sign_taproot(&MSG, &nonces, &shares, None) - }; - - assert!(SchnorrProof::new(&result.unwrap()).is_ok()); -} diff --git a/sbtc-cli/Cargo.toml b/sbtc-cli/Cargo.toml deleted file mode 100644 index 641b95e3..00000000 --- a/sbtc-cli/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "sbtc-cli" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -anyhow.workspace = true -array-bytes.workspace = true -bdk = { workspace = true, features = ["keys-bip39"] } -bitcoin.workspace = true -blockstack-core.workspace = true -clap.workspace = true -serde.workspace = true -serde_json.workspace = true -sha256.workspace = true -regex.workspace = true - -[[bin]] -name = "sbtc" -path = "src/main.rs" diff --git a/sbtc-cli/README.md b/sbtc-cli/README.md deleted file mode 100644 index c9ba0feb..00000000 --- a/sbtc-cli/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# sBTC CLI -The sBTC CLI allows creating and broadcasting sBTC transactions from the command line. The purpose is to aid experimenting with sBTC and testing it. The resulting transactions can be broadcasted to Bitcoin, or kept in hex format as test vectors. - -# Installation -From the root of the `core-eng` repo, run -``` -cargo install --path sbtc-cli -``` - -# Usage example -Create a deposit transaction -``` -sbtc deposit --wif --recipient ST3RBZ4TZ3EK22SZRKGFZYBCKD7WQ5B8FFRS57TT6 --amount 13370 --dkg-wallet tb1pewpc7x6nnea8clm2vn2d8xvpdwvkhucmfdwmm0p6vk2u5xgmwlzsdx3g6w -``` - -Create a withdrawal transaction -``` -sbtc withdraw --wif --recipient tb1q0jtfel9tp54dzud28uspe994rv8gajnxc85n8q --amount 42 --dkg-wallet tb1pewpc7x6nnea8clm2vn2d8xvpdwvkhucmfdwmm0p6vk2u5xgmwlzsdx3g6w --fulfillment-fee 1000 -``` - -Broadcast a transaction -``` -sbtc broadcast 01000000000101fb27b9579035b82d145b09f3e7e9d02f4ae077a5b3b3fc3356945bb3a3e411650200000000feffffff0300000000000000001a6a1854323c1a755e17b35c75fb5534190b26228187f05781b2823b05000000000000225120cb838f1b539e7a7c7f6a64d4d399816b996bf31b4b5dbdbc3a6595ca191b77c551401100000000001600147c969cfcab0d2ad171aa3f201c94b51b0e8eca6602473044022023371322ebc0311983374c7db5e1eeb2ecb40955c3917e71c3dd75b5e5a364fe02203641377a086795bf816d2b57c4682410cb2cc7bf21987853e6b7030c8a50b44501210215bd6d522931e602fde924571eb472bc1db953484b29ba6542774ebbf083412337322500 -``` - - -# Functionality -This list outlines supported and planned functoinality for the CLI. - -- Creating OP_RETURN transactions - - [X] deposit - - [X] withdrawal - - [ ] wallet handoff -- Creating OP_DROP transactions - - [ ] deposit - - [ ] withdrawal - - [ ] wallet handoff -- [X] Broadcast transactions diff --git a/sbtc-cli/src/commands/broadcast.rs b/sbtc-cli/src/commands/broadcast.rs deleted file mode 100644 index d770d87f..00000000 --- a/sbtc-cli/src/commands/broadcast.rs +++ /dev/null @@ -1,38 +0,0 @@ -use std::io::stdout; - -use anyhow::anyhow; -use bdk::blockchain::Blockchain; -use bitcoin::{ - psbt::serialize::{Deserialize, Serialize}, - Network, Transaction, -}; -use clap::Parser; - -use crate::commands::utils; - -#[derive(Parser, Debug, Clone)] -pub struct BroadcastArgs { - /// The network to broadcast to - #[clap(short, long, default_value_t = Network::Testnet)] - network: Network, - /// The transaction to broadcast - tx: String, -} - -pub fn broadcast_tx(broadcast: &BroadcastArgs) -> anyhow::Result<()> { - let blockchain = utils::init_blockchain()?; - let tx = Transaction::deserialize( - &array_bytes::hex2bytes(&broadcast.tx).map_err(|e| anyhow!("{:?}", e))?, - )?; - blockchain.broadcast(&tx)?; - - serde_json::to_writer_pretty( - stdout(), - &utils::TransactionData { - tx_id: tx.txid().to_string(), - tx_hex: array_bytes::bytes2hex("", tx.serialize()), - }, - )?; - - Ok(()) -} diff --git a/sbtc-cli/src/commands/deposit.rs b/sbtc-cli/src/commands/deposit.rs deleted file mode 100644 index 8632c4da..00000000 --- a/sbtc-cli/src/commands/deposit.rs +++ /dev/null @@ -1,103 +0,0 @@ -use std::{io::stdout, iter::once, str::FromStr}; - -use anyhow::anyhow; -use bdk::{database::MemoryDatabase, SignOptions, Wallet}; -use bitcoin::{ - psbt::{serialize::Serialize, PartiallySignedTransaction}, - Address as BitcoinAddress, Network, PrivateKey, -}; -use blockstack_lib::types::{chainstate::StacksAddress, Address}; -use clap::Parser; - -use crate::commands::utils; - -#[derive(Parser, Debug, Clone)] -pub struct DepositArgs { - /// P2WPKH BTC private key in WIF format - #[clap(short, long)] - wif: String, - - /// Stacks address that will receive sBTC - #[clap(short, long)] - recipient: String, - - /// The amount of sats to send - #[clap(short, long)] - amount: u64, - - /// Dkg wallet address - #[clap(short, long)] - dkg_wallet: String, -} - -pub fn build_deposit_tx(deposit: &DepositArgs) -> anyhow::Result<()> { - let private_key = PrivateKey::from_wif(&deposit.wif)?; - let wallet = utils::setup_wallet(private_key)?; - let recipient = StacksAddress::from_string(&deposit.recipient) - .ok_or(anyhow::anyhow!("Could not parse recipient Stacks address"))?; - let dkg_address = BitcoinAddress::from_str(&deposit.dkg_wallet)?; - - let mut psbt = deposit_psbt( - &wallet, - &recipient, - &dkg_address, - deposit.amount, - &private_key.network, - )?; - - wallet.sign(&mut psbt, SignOptions::default())?; - let tx = psbt.extract_tx(); - - serde_json::to_writer_pretty( - stdout(), - &utils::TransactionData { - tx_id: tx.txid().to_string(), - tx_hex: array_bytes::bytes2hex("", tx.serialize()), - }, - )?; - - Ok(()) -} - -fn deposit_psbt( - wallet: &Wallet, - recipient: &StacksAddress, - dkg_address: &BitcoinAddress, - amount: u64, - network: &Network, -) -> anyhow::Result { - let mut tx_builder = wallet.build_tx(); - - let op_return_script = utils::build_op_return_script(&deposit_data(recipient, network)); - let dkg_script = dkg_address.script_pubkey(); - let dust_amount = dkg_script.dust_value().to_sat(); - - if amount < dust_amount { - return Err(anyhow!( - "Provided amount {} is less than the dust amount: {}", - amount, - dust_amount - )); - } - - let outputs = [(op_return_script, 0), (dkg_script, amount)]; - - for (script, amount) in outputs.clone() { - tx_builder.add_recipient(script, amount); - } - - let (mut partial_tx, _) = tx_builder.finish()?; - - partial_tx.unsigned_tx.output = utils::reorder_outputs(partial_tx.unsigned_tx.output, outputs); - - Ok(partial_tx) -} - -fn deposit_data(recipient: &StacksAddress, network: &Network) -> Vec { - utils::magic_bytes(network) - .into_iter() - .chain(once(b'<')) - .chain(once(recipient.version)) - .chain(recipient.to_bytes()) - .collect() -} diff --git a/sbtc-cli/src/commands/generate.rs b/sbtc-cli/src/commands/generate.rs deleted file mode 100644 index 2aa4ded5..00000000 --- a/sbtc-cli/src/commands/generate.rs +++ /dev/null @@ -1,141 +0,0 @@ -use std::io::stdout; - -use anyhow::{anyhow, Context}; -use array_bytes::bytes2hex; -use bdk::{ - keys::{bip39::Mnemonic, DerivableKey, ExtendedKey}, - miniscript::BareCtx, -}; -use bitcoin::{ - schnorr::TweakedPublicKey, - secp256k1::{rand::random, Secp256k1}, - Address as BitcoinAddress, Network, PrivateKey, -}; -use blockstack_lib::{ - address::{C32_ADDRESS_VERSION_MAINNET_SINGLESIG, C32_ADDRESS_VERSION_TESTNET_SINGLESIG}, - types::chainstate::StacksAddress, - util::hash::Hash160, -}; -use clap::Parser; - -#[derive(Parser, Debug, Clone)] -pub struct GenerateArgs { - /// Specify how to generate the credentials - #[command(subcommand)] - subcommand: GenerateSubcommand, - /// The network to broadcast to - #[clap(short, long, default_value_t = Network::Testnet)] - network: Network, -} - -#[derive(clap::Subcommand, Debug, Clone)] -enum GenerateSubcommand { - New, - Wif { wif: String }, - PrivateKeyHex { private_key: String }, - Mnemonic { mnemonic: String }, -} - -#[derive(serde::Serialize, Debug, Clone)] -struct Credentials { - mnemonic: String, - wif: String, - private_key: String, - public_key: String, - stacks_address: String, - bitcoin_taproot_address_tweaked: String, - bitcoin_taproot_address_untweaked: String, - bitcoin_p2pkh_address: String, -} - -pub fn generate(generate_args: &GenerateArgs) -> anyhow::Result<()> { - let (private_key, maybe_mnemonic) = match &generate_args.subcommand { - GenerateSubcommand::New => { - let mnemonic = random_mnemonic()?; - ( - private_key_from_mnemonic(generate_args.network, mnemonic.clone())?, - Some(mnemonic), - ) - } - GenerateSubcommand::Wif { wif } => (private_key_from_wif(wif)?, None), - GenerateSubcommand::PrivateKeyHex { private_key } => ( - parse_private_key_from_hex(private_key, generate_args.network)?, - None, - ), - GenerateSubcommand::Mnemonic { mnemonic } => { - let mnemonic = Mnemonic::parse(mnemonic)?; - ( - private_key_from_mnemonic(generate_args.network, mnemonic.clone())?, - Some(mnemonic), - ) - } - }; - - let credentials = generate_credentials(&private_key, maybe_mnemonic)?; - - serde_json::to_writer_pretty(stdout(), &credentials)?; - - Ok(()) -} - -fn random_mnemonic() -> anyhow::Result { - let entropy: Vec = std::iter::from_fn(|| Some(random())).take(32).collect(); - Mnemonic::from_entropy(&entropy).context("Could not create mnemonic from entropy") -} - -fn private_key_from_wif(wif: &str) -> anyhow::Result { - Ok(PrivateKey::from_wif(wif)?) -} - -fn parse_private_key_from_hex(private_key: &str, network: Network) -> anyhow::Result { - let slice = array_bytes::hex2bytes(private_key) - .map_err(|_| anyhow::anyhow!("Failed to parse hex string: {}", private_key,))?; - Ok(PrivateKey::from_slice(&slice, network)?) -} - -fn private_key_from_mnemonic(network: Network, mnemonic: Mnemonic) -> anyhow::Result { - let extended_key: ExtendedKey = mnemonic.into_extended_key()?; - let private_key = extended_key - .into_xprv(network) - .ok_or(anyhow!("Could not create an extended private key"))?; - - Ok(private_key.to_priv()) -} - -fn generate_credentials( - private_key: &PrivateKey, - maybe_mnemonic: Option, -) -> anyhow::Result { - let secp = Secp256k1::new(); - let public_key = private_key.public_key(&secp); - - let stacks_address_version = match private_key.network { - Network::Testnet => C32_ADDRESS_VERSION_TESTNET_SINGLESIG, - Network::Bitcoin => C32_ADDRESS_VERSION_MAINNET_SINGLESIG, - _ => panic!("Not supported"), - }; - let public_key_hash = Hash160::from_vec(&public_key.pubkey_hash().as_hash().to_vec()).unwrap(); - let stacks_address = StacksAddress::new(stacks_address_version, public_key_hash); - let bitcoin_taproot_address_tweaked = - BitcoinAddress::p2tr(&secp, public_key.inner.into(), None, private_key.network).to_string(); - - let bitcoin_taproot_address_untweaked = BitcoinAddress::p2tr_tweaked( - TweakedPublicKey::dangerous_assume_tweaked(public_key.inner.into()), - private_key.network, - ) - .to_string(); - - Ok(Credentials { - mnemonic: maybe_mnemonic - .as_ref() - .map(ToString::to_string) - .unwrap_or_default(), - wif: private_key.to_wif(), - private_key: bytes2hex("0x", private_key.to_bytes()), - public_key: bytes2hex("0x", public_key.to_bytes()), - stacks_address: stacks_address.to_string(), - bitcoin_taproot_address_tweaked, - bitcoin_taproot_address_untweaked, - bitcoin_p2pkh_address: BitcoinAddress::p2pkh(&public_key, private_key.network).to_string(), - }) -} diff --git a/sbtc-cli/src/commands/mod.rs b/sbtc-cli/src/commands/mod.rs deleted file mode 100644 index 8dd728ba..00000000 --- a/sbtc-cli/src/commands/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod broadcast; -pub mod deposit; -pub mod generate; -pub mod utils; -pub mod withdraw; diff --git a/sbtc-cli/src/commands/utils.rs b/sbtc-cli/src/commands/utils.rs deleted file mode 100644 index fd597440..00000000 --- a/sbtc-cli/src/commands/utils.rs +++ /dev/null @@ -1,77 +0,0 @@ -use std::collections::{BTreeMap, HashMap}; - -use bdk::{ - blockchain::ElectrumBlockchain, database::MemoryDatabase, electrum_client::Client, - template::P2Wpkh, SyncOptions, Wallet, -}; -use bitcoin::{ - blockdata::{opcodes, script::Builder}, - Network, PrivateKey, Script, TxOut, -}; -use serde::Serialize; - -pub fn init_blockchain() -> anyhow::Result { - let client = Client::new("ssl://blockstream.info:993")?; - let blockchain = ElectrumBlockchain::from(client); - Ok(blockchain) -} - -pub fn setup_wallet(private_key: PrivateKey) -> anyhow::Result> { - let blockchain = init_blockchain()?; - let wallet = Wallet::new( - P2Wpkh(private_key), - Some(P2Wpkh(private_key)), - private_key.network, - MemoryDatabase::default(), - )?; - - wallet.sync(&blockchain, SyncOptions::default())?; - - Ok(wallet) -} - -pub fn build_op_return_script(data: &[u8]) -> Script { - Builder::new() - .push_opcode(opcodes::all::OP_RETURN) - .push_slice(data) - .into_script() -} - -pub fn reorder_outputs( - outputs: impl IntoIterator, - order: impl IntoIterator, -) -> Vec { - let indices: HashMap<(Script, u64), usize> = order - .into_iter() - .enumerate() - .map(|(idx, val)| (val, idx)) - .collect(); - - let outputs_ordered: BTreeMap = outputs - .into_iter() - .map(|txout| { - ( - *indices - .get(&(txout.script_pubkey.clone(), txout.value)) - .unwrap_or(&usize::MAX), // Change amount - txout, - ) - }) - .collect(); - - outputs_ordered.into_values().collect() -} - -pub fn magic_bytes(network: &Network) -> [u8; 2] { - match network { - Network::Bitcoin => [b'X', b'2'], - Network::Testnet => [b'T', b'2'], - _ => [b'i', b'd'], - } -} - -#[derive(Serialize)] -pub struct TransactionData { - pub tx_id: String, - pub tx_hex: String, -} diff --git a/sbtc-cli/src/commands/withdraw.rs b/sbtc-cli/src/commands/withdraw.rs deleted file mode 100644 index 740d64af..00000000 --- a/sbtc-cli/src/commands/withdraw.rs +++ /dev/null @@ -1,149 +0,0 @@ -use std::{io::stdout, iter::once, str::FromStr}; - -use anyhow::anyhow; -use bdk::{database::MemoryDatabase, SignOptions, Wallet}; -use bitcoin::{ - psbt::{serialize::Serialize, PartiallySignedTransaction}, - secp256k1::{Message, Secp256k1}, - Address as BitcoinAddress, Network, PrivateKey, -}; -use clap::Parser; - -use crate::commands::utils::TransactionData; -use crate::commands::utils::{build_op_return_script, magic_bytes, reorder_outputs, setup_wallet}; - -#[derive(Parser, Debug, Clone)] -pub struct WithdrawalArgs { - /// P2WPKH BTC private key in WIF format - #[clap(short, long)] - wif: String, - - /// P2WPKH sBTC sender private key in WIF format - #[clap(short, long)] - sender_wif: String, - - /// Bitcoin address that will receive BTC - #[clap(short, long)] - recipient: String, - - /// The amount of sats to send - #[clap(short, long)] - amount: u64, - - /// The amount of sats to send as the fulfillment fee - #[clap(short, long)] - fulfillment_fee: u64, - - /// Dkg wallet address - #[clap(short, long)] - dkg_wallet: String, -} - -pub fn build_withdrawal_tx(withdrawal: &WithdrawalArgs) -> anyhow::Result<()> { - let private_key = PrivateKey::from_wif(&withdrawal.wif)?; - - let wallet = setup_wallet(private_key)?; - - let sender_private_key = PrivateKey::from_wif(&withdrawal.sender_wif)?; - let recipient = BitcoinAddress::from_str(&withdrawal.recipient)?; - let dkg_address = BitcoinAddress::from_str(&withdrawal.dkg_wallet)?; - - let mut psbt = withdrawal_psbt( - &wallet, - &sender_private_key, - &recipient, - &dkg_address, - withdrawal.amount, - withdrawal.fulfillment_fee, - &private_key.network, - )?; - - wallet.sign(&mut psbt, SignOptions::default())?; - let tx = psbt.extract_tx(); - - serde_json::to_writer_pretty( - stdout(), - &TransactionData { - tx_id: tx.txid().to_string(), - tx_hex: array_bytes::bytes2hex("", tx.serialize()), - }, - )?; - - Ok(()) -} - -fn withdrawal_psbt( - wallet: &Wallet, - sender_private_key: &PrivateKey, - recipient: &BitcoinAddress, - dkg_address: &BitcoinAddress, - amount: u64, - fulfillment_fee: u64, - network: &Network, -) -> anyhow::Result { - let recipient_script = recipient.script_pubkey(); - let dkg_wallet_script = dkg_address.script_pubkey(); - - // Check that we have enough to cover dust - let recipient_dust_amount = recipient_script.dust_value().to_sat(); - let dkg_wallet_dust_amount = dkg_wallet_script.dust_value().to_sat(); - - if fulfillment_fee < dkg_wallet_dust_amount { - return Err(anyhow!( - "Provided fulfillment fee {} is less than the dust amount: {}", - fulfillment_fee, - dkg_wallet_dust_amount - )); - } - - let op_return_script = build_op_return_script(&withdrawal_data( - recipient, - amount, - sender_private_key, - network, - )); - - let mut tx_builder = wallet.build_tx(); - - let outputs = [ - (op_return_script, 0), - (recipient_script, recipient_dust_amount), - (dkg_wallet_script, fulfillment_fee), - ]; - - for (script, amount) in outputs.clone() { - tx_builder.add_recipient(script, amount); - } - - let (mut partial_tx, _) = tx_builder.finish()?; - - partial_tx.unsigned_tx.output = reorder_outputs(partial_tx.unsigned_tx.output, outputs); - - Ok(partial_tx) -} - -fn withdrawal_data( - recipient: &BitcoinAddress, - amount: u64, - sender_private_key: &PrivateKey, - network: &Network, -) -> Vec { - let mut msg = amount.to_be_bytes().to_vec(); - msg.extend_from_slice(recipient.script_pubkey().as_bytes()); - - let msg_hash = sha256::digest(msg.as_slice()); - let msg_hash_bytes = array_bytes::hex2bytes(msg_hash).unwrap(); - let msg_ecdsa = Message::from_slice(&msg_hash_bytes).unwrap(); - - let (recovery_id, signature) = Secp256k1::new() - .sign_ecdsa_recoverable(&msg_ecdsa, &sender_private_key.inner) - .serialize_compact(); - - magic_bytes(network) - .into_iter() - .chain(once(b'>')) - .chain(amount.to_be_bytes()) - .chain(once(recovery_id.to_i32() as u8)) - .chain(signature) - .collect() -} diff --git a/sbtc-cli/src/main.rs b/sbtc-cli/src/main.rs deleted file mode 100644 index 4e570403..00000000 --- a/sbtc-cli/src/main.rs +++ /dev/null @@ -1,33 +0,0 @@ -use clap::Parser; - -use crate::commands::broadcast::{broadcast_tx, BroadcastArgs}; -use crate::commands::deposit::{build_deposit_tx, DepositArgs}; -use crate::commands::generate::{generate, GenerateArgs}; -use crate::commands::withdraw::{build_withdrawal_tx, WithdrawalArgs}; - -mod commands; - -#[derive(Parser)] -struct Cli { - #[command(subcommand)] - command: Command, -} - -#[derive(clap::Subcommand, Debug, Clone)] -enum Command { - Deposit(DepositArgs), - Withdraw(WithdrawalArgs), - Broadcast(BroadcastArgs), - GenerateFrom(GenerateArgs), -} - -fn main() -> Result<(), anyhow::Error> { - let args = Cli::parse(); - - match args.command { - Command::Deposit(deposit_args) => build_deposit_tx(&deposit_args), - Command::Withdraw(withdrawal_args) => build_withdrawal_tx(&withdrawal_args), - Command::Broadcast(broadcast_args) => broadcast_tx(&broadcast_args), - Command::GenerateFrom(generate_args) => generate(&generate_args), - } -} diff --git a/sbtc-ops/smart-contract/.gitignore b/sbtc-ops/smart-contract/.gitignore new file mode 100644 index 00000000..5642685c --- /dev/null +++ b/sbtc-ops/smart-contract/.gitignore @@ -0,0 +1,109 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +docs/research.md +docs/deploy.md +.DS_Store +clarity/history.txt +keychain_mainnet.json +keychain_oracle.json +keychain_testnet.json +keychain_regtest.json +keychain_regtest_2.json +keychain_regtest_3.json +keychain_private_testnet.json +notes.md + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +# coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test +.env.regtest + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# IntelliJ +.idea + +# Clarinet requirements +.requirements +clarity/contracts/external/deps +clarity/contracts/external/gen \ No newline at end of file diff --git a/sbtc-ops/smart-contract/.prettierrc b/sbtc-ops/smart-contract/.prettierrc new file mode 100644 index 00000000..6237da14 --- /dev/null +++ b/sbtc-ops/smart-contract/.prettierrc @@ -0,0 +1,5 @@ +{ + "singleQuote": true, + "bracketSpacing": true, + "printWidth": 120 +} diff --git a/sbtc-ops/smart-contract/Clarinet.toml b/sbtc-ops/smart-contract/Clarinet.toml new file mode 100644 index 00000000..ee5225ba --- /dev/null +++ b/sbtc-ops/smart-contract/Clarinet.toml @@ -0,0 +1,128 @@ +[project] +name = 'DecentralizedStacksPools' +description = 'DecentralizedStacksPools' +authors = [] +telemetry = false +cache_dir = '.cache' + +[[project.requirements]] +contract_id = 'SP000000000000000000002Q6VF78.pox-2' + +[[project.requirements]] +contract_id = 'ST000000000000000000002AMW42H.pox-2' + +[[project.requirements]] +contract_id = 'ST000000000000000000002AMW42H.pox-3' +[contracts.Wrapped-Bitcoin] +path = 'contracts/Wrapped-Bitcoin.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.alex-vault-v1-1] +path = 'contracts/alex-vault-v1-1.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.amm-swap-pool-v1-1] +path = 'contracts/amm-swap-pool-v1-1.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.bridge-contract] +path = 'contracts/bridge-contract.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.clarity-bitcoin] +path = 'contracts/clarity-bitcoin.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.degen-bridge-testnet-v3] +path = 'contracts/degen-bridge-testnet-v3.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.ft-trait] +path = 'contracts/ft-trait.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.mining-pool] +path = 'contracts/mining-pool.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.mining-pool-5-blocks] +path = 'contracts/mining-pool-5-blocks.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.mining-pool-test] +path = 'contracts/mining-pool-test.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.restricted-token-trait] +path = 'contracts/restricted-token-trait.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.stacking-pool] +path = 'contracts/stacking-pool.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.stacking-pool-test] +path = 'contracts/stacking-pool-test.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.token-amm-swap-pool-v1-1] +path = 'contracts/token-amm-swap-pool-v1-1.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.token-wbtc] +path = 'contracts/token-wbtc.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.token-wstx] +path = 'contracts/token-wstx.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.trait-flash-loan-user] +path = 'contracts/trait-flash-loan-user.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.trait-ownable] +path = 'contracts/trait-ownable.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.trait-semi-fungible] +path = 'contracts/trait-semi-fungible.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.trait-sip-010] +path = 'contracts/trait-sip-010.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.trait-sip-010-sbtc] +path = 'contracts/trait-sip-010-sbtc.clar' +clarity_version = 2 +epoch = 2.4 + +[repl.analysis] +passes = [] + +[repl.analysis.check_checker] +strict = false +trusted_sender = false +trusted_caller = false +callee_filter = false diff --git a/sbtc-ops/smart-contract/README.md b/sbtc-ops/smart-contract/README.md new file mode 100644 index 00000000..c25b76b7 --- /dev/null +++ b/sbtc-ops/smart-contract/README.md @@ -0,0 +1,6 @@ +# Documentation at +### https://stacks-degens.gitbook.io/decentralized-mining-pool/smart-contract/ + + + + diff --git a/sbtc-ops/smart-contract/contracts/Wrapped-Bitcoin.clar b/sbtc-ops/smart-contract/contracts/Wrapped-Bitcoin.clar new file mode 100644 index 00000000..2de58742 --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/Wrapped-Bitcoin.clar @@ -0,0 +1,211 @@ +;; Implement the `ft-trait` trait defined in the `ft-trait` contract - SIP 10 +;; This can use sugared syntax in real deployment (unit tests do not allow) +(impl-trait .ft-trait.ft-trait) + +;; ;; Implement the token restriction trait +(impl-trait .restricted-token-trait.restricted-token-trait) + +;; Error returned for permission denied - stolen from http 403 +(define-constant PERMISSION_DENIED_ERROR u403) + +;; Data variables specific to the deployed token contract +(define-data-var token-name (string-ascii 32) "") +(define-data-var token-symbol (string-ascii 32) "") +(define-data-var token-decimals uint u0) + +;; Track who deployed the token and whether it has been initialized +(define-data-var deployer-principal principal tx-sender) +(define-data-var is-initialized bool false) + +;; Meta Read Only Functions for reading details about the contract - conforms to SIP 10 +;; -------------------------------------------------------------------------- + +;; Defines built in support functions for tokens used in this contract +;; A second optional parameter can be added here to set an upper limit on max total-supply +(define-fungible-token wrapped-bitcoin) + + +;; Get the token balance of the specified owner in base units +(define-read-only (get-balance (owner principal)) + (ok (ft-get-balance wrapped-bitcoin owner))) + +;; Returns the token name +(define-read-only (get-name) + (ok (var-get token-name))) ;; "Wrapped Bitcoin" + +;; Returns the symbol or "ticker" for this token +(define-read-only (get-symbol) + (ok (var-get token-symbol))) ;; "xBTC" + +;; Returns the number of decimals used +(define-read-only (get-decimals) + (ok (var-get token-decimals))) ;;u8 + +;; Returns the total number of tokens that currently exist +(define-read-only (get-total-supply) + (ok (ft-get-supply wrapped-bitcoin))) + + +;; Write function to transfer tokens between accounts - conforms to SIP 10 +;; -------------------------------------------------------------------------- + +;; Transfers tokens to a recipient +;; The originator of the transaction (tx-sender) must be the 'sender' principal +;; Smart contracts can move tokens from their own address by calling transfer with the 'as-contract' modifier to override the tx-sender. + +(define-public (transfer (amount uint) (sender principal) (recipient principal ) (memo (optional (buff 34) ))) + (begin + (try! (detect-transfer-restriction amount sender recipient)) ;; Ensure there is no restriction + (asserts! (is-eq tx-sender sender) (err u4)) ;; Ensure the originator is the sender principal + (print (default-to 0x memo)) + (ft-transfer? wrapped-bitcoin amount sender recipient) ) ) ;; Transfer + + +;; Role Based Access Control +;; -------------------------------------------------------------------------- +(define-constant OWNER_ROLE u0) ;; Can manage RBAC +(define-constant MINTER_ROLE u1) ;; Can mint new tokens to any account +(define-constant BURNER_ROLE u2) ;; Can burn tokens from any account +(define-constant REVOKER_ROLE u3) ;; Can revoke tokens and move them to any account +(define-constant BLACKLISTER_ROLE u4) ;; Can add principals to a blacklist that can prevent transfers + +;; Each role will have a mapping of principal to boolean. A true "allowed" in the mapping indicates that the principal has the role. +;; Each role will have special permissions to modify or manage specific capabilities in the contract. +;; Note that adding/removing roles could be optimized by having just 1 function, but since this is sensitive functionality, it was split +;; into 2 separate functions to make it explicit. +;; See the Readme about more details on the RBAC setup. +(define-map roles { role: uint, account: principal } { allowed: bool }) + +;; Checks if an account has the specified role +(define-read-only (has-role (role-to-check uint) (principal-to-check principal)) + (default-to false (get allowed (map-get? roles {role: role-to-check, account: principal-to-check})))) + +;; Add a principal to the specified role +;; Only existing principals with the OWNER_ROLE can modify roles +(define-public (add-principal-to-role (role-to-add uint) (principal-to-add principal)) + (begin + ;; Check the contract-caller to verify they have the owner role + (asserts! (has-role OWNER_ROLE contract-caller) (err PERMISSION_DENIED_ERROR)) + ;; Print the action for any off chain watchers + (print { action: "add-principal-to-role", role-to-add: role-to-add, principal-to-add: principal-to-add }) + (ok (map-set roles { role: role-to-add, account: principal-to-add } { allowed: true })))) + +;; Remove a principal from the specified role +;; Only existing principals with the OWNER_ROLE can modify roles +;; WARN: Removing all owners will irrevocably lose all ownership permissions +(define-public (remove-principal-from-role (role-to-remove uint) (principal-to-remove principal)) + (begin + ;; Check the contract-caller to verify they have the owner role + (asserts! (has-role OWNER_ROLE contract-caller) (err PERMISSION_DENIED_ERROR)) + ;; Print the action for any off chain watchers + (print { action: "remove-principal-from-role", role-to-remove: role-to-remove, principal-to-remove: principal-to-remove }) + (ok (map-set roles { role: role-to-remove, account: principal-to-remove } { allowed: false })))) + + +;; Token URI +;; -------------------------------------------------------------------------- + +;; Variable for URI storage +(define-data-var uri (string-utf8 256) u"") + +;; Public getter for the URI +(define-read-only (get-token-uri) + (ok (some (var-get uri)))) + +;; Setter for the URI - only the owner can set it +(define-public (set-token-uri (updated-uri (string-utf8 256))) + (begin + (asserts! (has-role OWNER_ROLE contract-caller) (err PERMISSION_DENIED_ERROR)) + ;; Print the action for any off chain watchers + (print { action: "set-token-uri", updated-uri: updated-uri }) + (ok (var-set uri updated-uri)))) + + +;; Minting and Burning +;; -------------------------------------------------------------------------- + +;; Mint tokens to the target address +;; Only existing principals with the MINTER_ROLE can mint tokens +(define-public (mint-tokens (mint-amount uint) (mint-to principal) ) + (begin + (asserts! (has-role MINTER_ROLE contract-caller) (err PERMISSION_DENIED_ERROR)) + ;; Print the action for any off chain watchers + (print { action: "mint-tokens", mint-amount: mint-amount, mint-to: mint-to }) + (ft-mint? wrapped-bitcoin mint-amount mint-to))) + +;; Burn tokens from the target address +;; Only existing principals with the BURNER_ROLE can mint tokens +(define-public (burn-tokens (burn-amount uint) (burn-from principal) ) + (begin + (asserts! (has-role BURNER_ROLE contract-caller) (err PERMISSION_DENIED_ERROR)) + ;; Print the action for any off chain watchers + (print { action: "burn-tokens", burn-amount: burn-amount, burn-from : burn-from }) + (ft-burn? wrapped-bitcoin burn-amount burn-from))) + + +;; Revoking Tokens +;; -------------------------------------------------------------------------- + +;; Moves tokens from one account to another +;; Only existing principals with the REVOKER_ROLE can revoke tokens +(define-public (revoke-tokens (revoke-amount uint) (revoke-from principal) (revoke-to principal) ) + (begin + (asserts! (has-role REVOKER_ROLE contract-caller) (err PERMISSION_DENIED_ERROR)) + ;; Print the action for any off chain watchers + (print { action: "revoke-tokens", revoke-amount: revoke-amount, revoke-from: revoke-from, revoke-to: revoke-to }) + (ft-transfer? wrapped-bitcoin revoke-amount revoke-from revoke-to))) + +;; Blacklisting Principals +;; -------------------------------------------------------------------------- + +;; Blacklist mapping. If an account has blacklisted = true then no transfers in or out are allowed +(define-map blacklist { account: principal } { blacklisted: bool }) + +;; Checks if an account is blacklisted +(define-read-only (is-blacklisted (principal-to-check principal)) + (default-to false (get blacklisted (map-get? blacklist { account: principal-to-check })))) + +;; Updates an account's blacklist status +;; Only existing principals with the BLACKLISTER_ROLE can update blacklist status +(define-public (update-blacklisted (principal-to-update principal) (set-blacklisted bool)) + (begin + (asserts! (has-role BLACKLISTER_ROLE contract-caller) (err PERMISSION_DENIED_ERROR)) + ;; Print the action for any off chain watchers + (print { action: "update-blacklisted", principal-to-update: principal-to-update, set-blacklisted: set-blacklisted }) + (ok (map-set blacklist { account: principal-to-update } { blacklisted: set-blacklisted })))) + +;; Transfer Restrictions +;; -------------------------------------------------------------------------- +(define-constant RESTRICTION_NONE u0) ;; No restriction detected +(define-constant RESTRICTION_BLACKLIST u5) ;; Sender or receiver is on the blacklist + +;; Checks to see if a transfer should be restricted. If so returns an error code that specifies restriction type. +(define-read-only (detect-transfer-restriction (amount uint) (sender principal) (recipient principal)) + (if (or (is-blacklisted sender) (is-blacklisted recipient)) + (err RESTRICTION_BLACKLIST) + (ok RESTRICTION_NONE))) + +;; Returns the user viewable string for a specific transfer restriction +(define-read-only (message-for-restriction (restriction-code uint)) + (if (is-eq restriction-code RESTRICTION_NONE) + (ok "No Restriction Detected") + (if (is-eq restriction-code RESTRICTION_BLACKLIST) + (ok "Sender or recipient is on the blacklist and prevented from transacting") + (ok "Unknown Error Code")))) + + +;; Initialization +;; -------------------------------------------------------------------------- + +;; Check to ensure that the same account that deployed the contract is initializing it +;; Only allow this funtion to be called once by checking "is-initialized" +(define-public (initialize (name-to-set (string-ascii 32)) (symbol-to-set (string-ascii 32) ) (decimals-to-set uint) (initial-owner principal)) + (begin + (asserts! (is-eq tx-sender (var-get deployer-principal)) (err PERMISSION_DENIED_ERROR)) + (asserts! (not (var-get is-initialized)) (err PERMISSION_DENIED_ERROR)) + (var-set is-initialized true) ;; Set to true so that this can't be called again + (var-set token-name name-to-set) + (var-set token-symbol symbol-to-set) + (var-set token-decimals decimals-to-set) + (map-set roles { role: OWNER_ROLE, account: initial-owner } { allowed: true }) + (ok true))) \ No newline at end of file diff --git a/sbtc-ops/smart-contract/contracts/alex-vault-v1-1.clar b/sbtc-ops/smart-contract/contracts/alex-vault-v1-1.clar new file mode 100644 index 00000000..52569e38 --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/alex-vault-v1-1.clar @@ -0,0 +1,165 @@ +(use-trait ft-trait .trait-sip-010.sip-010-trait) +(use-trait sft-trait .trait-semi-fungible.semi-fungible-trait) +(use-trait flash-loan-trait .trait-flash-loan-user.flash-loan-user-trait) +(define-constant ONE_8 u100000000) ;; 8 decimal places +(define-constant ERR-NOT-AUTHORIZED (err u1000)) +(define-constant ERR-PAUSED (err u1001)) +(define-constant ERR-INVALID-BALANCE (err u1002)) +(define-constant ERR-INVALID-TOKEN (err u2026)) +(define-constant ERR-AMOUNT-EXCEED-RESERVE (err u2024)) +(define-data-var contract-owner principal tx-sender) +(define-map approved-contracts principal bool) +(define-map approved-tokens principal bool) +(define-map approved-flash-loan-users principal bool) +(define-map reserve principal uint) +(define-data-var flash-loan-fee-rate uint u0) +(define-data-var flash-loan-enabled bool false) +(define-data-var paused bool false) +(define-read-only (get-flash-loan-enabled) + (var-get flash-loan-enabled) +) +(define-read-only (is-paused) + (var-get paused) +) +(define-read-only (get-contract-owner) + (ok (var-get contract-owner)) +) +(define-read-only (get-flash-loan-fee-rate) + (var-get flash-loan-fee-rate) +) +(define-read-only (get-reserve (the-token principal)) + (default-to u0 (map-get? reserve the-token)) +) +(define-public (get-balance (the-token )) + (begin + (try! (check-is-approved-token (contract-of the-token))) + (contract-call? the-token get-balance-fixed (as-contract tx-sender)) + ) +) +(define-public (set-flash-loan-enabled (enabled bool)) + (begin + (try! (check-is-owner)) + (ok (var-set flash-loan-enabled enabled)) + ) +) +(define-public (pause (new-paused bool)) + (begin + (try! (check-is-owner)) + (ok (var-set paused new-paused)) + ) +) +(define-public (set-contract-owner (owner principal)) + (begin + (try! (check-is-owner)) + (ok (var-set contract-owner owner)) + ) +) +(define-public (set-approved-contract (the-contract principal) (approved bool)) + (begin + (try! (check-is-owner)) + (ok (map-set approved-contracts the-contract approved)) + ) +) +(define-public (set-approved-flash-loan-user (the-flash-loan-user principal) (approved bool)) + (begin + (try! (check-is-owner)) + (ok (map-set approved-flash-loan-users the-flash-loan-user approved)) + ) +) +(define-public (set-approved-token (the-token principal) (approved bool)) + (begin + (try! (check-is-owner)) + (ok (map-set approved-tokens the-token approved)) + ) +) +(define-public (set-flash-loan-fee-rate (fee uint)) + (begin + (try! (check-is-owner)) + (ok (var-set flash-loan-fee-rate fee)) + ) +) +(define-public (transfer-ft (the-token ) (amount uint) (recipient principal)) + (begin + (asserts! (not (is-paused)) ERR-PAUSED) + (asserts! (and (or (is-ok (check-is-approved)) (is-ok (check-is-owner))) (is-ok (check-is-approved-token (contract-of the-token)))) ERR-NOT-AUTHORIZED) + (as-contract (contract-call? the-token transfer-fixed amount tx-sender recipient none)) + ) +) +(define-public (transfer-ft-two (token-x-trait ) (dx uint) (token-y-trait ) (dy uint) (recipient principal)) + (begin + (try! (transfer-ft token-x-trait dx recipient)) + (transfer-ft token-y-trait dy recipient) + ) +) +(define-public (transfer-sft (the-token ) (token-id uint) (amount uint) (recipient principal)) + (begin + (asserts! (not (is-paused)) ERR-PAUSED) + (asserts! (and (or (is-ok (check-is-approved)) (is-ok (check-is-owner))) (is-ok (check-is-approved-token (contract-of the-token)))) ERR-NOT-AUTHORIZED) + (as-contract (contract-call? the-token transfer-fixed token-id amount tx-sender recipient)) + ) +) +(define-public (flash-loan (the-flash-loan-user ) (the-token ) (amount uint) (memo (optional (buff 16)))) + (begin + (asserts! (not (is-paused)) ERR-PAUSED) + (asserts! (and (is-ok (check-is-approved-flash-loan-user (contract-of the-flash-loan-user))) (is-ok (check-is-approved-token (contract-of the-token)))) ERR-NOT-AUTHORIZED) + (let + ( + (pre-bal (unwrap! (get-balance the-token) ERR-INVALID-BALANCE)) + (fee-with-principal (+ ONE_8 (var-get flash-loan-fee-rate))) + (amount-with-fee (mul-up amount fee-with-principal)) + (recipient tx-sender) + ) + + ;; make sure current balance > loan amount + (asserts! (> pre-bal amount) ERR-INVALID-BALANCE) + ;; transfer loan to flash-loan-user + (as-contract (try! (contract-call? the-token transfer-fixed amount tx-sender recipient none))) + ;; flash-loan-user executes with loan received + (try! (contract-call? the-flash-loan-user execute the-token amount memo)) + ;; return the loan + fee + (try! (contract-call? the-token transfer-fixed amount-with-fee tx-sender (as-contract tx-sender) none)) + (ok amount-with-fee) + ) + ) +) +(define-public (add-to-reserve (the-token principal) (amount uint)) + (begin + (asserts! (not (is-paused)) ERR-PAUSED) + (asserts! (or (is-ok (check-is-approved)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED) + (ok (map-set reserve the-token (+ amount (get-reserve the-token)))) + ) +) +(define-public (remove-from-reserve (the-token principal) (amount uint)) + (begin + (asserts! (not (is-paused)) ERR-PAUSED) + (asserts! (or (is-ok (check-is-approved)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED) + (asserts! (<= amount (get-reserve the-token)) ERR-AMOUNT-EXCEED-RESERVE) + (ok (map-set reserve the-token (- (get-reserve the-token) amount))) + ) +) +(define-private (check-is-approved) + (ok (asserts! (default-to false (map-get? approved-contracts tx-sender)) ERR-NOT-AUTHORIZED)) +) +(define-private (check-is-owner) + (ok (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED)) +) +(define-private (check-is-approved-flash-loan-user (the-flash-loan-user principal)) + (ok (asserts! (default-to false (map-get? approved-flash-loan-users the-flash-loan-user)) ERR-NOT-AUTHORIZED)) +) +(define-private (check-is-approved-token (flash-loan-token principal)) + (ok (asserts! (default-to false (map-get? approved-tokens flash-loan-token)) ERR-NOT-AUTHORIZED)) +) +(define-private (mul-down (a uint) (b uint)) + (/ (* a b) ONE_8) +) +(define-private (mul-up (a uint) (b uint)) + (let + ( + (product (* a b)) + ) + (if (is-eq product u0) + u0 + (+ u1 (/ (- product u1) ONE_8)) + ) + ) +) \ No newline at end of file diff --git a/sbtc-ops/smart-contract/contracts/amm-swap-pool-v1-1.clar b/sbtc-ops/smart-contract/contracts/amm-swap-pool-v1-1.clar new file mode 100644 index 00000000..fedea768 --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/amm-swap-pool-v1-1.clar @@ -0,0 +1,1071 @@ +(use-trait ft-trait .trait-sip-010.sip-010-trait) +(define-constant ERR-NOT-AUTHORIZED (err u1000)) +(define-constant ERR-INVALID-POOL (err u2001)) +(define-constant ERR-INVALID-LIQUIDITY (err u2003)) +(define-constant ERR-POOL-ALREADY-EXISTS (err u2000)) +(define-constant ERR-PERCENT-GREATER-THAN-ONE (err u5000)) +(define-constant ERR-EXCEEDS-MAX-SLIPPAGE (err u2020)) +(define-constant ERR-ORACLE-NOT-ENABLED (err u7002)) +(define-constant ERR-ORACLE-AVERAGE-BIGGER-THAN-ONE (err u7004)) +(define-constant ERR-PAUSED (err u1001)) +(define-constant ERR-SWITCH-THRESHOLD-BIGGER-THAN-ONE (err u7005)) +(define-constant ERR-NO-LIQUIDITY (err u2002)) +(define-constant ERR-MAX-IN-RATIO (err u4001)) +(define-constant ERR-MAX-OUT-RATIO (err u4002)) +(define-data-var contract-owner principal tx-sender) +(define-data-var pool-nonce uint u0) +(define-data-var paused bool false) +(define-data-var switch-threshold uint u80000000) +(define-map pools-id-map + uint + { + token-x: principal, + token-y: principal, + factor: uint + } +) +(define-map pools-data-map + { + token-x: principal, + token-y: principal, + factor: uint + } + { + pool-id: uint, + total-supply: uint, + balance-x: uint, + balance-y: uint, + pool-owner: principal, + fee-rate-x: uint, + fee-rate-y: uint, + fee-rebate: uint, + oracle-enabled: bool, + oracle-average: uint, + oracle-resilient: uint, + start-block: uint, + end-block: uint, + threshold-x: uint, + threshold-y: uint, + max-in-ratio: uint, + max-out-ratio: uint + } +) +(define-read-only (get-switch-threshold) + (var-get switch-threshold) +) +(define-read-only (is-paused) + (var-get paused) +) +(define-read-only (get-contract-owner) + (ok (var-get contract-owner)) +) +(define-read-only (get-pool-details-by-id (pool-id uint)) + (ok (unwrap! (map-get? pools-id-map pool-id) ERR-INVALID-POOL)) +) +(define-read-only (get-pool-details (token-x principal) (token-y principal) (factor uint)) + (ok (unwrap! (get-pool-exists token-x token-y factor) ERR-INVALID-POOL)) +) +(define-read-only (get-pool-exists (token-x principal) (token-y principal) (factor uint)) + (map-get? pools-data-map { token-x: token-x, token-y: token-y, factor: factor }) +) +(define-read-only (get-balances (token-x principal) (token-y principal) (factor uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + ) + (ok {balance-x: (get balance-x pool), balance-y: (get balance-y pool)}) + ) +) +(define-read-only (get-start-block (token-x principal) (token-y principal) (factor uint)) + (ok (get start-block (try! (get-pool-details token-x token-y factor)))) +) +(define-read-only (get-end-block (token-x principal) (token-y principal) (factor uint)) + (ok (get end-block (try! (get-pool-details token-x token-y factor)))) +) +(define-read-only (get-max-in-ratio (token-x principal) (token-y principal) (factor uint)) + (ok (get max-in-ratio (try! (get-pool-details token-x token-y factor)))) +) +(define-read-only (get-max-out-ratio (token-x principal) (token-y principal) (factor uint)) + (ok (get max-out-ratio (try! (get-pool-details token-x token-y factor)))) +) +(define-read-only (check-pool-status (token-x principal) (token-y principal) (factor uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + ) + (ok (asserts! (and (>= block-height (get start-block pool)) (<= block-height (get end-block pool))) ERR-NOT-AUTHORIZED)) + ) +) +(define-read-only (get-oracle-enabled (token-x principal) (token-y principal) (factor uint)) + (ok (get oracle-enabled (try! (get-pool-details token-x token-y factor)))) +) +(define-read-only (get-oracle-average (token-x principal) (token-y principal) (factor uint)) + (ok (get oracle-average (try! (get-pool-details token-x token-y factor)))) +) +(define-read-only (get-oracle-resilient (token-x principal) (token-y principal) (factor uint)) + (let + ( + (exists (is-some (get-pool-exists token-x token-y factor))) + (pool + (if exists + (try! (get-pool-details token-x token-y factor)) + (try! (get-pool-details token-y token-x factor)) + ) + ) + ) + (asserts! (get oracle-enabled pool) ERR-ORACLE-NOT-ENABLED) + (ok (+ (mul-down (- ONE_8 (get oracle-average pool)) (try! (get-oracle-instant token-x token-y factor))) + (mul-down (get oracle-average pool) (get oracle-resilient pool))) + ) + ) +) +(define-read-only (get-oracle-instant (token-x principal) (token-y principal) (factor uint)) + (let + ( + (exists (is-some (get-pool-exists token-x token-y factor))) + (pool + (if exists + (try! (get-pool-details token-x token-y factor)) + (try! (get-pool-details token-y token-x factor)) + ) + ) + ) + (asserts! (get oracle-enabled pool) ERR-ORACLE-NOT-ENABLED) + (if exists + (ok (get-price-internal (get balance-x pool) (get balance-y pool) factor)) + (ok (get-price-internal (get balance-y pool) (get balance-x pool) factor)) + ) + ) +) +(define-read-only (get-price (token-x principal) (token-y principal) (factor uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + ) + (ok (get-price-internal (get balance-x pool) (get balance-y pool) factor)) + ) +) +(define-read-only (get-threshold-x (token-x principal) (token-y principal) (factor uint)) + (ok (get threshold-x (try! (get-pool-details token-x token-y factor)))) +) +(define-read-only (get-threshold-y (token-x principal) (token-y principal) (factor uint)) + (ok (get threshold-y (try! (get-pool-details token-x token-y factor)))) +) +(define-read-only (get-fee-rebate (token-x principal) (token-y principal) (factor uint)) + (ok (get fee-rebate (try! (get-pool-details token-x token-y factor)))) +) +(define-read-only (get-fee-rate-x (token-x principal) (token-y principal) (factor uint)) + (ok (get fee-rate-x (try! (get-pool-details token-x token-y factor)))) +) +(define-read-only (get-fee-rate-y (token-x principal) (token-y principal) (factor uint)) + (ok (get fee-rate-y (try! (get-pool-details token-x token-y factor)))) +) +(define-read-only (get-pool-owner (token-x principal) (token-y principal) (factor uint)) + (ok (get pool-owner (try! (get-pool-details token-x token-y factor)))) +) +(define-read-only (get-y-given-x (token-x principal) (token-y principal) (factor uint) (dx uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + (threshold (get threshold-x pool)) + (dy (if (>= dx threshold) + (get-y-given-x-internal (get balance-x pool) (get balance-y pool) factor dx) + (div-down (mul-down dx (get-y-given-x-internal (get balance-x pool) (get balance-y pool) factor threshold)) threshold) + )) + ) + (asserts! (< dx (mul-down (get balance-x pool) (get max-in-ratio pool))) ERR-MAX-IN-RATIO) + (asserts! (< dy (mul-down (get balance-y pool) (get max-out-ratio pool))) ERR-MAX-OUT-RATIO) + (ok dy) + ) +) +(define-read-only (get-x-given-y (token-x principal) (token-y principal) (factor uint) (dy uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + (threshold (get threshold-y pool)) + (dx (if (>= dy threshold) + (get-x-given-y-internal (get balance-x pool) (get balance-y pool) factor dy) + (div-down (mul-down dy (get-x-given-y-internal (get balance-x pool) (get balance-y pool) factor threshold)) threshold) + )) + ) + (asserts! (< dy (mul-down (get balance-y pool) (get max-in-ratio pool))) ERR-MAX-IN-RATIO) + (asserts! (< dx (mul-down (get balance-x pool) (get max-out-ratio pool))) ERR-MAX-OUT-RATIO) + (ok dx) + ) +) +(define-read-only (get-y-in-given-x-out (token-x principal) (token-y principal) (factor uint) (dx uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + (threshold (get threshold-x pool)) + (dy (if (>= dx threshold) + (get-y-in-given-x-out-internal (get balance-x pool) (get balance-y pool) factor dx) + (div-down (mul-down dx (get-y-in-given-x-out-internal (get balance-x pool) (get balance-y pool) factor threshold)) threshold) + )) + ) + (asserts! (< dy (mul-down (get balance-y pool) (get max-in-ratio pool))) ERR-MAX-IN-RATIO) + (asserts! (< dx (mul-down (get balance-x pool) (get max-out-ratio pool))) ERR-MAX-OUT-RATIO) + (ok dy) + ) +) +(define-read-only (get-x-in-given-y-out (token-x principal) (token-y principal) (factor uint) (dy uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + (threshold (get threshold-y pool)) + (dx (if (>= dy threshold) + (get-x-in-given-y-out-internal (get balance-x pool) (get balance-y pool) factor dy) + (div-down (mul-down dy (get-x-in-given-y-out-internal (get balance-x pool) (get balance-y pool) factor threshold)) threshold) + )) + ) + (asserts! (< dx (mul-down (get balance-x pool) (get max-in-ratio pool))) ERR-MAX-IN-RATIO) + (asserts! (< dy (mul-down (get balance-y pool) (get max-out-ratio pool))) ERR-MAX-OUT-RATIO) + (ok dx) + ) +) +(define-read-only (get-x-given-price (token-x principal) (token-y principal) (factor uint) (price uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + ) + (asserts! (< price (get-price-internal (get balance-x pool) (get balance-y pool) factor)) ERR-NO-LIQUIDITY) + (ok (get-x-given-price-internal (get balance-x pool) (get balance-y pool) factor price)) + ) +) +(define-read-only (get-y-given-price (token-x principal) (token-y principal) (factor uint) (price uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + ) + (asserts! (> price (get-price-internal (get balance-x pool) (get balance-y pool) factor)) ERR-NO-LIQUIDITY) + (ok (get-y-given-price-internal (get balance-x pool) (get balance-y pool) factor price)) + ) +) +(define-read-only (get-token-given-position (token-x principal) (token-y principal) (factor uint) (dx uint) (max-dy (optional uint))) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + (dy (default-to u340282366920938463463374607431768211455 max-dy)) + ) + (asserts! (and (> dx u0) (> dy u0)) ERR-NO-LIQUIDITY) + (ok (get-token-given-position-internal (get balance-x pool) (get balance-y pool) factor (get total-supply pool) dx dy)) + ) +) +(define-read-only (get-position-given-mint (token-x principal) (token-y principal) (factor uint) (token-amount uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + ) + (asserts! (> (get total-supply pool) u0) ERR-NO-LIQUIDITY) + (ok (get-position-given-mint-internal (get balance-x pool) (get balance-y pool) factor (get total-supply pool) token-amount)) + ) +) +(define-read-only (get-position-given-burn (token-x principal) (token-y principal) (factor uint) (token-amount uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + ) + (asserts! (> (get total-supply pool) u0) ERR-NO-LIQUIDITY) + (ok (get-position-given-burn-internal (get balance-x pool) (get balance-y pool) factor (get total-supply pool) token-amount)) + ) +) +(define-read-only (get-helper (token-x principal) (token-y principal) (factor uint) (dx uint)) + (if (is-some (get-pool-exists token-x token-y factor)) + (get-y-given-x token-x token-y factor dx) + (get-x-given-y token-y token-x factor dx) + ) +) +(define-read-only (get-helper-a (token-x principal) (token-y principal) (token-z principal) (factor-x uint) (factor-y uint) (dx uint)) + (get-helper token-y token-z factor-y (try! (get-helper token-x token-y factor-x dx))) +) +(define-read-only (get-helper-b + (token-x principal) (token-y principal) (token-z principal) (token-w principal) + (factor-x uint) (factor-y uint) (factor-z uint) + (dx uint)) + (get-helper token-z token-w factor-z (try! (get-helper-a token-x token-y token-z factor-x factor-y dx))) +) +(define-read-only (get-helper-c + (token-x principal) (token-y principal) (token-z principal) (token-w principal) (token-v principal) + (factor-x uint) (factor-y uint) (factor-z uint) (factor-w uint) + (dx uint)) + (get-helper-a token-z token-w token-v factor-z factor-w (try! (get-helper-a token-x token-y token-z factor-x factor-y dx))) +) +(define-read-only (fee-helper (token-x principal) (token-y principal) (factor uint)) + (if (is-some (get-pool-exists token-x token-y factor)) + (get-fee-rate-x token-x token-y factor) + (get-fee-rate-y token-y token-x factor) + ) +) +(define-read-only (fee-helper-a (token-x principal) (token-y principal) (token-z principal) (factor-x uint) (factor-y uint)) + (ok (+ + (try! (fee-helper token-x token-y factor-x)) + (try! (fee-helper token-y token-z factor-y)) + )) +) +(define-read-only (fee-helper-b + (token-x principal) (token-y principal) (token-z principal) (token-w principal) + (factor-x uint) (factor-y uint) (factor-z uint)) + (ok (+ + (try! (fee-helper-a token-x token-y token-z factor-x factor-y)) + (try! (fee-helper token-z token-w factor-z)) + )) +) +(define-read-only (fee-helper-c + (token-x principal) (token-y principal) (token-z principal) (token-w principal) (token-v principal) + (factor-x uint) (factor-y uint) (factor-z uint) (factor-w uint)) + (ok (+ + (try! (fee-helper-a token-x token-y token-z factor-x factor-y)) + (try! (fee-helper-a token-z token-w token-v factor-z factor-w)) + )) +) +(define-read-only (get-invariant (balance-x uint) (balance-y uint) (t uint)) + (if (>= t (var-get switch-threshold)) + (+ (mul-down (- ONE_8 t) (+ balance-x balance-y)) (mul-down t (mul-down balance-x balance-y))) + (+ (pow-down balance-x (- ONE_8 t)) (pow-down balance-y (- ONE_8 t))) + ) +) +(define-public (set-switch-threshold (new-threshold uint)) + (begin + (try! (check-is-owner)) + (asserts! (<= new-threshold ONE_8) ERR-SWITCH-THRESHOLD-BIGGER-THAN-ONE) + (ok (var-set switch-threshold new-threshold)) + ) +) +(define-public (pause (new-paused bool)) + (begin + (try! (check-is-owner)) + (ok (var-set paused new-paused)) + ) +) +(define-public (set-contract-owner (owner principal)) + (begin + (try! (check-is-owner)) + (ok (var-set contract-owner owner)) + ) +) +(define-public (set-fee-rebate (token-x principal) (token-y principal) (factor uint) (fee-rebate uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + ) + (try! (check-is-owner)) + (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool { fee-rebate: fee-rebate })) + (ok true) + ) +) +(define-public (set-pool-owner (token-x principal) (token-y principal) (factor uint) (pool-owner principal)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + ) + (try! (check-is-owner)) + (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool { pool-owner: pool-owner })) + (ok true) + ) +) +(define-public (set-start-block (token-x principal) (token-y principal) (factor uint) (new-start-block uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + ) + (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED) + (ok + (map-set + pools-data-map + { token-x: token-x, token-y: token-y, factor: factor } + (merge pool {start-block: new-start-block}) + ) + ) + ) +) +(define-public (set-end-block (token-x principal) (token-y principal) (factor uint) (new-end-block uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + ) + (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED) + (ok + (map-set + pools-data-map + { token-x: token-x, token-y: token-y, factor: factor } + (merge pool {end-block: new-end-block}) + ) + ) + ) +) +(define-public (set-max-in-ratio (token-x principal) (token-y principal) (factor uint) (new-max-in-ratio uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + ) + (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED) + (ok + (map-set + pools-data-map + { token-x: token-x, token-y: token-y, factor: factor } + (merge pool {max-in-ratio: new-max-in-ratio}) + ) + ) + ) +) +(define-public (set-max-out-ratio (token-x principal) (token-y principal) (factor uint) (new-max-out-ratio uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + ) + (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED) + (ok + (map-set + pools-data-map + { token-x: token-x, token-y: token-y, factor: factor } + (merge pool {max-out-ratio: new-max-out-ratio}) + ) + ) + ) +) +(define-public (set-oracle-enabled (token-x principal) (token-y principal) (factor uint) (enabled bool)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + ) + (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED) + (ok + (map-set + pools-data-map + { token-x: token-x, token-y: token-y, factor: factor } + (merge pool {oracle-enabled: enabled}) + ) + ) + ) +) +(define-public (set-oracle-average (token-x principal) (token-y principal) (factor uint) (new-oracle-average uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + ) + (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED) + (asserts! (get oracle-enabled pool) ERR-ORACLE-NOT-ENABLED) + (asserts! (< new-oracle-average ONE_8) ERR-ORACLE-AVERAGE-BIGGER-THAN-ONE) + (ok + (map-set + pools-data-map + { token-x: token-x, token-y: token-y, factor: factor } + (merge pool + { + oracle-average: new-oracle-average, + oracle-resilient: (try! (get-oracle-instant token-x token-y factor)) + } + ) + ) + ) + ) +) +(define-public (set-threshold-x (token-x principal) (token-y principal) (factor uint) (new-threshold uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + ) + (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED) + (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool { threshold-x: new-threshold })) + (ok true) + ) +) +(define-public (set-threshold-y (token-x principal) (token-y principal) (factor uint) (new-threshold uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + ) + (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED) + (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool { threshold-y: new-threshold })) + (ok true) + ) +) +(define-public (set-fee-rate-x (token-x principal) (token-y principal) (factor uint) (fee-rate-x uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + ) + (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED) + (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool { fee-rate-x: fee-rate-x })) + (ok true) + ) +) +(define-public (set-fee-rate-y (token-x principal) (token-y principal) (factor uint) (fee-rate-y uint)) + (let + ( + (pool (try! (get-pool-details token-x token-y factor))) + ) + (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED) + (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool { fee-rate-y: fee-rate-y })) + (ok true) + ) +) +(define-public (create-pool (token-x-trait ) (token-y-trait ) (factor uint) (pool-owner principal) (dx uint) (dy uint)) + (let + ( + (pool-id (+ (var-get pool-nonce) u1)) + (token-x (contract-of token-x-trait)) + (token-y (contract-of token-y-trait)) + (pool-data { + pool-id: pool-id, + total-supply: u0, + balance-x: u0, + balance-y: u0, + pool-owner: pool-owner, + fee-rate-x: u0, + fee-rate-y: u0, + fee-rebate: u0, + oracle-enabled: false, + oracle-average: u0, + oracle-resilient: u0, + start-block: u340282366920938463463374607431768211455, + end-block: u340282366920938463463374607431768211455, + threshold-x: u0, + threshold-y: u0, + max-in-ratio: u0, + max-out-ratio: u0 + }) + ) + (asserts! (not (is-paused)) ERR-PAUSED) + (asserts! + (and + (is-none (map-get? pools-data-map { token-x: token-x, token-y: token-y, factor: factor })) + (is-none (map-get? pools-data-map { token-x: token-y, token-y: token-x, factor: factor })) + ) + ERR-POOL-ALREADY-EXISTS + ) + (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } pool-data) + (map-set pools-id-map pool-id { token-x: token-x, token-y: token-y, factor: factor }) + (var-set pool-nonce pool-id) + (try! (add-to-position token-x-trait token-y-trait factor dx (some dy))) + (print { object: "pool", action: "created", data: pool-data }) + (ok true) + ) +) +(define-public (add-to-position (token-x-trait ) (token-y-trait ) (factor uint) (dx uint) (max-dy (optional uint))) + (let + ( + (token-x (contract-of token-x-trait)) + (token-y (contract-of token-y-trait)) + (pool (try! (get-pool-details token-x token-y factor))) + (balance-x (get balance-x pool)) + (balance-y (get balance-y pool)) + (total-supply (get total-supply pool)) + (add-data (try! (get-token-given-position token-x token-y factor dx max-dy))) + (new-supply (get token add-data)) + (dy (get dy add-data)) + (pool-updated (merge pool { + total-supply: (+ new-supply total-supply), + balance-x: (+ balance-x dx), + balance-y: (+ balance-y dy) + })) + (sender tx-sender) + ) + (asserts! (not (is-paused)) ERR-PAUSED) + (asserts! (and (> dx u0) (> dy u0)) ERR-INVALID-LIQUIDITY) + (asserts! (>= (default-to u340282366920938463463374607431768211455 max-dy) dy) ERR-EXCEEDS-MAX-SLIPPAGE) + (try! (contract-call? token-x-trait transfer-fixed dx sender .alex-vault-v1-1 none)) + (try! (contract-call? token-y-trait transfer-fixed dy sender .alex-vault-v1-1 none)) + (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } pool-updated) + (as-contract (try! (contract-call? .token-amm-swap-pool-v1-1 mint-fixed (get pool-id pool) new-supply sender))) + (print { object: "pool", action: "liquidity-added", data: pool-updated }) + (ok {supply: new-supply, dx: dx, dy: dy}) + ) +) +(define-public (reduce-position (token-x-trait ) (token-y-trait ) (factor uint) (percent uint)) + (let + ( + (token-x (contract-of token-x-trait)) + (token-y (contract-of token-y-trait)) + (pool (try! (get-pool-details token-x token-y factor))) + (balance-x (get balance-x pool)) + (balance-y (get balance-y pool)) + (total-shares (unwrap-panic (contract-call? .token-amm-swap-pool-v1-1 get-balance-fixed (get pool-id pool) tx-sender))) + (shares (if (is-eq percent ONE_8) total-shares (mul-down total-shares percent))) + (total-supply (get total-supply pool)) + (reduce-data (try! (get-position-given-burn token-x token-y factor shares))) + (dx (get dx reduce-data)) + (dy (get dy reduce-data)) + (pool-updated (merge pool { + total-supply: (if (<= total-supply shares) u0 (- total-supply shares)), + balance-x: (if (<= balance-x dx) u0 (- balance-x dx)), + balance-y: (if (<= balance-y dy) u0 (- balance-y dy)) + }) + ) + (sender tx-sender) + ) + (asserts! (not (is-paused)) ERR-PAUSED) + (asserts! (<= percent ONE_8) ERR-PERCENT-GREATER-THAN-ONE) + (as-contract (try! (contract-call? .alex-vault-v1-1 transfer-ft-two token-x-trait dx token-y-trait dy sender))) + (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } pool-updated) + (as-contract (try! (contract-call? .token-amm-swap-pool-v1-1 burn-fixed (get pool-id pool) shares sender))) + (print { object: "pool", action: "liquidity-removed", data: pool-updated }) + (ok {dx: dx, dy: dy}) + ) +) +(define-public (swap-x-for-y (token-x-trait ) (token-y-trait ) (factor uint) (dx uint) (min-dy (optional uint))) + (let + ( + (token-x (contract-of token-x-trait)) + (token-y (contract-of token-y-trait)) + (pool (try! (get-pool-details token-x token-y factor))) + (balance-x (get balance-x pool)) + (balance-y (get balance-y pool)) + (fee (mul-up dx (get fee-rate-x pool))) + (dx-net-fees (if (<= dx fee) u0 (- dx fee))) + (fee-rebate (mul-down fee (get fee-rebate pool))) + (dy (try! (get-y-given-x token-x token-y factor dx-net-fees))) + (pool-updated (merge pool { + balance-x: (+ balance-x dx-net-fees fee-rebate), + balance-y: (if (<= balance-y dy) u0 (- balance-y dy)), + oracle-resilient: (if (get oracle-enabled pool) (try! (get-oracle-resilient token-x token-y factor)) u0) + }) + ) + (sender tx-sender) + ) + (asserts! (not (is-paused)) ERR-PAUSED) + (try! (check-pool-status token-x token-y factor)) + (asserts! (> dx u0) ERR-INVALID-LIQUIDITY) + (asserts! (<= (div-down dy dx-net-fees) (get-price-internal balance-x balance-y factor)) ERR-INVALID-LIQUIDITY) + (asserts! (<= (default-to u0 min-dy) dy) ERR-EXCEEDS-MAX-SLIPPAGE) + (try! (contract-call? token-x-trait transfer-fixed dx sender .alex-vault-v1-1 none)) + (and (> dy u0) (as-contract (try! (contract-call? .alex-vault-v1-1 transfer-ft token-y-trait dy sender)))) + (as-contract (try! (contract-call? .alex-vault-v1-1 add-to-reserve token-x (- fee fee-rebate)))) + (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } pool-updated) + (print { object: "pool", action: "swap-x-for-y", data: pool-updated }) + (ok {dx: dx-net-fees, dy: dy}) + ) +) +(define-public (swap-y-for-x (token-x-trait ) (token-y-trait ) (factor uint) (dy uint) (min-dx (optional uint))) + (let + ( + (token-x (contract-of token-x-trait)) + (token-y (contract-of token-y-trait)) + (pool (try! (get-pool-details token-x token-y factor))) + (balance-x (get balance-x pool)) + (balance-y (get balance-y pool)) + (fee (mul-up dy (get fee-rate-y pool))) + (dy-net-fees (if (<= dy fee) u0 (- dy fee))) + (fee-rebate (mul-down fee (get fee-rebate pool))) + (dx (try! (get-x-given-y token-x token-y factor dy-net-fees))) + (pool-updated (merge pool { + balance-x: (if (<= balance-x dx) u0 (- balance-x dx)), + balance-y: (+ balance-y dy-net-fees fee-rebate), + oracle-resilient: (if (get oracle-enabled pool) (try! (get-oracle-resilient token-x token-y factor)) u0) + }) + ) + (sender tx-sender) + ) + (asserts! (not (is-paused)) ERR-PAUSED) + (try! (check-pool-status token-x token-y factor)) + (asserts! (> dy u0) ERR-INVALID-LIQUIDITY) + (asserts! (>= (div-down dy-net-fees dx) (get-price-internal balance-x balance-y factor)) ERR-INVALID-LIQUIDITY) + (asserts! (<= (default-to u0 min-dx) dx) ERR-EXCEEDS-MAX-SLIPPAGE) + (try! (contract-call? token-y-trait transfer-fixed dy sender .alex-vault-v1-1 none)) + (and (> dx u0) (as-contract (try! (contract-call? .alex-vault-v1-1 transfer-ft token-x-trait dx sender)))) + (as-contract (try! (contract-call? .alex-vault-v1-1 add-to-reserve token-y (- fee fee-rebate)))) + (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } pool-updated) + (print { object: "pool", action: "swap-y-for-x", data: pool-updated }) + (ok {dx: dx, dy: dy-net-fees}) + ) +) +(define-public (swap-helper (token-x-trait ) (token-y-trait ) (factor uint) (dx uint) (min-dy (optional uint))) + (if (is-some (get-pool-exists (contract-of token-x-trait) (contract-of token-y-trait) factor)) + (ok (get dy (try! (swap-x-for-y token-x-trait token-y-trait factor dx min-dy)))) + (ok (get dx (try! (swap-y-for-x token-y-trait token-x-trait factor dx min-dy)))) + ) +) +(define-public (swap-helper-a (token-x-trait ) (token-y-trait ) (token-z-trait ) (factor-x uint) (factor-y uint) (dx uint) (min-dz (optional uint))) + (swap-helper token-y-trait token-z-trait factor-y (try! (swap-helper token-x-trait token-y-trait factor-x dx none)) min-dz) +) +(define-public (swap-helper-b + (token-x-trait ) (token-y-trait ) (token-z-trait ) (token-w-trait ) + (factor-x uint) (factor-y uint) (factor-z uint) + (dx uint) (min-dw (optional uint))) + (swap-helper token-z-trait token-w-trait factor-z + (try! (swap-helper-a token-x-trait token-y-trait token-z-trait factor-x factor-y dx none)) none) +) +(define-public (swap-helper-c + (token-x-trait ) (token-y-trait ) (token-z-trait ) (token-w-trait ) (token-v-trait ) + (factor-x uint) (factor-y uint) (factor-z uint) (factor-w uint) + (dx uint) (min-dv (optional uint))) + (swap-helper-a token-z-trait token-w-trait token-v-trait factor-z factor-w + (try! (swap-helper-a token-x-trait token-y-trait token-z-trait factor-x factor-y dx none)) min-dv) +) +(define-private (check-is-owner) + (ok (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED)) +) +(define-private (get-price-internal (balance-x uint) (balance-y uint) (factor uint)) + (if (>= factor (var-get switch-threshold)) + (div-down (+ (- ONE_8 factor) (mul-down factor balance-y)) (+ (- ONE_8 factor) (mul-down factor balance-x))) + (pow-down (div-down balance-y balance-x) factor) + ) +) +(define-private (get-y-given-x-internal (balance-x uint) (balance-y uint) (t uint) (dx uint)) + (if (>= t (var-get switch-threshold)) + (let + ( + (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t))) + ) + (div-down (+ (mul-down t-comp dx) (mul-down t (mul-down dx balance-y))) (+ t-comp (mul-down t (+ balance-x dx)))) + ) + (let + ( + (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t))) + (t-comp-num-uncapped (div-up ONE_8 t-comp)) + (t-comp-num (if (< t-comp-num-uncapped MILD_EXPONENT_BOUND) t-comp-num-uncapped MILD_EXPONENT_BOUND)) + (x-pow (pow-up balance-x t-comp)) + (y-pow (pow-up balance-y t-comp)) + (x-dx-pow (pow-down (+ balance-x dx) t-comp)) + (add-term (+ x-pow y-pow)) + (term (if (<= add-term x-dx-pow) u0 (- add-term x-dx-pow))) + (final-term (pow-up term t-comp-num)) + ) + (if (<= balance-y final-term) u0 (- balance-y final-term)) + ) + ) +) +(define-private (get-x-given-y-internal (balance-x uint) (balance-y uint) (t uint) (dy uint)) + (if (>= t (var-get switch-threshold)) + (let + ( + (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t))) + ) + (div-down (+ (mul-down t-comp dy) (mul-down t (mul-down dy balance-x))) (+ t-comp (mul-down t (+ balance-y dy)))) + ) + (let + ( + (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t))) + (t-comp-num-uncapped (div-up ONE_8 t-comp)) + (t-comp-num (if (< t-comp-num-uncapped MILD_EXPONENT_BOUND) t-comp-num-uncapped MILD_EXPONENT_BOUND)) + (x-pow (pow-up balance-x t-comp)) + (y-pow (pow-up balance-y t-comp)) + (y-dy-pow (pow-down (+ balance-y dy) t-comp)) + (add-term (+ x-pow y-pow)) + (term (if (<= add-term y-dy-pow) u0 (- add-term y-dy-pow))) + (final-term (pow-up term t-comp-num)) + ) + (if (<= balance-x final-term) u0 (- balance-x final-term)) + ) + ) +) +(define-private (get-y-in-given-x-out-internal (balance-x uint) (balance-y uint) (t uint) (dx uint)) + (if (>= t (var-get switch-threshold)) + (let + ( + (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t))) + ) + (div-down (+ (mul-down t-comp dx) (mul-down t (mul-down dx balance-y))) (+ t-comp (mul-down t (- balance-x dx)))) + ) + (let + ( + (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t))) + (t-comp-num-uncapped (div-down ONE_8 t-comp)) + (t-comp-num (if (< t-comp-num-uncapped MILD_EXPONENT_BOUND) t-comp-num-uncapped MILD_EXPONENT_BOUND)) + (x-pow (pow-down balance-x t-comp)) + (y-pow (pow-down balance-y t-comp)) + (x-dx-pow (pow-up (if (<= balance-x dx) u0 (- balance-x dx)) t-comp)) + (add-term (+ x-pow y-pow)) + (term (if (<= add-term x-dx-pow) u0 (- add-term x-dx-pow))) + (final-term (pow-down term t-comp-num)) + ) + (if (<= final-term balance-y) u0 (- final-term balance-y)) + ) + ) +) +(define-private (get-x-in-given-y-out-internal (balance-x uint) (balance-y uint) (t uint) (dy uint)) + (if (>= t (var-get switch-threshold)) + (let + ( + (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t))) + ) + (div-down (+ (mul-down t-comp dy) (mul-down t (mul-down dy balance-x))) (+ t-comp (mul-down t (- balance-y dy)))) + ) + (let + ( + (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t))) + (t-comp-num-uncapped (div-down ONE_8 t-comp)) + (t-comp-num (if (< t-comp-num-uncapped MILD_EXPONENT_BOUND) t-comp-num-uncapped MILD_EXPONENT_BOUND)) + (x-pow (pow-down balance-x t-comp)) + (y-pow (pow-down balance-y t-comp)) + (y-dy-pow (pow-up (if (<= balance-y dy) u0 (- balance-y dy)) t-comp)) + (add-term (+ x-pow y-pow)) + (term (if (<= add-term y-dy-pow) u0 (- add-term y-dy-pow))) + (final-term (pow-down term t-comp-num)) + ) + (if (<= final-term balance-x) u0 (- final-term balance-x)) + ) + ) +) +(define-private (get-x-given-price-internal (balance-x uint) (balance-y uint) (t uint) (price uint)) + (if (>= t (var-get switch-threshold)) + (let + ( + (power (pow-down (div-down (get-price-internal balance-x balance-y t) price) u50000000)) + ) + (mul-down balance-x (if (<= power ONE_8) u0 (- power ONE_8))) + ) + (let + ( + (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t))) + (t-comp-num-uncapped (div-down ONE_8 t-comp)) + (t-comp-num (if (< t-comp-num-uncapped MILD_EXPONENT_BOUND) t-comp-num-uncapped MILD_EXPONENT_BOUND)) + (numer (+ ONE_8 (pow-down (get-price-internal balance-x balance-y t) (div-down t-comp t)))) + (denom (+ ONE_8 (pow-down price (div-down t-comp t)))) + (lead-term (pow-down (div-down numer denom) t-comp-num)) + ) + (if (<= lead-term ONE_8) u0 (mul-up balance-x (- lead-term ONE_8))) + ) + ) +) +(define-private (get-y-given-price-internal (balance-x uint) (balance-y uint) (t uint) (price uint)) + (if (>= t (var-get switch-threshold)) + (let + ( + (power (pow-down (div-down price (get-price-internal balance-x balance-y t)) u50000000)) + ) + (mul-down balance-y (if (<= power ONE_8) u0 (- power ONE_8))) + ) + (let + ( + (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t))) + (t-comp-num-uncapped (div-down ONE_8 t-comp)) + (t-comp-num (if (< t-comp-num-uncapped MILD_EXPONENT_BOUND) t-comp-num-uncapped MILD_EXPONENT_BOUND)) + (numer (+ ONE_8 (pow-down (get-price-internal balance-x balance-y t) (div-down t-comp t)))) + (denom (+ ONE_8 (pow-down price (div-down t-comp t)))) + (lead-term (pow-down (div-down numer denom) t-comp-num)) + ) + (if (<= ONE_8 lead-term) u0 (mul-up balance-y (- ONE_8 lead-term))) + ) + ) +) +(define-private (get-token-given-position-internal (balance-x uint) (balance-y uint) (t uint) (total-supply uint) (dx uint) (dy uint)) + (if (is-eq total-supply u0) + {token: (get-invariant dx dy t), dy: dy} + {token: (div-down (mul-down total-supply dx) balance-x), dy: (div-down (mul-down balance-y dx) balance-x)} + ) +) +(define-private (get-position-given-mint-internal (balance-x uint) (balance-y uint) (t uint) (total-supply uint) (token-amount uint)) + (let + ( + (token-div-supply (div-down token-amount total-supply)) + ) + {dx: (mul-down balance-x token-div-supply), dy: (mul-down balance-y token-div-supply)} + ) +) +(define-private (get-position-given-burn-internal (balance-x uint) (balance-y uint) (t uint) (total-supply uint) (token-amount uint)) + (get-position-given-mint-internal balance-x balance-y t total-supply token-amount) +) +(define-constant ONE_8 u100000000) ;; 8 decimal places +(define-constant MAX_POW_RELATIVE_ERROR u4) +(define-private (mul-down (a uint) (b uint)) + (/ (* a b) ONE_8) +) +(define-private (mul-up (a uint) (b uint)) + (let + ( + (product (* a b)) + ) + (if (is-eq product u0) + u0 + (+ u1 (/ (- product u1) ONE_8)) + ) + ) +) +(define-private (div-down (a uint) (b uint)) + (if (is-eq a u0) + u0 + (/ (* a ONE_8) b) + ) +) +(define-private (div-up (a uint) (b uint)) + (if (is-eq a u0) + u0 + (+ u1 (/ (- (* a ONE_8) u1) b)) + ) +) +(define-private (pow-down (a uint) (b uint)) + (let + ( + (raw (unwrap-panic (pow-fixed a b))) + (max-error (+ u1 (mul-up raw MAX_POW_RELATIVE_ERROR))) + ) + (if (< raw max-error) + u0 + (- raw max-error) + ) + ) +) +(define-private (pow-up (a uint) (b uint)) + (let + ( + (raw (unwrap-panic (pow-fixed a b))) + (max-error (+ u1 (mul-up raw MAX_POW_RELATIVE_ERROR))) + ) + (+ raw max-error) + ) +) +(define-constant UNSIGNED_ONE_8 (pow 10 8)) +(define-constant MAX_NATURAL_EXPONENT (* 69 UNSIGNED_ONE_8)) +(define-constant MIN_NATURAL_EXPONENT (* -18 UNSIGNED_ONE_8)) +(define-constant MILD_EXPONENT_BOUND (/ (pow u2 u126) (to-uint UNSIGNED_ONE_8))) +(define-constant x_a_list_no_deci (list {x_pre: 6400000000, a_pre: 62351490808116168829, use_deci: false} )) +(define-constant x_a_list (list +{x_pre: 3200000000, a_pre: 78962960182680695161, use_deci: true} ;; x2 = 2^5, a2 = e^(x2) +{x_pre: 1600000000, a_pre: 888611052050787, use_deci: true} ;; x3 = 2^4, a3 = e^(x3) +{x_pre: 800000000, a_pre: 298095798704, use_deci: true} ;; x4 = 2^3, a4 = e^(x4) +{x_pre: 400000000, a_pre: 5459815003, use_deci: true} ;; x5 = 2^2, a5 = e^(x5) +{x_pre: 200000000, a_pre: 738905610, use_deci: true} ;; x6 = 2^1, a6 = e^(x6) +{x_pre: 100000000, a_pre: 271828183, use_deci: true} ;; x7 = 2^0, a7 = e^(x7) +{x_pre: 50000000, a_pre: 164872127, use_deci: true} ;; x8 = 2^-1, a8 = e^(x8) +{x_pre: 25000000, a_pre: 128402542, use_deci: true} ;; x9 = 2^-2, a9 = e^(x9) +{x_pre: 12500000, a_pre: 113314845, use_deci: true} ;; x10 = 2^-3, a10 = e^(x10) +{x_pre: 6250000, a_pre: 106449446, use_deci: true} ;; x11 = 2^-4, a11 = e^x(11) +)) +(define-constant ERR-X-OUT-OF-BOUNDS (err u5009)) +(define-constant ERR-Y-OUT-OF-BOUNDS (err u5010)) +(define-constant ERR-PRODUCT-OUT-OF-BOUNDS (err u5011)) +(define-constant ERR-INVALID-EXPONENT (err u5012)) +(define-constant ERR-OUT-OF-BOUNDS (err u5013)) +(define-private (ln-priv (a int)) + (let + ( + (a_sum_no_deci (fold accumulate_division x_a_list_no_deci {a: a, sum: 0})) + (a_sum (fold accumulate_division x_a_list {a: (get a a_sum_no_deci), sum: (get sum a_sum_no_deci)})) + (out_a (get a a_sum)) + (out_sum (get sum a_sum)) + (z (/ (* (- out_a UNSIGNED_ONE_8) UNSIGNED_ONE_8) (+ out_a UNSIGNED_ONE_8))) + (z_squared (/ (* z z) UNSIGNED_ONE_8)) + (div_list (list 3 5 7 9 11)) + (num_sum_zsq (fold rolling_sum_div div_list {num: z, seriesSum: z, z_squared: z_squared})) + (seriesSum (get seriesSum num_sum_zsq)) + ) + (+ out_sum (* seriesSum 2)) + ) +) +(define-private (accumulate_division (x_a_pre (tuple (x_pre int) (a_pre int) (use_deci bool))) (rolling_a_sum (tuple (a int) (sum int)))) + (let + ( + (a_pre (get a_pre x_a_pre)) + (x_pre (get x_pre x_a_pre)) + (use_deci (get use_deci x_a_pre)) + (rolling_a (get a rolling_a_sum)) + (rolling_sum (get sum rolling_a_sum)) + ) + (if (>= rolling_a (if use_deci a_pre (* a_pre UNSIGNED_ONE_8))) + {a: (/ (* rolling_a (if use_deci UNSIGNED_ONE_8 1)) a_pre), sum: (+ rolling_sum x_pre)} + {a: rolling_a, sum: rolling_sum} + ) + ) +) +(define-private (rolling_sum_div (n int) (rolling (tuple (num int) (seriesSum int) (z_squared int)))) + (let + ( + (rolling_num (get num rolling)) + (rolling_sum (get seriesSum rolling)) + (z_squared (get z_squared rolling)) + (next_num (/ (* rolling_num z_squared) UNSIGNED_ONE_8)) + (next_sum (+ rolling_sum (/ next_num n))) + ) + {num: next_num, seriesSum: next_sum, z_squared: z_squared} + ) +) +(define-private (pow-priv (x uint) (y uint)) + (let + ( + (x-int (to-int x)) + (y-int (to-int y)) + (lnx (ln-priv x-int)) + (logx-times-y (/ (* lnx y-int) UNSIGNED_ONE_8)) + ) + (asserts! (and (<= MIN_NATURAL_EXPONENT logx-times-y) (<= logx-times-y MAX_NATURAL_EXPONENT)) ERR-PRODUCT-OUT-OF-BOUNDS) + (ok (to-uint (try! (exp-fixed logx-times-y)))) + ) +) +(define-private (exp-pos (x int)) + (begin + (asserts! (and (<= 0 x) (<= x MAX_NATURAL_EXPONENT)) ERR-INVALID-EXPONENT) + (let + ( + (x_product_no_deci (fold accumulate_product x_a_list_no_deci {x: x, product: 1})) + (x_adj (get x x_product_no_deci)) + (firstAN (get product x_product_no_deci)) + (x_product (fold accumulate_product x_a_list {x: x_adj, product: UNSIGNED_ONE_8})) + (product_out (get product x_product)) + (x_out (get x x_product)) + (seriesSum (+ UNSIGNED_ONE_8 x_out)) + (div_list (list 2 3 4 5 6 7 8 9 10 11 12)) + (term_sum_x (fold rolling_div_sum div_list {term: x_out, seriesSum: seriesSum, x: x_out})) + (sum (get seriesSum term_sum_x)) + ) + (ok (* (/ (* product_out sum) UNSIGNED_ONE_8) firstAN)) + ) + ) +) +(define-private (accumulate_product (x_a_pre (tuple (x_pre int) (a_pre int) (use_deci bool))) (rolling_x_p (tuple (x int) (product int)))) + (let + ( + (x_pre (get x_pre x_a_pre)) + (a_pre (get a_pre x_a_pre)) + (use_deci (get use_deci x_a_pre)) + (rolling_x (get x rolling_x_p)) + (rolling_product (get product rolling_x_p)) + ) + (if (>= rolling_x x_pre) + {x: (- rolling_x x_pre), product: (/ (* rolling_product a_pre) (if use_deci UNSIGNED_ONE_8 1))} + {x: rolling_x, product: rolling_product} + ) + ) +) +(define-private (rolling_div_sum (n int) (rolling (tuple (term int) (seriesSum int) (x int)))) + (let + ( + (rolling_term (get term rolling)) + (rolling_sum (get seriesSum rolling)) + (x (get x rolling)) + (next_term (/ (/ (* rolling_term x) UNSIGNED_ONE_8) n)) + (next_sum (+ rolling_sum next_term)) + ) + {term: next_term, seriesSum: next_sum, x: x} + ) +) +(define-private (pow-fixed (x uint) (y uint)) + (begin + (asserts! (< x (pow u2 u127)) ERR-X-OUT-OF-BOUNDS) + (asserts! (< y MILD_EXPONENT_BOUND) ERR-Y-OUT-OF-BOUNDS) + (if (is-eq y u0) + (ok (to-uint UNSIGNED_ONE_8)) + (if (is-eq x u0) + (ok u0) + (pow-priv x y) + ) + ) + ) +) +(define-private (exp-fixed (x int)) + (begin + (asserts! (and (<= MIN_NATURAL_EXPONENT x) (<= x MAX_NATURAL_EXPONENT)) ERR-INVALID-EXPONENT) + (if (< x 0) + (ok (/ (* UNSIGNED_ONE_8 UNSIGNED_ONE_8) (try! (exp-pos (* -1 x))))) + (exp-pos x) + ) + ) +) +(define-private (log-fixed (arg int) (base int)) + (let + ( + (logBase (* (ln-priv base) UNSIGNED_ONE_8)) + (logArg (* (ln-priv arg) UNSIGNED_ONE_8)) + ) + (ok (/ (* logArg UNSIGNED_ONE_8) logBase)) + ) +) +(define-private (ln-fixed (a int)) + (begin + (asserts! (> a 0) ERR-OUT-OF-BOUNDS) + (if (< a UNSIGNED_ONE_8) + (ok (- 0 (ln-priv (/ (* UNSIGNED_ONE_8 UNSIGNED_ONE_8) a)))) + (ok (ln-priv a)) + ) + ) +) \ No newline at end of file diff --git a/sbtc-ops/smart-contract/contracts/bridge-contract.clar b/sbtc-ops/smart-contract/contracts/bridge-contract.clar new file mode 100644 index 00000000..ffbd29f9 --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/bridge-contract.clar @@ -0,0 +1,116 @@ +;; exhchange utilities +(use-trait ft-trait .trait-sip-010.sip-010-trait) + +(define-map approved-tokens {token: principal} {approved: bool}) + +(define-constant err-forbidden (err u100)) +(define-constant err-token-not-approved (err u999)) + +(define-constant contract-admin tx-sender) +(define-constant ONE_8 u100000000) + +(map-set approved-tokens {token: .token-wbtc} {approved: true}) +(map-set approved-tokens {token: .token-wstx} {approved: true}) + +(define-private (to-one-8 (a uint)) + (* a ONE_8)) + +(define-private (mul-down (a uint) (b uint)) + (/ (* a b) ONE_8)) + +(define-private (div-down (a uint) (b uint)) + (if (is-eq a u0) + u0 + (/ (* a ONE_8) b))) + +(define-private (minus-percent (a uint) (percent uint)) + (if (is-eq a u0) + u0 + (/ (- (* a u100) (* a percent)) u100))) + +;; public to check return btc-to-stx amount +;; input: 'SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.token-wbtc 'SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.token-wstx uN (xBTC or STX) * 10^8 u5 (5%) +(define-public (swap-helper (token-x-trait ) (token-y-trait ) (multiplied-amount uint) (slippeage uint)) + (let ( + (token-x (contract-of token-x-trait)) + (token-y (contract-of token-y-trait)) + (fee-amount + (contract-call? .amm-swap-pool-v1-1 fee-helper token-x token-y ONE_8)) + (get-helper-result (try! (contract-call? .amm-swap-pool-v1-1 get-helper token-x token-y ONE_8 multiplied-amount))) + (converted-amount + (mul-down + get-helper-result + (- ONE_8 (unwrap-panic fee-amount)))) + (converted-amount-slippeage (minus-percent converted-amount slippeage))) + (asserts! + (and + (get-approved-token token-x) + (get-approved-token token-y)) + err-token-not-approved) + (ok (contract-call? + .amm-swap-pool-v1-1 swap-helper + token-x-trait + token-y-trait + ONE_8 + multiplied-amount + (some converted-amount-slippeage))))) + +(define-public (swap-bridge-stx-btc + (token-x-trait ) + (token-y-trait ) + (multiplied-amount uint) + (slippeage uint) + (btc-version (buff 1)) + (btc-hash (buff 20)) + (supplier-id uint)) + (let ( + (token-x (contract-of token-x-trait)) + (token-y (contract-of token-y-trait)) + (fee-amount + (contract-call? .amm-swap-pool-v1-1 fee-helper token-x token-y ONE_8)) + (get-helper-result (try! (contract-call? .amm-swap-pool-v1-1 get-helper token-x token-y ONE_8 multiplied-amount))) + (xbtc-amount + (mul-down + get-helper-result + (- ONE_8 (unwrap-panic fee-amount)))) + (xbtc-amount-slippeage (minus-percent xbtc-amount slippeage)) + (swap-result + (try! (contract-call? + .amm-swap-pool-v1-1 swap-helper + token-x-trait + token-y-trait + ONE_8 + multiplied-amount + (some xbtc-amount-slippeage))))) + (try! + (contract-call? .degen-bridge-testnet-v3 initiate-outbound-swap + swap-result + btc-version + btc-hash + supplier-id)) + (ok swap-result))) + +(define-public (swap-preview (token-x-trait ) (token-y-trait ) (multiplied-amount uint) (slippeage uint)) + (let ( + (token-x (contract-of token-x-trait)) + (token-y (contract-of token-y-trait)) + (fee-amount + (contract-call? .amm-swap-pool-v1-1 fee-helper token-x token-y ONE_8)) + (get-helper-result (try! (contract-call? .amm-swap-pool-v1-1 get-helper token-x token-y ONE_8 multiplied-amount))) + (converted-amount + (mul-down + get-helper-result + (- ONE_8 (unwrap-panic fee-amount)))) + (converted-amount-slippeage (minus-percent converted-amount slippeage))) + (ok converted-amount))) + +(define-public (set-approved-token (token principal)) +(begin + (asserts! (is-eq contract-caller contract-admin) err-forbidden) + (ok (map-set approved-tokens {token: token} {approved: true})))) + +(define-read-only (get-approved-token (token principal)) +(begin + (default-to false + (get approved + (map-get? approved-tokens {token: token}))))) \ No newline at end of file diff --git a/sbtc-ops/smart-contract/contracts/clarity-bitcoin.clar b/sbtc-ops/smart-contract/contracts/clarity-bitcoin.clar new file mode 100644 index 00000000..3db86897 --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/clarity-bitcoin.clar @@ -0,0 +1,811 @@ +;; Error codes +(define-constant ERR-OUT-OF-BOUNDS u1) +(define-constant ERR-TOO-MANY-TXINS u2) +(define-constant ERR-TOO-MANY-TXOUTS u3) +(define-constant ERR-VARSLICE-TOO-LONG u4) +(define-constant ERR-BAD-HEADER u5) +(define-constant ERR-PROOF-TOO-SHORT u6) +(define-constant ERR-INVALID-PARENT u7) + +;; lookup table for converting 1-byte buffers to uints via index-of +(define-constant BUFF_TO_BYTE (list + 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f + 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e 0x1f + 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x2a 0x2b 0x2c 0x2d 0x2e 0x2f + 0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x3a 0x3b 0x3c 0x3d 0x3e 0x3f + 0x40 0x41 0x42 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4a 0x4b 0x4c 0x4d 0x4e 0x4f + 0x50 0x51 0x52 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5a 0x5b 0x5c 0x5d 0x5e 0x5f + 0x60 0x61 0x62 0x63 0x64 0x65 0x66 0x67 0x68 0x69 0x6a 0x6b 0x6c 0x6d 0x6e 0x6f + 0x70 0x71 0x72 0x73 0x74 0x75 0x76 0x77 0x78 0x79 0x7a 0x7b 0x7c 0x7d 0x7e 0x7f + 0x80 0x81 0x82 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8a 0x8b 0x8c 0x8d 0x8e 0x8f + 0x90 0x91 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9a 0x9b 0x9c 0x9d 0x9e 0x9f + 0xa0 0xa1 0xa2 0xa3 0xa4 0xa5 0xa6 0xa7 0xa8 0xa9 0xaa 0xab 0xac 0xad 0xae 0xaf + 0xb0 0xb1 0xb2 0xb3 0xb4 0xb5 0xb6 0xb7 0xb8 0xb9 0xba 0xbb 0xbc 0xbd 0xbe 0xbf + 0xc0 0xc1 0xc2 0xc3 0xc4 0xc5 0xc6 0xc7 0xc8 0xc9 0xca 0xcb 0xcc 0xcd 0xce 0xcf + 0xd0 0xd1 0xd2 0xd3 0xd4 0xd5 0xd6 0xd7 0xd8 0xd9 0xda 0xdb 0xdc 0xdd 0xde 0xdf + 0xe0 0xe1 0xe2 0xe3 0xe4 0xe5 0xe6 0xe7 0xe8 0xe9 0xea 0xeb 0xec 0xed 0xee 0xef + 0xf0 0xf1 0xf2 0xf3 0xf4 0xf5 0xf6 0xf7 0xf8 0xf9 0xfa 0xfb 0xfc 0xfd 0xfe 0xff +)) + +;; List with 512 items, used for folding something 512 times +(define-constant LIST_512 (list + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true +)) + +;; List with 256 items, used for folding something 256 times +(define-constant LIST_256 (list + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true +)) + +;; List with 128 items, used for folding something 128 times +(define-constant LIST_128 (list + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true +)) + +;; List with 64 items, used for folding something 64 times +(define-constant LIST_64 (list + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true +)) + +;; List with 32 items, used for folding something 32 times +(define-constant LIST_32 (list + true true true true true true true true true true true true true true true true + true true true true true true true true true true true true true true true true +)) + +;; List with 16 items, used for folding something 16 times +(define-constant LIST_16 (list + true true true true true true true true true true true true true true true true +)) + +;; Convert a 1-byte buff into a uint. +(define-read-only (buff-to-u8 (byte (buff 1))) + (unwrap-panic (index-of BUFF_TO_BYTE byte))) + +;; Append a byte at the given index in the given data to acc. +(define-read-only (inner-read-slice-1024 (ignored bool) (input { acc: (buff 1024), data: (buff 1024), index: uint })) + (let ( + (acc (get acc input)) + (data (get data input)) + (ctr (get index input)) + (byte (unwrap-panic (element-at data ctr))) + ) + { + acc: (unwrap-panic (as-max-len? (concat acc byte) u1024)), + data: data, + index: (+ u1 ctr) + }) +) + +;; Read 512 bytes from data, starting at index. Return the 512-byte slice. +(define-read-only (read-slice-512 (input { data: (buff 1024), index: uint })) + (get acc + (fold inner-read-slice-1024 LIST_512 { acc: 0x, data: (get data input), index: (get index input) }))) + +;; Read 256 bytes from data, starting at index. Return the 256-byte slice. +(define-read-only (read-slice-256 (input { data: (buff 1024), index: uint })) + (get acc + (fold inner-read-slice-1024 LIST_256 { acc: 0x, data: (get data input), index: (get index input) }))) + +;; Read 128 bytes from data, starting at index. Return the 128-byte slice. +(define-read-only (read-slice-128 (input { data: (buff 1024), index: uint })) + (get acc + (fold inner-read-slice-1024 LIST_128 { acc: 0x, data: (get data input), index: (get index input) }))) + +;; Read 64 bytes from data, starting at index. Return the 64-byte slice. +(define-read-only (read-slice-64 (input { data: (buff 1024), index: uint })) + (get acc + (fold inner-read-slice-1024 LIST_64 { acc: 0x, data: (get data input), index: (get index input) }))) + +;; Read 32 bytes from data, starting at index. Return the 32-byte slice. +(define-read-only (read-slice-32 (input { data: (buff 1024), index: uint })) + (get acc + (fold inner-read-slice-1024 LIST_32 { acc: 0x, data: (get data input), index: (get index input) }))) + +;; Read 16 bytes from data, starting at index. Return the 16-byte slice. +(define-read-only (read-slice-16 (input { data: (buff 1024), index: uint })) + (get acc + (fold inner-read-slice-1024 LIST_16 { acc: 0x, data: (get data input), index: (get index input) }))) + +;; Read 8 bytes from data, starting at index. Return the 8-byte slice. +(define-read-only (read-slice-8 (input { data: (buff 1024), index: uint })) + (get acc + (fold inner-read-slice-1024 (list true true true true true true true true) { acc: 0x, data: (get data input), index: (get index input) }))) + +;; Read 4 bytes from data, starting at index. Return the 4-byte slice. +(define-read-only (read-slice-4 (input { data: (buff 1024), index: uint })) + (get acc + (fold inner-read-slice-1024 (list true true true true) { acc: 0x, data: (get data input), index: (get index input) }))) + +;; Read 2 bytes from data, starting at index. Return the 2-byte slice. +(define-read-only (read-slice-2 (input { data: (buff 1024), index: uint })) + (get acc + (fold inner-read-slice-1024 (list true true) { acc: 0x, data: (get data input), index: (get index input) }))) + +;; Read 1 byte from data, starting at index. Return the 1-byte slice. +(define-read-only (read-slice-1 (input { data: (buff 1024), index: uint })) + (get acc + (fold inner-read-slice-1024 (list true) { acc: 0x, data: (get data input), index: (get index input) }))) + +;; Read a fixed-sized chunk of data from a given buffer (up to remaining bytes), starting at index, and append it to acc. +;; chunk_size must be a power of 2, up to 1024 +(define-read-only (inner-read-slice (chunk_size uint) (input { acc: (buff 1024), buffer: (buff 1024), index: uint, remaining: uint })) + (let ( + (ctr (get index input)) + (remaining (get remaining input)) + ) + (if (is-eq u0 remaining) + ;; done reading + input + (let ( + (acc (get acc input)) + (databuff (get buffer input)) + ) + (if (> chunk_size remaining) + ;; chunk size too big for remainder, so just skip it. + input + ;; we have at least chunk_size bytes to read! + ;; dispatch to the right fixed-size slice reader. + (if (is-eq chunk_size u512) + { acc: (unwrap-panic (as-max-len? (concat acc (read-slice-512 { data: databuff, index: ctr })) u1024)), buffer: databuff, index: (+ chunk_size ctr), remaining: (- remaining chunk_size) } + (if (is-eq chunk_size u256) + { acc: (unwrap-panic (as-max-len? (concat acc (read-slice-256 { data: databuff, index: ctr })) u1024)), buffer: databuff, index: (+ chunk_size ctr), remaining: (- remaining chunk_size) } + (if (is-eq chunk_size u128) + { acc: (unwrap-panic (as-max-len? (concat acc (read-slice-128 { data: databuff, index: ctr })) u1024)), buffer: databuff, index: (+ chunk_size ctr), remaining: (- remaining chunk_size) } + (if (is-eq chunk_size u64) + { acc: (unwrap-panic (as-max-len? (concat acc (read-slice-64 { data: databuff, index: ctr })) u1024)), buffer: databuff, index: (+ chunk_size ctr), remaining: (- remaining chunk_size) } + (if (is-eq chunk_size u32) + { acc: (unwrap-panic (as-max-len? (concat acc (read-slice-32 { data: databuff, index: ctr })) u1024)), buffer: databuff, index: (+ chunk_size ctr), remaining: (- remaining chunk_size) } + (if (is-eq chunk_size u16) + { acc: (unwrap-panic (as-max-len? (concat acc (read-slice-16 { data: databuff, index: ctr })) u1024)), buffer: databuff, index: (+ chunk_size ctr), remaining: (- remaining chunk_size) } + (if (is-eq chunk_size u8) + { acc: (unwrap-panic (as-max-len? (concat acc (read-slice-8 { data: databuff, index: ctr })) u1024)), buffer: databuff, index: (+ chunk_size ctr), remaining: (- remaining chunk_size) } + (if (is-eq chunk_size u4) + { acc: (unwrap-panic (as-max-len? (concat acc (read-slice-4 { data: databuff, index: ctr })) u1024)), buffer: databuff, index: (+ chunk_size ctr), remaining: (- remaining chunk_size) } + (if (is-eq chunk_size u2) + { acc: (unwrap-panic (as-max-len? (concat acc (read-slice-2 { data: databuff, index: ctr })) u1024)), buffer: databuff, index: (+ chunk_size ctr), remaining: (- remaining chunk_size) } + (if (is-eq chunk_size u1) + { acc: (unwrap-panic (as-max-len? (concat acc (read-slice-1 { data: databuff, index: ctr })) u1024)), buffer: databuff, index: (+ chunk_size ctr), remaining: (- remaining chunk_size) } + { acc: acc, buffer: databuff, index: ctr, remaining: remaining } + )))))))))) + )) + )) +) + +;; Top-level function to read a slice of a given size from a given (buff 1024), starting at a given offset. +;; Returns (ok (buff 1024)) on success, and it contains "buff[offset..(offset+size)]" +;; Returns (err ERR-OUT-OF-BOUNDS) if the slice offset and/or size would copy a range of bytes outside the given buffer. +(define-read-only (read-slice (data (buff 1024)) (offset uint) (size uint)) + (if (or (>= offset (len data)) (> (+ offset size) (len data))) + (err ERR-OUT-OF-BOUNDS) + (begin + ;; (print "read slice") + ;; (print size) + (ok + (get acc + (fold inner-read-slice (list u512 u256 u128 u64 u32 u16 u8 u4 u2 u1) { acc: 0x, buffer: data, index: offset, remaining: size })) + ) + ) + ) +) + +;; Reads the next two bytes from txbuff as a big-endian 16-bit integer, and updates the index. +;; Returns (ok { uint16: uint, ctx: { txbuff: (buff 1024), index: uint } }) on success. +;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff +(define-read-only (read-uint16 (ctx { txbuff: (buff 1024), index: uint })) + (let ( + (data (get txbuff ctx)) + (base (get index ctx)) + (byte-1 (buff-to-u8 (unwrap! (element-at data base) (err ERR-OUT-OF-BOUNDS)))) + (byte-2 (buff-to-u8 (unwrap! (element-at data (+ u1 base)) (err ERR-OUT-OF-BOUNDS)))) + (ret (+ (* byte-2 u256) byte-1)) + ) + (begin + ;; (print "read uint16") + ;; (print ret) + (ok { + uint16: ret, + ctx: { txbuff: data, index: (+ u2 base) } + }) + )) +) + +;; Reads the next four bytes from txbuff as a big-endian 32-bit integer, and updates the index. +;; Returns (ok { uint32: uint, ctx: { txbuff: (buff 1024), index: uint } }) on success. +;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff +(define-read-only (read-uint32 (ctx { txbuff: (buff 1024), index: uint })) + (let ( + (data (get txbuff ctx)) + (base (get index ctx)) + (byte-1 (buff-to-u8 (unwrap! (element-at data base) (err ERR-OUT-OF-BOUNDS)))) + (byte-2 (buff-to-u8 (unwrap! (element-at data (+ u1 base)) (err ERR-OUT-OF-BOUNDS)))) + (byte-3 (buff-to-u8 (unwrap! (element-at data (+ u2 base)) (err ERR-OUT-OF-BOUNDS)))) + (byte-4 (buff-to-u8 (unwrap! (element-at data (+ u3 base)) (err ERR-OUT-OF-BOUNDS)))) + (ret (+ (* byte-4 u16777216) (* byte-3 u65536) (* byte-2 u256) byte-1)) + ) + (begin + ;; (print "read uint32") + ;; (print ret) + (ok { + uint32: ret, + ctx: { txbuff: data, index: (+ u4 base) } + }) + )) +) + +;; Reads the next eight bytes from txbuff as a big-endian 64-bit integer, and updates the index. +;; Returns (ok { uint64: uint, ctx: { txbuff: (buff 1024), index: uint } }) on success. +;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff +(define-read-only (read-uint64 (ctx { txbuff: (buff 1024), index: uint })) + (let ( + (data (get txbuff ctx)) + (base (get index ctx)) + (byte-1 (buff-to-u8 (unwrap! (element-at data base) (err ERR-OUT-OF-BOUNDS)))) + (byte-2 (buff-to-u8 (unwrap! (element-at data (+ u1 base)) (err ERR-OUT-OF-BOUNDS)))) + (byte-3 (buff-to-u8 (unwrap! (element-at data (+ u2 base)) (err ERR-OUT-OF-BOUNDS)))) + (byte-4 (buff-to-u8 (unwrap! (element-at data (+ u3 base)) (err ERR-OUT-OF-BOUNDS)))) + (byte-5 (buff-to-u8 (unwrap! (element-at data (+ u4 base)) (err ERR-OUT-OF-BOUNDS)))) + (byte-6 (buff-to-u8 (unwrap! (element-at data (+ u5 base)) (err ERR-OUT-OF-BOUNDS)))) + (byte-7 (buff-to-u8 (unwrap! (element-at data (+ u6 base)) (err ERR-OUT-OF-BOUNDS)))) + (byte-8 (buff-to-u8 (unwrap! (element-at data (+ u7 base)) (err ERR-OUT-OF-BOUNDS)))) + (ret (+ + (* byte-8 u72057594037927936) + (* byte-7 u281474976710656) + (* byte-6 u1099511627776) + (* byte-5 u4294967296) + (* byte-4 u16777216) + (* byte-3 u65536) + (* byte-2 u256) + byte-1)) + ) + (begin + ;; (print "read uint64") + ;; (print ret) + (ok { + uint64: ret, + ctx: { txbuff: data, index: (+ u8 base) } + }) + )) +) + +;; Reads the next varint from txbuff, and updates the index. +;; Returns (ok { varint: uint, ctx: { txbuff: (buff 1024), index: uint } }) on success +;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff. +(define-read-only (read-varint (ctx { txbuff: (buff 1024), index: uint })) + (let ( + (ptr (get index ctx)) + (tx (get txbuff ctx)) + (byte (buff-to-u8 (unwrap! (element-at tx ptr) + (err ERR-OUT-OF-BOUNDS)))) + ) + (if (<= byte u252) + (begin + ;; (print "varint 1") + ;; (print byte) + ;; given byte is the varint + (ok { varint: byte, ctx: { txbuff: tx, index: (+ u1 ptr) }}) + ) + (if (is-eq byte u253) + (let ( + ;; next two bytes is the varint + (parsed-u16 (try! (read-uint16 { txbuff: tx, index: (+ u1 ptr) }))) + ) + (begin + ;; (print "varint 2") + ;; (print (get uint16 parsed-u16)) + (ok { varint: (get uint16 parsed-u16), ctx: (get ctx parsed-u16) }) + )) + (if (is-eq byte u254) + (let ( + ;; next four bytes is the varint + (parsed-u32 (try! (read-uint32 { txbuff: tx, index: (+ u1 ptr) }))) + ) + (begin + ;; (print "varint 4") + ;; (print (get uint32 parsed-u32)) + (ok { varint: (get uint32 parsed-u32), ctx: (get ctx parsed-u32) }) + )) + (let ( + ;; next eight bytes is the varint + (parsed-u64 (try! (read-uint64 { txbuff: tx, index: (+ u1 ptr) }))) + ) + (begin + ;; (print "varint 8") + ;; (print (get uint64 parsed-u64)) + (ok { varint: (get uint64 parsed-u64), ctx: (get ctx parsed-u64) }) + )) + ) + ) + )) +) + +;; Reads a varint-prefixed byte slice from txbuff, and updates the index to point to the byte after the varint and slice. +;; Returns (ok { varslice: (buff 1024), ctx: { txbuff: (buff 1024), index: uint } }) on success, where varslice has the length of the varint prefix. +;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff. +(define-read-only (read-varslice (old-ctx { txbuff: (buff 1024), index: uint })) + (let ( + (parsed (try! (read-varint old-ctx))) + (slice-len (get varint parsed)) + (ctx (get ctx parsed)) + (slice-2 (try! (read-slice (get txbuff ctx) (get index ctx) slice-len))) + ) + (ok { + varslice: slice-2, + ctx: { txbuff: (get txbuff ctx), index: (+ (len slice-2) (get index ctx)) } + })) +) + +;; Generate a permutation of a given 32-byte buffer, appending the element at target-index to hash-output. +;; The target-index decides which index in hash-input gets appended to hash-output. +(define-read-only (inner-buff32-permutation (target-index uint) (state { hash-input: (buff 32), hash-output: (buff 32) })) + { + hash-input: (get hash-input state), + hash-output: (unwrap-panic + (as-max-len? (concat + (get hash-output state) + (unwrap-panic + (as-max-len? + (unwrap-panic + (element-at (get hash-input state) target-index)) + u32))) + u32)) + } +) + +;; Reverse the byte order of a 32-byte buffer. Returns the (buff 32). +(define-read-only (reverse-buff32 (input (buff 32))) + (get hash-output + (fold inner-buff32-permutation + (list u31 u30 u29 u28 u27 u26 u25 u24 u23 u22 u21 u20 u19 u18 u17 u16 u15 u14 u13 u12 u11 u10 u9 u8 u7 u6 u5 u4 u3 u2 u1 u0) + { hash-input: input, hash-output: 0x })) +) + +;; Reads a big-endian hash -- consume the next 32 bytes, and reverse them. +;; Returns (ok { hashslice: (buff 32), ctx: { txbuff: (buff 1024), index: uint } }) on success, and updates the index. +;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff. +(define-read-only (read-hashslice (old-ctx { txbuff: (buff 1024), index: uint })) + (let ( + (hash-le (unwrap-panic + (as-max-len? (try! + (read-slice (get txbuff old-ctx) (get index old-ctx) u32)) + u32))) + ) + (ok { + hashslice: (reverse-buff32 hash-le), + ctx: { txbuff: (get txbuff old-ctx), index: (+ u32 (get index old-ctx)) } + })) +) + +;; Inner fold method to read the next tx input from txbuff. +;; The index in ctx will be updated to point to the next tx input if all goes well (or to the start of the outputs) +;; Returns (ok { ... }) on success. +;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff. +;; Returns (err ERR-VARSLICE-TOO-LONG) if we find a scriptSig that's too long to parse. +;; Returns (err ERR-TOO-MANY-TXINS) if there are more than eight inputs to read. +(define-read-only (read-next-txin (ignored bool) + (state-res (response { + ctx: { txbuff: (buff 1024), index: uint }, + remaining: uint, + txins: (list 8 { + outpoint: { + hash: (buff 32), + index: uint + }, + scriptSig: (buff 256), ;; just big enough to hold a 2-of-3 multisig script + sequence: uint + }) + } uint))) + (match state-res + state + (if (< u0 (get remaining state)) + (let ( + (remaining (get remaining state)) + (ctx (get ctx state)) + (parsed-hash (try! (read-hashslice ctx))) + (parsed-index (try! (read-uint32 (get ctx parsed-hash)))) + (parsed-scriptSig (try! (read-varslice (get ctx parsed-index)))) + (parsed-sequence (try! (read-uint32 (get ctx parsed-scriptSig)))) + (new-ctx (get ctx parsed-sequence)) + ) + (ok { + ctx: new-ctx, + remaining: (- remaining u1), + txins: (unwrap! + (as-max-len? + (append (get txins state) + { + outpoint: { + hash: (get hashslice parsed-hash), + index: (get uint32 parsed-index) + }, + scriptSig: (unwrap! (as-max-len? (get varslice parsed-scriptSig) u256) (err ERR-VARSLICE-TOO-LONG)), + sequence: (get uint32 parsed-sequence) + }) + u8) + (err ERR-TOO-MANY-TXINS)) + })) + (ok state) + ) + error + (err error) + ) +) + +;; Read a transaction's inputs. +;; Returns (ok { txins: (list { ... }), remaining: uint, ctx: { txbuff: (buff 1024), index: uint } }) on success, and updates the index in ctx to point to the start of the tx outputs. +;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff. +;; Returns (err ERR-VARSLICE-TOO-LONG) if we find a scriptSig that's too long to parse. +;; Returns (err ERR-TOO-MANY-TXINS) if there are more than eight inputs to read. +(define-read-only (read-txins (ctx { txbuff: (buff 1024), index: uint })) + (let ( + (parsed-num-txins (try! (read-varint ctx))) + (num-txins (get varint parsed-num-txins)) + (new-ctx (get ctx parsed-num-txins)) + ) + (if (> num-txins u8) + (err ERR-TOO-MANY-TXINS) + (fold read-next-txin (list true true true true true true true true) (ok { ctx: new-ctx, remaining: num-txins, txins: (list ) })) + )) +) + +;; Read the next transaction output, and update the index in ctx to point to the next output. +;; Returns (ok { ... }) on success +;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff. +;; Returns (err ERR-VARSLICE-TOO-LONG) if we find a scriptPubKey that's too long to parse. +;; Returns (err ERR-TOO-MANY-TXOUTS) if there are more than eight outputs to read. +(define-read-only (read-next-txout (ignored bool) + (state-res (response { + ctx: { txbuff: (buff 1024), index: uint }, + remaining: uint, + txouts: (list 8 { + value: uint, + scriptPubKey: (buff 128) + }) + } uint))) + (match state-res + state + (if (< u0 (get remaining state)) + (let ( + (remaining (get remaining state)) + (parsed-value (try! (read-uint64 (get ctx state)))) + (parsed-script (try! (read-varslice (get ctx parsed-value)))) + (new-ctx (get ctx parsed-script)) + ) + (ok { + ctx: new-ctx, + remaining: (- remaining u1), + txouts: (unwrap! + (as-max-len? + (append (get txouts state) + { + value: (get uint64 parsed-value), + scriptPubKey: (unwrap! (as-max-len? (get varslice parsed-script) u128) (err ERR-VARSLICE-TOO-LONG)) + }) + u8) + (err ERR-TOO-MANY-TXOUTS)) + })) + (ok state) + ) + error + (err error) + ) +) + +;; Read all transaction outputs in a transaction. Update the index to point to the first byte after the outputs, if all goes well. +;; Returns (ok { txouts: (list { ... }), remaining: uint, ctx: { txbuff: (buff 1024), index: uint } }) on success, and updates the index in ctx to point to the start of the tx outputs. +;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff. +;; Returns (err ERR-VARSLICE-TOO-LONG) if we find a scriptPubKey that's too long to parse. +;; Returns (err ERR-TOO-MANY-TXOUTS) if there are more than eight outputs to read. +(define-read-only (read-txouts (ctx { txbuff: (buff 1024), index: uint })) + (let ( + (parsed-num-txouts (try! (read-varint ctx))) + (num-txouts (get varint parsed-num-txouts)) + (new-ctx (get ctx parsed-num-txouts)) + ) + (if (> num-txouts u8) + (err ERR-TOO-MANY-TXOUTS) + (fold read-next-txout (list true true true true true true true true) (ok { ctx: new-ctx, remaining: num-txouts, txouts: (list ) })) + )) +) + +;; Parse a Bitcoin transaction, with up to 8 inputs and 8 outputs, with scriptSigs of up to 256 bytes each, and with scriptPubKeys up to 128 bytes. +;; Returns a tuple structured as follows on success: +;; (ok { +;; version: uint, ;; tx version +;; ins: (list 8 +;; { +;; outpoint: { ;; pointer to the utxo this input consumes +;; hash: (buff 32), +;; index: uint +;; }, +;; scriptSig: (buff 256), ;; spending condition script +;; sequence: uint +;; }), +;; outs: (list 8 +;; { +;; value: uint, ;; satoshis sent +;; scriptPubKey: (buff 128) ;; parse this to get an address +;; }), +;; locktime: uint +;; }) +;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff. +;; Returns (err ERR-VARSLICE-TOO-LONG) if we find a scriptPubKey or scriptSig that's too long to parse. +;; Returns (err ERR-TOO-MANY-TXOUTS) if there are more than eight inputs to read. +;; Returns (err ERR-TOO-MANY-TXINS) if there are more than eight outputs to read. +(define-read-only (parse-tx (tx (buff 1024))) + (let ( + (ctx { txbuff: tx, index: u0 }) + (parsed-version (try! (read-uint32 ctx))) + (parsed-txins (try! (read-txins (get ctx parsed-version)))) + (parsed-txouts (try! (read-txouts (get ctx parsed-txins)))) + (parsed-locktime (try! (read-uint32 (get ctx parsed-txouts)))) + ) + (ok { + version: (get uint32 parsed-version), + ins: (get txins parsed-txins), + outs: (get txouts parsed-txouts), + locktime: (get uint32 parsed-locktime) + })) +) + +;; Parse a Bitcoin block header. +;; Returns a tuple structured as folowed on success: +;; (ok { +;; version: uint, ;; block version, +;; parent: (buff 32), ;; parent block hash, +;; merkle-root: (buff 32), ;; merkle root for all this block's transactions +;; timestamp: uint, ;; UNIX epoch timestamp of this block, in seconds +;; nbits: uint, ;; compact block difficulty representation +;; nonce: uint ;; PoW solution +;; }) +;; Returns (err ERR-BAD-HEADER) if the header buffer isn't actually 80 bytes long. +(define-read-only (parse-block-header (headerbuff (buff 80))) + (let ( + (ctx { txbuff: (unwrap! (as-max-len? headerbuff u1024) (err ERR-BAD-HEADER)), index: u0 }) + + ;; none of these should fail, since they're all fixed-length fields whose lengths sum to 80 + (parsed-version (unwrap-panic (read-uint32 ctx))) + (parsed-parent-hash (unwrap-panic (read-hashslice (get ctx parsed-version)))) + (parsed-merkle-root (unwrap-panic (read-hashslice (get ctx parsed-parent-hash)))) + (parsed-timestamp (unwrap-panic (read-uint32 (get ctx parsed-merkle-root)))) + (parsed-nbits (unwrap-panic (read-uint32 (get ctx parsed-timestamp)))) + (parsed-nonce (unwrap-panic (read-uint32 (get ctx parsed-nbits)))) + ) + (ok { + version: (get uint32 parsed-version), + parent: (get hashslice parsed-parent-hash), + merkle-root: (get hashslice parsed-merkle-root), + timestamp: (get uint32 parsed-timestamp), + nbits: (get uint32 parsed-nbits), + nonce: (get uint32 parsed-nonce) + })) +) + +;; Verify that a block header hashes to a burnchain header hash at a given height. +;; Returns true if so; false if not. +(define-read-only (verify-block-header (headerbuff (buff 80)) (expected-block-height uint)) + (match (get-block-info? burnchain-header-hash expected-block-height) + bhh (is-eq bhh (reverse-buff32 (sha256 (sha256 headerbuff)))) + false + ) +) + +;; Get the txid of a transaction, but big-endian. +;; This is the reverse of what you see on block explorers. +(define-read-only (get-reversed-txid (tx (buff 1024))) + (sha256 (sha256 tx))) + +;; Get the txid of a transaction. +;; This is what you see on block explorers. +(define-read-only (get-txid (tx (buff 1024))) + (reverse-buff32 (get-reversed-txid tx)) +) + +;; Determine if the ith bit in a uint is set to 1 +(define-read-only (is-bit-set (val uint) (bit uint)) + (is-eq (mod (/ val (pow u2 bit)) u2) u1) +) + +;; Verify the next step of a Merkle proof. +;; This hashes cur-hash against the ctr-th hash in proof-hashes, and uses that as the next cur-hash. +;; The path is a bitfield describing the walk from the txid up to the merkle root: +;; * if the ith bit is 0, then cur-hash is hashed before the next proof-hash (cur-hash is "left"). +;; * if the ith bit is 1, then the next proof-hash is hashed before cur-hash (cur-hash is "right"). +;; The proof verifies if cur-hash is equal to root-hash, and we're out of proof-hashes to check. +(define-read-only (inner-merkle-proof-verify (ctr uint) (state { path: uint, root-hash: (buff 32), proof-hashes: (list 12 (buff 32)), tree-depth: uint, cur-hash: (buff 32), verified: bool })) + (if (get verified state) + state + (if (>= ctr (get tree-depth state)) + (begin + ;; (print "ctr exceeds proof length or tree depth") + ;; (print ctr) + ;; (print (get tree-depth state)) + ;; (print (len (get proof-hashes state))) + (merge state { verified: false }) + ) + (let ( + (path (get path state)) + (is-left (is-bit-set path ctr)) + (proof-hashes (get proof-hashes state)) + (cur-hash (get cur-hash state)) + (root-hash (get root-hash state)) + + (h1 (if is-left (unwrap-panic (element-at proof-hashes ctr)) cur-hash)) + (h2 (if is-left cur-hash (unwrap-panic (element-at proof-hashes ctr)))) + (next-hash (sha256 (sha256 (concat h1 h2)))) + (is-verified (and (is-eq (+ u1 ctr) (len proof-hashes)) (is-eq next-hash root-hash))) + ) + (begin + ;; (print "cur-hash") + ;; (print cur-hash) + ;; (print "next-hash") + ;; (print h1) + ;; (print h2) + ;; (print next-hash) + (merge state { cur-hash: next-hash, verified: is-verified }) + )) + ) + ) +) + +;; Verify a Merkle proof, given the _reversed_ txid of a transaction, the merkle root of its block, and a proof consisting of: +;; * The index in the block where the transaction can be found (starting from 0), +;; * The list of hashes that link the txid to the merkle root, +;; * The depth of the block's merkle tree (required because Bitcoin does not identify merkle tree nodes as being leaves or intermediates). +;; The _reversed_ txid is required because that's the order (big-endian) processes them in. +;; The tx-index is required because it tells us the left/right traversals we'd make if we were walking down the tree from root to transaction, +;; and is thus used to deduce the order in which to hash the intermediate hashes with one another to link the txid to the merkle root. +;; Returns (ok true) if the proof is valid. +;; Returns (ok false) if the proof is invalid. +;; Returns (err ERR-PROOF-TOO-SHORT) if the proof's hashes aren't long enough to link the txid to the merkle root. +(define-read-only (verify-merkle-proof (reversed-txid (buff 32)) (merkle-root (buff 32)) (proof { tx-index: uint, hashes: (list 12 (buff 32)), tree-depth: uint })) + (if (> (get tree-depth proof) (len (get hashes proof))) + (err ERR-PROOF-TOO-SHORT) + (ok + (get verified + (fold inner-merkle-proof-verify + (list u0 u1 u2 u3 u4 u5 u6 u7 u8 u9 u10 u11) + { path: (+ (pow u2 (get tree-depth proof)) (get tx-index proof)), root-hash: merkle-root, proof-hashes: (get hashes proof), cur-hash: reversed-txid, tree-depth: (get tree-depth proof), verified: false })) + ) + ) +) + +;; Top-level verification code to determine whether or not a Bitcoin transaction was mined in a prior Bitcoin block. +;; It takes the block header and block height, the transaction, and a merkle proof, and determines that: +;; * the block header corresponds to the block that was mined at the given Bitcoin height +;; * the transaction's merkle proof links it to the block header's merkle root. +;; The proof is a list of sibling merkle tree nodes that allow us to calculate the parent node from two children nodes in each merkle tree level, +;; the depth of the block's merkle tree, and the index in the block in which the given transaction can be found (starting from 0). +;; The first element in hashes must be the given transaction's sibling transaction's ID. This and the given transaction's txid are hashed to +;; calculate the parent hash in the merkle tree, which is then hashed with the *next* hash in the proof, and so on and so forth, until the final +;; hash can be compared against the block header's merkle root field. The tx-index tells us in which order to hash each pair of siblings. +;; Note that the proof hashes -- including the sibling txid -- must be _big-endian_ hashes, because this is how Bitcoin generates them. +;; This is the reverse of what you'd see in a block explorer! +;; Returns (ok true) if the proof checks out. +;; Returns (ok false) if not. +;; Returns (err ERR-PROOF-TOO-SHORT) if the proof doesn't contain enough intermediate hash nodes in the merkle tree. +(define-read-only (was-tx-mined? (block { header: (buff 80), height: uint }) (tx (buff 1024)) (proof { tx-index: uint, hashes: (list 12 (buff 32)), tree-depth: uint })) + (let + ( + (header-valid (verify-block-header (get header block) (get height block))) + (reversed-txid (get-reversed-txid tx)) + (parsed-header (try! (parse-block-header (get header block)))) + (merkle-root (reverse-buff32 (get merkle-root parsed-header))) + (merkle-valid (verify-merkle-proof reversed-txid merkle-root proof)) + ) + (if header-valid + merkle-valid + (ok false) + ) + ) +) + +(define-read-only (verify-prev-block (block (buff 80)) (parent (buff 80))) + (let + ( + (parent-hash (sha256 (sha256 parent))) + (parent-reversed (reverse-buff32 parent-hash)) + (parsed (try! (parse-block-header block))) + (parsed-parent (get parent parsed)) + ) + (asserts! (is-eq parsed-parent parent-reversed) (err ERR-INVALID-PARENT)) + (ok true) + ) +) + +(define-read-only (verify-prev-blocks (block (buff 80)) (prev-blocks (list 10 (buff 80)))) + (let + ( + (iterator (ok block)) + (fold-resp (fold verify-prev-blocks-fold prev-blocks iterator)) + (last-block (try! fold-resp)) + ) + (ok last-block) + ) +) + +(define-read-only (verify-prev-blocks-fold + (parent (buff 80)) + (next-resp (response (buff 80) uint)) + ) + (let + ( + (next (try! next-resp)) + (is-valid (try! (verify-prev-block next parent))) + ) + (ok parent) + ) +) + +(define-read-only (was-tx-mined-prev? (block { header: (buff 80), height: uint }) (prev-blocks (list 10 (buff 80))) (tx (buff 1024)) (proof { tx-index: uint, hashes: (list 12 (buff 32)), tree-depth: uint })) + (let + ( + (header-valid (verify-block-header (get header block) (get height block))) + (previous-block (try! (verify-prev-blocks (get header block) prev-blocks))) + (reversed-txid (get-reversed-txid tx)) + (parsed-header (try! (parse-block-header previous-block))) + (merkle-root (reverse-buff32 (get merkle-root parsed-header))) + (merkle-valid (verify-merkle-proof reversed-txid merkle-root proof)) + ) + (if header-valid + merkle-valid + (ok false) + ) + ) +) \ No newline at end of file diff --git a/sbtc-ops/smart-contract/contracts/degen-bridge-testnet-v3.clar b/sbtc-ops/smart-contract/contracts/degen-bridge-testnet-v3.clar new file mode 100644 index 00000000..8c501dda --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/degen-bridge-testnet-v3.clar @@ -0,0 +1,935 @@ +(define-map supplier-by-id uint { + public-key: (buff 33), + controller: principal, + inbound-fee: (optional int), + outbound-fee: (optional int), + outbound-base-fee: int, + inbound-base-fee: int, +}) +(define-map supplier-by-public-key (buff 33) uint) +(define-map supplier-by-controller principal uint) + +(define-map swapper-by-id uint principal) +(define-map swapper-by-principal principal uint) + +;; amount of xBTC funds per supplier +;; supplier-id -> xBTC (sats) +(define-map supplier-funds uint uint) +;; amount of xBTC funds in escrow per supplier +;; supplier-id -> xBTC +(define-map supplier-escrow uint uint) + +;; info for inbound swaps +(define-map inbound-swaps (buff 32) { + swapper: uint, + xbtc: uint, + supplier: uint, + expiration: uint, + hash: (buff 32), +}) +;; extra info for inbound swaps - not needed for the `finalize` step +(define-map inbound-meta (buff 32) { + sender-public-key: (buff 33), + output-index: uint, + csv: uint, + sats: uint, + redeem-script: (buff 120), +}) +;; mapping of txid -> preimage +(define-map inbound-preimages (buff 32) (buff 128)) + +(define-map outbound-swaps uint { + swapper: principal, + sats: uint, + xbtc: uint, + supplier: uint, + version: (buff 1), + hash: (buff 20), + created-at: uint, +}) +;; mapping of swap -> txid +(define-map completed-outbound-swaps uint (buff 32)) +(define-map completed-outbound-swap-txids (buff 32) uint) + +;; tracking of total volume +(define-map user-inbound-volume-map principal uint) +(define-data-var total-inbound-volume-var uint u0) + +(define-map user-outbound-volume-map principal uint) +(define-data-var total-outbound-volume-var uint u0) + +(define-data-var next-supplier-id uint u0) +(define-data-var next-swapper-id uint u0) +(define-data-var next-outbound-id uint u0) + +(define-constant MIN_EXPIRATION u250) +(define-constant ESCROW_EXPIRATION u200) +(define-constant OUTBOUND_EXPIRATION u200) +(define-constant MAX_HTLC_EXPIRATION u550) + +(define-constant P2PKH_VERSION 0x00) +(define-constant P2SH_VERSION 0x05) + +;; use a placeholder txid to mark as "finalized" +(define-constant REVOKED_OUTBOUND_TXID 0x00) +;; placeholder to mark inbound swap as revoked +(define-constant REVOKED_INBOUND_PREIMAGE 0x00) +(define-constant ONE_8 u100000000) + +(define-constant ERR_PANIC (err u1)) ;; should never be thrown +(define-constant ERR_SUPPLIER_EXISTS (err u2)) +(define-constant ERR_UNAUTHORIZED (err u3)) +(define-constant ERR_ADD_FUNDS (err u4)) +(define-constant ERR_TRANSFER (err u5)) +(define-constant ERR_SUPPLIER_NOT_FOUND (err u6)) +(define-constant ERR_SWAPPER_NOT_FOUND (err u7)) +(define-constant ERR_FEE_INVALID (err u8)) +(define-constant ERR_SWAPPER_EXISTS (err u9)) +(define-constant ERR_INVALID_TX (err u10)) +(define-constant ERR_INVALID_OUTPUT (err u11)) +(define-constant ERR_INVALID_HASH (err u12)) +(define-constant ERR_INVALID_SUPPLIER (err u13)) +(define-constant ERR_INSUFFICIENT_FUNDS (err u14)) +(define-constant ERR_INVALID_EXPIRATION (err u15)) +(define-constant ERR_TXID_USED (err u16)) +(define-constant ERR_ALREADY_FINALIZED (err u17)) +(define-constant ERR_INVALID_ESCROW (err u18)) +(define-constant ERR_INVALID_PREIMAGE (err u19)) +(define-constant ERR_ESCROW_EXPIRED (err u20)) +(define-constant ERR_TX_NOT_MINED (err u21)) +(define-constant ERR_INVALID_BTC_ADDR (err u22)) +(define-constant ERR_SWAP_NOT_FOUND (err u23)) +(define-constant ERR_INSUFFICIENT_AMOUNT (err u24)) +(define-constant ERR_REVOKE_OUTBOUND_NOT_EXPIRED (err u25)) +(define-constant ERR_REVOKE_OUTBOUND_IS_FINALIZED (err u26)) +(define-constant ERR_INCONSISTENT_FEES (err u27)) +(define-constant ERR_REVOKE_INBOUND_NOT_EXPIRED (err u28)) +(define-constant ERR_REVOKE_INBOUND_IS_FINALIZED (err u29)) + + +;; Register a supplier and add funds. +;; Validates that the public key and "controller" (STX address) are not +;; in use for another controller. +;; +;; @returns the newly generated supplier ID. +;; +;; @param public-key; the public key used in HTLCs +;; @param inbound-fee; optional fee (in basis points) for inbound swaps +;; @param outbound-fee; optional fee (in basis points) for outbound +;; @param outbound-base-fee; fixed fee applied to outbound swaps (in xBTC sats) +;; @param inbound-base-fee; fixed fee for inbound swaps (in BTC/sats) +;; @param funds; amount of xBTC (sats) to initially supply +(define-public (register-supplier + (public-key (buff 33)) + (inbound-fee (optional int)) + (outbound-fee (optional int)) + (outbound-base-fee int) + (inbound-base-fee int) + (funds uint) + ) + (let + ( + (id (var-get next-supplier-id)) + (supplier { + inbound-fee: inbound-fee, + outbound-fee: outbound-fee, + public-key: public-key, + controller: tx-sender, + outbound-base-fee: outbound-base-fee, + inbound-base-fee: inbound-base-fee, + }) + ) + (asserts! (map-insert supplier-by-id id supplier) ERR_PANIC) + (asserts! (map-insert supplier-funds id u0) ERR_PANIC) + (asserts! (map-insert supplier-escrow id u0) ERR_PANIC) + (try! (validate-fee inbound-fee)) + (try! (validate-fee outbound-fee)) + + ;; validate that the public key and controller do not exist + (asserts! (map-insert supplier-by-public-key public-key id) ERR_SUPPLIER_EXISTS) + (asserts! (map-insert supplier-by-controller tx-sender id) ERR_SUPPLIER_EXISTS) + (var-set next-supplier-id (+ id u1)) + (try! (add-funds funds)) + (ok id) + ) +) + +;; As a supplier, add funds. +;; The `supplier-id` is automatically looked up from the `contract-caller` (tx-sender). +;; +;; @returns the new amount of funds pooled for this supplier +;; +;; @param amount; the amount of funds to add (in xBTC/sats) +(define-public (add-funds (amount uint)) + (let + ( + (supplier-id (unwrap! (get-supplier-id-by-controller contract-caller) ERR_UNAUTHORIZED)) + (existing-funds (get-funds supplier-id)) + (new-funds (+ amount existing-funds)) + ) + (try! (transfer amount tx-sender (as-contract tx-sender))) + (map-set supplier-funds supplier-id new-funds) + (ok new-funds) + ) +) + +;; As a supplier, remove funds. +;; +;; @returns the new amount of funds pooled for this supplier. +;; +;; @param amount; the amount of funds to remove (in xBTC/sats) +(define-public (remove-funds (amount uint)) + (let + ( + (supplier-id (unwrap! (get-supplier-id-by-controller contract-caller) ERR_UNAUTHORIZED)) + (existing-funds (get-funds supplier-id)) + (amount-ok (asserts! (>= existing-funds amount) ERR_INSUFFICIENT_FUNDS)) + (new-funds (- existing-funds amount)) + (controller contract-caller) + ) + (try! (as-contract (transfer amount tx-sender controller))) + (map-set supplier-funds supplier-id new-funds) + (ok new-funds) + ) +) + +;; Update fees for a supplier +;; +;; @returns new metadata for supplier +;; +;; @param inbound-fee; optional fee (in basis points) for inbound swaps +;; @param outbound-fee; optional fee (in basis points) for outbound +;; @param outbound-base-fee; fixed fee applied to outbound swaps (in xBTC sats) +;; @param inbound-base-fee; fixed fee for inbound swaps (in BTC/sats) +(define-public (update-supplier-fees + (inbound-fee (optional int)) + (outbound-fee (optional int)) + (outbound-base-fee int) + (inbound-base-fee int) + ) + (let + ( + (supplier-id (unwrap! (get-supplier-id-by-controller contract-caller) ERR_UNAUTHORIZED)) + (existing-supplier (unwrap! (get-supplier supplier-id) ERR_PANIC)) + (new-supplier (merge existing-supplier { + inbound-fee: inbound-fee, + outbound-fee: outbound-fee, + outbound-base-fee: outbound-base-fee, + inbound-base-fee: inbound-base-fee, + })) + ) + (try! (validate-fee inbound-fee)) + (try! (validate-fee outbound-fee)) + (map-set supplier-by-id supplier-id new-supplier) + (ok new-supplier) + ) +) + +;; Update the public-key for a supplier +;; +;; @returns new metadata for the supplier +;; +;; @param public-key; the public key used in HTLCs +(define-public (update-supplier-public-key (public-key (buff 33))) + (let + ( + (supplier-id (unwrap! (get-supplier-id-by-controller contract-caller) ERR_UNAUTHORIZED)) + (existing-supplier (unwrap! (get-supplier supplier-id) ERR_PANIC)) + (new-supplier (merge existing-supplier { + public-key: public-key, + })) + ) + (asserts! (map-insert supplier-by-public-key public-key supplier-id) ERR_SUPPLIER_EXISTS) + (asserts! (map-delete supplier-by-public-key (get public-key existing-supplier)) ERR_PANIC) + (map-set supplier-by-id supplier-id new-supplier) + (ok new-supplier) + ) +) + + +;; As a swapper, register with the contract to generate your `swapper-id`. +;; This is required to generate BTC deposit addresses that include metadata +;; that point to a specific STX address as the swapper. +;; +;; @returns the newly generated swapper ID. +(define-public (initialize-swapper) + (let + ( + (swapper tx-sender) + (id (var-get next-swapper-id)) + ) + (asserts! (map-insert swapper-by-id id swapper) ERR_PANIC) + (asserts! (map-insert swapper-by-principal swapper id) ERR_SWAPPER_EXISTS) + (var-set next-swapper-id (+ id u1)) + (ok id) + ) +) + +;; Escrow funds for a supplier after sending BTC during an inbound swap. +;; Validates that the BTC tx is valid by re-constructing the HTLC script +;; and comparing it to the BTC tx. +;; Validates that the HTLC data (like expiration) is valid. +;; +;; `tx-sender` must be equal to the swapper embedded in the HTLC. This ensures +;; that the `min-to-receive` parameter is provided by the end-user. +;; +;; @returns metadata regarding this inbound swap (see `inbound-meta` map) +;; +;; @param block; a tuple containing `header` (the Bitcoin block header) and the `height` (Stacks height) +;; where the BTC tx was confirmed. +;; @param prev-blocks; because Clarity contracts can't get Bitcoin headers when there is no Stacks block, +;; this param allows users to specify the chain of block headers going back to the block where the +;; BTC tx was confirmed. +;; @param tx; the hex data of the BTC tx +;; @param proof; a merkle proof to validate inclusion of this tx in the BTC block +;; @param output-index; the index of the HTLC output in the BTC tx +;; @param sender; The swapper's public key used in the HTLC +;; @param recipient; The supplier's public key used in the HTLC +;; @param expiration-buff; A 4-byte integer the indicated the expiration of the HTLC +;; @param hash; a hash of the `preimage` used in this swap +;; @param swapper-buff; a 4-byte integer that indicates the `swapper-id` +;; @param supplier-id; the supplier used in this swap +;; @param min-to-receive; minimum receivable calculated off-chain to avoid the supplier front-run the swap by adjusting fees +(define-public (escrow-swap + (block { header: (buff 80), height: uint }) + (prev-blocks (list 10 (buff 80))) + (tx (buff 1024)) + (proof { tx-index: uint, hashes: (list 12 (buff 32)), tree-depth: uint }) + (output-index uint) + (sender (buff 33)) + (recipient (buff 33)) + (expiration-buff (buff 4)) + (hash (buff 32)) + (swapper-buff (buff 4)) + (supplier-id uint) + (min-to-receive uint) + ) + (let + ( + (was-mined-bool (unwrap! (contract-call? .clarity-bitcoin was-tx-mined-prev? block prev-blocks tx proof) ERR_TX_NOT_MINED)) ;; ST19F1KWRKRF2BZMPW7MWV463K11WED2M39X1HR3A.clarity-bitcoin + (was-mined (asserts! was-mined-bool ERR_TX_NOT_MINED)) + (mined-height (get height block)) + (htlc-redeem (generate-htlc-script sender recipient expiration-buff hash swapper-buff)) + (htlc-output (generate-script-hash htlc-redeem)) + (parsed-tx (unwrap! (contract-call? .clarity-bitcoin parse-tx tx) ERR_INVALID_TX)) ;; ST19F1KWRKRF2BZMPW7MWV463K11WED2M39X1HR3A.clarity-bitcoin + (output (unwrap! (element-at (get outs parsed-tx) output-index) ERR_INVALID_TX)) + (output-script (get scriptPubKey output)) + (supplier (unwrap! (map-get? supplier-by-id supplier-id) ERR_INVALID_SUPPLIER)) + (sats (get value output)) + (fee-rate (default-to 0 (get inbound-fee supplier))) + (xbtc (try! (get-swap-amount sats fee-rate (get inbound-base-fee supplier)))) + (funds (get-funds supplier-id)) + (funds-ok (asserts! (>= funds xbtc) ERR_INSUFFICIENT_FUNDS)) + (escrowed (unwrap! (map-get? supplier-escrow supplier-id) ERR_PANIC)) + (new-funds (- funds xbtc)) + (new-escrow (+ escrowed xbtc)) + (expiration (try! (read-uint32 expiration-buff (len expiration-buff)))) + (swapper-id (try! (read-uint32 swapper-buff u4))) + (txid (contract-call? .clarity-bitcoin get-txid tx)) ;; ST19F1KWRKRF2BZMPW7MWV463K11WED2M39X1HR3A.clarity-bitcoin + (expiration-ok (try! (validate-expiration expiration mined-height))) + (escrow { + swapper: swapper-id, + supplier: supplier-id, + xbtc: xbtc, + expiration: (+ mined-height (- expiration ESCROW_EXPIRATION)), + hash: hash, + }) + (meta { + sender-public-key: sender, + output-index: output-index, + csv: expiration, + redeem-script: htlc-redeem, + sats: sats, + }) + ) + ;; assert tx-sender is swapper + (asserts! (is-eq tx-sender (unwrap! (map-get? swapper-by-id swapper-id) ERR_SWAPPER_NOT_FOUND)) ERR_UNAUTHORIZED) + (asserts! (is-eq (get public-key supplier) recipient) ERR_INVALID_OUTPUT) + (asserts! (is-eq output-script htlc-output) ERR_INVALID_OUTPUT) + (asserts! (is-eq (len hash) u32) ERR_INVALID_HASH) + (asserts! (map-insert inbound-swaps txid escrow) ERR_TXID_USED) + (asserts! (map-insert inbound-meta txid meta) ERR_PANIC) + (asserts! (>= xbtc min-to-receive) ERR_INCONSISTENT_FEES) + (map-set supplier-funds supplier-id new-funds) + (map-set supplier-escrow supplier-id new-escrow) + (print (merge (merge escrow meta) { + topic: "escrow", + txid: txid, + })) + (ok meta) + ) +) + +(define-private (mul-down (a uint) (b uint)) + (/ (* a b) ONE_8)) + +(define-private (minus-percent (a uint) (percent uint)) + (if (is-eq a u0) + u0 + (/ (- (* a u100) (* a percent)) u100))) + +;; Finalize an inbound swap by revealing the preimage. +;; Validates that `sha256(preimage)` is equal to the `hash` provided when +;; escrowing the swap. +;; +;; @returns metadata relating to the swap (see `inbound-swaps` map) +;; +;; @param txid; the txid of the BTC tx used for this inbound swap +;; @param preimage; the preimage that hashes to the swap's `hash` +(define-public (finalize-swap (txid (buff 32)) (preimage (buff 128))) + (match (map-get? inbound-preimages txid) + existing ERR_ALREADY_FINALIZED + (let + ( + (swap (unwrap! (map-get? inbound-swaps txid) ERR_INVALID_ESCROW)) + (stored-hash (get hash swap)) + (preimage-ok (asserts! (is-eq (sha256 preimage) stored-hash) ERR_INVALID_PREIMAGE)) + (supplier-id (get supplier swap)) + (xbtc (get xbtc swap)) + (escrowed (unwrap! (map-get? supplier-escrow supplier-id) ERR_PANIC)) + (swapper (unwrap! (get-swapper-principal (get swapper swap)) ERR_PANIC)) + (fee-amount + (contract-call? .amm-swap-pool-v1-1 fee-helper .token-wbtc .token-wstx ONE_8)) + (get-helper-result (try! (contract-call? .amm-swap-pool-v1-1 get-helper .token-wbtc .token-wstx ONE_8 xbtc))) + (stx-amount + (mul-down + get-helper-result + (- ONE_8 (unwrap-panic fee-amount)))) + (stx-amount-slippeage (minus-percent xbtc u5)) + (btc-to-xbtc-transfer-result (try! (as-contract (transfer xbtc tx-sender swapper)))) + (xbtc-to-stx-transfer-result + (try! (contract-call? .amm-swap-pool-v1-1 swap-helper + .token-wbtc + .token-wstx + ONE_8 + xbtc + (some stx-amount-slippeage)))) + ) + (map-insert inbound-preimages txid preimage) + (print xbtc-to-stx-transfer-result) + (asserts! (>= (get expiration swap) block-height) ERR_ESCROW_EXPIRED) + (map-set supplier-escrow supplier-id (- escrowed xbtc)) + (update-user-inbound-volume swapper xbtc) + (print (merge swap { + preimage: preimage, + topic: "finalize-inbound", + txid: txid, + })) + (ok swap) + ) + ) +) + +;; Revoke an expired inbound swap. +;; +;; If an inbound swap has expired, and is not finalized, then the `xbtc` +;; amount of the swap is "stuck" in escrow. Calling this function will: +;; +;; - Update the supplier's funds and escrow +;; - Mark the swap as finalized +;; +;; To finalize the swap, the pre-image stored for the swap is the constant +;; REVOKED_INBOUND_PREIMAGE (0x00). +;; +;; @returns the swap's metadata +;; +;; @param txid; the txid of the BTC tx used for this inbound swap +(define-public (revoke-expired-inbound (txid (buff 32))) + (match (map-get? inbound-preimages txid) + existing ERR_REVOKE_INBOUND_IS_FINALIZED + (let + ( + (swap (unwrap! (map-get? inbound-swaps txid) ERR_INVALID_ESCROW)) + (xbtc (get xbtc swap)) + (supplier-id (get supplier swap)) + (funds (get-funds supplier-id)) + (escrowed (unwrap! (get-escrow supplier-id) ERR_PANIC)) + (new-funds (+ funds xbtc)) + (new-escrow (- escrowed xbtc)) + ) + (asserts! (<= (get expiration swap) block-height) ERR_REVOKE_INBOUND_NOT_EXPIRED) + (map-insert inbound-preimages txid REVOKED_INBOUND_PREIMAGE) + (map-set supplier-escrow supplier-id new-escrow) + (map-set supplier-funds supplier-id new-funds) + (print (merge swap { + topic: "revoke-inbound", + txid: txid, + })) + (ok swap) + ) + ) +) + +;; outbound swaps + +;; Initiate an outbound swap. +;; Swapper provides the amount of xBTC and their withdraw address. +;; +;; @returns the auto-generated swap-id of this swap +;; +;; @param xbtc; amount of xBTC (sats) to swap +;; @param btc-version; the address version for the swapper's BTC address +;; @param btc-hash; the hash for the swapper's BTC address +;; @param supplier-id; the supplier used for this swap +(define-public (initiate-outbound-swap (xbtc uint) (btc-version (buff 1)) (btc-hash (buff 20)) (supplier-id uint)) + (let + ( + (supplier (unwrap! (map-get? supplier-by-id supplier-id) ERR_INVALID_SUPPLIER)) + (fee-rate (default-to 0 (get outbound-fee supplier))) + (sats (try! (get-swap-amount xbtc fee-rate (get outbound-base-fee supplier)))) + (swap { + sats: sats, + xbtc: xbtc, + supplier: supplier-id, + version: btc-version, + hash: btc-hash, + created-at: burn-block-height, + swapper: tx-sender, + }) + (swap-id (var-get next-outbound-id)) + ) + (try! (validate-btc-addr btc-version btc-hash)) + (try! (transfer xbtc tx-sender (as-contract tx-sender))) + (asserts! (map-insert outbound-swaps swap-id swap) ERR_PANIC) + (var-set next-outbound-id (+ swap-id u1)) + (print (merge swap { + swap-id: swap-id, + topic: "initiate-outbound", + })) + (ok swap-id) + ) +) + +;; Finalize an outbound swap. +;; This method is called by the supplier after they've sent the swapper BTC. +;; +;; @returns true +;; +;; @param block; a tuple containing `header` (the Bitcoin block header) and the `height` (Stacks height) +;; where the BTC tx was confirmed. +;; @param prev-blocks; because Clarity contracts can't get Bitcoin headers when there is no Stacks block, +;; this param allows users to specify the chain of block headers going back to the block where the +;; BTC tx was confirmed. +;; @param tx; the hex data of the BTC tx +;; @param proof; a merkle proof to validate inclusion of this tx in the BTC block +;; @param output-index; the index of the HTLC output in the BTC tx +;; @param swap-id; the outbound swap ID they're finalizing +(define-public (finalize-outbound-swap + (block { header: (buff 80), height: uint }) + (prev-blocks (list 10 (buff 80))) + (tx (buff 1024)) + (proof { tx-index: uint, hashes: (list 12 (buff 32)), tree-depth: uint }) + (output-index uint) + (swap-id uint) + ) + (let + ( + (was-mined-bool (unwrap! (contract-call? .clarity-bitcoin was-tx-mined-prev? block prev-blocks tx proof) ERR_TX_NOT_MINED)) ;;ST19F1KWRKRF2BZMPW7MWV463K11WED2M39X1HR3A.clarity-bitcoin + (was-mined (asserts! was-mined-bool ERR_TX_NOT_MINED)) + (swap (unwrap! (get-outbound-swap swap-id) ERR_SWAP_NOT_FOUND)) + (expected-output (generate-output (get version swap) (get hash swap))) + (parsed-tx (unwrap! (contract-call? .clarity-bitcoin parse-tx tx) ERR_INVALID_TX)) ;;ST19F1KWRKRF2BZMPW7MWV463K11WED2M39X1HR3A.clarity-bitcoin + (output (unwrap! (element-at (get outs parsed-tx) output-index) ERR_INVALID_TX)) + (output-script (get scriptPubKey output)) + (txid (contract-call? .clarity-bitcoin get-txid tx)) ;;ST19F1KWRKRF2BZMPW7MWV463K11WED2M39X1HR3A.clarity-bitcoin + (output-sats (get value output)) + (xbtc (get xbtc swap)) + (supplier (get supplier swap)) + (funds-before (get-funds supplier)) + ) + (map-set supplier-funds supplier (+ funds-before xbtc)) + (asserts! (is-eq output-script expected-output) ERR_INVALID_OUTPUT) + (asserts! (map-insert completed-outbound-swaps swap-id txid) ERR_ALREADY_FINALIZED) + (asserts! (map-insert completed-outbound-swap-txids txid swap-id) ERR_TXID_USED) + (asserts! (>= output-sats (get sats swap)) ERR_INSUFFICIENT_AMOUNT) + (update-user-outbound-volume (get swapper swap) xbtc) + (print (merge swap { + topic: "finalize-outbound", + txid: txid, + swap-id: swap-id, + })) + (ok true) + ) +) + +;; Revoke an expired outbound swap. +;; After an outbound swap has expired without finalizing, a swapper may call this function +;; to receive the xBTC escrowed. +;; +;; @returns the metadata regarding the outbound swap +;; +;; @param swap-id; the ID of the outbound swap being revoked. +(define-public (revoke-expired-outbound (swap-id uint)) + (let + ( + (swap (try! (validate-outbound-revocable swap-id))) + (xbtc (get xbtc swap)) + (swapper (get swapper swap)) + ) + (try! (as-contract (transfer xbtc tx-sender swapper))) + (asserts! (map-insert completed-outbound-swaps swap-id REVOKED_OUTBOUND_TXID) ERR_PANIC) + (print (merge swap { + topic: "revoke-outbound", + swap-id: swap-id, + })) + (ok swap) + ) +) + +;; getters + +(define-read-only (get-supplier-id-by-controller (controller principal)) + (map-get? supplier-by-controller controller) +) + +(define-read-only (get-supplier-id-by-public-key (public-key (buff 33))) + (map-get? supplier-by-public-key public-key) +) + +(define-read-only (get-supplier (id uint)) + (map-get? supplier-by-id id) +) + +(define-read-only (get-funds (id uint)) + (default-to u0 (map-get? supplier-funds id)) +) + +(define-read-only (get-escrow (id uint)) + (map-get? supplier-escrow id) +) + +(define-read-only (get-inbound-swap (txid (buff 32))) + (map-get? inbound-swaps txid) +) + +(define-read-only (get-preimage (txid (buff 32))) + (map-get? inbound-preimages txid) +) + +(define-read-only (get-outbound-swap (id uint)) + (map-get? outbound-swaps id) +) + +(define-read-only (get-completed-outbound-swap-txid (id uint)) + (map-get? completed-outbound-swaps id) +) + +(define-read-only (get-completed-outbound-swap-by-txid (txid (buff 32))) + (map-get? completed-outbound-swap-txids txid) +) + +(define-read-only (get-swapper-id (swapper principal)) + (map-get? swapper-by-principal swapper) +) + +(define-read-only (get-swapper-principal (id uint)) + (map-get? swapper-by-id id) +) + +(define-read-only (get-next-supplier-id) (var-get next-supplier-id)) +(define-read-only (get-next-swapper-id) (var-get next-swapper-id)) +(define-read-only (get-next-outbound-id) (var-get next-outbound-id)) + +(define-read-only (get-full-supplier (id uint)) + (let + ( + (supplier (unwrap! (get-supplier id) ERR_INVALID_SUPPLIER)) + (funds (get-funds id)) + (escrow (unwrap! (get-escrow id) ERR_PANIC)) + ) + (ok (merge supplier { funds: funds, escrow: escrow })) + ) +) + +(define-read-only (get-inbound-meta (txid (buff 32))) + (map-get? inbound-meta txid) +) + +(define-read-only (get-full-inbound (txid (buff 32))) + (let + ( + (swap (unwrap! (get-inbound-swap txid) ERR_INVALID_ESCROW)) + (meta (unwrap! (get-inbound-meta txid) ERR_INVALID_ESCROW)) + (swapper-principal (unwrap! (get-swapper-principal (get swapper swap)) ERR_PANIC)) + ) + (ok (merge { swapper-principal: swapper-principal } (merge swap meta))) + ) +) + +(define-read-only (get-user-inbound-volume (user principal)) + (match (map-get? user-inbound-volume-map user) + vol vol + u0 + ) +) + +(define-read-only (get-total-inbound-volume) (var-get total-inbound-volume-var)) + +(define-read-only (get-user-outbound-volume (user principal)) + (match (map-get? user-outbound-volume-map user) + vol vol + u0 + ) +) + +(define-read-only (get-total-outbound-volume) (var-get total-outbound-volume-var)) + +(define-read-only (get-user-total-volume (user principal)) + (+ (get-user-inbound-volume user) (get-user-outbound-volume user)) +) + +(define-read-only (get-total-volume) + (+ (get-total-inbound-volume) (get-total-outbound-volume)) +) + +;; helpers + +(define-private (transfer (amount uint) (sender principal) (recipient principal)) +;;On mainnet: we'll call AlexGo contract address +;;ST3VRQJMS69354J6DTPKG5W67XP31D4E6HJW708W4 used in testnet for Wrapped-Bitcoin + (match (contract-call? .token-wbtc transfer amount sender recipient none) + success (ok success) + error (begin + (print { transfer-error: error }) + ERR_TRANSFER + ) + ) +) + +(define-read-only (concat-buffs (buffs (list 6 (buff 32)))) + (let + ( + (initial-buff 0x) + (final (fold concat-buffs-fold buffs initial-buff)) + ) + final + ) +) + +(define-private (concat-buffs-fold (b (buff 32)) (result (buff 192))) + (let + ( + (next-buff (concat result b)) + (next-buff-min (as-max-len? next-buff u192)) + ) + (match next-buff-min + min-buff min-buff + result ;; if using `concat-buffs`, this should never happen. + ) + ) +) + +(define-read-only (get-swap-amount (amount uint) (fee-rate int) (base-fee int)) + (let + ( + (with-bps-fee (get-amount-with-fee-rate amount fee-rate)) + ) + (if (>= base-fee with-bps-fee) + ERR_INSUFFICIENT_AMOUNT + (ok (to-uint (- with-bps-fee base-fee))) + ) + ) +) + +(define-read-only (get-amount-with-fee-rate (amount uint) (fee-rate int)) + (let + ( + (numerator (* (to-int amount) (- 10000 fee-rate))) + (final (/ numerator 10000)) + ) + final + ) +) + +(define-private (update-user-inbound-volume (user principal) (amount uint)) + (let + ( + (user-total (get-user-inbound-volume user)) + (total (get-total-inbound-volume)) + ) + (map-set user-inbound-volume-map user (+ user-total amount)) + (var-set total-inbound-volume-var (+ total amount)) + true + ) +) + +(define-private (update-user-outbound-volume (user principal) (amount uint)) + (let + ( + (user-total (get-user-outbound-volume user)) + (total (get-total-outbound-volume)) + ) + (map-set user-outbound-volume-map user (+ user-total amount)) + (var-set total-outbound-volume-var (+ total amount)) + true + ) +) + +;; validators + +;; Validate the expiration for an inbound swap. +;; +;; There are two validations used here: +;; +;; - Expiration isn't too soon. To ensure that the swapper and supplier have sufficient +;; time to finalize, a swap must be escrowed with **at least** 250 blocks remaining. +;; - Expiration isn't too far. The HTLC must have a `CHECKSEQUENCEVERIFY` of less +;; than 550. This ensures that a supplier's xBTC isn't escrowed for unnecessarily long times. +;; +;; @param expiration; the amount of blocks that need to pass before +;; the sender can recover their HTLC. This is the value used with `CHECKSEQUENCEVERIFY` +;; in the HTLC script. +;; @param mined-height; the nearest stacks block after (or including) the Bitcoin +;; block where the HTLC was confirmed. +(define-read-only (validate-expiration (expiration uint) (mined-height uint)) + (if (> expiration (+ (- block-height mined-height) MIN_EXPIRATION)) + (if (< expiration MAX_HTLC_EXPIRATION) (ok true) ERR_INVALID_EXPIRATION) + ERR_INVALID_EXPIRATION + ) +) + +(define-read-only (validate-fee (fee-opt (optional int))) + (match fee-opt + fee (let + ( + (max-fee 10000) + (within-upper (< fee max-fee)) + (within-lower (> fee (* -1 max-fee))) + ) + (asserts! (and within-upper within-lower) ERR_FEE_INVALID) + (ok true) + ) + (ok true) + ) +) + +(define-read-only (validate-btc-addr (version (buff 1)) (hash (buff 20))) + (let + ( + (valid-hash (is-eq (len hash) u20)) + (valid-version (or (is-eq version P2PKH_VERSION) (is-eq version P2SH_VERSION))) + ) + (asserts! (and valid-hash valid-version) ERR_INVALID_BTC_ADDR) + (ok true) + ) +) + +;; lookup an outbound swap and validate that it is revocable. +;; to be revoked, it must be expired and not finalized +(define-read-only (validate-outbound-revocable (swap-id uint)) + (let + ( + (swap (unwrap! (get-outbound-swap swap-id) ERR_SWAP_NOT_FOUND)) + (finalize-txid (get-completed-outbound-swap-txid swap-id)) + (swap-expiration (+ (get created-at swap) OUTBOUND_EXPIRATION)) + (is-expired (>= burn-block-height swap-expiration)) + (is-not-finalized (is-none finalize-txid)) + ) + (asserts! is-expired ERR_REVOKE_OUTBOUND_NOT_EXPIRED) + (asserts! is-not-finalized ERR_REVOKE_OUTBOUND_IS_FINALIZED) + (ok swap) + ) +) + +;; htlc + +(define-read-only (generate-htlc-script + (sender (buff 33)) + (recipient (buff 33)) + (expiration (buff 4)) + (hash (buff 32)) + (swapper (buff 4)) + ) + (let + ( + (swapper-id (concat 0x04 swapper)) + (b10 (concat swapper-id 0x7563a820)) + (b1 (concat b10 hash)) + (b2 (concat b1 0x8821)) + (b3 (concat b2 recipient)) + (b4 (concat b3 0x67)) + (exp-len (bytes-len expiration)) + (b9 (concat b4 exp-len)) + (b5 (concat b9 expiration)) + (b6 (concat b5 0xb27521)) + (b7 (concat b6 sender)) + (b8 (concat b7 0x68ac)) + ) + b8 + ) +) + +(define-read-only (generate-script-hash (script (buff 120))) + (generate-p2sh-output (hash160 script)) +) + +(define-read-only (generate-htlc-script-hash + (sender (buff 33)) + (recipient (buff 33)) + (expiration (buff 4)) + (hash (buff 32)) + (swapper (buff 4)) + ) + (generate-script-hash (generate-htlc-script sender recipient expiration hash swapper)) +) + +(define-read-only (generate-p2pkh-output (hash (buff 20))) + (concat (concat 0x76a914 hash) 0x88ac) +) + +(define-read-only (generate-p2sh-output (hash (buff 20))) + (concat (concat 0xa914 hash) 0x87) +) + +;; generate an output, given btc address. +;; assumes that if the version is not p2sh, it's p2pkh. +;; for outbound swaps, the version is validated when initiated, +;; so it should only ever be these two. +(define-read-only (generate-output (version (buff 1)) (hash (buff 20))) + (if (is-eq version P2SH_VERSION) + (generate-p2sh-output hash) + (generate-p2pkh-output hash) + ) +) + +(define-read-only (bytes-len (bytes (buff 4))) + (unwrap-panic (element-at BUFF_TO_BYTE (len bytes))) +) + +(define-constant ERR_READ_UINT (err u100)) + +;; little-endian +(define-read-only (read-uint32 (num (buff 4)) (length uint)) + (let + ( + (byte-1 (buff-to-u8 (unwrap! (element-at num u0) ERR_READ_UINT))) + (byte-2 (if (> length u1) (buff-to-u8 (unwrap! (element-at num u1) ERR_READ_UINT)) u0)) + (byte-3 (if (> length u2) (buff-to-u8 (unwrap! (element-at num u2) ERR_READ_UINT)) u0)) + (byte-4 (if (> length u3) (buff-to-u8 (unwrap! (element-at num u3) ERR_READ_UINT)) u0)) + ) + (ok (+ (* byte-4 u16777216) (* byte-3 u65536) (* byte-2 u256) byte-1)) + ) +) + +(define-read-only (buff-to-u8 (byte (buff 1))) + (unwrap-panic (index-of BUFF_TO_BYTE byte))) + + +(define-constant BUFF_TO_BYTE (list + 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f + 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e 0x1f + 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x2a 0x2b 0x2c 0x2d 0x2e 0x2f + 0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x3a 0x3b 0x3c 0x3d 0x3e 0x3f + 0x40 0x41 0x42 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4a 0x4b 0x4c 0x4d 0x4e 0x4f + 0x50 0x51 0x52 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5a 0x5b 0x5c 0x5d 0x5e 0x5f + 0x60 0x61 0x62 0x63 0x64 0x65 0x66 0x67 0x68 0x69 0x6a 0x6b 0x6c 0x6d 0x6e 0x6f + 0x70 0x71 0x72 0x73 0x74 0x75 0x76 0x77 0x78 0x79 0x7a 0x7b 0x7c 0x7d 0x7e 0x7f + 0x80 0x81 0x82 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8a 0x8b 0x8c 0x8d 0x8e 0x8f + 0x90 0x91 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9a 0x9b 0x9c 0x9d 0x9e 0x9f + 0xa0 0xa1 0xa2 0xa3 0xa4 0xa5 0xa6 0xa7 0xa8 0xa9 0xaa 0xab 0xac 0xad 0xae 0xaf + 0xb0 0xb1 0xb2 0xb3 0xb4 0xb5 0xb6 0xb7 0xb8 0xb9 0xba 0xbb 0xbc 0xbd 0xbe 0xbf + 0xc0 0xc1 0xc2 0xc3 0xc4 0xc5 0xc6 0xc7 0xc8 0xc9 0xca 0xcb 0xcc 0xcd 0xce 0xcf + 0xd0 0xd1 0xd2 0xd3 0xd4 0xd5 0xd6 0xd7 0xd8 0xd9 0xda 0xdb 0xdc 0xdd 0xde 0xdf + 0xe0 0xe1 0xe2 0xe3 0xe4 0xe5 0xe6 0xe7 0xe8 0xe9 0xea 0xeb 0xec 0xed 0xee 0xef + 0xf0 0xf1 0xf2 0xf3 0xf4 0xf5 0xf6 0xf7 0xf8 0xf9 0xfa 0xfb 0xfc 0xfd 0xfe 0xff +)) \ No newline at end of file diff --git a/sbtc-ops/smart-contract/contracts/ft-trait.clar b/sbtc-ops/smart-contract/contracts/ft-trait.clar new file mode 100644 index 00000000..2d844cb9 --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/ft-trait.clar @@ -0,0 +1,24 @@ +(define-trait ft-trait + ( + ;; Transfer from the caller to a new principal + (transfer (uint principal principal (optional (buff 34))) (response bool uint)) + + ;; the human readable name of the token + (get-name () (response (string-ascii 32) uint)) + + ;; the ticker symbol, or empty if none + (get-symbol () (response (string-ascii 32) uint)) + + ;; the number of decimals used, e.g. 6 would mean 1_000_000 represents 1 token + (get-decimals () (response uint uint)) + + ;; the balance of the passed principal + (get-balance (principal) (response uint uint)) + + ;; the current total supply (which does not need to be a constant) + (get-total-supply () (response uint uint)) + + ;; an optional URI that represents metadata of this token + (get-token-uri () (response (optional (string-utf8 256)) uint)) + ) +) \ No newline at end of file diff --git a/sbtc-ops/smart-contract/contracts/mining-pool-5-blocks.clar b/sbtc-ops/smart-contract/contracts/mining-pool-5-blocks.clar new file mode 100644 index 00000000..733e1b0d --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/mining-pool-5-blocks.clar @@ -0,0 +1,1067 @@ +(define-constant err-invalid (err u300)) +(define-constant err-list-length-exceeded (err u101)) +(define-constant err-already-asked-to-join (err u102)) +(define-constant err-already-joined (err u103)) +(define-constant err-not-in-miner-map (err u104)) +(define-constant err-no-vote-permission (err u105)) +(define-constant err-more-blocks-to-pass (err u106)) +(define-constant err-no-pending-miners (err u107)) +(define-constant err-already-voted (err u108)) +(define-constant err-not-asked-to-join (err u109)) +(define-constant err-cant-unwrap-check-miner (err u110)) +(define-constant err-cant-unwrap-asked-to-join (err u111)) +(define-constant err-cant-unwrap-block-info (err u112)) +(define-constant err-currently-notifier (err u113)) +(define-constant err-not-in-miner-map-miner-to-remove (err u114)) +(define-constant err-already-proposed-for-removal (err u116)) +(define-constant err-not-proposed-for-removal (err u117)) +(define-constant err-cant-remove-when-alone-in-pool (err u118)) +(define-constant err-cant-vote-himself (err u119)) +(define-constant err-cant-change-notifier (err u120)) +(define-constant err-already-proposed-for-notifier (err u121)) +(define-constant err-not-proposed-for-removal-proposal-block-missing (err u122)) +(define-constant err-not-proposed-for-notifier-k-missing (err u123)) +(define-constant err-not-proposed-notifier (err u124)) +(define-constant err-already-notifier (err u125)) +(define-constant err-not-in-miner-map-proposed-notifier (err u126)) +(define-constant err-vote-started-already (err u127)) +(define-constant err-voting-still-active (err u128)) +(define-constant err-not-voting-period (err u129)) +(define-constant err-not-waiting (err u130)) +(define-constant err-not-pending (err u131)) +(define-constant err-no-join-block-data (err u132)) +(define-constant err-not-voted (err u133)) +(define-constant err-only-notifier (err u134)) +(define-constant err-one-warning-per-block (err u135)) +(define-constant err-block-height-invalid (err u136)) +(define-constant err-unwrap-miner-index (err u999)) +(define-constant err-insufficient-balance (err u1001)) +(define-constant err-missing-balance (err u1002)) +(define-constant err-already-distributed (err u1003)) +(define-constant err-cant-unwrap-rewarded-block (err u1004)) + +(define-constant notifier-election-blocks-to-pass u144) +(define-constant blocks-to-pass u5) + +(define-map balance principal uint) +(define-map claimed-rewards { block-number: uint } { claimed: bool }) +(define-map map-is-miner { address: principal } { value: bool }) +(define-map map-is-waiting { address: principal } { value: bool }) +(define-map map-is-pending { address: principal } { value: bool }) +(define-map map-is-proposed-for-removal { address: principal } { value: bool }) +(define-map map-block-asked-to-join { address: principal } { value: uint }) +(define-map map-block-proposed-to-remove { address: principal } { value: uint }) +(define-map map-block-joined { address: principal } { block-height: uint }) +(define-map map-balance-xBTC { address: principal } { value: uint }) +(define-map auto-exchange { address: principal } { value: bool }) +(define-map btc-address { address: principal } { btc-address: {hashbytes: (buff 20), version: (buff 1)} }) + +(define-map map-votes-accept-join { address: principal } { value: uint }) +(define-map map-votes-reject-join { address: principal } { value: uint }) +(define-map map-votes-accept-removal { address: principal } { value: uint }) +(define-map map-votes-reject-removal { address: principal } { value: uint }) +(define-map map-join-request-voter { miner-to-vote: principal, voter: principal } { value: bool }) +(define-map map-remove-request-voter { miner-to-vote: principal, voter: principal } { value: bool }) +(define-map map-voted-update-notifier { miner-who-voted: principal } { miner-voted: principal }) +(define-map map-votes-notifier { voted-notifier: principal } { votes-number: uint }) +(define-map map-blacklist { address: principal } { value: bool }) +(define-map map-total-withdraw { address: principal } { value: uint }) +(define-map map-warnings { address: principal } { value: uint }) + +(define-data-var miners-list-len-at-reward-block uint u0) +(define-data-var notifier principal tx-sender) +(define-data-var waiting-list (list 300 principal) (list )) +(define-data-var miners-list (list 300 principal) (list (var-get notifier))) +(define-data-var pending-accept-list (list 300 principal) (list )) +(define-data-var proposed-removal-list (list 300 principal) (list )) +(define-data-var n uint u1) +(define-data-var k-percentage uint u67) +(define-data-var k uint u1) +(define-data-var k-critical uint u75) +(define-data-var waiting-list-miner-to-remove principal tx-sender) ;; use in remove-principal-miners-list +(define-data-var pending-accept-list-miner-to-remove principal tx-sender) +(define-data-var miners-list-miner-to-remove principal tx-sender) +(define-data-var proposed-removal-list-miner-to-remove principal tx-sender) +(define-data-var last-join-done uint u1) +(define-data-var miner-to-remove-votes-join principal tx-sender) +(define-data-var miner-to-remove-votes-remove principal tx-sender) +(define-data-var notifier-previous-entries-removed bool true) +(define-data-var notifier-vote-active bool false) +(define-data-var notifier-vote-start-block uint u0) +(define-data-var notifier-vote-end-block uint u0) +(define-data-var max-votes-notifier uint u0) +(define-data-var max-voted-proposed-notifier principal tx-sender) +(define-data-var reward uint u0) +(define-data-var total-rewarded uint u0) +(define-data-var blocks-won uint u0) +(define-data-var reward-change-funds uint u0) +(define-data-var temp-distributed-change-funds uint u0) +(define-data-var temp-change-after-flushing uint u0) + +(map-set map-is-miner {address: tx-sender} {value: true}) +(map-set map-block-joined {address: tx-sender} {block-height: block-height}) +(map-set balance tx-sender u0) +;; at new join -> block height - last-join-done >= 100 ! + +;; READ ONLY FE UTILS + +;; waiting miners + +(define-read-only (get-all-data-waiting-miners (waiting-miners-list (list 100 principal))) +(map get-all-data-waiting-miner waiting-miners-list)) + +(define-private (get-all-data-waiting-miner (miner principal)) +(let ((k-at-block-asked-to-join (unwrap-panic (get-k-at-block-asked-to-join miner))) + (n-at-block-asked-to-join (unwrap-panic (get-n-at-block-asked-to-join miner)))) + (begin + (asserts! (is-some (get value (map-get? map-is-waiting {address: miner}))) err-not-waiting) + (ok + { + pos-votes: + (default-to u0 (get value (map-get? map-votes-accept-join {address: miner}))), + pos-thr: + (if + (is-eq k-at-block-asked-to-join u0) + u1 + k-at-block-asked-to-join), + neg-votes: + (default-to u0 (get value (map-get? map-votes-reject-join {address: miner}))), + neg-thr: + (if + (is-eq n-at-block-asked-to-join u1) + u1 + (if + (is-eq n-at-block-asked-to-join u2) + u2 + (+ (- n-at-block-asked-to-join k-at-block-asked-to-join) u1)))})))) + +;; miners proposed for removal + +(define-read-only (get-all-data-miners-proposed-for-removal (removal-miners-list (list 100 principal))) +(map get-all-data-miner-proposed-for-removal removal-miners-list)) + +(define-private (get-all-data-miner-proposed-for-removal (miner principal)) +(let ((k-at-block-proposed-removal (unwrap-panic (get-k-at-block-proposed-removal miner))) + (n-at-block-proposed-removal (unwrap-panic (get-n-at-block-proposed-removal miner)))) + (begin + (asserts! (is-some (get value (map-get? map-is-proposed-for-removal {address: miner}))) err-not-pending) + (ok + { + vts-for: + (default-to u0 (get value (map-get? map-votes-accept-removal {address: miner}))), + pos-thr: + (if + (is-eq k-at-block-proposed-removal u0) + u1 + k-at-block-proposed-removal), + vts-against: + (default-to u0 (get value (map-get? map-votes-reject-removal {address: miner}))), + neg-thr: + (if + (is-eq n-at-block-proposed-removal u2) + u1 + (if (is-eq n-at-block-proposed-removal u3) + u2 + (+ (- n-at-block-proposed-removal k-at-block-proposed-removal) u1)))})))) + +;; pending accept miners + +(define-read-only (get-all-data-miners-pending-accept (pending-miners-list (list 100 principal))) +(map get-data-miner-pending-accept pending-miners-list)) + +(define-private (get-data-miner-pending-accept (miner principal)) +(begin + (asserts! (is-some (get value (map-get? map-is-pending {address: miner}))) err-not-pending) + (ok + { + miner: miner, + remaining-blocks-until-join: (get-remaining-blocks-until-join) + }))) + +(define-read-only (get-remaining-blocks-until-join) + (if (> blocks-to-pass (- block-height (var-get last-join-done))) + (- blocks-to-pass (- block-height (var-get last-join-done))) + u0 + ) +) + +;; blocks number as miner +(define-read-only (get-all-data-miners-blocks (local-miners-list (list 100 principal))) +(map get-data-miner-blocks local-miners-list)) +(define-private (get-data-miner-blocks (miner principal)) +(begin + (asserts! (is-some (get value (map-get? map-is-miner {address: miner}))) err-not-in-miner-map) + (asserts! (is-some (get block-height (map-get? map-block-joined {address: miner}))) err-no-join-block-data) + (ok + { + miner: miner, + blocks-as-miner: (- block-height (unwrap-panic (get block-height (map-get? map-block-joined {address: miner})))) + }))) + +;; miners in pool + +(define-read-only (get-all-data-miners-in-pool (local-miners-list (list 100 principal))) +(map get-data-miner-in-pool local-miners-list)) + +(define-private (get-data-miner-in-pool (miner principal)) +(begin + (asserts! (is-some (get value (map-get? map-is-miner {address: miner}))) err-not-in-miner-map) + (asserts! (is-some (get block-height (map-get? map-block-joined {address: miner}))) err-no-join-block-data) + (ok + { + blocks-as-miner: (- block-height (unwrap-panic (get block-height (map-get? map-block-joined {address: miner})))), + was-blacklist: (default-to false (get value (map-get? map-blacklist {address: miner}))), + warnings: (default-to u0 (get value (map-get? map-warnings {address: miner}))), + }))) + +;; total withdrawals + +(define-read-only (get-all-data-total-withdrawals (local-miners-list (list 100 principal))) +(map get-data-miner-withdrawals local-miners-list)) + +(define-private (get-data-miner-withdrawals (miner principal)) +(begin + (ok + (default-to u0 (get value (map-get? map-total-withdraw {address: miner}))) + ))) + +;; notifier + +(define-read-only (get-data-notifier-election-process) +{ + vote-status: (var-get notifier-vote-active), + election-blocks-remaining: + (if (<= (var-get notifier-vote-end-block) block-height) + u0 + (- (var-get notifier-vote-end-block) block-height))}) + +(define-read-only (get-all-data-notifier-voter-miners (voter-miners-list (list 100 principal))) +(map get-data-notifier-voter-miner voter-miners-list)) + +(define-private (get-data-notifier-voter-miner (miner principal)) +(begin + (asserts! (is-some (get miner-voted (map-get? map-voted-update-notifier {miner-who-voted: miner}))) err-not-voted) + (ok + { + miner: miner, + voted-notifier: + (unwrap-panic (get miner-voted (map-get? map-voted-update-notifier {miner-who-voted: miner}))) + }))) + +;; balances + +(define-read-only (was-block-claimed (given-block-height uint)) + (if + (is-none (get claimed (map-get? claimed-rewards {block-number: given-block-height}))) + false + (if + (unwrap-panic (get claimed (map-get? claimed-rewards {block-number: given-block-height}))) + true + false))) + +;; BALANCES FLOW + +;; read balance +(define-read-only (get-balance (address principal)) +(map-get? balance address)) + +(define-read-only (get-miner-btc-address (miner-address principal)) + (map-get? btc-address {address: miner-address})) + +(define-public (set-my-btc-address (new-btc-address {hashbytes: (buff 20), version: (buff 1)})) + (ok (map-set btc-address {address: tx-sender} {btc-address: new-btc-address}))) + +;; deposit funds +(define-public (deposit-stx (amount uint)) +(let ((sender tx-sender) + (balance-sender (map-get? balance sender))) + (try! (stx-transfer? amount tx-sender (as-contract tx-sender))) + (if (is-none balance-sender) + (ok (map-set balance sender amount)) + (ok (map-set balance sender (+ (unwrap! balance-sender err-missing-balance) amount)))))) + +;; withdraw funds +(define-public (withdraw-stx (amount uint)) +(let ((receiver tx-sender)) + (asserts! (>= (unwrap! (map-get? balance receiver) err-missing-balance) amount) err-insufficient-balance) + (try! (as-contract (stx-transfer? amount (as-contract tx-sender) receiver))) + (if + (is-some (get value (map-get? map-total-withdraw {address: receiver}))) + (map-set map-total-withdraw {address: receiver} {value: (+ (unwrap-panic (get value (map-get? map-total-withdraw {address: receiver}))) amount)}) + (map-set map-total-withdraw {address: receiver} {value: amount})) + (ok (map-set balance receiver (- (unwrap! (map-get? balance receiver) err-missing-balance) amount))))) + +;; exchange funds +(define-public (set-auto-exchange (new-value bool)) + (ok (map-set auto-exchange {address: tx-sender} {value: new-value}))) + +(define-read-only (get-auto-exchange (address principal)) + (map-get? auto-exchange {address: address})) + +(define-public (reward-distribution (block-number uint)) +(begin + (asserts! (< block-number block-height) err-block-height-invalid) ;; +100 ? + (asserts! (is-none (get claimed (map-get? claimed-rewards {block-number: block-number}))) err-already-distributed) + (let ((miners-list-at-reward-block + (at-block (unwrap! (get-block-info? id-header-hash block-number) err-cant-unwrap-rewarded-block) (var-get miners-list))) + (block-reward (get-reward-at-block block-number)) + (current-reward (var-get reward)) + (current-miners-list-len (len miners-list-at-reward-block)) + (distribution-change-funds (mod current-reward current-miners-list-len)) + (current-change-funds (var-get reward-change-funds))) + (map-set claimed-rewards {block-number: block-number} {claimed: true}) + (var-set miners-list-len-at-reward-block current-miners-list-len) + (var-set reward (unwrap-panic (get reward block-reward))) + (var-set total-rewarded (+ (var-get total-rewarded) (var-get reward))) + (var-set blocks-won (+ (var-get blocks-won) u1)) + (var-set reward-change-funds (+ current-change-funds distribution-change-funds)) + (map distribute-reward-each-miner miners-list-at-reward-block) + (ok true)))) + +(define-private (distribute-reward-each-miner (miner principal)) +(let ((miner-balance (default-to u0 (map-get? balance miner))) + (current-reward (var-get reward)) + (current-change-funds (var-get reward-change-funds)) + (current-miners-list-len (var-get miners-list-len-at-reward-block)) + (distributed-amount (/ current-reward current-miners-list-len)) + ) + + (map-set balance miner + (+ miner-balance distributed-amount)))) + +(define-private (flush-change-each-miner (miner principal)) +(let ((miner-balance (default-to u0 (map-get? balance miner))) + (current-reward (var-get reward)) + (current-change-funds (var-get reward-change-funds)) + (current-miners-list-len (var-get miners-list-len-at-reward-block)) + (distributed-amount (/ current-reward current-miners-list-len)) + (distribution-change-funds (mod current-reward current-miners-list-len))) + (var-set reward-change-funds (+ current-change-funds distribution-change-funds)) + (map-set balance miner + (+ miner-balance distributed-amount)))) + +;; JOINING FLOW + +(define-public (ask-to-join (my-btc-address {hashbytes: (buff 20), version: (buff 1)})) +(begin + (asserts! (not (check-is-miner-now contract-caller)) err-already-joined) + (asserts! (not (check-is-waiting-now contract-caller)) err-already-asked-to-join) + (map-set map-block-asked-to-join {address: tx-sender} {value: block-height}) + (map-set btc-address {address: tx-sender} {btc-address: my-btc-address}) + (var-set waiting-list (unwrap-panic (as-max-len? (concat (var-get waiting-list) (list tx-sender)) u300))) + (map-set map-is-waiting {address: tx-sender} {value: true}) + (ok true))) + +(define-public (vote-positive-join-request (miner-to-vote principal)) +(begin + (asserts! (check-is-waiting-now miner-to-vote) err-not-asked-to-join) ;; map_is_waiting + (asserts! (unwrap! (check-is-miner-when-requested-join miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission) + (asserts! (has-voted-join miner-to-vote) err-already-voted) ;; O(1) + (map-set map-join-request-voter + {miner-to-vote: miner-to-vote, voter: tx-sender} + {value: true}) + (if (is-some (get value (map-get? map-votes-accept-join {address: miner-to-vote}))) + (map-set map-votes-accept-join {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-accept-join {address: miner-to-vote}))) u1)}) + (map-set map-votes-accept-join {address: miner-to-vote} {value: u1})) + (ok true))) + +(define-public (vote-negative-join-request (miner-to-vote principal)) +(begin + (asserts! (check-is-waiting-now miner-to-vote) err-not-asked-to-join) + (asserts! (unwrap! (check-is-miner-when-requested-join miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission) + (asserts! (has-voted-join miner-to-vote) err-already-voted) + (map-set map-join-request-voter + {miner-to-vote: miner-to-vote, voter: tx-sender} + {value: true}) + (if (is-some (get value (map-get? map-votes-reject-join {address: miner-to-vote}))) + (map-set map-votes-reject-join {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-reject-join {address: miner-to-vote}))) u1)}) + (map-set map-votes-reject-join {address: miner-to-vote} {value: u1})) + (some + (if (is-vote-rejected-join (unwrap-panic (get value (map-get? map-votes-reject-join {address: miner-to-vote}))) (unwrap-panic (get-k-at-block-asked-to-join miner-to-vote)) (unwrap-panic (get-n-at-block-asked-to-join miner-to-vote))) + (reject-miner-in-pool miner-to-vote) + false)) + (ok true))) + +(define-public (quit-waiting-list) + (begin + (asserts! (check-is-waiting-now contract-caller) err-not-asked-to-join) + (asserts! + (<= + blocks-to-pass + (- block-height + (default-to block-height + (get value + (map-get? map-block-asked-to-join {address: contract-caller}))))) err-more-blocks-to-pass) + (let ((remove-result (unwrap-panic (remove-principal-waiting-list contract-caller)))) + (var-set miner-to-remove-votes-join contract-caller) + (var-set waiting-list remove-result) + (map-delete map-is-waiting {address: contract-caller}) + (ok (clear-votes-map-join-vote contract-caller))))) + +(define-private (accept-miner-in-pool (miner principal)) +(begin + (let ((pending-accept-result (as-max-len? (concat (var-get pending-accept-list) (list miner)) u300))) + (asserts! (is-some pending-accept-result) err-list-length-exceeded) ;; O(1) + (map-set map-warnings {address: miner} {value: u0}) + (map-set balance miner u0) + (var-set miner-to-remove-votes-join miner) + (var-set waiting-list (unwrap-panic (as-max-len? (unwrap-panic (remove-principal-waiting-list miner)) u300))) ;; O(N) + (map-delete map-is-waiting {address: miner}) + (map-set map-is-pending {address: miner} {value: true}) + (clear-votes-map-join-vote miner) + (ok (var-set pending-accept-list (unwrap-panic pending-accept-result)))))) + +(define-private (reject-miner-in-pool (miner principal)) +(begin + (let ((remove-result (unwrap-panic (remove-principal-waiting-list miner)))) + (var-set miner-to-remove-votes-join miner) + (var-set waiting-list remove-result) + (map-delete map-is-waiting {address: miner}) + (clear-votes-map-join-vote miner) + true))) + +(define-private (clear-votes-map-join-vote (miner principal)) +(begin + (map-delete map-votes-accept-join {address: (var-get miner-to-remove-votes-join)}) + (map-delete map-votes-reject-join {address: (var-get miner-to-remove-votes-join)}) + (map-delete map-block-asked-to-join {address: (var-get miner-to-remove-votes-join)}) + (map remove-map-record-join-vote (var-get miners-list)))) + +(define-private (remove-map-record-join-vote (miner principal)) +(if (is-some (map-get? map-join-request-voter {miner-to-vote: (var-get miner-to-remove-votes-join), voter: miner})) + (map-delete map-join-request-voter {miner-to-vote: (var-get miner-to-remove-votes-join), voter: miner}) + false)) + +(define-private (is-in-voters-list (miner principal) (voters-list (list 300 principal))) +(is-some (index-of? voters-list miner))) + +(define-private (has-voted-join (miner principal)) +(not (if (is-some (get value (map-get? map-join-request-voter {miner-to-vote: miner, voter: tx-sender}))) + (unwrap-panic (get value (map-get? map-join-request-voter {miner-to-vote: miner, voter: tx-sender}))) + false))) + +(define-public (try-enter-pool) +(begin + (asserts! (is-some (get value (map-get? map-votes-accept-join {address: tx-sender}))) err-not-asked-to-join) + (if (is-vote-accepted (unwrap-panic (get value (map-get? map-votes-accept-join {address: tx-sender}))) (unwrap-panic (get-k-at-block-asked-to-join tx-sender))) + (accept-miner-in-pool tx-sender) + (ok false)))) + +(define-public (add-pending-miners-to-pool) +(begin + (let ((len-pending-accept-list (len (var-get pending-accept-list)))) + (asserts! (not (is-eq len-pending-accept-list u0)) err-no-pending-miners) + (asserts! (x-blocks-passed blocks-to-pass) err-more-blocks-to-pass) + (map add-miner-to-pool (var-get pending-accept-list)) + (asserts! (is-some (as-max-len? (concat (var-get miners-list) (var-get pending-accept-list)) u300)) err-list-length-exceeded) + (var-set miners-list (unwrap-panic (as-max-len? (concat (var-get miners-list) (var-get pending-accept-list)) u300))) + (var-set n (+ (var-get n) len-pending-accept-list)) + (var-set pending-accept-list (list )) + (var-set last-join-done block-height) + (some (update-threshold)) + (ok true)))) + +(define-private (update-threshold) +(let ((n-now (var-get n))) + (if + (or + (is-eq n-now u1) + (is-eq n-now u2)) + (var-set k u1) + (var-set k (/ (* (var-get k-percentage) (- n-now u1)) u100))))) + +(define-private (add-miner-to-pool (miner principal)) +(begin + (map-delete map-is-pending {address: miner}) + (map-set map-is-miner {address: miner} {value: true}) + (map-set map-block-joined {address: miner} {block-height: block-height}) + (ok true))) + +(define-private (x-blocks-passed (x uint)) +(if (>= (- block-height (var-get last-join-done)) x) + true + false)) + +(define-private (get-k-at-block-asked-to-join (miner-to-vote principal)) +(let ((block-asked-to-join (get value (map-get? map-block-asked-to-join {address: miner-to-vote})))) + (begin + (asserts! (is-some block-asked-to-join) err-not-asked-to-join) + (if + (is-eq + (unwrap-panic block-asked-to-join) + block-height) + (ok (var-get k)) + (at-block + (unwrap-panic + (get-block-info? id-header-hash + (unwrap-panic block-asked-to-join))) + (ok (var-get k))))))) + +(define-private (get-n-at-block-asked-to-join (miner-to-vote principal)) +(let ((block-asked-to-join (get value (map-get? map-block-asked-to-join {address: miner-to-vote})))) + (begin + (asserts! (is-some block-asked-to-join) err-not-asked-to-join) + (if + (is-eq + (unwrap-panic block-asked-to-join) block-height) + (ok (var-get n)) + (at-block + (unwrap-panic + (get-block-info? id-header-hash + (unwrap-panic block-asked-to-join))) + (ok (var-get n))))))) + +;; LEAVING FLOW + +(define-public (leave-pool) +(begin + (asserts! (check-is-miner-now contract-caller) err-not-in-miner-map) + (asserts! (not (is-eq (var-get notifier) contract-caller)) err-currently-notifier) + (let ((remove-result (unwrap-panic (remove-principal-miners-list tx-sender))) + (new-k-percentage (if (> (var-get n) u2) (/ (* (var-get k) u100) (- (var-get n) u2)) u100))) + ;; if n<=2, set a value for new-k-percentage > k-critical to make sure threshold is updated + (some (var-set miners-list remove-result)) + (var-set n (- (var-get n) u1)) + (map-set map-is-miner {address: tx-sender} {value: false}) + (if + (is-some (index-of? (var-get proposed-removal-list) tx-sender)) + (var-set proposed-removal-list (unwrap-panic (remove-principal-proposed-removal-list tx-sender))) + true) + (if + (>= new-k-percentage (var-get k-critical)) + (if + (> (var-get n) u1) + (some (update-threshold)) + (if + (is-eq (var-get n) u1) + (some (var-set k u1)) + (some (var-set k u0)))) + none) + (ok true)))) + +;; REMOVING FLOW + +(define-public (propose-removal (miner-to-remove principal)) +(begin + (asserts! (not (is-eq (var-get n) u1)) err-cant-remove-when-alone-in-pool) + (asserts! (check-is-miner-now contract-caller) err-not-in-miner-map) + (asserts! (check-is-miner-now miner-to-remove) err-not-in-miner-map-miner-to-remove) + (asserts! (not (check-is-proposed-for-removal-now miner-to-remove)) err-already-proposed-for-removal) + (map-set map-block-proposed-to-remove {address: miner-to-remove} {value: block-height}) + (map-set map-is-proposed-for-removal {address: miner-to-remove} {value: true}) + (var-set proposed-removal-list (unwrap! (as-max-len? (concat (var-get proposed-removal-list) (list miner-to-remove )) u300) err-list-length-exceeded)) + (ok true))) + +(define-public (vote-positive-remove-request (miner-to-vote principal)) +(begin + (asserts! (not (is-eq contract-caller miner-to-vote)) err-cant-vote-himself) + (asserts! (check-is-proposed-for-removal-now miner-to-vote) err-not-proposed-for-removal) ;; map_is_proposed_for_removal + (asserts! (is-ok (get-k-at-block-proposed-removal miner-to-vote)) err-not-proposed-for-removal-proposal-block-missing) + (asserts! (unwrap! (check-is-miner-when-requested-remove miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission) + (asserts! (has-voted-remove miner-to-vote) err-already-voted) ;; O(1) + (map-set map-remove-request-voter + {miner-to-vote: miner-to-vote, voter: tx-sender} + {value: true}) + (if (is-some (get value (map-get? map-votes-accept-removal {address: miner-to-vote}))) + (map-set map-votes-accept-removal {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-accept-removal {address: miner-to-vote}))) u1)}) + (map-set map-votes-accept-removal {address: miner-to-vote} {value: u1})) + (some + (if (is-vote-accepted (unwrap-panic (get value (map-get? map-votes-accept-removal {address: miner-to-vote}))) (unwrap-panic (get-k-at-block-proposed-removal miner-to-vote))) + (process-removal miner-to-vote) + (ok false))) + (ok true))) + +(define-public (vote-negative-remove-request (miner-to-vote principal)) +(begin + (asserts! (not (is-eq contract-caller miner-to-vote)) err-cant-vote-himself) + (asserts! (check-is-proposed-for-removal-now miner-to-vote) err-not-proposed-for-removal) ;; map_is_waiting + (asserts! (is-ok (get-k-at-block-proposed-removal miner-to-vote)) err-not-proposed-for-removal-proposal-block-missing) + (asserts! (unwrap! (check-is-miner-when-requested-remove miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission) + (asserts! (has-voted-remove miner-to-vote) err-already-voted) ;; O(1) + (map-set map-remove-request-voter + {miner-to-vote: miner-to-vote, voter: tx-sender} + {value: true}) + (if (is-some (get value (map-get? map-votes-reject-removal {address: miner-to-vote}))) + (map-set map-votes-reject-removal {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-reject-removal {address: miner-to-vote}))) u1)}) + (map-set map-votes-reject-removal {address: miner-to-vote} {value: u1})) + (some + (if (is-vote-rejected-remove (unwrap-panic (get value (map-get? map-votes-reject-removal {address: miner-to-vote}))) (unwrap-panic (get-k-at-block-proposed-removal miner-to-vote)) (unwrap-panic (get-n-at-block-proposed-removal miner-to-vote))) + (reject-removal miner-to-vote) + (ok false))) + (ok true))) + +(define-public (quit-proposed-removal-list) + (begin + (asserts! + (<= + blocks-to-pass + (- block-height + (default-to block-height + (get value + (map-get? map-block-proposed-to-remove {address: contract-caller}))))) err-more-blocks-to-pass) + (let ((remove-result (unwrap-panic (remove-principal-proposed-removal-list contract-caller)))) + (var-set miner-to-remove-votes-remove contract-caller) + (var-set proposed-removal-list remove-result) + (map-delete map-is-proposed-for-removal {address: contract-caller}) + (ok (clear-votes-map-remove-vote contract-caller))))) + +(define-private (process-removal (miner principal)) +(begin + (let ((remove-result (unwrap-panic (remove-principal-miners-list miner))) + (new-k-percentage (if (> (var-get n) u2) (/ (* (var-get k) u100) (- (var-get n) u2)) u100))) + (some (var-set miners-list remove-result)) + (var-set miner-to-remove-votes-remove miner) + (var-set n (- (var-get n) u1)) + (map-delete map-is-miner {address: miner}) + (map-set map-blacklist {address: miner} {value: true}) + (var-set proposed-removal-list (unwrap-panic (remove-principal-proposed-removal-list miner))) + (clear-votes-map-remove-vote miner) + (if (>= new-k-percentage (var-get k-critical)) + (if + (> (var-get n) u1) + (update-threshold) + (if + (is-eq (var-get n) u1) + (var-set k u1) + (var-set k u0))) + false) + (ok true)))) + +(define-private (reject-removal (miner principal)) +(begin + (var-set miner-to-remove-votes-remove miner) + (var-set proposed-removal-list (unwrap-panic (remove-principal-proposed-removal-list miner))) + (clear-votes-map-remove-vote miner) + (ok true))) + +(define-private (has-voted-remove (miner principal)) +(not (if (is-some (get value (map-get? map-remove-request-voter {miner-to-vote: miner, voter: tx-sender}))) + (unwrap-panic (get value (map-get? map-remove-request-voter {miner-to-vote: miner, voter: tx-sender}))) + false + ))) + +(define-private (clear-votes-map-remove-vote (miner principal)) +(begin + (map-delete map-votes-accept-removal {address: (var-get miner-to-remove-votes-remove)}) + (map-delete map-votes-reject-removal {address: (var-get miner-to-remove-votes-remove)}) + (map-delete map-block-proposed-to-remove {address: (var-get miner-to-remove-votes-remove)}) + (map-delete map-is-proposed-for-removal {address: (var-get miner-to-remove-votes-remove)}) + (map remove-map-record-remove-vote (var-get miners-list)))) + +(define-private (remove-map-record-remove-vote (miner principal)) +(if (is-some (map-get? map-remove-request-voter {miner-to-vote: (var-get miner-to-remove-votes-remove), voter: miner})) + (map-delete map-remove-request-voter {miner-to-vote: (var-get miner-to-remove-votes-remove), voter: miner}) + false)) + +(define-private (get-k-at-block-proposed-removal (miner-to-vote principal)) +(let ((block-proposed-to-remove (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})))) + (begin + (asserts! (is-some block-proposed-to-remove) err-not-proposed-for-removal) + (if + (is-eq (unwrap-panic + block-proposed-to-remove) + block-height) + (ok (var-get k)) + (at-block + (unwrap-panic + (get-block-info? id-header-hash + (unwrap-panic block-proposed-to-remove))) + (ok (var-get k))))))) + +(define-private (get-n-at-block-proposed-removal (miner-to-vote principal)) +(let ((block-proposed-to-remove (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})))) + (begin + (asserts! (is-some block-proposed-to-remove) err-not-proposed-for-removal) + (if + (is-eq (unwrap-panic + block-proposed-to-remove) + block-height) + (ok (var-get n)) + (at-block + (unwrap-panic + (get-block-info? id-header-hash + (unwrap-panic block-proposed-to-remove))) + (ok (var-get n))))))) + +;; UPDATE NOTIFIER + +(define-public (start-vote-notifier) +(begin + (asserts! (not (var-get notifier-vote-active)) err-vote-started-already) + (var-set notifier-vote-start-block block-height) + (var-set notifier-vote-end-block (+ (var-get notifier-vote-start-block) notifier-election-blocks-to-pass)) + (var-set notifier-vote-active true) + (if (var-get notifier-previous-entries-removed) + (begin + (ok (var-set notifier-previous-entries-removed false))) + (end-vote-notifier-private)))) + +(define-public (end-vote-notifier) +(begin + (asserts! (>= block-height (var-get notifier-vote-end-block)) err-voting-still-active) + (var-set notifier-vote-active false) + (end-vote-notifier-private))) + +(define-private (end-vote-notifier-private) +(begin + (unwrap! (get-max-votes-number-notifier) (err u99999)) + (if + (> + (var-get max-votes-notifier) + (/ (var-get k) u2)) + (var-set notifier (var-get max-voted-proposed-notifier)) + false) + (delete-all-notifier-entries) + (ok true))) + +(define-private (get-max-votes-number-notifier) +(ok (map compare-votes-number-notifier (var-get miners-list)))) + +(define-private (compare-votes-number-notifier (proposed-notifier principal)) +(ok +(if (is-some (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier}))) + (if (> (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier}))) (/ (var-get k) u2)) + (if + (> (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier}))) (var-get max-votes-notifier)) + (begin + (var-set max-votes-notifier (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier})))) + (var-set max-voted-proposed-notifier proposed-notifier)) + (if + (is-eq (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier}))) (var-get max-votes-notifier)) + (if + (< + (unwrap-panic (get block-height (map-get? map-block-joined {address: proposed-notifier}))) + (unwrap-panic (get block-height (map-get? map-block-joined {address: (var-get max-voted-proposed-notifier)})))) + (begin + (var-set max-voted-proposed-notifier proposed-notifier)) + false) + false)) + false) + false))) + +(define-private (delete-all-notifier-entries) +(begin + (var-set max-votes-notifier u0) + (var-set max-voted-proposed-notifier (var-get notifier)) + (map delete-one-notifier-entry (var-get miners-list)) + (var-set notifier-previous-entries-removed true))) + +(define-private (delete-one-notifier-entry (miner principal)) +(begin + (map-delete map-voted-update-notifier {miner-who-voted: miner}) + (map-delete map-votes-notifier {voted-notifier: miner}))) + +(define-public (vote-notifier (voted-notifier principal)) +(begin + (asserts! (and (is-some (get value (map-get? map-is-miner {address: voted-notifier}))) (unwrap-panic (get value (map-get? map-is-miner {address: voted-notifier})))) err-not-in-miner-map) + (asserts! (and (is-some (get value (map-get? map-is-miner {address: contract-caller}))) (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller})))) err-no-vote-permission) + (asserts! (var-get notifier-vote-active) err-not-voting-period) + (asserts! (not (is-eq contract-caller voted-notifier)) err-cant-vote-himself) + (asserts! (< block-height (var-get notifier-vote-end-block)) err-not-voting-period) + (asserts! (is-none (get miner-voted (map-get? map-voted-update-notifier {miner-who-voted: tx-sender}))) err-already-voted) + (map-set map-voted-update-notifier {miner-who-voted: tx-sender} {miner-voted: voted-notifier}) + (if (is-none (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) + (map-set map-votes-notifier {voted-notifier: voted-notifier} {votes-number: u1}) + (map-set map-votes-notifier {voted-notifier: voted-notifier} {votes-number: (+ (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) u1)})) + (try! + (if (is-vote-accepted (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) (var-get k)) + (begin + (var-set notifier voted-notifier) + (var-set notifier-vote-end-block block-height) + (var-set notifier-vote-active false) + (end-vote-notifier)) + (ok false))) +(ok true))) + +;; WARNING FLOW + +(define-public (warn-miner (miner principal)) +(begin +(let ((incremented-value + (if + (is-some (get value (map-get? map-warnings {address: miner}))) + (+ (unwrap-panic (get value (map-get? map-warnings {address: miner}))) u1) + u1))) + (asserts! (is-eq contract-caller (var-get notifier)) err-only-notifier) + (asserts! + (not (and + (is-none (at-block (unwrap! (get-block-info? id-header-hash (- block-height u1)) err-cant-unwrap-block-info) (get value (map-get? map-warnings {address: miner})))) + (>= incremented-value u2))) + err-one-warning-per-block) + (asserts! + (not + (>= + (- + incremented-value + (unwrap! (at-block (unwrap! (get-block-info? id-header-hash (- block-height u1)) err-cant-unwrap-block-info) (get value (map-get? map-warnings {address: miner}))) err-cant-unwrap-block-info)) + u2)) + err-one-warning-per-block) + (ok + (if + (is-some (get value (map-get? map-warnings {address: miner}))) + (map-set map-warnings {address: miner} {value: (+ (unwrap-panic (get value (map-get? map-warnings {address: miner}))) u1)}) + (map-set map-warnings {address: miner} {value: u1})))))) + +;; ELECTION FUNCTIONS + +(define-private (is-vote-accepted (votes-number uint) (k-local uint)) +(if + (is-eq k-local u0) ;; k is 0 for n=1, n=2 + (>= votes-number u1) + (>= votes-number k-local))) + +(define-private (is-democratic-vote-accepted-notifier (votes-number uint) (k-local uint)) +(if + (is-eq k-local u0) ;; k is 0 for n=1, n=2 + (>= votes-number u1) + (>= votes-number (/ k-local u2)))) + +(define-private (is-vote-rejected-join (votes-number uint) (k-local uint) (n-local uint)) +(if + (is-eq n-local u1) + (>= votes-number u1) + (if (is-eq n-local u2) + (>= votes-number u2) + (>= votes-number (+ (- n-local k-local) u1))))) + + +(define-private (is-vote-rejected-remove (votes-number uint) (k-local uint) (n-local uint)) +(if + (is-eq n-local u2) + (>= votes-number u1) + (if (is-eq n-local u3) + (>= votes-number u2) + (>= votes-number (+ (- n-local k-local) u1))))) + +(define-private (is-vote-rejected-notifier (votes-number uint) (k-local uint) (n-local uint)) +(if (is-eq n-local u2) + (>= votes-number u1) + (if (is-eq n-local u3) + (>= votes-number u2) + (>= votes-number (+ (- n-local k-local) u1))))) + +;; RECOVERING UNASSIGNED FUNDS FUNCTIONS + +;; A public function each miner can call in order to recover the +;; undistributed rewards caused by dividing rewards to stackers +(define-public (flush-change) +(let ((total-change-funds (var-get reward-change-funds)) + (current-miners-list-len (len (var-get miners-list))) + (distributed-change-funds (/ total-change-funds current-miners-list-len)) + (change-after-flushing (mod total-change-funds current-miners-list-len))) + (asserts! (check-is-miner-now contract-caller) err-not-in-miner-map) + (var-set temp-distributed-change-funds distributed-change-funds) + (var-set temp-change-after-flushing change-after-flushing) + (map flush-distribution-one-miner (var-get miners-list)) + (ok + (var-set reward-change-funds + (- + total-change-funds + (* + current-miners-list-len + distributed-change-funds)))))) + +(define-private (flush-distribution-one-miner (miner principal)) +(let ((miner-balance (default-to u0 (map-get? balance miner))) + (total-change-funds (var-get reward-change-funds)) + (current-miners-list-len (len (var-get miners-list))) + (distributed-change-funds (/ total-change-funds current-miners-list-len))) + (map-set balance miner + (+ + miner-balance + (var-get temp-distributed-change-funds))))) + +;; LIST PROCESSING FUNCTIONS + +(define-private (remove-principal-waiting-list (miner principal)) +(begin + (var-set waiting-list-miner-to-remove miner) + (ok (filter is-principal-in-waiting-list (var-get waiting-list))))) + +(define-private (remove-principal-pending-accept-list (miner principal)) +(begin + (var-set waiting-list-miner-to-remove miner) + (ok (filter is-principal-in-pending-accept-list (var-get pending-accept-list))))) + +(define-private (remove-principal-miners-list (miner principal)) +(begin + (var-set miners-list-miner-to-remove miner) + (ok (filter is-principal-in-miners-list (var-get miners-list))))) + +(define-private (remove-principal-proposed-removal-list (miner principal)) +(begin + (var-set proposed-removal-list-miner-to-remove miner) + (ok (filter is-principal-in-proposed-removal-list (var-get proposed-removal-list))))) + +;; MINER STATUS FUNCTIONS + +(define-private (check-is-miner-when-requested-join (miner-to-vote principal)) +(ok + (if + (is-some + (if + (is-eq + (unwrap! (get value (map-get? map-block-asked-to-join {address: miner-to-vote})) err-cant-unwrap-asked-to-join) + block-height) + (get value (map-get? map-is-miner {address: contract-caller})) + (at-block + (unwrap! + (get-block-info? id-header-hash + (unwrap! (get value (map-get? map-block-asked-to-join {address: miner-to-vote})) err-cant-unwrap-asked-to-join)) + err-cant-unwrap-block-info) + (get value (map-get? map-is-miner {address: contract-caller}))))) + (if + (is-eq + (unwrap! + (get value (map-get? map-block-asked-to-join {address: miner-to-vote})) + err-cant-unwrap-asked-to-join) + block-height) + (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller}))) + (at-block + (unwrap! + (get-block-info? id-header-hash + (unwrap-panic (get value (map-get? map-block-asked-to-join {address: miner-to-vote})))) + err-cant-unwrap-block-info) + (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller}))))) + false))) + +(define-private (check-is-miner-when-requested-remove (miner-to-vote principal)) +(ok + (if + (is-some + (if + (is-eq + (unwrap! (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})) err-cant-unwrap-asked-to-join) + block-height) + (get value (map-get? map-is-miner {address: contract-caller})) + (at-block + (unwrap! + (get-block-info? id-header-hash + (unwrap! (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})) err-cant-unwrap-asked-to-join)) + err-cant-unwrap-block-info) + (get value (map-get? map-is-miner {address: contract-caller}))))) + (if + (is-eq + (unwrap! + (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})) + err-cant-unwrap-asked-to-join) + block-height) + (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller}))) + (at-block + (unwrap! + (get-block-info? id-header-hash + (unwrap-panic (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})))) + err-cant-unwrap-block-info) + (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller}))))) + false))) + +(define-read-only (check-is-miner-when-requested-join-tool-fn (miner-to-vote-address principal)) +(get value (map-get? map-is-miner {address: contract-caller}))) + +(define-private (check-is-miner-now (miner principal)) +(if (is-some (get value (map-get? map-is-miner {address: miner}))) + (unwrap-panic (get value (map-get? map-is-miner {address: miner}))) + false)) + +(define-private (check-is-proposed-for-removal-now (miner principal)) +(if (is-some (get value (map-get? map-is-proposed-for-removal {address: miner}))) + (unwrap-panic (get value (map-get? map-is-proposed-for-removal {address: miner}))) + false)) + +(define-private (check-is-waiting-now (miner principal)) +(if (is-some (get value (map-get? map-is-waiting {address: miner}))) + (unwrap-panic (get value (map-get? map-is-waiting {address: miner}))) + false)) + +(define-private (check-is-pending-now (miner principal)) +(if (is-some (get value (map-get? map-is-pending {address: miner}))) + (unwrap-panic (get value (map-get? map-is-pending {address: miner}))) + false) +) + +(define-private (get-reward-at-block (block-number uint)) +(begin + {reward: (get-block-info? block-reward block-number), + claimer: (get-block-info? miner-address block-number)})) + +(define-read-only (get-reward-at-block-read (block-number uint)) +(begin + {reward: (get-block-info? block-reward block-number), + claimer: (get-block-info? miner-address block-number) + })) + +(define-read-only (get-address-status (address principal)) +(if (check-is-miner-now address) + (ok "is-miner") + (if (check-is-waiting-now address) + (ok "is-waiting") + (if (check-is-pending-now address) + (ok "is-pending") + (ok "is-none") + ) + ) +)) + +;; READ-ONLY UTILS + +;; (define-read-only (check-vote-accepted) ;; to check the vote status inside FE +;; (is-vote-accepted (unwrap-panic (get value (map-get? map-votes-accept-join {address: tx-sender}))))) + +(define-read-only (get-k) +(var-get k)) + +(define-read-only (get-notifier) +(var-get notifier)) + +(define-read-only (get-blocks-won) +(var-get blocks-won)) + +(define-read-only (get-total-rewards-distributed) +(var-get total-rewarded)) + +(define-read-only (get-waiting-list) +(var-get waiting-list)) + +(define-read-only (get-miners-list) +(var-get miners-list)) + +(define-read-only (get-pending-accept-list) +(var-get pending-accept-list )) + +(define-read-only (get-proposed-removal-list) +(var-get proposed-removal-list )) + +(define-read-only (get-notifier-vote-status) +(var-get notifier-vote-active)) + +(define-read-only (get-notifier-vote-number (voted-notifier principal)) +(get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) + +(define-read-only (get-max-voted-notifier) +(var-get max-voted-proposed-notifier)) + +(define-read-only (get-max-votes-notifier) +(var-get max-votes-notifier)) + +(define-read-only (get-current-block) +(ok block-height)) + +(define-private (is-principal-in-waiting-list (miner principal)) +(not (is-eq + (var-get waiting-list-miner-to-remove) + miner))) + +(define-private (is-principal-in-pending-accept-list (miner principal)) +(not (is-eq + (var-get pending-accept-list-miner-to-remove) + miner))) + +(define-private (is-principal-in-miners-list (miner principal)) +(not (is-eq + (var-get miners-list-miner-to-remove) + miner))) + +(define-private (is-principal-in-proposed-removal-list (miner principal)) +(not (is-eq + (var-get proposed-removal-list-miner-to-remove) + miner))) \ No newline at end of file diff --git a/sbtc-ops/smart-contract/contracts/mining-pool-test.clar b/sbtc-ops/smart-contract/contracts/mining-pool-test.clar new file mode 100644 index 00000000..3b024512 --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/mining-pool-test.clar @@ -0,0 +1,1067 @@ +(define-constant err-invalid (err u300)) +(define-constant err-list-length-exceeded (err u101)) +(define-constant err-already-asked-to-join (err u102)) +(define-constant err-already-joined (err u103)) +(define-constant err-not-in-miner-map (err u104)) +(define-constant err-no-vote-permission (err u105)) +(define-constant err-more-blocks-to-pass (err u106)) +(define-constant err-no-pending-miners (err u107)) +(define-constant err-already-voted (err u108)) +(define-constant err-not-asked-to-join (err u109)) +(define-constant err-cant-unwrap-check-miner (err u110)) +(define-constant err-cant-unwrap-asked-to-join (err u111)) +(define-constant err-cant-unwrap-block-info (err u112)) +(define-constant err-currently-notifier (err u113)) +(define-constant err-not-in-miner-map-miner-to-remove (err u114)) +(define-constant err-already-proposed-for-removal (err u116)) +(define-constant err-not-proposed-for-removal (err u117)) +(define-constant err-cant-remove-when-alone-in-pool (err u118)) +(define-constant err-cant-vote-himself (err u119)) +(define-constant err-cant-change-notifier (err u120)) +(define-constant err-already-proposed-for-notifier (err u121)) +(define-constant err-not-proposed-for-removal-proposal-block-missing (err u122)) +(define-constant err-not-proposed-for-notifier-k-missing (err u123)) +(define-constant err-not-proposed-notifier (err u124)) +(define-constant err-already-notifier (err u125)) +(define-constant err-not-in-miner-map-proposed-notifier (err u126)) +(define-constant err-vote-started-already (err u127)) +(define-constant err-voting-still-active (err u128)) +(define-constant err-not-voting-period (err u129)) +(define-constant err-not-waiting (err u130)) +(define-constant err-not-pending (err u131)) +(define-constant err-no-join-block-data (err u132)) +(define-constant err-not-voted (err u133)) +(define-constant err-only-notifier (err u134)) +(define-constant err-one-warning-per-block (err u135)) +(define-constant err-block-height-invalid (err u136)) +(define-constant err-unwrap-miner-index (err u999)) +(define-constant err-insufficient-balance (err u1001)) +(define-constant err-missing-balance (err u1002)) +(define-constant err-already-distributed (err u1003)) +(define-constant err-cant-unwrap-rewarded-block (err u1004)) + +(define-constant notifier-election-blocks-to-pass u144) +(define-constant blocks-to-pass u100) + +(define-map balance principal uint) +(define-map claimed-rewards { block-number: uint } { claimed: bool }) +(define-map map-is-miner { address: principal } { value: bool }) +(define-map map-is-waiting { address: principal } { value: bool }) +(define-map map-is-pending { address: principal } { value: bool }) +(define-map map-is-proposed-for-removal { address: principal } { value: bool }) +(define-map map-block-asked-to-join { address: principal } { value: uint }) +(define-map map-block-proposed-to-remove { address: principal } { value: uint }) +(define-map map-block-joined { address: principal } { block-height: uint }) +(define-map map-balance-xBTC { address: principal } { value: uint }) +(define-map auto-exchange { address: principal } { value: bool }) +(define-map btc-address { address: principal } { btc-address: (string-ascii 41) }) + +(define-map map-votes-accept-join { address: principal } { value: uint }) +(define-map map-votes-reject-join { address: principal } { value: uint }) +(define-map map-votes-accept-removal { address: principal } { value: uint }) +(define-map map-votes-reject-removal { address: principal } { value: uint }) +(define-map map-join-request-voter { miner-to-vote: principal, voter: principal } { value: bool }) +(define-map map-remove-request-voter { miner-to-vote: principal, voter: principal } { value: bool }) +(define-map map-voted-update-notifier { miner-who-voted: principal } { miner-voted: principal }) +(define-map map-votes-notifier { voted-notifier: principal } { votes-number: uint }) +(define-map map-blacklist { address: principal } { value: bool }) +(define-map map-total-withdraw { address: principal } { value: uint }) +(define-map map-warnings { address: principal } { value: uint }) + +(define-data-var miners-list-len-at-reward-block uint u0) +(define-data-var notifier principal tx-sender) +(define-data-var waiting-list (list 300 principal) (list )) +(define-data-var miners-list (list 300 principal) (list (var-get notifier))) +(define-data-var pending-accept-list (list 300 principal) (list )) +(define-data-var proposed-removal-list (list 300 principal) (list )) +(define-data-var n uint u1) +(define-data-var k-percentage uint u67) +(define-data-var k uint u1) +(define-data-var k-critical uint u75) +(define-data-var waiting-list-miner-to-remove principal tx-sender) ;; use in remove-principal-miners-list +(define-data-var pending-accept-list-miner-to-remove principal tx-sender) +(define-data-var miners-list-miner-to-remove principal tx-sender) +(define-data-var proposed-removal-list-miner-to-remove principal tx-sender) +(define-data-var last-join-done uint u1) +(define-data-var miner-to-remove-votes-join principal tx-sender) +(define-data-var miner-to-remove-votes-remove principal tx-sender) +(define-data-var notifier-previous-entries-removed bool true) +(define-data-var notifier-vote-active bool false) +(define-data-var notifier-vote-start-block uint u0) +(define-data-var notifier-vote-end-block uint u0) +(define-data-var max-votes-notifier uint u0) +(define-data-var max-voted-proposed-notifier principal tx-sender) +(define-data-var reward uint u0) +(define-data-var total-rewarded uint u0) +(define-data-var blocks-won uint u0) +(define-data-var reward-change-funds uint u0) +(define-data-var temp-distributed-change-funds uint u0) +(define-data-var temp-change-after-flushing uint u0) + +(map-set map-is-miner {address: tx-sender} {value: true}) +(map-set map-block-joined {address: tx-sender} {block-height: block-height}) +(map-set balance tx-sender u0) +;; at new join -> block height - last-join-done >= 100 ! + +;; READ ONLY FE UTILS + +;; waiting miners + +(define-read-only (get-all-data-waiting-miners (waiting-miners-list (list 100 principal))) +(map get-all-data-waiting-miner waiting-miners-list)) + +(define-private (get-all-data-waiting-miner (miner principal)) +(let ((k-at-block-asked-to-join (unwrap-panic (get-k-at-block-asked-to-join miner))) + (n-at-block-asked-to-join (unwrap-panic (get-n-at-block-asked-to-join miner)))) + (begin + (asserts! (is-some (get value (map-get? map-is-waiting {address: miner}))) err-not-waiting) + (ok + { + pos-votes: + (default-to u0 (get value (map-get? map-votes-accept-join {address: miner}))), + pos-thr: + (if + (is-eq k-at-block-asked-to-join u0) + u1 + k-at-block-asked-to-join), + neg-votes: + (default-to u0 (get value (map-get? map-votes-reject-join {address: miner}))), + neg-thr: + (if + (is-eq n-at-block-asked-to-join u1) + u1 + (if + (is-eq n-at-block-asked-to-join u2) + u2 + (+ (- n-at-block-asked-to-join k-at-block-asked-to-join) u1)))})))) + +;; miners proposed for removal + +(define-read-only (get-all-data-miners-proposed-for-removal (removal-miners-list (list 100 principal))) +(map get-all-data-miner-proposed-for-removal removal-miners-list)) + +(define-private (get-all-data-miner-proposed-for-removal (miner principal)) +(let ((k-at-block-proposed-removal (unwrap-panic (get-k-at-block-proposed-removal miner))) + (n-at-block-proposed-removal (unwrap-panic (get-n-at-block-proposed-removal miner)))) + (begin + (asserts! (is-some (get value (map-get? map-is-proposed-for-removal {address: miner}))) err-not-pending) + (ok + { + vts-for: + (default-to u0 (get value (map-get? map-votes-accept-removal {address: miner}))), + pos-thr: + (if + (is-eq k-at-block-proposed-removal u0) + u1 + k-at-block-proposed-removal), + vts-against: + (default-to u0 (get value (map-get? map-votes-reject-removal {address: miner}))), + neg-thr: + (if + (is-eq n-at-block-proposed-removal u2) + u1 + (if (is-eq n-at-block-proposed-removal u3) + u2 + (+ (- n-at-block-proposed-removal k-at-block-proposed-removal) u1)))})))) + +;; pending accept miners + +(define-read-only (get-all-data-miners-pending-accept (pending-miners-list (list 100 principal))) +(map get-data-miner-pending-accept pending-miners-list)) + +(define-private (get-data-miner-pending-accept (miner principal)) +(begin + (asserts! (is-some (get value (map-get? map-is-pending {address: miner}))) err-not-pending) + (ok + { + miner: miner, + remaining-blocks-until-join: (get-remaining-blocks-until-join) + }))) + +(define-read-only (get-remaining-blocks-until-join) + (if (> blocks-to-pass (- block-height (var-get last-join-done))) + (- blocks-to-pass (- block-height (var-get last-join-done))) + u0 + ) +) + +;; blocks number as miner +(define-read-only (get-all-data-miners-blocks (local-miners-list (list 100 principal))) +(map get-data-miner-blocks local-miners-list)) +(define-private (get-data-miner-blocks (miner principal)) +(begin + (asserts! (is-some (get value (map-get? map-is-miner {address: miner}))) err-not-in-miner-map) + (asserts! (is-some (get block-height (map-get? map-block-joined {address: miner}))) err-no-join-block-data) + (ok + { + miner: miner, + blocks-as-miner: (- block-height (unwrap-panic (get block-height (map-get? map-block-joined {address: miner})))) + }))) + +;; miners in pool + +(define-read-only (get-all-data-miners-in-pool (local-miners-list (list 100 principal))) +(map get-data-miner-in-pool local-miners-list)) + +(define-private (get-data-miner-in-pool (miner principal)) +(begin + (asserts! (is-some (get value (map-get? map-is-miner {address: miner}))) err-not-in-miner-map) + (asserts! (is-some (get block-height (map-get? map-block-joined {address: miner}))) err-no-join-block-data) + (ok + { + blocks-as-miner: (- block-height (unwrap-panic (get block-height (map-get? map-block-joined {address: miner})))), + was-blacklist: (default-to false (get value (map-get? map-blacklist {address: miner}))), + warnings: (default-to u0 (get value (map-get? map-warnings {address: miner}))), + }))) + +;; total withdrawals + +(define-read-only (get-all-data-total-withdrawals (local-miners-list (list 100 principal))) +(map get-data-miner-withdrawals local-miners-list)) + +(define-private (get-data-miner-withdrawals (miner principal)) +(begin + (ok + (default-to u0 (get value (map-get? map-total-withdraw {address: miner}))) + ))) + +;; notifier + +(define-read-only (get-data-notifier-election-process) +{ + vote-status: (var-get notifier-vote-active), + election-blocks-remaining: + (if (<= (var-get notifier-vote-end-block) block-height) + u0 + (- (var-get notifier-vote-end-block) block-height))}) + +(define-read-only (get-all-data-notifier-voter-miners (voter-miners-list (list 100 principal))) +(map get-data-notifier-voter-miner voter-miners-list)) + +(define-private (get-data-notifier-voter-miner (miner principal)) +(begin + (asserts! (is-some (get miner-voted (map-get? map-voted-update-notifier {miner-who-voted: miner}))) err-not-voted) + (ok + { + miner: miner, + voted-notifier: + (unwrap-panic (get miner-voted (map-get? map-voted-update-notifier {miner-who-voted: miner}))) + }))) + +;; balances + +(define-read-only (was-block-claimed (given-block-height uint)) + (if + (is-none (get claimed (map-get? claimed-rewards {block-number: given-block-height}))) + false + (if + (unwrap-panic (get claimed (map-get? claimed-rewards {block-number: given-block-height}))) + true + false))) + +;; BALANCES FLOW + +;; read balance +(define-read-only (get-balance (address principal)) +(map-get? balance address)) + +(define-read-only (get-miner-btc-address (miner-address principal)) + (map-get? btc-address {address: miner-address})) + +(define-public (set-my-btc-address (new-btc-address (string-ascii 41))) + (ok (map-set btc-address {address: tx-sender} {btc-address: new-btc-address}))) + +;; deposit funds +(define-public (deposit-stx (amount uint)) +(let ((sender tx-sender) + (balance-sender (map-get? balance sender))) + (try! (stx-transfer? amount tx-sender (as-contract tx-sender))) + (if (is-none balance-sender) + (ok (map-set balance sender amount)) + (ok (map-set balance sender (+ (unwrap! balance-sender err-missing-balance) amount)))))) + +;; withdraw funds +(define-public (withdraw-stx (amount uint)) +(let ((receiver tx-sender)) + (asserts! (>= (unwrap! (map-get? balance receiver) err-missing-balance) amount) err-insufficient-balance) + (try! (as-contract (stx-transfer? amount (as-contract tx-sender) receiver))) + (if + (is-some (get value (map-get? map-total-withdraw {address: receiver}))) + (map-set map-total-withdraw {address: receiver} {value: (+ (unwrap-panic (get value (map-get? map-total-withdraw {address: receiver}))) amount)}) + (map-set map-total-withdraw {address: receiver} {value: amount})) + (ok (map-set balance receiver (- (unwrap! (map-get? balance receiver) err-missing-balance) amount))))) + +;; exchange funds +(define-public (set-auto-exchange (new-value bool)) + (ok (map-set auto-exchange {address: tx-sender} {value: new-value}))) + +(define-read-only (get-auto-exchange (address principal)) + (map-get? auto-exchange {address: address})) + +(define-public (reward-distribution (block-number uint)) +(begin + (asserts! (< block-number block-height) err-block-height-invalid) ;; +100 ? + (asserts! (is-none (get claimed (map-get? claimed-rewards {block-number: block-number}))) err-already-distributed) + (let ((miners-list-at-reward-block + (at-block (unwrap! (get-block-info? id-header-hash block-number) err-cant-unwrap-rewarded-block) (var-get miners-list))) + (block-reward (get-reward-at-block block-number)) + (current-reward (var-get reward)) + (current-miners-list-len (len miners-list-at-reward-block)) + (distribution-change-funds (mod current-reward current-miners-list-len)) + (current-change-funds (var-get reward-change-funds))) + (map-set claimed-rewards {block-number: block-number} {claimed: true}) + (var-set miners-list-len-at-reward-block current-miners-list-len) + (var-set reward (unwrap-panic (get reward block-reward))) + (var-set total-rewarded (+ (var-get total-rewarded) (var-get reward))) + (var-set blocks-won (+ (var-get blocks-won) u1)) + (var-set reward-change-funds (+ current-change-funds distribution-change-funds)) + (map distribute-reward-each-miner miners-list-at-reward-block) + (ok true)))) + +(define-private (distribute-reward-each-miner (miner principal)) +(let ((miner-balance (default-to u0 (map-get? balance miner))) + (current-reward (var-get reward)) + (current-change-funds (var-get reward-change-funds)) + (current-miners-list-len (var-get miners-list-len-at-reward-block)) + (distributed-amount (/ current-reward current-miners-list-len)) + ) + + (map-set balance miner + (+ miner-balance distributed-amount)))) + +(define-private (flush-change-each-miner (miner principal)) +(let ((miner-balance (default-to u0 (map-get? balance miner))) + (current-reward (var-get reward)) + (current-change-funds (var-get reward-change-funds)) + (current-miners-list-len (var-get miners-list-len-at-reward-block)) + (distributed-amount (/ current-reward current-miners-list-len)) + (distribution-change-funds (mod current-reward current-miners-list-len))) + (var-set reward-change-funds (+ current-change-funds distribution-change-funds)) + (map-set balance miner + (+ miner-balance distributed-amount)))) + +;; JOINING FLOW + +(define-public (ask-to-join (my-btc-address (string-ascii 41))) +(begin + (asserts! (not (check-is-miner-now contract-caller)) err-already-joined) + (asserts! (not (check-is-waiting-now contract-caller)) err-already-asked-to-join) + (map-set map-block-asked-to-join {address: tx-sender} {value: block-height}) + (map-set btc-address {address: tx-sender} {btc-address: my-btc-address}) + (var-set waiting-list (unwrap-panic (as-max-len? (concat (var-get waiting-list) (list tx-sender)) u300))) + (map-set map-is-waiting {address: tx-sender} {value: true}) + (ok true))) + +(define-public (vote-positive-join-request (miner-to-vote principal)) +(begin + (asserts! (check-is-waiting-now miner-to-vote) err-not-asked-to-join) ;; map_is_waiting + (asserts! (unwrap! (check-is-miner-when-requested-join miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission) + (asserts! (has-voted-join miner-to-vote) err-already-voted) ;; O(1) + (map-set map-join-request-voter + {miner-to-vote: miner-to-vote, voter: tx-sender} + {value: true}) + (if (is-some (get value (map-get? map-votes-accept-join {address: miner-to-vote}))) + (map-set map-votes-accept-join {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-accept-join {address: miner-to-vote}))) u1)}) + (map-set map-votes-accept-join {address: miner-to-vote} {value: u1})) + (ok true))) + +(define-public (vote-negative-join-request (miner-to-vote principal)) +(begin + (asserts! (check-is-waiting-now miner-to-vote) err-not-asked-to-join) + (asserts! (unwrap! (check-is-miner-when-requested-join miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission) + (asserts! (has-voted-join miner-to-vote) err-already-voted) + (map-set map-join-request-voter + {miner-to-vote: miner-to-vote, voter: tx-sender} + {value: true}) + (if (is-some (get value (map-get? map-votes-reject-join {address: miner-to-vote}))) + (map-set map-votes-reject-join {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-reject-join {address: miner-to-vote}))) u1)}) + (map-set map-votes-reject-join {address: miner-to-vote} {value: u1})) + (some + (if (is-vote-rejected-join (unwrap-panic (get value (map-get? map-votes-reject-join {address: miner-to-vote}))) (unwrap-panic (get-k-at-block-asked-to-join miner-to-vote)) (unwrap-panic (get-n-at-block-asked-to-join miner-to-vote))) + (reject-miner-in-pool miner-to-vote) + false)) + (ok true))) + +(define-public (quit-waiting-list) + (begin + (asserts! (check-is-waiting-now contract-caller) err-not-asked-to-join) + (asserts! + (<= + blocks-to-pass + (- block-height + (default-to block-height + (get value + (map-get? map-block-asked-to-join {address: contract-caller}))))) err-more-blocks-to-pass) + (let ((remove-result (unwrap-panic (remove-principal-waiting-list contract-caller)))) + (var-set miner-to-remove-votes-join contract-caller) + (var-set waiting-list remove-result) + (map-delete map-is-waiting {address: contract-caller}) + (ok (clear-votes-map-join-vote contract-caller))))) + +(define-private (accept-miner-in-pool (miner principal)) +(begin + (let ((pending-accept-result (as-max-len? (concat (var-get pending-accept-list) (list miner)) u300))) + (asserts! (is-some pending-accept-result) err-list-length-exceeded) ;; O(1) + (map-set map-warnings {address: miner} {value: u0}) + (map-set balance miner u0) + (var-set miner-to-remove-votes-join miner) + (var-set waiting-list (unwrap-panic (as-max-len? (unwrap-panic (remove-principal-waiting-list miner)) u300))) ;; O(N) + (map-delete map-is-waiting {address: miner}) + (map-set map-is-pending {address: miner} {value: true}) + (clear-votes-map-join-vote miner) + (ok (var-set pending-accept-list (unwrap-panic pending-accept-result)))))) + +(define-private (reject-miner-in-pool (miner principal)) +(begin + (let ((remove-result (unwrap-panic (remove-principal-waiting-list miner)))) + (var-set miner-to-remove-votes-join miner) + (var-set waiting-list remove-result) + (map-delete map-is-waiting {address: miner}) + (clear-votes-map-join-vote miner) + true))) + +(define-private (clear-votes-map-join-vote (miner principal)) +(begin + (map-delete map-votes-accept-join {address: (var-get miner-to-remove-votes-join)}) + (map-delete map-votes-reject-join {address: (var-get miner-to-remove-votes-join)}) + (map-delete map-block-asked-to-join {address: (var-get miner-to-remove-votes-join)}) + (map remove-map-record-join-vote (var-get miners-list)))) + +(define-private (remove-map-record-join-vote (miner principal)) +(if (is-some (map-get? map-join-request-voter {miner-to-vote: (var-get miner-to-remove-votes-join), voter: miner})) + (map-delete map-join-request-voter {miner-to-vote: (var-get miner-to-remove-votes-join), voter: miner}) + false)) + +(define-private (is-in-voters-list (miner principal) (voters-list (list 300 principal))) +(is-some (index-of? voters-list miner))) + +(define-private (has-voted-join (miner principal)) +(not (if (is-some (get value (map-get? map-join-request-voter {miner-to-vote: miner, voter: tx-sender}))) + (unwrap-panic (get value (map-get? map-join-request-voter {miner-to-vote: miner, voter: tx-sender}))) + false))) + +(define-public (try-enter-pool) +(begin + (asserts! (is-some (get value (map-get? map-votes-accept-join {address: tx-sender}))) err-not-asked-to-join) + (if (is-vote-accepted (unwrap-panic (get value (map-get? map-votes-accept-join {address: tx-sender}))) (unwrap-panic (get-k-at-block-asked-to-join tx-sender))) + (accept-miner-in-pool tx-sender) + (ok false)))) + +(define-public (add-pending-miners-to-pool) +(begin + (let ((len-pending-accept-list (len (var-get pending-accept-list)))) + (asserts! (not (is-eq len-pending-accept-list u0)) err-no-pending-miners) + (asserts! (x-blocks-passed blocks-to-pass) err-more-blocks-to-pass) + (map add-miner-to-pool (var-get pending-accept-list)) + (asserts! (is-some (as-max-len? (concat (var-get miners-list) (var-get pending-accept-list)) u300)) err-list-length-exceeded) + (var-set miners-list (unwrap-panic (as-max-len? (concat (var-get miners-list) (var-get pending-accept-list)) u300))) + (var-set n (+ (var-get n) len-pending-accept-list)) + (var-set pending-accept-list (list )) + (var-set last-join-done block-height) + (some (update-threshold)) + (ok true)))) + +(define-private (update-threshold) +(let ((n-now (var-get n))) + (if + (or + (is-eq n-now u1) + (is-eq n-now u2)) + (var-set k u1) + (var-set k (/ (* (var-get k-percentage) (- n-now u1)) u100))))) + +(define-private (add-miner-to-pool (miner principal)) +(begin + (map-delete map-is-pending {address: miner}) + (map-set map-is-miner {address: miner} {value: true}) + (map-set map-block-joined {address: miner} {block-height: block-height}) + (ok true))) + +(define-private (x-blocks-passed (x uint)) +(if (>= (- block-height (var-get last-join-done)) x) + true + false)) + +(define-private (get-k-at-block-asked-to-join (miner-to-vote principal)) +(let ((block-asked-to-join (get value (map-get? map-block-asked-to-join {address: miner-to-vote})))) + (begin + (asserts! (is-some block-asked-to-join) err-not-asked-to-join) + (if + (is-eq + (unwrap-panic block-asked-to-join) + block-height) + (ok (var-get k)) + (at-block + (unwrap-panic + (get-block-info? id-header-hash + (unwrap-panic block-asked-to-join))) + (ok (var-get k))))))) + +(define-private (get-n-at-block-asked-to-join (miner-to-vote principal)) +(let ((block-asked-to-join (get value (map-get? map-block-asked-to-join {address: miner-to-vote})))) + (begin + (asserts! (is-some block-asked-to-join) err-not-asked-to-join) + (if + (is-eq + (unwrap-panic block-asked-to-join) block-height) + (ok (var-get n)) + (at-block + (unwrap-panic + (get-block-info? id-header-hash + (unwrap-panic block-asked-to-join))) + (ok (var-get n))))))) + +;; LEAVING FLOW + +(define-public (leave-pool) +(begin + (asserts! (check-is-miner-now contract-caller) err-not-in-miner-map) + (asserts! (not (is-eq (var-get notifier) contract-caller)) err-currently-notifier) + (let ((remove-result (unwrap-panic (remove-principal-miners-list tx-sender))) + (new-k-percentage (if (> (var-get n) u2) (/ (* (var-get k) u100) (- (var-get n) u2)) u100))) + ;; if n<=2, set a value for new-k-percentage > k-critical to make sure threshold is updated + (some (var-set miners-list remove-result)) + (var-set n (- (var-get n) u1)) + (map-set map-is-miner {address: tx-sender} {value: false}) + (if + (is-some (index-of? (var-get proposed-removal-list) tx-sender)) + (var-set proposed-removal-list (unwrap-panic (remove-principal-proposed-removal-list tx-sender))) + true) + (if + (>= new-k-percentage (var-get k-critical)) + (if + (> (var-get n) u1) + (some (update-threshold)) + (if + (is-eq (var-get n) u1) + (some (var-set k u1)) + (some (var-set k u0)))) + none) + (ok true)))) + +;; REMOVING FLOW + +(define-public (propose-removal (miner-to-remove principal)) +(begin + (asserts! (not (is-eq (var-get n) u1)) err-cant-remove-when-alone-in-pool) + (asserts! (check-is-miner-now contract-caller) err-not-in-miner-map) + (asserts! (check-is-miner-now miner-to-remove) err-not-in-miner-map-miner-to-remove) + (asserts! (not (check-is-proposed-for-removal-now miner-to-remove)) err-already-proposed-for-removal) + (map-set map-block-proposed-to-remove {address: miner-to-remove} {value: block-height}) + (map-set map-is-proposed-for-removal {address: miner-to-remove} {value: true}) + (var-set proposed-removal-list (unwrap! (as-max-len? (concat (var-get proposed-removal-list) (list miner-to-remove )) u300) err-list-length-exceeded)) + (ok true))) + +(define-public (vote-positive-remove-request (miner-to-vote principal)) +(begin + (asserts! (not (is-eq contract-caller miner-to-vote)) err-cant-vote-himself) + (asserts! (check-is-proposed-for-removal-now miner-to-vote) err-not-proposed-for-removal) ;; map_is_proposed_for_removal + (asserts! (is-ok (get-k-at-block-proposed-removal miner-to-vote)) err-not-proposed-for-removal-proposal-block-missing) + (asserts! (unwrap! (check-is-miner-when-requested-remove miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission) + (asserts! (has-voted-remove miner-to-vote) err-already-voted) ;; O(1) + (map-set map-remove-request-voter + {miner-to-vote: miner-to-vote, voter: tx-sender} + {value: true}) + (if (is-some (get value (map-get? map-votes-accept-removal {address: miner-to-vote}))) + (map-set map-votes-accept-removal {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-accept-removal {address: miner-to-vote}))) u1)}) + (map-set map-votes-accept-removal {address: miner-to-vote} {value: u1})) + (some + (if (is-vote-accepted (unwrap-panic (get value (map-get? map-votes-accept-removal {address: miner-to-vote}))) (unwrap-panic (get-k-at-block-proposed-removal miner-to-vote))) + (process-removal miner-to-vote) + (ok false))) + (ok true))) + +(define-public (vote-negative-remove-request (miner-to-vote principal)) +(begin + (asserts! (not (is-eq contract-caller miner-to-vote)) err-cant-vote-himself) + (asserts! (check-is-proposed-for-removal-now miner-to-vote) err-not-proposed-for-removal) ;; map_is_waiting + (asserts! (is-ok (get-k-at-block-proposed-removal miner-to-vote)) err-not-proposed-for-removal-proposal-block-missing) + (asserts! (unwrap! (check-is-miner-when-requested-remove miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission) + (asserts! (has-voted-remove miner-to-vote) err-already-voted) ;; O(1) + (map-set map-remove-request-voter + {miner-to-vote: miner-to-vote, voter: tx-sender} + {value: true}) + (if (is-some (get value (map-get? map-votes-reject-removal {address: miner-to-vote}))) + (map-set map-votes-reject-removal {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-reject-removal {address: miner-to-vote}))) u1)}) + (map-set map-votes-reject-removal {address: miner-to-vote} {value: u1})) + (some + (if (is-vote-rejected-remove (unwrap-panic (get value (map-get? map-votes-reject-removal {address: miner-to-vote}))) (unwrap-panic (get-k-at-block-proposed-removal miner-to-vote)) (unwrap-panic (get-n-at-block-proposed-removal miner-to-vote))) + (reject-removal miner-to-vote) + (ok false))) + (ok true))) + +(define-public (quit-proposed-removal-list) + (begin + (asserts! + (<= + blocks-to-pass + (- block-height + (default-to block-height + (get value + (map-get? map-block-proposed-to-remove {address: contract-caller}))))) err-more-blocks-to-pass) + (let ((remove-result (unwrap-panic (remove-principal-proposed-removal-list contract-caller)))) + (var-set miner-to-remove-votes-remove contract-caller) + (var-set proposed-removal-list remove-result) + (map-delete map-is-proposed-for-removal {address: contract-caller}) + (ok (clear-votes-map-remove-vote contract-caller))))) + +(define-private (process-removal (miner principal)) +(begin + (let ((remove-result (unwrap-panic (remove-principal-miners-list miner))) + (new-k-percentage (if (> (var-get n) u2) (/ (* (var-get k) u100) (- (var-get n) u2)) u100))) + (some (var-set miners-list remove-result)) + (var-set miner-to-remove-votes-remove miner) + (var-set n (- (var-get n) u1)) + (map-delete map-is-miner {address: miner}) + (map-set map-blacklist {address: miner} {value: true}) + (var-set proposed-removal-list (unwrap-panic (remove-principal-proposed-removal-list miner))) + (clear-votes-map-remove-vote miner) + (if (>= new-k-percentage (var-get k-critical)) + (if + (> (var-get n) u1) + (update-threshold) + (if + (is-eq (var-get n) u1) + (var-set k u1) + (var-set k u0))) + false) + (ok true)))) + +(define-private (reject-removal (miner principal)) +(begin + (var-set miner-to-remove-votes-remove miner) + (var-set proposed-removal-list (unwrap-panic (remove-principal-proposed-removal-list miner))) + (clear-votes-map-remove-vote miner) + (ok true))) + +(define-private (has-voted-remove (miner principal)) +(not (if (is-some (get value (map-get? map-remove-request-voter {miner-to-vote: miner, voter: tx-sender}))) + (unwrap-panic (get value (map-get? map-remove-request-voter {miner-to-vote: miner, voter: tx-sender}))) + false + ))) + +(define-private (clear-votes-map-remove-vote (miner principal)) +(begin + (map-delete map-votes-accept-removal {address: (var-get miner-to-remove-votes-remove)}) + (map-delete map-votes-reject-removal {address: (var-get miner-to-remove-votes-remove)}) + (map-delete map-block-proposed-to-remove {address: (var-get miner-to-remove-votes-remove)}) + (map-delete map-is-proposed-for-removal {address: (var-get miner-to-remove-votes-remove)}) + (map remove-map-record-remove-vote (var-get miners-list)))) + +(define-private (remove-map-record-remove-vote (miner principal)) +(if (is-some (map-get? map-remove-request-voter {miner-to-vote: (var-get miner-to-remove-votes-remove), voter: miner})) + (map-delete map-remove-request-voter {miner-to-vote: (var-get miner-to-remove-votes-remove), voter: miner}) + false)) + +(define-private (get-k-at-block-proposed-removal (miner-to-vote principal)) +(let ((block-proposed-to-remove (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})))) + (begin + (asserts! (is-some block-proposed-to-remove) err-not-proposed-for-removal) + (if + (is-eq (unwrap-panic + block-proposed-to-remove) + block-height) + (ok (var-get k)) + (at-block + (unwrap-panic + (get-block-info? id-header-hash + (unwrap-panic block-proposed-to-remove))) + (ok (var-get k))))))) + +(define-private (get-n-at-block-proposed-removal (miner-to-vote principal)) +(let ((block-proposed-to-remove (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})))) + (begin + (asserts! (is-some block-proposed-to-remove) err-not-proposed-for-removal) + (if + (is-eq (unwrap-panic + block-proposed-to-remove) + block-height) + (ok (var-get n)) + (at-block + (unwrap-panic + (get-block-info? id-header-hash + (unwrap-panic block-proposed-to-remove))) + (ok (var-get n))))))) + +;; UPDATE NOTIFIER + +(define-public (start-vote-notifier) +(begin + (asserts! (not (var-get notifier-vote-active)) err-vote-started-already) + (var-set notifier-vote-start-block block-height) + (var-set notifier-vote-end-block (+ (var-get notifier-vote-start-block) notifier-election-blocks-to-pass)) + (var-set notifier-vote-active true) + (if (var-get notifier-previous-entries-removed) + (begin + (ok (var-set notifier-previous-entries-removed false))) + (end-vote-notifier-private)))) + +(define-public (end-vote-notifier) +(begin + (asserts! (>= block-height (var-get notifier-vote-end-block)) err-voting-still-active) + (var-set notifier-vote-active false) + (end-vote-notifier-private))) + +(define-private (end-vote-notifier-private) +(begin + (unwrap! (get-max-votes-number-notifier) (err u99999)) + (if + (> + (var-get max-votes-notifier) + (/ (var-get k) u2)) + (var-set notifier (var-get max-voted-proposed-notifier)) + false) + (delete-all-notifier-entries) + (ok true))) + +(define-private (get-max-votes-number-notifier) +(ok (map compare-votes-number-notifier (var-get miners-list)))) + +(define-private (compare-votes-number-notifier (proposed-notifier principal)) +(ok +(if (is-some (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier}))) + (if (> (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier}))) (/ (var-get k) u2)) + (if + (> (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier}))) (var-get max-votes-notifier)) + (begin + (var-set max-votes-notifier (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier})))) + (var-set max-voted-proposed-notifier proposed-notifier)) + (if + (is-eq (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier}))) (var-get max-votes-notifier)) + (if + (< + (unwrap-panic (get block-height (map-get? map-block-joined {address: proposed-notifier}))) + (unwrap-panic (get block-height (map-get? map-block-joined {address: (var-get max-voted-proposed-notifier)})))) + (begin + (var-set max-voted-proposed-notifier proposed-notifier)) + false) + false)) + false) + false))) + +(define-private (delete-all-notifier-entries) +(begin + (var-set max-votes-notifier u0) + (var-set max-voted-proposed-notifier (var-get notifier)) + (map delete-one-notifier-entry (var-get miners-list)) + (var-set notifier-previous-entries-removed true))) + +(define-private (delete-one-notifier-entry (miner principal)) +(begin + (map-delete map-voted-update-notifier {miner-who-voted: miner}) + (map-delete map-votes-notifier {voted-notifier: miner}))) + +(define-public (vote-notifier (voted-notifier principal)) +(begin + (asserts! (and (is-some (get value (map-get? map-is-miner {address: voted-notifier}))) (unwrap-panic (get value (map-get? map-is-miner {address: voted-notifier})))) err-not-in-miner-map) + (asserts! (and (is-some (get value (map-get? map-is-miner {address: contract-caller}))) (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller})))) err-no-vote-permission) + (asserts! (var-get notifier-vote-active) err-not-voting-period) + (asserts! (not (is-eq contract-caller voted-notifier)) err-cant-vote-himself) + (asserts! (< block-height (var-get notifier-vote-end-block)) err-not-voting-period) + (asserts! (is-none (get miner-voted (map-get? map-voted-update-notifier {miner-who-voted: tx-sender}))) err-already-voted) + (map-set map-voted-update-notifier {miner-who-voted: tx-sender} {miner-voted: voted-notifier}) + (if (is-none (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) + (map-set map-votes-notifier {voted-notifier: voted-notifier} {votes-number: u1}) + (map-set map-votes-notifier {voted-notifier: voted-notifier} {votes-number: (+ (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) u1)})) + (try! + (if (is-vote-accepted (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) (var-get k)) + (begin + (var-set notifier voted-notifier) + (var-set notifier-vote-end-block block-height) + (var-set notifier-vote-active false) + (end-vote-notifier)) + (ok false))) +(ok true))) + +;; WARNING FLOW + +(define-public (warn-miner (miner principal)) +(begin +(let ((incremented-value + (if + (is-some (get value (map-get? map-warnings {address: miner}))) + (+ (unwrap-panic (get value (map-get? map-warnings {address: miner}))) u1) + u1))) + (asserts! (is-eq contract-caller (var-get notifier)) err-only-notifier) + (asserts! + (not (and + (is-none (at-block (unwrap! (get-block-info? id-header-hash (- block-height u1)) err-cant-unwrap-block-info) (get value (map-get? map-warnings {address: miner})))) + (>= incremented-value u2))) + err-one-warning-per-block) + (asserts! + (not + (>= + (- + incremented-value + (unwrap! (at-block (unwrap! (get-block-info? id-header-hash (- block-height u1)) err-cant-unwrap-block-info) (get value (map-get? map-warnings {address: miner}))) err-cant-unwrap-block-info)) + u2)) + err-one-warning-per-block) + (ok + (if + (is-some (get value (map-get? map-warnings {address: miner}))) + (map-set map-warnings {address: miner} {value: (+ (unwrap-panic (get value (map-get? map-warnings {address: miner}))) u1)}) + (map-set map-warnings {address: miner} {value: u1})))))) + +;; ELECTION FUNCTIONS + +(define-private (is-vote-accepted (votes-number uint) (k-local uint)) +(if + (is-eq k-local u0) ;; k is 0 for n=1, n=2 + (>= votes-number u1) + (>= votes-number k-local))) + +(define-private (is-democratic-vote-accepted-notifier (votes-number uint) (k-local uint)) +(if + (is-eq k-local u0) ;; k is 0 for n=1, n=2 + (>= votes-number u1) + (>= votes-number (/ k-local u2)))) + +(define-private (is-vote-rejected-join (votes-number uint) (k-local uint) (n-local uint)) +(if + (is-eq n-local u1) + (>= votes-number u1) + (if (is-eq n-local u2) + (>= votes-number u2) + (>= votes-number (+ (- n-local k-local) u1))))) + + +(define-private (is-vote-rejected-remove (votes-number uint) (k-local uint) (n-local uint)) +(if + (is-eq n-local u2) + (>= votes-number u1) + (if (is-eq n-local u3) + (>= votes-number u2) + (>= votes-number (+ (- n-local k-local) u1))))) + +(define-private (is-vote-rejected-notifier (votes-number uint) (k-local uint) (n-local uint)) +(if (is-eq n-local u2) + (>= votes-number u1) + (if (is-eq n-local u3) + (>= votes-number u2) + (>= votes-number (+ (- n-local k-local) u1))))) + +;; RECOVERING UNASSIGNED FUNDS FUNCTIONS + +;; A public function each miner can call in order to recover the +;; undistributed rewards caused by dividing rewards to stackers +(define-public (flush-change) +(let ((total-change-funds (var-get reward-change-funds)) + (current-miners-list-len (len (var-get miners-list))) + (distributed-change-funds (/ total-change-funds current-miners-list-len)) + (change-after-flushing (mod total-change-funds current-miners-list-len))) + (asserts! (check-is-miner-now contract-caller) err-not-in-miner-map) + (var-set temp-distributed-change-funds distributed-change-funds) + (var-set temp-change-after-flushing change-after-flushing) + (map flush-distribution-one-miner (var-get miners-list)) + (ok + (var-set reward-change-funds + (- + total-change-funds + (* + current-miners-list-len + distributed-change-funds)))))) + +(define-private (flush-distribution-one-miner (miner principal)) +(let ((miner-balance (default-to u0 (map-get? balance miner))) + (total-change-funds (var-get reward-change-funds)) + (current-miners-list-len (len (var-get miners-list))) + (distributed-change-funds (/ total-change-funds current-miners-list-len))) + (map-set balance miner + (+ + miner-balance + (var-get temp-distributed-change-funds))))) + +;; LIST PROCESSING FUNCTIONS + +(define-private (remove-principal-waiting-list (miner principal)) +(begin + (var-set waiting-list-miner-to-remove miner) + (ok (filter is-principal-in-waiting-list (var-get waiting-list))))) + +(define-private (remove-principal-pending-accept-list (miner principal)) +(begin + (var-set waiting-list-miner-to-remove miner) + (ok (filter is-principal-in-pending-accept-list (var-get pending-accept-list))))) + +(define-private (remove-principal-miners-list (miner principal)) +(begin + (var-set miners-list-miner-to-remove miner) + (ok (filter is-principal-in-miners-list (var-get miners-list))))) + +(define-private (remove-principal-proposed-removal-list (miner principal)) +(begin + (var-set proposed-removal-list-miner-to-remove miner) + (ok (filter is-principal-in-proposed-removal-list (var-get proposed-removal-list))))) + +;; MINER STATUS FUNCTIONS + +(define-private (check-is-miner-when-requested-join (miner-to-vote principal)) +(ok + (if + (is-some + (if + (is-eq + (unwrap! (get value (map-get? map-block-asked-to-join {address: miner-to-vote})) err-cant-unwrap-asked-to-join) + block-height) + (get value (map-get? map-is-miner {address: contract-caller})) + (at-block + (unwrap! + (get-block-info? id-header-hash + (unwrap! (get value (map-get? map-block-asked-to-join {address: miner-to-vote})) err-cant-unwrap-asked-to-join)) + err-cant-unwrap-block-info) + (get value (map-get? map-is-miner {address: contract-caller}))))) + (if + (is-eq + (unwrap! + (get value (map-get? map-block-asked-to-join {address: miner-to-vote})) + err-cant-unwrap-asked-to-join) + block-height) + (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller}))) + (at-block + (unwrap! + (get-block-info? id-header-hash + (unwrap-panic (get value (map-get? map-block-asked-to-join {address: miner-to-vote})))) + err-cant-unwrap-block-info) + (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller}))))) + false))) + +(define-private (check-is-miner-when-requested-remove (miner-to-vote principal)) +(ok + (if + (is-some + (if + (is-eq + (unwrap! (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})) err-cant-unwrap-asked-to-join) + block-height) + (get value (map-get? map-is-miner {address: contract-caller})) + (at-block + (unwrap! + (get-block-info? id-header-hash + (unwrap! (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})) err-cant-unwrap-asked-to-join)) + err-cant-unwrap-block-info) + (get value (map-get? map-is-miner {address: contract-caller}))))) + (if + (is-eq + (unwrap! + (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})) + err-cant-unwrap-asked-to-join) + block-height) + (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller}))) + (at-block + (unwrap! + (get-block-info? id-header-hash + (unwrap-panic (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})))) + err-cant-unwrap-block-info) + (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller}))))) + false))) + +(define-read-only (check-is-miner-when-requested-join-tool-fn (miner-to-vote-address principal)) +(get value (map-get? map-is-miner {address: contract-caller}))) + +(define-private (check-is-miner-now (miner principal)) +(if (is-some (get value (map-get? map-is-miner {address: miner}))) + (unwrap-panic (get value (map-get? map-is-miner {address: miner}))) + false)) + +(define-private (check-is-proposed-for-removal-now (miner principal)) +(if (is-some (get value (map-get? map-is-proposed-for-removal {address: miner}))) + (unwrap-panic (get value (map-get? map-is-proposed-for-removal {address: miner}))) + false)) + +(define-private (check-is-waiting-now (miner principal)) +(if (is-some (get value (map-get? map-is-waiting {address: miner}))) + (unwrap-panic (get value (map-get? map-is-waiting {address: miner}))) + false)) + +(define-private (check-is-pending-now (miner principal)) +(if (is-some (get value (map-get? map-is-pending {address: miner}))) + (unwrap-panic (get value (map-get? map-is-pending {address: miner}))) + false) +) + +(define-private (get-reward-at-block (block-number uint)) +(begin + {reward: (get-block-info? block-reward block-number), + claimer: (get-block-info? miner-address block-number)})) + +(define-read-only (get-reward-at-block-read (block-number uint)) +(begin + {reward: (get-block-info? block-reward block-number), + claimer: (get-block-info? miner-address block-number) + })) + +(define-read-only (get-address-status (address principal)) +(if (check-is-miner-now address) + (ok "is-miner") + (if (check-is-waiting-now address) + (ok "is-waiting") + (if (check-is-pending-now address) + (ok "is-pending") + (ok "is-none") + ) + ) +)) + +;; READ-ONLY UTILS + +;; (define-read-only (check-vote-accepted) ;; to check the vote status inside FE +;; (is-vote-accepted (unwrap-panic (get value (map-get? map-votes-accept-join {address: tx-sender}))))) + +(define-read-only (get-k) +(var-get k)) + +(define-read-only (get-notifier) +(var-get notifier)) + +(define-read-only (get-blocks-won) +(var-get blocks-won)) + +(define-read-only (get-total-rewards-distributed) +(var-get total-rewarded)) + +(define-read-only (get-waiting-list) +(var-get waiting-list)) + +(define-read-only (get-miners-list) +(var-get miners-list)) + +(define-read-only (get-pending-accept-list) +(var-get pending-accept-list )) + +(define-read-only (get-proposed-removal-list) +(var-get proposed-removal-list )) + +(define-read-only (get-notifier-vote-status) +(var-get notifier-vote-active)) + +(define-read-only (get-notifier-vote-number (voted-notifier principal)) +(get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) + +(define-read-only (get-max-voted-notifier) +(var-get max-voted-proposed-notifier)) + +(define-read-only (get-max-votes-notifier) +(var-get max-votes-notifier)) + +(define-read-only (get-current-block) +(ok block-height)) + +(define-private (is-principal-in-waiting-list (miner principal)) +(not (is-eq + (var-get waiting-list-miner-to-remove) + miner))) + +(define-private (is-principal-in-pending-accept-list (miner principal)) +(not (is-eq + (var-get pending-accept-list-miner-to-remove) + miner))) + +(define-private (is-principal-in-miners-list (miner principal)) +(not (is-eq + (var-get miners-list-miner-to-remove) + miner))) + +(define-private (is-principal-in-proposed-removal-list (miner principal)) +(not (is-eq + (var-get proposed-removal-list-miner-to-remove) + miner))) \ No newline at end of file diff --git a/sbtc-ops/smart-contract/contracts/mining-pool.clar b/sbtc-ops/smart-contract/contracts/mining-pool.clar new file mode 100644 index 00000000..8df9cab4 --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/mining-pool.clar @@ -0,0 +1,1285 @@ +(define-constant err-invalid (err u300)) +(define-constant err-list-length-exceeded (err u101)) +(define-constant err-already-asked-to-join (err u102)) +(define-constant err-already-joined (err u103)) +(define-constant err-not-in-miner-map (err u104)) +(define-constant err-no-vote-permission (err u105)) +(define-constant err-more-blocks-to-pass (err u106)) +(define-constant err-no-pending-miners (err u107)) +(define-constant err-already-voted (err u108)) +(define-constant err-not-asked-to-join (err u109)) +(define-constant err-cant-unwrap-check-miner (err u110)) +(define-constant err-cant-unwrap-asked-to-join (err u111)) +(define-constant err-cant-unwrap-block-info (err u112)) +(define-constant err-currently-notifier (err u113)) +(define-constant err-not-in-miner-map-miner-to-remove (err u114)) +(define-constant err-already-proposed-for-removal (err u116)) +(define-constant err-not-proposed-for-removal (err u117)) +(define-constant err-cant-remove-when-alone-in-pool (err u118)) +(define-constant err-cant-vote-himself (err u119)) +(define-constant err-cant-change-notifier (err u120)) +(define-constant err-already-proposed-for-notifier (err u121)) +(define-constant err-not-proposed-for-removal-proposal-block-missing (err u122)) +(define-constant err-not-proposed-for-notifier-k-missing (err u123)) +(define-constant err-not-proposed-notifier (err u124)) +(define-constant err-already-notifier (err u125)) +(define-constant err-not-in-miner-map-proposed-notifier (err u126)) +(define-constant err-vote-started-already (err u127)) +(define-constant err-voting-still-active (err u128)) +(define-constant err-not-voting-period (err u129)) +(define-constant err-not-waiting (err u130)) +(define-constant err-not-pending (err u131)) +(define-constant err-no-join-block-data (err u132)) +(define-constant err-not-voted (err u133)) +(define-constant err-only-notifier (err u134)) +(define-constant err-one-warning-per-block (err u135)) +(define-constant err-block-height-invalid (err u136)) +(define-constant err-unwrap-miner-index (err u999)) +(define-constant err-insufficient-balance (err u1001)) +(define-constant err-missing-balance (err u1002)) +(define-constant err-already-distributed (err u1003)) +(define-constant err-cant-unwrap-rewarded-block (err u1004)) + +(define-constant notifier-election-blocks-to-pass u144) +(define-constant blocks-to-pass u100) +(define-constant k-percentage u67) +(define-constant k-critical u75) + +(define-map balance principal uint) +(define-map claimed-rewards { block-number: uint } { claimed: bool }) +(define-map map-is-miner { address: principal } { value: bool }) +(define-map map-is-waiting { address: principal } { value: bool }) +(define-map map-is-pending { address: principal } { value: bool }) +(define-map map-is-proposed-for-removal { address: principal } { value: bool }) +(define-map map-block-asked-to-join { address: principal } { value: uint }) +(define-map map-block-proposed-to-remove { address: principal } { value: uint }) +(define-map map-block-joined { address: principal } { block-height: uint }) +(define-map map-balance-xBTC { address: principal } { value: uint }) +(define-map auto-exchange { address: principal } { value: bool }) +(define-map btc-address { address: principal } { btc-address: {hashbytes: (buff 32), version: (buff 1)} }) + +(define-map map-votes-accept-join { address: principal } { value: uint }) +(define-map map-votes-reject-join { address: principal } { value: uint }) +(define-map map-votes-accept-removal { address: principal } { value: uint }) +(define-map map-votes-reject-removal { address: principal } { value: uint }) +(define-map map-join-request-voter { miner-to-vote: principal, voter: principal } { value: bool }) +(define-map map-remove-request-voter { miner-to-vote: principal, voter: principal } { value: bool }) +(define-map map-voted-update-notifier { miner-who-voted: principal } { miner-voted: principal }) +(define-map map-votes-notifier { voted-notifier: principal } { votes-number: uint }) +(define-map map-blacklist { address: principal } { value: bool }) +(define-map map-total-withdraw { address: principal } { value: uint }) +(define-map map-warnings { address: principal } { value: uint }) + +(define-data-var miners-list-len-at-reward-block uint u0) +(define-data-var notifier principal tx-sender) +(define-data-var waiting-list (list 300 principal) (list )) +(define-data-var miners-list (list 300 principal) (list (var-get notifier))) +(define-data-var miners-list-bitcoin (list 300 {hashbytes: (buff 32), version: (buff 1)}) (list )) +(define-data-var pending-accept-list (list 300 principal) (list )) +(define-data-var proposed-removal-list (list 300 principal) (list )) +(define-data-var n uint u1) +(define-data-var k uint u1) +(define-data-var waiting-list-miner-to-remove principal tx-sender) ;; use in remove-principal-miners-list +(define-data-var pending-accept-list-miner-to-remove principal tx-sender) +(define-data-var miners-list-miner-to-remove principal tx-sender) +(define-data-var proposed-removal-list-miner-to-remove principal tx-sender) +(define-data-var last-join-done uint u1) +(define-data-var miner-to-remove-votes-join principal tx-sender) +(define-data-var miner-to-remove-votes-remove principal tx-sender) +(define-data-var notifier-previous-entries-removed bool true) +(define-data-var notifier-vote-active bool false) +(define-data-var notifier-vote-start-block uint u0) +(define-data-var notifier-vote-end-block uint u0) +(define-data-var max-votes-notifier uint u0) +(define-data-var max-voted-proposed-notifier principal tx-sender) +(define-data-var reward uint u0) +(define-data-var total-rewarded uint u0) +(define-data-var blocks-won uint u0) +(define-data-var reward-change-funds uint u0) +(define-data-var temp-distributed-change-funds uint u0) +(define-data-var temp-change-after-flushing uint u0) +(define-data-var temp-miners-bitcoin-lists (list 300 {hashbytes: (buff 32), version: (buff 1)}) (list )) + +(map-set map-is-miner {address: tx-sender} {value: true}) +(map-set map-block-joined {address: tx-sender} {block-height: block-height}) +(map-set balance tx-sender u0) +;; at new join -> block height - last-join-done >= 100 ! + +;; READ ONLY FE UTILS + +;; waiting miners + +(define-read-only (get-all-data-waiting-miners (waiting-miners-list (list 100 principal))) +(map get-all-data-waiting-miner waiting-miners-list)) + +(define-private (get-all-data-waiting-miner (miner principal)) +(let ((k-at-block-asked-to-join (unwrap-panic (get-k-at-block-asked-to-join miner))) + (n-at-block-asked-to-join (unwrap-panic (get-n-at-block-asked-to-join miner)))) + (begin + (asserts! (is-some (get value (map-get? map-is-waiting {address: miner}))) err-not-waiting) + (ok + { + pos-votes: + (default-to u0 (get value (map-get? map-votes-accept-join {address: miner}))), + pos-thr: + (if + (is-eq k-at-block-asked-to-join u0) + u1 + k-at-block-asked-to-join), + neg-votes: + (default-to u0 (get value (map-get? map-votes-reject-join {address: miner}))), + neg-thr: + (if + (is-eq n-at-block-asked-to-join u1) + u1 + (if + (is-eq n-at-block-asked-to-join u2) + u2 + (+ (- n-at-block-asked-to-join k-at-block-asked-to-join) u1)))})))) + +;; miners proposed for removal + +(define-read-only (get-all-data-miners-proposed-for-removal (removal-miners-list (list 100 principal))) +(map get-all-data-miner-proposed-for-removal removal-miners-list)) + +(define-private (get-all-data-miner-proposed-for-removal (miner principal)) +(let ((k-at-block-proposed-removal (unwrap-panic (get-k-at-block-proposed-removal miner))) + (n-at-block-proposed-removal (unwrap-panic (get-n-at-block-proposed-removal miner)))) + (begin + (asserts! (is-some (get value (map-get? map-is-proposed-for-removal {address: miner}))) err-not-pending) + (ok + { + vts-for: + (default-to u0 (get value (map-get? map-votes-accept-removal {address: miner}))), + pos-thr: + (if + (is-eq k-at-block-proposed-removal u0) + u1 + k-at-block-proposed-removal), + vts-against: + (default-to u0 (get value (map-get? map-votes-reject-removal {address: miner}))), + neg-thr: + (if + (is-eq n-at-block-proposed-removal u2) + u1 + (if (is-eq n-at-block-proposed-removal u3) + u2 + (+ (- n-at-block-proposed-removal k-at-block-proposed-removal) u1)))})))) + +;; pending accept miners + +(define-read-only (get-all-data-miners-pending-accept (pending-miners-list (list 100 principal))) +(map get-data-miner-pending-accept pending-miners-list)) + +(define-private (get-data-miner-pending-accept (miner principal)) +(begin + (asserts! (is-some (get value (map-get? map-is-pending {address: miner}))) err-not-pending) + (ok + { + miner: miner, + remaining-blocks-until-join: (get-remaining-blocks-until-join) + }))) + +(define-read-only (get-remaining-blocks-until-join) + (if (> blocks-to-pass (- block-height (var-get last-join-done))) + (- blocks-to-pass (- block-height (var-get last-join-done))) + u0 + ) +) + +;; blocks number as miner +(define-read-only (get-all-data-miners-blocks (local-miners-list (list 100 principal))) +(map get-data-miner-blocks local-miners-list)) +(define-private (get-data-miner-blocks (miner principal)) +(begin + (asserts! (is-some (get value (map-get? map-is-miner {address: miner}))) err-not-in-miner-map) + (asserts! (is-some (get block-height (map-get? map-block-joined {address: miner}))) err-no-join-block-data) + (ok + { + miner: miner, + blocks-as-miner: (- block-height (unwrap-panic (get block-height (map-get? map-block-joined {address: miner})))) + }))) + +;; miners in pool + +(define-read-only (get-all-data-miners-in-pool (local-miners-list (list 100 principal))) +(map get-data-miner-in-pool local-miners-list)) + +(define-private (get-data-miner-in-pool (miner principal)) +(begin + (asserts! (is-some (get value (map-get? map-is-miner {address: miner}))) err-not-in-miner-map) + (asserts! (is-some (get block-height (map-get? map-block-joined {address: miner}))) err-no-join-block-data) + (ok + { + blocks-as-miner: (- block-height (unwrap-panic (get block-height (map-get? map-block-joined {address: miner})))), + was-blacklist: (default-to false (get value (map-get? map-blacklist {address: miner}))), + warnings: (default-to u0 (get value (map-get? map-warnings {address: miner}))), + }))) + +;; total withdrawals + +(define-read-only (get-all-data-total-withdrawals (local-miners-list (list 100 principal))) +(map get-data-miner-withdrawals local-miners-list)) + +(define-private (get-data-miner-withdrawals (miner principal)) +(ok + (default-to u0 (get value (map-get? map-total-withdraw {address: miner}))) + )) + +;; notifier + +(define-read-only (get-data-notifier-election-process) +{ + vote-status: (var-get notifier-vote-active), + election-blocks-remaining: + (if (<= (var-get notifier-vote-end-block) block-height) + u0 + (- (var-get notifier-vote-end-block) block-height))}) + +(define-read-only (get-all-data-notifier-voter-miners (voter-miners-list (list 100 principal))) +(map get-data-notifier-voter-miner voter-miners-list)) + +(define-private (get-data-notifier-voter-miner (miner principal)) +(begin + (asserts! (is-some (get miner-voted (map-get? map-voted-update-notifier {miner-who-voted: miner}))) err-not-voted) + (ok + { + miner: miner, + voted-notifier: + (unwrap-panic (get miner-voted (map-get? map-voted-update-notifier {miner-who-voted: miner}))) + }))) + +;; balances + +(define-read-only (was-block-claimed (given-block-height uint)) + (if + (is-none (get claimed (map-get? claimed-rewards {block-number: given-block-height}))) + false + (if + (unwrap-panic (get claimed (map-get? claimed-rewards {block-number: given-block-height}))) + true + false))) + +;; BALANCES FLOW + +;; read balance +(define-read-only (get-balance (address principal)) +(map-get? balance address)) + +(define-read-only (get-miner-btc-address (miner-address principal)) + (map-get? btc-address {address: miner-address})) + +(define-public (set-my-btc-address (new-btc-address {hashbytes: (buff 32), version: (buff 1)})) + (ok (map-set btc-address {address: tx-sender} {btc-address: new-btc-address}))) + +;; deposit funds +(define-public (deposit-stx (amount uint)) +(let ((sender tx-sender) + (balance-sender (map-get? balance sender))) + (try! (stx-transfer? amount tx-sender (as-contract tx-sender))) + (if (is-none balance-sender) + (ok (map-set balance sender amount)) + (ok (map-set balance sender (+ (unwrap! balance-sender err-missing-balance) amount)))))) + +;; withdraw funds +(define-public (withdraw-stx (amount uint)) +(let ((receiver tx-sender)) + (asserts! (>= (unwrap! (map-get? balance receiver) err-missing-balance) amount) err-insufficient-balance) + (try! (as-contract (stx-transfer? amount tx-sender receiver))) + (if + (is-some (get value (map-get? map-total-withdraw {address: receiver}))) + (map-set map-total-withdraw {address: receiver} {value: (+ (unwrap-panic (get value (map-get? map-total-withdraw {address: receiver}))) amount)}) + (map-set map-total-withdraw {address: receiver} {value: amount})) + (ok (map-set balance receiver (- (unwrap! (map-get? balance receiver) err-missing-balance) amount))))) + +;; exchange funds +(define-public (set-auto-exchange (new-value bool)) + (ok (map-set auto-exchange {address: tx-sender} {value: new-value}))) + +(define-read-only (get-auto-exchange (address principal)) + (map-get? auto-exchange {address: address})) + +(define-public (reward-distribution (block-number uint)) +(begin + (asserts! (< block-number block-height) err-block-height-invalid) ;; +100 ? + (asserts! (is-none (get claimed (map-get? claimed-rewards {block-number: block-number}))) err-already-distributed) + (let ((miners-list-at-reward-block + (at-block (unwrap! (get-block-info? id-header-hash block-number) err-cant-unwrap-rewarded-block) (var-get miners-list))) + (block-reward (get-reward-at-block-read block-number)) + (current-reward (var-get reward)) + (current-miners-list-len (len miners-list-at-reward-block)) + (distribution-change-funds (mod current-reward current-miners-list-len)) + (current-change-funds (var-get reward-change-funds))) + (map-set claimed-rewards {block-number: block-number} {claimed: true}) + (var-set miners-list-len-at-reward-block current-miners-list-len) + (var-set reward (unwrap-panic (get reward block-reward))) + (var-set total-rewarded (+ (var-get total-rewarded) (var-get reward))) + (var-set blocks-won (+ (var-get blocks-won) u1)) + (var-set reward-change-funds (+ current-change-funds distribution-change-funds)) + (map distribute-reward-each-miner miners-list-at-reward-block) + (ok true)))) + +(define-private (distribute-reward-each-miner (miner principal)) +(let ((miner-balance (default-to u0 (map-get? balance miner))) + (current-reward (var-get reward)) + (current-change-funds (var-get reward-change-funds)) + (current-miners-list-len (var-get miners-list-len-at-reward-block)) + (distributed-amount (/ current-reward current-miners-list-len)) + ) + + (map-set balance miner + (+ miner-balance distributed-amount)))) + +(define-private (flush-change-each-miner (miner principal)) +(let ((miner-balance (default-to u0 (map-get? balance miner))) + (current-reward (var-get reward)) + (current-change-funds (var-get reward-change-funds)) + (current-miners-list-len (var-get miners-list-len-at-reward-block)) + (distributed-amount (/ current-reward current-miners-list-len)) + (distribution-change-funds (mod current-reward current-miners-list-len))) + (var-set reward-change-funds (+ current-change-funds distribution-change-funds)) + (map-set balance miner + (+ miner-balance distributed-amount)))) + +;; JOINING FLOW + +(define-public (ask-to-join (my-btc-address {hashbytes: (buff 32), version: (buff 1)})) +(begin + (asserts! (not (check-is-miner-now contract-caller)) err-already-joined) + (asserts! (not (check-is-waiting-now contract-caller)) err-already-asked-to-join) + (map-set map-block-asked-to-join {address: tx-sender} {value: block-height}) + (map-set btc-address {address: tx-sender} {btc-address: my-btc-address}) + (var-set waiting-list (unwrap-panic (as-max-len? (append (var-get waiting-list) tx-sender) u300))) + (map-set map-is-waiting {address: tx-sender} {value: true}) + (ok true))) + +(define-public (vote-positive-join-request (miner-to-vote principal)) +(begin + (asserts! (check-is-waiting-now miner-to-vote) err-not-asked-to-join) ;; map_is_waiting + (asserts! (unwrap! (check-is-miner-when-requested-join miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission) + (asserts! (not (has-voted-join miner-to-vote)) err-already-voted) ;; O(1) + (map-set map-join-request-voter + {miner-to-vote: miner-to-vote, voter: tx-sender} + {value: true}) + (if (is-some (get value (map-get? map-votes-accept-join {address: miner-to-vote}))) + (map-set map-votes-accept-join {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-accept-join {address: miner-to-vote}))) u1)}) + (map-set map-votes-accept-join {address: miner-to-vote} {value: u1})) + (ok true))) + +(define-public (vote-negative-join-request (miner-to-vote principal)) +(begin + (asserts! (check-is-waiting-now miner-to-vote) err-not-asked-to-join) + (asserts! (unwrap! (check-is-miner-when-requested-join miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission) + (asserts! (not (has-voted-join miner-to-vote)) err-already-voted) + (map-set map-join-request-voter + {miner-to-vote: miner-to-vote, voter: tx-sender} + {value: true}) + (if (is-some (get value (map-get? map-votes-reject-join {address: miner-to-vote}))) + (map-set map-votes-reject-join {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-reject-join {address: miner-to-vote}))) u1)}) + (map-set map-votes-reject-join {address: miner-to-vote} {value: u1})) + (some + (if (is-vote-rejected-join (unwrap-panic (get value (map-get? map-votes-reject-join {address: miner-to-vote}))) (unwrap-panic (get-k-at-block-asked-to-join miner-to-vote)) (unwrap-panic (get-n-at-block-asked-to-join miner-to-vote))) + (reject-miner-in-pool miner-to-vote) + false)) + (ok true))) + +(define-public (quit-waiting-list) + (begin + (asserts! (check-is-waiting-now contract-caller) err-not-asked-to-join) + (asserts! + (<= + blocks-to-pass + (- block-height + (default-to block-height + (get value + (map-get? map-block-asked-to-join {address: contract-caller}))))) err-more-blocks-to-pass) + (let ((remove-result (unwrap-panic (remove-principal-waiting-list contract-caller)))) + (var-set miner-to-remove-votes-join contract-caller) + (var-set waiting-list remove-result) + (map-delete map-is-waiting {address: contract-caller}) + (ok (clear-votes-map-join-vote contract-caller))))) + +(define-private (accept-miner-in-pool (miner principal)) +(let ((pending-accept-result (as-max-len? (append (var-get pending-accept-list) miner) u300))) + (asserts! (is-some pending-accept-result) err-list-length-exceeded) ;; O(1) + (map-set map-warnings {address: miner} {value: u0}) + (map-set balance miner u0) + (var-set miner-to-remove-votes-join miner) + (var-set waiting-list (unwrap-panic (as-max-len? (unwrap-panic (remove-principal-waiting-list miner)) u300))) ;; O(N) + (map-delete map-is-waiting {address: miner}) + (map-set map-is-pending {address: miner} {value: true}) + (clear-votes-map-join-vote miner) + (ok (var-set pending-accept-list (unwrap-panic pending-accept-result))))) + +(define-private (reject-miner-in-pool (miner principal)) +(let ((remove-result (unwrap-panic (remove-principal-waiting-list miner)))) + (var-set miner-to-remove-votes-join miner) + (var-set waiting-list remove-result) + (map-delete map-is-waiting {address: miner}) + (clear-votes-map-join-vote miner) + true)) + +(define-private (clear-votes-map-join-vote (miner principal)) +(begin + (map-delete map-votes-accept-join {address: (var-get miner-to-remove-votes-join)}) + (map-delete map-votes-reject-join {address: (var-get miner-to-remove-votes-join)}) + (map-delete map-block-asked-to-join {address: (var-get miner-to-remove-votes-join)}) + (map remove-map-record-join-vote (var-get miners-list)))) + +(define-private (remove-map-record-join-vote (miner principal)) +(map-delete map-join-request-voter {miner-to-vote: (var-get miner-to-remove-votes-join), voter: miner})) + +(define-private (is-in-voters-list (miner principal) (voters-list (list 300 principal))) +(is-some (index-of? voters-list miner))) + +(define-private (has-voted-join (miner principal)) +(if (is-some (get value (map-get? map-join-request-voter {miner-to-vote: miner, voter: tx-sender}))) + (unwrap-panic (get value (map-get? map-join-request-voter {miner-to-vote: miner, voter: tx-sender}))) + false)) + +(define-public (try-enter-pool) +(begin + (asserts! (is-some (get value (map-get? map-votes-accept-join {address: tx-sender}))) err-not-asked-to-join) + (if (is-vote-accepted (unwrap-panic (get value (map-get? map-votes-accept-join {address: tx-sender}))) (unwrap-panic (get-k-at-block-asked-to-join tx-sender))) + (accept-miner-in-pool tx-sender) + (ok false)))) + +(define-public (add-pending-miners-to-pool) +(let ((len-pending-accept-list (len (var-get pending-accept-list)))) + (asserts! (not (is-eq len-pending-accept-list u0)) err-no-pending-miners) + (asserts! (x-blocks-passed blocks-to-pass) err-more-blocks-to-pass) + (map add-miner-to-pool (var-get pending-accept-list)) + (asserts! (is-some (as-max-len? (concat (var-get miners-list) (var-get pending-accept-list)) u300)) err-list-length-exceeded) + (var-set miners-list (unwrap-panic (as-max-len? (concat (var-get miners-list) (var-get pending-accept-list)) u300))) + (var-set n (+ (var-get n) len-pending-accept-list)) + (var-set pending-accept-list (list )) + (var-set last-join-done block-height) + (some (update-threshold)) + (ok true))) + +(define-private (update-threshold) +(let ((n-now (var-get n))) + (if + (or + (is-eq n-now u1) + (is-eq n-now u2)) + (var-set k u1) + (var-set k (/ (* k-percentage (- n-now u1)) u100))))) + +(define-private (add-miner-to-pool (miner principal)) +(begin + (map-delete map-is-pending {address: miner}) + (map-set map-is-miner {address: miner} {value: true}) + (map-set map-block-joined {address: miner} {block-height: block-height}) + (ok true))) + +(define-private (x-blocks-passed (x uint)) +(if (>= (- block-height (var-get last-join-done)) x) + true + false)) + +(define-private (get-k-at-block-asked-to-join (miner-to-vote principal)) +(let ((block-asked-to-join (get value (map-get? map-block-asked-to-join {address: miner-to-vote})))) + (asserts! (is-some block-asked-to-join) err-not-asked-to-join) + (if + (is-eq + (unwrap-panic block-asked-to-join) + block-height) + (ok (var-get k)) + (at-block + (unwrap-panic + (get-block-info? id-header-hash + (unwrap-panic block-asked-to-join))) + (ok (var-get k)))))) + +(define-private (get-block-number-asked-to-join (miner-to-vote principal)) +(let ((block-asked-to-join (get value (map-get? map-block-asked-to-join {address: miner-to-vote})))) + (if (not (is-some block-asked-to-join)) u0 + (if + (is-eq + (unwrap-panic block-asked-to-join) + block-height) + (var-get k) + (at-block + (unwrap-panic + (get-block-info? id-header-hash + (unwrap-panic block-asked-to-join))) + (var-get k)))))) + +(define-private (get-n-at-block-asked-to-join (miner-to-vote principal)) +(let ((block-asked-to-join (get value (map-get? map-block-asked-to-join {address: miner-to-vote})))) + (asserts! (is-some block-asked-to-join) err-not-asked-to-join) + (if + (is-eq + (unwrap-panic block-asked-to-join) block-height) + (ok (var-get n)) + (at-block + (unwrap-panic + (get-block-info? id-header-hash + (unwrap-panic block-asked-to-join))) + (ok (var-get n)))))) + +;; LEAVING FLOW + +(define-public (leave-pool) +(begin + (asserts! (check-is-miner-now contract-caller) err-not-in-miner-map) + (asserts! (not (is-eq (var-get notifier) contract-caller)) err-currently-notifier) + (let ((remove-result (unwrap-panic (remove-principal-miners-list tx-sender))) + (new-k-percentage (if (> (var-get n) u2) (/ (* (var-get k) u100) (- (var-get n) u2)) u100))) + ;; if n<=2, set a value for new-k-percentage > k-critical to make sure threshold is updated + (some (var-set miners-list remove-result)) + (var-set n (- (var-get n) u1)) + (map-set map-is-miner {address: tx-sender} {value: false}) + (if + (is-some (index-of? (var-get proposed-removal-list) tx-sender)) + (var-set proposed-removal-list (unwrap-panic (remove-principal-proposed-removal-list tx-sender))) + true) + (if + (>= new-k-percentage k-critical) + (if + (> (var-get n) u1) + (some (update-threshold)) + (if + (is-eq (var-get n) u1) + (some (var-set k u1)) + (some (var-set k u0)))) + none) + (ok true)))) + +;; REMOVING FLOW + +(define-public (propose-removal (miner-to-remove principal)) +(begin + (asserts! (not (is-eq (var-get n) u1)) err-cant-remove-when-alone-in-pool) + (asserts! (check-is-miner-now contract-caller) err-not-in-miner-map) + (asserts! (check-is-miner-now miner-to-remove) err-not-in-miner-map-miner-to-remove) + (asserts! (not (check-is-proposed-for-removal-now miner-to-remove)) err-already-proposed-for-removal) + (map-set map-block-proposed-to-remove {address: miner-to-remove} {value: block-height}) + (map-set map-is-proposed-for-removal {address: miner-to-remove} {value: true}) + (var-set proposed-removal-list (unwrap! (as-max-len? (append (var-get proposed-removal-list) miner-to-remove) u300) err-list-length-exceeded)) + (ok true))) + +(define-public (vote-positive-remove-request (miner-to-vote principal)) +(begin + (asserts! (not (is-eq contract-caller miner-to-vote)) err-cant-vote-himself) + (asserts! (check-is-proposed-for-removal-now miner-to-vote) err-not-proposed-for-removal) ;; map_is_proposed_for_removal + (asserts! (is-ok (get-k-at-block-proposed-removal miner-to-vote)) err-not-proposed-for-removal-proposal-block-missing) + (asserts! (unwrap! (check-is-miner-when-requested-remove miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission) + (asserts! (not (has-voted-remove miner-to-vote)) err-already-voted) ;; O(1) + (map-set map-remove-request-voter + {miner-to-vote: miner-to-vote, voter: tx-sender} + {value: true}) + (if (is-some (get value (map-get? map-votes-accept-removal {address: miner-to-vote}))) + (map-set map-votes-accept-removal {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-accept-removal {address: miner-to-vote}))) u1)}) + (map-set map-votes-accept-removal {address: miner-to-vote} {value: u1})) + (some + (if (is-vote-accepted (unwrap-panic (get value (map-get? map-votes-accept-removal {address: miner-to-vote}))) (unwrap-panic (get-k-at-block-proposed-removal miner-to-vote))) + (process-removal miner-to-vote) + (ok false))) + (ok true))) + +(define-public (vote-negative-remove-request (miner-to-vote principal)) +(begin + (asserts! (not (is-eq contract-caller miner-to-vote)) err-cant-vote-himself) + (asserts! (check-is-proposed-for-removal-now miner-to-vote) err-not-proposed-for-removal) ;; map_is_waiting + (asserts! (is-ok (get-k-at-block-proposed-removal miner-to-vote)) err-not-proposed-for-removal-proposal-block-missing) + (asserts! (unwrap! (check-is-miner-when-requested-remove miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission) + (asserts! (not (has-voted-remove miner-to-vote)) err-already-voted) ;; O(1) + (map-set map-remove-request-voter + {miner-to-vote: miner-to-vote, voter: tx-sender} + {value: true}) + (if (is-some (get value (map-get? map-votes-reject-removal {address: miner-to-vote}))) + (map-set map-votes-reject-removal {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-reject-removal {address: miner-to-vote}))) u1)}) + (map-set map-votes-reject-removal {address: miner-to-vote} {value: u1})) + (some + (if (is-vote-rejected-remove (unwrap-panic (get value (map-get? map-votes-reject-removal {address: miner-to-vote}))) (unwrap-panic (get-k-at-block-proposed-removal miner-to-vote)) (unwrap-panic (get-n-at-block-proposed-removal miner-to-vote))) + (reject-removal miner-to-vote) + (ok false))) + (ok true))) + +(define-public (quit-proposed-removal-list) +(begin + (asserts! + (<= + blocks-to-pass + (- block-height + (default-to block-height + (get value + (map-get? map-block-proposed-to-remove {address: contract-caller}))))) + err-more-blocks-to-pass) + (let ((remove-result (unwrap-panic (remove-principal-proposed-removal-list contract-caller)))) + (var-set miner-to-remove-votes-remove contract-caller) + (var-set proposed-removal-list remove-result) + (map-delete map-is-proposed-for-removal {address: contract-caller}) + (ok (clear-votes-map-remove-vote contract-caller))))) + +(define-private (process-removal (miner principal)) +(let ((remove-result (unwrap-panic (remove-principal-miners-list miner))) + (new-k-percentage (if (> (var-get n) u2) (/ (* (var-get k) u100) (- (var-get n) u2)) u100))) + (some (var-set miners-list remove-result)) + (var-set miner-to-remove-votes-remove miner) + (var-set n (- (var-get n) u1)) + (map-delete map-is-miner {address: miner}) + (map-set map-blacklist {address: miner} {value: true}) + (var-set proposed-removal-list (unwrap-panic (remove-principal-proposed-removal-list miner))) + (clear-votes-map-remove-vote miner) + (if (>= new-k-percentage k-critical) + (if + (> (var-get n) u1) + (update-threshold) + (if + (is-eq (var-get n) u1) + (var-set k u1) + (var-set k u0))) + false) + (ok true))) + +(define-private (reject-removal (miner principal)) +(begin + (var-set miner-to-remove-votes-remove miner) + (var-set proposed-removal-list (unwrap-panic (remove-principal-proposed-removal-list miner))) + (clear-votes-map-remove-vote miner) + (ok true))) + +(define-private (has-voted-remove (miner principal)) +(if (is-some (get value (map-get? map-remove-request-voter {miner-to-vote: miner, voter: tx-sender}))) + (unwrap-panic (get value (map-get? map-remove-request-voter {miner-to-vote: miner, voter: tx-sender}))) + false)) + +(define-private (clear-votes-map-remove-vote (miner principal)) +(begin + (map-delete map-votes-accept-removal {address: (var-get miner-to-remove-votes-remove)}) + (map-delete map-votes-reject-removal {address: (var-get miner-to-remove-votes-remove)}) + (map-delete map-block-proposed-to-remove {address: (var-get miner-to-remove-votes-remove)}) + (map-delete map-is-proposed-for-removal {address: (var-get miner-to-remove-votes-remove)}) + (map remove-map-record-remove-vote (var-get miners-list)))) + +(define-private (remove-map-record-remove-vote (miner principal)) +(if (is-some (map-get? map-remove-request-voter {miner-to-vote: (var-get miner-to-remove-votes-remove), voter: miner})) + (map-delete map-remove-request-voter {miner-to-vote: (var-get miner-to-remove-votes-remove), voter: miner}) + false)) + +(define-private (get-k-at-block-proposed-removal (miner-to-vote principal)) +(let ((block-proposed-to-remove (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})))) + (asserts! (is-some block-proposed-to-remove) err-not-proposed-for-removal) + (if + (is-eq (unwrap-panic + block-proposed-to-remove) + block-height) + (ok (var-get k)) + (at-block + (unwrap-panic + (get-block-info? id-header-hash + (unwrap-panic block-proposed-to-remove))) + (ok (var-get k)))))) + +(define-private (get-n-at-block-proposed-removal (miner-to-vote principal)) +(let ((block-proposed-to-remove (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})))) + (asserts! (is-some block-proposed-to-remove) err-not-proposed-for-removal) + (if + (is-eq (unwrap-panic + block-proposed-to-remove) + block-height) + (ok (var-get n)) + (at-block + (unwrap-panic + (get-block-info? id-header-hash + (unwrap-panic block-proposed-to-remove))) + (ok (var-get n)))))) + +;; UPDATE NOTIFIER + +(define-public (start-vote-notifier) +(begin + (asserts! (not (var-get notifier-vote-active)) err-vote-started-already) + (var-set notifier-vote-start-block block-height) + (var-set notifier-vote-end-block (+ (var-get notifier-vote-start-block) notifier-election-blocks-to-pass)) + (var-set notifier-vote-active true) + (if (var-get notifier-previous-entries-removed) + (begin + (ok (var-set notifier-previous-entries-removed false))) + (end-vote-notifier-private)))) + +(define-public (end-vote-notifier) +(begin + (asserts! (>= block-height (var-get notifier-vote-end-block)) err-voting-still-active) + (var-set notifier-vote-active false) + (end-vote-notifier-private))) + +(define-private (end-vote-notifier-private) +(begin + (unwrap! (get-max-votes-number-notifier) (err u99999)) + (if + (> + (var-get max-votes-notifier) + (/ (var-get k) u2)) + (var-set notifier (var-get max-voted-proposed-notifier)) + false) + (delete-all-notifier-entries) + (ok true))) + +(define-private (get-max-votes-number-notifier) +(ok (map compare-votes-number-notifier (var-get miners-list)))) + +(define-private (compare-votes-number-notifier (proposed-notifier principal)) + (let ((proposed-notifier-votes (default-to u0 (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier}))))) + (ok + (if + (and + (> proposed-notifier-votes (/ (var-get k) u2)) + (or + (> proposed-notifier-votes (var-get max-votes-notifier)) + (and + (is-eq proposed-notifier-votes (var-get max-votes-notifier)) + (< + (unwrap-panic (get block-height (map-get? map-block-joined {address: proposed-notifier}))) + (unwrap-panic (get block-height (map-get? map-block-joined {address: (var-get max-voted-proposed-notifier)}))) + )))) + (begin + (var-set max-votes-notifier proposed-notifier-votes) + (var-set max-voted-proposed-notifier proposed-notifier)) + false)))) + +(define-private (delete-all-notifier-entries) +(begin + (var-set max-votes-notifier u0) + (var-set max-voted-proposed-notifier (var-get notifier)) + (map delete-one-notifier-entry (var-get miners-list)) + (var-set notifier-previous-entries-removed true))) + +(define-private (delete-one-notifier-entry (miner principal)) +(begin + (map-delete map-voted-update-notifier {miner-who-voted: miner}) + (map-delete map-votes-notifier {voted-notifier: miner}))) + +(define-public (vote-notifier (voted-notifier principal)) +(begin + (asserts! (and (is-some (get value (map-get? map-is-miner {address: voted-notifier}))) (unwrap-panic (get value (map-get? map-is-miner {address: voted-notifier})))) err-not-in-miner-map) + (asserts! (and (is-some (get value (map-get? map-is-miner {address: contract-caller}))) (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller})))) err-no-vote-permission) + (asserts! (var-get notifier-vote-active) err-not-voting-period) + (asserts! (not (is-eq contract-caller voted-notifier)) err-cant-vote-himself) + (asserts! (< block-height (var-get notifier-vote-end-block)) err-not-voting-period) + (asserts! (is-none (get miner-voted (map-get? map-voted-update-notifier {miner-who-voted: tx-sender}))) err-already-voted) + (map-set map-voted-update-notifier {miner-who-voted: tx-sender} {miner-voted: voted-notifier}) + (if (is-none (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) + (map-set map-votes-notifier {voted-notifier: voted-notifier} {votes-number: u1}) + (map-set map-votes-notifier {voted-notifier: voted-notifier} {votes-number: (+ (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) u1)})) + (try! + (if (is-vote-accepted (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) (var-get k)) + (begin + (var-set notifier voted-notifier) + (var-set notifier-vote-end-block block-height) + (var-set notifier-vote-active false) + (end-vote-notifier)) + (ok false))) +(ok true))) + +;; WARNING FLOW + +(define-public (warn-miner (miner principal)) +(let ((incremented-value (+ u1 (default-to u0 (get value (map-get? map-warnings {address: miner})))))) + (asserts! (is-eq contract-caller (var-get notifier)) err-only-notifier) + (asserts! + (not (and + (is-none (at-block (unwrap! (get-block-info? id-header-hash (- block-height u1)) err-cant-unwrap-block-info) (get value (map-get? map-warnings {address: miner})))) + (>= incremented-value u2))) + err-one-warning-per-block) + (asserts! + (not + (>= + (- + incremented-value + (unwrap! (at-block (unwrap! (get-block-info? id-header-hash (- block-height u1)) err-cant-unwrap-block-info) (get value (map-get? map-warnings {address: miner}))) err-cant-unwrap-block-info)) + u2)) + err-one-warning-per-block) + (ok (map-set map-warnings {address: miner} {value: incremented-value})))) + +;; ELECTION FUNCTIONS + +(define-private (is-vote-accepted (votes-number uint) (k-local uint)) +(if + (is-eq k-local u0) ;; k is 0 for n=1, n=2 + (>= votes-number u1) + (>= votes-number k-local))) + +(define-private (is-vote-rejected-join (votes-number uint) (k-local uint) (n-local uint)) +(if + (is-eq n-local u1) + (>= votes-number u1) + (if (is-eq n-local u2) + (>= votes-number u2) + (>= votes-number (+ (- n-local k-local) u1))))) + +(define-private (is-vote-rejected-remove (votes-number uint) (k-local uint) (n-local uint)) +(if + (is-eq n-local u2) + (>= votes-number u1) + (if (is-eq n-local u3) + (>= votes-number u2) + (>= votes-number (+ (- n-local k-local) u1))))) + +;; RECOVERING UNASSIGNED FUNDS FUNCTIONS + +;; A public function each miner can call in order to recover the +;; undistributed rewards caused by dividing rewards to stackers +(define-public (flush-change) +(let ((total-change-funds (var-get reward-change-funds)) + (current-miners-list-len (len (var-get miners-list))) + (distributed-change-funds (/ total-change-funds current-miners-list-len)) + (change-after-flushing (mod total-change-funds current-miners-list-len))) + (asserts! (check-is-miner-now contract-caller) err-not-in-miner-map) + (var-set temp-distributed-change-funds distributed-change-funds) + (var-set temp-change-after-flushing change-after-flushing) + (map flush-distribution-one-miner (var-get miners-list)) + (ok + (var-set reward-change-funds + (- + total-change-funds + (* + current-miners-list-len + distributed-change-funds)))))) + +(define-private (flush-distribution-one-miner (miner principal)) +(let ((miner-balance (default-to u0 (map-get? balance miner))) + (total-change-funds (var-get reward-change-funds)) + (current-miners-list-len (len (var-get miners-list))) + (distributed-change-funds (/ total-change-funds current-miners-list-len))) + (map-set balance miner + (+ + miner-balance + (var-get temp-distributed-change-funds))))) + +;; LIST PROCESSING FUNCTIONS + +(define-private (remove-principal-waiting-list (miner principal)) +(begin + (var-set waiting-list-miner-to-remove miner) + (ok (filter is-principal-in-waiting-list (var-get waiting-list))))) + +(define-private (remove-principal-pending-accept-list (miner principal)) +(begin + (var-set waiting-list-miner-to-remove miner) + (ok (filter is-principal-in-pending-accept-list (var-get pending-accept-list))))) + +(define-private (remove-principal-miners-list (miner principal)) +(begin + (var-set miners-list-miner-to-remove miner) + (ok (filter is-principal-in-miners-list (var-get miners-list))))) + +(define-private (remove-principal-proposed-removal-list (miner principal)) +(begin + (var-set proposed-removal-list-miner-to-remove miner) + (ok (filter is-principal-in-proposed-removal-list (var-get proposed-removal-list))))) + +;; MINER STATUS FUNCTIONS + +;; (define-private (check-is-miner-when-requested-join (miner-to-vote principal)) +;; (ok +;; (if +;; (is-some +;; (if +;; (is-eq +;; (unwrap! (get value (map-get? map-block-asked-to-join {address: miner-to-vote})) err-cant-unwrap-asked-to-join) +;; block-height) +;; (get value (map-get? map-is-miner {address: contract-caller})) +;; (at-block +;; (unwrap! +;; (get-block-info? id-header-hash +;; (unwrap! (get value (map-get? map-block-asked-to-join {address: miner-to-vote})) err-cant-unwrap-asked-to-join)) +;; err-cant-unwrap-block-info) +;; (get value (map-get? map-is-miner {address: contract-caller}))))) +;; (if +;; (is-eq +;; (unwrap! +;; (get value (map-get? map-block-asked-to-join {address: miner-to-vote})) +;; err-cant-unwrap-asked-to-join) +;; block-height) +;; (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller}))) +;; (at-block +;; (unwrap! +;; (get-block-info? id-header-hash +;; (unwrap-panic (get value (map-get? map-block-asked-to-join {address: miner-to-vote})))) +;; err-cant-unwrap-block-info) +;; (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller}))))) +;; false))) + +(define-private (check-is-miner-when-requested-join (miner-to-vote principal)) +(let ( + (asked-to-join-height (unwrap! (get value (map-get? map-block-asked-to-join {address: miner-to-vote})) err-cant-unwrap-asked-to-join)) + (is-asked-to-join-height (is-eq asked-to-join-height block-height)) + (asked-to-join-header-hash (unwrap! (get-block-info? id-header-hash asked-to-join-height) err-cant-unwrap-block-info))) + (ok + (default-to false + (if is-asked-to-join-height + (get value (map-get? map-is-miner {address: tx-sender})) + (at-block asked-to-join-header-hash (get value (map-get? map-is-miner {address: tx-sender})))))))) + +(define-private (check-is-miner-when-requested-remove (miner-to-vote principal)) +(ok + (if + (is-some + (if + (is-eq + (unwrap! (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})) err-cant-unwrap-asked-to-join) + block-height) + (get value (map-get? map-is-miner {address: contract-caller})) + (at-block + (unwrap! + (get-block-info? id-header-hash + (unwrap! (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})) err-cant-unwrap-asked-to-join)) + err-cant-unwrap-block-info) + (get value (map-get? map-is-miner {address: contract-caller}))))) + (if + (is-eq + (unwrap! + (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})) + err-cant-unwrap-asked-to-join) + block-height) + (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller}))) + (at-block + (unwrap! + (get-block-info? id-header-hash + (unwrap-panic (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})))) + err-cant-unwrap-block-info) + (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller}))))) + false))) + +(define-read-only (check-is-miner-when-requested-join-tool-fn (miner-to-vote-address principal)) +(get value (map-get? map-is-miner {address: contract-caller}))) + +(define-private (check-is-miner-now (miner principal)) +(default-to false (get value (map-get? map-is-miner {address: miner})))) + +(define-private (check-is-proposed-for-removal-now (miner principal)) +(default-to false (get value (map-get? map-is-proposed-for-removal {address: miner})))) + +(define-private (check-is-waiting-now (miner principal)) +(default-to false (get value (map-get? map-is-waiting {address: miner})))) + +(define-private (check-is-pending-now (miner principal)) +(default-to false (get value (map-get? map-is-pending {address: miner})))) + +(define-read-only (get-reward-at-block-read (block-number uint)) +{reward: (get-block-info? block-reward block-number), +claimer: (get-block-info? miner-address block-number)}) + +(define-read-only (get-address-status (address principal)) +(if (check-is-miner-now address) + (ok "is-miner") + (if (check-is-waiting-now address) + (ok "is-waiting") + (if (check-is-pending-now address) + (ok "is-pending") + (ok "is-none"))))) + +;; READ-ONLY UTILS + +(define-read-only (get-k) +(var-get k)) + +(define-read-only (get-notifier) +(var-get notifier)) + +(define-read-only (get-blocks-won) +(var-get blocks-won)) + +(define-read-only (get-total-rewards-distributed) +(var-get total-rewarded)) + +(define-read-only (get-waiting-list) +(var-get waiting-list)) + +(define-read-only (get-waiting-list-positions (index1 uint) (index2 uint)) +(default-to (list ) (slice? (var-get waiting-list) index1 index2))) + +(define-read-only (get-pending-accept-list) +(var-get pending-accept-list)) + +(define-read-only (get-pending-accept-list-positions (index1 uint) (index2 uint)) +(default-to (list ) (slice? (var-get pending-accept-list) index1 index2))) + +(define-read-only (get-miners-list) +(var-get miners-list)) + +(define-read-only (get-miners-list-positions (index1 uint) (index2 uint)) +(default-to (list ) (slice? (var-get miners-list) index1 index2))) + +;; (define-private (concat-us (b {hashbytes: (buff 32), version: (buff 1)})) +;; (let ((miner-bitcoin (unwrap-panic (get btc-address (map-get? btc-address {address: a}))))) +;; (default-to (list ) (as-max-len? (append (list miner-bitcoin) b) u50)))) + +;; write required +;; (define-read-only (get-miners-list-bitcoin (index1 uint) (index2 uint)) +;; ;; for each value starting from index1 to index2 -> map append value[map-bitcoin(address)] to list +;; (let ((current-list (default-to (list ) (slice? (var-get miners-list) index1 index2)))) +;; (var-set temp-miners-bitcoin-lists (list )) +;; (map concat-us current-list (list )))) + + +(define-read-only (get-proposed-removal-list) +(var-get proposed-removal-list)) + +(define-read-only (get-notifier-vote-status) +(var-get notifier-vote-active)) + +(define-read-only (get-notifier-vote-number (voted-notifier principal)) +(get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) + +(define-read-only (get-warnings-user (user principal)) +(default-to u0 (get value (map-get? map-warnings {address: user})))) + +(define-read-only (is-blacklisted (miner principal)) +(default-to false (get value (map-get? map-blacklist {address: miner})))) + +(define-read-only (is-claimed (block uint)) +(default-to false (get claimed (map-get? claimed-rewards {block-number: block})))) + +(define-read-only (get-max-voted-notifier) +(var-get max-voted-proposed-notifier)) + +(define-read-only (get-max-votes-notifier) +(var-get max-votes-notifier)) + +(define-read-only (get-current-block) +(ok block-height)) + +(define-read-only (is-user-accepted) +(is-vote-accepted + (default-to u0 (get value (map-get? map-votes-accept-join {address: tx-sender}))) + (get-block-number-asked-to-join tx-sender))) + +(define-read-only (blocks-passed-for-pending-miners) +(if (>= (- block-height (var-get last-join-done)) blocks-to-pass) + true + false)) + +(define-private (is-principal-in-waiting-list (miner principal)) +(not (is-eq + (var-get waiting-list-miner-to-remove) + miner))) + +(define-private (is-principal-in-pending-accept-list (miner principal)) +(not (is-eq + (var-get pending-accept-list-miner-to-remove) + miner))) + +(define-private (is-principal-in-miners-list (miner principal)) +(not (is-eq + (var-get miners-list-miner-to-remove) + miner))) + +(define-private (is-principal-in-proposed-removal-list (miner principal)) +(not (is-eq + (var-get proposed-removal-list-miner-to-remove) + miner))) + + + + + + + + + + + +;; title: sbtc-alpha +;; version: +;; summary: +;; description: + +;; traits + +;; token definitions +;; +(define-fungible-token sbtc u21000000) + +;; constants +;; +(define-constant err-invalid-caller u1) +(define-constant err-invalid-signer-id u2) +(define-constant err-not-token-owner u3) +(define-constant err-trading-halted u4) + +;; data vars +;; +(define-data-var contract-owner principal tx-sender) +(define-data-var coordinator (optional {addr: principal, key: (buff 33)}) none) +(define-data-var num-keys uint u4000) +(define-data-var num-signers uint u4000) +(define-data-var threshold uint u2800) +(define-data-var bitcoin-wallet-public-key (optional (buff 33)) none) +(define-data-var trading-halted bool false) + +;; data maps +;; +(define-map signers uint {addr: principal, public-key: (buff 33), key-ids: (list 4000 uint)}) + +;; public functions +;; +(define-public (set-contract-owner (owner principal)) + (begin + (asserts! (is-contract-owner) (err err-invalid-caller)) + (ok (var-set contract-owner owner)) + ) +) + +(define-public (set-coordinator-data (data {addr: principal, key: (buff 33)})) + (begin + (asserts! (is-contract-owner) (err err-invalid-caller)) + (ok (var-set coordinator (some data))) + ) +) + +(define-public (set-num-keys (nr uint)) + (begin + (asserts! (is-contract-owner) (err err-invalid-caller)) + (ok (var-set num-keys nr)) + ) +) + +(define-public (set-num-signers (nr uint)) + (begin + (asserts! (is-contract-owner) (err err-invalid-caller)) + (ok (var-set num-signers nr)) + ) +) + +(define-public (set-threshold (nr uint)) + (begin + (asserts! (is-contract-owner) (err err-invalid-caller)) + (ok (var-set threshold nr)) + ) +) + +(define-public (set-trading-halted (b bool)) + (begin + (asserts! (is-coordinator) (err err-invalid-caller)) + (ok (var-set trading-halted b)) + ) +) + +(define-public (set-signer-data (id uint) (data {addr: principal, public-key: (buff 33), key-ids: (list 4000 uint)})) + (begin + (asserts! (is-contract-owner) (err err-invalid-caller)) + (asserts! (is-valid-signer-id id) (err err-invalid-signer-id)) + (ok (map-set signers id data)) + ) +) + +(define-public (delete-signer-data (id uint)) + (begin + (asserts! (is-contract-owner) (err err-invalid-caller)) + (asserts! (is-valid-signer-id id) (err err-invalid-signer-id)) + (ok (map-delete signers id)) + ) +) + +(define-public (set-bitcoin-wallet-public-key (public-key (buff 33))) + (begin + (asserts! (is-coordinator) (err err-invalid-caller)) + (ok (var-set bitcoin-wallet-public-key (some public-key))) + ) +) + +(define-public (mint! (amount uint) (dst principal) (peg-in-txid (string-ascii 72))) + (begin + (asserts! (is-coordinator) (err err-invalid-caller)) + (print peg-in-txid) + (ft-mint? sbtc amount dst) + ) +) + +(define-public (burn! (amount uint) (src principal) (peg-out-txid (string-ascii 72))) + (begin + (asserts! (is-coordinator) (err err-invalid-caller)) + (print peg-out-txid) + (ft-burn? sbtc amount src) + ) +) + +(define-public (transfer (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34)))) + (begin + (asserts! (is-eq tx-sender sender) (err err-not-token-owner)) + (asserts! (is-eq (var-get trading-halted) false) (err err-trading-halted)) + (try! (ft-transfer? sbtc amount sender recipient)) + (match memo to-print (print to-print) 0x) + (ok true) + ) +) + +;; read only functions +;; +(define-read-only (get-coordinator-data) + (var-get coordinator) +) + +(define-read-only (get-num-keys) + (var-get num-keys) +) + +(define-read-only (get-num-signers) + (var-get num-signers) +) + +(define-read-only (get-threshold) + (var-get threshold) +) + +(define-read-only (get-trading-halted) + (var-get trading-halted) +) + +(define-read-only (get-bitcoin-wallet-public-key) + (var-get bitcoin-wallet-public-key) +) + +(define-read-only (get-signer-data (signer uint)) + (map-get? signers signer) +) + +;;(define-read-only (get-signers) +;; (map-get? signers) +;;) + +(define-read-only (get-name) + (ok "sBTC") +) + +(define-read-only (get-symbol) + (ok "sBTC") +) + +(define-read-only (get-decimals) + (ok u8) +) + +(define-read-only (get-balance-sbtc (who principal)) + (ok (ft-get-balance sbtc who)) +) + +(define-read-only (get-total-supply) + (ok (ft-get-supply sbtc)) +) + +(define-read-only (get-token-uri) + (ok (some u"https://assets.stacks.co/sbtc.pdf")) +) + +;; private functions +;; +(define-private (is-contract-owner) + (is-eq (var-get contract-owner) tx-sender) +) + +(define-private (is-coordinator) + (match (var-get coordinator) cdata + (is-eq (get addr cdata) tx-sender) + false + ) +) + +(define-private (is-valid-signer-id (id uint)) + (and (>= id u0) (< id (var-get num-signers))) +) diff --git a/sbtc-ops/smart-contract/contracts/pox-2-fake.clar b/sbtc-ops/smart-contract/contracts/pox-2-fake.clar new file mode 100644 index 00000000..0724c5db --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/pox-2-fake.clar @@ -0,0 +1,1312 @@ +;; PoX mainnet constants +;; Min/max number of reward cycles uSTX can be locked for +(define-constant MIN_POX_REWARD_CYCLES u1) +(define-constant MAX_POX_REWARD_CYCLES u12) + +;; Default length of the PoX registration window, in burnchain blocks. +(define-constant PREPARE_CYCLE_LENGTH u100) + +;; Default length of the PoX reward cycle, in burnchain blocks. +(define-constant REWARD_CYCLE_LENGTH u2100) + +;; Valid values for burnchain address versions. +;; These correspond to address hash modes in Stacks 2.0. +(define-constant ADDRESS_VERSION_P2PKH 0x00) +(define-constant ADDRESS_VERSION_P2SH 0x01) +(define-constant ADDRESS_VERSION_P2WPKH 0x02) +(define-constant ADDRESS_VERSION_P2WSH 0x03) + +;; Stacking thresholds +(define-constant STACKING_THRESHOLD_25 u20000) +(define-constant STACKING_THRESHOLD_100 u5000) + +;; The .pox-2 contract +;; Error codes +(define-constant ERR_STACKING_UNREACHABLE 255) +(define-constant ERR_STACKING_CORRUPTED_STATE 254) +(define-constant ERR_STACKING_INSUFFICIENT_FUNDS 1) +(define-constant ERR_STACKING_INVALID_LOCK_PERIOD 2) +(define-constant ERR_STACKING_ALREADY_STACKED 3) +(define-constant ERR_STACKING_NO_SUCH_PRINCIPAL 4) +(define-constant ERR_STACKING_EXPIRED 5) +(define-constant ERR_STACKING_STX_LOCKED 6) +(define-constant ERR_STACKING_PERMISSION_DENIED 9) +(define-constant ERR_STACKING_THRESHOLD_NOT_MET 11) +(define-constant ERR_STACKING_POX_ADDRESS_IN_USE 12) +(define-constant ERR_STACKING_INVALID_POX_ADDRESS 13) +(define-constant ERR_STACKING_ALREADY_REJECTED 17) +(define-constant ERR_STACKING_INVALID_AMOUNT 18) +(define-constant ERR_NOT_ALLOWED 19) +(define-constant ERR_STACKING_ALREADY_DELEGATED 20) +(define-constant ERR_DELEGATION_EXPIRES_DURING_LOCK 21) +(define-constant ERR_DELEGATION_TOO_MUCH_LOCKED 22) +(define-constant ERR_DELEGATION_POX_ADDR_REQUIRED 23) +(define-constant ERR_INVALID_START_BURN_HEIGHT 24) +(define-constant ERR_NOT_CURRENT_STACKER 25) +(define-constant ERR_STACK_EXTEND_NOT_LOCKED 26) +(define-constant ERR_STACK_INCREASE_NOT_LOCKED 27) +(define-constant ERR_DELEGATION_NO_REWARD_SLOT 28) +(define-constant ERR_DELEGATION_WRONG_REWARD_SLOT 29) + +;; PoX disabling threshold (a percent) +(define-constant POX_REJECTION_FRACTION u25) + +;; Valid values for burnchain address versions. +;; These first four correspond to address hash modes in Stacks 2.1, +;; and are defined in pox-mainnet.clar and pox-testnet.clar (so they +;; cannot be defined here again). +;; (define-constant ADDRESS_VERSION_P2PKH 0x00) +;; (define-constant ADDRESS_VERSION_P2SH 0x01) +;; (define-constant ADDRESS_VERSION_P2WPKH 0x02) +;; (define-constant ADDRESS_VERSION_P2WSH 0x03) +(define-constant ADDRESS_VERSION_NATIVE_P2WPKH 0x04) +(define-constant ADDRESS_VERSION_NATIVE_P2WSH 0x05) +(define-constant ADDRESS_VERSION_NATIVE_P2TR 0x06) +;; Keep these constants in lock-step with the address version buffs above +;; Maximum value of an address version as a uint +(define-constant MAX_ADDRESS_VERSION u6) +;; Maximum value of an address version that has a 20-byte hashbytes +;; (0x00, 0x01, 0x02, 0x03, and 0x04 have 20-byte hashbytes) +(define-constant MAX_ADDRESS_VERSION_BUFF_20 u4) +;; Maximum value of an address version that has a 32-byte hashbytes +;; (0x05 and 0x06 have 32-byte hashbytes) +(define-constant MAX_ADDRESS_VERSION_BUFF_32 u6) + +;; Data vars that store a copy of the burnchain configuration. +;; Implemented as data-vars, so that different configurations can be +;; used in e.g. test harnesses. +(define-data-var pox-prepare-cycle-length uint PREPARE_CYCLE_LENGTH) +(define-data-var pox-reward-cycle-length uint REWARD_CYCLE_LENGTH) +(define-data-var pox-rejection-fraction uint POX_REJECTION_FRACTION) +(define-data-var first-burnchain-block-height uint u0) +(define-data-var configured bool false) +(define-data-var first-2-1-reward-cycle uint u0) + +;; This function can only be called once, when it boots up +(define-public (set-burnchain-parameters (first-burn-height uint) + (prepare-cycle-length uint) + (reward-cycle-length uint) + (rejection-fraction uint) + (begin-2-1-reward-cycle uint)) + (begin + (asserts! (not (var-get configured)) (err ERR_NOT_ALLOWED)) + (var-set first-burnchain-block-height first-burn-height) + (var-set pox-prepare-cycle-length prepare-cycle-length) + (var-set pox-reward-cycle-length reward-cycle-length) + (var-set pox-rejection-fraction rejection-fraction) + (var-set first-2-1-reward-cycle begin-2-1-reward-cycle) + (var-set configured true) + (ok true)) +) + +;; The Stacking lock-up state and associated metadata. +;; Records are inserted into this map via `stack-stx`, `delegate-stack-stx`, `stack-extend` +;; `delegate-stack-extend` and burnchain transactions for invoking `stack-stx`, etc. +;; Records will be deleted from this map when auto-unlocks are processed +;; +;; This map de-normalizes some state from the `reward-cycle-pox-address-list` map +;; and the `pox-2` contract tries to keep this state in sync with the reward-cycle +;; state. The major invariants of this `stacking-state` map are: +;; (1) any entry in `reward-cycle-pox-address-list` with `some stacker` points to a real `stacking-state` +;; (2) `stacking-state.reward-set-indexes` matches the index of that `reward-cycle-pox-address-list` +;; (3) all `stacking-state.reward-set-indexes` match the index of their reward cycle entries +;; (4) `stacking-state.pox-addr` matches `reward-cycle-pox-address-list.pox-addr` +;; (5) if set, (len reward-set-indexes) == lock-period +;; (6) (reward-cycle-to-burn-height (+ lock-period first-reward-cycle)) == (get unlock-height (stx-account stacker)) +;; These invariants only hold while `cur-reward-cycle < (+ lock-period first-reward-cycle)` +;; +(define-map stacking-state + { stacker: principal } + { + ;; Description of the underlying burnchain address that will + ;; receive PoX'ed tokens. Translating this into an address + ;; depends on the burnchain being used. When Bitcoin is + ;; the burnchain, this gets translated into a p2pkh, p2sh, + ;; p2wpkh-p2sh, p2wsh-p2sh, p2wpkh, p2wsh, or p2tr UTXO, + ;; depending on the version. The `hashbytes` field *must* be + ;; either 20 bytes or 32 bytes, depending on the output. + pox-addr: { version: (buff 1), hashbytes: (buff 32) }, + ;; how long the uSTX are locked, in reward cycles. + lock-period: uint, + ;; reward cycle when rewards begin + first-reward-cycle: uint, + ;; indexes in each reward-set associated with this user. + ;; these indexes are only valid looking forward from + ;; `first-reward-cycle` (i.e., they do not correspond + ;; to entries in the reward set that may have been from + ;; previous stack-stx calls, or prior to an extend) + reward-set-indexes: (list 12 uint) + } +) + +;; Delegation relationships +(define-map delegation-state + { stacker: principal } + { + amount-ustx: uint, ;; how many uSTX delegated? + delegated-to: principal, ;; who are we delegating? + until-burn-ht: (optional uint), ;; how long does the delegation last? + ;; does the delegate _need_ to use a specific + ;; pox recipient address? + pox-addr: (optional { version: (buff 1), hashbytes: (buff 32) }) + } +) + +;; allowed contract-callers +(define-map allowance-contract-callers + { sender: principal, contract-caller: principal } + { until-burn-ht: (optional uint) }) + +;; How many uSTX are stacked in a given reward cycle. +;; Updated when a new PoX address is registered, or when more STX are granted +;; to it. +(define-map reward-cycle-total-stacked + { reward-cycle: uint } + { total-ustx: uint } +) + +;; Internal map read by the Stacks node to iterate through the list of +;; PoX reward addresses on a per-reward-cycle basis. +(define-map reward-cycle-pox-address-list + { reward-cycle: uint, index: uint } + { + pox-addr: { version: (buff 1), hashbytes: (buff 32) }, + total-ustx: uint, + stacker: (optional principal) + } +) + +(define-map reward-cycle-pox-address-list-len + { reward-cycle: uint } + { len: uint } +) + +;; how much has been locked up for this address before +;; committing? +;; this map allows stackers to stack amounts < minimum +;; by paying the cost of aggregation during the commit +(define-map partial-stacked-by-cycle + { + pox-addr: { version: (buff 1), hashbytes: (buff 32) }, + reward-cycle: uint, + sender: principal + } + { stacked-amount: uint } +) + +;; This is identical to partial-stacked-by-cycle, but its data is never deleted. +;; It is used to preserve data for downstream clients to observe aggregate +;; commits. Each key/value pair in this map is simply the last value of +;; partial-stacked-by-cycle right after it was deleted (so, subsequent calls +;; to the `stack-aggregation-*` functions will overwrite this). +(define-map logged-partial-stacked-by-cycle + { + pox-addr: { version: (buff 1), hashbytes: (buff 32) }, + reward-cycle: uint, + sender: principal + } + { stacked-amount: uint } +) + +;; Amount of uSTX that reject PoX, by reward cycle +(define-map stacking-rejection + { reward-cycle: uint } + { amount: uint } +) + +;; Who rejected in which reward cycle +(define-map stacking-rejectors + { stacker: principal, reward-cycle: uint } + { amount: uint } +) + +;; Getter for stacking-rejectors +(define-read-only (get-pox-rejection (stacker principal) (reward-cycle uint)) + (map-get? stacking-rejectors { stacker: stacker, reward-cycle: reward-cycle })) + +;; Has PoX been rejected in the given reward cycle? +(define-read-only (is-pox-active (reward-cycle uint)) + (let ( + (reject-votes + (default-to + u0 + (get amount (map-get? stacking-rejection { reward-cycle: reward-cycle })))) + ) + ;; (100 * reject-votes) / stx-liquid-supply < pox-rejection-fraction + (< (* u100 reject-votes) + (* (var-get pox-rejection-fraction) stx-liquid-supply))) +) + +;; What's the reward cycle number of the burnchain block height? +;; Will runtime-abort if height is less than the first burnchain block (this is intentional) +(define-read-only (burn-height-to-reward-cycle (height uint)) + (/ (- height (var-get first-burnchain-block-height)) (var-get pox-reward-cycle-length))) + +;; What's the block height at the start of a given reward cycle? +(define-read-only (reward-cycle-to-burn-height (cycle uint)) + (+ (var-get first-burnchain-block-height) (* cycle (var-get pox-reward-cycle-length)))) + +;; What's the current PoX reward cycle? +(define-read-only (current-pox-reward-cycle) + (burn-height-to-reward-cycle burn-block-height)) + +;; Get the _current_ PoX stacking principal information. If the information +;; is expired, or if there's never been such a stacker, then returns none. +(define-read-only (get-stacker-info (stacker principal)) + (match (map-get? stacking-state { stacker: stacker }) + stacking-info + (if (<= (+ (get first-reward-cycle stacking-info) (get lock-period stacking-info)) (current-pox-reward-cycle)) + ;; present, but lock has expired + none + ;; present, and lock has not expired + (some stacking-info) + ) + ;; no state at all + none + )) + +(define-read-only (check-caller-allowed) + (or (is-eq tx-sender contract-caller) + (let ((caller-allowed + ;; if not in the caller map, return false + (unwrap! (map-get? allowance-contract-callers + { sender: tx-sender, contract-caller: contract-caller }) + false)) + (expires-at + ;; if until-burn-ht not set, then return true (because no expiry) + (unwrap! (get until-burn-ht caller-allowed) true))) + ;; is the caller allowance expired? + (if (>= burn-block-height expires-at) + false + true)))) + +(define-read-only (get-check-delegation (stacker principal)) + (let ((delegation-info (try! (map-get? delegation-state { stacker: stacker })))) + ;; did the existing delegation expire? + (if (match (get until-burn-ht delegation-info) + until-burn-ht (> burn-block-height until-burn-ht) + false) + ;; it expired, return none + none + ;; delegation is active + (some delegation-info)))) + +;; Get the size of the reward set for a reward cycle. +;; Note that this does _not_ return duplicate PoX addresses. +;; Note that this also _will_ return PoX addresses that are beneath +;; the minimum threshold -- i.e. the threshold can increase after insertion. +;; Used internally by the Stacks node, which filters out the entries +;; in this map to select PoX addresses with enough STX. +(define-read-only (get-reward-set-size (reward-cycle uint)) + (default-to + u0 + (get len (map-get? reward-cycle-pox-address-list-len { reward-cycle: reward-cycle })))) + +;; How many rejection votes have we been accumulating for the next block +(define-read-only (next-cycle-rejection-votes) + (default-to + u0 + (get amount (map-get? stacking-rejection { reward-cycle: (+ u1 (current-pox-reward-cycle)) })))) + +;; Add a single PoX address to a single reward cycle. +;; Used to build up a set of per-reward-cycle PoX addresses. +;; No checking will be done -- don't call if this PoX address is already registered in this reward cycle! +;; Returns the index into the reward cycle that the PoX address is stored to +(define-private (append-reward-cycle-pox-addr (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) + (reward-cycle uint) + (amount-ustx uint) + (stacker (optional principal))) + (let ((sz (get-reward-set-size reward-cycle))) + (map-set reward-cycle-pox-address-list + { reward-cycle: reward-cycle, index: sz } + { pox-addr: pox-addr, total-ustx: amount-ustx, stacker: stacker }) + (map-set reward-cycle-pox-address-list-len + { reward-cycle: reward-cycle } + { len: (+ u1 sz) }) + sz)) + +;; How many uSTX are stacked? +(define-read-only (get-total-ustx-stacked (reward-cycle uint)) + (default-to + u0 + (get total-ustx (map-get? reward-cycle-total-stacked { reward-cycle: reward-cycle }))) +) + +;; Called internally by the node to iterate through the list of PoX addresses in this reward cycle. +;; Returns (optional (tuple (pox-addr ) (total-ustx ))) +(define-read-only (get-reward-set-pox-address (reward-cycle uint) (index uint)) + (map-get? reward-cycle-pox-address-list { reward-cycle: reward-cycle, index: index })) + +(define-private (fold-unlock-reward-cycle (set-index uint) + (data-res (response { cycle: uint, + first-unlocked-cycle: uint, + stacker: principal + } int))) + (let ((data (try! data-res)) + (cycle (get cycle data)) + (first-unlocked-cycle (get first-unlocked-cycle data))) + ;; if current-cycle hasn't reached first-unlocked-cycle, just continue to next iter + (asserts! (>= cycle first-unlocked-cycle) (ok (merge data { cycle: (+ u1 cycle) }))) + (let ((cycle-entry (unwrap-panic (map-get? reward-cycle-pox-address-list { reward-cycle: cycle, index: set-index }))) + (cycle-entry-u (get stacker cycle-entry)) + (cycle-entry-total-ustx (get total-ustx cycle-entry)) + (cycle-last-entry-ix (- (get len (unwrap-panic (map-get? reward-cycle-pox-address-list-len { reward-cycle: cycle }))) u1))) + (asserts! (is-eq cycle-entry-u (some (get stacker data))) (err ERR_STACKING_CORRUPTED_STATE)) + (if (not (is-eq cycle-last-entry-ix set-index)) + ;; do a "move" if the entry to remove isn't last + (let ((move-entry (unwrap-panic (map-get? reward-cycle-pox-address-list { reward-cycle: cycle, index: cycle-last-entry-ix })))) + (map-set reward-cycle-pox-address-list + { reward-cycle: cycle, index: set-index } + move-entry) + (match (get stacker move-entry) moved-stacker + ;; if the moved entry had an associated stacker, update its state + (let ((moved-state (unwrap-panic (map-get? stacking-state { stacker: moved-stacker }))) + ;; calculate the index into the reward-set-indexes that `cycle` is at + (moved-cycle-index (- cycle (get first-reward-cycle moved-state))) + (moved-reward-list (get reward-set-indexes moved-state)) + ;; reward-set-indexes[moved-cycle-index] = set-index via slice?, append, concat. + (update-list (unwrap-panic (replace-at? moved-reward-list moved-cycle-index set-index)))) + (map-set stacking-state { stacker: moved-stacker } + (merge moved-state { reward-set-indexes: update-list }))) + ;; otherwise, we don't need to update stacking-state after move + true)) + ;; if not moving, just noop + true) + ;; in all cases, we now need to delete the last list entry + (map-delete reward-cycle-pox-address-list { reward-cycle: cycle, index: cycle-last-entry-ix }) + (map-set reward-cycle-pox-address-list-len { reward-cycle: cycle } { len: cycle-last-entry-ix }) + ;; finally, update `reward-cycle-total-stacked` + (map-set reward-cycle-total-stacked { reward-cycle: cycle } + { total-ustx: (- (get total-ustx (unwrap-panic (map-get? reward-cycle-total-stacked { reward-cycle: cycle }))) + cycle-entry-total-ustx) }) + (ok (merge data { cycle: (+ u1 cycle)} ))))) + +;; This method is called by the Stacks block processor directly in order to handle the contract state mutations +;; associated with an early unlock. This can only be invoked by the block processor: it is private, and no methods +;; from this contract invoke it. +(define-private (handle-unlock (user principal) (amount-locked uint) (cycle-to-unlock uint)) + (let ((user-stacking-state (unwrap-panic (map-get? stacking-state { stacker: user }))) + (first-cycle-locked (get first-reward-cycle user-stacking-state)) + (reward-set-indexes (get reward-set-indexes user-stacking-state))) + ;; iterate over each reward set the user is a member of, and remove them from the sets. only apply to reward sets after cycle-to-unlock. + (try! (fold fold-unlock-reward-cycle reward-set-indexes (ok { cycle: first-cycle-locked, first-unlocked-cycle: cycle-to-unlock, stacker: user }))) + ;; Now that we've cleaned up all the reward set entries for the user, delete the user's stacking-state + (map-delete stacking-state { stacker: user }) + (ok true))) + +;; Add a PoX address to the `cycle-index`-th reward cycle, if `cycle-index` is between 0 and the given num-cycles (exclusive). +;; Arguments are given as a tuple, so this function can be (folded ..)'ed onto a list of its arguments. +;; Used by add-pox-addr-to-reward-cycles. +;; No checking is done. +;; The returned tuple is the same as inputted `params`, but the `i` field is incremented if +;; the pox-addr was added to the given cycle. Also, `reward-set-indexes` grows to include all +;; of the `reward-cycle-index` key parts of the `reward-cycle-pox-address-list` which get added by this function. +;; This way, the caller knows which items in a given reward cycle's PoX address list got updated. +(define-private (add-pox-addr-to-ith-reward-cycle (cycle-index uint) (params (tuple + (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) + (reward-set-indexes (list 12 uint)) + (first-reward-cycle uint) + (num-cycles uint) + (stacker (optional principal)) + (amount-ustx uint) + (i uint)))) + (let ((reward-cycle (+ (get first-reward-cycle params) (get i params))) + (num-cycles (get num-cycles params)) + (i (get i params)) + (reward-set-index (if (< i num-cycles) + (let ((total-ustx (get-total-ustx-stacked reward-cycle)) + (reward-index + ;; record how many uSTX this pox-addr will stack for in the given reward cycle + (append-reward-cycle-pox-addr + (get pox-addr params) + reward-cycle + (get amount-ustx params) + (get stacker params) + ))) + ;; update running total + (map-set reward-cycle-total-stacked + { reward-cycle: reward-cycle } + { total-ustx: (+ (get amount-ustx params) total-ustx) }) + (some reward-index)) + none)) + (next-i (if (< i num-cycles) (+ i u1) i))) + { + pox-addr: (get pox-addr params), + first-reward-cycle: (get first-reward-cycle params), + num-cycles: num-cycles, + amount-ustx: (get amount-ustx params), + stacker: (get stacker params), + reward-set-indexes: (match + reward-set-index new (unwrap-panic (as-max-len? (append (get reward-set-indexes params) new) u12)) + (get reward-set-indexes params)), + i: next-i + })) + +;; Add a PoX address to a given sequence of reward cycle lists. +;; A PoX address can be added to at most 12 consecutive cycles. +;; No checking is done. +(define-private (add-pox-addr-to-reward-cycles (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) + (first-reward-cycle uint) + (num-cycles uint) + (amount-ustx uint) + (stacker principal)) + (let ((cycle-indexes (list u0 u1 u2 u3 u4 u5 u6 u7 u8 u9 u10 u11)) + (results (fold add-pox-addr-to-ith-reward-cycle cycle-indexes + { pox-addr: pox-addr, first-reward-cycle: first-reward-cycle, num-cycles: num-cycles, + reward-set-indexes: (list), amount-ustx: amount-ustx, i: u0, stacker: (some stacker) })) + (reward-set-indexes (get reward-set-indexes results))) + ;; For safety, add up the number of times (add-principal-to-ith-reward-cycle) returns 1. + ;; It _should_ be equal to num-cycles. + (asserts! (is-eq num-cycles (get i results)) (err ERR_STACKING_UNREACHABLE)) + (asserts! (is-eq num-cycles (len reward-set-indexes)) (err ERR_STACKING_UNREACHABLE)) + (ok reward-set-indexes))) + +(define-private (add-pox-partial-stacked-to-ith-cycle + (cycle-index uint) + (params { pox-addr: { version: (buff 1), hashbytes: (buff 32) }, + reward-cycle: uint, + num-cycles: uint, + amount-ustx: uint })) + (let ((pox-addr (get pox-addr params)) + (num-cycles (get num-cycles params)) + (reward-cycle (get reward-cycle params)) + (amount-ustx (get amount-ustx params))) + (let ((current-amount + (default-to u0 + (get stacked-amount + (map-get? partial-stacked-by-cycle { sender: tx-sender, pox-addr: pox-addr, reward-cycle: reward-cycle }))))) + (if (>= cycle-index num-cycles) + ;; do not add to cycles >= cycle-index + false + ;; otherwise, add to the partial-stacked-by-cycle + (map-set partial-stacked-by-cycle + { sender: tx-sender, pox-addr: pox-addr, reward-cycle: reward-cycle } + { stacked-amount: (+ amount-ustx current-amount) })) + ;; produce the next params tuple + { pox-addr: pox-addr, + reward-cycle: (+ u1 reward-cycle), + num-cycles: num-cycles, + amount-ustx: amount-ustx }))) + +;; Add a PoX address to a given sequence of partial reward cycle lists. +;; A PoX address can be added to at most 12 consecutive cycles. +;; No checking is done. +(define-private (add-pox-partial-stacked (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) + (first-reward-cycle uint) + (num-cycles uint) + (amount-ustx uint)) + (let ((cycle-indexes (list u0 u1 u2 u3 u4 u5 u6 u7 u8 u9 u10 u11))) + (fold add-pox-partial-stacked-to-ith-cycle cycle-indexes + { pox-addr: pox-addr, reward-cycle: first-reward-cycle, num-cycles: num-cycles, amount-ustx: amount-ustx }) + true)) + +;; What is the minimum number of uSTX to be stacked in the given reward cycle? +;; Used internally by the Stacks node, and visible publicly. +(define-read-only (get-stacking-minimum) + (/ stx-liquid-supply STACKING_THRESHOLD_25)) + +;; Is the address mode valid for a PoX address? +(define-read-only (check-pox-addr-version (version (buff 1))) + (<= (buff-to-uint-be version) MAX_ADDRESS_VERSION)) + +;; Is this buffer the right length for the given PoX address? +(define-read-only (check-pox-addr-hashbytes (version (buff 1)) (hashbytes (buff 32))) + (if (<= (buff-to-uint-be version) MAX_ADDRESS_VERSION_BUFF_20) + (is-eq (len hashbytes) u20) + (if (<= (buff-to-uint-be version) MAX_ADDRESS_VERSION_BUFF_32) + (is-eq (len hashbytes) u32) + false))) + +;; Is the given lock period valid? +(define-read-only (check-pox-lock-period (lock-period uint)) + (and (>= lock-period MIN_POX_REWARD_CYCLES) + (<= lock-period MAX_POX_REWARD_CYCLES))) + +;; Evaluate if a participant can stack an amount of STX for a given period. +;; This method is designed as a read-only method so that it can be used as +;; a set of guard conditions and also as a read-only RPC call that can be +;; performed beforehand. +(define-read-only (can-stack-stx (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) + (amount-ustx uint) + (first-reward-cycle uint) + (num-cycles uint)) +(begin + ;; minimum uSTX must be met + (asserts! (<= (get-stacking-minimum) amount-ustx) + (err ERR_STACKING_THRESHOLD_NOT_MET)) + + (minimal-can-stack-stx pox-addr amount-ustx first-reward-cycle num-cycles))) + +;; Evaluate if a participant can stack an amount of STX for a given period. +;; This method is designed as a read-only method so that it can be used as +;; a set of guard conditions and also as a read-only RPC call that can be +;; performed beforehand. +(define-read-only (minimal-can-stack-stx + (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) + (amount-ustx uint) + (first-reward-cycle uint) + (num-cycles uint)) + (begin + ;; amount must be valid + (asserts! (> amount-ustx u0) + (err ERR_STACKING_INVALID_AMOUNT)) + + ;; sender principal must not have rejected in this upcoming reward cycle + (asserts! (is-none (get-pox-rejection tx-sender first-reward-cycle)) + (err ERR_STACKING_ALREADY_REJECTED)) + + ;; lock period must be in acceptable range. + (asserts! (check-pox-lock-period num-cycles) + (err ERR_STACKING_INVALID_LOCK_PERIOD)) + + ;; address version must be valid + (asserts! (check-pox-addr-version (get version pox-addr)) + (err ERR_STACKING_INVALID_POX_ADDRESS)) + + ;; address hashbytes must be valid for the version + (asserts! (check-pox-addr-hashbytes (get version pox-addr) (get hashbytes pox-addr)) + (err ERR_STACKING_INVALID_POX_ADDRESS)) + + (ok true))) + +;; Revoke contract-caller authorization to call stacking methods +(define-public (disallow-contract-caller (caller principal)) + (begin + (asserts! (is-eq tx-sender contract-caller) + (err ERR_STACKING_PERMISSION_DENIED)) + (ok (map-delete allowance-contract-callers { sender: tx-sender, contract-caller: caller })))) + +;; Give a contract-caller authorization to call stacking methods +;; normally, stacking methods may only be invoked by _direct_ transactions +;; (i.e., the tx-sender issues a direct contract-call to the stacking methods) +;; by issuing an allowance, the tx-sender may call through the allowed contract +(define-public (allow-contract-caller (caller principal) (until-burn-ht (optional uint))) + (begin + (asserts! (is-eq tx-sender contract-caller) + (err ERR_STACKING_PERMISSION_DENIED)) + (ok (map-set allowance-contract-callers + { sender: tx-sender, contract-caller: caller } + { until-burn-ht: until-burn-ht })))) + +;; Lock up some uSTX for stacking! Note that the given amount here is in micro-STX (uSTX). +;; The STX will be locked for the given number of reward cycles (lock-period). +;; This is the self-service interface. tx-sender will be the Stacker. +;; +;; * The given stacker cannot currently be stacking. +;; * You will need the minimum uSTX threshold. This will be determined by (get-stacking-minimum) +;; at the time this method is called. +;; * You may need to increase the amount of uSTX locked up later, since the minimum uSTX threshold +;; may increase between reward cycles. +;; * The Stacker will receive rewards in the reward cycle following `start-burn-ht`. +;; Importantly, `start-burn-ht` may not be further into the future than the next reward cycle, +;; and in most cases should be set to the current burn block height. +;; +;; The tokens will unlock and be returned to the Stacker (tx-sender) automatically. +(define-public (stack-stx (amount-ustx uint) + (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) + (start-burn-ht uint) + (lock-period uint)) + ;; this stacker's first reward cycle is the _next_ reward cycle + (let ((first-reward-cycle (+ u1 (current-pox-reward-cycle))) + (specified-reward-cycle (+ u1 (burn-height-to-reward-cycle start-burn-ht)))) + ;; the start-burn-ht must result in the next reward cycle, do not allow stackers + ;; to "post-date" their `stack-stx` transaction + (asserts! (is-eq first-reward-cycle specified-reward-cycle) + (err ERR_INVALID_START_BURN_HEIGHT)) + + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; tx-sender principal must not be stacking + (asserts! (is-none (get-stacker-info tx-sender)) + (err ERR_STACKING_ALREADY_STACKED)) + + ;; tx-sender must not be delegating + (asserts! (is-none (get-check-delegation tx-sender)) + (err ERR_STACKING_ALREADY_DELEGATED)) + + ;; the Stacker must have sufficient unlocked funds + (asserts! (>= (stx-get-balance tx-sender) amount-ustx) + (err ERR_STACKING_INSUFFICIENT_FUNDS)) + + ;; ensure that stacking can be performed + (try! (can-stack-stx pox-addr amount-ustx first-reward-cycle lock-period)) + + ;; register the PoX address with the amount stacked + (let ((reward-set-indexes (try! (add-pox-addr-to-reward-cycles pox-addr first-reward-cycle lock-period amount-ustx tx-sender)))) + ;; add stacker record + (map-set stacking-state + { stacker: tx-sender } + { pox-addr: pox-addr, + reward-set-indexes: reward-set-indexes, + first-reward-cycle: first-reward-cycle, + lock-period: lock-period }) + + ;; return the lock-up information, so the node can actually carry out the lock. + (ok { stacker: tx-sender, lock-amount: amount-ustx, unlock-burn-height: (reward-cycle-to-burn-height (+ first-reward-cycle lock-period)) })))) + +(define-public (revoke-delegate-stx) + (begin + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + (ok (map-delete delegation-state { stacker: tx-sender })))) + +;; Delegate to `delegate-to` the ability to stack from a given address. +;; This method _does not_ lock the funds, rather, it allows the delegate +;; to issue the stacking lock. +;; The caller specifies: +;; * amount-ustx: the total amount of ustx the delegate may be allowed to lock +;; * until-burn-ht: an optional burn height at which this delegation expires +;; * pox-addr: an optional address to which any rewards *must* be sent +(define-public (delegate-stx (amount-ustx uint) + (delegate-to principal) + (until-burn-ht (optional uint)) + (pox-addr (optional { version: (buff 1), + hashbytes: (buff 32) }))) + (begin + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; delegate-stx no longer requires the delegator to not currently + ;; be stacking. + + ;; pox-addr, if given, must be valid + (match pox-addr + address + (asserts! (check-pox-addr-version (get version address)) + (err ERR_STACKING_INVALID_POX_ADDRESS)) + true) + + ;; tx-sender must not be delegating + (asserts! (is-none (get-check-delegation tx-sender)) + (err ERR_STACKING_ALREADY_DELEGATED)) + + ;; add delegation record + (map-set delegation-state + { stacker: tx-sender } + { amount-ustx: amount-ustx, + delegated-to: delegate-to, + until-burn-ht: until-burn-ht, + pox-addr: pox-addr }) + + (ok true))) + +;; Commit partially stacked STX and allocate a new PoX reward address slot. +;; This allows a stacker/delegate to lock fewer STX than the minimal threshold in multiple transactions, +;; so long as: 1. The pox-addr is the same. +;; 2. This "commit" transaction is called _before_ the PoX anchor block. +;; This ensures that each entry in the reward set returned to the stacks-node is greater than the threshold, +;; but does not require it be all locked up within a single transaction +;; +;; Returns (ok uint) on success, where the given uint is the reward address's index in the list of reward +;; addresses allocated in this reward cycle. This index can then be passed to `stack-aggregation-increase` +;; to later increment the STX this PoX address represents, in amounts less than the stacking minimum. +;; +;; *New in Stacks 2.1.* +(define-private (inner-stack-aggregation-commit (pox-addr { version: (buff 1), hashbytes: (buff 32) }) + (reward-cycle uint)) + (let ((partial-stacked + ;; fetch the partial commitments + (unwrap! (map-get? partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) + (err ERR_STACKING_NO_SUCH_PRINCIPAL)))) + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + (let ((amount-ustx (get stacked-amount partial-stacked))) + (try! (can-stack-stx pox-addr amount-ustx reward-cycle u1)) + ;; Add the pox addr to the reward cycle, and extract the index of the PoX address + ;; so the delegator can later use it to call stack-aggregation-increase. + (let ((add-pox-addr-info + (add-pox-addr-to-ith-reward-cycle + u0 + { pox-addr: pox-addr, + first-reward-cycle: reward-cycle, + num-cycles: u1, + reward-set-indexes: (list), + stacker: none, + amount-ustx: amount-ustx, + i: u0 })) + (pox-addr-index (unwrap-panic + (element-at (get reward-set-indexes add-pox-addr-info) u0)))) + + ;; don't update the stacking-state map, + ;; because it _already has_ this stacker's state + ;; don't lock the STX, because the STX is already locked + ;; + ;; clear the partial-stacked state, and log it + (map-delete partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) + (map-set logged-partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle } partial-stacked) + (ok pox-addr-index))))) + +;; Legacy interface for stack-aggregation-commit. +;; Wraps inner-stack-aggregation-commit. See its docstring for details. +;; Returns (ok true) on success +;; Returns (err ...) on failure. +(define-public (stack-aggregation-commit (pox-addr { version: (buff 1), hashbytes: (buff 32) }) + (reward-cycle uint)) + (match (inner-stack-aggregation-commit pox-addr reward-cycle) + pox-addr-index (ok true) + commit-err (err commit-err))) + +;; Public interface to `inner-stack-aggregation-commit`. See its documentation for details. +;; *New in Stacks 2.1.* +(define-public (stack-aggregation-commit-indexed (pox-addr { version: (buff 1), hashbytes: (buff 32) }) + (reward-cycle uint)) + (inner-stack-aggregation-commit pox-addr reward-cycle)) + +;; Commit partially stacked STX to a PoX address which has already received some STX (more than the Stacking min). +;; This allows a delegator to lock up marginally more STX from new delegates, even if they collectively do not +;; exceed the Stacking minimum, so long as the target PoX address already represents at least as many STX as the +;; Stacking minimum. +;; +;; The `reward-cycle-index` is emitted as a contract event from `stack-aggregation-commit` when the initial STX are +;; locked up by this delegator. It must be passed here to add more STX behind this PoX address. If the delegator +;; called `stack-aggregation-commit` multiple times for the same PoX address, then any such `reward-cycle-index` will +;; work here. +;; +;; *New in Stacks 2.1* +;; +(define-public (stack-aggregation-increase (pox-addr { version: (buff 1), hashbytes: (buff 32) }) + (reward-cycle uint) + (reward-cycle-index uint)) + (let ((partial-stacked + ;; fetch the partial commitments + (unwrap! (map-get? partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) + (err ERR_STACKING_NO_SUCH_PRINCIPAL)))) + + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; reward-cycle must be in the future + (asserts! (> reward-cycle (current-pox-reward-cycle)) + (err ERR_STACKING_INVALID_LOCK_PERIOD)) + + (let ((amount-ustx (get stacked-amount partial-stacked)) + ;; reward-cycle must point to an existing record in reward-cycle-total-stacked + ;; infallible; getting something from partial-stacked-by-cycle succeeded so this must succeed + (existing-total (unwrap-panic (map-get? reward-cycle-total-stacked { reward-cycle: reward-cycle }))) + ;; reward-cycle and reward-cycle-index must point to an existing record in reward-cycle-pox-address-list + (existing-entry (unwrap! (map-get? reward-cycle-pox-address-list { reward-cycle: reward-cycle, index: reward-cycle-index }) + (err ERR_DELEGATION_NO_REWARD_SLOT))) + (increased-ustx (+ (get total-ustx existing-entry) amount-ustx)) + (total-ustx (+ (get total-ustx existing-total) amount-ustx))) + + ;; must be stackable + (try! (minimal-can-stack-stx pox-addr total-ustx reward-cycle u1)) + + ;; new total must exceed the stacking minimum + (asserts! (<= (get-stacking-minimum) total-ustx) + (err ERR_STACKING_THRESHOLD_NOT_MET)) + + ;; there must *not* be a stacker entry (since this is a delegator) + (asserts! (is-none (get stacker existing-entry)) + (err ERR_DELEGATION_WRONG_REWARD_SLOT)) + + ;; the given PoX address must match the one on record + (asserts! (is-eq pox-addr (get pox-addr existing-entry)) + (err ERR_DELEGATION_WRONG_REWARD_SLOT)) + + ;; update the pox-address list -- bump the total-ustx + (map-set reward-cycle-pox-address-list + { reward-cycle: reward-cycle, index: reward-cycle-index } + { pox-addr: pox-addr, + total-ustx: increased-ustx, + stacker: none }) + + ;; update the total ustx in this cycle + (map-set reward-cycle-total-stacked + { reward-cycle: reward-cycle } + { total-ustx: total-ustx }) + + ;; don't update the stacking-state map, + ;; because it _already has_ this stacker's state + ;; don't lock the STX, because the STX is already locked + ;; + ;; clear the partial-stacked state, and log it + (map-delete partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) + (map-set logged-partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle } partial-stacked) + (ok true)))) + +;; As a delegate, stack the given principal's STX using partial-stacked-by-cycle +;; Once the delegate has stacked > minimum, the delegate should call stack-aggregation-commit +(define-public (delegate-stack-stx (stacker principal) + (amount-ustx uint) + (pox-addr { version: (buff 1), hashbytes: (buff 32) }) + (start-burn-ht uint) + (lock-period uint)) + ;; this stacker's first reward cycle is the _next_ reward cycle + (let ((first-reward-cycle (+ u1 (current-pox-reward-cycle))) + (specified-reward-cycle (+ u1 (burn-height-to-reward-cycle start-burn-ht))) + (unlock-burn-height (reward-cycle-to-burn-height (+ (current-pox-reward-cycle) u1 lock-period)))) + ;; the start-burn-ht must result in the next reward cycle, do not allow stackers + ;; to "post-date" their `stack-stx` transaction + (asserts! (is-eq first-reward-cycle specified-reward-cycle) + (err ERR_INVALID_START_BURN_HEIGHT)) + + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; stacker must have delegated to the caller + (let ((delegation-info (unwrap! (get-check-delegation stacker) (err ERR_STACKING_PERMISSION_DENIED)))) + ;; must have delegated to tx-sender + (asserts! (is-eq (get delegated-to delegation-info) tx-sender) + (err ERR_STACKING_PERMISSION_DENIED)) + ;; must have delegated enough stx + (asserts! (>= (get amount-ustx delegation-info) amount-ustx) + (err ERR_DELEGATION_TOO_MUCH_LOCKED)) + ;; if pox-addr is set, must be equal to pox-addr + (asserts! (match (get pox-addr delegation-info) + specified-pox-addr (is-eq pox-addr specified-pox-addr) + true) + (err ERR_DELEGATION_POX_ADDR_REQUIRED)) + ;; delegation must not expire before lock period + (asserts! (match (get until-burn-ht delegation-info) + until-burn-ht (>= until-burn-ht + unlock-burn-height) + true) + (err ERR_DELEGATION_EXPIRES_DURING_LOCK))) + + ;; stacker principal must not be stacking + (asserts! (is-none (get-stacker-info stacker)) + (err ERR_STACKING_ALREADY_STACKED)) + + ;; the Stacker must have sufficient unlocked funds + (asserts! (>= (stx-get-balance stacker) amount-ustx) + (err ERR_STACKING_INSUFFICIENT_FUNDS)) + + ;; ensure that stacking can be performed + (try! (minimal-can-stack-stx pox-addr amount-ustx first-reward-cycle lock-period)) + + ;; register the PoX address with the amount stacked via partial stacking + ;; before it can be included in the reward set, this must be committed! + (add-pox-partial-stacked pox-addr first-reward-cycle lock-period amount-ustx) + + ;; add stacker record + (map-set stacking-state + { stacker: stacker } + { pox-addr: pox-addr, + first-reward-cycle: first-reward-cycle, + reward-set-indexes: (list), + lock-period: lock-period }) + + ;; return the lock-up information, so the node can actually carry out the lock. + (ok { stacker: stacker, + lock-amount: amount-ustx, + unlock-burn-height: unlock-burn-height }))) + +;; Reject Stacking for this reward cycle. +;; tx-sender votes all its uSTX for rejection. +;; Note that unlike PoX, rejecting PoX does not lock the tx-sender's +;; tokens. PoX rejection acts like a coin vote. +(define-public (reject-pox) + (let ( + (balance (stx-get-balance tx-sender)) + (vote-reward-cycle (+ u1 (current-pox-reward-cycle))) + ) + + ;; tx-sender principal must not have rejected in this upcoming reward cycle + (asserts! (is-none (get-pox-rejection tx-sender vote-reward-cycle)) + (err ERR_STACKING_ALREADY_REJECTED)) + + ;; tx-sender can't be a stacker + (asserts! (is-none (get-stacker-info tx-sender)) + (err ERR_STACKING_ALREADY_STACKED)) + + ;; vote for rejection + (map-set stacking-rejection + { reward-cycle: vote-reward-cycle } + { amount: (+ (next-cycle-rejection-votes) balance) } + ) + + ;; mark voted + (map-set stacking-rejectors + { stacker: tx-sender, reward-cycle: vote-reward-cycle } + { amount: balance } + ) + + (ok true)) +) + +;; Used for PoX parameters discovery +(define-read-only (get-pox-info) + (ok { + min-amount-ustx: (get-stacking-minimum), + reward-cycle-id: (current-pox-reward-cycle), + prepare-cycle-length: (var-get pox-prepare-cycle-length), + first-burnchain-block-height: (var-get first-burnchain-block-height), + reward-cycle-length: (var-get pox-reward-cycle-length), + rejection-fraction: (var-get pox-rejection-fraction), + current-rejection-votes: (next-cycle-rejection-votes), + total-liquid-supply-ustx: stx-liquid-supply, + }) +) + +;; Update the number of stacked STX in a given reward cycle entry. +;; `reward-cycle-index` is the index into the `reward-cycle-pox-address-list` map for a given reward cycle number. +;; `updates`, if `(some ..)`, encodes which PoX reward cycle entry (if any) gets updated. In particular, it must have +;; `(some stacker)` as the listed stacker, and must be an upcoming reward cycle. +(define-private (increase-reward-cycle-entry + (reward-cycle-index uint) + (updates (optional { first-cycle: uint, reward-cycle: uint, stacker: principal, add-amount: uint }))) + (let ((data (try! updates)) + (first-cycle (get first-cycle data)) + (reward-cycle (get reward-cycle data))) + (if (> first-cycle reward-cycle) + ;; not at first cycle to process yet + (some { first-cycle: first-cycle, reward-cycle: (+ u1 reward-cycle), stacker: (get stacker data), add-amount: (get add-amount data) }) + (let ((existing-entry (unwrap-panic (map-get? reward-cycle-pox-address-list { reward-cycle: reward-cycle, index: reward-cycle-index }))) + (existing-total (unwrap-panic (map-get? reward-cycle-total-stacked { reward-cycle: reward-cycle }))) + (total-ustx (+ (get total-ustx existing-total) (get add-amount data)))) + ;; stacker must match + (asserts! (is-eq (get stacker existing-entry) (some (get stacker data))) none) + ;; update the pox-address list + (map-set reward-cycle-pox-address-list + { reward-cycle: reward-cycle, index: reward-cycle-index } + { pox-addr: (get pox-addr existing-entry), + total-ustx: total-ustx, + stacker: (some (get stacker data)) }) + ;; update the total + (map-set reward-cycle-total-stacked + { reward-cycle: reward-cycle } + { total-ustx: total-ustx }) + (some { first-cycle: first-cycle, + reward-cycle: (+ u1 reward-cycle), + stacker: (get stacker data), + add-amount: (get add-amount data) }))))) + +;; Increase the number of STX locked. +;; *New in Stacks 2.1* +;; This method locks up an additional amount of STX from `tx-sender`'s, indicated +;; by `increase-by`. The `tx-sender` must already be Stacking. +(define-public (stack-increase (increase-by uint)) + (let ((stacker-info (stx-account tx-sender)) + (amount-stacked (get locked stacker-info)) + (amount-unlocked (get unlocked stacker-info)) + (unlock-height (get unlock-height stacker-info)) + (unlock-in-cycle (burn-height-to-reward-cycle unlock-height)) + (cur-cycle (current-pox-reward-cycle)) + (first-increased-cycle (+ cur-cycle u1)) + (stacker-state (unwrap! (map-get? stacking-state + { stacker: tx-sender }) + (err ERR_STACK_INCREASE_NOT_LOCKED)))) + ;; tx-sender must be currently locked + (asserts! (> amount-stacked u0) + (err ERR_STACK_INCREASE_NOT_LOCKED)) + ;; must be called with positive `increase-by` + (asserts! (>= increase-by u1) + (err ERR_STACKING_INVALID_AMOUNT)) + ;; stacker must have enough stx to lock + (asserts! (>= amount-unlocked increase-by) + (err ERR_STACKING_INSUFFICIENT_FUNDS)) + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + ;; stacker must be directly stacking + (asserts! (> (len (get reward-set-indexes stacker-state)) u0) + (err ERR_STACKING_ALREADY_DELEGATED)) + ;; update reward cycle amounts + (asserts! (is-some (fold increase-reward-cycle-entry + (get reward-set-indexes stacker-state) + (some { first-cycle: first-increased-cycle, + reward-cycle: (get first-reward-cycle stacker-state), + stacker: tx-sender, + add-amount: increase-by }))) + (err ERR_STACKING_UNREACHABLE)) + ;; NOTE: stacking-state map is unchanged: it no longer tracks amount-stacked in PoX-2 + (ok { stacker: tx-sender, total-locked: (+ amount-stacked increase-by)}))) + +;; Extend an active Stacking lock. +;; *New in Stacks 2.1* +;; This method extends the `tx-sender`'s current lockup for an additional `extend-count` +;; and associates `pox-addr` with the rewards +(define-public (stack-extend (extend-count uint) + (pox-addr { version: (buff 1), hashbytes: (buff 32) })) + (let ((stacker-info (stx-account tx-sender)) + (stacker-state (get-stacker-info tx-sender)) + (amount-ustx (get locked stacker-info)) + (unlock-height (get unlock-height stacker-info)) + (cur-cycle (current-pox-reward-cycle)) + (unlock-in-cycle (burn-height-to-reward-cycle unlock-height)) + ;; if the account unlocks *during* this cycle (should only occur during testing), + ;; set first-extend-cycle to the next cycle. + (first-extend-cycle (if (> (+ cur-cycle u1) unlock-in-cycle) + (+ cur-cycle u1) unlock-in-cycle)) + ;; maintaining valid stacking-state entries requires checking + ;; whether there is an existing entry for the stacker in the state + ;; this would be the case if the stacker is extending a lockup from PoX-1 + ;; to PoX-2 + (first-reward-cycle (match (get first-reward-cycle stacker-state) + ;; if we've stacked in PoX2, then max(cur-cycle, stacker-state.first-reward-cycle) is valid + old-first-cycle (if (> cur-cycle old-first-cycle) cur-cycle old-first-cycle) + ;; otherwise, there aren't PoX2 entries until first-extend-cycle + first-extend-cycle))) + + ;; must be called with positive extend-count + (asserts! (>= extend-count u1) + (err ERR_STACKING_INVALID_LOCK_PERIOD)) + + (let ((last-extend-cycle (- (+ first-extend-cycle extend-count) u1)) + (lock-period (+ u1 (- last-extend-cycle first-reward-cycle))) + (new-unlock-ht (reward-cycle-to-burn-height (+ u1 last-extend-cycle)))) + + ;; first cycle must be after the current cycle + (asserts! (> first-extend-cycle cur-cycle) (err ERR_STACKING_INVALID_LOCK_PERIOD)) + ;; lock period must be positive + (asserts! (> lock-period u0) (err ERR_STACKING_INVALID_LOCK_PERIOD)) + + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; tx-sender must be locked + (asserts! (> amount-ustx u0) + (err ERR_STACK_EXTEND_NOT_LOCKED)) + + ;; tx-sender must not be delegating + (asserts! (is-none (get-check-delegation tx-sender)) + (err ERR_STACKING_ALREADY_DELEGATED)) + + ;; standard can-stack-stx checks + (try! (can-stack-stx pox-addr amount-ustx first-extend-cycle lock-period)) + + ;; register the PoX address with the amount stacked + ;; for the new cycles + (let ((extended-reward-set-indexes (try! (add-pox-addr-to-reward-cycles pox-addr first-extend-cycle extend-count amount-ustx tx-sender))) + (reward-set-indexes (match stacker-state + ;; if there's active stacker state, we need to extend the existing reward-set-indexes + old-state (let ((cur-cycle-index (- first-reward-cycle (get first-reward-cycle old-state))) + (old-indexes (get reward-set-indexes old-state)) + ;; build index list by taking the old-indexes starting from cur cycle + ;; and adding the new indexes to it. this way, the index is valid starting from the current cycle + (new-list (concat (default-to (list) (slice? old-indexes cur-cycle-index (len old-indexes))) + extended-reward-set-indexes))) + (unwrap-panic (as-max-len? new-list u12))) + extended-reward-set-indexes))) + ;; update stacker record + (map-set stacking-state + { stacker: tx-sender } + { pox-addr: pox-addr, + reward-set-indexes: reward-set-indexes, + first-reward-cycle: first-reward-cycle, + lock-period: lock-period }) + + ;; return lock-up information + (ok { stacker: tx-sender, unlock-burn-height: new-unlock-ht }))))) + +;; As a delegator, increase an active Stacking lock, issuing a "partial commitment" for the +;; increased cycles. +;; *New in Stacks 2.1* +;; This method increases `stacker`'s current lockup and partially commits the additional +;; STX to `pox-addr` +(define-public (delegate-stack-increase + (stacker principal) + (pox-addr { version: (buff 1), hashbytes: (buff 32) }) + (increase-by uint)) + (let ((stacker-info (stx-account stacker)) + (existing-lock (get locked stacker-info)) + (available-stx (get unlocked stacker-info)) + (unlock-height (get unlock-height stacker-info))) + + ;; must be called with positive `increase-by` + (asserts! (>= increase-by u1) + (err ERR_STACKING_INVALID_AMOUNT)) + (print increase-by) + (let ((unlock-in-cycle (burn-height-to-reward-cycle unlock-height)) + (cur-cycle (current-pox-reward-cycle)) + (first-increase-cycle (+ cur-cycle u1)) + (last-increase-cycle (- unlock-in-cycle u1)) + (cycle-count (try! (if (<= first-increase-cycle last-increase-cycle) + (ok (+ u1 (- last-increase-cycle first-increase-cycle))) + (err ERR_STACKING_INVALID_LOCK_PERIOD)))) + (new-total-locked (+ increase-by existing-lock)) + (stacker-state + (unwrap! (map-get? stacking-state { stacker: stacker }) + (err ERR_STACK_INCREASE_NOT_LOCKED)))) + + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; stacker must be currently locked + (asserts! (> existing-lock u0) + (err ERR_STACK_INCREASE_NOT_LOCKED)) + + ;; stacker must have enough stx to lock + (asserts! (>= available-stx increase-by) + (err ERR_STACKING_INSUFFICIENT_FUNDS)) + + ;; stacker must have delegated to the caller + (let ((delegation-info (unwrap! (get-check-delegation stacker) (err ERR_STACKING_PERMISSION_DENIED))) + (delegated-to (get delegated-to delegation-info)) + (delegated-amount (get amount-ustx delegation-info)) + (delegated-pox-addr (get pox-addr delegation-info)) + (delegated-until (get until-burn-ht delegation-info))) + ;; must have delegated to tx-sender + (asserts! (is-eq delegated-to tx-sender) + (err ERR_STACKING_PERMISSION_DENIED)) + ;; must have delegated enough stx + (asserts! (>= delegated-amount new-total-locked) + (err ERR_DELEGATION_TOO_MUCH_LOCKED)) + ;; if pox-addr is set, must be equal to pox-addr + (asserts! (match delegated-pox-addr + specified-pox-addr (is-eq pox-addr specified-pox-addr) + true) + (err ERR_DELEGATION_POX_ADDR_REQUIRED)) + ;; delegation must not expire before lock period + (asserts! (match delegated-until + until-burn-ht + (>= until-burn-ht unlock-height) + true) + (err ERR_DELEGATION_EXPIRES_DURING_LOCK))) + + ;; delegate stacking does minimal-can-stack-stx + (try! (minimal-can-stack-stx pox-addr new-total-locked first-increase-cycle (+ u1 (- last-increase-cycle first-increase-cycle)))) + + ;; register the PoX address with the amount stacked via partial stacking + ;; before it can be included in the reward set, this must be committed! + (add-pox-partial-stacked pox-addr first-increase-cycle cycle-count increase-by) + + ;; stacking-state is unchanged, so no need to update + + ;; return the lock-up information, so the node can actually carry out the lock. + (ok { stacker: stacker, total-locked: new-total-locked})))) + +;; As a delegator, extend an active stacking lock, issuing a "partial commitment" for the +;; extended-to cycles. +;; *New in Stacks 2.1* +;; This method extends `stacker`'s current lockup for an additional `extend-count` +;; and partially commits those new cycles to `pox-addr` +(define-public (delegate-stack-extend + (stacker principal) + (pox-addr { version: (buff 1), hashbytes: (buff 32) }) + (extend-count uint)) + (let ((stacker-info (stx-account stacker)) + (stacker-state (get-stacker-info stacker)) + (amount-ustx (get locked stacker-info)) + (unlock-height (get unlock-height stacker-info)) + (unlock-in-cycle (burn-height-to-reward-cycle unlock-height)) + ;; if the account unlocks *during* this cycle (should only occur during testing), + ;; set first-extend-cycle to the next cycle. + (cur-cycle (current-pox-reward-cycle)) + (first-extend-cycle (if (> (+ cur-cycle u1) unlock-in-cycle) + (+ cur-cycle u1) unlock-in-cycle)) + ;; update stacker record + ;; maintaining valid stacking-state entries requires checking + ;; whether there is an existing entry for the stacker in the state + ;; this would be the case if the stacker is extending a lockup from PoX-1 + ;; to PoX-2 + (first-reward-cycle (match (get first-reward-cycle stacker-state) + ;; if stacker stacked in PoX2, then max(cur-cycle, stacker-state.first-reward-cycle) is valid + old-first-cycle (if (> cur-cycle old-first-cycle) cur-cycle old-first-cycle) + ;; otherwise, there aren't PoX2 entries until first-extend-cycle + first-extend-cycle))) + + ;; must be called with positive extend-count + (asserts! (>= extend-count u1) + (err ERR_STACKING_INVALID_LOCK_PERIOD)) + + (let ((last-extend-cycle (- (+ first-extend-cycle extend-count) u1)) + (lock-period (+ u1 (- last-extend-cycle first-reward-cycle))) + (new-unlock-ht (reward-cycle-to-burn-height (+ u1 last-extend-cycle)))) + + ;; first cycle must be after the current cycle + (asserts! (> first-extend-cycle cur-cycle) (err ERR_STACKING_INVALID_LOCK_PERIOD)) + ;; lock period must be positive + (asserts! (> lock-period u0) (err ERR_STACKING_INVALID_LOCK_PERIOD)) + + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; check valid lock period + (asserts! (check-pox-lock-period lock-period) + (err ERR_STACKING_INVALID_LOCK_PERIOD)) + + ;; stacker must be currently locked + (asserts! (> amount-ustx u0) + (err ERR_STACK_EXTEND_NOT_LOCKED)) + + ;; stacker must have delegated to the caller + (let ((delegation-info (unwrap! (get-check-delegation stacker) (err ERR_STACKING_PERMISSION_DENIED)))) + ;; must have delegated to tx-sender + (asserts! (is-eq (get delegated-to delegation-info) tx-sender) + (err ERR_STACKING_PERMISSION_DENIED)) + ;; must have delegated enough stx + (asserts! (>= (get amount-ustx delegation-info) amount-ustx) + (err ERR_DELEGATION_TOO_MUCH_LOCKED)) + ;; if pox-addr is set, must be equal to pox-addr + (asserts! (match (get pox-addr delegation-info) + specified-pox-addr (is-eq pox-addr specified-pox-addr) + true) + (err ERR_DELEGATION_POX_ADDR_REQUIRED)) + ;; delegation must not expire before lock period + (asserts! (match (get until-burn-ht delegation-info) + until-burn-ht (>= until-burn-ht + new-unlock-ht) + true) + (err ERR_DELEGATION_EXPIRES_DURING_LOCK))) + + ;; delegate stacking does minimal-can-stack-stx + (try! (minimal-can-stack-stx pox-addr amount-ustx first-extend-cycle lock-period)) + + ;; register the PoX address with the amount stacked via partial stacking + ;; before it can be included in the reward set, this must be committed! + (add-pox-partial-stacked pox-addr first-extend-cycle extend-count amount-ustx) + + (map-set stacking-state + { stacker: stacker } + { pox-addr: pox-addr, + reward-set-indexes: (list), + first-reward-cycle: first-reward-cycle, + lock-period: lock-period }) + + ;; return the lock-up information, so the node can actually carry out the lock. + (ok { stacker: stacker, + unlock-burn-height: new-unlock-ht })))) + +;; Get the _current_ PoX stacking delegation information for a stacker. If the information +;; is expired, or if there's never been such a stacker, then returns none. +;; *New in Stacks 2.1* +(define-read-only (get-delegation-info (stacker principal)) + (get-check-delegation stacker) +) + +;; Get the burn height at which a particular contract is allowed to stack for a particular principal. +;; *New in Stacks 2.1* +;; Returns (some (some X)) if X is the burn height at which the allowance terminates +;; Returns (some none) if the caller is allowed indefinitely +;; Returns none if there is no allowance record +(define-read-only (get-allowance-contract-callers (sender principal) (calling-contract principal)) + (map-get? allowance-contract-callers { sender: sender, contract-caller: calling-contract }) +) + +;; How many PoX addresses in this reward cycle? +;; *New in Stacks 2.1* +(define-read-only (get-num-reward-set-pox-addresses (reward-cycle uint)) + (match (map-get? reward-cycle-pox-address-list-len { reward-cycle: reward-cycle }) + num-addrs + (get len num-addrs) + u0 + ) +) + +;; How many uSTX have been locked up for this address so far, before the delegator commits them? +;; *New in Stacks 2.1* +(define-read-only (get-partial-stacked-by-cycle (pox-addr { version: (buff 1), hashbytes: (buff 32) }) (reward-cycle uint) (sender principal)) + (map-get? partial-stacked-by-cycle { pox-addr: pox-addr, reward-cycle: reward-cycle, sender: sender }) +) + +;; How many uSTX have voted to reject PoX in a given reward cycle? +;; *New in Stacks 2.1* +(define-read-only (get-total-pox-rejection (reward-cycle uint)) + (match (map-get? stacking-rejection { reward-cycle: reward-cycle }) + rejected + (get amount rejected) + u0 + ) +) \ No newline at end of file diff --git a/sbtc-ops/smart-contract/contracts/restricted-token-trait.clar b/sbtc-ops/smart-contract/contracts/restricted-token-trait.clar new file mode 100644 index 00000000..5001ab65 --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/restricted-token-trait.clar @@ -0,0 +1,12 @@ +(define-trait restricted-token-trait + ( + ;; Called to detect if a transfer restriction will take place. Returns the + ;; error code that explains why the transfer failed. + (detect-transfer-restriction (uint principal principal) (response uint uint)) + + ;; Returns human readable string for a specific transfer restriction error code + ;; which is returned from (detect-transfer-restriction). + ;; This is a convenience function for end user wallets. + (message-for-restriction (uint) (response (string-ascii 1024) uint)) + ) +) \ No newline at end of file diff --git a/sbtc-ops/smart-contract/contracts/stacking-pool-test.clar b/sbtc-ops/smart-contract/contracts/stacking-pool-test.clar new file mode 100644 index 00000000..a7f958e9 --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/stacking-pool-test.clar @@ -0,0 +1,741 @@ + +;; Main Stacking Pool Contract + + +;; Flow +;; 1. The liquidity provider deploys the contract +;; 2. He locks into the SC a sum which will be sufficient to cover all the stackers' rewards +;; 3. Stackers who want to stack through the stacking pool have to join the pool. +;; 4. They will have to delegate the STX they want to stack to the pool's POX address +;; 5. When the total amount commited is enough to be stacked, it will be auto committed +;; 6. The stackers will be able to claim the rewards after they are distributed + +;; + In prepare phase, calculate weight of the stackers inside the pool (Notion) + +;; Default length of the PoX registration window, in burnchain blocks. +;;TODO: pox-2 mainnet address: 'SP000000000000000000002Q6VF78 +(define-constant PREPARE_CYCLE_LENGTH (get prepare-cycle-length (unwrap-panic (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-pox-info)))) + +;; Default length of the PoX reward cycle, in burnchain blocks. +(define-constant REWARD_CYCLE_LENGTH (get reward-cycle-length (unwrap-panic (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-pox-info)))) + +;; Half cycle length is 1050 for mainnet +(define-constant half-cycle-length (/ (get reward-cycle-length (unwrap-panic (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-pox-info))) u2)) + + ;; minimum amount for the liquidity provider to transfer after deploy in microSTX (STX * 10^-6) +(define-constant minimum-deposit-amount-liquidity-provider u10000000000) + +(define-constant err-only-liquidity-provider (err u100)) +(define-constant err-already-in-pool (err u101)) +(define-constant err-not-in-pool (err u102)) +(define-constant err-liquidity-provider-not-permitted (err u103)) +(define-constant err-wrong-moment-to-update-balances (err u123)) +(define-constant err-allow-pool-in-SC-first (err u195)) +(define-constant err-allow-pool-in-pox-2-first (err u199)) +(define-constant err-insufficient-funds (err u200)) +(define-constant err-revoke-delegation-in-pox-first (err u201)) +(define-constant err-disallow-pool-in-pox-2-first (err u299)) +(define-constant err-full-stacking-pool (err u300)) +(define-constant err-same-value (err u325)) +(define-constant err-future-reward-not-covered (err u333)) +(define-constant err-not-delegated-that-amount (err u396)) +(define-constant err-no-locked-funds (err u456)) +(define-constant err-too-early (err u500)) +(define-constant err-decrease-forbidden (err u503)) +(define-constant err-no-reward-yet (err u576)) +(define-constant err-not-enough-reserved-balance (err u579)) +(define-constant err-stacking-permission-denied (err u609)) +(define-constant err-transfer-failed (err u777)) +(define-constant err-cant-calculate-weights (err u888)) +(define-constant err-already-updated-balances (err u895)) +(define-constant err-no-reward-for-this-block (err u900)) +(define-constant err-already-rewarded-block (err u992)) +(define-constant err-cant-withdraw-now (err u995)) +(define-constant err-cant-unwrap-exchange-preview (err u996)) +(define-constant err-return-div-exceeds-maximum (err u997)) +(define-constant err-pox-address-deactivated (err u999)) +(define-constant err-weights-not-calculated (err u1000)) + +(define-constant first-deposit u0) +(define-constant list-max-len u300) +(define-constant pool-contract (as-contract tx-sender)) +(define-constant pox-2-contract (as-contract 'ST000000000000000000002AMW42H.pox-3)) +(define-constant blocks-to-pass-until-reward u101) +(define-constant max-return-div-accepted u20) +(define-constant ONE-6 u1000000) +;; liquidity provider data vars +(define-data-var sc-total-balance uint u0) +(define-data-var sc-owned-balance uint u0) +(define-data-var sc-reserved-balance uint u0) + ;; (the percentage of the locked balance assured by the liquidity provider) ^ -1, + ;; return-div = u20 => the liquidity provider is ready to grant a maximum of 5% of the total locked balance. +(define-data-var return-div uint u20) +;; stackers data vars +(define-data-var sc-delegated-balance uint u0) +(define-data-var sc-locked-balance uint u0) + +;; temporary data var helpers +(define-data-var calc-delegated-balance uint u0) +(define-data-var calc-locked-balance uint u0) +(define-data-var reward-cycle-to-calculate-weight uint u0) +(define-data-var burn-block-to-distribute-rewards uint u0) +(define-data-var reward-cycle-to-distribute-rewards uint u0) +(define-data-var temp-current-reward uint u0) +;; common data vars +(define-data-var stackers-list (list 300 principal) (list tx-sender)) +(define-data-var liquidity-provider principal tx-sender) +(define-data-var active bool true) +(define-data-var blocks-rewarded uint u0) +(define-data-var amount-rewarded uint u0) + + +;; liqidity provider reward bitcoin address +(define-data-var pool-pox-address {hashbytes: (buff 32), version: (buff 1)} + { + version: 0x04, + hashbytes: 0x83ed66860315e334010bbfb76eb3eef887efee0a}) + +(define-data-var stx-buffer uint u0) ;; 0 STX + +;; data maps + +(define-map user-data { address: principal } {is-in-pool:bool, delegated-balance: uint, locked-balance:uint, until-burn-ht: (optional uint) }) +(define-map user-revoked-delegation principal bool) +(define-map pox-addr-indices uint uint) +(define-map last-aggregation uint uint) +(define-map allowance-contract-callers { sender: principal, contract-caller: principal} { until-burn-ht: (optional uint)}) +(define-map stacker-weights-per-reward-cycle { stacker: principal, reward-cycle: uint } { weight-percentage: uint }) +(define-map calculated-weights-reward-cycles { reward-cycle: uint } { calculated: bool }) +(define-map burn-block-rewards { burn-height: uint } { reward: uint }) +(define-map updated-sc-balances { reward-cycle: uint } { updated: bool, stackers-list: (list 300 principal) }) +(define-map already-rewarded { burn-block-height: uint } { value: bool }) +(allow-contract-caller (as-contract tx-sender) none) + +(map-set user-data {address: tx-sender} {is-in-pool: true, delegated-balance: u0, locked-balance: u0, until-burn-ht: none}) + +;; Public functions + +(define-public (deposit-stx-liquidity-provider (amount uint)) +(begin + (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider) + (asserts! (>= amount minimum-deposit-amount-liquidity-provider) err-future-reward-not-covered) + (try! (stx-transfer? amount tx-sender pool-contract)) + (var-set sc-total-balance (+ amount (var-get sc-total-balance))) + (var-set sc-owned-balance (+ amount (var-get sc-owned-balance))) + (ok true))) + +(define-public (withdraw-stx-liquidity-provider (amount uint)) +(begin + (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider) + (asserts! + (and + (check-can-decrement-owned-balance amount) + (check-can-decrement-total-balance amount)) + err-insufficient-funds) + (try! + (as-contract + (stx-transfer? amount tx-sender (var-get liquidity-provider)))) + (var-set sc-total-balance (- (var-get sc-total-balance) amount)) + (var-set sc-owned-balance (- (var-get sc-owned-balance) amount)) + (ok true))) + +(define-public (reserve-funds-future-rewards (amount uint)) +(begin + (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider) + (asserts! (>= (var-get sc-owned-balance) amount) err-insufficient-funds) + (asserts! (>= amount minimum-deposit-amount-liquidity-provider) err-future-reward-not-covered) + (var-set sc-owned-balance (- (var-get sc-owned-balance) amount)) + (var-set sc-reserved-balance (+ (var-get sc-reserved-balance) amount)) + (ok true))) + +(define-public (unlock-extra-reserved-funds) +(begin + (asserts! + (is-eq + contract-caller + (var-get liquidity-provider)) + err-only-liquidity-provider) + (asserts! (can-withdraw-extra-reserved-now) err-cant-withdraw-now) + (let ((unreserve-amount (calculate-extra-reserved-funds)) + (reserved-balance-before (var-get sc-reserved-balance)) + (owned-balance-before (var-get sc-owned-balance))) + (var-set sc-reserved-balance + (- + reserved-balance-before + unreserve-amount)) + (var-set sc-owned-balance + (+ + owned-balance-before + unreserve-amount)) + (ok unreserve-amount)))) + +(define-public (join-stacking-pool) +(begin + (asserts! (check-pool-SC-pox-2-allowance) err-allow-pool-in-pox-2-first) + (asserts! (is-none (map-get? user-data {address: tx-sender})) err-already-in-pool) + (var-set stackers-list (unwrap! (as-max-len? (concat (var-get stackers-list) (list tx-sender )) u300) err-full-stacking-pool)) + (map-set user-data {address: tx-sender} {is-in-pool: true, delegated-balance: u0, locked-balance: u0, until-burn-ht: none}) + (ok true))) + +(define-public (allow-contract-caller (caller principal) (until-burn-ht (optional uint))) +(begin + (asserts! (is-eq tx-sender contract-caller) err-stacking-permission-denied) + (ok (map-set allowance-contract-callers + { sender: tx-sender, contract-caller: caller} + { until-burn-ht: until-burn-ht})))) + +;; revoke contract-caller authorization to call stacking methods +(define-public (disallow-contract-caller (caller principal)) +(begin + (asserts! (is-eq tx-sender contract-caller) err-stacking-permission-denied) + (ok (map-delete allowance-contract-callers { sender: tx-sender, contract-caller: caller})))) + +(define-public (quit-stacking-pool) +(begin + (asserts! (is-none (get-check-delegation tx-sender)) err-revoke-delegation-in-pox-first) + (asserts! (not (check-pool-SC-pox-2-allowance)) err-disallow-pool-in-pox-2-first) + (asserts! (is-some (map-get? user-data {address: tx-sender})) err-not-in-pool) + (asserts! (not (is-eq contract-caller (var-get liquidity-provider))) err-liquidity-provider-not-permitted) + (try! (disallow-contract-caller pool-contract)) + (var-set stackers-list (filter remove-stacker-stackers-list (var-get stackers-list))) + (map-delete user-data {address: tx-sender}) + (ok true))) + +;; The SC balances need to be updated during the first half of every Prepare Phase +;; Everyone can call the function in order to recalculate each stacker's weight inside the pool +;; This WILL directly AFFECT the reward distribution +(define-public (update-sc-balances) +(let ( + (next-reward-cycle (get-next-reward-cycle)) + (next-reward-cycle-first-block (contract-call? 'ST000000000000000000002AMW42H.pox-3 reward-cycle-to-burn-height (get-next-reward-cycle)))) +(begin + ;; check current block to be inside the first half of the current reward cycle's prepare phase + (asserts! + (<= + burn-block-height + (+ + (- + next-reward-cycle-first-block + PREPARE_CYCLE_LENGTH) + (/ PREPARE_CYCLE_LENGTH u2))) + err-wrong-moment-to-update-balances) + (asserts! (is-none (map-get? updated-sc-balances {reward-cycle: next-reward-cycle})) err-already-updated-balances) + (var-set calc-locked-balance u0) + (var-set calc-delegated-balance u0) + (map update-sc-balances-one-stacker (var-get stackers-list)) + (var-set sc-locked-balance (var-get calc-locked-balance)) + (var-set sc-delegated-balance (var-get calc-delegated-balance)) + (map-set updated-sc-balances {reward-cycle: next-reward-cycle} {updated: true, stackers-list: (var-get stackers-list)}) + (var-set reward-cycle-to-calculate-weight next-reward-cycle) + (unwrap! (calculate-all-stackers-weights (var-get stackers-list) next-reward-cycle) err-cant-calculate-weights) + (ok true)))) + +;; recalculate balances inside pool +(define-public (update-sc-balances-one-stacker (stacker principal)) +(let ((user-until-burn-ht (default-to u0 (default-to (some u0) (get until-burn-ht (map-get? user-data {address: stacker}))))) + (user-delegated-balance (default-to u0 (get delegated-balance (map-get? user-data {address: stacker})))) + (user-locked-balance (default-to u0 (get locked-balance (map-get? user-data {address: stacker}))))) + (ok + ;; if burn-block-height < user's unlock burn block height, then user's balances + (and (< + burn-block-height + user-until-burn-ht) + (begin + (var-set calc-locked-balance + (+ + (var-get calc-locked-balance) + user-locked-balance)) + (var-set calc-delegated-balance + (+ + (var-get calc-delegated-balance) + user-delegated-balance))))))) + +;; The rewards will be distributed. At that moment, the SC balance should have been updated and the stackers' weights calculated +(define-public (reward-distribution (rewarded-burn-block uint)) +(let ((reward-cycle + (contract-call? 'ST000000000000000000002AMW42H.pox-3 burn-height-to-reward-cycle rewarded-burn-block)) + (stackers-list-for-reward-cycle + (default-to (list ) (get stackers-list (map-get? updated-sc-balances {reward-cycle: reward-cycle}))))) + (asserts! (< rewarded-burn-block burn-block-height) err-no-reward-yet) + (asserts! (check-won-block-rewards rewarded-burn-block) err-no-reward-for-this-block) + (asserts! (is-none (map-get? already-rewarded {burn-block-height: rewarded-burn-block})) err-already-rewarded-block) + (var-set amount-rewarded (+ (var-get amount-rewarded) (default-to u0 (get reward (map-get? burn-block-rewards { burn-height: rewarded-burn-block}))))) + (var-set blocks-rewarded (+ (var-get blocks-rewarded) u1)) + (map-set already-rewarded {burn-block-height: rewarded-burn-block} {value: true}) + (var-set reward-cycle-to-distribute-rewards reward-cycle) + (match (map-get? calculated-weights-reward-cycles {reward-cycle: reward-cycle}) + calculated (ok + (transfer-rewards-all-stackers stackers-list-for-reward-cycle)) + err-weights-not-calculated))) + +;; delegating stx to the pool SC +(define-public (delegate-stx (amount-ustx uint)) +(let ((user tx-sender) + (current-cycle (contract-call? 'ST000000000000000000002AMW42H.pox-3 current-pox-reward-cycle))) + (asserts! (check-caller-allowed) err-stacking-permission-denied) + (asserts! (check-pool-SC-pox-2-allowance) err-allow-pool-in-pox-2-first) + + (asserts! (is-in-pool) err-not-in-pool) + (try! (delegate-stx-inner amount-ustx (as-contract tx-sender) none)) + (try! (as-contract (lock-delegated-stx user))) + (ok (maybe-stack-aggregation-commit current-cycle)))) + +;; Stacks the delegated amount for the given user for the next cycle. +;; This function can be called by automation, friends or family for user that have delegated once. +;; This function can be called only after the current cycle is half through +(define-public (delegate-stack-stx (user principal)) + (let ((current-cycle (contract-call? 'ST000000000000000000002AMW42H.pox-3 current-pox-reward-cycle))) + (asserts! (can-lock-now current-cycle) err-too-early) + ;; Do 3. + (try! (as-contract (lock-delegated-stx user))) + ;; Do 4. + (ok (maybe-stack-aggregation-commit current-cycle)))) + +(define-public (delegate-stack-stx-many (stackers-lock-list (list 100 principal))) +(ok (map delegate-stack-stx stackers-lock-list))) + +(define-public (multiple-blocks-check-won-rewards (burn-heights-list (list 100 uint))) +(ok (map check-won-block-rewards burn-heights-list))) + +;; liquidity provider pool management functions + +(define-public (set-pool-pox-address (new-pool-pox-address {hashbytes: (buff 32), version: (buff 1)})) +(begin + (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider) + (ok (var-set pool-pox-address new-pool-pox-address)))) + +(define-public (set-active (is-active bool)) +(begin + (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider) + (ok (var-set active is-active)))) + +(define-public (set-liquidity-provider (new-liquidity-provider principal)) +(begin + (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider) + (asserts! (is-some (map-get? user-data {address: new-liquidity-provider})) err-not-in-pool) ;; new liquidity provider should be in pool + (ok (var-set liquidity-provider new-liquidity-provider)))) + +(define-public (update-return (new-return-value uint)) +(begin + (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider) + (asserts! (<= new-return-value max-return-div-accepted) err-return-div-exceeds-maximum) + (asserts! (not (is-eq new-return-value (var-get return-div))) err-same-value) + (var-set return-div new-return-value) + (ok new-return-value))) + +;; Private functions + +;; Pox operative functions + +(define-private (maybe-stack-aggregation-commit (current-cycle uint)) +(let ((reward-cycle (+ u1 current-cycle))) + (match (map-get? pox-addr-indices reward-cycle) + ;; Total stacked already reached minimum. + ;; Call stack-aggregate-increase. + ;; It might fail because called in the same cycle twice. + index (match (as-contract (contract-call? 'ST000000000000000000002AMW42H.pox-3 stack-aggregation-increase (var-get pool-pox-address) reward-cycle index)) + success (map-set last-aggregation reward-cycle block-height) + error (begin (print {err-increase-ignored: error}) false)) + ;; Total stacked is still below minimum. + ;; Just try to commit, it might fail because minimum not yet met + (match (as-contract (contract-call? 'ST000000000000000000002AMW42H.pox-3 stack-aggregation-commit-indexed (var-get pool-pox-address) reward-cycle)) + index (begin + (map-set pox-addr-indices reward-cycle index) + (map-set last-aggregation reward-cycle block-height)) + error (begin + (print {err-commit-ignored: error}) false))))) ;; ignore errors + +(define-private (delegate-stx-inner (amount-ustx uint) (delegate-to principal) (until-burn-ht (optional uint))) +(let ((result-revoke + ;; Calls revoke and ignores result + (contract-call? 'ST000000000000000000002AMW42H.pox-3 revoke-delegate-stx)) + (user-delegated-balance + (default-to u0 (get delegated-balance (map-get? user-data {address: tx-sender}))))) + (if + (is-ok result-revoke) + (if + (unwrap-panic result-revoke) + (begin + (asserts! + (check-can-decrement-delegated-balance + user-delegated-balance) + err-not-delegated-that-amount) + (decrement-sc-delegated-balance user-delegated-balance)) + (decrement-sc-delegated-balance u0)) + (decrement-sc-delegated-balance u0)) + ;; Calls delegate-stx, converts any error to uint + (match (contract-call? 'ST000000000000000000002AMW42H.pox-3 delegate-stx amount-ustx delegate-to until-burn-ht none) + success (begin + (increment-sc-delegated-balance amount-ustx) + (map-set + user-data + {address: tx-sender} + { + is-in-pool: (default-to false (get is-in-pool (map-get? user-data {address: tx-sender}))), + delegated-balance: amount-ustx, + locked-balance: (default-to u0 (get locked-balance (map-get? user-data {address: tx-sender}))), + until-burn-ht: until-burn-ht}) + (print "sc delegated balance") + (print (var-get sc-delegated-balance)) + (ok success)) + error (err (* u1000 (to-uint error)))))) + +(define-private (lock-delegated-stx (user principal)) +(let ((start-burn-ht (+ burn-block-height u1)) + (pox-address (var-get pool-pox-address)) + ;; changed buffer-amount (commission) to 0, kept structure + (buffer-amount u0) + (user-account (stx-account user)) + (allowed-amount (min (get-delegated-amount user) (+ (get locked user-account) (get unlocked user-account)))) + (amount-ustx (if (> allowed-amount buffer-amount) (- allowed-amount buffer-amount) allowed-amount))) + (asserts! (var-get active) err-pox-address-deactivated) + (match (contract-call? 'ST000000000000000000002AMW42H.pox-3 delegate-stack-stx + user amount-ustx + pox-address start-burn-ht u1) + stacker-details + (begin + (map-set + user-data + {address: user} + { + is-in-pool: + (default-to false (get is-in-pool (map-get? user-data {address: user}))), + delegated-balance: + (default-to u0 (get delegated-balance (map-get? user-data {address: user}))), + locked-balance: (get lock-amount stacker-details), + until-burn-ht: + (some (get unlock-burn-height stacker-details))}) + (increment-sc-locked-balance (get lock-amount stacker-details)) + (ok stacker-details)) + + error (if (is-eq error 3) ;; check whether user is already stacked + (delegate-stack-extend-increase user amount-ustx pox-address start-burn-ht) + (err (* u1000 (to-uint error))))))) + +(define-private (delegate-stack-extend-increase (user principal) + (amount-ustx uint) + (pox-address {hashbytes: (buff 32), version: (buff 1)}) + (start-burn-ht uint)) +(let ((status (stx-account user))) + (asserts! (>= amount-ustx (get locked status)) err-decrease-forbidden) + (match (contract-call? 'ST000000000000000000002AMW42H.pox-3 delegate-stack-extend + user pox-address u1) + success (begin + (print "success") + (print success) + (map-set user-data + {address: user} + { + is-in-pool: + (default-to false (get is-in-pool (map-get? user-data {address: user}))), + delegated-balance: + (default-to u0 (get delegated-balance (map-get? user-data {address: user}))), + locked-balance: + (default-to u0 (get locked-balance (map-get? user-data {address: user}))), + until-burn-ht: + (some (+ (default-to u0 (default-to (some u0) (get until-burn-ht (map-get? user-data {address: user})))) REWARD_CYCLE_LENGTH)) + }) + (if (> amount-ustx (get locked status)) + (match (contract-call? 'ST000000000000000000002AMW42H.pox-3 delegate-stack-increase + user + pox-address + (- + amount-ustx + (default-to u0 (get locked-balance (map-get? user-data {address: user}))))) + success-increase (begin + (print "success-increase") + (print success-increase) + (map-set user-data + {address: user} + { + is-in-pool: + (default-to false (get is-in-pool (map-get? user-data {address: user}))), + delegated-balance: + (default-to u0 (get delegated-balance (map-get? user-data {address: user}))), + locked-balance: (get total-locked success-increase), + until-burn-ht: + (default-to none (get until-burn-ht (map-get? user-data {address: user}))) + }) + (increment-sc-locked-balance + (- amount-ustx + (default-to u0 (get locked-balance (map-get? user-data {address: user}))))) + (ok {lock-amount: (get total-locked success-increase), + stacker: user, + unlock-burn-height: (get unlock-burn-height success)})) + error-increase (begin (print "error-increase") (err (* u1000000000 (to-uint error-increase))))) + (ok { + lock-amount: (get locked status), + stacker: user, + unlock-burn-height: (get unlock-burn-height success)}))) + error (err (* u1000000 (to-uint error)))))) + +;; Rewards transferring functions + +(define-private (transfer-rewards-all-stackers (stackers-list-before-cycle (list 300 principal))) +(map transfer-reward-one-stacker stackers-list-before-cycle)) + +(define-private (transfer-reward-one-stacker (stacker principal)) +(let ( + ;; hardcoded reward for testing + (reward + (* u426 + (default-to u0 + (get reward + (map-get? burn-block-rewards { burn-height: (var-get burn-block-to-distribute-rewards)}))))) + (stacker-weight + (default-to u0 + (get weight-percentage + (map-get? stacker-weights-per-reward-cycle {stacker: stacker, reward-cycle: (var-get reward-cycle-to-distribute-rewards)})))) + (stacker-reward (/ (* stacker-weight reward) ONE-6))) + (if (> stacker-weight u0) + + (match (as-contract (stx-transfer? stacker-reward tx-sender stacker)) + success + (begin + (if + (not (check-can-decrement-reserved-balance stacker-reward)) + (decrement-sc-owned-balance stacker-reward) + (decrement-sc-reserved-balance stacker-reward)) + (ok true)) + error (err error)) + (ok false)))) + + +(define-private (preview-exchange-reward (sats-amount uint) (slippeage uint)) +(contract-call? .bridge-contract swap-preview .token-wbtc .token-wstx sats-amount slippeage)) + +;; Weight calculation functions + +;; calculating one stacker's weight inside pool based on his balances +(define-private (weight-calculator (stacker-locked uint) (total-locked uint) (liquidity-provider-locked uint)) +(begin + (asserts! (> (+ total-locked liquidity-provider-locked) u0) err-no-locked-funds) + (ok (/ (* stacker-locked ONE-6) (+ total-locked liquidity-provider-locked))))) + +(define-private (calculate-all-stackers-weights (stackers-list-before-cycle (list 300 principal)) (next-reward-cycle uint)) +(begin + (map calculate-one-stacker-weight stackers-list-before-cycle) + (map-set calculated-weights-reward-cycles {reward-cycle: next-reward-cycle} {calculated: true}) + (ok true))) + +;; each stacker will have a weight inside the pool which will be used when distributing rewards +(define-private (calculate-one-stacker-weight (stacker principal)) +(let ((last-burn-block-before-reward-cycle + (- + (contract-call? 'ST000000000000000000002AMW42H.pox-3 reward-cycle-to-burn-height (var-get reward-cycle-to-calculate-weight)) + u1)) + ;; total locked by pool + (total-locked-at-reward-cycle + (var-get sc-locked-balance)) + ;; total reserved by liquidity provider + (liquidity-provider-reserved-at-reward-cycle + (var-get sc-reserved-balance)) + ;; total locked by a stacker + (stacker-locked-at-reward-cycle + (default-to u0 (get locked-balance (map-get? user-data {address: stacker})))) + (liquidity-provider-contribution (+ liquidity-provider-reserved-at-reward-cycle stacker-locked-at-reward-cycle)) + ;; the weight calculator result for the given stacker + (weight-calculator-result + (if + (is-eq stacker (var-get liquidity-provider)) + (weight-calculator + liquidity-provider-contribution + total-locked-at-reward-cycle + liquidity-provider-reserved-at-reward-cycle) + (weight-calculator + stacker-locked-at-reward-cycle + total-locked-at-reward-cycle + liquidity-provider-reserved-at-reward-cycle)))) + ;; register the stacker's weight for a given reward cycle using a map + (map-set stacker-weights-per-reward-cycle + {stacker: stacker, reward-cycle: (var-get reward-cycle-to-calculate-weight)} + {weight-percentage: + (if + (not (is-err weight-calculator-result)) + (unwrap-panic weight-calculator-result) + u0) + }))) + + +;; check if pool pox address has won the rewards for a given burn height and store the reward if true +(define-private (check-won-block-rewards (burn-height uint)) +(let ((reward-pox-addr-list (default-to (list ) (get addrs (get-burn-block-info? pox-addrs burn-height))))) + (if + (is-some + (index-of? reward-pox-addr-list (var-get pool-pox-address))) + (begin + (register-block-reward burn-height) + true) + false))) + +;; store the reward for a given block using a map +(define-private (register-block-reward (burn-height uint)) +(map-set burn-block-rewards {burn-height: burn-height} {reward: (default-to u0 (get payout (get-burn-block-info? pox-addrs burn-height)))})) + +(define-private (remove-stacker-stackers-list (address principal)) (not (is-eq tx-sender address))) + +(define-private (increment-sc-delegated-balance (amount-ustx uint)) +(var-set sc-delegated-balance (+ (var-get sc-delegated-balance) amount-ustx))) + +(define-private (increment-sc-locked-balance (amount-ustx uint)) +(var-set sc-locked-balance (+ (var-get sc-locked-balance) amount-ustx))) + +(define-private (decrement-sc-delegated-balance (amount-ustx uint)) +(var-set sc-delegated-balance (- (var-get sc-delegated-balance) amount-ustx))) + +(define-private (decrement-sc-locked-balance (amount-ustx uint)) +(var-set sc-locked-balance (- (var-get sc-locked-balance) amount-ustx))) + +(define-private (decrement-sc-reserved-balance (amount-ustx uint)) +(var-set sc-reserved-balance (- (var-get sc-reserved-balance) amount-ustx))) + +(define-private (decrement-sc-owned-balance (amount-ustx uint)) +(var-set sc-owned-balance (- (var-get sc-owned-balance) amount-ustx))) + +(define-private (check-can-decrement-delegated-balance (amount-ustx uint)) +(not + (< + (var-get sc-delegated-balance) + amount-ustx))) + +(define-private (check-can-decrement-locked-balance (amount-ustx uint)) +(not + (< + (var-get sc-locked-balance) + amount-ustx))) + +(define-private (check-can-decrement-reserved-balance (amount-ustx uint)) +(not + (< + (var-get sc-reserved-balance) + amount-ustx))) + +(define-private (check-can-decrement-total-balance (amount-ustx uint)) +(not + (< + (var-get sc-total-balance) + amount-ustx))) + +(define-private (check-can-decrement-owned-balance (amount-ustx uint)) +(not + (< + (var-get sc-owned-balance) + amount-ustx))) + +(define-private (min (amount-1 uint) (amount-2 uint)) + (if (< amount-1 amount-2) + amount-1 + amount-2)) + +(define-private (get-next-reward-cycle) +(+ (contract-call? 'ST000000000000000000002AMW42H.pox-3 burn-height-to-reward-cycle burn-block-height) u1)) + +;; Read-only helper functions + +(define-read-only (get-stx-account) +(stx-account tx-sender)) + +(define-read-only (get-pool-members) +(var-get stackers-list)) + +(define-read-only (check-caller-allowed) + (or (is-eq tx-sender contract-caller) + (let ((caller-allowed + ;; if not in the caller map, return false + (unwrap! + (map-get? allowance-contract-callers + { sender: tx-sender, contract-caller: contract-caller}) + false)) + (expires-at + ;; if until-burn-ht not set, then return true (because no expiry) + (unwrap! (get until-burn-ht caller-allowed) true))) + ;; is the caller allowance still valid + (< burn-block-height expires-at)))) + +(define-read-only (is-in-pool) +(default-to false (get is-in-pool (map-get? user-data {address: tx-sender})))) + +(define-read-only (get-stacker-weight (stacker principal) (reward-cycle uint)) +(get weight-percentage (map-get? stacker-weights-per-reward-cycle {stacker: stacker, reward-cycle: reward-cycle}))) + +(define-read-only (get-SC-total-balance) +(var-get sc-total-balance)) + +(define-read-only (get-SC-owned-balance) +(var-get sc-owned-balance)) + +(define-read-only (get-SC-locked-balance) +(var-get sc-locked-balance)) + +(define-read-only (get-SC-reserved-balance) +(var-get sc-reserved-balance)) + +(define-read-only (get-user-data (user principal)) +(map-get? user-data {address: user})) + +(define-read-only (check-pool-SC-pox-2-allowance) +(is-some (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-allowance-contract-callers tx-sender pool-contract))) + +(define-read-only (get-check-delegation (stacker principal)) +(contract-call? 'ST000000000000000000002AMW42H.pox-3 get-check-delegation stacker)) + +(define-read-only (get-pox-addr-indices (reward-cycle uint)) +(map-get? pox-addr-indices reward-cycle)) + +(define-read-only (get-block-rewards (burn-height uint)) +(ok (get-burn-block-info? pox-addrs burn-height))) + +(define-read-only (can-lock-now (cycle uint)) +(> burn-block-height (+ (contract-call? 'ST000000000000000000002AMW42H.pox-3 reward-cycle-to-burn-height cycle) half-cycle-length))) + +(define-read-only (get-delegated-amount (user principal)) +(default-to u0 (get amount-ustx (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-delegation-info user)))) + +(define-read-only (get-liquidity-provider) +(var-get liquidity-provider)) + +(define-read-only (get-amount-rewarded) +(var-get amount-rewarded)) + +(define-read-only (get-blocks-rewarded) +(var-get blocks-rewarded)) + +(define-read-only (get-stacked-this-cycle) +(var-get sc-locked-balance)) + +(define-private (check-is-liquidity-provider (address principal)) +(is-eq address (var-get liquidity-provider))) + +(define-private (check-is-stacker (address principal)) +(default-to false (get is-in-pool (map-get? user-data {address: address})))) + +(define-read-only (get-address-status (address principal)) +(if (check-is-liquidity-provider address) + (ok "is-provider") + (if (check-is-stacker address) + (ok "is-stacker") + (ok "is-none")))) + +(define-read-only (calculate-extra-reserved-funds) +;; subtract the potential return from the total reserved balance and get the extra reserved balance +(- + (var-get sc-reserved-balance) + (/ + (var-get sc-locked-balance) + (var-get return-div)))) + +(define-read-only (can-withdraw-extra-reserved-now) +;; liquidity provider can only withdraw extra reserved balance within the last 10 blocks of a reward cycle +(let ((mod-burn-height (mod burn-block-height REWARD_CYCLE_LENGTH)) + (start-value (- REWARD_CYCLE_LENGTH u10)) + (end-value (- REWARD_CYCLE_LENGTH u1))) + (and (>= mod-burn-height start-value) (<= mod-burn-height end-value)))) + +(define-read-only (get-return) +(var-get return-div)) + +(define-read-only (get-minimum-deposit-liquidity-provider) +minimum-deposit-amount-liquidity-provider) + +(define-read-only (was-block-claimed (rewarded-burn-block uint)) +(map-get? already-rewarded {burn-block-height: rewarded-burn-block})) diff --git a/sbtc-ops/smart-contract/contracts/stacking-pool.clar b/sbtc-ops/smart-contract/contracts/stacking-pool.clar new file mode 100644 index 00000000..837df282 --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/stacking-pool.clar @@ -0,0 +1,744 @@ + +;; Main Stacking Pool Contract + + +;; Flow +;; 1. The liquidity provider deploys the contract +;; 2. He locks into the SC a sum which will be sufficient to cover all the stackers' rewards +;; 3. Stackers who want to stack through the stacking pool have to join the pool. +;; 4. They will have to delegate the STX they want to stack to the pool's POX address +;; 5. When the total amount commited is enough to be stacked, it will be auto committed +;; 6. The stackers will be able to claim the rewards after they are distributed + +;; + In prepare phase, calculate weight of the stackers inside the pool (Notion) + +;; Default length of the PoX registration window, in burnchain blocks. +;;TODO: pox-2 mainnet address: 'SP000000000000000000002Q6VF78 +(define-constant PREPARE_CYCLE_LENGTH (get prepare-cycle-length (unwrap-panic (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-pox-info)))) + +;; Default length of the PoX reward cycle, in burnchain blocks. +(define-constant REWARD_CYCLE_LENGTH (get reward-cycle-length (unwrap-panic (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-pox-info)))) + +;; Half cycle length is 1050 for mainnet +(define-constant half-cycle-length (/ (get reward-cycle-length (unwrap-panic (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-pox-info))) u2)) + + ;; minimum amount for the liquidity provider to transfer after deploy in microSTX (STX * 10^-6) +(define-constant minimum-deposit-amount-liquidity-provider u10000000000) + +(define-constant err-only-liquidity-provider (err u100)) +(define-constant err-already-in-pool (err u101)) +(define-constant err-not-in-pool (err u102)) +(define-constant err-liquidity-provider-not-permitted (err u103)) +(define-constant err-wrong-moment-to-update-balances (err u123)) +(define-constant err-allow-pool-in-SC-first (err u195)) +(define-constant err-allow-pool-in-pox-2-first (err u199)) +(define-constant err-insufficient-funds (err u200)) +(define-constant err-revoke-delegation-in-pox-first (err u201)) +(define-constant err-disallow-pool-in-pox-2-first (err u299)) +(define-constant err-full-stacking-pool (err u300)) +(define-constant err-same-value (err u325)) +(define-constant err-future-reward-not-covered (err u333)) +(define-constant err-not-delegated-that-amount (err u396)) +(define-constant err-no-locked-funds (err u456)) +(define-constant err-too-early (err u500)) +(define-constant err-decrease-forbidden (err u503)) +(define-constant err-no-reward-yet (err u576)) +(define-constant err-not-enough-reserved-balance (err u579)) +(define-constant err-stacking-permission-denied (err u609)) +(define-constant err-transfer-failed (err u777)) +(define-constant err-cant-calculate-weights (err u888)) +(define-constant err-already-updated-balances (err u895)) +(define-constant err-no-reward-for-this-block (err u900)) +(define-constant err-already-rewarded-block (err u992)) +(define-constant err-cant-withdraw-now (err u995)) +(define-constant err-cant-unwrap-exchange-preview (err u996)) +(define-constant err-return-div-exceeds-maximum (err u997)) +(define-constant err-pox-address-deactivated (err u999)) +(define-constant err-weights-not-calculated (err u1000)) + +(define-constant first-deposit u0) +(define-constant list-max-len u300) +(define-constant pool-contract (as-contract tx-sender)) +(define-constant pox-2-contract (as-contract 'ST000000000000000000002AMW42H.pox-3)) +(define-constant blocks-to-pass-until-reward u101) +(define-constant max-return-div-accepted u20) +(define-constant ONE-6 u1000000) +;; liquidity provider data vars +(define-data-var sc-total-balance uint u0) +(define-data-var sc-owned-balance uint u0) +(define-data-var sc-reserved-balance uint u0) + ;; (the percentage of the locked balance assured by the liquidity provider) ^ -1, + ;; return-div = u20 => the liquidity provider is ready to grant a maximum of 5% of the total locked balance. +(define-data-var return-div uint u20) +;; stackers data vars +(define-data-var sc-delegated-balance uint u0) +(define-data-var sc-locked-balance uint u0) + +;; temporary data var helpers +(define-data-var calc-delegated-balance uint u0) +(define-data-var calc-locked-balance uint u0) +(define-data-var reward-cycle-to-calculate-weight uint u0) +(define-data-var burn-block-to-distribute-rewards uint u0) +(define-data-var reward-cycle-to-distribute-rewards uint u0) +(define-data-var temp-current-reward uint u0) +;; common data vars +(define-data-var stackers-list (list 300 principal) (list tx-sender)) +(define-data-var liquidity-provider principal tx-sender) +(define-data-var active bool true) +(define-data-var blocks-rewarded uint u0) +(define-data-var amount-rewarded uint u0) + + +;; liqidity provider reward bitcoin address +(define-data-var pool-pox-address {hashbytes: (buff 32), version: (buff 1)} + { + version: 0x04, + hashbytes: 0x83ed66860315e334010bbfb76eb3eef887efee0a}) + +(define-data-var stx-buffer uint u0) ;; 0 STX + +;; data maps + +(define-map user-data { address: principal } {is-in-pool:bool, delegated-balance: uint, locked-balance:uint, until-burn-ht: (optional uint) }) +(define-map user-revoked-delegation principal bool) +(define-map pox-addr-indices uint uint) +(define-map last-aggregation uint uint) +(define-map allowance-contract-callers { sender: principal, contract-caller: principal} { until-burn-ht: (optional uint)}) +(define-map stacker-weights-per-reward-cycle { stacker: principal, reward-cycle: uint } { weight-percentage: uint }) +(define-map calculated-weights-reward-cycles { reward-cycle: uint } { calculated: bool }) +(define-map burn-block-rewards { burn-height: uint } { reward: uint }) +(define-map updated-sc-balances { reward-cycle: uint } { updated: bool, stackers-list: (list 300 principal) }) +(define-map already-rewarded { burn-block-height: uint } { value: bool }) +(allow-contract-caller (as-contract tx-sender) none) + +(map-set user-data {address: tx-sender} {is-in-pool: true, delegated-balance: u0, locked-balance: u0, until-burn-ht: none}) + +;; Public functions + +(define-public (deposit-stx-liquidity-provider (amount uint)) +(begin + (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider) + (asserts! (>= amount minimum-deposit-amount-liquidity-provider) err-future-reward-not-covered) + (try! (stx-transfer? amount tx-sender pool-contract)) + (var-set sc-total-balance (+ amount (var-get sc-total-balance))) + (var-set sc-owned-balance (+ amount (var-get sc-owned-balance))) + (ok true))) + +(define-public (withdraw-stx-liquidity-provider (amount uint)) +(begin + (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider) + (asserts! + (and + (check-can-decrement-owned-balance amount) + (check-can-decrement-total-balance amount)) + err-insufficient-funds) + (try! + (as-contract + (stx-transfer? amount tx-sender (var-get liquidity-provider)))) + (var-set sc-total-balance (- (var-get sc-total-balance) amount)) + (var-set sc-owned-balance (- (var-get sc-owned-balance) amount)) + (ok true))) + +(define-public (reserve-funds-future-rewards (amount uint)) +(begin + (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider) + (asserts! (>= (var-get sc-owned-balance) amount) err-insufficient-funds) + (asserts! (>= amount minimum-deposit-amount-liquidity-provider) err-future-reward-not-covered) + (var-set sc-owned-balance (- (var-get sc-owned-balance) amount)) + (var-set sc-reserved-balance (+ (var-get sc-reserved-balance) amount)) + (ok true))) + +(define-public (unlock-extra-reserved-funds) +(begin + (asserts! + (is-eq + contract-caller + (var-get liquidity-provider)) + err-only-liquidity-provider) + (asserts! (can-withdraw-extra-reserved-now) err-cant-withdraw-now) + (let ((unreserve-amount (calculate-extra-reserved-funds)) + (reserved-balance-before (var-get sc-reserved-balance)) + (owned-balance-before (var-get sc-owned-balance))) + (var-set sc-reserved-balance + (- + reserved-balance-before + unreserve-amount)) + (var-set sc-owned-balance + (+ + owned-balance-before + unreserve-amount)) + (ok unreserve-amount)))) + +(define-public (join-stacking-pool) +(begin + (asserts! (check-pool-SC-pox-2-allowance) err-allow-pool-in-pox-2-first) + (asserts! (is-none (map-get? user-data {address: tx-sender})) err-already-in-pool) + (var-set stackers-list (unwrap! (as-max-len? (concat (var-get stackers-list) (list tx-sender )) u300) err-full-stacking-pool)) + (map-set user-data {address: tx-sender} {is-in-pool: true, delegated-balance: u0, locked-balance: u0, until-burn-ht: none}) + (ok true))) + +(define-public (allow-contract-caller (caller principal) (until-burn-ht (optional uint))) +(begin + (asserts! (is-eq tx-sender contract-caller) err-stacking-permission-denied) + (ok (map-set allowance-contract-callers + { sender: tx-sender, contract-caller: caller} + { until-burn-ht: until-burn-ht})))) + +;; revoke contract-caller authorization to call stacking methods +(define-public (disallow-contract-caller (caller principal)) +(begin + (asserts! (is-eq tx-sender contract-caller) err-stacking-permission-denied) + (ok (map-delete allowance-contract-callers { sender: tx-sender, contract-caller: caller})))) + +(define-public (quit-stacking-pool) +(begin + (asserts! (is-none (get-check-delegation tx-sender)) err-revoke-delegation-in-pox-first) + (asserts! (not (check-pool-SC-pox-2-allowance)) err-disallow-pool-in-pox-2-first) + (asserts! (is-some (map-get? user-data {address: tx-sender})) err-not-in-pool) + (asserts! (not (is-eq contract-caller (var-get liquidity-provider))) err-liquidity-provider-not-permitted) + (try! (disallow-contract-caller pool-contract)) + (var-set stackers-list (filter remove-stacker-stackers-list (var-get stackers-list))) + (map-delete user-data {address: tx-sender}) + (ok true))) + +;; The SC balances need to be updated during the first half of every Prepare Phase +;; Everyone can call the function in order to recalculate each stacker's weight inside the pool +;; This WILL directly AFFECT the reward distribution +(define-public (update-sc-balances) +(let ( + (next-reward-cycle (get-next-reward-cycle)) + (next-reward-cycle-first-block (contract-call? 'ST000000000000000000002AMW42H.pox-3 reward-cycle-to-burn-height (get-next-reward-cycle)))) +(begin + ;; check current block to be inside the first half of the current reward cycle's prepare phase + (asserts! + (<= + burn-block-height + (+ + (- + next-reward-cycle-first-block + PREPARE_CYCLE_LENGTH) + (/ PREPARE_CYCLE_LENGTH u2))) + err-wrong-moment-to-update-balances) + (asserts! (is-none (map-get? updated-sc-balances {reward-cycle: next-reward-cycle})) err-already-updated-balances) + (var-set calc-locked-balance u0) + (var-set calc-delegated-balance u0) + (map update-sc-balances-one-stacker (var-get stackers-list)) + (var-set sc-locked-balance (var-get calc-locked-balance)) + (var-set sc-delegated-balance (var-get calc-delegated-balance)) + (map-set updated-sc-balances {reward-cycle: next-reward-cycle} {updated: true, stackers-list: (var-get stackers-list)}) + (var-set reward-cycle-to-calculate-weight next-reward-cycle) + (unwrap! (calculate-all-stackers-weights (var-get stackers-list) next-reward-cycle) err-cant-calculate-weights) + (ok true)))) + +;; recalculate balances inside pool +(define-public (update-sc-balances-one-stacker (stacker principal)) +(let ((user-until-burn-ht (default-to u0 (default-to (some u0) (get until-burn-ht (map-get? user-data {address: stacker}))))) + (user-delegated-balance (default-to u0 (get delegated-balance (map-get? user-data {address: stacker})))) + (user-locked-balance (default-to u0 (get locked-balance (map-get? user-data {address: stacker}))))) + (ok + ;; if burn-block-height < user's unlock burn block height, then user's balances + (and (< + burn-block-height + user-until-burn-ht) + (begin + (var-set calc-locked-balance + (+ + (var-get calc-locked-balance) + user-locked-balance)) + (var-set calc-delegated-balance + (+ + (var-get calc-delegated-balance) + user-delegated-balance))))))) + +;; The rewards will be distributed. At that moment, the SC balance should have been updated and the stackers' weights calculated +(define-public (reward-distribution (rewarded-burn-block uint)) +(let ((reward-cycle + (contract-call? 'ST000000000000000000002AMW42H.pox-3 burn-height-to-reward-cycle rewarded-burn-block)) + (stackers-list-for-reward-cycle + (default-to (list ) (get stackers-list (map-get? updated-sc-balances {reward-cycle: reward-cycle}))))) + (asserts! (< rewarded-burn-block burn-block-height) err-no-reward-yet) + (asserts! (check-won-block-rewards rewarded-burn-block) err-no-reward-for-this-block) + (asserts! (is-none (map-get? already-rewarded {burn-block-height: rewarded-burn-block})) err-already-rewarded-block) + (var-set amount-rewarded (+ (var-get amount-rewarded) (default-to u0 (get reward (map-get? burn-block-rewards { burn-height: rewarded-burn-block}))))) + (var-set blocks-rewarded (+ (var-get blocks-rewarded) u1)) + (map-set already-rewarded {burn-block-height: rewarded-burn-block} {value: true}) + (var-set reward-cycle-to-distribute-rewards reward-cycle) + (match (map-get? calculated-weights-reward-cycles {reward-cycle: reward-cycle}) + calculated (ok + (unwrap-panic (transfer-rewards-all-stackers stackers-list-for-reward-cycle))) + err-weights-not-calculated))) + +;; delegating stx to the pool SC +(define-public (delegate-stx (amount-ustx uint)) +(let ((user tx-sender) + (current-cycle (contract-call? 'ST000000000000000000002AMW42H.pox-3 current-pox-reward-cycle))) + (asserts! (check-caller-allowed) err-stacking-permission-denied) + (asserts! (check-pool-SC-pox-2-allowance) err-allow-pool-in-pox-2-first) + + (asserts! (is-in-pool) err-not-in-pool) + (try! (delegate-stx-inner amount-ustx (as-contract tx-sender) none)) + (try! (as-contract (lock-delegated-stx user))) + (ok (maybe-stack-aggregation-commit current-cycle)))) + +;; Stacks the delegated amount for the given user for the next cycle. +;; This function can be called by automation, friends or family for user that have delegated once. +;; This function can be called only after the current cycle is half through +(define-public (delegate-stack-stx (user principal)) + (let ((current-cycle (contract-call? 'ST000000000000000000002AMW42H.pox-3 current-pox-reward-cycle))) + (asserts! (can-lock-now current-cycle) err-too-early) + ;; Do 3. + (try! (as-contract (lock-delegated-stx user))) + ;; Do 4. + (ok (maybe-stack-aggregation-commit current-cycle)))) + +(define-public (delegate-stack-stx-many (stackers-lock-list (list 100 principal))) +(ok (map delegate-stack-stx stackers-lock-list))) + +(define-public (multiple-blocks-check-won-rewards (burn-heights-list (list 100 uint))) +(ok (map check-won-block-rewards burn-heights-list))) + +;; liquidity provider pool management functions + +(define-public (set-pool-pox-address (new-pool-pox-address {hashbytes: (buff 32), version: (buff 1)})) +(begin + (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider) + (ok (var-set pool-pox-address new-pool-pox-address)))) + +(define-public (set-active (is-active bool)) +(begin + (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider) + (ok (var-set active is-active)))) + +(define-public (set-liquidity-provider (new-liquidity-provider principal)) +(begin + (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider) + (asserts! (is-some (map-get? user-data {address: new-liquidity-provider})) err-not-in-pool) ;; new liquidity provider should be in pool + (ok (var-set liquidity-provider new-liquidity-provider)))) + +(define-public (update-return (new-return-value uint)) +(begin + (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider) + (asserts! (<= new-return-value max-return-div-accepted) err-return-div-exceeds-maximum) + (asserts! (not (is-eq new-return-value (var-get return-div))) err-same-value) + (var-set return-div new-return-value) + (ok new-return-value))) + +;; Private functions + +;; Pox operative functions + +(define-private (maybe-stack-aggregation-commit (current-cycle uint)) +(let ((reward-cycle (+ u1 current-cycle))) + (match (map-get? pox-addr-indices reward-cycle) + ;; Total stacked already reached minimum. + ;; Call stack-aggregate-increase. + ;; It might fail because called in the same cycle twice. + index (match (as-contract (contract-call? 'ST000000000000000000002AMW42H.pox-3 stack-aggregation-increase (var-get pool-pox-address) reward-cycle index)) + success (map-set last-aggregation reward-cycle block-height) + error (begin (print {err-increase-ignored: error}) false)) + ;; Total stacked is still below minimum. + ;; Just try to commit, it might fail because minimum not yet met + (match (as-contract (contract-call? 'ST000000000000000000002AMW42H.pox-3 stack-aggregation-commit-indexed (var-get pool-pox-address) reward-cycle)) + index (begin + (map-set pox-addr-indices reward-cycle index) + (map-set last-aggregation reward-cycle block-height)) + error (begin + (print {err-commit-ignored: error}) false))))) ;; ignore errors + +(define-private (delegate-stx-inner (amount-ustx uint) (delegate-to principal) (until-burn-ht (optional uint))) +(let ((result-revoke + ;; Calls revoke and ignores result + (contract-call? 'ST000000000000000000002AMW42H.pox-3 revoke-delegate-stx)) + (user-delegated-balance + (default-to u0 (get delegated-balance (map-get? user-data {address: tx-sender}))))) + (if + (is-ok result-revoke) + (if + (unwrap-panic result-revoke) + (begin + (asserts! + (check-can-decrement-delegated-balance + user-delegated-balance) + err-not-delegated-that-amount) + (decrement-sc-delegated-balance user-delegated-balance)) + (decrement-sc-delegated-balance u0)) + (decrement-sc-delegated-balance u0)) + ;; Calls delegate-stx, converts any error to uint + (match (contract-call? 'ST000000000000000000002AMW42H.pox-3 delegate-stx amount-ustx delegate-to until-burn-ht none) + success (begin + (increment-sc-delegated-balance amount-ustx) + (map-set + user-data + {address: tx-sender} + { + is-in-pool: (default-to false (get is-in-pool (map-get? user-data {address: tx-sender}))), + delegated-balance: amount-ustx, + locked-balance: (default-to u0 (get locked-balance (map-get? user-data {address: tx-sender}))), + until-burn-ht: until-burn-ht}) + (print "sc delegated balance") + (print (var-get sc-delegated-balance)) + (ok success)) + error (err (* u1000 (to-uint error)))))) + +(define-private (lock-delegated-stx (user principal)) +(let ((start-burn-ht (+ burn-block-height u1)) + (pox-address (var-get pool-pox-address)) + ;; changed buffer-amount (commission) to 0, kept structure + (buffer-amount u0) + (user-account (stx-account user)) + (allowed-amount (min (get-delegated-amount user) (+ (get locked user-account) (get unlocked user-account)))) + (amount-ustx (if (> allowed-amount buffer-amount) (- allowed-amount buffer-amount) allowed-amount))) + (asserts! (var-get active) err-pox-address-deactivated) + (match (contract-call? 'ST000000000000000000002AMW42H.pox-3 delegate-stack-stx + user amount-ustx + pox-address start-burn-ht u1) + stacker-details + (begin + (map-set + user-data + {address: user} + { + is-in-pool: + (default-to false (get is-in-pool (map-get? user-data {address: user}))), + delegated-balance: + (default-to u0 (get delegated-balance (map-get? user-data {address: user}))), + locked-balance: (get lock-amount stacker-details), + until-burn-ht: + (some (get unlock-burn-height stacker-details))}) + (increment-sc-locked-balance (get lock-amount stacker-details)) + (ok stacker-details)) + + error (if (is-eq error 3) ;; check whether user is already stacked + (delegate-stack-extend-increase user amount-ustx pox-address start-burn-ht) + (err (* u1000 (to-uint error))))))) + +(define-private (delegate-stack-extend-increase (user principal) + (amount-ustx uint) + (pox-address {hashbytes: (buff 32), version: (buff 1)}) + (start-burn-ht uint)) +(let ((status (stx-account user))) + (asserts! (>= amount-ustx (get locked status)) err-decrease-forbidden) + (match (contract-call? 'ST000000000000000000002AMW42H.pox-3 delegate-stack-extend + user pox-address u1) + success (begin + (print "success") + (print success) + (map-set user-data + {address: user} + { + is-in-pool: + (default-to false (get is-in-pool (map-get? user-data {address: user}))), + delegated-balance: + (default-to u0 (get delegated-balance (map-get? user-data {address: user}))), + locked-balance: + (default-to u0 (get locked-balance (map-get? user-data {address: user}))), + until-burn-ht: + (some (+ (default-to u0 (default-to (some u0) (get until-burn-ht (map-get? user-data {address: user})))) REWARD_CYCLE_LENGTH)) + }) + (if (> amount-ustx (get locked status)) + (match (contract-call? 'ST000000000000000000002AMW42H.pox-3 delegate-stack-increase + user + pox-address + (- + amount-ustx + (default-to u0 (get locked-balance (map-get? user-data {address: user}))))) + success-increase (begin + (print "success-increase") + (print success-increase) + (map-set user-data + {address: user} + { + is-in-pool: + (default-to false (get is-in-pool (map-get? user-data {address: user}))), + delegated-balance: + (default-to u0 (get delegated-balance (map-get? user-data {address: user}))), + locked-balance: (get total-locked success-increase), + until-burn-ht: + (default-to none (get until-burn-ht (map-get? user-data {address: user}))) + }) + (increment-sc-locked-balance + (- amount-ustx + (default-to u0 (get locked-balance (map-get? user-data {address: user}))))) + (ok {lock-amount: (get total-locked success-increase), + stacker: user, + unlock-burn-height: (get unlock-burn-height success)})) + error-increase (begin (print "error-increase") (err (* u1000000000 (to-uint error-increase))))) + (ok { + lock-amount: (get locked status), + stacker: user, + unlock-burn-height: (get unlock-burn-height success)}))) + error (err (* u1000000 (to-uint error)))))) + +;; Rewards transferring functions + +(define-private (transfer-rewards-all-stackers (stackers-list-before-cycle (list 300 principal))) +(let ((current-reward + (unwrap! + (preview-exchange-reward + (default-to u0 + (get reward + (map-get? burn-block-rewards { burn-height: (var-get burn-block-to-distribute-rewards)}))) + u5) err-cant-unwrap-exchange-preview))) + (var-set temp-current-reward current-reward) + (ok (map transfer-reward-one-stacker stackers-list-before-cycle)))) + +(define-private (transfer-reward-one-stacker (stacker principal)) +(let ( + (reward (var-get temp-current-reward)) + (stacker-weight + (default-to u0 + (get weight-percentage + (map-get? stacker-weights-per-reward-cycle {stacker: stacker, reward-cycle: (var-get reward-cycle-to-distribute-rewards)})))) + (stacker-reward (/ (* stacker-weight reward) ONE-6))) + (if (> stacker-weight u0) + + (match (as-contract (stx-transfer? stacker-reward tx-sender stacker)) + success + (begin + (if + (not (check-can-decrement-reserved-balance stacker-reward)) + (decrement-sc-owned-balance stacker-reward) + (decrement-sc-reserved-balance stacker-reward)) + (ok true)) + error (err error)) + (ok false)))) + + +(define-private (preview-exchange-reward (sats-amount uint) (slippeage uint)) +(contract-call? .bridge-contract swap-preview .token-wbtc .token-wstx sats-amount slippeage)) + +;; Weight calculation functions + +;; calculating one stacker's weight inside pool based on his balances +(define-private (weight-calculator (stacker-locked uint) (total-locked uint) (liquidity-provider-locked uint)) +(begin + (asserts! (> (+ total-locked liquidity-provider-locked) u0) err-no-locked-funds) + (ok (/ (* stacker-locked ONE-6) (+ total-locked liquidity-provider-locked))))) + +(define-private (calculate-all-stackers-weights (stackers-list-before-cycle (list 300 principal)) (next-reward-cycle uint)) +(begin + (map calculate-one-stacker-weight stackers-list-before-cycle) + (map-set calculated-weights-reward-cycles {reward-cycle: next-reward-cycle} {calculated: true}) + (ok true))) + +;; each stacker will have a weight inside the pool which will be used when distributing rewards +(define-private (calculate-one-stacker-weight (stacker principal)) +(let ((last-burn-block-before-reward-cycle + (- + (contract-call? 'ST000000000000000000002AMW42H.pox-3 reward-cycle-to-burn-height (var-get reward-cycle-to-calculate-weight)) + u1)) + ;; total locked by pool + (total-locked-at-reward-cycle + (var-get sc-locked-balance)) + ;; total reserved by liquidity provider + (liquidity-provider-reserved-at-reward-cycle + (var-get sc-reserved-balance)) + ;; total locked by a stacker + (stacker-locked-at-reward-cycle + (default-to u0 (get locked-balance (map-get? user-data {address: stacker})))) + (liquidity-provider-contribution (+ liquidity-provider-reserved-at-reward-cycle stacker-locked-at-reward-cycle)) + ;; the weight calculator result for the given stacker + (weight-calculator-result + (if + (is-eq stacker (var-get liquidity-provider)) + (weight-calculator + liquidity-provider-contribution + total-locked-at-reward-cycle + liquidity-provider-reserved-at-reward-cycle) + (weight-calculator + stacker-locked-at-reward-cycle + total-locked-at-reward-cycle + liquidity-provider-reserved-at-reward-cycle)))) + ;; register the stacker's weight for a given reward cycle using a map + (map-set stacker-weights-per-reward-cycle + {stacker: stacker, reward-cycle: (var-get reward-cycle-to-calculate-weight)} + {weight-percentage: + (if + (not (is-err weight-calculator-result)) + (unwrap-panic weight-calculator-result) + u0) + }))) + + +;; check if pool pox address has won the rewards for a given burn height and store the reward if true +(define-private (check-won-block-rewards (burn-height uint)) +(let ((reward-pox-addr-list (default-to (list ) (get addrs (get-burn-block-info? pox-addrs burn-height))))) + (if + (is-some + (index-of? reward-pox-addr-list (var-get pool-pox-address))) + (begin + (register-block-reward burn-height) + true) + false))) + +;; store the reward for a given block using a map +(define-private (register-block-reward (burn-height uint)) +(map-set burn-block-rewards {burn-height: burn-height} {reward: (default-to u0 (get payout (get-burn-block-info? pox-addrs burn-height)))})) + +(define-private (remove-stacker-stackers-list (address principal)) (not (is-eq tx-sender address))) + +(define-private (increment-sc-delegated-balance (amount-ustx uint)) +(var-set sc-delegated-balance (+ (var-get sc-delegated-balance) amount-ustx))) + +(define-private (increment-sc-locked-balance (amount-ustx uint)) +(var-set sc-locked-balance (+ (var-get sc-locked-balance) amount-ustx))) + +(define-private (decrement-sc-delegated-balance (amount-ustx uint)) +(var-set sc-delegated-balance (- (var-get sc-delegated-balance) amount-ustx))) + +(define-private (decrement-sc-locked-balance (amount-ustx uint)) +(var-set sc-locked-balance (- (var-get sc-locked-balance) amount-ustx))) + +(define-private (decrement-sc-reserved-balance (amount-ustx uint)) +(var-set sc-reserved-balance (- (var-get sc-reserved-balance) amount-ustx))) + +(define-private (decrement-sc-owned-balance (amount-ustx uint)) +(var-set sc-owned-balance (- (var-get sc-owned-balance) amount-ustx))) + +(define-private (check-can-decrement-delegated-balance (amount-ustx uint)) +(not + (< + (var-get sc-delegated-balance) + amount-ustx))) + +(define-private (check-can-decrement-locked-balance (amount-ustx uint)) +(not + (< + (var-get sc-locked-balance) + amount-ustx))) + +(define-private (check-can-decrement-reserved-balance (amount-ustx uint)) +(not + (< + (var-get sc-reserved-balance) + amount-ustx))) + +(define-private (check-can-decrement-total-balance (amount-ustx uint)) +(not + (< + (var-get sc-total-balance) + amount-ustx))) + +(define-private (check-can-decrement-owned-balance (amount-ustx uint)) +(not + (< + (var-get sc-owned-balance) + amount-ustx))) + +(define-private (min (amount-1 uint) (amount-2 uint)) + (if (< amount-1 amount-2) + amount-1 + amount-2)) + +(define-private (get-next-reward-cycle) +(+ (contract-call? 'ST000000000000000000002AMW42H.pox-3 burn-height-to-reward-cycle burn-block-height) u1)) + +;; Read-only helper functions + +(define-read-only (get-stx-account) +(stx-account tx-sender)) + +(define-read-only (get-pool-members) +(var-get stackers-list)) + +(define-read-only (check-caller-allowed) + (or (is-eq tx-sender contract-caller) + (let ((caller-allowed + ;; if not in the caller map, return false + (unwrap! + (map-get? allowance-contract-callers + { sender: tx-sender, contract-caller: contract-caller}) + false)) + (expires-at + ;; if until-burn-ht not set, then return true (because no expiry) + (unwrap! (get until-burn-ht caller-allowed) true))) + ;; is the caller allowance still valid + (< burn-block-height expires-at)))) + +(define-read-only (is-in-pool) +(default-to false (get is-in-pool (map-get? user-data {address: tx-sender})))) + +(define-read-only (get-stacker-weight (stacker principal) (reward-cycle uint)) +(get weight-percentage (map-get? stacker-weights-per-reward-cycle {stacker: stacker, reward-cycle: reward-cycle}))) + +(define-read-only (get-SC-total-balance) +(var-get sc-total-balance)) + +(define-read-only (get-SC-owned-balance) +(var-get sc-owned-balance)) + +(define-read-only (get-SC-locked-balance) +(var-get sc-locked-balance)) + +(define-read-only (get-SC-reserved-balance) +(var-get sc-reserved-balance)) + +(define-read-only (get-user-data (user principal)) +(map-get? user-data {address: user})) + +(define-read-only (check-pool-SC-pox-2-allowance) +(is-some (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-allowance-contract-callers tx-sender pool-contract))) + +(define-read-only (get-check-delegation (stacker principal)) +(contract-call? 'ST000000000000000000002AMW42H.pox-3 get-check-delegation stacker)) + +(define-read-only (get-pox-addr-indices (reward-cycle uint)) +(map-get? pox-addr-indices reward-cycle)) + +(define-read-only (get-block-rewards (burn-height uint)) +(ok (get-burn-block-info? pox-addrs burn-height))) + +(define-read-only (can-lock-now (cycle uint)) +(> burn-block-height (+ (contract-call? 'ST000000000000000000002AMW42H.pox-3 reward-cycle-to-burn-height cycle) half-cycle-length))) + +(define-read-only (get-delegated-amount (user principal)) +(default-to u0 (get amount-ustx (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-delegation-info user)))) + +(define-read-only (get-liquidity-provider) +(var-get liquidity-provider)) + +(define-read-only (get-amount-rewarded) +(var-get amount-rewarded)) + +(define-read-only (get-blocks-rewarded) +(var-get blocks-rewarded)) + +(define-read-only (get-stacked-this-cycle) +(var-get sc-locked-balance)) + +(define-private (check-is-liquidity-provider (address principal)) +(is-eq address (var-get liquidity-provider))) + +(define-private (check-is-stacker (address principal)) +(default-to false (get is-in-pool (map-get? user-data {address: address})))) + +(define-read-only (get-address-status (address principal)) +(if (check-is-liquidity-provider address) + (ok "is-provider") + (if (check-is-stacker address) + (ok "is-stacker") + (ok "is-none")))) + +(define-read-only (calculate-extra-reserved-funds) +;; subtract the potential return from the total reserved balance and get the extra reserved balance +(- + (var-get sc-reserved-balance) + (/ + (var-get sc-locked-balance) + (var-get return-div)))) + +(define-read-only (can-withdraw-extra-reserved-now) +;; liquidity provider can only withdraw extra reserved balance within the last 10 blocks of a reward cycle +(let ((mod-burn-height (mod burn-block-height REWARD_CYCLE_LENGTH)) + (start-value (- REWARD_CYCLE_LENGTH u10)) + (end-value (- REWARD_CYCLE_LENGTH u1))) + (and (>= mod-burn-height start-value) (<= mod-burn-height end-value)))) + +(define-read-only (get-return) +(var-get return-div)) + +(define-read-only (get-minimum-deposit-liquidity-provider) +minimum-deposit-amount-liquidity-provider) + +(define-read-only (was-block-claimed (rewarded-burn-block uint)) +(map-get? already-rewarded {burn-block-height: rewarded-burn-block})) diff --git a/sbtc-ops/smart-contract/contracts/token-amm-swap-pool-v1-1.clar b/sbtc-ops/smart-contract/contracts/token-amm-swap-pool-v1-1.clar new file mode 100644 index 00000000..f2f473c8 --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/token-amm-swap-pool-v1-1.clar @@ -0,0 +1,268 @@ +(impl-trait .trait-ownable.ownable-trait) +(impl-trait .trait-semi-fungible.semi-fungible-trait) +(define-constant ERR-NOT-AUTHORIZED (err u1000)) +(define-constant ERR-TOO-MANY-POOLS (err u2004)) +(define-constant ERR-INVALID-BALANCE (err u1001)) +(define-constant ERR-TRANSFER-FAILED (err u3000)) +(define-fungible-token amm-swap-pool-v1-1) +(define-map token-balances {token-id: uint, owner: principal} uint) +(define-map token-supplies uint uint) +(define-map token-owned principal (list 200 uint)) +(define-data-var contract-owner principal tx-sender) +(define-map approved-contracts principal bool) +(define-data-var token-name (string-ascii 32) "amm-swap-pool-v1-1") +(define-data-var token-symbol (string-ascii 32) "amm-swap-pool-v1-1") +(define-data-var token-uri (optional (string-utf8 256)) (some u"https://cdn.alexlab.co/metadata/token-amm-swap-pool.json")) +(define-data-var token-decimals uint u8) +(define-data-var transferrable bool true) +(define-read-only (get-transferrable) + (ok (var-get transferrable)) +) +(define-public (set-transferrable (new-transferrable bool)) + (begin + (try! (check-is-owner)) + (ok (var-set transferrable new-transferrable)) + ) +) +(define-read-only (get-contract-owner) + (ok (var-get contract-owner)) +) +(define-public (set-contract-owner (owner principal)) + (begin + (try! (check-is-owner)) + (ok (var-set contract-owner owner)) + ) +) +(define-private (check-is-approved) + (ok (asserts! (default-to false (map-get? approved-contracts tx-sender)) ERR-NOT-AUTHORIZED)) +) +(define-private (check-is-owner) + (ok (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED)) +) +(define-public (add-approved-contract (new-approved-contract principal)) + (begin + (try! (check-is-owner)) + (map-set approved-contracts new-approved-contract true) + (ok true) + ) +) +(define-public (set-approved-contract (owner principal) (approved bool)) + (begin + (try! (check-is-owner)) + (ok (map-set approved-contracts owner approved)) + ) +) +(define-read-only (get-token-owned (owner principal)) + (default-to (list) (map-get? token-owned owner)) +) +(define-private (set-balance (token-id uint) (balance uint) (owner principal)) + (begin + (and + (is-none (index-of (get-token-owned owner) token-id)) + (map-set token-owned owner (unwrap! (as-max-len? (append (get-token-owned owner) token-id) u200) ERR-TOO-MANY-POOLS)) + ) + (map-set token-balances {token-id: token-id, owner: owner} balance) + (ok true) + ) +) +(define-private (get-balance-or-default (token-id uint) (who principal)) + (default-to u0 (map-get? token-balances {token-id: token-id, owner: who})) +) +(define-read-only (get-balance (token-id uint) (who principal)) + (ok (get-balance-or-default token-id who)) +) +(define-read-only (get-overall-balance (who principal)) + (ok (ft-get-balance amm-swap-pool-v1-1 who)) +) +(define-read-only (get-total-supply (token-id uint)) + (ok (default-to u0 (map-get? token-supplies token-id))) +) +(define-read-only (get-overall-supply) + (ok (ft-get-supply amm-swap-pool-v1-1)) +) +(define-read-only (get-decimals (token-id uint)) + (ok (var-get token-decimals)) +) +(define-public (set-decimals (new-decimals uint)) + (begin + (try! (check-is-owner)) + (ok (var-set token-decimals new-decimals)) + ) +) +(define-read-only (get-token-uri (token-id uint)) + (ok (var-get token-uri)) +) +(define-public (set-token-uri (new-uri (optional (string-utf8 256)))) + (begin + (try! (check-is-owner)) + (ok (var-set token-uri new-uri)) + ) +) +(define-read-only (get-name (token-id uint)) + (ok (var-get token-name)) +) +(define-public (set-name (new-name (string-ascii 32))) + (begin + (try! (check-is-owner)) + (ok (var-set token-name new-name)) + ) +) +(define-read-only (get-symbol (token-id uint)) + (ok (var-get token-symbol)) +) +(define-public (set-symbol (new-symbol (string-ascii 10))) + (begin + (try! (check-is-owner)) + (ok (var-set token-symbol new-symbol)) + ) +) +(define-public (transfer (token-id uint) (amount uint) (sender principal) (recipient principal)) + (let + ( + (sender-balance (get-balance-or-default token-id sender)) + ) + (asserts! (var-get transferrable) ERR-TRANSFER-FAILED) + (asserts! (is-eq tx-sender sender) ERR-NOT-AUTHORIZED) + (asserts! (<= amount sender-balance) ERR-INVALID-BALANCE) + (try! (ft-transfer? amm-swap-pool-v1-1 amount sender recipient)) + (try! (set-balance token-id (- sender-balance amount) sender)) + (try! (set-balance token-id (+ (get-balance-or-default token-id recipient) amount) recipient)) + (print {type: "sft_transfer", token-id: token-id, amount: amount, sender: sender, recipient: recipient}) + (ok true) + ) +) +(define-public (transfer-memo (token-id uint) (amount uint) (sender principal) (recipient principal) (memo (buff 34))) + (let + ( + (sender-balance (get-balance-or-default token-id sender)) + ) + (asserts! (var-get transferrable) ERR-TRANSFER-FAILED) + (asserts! (is-eq tx-sender sender) ERR-NOT-AUTHORIZED) + (asserts! (<= amount sender-balance) ERR-INVALID-BALANCE) + (try! (ft-transfer? amm-swap-pool-v1-1 amount sender recipient)) + (try! (set-balance token-id (- sender-balance amount) sender)) + (try! (set-balance token-id (+ (get-balance-or-default token-id recipient) amount) recipient)) + (print {type: "sft_transfer", token-id: token-id, amount: amount, sender: sender, recipient: recipient, memo: memo}) + (ok true) + ) +) +(define-public (mint (token-id uint) (amount uint) (recipient principal)) + (begin + (asserts! (or (is-ok (check-is-approved)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED) + (try! (ft-mint? amm-swap-pool-v1-1 amount recipient)) + (try! (set-balance token-id (+ (get-balance-or-default token-id recipient) amount) recipient)) + (map-set token-supplies token-id (+ (unwrap-panic (get-total-supply token-id)) amount)) + (print {type: "sft_mint", token-id: token-id, amount: amount, recipient: recipient}) + (ok true) + ) +) +(define-public (burn (token-id uint) (amount uint) (sender principal)) + (begin + (asserts! (or (is-ok (check-is-approved)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED) + (try! (ft-burn? amm-swap-pool-v1-1 amount sender)) + (try! (set-balance token-id (- (get-balance-or-default token-id sender) amount) sender)) + (map-set token-supplies token-id (- (unwrap-panic (get-total-supply token-id)) amount)) + (print {type: "sft_burn", token-id: token-id, amount: amount, sender: sender}) + (ok true) + ) +) +(define-constant ONE_8 u100000000) +(define-private (pow-decimals) + (pow u10 (unwrap-panic (get-decimals u0))) +) +(define-read-only (fixed-to-decimals (amount uint)) + (/ (* amount (pow-decimals)) ONE_8) +) +(define-private (decimals-to-fixed (amount uint)) + (/ (* amount ONE_8) (pow-decimals)) +) +(define-read-only (get-total-supply-fixed (token-id uint)) + (ok (decimals-to-fixed (default-to u0 (map-get? token-supplies token-id)))) +) +(define-read-only (get-balance-fixed (token-id uint) (who principal)) + (ok (decimals-to-fixed (get-balance-or-default token-id who))) +) +(define-read-only (get-overall-supply-fixed) + (ok (decimals-to-fixed (ft-get-supply amm-swap-pool-v1-1))) +) +(define-read-only (get-overall-balance-fixed (who principal)) + (ok (decimals-to-fixed (ft-get-balance amm-swap-pool-v1-1 who))) +) +(define-public (transfer-fixed (token-id uint) (amount uint) (sender principal) (recipient principal)) + (transfer token-id (fixed-to-decimals amount) sender recipient) +) +(define-public (transfer-memo-fixed (token-id uint) (amount uint) (sender principal) (recipient principal) (memo (buff 34))) + (transfer-memo token-id (fixed-to-decimals amount) sender recipient memo) +) +(define-public (mint-fixed (token-id uint) (amount uint) (recipient principal)) + (mint token-id (fixed-to-decimals amount) recipient) +) +(define-public (burn-fixed (token-id uint) (amount uint) (sender principal)) + (burn token-id (fixed-to-decimals amount) sender) +) +(define-private (transfer-many-iter (item {token-id: uint, amount: uint, sender: principal, recipient: principal}) (previous-response (response bool uint))) + (match previous-response prev-ok (transfer (get token-id item) (get amount item) (get sender item) (get recipient item)) prev-err previous-response) +) +(define-public (transfer-many (transfers (list 200 {token-id: uint, amount: uint, sender: principal, recipient: principal}))) + (fold transfer-many-iter transfers (ok true)) +) +(define-private (transfer-many-memo-iter (item {token-id: uint, amount: uint, sender: principal, recipient: principal, memo: (buff 34)}) (previous-response (response bool uint))) + (match previous-response prev-ok (transfer-memo (get token-id item) (get amount item) (get sender item) (get recipient item) (get memo item)) prev-err previous-response) +) +(define-public (transfer-many-memo (transfers (list 200 {token-id: uint, amount: uint, sender: principal, recipient: principal, memo: (buff 34)}))) + (fold transfer-many-memo-iter transfers (ok true)) +) +(define-private (transfer-many-fixed-iter (item {token-id: uint, amount: uint, sender: principal, recipient: principal}) (previous-response (response bool uint))) + (match previous-response prev-ok (transfer-fixed (get token-id item) (get amount item) (get sender item) (get recipient item)) prev-err previous-response) +) +(define-public (transfer-many-fixed (transfers (list 200 {token-id: uint, amount: uint, sender: principal, recipient: principal}))) + (fold transfer-many-fixed-iter transfers (ok true)) +) +(define-private (transfer-many-memo-fixed-iter (item {token-id: uint, amount: uint, sender: principal, recipient: principal, memo: (buff 34)}) (previous-response (response bool uint))) + (match previous-response prev-ok (transfer-memo-fixed (get token-id item) (get amount item) (get sender item) (get recipient item) (get memo item)) prev-err previous-response) +) +(define-public (transfer-many-memo-fixed (transfers (list 200 {token-id: uint, amount: uint, sender: principal, recipient: principal, memo: (buff 34)}))) + (fold transfer-many-memo-fixed-iter transfers (ok true)) +) +(define-private (create-tuple-token-balance (token-id uint) (balance uint)) + { token-id: token-id, balance: (decimals-to-fixed balance) } +) +(define-read-only (get-token-balance-owned-in-fixed (owner principal)) + (begin + (match (map-get? token-owned owner) + token-ids + (map + create-tuple-token-balance + token-ids + (map + get-balance-or-default + token-ids + (list + owner owner owner owner owner owner owner owner owner owner + owner owner owner owner owner owner owner owner owner owner + owner owner owner owner owner owner owner owner owner owner + owner owner owner owner owner owner owner owner owner owner + owner owner owner owner owner owner owner owner owner owner + owner owner owner owner owner owner owner owner owner owner + owner owner owner owner owner owner owner owner owner owner + owner owner owner owner owner owner owner owner owner owner + owner owner owner owner owner owner owner owner owner owner + owner owner owner owner owner owner owner owner owner owner + owner owner owner owner owner owner owner owner owner owner + owner owner owner owner owner owner owner owner owner owner + owner owner owner owner owner owner owner owner owner owner + owner owner owner owner owner owner owner owner owner owner + owner owner owner owner owner owner owner owner owner owner + owner owner owner owner owner owner owner owner owner owner + owner owner owner owner owner owner owner owner owner owner + owner owner owner owner owner owner owner owner owner owner + owner owner owner owner owner owner owner owner owner owner + owner owner owner owner owner owner owner owner owner owner + ) + ) + ) + (list) + ) + ) +) +(map-set approved-contracts .amm-swap-pool-v1-1 true) \ No newline at end of file diff --git a/sbtc-ops/smart-contract/contracts/token-wbtc.clar b/sbtc-ops/smart-contract/contracts/token-wbtc.clar new file mode 100644 index 00000000..04107b54 --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/token-wbtc.clar @@ -0,0 +1,206 @@ +(impl-trait .trait-ownable.ownable-trait) +(impl-trait .trait-sip-010.sip-010-trait) + +(define-fungible-token wbtc) + +(define-data-var token-name (string-ascii 32) "Wrapped Bitcoin") +(define-data-var token-symbol (string-ascii 10) "XBTC") +(define-data-var token-uri (optional (string-utf8 256)) (some u"https://cdn.alexlab.co/metadata/token-wbtc.json")) + +(define-data-var token-decimals uint u8) + +(define-data-var contract-owner principal tx-sender) + +;; errors +(define-constant ERR-NOT-AUTHORIZED (err u1000)) +(define-constant ERR-MINT-FAILED (err u6002)) +(define-constant ERR-BURN-FAILED (err u6003)) +(define-constant ERR-TRANSFER-FAILED (err u3000)) + +(define-read-only (get-contract-owner) + (ok (var-get contract-owner)) +) + +(define-public (set-contract-owner (owner principal)) + (begin + (try! (check-is-owner)) + (ok (var-set contract-owner owner)) + ) +) + +(define-private (check-is-owner) + (ok (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED)) +) + +(define-public (set-name (new-name (string-ascii 32))) + (begin + (try! (check-is-owner)) + (ok (var-set token-name new-name)) + ) +) + +(define-public (set-symbol (new-symbol (string-ascii 10))) + (begin + (try! (check-is-owner)) + (ok (var-set token-symbol new-symbol)) + ) +) + +(define-public (set-decimals (new-decimals uint)) + (begin + (try! (check-is-owner)) + (ok (var-set token-decimals new-decimals)) + ) +) + +(define-public (set-token-uri (new-uri (optional (string-utf8 256)))) + (begin + (try! (check-is-owner)) + (ok (var-set token-uri new-uri)) + ) +) + +;; --------------------------------------------------------- +;; SIP-10 Functions +;; --------------------------------------------------------- + +;; @desc get-total-supply +;; @returns (response uint) +(define-read-only (get-total-supply) + (ok u0) +) + +;; @desc get-name +;; @returns (response string-utf8) +(define-read-only (get-name) + (ok (var-get token-name)) +) + +;; @desc get-symbol +;; @returns (response string-utf8) +(define-read-only (get-symbol) + (ok (var-get token-symbol)) +) + +;; @desc get-decimals +;; @returns (response uint) +(define-read-only (get-decimals) + (ok (var-get token-decimals)) +) + +;; @desc get-balance +;; @params account +;; @returns (response uint) +(define-read-only (get-balance (account principal)) + (ok (/ (* (unwrap-panic (contract-call? .Wrapped-Bitcoin get-balance account)) (pow-decimals)) (pow u10 u8))) +) + +;; @desc get-token-uri +;; @returns (response some string-utf-8) +(define-read-only (get-token-uri) + (ok (var-get token-uri)) +) + +;; @desc transfer +;; @restricted sender; tx-sender should be sender +;; @params amount +;; @params sender +;; @params recipient +;; @params memo; expiry +;; @returns (response bool uint)/ error +(define-public (transfer (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34)))) + (begin + (asserts! (is-eq sender tx-sender) ERR-NOT-AUTHORIZED) + (contract-call? .Wrapped-Bitcoin transfer (/ (* amount (pow u10 u8)) (pow-decimals)) sender recipient memo) + ) +) + +(define-constant ONE_8 (pow u10 u8)) + +;; @desc pow-decimals +;; @returns uint +(define-private (pow-decimals) + (pow u10 (unwrap-panic (get-decimals))) +) + +;; @desc fixed-to-decimals +;; @params amount +;; @returns uint +(define-read-only (fixed-to-decimals (amount uint)) + (/ (* amount (pow-decimals)) ONE_8) +) + +;; @desc decimals-to-fixed +;; @params amount +;; @returns uint +(define-private (decimals-to-fixed (amount uint)) + (/ (* amount ONE_8) (pow-decimals)) +) + +;; @desc get-total-supply-fixed +;; @params token-id +;; @returns (response uint) +(define-read-only (get-total-supply-fixed) + (ok (decimals-to-fixed (unwrap-panic (get-total-supply)))) +) + +;; @desc get-balance-fixed +;; @params token-id +;; @params who +;; @returns (response uint) +(define-read-only (get-balance-fixed (account principal)) + (ok (decimals-to-fixed (unwrap-panic (get-balance account)))) +) + +;; @desc transfer-fixed +;; @params token-id +;; @params amount +;; @params sender +;; @params recipient +;; @returns (response bool) +(define-public (transfer-fixed (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34)))) + (transfer (fixed-to-decimals amount) sender recipient memo) +) + +(define-public (mint (amount uint) (recipient principal)) + ERR-MINT-FAILED +) + +(define-public (burn (amount uint) (sender principal)) + ERR-BURN-FAILED +) + +(define-public (mint-fixed (amount uint) (recipient principal)) + (mint (fixed-to-decimals amount) recipient) +) + +;; @desc burn-fixed +;; @params token-id +;; @params amount +;; @params sender +;; @returns (response bool) +(define-public (burn-fixed (amount uint) (sender principal)) + (burn (fixed-to-decimals amount) sender) +) + +;; @desc check-err +;; @params result +;; @params prior +;; @returns (response bool uint) +(define-private (check-err (result (response bool uint)) (prior (response bool uint))) + (match prior + ok-value result + err-value (err err-value) + ) +) + +(define-private (transfer-from-tuple (recipient { to: principal, amount: uint })) + (ok (unwrap! (transfer-fixed (get amount recipient) tx-sender (get to recipient) none) ERR-TRANSFER-FAILED)) +) + +(define-public (send-many (recipients (list 200 { to: principal, amount: uint}))) + (fold check-err (map transfer-from-tuple recipients) (ok true)) +) + +;; contract initialisation +(set-contract-owner tx-sender) \ No newline at end of file diff --git a/sbtc-ops/smart-contract/contracts/token-wstx.clar b/sbtc-ops/smart-contract/contracts/token-wstx.clar new file mode 100644 index 00000000..14bcdb07 --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/token-wstx.clar @@ -0,0 +1,179 @@ +(impl-trait .trait-ownable.ownable-trait) +(impl-trait .trait-sip-010.sip-010-trait) + +(define-fungible-token wstx) + +(define-data-var token-uri (string-utf8 256) u"") +(define-data-var contract-owner principal tx-sender) + +;; errors +(define-constant ERR-NOT-AUTHORIZED (err u1000)) +(define-constant ERR-MINT-FAILED (err u6002)) +(define-constant ERR-BURN-FAILED (err u6003)) +(define-constant ERR-TRANSFER-FAILED (err u3000)) + +(define-read-only (get-contract-owner) + (ok (var-get contract-owner)) +) + +(define-public (set-contract-owner (owner principal)) + (begin + (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED) + (ok (var-set contract-owner owner)) + ) +) + +;; --------------------------------------------------------- +;; SIP-10 Functions +;; --------------------------------------------------------- + +;; @desc get-total-supply +;; @returns (response uint) +(define-read-only (get-total-supply) + (ok u0) +) + +;; @desc get-name +;; @returns (response string-utf8) +(define-read-only (get-name) + (ok "wstx") +) + +;; @desc get-symbol +;; @returns (response string-utf8) +(define-read-only (get-symbol) + (ok "wstx") +) + +;; @desc get-decimals +;; @returns (response uint) +(define-read-only (get-decimals) + (ok u8) +) + +;; @desc get-balance +;; @params account +;; @returns (response uint) +(define-read-only (get-balance (account principal)) + (ok (/ (* (stx-get-balance account) ONE_8) (pow u10 u6))) +) + +;; @desc set-token-uri +;; @restricted Contract-Owner +;; @params value +;; @returns (response bool) +(define-public (set-token-uri (value (string-utf8 256))) + (begin + (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED) + (ok (var-set token-uri value)) + ) +) + +;; @desc get-token-uri +;; @returns (response some string-utf-8) +(define-read-only (get-token-uri) + (ok (some (var-get token-uri))) +) + +;; @desc transfer +;; @restricted sender; tx-sender should be sender +;; @params amount +;; @params sender +;; @params recipient +;; @params memo; expiry +;; @returns (response bool uint)/ error +(define-public (transfer (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34)))) + (begin + (asserts! (is-eq sender tx-sender) ERR-NOT-AUTHORIZED) + (try! (stx-transfer? (/ (* amount (pow u10 u6)) ONE_8) sender recipient)) + (match memo to-print (print to-print) 0x) + (ok true) + ) +) + +(define-constant ONE_8 (pow u10 u8)) + +;; @desc pow-decimals +;; @returns uint +(define-private (pow-decimals) + (pow u10 (unwrap-panic (get-decimals))) +) + +;; @desc fixed-to-decimals +;; @params amount +;; @returns uint +(define-read-only (fixed-to-decimals (amount uint)) + (/ (* amount (pow-decimals)) ONE_8) +) + +;; @desc decimals-to-fixed +;; @params amount +;; @returns uint +(define-private (decimals-to-fixed (amount uint)) + (/ (* amount ONE_8) (pow-decimals)) +) + +;; @desc get-total-supply-fixed +;; @params token-id +;; @returns (response uint) +(define-read-only (get-total-supply-fixed) + (ok (decimals-to-fixed (unwrap-panic (get-total-supply)))) +) + +;; @desc get-balance-fixed +;; @params token-id +;; @params who +;; @returns (response uint) +(define-read-only (get-balance-fixed (account principal)) + (ok (decimals-to-fixed (unwrap-panic (get-balance account)))) +) + +;; @desc transfer-fixed +;; @params token-id +;; @params amount +;; @params sender +;; @params recipient +;; @returns (response bool) +(define-public (transfer-fixed (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34)))) + (transfer (fixed-to-decimals amount) sender recipient memo) +) + +(define-public (mint (amount uint) (recipient principal)) + ERR-MINT-FAILED +) + +(define-public (burn (amount uint) (sender principal)) + ERR-BURN-FAILED +) + +(define-public (mint-fixed (amount uint) (recipient principal)) + (mint (fixed-to-decimals amount) recipient) +) + +;; @desc burn-fixed +;; @params token-id +;; @params amount +;; @params sender +;; @returns (response bool) +(define-public (burn-fixed (amount uint) (sender principal)) + (burn (fixed-to-decimals amount) sender) +) + +;; @desc check-err +;; @params result +;; @params prior +;; @returns (response bool uint) +(define-private (check-err (result (response bool uint)) (prior (response bool uint))) + (match prior + ok-value result + err-value (err err-value) + ) +) + +(define-private (transfer-from-tuple (recipient { to: principal, amount: uint })) + (ok (unwrap! (transfer-fixed (get amount recipient) tx-sender (get to recipient) none) ERR-TRANSFER-FAILED)) +) + +(define-public (send-many (recipients (list 200 { to: principal, amount: uint}))) + (fold check-err (map transfer-from-tuple recipients) (ok true)) +) \ No newline at end of file diff --git a/sbtc-ops/smart-contract/contracts/trait-flash-loan-user.clar b/sbtc-ops/smart-contract/contracts/trait-flash-loan-user.clar new file mode 100644 index 00000000..434c7a80 --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/trait-flash-loan-user.clar @@ -0,0 +1,7 @@ +(use-trait ft-trait .trait-sip-010.sip-010-trait) + +(define-trait flash-loan-user-trait + ( + (execute ( uint (optional (buff 16))) (response bool uint)) + ) +) \ No newline at end of file diff --git a/sbtc-ops/smart-contract/contracts/trait-ownable.clar b/sbtc-ops/smart-contract/contracts/trait-ownable.clar new file mode 100644 index 00000000..cce2578f --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/trait-ownable.clar @@ -0,0 +1,6 @@ +(define-trait ownable-trait + ( + (get-contract-owner () (response principal uint)) + (set-contract-owner (principal) (response bool uint)) + ) +) \ No newline at end of file diff --git a/sbtc-ops/smart-contract/contracts/trait-semi-fungible.clar b/sbtc-ops/smart-contract/contracts/trait-semi-fungible.clar new file mode 100644 index 00000000..dcb1d072 --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/trait-semi-fungible.clar @@ -0,0 +1,41 @@ +(define-trait semi-fungible-trait + ( + ;; Get a token type balance of the passed principal. + (get-balance (uint principal) (response uint uint)) + + ;; Get the total SFT balance of the passed principal. + (get-overall-balance (principal) (response uint uint)) + + ;; Get the current total supply of a token type. + (get-total-supply (uint) (response uint uint)) + + ;; Get the overall SFT supply. + (get-overall-supply () (response uint uint)) + + ;; Get the number of decimal places of a token type. + (get-decimals (uint) (response uint uint)) + + ;; Get an optional token URI that represents metadata for a specific token. + (get-token-uri (uint) (response (optional (string-utf8 256)) uint)) + + ;; Transfer from one principal to another. + (transfer (uint uint principal principal) (response bool uint)) + + ;; Transfer from one principal to another with a memo. + (transfer-memo (uint uint principal principal (buff 34)) (response bool uint)) + + ;; helper functions for fixed notation + (transfer-fixed (uint uint principal principal) (response bool uint)) + (transfer-memo-fixed (uint uint principal principal (buff 34)) (response bool uint)) + (get-balance-fixed (uint principal) (response uint uint)) + (get-total-supply-fixed (uint) (response uint uint)) + ;; (get-total-supply-fixed (uint) (response uint uint)) + (get-overall-balance-fixed (principal) (response uint uint)) + (get-overall-supply-fixed () (response uint uint)) + + (mint (uint uint principal) (response bool uint)) + (burn (uint uint principal) (response bool uint)) + (mint-fixed (uint uint principal) (response bool uint)) + (burn-fixed (uint uint principal) (response bool uint)) + ) +) \ No newline at end of file diff --git a/sbtc-ops/smart-contract/contracts/trait-sip-010-sbtc.clar b/sbtc-ops/smart-contract/contracts/trait-sip-010-sbtc.clar new file mode 100644 index 00000000..32241f64 --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/trait-sip-010-sbtc.clar @@ -0,0 +1,38 @@ +;; trait-sip-010 + +;; sip-010-trait +(define-trait sip-010-trait + ( + ;; Transfer from the caller to a new principal + (transfer (uint principal principal (optional (buff 34))) (response bool uint)) + + ;; the human readable name of the token + (get-name () (response (string-ascii 32) uint)) + + ;; the ticker symbol, or empty if none + (get-symbol () (response (string-ascii 32) uint)) + + ;; the number of decimals used, e.g. 6 would mean 1_000_000 represents 1 token + (get-decimals () (response uint uint)) + + ;; the balance of the passed principal + (get-balance (principal) (response uint uint)) + + ;; the current total supply (which does not need to be a constant) + (get-total-supply () (response uint uint)) + + ;; an optional URI that represents metadata of this token + (get-token-uri () (response (optional (string-utf8 256)) uint)) + + ;; helper functions for 8-digit fixed notation + (transfer-fixed (uint principal principal (optional (buff 34))) (response bool uint)) + (get-balance-fixed (principal) (response uint uint)) + (get-total-supply-fixed () (response uint uint)) + + + (mint (uint principal) (response bool uint)) + (burn (uint principal (string-ascii 72)) (response bool uint)) + (mint-fixed (uint principal) (response bool uint)) + (burn-fixed (uint principal) (response bool uint)) + ) +) \ No newline at end of file diff --git a/sbtc-ops/smart-contract/contracts/trait-sip-010.clar b/sbtc-ops/smart-contract/contracts/trait-sip-010.clar new file mode 100644 index 00000000..8fe98337 --- /dev/null +++ b/sbtc-ops/smart-contract/contracts/trait-sip-010.clar @@ -0,0 +1,38 @@ +;; trait-sip-010 + +;; sip-010-trait +(define-trait sip-010-trait + ( + ;; Transfer from the caller to a new principal + (transfer (uint principal principal (optional (buff 34))) (response bool uint)) + + ;; the human readable name of the token + (get-name () (response (string-ascii 32) uint)) + + ;; the ticker symbol, or empty if none + (get-symbol () (response (string-ascii 32) uint)) + + ;; the number of decimals used, e.g. 6 would mean 1_000_000 represents 1 token + (get-decimals () (response uint uint)) + + ;; the balance of the passed principal + (get-balance (principal) (response uint uint)) + + ;; the current total supply (which does not need to be a constant) + (get-total-supply () (response uint uint)) + + ;; an optional URI that represents metadata of this token + (get-token-uri () (response (optional (string-utf8 256)) uint)) + + ;; helper functions for 8-digit fixed notation + (transfer-fixed (uint principal principal (optional (buff 34))) (response bool uint)) + (get-balance-fixed (principal) (response uint uint)) + (get-total-supply-fixed () (response uint uint)) + + + (mint (uint principal) (response bool uint)) + (burn (uint principal) (response bool uint)) + (mint-fixed (uint principal) (response bool uint)) + (burn-fixed (uint principal) (response bool uint)) + ) +) \ No newline at end of file diff --git a/sbtc-ops/smart-contract/costs/contract-costs.txt b/sbtc-ops/smart-contract/costs/contract-costs.txt new file mode 100644 index 00000000..296fbddc --- /dev/null +++ b/sbtc-ops/smart-contract/costs/contract-costs.txt @@ -0,0 +1,338 @@ +Contract calls cost synthesis +┌──────────────────────────────┬─────────┬─────────────────────────┬─────────────────────┬─────────────────────────┬─────────────────────┬───────────────────────┬──────────────┐ +│ │ +│ ✨ ST000000000000000000002AMW42H.pox-3 │ +│ │ +├──────────────────────────────┼─────────┼─────────────────────────┼─────────────────────┼─────────────────────────┼─────────────────────┼───────────────────────┼──────────────┤ +│ │ # Calls │ Runtime (units) │ Read Count │ Read Length (bytes) │ Write Count │ Write Length (bytes) │ Tx per Block │ +├──────────────────────────────┼─────────┼─────────────────────────┼─────────────────────┼─────────────────────────┼─────────────────────┼───────────────────────┼──────────────┤ +│ allow-contract-caller │ 21 │ min: 75456 (0.002%) │ min: 4 (0.027%) │ min: 67508 (0.068%) │ min: 1 (0.007%) │ min: 113 (0.001%) │ 1481 │ +│ │ │ max: 75456 (0.002%) │ max: 4 (0.027%) │ max: 67508 (0.068%) │ max: 1 (0.007%) │ max: 113 (0.001%) │ 1481 │ +│ │ │ avg: 75456 (0.002%) │ avg: 4 (0.027%) │ avg: 67508 (0.068%) │ avg: 1 (0.007%) │ avg: 113 (0.001%) │ 1481 │ +├──────────────────────────────┼─────────┼─────────────────────────┼─────────────────────┼─────────────────────────┼─────────────────────┼───────────────────────┼──────────────┤ +│ disallow-contract-caller │ 1 │ min: 73413 (0.001%) │ min: 4 (0.027%) │ min: 67508 (0.068%) │ min: 1 (0.007%) │ min: 93 (0.001%) │ 1481 │ +│ │ │ max: 73413 (0.001%) │ max: 4 (0.027%) │ max: 67508 (0.068%) │ max: 1 (0.007%) │ max: 93 (0.001%) │ 1481 │ +│ │ │ avg: 73413 (0.001%) │ avg: 4 (0.027%) │ avg: 67508 (0.068%) │ avg: 1 (0.007%) │ avg: 93 (0.001%) │ 1481 │ +├──────────────────────────────┼─────────┼─────────────────────────┼─────────────────────┼─────────────────────────┼─────────────────────┼───────────────────────┼──────────────┤ +│ get-partial-stacked-by-cycle │ 7 │ min: 73735 (0.001%) │ min: 4 (0.027%) │ min: 67658 (0.068%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 1478 │ +│ │ │ max: 73735 (0.001%) │ max: 4 (0.027%) │ max: 67658 (0.068%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 1478 │ +│ │ │ avg: 73735 (0.001%) │ avg: 4 (0.027%) │ avg: 67658 (0.068%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 1478 │ +├──────────────────────────────┼─────────┼─────────────────────────┼─────────────────────┼─────────────────────────┼─────────────────────┼───────────────────────┼──────────────┤ +│ get-reward-set-pox-address │ 10 │ min: 70797 (0.001%) │ min: 4 (0.027%) │ min: 67567 (0.068%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 1480 │ +│ │ │ max: 70903 (0.001%) │ max: 4 (0.027%) │ max: 67673 (0.068%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 1477 │ +│ │ │ avg: 70871 (0.001%) │ avg: 4 (0.027%) │ avg: 67641 (0.068%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 1477 │ +├──────────────────────────────┼─────────┼─────────────────────────┼─────────────────────┼─────────────────────────┼─────────────────────┼───────────────────────┼──────────────┤ +│ Block Limits │ - │ 5000000000 │ 15000 │ 100000000 │ 15000 │ 15000000 │ │ +└──────────────────────────────┴─────────┴─────────────────────────┴─────────────────────┴─────────────────────────┴─────────────────────┴───────────────────────┴──────────────┘ + +┌───────────────────────┬──────────┬─────────────────────────┬──────────────────────┬─────────────────────────┬─────────────────────┬───────────────────────┬──────────────┐ +│ │ +│ ✨ ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.Wrapped-Bitcoin │ +│ │ +├───────────────────────┼──────────┼─────────────────────────┼──────────────────────┼─────────────────────────┼─────────────────────┼───────────────────────┼──────────────┤ +│ │ # Calls │ Runtime (units) │ Read Count │ Read Length (bytes) │ Write Count │ Write Length (bytes) │ Tx per Block │ +├───────────────────────┼──────────┼─────────────────────────┼──────────────────────┼─────────────────────────┼─────────────────────┼───────────────────────┼──────────────┤ +│ add-principal-to-role │ 1 │ min: 31472 (0.001%) │ min: 5 (0.033%) │ min: 10530 (0.011%) │ min: 1 (0.007%) │ min: 73 (0.000%) │ 3000 │ +│ │ │ max: 31472 (0.001%) │ max: 5 (0.033%) │ max: 10530 (0.011%) │ max: 1 (0.007%) │ max: 73 (0.000%) │ 3000 │ +│ │ │ avg: 31472 (0.001%) │ avg: 5 (0.033%) │ avg: 10530 (0.011%) │ avg: 1 (0.007%) │ avg: 73 (0.000%) │ 3000 │ +├───────────────────────┼──────────┼─────────────────────────┼──────────────────────┼─────────────────────────┼─────────────────────┼───────────────────────┼──────────────┤ +│ initialize │ 1 │ min: 22777 (0.000%) │ min: 10 (0.067%) │ min: 10482 (0.010%) │ min: 5 (0.033%) │ min: 124 (0.001%) │ 1500 │ +│ │ │ max: 22777 (0.000%) │ max: 10 (0.067%) │ max: 10482 (0.010%) │ max: 5 (0.033%) │ max: 124 (0.001%) │ 1500 │ +│ │ │ avg: 22777 (0.000%) │ avg: 10 (0.067%) │ avg: 10482 (0.010%) │ avg: 5 (0.033%) │ avg: 124 (0.001%) │ 1500 │ +├───────────────────────┼──────────┼─────────────────────────┼──────────────────────┼─────────────────────────┼─────────────────────┼───────────────────────┼──────────────┤ +│ mint-tokens │ 9 │ min: 26325 (0.001%) │ min: 6 (0.040%) │ min: 10531 (0.011%) │ min: 2 (0.013%) │ min: 1 (0.000%) │ 2500 │ +│ │ │ max: 26325 (0.001%) │ max: 6 (0.040%) │ max: 10531 (0.011%) │ max: 2 (0.013%) │ max: 1 (0.000%) │ 2500 │ +│ │ │ avg: 26325 (0.001%) │ avg: 6 (0.040%) │ avg: 10531 (0.011%) │ avg: 2 (0.013%) │ avg: 1 (0.000%) │ 2500 │ +├───────────────────────┼──────────┼─────────────────────────┼──────────────────────┼─────────────────────────┼─────────────────────┼───────────────────────┼──────────────┤ +│ Block Limits │ - │ 5000000000 │ 15000 │ 100000000 │ 15000 │ 15000000 │ │ +└───────────────────────┴──────────┴─────────────────────────┴──────────────────────┴─────────────────────────┴─────────────────────┴───────────────────────┴──────────────┘ + +┌───────────────────────┬──────────┬─────────────────────────┬─────────────────────┬────────────────────────┬─────────────────────┬──────────────────────┬──────────────┐ +│ │ +│ ✨ ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.alex-vault-v1-1 │ +│ │ +├───────────────────────┼──────────┼─────────────────────────┼─────────────────────┼────────────────────────┼─────────────────────┼──────────────────────┼──────────────┤ +│ │ # Calls │ Runtime (units) │ Read Count │ Read Length (bytes) │ Write Count │ Write Length (bytes) │ Tx per Block │ +├───────────────────────┼──────────┼─────────────────────────┼─────────────────────┼────────────────────────┼─────────────────────┼──────────────────────┼──────────────┤ +│ set-approved-token │ 2 │ min: 11176 (0.000%) │ min: 5 (0.033%) │ min: 6399 (0.006%) │ min: 1 (0.007%) │ min: 36 (0.000%) │ 3000 │ +│ │ │ max: 11176 (0.000%) │ max: 5 (0.033%) │ max: 6399 (0.006%) │ max: 1 (0.007%) │ max: 36 (0.000%) │ 3000 │ +│ │ │ avg: 11176 (0.000%) │ avg: 5 (0.033%) │ avg: 6399 (0.006%) │ avg: 1 (0.007%) │ avg: 36 (0.000%) │ 3000 │ +├───────────────────────┼──────────┼─────────────────────────┼─────────────────────┼────────────────────────┼─────────────────────┼──────────────────────┼──────────────┤ +│ set-approved-contract │ 1 │ min: 11208 (0.000%) │ min: 5 (0.033%) │ min: 6399 (0.006%) │ min: 1 (0.007%) │ min: 44 (0.000%) │ 3000 │ +│ │ │ max: 11208 (0.000%) │ max: 5 (0.033%) │ max: 6399 (0.006%) │ max: 1 (0.007%) │ max: 44 (0.000%) │ 3000 │ +│ │ │ avg: 11208 (0.000%) │ avg: 5 (0.033%) │ avg: 6399 (0.006%) │ avg: 1 (0.007%) │ avg: 44 (0.000%) │ 3000 │ +├───────────────────────┼──────────┼─────────────────────────┼─────────────────────┼────────────────────────┼─────────────────────┼──────────────────────┼──────────────┤ +│ Block Limits │ - │ 5000000000 │ 15000 │ 100000000 │ 15000 │ 15000000 │ │ +└───────────────────────┴──────────┴─────────────────────────┴─────────────────────┴────────────────────────┴─────────────────────┴──────────────────────┴──────────────┘ + +┌───────────────────┬──────────┬──────────────────────────┬──────────────────────┬─────────────────────────┬──────────────────────┬────────────────────────┬──────────────┐ +│ │ +│ ✨ ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.amm-swap-pool-v1-1 │ +│ │ +├───────────────────┼──────────┼──────────────────────────┼──────────────────────┼─────────────────────────┼──────────────────────┼────────────────────────┼──────────────┤ +│ │ # Calls │ Runtime (units) │ Read Count │ Read Length (bytes) │ Write Count │ Write Length (bytes) │ Tx per Block │ +├───────────────────┼──────────┼──────────────────────────┼──────────────────────┼─────────────────────────┼──────────────────────┼────────────────────────┼──────────────┤ +│ create-pool │ 1 │ min: 365534 (0.007%) │ min: 45 (0.300%) │ min: 80586 (0.081%) │ min: 12 (0.080%) │ min: 1513 (0.010%) │ 333 │ +│ │ │ max: 365534 (0.007%) │ max: 45 (0.300%) │ max: 80586 (0.081%) │ max: 12 (0.080%) │ max: 1513 (0.010%) │ 333 │ +│ │ │ avg: 365534 (0.007%) │ avg: 45 (0.300%) │ avg: 80586 (0.081%) │ avg: 12 (0.080%) │ avg: 1513 (0.010%) │ 333 │ +├───────────────────┼──────────┼──────────────────────────┼──────────────────────┼─────────────────────────┼──────────────────────┼────────────────────────┼──────────────┤ +│ get-pool-details │ 1 │ min: 53648 (0.001%) │ min: 4 (0.027%) │ min: 47346 (0.047%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2112 │ +│ │ │ max: 53648 (0.001%) │ max: 4 (0.027%) │ max: 47346 (0.047%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 2112 │ +│ │ │ avg: 53648 (0.001%) │ avg: 4 (0.027%) │ avg: 47346 (0.047%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 2112 │ +├───────────────────┼──────────┼──────────────────────────┼──────────────────────┼─────────────────────────┼──────────────────────┼────────────────────────┼──────────────┤ +│ set-max-in-ratio │ 1 │ min: 73368 (0.001%) │ min: 5 (0.033%) │ min: 47346 (0.047%) │ min: 1 (0.007%) │ min: 601 (0.004%) │ 2112 │ +│ │ │ max: 73368 (0.001%) │ max: 5 (0.033%) │ max: 47346 (0.047%) │ max: 1 (0.007%) │ max: 601 (0.004%) │ 2112 │ +│ │ │ avg: 73368 (0.001%) │ avg: 5 (0.033%) │ avg: 47346 (0.047%) │ avg: 1 (0.007%) │ avg: 601 (0.004%) │ 2112 │ +├───────────────────┼──────────┼──────────────────────────┼──────────────────────┼─────────────────────────┼──────────────────────┼────────────────────────┼──────────────┤ +│ set-max-out-ratio │ 1 │ min: 73372 (0.001%) │ min: 5 (0.033%) │ min: 47346 (0.047%) │ min: 1 (0.007%) │ min: 601 (0.004%) │ 2112 │ +│ │ │ max: 73372 (0.001%) │ max: 5 (0.033%) │ max: 47346 (0.047%) │ max: 1 (0.007%) │ max: 601 (0.004%) │ 2112 │ +│ │ │ avg: 73372 (0.001%) │ avg: 5 (0.033%) │ avg: 47346 (0.047%) │ avg: 1 (0.007%) │ avg: 601 (0.004%) │ 2112 │ +├───────────────────┼──────────┼──────────────────────────┼──────────────────────┼─────────────────────────┼──────────────────────┼────────────────────────┼──────────────┤ +│ set-start-block │ 1 │ min: 73364 (0.001%) │ min: 5 (0.033%) │ min: 47346 (0.047%) │ min: 1 (0.007%) │ min: 601 (0.004%) │ 2112 │ +│ │ │ max: 73364 (0.001%) │ max: 5 (0.033%) │ max: 47346 (0.047%) │ max: 1 (0.007%) │ max: 601 (0.004%) │ 2112 │ +│ │ │ avg: 73364 (0.001%) │ avg: 5 (0.033%) │ avg: 47346 (0.047%) │ avg: 1 (0.007%) │ avg: 601 (0.004%) │ 2112 │ +├───────────────────┼──────────┼──────────────────────────┼──────────────────────┼─────────────────────────┼──────────────────────┼────────────────────────┼──────────────┤ +│ Block Limits │ - │ 5000000000 │ 15000 │ 100000000 │ 15000 │ 15000000 │ │ +└───────────────────┴──────────┴──────────────────────────┴──────────────────────┴─────────────────────────┴──────────────────────┴────────────────────────┴──────────────┘ + +┌─────────────────────┬──────────┬──────────────────────────┬──────────────────────┬──────────────────────────┬─────────────────────┬───────────────────────┬──────────────┐ +│ │ +│ ✨ ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.bridge-contract │ +│ │ +├─────────────────────┼──────────┼──────────────────────────┼──────────────────────┼──────────────────────────┼─────────────────────┼───────────────────────┼──────────────┤ +│ │ # Calls │ Runtime (units) │ Read Count │ Read Length (bytes) │ Write Count │ Write Length (bytes) │ Tx per Block │ +├─────────────────────┼──────────┼──────────────────────────┼──────────────────────┼──────────────────────────┼─────────────────────┼───────────────────────┼──────────────┤ +│ swap-bridge-stx-btc │ 1 │ min: 666928 (0.013%) │ min: 75 (0.500%) │ min: 230321 (0.230%) │ min: 9 (0.060%) │ min: 870 (0.006%) │ 200 │ +│ │ │ max: 666928 (0.013%) │ max: 75 (0.500%) │ max: 230321 (0.230%) │ max: 9 (0.060%) │ max: 870 (0.006%) │ 200 │ +│ │ │ avg: 666928 (0.013%) │ avg: 75 (0.500%) │ avg: 230321 (0.230%) │ avg: 9 (0.060%) │ avg: 870 (0.006%) │ 200 │ +├─────────────────────┼──────────┼──────────────────────────┼──────────────────────┼──────────────────────────┼─────────────────────┼───────────────────────┼──────────────┤ +│ swap-helper │ 16 │ min: 572366 (0.011%) │ min: 58 (0.387%) │ min: 181389 (0.181%) │ min: 5 (0.033%) │ min: 655 (0.004%) │ 258 │ +│ │ │ max: 573591 (0.011%) │ max: 58 (0.387%) │ max: 182838 (0.183%) │ max: 5 (0.033%) │ max: 655 (0.004%) │ 258 │ +│ │ │ avg: 572985 (0.011%) │ avg: 58 (0.387%) │ avg: 182120 (0.182%) │ avg: 5 (0.033%) │ avg: 655 (0.004%) │ 258 │ +├─────────────────────┼──────────┼──────────────────────────┼──────────────────────┼──────────────────────────┼─────────────────────┼───────────────────────┼──────────────┤ +│ Block Limits │ - │ 5000000000 │ 15000 │ 100000000 │ 15000 │ 15000000 │ │ +└─────────────────────┴──────────┴──────────────────────────┴──────────────────────┴──────────────────────────┴─────────────────────┴───────────────────────┴──────────────┘ + +┌───────────────────┬───────────┬─────────────────────────┬──────────────────────┬─────────────────────────┬─────────────────────┬───────────────────────┬──────────────┐ +│ │ +│ ✨ ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.degen-bridge-testnet-v3 │ +│ │ +├───────────────────┼───────────┼─────────────────────────┼──────────────────────┼─────────────────────────┼─────────────────────┼───────────────────────┼──────────────┤ +│ │ # Calls │ Runtime (units) │ Read Count │ Read Length (bytes) │ Write Count │ Write Length (bytes) │ Tx per Block │ +├───────────────────┼───────────┼─────────────────────────┼──────────────────────┼─────────────────────────┼─────────────────────┼───────────────────────┼──────────────┤ +│ register-supplier │ 1 │ min: 97743 (0.002%) │ min: 24 (0.160%) │ min: 48927 (0.049%) │ min: 9 (0.060%) │ min: 427 (0.003%) │ 625 │ +│ │ │ max: 97743 (0.002%) │ max: 24 (0.160%) │ max: 48927 (0.049%) │ max: 9 (0.060%) │ max: 427 (0.003%) │ 625 │ +│ │ │ avg: 97743 (0.002%) │ avg: 24 (0.160%) │ avg: 48927 (0.049%) │ avg: 9 (0.060%) │ avg: 427 (0.003%) │ 625 │ +├───────────────────┼───────────┼─────────────────────────┼──────────────────────┼─────────────────────────┼─────────────────────┼───────────────────────┼──────────────┤ +│ Block Limits │ - │ 5000000000 │ 15000 │ 100000000 │ 15000 │ 15000000 │ │ +└───────────────────┴───────────┴─────────────────────────┴──────────────────────┴─────────────────────────┴─────────────────────┴───────────────────────┴──────────────┘ + +┌──────────────────────────────────────────┬─────────┬───────────────────────────┬─────────────────────────┬──────────────────────────┬───────────────────────┬─────────────────────────┬──────────────┐ +│ │ +│ ✨ ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.mining-pool │ +│ │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ │ # Calls │ Runtime (units) │ Read Count │ Read Length (bytes) │ Write Count │ Write Length (bytes) │ Tx per Block │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ add-pending-miners-to-pool │ 31 │ min: 49707 (0.001%) │ min: 4 (0.027%) │ min: 47593 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2101 │ +│ │ │ max: 5504472 (0.110%) │ max: 1216 (8.107%) │ max: 74653 (0.075%) │ max: 902 (6.013%) │ max: 53908 (0.359%) │ 12 │ +│ │ │ avg: 1564162 (0.031%) │ avg: 345 (2.300%) │ avg: 55131 (0.055%) │ avg: 251 (1.673%) │ avg: 14920 (0.099%) │ 800 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ ask-to-join │ 2962 │ min: 132259 (0.003%) │ min: 11 (0.073%) │ min: 54223 (0.054%) │ min: 4 (0.027%) │ min: 6785 (0.045%) │ 1363 │ +│ │ │ max: 81897 (0.002%) │ max: 11 (0.073%) │ max: 47667 (0.048%) │ max: 4 (0.027%) │ max: 229 (0.002%) │ 1363 │ +│ │ │ avg: 103514 (0.002%) │ avg: 11 (0.073%) │ avg: 50481 (0.050%) │ avg: 4 (0.027%) │ avg: 3043 (0.020%) │ 1363 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ deposit-stx │ 699 │ min: 57670 (0.001%) │ min: 6 (0.040%) │ min: 47611 (0.048%) │ min: 2 (0.013%) │ min: 42 (0.000%) │ 2100 │ +│ │ │ max: 57670 (0.001%) │ max: 6 (0.040%) │ max: 47611 (0.048%) │ max: 2 (0.013%) │ max: 42 (0.000%) │ 2100 │ +│ │ │ avg: 57670 (0.001%) │ avg: 6 (0.040%) │ avg: 47611 (0.048%) │ avg: 2 (0.013%) │ avg: 42 (0.000%) │ 2100 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ end-vote-notifier │ 2 │ min: 4073122 (0.081%) │ min: 916 (6.107%) │ min: 73912 (0.074%) │ min: 604 (4.027%) │ min: 26195 (0.175%) │ 16 │ +│ │ │ max: 4088222 (0.082%) │ max: 923 (6.153%) │ max: 74111 (0.074%) │ max: 607 (4.047%) │ max: 26312 (0.175%) │ 16 │ +│ │ │ avg: 4080672 (0.082%) │ avg: 919 (6.127%) │ avg: 74011 (0.074%) │ avg: 605 (4.033%) │ avg: 26253 (0.175%) │ 16 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ get-all-data-miners-proposed-for-removal │ 1 │ min: 121133 (0.002%) │ min: 12 (0.080%) │ min: 47878 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 1250 │ +│ │ │ max: 121133 (0.002%) │ max: 12 (0.080%) │ max: 47878 (0.048%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 1250 │ +│ │ │ avg: 121133 (0.002%) │ avg: 12 (0.080%) │ avg: 47878 (0.048%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 1250 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ get-all-data-waiting-miners │ 2 │ min: 121133 (0.002%) │ min: 12 (0.080%) │ min: 47878 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 1250 │ +│ │ │ max: 141169 (0.003%) │ max: 16 (0.107%) │ max: 47940 (0.048%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 937 │ +│ │ │ avg: 131151 (0.003%) │ avg: 14 (0.093%) │ avg: 47909 (0.048%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 1093 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ get-balance │ 507 │ min: 49359 (0.001%) │ min: 4 (0.027%) │ min: 47610 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2100 │ +│ │ │ max: 49377 (0.001%) │ max: 4 (0.027%) │ max: 47628 (0.048%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 2099 │ +│ │ │ avg: 49373 (0.001%) │ avg: 4 (0.027%) │ avg: 47624 (0.048%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 2099 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ get-k │ 13 │ min: 48172 (0.001%) │ min: 4 (0.027%) │ min: 47605 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2100 │ +│ │ │ max: 48172 (0.001%) │ max: 4 (0.027%) │ max: 47605 (0.048%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 2100 │ +│ │ │ avg: 48172 (0.001%) │ avg: 4 (0.027%) │ avg: 47605 (0.048%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 2100 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ get-max-voted-notifier │ 2 │ min: 48177 (0.001%) │ min: 4 (0.027%) │ min: 47610 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2100 │ +│ │ │ max: 48177 (0.001%) │ max: 4 (0.027%) │ max: 47610 (0.048%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 2100 │ +│ │ │ avg: 48177 (0.001%) │ avg: 4 (0.027%) │ avg: 47610 (0.048%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 2100 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ get-max-votes-notifier │ 2 │ min: 48172 (0.001%) │ min: 4 (0.027%) │ min: 47605 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2100 │ +│ │ │ max: 48172 (0.001%) │ max: 4 (0.027%) │ max: 47605 (0.048%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 2100 │ +│ │ │ avg: 48172 (0.001%) │ avg: 4 (0.027%) │ avg: 47605 (0.048%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 2100 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ get-miners-list │ 48 │ min: 48182 (0.001%) │ min: 4 (0.027%) │ min: 47615 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2100 │ +│ │ │ max: 54760 (0.001%) │ max: 4 (0.027%) │ max: 54193 (0.054%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 1845 │ +│ │ │ avg: 49529 (0.001%) │ avg: 4 (0.027%) │ avg: 48962 (0.049%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 2046 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ get-notifier │ 3 │ min: 48177 (0.001%) │ min: 4 (0.027%) │ min: 47610 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2100 │ +│ │ │ max: 48177 (0.001%) │ max: 4 (0.027%) │ max: 47610 (0.048%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 2100 │ +│ │ │ avg: 48177 (0.001%) │ avg: 4 (0.027%) │ avg: 47610 (0.048%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 2100 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ get-notifier-vote-number │ 252 │ min: 51288 (0.001%) │ min: 4 (0.027%) │ min: 47631 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2099 │ +│ │ │ max: 53059 (0.001%) │ max: 4 (0.027%) │ max: 47666 (0.048%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 2097 │ +│ │ │ avg: 53044 (0.001%) │ avg: 4 (0.027%) │ avg: 47665 (0.048%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 2097 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ get-notifier-vote-status │ 7 │ min: 48156 (0.001%) │ min: 4 (0.027%) │ min: 47589 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2101 │ +│ │ │ max: 48156 (0.001%) │ max: 4 (0.027%) │ max: 47589 (0.048%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 2101 │ +│ │ │ avg: 48156 (0.001%) │ avg: 4 (0.027%) │ avg: 47589 (0.048%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 2101 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ get-pending-accept-list │ 51 │ min: 48160 (0.001%) │ min: 4 (0.027%) │ min: 47593 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2101 │ +│ │ │ max: 54738 (0.001%) │ max: 4 (0.027%) │ max: 54171 (0.054%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 1846 │ +│ │ │ avg: 49269 (0.001%) │ avg: 4 (0.027%) │ avg: 48702 (0.049%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 2057 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ get-reward-at-block-read │ 3 │ min: 62555 (0.001%) │ min: 5 (0.033%) │ min: 47589 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2101 │ +│ │ │ max: 62555 (0.001%) │ max: 5 (0.033%) │ max: 47589 (0.048%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 2101 │ +│ │ │ avg: 62555 (0.001%) │ avg: 5 (0.033%) │ avg: 47589 (0.048%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 2101 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ get-waiting-list │ 34 │ min: 48160 (0.001%) │ min: 4 (0.027%) │ min: 47593 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2101 │ +│ │ │ max: 54738 (0.001%) │ max: 4 (0.027%) │ max: 54171 (0.054%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 1846 │ +│ │ │ avg: 50056 (0.001%) │ avg: 4 (0.027%) │ avg: 49489 (0.049%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 2026 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ leave-pool │ 405 │ min: 60219 (0.001%) │ min: 6 (0.040%) │ min: 47708 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2096 │ +│ │ │ max: 734563 (0.015%) │ max: 317 (2.113%) │ max: 61310 (0.061%) │ max: 4 (0.027%) │ max: 6674 (0.044%) │ 47 │ +│ │ │ avg: 402693 (0.008%) │ avg: 142 (0.947%) │ avg: 53435 (0.053%) │ avg: 4 (0.027%) │ avg: 2823 (0.019%) │ 174 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ propose-removal │ 13 │ min: 94784 (0.002%) │ min: 14 (0.093%) │ min: 47846 (0.048%) │ min: 3 (0.020%) │ min: 142 (0.001%) │ 1071 │ +│ │ │ max: 94783 (0.002%) │ max: 14 (0.093%) │ max: 47845 (0.048%) │ max: 3 (0.020%) │ max: 142 (0.001%) │ 1071 │ +│ │ │ avg: 94796 (0.002%) │ avg: 14 (0.093%) │ avg: 47847 (0.048%) │ avg: 3 (0.020%) │ avg: 143 (0.001%) │ 1071 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ reward-distribution │ 4 │ min: 53832 (0.001%) │ min: 5 (0.033%) │ min: 47640 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2099 │ +│ │ │ max: 2419027 (0.048%) │ max: 1521 (10.140%) │ max: 82825 (0.083%) │ max: 306 (2.040%) │ max: 12441 (0.083%) │ 9 │ +│ │ │ avg: 759700 (0.015%) │ avg: 394 (2.627%) │ avg: 56537 (0.057%) │ avg: 80 (0.533%) │ avg: 3201 (0.021%) │ 815 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ start-vote-notifier │ 3 │ min: 53703 (0.001%) │ min: 11 (0.073%) │ min: 47611 (0.048%) │ min: 4 (0.027%) │ min: 40 (0.000%) │ 1363 │ +│ │ │ max: 53703 (0.001%) │ max: 11 (0.073%) │ max: 47611 (0.048%) │ max: 4 (0.027%) │ max: 40 (0.000%) │ 1363 │ +│ │ │ avg: 53703 (0.001%) │ avg: 11 (0.073%) │ avg: 47611 (0.048%) │ avg: 4 (0.027%) │ avg: 40 (0.000%) │ 1363 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ try-enter-pool │ 2958 │ min: 51062 (0.001%) │ min: 4 (0.027%) │ min: 47623 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2099 │ +│ │ │ max: 881675 (0.018%) │ max: 330 (2.200%) │ max: 61497 (0.061%) │ max: 12 (0.080%) │ max: 7009 (0.047%) │ 45 │ +│ │ │ avg: 510651 (0.010%) │ avg: 139 (0.927%) │ avg: 55485 (0.055%) │ avg: 10 (0.067%) │ avg: 5291 (0.035%) │ 406 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ vote-negative-join-request │ 497 │ min: 84085 (0.002%) │ min: 11 (0.073%) │ min: 47855 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 1363 │ +│ │ │ max: 869058 (0.017%) │ max: 347 (2.313%) │ max: 62083 (0.062%) │ max: 10 (0.067%) │ max: 6974 (0.046%) │ 43 │ +│ │ │ avg: 469019 (0.009%) │ avg: 145 (0.967%) │ avg: 53118 (0.053%) │ avg: 8 (0.053%) │ avg: 2557 (0.017%) │ 183 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ vote-negative-remove-request │ 135 │ min: 49121 (0.001%) │ min: 3 (0.020%) │ min: 47587 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2101 │ +│ │ │ max: 2102015 (0.042%) │ max: 854 (5.693%) │ max: 86862 (0.087%) │ max: 110 (0.733%) │ max: 7518 (0.050%) │ 17 │ +│ │ │ avg: 203313 (0.004%) │ avg: 42 (0.280%) │ avg: 48813 (0.049%) │ avg: 2 (0.013%) │ avg: 203 (0.001%) │ 529 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ vote-notifier │ 352 │ min: 69737 (0.001%) │ min: 8 (0.053%) │ min: 47785 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 1875 │ +│ │ │ max: 4150953 (0.083%) │ max: 941 (6.273%) │ max: 74629 (0.075%) │ max: 612 (4.080%) │ max: 26616 (0.177%) │ 15 │ +│ │ │ avg: 118378 (0.002%) │ avg: 19 (0.127%) │ avg: 48177 (0.048%) │ avg: 3 (0.020%) │ avg: 237 (0.002%) │ 882 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ vote-positive-join-request │ 2771 │ min: 52518 (0.001%) │ min: 4 (0.027%) │ min: 47624 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2099 │ +│ │ │ max: 139110 (0.003%) │ max: 22 (0.147%) │ max: 48251 (0.048%) │ max: 2 (0.013%) │ max: 148 (0.001%) │ 681 │ +│ │ │ avg: 132336 (0.003%) │ avg: 21 (0.140%) │ avg: 48163 (0.048%) │ avg: 1 (0.007%) │ avg: 147 (0.001%) │ 712 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ vote-positive-remove-request │ 225 │ min: 53589 (0.001%) │ min: 4 (0.027%) │ min: 47624 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2099 │ +│ │ │ max: 3244433 (0.065%) │ max: 1356 (9.040%) │ max: 103820 (0.104%) │ max: 214 (1.427%) │ max: 21258 (0.142%) │ 11 │ +│ │ │ avg: 199967 (0.004%) │ avg: 40 (0.267%) │ avg: 48768 (0.049%) │ avg: 3 (0.020%) │ avg: 261 (0.002%) │ 452 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ withdraw-stx │ 504 │ min: 49894 (0.001%) │ min: 4 (0.027%) │ min: 47610 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2100 │ +│ │ │ max: 70202 (0.001%) │ max: 9 (0.060%) │ max: 47706 (0.048%) │ max: 3 (0.020%) │ max: 107 (0.001%) │ 1666 │ +│ │ │ avg: 62208 (0.001%) │ avg: 7 (0.047%) │ avg: 47671 (0.048%) │ avg: 1 (0.007%) │ avg: 64 (0.000%) │ 1838 │ +├──────────────────────────────────────────┼─────────┼───────────────────────────┼─────────────────────────┼──────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ Block Limits │ - │ 5000000000 │ 15000 │ 100000000 │ 15000 │ 15000000 │ │ +└──────────────────────────────────────────┴─────────┴───────────────────────────┴─────────────────────────┴──────────────────────────┴───────────────────────┴─────────────────────────┴──────────────┘ + +┌──────────────────────────────────────────┬──────────┬───────────────────────────┬────────────────────────┬─────────────────────────┬───────────────────────┬─────────────────────────┬──────────────┐ +│ │ +│ ✨ ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.mining-pool-5-blocks │ +│ │ +├──────────────────────────────────────────┼──────────┼───────────────────────────┼────────────────────────┼─────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ │ # Calls │ Runtime (units) │ Read Count │ Read Length (bytes) │ Write Count │ Write Length (bytes) │ Tx per Block │ +├──────────────────────────────────────────┼──────────┼───────────────────────────┼────────────────────────┼─────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ add-pending-miners-to-pool │ 2 │ min: 5504470 (0.110%) │ min: 1216 (8.107%) │ min: 74651 (0.075%) │ min: 902 (6.013%) │ min: 53908 (0.359%) │ 12 │ +│ │ │ max: 5504470 (0.110%) │ max: 1216 (8.107%) │ max: 74651 (0.075%) │ max: 902 (6.013%) │ max: 53908 (0.359%) │ 12 │ +│ │ │ avg: 5504470 (0.110%) │ avg: 1216 (8.107%) │ avg: 74651 (0.075%) │ avg: 902 (6.013%) │ avg: 53908 (0.359%) │ 12 │ +├──────────────────────────────────────────┼──────────┼───────────────────────────┼────────────────────────┼─────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ ask-to-join │ 599 │ min: 132257 (0.003%) │ min: 11 (0.073%) │ min: 54221 (0.054%) │ min: 4 (0.027%) │ min: 6785 (0.045%) │ 1363 │ +│ │ │ max: 81895 (0.002%) │ max: 11 (0.073%) │ max: 47665 (0.048%) │ max: 4 (0.027%) │ max: 229 (0.002%) │ 1363 │ +│ │ │ avg: 107033 (0.002%) │ avg: 11 (0.073%) │ avg: 50937 (0.051%) │ avg: 4 (0.027%) │ avg: 3501 (0.023%) │ 1363 │ +├──────────────────────────────────────────┼──────────┼───────────────────────────┼────────────────────────┼─────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ get-all-data-miners-proposed-for-removal │ 1 │ min: 121131 (0.002%) │ min: 12 (0.080%) │ min: 47876 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 1250 │ +│ │ │ max: 121131 (0.002%) │ max: 12 (0.080%) │ max: 47876 (0.048%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 1250 │ +│ │ │ avg: 121131 (0.002%) │ avg: 12 (0.080%) │ avg: 47876 (0.048%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 1250 │ +├──────────────────────────────────────────┼──────────┼───────────────────────────┼────────────────────────┼─────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ get-all-data-waiting-miners │ 2 │ min: 121131 (0.002%) │ min: 12 (0.080%) │ min: 47876 (0.048%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 1250 │ +│ │ │ max: 141167 (0.003%) │ max: 16 (0.107%) │ max: 47938 (0.048%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 937 │ +│ │ │ avg: 131149 (0.003%) │ avg: 14 (0.093%) │ avg: 47907 (0.048%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 1093 │ +├──────────────────────────────────────────┼──────────┼───────────────────────────┼────────────────────────┼─────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ get-miners-list │ 2 │ min: 54758 (0.001%) │ min: 4 (0.027%) │ min: 54191 (0.054%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 1845 │ +│ │ │ max: 54758 (0.001%) │ max: 4 (0.027%) │ max: 54191 (0.054%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 1845 │ +│ │ │ avg: 54758 (0.001%) │ avg: 4 (0.027%) │ avg: 54191 (0.054%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 1845 │ +├──────────────────────────────────────────┼──────────┼───────────────────────────┼────────────────────────┼─────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ get-pending-accept-list │ 2 │ min: 54736 (0.001%) │ min: 4 (0.027%) │ min: 54169 (0.054%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 1846 │ +│ │ │ max: 54736 (0.001%) │ max: 4 (0.027%) │ max: 54169 (0.054%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 1846 │ +│ │ │ avg: 54736 (0.001%) │ avg: 4 (0.027%) │ avg: 54169 (0.054%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 1846 │ +├──────────────────────────────────────────┼──────────┼───────────────────────────┼────────────────────────┼─────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ get-waiting-list │ 2 │ min: 54736 (0.001%) │ min: 4 (0.027%) │ min: 54169 (0.054%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 1846 │ +│ │ │ max: 54736 (0.001%) │ max: 4 (0.027%) │ max: 54169 (0.054%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 1846 │ +│ │ │ avg: 54736 (0.001%) │ avg: 4 (0.027%) │ avg: 54169 (0.054%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 1846 │ +├──────────────────────────────────────────┼──────────┼───────────────────────────┼────────────────────────┼─────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ propose-removal │ 1 │ min: 94781 (0.002%) │ min: 14 (0.093%) │ min: 47843 (0.048%) │ min: 3 (0.020%) │ min: 142 (0.001%) │ 1071 │ +│ │ │ max: 94781 (0.002%) │ max: 14 (0.093%) │ max: 47843 (0.048%) │ max: 3 (0.020%) │ max: 142 (0.001%) │ 1071 │ +│ │ │ avg: 94781 (0.002%) │ avg: 14 (0.093%) │ avg: 47843 (0.048%) │ avg: 3 (0.020%) │ avg: 142 (0.001%) │ 1071 │ +├──────────────────────────────────────────┼──────────┼───────────────────────────┼────────────────────────┼─────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ try-enter-pool │ 598 │ min: 366729 (0.007%) │ min: 32 (0.213%) │ min: 54641 (0.055%) │ min: 12 (0.080%) │ min: 7009 (0.047%) │ 468 │ +│ │ │ max: 881673 (0.018%) │ max: 330 (2.200%) │ max: 61495 (0.061%) │ max: 12 (0.080%) │ max: 7009 (0.047%) │ 45 │ +│ │ │ avg: 624201 (0.012%) │ avg: 181 (1.207%) │ avg: 58068 (0.058%) │ avg: 12 (0.080%) │ avg: 7009 (0.047%) │ 117 │ +├──────────────────────────────────────────┼──────────┼───────────────────────────┼────────────────────────┼─────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ vote-negative-join-request │ 90 │ min: 174047 (0.003%) │ min: 32 (0.213%) │ min: 48394 (0.048%) │ min: 2 (0.013%) │ min: 148 (0.001%) │ 468 │ +│ │ │ max: 181290 (0.004%) │ max: 33 (0.220%) │ max: 48488 (0.048%) │ max: 2 (0.013%) │ max: 148 (0.001%) │ 454 │ +│ │ │ avg: 181209 (0.004%) │ avg: 32 (0.213%) │ avg: 48486 (0.048%) │ avg: 2 (0.013%) │ avg: 148 (0.001%) │ 454 │ +├──────────────────────────────────────────┼──────────┼───────────────────────────┼────────────────────────┼─────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ vote-positive-join-request │ 807 │ min: 131865 (0.003%) │ min: 21 (0.140%) │ min: 48155 (0.048%) │ min: 2 (0.013%) │ min: 148 (0.001%) │ 714 │ +│ │ │ max: 139108 (0.003%) │ max: 22 (0.147%) │ max: 48249 (0.048%) │ max: 2 (0.013%) │ max: 148 (0.001%) │ 681 │ +│ │ │ avg: 133731 (0.003%) │ avg: 21 (0.140%) │ avg: 48179 (0.048%) │ avg: 2 (0.013%) │ avg: 148 (0.001%) │ 705 │ +├──────────────────────────────────────────┼──────────┼───────────────────────────┼────────────────────────┼─────────────────────────┼───────────────────────┼─────────────────────────┼──────────────┤ +│ Block Limits │ - │ 5000000000 │ 15000 │ 100000000 │ 15000 │ 15000000 │ │ +└──────────────────────────────────────────┴──────────┴───────────────────────────┴────────────────────────┴─────────────────────────┴───────────────────────┴─────────────────────────┴──────────────┘ + +┌────────────────────────────────┬──────────┬───────────────────────────┬──────────────────────┬──────────────────────────┬──────────────────────┬────────────────────────┬──────────────┐ +│ │ +│ ✨ ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.stacking-pool-test │ +│ │ +├────────────────────────────────┼──────────┼───────────────────────────┼──────────────────────┼──────────────────────────┼──────────────────────┼────────────────────────┼──────────────┤ +│ │ # Calls │ Runtime (units) │ Read Count │ Read Length (bytes) │ Write Count │ Write Length (bytes) │ Tx per Block │ +├────────────────────────────────┼──────────┼───────────────────────────┼──────────────────────┼──────────────────────────┼──────────────────────┼────────────────────────┼──────────────┤ +│ delegate-stx │ 22 │ min: 182227 (0.004%) │ min: 14 (0.093%) │ min: 170211 (0.170%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 587 │ +│ │ │ max: 1005258 (0.020%) │ max: 97 (0.647%) │ max: 511898 (0.512%) │ max: 14 (0.093%) │ max: 1567 (0.010%) │ 154 │ +│ │ │ avg: 878063 (0.018%) │ avg: 79 (0.527%) │ avg: 465102 (0.465%) │ avg: 10 (0.067%) │ avg: 1096 (0.007%) │ 223 │ +├────────────────────────────────┼──────────┼───────────────────────────┼──────────────────────┼──────────────────────────┼──────────────────────┼────────────────────────┼──────────────┤ +│ deposit-stx-liquidity-provider │ 1 │ min: 45749 (0.001%) │ min: 10 (0.067%) │ min: 35086 (0.035%) │ min: 3 (0.020%) │ min: 37 (0.000%) │ 1500 │ +│ │ │ max: 45749 (0.001%) │ max: 10 (0.067%) │ max: 35086 (0.035%) │ max: 3 (0.020%) │ max: 37 (0.000%) │ 1500 │ +│ │ │ avg: 45749 (0.001%) │ avg: 10 (0.067%) │ avg: 35086 (0.035%) │ avg: 3 (0.020%) │ avg: 37 (0.000%) │ 1500 │ +├────────────────────────────────┼──────────┼───────────────────────────┼──────────────────────┼──────────────────────────┼──────────────────────┼────────────────────────┼──────────────┤ +│ get-SC-total-balance │ 1 │ min: 35593 (0.001%) │ min: 4 (0.027%) │ min: 35026 (0.035%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2855 │ +│ │ │ max: 35593 (0.001%) │ max: 4 (0.027%) │ max: 35026 (0.035%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 2855 │ +│ │ │ avg: 35593 (0.001%) │ avg: 4 (0.027%) │ avg: 35026 (0.035%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 2855 │ +├────────────────────────────────┼──────────┼───────────────────────────┼──────────────────────┼──────────────────────────┼──────────────────────┼────────────────────────┼──────────────┤ +│ get-user-data │ 19 │ min: 38685 (0.001%) │ min: 4 (0.027%) │ min: 35044 (0.035%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 2853 │ +│ │ │ max: 38802 (0.001%) │ max: 4 (0.027%) │ max: 35161 (0.035%) │ max: 0 (0.000%) │ max: 0 (0.000%) │ 2844 │ +│ │ │ avg: 38795 (0.001%) │ avg: 4 (0.027%) │ avg: 35154 (0.035%) │ avg: 0 (0.000%) │ avg: 0 (0.000%) │ 2844 │ +├────────────────────────────────┼──────────┼───────────────────────────┼──────────────────────┼──────────────────────────┼──────────────────────┼────────────────────────┼──────────────┤ +│ join-stacking-pool │ 22 │ min: 107854 (0.002%) │ min: 7 (0.047%) │ min: 102608 (0.103%) │ min: 0 (0.000%) │ min: 0 (0.000%) │ 974 │ +│ │ │ max: 123348 (0.002%) │ max: 11 (0.073%) │ max: 102781 (0.103%) │ max: 2 (0.013%) │ max: 274 (0.002%) │ 972 │ +│ │ │ avg: 122344 (0.002%) │ avg: 10 (0.067%) │ avg: 102734 (0.103%) │ avg: 1 (0.007%) │ avg: 222 (0.001%) │ 972 │ +├────────────────────────────────┼──────────┼───────────────────────────┼──────────────────────┼──────────────────────────┼──────────────────────┼────────────────────────┼──────────────┤ +│ quit-stacking-pool │ 1 │ min: 200603 (0.004%) │ min: 17 (0.113%) │ min: 170362 (0.170%) │ min: 3 (0.020%) │ min: 157 (0.001%) │ 586 │ +│ │ │ max: 200603 (0.004%) │ max: 17 (0.113%) │ max: 170362 (0.170%) │ max: 3 (0.020%) │ max: 157 (0.001%) │ 586 │ +│ │ │ avg: 200603 (0.004%) │ avg: 17 (0.113%) │ avg: 170362 (0.170%) │ avg: 3 (0.020%) │ avg: 157 (0.001%) │ 586 │ +├────────────────────────────────┼──────────┼───────────────────────────┼──────────────────────┼──────────────────────────┼──────────────────────┼────────────────────────┼──────────────┤ +│ Block Limits │ - │ 5000000000 │ 15000 │ 100000000 │ 15000 │ 15000000 │ │ +└────────────────────────────────┴──────────┴───────────────────────────┴──────────────────────┴──────────────────────────┴──────────────────────┴────────────────────────┴──────────────┘ diff --git a/sbtc-ops/smart-contract/coverage/amber.png b/sbtc-ops/smart-contract/coverage/amber.png new file mode 100644 index 00000000..2cab170d Binary files /dev/null and b/sbtc-ops/smart-contract/coverage/amber.png differ diff --git a/sbtc-ops/smart-contract/coverage/contracts/Wrapped-Bitcoin.clar.func-sort-c.html b/sbtc-ops/smart-contract/coverage/contracts/Wrapped-Bitcoin.clar.func-sort-c.html new file mode 100644 index 00000000..f3a388db --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/Wrapped-Bitcoin.clar.func-sort-c.html @@ -0,0 +1,157 @@ + + + + + + + LCOV - coverage.lcov - contracts/Wrapped-Bitcoin.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - Wrapped-Bitcoin.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:265844.8 %
Date:2023-08-18 21:53:02Functions:71936.8 %
Branches:31816.7 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
burn-tokens0
get-balance0
get-decimals0
get-name0
get-symbol0
get-token-uri0
get-total-supply0
message-for-restriction0
remove-principal-from-role0
revoke-tokens0
set-token-uri0
update-blacklisted0
add-principal-to-role1
initialize1
mint-tokens9
has-role10
detect-transfer-restriction19
is-blacklisted19
transfer19
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/Wrapped-Bitcoin.clar.func.html b/sbtc-ops/smart-contract/coverage/contracts/Wrapped-Bitcoin.clar.func.html new file mode 100644 index 00000000..3d4d1091 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/Wrapped-Bitcoin.clar.func.html @@ -0,0 +1,157 @@ + + + + + + + LCOV - coverage.lcov - contracts/Wrapped-Bitcoin.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - Wrapped-Bitcoin.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:265844.8 %
Date:2023-08-18 21:53:02Functions:71936.8 %
Branches:31816.7 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
add-principal-to-role1
burn-tokens0
detect-transfer-restriction19
get-balance0
get-decimals0
get-name0
get-symbol0
get-token-uri0
get-total-supply0
has-role10
initialize1
is-blacklisted19
message-for-restriction0
mint-tokens9
remove-principal-from-role0
revoke-tokens0
set-token-uri0
transfer19
update-blacklisted0
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/Wrapped-Bitcoin.clar.gcov.html b/sbtc-ops/smart-contract/coverage/contracts/Wrapped-Bitcoin.clar.gcov.html new file mode 100644 index 00000000..1a866ac2 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/Wrapped-Bitcoin.clar.gcov.html @@ -0,0 +1,296 @@ + + + + + + + LCOV - coverage.lcov - contracts/Wrapped-Bitcoin.clar + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - Wrapped-Bitcoin.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:265844.8 %
Date:2023-08-18 21:53:02Functions:71936.8 %
Branches:31816.7 %
+
+ + + + + + + + +

+
           Branch data     Line data    Source code
+
+       1                 :            : ;; Implement the `ft-trait` trait defined in the `ft-trait` contract - SIP 10
+       2                 :            : ;; This can use sugared syntax in real deployment (unit tests do not allow)
+       3                 :            : (impl-trait .ft-trait.ft-trait)
+       4                 :            : 
+       5                 :            : ;; ;; Implement the token restriction trait
+       6                 :            : (impl-trait .restricted-token-trait.restricted-token-trait)
+       7                 :            : 
+       8                 :            : ;; Error returned for permission denied - stolen from http 403
+       9                 :            : (define-constant PERMISSION_DENIED_ERROR u403)
+      10                 :            : 
+      11                 :            : ;; Data variables specific to the deployed token contract
+      12                 :            : (define-data-var token-name (string-ascii 32) "")
+      13                 :            : (define-data-var token-symbol (string-ascii 32) "")
+      14                 :            : (define-data-var token-decimals uint u0)
+      15                 :            : 
+      16                 :            : ;; Track who deployed the token and whether it has been initialized
+      17                 :            : (define-data-var deployer-principal principal tx-sender)
+      18                 :            : (define-data-var is-initialized bool false)
+      19                 :            : 
+      20                 :            : ;; Meta Read Only Functions for reading details about the contract - conforms to SIP 10
+      21                 :            : ;; --------------------------------------------------------------------------
+      22                 :            : 
+      23                 :            : ;; Defines built in support functions for tokens used in this contract
+      24                 :            : ;; A second optional parameter can be added here to set an upper limit on max total-supply
+      25                 :            : (define-fungible-token wrapped-bitcoin)
+      26                 :            : 
+      27                 :            : 
+      28                 :            : ;; Get the token balance of the specified owner in base units
+      29                 :            : (define-read-only (get-balance (owner principal))
+      30                 :          0 :   (ok (ft-get-balance wrapped-bitcoin owner)))
+      31                 :            : 
+      32                 :            : ;; Returns the token name
+      33                 :            : (define-read-only (get-name)
+      34                 :          0 :   (ok (var-get token-name))) ;; "Wrapped Bitcoin"
+      35                 :            : 
+      36                 :            : ;; Returns the symbol or "ticker" for this token
+      37                 :            : (define-read-only (get-symbol)
+      38                 :          0 :   (ok (var-get token-symbol))) ;; "xBTC"
+      39                 :            : 
+      40                 :            : ;; Returns the number of decimals used
+      41                 :            : (define-read-only (get-decimals)
+      42                 :          0 :   (ok (var-get token-decimals))) ;;u8
+      43                 :            : 
+      44                 :            : ;; Returns the total number of tokens that currently exist
+      45                 :            : (define-read-only (get-total-supply)
+      46                 :          0 :   (ok (ft-get-supply wrapped-bitcoin)))
+      47                 :            : 
+      48                 :            : 
+      49                 :            : ;; Write function to transfer tokens between accounts - conforms to SIP 10
+      50                 :            : ;; --------------------------------------------------------------------------
+      51                 :            : 
+      52                 :            : ;; Transfers tokens to a recipient
+      53                 :            : ;; The originator of the transaction (tx-sender) must be the 'sender' principal
+      54                 :            : ;; Smart contracts can move tokens from their own address by calling transfer with the 'as-contract' modifier to override the tx-sender.
+      55                 :            : 
+      56                 :            : (define-public (transfer (amount uint) (sender principal) (recipient principal ) (memo (optional (buff 34) )))
+      57                 :         20 :   (begin
+      58                 :         20 :     (try! (detect-transfer-restriction amount sender recipient)) ;; Ensure there is no restriction
+      59            [ - ]:         20 :     (asserts! (is-eq tx-sender sender) (err u4)) ;; Ensure the originator is the sender principal
+      60                 :         20 :     (print (default-to 0x memo))
+      61                 :         20 :     (ft-transfer? wrapped-bitcoin amount sender recipient) ) ) ;; Transfer
+      62                 :            : 
+      63                 :            : 
+      64                 :            : ;; Role Based Access Control
+      65                 :            : ;; --------------------------------------------------------------------------
+      66                 :            : (define-constant OWNER_ROLE u0) ;; Can manage RBAC
+      67                 :            : (define-constant MINTER_ROLE u1) ;; Can mint new tokens to any account
+      68                 :            : (define-constant BURNER_ROLE u2) ;; Can burn tokens from any account
+      69                 :            : (define-constant REVOKER_ROLE u3) ;; Can revoke tokens and move them to any account
+      70                 :            : (define-constant BLACKLISTER_ROLE u4) ;; Can add principals to a blacklist that can prevent transfers
+      71                 :            : 
+      72                 :            : ;; Each role will have a mapping of principal to boolean.  A true "allowed" in the mapping indicates that the principal has the role.
+      73                 :            : ;; Each role will have special permissions to modify or manage specific capabilities in the contract.
+      74                 :            : ;; Note that adding/removing roles could be optimized by having just 1 function, but since this is sensitive functionality, it was split
+      75                 :            : ;;    into 2 separate functions to make it explicit.
+      76                 :            : ;; See the Readme about more details on the RBAC setup.
+      77                 :            : (define-map roles { role: uint, account: principal } { allowed: bool })
+      78                 :            : 
+      79                 :            : ;; Checks if an account has the specified role
+      80                 :            : (define-read-only (has-role (role-to-check uint) (principal-to-check principal))
+      81                 :         10 :   (default-to false (get allowed (map-get? roles {role: role-to-check, account: principal-to-check}))))  
+      82                 :            : 
+      83                 :            : ;; Add a principal to the specified role
+      84                 :            : ;; Only existing principals with the OWNER_ROLE can modify roles
+      85                 :            : (define-public (add-principal-to-role (role-to-add uint) (principal-to-add principal))   
+      86                 :          1 :    (begin
+      87                 :            :     ;; Check the contract-caller to verify they have the owner role
+      88            [ - ]:          1 :     (asserts! (has-role OWNER_ROLE contract-caller) (err PERMISSION_DENIED_ERROR))
+      89                 :            :     ;; Print the action for any off chain watchers
+      90                 :          1 :     (print { action: "add-principal-to-role", role-to-add: role-to-add, principal-to-add: principal-to-add })
+      91                 :          1 :     (ok (map-set roles { role: role-to-add, account: principal-to-add } { allowed: true }))))
+      92                 :            :    
+      93                 :            : ;; Remove a principal from the specified role
+      94                 :            : ;; Only existing principals with the OWNER_ROLE can modify roles
+      95                 :            : ;; WARN: Removing all owners will irrevocably lose all ownership permissions
+      96                 :            : (define-public (remove-principal-from-role (role-to-remove uint) (principal-to-remove principal))   
+      97                 :          0 :    (begin
+      98                 :            :     ;; Check the contract-caller to verify they have the owner role
+      99            [ - ]:          0 :     (asserts! (has-role OWNER_ROLE contract-caller) (err PERMISSION_DENIED_ERROR))
+     100                 :            :     ;; Print the action for any off chain watchers
+     101                 :          0 :     (print { action: "remove-principal-from-role", role-to-remove: role-to-remove, principal-to-remove: principal-to-remove })
+     102                 :          0 :     (ok (map-set roles { role: role-to-remove, account: principal-to-remove } { allowed: false }))))
+     103                 :            : 
+     104                 :            : 
+     105                 :            : ;; Token URI
+     106                 :            : ;; --------------------------------------------------------------------------
+     107                 :            : 
+     108                 :            : ;; Variable for URI storage
+     109                 :            : (define-data-var uri (string-utf8 256) u"")
+     110                 :            : 
+     111                 :            : ;; Public getter for the URI
+     112                 :            : (define-read-only (get-token-uri)
+     113                 :          0 :   (ok (some (var-get uri))))
+     114                 :            : 
+     115                 :            : ;; Setter for the URI - only the owner can set it
+     116                 :            : (define-public (set-token-uri (updated-uri (string-utf8 256)))
+     117                 :          0 :   (begin
+     118            [ - ]:          0 :     (asserts! (has-role OWNER_ROLE contract-caller) (err PERMISSION_DENIED_ERROR))
+     119                 :            :     ;; Print the action for any off chain watchers
+     120                 :          0 :     (print { action: "set-token-uri", updated-uri: updated-uri })
+     121                 :          0 :     (ok (var-set uri updated-uri))))
+     122                 :            : 
+     123                 :            : 
+     124                 :            : ;; Minting and Burning
+     125                 :            : ;; --------------------------------------------------------------------------
+     126                 :            : 
+     127                 :            : ;; Mint tokens to the target address
+     128                 :            : ;; Only existing principals with the MINTER_ROLE can mint tokens
+     129                 :            : (define-public (mint-tokens (mint-amount uint) (mint-to principal) )
+     130                 :          9 :   (begin
+     131            [ - ]:          9 :     (asserts! (has-role MINTER_ROLE contract-caller) (err PERMISSION_DENIED_ERROR))
+     132                 :            :     ;; Print the action for any off chain watchers
+     133                 :          9 :     (print { action: "mint-tokens", mint-amount: mint-amount, mint-to: mint-to  })
+     134                 :          9 :     (ft-mint? wrapped-bitcoin mint-amount mint-to)))
+     135                 :            : 
+     136                 :            : ;; Burn tokens from the target address
+     137                 :            : ;; Only existing principals with the BURNER_ROLE can mint tokens
+     138                 :            : (define-public (burn-tokens (burn-amount uint) (burn-from principal) )
+     139                 :          0 :   (begin
+     140            [ - ]:          0 :     (asserts! (has-role BURNER_ROLE contract-caller) (err PERMISSION_DENIED_ERROR))
+     141                 :            :     ;; Print the action for any off chain watchers
+     142                 :          0 :     (print { action: "burn-tokens", burn-amount: burn-amount, burn-from : burn-from  })
+     143                 :          0 :     (ft-burn? wrapped-bitcoin burn-amount burn-from)))
+     144                 :            : 
+     145                 :            : 
+     146                 :            : ;; Revoking Tokens
+     147                 :            : ;; --------------------------------------------------------------------------
+     148                 :            : 
+     149                 :            : ;; Moves tokens from one account to another
+     150                 :            : ;; Only existing principals with the REVOKER_ROLE can revoke tokens
+     151                 :            : (define-public (revoke-tokens (revoke-amount uint) (revoke-from principal) (revoke-to principal) )
+     152                 :          0 :   (begin
+     153            [ - ]:          0 :     (asserts! (has-role REVOKER_ROLE contract-caller) (err PERMISSION_DENIED_ERROR))
+     154                 :            :     ;; Print the action for any off chain watchers
+     155                 :          0 :     (print { action: "revoke-tokens", revoke-amount: revoke-amount, revoke-from: revoke-from, revoke-to: revoke-to })
+     156                 :          0 :     (ft-transfer? wrapped-bitcoin revoke-amount revoke-from revoke-to)))
+     157                 :            : 
+     158                 :            : ;; Blacklisting Principals
+     159                 :            : ;; --------------------------------------------------------------------------
+     160                 :            : 
+     161                 :            : ;; Blacklist mapping.  If an account has blacklisted = true then no transfers in or out are allowed
+     162                 :            : (define-map blacklist { account: principal } { blacklisted: bool })
+     163                 :            : 
+     164                 :            : ;; Checks if an account is blacklisted
+     165                 :            : (define-read-only (is-blacklisted (principal-to-check principal))
+     166                 :         40 :   (default-to false (get blacklisted (map-get? blacklist { account: principal-to-check }))))
+     167                 :            : 
+     168                 :            : ;; Updates an account's blacklist status
+     169                 :            : ;; Only existing principals with the BLACKLISTER_ROLE can update blacklist status
+     170                 :            : (define-public (update-blacklisted (principal-to-update principal) (set-blacklisted bool))
+     171                 :          0 :   (begin
+     172            [ - ]:          0 :     (asserts! (has-role BLACKLISTER_ROLE contract-caller) (err PERMISSION_DENIED_ERROR))
+     173                 :            :     ;; Print the action for any off chain watchers
+     174                 :          0 :     (print { action: "update-blacklisted", principal-to-update: principal-to-update, set-blacklisted: set-blacklisted })
+     175                 :          0 :     (ok (map-set blacklist { account: principal-to-update } { blacklisted: set-blacklisted }))))
+     176                 :            : 
+     177                 :            : ;; Transfer Restrictions
+     178                 :            : ;; --------------------------------------------------------------------------
+     179                 :            : (define-constant RESTRICTION_NONE u0) ;; No restriction detected
+     180                 :            : (define-constant RESTRICTION_BLACKLIST u5) ;; Sender or receiver is on the blacklist
+     181                 :            : 
+     182                 :            : ;; Checks to see if a transfer should be restricted.  If so returns an error code that specifies restriction type.
+     183                 :            : (define-read-only (detect-transfer-restriction (amount uint) (sender principal) (recipient principal))
+     184         [ +  + ]:         20 :   (if (or (is-blacklisted sender) (is-blacklisted recipient))
+     185            [ - ]:          0 :     (err RESTRICTION_BLACKLIST)
+     186            [ + ]:         20 :     (ok RESTRICTION_NONE)))
+     187                 :            : 
+     188                 :            : ;; Returns the user viewable string for a specific transfer restriction
+     189                 :            : (define-read-only (message-for-restriction (restriction-code uint))
+     190                 :          0 :   (if (is-eq restriction-code RESTRICTION_NONE)
+     191            [ - ]:          0 :     (ok "No Restriction Detected")
+     192            [ - ]:          0 :     (if (is-eq restriction-code RESTRICTION_BLACKLIST)
+     193            [ - ]:          0 :       (ok "Sender or recipient is on the blacklist and prevented from transacting")
+     194            [ - ]:          0 :       (ok "Unknown Error Code"))))
+     195                 :            : 
+     196                 :            : 
+     197                 :            : ;; Initialization
+     198                 :            : ;; --------------------------------------------------------------------------
+     199                 :            : 
+     200                 :            : ;; Check to ensure that the same account that deployed the contract is initializing it
+     201                 :            : ;; Only allow this funtion to be called once by checking "is-initialized"
+     202                 :            : (define-public (initialize (name-to-set (string-ascii 32)) (symbol-to-set (string-ascii 32) ) (decimals-to-set uint) (initial-owner principal))
+     203                 :          1 :   (begin
+     204            [ - ]:          1 :     (asserts! (is-eq tx-sender (var-get deployer-principal)) (err PERMISSION_DENIED_ERROR))
+     205            [ - ]:          1 :     (asserts! (not (var-get is-initialized)) (err PERMISSION_DENIED_ERROR))
+     206                 :          1 :     (var-set is-initialized true) ;; Set to true so that this can't be called again
+     207                 :          1 :     (var-set token-name name-to-set)
+     208                 :          1 :     (var-set token-symbol symbol-to-set)
+     209                 :          1 :     (var-set token-decimals decimals-to-set)
+     210                 :          1 :     (map-set roles { role: OWNER_ROLE, account: initial-owner } { allowed: true })
+     211                 :          1 :     (ok true)))
+
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/alex-vault-v1-1.clar.func-sort-c.html b/sbtc-ops/smart-contract/coverage/contracts/alex-vault-v1-1.clar.func-sort-c.html new file mode 100644 index 00000000..3989b57a --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/alex-vault-v1-1.clar.func-sort-c.html @@ -0,0 +1,181 @@ + + + + + + + LCOV - coverage.lcov - contracts/alex-vault-v1-1.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - alex-vault-v1-1.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:197226.4 %
Date:2023-08-18 21:53:02Functions:92536.0 %
Branches:43212.5 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
check-is-approved-flash-loan-user0
flash-loan0
get-balance0
get-contract-owner0
get-flash-loan-enabled0
get-flash-loan-fee-rate0
mul-down0
mul-up0
pause0
remove-from-reserve0
set-approved-flash-loan-user0
set-contract-owner0
set-flash-loan-enabled0
set-flash-loan-fee-rate0
transfer-ft-two0
transfer-sft0
set-approved-contract1
set-approved-token2
check-is-owner3
add-to-reserve17
check-is-approved17
check-is-approved-token17
get-reserve17
is-paused17
transfer-ft17
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/alex-vault-v1-1.clar.func.html b/sbtc-ops/smart-contract/coverage/contracts/alex-vault-v1-1.clar.func.html new file mode 100644 index 00000000..2f705f0f --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/alex-vault-v1-1.clar.func.html @@ -0,0 +1,181 @@ + + + + + + + LCOV - coverage.lcov - contracts/alex-vault-v1-1.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - alex-vault-v1-1.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:197226.4 %
Date:2023-08-18 21:53:02Functions:92536.0 %
Branches:43212.5 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
add-to-reserve17
check-is-approved17
check-is-approved-flash-loan-user0
check-is-approved-token17
check-is-owner3
flash-loan0
get-balance0
get-contract-owner0
get-flash-loan-enabled0
get-flash-loan-fee-rate0
get-reserve17
is-paused17
mul-down0
mul-up0
pause0
remove-from-reserve0
set-approved-contract1
set-approved-flash-loan-user0
set-approved-token2
set-contract-owner0
set-flash-loan-enabled0
set-flash-loan-fee-rate0
transfer-ft17
transfer-ft-two0
transfer-sft0
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/alex-vault-v1-1.clar.gcov.html b/sbtc-ops/smart-contract/coverage/contracts/alex-vault-v1-1.clar.gcov.html new file mode 100644 index 00000000..def110cb --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/alex-vault-v1-1.clar.gcov.html @@ -0,0 +1,252 @@ + + + + + + + LCOV - coverage.lcov - contracts/alex-vault-v1-1.clar + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - alex-vault-v1-1.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:197226.4 %
Date:2023-08-18 21:53:02Functions:92536.0 %
Branches:43212.5 %
+
+ + + + + + + + +

+
           Branch data     Line data    Source code
+
+       1                 :            : (use-trait ft-trait .trait-sip-010.sip-010-trait)
+       2                 :            : (use-trait sft-trait .trait-semi-fungible.semi-fungible-trait)
+       3                 :            : (use-trait flash-loan-trait .trait-flash-loan-user.flash-loan-user-trait)
+       4                 :            : (define-constant ONE_8 u100000000) ;; 8 decimal places
+       5                 :            : (define-constant ERR-NOT-AUTHORIZED (err u1000))
+       6                 :            : (define-constant ERR-PAUSED (err u1001))
+       7                 :            : (define-constant ERR-INVALID-BALANCE (err u1002))
+       8                 :            : (define-constant ERR-INVALID-TOKEN (err u2026))
+       9                 :            : (define-constant ERR-AMOUNT-EXCEED-RESERVE (err u2024))
+      10                 :            : (define-data-var contract-owner principal tx-sender)
+      11                 :            : (define-map approved-contracts principal bool)
+      12                 :            : (define-map approved-tokens principal bool)
+      13                 :            : (define-map approved-flash-loan-users principal bool)
+      14                 :            : (define-map reserve principal uint)
+      15                 :            : (define-data-var flash-loan-fee-rate uint u0)
+      16                 :            : (define-data-var flash-loan-enabled bool false)
+      17                 :            : (define-data-var paused bool false)
+      18                 :            : (define-read-only (get-flash-loan-enabled)
+      19                 :          0 :   (var-get flash-loan-enabled)
+      20                 :            : )
+      21                 :            : (define-read-only (is-paused)
+      22                 :         34 :   (var-get paused)
+      23                 :            : )
+      24                 :            : (define-read-only (get-contract-owner)
+      25                 :          0 :   (ok (var-get contract-owner))
+      26                 :            : )
+      27                 :            : (define-read-only (get-flash-loan-fee-rate)
+      28                 :          0 :   (var-get flash-loan-fee-rate)
+      29                 :            : )
+      30                 :            : (define-read-only (get-reserve (the-token principal))
+      31                 :         17 :   (default-to u0 (map-get? reserve the-token))
+      32                 :            : )
+      33                 :            : (define-public (get-balance (the-token <ft-trait>))
+      34                 :          0 :   (begin 
+      35                 :          0 :     (try! (check-is-approved-token (contract-of the-token))) 
+      36                 :          0 :     (contract-call? the-token get-balance-fixed (as-contract tx-sender))
+      37                 :            :   )
+      38                 :            : )
+      39                 :            : (define-public (set-flash-loan-enabled (enabled bool))
+      40                 :          0 :   (begin
+      41                 :          0 :     (try! (check-is-owner)) 
+      42                 :          0 :     (ok (var-set flash-loan-enabled enabled))
+      43                 :            :   )
+      44                 :            : )
+      45                 :            : (define-public (pause (new-paused bool))
+      46                 :          0 :     (begin 
+      47                 :          0 :         (try! (check-is-owner))
+      48                 :          0 :         (ok (var-set paused new-paused))
+      49                 :            :     )
+      50                 :            : )
+      51                 :            : (define-public (set-contract-owner (owner principal))
+      52                 :          0 :   (begin
+      53                 :          0 :     (try! (check-is-owner)) 
+      54                 :          0 :     (ok (var-set contract-owner owner))
+      55                 :            :   )
+      56                 :            : )
+      57                 :            : (define-public (set-approved-contract (the-contract principal) (approved bool))
+      58                 :          1 :   (begin 
+      59                 :          1 :     (try! (check-is-owner)) 
+      60                 :          1 :     (ok (map-set approved-contracts the-contract approved))
+      61                 :            :   )
+      62                 :            : )
+      63                 :            : (define-public (set-approved-flash-loan-user (the-flash-loan-user principal) (approved bool))
+      64                 :          0 :   (begin 
+      65                 :          0 :     (try! (check-is-owner)) 
+      66                 :          0 :     (ok (map-set approved-flash-loan-users the-flash-loan-user approved))
+      67                 :            :   )
+      68                 :            : )
+      69                 :            : (define-public (set-approved-token (the-token principal) (approved bool))
+      70                 :          2 :   (begin 
+      71                 :          2 :     (try! (check-is-owner)) 
+      72                 :          2 :     (ok (map-set approved-tokens the-token approved))
+      73                 :            :   )
+      74                 :            : )
+      75                 :            : (define-public (set-flash-loan-fee-rate (fee uint))
+      76                 :          0 :   (begin 
+      77                 :          0 :     (try! (check-is-owner)) 
+      78                 :          0 :     (ok (var-set flash-loan-fee-rate fee))
+      79                 :            :   )
+      80                 :            : )
+      81                 :            : (define-public (transfer-ft (the-token <ft-trait>) (amount uint) (recipient principal))
+      82                 :         17 :   (begin     
+      83            [ - ]:         17 :     (asserts! (not (is-paused)) ERR-PAUSED)
+      84    [ - ][ +  + ]:         17 :     (asserts! (and (or (is-ok (check-is-approved)) (is-ok (check-is-owner))) (is-ok (check-is-approved-token (contract-of the-token)))) ERR-NOT-AUTHORIZED)
+                 [ +  - ]
+      85                 :         17 :     (as-contract (contract-call? the-token transfer-fixed amount tx-sender recipient none))
+      86                 :            :   )
+      87                 :            : )
+      88                 :            : (define-public (transfer-ft-two (token-x-trait <ft-trait>) (dx uint) (token-y-trait <ft-trait>) (dy uint) (recipient principal))
+      89                 :          0 :   (begin 
+      90                 :          0 :     (try! (transfer-ft token-x-trait dx recipient))
+      91                 :          0 :     (transfer-ft token-y-trait dy recipient)
+      92                 :            :   )
+      93                 :            : )
+      94                 :            : (define-public (transfer-sft (the-token <sft-trait>) (token-id uint) (amount uint) (recipient principal))
+      95                 :          0 :   (begin     
+      96            [ - ]:          0 :     (asserts! (not (is-paused)) ERR-PAUSED)
+      97    [ - ][ -  - ]:          0 :     (asserts! (and (or (is-ok (check-is-approved)) (is-ok (check-is-owner))) (is-ok (check-is-approved-token (contract-of the-token)))) ERR-NOT-AUTHORIZED) 
+                 [ -  - ]
+      98                 :          0 :     (as-contract (contract-call? the-token transfer-fixed token-id amount tx-sender recipient))
+      99                 :            :   )
+     100                 :            : )
+     101                 :            : (define-public (flash-loan (the-flash-loan-user <flash-loan-trait>) (the-token <ft-trait>) (amount uint) (memo (optional (buff 16))))
+     102                 :          0 :   (begin
+     103            [ - ]:          0 :     (asserts! (not (is-paused)) ERR-PAUSED)
+     104    [ - ][ -  - ]:          0 :     (asserts! (and (is-ok (check-is-approved-flash-loan-user (contract-of the-flash-loan-user))) (is-ok (check-is-approved-token (contract-of the-token)))) ERR-NOT-AUTHORIZED)
+     105                 :          0 :     (let 
+     106                 :            :       (
+     107                 :          0 :         (pre-bal (unwrap! (get-balance the-token) ERR-INVALID-BALANCE))
+     108                 :          0 :         (fee-with-principal (+ ONE_8 (var-get flash-loan-fee-rate)))
+     109                 :          0 :         (amount-with-fee (mul-up amount fee-with-principal))
+     110                 :          0 :         (recipient tx-sender)
+     111                 :            :       )
+     112                 :            :     
+     113                 :            :       ;; make sure current balance > loan amount
+     114            [ - ]:          0 :       (asserts! (> pre-bal amount) ERR-INVALID-BALANCE)
+     115                 :            :       ;; transfer loan to flash-loan-user
+     116                 :          0 :       (as-contract (try! (contract-call? the-token transfer-fixed amount tx-sender recipient none)))
+     117                 :            :       ;; flash-loan-user executes with loan received
+     118                 :          0 :       (try! (contract-call? the-flash-loan-user execute the-token amount memo))
+     119                 :            :       ;; return the loan + fee
+     120                 :          0 :       (try! (contract-call? the-token transfer-fixed amount-with-fee tx-sender (as-contract tx-sender) none))
+     121                 :          0 :       (ok amount-with-fee)
+     122                 :            :     )
+     123                 :            :   )
+     124                 :            : )
+     125                 :            : (define-public (add-to-reserve (the-token principal) (amount uint))
+     126                 :         17 :   (begin
+     127            [ - ]:         17 :     (asserts! (not (is-paused)) ERR-PAUSED)
+     128    [ - ][ +  - ]:         17 :     (asserts! (or (is-ok (check-is-approved)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED) 
+     129                 :         17 :     (ok (map-set reserve the-token (+ amount (get-reserve the-token))))
+     130                 :            :   )
+     131                 :            : )
+     132                 :            : (define-public (remove-from-reserve (the-token principal) (amount uint))
+     133                 :          0 :   (begin
+     134            [ - ]:          0 :     (asserts! (not (is-paused)) ERR-PAUSED)
+     135    [ - ][ -  - ]:          0 :     (asserts! (or (is-ok (check-is-approved)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED)
+     136            [ - ]:          0 :     (asserts! (<= amount (get-reserve the-token)) ERR-AMOUNT-EXCEED-RESERVE)
+     137                 :          0 :     (ok (map-set reserve the-token (- (get-reserve the-token) amount)))
+     138                 :            :   )
+     139                 :            : )
+     140                 :            : (define-private (check-is-approved)
+     141            [ - ]:         34 :   (ok (asserts! (default-to false (map-get? approved-contracts tx-sender)) ERR-NOT-AUTHORIZED))
+     142                 :            : )
+     143                 :            : (define-private (check-is-owner)
+     144            [ - ]:          3 :   (ok (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED))
+     145                 :            : )
+     146                 :            : (define-private (check-is-approved-flash-loan-user (the-flash-loan-user principal))
+     147            [ - ]:          0 :   (ok (asserts! (default-to false (map-get? approved-flash-loan-users the-flash-loan-user)) ERR-NOT-AUTHORIZED))
+     148                 :            : )
+     149                 :            : (define-private (check-is-approved-token (flash-loan-token principal))
+     150            [ - ]:         17 :   (ok (asserts! (default-to false (map-get? approved-tokens flash-loan-token)) ERR-NOT-AUTHORIZED))
+     151                 :            : )
+     152                 :            : (define-private (mul-down (a uint) (b uint))
+     153                 :          0 :     (/ (* a b) ONE_8)
+     154                 :            : )
+     155                 :            : (define-private (mul-up (a uint) (b uint))
+     156                 :          0 :     (let
+     157                 :            :         (
+     158                 :          0 :             (product (* a b))
+     159                 :            :        )
+     160                 :          0 :         (if (is-eq product u0)
+     161            [ - ]:          0 :             u0
+     162            [ - ]:          0 :             (+ u1 (/ (- product u1) ONE_8))
+     163                 :            :        )
+     164                 :            :    )
+     165                 :            : )
+
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/amm-swap-pool-v1-1.clar.func-sort-c.html b/sbtc-ops/smart-contract/coverage/contracts/amm-swap-pool-v1-1.clar.func-sort-c.html new file mode 100644 index 00000000..3b658cc3 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/amm-swap-pool-v1-1.clar.func-sort-c.html @@ -0,0 +1,453 @@ + + + + + + + LCOV - coverage.lcov - contracts/amm-swap-pool-v1-1.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - amm-swap-pool-v1-1.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:18856133.5 %
Date:2023-08-18 21:53:02Functions:279329.0 %
Branches:3722716.3 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
accumulate_division0
accumulate_product0
check-is-owner0
div-up0
exp-fixed0
exp-pos0
fee-helper-a0
fee-helper-b0
fee-helper-c0
get-balances0
get-contract-owner0
get-end-block0
get-fee-rebate0
get-helper-a0
get-helper-b0
get-helper-c0
get-max-in-ratio0
get-max-out-ratio0
get-oracle-average0
get-oracle-enabled0
get-oracle-instant0
get-oracle-resilient0
get-pool-details-by-id0
get-pool-owner0
get-position-given-burn0
get-position-given-burn-internal0
get-position-given-mint0
get-position-given-mint-internal0
get-price0
get-start-block0
get-switch-threshold0
get-threshold-x0
get-threshold-y0
get-x-given-price0
get-x-given-price-internal0
get-x-in-given-y-out0
get-x-in-given-y-out-internal0
get-y-given-price0
get-y-given-price-internal0
get-y-in-given-x-out0
get-y-in-given-x-out-internal0
ln-fixed0
ln-priv0
log-fixed0
pause0
pow-down0
pow-fixed0
pow-priv0
pow-up0
reduce-position0
rolling_div_sum0
rolling_sum_div0
set-contract-owner0
set-end-block0
set-fee-rate-x0
set-fee-rate-y0
set-fee-rebate0
set-oracle-average0
set-oracle-enabled0
set-pool-owner0
set-switch-threshold0
set-threshold-x0
set-threshold-y0
swap-helper-a0
swap-helper-b0
swap-helper-c0
add-to-position1
create-pool1
get-invariant1
get-token-given-position1
get-token-given-position-internal1
set-max-in-ratio1
set-max-out-ratio1
set-start-block1
get-fee-rate-x8
get-y-given-x8
get-y-given-x-internal8
swap-x-for-y8
get-fee-rate-y9
get-x-given-y9
get-x-given-y-internal9
swap-y-for-x9
check-pool-status17
div-down17
fee-helper17
get-helper17
get-price-internal17
mul-up17
swap-helper17
is-paused18
mul-down18
get-pool-details22
get-pool-exists22
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/amm-swap-pool-v1-1.clar.func.html b/sbtc-ops/smart-contract/coverage/contracts/amm-swap-pool-v1-1.clar.func.html new file mode 100644 index 00000000..403c82ce --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/amm-swap-pool-v1-1.clar.func.html @@ -0,0 +1,453 @@ + + + + + + + LCOV - coverage.lcov - contracts/amm-swap-pool-v1-1.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - amm-swap-pool-v1-1.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:18856133.5 %
Date:2023-08-18 21:53:02Functions:279329.0 %
Branches:3722716.3 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
accumulate_division0
accumulate_product0
add-to-position1
check-is-owner0
check-pool-status17
create-pool1
div-down17
div-up0
exp-fixed0
exp-pos0
fee-helper17
fee-helper-a0
fee-helper-b0
fee-helper-c0
get-balances0
get-contract-owner0
get-end-block0
get-fee-rate-x8
get-fee-rate-y9
get-fee-rebate0
get-helper17
get-helper-a0
get-helper-b0
get-helper-c0
get-invariant1
get-max-in-ratio0
get-max-out-ratio0
get-oracle-average0
get-oracle-enabled0
get-oracle-instant0
get-oracle-resilient0
get-pool-details22
get-pool-details-by-id0
get-pool-exists22
get-pool-owner0
get-position-given-burn0
get-position-given-burn-internal0
get-position-given-mint0
get-position-given-mint-internal0
get-price0
get-price-internal17
get-start-block0
get-switch-threshold0
get-threshold-x0
get-threshold-y0
get-token-given-position1
get-token-given-position-internal1
get-x-given-price0
get-x-given-price-internal0
get-x-given-y9
get-x-given-y-internal9
get-x-in-given-y-out0
get-x-in-given-y-out-internal0
get-y-given-price0
get-y-given-price-internal0
get-y-given-x8
get-y-given-x-internal8
get-y-in-given-x-out0
get-y-in-given-x-out-internal0
is-paused18
ln-fixed0
ln-priv0
log-fixed0
mul-down18
mul-up17
pause0
pow-down0
pow-fixed0
pow-priv0
pow-up0
reduce-position0
rolling_div_sum0
rolling_sum_div0
set-contract-owner0
set-end-block0
set-fee-rate-x0
set-fee-rate-y0
set-fee-rebate0
set-max-in-ratio1
set-max-out-ratio1
set-oracle-average0
set-oracle-enabled0
set-pool-owner0
set-start-block1
set-switch-threshold0
set-threshold-x0
set-threshold-y0
swap-helper17
swap-helper-a0
swap-helper-b0
swap-helper-c0
swap-x-for-y8
swap-y-for-x9
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/amm-swap-pool-v1-1.clar.gcov.html b/sbtc-ops/smart-contract/coverage/contracts/amm-swap-pool-v1-1.clar.gcov.html new file mode 100644 index 00000000..7829a04a --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/amm-swap-pool-v1-1.clar.gcov.html @@ -0,0 +1,1156 @@ + + + + + + + LCOV - coverage.lcov - contracts/amm-swap-pool-v1-1.clar + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - amm-swap-pool-v1-1.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:18856133.5 %
Date:2023-08-18 21:53:02Functions:279329.0 %
Branches:3722716.3 %
+
+ + + + + + + + +

+
           Branch data     Line data    Source code
+
+       1                 :            : (use-trait ft-trait .trait-sip-010.sip-010-trait)
+       2                 :            : (define-constant ERR-NOT-AUTHORIZED (err u1000))
+       3                 :            : (define-constant ERR-INVALID-POOL (err u2001))
+       4                 :            : (define-constant ERR-INVALID-LIQUIDITY (err u2003))
+       5                 :            : (define-constant ERR-POOL-ALREADY-EXISTS (err u2000))
+       6                 :            : (define-constant ERR-PERCENT-GREATER-THAN-ONE (err u5000))
+       7                 :            : (define-constant ERR-EXCEEDS-MAX-SLIPPAGE (err u2020))
+       8                 :            : (define-constant ERR-ORACLE-NOT-ENABLED (err u7002))
+       9                 :            : (define-constant ERR-ORACLE-AVERAGE-BIGGER-THAN-ONE (err u7004))
+      10                 :            : (define-constant ERR-PAUSED (err u1001))
+      11                 :            : (define-constant ERR-SWITCH-THRESHOLD-BIGGER-THAN-ONE (err u7005))
+      12                 :            : (define-constant ERR-NO-LIQUIDITY (err u2002))
+      13                 :            : (define-constant ERR-MAX-IN-RATIO (err u4001))
+      14                 :            : (define-constant ERR-MAX-OUT-RATIO (err u4002))
+      15                 :            : (define-data-var contract-owner principal tx-sender)
+      16                 :            : (define-data-var pool-nonce uint u0)
+      17                 :            : (define-data-var paused bool false)
+      18                 :            : (define-data-var switch-threshold uint u80000000)
+      19                 :            : (define-map pools-id-map
+      20                 :            :     uint 
+      21                 :            :     {
+      22                 :            :         token-x: principal,
+      23                 :            :         token-y: principal,
+      24                 :            :         factor: uint
+      25                 :            :     }    
+      26                 :            : )
+      27                 :            : (define-map pools-data-map
+      28                 :            :   {
+      29                 :            :     token-x: principal,
+      30                 :            :     token-y: principal,
+      31                 :            :     factor: uint
+      32                 :            :   }
+      33                 :            :   {
+      34                 :            :     pool-id: uint,
+      35                 :            :     total-supply: uint,
+      36                 :            :     balance-x: uint,
+      37                 :            :     balance-y: uint,
+      38                 :            :     pool-owner: principal,    
+      39                 :            :     fee-rate-x: uint,
+      40                 :            :     fee-rate-y: uint,
+      41                 :            :     fee-rebate: uint,
+      42                 :            :     oracle-enabled: bool,
+      43                 :            :     oracle-average: uint,
+      44                 :            :     oracle-resilient: uint,
+      45                 :            :     start-block: uint,
+      46                 :            :     end-block: uint,
+      47                 :            :     threshold-x: uint,
+      48                 :            :     threshold-y: uint,
+      49                 :            :     max-in-ratio: uint,
+      50                 :            :     max-out-ratio: uint
+      51                 :            :   }
+      52                 :            : )
+      53                 :            : (define-read-only (get-switch-threshold)
+      54                 :          0 :     (var-get switch-threshold)
+      55                 :            : )
+      56                 :            : (define-read-only (is-paused)
+      57                 :         19 :     (var-get paused)
+      58                 :            : )
+      59                 :            : (define-read-only (get-contract-owner)
+      60                 :          0 :   (ok (var-get contract-owner))
+      61                 :            : )
+      62                 :            : (define-read-only (get-pool-details-by-id (pool-id uint))
+      63                 :          0 :     (ok (unwrap! (map-get? pools-id-map pool-id) ERR-INVALID-POOL))
+      64                 :            : )
+      65                 :            : (define-read-only (get-pool-details (token-x principal) (token-y principal) (factor uint))
+      66                 :         91 :     (ok (unwrap! (get-pool-exists token-x token-y factor) ERR-INVALID-POOL))
+      67                 :            : )
+      68                 :            : (define-read-only (get-pool-exists (token-x principal) (token-y principal) (factor uint))
+      69                 :        142 :     (map-get? pools-data-map { token-x: token-x, token-y: token-y, factor: factor }) 
+      70                 :            : )
+      71                 :            : (define-read-only (get-balances (token-x principal) (token-y principal) (factor uint))
+      72                 :          0 :   (let
+      73                 :            :     (
+      74                 :          0 :       (pool (try! (get-pool-details token-x token-y factor)))
+      75                 :            :     )
+      76                 :          0 :     (ok {balance-x: (get balance-x pool), balance-y: (get balance-y pool)})
+      77                 :            :   )
+      78                 :            : )
+      79                 :            : (define-read-only (get-start-block (token-x principal) (token-y principal) (factor uint))
+      80                 :          0 :     (ok (get start-block (try! (get-pool-details token-x token-y factor))))
+      81                 :            : )
+      82                 :            : (define-read-only (get-end-block (token-x principal) (token-y principal) (factor uint))
+      83                 :          0 :     (ok (get end-block (try! (get-pool-details token-x token-y factor))))
+      84                 :            : )
+      85                 :            : (define-read-only (get-max-in-ratio (token-x principal) (token-y principal) (factor uint))
+      86                 :          0 :     (ok (get max-in-ratio (try! (get-pool-details token-x token-y factor))))
+      87                 :            : )
+      88                 :            : (define-read-only (get-max-out-ratio (token-x principal) (token-y principal) (factor uint))
+      89                 :          0 :     (ok (get max-out-ratio (try! (get-pool-details token-x token-y factor))))
+      90                 :            : )
+      91                 :            : (define-read-only (check-pool-status (token-x principal) (token-y principal) (factor uint))
+      92                 :         17 :     (let
+      93                 :            :         (
+      94                 :         17 :             (pool (try! (get-pool-details token-x token-y factor)))
+      95                 :            :         )
+      96    [ - ][ +  + ]:         17 :         (ok (asserts! (and (>= block-height (get start-block pool)) (<= block-height (get end-block pool))) ERR-NOT-AUTHORIZED))
+      97                 :            :     )
+      98                 :            : )
+      99                 :            : (define-read-only (get-oracle-enabled (token-x principal) (token-y principal) (factor uint))
+     100                 :          0 :     (ok (get oracle-enabled (try! (get-pool-details token-x token-y factor))))
+     101                 :            : )
+     102                 :            : (define-read-only (get-oracle-average (token-x principal) (token-y principal) (factor uint))
+     103                 :          0 :     (ok (get oracle-average (try! (get-pool-details token-x token-y factor))))
+     104                 :            : )
+     105                 :            : (define-read-only (get-oracle-resilient (token-x principal) (token-y principal) (factor uint))
+     106                 :          0 :     (let
+     107                 :            :         (
+     108                 :          0 :             (exists (is-some (get-pool-exists token-x token-y factor)))
+     109                 :          0 :             (pool
+     110                 :          0 :                 (if exists
+     111            [ - ]:          0 :                     (try! (get-pool-details token-x token-y factor))
+     112            [ - ]:          0 :                     (try! (get-pool-details token-y token-x factor))                    
+     113                 :            :                 )
+     114                 :            :             )
+     115                 :            :         )
+     116            [ - ]:          0 :         (asserts! (get oracle-enabled pool) ERR-ORACLE-NOT-ENABLED)
+     117                 :          0 :         (ok (+ (mul-down (- ONE_8 (get oracle-average pool)) (try! (get-oracle-instant token-x token-y factor))) 
+     118                 :          0 :             (mul-down (get oracle-average pool) (get oracle-resilient pool)))
+     119                 :            :         )           
+     120                 :            :     )
+     121                 :            : )
+     122                 :            : (define-read-only (get-oracle-instant (token-x principal) (token-y principal) (factor uint))
+     123                 :          0 :     (let                 
+     124                 :            :         (
+     125                 :          0 :             (exists (is-some (get-pool-exists token-x token-y factor)))
+     126                 :          0 :             (pool
+     127                 :          0 :                 (if exists
+     128            [ - ]:          0 :                     (try! (get-pool-details token-x token-y factor))
+     129            [ - ]:          0 :                     (try! (get-pool-details token-y token-x factor))                    
+     130                 :            :                 )
+     131                 :            :             )
+     132                 :            :         )
+     133            [ - ]:          0 :         (asserts! (get oracle-enabled pool) ERR-ORACLE-NOT-ENABLED)
+     134                 :          0 :         (if exists 
+     135            [ - ]:          0 :             (ok (get-price-internal (get balance-x pool) (get balance-y pool) factor))
+     136            [ - ]:          0 :             (ok (get-price-internal (get balance-y pool) (get balance-x pool) factor))
+     137                 :            :         )
+     138                 :            :     )
+     139                 :            : )
+     140                 :            : (define-read-only (get-price (token-x principal) (token-y principal) (factor uint))
+     141                 :          0 :     (let
+     142                 :            :         (
+     143                 :          0 :             (pool (try! (get-pool-details token-x token-y factor)))
+     144                 :            :         )
+     145                 :          0 :         (ok (get-price-internal (get balance-x pool) (get balance-y pool) factor))
+     146                 :            :     )
+     147                 :            : )
+     148                 :            : (define-read-only (get-threshold-x (token-x principal) (token-y principal) (factor uint))
+     149                 :          0 :     (ok (get threshold-x (try! (get-pool-details token-x token-y factor))))
+     150                 :            : )
+     151                 :            : (define-read-only (get-threshold-y (token-x principal) (token-y principal) (factor uint))
+     152                 :          0 :     (ok (get threshold-y (try! (get-pool-details token-x token-y factor))))
+     153                 :            : )
+     154                 :            : (define-read-only (get-fee-rebate (token-x principal) (token-y principal) (factor uint))
+     155                 :          0 :     (ok (get fee-rebate (try! (get-pool-details token-x token-y factor))))
+     156                 :            : )
+     157                 :            : (define-read-only (get-fee-rate-x (token-x principal) (token-y principal) (factor uint))
+     158                 :          8 :     (ok (get fee-rate-x (try! (get-pool-details token-x token-y factor))))
+     159                 :            : )
+     160                 :            : (define-read-only (get-fee-rate-y (token-x principal) (token-y principal) (factor uint))
+     161                 :          9 :     (ok (get fee-rate-y (try! (get-pool-details token-x token-y factor))))
+     162                 :            : )
+     163                 :            : (define-read-only (get-pool-owner (token-x principal) (token-y principal) (factor uint))
+     164                 :          0 :     (ok (get pool-owner (try! (get-pool-details token-x token-y factor))))
+     165                 :            : )
+     166                 :            : (define-read-only (get-y-given-x (token-x principal) (token-y principal) (factor uint) (dx uint))
+     167                 :         16 :     (let 
+     168                 :            :         (
+     169                 :         16 :             (pool (try! (get-pool-details token-x token-y factor)))
+     170                 :         16 :             (threshold (get threshold-x pool))
+     171                 :         16 :             (dy (if (>= dx threshold)
+     172            [ + ]:         16 :                 (get-y-given-x-internal (get balance-x pool) (get balance-y pool) factor dx)
+     173            [ - ]:          0 :                 (div-down (mul-down dx (get-y-given-x-internal (get balance-x pool) (get balance-y pool) factor threshold)) threshold)
+     174                 :            :             ))
+     175                 :            :         )
+     176            [ - ]:         16 :         (asserts! (< dx (mul-down (get balance-x pool) (get max-in-ratio pool))) ERR-MAX-IN-RATIO)     
+     177            [ - ]:         16 :         (asserts! (< dy (mul-down (get balance-y pool) (get max-out-ratio pool))) ERR-MAX-OUT-RATIO)
+     178                 :         16 :         (ok dy)
+     179                 :            :     )
+     180                 :            : )
+     181                 :            : (define-read-only (get-x-given-y (token-x principal) (token-y principal) (factor uint) (dy uint)) 
+     182                 :         18 :     (let 
+     183                 :            :         (
+     184                 :         18 :             (pool (try! (get-pool-details token-x token-y factor)))
+     185                 :         18 :             (threshold (get threshold-y pool))
+     186                 :         18 :             (dx (if (>= dy threshold)
+     187            [ + ]:         18 :                 (get-x-given-y-internal (get balance-x pool) (get balance-y pool) factor dy)
+     188            [ - ]:          0 :                 (div-down (mul-down dy (get-x-given-y-internal (get balance-x pool) (get balance-y pool) factor threshold)) threshold)         
+     189                 :            :             ))
+     190                 :            :         )        
+     191            [ - ]:         18 :         (asserts! (< dy (mul-down (get balance-y pool) (get max-in-ratio pool))) ERR-MAX-IN-RATIO)
+     192            [ - ]:         18 :         (asserts! (< dx (mul-down (get balance-x pool) (get max-out-ratio pool))) ERR-MAX-OUT-RATIO)
+     193                 :         18 :         (ok dx)
+     194                 :            :     )
+     195                 :            : )
+     196                 :            : (define-read-only (get-y-in-given-x-out (token-x principal) (token-y principal) (factor uint) (dx uint))
+     197                 :          0 :     (let 
+     198                 :            :         (
+     199                 :          0 :             (pool (try! (get-pool-details token-x token-y factor)))
+     200                 :          0 :             (threshold (get threshold-x pool))
+     201                 :          0 :             (dy (if (>= dx threshold)
+     202            [ - ]:          0 :                 (get-y-in-given-x-out-internal (get balance-x pool) (get balance-y pool) factor dx)
+     203            [ - ]:          0 :                 (div-down (mul-down dx (get-y-in-given-x-out-internal (get balance-x pool) (get balance-y pool) factor threshold)) threshold)
+     204                 :            :             ))
+     205                 :            :         )
+     206            [ - ]:          0 :         (asserts! (< dy (mul-down (get balance-y pool) (get max-in-ratio pool))) ERR-MAX-IN-RATIO)
+     207            [ - ]:          0 :         (asserts! (< dx (mul-down (get balance-x pool) (get max-out-ratio pool))) ERR-MAX-OUT-RATIO)
+     208                 :          0 :         (ok dy)
+     209                 :            :     )
+     210                 :            : )
+     211                 :            : (define-read-only (get-x-in-given-y-out (token-x principal) (token-y principal) (factor uint) (dy uint)) 
+     212                 :          0 :     (let 
+     213                 :            :         (
+     214                 :          0 :             (pool (try! (get-pool-details token-x token-y factor)))
+     215                 :          0 :             (threshold (get threshold-y pool))
+     216                 :          0 :             (dx (if (>= dy threshold)
+     217            [ - ]:          0 :                 (get-x-in-given-y-out-internal (get balance-x pool) (get balance-y pool) factor dy)
+     218            [ - ]:          0 :                 (div-down (mul-down dy (get-x-in-given-y-out-internal (get balance-x pool) (get balance-y pool) factor threshold)) threshold)
+     219                 :            :             ))
+     220                 :            :         )
+     221            [ - ]:          0 :         (asserts! (< dx (mul-down (get balance-x pool) (get max-in-ratio pool))) ERR-MAX-IN-RATIO)     
+     222            [ - ]:          0 :         (asserts! (< dy (mul-down (get balance-y pool) (get max-out-ratio pool))) ERR-MAX-OUT-RATIO)
+     223                 :          0 :         (ok dx)
+     224                 :            :     )
+     225                 :            : )
+     226                 :            : (define-read-only (get-x-given-price (token-x principal) (token-y principal) (factor uint) (price uint))
+     227                 :          0 :     (let 
+     228                 :            :         (
+     229                 :          0 :             (pool (try! (get-pool-details token-x token-y factor)))
+     230                 :            :         )
+     231            [ - ]:          0 :         (asserts! (< price (get-price-internal (get balance-x pool) (get balance-y pool) factor)) ERR-NO-LIQUIDITY) 
+     232                 :          0 :         (ok (get-x-given-price-internal (get balance-x pool) (get balance-y pool) factor price))
+     233                 :            :     )
+     234                 :            : )
+     235                 :            : (define-read-only (get-y-given-price (token-x principal) (token-y principal) (factor uint) (price uint))
+     236                 :          0 :     (let 
+     237                 :            :         (
+     238                 :          0 :             (pool (try! (get-pool-details token-x token-y factor)))
+     239                 :            :         )
+     240            [ - ]:          0 :         (asserts! (> price (get-price-internal (get balance-x pool) (get balance-y pool) factor)) ERR-NO-LIQUIDITY)
+     241                 :          0 :         (ok (get-y-given-price-internal (get balance-x pool) (get balance-y pool) factor price))
+     242                 :            :     )
+     243                 :            : )
+     244                 :            : (define-read-only (get-token-given-position (token-x principal) (token-y principal) (factor uint) (dx uint) (max-dy (optional uint)))
+     245                 :          1 :     (let 
+     246                 :            :         (
+     247                 :          1 :             (pool (try! (get-pool-details token-x token-y factor)))
+     248                 :          1 :             (dy (default-to u340282366920938463463374607431768211455 max-dy))
+     249                 :            :         )
+     250    [ - ][ +  + ]:          1 :         (asserts! (and (> dx u0) (> dy u0))  ERR-NO-LIQUIDITY)
+     251                 :          1 :         (ok (get-token-given-position-internal (get balance-x pool) (get balance-y pool) factor (get total-supply pool) dx dy))
+     252                 :            :     )
+     253                 :            : )
+     254                 :            : (define-read-only (get-position-given-mint (token-x principal) (token-y principal) (factor uint) (token-amount uint))
+     255                 :          0 :     (let 
+     256                 :            :         (
+     257                 :          0 :             (pool (try! (get-pool-details token-x token-y factor)))
+     258                 :            :         )
+     259            [ - ]:          0 :         (asserts! (> (get total-supply pool) u0) ERR-NO-LIQUIDITY)
+     260                 :          0 :         (ok (get-position-given-mint-internal (get balance-x pool) (get balance-y pool) factor (get total-supply pool) token-amount))
+     261                 :            :     )
+     262                 :            : )
+     263                 :            : (define-read-only (get-position-given-burn (token-x principal) (token-y principal) (factor uint) (token-amount uint))
+     264                 :          0 :     (let 
+     265                 :            :         (
+     266                 :          0 :             (pool (try! (get-pool-details token-x token-y factor)))
+     267                 :            :         )
+     268            [ - ]:          0 :         (asserts! (> (get total-supply pool) u0) ERR-NO-LIQUIDITY)
+     269                 :          0 :         (ok (get-position-given-burn-internal (get balance-x pool) (get balance-y pool) factor (get total-supply pool) token-amount))
+     270                 :            :     )
+     271                 :            : )
+     272                 :            : (define-read-only (get-helper (token-x principal) (token-y principal) (factor uint) (dx uint))
+     273                 :         17 :     (if (is-some (get-pool-exists token-x token-y factor))
+     274            [ + ]:          8 :         (get-y-given-x token-x token-y factor dx)
+     275            [ + ]:          9 :         (get-x-given-y token-y token-x factor dx)
+     276                 :            :     )
+     277                 :            : )
+     278                 :            : (define-read-only (get-helper-a (token-x principal) (token-y principal) (token-z principal) (factor-x uint) (factor-y uint) (dx uint))
+     279                 :          0 :     (get-helper token-y token-z factor-y (try! (get-helper token-x token-y factor-x dx)))
+     280                 :            : )
+     281                 :            : (define-read-only (get-helper-b
+     282                 :            :     (token-x principal) (token-y principal) (token-z principal) (token-w principal)
+     283                 :            :     (factor-x uint) (factor-y uint) (factor-z uint)
+     284                 :            :     (dx uint))
+     285                 :          0 :     (get-helper token-z token-w factor-z (try! (get-helper-a token-x token-y token-z factor-x factor-y dx)))
+     286                 :            : )
+     287                 :            : (define-read-only (get-helper-c
+     288                 :            :     (token-x principal) (token-y principal) (token-z principal) (token-w principal) (token-v principal)
+     289                 :            :     (factor-x uint) (factor-y uint) (factor-z uint) (factor-w uint)
+     290                 :            :     (dx uint))
+     291                 :          0 :     (get-helper-a token-z token-w token-v factor-z factor-w (try! (get-helper-a token-x token-y token-z factor-x factor-y dx)))
+     292                 :            : )
+     293                 :            : (define-read-only (fee-helper (token-x principal) (token-y principal) (factor uint))
+     294                 :         17 :     (if (is-some (get-pool-exists token-x token-y factor))
+     295            [ + ]:          8 :         (get-fee-rate-x token-x token-y factor)
+     296            [ + ]:          9 :         (get-fee-rate-y token-y token-x factor)
+     297                 :            :     )
+     298                 :            : )
+     299                 :            : (define-read-only (fee-helper-a (token-x principal) (token-y principal) (token-z principal) (factor-x uint) (factor-y uint))
+     300                 :          0 :     (ok (+ 
+     301                 :          0 :             (try! (fee-helper token-x token-y factor-x))
+     302                 :          0 :             (try! (fee-helper token-y token-z factor-y))
+     303                 :            :     ))
+     304                 :            : )
+     305                 :            : (define-read-only (fee-helper-b 
+     306                 :            :     (token-x principal) (token-y principal) (token-z principal) (token-w principal)
+     307                 :            :     (factor-x uint) (factor-y uint) (factor-z uint))
+     308                 :          0 :     (ok (+ 
+     309                 :          0 :             (try! (fee-helper-a token-x token-y token-z factor-x factor-y))
+     310                 :          0 :             (try! (fee-helper token-z token-w factor-z))
+     311                 :            :     ))
+     312                 :            : )
+     313                 :            : (define-read-only (fee-helper-c 
+     314                 :            :     (token-x principal) (token-y principal) (token-z principal) (token-w principal) (token-v principal)
+     315                 :            :     (factor-x uint) (factor-y uint) (factor-z uint) (factor-w uint))
+     316                 :          0 :     (ok (+ 
+     317                 :          0 :             (try! (fee-helper-a token-x token-y token-z factor-x factor-y))
+     318                 :          0 :             (try! (fee-helper-a token-z token-w token-v factor-z factor-w))
+     319                 :            :     ))
+     320                 :            : )
+     321                 :            : (define-read-only (get-invariant (balance-x uint) (balance-y uint) (t uint))
+     322                 :          1 :     (if (>= t (var-get switch-threshold))
+     323            [ + ]:          1 :         (+ (mul-down (- ONE_8 t) (+ balance-x balance-y)) (mul-down t (mul-down balance-x balance-y)))
+     324            [ - ]:          0 :         (+ (pow-down balance-x (- ONE_8 t)) (pow-down balance-y (- ONE_8 t)))
+     325                 :            :     )
+     326                 :            : )
+     327                 :            : (define-public (set-switch-threshold (new-threshold uint))
+     328                 :          0 :     (begin 
+     329                 :          0 :         (try! (check-is-owner))
+     330            [ - ]:          0 :         (asserts! (<= new-threshold ONE_8) ERR-SWITCH-THRESHOLD-BIGGER-THAN-ONE)
+     331                 :          0 :         (ok (var-set switch-threshold new-threshold))
+     332                 :            :     )
+     333                 :            : )
+     334                 :            : (define-public (pause (new-paused bool))
+     335                 :          0 :     (begin 
+     336                 :          0 :         (try! (check-is-owner))
+     337                 :          0 :         (ok (var-set paused new-paused))
+     338                 :            :     )
+     339                 :            : )
+     340                 :            : (define-public (set-contract-owner (owner principal))
+     341                 :          0 :   (begin
+     342                 :          0 :     (try! (check-is-owner))
+     343                 :          0 :     (ok (var-set contract-owner owner))
+     344                 :            :   )
+     345                 :            : )
+     346                 :            : (define-public (set-fee-rebate (token-x principal) (token-y principal) (factor uint) (fee-rebate uint))
+     347                 :          0 :     (let 
+     348                 :            :         (            
+     349                 :          0 :             (pool (try! (get-pool-details token-x token-y factor)))
+     350                 :            :         )
+     351                 :          0 :         (try! (check-is-owner))
+     352                 :          0 :         (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool { fee-rebate: fee-rebate }))
+     353                 :          0 :         (ok true)     
+     354                 :            :     )
+     355                 :            : )
+     356                 :            : (define-public (set-pool-owner (token-x principal) (token-y principal) (factor uint) (pool-owner principal))
+     357                 :          0 :     (let 
+     358                 :            :         (
+     359                 :          0 :             (pool (try! (get-pool-details token-x token-y factor)))
+     360                 :            :         )
+     361                 :          0 :         (try! (check-is-owner))
+     362                 :          0 :         (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool { pool-owner: pool-owner }))
+     363                 :          0 :         (ok true)     
+     364                 :            :     )
+     365                 :            : )
+     366                 :            : (define-public (set-start-block (token-x principal) (token-y principal) (factor uint) (new-start-block uint))
+     367                 :          1 :     (let
+     368                 :            :         (
+     369                 :          1 :             (pool (try! (get-pool-details token-x token-y factor)))
+     370                 :            :         )
+     371    [ - ][ +  - ]:          1 :         (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED)
+     372                 :          1 :         (ok
+     373                 :          1 :             (map-set 
+     374                 :          0 :                 pools-data-map 
+     375                 :          1 :                 { token-x: token-x, token-y: token-y, factor: factor } 
+     376                 :          1 :                 (merge pool {start-block: new-start-block})
+     377                 :            :             )
+     378                 :            :         )    
+     379                 :            :     )
+     380                 :            : )
+     381                 :            : (define-public (set-end-block (token-x principal) (token-y principal) (factor uint) (new-end-block uint))
+     382                 :          0 :     (let
+     383                 :            :         (
+     384                 :          0 :             (pool (try! (get-pool-details token-x token-y factor)))
+     385                 :            :         )
+     386    [ - ][ -  - ]:          0 :         (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED)
+     387                 :          0 :         (ok
+     388                 :          0 :             (map-set 
+     389                 :          0 :                 pools-data-map 
+     390                 :          0 :                 { token-x: token-x, token-y: token-y, factor: factor } 
+     391                 :          0 :                 (merge pool {end-block: new-end-block})
+     392                 :            :             )
+     393                 :            :         )    
+     394                 :            :     )
+     395                 :            : )
+     396                 :            : (define-public (set-max-in-ratio (token-x principal) (token-y principal) (factor uint) (new-max-in-ratio uint))
+     397                 :          1 :     (let
+     398                 :            :         (
+     399                 :          1 :             (pool (try! (get-pool-details token-x token-y factor)))
+     400                 :            :         )
+     401    [ - ][ +  - ]:          1 :         (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED)
+     402                 :          1 :         (ok
+     403                 :          1 :             (map-set 
+     404                 :          0 :                 pools-data-map 
+     405                 :          1 :                 { token-x: token-x, token-y: token-y, factor: factor } 
+     406                 :          1 :                 (merge pool {max-in-ratio: new-max-in-ratio})
+     407                 :            :             )
+     408                 :            :         )    
+     409                 :            :     )
+     410                 :            : )
+     411                 :            : (define-public (set-max-out-ratio (token-x principal) (token-y principal) (factor uint) (new-max-out-ratio uint))
+     412                 :          1 :     (let
+     413                 :            :         (
+     414                 :          1 :             (pool (try! (get-pool-details token-x token-y factor)))
+     415                 :            :         )
+     416    [ - ][ +  - ]:          1 :         (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED)
+     417                 :          1 :         (ok
+     418                 :          1 :             (map-set 
+     419                 :          0 :                 pools-data-map 
+     420                 :          1 :                 { token-x: token-x, token-y: token-y, factor: factor } 
+     421                 :          1 :                 (merge pool {max-out-ratio: new-max-out-ratio})
+     422                 :            :             )
+     423                 :            :         )    
+     424                 :            :     )
+     425                 :            : )
+     426                 :            : (define-public (set-oracle-enabled (token-x principal) (token-y principal) (factor uint) (enabled bool))
+     427                 :          0 :     (let
+     428                 :            :         (
+     429                 :          0 :             (pool (try! (get-pool-details token-x token-y factor)))
+     430                 :            :         )
+     431    [ - ][ -  - ]:          0 :         (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED)
+     432                 :          0 :         (ok
+     433                 :          0 :             (map-set 
+     434                 :          0 :                 pools-data-map 
+     435                 :          0 :                 { token-x: token-x, token-y: token-y, factor: factor } 
+     436                 :          0 :                 (merge pool {oracle-enabled: enabled})
+     437                 :            :             )
+     438                 :            :         )
+     439                 :            :     )    
+     440                 :            : )
+     441                 :            : (define-public (set-oracle-average (token-x principal) (token-y principal) (factor uint) (new-oracle-average uint))
+     442                 :          0 :     (let
+     443                 :            :         (
+     444                 :          0 :             (pool (try! (get-pool-details token-x token-y factor)))
+     445                 :            :         )
+     446    [ - ][ -  - ]:          0 :         (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED)
+     447            [ - ]:          0 :         (asserts! (get oracle-enabled pool) ERR-ORACLE-NOT-ENABLED)
+     448            [ - ]:          0 :         (asserts! (< new-oracle-average ONE_8) ERR-ORACLE-AVERAGE-BIGGER-THAN-ONE)
+     449                 :          0 :         (ok 
+     450                 :          0 :             (map-set 
+     451                 :          0 :                 pools-data-map 
+     452                 :          0 :                 { token-x: token-x, token-y: token-y, factor: factor } 
+     453                 :          0 :                 (merge pool 
+     454                 :            :                     {
+     455                 :          0 :                     oracle-average: new-oracle-average,
+     456                 :          0 :                     oracle-resilient: (try! (get-oracle-instant token-x token-y factor))
+     457                 :            :                     }
+     458                 :            :                 )
+     459                 :            :             )
+     460                 :            :         )
+     461                 :            :     )    
+     462                 :            : )
+     463                 :            : (define-public (set-threshold-x (token-x principal) (token-y principal) (factor uint) (new-threshold uint))
+     464                 :          0 :     (let 
+     465                 :            :         (
+     466                 :          0 :             (pool (try! (get-pool-details token-x token-y factor)))
+     467                 :            :         )
+     468    [ - ][ -  - ]:          0 :         (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED)
+     469                 :          0 :         (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool { threshold-x: new-threshold }))
+     470                 :          0 :         (ok true)
+     471                 :            :     )
+     472                 :            : )
+     473                 :            : (define-public (set-threshold-y (token-x principal) (token-y principal) (factor uint) (new-threshold uint))
+     474                 :          0 :     (let 
+     475                 :            :         (
+     476                 :          0 :             (pool (try! (get-pool-details token-x token-y factor)))
+     477                 :            :         )
+     478    [ - ][ -  - ]:          0 :         (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED)
+     479                 :          0 :         (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool { threshold-y: new-threshold }))
+     480                 :          0 :         (ok true)
+     481                 :            :     )
+     482                 :            : )
+     483                 :            : (define-public (set-fee-rate-x (token-x principal) (token-y principal) (factor uint) (fee-rate-x uint))
+     484                 :          0 :     (let 
+     485                 :            :         (        
+     486                 :          0 :             (pool (try! (get-pool-details token-x token-y factor)))
+     487                 :            :         )
+     488    [ - ][ -  - ]:          0 :         (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED)
+     489                 :          0 :         (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool { fee-rate-x: fee-rate-x }))
+     490                 :          0 :         (ok true)     
+     491                 :            :     )
+     492                 :            : )
+     493                 :            : (define-public (set-fee-rate-y (token-x principal) (token-y principal) (factor uint) (fee-rate-y uint))
+     494                 :          0 :     (let 
+     495                 :            :         (    
+     496                 :          0 :             (pool (try! (get-pool-details token-x token-y factor)))
+     497                 :            :         )
+     498    [ - ][ -  - ]:          0 :         (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED)
+     499                 :          0 :         (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool { fee-rate-y: fee-rate-y }))
+     500                 :          0 :         (ok true)     
+     501                 :            :     )
+     502                 :            : )
+     503                 :            : (define-public (create-pool (token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (factor uint) (pool-owner principal) (dx uint) (dy uint)) 
+     504                 :          1 :     (let
+     505                 :            :         (
+     506                 :          1 :             (pool-id (+ (var-get pool-nonce) u1))
+     507                 :          1 :             (token-x (contract-of token-x-trait))
+     508                 :          1 :             (token-y (contract-of token-y-trait))
+     509                 :          0 :             (pool-data {
+     510                 :          1 :                 pool-id: pool-id,
+     511                 :          1 :                 total-supply: u0,
+     512                 :          1 :                 balance-x: u0,
+     513                 :          1 :                 balance-y: u0,
+     514                 :          1 :                 pool-owner: pool-owner,
+     515                 :          1 :                 fee-rate-x: u0,
+     516                 :          1 :                 fee-rate-y: u0,
+     517                 :          1 :                 fee-rebate: u0,
+     518                 :          1 :                 oracle-enabled: false,
+     519                 :          1 :                 oracle-average: u0,
+     520                 :          1 :                 oracle-resilient: u0,
+     521                 :          1 :                 start-block: u340282366920938463463374607431768211455,
+     522                 :          1 :                 end-block: u340282366920938463463374607431768211455,
+     523                 :          1 :                 threshold-x: u0,
+     524                 :          1 :                 threshold-y: u0,
+     525                 :          1 :                 max-in-ratio: u0,
+     526                 :          1 :                 max-out-ratio: u0
+     527                 :            :             })
+     528                 :            :         )
+     529            [ - ]:          1 :         (asserts! (not (is-paused)) ERR-PAUSED)
+     530                 :          1 :         (asserts!
+     531                 :          1 :             (and
+     532            [ + ]:          1 :                 (is-none (map-get? pools-data-map { token-x: token-x, token-y: token-y, factor: factor }))
+     533            [ + ]:          1 :                 (is-none (map-get? pools-data-map { token-x: token-y, token-y: token-x, factor: factor }))
+     534                 :            :             )
+     535            [ - ]:          0 :             ERR-POOL-ALREADY-EXISTS
+     536                 :            :         )             
+     537                 :          1 :         (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } pool-data)
+     538                 :          1 :         (map-set pools-id-map pool-id { token-x: token-x, token-y: token-y, factor: factor })
+     539                 :          1 :         (var-set pool-nonce pool-id)
+     540                 :          1 :         (try! (add-to-position token-x-trait token-y-trait factor dx (some dy)))
+     541                 :          1 :         (print { object: "pool", action: "created", data: pool-data })
+     542                 :          1 :         (ok true)
+     543                 :            :     )
+     544                 :            : )
+     545                 :            : (define-public (add-to-position (token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (factor uint) (dx uint) (max-dy (optional uint)))
+     546                 :          1 :     (let
+     547                 :            :         (
+     548                 :          1 :             (token-x (contract-of token-x-trait))
+     549                 :          1 :             (token-y (contract-of token-y-trait))
+     550                 :          1 :             (pool (try! (get-pool-details token-x token-y factor)))
+     551                 :          1 :             (balance-x (get balance-x pool))
+     552                 :          1 :             (balance-y (get balance-y pool))
+     553                 :          1 :             (total-supply (get total-supply pool))
+     554                 :          1 :             (add-data (try! (get-token-given-position token-x token-y factor dx max-dy)))
+     555                 :          1 :             (new-supply (get token add-data))
+     556                 :          1 :             (dy (get dy add-data))
+     557                 :          1 :             (pool-updated (merge pool {
+     558                 :          1 :                 total-supply: (+ new-supply total-supply),
+     559                 :          1 :                 balance-x: (+ balance-x dx),
+     560                 :          1 :                 balance-y: (+ balance-y dy)
+     561                 :            :             }))
+     562                 :          1 :             (sender tx-sender)
+     563                 :            :         )
+     564            [ - ]:          1 :         (asserts! (not (is-paused)) ERR-PAUSED)
+     565    [ - ][ +  + ]:          1 :         (asserts! (and (> dx u0) (> dy u0)) ERR-INVALID-LIQUIDITY)
+     566            [ - ]:          1 :         (asserts! (>= (default-to u340282366920938463463374607431768211455 max-dy) dy) ERR-EXCEEDS-MAX-SLIPPAGE)
+     567                 :          1 :         (try! (contract-call? token-x-trait transfer-fixed dx sender .alex-vault-v1-1 none))
+     568                 :          1 :         (try! (contract-call? token-y-trait transfer-fixed dy sender .alex-vault-v1-1 none))
+     569                 :          1 :         (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } pool-updated)
+     570                 :          1 :         (as-contract (try! (contract-call? .token-amm-swap-pool-v1-1 mint-fixed (get pool-id pool) new-supply sender)))
+     571                 :          1 :         (print { object: "pool", action: "liquidity-added", data: pool-updated })
+     572                 :          1 :         (ok {supply: new-supply, dx: dx, dy: dy})
+     573                 :            :     )
+     574                 :            : )
+     575                 :            : (define-public (reduce-position (token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (factor uint) (percent uint))
+     576                 :          0 :     (let
+     577                 :            :         (
+     578                 :          0 :             (token-x (contract-of token-x-trait))
+     579                 :          0 :             (token-y (contract-of token-y-trait))
+     580                 :          0 :             (pool (try! (get-pool-details token-x token-y factor)))
+     581                 :          0 :             (balance-x (get balance-x pool))
+     582                 :          0 :             (balance-y (get balance-y pool))
+     583                 :          0 :             (total-shares (unwrap-panic (contract-call? .token-amm-swap-pool-v1-1 get-balance-fixed (get pool-id pool) tx-sender)))
+     584         [ -  - ]:          0 :             (shares (if (is-eq percent ONE_8) total-shares (mul-down total-shares percent)))
+     585                 :          0 :             (total-supply (get total-supply pool))
+     586                 :          0 :             (reduce-data (try! (get-position-given-burn token-x token-y factor shares)))
+     587                 :          0 :             (dx (get dx reduce-data))
+     588                 :          0 :             (dy (get dy reduce-data))
+     589                 :          0 :             (pool-updated (merge pool {
+     590         [ -  - ]:          0 :                 total-supply: (if (<= total-supply shares) u0 (- total-supply shares)),
+     591         [ -  - ]:          0 :                 balance-x: (if (<= balance-x dx) u0 (- balance-x dx)),
+     592         [ -  - ]:          0 :                 balance-y: (if (<= balance-y dy) u0 (- balance-y dy))
+     593                 :            :                 })
+     594                 :            :             )
+     595                 :          0 :             (sender tx-sender)
+     596                 :            :         )  
+     597            [ - ]:          0 :         (asserts! (not (is-paused)) ERR-PAUSED)       
+     598            [ - ]:          0 :         (asserts! (<= percent ONE_8) ERR-PERCENT-GREATER-THAN-ONE)
+     599                 :          0 :         (as-contract (try! (contract-call? .alex-vault-v1-1 transfer-ft-two token-x-trait dx token-y-trait dy sender)))
+     600                 :          0 :         (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } pool-updated)
+     601                 :          0 :         (as-contract (try! (contract-call? .token-amm-swap-pool-v1-1 burn-fixed (get pool-id pool) shares sender)))
+     602                 :          0 :         (print { object: "pool", action: "liquidity-removed", data: pool-updated })
+     603                 :          0 :         (ok {dx: dx, dy: dy})
+     604                 :            :     )
+     605                 :            : )
+     606                 :            : (define-public (swap-x-for-y (token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (factor uint) (dx uint) (min-dy (optional uint)))
+     607                 :          8 :     (let
+     608                 :            :         (
+     609                 :          8 :             (token-x (contract-of token-x-trait))
+     610                 :          8 :             (token-y (contract-of token-y-trait))
+     611                 :          8 :             (pool (try! (get-pool-details token-x token-y factor)))
+     612                 :          8 :             (balance-x (get balance-x pool))
+     613                 :          8 :             (balance-y (get balance-y pool))
+     614                 :          8 :             (fee (mul-up dx (get fee-rate-x pool)))
+     615         [ -  + ]:          8 :             (dx-net-fees (if (<= dx fee) u0 (- dx fee)))
+     616                 :          8 :             (fee-rebate (mul-down fee (get fee-rebate pool)))
+     617                 :          8 :             (dy (try! (get-y-given-x token-x token-y factor dx-net-fees)))                
+     618                 :          8 :             (pool-updated (merge pool {
+     619                 :          8 :                 balance-x: (+ balance-x dx-net-fees fee-rebate),
+     620         [ -  + ]:          8 :                 balance-y: (if (<= balance-y dy) u0 (- balance-y dy)),
+     621         [ -  + ]:          8 :                 oracle-resilient: (if (get oracle-enabled pool) (try! (get-oracle-resilient token-x token-y factor)) u0)
+     622                 :            :                 })
+     623                 :            :             )
+     624                 :          8 :             (sender tx-sender)             
+     625                 :            :         )
+     626            [ - ]:          8 :         (asserts! (not (is-paused)) ERR-PAUSED)
+     627                 :          8 :         (try! (check-pool-status token-x token-y factor))
+     628            [ - ]:          8 :         (asserts! (> dx u0) ERR-INVALID-LIQUIDITY)
+     629            [ - ]:          8 :         (asserts! (<= (div-down dy dx-net-fees) (get-price-internal balance-x balance-y factor)) ERR-INVALID-LIQUIDITY)
+     630            [ - ]:          8 :         (asserts! (<= (default-to u0 min-dy) dy) ERR-EXCEEDS-MAX-SLIPPAGE)
+     631                 :          8 :         (try! (contract-call? token-x-trait transfer-fixed dx sender .alex-vault-v1-1 none))
+     632         [ +  + ]:          8 :         (and (> dy u0) (as-contract (try! (contract-call? .alex-vault-v1-1 transfer-ft token-y-trait dy sender))))
+     633                 :          8 :         (as-contract (try! (contract-call? .alex-vault-v1-1 add-to-reserve token-x (- fee fee-rebate))))
+     634                 :          8 :         (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } pool-updated)
+     635                 :          8 :         (print { object: "pool", action: "swap-x-for-y", data: pool-updated })
+     636                 :          8 :         (ok {dx: dx-net-fees, dy: dy})
+     637                 :            :     )
+     638                 :            : )
+     639                 :            : (define-public (swap-y-for-x (token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (factor uint) (dy uint) (min-dx (optional uint)))
+     640                 :          9 :     (let
+     641                 :            :         (
+     642                 :          9 :             (token-x (contract-of token-x-trait))
+     643                 :          9 :             (token-y (contract-of token-y-trait))
+     644                 :          9 :             (pool (try! (get-pool-details token-x token-y factor)))
+     645                 :          9 :             (balance-x (get balance-x pool))
+     646                 :          9 :             (balance-y (get balance-y pool))
+     647                 :          9 :             (fee (mul-up dy (get fee-rate-y pool)))
+     648         [ -  + ]:          9 :             (dy-net-fees (if (<= dy fee) u0 (- dy fee)))
+     649                 :          9 :             (fee-rebate (mul-down fee (get fee-rebate pool)))
+     650                 :          9 :             (dx (try! (get-x-given-y token-x token-y factor dy-net-fees)))
+     651                 :          9 :             (pool-updated (merge pool {
+     652         [ -  + ]:          9 :                 balance-x: (if (<= balance-x dx) u0 (- balance-x dx)),
+     653                 :          9 :                 balance-y: (+ balance-y dy-net-fees fee-rebate),
+     654         [ -  + ]:          9 :                 oracle-resilient: (if (get oracle-enabled pool) (try! (get-oracle-resilient token-x token-y factor)) u0)
+     655                 :            :                 })
+     656                 :            :             )
+     657                 :          9 :             (sender tx-sender)
+     658                 :            :         )
+     659            [ - ]:          9 :         (asserts! (not (is-paused)) ERR-PAUSED)
+     660                 :          9 :         (try! (check-pool-status token-x token-y factor))
+     661            [ - ]:          9 :         (asserts! (> dy u0) ERR-INVALID-LIQUIDITY)        
+     662            [ - ]:          9 :         (asserts! (>= (div-down dy-net-fees dx) (get-price-internal balance-x balance-y factor)) ERR-INVALID-LIQUIDITY)
+     663            [ - ]:          9 :         (asserts! (<= (default-to u0 min-dx) dx) ERR-EXCEEDS-MAX-SLIPPAGE)        
+     664                 :          9 :         (try! (contract-call? token-y-trait transfer-fixed dy sender .alex-vault-v1-1 none))
+     665         [ +  + ]:          9 :         (and (> dx u0) (as-contract (try! (contract-call? .alex-vault-v1-1 transfer-ft token-x-trait dx sender))))            
+     666                 :          9 :         (as-contract (try! (contract-call? .alex-vault-v1-1 add-to-reserve token-y (- fee fee-rebate))))
+     667                 :          9 :         (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } pool-updated)
+     668                 :          9 :         (print { object: "pool", action: "swap-y-for-x", data: pool-updated })
+     669                 :          9 :         (ok {dx: dx, dy: dy-net-fees})
+     670                 :            :     )
+     671                 :            : )
+     672                 :            : (define-public (swap-helper (token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (factor uint) (dx uint) (min-dy (optional uint)))
+     673                 :         17 :     (if (is-some (get-pool-exists (contract-of token-x-trait) (contract-of token-y-trait) factor))
+     674            [ + ]:          8 :         (ok (get dy (try! (swap-x-for-y token-x-trait token-y-trait factor dx min-dy))))
+     675            [ + ]:          9 :         (ok (get dx (try! (swap-y-for-x token-y-trait token-x-trait factor dx min-dy))))
+     676                 :            :     )
+     677                 :            : )
+     678                 :            : (define-public (swap-helper-a (token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (token-z-trait <ft-trait>) (factor-x uint) (factor-y uint) (dx uint) (min-dz (optional uint)))
+     679                 :          0 :     (swap-helper token-y-trait token-z-trait factor-y (try! (swap-helper token-x-trait token-y-trait factor-x dx none)) min-dz)
+     680                 :            : )
+     681                 :            : (define-public (swap-helper-b 
+     682                 :            :     (token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (token-z-trait <ft-trait>) (token-w-trait <ft-trait>) 
+     683                 :            :     (factor-x uint) (factor-y uint) (factor-z uint)
+     684                 :            :     (dx uint) (min-dw (optional uint)))
+     685                 :          0 :     (swap-helper token-z-trait token-w-trait factor-z 
+     686                 :          0 :         (try! (swap-helper-a token-x-trait token-y-trait token-z-trait factor-x factor-y dx none)) none)
+     687                 :            : )
+     688                 :            : (define-public (swap-helper-c
+     689                 :            :     (token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (token-z-trait <ft-trait>) (token-w-trait <ft-trait>) (token-v-trait <ft-trait>)
+     690                 :            :     (factor-x uint) (factor-y uint) (factor-z uint) (factor-w uint)
+     691                 :            :     (dx uint) (min-dv (optional uint)))
+     692                 :          0 :     (swap-helper-a token-z-trait token-w-trait token-v-trait factor-z factor-w
+     693                 :          0 :         (try! (swap-helper-a token-x-trait token-y-trait token-z-trait factor-x factor-y dx none)) min-dv)
+     694                 :            : )
+     695                 :            : (define-private (check-is-owner)
+     696            [ - ]:          0 :     (ok (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED))
+     697                 :            : )
+     698                 :            : (define-private (get-price-internal (balance-x uint) (balance-y uint) (factor uint))
+     699                 :         17 :     (if (>= factor (var-get switch-threshold))
+     700            [ + ]:         17 :         (div-down (+ (- ONE_8 factor) (mul-down factor balance-y)) (+ (- ONE_8 factor) (mul-down factor balance-x)))
+     701            [ - ]:          0 :         (pow-down (div-down balance-y balance-x) factor)
+     702                 :            :     )
+     703                 :            : )
+     704                 :            : (define-private (get-y-given-x-internal (balance-x uint) (balance-y uint) (t uint) (dx uint))
+     705                 :         16 :     (if (>= t (var-get switch-threshold))
+     706            [ + ]:         16 :     (let
+     707                 :            :         (
+     708         [ +  - ]:         16 :             (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t)))
+     709                 :            :         )        
+     710                 :         16 :         (div-down (+ (mul-down t-comp dx) (mul-down t (mul-down dx balance-y))) (+ t-comp (mul-down t (+ balance-x dx))))
+     711                 :            :     )
+     712            [ - ]:          0 :     (let
+     713                 :            :         (
+     714         [ -  - ]:          0 :             (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t)))
+     715                 :          0 :             (t-comp-num-uncapped (div-up ONE_8 t-comp))
+     716         [ -  - ]:          0 :             (t-comp-num (if (< t-comp-num-uncapped MILD_EXPONENT_BOUND) t-comp-num-uncapped MILD_EXPONENT_BOUND))            
+     717                 :          0 :             (x-pow (pow-up balance-x t-comp))
+     718                 :          0 :             (y-pow (pow-up balance-y t-comp))
+     719                 :          0 :             (x-dx-pow (pow-down (+ balance-x dx) t-comp))
+     720                 :          0 :             (add-term (+ x-pow y-pow))
+     721         [ -  - ]:          0 :             (term (if (<= add-term x-dx-pow) u0 (- add-term x-dx-pow)))
+     722                 :          0 :             (final-term (pow-up term t-comp-num))
+     723                 :            :         )        
+     724         [ -  - ]:          0 :         (if (<= balance-y final-term) u0 (- balance-y final-term))
+     725                 :            :     )  
+     726                 :            :     )      
+     727                 :            : )
+     728                 :            : (define-private (get-x-given-y-internal (balance-x uint) (balance-y uint) (t uint) (dy uint))
+     729                 :         18 :     (if (>= t (var-get switch-threshold))
+     730            [ + ]:         18 :     (let
+     731                 :            :         (
+     732         [ +  - ]:         18 :             (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t)))
+     733                 :            :         )        
+     734                 :         18 :         (div-down (+ (mul-down t-comp dy) (mul-down t (mul-down dy balance-x))) (+ t-comp (mul-down t (+ balance-y dy))))
+     735                 :            :     )  
+     736            [ - ]:          0 :     (let 
+     737                 :            :         (          
+     738         [ -  - ]:          0 :             (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t)))
+     739                 :          0 :             (t-comp-num-uncapped (div-up ONE_8 t-comp))
+     740         [ -  - ]:          0 :             (t-comp-num (if (< t-comp-num-uncapped MILD_EXPONENT_BOUND) t-comp-num-uncapped MILD_EXPONENT_BOUND))            
+     741                 :          0 :             (x-pow (pow-up balance-x t-comp))
+     742                 :          0 :             (y-pow (pow-up balance-y t-comp))
+     743                 :          0 :             (y-dy-pow (pow-down (+ balance-y dy) t-comp))
+     744                 :          0 :             (add-term (+ x-pow y-pow))
+     745         [ -  - ]:          0 :             (term (if (<= add-term y-dy-pow) u0 (- add-term y-dy-pow)))
+     746                 :          0 :             (final-term (pow-up term t-comp-num))
+     747                 :            :         )
+     748         [ -  - ]:          0 :         (if (<= balance-x final-term) u0 (- balance-x final-term))
+     749                 :            :     )
+     750                 :            :     )    
+     751                 :            : )
+     752                 :            : (define-private (get-y-in-given-x-out-internal (balance-x uint) (balance-y uint) (t uint) (dx uint))    
+     753                 :          0 :     (if (>= t (var-get switch-threshold))
+     754            [ - ]:          0 :     (let 
+     755                 :            :         (
+     756         [ -  - ]:          0 :             (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t)))
+     757                 :            :         )
+     758                 :          0 :         (div-down (+ (mul-down t-comp dx) (mul-down t (mul-down dx balance-y))) (+ t-comp (mul-down t (- balance-x dx))))
+     759                 :            :     )
+     760            [ - ]:          0 :     (let 
+     761                 :            :         (
+     762         [ -  - ]:          0 :             (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t)))
+     763                 :          0 :             (t-comp-num-uncapped (div-down ONE_8 t-comp))
+     764         [ -  - ]:          0 :             (t-comp-num (if (< t-comp-num-uncapped MILD_EXPONENT_BOUND) t-comp-num-uncapped MILD_EXPONENT_BOUND))            
+     765                 :          0 :             (x-pow (pow-down balance-x t-comp))
+     766                 :          0 :             (y-pow (pow-down balance-y t-comp))
+     767         [ -  - ]:          0 :             (x-dx-pow (pow-up (if (<= balance-x dx) u0 (- balance-x dx)) t-comp))
+     768                 :          0 :             (add-term (+ x-pow y-pow))
+     769         [ -  - ]:          0 :             (term (if (<= add-term x-dx-pow) u0 (- add-term x-dx-pow)))
+     770                 :          0 :             (final-term (pow-down term t-comp-num))
+     771                 :            :         )
+     772         [ -  - ]:          0 :         (if (<= final-term balance-y) u0 (- final-term balance-y))
+     773                 :            :     )
+     774                 :            :     )    
+     775                 :            : )
+     776                 :            : (define-private (get-x-in-given-y-out-internal (balance-x uint) (balance-y uint) (t uint) (dy uint))
+     777                 :          0 :     (if (>= t (var-get switch-threshold))
+     778            [ - ]:          0 :     (let 
+     779                 :            :         (          
+     780         [ -  - ]:          0 :             (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t)))
+     781                 :            :         )
+     782                 :          0 :         (div-down (+ (mul-down t-comp dy) (mul-down t (mul-down dy balance-x))) (+ t-comp (mul-down t (- balance-y dy))))
+     783                 :            :     )
+     784            [ - ]:          0 :     (let 
+     785                 :            :         (          
+     786         [ -  - ]:          0 :             (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t)))
+     787                 :          0 :             (t-comp-num-uncapped (div-down ONE_8 t-comp))
+     788         [ -  - ]:          0 :             (t-comp-num (if (< t-comp-num-uncapped MILD_EXPONENT_BOUND) t-comp-num-uncapped MILD_EXPONENT_BOUND))            
+     789                 :          0 :             (x-pow (pow-down balance-x t-comp))
+     790                 :          0 :             (y-pow (pow-down balance-y t-comp))
+     791         [ -  - ]:          0 :             (y-dy-pow (pow-up (if (<= balance-y dy) u0 (- balance-y dy)) t-comp))
+     792                 :          0 :             (add-term (+ x-pow y-pow))
+     793         [ -  - ]:          0 :             (term (if (<= add-term y-dy-pow) u0 (- add-term y-dy-pow)))
+     794                 :          0 :             (final-term (pow-down term t-comp-num))
+     795                 :            :         )
+     796         [ -  - ]:          0 :         (if (<= final-term balance-x) u0 (- final-term balance-x))
+     797                 :            :     )
+     798                 :            :     )    
+     799                 :            : )
+     800                 :            : (define-private (get-x-given-price-internal (balance-x uint) (balance-y uint) (t uint) (price uint))
+     801                 :          0 :     (if (>= t (var-get switch-threshold))
+     802            [ - ]:          0 :     (let
+     803                 :            :         (
+     804                 :          0 :             (power (pow-down (div-down (get-price-internal balance-x balance-y t) price) u50000000))
+     805                 :            :         )
+     806         [ -  - ]:          0 :         (mul-down balance-x (if (<= power ONE_8) u0 (- power ONE_8)))
+     807                 :            :     ) 
+     808            [ - ]:          0 :     (let 
+     809                 :            :         (
+     810         [ -  - ]:          0 :             (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t)))
+     811                 :          0 :             (t-comp-num-uncapped (div-down ONE_8 t-comp))
+     812         [ -  - ]:          0 :             (t-comp-num (if (< t-comp-num-uncapped MILD_EXPONENT_BOUND) t-comp-num-uncapped MILD_EXPONENT_BOUND))            
+     813                 :          0 :             (numer (+ ONE_8 (pow-down (get-price-internal balance-x balance-y t) (div-down t-comp t))))
+     814                 :          0 :             (denom (+ ONE_8 (pow-down price (div-down t-comp t))))
+     815                 :          0 :             (lead-term (pow-down (div-down numer denom) t-comp-num))
+     816                 :            :         )
+     817         [ -  - ]:          0 :         (if (<= lead-term ONE_8) u0 (mul-up balance-x (- lead-term ONE_8)))
+     818                 :            :     )
+     819                 :            :     )    
+     820                 :            : )
+     821                 :            : (define-private (get-y-given-price-internal (balance-x uint) (balance-y uint) (t uint) (price uint))
+     822                 :          0 :     (if (>= t (var-get switch-threshold))
+     823            [ - ]:          0 :     (let
+     824                 :            :         (            
+     825                 :          0 :             (power (pow-down (div-down price (get-price-internal balance-x balance-y t)) u50000000))
+     826                 :            :         )
+     827         [ -  - ]:          0 :         (mul-down balance-y (if (<= power ONE_8) u0 (- power ONE_8)))
+     828                 :            :     )
+     829            [ - ]:          0 :     (let 
+     830                 :            :         (
+     831         [ -  - ]:          0 :             (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t)))
+     832                 :          0 :             (t-comp-num-uncapped (div-down ONE_8 t-comp))
+     833         [ -  - ]:          0 :             (t-comp-num (if (< t-comp-num-uncapped MILD_EXPONENT_BOUND) t-comp-num-uncapped MILD_EXPONENT_BOUND))            
+     834                 :          0 :             (numer (+ ONE_8 (pow-down (get-price-internal balance-x balance-y t) (div-down t-comp t))))
+     835                 :          0 :             (denom (+ ONE_8 (pow-down price (div-down t-comp t))))
+     836                 :          0 :             (lead-term (pow-down (div-down numer denom) t-comp-num))
+     837                 :            :         )
+     838         [ -  - ]:          0 :         (if (<= ONE_8 lead-term) u0 (mul-up balance-y (- ONE_8 lead-term)))
+     839                 :            :     )
+     840                 :            :     )    
+     841                 :            : )
+     842                 :            : (define-private (get-token-given-position-internal (balance-x uint) (balance-y uint) (t uint) (total-supply uint) (dx uint) (dy uint))
+     843                 :          1 :     (if (is-eq total-supply u0)
+     844                 :          1 :         {token: (get-invariant dx dy t), dy: dy}
+     845                 :          0 :         {token: (div-down (mul-down total-supply dx) balance-x), dy: (div-down (mul-down balance-y dx) balance-x)}
+     846                 :            :     )
+     847                 :            : )
+     848                 :            : (define-private (get-position-given-mint-internal (balance-x uint) (balance-y uint) (t uint) (total-supply uint) (token-amount uint))
+     849                 :          0 :     (let
+     850                 :            :         (
+     851                 :          0 :             (token-div-supply (div-down token-amount total-supply))
+     852                 :            :         )                
+     853                 :          0 :         {dx: (mul-down balance-x token-div-supply), dy: (mul-down balance-y token-div-supply)}    
+     854                 :            :     )
+     855                 :            : )
+     856                 :            : (define-private (get-position-given-burn-internal (balance-x uint) (balance-y uint) (t uint) (total-supply uint) (token-amount uint))
+     857                 :          0 :     (get-position-given-mint-internal balance-x balance-y t total-supply token-amount)
+     858                 :            : )
+     859                 :            : (define-constant ONE_8 u100000000) ;; 8 decimal places
+     860                 :            : (define-constant MAX_POW_RELATIVE_ERROR u4) 
+     861                 :            : (define-private (mul-down (a uint) (b uint))
+     862                 :        258 :     (/ (* a b) ONE_8)
+     863                 :            : )
+     864                 :            : (define-private (mul-up (a uint) (b uint))
+     865                 :         17 :     (let
+     866                 :            :         (
+     867                 :         17 :             (product (* a b))
+     868                 :            :        )
+     869                 :         17 :         (if (is-eq product u0)
+     870            [ + ]:         17 :             u0
+     871            [ - ]:          0 :             (+ u1 (/ (- product u1) ONE_8))
+     872                 :            :        )
+     873                 :            :    )
+     874                 :            : )
+     875                 :            : (define-private (div-down (a uint) (b uint))
+     876                 :         68 :     (if (is-eq a u0)
+     877            [ - ]:          0 :         u0
+     878            [ + ]:         68 :         (/ (* a ONE_8) b)
+     879                 :            :    )
+     880                 :            : )
+     881                 :            : (define-private (div-up (a uint) (b uint))
+     882                 :          0 :     (if (is-eq a u0)
+     883            [ - ]:          0 :         u0
+     884            [ - ]:          0 :         (+ u1 (/ (- (* a ONE_8) u1) b))
+     885                 :            :     )
+     886                 :            : )
+     887                 :            : (define-private (pow-down (a uint) (b uint))    
+     888                 :          0 :     (let
+     889                 :            :         (
+     890                 :          0 :             (raw (unwrap-panic (pow-fixed a b)))
+     891                 :          0 :             (max-error (+ u1 (mul-up raw MAX_POW_RELATIVE_ERROR)))
+     892                 :            :         )
+     893                 :          0 :         (if (< raw max-error)
+     894            [ - ]:          0 :             u0
+     895            [ - ]:          0 :             (- raw max-error)
+     896                 :            :         )
+     897                 :            :     )
+     898                 :            : )
+     899                 :            : (define-private (pow-up (a uint) (b uint))
+     900                 :          0 :     (let
+     901                 :            :         (
+     902                 :          0 :             (raw (unwrap-panic (pow-fixed a b)))
+     903                 :          0 :             (max-error (+ u1 (mul-up raw MAX_POW_RELATIVE_ERROR)))
+     904                 :            :         )
+     905                 :          0 :         (+ raw max-error)
+     906                 :            :     )
+     907                 :            : )
+     908                 :            : (define-constant UNSIGNED_ONE_8 (pow 10 8))
+     909                 :            : (define-constant MAX_NATURAL_EXPONENT (* 69 UNSIGNED_ONE_8))
+     910                 :            : (define-constant MIN_NATURAL_EXPONENT (* -18 UNSIGNED_ONE_8))
+     911                 :            : (define-constant MILD_EXPONENT_BOUND (/ (pow u2 u126) (to-uint UNSIGNED_ONE_8)))
+     912                 :            : (define-constant x_a_list_no_deci (list {x_pre: 6400000000, a_pre: 62351490808116168829, use_deci: false} ))
+     913                 :            : (define-constant x_a_list (list 
+     914                 :            : {x_pre: 3200000000, a_pre: 78962960182680695161, use_deci: true} ;; x2 = 2^5, a2 = e^(x2)
+     915                 :            : {x_pre: 1600000000, a_pre: 888611052050787, use_deci: true} ;; x3 = 2^4, a3 = e^(x3)
+     916                 :            : {x_pre: 800000000, a_pre: 298095798704, use_deci: true} ;; x4 = 2^3, a4 = e^(x4)
+     917                 :            : {x_pre: 400000000, a_pre: 5459815003, use_deci: true} ;; x5 = 2^2, a5 = e^(x5)
+     918                 :            : {x_pre: 200000000, a_pre: 738905610, use_deci: true} ;; x6 = 2^1, a6 = e^(x6)
+     919                 :            : {x_pre: 100000000, a_pre: 271828183, use_deci: true} ;; x7 = 2^0, a7 = e^(x7)
+     920                 :            : {x_pre: 50000000, a_pre: 164872127, use_deci: true} ;; x8 = 2^-1, a8 = e^(x8)
+     921                 :            : {x_pre: 25000000, a_pre: 128402542, use_deci: true} ;; x9 = 2^-2, a9 = e^(x9)
+     922                 :            : {x_pre: 12500000, a_pre: 113314845, use_deci: true} ;; x10 = 2^-3, a10 = e^(x10)
+     923                 :            : {x_pre: 6250000, a_pre: 106449446, use_deci: true} ;; x11 = 2^-4, a11 = e^x(11)
+     924                 :            : ))
+     925                 :            : (define-constant ERR-X-OUT-OF-BOUNDS (err u5009))
+     926                 :            : (define-constant ERR-Y-OUT-OF-BOUNDS (err u5010))
+     927                 :            : (define-constant ERR-PRODUCT-OUT-OF-BOUNDS (err u5011))
+     928                 :            : (define-constant ERR-INVALID-EXPONENT (err u5012))
+     929                 :            : (define-constant ERR-OUT-OF-BOUNDS (err u5013))
+     930                 :            : (define-private (ln-priv (a int))
+     931                 :          0 :   (let
+     932                 :            :     (
+     933                 :          0 :       (a_sum_no_deci (fold accumulate_division x_a_list_no_deci {a: a, sum: 0}))
+     934                 :          0 :       (a_sum (fold accumulate_division x_a_list {a: (get a a_sum_no_deci), sum: (get sum a_sum_no_deci)}))
+     935                 :          0 :       (out_a (get a a_sum))
+     936                 :          0 :       (out_sum (get sum a_sum))
+     937                 :          0 :       (z (/ (* (- out_a UNSIGNED_ONE_8) UNSIGNED_ONE_8) (+ out_a UNSIGNED_ONE_8)))
+     938                 :          0 :       (z_squared (/ (* z z) UNSIGNED_ONE_8))
+     939                 :          0 :       (div_list (list 3 5 7 9 11))
+     940                 :          0 :       (num_sum_zsq (fold rolling_sum_div div_list {num: z, seriesSum: z, z_squared: z_squared}))
+     941                 :          0 :       (seriesSum (get seriesSum num_sum_zsq))
+     942                 :            :     )
+     943                 :          0 :     (+ out_sum (* seriesSum 2))
+     944                 :            :   )
+     945                 :            : )
+     946                 :            : (define-private (accumulate_division (x_a_pre (tuple (x_pre int) (a_pre int) (use_deci bool))) (rolling_a_sum (tuple (a int) (sum int))))
+     947                 :          0 :   (let
+     948                 :            :     (
+     949                 :          0 :       (a_pre (get a_pre x_a_pre))
+     950                 :          0 :       (x_pre (get x_pre x_a_pre))
+     951                 :          0 :       (use_deci (get use_deci x_a_pre))
+     952                 :          0 :       (rolling_a (get a rolling_a_sum))
+     953                 :          0 :       (rolling_sum (get sum rolling_a_sum))
+     954                 :            :    )
+     955         [ -  - ]:          0 :     (if (>= rolling_a (if use_deci a_pre (* a_pre UNSIGNED_ONE_8)))
+     956         [ -  - ]:          0 :       {a: (/ (* rolling_a (if use_deci UNSIGNED_ONE_8 1)) a_pre), sum: (+ rolling_sum x_pre)}
+     957                 :          0 :       {a: rolling_a, sum: rolling_sum}
+     958                 :            :    )
+     959                 :            :  )
+     960                 :            : )
+     961                 :            : (define-private (rolling_sum_div (n int) (rolling (tuple (num int) (seriesSum int) (z_squared int))))
+     962                 :          0 :   (let
+     963                 :            :     (
+     964                 :          0 :       (rolling_num (get num rolling))
+     965                 :          0 :       (rolling_sum (get seriesSum rolling))
+     966                 :          0 :       (z_squared (get z_squared rolling))
+     967                 :          0 :       (next_num (/ (* rolling_num z_squared) UNSIGNED_ONE_8))
+     968                 :          0 :       (next_sum (+ rolling_sum (/ next_num n)))
+     969                 :            :    )
+     970                 :          0 :     {num: next_num, seriesSum: next_sum, z_squared: z_squared}
+     971                 :            :  )
+     972                 :            : )
+     973                 :            : (define-private (pow-priv (x uint) (y uint))
+     974                 :          0 :   (let
+     975                 :            :     (
+     976                 :          0 :       (x-int (to-int x))
+     977                 :          0 :       (y-int (to-int y))
+     978                 :          0 :       (lnx (ln-priv x-int))
+     979                 :          0 :       (logx-times-y (/ (* lnx y-int) UNSIGNED_ONE_8))
+     980                 :            :     )
+     981    [ - ][ -  - ]:          0 :     (asserts! (and (<= MIN_NATURAL_EXPONENT logx-times-y) (<= logx-times-y MAX_NATURAL_EXPONENT)) ERR-PRODUCT-OUT-OF-BOUNDS)
+     982                 :          0 :     (ok (to-uint (try! (exp-fixed logx-times-y))))
+     983                 :            :   )
+     984                 :            : )
+     985                 :            : (define-private (exp-pos (x int))
+     986                 :          0 :   (begin
+     987    [ - ][ -  - ]:          0 :     (asserts! (and (<= 0 x) (<= x MAX_NATURAL_EXPONENT)) ERR-INVALID-EXPONENT)
+     988                 :          0 :     (let
+     989                 :            :       (
+     990                 :          0 :         (x_product_no_deci (fold accumulate_product x_a_list_no_deci {x: x, product: 1}))
+     991                 :          0 :         (x_adj (get x x_product_no_deci))
+     992                 :          0 :         (firstAN (get product x_product_no_deci))
+     993                 :          0 :         (x_product (fold accumulate_product x_a_list {x: x_adj, product: UNSIGNED_ONE_8}))
+     994                 :          0 :         (product_out (get product x_product))
+     995                 :          0 :         (x_out (get x x_product))
+     996                 :          0 :         (seriesSum (+ UNSIGNED_ONE_8 x_out))
+     997                 :          0 :         (div_list (list 2 3 4 5 6 7 8 9 10 11 12))
+     998                 :          0 :         (term_sum_x (fold rolling_div_sum div_list {term: x_out, seriesSum: seriesSum, x: x_out}))
+     999                 :          0 :         (sum (get seriesSum term_sum_x))
+    1000                 :            :      )
+    1001                 :          0 :       (ok (* (/ (* product_out sum) UNSIGNED_ONE_8) firstAN))
+    1002                 :            :    )
+    1003                 :            :  )
+    1004                 :            : )
+    1005                 :            : (define-private (accumulate_product (x_a_pre (tuple (x_pre int) (a_pre int) (use_deci bool))) (rolling_x_p (tuple (x int) (product int))))
+    1006                 :          0 :   (let
+    1007                 :            :     (
+    1008                 :          0 :       (x_pre (get x_pre x_a_pre))
+    1009                 :          0 :       (a_pre (get a_pre x_a_pre))
+    1010                 :          0 :       (use_deci (get use_deci x_a_pre))
+    1011                 :          0 :       (rolling_x (get x rolling_x_p))
+    1012                 :          0 :       (rolling_product (get product rolling_x_p))
+    1013                 :            :    )
+    1014                 :          0 :     (if (>= rolling_x x_pre)
+    1015         [ -  - ]:          0 :       {x: (- rolling_x x_pre), product: (/ (* rolling_product a_pre) (if use_deci UNSIGNED_ONE_8 1))}
+    1016                 :          0 :       {x: rolling_x, product: rolling_product}
+    1017                 :            :    )
+    1018                 :            :  )
+    1019                 :            : )
+    1020                 :            : (define-private (rolling_div_sum (n int) (rolling (tuple (term int) (seriesSum int) (x int))))
+    1021                 :          0 :   (let
+    1022                 :            :     (
+    1023                 :          0 :       (rolling_term (get term rolling))
+    1024                 :          0 :       (rolling_sum (get seriesSum rolling))
+    1025                 :          0 :       (x (get x rolling))
+    1026                 :          0 :       (next_term (/ (/ (* rolling_term x) UNSIGNED_ONE_8) n))
+    1027                 :          0 :       (next_sum (+ rolling_sum next_term))
+    1028                 :            :    )
+    1029                 :          0 :     {term: next_term, seriesSum: next_sum, x: x}
+    1030                 :            :  )
+    1031                 :            : )
+    1032                 :            : (define-private (pow-fixed (x uint) (y uint))
+    1033                 :          0 :   (begin
+    1034            [ - ]:          0 :     (asserts! (< x (pow u2 u127)) ERR-X-OUT-OF-BOUNDS)
+    1035            [ - ]:          0 :     (asserts! (< y MILD_EXPONENT_BOUND) ERR-Y-OUT-OF-BOUNDS)
+    1036                 :          0 :     (if (is-eq y u0) 
+    1037            [ - ]:          0 :       (ok (to-uint UNSIGNED_ONE_8))
+    1038            [ - ]:          0 :       (if (is-eq x u0) 
+    1039            [ - ]:          0 :         (ok u0)
+    1040            [ - ]:          0 :         (pow-priv x y)
+    1041                 :            :       )
+    1042                 :            :     )
+    1043                 :            :   )
+    1044                 :            : )
+    1045                 :            : (define-private (exp-fixed (x int))
+    1046                 :          0 :   (begin
+    1047    [ - ][ -  - ]:          0 :     (asserts! (and (<= MIN_NATURAL_EXPONENT x) (<= x MAX_NATURAL_EXPONENT)) ERR-INVALID-EXPONENT)
+    1048                 :          0 :     (if (< x 0)
+    1049            [ - ]:          0 :       (ok (/ (* UNSIGNED_ONE_8 UNSIGNED_ONE_8) (try! (exp-pos (* -1 x)))))
+    1050            [ - ]:          0 :       (exp-pos x)
+    1051                 :            :     )
+    1052                 :            :   )
+    1053                 :            : )
+    1054                 :            : (define-private (log-fixed (arg int) (base int))
+    1055                 :          0 :   (let
+    1056                 :            :     (
+    1057                 :          0 :       (logBase (* (ln-priv base) UNSIGNED_ONE_8))
+    1058                 :          0 :       (logArg (* (ln-priv arg) UNSIGNED_ONE_8))
+    1059                 :            :    )
+    1060                 :          0 :     (ok (/ (* logArg UNSIGNED_ONE_8) logBase))
+    1061                 :            :  )
+    1062                 :            : )
+    1063                 :            : (define-private (ln-fixed (a int))
+    1064                 :          0 :   (begin
+    1065            [ - ]:          0 :     (asserts! (> a 0) ERR-OUT-OF-BOUNDS)
+    1066                 :          0 :     (if (< a UNSIGNED_ONE_8)
+    1067            [ - ]:          0 :       (ok (- 0 (ln-priv (/ (* UNSIGNED_ONE_8 UNSIGNED_ONE_8) a))))
+    1068            [ - ]:          0 :       (ok (ln-priv a))
+    1069                 :            :    )
+    1070                 :            :  )
+    1071                 :            : )
+
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/bridge-contract.clar.func-sort-c.html b/sbtc-ops/smart-contract/coverage/contracts/bridge-contract.clar.func-sort-c.html new file mode 100644 index 00000000..888b870b --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/bridge-contract.clar.func-sort-c.html @@ -0,0 +1,117 @@ + + + + + + + LCOV - coverage.lcov - contracts/bridge-contract.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - bridge-contract.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:507864.1 %
Date:2023-08-18 21:53:02Functions:5955.6 %
Branches:3837.5 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
div-down0
set-approved-token0
swap-preview0
to-one-80
swap-bridge-stx-btc1
get-approved-token16
swap-helper16
minus-percent17
mul-down17
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/bridge-contract.clar.func.html b/sbtc-ops/smart-contract/coverage/contracts/bridge-contract.clar.func.html new file mode 100644 index 00000000..b0ddc4a7 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/bridge-contract.clar.func.html @@ -0,0 +1,117 @@ + + + + + + + LCOV - coverage.lcov - contracts/bridge-contract.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - bridge-contract.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:507864.1 %
Date:2023-08-18 21:53:02Functions:5955.6 %
Branches:3837.5 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
div-down0
get-approved-token16
minus-percent17
mul-down17
set-approved-token0
swap-bridge-stx-btc1
swap-helper16
swap-preview0
to-one-80
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/bridge-contract.clar.gcov.html b/sbtc-ops/smart-contract/coverage/contracts/bridge-contract.clar.gcov.html new file mode 100644 index 00000000..c2f711a4 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/bridge-contract.clar.gcov.html @@ -0,0 +1,201 @@ + + + + + + + LCOV - coverage.lcov - contracts/bridge-contract.clar + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - bridge-contract.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:507864.1 %
Date:2023-08-18 21:53:02Functions:5955.6 %
Branches:3837.5 %
+
+ + + + + + + + +

+
           Branch data     Line data    Source code
+
+       1                 :            : ;; exhchange utilities
+       2                 :            : (use-trait ft-trait .trait-sip-010.sip-010-trait)
+       3                 :            : 
+       4                 :            : (define-map approved-tokens {token: principal} {approved: bool})
+       5                 :            : 
+       6                 :            : (define-constant err-forbidden (err u100))
+       7                 :            : (define-constant err-token-not-approved (err u999))
+       8                 :            : 
+       9                 :            : (define-constant contract-admin tx-sender)
+      10                 :            : (define-constant ONE_8 u100000000)
+      11                 :            : 
+      12                 :         41 : (map-set approved-tokens {token: .token-wbtc} {approved: true})
+      13                 :         41 : (map-set approved-tokens {token: .token-wstx} {approved: true})
+      14                 :            : 
+      15                 :            : (define-private (to-one-8 (a uint))
+      16                 :          0 :   (* a ONE_8))
+      17                 :            : 
+      18                 :            : (define-private (mul-down (a uint) (b uint))
+      19                 :         17 :   (/ (* a b) ONE_8))
+      20                 :            : 
+      21                 :            : (define-private (div-down (a uint) (b uint))
+      22                 :          0 :   (if (is-eq a u0)
+      23            [ - ]:          0 :     u0
+      24            [ - ]:          0 :     (/ (* a ONE_8) b)))
+      25                 :            : 
+      26                 :            : (define-private (minus-percent (a uint) (percent uint))
+      27                 :         17 :   (if (is-eq a u0)
+      28            [ - ]:          0 :     u0
+      29            [ + ]:         17 :     (/ (- (* a u100) (* a percent)) u100)))
+      30                 :            : 
+      31                 :            : ;; public to check return btc-to-stx amount
+      32                 :            : ;; input: 'SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.token-wbtc 'SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.token-wstx uN (xBTC or STX) * 10^8  u5 (5%)
+      33                 :            : (define-public (swap-helper (token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (multiplied-amount uint) (slippeage uint)) 
+      34                 :         16 :   (let (
+      35                 :         16 :     (token-x (contract-of token-x-trait))
+      36                 :         16 :     (token-y (contract-of token-y-trait))
+      37                 :          0 :     (fee-amount 
+      38                 :         16 :       (contract-call? .amm-swap-pool-v1-1 fee-helper token-x token-y ONE_8))
+      39                 :         16 :     (get-helper-result (try! (contract-call? .amm-swap-pool-v1-1 get-helper token-x token-y ONE_8 multiplied-amount)))
+      40                 :          0 :     (converted-amount 
+      41                 :         16 :       (mul-down 
+      42                 :         16 :         get-helper-result 
+      43                 :         16 :         (- ONE_8 (unwrap-panic fee-amount))))
+      44                 :         16 :     (converted-amount-slippeage (minus-percent converted-amount slippeage)))
+      45                 :         16 :     (asserts! 
+      46                 :         16 :     (and 
+      47            [ + ]:         16 :       (get-approved-token token-x)
+      48            [ + ]:         16 :       (get-approved-token token-y))
+      49            [ - ]:          0 :     err-token-not-approved)
+      50                 :         16 :     (ok (contract-call? 
+      51                 :          0 :         .amm-swap-pool-v1-1 swap-helper
+      52                 :         16 :           token-x-trait
+      53                 :         16 :           token-y-trait
+      54                 :         16 :           ONE_8
+      55                 :         16 :           multiplied-amount
+      56                 :         16 :         (some converted-amount-slippeage)))))
+      57                 :            : 
+      58                 :            : (define-public (swap-bridge-stx-btc 
+      59                 :            :                   (token-x-trait <ft-trait>) 
+      60                 :            :                   (token-y-trait <ft-trait>) 
+      61                 :            :                   (multiplied-amount uint) 
+      62                 :            :                   (slippeage uint) 
+      63                 :            :                   (btc-version (buff 1)) 
+      64                 :            :                   (btc-hash (buff 20)) 
+      65                 :            :                   (supplier-id uint)) 
+      66                 :          1 :   (let (
+      67                 :          1 :     (token-x (contract-of token-x-trait))
+      68                 :          1 :     (token-y (contract-of token-y-trait))
+      69                 :          0 :     (fee-amount 
+      70                 :          1 :       (contract-call? .amm-swap-pool-v1-1 fee-helper token-x token-y ONE_8))
+      71                 :          1 :     (get-helper-result (try! (contract-call? .amm-swap-pool-v1-1 get-helper token-x token-y ONE_8 multiplied-amount)))
+      72                 :          0 :     (xbtc-amount 
+      73                 :          1 :       (mul-down 
+      74                 :          1 :         get-helper-result 
+      75                 :          1 :         (- ONE_8 (unwrap-panic fee-amount))))
+      76                 :          1 :     (xbtc-amount-slippeage (minus-percent xbtc-amount slippeage))
+      77                 :          0 :     (swap-result 
+      78                 :          1 :       (try! (contract-call? 
+      79                 :          0 :         .amm-swap-pool-v1-1 swap-helper
+      80                 :          1 :           token-x-trait
+      81                 :          1 :           token-y-trait
+      82                 :          1 :           ONE_8
+      83                 :          1 :           multiplied-amount
+      84                 :          1 :         (some xbtc-amount-slippeage)))))
+      85                 :          1 :     (try! 
+      86                 :          1 :       (contract-call? .degen-bridge-testnet-v3 initiate-outbound-swap 
+      87                 :          1 :                           swap-result 
+      88                 :          1 :                           btc-version 
+      89                 :          1 :                           btc-hash 
+      90                 :          1 :                           supplier-id))
+      91                 :          1 :     (ok swap-result)))
+      92                 :            : 
+      93                 :            : (define-public (swap-preview (token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (multiplied-amount uint) (slippeage uint)) 
+      94                 :          0 :   (let (
+      95                 :          0 :     (token-x (contract-of token-x-trait))
+      96                 :          0 :     (token-y (contract-of token-y-trait))
+      97                 :          0 :     (fee-amount 
+      98                 :          0 :       (contract-call? .amm-swap-pool-v1-1 fee-helper token-x token-y ONE_8))
+      99                 :          0 :     (get-helper-result (try! (contract-call? .amm-swap-pool-v1-1 get-helper token-x token-y ONE_8 multiplied-amount)))
+     100                 :          0 :     (converted-amount 
+     101                 :          0 :       (mul-down 
+     102                 :          0 :         get-helper-result 
+     103                 :          0 :         (- ONE_8 (unwrap-panic fee-amount))))
+     104                 :          0 :     (converted-amount-slippeage (minus-percent converted-amount slippeage)))
+     105                 :          0 :       (ok converted-amount)))
+     106                 :            : 
+     107                 :            : (define-public (set-approved-token (token principal))
+     108                 :          0 : (begin 
+     109            [ - ]:          0 :   (asserts! (is-eq contract-caller contract-admin) err-forbidden)
+     110                 :          0 :   (ok (map-set approved-tokens {token: token} {approved: true}))))
+     111                 :            : 
+     112                 :            : (define-read-only (get-approved-token (token principal))
+     113                 :         32 : (begin
+     114                 :         32 :   (default-to false 
+     115                 :         32 :     (get approved 
+     116                 :         32 :       (map-get? approved-tokens {token: token})))))
+
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/clarity-bitcoin.clar.func-sort-c.html b/sbtc-ops/smart-contract/coverage/contracts/clarity-bitcoin.clar.func-sort-c.html new file mode 100644 index 00000000..7aee6ff8 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/clarity-bitcoin.clar.func-sort-c.html @@ -0,0 +1,237 @@ + + + + + + + LCOV - coverage.lcov - contracts/clarity-bitcoin.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - clarity-bitcoin.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:03130.0 %
Date:2023-08-18 21:53:02Functions:0390.0 %
Branches:0540.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
buff-to-u80
get-reversed-txid0
get-txid0
inner-buff32-permutation0
inner-merkle-proof-verify0
inner-read-slice0
inner-read-slice-10240
is-bit-set0
parse-block-header0
parse-tx0
read-hashslice0
read-next-txin0
read-next-txout0
read-slice0
read-slice-10
read-slice-1280
read-slice-160
read-slice-20
read-slice-2560
read-slice-320
read-slice-40
read-slice-5120
read-slice-640
read-slice-80
read-txins0
read-txouts0
read-uint160
read-uint320
read-uint640
read-varint0
read-varslice0
reverse-buff320
verify-block-header0
verify-merkle-proof0
verify-prev-block0
verify-prev-blocks0
verify-prev-blocks-fold0
was-tx-mined-prev?0
was-tx-mined?0
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/clarity-bitcoin.clar.func.html b/sbtc-ops/smart-contract/coverage/contracts/clarity-bitcoin.clar.func.html new file mode 100644 index 00000000..55f8ca01 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/clarity-bitcoin.clar.func.html @@ -0,0 +1,237 @@ + + + + + + + LCOV - coverage.lcov - contracts/clarity-bitcoin.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - clarity-bitcoin.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:03130.0 %
Date:2023-08-18 21:53:02Functions:0390.0 %
Branches:0540.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
buff-to-u80
get-reversed-txid0
get-txid0
inner-buff32-permutation0
inner-merkle-proof-verify0
inner-read-slice0
inner-read-slice-10240
is-bit-set0
parse-block-header0
parse-tx0
read-hashslice0
read-next-txin0
read-next-txout0
read-slice0
read-slice-10
read-slice-1280
read-slice-160
read-slice-20
read-slice-2560
read-slice-320
read-slice-40
read-slice-5120
read-slice-640
read-slice-80
read-txins0
read-txouts0
read-uint160
read-uint320
read-uint640
read-varint0
read-varslice0
reverse-buff320
verify-block-header0
verify-merkle-proof0
verify-prev-block0
verify-prev-blocks0
verify-prev-blocks-fold0
was-tx-mined-prev?0
was-tx-mined?0
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/clarity-bitcoin.clar.gcov.html b/sbtc-ops/smart-contract/coverage/contracts/clarity-bitcoin.clar.gcov.html new file mode 100644 index 00000000..ec9d53b8 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/clarity-bitcoin.clar.gcov.html @@ -0,0 +1,896 @@ + + + + + + + LCOV - coverage.lcov - contracts/clarity-bitcoin.clar + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - clarity-bitcoin.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:03130.0 %
Date:2023-08-18 21:53:02Functions:0390.0 %
Branches:0540.0 %
+
+ + + + + + + + +

+
           Branch data     Line data    Source code
+
+       1                 :            : ;; Error codes
+       2                 :            : (define-constant ERR-OUT-OF-BOUNDS u1)
+       3                 :            : (define-constant ERR-TOO-MANY-TXINS u2)
+       4                 :            : (define-constant ERR-TOO-MANY-TXOUTS u3)
+       5                 :            : (define-constant ERR-VARSLICE-TOO-LONG u4)
+       6                 :            : (define-constant ERR-BAD-HEADER u5)
+       7                 :            : (define-constant ERR-PROOF-TOO-SHORT u6)
+       8                 :            : (define-constant ERR-INVALID-PARENT u7)
+       9                 :            : 
+      10                 :            : ;; lookup table for converting 1-byte buffers to uints via index-of
+      11                 :            : (define-constant BUFF_TO_BYTE (list 
+      12                 :            :    0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f
+      13                 :            :    0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e 0x1f
+      14                 :            :    0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x2a 0x2b 0x2c 0x2d 0x2e 0x2f
+      15                 :            :    0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x3a 0x3b 0x3c 0x3d 0x3e 0x3f
+      16                 :            :    0x40 0x41 0x42 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4a 0x4b 0x4c 0x4d 0x4e 0x4f
+      17                 :            :    0x50 0x51 0x52 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5a 0x5b 0x5c 0x5d 0x5e 0x5f
+      18                 :            :    0x60 0x61 0x62 0x63 0x64 0x65 0x66 0x67 0x68 0x69 0x6a 0x6b 0x6c 0x6d 0x6e 0x6f
+      19                 :            :    0x70 0x71 0x72 0x73 0x74 0x75 0x76 0x77 0x78 0x79 0x7a 0x7b 0x7c 0x7d 0x7e 0x7f
+      20                 :            :    0x80 0x81 0x82 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8a 0x8b 0x8c 0x8d 0x8e 0x8f
+      21                 :            :    0x90 0x91 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9a 0x9b 0x9c 0x9d 0x9e 0x9f
+      22                 :            :    0xa0 0xa1 0xa2 0xa3 0xa4 0xa5 0xa6 0xa7 0xa8 0xa9 0xaa 0xab 0xac 0xad 0xae 0xaf
+      23                 :            :    0xb0 0xb1 0xb2 0xb3 0xb4 0xb5 0xb6 0xb7 0xb8 0xb9 0xba 0xbb 0xbc 0xbd 0xbe 0xbf
+      24                 :            :    0xc0 0xc1 0xc2 0xc3 0xc4 0xc5 0xc6 0xc7 0xc8 0xc9 0xca 0xcb 0xcc 0xcd 0xce 0xcf
+      25                 :            :    0xd0 0xd1 0xd2 0xd3 0xd4 0xd5 0xd6 0xd7 0xd8 0xd9 0xda 0xdb 0xdc 0xdd 0xde 0xdf
+      26                 :            :    0xe0 0xe1 0xe2 0xe3 0xe4 0xe5 0xe6 0xe7 0xe8 0xe9 0xea 0xeb 0xec 0xed 0xee 0xef
+      27                 :            :    0xf0 0xf1 0xf2 0xf3 0xf4 0xf5 0xf6 0xf7 0xf8 0xf9 0xfa 0xfb 0xfc 0xfd 0xfe 0xff
+      28                 :            : ))
+      29                 :            : 
+      30                 :            : ;; List with 512 items, used for folding something 512 times
+      31                 :            : (define-constant LIST_512 (list
+      32                 :            :    true true true true true true true true true true true true true true true true
+      33                 :            :    true true true true true true true true true true true true true true true true
+      34                 :            :    true true true true true true true true true true true true true true true true
+      35                 :            :    true true true true true true true true true true true true true true true true
+      36                 :            :    true true true true true true true true true true true true true true true true
+      37                 :            :    true true true true true true true true true true true true true true true true
+      38                 :            :    true true true true true true true true true true true true true true true true
+      39                 :            :    true true true true true true true true true true true true true true true true
+      40                 :            :    true true true true true true true true true true true true true true true true
+      41                 :            :    true true true true true true true true true true true true true true true true
+      42                 :            :    true true true true true true true true true true true true true true true true
+      43                 :            :    true true true true true true true true true true true true true true true true
+      44                 :            :    true true true true true true true true true true true true true true true true
+      45                 :            :    true true true true true true true true true true true true true true true true
+      46                 :            :    true true true true true true true true true true true true true true true true
+      47                 :            :    true true true true true true true true true true true true true true true true
+      48                 :            :    true true true true true true true true true true true true true true true true
+      49                 :            :    true true true true true true true true true true true true true true true true
+      50                 :            :    true true true true true true true true true true true true true true true true
+      51                 :            :    true true true true true true true true true true true true true true true true
+      52                 :            :    true true true true true true true true true true true true true true true true
+      53                 :            :    true true true true true true true true true true true true true true true true
+      54                 :            :    true true true true true true true true true true true true true true true true
+      55                 :            :    true true true true true true true true true true true true true true true true
+      56                 :            :    true true true true true true true true true true true true true true true true
+      57                 :            :    true true true true true true true true true true true true true true true true
+      58                 :            :    true true true true true true true true true true true true true true true true
+      59                 :            :    true true true true true true true true true true true true true true true true
+      60                 :            :    true true true true true true true true true true true true true true true true
+      61                 :            :    true true true true true true true true true true true true true true true true
+      62                 :            :    true true true true true true true true true true true true true true true true
+      63                 :            :    true true true true true true true true true true true true true true true true
+      64                 :            : ))
+      65                 :            : 
+      66                 :            : ;; List with 256 items, used for folding something 256 times
+      67                 :            : (define-constant LIST_256 (list
+      68                 :            :    true true true true true true true true true true true true true true true true
+      69                 :            :    true true true true true true true true true true true true true true true true
+      70                 :            :    true true true true true true true true true true true true true true true true
+      71                 :            :    true true true true true true true true true true true true true true true true
+      72                 :            :    true true true true true true true true true true true true true true true true
+      73                 :            :    true true true true true true true true true true true true true true true true
+      74                 :            :    true true true true true true true true true true true true true true true true
+      75                 :            :    true true true true true true true true true true true true true true true true
+      76                 :            :    true true true true true true true true true true true true true true true true
+      77                 :            :    true true true true true true true true true true true true true true true true
+      78                 :            :    true true true true true true true true true true true true true true true true
+      79                 :            :    true true true true true true true true true true true true true true true true
+      80                 :            :    true true true true true true true true true true true true true true true true
+      81                 :            :    true true true true true true true true true true true true true true true true
+      82                 :            :    true true true true true true true true true true true true true true true true
+      83                 :            :    true true true true true true true true true true true true true true true true
+      84                 :            : ))
+      85                 :            : 
+      86                 :            : ;; List with 128 items, used for folding something 128 times
+      87                 :            : (define-constant LIST_128 (list
+      88                 :            :    true true true true true true true true true true true true true true true true
+      89                 :            :    true true true true true true true true true true true true true true true true
+      90                 :            :    true true true true true true true true true true true true true true true true
+      91                 :            :    true true true true true true true true true true true true true true true true
+      92                 :            :    true true true true true true true true true true true true true true true true
+      93                 :            :    true true true true true true true true true true true true true true true true
+      94                 :            :    true true true true true true true true true true true true true true true true
+      95                 :            :    true true true true true true true true true true true true true true true true
+      96                 :            : ))
+      97                 :            : 
+      98                 :            : ;; List with 64 items, used for folding something 64 times
+      99                 :            : (define-constant LIST_64 (list
+     100                 :            :    true true true true true true true true true true true true true true true true
+     101                 :            :    true true true true true true true true true true true true true true true true
+     102                 :            :    true true true true true true true true true true true true true true true true
+     103                 :            :    true true true true true true true true true true true true true true true true
+     104                 :            : ))
+     105                 :            : 
+     106                 :            : ;; List with 32 items, used for folding something 32 times
+     107                 :            : (define-constant LIST_32 (list
+     108                 :            :    true true true true true true true true true true true true true true true true
+     109                 :            :    true true true true true true true true true true true true true true true true
+     110                 :            : ))
+     111                 :            : 
+     112                 :            : ;; List with 16 items, used for folding something 16 times
+     113                 :            : (define-constant LIST_16 (list
+     114                 :            :    true true true true true true true true true true true true true true true true
+     115                 :            : ))
+     116                 :            : 
+     117                 :            : ;; Convert a 1-byte buff into a uint.
+     118                 :            : (define-read-only (buff-to-u8 (byte (buff 1)))
+     119                 :          0 :     (unwrap-panic (index-of BUFF_TO_BYTE byte)))
+     120                 :            : 
+     121                 :            : ;; Append a byte at the given index in the given data to acc.
+     122                 :            : (define-read-only (inner-read-slice-1024 (ignored bool) (input { acc: (buff 1024), data: (buff 1024), index: uint }))
+     123                 :          0 :     (let (
+     124                 :          0 :         (acc (get acc input))
+     125                 :          0 :         (data (get data input))
+     126                 :          0 :         (ctr (get index input))
+     127                 :          0 :         (byte (unwrap-panic (element-at data ctr)))
+     128                 :            :     )
+     129                 :            :     {
+     130                 :          0 :         acc: (unwrap-panic (as-max-len? (concat acc byte) u1024)),
+     131                 :          0 :         data: data,
+     132                 :          0 :         index: (+ u1 ctr)
+     133                 :            :     })
+     134                 :            : )
+     135                 :            : 
+     136                 :            : ;; Read 512 bytes from data, starting at index.  Return the 512-byte slice.
+     137                 :            : (define-read-only (read-slice-512 (input { data: (buff 1024), index: uint }))
+     138                 :          0 :     (get acc
+     139                 :          0 :         (fold inner-read-slice-1024 LIST_512 { acc: 0x, data: (get data input), index: (get index input) })))
+     140                 :            : 
+     141                 :            : ;; Read 256 bytes from data, starting at index.  Return the 256-byte slice.
+     142                 :            : (define-read-only (read-slice-256 (input { data: (buff 1024), index: uint }))
+     143                 :          0 :     (get acc
+     144                 :          0 :         (fold inner-read-slice-1024 LIST_256 { acc: 0x, data: (get data input), index: (get index input) })))
+     145                 :            : 
+     146                 :            : ;; Read 128 bytes from data, starting at index.  Return the 128-byte slice.
+     147                 :            : (define-read-only (read-slice-128 (input { data: (buff 1024), index: uint }))
+     148                 :          0 :     (get acc
+     149                 :          0 :         (fold inner-read-slice-1024 LIST_128 { acc: 0x, data: (get data input), index: (get index input) })))
+     150                 :            : 
+     151                 :            : ;; Read 64 bytes from data, starting at index.  Return the 64-byte slice.
+     152                 :            : (define-read-only (read-slice-64 (input { data: (buff 1024), index: uint }))
+     153                 :          0 :     (get acc
+     154                 :          0 :         (fold inner-read-slice-1024 LIST_64 { acc: 0x, data: (get data input), index: (get index input) })))
+     155                 :            : 
+     156                 :            : ;; Read 32 bytes from data, starting at index.  Return the 32-byte slice.
+     157                 :            : (define-read-only (read-slice-32 (input { data: (buff 1024), index: uint }))
+     158                 :          0 :     (get acc
+     159                 :          0 :         (fold inner-read-slice-1024 LIST_32 { acc: 0x, data: (get data input), index: (get index input) })))
+     160                 :            : 
+     161                 :            : ;; Read 16 bytes from data, starting at index.  Return the 16-byte slice.
+     162                 :            : (define-read-only (read-slice-16 (input { data: (buff 1024), index: uint }))
+     163                 :          0 :     (get acc
+     164                 :          0 :         (fold inner-read-slice-1024 LIST_16 { acc: 0x, data: (get data input), index: (get index input) })))
+     165                 :            : 
+     166                 :            : ;; Read 8 bytes from data, starting at index.  Return the 8-byte slice.
+     167                 :            : (define-read-only (read-slice-8 (input { data: (buff 1024), index: uint }))
+     168                 :          0 :     (get acc
+     169                 :          0 :         (fold inner-read-slice-1024 (list true true true true true true true true) { acc: 0x, data: (get data input), index: (get index input) })))
+     170                 :            : 
+     171                 :            : ;; Read 4 bytes from data, starting at index.  Return the 4-byte slice.
+     172                 :            : (define-read-only (read-slice-4 (input { data: (buff 1024), index: uint }))
+     173                 :          0 :     (get acc
+     174                 :          0 :         (fold inner-read-slice-1024 (list true true true true) { acc: 0x, data: (get data input), index: (get index input) })))
+     175                 :            : 
+     176                 :            : ;; Read 2 bytes from data, starting at index.  Return the 2-byte slice.
+     177                 :            : (define-read-only (read-slice-2 (input { data: (buff 1024), index: uint }))
+     178                 :          0 :     (get acc
+     179                 :          0 :         (fold inner-read-slice-1024 (list true true) { acc: 0x, data: (get data input), index: (get index input) })))
+     180                 :            : 
+     181                 :            : ;; Read 1 byte from data, starting at index.  Return the 1-byte slice.
+     182                 :            : (define-read-only (read-slice-1 (input { data: (buff 1024), index: uint }))
+     183                 :          0 :     (get acc
+     184                 :          0 :         (fold inner-read-slice-1024 (list true) { acc: 0x, data: (get data input), index: (get index input) })))
+     185                 :            : 
+     186                 :            : ;; Read a fixed-sized chunk of data from a given buffer (up to remaining bytes), starting at index, and append it to acc.
+     187                 :            : ;; chunk_size must be a power of 2, up to 1024
+     188                 :            : (define-read-only (inner-read-slice (chunk_size uint) (input { acc: (buff 1024), buffer: (buff 1024), index: uint, remaining: uint }))
+     189                 :          0 :     (let (
+     190                 :          0 :         (ctr (get index input))
+     191                 :          0 :         (remaining (get remaining input))
+     192                 :            :     )
+     193                 :          0 :     (if (is-eq u0 remaining)
+     194                 :            :         ;; done reading
+     195            [ - ]:          0 :         input
+     196            [ - ]:          0 :         (let (
+     197                 :          0 :             (acc (get acc input))
+     198                 :          0 :             (databuff (get buffer input))
+     199                 :            :         )
+     200                 :          0 :         (if (> chunk_size remaining)
+     201                 :            :             ;; chunk size too big for remainder, so just skip it.
+     202            [ - ]:          0 :             input
+     203                 :            :             ;; we have at least chunk_size bytes to read!
+     204                 :            :             ;; dispatch to the right fixed-size slice reader.
+     205            [ - ]:          0 :             (if (is-eq chunk_size u512)
+     206                 :          0 :                 { acc: (unwrap-panic (as-max-len? (concat acc (read-slice-512 { data: databuff, index: ctr })) u1024)), buffer: databuff, index: (+ chunk_size ctr), remaining: (- remaining chunk_size) }
+     207            [ - ]:          0 :             (if (is-eq chunk_size u256)
+     208                 :          0 :                 { acc: (unwrap-panic (as-max-len? (concat acc (read-slice-256 { data: databuff, index: ctr })) u1024)), buffer: databuff, index: (+ chunk_size ctr), remaining: (- remaining chunk_size) }
+     209            [ - ]:          0 :             (if (is-eq chunk_size u128)
+     210                 :          0 :                 { acc: (unwrap-panic (as-max-len? (concat acc (read-slice-128 { data: databuff, index: ctr })) u1024)), buffer: databuff, index: (+ chunk_size ctr), remaining: (- remaining chunk_size) }
+     211            [ - ]:          0 :             (if (is-eq chunk_size u64)
+     212                 :          0 :                 { acc: (unwrap-panic (as-max-len? (concat acc (read-slice-64 { data: databuff, index: ctr })) u1024)), buffer: databuff, index: (+ chunk_size ctr), remaining: (- remaining chunk_size) }
+     213            [ - ]:          0 :             (if (is-eq chunk_size u32)
+     214                 :          0 :                 { acc: (unwrap-panic (as-max-len? (concat acc (read-slice-32 { data: databuff, index: ctr })) u1024)), buffer: databuff, index: (+ chunk_size ctr), remaining: (- remaining chunk_size) }
+     215            [ - ]:          0 :             (if (is-eq chunk_size u16)
+     216                 :          0 :                 { acc: (unwrap-panic (as-max-len? (concat acc (read-slice-16 { data: databuff, index: ctr })) u1024)), buffer: databuff, index: (+ chunk_size ctr), remaining: (- remaining chunk_size) }
+     217            [ - ]:          0 :             (if (is-eq chunk_size u8)
+     218                 :          0 :                 { acc: (unwrap-panic (as-max-len? (concat acc (read-slice-8 { data: databuff, index: ctr })) u1024)), buffer: databuff, index: (+ chunk_size ctr), remaining: (- remaining chunk_size) }
+     219            [ - ]:          0 :             (if (is-eq chunk_size u4)
+     220                 :          0 :                 { acc: (unwrap-panic (as-max-len? (concat acc (read-slice-4 { data: databuff, index: ctr })) u1024)), buffer: databuff, index: (+ chunk_size ctr), remaining: (- remaining chunk_size) }
+     221            [ - ]:          0 :             (if (is-eq chunk_size u2)
+     222                 :          0 :                 { acc: (unwrap-panic (as-max-len? (concat acc (read-slice-2 { data: databuff, index: ctr })) u1024)), buffer: databuff, index: (+ chunk_size ctr), remaining: (- remaining chunk_size) }
+     223            [ - ]:          0 :             (if (is-eq chunk_size u1)
+     224                 :          0 :                 { acc: (unwrap-panic (as-max-len? (concat acc (read-slice-1 { data: databuff, index: ctr })) u1024)), buffer: databuff, index: (+ chunk_size ctr), remaining: (- remaining chunk_size) }
+     225                 :          0 :                 { acc: acc, buffer: databuff, index: ctr, remaining: remaining }
+     226                 :            :             ))))))))))
+     227                 :            :         ))
+     228                 :            :     ))
+     229                 :            : )
+     230                 :            : 
+     231                 :            : ;; Top-level function to read a slice of a given size from a given (buff 1024), starting at a given offset.
+     232                 :            : ;; Returns (ok (buff 1024)) on success, and it contains "buff[offset..(offset+size)]"
+     233                 :            : ;; Returns (err ERR-OUT-OF-BOUNDS) if the slice offset and/or size would copy a range of bytes outside the given buffer.
+     234                 :            : (define-read-only (read-slice (data (buff 1024)) (offset uint) (size uint))
+     235         [ -  - ]:          0 :     (if (or (>= offset (len data)) (> (+ offset size) (len data)))
+     236            [ - ]:          0 :         (err ERR-OUT-OF-BOUNDS)
+     237            [ - ]:          0 :         (begin
+     238                 :            :         ;;    (print "read slice")
+     239                 :            :         ;;    (print size)
+     240                 :          0 :            (ok
+     241                 :          0 :              (get acc
+     242                 :          0 :                  (fold inner-read-slice (list u512 u256 u128 u64 u32 u16 u8 u4 u2 u1) { acc: 0x, buffer: data, index: offset, remaining: size }))
+     243                 :            :            )
+     244                 :            :         )
+     245                 :            :     )
+     246                 :            : )
+     247                 :            : 
+     248                 :            : ;; Reads the next two bytes from txbuff as a big-endian 16-bit integer, and updates the index.
+     249                 :            : ;; Returns (ok { uint16: uint, ctx: { txbuff: (buff 1024), index: uint } }) on success.
+     250                 :            : ;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff
+     251                 :            : (define-read-only (read-uint16 (ctx { txbuff: (buff 1024), index: uint }))
+     252                 :          0 :     (let (
+     253                 :          0 :         (data (get txbuff ctx))
+     254                 :          0 :         (base (get index ctx))
+     255                 :          0 :         (byte-1 (buff-to-u8 (unwrap! (element-at data base) (err ERR-OUT-OF-BOUNDS))))
+     256                 :          0 :         (byte-2 (buff-to-u8 (unwrap! (element-at data (+ u1 base)) (err ERR-OUT-OF-BOUNDS))))
+     257                 :          0 :         (ret (+ (* byte-2 u256) byte-1))
+     258                 :            :     )
+     259                 :          0 :     (begin
+     260                 :            :     ;;    (print "read uint16")
+     261                 :            :     ;;    (print ret)
+     262                 :          0 :        (ok {
+     263                 :          0 :            uint16: ret,
+     264                 :          0 :            ctx: { txbuff: data, index: (+ u2 base) }
+     265                 :            :        })
+     266                 :            :     ))
+     267                 :            : )
+     268                 :            : 
+     269                 :            : ;; Reads the next four bytes from txbuff as a big-endian 32-bit integer, and updates the index.
+     270                 :            : ;; Returns (ok { uint32: uint, ctx: { txbuff: (buff 1024), index: uint } }) on success.
+     271                 :            : ;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff
+     272                 :            : (define-read-only (read-uint32 (ctx { txbuff: (buff 1024), index: uint }))
+     273                 :          0 :     (let (
+     274                 :          0 :         (data (get txbuff ctx))
+     275                 :          0 :         (base (get index ctx))
+     276                 :          0 :         (byte-1 (buff-to-u8 (unwrap! (element-at data base) (err ERR-OUT-OF-BOUNDS))))
+     277                 :          0 :         (byte-2 (buff-to-u8 (unwrap! (element-at data (+ u1 base)) (err ERR-OUT-OF-BOUNDS))))
+     278                 :          0 :         (byte-3 (buff-to-u8 (unwrap! (element-at data (+ u2 base)) (err ERR-OUT-OF-BOUNDS))))
+     279                 :          0 :         (byte-4 (buff-to-u8 (unwrap! (element-at data (+ u3 base)) (err ERR-OUT-OF-BOUNDS))))
+     280                 :          0 :         (ret (+ (* byte-4 u16777216) (* byte-3 u65536) (* byte-2 u256) byte-1))
+     281                 :            :     )
+     282                 :          0 :     (begin
+     283                 :            :     ;;    (print "read uint32")
+     284                 :            :     ;;    (print ret)
+     285                 :          0 :        (ok {
+     286                 :          0 :            uint32: ret,
+     287                 :          0 :            ctx: { txbuff: data, index: (+ u4 base) }
+     288                 :            :        })
+     289                 :            :     ))
+     290                 :            : )
+     291                 :            : 
+     292                 :            : ;; Reads the next eight bytes from txbuff as a big-endian 64-bit integer, and updates the index.
+     293                 :            : ;; Returns (ok { uint64: uint, ctx: { txbuff: (buff 1024), index: uint } }) on success.
+     294                 :            : ;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff
+     295                 :            : (define-read-only (read-uint64 (ctx { txbuff: (buff 1024), index: uint }))
+     296                 :          0 :     (let (
+     297                 :          0 :         (data (get txbuff ctx))
+     298                 :          0 :         (base (get index ctx))
+     299                 :          0 :         (byte-1 (buff-to-u8 (unwrap! (element-at data base) (err ERR-OUT-OF-BOUNDS))))
+     300                 :          0 :         (byte-2 (buff-to-u8 (unwrap! (element-at data (+ u1 base)) (err ERR-OUT-OF-BOUNDS))))
+     301                 :          0 :         (byte-3 (buff-to-u8 (unwrap! (element-at data (+ u2 base)) (err ERR-OUT-OF-BOUNDS))))
+     302                 :          0 :         (byte-4 (buff-to-u8 (unwrap! (element-at data (+ u3 base)) (err ERR-OUT-OF-BOUNDS))))
+     303                 :          0 :         (byte-5 (buff-to-u8 (unwrap! (element-at data (+ u4 base)) (err ERR-OUT-OF-BOUNDS))))
+     304                 :          0 :         (byte-6 (buff-to-u8 (unwrap! (element-at data (+ u5 base)) (err ERR-OUT-OF-BOUNDS))))
+     305                 :          0 :         (byte-7 (buff-to-u8 (unwrap! (element-at data (+ u6 base)) (err ERR-OUT-OF-BOUNDS))))
+     306                 :          0 :         (byte-8 (buff-to-u8 (unwrap! (element-at data (+ u7 base)) (err ERR-OUT-OF-BOUNDS))))
+     307                 :          0 :         (ret (+ 
+     308                 :          0 :            (* byte-8 u72057594037927936)
+     309                 :          0 :            (* byte-7 u281474976710656)
+     310                 :          0 :            (* byte-6 u1099511627776)
+     311                 :          0 :            (* byte-5 u4294967296)
+     312                 :          0 :            (* byte-4 u16777216)
+     313                 :          0 :            (* byte-3 u65536)
+     314                 :          0 :            (* byte-2 u256)
+     315                 :          0 :            byte-1))
+     316                 :            :     )
+     317                 :          0 :     (begin
+     318                 :            :     ;;    (print "read uint64")
+     319                 :            :     ;;    (print ret)
+     320                 :          0 :        (ok {
+     321                 :          0 :            uint64: ret,
+     322                 :          0 :            ctx: { txbuff: data, index: (+ u8 base) }
+     323                 :            :        })
+     324                 :            :     ))
+     325                 :            : )
+     326                 :            : 
+     327                 :            : ;; Reads the next varint from txbuff, and updates the index.
+     328                 :            : ;; Returns (ok { varint: uint, ctx: { txbuff: (buff 1024), index: uint } }) on success
+     329                 :            : ;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff.
+     330                 :            : (define-read-only (read-varint (ctx { txbuff: (buff 1024), index: uint }))
+     331                 :          0 :     (let (
+     332                 :          0 :         (ptr (get index ctx))
+     333                 :          0 :         (tx (get txbuff ctx))
+     334                 :          0 :         (byte (buff-to-u8 (unwrap! (element-at tx ptr)
+     335                 :          0 :                             (err ERR-OUT-OF-BOUNDS))))
+     336                 :            :     )
+     337                 :          0 :     (if (<= byte u252)
+     338            [ - ]:          0 :         (begin
+     339                 :            :         ;;    (print "varint 1")
+     340                 :            :         ;;    (print byte)
+     341                 :            :            ;; given byte is the varint
+     342                 :          0 :            (ok { varint: byte, ctx: { txbuff: tx, index: (+ u1 ptr) }})
+     343                 :            :         )
+     344            [ - ]:          0 :         (if (is-eq byte u253)
+     345            [ - ]:          0 :             (let (
+     346                 :            :                 ;; next two bytes is the varint
+     347                 :          0 :                 (parsed-u16 (try! (read-uint16 { txbuff: tx, index: (+ u1 ptr) })))
+     348                 :            :             )
+     349                 :          0 :             (begin
+     350                 :            :                 ;; (print "varint 2")
+     351                 :            :                 ;; (print (get uint16 parsed-u16))
+     352                 :          0 :                 (ok { varint: (get uint16 parsed-u16), ctx: (get ctx parsed-u16) })
+     353                 :            :             ))
+     354            [ - ]:          0 :             (if (is-eq byte u254)
+     355            [ - ]:          0 :                 (let (
+     356                 :            :                     ;; next four bytes is the varint
+     357                 :          0 :                     (parsed-u32 (try! (read-uint32 { txbuff: tx, index: (+ u1 ptr) })))
+     358                 :            :                 )
+     359                 :          0 :                 (begin
+     360                 :            :                     ;; (print "varint 4")
+     361                 :            :                     ;; (print (get uint32 parsed-u32))
+     362                 :          0 :                     (ok { varint: (get uint32 parsed-u32), ctx: (get ctx parsed-u32) })
+     363                 :            :                 ))
+     364            [ - ]:          0 :                 (let (
+     365                 :            :                     ;; next eight bytes is the varint
+     366                 :          0 :                     (parsed-u64 (try! (read-uint64 { txbuff: tx, index: (+ u1 ptr) })))
+     367                 :            :                 )
+     368                 :          0 :                 (begin
+     369                 :            :                     ;; (print "varint 8")
+     370                 :            :                     ;; (print (get uint64 parsed-u64))
+     371                 :          0 :                     (ok { varint: (get uint64 parsed-u64), ctx: (get ctx parsed-u64) })
+     372                 :            :                 ))
+     373                 :            :             )
+     374                 :            :         )
+     375                 :            :     ))
+     376                 :            : )
+     377                 :            : 
+     378                 :            : ;; Reads a varint-prefixed byte slice from txbuff, and updates the index to point to the byte after the varint and slice.
+     379                 :            : ;; Returns (ok { varslice: (buff 1024), ctx: { txbuff: (buff 1024), index: uint } }) on success, where varslice has the length of the varint prefix.
+     380                 :            : ;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff.
+     381                 :            : (define-read-only (read-varslice (old-ctx { txbuff: (buff 1024), index: uint }))
+     382                 :          0 :     (let (
+     383                 :          0 :         (parsed (try! (read-varint old-ctx)))
+     384                 :          0 :         (slice-len (get varint parsed))
+     385                 :          0 :         (ctx (get ctx parsed))
+     386                 :          0 :         (slice-2 (try! (read-slice (get txbuff ctx) (get index ctx) slice-len)))
+     387                 :            :     )
+     388                 :          0 :     (ok {
+     389                 :          0 :         varslice: slice-2,
+     390                 :          0 :         ctx: { txbuff: (get txbuff ctx), index: (+ (len slice-2) (get index ctx)) }
+     391                 :            :     }))
+     392                 :            : )
+     393                 :            : 
+     394                 :            : ;; Generate a permutation of a given 32-byte buffer, appending the element at target-index to hash-output.
+     395                 :            : ;; The target-index decides which index in hash-input gets appended to hash-output.
+     396                 :            : (define-read-only (inner-buff32-permutation (target-index uint) (state { hash-input: (buff 32), hash-output: (buff 32) }))
+     397                 :            :     {
+     398                 :          0 :         hash-input: (get hash-input state),
+     399                 :          0 :         hash-output: (unwrap-panic
+     400                 :          0 :             (as-max-len? (concat
+     401                 :          0 :                 (get hash-output state)
+     402                 :          0 :                 (unwrap-panic
+     403                 :          0 :                     (as-max-len?
+     404                 :          0 :                         (unwrap-panic
+     405                 :          0 :                             (element-at (get hash-input state) target-index))
+     406                 :          0 :                     u32)))
+     407                 :          0 :             u32))
+     408                 :            :     }
+     409                 :            : )
+     410                 :            : 
+     411                 :            : ;; Reverse the byte order of a 32-byte buffer.  Returns the (buff 32).
+     412                 :            : (define-read-only (reverse-buff32 (input (buff 32)))
+     413                 :          0 :     (get hash-output
+     414                 :          0 :          (fold inner-buff32-permutation
+     415                 :          0 :              (list u31 u30 u29 u28 u27 u26 u25 u24 u23 u22 u21 u20 u19 u18 u17 u16 u15 u14 u13 u12 u11 u10 u9 u8 u7 u6 u5 u4 u3 u2 u1 u0)
+     416                 :          0 :              { hash-input: input, hash-output: 0x }))
+     417                 :            : )
+     418                 :            : 
+     419                 :            : ;; Reads a big-endian hash -- consume the next 32 bytes, and reverse them.
+     420                 :            : ;; Returns (ok { hashslice: (buff 32), ctx: { txbuff: (buff 1024), index: uint } }) on success, and updates the index.
+     421                 :            : ;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff.
+     422                 :            : (define-read-only (read-hashslice (old-ctx { txbuff: (buff 1024), index: uint }))
+     423                 :          0 :     (let (
+     424                 :          0 :         (hash-le (unwrap-panic
+     425                 :          0 :                     (as-max-len? (try!
+     426                 :          0 :                                     (read-slice (get txbuff old-ctx) (get index old-ctx) u32))
+     427                 :          0 :                     u32)))
+     428                 :            :     )
+     429                 :          0 :     (ok {
+     430                 :          0 :         hashslice: (reverse-buff32 hash-le),
+     431                 :          0 :         ctx: { txbuff: (get txbuff old-ctx), index: (+ u32 (get index old-ctx)) }
+     432                 :            :     }))
+     433                 :            : )
+     434                 :            : 
+     435                 :            : ;; Inner fold method to read the next tx input from txbuff. 
+     436                 :            : ;; The index in ctx will be updated to point to the next tx input if all goes well (or to the start of the outputs)
+     437                 :            : ;; Returns (ok { ... }) on success.
+     438                 :            : ;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff.
+     439                 :            : ;; Returns (err ERR-VARSLICE-TOO-LONG) if we find a scriptSig that's too long to parse.
+     440                 :            : ;; Returns (err ERR-TOO-MANY-TXINS) if there are more than eight inputs to read.
+     441                 :            : (define-read-only (read-next-txin (ignored bool)
+     442                 :            :                                   (state-res (response {
+     443                 :            :                                     ctx: { txbuff: (buff 1024), index: uint },
+     444                 :            :                                     remaining: uint,
+     445                 :            :                                     txins: (list 8 {
+     446                 :            :                                         outpoint: {
+     447                 :            :                                             hash: (buff 32),
+     448                 :            :                                             index: uint
+     449                 :            :                                         },
+     450                 :            :                                         scriptSig: (buff 256),      ;; just big enough to hold a 2-of-3 multisig script
+     451                 :            :                                         sequence: uint
+     452                 :            :                                     })
+     453                 :            :                                   } uint)))
+     454                 :          0 :     (match state-res
+     455                 :            :         state
+     456            [ - ]:          0 :             (if (< u0 (get remaining state))
+     457            [ - ]:          0 :                 (let (
+     458                 :          0 :                    (remaining (get remaining state))
+     459                 :          0 :                    (ctx (get ctx state))
+     460                 :          0 :                    (parsed-hash (try! (read-hashslice ctx)))
+     461                 :          0 :                    (parsed-index (try! (read-uint32 (get ctx parsed-hash))))
+     462                 :          0 :                    (parsed-scriptSig (try! (read-varslice (get ctx parsed-index))))
+     463                 :          0 :                    (parsed-sequence (try! (read-uint32 (get ctx parsed-scriptSig))))
+     464                 :          0 :                    (new-ctx (get ctx parsed-sequence))
+     465                 :            :                 )
+     466                 :          0 :                 (ok {
+     467                 :          0 :                    ctx: new-ctx,
+     468                 :          0 :                    remaining: (- remaining u1),
+     469                 :          0 :                    txins: (unwrap!
+     470                 :          0 :                      (as-max-len?
+     471                 :          0 :                          (append (get txins state)
+     472                 :            :                              {
+     473                 :          0 :                                  outpoint: {
+     474                 :          0 :                                     hash: (get hashslice parsed-hash),
+     475                 :          0 :                                     index: (get uint32 parsed-index)
+     476                 :            :                                  },
+     477                 :          0 :                                  scriptSig: (unwrap! (as-max-len? (get varslice parsed-scriptSig) u256) (err ERR-VARSLICE-TOO-LONG)),
+     478                 :          0 :                                  sequence: (get uint32 parsed-sequence)
+     479                 :            :                              })
+     480                 :          0 :                      u8)
+     481                 :          0 :                      (err ERR-TOO-MANY-TXINS))
+     482                 :            :                 }))
+     483            [ - ]:          0 :                 (ok state)
+     484                 :            :             )
+     485                 :            :         error
+     486            [ - ]:          0 :             (err error)
+     487                 :            :     )
+     488                 :            : )
+     489                 :            : 
+     490                 :            : ;; Read a transaction's inputs.
+     491                 :            : ;; Returns (ok { txins: (list { ... }), remaining: uint, ctx: { txbuff: (buff 1024), index: uint } }) on success, and updates the index in ctx to point to the start of the tx outputs.
+     492                 :            : ;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff.
+     493                 :            : ;; Returns (err ERR-VARSLICE-TOO-LONG) if we find a scriptSig that's too long to parse.
+     494                 :            : ;; Returns (err ERR-TOO-MANY-TXINS) if there are more than eight inputs to read.
+     495                 :            : (define-read-only (read-txins (ctx { txbuff: (buff 1024), index: uint }))
+     496                 :          0 :     (let (
+     497                 :          0 :         (parsed-num-txins (try! (read-varint ctx)))
+     498                 :          0 :         (num-txins (get varint parsed-num-txins))
+     499                 :          0 :         (new-ctx (get ctx parsed-num-txins))
+     500                 :            :     )
+     501                 :          0 :     (if (> num-txins u8)
+     502            [ - ]:          0 :         (err ERR-TOO-MANY-TXINS)
+     503            [ - ]:          0 :         (fold read-next-txin (list true true true true true true true true) (ok { ctx: new-ctx, remaining: num-txins, txins: (list ) }))
+     504                 :            :     ))
+     505                 :            : )
+     506                 :            : 
+     507                 :            : ;; Read the next transaction output, and update the index in ctx to point to the next output.
+     508                 :            : ;; Returns (ok { ... }) on success
+     509                 :            : ;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff.
+     510                 :            : ;; Returns (err ERR-VARSLICE-TOO-LONG) if we find a scriptPubKey that's too long to parse.
+     511                 :            : ;; Returns (err ERR-TOO-MANY-TXOUTS) if there are more than eight outputs to read.
+     512                 :            : (define-read-only (read-next-txout (ignored bool)
+     513                 :            :                                    (state-res (response {
+     514                 :            :                                     ctx: { txbuff: (buff 1024), index: uint },
+     515                 :            :                                     remaining: uint,
+     516                 :            :                                     txouts: (list 8 {
+     517                 :            :                                         value: uint,
+     518                 :            :                                         scriptPubKey: (buff 128)
+     519                 :            :                                     })
+     520                 :            :                                    } uint)))
+     521                 :          0 :     (match state-res
+     522                 :            :         state
+     523            [ - ]:          0 :             (if (< u0 (get remaining state))
+     524            [ - ]:          0 :                 (let (
+     525                 :          0 :                     (remaining (get remaining state))
+     526                 :          0 :                     (parsed-value (try! (read-uint64 (get ctx state))))
+     527                 :          0 :                     (parsed-script (try! (read-varslice (get ctx parsed-value))))
+     528                 :          0 :                     (new-ctx (get ctx parsed-script))
+     529                 :            :                 )
+     530                 :          0 :                 (ok {
+     531                 :          0 :                     ctx: new-ctx,
+     532                 :          0 :                     remaining: (- remaining u1),
+     533                 :          0 :                     txouts: (unwrap!
+     534                 :          0 :                         (as-max-len?
+     535                 :          0 :                             (append (get txouts state)
+     536                 :            :                                 {
+     537                 :          0 :                                     value: (get uint64 parsed-value),
+     538                 :          0 :                                     scriptPubKey: (unwrap! (as-max-len? (get varslice parsed-script) u128) (err ERR-VARSLICE-TOO-LONG))
+     539                 :            :                                 })
+     540                 :          0 :                         u8) 
+     541                 :          0 :                         (err ERR-TOO-MANY-TXOUTS))
+     542                 :            :                 }))
+     543            [ - ]:          0 :                 (ok state)
+     544                 :            :             )
+     545                 :            :         error
+     546            [ - ]:          0 :             (err error)
+     547                 :            :     )
+     548                 :            : )
+     549                 :            : 
+     550                 :            : ;; Read all transaction outputs in a transaction.  Update the index to point to the first byte after the outputs, if all goes well.
+     551                 :            : ;; Returns (ok { txouts: (list { ... }), remaining: uint, ctx: { txbuff: (buff 1024), index: uint } }) on success, and updates the index in ctx to point to the start of the tx outputs.
+     552                 :            : ;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff.
+     553                 :            : ;; Returns (err ERR-VARSLICE-TOO-LONG) if we find a scriptPubKey that's too long to parse.
+     554                 :            : ;; Returns (err ERR-TOO-MANY-TXOUTS) if there are more than eight outputs to read.
+     555                 :            : (define-read-only (read-txouts (ctx { txbuff: (buff 1024), index: uint }))
+     556                 :          0 :     (let (
+     557                 :          0 :         (parsed-num-txouts (try! (read-varint ctx)))
+     558                 :          0 :         (num-txouts (get varint parsed-num-txouts))
+     559                 :          0 :         (new-ctx (get ctx parsed-num-txouts))
+     560                 :            :     )
+     561                 :          0 :     (if (> num-txouts u8)
+     562            [ - ]:          0 :         (err ERR-TOO-MANY-TXOUTS)
+     563            [ - ]:          0 :         (fold read-next-txout (list true true true true true true true true) (ok { ctx: new-ctx, remaining: num-txouts, txouts: (list ) }))
+     564                 :            :     ))
+     565                 :            : )
+     566                 :            : 
+     567                 :            : ;; Parse a Bitcoin transaction, with up to 8 inputs and 8 outputs, with scriptSigs of up to 256 bytes each, and with scriptPubKeys up to 128 bytes.
+     568                 :            : ;; Returns a tuple structured as follows on success:
+     569                 :            : ;; (ok {
+     570                 :            : ;;      version: uint,                      ;; tx version
+     571                 :            : ;;      ins: (list 8
+     572                 :            : ;;          {
+     573                 :            : ;;              outpoint: {                 ;; pointer to the utxo this input consumes
+     574                 :            : ;;                  hash: (buff 32),
+     575                 :            : ;;                  index: uint
+     576                 :            : ;;              },
+     577                 :            : ;;              scriptSig: (buff 256),      ;; spending condition script
+     578                 :            : ;;              sequence: uint
+     579                 :            : ;;          }),
+     580                 :            : ;;      outs: (list 8
+     581                 :            : ;;          {
+     582                 :            : ;;              value: uint,                ;; satoshis sent
+     583                 :            : ;;              scriptPubKey: (buff 128)    ;; parse this to get an address
+     584                 :            : ;;          }),
+     585                 :            : ;;      locktime: uint
+     586                 :            : ;; })
+     587                 :            : ;; Returns (err ERR-OUT-OF-BOUNDS) if we read past the end of txbuff.
+     588                 :            : ;; Returns (err ERR-VARSLICE-TOO-LONG) if we find a scriptPubKey or scriptSig that's too long to parse.
+     589                 :            : ;; Returns (err ERR-TOO-MANY-TXOUTS) if there are more than eight inputs to read.
+     590                 :            : ;; Returns (err ERR-TOO-MANY-TXINS) if there are more than eight outputs to read.
+     591                 :            : (define-read-only (parse-tx (tx (buff 1024)))
+     592                 :          0 :     (let (
+     593                 :          0 :         (ctx { txbuff: tx, index: u0 })
+     594                 :          0 :         (parsed-version (try! (read-uint32 ctx)))
+     595                 :          0 :         (parsed-txins (try! (read-txins (get ctx parsed-version))))
+     596                 :          0 :         (parsed-txouts (try! (read-txouts (get ctx parsed-txins))))
+     597                 :          0 :         (parsed-locktime (try! (read-uint32 (get ctx parsed-txouts))))
+     598                 :            :     )
+     599                 :          0 :     (ok {
+     600                 :          0 :         version: (get uint32 parsed-version),
+     601                 :          0 :         ins: (get txins parsed-txins),
+     602                 :          0 :         outs: (get txouts parsed-txouts),
+     603                 :          0 :         locktime: (get uint32 parsed-locktime)
+     604                 :            :     }))
+     605                 :            : )
+     606                 :            : 
+     607                 :            : ;; Parse a Bitcoin block header.
+     608                 :            : ;; Returns a tuple structured as folowed on success:
+     609                 :            : ;; (ok {
+     610                 :            : ;;      version: uint,                  ;; block version,
+     611                 :            : ;;      parent: (buff 32),              ;; parent block hash,
+     612                 :            : ;;      merkle-root: (buff 32),         ;; merkle root for all this block's transactions
+     613                 :            : ;;      timestamp: uint,                ;; UNIX epoch timestamp of this block, in seconds
+     614                 :            : ;;      nbits: uint,                    ;; compact block difficulty representation
+     615                 :            : ;;      nonce: uint                     ;; PoW solution
+     616                 :            : ;; })
+     617                 :            : ;; Returns (err ERR-BAD-HEADER) if the header buffer isn't actually 80 bytes long.
+     618                 :            : (define-read-only (parse-block-header (headerbuff (buff 80)))
+     619                 :          0 :     (let (
+     620                 :          0 :         (ctx { txbuff: (unwrap! (as-max-len? headerbuff u1024) (err ERR-BAD-HEADER)), index: u0 })
+     621                 :            : 
+     622                 :            :         ;; none of these should fail, since they're all fixed-length fields whose lengths sum to 80
+     623                 :          0 :         (parsed-version (unwrap-panic (read-uint32 ctx)))
+     624                 :          0 :         (parsed-parent-hash (unwrap-panic (read-hashslice (get ctx parsed-version))))
+     625                 :          0 :         (parsed-merkle-root (unwrap-panic (read-hashslice (get ctx parsed-parent-hash))))
+     626                 :          0 :         (parsed-timestamp (unwrap-panic (read-uint32 (get ctx parsed-merkle-root))))
+     627                 :          0 :         (parsed-nbits (unwrap-panic (read-uint32 (get ctx parsed-timestamp))))
+     628                 :          0 :         (parsed-nonce (unwrap-panic (read-uint32 (get ctx parsed-nbits))))
+     629                 :            :     )
+     630                 :          0 :     (ok {
+     631                 :          0 :         version: (get uint32 parsed-version),
+     632                 :          0 :         parent: (get hashslice parsed-parent-hash),
+     633                 :          0 :         merkle-root: (get hashslice parsed-merkle-root),
+     634                 :          0 :         timestamp: (get uint32 parsed-timestamp),
+     635                 :          0 :         nbits: (get uint32 parsed-nbits),
+     636                 :          0 :         nonce: (get uint32 parsed-nonce)
+     637                 :            :     }))
+     638                 :            : )
+     639                 :            : 
+     640                 :            : ;; Verify that a block header hashes to a burnchain header hash at a given height.
+     641                 :            : ;; Returns true if so; false if not.
+     642                 :            : (define-read-only (verify-block-header (headerbuff (buff 80)) (expected-block-height uint))
+     643                 :          0 :     (match (get-block-info? burnchain-header-hash expected-block-height)
+     644            [ - ]:          0 :         bhh (is-eq bhh (reverse-buff32 (sha256 (sha256 headerbuff))))
+     645            [ - ]:          0 :         false
+     646                 :            :     )
+     647                 :            : )
+     648                 :            : 
+     649                 :            : ;; Get the txid of a transaction, but big-endian.
+     650                 :            : ;; This is the reverse of what you see on block explorers.
+     651                 :            : (define-read-only (get-reversed-txid (tx (buff 1024)))
+     652                 :          0 :     (sha256 (sha256 tx)))
+     653                 :            : 
+     654                 :            : ;; Get the txid of a transaction.
+     655                 :            : ;; This is what you see on block explorers.
+     656                 :            : (define-read-only (get-txid (tx (buff 1024)))
+     657                 :          0 :     (reverse-buff32 (get-reversed-txid tx))
+     658                 :            : )
+     659                 :            : 
+     660                 :            : ;; Determine if the ith bit in a uint is set to 1
+     661                 :            : (define-read-only (is-bit-set (val uint) (bit uint))
+     662                 :          0 :     (is-eq (mod (/ val (pow u2 bit)) u2) u1)
+     663                 :            : )
+     664                 :            : 
+     665                 :            : ;; Verify the next step of a Merkle proof.
+     666                 :            : ;; This hashes cur-hash against the ctr-th hash in proof-hashes, and uses that as the next cur-hash.
+     667                 :            : ;; The path is a bitfield describing the walk from the txid up to the merkle root:
+     668                 :            : ;; * if the ith bit is 0, then cur-hash is hashed before the next proof-hash (cur-hash is "left").
+     669                 :            : ;; * if the ith bit is 1, then the next proof-hash is hashed before cur-hash (cur-hash is "right").
+     670                 :            : ;; The proof verifies if cur-hash is equal to root-hash, and we're out of proof-hashes to check.
+     671                 :            : (define-read-only (inner-merkle-proof-verify (ctr uint) (state { path: uint, root-hash: (buff 32), proof-hashes: (list 12 (buff 32)), tree-depth: uint, cur-hash: (buff 32), verified: bool }))
+     672                 :          0 :     (if (get verified state)
+     673            [ - ]:          0 :         state
+     674            [ - ]:          0 :         (if (>= ctr (get tree-depth state))
+     675            [ - ]:          0 :             (begin
+     676                 :            :                 ;; (print "ctr exceeds proof length or tree depth")
+     677                 :            :                 ;; (print ctr)
+     678                 :            :                 ;; (print (get tree-depth state))
+     679                 :            :                 ;; (print (len (get proof-hashes state)))
+     680                 :          0 :                 (merge state { verified: false })
+     681                 :            :             )
+     682            [ - ]:          0 :             (let (
+     683                 :          0 :                 (path (get path state))
+     684                 :          0 :                 (is-left (is-bit-set path ctr))
+     685                 :          0 :                 (proof-hashes (get proof-hashes state))
+     686                 :          0 :                 (cur-hash (get cur-hash state))
+     687                 :          0 :                 (root-hash (get root-hash state))
+     688                 :            : 
+     689         [ -  - ]:          0 :                 (h1 (if is-left (unwrap-panic (element-at proof-hashes ctr)) cur-hash))
+     690         [ -  - ]:          0 :                 (h2 (if is-left cur-hash (unwrap-panic (element-at proof-hashes ctr))))
+     691                 :          0 :                 (next-hash (sha256 (sha256 (concat h1 h2))))
+     692         [ -  - ]:          0 :                 (is-verified (and (is-eq (+ u1 ctr) (len proof-hashes)) (is-eq next-hash root-hash)))
+     693                 :            :             )
+     694                 :          0 :             (begin
+     695                 :            :                 ;; (print "cur-hash")
+     696                 :            :                 ;; (print cur-hash)
+     697                 :            :                 ;; (print "next-hash")
+     698                 :            :                 ;; (print h1)
+     699                 :            :                 ;; (print h2)
+     700                 :            :                 ;; (print next-hash)
+     701                 :          0 :                 (merge state { cur-hash: next-hash, verified: is-verified })
+     702                 :            :             ))
+     703                 :            :         )
+     704                 :            :     )
+     705                 :            : )
+     706                 :            : 
+     707                 :            : ;; Verify a Merkle proof, given the _reversed_ txid of a transaction, the merkle root of its block, and a proof consisting of:
+     708                 :            : ;; * The index in the block where the transaction can be found (starting from 0),
+     709                 :            : ;; * The list of hashes that link the txid to the merkle root,
+     710                 :            : ;; * The depth of the block's merkle tree (required because Bitcoin does not identify merkle tree nodes as being leaves or intermediates).
+     711                 :            : ;; The _reversed_ txid is required because that's the order (big-endian) processes them in.
+     712                 :            : ;; The tx-index is required because it tells us the left/right traversals we'd make if we were walking down the tree from root to transaction, 
+     713                 :            : ;; and is thus used to deduce the order in which to hash the intermediate hashes with one another to link the txid to the merkle root.
+     714                 :            : ;; Returns (ok true) if the proof is valid.
+     715                 :            : ;; Returns (ok false) if the proof is invalid.
+     716                 :            : ;; Returns (err ERR-PROOF-TOO-SHORT) if the proof's hashes aren't long enough to link the txid to the merkle root.
+     717                 :            : (define-read-only (verify-merkle-proof (reversed-txid (buff 32)) (merkle-root (buff 32)) (proof { tx-index: uint, hashes: (list 12 (buff 32)), tree-depth: uint }))
+     718                 :          0 :     (if (> (get tree-depth proof) (len (get hashes proof)))
+     719            [ - ]:          0 :         (err ERR-PROOF-TOO-SHORT)
+     720            [ - ]:          0 :         (ok
+     721                 :          0 :           (get verified
+     722                 :          0 :               (fold inner-merkle-proof-verify
+     723                 :          0 :                   (list u0 u1 u2 u3 u4 u5 u6 u7 u8 u9 u10 u11)
+     724                 :          0 :                   { path: (+ (pow u2 (get tree-depth proof)) (get tx-index proof)), root-hash: merkle-root, proof-hashes: (get hashes proof), cur-hash: reversed-txid, tree-depth: (get tree-depth proof), verified: false }))
+     725                 :            :         )
+     726                 :            :     )
+     727                 :            : )
+     728                 :            : 
+     729                 :            : ;; Top-level verification code to determine whether or not a Bitcoin transaction was mined in a prior Bitcoin block.
+     730                 :            : ;; It takes the block header and block height, the transaction, and a merkle proof, and determines that:
+     731                 :            : ;; * the block header corresponds to the block that was mined at the given Bitcoin height
+     732                 :            : ;; * the transaction's merkle proof links it to the block header's merkle root.
+     733                 :            : ;; The proof is a list of sibling merkle tree nodes that allow us to calculate the parent node from two children nodes in each merkle tree level,
+     734                 :            : ;; the depth of the block's merkle tree, and the index in the block in which the given transaction can be found (starting from 0).
+     735                 :            : ;; The first element in hashes must be the given transaction's sibling transaction's ID.  This and the given transaction's txid are hashed to 
+     736                 :            : ;; calculate the parent hash in the merkle tree, which is then hashed with the *next* hash in the proof, and so on and so forth, until the final
+     737                 :            : ;; hash can be compared against the block header's merkle root field.  The tx-index tells us in which order to hash each pair of siblings.
+     738                 :            : ;; Note that the proof hashes -- including the sibling txid -- must be _big-endian_ hashes, because this is how Bitcoin generates them.
+     739                 :            : ;; This is the reverse of what you'd see in a block explorer!
+     740                 :            : ;; Returns (ok true) if the proof checks out.
+     741                 :            : ;; Returns (ok false) if not.
+     742                 :            : ;; Returns (err ERR-PROOF-TOO-SHORT) if the proof doesn't contain enough intermediate hash nodes in the merkle tree.
+     743                 :            : (define-read-only (was-tx-mined? (block { header: (buff 80), height: uint }) (tx (buff 1024)) (proof { tx-index: uint, hashes: (list 12 (buff 32)), tree-depth: uint }))
+     744                 :          0 :   (let
+     745                 :            :     (
+     746                 :          0 :       (header-valid (verify-block-header (get header block) (get height block)))
+     747                 :          0 :       (reversed-txid (get-reversed-txid tx))
+     748                 :          0 :       (parsed-header (try! (parse-block-header (get header block))))
+     749                 :          0 :       (merkle-root (reverse-buff32 (get merkle-root parsed-header)))
+     750                 :          0 :       (merkle-valid (verify-merkle-proof reversed-txid merkle-root proof))
+     751                 :            :     )
+     752                 :          0 :     (if header-valid
+     753            [ - ]:          0 :       merkle-valid
+     754            [ - ]:          0 :       (ok false)
+     755                 :            :     )
+     756                 :            :   )
+     757                 :            : )
+     758                 :            : 
+     759                 :            : (define-read-only (verify-prev-block (block (buff 80)) (parent (buff 80)))
+     760                 :          0 :   (let
+     761                 :            :     (
+     762                 :          0 :       (parent-hash (sha256 (sha256 parent)))
+     763                 :          0 :       (parent-reversed (reverse-buff32 parent-hash))
+     764                 :          0 :       (parsed (try! (parse-block-header block)))
+     765                 :          0 :       (parsed-parent (get parent parsed))
+     766                 :            :     )
+     767            [ - ]:          0 :     (asserts! (is-eq parsed-parent parent-reversed) (err ERR-INVALID-PARENT))
+     768                 :          0 :     (ok true)
+     769                 :            :   )
+     770                 :            : )
+     771                 :            : 
+     772                 :            : (define-read-only (verify-prev-blocks (block (buff 80)) (prev-blocks (list 10 (buff 80))))
+     773                 :          0 :   (let
+     774                 :            :     (
+     775                 :          0 :       (iterator (ok block))
+     776                 :          0 :       (fold-resp (fold verify-prev-blocks-fold prev-blocks iterator))
+     777                 :          0 :       (last-block (try! fold-resp))
+     778                 :            :     )
+     779                 :          0 :     (ok last-block)
+     780                 :            :   )
+     781                 :            : )
+     782                 :            : 
+     783                 :            : (define-read-only (verify-prev-blocks-fold 
+     784                 :            :     (parent (buff 80))
+     785                 :            :     (next-resp (response (buff 80) uint))
+     786                 :            :   )
+     787                 :          0 :   (let
+     788                 :            :     (
+     789                 :          0 :       (next (try! next-resp))
+     790                 :          0 :       (is-valid (try! (verify-prev-block next parent)))
+     791                 :            :     )
+     792                 :          0 :     (ok parent)
+     793                 :            :   )
+     794                 :            : )
+     795                 :            : 
+     796                 :            : (define-read-only (was-tx-mined-prev? (block { header: (buff 80), height: uint }) (prev-blocks (list 10 (buff 80))) (tx (buff 1024)) (proof { tx-index: uint, hashes: (list 12 (buff 32)), tree-depth: uint }))
+     797                 :          0 :   (let
+     798                 :            :     (
+     799                 :          0 :       (header-valid (verify-block-header (get header block) (get height block)))
+     800                 :          0 :       (previous-block (try! (verify-prev-blocks (get header block) prev-blocks)))
+     801                 :          0 :       (reversed-txid (get-reversed-txid tx))
+     802                 :          0 :       (parsed-header (try! (parse-block-header previous-block)))
+     803                 :          0 :       (merkle-root (reverse-buff32 (get merkle-root parsed-header)))
+     804                 :          0 :       (merkle-valid (verify-merkle-proof reversed-txid merkle-root proof))
+     805                 :            :     )
+     806                 :          0 :     (if header-valid
+     807            [ - ]:          0 :       merkle-valid
+     808            [ - ]:          0 :       (ok false)
+     809                 :            :     )
+     810                 :            :   )
+     811                 :            : )
+
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/degen-bridge-testnet-v3.clar.func-sort-c.html b/sbtc-ops/smart-contract/coverage/contracts/degen-bridge-testnet-v3.clar.func-sort-c.html new file mode 100644 index 00000000..4c696f45 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/degen-bridge-testnet-v3.clar.func-sort-c.html @@ -0,0 +1,313 @@ + + + + + + + LCOV - coverage.lcov - contracts/degen-bridge-testnet-v3.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - degen-bridge-testnet-v3.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:6434918.3 %
Date:2023-08-18 21:53:02Functions:105817.2 %
Branches:6698.7 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
buff-to-u80
bytes-len0
concat-buffs0
concat-buffs-fold0
escrow-swap0
finalize-outbound-swap0
finalize-swap0
generate-htlc-script0
generate-htlc-script-hash0
generate-output0
generate-p2pkh-output0
generate-p2sh-output0
generate-script-hash0
get-completed-outbound-swap-by-txid0
get-completed-outbound-swap-txid0
get-escrow0
get-full-inbound0
get-full-supplier0
get-inbound-meta0
get-inbound-swap0
get-next-outbound-id0
get-next-supplier-id0
get-next-swapper-id0
get-outbound-swap0
get-preimage0
get-supplier0
get-supplier-id-by-public-key0
get-swapper-id0
get-swapper-principal0
get-total-inbound-volume0
get-total-outbound-volume0
get-total-volume0
get-user-inbound-volume0
get-user-outbound-volume0
get-user-total-volume0
initialize-swapper0
minus-percent0
mul-down0
read-uint320
remove-funds0
revoke-expired-inbound0
revoke-expired-outbound0
update-supplier-fees0
update-supplier-public-key0
update-user-inbound-volume0
update-user-outbound-volume0
validate-expiration0
validate-outbound-revocable0
add-funds1
get-amount-with-fee-rate1
get-funds1
get-supplier-id-by-controller1
get-swap-amount1
initiate-outbound-swap1
register-supplier1
validate-btc-addr1
validate-fee1
transfer2
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/degen-bridge-testnet-v3.clar.func.html b/sbtc-ops/smart-contract/coverage/contracts/degen-bridge-testnet-v3.clar.func.html new file mode 100644 index 00000000..ffec06d1 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/degen-bridge-testnet-v3.clar.func.html @@ -0,0 +1,313 @@ + + + + + + + LCOV - coverage.lcov - contracts/degen-bridge-testnet-v3.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - degen-bridge-testnet-v3.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:6434918.3 %
Date:2023-08-18 21:53:02Functions:105817.2 %
Branches:6698.7 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
add-funds1
buff-to-u80
bytes-len0
concat-buffs0
concat-buffs-fold0
escrow-swap0
finalize-outbound-swap0
finalize-swap0
generate-htlc-script0
generate-htlc-script-hash0
generate-output0
generate-p2pkh-output0
generate-p2sh-output0
generate-script-hash0
get-amount-with-fee-rate1
get-completed-outbound-swap-by-txid0
get-completed-outbound-swap-txid0
get-escrow0
get-full-inbound0
get-full-supplier0
get-funds1
get-inbound-meta0
get-inbound-swap0
get-next-outbound-id0
get-next-supplier-id0
get-next-swapper-id0
get-outbound-swap0
get-preimage0
get-supplier0
get-supplier-id-by-controller1
get-supplier-id-by-public-key0
get-swap-amount1
get-swapper-id0
get-swapper-principal0
get-total-inbound-volume0
get-total-outbound-volume0
get-total-volume0
get-user-inbound-volume0
get-user-outbound-volume0
get-user-total-volume0
initialize-swapper0
initiate-outbound-swap1
minus-percent0
mul-down0
read-uint320
register-supplier1
remove-funds0
revoke-expired-inbound0
revoke-expired-outbound0
transfer2
update-supplier-fees0
update-supplier-public-key0
update-user-inbound-volume0
update-user-outbound-volume0
validate-btc-addr1
validate-expiration0
validate-fee1
validate-outbound-revocable0
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/degen-bridge-testnet-v3.clar.gcov.html b/sbtc-ops/smart-contract/coverage/contracts/degen-bridge-testnet-v3.clar.gcov.html new file mode 100644 index 00000000..4d6cc86c --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/degen-bridge-testnet-v3.clar.gcov.html @@ -0,0 +1,1020 @@ + + + + + + + LCOV - coverage.lcov - contracts/degen-bridge-testnet-v3.clar + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - degen-bridge-testnet-v3.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:6434918.3 %
Date:2023-08-18 21:53:02Functions:105817.2 %
Branches:6698.7 %
+
+ + + + + + + + +

+
           Branch data     Line data    Source code
+
+       1                 :            : (define-map supplier-by-id uint {
+       2                 :            :   public-key: (buff 33),
+       3                 :            :   controller: principal,
+       4                 :            :   inbound-fee: (optional int),
+       5                 :            :   outbound-fee: (optional int),
+       6                 :            :   outbound-base-fee: int,
+       7                 :            :   inbound-base-fee: int,
+       8                 :            : })
+       9                 :            : (define-map supplier-by-public-key (buff 33) uint)
+      10                 :            : (define-map supplier-by-controller principal uint)
+      11                 :            : 
+      12                 :            : (define-map swapper-by-id uint principal)
+      13                 :            : (define-map swapper-by-principal principal uint)
+      14                 :            : 
+      15                 :            : ;; amount of xBTC funds per supplier
+      16                 :            : ;; supplier-id -> xBTC (sats)
+      17                 :            : (define-map supplier-funds uint uint)
+      18                 :            : ;; amount of xBTC funds in escrow per supplier
+      19                 :            : ;; supplier-id -> xBTC
+      20                 :            : (define-map supplier-escrow uint uint)
+      21                 :            : 
+      22                 :            : ;; info for inbound swaps
+      23                 :            : (define-map inbound-swaps (buff 32) {
+      24                 :            :   swapper: uint,
+      25                 :            :   xbtc: uint,
+      26                 :            :   supplier: uint,
+      27                 :            :   expiration: uint,
+      28                 :            :   hash: (buff 32),
+      29                 :            : })
+      30                 :            : ;; extra info for inbound swaps - not needed for the `finalize` step
+      31                 :            : (define-map inbound-meta (buff 32) {
+      32                 :            :   sender-public-key: (buff 33),
+      33                 :            :   output-index: uint,
+      34                 :            :   csv: uint,
+      35                 :            :   sats: uint,
+      36                 :            :   redeem-script: (buff 120),
+      37                 :            : })
+      38                 :            : ;; mapping of txid -> preimage
+      39                 :            : (define-map inbound-preimages (buff 32) (buff 128))
+      40                 :            : 
+      41                 :            : (define-map outbound-swaps uint {
+      42                 :            :   swapper: principal,
+      43                 :            :   sats: uint,
+      44                 :            :   xbtc: uint,
+      45                 :            :   supplier: uint,
+      46                 :            :   version: (buff 1),
+      47                 :            :   hash: (buff 20),
+      48                 :            :   created-at: uint,
+      49                 :            : })
+      50                 :            : ;; mapping of swap -> txid
+      51                 :            : (define-map completed-outbound-swaps uint (buff 32))
+      52                 :            : (define-map completed-outbound-swap-txids (buff 32) uint)
+      53                 :            : 
+      54                 :            : ;; tracking of total volume
+      55                 :            : (define-map user-inbound-volume-map principal uint)
+      56                 :            : (define-data-var total-inbound-volume-var uint u0)
+      57                 :            : 
+      58                 :            : (define-map user-outbound-volume-map principal uint)
+      59                 :            : (define-data-var total-outbound-volume-var uint u0)
+      60                 :            : 
+      61                 :            : (define-data-var next-supplier-id uint u0)
+      62                 :            : (define-data-var next-swapper-id uint u0)
+      63                 :            : (define-data-var next-outbound-id uint u0)
+      64                 :            : 
+      65                 :            : (define-constant MIN_EXPIRATION u250)
+      66                 :            : (define-constant ESCROW_EXPIRATION u200)
+      67                 :            : (define-constant OUTBOUND_EXPIRATION u200)
+      68                 :            : (define-constant MAX_HTLC_EXPIRATION u550)
+      69                 :            : 
+      70                 :            : (define-constant P2PKH_VERSION 0x00)
+      71                 :            : (define-constant P2SH_VERSION 0x05)
+      72                 :            : 
+      73                 :            : ;; use a placeholder txid to mark as "finalized"
+      74                 :            : (define-constant REVOKED_OUTBOUND_TXID 0x00)
+      75                 :            : ;; placeholder to mark inbound swap as revoked
+      76                 :            : (define-constant REVOKED_INBOUND_PREIMAGE 0x00)
+      77                 :            : (define-constant ONE_8 u100000000)
+      78                 :            : 
+      79                 :            : (define-constant ERR_PANIC (err u1)) ;; should never be thrown
+      80                 :            : (define-constant ERR_SUPPLIER_EXISTS (err u2))
+      81                 :            : (define-constant ERR_UNAUTHORIZED (err u3))
+      82                 :            : (define-constant ERR_ADD_FUNDS (err u4))
+      83                 :            : (define-constant ERR_TRANSFER (err u5))
+      84                 :            : (define-constant ERR_SUPPLIER_NOT_FOUND (err u6))
+      85                 :            : (define-constant ERR_SWAPPER_NOT_FOUND (err u7))
+      86                 :            : (define-constant ERR_FEE_INVALID (err u8))
+      87                 :            : (define-constant ERR_SWAPPER_EXISTS (err u9))
+      88                 :            : (define-constant ERR_INVALID_TX (err u10))
+      89                 :            : (define-constant ERR_INVALID_OUTPUT (err u11))
+      90                 :            : (define-constant ERR_INVALID_HASH (err u12))
+      91                 :            : (define-constant ERR_INVALID_SUPPLIER (err u13))
+      92                 :            : (define-constant ERR_INSUFFICIENT_FUNDS (err u14))
+      93                 :            : (define-constant ERR_INVALID_EXPIRATION (err u15))
+      94                 :            : (define-constant ERR_TXID_USED (err u16))
+      95                 :            : (define-constant ERR_ALREADY_FINALIZED (err u17))
+      96                 :            : (define-constant ERR_INVALID_ESCROW (err u18))
+      97                 :            : (define-constant ERR_INVALID_PREIMAGE (err u19))
+      98                 :            : (define-constant ERR_ESCROW_EXPIRED (err u20))
+      99                 :            : (define-constant ERR_TX_NOT_MINED (err u21))
+     100                 :            : (define-constant ERR_INVALID_BTC_ADDR (err u22))
+     101                 :            : (define-constant ERR_SWAP_NOT_FOUND (err u23))
+     102                 :            : (define-constant ERR_INSUFFICIENT_AMOUNT (err u24))
+     103                 :            : (define-constant ERR_REVOKE_OUTBOUND_NOT_EXPIRED (err u25))
+     104                 :            : (define-constant ERR_REVOKE_OUTBOUND_IS_FINALIZED (err u26))
+     105                 :            : (define-constant ERR_INCONSISTENT_FEES (err u27))
+     106                 :            : (define-constant ERR_REVOKE_INBOUND_NOT_EXPIRED (err u28))
+     107                 :            : (define-constant ERR_REVOKE_INBOUND_IS_FINALIZED (err u29))
+     108                 :            : 
+     109                 :            : 
+     110                 :            : ;; Register a supplier and add funds.
+     111                 :            : ;; Validates that the public key and "controller" (STX address) are not
+     112                 :            : ;; in use for another controller.
+     113                 :            : ;;
+     114                 :            : ;; @returns the newly generated supplier ID.
+     115                 :            : ;; 
+     116                 :            : ;; @param public-key; the public key used in HTLCs
+     117                 :            : ;; @param inbound-fee; optional fee (in basis points) for inbound swaps
+     118                 :            : ;; @param outbound-fee; optional fee (in basis points) for outbound
+     119                 :            : ;; @param outbound-base-fee; fixed fee applied to outbound swaps (in xBTC sats)
+     120                 :            : ;; @param inbound-base-fee; fixed fee for inbound swaps (in BTC/sats)
+     121                 :            : ;; @param funds; amount of xBTC (sats) to initially supply
+     122                 :            : (define-public (register-supplier
+     123                 :            :     (public-key (buff 33))
+     124                 :            :     (inbound-fee (optional int))
+     125                 :            :     (outbound-fee (optional int))
+     126                 :            :     (outbound-base-fee int)
+     127                 :            :     (inbound-base-fee int)
+     128                 :            :     (funds uint)
+     129                 :            :   )
+     130                 :          1 :   (let
+     131                 :            :     (
+     132                 :          1 :       (id (var-get next-supplier-id))
+     133                 :          0 :       (supplier { 
+     134                 :          1 :         inbound-fee: inbound-fee, 
+     135                 :          1 :         outbound-fee: outbound-fee, 
+     136                 :          1 :         public-key: public-key, 
+     137                 :          1 :         controller: tx-sender, 
+     138                 :          1 :         outbound-base-fee: outbound-base-fee,
+     139                 :          1 :         inbound-base-fee: inbound-base-fee,
+     140                 :            :       })
+     141                 :            :     )
+     142            [ - ]:          1 :     (asserts! (map-insert supplier-by-id id supplier) ERR_PANIC)
+     143            [ - ]:          1 :     (asserts! (map-insert supplier-funds id u0) ERR_PANIC)
+     144            [ - ]:          1 :     (asserts! (map-insert supplier-escrow id u0) ERR_PANIC)
+     145                 :          1 :     (try! (validate-fee inbound-fee))
+     146                 :          1 :     (try! (validate-fee outbound-fee))
+     147                 :            : 
+     148                 :            :     ;; validate that the public key and controller do not exist
+     149            [ - ]:          1 :     (asserts! (map-insert supplier-by-public-key public-key id) ERR_SUPPLIER_EXISTS)
+     150            [ - ]:          1 :     (asserts! (map-insert supplier-by-controller tx-sender id) ERR_SUPPLIER_EXISTS)
+     151                 :          1 :     (var-set next-supplier-id (+ id u1))
+     152                 :          1 :     (try! (add-funds funds))
+     153                 :          1 :     (ok id)
+     154                 :            :   )
+     155                 :            : )
+     156                 :            : 
+     157                 :            : ;; As a supplier, add funds.
+     158                 :            : ;; The `supplier-id` is automatically looked up from the `contract-caller` (tx-sender).
+     159                 :            : ;;
+     160                 :            : ;; @returns the new amount of funds pooled for this supplier
+     161                 :            : ;;
+     162                 :            : ;; @param amount; the amount of funds to add (in xBTC/sats)
+     163                 :            : (define-public (add-funds (amount uint))
+     164                 :          1 :   (let
+     165                 :            :     (
+     166                 :          1 :       (supplier-id (unwrap! (get-supplier-id-by-controller contract-caller) ERR_UNAUTHORIZED))
+     167                 :          1 :       (existing-funds (get-funds supplier-id))
+     168                 :          1 :       (new-funds (+ amount existing-funds))
+     169                 :            :     )
+     170                 :          1 :     (try! (transfer amount tx-sender (as-contract tx-sender)))
+     171                 :          1 :     (map-set supplier-funds supplier-id new-funds)
+     172                 :          1 :     (ok new-funds)
+     173                 :            :   )
+     174                 :            : )
+     175                 :            : 
+     176                 :            : ;; As a supplier, remove funds.
+     177                 :            : ;;
+     178                 :            : ;; @returns the new amount of funds pooled for this supplier.
+     179                 :            : ;;
+     180                 :            : ;; @param amount; the amount of funds to remove (in xBTC/sats)
+     181                 :            : (define-public (remove-funds (amount uint))
+     182                 :          0 :   (let
+     183                 :            :     (
+     184                 :          0 :       (supplier-id (unwrap! (get-supplier-id-by-controller contract-caller) ERR_UNAUTHORIZED))
+     185                 :          0 :       (existing-funds (get-funds supplier-id))
+     186            [ - ]:          0 :       (amount-ok (asserts! (>= existing-funds amount) ERR_INSUFFICIENT_FUNDS))
+     187                 :          0 :       (new-funds (- existing-funds amount))
+     188                 :          0 :       (controller contract-caller)
+     189                 :            :     )
+     190                 :          0 :     (try! (as-contract (transfer amount tx-sender controller)))
+     191                 :          0 :     (map-set supplier-funds supplier-id new-funds)
+     192                 :          0 :     (ok new-funds)
+     193                 :            :   )
+     194                 :            : )
+     195                 :            : 
+     196                 :            : ;; Update fees for a supplier
+     197                 :            : ;;
+     198                 :            : ;; @returns new metadata for supplier
+     199                 :            : ;;
+     200                 :            : ;; @param inbound-fee; optional fee (in basis points) for inbound swaps
+     201                 :            : ;; @param outbound-fee; optional fee (in basis points) for outbound
+     202                 :            : ;; @param outbound-base-fee; fixed fee applied to outbound swaps (in xBTC sats)
+     203                 :            : ;; @param inbound-base-fee; fixed fee for inbound swaps (in BTC/sats)
+     204                 :            : (define-public (update-supplier-fees
+     205                 :            :     (inbound-fee (optional int))
+     206                 :            :     (outbound-fee (optional int))
+     207                 :            :     (outbound-base-fee int)
+     208                 :            :     (inbound-base-fee int)
+     209                 :            :   )
+     210                 :          0 :   (let
+     211                 :            :     (
+     212                 :          0 :       (supplier-id (unwrap! (get-supplier-id-by-controller contract-caller) ERR_UNAUTHORIZED))
+     213                 :          0 :       (existing-supplier (unwrap! (get-supplier supplier-id) ERR_PANIC))
+     214                 :          0 :       (new-supplier (merge existing-supplier {
+     215                 :          0 :         inbound-fee: inbound-fee, 
+     216                 :          0 :         outbound-fee: outbound-fee, 
+     217                 :          0 :         outbound-base-fee: outbound-base-fee,
+     218                 :          0 :         inbound-base-fee: inbound-base-fee,
+     219                 :            :       }))
+     220                 :            :     )
+     221                 :          0 :     (try! (validate-fee inbound-fee))
+     222                 :          0 :     (try! (validate-fee outbound-fee))
+     223                 :          0 :     (map-set supplier-by-id supplier-id new-supplier)
+     224                 :          0 :     (ok new-supplier)
+     225                 :            :   )
+     226                 :            : )
+     227                 :            : 
+     228                 :            : ;; Update the public-key for a supplier
+     229                 :            : ;;
+     230                 :            : ;; @returns new metadata for the supplier
+     231                 :            : ;;
+     232                 :            : ;; @param public-key; the public key used in HTLCs
+     233                 :            : (define-public (update-supplier-public-key (public-key (buff 33)))
+     234                 :          0 :   (let
+     235                 :            :     (
+     236                 :          0 :       (supplier-id (unwrap! (get-supplier-id-by-controller contract-caller) ERR_UNAUTHORIZED))
+     237                 :          0 :       (existing-supplier (unwrap! (get-supplier supplier-id) ERR_PANIC))
+     238                 :          0 :       (new-supplier (merge existing-supplier {
+     239                 :          0 :         public-key: public-key,
+     240                 :            :       }))
+     241                 :            :     )
+     242            [ - ]:          0 :     (asserts! (map-insert supplier-by-public-key public-key supplier-id) ERR_SUPPLIER_EXISTS)
+     243            [ - ]:          0 :     (asserts! (map-delete supplier-by-public-key (get public-key existing-supplier)) ERR_PANIC)
+     244                 :          0 :     (map-set supplier-by-id supplier-id new-supplier)
+     245                 :          0 :     (ok new-supplier)
+     246                 :            :   )
+     247                 :            : )
+     248                 :            : 
+     249                 :            : 
+     250                 :            : ;; As a swapper, register with the contract to generate your `swapper-id`.
+     251                 :            : ;; This is required to generate BTC deposit addresses that include metadata
+     252                 :            : ;; that point to a specific STX address as the swapper.
+     253                 :            : ;;
+     254                 :            : ;; @returns the newly generated swapper ID.
+     255                 :            : (define-public (initialize-swapper)
+     256                 :          0 :   (let
+     257                 :            :     (
+     258                 :          0 :       (swapper tx-sender)
+     259                 :          0 :       (id (var-get next-swapper-id))
+     260                 :            :     )
+     261            [ - ]:          0 :     (asserts! (map-insert swapper-by-id id swapper) ERR_PANIC)
+     262            [ - ]:          0 :     (asserts! (map-insert swapper-by-principal swapper id) ERR_SWAPPER_EXISTS)
+     263                 :          0 :     (var-set next-swapper-id (+ id u1))
+     264                 :          0 :     (ok id)
+     265                 :            :   )
+     266                 :            : )
+     267                 :            : 
+     268                 :            : ;; Escrow funds for a supplier after sending BTC during an inbound swap.
+     269                 :            : ;; Validates that the BTC tx is valid by re-constructing the HTLC script
+     270                 :            : ;; and comparing it to the BTC tx.
+     271                 :            : ;; Validates that the HTLC data (like expiration) is valid.
+     272                 :            : ;; 
+     273                 :            : ;; `tx-sender` must be equal to the swapper embedded in the HTLC. This ensures
+     274                 :            : ;; that the `min-to-receive` parameter is provided by the end-user.
+     275                 :            : ;;
+     276                 :            : ;; @returns metadata regarding this inbound swap (see `inbound-meta` map)
+     277                 :            : ;;
+     278                 :            : ;; @param block; a tuple containing `header` (the Bitcoin block header) and the `height` (Stacks height)
+     279                 :            : ;; where the BTC tx was confirmed.
+     280                 :            : ;; @param prev-blocks; because Clarity contracts can't get Bitcoin headers when there is no Stacks block,
+     281                 :            : ;; this param allows users to specify the chain of block headers going back to the block where the
+     282                 :            : ;; BTC tx was confirmed.
+     283                 :            : ;; @param tx; the hex data of the BTC tx
+     284                 :            : ;; @param proof; a merkle proof to validate inclusion of this tx in the BTC block
+     285                 :            : ;; @param output-index; the index of the HTLC output in the BTC tx
+     286                 :            : ;; @param sender; The swapper's public key used in the HTLC
+     287                 :            : ;; @param recipient; The supplier's public key used in the HTLC
+     288                 :            : ;; @param expiration-buff; A 4-byte integer the indicated the expiration of the HTLC
+     289                 :            : ;; @param hash; a hash of the `preimage` used in this swap
+     290                 :            : ;; @param swapper-buff; a 4-byte integer that indicates the `swapper-id`
+     291                 :            : ;; @param supplier-id; the supplier used in this swap
+     292                 :            : ;; @param min-to-receive; minimum receivable calculated off-chain to avoid the supplier front-run the swap by adjusting fees
+     293                 :            : (define-public (escrow-swap
+     294                 :            :     (block { header: (buff 80), height: uint })
+     295                 :            :     (prev-blocks (list 10 (buff 80)))
+     296                 :            :     (tx (buff 1024))
+     297                 :            :     (proof { tx-index: uint, hashes: (list 12 (buff 32)), tree-depth: uint })
+     298                 :            :     (output-index uint)
+     299                 :            :     (sender (buff 33))
+     300                 :            :     (recipient (buff 33))
+     301                 :            :     (expiration-buff (buff 4))
+     302                 :            :     (hash (buff 32))
+     303                 :            :     (swapper-buff (buff 4))
+     304                 :            :     (supplier-id uint)
+     305                 :            :     (min-to-receive uint)
+     306                 :            :   )
+     307                 :          0 :   (let
+     308                 :            :     (
+     309                 :          0 :       (was-mined-bool (unwrap! (contract-call? .clarity-bitcoin was-tx-mined-prev? block prev-blocks tx proof) ERR_TX_NOT_MINED)) ;; ST19F1KWRKRF2BZMPW7MWV463K11WED2M39X1HR3A.clarity-bitcoin
+     310            [ - ]:          0 :       (was-mined (asserts! was-mined-bool ERR_TX_NOT_MINED))
+     311                 :          0 :       (mined-height (get height block))
+     312                 :          0 :       (htlc-redeem (generate-htlc-script sender recipient expiration-buff hash swapper-buff))
+     313                 :          0 :       (htlc-output (generate-script-hash htlc-redeem))
+     314                 :          0 :       (parsed-tx (unwrap! (contract-call? .clarity-bitcoin parse-tx tx) ERR_INVALID_TX)) ;; ST19F1KWRKRF2BZMPW7MWV463K11WED2M39X1HR3A.clarity-bitcoin
+     315                 :          0 :       (output (unwrap! (element-at (get outs parsed-tx) output-index) ERR_INVALID_TX))
+     316                 :          0 :       (output-script (get scriptPubKey output))
+     317                 :          0 :       (supplier (unwrap! (map-get? supplier-by-id supplier-id) ERR_INVALID_SUPPLIER))
+     318                 :          0 :       (sats (get value output))
+     319                 :          0 :       (fee-rate (default-to 0 (get inbound-fee supplier)))
+     320                 :          0 :       (xbtc (try! (get-swap-amount sats fee-rate (get inbound-base-fee supplier))))
+     321                 :          0 :       (funds (get-funds supplier-id))
+     322            [ - ]:          0 :       (funds-ok (asserts! (>= funds xbtc) ERR_INSUFFICIENT_FUNDS))
+     323                 :          0 :       (escrowed (unwrap! (map-get? supplier-escrow supplier-id) ERR_PANIC))
+     324                 :          0 :       (new-funds (- funds xbtc))
+     325                 :          0 :       (new-escrow (+ escrowed xbtc))
+     326                 :          0 :       (expiration (try! (read-uint32 expiration-buff (len expiration-buff))))
+     327                 :          0 :       (swapper-id (try! (read-uint32 swapper-buff u4)))
+     328                 :          0 :       (txid (contract-call? .clarity-bitcoin get-txid tx)) ;; ST19F1KWRKRF2BZMPW7MWV463K11WED2M39X1HR3A.clarity-bitcoin
+     329                 :          0 :       (expiration-ok (try! (validate-expiration expiration mined-height)))
+     330                 :          0 :       (escrow {
+     331                 :          0 :         swapper: swapper-id,
+     332                 :          0 :         supplier: supplier-id,
+     333                 :          0 :         xbtc: xbtc,
+     334                 :          0 :         expiration: (+ mined-height (- expiration ESCROW_EXPIRATION)),
+     335                 :          0 :         hash: hash,
+     336                 :            :       })
+     337                 :          0 :       (meta {
+     338                 :          0 :         sender-public-key: sender,
+     339                 :          0 :         output-index: output-index,
+     340                 :          0 :         csv: expiration,
+     341                 :          0 :         redeem-script: htlc-redeem,
+     342                 :          0 :         sats: sats,
+     343                 :            :       })
+     344                 :            :     )
+     345                 :            :     ;; assert tx-sender is swapper
+     346            [ - ]:          0 :     (asserts! (is-eq tx-sender (unwrap! (map-get? swapper-by-id swapper-id) ERR_SWAPPER_NOT_FOUND)) ERR_UNAUTHORIZED)
+     347            [ - ]:          0 :     (asserts! (is-eq (get public-key supplier) recipient) ERR_INVALID_OUTPUT)
+     348            [ - ]:          0 :     (asserts! (is-eq output-script htlc-output) ERR_INVALID_OUTPUT)
+     349            [ - ]:          0 :     (asserts! (is-eq (len hash) u32) ERR_INVALID_HASH)
+     350            [ - ]:          0 :     (asserts! (map-insert inbound-swaps txid escrow) ERR_TXID_USED)
+     351            [ - ]:          0 :     (asserts! (map-insert inbound-meta txid meta) ERR_PANIC)
+     352            [ - ]:          0 :     (asserts! (>= xbtc min-to-receive) ERR_INCONSISTENT_FEES)
+     353                 :          0 :     (map-set supplier-funds supplier-id new-funds)
+     354                 :          0 :     (map-set supplier-escrow supplier-id new-escrow)
+     355                 :          0 :     (print (merge (merge escrow meta) { 
+     356                 :          0 :       topic: "escrow",
+     357                 :          0 :       txid: txid,
+     358                 :            :     }))
+     359                 :          0 :     (ok meta)
+     360                 :            :   )
+     361                 :            : )
+     362                 :            : 
+     363                 :            : (define-private (mul-down (a uint) (b uint))
+     364                 :          0 :   (/ (* a b) ONE_8))
+     365                 :            : 
+     366                 :            : (define-private (minus-percent (a uint) (percent uint))
+     367                 :          0 :   (if (is-eq a u0)
+     368            [ - ]:          0 :     u0
+     369            [ - ]:          0 :     (/ (- (* a u100) (* a percent)) u100)))
+     370                 :            : 
+     371                 :            : ;; Finalize an inbound swap by revealing the preimage.
+     372                 :            : ;; Validates that `sha256(preimage)` is equal to the `hash` provided when
+     373                 :            : ;; escrowing the swap.
+     374                 :            : ;;
+     375                 :            : ;; @returns metadata relating to the swap (see `inbound-swaps` map)
+     376                 :            : ;;
+     377                 :            : ;; @param txid; the txid of the BTC tx used for this inbound swap
+     378                 :            : ;; @param preimage; the preimage that hashes to the swap's `hash`
+     379                 :            : (define-public (finalize-swap (txid (buff 32)) (preimage (buff 128)))
+     380                 :          0 :   (match (map-get? inbound-preimages txid)
+     381            [ - ]:          0 :     existing ERR_ALREADY_FINALIZED
+     382            [ - ]:          0 :     (let
+     383                 :            :       (
+     384                 :          0 :         (swap (unwrap! (map-get? inbound-swaps txid) ERR_INVALID_ESCROW))
+     385                 :          0 :         (stored-hash (get hash swap))
+     386            [ - ]:          0 :         (preimage-ok (asserts! (is-eq (sha256 preimage) stored-hash) ERR_INVALID_PREIMAGE))
+     387                 :          0 :         (supplier-id (get supplier swap))
+     388                 :          0 :         (xbtc (get xbtc swap))
+     389                 :          0 :         (escrowed (unwrap! (map-get? supplier-escrow supplier-id) ERR_PANIC))
+     390                 :          0 :         (swapper (unwrap! (get-swapper-principal (get swapper swap)) ERR_PANIC))
+     391                 :          0 :         (fee-amount 
+     392                 :          0 :           (contract-call? .amm-swap-pool-v1-1 fee-helper .token-wbtc .token-wstx ONE_8))
+     393                 :          0 :         (get-helper-result (try! (contract-call? .amm-swap-pool-v1-1 get-helper .token-wbtc .token-wstx ONE_8 xbtc)))
+     394                 :          0 :         (stx-amount 
+     395                 :          0 :           (mul-down 
+     396                 :          0 :             get-helper-result 
+     397                 :          0 :             (- ONE_8 (unwrap-panic fee-amount))))
+     398                 :          0 :         (stx-amount-slippeage (minus-percent xbtc u5))
+     399                 :          0 :         (btc-to-xbtc-transfer-result (try! (as-contract (transfer xbtc tx-sender swapper))))
+     400                 :          0 :         (xbtc-to-stx-transfer-result 
+     401                 :          0 :           (try! (contract-call? .amm-swap-pool-v1-1 swap-helper
+     402                 :          0 :                     .token-wbtc
+     403                 :          0 :                     .token-wstx
+     404                 :          0 :                     ONE_8
+     405                 :          0 :                     xbtc
+     406                 :          0 :                     (some stx-amount-slippeage))))
+     407                 :            :       )
+     408                 :          0 :       (map-insert inbound-preimages txid preimage)
+     409                 :          0 :       (print xbtc-to-stx-transfer-result)      
+     410            [ - ]:          0 :       (asserts! (>= (get expiration swap) block-height) ERR_ESCROW_EXPIRED)
+     411                 :          0 :       (map-set supplier-escrow supplier-id (- escrowed xbtc))
+     412                 :          0 :       (update-user-inbound-volume swapper xbtc)
+     413                 :          0 :       (print (merge swap {
+     414                 :          0 :         preimage: preimage,
+     415                 :          0 :         topic: "finalize-inbound",
+     416                 :          0 :         txid: txid,
+     417                 :            :       }))
+     418                 :          0 :       (ok swap)
+     419                 :            :     )
+     420                 :            :   )
+     421                 :            : )
+     422                 :            : 
+     423                 :            : ;; Revoke an expired inbound swap.
+     424                 :            : ;; 
+     425                 :            : ;; If an inbound swap has expired, and is not finalized, then the `xbtc`
+     426                 :            : ;; amount of the swap is "stuck" in escrow. Calling this function will:
+     427                 :            : ;; 
+     428                 :            : ;; - Update the supplier's funds and escrow
+     429                 :            : ;; - Mark the swap as finalized
+     430                 :            : ;; 
+     431                 :            : ;; To finalize the swap, the pre-image stored for the swap is the constant
+     432                 :            : ;; REVOKED_INBOUND_PREIMAGE (0x00).
+     433                 :            : ;; 
+     434                 :            : ;; @returns the swap's metadata
+     435                 :            : ;; 
+     436                 :            : ;; @param txid; the txid of the BTC tx used for this inbound swap
+     437                 :            : (define-public (revoke-expired-inbound (txid (buff 32)))
+     438                 :          0 :   (match (map-get? inbound-preimages txid)
+     439            [ - ]:          0 :     existing ERR_REVOKE_INBOUND_IS_FINALIZED
+     440            [ - ]:          0 :     (let
+     441                 :            :       (
+     442                 :          0 :         (swap (unwrap! (map-get? inbound-swaps txid) ERR_INVALID_ESCROW))
+     443                 :          0 :         (xbtc (get xbtc swap))
+     444                 :          0 :         (supplier-id (get supplier swap))
+     445                 :          0 :         (funds (get-funds supplier-id))
+     446                 :          0 :         (escrowed (unwrap! (get-escrow supplier-id) ERR_PANIC))
+     447                 :          0 :         (new-funds (+ funds xbtc))
+     448                 :          0 :         (new-escrow (- escrowed xbtc))
+     449                 :            :       )
+     450            [ - ]:          0 :       (asserts! (<= (get expiration swap) block-height) ERR_REVOKE_INBOUND_NOT_EXPIRED)
+     451                 :          0 :       (map-insert inbound-preimages txid REVOKED_INBOUND_PREIMAGE)
+     452                 :          0 :       (map-set supplier-escrow supplier-id new-escrow)
+     453                 :          0 :       (map-set supplier-funds supplier-id new-funds)
+     454                 :          0 :       (print (merge swap {
+     455                 :          0 :         topic: "revoke-inbound",
+     456                 :          0 :         txid: txid,
+     457                 :            :       }))
+     458                 :          0 :       (ok swap)
+     459                 :            :     )
+     460                 :            :   )
+     461                 :            : )
+     462                 :            : 
+     463                 :            : ;; outbound swaps
+     464                 :            : 
+     465                 :            : ;; Initiate an outbound swap.
+     466                 :            : ;; Swapper provides the amount of xBTC and their withdraw address.
+     467                 :            : ;;
+     468                 :            : ;; @returns the auto-generated swap-id of this swap
+     469                 :            : ;;
+     470                 :            : ;; @param xbtc; amount of xBTC (sats) to swap
+     471                 :            : ;; @param btc-version; the address version for the swapper's BTC address
+     472                 :            : ;; @param btc-hash; the hash for the swapper's BTC address
+     473                 :            : ;; @param supplier-id; the supplier used for this swap
+     474                 :            : (define-public (initiate-outbound-swap (xbtc uint) (btc-version (buff 1)) (btc-hash (buff 20)) (supplier-id uint))
+     475                 :          1 :   (let
+     476                 :            :     (
+     477                 :          1 :       (supplier (unwrap! (map-get? supplier-by-id supplier-id) ERR_INVALID_SUPPLIER))
+     478                 :          1 :       (fee-rate (default-to 0 (get outbound-fee supplier)))
+     479                 :          1 :       (sats (try! (get-swap-amount xbtc fee-rate (get outbound-base-fee supplier))))
+     480                 :          0 :       (swap {
+     481                 :          1 :         sats: sats,
+     482                 :          1 :         xbtc: xbtc,
+     483                 :          1 :         supplier: supplier-id,
+     484                 :          1 :         version: btc-version,
+     485                 :          1 :         hash: btc-hash,
+     486                 :          1 :         created-at: burn-block-height,
+     487                 :          1 :         swapper: tx-sender,
+     488                 :            :       })
+     489                 :          1 :       (swap-id (var-get next-outbound-id))
+     490                 :            :     )
+     491                 :          1 :     (try! (validate-btc-addr btc-version btc-hash))
+     492                 :          1 :     (try! (transfer xbtc tx-sender (as-contract tx-sender)))
+     493            [ - ]:          1 :     (asserts! (map-insert outbound-swaps swap-id swap) ERR_PANIC)
+     494                 :          1 :     (var-set next-outbound-id (+ swap-id u1))
+     495                 :          1 :     (print (merge swap {
+     496                 :          1 :       swap-id: swap-id,
+     497                 :          1 :       topic: "initiate-outbound",
+     498                 :            :     }))
+     499                 :          1 :     (ok swap-id)
+     500                 :            :   )
+     501                 :            : )
+     502                 :            : 
+     503                 :            : ;; Finalize an outbound swap.
+     504                 :            : ;; This method is called by the supplier after they've sent the swapper BTC.
+     505                 :            : ;;
+     506                 :            : ;; @returns true
+     507                 :            : ;;
+     508                 :            : ;; @param block; a tuple containing `header` (the Bitcoin block header) and the `height` (Stacks height)
+     509                 :            : ;; where the BTC tx was confirmed.
+     510                 :            : ;; @param prev-blocks; because Clarity contracts can't get Bitcoin headers when there is no Stacks block,
+     511                 :            : ;; this param allows users to specify the chain of block headers going back to the block where the
+     512                 :            : ;; BTC tx was confirmed.
+     513                 :            : ;; @param tx; the hex data of the BTC tx
+     514                 :            : ;; @param proof; a merkle proof to validate inclusion of this tx in the BTC block
+     515                 :            : ;; @param output-index; the index of the HTLC output in the BTC tx
+     516                 :            : ;; @param swap-id; the outbound swap ID they're finalizing
+     517                 :            : (define-public (finalize-outbound-swap
+     518                 :            :     (block { header: (buff 80), height: uint })
+     519                 :            :     (prev-blocks (list 10 (buff 80)))
+     520                 :            :     (tx (buff 1024))
+     521                 :            :     (proof { tx-index: uint, hashes: (list 12 (buff 32)), tree-depth: uint })
+     522                 :            :     (output-index uint)
+     523                 :            :     (swap-id uint)
+     524                 :            :   )
+     525                 :          0 :   (let
+     526                 :            :     (
+     527                 :          0 :       (was-mined-bool (unwrap! (contract-call? .clarity-bitcoin was-tx-mined-prev? block prev-blocks tx proof) ERR_TX_NOT_MINED)) ;;ST19F1KWRKRF2BZMPW7MWV463K11WED2M39X1HR3A.clarity-bitcoin
+     528            [ - ]:          0 :       (was-mined (asserts! was-mined-bool ERR_TX_NOT_MINED))
+     529                 :          0 :       (swap (unwrap! (get-outbound-swap swap-id) ERR_SWAP_NOT_FOUND))
+     530                 :          0 :       (expected-output (generate-output (get version swap) (get hash swap)))
+     531                 :          0 :       (parsed-tx (unwrap! (contract-call? .clarity-bitcoin parse-tx tx) ERR_INVALID_TX)) ;;ST19F1KWRKRF2BZMPW7MWV463K11WED2M39X1HR3A.clarity-bitcoin
+     532                 :          0 :       (output (unwrap! (element-at (get outs parsed-tx) output-index) ERR_INVALID_TX))
+     533                 :          0 :       (output-script (get scriptPubKey output))
+     534                 :          0 :       (txid (contract-call? .clarity-bitcoin get-txid tx)) ;;ST19F1KWRKRF2BZMPW7MWV463K11WED2M39X1HR3A.clarity-bitcoin
+     535                 :          0 :       (output-sats (get value output))
+     536                 :          0 :       (xbtc (get xbtc swap))
+     537                 :          0 :       (supplier (get supplier swap))
+     538                 :          0 :       (funds-before (get-funds supplier))
+     539                 :            :     )
+     540                 :          0 :     (map-set supplier-funds supplier (+ funds-before xbtc))
+     541            [ - ]:          0 :     (asserts! (is-eq output-script expected-output) ERR_INVALID_OUTPUT)
+     542            [ - ]:          0 :     (asserts! (map-insert completed-outbound-swaps swap-id txid) ERR_ALREADY_FINALIZED)
+     543            [ - ]:          0 :     (asserts! (map-insert completed-outbound-swap-txids txid swap-id) ERR_TXID_USED)
+     544            [ - ]:          0 :     (asserts! (>= output-sats (get sats swap)) ERR_INSUFFICIENT_AMOUNT)
+     545                 :          0 :     (update-user-outbound-volume (get swapper swap) xbtc)
+     546                 :          0 :     (print (merge swap {
+     547                 :          0 :       topic: "finalize-outbound",
+     548                 :          0 :       txid: txid,
+     549                 :          0 :       swap-id: swap-id,
+     550                 :            :     }))
+     551                 :          0 :     (ok true)
+     552                 :            :   )
+     553                 :            : )
+     554                 :            : 
+     555                 :            : ;; Revoke an expired outbound swap.
+     556                 :            : ;; After an outbound swap has expired without finalizing, a swapper may call this function
+     557                 :            : ;; to receive the xBTC escrowed.
+     558                 :            : ;;
+     559                 :            : ;; @returns the metadata regarding the outbound swap
+     560                 :            : ;;
+     561                 :            : ;; @param swap-id; the ID of the outbound swap being revoked.
+     562                 :            : (define-public (revoke-expired-outbound (swap-id uint))
+     563                 :          0 :   (let
+     564                 :            :     (
+     565                 :          0 :       (swap (try! (validate-outbound-revocable swap-id)))
+     566                 :          0 :       (xbtc (get xbtc swap))
+     567                 :          0 :       (swapper (get swapper swap))
+     568                 :            :     )
+     569                 :          0 :     (try! (as-contract (transfer xbtc tx-sender swapper)))
+     570            [ - ]:          0 :     (asserts! (map-insert completed-outbound-swaps swap-id REVOKED_OUTBOUND_TXID) ERR_PANIC)
+     571                 :          0 :     (print (merge swap {
+     572                 :          0 :       topic: "revoke-outbound",
+     573                 :          0 :       swap-id: swap-id,
+     574                 :            :     }))
+     575                 :          0 :     (ok swap)
+     576                 :            :   )
+     577                 :            : )
+     578                 :            : 
+     579                 :            : ;; getters
+     580                 :            : 
+     581                 :            : (define-read-only (get-supplier-id-by-controller (controller principal))
+     582                 :          1 :   (map-get? supplier-by-controller controller)
+     583                 :            : )
+     584                 :            : 
+     585                 :            : (define-read-only (get-supplier-id-by-public-key (public-key (buff 33)))
+     586                 :          0 :   (map-get? supplier-by-public-key public-key)
+     587                 :            : )
+     588                 :            : 
+     589                 :            : (define-read-only (get-supplier (id uint))
+     590                 :          0 :   (map-get? supplier-by-id id)
+     591                 :            : )
+     592                 :            : 
+     593                 :            : (define-read-only (get-funds (id uint))
+     594                 :          1 :   (default-to u0 (map-get? supplier-funds id))
+     595                 :            : )
+     596                 :            : 
+     597                 :            : (define-read-only (get-escrow (id uint))
+     598                 :          0 :   (map-get? supplier-escrow id)
+     599                 :            : )
+     600                 :            : 
+     601                 :            : (define-read-only (get-inbound-swap (txid (buff 32)))
+     602                 :          0 :   (map-get? inbound-swaps txid)
+     603                 :            : )
+     604                 :            : 
+     605                 :            : (define-read-only (get-preimage (txid (buff 32)))
+     606                 :          0 :   (map-get? inbound-preimages txid)
+     607                 :            : )
+     608                 :            : 
+     609                 :            : (define-read-only (get-outbound-swap (id uint))
+     610                 :          0 :   (map-get? outbound-swaps id)
+     611                 :            : )
+     612                 :            : 
+     613                 :            : (define-read-only (get-completed-outbound-swap-txid (id uint))
+     614                 :          0 :   (map-get? completed-outbound-swaps id)
+     615                 :            : )
+     616                 :            : 
+     617                 :            : (define-read-only (get-completed-outbound-swap-by-txid (txid (buff 32)))
+     618                 :          0 :   (map-get? completed-outbound-swap-txids txid)
+     619                 :            : )
+     620                 :            : 
+     621                 :            : (define-read-only (get-swapper-id (swapper principal))
+     622                 :          0 :   (map-get? swapper-by-principal swapper)
+     623                 :            : )
+     624                 :            : 
+     625                 :            : (define-read-only (get-swapper-principal (id uint))
+     626                 :          0 :   (map-get? swapper-by-id id)
+     627                 :            : )
+     628                 :            : 
+     629                 :          0 : (define-read-only (get-next-supplier-id) (var-get next-supplier-id))
+     630                 :          0 : (define-read-only (get-next-swapper-id) (var-get next-swapper-id))
+     631                 :          0 : (define-read-only (get-next-outbound-id) (var-get next-outbound-id))
+     632                 :            : 
+     633                 :            : (define-read-only (get-full-supplier (id uint))
+     634                 :          0 :   (let
+     635                 :            :     (
+     636                 :          0 :       (supplier (unwrap! (get-supplier id) ERR_INVALID_SUPPLIER))
+     637                 :          0 :       (funds (get-funds id))
+     638                 :          0 :       (escrow (unwrap! (get-escrow id) ERR_PANIC))
+     639                 :            :     )
+     640                 :          0 :     (ok (merge supplier { funds: funds, escrow: escrow }))
+     641                 :            :   )
+     642                 :            : )
+     643                 :            : 
+     644                 :            : (define-read-only (get-inbound-meta (txid (buff 32)))
+     645                 :          0 :   (map-get? inbound-meta txid)
+     646                 :            : )
+     647                 :            : 
+     648                 :            : (define-read-only (get-full-inbound (txid (buff 32)))
+     649                 :          0 :   (let
+     650                 :            :     (
+     651                 :          0 :       (swap (unwrap! (get-inbound-swap txid) ERR_INVALID_ESCROW))
+     652                 :          0 :       (meta (unwrap! (get-inbound-meta txid) ERR_INVALID_ESCROW))
+     653                 :          0 :       (swapper-principal (unwrap! (get-swapper-principal (get swapper swap)) ERR_PANIC))
+     654                 :            :     )
+     655                 :          0 :     (ok (merge { swapper-principal: swapper-principal } (merge swap meta)))
+     656                 :            :   )
+     657                 :            : )
+     658                 :            : 
+     659                 :            : (define-read-only (get-user-inbound-volume (user principal))
+     660                 :          0 :   (match (map-get? user-inbound-volume-map user)
+     661            [ - ]:          0 :     vol vol
+     662            [ - ]:          0 :     u0
+     663                 :            :   )
+     664                 :            : )
+     665                 :            : 
+     666                 :          0 : (define-read-only (get-total-inbound-volume) (var-get total-inbound-volume-var))
+     667                 :            : 
+     668                 :            : (define-read-only (get-user-outbound-volume (user principal))
+     669                 :          0 :   (match (map-get? user-outbound-volume-map user)
+     670            [ - ]:          0 :     vol vol
+     671            [ - ]:          0 :     u0
+     672                 :            :   )
+     673                 :            : )
+     674                 :            : 
+     675                 :          0 : (define-read-only (get-total-outbound-volume) (var-get total-outbound-volume-var))
+     676                 :            : 
+     677                 :            : (define-read-only (get-user-total-volume (user principal))
+     678                 :          0 :   (+ (get-user-inbound-volume user) (get-user-outbound-volume user))
+     679                 :            : )
+     680                 :            : 
+     681                 :            : (define-read-only (get-total-volume)
+     682                 :          0 :   (+ (get-total-inbound-volume) (get-total-outbound-volume))
+     683                 :            : )
+     684                 :            : 
+     685                 :            : ;; helpers
+     686                 :            : 
+     687                 :            : (define-private (transfer (amount uint) (sender principal) (recipient principal))
+     688                 :            : ;;On mainnet: we'll call AlexGo contract address
+     689                 :            : ;;ST3VRQJMS69354J6DTPKG5W67XP31D4E6HJW708W4 used in testnet for Wrapped-Bitcoin
+     690                 :          2 :   (match (contract-call? .token-wbtc transfer amount sender recipient none) 
+     691            [ + ]:          2 :     success (ok success)
+     692            [ - ]:          0 :     error (begin
+     693                 :          0 :       (print { transfer-error: error })
+     694                 :          0 :       ERR_TRANSFER
+     695                 :            :     )
+     696                 :            :   )
+     697                 :            : )
+     698                 :            : 
+     699                 :            : (define-read-only (concat-buffs (buffs (list 6 (buff 32))))
+     700                 :          0 :   (let
+     701                 :            :     (
+     702                 :          0 :       (initial-buff 0x)
+     703                 :          0 :       (final (fold concat-buffs-fold buffs initial-buff))
+     704                 :            :     )
+     705                 :          0 :     final
+     706                 :            :   )
+     707                 :            : )
+     708                 :            : 
+     709                 :            : (define-private (concat-buffs-fold (b (buff 32)) (result (buff 192)))
+     710                 :          0 :   (let
+     711                 :            :     (
+     712                 :          0 :       (next-buff (concat result b))
+     713                 :          0 :       (next-buff-min (as-max-len? next-buff u192))
+     714                 :            :     )
+     715                 :          0 :     (match next-buff-min
+     716            [ - ]:          0 :       min-buff min-buff
+     717            [ - ]:          0 :       result ;; if using `concat-buffs`, this should never happen.
+     718                 :            :     )
+     719                 :            :   )
+     720                 :            : )
+     721                 :            : 
+     722                 :            : (define-read-only (get-swap-amount (amount uint) (fee-rate int) (base-fee int))
+     723                 :          1 :   (let
+     724                 :            :     (
+     725                 :          1 :       (with-bps-fee (get-amount-with-fee-rate amount fee-rate))
+     726                 :            :     )
+     727                 :          1 :     (if (>= base-fee with-bps-fee)
+     728            [ - ]:          0 :       ERR_INSUFFICIENT_AMOUNT
+     729            [ + ]:          1 :       (ok (to-uint (- with-bps-fee base-fee)))
+     730                 :            :     )
+     731                 :            :   )
+     732                 :            : )
+     733                 :            : 
+     734                 :            : (define-read-only (get-amount-with-fee-rate (amount uint) (fee-rate int))
+     735                 :          1 :   (let
+     736                 :            :     (
+     737                 :          1 :       (numerator (* (to-int amount) (- 10000 fee-rate)))
+     738                 :          1 :       (final (/ numerator 10000))
+     739                 :            :     )
+     740                 :          1 :     final
+     741                 :            :   )
+     742                 :            : )
+     743                 :            : 
+     744                 :            : (define-private (update-user-inbound-volume (user principal) (amount uint))
+     745                 :          0 :   (let
+     746                 :            :     (
+     747                 :          0 :       (user-total (get-user-inbound-volume user))
+     748                 :          0 :       (total (get-total-inbound-volume))
+     749                 :            :     )
+     750                 :          0 :     (map-set user-inbound-volume-map user (+ user-total amount))
+     751                 :          0 :     (var-set total-inbound-volume-var (+ total amount))
+     752                 :          0 :     true
+     753                 :            :   )
+     754                 :            : )
+     755                 :            : 
+     756                 :            : (define-private (update-user-outbound-volume (user principal) (amount uint))
+     757                 :          0 :   (let
+     758                 :            :     (
+     759                 :          0 :       (user-total (get-user-outbound-volume user))
+     760                 :          0 :       (total (get-total-outbound-volume))
+     761                 :            :     )
+     762                 :          0 :     (map-set user-outbound-volume-map user (+ user-total amount))
+     763                 :          0 :     (var-set total-outbound-volume-var (+ total amount))
+     764                 :          0 :     true
+     765                 :            :   )
+     766                 :            : )
+     767                 :            : 
+     768                 :            : ;; validators
+     769                 :            : 
+     770                 :            : ;; Validate the expiration for an inbound swap.
+     771                 :            : ;; 
+     772                 :            : ;; There are two validations used here:
+     773                 :            : ;; 
+     774                 :            : ;; - Expiration isn't too soon. To ensure that the swapper and supplier have sufficient
+     775                 :            : ;; time to finalize, a swap must be escrowed with **at least** 250 blocks remaining.
+     776                 :            : ;; - Expiration isn't too far. The HTLC must have a `CHECKSEQUENCEVERIFY` of less
+     777                 :            : ;; than 550. This ensures that a supplier's xBTC isn't escrowed for unnecessarily long times.
+     778                 :            : ;; 
+     779                 :            : ;; @param expiration; the amount of blocks that need to pass before
+     780                 :            : ;; the sender can recover their HTLC. This is the value used with `CHECKSEQUENCEVERIFY`
+     781                 :            : ;; in the HTLC script.
+     782                 :            : ;; @param mined-height; the nearest stacks block after (or including) the Bitcoin
+     783                 :            : ;; block where the HTLC was confirmed.
+     784                 :            : (define-read-only (validate-expiration (expiration uint) (mined-height uint))
+     785                 :          0 :   (if (> expiration (+ (- block-height mined-height) MIN_EXPIRATION))
+     786    [ - ][ -  - ]:          0 :     (if (< expiration MAX_HTLC_EXPIRATION) (ok true) ERR_INVALID_EXPIRATION)
+     787            [ - ]:          0 :     ERR_INVALID_EXPIRATION
+     788                 :            :   )
+     789                 :            : )
+     790                 :            : 
+     791                 :            : (define-read-only (validate-fee (fee-opt (optional int)))
+     792                 :          2 :   (match fee-opt
+     793            [ - ]:          0 :     fee (let
+     794                 :            :       (
+     795                 :          0 :         (max-fee 10000)
+     796                 :          0 :         (within-upper (< fee max-fee))
+     797                 :          0 :         (within-lower (> fee (* -1 max-fee)))
+     798                 :            :       )
+     799    [ - ][ -  - ]:          0 :       (asserts! (and within-upper within-lower) ERR_FEE_INVALID)
+     800                 :          0 :       (ok true)
+     801                 :            :     )
+     802            [ + ]:          2 :     (ok true)
+     803                 :            :   )
+     804                 :            : )
+     805                 :            : 
+     806                 :            : (define-read-only (validate-btc-addr (version (buff 1)) (hash (buff 20)))
+     807                 :          1 :   (let
+     808                 :            :     (
+     809                 :          1 :       (valid-hash (is-eq (len hash) u20))
+     810         [ +  - ]:          1 :       (valid-version (or (is-eq version P2PKH_VERSION) (is-eq version P2SH_VERSION)))
+     811                 :            :     )
+     812    [ - ][ +  + ]:          1 :     (asserts! (and valid-hash valid-version) ERR_INVALID_BTC_ADDR)
+     813                 :          1 :     (ok true)
+     814                 :            :   )
+     815                 :            : )
+     816                 :            : 
+     817                 :            : ;; lookup an outbound swap and validate that it is revocable.
+     818                 :            : ;; to be revoked, it must be expired and not finalized
+     819                 :            : (define-read-only (validate-outbound-revocable (swap-id uint))
+     820                 :          0 :   (let
+     821                 :            :     (
+     822                 :          0 :       (swap (unwrap! (get-outbound-swap swap-id) ERR_SWAP_NOT_FOUND))
+     823                 :          0 :       (finalize-txid (get-completed-outbound-swap-txid swap-id))
+     824                 :          0 :       (swap-expiration (+ (get created-at swap) OUTBOUND_EXPIRATION))
+     825                 :          0 :       (is-expired (>= burn-block-height swap-expiration))
+     826                 :          0 :       (is-not-finalized (is-none finalize-txid))
+     827                 :            :     )
+     828            [ - ]:          0 :     (asserts! is-expired ERR_REVOKE_OUTBOUND_NOT_EXPIRED)
+     829            [ - ]:          0 :     (asserts! is-not-finalized ERR_REVOKE_OUTBOUND_IS_FINALIZED)
+     830                 :          0 :     (ok swap)
+     831                 :            :   )
+     832                 :            : )
+     833                 :            : 
+     834                 :            : ;; htlc
+     835                 :            : 
+     836                 :            : (define-read-only (generate-htlc-script
+     837                 :            :     (sender (buff 33))
+     838                 :            :     (recipient (buff 33))
+     839                 :            :     (expiration (buff 4))
+     840                 :            :     (hash (buff 32))
+     841                 :            :     (swapper (buff 4))
+     842                 :            :   )
+     843                 :          0 :   (let
+     844                 :            :     (
+     845                 :          0 :       (swapper-id (concat 0x04 swapper))
+     846                 :          0 :       (b10 (concat swapper-id 0x7563a820))
+     847                 :          0 :       (b1 (concat b10 hash))
+     848                 :          0 :       (b2 (concat b1 0x8821))
+     849                 :          0 :       (b3 (concat b2 recipient))
+     850                 :          0 :       (b4 (concat b3 0x67))
+     851                 :          0 :       (exp-len (bytes-len expiration))
+     852                 :          0 :       (b9 (concat b4 exp-len))
+     853                 :          0 :       (b5 (concat b9 expiration))
+     854                 :          0 :       (b6 (concat b5 0xb27521))
+     855                 :          0 :       (b7 (concat b6 sender))
+     856                 :          0 :       (b8 (concat b7 0x68ac))
+     857                 :            :     )
+     858                 :          0 :     b8
+     859                 :            :   )
+     860                 :            : )
+     861                 :            : 
+     862                 :            : (define-read-only (generate-script-hash (script (buff 120)))
+     863                 :          0 :   (generate-p2sh-output (hash160 script))
+     864                 :            : )
+     865                 :            : 
+     866                 :            : (define-read-only (generate-htlc-script-hash
+     867                 :            :     (sender (buff 33))
+     868                 :            :     (recipient (buff 33))
+     869                 :            :     (expiration (buff 4))
+     870                 :            :     (hash (buff 32))
+     871                 :            :     (swapper (buff 4))
+     872                 :            :   )
+     873                 :          0 :   (generate-script-hash (generate-htlc-script sender recipient expiration hash swapper))
+     874                 :            : )
+     875                 :            : 
+     876                 :            : (define-read-only (generate-p2pkh-output (hash (buff 20)))
+     877                 :          0 :   (concat (concat 0x76a914 hash) 0x88ac)
+     878                 :            : )
+     879                 :            : 
+     880                 :            : (define-read-only (generate-p2sh-output (hash (buff 20)))
+     881                 :          0 :   (concat (concat 0xa914 hash) 0x87)
+     882                 :            : )
+     883                 :            : 
+     884                 :            : ;; generate an output, given btc address.
+     885                 :            : ;; assumes that if the version is not p2sh, it's p2pkh.
+     886                 :            : ;; for outbound swaps, the version is validated when initiated,
+     887                 :            : ;; so it should only ever be these two.
+     888                 :            : (define-read-only (generate-output (version (buff 1)) (hash (buff 20)))
+     889                 :          0 :   (if (is-eq version P2SH_VERSION)
+     890            [ - ]:          0 :     (generate-p2sh-output hash)
+     891            [ - ]:          0 :     (generate-p2pkh-output hash)
+     892                 :            :   )
+     893                 :            : )
+     894                 :            : 
+     895                 :            : (define-read-only (bytes-len (bytes (buff 4)))
+     896                 :          0 :   (unwrap-panic (element-at BUFF_TO_BYTE (len bytes)))
+     897                 :            : )
+     898                 :            : 
+     899                 :            : (define-constant ERR_READ_UINT (err u100))
+     900                 :            : 
+     901                 :            : ;; little-endian
+     902                 :            : (define-read-only (read-uint32 (num (buff 4)) (length uint))
+     903                 :          0 :   (let
+     904                 :            :     (
+     905                 :          0 :       (byte-1 (buff-to-u8 (unwrap! (element-at num u0) ERR_READ_UINT)))
+     906         [ -  - ]:          0 :       (byte-2 (if (> length u1) (buff-to-u8 (unwrap! (element-at num u1) ERR_READ_UINT)) u0))
+     907         [ -  - ]:          0 :       (byte-3 (if (> length u2) (buff-to-u8 (unwrap! (element-at num u2) ERR_READ_UINT)) u0))
+     908         [ -  - ]:          0 :       (byte-4 (if (> length u3) (buff-to-u8 (unwrap! (element-at num u3) ERR_READ_UINT)) u0))
+     909                 :            :     )
+     910                 :          0 :     (ok (+ (* byte-4 u16777216) (* byte-3 u65536) (* byte-2 u256) byte-1))
+     911                 :            :   )
+     912                 :            : )
+     913                 :            : 
+     914                 :            : (define-read-only (buff-to-u8 (byte (buff 1)))
+     915                 :          0 :     (unwrap-panic (index-of BUFF_TO_BYTE byte)))
+     916                 :            : 
+     917                 :            : 
+     918                 :            : (define-constant BUFF_TO_BYTE (list 
+     919                 :            :    0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f
+     920                 :            :    0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e 0x1f
+     921                 :            :    0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x2a 0x2b 0x2c 0x2d 0x2e 0x2f
+     922                 :            :    0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x3a 0x3b 0x3c 0x3d 0x3e 0x3f
+     923                 :            :    0x40 0x41 0x42 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4a 0x4b 0x4c 0x4d 0x4e 0x4f
+     924                 :            :    0x50 0x51 0x52 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5a 0x5b 0x5c 0x5d 0x5e 0x5f
+     925                 :            :    0x60 0x61 0x62 0x63 0x64 0x65 0x66 0x67 0x68 0x69 0x6a 0x6b 0x6c 0x6d 0x6e 0x6f
+     926                 :            :    0x70 0x71 0x72 0x73 0x74 0x75 0x76 0x77 0x78 0x79 0x7a 0x7b 0x7c 0x7d 0x7e 0x7f
+     927                 :            :    0x80 0x81 0x82 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8a 0x8b 0x8c 0x8d 0x8e 0x8f
+     928                 :            :    0x90 0x91 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9a 0x9b 0x9c 0x9d 0x9e 0x9f
+     929                 :            :    0xa0 0xa1 0xa2 0xa3 0xa4 0xa5 0xa6 0xa7 0xa8 0xa9 0xaa 0xab 0xac 0xad 0xae 0xaf
+     930                 :            :    0xb0 0xb1 0xb2 0xb3 0xb4 0xb5 0xb6 0xb7 0xb8 0xb9 0xba 0xbb 0xbc 0xbd 0xbe 0xbf
+     931                 :            :    0xc0 0xc1 0xc2 0xc3 0xc4 0xc5 0xc6 0xc7 0xc8 0xc9 0xca 0xcb 0xcc 0xcd 0xce 0xcf
+     932                 :            :    0xd0 0xd1 0xd2 0xd3 0xd4 0xd5 0xd6 0xd7 0xd8 0xd9 0xda 0xdb 0xdc 0xdd 0xde 0xdf
+     933                 :            :    0xe0 0xe1 0xe2 0xe3 0xe4 0xe5 0xe6 0xe7 0xe8 0xe9 0xea 0xeb 0xec 0xed 0xee 0xef
+     934                 :            :    0xf0 0xf1 0xf2 0xf3 0xf4 0xf5 0xf6 0xf7 0xf8 0xf9 0xfa 0xfb 0xfc 0xfd 0xfe 0xff
+     935                 :            : ))
+
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/index-sort-b.html b/sbtc-ops/smart-contract/coverage/contracts/index-sort-b.html new file mode 100644 index 00000000..6f43ef1d --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/index-sort-b.html @@ -0,0 +1,263 @@ + + + + + + + LCOV - coverage.lcov - contracts + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contractsHitTotalCoverage
Test:coverage.lcovLines:1197457326.2 %
Date:2023-08-18 21:53:02Functions:20279625.4 %
Branches:214127016.9 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Filename Sort by nameLine Coverage Sort by line coverageFunctions Sort by function coverageBranches Sort by branch coverage
token-wbtc.clar +
28.6%28.6%
+
28.6 %12 / 4225.9 %7 / 270.0 %0 / 4
clarity-bitcoin.clar +
0.0%
+
0.0 %0 / 3130.0 %0 / 390.0 %0 / 54
stacking-pool.clar +
1.6%1.6%
+
1.6 %7 / 4371.4 %1 / 730.0 %0 / 99
mining-pool-test.clar +
0.4%0.4%
+
0.4 %3 / 6890.0 %0 / 1030.0 %0 / 209
degen-bridge-testnet-v3.clar +
18.3%18.3%
+
18.3 %64 / 34917.2 %10 / 588.7 %6 / 69
token-amm-swap-pool-v1-1.clar +
16.9%16.9%
+
16.9 %22 / 13020.8 %10 / 4811.5 %3 / 26
alex-vault-v1-1.clar +
26.4%26.4%
+
26.4 %19 / 7236.0 %9 / 2512.5 %4 / 32
token-wstx.clar +
27.3%27.3%
+
27.3 %9 / 3321.7 %5 / 2314.3 %1 / 7
stacking-pool-test.clar +
23.8%23.8%
+
23.8 %103 / 43328.8 %21 / 7316.2 %16 / 99
amm-swap-pool-v1-1.clar +
33.5%33.5%
+
33.5 %188 / 56129.0 %27 / 9316.3 %37 / 227
Wrapped-Bitcoin.clar +
44.8%44.8%
+
44.8 %26 / 5836.8 %7 / 1916.7 %3 / 18
mining-pool-5-blocks.clar +
31.2%31.2%
+
31.2 %215 / 68931.1 %32 / 10316.7 %35 / 209
bridge-contract.clar +
64.1%64.1%
+
64.1 %50 / 7855.6 %5 / 937.5 %3 / 8
mining-pool.clar +
69.5%69.5%
+
69.5 %479 / 68966.0 %68 / 10350.7 %106 / 209
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/index-sort-f.html b/sbtc-ops/smart-contract/coverage/contracts/index-sort-f.html new file mode 100644 index 00000000..d3c9cf0d --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/index-sort-f.html @@ -0,0 +1,263 @@ + + + + + + + LCOV - coverage.lcov - contracts + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contractsHitTotalCoverage
Test:coverage.lcovLines:1197457326.2 %
Date:2023-08-18 21:53:02Functions:20279625.4 %
Branches:214127016.9 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Filename Sort by nameLine Coverage Sort by line coverageFunctions Sort by function coverageBranches Sort by branch coverage
clarity-bitcoin.clar +
0.0%
+
0.0 %0 / 3130.0 %0 / 390.0 %0 / 54
mining-pool-test.clar +
0.4%0.4%
+
0.4 %3 / 6890.0 %0 / 1030.0 %0 / 209
stacking-pool.clar +
1.6%1.6%
+
1.6 %7 / 4371.4 %1 / 730.0 %0 / 99
degen-bridge-testnet-v3.clar +
18.3%18.3%
+
18.3 %64 / 34917.2 %10 / 588.7 %6 / 69
token-amm-swap-pool-v1-1.clar +
16.9%16.9%
+
16.9 %22 / 13020.8 %10 / 4811.5 %3 / 26
token-wstx.clar +
27.3%27.3%
+
27.3 %9 / 3321.7 %5 / 2314.3 %1 / 7
token-wbtc.clar +
28.6%28.6%
+
28.6 %12 / 4225.9 %7 / 270.0 %0 / 4
stacking-pool-test.clar +
23.8%23.8%
+
23.8 %103 / 43328.8 %21 / 7316.2 %16 / 99
amm-swap-pool-v1-1.clar +
33.5%33.5%
+
33.5 %188 / 56129.0 %27 / 9316.3 %37 / 227
mining-pool-5-blocks.clar +
31.2%31.2%
+
31.2 %215 / 68931.1 %32 / 10316.7 %35 / 209
alex-vault-v1-1.clar +
26.4%26.4%
+
26.4 %19 / 7236.0 %9 / 2512.5 %4 / 32
Wrapped-Bitcoin.clar +
44.8%44.8%
+
44.8 %26 / 5836.8 %7 / 1916.7 %3 / 18
bridge-contract.clar +
64.1%64.1%
+
64.1 %50 / 7855.6 %5 / 937.5 %3 / 8
mining-pool.clar +
69.5%69.5%
+
69.5 %479 / 68966.0 %68 / 10350.7 %106 / 209
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/index-sort-l.html b/sbtc-ops/smart-contract/coverage/contracts/index-sort-l.html new file mode 100644 index 00000000..57dfffae --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/index-sort-l.html @@ -0,0 +1,263 @@ + + + + + + + LCOV - coverage.lcov - contracts + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contractsHitTotalCoverage
Test:coverage.lcovLines:1197457326.2 %
Date:2023-08-18 21:53:02Functions:20279625.4 %
Branches:214127016.9 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Filename Sort by nameLine Coverage Sort by line coverageFunctions Sort by function coverageBranches Sort by branch coverage
clarity-bitcoin.clar +
0.0%
+
0.0 %0 / 3130.0 %0 / 390.0 %0 / 54
mining-pool-test.clar +
0.4%0.4%
+
0.4 %3 / 6890.0 %0 / 1030.0 %0 / 209
stacking-pool.clar +
1.6%1.6%
+
1.6 %7 / 4371.4 %1 / 730.0 %0 / 99
token-amm-swap-pool-v1-1.clar +
16.9%16.9%
+
16.9 %22 / 13020.8 %10 / 4811.5 %3 / 26
degen-bridge-testnet-v3.clar +
18.3%18.3%
+
18.3 %64 / 34917.2 %10 / 588.7 %6 / 69
stacking-pool-test.clar +
23.8%23.8%
+
23.8 %103 / 43328.8 %21 / 7316.2 %16 / 99
alex-vault-v1-1.clar +
26.4%26.4%
+
26.4 %19 / 7236.0 %9 / 2512.5 %4 / 32
token-wstx.clar +
27.3%27.3%
+
27.3 %9 / 3321.7 %5 / 2314.3 %1 / 7
token-wbtc.clar +
28.6%28.6%
+
28.6 %12 / 4225.9 %7 / 270.0 %0 / 4
mining-pool-5-blocks.clar +
31.2%31.2%
+
31.2 %215 / 68931.1 %32 / 10316.7 %35 / 209
amm-swap-pool-v1-1.clar +
33.5%33.5%
+
33.5 %188 / 56129.0 %27 / 9316.3 %37 / 227
Wrapped-Bitcoin.clar +
44.8%44.8%
+
44.8 %26 / 5836.8 %7 / 1916.7 %3 / 18
bridge-contract.clar +
64.1%64.1%
+
64.1 %50 / 7855.6 %5 / 937.5 %3 / 8
mining-pool.clar +
69.5%69.5%
+
69.5 %479 / 68966.0 %68 / 10350.7 %106 / 209
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/index.html b/sbtc-ops/smart-contract/coverage/contracts/index.html new file mode 100644 index 00000000..7bad0bc9 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/index.html @@ -0,0 +1,263 @@ + + + + + + + LCOV - coverage.lcov - contracts + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contractsHitTotalCoverage
Test:coverage.lcovLines:1197457326.2 %
Date:2023-08-18 21:53:02Functions:20279625.4 %
Branches:214127016.9 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Filename Sort by nameLine Coverage Sort by line coverageFunctions Sort by function coverageBranches Sort by branch coverage
Wrapped-Bitcoin.clar +
44.8%44.8%
+
44.8 %26 / 5836.8 %7 / 1916.7 %3 / 18
alex-vault-v1-1.clar +
26.4%26.4%
+
26.4 %19 / 7236.0 %9 / 2512.5 %4 / 32
amm-swap-pool-v1-1.clar +
33.5%33.5%
+
33.5 %188 / 56129.0 %27 / 9316.3 %37 / 227
bridge-contract.clar +
64.1%64.1%
+
64.1 %50 / 7855.6 %5 / 937.5 %3 / 8
clarity-bitcoin.clar +
0.0%
+
0.0 %0 / 3130.0 %0 / 390.0 %0 / 54
degen-bridge-testnet-v3.clar +
18.3%18.3%
+
18.3 %64 / 34917.2 %10 / 588.7 %6 / 69
mining-pool-5-blocks.clar +
31.2%31.2%
+
31.2 %215 / 68931.1 %32 / 10316.7 %35 / 209
mining-pool-test.clar +
0.4%0.4%
+
0.4 %3 / 6890.0 %0 / 1030.0 %0 / 209
mining-pool.clar +
69.5%69.5%
+
69.5 %479 / 68966.0 %68 / 10350.7 %106 / 209
stacking-pool-test.clar +
23.8%23.8%
+
23.8 %103 / 43328.8 %21 / 7316.2 %16 / 99
stacking-pool.clar +
1.6%1.6%
+
1.6 %7 / 4371.4 %1 / 730.0 %0 / 99
token-amm-swap-pool-v1-1.clar +
16.9%16.9%
+
16.9 %22 / 13020.8 %10 / 4811.5 %3 / 26
token-wbtc.clar +
28.6%28.6%
+
28.6 %12 / 4225.9 %7 / 270.0 %0 / 4
token-wstx.clar +
27.3%27.3%
+
27.3 %9 / 3321.7 %5 / 2314.3 %1 / 7
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/mining-pool-5-blocks.clar.func-sort-c.html b/sbtc-ops/smart-contract/coverage/contracts/mining-pool-5-blocks.clar.func-sort-c.html new file mode 100644 index 00000000..df9ea948 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/mining-pool-5-blocks.clar.func-sort-c.html @@ -0,0 +1,493 @@ + + + + + + + LCOV - coverage.lcov - contracts/mining-pool-5-blocks.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - mining-pool-5-blocks.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:21568931.2 %
Date:2023-08-18 21:53:02Functions:3210331.1 %
Branches:3520916.7 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
check-is-miner-when-requested-join-tool-fn0
check-is-miner-when-requested-remove0
check-is-pending-now0
clear-votes-map-remove-vote0
compare-votes-number-notifier0
delete-all-notifier-entries0
delete-one-notifier-entry0
deposit-stx0
distribute-reward-each-miner0
end-vote-notifier0
end-vote-notifier-private0
flush-change0
flush-change-each-miner0
flush-distribution-one-miner0
get-address-status0
get-all-data-miners-blocks0
get-all-data-miners-in-pool0
get-all-data-miners-pending-accept0
get-all-data-notifier-voter-miners0
get-all-data-total-withdrawals0
get-auto-exchange0
get-balance0
get-blocks-won0
get-current-block0
get-data-miner-blocks0
get-data-miner-in-pool0
get-data-miner-pending-accept0
get-data-miner-withdrawals0
get-data-notifier-election-process0
get-data-notifier-voter-miner0
get-k0
get-max-voted-notifier0
get-max-votes-notifier0
get-max-votes-number-notifier0
get-miner-btc-address0
get-notifier0
get-notifier-vote-number0
get-notifier-vote-status0
get-proposed-removal-list0
get-remaining-blocks-until-join0
get-reward-at-block0
get-reward-at-block-read0
get-total-rewards-distributed0
has-voted-remove0
is-democratic-vote-accepted-notifier0
is-in-voters-list0
is-principal-in-miners-list0
is-principal-in-pending-accept-list0
is-principal-in-proposed-removal-list0
is-vote-rejected-notifier0
is-vote-rejected-remove0
leave-pool0
process-removal0
quit-proposed-removal-list0
quit-waiting-list0
reject-miner-in-pool0
reject-removal0
remove-map-record-remove-vote0
remove-principal-miners-list0
remove-principal-pending-accept-list0
remove-principal-proposed-removal-list0
reward-distribution0
set-auto-exchange0
set-my-btc-address0
start-vote-notifier0
vote-negative-remove-request0
vote-notifier0
vote-positive-remove-request0
warn-miner0
was-block-claimed0
withdraw-stx0
check-is-proposed-for-removal-now1
get-all-data-miner-proposed-for-removal1
get-all-data-miners-proposed-for-removal1
get-k-at-block-proposed-removal1
get-n-at-block-proposed-removal1
propose-removal1
add-miner-to-pool2
add-pending-miners-to-pool2
get-all-data-waiting-miner2
get-all-data-waiting-miners2
get-miners-list2
get-pending-accept-list2
get-waiting-list2
update-threshold2
x-blocks-passed2
is-vote-rejected-join90
vote-negative-join-request90
get-n-at-block-asked-to-join92
accept-miner-in-pool598
clear-votes-map-join-vote598
is-principal-in-waiting-list598
is-vote-accepted598
remove-map-record-join-vote598
remove-principal-waiting-list598
try-enter-pool598
ask-to-join599
check-is-miner-now600
get-k-at-block-asked-to-join690
vote-positive-join-request807
check-is-miner-when-requested-join897
has-voted-join897
check-is-waiting-now1496
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/mining-pool-5-blocks.clar.func.html b/sbtc-ops/smart-contract/coverage/contracts/mining-pool-5-blocks.clar.func.html new file mode 100644 index 00000000..7c26c793 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/mining-pool-5-blocks.clar.func.html @@ -0,0 +1,493 @@ + + + + + + + LCOV - coverage.lcov - contracts/mining-pool-5-blocks.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - mining-pool-5-blocks.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:21568931.2 %
Date:2023-08-18 21:53:02Functions:3210331.1 %
Branches:3520916.7 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
accept-miner-in-pool598
add-miner-to-pool2
add-pending-miners-to-pool2
ask-to-join599
check-is-miner-now600
check-is-miner-when-requested-join897
check-is-miner-when-requested-join-tool-fn0
check-is-miner-when-requested-remove0
check-is-pending-now0
check-is-proposed-for-removal-now1
check-is-waiting-now1496
clear-votes-map-join-vote598
clear-votes-map-remove-vote0
compare-votes-number-notifier0
delete-all-notifier-entries0
delete-one-notifier-entry0
deposit-stx0
distribute-reward-each-miner0
end-vote-notifier0
end-vote-notifier-private0
flush-change0
flush-change-each-miner0
flush-distribution-one-miner0
get-address-status0
get-all-data-miner-proposed-for-removal1
get-all-data-miners-blocks0
get-all-data-miners-in-pool0
get-all-data-miners-pending-accept0
get-all-data-miners-proposed-for-removal1
get-all-data-notifier-voter-miners0
get-all-data-total-withdrawals0
get-all-data-waiting-miner2
get-all-data-waiting-miners2
get-auto-exchange0
get-balance0
get-blocks-won0
get-current-block0
get-data-miner-blocks0
get-data-miner-in-pool0
get-data-miner-pending-accept0
get-data-miner-withdrawals0
get-data-notifier-election-process0
get-data-notifier-voter-miner0
get-k0
get-k-at-block-asked-to-join690
get-k-at-block-proposed-removal1
get-max-voted-notifier0
get-max-votes-notifier0
get-max-votes-number-notifier0
get-miner-btc-address0
get-miners-list2
get-n-at-block-asked-to-join92
get-n-at-block-proposed-removal1
get-notifier0
get-notifier-vote-number0
get-notifier-vote-status0
get-pending-accept-list2
get-proposed-removal-list0
get-remaining-blocks-until-join0
get-reward-at-block0
get-reward-at-block-read0
get-total-rewards-distributed0
get-waiting-list2
has-voted-join897
has-voted-remove0
is-democratic-vote-accepted-notifier0
is-in-voters-list0
is-principal-in-miners-list0
is-principal-in-pending-accept-list0
is-principal-in-proposed-removal-list0
is-principal-in-waiting-list598
is-vote-accepted598
is-vote-rejected-join90
is-vote-rejected-notifier0
is-vote-rejected-remove0
leave-pool0
process-removal0
propose-removal1
quit-proposed-removal-list0
quit-waiting-list0
reject-miner-in-pool0
reject-removal0
remove-map-record-join-vote598
remove-map-record-remove-vote0
remove-principal-miners-list0
remove-principal-pending-accept-list0
remove-principal-proposed-removal-list0
remove-principal-waiting-list598
reward-distribution0
set-auto-exchange0
set-my-btc-address0
start-vote-notifier0
try-enter-pool598
update-threshold2
vote-negative-join-request90
vote-negative-remove-request0
vote-notifier0
vote-positive-join-request807
vote-positive-remove-request0
warn-miner0
was-block-claimed0
withdraw-stx0
x-blocks-passed2
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/mining-pool-5-blocks.clar.gcov.html b/sbtc-ops/smart-contract/coverage/contracts/mining-pool-5-blocks.clar.gcov.html new file mode 100644 index 00000000..cccd2a0f --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/mining-pool-5-blocks.clar.gcov.html @@ -0,0 +1,1152 @@ + + + + + + + LCOV - coverage.lcov - contracts/mining-pool-5-blocks.clar + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - mining-pool-5-blocks.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:21568931.2 %
Date:2023-08-18 21:53:02Functions:3210331.1 %
Branches:3520916.7 %
+
+ + + + + + + + +

+
           Branch data     Line data    Source code
+
+       1                 :            : (define-constant err-invalid (err u300))
+       2                 :            : (define-constant err-list-length-exceeded (err u101))
+       3                 :            : (define-constant err-already-asked-to-join (err u102))
+       4                 :            : (define-constant err-already-joined (err u103))
+       5                 :            : (define-constant err-not-in-miner-map (err u104))
+       6                 :            : (define-constant err-no-vote-permission (err u105))
+       7                 :            : (define-constant err-more-blocks-to-pass (err u106))
+       8                 :            : (define-constant err-no-pending-miners (err u107))
+       9                 :            : (define-constant err-already-voted (err u108))
+      10                 :            : (define-constant err-not-asked-to-join (err u109))
+      11                 :            : (define-constant err-cant-unwrap-check-miner (err u110))
+      12                 :            : (define-constant err-cant-unwrap-asked-to-join (err u111))
+      13                 :            : (define-constant err-cant-unwrap-block-info (err u112))
+      14                 :            : (define-constant err-currently-notifier (err u113))
+      15                 :            : (define-constant err-not-in-miner-map-miner-to-remove (err u114))
+      16                 :            : (define-constant err-already-proposed-for-removal (err u116))
+      17                 :            : (define-constant err-not-proposed-for-removal (err u117))
+      18                 :            : (define-constant err-cant-remove-when-alone-in-pool (err u118))
+      19                 :            : (define-constant err-cant-vote-himself (err u119))
+      20                 :            : (define-constant err-cant-change-notifier (err u120))
+      21                 :            : (define-constant err-already-proposed-for-notifier (err u121))
+      22                 :            : (define-constant err-not-proposed-for-removal-proposal-block-missing (err u122))
+      23                 :            : (define-constant err-not-proposed-for-notifier-k-missing (err u123))
+      24                 :            : (define-constant err-not-proposed-notifier (err u124))
+      25                 :            : (define-constant err-already-notifier (err u125))
+      26                 :            : (define-constant err-not-in-miner-map-proposed-notifier (err u126))
+      27                 :            : (define-constant err-vote-started-already (err u127))
+      28                 :            : (define-constant err-voting-still-active (err u128))
+      29                 :            : (define-constant err-not-voting-period (err u129))
+      30                 :            : (define-constant err-not-waiting (err u130)) 
+      31                 :            : (define-constant err-not-pending (err u131))
+      32                 :            : (define-constant err-no-join-block-data (err u132))
+      33                 :            : (define-constant err-not-voted (err u133))
+      34                 :            : (define-constant err-only-notifier (err u134))
+      35                 :            : (define-constant err-one-warning-per-block (err u135))
+      36                 :            : (define-constant err-block-height-invalid (err u136))
+      37                 :            : (define-constant err-unwrap-miner-index (err u999))
+      38                 :            : (define-constant err-insufficient-balance (err u1001))
+      39                 :            : (define-constant err-missing-balance (err u1002))
+      40                 :            : (define-constant err-already-distributed (err u1003))
+      41                 :            : (define-constant err-cant-unwrap-rewarded-block (err u1004))
+      42                 :            : 
+      43                 :            : (define-constant notifier-election-blocks-to-pass u144)
+      44                 :            : (define-constant blocks-to-pass u5)
+      45                 :            : 
+      46                 :            : (define-map balance principal uint)
+      47                 :            : (define-map claimed-rewards { block-number: uint } { claimed: bool })
+      48                 :            : (define-map map-is-miner { address: principal } { value: bool })
+      49                 :            : (define-map map-is-waiting { address: principal } { value: bool })
+      50                 :            : (define-map map-is-pending { address: principal } { value: bool })
+      51                 :            : (define-map map-is-proposed-for-removal { address: principal } { value: bool })
+      52                 :            : (define-map map-block-asked-to-join { address: principal } { value: uint })
+      53                 :            : (define-map map-block-proposed-to-remove { address: principal } { value: uint })
+      54                 :            : (define-map map-block-joined { address: principal } { block-height: uint })
+      55                 :            : (define-map map-balance-xBTC { address: principal } { value: uint })
+      56                 :            : (define-map auto-exchange { address: principal } { value: bool })
+      57                 :            : (define-map btc-address { address: principal } { btc-address: {hashbytes: (buff 20), version: (buff 1)} })
+      58                 :            : 
+      59                 :            : (define-map map-votes-accept-join { address: principal } { value: uint })
+      60                 :            : (define-map map-votes-reject-join { address: principal } { value: uint })
+      61                 :            : (define-map map-votes-accept-removal { address: principal } { value: uint })
+      62                 :            : (define-map map-votes-reject-removal { address: principal } { value: uint })
+      63                 :            : (define-map map-join-request-voter { miner-to-vote: principal, voter: principal } { value: bool })
+      64                 :            : (define-map map-remove-request-voter { miner-to-vote: principal, voter: principal } { value: bool })
+      65                 :            : (define-map map-voted-update-notifier { miner-who-voted: principal } { miner-voted: principal })
+      66                 :            : (define-map map-votes-notifier { voted-notifier: principal } { votes-number: uint })
+      67                 :            : (define-map map-blacklist { address: principal } { value: bool })
+      68                 :            : (define-map map-total-withdraw { address: principal } { value: uint })
+      69                 :            : (define-map map-warnings { address: principal } { value: uint })
+      70                 :            : 
+      71                 :            : (define-data-var miners-list-len-at-reward-block uint u0)
+      72                 :            : (define-data-var notifier principal tx-sender)
+      73                 :            : (define-data-var waiting-list (list 300 principal) (list ))
+      74                 :            : (define-data-var miners-list (list 300 principal) (list (var-get notifier)))
+      75                 :            : (define-data-var pending-accept-list (list 300 principal) (list ))
+      76                 :            : (define-data-var proposed-removal-list (list 300 principal) (list ))
+      77                 :            : (define-data-var n uint u1)
+      78                 :            : (define-data-var k-percentage uint u67)
+      79                 :            : (define-data-var k uint u1)
+      80                 :            : (define-data-var k-critical uint u75)
+      81                 :            : (define-data-var waiting-list-miner-to-remove principal tx-sender) ;; use in remove-principal-miners-list
+      82                 :            : (define-data-var pending-accept-list-miner-to-remove principal tx-sender)
+      83                 :            : (define-data-var miners-list-miner-to-remove principal tx-sender)
+      84                 :            : (define-data-var proposed-removal-list-miner-to-remove principal tx-sender)
+      85                 :            : (define-data-var last-join-done uint u1)
+      86                 :            : (define-data-var miner-to-remove-votes-join principal tx-sender)
+      87                 :            : (define-data-var miner-to-remove-votes-remove principal tx-sender)
+      88                 :            : (define-data-var notifier-previous-entries-removed bool true)
+      89                 :            : (define-data-var notifier-vote-active bool false)
+      90                 :            : (define-data-var notifier-vote-start-block uint u0)
+      91                 :            : (define-data-var notifier-vote-end-block uint u0)
+      92                 :            : (define-data-var max-votes-notifier uint u0)
+      93                 :            : (define-data-var max-voted-proposed-notifier principal tx-sender)
+      94                 :            : (define-data-var reward uint u0)
+      95                 :            : (define-data-var total-rewarded uint u0)
+      96                 :            : (define-data-var blocks-won uint u0)
+      97                 :            : (define-data-var reward-change-funds uint u0)
+      98                 :            : (define-data-var temp-distributed-change-funds uint u0)
+      99                 :            : (define-data-var temp-change-after-flushing uint u0)
+     100                 :            : 
+     101                 :         41 : (map-set map-is-miner {address: tx-sender} {value: true})
+     102                 :         41 : (map-set map-block-joined {address: tx-sender} {block-height: block-height})
+     103                 :         41 : (map-set balance tx-sender u0)
+     104                 :            : ;; at new join -> block height - last-join-done >= 100 !
+     105                 :            : 
+     106                 :            : ;; READ ONLY FE UTILS
+     107                 :            : 
+     108                 :            : ;; waiting miners
+     109                 :            : 
+     110                 :            : (define-read-only (get-all-data-waiting-miners (waiting-miners-list (list 100 principal))) 
+     111                 :          2 : (map get-all-data-waiting-miner waiting-miners-list))
+     112                 :            : 
+     113                 :            : (define-private (get-all-data-waiting-miner (miner principal))
+     114                 :          2 : (let ((k-at-block-asked-to-join (unwrap-panic (get-k-at-block-asked-to-join miner)))
+     115                 :          2 :       (n-at-block-asked-to-join (unwrap-panic (get-n-at-block-asked-to-join miner))))
+     116                 :          2 :   (begin 
+     117            [ - ]:          2 :     (asserts! (is-some (get value (map-get? map-is-waiting {address: miner}))) err-not-waiting)
+     118                 :          2 :     (ok 
+     119                 :            :       {
+     120                 :          0 :         pos-votes: 
+     121                 :          2 :           (default-to u0 (get value (map-get? map-votes-accept-join {address: miner}))),
+     122                 :          0 :         pos-thr: 
+     123                 :          2 :           (if 
+     124                 :          2 :             (is-eq k-at-block-asked-to-join u0) 
+     125            [ - ]:          0 :             u1 
+     126            [ + ]:          2 :             k-at-block-asked-to-join),
+     127                 :          0 :         neg-votes:
+     128                 :          2 :           (default-to u0 (get value (map-get? map-votes-reject-join {address: miner}))),
+     129                 :          0 :         neg-thr: 
+     130                 :          2 :           (if   
+     131                 :          2 :             (is-eq n-at-block-asked-to-join u1) 
+     132            [ - ]:          0 :             u1 
+     133            [ + ]:          2 :             (if 
+     134                 :          2 :               (is-eq n-at-block-asked-to-join u2) 
+     135            [ - ]:          0 :               u2 
+     136            [ + ]:          2 :               (+ (- n-at-block-asked-to-join k-at-block-asked-to-join) u1)))}))))
+     137                 :            : 
+     138                 :            : ;; miners proposed for removal
+     139                 :            : 
+     140                 :            : (define-read-only (get-all-data-miners-proposed-for-removal (removal-miners-list (list 100 principal))) 
+     141                 :          1 : (map get-all-data-miner-proposed-for-removal removal-miners-list))
+     142                 :            : 
+     143                 :            : (define-private (get-all-data-miner-proposed-for-removal (miner principal)) 
+     144                 :          1 : (let ((k-at-block-proposed-removal (unwrap-panic (get-k-at-block-proposed-removal miner)))
+     145                 :          1 :       (n-at-block-proposed-removal (unwrap-panic (get-n-at-block-proposed-removal miner))))
+     146                 :          1 :   (begin 
+     147            [ - ]:          1 :     (asserts! (is-some (get value (map-get? map-is-proposed-for-removal {address: miner}))) err-not-pending)
+     148                 :          1 :     (ok 
+     149                 :            :       {
+     150                 :          0 :         vts-for: 
+     151                 :          1 :           (default-to u0 (get value (map-get? map-votes-accept-removal {address: miner}))),
+     152                 :          0 :         pos-thr: 
+     153                 :          1 :           (if 
+     154                 :          1 :             (is-eq k-at-block-proposed-removal u0) 
+     155            [ - ]:          0 :             u1 
+     156            [ + ]:          1 :             k-at-block-proposed-removal),
+     157                 :          0 :         vts-against: 
+     158                 :          1 :           (default-to u0 (get value (map-get? map-votes-reject-removal {address: miner}))),
+     159                 :          0 :         neg-thr: 
+     160                 :          1 :           (if   
+     161                 :          1 :             (is-eq n-at-block-proposed-removal u2) 
+     162            [ - ]:          0 :             u1 
+     163            [ + ]:          1 :             (if (is-eq n-at-block-proposed-removal u3) 
+     164            [ - ]:          0 :               u2 
+     165            [ + ]:          1 :               (+ (- n-at-block-proposed-removal k-at-block-proposed-removal) u1)))}))))
+     166                 :            : 
+     167                 :            : ;; pending accept miners
+     168                 :            : 
+     169                 :            : (define-read-only (get-all-data-miners-pending-accept (pending-miners-list (list 100 principal))) 
+     170                 :          0 : (map get-data-miner-pending-accept pending-miners-list))
+     171                 :            : 
+     172                 :            : (define-private (get-data-miner-pending-accept (miner principal)) 
+     173                 :          0 : (begin 
+     174            [ - ]:          0 :   (asserts! (is-some (get value (map-get? map-is-pending {address: miner}))) err-not-pending)
+     175                 :          0 :   (ok 
+     176                 :            :     {
+     177                 :          0 :       miner: miner,
+     178                 :          0 :       remaining-blocks-until-join: (get-remaining-blocks-until-join)
+     179                 :            :     })))
+     180                 :            : 
+     181                 :            : (define-read-only (get-remaining-blocks-until-join)
+     182                 :          0 :   (if (> blocks-to-pass (- block-height (var-get last-join-done)))
+     183            [ - ]:          0 :     (- blocks-to-pass (- block-height (var-get last-join-done)))
+     184            [ - ]:          0 :     u0
+     185                 :            :   )
+     186                 :            : )
+     187                 :            : 
+     188                 :            : ;; blocks number as miner
+     189                 :            : (define-read-only (get-all-data-miners-blocks (local-miners-list (list 100 principal))) 
+     190                 :          0 : (map get-data-miner-blocks local-miners-list))
+     191                 :            : (define-private (get-data-miner-blocks (miner principal)) 
+     192                 :          0 : (begin 
+     193            [ - ]:          0 :   (asserts! (is-some (get value (map-get? map-is-miner {address: miner}))) err-not-in-miner-map)
+     194            [ - ]:          0 :   (asserts! (is-some (get block-height (map-get? map-block-joined {address: miner}))) err-no-join-block-data)
+     195                 :          0 :   (ok 
+     196                 :            :     {
+     197                 :          0 :       miner: miner,
+     198                 :          0 :       blocks-as-miner: (- block-height (unwrap-panic (get block-height (map-get? map-block-joined {address: miner}))))
+     199                 :            :     })))
+     200                 :            : 
+     201                 :            : ;; miners in pool
+     202                 :            : 
+     203                 :            : (define-read-only (get-all-data-miners-in-pool (local-miners-list (list 100 principal))) 
+     204                 :          0 : (map get-data-miner-in-pool local-miners-list))
+     205                 :            : 
+     206                 :            : (define-private (get-data-miner-in-pool (miner principal)) 
+     207                 :          0 : (begin 
+     208            [ - ]:          0 :   (asserts! (is-some (get value (map-get? map-is-miner {address: miner}))) err-not-in-miner-map)
+     209            [ - ]:          0 :   (asserts! (is-some (get block-height (map-get? map-block-joined {address: miner}))) err-no-join-block-data)
+     210                 :          0 :   (ok 
+     211                 :            :     {
+     212                 :          0 :       blocks-as-miner: (- block-height (unwrap-panic (get block-height (map-get? map-block-joined {address: miner})))),
+     213                 :          0 :       was-blacklist: (default-to false (get value (map-get? map-blacklist {address: miner}))),
+     214                 :          0 :       warnings: (default-to u0 (get value (map-get? map-warnings {address: miner}))),
+     215                 :            :     })))
+     216                 :            : 
+     217                 :            : ;; total withdrawals
+     218                 :            : 
+     219                 :            : (define-read-only (get-all-data-total-withdrawals (local-miners-list (list 100 principal))) 
+     220                 :          0 : (map get-data-miner-withdrawals local-miners-list))
+     221                 :            : 
+     222                 :            : (define-private (get-data-miner-withdrawals (miner principal)) 
+     223                 :          0 : (begin
+     224                 :          0 :   (ok 
+     225                 :          0 :     (default-to u0 (get value (map-get? map-total-withdraw {address: miner})))
+     226                 :            :     )))
+     227                 :            : 
+     228                 :            : ;; notifier
+     229                 :            : 
+     230                 :            : (define-read-only (get-data-notifier-election-process)
+     231                 :            : {
+     232                 :          0 :   vote-status: (var-get notifier-vote-active), 
+     233                 :          0 :   election-blocks-remaining:
+     234                 :          0 :     (if (<= (var-get notifier-vote-end-block) block-height)
+     235            [ - ]:          0 :     u0
+     236            [ - ]:          0 :     (- (var-get notifier-vote-end-block) block-height))})
+     237                 :            : 
+     238                 :            : (define-read-only (get-all-data-notifier-voter-miners (voter-miners-list (list 100 principal)))
+     239                 :          0 : (map get-data-notifier-voter-miner voter-miners-list))
+     240                 :            : 
+     241                 :            : (define-private (get-data-notifier-voter-miner (miner principal)) 
+     242                 :          0 : (begin 
+     243            [ - ]:          0 :   (asserts! (is-some (get miner-voted (map-get? map-voted-update-notifier {miner-who-voted: miner}))) err-not-voted)
+     244                 :          0 :   (ok 
+     245                 :            :     {
+     246                 :          0 :       miner: miner,
+     247                 :          0 :       voted-notifier: 
+     248                 :          0 :       (unwrap-panic (get miner-voted (map-get? map-voted-update-notifier {miner-who-voted: miner}))) 
+     249                 :            :     })))
+     250                 :            : 
+     251                 :            : ;; balances 
+     252                 :            : 
+     253                 :            : (define-read-only (was-block-claimed (given-block-height uint)) 
+     254                 :          0 :   (if 
+     255                 :          0 :     (is-none (get claimed (map-get? claimed-rewards {block-number: given-block-height}))) 
+     256            [ - ]:          0 :     false 
+     257            [ - ]:          0 :     (if 
+     258                 :          0 :       (unwrap-panic (get claimed (map-get? claimed-rewards {block-number: given-block-height}))) 
+     259            [ - ]:          0 :       true 
+     260            [ - ]:          0 :       false)))
+     261                 :            : 
+     262                 :            : ;; BALANCES FLOW
+     263                 :            : 
+     264                 :            : ;; read balance
+     265                 :            : (define-read-only (get-balance (address principal)) 
+     266                 :          0 : (map-get? balance address))
+     267                 :            : 
+     268                 :            : (define-read-only (get-miner-btc-address (miner-address principal))
+     269                 :          0 :   (map-get? btc-address {address: miner-address}))
+     270                 :            : 
+     271                 :            : (define-public (set-my-btc-address (new-btc-address {hashbytes: (buff 20), version: (buff 1)})) 
+     272                 :          0 :   (ok (map-set btc-address {address: tx-sender} {btc-address: new-btc-address})))
+     273                 :            : 
+     274                 :            : ;; deposit funds
+     275                 :            : (define-public (deposit-stx (amount uint))
+     276                 :          0 : (let ((sender tx-sender)
+     277                 :          0 :       (balance-sender (map-get? balance sender)))
+     278                 :          0 :   (try! (stx-transfer? amount tx-sender (as-contract tx-sender)))
+     279                 :          0 :   (if (is-none balance-sender) 
+     280            [ - ]:          0 :     (ok (map-set balance sender amount))
+     281            [ - ]:          0 :     (ok (map-set balance sender (+ (unwrap! balance-sender err-missing-balance) amount))))))
+     282                 :            : 
+     283                 :            : ;; withdraw funds
+     284                 :            : (define-public (withdraw-stx (amount uint)) 
+     285                 :          0 : (let ((receiver tx-sender)) 
+     286            [ - ]:          0 :   (asserts! (>= (unwrap! (map-get? balance receiver) err-missing-balance) amount) err-insufficient-balance)
+     287                 :          0 :   (try! (as-contract (stx-transfer? amount (as-contract tx-sender) receiver)))
+     288                 :          0 :   (if 
+     289                 :          0 :     (is-some (get value (map-get? map-total-withdraw {address: receiver}))) 
+     290            [ - ]:          0 :     (map-set map-total-withdraw {address: receiver} {value: (+ (unwrap-panic (get value (map-get? map-total-withdraw {address: receiver}))) amount)}) 
+     291            [ - ]:          0 :     (map-set map-total-withdraw {address: receiver} {value: amount}))
+     292                 :          0 :   (ok (map-set balance receiver (- (unwrap! (map-get? balance receiver) err-missing-balance) amount)))))
+     293                 :            : 
+     294                 :            : ;; exchange funds
+     295                 :            : (define-public (set-auto-exchange (new-value bool)) 
+     296                 :          0 :   (ok (map-set auto-exchange {address: tx-sender} {value: new-value})))
+     297                 :            : 
+     298                 :            : (define-read-only (get-auto-exchange (address principal)) 
+     299                 :          0 :   (map-get? auto-exchange {address: address}))
+     300                 :            : 
+     301                 :            : (define-public (reward-distribution (block-number uint))
+     302                 :          0 : (begin 
+     303            [ - ]:          0 :   (asserts! (< block-number block-height) err-block-height-invalid) ;; +100  ? 
+     304            [ - ]:          0 :   (asserts! (is-none (get claimed (map-get? claimed-rewards {block-number: block-number}))) err-already-distributed)
+     305                 :          0 :   (let ((miners-list-at-reward-block 
+     306                 :          0 :           (at-block (unwrap! (get-block-info? id-header-hash block-number) err-cant-unwrap-rewarded-block) (var-get miners-list)))
+     307                 :          0 :         (block-reward (get-reward-at-block block-number))
+     308                 :          0 :         (current-reward (var-get reward))
+     309                 :          0 :         (current-miners-list-len (len miners-list-at-reward-block))
+     310                 :          0 :         (distribution-change-funds (mod current-reward current-miners-list-len))
+     311                 :          0 :         (current-change-funds (var-get reward-change-funds)))
+     312                 :          0 :     (map-set claimed-rewards {block-number: block-number} {claimed: true})
+     313                 :          0 :     (var-set miners-list-len-at-reward-block current-miners-list-len) 
+     314                 :          0 :     (var-set reward (unwrap-panic (get reward block-reward)))
+     315                 :          0 :     (var-set total-rewarded (+ (var-get total-rewarded) (var-get reward)))
+     316                 :          0 :     (var-set blocks-won (+ (var-get blocks-won) u1))
+     317                 :          0 :     (var-set reward-change-funds (+ current-change-funds distribution-change-funds))
+     318                 :          0 :     (map distribute-reward-each-miner miners-list-at-reward-block)
+     319                 :          0 :     (ok true))))
+     320                 :            : 
+     321                 :            : (define-private (distribute-reward-each-miner (miner principal)) 
+     322                 :          0 : (let ((miner-balance (default-to u0 (map-get? balance miner)))
+     323                 :          0 :       (current-reward (var-get reward))
+     324                 :          0 :       (current-change-funds (var-get reward-change-funds))
+     325                 :          0 :       (current-miners-list-len (var-get miners-list-len-at-reward-block))
+     326                 :          0 :       (distributed-amount (/ current-reward current-miners-list-len))
+     327                 :            :       ) 
+     328                 :            :   
+     329                 :          0 :   (map-set balance miner 
+     330                 :          0 :     (+ miner-balance distributed-amount))))
+     331                 :            : 
+     332                 :            : (define-private (flush-change-each-miner (miner principal)) 
+     333                 :          0 : (let ((miner-balance (default-to u0 (map-get? balance miner)))
+     334                 :          0 :       (current-reward (var-get reward))
+     335                 :          0 :       (current-change-funds (var-get reward-change-funds))
+     336                 :          0 :       (current-miners-list-len (var-get miners-list-len-at-reward-block))
+     337                 :          0 :       (distributed-amount (/ current-reward current-miners-list-len))
+     338                 :          0 :       (distribution-change-funds (mod current-reward current-miners-list-len))) 
+     339                 :          0 :   (var-set reward-change-funds (+ current-change-funds distribution-change-funds))
+     340                 :          0 :   (map-set balance miner 
+     341                 :          0 :     (+ miner-balance distributed-amount))))
+     342                 :            : 
+     343                 :            : ;; JOINING FLOW
+     344                 :            : 
+     345                 :            : (define-public (ask-to-join (my-btc-address {hashbytes: (buff 20), version: (buff 1)}))
+     346                 :        599 : (begin 
+     347            [ - ]:        599 :   (asserts! (not (check-is-miner-now contract-caller)) err-already-joined) 
+     348            [ - ]:        599 :   (asserts! (not (check-is-waiting-now contract-caller)) err-already-asked-to-join) 
+     349                 :        599 :   (map-set map-block-asked-to-join {address: tx-sender} {value: block-height})
+     350                 :        599 :   (map-set btc-address {address: tx-sender} {btc-address: my-btc-address})
+     351                 :        599 :   (var-set waiting-list (unwrap-panic (as-max-len? (concat (var-get waiting-list) (list tx-sender)) u300)))
+     352                 :        599 :   (map-set map-is-waiting {address: tx-sender} {value: true})
+     353                 :        599 :   (ok true)))
+     354                 :            : 
+     355                 :            : (define-public (vote-positive-join-request (miner-to-vote principal))
+     356                 :        807 : (begin
+     357            [ - ]:        807 :   (asserts! (check-is-waiting-now miner-to-vote) err-not-asked-to-join) ;; map_is_waiting
+     358            [ - ]:        807 :     (asserts! (unwrap! (check-is-miner-when-requested-join miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission)
+     359            [ - ]:        807 :     (asserts! (has-voted-join miner-to-vote) err-already-voted) ;; O(1)
+     360                 :        807 :     (map-set map-join-request-voter 
+     361                 :        807 :       {miner-to-vote: miner-to-vote, voter: tx-sender} 
+     362                 :        807 :       {value: true})
+     363                 :        807 :     (if (is-some (get value (map-get? map-votes-accept-join {address: miner-to-vote}))) 
+     364            [ + ]:        208 :       (map-set map-votes-accept-join {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-accept-join {address: miner-to-vote}))) u1)})
+     365            [ + ]:        599 :       (map-set map-votes-accept-join {address: miner-to-vote} {value: u1}))
+     366                 :        807 :     (ok true)))
+     367                 :            : 
+     368                 :            : (define-public (vote-negative-join-request (miner-to-vote principal))
+     369                 :         90 : (begin
+     370            [ - ]:         90 :   (asserts! (check-is-waiting-now miner-to-vote) err-not-asked-to-join)
+     371            [ - ]:         90 :     (asserts! (unwrap! (check-is-miner-when-requested-join miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission)
+     372            [ - ]:         90 :     (asserts! (has-voted-join miner-to-vote) err-already-voted)    
+     373                 :         90 :     (map-set map-join-request-voter 
+     374                 :         90 :       {miner-to-vote: miner-to-vote, voter: tx-sender} 
+     375                 :         90 :       {value: true})
+     376                 :         90 :     (if (is-some (get value (map-get? map-votes-reject-join {address: miner-to-vote}))) 
+     377            [ + ]:         89 :       (map-set map-votes-reject-join {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-reject-join {address: miner-to-vote}))) u1)})
+     378            [ + ]:          1 :       (map-set map-votes-reject-join {address: miner-to-vote} {value: u1}))  
+     379                 :         90 :     (some
+     380                 :         90 :       (if (is-vote-rejected-join (unwrap-panic (get value (map-get? map-votes-reject-join {address: miner-to-vote}))) (unwrap-panic (get-k-at-block-asked-to-join miner-to-vote)) (unwrap-panic (get-n-at-block-asked-to-join miner-to-vote)))
+     381            [ - ]:          0 :         (reject-miner-in-pool miner-to-vote) 
+     382            [ + ]:         90 :         false))
+     383                 :         90 :     (ok true)))
+     384                 :            : 
+     385                 :            : (define-public (quit-waiting-list) 
+     386                 :          0 :   (begin 
+     387            [ - ]:          0 :     (asserts! (check-is-waiting-now contract-caller) err-not-asked-to-join)
+     388                 :          0 :     (asserts! 
+     389                 :          0 :       (<= 
+     390                 :          0 :         blocks-to-pass
+     391                 :          0 :         (- block-height 
+     392                 :          0 :           (default-to block-height 
+     393                 :          0 :             (get value 
+     394            [ - ]:          0 :               (map-get? map-block-asked-to-join {address: contract-caller}))))) err-more-blocks-to-pass)
+     395                 :          0 :     (let ((remove-result (unwrap-panic (remove-principal-waiting-list contract-caller))))
+     396                 :          0 :       (var-set miner-to-remove-votes-join contract-caller)
+     397                 :          0 :       (var-set waiting-list remove-result)
+     398                 :          0 :       (map-delete map-is-waiting {address: contract-caller})
+     399                 :          0 :       (ok (clear-votes-map-join-vote contract-caller)))))
+     400                 :            : 
+     401                 :            : (define-private (accept-miner-in-pool (miner principal)) 
+     402                 :        598 : (begin 
+     403                 :        598 :   (let ((pending-accept-result (as-max-len? (concat (var-get pending-accept-list) (list miner)) u300)))
+     404            [ - ]:        598 :   (asserts! (is-some pending-accept-result) err-list-length-exceeded) ;; O(1) 
+     405                 :        598 :   (map-set map-warnings {address: miner} {value: u0})
+     406                 :        598 :   (map-set balance miner u0)
+     407                 :        598 :   (var-set miner-to-remove-votes-join miner)
+     408                 :        598 :   (var-set waiting-list (unwrap-panic (as-max-len? (unwrap-panic (remove-principal-waiting-list miner)) u300))) ;; O(N)
+     409                 :        598 :   (map-delete map-is-waiting {address: miner})
+     410                 :        598 :   (map-set map-is-pending {address: miner} {value: true})
+     411                 :        598 :   (clear-votes-map-join-vote miner)
+     412                 :        598 :   (ok (var-set pending-accept-list (unwrap-panic pending-accept-result))))))
+     413                 :            : 
+     414                 :            : (define-private (reject-miner-in-pool (miner principal)) 
+     415                 :          0 : (begin 
+     416                 :          0 :   (let ((remove-result (unwrap-panic (remove-principal-waiting-list miner))))
+     417                 :          0 :     (var-set miner-to-remove-votes-join miner)
+     418                 :          0 :     (var-set waiting-list remove-result)
+     419                 :          0 :     (map-delete map-is-waiting {address: miner})
+     420                 :          0 :     (clear-votes-map-join-vote miner)
+     421                 :          0 :     true)))
+     422                 :            : 
+     423                 :            : (define-private (clear-votes-map-join-vote (miner principal)) 
+     424                 :        598 : (begin 
+     425                 :        598 :   (map-delete map-votes-accept-join {address: (var-get miner-to-remove-votes-join)})
+     426                 :        598 :   (map-delete map-votes-reject-join {address: (var-get miner-to-remove-votes-join)})
+     427                 :        598 :   (map-delete map-block-asked-to-join {address: (var-get miner-to-remove-votes-join)})
+     428                 :        598 :   (map remove-map-record-join-vote (var-get miners-list))))
+     429                 :            : 
+     430                 :            : (define-private (remove-map-record-join-vote (miner principal))
+     431                 :        598 : (if (is-some (map-get? map-join-request-voter {miner-to-vote: (var-get miner-to-remove-votes-join), voter: miner})) 
+     432            [ + ]:        598 :   (map-delete map-join-request-voter {miner-to-vote: (var-get miner-to-remove-votes-join), voter: miner})
+     433            [ - ]:          0 :   false))
+     434                 :            : 
+     435                 :            : (define-private (is-in-voters-list (miner principal) (voters-list (list 300 principal))) 
+     436                 :          0 : (is-some (index-of? voters-list miner)))
+     437                 :            : 
+     438                 :            : (define-private (has-voted-join (miner principal)) 
+     439                 :        897 : (not (if (is-some (get value (map-get? map-join-request-voter {miner-to-vote: miner, voter: tx-sender})))
+     440            [ - ]:          0 :           (unwrap-panic (get value (map-get? map-join-request-voter {miner-to-vote: miner, voter: tx-sender})))
+     441            [ + ]:        897 :           false)))
+     442                 :            : 
+     443                 :            : (define-public (try-enter-pool)
+     444                 :        598 : (begin 
+     445            [ - ]:        598 :   (asserts! (is-some (get value (map-get? map-votes-accept-join {address: tx-sender}))) err-not-asked-to-join)
+     446                 :        598 :   (if (is-vote-accepted (unwrap-panic (get value (map-get? map-votes-accept-join {address: tx-sender}))) (unwrap-panic (get-k-at-block-asked-to-join tx-sender)))
+     447            [ + ]:        598 :     (accept-miner-in-pool tx-sender) 
+     448            [ - ]:          0 :     (ok false))))
+     449                 :            : 
+     450                 :            : (define-public (add-pending-miners-to-pool) 
+     451                 :          2 : (begin
+     452                 :          2 :   (let ((len-pending-accept-list (len (var-get pending-accept-list))))
+     453            [ - ]:          2 :     (asserts! (not (is-eq len-pending-accept-list u0)) err-no-pending-miners)
+     454            [ - ]:          2 :     (asserts! (x-blocks-passed blocks-to-pass) err-more-blocks-to-pass)
+     455                 :          2 :     (map add-miner-to-pool (var-get pending-accept-list))
+     456            [ - ]:          2 :     (asserts! (is-some (as-max-len? (concat (var-get miners-list) (var-get pending-accept-list)) u300)) err-list-length-exceeded)
+     457                 :          2 :     (var-set miners-list (unwrap-panic (as-max-len? (concat (var-get miners-list) (var-get pending-accept-list)) u300)))
+     458                 :          2 :     (var-set n (+ (var-get n) len-pending-accept-list))
+     459                 :          2 :     (var-set pending-accept-list (list ))
+     460                 :          2 :     (var-set last-join-done block-height)
+     461                 :          2 :     (some (update-threshold))
+     462                 :          2 :     (ok true))))
+     463                 :            : 
+     464                 :            : (define-private (update-threshold) 
+     465                 :          2 : (let ((n-now (var-get n))) 
+     466                 :          2 :   (if 
+     467                 :          2 :     (or 
+     468            [ + ]:          2 :       (is-eq n-now u1) 
+     469            [ + ]:          2 :       (is-eq n-now u2)) 
+     470            [ - ]:          0 :     (var-set k u1)
+     471            [ + ]:          2 :     (var-set k (/ (* (var-get k-percentage) (- n-now u1)) u100)))))
+     472                 :            : 
+     473                 :            : (define-private (add-miner-to-pool (miner principal))
+     474                 :        598 : (begin 
+     475                 :        598 :   (map-delete map-is-pending {address: miner})
+     476                 :        598 :   (map-set map-is-miner {address: miner} {value: true})
+     477                 :        598 :   (map-set map-block-joined {address: miner} {block-height: block-height})
+     478                 :        598 :   (ok true)))
+     479                 :            : 
+     480                 :            : (define-private (x-blocks-passed (x uint)) 
+     481                 :          2 : (if (>= (- block-height (var-get last-join-done)) x)
+     482            [ + ]:          2 :   true
+     483            [ - ]:          0 :   false))
+     484                 :            : 
+     485                 :            : (define-private (get-k-at-block-asked-to-join (miner-to-vote principal))
+     486                 :        690 : (let ((block-asked-to-join (get value (map-get? map-block-asked-to-join {address: miner-to-vote}))))
+     487                 :        690 :   (begin 
+     488            [ - ]:        690 :     (asserts! (is-some block-asked-to-join) err-not-asked-to-join)
+     489                 :        690 :     (if 
+     490                 :        690 :       (is-eq 
+     491                 :        690 :         (unwrap-panic block-asked-to-join) 
+     492                 :        690 :         block-height) 
+     493            [ + ]:          1 :       (ok (var-get k)) 
+     494            [ + ]:        689 :       (at-block 
+     495                 :        689 :       (unwrap-panic 
+     496                 :        689 :         (get-block-info? id-header-hash 
+     497                 :        689 :           (unwrap-panic block-asked-to-join))) 
+     498                 :        689 :             (ok (var-get k)))))))
+     499                 :            : 
+     500                 :            : (define-private (get-n-at-block-asked-to-join (miner-to-vote principal)) 
+     501                 :         92 : (let ((block-asked-to-join (get value (map-get? map-block-asked-to-join {address: miner-to-vote}))))
+     502                 :         92 :   (begin 
+     503            [ - ]:         92 :     (asserts! (is-some block-asked-to-join) err-not-asked-to-join)
+     504                 :         92 :     (if 
+     505                 :         92 :       (is-eq 
+     506                 :         92 :         (unwrap-panic block-asked-to-join) block-height) 
+     507            [ + ]:          1 :       (ok (var-get n)) 
+     508            [ + ]:         91 :       (at-block  
+     509                 :         91 :       (unwrap-panic 
+     510                 :         91 :         (get-block-info? id-header-hash 
+     511                 :         91 :           (unwrap-panic block-asked-to-join))) 
+     512                 :         91 :             (ok (var-get n)))))))
+     513                 :            : 
+     514                 :            : ;; LEAVING FLOW
+     515                 :            : 
+     516                 :            : (define-public (leave-pool)
+     517                 :          0 : (begin 
+     518            [ - ]:          0 :   (asserts! (check-is-miner-now contract-caller) err-not-in-miner-map)
+     519            [ - ]:          0 :   (asserts! (not (is-eq (var-get notifier) contract-caller)) err-currently-notifier)
+     520                 :          0 :   (let ((remove-result (unwrap-panic (remove-principal-miners-list tx-sender)))
+     521         [ -  - ]:          0 :         (new-k-percentage (if (> (var-get n) u2) (/ (* (var-get k) u100) (- (var-get n) u2)) u100))) 
+     522                 :            :         ;; if n<=2, set a value for new-k-percentage > k-critical to make sure threshold is updated
+     523                 :          0 :     (some (var-set miners-list remove-result))
+     524                 :          0 :     (var-set n (- (var-get n) u1))
+     525                 :          0 :     (map-set map-is-miner {address: tx-sender} {value: false})
+     526                 :          0 :     (if 
+     527                 :          0 :       (is-some (index-of? (var-get proposed-removal-list) tx-sender)) 
+     528            [ - ]:          0 :       (var-set proposed-removal-list (unwrap-panic (remove-principal-proposed-removal-list tx-sender))) 
+     529            [ - ]:          0 :       true)
+     530                 :          0 :     (if 
+     531                 :          0 :       (>= new-k-percentage (var-get k-critical)) 
+     532            [ - ]:          0 :       (if 
+     533                 :          0 :         (> (var-get n) u1) 
+     534            [ - ]:          0 :         (some (update-threshold)) 
+     535            [ - ]:          0 :         (if 
+     536                 :          0 :           (is-eq (var-get n) u1) 
+     537            [ - ]:          0 :           (some (var-set k u1)) 
+     538            [ - ]:          0 :           (some (var-set k u0))))
+     539            [ - ]:          0 :       none)
+     540                 :          0 :     (ok true))))
+     541                 :            : 
+     542                 :            : ;; REMOVING FLOW
+     543                 :            : 
+     544                 :            : (define-public (propose-removal (miner-to-remove principal))
+     545                 :          1 : (begin 
+     546            [ - ]:          1 :   (asserts! (not (is-eq (var-get n) u1)) err-cant-remove-when-alone-in-pool)
+     547            [ - ]:          1 :   (asserts! (check-is-miner-now contract-caller) err-not-in-miner-map) 
+     548            [ - ]:          1 :   (asserts! (check-is-miner-now miner-to-remove) err-not-in-miner-map-miner-to-remove)
+     549            [ - ]:          1 :   (asserts! (not (check-is-proposed-for-removal-now miner-to-remove)) err-already-proposed-for-removal) 
+     550                 :          1 :   (map-set map-block-proposed-to-remove {address: miner-to-remove} {value: block-height})
+     551                 :          1 :   (map-set map-is-proposed-for-removal {address: miner-to-remove} {value: true})
+     552                 :          1 :   (var-set proposed-removal-list (unwrap! (as-max-len? (concat (var-get proposed-removal-list) (list miner-to-remove )) u300) err-list-length-exceeded))
+     553                 :          1 :   (ok true)))
+     554                 :            : 
+     555                 :            : (define-public (vote-positive-remove-request (miner-to-vote principal))
+     556                 :          0 : (begin
+     557            [ - ]:          0 :   (asserts! (not (is-eq contract-caller miner-to-vote)) err-cant-vote-himself)
+     558            [ - ]:          0 :   (asserts! (check-is-proposed-for-removal-now miner-to-vote) err-not-proposed-for-removal) ;; map_is_proposed_for_removal
+     559            [ - ]:          0 :   (asserts! (is-ok (get-k-at-block-proposed-removal miner-to-vote)) err-not-proposed-for-removal-proposal-block-missing)
+     560            [ - ]:          0 :     (asserts! (unwrap! (check-is-miner-when-requested-remove miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission)
+     561            [ - ]:          0 :     (asserts! (has-voted-remove miner-to-vote) err-already-voted) ;; O(1)
+     562                 :          0 :     (map-set map-remove-request-voter 
+     563                 :          0 :       {miner-to-vote: miner-to-vote, voter: tx-sender} 
+     564                 :          0 :       {value: true})
+     565                 :          0 :     (if (is-some (get value (map-get? map-votes-accept-removal {address: miner-to-vote}))) 
+     566            [ - ]:          0 :       (map-set map-votes-accept-removal {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-accept-removal  {address: miner-to-vote}))) u1)})
+     567            [ - ]:          0 :       (map-set map-votes-accept-removal {address: miner-to-vote} {value: u1}))
+     568                 :          0 :     (some
+     569                 :          0 :       (if (is-vote-accepted (unwrap-panic (get value (map-get? map-votes-accept-removal {address: miner-to-vote}))) (unwrap-panic (get-k-at-block-proposed-removal miner-to-vote)))
+     570            [ - ]:          0 :         (process-removal miner-to-vote)
+     571            [ - ]:          0 :         (ok false)))
+     572                 :          0 :     (ok true)))
+     573                 :            : 
+     574                 :            : (define-public (vote-negative-remove-request (miner-to-vote principal))
+     575                 :          0 : (begin
+     576            [ - ]:          0 :   (asserts! (not (is-eq contract-caller miner-to-vote)) err-cant-vote-himself)
+     577            [ - ]:          0 :   (asserts! (check-is-proposed-for-removal-now miner-to-vote) err-not-proposed-for-removal) ;; map_is_waiting
+     578            [ - ]:          0 :   (asserts! (is-ok (get-k-at-block-proposed-removal miner-to-vote)) err-not-proposed-for-removal-proposal-block-missing)
+     579            [ - ]:          0 :   (asserts! (unwrap! (check-is-miner-when-requested-remove miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission)
+     580            [ - ]:          0 :   (asserts! (has-voted-remove miner-to-vote) err-already-voted) ;; O(1)
+     581                 :          0 :   (map-set map-remove-request-voter 
+     582                 :          0 :     {miner-to-vote: miner-to-vote, voter: tx-sender} 
+     583                 :          0 :     {value: true})
+     584                 :          0 :   (if (is-some (get value (map-get? map-votes-reject-removal {address: miner-to-vote}))) 
+     585            [ - ]:          0 :     (map-set map-votes-reject-removal {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-reject-removal {address: miner-to-vote}))) u1)})
+     586            [ - ]:          0 :     (map-set map-votes-reject-removal {address: miner-to-vote} {value: u1}))
+     587                 :          0 :   (some
+     588                 :          0 :     (if (is-vote-rejected-remove (unwrap-panic (get value (map-get? map-votes-reject-removal {address: miner-to-vote}))) (unwrap-panic (get-k-at-block-proposed-removal miner-to-vote)) (unwrap-panic (get-n-at-block-proposed-removal miner-to-vote)))
+     589            [ - ]:          0 :       (reject-removal miner-to-vote)
+     590            [ - ]:          0 :       (ok false)))
+     591                 :          0 :   (ok true)))
+     592                 :            : 
+     593                 :            : (define-public (quit-proposed-removal-list) 
+     594                 :          0 :   (begin 
+     595                 :          0 :     (asserts! 
+     596                 :          0 :       (<= 
+     597                 :          0 :         blocks-to-pass
+     598                 :          0 :         (- block-height 
+     599                 :          0 :           (default-to block-height 
+     600                 :          0 :             (get value 
+     601            [ - ]:          0 :               (map-get? map-block-proposed-to-remove {address: contract-caller}))))) err-more-blocks-to-pass)
+     602                 :          0 :     (let ((remove-result (unwrap-panic (remove-principal-proposed-removal-list contract-caller))))
+     603                 :          0 :       (var-set miner-to-remove-votes-remove contract-caller)
+     604                 :          0 :       (var-set proposed-removal-list remove-result)
+     605                 :          0 :       (map-delete map-is-proposed-for-removal {address: contract-caller})
+     606                 :          0 :       (ok (clear-votes-map-remove-vote contract-caller)))))
+     607                 :            : 
+     608                 :            : (define-private (process-removal (miner principal))
+     609                 :          0 : (begin 
+     610                 :          0 :   (let ((remove-result (unwrap-panic (remove-principal-miners-list miner)))
+     611         [ -  - ]:          0 :         (new-k-percentage (if (> (var-get n) u2) (/ (* (var-get k) u100) (- (var-get n) u2)) u100)))
+     612                 :          0 :     (some (var-set miners-list remove-result))
+     613                 :          0 :     (var-set miner-to-remove-votes-remove miner)
+     614                 :          0 :     (var-set n (- (var-get n) u1))
+     615                 :          0 :     (map-delete map-is-miner {address: miner})
+     616                 :          0 :     (map-set map-blacklist {address: miner} {value: true})
+     617                 :          0 :     (var-set proposed-removal-list (unwrap-panic (remove-principal-proposed-removal-list miner)))
+     618                 :          0 :     (clear-votes-map-remove-vote miner)
+     619                 :          0 :     (if (>= new-k-percentage (var-get k-critical))
+     620            [ - ]:          0 :       (if 
+     621                 :          0 :         (> (var-get n) u1) 
+     622            [ - ]:          0 :         (update-threshold) 
+     623            [ - ]:          0 :         (if 
+     624                 :          0 :           (is-eq (var-get n) u1) 
+     625            [ - ]:          0 :           (var-set k u1)
+     626            [ - ]:          0 :           (var-set k u0)))
+     627            [ - ]:          0 :       false)
+     628                 :          0 :     (ok true))))
+     629                 :            : 
+     630                 :            : (define-private (reject-removal (miner principal))
+     631                 :          0 : (begin 
+     632                 :          0 :   (var-set miner-to-remove-votes-remove miner)
+     633                 :          0 :   (var-set proposed-removal-list (unwrap-panic (remove-principal-proposed-removal-list miner)))
+     634                 :          0 :   (clear-votes-map-remove-vote miner)
+     635                 :          0 :   (ok true)))
+     636                 :            : 
+     637                 :            : (define-private (has-voted-remove (miner principal)) 
+     638                 :          0 : (not (if (is-some (get value (map-get? map-remove-request-voter {miner-to-vote: miner, voter: tx-sender})))
+     639            [ - ]:          0 :           (unwrap-panic (get value (map-get? map-remove-request-voter {miner-to-vote: miner, voter: tx-sender})))
+     640            [ - ]:          0 :           false
+     641                 :            :   )))
+     642                 :            : 
+     643                 :            : (define-private (clear-votes-map-remove-vote (miner principal)) 
+     644                 :          0 : (begin 
+     645                 :          0 :   (map-delete map-votes-accept-removal {address: (var-get miner-to-remove-votes-remove)})
+     646                 :          0 :   (map-delete map-votes-reject-removal {address: (var-get miner-to-remove-votes-remove)})
+     647                 :          0 :   (map-delete map-block-proposed-to-remove {address: (var-get miner-to-remove-votes-remove)})
+     648                 :          0 :   (map-delete map-is-proposed-for-removal {address: (var-get miner-to-remove-votes-remove)})
+     649                 :          0 :   (map remove-map-record-remove-vote (var-get miners-list))))
+     650                 :            : 
+     651                 :            : (define-private (remove-map-record-remove-vote (miner principal))
+     652                 :          0 : (if (is-some (map-get? map-remove-request-voter {miner-to-vote: (var-get miner-to-remove-votes-remove), voter: miner}))
+     653            [ - ]:          0 :   (map-delete map-remove-request-voter {miner-to-vote: (var-get miner-to-remove-votes-remove), voter: miner})
+     654            [ - ]:          0 :   false))
+     655                 :            : 
+     656                 :            : (define-private (get-k-at-block-proposed-removal (miner-to-vote principal)) 
+     657                 :          1 : (let ((block-proposed-to-remove (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote}))))
+     658                 :          1 :   (begin 
+     659            [ - ]:          1 :     (asserts! (is-some block-proposed-to-remove) err-not-proposed-for-removal)
+     660                 :          1 :     (if 
+     661                 :          1 :       (is-eq (unwrap-panic 
+     662                 :          1 :         block-proposed-to-remove) 
+     663                 :          1 :         block-height) 
+     664            [ + ]:          1 :       (ok (var-get k)) 
+     665            [ - ]:          0 :       (at-block 
+     666                 :          0 :         (unwrap-panic 
+     667                 :          0 :           (get-block-info? id-header-hash 
+     668                 :          0 :             (unwrap-panic block-proposed-to-remove))) 
+     669                 :          0 :               (ok (var-get k)))))))
+     670                 :            : 
+     671                 :            : (define-private (get-n-at-block-proposed-removal (miner-to-vote principal))
+     672                 :          1 : (let ((block-proposed-to-remove (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})))) 
+     673                 :          1 :   (begin 
+     674            [ - ]:          1 :     (asserts! (is-some block-proposed-to-remove) err-not-proposed-for-removal)
+     675                 :          1 :     (if 
+     676                 :          1 :       (is-eq (unwrap-panic 
+     677                 :          1 :         block-proposed-to-remove) 
+     678                 :          1 :         block-height) 
+     679            [ + ]:          1 :       (ok (var-get n)) 
+     680            [ - ]:          0 :       (at-block 
+     681                 :          0 :         (unwrap-panic 
+     682                 :          0 :           (get-block-info? id-header-hash 
+     683                 :          0 :             (unwrap-panic block-proposed-to-remove))) 
+     684                 :          0 :               (ok (var-get n)))))))
+     685                 :            : 
+     686                 :            : ;; UPDATE NOTIFIER
+     687                 :            : 
+     688                 :            : (define-public (start-vote-notifier) 
+     689                 :          0 : (begin 
+     690            [ - ]:          0 :   (asserts! (not (var-get notifier-vote-active)) err-vote-started-already)
+     691                 :          0 :   (var-set notifier-vote-start-block block-height)
+     692                 :          0 :   (var-set notifier-vote-end-block (+ (var-get notifier-vote-start-block) notifier-election-blocks-to-pass))
+     693                 :          0 :   (var-set notifier-vote-active true)
+     694                 :          0 :   (if (var-get notifier-previous-entries-removed) 
+     695            [ - ]:          0 :       (begin 
+     696                 :          0 :         (ok (var-set notifier-previous-entries-removed false))) 
+     697            [ - ]:          0 :       (end-vote-notifier-private))))
+     698                 :            : 
+     699                 :            : (define-public (end-vote-notifier) 
+     700                 :          0 : (begin 
+     701            [ - ]:          0 :   (asserts! (>= block-height (var-get notifier-vote-end-block)) err-voting-still-active)
+     702                 :          0 :   (var-set notifier-vote-active false)
+     703                 :          0 :   (end-vote-notifier-private)))
+     704                 :            : 
+     705                 :            : (define-private (end-vote-notifier-private) 
+     706                 :          0 : (begin 
+     707                 :          0 :   (unwrap! (get-max-votes-number-notifier) (err u99999))
+     708                 :          0 :   (if 
+     709                 :          0 :     (> 
+     710                 :          0 :       (var-get max-votes-notifier) 
+     711                 :          0 :       (/ (var-get k) u2)) 
+     712            [ - ]:          0 :     (var-set notifier (var-get max-voted-proposed-notifier))
+     713            [ - ]:          0 :     false)
+     714                 :          0 :   (delete-all-notifier-entries)
+     715                 :          0 :   (ok true)))
+     716                 :            : 
+     717                 :            : (define-private (get-max-votes-number-notifier) 
+     718                 :          0 : (ok (map compare-votes-number-notifier (var-get miners-list))))
+     719                 :            : 
+     720                 :            : (define-private (compare-votes-number-notifier (proposed-notifier principal)) 
+     721                 :          0 : (ok 
+     722                 :          0 : (if (is-some (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier})))
+     723            [ - ]:          0 :     (if (> (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier}))) (/ (var-get k) u2))
+     724            [ - ]:          0 :       (if 
+     725                 :          0 :         (> (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier}))) (var-get max-votes-notifier)) 
+     726            [ - ]:          0 :         (begin 
+     727                 :          0 :           (var-set max-votes-notifier (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier})))) 
+     728                 :          0 :           (var-set max-voted-proposed-notifier proposed-notifier))
+     729            [ - ]:          0 :         (if 
+     730                 :          0 :           (is-eq (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier}))) (var-get max-votes-notifier)) 
+     731            [ - ]:          0 :           (if 
+     732                 :          0 :             (< 
+     733                 :          0 :               (unwrap-panic (get block-height (map-get? map-block-joined {address: proposed-notifier}))) 
+     734                 :          0 :               (unwrap-panic (get block-height (map-get? map-block-joined {address: (var-get max-voted-proposed-notifier)})))) 
+     735            [ - ]:          0 :             (begin 
+     736                 :          0 :                 (var-set max-voted-proposed-notifier proposed-notifier))
+     737            [ - ]:          0 :             false)
+     738            [ - ]:          0 :         false))
+     739            [ - ]:          0 :       false)
+     740            [ - ]:          0 :     false)))
+     741                 :            : 
+     742                 :            : (define-private (delete-all-notifier-entries) 
+     743                 :          0 : (begin 
+     744                 :          0 :   (var-set max-votes-notifier u0)
+     745                 :          0 :   (var-set max-voted-proposed-notifier (var-get notifier))
+     746                 :          0 :   (map delete-one-notifier-entry (var-get miners-list))
+     747                 :          0 :   (var-set notifier-previous-entries-removed true)))
+     748                 :            : 
+     749                 :            : (define-private (delete-one-notifier-entry (miner principal)) 
+     750                 :          0 : (begin 
+     751                 :          0 :   (map-delete map-voted-update-notifier {miner-who-voted: miner})
+     752                 :          0 :   (map-delete map-votes-notifier {voted-notifier: miner})))
+     753                 :            : 
+     754                 :            : (define-public (vote-notifier (voted-notifier principal)) 
+     755                 :          0 : (begin 
+     756    [ - ][ -  - ]:          0 :   (asserts! (and (is-some (get value (map-get? map-is-miner {address: voted-notifier}))) (unwrap-panic (get value (map-get? map-is-miner {address: voted-notifier})))) err-not-in-miner-map)
+     757    [ - ][ -  - ]:          0 :   (asserts! (and (is-some (get value (map-get? map-is-miner {address: contract-caller}))) (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller})))) err-no-vote-permission)
+     758            [ - ]:          0 :   (asserts! (var-get notifier-vote-active) err-not-voting-period)
+     759            [ - ]:          0 :   (asserts! (not (is-eq contract-caller voted-notifier)) err-cant-vote-himself)
+     760            [ - ]:          0 :   (asserts! (< block-height (var-get notifier-vote-end-block)) err-not-voting-period)
+     761            [ - ]:          0 :   (asserts! (is-none (get miner-voted (map-get? map-voted-update-notifier {miner-who-voted: tx-sender}))) err-already-voted)
+     762                 :          0 :   (map-set map-voted-update-notifier {miner-who-voted: tx-sender} {miner-voted: voted-notifier})
+     763                 :          0 :   (if (is-none (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) 
+     764            [ - ]:          0 :     (map-set map-votes-notifier {voted-notifier: voted-notifier} {votes-number: u1}) 
+     765            [ - ]:          0 :     (map-set map-votes-notifier {voted-notifier: voted-notifier} {votes-number: (+ (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) u1)}))
+     766                 :          0 :   (try! 
+     767                 :          0 :     (if (is-vote-accepted (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) (var-get k)) 
+     768            [ - ]:          0 :     (begin 
+     769                 :          0 :       (var-set notifier voted-notifier)
+     770                 :          0 :       (var-set notifier-vote-end-block block-height)
+     771                 :          0 :       (var-set notifier-vote-active false)
+     772                 :          0 :       (end-vote-notifier))
+     773            [ - ]:          0 :     (ok false)))
+     774                 :          0 : (ok true)))
+     775                 :            : 
+     776                 :            : ;; WARNING FLOW
+     777                 :            : 
+     778                 :            : (define-public (warn-miner (miner principal)) 
+     779                 :          0 : (begin 
+     780                 :          0 : (let ((incremented-value 
+     781                 :          0 :       (if 
+     782                 :          0 :         (is-some (get value (map-get? map-warnings {address: miner}))) 
+     783            [ - ]:          0 :         (+ (unwrap-panic (get value (map-get? map-warnings {address: miner}))) u1) 
+     784            [ - ]:          0 :         u1))) 
+     785            [ - ]:          0 :   (asserts! (is-eq contract-caller (var-get notifier)) err-only-notifier) 
+     786                 :          0 :   (asserts! 
+     787                 :          0 :     (not (and 
+     788            [ - ]:          0 :       (is-none (at-block (unwrap! (get-block-info? id-header-hash (- block-height u1)) err-cant-unwrap-block-info) (get value (map-get? map-warnings {address: miner}))))
+     789            [ - ]:          0 :       (>= incremented-value u2))) 
+     790            [ - ]:          0 :     err-one-warning-per-block)
+     791                 :          0 :   (asserts! 
+     792                 :          0 :     (not 
+     793                 :          0 :       (>= 
+     794                 :          0 :         (- 
+     795                 :          0 :           incremented-value
+     796                 :          0 :           (unwrap! (at-block (unwrap! (get-block-info? id-header-hash (- block-height u1)) err-cant-unwrap-block-info) (get value (map-get? map-warnings {address: miner}))) err-cant-unwrap-block-info)) 
+     797                 :          0 :         u2)) 
+     798            [ - ]:          0 :     err-one-warning-per-block)
+     799                 :          0 :     (ok 
+     800                 :          0 :       (if 
+     801                 :          0 :         (is-some (get value (map-get? map-warnings {address: miner}))) 
+     802            [ - ]:          0 :         (map-set map-warnings {address: miner} {value: (+ (unwrap-panic (get value (map-get? map-warnings {address: miner}))) u1)})
+     803            [ - ]:          0 :         (map-set map-warnings {address: miner} {value: u1}))))))
+     804                 :            : 
+     805                 :            : ;; ELECTION FUNCTIONS
+     806                 :            : 
+     807                 :            : (define-private (is-vote-accepted (votes-number uint) (k-local uint))
+     808                 :        598 : (if 
+     809                 :        598 :   (is-eq k-local u0) ;; k is 0 for n=1, n=2 
+     810            [ - ]:          0 :     (>= votes-number u1) 
+     811            [ + ]:        598 :     (>= votes-number k-local)))
+     812                 :            : 
+     813                 :            : (define-private (is-democratic-vote-accepted-notifier (votes-number uint) (k-local uint))
+     814                 :          0 : (if 
+     815                 :          0 :   (is-eq k-local u0) ;; k is 0 for n=1, n=2 
+     816            [ - ]:          0 :     (>= votes-number u1) 
+     817            [ - ]:          0 :     (>= votes-number (/ k-local u2))))
+     818                 :            : 
+     819                 :            : (define-private (is-vote-rejected-join (votes-number uint) (k-local uint) (n-local uint))
+     820                 :         90 : (if 
+     821                 :         90 :   (is-eq n-local u1) 
+     822            [ - ]:          0 :   (>= votes-number u1) 
+     823            [ + ]:         90 :   (if (is-eq n-local u2) 
+     824            [ - ]:          0 :     (>= votes-number u2) 
+     825            [ + ]:         90 :     (>= votes-number (+ (- n-local k-local) u1)))))
+     826                 :            : 
+     827                 :            : 
+     828                 :            : (define-private (is-vote-rejected-remove (votes-number uint) (k-local uint) (n-local uint))
+     829                 :          0 : (if 
+     830                 :          0 :   (is-eq n-local u2) 
+     831            [ - ]:          0 :   (>= votes-number u1) 
+     832            [ - ]:          0 :   (if (is-eq n-local u3)
+     833            [ - ]:          0 :     (>= votes-number u2)
+     834            [ - ]:          0 :     (>= votes-number (+ (- n-local k-local) u1)))))
+     835                 :            : 
+     836                 :            : (define-private (is-vote-rejected-notifier (votes-number uint) (k-local uint) (n-local uint))
+     837                 :          0 : (if (is-eq n-local u2) 
+     838            [ - ]:          0 :   (>= votes-number u1) 
+     839            [ - ]:          0 :   (if (is-eq n-local u3)
+     840            [ - ]:          0 :     (>= votes-number u2)
+     841            [ - ]:          0 :     (>= votes-number (+ (- n-local k-local) u1)))))
+     842                 :            : 
+     843                 :            : ;; RECOVERING UNASSIGNED FUNDS FUNCTIONS
+     844                 :            : 
+     845                 :            : ;; A public function each miner can call in order to recover the 
+     846                 :            : ;; undistributed rewards caused by dividing rewards to stackers
+     847                 :            : (define-public (flush-change)
+     848                 :          0 : (let ((total-change-funds (var-get reward-change-funds))
+     849                 :          0 :       (current-miners-list-len (len (var-get miners-list)))
+     850                 :          0 :       (distributed-change-funds (/ total-change-funds current-miners-list-len))
+     851                 :          0 :       (change-after-flushing (mod total-change-funds current-miners-list-len))) 
+     852            [ - ]:          0 :   (asserts! (check-is-miner-now contract-caller) err-not-in-miner-map)
+     853                 :          0 :   (var-set temp-distributed-change-funds distributed-change-funds)
+     854                 :          0 :   (var-set temp-change-after-flushing change-after-flushing)
+     855                 :          0 :   (map flush-distribution-one-miner (var-get miners-list))
+     856                 :          0 :   (ok   
+     857                 :          0 :     (var-set reward-change-funds 
+     858                 :          0 :       (- 
+     859                 :          0 :         total-change-funds 
+     860                 :          0 :         (* 
+     861                 :          0 :           current-miners-list-len 
+     862                 :          0 :           distributed-change-funds))))))
+     863                 :            : 
+     864                 :            : (define-private (flush-distribution-one-miner (miner principal))
+     865                 :          0 : (let ((miner-balance (default-to u0 (map-get? balance miner)))
+     866                 :          0 :       (total-change-funds (var-get reward-change-funds))
+     867                 :          0 :       (current-miners-list-len (len (var-get miners-list)))
+     868                 :          0 :       (distributed-change-funds (/ total-change-funds current-miners-list-len)))
+     869                 :          0 :   (map-set balance miner 
+     870                 :          0 :     (+ 
+     871                 :          0 :       miner-balance 
+     872                 :          0 :       (var-get temp-distributed-change-funds)))))
+     873                 :            : 
+     874                 :            : ;; LIST PROCESSING FUNCTIONS
+     875                 :            : 
+     876                 :            : (define-private (remove-principal-waiting-list (miner principal))
+     877                 :        598 : (begin
+     878                 :        598 :     (var-set waiting-list-miner-to-remove miner) 
+     879                 :        598 :     (ok (filter is-principal-in-waiting-list (var-get waiting-list))))) 
+     880                 :            : 
+     881                 :            : (define-private (remove-principal-pending-accept-list (miner principal))
+     882                 :          0 : (begin 
+     883                 :          0 :     (var-set waiting-list-miner-to-remove miner) 
+     884                 :          0 :     (ok (filter is-principal-in-pending-accept-list (var-get pending-accept-list)))))
+     885                 :            : 
+     886                 :            : (define-private (remove-principal-miners-list (miner principal))
+     887                 :          0 : (begin
+     888                 :          0 :   (var-set miners-list-miner-to-remove miner) 
+     889                 :          0 :   (ok (filter is-principal-in-miners-list (var-get miners-list)))))
+     890                 :            : 
+     891                 :            : (define-private (remove-principal-proposed-removal-list (miner principal))
+     892                 :          0 : (begin
+     893                 :          0 :   (var-set proposed-removal-list-miner-to-remove miner) 
+     894                 :          0 :   (ok (filter is-principal-in-proposed-removal-list (var-get proposed-removal-list)))))
+     895                 :            : 
+     896                 :            : ;; MINER STATUS FUNCTIONS
+     897                 :            : 
+     898                 :            : (define-private (check-is-miner-when-requested-join (miner-to-vote principal))
+     899                 :        897 : (ok 
+     900                 :        897 :   (if 
+     901                 :        897 :     (is-some 
+     902                 :        897 :       (if 
+     903                 :        897 :         (is-eq  
+     904                 :        897 :           (unwrap! (get value (map-get? map-block-asked-to-join {address: miner-to-vote})) err-cant-unwrap-asked-to-join) 
+     905                 :        897 :           block-height)
+     906            [ - ]:          0 :         (get value (map-get? map-is-miner {address: contract-caller})) 
+     907            [ + ]:        897 :         (at-block 
+     908                 :        897 :           (unwrap! 
+     909                 :        897 :             (get-block-info? id-header-hash 
+     910                 :        897 :               (unwrap! (get value (map-get? map-block-asked-to-join {address: miner-to-vote})) err-cant-unwrap-asked-to-join)) 
+     911                 :        897 :           err-cant-unwrap-block-info) 
+     912                 :        897 :           (get value (map-get? map-is-miner {address: contract-caller})))))
+     913            [ + ]:        897 :     (if 
+     914                 :        897 :       (is-eq 
+     915                 :        897 :         (unwrap! 
+     916                 :        897 :           (get value (map-get? map-block-asked-to-join {address: miner-to-vote})) 
+     917                 :        897 :         err-cant-unwrap-asked-to-join) 
+     918                 :        897 :         block-height) 
+     919            [ - ]:          0 :       (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller}))) 
+     920            [ + ]:        897 :       (at-block
+     921                 :        897 :         (unwrap! 
+     922                 :        897 :           (get-block-info? id-header-hash 
+     923                 :        897 :             (unwrap-panic (get value (map-get? map-block-asked-to-join {address: miner-to-vote}))))
+     924                 :        897 :         err-cant-unwrap-block-info)
+     925                 :        897 :         (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller})))))
+     926            [ - ]:          0 :   false)))
+     927                 :            : 
+     928                 :            : (define-private (check-is-miner-when-requested-remove (miner-to-vote principal))
+     929                 :          0 : (ok 
+     930                 :          0 :   (if 
+     931                 :          0 :     (is-some 
+     932                 :          0 :       (if 
+     933                 :          0 :         (is-eq  
+     934                 :          0 :           (unwrap! (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})) err-cant-unwrap-asked-to-join) 
+     935                 :          0 :           block-height)
+     936            [ - ]:          0 :         (get value (map-get? map-is-miner {address: contract-caller})) 
+     937            [ - ]:          0 :         (at-block 
+     938                 :          0 :           (unwrap! 
+     939                 :          0 :             (get-block-info? id-header-hash 
+     940                 :          0 :               (unwrap! (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})) err-cant-unwrap-asked-to-join)) 
+     941                 :          0 :           err-cant-unwrap-block-info) 
+     942                 :          0 :           (get value (map-get? map-is-miner {address: contract-caller})))))
+     943            [ - ]:          0 :   (if 
+     944                 :          0 :       (is-eq 
+     945                 :          0 :         (unwrap! 
+     946                 :          0 :           (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})) 
+     947                 :          0 :         err-cant-unwrap-asked-to-join) 
+     948                 :          0 :         block-height) 
+     949            [ - ]:          0 :       (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller}))) 
+     950            [ - ]:          0 :       (at-block
+     951                 :          0 :         (unwrap! 
+     952                 :          0 :           (get-block-info? id-header-hash 
+     953                 :          0 :             (unwrap-panic (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote}))))
+     954                 :          0 :         err-cant-unwrap-block-info)
+     955                 :          0 :         (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller})))))
+     956            [ - ]:          0 :   false)))
+     957                 :            : 
+     958                 :            : (define-read-only (check-is-miner-when-requested-join-tool-fn (miner-to-vote-address principal)) 
+     959                 :          0 : (get value (map-get? map-is-miner {address: contract-caller})))
+     960                 :            : 
+     961                 :            : (define-private (check-is-miner-now (miner principal))
+     962                 :        601 : (if (is-some (get value (map-get? map-is-miner {address: miner})))
+     963            [ + ]:          2 :   (unwrap-panic (get value (map-get? map-is-miner {address: miner})))
+     964            [ + ]:        599 :   false))
+     965                 :            : 
+     966                 :            : (define-private (check-is-proposed-for-removal-now (miner principal))
+     967                 :          1 : (if (is-some (get value (map-get? map-is-proposed-for-removal {address: miner})))
+     968            [ - ]:          0 :   (unwrap-panic (get value (map-get? map-is-proposed-for-removal {address: miner})))
+     969            [ + ]:          1 :   false))
+     970                 :            : 
+     971                 :            : (define-private (check-is-waiting-now (miner principal))
+     972                 :       1496 : (if (is-some (get value (map-get? map-is-waiting {address: miner})))
+     973            [ + ]:        897 :   (unwrap-panic (get value (map-get? map-is-waiting {address: miner})))
+     974            [ + ]:        599 :   false))
+     975                 :            : 
+     976                 :            : (define-private (check-is-pending-now (miner principal))
+     977                 :          0 : (if (is-some (get value (map-get? map-is-pending {address: miner})))
+     978            [ - ]:          0 :   (unwrap-panic (get value (map-get? map-is-pending {address: miner})))
+     979            [ - ]:          0 :   false)
+     980                 :            : )
+     981                 :            : 
+     982                 :            : (define-private (get-reward-at-block (block-number uint)) 
+     983                 :          0 : (begin 
+     984                 :          0 :   {reward: (get-block-info? block-reward block-number), 
+     985                 :          0 :   claimer: (get-block-info? miner-address block-number)}))
+     986                 :            : 
+     987                 :            : (define-read-only (get-reward-at-block-read (block-number uint)) 
+     988                 :          0 : (begin 
+     989                 :          0 :   {reward: (get-block-info? block-reward block-number), 
+     990                 :          0 :   claimer: (get-block-info? miner-address block-number)
+     991                 :            :   }))
+     992                 :            : 
+     993                 :            : (define-read-only (get-address-status (address principal))
+     994                 :          0 : (if (check-is-miner-now address)
+     995            [ - ]:          0 :   (ok "is-miner")
+     996            [ - ]:          0 :   (if (check-is-waiting-now address)
+     997            [ - ]:          0 :     (ok "is-waiting")
+     998            [ - ]:          0 :     (if (check-is-pending-now address)
+     999            [ - ]:          0 :       (ok "is-pending")
+    1000            [ - ]:          0 :       (ok "is-none")
+    1001                 :            :     )
+    1002                 :            :   )
+    1003                 :            : ))
+    1004                 :            : 
+    1005                 :            : ;; READ-ONLY UTILS
+    1006                 :            : 
+    1007                 :            : ;; (define-read-only (check-vote-accepted) ;; to check the vote status inside FE
+    1008                 :            : ;; (is-vote-accepted (unwrap-panic (get value (map-get? map-votes-accept-join {address: tx-sender})))))
+    1009                 :            : 
+    1010                 :            : (define-read-only (get-k) 
+    1011                 :          0 : (var-get k))
+    1012                 :            : 
+    1013                 :            : (define-read-only (get-notifier) 
+    1014                 :          0 : (var-get notifier))
+    1015                 :            : 
+    1016                 :            : (define-read-only (get-blocks-won) 
+    1017                 :          0 : (var-get blocks-won))
+    1018                 :            : 
+    1019                 :            : (define-read-only (get-total-rewards-distributed) 
+    1020                 :          0 : (var-get total-rewarded))
+    1021                 :            : 
+    1022                 :            : (define-read-only (get-waiting-list) 
+    1023                 :          2 : (var-get waiting-list))
+    1024                 :            : 
+    1025                 :            : (define-read-only (get-miners-list) 
+    1026                 :          2 : (var-get miners-list))
+    1027                 :            : 
+    1028                 :            : (define-read-only (get-pending-accept-list) 
+    1029                 :          2 : (var-get pending-accept-list ))
+    1030                 :            : 
+    1031                 :            : (define-read-only (get-proposed-removal-list) 
+    1032                 :          0 : (var-get proposed-removal-list ))
+    1033                 :            : 
+    1034                 :            : (define-read-only (get-notifier-vote-status) 
+    1035                 :          0 : (var-get notifier-vote-active))
+    1036                 :            : 
+    1037                 :            : (define-read-only (get-notifier-vote-number (voted-notifier principal)) 
+    1038                 :          0 : (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier})))
+    1039                 :            : 
+    1040                 :            : (define-read-only (get-max-voted-notifier) 
+    1041                 :          0 : (var-get max-voted-proposed-notifier))
+    1042                 :            : 
+    1043                 :            : (define-read-only (get-max-votes-notifier) 
+    1044                 :          0 : (var-get max-votes-notifier))
+    1045                 :            : 
+    1046                 :            : (define-read-only (get-current-block)
+    1047                 :          0 : (ok block-height))
+    1048                 :            : 
+    1049                 :            : (define-private (is-principal-in-waiting-list (miner principal))
+    1050                 :      89700 : (not (is-eq 
+    1051                 :      89700 :   (var-get waiting-list-miner-to-remove)
+    1052                 :      89700 :   miner)))
+    1053                 :            : 
+    1054                 :            : (define-private (is-principal-in-pending-accept-list (miner principal))
+    1055                 :          0 : (not (is-eq 
+    1056                 :          0 :   (var-get pending-accept-list-miner-to-remove)
+    1057                 :          0 :   miner)))
+    1058                 :            : 
+    1059                 :            : (define-private (is-principal-in-miners-list (miner principal))
+    1060                 :          0 : (not (is-eq  
+    1061                 :          0 :   (var-get miners-list-miner-to-remove) 
+    1062                 :          0 :   miner)))
+    1063                 :            : 
+    1064                 :            : (define-private (is-principal-in-proposed-removal-list (miner principal))
+    1065                 :          0 : (not (is-eq  
+    1066                 :          0 :   (var-get proposed-removal-list-miner-to-remove) 
+    1067                 :          0 :   miner)))
+
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/mining-pool-test.clar.func-sort-c.html b/sbtc-ops/smart-contract/coverage/contracts/mining-pool-test.clar.func-sort-c.html new file mode 100644 index 00000000..901b9bb8 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/mining-pool-test.clar.func-sort-c.html @@ -0,0 +1,493 @@ + + + + + + + LCOV - coverage.lcov - contracts/mining-pool-test.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - mining-pool-test.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:36890.4 %
Date:2023-08-18 21:53:02Functions:01030.0 %
Branches:02090.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
accept-miner-in-pool0
add-miner-to-pool0
add-pending-miners-to-pool0
ask-to-join0
check-is-miner-now0
check-is-miner-when-requested-join0
check-is-miner-when-requested-join-tool-fn0
check-is-miner-when-requested-remove0
check-is-pending-now0
check-is-proposed-for-removal-now0
check-is-waiting-now0
clear-votes-map-join-vote0
clear-votes-map-remove-vote0
compare-votes-number-notifier0
delete-all-notifier-entries0
delete-one-notifier-entry0
deposit-stx0
distribute-reward-each-miner0
end-vote-notifier0
end-vote-notifier-private0
flush-change0
flush-change-each-miner0
flush-distribution-one-miner0
get-address-status0
get-all-data-miner-proposed-for-removal0
get-all-data-miners-blocks0
get-all-data-miners-in-pool0
get-all-data-miners-pending-accept0
get-all-data-miners-proposed-for-removal0
get-all-data-notifier-voter-miners0
get-all-data-total-withdrawals0
get-all-data-waiting-miner0
get-all-data-waiting-miners0
get-auto-exchange0
get-balance0
get-blocks-won0
get-current-block0
get-data-miner-blocks0
get-data-miner-in-pool0
get-data-miner-pending-accept0
get-data-miner-withdrawals0
get-data-notifier-election-process0
get-data-notifier-voter-miner0
get-k0
get-k-at-block-asked-to-join0
get-k-at-block-proposed-removal0
get-max-voted-notifier0
get-max-votes-notifier0
get-max-votes-number-notifier0
get-miner-btc-address0
get-miners-list0
get-n-at-block-asked-to-join0
get-n-at-block-proposed-removal0
get-notifier0
get-notifier-vote-number0
get-notifier-vote-status0
get-pending-accept-list0
get-proposed-removal-list0
get-remaining-blocks-until-join0
get-reward-at-block0
get-reward-at-block-read0
get-total-rewards-distributed0
get-waiting-list0
has-voted-join0
has-voted-remove0
is-democratic-vote-accepted-notifier0
is-in-voters-list0
is-principal-in-miners-list0
is-principal-in-pending-accept-list0
is-principal-in-proposed-removal-list0
is-principal-in-waiting-list0
is-vote-accepted0
is-vote-rejected-join0
is-vote-rejected-notifier0
is-vote-rejected-remove0
leave-pool0
process-removal0
propose-removal0
quit-proposed-removal-list0
quit-waiting-list0
reject-miner-in-pool0
reject-removal0
remove-map-record-join-vote0
remove-map-record-remove-vote0
remove-principal-miners-list0
remove-principal-pending-accept-list0
remove-principal-proposed-removal-list0
remove-principal-waiting-list0
reward-distribution0
set-auto-exchange0
set-my-btc-address0
start-vote-notifier0
try-enter-pool0
update-threshold0
vote-negative-join-request0
vote-negative-remove-request0
vote-notifier0
vote-positive-join-request0
vote-positive-remove-request0
warn-miner0
was-block-claimed0
withdraw-stx0
x-blocks-passed0
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/mining-pool-test.clar.func.html b/sbtc-ops/smart-contract/coverage/contracts/mining-pool-test.clar.func.html new file mode 100644 index 00000000..5a9d6b84 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/mining-pool-test.clar.func.html @@ -0,0 +1,493 @@ + + + + + + + LCOV - coverage.lcov - contracts/mining-pool-test.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - mining-pool-test.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:36890.4 %
Date:2023-08-18 21:53:02Functions:01030.0 %
Branches:02090.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
accept-miner-in-pool0
add-miner-to-pool0
add-pending-miners-to-pool0
ask-to-join0
check-is-miner-now0
check-is-miner-when-requested-join0
check-is-miner-when-requested-join-tool-fn0
check-is-miner-when-requested-remove0
check-is-pending-now0
check-is-proposed-for-removal-now0
check-is-waiting-now0
clear-votes-map-join-vote0
clear-votes-map-remove-vote0
compare-votes-number-notifier0
delete-all-notifier-entries0
delete-one-notifier-entry0
deposit-stx0
distribute-reward-each-miner0
end-vote-notifier0
end-vote-notifier-private0
flush-change0
flush-change-each-miner0
flush-distribution-one-miner0
get-address-status0
get-all-data-miner-proposed-for-removal0
get-all-data-miners-blocks0
get-all-data-miners-in-pool0
get-all-data-miners-pending-accept0
get-all-data-miners-proposed-for-removal0
get-all-data-notifier-voter-miners0
get-all-data-total-withdrawals0
get-all-data-waiting-miner0
get-all-data-waiting-miners0
get-auto-exchange0
get-balance0
get-blocks-won0
get-current-block0
get-data-miner-blocks0
get-data-miner-in-pool0
get-data-miner-pending-accept0
get-data-miner-withdrawals0
get-data-notifier-election-process0
get-data-notifier-voter-miner0
get-k0
get-k-at-block-asked-to-join0
get-k-at-block-proposed-removal0
get-max-voted-notifier0
get-max-votes-notifier0
get-max-votes-number-notifier0
get-miner-btc-address0
get-miners-list0
get-n-at-block-asked-to-join0
get-n-at-block-proposed-removal0
get-notifier0
get-notifier-vote-number0
get-notifier-vote-status0
get-pending-accept-list0
get-proposed-removal-list0
get-remaining-blocks-until-join0
get-reward-at-block0
get-reward-at-block-read0
get-total-rewards-distributed0
get-waiting-list0
has-voted-join0
has-voted-remove0
is-democratic-vote-accepted-notifier0
is-in-voters-list0
is-principal-in-miners-list0
is-principal-in-pending-accept-list0
is-principal-in-proposed-removal-list0
is-principal-in-waiting-list0
is-vote-accepted0
is-vote-rejected-join0
is-vote-rejected-notifier0
is-vote-rejected-remove0
leave-pool0
process-removal0
propose-removal0
quit-proposed-removal-list0
quit-waiting-list0
reject-miner-in-pool0
reject-removal0
remove-map-record-join-vote0
remove-map-record-remove-vote0
remove-principal-miners-list0
remove-principal-pending-accept-list0
remove-principal-proposed-removal-list0
remove-principal-waiting-list0
reward-distribution0
set-auto-exchange0
set-my-btc-address0
start-vote-notifier0
try-enter-pool0
update-threshold0
vote-negative-join-request0
vote-negative-remove-request0
vote-notifier0
vote-positive-join-request0
vote-positive-remove-request0
warn-miner0
was-block-claimed0
withdraw-stx0
x-blocks-passed0
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/mining-pool-test.clar.gcov.html b/sbtc-ops/smart-contract/coverage/contracts/mining-pool-test.clar.gcov.html new file mode 100644 index 00000000..d715c50b --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/mining-pool-test.clar.gcov.html @@ -0,0 +1,1152 @@ + + + + + + + LCOV - coverage.lcov - contracts/mining-pool-test.clar + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - mining-pool-test.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:36890.4 %
Date:2023-08-18 21:53:02Functions:01030.0 %
Branches:02090.0 %
+
+ + + + + + + + +

+
           Branch data     Line data    Source code
+
+       1                 :            : (define-constant err-invalid (err u300))
+       2                 :            : (define-constant err-list-length-exceeded (err u101))
+       3                 :            : (define-constant err-already-asked-to-join (err u102))
+       4                 :            : (define-constant err-already-joined (err u103))
+       5                 :            : (define-constant err-not-in-miner-map (err u104))
+       6                 :            : (define-constant err-no-vote-permission (err u105))
+       7                 :            : (define-constant err-more-blocks-to-pass (err u106))
+       8                 :            : (define-constant err-no-pending-miners (err u107))
+       9                 :            : (define-constant err-already-voted (err u108))
+      10                 :            : (define-constant err-not-asked-to-join (err u109))
+      11                 :            : (define-constant err-cant-unwrap-check-miner (err u110))
+      12                 :            : (define-constant err-cant-unwrap-asked-to-join (err u111))
+      13                 :            : (define-constant err-cant-unwrap-block-info (err u112))
+      14                 :            : (define-constant err-currently-notifier (err u113))
+      15                 :            : (define-constant err-not-in-miner-map-miner-to-remove (err u114))
+      16                 :            : (define-constant err-already-proposed-for-removal (err u116))
+      17                 :            : (define-constant err-not-proposed-for-removal (err u117))
+      18                 :            : (define-constant err-cant-remove-when-alone-in-pool (err u118))
+      19                 :            : (define-constant err-cant-vote-himself (err u119))
+      20                 :            : (define-constant err-cant-change-notifier (err u120))
+      21                 :            : (define-constant err-already-proposed-for-notifier (err u121))
+      22                 :            : (define-constant err-not-proposed-for-removal-proposal-block-missing (err u122))
+      23                 :            : (define-constant err-not-proposed-for-notifier-k-missing (err u123))
+      24                 :            : (define-constant err-not-proposed-notifier (err u124))
+      25                 :            : (define-constant err-already-notifier (err u125))
+      26                 :            : (define-constant err-not-in-miner-map-proposed-notifier (err u126))
+      27                 :            : (define-constant err-vote-started-already (err u127))
+      28                 :            : (define-constant err-voting-still-active (err u128))
+      29                 :            : (define-constant err-not-voting-period (err u129))
+      30                 :            : (define-constant err-not-waiting (err u130)) 
+      31                 :            : (define-constant err-not-pending (err u131))
+      32                 :            : (define-constant err-no-join-block-data (err u132))
+      33                 :            : (define-constant err-not-voted (err u133))
+      34                 :            : (define-constant err-only-notifier (err u134))
+      35                 :            : (define-constant err-one-warning-per-block (err u135))
+      36                 :            : (define-constant err-block-height-invalid (err u136))
+      37                 :            : (define-constant err-unwrap-miner-index (err u999))
+      38                 :            : (define-constant err-insufficient-balance (err u1001))
+      39                 :            : (define-constant err-missing-balance (err u1002))
+      40                 :            : (define-constant err-already-distributed (err u1003))
+      41                 :            : (define-constant err-cant-unwrap-rewarded-block (err u1004))
+      42                 :            : 
+      43                 :            : (define-constant notifier-election-blocks-to-pass u144)
+      44                 :            : (define-constant blocks-to-pass u100)
+      45                 :            : 
+      46                 :            : (define-map balance principal uint)
+      47                 :            : (define-map claimed-rewards { block-number: uint } { claimed: bool })
+      48                 :            : (define-map map-is-miner { address: principal } { value: bool })
+      49                 :            : (define-map map-is-waiting { address: principal } { value: bool })
+      50                 :            : (define-map map-is-pending { address: principal } { value: bool })
+      51                 :            : (define-map map-is-proposed-for-removal { address: principal } { value: bool })
+      52                 :            : (define-map map-block-asked-to-join { address: principal } { value: uint })
+      53                 :            : (define-map map-block-proposed-to-remove { address: principal } { value: uint })
+      54                 :            : (define-map map-block-joined { address: principal } { block-height: uint })
+      55                 :            : (define-map map-balance-xBTC { address: principal } { value: uint })
+      56                 :            : (define-map auto-exchange { address: principal } { value: bool })
+      57                 :            : (define-map btc-address { address: principal } { btc-address: (string-ascii 41) })
+      58                 :            : 
+      59                 :            : (define-map map-votes-accept-join { address: principal } { value: uint })
+      60                 :            : (define-map map-votes-reject-join { address: principal } { value: uint })
+      61                 :            : (define-map map-votes-accept-removal { address: principal } { value: uint })
+      62                 :            : (define-map map-votes-reject-removal { address: principal } { value: uint })
+      63                 :            : (define-map map-join-request-voter { miner-to-vote: principal, voter: principal } { value: bool })
+      64                 :            : (define-map map-remove-request-voter { miner-to-vote: principal, voter: principal } { value: bool })
+      65                 :            : (define-map map-voted-update-notifier { miner-who-voted: principal } { miner-voted: principal })
+      66                 :            : (define-map map-votes-notifier { voted-notifier: principal } { votes-number: uint })
+      67                 :            : (define-map map-blacklist { address: principal } { value: bool })
+      68                 :            : (define-map map-total-withdraw { address: principal } { value: uint })
+      69                 :            : (define-map map-warnings { address: principal } { value: uint })
+      70                 :            : 
+      71                 :            : (define-data-var miners-list-len-at-reward-block uint u0)
+      72                 :            : (define-data-var notifier principal tx-sender)
+      73                 :            : (define-data-var waiting-list (list 300 principal) (list ))
+      74                 :            : (define-data-var miners-list (list 300 principal) (list (var-get notifier)))
+      75                 :            : (define-data-var pending-accept-list (list 300 principal) (list ))
+      76                 :            : (define-data-var proposed-removal-list (list 300 principal) (list ))
+      77                 :            : (define-data-var n uint u1)
+      78                 :            : (define-data-var k-percentage uint u67)
+      79                 :            : (define-data-var k uint u1)
+      80                 :            : (define-data-var k-critical uint u75)
+      81                 :            : (define-data-var waiting-list-miner-to-remove principal tx-sender) ;; use in remove-principal-miners-list
+      82                 :            : (define-data-var pending-accept-list-miner-to-remove principal tx-sender)
+      83                 :            : (define-data-var miners-list-miner-to-remove principal tx-sender)
+      84                 :            : (define-data-var proposed-removal-list-miner-to-remove principal tx-sender)
+      85                 :            : (define-data-var last-join-done uint u1)
+      86                 :            : (define-data-var miner-to-remove-votes-join principal tx-sender)
+      87                 :            : (define-data-var miner-to-remove-votes-remove principal tx-sender)
+      88                 :            : (define-data-var notifier-previous-entries-removed bool true)
+      89                 :            : (define-data-var notifier-vote-active bool false)
+      90                 :            : (define-data-var notifier-vote-start-block uint u0)
+      91                 :            : (define-data-var notifier-vote-end-block uint u0)
+      92                 :            : (define-data-var max-votes-notifier uint u0)
+      93                 :            : (define-data-var max-voted-proposed-notifier principal tx-sender)
+      94                 :            : (define-data-var reward uint u0)
+      95                 :            : (define-data-var total-rewarded uint u0)
+      96                 :            : (define-data-var blocks-won uint u0)
+      97                 :            : (define-data-var reward-change-funds uint u0)
+      98                 :            : (define-data-var temp-distributed-change-funds uint u0)
+      99                 :            : (define-data-var temp-change-after-flushing uint u0)
+     100                 :            : 
+     101                 :         41 : (map-set map-is-miner {address: tx-sender} {value: true})
+     102                 :         41 : (map-set map-block-joined {address: tx-sender} {block-height: block-height})
+     103                 :         41 : (map-set balance tx-sender u0)
+     104                 :            : ;; at new join -> block height - last-join-done >= 100 !
+     105                 :            : 
+     106                 :            : ;; READ ONLY FE UTILS
+     107                 :            : 
+     108                 :            : ;; waiting miners
+     109                 :            : 
+     110                 :            : (define-read-only (get-all-data-waiting-miners (waiting-miners-list (list 100 principal))) 
+     111                 :          0 : (map get-all-data-waiting-miner waiting-miners-list))
+     112                 :            : 
+     113                 :            : (define-private (get-all-data-waiting-miner (miner principal))
+     114                 :          0 : (let ((k-at-block-asked-to-join (unwrap-panic (get-k-at-block-asked-to-join miner)))
+     115                 :          0 :       (n-at-block-asked-to-join (unwrap-panic (get-n-at-block-asked-to-join miner))))
+     116                 :          0 :   (begin 
+     117            [ - ]:          0 :     (asserts! (is-some (get value (map-get? map-is-waiting {address: miner}))) err-not-waiting)
+     118                 :          0 :     (ok 
+     119                 :            :       {
+     120                 :          0 :         pos-votes: 
+     121                 :          0 :           (default-to u0 (get value (map-get? map-votes-accept-join {address: miner}))),
+     122                 :          0 :         pos-thr: 
+     123                 :          0 :           (if 
+     124                 :          0 :             (is-eq k-at-block-asked-to-join u0) 
+     125            [ - ]:          0 :             u1 
+     126            [ - ]:          0 :             k-at-block-asked-to-join),
+     127                 :          0 :         neg-votes:
+     128                 :          0 :           (default-to u0 (get value (map-get? map-votes-reject-join {address: miner}))),
+     129                 :          0 :         neg-thr: 
+     130                 :          0 :           (if   
+     131                 :          0 :             (is-eq n-at-block-asked-to-join u1) 
+     132            [ - ]:          0 :             u1 
+     133            [ - ]:          0 :             (if 
+     134                 :          0 :               (is-eq n-at-block-asked-to-join u2) 
+     135            [ - ]:          0 :               u2 
+     136            [ - ]:          0 :               (+ (- n-at-block-asked-to-join k-at-block-asked-to-join) u1)))}))))
+     137                 :            : 
+     138                 :            : ;; miners proposed for removal
+     139                 :            : 
+     140                 :            : (define-read-only (get-all-data-miners-proposed-for-removal (removal-miners-list (list 100 principal))) 
+     141                 :          0 : (map get-all-data-miner-proposed-for-removal removal-miners-list))
+     142                 :            : 
+     143                 :            : (define-private (get-all-data-miner-proposed-for-removal (miner principal)) 
+     144                 :          0 : (let ((k-at-block-proposed-removal (unwrap-panic (get-k-at-block-proposed-removal miner)))
+     145                 :          0 :       (n-at-block-proposed-removal (unwrap-panic (get-n-at-block-proposed-removal miner))))
+     146                 :          0 :   (begin 
+     147            [ - ]:          0 :     (asserts! (is-some (get value (map-get? map-is-proposed-for-removal {address: miner}))) err-not-pending)
+     148                 :          0 :     (ok 
+     149                 :            :       {
+     150                 :          0 :         vts-for: 
+     151                 :          0 :           (default-to u0 (get value (map-get? map-votes-accept-removal {address: miner}))),
+     152                 :          0 :         pos-thr: 
+     153                 :          0 :           (if 
+     154                 :          0 :             (is-eq k-at-block-proposed-removal u0) 
+     155            [ - ]:          0 :             u1 
+     156            [ - ]:          0 :             k-at-block-proposed-removal),
+     157                 :          0 :         vts-against: 
+     158                 :          0 :           (default-to u0 (get value (map-get? map-votes-reject-removal {address: miner}))),
+     159                 :          0 :         neg-thr: 
+     160                 :          0 :           (if   
+     161                 :          0 :             (is-eq n-at-block-proposed-removal u2) 
+     162            [ - ]:          0 :             u1 
+     163            [ - ]:          0 :             (if (is-eq n-at-block-proposed-removal u3) 
+     164            [ - ]:          0 :               u2 
+     165            [ - ]:          0 :               (+ (- n-at-block-proposed-removal k-at-block-proposed-removal) u1)))}))))
+     166                 :            : 
+     167                 :            : ;; pending accept miners
+     168                 :            : 
+     169                 :            : (define-read-only (get-all-data-miners-pending-accept (pending-miners-list (list 100 principal))) 
+     170                 :          0 : (map get-data-miner-pending-accept pending-miners-list))
+     171                 :            : 
+     172                 :            : (define-private (get-data-miner-pending-accept (miner principal)) 
+     173                 :          0 : (begin 
+     174            [ - ]:          0 :   (asserts! (is-some (get value (map-get? map-is-pending {address: miner}))) err-not-pending)
+     175                 :          0 :   (ok 
+     176                 :            :     {
+     177                 :          0 :       miner: miner,
+     178                 :          0 :       remaining-blocks-until-join: (get-remaining-blocks-until-join)
+     179                 :            :     })))
+     180                 :            : 
+     181                 :            : (define-read-only (get-remaining-blocks-until-join)
+     182                 :          0 :   (if (> blocks-to-pass (- block-height (var-get last-join-done)))
+     183            [ - ]:          0 :     (- blocks-to-pass (- block-height (var-get last-join-done)))
+     184            [ - ]:          0 :     u0
+     185                 :            :   )
+     186                 :            : )
+     187                 :            : 
+     188                 :            : ;; blocks number as miner
+     189                 :            : (define-read-only (get-all-data-miners-blocks (local-miners-list (list 100 principal))) 
+     190                 :          0 : (map get-data-miner-blocks local-miners-list))
+     191                 :            : (define-private (get-data-miner-blocks (miner principal)) 
+     192                 :          0 : (begin 
+     193            [ - ]:          0 :   (asserts! (is-some (get value (map-get? map-is-miner {address: miner}))) err-not-in-miner-map)
+     194            [ - ]:          0 :   (asserts! (is-some (get block-height (map-get? map-block-joined {address: miner}))) err-no-join-block-data)
+     195                 :          0 :   (ok 
+     196                 :            :     {
+     197                 :          0 :       miner: miner,
+     198                 :          0 :       blocks-as-miner: (- block-height (unwrap-panic (get block-height (map-get? map-block-joined {address: miner}))))
+     199                 :            :     })))
+     200                 :            : 
+     201                 :            : ;; miners in pool
+     202                 :            : 
+     203                 :            : (define-read-only (get-all-data-miners-in-pool (local-miners-list (list 100 principal))) 
+     204                 :          0 : (map get-data-miner-in-pool local-miners-list))
+     205                 :            : 
+     206                 :            : (define-private (get-data-miner-in-pool (miner principal)) 
+     207                 :          0 : (begin 
+     208            [ - ]:          0 :   (asserts! (is-some (get value (map-get? map-is-miner {address: miner}))) err-not-in-miner-map)
+     209            [ - ]:          0 :   (asserts! (is-some (get block-height (map-get? map-block-joined {address: miner}))) err-no-join-block-data)
+     210                 :          0 :   (ok 
+     211                 :            :     {
+     212                 :          0 :       blocks-as-miner: (- block-height (unwrap-panic (get block-height (map-get? map-block-joined {address: miner})))),
+     213                 :          0 :       was-blacklist: (default-to false (get value (map-get? map-blacklist {address: miner}))),
+     214                 :          0 :       warnings: (default-to u0 (get value (map-get? map-warnings {address: miner}))),
+     215                 :            :     })))
+     216                 :            : 
+     217                 :            : ;; total withdrawals
+     218                 :            : 
+     219                 :            : (define-read-only (get-all-data-total-withdrawals (local-miners-list (list 100 principal))) 
+     220                 :          0 : (map get-data-miner-withdrawals local-miners-list))
+     221                 :            : 
+     222                 :            : (define-private (get-data-miner-withdrawals (miner principal)) 
+     223                 :          0 : (begin
+     224                 :          0 :   (ok 
+     225                 :          0 :     (default-to u0 (get value (map-get? map-total-withdraw {address: miner})))
+     226                 :            :     )))
+     227                 :            : 
+     228                 :            : ;; notifier
+     229                 :            : 
+     230                 :            : (define-read-only (get-data-notifier-election-process)
+     231                 :            : {
+     232                 :          0 :   vote-status: (var-get notifier-vote-active), 
+     233                 :          0 :   election-blocks-remaining:
+     234                 :          0 :     (if (<= (var-get notifier-vote-end-block) block-height)
+     235            [ - ]:          0 :     u0
+     236            [ - ]:          0 :     (- (var-get notifier-vote-end-block) block-height))})
+     237                 :            : 
+     238                 :            : (define-read-only (get-all-data-notifier-voter-miners (voter-miners-list (list 100 principal)))
+     239                 :          0 : (map get-data-notifier-voter-miner voter-miners-list))
+     240                 :            : 
+     241                 :            : (define-private (get-data-notifier-voter-miner (miner principal)) 
+     242                 :          0 : (begin 
+     243            [ - ]:          0 :   (asserts! (is-some (get miner-voted (map-get? map-voted-update-notifier {miner-who-voted: miner}))) err-not-voted)
+     244                 :          0 :   (ok 
+     245                 :            :     {
+     246                 :          0 :       miner: miner,
+     247                 :          0 :       voted-notifier: 
+     248                 :          0 :       (unwrap-panic (get miner-voted (map-get? map-voted-update-notifier {miner-who-voted: miner}))) 
+     249                 :            :     })))
+     250                 :            : 
+     251                 :            : ;; balances 
+     252                 :            : 
+     253                 :            : (define-read-only (was-block-claimed (given-block-height uint)) 
+     254                 :          0 :   (if 
+     255                 :          0 :     (is-none (get claimed (map-get? claimed-rewards {block-number: given-block-height}))) 
+     256            [ - ]:          0 :     false 
+     257            [ - ]:          0 :     (if 
+     258                 :          0 :       (unwrap-panic (get claimed (map-get? claimed-rewards {block-number: given-block-height}))) 
+     259            [ - ]:          0 :       true 
+     260            [ - ]:          0 :       false)))
+     261                 :            : 
+     262                 :            : ;; BALANCES FLOW
+     263                 :            : 
+     264                 :            : ;; read balance
+     265                 :            : (define-read-only (get-balance (address principal)) 
+     266                 :          0 : (map-get? balance address))
+     267                 :            : 
+     268                 :            : (define-read-only (get-miner-btc-address (miner-address principal))
+     269                 :          0 :   (map-get? btc-address {address: miner-address}))
+     270                 :            : 
+     271                 :            : (define-public (set-my-btc-address (new-btc-address (string-ascii 41))) 
+     272                 :          0 :   (ok (map-set btc-address {address: tx-sender} {btc-address: new-btc-address})))
+     273                 :            : 
+     274                 :            : ;; deposit funds
+     275                 :            : (define-public (deposit-stx (amount uint))
+     276                 :          0 : (let ((sender tx-sender)
+     277                 :          0 :       (balance-sender (map-get? balance sender)))
+     278                 :          0 :   (try! (stx-transfer? amount tx-sender (as-contract tx-sender)))
+     279                 :          0 :   (if (is-none balance-sender) 
+     280            [ - ]:          0 :     (ok (map-set balance sender amount))
+     281            [ - ]:          0 :     (ok (map-set balance sender (+ (unwrap! balance-sender err-missing-balance) amount))))))
+     282                 :            : 
+     283                 :            : ;; withdraw funds
+     284                 :            : (define-public (withdraw-stx (amount uint)) 
+     285                 :          0 : (let ((receiver tx-sender)) 
+     286            [ - ]:          0 :   (asserts! (>= (unwrap! (map-get? balance receiver) err-missing-balance) amount) err-insufficient-balance)
+     287                 :          0 :   (try! (as-contract (stx-transfer? amount (as-contract tx-sender) receiver)))
+     288                 :          0 :   (if 
+     289                 :          0 :     (is-some (get value (map-get? map-total-withdraw {address: receiver}))) 
+     290            [ - ]:          0 :     (map-set map-total-withdraw {address: receiver} {value: (+ (unwrap-panic (get value (map-get? map-total-withdraw {address: receiver}))) amount)}) 
+     291            [ - ]:          0 :     (map-set map-total-withdraw {address: receiver} {value: amount}))
+     292                 :          0 :   (ok (map-set balance receiver (- (unwrap! (map-get? balance receiver) err-missing-balance) amount)))))
+     293                 :            : 
+     294                 :            : ;; exchange funds
+     295                 :            : (define-public (set-auto-exchange (new-value bool)) 
+     296                 :          0 :   (ok (map-set auto-exchange {address: tx-sender} {value: new-value})))
+     297                 :            : 
+     298                 :            : (define-read-only (get-auto-exchange (address principal)) 
+     299                 :          0 :   (map-get? auto-exchange {address: address}))
+     300                 :            : 
+     301                 :            : (define-public (reward-distribution (block-number uint))
+     302                 :          0 : (begin 
+     303            [ - ]:          0 :   (asserts! (< block-number block-height) err-block-height-invalid) ;; +100  ? 
+     304            [ - ]:          0 :   (asserts! (is-none (get claimed (map-get? claimed-rewards {block-number: block-number}))) err-already-distributed)
+     305                 :          0 :   (let ((miners-list-at-reward-block 
+     306                 :          0 :           (at-block (unwrap! (get-block-info? id-header-hash block-number) err-cant-unwrap-rewarded-block) (var-get miners-list)))
+     307                 :          0 :         (block-reward (get-reward-at-block block-number))
+     308                 :          0 :         (current-reward (var-get reward))
+     309                 :          0 :         (current-miners-list-len (len miners-list-at-reward-block))
+     310                 :          0 :         (distribution-change-funds (mod current-reward current-miners-list-len))
+     311                 :          0 :         (current-change-funds (var-get reward-change-funds)))
+     312                 :          0 :     (map-set claimed-rewards {block-number: block-number} {claimed: true})
+     313                 :          0 :     (var-set miners-list-len-at-reward-block current-miners-list-len) 
+     314                 :          0 :     (var-set reward (unwrap-panic (get reward block-reward)))
+     315                 :          0 :     (var-set total-rewarded (+ (var-get total-rewarded) (var-get reward)))
+     316                 :          0 :     (var-set blocks-won (+ (var-get blocks-won) u1))
+     317                 :          0 :     (var-set reward-change-funds (+ current-change-funds distribution-change-funds))
+     318                 :          0 :     (map distribute-reward-each-miner miners-list-at-reward-block)
+     319                 :          0 :     (ok true))))
+     320                 :            : 
+     321                 :            : (define-private (distribute-reward-each-miner (miner principal)) 
+     322                 :          0 : (let ((miner-balance (default-to u0 (map-get? balance miner)))
+     323                 :          0 :       (current-reward (var-get reward))
+     324                 :          0 :       (current-change-funds (var-get reward-change-funds))
+     325                 :          0 :       (current-miners-list-len (var-get miners-list-len-at-reward-block))
+     326                 :          0 :       (distributed-amount (/ current-reward current-miners-list-len))
+     327                 :            :       ) 
+     328                 :            :   
+     329                 :          0 :   (map-set balance miner 
+     330                 :          0 :     (+ miner-balance distributed-amount))))
+     331                 :            : 
+     332                 :            : (define-private (flush-change-each-miner (miner principal)) 
+     333                 :          0 : (let ((miner-balance (default-to u0 (map-get? balance miner)))
+     334                 :          0 :       (current-reward (var-get reward))
+     335                 :          0 :       (current-change-funds (var-get reward-change-funds))
+     336                 :          0 :       (current-miners-list-len (var-get miners-list-len-at-reward-block))
+     337                 :          0 :       (distributed-amount (/ current-reward current-miners-list-len))
+     338                 :          0 :       (distribution-change-funds (mod current-reward current-miners-list-len))) 
+     339                 :          0 :   (var-set reward-change-funds (+ current-change-funds distribution-change-funds))
+     340                 :          0 :   (map-set balance miner 
+     341                 :          0 :     (+ miner-balance distributed-amount))))
+     342                 :            : 
+     343                 :            : ;; JOINING FLOW
+     344                 :            : 
+     345                 :            : (define-public (ask-to-join (my-btc-address (string-ascii 41)))
+     346                 :          0 : (begin 
+     347            [ - ]:          0 :   (asserts! (not (check-is-miner-now contract-caller)) err-already-joined) 
+     348            [ - ]:          0 :   (asserts! (not (check-is-waiting-now contract-caller)) err-already-asked-to-join) 
+     349                 :          0 :   (map-set map-block-asked-to-join {address: tx-sender} {value: block-height})
+     350                 :          0 :   (map-set btc-address {address: tx-sender} {btc-address: my-btc-address})
+     351                 :          0 :   (var-set waiting-list (unwrap-panic (as-max-len? (concat (var-get waiting-list) (list tx-sender)) u300)))
+     352                 :          0 :   (map-set map-is-waiting {address: tx-sender} {value: true})
+     353                 :          0 :   (ok true)))
+     354                 :            : 
+     355                 :            : (define-public (vote-positive-join-request (miner-to-vote principal))
+     356                 :          0 : (begin
+     357            [ - ]:          0 :   (asserts! (check-is-waiting-now miner-to-vote) err-not-asked-to-join) ;; map_is_waiting
+     358            [ - ]:          0 :     (asserts! (unwrap! (check-is-miner-when-requested-join miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission)
+     359            [ - ]:          0 :     (asserts! (has-voted-join miner-to-vote) err-already-voted) ;; O(1)
+     360                 :          0 :     (map-set map-join-request-voter 
+     361                 :          0 :       {miner-to-vote: miner-to-vote, voter: tx-sender} 
+     362                 :          0 :       {value: true})
+     363                 :          0 :     (if (is-some (get value (map-get? map-votes-accept-join {address: miner-to-vote}))) 
+     364            [ - ]:          0 :       (map-set map-votes-accept-join {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-accept-join {address: miner-to-vote}))) u1)})
+     365            [ - ]:          0 :       (map-set map-votes-accept-join {address: miner-to-vote} {value: u1}))
+     366                 :          0 :     (ok true)))
+     367                 :            : 
+     368                 :            : (define-public (vote-negative-join-request (miner-to-vote principal))
+     369                 :          0 : (begin
+     370            [ - ]:          0 :   (asserts! (check-is-waiting-now miner-to-vote) err-not-asked-to-join)
+     371            [ - ]:          0 :     (asserts! (unwrap! (check-is-miner-when-requested-join miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission)
+     372            [ - ]:          0 :     (asserts! (has-voted-join miner-to-vote) err-already-voted)    
+     373                 :          0 :     (map-set map-join-request-voter 
+     374                 :          0 :       {miner-to-vote: miner-to-vote, voter: tx-sender} 
+     375                 :          0 :       {value: true})
+     376                 :          0 :     (if (is-some (get value (map-get? map-votes-reject-join {address: miner-to-vote}))) 
+     377            [ - ]:          0 :       (map-set map-votes-reject-join {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-reject-join {address: miner-to-vote}))) u1)})
+     378            [ - ]:          0 :       (map-set map-votes-reject-join {address: miner-to-vote} {value: u1}))  
+     379                 :          0 :     (some
+     380                 :          0 :       (if (is-vote-rejected-join (unwrap-panic (get value (map-get? map-votes-reject-join {address: miner-to-vote}))) (unwrap-panic (get-k-at-block-asked-to-join miner-to-vote)) (unwrap-panic (get-n-at-block-asked-to-join miner-to-vote)))
+     381            [ - ]:          0 :         (reject-miner-in-pool miner-to-vote) 
+     382            [ - ]:          0 :         false))
+     383                 :          0 :     (ok true)))
+     384                 :            : 
+     385                 :            : (define-public (quit-waiting-list) 
+     386                 :          0 :   (begin 
+     387            [ - ]:          0 :     (asserts! (check-is-waiting-now contract-caller) err-not-asked-to-join)
+     388                 :          0 :     (asserts! 
+     389                 :          0 :       (<= 
+     390                 :          0 :         blocks-to-pass
+     391                 :          0 :         (- block-height 
+     392                 :          0 :           (default-to block-height 
+     393                 :          0 :             (get value 
+     394            [ - ]:          0 :               (map-get? map-block-asked-to-join {address: contract-caller}))))) err-more-blocks-to-pass)
+     395                 :          0 :     (let ((remove-result (unwrap-panic (remove-principal-waiting-list contract-caller))))
+     396                 :          0 :       (var-set miner-to-remove-votes-join contract-caller)
+     397                 :          0 :       (var-set waiting-list remove-result)
+     398                 :          0 :       (map-delete map-is-waiting {address: contract-caller})
+     399                 :          0 :       (ok (clear-votes-map-join-vote contract-caller)))))
+     400                 :            : 
+     401                 :            : (define-private (accept-miner-in-pool (miner principal)) 
+     402                 :          0 : (begin 
+     403                 :          0 :   (let ((pending-accept-result (as-max-len? (concat (var-get pending-accept-list) (list miner)) u300)))
+     404            [ - ]:          0 :   (asserts! (is-some pending-accept-result) err-list-length-exceeded) ;; O(1) 
+     405                 :          0 :   (map-set map-warnings {address: miner} {value: u0})
+     406                 :          0 :   (map-set balance miner u0)
+     407                 :          0 :   (var-set miner-to-remove-votes-join miner)
+     408                 :          0 :   (var-set waiting-list (unwrap-panic (as-max-len? (unwrap-panic (remove-principal-waiting-list miner)) u300))) ;; O(N)
+     409                 :          0 :   (map-delete map-is-waiting {address: miner})
+     410                 :          0 :   (map-set map-is-pending {address: miner} {value: true})
+     411                 :          0 :   (clear-votes-map-join-vote miner)
+     412                 :          0 :   (ok (var-set pending-accept-list (unwrap-panic pending-accept-result))))))
+     413                 :            : 
+     414                 :            : (define-private (reject-miner-in-pool (miner principal)) 
+     415                 :          0 : (begin 
+     416                 :          0 :   (let ((remove-result (unwrap-panic (remove-principal-waiting-list miner))))
+     417                 :          0 :     (var-set miner-to-remove-votes-join miner)
+     418                 :          0 :     (var-set waiting-list remove-result)
+     419                 :          0 :     (map-delete map-is-waiting {address: miner})
+     420                 :          0 :     (clear-votes-map-join-vote miner)
+     421                 :          0 :     true)))
+     422                 :            : 
+     423                 :            : (define-private (clear-votes-map-join-vote (miner principal)) 
+     424                 :          0 : (begin 
+     425                 :          0 :   (map-delete map-votes-accept-join {address: (var-get miner-to-remove-votes-join)})
+     426                 :          0 :   (map-delete map-votes-reject-join {address: (var-get miner-to-remove-votes-join)})
+     427                 :          0 :   (map-delete map-block-asked-to-join {address: (var-get miner-to-remove-votes-join)})
+     428                 :          0 :   (map remove-map-record-join-vote (var-get miners-list))))
+     429                 :            : 
+     430                 :            : (define-private (remove-map-record-join-vote (miner principal))
+     431                 :          0 : (if (is-some (map-get? map-join-request-voter {miner-to-vote: (var-get miner-to-remove-votes-join), voter: miner})) 
+     432            [ - ]:          0 :   (map-delete map-join-request-voter {miner-to-vote: (var-get miner-to-remove-votes-join), voter: miner})
+     433            [ - ]:          0 :   false))
+     434                 :            : 
+     435                 :            : (define-private (is-in-voters-list (miner principal) (voters-list (list 300 principal))) 
+     436                 :          0 : (is-some (index-of? voters-list miner)))
+     437                 :            : 
+     438                 :            : (define-private (has-voted-join (miner principal)) 
+     439                 :          0 : (not (if (is-some (get value (map-get? map-join-request-voter {miner-to-vote: miner, voter: tx-sender})))
+     440            [ - ]:          0 :           (unwrap-panic (get value (map-get? map-join-request-voter {miner-to-vote: miner, voter: tx-sender})))
+     441            [ - ]:          0 :           false)))
+     442                 :            : 
+     443                 :            : (define-public (try-enter-pool)
+     444                 :          0 : (begin 
+     445            [ - ]:          0 :   (asserts! (is-some (get value (map-get? map-votes-accept-join {address: tx-sender}))) err-not-asked-to-join)
+     446                 :          0 :   (if (is-vote-accepted (unwrap-panic (get value (map-get? map-votes-accept-join {address: tx-sender}))) (unwrap-panic (get-k-at-block-asked-to-join tx-sender)))
+     447            [ - ]:          0 :     (accept-miner-in-pool tx-sender) 
+     448            [ - ]:          0 :     (ok false))))
+     449                 :            : 
+     450                 :            : (define-public (add-pending-miners-to-pool) 
+     451                 :          0 : (begin
+     452                 :          0 :   (let ((len-pending-accept-list (len (var-get pending-accept-list))))
+     453            [ - ]:          0 :     (asserts! (not (is-eq len-pending-accept-list u0)) err-no-pending-miners)
+     454            [ - ]:          0 :     (asserts! (x-blocks-passed blocks-to-pass) err-more-blocks-to-pass)
+     455                 :          0 :     (map add-miner-to-pool (var-get pending-accept-list))
+     456            [ - ]:          0 :     (asserts! (is-some (as-max-len? (concat (var-get miners-list) (var-get pending-accept-list)) u300)) err-list-length-exceeded)
+     457                 :          0 :     (var-set miners-list (unwrap-panic (as-max-len? (concat (var-get miners-list) (var-get pending-accept-list)) u300)))
+     458                 :          0 :     (var-set n (+ (var-get n) len-pending-accept-list))
+     459                 :          0 :     (var-set pending-accept-list (list ))
+     460                 :          0 :     (var-set last-join-done block-height)
+     461                 :          0 :     (some (update-threshold))
+     462                 :          0 :     (ok true))))
+     463                 :            : 
+     464                 :            : (define-private (update-threshold) 
+     465                 :          0 : (let ((n-now (var-get n))) 
+     466                 :          0 :   (if 
+     467                 :          0 :     (or 
+     468            [ - ]:          0 :       (is-eq n-now u1) 
+     469            [ - ]:          0 :       (is-eq n-now u2)) 
+     470            [ - ]:          0 :     (var-set k u1)
+     471            [ - ]:          0 :     (var-set k (/ (* (var-get k-percentage) (- n-now u1)) u100)))))
+     472                 :            : 
+     473                 :            : (define-private (add-miner-to-pool (miner principal))
+     474                 :          0 : (begin 
+     475                 :          0 :   (map-delete map-is-pending {address: miner})
+     476                 :          0 :   (map-set map-is-miner {address: miner} {value: true})
+     477                 :          0 :   (map-set map-block-joined {address: miner} {block-height: block-height})
+     478                 :          0 :   (ok true)))
+     479                 :            : 
+     480                 :            : (define-private (x-blocks-passed (x uint)) 
+     481                 :          0 : (if (>= (- block-height (var-get last-join-done)) x)
+     482            [ - ]:          0 :   true
+     483            [ - ]:          0 :   false))
+     484                 :            : 
+     485                 :            : (define-private (get-k-at-block-asked-to-join (miner-to-vote principal))
+     486                 :          0 : (let ((block-asked-to-join (get value (map-get? map-block-asked-to-join {address: miner-to-vote}))))
+     487                 :          0 :   (begin 
+     488            [ - ]:          0 :     (asserts! (is-some block-asked-to-join) err-not-asked-to-join)
+     489                 :          0 :     (if 
+     490                 :          0 :       (is-eq 
+     491                 :          0 :         (unwrap-panic block-asked-to-join) 
+     492                 :          0 :         block-height) 
+     493            [ - ]:          0 :       (ok (var-get k)) 
+     494            [ - ]:          0 :       (at-block 
+     495                 :          0 :       (unwrap-panic 
+     496                 :          0 :         (get-block-info? id-header-hash 
+     497                 :          0 :           (unwrap-panic block-asked-to-join))) 
+     498                 :          0 :             (ok (var-get k)))))))
+     499                 :            : 
+     500                 :            : (define-private (get-n-at-block-asked-to-join (miner-to-vote principal)) 
+     501                 :          0 : (let ((block-asked-to-join (get value (map-get? map-block-asked-to-join {address: miner-to-vote}))))
+     502                 :          0 :   (begin 
+     503            [ - ]:          0 :     (asserts! (is-some block-asked-to-join) err-not-asked-to-join)
+     504                 :          0 :     (if 
+     505                 :          0 :       (is-eq 
+     506                 :          0 :         (unwrap-panic block-asked-to-join) block-height) 
+     507            [ - ]:          0 :       (ok (var-get n)) 
+     508            [ - ]:          0 :       (at-block  
+     509                 :          0 :       (unwrap-panic 
+     510                 :          0 :         (get-block-info? id-header-hash 
+     511                 :          0 :           (unwrap-panic block-asked-to-join))) 
+     512                 :          0 :             (ok (var-get n)))))))
+     513                 :            : 
+     514                 :            : ;; LEAVING FLOW
+     515                 :            : 
+     516                 :            : (define-public (leave-pool)
+     517                 :          0 : (begin 
+     518            [ - ]:          0 :   (asserts! (check-is-miner-now contract-caller) err-not-in-miner-map)
+     519            [ - ]:          0 :   (asserts! (not (is-eq (var-get notifier) contract-caller)) err-currently-notifier)
+     520                 :          0 :   (let ((remove-result (unwrap-panic (remove-principal-miners-list tx-sender)))
+     521         [ -  - ]:          0 :         (new-k-percentage (if (> (var-get n) u2) (/ (* (var-get k) u100) (- (var-get n) u2)) u100))) 
+     522                 :            :         ;; if n<=2, set a value for new-k-percentage > k-critical to make sure threshold is updated
+     523                 :          0 :     (some (var-set miners-list remove-result))
+     524                 :          0 :     (var-set n (- (var-get n) u1))
+     525                 :          0 :     (map-set map-is-miner {address: tx-sender} {value: false})
+     526                 :          0 :     (if 
+     527                 :          0 :       (is-some (index-of? (var-get proposed-removal-list) tx-sender)) 
+     528            [ - ]:          0 :       (var-set proposed-removal-list (unwrap-panic (remove-principal-proposed-removal-list tx-sender))) 
+     529            [ - ]:          0 :       true)
+     530                 :          0 :     (if 
+     531                 :          0 :       (>= new-k-percentage (var-get k-critical)) 
+     532            [ - ]:          0 :       (if 
+     533                 :          0 :         (> (var-get n) u1) 
+     534            [ - ]:          0 :         (some (update-threshold)) 
+     535            [ - ]:          0 :         (if 
+     536                 :          0 :           (is-eq (var-get n) u1) 
+     537            [ - ]:          0 :           (some (var-set k u1)) 
+     538            [ - ]:          0 :           (some (var-set k u0))))
+     539            [ - ]:          0 :       none)
+     540                 :          0 :     (ok true))))
+     541                 :            : 
+     542                 :            : ;; REMOVING FLOW
+     543                 :            : 
+     544                 :            : (define-public (propose-removal (miner-to-remove principal))
+     545                 :          0 : (begin 
+     546            [ - ]:          0 :   (asserts! (not (is-eq (var-get n) u1)) err-cant-remove-when-alone-in-pool)
+     547            [ - ]:          0 :   (asserts! (check-is-miner-now contract-caller) err-not-in-miner-map) 
+     548            [ - ]:          0 :   (asserts! (check-is-miner-now miner-to-remove) err-not-in-miner-map-miner-to-remove)
+     549            [ - ]:          0 :   (asserts! (not (check-is-proposed-for-removal-now miner-to-remove)) err-already-proposed-for-removal) 
+     550                 :          0 :   (map-set map-block-proposed-to-remove {address: miner-to-remove} {value: block-height})
+     551                 :          0 :   (map-set map-is-proposed-for-removal {address: miner-to-remove} {value: true})
+     552                 :          0 :   (var-set proposed-removal-list (unwrap! (as-max-len? (concat (var-get proposed-removal-list) (list miner-to-remove )) u300) err-list-length-exceeded))
+     553                 :          0 :   (ok true)))
+     554                 :            : 
+     555                 :            : (define-public (vote-positive-remove-request (miner-to-vote principal))
+     556                 :          0 : (begin
+     557            [ - ]:          0 :   (asserts! (not (is-eq contract-caller miner-to-vote)) err-cant-vote-himself)
+     558            [ - ]:          0 :   (asserts! (check-is-proposed-for-removal-now miner-to-vote) err-not-proposed-for-removal) ;; map_is_proposed_for_removal
+     559            [ - ]:          0 :   (asserts! (is-ok (get-k-at-block-proposed-removal miner-to-vote)) err-not-proposed-for-removal-proposal-block-missing)
+     560            [ - ]:          0 :     (asserts! (unwrap! (check-is-miner-when-requested-remove miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission)
+     561            [ - ]:          0 :     (asserts! (has-voted-remove miner-to-vote) err-already-voted) ;; O(1)
+     562                 :          0 :     (map-set map-remove-request-voter 
+     563                 :          0 :       {miner-to-vote: miner-to-vote, voter: tx-sender} 
+     564                 :          0 :       {value: true})
+     565                 :          0 :     (if (is-some (get value (map-get? map-votes-accept-removal {address: miner-to-vote}))) 
+     566            [ - ]:          0 :       (map-set map-votes-accept-removal {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-accept-removal  {address: miner-to-vote}))) u1)})
+     567            [ - ]:          0 :       (map-set map-votes-accept-removal {address: miner-to-vote} {value: u1}))
+     568                 :          0 :     (some
+     569                 :          0 :       (if (is-vote-accepted (unwrap-panic (get value (map-get? map-votes-accept-removal {address: miner-to-vote}))) (unwrap-panic (get-k-at-block-proposed-removal miner-to-vote)))
+     570            [ - ]:          0 :         (process-removal miner-to-vote)
+     571            [ - ]:          0 :         (ok false)))
+     572                 :          0 :     (ok true)))
+     573                 :            : 
+     574                 :            : (define-public (vote-negative-remove-request (miner-to-vote principal))
+     575                 :          0 : (begin
+     576            [ - ]:          0 :   (asserts! (not (is-eq contract-caller miner-to-vote)) err-cant-vote-himself)
+     577            [ - ]:          0 :   (asserts! (check-is-proposed-for-removal-now miner-to-vote) err-not-proposed-for-removal) ;; map_is_waiting
+     578            [ - ]:          0 :   (asserts! (is-ok (get-k-at-block-proposed-removal miner-to-vote)) err-not-proposed-for-removal-proposal-block-missing)
+     579            [ - ]:          0 :   (asserts! (unwrap! (check-is-miner-when-requested-remove miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission)
+     580            [ - ]:          0 :   (asserts! (has-voted-remove miner-to-vote) err-already-voted) ;; O(1)
+     581                 :          0 :   (map-set map-remove-request-voter 
+     582                 :          0 :     {miner-to-vote: miner-to-vote, voter: tx-sender} 
+     583                 :          0 :     {value: true})
+     584                 :          0 :   (if (is-some (get value (map-get? map-votes-reject-removal {address: miner-to-vote}))) 
+     585            [ - ]:          0 :     (map-set map-votes-reject-removal {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-reject-removal {address: miner-to-vote}))) u1)})
+     586            [ - ]:          0 :     (map-set map-votes-reject-removal {address: miner-to-vote} {value: u1}))
+     587                 :          0 :   (some
+     588                 :          0 :     (if (is-vote-rejected-remove (unwrap-panic (get value (map-get? map-votes-reject-removal {address: miner-to-vote}))) (unwrap-panic (get-k-at-block-proposed-removal miner-to-vote)) (unwrap-panic (get-n-at-block-proposed-removal miner-to-vote)))
+     589            [ - ]:          0 :       (reject-removal miner-to-vote)
+     590            [ - ]:          0 :       (ok false)))
+     591                 :          0 :   (ok true)))
+     592                 :            : 
+     593                 :            : (define-public (quit-proposed-removal-list) 
+     594                 :          0 :   (begin 
+     595                 :          0 :     (asserts! 
+     596                 :          0 :       (<= 
+     597                 :          0 :         blocks-to-pass
+     598                 :          0 :         (- block-height 
+     599                 :          0 :           (default-to block-height 
+     600                 :          0 :             (get value 
+     601            [ - ]:          0 :               (map-get? map-block-proposed-to-remove {address: contract-caller}))))) err-more-blocks-to-pass)
+     602                 :          0 :     (let ((remove-result (unwrap-panic (remove-principal-proposed-removal-list contract-caller))))
+     603                 :          0 :       (var-set miner-to-remove-votes-remove contract-caller)
+     604                 :          0 :       (var-set proposed-removal-list remove-result)
+     605                 :          0 :       (map-delete map-is-proposed-for-removal {address: contract-caller})
+     606                 :          0 :       (ok (clear-votes-map-remove-vote contract-caller)))))
+     607                 :            : 
+     608                 :            : (define-private (process-removal (miner principal))
+     609                 :          0 : (begin 
+     610                 :          0 :   (let ((remove-result (unwrap-panic (remove-principal-miners-list miner)))
+     611         [ -  - ]:          0 :         (new-k-percentage (if (> (var-get n) u2) (/ (* (var-get k) u100) (- (var-get n) u2)) u100)))
+     612                 :          0 :     (some (var-set miners-list remove-result))
+     613                 :          0 :     (var-set miner-to-remove-votes-remove miner)
+     614                 :          0 :     (var-set n (- (var-get n) u1))
+     615                 :          0 :     (map-delete map-is-miner {address: miner})
+     616                 :          0 :     (map-set map-blacklist {address: miner} {value: true})
+     617                 :          0 :     (var-set proposed-removal-list (unwrap-panic (remove-principal-proposed-removal-list miner)))
+     618                 :          0 :     (clear-votes-map-remove-vote miner)
+     619                 :          0 :     (if (>= new-k-percentage (var-get k-critical))
+     620            [ - ]:          0 :       (if 
+     621                 :          0 :         (> (var-get n) u1) 
+     622            [ - ]:          0 :         (update-threshold) 
+     623            [ - ]:          0 :         (if 
+     624                 :          0 :           (is-eq (var-get n) u1) 
+     625            [ - ]:          0 :           (var-set k u1)
+     626            [ - ]:          0 :           (var-set k u0)))
+     627            [ - ]:          0 :       false)
+     628                 :          0 :     (ok true))))
+     629                 :            : 
+     630                 :            : (define-private (reject-removal (miner principal))
+     631                 :          0 : (begin 
+     632                 :          0 :   (var-set miner-to-remove-votes-remove miner)
+     633                 :          0 :   (var-set proposed-removal-list (unwrap-panic (remove-principal-proposed-removal-list miner)))
+     634                 :          0 :   (clear-votes-map-remove-vote miner)
+     635                 :          0 :   (ok true)))
+     636                 :            : 
+     637                 :            : (define-private (has-voted-remove (miner principal)) 
+     638                 :          0 : (not (if (is-some (get value (map-get? map-remove-request-voter {miner-to-vote: miner, voter: tx-sender})))
+     639            [ - ]:          0 :           (unwrap-panic (get value (map-get? map-remove-request-voter {miner-to-vote: miner, voter: tx-sender})))
+     640            [ - ]:          0 :           false
+     641                 :            :   )))
+     642                 :            : 
+     643                 :            : (define-private (clear-votes-map-remove-vote (miner principal)) 
+     644                 :          0 : (begin 
+     645                 :          0 :   (map-delete map-votes-accept-removal {address: (var-get miner-to-remove-votes-remove)})
+     646                 :          0 :   (map-delete map-votes-reject-removal {address: (var-get miner-to-remove-votes-remove)})
+     647                 :          0 :   (map-delete map-block-proposed-to-remove {address: (var-get miner-to-remove-votes-remove)})
+     648                 :          0 :   (map-delete map-is-proposed-for-removal {address: (var-get miner-to-remove-votes-remove)})
+     649                 :          0 :   (map remove-map-record-remove-vote (var-get miners-list))))
+     650                 :            : 
+     651                 :            : (define-private (remove-map-record-remove-vote (miner principal))
+     652                 :          0 : (if (is-some (map-get? map-remove-request-voter {miner-to-vote: (var-get miner-to-remove-votes-remove), voter: miner}))
+     653            [ - ]:          0 :   (map-delete map-remove-request-voter {miner-to-vote: (var-get miner-to-remove-votes-remove), voter: miner})
+     654            [ - ]:          0 :   false))
+     655                 :            : 
+     656                 :            : (define-private (get-k-at-block-proposed-removal (miner-to-vote principal)) 
+     657                 :          0 : (let ((block-proposed-to-remove (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote}))))
+     658                 :          0 :   (begin 
+     659            [ - ]:          0 :     (asserts! (is-some block-proposed-to-remove) err-not-proposed-for-removal)
+     660                 :          0 :     (if 
+     661                 :          0 :       (is-eq (unwrap-panic 
+     662                 :          0 :         block-proposed-to-remove) 
+     663                 :          0 :         block-height) 
+     664            [ - ]:          0 :       (ok (var-get k)) 
+     665            [ - ]:          0 :       (at-block 
+     666                 :          0 :         (unwrap-panic 
+     667                 :          0 :           (get-block-info? id-header-hash 
+     668                 :          0 :             (unwrap-panic block-proposed-to-remove))) 
+     669                 :          0 :               (ok (var-get k)))))))
+     670                 :            : 
+     671                 :            : (define-private (get-n-at-block-proposed-removal (miner-to-vote principal))
+     672                 :          0 : (let ((block-proposed-to-remove (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})))) 
+     673                 :          0 :   (begin 
+     674            [ - ]:          0 :     (asserts! (is-some block-proposed-to-remove) err-not-proposed-for-removal)
+     675                 :          0 :     (if 
+     676                 :          0 :       (is-eq (unwrap-panic 
+     677                 :          0 :         block-proposed-to-remove) 
+     678                 :          0 :         block-height) 
+     679            [ - ]:          0 :       (ok (var-get n)) 
+     680            [ - ]:          0 :       (at-block 
+     681                 :          0 :         (unwrap-panic 
+     682                 :          0 :           (get-block-info? id-header-hash 
+     683                 :          0 :             (unwrap-panic block-proposed-to-remove))) 
+     684                 :          0 :               (ok (var-get n)))))))
+     685                 :            : 
+     686                 :            : ;; UPDATE NOTIFIER
+     687                 :            : 
+     688                 :            : (define-public (start-vote-notifier) 
+     689                 :          0 : (begin 
+     690            [ - ]:          0 :   (asserts! (not (var-get notifier-vote-active)) err-vote-started-already)
+     691                 :          0 :   (var-set notifier-vote-start-block block-height)
+     692                 :          0 :   (var-set notifier-vote-end-block (+ (var-get notifier-vote-start-block) notifier-election-blocks-to-pass))
+     693                 :          0 :   (var-set notifier-vote-active true)
+     694                 :          0 :   (if (var-get notifier-previous-entries-removed) 
+     695            [ - ]:          0 :       (begin 
+     696                 :          0 :         (ok (var-set notifier-previous-entries-removed false))) 
+     697            [ - ]:          0 :       (end-vote-notifier-private))))
+     698                 :            : 
+     699                 :            : (define-public (end-vote-notifier) 
+     700                 :          0 : (begin 
+     701            [ - ]:          0 :   (asserts! (>= block-height (var-get notifier-vote-end-block)) err-voting-still-active)
+     702                 :          0 :   (var-set notifier-vote-active false)
+     703                 :          0 :   (end-vote-notifier-private)))
+     704                 :            : 
+     705                 :            : (define-private (end-vote-notifier-private) 
+     706                 :          0 : (begin 
+     707                 :          0 :   (unwrap! (get-max-votes-number-notifier) (err u99999))
+     708                 :          0 :   (if 
+     709                 :          0 :     (> 
+     710                 :          0 :       (var-get max-votes-notifier) 
+     711                 :          0 :       (/ (var-get k) u2)) 
+     712            [ - ]:          0 :     (var-set notifier (var-get max-voted-proposed-notifier))
+     713            [ - ]:          0 :     false)
+     714                 :          0 :   (delete-all-notifier-entries)
+     715                 :          0 :   (ok true)))
+     716                 :            : 
+     717                 :            : (define-private (get-max-votes-number-notifier) 
+     718                 :          0 : (ok (map compare-votes-number-notifier (var-get miners-list))))
+     719                 :            : 
+     720                 :            : (define-private (compare-votes-number-notifier (proposed-notifier principal)) 
+     721                 :          0 : (ok 
+     722                 :          0 : (if (is-some (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier})))
+     723            [ - ]:          0 :     (if (> (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier}))) (/ (var-get k) u2))
+     724            [ - ]:          0 :       (if 
+     725                 :          0 :         (> (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier}))) (var-get max-votes-notifier)) 
+     726            [ - ]:          0 :         (begin 
+     727                 :          0 :           (var-set max-votes-notifier (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier})))) 
+     728                 :          0 :           (var-set max-voted-proposed-notifier proposed-notifier))
+     729            [ - ]:          0 :         (if 
+     730                 :          0 :           (is-eq (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier}))) (var-get max-votes-notifier)) 
+     731            [ - ]:          0 :           (if 
+     732                 :          0 :             (< 
+     733                 :          0 :               (unwrap-panic (get block-height (map-get? map-block-joined {address: proposed-notifier}))) 
+     734                 :          0 :               (unwrap-panic (get block-height (map-get? map-block-joined {address: (var-get max-voted-proposed-notifier)})))) 
+     735            [ - ]:          0 :             (begin 
+     736                 :          0 :                 (var-set max-voted-proposed-notifier proposed-notifier))
+     737            [ - ]:          0 :             false)
+     738            [ - ]:          0 :         false))
+     739            [ - ]:          0 :       false)
+     740            [ - ]:          0 :     false)))
+     741                 :            : 
+     742                 :            : (define-private (delete-all-notifier-entries) 
+     743                 :          0 : (begin 
+     744                 :          0 :   (var-set max-votes-notifier u0)
+     745                 :          0 :   (var-set max-voted-proposed-notifier (var-get notifier))
+     746                 :          0 :   (map delete-one-notifier-entry (var-get miners-list))
+     747                 :          0 :   (var-set notifier-previous-entries-removed true)))
+     748                 :            : 
+     749                 :            : (define-private (delete-one-notifier-entry (miner principal)) 
+     750                 :          0 : (begin 
+     751                 :          0 :   (map-delete map-voted-update-notifier {miner-who-voted: miner})
+     752                 :          0 :   (map-delete map-votes-notifier {voted-notifier: miner})))
+     753                 :            : 
+     754                 :            : (define-public (vote-notifier (voted-notifier principal)) 
+     755                 :          0 : (begin 
+     756    [ - ][ -  - ]:          0 :   (asserts! (and (is-some (get value (map-get? map-is-miner {address: voted-notifier}))) (unwrap-panic (get value (map-get? map-is-miner {address: voted-notifier})))) err-not-in-miner-map)
+     757    [ - ][ -  - ]:          0 :   (asserts! (and (is-some (get value (map-get? map-is-miner {address: contract-caller}))) (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller})))) err-no-vote-permission)
+     758            [ - ]:          0 :   (asserts! (var-get notifier-vote-active) err-not-voting-period)
+     759            [ - ]:          0 :   (asserts! (not (is-eq contract-caller voted-notifier)) err-cant-vote-himself)
+     760            [ - ]:          0 :   (asserts! (< block-height (var-get notifier-vote-end-block)) err-not-voting-period)
+     761            [ - ]:          0 :   (asserts! (is-none (get miner-voted (map-get? map-voted-update-notifier {miner-who-voted: tx-sender}))) err-already-voted)
+     762                 :          0 :   (map-set map-voted-update-notifier {miner-who-voted: tx-sender} {miner-voted: voted-notifier})
+     763                 :          0 :   (if (is-none (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) 
+     764            [ - ]:          0 :     (map-set map-votes-notifier {voted-notifier: voted-notifier} {votes-number: u1}) 
+     765            [ - ]:          0 :     (map-set map-votes-notifier {voted-notifier: voted-notifier} {votes-number: (+ (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) u1)}))
+     766                 :          0 :   (try! 
+     767                 :          0 :     (if (is-vote-accepted (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) (var-get k)) 
+     768            [ - ]:          0 :     (begin 
+     769                 :          0 :       (var-set notifier voted-notifier)
+     770                 :          0 :       (var-set notifier-vote-end-block block-height)
+     771                 :          0 :       (var-set notifier-vote-active false)
+     772                 :          0 :       (end-vote-notifier))
+     773            [ - ]:          0 :     (ok false)))
+     774                 :          0 : (ok true)))
+     775                 :            : 
+     776                 :            : ;; WARNING FLOW
+     777                 :            : 
+     778                 :            : (define-public (warn-miner (miner principal)) 
+     779                 :          0 : (begin 
+     780                 :          0 : (let ((incremented-value 
+     781                 :          0 :       (if 
+     782                 :          0 :         (is-some (get value (map-get? map-warnings {address: miner}))) 
+     783            [ - ]:          0 :         (+ (unwrap-panic (get value (map-get? map-warnings {address: miner}))) u1) 
+     784            [ - ]:          0 :         u1))) 
+     785            [ - ]:          0 :   (asserts! (is-eq contract-caller (var-get notifier)) err-only-notifier) 
+     786                 :          0 :   (asserts! 
+     787                 :          0 :     (not (and 
+     788            [ - ]:          0 :       (is-none (at-block (unwrap! (get-block-info? id-header-hash (- block-height u1)) err-cant-unwrap-block-info) (get value (map-get? map-warnings {address: miner}))))
+     789            [ - ]:          0 :       (>= incremented-value u2))) 
+     790            [ - ]:          0 :     err-one-warning-per-block)
+     791                 :          0 :   (asserts! 
+     792                 :          0 :     (not 
+     793                 :          0 :       (>= 
+     794                 :          0 :         (- 
+     795                 :          0 :           incremented-value
+     796                 :          0 :           (unwrap! (at-block (unwrap! (get-block-info? id-header-hash (- block-height u1)) err-cant-unwrap-block-info) (get value (map-get? map-warnings {address: miner}))) err-cant-unwrap-block-info)) 
+     797                 :          0 :         u2)) 
+     798            [ - ]:          0 :     err-one-warning-per-block)
+     799                 :          0 :     (ok 
+     800                 :          0 :       (if 
+     801                 :          0 :         (is-some (get value (map-get? map-warnings {address: miner}))) 
+     802            [ - ]:          0 :         (map-set map-warnings {address: miner} {value: (+ (unwrap-panic (get value (map-get? map-warnings {address: miner}))) u1)})
+     803            [ - ]:          0 :         (map-set map-warnings {address: miner} {value: u1}))))))
+     804                 :            : 
+     805                 :            : ;; ELECTION FUNCTIONS
+     806                 :            : 
+     807                 :            : (define-private (is-vote-accepted (votes-number uint) (k-local uint))
+     808                 :          0 : (if 
+     809                 :          0 :   (is-eq k-local u0) ;; k is 0 for n=1, n=2 
+     810            [ - ]:          0 :     (>= votes-number u1) 
+     811            [ - ]:          0 :     (>= votes-number k-local)))
+     812                 :            : 
+     813                 :            : (define-private (is-democratic-vote-accepted-notifier (votes-number uint) (k-local uint))
+     814                 :          0 : (if 
+     815                 :          0 :   (is-eq k-local u0) ;; k is 0 for n=1, n=2 
+     816            [ - ]:          0 :     (>= votes-number u1) 
+     817            [ - ]:          0 :     (>= votes-number (/ k-local u2))))
+     818                 :            : 
+     819                 :            : (define-private (is-vote-rejected-join (votes-number uint) (k-local uint) (n-local uint))
+     820                 :          0 : (if 
+     821                 :          0 :   (is-eq n-local u1) 
+     822            [ - ]:          0 :   (>= votes-number u1) 
+     823            [ - ]:          0 :   (if (is-eq n-local u2) 
+     824            [ - ]:          0 :     (>= votes-number u2) 
+     825            [ - ]:          0 :     (>= votes-number (+ (- n-local k-local) u1)))))
+     826                 :            : 
+     827                 :            : 
+     828                 :            : (define-private (is-vote-rejected-remove (votes-number uint) (k-local uint) (n-local uint))
+     829                 :          0 : (if 
+     830                 :          0 :   (is-eq n-local u2) 
+     831            [ - ]:          0 :   (>= votes-number u1) 
+     832            [ - ]:          0 :   (if (is-eq n-local u3)
+     833            [ - ]:          0 :     (>= votes-number u2)
+     834            [ - ]:          0 :     (>= votes-number (+ (- n-local k-local) u1)))))
+     835                 :            : 
+     836                 :            : (define-private (is-vote-rejected-notifier (votes-number uint) (k-local uint) (n-local uint))
+     837                 :          0 : (if (is-eq n-local u2) 
+     838            [ - ]:          0 :   (>= votes-number u1) 
+     839            [ - ]:          0 :   (if (is-eq n-local u3)
+     840            [ - ]:          0 :     (>= votes-number u2)
+     841            [ - ]:          0 :     (>= votes-number (+ (- n-local k-local) u1)))))
+     842                 :            : 
+     843                 :            : ;; RECOVERING UNASSIGNED FUNDS FUNCTIONS
+     844                 :            : 
+     845                 :            : ;; A public function each miner can call in order to recover the 
+     846                 :            : ;; undistributed rewards caused by dividing rewards to stackers
+     847                 :            : (define-public (flush-change)
+     848                 :          0 : (let ((total-change-funds (var-get reward-change-funds))
+     849                 :          0 :       (current-miners-list-len (len (var-get miners-list)))
+     850                 :          0 :       (distributed-change-funds (/ total-change-funds current-miners-list-len))
+     851                 :          0 :       (change-after-flushing (mod total-change-funds current-miners-list-len))) 
+     852            [ - ]:          0 :   (asserts! (check-is-miner-now contract-caller) err-not-in-miner-map)
+     853                 :          0 :   (var-set temp-distributed-change-funds distributed-change-funds)
+     854                 :          0 :   (var-set temp-change-after-flushing change-after-flushing)
+     855                 :          0 :   (map flush-distribution-one-miner (var-get miners-list))
+     856                 :          0 :   (ok   
+     857                 :          0 :     (var-set reward-change-funds 
+     858                 :          0 :       (- 
+     859                 :          0 :         total-change-funds 
+     860                 :          0 :         (* 
+     861                 :          0 :           current-miners-list-len 
+     862                 :          0 :           distributed-change-funds))))))
+     863                 :            : 
+     864                 :            : (define-private (flush-distribution-one-miner (miner principal))
+     865                 :          0 : (let ((miner-balance (default-to u0 (map-get? balance miner)))
+     866                 :          0 :       (total-change-funds (var-get reward-change-funds))
+     867                 :          0 :       (current-miners-list-len (len (var-get miners-list)))
+     868                 :          0 :       (distributed-change-funds (/ total-change-funds current-miners-list-len)))
+     869                 :          0 :   (map-set balance miner 
+     870                 :          0 :     (+ 
+     871                 :          0 :       miner-balance 
+     872                 :          0 :       (var-get temp-distributed-change-funds)))))
+     873                 :            : 
+     874                 :            : ;; LIST PROCESSING FUNCTIONS
+     875                 :            : 
+     876                 :            : (define-private (remove-principal-waiting-list (miner principal))
+     877                 :          0 : (begin
+     878                 :          0 :     (var-set waiting-list-miner-to-remove miner) 
+     879                 :          0 :     (ok (filter is-principal-in-waiting-list (var-get waiting-list))))) 
+     880                 :            : 
+     881                 :            : (define-private (remove-principal-pending-accept-list (miner principal))
+     882                 :          0 : (begin 
+     883                 :          0 :     (var-set waiting-list-miner-to-remove miner) 
+     884                 :          0 :     (ok (filter is-principal-in-pending-accept-list (var-get pending-accept-list)))))
+     885                 :            : 
+     886                 :            : (define-private (remove-principal-miners-list (miner principal))
+     887                 :          0 : (begin
+     888                 :          0 :   (var-set miners-list-miner-to-remove miner) 
+     889                 :          0 :   (ok (filter is-principal-in-miners-list (var-get miners-list)))))
+     890                 :            : 
+     891                 :            : (define-private (remove-principal-proposed-removal-list (miner principal))
+     892                 :          0 : (begin
+     893                 :          0 :   (var-set proposed-removal-list-miner-to-remove miner) 
+     894                 :          0 :   (ok (filter is-principal-in-proposed-removal-list (var-get proposed-removal-list)))))
+     895                 :            : 
+     896                 :            : ;; MINER STATUS FUNCTIONS
+     897                 :            : 
+     898                 :            : (define-private (check-is-miner-when-requested-join (miner-to-vote principal))
+     899                 :          0 : (ok 
+     900                 :          0 :   (if 
+     901                 :          0 :     (is-some 
+     902                 :          0 :       (if 
+     903                 :          0 :         (is-eq  
+     904                 :          0 :           (unwrap! (get value (map-get? map-block-asked-to-join {address: miner-to-vote})) err-cant-unwrap-asked-to-join) 
+     905                 :          0 :           block-height)
+     906            [ - ]:          0 :         (get value (map-get? map-is-miner {address: contract-caller})) 
+     907            [ - ]:          0 :         (at-block 
+     908                 :          0 :           (unwrap! 
+     909                 :          0 :             (get-block-info? id-header-hash 
+     910                 :          0 :               (unwrap! (get value (map-get? map-block-asked-to-join {address: miner-to-vote})) err-cant-unwrap-asked-to-join)) 
+     911                 :          0 :           err-cant-unwrap-block-info) 
+     912                 :          0 :           (get value (map-get? map-is-miner {address: contract-caller})))))
+     913            [ - ]:          0 :     (if 
+     914                 :          0 :       (is-eq 
+     915                 :          0 :         (unwrap! 
+     916                 :          0 :           (get value (map-get? map-block-asked-to-join {address: miner-to-vote})) 
+     917                 :          0 :         err-cant-unwrap-asked-to-join) 
+     918                 :          0 :         block-height) 
+     919            [ - ]:          0 :       (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller}))) 
+     920            [ - ]:          0 :       (at-block
+     921                 :          0 :         (unwrap! 
+     922                 :          0 :           (get-block-info? id-header-hash 
+     923                 :          0 :             (unwrap-panic (get value (map-get? map-block-asked-to-join {address: miner-to-vote}))))
+     924                 :          0 :         err-cant-unwrap-block-info)
+     925                 :          0 :         (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller})))))
+     926            [ - ]:          0 :   false)))
+     927                 :            : 
+     928                 :            : (define-private (check-is-miner-when-requested-remove (miner-to-vote principal))
+     929                 :          0 : (ok 
+     930                 :          0 :   (if 
+     931                 :          0 :     (is-some 
+     932                 :          0 :       (if 
+     933                 :          0 :         (is-eq  
+     934                 :          0 :           (unwrap! (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})) err-cant-unwrap-asked-to-join) 
+     935                 :          0 :           block-height)
+     936            [ - ]:          0 :         (get value (map-get? map-is-miner {address: contract-caller})) 
+     937            [ - ]:          0 :         (at-block 
+     938                 :          0 :           (unwrap! 
+     939                 :          0 :             (get-block-info? id-header-hash 
+     940                 :          0 :               (unwrap! (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})) err-cant-unwrap-asked-to-join)) 
+     941                 :          0 :           err-cant-unwrap-block-info) 
+     942                 :          0 :           (get value (map-get? map-is-miner {address: contract-caller})))))
+     943            [ - ]:          0 :   (if 
+     944                 :          0 :       (is-eq 
+     945                 :          0 :         (unwrap! 
+     946                 :          0 :           (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})) 
+     947                 :          0 :         err-cant-unwrap-asked-to-join) 
+     948                 :          0 :         block-height) 
+     949            [ - ]:          0 :       (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller}))) 
+     950            [ - ]:          0 :       (at-block
+     951                 :          0 :         (unwrap! 
+     952                 :          0 :           (get-block-info? id-header-hash 
+     953                 :          0 :             (unwrap-panic (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote}))))
+     954                 :          0 :         err-cant-unwrap-block-info)
+     955                 :          0 :         (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller})))))
+     956            [ - ]:          0 :   false)))
+     957                 :            : 
+     958                 :            : (define-read-only (check-is-miner-when-requested-join-tool-fn (miner-to-vote-address principal)) 
+     959                 :          0 : (get value (map-get? map-is-miner {address: contract-caller})))
+     960                 :            : 
+     961                 :            : (define-private (check-is-miner-now (miner principal))
+     962                 :          0 : (if (is-some (get value (map-get? map-is-miner {address: miner})))
+     963            [ - ]:          0 :   (unwrap-panic (get value (map-get? map-is-miner {address: miner})))
+     964            [ - ]:          0 :   false))
+     965                 :            : 
+     966                 :            : (define-private (check-is-proposed-for-removal-now (miner principal))
+     967                 :          0 : (if (is-some (get value (map-get? map-is-proposed-for-removal {address: miner})))
+     968            [ - ]:          0 :   (unwrap-panic (get value (map-get? map-is-proposed-for-removal {address: miner})))
+     969            [ - ]:          0 :   false))
+     970                 :            : 
+     971                 :            : (define-private (check-is-waiting-now (miner principal))
+     972                 :          0 : (if (is-some (get value (map-get? map-is-waiting {address: miner})))
+     973            [ - ]:          0 :   (unwrap-panic (get value (map-get? map-is-waiting {address: miner})))
+     974            [ - ]:          0 :   false))
+     975                 :            : 
+     976                 :            : (define-private (check-is-pending-now (miner principal))
+     977                 :          0 : (if (is-some (get value (map-get? map-is-pending {address: miner})))
+     978            [ - ]:          0 :   (unwrap-panic (get value (map-get? map-is-pending {address: miner})))
+     979            [ - ]:          0 :   false)
+     980                 :            : )
+     981                 :            : 
+     982                 :            : (define-private (get-reward-at-block (block-number uint)) 
+     983                 :          0 : (begin 
+     984                 :          0 :   {reward: (get-block-info? block-reward block-number), 
+     985                 :          0 :   claimer: (get-block-info? miner-address block-number)}))
+     986                 :            : 
+     987                 :            : (define-read-only (get-reward-at-block-read (block-number uint)) 
+     988                 :          0 : (begin 
+     989                 :          0 :   {reward: (get-block-info? block-reward block-number), 
+     990                 :          0 :   claimer: (get-block-info? miner-address block-number)
+     991                 :            :   }))
+     992                 :            : 
+     993                 :            : (define-read-only (get-address-status (address principal))
+     994                 :          0 : (if (check-is-miner-now address)
+     995            [ - ]:          0 :   (ok "is-miner")
+     996            [ - ]:          0 :   (if (check-is-waiting-now address)
+     997            [ - ]:          0 :     (ok "is-waiting")
+     998            [ - ]:          0 :     (if (check-is-pending-now address)
+     999            [ - ]:          0 :       (ok "is-pending")
+    1000            [ - ]:          0 :       (ok "is-none")
+    1001                 :            :     )
+    1002                 :            :   )
+    1003                 :            : ))
+    1004                 :            : 
+    1005                 :            : ;; READ-ONLY UTILS
+    1006                 :            : 
+    1007                 :            : ;; (define-read-only (check-vote-accepted) ;; to check the vote status inside FE
+    1008                 :            : ;; (is-vote-accepted (unwrap-panic (get value (map-get? map-votes-accept-join {address: tx-sender})))))
+    1009                 :            : 
+    1010                 :            : (define-read-only (get-k) 
+    1011                 :          0 : (var-get k))
+    1012                 :            : 
+    1013                 :            : (define-read-only (get-notifier) 
+    1014                 :          0 : (var-get notifier))
+    1015                 :            : 
+    1016                 :            : (define-read-only (get-blocks-won) 
+    1017                 :          0 : (var-get blocks-won))
+    1018                 :            : 
+    1019                 :            : (define-read-only (get-total-rewards-distributed) 
+    1020                 :          0 : (var-get total-rewarded))
+    1021                 :            : 
+    1022                 :            : (define-read-only (get-waiting-list) 
+    1023                 :          0 : (var-get waiting-list))
+    1024                 :            : 
+    1025                 :            : (define-read-only (get-miners-list) 
+    1026                 :          0 : (var-get miners-list))
+    1027                 :            : 
+    1028                 :            : (define-read-only (get-pending-accept-list) 
+    1029                 :          0 : (var-get pending-accept-list ))
+    1030                 :            : 
+    1031                 :            : (define-read-only (get-proposed-removal-list) 
+    1032                 :          0 : (var-get proposed-removal-list ))
+    1033                 :            : 
+    1034                 :            : (define-read-only (get-notifier-vote-status) 
+    1035                 :          0 : (var-get notifier-vote-active))
+    1036                 :            : 
+    1037                 :            : (define-read-only (get-notifier-vote-number (voted-notifier principal)) 
+    1038                 :          0 : (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier})))
+    1039                 :            : 
+    1040                 :            : (define-read-only (get-max-voted-notifier) 
+    1041                 :          0 : (var-get max-voted-proposed-notifier))
+    1042                 :            : 
+    1043                 :            : (define-read-only (get-max-votes-notifier) 
+    1044                 :          0 : (var-get max-votes-notifier))
+    1045                 :            : 
+    1046                 :            : (define-read-only (get-current-block)
+    1047                 :          0 : (ok block-height))
+    1048                 :            : 
+    1049                 :            : (define-private (is-principal-in-waiting-list (miner principal))
+    1050                 :          0 : (not (is-eq 
+    1051                 :          0 :   (var-get waiting-list-miner-to-remove)
+    1052                 :          0 :   miner)))
+    1053                 :            : 
+    1054                 :            : (define-private (is-principal-in-pending-accept-list (miner principal))
+    1055                 :          0 : (not (is-eq 
+    1056                 :          0 :   (var-get pending-accept-list-miner-to-remove)
+    1057                 :          0 :   miner)))
+    1058                 :            : 
+    1059                 :            : (define-private (is-principal-in-miners-list (miner principal))
+    1060                 :          0 : (not (is-eq  
+    1061                 :          0 :   (var-get miners-list-miner-to-remove) 
+    1062                 :          0 :   miner)))
+    1063                 :            : 
+    1064                 :            : (define-private (is-principal-in-proposed-removal-list (miner principal))
+    1065                 :          0 : (not (is-eq  
+    1066                 :          0 :   (var-get proposed-removal-list-miner-to-remove) 
+    1067                 :          0 :   miner)))
+
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/mining-pool.clar.func-sort-c.html b/sbtc-ops/smart-contract/coverage/contracts/mining-pool.clar.func-sort-c.html new file mode 100644 index 00000000..bd8fcfe3 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/mining-pool.clar.func-sort-c.html @@ -0,0 +1,493 @@ + + + + + + + LCOV - coverage.lcov - contracts/mining-pool.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - mining-pool.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:47968969.5 %
Date:2023-08-18 21:53:02Functions:6810366.0 %
Branches:10620950.7 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
check-is-miner-when-requested-join-tool-fn0
check-is-pending-now0
flush-change0
flush-change-each-miner0
flush-distribution-one-miner0
get-address-status0
get-all-data-miners-blocks0
get-all-data-miners-in-pool0
get-all-data-miners-pending-accept0
get-all-data-notifier-voter-miners0
get-all-data-total-withdrawals0
get-auto-exchange0
get-blocks-won0
get-current-block0
get-data-miner-blocks0
get-data-miner-in-pool0
get-data-miner-pending-accept0
get-data-miner-withdrawals0
get-data-notifier-election-process0
get-data-notifier-voter-miner0
get-miner-btc-address0
get-proposed-removal-list0
get-remaining-blocks-until-join0
get-total-rewards-distributed0
is-democratic-vote-accepted-notifier0
is-in-voters-list0
is-principal-in-pending-accept-list0
is-vote-rejected-notifier0
quit-proposed-removal-list0
quit-waiting-list0
remove-principal-pending-accept-list0
set-auto-exchange0
set-my-btc-address0
warn-miner0
was-block-claimed0
get-all-data-miner-proposed-for-removal1
get-all-data-miners-proposed-for-removal1
get-all-data-waiting-miner2
get-all-data-waiting-miners2
get-max-voted-notifier2
get-max-votes-notifier2
compare-votes-number-notifier3
delete-all-notifier-entries3
delete-one-notifier-entry3
distribute-reward-each-miner3
end-vote-notifier3
end-vote-notifier-private3
get-max-votes-number-notifier3
get-notifier3
get-reward-at-block3
get-reward-at-block-read3
start-vote-notifier3
reward-distribution4
reject-removal5
get-notifier-vote-status7
process-removal7
clear-votes-map-remove-vote12
is-principal-in-proposed-removal-list12
remove-map-record-remove-vote12
remove-principal-proposed-removal-list12
get-k13
propose-removal13
add-miner-to-pool21
x-blocks-passed24
add-pending-miners-to-pool31
get-waiting-list34
get-miners-list48
get-pending-accept-list51
update-threshold75
is-vote-rejected-remove121
get-n-at-block-proposed-removal122
vote-negative-remove-request135
vote-positive-remove-request225
get-notifier-vote-number252
check-is-miner-when-requested-remove348
has-voted-remove348
get-k-at-block-proposed-removal349
vote-notifier352
check-is-proposed-for-removal-now370
reject-miner-in-pool402
leave-pool405
is-principal-in-miners-list411
remove-principal-miners-list411
is-vote-rejected-join495
get-n-at-block-asked-to-join497
vote-negative-join-request497
withdraw-stx504
get-balance507
deposit-stx699
accept-miner-in-pool2559
vote-positive-join-request2771
try-enter-pool2958
clear-votes-map-join-vote2961
is-principal-in-waiting-list2961
remove-map-record-join-vote2961
remove-principal-waiting-list2961
ask-to-join2962
get-k-at-block-asked-to-join3056
is-vote-accepted3134
has-voted-join3264
check-is-miner-when-requested-join3266
check-is-miner-now3380
check-is-waiting-now6230
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/mining-pool.clar.func.html b/sbtc-ops/smart-contract/coverage/contracts/mining-pool.clar.func.html new file mode 100644 index 00000000..195824ca --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/mining-pool.clar.func.html @@ -0,0 +1,493 @@ + + + + + + + LCOV - coverage.lcov - contracts/mining-pool.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - mining-pool.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:47968969.5 %
Date:2023-08-18 21:53:02Functions:6810366.0 %
Branches:10620950.7 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
accept-miner-in-pool2559
add-miner-to-pool21
add-pending-miners-to-pool31
ask-to-join2962
check-is-miner-now3380
check-is-miner-when-requested-join3266
check-is-miner-when-requested-join-tool-fn0
check-is-miner-when-requested-remove348
check-is-pending-now0
check-is-proposed-for-removal-now370
check-is-waiting-now6230
clear-votes-map-join-vote2961
clear-votes-map-remove-vote12
compare-votes-number-notifier3
delete-all-notifier-entries3
delete-one-notifier-entry3
deposit-stx699
distribute-reward-each-miner3
end-vote-notifier3
end-vote-notifier-private3
flush-change0
flush-change-each-miner0
flush-distribution-one-miner0
get-address-status0
get-all-data-miner-proposed-for-removal1
get-all-data-miners-blocks0
get-all-data-miners-in-pool0
get-all-data-miners-pending-accept0
get-all-data-miners-proposed-for-removal1
get-all-data-notifier-voter-miners0
get-all-data-total-withdrawals0
get-all-data-waiting-miner2
get-all-data-waiting-miners2
get-auto-exchange0
get-balance507
get-blocks-won0
get-current-block0
get-data-miner-blocks0
get-data-miner-in-pool0
get-data-miner-pending-accept0
get-data-miner-withdrawals0
get-data-notifier-election-process0
get-data-notifier-voter-miner0
get-k13
get-k-at-block-asked-to-join3056
get-k-at-block-proposed-removal349
get-max-voted-notifier2
get-max-votes-notifier2
get-max-votes-number-notifier3
get-miner-btc-address0
get-miners-list48
get-n-at-block-asked-to-join497
get-n-at-block-proposed-removal122
get-notifier3
get-notifier-vote-number252
get-notifier-vote-status7
get-pending-accept-list51
get-proposed-removal-list0
get-remaining-blocks-until-join0
get-reward-at-block3
get-reward-at-block-read3
get-total-rewards-distributed0
get-waiting-list34
has-voted-join3264
has-voted-remove348
is-democratic-vote-accepted-notifier0
is-in-voters-list0
is-principal-in-miners-list411
is-principal-in-pending-accept-list0
is-principal-in-proposed-removal-list12
is-principal-in-waiting-list2961
is-vote-accepted3134
is-vote-rejected-join495
is-vote-rejected-notifier0
is-vote-rejected-remove121
leave-pool405
process-removal7
propose-removal13
quit-proposed-removal-list0
quit-waiting-list0
reject-miner-in-pool402
reject-removal5
remove-map-record-join-vote2961
remove-map-record-remove-vote12
remove-principal-miners-list411
remove-principal-pending-accept-list0
remove-principal-proposed-removal-list12
remove-principal-waiting-list2961
reward-distribution4
set-auto-exchange0
set-my-btc-address0
start-vote-notifier3
try-enter-pool2958
update-threshold75
vote-negative-join-request497
vote-negative-remove-request135
vote-notifier352
vote-positive-join-request2771
vote-positive-remove-request225
warn-miner0
was-block-claimed0
withdraw-stx504
x-blocks-passed24
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/mining-pool.clar.gcov.html b/sbtc-ops/smart-contract/coverage/contracts/mining-pool.clar.gcov.html new file mode 100644 index 00000000..8f592738 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/mining-pool.clar.gcov.html @@ -0,0 +1,1152 @@ + + + + + + + LCOV - coverage.lcov - contracts/mining-pool.clar + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - mining-pool.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:47968969.5 %
Date:2023-08-18 21:53:02Functions:6810366.0 %
Branches:10620950.7 %
+
+ + + + + + + + +

+
           Branch data     Line data    Source code
+
+       1                 :            : (define-constant err-invalid (err u300))
+       2                 :            : (define-constant err-list-length-exceeded (err u101))
+       3                 :            : (define-constant err-already-asked-to-join (err u102))
+       4                 :            : (define-constant err-already-joined (err u103))
+       5                 :            : (define-constant err-not-in-miner-map (err u104))
+       6                 :            : (define-constant err-no-vote-permission (err u105))
+       7                 :            : (define-constant err-more-blocks-to-pass (err u106))
+       8                 :            : (define-constant err-no-pending-miners (err u107))
+       9                 :            : (define-constant err-already-voted (err u108))
+      10                 :            : (define-constant err-not-asked-to-join (err u109))
+      11                 :            : (define-constant err-cant-unwrap-check-miner (err u110))
+      12                 :            : (define-constant err-cant-unwrap-asked-to-join (err u111))
+      13                 :            : (define-constant err-cant-unwrap-block-info (err u112))
+      14                 :            : (define-constant err-currently-notifier (err u113))
+      15                 :            : (define-constant err-not-in-miner-map-miner-to-remove (err u114))
+      16                 :            : (define-constant err-already-proposed-for-removal (err u116))
+      17                 :            : (define-constant err-not-proposed-for-removal (err u117))
+      18                 :            : (define-constant err-cant-remove-when-alone-in-pool (err u118))
+      19                 :            : (define-constant err-cant-vote-himself (err u119))
+      20                 :            : (define-constant err-cant-change-notifier (err u120))
+      21                 :            : (define-constant err-already-proposed-for-notifier (err u121))
+      22                 :            : (define-constant err-not-proposed-for-removal-proposal-block-missing (err u122))
+      23                 :            : (define-constant err-not-proposed-for-notifier-k-missing (err u123))
+      24                 :            : (define-constant err-not-proposed-notifier (err u124))
+      25                 :            : (define-constant err-already-notifier (err u125))
+      26                 :            : (define-constant err-not-in-miner-map-proposed-notifier (err u126))
+      27                 :            : (define-constant err-vote-started-already (err u127))
+      28                 :            : (define-constant err-voting-still-active (err u128))
+      29                 :            : (define-constant err-not-voting-period (err u129))
+      30                 :            : (define-constant err-not-waiting (err u130)) 
+      31                 :            : (define-constant err-not-pending (err u131))
+      32                 :            : (define-constant err-no-join-block-data (err u132))
+      33                 :            : (define-constant err-not-voted (err u133))
+      34                 :            : (define-constant err-only-notifier (err u134))
+      35                 :            : (define-constant err-one-warning-per-block (err u135))
+      36                 :            : (define-constant err-block-height-invalid (err u136))
+      37                 :            : (define-constant err-unwrap-miner-index (err u999))
+      38                 :            : (define-constant err-insufficient-balance (err u1001))
+      39                 :            : (define-constant err-missing-balance (err u1002))
+      40                 :            : (define-constant err-already-distributed (err u1003))
+      41                 :            : (define-constant err-cant-unwrap-rewarded-block (err u1004))
+      42                 :            : 
+      43                 :            : (define-constant notifier-election-blocks-to-pass u144)
+      44                 :            : (define-constant blocks-to-pass u100)
+      45                 :            : 
+      46                 :            : (define-map balance principal uint)
+      47                 :            : (define-map claimed-rewards { block-number: uint } { claimed: bool })
+      48                 :            : (define-map map-is-miner { address: principal } { value: bool })
+      49                 :            : (define-map map-is-waiting { address: principal } { value: bool })
+      50                 :            : (define-map map-is-pending { address: principal } { value: bool })
+      51                 :            : (define-map map-is-proposed-for-removal { address: principal } { value: bool })
+      52                 :            : (define-map map-block-asked-to-join { address: principal } { value: uint })
+      53                 :            : (define-map map-block-proposed-to-remove { address: principal } { value: uint })
+      54                 :            : (define-map map-block-joined { address: principal } { block-height: uint })
+      55                 :            : (define-map map-balance-xBTC { address: principal } { value: uint })
+      56                 :            : (define-map auto-exchange { address: principal } { value: bool })
+      57                 :            : (define-map btc-address { address: principal } { btc-address: {hashbytes: (buff 20), version: (buff 1)} })
+      58                 :            : 
+      59                 :            : (define-map map-votes-accept-join { address: principal } { value: uint })
+      60                 :            : (define-map map-votes-reject-join { address: principal } { value: uint })
+      61                 :            : (define-map map-votes-accept-removal { address: principal } { value: uint })
+      62                 :            : (define-map map-votes-reject-removal { address: principal } { value: uint })
+      63                 :            : (define-map map-join-request-voter { miner-to-vote: principal, voter: principal } { value: bool })
+      64                 :            : (define-map map-remove-request-voter { miner-to-vote: principal, voter: principal } { value: bool })
+      65                 :            : (define-map map-voted-update-notifier { miner-who-voted: principal } { miner-voted: principal })
+      66                 :            : (define-map map-votes-notifier { voted-notifier: principal } { votes-number: uint })
+      67                 :            : (define-map map-blacklist { address: principal } { value: bool })
+      68                 :            : (define-map map-total-withdraw { address: principal } { value: uint })
+      69                 :            : (define-map map-warnings { address: principal } { value: uint })
+      70                 :            : 
+      71                 :            : (define-data-var miners-list-len-at-reward-block uint u0)
+      72                 :            : (define-data-var notifier principal tx-sender)
+      73                 :            : (define-data-var waiting-list (list 300 principal) (list ))
+      74                 :            : (define-data-var miners-list (list 300 principal) (list (var-get notifier)))
+      75                 :            : (define-data-var pending-accept-list (list 300 principal) (list ))
+      76                 :            : (define-data-var proposed-removal-list (list 300 principal) (list ))
+      77                 :            : (define-data-var n uint u1)
+      78                 :            : (define-data-var k-percentage uint u67)
+      79                 :            : (define-data-var k uint u1)
+      80                 :            : (define-data-var k-critical uint u75)
+      81                 :            : (define-data-var waiting-list-miner-to-remove principal tx-sender) ;; use in remove-principal-miners-list
+      82                 :            : (define-data-var pending-accept-list-miner-to-remove principal tx-sender)
+      83                 :            : (define-data-var miners-list-miner-to-remove principal tx-sender)
+      84                 :            : (define-data-var proposed-removal-list-miner-to-remove principal tx-sender)
+      85                 :            : (define-data-var last-join-done uint u1)
+      86                 :            : (define-data-var miner-to-remove-votes-join principal tx-sender)
+      87                 :            : (define-data-var miner-to-remove-votes-remove principal tx-sender)
+      88                 :            : (define-data-var notifier-previous-entries-removed bool true)
+      89                 :            : (define-data-var notifier-vote-active bool false)
+      90                 :            : (define-data-var notifier-vote-start-block uint u0)
+      91                 :            : (define-data-var notifier-vote-end-block uint u0)
+      92                 :            : (define-data-var max-votes-notifier uint u0)
+      93                 :            : (define-data-var max-voted-proposed-notifier principal tx-sender)
+      94                 :            : (define-data-var reward uint u0)
+      95                 :            : (define-data-var total-rewarded uint u0)
+      96                 :            : (define-data-var blocks-won uint u0)
+      97                 :            : (define-data-var reward-change-funds uint u0)
+      98                 :            : (define-data-var temp-distributed-change-funds uint u0)
+      99                 :            : (define-data-var temp-change-after-flushing uint u0)
+     100                 :            : 
+     101                 :         41 : (map-set map-is-miner {address: tx-sender} {value: true})
+     102                 :         41 : (map-set map-block-joined {address: tx-sender} {block-height: block-height})
+     103                 :         41 : (map-set balance tx-sender u0)
+     104                 :            : ;; at new join -> block height - last-join-done >= 100 !
+     105                 :            : 
+     106                 :            : ;; READ ONLY FE UTILS
+     107                 :            : 
+     108                 :            : ;; waiting miners
+     109                 :            : 
+     110                 :            : (define-read-only (get-all-data-waiting-miners (waiting-miners-list (list 100 principal))) 
+     111                 :          2 : (map get-all-data-waiting-miner waiting-miners-list))
+     112                 :            : 
+     113                 :            : (define-private (get-all-data-waiting-miner (miner principal))
+     114                 :          2 : (let ((k-at-block-asked-to-join (unwrap-panic (get-k-at-block-asked-to-join miner)))
+     115                 :          2 :       (n-at-block-asked-to-join (unwrap-panic (get-n-at-block-asked-to-join miner))))
+     116                 :          2 :   (begin 
+     117            [ - ]:          2 :     (asserts! (is-some (get value (map-get? map-is-waiting {address: miner}))) err-not-waiting)
+     118                 :          2 :     (ok 
+     119                 :            :       {
+     120                 :          0 :         pos-votes: 
+     121                 :          2 :           (default-to u0 (get value (map-get? map-votes-accept-join {address: miner}))),
+     122                 :          0 :         pos-thr: 
+     123                 :          2 :           (if 
+     124                 :          2 :             (is-eq k-at-block-asked-to-join u0) 
+     125            [ - ]:          0 :             u1 
+     126            [ + ]:          2 :             k-at-block-asked-to-join),
+     127                 :          0 :         neg-votes:
+     128                 :          2 :           (default-to u0 (get value (map-get? map-votes-reject-join {address: miner}))),
+     129                 :          0 :         neg-thr: 
+     130                 :          2 :           (if   
+     131                 :          2 :             (is-eq n-at-block-asked-to-join u1) 
+     132            [ - ]:          0 :             u1 
+     133            [ + ]:          2 :             (if 
+     134                 :          2 :               (is-eq n-at-block-asked-to-join u2) 
+     135            [ - ]:          0 :               u2 
+     136            [ + ]:          2 :               (+ (- n-at-block-asked-to-join k-at-block-asked-to-join) u1)))}))))
+     137                 :            : 
+     138                 :            : ;; miners proposed for removal
+     139                 :            : 
+     140                 :            : (define-read-only (get-all-data-miners-proposed-for-removal (removal-miners-list (list 100 principal))) 
+     141                 :          1 : (map get-all-data-miner-proposed-for-removal removal-miners-list))
+     142                 :            : 
+     143                 :            : (define-private (get-all-data-miner-proposed-for-removal (miner principal)) 
+     144                 :          1 : (let ((k-at-block-proposed-removal (unwrap-panic (get-k-at-block-proposed-removal miner)))
+     145                 :          1 :       (n-at-block-proposed-removal (unwrap-panic (get-n-at-block-proposed-removal miner))))
+     146                 :          1 :   (begin 
+     147            [ - ]:          1 :     (asserts! (is-some (get value (map-get? map-is-proposed-for-removal {address: miner}))) err-not-pending)
+     148                 :          1 :     (ok 
+     149                 :            :       {
+     150                 :          0 :         vts-for: 
+     151                 :          1 :           (default-to u0 (get value (map-get? map-votes-accept-removal {address: miner}))),
+     152                 :          0 :         pos-thr: 
+     153                 :          1 :           (if 
+     154                 :          1 :             (is-eq k-at-block-proposed-removal u0) 
+     155            [ - ]:          0 :             u1 
+     156            [ + ]:          1 :             k-at-block-proposed-removal),
+     157                 :          0 :         vts-against: 
+     158                 :          1 :           (default-to u0 (get value (map-get? map-votes-reject-removal {address: miner}))),
+     159                 :          0 :         neg-thr: 
+     160                 :          1 :           (if   
+     161                 :          1 :             (is-eq n-at-block-proposed-removal u2) 
+     162            [ - ]:          0 :             u1 
+     163            [ + ]:          1 :             (if (is-eq n-at-block-proposed-removal u3) 
+     164            [ - ]:          0 :               u2 
+     165            [ + ]:          1 :               (+ (- n-at-block-proposed-removal k-at-block-proposed-removal) u1)))}))))
+     166                 :            : 
+     167                 :            : ;; pending accept miners
+     168                 :            : 
+     169                 :            : (define-read-only (get-all-data-miners-pending-accept (pending-miners-list (list 100 principal))) 
+     170                 :          0 : (map get-data-miner-pending-accept pending-miners-list))
+     171                 :            : 
+     172                 :            : (define-private (get-data-miner-pending-accept (miner principal)) 
+     173                 :          0 : (begin 
+     174            [ - ]:          0 :   (asserts! (is-some (get value (map-get? map-is-pending {address: miner}))) err-not-pending)
+     175                 :          0 :   (ok 
+     176                 :            :     {
+     177                 :          0 :       miner: miner,
+     178                 :          0 :       remaining-blocks-until-join: (get-remaining-blocks-until-join)
+     179                 :            :     })))
+     180                 :            : 
+     181                 :            : (define-read-only (get-remaining-blocks-until-join)
+     182                 :          0 :   (if (> blocks-to-pass (- block-height (var-get last-join-done)))
+     183            [ - ]:          0 :     (- blocks-to-pass (- block-height (var-get last-join-done)))
+     184            [ - ]:          0 :     u0
+     185                 :            :   )
+     186                 :            : )
+     187                 :            : 
+     188                 :            : ;; blocks number as miner
+     189                 :            : (define-read-only (get-all-data-miners-blocks (local-miners-list (list 100 principal))) 
+     190                 :          0 : (map get-data-miner-blocks local-miners-list))
+     191                 :            : (define-private (get-data-miner-blocks (miner principal)) 
+     192                 :          0 : (begin 
+     193            [ - ]:          0 :   (asserts! (is-some (get value (map-get? map-is-miner {address: miner}))) err-not-in-miner-map)
+     194            [ - ]:          0 :   (asserts! (is-some (get block-height (map-get? map-block-joined {address: miner}))) err-no-join-block-data)
+     195                 :          0 :   (ok 
+     196                 :            :     {
+     197                 :          0 :       miner: miner,
+     198                 :          0 :       blocks-as-miner: (- block-height (unwrap-panic (get block-height (map-get? map-block-joined {address: miner}))))
+     199                 :            :     })))
+     200                 :            : 
+     201                 :            : ;; miners in pool
+     202                 :            : 
+     203                 :            : (define-read-only (get-all-data-miners-in-pool (local-miners-list (list 100 principal))) 
+     204                 :          0 : (map get-data-miner-in-pool local-miners-list))
+     205                 :            : 
+     206                 :            : (define-private (get-data-miner-in-pool (miner principal)) 
+     207                 :          0 : (begin 
+     208            [ - ]:          0 :   (asserts! (is-some (get value (map-get? map-is-miner {address: miner}))) err-not-in-miner-map)
+     209            [ - ]:          0 :   (asserts! (is-some (get block-height (map-get? map-block-joined {address: miner}))) err-no-join-block-data)
+     210                 :          0 :   (ok 
+     211                 :            :     {
+     212                 :          0 :       blocks-as-miner: (- block-height (unwrap-panic (get block-height (map-get? map-block-joined {address: miner})))),
+     213                 :          0 :       was-blacklist: (default-to false (get value (map-get? map-blacklist {address: miner}))),
+     214                 :          0 :       warnings: (default-to u0 (get value (map-get? map-warnings {address: miner}))),
+     215                 :            :     })))
+     216                 :            : 
+     217                 :            : ;; total withdrawals
+     218                 :            : 
+     219                 :            : (define-read-only (get-all-data-total-withdrawals (local-miners-list (list 100 principal))) 
+     220                 :          0 : (map get-data-miner-withdrawals local-miners-list))
+     221                 :            : 
+     222                 :            : (define-private (get-data-miner-withdrawals (miner principal)) 
+     223                 :          0 : (begin
+     224                 :          0 :   (ok 
+     225                 :          0 :     (default-to u0 (get value (map-get? map-total-withdraw {address: miner})))
+     226                 :            :     )))
+     227                 :            : 
+     228                 :            : ;; notifier
+     229                 :            : 
+     230                 :            : (define-read-only (get-data-notifier-election-process)
+     231                 :            : {
+     232                 :          0 :   vote-status: (var-get notifier-vote-active), 
+     233                 :          0 :   election-blocks-remaining:
+     234                 :          0 :     (if (<= (var-get notifier-vote-end-block) block-height)
+     235            [ - ]:          0 :     u0
+     236            [ - ]:          0 :     (- (var-get notifier-vote-end-block) block-height))})
+     237                 :            : 
+     238                 :            : (define-read-only (get-all-data-notifier-voter-miners (voter-miners-list (list 100 principal)))
+     239                 :          0 : (map get-data-notifier-voter-miner voter-miners-list))
+     240                 :            : 
+     241                 :            : (define-private (get-data-notifier-voter-miner (miner principal)) 
+     242                 :          0 : (begin 
+     243            [ - ]:          0 :   (asserts! (is-some (get miner-voted (map-get? map-voted-update-notifier {miner-who-voted: miner}))) err-not-voted)
+     244                 :          0 :   (ok 
+     245                 :            :     {
+     246                 :          0 :       miner: miner,
+     247                 :          0 :       voted-notifier: 
+     248                 :          0 :       (unwrap-panic (get miner-voted (map-get? map-voted-update-notifier {miner-who-voted: miner}))) 
+     249                 :            :     })))
+     250                 :            : 
+     251                 :            : ;; balances 
+     252                 :            : 
+     253                 :            : (define-read-only (was-block-claimed (given-block-height uint)) 
+     254                 :          0 :   (if 
+     255                 :          0 :     (is-none (get claimed (map-get? claimed-rewards {block-number: given-block-height}))) 
+     256            [ - ]:          0 :     false 
+     257            [ - ]:          0 :     (if 
+     258                 :          0 :       (unwrap-panic (get claimed (map-get? claimed-rewards {block-number: given-block-height}))) 
+     259            [ - ]:          0 :       true 
+     260            [ - ]:          0 :       false)))
+     261                 :            : 
+     262                 :            : ;; BALANCES FLOW
+     263                 :            : 
+     264                 :            : ;; read balance
+     265                 :            : (define-read-only (get-balance (address principal)) 
+     266                 :        507 : (map-get? balance address))
+     267                 :            : 
+     268                 :            : (define-read-only (get-miner-btc-address (miner-address principal))
+     269                 :          0 :   (map-get? btc-address {address: miner-address}))
+     270                 :            : 
+     271                 :            : (define-public (set-my-btc-address (new-btc-address {hashbytes: (buff 20), version: (buff 1)})) 
+     272                 :          0 :   (ok (map-set btc-address {address: tx-sender} {btc-address: new-btc-address})))
+     273                 :            : 
+     274                 :            : ;; deposit funds
+     275                 :            : (define-public (deposit-stx (amount uint))
+     276                 :        699 : (let ((sender tx-sender)
+     277                 :        699 :       (balance-sender (map-get? balance sender)))
+     278                 :        699 :   (try! (stx-transfer? amount tx-sender (as-contract tx-sender)))
+     279                 :        699 :   (if (is-none balance-sender) 
+     280            [ + ]:        699 :     (ok (map-set balance sender amount))
+     281            [ - ]:          0 :     (ok (map-set balance sender (+ (unwrap! balance-sender err-missing-balance) amount))))))
+     282                 :            : 
+     283                 :            : ;; withdraw funds
+     284                 :            : (define-public (withdraw-stx (amount uint)) 
+     285                 :        504 : (let ((receiver tx-sender)) 
+     286            [ + ]:        504 :   (asserts! (>= (unwrap! (map-get? balance receiver) err-missing-balance) amount) err-insufficient-balance)
+     287                 :        304 :   (try! (as-contract (stx-transfer? amount (as-contract tx-sender) receiver)))
+     288                 :        304 :   (if 
+     289                 :        304 :     (is-some (get value (map-get? map-total-withdraw {address: receiver}))) 
+     290            [ - ]:          0 :     (map-set map-total-withdraw {address: receiver} {value: (+ (unwrap-panic (get value (map-get? map-total-withdraw {address: receiver}))) amount)}) 
+     291            [ + ]:        304 :     (map-set map-total-withdraw {address: receiver} {value: amount}))
+     292                 :        304 :   (ok (map-set balance receiver (- (unwrap! (map-get? balance receiver) err-missing-balance) amount)))))
+     293                 :            : 
+     294                 :            : ;; exchange funds
+     295                 :            : (define-public (set-auto-exchange (new-value bool)) 
+     296                 :          0 :   (ok (map-set auto-exchange {address: tx-sender} {value: new-value})))
+     297                 :            : 
+     298                 :            : (define-read-only (get-auto-exchange (address principal)) 
+     299                 :          0 :   (map-get? auto-exchange {address: address}))
+     300                 :            : 
+     301                 :            : (define-public (reward-distribution (block-number uint))
+     302                 :          4 : (begin 
+     303            [ - ]:          4 :   (asserts! (< block-number block-height) err-block-height-invalid) ;; +100  ? 
+     304            [ + ]:          4 :   (asserts! (is-none (get claimed (map-get? claimed-rewards {block-number: block-number}))) err-already-distributed)
+     305                 :          3 :   (let ((miners-list-at-reward-block 
+     306                 :          3 :           (at-block (unwrap! (get-block-info? id-header-hash block-number) err-cant-unwrap-rewarded-block) (var-get miners-list)))
+     307                 :          3 :         (block-reward (get-reward-at-block block-number))
+     308                 :          3 :         (current-reward (var-get reward))
+     309                 :          3 :         (current-miners-list-len (len miners-list-at-reward-block))
+     310                 :          3 :         (distribution-change-funds (mod current-reward current-miners-list-len))
+     311                 :          3 :         (current-change-funds (var-get reward-change-funds)))
+     312                 :          3 :     (map-set claimed-rewards {block-number: block-number} {claimed: true})
+     313                 :          3 :     (var-set miners-list-len-at-reward-block current-miners-list-len) 
+     314                 :          3 :     (var-set reward (unwrap-panic (get reward block-reward)))
+     315                 :          3 :     (var-set total-rewarded (+ (var-get total-rewarded) (var-get reward)))
+     316                 :          3 :     (var-set blocks-won (+ (var-get blocks-won) u1))
+     317                 :          3 :     (var-set reward-change-funds (+ current-change-funds distribution-change-funds))
+     318                 :          3 :     (map distribute-reward-each-miner miners-list-at-reward-block)
+     319                 :          3 :     (ok true))))
+     320                 :            : 
+     321                 :            : (define-private (distribute-reward-each-miner (miner principal)) 
+     322                 :        302 : (let ((miner-balance (default-to u0 (map-get? balance miner)))
+     323                 :        302 :       (current-reward (var-get reward))
+     324                 :        302 :       (current-change-funds (var-get reward-change-funds))
+     325                 :        302 :       (current-miners-list-len (var-get miners-list-len-at-reward-block))
+     326                 :        302 :       (distributed-amount (/ current-reward current-miners-list-len))
+     327                 :            :       ) 
+     328                 :            :   
+     329                 :        302 :   (map-set balance miner 
+     330                 :        302 :     (+ miner-balance distributed-amount))))
+     331                 :            : 
+     332                 :            : (define-private (flush-change-each-miner (miner principal)) 
+     333                 :          0 : (let ((miner-balance (default-to u0 (map-get? balance miner)))
+     334                 :          0 :       (current-reward (var-get reward))
+     335                 :          0 :       (current-change-funds (var-get reward-change-funds))
+     336                 :          0 :       (current-miners-list-len (var-get miners-list-len-at-reward-block))
+     337                 :          0 :       (distributed-amount (/ current-reward current-miners-list-len))
+     338                 :          0 :       (distribution-change-funds (mod current-reward current-miners-list-len))) 
+     339                 :          0 :   (var-set reward-change-funds (+ current-change-funds distribution-change-funds))
+     340                 :          0 :   (map-set balance miner 
+     341                 :          0 :     (+ miner-balance distributed-amount))))
+     342                 :            : 
+     343                 :            : ;; JOINING FLOW
+     344                 :            : 
+     345                 :            : (define-public (ask-to-join (my-btc-address {hashbytes: (buff 20), version: (buff 1)}))
+     346                 :       2962 : (begin 
+     347            [ - ]:       2962 :   (asserts! (not (check-is-miner-now contract-caller)) err-already-joined) 
+     348            [ - ]:       2962 :   (asserts! (not (check-is-waiting-now contract-caller)) err-already-asked-to-join) 
+     349                 :       2962 :   (map-set map-block-asked-to-join {address: tx-sender} {value: block-height})
+     350                 :       2962 :   (map-set btc-address {address: tx-sender} {btc-address: my-btc-address})
+     351                 :       2962 :   (var-set waiting-list (unwrap-panic (as-max-len? (concat (var-get waiting-list) (list tx-sender)) u300)))
+     352                 :       2962 :   (map-set map-is-waiting {address: tx-sender} {value: true})
+     353                 :       2962 :   (ok true)))
+     354                 :            : 
+     355                 :            : (define-public (vote-positive-join-request (miner-to-vote principal))
+     356                 :       2771 : (begin
+     357            [ + ]:       2771 :   (asserts! (check-is-waiting-now miner-to-vote) err-not-asked-to-join) ;; map_is_waiting
+     358            [ + ]:       2769 :     (asserts! (unwrap! (check-is-miner-when-requested-join miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission)
+     359            [ - ]:       2768 :     (asserts! (has-voted-join miner-to-vote) err-already-voted) ;; O(1)
+     360                 :       2768 :     (map-set map-join-request-voter 
+     361                 :       2768 :       {miner-to-vote: miner-to-vote, voter: tx-sender} 
+     362                 :       2768 :       {value: true})
+     363                 :       2768 :     (if (is-some (get value (map-get? map-votes-accept-join {address: miner-to-vote}))) 
+     364            [ + ]:        208 :       (map-set map-votes-accept-join {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-accept-join {address: miner-to-vote}))) u1)})
+     365            [ + ]:       2560 :       (map-set map-votes-accept-join {address: miner-to-vote} {value: u1}))
+     366                 :       2768 :     (ok true)))
+     367                 :            : 
+     368                 :            : (define-public (vote-negative-join-request (miner-to-vote principal))
+     369                 :        497 : (begin
+     370            [ - ]:        497 :   (asserts! (check-is-waiting-now miner-to-vote) err-not-asked-to-join)
+     371            [ + ]:        497 :     (asserts! (unwrap! (check-is-miner-when-requested-join miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission)
+     372            [ + ]:        496 :     (asserts! (has-voted-join miner-to-vote) err-already-voted)    
+     373                 :        495 :     (map-set map-join-request-voter 
+     374                 :        495 :       {miner-to-vote: miner-to-vote, voter: tx-sender} 
+     375                 :        495 :       {value: true})
+     376                 :        495 :     (if (is-some (get value (map-get? map-votes-reject-join {address: miner-to-vote}))) 
+     377            [ + ]:         92 :       (map-set map-votes-reject-join {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-reject-join {address: miner-to-vote}))) u1)})
+     378            [ + ]:        403 :       (map-set map-votes-reject-join {address: miner-to-vote} {value: u1}))  
+     379                 :        495 :     (some
+     380                 :        495 :       (if (is-vote-rejected-join (unwrap-panic (get value (map-get? map-votes-reject-join {address: miner-to-vote}))) (unwrap-panic (get-k-at-block-asked-to-join miner-to-vote)) (unwrap-panic (get-n-at-block-asked-to-join miner-to-vote)))
+     381            [ + ]:        402 :         (reject-miner-in-pool miner-to-vote) 
+     382            [ + ]:         93 :         false))
+     383                 :        495 :     (ok true)))
+     384                 :            : 
+     385                 :            : (define-public (quit-waiting-list) 
+     386                 :          0 :   (begin 
+     387            [ - ]:          0 :     (asserts! (check-is-waiting-now contract-caller) err-not-asked-to-join)
+     388                 :          0 :     (asserts! 
+     389                 :          0 :       (<= 
+     390                 :          0 :         blocks-to-pass
+     391                 :          0 :         (- block-height 
+     392                 :          0 :           (default-to block-height 
+     393                 :          0 :             (get value 
+     394            [ - ]:          0 :               (map-get? map-block-asked-to-join {address: contract-caller}))))) err-more-blocks-to-pass)
+     395                 :          0 :     (let ((remove-result (unwrap-panic (remove-principal-waiting-list contract-caller))))
+     396                 :          0 :       (var-set miner-to-remove-votes-join contract-caller)
+     397                 :          0 :       (var-set waiting-list remove-result)
+     398                 :          0 :       (map-delete map-is-waiting {address: contract-caller})
+     399                 :          0 :       (ok (clear-votes-map-join-vote contract-caller)))))
+     400                 :            : 
+     401                 :            : (define-private (accept-miner-in-pool (miner principal)) 
+     402                 :       2559 : (begin 
+     403                 :       2559 :   (let ((pending-accept-result (as-max-len? (concat (var-get pending-accept-list) (list miner)) u300)))
+     404            [ - ]:       2559 :   (asserts! (is-some pending-accept-result) err-list-length-exceeded) ;; O(1) 
+     405                 :       2559 :   (map-set map-warnings {address: miner} {value: u0})
+     406                 :       2559 :   (map-set balance miner u0)
+     407                 :       2559 :   (var-set miner-to-remove-votes-join miner)
+     408                 :       2559 :   (var-set waiting-list (unwrap-panic (as-max-len? (unwrap-panic (remove-principal-waiting-list miner)) u300))) ;; O(N)
+     409                 :       2559 :   (map-delete map-is-waiting {address: miner})
+     410                 :       2559 :   (map-set map-is-pending {address: miner} {value: true})
+     411                 :       2559 :   (clear-votes-map-join-vote miner)
+     412                 :       2559 :   (ok (var-set pending-accept-list (unwrap-panic pending-accept-result))))))
+     413                 :            : 
+     414                 :            : (define-private (reject-miner-in-pool (miner principal)) 
+     415                 :        402 : (begin 
+     416                 :        402 :   (let ((remove-result (unwrap-panic (remove-principal-waiting-list miner))))
+     417                 :        402 :     (var-set miner-to-remove-votes-join miner)
+     418                 :        402 :     (var-set waiting-list remove-result)
+     419                 :        402 :     (map-delete map-is-waiting {address: miner})
+     420                 :        402 :     (clear-votes-map-join-vote miner)
+     421                 :        402 :     true)))
+     422                 :            : 
+     423                 :            : (define-private (clear-votes-map-join-vote (miner principal)) 
+     424                 :       2961 : (begin 
+     425                 :       2961 :   (map-delete map-votes-accept-join {address: (var-get miner-to-remove-votes-join)})
+     426                 :       2961 :   (map-delete map-votes-reject-join {address: (var-get miner-to-remove-votes-join)})
+     427                 :       2961 :   (map-delete map-block-asked-to-join {address: (var-get miner-to-remove-votes-join)})
+     428                 :       2961 :   (map remove-map-record-join-vote (var-get miners-list))))
+     429                 :            : 
+     430                 :            : (define-private (remove-map-record-join-vote (miner principal))
+     431                 :       2971 : (if (is-some (map-get? map-join-request-voter {miner-to-vote: (var-get miner-to-remove-votes-join), voter: miner})) 
+     432            [ + ]:       2964 :   (map-delete map-join-request-voter {miner-to-vote: (var-get miner-to-remove-votes-join), voter: miner})
+     433            [ + ]:          7 :   false))
+     434                 :            : 
+     435                 :            : (define-private (is-in-voters-list (miner principal) (voters-list (list 300 principal))) 
+     436                 :          0 : (is-some (index-of? voters-list miner)))
+     437                 :            : 
+     438                 :            : (define-private (has-voted-join (miner principal)) 
+     439                 :       3264 : (not (if (is-some (get value (map-get? map-join-request-voter {miner-to-vote: miner, voter: tx-sender})))
+     440            [ + ]:          1 :           (unwrap-panic (get value (map-get? map-join-request-voter {miner-to-vote: miner, voter: tx-sender})))
+     441            [ + ]:       3263 :           false)))
+     442                 :            : 
+     443                 :            : (define-public (try-enter-pool)
+     444                 :       2958 : (begin 
+     445            [ + ]:       2958 :   (asserts! (is-some (get value (map-get? map-votes-accept-join {address: tx-sender}))) err-not-asked-to-join)
+     446                 :       2559 :   (if (is-vote-accepted (unwrap-panic (get value (map-get? map-votes-accept-join {address: tx-sender}))) (unwrap-panic (get-k-at-block-asked-to-join tx-sender)))
+     447            [ + ]:       2559 :     (accept-miner-in-pool tx-sender) 
+     448            [ - ]:          0 :     (ok false))))
+     449                 :            : 
+     450                 :            : (define-public (add-pending-miners-to-pool) 
+     451                 :         31 : (begin
+     452                 :         31 :   (let ((len-pending-accept-list (len (var-get pending-accept-list))))
+     453            [ + ]:         31 :     (asserts! (not (is-eq len-pending-accept-list u0)) err-no-pending-miners)
+     454            [ + ]:         24 :     (asserts! (x-blocks-passed blocks-to-pass) err-more-blocks-to-pass)
+     455                 :         21 :     (map add-miner-to-pool (var-get pending-accept-list))
+     456            [ - ]:         21 :     (asserts! (is-some (as-max-len? (concat (var-get miners-list) (var-get pending-accept-list)) u300)) err-list-length-exceeded)
+     457                 :         21 :     (var-set miners-list (unwrap-panic (as-max-len? (concat (var-get miners-list) (var-get pending-accept-list)) u300)))
+     458                 :         21 :     (var-set n (+ (var-get n) len-pending-accept-list))
+     459                 :         21 :     (var-set pending-accept-list (list ))
+     460                 :         21 :     (var-set last-join-done block-height)
+     461                 :         21 :     (some (update-threshold))
+     462                 :         21 :     (ok true))))
+     463                 :            : 
+     464                 :            : (define-private (update-threshold) 
+     465                 :         75 : (let ((n-now (var-get n))) 
+     466                 :         75 :   (if 
+     467                 :         75 :     (or 
+     468            [ + ]:         75 :       (is-eq n-now u1) 
+     469            [ + ]:         75 :       (is-eq n-now u2)) 
+     470            [ + ]:          3 :     (var-set k u1)
+     471            [ + ]:         72 :     (var-set k (/ (* (var-get k-percentage) (- n-now u1)) u100)))))
+     472                 :            : 
+     473                 :            : (define-private (add-miner-to-pool (miner principal))
+     474                 :       2559 : (begin 
+     475                 :       2559 :   (map-delete map-is-pending {address: miner})
+     476                 :       2559 :   (map-set map-is-miner {address: miner} {value: true})
+     477                 :       2559 :   (map-set map-block-joined {address: miner} {block-height: block-height})
+     478                 :       2559 :   (ok true)))
+     479                 :            : 
+     480                 :            : (define-private (x-blocks-passed (x uint)) 
+     481                 :         24 : (if (>= (- block-height (var-get last-join-done)) x)
+     482            [ + ]:         21 :   true
+     483            [ + ]:          3 :   false))
+     484                 :            : 
+     485                 :            : (define-private (get-k-at-block-asked-to-join (miner-to-vote principal))
+     486                 :       3056 : (let ((block-asked-to-join (get value (map-get? map-block-asked-to-join {address: miner-to-vote}))))
+     487                 :       3056 :   (begin 
+     488            [ - ]:       3056 :     (asserts! (is-some block-asked-to-join) err-not-asked-to-join)
+     489                 :       3056 :     (if 
+     490                 :       3056 :       (is-eq 
+     491                 :       3056 :         (unwrap-panic block-asked-to-join) 
+     492                 :       3056 :         block-height) 
+     493            [ + ]:          1 :       (ok (var-get k)) 
+     494            [ + ]:       3055 :       (at-block 
+     495                 :       3055 :       (unwrap-panic 
+     496                 :       3055 :         (get-block-info? id-header-hash 
+     497                 :       3055 :           (unwrap-panic block-asked-to-join))) 
+     498                 :       3055 :             (ok (var-get k)))))))
+     499                 :            : 
+     500                 :            : (define-private (get-n-at-block-asked-to-join (miner-to-vote principal)) 
+     501                 :        497 : (let ((block-asked-to-join (get value (map-get? map-block-asked-to-join {address: miner-to-vote}))))
+     502                 :        497 :   (begin 
+     503            [ - ]:        497 :     (asserts! (is-some block-asked-to-join) err-not-asked-to-join)
+     504                 :        497 :     (if 
+     505                 :        497 :       (is-eq 
+     506                 :        497 :         (unwrap-panic block-asked-to-join) block-height) 
+     507            [ + ]:          1 :       (ok (var-get n)) 
+     508            [ + ]:        496 :       (at-block  
+     509                 :        496 :       (unwrap-panic 
+     510                 :        496 :         (get-block-info? id-header-hash 
+     511                 :        496 :           (unwrap-panic block-asked-to-join))) 
+     512                 :        496 :             (ok (var-get n)))))))
+     513                 :            : 
+     514                 :            : ;; LEAVING FLOW
+     515                 :            : 
+     516                 :            : (define-public (leave-pool)
+     517                 :        405 : (begin 
+     518            [ - ]:        405 :   (asserts! (check-is-miner-now contract-caller) err-not-in-miner-map)
+     519            [ + ]:        405 :   (asserts! (not (is-eq (var-get notifier) contract-caller)) err-currently-notifier)
+     520                 :        404 :   (let ((remove-result (unwrap-panic (remove-principal-miners-list tx-sender)))
+     521         [ +  - ]:        404 :         (new-k-percentage (if (> (var-get n) u2) (/ (* (var-get k) u100) (- (var-get n) u2)) u100))) 
+     522                 :            :         ;; if n<=2, set a value for new-k-percentage > k-critical to make sure threshold is updated
+     523                 :        404 :     (some (var-set miners-list remove-result))
+     524                 :        404 :     (var-set n (- (var-get n) u1))
+     525                 :        404 :     (map-set map-is-miner {address: tx-sender} {value: false})
+     526                 :        404 :     (if 
+     527                 :        404 :       (is-some (index-of? (var-get proposed-removal-list) tx-sender)) 
+     528            [ - ]:          0 :       (var-set proposed-removal-list (unwrap-panic (remove-principal-proposed-removal-list tx-sender))) 
+     529            [ + ]:        404 :       true)
+     530                 :        404 :     (if 
+     531                 :        404 :       (>= new-k-percentage (var-get k-critical)) 
+     532            [ + ]:         51 :       (if 
+     533                 :         51 :         (> (var-get n) u1) 
+     534            [ + ]:         51 :         (some (update-threshold)) 
+     535            [ - ]:          0 :         (if 
+     536                 :          0 :           (is-eq (var-get n) u1) 
+     537            [ - ]:          0 :           (some (var-set k u1)) 
+     538            [ - ]:          0 :           (some (var-set k u0))))
+     539            [ + ]:        353 :       none)
+     540                 :        404 :     (ok true))))
+     541                 :            : 
+     542                 :            : ;; REMOVING FLOW
+     543                 :            : 
+     544                 :            : (define-public (propose-removal (miner-to-remove principal))
+     545                 :         13 : (begin 
+     546            [ - ]:         13 :   (asserts! (not (is-eq (var-get n) u1)) err-cant-remove-when-alone-in-pool)
+     547            [ - ]:         13 :   (asserts! (check-is-miner-now contract-caller) err-not-in-miner-map) 
+     548            [ - ]:         13 :   (asserts! (check-is-miner-now miner-to-remove) err-not-in-miner-map-miner-to-remove)
+     549            [ - ]:         13 :   (asserts! (not (check-is-proposed-for-removal-now miner-to-remove)) err-already-proposed-for-removal) 
+     550                 :         13 :   (map-set map-block-proposed-to-remove {address: miner-to-remove} {value: block-height})
+     551                 :         13 :   (map-set map-is-proposed-for-removal {address: miner-to-remove} {value: true})
+     552                 :         13 :   (var-set proposed-removal-list (unwrap! (as-max-len? (concat (var-get proposed-removal-list) (list miner-to-remove )) u300) err-list-length-exceeded))
+     553                 :         13 :   (ok true)))
+     554                 :            : 
+     555                 :            : (define-public (vote-positive-remove-request (miner-to-vote principal))
+     556                 :        225 : (begin
+     557            [ - ]:        225 :   (asserts! (not (is-eq contract-caller miner-to-vote)) err-cant-vote-himself)
+     558            [ + ]:        225 :   (asserts! (check-is-proposed-for-removal-now miner-to-vote) err-not-proposed-for-removal) ;; map_is_proposed_for_removal
+     559            [ - ]:        224 :   (asserts! (is-ok (get-k-at-block-proposed-removal miner-to-vote)) err-not-proposed-for-removal-proposal-block-missing)
+     560            [ - ]:        224 :     (asserts! (unwrap! (check-is-miner-when-requested-remove miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission)
+     561            [ - ]:        224 :     (asserts! (has-voted-remove miner-to-vote) err-already-voted) ;; O(1)
+     562                 :        224 :     (map-set map-remove-request-voter 
+     563                 :        224 :       {miner-to-vote: miner-to-vote, voter: tx-sender} 
+     564                 :        224 :       {value: true})
+     565                 :        224 :     (if (is-some (get value (map-get? map-votes-accept-removal {address: miner-to-vote}))) 
+     566            [ + ]:        217 :       (map-set map-votes-accept-removal {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-accept-removal  {address: miner-to-vote}))) u1)})
+     567            [ + ]:          7 :       (map-set map-votes-accept-removal {address: miner-to-vote} {value: u1}))
+     568                 :        224 :     (some
+     569                 :        224 :       (if (is-vote-accepted (unwrap-panic (get value (map-get? map-votes-accept-removal {address: miner-to-vote}))) (unwrap-panic (get-k-at-block-proposed-removal miner-to-vote)))
+     570            [ + ]:          7 :         (process-removal miner-to-vote)
+     571            [ + ]:        217 :         (ok false)))
+     572                 :        224 :     (ok true)))
+     573                 :            : 
+     574                 :            : (define-public (vote-negative-remove-request (miner-to-vote principal))
+     575                 :        135 : (begin
+     576            [ + ]:        135 :   (asserts! (not (is-eq contract-caller miner-to-vote)) err-cant-vote-himself)
+     577            [ + ]:        132 :   (asserts! (check-is-proposed-for-removal-now miner-to-vote) err-not-proposed-for-removal) ;; map_is_waiting
+     578            [ - ]:        124 :   (asserts! (is-ok (get-k-at-block-proposed-removal miner-to-vote)) err-not-proposed-for-removal-proposal-block-missing)
+     579            [ - ]:        124 :   (asserts! (unwrap! (check-is-miner-when-requested-remove miner-to-vote) err-cant-unwrap-check-miner) err-no-vote-permission)
+     580            [ + ]:        124 :   (asserts! (has-voted-remove miner-to-vote) err-already-voted) ;; O(1)
+     581                 :        121 :   (map-set map-remove-request-voter 
+     582                 :        121 :     {miner-to-vote: miner-to-vote, voter: tx-sender} 
+     583                 :        121 :     {value: true})
+     584                 :        121 :   (if (is-some (get value (map-get? map-votes-reject-removal {address: miner-to-vote}))) 
+     585            [ + ]:        116 :     (map-set map-votes-reject-removal {address: miner-to-vote} {value: (+ (unwrap-panic (get value (map-get? map-votes-reject-removal {address: miner-to-vote}))) u1)})
+     586            [ + ]:          5 :     (map-set map-votes-reject-removal {address: miner-to-vote} {value: u1}))
+     587                 :        121 :   (some
+     588                 :        121 :     (if (is-vote-rejected-remove (unwrap-panic (get value (map-get? map-votes-reject-removal {address: miner-to-vote}))) (unwrap-panic (get-k-at-block-proposed-removal miner-to-vote)) (unwrap-panic (get-n-at-block-proposed-removal miner-to-vote)))
+     589            [ + ]:          5 :       (reject-removal miner-to-vote)
+     590            [ + ]:        116 :       (ok false)))
+     591                 :        121 :   (ok true)))
+     592                 :            : 
+     593                 :            : (define-public (quit-proposed-removal-list) 
+     594                 :          0 :   (begin 
+     595                 :          0 :     (asserts! 
+     596                 :          0 :       (<= 
+     597                 :          0 :         blocks-to-pass
+     598                 :          0 :         (- block-height 
+     599                 :          0 :           (default-to block-height 
+     600                 :          0 :             (get value 
+     601            [ - ]:          0 :               (map-get? map-block-proposed-to-remove {address: contract-caller}))))) err-more-blocks-to-pass)
+     602                 :          0 :     (let ((remove-result (unwrap-panic (remove-principal-proposed-removal-list contract-caller))))
+     603                 :          0 :       (var-set miner-to-remove-votes-remove contract-caller)
+     604                 :          0 :       (var-set proposed-removal-list remove-result)
+     605                 :          0 :       (map-delete map-is-proposed-for-removal {address: contract-caller})
+     606                 :          0 :       (ok (clear-votes-map-remove-vote contract-caller)))))
+     607                 :            : 
+     608                 :            : (define-private (process-removal (miner principal))
+     609                 :          7 : (begin 
+     610                 :          7 :   (let ((remove-result (unwrap-panic (remove-principal-miners-list miner)))
+     611         [ +  - ]:          7 :         (new-k-percentage (if (> (var-get n) u2) (/ (* (var-get k) u100) (- (var-get n) u2)) u100)))
+     612                 :          7 :     (some (var-set miners-list remove-result))
+     613                 :          7 :     (var-set miner-to-remove-votes-remove miner)
+     614                 :          7 :     (var-set n (- (var-get n) u1))
+     615                 :          7 :     (map-delete map-is-miner {address: miner})
+     616                 :          7 :     (map-set map-blacklist {address: miner} {value: true})
+     617                 :          7 :     (var-set proposed-removal-list (unwrap-panic (remove-principal-proposed-removal-list miner)))
+     618                 :          7 :     (clear-votes-map-remove-vote miner)
+     619                 :          7 :     (if (>= new-k-percentage (var-get k-critical))
+     620            [ + ]:          3 :       (if 
+     621                 :          3 :         (> (var-get n) u1) 
+     622            [ + ]:          3 :         (update-threshold) 
+     623            [ - ]:          0 :         (if 
+     624                 :          0 :           (is-eq (var-get n) u1) 
+     625            [ - ]:          0 :           (var-set k u1)
+     626            [ - ]:          0 :           (var-set k u0)))
+     627            [ + ]:          4 :       false)
+     628                 :          7 :     (ok true))))
+     629                 :            : 
+     630                 :            : (define-private (reject-removal (miner principal))
+     631                 :          5 : (begin 
+     632                 :          5 :   (var-set miner-to-remove-votes-remove miner)
+     633                 :          5 :   (var-set proposed-removal-list (unwrap-panic (remove-principal-proposed-removal-list miner)))
+     634                 :          5 :   (clear-votes-map-remove-vote miner)
+     635                 :          5 :   (ok true)))
+     636                 :            : 
+     637                 :            : (define-private (has-voted-remove (miner principal)) 
+     638                 :        348 : (not (if (is-some (get value (map-get? map-remove-request-voter {miner-to-vote: miner, voter: tx-sender})))
+     639            [ + ]:          3 :           (unwrap-panic (get value (map-get? map-remove-request-voter {miner-to-vote: miner, voter: tx-sender})))
+     640            [ + ]:        345 :           false
+     641                 :            :   )))
+     642                 :            : 
+     643                 :            : (define-private (clear-votes-map-remove-vote (miner principal)) 
+     644                 :         12 : (begin 
+     645                 :         12 :   (map-delete map-votes-accept-removal {address: (var-get miner-to-remove-votes-remove)})
+     646                 :         12 :   (map-delete map-votes-reject-removal {address: (var-get miner-to-remove-votes-remove)})
+     647                 :         12 :   (map-delete map-block-proposed-to-remove {address: (var-get miner-to-remove-votes-remove)})
+     648                 :         12 :   (map-delete map-is-proposed-for-removal {address: (var-get miner-to-remove-votes-remove)})
+     649                 :         12 :   (map remove-map-record-remove-vote (var-get miners-list))))
+     650                 :            : 
+     651                 :            : (define-private (remove-map-record-remove-vote (miner principal))
+     652                 :        674 : (if (is-some (map-get? map-remove-request-voter {miner-to-vote: (var-get miner-to-remove-votes-remove), voter: miner}))
+     653            [ + ]:        344 :   (map-delete map-remove-request-voter {miner-to-vote: (var-get miner-to-remove-votes-remove), voter: miner})
+     654            [ + ]:        330 :   false))
+     655                 :            : 
+     656                 :            : (define-private (get-k-at-block-proposed-removal (miner-to-vote principal)) 
+     657                 :        694 : (let ((block-proposed-to-remove (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote}))))
+     658                 :        694 :   (begin 
+     659            [ - ]:        694 :     (asserts! (is-some block-proposed-to-remove) err-not-proposed-for-removal)
+     660                 :        694 :     (if 
+     661                 :        694 :       (is-eq (unwrap-panic 
+     662                 :        694 :         block-proposed-to-remove) 
+     663                 :        694 :         block-height) 
+     664            [ + ]:          1 :       (ok (var-get k)) 
+     665            [ + ]:        693 :       (at-block 
+     666                 :        693 :         (unwrap-panic 
+     667                 :        693 :           (get-block-info? id-header-hash 
+     668                 :        693 :             (unwrap-panic block-proposed-to-remove))) 
+     669                 :        693 :               (ok (var-get k)))))))
+     670                 :            : 
+     671                 :            : (define-private (get-n-at-block-proposed-removal (miner-to-vote principal))
+     672                 :        122 : (let ((block-proposed-to-remove (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})))) 
+     673                 :        122 :   (begin 
+     674            [ - ]:        122 :     (asserts! (is-some block-proposed-to-remove) err-not-proposed-for-removal)
+     675                 :        122 :     (if 
+     676                 :        122 :       (is-eq (unwrap-panic 
+     677                 :        122 :         block-proposed-to-remove) 
+     678                 :        122 :         block-height) 
+     679            [ + ]:          1 :       (ok (var-get n)) 
+     680            [ + ]:        121 :       (at-block 
+     681                 :        121 :         (unwrap-panic 
+     682                 :        121 :           (get-block-info? id-header-hash 
+     683                 :        121 :             (unwrap-panic block-proposed-to-remove))) 
+     684                 :        121 :               (ok (var-get n)))))))
+     685                 :            : 
+     686                 :            : ;; UPDATE NOTIFIER
+     687                 :            : 
+     688                 :            : (define-public (start-vote-notifier) 
+     689                 :          3 : (begin 
+     690            [ - ]:          3 :   (asserts! (not (var-get notifier-vote-active)) err-vote-started-already)
+     691                 :          3 :   (var-set notifier-vote-start-block block-height)
+     692                 :          3 :   (var-set notifier-vote-end-block (+ (var-get notifier-vote-start-block) notifier-election-blocks-to-pass))
+     693                 :          3 :   (var-set notifier-vote-active true)
+     694                 :          3 :   (if (var-get notifier-previous-entries-removed) 
+     695            [ + ]:          3 :       (begin 
+     696                 :          3 :         (ok (var-set notifier-previous-entries-removed false))) 
+     697            [ - ]:          0 :       (end-vote-notifier-private))))
+     698                 :            : 
+     699                 :            : (define-public (end-vote-notifier) 
+     700                 :          3 : (begin 
+     701            [ - ]:          3 :   (asserts! (>= block-height (var-get notifier-vote-end-block)) err-voting-still-active)
+     702                 :          3 :   (var-set notifier-vote-active false)
+     703                 :          3 :   (end-vote-notifier-private)))
+     704                 :            : 
+     705                 :            : (define-private (end-vote-notifier-private) 
+     706                 :          3 : (begin 
+     707                 :          3 :   (unwrap! (get-max-votes-number-notifier) (err u99999))
+     708                 :          3 :   (if 
+     709                 :          3 :     (> 
+     710                 :          3 :       (var-get max-votes-notifier) 
+     711                 :          3 :       (/ (var-get k) u2)) 
+     712            [ + ]:          2 :     (var-set notifier (var-get max-voted-proposed-notifier))
+     713            [ + ]:          1 :     false)
+     714                 :          3 :   (delete-all-notifier-entries)
+     715                 :          3 :   (ok true)))
+     716                 :            : 
+     717                 :            : (define-private (get-max-votes-number-notifier) 
+     718                 :          3 : (ok (map compare-votes-number-notifier (var-get miners-list))))
+     719                 :            : 
+     720                 :            : (define-private (compare-votes-number-notifier (proposed-notifier principal)) 
+     721                 :        900 : (ok 
+     722                 :        900 : (if (is-some (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier})))
+     723            [ + ]:          3 :     (if (> (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier}))) (/ (var-get k) u2))
+     724            [ + ]:          2 :       (if 
+     725                 :          2 :         (> (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier}))) (var-get max-votes-notifier)) 
+     726            [ + ]:          2 :         (begin 
+     727                 :          2 :           (var-set max-votes-notifier (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier})))) 
+     728                 :          2 :           (var-set max-voted-proposed-notifier proposed-notifier))
+     729            [ - ]:          0 :         (if 
+     730                 :          0 :           (is-eq (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: proposed-notifier}))) (var-get max-votes-notifier)) 
+     731            [ - ]:          0 :           (if 
+     732                 :          0 :             (< 
+     733                 :          0 :               (unwrap-panic (get block-height (map-get? map-block-joined {address: proposed-notifier}))) 
+     734                 :          0 :               (unwrap-panic (get block-height (map-get? map-block-joined {address: (var-get max-voted-proposed-notifier)})))) 
+     735            [ - ]:          0 :             (begin 
+     736                 :          0 :                 (var-set max-voted-proposed-notifier proposed-notifier))
+     737            [ - ]:          0 :             false)
+     738            [ - ]:          0 :         false))
+     739            [ + ]:          1 :       false)
+     740            [ + ]:        897 :     false)))
+     741                 :            : 
+     742                 :            : (define-private (delete-all-notifier-entries) 
+     743                 :          3 : (begin 
+     744                 :          3 :   (var-set max-votes-notifier u0)
+     745                 :          3 :   (var-set max-voted-proposed-notifier (var-get notifier))
+     746                 :          3 :   (map delete-one-notifier-entry (var-get miners-list))
+     747                 :          3 :   (var-set notifier-previous-entries-removed true)))
+     748                 :            : 
+     749                 :            : (define-private (delete-one-notifier-entry (miner principal)) 
+     750                 :        900 : (begin 
+     751                 :        900 :   (map-delete map-voted-update-notifier {miner-who-voted: miner})
+     752                 :        900 :   (map-delete map-votes-notifier {voted-notifier: miner})))
+     753                 :            : 
+     754                 :            : (define-public (vote-notifier (voted-notifier principal)) 
+     755                 :        352 : (begin 
+     756    [ - ][ +  + ]:        352 :   (asserts! (and (is-some (get value (map-get? map-is-miner {address: voted-notifier}))) (unwrap-panic (get value (map-get? map-is-miner {address: voted-notifier})))) err-not-in-miner-map)
+     757    [ - ][ +  + ]:        352 :   (asserts! (and (is-some (get value (map-get? map-is-miner {address: contract-caller}))) (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller})))) err-no-vote-permission)
+     758            [ + ]:        352 :   (asserts! (var-get notifier-vote-active) err-not-voting-period)
+     759            [ - ]:        351 :   (asserts! (not (is-eq contract-caller voted-notifier)) err-cant-vote-himself)
+     760            [ - ]:        351 :   (asserts! (< block-height (var-get notifier-vote-end-block)) err-not-voting-period)
+     761            [ - ]:        351 :   (asserts! (is-none (get miner-voted (map-get? map-voted-update-notifier {miner-who-voted: tx-sender}))) err-already-voted)
+     762                 :        351 :   (map-set map-voted-update-notifier {miner-who-voted: tx-sender} {miner-voted: voted-notifier})
+     763                 :        351 :   (if (is-none (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) 
+     764            [ + ]:          3 :     (map-set map-votes-notifier {voted-notifier: voted-notifier} {votes-number: u1}) 
+     765            [ + ]:        348 :     (map-set map-votes-notifier {voted-notifier: voted-notifier} {votes-number: (+ (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) u1)}))
+     766                 :        351 :   (try! 
+     767                 :        351 :     (if (is-vote-accepted (unwrap-panic (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier}))) (var-get k)) 
+     768            [ + ]:          1 :     (begin 
+     769                 :          1 :       (var-set notifier voted-notifier)
+     770                 :          1 :       (var-set notifier-vote-end-block block-height)
+     771                 :          1 :       (var-set notifier-vote-active false)
+     772                 :          1 :       (end-vote-notifier))
+     773            [ + ]:        350 :     (ok false)))
+     774                 :        351 : (ok true)))
+     775                 :            : 
+     776                 :            : ;; WARNING FLOW
+     777                 :            : 
+     778                 :            : (define-public (warn-miner (miner principal)) 
+     779                 :          0 : (begin 
+     780                 :          0 : (let ((incremented-value 
+     781                 :          0 :       (if 
+     782                 :          0 :         (is-some (get value (map-get? map-warnings {address: miner}))) 
+     783            [ - ]:          0 :         (+ (unwrap-panic (get value (map-get? map-warnings {address: miner}))) u1) 
+     784            [ - ]:          0 :         u1))) 
+     785            [ - ]:          0 :   (asserts! (is-eq contract-caller (var-get notifier)) err-only-notifier) 
+     786                 :          0 :   (asserts! 
+     787                 :          0 :     (not (and 
+     788            [ - ]:          0 :       (is-none (at-block (unwrap! (get-block-info? id-header-hash (- block-height u1)) err-cant-unwrap-block-info) (get value (map-get? map-warnings {address: miner}))))
+     789            [ - ]:          0 :       (>= incremented-value u2))) 
+     790            [ - ]:          0 :     err-one-warning-per-block)
+     791                 :          0 :   (asserts! 
+     792                 :          0 :     (not 
+     793                 :          0 :       (>= 
+     794                 :          0 :         (- 
+     795                 :          0 :           incremented-value
+     796                 :          0 :           (unwrap! (at-block (unwrap! (get-block-info? id-header-hash (- block-height u1)) err-cant-unwrap-block-info) (get value (map-get? map-warnings {address: miner}))) err-cant-unwrap-block-info)) 
+     797                 :          0 :         u2)) 
+     798            [ - ]:          0 :     err-one-warning-per-block)
+     799                 :          0 :     (ok 
+     800                 :          0 :       (if 
+     801                 :          0 :         (is-some (get value (map-get? map-warnings {address: miner}))) 
+     802            [ - ]:          0 :         (map-set map-warnings {address: miner} {value: (+ (unwrap-panic (get value (map-get? map-warnings {address: miner}))) u1)})
+     803            [ - ]:          0 :         (map-set map-warnings {address: miner} {value: u1}))))))
+     804                 :            : 
+     805                 :            : ;; ELECTION FUNCTIONS
+     806                 :            : 
+     807                 :            : (define-private (is-vote-accepted (votes-number uint) (k-local uint))
+     808                 :       3134 : (if 
+     809                 :       3134 :   (is-eq k-local u0) ;; k is 0 for n=1, n=2 
+     810            [ - ]:          0 :     (>= votes-number u1) 
+     811            [ + ]:       3134 :     (>= votes-number k-local)))
+     812                 :            : 
+     813                 :            : (define-private (is-democratic-vote-accepted-notifier (votes-number uint) (k-local uint))
+     814                 :          0 : (if 
+     815                 :          0 :   (is-eq k-local u0) ;; k is 0 for n=1, n=2 
+     816            [ - ]:          0 :     (>= votes-number u1) 
+     817            [ - ]:          0 :     (>= votes-number (/ k-local u2))))
+     818                 :            : 
+     819                 :            : (define-private (is-vote-rejected-join (votes-number uint) (k-local uint) (n-local uint))
+     820                 :        495 : (if 
+     821                 :        495 :   (is-eq n-local u1) 
+     822            [ + ]:        400 :   (>= votes-number u1) 
+     823            [ + ]:         95 :   (if (is-eq n-local u2) 
+     824            [ + ]:          2 :     (>= votes-number u2) 
+     825            [ + ]:         93 :     (>= votes-number (+ (- n-local k-local) u1)))))
+     826                 :            : 
+     827                 :            : 
+     828                 :            : (define-private (is-vote-rejected-remove (votes-number uint) (k-local uint) (n-local uint))
+     829                 :        121 : (if 
+     830                 :        121 :   (is-eq n-local u2) 
+     831            [ - ]:          0 :   (>= votes-number u1) 
+     832            [ + ]:        121 :   (if (is-eq n-local u3)
+     833            [ + ]:          2 :     (>= votes-number u2)
+     834            [ + ]:        119 :     (>= votes-number (+ (- n-local k-local) u1)))))
+     835                 :            : 
+     836                 :            : (define-private (is-vote-rejected-notifier (votes-number uint) (k-local uint) (n-local uint))
+     837                 :          0 : (if (is-eq n-local u2) 
+     838            [ - ]:          0 :   (>= votes-number u1) 
+     839            [ - ]:          0 :   (if (is-eq n-local u3)
+     840            [ - ]:          0 :     (>= votes-number u2)
+     841            [ - ]:          0 :     (>= votes-number (+ (- n-local k-local) u1)))))
+     842                 :            : 
+     843                 :            : ;; RECOVERING UNASSIGNED FUNDS FUNCTIONS
+     844                 :            : 
+     845                 :            : ;; A public function each miner can call in order to recover the 
+     846                 :            : ;; undistributed rewards caused by dividing rewards to stackers
+     847                 :            : (define-public (flush-change)
+     848                 :          0 : (let ((total-change-funds (var-get reward-change-funds))
+     849                 :          0 :       (current-miners-list-len (len (var-get miners-list)))
+     850                 :          0 :       (distributed-change-funds (/ total-change-funds current-miners-list-len))
+     851                 :          0 :       (change-after-flushing (mod total-change-funds current-miners-list-len))) 
+     852            [ - ]:          0 :   (asserts! (check-is-miner-now contract-caller) err-not-in-miner-map)
+     853                 :          0 :   (var-set temp-distributed-change-funds distributed-change-funds)
+     854                 :          0 :   (var-set temp-change-after-flushing change-after-flushing)
+     855                 :          0 :   (map flush-distribution-one-miner (var-get miners-list))
+     856                 :          0 :   (ok   
+     857                 :          0 :     (var-set reward-change-funds 
+     858                 :          0 :       (- 
+     859                 :          0 :         total-change-funds 
+     860                 :          0 :         (* 
+     861                 :          0 :           current-miners-list-len 
+     862                 :          0 :           distributed-change-funds))))))
+     863                 :            : 
+     864                 :            : (define-private (flush-distribution-one-miner (miner principal))
+     865                 :          0 : (let ((miner-balance (default-to u0 (map-get? balance miner)))
+     866                 :          0 :       (total-change-funds (var-get reward-change-funds))
+     867                 :          0 :       (current-miners-list-len (len (var-get miners-list)))
+     868                 :          0 :       (distributed-change-funds (/ total-change-funds current-miners-list-len)))
+     869                 :          0 :   (map-set balance miner 
+     870                 :          0 :     (+ 
+     871                 :          0 :       miner-balance 
+     872                 :          0 :       (var-get temp-distributed-change-funds)))))
+     873                 :            : 
+     874                 :            : ;; LIST PROCESSING FUNCTIONS
+     875                 :            : 
+     876                 :            : (define-private (remove-principal-waiting-list (miner principal))
+     877                 :       2961 : (begin
+     878                 :       2961 :     (var-set waiting-list-miner-to-remove miner) 
+     879                 :       2961 :     (ok (filter is-principal-in-waiting-list (var-get waiting-list))))) 
+     880                 :            : 
+     881                 :            : (define-private (remove-principal-pending-accept-list (miner principal))
+     882                 :          0 : (begin 
+     883                 :          0 :     (var-set waiting-list-miner-to-remove miner) 
+     884                 :          0 :     (ok (filter is-principal-in-pending-accept-list (var-get pending-accept-list)))))
+     885                 :            : 
+     886                 :            : (define-private (remove-principal-miners-list (miner principal))
+     887                 :        411 : (begin
+     888                 :        411 :   (var-set miners-list-miner-to-remove miner) 
+     889                 :        411 :   (ok (filter is-principal-in-miners-list (var-get miners-list)))))
+     890                 :            : 
+     891                 :            : (define-private (remove-principal-proposed-removal-list (miner principal))
+     892                 :         12 : (begin
+     893                 :         12 :   (var-set proposed-removal-list-miner-to-remove miner) 
+     894                 :         12 :   (ok (filter is-principal-in-proposed-removal-list (var-get proposed-removal-list)))))
+     895                 :            : 
+     896                 :            : ;; MINER STATUS FUNCTIONS
+     897                 :            : 
+     898                 :            : (define-private (check-is-miner-when-requested-join (miner-to-vote principal))
+     899                 :       3266 : (ok 
+     900                 :       3266 :   (if 
+     901                 :       3266 :     (is-some 
+     902                 :       3266 :       (if 
+     903                 :       3266 :         (is-eq  
+     904                 :       3266 :           (unwrap! (get value (map-get? map-block-asked-to-join {address: miner-to-vote})) err-cant-unwrap-asked-to-join) 
+     905                 :       3266 :           block-height)
+     906            [ - ]:          0 :         (get value (map-get? map-is-miner {address: contract-caller})) 
+     907            [ + ]:       3266 :         (at-block 
+     908                 :       3266 :           (unwrap! 
+     909                 :       3266 :             (get-block-info? id-header-hash 
+     910                 :       3266 :               (unwrap! (get value (map-get? map-block-asked-to-join {address: miner-to-vote})) err-cant-unwrap-asked-to-join)) 
+     911                 :       3266 :           err-cant-unwrap-block-info) 
+     912                 :       3266 :           (get value (map-get? map-is-miner {address: contract-caller})))))
+     913            [ + ]:       3264 :     (if 
+     914                 :       3264 :       (is-eq 
+     915                 :       3264 :         (unwrap! 
+     916                 :       3264 :           (get value (map-get? map-block-asked-to-join {address: miner-to-vote})) 
+     917                 :       3264 :         err-cant-unwrap-asked-to-join) 
+     918                 :       3264 :         block-height) 
+     919            [ - ]:          0 :       (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller}))) 
+     920            [ + ]:       3264 :       (at-block
+     921                 :       3264 :         (unwrap! 
+     922                 :       3264 :           (get-block-info? id-header-hash 
+     923                 :       3264 :             (unwrap-panic (get value (map-get? map-block-asked-to-join {address: miner-to-vote}))))
+     924                 :       3264 :         err-cant-unwrap-block-info)
+     925                 :       3264 :         (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller})))))
+     926            [ + ]:          2 :   false)))
+     927                 :            : 
+     928                 :            : (define-private (check-is-miner-when-requested-remove (miner-to-vote principal))
+     929                 :        348 : (ok 
+     930                 :        348 :   (if 
+     931                 :        348 :     (is-some 
+     932                 :        348 :       (if 
+     933                 :        348 :         (is-eq  
+     934                 :        348 :           (unwrap! (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})) err-cant-unwrap-asked-to-join) 
+     935                 :        348 :           block-height)
+     936            [ - ]:          0 :         (get value (map-get? map-is-miner {address: contract-caller})) 
+     937            [ + ]:        348 :         (at-block 
+     938                 :        348 :           (unwrap! 
+     939                 :        348 :             (get-block-info? id-header-hash 
+     940                 :        348 :               (unwrap! (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})) err-cant-unwrap-asked-to-join)) 
+     941                 :        348 :           err-cant-unwrap-block-info) 
+     942                 :        348 :           (get value (map-get? map-is-miner {address: contract-caller})))))
+     943            [ + ]:        348 :   (if 
+     944                 :        348 :       (is-eq 
+     945                 :        348 :         (unwrap! 
+     946                 :        348 :           (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote})) 
+     947                 :        348 :         err-cant-unwrap-asked-to-join) 
+     948                 :        348 :         block-height) 
+     949            [ - ]:          0 :       (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller}))) 
+     950            [ + ]:        348 :       (at-block
+     951                 :        348 :         (unwrap! 
+     952                 :        348 :           (get-block-info? id-header-hash 
+     953                 :        348 :             (unwrap-panic (get value (map-get? map-block-proposed-to-remove {address: miner-to-vote}))))
+     954                 :        348 :         err-cant-unwrap-block-info)
+     955                 :        348 :         (unwrap-panic (get value (map-get? map-is-miner {address: contract-caller})))))
+     956            [ - ]:          0 :   false)))
+     957                 :            : 
+     958                 :            : (define-read-only (check-is-miner-when-requested-join-tool-fn (miner-to-vote-address principal)) 
+     959                 :          0 : (get value (map-get? map-is-miner {address: contract-caller})))
+     960                 :            : 
+     961                 :            : (define-private (check-is-miner-now (miner principal))
+     962                 :       3393 : (if (is-some (get value (map-get? map-is-miner {address: miner})))
+     963            [ + ]:        431 :   (unwrap-panic (get value (map-get? map-is-miner {address: miner})))
+     964            [ + ]:       2962 :   false))
+     965                 :            : 
+     966                 :            : (define-private (check-is-proposed-for-removal-now (miner principal))
+     967                 :        370 : (if (is-some (get value (map-get? map-is-proposed-for-removal {address: miner})))
+     968            [ + ]:        348 :   (unwrap-panic (get value (map-get? map-is-proposed-for-removal {address: miner})))
+     969            [ + ]:         22 :   false))
+     970                 :            : 
+     971                 :            : (define-private (check-is-waiting-now (miner principal))
+     972                 :       6230 : (if (is-some (get value (map-get? map-is-waiting {address: miner})))
+     973            [ + ]:       3266 :   (unwrap-panic (get value (map-get? map-is-waiting {address: miner})))
+     974            [ + ]:       2964 :   false))
+     975                 :            : 
+     976                 :            : (define-private (check-is-pending-now (miner principal))
+     977                 :          0 : (if (is-some (get value (map-get? map-is-pending {address: miner})))
+     978            [ - ]:          0 :   (unwrap-panic (get value (map-get? map-is-pending {address: miner})))
+     979            [ - ]:          0 :   false)
+     980                 :            : )
+     981                 :            : 
+     982                 :            : (define-private (get-reward-at-block (block-number uint)) 
+     983                 :          3 : (begin 
+     984                 :          3 :   {reward: (get-block-info? block-reward block-number), 
+     985                 :          3 :   claimer: (get-block-info? miner-address block-number)}))
+     986                 :            : 
+     987                 :            : (define-read-only (get-reward-at-block-read (block-number uint)) 
+     988                 :          3 : (begin 
+     989                 :          3 :   {reward: (get-block-info? block-reward block-number), 
+     990                 :          3 :   claimer: (get-block-info? miner-address block-number)
+     991                 :            :   }))
+     992                 :            : 
+     993                 :            : (define-read-only (get-address-status (address principal))
+     994                 :          0 : (if (check-is-miner-now address)
+     995            [ - ]:          0 :   (ok "is-miner")
+     996            [ - ]:          0 :   (if (check-is-waiting-now address)
+     997            [ - ]:          0 :     (ok "is-waiting")
+     998            [ - ]:          0 :     (if (check-is-pending-now address)
+     999            [ - ]:          0 :       (ok "is-pending")
+    1000            [ - ]:          0 :       (ok "is-none")
+    1001                 :            :     )
+    1002                 :            :   )
+    1003                 :            : ))
+    1004                 :            : 
+    1005                 :            : ;; READ-ONLY UTILS
+    1006                 :            : 
+    1007                 :            : ;; (define-read-only (check-vote-accepted) ;; to check the vote status inside FE
+    1008                 :            : ;; (is-vote-accepted (unwrap-panic (get value (map-get? map-votes-accept-join {address: tx-sender})))))
+    1009                 :            : 
+    1010                 :            : (define-read-only (get-k) 
+    1011                 :         13 : (var-get k))
+    1012                 :            : 
+    1013                 :            : (define-read-only (get-notifier) 
+    1014                 :          3 : (var-get notifier))
+    1015                 :            : 
+    1016                 :            : (define-read-only (get-blocks-won) 
+    1017                 :          0 : (var-get blocks-won))
+    1018                 :            : 
+    1019                 :            : (define-read-only (get-total-rewards-distributed) 
+    1020                 :          0 : (var-get total-rewarded))
+    1021                 :            : 
+    1022                 :            : (define-read-only (get-waiting-list) 
+    1023                 :         34 : (var-get waiting-list))
+    1024                 :            : 
+    1025                 :            : (define-read-only (get-miners-list) 
+    1026                 :         48 : (var-get miners-list))
+    1027                 :            : 
+    1028                 :            : (define-read-only (get-pending-accept-list) 
+    1029                 :         51 : (var-get pending-accept-list ))
+    1030                 :            : 
+    1031                 :            : (define-read-only (get-proposed-removal-list) 
+    1032                 :          0 : (var-get proposed-removal-list ))
+    1033                 :            : 
+    1034                 :            : (define-read-only (get-notifier-vote-status) 
+    1035                 :          7 : (var-get notifier-vote-active))
+    1036                 :            : 
+    1037                 :            : (define-read-only (get-notifier-vote-number (voted-notifier principal)) 
+    1038                 :        252 : (get votes-number (map-get? map-votes-notifier {voted-notifier: voted-notifier})))
+    1039                 :            : 
+    1040                 :            : (define-read-only (get-max-voted-notifier) 
+    1041                 :          2 : (var-get max-voted-proposed-notifier))
+    1042                 :            : 
+    1043                 :            : (define-read-only (get-max-votes-notifier) 
+    1044                 :          2 : (var-get max-votes-notifier))
+    1045                 :            : 
+    1046                 :            : (define-read-only (get-current-block)
+    1047                 :          0 : (ok block-height))
+    1048                 :            : 
+    1049                 :            : (define-private (is-principal-in-waiting-list (miner principal))
+    1050                 :     381850 : (not (is-eq 
+    1051                 :     381850 :   (var-get waiting-list-miner-to-remove)
+    1052                 :     381850 :   miner)))
+    1053                 :            : 
+    1054                 :            : (define-private (is-principal-in-pending-accept-list (miner principal))
+    1055                 :          0 : (not (is-eq 
+    1056                 :          0 :   (var-get pending-accept-list-miner-to-remove)
+    1057                 :          0 :   miner)))
+    1058                 :            : 
+    1059                 :            : (define-private (is-principal-in-miners-list (miner principal))
+    1060                 :      50913 : (not (is-eq  
+    1061                 :      50913 :   (var-get miners-list-miner-to-remove) 
+    1062                 :      50913 :   miner)))
+    1063                 :            : 
+    1064                 :            : (define-private (is-principal-in-proposed-removal-list (miner principal))
+    1065                 :         13 : (not (is-eq  
+    1066                 :         13 :   (var-get proposed-removal-list-miner-to-remove) 
+    1067                 :         13 :   miner)))
+
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/stacking-pool-test.clar.func-sort-c.html b/sbtc-ops/smart-contract/coverage/contracts/stacking-pool-test.clar.func-sort-c.html new file mode 100644 index 00000000..4e81a6e0 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/stacking-pool-test.clar.func-sort-c.html @@ -0,0 +1,373 @@ + + + + + + + LCOV - coverage.lcov - contracts/stacking-pool-test.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - stacking-pool-test.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:10343323.8 %
Date:2023-08-18 21:53:02Functions:217328.8 %
Branches:169916.2 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
calculate-all-stackers-weights0
calculate-extra-reserved-funds0
calculate-one-stacker-weight0
can-lock-now0
can-withdraw-extra-reserved-now0
check-can-decrement-delegated-balance0
check-can-decrement-locked-balance0
check-can-decrement-owned-balance0
check-can-decrement-reserved-balance0
check-can-decrement-total-balance0
check-is-liquidity-provider0
check-is-stacker0
check-won-block-rewards0
decrement-sc-locked-balance0
decrement-sc-owned-balance0
decrement-sc-reserved-balance0
delegate-stack-extend-increase0
delegate-stack-stx0
delegate-stack-stx-many0
get-SC-locked-balance0
get-SC-owned-balance0
get-SC-reserved-balance0
get-address-status0
get-amount-rewarded0
get-block-rewards0
get-blocks-rewarded0
get-liquidity-provider0
get-minimum-deposit-liquidity-provider0
get-next-reward-cycle0
get-pool-members0
get-pox-addr-indices0
get-return0
get-stacked-this-cycle0
get-stacker-weight0
get-stx-account0
multiple-blocks-check-won-rewards0
preview-exchange-reward0
register-block-reward0
reserve-funds-future-rewards0
reward-distribution0
set-active0
set-liquidity-provider0
set-pool-pox-address0
transfer-reward-one-stacker0
transfer-rewards-all-stackers0
unlock-extra-reserved-funds0
update-return0
update-sc-balances0
update-sc-balances-one-stacker0
was-block-claimed0
weight-calculator0
withdraw-stx-liquidity-provider0
deposit-stx-liquidity-provider1
disallow-contract-caller1
get-SC-total-balance1
get-check-delegation1
quit-stacking-pool1
remove-stacker-stackers-list1
decrement-sc-delegated-balance19
delegate-stx-inner19
get-delegated-amount19
get-user-data19
increment-sc-delegated-balance19
increment-sc-locked-balance19
lock-delegated-stx19
maybe-stack-aggregation-commit19
min19
is-in-pool20
check-caller-allowed22
delegate-stx22
join-stacking-pool22
allow-contract-caller41
check-pool-SC-pox-2-allowance45
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/stacking-pool-test.clar.func.html b/sbtc-ops/smart-contract/coverage/contracts/stacking-pool-test.clar.func.html new file mode 100644 index 00000000..0198d4ca --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/stacking-pool-test.clar.func.html @@ -0,0 +1,373 @@ + + + + + + + LCOV - coverage.lcov - contracts/stacking-pool-test.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - stacking-pool-test.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:10343323.8 %
Date:2023-08-18 21:53:02Functions:217328.8 %
Branches:169916.2 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
allow-contract-caller41
calculate-all-stackers-weights0
calculate-extra-reserved-funds0
calculate-one-stacker-weight0
can-lock-now0
can-withdraw-extra-reserved-now0
check-caller-allowed22
check-can-decrement-delegated-balance0
check-can-decrement-locked-balance0
check-can-decrement-owned-balance0
check-can-decrement-reserved-balance0
check-can-decrement-total-balance0
check-is-liquidity-provider0
check-is-stacker0
check-pool-SC-pox-2-allowance45
check-won-block-rewards0
decrement-sc-delegated-balance19
decrement-sc-locked-balance0
decrement-sc-owned-balance0
decrement-sc-reserved-balance0
delegate-stack-extend-increase0
delegate-stack-stx0
delegate-stack-stx-many0
delegate-stx22
delegate-stx-inner19
deposit-stx-liquidity-provider1
disallow-contract-caller1
get-SC-locked-balance0
get-SC-owned-balance0
get-SC-reserved-balance0
get-SC-total-balance1
get-address-status0
get-amount-rewarded0
get-block-rewards0
get-blocks-rewarded0
get-check-delegation1
get-delegated-amount19
get-liquidity-provider0
get-minimum-deposit-liquidity-provider0
get-next-reward-cycle0
get-pool-members0
get-pox-addr-indices0
get-return0
get-stacked-this-cycle0
get-stacker-weight0
get-stx-account0
get-user-data19
increment-sc-delegated-balance19
increment-sc-locked-balance19
is-in-pool20
join-stacking-pool22
lock-delegated-stx19
maybe-stack-aggregation-commit19
min19
multiple-blocks-check-won-rewards0
preview-exchange-reward0
quit-stacking-pool1
register-block-reward0
remove-stacker-stackers-list1
reserve-funds-future-rewards0
reward-distribution0
set-active0
set-liquidity-provider0
set-pool-pox-address0
transfer-reward-one-stacker0
transfer-rewards-all-stackers0
unlock-extra-reserved-funds0
update-return0
update-sc-balances0
update-sc-balances-one-stacker0
was-block-claimed0
weight-calculator0
withdraw-stx-liquidity-provider0
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/stacking-pool-test.clar.gcov.html b/sbtc-ops/smart-contract/coverage/contracts/stacking-pool-test.clar.gcov.html new file mode 100644 index 00000000..b3d26b99 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/stacking-pool-test.clar.gcov.html @@ -0,0 +1,839 @@ + + + + + + + LCOV - coverage.lcov - contracts/stacking-pool-test.clar + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - stacking-pool-test.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:10343323.8 %
Date:2023-08-18 21:53:02Functions:217328.8 %
Branches:169916.2 %
+
+ + + + + + + + +

+
           Branch data     Line data    Source code
+
+       1                 :            : 
+       2                 :            : ;; Main Stacking Pool Contract
+       3                 :            : 
+       4                 :            : 
+       5                 :            : ;; Flow
+       6                 :            : ;; 1. The liquidity provider deploys the contract 
+       7                 :            : ;; 2. He locks into the SC a sum which will be sufficient to cover all the stackers' rewards
+       8                 :            : ;; 3. Stackers who want to stack through the stacking pool have to join the pool.
+       9                 :            : ;; 4. They will have to delegate the STX they want to stack to the pool's POX address
+      10                 :            : ;; 5. When the total amount commited is enough to be stacked, it will be auto committed
+      11                 :            : ;; 6. The stackers will be able to claim the rewards after they are distributed
+      12                 :            : 
+      13                 :            : ;; + In prepare phase, calculate weight of the stackers inside the pool (Notion)
+      14                 :            : 
+      15                 :            : ;; Default length of the PoX registration window, in burnchain blocks.
+      16                 :            : ;;TODO: pox-2 mainnet address: 'SP000000000000000000002Q6VF78
+      17                 :            : (define-constant PREPARE_CYCLE_LENGTH (get prepare-cycle-length (unwrap-panic (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-pox-info))))
+      18                 :            : 
+      19                 :            : ;; Default length of the PoX reward cycle, in burnchain blocks.
+      20                 :            : (define-constant REWARD_CYCLE_LENGTH (get reward-cycle-length (unwrap-panic (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-pox-info))))
+      21                 :            : 
+      22                 :            : ;; Half cycle length is 1050 for mainnet
+      23                 :            : (define-constant half-cycle-length (/ (get reward-cycle-length (unwrap-panic (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-pox-info))) u2))
+      24                 :            : 
+      25                 :            : (define-constant err-only-liquidity-provider (err u100))
+      26                 :            : (define-constant err-already-in-pool (err u101))
+      27                 :            : (define-constant err-not-in-pool (err u102))
+      28                 :            : (define-constant err-liquidity-provider-not-permitted (err u103))
+      29                 :            : (define-constant err-wrong-moment-to-update-balances (err u123))
+      30                 :            : (define-constant err-allow-pool-in-SC-first (err u195))
+      31                 :            : (define-constant err-allow-pool-in-pox-2-first (err u199))
+      32                 :            : (define-constant err-insufficient-funds (err u200))
+      33                 :            : (define-constant err-revoke-delegation-in-pox-first (err u201))
+      34                 :            : (define-constant err-disallow-pool-in-pox-2-first (err u299))
+      35                 :            : (define-constant err-full-stacking-pool (err u300))
+      36                 :            : (define-constant err-same-value (err u325))
+      37                 :            : (define-constant err-future-reward-not-covered (err u333))
+      38                 :            : (define-constant err-not-delegated-that-amount (err u396))
+      39                 :            : (define-constant err-no-locked-funds (err u456))
+      40                 :            : (define-constant err-too-early (err u500))
+      41                 :            : (define-constant err-decrease-forbidden (err u503))
+      42                 :            : (define-constant err-no-reward-yet (err u576))
+      43                 :            : (define-constant err-not-enough-reserved-balance (err u579))
+      44                 :            : (define-constant err-stacking-permission-denied (err u609))
+      45                 :            : (define-constant err-transfer-failed (err u777))
+      46                 :            : (define-constant err-cant-calculate-weights (err u888))
+      47                 :            : (define-constant err-already-updated-balances (err u895))
+      48                 :            : (define-constant err-no-reward-for-this-block (err u900))
+      49                 :            : (define-constant err-already-rewarded-block (err u992))
+      50                 :            : (define-constant err-cant-withdraw-now (err u995))
+      51                 :            : (define-constant err-cant-unwrap-exchange-preview (err u996))
+      52                 :            : (define-constant err-return-div-exceeds-maximum (err u997))
+      53                 :            : (define-constant err-pox-address-deactivated (err u999))
+      54                 :            : (define-constant err-weights-not-calculated (err u1000))
+      55                 :            : 
+      56                 :            : (define-constant first-deposit u0)
+      57                 :            : (define-constant list-max-len u300)
+      58                 :            : (define-constant pool-contract (as-contract tx-sender))
+      59                 :            : (define-constant pox-2-contract (as-contract 'ST000000000000000000002AMW42H.pox-3))
+      60                 :            : (define-constant blocks-to-pass-until-reward u101)
+      61                 :            : (define-constant max-return-div-accepted u20)
+      62                 :            : (define-constant ONE-6 u1000000)
+      63                 :            : ;; liquidity provider data vars
+      64                 :            : (define-data-var sc-total-balance uint u0)
+      65                 :            : (define-data-var sc-owned-balance uint u0)
+      66                 :            : (define-data-var sc-reserved-balance uint u0)
+      67                 :            :   ;; (the percentage of the locked balance assured by the liquidity provider) ^ -1,
+      68                 :            :   ;; return-div = u20 => the liquidity provider is ready to grant a maximum of 5% of the total locked balance.
+      69                 :            : (define-data-var return-div uint u20)  
+      70                 :            : ;; stackers data vars
+      71                 :            : (define-data-var sc-delegated-balance uint u0)
+      72                 :            : (define-data-var sc-locked-balance uint u0)
+      73                 :            : 
+      74                 :            : ;; temporary data var helpers
+      75                 :            : (define-data-var calc-delegated-balance uint u0)
+      76                 :            : (define-data-var calc-locked-balance uint u0)
+      77                 :            : (define-data-var reward-cycle-to-calculate-weight uint u0)
+      78                 :            : (define-data-var burn-block-to-distribute-rewards uint u0)
+      79                 :            : (define-data-var reward-cycle-to-distribute-rewards uint u0)
+      80                 :            : ;; common data vars
+      81                 :            : (define-data-var minimum-deposit-amount-liquidity-provider uint u10000000000) ;; minimum amount for the liquidity provider to transfer after deploy in microSTX (STX * 10^-6)
+      82                 :            : (define-data-var stackers-list (list 300 principal) (list tx-sender))
+      83                 :            : (define-data-var liquidity-provider principal tx-sender)
+      84                 :            : (define-data-var active bool true)
+      85                 :            : (define-data-var blocks-rewarded uint u0)
+      86                 :            : (define-data-var amount-rewarded uint u0)
+      87                 :            : 
+      88                 :            : 
+      89                 :            : ;; liqidity provider reward bitcoin address
+      90                 :            : (define-data-var pool-pox-address {hashbytes: (buff 32), version: (buff 1)}
+      91                 :            :   {
+      92                 :            :     version: 0x04,
+      93                 :            :     hashbytes: 0x83ed66860315e334010bbfb76eb3eef887efee0a})
+      94                 :            : 
+      95                 :            : (define-data-var stx-buffer uint u0) ;; 0 STX
+      96                 :            : 
+      97                 :            : ;; data maps
+      98                 :            : 
+      99                 :            : (define-map user-data { address: principal } {is-in-pool:bool, delegated-balance: uint, locked-balance:uint, until-burn-ht: (optional uint) })
+     100                 :            : (define-map user-revoked-delegation principal bool)
+     101                 :            : (define-map pox-addr-indices uint uint)
+     102                 :            : (define-map last-aggregation uint uint)
+     103                 :            : (define-map allowance-contract-callers { sender: principal, contract-caller: principal} { until-burn-ht: (optional uint)})
+     104                 :            : (define-map stacker-weights-per-reward-cycle { stacker: principal, reward-cycle: uint } { weight-percentage: uint })
+     105                 :            : (define-map calculated-weights-reward-cycles { reward-cycle: uint } { calculated: bool })
+     106                 :            : (define-map burn-block-rewards { burn-height: uint } { reward: uint })
+     107                 :            : (define-map updated-sc-balances { reward-cycle: uint } { updated: bool, stackers-list: (list 300 principal) })
+     108                 :            : (define-map already-rewarded { burn-block-height: uint } { value: bool })
+     109                 :         41 : (allow-contract-caller (as-contract tx-sender) none)
+     110                 :            : 
+     111                 :         41 : (map-set user-data {address: tx-sender} {is-in-pool: true, delegated-balance: u0, locked-balance: u0, until-burn-ht: none})
+     112                 :            : 
+     113                 :            : ;; Public functions
+     114                 :            : 
+     115                 :            : (define-public (deposit-stx-liquidity-provider (amount uint)) 
+     116                 :          1 : (begin 
+     117            [ - ]:          1 :   (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider)
+     118            [ - ]:          1 :   (asserts! (>= amount (var-get minimum-deposit-amount-liquidity-provider)) err-future-reward-not-covered)
+     119                 :          1 :   (try! (stx-transfer? amount tx-sender pool-contract))
+     120                 :          1 :   (var-set sc-total-balance (+ amount (var-get sc-total-balance)))
+     121                 :          1 :   (var-set sc-owned-balance (+ amount (var-get sc-owned-balance)))
+     122                 :          1 :   (ok true)))
+     123                 :            : 
+     124                 :            : (define-public (withdraw-stx-liquidity-provider (amount uint)) 
+     125                 :          0 : (begin 
+     126            [ - ]:          0 :   (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider)
+     127                 :          0 :   (asserts! 
+     128                 :          0 :     (and 
+     129            [ - ]:          0 :       (check-can-decrement-owned-balance amount) 
+     130            [ - ]:          0 :       (check-can-decrement-total-balance amount)) 
+     131            [ - ]:          0 :   err-insufficient-funds)
+     132                 :          0 :   (try! 
+     133                 :          0 :     (as-contract 
+     134                 :          0 :       (stx-transfer? amount tx-sender (var-get liquidity-provider))))
+     135                 :          0 :   (var-set sc-total-balance (- (var-get sc-total-balance) amount))
+     136                 :          0 :   (var-set sc-owned-balance (- (var-get sc-owned-balance) amount))
+     137                 :          0 :   (ok true)))
+     138                 :            : 
+     139                 :            : (define-public (reserve-funds-future-rewards (amount uint)) 
+     140                 :          0 : (begin 
+     141            [ - ]:          0 :   (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider)
+     142            [ - ]:          0 :   (asserts! (>= (var-get sc-owned-balance) amount) err-insufficient-funds) 
+     143            [ - ]:          0 :   (asserts! (>= amount (var-get minimum-deposit-amount-liquidity-provider)) err-future-reward-not-covered)
+     144                 :          0 :   (var-set sc-owned-balance (- (var-get sc-owned-balance) amount))
+     145                 :          0 :   (var-set sc-reserved-balance (+ (var-get sc-reserved-balance) amount))
+     146                 :          0 :   (ok true)))
+     147                 :            : 
+     148                 :            : (define-public (unlock-extra-reserved-funds) 
+     149                 :          0 : (begin 
+     150                 :          0 :   (asserts! 
+     151                 :          0 :     (is-eq 
+     152                 :          0 :       contract-caller 
+     153                 :          0 :       (var-get liquidity-provider)) 
+     154            [ - ]:          0 :   err-only-liquidity-provider)
+     155            [ - ]:          0 :   (asserts! (can-withdraw-extra-reserved-now) err-cant-withdraw-now)
+     156                 :          0 :     (let ((unreserve-amount (calculate-extra-reserved-funds))
+     157                 :          0 :           (reserved-balance-before (var-get sc-reserved-balance))
+     158                 :          0 :           (owned-balance-before (var-get sc-owned-balance))) 
+     159                 :          0 :       (var-set sc-reserved-balance 
+     160                 :          0 :         (- 
+     161                 :          0 :           reserved-balance-before 
+     162                 :          0 :           unreserve-amount))
+     163                 :          0 :       (var-set sc-owned-balance 
+     164                 :          0 :         (+ 
+     165                 :          0 :           owned-balance-before 
+     166                 :          0 :           unreserve-amount))
+     167                 :          0 :       (ok unreserve-amount))))
+     168                 :            : 
+     169                 :            : (define-public (join-stacking-pool)
+     170                 :         22 : (begin
+     171            [ + ]:         22 :   (asserts! (check-pool-SC-pox-2-allowance) err-allow-pool-in-pox-2-first)
+     172            [ - ]:         21 :   (asserts! (is-none (map-get? user-data {address: tx-sender})) err-already-in-pool)
+     173                 :         21 :   (var-set stackers-list (unwrap! (as-max-len? (concat (var-get stackers-list) (list tx-sender )) u300) err-full-stacking-pool)) 
+     174                 :         21 :   (map-set user-data {address: tx-sender} {is-in-pool: true, delegated-balance: u0, locked-balance: u0, until-burn-ht: none})
+     175                 :         21 :   (ok true)))
+     176                 :            : 
+     177                 :            : (define-public (allow-contract-caller (caller principal) (until-burn-ht (optional uint)))
+     178                 :         41 : (begin
+     179            [ - ]:         41 :   (asserts! (is-eq tx-sender contract-caller) err-stacking-permission-denied)
+     180                 :         41 :   (ok (map-set allowance-contract-callers
+     181                 :         41 :         { sender: tx-sender, contract-caller: caller}
+     182                 :         41 :         { until-burn-ht: until-burn-ht}))))
+     183                 :            : 
+     184                 :            : ;; revoke contract-caller authorization to call stacking methods
+     185                 :            : (define-public (disallow-contract-caller (caller principal))
+     186                 :          1 : (begin
+     187            [ - ]:          1 :   (asserts! (is-eq tx-sender contract-caller) err-stacking-permission-denied)
+     188                 :          1 :   (ok (map-delete allowance-contract-callers { sender: tx-sender, contract-caller: caller}))))
+     189                 :            : 
+     190                 :            : (define-public (quit-stacking-pool)
+     191                 :          1 : (begin
+     192            [ - ]:          1 :   (asserts! (is-none (get-check-delegation tx-sender)) err-revoke-delegation-in-pox-first)
+     193            [ - ]:          1 :   (asserts! (not (check-pool-SC-pox-2-allowance)) err-disallow-pool-in-pox-2-first)
+     194            [ - ]:          1 :   (asserts! (is-some (map-get? user-data {address: tx-sender})) err-not-in-pool)
+     195            [ - ]:          1 :   (asserts! (not (is-eq contract-caller (var-get liquidity-provider))) err-liquidity-provider-not-permitted)
+     196                 :          1 :     (try! (disallow-contract-caller pool-contract))
+     197                 :          1 :     (var-set stackers-list (filter remove-stacker-stackers-list (var-get stackers-list))) 
+     198                 :          1 :     (map-delete user-data {address: tx-sender})
+     199                 :          1 :     (ok true)))
+     200                 :            : 
+     201                 :            : ;; The SC balances need to be updated during the first half of every Prepare Phase
+     202                 :            : ;; Everyone can call the function in order to recalculate each stacker's weight inside the pool
+     203                 :            : ;; This WILL directly AFFECT the reward distribution
+     204                 :            : (define-public (update-sc-balances)
+     205                 :          0 : (let (
+     206                 :          0 :   (next-reward-cycle (get-next-reward-cycle))
+     207                 :          0 :   (next-reward-cycle-first-block (contract-call? 'ST000000000000000000002AMW42H.pox-3 reward-cycle-to-burn-height (get-next-reward-cycle)))) 
+     208                 :          0 : (begin 
+     209                 :            :   ;; check current block to be inside the first half of the current reward cycle's prepare phase
+     210                 :          0 :   (asserts! 
+     211                 :          0 :     (<= 
+     212                 :          0 :       burn-block-height 
+     213                 :          0 :       (+ 
+     214                 :          0 :         (- 
+     215                 :          0 :           next-reward-cycle-first-block
+     216                 :          0 :           PREPARE_CYCLE_LENGTH)
+     217                 :          0 :         (/ PREPARE_CYCLE_LENGTH u2))) 
+     218            [ - ]:          0 :   err-wrong-moment-to-update-balances)
+     219            [ - ]:          0 :   (asserts! (is-none (map-get? updated-sc-balances {reward-cycle: next-reward-cycle})) err-already-updated-balances)
+     220                 :          0 :   (var-set calc-locked-balance u0)
+     221                 :          0 :   (var-set calc-delegated-balance u0)
+     222                 :          0 :   (map update-sc-balances-one-stacker (var-get stackers-list))
+     223                 :          0 :   (var-set sc-locked-balance (var-get calc-locked-balance))
+     224                 :          0 :   (var-set sc-delegated-balance (var-get calc-delegated-balance))
+     225                 :          0 :   (map-set updated-sc-balances {reward-cycle: next-reward-cycle} {updated: true, stackers-list: (var-get stackers-list)})
+     226                 :          0 :   (var-set reward-cycle-to-calculate-weight next-reward-cycle)
+     227                 :          0 :   (unwrap! (calculate-all-stackers-weights (var-get stackers-list) next-reward-cycle) err-cant-calculate-weights)
+     228                 :          0 :   (ok true))))
+     229                 :            : 
+     230                 :            : ;; recalculate balances inside pool
+     231                 :            : (define-public (update-sc-balances-one-stacker (stacker principal))
+     232                 :          0 : (let ((user-until-burn-ht (default-to u0 (default-to (some u0) (get until-burn-ht (map-get? user-data {address: stacker})))))
+     233                 :          0 :       (user-delegated-balance (default-to u0 (get delegated-balance (map-get? user-data {address: stacker}))))
+     234                 :          0 :       (user-locked-balance (default-to u0 (get locked-balance (map-get? user-data {address: stacker}))))) 
+     235                 :          0 :   (ok 
+     236                 :            :     ;; if burn-block-height < user's unlock burn block height, then user's balances 
+     237                 :          0 :     (if 
+     238                 :          0 :         (< 
+     239                 :          0 :           burn-block-height 
+     240                 :          0 :           user-until-burn-ht) 
+     241            [ - ]:          0 :         (begin 
+     242                 :          0 :           (var-set calc-locked-balance 
+     243                 :          0 :             (+ 
+     244                 :          0 :               (var-get calc-locked-balance) 
+     245                 :          0 :               user-locked-balance))
+     246                 :          0 :           (var-set calc-delegated-balance 
+     247                 :          0 :             (+ 
+     248                 :          0 :               (var-get calc-delegated-balance) 
+     249                 :          0 :               user-delegated-balance))) 
+     250            [ - ]:          0 :         (begin 
+     251                 :          0 :           (var-set calc-locked-balance (var-get calc-locked-balance))
+     252                 :          0 :           (var-set calc-delegated-balance (var-get calc-delegated-balance)))))))
+     253                 :            : 
+     254                 :            : ;; The rewards will be distributed. At that moment, the SC balance should have been updated and the stackers' weights calculated
+     255                 :            : (define-public (reward-distribution (rewarded-burn-block uint))
+     256                 :          0 : (let ((reward-cycle 
+     257                 :          0 :         (contract-call? 'ST000000000000000000002AMW42H.pox-3 burn-height-to-reward-cycle rewarded-burn-block))
+     258                 :          0 :       (stackers-list-for-reward-cycle 
+     259                 :          0 :         (default-to (list ) (get stackers-list (map-get? updated-sc-balances {reward-cycle: reward-cycle})))))
+     260            [ - ]:          0 :           (asserts! (< rewarded-burn-block burn-block-height) err-no-reward-yet)
+     261            [ - ]:          0 :           (asserts! (check-won-block-rewards rewarded-burn-block) err-no-reward-for-this-block)
+     262            [ - ]:          0 :           (asserts! (is-none (map-get? already-rewarded {burn-block-height: rewarded-burn-block})) err-already-rewarded-block)
+     263                 :          0 :           (var-set burn-block-to-distribute-rewards rewarded-burn-block)
+     264                 :          0 :           (var-set amount-rewarded (+ (var-get amount-rewarded) (default-to u0 (get reward (map-get? burn-block-rewards { burn-height: (var-get burn-block-to-distribute-rewards)})))))
+     265                 :          0 :           (var-set blocks-rewarded (+ (var-get blocks-rewarded) u1))
+     266                 :          0 :           (map-set already-rewarded {burn-block-height: rewarded-burn-block} {value: true})
+     267                 :          0 :           (var-set reward-cycle-to-distribute-rewards reward-cycle)
+     268                 :          0 :           (match (map-get? calculated-weights-reward-cycles {reward-cycle: reward-cycle}) 
+     269            [ - ]:          0 :             calculated (ok 
+     270                 :          0 :                           (transfer-rewards-all-stackers stackers-list-for-reward-cycle))
+     271            [ - ]:          0 :             err-weights-not-calculated)))
+     272                 :            : 
+     273                 :            : ;; delegating stx to the pool SC
+     274                 :            : (define-public (delegate-stx (amount-ustx uint))
+     275                 :         22 : (let ((user tx-sender)
+     276                 :         22 :       (current-cycle (contract-call? 'ST000000000000000000002AMW42H.pox-3 current-pox-reward-cycle)))
+     277            [ - ]:         22 :   (asserts! (check-caller-allowed) err-stacking-permission-denied)
+     278            [ + ]:         22 :   (asserts! (check-pool-SC-pox-2-allowance) err-allow-pool-in-pox-2-first)
+     279                 :            :   
+     280            [ + ]:         20 :   (asserts! (is-in-pool) err-not-in-pool)
+     281                 :         19 :   (try! (delegate-stx-inner amount-ustx (as-contract tx-sender) none))
+     282                 :         19 :   (try! (as-contract (lock-delegated-stx user)))
+     283                 :         19 :   (ok (maybe-stack-aggregation-commit current-cycle))))
+     284                 :            : 
+     285                 :            : ;; Stacks the delegated amount for the given user for the next cycle.
+     286                 :            : ;; This function can be called by automation, friends or family for user that have delegated once.
+     287                 :            : ;; This function can be called only after the current cycle is half through
+     288                 :            : (define-public (delegate-stack-stx (user principal))
+     289                 :          0 :   (let ((current-cycle (contract-call? 'ST000000000000000000002AMW42H.pox-3 current-pox-reward-cycle)))
+     290            [ - ]:          0 :     (asserts! (can-lock-now current-cycle) err-too-early)
+     291                 :            :     ;; Do 3.
+     292                 :          0 :     (try! (as-contract (lock-delegated-stx user)))
+     293                 :            :     ;; Do 4.
+     294                 :          0 :     (ok (maybe-stack-aggregation-commit current-cycle))))
+     295                 :            : 
+     296                 :            : (define-public (delegate-stack-stx-many (stackers-lock-list (list 100 principal))) 
+     297                 :          0 : (ok (map delegate-stack-stx stackers-lock-list)))
+     298                 :            : 
+     299                 :            : (define-public (multiple-blocks-check-won-rewards (burn-heights-list (list 100 uint))) 
+     300                 :          0 : (ok (map check-won-block-rewards burn-heights-list)))
+     301                 :            : 
+     302                 :            : ;; liquidity provider pool management functions
+     303                 :            : 
+     304                 :            : (define-public (set-pool-pox-address (new-pool-pox-address {hashbytes: (buff 32), version: (buff 1)})) 
+     305                 :          0 : (begin 
+     306            [ - ]:          0 :   (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider)
+     307                 :          0 :   (ok (var-set pool-pox-address new-pool-pox-address))))
+     308                 :            : 
+     309                 :            : (define-public (set-active (is-active bool))
+     310                 :          0 : (begin
+     311            [ - ]:          0 :   (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider)    
+     312                 :          0 :   (ok (var-set active is-active))))
+     313                 :            : 
+     314                 :            : (define-public (set-liquidity-provider (new-liquidity-provider principal)) 
+     315                 :          0 : (begin 
+     316            [ - ]:          0 :   (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider)
+     317            [ - ]:          0 :   (asserts! (is-some (map-get? user-data {address: new-liquidity-provider})) err-not-in-pool) ;; new liquidity provider should be in pool
+     318                 :          0 :   (ok (var-set liquidity-provider new-liquidity-provider))))
+     319                 :            : 
+     320                 :            : (define-public (update-return (new-return-value uint)) 
+     321                 :          0 : (begin 
+     322            [ - ]:          0 :   (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider) 
+     323            [ - ]:          0 :   (asserts! (<= new-return-value max-return-div-accepted) err-return-div-exceeds-maximum)
+     324            [ - ]:          0 :   (asserts! (not (is-eq new-return-value (var-get return-div))) err-same-value)
+     325                 :          0 :   (var-set return-div new-return-value)
+     326                 :          0 :   (ok new-return-value)))
+     327                 :            : 
+     328                 :            : ;; Private functions
+     329                 :            : 
+     330                 :            : ;; Pox operative functions
+     331                 :            : 
+     332                 :            : (define-private (maybe-stack-aggregation-commit (current-cycle uint))
+     333                 :         19 : (let ((reward-cycle (+ u1 current-cycle)))
+     334                 :         19 :   (match (map-get? pox-addr-indices reward-cycle)
+     335                 :            :           ;; Total stacked already reached minimum.
+     336                 :            :           ;; Call stack-aggregate-increase.
+     337                 :            :           ;; It might fail because called in the same cycle twice.
+     338            [ + ]:          5 :     index (match (as-contract (contract-call? 'ST000000000000000000002AMW42H.pox-3 stack-aggregation-increase (var-get pool-pox-address) reward-cycle index))
+     339            [ + ]:          5 :             success (map-set last-aggregation reward-cycle block-height)
+     340            [ - ]:          0 :             error (begin (print {err-increase-ignored: error}) false))
+     341                 :            :           ;; Total stacked is still below minimum.
+     342                 :            :           ;; Just try to commit, it might fail because minimum not yet met
+     343            [ + ]:         14 :     (match (as-contract (contract-call? 'ST000000000000000000002AMW42H.pox-3 stack-aggregation-commit-indexed (var-get pool-pox-address) reward-cycle))
+     344            [ + ]:          4 :       index (begin
+     345                 :          4 :               (map-set pox-addr-indices reward-cycle index)
+     346                 :          4 :               (map-set last-aggregation reward-cycle block-height))
+     347            [ + ]:         10 :       error (begin 
+     348                 :         10 :               (print {err-commit-ignored: error}) false))))) ;; ignore errors
+     349                 :            : 
+     350                 :            : (define-private (delegate-stx-inner (amount-ustx uint) (delegate-to principal) (until-burn-ht (optional uint)))
+     351                 :         19 : (let ((result-revoke
+     352                 :            :         ;; Calls revoke and ignores result
+     353                 :         19 :         (contract-call? 'ST000000000000000000002AMW42H.pox-3 revoke-delegate-stx))
+     354                 :          0 :       (user-delegated-balance 
+     355                 :         19 :         (default-to u0 (get delegated-balance (map-get? user-data {address: tx-sender})))))
+     356                 :         19 :       (if 
+     357                 :         19 :           (is-ok result-revoke) 
+     358            [ + ]:         19 :           (if 
+     359                 :         19 :             (unwrap-panic result-revoke) 
+     360            [ - ]:          0 :             (begin 
+     361                 :          0 :               (asserts! 
+     362                 :          0 :                 (check-can-decrement-delegated-balance 
+     363                 :          0 :                   user-delegated-balance) 
+     364            [ - ]:          0 :               err-not-delegated-that-amount) 
+     365                 :          0 :               (decrement-sc-delegated-balance user-delegated-balance)) 
+     366            [ + ]:         19 :             (decrement-sc-delegated-balance u0)) 
+     367            [ - ]:          0 :           (decrement-sc-delegated-balance u0))
+     368                 :            :   ;; Calls delegate-stx, converts any error to uint
+     369                 :         19 :   (match (contract-call? 'ST000000000000000000002AMW42H.pox-3 delegate-stx amount-ustx delegate-to until-burn-ht none)
+     370            [ + ]:         19 :     success (begin 
+     371                 :         19 :               (increment-sc-delegated-balance amount-ustx)
+     372                 :         19 :               (map-set 
+     373                 :          0 :                 user-data 
+     374                 :         19 :                   {address: tx-sender} 
+     375                 :            :                   {
+     376                 :         19 :                     is-in-pool: (default-to false (get is-in-pool (map-get? user-data {address: tx-sender}))),                    
+     377                 :         19 :                     delegated-balance: amount-ustx, 
+     378                 :         19 :                     locked-balance: (default-to u0 (get locked-balance (map-get? user-data {address: tx-sender}))),
+     379                 :         19 :                     until-burn-ht: until-burn-ht})
+     380                 :         19 :               (print "sc delegated balance")
+     381                 :         19 :               (print (var-get sc-delegated-balance))
+     382                 :         19 :               (ok success))
+     383            [ - ]:          0 :     error (err (* u1000 (to-uint error))))))
+     384                 :            : 
+     385                 :            : (define-private (lock-delegated-stx (user principal))
+     386                 :         19 : (let ((start-burn-ht (+ burn-block-height u1))
+     387                 :         19 :       (pox-address (var-get pool-pox-address))
+     388                 :         19 :       (buffer-amount u0)
+     389                 :         19 :       (user-account (stx-account user))
+     390                 :         19 :       (allowed-amount (min (get-delegated-amount user) (+ (get locked user-account) (get unlocked user-account))))
+     391         [ +  - ]:         19 :       (amount-ustx (if (> allowed-amount buffer-amount) (- allowed-amount buffer-amount) allowed-amount)))
+     392            [ - ]:         19 :   (asserts! (var-get active) err-pox-address-deactivated)
+     393                 :         19 :   (match (contract-call? 'ST000000000000000000002AMW42H.pox-3 delegate-stack-stx
+     394                 :         19 :             user amount-ustx
+     395                 :         19 :             pox-address start-burn-ht u1)
+     396                 :            :     stacker-details 
+     397            [ + ]:         19 :       (begin 
+     398                 :         19 :         (map-set 
+     399                 :          0 :           user-data 
+     400                 :         19 :             {address: user} 
+     401                 :            :             {
+     402                 :          0 :               is-in-pool: 
+     403                 :         19 :                 (default-to false (get is-in-pool (map-get? user-data {address: user}))),
+     404                 :          0 :               delegated-balance: 
+     405                 :         19 :                 (default-to u0 (get delegated-balance (map-get? user-data {address: user}))),
+     406                 :         19 :               locked-balance: (get lock-amount stacker-details),
+     407                 :          0 :               until-burn-ht: 
+     408                 :         19 :                   (some (get unlock-burn-height stacker-details))})
+     409                 :         19 :         (increment-sc-locked-balance (get lock-amount stacker-details))
+     410                 :         19 :         (ok stacker-details))
+     411                 :            : 
+     412            [ - ]:          0 :       error (if (is-eq error 3) ;; check whether user is already stacked
+     413            [ - ]:          0 :               (delegate-stack-extend-increase user amount-ustx pox-address start-burn-ht)
+     414            [ - ]:          0 :               (err (* u1000 (to-uint error)))))))
+     415                 :            : 
+     416                 :            : (define-private (delegate-stack-extend-increase (user principal)
+     417                 :            :                   (amount-ustx uint)
+     418                 :            :                   (pox-address {hashbytes: (buff 32), version: (buff 1)})
+     419                 :            :                   (start-burn-ht uint))
+     420                 :          0 : (let ((status (stx-account user)))
+     421            [ - ]:          0 :   (asserts! (>= amount-ustx (get locked status)) err-decrease-forbidden)
+     422                 :          0 :   (match (contract-call? 'ST000000000000000000002AMW42H.pox-3 delegate-stack-extend
+     423                 :          0 :           user pox-address u1)
+     424            [ - ]:          0 :     success (begin 
+     425                 :          0 :             (print "success")
+     426                 :          0 :             (print success)
+     427                 :          0 :             (map-set user-data 
+     428                 :          0 :                     {address: user} 
+     429                 :            :                     {
+     430                 :          0 :                     is-in-pool: 
+     431                 :          0 :                       (default-to false (get is-in-pool (map-get? user-data {address: user}))),
+     432                 :          0 :                     delegated-balance: 
+     433                 :          0 :                       (default-to u0 (get delegated-balance (map-get? user-data {address: user}))), 
+     434                 :          0 :                     locked-balance: 
+     435                 :          0 :                       (default-to u0 (get locked-balance (map-get? user-data {address: user}))),
+     436                 :          0 :                     until-burn-ht: 
+     437                 :          0 :                       (some (+ (default-to u0 (default-to (some u0) (get until-burn-ht (map-get? user-data {address: user})))) REWARD_CYCLE_LENGTH))
+     438                 :            :                     })
+     439                 :          0 :             (if (> amount-ustx (get locked status))          
+     440            [ - ]:          0 :               (match (contract-call? 'ST000000000000000000002AMW42H.pox-3 delegate-stack-increase 
+     441                 :          0 :                 user 
+     442                 :          0 :                 pox-address 
+     443                 :          0 :                 (- 
+     444                 :          0 :                   amount-ustx 
+     445                 :          0 :                   (default-to u0 (get locked-balance (map-get? user-data {address: user})))))
+     446            [ - ]:          0 :                 success-increase (begin
+     447                 :          0 :                                   (print "success-increase")
+     448                 :          0 :                                   (print success-increase)
+     449                 :          0 :                                   (map-set user-data 
+     450                 :          0 :                                     {address: user} 
+     451                 :            :                                     {
+     452                 :          0 :                                     is-in-pool:
+     453                 :          0 :                                       (default-to false (get is-in-pool (map-get? user-data {address: user}))),
+     454                 :          0 :                                     delegated-balance: 
+     455                 :          0 :                                       (default-to u0 (get delegated-balance (map-get? user-data {address: user}))), 
+     456                 :          0 :                                     locked-balance: (get total-locked success-increase),
+     457                 :          0 :                                     until-burn-ht:
+     458                 :          0 :                                       (default-to none (get until-burn-ht (map-get? user-data {address: user})))
+     459                 :            :                                     })
+     460                 :          0 :                                   (increment-sc-locked-balance 
+     461                 :          0 :                                     (- amount-ustx 
+     462                 :          0 :                                       (default-to u0 (get locked-balance (map-get? user-data {address: user})))))
+     463                 :          0 :                                   (ok {lock-amount: (get total-locked success-increase),
+     464                 :          0 :                                       stacker: user,
+     465                 :          0 :                                       unlock-burn-height: (get unlock-burn-height success)}))
+     466            [ - ]:          0 :                 error-increase (begin (print "error-increase") (err (* u1000000000 (to-uint error-increase)))))
+     467            [ - ]:          0 :               (ok {
+     468                 :          0 :                     lock-amount: (get locked status),
+     469                 :          0 :                     stacker: user,
+     470                 :          0 :                     unlock-burn-height: (get unlock-burn-height success)})))
+     471            [ - ]:          0 :     error (err (* u1000000 (to-uint error))))))
+     472                 :            : 
+     473                 :            : ;; Rewards transferring functions
+     474                 :            : 
+     475                 :            : (define-private (transfer-rewards-all-stackers (stackers-list-before-cycle (list 300 principal)))
+     476                 :          0 : (map transfer-reward-one-stacker stackers-list-before-cycle))
+     477                 :            : 
+     478                 :            : (define-private (transfer-reward-one-stacker (stacker principal)) 
+     479                 :          0 : (let (
+     480                 :            :       ;; hardcoded reward for testing
+     481                 :          0 :       (reward  
+     482                 :          0 :         (* u426 
+     483                 :          0 :           (default-to u0 
+     484                 :          0 :             (get reward 
+     485                 :          0 :               (map-get? burn-block-rewards { burn-height: (var-get burn-block-to-distribute-rewards)}))))) 
+     486                 :          0 :       (stacker-weight 
+     487                 :          0 :         (default-to u0 
+     488                 :          0 :           (get weight-percentage 
+     489                 :          0 :             (map-get? stacker-weights-per-reward-cycle {stacker: stacker, reward-cycle: (var-get reward-cycle-to-distribute-rewards)}))))
+     490                 :          0 :       (stacker-reward (/ (* stacker-weight reward) ONE-6))) 
+     491                 :          0 :       (if (> stacker-weight u0) 
+     492                 :            : 
+     493            [ - ]:          0 :           (match (as-contract (stx-transfer? stacker-reward tx-sender stacker))
+     494                 :            :             success 
+     495            [ - ]:          0 :               (begin 
+     496                 :          0 :                 (if 
+     497                 :          0 :                   (not (check-can-decrement-reserved-balance stacker-reward))
+     498            [ - ]:          0 :                   (decrement-sc-owned-balance stacker-reward)
+     499            [ - ]:          0 :                   (decrement-sc-reserved-balance stacker-reward)) 
+     500                 :          0 :                 (ok true))
+     501            [ - ]:          0 :             error (err error)) 
+     502            [ - ]:          0 :           (ok false))))
+     503                 :            : 
+     504                 :            : 
+     505                 :            : (define-private (preview-exchange-reward (sats-amount uint) (slippeage uint)) 
+     506                 :          0 : (contract-call? .bridge-contract swap-preview .token-wbtc .token-wstx sats-amount slippeage))
+     507                 :            : 
+     508                 :            : ;; Weight calculation functions
+     509                 :            : 
+     510                 :            : ;; calculating one stacker's weight inside pool based on his balances
+     511                 :            : (define-private (weight-calculator (stacker principal) (stacker-locked uint) (total-locked uint) (liquidity-provider-locked uint)) 
+     512                 :          0 : (begin 
+     513            [ - ]:          0 :   (asserts! (> (+ total-locked liquidity-provider-locked) u0) err-no-locked-funds) 
+     514                 :          0 :   (ok (/ (* stacker-locked ONE-6) (+ total-locked liquidity-provider-locked)))))
+     515                 :            : 
+     516                 :            : (define-private (calculate-all-stackers-weights (stackers-list-before-cycle (list 300 principal)) (next-reward-cycle uint))
+     517                 :          0 : (begin 
+     518                 :          0 :   (map calculate-one-stacker-weight stackers-list-before-cycle)
+     519                 :          0 :   (map-set calculated-weights-reward-cycles {reward-cycle: next-reward-cycle} {calculated: true})
+     520                 :          0 :   (ok true)))
+     521                 :            : 
+     522                 :            : ;; each stacker will have a weight inside the pool which will be used when distributing rewards
+     523                 :            : (define-private (calculate-one-stacker-weight (stacker principal))
+     524                 :          0 : (let ((last-burn-block-before-reward-cycle 
+     525                 :          0 :         (- 
+     526                 :          0 :           (contract-call? 'ST000000000000000000002AMW42H.pox-3 reward-cycle-to-burn-height (var-get reward-cycle-to-calculate-weight)) 
+     527                 :          0 :           u1))
+     528                 :            :       ;; total locked by pool
+     529                 :          0 :       (total-locked-at-reward-cycle 
+     530                 :          0 :         (var-get sc-locked-balance))
+     531                 :            :       ;; total reserved by liquidity provider
+     532                 :          0 :       (liquidity-provider-reserved-at-reward-cycle 
+     533                 :          0 :         (var-get sc-reserved-balance))
+     534                 :            :       ;; total locked by a stacker
+     535                 :          0 :       (stacker-locked-at-reward-cycle 
+     536                 :          0 :         (default-to u0 (get locked-balance (map-get? user-data {address: stacker}))))
+     537                 :          0 :       (liquidity-provider-contribution (+ liquidity-provider-reserved-at-reward-cycle stacker-locked-at-reward-cycle))
+     538                 :            :       ;; the weight calculator result for the given stacker
+     539                 :          0 :       (weight-calculator-result 
+     540                 :          0 :         (if 
+     541                 :          0 :           (is-eq stacker (var-get liquidity-provider)) 
+     542            [ - ]:          0 :           (weight-calculator 
+     543                 :          0 :             stacker 
+     544                 :          0 :             liquidity-provider-contribution
+     545                 :          0 :             total-locked-at-reward-cycle 
+     546                 :          0 :             liquidity-provider-reserved-at-reward-cycle)
+     547            [ - ]:          0 :           (weight-calculator 
+     548                 :          0 :             stacker 
+     549                 :          0 :             stacker-locked-at-reward-cycle 
+     550                 :          0 :             total-locked-at-reward-cycle 
+     551                 :          0 :             liquidity-provider-reserved-at-reward-cycle))))
+     552                 :            :     ;; register the stacker's weight for a given reward cycle using a map
+     553                 :          0 :     (map-set stacker-weights-per-reward-cycle 
+     554                 :          0 :       {stacker: stacker, reward-cycle: (var-get reward-cycle-to-calculate-weight)} 
+     555                 :          0 :       {weight-percentage: 
+     556                 :          0 :         (if 
+     557                 :          0 :           (not (is-err weight-calculator-result)) 
+     558            [ - ]:          0 :           (unwrap-panic weight-calculator-result) 
+     559            [ - ]:          0 :           u0)
+     560                 :            :       })))
+     561                 :            : 
+     562                 :            : 
+     563                 :            : ;; check if pool pox address has won the rewards for a given burn height and store the reward if true
+     564                 :            : (define-private (check-won-block-rewards (burn-height uint)) 
+     565                 :          0 : (let ((reward-pox-addr-list (default-to (list ) (get addrs (get-burn-block-info? pox-addrs burn-height))))) 
+     566                 :          0 :   (if 
+     567                 :          0 :     (is-some 
+     568                 :          0 :       (index-of? reward-pox-addr-list (var-get pool-pox-address))) 
+     569            [ - ]:          0 :     (begin 
+     570                 :          0 :       (register-block-reward burn-height)
+     571                 :          0 :       true) 
+     572            [ - ]:          0 :     false)))
+     573                 :            : 
+     574                 :            : ;; store the reward for a given block using a map
+     575                 :            : (define-private (register-block-reward (burn-height uint)) 
+     576                 :          0 : (map-set burn-block-rewards {burn-height: burn-height} {reward: (default-to u0 (get payout (get-burn-block-info? pox-addrs burn-height)))}))
+     577                 :            : 
+     578                 :          2 : (define-private (remove-stacker-stackers-list (address principal)) (not (is-eq tx-sender address)))
+     579                 :            : 
+     580                 :            : (define-private (increment-sc-delegated-balance (amount-ustx uint)) 
+     581                 :         19 : (var-set sc-delegated-balance (+ (var-get sc-delegated-balance) amount-ustx)))
+     582                 :            : 
+     583                 :            : (define-private (increment-sc-locked-balance (amount-ustx uint)) 
+     584                 :         19 : (var-set sc-locked-balance (+ (var-get sc-locked-balance) amount-ustx)))
+     585                 :            : 
+     586                 :            : (define-private (decrement-sc-delegated-balance (amount-ustx uint)) 
+     587                 :         19 : (var-set sc-delegated-balance (- (var-get sc-delegated-balance) amount-ustx)))
+     588                 :            : 
+     589                 :            : (define-private (decrement-sc-locked-balance (amount-ustx uint)) 
+     590                 :          0 : (var-set sc-locked-balance (- (var-get sc-locked-balance) amount-ustx)))
+     591                 :            : 
+     592                 :            : (define-private (decrement-sc-reserved-balance (amount-ustx uint)) 
+     593                 :          0 : (var-set sc-reserved-balance (- (var-get sc-reserved-balance) amount-ustx)))
+     594                 :            : 
+     595                 :            : (define-private (decrement-sc-owned-balance (amount-ustx uint)) 
+     596                 :          0 : (var-set sc-owned-balance (- (var-get sc-owned-balance) amount-ustx)))
+     597                 :            : 
+     598                 :            : (define-private (check-can-decrement-delegated-balance (amount-ustx uint)) 
+     599                 :          0 : (if 
+     600                 :          0 :   (< 
+     601                 :          0 :     (var-get sc-delegated-balance) 
+     602                 :          0 :     amount-ustx) 
+     603            [ - ]:          0 :   false
+     604            [ - ]:          0 : true))
+     605                 :            : 
+     606                 :            : (define-private (check-can-decrement-locked-balance (amount-ustx uint)) 
+     607                 :          0 : (if 
+     608                 :          0 :   (< 
+     609                 :          0 :     (var-get sc-locked-balance) 
+     610                 :          0 :     amount-ustx) 
+     611            [ - ]:          0 :   false
+     612            [ - ]:          0 : true))
+     613                 :            : 
+     614                 :            : (define-private (check-can-decrement-reserved-balance (amount-ustx uint)) 
+     615                 :          0 : (if 
+     616                 :          0 :   (< 
+     617                 :          0 :     (var-get sc-reserved-balance) 
+     618                 :          0 :     amount-ustx) 
+     619            [ - ]:          0 :   false
+     620            [ - ]:          0 : true))
+     621                 :            : 
+     622                 :            : (define-private (check-can-decrement-total-balance (amount-ustx uint)) 
+     623                 :          0 : (if 
+     624                 :          0 :   (< 
+     625                 :          0 :     (var-get sc-total-balance) 
+     626                 :          0 :     amount-ustx) 
+     627            [ - ]:          0 :   false
+     628            [ - ]:          0 : true))
+     629                 :            : 
+     630                 :            : (define-private (check-can-decrement-owned-balance (amount-ustx uint)) 
+     631                 :          0 : (if 
+     632                 :          0 :   (< 
+     633                 :          0 :     (var-get sc-owned-balance) 
+     634                 :          0 :     amount-ustx) 
+     635            [ - ]:          0 :   false
+     636            [ - ]:          0 : true))
+     637                 :            : 
+     638                 :            : (define-private (min (amount-1 uint) (amount-2 uint))
+     639                 :         19 :   (if (< amount-1 amount-2)
+     640            [ + ]:         17 :     amount-1
+     641            [ + ]:          2 :     amount-2))
+     642                 :            : 
+     643                 :            : (define-private (get-next-reward-cycle) 
+     644                 :          0 : (+ (contract-call? 'ST000000000000000000002AMW42H.pox-3 burn-height-to-reward-cycle burn-block-height) u1))
+     645                 :            : 
+     646                 :            : ;; Read-only helper functions
+     647                 :            : 
+     648                 :            : (define-read-only (get-stx-account)
+     649                 :          0 : (stx-account tx-sender))
+     650                 :            : 
+     651                 :            : (define-read-only (get-pool-members) 
+     652                 :          0 : (var-get stackers-list))
+     653                 :            : 
+     654                 :            : (define-read-only (check-caller-allowed)
+     655            [ + ]:         22 :   (or (is-eq tx-sender contract-caller)
+     656            [ - ]:          0 :     (let ((caller-allowed
+     657                 :            :             ;; if not in the caller map, return false
+     658                 :          0 :             (unwrap! 
+     659                 :          0 :               (map-get? allowance-contract-callers
+     660                 :          0 :                 { sender: tx-sender, contract-caller: contract-caller})
+     661                 :          0 :             false))
+     662                 :          0 :           (expires-at
+     663                 :            :             ;; if until-burn-ht not set, then return true (because no expiry)
+     664                 :          0 :             (unwrap! (get until-burn-ht caller-allowed) true)))
+     665                 :            :       ;; is the caller allowance still valid
+     666                 :          0 :       (< burn-block-height expires-at))))
+     667                 :            : 
+     668                 :            : (define-read-only (is-in-pool) 
+     669                 :         20 : (default-to false (get is-in-pool (map-get? user-data {address: tx-sender}))))
+     670                 :            : 
+     671                 :            : (define-read-only (get-stacker-weight (stacker principal) (reward-cycle uint)) 
+     672                 :          0 : (get weight-percentage (map-get? stacker-weights-per-reward-cycle {stacker: stacker, reward-cycle: reward-cycle})))
+     673                 :            : 
+     674                 :            : (define-read-only (get-SC-total-balance) 
+     675                 :          1 : (var-get sc-total-balance))
+     676                 :            : 
+     677                 :            : (define-read-only (get-SC-owned-balance) 
+     678                 :          0 : (var-get sc-owned-balance))
+     679                 :            : 
+     680                 :            : (define-read-only (get-SC-locked-balance)
+     681                 :          0 : (var-get sc-locked-balance))
+     682                 :            : 
+     683                 :            : (define-read-only (get-SC-reserved-balance) 
+     684                 :          0 : (var-get sc-reserved-balance))
+     685                 :            : 
+     686                 :            : (define-read-only (get-user-data (user principal)) 
+     687                 :         19 : (map-get? user-data {address: user}))
+     688                 :            : 
+     689                 :            : (define-read-only (check-pool-SC-pox-2-allowance)
+     690                 :         45 : (is-some (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-allowance-contract-callers tx-sender pool-contract)))
+     691                 :            : 
+     692                 :            : (define-read-only (get-check-delegation (stacker principal))
+     693                 :          1 : (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-check-delegation stacker))
+     694                 :            : 
+     695                 :            : (define-read-only (get-pox-addr-indices (reward-cycle uint))
+     696                 :          0 : (map-get? pox-addr-indices reward-cycle))
+     697                 :            : 
+     698                 :            : (define-read-only (get-block-rewards (burn-height uint)) 
+     699                 :          0 : (ok (get-burn-block-info? pox-addrs burn-height)))
+     700                 :            : 
+     701                 :            : (define-read-only (can-lock-now (cycle uint))
+     702                 :          0 : (> burn-block-height (+ (contract-call? 'ST000000000000000000002AMW42H.pox-3 reward-cycle-to-burn-height cycle) half-cycle-length)))
+     703                 :            : 
+     704                 :            : (define-read-only (get-delegated-amount (user principal))
+     705                 :         19 : (default-to u0 (get amount-ustx (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-delegation-info user))))
+     706                 :            : 
+     707                 :            : (define-read-only (get-liquidity-provider) 
+     708                 :          0 : (var-get liquidity-provider))
+     709                 :            : 
+     710                 :            : (define-read-only (get-amount-rewarded) 
+     711                 :          0 : (var-get amount-rewarded))
+     712                 :            : 
+     713                 :            : (define-read-only (get-blocks-rewarded) 
+     714                 :          0 : (var-get blocks-rewarded))
+     715                 :            : 
+     716                 :            : (define-read-only (get-stacked-this-cycle) 
+     717                 :          0 : (var-get sc-locked-balance))
+     718                 :            : 
+     719                 :            : (define-private (check-is-liquidity-provider (address principal)) 
+     720                 :          0 : (is-eq address (var-get liquidity-provider)))
+     721                 :            : 
+     722                 :            : (define-private (check-is-stacker (address principal)) 
+     723                 :          0 : (default-to false (get is-in-pool (map-get? user-data {address: address}))))
+     724                 :            : 
+     725                 :            : (define-read-only (get-address-status (address principal))
+     726                 :          0 : (if (check-is-liquidity-provider address)  
+     727            [ - ]:          0 :   (ok "is-provider")
+     728            [ - ]:          0 :   (if (check-is-stacker address)
+     729            [ - ]:          0 :     (ok "is-stacker")
+     730            [ - ]:          0 :     (ok "is-none"))))
+     731                 :            : 
+     732                 :            : (define-read-only (calculate-extra-reserved-funds) 
+     733                 :            : ;; subtract the potential return from the total reserved balance and get the extra reserved balance
+     734                 :          0 : (- 
+     735                 :          0 :   (var-get sc-reserved-balance) 
+     736                 :          0 :     (/ 
+     737                 :          0 :       (var-get sc-locked-balance) 
+     738                 :          0 :       (var-get return-div))))
+     739                 :            : 
+     740                 :            : (define-read-only (can-withdraw-extra-reserved-now) 
+     741                 :            : ;; liquidity provider can only withdraw extra reserved balance within the last 10 blocks of a reward cycle
+     742                 :          0 : (let ((mod-burn-height (mod burn-block-height REWARD_CYCLE_LENGTH))
+     743                 :          0 :       (start-value (- REWARD_CYCLE_LENGTH u10))
+     744                 :          0 :       (end-value (- REWARD_CYCLE_LENGTH u1))) 
+     745         [ -  - ]:          0 :   (and (>= mod-burn-height start-value) (<= mod-burn-height end-value))))
+     746                 :            : 
+     747                 :            : (define-read-only (get-return) 
+     748                 :          0 : (var-get return-div))
+     749                 :            : 
+     750                 :            : (define-read-only (get-minimum-deposit-liquidity-provider) 
+     751                 :          0 : (var-get minimum-deposit-amount-liquidity-provider))
+     752                 :            : 
+     753                 :            : (define-read-only (was-block-claimed (rewarded-burn-block uint))
+     754                 :          0 : (map-get? already-rewarded {burn-block-height: rewarded-burn-block}))
+
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/stacking-pool.clar.func-sort-c.html b/sbtc-ops/smart-contract/coverage/contracts/stacking-pool.clar.func-sort-c.html new file mode 100644 index 00000000..38d23d3b --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/stacking-pool.clar.func-sort-c.html @@ -0,0 +1,373 @@ + + + + + + + LCOV - coverage.lcov - contracts/stacking-pool.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - stacking-pool.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:74371.6 %
Date:2023-08-18 21:53:02Functions:1731.4 %
Branches:0990.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
calculate-all-stackers-weights0
calculate-extra-reserved-funds0
calculate-one-stacker-weight0
can-lock-now0
can-withdraw-extra-reserved-now0
check-caller-allowed0
check-can-decrement-delegated-balance0
check-can-decrement-locked-balance0
check-can-decrement-owned-balance0
check-can-decrement-reserved-balance0
check-can-decrement-total-balance0
check-is-liquidity-provider0
check-is-stacker0
check-pool-SC-pox-2-allowance0
check-won-block-rewards0
decrement-sc-delegated-balance0
decrement-sc-locked-balance0
decrement-sc-owned-balance0
decrement-sc-reserved-balance0
delegate-stack-extend-increase0
delegate-stack-stx0
delegate-stack-stx-many0
delegate-stx0
delegate-stx-inner0
deposit-stx-liquidity-provider0
disallow-contract-caller0
get-SC-locked-balance0
get-SC-owned-balance0
get-SC-reserved-balance0
get-SC-total-balance0
get-address-status0
get-amount-rewarded0
get-block-rewards0
get-blocks-rewarded0
get-check-delegation0
get-delegated-amount0
get-liquidity-provider0
get-minimum-deposit-liquidity-provider0
get-next-reward-cycle0
get-pool-members0
get-pox-addr-indices0
get-return0
get-stacked-this-cycle0
get-stacker-weight0
get-stx-account0
get-user-data0
increment-sc-delegated-balance0
increment-sc-locked-balance0
is-in-pool0
join-stacking-pool0
lock-delegated-stx0
maybe-stack-aggregation-commit0
min0
multiple-blocks-check-won-rewards0
preview-exchange-reward0
quit-stacking-pool0
register-block-reward0
remove-stacker-stackers-list0
reserve-funds-future-rewards0
reward-distribution0
set-active0
set-liquidity-provider0
set-pool-pox-address0
transfer-reward-one-stacker0
transfer-rewards-all-stackers0
unlock-extra-reserved-funds0
update-return0
update-sc-balances0
update-sc-balances-one-stacker0
was-block-claimed0
weight-calculator0
withdraw-stx-liquidity-provider0
allow-contract-caller41
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/stacking-pool.clar.func.html b/sbtc-ops/smart-contract/coverage/contracts/stacking-pool.clar.func.html new file mode 100644 index 00000000..440648e9 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/stacking-pool.clar.func.html @@ -0,0 +1,373 @@ + + + + + + + LCOV - coverage.lcov - contracts/stacking-pool.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - stacking-pool.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:74371.6 %
Date:2023-08-18 21:53:02Functions:1731.4 %
Branches:0990.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
allow-contract-caller41
calculate-all-stackers-weights0
calculate-extra-reserved-funds0
calculate-one-stacker-weight0
can-lock-now0
can-withdraw-extra-reserved-now0
check-caller-allowed0
check-can-decrement-delegated-balance0
check-can-decrement-locked-balance0
check-can-decrement-owned-balance0
check-can-decrement-reserved-balance0
check-can-decrement-total-balance0
check-is-liquidity-provider0
check-is-stacker0
check-pool-SC-pox-2-allowance0
check-won-block-rewards0
decrement-sc-delegated-balance0
decrement-sc-locked-balance0
decrement-sc-owned-balance0
decrement-sc-reserved-balance0
delegate-stack-extend-increase0
delegate-stack-stx0
delegate-stack-stx-many0
delegate-stx0
delegate-stx-inner0
deposit-stx-liquidity-provider0
disallow-contract-caller0
get-SC-locked-balance0
get-SC-owned-balance0
get-SC-reserved-balance0
get-SC-total-balance0
get-address-status0
get-amount-rewarded0
get-block-rewards0
get-blocks-rewarded0
get-check-delegation0
get-delegated-amount0
get-liquidity-provider0
get-minimum-deposit-liquidity-provider0
get-next-reward-cycle0
get-pool-members0
get-pox-addr-indices0
get-return0
get-stacked-this-cycle0
get-stacker-weight0
get-stx-account0
get-user-data0
increment-sc-delegated-balance0
increment-sc-locked-balance0
is-in-pool0
join-stacking-pool0
lock-delegated-stx0
maybe-stack-aggregation-commit0
min0
multiple-blocks-check-won-rewards0
preview-exchange-reward0
quit-stacking-pool0
register-block-reward0
remove-stacker-stackers-list0
reserve-funds-future-rewards0
reward-distribution0
set-active0
set-liquidity-provider0
set-pool-pox-address0
transfer-reward-one-stacker0
transfer-rewards-all-stackers0
unlock-extra-reserved-funds0
update-return0
update-sc-balances0
update-sc-balances-one-stacker0
was-block-claimed0
weight-calculator0
withdraw-stx-liquidity-provider0
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/stacking-pool.clar.gcov.html b/sbtc-ops/smart-contract/coverage/contracts/stacking-pool.clar.gcov.html new file mode 100644 index 00000000..67cc6888 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/stacking-pool.clar.gcov.html @@ -0,0 +1,843 @@ + + + + + + + LCOV - coverage.lcov - contracts/stacking-pool.clar + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - stacking-pool.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:74371.6 %
Date:2023-08-18 21:53:02Functions:1731.4 %
Branches:0990.0 %
+
+ + + + + + + + +

+
           Branch data     Line data    Source code
+
+       1                 :            : 
+       2                 :            : ;; Main Stacking Pool Contract
+       3                 :            : 
+       4                 :            : 
+       5                 :            : ;; Flow
+       6                 :            : ;; 1. The liquidity provider deploys the contract 
+       7                 :            : ;; 2. He locks into the SC a sum which will be sufficient to cover all the stackers' rewards
+       8                 :            : ;; 3. Stackers who want to stack through the stacking pool have to join the pool.
+       9                 :            : ;; 4. They will have to delegate the STX they want to stack to the pool's POX address
+      10                 :            : ;; 5. When the total amount commited is enough to be stacked, it will be auto committed
+      11                 :            : ;; 6. The stackers will be able to claim the rewards after they are distributed
+      12                 :            : 
+      13                 :            : ;; + In prepare phase, calculate weight of the stackers inside the pool (Notion)
+      14                 :            : 
+      15                 :            : ;; Default length of the PoX registration window, in burnchain blocks.
+      16                 :            : ;;TODO: pox-2 mainnet address: 'SP000000000000000000002Q6VF78
+      17                 :            : (define-constant PREPARE_CYCLE_LENGTH (get prepare-cycle-length (unwrap-panic (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-pox-info))))
+      18                 :            : 
+      19                 :            : ;; Default length of the PoX reward cycle, in burnchain blocks.
+      20                 :            : (define-constant REWARD_CYCLE_LENGTH (get reward-cycle-length (unwrap-panic (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-pox-info))))
+      21                 :            : 
+      22                 :            : ;; Half cycle length is 1050 for mainnet
+      23                 :            : (define-constant half-cycle-length (/ (get reward-cycle-length (unwrap-panic (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-pox-info))) u2))
+      24                 :            : 
+      25                 :            : (define-constant err-only-liquidity-provider (err u100))
+      26                 :            : (define-constant err-already-in-pool (err u101))
+      27                 :            : (define-constant err-not-in-pool (err u102))
+      28                 :            : (define-constant err-liquidity-provider-not-permitted (err u103))
+      29                 :            : (define-constant err-wrong-moment-to-update-balances (err u123))
+      30                 :            : (define-constant err-allow-pool-in-SC-first (err u195))
+      31                 :            : (define-constant err-allow-pool-in-pox-2-first (err u199))
+      32                 :            : (define-constant err-insufficient-funds (err u200))
+      33                 :            : (define-constant err-revoke-delegation-in-pox-first (err u201))
+      34                 :            : (define-constant err-disallow-pool-in-pox-2-first (err u299))
+      35                 :            : (define-constant err-full-stacking-pool (err u300))
+      36                 :            : (define-constant err-same-value (err u325))
+      37                 :            : (define-constant err-future-reward-not-covered (err u333))
+      38                 :            : (define-constant err-not-delegated-that-amount (err u396))
+      39                 :            : (define-constant err-no-locked-funds (err u456))
+      40                 :            : (define-constant err-too-early (err u500))
+      41                 :            : (define-constant err-decrease-forbidden (err u503))
+      42                 :            : (define-constant err-no-reward-yet (err u576))
+      43                 :            : (define-constant err-not-enough-reserved-balance (err u579))
+      44                 :            : (define-constant err-stacking-permission-denied (err u609))
+      45                 :            : (define-constant err-transfer-failed (err u777))
+      46                 :            : (define-constant err-cant-calculate-weights (err u888))
+      47                 :            : (define-constant err-already-updated-balances (err u895))
+      48                 :            : (define-constant err-no-reward-for-this-block (err u900))
+      49                 :            : (define-constant err-already-rewarded-block (err u992))
+      50                 :            : (define-constant err-cant-withdraw-now (err u995))
+      51                 :            : (define-constant err-cant-unwrap-exchange-preview (err u996))
+      52                 :            : (define-constant err-return-div-exceeds-maximum (err u997))
+      53                 :            : (define-constant err-pox-address-deactivated (err u999))
+      54                 :            : (define-constant err-weights-not-calculated (err u1000))
+      55                 :            : 
+      56                 :            : (define-constant first-deposit u0)
+      57                 :            : (define-constant list-max-len u300)
+      58                 :            : (define-constant pool-contract (as-contract tx-sender))
+      59                 :            : (define-constant pox-2-contract (as-contract 'ST000000000000000000002AMW42H.pox-3))
+      60                 :            : (define-constant blocks-to-pass-until-reward u101)
+      61                 :            : (define-constant max-return-div-accepted u20)
+      62                 :            : (define-constant ONE-6 u1000000)
+      63                 :            : ;; liquidity provider data vars
+      64                 :            : (define-data-var sc-total-balance uint u0)
+      65                 :            : (define-data-var sc-owned-balance uint u0)
+      66                 :            : (define-data-var sc-reserved-balance uint u0)
+      67                 :            :   ;; (the percentage of the locked balance assured by the liquidity provider) ^ -1,
+      68                 :            :   ;; return-div = u20 => the liquidity provider is ready to grant a maximum of 5% of the total locked balance.
+      69                 :            : (define-data-var return-div uint u20)  
+      70                 :            : ;; stackers data vars
+      71                 :            : (define-data-var sc-delegated-balance uint u0)
+      72                 :            : (define-data-var sc-locked-balance uint u0)
+      73                 :            : 
+      74                 :            : ;; temporary data var helpers
+      75                 :            : (define-data-var calc-delegated-balance uint u0)
+      76                 :            : (define-data-var calc-locked-balance uint u0)
+      77                 :            : (define-data-var reward-cycle-to-calculate-weight uint u0)
+      78                 :            : (define-data-var burn-block-to-distribute-rewards uint u0)
+      79                 :            : (define-data-var reward-cycle-to-distribute-rewards uint u0)
+      80                 :            : (define-data-var temp-current-reward uint u0)
+      81                 :            : ;; common data vars
+      82                 :            : (define-data-var minimum-deposit-amount-liquidity-provider uint u10000000000) ;; minimum amount for the liquidity provider to transfer after deploy in microSTX (STX * 10^-6)
+      83                 :            : (define-data-var stackers-list (list 300 principal) (list tx-sender))
+      84                 :            : (define-data-var liquidity-provider principal tx-sender)
+      85                 :            : (define-data-var active bool true)
+      86                 :            : (define-data-var blocks-rewarded uint u0)
+      87                 :            : (define-data-var amount-rewarded uint u0)
+      88                 :            : 
+      89                 :            : 
+      90                 :            : ;; liqidity provider reward bitcoin address
+      91                 :            : (define-data-var pool-pox-address {hashbytes: (buff 32), version: (buff 1)}
+      92                 :            :   {
+      93                 :            :     version: 0x04,
+      94                 :            :     hashbytes: 0x83ed66860315e334010bbfb76eb3eef887efee0a})
+      95                 :            : 
+      96                 :            : (define-data-var stx-buffer uint u0) ;; 0 STX
+      97                 :            : 
+      98                 :            : ;; data maps
+      99                 :            : 
+     100                 :            : (define-map user-data { address: principal } {is-in-pool:bool, delegated-balance: uint, locked-balance:uint, until-burn-ht: (optional uint) })
+     101                 :            : (define-map user-revoked-delegation principal bool)
+     102                 :            : (define-map pox-addr-indices uint uint)
+     103                 :            : (define-map last-aggregation uint uint)
+     104                 :            : (define-map allowance-contract-callers { sender: principal, contract-caller: principal} { until-burn-ht: (optional uint)})
+     105                 :            : (define-map stacker-weights-per-reward-cycle { stacker: principal, reward-cycle: uint } { weight-percentage: uint })
+     106                 :            : (define-map calculated-weights-reward-cycles { reward-cycle: uint } { calculated: bool })
+     107                 :            : (define-map burn-block-rewards { burn-height: uint } { reward: uint })
+     108                 :            : (define-map updated-sc-balances { reward-cycle: uint } { updated: bool, stackers-list: (list 300 principal) })
+     109                 :            : (define-map already-rewarded { burn-block-height: uint } { value: bool })
+     110                 :         41 : (allow-contract-caller (as-contract tx-sender) none)
+     111                 :            : 
+     112                 :         41 : (map-set user-data {address: tx-sender} {is-in-pool: true, delegated-balance: u0, locked-balance: u0, until-burn-ht: none})
+     113                 :            : 
+     114                 :            : ;; Public functions
+     115                 :            : 
+     116                 :            : (define-public (deposit-stx-liquidity-provider (amount uint)) 
+     117                 :          0 : (begin 
+     118            [ - ]:          0 :   (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider)
+     119            [ - ]:          0 :   (asserts! (>= amount (var-get minimum-deposit-amount-liquidity-provider)) err-future-reward-not-covered)
+     120                 :          0 :   (try! (stx-transfer? amount tx-sender pool-contract))
+     121                 :          0 :   (var-set sc-total-balance (+ amount (var-get sc-total-balance)))
+     122                 :          0 :   (var-set sc-owned-balance (+ amount (var-get sc-owned-balance)))
+     123                 :          0 :   (ok true)))
+     124                 :            : 
+     125                 :            : (define-public (withdraw-stx-liquidity-provider (amount uint)) 
+     126                 :          0 : (begin 
+     127            [ - ]:          0 :   (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider)
+     128                 :          0 :   (asserts! 
+     129                 :          0 :     (and 
+     130            [ - ]:          0 :       (check-can-decrement-owned-balance amount) 
+     131            [ - ]:          0 :       (check-can-decrement-total-balance amount)) 
+     132            [ - ]:          0 :   err-insufficient-funds)
+     133                 :          0 :   (try! 
+     134                 :          0 :     (as-contract 
+     135                 :          0 :       (stx-transfer? amount tx-sender (var-get liquidity-provider))))
+     136                 :          0 :   (var-set sc-total-balance (- (var-get sc-total-balance) amount))
+     137                 :          0 :   (var-set sc-owned-balance (- (var-get sc-owned-balance) amount))
+     138                 :          0 :   (ok true)))
+     139                 :            : 
+     140                 :            : (define-public (reserve-funds-future-rewards (amount uint)) 
+     141                 :          0 : (begin 
+     142            [ - ]:          0 :   (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider)
+     143            [ - ]:          0 :   (asserts! (>= (var-get sc-owned-balance) amount) err-insufficient-funds) 
+     144            [ - ]:          0 :   (asserts! (>= amount (var-get minimum-deposit-amount-liquidity-provider)) err-future-reward-not-covered)
+     145                 :          0 :   (var-set sc-owned-balance (- (var-get sc-owned-balance) amount))
+     146                 :          0 :   (var-set sc-reserved-balance (+ (var-get sc-reserved-balance) amount))
+     147                 :          0 :   (ok true)))
+     148                 :            : 
+     149                 :            : (define-public (unlock-extra-reserved-funds) 
+     150                 :          0 : (begin 
+     151                 :          0 :   (asserts! 
+     152                 :          0 :     (is-eq 
+     153                 :          0 :       contract-caller 
+     154                 :          0 :       (var-get liquidity-provider)) 
+     155            [ - ]:          0 :   err-only-liquidity-provider)
+     156            [ - ]:          0 :   (asserts! (can-withdraw-extra-reserved-now) err-cant-withdraw-now)
+     157                 :          0 :     (let ((unreserve-amount (calculate-extra-reserved-funds))
+     158                 :          0 :           (reserved-balance-before (var-get sc-reserved-balance))
+     159                 :          0 :           (owned-balance-before (var-get sc-owned-balance))) 
+     160                 :          0 :       (var-set sc-reserved-balance 
+     161                 :          0 :         (- 
+     162                 :          0 :           reserved-balance-before 
+     163                 :          0 :           unreserve-amount))
+     164                 :          0 :       (var-set sc-owned-balance 
+     165                 :          0 :         (+ 
+     166                 :          0 :           owned-balance-before 
+     167                 :          0 :           unreserve-amount))
+     168                 :          0 :       (ok unreserve-amount))))
+     169                 :            : 
+     170                 :            : (define-public (join-stacking-pool)
+     171                 :          0 : (begin
+     172            [ - ]:          0 :   (asserts! (check-pool-SC-pox-2-allowance) err-allow-pool-in-pox-2-first)
+     173            [ - ]:          0 :   (asserts! (is-none (map-get? user-data {address: tx-sender})) err-already-in-pool)
+     174                 :          0 :   (var-set stackers-list (unwrap! (as-max-len? (concat (var-get stackers-list) (list tx-sender )) u300) err-full-stacking-pool)) 
+     175                 :          0 :   (map-set user-data {address: tx-sender} {is-in-pool: true, delegated-balance: u0, locked-balance: u0, until-burn-ht: none})
+     176                 :          0 :   (ok true)))
+     177                 :            : 
+     178                 :            : (define-public (allow-contract-caller (caller principal) (until-burn-ht (optional uint)))
+     179                 :         41 : (begin
+     180            [ - ]:         41 :   (asserts! (is-eq tx-sender contract-caller) err-stacking-permission-denied)
+     181                 :         41 :   (ok (map-set allowance-contract-callers
+     182                 :         41 :         { sender: tx-sender, contract-caller: caller}
+     183                 :         41 :         { until-burn-ht: until-burn-ht}))))
+     184                 :            : 
+     185                 :            : ;; revoke contract-caller authorization to call stacking methods
+     186                 :            : (define-public (disallow-contract-caller (caller principal))
+     187                 :          0 : (begin
+     188            [ - ]:          0 :   (asserts! (is-eq tx-sender contract-caller) err-stacking-permission-denied)
+     189                 :          0 :   (ok (map-delete allowance-contract-callers { sender: tx-sender, contract-caller: caller}))))
+     190                 :            : 
+     191                 :            : (define-public (quit-stacking-pool)
+     192                 :          0 : (begin
+     193            [ - ]:          0 :   (asserts! (is-none (get-check-delegation tx-sender)) err-revoke-delegation-in-pox-first)
+     194            [ - ]:          0 :   (asserts! (not (check-pool-SC-pox-2-allowance)) err-disallow-pool-in-pox-2-first)
+     195            [ - ]:          0 :   (asserts! (is-some (map-get? user-data {address: tx-sender})) err-not-in-pool)
+     196            [ - ]:          0 :   (asserts! (not (is-eq contract-caller (var-get liquidity-provider))) err-liquidity-provider-not-permitted)
+     197                 :          0 :     (try! (disallow-contract-caller pool-contract))
+     198                 :          0 :     (var-set stackers-list (filter remove-stacker-stackers-list (var-get stackers-list))) 
+     199                 :          0 :     (map-delete user-data {address: tx-sender})
+     200                 :          0 :     (ok true)))
+     201                 :            : 
+     202                 :            : ;; The SC balances need to be updated during the first half of every Prepare Phase
+     203                 :            : ;; Everyone can call the function in order to recalculate each stacker's weight inside the pool
+     204                 :            : ;; This WILL directly AFFECT the reward distribution
+     205                 :            : (define-public (update-sc-balances)
+     206                 :          0 : (let (
+     207                 :          0 :   (next-reward-cycle (get-next-reward-cycle))
+     208                 :          0 :   (next-reward-cycle-first-block (contract-call? 'ST000000000000000000002AMW42H.pox-3 reward-cycle-to-burn-height (get-next-reward-cycle)))) 
+     209                 :          0 : (begin 
+     210                 :            :   ;; check current block to be inside the first half of the current reward cycle's prepare phase
+     211                 :          0 :   (asserts! 
+     212                 :          0 :     (<= 
+     213                 :          0 :       burn-block-height 
+     214                 :          0 :       (+ 
+     215                 :          0 :         (- 
+     216                 :          0 :           next-reward-cycle-first-block
+     217                 :          0 :           PREPARE_CYCLE_LENGTH)
+     218                 :          0 :         (/ PREPARE_CYCLE_LENGTH u2))) 
+     219            [ - ]:          0 :   err-wrong-moment-to-update-balances)
+     220            [ - ]:          0 :   (asserts! (is-none (map-get? updated-sc-balances {reward-cycle: next-reward-cycle})) err-already-updated-balances)
+     221                 :          0 :   (var-set calc-locked-balance u0)
+     222                 :          0 :   (var-set calc-delegated-balance u0)
+     223                 :          0 :   (map update-sc-balances-one-stacker (var-get stackers-list))
+     224                 :          0 :   (var-set sc-locked-balance (var-get calc-locked-balance))
+     225                 :          0 :   (var-set sc-delegated-balance (var-get calc-delegated-balance))
+     226                 :          0 :   (map-set updated-sc-balances {reward-cycle: next-reward-cycle} {updated: true, stackers-list: (var-get stackers-list)})
+     227                 :          0 :   (var-set reward-cycle-to-calculate-weight next-reward-cycle)
+     228                 :          0 :   (unwrap! (calculate-all-stackers-weights (var-get stackers-list) next-reward-cycle) err-cant-calculate-weights)
+     229                 :          0 :   (ok true))))
+     230                 :            : 
+     231                 :            : ;; recalculate balances inside pool
+     232                 :            : (define-public (update-sc-balances-one-stacker (stacker principal))
+     233                 :          0 : (let ((user-until-burn-ht (default-to u0 (default-to (some u0) (get until-burn-ht (map-get? user-data {address: stacker})))))
+     234                 :          0 :       (user-delegated-balance (default-to u0 (get delegated-balance (map-get? user-data {address: stacker}))))
+     235                 :          0 :       (user-locked-balance (default-to u0 (get locked-balance (map-get? user-data {address: stacker}))))) 
+     236                 :          0 :   (ok 
+     237                 :            :     ;; if burn-block-height < user's unlock burn block height, then user's balances 
+     238                 :          0 :     (if 
+     239                 :          0 :         (< 
+     240                 :          0 :           burn-block-height 
+     241                 :          0 :           user-until-burn-ht) 
+     242            [ - ]:          0 :         (begin 
+     243                 :          0 :           (var-set calc-locked-balance 
+     244                 :          0 :             (+ 
+     245                 :          0 :               (var-get calc-locked-balance) 
+     246                 :          0 :               user-locked-balance))
+     247                 :          0 :           (var-set calc-delegated-balance 
+     248                 :          0 :             (+ 
+     249                 :          0 :               (var-get calc-delegated-balance) 
+     250                 :          0 :               user-delegated-balance))) 
+     251            [ - ]:          0 :         (begin 
+     252                 :          0 :           (var-set calc-locked-balance (var-get calc-locked-balance))
+     253                 :          0 :           (var-set calc-delegated-balance (var-get calc-delegated-balance)))))))
+     254                 :            : 
+     255                 :            : ;; The rewards will be distributed. At that moment, the SC balance should have been updated and the stackers' weights calculated
+     256                 :            : (define-public (reward-distribution (rewarded-burn-block uint))
+     257                 :          0 : (let ((reward-cycle 
+     258                 :          0 :         (contract-call? 'ST000000000000000000002AMW42H.pox-3 burn-height-to-reward-cycle rewarded-burn-block))
+     259                 :          0 :       (stackers-list-for-reward-cycle 
+     260                 :          0 :         (default-to (list ) (get stackers-list (map-get? updated-sc-balances {reward-cycle: reward-cycle})))))
+     261            [ - ]:          0 :           (asserts! (< rewarded-burn-block burn-block-height) err-no-reward-yet)
+     262            [ - ]:          0 :           (asserts! (check-won-block-rewards rewarded-burn-block) err-no-reward-for-this-block)
+     263            [ - ]:          0 :           (asserts! (is-none (map-get? already-rewarded {burn-block-height: rewarded-burn-block})) err-already-rewarded-block)
+     264                 :          0 :           (var-set burn-block-to-distribute-rewards rewarded-burn-block)
+     265                 :          0 :           (var-set amount-rewarded (+ (var-get amount-rewarded) (default-to u0 (get reward (map-get? burn-block-rewards { burn-height: (var-get burn-block-to-distribute-rewards)})))))
+     266                 :          0 :           (var-set blocks-rewarded (+ (var-get blocks-rewarded) u1))
+     267                 :          0 :           (map-set already-rewarded {burn-block-height: rewarded-burn-block} {value: true})
+     268                 :          0 :           (var-set reward-cycle-to-distribute-rewards reward-cycle)
+     269                 :          0 :           (match (map-get? calculated-weights-reward-cycles {reward-cycle: reward-cycle}) 
+     270            [ - ]:          0 :             calculated (ok 
+     271                 :          0 :                           (unwrap-panic (transfer-rewards-all-stackers stackers-list-for-reward-cycle)))
+     272            [ - ]:          0 :             err-weights-not-calculated)))
+     273                 :            : 
+     274                 :            : ;; delegating stx to the pool SC
+     275                 :            : (define-public (delegate-stx (amount-ustx uint))
+     276                 :          0 : (let ((user tx-sender)
+     277                 :          0 :       (current-cycle (contract-call? 'ST000000000000000000002AMW42H.pox-3 current-pox-reward-cycle)))
+     278            [ - ]:          0 :   (asserts! (check-caller-allowed) err-stacking-permission-denied)
+     279            [ - ]:          0 :   (asserts! (check-pool-SC-pox-2-allowance) err-allow-pool-in-pox-2-first)
+     280                 :            :   
+     281            [ - ]:          0 :   (asserts! (is-in-pool) err-not-in-pool)
+     282                 :          0 :   (try! (delegate-stx-inner amount-ustx (as-contract tx-sender) none))
+     283                 :          0 :   (try! (as-contract (lock-delegated-stx user)))
+     284                 :          0 :   (ok (maybe-stack-aggregation-commit current-cycle))))
+     285                 :            : 
+     286                 :            : ;; Stacks the delegated amount for the given user for the next cycle.
+     287                 :            : ;; This function can be called by automation, friends or family for user that have delegated once.
+     288                 :            : ;; This function can be called only after the current cycle is half through
+     289                 :            : (define-public (delegate-stack-stx (user principal))
+     290                 :          0 :   (let ((current-cycle (contract-call? 'ST000000000000000000002AMW42H.pox-3 current-pox-reward-cycle)))
+     291            [ - ]:          0 :     (asserts! (can-lock-now current-cycle) err-too-early)
+     292                 :            :     ;; Do 3.
+     293                 :          0 :     (try! (as-contract (lock-delegated-stx user)))
+     294                 :            :     ;; Do 4.
+     295                 :          0 :     (ok (maybe-stack-aggregation-commit current-cycle))))
+     296                 :            : 
+     297                 :            : (define-public (delegate-stack-stx-many (stackers-lock-list (list 100 principal))) 
+     298                 :          0 : (ok (map delegate-stack-stx stackers-lock-list)))
+     299                 :            : 
+     300                 :            : (define-public (multiple-blocks-check-won-rewards (burn-heights-list (list 100 uint))) 
+     301                 :          0 : (ok (map check-won-block-rewards burn-heights-list)))
+     302                 :            : 
+     303                 :            : ;; liquidity provider pool management functions
+     304                 :            : 
+     305                 :            : (define-public (set-pool-pox-address (new-pool-pox-address {hashbytes: (buff 32), version: (buff 1)})) 
+     306                 :          0 : (begin 
+     307            [ - ]:          0 :   (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider)
+     308                 :          0 :   (ok (var-set pool-pox-address new-pool-pox-address))))
+     309                 :            : 
+     310                 :            : (define-public (set-active (is-active bool))
+     311                 :          0 : (begin
+     312            [ - ]:          0 :   (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider)    
+     313                 :          0 :   (ok (var-set active is-active))))
+     314                 :            : 
+     315                 :            : (define-public (set-liquidity-provider (new-liquidity-provider principal)) 
+     316                 :          0 : (begin 
+     317            [ - ]:          0 :   (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider)
+     318            [ - ]:          0 :   (asserts! (is-some (map-get? user-data {address: new-liquidity-provider})) err-not-in-pool) ;; new liquidity provider should be in pool
+     319                 :          0 :   (ok (var-set liquidity-provider new-liquidity-provider))))
+     320                 :            : 
+     321                 :            : (define-public (update-return (new-return-value uint)) 
+     322                 :          0 : (begin 
+     323            [ - ]:          0 :   (asserts! (is-eq contract-caller (var-get liquidity-provider)) err-only-liquidity-provider) 
+     324            [ - ]:          0 :   (asserts! (<= new-return-value max-return-div-accepted) err-return-div-exceeds-maximum)
+     325            [ - ]:          0 :   (asserts! (not (is-eq new-return-value (var-get return-div))) err-same-value)
+     326                 :          0 :   (var-set return-div new-return-value)
+     327                 :          0 :   (ok new-return-value)))
+     328                 :            : 
+     329                 :            : ;; Private functions
+     330                 :            : 
+     331                 :            : ;; Pox operative functions
+     332                 :            : 
+     333                 :            : (define-private (maybe-stack-aggregation-commit (current-cycle uint))
+     334                 :          0 : (let ((reward-cycle (+ u1 current-cycle)))
+     335                 :          0 :   (match (map-get? pox-addr-indices reward-cycle)
+     336                 :            :           ;; Total stacked already reached minimum.
+     337                 :            :           ;; Call stack-aggregate-increase.
+     338                 :            :           ;; It might fail because called in the same cycle twice.
+     339            [ - ]:          0 :     index (match (as-contract (contract-call? 'ST000000000000000000002AMW42H.pox-3 stack-aggregation-increase (var-get pool-pox-address) reward-cycle index))
+     340            [ - ]:          0 :             success (map-set last-aggregation reward-cycle block-height)
+     341            [ - ]:          0 :             error (begin (print {err-increase-ignored: error}) false))
+     342                 :            :           ;; Total stacked is still below minimum.
+     343                 :            :           ;; Just try to commit, it might fail because minimum not yet met
+     344            [ - ]:          0 :     (match (as-contract (contract-call? 'ST000000000000000000002AMW42H.pox-3 stack-aggregation-commit-indexed (var-get pool-pox-address) reward-cycle))
+     345            [ - ]:          0 :       index (begin
+     346                 :          0 :               (map-set pox-addr-indices reward-cycle index)
+     347                 :          0 :               (map-set last-aggregation reward-cycle block-height))
+     348            [ - ]:          0 :       error (begin 
+     349                 :          0 :               (print {err-commit-ignored: error}) false))))) ;; ignore errors
+     350                 :            : 
+     351                 :            : (define-private (delegate-stx-inner (amount-ustx uint) (delegate-to principal) (until-burn-ht (optional uint)))
+     352                 :          0 : (let ((result-revoke
+     353                 :            :         ;; Calls revoke and ignores result
+     354                 :          0 :         (contract-call? 'ST000000000000000000002AMW42H.pox-3 revoke-delegate-stx))
+     355                 :          0 :       (user-delegated-balance 
+     356                 :          0 :         (default-to u0 (get delegated-balance (map-get? user-data {address: tx-sender})))))
+     357                 :          0 :       (if 
+     358                 :          0 :           (is-ok result-revoke) 
+     359            [ - ]:          0 :           (if 
+     360                 :          0 :             (unwrap-panic result-revoke) 
+     361            [ - ]:          0 :             (begin 
+     362                 :          0 :               (asserts! 
+     363                 :          0 :                 (check-can-decrement-delegated-balance 
+     364                 :          0 :                   user-delegated-balance) 
+     365            [ - ]:          0 :               err-not-delegated-that-amount) 
+     366                 :          0 :               (decrement-sc-delegated-balance user-delegated-balance)) 
+     367            [ - ]:          0 :             (decrement-sc-delegated-balance u0)) 
+     368            [ - ]:          0 :           (decrement-sc-delegated-balance u0))
+     369                 :            :   ;; Calls delegate-stx, converts any error to uint
+     370                 :          0 :   (match (contract-call? 'ST000000000000000000002AMW42H.pox-3 delegate-stx amount-ustx delegate-to until-burn-ht none)
+     371            [ - ]:          0 :     success (begin 
+     372                 :          0 :               (increment-sc-delegated-balance amount-ustx)
+     373                 :          0 :               (map-set 
+     374                 :          0 :                 user-data 
+     375                 :          0 :                   {address: tx-sender} 
+     376                 :            :                   {
+     377                 :          0 :                     is-in-pool: (default-to false (get is-in-pool (map-get? user-data {address: tx-sender}))),                    
+     378                 :          0 :                     delegated-balance: amount-ustx, 
+     379                 :          0 :                     locked-balance: (default-to u0 (get locked-balance (map-get? user-data {address: tx-sender}))),
+     380                 :          0 :                     until-burn-ht: until-burn-ht})
+     381                 :          0 :               (print "sc delegated balance")
+     382                 :          0 :               (print (var-get sc-delegated-balance))
+     383                 :          0 :               (ok success))
+     384            [ - ]:          0 :     error (err (* u1000 (to-uint error))))))
+     385                 :            : 
+     386                 :            : (define-private (lock-delegated-stx (user principal))
+     387                 :          0 : (let ((start-burn-ht (+ burn-block-height u1))
+     388                 :          0 :       (pox-address (var-get pool-pox-address))
+     389                 :          0 :       (buffer-amount u0)
+     390                 :          0 :       (user-account (stx-account user))
+     391                 :          0 :       (allowed-amount (min (get-delegated-amount user) (+ (get locked user-account) (get unlocked user-account))))
+     392         [ -  - ]:          0 :       (amount-ustx (if (> allowed-amount buffer-amount) (- allowed-amount buffer-amount) allowed-amount)))
+     393            [ - ]:          0 :   (asserts! (var-get active) err-pox-address-deactivated)
+     394                 :          0 :   (match (contract-call? 'ST000000000000000000002AMW42H.pox-3 delegate-stack-stx
+     395                 :          0 :             user amount-ustx
+     396                 :          0 :             pox-address start-burn-ht u1)
+     397                 :            :     stacker-details 
+     398            [ - ]:          0 :       (begin 
+     399                 :          0 :         (map-set 
+     400                 :          0 :           user-data 
+     401                 :          0 :             {address: user} 
+     402                 :            :             {
+     403                 :          0 :               is-in-pool: 
+     404                 :          0 :                 (default-to false (get is-in-pool (map-get? user-data {address: user}))),
+     405                 :          0 :               delegated-balance: 
+     406                 :          0 :                 (default-to u0 (get delegated-balance (map-get? user-data {address: user}))),
+     407                 :          0 :               locked-balance: (get lock-amount stacker-details),
+     408                 :          0 :               until-burn-ht: 
+     409                 :          0 :                   (some (get unlock-burn-height stacker-details))})
+     410                 :          0 :         (increment-sc-locked-balance (get lock-amount stacker-details))
+     411                 :          0 :         (ok stacker-details))
+     412                 :            : 
+     413            [ - ]:          0 :       error (if (is-eq error 3) ;; check whether user is already stacked
+     414            [ - ]:          0 :               (delegate-stack-extend-increase user amount-ustx pox-address start-burn-ht)
+     415            [ - ]:          0 :               (err (* u1000 (to-uint error)))))))
+     416                 :            : 
+     417                 :            : (define-private (delegate-stack-extend-increase (user principal)
+     418                 :            :                   (amount-ustx uint)
+     419                 :            :                   (pox-address {hashbytes: (buff 32), version: (buff 1)})
+     420                 :            :                   (start-burn-ht uint))
+     421                 :          0 : (let ((status (stx-account user)))
+     422            [ - ]:          0 :   (asserts! (>= amount-ustx (get locked status)) err-decrease-forbidden)
+     423                 :          0 :   (match (contract-call? 'ST000000000000000000002AMW42H.pox-3 delegate-stack-extend
+     424                 :          0 :           user pox-address u1)
+     425            [ - ]:          0 :     success (begin 
+     426                 :          0 :             (print "success")
+     427                 :          0 :             (print success)
+     428                 :          0 :             (map-set user-data 
+     429                 :          0 :                     {address: user} 
+     430                 :            :                     {
+     431                 :          0 :                     is-in-pool: 
+     432                 :          0 :                       (default-to false (get is-in-pool (map-get? user-data {address: user}))),
+     433                 :          0 :                     delegated-balance: 
+     434                 :          0 :                       (default-to u0 (get delegated-balance (map-get? user-data {address: user}))), 
+     435                 :          0 :                     locked-balance: 
+     436                 :          0 :                       (default-to u0 (get locked-balance (map-get? user-data {address: user}))),
+     437                 :          0 :                     until-burn-ht: 
+     438                 :          0 :                       (some (+ (default-to u0 (default-to (some u0) (get until-burn-ht (map-get? user-data {address: user})))) REWARD_CYCLE_LENGTH))
+     439                 :            :                     })
+     440                 :          0 :             (if (> amount-ustx (get locked status))          
+     441            [ - ]:          0 :               (match (contract-call? 'ST000000000000000000002AMW42H.pox-3 delegate-stack-increase 
+     442                 :          0 :                 user 
+     443                 :          0 :                 pox-address 
+     444                 :          0 :                 (- 
+     445                 :          0 :                   amount-ustx 
+     446                 :          0 :                   (default-to u0 (get locked-balance (map-get? user-data {address: user})))))
+     447            [ - ]:          0 :                 success-increase (begin
+     448                 :          0 :                                   (print "success-increase")
+     449                 :          0 :                                   (print success-increase)
+     450                 :          0 :                                   (map-set user-data 
+     451                 :          0 :                                     {address: user} 
+     452                 :            :                                     {
+     453                 :          0 :                                     is-in-pool:
+     454                 :          0 :                                       (default-to false (get is-in-pool (map-get? user-data {address: user}))),
+     455                 :          0 :                                     delegated-balance: 
+     456                 :          0 :                                       (default-to u0 (get delegated-balance (map-get? user-data {address: user}))), 
+     457                 :          0 :                                     locked-balance: (get total-locked success-increase),
+     458                 :          0 :                                     until-burn-ht:
+     459                 :          0 :                                       (default-to none (get until-burn-ht (map-get? user-data {address: user})))
+     460                 :            :                                     })
+     461                 :          0 :                                   (increment-sc-locked-balance 
+     462                 :          0 :                                     (- amount-ustx 
+     463                 :          0 :                                       (default-to u0 (get locked-balance (map-get? user-data {address: user})))))
+     464                 :          0 :                                   (ok {lock-amount: (get total-locked success-increase),
+     465                 :          0 :                                       stacker: user,
+     466                 :          0 :                                       unlock-burn-height: (get unlock-burn-height success)}))
+     467            [ - ]:          0 :                 error-increase (begin (print "error-increase") (err (* u1000000000 (to-uint error-increase)))))
+     468            [ - ]:          0 :               (ok {
+     469                 :          0 :                     lock-amount: (get locked status),
+     470                 :          0 :                     stacker: user,
+     471                 :          0 :                     unlock-burn-height: (get unlock-burn-height success)})))
+     472            [ - ]:          0 :     error (err (* u1000000 (to-uint error))))))
+     473                 :            : 
+     474                 :            : ;; Rewards transferring functions
+     475                 :            : 
+     476                 :            : (define-private (transfer-rewards-all-stackers (stackers-list-before-cycle (list 300 principal)))
+     477                 :          0 : (let ((current-reward 
+     478                 :          0 :         (unwrap! 
+     479                 :          0 :           (preview-exchange-reward 
+     480                 :          0 :             (default-to u0 
+     481                 :          0 :               (get reward 
+     482                 :          0 :                 (map-get? burn-block-rewards { burn-height: (var-get burn-block-to-distribute-rewards)}))) 
+     483                 :          0 :             u5) err-cant-unwrap-exchange-preview))) 
+     484                 :          0 :       (var-set temp-current-reward current-reward)
+     485                 :          0 :       (ok (map transfer-reward-one-stacker stackers-list-before-cycle))))
+     486                 :            : 
+     487                 :            : (define-private (transfer-reward-one-stacker (stacker principal)) 
+     488                 :          0 : (let (
+     489                 :          0 :       (reward (var-get temp-current-reward))
+     490                 :          0 :       (stacker-weight 
+     491                 :          0 :         (default-to u0 
+     492                 :          0 :           (get weight-percentage 
+     493                 :          0 :             (map-get? stacker-weights-per-reward-cycle {stacker: stacker, reward-cycle: (var-get reward-cycle-to-distribute-rewards)}))))
+     494                 :          0 :       (stacker-reward (/ (* stacker-weight reward) ONE-6))) 
+     495                 :          0 :       (if (> stacker-weight u0) 
+     496                 :            : 
+     497            [ - ]:          0 :           (match (as-contract (stx-transfer? stacker-reward tx-sender stacker))
+     498                 :            :             success 
+     499            [ - ]:          0 :               (begin 
+     500                 :          0 :                 (if 
+     501                 :          0 :                   (not (check-can-decrement-reserved-balance stacker-reward))
+     502            [ - ]:          0 :                   (decrement-sc-owned-balance stacker-reward)
+     503            [ - ]:          0 :                   (decrement-sc-reserved-balance stacker-reward)) 
+     504                 :          0 :                 (ok true))
+     505            [ - ]:          0 :             error (err error)) 
+     506            [ - ]:          0 :           (ok false))))
+     507                 :            : 
+     508                 :            : 
+     509                 :            : (define-private (preview-exchange-reward (sats-amount uint) (slippeage uint)) 
+     510                 :          0 : (contract-call? .bridge-contract swap-preview .token-wbtc .token-wstx sats-amount slippeage))
+     511                 :            : 
+     512                 :            : ;; Weight calculation functions
+     513                 :            : 
+     514                 :            : ;; calculating one stacker's weight inside pool based on his balances
+     515                 :            : (define-private (weight-calculator (stacker principal) (stacker-locked uint) (total-locked uint) (liquidity-provider-locked uint)) 
+     516                 :          0 : (begin 
+     517            [ - ]:          0 :   (asserts! (> (+ total-locked liquidity-provider-locked) u0) err-no-locked-funds) 
+     518                 :          0 :   (ok (/ (* stacker-locked ONE-6) (+ total-locked liquidity-provider-locked)))))
+     519                 :            : 
+     520                 :            : (define-private (calculate-all-stackers-weights (stackers-list-before-cycle (list 300 principal)) (next-reward-cycle uint))
+     521                 :          0 : (begin 
+     522                 :          0 :   (map calculate-one-stacker-weight stackers-list-before-cycle)
+     523                 :          0 :   (map-set calculated-weights-reward-cycles {reward-cycle: next-reward-cycle} {calculated: true})
+     524                 :          0 :   (ok true)))
+     525                 :            : 
+     526                 :            : ;; each stacker will have a weight inside the pool which will be used when distributing rewards
+     527                 :            : (define-private (calculate-one-stacker-weight (stacker principal))
+     528                 :          0 : (let ((last-burn-block-before-reward-cycle 
+     529                 :          0 :         (- 
+     530                 :          0 :           (contract-call? 'ST000000000000000000002AMW42H.pox-3 reward-cycle-to-burn-height (var-get reward-cycle-to-calculate-weight)) 
+     531                 :          0 :           u1))
+     532                 :            :       ;; total locked by pool
+     533                 :          0 :       (total-locked-at-reward-cycle 
+     534                 :          0 :         (var-get sc-locked-balance))
+     535                 :            :       ;; total reserved by liquidity provider
+     536                 :          0 :       (liquidity-provider-reserved-at-reward-cycle 
+     537                 :          0 :         (var-get sc-reserved-balance))
+     538                 :            :       ;; total locked by a stacker
+     539                 :          0 :       (stacker-locked-at-reward-cycle 
+     540                 :          0 :         (default-to u0 (get locked-balance (map-get? user-data {address: stacker}))))
+     541                 :          0 :       (liquidity-provider-contribution (+ liquidity-provider-reserved-at-reward-cycle stacker-locked-at-reward-cycle))
+     542                 :            :       ;; the weight calculator result for the given stacker
+     543                 :          0 :       (weight-calculator-result 
+     544                 :          0 :         (if 
+     545                 :          0 :           (is-eq stacker (var-get liquidity-provider)) 
+     546            [ - ]:          0 :           (weight-calculator 
+     547                 :          0 :             stacker 
+     548                 :          0 :             liquidity-provider-contribution
+     549                 :          0 :             total-locked-at-reward-cycle 
+     550                 :          0 :             liquidity-provider-reserved-at-reward-cycle)
+     551            [ - ]:          0 :           (weight-calculator 
+     552                 :          0 :             stacker 
+     553                 :          0 :             stacker-locked-at-reward-cycle 
+     554                 :          0 :             total-locked-at-reward-cycle 
+     555                 :          0 :             liquidity-provider-reserved-at-reward-cycle))))
+     556                 :            :     ;; register the stacker's weight for a given reward cycle using a map
+     557                 :          0 :     (map-set stacker-weights-per-reward-cycle 
+     558                 :          0 :       {stacker: stacker, reward-cycle: (var-get reward-cycle-to-calculate-weight)} 
+     559                 :          0 :       {weight-percentage: 
+     560                 :          0 :         (if 
+     561                 :          0 :           (not (is-err weight-calculator-result)) 
+     562            [ - ]:          0 :           (unwrap-panic weight-calculator-result) 
+     563            [ - ]:          0 :           u0)
+     564                 :            :       })))
+     565                 :            : 
+     566                 :            : 
+     567                 :            : ;; check if pool pox address has won the rewards for a given burn height and store the reward if true
+     568                 :            : (define-private (check-won-block-rewards (burn-height uint)) 
+     569                 :          0 : (let ((reward-pox-addr-list (default-to (list ) (get addrs (get-burn-block-info? pox-addrs burn-height))))) 
+     570                 :          0 :   (if 
+     571                 :          0 :     (is-some 
+     572                 :          0 :       (index-of? reward-pox-addr-list (var-get pool-pox-address))) 
+     573            [ - ]:          0 :     (begin 
+     574                 :          0 :       (register-block-reward burn-height)
+     575                 :          0 :       true) 
+     576            [ - ]:          0 :     false)))
+     577                 :            : 
+     578                 :            : ;; store the reward for a given block using a map
+     579                 :            : (define-private (register-block-reward (burn-height uint)) 
+     580                 :          0 : (map-set burn-block-rewards {burn-height: burn-height} {reward: (default-to u0 (get payout (get-burn-block-info? pox-addrs burn-height)))}))
+     581                 :            : 
+     582                 :          0 : (define-private (remove-stacker-stackers-list (address principal)) (not (is-eq tx-sender address)))
+     583                 :            : 
+     584                 :            : (define-private (increment-sc-delegated-balance (amount-ustx uint)) 
+     585                 :          0 : (var-set sc-delegated-balance (+ (var-get sc-delegated-balance) amount-ustx)))
+     586                 :            : 
+     587                 :            : (define-private (increment-sc-locked-balance (amount-ustx uint)) 
+     588                 :          0 : (var-set sc-locked-balance (+ (var-get sc-locked-balance) amount-ustx)))
+     589                 :            : 
+     590                 :            : (define-private (decrement-sc-delegated-balance (amount-ustx uint)) 
+     591                 :          0 : (var-set sc-delegated-balance (- (var-get sc-delegated-balance) amount-ustx)))
+     592                 :            : 
+     593                 :            : (define-private (decrement-sc-locked-balance (amount-ustx uint)) 
+     594                 :          0 : (var-set sc-locked-balance (- (var-get sc-locked-balance) amount-ustx)))
+     595                 :            : 
+     596                 :            : (define-private (decrement-sc-reserved-balance (amount-ustx uint)) 
+     597                 :          0 : (var-set sc-reserved-balance (- (var-get sc-reserved-balance) amount-ustx)))
+     598                 :            : 
+     599                 :            : (define-private (decrement-sc-owned-balance (amount-ustx uint)) 
+     600                 :          0 : (var-set sc-owned-balance (- (var-get sc-owned-balance) amount-ustx)))
+     601                 :            : 
+     602                 :            : (define-private (check-can-decrement-delegated-balance (amount-ustx uint)) 
+     603                 :          0 : (if 
+     604                 :          0 :   (< 
+     605                 :          0 :     (var-get sc-delegated-balance) 
+     606                 :          0 :     amount-ustx) 
+     607            [ - ]:          0 :   false
+     608            [ - ]:          0 : true))
+     609                 :            : 
+     610                 :            : (define-private (check-can-decrement-locked-balance (amount-ustx uint)) 
+     611                 :          0 : (if 
+     612                 :          0 :   (< 
+     613                 :          0 :     (var-get sc-locked-balance) 
+     614                 :          0 :     amount-ustx) 
+     615            [ - ]:          0 :   false
+     616            [ - ]:          0 : true))
+     617                 :            : 
+     618                 :            : (define-private (check-can-decrement-reserved-balance (amount-ustx uint)) 
+     619                 :          0 : (if 
+     620                 :          0 :   (< 
+     621                 :          0 :     (var-get sc-reserved-balance) 
+     622                 :          0 :     amount-ustx) 
+     623            [ - ]:          0 :   false
+     624            [ - ]:          0 : true))
+     625                 :            : 
+     626                 :            : (define-private (check-can-decrement-total-balance (amount-ustx uint)) 
+     627                 :          0 : (if 
+     628                 :          0 :   (< 
+     629                 :          0 :     (var-get sc-total-balance) 
+     630                 :          0 :     amount-ustx) 
+     631            [ - ]:          0 :   false
+     632            [ - ]:          0 : true))
+     633                 :            : 
+     634                 :            : (define-private (check-can-decrement-owned-balance (amount-ustx uint)) 
+     635                 :          0 : (if 
+     636                 :          0 :   (< 
+     637                 :          0 :     (var-get sc-owned-balance) 
+     638                 :          0 :     amount-ustx) 
+     639            [ - ]:          0 :   false
+     640            [ - ]:          0 : true))
+     641                 :            : 
+     642                 :            : (define-private (min (amount-1 uint) (amount-2 uint))
+     643                 :          0 :   (if (< amount-1 amount-2)
+     644            [ - ]:          0 :     amount-1
+     645            [ - ]:          0 :     amount-2))
+     646                 :            : 
+     647                 :            : (define-private (get-next-reward-cycle) 
+     648                 :          0 : (+ (contract-call? 'ST000000000000000000002AMW42H.pox-3 burn-height-to-reward-cycle burn-block-height) u1))
+     649                 :            : 
+     650                 :            : ;; Read-only helper functions
+     651                 :            : 
+     652                 :            : (define-read-only (get-stx-account)
+     653                 :          0 : (stx-account tx-sender))
+     654                 :            : 
+     655                 :            : (define-read-only (get-pool-members) 
+     656                 :          0 : (var-get stackers-list))
+     657                 :            : 
+     658                 :            : (define-read-only (check-caller-allowed)
+     659            [ - ]:          0 :   (or (is-eq tx-sender contract-caller)
+     660            [ - ]:          0 :     (let ((caller-allowed
+     661                 :            :             ;; if not in the caller map, return false
+     662                 :          0 :             (unwrap! 
+     663                 :          0 :               (map-get? allowance-contract-callers
+     664                 :          0 :                 { sender: tx-sender, contract-caller: contract-caller})
+     665                 :          0 :             false))
+     666                 :          0 :           (expires-at
+     667                 :            :             ;; if until-burn-ht not set, then return true (because no expiry)
+     668                 :          0 :             (unwrap! (get until-burn-ht caller-allowed) true)))
+     669                 :            :       ;; is the caller allowance still valid
+     670                 :          0 :       (< burn-block-height expires-at))))
+     671                 :            : 
+     672                 :            : (define-read-only (is-in-pool) 
+     673                 :          0 : (default-to false (get is-in-pool (map-get? user-data {address: tx-sender}))))
+     674                 :            : 
+     675                 :            : (define-read-only (get-stacker-weight (stacker principal) (reward-cycle uint)) 
+     676                 :          0 : (get weight-percentage (map-get? stacker-weights-per-reward-cycle {stacker: stacker, reward-cycle: reward-cycle})))
+     677                 :            : 
+     678                 :            : (define-read-only (get-SC-total-balance) 
+     679                 :          0 : (var-get sc-total-balance))
+     680                 :            : 
+     681                 :            : (define-read-only (get-SC-owned-balance) 
+     682                 :          0 : (var-get sc-owned-balance))
+     683                 :            : 
+     684                 :            : (define-read-only (get-SC-locked-balance)
+     685                 :          0 : (var-get sc-locked-balance))
+     686                 :            : 
+     687                 :            : (define-read-only (get-SC-reserved-balance) 
+     688                 :          0 : (var-get sc-reserved-balance))
+     689                 :            : 
+     690                 :            : (define-read-only (get-user-data (user principal)) 
+     691                 :          0 : (map-get? user-data {address: user}))
+     692                 :            : 
+     693                 :            : (define-read-only (check-pool-SC-pox-2-allowance)
+     694                 :          0 : (is-some (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-allowance-contract-callers tx-sender pool-contract)))
+     695                 :            : 
+     696                 :            : (define-read-only (get-check-delegation (stacker principal))
+     697                 :          0 : (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-check-delegation stacker))
+     698                 :            : 
+     699                 :            : (define-read-only (get-pox-addr-indices (reward-cycle uint))
+     700                 :          0 : (map-get? pox-addr-indices reward-cycle))
+     701                 :            : 
+     702                 :            : (define-read-only (get-block-rewards (burn-height uint)) 
+     703                 :          0 : (ok (get-burn-block-info? pox-addrs burn-height)))
+     704                 :            : 
+     705                 :            : (define-read-only (can-lock-now (cycle uint))
+     706                 :          0 : (> burn-block-height (+ (contract-call? 'ST000000000000000000002AMW42H.pox-3 reward-cycle-to-burn-height cycle) half-cycle-length)))
+     707                 :            : 
+     708                 :            : (define-read-only (get-delegated-amount (user principal))
+     709                 :          0 : (default-to u0 (get amount-ustx (contract-call? 'ST000000000000000000002AMW42H.pox-3 get-delegation-info user))))
+     710                 :            : 
+     711                 :            : (define-read-only (get-liquidity-provider) 
+     712                 :          0 : (var-get liquidity-provider))
+     713                 :            : 
+     714                 :            : (define-read-only (get-amount-rewarded) 
+     715                 :          0 : (var-get amount-rewarded))
+     716                 :            : 
+     717                 :            : (define-read-only (get-blocks-rewarded) 
+     718                 :          0 : (var-get blocks-rewarded))
+     719                 :            : 
+     720                 :            : (define-read-only (get-stacked-this-cycle) 
+     721                 :          0 : (var-get sc-locked-balance))
+     722                 :            : 
+     723                 :            : (define-private (check-is-liquidity-provider (address principal)) 
+     724                 :          0 : (is-eq address (var-get liquidity-provider)))
+     725                 :            : 
+     726                 :            : (define-private (check-is-stacker (address principal)) 
+     727                 :          0 : (default-to false (get is-in-pool (map-get? user-data {address: address}))))
+     728                 :            : 
+     729                 :            : (define-read-only (get-address-status (address principal))
+     730                 :          0 : (if (check-is-liquidity-provider address)  
+     731            [ - ]:          0 :   (ok "is-provider")
+     732            [ - ]:          0 :   (if (check-is-stacker address)
+     733            [ - ]:          0 :     (ok "is-stacker")
+     734            [ - ]:          0 :     (ok "is-none"))))
+     735                 :            : 
+     736                 :            : (define-read-only (calculate-extra-reserved-funds) 
+     737                 :            : ;; subtract the potential return from the total reserved balance and get the extra reserved balance
+     738                 :          0 : (- 
+     739                 :          0 :   (var-get sc-reserved-balance) 
+     740                 :          0 :     (/ 
+     741                 :          0 :       (var-get sc-locked-balance) 
+     742                 :          0 :       (var-get return-div))))
+     743                 :            : 
+     744                 :            : (define-read-only (can-withdraw-extra-reserved-now) 
+     745                 :            : ;; liquidity provider can only withdraw extra reserved balance within the last 10 blocks of a reward cycle
+     746                 :          0 : (let ((mod-burn-height (mod burn-block-height REWARD_CYCLE_LENGTH))
+     747                 :          0 :       (start-value (- REWARD_CYCLE_LENGTH u10))
+     748                 :          0 :       (end-value (- REWARD_CYCLE_LENGTH u1))) 
+     749         [ -  - ]:          0 :   (and (>= mod-burn-height start-value) (<= mod-burn-height end-value))))
+     750                 :            : 
+     751                 :            : (define-read-only (get-return) 
+     752                 :          0 : (var-get return-div))
+     753                 :            : 
+     754                 :            : (define-read-only (get-minimum-deposit-liquidity-provider) 
+     755                 :          0 : (var-get minimum-deposit-amount-liquidity-provider))
+     756                 :            : 
+     757                 :            : (define-read-only (was-block-claimed (rewarded-burn-block uint))
+     758                 :          0 : (map-get? already-rewarded {burn-block-height: rewarded-burn-block}))
+
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/token-amm-swap-pool-v1-1.clar.func-sort-c.html b/sbtc-ops/smart-contract/coverage/contracts/token-amm-swap-pool-v1-1.clar.func-sort-c.html new file mode 100644 index 00000000..ac6145ad --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/token-amm-swap-pool-v1-1.clar.func-sort-c.html @@ -0,0 +1,273 @@ + + + + + + + LCOV - coverage.lcov - contracts/token-amm-swap-pool-v1-1.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - token-amm-swap-pool-v1-1.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:2213016.9 %
Date:2023-08-18 21:53:02Functions:104820.8 %
Branches:32611.5 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
add-approved-contract0
burn0
burn-fixed0
check-is-owner0
create-tuple-token-balance0
decimals-to-fixed0
get-balance0
get-balance-fixed0
get-contract-owner0
get-name0
get-overall-balance0
get-overall-balance-fixed0
get-overall-supply0
get-overall-supply-fixed0
get-symbol0
get-token-balance-owned-in-fixed0
get-token-uri0
get-total-supply-fixed0
get-transferrable0
set-approved-contract0
set-contract-owner0
set-decimals0
set-name0
set-symbol0
set-token-uri0
set-transferrable0
transfer0
transfer-fixed0
transfer-many0
transfer-many-fixed0
transfer-many-fixed-iter0
transfer-many-iter0
transfer-many-memo0
transfer-many-memo-fixed0
transfer-many-memo-fixed-iter0
transfer-many-memo-iter0
transfer-memo0
transfer-memo-fixed0
check-is-approved1
fixed-to-decimals1
get-balance-or-default1
get-decimals1
get-token-owned1
get-total-supply1
mint1
mint-fixed1
pow-decimals1
set-balance1
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/token-amm-swap-pool-v1-1.clar.func.html b/sbtc-ops/smart-contract/coverage/contracts/token-amm-swap-pool-v1-1.clar.func.html new file mode 100644 index 00000000..d10e0494 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/token-amm-swap-pool-v1-1.clar.func.html @@ -0,0 +1,273 @@ + + + + + + + LCOV - coverage.lcov - contracts/token-amm-swap-pool-v1-1.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - token-amm-swap-pool-v1-1.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:2213016.9 %
Date:2023-08-18 21:53:02Functions:104820.8 %
Branches:32611.5 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
add-approved-contract0
burn0
burn-fixed0
check-is-approved1
check-is-owner0
create-tuple-token-balance0
decimals-to-fixed0
fixed-to-decimals1
get-balance0
get-balance-fixed0
get-balance-or-default1
get-contract-owner0
get-decimals1
get-name0
get-overall-balance0
get-overall-balance-fixed0
get-overall-supply0
get-overall-supply-fixed0
get-symbol0
get-token-balance-owned-in-fixed0
get-token-owned1
get-token-uri0
get-total-supply1
get-total-supply-fixed0
get-transferrable0
mint1
mint-fixed1
pow-decimals1
set-approved-contract0
set-balance1
set-contract-owner0
set-decimals0
set-name0
set-symbol0
set-token-uri0
set-transferrable0
transfer0
transfer-fixed0
transfer-many0
transfer-many-fixed0
transfer-many-fixed-iter0
transfer-many-iter0
transfer-many-memo0
transfer-many-memo-fixed0
transfer-many-memo-fixed-iter0
transfer-many-memo-iter0
transfer-memo0
transfer-memo-fixed0
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/token-amm-swap-pool-v1-1.clar.gcov.html b/sbtc-ops/smart-contract/coverage/contracts/token-amm-swap-pool-v1-1.clar.gcov.html new file mode 100644 index 00000000..612a84de --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/token-amm-swap-pool-v1-1.clar.gcov.html @@ -0,0 +1,353 @@ + + + + + + + LCOV - coverage.lcov - contracts/token-amm-swap-pool-v1-1.clar + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - token-amm-swap-pool-v1-1.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:2213016.9 %
Date:2023-08-18 21:53:02Functions:104820.8 %
Branches:32611.5 %
+
+ + + + + + + + +

+
           Branch data     Line data    Source code
+
+       1                 :            : (impl-trait .trait-ownable.ownable-trait)
+       2                 :            : (impl-trait .trait-semi-fungible.semi-fungible-trait)
+       3                 :            : (define-constant ERR-NOT-AUTHORIZED (err u1000))
+       4                 :            : (define-constant ERR-TOO-MANY-POOLS (err u2004))
+       5                 :            : (define-constant ERR-INVALID-BALANCE (err u1001))
+       6                 :            : (define-constant ERR-TRANSFER-FAILED (err u3000))
+       7                 :            : (define-fungible-token amm-swap-pool-v1-1)
+       8                 :            : (define-map token-balances {token-id: uint, owner: principal} uint)
+       9                 :            : (define-map token-supplies uint uint)
+      10                 :            : (define-map token-owned principal (list 200 uint))
+      11                 :            : (define-data-var contract-owner principal tx-sender)
+      12                 :            : (define-map approved-contracts principal bool)
+      13                 :            : (define-data-var token-name (string-ascii 32) "amm-swap-pool-v1-1")
+      14                 :            : (define-data-var token-symbol (string-ascii 32) "amm-swap-pool-v1-1")
+      15                 :            : (define-data-var token-uri (optional (string-utf8 256)) (some u"https://cdn.alexlab.co/metadata/token-amm-swap-pool.json"))
+      16                 :            : (define-data-var token-decimals uint u8)
+      17                 :            : (define-data-var transferrable bool true)
+      18                 :            : (define-read-only (get-transferrable)
+      19                 :          0 :         (ok (var-get transferrable))
+      20                 :            : )
+      21                 :            : (define-public (set-transferrable (new-transferrable bool))
+      22                 :          0 :         (begin 
+      23                 :          0 :                 (try! (check-is-owner))
+      24                 :          0 :                 (ok (var-set transferrable new-transferrable))
+      25                 :            :         )
+      26                 :            : )
+      27                 :            : (define-read-only (get-contract-owner)
+      28                 :          0 :   (ok (var-get contract-owner))
+      29                 :            : )
+      30                 :            : (define-public (set-contract-owner (owner principal))
+      31                 :          0 :   (begin
+      32                 :          0 :     (try! (check-is-owner))
+      33                 :          0 :     (ok (var-set contract-owner owner))
+      34                 :            :   )
+      35                 :            : )
+      36                 :            : (define-private (check-is-approved)
+      37            [ - ]:          1 :   (ok (asserts! (default-to false (map-get? approved-contracts tx-sender)) ERR-NOT-AUTHORIZED))
+      38                 :            : )
+      39                 :            : (define-private (check-is-owner)
+      40            [ - ]:          0 :         (ok (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED))
+      41                 :            : )
+      42                 :            : (define-public (add-approved-contract (new-approved-contract principal))
+      43                 :          0 :   (begin
+      44                 :          0 :     (try! (check-is-owner))
+      45                 :          0 :     (map-set approved-contracts new-approved-contract true)
+      46                 :          0 :     (ok true)
+      47                 :            :   )
+      48                 :            : )
+      49                 :            : (define-public (set-approved-contract (owner principal) (approved bool))
+      50                 :          0 :         (begin
+      51                 :          0 :                 (try! (check-is-owner))
+      52                 :          0 :                 (ok (map-set approved-contracts owner approved))
+      53                 :            :         )
+      54                 :            : )
+      55                 :            : (define-read-only (get-token-owned (owner principal))
+      56                 :          2 :     (default-to (list) (map-get? token-owned owner))
+      57                 :            : )
+      58                 :            : (define-private (set-balance (token-id uint) (balance uint) (owner principal))
+      59                 :          1 :     (begin
+      60                 :          1 :                 (and 
+      61            [ + ]:          1 :                         (is-none (index-of (get-token-owned owner) token-id))
+      62            [ + ]:          1 :                         (map-set token-owned owner (unwrap! (as-max-len? (append (get-token-owned owner) token-id) u200) ERR-TOO-MANY-POOLS))
+      63                 :            :                 )       
+      64                 :          1 :             (map-set token-balances {token-id: token-id, owner: owner} balance)
+      65                 :          1 :         (ok true)
+      66                 :            :     )
+      67                 :            : )
+      68                 :            : (define-private (get-balance-or-default (token-id uint) (who principal))
+      69                 :          1 :         (default-to u0 (map-get? token-balances {token-id: token-id, owner: who}))
+      70                 :            : )
+      71                 :            : (define-read-only (get-balance (token-id uint) (who principal))
+      72                 :          0 :         (ok (get-balance-or-default token-id who))
+      73                 :            : )
+      74                 :            : (define-read-only (get-overall-balance (who principal))
+      75                 :          0 :         (ok (ft-get-balance amm-swap-pool-v1-1 who))
+      76                 :            : )
+      77                 :            : (define-read-only (get-total-supply (token-id uint))
+      78                 :          1 :         (ok (default-to u0 (map-get? token-supplies token-id)))
+      79                 :            : )
+      80                 :            : (define-read-only (get-overall-supply)
+      81                 :          0 :         (ok (ft-get-supply amm-swap-pool-v1-1))
+      82                 :            : )
+      83                 :            : (define-read-only (get-decimals (token-id uint))
+      84                 :          1 :         (ok (var-get token-decimals))
+      85                 :            : )
+      86                 :            : (define-public (set-decimals (new-decimals uint))
+      87                 :          0 :         (begin
+      88                 :          0 :                 (try! (check-is-owner))
+      89                 :          0 :                 (ok (var-set token-decimals new-decimals))
+      90                 :            :         )
+      91                 :            : )
+      92                 :            : (define-read-only (get-token-uri (token-id uint))
+      93                 :          0 :         (ok (var-get token-uri))
+      94                 :            : )
+      95                 :            : (define-public (set-token-uri (new-uri (optional (string-utf8 256))))
+      96                 :          0 :         (begin
+      97                 :          0 :                 (try! (check-is-owner))
+      98                 :          0 :                 (ok (var-set token-uri new-uri))
+      99                 :            :         )
+     100                 :            : )
+     101                 :            : (define-read-only (get-name (token-id uint))
+     102                 :          0 :         (ok (var-get token-name))
+     103                 :            : )
+     104                 :            : (define-public (set-name (new-name (string-ascii 32)))
+     105                 :          0 :         (begin
+     106                 :          0 :                 (try! (check-is-owner))
+     107                 :          0 :                 (ok (var-set token-name new-name))
+     108                 :            :         )
+     109                 :            : )
+     110                 :            : (define-read-only (get-symbol (token-id uint))
+     111                 :          0 :         (ok (var-get token-symbol))
+     112                 :            : )
+     113                 :            : (define-public (set-symbol (new-symbol (string-ascii 10)))
+     114                 :          0 :         (begin
+     115                 :          0 :                 (try! (check-is-owner))
+     116                 :          0 :                 (ok (var-set token-symbol new-symbol))
+     117                 :            :         )
+     118                 :            : )
+     119                 :            : (define-public (transfer (token-id uint) (amount uint) (sender principal) (recipient principal))
+     120                 :          0 :         (let
+     121                 :            :                 (
+     122                 :          0 :                         (sender-balance (get-balance-or-default token-id sender))
+     123                 :            :                 )
+     124            [ - ]:          0 :                 (asserts! (var-get transferrable) ERR-TRANSFER-FAILED)
+     125            [ - ]:          0 :                 (asserts! (is-eq tx-sender sender) ERR-NOT-AUTHORIZED)
+     126            [ - ]:          0 :                 (asserts! (<= amount sender-balance) ERR-INVALID-BALANCE)
+     127                 :          0 :                 (try! (ft-transfer? amm-swap-pool-v1-1 amount sender recipient))
+     128                 :          0 :                 (try! (set-balance token-id (- sender-balance amount) sender))
+     129                 :          0 :                 (try! (set-balance token-id (+ (get-balance-or-default token-id recipient) amount) recipient))
+     130                 :          0 :                 (print {type: "sft_transfer", token-id: token-id, amount: amount, sender: sender, recipient: recipient})
+     131                 :          0 :                 (ok true)
+     132                 :            :         )
+     133                 :            : )
+     134                 :            : (define-public (transfer-memo (token-id uint) (amount uint) (sender principal) (recipient principal) (memo (buff 34)))
+     135                 :          0 :         (let
+     136                 :            :                 (
+     137                 :          0 :                         (sender-balance (get-balance-or-default token-id sender))
+     138                 :            :                 )
+     139            [ - ]:          0 :                 (asserts! (var-get transferrable) ERR-TRANSFER-FAILED)
+     140            [ - ]:          0 :                 (asserts! (is-eq tx-sender sender) ERR-NOT-AUTHORIZED)
+     141            [ - ]:          0 :                 (asserts! (<= amount sender-balance) ERR-INVALID-BALANCE)
+     142                 :          0 :                 (try! (ft-transfer? amm-swap-pool-v1-1 amount sender recipient))
+     143                 :          0 :                 (try! (set-balance token-id (- sender-balance amount) sender))
+     144                 :          0 :                 (try! (set-balance token-id (+ (get-balance-or-default token-id recipient) amount) recipient))
+     145                 :          0 :                 (print {type: "sft_transfer", token-id: token-id, amount: amount, sender: sender, recipient: recipient, memo: memo})
+     146                 :          0 :                 (ok true)
+     147                 :            :         )
+     148                 :            : )
+     149                 :            : (define-public (mint (token-id uint) (amount uint) (recipient principal))
+     150                 :          1 :         (begin
+     151    [ - ][ +  - ]:          1 :                 (asserts! (or (is-ok (check-is-approved)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED)
+     152                 :          1 :                 (try! (ft-mint? amm-swap-pool-v1-1 amount recipient))
+     153                 :          1 :                 (try! (set-balance token-id (+ (get-balance-or-default token-id recipient) amount) recipient))
+     154                 :          1 :                 (map-set token-supplies token-id (+ (unwrap-panic (get-total-supply token-id)) amount))
+     155                 :          1 :                 (print {type: "sft_mint", token-id: token-id, amount: amount, recipient: recipient})
+     156                 :          1 :                 (ok true)
+     157                 :            :         )
+     158                 :            : )
+     159                 :            : (define-public (burn (token-id uint) (amount uint) (sender principal))
+     160                 :          0 :         (begin
+     161    [ - ][ -  - ]:          0 :                 (asserts! (or (is-ok (check-is-approved)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED)
+     162                 :          0 :                 (try! (ft-burn? amm-swap-pool-v1-1 amount sender))
+     163                 :          0 :                 (try! (set-balance token-id (- (get-balance-or-default token-id sender) amount) sender))
+     164                 :          0 :                 (map-set token-supplies token-id (- (unwrap-panic (get-total-supply token-id)) amount))
+     165                 :          0 :                 (print {type: "sft_burn", token-id: token-id, amount: amount, sender: sender})
+     166                 :          0 :                 (ok true)
+     167                 :            :         )
+     168                 :            : )
+     169                 :            : (define-constant ONE_8 u100000000)
+     170                 :            : (define-private (pow-decimals)
+     171                 :          1 :         (pow u10 (unwrap-panic (get-decimals u0)))
+     172                 :            : )
+     173                 :            : (define-read-only (fixed-to-decimals (amount uint))
+     174                 :          1 :         (/ (* amount (pow-decimals)) ONE_8)
+     175                 :            : )
+     176                 :            : (define-private (decimals-to-fixed (amount uint))
+     177                 :          0 :         (/ (* amount ONE_8) (pow-decimals))
+     178                 :            : )
+     179                 :            : (define-read-only (get-total-supply-fixed (token-id uint))
+     180                 :          0 :         (ok (decimals-to-fixed (default-to u0 (map-get? token-supplies token-id))))
+     181                 :            : )
+     182                 :            : (define-read-only (get-balance-fixed (token-id uint) (who principal))
+     183                 :          0 :         (ok (decimals-to-fixed (get-balance-or-default token-id who)))
+     184                 :            : )
+     185                 :            : (define-read-only (get-overall-supply-fixed)
+     186                 :          0 :         (ok (decimals-to-fixed (ft-get-supply amm-swap-pool-v1-1)))
+     187                 :            : )
+     188                 :            : (define-read-only (get-overall-balance-fixed (who principal))
+     189                 :          0 :         (ok (decimals-to-fixed (ft-get-balance amm-swap-pool-v1-1 who)))
+     190                 :            : )
+     191                 :            : (define-public (transfer-fixed (token-id uint) (amount uint) (sender principal) (recipient principal))
+     192                 :          0 :         (transfer token-id (fixed-to-decimals amount) sender recipient)
+     193                 :            : )
+     194                 :            : (define-public (transfer-memo-fixed (token-id uint) (amount uint) (sender principal) (recipient principal) (memo (buff 34)))
+     195                 :          0 :         (transfer-memo token-id (fixed-to-decimals amount) sender recipient memo)
+     196                 :            : )
+     197                 :            : (define-public (mint-fixed (token-id uint) (amount uint) (recipient principal))
+     198                 :          1 :         (mint token-id (fixed-to-decimals amount) recipient)
+     199                 :            : )
+     200                 :            : (define-public (burn-fixed (token-id uint) (amount uint) (sender principal))
+     201                 :          0 :         (burn token-id (fixed-to-decimals amount) sender)
+     202                 :            : )
+     203                 :            : (define-private (transfer-many-iter (item {token-id: uint, amount: uint, sender: principal, recipient: principal}) (previous-response (response bool uint)))
+     204         [ -  - ]:          0 :         (match previous-response prev-ok (transfer (get token-id item) (get amount item) (get sender item) (get recipient item)) prev-err previous-response)
+     205                 :            : )
+     206                 :            : (define-public (transfer-many (transfers (list 200 {token-id: uint, amount: uint, sender: principal, recipient: principal})))
+     207                 :          0 :         (fold transfer-many-iter transfers (ok true))
+     208                 :            : )
+     209                 :            : (define-private (transfer-many-memo-iter (item {token-id: uint, amount: uint, sender: principal, recipient: principal, memo: (buff 34)}) (previous-response (response bool uint)))
+     210         [ -  - ]:          0 :         (match previous-response prev-ok (transfer-memo (get token-id item) (get amount item) (get sender item) (get recipient item) (get memo item)) prev-err previous-response)
+     211                 :            : )
+     212                 :            : (define-public (transfer-many-memo (transfers (list 200 {token-id: uint, amount: uint, sender: principal, recipient: principal, memo: (buff 34)})))
+     213                 :          0 :         (fold transfer-many-memo-iter transfers (ok true))
+     214                 :            : )
+     215                 :            : (define-private (transfer-many-fixed-iter (item {token-id: uint, amount: uint, sender: principal, recipient: principal}) (previous-response (response bool uint)))
+     216         [ -  - ]:          0 :         (match previous-response prev-ok (transfer-fixed (get token-id item) (get amount item) (get sender item) (get recipient item)) prev-err previous-response)
+     217                 :            : )
+     218                 :            : (define-public (transfer-many-fixed (transfers (list 200 {token-id: uint, amount: uint, sender: principal, recipient: principal})))
+     219                 :          0 :         (fold transfer-many-fixed-iter transfers (ok true))
+     220                 :            : )
+     221                 :            : (define-private (transfer-many-memo-fixed-iter (item {token-id: uint, amount: uint, sender: principal, recipient: principal, memo: (buff 34)}) (previous-response (response bool uint)))
+     222         [ -  - ]:          0 :         (match previous-response prev-ok (transfer-memo-fixed (get token-id item) (get amount item) (get sender item) (get recipient item) (get memo item)) prev-err previous-response)
+     223                 :            : )
+     224                 :            : (define-public (transfer-many-memo-fixed (transfers (list 200 {token-id: uint, amount: uint, sender: principal, recipient: principal, memo: (buff 34)})))
+     225                 :          0 :         (fold transfer-many-memo-fixed-iter transfers (ok true))
+     226                 :            : )
+     227                 :            : (define-private (create-tuple-token-balance (token-id uint) (balance uint))
+     228                 :          0 :         { token-id: token-id, balance: (decimals-to-fixed balance) }
+     229                 :            : )
+     230                 :            : (define-read-only (get-token-balance-owned-in-fixed (owner principal))
+     231                 :          0 :         (begin 
+     232                 :          0 :                 (match (map-get? token-owned owner)
+     233                 :            :                         token-ids
+     234            [ - ]:          0 :                         (map 
+     235                 :          0 :                                 create-tuple-token-balance 
+     236                 :          0 :                                 token-ids 
+     237                 :          0 :                                 (map 
+     238                 :          0 :                                         get-balance-or-default
+     239                 :          0 :                                         token-ids
+     240                 :          0 :                                         (list 
+     241                 :          0 :                                                 owner   owner   owner   owner   owner   owner   owner   owner   owner   owner
+     242                 :          0 :                                                 owner   owner   owner   owner   owner   owner   owner   owner   owner   owner
+     243                 :          0 :                                                 owner   owner   owner   owner   owner   owner   owner   owner   owner   owner
+     244                 :          0 :                                                 owner   owner   owner   owner   owner   owner   owner   owner   owner   owner
+     245                 :          0 :                                                 owner   owner   owner   owner   owner   owner   owner   owner   owner   owner
+     246                 :          0 :                                                 owner   owner   owner   owner   owner   owner   owner   owner   owner   owner
+     247                 :          0 :                                                 owner   owner   owner   owner   owner   owner   owner   owner   owner   owner
+     248                 :          0 :                                                 owner   owner   owner   owner   owner   owner   owner   owner   owner   owner
+     249                 :          0 :                                                 owner   owner   owner   owner   owner   owner   owner   owner   owner   owner
+     250                 :          0 :                                                 owner   owner   owner   owner   owner   owner   owner   owner   owner   owner
+     251                 :          0 :                                                 owner   owner   owner   owner   owner   owner   owner   owner   owner   owner
+     252                 :          0 :                                                 owner   owner   owner   owner   owner   owner   owner   owner   owner   owner
+     253                 :          0 :                                                 owner   owner   owner   owner   owner   owner   owner   owner   owner   owner
+     254                 :          0 :                                                 owner   owner   owner   owner   owner   owner   owner   owner   owner   owner
+     255                 :          0 :                                                 owner   owner   owner   owner   owner   owner   owner   owner   owner   owner
+     256                 :          0 :                                                 owner   owner   owner   owner   owner   owner   owner   owner   owner   owner
+     257                 :          0 :                                                 owner   owner   owner   owner   owner   owner   owner   owner   owner   owner
+     258                 :          0 :                                                 owner   owner   owner   owner   owner   owner   owner   owner   owner   owner
+     259                 :          0 :                                                 owner   owner   owner   owner   owner   owner   owner   owner   owner   owner
+     260                 :          0 :                                                 owner   owner   owner   owner   owner   owner   owner   owner   owner   owner
+     261                 :            :                                         )
+     262                 :            :                                 )
+     263                 :            :                         )
+     264            [ - ]:          0 :                         (list)
+     265                 :            :                 )
+     266                 :            :         )
+     267                 :            : )
+     268                 :         41 : (map-set approved-contracts .amm-swap-pool-v1-1 true)
+
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/token-wbtc.clar.func-sort-c.html b/sbtc-ops/smart-contract/coverage/contracts/token-wbtc.clar.func-sort-c.html new file mode 100644 index 00000000..5a123b69 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/token-wbtc.clar.func-sort-c.html @@ -0,0 +1,189 @@ + + + + + + + LCOV - coverage.lcov - contracts/token-wbtc.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - token-wbtc.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:124228.6 %
Date:2023-08-18 21:53:02Functions:72725.9 %
Branches:040.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
burn0
burn-fixed0
check-err0
decimals-to-fixed0
get-balance0
get-balance-fixed0
get-contract-owner0
get-name0
get-symbol0
get-token-uri0
get-total-supply0
get-total-supply-fixed0
mint0
mint-fixed0
send-many0
set-decimals0
set-name0
set-symbol0
set-token-uri0
transfer-from-tuple0
fixed-to-decimals18
transfer-fixed18
get-decimals19
pow-decimals19
transfer19
check-is-owner41
set-contract-owner41
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/token-wbtc.clar.func.html b/sbtc-ops/smart-contract/coverage/contracts/token-wbtc.clar.func.html new file mode 100644 index 00000000..3441aa02 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/token-wbtc.clar.func.html @@ -0,0 +1,189 @@ + + + + + + + LCOV - coverage.lcov - contracts/token-wbtc.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - token-wbtc.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:124228.6 %
Date:2023-08-18 21:53:02Functions:72725.9 %
Branches:040.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
burn0
burn-fixed0
check-err0
check-is-owner41
decimals-to-fixed0
fixed-to-decimals18
get-balance0
get-balance-fixed0
get-contract-owner0
get-decimals19
get-name0
get-symbol0
get-token-uri0
get-total-supply0
get-total-supply-fixed0
mint0
mint-fixed0
pow-decimals19
send-many0
set-contract-owner41
set-decimals0
set-name0
set-symbol0
set-token-uri0
transfer19
transfer-fixed18
transfer-from-tuple0
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/token-wbtc.clar.gcov.html b/sbtc-ops/smart-contract/coverage/contracts/token-wbtc.clar.gcov.html new file mode 100644 index 00000000..bdd1b204 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/token-wbtc.clar.gcov.html @@ -0,0 +1,291 @@ + + + + + + + LCOV - coverage.lcov - contracts/token-wbtc.clar + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - token-wbtc.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:124228.6 %
Date:2023-08-18 21:53:02Functions:72725.9 %
Branches:040.0 %
+
+ + + + + + + + +

+
           Branch data     Line data    Source code
+
+       1                 :            : (impl-trait .trait-ownable.ownable-trait)
+       2                 :            : (impl-trait .trait-sip-010.sip-010-trait)
+       3                 :            : 
+       4                 :            : (define-fungible-token wbtc)
+       5                 :            : 
+       6                 :            : (define-data-var token-name (string-ascii 32) "Wrapped Bitcoin")
+       7                 :            : (define-data-var token-symbol (string-ascii 10) "XBTC")
+       8                 :            : (define-data-var token-uri (optional (string-utf8 256)) (some u"https://cdn.alexlab.co/metadata/token-wbtc.json"))
+       9                 :            : 
+      10                 :            : (define-data-var token-decimals uint u8)
+      11                 :            : 
+      12                 :            : (define-data-var contract-owner principal tx-sender)
+      13                 :            : 
+      14                 :            : ;; errors
+      15                 :            : (define-constant ERR-NOT-AUTHORIZED (err u1000))
+      16                 :            : (define-constant ERR-MINT-FAILED (err u6002))
+      17                 :            : (define-constant ERR-BURN-FAILED (err u6003))
+      18                 :            : (define-constant ERR-TRANSFER-FAILED (err u3000))
+      19                 :            : 
+      20                 :            : (define-read-only (get-contract-owner)
+      21                 :          0 :   (ok (var-get contract-owner))
+      22                 :            : )
+      23                 :            : 
+      24                 :            : (define-public (set-contract-owner (owner principal))
+      25                 :         41 :   (begin
+      26                 :         41 :     (try! (check-is-owner))
+      27                 :         41 :     (ok (var-set contract-owner owner))
+      28                 :            :   )
+      29                 :            : )
+      30                 :            : 
+      31                 :            : (define-private (check-is-owner)
+      32            [ - ]:         41 :   (ok (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED))
+      33                 :            : )
+      34                 :            : 
+      35                 :            : (define-public (set-name (new-name (string-ascii 32)))
+      36                 :          0 :         (begin
+      37                 :          0 :                 (try! (check-is-owner))
+      38                 :          0 :                 (ok (var-set token-name new-name))
+      39                 :            :         )
+      40                 :            : )
+      41                 :            : 
+      42                 :            : (define-public (set-symbol (new-symbol (string-ascii 10)))
+      43                 :          0 :         (begin
+      44                 :          0 :                 (try! (check-is-owner))
+      45                 :          0 :                 (ok (var-set token-symbol new-symbol))
+      46                 :            :         )
+      47                 :            : )
+      48                 :            : 
+      49                 :            : (define-public (set-decimals (new-decimals uint))
+      50                 :          0 :         (begin
+      51                 :          0 :                 (try! (check-is-owner))
+      52                 :          0 :                 (ok (var-set token-decimals new-decimals))
+      53                 :            :         )
+      54                 :            : )
+      55                 :            : 
+      56                 :            : (define-public (set-token-uri (new-uri (optional (string-utf8 256))))
+      57                 :          0 :         (begin
+      58                 :          0 :                 (try! (check-is-owner))
+      59                 :          0 :                 (ok (var-set token-uri new-uri))
+      60                 :            :         )
+      61                 :            : )
+      62                 :            : 
+      63                 :            : ;; ---------------------------------------------------------
+      64                 :            : ;; SIP-10 Functions
+      65                 :            : ;; ---------------------------------------------------------
+      66                 :            : 
+      67                 :            : ;; @desc get-total-supply
+      68                 :            : ;; @returns (response uint)
+      69                 :            : (define-read-only (get-total-supply)
+      70                 :          0 :   (ok u0)
+      71                 :            : )
+      72                 :            : 
+      73                 :            : ;; @desc get-name
+      74                 :            : ;; @returns (response string-utf8)
+      75                 :            : (define-read-only (get-name)
+      76                 :          0 :   (ok (var-get token-name))
+      77                 :            : )
+      78                 :            : 
+      79                 :            : ;; @desc get-symbol
+      80                 :            : ;; @returns (response string-utf8)
+      81                 :            : (define-read-only (get-symbol)
+      82                 :          0 :   (ok (var-get token-symbol))
+      83                 :            : )
+      84                 :            : 
+      85                 :            : ;; @desc get-decimals
+      86                 :            : ;; @returns (response uint)
+      87                 :            : (define-read-only (get-decimals)
+      88                 :         38 :   (ok (var-get token-decimals))
+      89                 :            : )
+      90                 :            : 
+      91                 :            : ;; @desc get-balance
+      92                 :            : ;; @params account
+      93                 :            : ;; @returns (response uint)
+      94                 :            : (define-read-only (get-balance (account principal))
+      95                 :          0 :   (ok (/ (* (unwrap-panic (contract-call? .Wrapped-Bitcoin get-balance account)) (pow-decimals)) (pow u10 u8)))
+      96                 :            : )
+      97                 :            : 
+      98                 :            : ;; @desc get-token-uri
+      99                 :            : ;; @returns (response some string-utf-8)
+     100                 :            : (define-read-only (get-token-uri)
+     101                 :          0 :   (ok (var-get token-uri))
+     102                 :            : )
+     103                 :            : 
+     104                 :            : ;; @desc transfer
+     105                 :            : ;; @restricted sender; tx-sender should be sender
+     106                 :            : ;; @params amount
+     107                 :            : ;; @params sender
+     108                 :            : ;; @params recipient
+     109                 :            : ;; @params memo; expiry
+     110                 :            : ;; @returns (response bool uint)/ error
+     111                 :            : (define-public (transfer (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34))))
+     112                 :         20 :   (begin
+     113            [ - ]:         20 :     (asserts! (is-eq sender tx-sender) ERR-NOT-AUTHORIZED)
+     114                 :         20 :     (contract-call? .Wrapped-Bitcoin transfer (/ (* amount (pow u10 u8)) (pow-decimals)) sender recipient memo)
+     115                 :            :   )
+     116                 :            : )
+     117                 :            : 
+     118                 :            : (define-constant ONE_8 (pow u10 u8))
+     119                 :            : 
+     120                 :            : ;; @desc pow-decimals
+     121                 :            : ;; @returns uint
+     122                 :            : (define-private (pow-decimals)
+     123                 :         38 :   (pow u10 (unwrap-panic (get-decimals)))
+     124                 :            : )
+     125                 :            : 
+     126                 :            : ;; @desc fixed-to-decimals
+     127                 :            : ;; @params amount
+     128                 :            : ;; @returns uint
+     129                 :            : (define-read-only (fixed-to-decimals (amount uint))
+     130                 :         18 :   (/ (* amount (pow-decimals)) ONE_8)
+     131                 :            : )
+     132                 :            : 
+     133                 :            : ;; @desc decimals-to-fixed 
+     134                 :            : ;; @params amount
+     135                 :            : ;; @returns uint
+     136                 :            : (define-private (decimals-to-fixed (amount uint))
+     137                 :          0 :   (/ (* amount ONE_8) (pow-decimals))
+     138                 :            : )
+     139                 :            : 
+     140                 :            : ;; @desc get-total-supply-fixed
+     141                 :            : ;; @params token-id
+     142                 :            : ;; @returns (response uint)
+     143                 :            : (define-read-only (get-total-supply-fixed)
+     144                 :          0 :   (ok (decimals-to-fixed (unwrap-panic (get-total-supply))))
+     145                 :            : )
+     146                 :            : 
+     147                 :            : ;; @desc get-balance-fixed
+     148                 :            : ;; @params token-id
+     149                 :            : ;; @params who
+     150                 :            : ;; @returns (response uint)
+     151                 :            : (define-read-only (get-balance-fixed (account principal))
+     152                 :          0 :   (ok (decimals-to-fixed (unwrap-panic (get-balance account))))
+     153                 :            : )
+     154                 :            : 
+     155                 :            : ;; @desc transfer-fixed
+     156                 :            : ;; @params token-id
+     157                 :            : ;; @params amount
+     158                 :            : ;; @params sender
+     159                 :            : ;; @params recipient
+     160                 :            : ;; @returns (response bool)
+     161                 :            : (define-public (transfer-fixed (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34))))
+     162                 :         18 :   (transfer (fixed-to-decimals amount) sender recipient memo)
+     163                 :            : )
+     164                 :            : 
+     165                 :            : (define-public (mint (amount uint) (recipient principal))
+     166                 :          0 :   ERR-MINT-FAILED
+     167                 :            : )
+     168                 :            : 
+     169                 :            : (define-public (burn (amount uint) (sender principal))
+     170                 :          0 :   ERR-BURN-FAILED
+     171                 :            : )
+     172                 :            : 
+     173                 :            : (define-public (mint-fixed (amount uint) (recipient principal))
+     174                 :          0 :   (mint (fixed-to-decimals amount) recipient)
+     175                 :            : )
+     176                 :            : 
+     177                 :            : ;; @desc burn-fixed
+     178                 :            : ;; @params token-id
+     179                 :            : ;; @params amount
+     180                 :            : ;; @params sender
+     181                 :            : ;; @returns (response bool)
+     182                 :            : (define-public (burn-fixed (amount uint) (sender principal))
+     183                 :          0 :   (burn (fixed-to-decimals amount) sender)
+     184                 :            : )
+     185                 :            : 
+     186                 :            : ;; @desc check-err
+     187                 :            : ;; @params result 
+     188                 :            : ;; @params prior
+     189                 :            : ;; @returns (response bool uint)
+     190                 :            : (define-private (check-err (result (response bool uint)) (prior (response bool uint)))
+     191                 :          0 :     (match prior 
+     192            [ - ]:          0 :         ok-value result
+     193            [ - ]:          0 :         err-value (err err-value)
+     194                 :            :     )
+     195                 :            : )
+     196                 :            : 
+     197                 :            : (define-private (transfer-from-tuple (recipient { to: principal, amount: uint }))
+     198                 :          0 :   (ok (unwrap! (transfer-fixed (get amount recipient) tx-sender (get to recipient) none) ERR-TRANSFER-FAILED))
+     199                 :            : )
+     200                 :            : 
+     201                 :            : (define-public (send-many (recipients (list 200 { to: principal, amount: uint})))
+     202                 :          0 :   (fold check-err (map transfer-from-tuple recipients) (ok true))
+     203                 :            : )
+     204                 :            : 
+     205                 :            : ;; contract initialisation
+     206                 :         41 : (set-contract-owner tx-sender)
+
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/token-wstx.clar.func-sort-c.html b/sbtc-ops/smart-contract/coverage/contracts/token-wstx.clar.func-sort-c.html new file mode 100644 index 00000000..86ab0479 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/token-wstx.clar.func-sort-c.html @@ -0,0 +1,173 @@ + + + + + + + LCOV - coverage.lcov - contracts/token-wstx.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - token-wstx.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:93327.3 %
Date:2023-08-18 21:53:02Functions:52321.7 %
Branches:1714.3 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
burn0
burn-fixed0
check-err0
decimals-to-fixed0
get-balance0
get-balance-fixed0
get-contract-owner0
get-name0
get-symbol0
get-token-uri0
get-total-supply0
get-total-supply-fixed0
mint0
mint-fixed0
send-many0
set-contract-owner0
set-token-uri0
transfer-from-tuple0
fixed-to-decimals18
get-decimals18
pow-decimals18
transfer18
transfer-fixed18
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/token-wstx.clar.func.html b/sbtc-ops/smart-contract/coverage/contracts/token-wstx.clar.func.html new file mode 100644 index 00000000..1b40f0eb --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/token-wstx.clar.func.html @@ -0,0 +1,173 @@ + + + + + + + LCOV - coverage.lcov - contracts/token-wstx.clar - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - token-wstx.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:93327.3 %
Date:2023-08-18 21:53:02Functions:52321.7 %
Branches:1714.3 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
burn0
burn-fixed0
check-err0
decimals-to-fixed0
fixed-to-decimals18
get-balance0
get-balance-fixed0
get-contract-owner0
get-decimals18
get-name0
get-symbol0
get-token-uri0
get-total-supply0
get-total-supply-fixed0
mint0
mint-fixed0
pow-decimals18
send-many0
set-contract-owner0
set-token-uri0
transfer18
transfer-fixed18
transfer-from-tuple0
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/contracts/token-wstx.clar.gcov.html b/sbtc-ops/smart-contract/coverage/contracts/token-wstx.clar.gcov.html new file mode 100644 index 00000000..5384f3a2 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/contracts/token-wstx.clar.gcov.html @@ -0,0 +1,264 @@ + + + + + + + LCOV - coverage.lcov - contracts/token-wstx.clar + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - contracts - token-wstx.clar (source / functions)HitTotalCoverage
Test:coverage.lcovLines:93327.3 %
Date:2023-08-18 21:53:02Functions:52321.7 %
Branches:1714.3 %
+
+ + + + + + + + +

+
           Branch data     Line data    Source code
+
+       1                 :            : (impl-trait .trait-ownable.ownable-trait)
+       2                 :            : (impl-trait .trait-sip-010.sip-010-trait)
+       3                 :            : 
+       4                 :            : (define-fungible-token wstx)
+       5                 :            : 
+       6                 :            : (define-data-var token-uri (string-utf8 256) u"")
+       7                 :            : (define-data-var contract-owner principal tx-sender)
+       8                 :            : 
+       9                 :            : ;; errors
+      10                 :            : (define-constant ERR-NOT-AUTHORIZED (err u1000))
+      11                 :            : (define-constant ERR-MINT-FAILED (err u6002))
+      12                 :            : (define-constant ERR-BURN-FAILED (err u6003))
+      13                 :            : (define-constant ERR-TRANSFER-FAILED (err u3000))
+      14                 :            : 
+      15                 :            : (define-read-only (get-contract-owner)
+      16                 :          0 :   (ok (var-get contract-owner))
+      17                 :            : )
+      18                 :            : 
+      19                 :            : (define-public (set-contract-owner (owner principal))
+      20                 :          0 :   (begin
+      21            [ - ]:          0 :     (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED)
+      22                 :          0 :     (ok (var-set contract-owner owner))
+      23                 :            :   )
+      24                 :            : )
+      25                 :            : 
+      26                 :            : ;; ---------------------------------------------------------
+      27                 :            : ;; SIP-10 Functions
+      28                 :            : ;; ---------------------------------------------------------
+      29                 :            : 
+      30                 :            : ;; @desc get-total-supply
+      31                 :            : ;; @returns (response uint)
+      32                 :            : (define-read-only (get-total-supply)
+      33                 :          0 :   (ok u0)
+      34                 :            : )
+      35                 :            : 
+      36                 :            : ;; @desc get-name
+      37                 :            : ;; @returns (response string-utf8)
+      38                 :            : (define-read-only (get-name)
+      39                 :          0 :   (ok "wstx")
+      40                 :            : )
+      41                 :            : 
+      42                 :            : ;; @desc get-symbol
+      43                 :            : ;; @returns (response string-utf8)
+      44                 :            : (define-read-only (get-symbol)
+      45                 :          0 :   (ok "wstx")
+      46                 :            : )
+      47                 :            : 
+      48                 :            : ;; @desc get-decimals
+      49                 :            : ;; @returns (response uint)
+      50                 :            : (define-read-only (get-decimals)
+      51                 :         18 :   (ok u8)
+      52                 :            : )
+      53                 :            : 
+      54                 :            : ;; @desc get-balance
+      55                 :            : ;; @params account
+      56                 :            : ;; @returns (response uint)
+      57                 :            : (define-read-only (get-balance (account principal))
+      58                 :          0 :   (ok (/ (* (stx-get-balance account) ONE_8) (pow u10 u6)))
+      59                 :            : )
+      60                 :            : 
+      61                 :            : ;; @desc set-token-uri
+      62                 :            : ;; @restricted Contract-Owner
+      63                 :            : ;; @params value
+      64                 :            : ;; @returns (response bool)
+      65                 :            : (define-public (set-token-uri (value (string-utf8 256)))
+      66                 :          0 :   (begin
+      67            [ - ]:          0 :     (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED)
+      68                 :          0 :     (ok (var-set token-uri value))
+      69                 :            :   )
+      70                 :            : )
+      71                 :            : 
+      72                 :            : ;; @desc get-token-uri
+      73                 :            : ;; @returns (response some string-utf-8)
+      74                 :            : (define-read-only (get-token-uri)
+      75                 :          0 :   (ok (some (var-get token-uri)))
+      76                 :            : )
+      77                 :            : 
+      78                 :            : ;; @desc transfer
+      79                 :            : ;; @restricted sender; tx-sender should be sender
+      80                 :            : ;; @params amount
+      81                 :            : ;; @params sender
+      82                 :            : ;; @params recipient
+      83                 :            : ;; @params memo; expiry
+      84                 :            : ;; @returns (response bool uint)/ error
+      85                 :            : (define-public (transfer (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34))))
+      86                 :         18 :   (begin
+      87            [ - ]:         18 :     (asserts! (is-eq sender tx-sender) ERR-NOT-AUTHORIZED)
+      88                 :         18 :     (try! (stx-transfer? (/ (* amount (pow u10 u6)) ONE_8) sender recipient))
+      89         [ -  + ]:         18 :     (match memo to-print (print to-print) 0x)
+      90                 :         18 :     (ok true)
+      91                 :            :   )
+      92                 :            : )
+      93                 :            : 
+      94                 :            : (define-constant ONE_8 (pow u10 u8))
+      95                 :            : 
+      96                 :            : ;; @desc pow-decimals
+      97                 :            : ;; @returns uint
+      98                 :            : (define-private (pow-decimals)
+      99                 :         18 :   (pow u10 (unwrap-panic (get-decimals)))
+     100                 :            : )
+     101                 :            : 
+     102                 :            : ;; @desc fixed-to-decimals
+     103                 :            : ;; @params amount
+     104                 :            : ;; @returns uint
+     105                 :            : (define-read-only (fixed-to-decimals (amount uint))
+     106                 :         18 :   (/ (* amount (pow-decimals)) ONE_8)
+     107                 :            : )
+     108                 :            : 
+     109                 :            : ;; @desc decimals-to-fixed 
+     110                 :            : ;; @params amount
+     111                 :            : ;; @returns uint
+     112                 :            : (define-private (decimals-to-fixed (amount uint))
+     113                 :          0 :   (/ (* amount ONE_8) (pow-decimals))
+     114                 :            : )
+     115                 :            : 
+     116                 :            : ;; @desc get-total-supply-fixed
+     117                 :            : ;; @params token-id
+     118                 :            : ;; @returns (response uint)
+     119                 :            : (define-read-only (get-total-supply-fixed)
+     120                 :          0 :   (ok (decimals-to-fixed (unwrap-panic (get-total-supply))))
+     121                 :            : )
+     122                 :            : 
+     123                 :            : ;; @desc get-balance-fixed
+     124                 :            : ;; @params token-id
+     125                 :            : ;; @params who
+     126                 :            : ;; @returns (response uint)
+     127                 :            : (define-read-only (get-balance-fixed (account principal))
+     128                 :          0 :   (ok (decimals-to-fixed (unwrap-panic (get-balance account))))
+     129                 :            : )
+     130                 :            : 
+     131                 :            : ;; @desc transfer-fixed
+     132                 :            : ;; @params token-id
+     133                 :            : ;; @params amount
+     134                 :            : ;; @params sender
+     135                 :            : ;; @params recipient
+     136                 :            : ;; @returns (response bool)
+     137                 :            : (define-public (transfer-fixed (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34))))
+     138                 :         18 :   (transfer (fixed-to-decimals amount) sender recipient memo)
+     139                 :            : )
+     140                 :            : 
+     141                 :            : (define-public (mint (amount uint) (recipient principal))
+     142                 :          0 :   ERR-MINT-FAILED
+     143                 :            : )
+     144                 :            : 
+     145                 :            : (define-public (burn (amount uint) (sender principal))
+     146                 :          0 :   ERR-BURN-FAILED
+     147                 :            : )
+     148                 :            : 
+     149                 :            : (define-public (mint-fixed (amount uint) (recipient principal))
+     150                 :          0 :   (mint (fixed-to-decimals amount) recipient)
+     151                 :            : )
+     152                 :            : 
+     153                 :            : ;; @desc burn-fixed
+     154                 :            : ;; @params token-id
+     155                 :            : ;; @params amount
+     156                 :            : ;; @params sender
+     157                 :            : ;; @returns (response bool)
+     158                 :            : (define-public (burn-fixed (amount uint) (sender principal))
+     159                 :          0 :   (burn (fixed-to-decimals amount) sender)
+     160                 :            : )
+     161                 :            : 
+     162                 :            : ;; @desc check-err
+     163                 :            : ;; @params result 
+     164                 :            : ;; @params prior
+     165                 :            : ;; @returns (response bool uint)
+     166                 :            : (define-private (check-err (result (response bool uint)) (prior (response bool uint)))
+     167                 :          0 :     (match prior 
+     168            [ - ]:          0 :         ok-value result
+     169            [ - ]:          0 :         err-value (err err-value)
+     170                 :            :     )
+     171                 :            : )
+     172                 :            : 
+     173                 :            : (define-private (transfer-from-tuple (recipient { to: principal, amount: uint }))
+     174                 :          0 :   (ok (unwrap! (transfer-fixed (get amount recipient) tx-sender (get to recipient) none) ERR-TRANSFER-FAILED))
+     175                 :            : )
+     176                 :            : 
+     177                 :            : (define-public (send-many (recipients (list 200 { to: principal, amount: uint})))
+     178                 :          0 :   (fold check-err (map transfer-from-tuple recipients) (ok true))
+     179                 :            : )
+
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/emerald.png b/sbtc-ops/smart-contract/coverage/emerald.png new file mode 100644 index 00000000..38ad4f40 Binary files /dev/null and b/sbtc-ops/smart-contract/coverage/emerald.png differ diff --git a/sbtc-ops/smart-contract/coverage/gcov.css b/sbtc-ops/smart-contract/coverage/gcov.css new file mode 100644 index 00000000..0fcdff13 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/gcov.css @@ -0,0 +1,519 @@ +/* All views: initial background and text color */ +body +{ + color: #000000; + background-color: #ffffff; +} + +/* All views: standard link format*/ +a:link +{ + color: #284fa8; + text-decoration: underline; +} + +/* All views: standard link - visited format */ +a:visited +{ + color: #00cb40; + text-decoration: underline; +} + +/* All views: standard link - activated format */ +a:active +{ + color: #ff0040; + text-decoration: underline; +} + +/* All views: main title format */ +td.title +{ + text-align: center; + padding-bottom: 10px; + font-family: sans-serif; + font-size: 20pt; + font-style: italic; + font-weight: bold; +} + +/* All views: header item format */ +td.headerItem +{ + text-align: right; + padding-right: 6px; + font-family: sans-serif; + font-weight: bold; + vertical-align: top; + white-space: nowrap; +} + +/* All views: header item value format */ +td.headerValue +{ + text-align: left; + color: #284fa8; + font-family: sans-serif; + font-weight: bold; + white-space: nowrap; +} + +/* All views: header item coverage table heading */ +td.headerCovTableHead +{ + text-align: center; + padding-right: 6px; + padding-left: 6px; + padding-bottom: 0px; + font-family: sans-serif; + font-size: 80%; + white-space: nowrap; +} + +/* All views: header item coverage table entry */ +td.headerCovTableEntry +{ + text-align: right; + color: #284fa8; + font-family: sans-serif; + font-weight: bold; + white-space: nowrap; + padding-left: 12px; + padding-right: 4px; + background-color: #dae7fe; +} + +/* All views: header item coverage table entry for high coverage rate */ +td.headerCovTableEntryHi +{ + text-align: right; + color: #000000; + font-family: sans-serif; + font-weight: bold; + white-space: nowrap; + padding-left: 12px; + padding-right: 4px; + background-color: #a7fc9d; +} + +/* All views: header item coverage table entry for medium coverage rate */ +td.headerCovTableEntryMed +{ + text-align: right; + color: #000000; + font-family: sans-serif; + font-weight: bold; + white-space: nowrap; + padding-left: 12px; + padding-right: 4px; + background-color: #ffea20; +} + +/* All views: header item coverage table entry for ow coverage rate */ +td.headerCovTableEntryLo +{ + text-align: right; + color: #000000; + font-family: sans-serif; + font-weight: bold; + white-space: nowrap; + padding-left: 12px; + padding-right: 4px; + background-color: #ff0000; +} + +/* All views: header legend value for legend entry */ +td.headerValueLeg +{ + text-align: left; + color: #000000; + font-family: sans-serif; + font-size: 80%; + white-space: nowrap; + padding-top: 4px; +} + +/* All views: color of horizontal ruler */ +td.ruler +{ + background-color: #6688d4; +} + +/* All views: version string format */ +td.versionInfo +{ + text-align: center; + padding-top: 2px; + font-family: sans-serif; + font-style: italic; +} + +/* Directory view/File view (all)/Test case descriptions: + table headline format */ +td.tableHead +{ + text-align: center; + color: #ffffff; + background-color: #6688d4; + font-family: sans-serif; + font-size: 120%; + font-weight: bold; + white-space: nowrap; + padding-left: 4px; + padding-right: 4px; +} + +span.tableHeadSort +{ + padding-right: 4px; +} + +/* Directory view/File view (all): filename entry format */ +td.coverFile +{ + text-align: left; + padding-left: 10px; + padding-right: 20px; + color: #284fa8; + background-color: #dae7fe; + font-family: monospace; +} + +/* Directory view/File view (all): bar-graph entry format*/ +td.coverBar +{ + padding-left: 10px; + padding-right: 10px; + background-color: #dae7fe; +} + +/* Directory view/File view (all): bar-graph outline color */ +td.coverBarOutline +{ + background-color: #000000; +} + +/* Directory view/File view (all): percentage entry for files with + high coverage rate */ +td.coverPerHi +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #a7fc9d; + font-weight: bold; + font-family: sans-serif; +} + +/* Directory view/File view (all): line count entry for files with + high coverage rate */ +td.coverNumHi +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #a7fc9d; + white-space: nowrap; + font-family: sans-serif; +} + +/* Directory view/File view (all): percentage entry for files with + medium coverage rate */ +td.coverPerMed +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #ffea20; + font-weight: bold; + font-family: sans-serif; +} + +/* Directory view/File view (all): line count entry for files with + medium coverage rate */ +td.coverNumMed +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #ffea20; + white-space: nowrap; + font-family: sans-serif; +} + +/* Directory view/File view (all): percentage entry for files with + low coverage rate */ +td.coverPerLo +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #ff0000; + font-weight: bold; + font-family: sans-serif; +} + +/* Directory view/File view (all): line count entry for files with + low coverage rate */ +td.coverNumLo +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #ff0000; + white-space: nowrap; + font-family: sans-serif; +} + +/* File view (all): "show/hide details" link format */ +a.detail:link +{ + color: #B8D0FF; + font-size:80%; +} + +/* File view (all): "show/hide details" link - visited format */ +a.detail:visited +{ + color: #B8D0FF; + font-size:80%; +} + +/* File view (all): "show/hide details" link - activated format */ +a.detail:active +{ + color: #ffffff; + font-size:80%; +} + +/* File view (detail): test name entry */ +td.testName +{ + text-align: right; + padding-right: 10px; + background-color: #dae7fe; + font-family: sans-serif; +} + +/* File view (detail): test percentage entry */ +td.testPer +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #dae7fe; + font-family: sans-serif; +} + +/* File view (detail): test lines count entry */ +td.testNum +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #dae7fe; + font-family: sans-serif; +} + +/* Test case descriptions: test name format*/ +dt +{ + font-family: sans-serif; + font-weight: bold; +} + +/* Test case descriptions: description table body */ +td.testDescription +{ + padding-top: 10px; + padding-left: 30px; + padding-bottom: 10px; + padding-right: 30px; + background-color: #dae7fe; +} + +/* Source code view: function entry */ +td.coverFn +{ + text-align: left; + padding-left: 10px; + padding-right: 20px; + color: #284fa8; + background-color: #dae7fe; + font-family: monospace; +} + +/* Source code view: function entry zero count*/ +td.coverFnLo +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #ff0000; + font-weight: bold; + font-family: sans-serif; +} + +/* Source code view: function entry nonzero count*/ +td.coverFnHi +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #dae7fe; + font-weight: bold; + font-family: sans-serif; +} + +/* Source code view: source code format */ +pre.source +{ + font-family: monospace; + white-space: pre; + margin-top: 2px; +} + +/* Source code view: line number format */ +span.lineNum +{ + background-color: #efe383; +} + +/* Source code view: format for lines which were executed */ +td.lineCov, +span.lineCov +{ + background-color: #cad7fe; +} + +/* Source code view: format for Cov legend */ +span.coverLegendCov +{ + padding-left: 10px; + padding-right: 10px; + padding-bottom: 2px; + background-color: #cad7fe; +} + +/* Source code view: format for lines which were not executed */ +td.lineNoCov, +span.lineNoCov +{ + background-color: #ff6230; +} + +/* Source code view: format for NoCov legend */ +span.coverLegendNoCov +{ + padding-left: 10px; + padding-right: 10px; + padding-bottom: 2px; + background-color: #ff6230; +} + +/* Source code view (function table): standard link - visited format */ +td.lineNoCov > a:visited, +td.lineCov > a:visited +{ + color: #000000; + text-decoration: underline; +} + +/* Source code view: format for lines which were executed only in a + previous version */ +span.lineDiffCov +{ + background-color: #b5f7af; +} + +/* Source code view: format for branches which were executed + * and taken */ +span.branchCov +{ + background-color: #cad7fe; +} + +/* Source code view: format for branches which were executed + * but not taken */ +span.branchNoCov +{ + background-color: #ff6230; +} + +/* Source code view: format for branches which were not executed */ +span.branchNoExec +{ + background-color: #ff6230; +} + +/* Source code view: format for the source code heading line */ +pre.sourceHeading +{ + white-space: pre; + font-family: monospace; + font-weight: bold; + margin: 0px; +} + +/* All views: header legend value for low rate */ +td.headerValueLegL +{ + font-family: sans-serif; + text-align: center; + white-space: nowrap; + padding-left: 4px; + padding-right: 2px; + background-color: #ff0000; + font-size: 80%; +} + +/* All views: header legend value for med rate */ +td.headerValueLegM +{ + font-family: sans-serif; + text-align: center; + white-space: nowrap; + padding-left: 2px; + padding-right: 2px; + background-color: #ffea20; + font-size: 80%; +} + +/* All views: header legend value for hi rate */ +td.headerValueLegH +{ + font-family: sans-serif; + text-align: center; + white-space: nowrap; + padding-left: 2px; + padding-right: 4px; + background-color: #a7fc9d; + font-size: 80%; +} + +/* All views except source code view: legend format for low coverage */ +span.coverLegendCovLo +{ + padding-left: 10px; + padding-right: 10px; + padding-top: 2px; + background-color: #ff0000; +} + +/* All views except source code view: legend format for med coverage */ +span.coverLegendCovMed +{ + padding-left: 10px; + padding-right: 10px; + padding-top: 2px; + background-color: #ffea20; +} + +/* All views except source code view: legend format for hi coverage */ +span.coverLegendCovHi +{ + padding-left: 10px; + padding-right: 10px; + padding-top: 2px; + background-color: #a7fc9d; +} diff --git a/sbtc-ops/smart-contract/coverage/glass.png b/sbtc-ops/smart-contract/coverage/glass.png new file mode 100644 index 00000000..e1abc006 Binary files /dev/null and b/sbtc-ops/smart-contract/coverage/glass.png differ diff --git a/sbtc-ops/smart-contract/coverage/index-sort-b.html b/sbtc-ops/smart-contract/coverage/index-sort-b.html new file mode 100644 index 00000000..8e991ac7 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/index-sort-b.html @@ -0,0 +1,107 @@ + + + + + + + LCOV - coverage.lcov + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top levelHitTotalCoverage
Test:coverage.lcovLines:1197457326.2 %
Date:2023-08-18 21:53:02Functions:20279625.4 %
Branches:214127016.9 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Directory Sort by nameLine Coverage Sort by line coverageFunctions Sort by function coverageBranches Sort by branch coverage
contracts +
26.2%26.2%
+
26.2 %1197 / 457325.4 %202 / 79616.9 %214 / 1270
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/index-sort-f.html b/sbtc-ops/smart-contract/coverage/index-sort-f.html new file mode 100644 index 00000000..6b32f061 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/index-sort-f.html @@ -0,0 +1,107 @@ + + + + + + + LCOV - coverage.lcov + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top levelHitTotalCoverage
Test:coverage.lcovLines:1197457326.2 %
Date:2023-08-18 21:53:02Functions:20279625.4 %
Branches:214127016.9 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Directory Sort by nameLine Coverage Sort by line coverageFunctions Sort by function coverageBranches Sort by branch coverage
contracts +
26.2%26.2%
+
26.2 %1197 / 457325.4 %202 / 79616.9 %214 / 1270
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/index-sort-l.html b/sbtc-ops/smart-contract/coverage/index-sort-l.html new file mode 100644 index 00000000..893498b8 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/index-sort-l.html @@ -0,0 +1,107 @@ + + + + + + + LCOV - coverage.lcov + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top levelHitTotalCoverage
Test:coverage.lcovLines:1197457326.2 %
Date:2023-08-18 21:53:02Functions:20279625.4 %
Branches:214127016.9 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Directory Sort by nameLine Coverage Sort by line coverageFunctions Sort by function coverageBranches Sort by branch coverage
contracts +
26.2%26.2%
+
26.2 %1197 / 457325.4 %202 / 79616.9 %214 / 1270
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/index.html b/sbtc-ops/smart-contract/coverage/index.html new file mode 100644 index 00000000..5f9a8c37 --- /dev/null +++ b/sbtc-ops/smart-contract/coverage/index.html @@ -0,0 +1,107 @@ + + + + + + + LCOV - coverage.lcov + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top levelHitTotalCoverage
Test:coverage.lcovLines:1197457326.2 %
Date:2023-08-18 21:53:02Functions:20279625.4 %
Branches:214127016.9 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Directory Sort by nameLine Coverage Sort by line coverageFunctions Sort by function coverageBranches Sort by branch coverage
contracts +
26.2%26.2%
+
26.2 %1197 / 457325.4 %202 / 79616.9 %214 / 1270
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/sbtc-ops/smart-contract/coverage/ruby.png b/sbtc-ops/smart-contract/coverage/ruby.png new file mode 100644 index 00000000..991b6d4e Binary files /dev/null and b/sbtc-ops/smart-contract/coverage/ruby.png differ diff --git a/sbtc-ops/smart-contract/coverage/snow.png b/sbtc-ops/smart-contract/coverage/snow.png new file mode 100644 index 00000000..2cdae107 Binary files /dev/null and b/sbtc-ops/smart-contract/coverage/snow.png differ diff --git a/sbtc-ops/smart-contract/coverage/updown.png b/sbtc-ops/smart-contract/coverage/updown.png new file mode 100644 index 00000000..aa56a238 Binary files /dev/null and b/sbtc-ops/smart-contract/coverage/updown.png differ diff --git a/sbtc-ops/smart-contract/deployments/default.devnet-plan.yaml b/sbtc-ops/smart-contract/deployments/default.devnet-plan.yaml new file mode 100644 index 00000000..23e42770 --- /dev/null +++ b/sbtc-ops/smart-contract/deployments/default.devnet-plan.yaml @@ -0,0 +1,158 @@ +--- +id: 0 +name: Devnet deployment +network: devnet +stacks-node: "http://localhost:20443" +bitcoin-node: "http://devnet:devnet@localhost:18443" +plan: + batches: + - id: 0 + transactions: + - contract-publish: + contract-name: ft-trait + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 8340 + path: contracts/ft-trait.clar + anchor-block-only: true + clarity-version: 2 + - contract-publish: + contract-name: restricted-token-trait + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 5460 + path: contracts/restricted-token-trait.clar + anchor-block-only: true + clarity-version: 2 + - contract-publish: + contract-name: Wrapped-Bitcoin + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 104560 + path: contracts/Wrapped-Bitcoin.clar + anchor-block-only: true + clarity-version: 2 + - contract-publish: + contract-name: trait-sip-010 + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 13450 + path: contracts/trait-sip-010.clar + anchor-block-only: true + clarity-version: 2 + - contract-publish: + contract-name: trait-flash-loan-user + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 1700 + path: contracts/trait-flash-loan-user.clar + anchor-block-only: true + clarity-version: 2 + - contract-publish: + contract-name: trait-semi-fungible + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 16920 + path: contracts/trait-semi-fungible.clar + anchor-block-only: true + clarity-version: 2 + - contract-publish: + contract-name: alex-vault-v1-1 + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 63750 + path: contracts/alex-vault-v1-1.clar + anchor-block-only: true + clarity-version: 2 + - contract-publish: + contract-name: trait-ownable + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 1430 + path: contracts/trait-ownable.clar + anchor-block-only: true + clarity-version: 2 + - contract-publish: + contract-name: token-amm-swap-pool-v1-1 + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 117150 + path: contracts/token-amm-swap-pool-v1-1.clar + anchor-block-only: true + clarity-version: 2 + - contract-publish: + contract-name: amm-swap-pool-v1-1 + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 467440 + path: contracts/amm-swap-pool-v1-1.clar + anchor-block-only: true + clarity-version: 2 + - contract-publish: + contract-name: clarity-bitcoin + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 412540 + path: contracts/clarity-bitcoin.clar + anchor-block-only: true + clarity-version: 2 + - contract-publish: + contract-name: token-wbtc + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 52910 + path: contracts/token-wbtc.clar + anchor-block-only: true + clarity-version: 2 + - contract-publish: + contract-name: token-wstx + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 45960 + path: contracts/token-wstx.clar + anchor-block-only: true + clarity-version: 2 + - contract-publish: + contract-name: degen-bridge-testnet-v3 + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 329670 + path: contracts/degen-bridge-testnet-v3.clar + anchor-block-only: true + clarity-version: 2 + - contract-publish: + contract-name: bridge-contract + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 42260 + path: contracts/bridge-contract.clar + anchor-block-only: true + clarity-version: 2 + - contract-publish: + contract-name: mining-pool + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 532050 + path: contracts/mining-pool.clar + anchor-block-only: true + clarity-version: 2 + - contract-publish: + contract-name: mining-pool-5-blocks + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 475840 + path: contracts/mining-pool-5-blocks.clar + anchor-block-only: true + clarity-version: 2 + - contract-publish: + contract-name: mining-pool-test + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 475140 + path: contracts/mining-pool-test.clar + anchor-block-only: true + clarity-version: 2 + - contract-publish: + contract-name: stacking-pool + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 348480 + path: contracts/stacking-pool.clar + anchor-block-only: true + clarity-version: 2 + - contract-publish: + contract-name: stacking-pool-test + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 346620 + path: contracts/stacking-pool-test.clar + anchor-block-only: true + clarity-version: 2 + - contract-publish: + contract-name: trait-sip-010-sbtc + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 13630 + path: contracts/trait-sip-010-sbtc.clar + anchor-block-only: true + clarity-version: 2 + epoch: "2.4" diff --git a/sbtc-ops/smart-contract/history.txt b/sbtc-ops/smart-contract/history.txt new file mode 100644 index 00000000..88be7a24 --- /dev/null +++ b/sbtc-ops/smart-contract/history.txt @@ -0,0 +1,15 @@ +#V2 +(contract-call .alex-go-swap swap-stx-to-xbtc 10 4) +(contract-call? .alex-go-swap swap-stx-to-xbtc 10 4) +(contract-call? .alex-go-swap swap-stx-to-xbtc u10 u4) +(contract-call? .mining-pool-5-blocks update-threshold) +(contract-call? .mining-pool-5-blocks get-k) +(contract-call? .stacking-pool check-pool-SC-pox-2-allowance) +:set_tx-sender ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5 +:set_tx_sender ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5 +:set-tx-sender ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5 +::help +::set-tx-sender ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5 +::set_tx_sender ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5 +(contract-call? .stacking-pool check-pool-SC-pox-2-allowance) +(contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.stacking-pool check-pool-SC-pox-2-allowance) diff --git a/sbtc-ops/smart-contract/integration/allowContractCaller.ts b/sbtc-ops/smart-contract/integration/allowContractCaller.ts new file mode 100644 index 00000000..dc3b8976 --- /dev/null +++ b/sbtc-ops/smart-contract/integration/allowContractCaller.ts @@ -0,0 +1,36 @@ +import { + AnchorMode, + broadcastTransaction, + contractPrincipalCV, + makeContractCall, + noneCV, + PostConditionMode, +} from '@stacks/transactions'; +import { mainContract, poxPools1CycleContract } from './contracts'; +import { StacksTestnet } from '@stacks/network'; + +export async function broadcastAllowContractCallerContracCall({ + senderKey, + network, + nonce, +}: { + senderKey: string; + network: StacksTestnet; + nonce: number; +}) { + let txOptions = { + contractAddress: 'ST000000000000000000002AMW42H', + contractName: 'pox-3', + functionName: 'allow-contract-caller', + functionArgs: [contractPrincipalCV(mainContract.address, mainContract.name), noneCV()], + network, + nonce, + anchorMode: AnchorMode.OnChainOnly, + postConditionMode: PostConditionMode.Allow, + senderKey, + }; + // @ts-ignore + let tx = await makeContractCall(txOptions); + // Broadcast transaction to our Devnet stacks node + return broadcastTransaction(tx, network); +} diff --git a/sbtc-ops/smart-contract/integration/balance.test.ts b/sbtc-ops/smart-contract/integration/balance.test.ts new file mode 100644 index 00000000..283a5da8 --- /dev/null +++ b/sbtc-ops/smart-contract/integration/balance.test.ts @@ -0,0 +1,277 @@ +// deposit stx deployer + +/// get balance deployer +// everything happens on stacks_2.1 +// pox_2 +import { + balanceDepositSTX, + getBalanceSTX, + buildDevnetNetworkOrchestrator, + DEFAULT_EPOCH_TIMELINE, + getAccount, + getNetworkIdFromEnv, + getRewardAtBlock, + votePositive, + tryEnterPool, + addPendingMinersToPool, + askToJoin, + distributeRewards, +} from './helpers'; +import { StacksTestnet } from '@stacks/network'; +import { DevnetNetworkOrchestrator } from '@hirosystems/stacks-devnet-js'; +import { FAST_FORWARD_TO_EPOCH_2_4 } from './helpers-stacking'; +import { mainContract } from './contracts'; +import { crypto } from 'bitcoinjs-lib'; + +describe('testing depositing balance stx', () => { + let orchestrator: DevnetNetworkOrchestrator; + let timeline = FAST_FORWARD_TO_EPOCH_2_4; + + beforeAll(() => { + orchestrator = buildDevnetNetworkOrchestrator(getNetworkIdFromEnv()); + orchestrator.start(1000); + }); + + afterAll(() => { + orchestrator.terminate(); + }); + + it('test whole flow with initiate, deposit STX and see balance', async () => { + const Accounts = { + DEPLOYER: { + stxAddress: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM', + btcAddress: 'mqVnk6NPRdhntvfm4hh9vvjiRkFDUuSYsH', + secretKey: '753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601', + }, + WALLET_1: { + stxAddress: 'ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5', + btcAddress: 'mr1iPkD9N3RJZZxXRk7xF9d36gffa6exNC', + secretKey: '7287ba251d44a4d3fd9276c88ce34c5c52a038955511cccaf77e61068649c17801', + }, + WALLET_2: { + stxAddress: 'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG', + btcAddress: 'muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG', + secretKey: '530d9f61984c888536871c6573073bdfc0058896dc1adfe9a6a10dfacadc209101', + }, + WALLET_3: { + stxAddress: 'ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC', + btcAddress: 'mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7', + secretKey: 'd655b2523bcd65e34889725c73064feb17ceb796831c0e111ba1a552b0f31b3901', + }, + WALLET_4: { + stxAddress: 'ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND', + btcAddress: 'mg1C76bNTutiCDV3t9nWhZs3Dc8LzUufj8', + secretKey: 'f9d7206a47f14d2870c163ebab4bf3e70d18f5d14ce1031f3902fbbc894fe4c701', + }, + WALLET_5: { + stxAddress: 'ST2REHHS5J3CERCRBEPMGH7921Q6PYKAADT7JP2VB', + btcAddress: 'mweN5WVqadScHdA81aATSdcVr4B6dNokqx', + secretKey: '3eccc5dac8056590432db6a35d52b9896876a3d5cbdea53b72400bc9c2099fe801', + }, + WALLET_6: { + stxAddress: 'ST3AM1A56AK2C1XAFJ4115ZSV26EB49BVQ10MGCS0', + btcAddress: 'mzxXgV6e4BZSsz8zVHm3TmqbECt7mbuErt', + secretKey: '7036b29cb5e235e5fd9b09ae3e8eec4404e44906814d5d01cbca968a60ed4bfb01', + }, + WALLET_7: { + stxAddress: 'ST3PF13W7Z0RRM42A8VZRVFQ75SV1K26RXEP8YGKJ', + btcAddress: 'n37mwmru2oaVosgfuvzBwgV2ysCQRrLko7', + secretKey: 'b463f0df6c05d2f156393eee73f8016c5372caa0e9e29a901bb7171d90dc4f1401', + }, + WALLET_8: { + stxAddress: 'ST3NBRSFKX28FQ2ZJ1MAKX58HKHSDGNV5N7R21XCP', + btcAddress: 'n2v875jbJ4RjBnTjgbfikDfnwsDV5iUByw', + secretKey: '6a1a754ba863d7bab14adbbc3f8ebb090af9e871ace621d3e5ab634e1422885e01', + }, + WALLET_9: { + stxAddress: 'ST2RF0D3GJR7ZX63PQ9WXVAPNG6EJCCR97NTVFGES', + btcAddress: 'mweWyYM2SdmaZRMZneysa2XtsP1o6wFWKC', + secretKey: '2b2c2dd7ae64aa7880042a443f5a1e1a1b575cc0ef16d6c7e3bb8a9ce08bfe1d01', + }, + WALLET_10: { + stxAddress: 'ST3CD3T03P3Z8RMAYR7S6BZQ65QNYS9W18QHHJ4KM', + btcAddress: 'n1HPi2zoJZzwjAFgGrYckLxPnBsR2m7ViD', + secretKey: '47599fb4a8cfb07e2db61484c81459db81a5480e770b0de8cbe8de960834bf1701', + }, + }; + const network = new StacksTestnet({ url: orchestrator.getStacksNodeUrl() }); + const fee = 1000; + + const btcAddressVersionUintArray = Uint8Array.from(Buffer.from('00', 'hex')); + const publicKeyHex = '02e8f7dc91e49a577ce9ea8989c7184aea8886fe5250f02120dc6f98e3619679b0'; + const publicKey = Buffer.from(publicKeyHex, 'hex'); + const pKhash160 = crypto.hash160(publicKey); + const btcHashBuffer = pKhash160; + const btcUintArray = Uint8Array.from(btcHashBuffer); + + // Advance to make sure PoX-3 is activated + + await orchestrator.waitForStacksBlockAnchoredOnBitcoinBlockOfHeight(timeline.epoch_2_4 + 1, 5, true); + // get nonces + let nonceWallets = {}; + for (let i = 6; i < 10; i++) { + nonceWallets[i] = (await getAccount(network, Accounts[`WALLET_${i}`].stxAddress)).nonce; + } + + // ask to join with 4 participants + + for (let i = 6; i < 10; i++) { + let responseawait = await askToJoin( + btcAddressVersionUintArray, + btcUintArray, + network, + Accounts[`WALLET_${i}`], + fee, + nonceWallets[i] + ); + nonceWallets[i] += 1; + } + let chainUpdate, txs; + + let askToJoinTxIndex = 0; + let blockIndex = 0; + while (askToJoinTxIndex < 4) { + chainUpdate = await orchestrator.waitForNextStacksBlock(); + txs = chainUpdate.new_blocks[0].block.transactions; + blockIndex++; + if (txs.length > 1) { + for (let i = 1; i <= txs.length - 1; i++) { + let txMetadata = txs[i].metadata; + let txData = txMetadata.kind.data; + let txSC = txData['contract_identifier']; + let txMethod = txData['method']; + + if (txSC === `${mainContract.address}.mining-pool-5-blocks` && txMethod === `ask-to-join`) { + expect((txMetadata as any)['result']).toBe('(ok true)'); + expect((txMetadata as any)['success']).toBe(true); + askToJoinTxIndex++; + } + } + } + } + + // deployer vote positive for joining each participant + + let nonceDeployer = (await getAccount(network, Accounts.DEPLOYER.stxAddress)).nonce; + for (let i = 6; i < 10; i++) { + let responseFor = await votePositive( + Accounts[`WALLET_${i}`].stxAddress, + network, + Accounts.DEPLOYER, + fee, + nonceDeployer + ); + nonceDeployer += 1; + expect(responseFor.error).toBeUndefined(); + } + + let votePositiveJoinIndex = 0; + blockIndex = 0; + + while (votePositiveJoinIndex < 4) { + chainUpdate = await orchestrator.waitForNextStacksBlock(); + txs = chainUpdate.new_blocks[0].block.transactions; + blockIndex++; + if (txs.length > 1) { + for (let i = 1; i <= txs.length - 1; i++) { + let txMetadata = txs[i].metadata; + let txData = txMetadata.kind.data; + let txSC = txData['contract_identifier']; + let txMethod = txData['method']; + + if (txSC === `${mainContract.address}.mining-pool-5-blocks` && txMethod === `vote-positive-join-request`) { + expect((txMetadata as any)['result']).toBe('(ok true)'); + expect((txMetadata as any)['success']).toBe(true); + votePositiveJoinIndex++; + } + } + } + } + + // try-enter-pool + + for (let i = 6; i < 10; i++) { + let responseFor = await tryEnterPool(network, Accounts[`WALLET_${i}`], fee, nonceWallets[i]); + nonceWallets[i] += 1; + expect(responseFor.error).toBeUndefined(); + } + + let tryEnterPoolIndex = 0; + blockIndex = 0; + + while (tryEnterPoolIndex < 4) { + chainUpdate = await orchestrator.waitForNextStacksBlock(); + txs = chainUpdate.new_blocks[0].block.transactions; + blockIndex++; + if (txs.length > 1) { + for (let i = 1; i <= txs.length - 1; i++) { + let txMetadata = txs[i].metadata; + let txData = txMetadata.kind.data; + let txSC = txData['contract_identifier']; + let txMethod = txData['method']; + + if (txSC === `${mainContract.address}.mining-pool-5-blocks` && txMethod === `try-enter-pool`) { + expect((txMetadata as any)['result']).toBe('(ok true)'); + expect((txMetadata as any)['success']).toBe(true); + tryEnterPoolIndex++; + } + } + } + } + + let current_block_height = chainUpdate.new_blocks[0].block.block_identifier.index; + current_block_height += 6; + let reward_block_height = current_block_height + 1; + await orchestrator.waitForStacksBlockOfHeight(current_block_height); + + // add pending miners to pool + + let response = await addPendingMinersToPool(network, Accounts.DEPLOYER, fee, nonceDeployer); + nonceDeployer += 1; + expect(response.error).toBeUndefined(); + + chainUpdate = await orchestrator.waitForNextStacksBlock(); + let metadata = chainUpdate.new_blocks[0].block.transactions[1].metadata; + expect((metadata as any)['success']).toBe(true); + expect((metadata as any)['result']).toBe('(ok true)'); + + // now 5 participants in mining pool + // Check balance for particiapnts + + current_block_height = chainUpdate.new_blocks[0].block.block_identifier.index; + current_block_height += 110; + await orchestrator.waitForStacksBlockOfHeight(current_block_height); + let info = await getRewardAtBlock(network, 15); + console.log('rewards at block 15:'); + console.log(info.value.claimer.value); + console.log(info.value.reward.value); + info = await getRewardAtBlock(network, reward_block_height); // 27 + console.log(info.value.reward.value); + let distributedAmount = 1000301000; + + // 1 000 301 000 + // 3 000 900 000 + // // we are at block 125+ + + nonceDeployer = (await getAccount(network, Accounts.DEPLOYER.stxAddress)).nonce; + distributeRewards(reward_block_height, network, Accounts.DEPLOYER, fee, nonceDeployer); + nonceDeployer += 1; + chainUpdate = await orchestrator.waitForNextStacksBlock(); + // 100030100 / 5 = 200060200 + info = await getRewardAtBlock(network, 122); // 27 + console.log('rewards at block 122 principal: '); + console.log(info.value.claimer.value); + for (let i = 6; i < 10; i++) { + info = await getBalanceSTX(network, Accounts[`WALLET_${i}`].stxAddress); + console.log('Earned values by ', i); + console.log(info); + expect(info.value.value).toBe(`200060200`); + } + // see at what block_height miners join pool + // distribute rewards once - working for block 3 at block 120 + // verify with balances of the miners before and after distributing the rewards + // distribute rewards same - not working for block 3 at block 120 + // distribute rewards same - working for block 4 at block 120 + // check balances for miners + }); +}); diff --git a/sbtc-ops/smart-contract/integration/constants-stacking.ts b/sbtc-ops/smart-contract/integration/constants-stacking.ts new file mode 100644 index 00000000..12335f05 --- /dev/null +++ b/sbtc-ops/smart-contract/integration/constants-stacking.ts @@ -0,0 +1,61 @@ +export namespace Constants { + export const DEVNET_DEFAULT_EPOCH_2_0 = 100; + export const DEVNET_DEFAULT_EPOCH_2_05 = 102; + export const DEVNET_DEFAULT_EPOCH_2_1 = 113; + export const DEVNET_DEFAULT_POX_2_ACTIVATION = 120; + export const DEVNET_DEFAULT_EPOCH_2_2 = 122; + export const DEVNET_DEFAULT_EPOCH_2_3 = 128; + export const DEVNET_DEFAULT_EPOCH_2_4 = 134; + export const BITCOIN_BLOCK_TIME = 1_000; + export const REWARD_CYCLE_LENGTH = 6; +} + +export namespace Contracts { + export const POX_1 = { + address: 'ST000000000000000000002AMW42H', + name: 'pox', + }; + export const POX_2 = { + address: 'ST000000000000000000002AMW42H', + name: 'pox-2', + }; + export const POX_3 = { + address: 'ST000000000000000000002AMW42H', + name: 'pox-3', + }; +} + +export namespace Accounts { + export const DEPLOYER = { + stxAddress: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM', + btcAddress: 'mqVnk6NPRdhntvfm4hh9vvjiRkFDUuSYsH', + secretKey: '753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601', + }; + export const WALLET_1 = { + stxAddress: 'ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5', + btcAddress: 'mr1iPkD9N3RJZZxXRk7xF9d36gffa6exNC', + secretKey: '7287ba251d44a4d3fd9276c88ce34c5c52a038955511cccaf77e61068649c17801', + }; + export const WALLET_2 = { + stxAddress: 'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG', + btcAddress: 'muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG', + secretKey: '530d9f61984c888536871c6573073bdfc0058896dc1adfe9a6a10dfacadc209101', + }; + export const WALLET_3 = { + stxAddress: 'ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC', + btcAddress: 'mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7', + secretKey: 'd655b2523bcd65e34889725c73064feb17ceb796831c0e111ba1a552b0f31b3901', + }; + + export const WALLET_4 = { + secretKey: 'f9d7206a47f14d2870c163ebab4bf3e70d18f5d14ce1031f3902fbbc894fe4c701', + stxAddress: 'ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND', + btcAddress: 'mg1C76bNTutiCDV3t9nWhZs3Dc8LzUufj8', + }; + + export const WALLET_8 = { + secretKey: '6a1a754ba863d7bab14adbbc3f8ebb090af9e871ace621d3e5ab634e1422885e01', + stxAddress: 'ST3NBRSFKX28FQ2ZJ1MAKX58HKHSDGNV5N7R21XCP', + btcAddress: 'n2v875jbJ4RjBnTjgbfikDfnwsDV5iUByw', + }; +} diff --git a/sbtc-ops/smart-contract/integration/constants.ts b/sbtc-ops/smart-contract/integration/constants.ts new file mode 100644 index 00000000..a5fa16d5 --- /dev/null +++ b/sbtc-ops/smart-contract/integration/constants.ts @@ -0,0 +1,79 @@ +export namespace Constants { + export const DEVNET_DEFAULT_EPOCH_2_0 = 100; + export const DEVNET_DEFAULT_EPOCH_2_05 = 102; + export const DEVNET_DEFAULT_EPOCH_2_1 = 113; + export const DEVNET_DEFAULT_POX_2_ACTIVATION = 120; + export const DEVNET_DEFAULT_EPOCH_2_2 = 122; + export const DEVNET_DEFAULT_EPOCH_2_3 = 128; + export const DEVNET_DEFAULT_EPOCH_2_4 = 134; + export const BITCOIN_BLOCK_TIME = 1_000; +} + +export namespace Contracts { + export const POX_1 = { + address: 'ST000000000000000000002AMW42H', + name: 'pox', + }; + export const POX_2 = { + address: 'ST000000000000000000002AMW42H', + name: 'pox-2', + }; +} + +export const Accounts = { + DEPLOYER: { + stxAddress: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM', + btcAddress: 'mqVnk6NPRdhntvfm4hh9vvjiRkFDUuSYsH', + secretKey: '753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601', + }, + WALLET_1: { + stxAddress: 'ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5', + btcAddress: 'mr1iPkD9N3RJZZxXRk7xF9d36gffa6exNC', + secretKey: '7287ba251d44a4d3fd9276c88ce34c5c52a038955511cccaf77e61068649c17801', + }, + WALLET_2: { + stxAddress: 'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG', + btcAddress: 'muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG', + secretKey: '530d9f61984c888536871c6573073bdfc0058896dc1adfe9a6a10dfacadc209101', + }, + WALLET_3: { + stxAddress: 'ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC', + btcAddress: 'mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7', + secretKey: 'd655b2523bcd65e34889725c73064feb17ceb796831c0e111ba1a552b0f31b3901', + }, + WALLET_4: { + stxAddress: 'ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND', + btcAddress: 'mg1C76bNTutiCDV3t9nWhZs3Dc8LzUufj8', + secretKey: 'f9d7206a47f14d2870c163ebab4bf3e70d18f5d14ce1031f3902fbbc894fe4c701', + }, + WALLET_5: { + stxAddress: 'ST2REHHS5J3CERCRBEPMGH7921Q6PYKAADT7JP2VB', + btcAddress: 'mweN5WVqadScHdA81aATSdcVr4B6dNokqx', + secretKey: '3eccc5dac8056590432db6a35d52b9896876a3d5cbdea53b72400bc9c2099fe801', + }, + WALLET_6: { + stxAddress: 'ST3AM1A56AK2C1XAFJ4115ZSV26EB49BVQ10MGCS0', + btcAddress: 'mzxXgV6e4BZSsz8zVHm3TmqbECt7mbuErt', + secretKey: '7036b29cb5e235e5fd9b09ae3e8eec4404e44906814d5d01cbca968a60ed4bfb01', + }, + WALLET_7: { + stxAddress: 'ST3PF13W7Z0RRM42A8VZRVFQ75SV1K26RXEP8YGKJ', + btcAddress: 'n37mwmru2oaVosgfuvzBwgV2ysCQRrLko7', + secretKey: 'b463f0df6c05d2f156393eee73f8016c5372caa0e9e29a901bb7171d90dc4f1401', + }, + WALLET_8: { + stxAddress: 'ST3NBRSFKX28FQ2ZJ1MAKX58HKHSDGNV5N7R21XCP', + btcAddress: 'n2v875jbJ4RjBnTjgbfikDfnwsDV5iUByw', + secretKey: '6a1a754ba863d7bab14adbbc3f8ebb090af9e871ace621d3e5ab634e1422885e01', + }, + WALLET_9: { + stxAddress: 'ST2RF0D3GJR7ZX63PQ9WXVAPNG6EJCCR97NTVFGES', + btcAddress: 'mweWyYM2SdmaZRMZneysa2XtsP1o6wFWKC', + secretKey: '2b2c2dd7ae64aa7880042a443f5a1e1a1b575cc0ef16d6c7e3bb8a9ce08bfe1d01', + }, + WALLET_10: { + stxAddress: 'ST3CD3T03P3Z8RMAYR7S6BZQ65QNYS9W18QHHJ4KM', + btcAddress: 'n1HPi2zoJZzwjAFgGrYckLxPnBsR2m7ViD', + secretKey: '47599fb4a8cfb07e2db61484c81459db81a5480e770b0de8cbe8de960834bf1701', + }, +}; diff --git a/sbtc-ops/smart-contract/integration/contracts.ts b/sbtc-ops/smart-contract/integration/contracts.ts new file mode 100644 index 00000000..2a0c103c --- /dev/null +++ b/sbtc-ops/smart-contract/integration/contracts.ts @@ -0,0 +1,364 @@ +// Code generated with the stacksjs-helper-generator extension +// Manual edits will be overwritten + +import { + ClarityValue, + BooleanCV, + IntCV, + UIntCV, + BufferCV, + OptionalCV, + ResponseCV, + PrincipalCV, + ListCV, + TupleCV, + StringAsciiCV, + StringUtf8CV, + NoneCV, + StandardPrincipalCV, +} from '@stacks/transactions'; + +export namespace mainContract { + export const address = 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM'; + export const name = 'stacking-pool-test'; + + // Functions + export namespace Functions { + // delegate-stack-stx + export namespace DelegateStackStx { + export const name = 'delegate-stack-stx'; + + export interface DelegateStackStxArgs { + amountUstx: UIntCV; + user: PrincipalCV; + } + + export function args(args: DelegateStackStxArgs): ClarityValue[] { + return [args.amountUstx, args.user]; + } + } + + export namespace DelegateStackStxMany { + export const name = 'delegate-stack-stx-many'; + + export interface DelegateStackStxArgs { + stackersLockList: ListCV; + } + + export function args(args: DelegateStackStxArgs): ClarityValue[] { + return [args.stackersLockList]; + } + } + + export namespace DepositStxOwner { + export const name = 'deposit-stx-liquidity-provider'; + + export interface DepositStxOwnerArgs { + amountUstx: UIntCV; + } + + export function args(args: DepositStxOwnerArgs): ClarityValue[] { + return [args.amountUstx]; + } + } + + export namespace ReserveFundsLiqProvider { + export const name = 'reserve-funds-future-rewards'; + + export interface ReserveFundsLiqProviderArgs { + amountUstx: UIntCV; + } + + export function args(args: ReserveFundsLiqProviderArgs): ClarityValue[] { + return [args.amountUstx]; + } + } + + export namespace JoinStackingPool { + export const name = 'join-stacking-pool'; + + export interface joinStackingPoolArgs {} + + export function args(args: joinStackingPoolArgs): ClarityValue[] { + return []; + } + } + + export namespace UpdateScBalances { + export const name = 'update-sc-balances'; + + export interface DelegateStxArgs {} + + export function args(args: DelegateStxArgs): ClarityValue[] { + return []; + } + } + + export namespace DelegateStx { + export const name = 'delegate-stx'; + + export interface DelegateStxArgs { + amountUstx: UIntCV; + } + + export function args(args: DelegateStxArgs): ClarityValue[] { + return [args.amountUstx]; + } + } + + export namespace RewardDistribution { + export const name = 'reward-distribution'; + + export interface RewardDistributionArgs { + burnBlockHeight: UIntCV; + } + + export function args(args: RewardDistributionArgs): ClarityValue[] { + return [args.burnBlockHeight]; + } + } + + // get-first-result + export namespace GetFirstResult { + export const name = 'get-first-result'; + + export interface GetFirstResultArgs { + results: ClarityValue; + } + + export function args(args: GetFirstResultArgs): ClarityValue[] { + return [args.results]; + } + } + } +} + +export namespace poxPoolsSelfServiceContract { + export const address = 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM'; + export const name = 'pox-pool-self-service'; + + // Functions + export namespace Functions { + // delegate-stack-stx + export namespace DelegateStackStx { + export const name = 'delegate-stack-stx'; + + export interface DelegateStackStxArgs { + amountUstx: UIntCV; + user: PrincipalCV; + } + + export function args(args: DelegateStackStxArgs): ClarityValue[] { + return [args.amountUstx, args.user]; + } + } + + // delegate-stx + export namespace DelegateStx { + export const name = 'delegate-stx'; + + export interface DelegateStxArgs { + amountUstx: UIntCV; + } + + export function args(args: DelegateStxArgs): ClarityValue[] { + return [args.amountUstx]; + } + } + + // get-first-result + export namespace GetFirstResult { + export const name = 'get-first-result'; + + export interface GetFirstResultArgs { + results: ClarityValue; + } + + export function args(args: GetFirstResultArgs): ClarityValue[] { + return [args.results]; + } + } + } +} + +export namespace HelperContract { + export const address = 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM'; + export const name = 'helper'; + + // Functions + export namespace Functions { + // get-stx-account + export namespace GetStxAccount { + export const name = 'get-stx-account'; + + export interface GetStxAccountArgs { + user: PrincipalCV; + } + + export function args(args: GetStxAccountArgs): ClarityValue[] { + return [args.user]; + } + } + + // get-user-data + export namespace GetUserData { + export const name = 'get-user-data'; + + export interface GetUserDataArgs { + user: PrincipalCV; + } + + export function args(args: GetUserDataArgs): ClarityValue[] { + return [args.user]; + } + } + } +} + +export namespace poxPools1CycleContract { + export const address = 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM'; + export const name = 'pox-pools-1-cycle'; + + // Functions + export namespace Functions { + // delegate-stack-stx + export namespace DelegateStackStx { + export const name = 'delegate-stack-stx'; + + export interface DelegateStackStxArgs { + users: ListCV; + poxAddress: TupleCV; + startBurnHt: UIntCV; + lockPeriod: UIntCV; + } + + export function args(args: DelegateStackStxArgs): ClarityValue[] { + return [args.users, args.poxAddress, args.startBurnHt, args.lockPeriod]; + } + } + + // delegate-stx + export namespace DelegateStx { + export const name = 'delegate-stx'; + + export interface DelegateStxArgs { + amountUstx: UIntCV; + delegateTo: StandardPrincipalCV; + untilBurnHt: NoneCV; + poolPoxAddr: NoneCV; + userPoxAddr: TupleCV; + lockPeriod: UIntCV; + } + + export function args(args: DelegateStxArgs): ClarityValue[] { + return [ + args.amountUstx, + args.delegateTo, + args.untilBurnHt, + args.poolPoxAddr, + args.userPoxAddr, + args.lockPeriod, + ]; + } + } + + // as-response-with-uint + export namespace AsResponseWithUint { + export const name = 'as-response-with-uint'; + + export interface AsResponseWithUintArgs { + result: ClarityValue; + } + + export function args(args: AsResponseWithUintArgs): ClarityValue[] { + return [args.result]; + } + } + + // get-status + export namespace GetStatus { + export const name = 'get-status'; + + export interface GetStatusArgs { + pool: PrincipalCV; + user: PrincipalCV; + } + + export function args(args: GetStatusArgs): ClarityValue[] { + return [args.pool, args.user]; + } + } + + // get-status-list + export namespace GetStatusList { + export const name = 'get-status-list'; + + export interface GetStatusListArgs { + pool: PrincipalCV; + rewardCycle: UIntCV; + lockPeriod: UIntCV; + index: UIntCV; + } + + export function args(args: GetStatusListArgs): ClarityValue[] { + return [args.pool, args.rewardCycle, args.lockPeriod, args.index]; + } + } + + // get-status-lists-last-index + export namespace GetStatusListsLastIndex { + export const name = 'get-status-lists-last-index'; + + export interface GetStatusListsLastIndexArgs { + pool: PrincipalCV; + rewardCycle: UIntCV; + lockPeriod: UIntCV; + } + + export function args(args: GetStatusListsLastIndexArgs): ClarityValue[] { + return [args.pool, args.rewardCycle, args.lockPeriod]; + } + } + + // get-stx-account + export namespace GetStxAccount { + export const name = 'get-stx-account'; + + export interface GetStxAccountArgs { + user: PrincipalCV; + } + + export function args(args: GetStxAccountArgs): ClarityValue[] { + return [args.user]; + } + } + + // get-total + export namespace GetTotal { + export const name = 'get-total'; + + export interface GetTotalArgs { + pool: PrincipalCV; + rewardCycle: UIntCV; + lockPeriod: UIntCV; + } + + export function args(args: GetTotalArgs): ClarityValue[] { + return [args.pool, args.rewardCycle, args.lockPeriod]; + } + } + + // get-user-data + export namespace GetUserData { + export const name = 'get-user-data'; + + export interface GetUserDataArgs { + user: PrincipalCV; + } + + export function args(args: GetUserDataArgs): ClarityValue[] { + return [args.user]; + } + } + } +} diff --git a/sbtc-ops/smart-contract/integration/functional.ts b/sbtc-ops/smart-contract/integration/functional.ts new file mode 100644 index 00000000..22b5a253 --- /dev/null +++ b/sbtc-ops/smart-contract/integration/functional.ts @@ -0,0 +1,8 @@ +import { buildDevnetNetworkOrchestrator, getNetworkIdFromEnv } from './helpers-stacking'; + +export const startOrchestrator = () => { + let orchestrator = buildDevnetNetworkOrchestrator(getNetworkIdFromEnv()); + console.log('ORCHESTRATOR', orchestrator); + // orchestrator.start(120000); + return orchestrator; +}; diff --git a/sbtc-ops/smart-contract/integration/helper-fp.ts b/sbtc-ops/smart-contract/integration/helper-fp.ts new file mode 100644 index 00000000..aa09f639 --- /dev/null +++ b/sbtc-ops/smart-contract/integration/helper-fp.ts @@ -0,0 +1,294 @@ +import { + AnchorMode, + broadcastTransaction, + bufferCV, + ClarityValue, + listCV, + noneCV, + PostConditionMode, + principalCV, + standardPrincipalCV, + tupleCV, + uintCV, + makeContractCall, + TxBroadcastResult, +} from '@stacks/transactions'; +import { StacksNetwork } from '@stacks/network'; +// import { Accounts } from './constants'; +import { HelperContract, mainContract, poxPoolsSelfServiceContract } from './contracts'; +import { decodeBtcAddress } from '@stacks/stacking'; +import { toBytes } from '@stacks/common'; +import { handleContractCall } from './helpers-stacking'; +import { Accounts } from './constants-stacking'; + +interface Account { + stxAddress: string; + btcAddress: string; + secretKey: string; +} + +export async function broadcastJoinPool({ + nonce, + network, + user, +}: { + nonce: number; + network: StacksNetwork; + user: { stxAddress: string; secretKey: string }; +}) { + let txOptions = { + contractAddress: mainContract.address, + contractName: mainContract.name, + functionName: mainContract.Functions.JoinStackingPool.name, + functionArgs: mainContract.Functions.JoinStackingPool.args({}), + nonce, + network, + anchorMode: AnchorMode.OnChainOnly, + postConditionMode: PostConditionMode.Allow, + senderKey: user.secretKey, + }; + return handleContractCall({ txOptions, network }); +} + +export async function broadcastDelegateStx({ + amountUstx, + nonce, + network, + user, +}: { + amountUstx: number; + nonce: number; + network: StacksNetwork; + user: { stxAddress: string; secretKey: string }; +}) { + let txOptions = { + contractAddress: mainContract.address, + contractName: mainContract.name, + functionName: mainContract.Functions.DelegateStx.name, + functionArgs: mainContract.Functions.DelegateStx.args({ + amountUstx: uintCV(amountUstx), + }), + nonce, + network, + anchorMode: AnchorMode.OnChainOnly, + postConditionMode: PostConditionMode.Allow, + senderKey: user.secretKey, + }; + return handleContractCall({ txOptions, network }); +} + +export const broadcastDelegateStackStx = async ( + user: string, + network: StacksNetwork, + account: Account, + fee: number, + nonce: number +): Promise => { + const txOptions = { + contractAddress: Accounts.DEPLOYER.stxAddress, + contractName: mainContract.name, + functionName: 'delegate-stack-stx', + functionArgs: [principalCV(user)], + fee, + nonce, + network, + anchorMode: AnchorMode.OnChainOnly, + postConditionMode: PostConditionMode.Allow, + senderKey: account.secretKey, + }; + // @ts-ignore + const tx = await makeContractCall(txOptions); + // Broadcast transaction to our Devnet stacks node + const result = await broadcastTransaction(tx, network); + return result; +}; + +export async function broadcastUpdateScBalances({ + nonce, + network, + user, +}: { + nonce: number; + network: StacksNetwork; + user: { stxAddress: string; secretKey: string }; +}) { + let txOptions = { + contractAddress: mainContract.address, + contractName: mainContract.name, + functionName: mainContract.Functions.UpdateScBalances.name, + functionArgs: mainContract.Functions.UpdateScBalances.args({}), + nonce, + network, + anchorMode: AnchorMode.OnChainOnly, + postConditionMode: PostConditionMode.Allow, + senderKey: user.secretKey, + }; + return handleContractCall({ txOptions, network }); +} + +export async function broadcastRewardDistribution({ + burnBlockHeight, + nonce, + network, + user, +}: { + burnBlockHeight: number; + nonce: number; + network: StacksNetwork; + user: { stxAddress: string; secretKey: string }; +}) { + let txOptions = { + contractAddress: mainContract.address, + contractName: mainContract.name, + functionName: mainContract.Functions.RewardDistribution.name, + functionArgs: mainContract.Functions.RewardDistribution.args({ + burnBlockHeight: uintCV(burnBlockHeight), + }), + nonce, + network, + anchorMode: AnchorMode.OnChainOnly, + postConditionMode: PostConditionMode.Allow, + senderKey: user.secretKey, + }; + return handleContractCall({ txOptions, network }); +} + +// export async function broadcastDelegateStackStx({ +// stacker, +// amountUstx, +// user, +// nonce, +// network, +// }: { +// stacker: { stxAddress: string; secretKey: string }; +// amountUstx: number; +// user: { stxAddress: string; secretKey: string }; +// nonce: number; +// network: StacksNetwork; +// }) { +// let txOptions = { +// contractAddress: poxPoolsSelfServiceContract.address, +// contractName: poxPoolsSelfServiceContract.name, +// functionName: poxPoolsSelfServiceContract.Functions.DelegateStackStx.name, +// functionArgs: poxPoolsSelfServiceContract.Functions.DelegateStackStx.args({ +// user: principalCV(stacker.stxAddress), +// amountUstx: uintCV(amountUstx), +// }), +// nonce, +// network, +// anchorMode: AnchorMode.OnChainOnly, +// postConditionMode: PostConditionMode.Allow, +// senderKey: user.secretKey, +// }; +// return handleContractCall({ txOptions, network }); +// } + +// export const broadcastDepositStxOwner = async ( +// amountUstx: number, +// network: StacksNetwork, +// account: Account, +// fee: number, +// nonce: number +// ): Promise => { +// const txOptions = { +// contractAddress: Accounts.DEPLOYER.stxAddress, +// contractName: mainContract.name, +// functionName: 'deposit-stx-liquidity-provider', +// functionArgs: [uintCV(amountUstx)], +// fee, +// nonce, +// network, +// anchorMode: AnchorMode.OnChainOnly, +// postConditionMode: PostConditionMode.Allow, +// senderKey: account.secretKey, +// }; +// // @ts-ignore +// const tx = await makeContractCall(txOptions); +// // Broadcast transaction to our Devnet stacks node +// const result = await broadcastTransaction(tx, network); +// return result; +// }; + +export async function broadcastDepositStxOwner({ + amountUstx, + nonce, + network, + user, +}: { + amountUstx: number; + nonce: number; + network: StacksNetwork; + user: { stxAddress: string; secretKey: string }; +}) { + let txOptions = { + contractAddress: mainContract.address, + contractName: mainContract.name, + functionName: mainContract.Functions.DepositStxOwner.name, + functionArgs: mainContract.Functions.DepositStxOwner.args({ + amountUstx: uintCV(amountUstx), + }), + nonce, + network, + anchorMode: AnchorMode.OnChainOnly, + postConditionMode: PostConditionMode.Allow, + senderKey: user.secretKey, + }; + return handleContractCall({ txOptions, network }); +} + +export async function broadcastDelegateStackStxMany({ + stackersLockList, + nonce, + network, + user, +}: { + stackersLockList: [string]; + nonce: number; + network: StacksNetwork; + user: { stxAddress: string; secretKey: string }; +}) { + let convertedList = []; + stackersLockList.forEach((stacker) => convertedList.push(standardPrincipalCV(stacker))); + + let txOptions = { + contractAddress: mainContract.address, + contractName: mainContract.name, + functionName: mainContract.Functions.DelegateStackStxMany.name, + functionArgs: mainContract.Functions.DelegateStackStxMany.args({ + stackersLockList: listCV(convertedList), + }), + nonce, + network, + anchorMode: AnchorMode.OnChainOnly, + postConditionMode: PostConditionMode.Allow, + senderKey: user.secretKey, + }; + return handleContractCall({ txOptions, network }); +} + +export async function broadcastReserveStxOwner({ + amountUstx, + nonce, + network, + user, +}: { + amountUstx: number; + nonce: number; + network: StacksNetwork; + user: { stxAddress: string; secretKey: string }; +}) { + let txOptions = { + contractAddress: mainContract.address, + contractName: mainContract.name, + functionName: mainContract.Functions.ReserveFundsLiqProvider.name, + functionArgs: mainContract.Functions.ReserveFundsLiqProvider.args({ + amountUstx: uintCV(amountUstx), + }), + nonce, + network, + anchorMode: AnchorMode.OnChainOnly, + postConditionMode: PostConditionMode.Allow, + senderKey: user.secretKey, + }; + return handleContractCall({ txOptions, network }); +} diff --git a/sbtc-ops/smart-contract/integration/helpers-stacking.ts b/sbtc-ops/smart-contract/integration/helpers-stacking.ts new file mode 100644 index 00000000..d3011b46 --- /dev/null +++ b/sbtc-ops/smart-contract/integration/helpers-stacking.ts @@ -0,0 +1,624 @@ +import { Accounts, Contracts } from './constants-stacking'; +import { + StacksBlockMetadata, + StacksChainUpdate, + DevnetNetworkOrchestrator, + StacksTransactionMetadata, + getIsolatedNetworkConfigUsingNetworkId, +} from '@hirosystems/stacks-devnet-js'; +import { StacksNetwork, StacksTestnet } from '@stacks/network'; +import { + AnchorMode, + broadcastTransaction, + bufferCV, + contractPrincipalCV, + falseCV, + getNonce, + makeContractCall, + PostConditionMode, + stringAsciiCV, + trueCV, + tupleCV, + TxBroadcastResult, + uintCV, + callReadOnlyFunction, + cvToJSON, + principalCV, + cvToHex, + hexToCV, + ClarityType, + SomeCV, + TupleCV, + OptionalCV, + PrincipalCV, + UIntCV, + cvToString, + ClarityValue, +} from '@stacks/transactions'; +import { Constants } from './constants-stacking'; + +import { decodeBtcAddress } from '@stacks/stacking'; +import { toBytes } from '@stacks/common'; +import { expect } from 'vitest'; +import { mainContract } from './contracts'; +const fetch = require('node-fetch'); + +interface Account { + stxAddress: string; + btcAddress: string; + secretKey: string; +} + +interface EpochTimeline { + epoch_2_0: number; + epoch_2_05: number; + epoch_2_1: number; + pox_2_activation: number; + epoch_2_2: number; + epoch_2_3: number; + epoch_2_4: number; +} + +export const DEFAULT_EPOCH_TIMELINE = { + epoch_2_0: Constants.DEVNET_DEFAULT_EPOCH_2_0, + epoch_2_05: Constants.DEVNET_DEFAULT_EPOCH_2_05, + epoch_2_1: Constants.DEVNET_DEFAULT_EPOCH_2_1, + pox_2_activation: Constants.DEVNET_DEFAULT_POX_2_ACTIVATION, + epoch_2_2: 106, + epoch_2_3: 108, + epoch_2_4: 112, +}; + +export const FAST_FORWARD_TO_EPOCH_2_4 = { + epoch_2_0: 100, + epoch_2_05: 102, + epoch_2_1: 104, + pox_2_activation: 105, + epoch_2_2: 106, + epoch_2_3: 108, + epoch_2_4: 112, +}; + +const delay = () => new Promise((resolve) => setTimeout(resolve, 3000)); + +export function buildDevnetNetworkOrchestrator( + networkId: number, + timeline: EpochTimeline = DEFAULT_EPOCH_TIMELINE, + logs = true +) { + let uuid = Date.now(); + let working_dir = `/tmp/stacks-test-${uuid}-${networkId}`; + let config = { + logs, + devnet: { + name: `ephemeral-devnet-${uuid}`, + bitcoin_controller_block_time: Constants.BITCOIN_BLOCK_TIME, + bitcoin_controller_automining_disabled: false, + working_dir, + use_docker_gateway_routing: process.env.GITHUB_ACTIONS ? true : false, + stacks_api_url: 'hirosystems/stacks-blockchain-api:latest', + stacks_explorer_url: 'hirosystems/explorer:latest', + }, + }; + let consolidatedConfig = getIsolatedNetworkConfigUsingNetworkId(networkId, config); + console.log('isolated Config!', consolidatedConfig); + let orchestrator = new DevnetNetworkOrchestrator(consolidatedConfig, 2500); + return orchestrator; +} + +export const getBitcoinBlockHeight = (chainUpdate: StacksChainUpdate): number => { + let metadata = chainUpdate.new_blocks[0].block.metadata! as StacksBlockMetadata; + return metadata.bitcoin_anchor_block_identifier.index; +}; + +export const waitForStacksTransaction = async ( + orchestrator: DevnetNetworkOrchestrator, + txid: string +): Promise<[StacksBlockMetadata, StacksTransactionMetadata]> => { + let { chainUpdate, transaction } = await orchestrator.waitForStacksBlockIncludingTransaction(txid); + return [ + chainUpdate.new_blocks[0].block.metadata, + transaction.metadata, + ]; +}; + +export const getNetworkIdFromEnv = (): number => { + let networkId = process.env.JEST_WORKER_ID + ? parseInt(process.env.JEST_WORKER_ID!) + : process.env.VITEST_WORKER_ID + ? parseInt(process.env.VITEST_WORKER_ID!) + : 1; + return networkId; +}; + +export async function asyncExpectCurrentCycleIdToBe(cycleId: number, network: StacksTestnet) { + let poxInfo = await getPoxInfo(network); + console.log('PoxInfo', poxInfo); + expect(poxInfo.current_cycle.id).toBe(cycleId); +} + +export const getChainInfo = async (network: StacksNetwork, retry?: number): Promise => { + let retryCountdown = retry ? retry : 20; + if (retryCountdown == 0) return Promise.reject(); + try { + let response = await fetch(network.getInfoUrl(), {}); + let info = await response.json(); + return info; + } catch (e) { + await delay(); + return await getChainInfo(network, retryCountdown - 1); + } +}; + +export const getPoxInfo = async (network: StacksNetwork, retry?: number): Promise => { + let retryCountdown = retry ? retry : 20; + if (retryCountdown == 0) return Promise.reject(); + try { + let response = await fetch(network.getPoxInfoUrl(), {}); + let poxInfo = await response.json(); + return poxInfo; + } catch (e) { + await delay(); + return await getPoxInfo(network, retryCountdown - 1); + } +}; + +export const getAccount = async (network: StacksNetwork, address: string, retry?: number): Promise => { + let retryCountdown = retry ? retry : 20; + if (retryCountdown == 0) return Promise.reject(); + try { + let response = await fetch(network.getAccountApiUrl(address), {}); + let payload: any = await response.json(); + return { + balance: BigInt(payload.balance), + locked: BigInt(payload.locked), + unlock_height: payload.unlock_height, + nonce: payload.nonce, + }; + } catch (e) { + await delay(); + return await getAccount(network, address, retryCountdown - 1); + } +}; + +export const getBitcoinHeightOfNextRewardPhase = async (network: StacksNetwork, retry?: number): Promise => { + let response = await getPoxInfo(network, retry); + return response.next_cycle.reward_phase_start_block_height; +}; + +export const getBitcoinHeightOfNextPreparePhase = async (network: StacksNetwork, retry?: number): Promise => { + let response = await getPoxInfo(network, retry); + return response.next_cycle.prepare_phase_start_block_height; +}; + +export const waitForNextPreparePhase = async ( + network: StacksNetwork, + orchestrator: DevnetNetworkOrchestrator, + offset?: number +): Promise => { + var height = await getBitcoinHeightOfNextPreparePhase(network); + if (offset) { + height = height + offset; + } + return await orchestrator.waitForStacksBlockAnchoredOnBitcoinBlockOfHeight(height); +}; + +export const waitForRewardCycleId = async ( + network: StacksNetwork, + orchestrator: DevnetNetworkOrchestrator, + id: number, + offset?: number +): Promise => { + let response = await getPoxInfo(network); + let height = response.first_burnchain_block_height + id * response.reward_cycle_length; + if (offset) { + height = height + offset; + } + return await orchestrator.waitForStacksBlockAnchoredOnBitcoinBlockOfHeight(height); +}; + +export const waitForStacks24 = async (orchestrator, timeline) => { + // Wait for 2.4 to go live + await orchestrator.waitForStacksBlockAnchoredOnBitcoinBlockOfHeight(timeline.epoch_2_4); +}; + +export const waitForNextRewardPhase = async ( + network: StacksNetwork, + orchestrator: DevnetNetworkOrchestrator, + offset?: number +): Promise => { + var height = await getBitcoinHeightOfNextRewardPhase(network); + if (offset) { + height = height + offset; + } + return await orchestrator.waitForStacksBlockAnchoredOnBitcoinBlockOfHeight(height); +}; + +export const expectAccountToBe = async (network: StacksNetwork, address: string, account: number, locked: number) => { + let wallet = await getAccount(network, address); + expect(wallet.balance).toBe(BigInt(account)); + expect(wallet.locked).toBe(BigInt(locked)); +}; + +export async function handleContractCall({ txOptions, network }: { txOptions: any; network: StacksNetwork }) { + // @ts-ignore + const tx = await makeContractCall(txOptions); + // Broadcast transaction to our Devnet stacks node + const response = await broadcastTransaction(tx, network); + expect(response.error, 'tx failed\n' + response.reason + ' ' + JSON.stringify(response.reason_data)).toBeUndefined(); + return response; +} + +export const broadcastStackSTX = async ( + poxVersion: number, + network: StacksNetwork, + amount: number, + account: Account, + blockHeight: number, + cycles: number, + fee: number, + nonce: number +): Promise => { + const { version, data } = decodeBtcAddress(account.btcAddress); + // @ts-ignore + const address = { + version: bufferCV(toBytes(new Uint8Array([version.valueOf()]))), + hashbytes: bufferCV(data), + }; + + const txOptions = { + contractAddress: Contracts.POX_1.address, + contractName: poxVersion == 1 ? Contracts.POX_1.name : Contracts.POX_2.name, + functionName: 'stack-stx', + functionArgs: [uintCV(amount), tupleCV(address), uintCV(blockHeight), uintCV(cycles)], + fee, + nonce, + network, + anchorMode: AnchorMode.OnChainOnly, + postConditionMode: PostConditionMode.Allow, + senderKey: account.secretKey, + }; + // @ts-ignore + const tx = await makeContractCall(txOptions); + // Broadcast transaction to our Devnet stacks node + const response = await broadcastTransaction(tx, network); + return response; +}; + +export const createVault = async ( + collateralAmount: number, + usda: number, + network: StacksNetwork, + account: Account, + fee: number, + nonce: number +): Promise => { + const txOptions = { + contractAddress: Accounts.DEPLOYER.stxAddress, + contractName: 'arkadiko-freddie-v1-1', + functionName: 'collateralize-and-mint', + functionArgs: [ + uintCV(collateralAmount * 1000000), + uintCV(usda * 1000000), + tupleCV({ + 'stack-pox': trueCV(), + 'auto-payoff': falseCV(), + }), + stringAsciiCV('STX-A'), + contractPrincipalCV(Accounts.DEPLOYER.stxAddress, 'arkadiko-stx-reserve-v1-1'), + contractPrincipalCV(Accounts.DEPLOYER.stxAddress, 'arkadiko-token'), + contractPrincipalCV(Accounts.DEPLOYER.stxAddress, 'arkadiko-collateral-types-v3-1'), + contractPrincipalCV(Accounts.DEPLOYER.stxAddress, 'arkadiko-oracle-v1-1'), + ], + fee, + nonce, + network, + anchorMode: AnchorMode.OnChainOnly, + postConditionMode: PostConditionMode.Allow, + senderKey: account.secretKey, + }; + // @ts-ignore + const tx = await makeContractCall(txOptions); + // Broadcast transaction to our Devnet stacks node + const result = await broadcastTransaction(tx, network); + return result; +}; + +export const initiateStacking = async ( + network: StacksNetwork, + account: Account, + blockHeight: number, + cycles: number, + fee: number, + nonce: number +): Promise => { + const { version, data } = decodeBtcAddress(account.btcAddress); + // @ts-ignore + const address = { + version: bufferCV(toBytes(new Uint8Array([version.valueOf()]))), + hashbytes: bufferCV(data), + }; + + const txOptions = { + contractAddress: Accounts.DEPLOYER.stxAddress, + contractName: 'arkadiko-stacker-v2-1', + functionName: 'initiate-stacking', + functionArgs: [tupleCV(address), uintCV(blockHeight), uintCV(cycles)], + fee, + nonce, + network, + anchorMode: AnchorMode.OnChainOnly, + postConditionMode: PostConditionMode.Allow, + senderKey: account.secretKey, + }; + // @ts-ignore + const tx = await makeContractCall(txOptions); + // Broadcast transaction to our Devnet stacks node + const result = await broadcastTransaction(tx, network); + return result; +}; + +export const stackIncrease = async ( + network: StacksNetwork, + account: Account, + stackerName: string, + fee: number, + nonce: number +): Promise => { + const txOptions = { + contractAddress: Accounts.DEPLOYER.stxAddress, + contractName: 'arkadiko-stacker-v2-1', + functionName: 'stack-increase', + functionArgs: [stringAsciiCV(stackerName)], + fee, + nonce, + network, + anchorMode: AnchorMode.OnChainOnly, + postConditionMode: PostConditionMode.Allow, + senderKey: account.secretKey, + }; + // @ts-ignore + const tx = await makeContractCall(txOptions); + // Broadcast transaction to our Devnet stacks node + const result = await broadcastTransaction(tx, network); + return result; +}; + +export const getStackerInfo = async (network: StacksNetwork) => { + const supplyCall = await callReadOnlyFunction({ + contractAddress: Accounts.DEPLOYER.stxAddress, + contractName: 'arkadiko-stacker-v2-1', + functionName: 'get-stacker-info', + functionArgs: [], + senderAddress: Accounts.DEPLOYER.stxAddress, + network: network, + }); + const json = cvToJSON(supplyCall); + console.log('STACKER INFO JSON:', json); + + return json; +}; + +export const getScLockedBalance = async (network: StacksNetwork) => { + const supplyCall = await callReadOnlyFunction({ + contractAddress: Accounts.DEPLOYER.stxAddress, + contractName: 'stacking-pool-test', + functionName: 'get-SC-locked-balance', + functionArgs: [], + senderAddress: Accounts.DEPLOYER.stxAddress, + network: network, + }); + const json = cvToJSON(supplyCall); + console.log('SC Locked Balance:', json); + + return json; +}; + +export const getStackerWeight = async (network: StacksNetwork, stacker: string, rewardCycle: number) => { + const supplyCall = await callReadOnlyFunction({ + contractAddress: mainContract.address, + contractName: 'stacking-pool-test', + functionName: 'get-stacker-weight', + functionArgs: [principalCV(stacker), uintCV(rewardCycle)], + senderAddress: mainContract.address, + network: network, + }); + const json = cvToJSON(supplyCall); + console.log(`Stacker ${stacker} weight:`, json.value ? json.value.value : json); + + return json.value ? json.value.value : json; +}; + +export const getBlockPoxAddresses = async (network: StacksNetwork, stacker: string, burnHeight: number) => { + const supplyCall = await callReadOnlyFunction({ + contractAddress: Accounts.DEPLOYER.stxAddress, + contractName: 'stacking-pool-test', + functionName: 'get-block-rewards', + functionArgs: [uintCV(burnHeight)], + senderAddress: Accounts.DEPLOYER.stxAddress, + network: network, + }); + const json = cvToJSON(supplyCall); + if (json.value.value) { + console.log( + `Block ${burnHeight} pox addr 1`, + json.value.value.value.addrs.value[0] //.value.hashbytes + ); + console.log( + `Block ${burnHeight} pox addr 2`, + json.value.value.value.addrs.value[1] //.value.hashbytes + ); + } + + return json; +}; + +export const getBlockRewards = async (network: StacksNetwork, stacker: string, burnHeight: number) => { + const supplyCall = await callReadOnlyFunction({ + contractAddress: Accounts.DEPLOYER.stxAddress, + contractName: 'stacking-pool-test', + functionName: 'get-block-rewards', + functionArgs: [uintCV(burnHeight)], + senderAddress: Accounts.DEPLOYER.stxAddress, + network: network, + }); + const json = cvToJSON(supplyCall); + console.log(`Block ${burnHeight} rewards`, json.value.value); + + return json; +}; + +export const getPoolMembers = async (network: StacksNetwork) => { + const supplyCall = await callReadOnlyFunction({ + contractAddress: Accounts.DEPLOYER.stxAddress, + contractName: 'stacking-pool-test', + functionName: 'get-pool-members', + functionArgs: [], + senderAddress: Accounts.DEPLOYER.stxAddress, + network: network, + }); + const json = cvToJSON(supplyCall); + console.log(`Stackers list:`, json); + + return json; +}; + +export const getCheckDelegation = async (network: StacksNetwork, stacker: string) => { + const supplyCall = await callReadOnlyFunction({ + contractAddress: 'ST000000000000000000002AMW42H', + contractName: 'pox-3', + functionName: 'get-check-delegation', + functionArgs: [principalCV(stacker)], + senderAddress: Accounts.DEPLOYER.stxAddress, + network: network, + }); + const json = cvToJSON(supplyCall); + // console.log('Stacker delegation json', json); + // if (json.value) console.log(`Stacker delegation info:`, json.value.value); + + return json; +}; + +export const readRewardCyclePoxAddressList = async (network: StacksNetwork, cycleId: number) => { + const url = network.getMapEntryUrl('ST000000000000000000002AMW42H', 'pox-3', 'reward-cycle-pox-address-list-len'); + const cycleIdValue = uintCV(cycleId); + const keyValue = tupleCV({ + 'reward-cycle': cycleIdValue, + }); + const response = await network.fetchFn(url, { + method: 'POST', + body: JSON.stringify(cvToHex(keyValue)), + headers: { + 'Content-Type': 'application/json', + }, + }); + if (!response.ok) { + const msg = await response.text().catch(() => ''); + throw new Error( + `Error calling read-only function. Response ${response.status}: ${response.statusText}. Attempted to fetch ${url} and failed with the message: "${msg}"` + ); + } + let lengthJson = await response.json(); + let lengthSome = hexToCV(lengthJson.data) as OptionalCV; + if (lengthSome.type === ClarityType.OptionalNone) { + return null; + } + let lengthUint = lengthSome.value.data['len'] as UIntCV; + let length = Number(lengthUint.value); + + let poxAddrInfoList = []; + for (let i = 0; i < length; i++) { + let poxAddressInfo = (await readRewardCyclePoxAddressListAtIndex(network, cycleId, i)) as Record< + string, + ClarityValue + >; + poxAddrInfoList.push(poxAddressInfo); + } + + return poxAddrInfoList; +}; + +export const readRewardCyclePoxAddressForAddress = async (network: StacksNetwork, cycleId: number, address: string) => { + // TODO: There might be a better way to do this using the `stacking-state` + // map to get the index + const url = network.getMapEntryUrl('ST000000000000000000002AMW42H', 'pox-3', 'reward-cycle-pox-address-list-len'); + const cycleIdValue = uintCV(cycleId); + const keyValue = tupleCV({ + 'reward-cycle': cycleIdValue, + }); + const response = await network.fetchFn(url, { + method: 'POST', + body: JSON.stringify(cvToHex(keyValue)), + headers: { + 'Content-Type': 'application/json', + }, + }); + if (!response.ok) { + const msg = await response.text().catch(() => ''); + throw new Error( + `Error calling read-only function. Response ${response.status}: ${response.statusText}. Attempted to fetch ${url} and failed with the message: "${msg}"` + ); + } + let lengthJson = await response.json(); + let lengthSome = hexToCV(lengthJson.data) as OptionalCV; + if (lengthSome.type === ClarityType.OptionalNone) { + return null; + } + let lengthUint = lengthSome.value.data['len'] as UIntCV; + let length = Number(lengthUint.value); + + for (let i = 0; i < length; i++) { + let poxAddressInfo = await readRewardCyclePoxAddressListAtIndex(network, cycleId, i); + if (poxAddressInfo?.['stacker']?.type === ClarityType.OptionalNone) { + continue; + } else if (poxAddressInfo?.['stacker']?.type === ClarityType.OptionalSome) { + let stackerSome = poxAddressInfo['stacker'] as SomeCV; + if (cvToString(stackerSome.value) === address) { + return poxAddressInfo; + } + } + } + + return null; +}; + +export type RewardCyclePoxAddressMapEntry = { + 'total-ustx': UIntCV; + stacker: OptionalCV; +}; + +export const readRewardCyclePoxAddressListAtIndex = async ( + network: StacksNetwork, + cycleId: number, + index: number +): Promise => { + const url = network.getMapEntryUrl('ST000000000000000000002AMW42H', 'pox-3', 'reward-cycle-pox-address-list'); + const cycleIdValue = uintCV(cycleId); + const indexValue = uintCV(index); + const keyValue = tupleCV({ + 'reward-cycle': cycleIdValue, + index: indexValue, + }); + const response = await network.fetchFn(url, { + method: 'POST', + body: JSON.stringify(cvToHex(keyValue)), + headers: { + 'Content-Type': 'application/json', + }, + }); + if (!response.ok) { + const msg = await response.text().catch(() => ''); + throw new Error( + `Error calling read-only function. Response ${response.status}: ${response.statusText}. Attempted to fetch ${url} and failed with the message: "${msg}"` + ); + } + let poxAddrInfoJson = await response.json(); + let cv = hexToCV(poxAddrInfoJson.data); + if (cv.type === ClarityType.OptionalSome) { + let someCV = cv as SomeCV; + const tupleData = someCV.value.data as RewardCyclePoxAddressMapEntry; + return tupleData; + } else if (cv.type === ClarityType.OptionalNone) { + return null; + } +}; diff --git a/sbtc-ops/smart-contract/integration/helpers.ts b/sbtc-ops/smart-contract/integration/helpers.ts new file mode 100644 index 00000000..0a87dbde --- /dev/null +++ b/sbtc-ops/smart-contract/integration/helpers.ts @@ -0,0 +1,309 @@ +import { Accounts, Contracts } from './constants'; +import { + StacksBlockMetadata, + StacksChainUpdate, + DevnetNetworkOrchestrator, + StacksTransactionMetadata, + getIsolatedNetworkConfigUsingNetworkId, +} from '@hirosystems/stacks-devnet-js'; +import { StacksNetwork } from '@stacks/network'; +import { + AnchorMode, + broadcastTransaction, + makeContractCall, + PostConditionMode, + TxBroadcastResult, + uintCV, + callReadOnlyFunction, + cvToJSON, + principalCV, + standardPrincipalCV, + bufferCV, + tupleCV, +} from '@stacks/transactions'; +import { Constants } from './constants'; +const fetch = require('node-fetch'); + +interface Account { + stxAddress: string; + btcAddress: string; + secretKey: string; +} + +interface EpochTimeline { + epoch_2_0: number; + epoch_2_05: number; + epoch_2_1: number; + pox_2_activation: number; +} + +export const DEFAULT_EPOCH_TIMELINE = { + epoch_2_0: Constants.DEVNET_DEFAULT_EPOCH_2_0, + epoch_2_05: Constants.DEVNET_DEFAULT_EPOCH_2_05, + epoch_2_1: Constants.DEVNET_DEFAULT_EPOCH_2_1, + pox_2_activation: Constants.DEVNET_DEFAULT_POX_2_ACTIVATION, +}; + +export const delay = () => new Promise((resolve) => setTimeout(resolve, 3000)); + +export function buildDevnetNetworkOrchestrator( + networkId: number, + timeline: EpochTimeline = DEFAULT_EPOCH_TIMELINE, + logs = true +) { + let uuid = Date.now(); + let working_dir = `/tmp/stacks-test-${uuid}-${networkId}`; + let config = { + logs, + devnet: { + name: `ephemeral-devnet-${uuid}`, + bitcoin_controller_block_time: Constants.BITCOIN_BLOCK_TIME, + bitcoin_controller_automining_disabled: false, + working_dir, + use_docker_gateway_routing: process.env.GITHUB_ACTIONS ? true : false, + }, + }; + let consolidatedConfig = getIsolatedNetworkConfigUsingNetworkId(networkId, config); + let orchestrator = new DevnetNetworkOrchestrator(consolidatedConfig, 2500); + return orchestrator; +} + +export const getBitcoinBlockHeight = (chainUpdate: StacksChainUpdate): number => { + let metadata = chainUpdate.new_blocks[0].block.metadata! as StacksBlockMetadata; + return metadata.bitcoin_anchor_block_identifier.index; +}; + +export const waitForStacksTransaction = async ( + orchestrator: DevnetNetworkOrchestrator, + txid: string +): Promise<[StacksBlockMetadata, StacksTransactionMetadata]> => { + let { chainUpdate, transaction } = await orchestrator.waitForStacksBlockIncludingTransaction(txid); + return [ + chainUpdate.new_blocks[0].block.metadata, + transaction.metadata, + ]; +}; + +export const getNetworkIdFromEnv = (): number => { + let networkId = process.env.JEST_WORKER_ID + ? parseInt(process.env.JEST_WORKER_ID!) + : process.env.VITEST_WORKER_ID + ? parseInt(process.env.VITEST_WORKER_ID!) + : 1; + return networkId; +}; + +export const getChainInfo = async (network: StacksNetwork, retry?: number): Promise => { + let retryCountdown = retry ? retry : 20; + if (retryCountdown == 0) return Promise.reject(); + try { + let response = await fetch(network.getInfoUrl(), {}); + let info = await response.json(); + return info; + } catch (e) { + await delay(); + return await getChainInfo(network, retryCountdown - 1); + } +}; + +export const getAccount = async (network: StacksNetwork, address: string, retry?: number): Promise => { + let retryCountdown = retry ? retry : 20; + if (retryCountdown == 0) return Promise.reject(); + try { + let response = await fetch(network.getAccountApiUrl(address), {}); + let payload: any = await response.json(); + return { + balance: BigInt(payload.balance), + locked: BigInt(payload.locked), + unlock_height: payload.unlock_height, + nonce: payload.nonce, + }; + } catch (e) { + await delay(); + return await getAccount(network, address, retryCountdown - 1); + } +}; + +export const balanceDepositSTX = async ( + amount: number, + network: StacksNetwork, + account: Account, + fee: number, + nonce: number +): Promise => { + const txOptions = { + contractAddress: Accounts.DEPLOYER.stxAddress, + contractName: 'mining-pool-5-blocks', + functionName: 'deposit-stx', + functionArgs: [uintCV(amount * 1000000)], + fee, + nonce, + network, + anchorMode: AnchorMode.OnChainOnly, + postConditionMode: PostConditionMode.Allow, + senderKey: account.secretKey, + }; + // @ts-ignore + const tx = await makeContractCall(txOptions); + // Broadcast transaction to our Devnet stacks node + const result = await broadcastTransaction(tx, network); + return result; +}; + +export const getBalanceSTX = async (network: StacksNetwork, participant: string) => { + const supplyCall = await callReadOnlyFunction({ + contractAddress: Accounts.DEPLOYER.stxAddress, + contractName: 'mining-pool-5-blocks', + functionName: 'get-balance', + functionArgs: [principalCV(participant)], + senderAddress: Accounts.DEPLOYER.stxAddress, + network: network, + }); + const json = cvToJSON(supplyCall); + return json; +}; + +export const getRewardAtBlock = async (network: StacksNetwork, blockHeight: number) => { + const supplyCall = await callReadOnlyFunction({ + contractAddress: Accounts.DEPLOYER.stxAddress, + contractName: 'mining-pool-5-blocks', + functionName: 'get-reward-at-block-read', + functionArgs: [uintCV(blockHeight)], + senderAddress: Accounts.DEPLOYER.stxAddress, + network: network, + }); + const json = cvToJSON(supplyCall); + return json; +}; + +export const askToJoin = async ( + btcAddressVersion: Uint8Array, + btcAddressHashbytesUintArray: Uint8Array, + network: StacksNetwork, + account: Account, + fee: number, + nonce: number +): Promise => { + const txOptions = { + contractAddress: Accounts.DEPLOYER.stxAddress, + contractName: 'mining-pool-5-blocks', + functionName: 'ask-to-join', + functionArgs: [ + tupleCV({ version: bufferCV(btcAddressVersion), hashbytes: bufferCV(btcAddressHashbytesUintArray) }), + ], + fee, + nonce, + network, + anchorMode: AnchorMode.OnChainOnly, + postConditionMode: PostConditionMode.Allow, + senderKey: account.secretKey, + }; + // @ts-ignore + const tx = await makeContractCall(txOptions); + console.log(tx); + // Broadcast transaction to our Devnet stacks node + const result = await broadcastTransaction(tx, network); + return result; +}; + +export const votePositive = async ( + minerAddress: string, + network: StacksNetwork, + account: Account, + fee: number, + nonce: number +): Promise => { + const txOptions = { + contractAddress: Accounts.DEPLOYER.stxAddress, + contractName: 'mining-pool-5-blocks', + functionName: 'vote-positive-join-request', + functionArgs: [principalCV(minerAddress)], + fee, + nonce, + network, + anchorMode: AnchorMode.OnChainOnly, + postConditionMode: PostConditionMode.Allow, + senderKey: account.secretKey, + }; + // @ts-ignore + const tx = await makeContractCall(txOptions); + // Broadcast transaction to our Devnet stacks node + const result = await broadcastTransaction(tx, network); + return result; +}; + +export const tryEnterPool = async ( + network: StacksNetwork, + account: Account, + fee: number, + nonce: number +): Promise => { + const txOptions = { + contractAddress: Accounts.DEPLOYER.stxAddress, + contractName: 'mining-pool-5-blocks', + functionName: 'try-enter-pool', + functionArgs: [], + fee, + nonce, + network, + anchorMode: AnchorMode.OnChainOnly, + postConditionMode: PostConditionMode.Allow, + senderKey: account.secretKey, + }; + // @ts-ignore + const tx = await makeContractCall(txOptions); + // Broadcast transaction to our Devnet stacks node + const result = await broadcastTransaction(tx, network); + return result; +}; + +export const addPendingMinersToPool = async ( + network: StacksNetwork, + account: Account, + fee: number, + nonce: number +): Promise => { + const txOptions = { + contractAddress: Accounts.DEPLOYER.stxAddress, + contractName: 'mining-pool-5-blocks', + functionName: 'add-pending-miners-to-pool', + functionArgs: [], + fee, + nonce, + network, + anchorMode: AnchorMode.OnChainOnly, + postConditionMode: PostConditionMode.Allow, + senderKey: account.secretKey, + }; + // @ts-ignore + const tx = await makeContractCall(txOptions); + // Broadcast transaction to our Devnet stacks node + const result = await broadcastTransaction(tx, network); + return result; +}; + +export const distributeRewards = async ( + blockHeight: number, + network: StacksNetwork, + account: Account, + fee: number, + nonce: number +): Promise => { + const txOptions = { + contractAddress: Accounts.DEPLOYER.stxAddress, + contractName: 'mining-pool-5-blocks', + functionName: 'reward-distribution', + functionArgs: [uintCV(blockHeight)], + fee, + nonce, + network, + anchorMode: AnchorMode.OnChainOnly, + postConditionMode: PostConditionMode.Allow, + senderKey: account.secretKey, + }; + // @ts-ignore + const tx = await makeContractCall(txOptions); + // Broadcast transaction to our Devnet stacks node + const result = await broadcastTransaction(tx, network); + return result; +}; diff --git a/sbtc-ops/smart-contract/integration/stacking.test.ts b/sbtc-ops/smart-contract/integration/stacking.test.ts new file mode 100644 index 00000000..2363e68f --- /dev/null +++ b/sbtc-ops/smart-contract/integration/stacking.test.ts @@ -0,0 +1,643 @@ +import { + buildDevnetNetworkOrchestrator, + FAST_FORWARD_TO_EPOCH_2_4, + getAccount, + getBlockPoxAddresses, + getBlockRewards, + getCheckDelegation, + getNetworkIdFromEnv, + getScLockedBalance, + getStackerWeight, + getPoxInfo, + readRewardCyclePoxAddressForAddress, + readRewardCyclePoxAddressListAtIndex, + waitForRewardCycleId, + waitForNextPreparePhase, + waitForNextRewardPhase, +} from './helpers-stacking'; +import { Accounts, Contracts, Constants } from './constants-stacking'; +import { StacksTestnet } from '@stacks/network'; +import { DevnetNetworkOrchestrator, StacksBlockMetadata } from '@hirosystems/stacks-devnet-js'; +import { broadcastAllowContractCallerContracCall } from './allowContractCaller'; +import { afterAll, beforeAll, describe, it } from 'vitest'; +import { + broadcastDelegateStackStx, + broadcastDelegateStackStxMany, + broadcastDelegateStx, + broadcastDepositStxOwner, + broadcastJoinPool, + broadcastReserveStxOwner, + broadcastRewardDistribution, + broadcastUpdateScBalances, +} from './helper-fp'; +import { expect } from 'chai'; +import { uintCV } from '@stacks/transactions'; +import { mainContract } from './contracts'; + +describe('testing stacking under epoch 2.4', () => { + let orchestrator: DevnetNetworkOrchestrator; + let timeline = FAST_FORWARD_TO_EPOCH_2_4; + + beforeAll(() => { + orchestrator = buildDevnetNetworkOrchestrator(getNetworkIdFromEnv(), timeline); + orchestrator.start(12000); + }); + + afterAll(() => { + orchestrator.terminate(); + }); + + it('whole flow many cycles 4 stackers + liquidity provider', async () => { + console.log('POX-3 test beginning'); + const network = new StacksTestnet({ url: orchestrator.getStacksNodeUrl() }); + + let usersList = [Accounts.WALLET_1, Accounts.WALLET_2, Accounts.WALLET_3, Accounts.WALLET_4]; + + // Wait for Pox-3 activation + + await orchestrator.waitForStacksBlockAnchoredOnBitcoinBlockOfHeight(timeline.epoch_2_4 + 1, 5, true); + console.log(await getPoxInfo(network)); + + // Wait for the contracts to be deployed + + let chainUpdate, txs; + // chainUpdate = await orchestrator.waitForNextStacksBlock(); + // txs = chainUpdate.new_blocks[0].block.transactions; + + // Deposit STX Liquidity Provider + + await broadcastDepositStxOwner({ + amountUstx: 11_000_000_000, + nonce: (await getAccount(network, Accounts.DEPLOYER.stxAddress)).nonce, + network: network, + user: Accounts.DEPLOYER, + }); + + // Check the deposit tx + + let depositTxIndex = 0; + let blockIndex = 0; + while (depositTxIndex < 1) { + chainUpdate = await orchestrator.waitForNextStacksBlock(); + txs = chainUpdate.new_blocks[0].block.transactions; + blockIndex++; + if (txs.length > 1) { + let txMetadata = txs[1].metadata; + let txData = txMetadata.kind.data; + let txSC = txData['contract_identifier']; + let txMethod = txData['method']; + if (txMethod == 'deposit-stx-liquidity-provider') { + expect(txSC as any).toBe(`${mainContract.address}.${mainContract.name}`); + expect(txMethod as any).toBe('deposit-stx-liquidity-provider'); + expect((txMetadata as any)['success']).toBe(true); + expect((txMetadata as any)['result']).toBe(`(ok true)`); + console.log(`Deposit STX Metadata, (block index ${blockIndex}): `, txMetadata); + depositTxIndex++; + } + } + } + + // Allow pool in Pox-3 + + await broadcastAllowContractCallerContracCall({ + network, + nonce: (await getAccount(network, usersList[0].stxAddress)).nonce, + senderKey: usersList[0].secretKey, + }); + await broadcastAllowContractCallerContracCall({ + network, + nonce: (await getAccount(network, usersList[1].stxAddress)).nonce, + senderKey: usersList[1].secretKey, + }); + await broadcastAllowContractCallerContracCall({ + network, + nonce: (await getAccount(network, usersList[2].stxAddress)).nonce, + senderKey: usersList[2].secretKey, + }); + + // Check the allow txs + + let allowTxIndex = 0; + blockIndex = 0; + + while (allowTxIndex < 3) { + chainUpdate = await orchestrator.waitForNextStacksBlock(); + txs = chainUpdate.new_blocks[0].block.transactions; + blockIndex++; + + if (txs.length > 1) { + for (let i = 1; i <= txs.length - 1; i++) { + let txMetadata = txs[i].metadata; + let txData = txMetadata.kind.data; + let txSC = txData['contract_identifier']; + let txMethod = txData['method']; + + if (txMethod == 'allow-contract-caller') { + expect(txSC as any).toBe(`${Contracts.POX_3.address}.${Contracts.POX_3.name}`); + expect(txMethod as any).toBe('allow-contract-caller'); + expect((txMetadata as any)['success']).toBe(true); + expect((txMetadata as any)['result']).toBe(`(ok true)`); + allowTxIndex++; + console.log(`Allow Contract Caller Metadata ${allowTxIndex}, block index ${blockIndex}`, txMetadata); + } + } + } + } + + // Reserve STX for future rewards Liquidity Provider + + await broadcastReserveStxOwner({ + amountUstx: 11_000_000_000, + nonce: (await getAccount(network, Accounts.DEPLOYER.stxAddress)).nonce, + network: network, + user: Accounts.DEPLOYER, + }); + + // Check the Reserve STX tx + + for (let i = 0; i <= 15; i++) { + chainUpdate = await orchestrator.waitForNextStacksBlock(); + txs = chainUpdate.new_blocks[0].block.transactions; + + if (txs.length > 1) { + let txMetadata = txs[1].metadata; + let txData = txMetadata.kind.data; + let txSC = txData['contract_identifier']; + let txMethod = txData['method']; + + expect(txSC as any).toBe(`${mainContract.address}.${mainContract.name}`); + expect(txMethod as any).toBe('reserve-funds-future-rewards'); + expect((txMetadata as any)['success']).toBe(true); + expect((txMetadata as any)['result']).toBe(`(ok true)`); + console.log(`Reserve STX Metadata, block index ${i + 1}`, txMetadata); + i = 15; + } + } + + // 3 Stackers Join Pool + + for (let i = 0; i < usersList.length - 1; i++) { + await broadcastJoinPool({ + nonce: (await getAccount(network, usersList[i].stxAddress)).nonce, + network, + user: usersList[i], + }); + } + + // Check the Join txs + + let joinTxIndex = 0; + blockIndex = 0; + while (joinTxIndex < 3) { + chainUpdate = await orchestrator.waitForNextStacksBlock(); + txs = chainUpdate.new_blocks[0].block.transactions; + blockIndex++; + if (txs.length > 1) { + for (let i = 1; i <= txs.length - 1; i++) { + let txMetadata = txs[i].metadata; + let txData = txMetadata.kind.data; + let txSC = txData['contract_identifier']; + let txMethod = txData['method']; + + expect(txSC as any).toBe(`${mainContract.address}.${mainContract.name}`); + expect(txMethod as any).toBe('join-stacking-pool'); + expect((txMetadata as any)['success']).toBe(true); + expect((txMetadata as any)['result']).toBe(`(ok true)`); + joinTxIndex++; + console.log(`Join Pool Metadata ${joinTxIndex}, block index ${blockIndex}`, txMetadata); + } + } + } + await waitForNextRewardPhase(network, orchestrator); + chainUpdate = await orchestrator.waitForNextStacksBlock(); + + console.log( + '** BEFORE DELEGATE STX ' + + (chainUpdate.new_blocks[0].block.metadata as StacksBlockMetadata).bitcoin_anchor_block_identifier.index + ); + + // 3 Stackers Delegate STX + + await broadcastDelegateStx({ + amountUstx: 125_000_000_000, + user: usersList[0], + nonce: (await getAccount(network, usersList[0].stxAddress)).nonce, + network, + }); + + await broadcastDelegateStx({ + amountUstx: 125_000_000_000, + user: usersList[1], + nonce: (await getAccount(network, usersList[1].stxAddress)).nonce, + network, + }); + + await broadcastDelegateStx({ + amountUstx: 50_286_942_145_278, // pox activation threshold + user: usersList[2], + nonce: (await getAccount(network, usersList[2].stxAddress)).nonce, + network, + }); + + // Check the Delegate txs + + let delegateTxIndex = 0; + blockIndex = 0; + while (delegateTxIndex < 3) { + chainUpdate = await orchestrator.waitForNextStacksBlock(); + txs = chainUpdate.new_blocks[0].block.transactions; + blockIndex++; + if (txs.length > 1) { + for (let i = 1; i <= txs.length - 1; i++) { + let txMetadata = txs[i].metadata; + let txData = txMetadata.kind.data; + let txSC = txData['contract_identifier']; + let txMethod = txData['method']; + + expect(txSC as any).toBe(`${mainContract.address}.${mainContract.name}`); + expect(txMethod as any).toBe('delegate-stx'); + expect((txMetadata as any)['success']).toBe(true); + // expect((txMetadata as any)['result']).toBe(`(ok true)`); + delegateTxIndex++; + console.log(`Delegate STX Metadata ${delegateTxIndex}, block index ${blockIndex}`, txMetadata); + } + } + } + + console.log( + 'The last Delegation block: ' + + (chainUpdate.new_blocks[0].block.metadata as StacksBlockMetadata).bitcoin_anchor_block_identifier.index + ); + + // Friedger check table entry: + + let poxInfo = await getPoxInfo(network); + console.log('pox info CURRENT CYCLE:', poxInfo.current_cycle); + console.log('pox info NEXT CYCLE:', poxInfo.next_cycle); + + let poxAddrInfo0 = await readRewardCyclePoxAddressForAddress( + network, + poxInfo.current_cycle.id, + Accounts.DEPLOYER.stxAddress + ); + + expect(poxAddrInfo0).toBeNull(); + + let poxAddrInfo1 = await readRewardCyclePoxAddressListAtIndex(network, await poxInfo.next_cycle.id, 0); + let poxAddrInfo2 = await readRewardCyclePoxAddressListAtIndex(network, await poxInfo.next_cycle.id, 1); + let poxAddrInfo; + + if (poxAddrInfo2) { + poxAddrInfo = poxAddrInfo2; + } else { + poxAddrInfo = poxAddrInfo1; + } + + // Check the Total Stacked STX + + expect(poxAddrInfo?.['total-ustx']).toEqual(uintCV(50_536_942_145_278)); + + // Check balances + + let userAccount1 = await getAccount(network, usersList[0].stxAddress); + console.log('first user:', userAccount1); + + let userAccount2 = await getAccount(network, usersList[1].stxAddress); + console.log('second user:', userAccount2); + + let userAccount3 = await getAccount(network, usersList[2].stxAddress); + console.log('third user:', userAccount3); + + let userAccount4 = await getAccount(network, usersList[3].stxAddress); + console.log('fourth user:', userAccount4); + + // Update SC balances + + await waitForNextPreparePhase(network, orchestrator); + chainUpdate = await orchestrator.waitForNextStacksBlock(); + + await broadcastUpdateScBalances({ + user: Accounts.DEPLOYER, + nonce: (await getAccount(network, Accounts.DEPLOYER.stxAddress)).nonce, + network, + }); + + let updateBalancesTxIndex = 0; + blockIndex = 0; + + while (updateBalancesTxIndex < 1) { + chainUpdate = await orchestrator.waitForNextStacksBlock(); + txs = chainUpdate.new_blocks[0].block.transactions; + blockIndex++; + + if (txs.length > 1) { + for (let i = 1; i <= txs.length - 1; i++) { + let txMetadata = txs[i].metadata; + let txData = txMetadata.kind.data; + let txSC = txData['contract_identifier']; + let txMethod = txData['method']; + let updateBalancesBlock = (chainUpdate.new_blocks[0].block.metadata as StacksBlockMetadata) + .bitcoin_anchor_block_identifier.index; + console.log('** UPDATE BALANCES BLOCK ' + updateBalancesBlock); + if (updateBalancesBlock % 10 > 8 || updateBalancesBlock % 10 < 6) + console.log('COULD NOT UPDATE BALANCES DUE TO BLOCK DELAYS. PLEASE RESTART TEST!'); + + expect(txSC as any).toBe(`${mainContract.address}.${mainContract.name}`); + expect(txMethod as any).toBe('update-sc-balances'); + expect((txMetadata as any)['success']).toBe(true); + expect((txMetadata as any)['result']).toBe(`(ok true)`); + updateBalancesTxIndex++; + console.log(`Update Balances Metadata ${updateBalancesTxIndex}, block index ${blockIndex}`, txMetadata); + } + } + } + + let scLockedBalance = await getScLockedBalance(network); + expect(scLockedBalance.value as any).toBe('50536942145278'); + + // Check weights + + let deployerWeight = await getStackerWeight(network, Accounts.DEPLOYER.stxAddress, poxInfo.next_cycle.id); + expect(deployerWeight as any).toBe('217'); + + for (let i = 0; i <= 3; i++) { + let userWeight = await getStackerWeight(network, usersList[i].stxAddress, poxInfo.next_cycle.id); + i == 2 + ? expect(userWeight as any).toBe('994836') + : i < 2 + ? expect(userWeight as any).toBe('2472') + : expect(userWeight.value as any).toBe(null); + } + + chainUpdate = await waitForRewardCycleId(network, orchestrator, poxInfo.next_cycle.id); + let firstBurnBlockPastRewardCycle = (chainUpdate.new_blocks[0].block.metadata as StacksBlockMetadata) + .bitcoin_anchor_block_identifier.index; + console.log('** First burn block to check rewards: ' + firstBurnBlockPastRewardCycle); + + for (let i = 1; i <= 7; i++) chainUpdate = await orchestrator.waitForNextStacksBlock(); + + console.log( + 'Burn block when checking rewards: ' + + (chainUpdate.new_blocks[0].block.metadata as StacksBlockMetadata).bitcoin_anchor_block_identifier.index + ); + + // Print Pox Addresses for the blocks from 130 to 133 + + for (let i = firstBurnBlockPastRewardCycle; i <= firstBurnBlockPastRewardCycle + Constants.REWARD_CYCLE_LENGTH; i++) + await getBlockPoxAddresses(network, Accounts.DEPLOYER.stxAddress, i); + + // Print Block Rewards for the blocks from 130 to 133 + + for (let i = firstBurnBlockPastRewardCycle; i <= firstBurnBlockPastRewardCycle + Constants.REWARD_CYCLE_LENGTH; i++) + await getBlockRewards(network, Accounts.DEPLOYER.stxAddress, i); + + // Distribute Rewards For The Previously Verified Blocks (even if won or not) + + let userIndex = 0; + let repeatedUsers = false; + for ( + let i = firstBurnBlockPastRewardCycle; + i <= firstBurnBlockPastRewardCycle + Constants.REWARD_CYCLE_LENGTH; + i++ + ) { + if (userIndex == 4) { + userIndex = 0; + repeatedUsers = true; + } + userIndex == 4 ? (userIndex = 0) : (userIndex = userIndex); + + await broadcastRewardDistribution({ + burnBlockHeight: i, + network, + user: usersList[userIndex], + nonce: repeatedUsers + ? (await getAccount(network, usersList[userIndex].stxAddress)).nonce + 1 + : ( + await getAccount(network, usersList[userIndex].stxAddress) + ).nonce, + }); + userIndex++; + } + + let rewardDistributionTxIndex = 0; + blockIndex = 0; + + while (rewardDistributionTxIndex < 6) { + chainUpdate = await orchestrator.waitForNextStacksBlock(); + txs = chainUpdate.new_blocks[0].block.transactions; + blockIndex++; + + if (txs.length > 1) { + for (let i = 1; i <= txs.length - 1; i++) { + let txMetadata = txs[i].metadata; + let txData = txMetadata.kind.data; + let txSC = txData['contract_identifier']; + let txMethod = txData['method']; + expect(txSC as any).toBe(`${mainContract.address}.${mainContract.name}`); + expect(txMethod as any).toBe('reward-distribution'); + console.log('Reward distribution result: ', (txMetadata as any)['result']); + rewardDistributionTxIndex++; + console.log( + `Reward Distribution Metadata ${rewardDistributionTxIndex}, block index ${blockIndex}`, + txMetadata + ); + } + } + } + + // Check Balances + + console.log('deployer:', await getAccount(network, Accounts.DEPLOYER.stxAddress)); + console.log('first user:', await getAccount(network, usersList[0].stxAddress)); + console.log('second user:', await getAccount(network, usersList[1].stxAddress)); + console.log('third user:', await getAccount(network, usersList[2].stxAddress)); + console.log('fourth user:', await getAccount(network, usersList[3].stxAddress)); + + await getCheckDelegation(network, usersList[0].stxAddress); + await getCheckDelegation(network, usersList[1].stxAddress); + await getCheckDelegation(network, usersList[2].stxAddress); + await getCheckDelegation(network, usersList[3].stxAddress); + + // Delegate Stack Stx (must happen on the second half of the reward phase) + + poxInfo = await getPoxInfo(network); + chainUpdate = await waitForRewardCycleId(network, orchestrator, poxInfo.next_cycle.id); + + for (let i = 1; i <= 6; i++) chainUpdate = await orchestrator.waitForNextStacksBlock(); + + console.log( + '** BEFORE DELEGATE STACK ' + + (chainUpdate.new_blocks[0].block.metadata as StacksBlockMetadata).bitcoin_anchor_block_identifier.index + ); + + let delegateStack1 = await broadcastDelegateStackStx( + usersList[0].stxAddress, + network, + Accounts.DEPLOYER, + 1000, + ( + await getAccount(network, Accounts.DEPLOYER.stxAddress) + ).nonce + ); + let delegateStack2 = await broadcastDelegateStackStx( + usersList[1].stxAddress, + network, + Accounts.DEPLOYER, + 1000, + (await getAccount(network, Accounts.DEPLOYER.stxAddress)).nonce + 1 + ); + let delegateStack3 = await broadcastDelegateStackStx( + usersList[2].stxAddress, + network, + Accounts.DEPLOYER, + 1000, + (await getAccount(network, Accounts.DEPLOYER.stxAddress)).nonce + 2 + ); + + let delegateStackTxIndex = 0; + blockIndex = 0; + while (delegateStackTxIndex < 3) { + chainUpdate = await orchestrator.waitForNextStacksBlock(); + txs = chainUpdate.new_blocks[0].block.transactions; + blockIndex++; + if (txs.length > 1) { + for (let i = 1; i <= txs.length - 1; i++) { + let txMetadata = txs[i].metadata; + let txData = txMetadata.kind.data; + let txSC = txData['contract_identifier']; + let txMethod = txData['method']; + + expect(txSC as any).toBe(`${mainContract.address}.${mainContract.name}`); + expect(txMethod as any).toBe('delegate-stack-stx'); + console.log(`Delegate Stack STX Metadata ${delegateStackTxIndex}, block index ${blockIndex}`, txMetadata); + + expect((txMetadata as any)['success']).toBe(true); + delegateStackTxIndex++; + } + } + } + + // Friedger check table entry: + + poxInfo = await getPoxInfo(network); + console.log('pox info CURRENT CYCLE:', poxInfo.current_cycle); + console.log('pox info NEXT CYCLE:', poxInfo.next_cycle); + + poxAddrInfo0 = await readRewardCyclePoxAddressForAddress( + network, + poxInfo.current_cycle.id, + Accounts.DEPLOYER.stxAddress + ); + + expect(poxAddrInfo0).toBeNull(); + + poxAddrInfo1 = await readRewardCyclePoxAddressListAtIndex(network, await poxInfo.next_cycle.id, 0); + poxAddrInfo2 = await readRewardCyclePoxAddressListAtIndex(network, await poxInfo.next_cycle.id, 1); + poxAddrInfo; + + if (poxAddrInfo2) { + poxAddrInfo = poxAddrInfo2; + } else { + poxAddrInfo = poxAddrInfo1; + } + + // Check total stacked ustx + + expect(poxAddrInfo?.['total-ustx']).toEqual(uintCV(50_536_942_145_278)); + + // Check balances + + userAccount1 = await getAccount(network, usersList[0].stxAddress); + console.log('first user:', userAccount1); + + userAccount2 = await getAccount(network, usersList[1].stxAddress); + console.log('second user:', userAccount2); + + userAccount3 = await getAccount(network, usersList[2].stxAddress); + console.log('third user:', userAccount3); + + userAccount4 = await getAccount(network, usersList[3].stxAddress); + console.log('fourth user:', userAccount4); + + await orchestrator.waitForNextStacksBlock(); + + // Delegate Stack Stx Many for all stackers in the list + // can be done after the reward cycle is half through, so wait for the next prepare phase + // next prepare phase -> 146 > 145 + + poxInfo = await getPoxInfo(network); + chainUpdate = await waitForRewardCycleId(network, orchestrator, poxInfo.next_cycle.id); + + for (let i = 1; i <= 6; i++) chainUpdate = await orchestrator.waitForNextStacksBlock(); + + let usersAddressesList = []; + usersList.forEach((user) => { + if (user && user.stxAddress) usersAddressesList.push(user.stxAddress); + }); + + console.log( + '** BEFORE DELEGATE STACK MANY ' + + (chainUpdate.new_blocks[0].block.metadata as StacksBlockMetadata).bitcoin_anchor_block_identifier.index + ); + + let delegateStackMany1 = await broadcastDelegateStackStxMany({ + stackersLockList: usersAddressesList, + network: network, + nonce: (await getAccount(network, Accounts.DEPLOYER.stxAddress)).nonce, + user: Accounts.DEPLOYER, + }); + + let delegateStackManyTxIndex = 0; + blockIndex = 0; + while (delegateStackManyTxIndex < 1) { + chainUpdate = await orchestrator.waitForNextStacksBlock(); + txs = chainUpdate.new_blocks[0].block.transactions; + blockIndex++; + if (txs.length > 1) { + for (let i = 1; i <= txs.length - 1; i++) { + let txMetadata = txs[i].metadata; + let txData = txMetadata.kind.data; + let txSC = txData['contract_identifier']; + let txMethod = txData['method']; + + expect(txSC as any).toBe(`${mainContract.address}.${mainContract.name}`); + expect(txMethod as any).toBe('delegate-stack-stx-many'); + // expect((txMetadata as any)['result']).toBe('(ok ((ok false) (ok true) (ok true) (err u9000)))'); + console.log('Delegate Stack Many Result: ', txMetadata['result']); + console.log(`Delegate Stack STX Metadata ${delegateStackManyTxIndex}, block index ${blockIndex}`, txMetadata); + + expect((txMetadata as any)['success']).toBe(true); + delegateStackManyTxIndex++; + } + } + } + + // Friedger check table entry: + + poxInfo = await getPoxInfo(network); + console.log('pox info CURRENT CYCLE:', poxInfo.current_cycle); + console.log('pox info NEXT CYCLE:', poxInfo.next_cycle); + + poxAddrInfo0 = await readRewardCyclePoxAddressForAddress( + network, + poxInfo.current_cycle.id, + Accounts.DEPLOYER.stxAddress + ); + + expect(poxAddrInfo0).toBeNull(); + + poxAddrInfo1 = await readRewardCyclePoxAddressListAtIndex(network, await poxInfo.next_cycle.id, 0); + poxAddrInfo2 = await readRewardCyclePoxAddressListAtIndex(network, await poxInfo.next_cycle.id, 1); + poxAddrInfo; + + if (poxAddrInfo2) { + poxAddrInfo = poxAddrInfo2; + } else { + poxAddrInfo = poxAddrInfo1; + } + + // Check the Total Stacked STX + + expect(poxAddrInfo?.['total-ustx']).toEqual(uintCV(50_536_942_145_278)); + }); +}); diff --git a/sbtc-ops/smart-contract/jest.config.js b/sbtc-ops/smart-contract/jest.config.js new file mode 100644 index 00000000..f7c89de8 --- /dev/null +++ b/sbtc-ops/smart-contract/jest.config.js @@ -0,0 +1,9 @@ +/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testTimeout: 1900_000, + maxWorkers: 1, + reporters: ['default', 'jest-github-actions-reporter'], + testLocationInResults: true, +}; diff --git a/sbtc-ops/smart-contract/package-lock.json b/sbtc-ops/smart-contract/package-lock.json new file mode 100644 index 00000000..b7ff47ec --- /dev/null +++ b/sbtc-ops/smart-contract/package-lock.json @@ -0,0 +1,7228 @@ +{ + "name": "arkadiko-dao-clarity", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "arkadiko-dao-clarity", + "license": "ISC", + "dependencies": { + "@hirosystems/stacks-devnet-js": "^1.6.2", + "@noble/hashes": "^1.1.5", + "@noble/secp256k1": "^1.7.0", + "@scure/base": "^1.1.1", + "@scure/bip32": "^1.1.1", + "@scure/bip39": "^1.1.0", + "@stacks/common": "^6.0.0", + "@stacks/network": "^6.0.0", + "@stacks/stacking": "^6.0.2", + "@stacks/transactions": "^6.5.0", + "@types/node": "^16.7.13", + "bitcoinjs-lib": "^6.1.3", + "buffer": "^6.0.3", + "micro-btc-signer": "^0.2.0", + "mocha": "^10.2.0", + "node-fetch": "^2.6.7", + "typescript": "^4.9.0" + }, + "devDependencies": { + "@types/jest": "^27.5.2", + "@vitest/ui": "^0.25.8", + "jest": "^27.4.5", + "jest-github-actions-reporter": "^1.0.3", + "prettier": "2.8.1", + "ts-jest": "^27.1.2", + "vite": "^4.0.4", + "vitest": "^0.32.0", + "vitest-github-actions-reporter": "^0.9.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@actions/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz", + "integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==", + "dev": true, + "dependencies": { + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + } + }, + "node_modules/@actions/http-client": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz", + "integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==", + "dev": true, + "dependencies": { + "tunnel": "^0.0.6" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", + "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz", + "integrity": "sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz", + "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.21.4", + "@babel/helper-compilation-targets": "^7.21.4", + "@babel/helper-module-transforms": "^7.21.2", + "@babel/helpers": "^7.21.0", + "@babel/parser": "^7.21.4", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.4", + "@babel/types": "^7.21.4", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz", + "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.4", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz", + "integrity": "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.21.4", + "@babel/helper-validator-option": "^7.21.0", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", + "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", + "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.2", + "@babel/types": "^7.21.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", + "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", + "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", + "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz", + "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.21.4", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.21.4", + "@babel/types": "^7.21.4", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz", + "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@esbuild/android-arm": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.15.tgz", + "integrity": "sha512-sRSOVlLawAktpMvDyJIkdLI/c/kdRTOqo8t6ImVxg8yT7LQDUYV5Rp2FKeEosLr6ZCja9UjYAzyRSxGteSJPYg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.15.tgz", + "integrity": "sha512-0kOB6Y7Br3KDVgHeg8PRcvfLkq+AccreK///B4Z6fNZGr/tNHX0z2VywCc7PTeWp+bPvjA5WMvNXltHw5QjAIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.15.tgz", + "integrity": "sha512-MzDqnNajQZ63YkaUWVl9uuhcWyEyh69HGpMIrf+acR4otMkfLJ4sUCxqwbCyPGicE9dVlrysI3lMcDBjGiBBcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.15.tgz", + "integrity": "sha512-7siLjBc88Z4+6qkMDxPT2juf2e8SJxmsbNVKFY2ifWCDT72v5YJz9arlvBw5oB4W/e61H1+HDB/jnu8nNg0rLA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.15.tgz", + "integrity": "sha512-NbImBas2rXwYI52BOKTW342Tm3LTeVlaOQ4QPZ7XuWNKiO226DisFk/RyPk3T0CKZkKMuU69yOvlapJEmax7cg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.15.tgz", + "integrity": "sha512-Xk9xMDjBVG6CfgoqlVczHAdJnCs0/oeFOspFap5NkYAmRCT2qTn1vJWA2f419iMtsHSLm+O8B6SLV/HlY5cYKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.15.tgz", + "integrity": "sha512-3TWAnnEOdclvb2pnfsTWtdwthPfOz7qAfcwDLcfZyGJwm1SRZIMOeB5FODVhnM93mFSPsHB9b/PmxNNbSnd0RQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.15.tgz", + "integrity": "sha512-MLTgiXWEMAMr8nmS9Gigx43zPRmEfeBfGCwxFQEMgJ5MC53QKajaclW6XDPjwJvhbebv+RzK05TQjvH3/aM4Xw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.15.tgz", + "integrity": "sha512-T0MVnYw9KT6b83/SqyznTs/3Jg2ODWrZfNccg11XjDehIved2oQfrX/wVuev9N936BpMRaTR9I1J0tdGgUgpJA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.15.tgz", + "integrity": "sha512-wp02sHs015T23zsQtU4Cj57WiteiuASHlD7rXjKUyAGYzlOKDAjqK6bk5dMi2QEl/KVOcsjwL36kD+WW7vJt8Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.15.tgz", + "integrity": "sha512-k7FsUJjGGSxwnBmMh8d7IbObWu+sF/qbwc+xKZkBe/lTAF16RqxRCnNHA7QTd3oS2AfGBAnHlXL67shV5bBThQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.15.tgz", + "integrity": "sha512-ZLWk6czDdog+Q9kE/Jfbilu24vEe/iW/Sj2d8EVsmiixQ1rM2RKH2n36qfxK4e8tVcaXkvuV3mU5zTZviE+NVQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.15.tgz", + "integrity": "sha512-mY6dPkIRAiFHRsGfOYZC8Q9rmr8vOBZBme0/j15zFUKM99d4ILY4WpOC7i/LqoY+RE7KaMaSfvY8CqjJtuO4xg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.15.tgz", + "integrity": "sha512-EcyUtxffdDtWjjwIH8sKzpDRLcVtqANooMNASO59y+xmqqRYBBM7xVLQhqF7nksIbm2yHABptoioS9RAbVMWVA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.15.tgz", + "integrity": "sha512-BuS6Jx/ezxFuHxgsfvz7T4g4YlVrmCmg7UAwboeyNNg0OzNzKsIZXpr3Sb/ZREDXWgt48RO4UQRDBxJN3B9Rbg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.15.tgz", + "integrity": "sha512-JsdS0EgEViwuKsw5tiJQo9UdQdUJYuB+Mf6HxtJSPN35vez1hlrNb1KajvKWF5Sa35j17+rW1ECEO9iNrIXbNg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.15.tgz", + "integrity": "sha512-R6fKjtUysYGym6uXf6qyNephVUQAGtf3n2RCsOST/neIwPqRWcnc3ogcielOd6pT+J0RDR1RGcy0ZY7d3uHVLA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.15.tgz", + "integrity": "sha512-mVD4PGc26b8PI60QaPUltYKeSX0wxuy0AltC+WCTFwvKCq2+OgLP4+fFd+hZXzO2xW1HPKcytZBdjqL6FQFa7w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.15.tgz", + "integrity": "sha512-U6tYPovOkw3459t2CBwGcFYfFRjivcJJc1WC8Q3funIwX8x4fP+R6xL/QuTPNGOblbq/EUDxj9GU+dWKX0oWlQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.15.tgz", + "integrity": "sha512-W+Z5F++wgKAleDABemiyXVnzXgvRFs+GVKThSI+mGgleLWluv0D7Diz4oQpgdpNzh4i2nNDzQtWbjJiqutRp6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.15.tgz", + "integrity": "sha512-Muz/+uGgheShKGqSVS1KsHtCyEzcdOn/W/Xbh6H91Etm+wiIfwZaBn1W58MeGtfI8WA961YMHFYTthBdQs4t+w==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.15.tgz", + "integrity": "sha512-DjDa9ywLUUmjhV2Y9wUTIF+1XsmuFGvZoCmOWkli1XcNAh5t25cc7fgsCx4Zi/Uurep3TTLyDiKATgGEg61pkA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@hirosystems/chainhook-types": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@hirosystems/chainhook-types/-/chainhook-types-1.1.2.tgz", + "integrity": "sha512-klQDKRyiPqF9fgDetMLTPMcB4A/+Vo7px74RbzzvmnCSDMPQua0DD5zhfuo12Ga3pexFFoIftIqnSjWvjncKNQ==" + }, + "node_modules/@hirosystems/stacks-devnet-js": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@hirosystems/stacks-devnet-js/-/stacks-devnet-js-1.7.0.tgz", + "integrity": "sha512-F76e+4EEDzZxEuqzP4jSmbmL6/WtiTm5UH7TeAGgHEDA7SYEy+ZSMd/xvF2PNnwgBRXMFKWF+kZcYlwygNr3Mw==", + "hasInstallScript": true, + "dependencies": { + "@hirosystems/chainhook-types": "^1.1.2", + "@mapbox/node-pre-gyp": "^1.0.8", + "neon-cli": "^0.9.1", + "node-pre-gyp-github": "^1.4.3", + "typescript": "^4.5.5" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/core": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", + "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "dev": true, + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/reporters": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^27.5.1", + "jest-config": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-resolve-dependencies": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "jest-watcher": "^27.5.1", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", + "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-haste-map": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.1.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.0", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.0.tgz", + "integrity": "sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "dev": true, + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", + "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-runtime": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", + "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@noble/curves": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-0.8.3.tgz", + "integrity": "sha512-OqaOf4RWDaCRuBKJLDURrgVxjLmneGsiCXGuzYB5y95YithZMA6w4uk34DHSm0rKMrrYiaeZj48/81EvaAScLQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "1.3.0" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", + "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@noble/secp256k1": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", + "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "dependencies": { + "@octokit/types": "^6.0.3" + } + }, + "node_modules/@octokit/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "dependencies": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "dependencies": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "dependencies": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", + "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "2.21.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", + "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", + "dependencies": { + "@octokit/types": "^6.40.0" + }, + "peerDependencies": { + "@octokit/core": ">=2" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", + "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", + "dependencies": { + "@octokit/types": "^6.39.0", + "deprecation": "^2.3.1" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "dependencies": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "dependencies": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "node_modules/@octokit/rest": { + "version": "18.12.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", + "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", + "dependencies": { + "@octokit/core": "^3.5.1", + "@octokit/plugin-paginate-rest": "^2.16.8", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^5.12.0" + } + }, + "node_modules/@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "dependencies": { + "@octokit/openapi-types": "^12.11.0" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.21", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", + "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", + "dev": true + }, + "node_modules/@scure/base": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", + "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@scure/bip32": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.2.0.tgz", + "integrity": "sha512-O+vT/hBVk+ag2i6j2CDemwd1E1MtGt+7O1KzrPNsaNvSsiEK55MyPIxJIMI2PS8Ijj464B2VbQlpRoQXxw1uHg==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/curves": "~0.8.3", + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@scure/bip39": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.0.tgz", + "integrity": "sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@stacks/common": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@stacks/common/-/common-6.0.0.tgz", + "integrity": "sha512-tETwccvbYvaZ7u3ZucWNMOIPN97r6IPeZXKIFhLc1KSVaWSGEPTtZcwVp+Rz3mu2XgI2pg37SUrOWXSL7OOkDw==", + "dependencies": { + "@types/bn.js": "^5.1.0", + "@types/node": "^18.0.4" + } + }, + "node_modules/@stacks/common/node_modules/@types/node": { + "version": "18.15.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", + "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==" + }, + "node_modules/@stacks/encryption": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@stacks/encryption/-/encryption-6.5.0.tgz", + "integrity": "sha512-QE1+gy1x6spGkpK5PnZxKoX1hL8eeIYxYa5HNMl4cbdIVKaFgqjoGFKMtTA/tQMc91T/saXLqbQLyh/U4AVpTA==", + "dependencies": { + "@noble/hashes": "1.1.5", + "@noble/secp256k1": "1.7.1", + "@scure/bip39": "1.1.0", + "@stacks/common": "^6.0.0", + "@types/node": "^18.0.4", + "base64-js": "^1.5.1", + "bs58": "^5.0.0", + "ripemd160-min": "^0.0.6", + "varuint-bitcoin": "^1.1.2" + } + }, + "node_modules/@stacks/encryption/node_modules/@noble/hashes": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.5.tgz", + "integrity": "sha512-LTMZiiLc+V4v1Yi16TD6aX2gmtKszNye0pQgbaLqkvhIqP7nVsSaJsWloGQjJfJ8offaoP5GtX3yY5swbcJxxQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@stacks/encryption/node_modules/@scure/bip39": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz", + "integrity": "sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "~1.1.1", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@stacks/encryption/node_modules/@types/node": { + "version": "18.15.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", + "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==" + }, + "node_modules/@stacks/network": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@stacks/network/-/network-6.3.0.tgz", + "integrity": "sha512-573ZldQ+Iy0nCCxprXLLvkAo1AMEXncfmMUvqQ+5TN3m7VqCVADtb5G5WzMZsyR4m/k9oPsv076Lmqyl8AtR2A==", + "dependencies": { + "@stacks/common": "^6.0.0", + "cross-fetch": "^3.1.5" + } + }, + "node_modules/@stacks/stacking": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@stacks/stacking/-/stacking-6.5.0.tgz", + "integrity": "sha512-Kb8FK+NxEFDXP2r9xXGdeR7cZp52Cz5btpW4xKAEM7Ze3QPB6cM8IALvMBh3e53qqw8sO/dpRqcIjrNgLf5gqg==", + "dependencies": { + "@scure/base": "1.1.1", + "@stacks/common": "^6.0.0", + "@stacks/encryption": "^6.5.0", + "@stacks/network": "^6.3.0", + "@stacks/stacks-blockchain-api-types": "^0.61.0", + "@stacks/transactions": "^6.5.0", + "bs58": "^5.0.0" + } + }, + "node_modules/@stacks/stacks-blockchain-api-types": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@stacks/stacks-blockchain-api-types/-/stacks-blockchain-api-types-0.61.0.tgz", + "integrity": "sha512-yPOfTUboo5eA9BZL/hqMcM71GstrFs9YWzOrJFPeP4cOO1wgYvAcckgBRbgiE3NqeX0A7SLZLDAXLZbATuRq9w==" + }, + "node_modules/@stacks/transactions": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@stacks/transactions/-/transactions-6.5.0.tgz", + "integrity": "sha512-kwE8cZq+QdAum4/LC+lSlAXVvzkdsSHTkCbfg4+VCWPBqA+gdXEqZe6R9SNBtMb8yGQrqUY8uIGRLVCWcXJ8zQ==", + "dependencies": { + "@noble/hashes": "1.1.5", + "@noble/secp256k1": "1.7.1", + "@stacks/common": "^6.0.0", + "@stacks/network": "^6.3.0", + "c32check": "^2.0.0", + "lodash.clonedeep": "^4.5.0" + } + }, + "node_modules/@stacks/transactions/node_modules/@noble/hashes": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.5.tgz", + "integrity": "sha512-LTMZiiLc+V4v1Yi16TD6aX2gmtKszNye0pQgbaLqkvhIqP7nVsSaJsWloGQjJfJ8offaoP5GtX3yY5swbcJxxQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", + "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", + "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/chai": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", + "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==", + "dev": true + }, + "node_modules/@types/chai-subset": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.3.tgz", + "integrity": "sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==", + "dev": true, + "dependencies": { + "@types/chai": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "27.5.2", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.5.2.tgz", + "integrity": "sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==", + "dev": true, + "dependencies": { + "jest-matcher-utils": "^27.0.0", + "pretty-format": "^27.0.0" + } + }, + "node_modules/@types/node": { + "version": "16.18.23", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.23.tgz", + "integrity": "sha512-XAMpaw1s1+6zM+jn2tmw8MyaRDIJfXxqmIQIS0HfoGYPuf7dUWeiUKopwq13KFX9lEp1+THGtlaaYx39Nxr58g==" + }, + "node_modules/@types/prettier": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", + "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "16.0.5", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", + "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/@vitest/expect": { + "version": "0.32.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.32.4.tgz", + "integrity": "sha512-m7EPUqmGIwIeoU763N+ivkFjTzbaBn0n9evsTOcde03ugy2avPs3kZbYmw3DkcH1j5mxhMhdamJkLQ6dM1bk/A==", + "dev": true, + "dependencies": { + "@vitest/spy": "0.32.4", + "@vitest/utils": "0.32.4", + "chai": "^4.3.7" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "0.32.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.32.4.tgz", + "integrity": "sha512-cHOVCkiRazobgdKLnczmz2oaKK9GJOw6ZyRcaPdssO1ej+wzHVIkWiCiNacb3TTYPdzMddYkCgMjZ4r8C0JFCw==", + "dev": true, + "dependencies": { + "@vitest/utils": "0.32.4", + "p-limit": "^4.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/snapshot": { + "version": "0.32.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.32.4.tgz", + "integrity": "sha512-IRpyqn9t14uqsFlVI2d7DFMImGMs1Q9218of40bdQQgMePwVdmix33yMNnebXcTzDU5eiV3eUsoxxH5v0x/IQA==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.0", + "pathe": "^1.1.1", + "pretty-format": "^29.5.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@vitest/snapshot/node_modules/pretty-format": { + "version": "29.6.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.1.tgz", + "integrity": "sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.0", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/snapshot/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/@vitest/spy": { + "version": "0.32.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.32.4.tgz", + "integrity": "sha512-oA7rCOqVOOpE6rEoXuCOADX7Lla1LIa4hljI2MSccbpec54q+oifhziZIJXxlE/CvI2E+ElhBHzVu0VEvJGQKQ==", + "dev": true, + "dependencies": { + "tinyspy": "^2.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/ui": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-0.25.8.tgz", + "integrity": "sha512-wfuhghldD5QHLYpS46GK8Ru8P3XcMrWvFjRQD21KNzc9Y/qtJsqoC8KmT6xWVkMNw4oHYixpo3a4ZySRJdserw==", + "dev": true, + "dependencies": { + "sirv": "^2.0.2" + } + }, + "node_modules/@vitest/utils": { + "version": "0.32.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.32.4.tgz", + "integrity": "sha512-Gwnl8dhd1uJ+HXrYyV0eRqfmk9ek1ASE/LWfTCuWMw+d07ogHqp4hEAV28NiecimK6UY9DpSEPh+pXBA5gtTBg==", + "dev": true, + "dependencies": { + "diff-sequences": "^29.4.3", + "loupe": "^2.3.6", + "pretty-format": "^29.5.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@vitest/utils/node_modules/diff-sequences": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", + "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/utils/node_modules/pretty-format": { + "version": "29.6.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.1.tgz", + "integrity": "sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.0", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/utils/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/babel-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", + "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", + "dev": true, + "dependencies": { + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", + "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", + "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^27.5.1", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base-x": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz", + "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bech32": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", + "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" + }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/bip174": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bip174/-/bip174-2.1.0.tgz", + "integrity": "sha512-lkc0XyiX9E9KiVAS1ZiOqK1xfiwvf4FXDDdkDq5crcDzOq+xGytY+14qCsqz7kCiy8rpN1CRNfacRhf9G3JNSA==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/bitcoinjs-lib": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.1.3.tgz", + "integrity": "sha512-TYXs/Qf+GNk2nnsB9HrXWqzFuEgCg0Gx+v3UW3B8VuceFHXVvhT+7hRnTSvwkX0i8rz2rtujeU6gFaDcFqYFDw==", + "dependencies": { + "@noble/hashes": "^1.2.0", + "bech32": "^2.0.0", + "bip174": "^2.1.0", + "bs58check": "^3.0.1", + "typeforce": "^1.11.3", + "varuint-bitcoin": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" + }, + "node_modules/browserslist": { + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bs58": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz", + "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==", + "dependencies": { + "base-x": "^4.0.0" + } + }, + "node_modules/bs58check": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-3.0.1.tgz", + "integrity": "sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ==", + "dependencies": { + "@noble/hashes": "^1.2.0", + "bs58": "^5.0.0" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==" + }, + "node_modules/c32check": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/c32check/-/c32check-2.0.0.tgz", + "integrity": "sha512-rpwfAcS/CMqo0oCqDf3r9eeLgScRE3l/xHDCXhM3UyrfvIn7PrLq63uHh7yYbv8NzaZn5MVsVhIRpQ+5GZ5HyA==", + "dependencies": { + "@noble/hashes": "^1.1.2", + "base-x": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001474", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001474.tgz", + "integrity": "sha512-iaIZ8gVrWfemh5DG3T9/YqarVZoYf0r188IjaGwx68j4Pf0SGY6CQkmJUIE+NZHkkecQGohzXmBGEwWDr9aM3Q==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chai": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", + "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^4.1.2", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/command-line-args": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", + "dependencies": { + "array-back": "^3.1.0", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-commands": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/command-line-commands/-/command-line-commands-3.0.2.tgz", + "integrity": "sha512-ac6PdCtdR6q7S3HN+JiVLIWGHY30PRYIEl2qPo+FuEuzwAUk0UYyimrngrg7FvF/mCr4Jgoqv5ZnHZgads50rw==", + "dependencies": { + "array-back": "^4.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/command-line-commands/node_modules/array-back": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/command-line-usage": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.3.tgz", + "integrity": "sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==", + "dependencies": { + "array-back": "^4.0.2", + "chalk": "^2.4.2", + "table-layout": "^1.0.2", + "typical": "^5.2.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/command-line-usage/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/array-back": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/command-line-usage/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/command-line-usage/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/command-line-usage/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "dependencies": { + "node-fetch": "2.6.7" + } + }, + "node_modules/cross-fetch/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/cross-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/cross-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/cross-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "node_modules/detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.352", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.352.tgz", + "integrity": "sha512-ikFUEyu5/q+wJpMOxWxTaEVk2M1qKqTGKKyfJmod1CPZxKfYnxVS41/GCBQg21ItBpZybyN8sNpRqCUGm+Zc4Q==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/esbuild": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.15.tgz", + "integrity": "sha512-LBUV2VsUIc/iD9ME75qhT4aJj0r75abCVS0jakhFzOtR7TQsqQA5w0tZ+KTKnwl3kXE0MhskNdHDh/I5aCR1Zw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.17.15", + "@esbuild/android-arm64": "0.17.15", + "@esbuild/android-x64": "0.17.15", + "@esbuild/darwin-arm64": "0.17.15", + "@esbuild/darwin-x64": "0.17.15", + "@esbuild/freebsd-arm64": "0.17.15", + "@esbuild/freebsd-x64": "0.17.15", + "@esbuild/linux-arm": "0.17.15", + "@esbuild/linux-arm64": "0.17.15", + "@esbuild/linux-ia32": "0.17.15", + "@esbuild/linux-loong64": "0.17.15", + "@esbuild/linux-mips64el": "0.17.15", + "@esbuild/linux-ppc64": "0.17.15", + "@esbuild/linux-riscv64": "0.17.15", + "@esbuild/linux-s390x": "0.17.15", + "@esbuild/linux-x64": "0.17.15", + "@esbuild/netbsd-x64": "0.17.15", + "@esbuild/openbsd-x64": "0.17.15", + "@esbuild/sunos-x64": "0.17.15", + "@esbuild/win32-arm64": "0.17.15", + "@esbuild/win32-ia32": "0.17.15", + "@esbuild/win32-x64": "0.17.15" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-replace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "dependencies": { + "array-back": "^3.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-config": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/git-config/-/git-config-0.0.7.tgz", + "integrity": "sha512-LidZlYZXWzVjS+M3TEwhtYBaYwLeOZrXci1tBgqp/vDdZTBMl02atvwb6G35L64ibscYoPnxfbwwUS+VZAISLA==", + "dependencies": { + "iniparser": "~1.0.5" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/iniparser": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/iniparser/-/iniparser-1.0.5.tgz", + "integrity": "sha512-i40MWqgTU6h/70NtMsDVVDLjDYWwcIR1yIEVDPfxZIJno9z9L4s83p/V7vAu2i48Vj0gpByrkGFub7ko9XvPrw==", + "engines": { + "node": "*" + } + }, + "node_modules/inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.19", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "dev": true, + "dependencies": { + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "execa": "^5.0.0", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", + "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-cli": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", + "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "dev": true, + "dependencies": { + "@jest/core": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "prompts": "^2.0.1", + "yargs": "^16.2.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", + "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.8.0", + "@jest/test-sequencer": "^27.5.1", + "@jest/types": "^27.5.1", + "babel-jest": "^27.5.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.9", + "jest-circus": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-jasmine2": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-github-actions-reporter": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/jest-github-actions-reporter/-/jest-github-actions-reporter-1.0.3.tgz", + "integrity": "sha512-IwLAKLSWLN8ZVfcfEEv6rfeWb78wKDeOhvOmH9KKXayKsKLSCwceopBcB+KUtwxfB5wYnT8Y9s2eZ+WdhA5yng==", + "dev": true, + "dependencies": { + "@actions/core": "^1.2.0" + } + }, + "node_modules/jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-jasmine2": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", + "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-leak-detector": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "dev": true, + "dependencies": { + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runner": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "dev": true, + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-serializer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", + "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", + "dev": true, + "dependencies": { + "@types/node": "*", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.5.1", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "dev": true, + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/local-pkg": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", + "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.0" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz", + "integrity": "sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/magic-string/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/make-promises-safe": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/make-promises-safe/-/make-promises-safe-5.1.0.tgz", + "integrity": "sha512-AfdZ49rtyhQR/6cqVKGoH7y4ql7XkS5HJI1lZm0/5N6CQosy1eYbBJ/qbhkKHzo17UH7M918Bysf6XB9f3kS1g==" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/micro-btc-signer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/micro-btc-signer/-/micro-btc-signer-0.2.0.tgz", + "integrity": "sha512-Rho4MgGnDoEt/nHKHc86+nNCU2xUu+u1XIn4+Qy3e2QeJ2FILr8f/EU80I3QDavJBGvcByrsG6qICuV178HmVg==", + "deprecated": "Switch to @scure/btc-signer for security updates and support", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "~1.1.1", + "@noble/secp256k1": "~1.7.0", + "@scure/base": "~1.1.0", + "micro-packed": "~0.3.0" + } + }, + "node_modules/micro-btc-signer/node_modules/@noble/hashes": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.5.tgz", + "integrity": "sha512-LTMZiiLc+V4v1Yi16TD6aX2gmtKszNye0pQgbaLqkvhIqP7nVsSaJsWloGQjJfJ8offaoP5GtX3yY5swbcJxxQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/micro-packed": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/micro-packed/-/micro-packed-0.3.2.tgz", + "integrity": "sha512-D1Bq0/lVOzdxhnX5vylCxZpdw5LylH7Vd81py0DfRsKUP36XYpwvy8ZIsECVo3UfnoROn8pdKqkOzL7Cd82sGA==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@scure/base": "~1.1.1" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", + "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mlly": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.0.tgz", + "integrity": "sha512-ua8PAThnTwpprIaU47EPeZ/bPUVp2QYBbWMphUQpVdBI3Lgqzm5KZQ45Agm3YJedHXaIHl6pBGabaLSUPPSptg==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "pathe": "^1.1.1", + "pkg-types": "^1.0.3", + "ufo": "^1.1.2" + } + }, + "node_modules/mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/mocha/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/mocha/node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/mocha/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mrmime": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/neon-cli": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/neon-cli/-/neon-cli-0.9.1.tgz", + "integrity": "sha512-iWwKjI1Q4o1lWVc4vkr59BXEYlBl5HggKx5ETw5kStpuvT1nVcolPtTdydisx8pLRRxN+gjER571DOGnA0Yxcg==", + "dependencies": { + "chalk": "^4.1.0", + "command-line-args": "^5.1.1", + "command-line-commands": "^3.0.1", + "command-line-usage": "^6.1.0", + "git-config": "0.0.7", + "handlebars": "^4.7.6", + "inquirer": "^7.3.3", + "make-promises-safe": "^5.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.2", + "toml": "^3.0.0", + "ts-typed-json": "^0.3.2", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^3.0.0" + }, + "bin": { + "neon": "bin/cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-fetch": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", + "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-pre-gyp-github": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/node-pre-gyp-github/-/node-pre-gyp-github-1.4.4.tgz", + "integrity": "sha512-oE9JD1aXRi4+1jSH7Q+ybEhfujW5bJ66n4YMGpaUp/k2/X/8i09ouK1seznf3wOagcKjytRJCkf71DdEJx2zhA==", + "dependencies": { + "@octokit/rest": "18.12.0", + "commander": "7.2.0" + }, + "bin": { + "node-pre-gyp-github": "bin/node-pre-gyp-github.js" + } + }, + "node_modules/node-releases": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "dev": true + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/nwsapi": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", + "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==", + "dev": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/pathe": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.1.tgz", + "integrity": "sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==", + "dev": true + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", + "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.2.0", + "mlly": "^1.2.0", + "pathe": "^1.1.0" + } + }, + "node_modules/postcss": { + "version": "8.4.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", + "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.1.tgz", + "integrity": "sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reduce-flatten": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", + "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==", + "engines": { + "node": ">=6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", + "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ripemd160-min": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/ripemd160-min/-/ripemd160-min-0.0.6.tgz", + "integrity": "sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/rollup": { + "version": "3.20.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.20.2.tgz", + "integrity": "sha512-3zwkBQl7Ai7MFYQE0y1MeQ15+9jsi7XxfrqwTb/9EK8D9C9+//EBR4M+CuA1KODRaNbFez/lWxA5vhEGZp4MUg==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/sirv": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.2.tgz", + "integrity": "sha512-4Qog6aE29nIjAOKe/wowFTxOdmbEZKb+3tsLljaBRzJwtqto0BChD2zzH0LhgCSXiI+V7X+Y45v14wBZQ1TK3w==", + "dev": true, + "dependencies": { + "@polka/url": "^1.0.0-next.20", + "mrmime": "^1.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==" + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, + "node_modules/std-env": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.3.tgz", + "integrity": "sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==", + "dev": true + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-literal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.0.1.tgz", + "integrity": "sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q==", + "dev": true, + "dependencies": { + "acorn": "^8.8.2" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "node_modules/table-layout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz", + "integrity": "sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==", + "dependencies": { + "array-back": "^4.0.1", + "deep-extend": "~0.6.0", + "typical": "^5.2.0", + "wordwrapjs": "^4.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/table-layout/node_modules/array-back": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/table-layout/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^4.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/throat": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", + "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, + "node_modules/tinybench": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.5.0.tgz", + "integrity": "sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==", + "dev": true + }, + "node_modules/tinypool": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.5.0.tgz", + "integrity": "sha512-paHQtnrlS1QZYKF/GnLoOM/DN9fqaGOFbCbxzAhwniySnzl9Ebk8w73/dd34DAhe/obUbPAOldTyYXQZxnPBPQ==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.1.1.tgz", + "integrity": "sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==" + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", + "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-jest": { + "version": "27.1.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.5.tgz", + "integrity": "sha512-Xv6jBQPoBEvBq/5i2TeSG9tt/nqkbpcurrEG1b+2yfBrcJelOZF9Ml6dmyMh7bcW9JyFbRYpR5rxROSlBLTZHA==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^27.0.0", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "20.x" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@types/jest": "^27.0.0", + "babel-jest": ">=27.0.0 <28", + "jest": "^27.0.0", + "typescript": ">=3.8 <5.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@types/jest": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-typed-json": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/ts-typed-json/-/ts-typed-json-0.3.2.tgz", + "integrity": "sha512-Tdu3BWzaer7R5RvBIJcg9r8HrTZgpJmsX+1meXMJzYypbkj8NK2oJN0yvm4Dp/Iv6tzFa/L5jKRmEVTga6K3nA==" + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typeforce": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", + "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/typical": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ufo": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.1.2.tgz", + "integrity": "sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==", + "dev": true + }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", + "dependencies": { + "builtins": "^1.0.3" + } + }, + "node_modules/varuint-bitcoin": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz", + "integrity": "sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw==", + "dependencies": { + "safe-buffer": "^5.1.1" + } + }, + "node_modules/vite": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz", + "integrity": "sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==", + "dev": true, + "dependencies": { + "esbuild": "^0.17.5", + "postcss": "^8.4.21", + "resolve": "^1.22.1", + "rollup": "^3.18.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "0.32.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.32.4.tgz", + "integrity": "sha512-L2gIw+dCxO0LK14QnUMoqSYpa9XRGnTTTDjW2h19Mr+GR0EFj4vx52W41gFXfMLqpA00eK4ZjOVYo1Xk//LFEw==", + "dev": true, + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "mlly": "^1.4.0", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^3.0.0 || ^4.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": ">=v14.18.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "0.32.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.32.4.tgz", + "integrity": "sha512-3czFm8RnrsWwIzVDu/Ca48Y/M+qh3vOnF16czJm98Q/AN1y3B6PBsyV8Re91Ty5s7txKNjEhpgtGPcfdbh2MZg==", + "dev": true, + "dependencies": { + "@types/chai": "^4.3.5", + "@types/chai-subset": "^1.3.3", + "@types/node": "*", + "@vitest/expect": "0.32.4", + "@vitest/runner": "0.32.4", + "@vitest/snapshot": "0.32.4", + "@vitest/spy": "0.32.4", + "@vitest/utils": "0.32.4", + "acorn": "^8.9.0", + "acorn-walk": "^8.2.0", + "cac": "^6.7.14", + "chai": "^4.3.7", + "debug": "^4.3.4", + "local-pkg": "^0.4.3", + "magic-string": "^0.30.0", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.3.3", + "strip-literal": "^1.0.1", + "tinybench": "^2.5.0", + "tinypool": "^0.5.0", + "vite": "^3.0.0 || ^4.0.0", + "vite-node": "0.32.4", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": ">=v14.18.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@vitest/browser": "*", + "@vitest/ui": "*", + "happy-dom": "*", + "jsdom": "*", + "playwright": "*", + "safaridriver": "*", + "webdriverio": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "playwright": { + "optional": true + }, + "safaridriver": { + "optional": true + }, + "webdriverio": { + "optional": true + } + } + }, + "node_modules/vitest-github-actions-reporter": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/vitest-github-actions-reporter/-/vitest-github-actions-reporter-0.9.0.tgz", + "integrity": "sha512-tCC9exD0HLH4oTdZBuAMapwkVU9RAcU2GpbtO4a5nqYG4Ty2LW61z90srbWqk3rZHL8IxGo2WfL8r7U+EeeYug==", + "dev": true, + "dependencies": { + "@actions/core": "^1.10.0" + }, + "engines": { + "node": ">=14.16.0" + }, + "peerDependencies": { + "vitest": ">=0.16.0" + } + }, + "node_modules/vitest/node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", + "dev": true, + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true, + "engines": { + "node": ">=10.4" + } + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + }, + "node_modules/wordwrapjs": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz", + "integrity": "sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==", + "dependencies": { + "reduce-flatten": "^2.0.0", + "typical": "^5.2.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/wordwrapjs/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/sbtc-ops/smart-contract/package.json b/sbtc-ops/smart-contract/package.json new file mode 100644 index 00000000..e114a3cb --- /dev/null +++ b/sbtc-ops/smart-contract/package.json @@ -0,0 +1,50 @@ +{ + "name": "DecentralizedStacksPools", + "author": { + "name": "StacksDegens", + "email": "stacksdegensteam_gmail.com" + }, + "repository": "https://github.com/stack-sdegens/stacks-pools.git", + "private": true, + "engines": { + "node": ">=16" + }, + "scripts": { + "build": "tsc --build", + "test:dev": "vitest", + "test:ci": "jest --bail=1 --silent=false", + "fmt:check": "./node_modules/.bin/prettier --check .", + "fmt": "./node_modules/.bin/prettier --write ." + }, + "dependencies": { + "@hirosystems/stacks-devnet-js": "^1.6.2", + "@noble/hashes": "^1.1.5", + "@noble/secp256k1": "^1.7.0", + "@scure/base": "^1.1.1", + "@scure/bip32": "^1.1.1", + "@scure/bip39": "^1.1.0", + "@stacks/common": "^6.0.0", + "@stacks/network": "^6.0.0", + "@stacks/stacking": "^6.0.2", + "@stacks/transactions": "^6.5.0", + "@types/node": "^16.7.13", + "bitcoinjs-lib": "^6.1.3", + "buffer": "^6.0.3", + "micro-btc-signer": "^0.2.0", + "mocha": "^10.2.0", + "node-fetch": "^2.6.7", + "typescript": "^4.9.0" + }, + "license": "ISC", + "devDependencies": { + "@types/jest": "^27.5.2", + "@vitest/ui": "^0.25.8", + "jest": "^27.4.5", + "jest-github-actions-reporter": "^1.0.3", + "prettier": "2.8.1", + "ts-jest": "^27.1.2", + "vite": "^4.0.4", + "vitest": "^0.32.0", + "vitest-github-actions-reporter": "^0.9.0" + } +} diff --git a/sbtc-ops/smart-contract/settings/Devnet.toml b/sbtc-ops/smart-contract/settings/Devnet.toml new file mode 100644 index 00000000..14109873 --- /dev/null +++ b/sbtc-ops/smart-contract/settings/Devnet.toml @@ -0,0 +1,1611 @@ +[network] +name = "devnet" +deployment_fee_rate = 10 + +[accounts.deployer] +mnemonic = "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw" +balance = 100_000_000_000_000 +# secret_key: 753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601 +# stx_address: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM +# btc_address: mqVnk6NPRdhntvfm4hh9vvjiRkFDUuSYsH + +[accounts.wallet_1] +mnemonic = "sell invite acquire kitten bamboo drastic jelly vivid peace spawn twice guilt pave pen trash pretty park cube fragile unaware remain midnight betray rebuild" +balance = 100_000_000_000_000 +# secret_key: 7287ba251d44a4d3fd9276c88ce34c5c52a038955511cccaf77e61068649c17801 +# stx_address: ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5 +# btc_address: mr1iPkD9N3RJZZxXRk7xF9d36gffa6exNC + +[accounts.wallet_2] +mnemonic = "hold excess usual excess ring elephant install account glad dry fragile donkey gaze humble truck breeze nation gasp vacuum limb head keep delay hospital" +balance = 100_000_000_000_000 +# secret_key: 530d9f61984c888536871c6573073bdfc0058896dc1adfe9a6a10dfacadc209101 +# stx_address: ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG +# btc_address: muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG + +[accounts.wallet_3] +mnemonic = "cycle puppy glare enroll cost improve round trend wrist mushroom scorpion tower claim oppose clever elephant dinosaur eight problem before frozen dune wagon high" +balance = 100_000_000_000_000 +# secret_key: d655b2523bcd65e34889725c73064feb17ceb796831c0e111ba1a552b0f31b3901 +# stx_address: ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC +# btc_address: mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7 + +[accounts.wallet_4] +mnemonic = "board list obtain sugar hour worth raven scout denial thunder horse logic fury scorpion fold genuine phrase wealth news aim below celery when cabin" +balance = 100_000_000_000_000 +# secret_key: f9d7206a47f14d2870c163ebab4bf3e70d18f5d14ce1031f3902fbbc894fe4c701 +# stx_address: ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND +# btc_address: mg1C76bNTutiCDV3t9nWhZs3Dc8LzUufj8 + +[accounts.wallet_5] +mnemonic = "hurry aunt blame peanut heavy update captain human rice crime juice adult scale device promote vast project quiz unit note reform update climb purchase" +balance = 100_000_000_000_000 +# secret_key: 3eccc5dac8056590432db6a35d52b9896876a3d5cbdea53b72400bc9c2099fe801 +# stx_address: ST2REHHS5J3CERCRBEPMGH7921Q6PYKAADT7JP2VB +# btc_address: mweN5WVqadScHdA81aATSdcVr4B6dNokqx + +[accounts.wallet_6] +mnemonic = "area desk dutch sign gold cricket dawn toward giggle vibrant indoor bench warfare wagon number tiny universe sand talk dilemma pottery bone trap buddy" +balance = 100_000_000_000_000 +# secret_key: 7036b29cb5e235e5fd9b09ae3e8eec4404e44906814d5d01cbca968a60ed4bfb01 +# stx_address: ST3AM1A56AK2C1XAFJ4115ZSV26EB49BVQ10MGCS0 +# btc_address: mzxXgV6e4BZSsz8zVHm3TmqbECt7mbuErt + +[accounts.wallet_7] +mnemonic = "prevent gallery kind limb income control noise together echo rival record wedding sense uncover school version force bleak nuclear include danger skirt enact arrow" +balance = 100_000_000_000_000 +# secret_key: b463f0df6c05d2f156393eee73f8016c5372caa0e9e29a901bb7171d90dc4f1401 +# stx_address: ST3PF13W7Z0RRM42A8VZRVFQ75SV1K26RXEP8YGKJ +# btc_address: n37mwmru2oaVosgfuvzBwgV2ysCQRrLko7 + +[accounts.wallet_8] +mnemonic = "female adjust gallery certain visit token during great side clown fitness like hurt clip knife warm bench start reunion globe detail dream depend fortune" +balance = 100_000_000_000_000 +# secret_key: 6a1a754ba863d7bab14adbbc3f8ebb090af9e871ace621d3e5ab634e1422885e01 +# stx_address: ST3NBRSFKX28FQ2ZJ1MAKX58HKHSDGNV5N7R21XCP +# btc_address: n2v875jbJ4RjBnTjgbfikDfnwsDV5iUByw + +# [accounts.wallet_9] +# mnemonic = "lion stadium nation move truck family voice giggle quote round measure subway easily rebel disease protect weather install lend visual year three permit sight" +# balance = 100_000_000_000_000 +# # stx_address: ST2RF0D3GJR7ZX63PQ9WXVAPNG6EJCCR97NTVFGES + +# [accounts.wallet_10] +# mnemonic = "benefit raw tape silver pulp spell disorder sun gate finish plastic kangaroo please glare perfect cave sort weird ivory avoid text tent weasel book" +# balance = 100_000_000_000_000 +# # stx_address: ST3CD3T03P3Z8RMAYR7S6BZQ65QNYS9W18QHHJ4KM + +# [accounts.wallet_11] +# mnemonic = "aisle excuse bachelor depart live flag essay improve coffee lumber water acquire muffin medal cart crouch language squeeze satoshi staff mean rubber cereal fog" +# balance = 100_000_000_000_000 +# # stx_address: ST37ZZMNV6T9Q7SFMHA01070F2QKZ9XPF06WPD527 + +# [accounts.wallet_12] +# mnemonic = "viable episode stumble piano pill glad reopen arena collect crew unhappy hair ripple sad scout open miss title zero author nation number miss slogan" +# balance = 100_000_000_000_000 +# # stx_address: ST3K7SXC4V640YW26XBXR5PB21N7XFZ1ECKNF2C8B + +# [accounts.wallet_13] +# mnemonic = "net lottery abuse galaxy cook strategy fix lunar kite draw escape ostrich release journey bonus cycle vacuum leaf mule angle chuckle deposit employ erode" +# balance = 100_000_000_000_000 +# # stx_address: ST7MG0WM7AER9D9HV4QCMJDZDZBEEX5HMQTZZG7N + +# [accounts.wallet_14] +# mnemonic = "tuition coral fashion coach elegant benefit left under pet broken battle cloud supply latin pyramid denial soldier over equip title end marriage problem local" +# balance = 100_000_000_000_000 +# # stx_address: ST2PKP8HH95VTP6MFJXWND57NSY8S22HEC4D5WP6E + +# [accounts.wallet_15] +# mnemonic = "ask beyond whisper bless poverty immense junior shiver type minor make debris sound hour seven citizen order conduct change bacon stay acoustic key canal" +# balance = 100_000_000_000_000 +# # stx_address: ST3EHE9BKW6662HM8GTTBQKAVFCZR2HTEXVYYZW6Z + +# [accounts.wallet_16] +# mnemonic = "biology soul mouse mimic razor unique eyebrow base lamp amount brush type track woman neutral lizard island easy rhythm foam divert mosquito welcome lesson" +# balance = 100_000_000_000_000 +# # stx_address: ST2VY5NGK04RYHRM2ZVJPJN11C34KDEKK2RMFG69Q + +# [accounts.wallet_17] +# mnemonic = "purchase staff explain trap whip joy logic height steak increase tone update accuse delay fury harbor solve survey history similar monitor scrub control check" +# balance = 100_000_000_000_000 +# # stx_address: ST1AP1SWES8TFVX3WKYS3JT3530DBMDWCK62QZNNQ + +# [accounts.wallet_18] +# mnemonic = "script fix hover stomach limb response spawn foam mention battle together exact main believe labor magic unlock jacket raven wife bread want heavy worth" +# balance = 100_000_000_000_000 +# # stx_address: STJS6RB142BYG7ZSSA003N7MT1TSWTGTR78G0MCJ + +# [accounts.wallet_19] +# mnemonic = "master squirrel salt pitch wrist crazy embrace coffee below beach huge apology paper foster vast leisure desk battle letter gain stumble name floor lyrics" +# balance = 100_000_000_000_000 +# # stx_address: ST10N2K2GPCN67C8SQAS811PT2KCDEJ1S0QD6MCMC + +# [accounts.wallet_20] +# mnemonic = "lava rug rude energy tunnel wolf live moral color arrive raven glory shoot rural kid still noise potato barely taxi expand deposit grow firm" +# balance = 100_000_000_000_000 +# # stx_address: ST32DZP6WV5851N9QHSXAVHT2H41PZNB5MDT295VJ + +# [accounts.wallet_21] +# mnemonic = "add abuse photo shoe timber cup filter appear flame myth charge empty wreck copy canoe umbrella crop damage senior typical return spare armor evoke" +# balance = 100_000_000_000_000 +# # stx_address: ST361ME06ZBVNZXKF5W095QBMFZ2HQ3F48DBBP55D + +# [accounts.wallet_22] +# mnemonic = "whale frown brush toe supreme hill trigger party cram display assume cool gorilla mirror scissors corn person tired solve soft merit nut bundle grass" +# balance = 100_000_000_000_000 +# # stx_address: ST2CP5W2G0DSREJVWWJP6TTHEAFW98K0XW84J2Q4T + +# [accounts.wallet_23] +# mnemonic = "board uncover grow illegal sand mushroom fabric case addict tower embark galaxy educate dance cupboard exile boring connect flash unit pony embody toast bring" +# balance = 100_000_000_000_000 +# # stx_address: ST1A844HC4Y55YAYH0XE4N2A98MNV6N27SF7Z0CEK + +# [accounts.wallet_24] +# mnemonic = "extra dream emerge open cram danger vessel diamond merit camp sign exact any shy desert enact wrist universe settle bleak toe blossom cry escape" +# balance = 100_000_000_000_000 +# # stx_address: ST13JYNQXJPNN8XQBPBQH9SWDKA8F2BJG10TTW8K5 + +# [accounts.wallet_25] +# mnemonic = "liberty swear tide potato cannon exercise vendor nation coconut circle few rude episode point power field boil sentence jungle change floor gain diamond twice" +# balance = 100_000_000_000_000 +# # stx_address: ST1ZT94HHXNQ9QKH0TREWQ01V2Z1KC44M05EJZ4C5 + +# [accounts.wallet_26] +# mnemonic = "where purchase lobster clump try adjust rally few stock burst tissue grain outer coral ready book squirrel olive pudding erupt vanish danger visual place" +# balance = 100_000_000_000_000 +# # stx_address: ST3CTFQ90STH7KJ64B3CZGQRDSPFAPHZ7884EHC51 + +# [accounts.wallet_27] +# mnemonic = "inch order borrow high sorry clog salt pretty clarify dinosaur nominee draft laundry radar buddy doctor detect rubber hamster swap swing discover educate marine" +# balance = 100_000_000_000_000 +# # stx_address: ST1K9H8ZS8KRD9CXM5WQCMM9D44MY9CQ18SPQ18JY + +# [accounts.wallet_28] +# mnemonic = "table scatter neither rich habit labor clay scout glare exit quiz donkey also antenna step catch shop number craft rifle exercise alley climb inspire" +# balance = 100_000_000_000_000 +# # stx_address: ST219BCA7C5R21Q4QA1903M2X57DTYANQDXV95AY1 + +# [accounts.wallet_29] +# mnemonic = "run try pretty sight draw clarify amused process aware sound gadget gossip column list lunch casino describe also absent observe limit turn spring medal" +# balance = 100_000_000_000_000 +# # stx_address: ST37ENW53XXK7MR4PG41TB0FXXQ8X63V9FTY9R3E7 + +# [accounts.wallet_30] +# mnemonic = "fortune garden horse swarm hour coyote cushion actress minor tree vapor provide general help weapon view gift valley palace include casual actress patient urban" +# balance = 100_000_000_000_000 +# # stx_address: ST26EEZN4SPPP6VFWRG26DGX9JAT10GEN6XFEJSJ1 + +# [accounts.wallet_31] +# mnemonic = "glimpse student snap genuine taxi genuine useful erupt once kit tired display token update connect fancy powder symptom double describe strike only still mechanic" +# balance = 100_000_000_000_000 +# # stx_address: STY6BX433BDNTMN7WNBQ38ZYXDV25ACRP7TB01C8 + +# [accounts.wallet_32] +# mnemonic = "modify bright custom ancient wrist negative memory school okay talent sheriff limit name sniff radar prepare repair sand rabbit essence amateur twist super portion" +# balance = 100_000_000_000_000 +# # stx_address: ST19PR9QMQK3SF7R35WTG96F147HSFV16MH5P8Q18 + +# [accounts.wallet_33] +# mnemonic = "nature success brown size group rose company cost action chunk obey okay garment auction move mule smooth pencil real liquid similar lake horn fish" +# balance = 100_000_000_000_000 +# # stx_address: ST2QB4R01DRY9GYW8R8VPJ7WNCDTYK6Z1EHJT2P26 + +# [accounts.wallet_34] +# mnemonic = "sustain emotion change dirt rally unknown very drive dilemma pizza spread scare stem mechanic core manage world name actual record trap artist glove fire" +# balance = 100_000_000_000_000 +# # stx_address: ST2E0J70Y06D20TY51ZBHPN2ZD9S08VXZBXT5RGFP + +# [accounts.wallet_35] +# mnemonic = "fire slim aunt van kangaroo habit sport sure urban wheel scout valid wedding diary member kitchen find submit parade sell major exhibit wear try" +# balance = 100_000_000_000_000 +# # stx_address: ST1NWYATP33Y56DE2JP2300MFHPYWB29KWN2PGYKK + +# [accounts.wallet_36] +# mnemonic = "frown digital myth pottery cousin pelican clever wonder piece wear clerk uniform glare juice stem exhibit biology enjoy dash cigar report title maple ice" +# balance = 100_000_000_000_000 +# # stx_address: ST25Q6FYQTPYQVB3ZP8NRT0FYTKKWM0R6RTA06G3F + +# [accounts.wallet_37] +# mnemonic = "august sunny spare scorpion skull anger excess prevent method slush mutual ketchup crew split multiply sorry wagon replace portion frequent ethics easy crumble aspect" +# balance = 100_000_000_000_000 +# # stx_address: ST343RAG54HF5JRX4M1RT2BW8582PTX2C9DYACM9H + +# [accounts.wallet_38] +# mnemonic = "hazard present snack woman alien retreat divide basic nice welcome round amused obvious nut cereal action top high duty help balcony quote shock improve" +# balance = 100_000_000_000_000 +# # stx_address: STGTQ454731S4XX3A9VRAAAP7RYT0RCJBBQ1DJ1J + +# [accounts.wallet_39] +# mnemonic = "sock budget rebel patient people source sport farm post airport area option boil off brief off habit type problem glare distance brief category bounce" +# balance = 100_000_000_000_000 +# # stx_address: ST27A7VMKV7KKQH8SDM3TJRJJ8SWBGR7MFFEXXTWH + +# [accounts.wallet_40] +# mnemonic = "viable liar doctor autumn risk joke brown system clog wine fat life kiss cushion episode reveal gym pear uphold length humor copy jar poem" +# balance = 100_000_000_000_000 +# # stx_address: ST1YHDJMMZSA2FFRF5AXAM0SRF9WMQHKS69FJ1SXS + +# [accounts.wallet_41] +# mnemonic = "assist oval attack various stuff chest rabbit cool tenant powder invest arena pioneer siege tiny visual claw often day assist spoil oval stairs glow" +# balance = 100_000_000_000_000 +# # stx_address: ST2F4Y9XFQMH72NVSS1WMZQBS7CKPA1ZKR77CFY1M + +# [accounts.wallet_42] +# mnemonic = "sudden habit repair spawn finger smoke above make foot unique scan wagon soccer slide knee usage bubble reject turtle toilet poet carry symbol marriage" +# balance = 100_000_000_000_000 +# # stx_address: ST2NR8V7ZZKHVK4SHSHX36GZS9C7FB8AW59AA52G5 + +# [accounts.wallet_43] +# mnemonic = "inject velvet exotic jazz pistol into crime switch pave virus total brain false scissors unfold potato casual mask unhappy cube shy please pumpkin width" +# balance = 100_000_000_000_000 +# # stx_address: ST28V3XE7F16RYZYBQP6HATHAXF05MYRMS7P9JVBA + +# [accounts.wallet_44] +# mnemonic = "allow alpha future spy wolf west wine opinion segment buzz blanket license movie float abstract renew grocery clerk ankle multiply marine fantasy nice patrol" +# balance = 100_000_000_000_000 +# # stx_address: ST1GW4TR2DR4DVPR9Z3GCMZTFS8KQQWAPHF70CA1F + +# [accounts.wallet_45] +# mnemonic = "bread mixed differ shallow orient salt tumble ginger vibrant future tomorrow similar promote congress useful treat outdoor science exclude uniform balcony boost decorate timber" +# balance = 100_000_000_000_000 +# # stx_address: STKEXSABYW6111F7MJ696SS5N1YGN8CDEVMP49Q0 + +# [accounts.wallet_46] +# mnemonic = "alcohol please destroy north rug explain october predict board response siege jump baby guide praise sketch virtual attend auto kidney glass jacket midnight property" +# balance = 100_000_000_000_000 +# # stx_address: STAK99C5P0J3A9Y5DNPWETNC82JPC6SQ4F88413W + +# [accounts.wallet_47] +# mnemonic = "post cruel comic forward shine child smart veteran easy legal spirit mother soccer stage danger shoulder captain disorder language luxury champion salmon field true" +# balance = 100_000_000_000_000 +# # stx_address: ST3R2ZQHZAJXYYKT83Q0SZTTZDXEKF0V77QA6K713 + +# [accounts.wallet_48] +# mnemonic = "wise scout runway fluid squirrel wing lock stem dutch team sudden tired process glue drink lemon upset shadow naive honey surge point praise airport" +# balance = 100_000_000_000_000 +# # stx_address: ST34S6C5J740H3F06NT2822ZK1KAHC7E21T0X8CTX + +# [accounts.wallet_49] +# mnemonic = "struggle ladder fantasy cart advice umbrella render mechanic skin renew vocal shadow blue saddle flag lamp aerobic relief cabin tuition local relief bring punch" +# balance = 100_000_000_000_000 +# # stx_address: ST3J6BAT10DSN9SBKAGE3TK15W95PCW20NPV34Q1C + +# [accounts.wallet_50] +# mnemonic = "impose monster oblige they delay device clean vacuum nominee august siege journey injury sing audit approve gentle wedding place body saddle august wool tuna" +# balance = 100_000_000_000_000 +# # stx_address: ST23C73YP53S4K7780W71J8NJACZJ3621WN1T33V2 + +# [accounts.wallet_51] +# mnemonic = "armed win attack domain wrong first token around sea confirm cup flame arrange tilt scissors series toy wisdom pull jar course mobile hamster salmon" +# balance = 100_000_000_000_000 +# # stx_address: ST3C3XY6ECHZJ2RDBSF31404DE1SMSTVBF1ZPG5SH + +# [accounts.wallet_52] +# mnemonic = "federal paper volcano casual want muscle run rocket suggest august word gain fiction museum engine immune below thunder unusual twelve sustain rude blanket excite" +# balance = 100_000_000_000_000 +# # stx_address: ST2A7RX6C1WJA0ZYF21R04K1X4AKFDZXFWKY2GRSW + +# [accounts.wallet_53] +# mnemonic = "obvious try artwork olympic time coach battle park hobby hundred wild mind victory electric truth goat limb gaze grit dial culture digital reflect pill" +# balance = 100_000_000_000_000 +# # stx_address: ST27JKP4CEPSHXQ317AAJQ2VGVDA5EBER7ZMBGTVF + +# [accounts.wallet_54] +# mnemonic = "love spoon abuse say banner coconut symptom pole skin ribbon pumpkin rally treat flee anger silent boil twelve ask confirm tomato dragon layer label" +# balance = 100_000_000_000_000 +# # stx_address: ST2334V3B3X4HJQYQCYA23XGT589X6GRKZ8CRT8YD + +# [accounts.wallet_55] +# mnemonic = "wide pitch plunge equal school puppy onion retire clock toddler census jealous verify reopen oblige sunny carpet mammal dove polar picnic square worth lock" +# balance = 100_000_000_000_000 +# # stx_address: ST35X8BXHXVKW0CFZBV2B1TJJTHD7M89CD1ZFD759 + +# [accounts.wallet_56] +# mnemonic = "actress start jelly rack outer net whip advance horn slim daring flee exist enroll ball eight cattle saddle bridge more alien radio hat trash" +# balance = 100_000_000_000_000 +# # stx_address: ST1P0W50P6MFKD4YKNCJ437SJ7037TTBC049ZR409 + +# [accounts.wallet_57] +# mnemonic = "nation arrow peasant crowd pistol decorate venue soldier vacuum license cotton object pudding actor opinion refuse tag close wire maze derive visit suffer fabric" +# balance = 100_000_000_000_000 +# # stx_address: ST26F9PFXR1MFPF9MBZ1VA4GETAWCBXS4ERD5EM3E + +# [accounts.wallet_58] +# mnemonic = "weird spare city arrest snack main slender ignore spice theme morning miracle tube pelican original daughter spawn process pulse silk enlist aware timber hill" +# balance = 100_000_000_000_000 +# # stx_address: ST2HY3P2SD4SZV55E91WN3K4C5JKHAZ1XSVWKYYW1 + +# [accounts.wallet_59] +# mnemonic = "chaos result cherry insect number object coast priority toilet wrestle lunar couch hat kite payment solid illness dawn yard dune need crunch twenty waste" +# balance = 100_000_000_000_000 +# # stx_address: STKS8VV5HA7K1NJM57BKWBYG23VDDKFF2G8BM9TC + +# [accounts.wallet_60] +# mnemonic = "resist casino fork medal theme jacket this rich huge egg domain basket obvious inmate summer vital stadium reunion disease before myth problem all control" +# balance = 100_000_000_000_000 +# # stx_address: ST8Y4ZWX5ZEVG224CQ4EXD1B5H5HWY59JMFSFCBY + +# [accounts.wallet_61] +# mnemonic = "hair dilemma wreck play fresh push junk push pigeon series loud pulp release switch also clog ocean business buyer boat law twin front lucky" +# balance = 100_000_000_000_000 +# # stx_address: ST3FSXHEE9CBR1QTK31150FEB0CZM290QPX07WSAC + +# [accounts.wallet_62] +# mnemonic = "cereal auto indoor forest isolate annual lesson rebel wear scan ask fame common famous barely laptop poet term kite axis very doll speak bullet" +# balance = 100_000_000_000_000 +# # stx_address: ST3RDDM60SFZY7D1CFEN69EK2P17GB9WKTQNFTBPZ + +# [accounts.wallet_63] +# mnemonic = "thrive opera dose owner cook caught merit move exercise fame mail spatial program sun empower excess foot praise reason clip profit olympic when render" +# balance = 100_000_000_000_000 +# # stx_address: STNJHGHHXR51WW7N0JDANT5DT1GK39QKHEVH5AD4 + +# [accounts.wallet_64] +# mnemonic = "bean hollow mass bullet stove outdoor regular bring educate bird tissue scare town throw hurry enforce clean pool work bunker evidence lamp favorite scorpion" +# balance = 100_000_000_000_000 +# # stx_address: ST1TZ6MCJZ066RM3JTP1KNZGV6SD068A0D7AAH71G + +# [accounts.wallet_65] +# mnemonic = "also flash ridge shoulder harbor net dry badge joy sausage armed now curve napkin retreat return priority note fiction elbow junior jelly actual mammal" +# balance = 100_000_000_000_000 +# # stx_address: ST298G8HHCSAMWRX5JGYNX2KDC6YCV62RBMP6ZFGQ + +# [accounts.wallet_66] +# mnemonic = "evoke club emotion offer column trust joy solid future scout year wall iron popular scrub deliver invest razor album chronic shuffle evolve notable call" +# balance = 100_000_000_000_000 +# # stx_address: ST2XNVGS7K8FYJQER6BMVX4SGKM9MYHVGVS00S7E7 + +# [accounts.wallet_67] +# mnemonic = "genre bundle sphere helmet rocket all service tennis minor shrug quality grape upgrade season roast gossip cousin cube walk admit tube clerk trip aspect" +# balance = 100_000_000_000_000 +# # stx_address: ST2QN5GJ0BRH5MFHMT3RAG1GG2XF7W3414G1666H0 + +# [accounts.wallet_68] +# mnemonic = "file juice tent track slab staff aerobic parrot system affair hybrid garlic rotate permit echo genius link dinosaur wrap sketch recycle correct raw deny" +# balance = 100_000_000_000_000 +# # stx_address: ST10A4GXZ6YF5A5CZY5F0V10WTJ5C21JZWKZT7TE0 + +# [accounts.wallet_69] +# mnemonic = "whale peasant obtain cheap park fee merit carbon faculty tilt conduct road skate round sauce student either output boil draft auto dust fruit case" +# balance = 100_000_000_000_000 +# # stx_address: ST2VM2PP828DAQQ14XM5FRZ0RMFNKT1HAAD6HNXJD + +# [accounts.wallet_70] +# mnemonic = "orchard grow harvest among eternal topic upset alter minor match embody sun bachelor worth print record slender popular manual lizard acoustic indicate letter then" +# balance = 100_000_000_000_000 +# # stx_address: ST3AXH10DE1D09XMBGV9A3Y29TVEBGHG9V79PNZA9 + +# [accounts.wallet_71] +# mnemonic = "leave energy midnight upon solar axis olive insect able obey mule expand public divorce mechanic lazy jazz cabin dilemma vanish pride fuel joy style" +# balance = 100_000_000_000_000 +# # stx_address: ST3KEKZ2EGT7NDF97C8SA0MRT2S6VD5K6DVK0ZQDW + +# [accounts.wallet_72] +# mnemonic = "nose donkey heart spring arena dilemma gasp shiver afford staff taste staff dance good stay response rule frame fire focus clump drip filter month" +# balance = 100_000_000_000_000 +# # stx_address: ST2ZBVH1NJ88P0942TF1DVT58F1DJ4M2CP5EX66G1 + +# [accounts.wallet_73] +# mnemonic = "pudding effort web chief viable bonus sausage girl hat remove behave banana width current sorry please dry tone matrix stick boat luxury shuffle fire" +# balance = 100_000_000_000_000 +# # stx_address: ST3QXN8YT9YFTEKFD4FB97PGMS21H3MZ6FR8XS78A + +# [accounts.wallet_74] +# mnemonic = "intact come canvas giggle skill weapon defense viable before attend crawl quit shock foot state stumble force enforce track tag find awkward gate memory" +# balance = 100_000_000_000_000 +# # stx_address: ST2NJDJ7VZ4ADR7C5SN9X8T1341DCEM7YPNJM8E81 + +# [accounts.wallet_75] +# mnemonic = "sample proof below attend action adapt negative depart pupil wild number toss near laundry powder illegal argue shrug joy true relief culture canoe defy" +# balance = 100_000_000_000_000 +# # stx_address: ST1HE981KNJDG670ESVP0DEAEC68FSTBN4NGHR5FP + +# [accounts.wallet_76] +# mnemonic = "milk cloth manual soon there hood peasant uncover alone evil addict belt buffalo comfort machine bamboo perfect naive clever mimic please infant beauty banner" +# balance = 100_000_000_000_000 +# # stx_address: STTY1EAVR5C23XR34JK8GES3TNX4TY3HTQ17CPD7 + +# [accounts.wallet_77] +# mnemonic = "purity admit report toddler wash crack have parade used festival helmet punch cradle quiz dose brass party van wave impose planet goat proud dinner" +# balance = 100_000_000_000_000 +# # stx_address: ST1P2NB6GEGYB67AMHAD8Z72EHKM741A5RZXPGAF2 + +# [accounts.wallet_78] +# mnemonic = "member chest same gravity crucial couch pizza little bus friend pilot beyond idle hip rate decrease gauge heavy convince waste behave display series coin" +# balance = 100_000_000_000_000 +# # stx_address: ST2QQZ6DGH4GPJD8YZG5TG99BA4M327SPDJJJG6CT + +# [accounts.wallet_79] +# mnemonic = "doctor suit lamp tonight submit sleep benefit invite organ catalog elegant team mutual trial fall alert parade tragic hazard card shoot song best tiger" +# balance = 100_000_000_000_000 +# # stx_address: ST37BZYYSHZXT33BK47QD6KMZTPFG7Q77P2APP0M7 + +# [accounts.wallet_80] +# mnemonic = "language maze shuffle latin hundred unaware race abuse humble force latin raven add middle book music lobster dolphin leisure code blush pipe decide drip" +# balance = 100_000_000_000_000 +# # stx_address: ST23ZMRTEFMDXFF6X0V84WD7NB5X00B80BCNAX0HM + +# [accounts.wallet_81] +# mnemonic = "twin diamond genre father expect media taste glory habit output game eagle vehicle december enforce zero shallow like situate someone exile balcony project amateur" +# balance = 100_000_000_000_000 +# # stx_address: ST1BD939ZBWSZFABB4E3HCWNG4B4ZWTQGF2AD95ZV + +# [accounts.wallet_82] +# mnemonic = "hill banana giant myth admit bench economy harsh afraid broken bicycle whisper scale grow parent layer blossom crash early plastic spike barrel mention phone" +# balance = 100_000_000_000_000 +# # stx_address: ST3C6AVDD1Y3K762CQWVE93JNHA3T4ZX9K4V7QE17 + +# [accounts.wallet_83] +# mnemonic = "wisdom approve acid finger surface people diet decide six arch motion awkward retreat sleep bless because treat equip host device gadget short peace chronic" +# balance = 100_000_000_000_000 +# # stx_address: ST3NF1R9N8JR1TRPDF1JC5PVZW04BAFM1YG4506J6 + +# [accounts.wallet_84] +# mnemonic = "foot immune honey clerk inherit unique embrace charge rifle flush nerve alter floor pet toast false wisdom awful diary rich donkey urge envelope token" +# balance = 100_000_000_000_000 +# # stx_address: ST2K64FRQ5Y330WXN38SQDB1EJ23PJEYAER27TZMV + +# [accounts.wallet_85] +# mnemonic = "person parrot quantum gravity orient item hip movie velvet text pass dress risk news odor assume donkey switch ketchup visa ask federal april crane" +# balance = 100_000_000_000_000 +# # stx_address: STTM0M6136TGZC0E95P1HQ54T4K4PS6CMGJ625QV + +# [accounts.wallet_86] +# mnemonic = "public metal similar reflect gentle oil doll dentist jazz diary outer consider culture term gorilla final fat alter spare mule stomach honey excuse taste" +# balance = 100_000_000_000_000 +# # stx_address: ST33BJ4396XVCJN6DZRTG2Z18BYSE4DS3JZG2A8K0 + +# [accounts.wallet_87] +# mnemonic = "priority car rich poet tornado smile ask off cricket ocean whisper bridge submit twelve wolf section orient fresh print jewel burden buyer brain sea" +# balance = 100_000_000_000_000 +# # stx_address: STJCVF6XRMWYFABAEWK9PFQB1EMP8C6F0CSHGS9S + +# [accounts.wallet_88] +# mnemonic = "purpose curious bounce swallow plunge reduce bachelor prepare print icon number live front fault fire fiber way orient economy apology shuffle erosion slush protect" +# balance = 100_000_000_000_000 +# # stx_address: ST2REAZT5M7KGNMFQNJ61PFPP5KZVVHG0BQBZ93BM + +# [accounts.wallet_89] +# mnemonic = "inner sure cave mind silver remove clip ready prison arrange duty pen lemon mercy desert install inform top wool tumble need hip aim egg" +# balance = 100_000_000_000_000 +# # stx_address: ST16JBADHZRWAP3PV82RKDMQCKS8AVR15W1WEDB9A + +# [accounts.wallet_90] +# mnemonic = "plastic wire novel suit bundle smooth amateur pencil today outdoor sadness truck coast creek coast cry veteran client remove skate amazing easily minute theme" +# balance = 100_000_000_000_000 +# # stx_address: STS3BB2QRH8GAJB0E3DDW6GPMMJM41QN7RHTB5DQ + +# [accounts.wallet_91] +# mnemonic = "idea check chief shield damage enforce truly valve era bulb wheat joke tiny fuel evoke rate exotic muscle rhythm six actor real slide equip" +# balance = 100_000_000_000_000 +# # stx_address: ST20R0VWB63M90HR3G9MCZ0MKK8PRCZXCH0Z6NXG + +# [accounts.wallet_92] +# mnemonic = "pause notable advice brand lens table subway sell swallow range gasp undo uphold tank minimum hunt disease job olympic topple cloud orphan win rude" +# balance = 100_000_000_000_000 +# # stx_address: ST2DP0RZ10NXFFW72JR4R2PMJ0SZYWHG8C336YXM3 + +# [accounts.wallet_93] +# mnemonic = "coconut slogan ranch salmon act claw blossom grape swim year wing rotate minimum color wrist air swing plunge pumpkin pepper shed smile novel bring" +# balance = 100_000_000_000_000 +# # stx_address: ST9TK8DEW8BH5JGRTQSBN3FG9FRB3TMK52Y0KHMY + +# [accounts.wallet_94] +# mnemonic = "celery ill fatigue camp picture family black bullet ladder where antenna engage real people chicken chicken mind spice work need anger pass jungle divorce" +# balance = 100_000_000_000_000 +# # stx_address: ST3V81THYG6EZVM97E9EGK2B6SJ763HHQW4NHPN11 + +# [accounts.wallet_95] +# mnemonic = "roof call glove round frog visual discover planet negative room science debris tide broccoli envelope disease future park story shift main estate beyond beauty" +# balance = 100_000_000_000_000 +# # stx_address: STAD291HET5ERY4AW0W3BEG2609XV1MQB4AVCWM1 + +# [accounts.wallet_96] +# mnemonic = "clip bird kite among uniform lock guess fruit elegant build science master horn example flip crop chapter divide tide attack wedding duty cheap method" +# balance = 100_000_000_000_000 +# # stx_address: ST23ZDV1FXXTTHV229YW30MF92AEHV7A75F0NW66V + +# [accounts.wallet_97] +# mnemonic = "bind evil tool expose live slogan pool stairs truck age exotic foster glare soap drink maple soap pretty hockey organ scorpion lumber copy side" +# balance = 100_000_000_000_000 +# # stx_address: ST1MM9S1Z7CRP0DBG9A6C3E79RW941NCABDF0D427 + +# [accounts.wallet_98] +# mnemonic = "isolate pluck execute fan wheel series alcohol annual other add allow expect quiz inspire swift ramp battle dry zebra hill click dial chimney jungle" +# balance = 100_000_000_000_000 +# # stx_address: STTQ07A0BQTEX7SY2JE65TSRAMQB5BVY6S850AWQ + +# [accounts.wallet_99] +# mnemonic = "disease grab list stay vast settle tomorrow example grain sadness property prison credit original spare crash trumpet onion million rookie cave viable orphan path" +# balance = 100_000_000_000_000 +# # stx_address: STKF3HBXEV6XMD1X1RV1B54SZX7DG501R9ESAXFM + +# [accounts.wallet_100] +# mnemonic = "mixture position practice critic void wish sausage private mean protect wrong oven load addict plunge occur fun rubber elegant lounge radio claim crazy trim" +# balance = 100_000_000_000_000 +# # stx_address: ST3ZTM14Y6EEBR011BNCFYJXGSDNE7K71QZ5MBYH0 + +# [accounts.wallet_101] +# mnemonic = "auction empower anxiety dice normal shine zone drip joke worry ancient inquiry card lake liberty online fantasy shed point roof pilot object retire camera" +# balance = 100_000_000_000_000 +# # stx_address: STN7ES6EGND0PVPFM7D2C5Q6B8PTA7AMYGW537KZ + +# [accounts.wallet_102] +# mnemonic = "rebuild enough impact effort void affair ramp chest about anchor discover nerve jelly frost oven finish brain critic faint twin benefit tomato place damp" +# balance = 100_000_000_000_000 +# # stx_address: STV1HKSE7C9RQ9SY32PYYFC5HV4ADNQQFB617TW + +# [accounts.wallet_103] +# mnemonic = "claw chaos bulb skirt garment predict company vacuum axis gasp peasant world oil funny mask phone harsh fiscal clap shop verb century chair copy" +# balance = 100_000_000_000_000 +# # stx_address: ST2FFE62QSC896C9A6X08D95FGX9ZGY3J8HGMYREC + +# [accounts.wallet_104] +# mnemonic = "infant spice mention punch novel shoulder high primary prosper holiday dilemma shift scout image negative stage clap human slice crew load equal salad inflict" +# balance = 100_000_000_000_000 +# # stx_address: ST1Q8Q8CBC1DXSWCNWT22HXD82WE988VKDGY7X8RD + +# [accounts.wallet_105] +# mnemonic = "anchor finish wasp gold solution present light wrestle clump daughter infant figure member spice manual tortoise grit guilt much acquire purity business punch armor" +# balance = 100_000_000_000_000 +# # stx_address: ST5KRHNTH1Z116PH6ZPWG2N22K7373BGSVPMSFAT + +# [accounts.wallet_106] +# mnemonic = "maze nephew account surprise season people shell eye spare tissue twist attitude into machine shrimp proof snack slush assault end knock exhibit either matrix" +# balance = 100_000_000_000_000 +# # stx_address: ST3AZ38RBMJNZ3KE0S3P3JZQX4FBRHZZ6WGKYZ9J0 + +# [accounts.wallet_107] +# mnemonic = "seminar mimic awkward inmate sweet aisle empty cradle example bus sword pill spice trigger violin rural ghost quiz index ahead energy shoot brain ritual" +# balance = 100_000_000_000_000 +# # stx_address: ST3FD1J7YFD32ACJGDMSGVCXTSKD0NFFWY99NENYX + +# [accounts.wallet_108] +# mnemonic = "tool segment film buffalo page brick source rocket aim food right jump monster offer muscle author business grass airport speed misery panel bid solution" +# balance = 100_000_000_000_000 +# # stx_address: STS5KVVHHJP17HCK0S53XK8NWAAX0RPEK52WZ12S + +# [accounts.wallet_109] +# mnemonic = "hazard ancient target enforce orbit bundle predict mouse zebra liberty stone hobby please donor knock dizzy author dutch traffic rotate away special drop melt" +# balance = 100_000_000_000_000 +# # stx_address: ST2AGM9NF5T056FJ7V4N4QZATDESBYZT7XR94HE2D + +# [accounts.wallet_110] +# mnemonic = "alarm borrow effort park they peanut scene act chef split slogan transfer diet engage depart rifle lecture order trigger trim swing must real then" +# balance = 100_000_000_000_000 +# # stx_address: ST1EJ4ZQ1MVNR5Q0M1KZVSWZ3VB4E8T64NTFQX2Y3 + +# [accounts.wallet_111] +# mnemonic = "orient drop govern orphan peanut prosper village page marine diet hill cushion nature crane six bulk ice draw choice apart large drum shallow virtual" +# balance = 100_000_000_000_000 +# # stx_address: ST1K4JX669PQ8B898NMQF6Z68EW8SX3W00K5PBH6N + +# [accounts.wallet_112] +# mnemonic = "seek because tree rib swift clean coin side mystery then pioneer excite method memory estate wrap endorse electric tennis flee detail immune fix pink" +# balance = 100_000_000_000_000 +# # stx_address: ST3XVM6V3VZCP477F3VFRX5SBY67FZHK6MAWECWVR + +# [accounts.wallet_113] +# mnemonic = "supply dolphin crane motion rotate hungry bus device inner pottery noodle annual patrol lonely rubber book siren burger system typical discover habit once vendor" +# balance = 100_000_000_000_000 +# # stx_address: ST4RR09DQHJ5CJ2CS8NJ97BJZTGZNAXEX1SP1C3J + +# [accounts.wallet_114] +# mnemonic = "purpose tree quit jeans scissors heavy path forum census shallow nurse sample today choice live exotic fame repeat over survey business smoke pig must" +# balance = 100_000_000_000_000 +# # stx_address: ST2MZTG9G098CA54VQQFNYG50XXZE0VYHYSMZ59Y3 + +# [accounts.wallet_115] +# mnemonic = "security tape lens offer trouble assault like beauty mass jealous sustain rival orange arrest spatial luxury shove buzz whisper rice reveal seven rely inherit" +# balance = 100_000_000_000_000 +# # stx_address: STS2QPPFZDGFYMJTYA541WYTV33EFGH5NGBX03VW + +# [accounts.wallet_116] +# mnemonic = "believe click attitude enlist innocent truly above theme option much fashion lion grunt trick ghost clock mammal merge property traffic blouse bounce address damage" +# balance = 100_000_000_000_000 +# # stx_address: STM925RXS61D23EYMVHR9MJJTHZ0WXZW931WYMGQ + +# [accounts.wallet_117] +# mnemonic = "since egg forum gold tooth talk ability market devote share web assault tattoo ability suit true volcano pause close opinion correct negative divert head" +# balance = 100_000_000_000_000 +# # stx_address: ST2KPGZF06HYHN2JEYWZJNNP0PXJF5ZM2KZDX08N + +# [accounts.wallet_118] +# mnemonic = "throw robust merit sea arrest own custom unable virtual predict hunt soul mind shaft angle acid good ring toss blossom slot tray nest repeat" +# balance = 100_000_000_000_000 +# # stx_address: ST2XGATEJT79EK4442124T63GNJ9C6DF1DWFHCP2K + +# [accounts.wallet_119] +# mnemonic = "helmet cost castle they choice only opera oak shell lounge flag pass gift dish paper chaos garage news initial antenna build poverty despair glory" +# balance = 100_000_000_000_000 +# # stx_address: ST2M3VK6ANQ7Q1JHPH2CT6B1N9HQYXCY4BSDAJ9XB + +# [accounts.wallet_120] +# mnemonic = "rent remind situate absorb source caught top winner lift deposit acquire ethics slogan machine beach emotion among crumble thunder waste guilt goddess hour dry" +# balance = 100_000_000_000_000 +# # stx_address: ST1DPHXXDS033CP10EJ5XKJ6V6R25SGYAYQM8CVAF + +# [accounts.wallet_121] +# mnemonic = "march canyon all ostrich until bomb unaware task swallow wise gas nothing steak tank drink bench inherit fever task clever cactus nature trigger demand" +# balance = 100_000_000_000_000 +# # stx_address: STS8B23TP0PDCC1YKG97QF0AX8FXV3HFA2RDNS43 + +# [accounts.wallet_122] +# mnemonic = "horn measure poet monster music stomach pilot ketchup bright muffin miracle message zebra demand usual kite six napkin relief double unfold scorpion border night" +# balance = 100_000_000_000_000 +# # stx_address: ST39BNTWPN9HZDSJTYVAK81XMM0T65CYN2A3Q7NMS + +# [accounts.wallet_123] +# mnemonic = "monster goddess mutual steak fiber say ridge strike asthma describe idea blouse gasp jewel excuse toilet anxiety club pudding party job rely retreat amused" +# balance = 100_000_000_000_000 +# # stx_address: ST3BF3EK5WCQFCF3E4CDYMK9HVZRY9DW5BFR877QE + +# [accounts.wallet_124] +# mnemonic = "outdoor claim drill image opera moral industry devote hidden license acoustic fine vault replace current vote depend fault rough stomach rocket laptop inside dynamic" +# balance = 100_000_000_000_000 +# # stx_address: ST2ZWHKJQ4CQVBFZBXSVN32QPWKYRTPBE924EFSAC + +# [accounts.wallet_125] +# mnemonic = "action build lab venture bulb section dove wheel vibrant gauge labor fence grace phrase solid organ undo athlete else middle iron awesome tent just" +# balance = 100_000_000_000_000 +# # stx_address: ST14VW65E5774PDSS8FSHS25YRSAS3VB9KN4MKRQ8 + +# [accounts.wallet_126] +# mnemonic = "episode resemble autumn another brush alcohol merit proof sniff deny domain maple wasp clutch style purpose perfect diamond taxi shield brief hero course accuse" +# balance = 100_000_000_000_000 +# # stx_address: ST368SDFZC5QPWB6QH1WBJGJFKX4YZ79K90CDFHYT + +# [accounts.wallet_127] +# mnemonic = "vacant raise vendor staff desert gossip twist bind salmon coconut call pyramid shuffle client charge year ice hungry gas oppose deliver wrong present salad" +# balance = 100_000_000_000_000 +# # stx_address: ST3YT87BVPG83FG09YAAVRPH4PJ4H4BV0ZNC3E353 + +# [accounts.wallet_128] +# mnemonic = "sniff exit chef trigger paddle attract intact credit put finish ostrich desk shove tail avoid urban flash much daring imitate legal kid protect uncover" +# balance = 100_000_000_000_000 +# # stx_address: STJ06WMDQTFKDYSYFESCPHWEYDDDZRJC6FC4FNS7 + +# [accounts.wallet_129] +# mnemonic = "act tattoo local pig gorilla mad merry trap clever dawn hello bubble toward suggest main say mimic shaft cool alarm slice renew excess retire" +# balance = 100_000_000_000_000 +# # stx_address: STFEAGVDTHH4ANH1V2CRS9CJ4QC6BWBT6HYV8PBE + +# [accounts.wallet_130] +# mnemonic = "donor normal animal van oven hurry planet benefit segment youth famous topple spider assault winner scrap kitten also lemon party bracket end senior palace" +# balance = 100_000_000_000_000 +# # stx_address: STCCFM55PKFJ0G91WTH2TSWSK9XVX1K032VSEW05 + +# [accounts.wallet_131] +# mnemonic = "exist page secret suffer recycle allow winter model world broken promote kitten eye autumn make yard bounce tube label funny eyebrow energy awake crash" +# balance = 100_000_000_000_000 +# # stx_address: ST50SZHK7WGM3Y6F0GJ4T1190S7VYH7K7HCMSVQM + +# [accounts.wallet_132] +# mnemonic = "wire ten detect swarm wheat bulk protect labor day lumber couple shove review frog true snap rubber friend athlete roast electric spike honey town" +# balance = 100_000_000_000_000 +# # stx_address: ST16S78EA4FRRSDBVXH8CD0X1W3R8BR7RA3P0NNDV + +# [accounts.wallet_133] +# mnemonic = "dentist bone seek bacon obscure always arrest pumpkin snack atom panther trouble general daughter essence pluck track drastic sense observe neutral dragon scrap beauty" +# balance = 100_000_000_000_000 +# # stx_address: ST5MKCNDPR2T954CWDXAZHKY0XWJKXH2F97WNKNY + +# [accounts.wallet_134] +# mnemonic = "knife fatal junk forget talk toilet enrich solar special process basic ship charge wide sport merry maximum say demand airport valid deny robust slender" +# balance = 100_000_000_000_000 +# # stx_address: ST1DJTABB748A22E5BHS12CR79PC8X4PG1EEWEP40 + +# [accounts.wallet_135] +# mnemonic = "sphere festival round timber bicycle circle genuine dial fix ankle again tackle gown cloth vapor else salute element cry edge coyote reason eye priority" +# balance = 100_000_000_000_000 +# # stx_address: ST2FY87A77JJ43C7WTQS3QWST5AFHP61MZMCT0BS2 + +# [accounts.wallet_136] +# mnemonic = "cricket shoulder warfare genuine aware license loan sadness chapter help flat basket smart leaf zero rifle cycle praise pupil used start tomorrow elegant ice" +# balance = 100_000_000_000_000 +# # stx_address: ST3C25RQBJ72T22DNDTKRX3SAWH7R2R7V2R75KX34 + +# [accounts.wallet_137] +# mnemonic = "permit pretty surge guard push squirrel tongue scare news hair quarter pattern west steak scan belt series cigar worth attack repeat awake cement night" +# balance = 100_000_000_000_000 +# # stx_address: STP8BSDJYSQK00CGY8RPBS6H9QYM69YNMTWM7RFJ + +# [accounts.wallet_138] +# mnemonic = "adjust cluster eyebrow pipe pattern treat below dumb picnic assault frog cause alien excite road helmet enlist tool labor exact negative name organ critic" +# balance = 100_000_000_000_000 +# # stx_address: ST21S5R8G4B6CXM42HQRSW1WP77JS2FMA4AW4P761 + +# [accounts.wallet_139] +# mnemonic = "month elbow dove symptom fog grit number shaft enjoy claim sing knife kitten misery vital pudding push trap sting eagle element zero jaguar jar" +# balance = 100_000_000_000_000 +# # stx_address: ST2V5Q6EG1EKPQ0PT75YB9VKQEQCVW2CX2S157CT6 + +# [accounts.wallet_140] +# mnemonic = "chief wet film father alter false result clay legal shadow system element require ill various saddle embrace swarm unique perfect metal will employ turtle" +# balance = 100_000_000_000_000 +# # stx_address: ST2XVD1AM0PCKR0DKGX2S8XH8DJEB4H9JXCZNT4C8 + +# [accounts.wallet_141] +# mnemonic = "shove install hard moral strategy exit hire hope rent virtual outdoor differ sock purpose much crystal scout path cake man friend deposit game build" +# balance = 100_000_000_000_000 +# # stx_address: ST2YWPCMCJDHZZGPQNKJ5YND2TK1ZNC4T1N9W10R8 + +# [accounts.wallet_142] +# mnemonic = "midnight salt program snap capable later bike better hungry daughter wrong avocado drastic huge century similar glow solar put buyer gate pitch funny hill" +# balance = 100_000_000_000_000 +# # stx_address: ST1NTFPFNXAEX54Y00K4QVE1CC09Q0M7ED212ZRWN + +# [accounts.wallet_143] +# mnemonic = "identify pink victory punch flight ethics evolve math robot gather minimum hamster equal chunk wrap tape hawk supply ridge unknown there ramp elder tenant" +# balance = 100_000_000_000_000 +# # stx_address: STYRA48Q065VG75AZEJ5J8RT27RZWK8C7QFDB0VV + +# [accounts.wallet_144] +# mnemonic = "frozen lumber pact horn ski film case autumn general night clarify guilt viable change asthma liar chat proud donate host range buffalo concert abuse" +# balance = 100_000_000_000_000 +# # stx_address: STP0NSD33KJ6YQ2YQWKDMG9TFBJ6HNFPPRN3DJWV + +# [accounts.wallet_145] +# mnemonic = "enable domain cage body just body cycle depend glass aunt edge food jelly cat goddess company skull track surge dinner admit spirit endorse random" +# balance = 100_000_000_000_000 +# # stx_address: ST1P2KZGQJH22CWDMB5N3ZKSGTWPS1JC51466P909 + +# [accounts.wallet_146] +# mnemonic = "ecology grape fatal height correct shaft guitar grain earn knee attack cost cloth scan squirrel melt rack firm squeeze brave harvest ticket lucky erode" +# balance = 100_000_000_000_000 +# # stx_address: STRB70CA6RHH8AJNGTC3DB5MMK9HW4KB5CAJFFBT + +# [accounts.wallet_147] +# mnemonic = "awkward wheat oak marble grid budget lucky cigar fetch weird online actual enforce praise sell field output idea derive all hurt truth math fantasy" +# balance = 100_000_000_000_000 +# # stx_address: ST1KJY77M1NZ2WDH6KGFVFDAPW9Q4Y55Y00N6D4CB + +# [accounts.wallet_148] +# mnemonic = "nest mystery adapt normal butter miracle material visa come glass deputy history disease range chase salon strategy artefact flee give favorite digital clip spray" +# balance = 100_000_000_000_000 +# # stx_address: ST12SE1FP759764F27ET2STPZD5TV2X0HFFT6PE2X + +# [accounts.wallet_149] +# mnemonic = "today patch price weekend cream alarm fiscal follow diary cherry surprise south try obvious recipe rather easy buffalo thing fork social various strategy arrow" +# balance = 100_000_000_000_000 +# # stx_address: ST3P0ZK7Z1NGC5KSFZ1H8JETHX45WH6FC8AAE59ZH + +# [accounts.wallet_150] +# mnemonic = "kitten category grit decade true vibrant layer panel merry swamp diesel release spend impose plate hazard bottom fruit marine canoe beauty thrive laugh moral" +# balance = 100_000_000_000_000 +# # stx_address: ST10894C79DFD39RAJPSKHMD2YW1QWZCSDWV619ZV + +# [accounts.wallet_151] +# mnemonic = "dilemma code soup thing pitch candy napkin law poverty evolve hamster hill solid strike fitness tower deposit hire view dance spread around bind slender" +# balance = 100_000_000_000_000 +# # stx_address: ST2EPYHN98KACREC7Z6WZHPP59BV1X14JYWNYCH9A + +# [accounts.wallet_152] +# mnemonic = "element art advice canyon congress board van regret call pyramid kingdom mirror media waste gate sorry already pig worth explain supreme rich wall able" +# balance = 100_000_000_000_000 +# # stx_address: ST1XXE319JCJBT9W1RY92QF1M3V3Q2Z4DC25HZRVV + +# [accounts.wallet_153] +# mnemonic = "winter derive swamp cute network expose major there ozone help fossil kitten habit naive slam abuse kite addict length client palm awkward express manage" +# balance = 100_000_000_000_000 +# # stx_address: ST2KJPJVWRZTWFYPXQVRE1XB437Z499801SMR2TXA + +# [accounts.wallet_154] +# mnemonic = "fine wish blade olive pipe frozen pluck upon bulb catch ill help spawn clown tattoo matter label sleep gossip dream lock want diesel cry" +# balance = 100_000_000_000_000 +# # stx_address: ST31V499EHW152XWWJNF3P9SDBPMK9BX32D3WBMJR + +# [accounts.wallet_155] +# mnemonic = "tattoo gown robot inherit ship enrich shallow solid leisure young soccer cement orchard room make infant mass brush stone option stamp hazard floor spirit" +# balance = 100_000_000_000_000 +# # stx_address: ST1JG6PYFYCM2BQG28NMRV1A2X8VX96MJ9E4554Z3 + +# [accounts.wallet_156] +# mnemonic = "coffee rigid vote adult nerve mind window farm grocery unfair gold act loop honey sugar immense desert bargain reflect right degree now adult ball" +# balance = 100_000_000_000_000 +# # stx_address: ST2B5MT6AE682QXEM7JFM6G3ZFAS715H5XNG1AF31 + +# [accounts.wallet_157] +# mnemonic = "junk random elbow resource zone kiss squirrel captain soul soup possible winter silver frame disease shrimp clerk piece nice olive ladder panic rain visa" +# balance = 100_000_000_000_000 +# # stx_address: ST2N44W1VEA6PD1W407VDDT90KX5821XGCF5CRX1W + +# [accounts.wallet_158] +# mnemonic = "museum wink cliff library parrot quarter donor plastic south capable mail before weasel polar worry grain another filter medal laugh shell isolate fetch valid" +# balance = 100_000_000_000_000 +# # stx_address: STHG90SJPEJE6BA55TCJADB4THHFGJZZMJARMASS + +# [accounts.wallet_159] +# mnemonic = "pencil giraffe vital caught pig dolphin orient wire around finger cushion ridge account wheel there slam chuckle grab plate deputy snow nephew wise bind" +# balance = 100_000_000_000_000 +# # stx_address: ST7K1MMZHX1NMDZDF8NN05XZFH9CDQACC9CFETTX + +# [accounts.wallet_160] +# mnemonic = "series strong such alone certain grant opinion series artist bicycle nurse target tackle general across anchor mean cruise core maid curtain submit consider view" +# balance = 100_000_000_000_000 +# # stx_address: ST1SHMN2YXW3XC9KMBTYSDA8137FPWKVAY45V7PND + +# [accounts.wallet_161] +# mnemonic = "reopen country off pulp castle sail machine join engine speed jeans dune evoke grunt net better this uphold similar snake polar west afford curious" +# balance = 100_000_000_000_000 +# # stx_address: ST09MSR8SKQ3P9R3G9AJPS0MBC5FWTRHHYAEY83V + +# [accounts.wallet_162] +# mnemonic = "spin hard picture abuse trim sad intact enrich cram civil virus please ghost hawk behind venture figure wise then marble sense normal gospel rally" +# balance = 100_000_000_000_000 +# # stx_address: ST183Q218DJXYF82WW8EW5K3FZFRPGSG2RGZ39YXE + +# [accounts.wallet_163] +# mnemonic = "usual thank left whip evil join length dirt attack size draw kind garment that chase trial possible copper moral month depart forget route push" +# balance = 100_000_000_000_000 +# # stx_address: ST1AFM76SRSPKZ3GMSX0N95X3Y1A9Z3ABVSH6YYNX + +# [accounts.wallet_164] +# mnemonic = "include pistol rally raw upset album tuna fancy lonely apart creek echo shield danger return scene video obey bike chest hidden wild code runway" +# balance = 100_000_000_000_000 +# # stx_address: STB58YK523MPHRG7NQY550P5RX3GQM20GVQN0QF9 + +# [accounts.wallet_165] +# mnemonic = "smart deny soldier midnight upgrade conduct online cage ranch health skull armed vault shove error attitude primary pioneer square approve eyebrow bone seed bind" +# balance = 100_000_000_000_000 +# # stx_address: ST1HP0YMMA3VSARSFNC4HGCD6FZ9AP18KAE3WYBPX + +# [accounts.wallet_166] +# mnemonic = "carry able butter final today indoor grab kite possible stamp universe copper wage blood wash option timber present power mail cargo crane hospital mass" +# balance = 100_000_000_000_000 +# # stx_address: ST3Z2QZ3389CXNHZ5HF1Y6STK1EF159JR6DQPXMCT + +# [accounts.wallet_167] +# mnemonic = "region foil kind pipe share enlist amount mail prize letter kitchen aim cargo nuclear van entire discover liquid orient demand slice name also approve" +# balance = 100_000_000_000_000 +# # stx_address: ST3FZ8KZJZQEHKH99M1FPR49ZFA5XJEAX0E4T2J05 + +# [accounts.wallet_168] +# mnemonic = "author become where off resource donkey craft bronze picnic outside dragon border armed chef left lunar practice siege protect grain beauty oyster empty awake" +# balance = 100_000_000_000_000 +# # stx_address: ST2JBDEF6NZYRK5ZD57J4VX8B84TCFDSYX1ZRPQEV + +# [accounts.wallet_169] +# mnemonic = "arena pond joke half wine speak video soul butter galaxy bread lunar excite exit dress express buddy settle frog affair stadium wealth bomb stone" +# balance = 100_000_000_000_000 +# # stx_address: ST266W858Q9XWDV26ZRN9VS3C7W38J2SNADH9VEWM + +# [accounts.wallet_170] +# mnemonic = "future capital rabbit trial uncover wasp stock argue theme island nose nice myth project horror couple gospel float must celery oval fish sample desk" +# balance = 100_000_000_000_000 +# # stx_address: ST4DCNKQY8M6Y2KJVZS29EPV0BQWRMGK27QYQK6Z + +# [accounts.wallet_171] +# mnemonic = "hope stamp credit axis radar column comic novel leaf legend chuckle axis toddler silk dilemma firm track ivory evidence seed cost skull album document" +# balance = 100_000_000_000_000 +# # stx_address: ST363P049B6N34MRXAW3XKTJW7AZGC3RAE6EPFWYR + +# [accounts.wallet_172] +# mnemonic = "corn dwarf casual tiger rookie subway ride glimpse cloth mansion admit option differ next ride prefer axis youth prosper fragile multiply shrug cream expose" +# balance = 100_000_000_000_000 +# # stx_address: ST1YQFY0GRWDXXP06ZT8J39BBE3TKYMZDS9F234KT + +# [accounts.wallet_173] +# mnemonic = "miss banner envelope punch cross predict canal cheap gallery shrimp involve drastic polar report kiwi toward trip author route coconut inherit sweet broken security" +# balance = 100_000_000_000_000 +# # stx_address: ST2Y54JHGJ12PWRK2Z8B4Q0D9FFK5X80A1K3JMPPZ + +# [accounts.wallet_174] +# mnemonic = "wet lucky minimum civil panel river tomato arrange spend creek mass harvest exclude member rude castle snow meat strategy exhibit shiver host upgrade lawn" +# balance = 100_000_000_000_000 +# # stx_address: ST1E2MP5QN9G9KRW593H8G91ZGQJC5PXSQ8516GX + +# [accounts.wallet_175] +# mnemonic = "film sausage angle fence basket vacant tape boat moment damp emerge match since bounce convince thrive venture laugh mind enroll swallow device trigger clarify" +# balance = 100_000_000_000_000 +# # stx_address: ST1K725HJ56SCAFKZG3978DMN5GCGBMCKT5RJT0HC + +# [accounts.wallet_176] +# mnemonic = "forward place cricket limit segment hard damage region burger disorder remain furnace flavor local attend forest material define gravity lunar antique curtain female fix" +# balance = 100_000_000_000_000 +# # stx_address: ST1EC5DKGC7YQSX7CDYYE95VGS57E5H0ASBNHSY44 + +# [accounts.wallet_177] +# mnemonic = "fan number garbage unable rhythm rifle prize vacuum drama desert pencil lemon ship shadow eagle ability crouch weapon health dream wide eye raw humor" +# balance = 100_000_000_000_000 +# # stx_address: ST3KQYV1ZE4KTMHMC1WZ7NWSY0GS5201NGDZRXVMZ + +# [accounts.wallet_178] +# mnemonic = "magnet bid dance runway burden ready ship bunker apart angry force afraid kangaroo invite behind group table expand evidence win finish dawn rent hold" +# balance = 100_000_000_000_000 +# # stx_address: ST2VSAYCV0ZE2RGSYGXV0HYQP7HCPYN1SEDYWQMQY + +# [accounts.wallet_179] +# mnemonic = "insect entry produce zoo combine slam capable assault rough absurd risk cream avocado height better iron vacuum volume chalk betray today denial stuff market" +# balance = 100_000_000_000_000 +# # stx_address: ST3CYCCWSM9JAMFFFFF7R75A2K2QYY4QR8Z8HP1RH + +# [accounts.wallet_180] +# mnemonic = "potato bright dog budget novel nephew protect dust usage fancy wealth flock fetch laugh sure another mushroom guitar bean garage hat snap draft physical" +# balance = 100_000_000_000_000 +# # stx_address: ST2RXPTVBZN8W5N0DXHTCG43F1CWQR9M72VJRRHFT + +# [accounts.wallet_181] +# mnemonic = "unable position transfer vanish pause stereo pioneer ketchup flush fruit obtain drum choice parrot inmate enough outer shield gate rebuild cruel liberty install profit" +# balance = 100_000_000_000_000 +# # stx_address: ST3W0476H89D2H73S0Y9A2JXDPTQYZNWR2FY0C0SA + +# [accounts.wallet_182] +# mnemonic = "broom rapid dwarf coffee bachelor seat lucky athlete ostrich quantum tongue effort proud scatter february human torch stairs seat game fix piece castle network" +# balance = 100_000_000_000_000 +# # stx_address: ST44GW7Q0BDX9YAEJTYRZ46J1YERHF7K1CCHG5TV + +# [accounts.wallet_183] +# mnemonic = "manage cradle rookie distance leader tired divorce kind bamboo resource distance lobster snow comic gown end misery hospital armor brick dad kidney fork crisp" +# balance = 100_000_000_000_000 +# # stx_address: ST2W6ZS6KNDWH3W1B7M57PYYNC11TGG5BE76RE4QS + +# [accounts.wallet_184] +# mnemonic = "soup tree vacant crouch cage snow whale jewel paper exotic aisle cushion fly calm canoe produce radar pet crew glance baby duck promote fat" +# balance = 100_000_000_000_000 +# # stx_address: STEMAKFR4ET99R8892PFYBQDV091GPCAP8PEGDG0 + +# [accounts.wallet_185] +# mnemonic = "float thank bone culture finish wink isolate grid desert spike uncle pony rare deer subject anchor horror trim false senior invite awkward pen detail" +# balance = 100_000_000_000_000 +# # stx_address: ST1XQQRZ55M0JHEREY5EK8RJYYM4788239NSJYNBY + +# [accounts.wallet_186] +# mnemonic = "spell hockey rug wood uniform brother salad frog order unlock acoustic act violin good borrow stadium fatal small timber raccoon tank rack joke history" +# balance = 100_000_000_000_000 +# # stx_address: ST24KAVGCAVZ9KB13V2SJX6CGNG6W6K7QTPZH8BEY + +# [accounts.wallet_187] +# mnemonic = "quote broom please luggage priority flight program laundry health token dignity mixture what grant more curve kit kite danger average perfect license animal hundred" +# balance = 100_000_000_000_000 +# # stx_address: ST2Y9RTGZFQGDR6E1HXHZ4XN20DHEKQ1S7ZGRVPWP + +# [accounts.wallet_188] +# mnemonic = "cake analyst hamster test warm away trip promote scrub network lottery island shoulder slow decorate butter put next wall scorpion alpha vehicle ceiling main" +# balance = 100_000_000_000_000 +# # stx_address: ST268VWPNMMD78PAB2Q4CHGPG034WVGT32J84RZTF + +# [accounts.wallet_189] +# mnemonic = "napkin culture motion album snack endless cereal shop demand sad claim cage craft physical nothing swing tooth lock able ball barrel peace sense morning" +# balance = 100_000_000_000_000 +# # stx_address: ST1M7FXTKF272XGDCQMFYYG1MGQYSN86BHS7PHR3B + +# [accounts.wallet_190] +# mnemonic = "rice price quote gospel twelve priority urge calm sting street music company hawk fiction fun pen peace piano inquiry beauty miss marine man joy" +# balance = 100_000_000_000_000 +# # stx_address: ST3XREQGPEYEWZMAHV0A3TQJBJF2K3H0MJK9YV8SD + +# [accounts.wallet_191] +# mnemonic = "toddler indicate brisk together loyal camp smart zero frozen chronic banner celery give pull kitchen clown exhibit income parade loan fan curve next helmet" +# balance = 100_000_000_000_000 +# # stx_address: ST2GK48NRZ8XQRC899CC5A140QWGTC25P0CDZYNHX + +# [accounts.wallet_192] +# mnemonic = "coin shrug entire noise volume favorite exist order wrong labor crush swim intact ethics forum paper patrol web smile net lemon dignity slogan nephew" +# balance = 100_000_000_000_000 +# # stx_address: ST1320P53RQT2YQRF3M2S0ZA4DAJEY3KY0C61TWH0 + +# [accounts.wallet_193] +# mnemonic = "news agent fantasy gloom crawl dilemma embark spread game doctor tortoise armor frozen palm power enforce harsh make diagram tennis walnut carpet unable decade" +# balance = 100_000_000_000_000 +# # stx_address: ST2F7X04DQJ59Q4E2GJ8VHHYVVZ9ZA2M60SYCWH16 + +# [accounts.wallet_194] +# mnemonic = "perfect quarter candy defy above sunny any census spring desk woman ring bulb correct orient senior crystal chair give stereo chef defense blossom check" +# balance = 100_000_000_000_000 +# # stx_address: STH7J5GD4X9J203EP8495CZ1PYZR4370WYSB659N + +# [accounts.wallet_195] +# mnemonic = "opera essence friend river extend burden giggle vapor often six cause always stadium include result state grief arrange blur place trust pitch step spend" +# balance = 100_000_000_000_000 +# # stx_address: ST5AZVA3YNZM9SBKJWXC30JRG6A6395FB7G7S442 + +# [accounts.wallet_196] +# mnemonic = "love crime life stone shadow twice salon transfer language code admit heavy matter churn domain trouble copper road twist sign hybrid cook warfare salt" +# balance = 100_000_000_000_000 +# # stx_address: STHPNPFCPM1FM896D9WJ9A26XZ582QE8NT1PT3KG + +# [accounts.wallet_197] +# mnemonic = "inch busy ceiling initial skin original symbol theme artefact tent series famous vanish season cushion tail visit pipe evidence giant shoulder fit vintage field" +# balance = 100_000_000_000_000 +# # stx_address: ST3NWAAJSXXN4YGQCJ3VY37HC60VXFDHBK66XG1XP + +# [accounts.wallet_198] +# mnemonic = "catalog ahead trouble start stove exact exile puppy finger citizen village rural arch cliff world tell pool stairs cricket guilt replace multiply kitchen cup" +# balance = 100_000_000_000_000 +# # stx_address: ST1SVKRSX8BB94BGN24EYYZN5S070HH3G4M02PV6T + +# [accounts.wallet_199] +# mnemonic = "wedding rely verb sick rotate since hazard suffer pole arrange delay slot manage express trust tag unhappy physical genius gun layer one agent wreck" +# balance = 100_000_000_000_000 +# # stx_address: ST24BHSCT28CB01XQP33Q2WCW3SP9SRRCZQE6R69B + +# [accounts.wallet_200] +# mnemonic = "squirrel radio smoke army damp width erupt kingdom essence absorb danger final trust select pattern north bubble adapt sudden allow main better trade morning" +# balance = 100_000_000_000_000 +# # stx_address: ST3V0HTDNSB9EPSS9C6FHZEZTBHBFMHAKE19R62R5 + +# [accounts.wallet_201] +# mnemonic = "negative unusual symptom again tragic west tip myself course rebel victory candy insect slim sport cost own relief announce auction divide rug armed anxiety" +# balance = 100_000_000_000_000 +# # stx_address: ST14S87109TYXV6DKW9DNG0C8YCZ3WJTNPD1FXEDP + +# [accounts.wallet_202] +# mnemonic = "cargo once alpha debris wolf swarm mirror friend inform nose quarter brief cancel session judge fiction guide tomato anger vague stem more topic source" +# balance = 100_000_000_000_000 +# # stx_address: ST2MZZRPYJDX50HGJ0P59YR9C2GAHMG6B1GS96ZC5 + +# [accounts.wallet_203] +# mnemonic = "toy keen normal whisper donor flight decorate cupboard swear barrel margin vivid lizard angle kite dash trumpet draft daughter month calm witness live hundred" +# balance = 100_000_000_000_000 +# # stx_address: STFE6PRG1F4CZ4KE7Z80P8W02379R9H4F0E42PX3 + +# [accounts.wallet_204] +# mnemonic = "try apology slogan salt cross ugly eagle input emotion bag kidney minute buddy ecology grab inside pencil now rookie outdoor rifle mix average screen" +# balance = 100_000_000_000_000 +# # stx_address: ST3605YC81PKVSXEV9REKPEX3H2J0X1DNPTYC122T + +# [accounts.wallet_205] +# mnemonic = "enact mouse sadness goose defy detect where embrace best world detail hamster eagle plastic real duty burden panda can budget then able shrimp glove" +# balance = 100_000_000_000_000 +# # stx_address: ST3ZB8QQ3Q5KGGA651F77TSZQSGBZVTJY14XS63M8 + +# [accounts.wallet_206] +# mnemonic = "narrow crop acid clock chicken still worry this unknown suggest repair brown lonely ordinary cart rocket under whip flight mountain collect town ugly barrel" +# balance = 100_000_000_000_000 +# # stx_address: STEXEB9FGQEMBMHKSN41CN888KC7DSE6ZRX10DVS + +# [accounts.wallet_207] +# mnemonic = "armor glare mammal dry wrist injury disagree rail calm decline palace cry acid equal inspire vapor steel inquiry sight rent pizza gloom reopen rice" +# balance = 100_000_000_000_000 +# # stx_address: STJJDXCCCZXXSWQRR6RVGJY4CKACWT2DQ52K1K2R + +# [accounts.wallet_208] +# mnemonic = "file garden kitten lock mixed innocent medal enable eager crazy excuse bacon wrap ordinary try burst possible kitchen want toilet junk air scorpion type" +# balance = 100_000_000_000_000 +# # stx_address: ST28VDXCKP2PZE22K9XJZNGXB19YEFQCZP2DKJVTS + +# [accounts.wallet_209] +# mnemonic = "job school hire decrease nose across ability orange disagree resource canal hundred tennis vehicle kind reward intact swallow quit park uncover hero win lunar" +# balance = 100_000_000_000_000 +# # stx_address: ST26NWC1F795FD8W66Q2GZBSW2WKJ46F2N2Y5SJJF + +# [accounts.wallet_210] +# mnemonic = "submit please come weekend broom help gas click void hero lava unveil install fuel wash sibling taxi bunker brass hint fine scene salt volume" +# balance = 100_000_000_000_000 +# # stx_address: ST2EJ4QM2VY839C5CZ20JSMXSDKJ201HBD3SV00YB + +# [accounts.wallet_211] +# mnemonic = "barrel lucky humble fall infant someone mango liquid zero safe eager quiz amount join steel label army build conduct girl social wealth world curious" +# balance = 100_000_000_000_000 +# # stx_address: ST1TGVT0MQ8ABM9RAY55190MQKQXGC8RNG6SZN7S9 + +# [accounts.wallet_212] +# mnemonic = "spray toilet lucky sunny picnic media manual wrestle sign federal season shrimp category frown kidney catalog bird suspect hip neither hungry unlock stable tortoise" +# balance = 100_000_000_000_000 +# # stx_address: ST2ZQ1VS4743Q9WVZP4P8XS5CTDRKXB809D2HP8KC + +# [accounts.wallet_213] +# mnemonic = "volcano shadow spike uphold process vivid chicken become face gadget budget okay rocket stumble police sort isolate onion few bench ozone sibling bundle neglect" +# balance = 100_000_000_000_000 +# # stx_address: ST3T7051YKBGBZF1ZXVTMAVRFE511Q6NWQNT4M82X + +# [accounts.wallet_214] +# mnemonic = "army primary genius section innocent process urge strategy much inherit family neglect heart mosquito zone kit mango oxygen round slide extend chef section hungry" +# balance = 100_000_000_000_000 +# # stx_address: STV8PXB4B7SAJWR2M1M2STVQ4GPG85S21DB3NJ69 + +# [accounts.wallet_215] +# mnemonic = "section market family shrimp sick loud settle vocal drill race decade decade scan rose electric rabbit course payment gold virtual modify pause gather erode" +# balance = 100_000_000_000_000 +# # stx_address: ST1TY9DK6NQNDJMRA6TZPFW8E8QTJ0J7KJX5QCRJE + +# [accounts.wallet_216] +# mnemonic = "again spray cloud truck delay later shy mandate account wheel invest auto record design piano right have decrease once syrup fence dish flower odor" +# balance = 100_000_000_000_000 +# # stx_address: ST2KYJQXYW09SHB501ZH307MFS5AHGWY8C1CTJF15 + +# [accounts.wallet_217] +# mnemonic = "worth toe rebuild media ticket clinic try pulse bamboo steel when aerobic fluid shield ivory drama dynamic latin jar evil mimic roast scan achieve" +# balance = 100_000_000_000_000 +# # stx_address: ST3SER8S101K1WG8KWSQMR9ZPTWQ5MKJXN6XC51WR + +# [accounts.wallet_218] +# mnemonic = "layer depart side unable axis parent orient safe false fatal keen spoil kite idle matter midnight visit will profit nothing detail square pull mail" +# balance = 100_000_000_000_000 +# # stx_address: ST3NMX0GMTKNPE9Y04GS88EWQ8DMSTCFTKCTATR7 + +# [accounts.wallet_219] +# mnemonic = "source super soldier permit forum tobacco chunk gasp avoid leisure wedding fault blanket sun hen ridge eager donate help voice easily scatter phrase anger" +# balance = 100_000_000_000_000 +# # stx_address: ST3X5J18PADYRM7NCSXJ55N7DZT259WREX672NGB6 + +# [accounts.wallet_220] +# mnemonic = "sustain afraid axis price else differ wife exile replace quiz radar order cool blame gown twin heart pizza mixture dizzy sword mass fiscal please" +# balance = 100_000_000_000_000 +# # stx_address: ST2CRZ1R2479M8NKYHMTNV8DS3PMHGPAWJFW6N9KE + +# [accounts.wallet_221] +# mnemonic = "giggle sail reduce pave consider kitchen rude tenant derive enrich forest mushroom dose chest acid beef track door hotel math net kind crucial prosper" +# balance = 100_000_000_000_000 +# # stx_address: ST1ERMMJT36GVJ2C3A0X1NG2G8ERV33GMWKWY4DBD + +# [accounts.wallet_222] +# mnemonic = "smile elevator test unfair eager curious other planet flush network tired cereal place chalk crouch grain carpet margin retreat aunt ten note they lazy" +# balance = 100_000_000_000_000 +# # stx_address: ST2V5CH351B1GKFECWPJH59Z8XVFCGFQ9PXKR68TS + +# [accounts.wallet_223] +# mnemonic = "dutch item shop power grant they local novel name actress stand service client lion faint when that glad arm right tattoo glance summer push" +# balance = 100_000_000_000_000 +# # stx_address: ST3QSQAEBY04YKV1PS4K8MQPJ7PBAX9BJQDB9JKFD + +# [accounts.wallet_224] +# mnemonic = "antenna museum mule citizen math stone clarify hub patrol lake gap assault win guitar goddess bulk capital dust mind unaware enact neck crime ugly" +# balance = 100_000_000_000_000 +# # stx_address: STAFM44R04REWVK57V1BHTZWG12RVSG7PCFAWV1R + +# [accounts.wallet_225] +# mnemonic = "toilet duck north slide width bundle jealous defense dial theme swarm core bracket regret loud weather lamp auction manage dilemma electric step connect lab" +# balance = 100_000_000_000_000 +# # stx_address: STBHBR1XAS64T8STDGJWXX7G3CRXA7MVY3A7T2AW + +# [accounts.wallet_226] +# mnemonic = "glow gauge awful current trash feel police exercise moon exit tuna fresh bulb stamp frown duty file owner nature ostrich wish duty symbol slogan" +# balance = 100_000_000_000_000 +# # stx_address: ST81MVBAYKCESJNECD23H33Y72CW7NPGJFQCJRFF + +# [accounts.wallet_227] +# mnemonic = "coach artist solar pretty survey actress tree inhale behind month fever wink lend human frog armor aspect ginger cloth benefit caught future cannon wall" +# balance = 100_000_000_000_000 +# # stx_address: ST1G0PBBASNZ5SPFHTWNW7FHE626DCSDAYGJNJ3Q4 + +# [accounts.wallet_228] +# mnemonic = "sure fluid inspire humble oven acid pen decline mansion post choose result legend sure canoe across balcony art reward mix left please leg accuse" +# balance = 100_000_000_000_000 +# # stx_address: STA9PTHD0E67SE95QFFDFGV2PWVRVQ7HPFM9EJ4H + +# [accounts.wallet_229] +# mnemonic = "quantum steak endorse business error century earn person impose drum save flush valid join wild penalty powder dwarf coffee height arctic bind slender glance" +# balance = 100_000_000_000_000 +# # stx_address: ST3862NN9WZGC0YR11SRBY4GDNTDEBY3XDPHAYEGS + +# [accounts.wallet_230] +# mnemonic = "address resemble fabric property engage merry issue famous case regret polar know crawl wing bench glove verify creek process abstract sugar size wave endorse" +# balance = 100_000_000_000_000 +# # stx_address: ST2XW1642ZMEBE9QJ06APRR329FY1PMEGRJJ1YC73 + +# [accounts.wallet_231] +# mnemonic = "hello spare home version angle grant afford immune orchard injury surprise fame barrel firm tourist rebel example simple rocket agree plug elite eyebrow rigid" +# balance = 100_000_000_000_000 +# # stx_address: ST1FA2ACH3SXSRHW32WF44CVKPK3GV352GVY03RFF + +# [accounts.wallet_232] +# mnemonic = "fire mandate equip corn either ice dream this swamp certain shiver cereal panel title disease throw talk alarm label turkey quantum nest worth this" +# balance = 100_000_000_000_000 +# # stx_address: ST3N0S7E63FKW5Q6VJSP0KRPBRXZK85ZX0S0MF1KS + +# [accounts.wallet_233] +# mnemonic = "pilot food strategy hint broccoli walk industry foil notable predict bind organ cushion habit toast label ranch sting salad violin uniform trigger noise tennis" +# balance = 100_000_000_000_000 +# # stx_address: STQ90FJT03B9V58J11CR83VSDW3J3FSVMPF88HJ7 + +# [accounts.wallet_234] +# mnemonic = "dignity furnace blind explain trial shy scissors barrel lounge curious attend predict wedding tell all interest truly cash spawn anger advice inside gas abandon" +# balance = 100_000_000_000_000 +# # stx_address: ST3A3W1AQS3PX342Y98P5T8D93410ZV58JT5H6TVM + +# [accounts.wallet_235] +# mnemonic = "guard special afford city window release song acquire muscle remain awesome buffalo bottom atom edit cabin steel cereal funny pride display security used open" +# balance = 100_000_000_000_000 +# # stx_address: ST2Z668XHZ3ZQM4ZZ86N1BMW9EHFFYKWK7ZV33VT1 + +# [accounts.wallet_236] +# mnemonic = "engine antenna fuel chuckle order advance put orient update blouse raise riot chapter car father process summer bid token client actor shine behave buffalo" +# balance = 100_000_000_000_000 +# # stx_address: ST24EYPX4KGXM9080EFXM84N3X39M15F91F46CYQY + +# [accounts.wallet_237] +# mnemonic = "girl stereo worth circle bag neutral actress human blood come demand topple peanut fade boss burden crop teach chalk cheap alarm square rail cupboard" +# balance = 100_000_000_000_000 +# # stx_address: STTAV72769X9A3408WKMKNJQ8HCJW5EHQK0G1B4N + +# [accounts.wallet_238] +# mnemonic = "glove tent daring leopard chief control monitor way staff junk affair volume dune work destroy achieve rely south legal polar rack quality submit cheap" +# balance = 100_000_000_000_000 +# # stx_address: ST3PBJ9EYCB4D7ZDDWV6G4XMS74JZ8QBPDTF10636 + +# [accounts.wallet_239] +# mnemonic = "ghost shaft loyal theory cluster tourist celery upgrade aunt science divorce wasp release decade grid news noise nest latin define capable cabin peasant mass" +# balance = 100_000_000_000_000 +# # stx_address: ST372SHXBNZ3QWZWG6T7SRY9HYVT98246MHR6M0M8 + +# [accounts.wallet_240] +# mnemonic = "song keen dog fold drift empower artist culture speed arrest saddle sponsor main swear subway drive gesture educate help easy must benefit sausage dizzy" +# balance = 100_000_000_000_000 +# # stx_address: ST1HHBQZKNY3FE4TN2ERBYCVGZV938EPV3RB1JCGK + +# [accounts.wallet_241] +# mnemonic = "parent pond lawsuit awake woman latin matter wild buffalo dose token will climb mass head vault quiz canoe absorb clock hurdle bunker alcohol poverty" +# balance = 100_000_000_000_000 +# # stx_address: ST2DS1T540027D8MMERQ8C7BWJ7D5XNSVSAS9KXH3 + +# [accounts.wallet_242] +# mnemonic = "undo crouch accident slide hand goddess frown clown picnic garage prize sail mean nation twist keep other forget wheel author private buyer column current" +# balance = 100_000_000_000_000 +# # stx_address: ST2YQWFV0HT8X900MKFQ7B0NSGTRDADN9RP99JY4D + +# [accounts.wallet_243] +# mnemonic = "car media photo proud vibrant noodle run brass slab rule electric decade trust rural dynamic amateur door shrug shield swing tornado own cart aim" +# balance = 100_000_000_000_000 +# # stx_address: ST2XJ3KH8BSD0KZBQBXBRPWK3VQ96C7HN5ZDZ79D4 + +# [accounts.wallet_244] +# mnemonic = "skill beef toddler amused doctor move expire ribbon boss possible cabbage visa flip salmon garage kiwi ski world joke quick kitchen auction view that" +# balance = 100_000_000_000_000 +# # stx_address: ST2APDCKZNGBWQYST6GGH9D27ZQCHQQBB30XS66J8 + +# [accounts.wallet_245] +# mnemonic = "genuine possible nasty flower rigid corn twice mandate machine embody jelly snap rich stock project length post twist edit crew mountain clever prepare verb" +# balance = 100_000_000_000_000 +# # stx_address: ST1DQWEPD76FCE10R695986PWSZ4F1XFZGM7KX78Y + +# [accounts.wallet_246] +# mnemonic = "fantasy collect neglect cover bread reopen depart night soda mean love spell right wire fiction help guilt lawsuit gun cement deal pumpkin maple release" +# balance = 100_000_000_000_000 +# # stx_address: STRD12XRPZVSK2HJ21R3FFW65JVGFTA7TY7MHH0W + +# [accounts.wallet_247] +# mnemonic = "dentist guard hen real puzzle bleak dose off push drum prefer artefact brisk hazard uncover destroy arrest chat mechanic song charge link copper shove" +# balance = 100_000_000_000_000 +# # stx_address: ST2GYMTA4108JSAE368S1M9SKQ3ZCP6H3VSX6HVPB + +# [accounts.wallet_248] +# mnemonic = "size crater order gun drift avocado great place because torch thank load hand cargo twelve ranch deny promote evoke hazard wreck toddler patrol shuffle" +# balance = 100_000_000_000_000 +# # stx_address: ST19TB1T3CT67QA091GEEFXM3PQVFVWEWYR2Q72J7 + +# [accounts.wallet_249] +# mnemonic = "path vendor strike swim bus erode false review people teach broken hood leopard cushion shove twenty machine brick elevator palm illness bone heart drift" +# balance = 100_000_000_000_000 +# # stx_address: ST28T0M2ZQ20T36SPYKXZVNE3T7MZRNGYHCYQCKEP + +# [accounts.wallet_250] +# mnemonic = "fitness hard million oil alley slab mistake zone chapter paper absorb medal canoe search cause pistol foot bike phrase vapor clock floor evoke buddy" +# balance = 100_000_000_000_000 +# # stx_address: ST3KTHGK5XFF36ZVB3CXAQ0TMBMQNFB3KS7ZRNPWE + +# [accounts.wallet_251] +# mnemonic = "impose actress cherry want food gaze cloth misery voyage adult grief math kit file matter grant chunk tree modify blame short pig business abandon" +# balance = 100_000_000_000_000 +# # stx_address: ST1JZV0A3MNCQYKXH62771BA44RKF3CEPEWV5CQ15 + +# [accounts.wallet_252] +# mnemonic = "awkward picture luxury bone weasel fold slab never dose senior assault vote urge voice spare universe quarter shift circle recall pull hawk furnace feel" +# balance = 100_000_000_000_000 +# # stx_address: ST210RQMJGSQ7P5ZMD1VA359X0HJ6Z1AVHW36PY0G + +# [accounts.wallet_253] +# mnemonic = "relax symptom erupt recall attack radar inmate elephant jaguar blast sweet hip gallery adjust violin silver script yellow yellow mesh good quit ice column" +# balance = 100_000_000_000_000 +# # stx_address: ST1A1PRZ9F5SPB65JHMN7RAR5ZKM6EKE8T93YC82W + +# [accounts.wallet_254] +# mnemonic = "space strike leaf early cabin furnace jaguar coil radio tenant special repeat bulb genuine choose maid best visual brass body brother floor model attitude" +# balance = 100_000_000_000_000 +# # stx_address: ST2XF55S30H47M01N11VPEJCP4MFEQK0R6HK8YHDS + +# [accounts.wallet_255] +# mnemonic = "tone require destroy ugly scrub kiwi diagram syrup nerve uncle twin crazy noble urban garlic obvious only fortune cheese party give salmon image width" +# balance = 100_000_000_000_000 +# # stx_address: STFFRH3F09F8P1F0F2B7Z401WVY6V2T1FVHE9FBR + +# [accounts.wallet_256] +# mnemonic = "oven screen merge drift breeze dial extend flavor hope planet summer total cycle boost half spin regret poet base strategy decline fox write blouse" +# balance = 100_000_000_000_000 +# # stx_address: ST18XGDMP2XDG8SKNZ41DPG6SREJMR5ND470CHT5B + +# [accounts.wallet_257] +# mnemonic = "fabric wait joy dinner brother toddler wall genre dash essence eager acquire answer option claim rule travel runway require dawn cry skull fix bird" +# balance = 100_000_000_000_000 +# # stx_address: STQ34Y2RT4P52RY5569CAX8SCVR6ZV0N02FC4RAZ + +# [accounts.wallet_258] +# mnemonic = "cycle cube ridge jelly special misery panic marriage language pave grant ship credit scene dynamic supply body point leave wrap tray embody begin doctor" +# balance = 100_000_000_000_000 +# # stx_address: STNZ3J2YE22VDXPN12TFFWEFZEHRTE2DTRTAPGYK + +# [accounts.wallet_259] +# mnemonic = "proof maze pear estate speak clock hover unknown hidden example ski theme safe wealth van amazing unveil village excuse beyond tired liberty raw glimpse" +# balance = 100_000_000_000_000 +# # stx_address: ST19J0FC9KFWFGFQP8T6VPAQSDZWPH4XG8ZH7H76C + +# [accounts.wallet_260] +# mnemonic = "loop gown angle jazz column inflict label gravity abuse tuition surround maximum depart drastic dove limit tooth arrest remember moon journey post elbow fury" +# balance = 100_000_000_000_000 +# # stx_address: ST1AQKP4293D6S4A9ZK3Y1P83ZBGBPSFH4JC6VB13 + +# [accounts.wallet_261] +# mnemonic = "insane vast valid toilet timber expose mosquito glide setup fat rhythm tuition night around mix rain dizzy grit knee rice vendor gate lunch screen" +# balance = 100_000_000_000_000 +# # stx_address: ST1TSWWSZ58FAW47HVYDEDGJNCJRYZ375VAJ96971 + +# [accounts.wallet_262] +# mnemonic = "mandate still include actual myself empty suit aspect remind fat have cousin grace enroll mad bind owner cheese dragon dutch keen deal valid west" +# balance = 100_000_000_000_000 +# # stx_address: ST2KF0ZFSHMCBNSBK8EQKF7RJXFX6VBR96MEW8RZ8 + +# [accounts.wallet_263] +# mnemonic = "side target piece still onion bounce sheriff swap thought scan minor dance antique rescue mom six sure bread pipe fog alter nurse shop main" +# balance = 100_000_000_000_000 +# # stx_address: ST36AHHV7WWMDES1N1WPVSAWR0Y5PJMA5BHRHSJNN + +# [accounts.wallet_264] +# mnemonic = "grass diary milk minimum million bachelor snow fence tail short level orbit frown salmon satoshi quiz fitness cave tribe guitar minimum tissue deliver danger" +# balance = 100_000_000_000_000 +# # stx_address: STS0J96DMCJ3XK55P64MQ2XMAVPK21NCWRMSFSWB + +# [accounts.wallet_265] +# mnemonic = "diary pelican attract solar donkey manual churn brother bounce swallow boss tomorrow cradle renew blood slow job clap web fever pony fun skull work" +# balance = 100_000_000_000_000 +# # stx_address: STY5011J6F899F85EDCSV7DGRRZCQ20GVKJFWCEM + +# [accounts.wallet_266] +# mnemonic = "anchor leisure blossom present diesel brass lounge olive frequent corn amazing flame balcony chicken claim clog machine slot equal tuition index uniform mistake utility" +# balance = 100_000_000_000_000 +# # stx_address: ST3MNM6128ERXFJB9VH0A41CZAATWZKEDR1GHKRGV + +# [accounts.wallet_267] +# mnemonic = "cover rifle price swarm pluck gas issue sugar theory input curve symptom pill focus neglect antique assist loan special myth excite business zebra target" +# balance = 100_000_000_000_000 +# # stx_address: ST12MNYMC96TW7C6NMNDWYCPW09X6659PAPPGVWQG + +# [accounts.wallet_268] +# mnemonic = "occur fever wink wet fix where pair member flash proud danger tide almost discover wear maze nest surge suspect endorse merry power barely list" +# balance = 100_000_000_000_000 +# # stx_address: STBM46MFC084ZMQ9ZKK81HV12NRJNE143C1WN6BV + +# [accounts.wallet_269] +# mnemonic = "faith panic damage normal early target invite scrap ramp venue hair belt phrase pigeon vicious scheme illegal like mean prison globe era credit among" +# balance = 100_000_000_000_000 +# # stx_address: ST3C4D478ZP3VVAXRJDVHRHX6J5YXWE5GEP4BQZVD + +# [accounts.wallet_270] +# mnemonic = "spike punch card tuition near noble early tray silent combine food this exhibit cushion error deputy captain melody property labor title snack nature case" +# balance = 100_000_000_000_000 +# # stx_address: ST2CRWMQGWB7E2819JA3JYCAH2FPYS92NM0T7QMW8 + +# [accounts.wallet_271] +# mnemonic = "nation off want depth please hip crunch combine supply opera very wall depth brown ignore shrimp issue near maid swing project eager absent history" +# balance = 100_000_000_000_000 +# # stx_address: ST2G52ZHB418J2AGJ2FT3FS9GEB4NMMD6A3H1WMD4 + +# [accounts.wallet_272] +# mnemonic = "ring rebuild festival top journey spray kingdom build raccoon critic rabbit snack try opera mixture fox moment notice clog ranch layer release spell square" +# balance = 100_000_000_000_000 +# # stx_address: STAX83AXMCZND0X3HYXYS0K4NNG66410RGWQ3SXD + +# [accounts.wallet_273] +# mnemonic = "large oil tissue detect weekend fresh olive also tired matrix enact thank brain exclude opinion kiwi stay ensure alcohol awkward engage vanish steak satoshi" +# balance = 100_000_000_000_000 +# # stx_address: ST3D730J0W7ZYRBYTKRD2RB7T0K8ERRVEPX8F1DVF + +# [accounts.wallet_274] +# mnemonic = "put people require opera size shove verify script record angle brass trigger tomato rough cat spy hotel elephant setup minimum pumpkin open woman lobster" +# balance = 100_000_000_000_000 +# # stx_address: STYA37DN4PTAR3M83WPWHV73KVPBBDGGW4FAF721 + +# [accounts.wallet_275] +# mnemonic = "hurdle kite race gentle wave pause hunt lecture shield royal caught oxygen doll trick sense cost have elite era hint way flee breeze liquid" +# balance = 100_000_000_000_000 +# # stx_address: STZRX6B4HY58CHAF0PWM7DCHCTYKGNV62VJNDYRA + +# [accounts.wallet_276] +# mnemonic = "mosquito nothing bounce anchor size estate canoe cook sand fan olive stove trumpet series flip size elephant hobby runway future exhaust resist valid lemon" +# balance = 100_000_000_000_000 +# # stx_address: ST112ARABY0EKE3S2HD1S2FP0J4DSBHZMZ2925C8T + +# [accounts.wallet_277] +# mnemonic = "spy air orchard lecture syrup traffic either uncover clean witness much small absurd detail utility drift shuffle defy dwarf hotel job calm acid cheese" +# balance = 100_000_000_000_000 +# # stx_address: ST2MP23P7HHTJAN1H0WEQ56F9GX19RWJNK3A17CT3 + +# [accounts.wallet_278] +# mnemonic = "lift sand dizzy utility finish fury flip town resemble combine flower void stick ball pattern parent either vivid eight sand few alley swarm juice" +# balance = 100_000_000_000_000 +# # stx_address: ST2E2TFWE14MJRGWSNTHK1CRQVM513QHJNVMAEAX + +# [accounts.wallet_279] +# mnemonic = "merry tortoise magnet squeeze pelican parrot sight tornado quantum angry ticket border island typical rail interest damage arch frozen private antique since habit rigid" +# balance = 100_000_000_000_000 +# # stx_address: ST3FCDKASSJPEKVZQV0ADE8VTWCC4QYY1BNX7TRJE + +# [accounts.wallet_280] +# mnemonic = "merit suggest rabbit where girl label horse marriage summer sport earn phrase theory ensure volcano job produce slight play exile kiwi often black repeat" +# balance = 100_000_000_000_000 +# # stx_address: ST172BC87JQR1SSNK4MB0XM3SWVQTZE88BTMEFVBX + +# [accounts.wallet_281] +# mnemonic = "weapon hope brand regular grid smooth enable cement assist symptom improve female divorce veteran nerve crumble balcony cry issue trophy drama meadow collect medal" +# balance = 100_000_000_000_000 +# # stx_address: ST27AHVK5TYF08DHD8WD8T212S379BJQH67Q4G6RD + +# [accounts.wallet_282] +# mnemonic = "neither buffalo slogan artefact fiscal weekend random tomorrow dog cupboard cube where also okay alert follow pear brave input mind cement celery truth modify" +# balance = 100_000_000_000_000 +# # stx_address: ST3FZN9NAJXPQJRRDHD1XWKC55VTK7523QC3ACY1N + +# [accounts.wallet_283] +# mnemonic = "window color model off water glad trial stage snap jazz shove wise attract jazz clip mansion alien guard police apple cheap nest bomb survey" +# balance = 100_000_000_000_000 +# # stx_address: ST14JCGKFXD40EEA3VBBS0CVDXGG5QYCBY0CMFC84 + +# [accounts.wallet_284] +# mnemonic = "link toe purchase remain modify laptop gap message quality fog half absent speak learn shuffle donor animal sunset popular brush antenna usual protect jar" +# balance = 100_000_000_000_000 +# # stx_address: ST4VT08M4YHAT8G2D55194P4N08DEVDG6K159J4V + +# [accounts.wallet_285] +# mnemonic = "confirm turtle parent alley planet bridge forum roof scout echo together enlist tomorrow rally flame cart second cause slender aisle crane gap brave nothing" +# balance = 100_000_000_000_000 +# # stx_address: ST2199Y9CM5F10TR4TD44AHWZJB9RPNZV9E50XKZC + +# [accounts.wallet_286] +# mnemonic = "glove knee mercy sail hope miracle circle hand that gadget spy stadium country warfare sure pond cancel edge palace film puzzle eagle fabric muscle" +# balance = 100_000_000_000_000 +# # stx_address: ST3116AAKYVQM0JQFMTD9MP27CAM93KR71K4JGZ3Y + +# [accounts.wallet_287] +# mnemonic = "safe frost curve super normal soda shoulder state urge help thing lift light learn swear cattle violin ritual report wash rain combine job gap" +# balance = 100_000_000_000_000 +# # stx_address: ST2DKAKYTR20FVWX9X62YQ8KY94B3SXZEX3Z850C7 + +# [accounts.wallet_288] +# mnemonic = "sad banana enforce height warrior oil add effort where rebel pledge dove tackle victory million pelican milk vast ridge measure survey fall animal fire" +# balance = 100_000_000_000_000 +# # stx_address: ST5NN3TJEVZSAP82MH4BA2BPTJ7WAA196ZJ18FBX + +# [accounts.wallet_289] +# mnemonic = "release piece image fancy ketchup improve permit camera man same ceiling cook slim buddy wide stereo uphold snap search half inside dash blur math" +# balance = 100_000_000_000_000 +# # stx_address: ST1G39YHBMPAY3M5HXECJX28VWK29Q30AV3QQGZJ0 + +# [accounts.wallet_290] +# mnemonic = "they flee slam crumble armor crack frequent video stuff urge gospel rain deny raw resemble team inform wolf buddy oxygen sponsor hospital knee track" +# balance = 100_000_000_000_000 +# # stx_address: STCYS3GMYXJ179Y0BEM2Q938K9YJPE2T827X9BGG + +# [accounts.wallet_291] +# mnemonic = "gauge gasp struggle enlist pull century math hover van put balance crash wise violin pet dream bacon switch spoon best smart federal lake notable" +# balance = 100_000_000_000_000 +# # stx_address: ST2F2D765S15Z50437MMVJK5N74W8AEN7S8P7FGC7 + +# [accounts.wallet_292] +# mnemonic = "then firm luxury inflict gospel old wonder image crucial oil green any innocent ordinary beauty wonder cannon crawl era merge awkward weasel reduce tumble" +# balance = 100_000_000_000_000 +# # stx_address: ST9G2K8Z4HE5XPV00V9DAZZF13981PX38WQV0VDS + +# [accounts.wallet_293] +# mnemonic = "lawsuit recipe quality ramp keep athlete horse comfort safe security junior river excess scheme drink okay tail lyrics abuse ghost cave token ice return" +# balance = 100_000_000_000_000 +# # stx_address: ST36M7B28BDRMFXXTS76X22GGMEN51SA5EXZYYWBC + +# [accounts.wallet_294] +# mnemonic = "uphold winter guard torch object muffin short nothing citizen isolate inspire major first season grain vast access hungry talent tongue clerk scrub poem scout" +# balance = 100_000_000_000_000 +# # stx_address: ST230BFE44WBW7S7077DNQBN5S2868ZKN2P7Y6T7M + +# [accounts.wallet_295] +# mnemonic = "moon hair slot misery gravity chalk lobster name reform can around mom confirm devote door job ahead avocado crowd hidden fitness engage run oil" +# balance = 100_000_000_000_000 +# # stx_address: ST1JSFPPMD5BYYW8NJ22VRY3C58G46041H4Q7AEA6 + +# [accounts.wallet_296] +# mnemonic = "wood force theory series donate festival suffer message vacant wood define story near year sound galaxy video width february display another impose cabin badge" +# balance = 100_000_000_000_000 +# # stx_address: ST1FZZ8WFE6SRD4W3YS8AC4XJTNGYZGXK2RT5K6EK + +# [accounts.wallet_297] +# mnemonic = "receive until robust loyal alone oval bone jazz update nation taxi bone wrap equip confirm fluid have raw either brick winner dust core one" +# balance = 100_000_000_000_000 +# # stx_address: ST3J2FN3FS6AVWW1C7D87J5RMK860YKE8JS55GCJB + +# [accounts.wallet_298] +# mnemonic = "warm pioneer obscure soldier never aerobic company drift enforce bunker dinner frog bid evil tissue fresh arch moral table embrace panther hunt reveal peasant" +# balance = 100_000_000_000_000 +# # stx_address: ST3CBP0S93SBVH24646HXRKA02BHA8J3DHV1MZZMZ + +# [accounts.wallet_299] +# mnemonic = "goddess shoot myth crash purchase junk raven gown hat valid mushroom weasel hidden opera regular saddle rival bulk chapter paper three nothing moon fuel" +# balance = 100_000_000_000_000 +# # stx_address: ST207PMA3S3GC9JN4HR1A4W77RVB06NXZGD388A8F + +# [accounts.wallet_300] +# mnemonic = "slim wild bridge shiver clinic grain music elegant cross monkey inform guilt green misery canyon sight letter believe attract card twenty ask duck boy" +# balance = 100_000_000_000_000 +# # stx_address: ST25GS8Q6ST2WY0XY80M2T9543H15PMRRQKEGVSA0 + +[accounts.faucet] +mnemonic = "shadow private easily thought say logic fault paddle word top book during ignore notable orange flight clock image wealth health outside kitten belt reform" +balance = 100_000_000_000_000 +# secret_key: de433bdfa14ec43aa1098d5be594c8ffb20a31485ff9de2923b2689471c401b801 +# stx_address: STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6 +# btc_address: mjSrB3wS4xab3kYqFktwBzfTdPg367ZJ2d + +[devnet] +# disable_stacks_explorer = true +# disable_stacks_api = true +stacks_node_wait_time_for_microblocks = 1_000 +stacks_node_first_attempt_time_ms = 15_000 +stacks_node_subsequent_attempt_time_ms = 5_000 +# disable_bitcoin_explorer = true +# working_dir = "tmp/devnet" +# stacks_node_events_observers = ["host.docker.internal:8002"] +# miner_mnemonic = "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw" +# miner_derivation_path = "m/44'/5757'/0'/0/0" +miner_coinbase_recipient = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.mining-pool-5-blocks" +# faucet_mnemonic = "shadow private easily thought say logic fault paddle word top book during ignore notable orange flight clock image wealth health outside kitten belt reform" +# faucet_derivation_path = "m/44'/5757'/0'/0/0" +# orchestrator_port = 20445 +# bitcoin_node_p2p_port = 18444 +# bitcoin_node_rpc_port = 18443 +# bitcoin_node_username = "devnet" +# bitcoin_node_password = "devnet" +bitcoin_controller_automining_disabled = true +bitcoin_controller_block_time = 1_000 +# stacks_node_rpc_port = 20443 +# stacks_node_p2p_port = 20444 +# stacks_api_port = 3999 +# stacks_api_events_port = 3700 +# bitcoin_explorer_port = 8001 +# stacks_explorer_port = 8000 +# postgres_port = 5432 +# postgres_username = "postgres" +# postgres_password = "postgres" +# postgres_database = "postgres" +# bitcoin_node_image_url = "quay.io/hirosystems/bitcoind:devnet-v2" +# stacks_node_image_url = "localhost:5000/stacks-node:devnet-v2" +stacks_api_image_url = "hirosystems/stacks-blockchain-api:latest" +stacks_explorer_image_url = "hirosystems/explorer:latest" +# bitcoin_explorer_image_url = "quay.io/hirosystems/bitcoin-explorer:devnet" +# postgres_image_url = "postgres:14" +# enable_subnet_node = true +# subnet_node_image_url = "hirosystems/hyperchains:0.0.4-stretch" +# subnet_leader_mnemonic = "female adjust gallery certain visit token during great side clown fitness like hurt clip knife warm bench start reunion globe detail dream depend fortune" +# subnet_leader_derivation_path = "m/44'/5757'/0'/0/0" +# subnet_contract_id = "STXMJXCJDCT4WPF2X1HE42T6ZCCK3TPMBRZ51JEG.hc-alpha" +# subnet_node_rpc_port = 30443 +# subnet_node_p2p_port = 30444 +# subnet_events_ingestion_port = 30445 +# subnet_node_events_observers = ["host.docker.internal:8002"] + +# For testing in epoch 2.4 / using Clarity2 +epoch_2_0 = 100 +epoch_2_05 = 102 +epoch_2_1 = 104 +pox_2_activation = 105 +epoch_2_2 = 106 +epoch_2_3 = 108 +epoch_2_4 = 112 + +# # Send some stacking orders +# [[devnet.pox_stacking_orders]] +# start_at_cycle = 3 +# duration = 12 +# wallet = "wallet_1" +# slots = 2 +# btc_address = "mr1iPkD9N3RJZZxXRk7xF9d36gffa6exNC" + +[[devnet.pox_stacking_orders]] +start_at_cycle = 2 +duration = 12 +wallet = "wallet_5" +slots = 1 +btc_address = "muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG" + +# # [[devnet.pox_stacking_orders]] +# start_at_cycle = 3 +# duration = 12 +# wallet = "wallet_3" +# slots = 1 +# btc_address = "mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7" diff --git a/sbtc-ops/smart-contract/tests/bridge-contract_test.ts b/sbtc-ops/smart-contract/tests/bridge-contract_test.ts new file mode 100644 index 00000000..6dcbf71c --- /dev/null +++ b/sbtc-ops/smart-contract/tests/bridge-contract_test.ts @@ -0,0 +1,349 @@ +import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet@v1.6.1/index.ts'; +import { assertEquals } from 'https://deno.land/std@0.170.0/testing/asserts.ts'; + +const ammSwapPoolContract = 'amm-swap-pool-v1-1'; +const degenBridgeContract = 'degen-bridge-testnet-v3'; +const wrappedBitcoinContract = 'Wrapped-Bitcoin'; +const alexVaultContract = 'alex-vault-v1-1'; +const bridgeContract = 'bridge-contract'; +const createPool = 'create-pool'; +const initializeWrappedBitcoinSC = 'initialize'; +const swapBridgeStxBtc = 'swap-bridge-stx-btc'; +const setMaxInRatio = 'set-max-in-ratio'; +const setMaxOutRatio = 'set-max-out-ratio'; +const setPoolStartBlock = 'set-start-block'; +const getPoolDetails = 'get-pool-details'; +const addPrincipalToRole = 'add-principal-to-role'; +const setApprovedSC = 'set-approved-contract'; +const setApprovedToken = ' set-approved-token'; +const bridgeSwapFn = 'swap-helper'; +const minterRole = 1; +const mintWrappedBitcoin = 'mint-tokens'; +const registerSupplier = 'register-supplier'; +const wrappedBitcoinTokenName = 'Wrapped Bitcoin'; +const wrappedBitcoinSymbol = 'xBTC'; +const wrappedBitcoinDecimals = 8; +const xBtcStxTransferResults = { + 0.1: [417032098765, 406860584161, 397056714663, 387602983362, 378482913165, 369680984952, 361182571504, 352973876697], +}; +const StxXbtcTransferResults = { + 100: [286470, 286283, 286097, 285911, 285725, 285539, 285354, 285168], +}; + +const to_one_8 = (amount) => { + return amount * 100_000_000; +}; +const div_one_8 = (amount) => { + return amount / 100_000_000; +}; + +Clarinet.test({ + name: 'Exchanging both STX to xBTC and xBTC to STX with 8 users', + async fn(chain: Chain, accounts: Map) { + let deployer = accounts.get('deployer')!; + let wallet_1 = accounts.get('wallet_1')!; + let wallet_2 = accounts.get('wallet_2')!; + let wallet_3 = accounts.get('wallet_3')!; + let wallet_4 = accounts.get('wallet_4')!; + let wallet_5 = accounts.get('wallet_5')!; + let wallet_6 = accounts.get('wallet_6')!; + + // Prerequirements + + // 1. Wrapped-Bitcoin + + // a. Initialize the Wrapped-Bitcoin contract (token-name, symbol, decimals, owner) + // b. Add minter role for deployer in order to mint + let block = chain.mineBlock([ + Tx.contractCall( + wrappedBitcoinContract, + initializeWrappedBitcoinSC, + [ + types.ascii(wrappedBitcoinTokenName), + types.ascii(wrappedBitcoinSymbol), + types.uint(wrappedBitcoinDecimals), + types.principal(deployer.address), + ], + deployer.address + ), + Tx.contractCall( + wrappedBitcoinContract, + addPrincipalToRole, + [types.uint(minterRole), types.principal(deployer.address)], + deployer.address + ), + ]); + + assertEquals(block.receipts.length, 2); + assertEquals(block.height, 2); + block.receipts[0].result.expectOk().expectBool(true); + block.receipts[1].result.expectOk().expectBool(true); + + // c. Mint resources in order to create the trading pool + block = chain.mineBlock([ + Tx.contractCall( + wrappedBitcoinContract, + mintWrappedBitcoin, + [ + types.uint(to_one_8(12)), // 12 BTC + types.principal(deployer.address), + ], + deployer.address + ), + ]); + + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 3); + block.receipts[0].result.expectOk().expectBool(true); + + for (let i = 1; i <= 4; i++) { + block = chain.mineBlock([ + Tx.contractCall( + wrappedBitcoinContract, + mintWrappedBitcoin, + [ + types.uint(to_one_8(12)), // 12 BTC + types.principal(accounts.get(`wallet_${2 * i - 1}`)!.address), + ], + deployer.address + ), + Tx.contractCall( + wrappedBitcoinContract, + mintWrappedBitcoin, + [ + types.uint(to_one_8(12)), // 12 BTC + types.principal(accounts.get(`wallet_${2 * i}`)!.address), + ], + deployer.address + ), + ]); + + assertEquals(block.receipts.length, 2); + assertEquals(block.height, i + 3); + block.receipts[0].result.expectOk().expectBool(true); + } + + // 2. alex-vault-v1-1 + + // a. Set Approved Contract: amm-swap-pool-v1-1 + // b. Approve tokens: .wbtc, .wstx + block = chain.mineBlock([ + Tx.contractCall( + alexVaultContract, + setApprovedSC, + [types.principal(`${deployer.address}.amm-swap-pool-v1-1`), types.bool(true)], + deployer.address + ), + Tx.contractCall( + alexVaultContract, + setApprovedToken, + [types.principal(`${deployer.address}.token-wbtc`), types.bool(true)], + deployer.address + ), + Tx.contractCall( + alexVaultContract, + setApprovedToken, + [types.principal(`${deployer.address}.token-wstx`), types.bool(true)], + deployer.address + ), + ]); + + assertEquals(block.receipts.length, 3); + assertEquals(block.height, 8); + block.receipts[0].result.expectOk().expectBool(true); + block.receipts[1].result.expectOk().expectBool(true); + block.receipts[2].result.expectOk().expectBool(true); + + // 3. amm-swap-pool-v1-1 + + // a. Create the xBTC - STX trading pool + // b. Set Max In Ratio + // c. Set Max Out Ratio + // d. Set Start Block + // e. check pool details to be correct + block = chain.mineBlock([ + Tx.contractCall( + ammSwapPoolContract, + createPool, + [ + types.principal(`${deployer.address}.token-wbtc`), + types.principal(`${deployer.address}.token-wstx`), + types.uint(100_000_000), + types.principal(deployer.address), + types.uint(to_one_8(8)), // 8 BTC + types.uint(to_one_8(337_796)), // 337_796 STX + ], + deployer.address + ), + Tx.contractCall( + ammSwapPoolContract, + setMaxInRatio, + [ + types.principal(`${deployer.address}.token-wbtc`), + types.principal(`${deployer.address}.token-wstx`), + types.uint(100_000_000), + types.uint(to_one_8(0.5)), // in ratio == 0.5 == 50% of balance + ], + deployer.address + ), + Tx.contractCall( + ammSwapPoolContract, + setMaxOutRatio, + [ + types.principal(`${deployer.address}.token-wbtc`), + types.principal(`${deployer.address}.token-wstx`), + types.uint(100_000_000), + types.uint(to_one_8(0.5)), // out ratio == 0.5 == 50% of balance + ], + deployer.address + ), + Tx.contractCall( + ammSwapPoolContract, + setPoolStartBlock, + [ + types.principal(`${deployer.address}.token-wbtc`), + types.principal(`${deployer.address}.token-wstx`), + types.uint(100_000_000), + types.uint(1), //start block + ], + deployer.address + ), + Tx.contractCall( + ammSwapPoolContract, + getPoolDetails, + [ + types.principal(`${deployer.address}.token-wbtc`), + types.principal(`${deployer.address}.token-wstx`), + types.uint(100_000_000), + ], + deployer.address + ), + ]); + + assertEquals(block.receipts.length, 5); + assertEquals(block.height, 9); + block.receipts[0].result.expectOk().expectBool(true); + block.receipts[1].result.expectOk().expectBool(true); + block.receipts[2].result.expectOk().expectBool(true); + block.receipts[3].result.expectOk().expectBool(true); + console.log('Trading Pool details: ', block.receipts[4].result); + + // 4. bridge-contract + + // a. swap 10_000 sats using the exchange function + console.log('8 SWAPS 0.1 xBTC to STX:'); + for (let i = 1; i <= 8; i++) { + block = chain.mineBlock([ + Tx.contractCall( + bridgeContract, + bridgeSwapFn, + [ + types.principal(`${deployer.address}.token-wbtc`), + types.principal(`${deployer.address}.token-wstx`), + types.uint(to_one_8(0.1)), //0.1 xBTC, we have to parse amount in xBTC * 10^8 (sats) + types.uint(5), + ], + accounts.get(`wallet_${i}`).address + ), + ]); + + assertEquals(block.receipts.length, 1); + assertEquals(block.height, i + 9); + + block.receipts[0].result + .expectOk() + .expectOk() + .expectUint(xBtcStxTransferResults[0.1][i - 1]); + let StxExchangeResult = block.receipts[0].result + .expectOk() + .expectOk() + .expectUint(xBtcStxTransferResults[0.1][i - 1]); + console.log( + '0.1 xBTC == ', + + div_one_8(parseFloat(StxExchangeResult)), + 'STX' + ); + } + for (let i = 1; i <= 8; i++) { + block = chain.mineBlock([ + Tx.contractCall( + bridgeContract, + bridgeSwapFn, + [ + types.principal(`${deployer.address}.token-wstx`), + types.principal(`${deployer.address}.token-wbtc`), + types.uint(to_one_8(100)), //100 STX, we have to parse amount in STX * 10^8 + types.uint(5), + ], + accounts.get(`wallet_${i}`).address + ), + ]); + + assertEquals(block.receipts.length, 1); + console.log(i + 20); + assertEquals(block.height, i + 17); + let StxExchangeResult = block.receipts[0].result + .expectOk() + .expectOk() + .expectUint(StxXbtcTransferResults[100][i - 1]); + console.log('100 STX == ', div_one_8(parseFloat(StxExchangeResult)), 'xBTC'); + } + // register supplier Bridge + + block = chain.mineBlock([ + Tx.contractCall( + degenBridgeContract, + registerSupplier, + [ + types.buff( + Uint8Array.from([ + 2, 186, 94, 65, 230, 231, 129, 119, 148, 195, 178, 221, 245, 108, 78, 140, 61, 60, 139, 195, 90, 1, 107, + 255, 151, 68, 49, 178, 71, 209, 100, 179, 235, + ]) // the Uint8Array corresponding to publicKey: 02ba5e41e6e7817794c3b2ddf56c4e8c3d3c8bc35a016bff974431b247d164b3eb + ), + types.none(), + types.none(), + types.int(0), + types.int(to_one_8(0.1)), // supply with 0.1 xBTC + types.uint(1000), + ], + deployer.address + ), + ]); + + // assert: review returned data, contract state, and other requirements + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 26); + block.receipts[0].result.expectOk().expectUint(0); // supplier Id + + // register supplier Bridge + + block = chain.mineBlock([ + Tx.contractCall( + bridgeContract, + swapBridgeStxBtc, + [ + types.principal(`${deployer.address}.token-wstx`), + types.principal(`${deployer.address}.token-wbtc`), + types.uint(to_one_8(100)), //100 STX + types.uint(5), + types.buff( + Uint8Array.from([0]) // the Uint8Array corresponding to btc address version (00 or 0x00) + ), + types.buff( + Uint8Array.from([ + 217, 70, 197, 171, 28, 179, 27, 246, 208, 166, 125, 130, 78, 84, 131, 147, 220, 163, 185, 248, + ]) + ), //02e8f7dc91e49a577ce9ea8989c7184aea8886fe5250f02120dc6f98e3619679b0 + types.uint(0), + ], + wallet_1.address + ), + ]); + + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 27); + block.receipts[0].result.expectOk().expectUint(284983); + }, +}); diff --git a/sbtc-ops/smart-contract/tests/client/pox-2-client.ts b/sbtc-ops/smart-contract/tests/client/pox-2-client.ts new file mode 100644 index 00000000..692ae1dc --- /dev/null +++ b/sbtc-ops/smart-contract/tests/client/pox-2-client.ts @@ -0,0 +1,96 @@ +import { Chain, Tx, types, Account } from '../deps.ts'; + +export function allowContractCaller(contractCaller: string, untilBurnHt: number | undefined, user: Account) { + return Tx.contractCall( + 'ST000000000000000000002AMW42H.pox-3', + 'allow-contract-caller', + [types.principal(contractCaller), untilBurnHt ? types.some(types.uint(untilBurnHt)) : types.none()], + user.address + ); +} + +export function disallowContractCaller(contractCaller: string, user: Account) { + return Tx.contractCall( + 'ST000000000000000000002AMW42H.pox-3', + 'disallow-contract-caller', + [types.principal(contractCaller)], + user.address + ); +} + +export function delegateStx(amount: number, delegateTo: string, user: Account) { + return Tx.contractCall( + 'ST000000000000000000002AMW42H.pox-3', + 'delegate-stx', + [types.uint(amount), types.principal(delegateTo), types.none(), types.none()], + user.address + ); +} + +export function stackAggregationCommitIndexed( + poxAddr: { version: string; hashbytes: string }, + cycle: number, + poolOperator: Account +) { + return Tx.contractCall( + 'ST000000000000000000002AMW42H.pox-3', + 'stack-aggregation-commit-indexed', + [types.tuple(poxAddr), types.uint(cycle)], + poolOperator.address + ); +} + +export function stackAggregationIncrease( + poxAddr: { version: string; hashbytes: string }, + cycle: number, + poxAddrIndex: number, + poolOperator: Account +) { + return Tx.contractCall( + 'ST000000000000000000002AMW42H.pox-3', + 'stack-aggregation-increase', + [types.tuple(poxAddr), types.uint(cycle), types.uint(poxAddrIndex)], + poolOperator.address + ); +} + +export function revokeDelegateStx(user: Account) { + return Tx.contractCall('ST000000000000000000002AMW42H.pox-3', 'revoke-delegate-stx', [], user.address); +} + +export function getPartialStackedByCycle( + poolPoxAddr: { version: string; hashbytes: string }, + cycle: number, + poolAddress: string, + chain: Chain, + user: Account +) { + return chain.callReadOnlyFn( + 'ST000000000000000000002AMW42H.pox-3', + 'get-partial-stacked-by-cycle', + [types.tuple(poolPoxAddr), types.uint(cycle), types.principal(poolAddress)], + user.address + ); +} + +export function getRewardSetPoxAddress(cycle: number, index: number, chain: Chain, user: Account) { + return chain.callReadOnlyFn( + 'ST000000000000000000002AMW42H.pox-3', + 'get-reward-set-pox-address', + [types.uint(cycle), types.uint(index)], + user.address + ); +} + +export function getPoxInfo(chain: Chain, user: Account) { + return chain.callReadOnlyFn('ST000000000000000000002AMW42H.pox-3', 'get-pox-info', [], user.address); +} + +// export function getStackerInfo(chain: Chain, user: Account, stacker: Account) { +// return chain.contractCall( +// "ST000000000000000000002AMW42H.pox-2", +// "get-stacker-info", +// [types.principal(stacker)], +// user.address +// ); +// } diff --git a/sbtc-ops/smart-contract/tests/client/pox-pools-1-cycle-client.ts b/sbtc-ops/smart-contract/tests/client/pox-pools-1-cycle-client.ts new file mode 100644 index 00000000..4c953735 --- /dev/null +++ b/sbtc-ops/smart-contract/tests/client/pox-pools-1-cycle-client.ts @@ -0,0 +1,148 @@ +import { Chain, Tx, types, Account } from "../deps.ts"; + +export function poxDelegationAllowContractCaller( + contractCaller: string, + untilBurnHt: number | undefined, + user: Account +) { + return Tx.contractCall( + "pox-pools-1-cycle", + "allow-contract-caller", + [ + types.principal(contractCaller), + untilBurnHt ? types.some(types.uint(untilBurnHt)) : types.none(), + ], + user.address + ); +} + +export function delegateStx( + amount: number, + poolAddress: string, + untilBurnHt: number | undefined, + poolPoxAddr: { version: string; hashbytes: string } | undefined, + userPoxAddr: { version: string; hashbytes: string }, + user: Account +) { + return Tx.contractCall( + "pox-pools-1-cycle", + "delegate-stx", + [ + types.uint(amount), + types.principal(poolAddress), + untilBurnHt ? types.some(types.uint(untilBurnHt)) : types.none(), + poolPoxAddr ? types.some(types.tuple(poolPoxAddr)) : types.none(), + types.tuple(userPoxAddr), + ], + user.address + ); +} + +export function delegateStackStx( + members: { user: Account; amountUstx: number }[], + poolPoxAddr: { version: string; hashbytes: string }, + startBurnHt: number, + poolOperator: Account +) { + return Tx.contractCall( + "pox-pools-1-cycle", + "delegate-stack-stx", + [ + types.list( + members.map((m) => + types.tuple({ + user: types.principal(m.user.address), + "amount-ustx": types.uint(m.amountUstx), + }) + ) + ), + + types.tuple(poolPoxAddr), + types.uint(startBurnHt), + ], + poolOperator.address + ); +} + +export function delegateStackStxSimple( + members: Account[], + poolPoxAddr: { version: string; hashbytes: string }, + startBurnHt: number, + poolOperator: Account +) { + return Tx.contractCall( + "pox-pools-1-cycle", + "delegate-stack-stx-simple", + [ + types.list(members.map((u) => types.principal(u.address))), + types.tuple(poolPoxAddr), + types.uint(startBurnHt), + ], + poolOperator.address + ); +} +export function getStatusListsLastIndex( + poolAddress: string, + cycle: number, + chain: Chain, + user: Account +) { + return chain.callReadOnlyFn( + "pox-pools-1-cycle", + "get-status-lists-last-index", + [types.principal(poolAddress), types.uint(cycle)], + user.address + ); +} + +export function getStatusList( + poolAddress: string, + cycle: number, + index: number, + chain: Chain, + user: Account +) { + return chain.callReadOnlyFn( + "pox-pools-1-cycle", + "get-status-list", + [types.principal(poolAddress), types.uint(cycle), types.uint(index)], + user.address + ); +} + +export function getTotal( + poolAddress: string, + cycle: number, + chain: Chain, + user: Account +) { + return chain.callReadOnlyFn( + "pox-pools-1-cycle", + "get-total", + [types.principal(poolAddress), types.uint(cycle)], + user.address + ); +} + +export function getStatus( + poolAddress: string, + userAddress: string, + chain: Chain, + user: Account +) { + return chain.callReadOnlyFn( + "pox-pools-1-cycle", + "get-status", + [types.principal(poolAddress), types.principal(userAddress)], + user.address + ); +} + +export function getUserData(userAddress: string, chain: Chain, user: Account) { + return chain.callReadOnlyFn( + "pox-pools-1-cycle", + "get-user-data", + [types.principal(userAddress)], + user.address + ); +} diff --git a/sbtc-ops/smart-contract/tests/client/stacking-pool-client.ts b/sbtc-ops/smart-contract/tests/client/stacking-pool-client.ts new file mode 100644 index 00000000..9730cb79 --- /dev/null +++ b/sbtc-ops/smart-contract/tests/client/stacking-pool-client.ts @@ -0,0 +1,88 @@ +import { Chain, Tx, types, Account } from '../deps.ts'; + +export function fpDelegationAllowContractCaller( + contractCaller: string, + untilBurnHt: number | undefined, + user: Account +) { + return Tx.contractCall( + 'stacking-pool-test', + 'allow-contract-caller', + [types.principal(contractCaller), untilBurnHt ? types.some(types.uint(untilBurnHt)) : types.none()], + user.address + ); +} + +export function delegateStx(amount: number, user: Account) { + return Tx.contractCall('stacking-pool-test', 'delegate-stx', [types.uint(amount)], user.address); +} + +export function delegateStackStx(stacker: Account, user: Account) { + return Tx.contractCall('stacking-pool-test', 'delegate-stack-stx', [types.principal(stacker.address)], user.address); +} + +export function joinStackingPool(user: Account) { + return Tx.contractCall('stacking-pool-test', 'join-stacking-pool', [], user.address); +} + +export function quitStackingPool(user: Account) { + return Tx.contractCall('stacking-pool-test', 'quit-stacking-pool', [], user.address); +} + +export function delegateStackStxMany(stackers: Account[], user: Account) { + return Tx.contractCall( + 'stacking-pool-test', + 'delegate-stack-stx-many', + [types.list(stackers.map((s) => types.principal(s.address)))], + user.address + ); +} + +export function getUserData(stacker: Account, user: Account) { + return Tx.contractCall('stacking-pool-test', 'get-user-data', [types.principal(stacker.address)], user.address); +} + +// // admin functions + +// export function setActive(active: boolean, user: Account) { +// return Tx.contractCall( +// "stacking-pool", +// "set-active", +// [types.bool(active)], +// user.address +// ); +// } + +// export function setStxBuffer(amount: number, user: Account) { +// return Tx.contractCall( +// "stacking-pool", +// "set-stx-buffer", +// [types.uint(amount)], +// user.address +// ); +// } + +// export function setPoolPoxAddress( +// poxAddress: { hashbytes: string; version: string }, +// user: Account +// ) { +// return Tx.contractCall( +// "stacking-pool", +// "set-pool-pox-address", +// [types.tuple(poxAddress)], +// user.address +// ); +// } + +// export function setRewardAdmin( +// newAdmin: string, +// enable: boolean, +// user: Account +// ) { +// return Tx.contractCall( +// "stacking-pool", +// "set-reward-admin", +// [types.principal(newAdmin), types.bool(enable)], +// user.address +// ); +// } diff --git a/sbtc-ops/smart-contract/tests/constants.ts b/sbtc-ops/smart-contract/tests/constants.ts new file mode 100644 index 00000000..bf2589e6 --- /dev/null +++ b/sbtc-ops/smart-contract/tests/constants.ts @@ -0,0 +1,52 @@ +export const btcAddrWallet1 = { + version: "0x01", + hashbytes: "0x000102030405060708090a0b0c0d0e0f00010203", +}; + +export const btcAddrWallet2 = { + version: "0x01", + hashbytes: "0x00102030405060708090a0b0c0d0e0f000102030", +}; + +export const poxAddrPool1 = { + version: "0x01", + hashbytes: "0xb0b75f408a29c271d107e05d614d0ff439813d02", +}; + +export const poxAddrPool2 = { + version: "0x01", + hashbytes: "0x00b0b75f408a29c271d107e05d614d0ff439813d", +}; + +// as defined in pox-pool-self-service.clar +export const poxAddrFP = { + version: "0x04", + hashbytes: "0x83ed66860315e334010bbfb76eb3eef887efee0a", +}; + +export const Errors = { + NotInPool: 102, + AllowPoolInPox2: 199, + NotFound: 404, + NonPositiveAmount: 500, + NoStackerInfo: 501, + NoUserInfo: 502, + StackingPermissionDenied: 609, +}; + +export const PoxErrors = { + StackingAlreadyStacked: 3, + StackingPermissionDenied: 9, + StackingThresholdNotMet: 11, + DelegationTooMuchLocked: 22, + InvalidStartBurnHeight: 24, + StackExtendNotLocked: 26, +}; + +export const FpErrors = { + Unauthorized: 401, + Forbidden: 403, + TooEarly: 500, + PoxAddressDeactivated: 504, + StackingPermissionDenied: 609, +}; diff --git a/sbtc-ops/smart-contract/tests/deps.ts b/sbtc-ops/smart-contract/tests/deps.ts new file mode 100644 index 00000000..be48ace6 --- /dev/null +++ b/sbtc-ops/smart-contract/tests/deps.ts @@ -0,0 +1,22 @@ +export type { + Account, + ReadOnlyFn, + } from "https://deno.land/x/clarinet@v1.5.4/index.ts"; + + export { + Clarinet, + Chain, + Tx, + types, + } from "https://deno.land/x/clarinet@v1.5.4/index.ts"; + + export { + assertEquals, + assertObjectMatch, + } from "https://deno.land/std@0.182.0/testing/asserts.ts"; + + export { + beforeEach, + describe, + it, + } from "https://deno.land/x/test_suite@0.16.1/mod.ts"; diff --git a/sbtc-ops/smart-contract/tests/mining-pool-5-blocks_test.ts b/sbtc-ops/smart-contract/tests/mining-pool-5-blocks_test.ts new file mode 100644 index 00000000..bdba446d --- /dev/null +++ b/sbtc-ops/smart-contract/tests/mining-pool-5-blocks_test.ts @@ -0,0 +1,259 @@ +// import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet@v1.5.4/index.ts'; +// import { assertEquals } from 'https://deno.land/std@0.170.0/testing/asserts.ts'; +// import sha256 from 'https://deno.land/x/sha256js/sha256.mjs'; +// import ripemd160 from 'https://deno.land/x/ripemd160js@v2.0.3/ripemd160.mjs'; + +// const CONVERT_TO_STX = (amount: number) => { +// return amount * 1000000; +// }; + +// const CONTRACT_NAME = 'mining-pool-5-blocks'; +// const ASK_TO_JOIN = 'ask-to-join'; +// const GET_MINERS_LIST = 'get-miners-list'; +// const GET_WAITING_LIST = 'get-waiting-list'; +// const GET_PENDING_LIST = `get-pending-accept-list`; +// const ADD_PENDING_MINERS = 'add-pending-miners-to-pool'; +// const VOTE_POSITIVE_JOIN = 'vote-positive-join-request'; +// const VOTE_NEGATIVE_JOIN = 'vote-negative-join-request'; +// const VOTE_POSITIVE_REMOVE = 'vote-positive-remove-request'; +// const VOTE_NEGATIVE_REMOVE = 'vote-negative-remove-request'; +// const START_VOTE_NOTIFIER = 'start-vote-notifier'; +// const END_VOTE_NOTIFIER = 'end-vote-notifier'; +// const VOTE_NOTIFIER = 'vote-notifier'; +// const DEPOSIT = 'deposit-stx'; +// const WITHDRAW = 'withdraw-stx'; +// const GET_BALANCE = 'get-balance'; +// const GET_REWARD_AT_BLOCK_READ = 'get-reward-at-block-read'; +// const REWARD_DISTRIBUTION = 'reward-distribution'; +// const GET_DATA_WAITING_MINER = 'get-all-data-waiting-miners'; +// const GET_DATA_REMOVAL = 'get-all-data-miners-proposed-for-removal'; +// const err_insufficient_balance = '(err u1001)'; +// const err_missing_balance = '(err u1002)'; +// const LEAVE_POOL = 'leave-pool'; +// const TRY_ENTER_POOL = 'try-enter-pool'; +// const PROPOSE_REMOVAL = 'propose-removal'; +// const PROPOSE_NOTIFIER = 'propose-notifier'; +// const GET_PROPOSED_NOTIFIERS_LIST = 'get-proposed-notifiers-list'; +// const GET_K = 'get-k'; +// const GET_NOTIFIER = 'get-notifier'; +// const GET_NOTIFIER_VOTE_STATUS = 'get-notifier-vote-status'; +// const GET_NOTIFIER_VOTE_NUMBER = 'get-notifier-vote-number'; +// const GET_MAX_VOTES_NOTIFIER = 'get-max-votes-notifier '; +// const GET_MAX_VOTED_NOTIFIER = 'get-max-voted-notifier '; +// const err_no_vote_permission = '(err u105)'; +// const err_more_blocks_to_pass = '(err u106)'; +// const err_no_pending_miners = '(err u107)'; +// const err_already_voted = '(err u108)'; +// const err_not_asked_to_join = '(err u109)'; +// const err_notifier = '(err u113)'; +// const err_not_proposed_removal = '(err u117)'; +// const err_cant_vote_himself = '(err u119)'; +// const err_already_proposed_for_notifier = '(err u121)'; +// const err_not_proposed_notifier = '(err u124)'; +// const err_already_notifier = '(err u125)'; +// const err_no_voting_period = '(err u129)'; + +// // Conversions for hashing +// const publicKeyHex = '02e8f7dc91e49a577ce9ea8989c7184aea8886fe5250f02120dc6f98e3619679b0'; + +// const buffer_from = (num: string) => { +// const hexString = num; +// const byteArray = new Uint8Array(hexString.length / 2); + +// for (let i = 0; i < byteArray.length; i++) { +// byteArray[i] = parseInt(hexString.substr(i * 2, 2), 16); +// } +// return byteArray; +// }; + +// const hash160 = (value: any) => { +// return sha256(value).then((x: any) => ripemd160(x)); +// }; + +// Clarinet.test({ +// name: 'Get All Data Waiting 300 Miners', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// let waiting_list = []; +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall( +// CONTRACT_NAME, +// ASK_TO_JOIN, +// [ +// types.tuple({ +// version: types.buff(hash160(buffer_from('00'))), +// hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), +// }), +// ], +// miner.address +// ), +// ]); +// if (i == 1) waiting_list.push(`${miner.address}`); +// else waiting_list.push(` ${miner.address}`); +// } +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${waiting_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 302); + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), +// ]); +// } + +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${waiting_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 602); + +// for (let i = 1; i <= 50; i++) { +// block = chain.mineBlock([]); +// } + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[${deployer.address}, ${waiting_list}]`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 653); + +// block = chain.mineBlock([ +// Tx.contractCall( +// CONTRACT_NAME, +// ASK_TO_JOIN, +// [ +// types.tuple({ +// version: types.buff(hash160(buffer_from('00'))), +// hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), +// }), +// ], +// accounts.get(`wallet_${300}`)!.address +// ), +// Tx.contractCall( +// CONTRACT_NAME, +// GET_DATA_WAITING_MINER, +// [types.list([types.principal(accounts.get(`wallet_${300}`)!.address)])], +// deployer.address +// ), +// ]); + +// for (let i = 91; i <= 299; i++) { +// const miner = accounts.get(`wallet_${300}`)!; +// block = chain.mineBlock([ +// Tx.contractCall( +// CONTRACT_NAME, +// VOTE_POSITIVE_JOIN, +// [types.principal(miner.address)], +// accounts.get(`wallet_${i}`)!.address +// ), +// ]); +// } + +// for (let i = 1; i <= 90; i++) { +// const miner = accounts.get(`wallet_${300}`)!; +// block = chain.mineBlock([ +// Tx.contractCall( +// CONTRACT_NAME, +// VOTE_NEGATIVE_JOIN, +// [types.principal(miner.address)], +// accounts.get(`wallet_${i}`)!.address +// ), +// ]); +// } + +// block = chain.mineBlock([ +// Tx.contractCall( +// CONTRACT_NAME, +// GET_DATA_WAITING_MINER, +// [types.list([types.principal(accounts.get(`wallet_${300}`)!.address)])], +// deployer.address +// ), +// ]); +// }, +// }); + +// Clarinet.test({ +// name: 'Get All Data Removals 300 Miners', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// let waiting_list = []; +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall( +// CONTRACT_NAME, +// ASK_TO_JOIN, +// [ +// types.tuple({ +// version: types.buff(hash160(buffer_from('00'))), +// hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), +// }), +// ], +// miner.address +// ), +// ]); +// if (i == 1) waiting_list.push(`${miner.address}`); +// else waiting_list.push(` ${miner.address}`); +// } +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${waiting_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 302); + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), +// ]); +// } + +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${waiting_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 602); + +// for (let i = 1; i <= 50; i++) { +// block = chain.mineBlock([]); +// } + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[${deployer.address}, ${waiting_list}]`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 653); + +// block = chain.mineBlock([ +// Tx.contractCall( +// CONTRACT_NAME, +// PROPOSE_REMOVAL, +// [types.principal(accounts.get(`wallet_${299}`)!.address)], +// deployer.address +// ), +// Tx.contractCall( +// CONTRACT_NAME, +// GET_DATA_REMOVAL, +// [types.list([types.principal(accounts.get(`wallet_${299}`)!.address)])], +// deployer.address +// ), +// ]); +// }, +// }); diff --git a/sbtc-ops/smart-contract/tests/mining-pool-test_test.ts b/sbtc-ops/smart-contract/tests/mining-pool-test_test.ts new file mode 100644 index 00000000..444544ee --- /dev/null +++ b/sbtc-ops/smart-contract/tests/mining-pool-test_test.ts @@ -0,0 +1,2582 @@ +// import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet@v1.4.0/index.ts'; +// import { assertEquals } from 'https://deno.land/std@0.170.0/testing/asserts.ts'; + +// const CONVERT_TO_STX = (amount: number) => { +// return amount * 1000000; +// }; + +// const CONTRACT_NAME = 'mining-pool-test'; +// const ASK_TO_JOIN = 'ask-to-join'; +// const GET_MINERS_LIST = 'get-miners-list'; +// const GET_WAITING_LIST = 'get-waiting-list'; +// const GET_PENDING_LIST = `get-pending-accept-list`; +// const ADD_PENDING_MINERS = 'add-pending-miners-to-pool'; +// const VOTE_POSITIVE_JOIN = 'vote-positive-join-request'; +// const VOTE_NEGATIVE_JOIN = 'vote-negative-join-request'; +// const VOTE_POSITIVE_REMOVE = 'vote-positive-remove-request'; +// const VOTE_NEGATIVE_REMOVE = 'vote-negative-remove-request'; +// const START_VOTE_NOTIFIER = 'start-vote-notifier'; +// const END_VOTE_NOTIFIER = 'end-vote-notifier'; +// const VOTE_NOTIFIER = 'vote-notifier'; +// const DEPOSIT = 'deposit-stx'; +// const WITHDRAW = 'withdraw-stx'; +// const GET_BALANCE = 'get-balance'; +// const GET_REWARD_AT_BLOCK_READ = 'get-reward-at-block-read'; +// const REWARD_DISTRIBUTION = 'reward-distribution'; +// const GET_DATA_WAITING_MINER = 'get-all-data-waiting-miners'; +// const GET_DATA_REMOVAL = 'get-all-data-miners-proposed-for-removal'; +// const err_insufficient_balance = '(err u1001)'; +// const err_missing_balance = '(err u1002)'; +// const LEAVE_POOL = 'leave-pool'; +// const TRY_ENTER_POOL = 'try-enter-pool'; +// const PROPOSE_REMOVAL = 'propose-removal'; +// const PROPOSE_NOTIFIER = 'propose-notifier'; +// const GET_PROPOSED_NOTIFIERS_LIST = 'get-proposed-notifiers-list'; +// const GET_K = 'get-k'; +// const GET_NOTIFIER = 'get-notifier'; +// const GET_NOTIFIER_VOTE_STATUS = 'get-notifier-vote-status'; +// const GET_NOTIFIER_VOTE_NUMBER = 'get-notifier-vote-number'; +// const GET_MAX_VOTES_NOTIFIER = 'get-max-votes-notifier '; +// const GET_MAX_VOTED_NOTIFIER = 'get-max-voted-notifier '; +// const err_no_vote_permission = '(err u105)'; +// const err_more_blocks_to_pass = '(err u106)'; +// const err_no_pending_miners = '(err u107)'; +// const err_already_voted = '(err u108)'; +// const err_not_asked_to_join = '(err u109)'; +// const err_notifier = '(err u113)'; +// const err_not_proposed_removal = '(err u117)'; +// const err_cant_vote_himself = '(err u119)'; +// const err_already_proposed_for_notifier = '(err u121)'; +// const err_not_proposed_notifier = '(err u124)'; +// const err_already_notifier = '(err u125)'; +// const err_no_voting_period = '(err u129)'; + +// Clarinet.test({ +// name: 'Get All Data Waiting 300 Miners', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// let testing_list = []; +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(miner.address)], miner.address), +// ]); +// if (i == 1) testing_list.push(`${miner.address}`); +// else testing_list.push(` ${miner.address}`); +// } +// const testing_list_length = testing_list.length; +// assertEquals(testing_list_length, 299); +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 302); + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), +// ]); +// } + +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 602); + +// for (let i = 1; i <= 50; i++) { +// block = chain.mineBlock([]); +// } + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 653); + +// block = chain.mineBlock([ +// Tx.contractCall( +// CONTRACT_NAME, +// ASK_TO_JOIN, +// [types.ascii(accounts.get(`wallet_${300}`)!.address)], +// accounts.get(`wallet_${300}`)!.address +// ), +// Tx.contractCall( +// CONTRACT_NAME, +// GET_DATA_WAITING_MINER, +// [types.list([types.principal(accounts.get(`wallet_${300}`)!.address)])], +// deployer.address +// ), +// ]); + +// for (let i = 91; i <= 299; i++) { +// const miner = accounts.get(`wallet_${300}`)!; +// block = chain.mineBlock([ +// Tx.contractCall( +// CONTRACT_NAME, +// VOTE_POSITIVE_JOIN, +// [types.principal(miner.address)], +// accounts.get(`wallet_${i}`)!.address +// ), +// ]); +// } + +// for (let i = 1; i <= 90; i++) { +// const miner = accounts.get(`wallet_${300}`)!; +// block = chain.mineBlock([ +// Tx.contractCall( +// CONTRACT_NAME, +// VOTE_NEGATIVE_JOIN, +// [types.principal(miner.address)], +// accounts.get(`wallet_${i}`)!.address +// ), +// ]); +// } + +// block = chain.mineBlock([ +// Tx.contractCall( +// CONTRACT_NAME, +// GET_DATA_WAITING_MINER, +// [types.list([types.principal(accounts.get(`wallet_${300}`)!.address)])], +// deployer.address +// ), +// ]); +// }, +// }); + +// Clarinet.test({ +// name: 'Get All Data Removals 300 Miners', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// let testing_list = []; +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(miner.address)], miner.address), +// ]); +// if (i == 1) testing_list.push(`${miner.address}`); +// else testing_list.push(` ${miner.address}`); +// } +// const testing_list_length = testing_list.length; +// assertEquals(testing_list_length, 299); +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 302); + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), +// ]); +// } + +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 602); + +// for (let i = 1; i <= 50; i++) { +// block = chain.mineBlock([]); +// } + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 653); + +// block = chain.mineBlock([ +// Tx.contractCall( +// CONTRACT_NAME, +// PROPOSE_REMOVAL, +// [types.principal(accounts.get(`wallet_${299}`)!.address)], +// deployer.address +// ), +// Tx.contractCall( +// CONTRACT_NAME, +// GET_DATA_REMOVAL, +// [types.list([types.principal(accounts.get(`wallet_${299}`)!.address)])], +// deployer.address +// ), +// ]); +// }, +// }); + +// Clarinet.test({ +// name: 'Adding miners to pool, election process', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// const user1 = accounts.get('wallet_1')!; +// const user2 = accounts.get('wallet_2')!; +// const user3 = accounts.get('wallet_3')!; +// const user4 = accounts.get('wallet_4')!; +// const user5 = accounts.get('wallet_5')!; +// const user6 = accounts.get('wallet_6')!; +// const user7 = accounts.get('wallet_7')!; +// const user8 = accounts.get('wallet_8')!; + +// // 1 miner asks to join + +// let block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(deployer.address)], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], user1.address), +// ]); +// block.receipts[0].result.expectOk().expectBool(true); +// assertEquals(block.receipts[1].result, `[${user1.address}]`); +// assertEquals(block.receipts[2].result, `[${deployer.address}]`); +// assertEquals(block.receipts[3].result, `[]`); +// assertEquals(block.receipts[4].result, `${err_no_pending_miners}`); +// assertEquals(block.receipts.length, 5); +// assertEquals(block.height, 2); + +// // deployer votes no, so it is rejected + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_JOIN, [types.principal(user1.address)], user1.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_JOIN, [types.principal(user1.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// ]); +// assertEquals(block.receipts[0].result, `${err_no_vote_permission}`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts[2].result, `[]`); +// assertEquals(block.receipts[3].result, `[${deployer.address}]`); +// assertEquals(block.receipts[4].result, `[]`); +// assertEquals(block.receipts[5].result, `${err_no_pending_miners}`); +// assertEquals(block.receipts.length, 6); +// assertEquals(block.height, 3); + +// // 1 miner asks to join + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(deployer.address)], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], user1.address), +// ]); +// block.receipts[0].result.expectOk().expectBool(true); +// assertEquals(block.receipts[1].result, `[${user1.address}]`); +// assertEquals(block.receipts[2].result, `[${deployer.address}]`); +// assertEquals(block.receipts[3].result, `[]`); +// assertEquals(block.receipts[4].result, `${err_no_pending_miners}`); +// assertEquals(block.receipts.length, 5); +// assertEquals(block.height, 4); + +// // 100 blocks pass + +// for (let i = 1; i <= 96; i++) { +// block = chain.mineBlock([]); +// } + +// // deployer votes yes, so it is pending + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user1.address)], user1.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user1.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// ]); +// assertEquals(block.receipts[0].result, `${err_no_vote_permission}`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts[2].result, `(ok true)`); +// assertEquals(block.receipts[3].result, `[]`); +// assertEquals(block.receipts[4].result, `[${deployer.address}]`); +// assertEquals(block.receipts[5].result, `[${user1.address}]`); +// assertEquals(block.receipts[6].result, `${err_more_blocks_to_pass}`); +// assertEquals(block.receipts[7].result, `[${user1.address}]`); +// assertEquals(block.receipts[8].result, `[${deployer.address}]`); +// assertEquals(block.receipts.length, 9); +// assertEquals(block.height, 101); + +// // deployer tries to vote yes, no vote permission. Add miner to pool (100 blocks passed) + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user1.address)], user1.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user1.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `${err_not_asked_to_join}`); +// assertEquals(block.receipts[1].result, `${err_not_asked_to_join}`); +// assertEquals(block.receipts[2].result, `[]`); +// assertEquals(block.receipts[3].result, `[${deployer.address}]`); +// assertEquals(block.receipts[4].result, `[${user1.address}]`); +// assertEquals(block.receipts[5].result, `(ok true)`); +// assertEquals(block.receipts[6].result, `[]`); +// assertEquals(block.receipts[7].result, `[${deployer.address}, ${user1.address}]`); +// assertEquals(block.receipts.length, 8); +// assertEquals(block.height, 102); + +// // another user asks to join + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user2.address)], user2.address), +// Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], user1.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[${user2.address}]`); +// assertEquals(block.receipts[2].result, `[${deployer.address}, ${user1.address}]`); +// assertEquals(block.receipts[3].result, `[]`); +// assertEquals(block.receipts[4].result, `${err_no_pending_miners}`); +// assertEquals(block.receipts.length, 5); +// assertEquals(block.height, 103); + +// let k = chain.callReadOnlyFn(CONTRACT_NAME, GET_K, [], deployer.address); +// k.result.expectUint(1); // check k==1 + +// // 1 negative vote, 1 negative vote + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_JOIN, [types.principal(user2.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_JOIN, [types.principal(user2.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_JOIN, [types.principal(user2.address)], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[${user2.address}]`); +// assertEquals(block.receipts[2].result, `[${deployer.address}, ${user1.address}]`); +// assertEquals(block.receipts[3].result, `[]`); +// assertEquals(block.receipts[4].result, `${err_no_pending_miners}`); +// assertEquals(block.receipts[5].result, `${err_already_voted}`); // deployer tried to vote again +// assertEquals(block.receipts[6].result, `(ok true)`); // user 1 voted negative +// assertEquals(block.receipts[7].result, `[]`); // no adresses in waiting list, negative election completed +// assertEquals(block.receipts.length, 8); +// assertEquals(block.height, 104); + +// // another user asks to join + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user2.address)], user2.address), +// Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], user1.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[${user2.address}]`); +// assertEquals(block.receipts[2].result, `[${deployer.address}, ${user1.address}]`); +// assertEquals(block.receipts[3].result, `[]`); +// assertEquals(block.receipts[4].result, `${err_no_pending_miners}`); +// assertEquals(block.receipts.length, 5); +// assertEquals(block.height, 105); + +// k = chain.callReadOnlyFn(CONTRACT_NAME, GET_K, [], deployer.address); +// k.result.expectUint(1); // check k==1 +// // 1 positive vote + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user2.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user2.address), +// Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], user1.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts[2].result, `[]`); +// assertEquals(block.receipts[3].result, `[${deployer.address}, ${user1.address}]`); +// assertEquals(block.receipts[4].result, `[${user2.address}]`); // user 2 pending accept, positive election completed +// assertEquals(block.receipts[5].result, `${err_more_blocks_to_pass}`); +// assertEquals(block.receipts.length, 6); +// assertEquals(block.height, 106); + +// for (let i = 1; i <= 96; i++) block = chain.mineBlock([]); + +// // accept pending user in pool + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `[]`); +// assertEquals(block.receipts[1].result, `[${deployer.address}, ${user1.address}]`); +// assertEquals(block.receipts[2].result, `[${user2.address}]`); +// assertEquals(block.receipts[3].result, `(ok true)`); +// assertEquals(block.receipts[4].result, `[]`); +// assertEquals(block.receipts[5].result, `[${deployer.address}, ${user1.address}, ${user2.address}]`); +// assertEquals(block.receipts.length, 6); +// assertEquals(block.height, 203); + +// // two users asks to join + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user3.address)], user3.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user4.address)], user4.address), +// Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], user1.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts[2].result, `[${user3.address}, ${user4.address}]`); +// assertEquals(block.receipts[3].result, `[${deployer.address}, ${user1.address}, ${user2.address}]`); +// assertEquals(block.receipts[4].result, `[]`); +// assertEquals(block.receipts[5].result, `${err_no_pending_miners}`); +// assertEquals(block.receipts.length, 6); +// assertEquals(block.height, 204); + +// k = chain.callReadOnlyFn(CONTRACT_NAME, GET_K, [], deployer.address); +// k.result.expectUint(1); // check k==1 (3 users in pool) + +// // reject 1, accept 1 + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_JOIN, [types.principal(user3.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_JOIN, [types.principal(user3.address)], user1.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_JOIN, [types.principal(user3.address)], user2.address), +// Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user4.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user4.address), +// Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts[3].result, `[${user4.address}]`); +// assertEquals(block.receipts[4].result, `[${deployer.address}, ${user1.address}, ${user2.address}]`); +// assertEquals(block.receipts[5].result, `[]`); +// assertEquals(block.receipts[6].result, `(ok true)`); // deployer vote positive for user +// assertEquals(block.receipts[7].result, `(ok true)`); +// assertEquals(block.receipts[8].result, `[]`); // no adresses in waiting list, negative/positive election completed +// assertEquals(block.receipts[9].result, `[${deployer.address}, ${user1.address}, ${user2.address}]`); // all miners in miners list +// assertEquals(block.receipts[10].result, `[${user4.address}]`); // user 4 in pending list, positive election completed +// assertEquals(block.receipts.length, 11); +// assertEquals(block.height, 205); + +// // two users asks to join + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user3.address)], user3.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user5.address)], user5.address), +// Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], user1.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts[2].result, `[${user3.address}, ${user5.address}]`); +// assertEquals(block.receipts[3].result, `[${deployer.address}, ${user1.address}, ${user2.address}]`); +// assertEquals(block.receipts[4].result, `[${user4.address}]`); +// assertEquals(block.receipts[5].result, `${err_more_blocks_to_pass}`); +// assertEquals(block.receipts.length, 6); +// assertEquals(block.height, 206); + +// k = chain.callReadOnlyFn(CONTRACT_NAME, GET_K, [], deployer.address); +// k.result.expectUint(1); // check k==1 (3 users in pool) + +// // accept both + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user3.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user3.address), +// Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user5.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user5.address), +// Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts[2].result, `[${user5.address}]`); +// assertEquals(block.receipts[3].result, `[${deployer.address}, ${user1.address}, ${user2.address}]`); +// assertEquals(block.receipts[4].result, `[${user4.address}, ${user3.address}]`); +// assertEquals(block.receipts[5].result, `(ok true)`); // deployer vote positive for user +// assertEquals(block.receipts[6].result, `(ok true)`); +// assertEquals(block.receipts[7].result, `[]`); // no adresses in waiting list, negative/positive election completed +// assertEquals(block.receipts[8].result, `[${deployer.address}, ${user1.address}, ${user2.address}]`); // all miners in miners list +// assertEquals(block.receipts[9].result, `[${user4.address}, ${user3.address}, ${user5.address}]`); // user 4 in pending list, positive election completed +// assertEquals(block.receipts.length, 10); +// assertEquals(block.height, 207); + +// k = chain.callReadOnlyFn(CONTRACT_NAME, GET_K, [], deployer.address); +// k.result.expectUint(1); // check k==1 + +// // 100 blocks pass + +// for (let i = 1; i <= 96; i++) block = chain.mineBlock([]); + +// // add pending miners to pool + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[]`); +// assertEquals( +// block.receipts[2].result, +// `[${deployer.address}, ${user1.address}, ${user2.address}, ${user4.address}, ${user3.address}, ${user5.address}]` +// ); // 6 miners in pool +// assertEquals(block.receipts[3].result, `[]`); // no more pending miners + +// k = chain.callReadOnlyFn(CONTRACT_NAME, GET_K, [], deployer.address); +// k.result.expectUint(3); // check k==3 (n=6) +// }, +// }); + +// Clarinet.test({ +// name: '46 Miners ask to join', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// let testing_list = []; +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// for (let i = 1; i <= 46; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(miner.address)], miner.address), +// ]); +// if (i == 1) testing_list.push(`${miner.address}`); +// else testing_list.push(` ${miner.address}`); +// } +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 49); + +// for (let i = 1; i <= 46; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), +// ]); +// } + +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 96); + +// for (let i = 1; i <= 50; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([]); +// } + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 147); +// }, +// }); + +// Clarinet.test({ +// name: '100 Miners ask to join', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// let testing_list = []; +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// for (let i = 1; i <= 100; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(miner.address)], miner.address), +// ]); +// if (i == 1) testing_list.push(`${miner.address}`); +// else testing_list.push(` ${miner.address}`); +// } +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 103); + +// for (let i = 1; i <= 100; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), +// ]); +// } + +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 204); + +// for (let i = 1; i <= 50; i++) { +// block = chain.mineBlock([]); +// } + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 255); +// }, +// }); + +// Clarinet.test({ +// name: '300 Miners ask to join', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// let testing_list = []; +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(miner.address)], miner.address), +// ]); +// if (i == 1) testing_list.push(`${miner.address}`); +// else testing_list.push(` ${miner.address}`); +// } +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 302); + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), +// ]); +// } + +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 602); + +// for (let i = 1; i <= 50; i++) { +// block = chain.mineBlock([]); +// } + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 653); +// }, +// }); + +// Clarinet.test({ +// name: '100 Miners ask to join, vote NEGATIVE', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// let testing_list = []; +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// for (let i = 1; i <= 100; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(miner.address)], miner.address), +// ]); +// if (i == 1) testing_list.push(`${miner.address}`); +// else testing_list.push(` ${miner.address}`); +// } +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 103); + +// for (let i = 1; i <= 100; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_JOIN, [types.principal(miner.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), +// ]); +// } + +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 204); +// }, +// }); + +// Clarinet.test({ +// name: '300 Miners ask to join, vote NEGATIVE', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// let testing_list = []; +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(miner.address)], miner.address), +// ]); +// if (i == 1) testing_list.push(`${miner.address}`); +// else testing_list.push(` ${miner.address}`); +// } +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 302); + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_JOIN, [types.principal(miner.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), +// ]); +// } + +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 602); +// }, +// }); + +// Clarinet.test({ +// name: 'Leave pool 15 users', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// const user1 = accounts.get('wallet_1')!; +// const user2 = accounts.get('wallet_2')!; +// const user3 = accounts.get('wallet_3')!; +// const user4 = accounts.get('wallet_4')!; +// const user5 = accounts.get('wallet_5')!; +// const user6 = accounts.get('wallet_6')!; +// const user7 = accounts.get('wallet_7')!; +// let testing_list = []; +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// for (let i = 1; i <= 14; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(miner.address)], miner.address), +// ]); +// if (i == 1) testing_list.push(`${miner.address}`); +// else testing_list.push(` ${miner.address}`); +// } +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 17); + +// for (let i = 1; i <= 14; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), +// ]); +// } + +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 32); + +// for (let i = 1; i <= 80; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([]); +// } + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_K, [], user3.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); +// block.receipts[2].result.expectUint(9); // n=15, k=(15-1)*0.67=9 +// assertEquals(block.receipts.length, 3); +// assertEquals(block.height, 113); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user2.address), +// Tx.contractCall(CONTRACT_NAME, GET_K, [], user3.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// block.receipts[2].result.expectUint(8); // expecting k to be updated to u8 (9/(13-1)==0.75==k-critical) => k=[13*0.67]=8 +// assertEquals(block.receipts.length, 3); +// assertEquals(block.height, 114); +// }, +// }); + +// Clarinet.test({ +// name: 'Leave pool 20 users', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// const user1 = accounts.get('wallet_1')!; +// const user2 = accounts.get('wallet_2')!; +// const user3 = accounts.get('wallet_3')!; +// const user4 = accounts.get('wallet_4')!; +// const user5 = accounts.get('wallet_5')!; +// const user6 = accounts.get('wallet_6')!; +// const user7 = accounts.get('wallet_7')!; +// let testing_list = []; +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// for (let i = 1; i <= 19; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(miner.address)], miner.address), +// ]); +// if (i == 1) testing_list.push(`${miner.address}`); +// else testing_list.push(` ${miner.address}`); +// } +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 22); + +// for (let i = 1; i <= 19; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), +// ]); +// } + +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 42); + +// for (let i = 1; i <= 80; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([]); +// } + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_K, [], user3.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); +// block.receipts[2].result.expectUint(12); // n=20, k=(20-1)*0.67=12 +// assertEquals(block.receipts.length, 3); +// assertEquals(block.height, 123); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user2.address), +// Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user3.address), +// Tx.contractCall(CONTRACT_NAME, GET_K, [], user3.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts[2].result, `(ok true)`); +// block.receipts[3].result.expectUint(10); // expecting k to be updated to u10 (12/(17-1)==0.75==k-critical) => k=[16*0.67]=10 +// assertEquals(block.receipts.length, 4); +// assertEquals(block.height, 124); +// }, +// }); + +// Clarinet.test({ +// name: 'Leave pool 50 users', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// const user1 = accounts.get('wallet_1')!; +// const user2 = accounts.get('wallet_2')!; +// const user3 = accounts.get('wallet_3')!; +// const user4 = accounts.get('wallet_4')!; +// const user5 = accounts.get('wallet_5')!; +// const user6 = accounts.get('wallet_6')!; +// const user7 = accounts.get('wallet_7')!; +// const user8 = accounts.get('wallet_8')!; +// const user9 = accounts.get('wallet_9')!; +// const user10 = accounts.get('wallet_10')!; +// const user11 = accounts.get('wallet_11')!; +// const user12 = accounts.get('wallet_12')!; +// const user13 = accounts.get('wallet_13')!; +// const user14 = accounts.get('wallet_14')!; +// const user15 = accounts.get('wallet_15')!; +// const user16 = accounts.get('wallet_16')!; +// const user17 = accounts.get('wallet_17')!; +// const user18 = accounts.get('wallet_18')!; +// const user19 = accounts.get('wallet_19')!; +// const user20 = accounts.get('wallet_20')!; +// const user21 = accounts.get('wallet_21')!; +// const user22 = accounts.get('wallet_22')!; +// const user23 = accounts.get('wallet_23')!; +// const user24 = accounts.get('wallet_24')!; +// const user25 = accounts.get('wallet_25')!; +// const user26 = accounts.get('wallet_26')!; +// const user27 = accounts.get('wallet_27')!; +// const user28 = accounts.get('wallet_28')!; +// const user29 = accounts.get('wallet_29')!; +// const user30 = accounts.get('wallet_30')!; +// const user31 = accounts.get('wallet_31')!; +// const user32 = accounts.get('wallet_32')!; +// const user33 = accounts.get('wallet_33')!; +// const user34 = accounts.get('wallet_34')!; +// const user35 = accounts.get('wallet_35')!; +// const user36 = accounts.get('wallet_36')!; +// const user37 = accounts.get('wallet_37')!; +// const user38 = accounts.get('wallet_38')!; +// const user39 = accounts.get('wallet_39')!; +// const user40 = accounts.get('wallet_40')!; +// const user41 = accounts.get('wallet_41')!; +// const user42 = accounts.get('wallet_42')!; +// const user43 = accounts.get('wallet_43')!; +// const user44 = accounts.get('wallet_44')!; +// const user45 = accounts.get('wallet_45')!; +// const user46 = accounts.get('wallet_46')!; +// const user47 = accounts.get('wallet_47')!; +// const user48 = accounts.get('wallet_48')!; +// const user49 = accounts.get('wallet_49')!; +// const user50 = accounts.get('wallet_50')!; +// const user51 = accounts.get('wallet_51')!; +// const user52 = accounts.get('wallet_52')!; +// const user53 = accounts.get('wallet_53')!; +// const user54 = accounts.get('wallet_54')!; +// const user55 = accounts.get('wallet_55')!; +// const user56 = accounts.get('wallet_56')!; +// let testing_list = []; +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// for (let i = 1; i <= 49; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(miner.address)], miner.address), +// ]); +// if (i == 1) testing_list.push(`${miner.address}`); +// else testing_list.push(` ${miner.address}`); +// } +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 52); + +// for (let i = 1; i <= 49; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), +// ]); +// } + +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 102); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_K, [], user3.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); +// // block.receipts[2].result.expectUint(12); // n=20, k=(20-1)*0.67=12 +// assertEquals(block.receipts.length, 3); +// assertEquals(block.height, 103); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user2.address), +// Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user3.address), +// Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user4.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user5.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user6.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user7.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user8.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user9.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user10.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user11.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user12.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user13.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user14.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user15.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user16.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user17.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user18.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user19.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user20.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user21.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user22.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user23.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user24.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user25.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user26.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user27.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user28.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user29.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user30.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user31.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user32.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user33.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user34.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user35.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user36.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user37.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user38.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user39.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user40.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user41.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user42.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user43.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user44.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user45.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user46.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user47.address), +// // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user48.address), +// Tx.contractCall(CONTRACT_NAME, GET_K, [], user3.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts[2].result, `(ok true)`); +// assertEquals(block.receipts[3].result, `(ok true)`); +// block.receipts[4].result.expectUint(32); // expecting k to be updated to u10 (12/(17-1)==0.75==k-critical) => k=[16*0.67]=10 +// assertEquals(block.receipts.length, 5); +// assertEquals(block.height, 104); +// }, +// }); + +// Clarinet.test({ +// name: 'Leave pool 100 users', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; + +// let testing_list = []; +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// for (let i = 1; i <= 100; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(miner.address)], miner.address), +// ]); +// if (i == 1) testing_list.push(`${miner.address}`); +// else testing_list.push(` ${miner.address}`); +// } +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 103); + +// for (let i = 1; i <= 100; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), +// ]); +// } + +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 204); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 205); + +// for (let i = 1; i <= 98; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], miner.address)]); +// } +// assertEquals(block.height, 303); +// }, +// }); + +// Clarinet.test({ +// name: 'Leave pool 300 users', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; + +// let testing_list = []; +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(miner.address)], miner.address), +// ]); +// if (i == 1) testing_list.push(`${miner.address}`); +// else testing_list.push(` ${miner.address}`); +// } +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 302); + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), +// ]); +// } + +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 602); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 603); + +// for (let i = 1; i <= 297; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], miner.address)]); +// } +// assertEquals(block.height, 900); +// }, +// }); + +// Clarinet.test({ +// name: 'Leave pool notifier', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; + +// let block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], deployer.address)]); + +// assertEquals(block.receipts[0].result, `${err_notifier}`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 2); +// }, +// }); + +// Clarinet.test({ +// name: 'Remove case 3 miners in pool', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// const user1 = accounts.get('wallet_1')!; +// const user2 = accounts.get('wallet_2')!; + +// let block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user1.address)], user1.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user2.address)], user2.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 2); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user1.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user2.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user2.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts[2].result, `[]`); +// assertEquals(block.receipts[3].result, `(ok true)`); +// assertEquals(block.receipts[4].result, `[${user1.address}]`); +// assertEquals(block.receipts[5].result, `(ok true)`); +// assertEquals(block.receipts[6].result, `[${user1.address}, ${user2.address}]`); +// assertEquals(block.receipts.length, 7); +// assertEquals(block.height, 3); + +// for (let i = 0; i <= 101; i++) block = chain.mineBlock([]); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[]`); +// assertEquals(block.receipts[2].result, `[${deployer.address}, ${user1.address}, ${user2.address}]`); +// assertEquals(block.receipts.length, 3); +// assertEquals(block.height, 106); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user1.address)], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 107); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts[2].result, `[${deployer.address}, ${user1.address}, ${user2.address}]`); +// assertEquals(block.receipts[3].result, `${err_not_proposed_removal}`); +// assertEquals(block.receipts.length, 4); +// assertEquals(block.height, 108); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user1.address)], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 109); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[${deployer.address}, ${user2.address}]`); +// assertEquals(block.receipts[2].result, `${err_not_proposed_removal}`); +// assertEquals(block.receipts.length, 3); +// assertEquals(block.height, 110); +// }, +// }); + +// Clarinet.test({ +// name: 'Remove case 5 miners in pool', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// const user1 = accounts.get('wallet_1')!; +// const user2 = accounts.get('wallet_2')!; +// const user3 = accounts.get('wallet_3')!; +// const user4 = accounts.get('wallet_4')!; + +// let block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user1.address)], user1.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user2.address)], user2.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user3.address)], user3.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user4.address)], user4.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts[2].result, `(ok true)`); +// assertEquals(block.receipts[3].result, `(ok true)`); +// assertEquals(block.receipts.length, 4); +// assertEquals(block.height, 2); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user1.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user2.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user3.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user4.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user2.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user3.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user4.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts[2].result, `(ok true)`); +// assertEquals(block.receipts[3].result, `(ok true)`); +// assertEquals(block.receipts[4].result, `[]`); +// assertEquals(block.receipts[5].result, `(ok true)`); +// assertEquals(block.receipts[6].result, `(ok true)`); +// assertEquals(block.receipts[7].result, `[${user1.address}, ${user2.address}]`); +// assertEquals(block.receipts[8].result, `(ok true)`); +// assertEquals(block.receipts[9].result, `(ok true)`); +// assertEquals(block.receipts[10].result, `[${user1.address}, ${user2.address}, ${user3.address}, ${user4.address}]`); +// assertEquals(block.receipts.length, 11); +// assertEquals(block.height, 3); + +// for (let i = 0; i <= 101; i++) block = chain.mineBlock([]); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[]`); +// assertEquals( +// block.receipts[2].result, +// `[${deployer.address}, ${user1.address}, ${user2.address}, ${user3.address}, ${user4.address}]` +// ); +// assertEquals(block.receipts.length, 3); +// assertEquals(block.height, 106); + +// // 5 miners in pool NOW +// // propose removal 1 user + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user1.address)], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 107); + +// // reject removal + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user1.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user3.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user4.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals( +// block.receipts[2].result, +// `[${deployer.address}, ${user1.address}, ${user2.address}, ${user3.address}, ${user4.address}]` +// ); +// assertEquals(block.receipts[3].result, `${err_already_voted}`); +// assertEquals(block.receipts[4].result, `${err_cant_vote_himself}`); +// assertEquals(block.receipts[5].result, `(ok true)`); +// assertEquals(block.receipts[6].result, `(ok true)`); +// assertEquals(block.receipts.length, 7); +// assertEquals(block.height, 108); + +// // still 5 users NOW +// // propose removal 1 user + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user1.address)], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 109); + +// // accept removal + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user3.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals( +// block.receipts[2].result, +// `[${deployer.address}, ${user2.address}, ${user3.address}, ${user4.address}]` +// ); +// assertEquals(block.receipts[3].result, `${err_not_proposed_removal}`); +// assertEquals(block.receipts.length, 4); +// assertEquals(block.height, 110); + +// // 4 users NOW +// // propose removal 2 users (same block! so same k for voting) + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user2.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user3.address)], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 111); + +// // accept removal both users + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user2.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user2.address)], user3.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user3.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user3.address)], user2.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts[2].result, `(ok true)`); +// assertEquals(block.receipts[3].result, `(ok true)`); +// assertEquals(block.receipts[4].result, `[${deployer.address}, ${user4.address}]`); +// assertEquals(block.receipts[5].result, `${err_not_proposed_removal}`); +// assertEquals(block.receipts.length, 6); +// assertEquals(block.height, 112); +// }, +// }); + +// Clarinet.test({ +// name: 'Remove case 9 miners in pool', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// const user1 = accounts.get('wallet_1')!; +// const user2 = accounts.get('wallet_2')!; +// const user3 = accounts.get('wallet_3')!; +// const user4 = accounts.get('wallet_4')!; +// const user5 = accounts.get('wallet_5')!; +// const user6 = accounts.get('wallet_6')!; +// const user7 = accounts.get('wallet_7')!; +// const user8 = accounts.get('wallet_8')!; + +// let block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user1.address)], user1.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user2.address)], user2.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user3.address)], user3.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user4.address)], user4.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user5.address)], user5.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user6.address)], user6.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user7.address)], user7.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user8.address)], user8.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts[2].result, `(ok true)`); +// assertEquals(block.receipts[3].result, `(ok true)`); +// assertEquals(block.receipts[4].result, `(ok true)`); +// assertEquals(block.receipts[5].result, `(ok true)`); +// assertEquals(block.receipts[6].result, `(ok true)`); +// assertEquals(block.receipts[7].result, `(ok true)`); +// assertEquals(block.receipts.length, 8); +// assertEquals(block.height, 2); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user1.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user2.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user3.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user4.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user5.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user6.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user7.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user8.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user2.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user3.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user4.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user5.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user6.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user7.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user8.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts[2].result, `(ok true)`); +// assertEquals(block.receipts[3].result, `(ok true)`); +// assertEquals(block.receipts[4].result, `(ok true)`); +// assertEquals(block.receipts[5].result, `(ok true)`); +// assertEquals(block.receipts[6].result, `(ok true)`); +// assertEquals(block.receipts[7].result, `(ok true)`); +// assertEquals(block.receipts[8].result, `[]`); +// assertEquals(block.receipts[9].result, `(ok true)`); +// assertEquals(block.receipts[10].result, `(ok true)`); +// assertEquals(block.receipts[11].result, `(ok true)`); +// assertEquals(block.receipts[12].result, `(ok true)`); +// assertEquals(block.receipts[13].result, `[${user1.address}, ${user2.address}, ${user3.address}, ${user4.address}]`); +// assertEquals(block.receipts[14].result, `(ok true)`); +// assertEquals(block.receipts[15].result, `(ok true)`); +// assertEquals(block.receipts[16].result, `(ok true)`); +// assertEquals(block.receipts[17].result, `(ok true)`); +// assertEquals( +// block.receipts[18].result, +// `[${user1.address}, ${user2.address}, ${user3.address}, ${user4.address}, ${user5.address}, ${user6.address}, ${user7.address}, ${user8.address}]` +// ); +// assertEquals(block.receipts.length, 19); +// assertEquals(block.height, 3); + +// for (let i = 0; i <= 101; i++) block = chain.mineBlock([]); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[]`); +// assertEquals( +// block.receipts[2].result, +// `[${deployer.address}, ${user1.address}, ${user2.address}, ${user3.address}, ${user4.address}, ${user5.address}, ${user6.address}, ${user7.address}, ${user8.address}]` +// ); +// assertEquals(block.receipts.length, 3); +// assertEquals(block.height, 106); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user1.address)], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 107); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user1.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user3.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user4.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user5.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals( +// block.receipts[2].result, +// `[${deployer.address}, ${user1.address}, ${user2.address}, ${user3.address}, ${user4.address}, ${user5.address}, ${user6.address}, ${user7.address}, ${user8.address}]` +// ); +// assertEquals(block.receipts[3].result, `${err_already_voted}`); +// assertEquals(block.receipts[4].result, `${err_cant_vote_himself}`); +// assertEquals(block.receipts[5].result, `(ok true)`); +// assertEquals(block.receipts[6].result, `(ok true)`); +// assertEquals(block.receipts[7].result, `(ok true)`); +// assertEquals(block.receipts.length, 8); +// assertEquals(block.height, 108); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user1.address)], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 109); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user2.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user3.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user4.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user5.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts[2].result, `(ok true)`); +// assertEquals(block.receipts[3].result, `(ok true)`); +// assertEquals(block.receipts[4].result, `(ok true)`); +// assertEquals( +// block.receipts[5].result, +// `[${deployer.address}, ${user2.address}, ${user3.address}, ${user4.address}, ${user5.address}, ${user6.address}, ${user7.address}, ${user8.address}]` +// ); +// assertEquals(block.receipts[6].result, `${err_not_proposed_removal}`); +// assertEquals(block.receipts.length, 7); +// assertEquals(block.height, 110); +// }, +// }); + +// Clarinet.test({ +// name: 'Remove case 20 miners in pool', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// const user1 = accounts.get('wallet_1')!; +// const user2 = accounts.get('wallet_2')!; +// const user3 = accounts.get('wallet_3')!; +// const user4 = accounts.get('wallet_4')!; +// const user5 = accounts.get('wallet_5')!; +// const user6 = accounts.get('wallet_6')!; +// const user7 = accounts.get('wallet_7')!; +// const user8 = accounts.get('wallet_8')!; +// const user9 = accounts.get('wallet_9')!; +// const user10 = accounts.get('wallet_10')!; +// const user11 = accounts.get('wallet_11')!; +// const user12 = accounts.get('wallet_12')!; +// const user13 = accounts.get('wallet_13')!; +// const user14 = accounts.get('wallet_14')!; +// const user15 = accounts.get('wallet_15')!; +// const user16 = accounts.get('wallet_16')!; +// const user17 = accounts.get('wallet_17')!; +// const user18 = accounts.get('wallet_18')!; +// const user19 = accounts.get('wallet_19')!; + +// let block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user1.address)], user1.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user2.address)], user2.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user3.address)], user3.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user4.address)], user4.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user5.address)], user5.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user6.address)], user6.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user7.address)], user7.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user8.address)], user8.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user9.address)], user9.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user10.address)], user10.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user11.address)], user11.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user12.address)], user12.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user13.address)], user13.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user14.address)], user14.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user15.address)], user15.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user16.address)], user16.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user17.address)], user17.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user18.address)], user18.address), +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(user19.address)], user19.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts[2].result, `(ok true)`); +// assertEquals(block.receipts[3].result, `(ok true)`); +// assertEquals(block.receipts[4].result, `(ok true)`); +// assertEquals(block.receipts[5].result, `(ok true)`); +// assertEquals(block.receipts[6].result, `(ok true)`); +// assertEquals(block.receipts[7].result, `(ok true)`); +// assertEquals(block.receipts[8].result, `(ok true)`); +// assertEquals(block.receipts[9].result, `(ok true)`); +// assertEquals(block.receipts[10].result, `(ok true)`); +// assertEquals(block.receipts[11].result, `(ok true)`); +// assertEquals(block.receipts[12].result, `(ok true)`); +// assertEquals(block.receipts[13].result, `(ok true)`); +// assertEquals(block.receipts[14].result, `(ok true)`); +// assertEquals(block.receipts[15].result, `(ok true)`); +// assertEquals(block.receipts[16].result, `(ok true)`); +// assertEquals(block.receipts[17].result, `(ok true)`); +// assertEquals(block.receipts[18].result, `(ok true)`); +// assertEquals(block.receipts.length, 19); +// assertEquals(block.height, 2); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user1.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user2.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user3.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user4.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user5.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user6.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user7.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user8.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user9.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user10.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user11.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user12.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user13.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user14.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user15.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user16.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user17.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user18.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user19.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user1.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user2.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user3.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user4.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user5.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user6.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user7.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user8.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user9.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user10.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user11.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user12.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user13.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user14.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user15.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user16.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user17.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user18.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user19.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// ]); + +// assertEquals(block.receipts[19].result, `[]`); +// assertEquals( +// block.receipts[39].result, +// `[${user1.address}, ${user2.address}, ${user3.address}, ${user4.address}, ${user5.address}, ${user6.address}, ${user7.address}, ${user8.address}, ${user9.address}, ${user10.address}, ${user11.address}, ${user12.address}, ${user13.address}, ${user14.address}, ${user15.address}, ${user16.address}, ${user17.address}, ${user18.address}, ${user19.address}]` +// ); +// assertEquals(block.receipts.length, 40); +// assertEquals(block.height, 3); + +// for (let i = 0; i <= 101; i++) block = chain.mineBlock([]); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[]`); +// assertEquals( +// block.receipts[2].result, +// `[${deployer.address}, ${user1.address}, ${user2.address}, ${user3.address}, ${user4.address}, ${user5.address}, ${user6.address}, ${user7.address}, ${user8.address}, ${user9.address}, ${user10.address}, ${user11.address}, ${user12.address}, ${user13.address}, ${user14.address}, ${user15.address}, ${user16.address}, ${user17.address}, ${user18.address}, ${user19.address}]` +// ); +// assertEquals(block.receipts.length, 3); +// assertEquals(block.height, 106); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user1.address)], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 107); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user1.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user3.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user4.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user5.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user6.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user7.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user8.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user9.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user10.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals( +// block.receipts[2].result, +// `[${deployer.address}, ${user1.address}, ${user2.address}, ${user3.address}, ${user4.address}, ${user5.address}, ${user6.address}, ${user7.address}, ${user8.address}, ${user9.address}, ${user10.address}, ${user11.address}, ${user12.address}, ${user13.address}, ${user14.address}, ${user15.address}, ${user16.address}, ${user17.address}, ${user18.address}, ${user19.address}]` +// ); +// assertEquals(block.receipts[3].result, `${err_already_voted}`); +// assertEquals(block.receipts[4].result, `${err_cant_vote_himself}`); +// assertEquals(block.receipts[5].result, `(ok true)`); +// assertEquals(block.receipts[6].result, `(ok true)`); +// assertEquals(block.receipts[7].result, `(ok true)`); +// assertEquals(block.receipts[8].result, `(ok true)`); +// assertEquals(block.receipts[9].result, `(ok true)`); +// assertEquals(block.receipts[10].result, `(ok true)`); +// assertEquals(block.receipts[11].result, `(ok true)`); +// assertEquals(block.receipts[12].result, `${err_not_proposed_removal}`); +// assertEquals(block.receipts.length, 13); +// assertEquals(block.height, 108); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user1.address)], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 109); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user2.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user3.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user4.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user5.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user6.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user7.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user8.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user9.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user10.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user11.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user12.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user13.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts[2].result, `(ok true)`); +// assertEquals(block.receipts[3].result, `(ok true)`); +// assertEquals(block.receipts[4].result, `(ok true)`); +// assertEquals(block.receipts[5].result, `(ok true)`); +// assertEquals(block.receipts[6].result, `(ok true)`); +// assertEquals(block.receipts[7].result, `(ok true)`); +// assertEquals(block.receipts[8].result, `(ok true)`); +// assertEquals(block.receipts[9].result, `(ok true)`); +// assertEquals(block.receipts[10].result, `(ok true)`); +// assertEquals(block.receipts[11].result, `(ok true)`); +// assertEquals(block.receipts[12].result, `${err_not_proposed_removal}`); +// assertEquals( +// block.receipts[13].result, +// `[${deployer.address}, ${user2.address}, ${user3.address}, ${user4.address}, ${user5.address}, ${user6.address}, ${user7.address}, ${user8.address}, ${user9.address}, ${user10.address}, ${user11.address}, ${user12.address}, ${user13.address}, ${user14.address}, ${user15.address}, ${user16.address}, ${user17.address}, ${user18.address}, ${user19.address}]` +// ); +// assertEquals(block.receipts[14].result, `${err_not_proposed_removal}`); +// assertEquals(block.receipts.length, 15); +// assertEquals(block.height, 110); +// }, +// }); + +// Clarinet.test({ +// name: 'Remove case 300 miners in pool', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// const user1 = accounts.get('wallet_1')!; +// const user2 = accounts.get('wallet_2')!; +// let testing_list = []; +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(miner.address)], miner.address), +// ]); +// if (i == 1) testing_list.push(`${miner.address}`); +// else testing_list.push(` ${miner.address}`); +// } +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 302); + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), +// ]); +// } + +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 602); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 603); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user1.address)], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 604); + +// for (let i = 2; i <= 102; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], miner.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// } +// assertEquals(block.height, 705); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user1.address)], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 706); + +// for (let i = 2; i <= 201; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], miner.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// } +// assertEquals(block.height, 906); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), +// ]); + +// assertEquals(block.receipts[0].result, `[${deployer.address},${testing_list.slice(1)}]`); +// assertEquals(block.receipts[1].result, `${err_not_proposed_removal}`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 907); +// }, +// }); + +// Clarinet.test({ +// name: 'Update notifier 300 miners in pool', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// const user1 = accounts.get('wallet_1')!; +// const user2 = accounts.get('wallet_2')!; +// let testing_list = []; +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(miner.address)], miner.address), +// ]); +// if (i == 1) testing_list.push(`${miner.address}`); +// else testing_list.push(` ${miner.address}`); +// } +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 302); + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), +// ]); +// } + +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 602); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 603); + +// // First case: accept notifier update comparing votes number to k on vote fn in less than 144 blocks + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER_VOTE_STATUS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, START_VOTE_NOTIFIER, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER_VOTE_STATUS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_K, [], deployer.address), +// ]); +// assertEquals(block.receipts[0].result, `false`); +// assertEquals(block.receipts[1].result, `(ok true)`); +// assertEquals(block.receipts[2].result, `true`); +// assertEquals(block.receipts[3].result, `u200`); +// assertEquals(block.receipts.length, 4); +// assertEquals(block.height, 604); + +// for (let i = 2; i <= 101; i++) { +// const miner1 = accounts.get(`wallet_${2 * i}`)!; +// const miner2 = accounts.get(`wallet_${2 * i + 1}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER_VOTE_NUMBER, [types.principal(user1.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NOTIFIER, [types.principal(user1.address)], miner1.address), +// Tx.contractCall(CONTRACT_NAME, VOTE_NOTIFIER, [types.principal(user1.address)], miner2.address), +// ]); +// if (i != 2) assertEquals(block.receipts[0].result, `(some u${2 * i - 4})`); +// else assertEquals(block.receipts[0].result, `none`); + +// assertEquals(block.receipts[1].result, `(ok true)`); + +// if (2 * i + 1 == 203) { +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER_VOTE_NUMBER, [types.principal(user1.address)], deployer.address), +// Tx.contractCall( +// CONTRACT_NAME, +// VOTE_NOTIFIER, +// [types.principal(user1.address)], +// accounts.get(`wallet_${2 * i + 2}`).address +// ), +// ]); + +// assertEquals(block.receipts[0].result, `none`); +// assertEquals(block.receipts[1].result, `${err_no_voting_period}`); +// } else assertEquals(block.receipts[2].result, `(ok true)`); +// } + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER_VOTE_STATUS, [], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `${user1.address}`); +// assertEquals(block.receipts[1].result, `false`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 706); + +// // Second case: accept notifier update comparing votes number to k/2 on vote fn after 144 blocks +// // k=200, k/2=100 + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, START_VOTE_NOTIFIER, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER_VOTE_STATUS, [], deployer.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `true`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 707); + +// for (let i = 2; i <= 147; i++) { +// const miner = accounts.get(`wallet_${i}`)!; + +// // 101 votes first 101 blocks, the rest to 144 blocks empty blocks +// if (i <= 103) { +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_NOTIFIER, [types.principal(deployer.address)], miner.address), +// Tx.contractCall( +// CONTRACT_NAME, +// GET_NOTIFIER_VOTE_NUMBER, +// [types.principal(deployer.address)], +// deployer.address +// ), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(some u${i - 1})`); +// } else { +// block = chain.mineBlock([]); +// } +// } + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, GET_MAX_VOTED_NOTIFIER, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MAX_VOTES_NOTIFIER, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, END_VOTE_NOTIFIER, [], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `${user1.address}`); +// assertEquals(block.receipts[1].result, `u0`); +// assertEquals(block.receipts[2].result, `(ok true)`); +// assertEquals(block.receipts.length, 3); +// assertEquals(block.height, 854); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER_VOTE_STATUS, [], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `${deployer.address}`); +// assertEquals(block.receipts[1].result, `false`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 855); + +// // Third case: Reject notifier update not enough votes (k/2) for at least one notifier after 144 blocks +// // k=200, k/2=100 + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, START_VOTE_NOTIFIER, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER_VOTE_STATUS, [], deployer.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `true`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 856); + +// for (let i = 2; i <= 146; i++) { +// const miner = accounts.get(`wallet_${i}`)!; + +// // 101 votes first 101 blocks, the rest to 144 blocks empty blocks +// if (i <= 50) { +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_NOTIFIER, [types.principal(deployer.address)], miner.address), +// Tx.contractCall( +// CONTRACT_NAME, +// GET_NOTIFIER_VOTE_NUMBER, +// [types.principal(deployer.address)], +// deployer.address +// ), +// ]); + +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `(some u${i - 1})`); +// } else { +// block = chain.mineBlock([]); +// } +// } + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, GET_MAX_VOTED_NOTIFIER, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MAX_VOTES_NOTIFIER, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, END_VOTE_NOTIFIER, [], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `${deployer.address}`); +// assertEquals(block.receipts[1].result, `u0`); +// assertEquals(block.receipts[2].result, `(ok true)`); +// assertEquals(block.receipts.length, 3); +// assertEquals(block.height, 1002); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER_VOTE_STATUS, [], deployer.address), +// ]); + +// assertEquals(block.receipts[0].result, `${deployer.address}`); +// assertEquals(block.receipts[1].result, `false`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 1003); +// }, +// }); + +// // test for 100 users +// // each deposits his index amount +// // check with events emitted + +// Clarinet.test({ +// name: 'Deposit test', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; + +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// for (let i = 1; i <= 100; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, DEPOSIT, [types.uint(CONVERT_TO_STX(i))], miner.address), +// ]); +// block.receipts[0].result.expectOk(); +// //amount: bigint; sender: string; recipient: string; } +// // block.receipts[0].events.ExpectSTXTransferEvent(i, miner.address, CONTRACT_NAME); +// assertEquals(block.receipts[0].events[0].type, 'stx_transfer_event'); +// assertEquals(block.receipts[0].events[0].stx_transfer_event.sender, miner.address); +// assertEquals(block.receipts[0].events[0].stx_transfer_event.recipient, deployer.address + '.' + CONTRACT_NAME); +// assertEquals(block.receipts[0].events[0].stx_transfer_event.amount, `${CONVERT_TO_STX(i)}`); +// } +// }, +// }); + +// // check each has that using read only call +// Clarinet.test({ +// name: 'Deposit successfully + check balances test', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; + +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// // read only call +// for (let i = 1; i <= 100; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// let balance = chain.callReadOnlyFn( +// CONTRACT_NAME, +// GET_BALANCE, +// [types.principal(miner.address)], +// deployer.address +// ); +// balance.result.expectNone(); +// } + +// for (let i = 1; i <= 100; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, DEPOSIT, [types.uint(CONVERT_TO_STX(i))], miner.address), +// ]); + +// block.receipts[0].result.expectOk(); +// block.receipts[0].events.expectSTXTransferEvent( +// CONVERT_TO_STX(i), +// miner.address, +// deployer.address + '.' + CONTRACT_NAME +// ); +// } + +// // read only call +// for (let i = 1; i <= 100; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// let balance = chain.callReadOnlyFn( +// CONTRACT_NAME, +// GET_BALANCE, +// [types.principal(miner.address)], +// deployer.address +// ); +// balance.result.expectSome().expectUint(CONVERT_TO_STX(i)); // check k==1 +// } +// }, +// }); + +// // check no one can withdraw without depositing first and neither can withdraw more than deposited +// Clarinet.test({ +// name: 'Ensure 100 users cannot withdraw without depositing first and neither cannot withdraw more than deposited', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; + +// let testing_list = []; +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// for (let i = 1; i <= 100; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, WITHDRAW, [types.uint(CONVERT_TO_STX(i))], miner.address), +// ]); +// block.receipts[0].result.expectErr(err_insufficient_balance); +// } + +// for (let i = 1; i <= 100; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, DEPOSIT, [types.uint(CONVERT_TO_STX(i))], miner.address), +// ]); + +// block.receipts[0].result.expectOk(); +// block.receipts[0].events.expectSTXTransferEvent( +// CONVERT_TO_STX(i), +// miner.address, +// deployer.address + '.' + CONTRACT_NAME +// ); +// } + +// for (let i = 1; i <= 100; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, WITHDRAW, [types.uint(CONVERT_TO_STX(i) + 1)], miner.address), +// ]); +// block.receipts[0].result.expectErr(err_insufficient_balance); +// } +// }, +// }); + +// // check each can withdraw his amount +// Clarinet.test({ +// name: 'Ensure 100 users can withdraw their amount', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; + +// let testing_list = []; +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// for (let i = 1; i <= 100; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, DEPOSIT, [types.uint(CONVERT_TO_STX(i))], miner.address), +// ]); + +// block.receipts[0].result.expectOk(); +// block.receipts[0].events.expectSTXTransferEvent( +// CONVERT_TO_STX(i), +// miner.address, +// deployer.address + '.' + CONTRACT_NAME +// ); +// } + +// for (let i = 1; i <= 5; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, WITHDRAW, [types.uint(CONVERT_TO_STX(i))], miner.address), +// ]); +// block.receipts[0].result.expectOk().expectBool(true); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, GET_BALANCE, [types.principal(miner.address)], miner.address), +// ]); +// block.receipts[0].result.expectSome().expectUint(0); +// } +// }, +// }); + +// // check each can withdraw his amount +// Clarinet.test({ +// name: 'Ensure 300 users can withdraw their amount', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; + +// let testing_list = []; +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, DEPOSIT, [types.uint(CONVERT_TO_STX(i))], miner.address), +// ]); + +// block.receipts[0].result.expectOk(); +// block.receipts[0].events.expectSTXTransferEvent( +// CONVERT_TO_STX(i), +// miner.address, +// deployer.address + '.' + CONTRACT_NAME +// ); +// } + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, WITHDRAW, [types.uint(CONVERT_TO_STX(i))], miner.address), +// ]); +// block.receipts[0].result.expectOk().expectBool(true); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, GET_BALANCE, [types.principal(miner.address)], miner.address), +// ]); +// block.receipts[0].result.expectSome().expectUint(0); +// } +// }, +// }); + +// Clarinet.test({ +// name: 'Rewards distribution test 100 users', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// const user1 = accounts.get('wallet_1')!; +// let testing_list = []; +// let block; + +// for (let i = 1; i <= 100; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(miner.address)], miner.address), +// ]); +// if (i == 1) testing_list.push(`${miner.address}`); +// else testing_list.push(` ${miner.address}`); +// } +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 102); + +// for (let i = 1; i <= 100; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), +// ]); +// } + +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 203); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 204); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, GET_REWARD_AT_BLOCK_READ, [types.uint(10)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, REWARD_DISTRIBUTION, [types.uint(10)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_BALANCE, [types.principal(deployer.address)], deployer.address), +// ]); +// // console.log(block.receipts[0].result); +// // GOT a reward of (some u5000) = 0.005 STX, only deployer in pool at block 10 +// block.receipts[1].result.expectOk().expectBool(true); +// block.receipts[2].result.expectSome().expectUint(CONVERT_TO_STX(0.005)); + +// // HERE, THE REWARD RESPONSE WAS NONE FOR BLOCK 110, CREATED A SPECIFIC ERROR FOR THIS: (err 137) +// // block = chain.mineBlock([ +// // Tx.contractCall(CONTRACT_NAME, REWARD_DISTRIBUTION, [types.uint(110)], deployer.address), +// // Tx.contractCall(CONTRACT_NAME, GET_BALANCE, [types.principal(user1.address)], deployer.address), +// // ]); + +// // console.log(block.receipts[0].result); +// // block.receipts[0].result.expectOk().expectBool(true); +// // block.receipts[1].result.expectSome().expectUint(CONVERT_TO_STX(9.90099)); //1000 STX / 101 miners = 9.9009900990099009900990099009901 +// }, +// }); + +// Clarinet.test({ +// name: 'Rewards distribution test 300 users', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; +// const user1 = accounts.get('wallet_1')!; +// let testing_list = []; +// let block; + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ASK_TO_JOIN, [types.ascii(miner.address)], miner.address), +// ]); +// if (i == 1) testing_list.push(`${miner.address}`); +// else testing_list.push(` ${miner.address}`); +// } +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 301); + +// for (let i = 1; i <= 299; i++) { +// const miner = accounts.get(`wallet_${i}`)!; +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), +// ]); +// } + +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); +// assertEquals(block.receipts[0].result, `[${testing_list}]`); +// assertEquals(block.receipts.length, 1); +// assertEquals(block.height, 601); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), +// ]); +// assertEquals(block.receipts[0].result, `(ok true)`); +// assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); +// assertEquals(block.receipts.length, 2); +// assertEquals(block.height, 602); + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, REWARD_DISTRIBUTION, [types.uint(10)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_BALANCE, [types.principal(deployer.address)], deployer.address), +// ]); + +// block.receipts[0].result.expectOk().expectBool(true); +// block.receipts[1].result.expectSome().expectUint(CONVERT_TO_STX(0.005)); + +// // some blocks pass to get the reward + +// for (let i = 1; i <= 120; i++) { +// block = chain.mineBlock([]); +// } + +// // distributing rewards + +// block = chain.mineBlock([ +// Tx.contractCall(CONTRACT_NAME, GET_REWARD_AT_BLOCK_READ, [types.uint(10)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, REWARD_DISTRIBUTION, [types.uint(602)], deployer.address), +// Tx.contractCall(CONTRACT_NAME, GET_BALANCE, [types.principal(user1.address)], deployer.address), +// ]); + +// // console.log (block.receipts[0].result) +// // REWARD: (some u5000) => 0.005 STX +// block.receipts[1].result.expectOk().expectBool(true); +// block.receipts[2].result.expectSome().expectUint(CONVERT_TO_STX(0.000016)); //0.005 STX / 300 miners = 0.000016 + +// block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, REWARD_DISTRIBUTION, [types.uint(602)], deployer.address)]); + +// block.receipts[0].result.expectErr().expectUint(1003); +// }, +// }); + +// Clarinet.test({ +// name: 'Check block stacks info', +// async fn(chain: Chain, accounts: Map) { +// const deployer = accounts.get('deployer')!; + +// let block = chain.mineBlock([]); +// assertEquals(block.receipts.length, 0); +// assertEquals(block.height, 2); + +// // read only call +// for (let i = 1; i <= 103; i++) chain.mineBlock([]); + +// let block_info = chain.callReadOnlyFn(CONTRACT_NAME, GET_REWARD_AT_BLOCK_READ, [types.uint(3)], deployer.address); +// // balance.result.expectNone(); +// // } + +// // console.log(block_info); + +// // for (let i = 1; i <= 100; i++) { +// // const miner = accounts.get(`wallet_${i}`)!; +// // block = chain.mineBlock([ +// // Tx.contractCall(CONTRACT_NAME, DEPOSIT, [types.uint(CONVERT_TO_STX(i))], miner.address), +// // ]); + +// // block.receipts[0].result.expectOk(); +// // block.receipts[0].events.expectSTXTransferEvent( +// // CONVERT_TO_STX(i), +// // miner.address, +// // deployer.address + '.' + CONTRACT_NAME +// // ); +// // } + +// // read only call +// // for (let i = 1; i <= 100; i++) { +// // const miner = accounts.get(`wallet_${i}`)!; +// // let balance = chain.callReadOnlyFn( +// // CONTRACT_NAME, +// // GET_BALANCE, +// // [types.principal(miner.address)], +// // deployer.address +// // ); +// // balance.result.expectSome().expectUint(CONVERT_TO_STX(i)); // check k==1 +// // } +// }, +// }); diff --git a/sbtc-ops/smart-contract/tests/mining-pool_test.ts b/sbtc-ops/smart-contract/tests/mining-pool_test.ts new file mode 100644 index 00000000..974e2f15 --- /dev/null +++ b/sbtc-ops/smart-contract/tests/mining-pool_test.ts @@ -0,0 +1,3178 @@ +import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet@v1.5.4/index.ts'; +import { assertEquals } from 'https://deno.land/std@0.170.0/testing/asserts.ts'; +import sha256 from 'https://deno.land/x/sha256js/sha256.mjs'; +import ripemd160 from 'https://deno.land/x/ripemd160js@v2.0.3/ripemd160.mjs'; + +// hash160 = ripemd(sha256(value)) + +const CONVERT_TO_STX = (amount: number) => { + return amount * 1000000; +}; + +const CONTRACT_NAME = 'mining-pool'; +const ASK_TO_JOIN = 'ask-to-join'; +const GET_MINERS_LIST = 'get-miners-list'; +const GET_WAITING_LIST = 'get-waiting-list'; +const GET_PENDING_LIST = `get-pending-accept-list`; +const ADD_PENDING_MINERS = 'add-pending-miners-to-pool'; +const VOTE_POSITIVE_JOIN = 'vote-positive-join-request'; +const VOTE_NEGATIVE_JOIN = 'vote-negative-join-request'; +const VOTE_POSITIVE_REMOVE = 'vote-positive-remove-request'; +const VOTE_NEGATIVE_REMOVE = 'vote-negative-remove-request'; +const START_VOTE_NOTIFIER = 'start-vote-notifier'; +const END_VOTE_NOTIFIER = 'end-vote-notifier'; +const VOTE_NOTIFIER = 'vote-notifier'; +const DEPOSIT = 'deposit-stx'; +const WITHDRAW = 'withdraw-stx'; +const GET_BALANCE = 'get-balance'; +const GET_REWARD_AT_BLOCK_READ = 'get-reward-at-block-read'; +const REWARD_DISTRIBUTION = 'reward-distribution'; +const GET_DATA_WAITING_MINER = 'get-all-data-waiting-miners'; +const GET_DATA_REMOVAL = 'get-all-data-miners-proposed-for-removal'; +const err_insufficient_balance = '(err u1001)'; +const err_missing_balance = '(err u1002)'; +const LEAVE_POOL = 'leave-pool'; +const TRY_ENTER_POOL = 'try-enter-pool'; +const PROPOSE_REMOVAL = 'propose-removal'; +const PROPOSE_NOTIFIER = 'propose-notifier'; +const GET_PROPOSED_NOTIFIERS_LIST = 'get-proposed-notifiers-list'; +const GET_K = 'get-k'; +const GET_NOTIFIER = 'get-notifier'; +const GET_NOTIFIER_VOTE_STATUS = 'get-notifier-vote-status'; +const GET_NOTIFIER_VOTE_NUMBER = 'get-notifier-vote-number'; +const GET_MAX_VOTES_NOTIFIER = 'get-max-votes-notifier '; +const GET_MAX_VOTED_NOTIFIER = 'get-max-voted-notifier '; +const err_no_vote_permission = '(err u105)'; +const err_more_blocks_to_pass = '(err u106)'; +const err_no_pending_miners = '(err u107)'; +const err_already_voted = '(err u108)'; +const err_not_asked_to_join = '(err u109)'; +const err_notifier = '(err u113)'; +const err_not_proposed_removal = '(err u117)'; +const err_cant_vote_himself = '(err u119)'; +const err_already_proposed_for_notifier = '(err u121)'; +const err_not_proposed_notifier = '(err u124)'; +const err_already_notifier = '(err u125)'; +const err_no_voting_period = '(err u129)'; + +// Conversions for hashing +const publicKeyHex = '02e8f7dc91e49a577ce9ea8989c7184aea8886fe5250f02120dc6f98e3619679b0'; + +const buffer_from = (num: string) => { + const hexString = num; + const byteArray = new Uint8Array(hexString.length / 2); + + for (let i = 0; i < byteArray.length; i++) { + byteArray[i] = parseInt(hexString.substr(i * 2, 2), 16); + } + return byteArray; +}; + +const hash160 = (value: any) => { + return sha256(value).then((x: any) => ripemd160(x)); +}; + +Clarinet.test({ + name: 'Get All Data Waiting 300 Miners', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + let testing_list = []; + let block = chain.mineBlock([]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + for (let i = 1; i <= 299; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + miner.address + ), + ]); + if (i == 1) testing_list.push(`${miner.address}`); + else testing_list.push(` ${miner.address}`); + } + const testing_list_length = testing_list.length; + assertEquals(testing_list_length, 299); + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 302); + + for (let i = 1; i <= 299; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), + ]); + } + + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 602); + + for (let i = 1; i <= 50; i++) { + block = chain.mineBlock([]); + } + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); + assertEquals(block.receipts.length, 2); + assertEquals(block.height, 653); + + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + accounts.get(`wallet_${300}`)!.address + ), + Tx.contractCall( + CONTRACT_NAME, + GET_DATA_WAITING_MINER, + [types.list([types.principal(accounts.get(`wallet_${300}`)!.address)])], + deployer.address + ), + ]); + + for (let i = 91; i <= 299; i++) { + const miner = accounts.get(`wallet_${300}`)!; + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + VOTE_POSITIVE_JOIN, + [types.principal(miner.address)], + accounts.get(`wallet_${i}`)!.address + ), + ]); + } + + for (let i = 1; i <= 90; i++) { + const miner = accounts.get(`wallet_${300}`)!; + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + VOTE_NEGATIVE_JOIN, + [types.principal(miner.address)], + accounts.get(`wallet_${i}`)!.address + ), + ]); + } + + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + GET_DATA_WAITING_MINER, + [types.list([types.principal(accounts.get(`wallet_${300}`)!.address)])], + deployer.address + ), + ]); + }, +}); + +Clarinet.test({ + name: 'Get All Data Removals 300 Miners', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + let testing_list = []; + let block = chain.mineBlock([]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + for (let i = 1; i <= 299; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + miner.address + ), + ]); + if (i == 1) testing_list.push(`${miner.address}`); + else testing_list.push(` ${miner.address}`); + } + const testing_list_length = testing_list.length; + assertEquals(testing_list_length, 299); + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 302); + + for (let i = 1; i <= 299; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), + ]); + } + + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 602); + + for (let i = 1; i <= 50; i++) { + block = chain.mineBlock([]); + } + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); + assertEquals(block.receipts.length, 2); + assertEquals(block.height, 653); + + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + PROPOSE_REMOVAL, + [types.principal(accounts.get(`wallet_${299}`)!.address)], + deployer.address + ), + Tx.contractCall( + CONTRACT_NAME, + GET_DATA_REMOVAL, + [types.list([types.principal(accounts.get(`wallet_${299}`)!.address)])], + deployer.address + ), + ]); + }, +}); + +Clarinet.test({ + name: 'Adding miners to pool, election process', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + const user1 = accounts.get('wallet_1')!; + const user2 = accounts.get('wallet_2')!; + const user3 = accounts.get('wallet_3')!; + const user4 = accounts.get('wallet_4')!; + const user5 = accounts.get('wallet_5')!; + const user6 = accounts.get('wallet_6')!; + const user7 = accounts.get('wallet_7')!; + const user8 = accounts.get('wallet_8')!; + + // 1 miner asks to join + + let block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user1.address + ), + Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], user1.address), + ]); + block.receipts[0].result.expectOk().expectBool(true); + assertEquals(block.receipts[1].result, `[${user1.address}]`); + assertEquals(block.receipts[2].result, `[${deployer.address}]`); + assertEquals(block.receipts[3].result, `[]`); + assertEquals(block.receipts[4].result, `${err_no_pending_miners}`); + assertEquals(block.receipts.length, 5); + assertEquals(block.height, 2); + + // deployer votes no, so it is rejected + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_JOIN, [types.principal(user1.address)], user1.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_JOIN, [types.principal(user1.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + ]); + assertEquals(block.receipts[0].result, `${err_no_vote_permission}`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts[2].result, `[]`); + assertEquals(block.receipts[3].result, `[${deployer.address}]`); + assertEquals(block.receipts[4].result, `[]`); + assertEquals(block.receipts[5].result, `${err_no_pending_miners}`); + assertEquals(block.receipts.length, 6); + assertEquals(block.height, 3); + + // 1 miner asks to join + + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user1.address + ), + Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], user1.address), + ]); + block.receipts[0].result.expectOk().expectBool(true); + assertEquals(block.receipts[1].result, `[${user1.address}]`); + assertEquals(block.receipts[2].result, `[${deployer.address}]`); + assertEquals(block.receipts[3].result, `[]`); + assertEquals(block.receipts[4].result, `${err_no_pending_miners}`); + assertEquals(block.receipts.length, 5); + assertEquals(block.height, 4); + + // 100 blocks pass + + for (let i = 1; i <= 96; i++) { + block = chain.mineBlock([]); + } + + // deployer votes yes, so it is pending + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user1.address)], user1.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user1.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + ]); + assertEquals(block.receipts[0].result, `${err_no_vote_permission}`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts[2].result, `(ok true)`); + assertEquals(block.receipts[3].result, `[]`); + assertEquals(block.receipts[4].result, `[${deployer.address}]`); + assertEquals(block.receipts[5].result, `[${user1.address}]`); + assertEquals(block.receipts[6].result, `${err_more_blocks_to_pass}`); + assertEquals(block.receipts[7].result, `[${user1.address}]`); + assertEquals(block.receipts[8].result, `[${deployer.address}]`); + assertEquals(block.receipts.length, 9); + assertEquals(block.height, 101); + + // deployer tries to vote yes, no vote permission. Add miner to pool (100 blocks passed) + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user1.address)], user1.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user1.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `${err_not_asked_to_join}`); + assertEquals(block.receipts[1].result, `${err_not_asked_to_join}`); + assertEquals(block.receipts[2].result, `[]`); + assertEquals(block.receipts[3].result, `[${deployer.address}]`); + assertEquals(block.receipts[4].result, `[${user1.address}]`); + assertEquals(block.receipts[5].result, `(ok true)`); + assertEquals(block.receipts[6].result, `[]`); + assertEquals(block.receipts[7].result, `[${deployer.address}, ${user1.address}]`); + assertEquals(block.receipts.length, 8); + assertEquals(block.height, 102); + + // another user asks to join + + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user2.address + ), + Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], user1.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[${user2.address}]`); + assertEquals(block.receipts[2].result, `[${deployer.address}, ${user1.address}]`); + assertEquals(block.receipts[3].result, `[]`); + assertEquals(block.receipts[4].result, `${err_no_pending_miners}`); + assertEquals(block.receipts.length, 5); + assertEquals(block.height, 103); + + let k = chain.callReadOnlyFn(CONTRACT_NAME, GET_K, [], deployer.address); + k.result.expectUint(1); // check k==1 + + // 1 negative vote, 1 negative vote + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_JOIN, [types.principal(user2.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], user1.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_JOIN, [types.principal(user2.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_JOIN, [types.principal(user2.address)], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[${user2.address}]`); + assertEquals(block.receipts[2].result, `[${deployer.address}, ${user1.address}]`); + assertEquals(block.receipts[3].result, `[]`); + assertEquals(block.receipts[4].result, `${err_no_pending_miners}`); + assertEquals(block.receipts[5].result, `${err_already_voted}`); // deployer tried to vote again + assertEquals(block.receipts[6].result, `(ok true)`); // user 1 voted negative + assertEquals(block.receipts[7].result, `[]`); // no adresses in waiting list, negative election completed + assertEquals(block.receipts.length, 8); + assertEquals(block.height, 104); + + // another user asks to join + + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user2.address + ), + Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], user1.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[${user2.address}]`); + assertEquals(block.receipts[2].result, `[${deployer.address}, ${user1.address}]`); + assertEquals(block.receipts[3].result, `[]`); + assertEquals(block.receipts[4].result, `${err_no_pending_miners}`); + assertEquals(block.receipts.length, 5); + assertEquals(block.height, 105); + + k = chain.callReadOnlyFn(CONTRACT_NAME, GET_K, [], deployer.address); + k.result.expectUint(1); // check k==1 + // 1 positive vote + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user2.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user2.address), + Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], user1.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts[2].result, `[]`); + assertEquals(block.receipts[3].result, `[${deployer.address}, ${user1.address}]`); + assertEquals(block.receipts[4].result, `[${user2.address}]`); // user 2 pending accept, positive election completed + assertEquals(block.receipts[5].result, `${err_more_blocks_to_pass}`); + assertEquals(block.receipts.length, 6); + assertEquals(block.height, 106); + + for (let i = 1; i <= 96; i++) block = chain.mineBlock([]); + + // accept pending user in pool + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `[]`); + assertEquals(block.receipts[1].result, `[${deployer.address}, ${user1.address}]`); + assertEquals(block.receipts[2].result, `[${user2.address}]`); + assertEquals(block.receipts[3].result, `(ok true)`); + assertEquals(block.receipts[4].result, `[]`); + assertEquals(block.receipts[5].result, `[${deployer.address}, ${user1.address}, ${user2.address}]`); + assertEquals(block.receipts.length, 6); + assertEquals(block.height, 203); + + // two users asks to join + + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user3.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user4.address + ), + Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], user1.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts[2].result, `[${user3.address}, ${user4.address}]`); + assertEquals(block.receipts[3].result, `[${deployer.address}, ${user1.address}, ${user2.address}]`); + assertEquals(block.receipts[4].result, `[]`); + assertEquals(block.receipts[5].result, `${err_no_pending_miners}`); + assertEquals(block.receipts.length, 6); + assertEquals(block.height, 204); + + k = chain.callReadOnlyFn(CONTRACT_NAME, GET_K, [], deployer.address); + k.result.expectUint(1); // check k==1 (3 users in pool) + + // reject 1, accept 1 + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_JOIN, [types.principal(user3.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_JOIN, [types.principal(user3.address)], user1.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_JOIN, [types.principal(user3.address)], user2.address), + Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user4.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user4.address), + Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts[3].result, `[${user4.address}]`); + assertEquals(block.receipts[4].result, `[${deployer.address}, ${user1.address}, ${user2.address}]`); + assertEquals(block.receipts[5].result, `[]`); + assertEquals(block.receipts[6].result, `(ok true)`); // deployer vote positive for user + assertEquals(block.receipts[7].result, `(ok true)`); + assertEquals(block.receipts[8].result, `[]`); // no adresses in waiting list, negative/positive election completed + assertEquals(block.receipts[9].result, `[${deployer.address}, ${user1.address}, ${user2.address}]`); // all miners in miners list + assertEquals(block.receipts[10].result, `[${user4.address}]`); // user 4 in pending list, positive election completed + assertEquals(block.receipts.length, 11); + assertEquals(block.height, 205); + + // two users asks to join + + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user3.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user5.address + ), + Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], user1.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts[2].result, `[${user3.address}, ${user5.address}]`); + assertEquals(block.receipts[3].result, `[${deployer.address}, ${user1.address}, ${user2.address}]`); + assertEquals(block.receipts[4].result, `[${user4.address}]`); + assertEquals(block.receipts[5].result, `${err_more_blocks_to_pass}`); + assertEquals(block.receipts.length, 6); + assertEquals(block.height, 206); + + k = chain.callReadOnlyFn(CONTRACT_NAME, GET_K, [], deployer.address); + k.result.expectUint(1); // check k==1 (3 users in pool) + + // accept both + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user3.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user3.address), + Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user5.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user5.address), + Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts[2].result, `[${user5.address}]`); + assertEquals(block.receipts[3].result, `[${deployer.address}, ${user1.address}, ${user2.address}]`); + assertEquals(block.receipts[4].result, `[${user4.address}, ${user3.address}]`); + assertEquals(block.receipts[5].result, `(ok true)`); // deployer vote positive for user + assertEquals(block.receipts[6].result, `(ok true)`); + assertEquals(block.receipts[7].result, `[]`); // no adresses in waiting list, negative/positive election completed + assertEquals(block.receipts[8].result, `[${deployer.address}, ${user1.address}, ${user2.address}]`); // all miners in miners list + assertEquals(block.receipts[9].result, `[${user4.address}, ${user3.address}, ${user5.address}]`); // user 4 in pending list, positive election completed + assertEquals(block.receipts.length, 10); + assertEquals(block.height, 207); + + k = chain.callReadOnlyFn(CONTRACT_NAME, GET_K, [], deployer.address); + k.result.expectUint(1); // check k==1 + + // 100 blocks pass + + for (let i = 1; i <= 96; i++) block = chain.mineBlock([]); + + // add pending miners to pool + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], user1.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[]`); + assertEquals( + block.receipts[2].result, + `[${deployer.address}, ${user1.address}, ${user2.address}, ${user4.address}, ${user3.address}, ${user5.address}]` + ); // 6 miners in pool + assertEquals(block.receipts[3].result, `[]`); // no more pending miners + + k = chain.callReadOnlyFn(CONTRACT_NAME, GET_K, [], deployer.address); + k.result.expectUint(3); // check k==3 (n=6) + }, +}); + +Clarinet.test({ + name: '46 Miners ask to join', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + let testing_list = []; + let block = chain.mineBlock([]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + for (let i = 1; i <= 46; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + miner.address + ), + ]); + if (i == 1) testing_list.push(`${miner.address}`); + else testing_list.push(` ${miner.address}`); + } + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 49); + + for (let i = 1; i <= 46; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), + ]); + } + + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 96); + + for (let i = 1; i <= 50; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([]); + } + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); + assertEquals(block.receipts.length, 2); + assertEquals(block.height, 147); + }, +}); + +Clarinet.test({ + name: '100 Miners ask to join', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + let testing_list = []; + let block = chain.mineBlock([]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + for (let i = 1; i <= 100; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + miner.address + ), + ]); + if (i == 1) testing_list.push(`${miner.address}`); + else testing_list.push(` ${miner.address}`); + } + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 103); + + for (let i = 1; i <= 100; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), + ]); + } + + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 204); + + for (let i = 1; i <= 50; i++) { + block = chain.mineBlock([]); + } + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); + assertEquals(block.receipts.length, 2); + assertEquals(block.height, 255); + }, +}); + +Clarinet.test({ + name: '300 Miners ask to join', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + let testing_list = []; + let block = chain.mineBlock([]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + for (let i = 1; i <= 299; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + miner.address + ), + ]); + if (i == 1) testing_list.push(`${miner.address}`); + else testing_list.push(` ${miner.address}`); + } + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 302); + + for (let i = 1; i <= 299; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), + ]); + } + + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 602); + + for (let i = 1; i <= 50; i++) { + block = chain.mineBlock([]); + } + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); + assertEquals(block.receipts.length, 2); + assertEquals(block.height, 653); + }, +}); + +Clarinet.test({ + name: '100 Miners ask to join, vote NEGATIVE', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + let testing_list = []; + let block = chain.mineBlock([]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + for (let i = 1; i <= 100; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + miner.address + ), + ]); + if (i == 1) testing_list.push(`${miner.address}`); + else testing_list.push(` ${miner.address}`); + } + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 103); + + for (let i = 1; i <= 100; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_JOIN, [types.principal(miner.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), + ]); + } + + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 204); + }, +}); + +Clarinet.test({ + name: '300 Miners ask to join, vote NEGATIVE', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + let testing_list = []; + let block = chain.mineBlock([]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + for (let i = 1; i <= 299; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + miner.address + ), + ]); + if (i == 1) testing_list.push(`${miner.address}`); + else testing_list.push(` ${miner.address}`); + } + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 302); + + for (let i = 1; i <= 299; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_JOIN, [types.principal(miner.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), + ]); + } + + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 602); + }, +}); + +Clarinet.test({ + name: 'Leave pool 15 users', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + const user1 = accounts.get('wallet_1')!; + const user2 = accounts.get('wallet_2')!; + const user3 = accounts.get('wallet_3')!; + const user4 = accounts.get('wallet_4')!; + const user5 = accounts.get('wallet_5')!; + const user6 = accounts.get('wallet_6')!; + const user7 = accounts.get('wallet_7')!; + let testing_list = []; + let block = chain.mineBlock([]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + for (let i = 1; i <= 14; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + miner.address + ), + ]); + if (i == 1) testing_list.push(`${miner.address}`); + else testing_list.push(` ${miner.address}`); + } + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 17); + + for (let i = 1; i <= 14; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), + ]); + } + + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 32); + + for (let i = 1; i <= 80; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([]); + } + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_K, [], user3.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); + block.receipts[2].result.expectUint(9); // n=15, k=(15-1)*0.67=9 + assertEquals(block.receipts.length, 3); + assertEquals(block.height, 113); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user1.address), + Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user2.address), + Tx.contractCall(CONTRACT_NAME, GET_K, [], user3.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + block.receipts[2].result.expectUint(8); // expecting k to be updated to u8 (9/(13-1)==0.75==k-critical) => k=[13*0.67]=8 + assertEquals(block.receipts.length, 3); + assertEquals(block.height, 114); + }, +}); + +Clarinet.test({ + name: 'Leave pool 20 users', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + const user1 = accounts.get('wallet_1')!; + const user2 = accounts.get('wallet_2')!; + const user3 = accounts.get('wallet_3')!; + const user4 = accounts.get('wallet_4')!; + const user5 = accounts.get('wallet_5')!; + const user6 = accounts.get('wallet_6')!; + const user7 = accounts.get('wallet_7')!; + let testing_list = []; + let block = chain.mineBlock([]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + for (let i = 1; i <= 19; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + miner.address + ), + ]); + if (i == 1) testing_list.push(`${miner.address}`); + else testing_list.push(` ${miner.address}`); + } + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 22); + + for (let i = 1; i <= 19; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), + ]); + } + + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 42); + + for (let i = 1; i <= 80; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([]); + } + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_K, [], user3.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); + block.receipts[2].result.expectUint(12); // n=20, k=(20-1)*0.67=12 + assertEquals(block.receipts.length, 3); + assertEquals(block.height, 123); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user1.address), + Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user2.address), + Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user3.address), + Tx.contractCall(CONTRACT_NAME, GET_K, [], user3.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts[2].result, `(ok true)`); + block.receipts[3].result.expectUint(10); // expecting k to be updated to u10 (12/(17-1)==0.75==k-critical) => k=[16*0.67]=10 + assertEquals(block.receipts.length, 4); + assertEquals(block.height, 124); + }, +}); + +Clarinet.test({ + name: 'Leave pool 50 users', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + const user1 = accounts.get('wallet_1')!; + const user2 = accounts.get('wallet_2')!; + const user3 = accounts.get('wallet_3')!; + const user4 = accounts.get('wallet_4')!; + const user5 = accounts.get('wallet_5')!; + const user6 = accounts.get('wallet_6')!; + const user7 = accounts.get('wallet_7')!; + const user8 = accounts.get('wallet_8')!; + const user9 = accounts.get('wallet_9')!; + const user10 = accounts.get('wallet_10')!; + const user11 = accounts.get('wallet_11')!; + const user12 = accounts.get('wallet_12')!; + const user13 = accounts.get('wallet_13')!; + const user14 = accounts.get('wallet_14')!; + const user15 = accounts.get('wallet_15')!; + const user16 = accounts.get('wallet_16')!; + const user17 = accounts.get('wallet_17')!; + const user18 = accounts.get('wallet_18')!; + const user19 = accounts.get('wallet_19')!; + const user20 = accounts.get('wallet_20')!; + const user21 = accounts.get('wallet_21')!; + const user22 = accounts.get('wallet_22')!; + const user23 = accounts.get('wallet_23')!; + const user24 = accounts.get('wallet_24')!; + const user25 = accounts.get('wallet_25')!; + const user26 = accounts.get('wallet_26')!; + const user27 = accounts.get('wallet_27')!; + const user28 = accounts.get('wallet_28')!; + const user29 = accounts.get('wallet_29')!; + const user30 = accounts.get('wallet_30')!; + const user31 = accounts.get('wallet_31')!; + const user32 = accounts.get('wallet_32')!; + const user33 = accounts.get('wallet_33')!; + const user34 = accounts.get('wallet_34')!; + const user35 = accounts.get('wallet_35')!; + const user36 = accounts.get('wallet_36')!; + const user37 = accounts.get('wallet_37')!; + const user38 = accounts.get('wallet_38')!; + const user39 = accounts.get('wallet_39')!; + const user40 = accounts.get('wallet_40')!; + const user41 = accounts.get('wallet_41')!; + const user42 = accounts.get('wallet_42')!; + const user43 = accounts.get('wallet_43')!; + const user44 = accounts.get('wallet_44')!; + const user45 = accounts.get('wallet_45')!; + const user46 = accounts.get('wallet_46')!; + const user47 = accounts.get('wallet_47')!; + const user48 = accounts.get('wallet_48')!; + const user49 = accounts.get('wallet_49')!; + const user50 = accounts.get('wallet_50')!; + const user51 = accounts.get('wallet_51')!; + const user52 = accounts.get('wallet_52')!; + const user53 = accounts.get('wallet_53')!; + const user54 = accounts.get('wallet_54')!; + const user55 = accounts.get('wallet_55')!; + const user56 = accounts.get('wallet_56')!; + let testing_list = []; + let block = chain.mineBlock([]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + for (let i = 1; i <= 49; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + miner.address + ), + ]); + if (i == 1) testing_list.push(`${miner.address}`); + else testing_list.push(` ${miner.address}`); + } + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 52); + + for (let i = 1; i <= 49; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), + ]); + } + + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 102); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_K, [], user3.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); + // block.receipts[2].result.expectUint(12); // n=20, k=(20-1)*0.67=12 + assertEquals(block.receipts.length, 3); + assertEquals(block.height, 103); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user1.address), + Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user2.address), + Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user3.address), + Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user4.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user5.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user6.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user7.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user8.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user9.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user10.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user11.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user12.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user13.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user14.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user15.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user16.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user17.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user18.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user19.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user20.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user21.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user22.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user23.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user24.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user25.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user26.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user27.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user28.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user29.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user30.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user31.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user32.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user33.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user34.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user35.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user36.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user37.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user38.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user39.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user40.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user41.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user42.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user43.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user44.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user45.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user46.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user47.address), + // Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], user48.address), + Tx.contractCall(CONTRACT_NAME, GET_K, [], user3.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts[2].result, `(ok true)`); + assertEquals(block.receipts[3].result, `(ok true)`); + block.receipts[4].result.expectUint(32); // expecting k to be updated to u10 (12/(17-1)==0.75==k-critical) => k=[16*0.67]=10 + assertEquals(block.receipts.length, 5); + assertEquals(block.height, 104); + }, +}); + +Clarinet.test({ + name: 'Leave pool 100 users', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + + let testing_list = []; + let block = chain.mineBlock([]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + for (let i = 1; i <= 100; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + miner.address + ), + ]); + if (i == 1) testing_list.push(`${miner.address}`); + else testing_list.push(` ${miner.address}`); + } + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 103); + + for (let i = 1; i <= 100; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), + ]); + } + + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 204); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); + assertEquals(block.receipts.length, 2); + assertEquals(block.height, 205); + + for (let i = 1; i <= 98; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], miner.address)]); + } + assertEquals(block.height, 303); + }, +}); + +Clarinet.test({ + name: 'Leave pool 300 users', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + + let testing_list = []; + let block = chain.mineBlock([]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + for (let i = 1; i <= 299; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + miner.address + ), + ]); + if (i == 1) testing_list.push(`${miner.address}`); + else testing_list.push(` ${miner.address}`); + } + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 302); + + for (let i = 1; i <= 299; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), + ]); + } + + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 602); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); + assertEquals(block.receipts.length, 2); + assertEquals(block.height, 603); + + for (let i = 1; i <= 297; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], miner.address)]); + } + assertEquals(block.height, 900); + }, +}); + +Clarinet.test({ + name: 'Leave pool notifier', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + + let block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, LEAVE_POOL, [], deployer.address)]); + + assertEquals(block.receipts[0].result, `${err_notifier}`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 2); + }, +}); + +Clarinet.test({ + name: 'Remove case 3 miners in pool', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + const user1 = accounts.get('wallet_1')!; + const user2 = accounts.get('wallet_2')!; + + let block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user1.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user2.address + ), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts.length, 2); + assertEquals(block.height, 2); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user1.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user2.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user1.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user2.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts[2].result, `[]`); + assertEquals(block.receipts[3].result, `(ok true)`); + assertEquals(block.receipts[4].result, `[${user1.address}]`); + assertEquals(block.receipts[5].result, `(ok true)`); + assertEquals(block.receipts[6].result, `[${user1.address}, ${user2.address}]`); + assertEquals(block.receipts.length, 7); + assertEquals(block.height, 3); + + for (let i = 0; i <= 101; i++) block = chain.mineBlock([]); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[]`); + assertEquals(block.receipts[2].result, `[${deployer.address}, ${user1.address}, ${user2.address}]`); + assertEquals(block.receipts.length, 3); + assertEquals(block.height, 106); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user1.address)], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 107); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts[2].result, `[${deployer.address}, ${user1.address}, ${user2.address}]`); + assertEquals(block.receipts[3].result, `${err_not_proposed_removal}`); + assertEquals(block.receipts.length, 4); + assertEquals(block.height, 108); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user1.address)], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 109); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[${deployer.address}, ${user2.address}]`); + assertEquals(block.receipts[2].result, `${err_not_proposed_removal}`); + assertEquals(block.receipts.length, 3); + assertEquals(block.height, 110); + }, +}); + +Clarinet.test({ + name: 'Remove case 5 miners in pool', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + const user1 = accounts.get('wallet_1')!; + const user2 = accounts.get('wallet_2')!; + const user3 = accounts.get('wallet_3')!; + const user4 = accounts.get('wallet_4')!; + + let block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user1.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user2.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user3.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user4.address + ), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts[2].result, `(ok true)`); + assertEquals(block.receipts[3].result, `(ok true)`); + assertEquals(block.receipts.length, 4); + assertEquals(block.height, 2); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user1.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user2.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user3.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user4.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user1.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user2.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user3.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user4.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts[2].result, `(ok true)`); + assertEquals(block.receipts[3].result, `(ok true)`); + assertEquals(block.receipts[4].result, `[]`); + assertEquals(block.receipts[5].result, `(ok true)`); + assertEquals(block.receipts[6].result, `(ok true)`); + assertEquals(block.receipts[7].result, `[${user1.address}, ${user2.address}]`); + assertEquals(block.receipts[8].result, `(ok true)`); + assertEquals(block.receipts[9].result, `(ok true)`); + assertEquals(block.receipts[10].result, `[${user1.address}, ${user2.address}, ${user3.address}, ${user4.address}]`); + assertEquals(block.receipts.length, 11); + assertEquals(block.height, 3); + + for (let i = 0; i <= 101; i++) block = chain.mineBlock([]); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[]`); + assertEquals( + block.receipts[2].result, + `[${deployer.address}, ${user1.address}, ${user2.address}, ${user3.address}, ${user4.address}]` + ); + assertEquals(block.receipts.length, 3); + assertEquals(block.height, 106); + + // 5 miners in pool NOW + // propose removal 1 user + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user1.address)], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 107); + + // reject removal + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user1.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user3.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user4.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals( + block.receipts[2].result, + `[${deployer.address}, ${user1.address}, ${user2.address}, ${user3.address}, ${user4.address}]` + ); + assertEquals(block.receipts[3].result, `${err_already_voted}`); + assertEquals(block.receipts[4].result, `${err_cant_vote_himself}`); + assertEquals(block.receipts[5].result, `(ok true)`); + assertEquals(block.receipts[6].result, `(ok true)`); + assertEquals(block.receipts.length, 7); + assertEquals(block.height, 108); + + // still 5 users NOW + // propose removal 1 user + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user1.address)], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 109); + + // accept removal + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user3.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals( + block.receipts[2].result, + `[${deployer.address}, ${user2.address}, ${user3.address}, ${user4.address}]` + ); + assertEquals(block.receipts[3].result, `${err_not_proposed_removal}`); + assertEquals(block.receipts.length, 4); + assertEquals(block.height, 110); + + // 4 users NOW + // propose removal 2 users (same block! so same k for voting) + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user2.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user3.address)], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts.length, 2); + assertEquals(block.height, 111); + + // accept removal both users + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user2.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user2.address)], user3.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user3.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user3.address)], user2.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts[2].result, `(ok true)`); + assertEquals(block.receipts[3].result, `(ok true)`); + assertEquals(block.receipts[4].result, `[${deployer.address}, ${user4.address}]`); + assertEquals(block.receipts[5].result, `${err_not_proposed_removal}`); + assertEquals(block.receipts.length, 6); + assertEquals(block.height, 112); + }, +}); + +Clarinet.test({ + name: 'Remove case 9 miners in pool', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + const user1 = accounts.get('wallet_1')!; + const user2 = accounts.get('wallet_2')!; + const user3 = accounts.get('wallet_3')!; + const user4 = accounts.get('wallet_4')!; + const user5 = accounts.get('wallet_5')!; + const user6 = accounts.get('wallet_6')!; + const user7 = accounts.get('wallet_7')!; + const user8 = accounts.get('wallet_8')!; + + let block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user1.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user2.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user3.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user4.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user5.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user6.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user7.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user8.address + ), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts[2].result, `(ok true)`); + assertEquals(block.receipts[3].result, `(ok true)`); + assertEquals(block.receipts[4].result, `(ok true)`); + assertEquals(block.receipts[5].result, `(ok true)`); + assertEquals(block.receipts[6].result, `(ok true)`); + assertEquals(block.receipts[7].result, `(ok true)`); + assertEquals(block.receipts.length, 8); + assertEquals(block.height, 2); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user1.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user2.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user3.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user4.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user5.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user6.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user7.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user8.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user1.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user2.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user3.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user4.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user5.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user6.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user7.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user8.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts[2].result, `(ok true)`); + assertEquals(block.receipts[3].result, `(ok true)`); + assertEquals(block.receipts[4].result, `(ok true)`); + assertEquals(block.receipts[5].result, `(ok true)`); + assertEquals(block.receipts[6].result, `(ok true)`); + assertEquals(block.receipts[7].result, `(ok true)`); + assertEquals(block.receipts[8].result, `[]`); + assertEquals(block.receipts[9].result, `(ok true)`); + assertEquals(block.receipts[10].result, `(ok true)`); + assertEquals(block.receipts[11].result, `(ok true)`); + assertEquals(block.receipts[12].result, `(ok true)`); + assertEquals(block.receipts[13].result, `[${user1.address}, ${user2.address}, ${user3.address}, ${user4.address}]`); + assertEquals(block.receipts[14].result, `(ok true)`); + assertEquals(block.receipts[15].result, `(ok true)`); + assertEquals(block.receipts[16].result, `(ok true)`); + assertEquals(block.receipts[17].result, `(ok true)`); + assertEquals( + block.receipts[18].result, + `[${user1.address}, ${user2.address}, ${user3.address}, ${user4.address}, ${user5.address}, ${user6.address}, ${user7.address}, ${user8.address}]` + ); + assertEquals(block.receipts.length, 19); + assertEquals(block.height, 3); + + for (let i = 0; i <= 101; i++) block = chain.mineBlock([]); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[]`); + assertEquals( + block.receipts[2].result, + `[${deployer.address}, ${user1.address}, ${user2.address}, ${user3.address}, ${user4.address}, ${user5.address}, ${user6.address}, ${user7.address}, ${user8.address}]` + ); + assertEquals(block.receipts.length, 3); + assertEquals(block.height, 106); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user1.address)], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 107); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user1.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user3.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user4.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user5.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals( + block.receipts[2].result, + `[${deployer.address}, ${user1.address}, ${user2.address}, ${user3.address}, ${user4.address}, ${user5.address}, ${user6.address}, ${user7.address}, ${user8.address}]` + ); + assertEquals(block.receipts[3].result, `${err_already_voted}`); + assertEquals(block.receipts[4].result, `${err_cant_vote_himself}`); + assertEquals(block.receipts[5].result, `(ok true)`); + assertEquals(block.receipts[6].result, `(ok true)`); + assertEquals(block.receipts[7].result, `(ok true)`); + assertEquals(block.receipts.length, 8); + assertEquals(block.height, 108); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user1.address)], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 109); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user2.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user3.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user4.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user5.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts[2].result, `(ok true)`); + assertEquals(block.receipts[3].result, `(ok true)`); + assertEquals(block.receipts[4].result, `(ok true)`); + assertEquals( + block.receipts[5].result, + `[${deployer.address}, ${user2.address}, ${user3.address}, ${user4.address}, ${user5.address}, ${user6.address}, ${user7.address}, ${user8.address}]` + ); + assertEquals(block.receipts[6].result, `${err_not_proposed_removal}`); + assertEquals(block.receipts.length, 7); + assertEquals(block.height, 110); + }, +}); + +Clarinet.test({ + name: 'Remove case 20 miners in pool', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + const user1 = accounts.get('wallet_1')!; + const user2 = accounts.get('wallet_2')!; + const user3 = accounts.get('wallet_3')!; + const user4 = accounts.get('wallet_4')!; + const user5 = accounts.get('wallet_5')!; + const user6 = accounts.get('wallet_6')!; + const user7 = accounts.get('wallet_7')!; + const user8 = accounts.get('wallet_8')!; + const user9 = accounts.get('wallet_9')!; + const user10 = accounts.get('wallet_10')!; + const user11 = accounts.get('wallet_11')!; + const user12 = accounts.get('wallet_12')!; + const user13 = accounts.get('wallet_13')!; + const user14 = accounts.get('wallet_14')!; + const user15 = accounts.get('wallet_15')!; + const user16 = accounts.get('wallet_16')!; + const user17 = accounts.get('wallet_17')!; + const user18 = accounts.get('wallet_18')!; + const user19 = accounts.get('wallet_19')!; + + let block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user1.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user2.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user3.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user4.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user5.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user6.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user7.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user8.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user9.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user10.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user11.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user12.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user13.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user14.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user15.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user16.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user17.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user18.address + ), + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + user19.address + ), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts[2].result, `(ok true)`); + assertEquals(block.receipts[3].result, `(ok true)`); + assertEquals(block.receipts[4].result, `(ok true)`); + assertEquals(block.receipts[5].result, `(ok true)`); + assertEquals(block.receipts[6].result, `(ok true)`); + assertEquals(block.receipts[7].result, `(ok true)`); + assertEquals(block.receipts[8].result, `(ok true)`); + assertEquals(block.receipts[9].result, `(ok true)`); + assertEquals(block.receipts[10].result, `(ok true)`); + assertEquals(block.receipts[11].result, `(ok true)`); + assertEquals(block.receipts[12].result, `(ok true)`); + assertEquals(block.receipts[13].result, `(ok true)`); + assertEquals(block.receipts[14].result, `(ok true)`); + assertEquals(block.receipts[15].result, `(ok true)`); + assertEquals(block.receipts[16].result, `(ok true)`); + assertEquals(block.receipts[17].result, `(ok true)`); + assertEquals(block.receipts[18].result, `(ok true)`); + assertEquals(block.receipts.length, 19); + assertEquals(block.height, 2); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user1.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user2.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user3.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user4.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user5.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user6.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user7.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user8.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user9.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user10.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user11.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user12.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user13.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user14.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user15.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user16.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user17.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user18.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(user19.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user1.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user2.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user3.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user4.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user5.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user6.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user7.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user8.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user9.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user10.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user11.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user12.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user13.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user14.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user15.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user16.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user17.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user18.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], user19.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + ]); + + assertEquals(block.receipts[19].result, `[]`); + assertEquals( + block.receipts[39].result, + `[${user1.address}, ${user2.address}, ${user3.address}, ${user4.address}, ${user5.address}, ${user6.address}, ${user7.address}, ${user8.address}, ${user9.address}, ${user10.address}, ${user11.address}, ${user12.address}, ${user13.address}, ${user14.address}, ${user15.address}, ${user16.address}, ${user17.address}, ${user18.address}, ${user19.address}]` + ); + assertEquals(block.receipts.length, 40); + assertEquals(block.height, 3); + + for (let i = 0; i <= 101; i++) block = chain.mineBlock([]); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[]`); + assertEquals( + block.receipts[2].result, + `[${deployer.address}, ${user1.address}, ${user2.address}, ${user3.address}, ${user4.address}, ${user5.address}, ${user6.address}, ${user7.address}, ${user8.address}, ${user9.address}, ${user10.address}, ${user11.address}, ${user12.address}, ${user13.address}, ${user14.address}, ${user15.address}, ${user16.address}, ${user17.address}, ${user18.address}, ${user19.address}]` + ); + assertEquals(block.receipts.length, 3); + assertEquals(block.height, 106); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user1.address)], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 107); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user1.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user3.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user4.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user5.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user6.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user7.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user8.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user9.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user10.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals( + block.receipts[2].result, + `[${deployer.address}, ${user1.address}, ${user2.address}, ${user3.address}, ${user4.address}, ${user5.address}, ${user6.address}, ${user7.address}, ${user8.address}, ${user9.address}, ${user10.address}, ${user11.address}, ${user12.address}, ${user13.address}, ${user14.address}, ${user15.address}, ${user16.address}, ${user17.address}, ${user18.address}, ${user19.address}]` + ); + assertEquals(block.receipts[3].result, `${err_already_voted}`); + assertEquals(block.receipts[4].result, `${err_cant_vote_himself}`); + assertEquals(block.receipts[5].result, `(ok true)`); + assertEquals(block.receipts[6].result, `(ok true)`); + assertEquals(block.receipts[7].result, `(ok true)`); + assertEquals(block.receipts[8].result, `(ok true)`); + assertEquals(block.receipts[9].result, `(ok true)`); + assertEquals(block.receipts[10].result, `(ok true)`); + assertEquals(block.receipts[11].result, `(ok true)`); + assertEquals(block.receipts[12].result, `${err_not_proposed_removal}`); + assertEquals(block.receipts.length, 13); + assertEquals(block.height, 108); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user1.address)], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 109); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user2.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user3.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user4.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user5.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user6.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user7.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user8.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user9.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user10.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user11.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user12.address), + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], user13.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts[2].result, `(ok true)`); + assertEquals(block.receipts[3].result, `(ok true)`); + assertEquals(block.receipts[4].result, `(ok true)`); + assertEquals(block.receipts[5].result, `(ok true)`); + assertEquals(block.receipts[6].result, `(ok true)`); + assertEquals(block.receipts[7].result, `(ok true)`); + assertEquals(block.receipts[8].result, `(ok true)`); + assertEquals(block.receipts[9].result, `(ok true)`); + assertEquals(block.receipts[10].result, `(ok true)`); + assertEquals(block.receipts[11].result, `(ok true)`); + assertEquals(block.receipts[12].result, `${err_not_proposed_removal}`); + assertEquals( + block.receipts[13].result, + `[${deployer.address}, ${user2.address}, ${user3.address}, ${user4.address}, ${user5.address}, ${user6.address}, ${user7.address}, ${user8.address}, ${user9.address}, ${user10.address}, ${user11.address}, ${user12.address}, ${user13.address}, ${user14.address}, ${user15.address}, ${user16.address}, ${user17.address}, ${user18.address}, ${user19.address}]` + ); + assertEquals(block.receipts[14].result, `${err_not_proposed_removal}`); + assertEquals(block.receipts.length, 15); + assertEquals(block.height, 110); + }, +}); + +Clarinet.test({ + name: 'Remove case 300 miners in pool', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + const user1 = accounts.get('wallet_1')!; + const user2 = accounts.get('wallet_2')!; + let testing_list = []; + let block = chain.mineBlock([]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + for (let i = 1; i <= 299; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + miner.address + ), + ]); + if (i == 1) testing_list.push(`${miner.address}`); + else testing_list.push(` ${miner.address}`); + } + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 302); + + for (let i = 1; i <= 299; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), + ]); + } + + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 602); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); + assertEquals(block.receipts.length, 2); + assertEquals(block.height, 603); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user1.address)], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 604); + + for (let i = 2; i <= 102; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], miner.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + } + assertEquals(block.height, 705); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, PROPOSE_REMOVAL, [types.principal(user1.address)], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 706); + + for (let i = 2; i <= 201; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_REMOVE, [types.principal(user1.address)], miner.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + } + assertEquals(block.height, 906); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NEGATIVE_REMOVE, [types.principal(user1.address)], user2.address), + ]); + + assertEquals(block.receipts[0].result, `[${deployer.address},${testing_list.slice(1)}]`); + assertEquals(block.receipts[1].result, `${err_not_proposed_removal}`); + assertEquals(block.receipts.length, 2); + assertEquals(block.height, 907); + }, +}); + +Clarinet.test({ + name: 'Update notifier 300 miners in pool', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + const user1 = accounts.get('wallet_1')!; + const user2 = accounts.get('wallet_2')!; + let testing_list = []; + let block = chain.mineBlock([]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + for (let i = 1; i <= 299; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + miner.address + ), + ]); + if (i == 1) testing_list.push(`${miner.address}`); + else testing_list.push(` ${miner.address}`); + } + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 302); + + for (let i = 1; i <= 299; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), + ]); + } + + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 602); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); + assertEquals(block.receipts.length, 2); + assertEquals(block.height, 603); + + // First case: accept notifier update comparing votes number to k on vote fn in less than 144 blocks + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER_VOTE_STATUS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, START_VOTE_NOTIFIER, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER_VOTE_STATUS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_K, [], deployer.address), + ]); + assertEquals(block.receipts[0].result, `false`); + assertEquals(block.receipts[1].result, `(ok true)`); + assertEquals(block.receipts[2].result, `true`); + assertEquals(block.receipts[3].result, `u200`); + assertEquals(block.receipts.length, 4); + assertEquals(block.height, 604); + + for (let i = 2; i <= 101; i++) { + const miner1 = accounts.get(`wallet_${2 * i}`)!; + const miner2 = accounts.get(`wallet_${2 * i + 1}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER_VOTE_NUMBER, [types.principal(user1.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NOTIFIER, [types.principal(user1.address)], miner1.address), + Tx.contractCall(CONTRACT_NAME, VOTE_NOTIFIER, [types.principal(user1.address)], miner2.address), + ]); + if (i != 2) assertEquals(block.receipts[0].result, `(some u${2 * i - 4})`); + else assertEquals(block.receipts[0].result, `none`); + + assertEquals(block.receipts[1].result, `(ok true)`); + + if (2 * i + 1 == 203) { + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER_VOTE_NUMBER, [types.principal(user1.address)], deployer.address), + Tx.contractCall( + CONTRACT_NAME, + VOTE_NOTIFIER, + [types.principal(user1.address)], + accounts.get(`wallet_${2 * i + 2}`).address + ), + ]); + + assertEquals(block.receipts[0].result, `none`); + assertEquals(block.receipts[1].result, `${err_no_voting_period}`); + } else assertEquals(block.receipts[2].result, `(ok true)`); + } + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER_VOTE_STATUS, [], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `${user1.address}`); + assertEquals(block.receipts[1].result, `false`); + assertEquals(block.receipts.length, 2); + assertEquals(block.height, 706); + + // Second case: accept notifier update comparing votes number to k/2 on vote fn after 144 blocks + // k=200, k/2=100 + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, START_VOTE_NOTIFIER, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER_VOTE_STATUS, [], deployer.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `true`); + assertEquals(block.receipts.length, 2); + assertEquals(block.height, 707); + + for (let i = 2; i <= 147; i++) { + const miner = accounts.get(`wallet_${i}`)!; + + // 101 votes first 101 blocks, the rest to 144 blocks empty blocks + if (i <= 103) { + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_NOTIFIER, [types.principal(deployer.address)], miner.address), + Tx.contractCall( + CONTRACT_NAME, + GET_NOTIFIER_VOTE_NUMBER, + [types.principal(deployer.address)], + deployer.address + ), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(some u${i - 1})`); + } else { + block = chain.mineBlock([]); + } + } + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, GET_MAX_VOTED_NOTIFIER, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MAX_VOTES_NOTIFIER, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, END_VOTE_NOTIFIER, [], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `${user1.address}`); + assertEquals(block.receipts[1].result, `u0`); + assertEquals(block.receipts[2].result, `(ok true)`); + assertEquals(block.receipts.length, 3); + assertEquals(block.height, 854); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER_VOTE_STATUS, [], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `${deployer.address}`); + assertEquals(block.receipts[1].result, `false`); + assertEquals(block.receipts.length, 2); + assertEquals(block.height, 855); + + // Third case: Reject notifier update not enough votes (k/2) for at least one notifier after 144 blocks + // k=200, k/2=100 + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, START_VOTE_NOTIFIER, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER_VOTE_STATUS, [], deployer.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `true`); + assertEquals(block.receipts.length, 2); + assertEquals(block.height, 856); + + for (let i = 2; i <= 146; i++) { + const miner = accounts.get(`wallet_${i}`)!; + + // 101 votes first 101 blocks, the rest to 144 blocks empty blocks + if (i <= 50) { + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_NOTIFIER, [types.principal(deployer.address)], miner.address), + Tx.contractCall( + CONTRACT_NAME, + GET_NOTIFIER_VOTE_NUMBER, + [types.principal(deployer.address)], + deployer.address + ), + ]); + + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `(some u${i - 1})`); + } else { + block = chain.mineBlock([]); + } + } + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, GET_MAX_VOTED_NOTIFIER, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MAX_VOTES_NOTIFIER, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, END_VOTE_NOTIFIER, [], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `${deployer.address}`); + assertEquals(block.receipts[1].result, `u0`); + assertEquals(block.receipts[2].result, `(ok true)`); + assertEquals(block.receipts.length, 3); + assertEquals(block.height, 1002); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_NOTIFIER_VOTE_STATUS, [], deployer.address), + ]); + + assertEquals(block.receipts[0].result, `${deployer.address}`); + assertEquals(block.receipts[1].result, `false`); + assertEquals(block.receipts.length, 2); + assertEquals(block.height, 1003); + }, +}); + +// test for 100 users +// each deposits his index amount +// check with events emitted + +Clarinet.test({ + name: 'Deposit test', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + + let block = chain.mineBlock([]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + for (let i = 1; i <= 100; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, DEPOSIT, [types.uint(CONVERT_TO_STX(i))], miner.address), + ]); + block.receipts[0].result.expectOk(); + //amount: bigint; sender: string; recipient: string; } + // block.receipts[0].events.ExpectSTXTransferEvent(i, miner.address, CONTRACT_NAME); + assertEquals(block.receipts[0].events[0].type, 'stx_transfer_event'); + assertEquals(block.receipts[0].events[0].stx_transfer_event.sender, miner.address); + assertEquals(block.receipts[0].events[0].stx_transfer_event.recipient, deployer.address + '.' + CONTRACT_NAME); + assertEquals(block.receipts[0].events[0].stx_transfer_event.amount, `${CONVERT_TO_STX(i)}`); + } + }, +}); + +// check each has that using read only call +Clarinet.test({ + name: 'Deposit successfully + check balances test', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + + let block = chain.mineBlock([]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + // read only call + for (let i = 1; i <= 100; i++) { + const miner = accounts.get(`wallet_${i}`)!; + let balance = chain.callReadOnlyFn( + CONTRACT_NAME, + GET_BALANCE, + [types.principal(miner.address)], + deployer.address + ); + balance.result.expectNone(); + } + + for (let i = 1; i <= 100; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, DEPOSIT, [types.uint(CONVERT_TO_STX(i))], miner.address), + ]); + + block.receipts[0].result.expectOk(); + block.receipts[0].events.expectSTXTransferEvent( + CONVERT_TO_STX(i), + miner.address, + deployer.address + '.' + CONTRACT_NAME + ); + } + + // read only call + for (let i = 1; i <= 100; i++) { + const miner = accounts.get(`wallet_${i}`)!; + let balance = chain.callReadOnlyFn( + CONTRACT_NAME, + GET_BALANCE, + [types.principal(miner.address)], + deployer.address + ); + balance.result.expectSome().expectUint(CONVERT_TO_STX(i)); // check k==1 + } + }, +}); + +// check no one can withdraw without depositing first and neither can withdraw more than deposited +Clarinet.test({ + name: 'Ensure 100 users cannot withdraw without depositing first and neither cannot withdraw more than deposited', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + + let testing_list = []; + let block = chain.mineBlock([]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + for (let i = 1; i <= 100; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, WITHDRAW, [types.uint(CONVERT_TO_STX(i))], miner.address), + ]); + block.receipts[0].result.expectErr(err_insufficient_balance); + } + + for (let i = 1; i <= 100; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, DEPOSIT, [types.uint(CONVERT_TO_STX(i))], miner.address), + ]); + + block.receipts[0].result.expectOk(); + block.receipts[0].events.expectSTXTransferEvent( + CONVERT_TO_STX(i), + miner.address, + deployer.address + '.' + CONTRACT_NAME + ); + } + + for (let i = 1; i <= 100; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, WITHDRAW, [types.uint(CONVERT_TO_STX(i) + 1)], miner.address), + ]); + block.receipts[0].result.expectErr(err_insufficient_balance); + } + }, +}); + +// check each can withdraw his amount +Clarinet.test({ + name: 'Ensure 100 users can withdraw their amount', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + + let testing_list = []; + let block = chain.mineBlock([]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + for (let i = 1; i <= 100; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, DEPOSIT, [types.uint(CONVERT_TO_STX(i))], miner.address), + ]); + + block.receipts[0].result.expectOk(); + block.receipts[0].events.expectSTXTransferEvent( + CONVERT_TO_STX(i), + miner.address, + deployer.address + '.' + CONTRACT_NAME + ); + } + + for (let i = 1; i <= 5; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, WITHDRAW, [types.uint(CONVERT_TO_STX(i))], miner.address), + ]); + block.receipts[0].result.expectOk().expectBool(true); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, GET_BALANCE, [types.principal(miner.address)], miner.address), + ]); + block.receipts[0].result.expectSome().expectUint(0); + } + }, +}); + +// check each can withdraw his amount +Clarinet.test({ + name: 'Ensure 300 users can withdraw their amount', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + + let testing_list = []; + let block = chain.mineBlock([]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + for (let i = 1; i <= 299; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, DEPOSIT, [types.uint(CONVERT_TO_STX(i))], miner.address), + ]); + + block.receipts[0].result.expectOk(); + block.receipts[0].events.expectSTXTransferEvent( + CONVERT_TO_STX(i), + miner.address, + deployer.address + '.' + CONTRACT_NAME + ); + } + + for (let i = 1; i <= 299; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, WITHDRAW, [types.uint(CONVERT_TO_STX(i))], miner.address), + ]); + block.receipts[0].result.expectOk().expectBool(true); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, GET_BALANCE, [types.principal(miner.address)], miner.address), + ]); + block.receipts[0].result.expectSome().expectUint(0); + } + }, +}); + +Clarinet.test({ + name: 'Rewards distribution test 100 users', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + const user1 = accounts.get('wallet_1')!; + let testing_list = []; + let block; + + for (let i = 1; i <= 100; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + miner.address + ), + ]); + if (i == 1) testing_list.push(`${miner.address}`); + else testing_list.push(` ${miner.address}`); + } + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 102); + + for (let i = 1; i <= 100; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), + ]); + } + + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 203); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); + assertEquals(block.receipts.length, 2); + assertEquals(block.height, 204); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, GET_REWARD_AT_BLOCK_READ, [types.uint(10)], deployer.address), + Tx.contractCall(CONTRACT_NAME, REWARD_DISTRIBUTION, [types.uint(10)], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_BALANCE, [types.principal(deployer.address)], deployer.address), + ]); + // console.log(block.receipts[0].result); + // GOT a reward of (some u5000) = 0.005 STX, only deployer in pool at block 10 + block.receipts[1].result.expectOk().expectBool(true); + block.receipts[2].result.expectSome().expectUint(CONVERT_TO_STX(0.005)); + + // HERE, THE REWARD RESPONSE WAS NONE FOR BLOCK 110, CREATED A SPECIFIC ERROR FOR THIS: (err 137) + // block = chain.mineBlock([ + // Tx.contractCall(CONTRACT_NAME, REWARD_DISTRIBUTION, [types.uint(110)], deployer.address), + // Tx.contractCall(CONTRACT_NAME, GET_BALANCE, [types.principal(user1.address)], deployer.address), + // ]); + + // console.log(block.receipts[0].result); + // block.receipts[0].result.expectOk().expectBool(true); + // block.receipts[1].result.expectSome().expectUint(CONVERT_TO_STX(9.90099)); //1000 STX / 101 miners = 9.9009900990099009900990099009901 + }, +}); + +Clarinet.test({ + name: 'Rewards distribution test 300 users', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + const user1 = accounts.get('wallet_1')!; + let testing_list = []; + let block; + + for (let i = 1; i <= 299; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall( + CONTRACT_NAME, + ASK_TO_JOIN, + [ + types.tuple({ + version: types.buff(hash160(buffer_from('00'))), + hashbytes: types.buff(hash160(buffer_from(publicKeyHex))), + }), + ], + miner.address + ), + ]); + if (i == 1) testing_list.push(`${miner.address}`); + else testing_list.push(` ${miner.address}`); + } + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_WAITING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 301); + + for (let i = 1; i <= 299; i++) { + const miner = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, VOTE_POSITIVE_JOIN, [types.principal(miner.address)], deployer.address), + Tx.contractCall(CONTRACT_NAME, TRY_ENTER_POOL, [], miner.address), + ]); + } + + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, GET_PENDING_LIST, [], deployer.address)]); + assertEquals(block.receipts[0].result, `[${testing_list}]`); + assertEquals(block.receipts.length, 1); + assertEquals(block.height, 601); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, ADD_PENDING_MINERS, [], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_MINERS_LIST, [], deployer.address), + ]); + assertEquals(block.receipts[0].result, `(ok true)`); + assertEquals(block.receipts[1].result, `[${deployer.address}, ${testing_list}]`); + assertEquals(block.receipts.length, 2); + assertEquals(block.height, 602); + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, REWARD_DISTRIBUTION, [types.uint(10)], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_BALANCE, [types.principal(deployer.address)], deployer.address), + ]); + + block.receipts[0].result.expectOk().expectBool(true); + block.receipts[1].result.expectSome().expectUint(CONVERT_TO_STX(0.005)); + + // some blocks pass to get the reward + + for (let i = 1; i <= 120; i++) { + block = chain.mineBlock([]); + } + + // distributing rewards + + block = chain.mineBlock([ + Tx.contractCall(CONTRACT_NAME, GET_REWARD_AT_BLOCK_READ, [types.uint(10)], deployer.address), + Tx.contractCall(CONTRACT_NAME, REWARD_DISTRIBUTION, [types.uint(602)], deployer.address), + Tx.contractCall(CONTRACT_NAME, GET_BALANCE, [types.principal(user1.address)], deployer.address), + ]); + + // console.log (block.receipts[0].result) + // REWARD: (some u5000) => 0.005 STX + block.receipts[1].result.expectOk().expectBool(true); + block.receipts[2].result.expectSome().expectUint(CONVERT_TO_STX(0.000016)); //0.005 STX / 300 miners = 0.000016 + + block = chain.mineBlock([Tx.contractCall(CONTRACT_NAME, REWARD_DISTRIBUTION, [types.uint(602)], deployer.address)]); + + block.receipts[0].result.expectErr().expectUint(1003); + }, +}); + +Clarinet.test({ + name: 'Check block stacks info', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + + let block = chain.mineBlock([]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + // read only call + for (let i = 1; i <= 103; i++) chain.mineBlock([]); + + let block_info = chain.callReadOnlyFn(CONTRACT_NAME, GET_REWARD_AT_BLOCK_READ, [types.uint(3)], deployer.address); + // balance.result.expectNone(); + // } + + // console.log(block_info); + + // for (let i = 1; i <= 100; i++) { + // const miner = accounts.get(`wallet_${i}`)!; + // block = chain.mineBlock([ + // Tx.contractCall(CONTRACT_NAME, DEPOSIT, [types.uint(CONVERT_TO_STX(i))], miner.address), + // ]); + + // block.receipts[0].result.expectOk(); + // block.receipts[0].events.expectSTXTransferEvent( + // CONVERT_TO_STX(i), + // miner.address, + // deployer.address + '.' + CONTRACT_NAME + // ); + // } + + // read only call + // for (let i = 1; i <= 100; i++) { + // const miner = accounts.get(`wallet_${i}`)!; + // let balance = chain.callReadOnlyFn( + // CONTRACT_NAME, + // GET_BALANCE, + // [types.principal(miner.address)], + // deployer.address + // ); + // balance.result.expectSome().expectUint(CONVERT_TO_STX(i)); // check k==1 + // } + }, +}); diff --git a/sbtc-ops/smart-contract/tests/pox-pool-self-service_allowance_test.ts b/sbtc-ops/smart-contract/tests/pox-pool-self-service_allowance_test.ts new file mode 100644 index 00000000..44200699 --- /dev/null +++ b/sbtc-ops/smart-contract/tests/pox-pool-self-service_allowance_test.ts @@ -0,0 +1,547 @@ +import { allowContractCaller, disallowContractCaller, getStackerInfo } from './client/pox-2-client.ts'; +import { + delegateStackStx, + delegateStx, + fpDelegationAllowContractCaller, + getUserData, + joinStackingPool, + quitStackingPool, +} from './client/stacking-pool-client.ts'; +import { Clarinet, Chain, Account, Tx, types } from './deps.ts'; +import { Errors, PoxErrors, poxAddrFP, poxAddrPool1, poxAddrPool2 } from './constants.ts'; + +import { expectPartialStackedByCycle, expectTotalStackedByCycle } from './utils.ts'; + +Clarinet.test({ + name: "Ensure that user can't delegate without allowance", + async fn(chain: Chain, accounts: Map) { + const wallet_1 = accounts.get('wallet_1')!; + + // try without any allowance + let block = chain.mineBlock([delegateStx(20_000_000_000_000, wallet_1)]); + + // check delegation calls + block.receipts[0].result.expectErr().expectUint(Errors.AllowPoolInPox2); + }, +}); + +Clarinet.test({ + name: 'Ensure liquidity provider can deposit STX into the SC', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + const wallet_1 = accounts.get('wallet_1')!; + + // try without any allowance + let block = chain.mineBlock([ + Tx.contractCall( + 'stacking-pool-test', + 'deposit-stx-liquidity-provider', + [types.uint(10_000_000_000)], + deployer.address + ), + ]); + + // check delegation calls + block.receipts[0].result.expectOk().expectBool(true); + + block = chain.mineBlock([Tx.contractCall('stacking-pool-test', 'get-SC-total-balance', [], deployer.address)]); + + block.receipts[0].result.expectUint(10_000_000_000); + }, +}); + +Clarinet.test({ + name: "Ensure that user can't join the pool without allowance", + async fn(chain: Chain, accounts: Map) { + const wallet_1 = accounts.get('wallet_1')!; + + // try without any allowance + let block = chain.mineBlock([joinStackingPool(wallet_1)]); + + // check error to be returned + block.receipts[0].result.expectErr().expectUint(Errors.AllowPoolInPox2); + }, +}); + +Clarinet.test({ + name: 'Ensure that user can join the pool after allowing pool SC in pox-2 contract', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + const wallet_1 = accounts.get('wallet_1')!; + const mainContract = deployer.address + '.stacking-pool-test'; + + // try without any allowance + let block = chain.mineBlock([allowContractCaller(mainContract, undefined, wallet_1), joinStackingPool(wallet_1)]); + + // check that both calls above return true + block.receipts[0].result.expectOk().expectBool(true); + block.receipts[1].result.expectOk().expectBool(true); + }, +}); + +Clarinet.test({ + name: 'Ensure that user can quit the pool after disallowing pool SC in pox-2 contract', + async fn(chain: Chain, accounts: Map) { + const deployer = accounts.get('deployer')!; + const wallet_1 = accounts.get('wallet_1')!; + const mainContract = deployer.address + '.stacking-pool-test'; + + let block = chain.mineBlock([ + allowContractCaller(mainContract, undefined, wallet_1), + joinStackingPool(wallet_1), + disallowContractCaller(mainContract, wallet_1), + quitStackingPool(wallet_1), + ]); + + // check that both calls above return true + block.receipts[0].result.expectOk().expectBool(true); + block.receipts[1].result.expectOk().expectBool(true); + block.receipts[2].result.expectOk().expectBool(true); + block.receipts[3].result.expectOk().expectBool(true); + }, +}); + +Clarinet.test({ + name: 'Ensure that user can only delegate from a contract allowing pox-2 and joining the pool', + async fn(chain: Chain, accounts: Map) { + const mainDelegateStx = (amountUstx: number, user: Account) => { + return Tx.contractCall('stacking-pool-test', 'delegate-stx', [types.uint(amountUstx)], user.address); + }; + const deployer = accounts.get('deployer')!; + const wallet_1 = accounts.get('wallet_1')!; + const wallet_2 = accounts.get('wallet_2')!; + const mainContract = deployer.address + '.stacking-pool-test'; + + // try without any allowance + let block = chain.mineBlock([mainDelegateStx(20_000_000_000, wallet_1)]); + block.receipts[0].result.expectErr().expectUint(Errors.AllowPoolInPox2); + + // try with pox allowance only + block = chain.mineBlock([ + allowContractCaller(mainContract, undefined, wallet_1), + mainDelegateStx(20_000_000_000_000, wallet_1), + ]); + + block.receipts[0].result.expectOk().expectBool(true); + block.receipts[1].result.expectErr().expectUint(Errors.NotInPool); + + // delegate-stx with pox-2 allowance and joining the pool + block = chain.mineBlock([joinStackingPool(wallet_1), mainDelegateStx(20_000_000_000_000, wallet_1)]); + block.receipts[0].result.expectOk().expectBool(true); + block.receipts[1].result.expectOk().expectBool(true); + expectTotalStackedByCycle(1, 0, 20_000_000_000_000, chain, deployer); + }, +}); + +Clarinet.test({ + name: 'Ensure that user can delegate how much he wants, but can lock only funds he owns', + async fn(chain: Chain, accounts: Map) { + const mainDelegateStx = (amountUstx: number, user: Account) => { + return Tx.contractCall('stacking-pool-test', 'delegate-stx', [types.uint(amountUstx)], user.address); + }; + const deployer = accounts.get('deployer')!; + const wallet_1 = accounts.get('wallet_1')!; + const wallet_2 = accounts.get('wallet_2')!; + const mainContract = deployer.address + '.stacking-pool-test'; + + // user owns 100_000_000_000_000, delegates 200_000_000_000_000 + let block = chain.mineBlock([ + allowContractCaller(mainContract, undefined, wallet_1), + joinStackingPool(wallet_1), + mainDelegateStx(200_000_000_000_000, wallet_1), + getUserData(wallet_1, wallet_1), + ]); + block.receipts[0].result.expectOk().expectBool(true); + block.receipts[1].result.expectOk().expectBool(true); + block.receipts[2].result.expectOk().expectBool(true); + // 200_000_000_000_000 delegated, 100_000_000_000_000 locked + block.receipts[3].result.expectSome().expectTuple()['delegated-balance'].expectUint(200_000_000_000_000); + block.receipts[3].result.expectSome().expectTuple()['locked-balance'].expectUint(100_000_000_000_000); + // check total to be 100_000_000_000_000 instead of what user has delegated + expectTotalStackedByCycle(1, 0, 100_000_000_000_000, chain, deployer); + + block = chain.mineBlock([ + allowContractCaller(mainContract, undefined, wallet_2), + joinStackingPool(wallet_2), + mainDelegateStx(400_000_000_000_000, wallet_2), + getUserData(wallet_2, wallet_2), + ]); + block.receipts[0].result.expectOk().expectBool(true); + block.receipts[1].result.expectOk().expectBool(true); + block.receipts[2].result.expectOk().expectBool(true); + // 400_000_000_000_000 delegated, 200_000_000_000_000 locked + block.receipts[3].result.expectSome().expectTuple()['delegated-balance'].expectUint(400_000_000_000_000); + block.receipts[3].result.expectSome().expectTuple()['locked-balance'].expectUint(100_000_000_000_000); + // check total to be 200_000_000_000_000 (100_000_000_000_000 + 100_000_000_000_000 already stacked) instead of what user has delegated + expectTotalStackedByCycle(1, 0, 200_000_000_000_000, chain, deployer); + }, +}); + +Clarinet.test({ + name: 'Ensure that user can delegate how much he wants, but it will be locked just when the threshold is met', + // stx_liq_supply / threshold_25 == 1_000_000_000_000_000 / 20_000_000_000 = 50_000 + + async fn(chain: Chain, accounts: Map) { + const mainDelegateStx = (amountUstx: number, user: Account) => { + return Tx.contractCall('stacking-pool-test', 'delegate-stx', [types.uint(amountUstx)], user.address); + }; + const deployer = accounts.get('deployer')!; + const wallet_1 = accounts.get('wallet_1')!; + const wallet_2 = accounts.get('wallet_2')!; + const mainContract = deployer.address + '.stacking-pool-test'; + + // Allow pool SC in pox-2, join stacking pool and delegate with wallet_1 + let block = chain.mineBlock([ + allowContractCaller(mainContract, undefined, wallet_1), + joinStackingPool(wallet_1), + mainDelegateStx(1_000_000_000, wallet_1), // < 50_000_000_000 uSTX + ]); + block.receipts[0].result.expectOk().expectBool(true); + block.receipts[1].result.expectOk().expectBool(true); + block.receipts[2].result.expectOk().expectBool(false); // expect false, commit ignored + // {err-commit-ignored:11} + + // Check local user data + block = chain.mineBlock([getUserData(wallet_1, deployer), getUserData(wallet_2, deployer)]); + + // verify delegated-balance==locked-balance==1_000_000_000 + block.receipts[0].result.expectSome().expectTuple()['delegated-balance'].expectUint(1_000_000_000); + block.receipts[0].result.expectSome().expectTuple()['locked-balance'].expectUint(1_000_000_000); + + // verify is-none - not in stacking pool yet + block.receipts[1].result.expectNone(); + + expectPartialStackedByCycle(poxAddrFP, 1, 0, chain, deployer); // does not commit partially + expectTotalStackedByCycle(1, 0, 0, chain, deployer); // does not commit totally + + // Allow pool SC in pox-2, join stacking pool and delegate with wallet_2 + block = chain.mineBlock([ + allowContractCaller(mainContract, undefined, wallet_2), + joinStackingPool(wallet_2), + mainDelegateStx(10_000_000_000_000, wallet_2), // > 50_000_000_000 uSTX + ]); + block.receipts[0].result.expectOk().expectBool(true); + block.receipts[1].result.expectOk().expectBool(true); + block.receipts[2].result.expectOk().expectBool(true); + + expectPartialStackedByCycle(poxAddrFP, 1, 0, chain, deployer); // does not commit partially + expectTotalStackedByCycle(1, 0, 10_001_000_000_000, chain, deployer); // commits totally the amount wallet_1 + wallet_2 delegated + + block = chain.mineBlock([getUserData(wallet_1, deployer), getUserData(wallet_2, deployer)]); + + // verify delegated-balance==locked-balance== 1_000_000_000 + block.receipts[0].result.expectSome().expectTuple()['delegated-balance'].expectUint(1_000_000_000); + block.receipts[0].result.expectSome().expectTuple()['locked-balance'].expectUint(1_000_000_000); + + // verify delegated-balance==locked-balance==124_000_000_000 + block.receipts[1].result.expectSome().expectTuple()['delegated-balance'].expectUint(10_000_000_000_000); + block.receipts[1].result.expectSome().expectTuple()['locked-balance'].expectUint(10_000_000_000_000); + }, +}); + +// Clarinet.test({ +// name: "Ensure stack is extended", + +// async fn(chain: Chain, accounts: Map) { +// const mainDelegateStx = (amountUstx: number, user: Account) => { +// return Tx.contractCall( +// "stacking-pool", +// "delegate-stx", +// [types.uint(amountUstx)], +// user.address +// ); +// }; +// const deployer = accounts.get("deployer")!; +// const wallet_1 = accounts.get("wallet_1")!; +// const wallet_2 = accounts.get("wallet_2")!; +// const mainContract = deployer.address + ".stacking-pool"; + +// // Allow pool SC in pox-2, join stacking pool and delegate with wallet_1 +// let block = chain.mineBlock([ +// allowContractCaller(mainContract, undefined, wallet_1), +// joinStackingPool(wallet_1), +// mainDelegateStx(125_000_000_000, wallet_1), +// ]); +// block.receipts[0].result.expectOk().expectBool(true); +// block.receipts[1].result.expectOk().expectBool(true); +// block.receipts[2].result.expectOk().expectBool(true); + +// // Check local user data +// block = chain.mineBlock([getUserData(wallet_1, deployer)]); + +// // verify delegated-balance==locked-balance==125_000_000_000 +// block.receipts[0].result +// .expectSome() +// .expectTuple() +// ["delegated-balance"].expectUint(125_000_000_000); +// block.receipts[0].result +// .expectSome() +// .expectTuple() +// ["locked-balance"].expectUint(125_000_000_000); +// expectPartialStackedByCycle(poxAddrFP, 1, 0, chain, deployer); // does not commit partially +// expectTotalStackedByCycle(1, 0, 125_000_000_000, chain, deployer); // commits totally + +// block = chain.mineBlock([ +// Tx.contractCall( +// "ST000000000000000000002AMW42H.pox-2", +// "get-stacker-info", +// [types.principal(wallet_1.address)], +// deployer.address +// ), +// ]); + +// console.log("STACKER INFO BEFORE:", block.receipts[0].result); + +// for (let i = 1; i <= 4196; i++) { +// // why only > 4195 works?? +// block = chain.mineBlock([]); +// } + +// block = chain.mineBlock([ +// Tx.contractCall( +// "ST000000000000000000002AMW42H.pox-2", +// "get-stacker-info", +// [types.principal(wallet_1.address)], +// deployer.address +// ), +// ]); + +// console.log("STACKER INFO AFTER:", block.receipts[0].result); + +// block = chain.mineBlock([mainDelegateStx(125_000_000_000, wallet_1)]); +// block.receipts[0].result.expectOk().expectBool(true); // expect true, lock funds, extend lock period + +// block = chain.mineBlock([getUserData(wallet_1, deployer)]); + +// block.receipts[0].result +// .expectSome() +// .expectTuple() +// ["until-burn-ht"].expectSome() +// .expectUint(6300); +// }, +// }); + +Clarinet.test({ + name: 'Ensure that commit is ignored when multiple wallets delegate small amounts and total delegated < threshold', + // stx_liq_supply / threshold_25 == 1_000_000_000_000_000 / 20_000_000_000 = 50_000 + + async fn(chain: Chain, accounts: Map) { + const mainDelegateStx = (amountUstx: number, user: Account) => { + return Tx.contractCall('stacking-pool-test', 'delegate-stx', [types.uint(amountUstx)], user.address); + }; + const deployer = accounts.get('deployer')!; + const mainContract = deployer.address + '.stacking-pool-test'; + + // Allow pool SC in pox-2, join stacking pool and delegate with first 6 wallets + let block: any; + + for (let i = 1; i <= 6; i++) { + let stacker = accounts.get(`wallet_${i}`); + block = chain.mineBlock([ + allowContractCaller(mainContract, undefined, stacker), + joinStackingPool(stacker), + mainDelegateStx(1_000_000_000, stacker), // < 50_000_000_000 uSTX + ]); + block.receipts[0].result.expectOk().expectBool(true); + block.receipts[1].result.expectOk().expectBool(true); + block.receipts[2].result.expectOk().expectBool(false); // expect false, commit ignored + // {err-commit-ignored:11} + } + + for (let i = 1; i <= 6; i++) { + let stacker = accounts.get(`wallet_${i}`); + + // Check local user data + block = chain.mineBlock([getUserData(stacker, deployer)]); + + // verify delegated-balance==locked-balance==1_000_000_000 + block.receipts[0].result.expectSome().expectTuple()['delegated-balance'].expectUint(1_000_000_000); + block.receipts[0].result.expectSome().expectTuple()['locked-balance'].expectUint(1_000_000_000); + } + + expectPartialStackedByCycle(poxAddrFP, 1, 0, chain, deployer); // does not commit partially + expectTotalStackedByCycle(1, 0, 0, chain, deployer); // does not commit totally + }, +}); + +Clarinet.test({ + name: 'Ensure that user can delegate how much he wants, but it will be locked just when the threshold is met', + + async fn(chain: Chain, accounts: Map) { + const mainDelegateStx = (amountUstx: number, user: Account) => { + return Tx.contractCall('stacking-pool-test', 'delegate-stx', [types.uint(amountUstx)], user.address); + }; + const deployer = accounts.get('deployer')!; + const wallet_4 = accounts.get('wallet_4')!; + const wallet_8 = accounts.get('wallet_8')!; + const mainContract = deployer.address + '.stacking-pool-test'; + + // Allow pool SC in pox-2, join stacking pool and delegate with first 3 wallets + let block: any; + + for (let i = 1; i <= 3; i++) { + let stacker = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + allowContractCaller(mainContract, undefined, stacker), + joinStackingPool(stacker), + mainDelegateStx(1_000_000_000, stacker), // < 125_000_000_000 uSTX + ]); + block.receipts[0].result.expectOk().expectBool(true); + block.receipts[1].result.expectOk().expectBool(true); + block.receipts[2].result.expectOk().expectBool(false); // expect false, commit ignored + // {err-commit-ignored:11} + } + + // Check local user data + for (let i = 1; i <= 3; i++) { + let stacker = accounts.get(`wallet_${i}`)!; + + // Check local user data + block = chain.mineBlock([getUserData(stacker, deployer)]); + + // verify delegated-balance==locked-balance==1_000_000_000 + block.receipts[0].result.expectSome().expectTuple()['delegated-balance'].expectUint(1_000_000_000); + block.receipts[0].result.expectSome().expectTuple()['locked-balance'].expectUint(1_000_000_000); + } + + expectPartialStackedByCycle(poxAddrFP, 1, 0, chain, deployer); // does not commit partially + expectTotalStackedByCycle(1, 0, 0, chain, deployer); // does not commit totally + + // Allow pool SC in pox-2, join stacking pool and delegate with wallet_4 + block = chain.mineBlock([ + allowContractCaller(mainContract, undefined, wallet_4), + joinStackingPool(wallet_4), + mainDelegateStx(3_780_000_000_000, wallet_4), // approximate threshold + ]); + block.receipts[0].result.expectOk().expectBool(true); + block.receipts[1].result.expectOk().expectBool(true); + block.receipts[2].result.expectOk().expectBool(true); + // no print, returns ok true => amount commited + + expectPartialStackedByCycle(poxAddrFP, 1, 0, chain, deployer); // does not commit partially + expectTotalStackedByCycle(1, 0, 3783000000000, chain, deployer); // commits totally the amount wallet_1 + wallet_2 delegated + + for (let i = 5; i <= 7; i++) { + let stacker = accounts.get(`wallet_${i}`)!; + block = chain.mineBlock([ + allowContractCaller(mainContract, undefined, stacker), + joinStackingPool(stacker), + mainDelegateStx(1_000_000_000, stacker), // < 125_000_000_000 uSTX + ]); + block.receipts[0].result.expectOk().expectBool(true); + block.receipts[1].result.expectOk().expectBool(true); + block.receipts[2].result.expectOk().expectBool(true); // after threshold is met, everything is commited (increase) + // threshold was previously met, ok true + } + + for (let i = 5; i <= 7; i++) { + let stacker = accounts.get(`wallet_${i}`)!; + + // Check local user data + block = chain.mineBlock([getUserData(stacker, deployer)]); + + // verify delegated-balance==locked-balance==1_000_000_000 + block.receipts[0].result.expectSome().expectTuple()['delegated-balance'].expectUint(1_000_000_000); + block.receipts[0].result.expectSome().expectTuple()['locked-balance'].expectUint(1_000_000_000); + } + + expectPartialStackedByCycle(poxAddrFP, 1, 0, chain, deployer); // does not commit partially + expectTotalStackedByCycle(1, 0, 3786000000000, chain, deployer); // does not commit totally + + // Allow pool SC in pox-2, join stacking pool and delegate with wallet_8 + block = chain.mineBlock([ + allowContractCaller(mainContract, undefined, wallet_8), + joinStackingPool(wallet_8), + mainDelegateStx(122_000_000_000, wallet_8), // < 125_000_000_000 uSTX + ]); + block.receipts[0].result.expectOk().expectBool(true); + block.receipts[1].result.expectOk().expectBool(true); + block.receipts[2].result.expectOk().expectBool(true); + // no print, returns ok true => amount commited + + expectPartialStackedByCycle(poxAddrFP, 1, 0, chain, deployer); // does not commit partially + expectTotalStackedByCycle(1, 0, 3908000000000, chain, deployer); // commits totally the amount wallet_1, wallet_2, ... wallet_8 delegated + + block = chain.mineBlock([getUserData(wallet_8, deployer)]); + + // verify delegated-balance==locked-balance==122_000_000_000 + block.receipts[0].result.expectSome().expectTuple()['delegated-balance'].expectUint(122_000_000_000); + block.receipts[0].result.expectSome().expectTuple()['locked-balance'].expectUint(122_000_000_000); + }, +}); + +// Clarinet.test({ +// name: "Ensure stack is increased", +// // stx_liq_supply / threshold_25 == 1_000_000_000_000_000 / 20_000_000_000 = 50_000 + +// async fn(chain: Chain, accounts: Map) { +// const mainDelegateStx = (amountUstx: number, user: Account) => { +// return Tx.contractCall( +// "stacking-pool", +// "delegate-stx", +// [types.uint(amountUstx)], +// user.address +// ); +// }; +// const deployer = accounts.get("deployer")!; +// const wallet_1 = accounts.get("wallet_1")!; +// const wallet_2 = accounts.get("wallet_2")!; +// const mainContract = deployer.address + ".stacking-pool"; + +// // Allow pool SC in pox-2, join stacking pool and delegate with wallet_1 +// let block = chain.mineBlock([ +// allowContractCaller(mainContract, undefined, wallet_1), +// joinStackingPool(wallet_1), +// mainDelegateStx(125_000_000_000, wallet_1), // min increment stx = 125717355363 +// ]); +// block.receipts[0].result.expectOk().expectBool(true); +// block.receipts[1].result.expectOk().expectBool(true); +// block.receipts[2].result.expectOk().expectBool(true); +// console.log(block.receipts[2]); + +// // Check local user data +// block = chain.mineBlock([getUserData(wallet_1, deployer)]); + +// // verify delegated-balance==locked-balance==125_000_000_000 +// block.receipts[0].result +// .expectSome() +// .expectTuple() +// ["delegated-balance"].expectUint(125_000_000_000); +// block.receipts[0].result +// .expectSome() +// .expectTuple() +// ["locked-balance"].expectUint(125_000_000_000); + +// expectPartialStackedByCycle(poxAddrFP, 1, 0, chain, deployer); // does not commit partially +// expectTotalStackedByCycle(1, 0, 125_000_000_000, chain, deployer); // commits totally + +// // one cycle (== 2100) blocks pass +// for (let i = 1; i <= 4200; i++) { +// block = chain.mineBlock([]); // why 4198?? +// } + +// // increase stacked amount + +// block = chain.mineBlock([delegateStx(126_000_000_000, wallet_1)]); +// block.receipts[0].result.expectOk().expectBool(true); +// console.log(block.receipts[0]); + +// // Check local user data +// block = chain.mineBlock([getUserData(wallet_1, deployer)]); + +// // verify delegated-balance==locked-balance==126_000_000_000 until 2nd reward cycle's end (6300 block height) +// block.receipts[0].result +// .expectSome() +// .expectTuple() +// ["delegated-balance"].expectUint(126_000_000_000); +// block.receipts[0].result +// .expectSome() +// .expectTuple() +// ["locked-balance"].expectUint(126_000_000_000); +// block.receipts[0].result +// .expectSome() +// .expectTuple() +// ["until-burn-ht"].expectSome() +// .expectUint(6300); +// }, +// }); diff --git a/sbtc-ops/smart-contract/tests/utils.ts b/sbtc-ops/smart-contract/tests/utils.ts new file mode 100644 index 00000000..5fa47d59 --- /dev/null +++ b/sbtc-ops/smart-contract/tests/utils.ts @@ -0,0 +1,39 @@ +import { Account, Chain } from './deps.ts'; +import { getPartialStackedByCycle, getRewardSetPoxAddress } from './client/pox-2-client.ts'; +import { poxAddrPool1 } from './constants.ts'; + +export function expectPartialStackedByCycle( + poxAddr: { version: string; hashbytes: string }, + cycle: number, + amountUstx: number | undefined, + chain: Chain, + deployer: Account +) { + const result = getPartialStackedByCycle( + poxAddr, + cycle, + `${deployer.address}.pox-pool-self-service`, + chain, + deployer + ).result; + if (amountUstx) { + result.expectSome().expectTuple()['stacked-amount'].expectUint(amountUstx); + } else { + result.expectNone(); + } +} + +export function expectTotalStackedByCycle( + cycle: number, + index: number, + amountUstx: number, + chain: Chain, + user: Account +) { + const result = getRewardSetPoxAddress(cycle, index, chain, user).result; + if (amountUstx) { + result.expectSome().expectTuple()['total-ustx'].expectUint(amountUstx); + } else { + result.expectNone(); + } +} diff --git a/sbtc-ops/smart-contract/tsconfig.json b/sbtc-ops/smart-contract/tsconfig.json new file mode 100644 index 00000000..15b44249 --- /dev/null +++ b/sbtc-ops/smart-contract/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "types": ["vitest/globals"] + }, + "include": ["tests"] +} \ No newline at end of file diff --git a/sbtc-ops/smart-contract/vite.config.ts b/sbtc-ops/smart-contract/vite.config.ts new file mode 100644 index 00000000..1118b388 --- /dev/null +++ b/sbtc-ops/smart-contract/vite.config.ts @@ -0,0 +1,18 @@ +/// +import { defineConfig } from 'vitest/config'; +import GithubActionsReporter from 'vitest-github-actions-reporter'; + +export default defineConfig({ + test: { + globals: true, + testTimeout: 900_000, + hookTimeout: 150_000, + teardownTimeout: 15_000, + reporters: process.env.GITHUB_ACTIONS ? ['default', new GithubActionsReporter()] : 'default', + // minThreads: 2, + // maxThreads: 4, + // silent: true, + // maxConcurrency: 5, + // slowTestThreshold: 120_000; + }, +}); diff --git a/sbtc-ops/smart-contract/yarn.lock b/sbtc-ops/smart-contract/yarn.lock new file mode 100644 index 00000000..24343d95 --- /dev/null +++ b/sbtc-ops/smart-contract/yarn.lock @@ -0,0 +1,4250 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@actions/core@^1.10.0", "@actions/core@^1.2.0": + version "1.10.0" + resolved "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz" + integrity sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug== + dependencies: + "@actions/http-client" "^2.0.1" + uuid "^8.3.2" + +"@actions/http-client@^2.0.1": + version "2.1.0" + resolved "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz" + integrity sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw== + dependencies: + tunnel "^0.0.6" + +"@ampproject/remapping@^2.2.0": + version "2.2.0" + resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz" + integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== + dependencies: + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": + version "7.21.4" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz" + integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== + dependencies: + "@babel/highlight" "^7.18.6" + +"@babel/compat-data@^7.21.4": + version "7.21.4" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz" + integrity sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g== + +"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0", "@babel/core@>=7.0.0-beta.0 <8": + version "7.21.4" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz" + integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" + "@babel/helper-compilation-targets" "^7.21.4" + "@babel/helper-module-transforms" "^7.21.2" + "@babel/helpers" "^7.21.0" + "@babel/parser" "^7.21.4" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.4" + "@babel/types" "^7.21.4" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.0" + +"@babel/generator@^7.21.4", "@babel/generator@^7.7.2": + version "7.21.4" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz" + integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== + dependencies: + "@babel/types" "^7.21.4" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/helper-compilation-targets@^7.21.4": + version "7.21.4" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz" + integrity sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg== + dependencies: + "@babel/compat-data" "^7.21.4" + "@babel/helper-validator-option" "^7.21.0" + browserslist "^4.21.3" + lru-cache "^5.1.1" + semver "^6.3.0" + +"@babel/helper-environment-visitor@^7.18.9": + version "7.18.9" + resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz" + integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== + +"@babel/helper-function-name@^7.21.0": + version "7.21.0" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz" + integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== + dependencies: + "@babel/template" "^7.20.7" + "@babel/types" "^7.21.0" + +"@babel/helper-hoist-variables@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-module-imports@^7.18.6": + version "7.21.4" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz" + integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== + dependencies: + "@babel/types" "^7.21.4" + +"@babel/helper-module-transforms@^7.21.2": + version "7.21.2" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz" + integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.2" + "@babel/types" "^7.21.2" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0": + version "7.20.2" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz" + integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== + +"@babel/helper-simple-access@^7.20.2": + version "7.20.2" + resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz" + integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== + dependencies: + "@babel/types" "^7.20.2" + +"@babel/helper-split-export-declaration@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-string-parser@^7.19.4": + version "7.19.4" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz" + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== + +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": + version "7.19.1" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== + +"@babel/helper-validator-option@^7.21.0": + version "7.21.0" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz" + integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== + +"@babel/helpers@^7.21.0": + version "7.21.0" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz" + integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== + dependencies: + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.0" + "@babel/types" "^7.21.0" + +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4": + version "7.21.4" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz" + integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.8.3": + version "7.12.13" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.14.5" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.7.2": + version "7.21.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz" + integrity sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/template@^7.20.7", "@babel/template@^7.3.3": + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz" + integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + +"@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4", "@babel/traverse@^7.7.2": + version "7.21.4" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz" + integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== + dependencies: + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.21.4" + "@babel/types" "^7.21.4" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.21.4" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz" + integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + +"@esbuild/darwin-arm64@0.17.15": + version "0.17.15" + resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.15.tgz" + integrity sha512-7siLjBc88Z4+6qkMDxPT2juf2e8SJxmsbNVKFY2ifWCDT72v5YJz9arlvBw5oB4W/e61H1+HDB/jnu8nNg0rLA== + +"@hirosystems/chainhook-types@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@hirosystems/chainhook-types/-/chainhook-types-1.1.2.tgz" + integrity sha512-klQDKRyiPqF9fgDetMLTPMcB4A/+Vo7px74RbzzvmnCSDMPQua0DD5zhfuo12Ga3pexFFoIftIqnSjWvjncKNQ== + +"@hirosystems/stacks-devnet-js@^1.6.2": + version "1.7.0" + resolved "https://registry.npmjs.org/@hirosystems/stacks-devnet-js/-/stacks-devnet-js-1.7.0.tgz" + integrity sha512-F76e+4EEDzZxEuqzP4jSmbmL6/WtiTm5UH7TeAGgHEDA7SYEy+ZSMd/xvF2PNnwgBRXMFKWF+kZcYlwygNr3Mw== + dependencies: + "@hirosystems/chainhook-types" "^1.1.2" + "@mapbox/node-pre-gyp" "^1.0.8" + neon-cli "^0.9.1" + node-pre-gyp-github "^1.4.3" + typescript "^4.5.5" + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/console@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz" + integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== + dependencies: + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^27.5.1" + jest-util "^27.5.1" + slash "^3.0.0" + +"@jest/core@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz" + integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== + dependencies: + "@jest/console" "^27.5.1" + "@jest/reporters" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.8.1" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^27.5.1" + jest-config "^27.5.1" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-resolve-dependencies "^27.5.1" + jest-runner "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + jest-watcher "^27.5.1" + micromatch "^4.0.4" + rimraf "^3.0.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz" + integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA== + dependencies: + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" + +"@jest/fake-timers@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz" + integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ== + dependencies: + "@jest/types" "^27.5.1" + "@sinonjs/fake-timers" "^8.0.1" + "@types/node" "*" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-util "^27.5.1" + +"@jest/globals@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz" + integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/types" "^27.5.1" + expect "^27.5.1" + +"@jest/reporters@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz" + integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.2" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^5.1.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-haste-map "^27.5.1" + jest-resolve "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" + slash "^3.0.0" + source-map "^0.6.0" + string-length "^4.0.1" + terminal-link "^2.0.0" + v8-to-istanbul "^8.1.0" + +"@jest/schemas@^29.6.0": + version "29.6.0" + resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.0.tgz" + integrity sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ== + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/source-map@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz" + integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.2.9" + source-map "^0.6.0" + +"@jest/test-result@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz" + integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== + dependencies: + "@jest/console" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz" + integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== + dependencies: + "@jest/test-result" "^27.5.1" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-runtime "^27.5.1" + +"@jest/transform@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz" + integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^27.5.1" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-regex-util "^27.5.1" + jest-util "^27.5.1" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + source-map "^0.6.1" + write-file-atomic "^3.0.0" + +"@jest/types@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz" + integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^16.0.0" + chalk "^4.0.0" + +"@jridgewell/gen-mapping@^0.1.0": + version "0.1.1" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz" + integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.2" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@3.1.0": + version "3.1.0" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@1.4.14": + version "1.4.14" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/sourcemap-codec@^1.4.15": + version "1.4.15" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.17" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + +"@mapbox/node-pre-gyp@^1.0.8": + version "1.0.10" + resolved "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz" + integrity sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA== + dependencies: + detect-libc "^2.0.0" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.7" + nopt "^5.0.0" + npmlog "^5.0.1" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.11" + +"@noble/curves@~0.8.3": + version "0.8.3" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-0.8.3.tgz" + integrity sha512-OqaOf4RWDaCRuBKJLDURrgVxjLmneGsiCXGuzYB5y95YithZMA6w4uk34DHSm0rKMrrYiaeZj48/81EvaAScLQ== + dependencies: + "@noble/hashes" "1.3.0" + +"@noble/hashes@^1.1.2", "@noble/hashes@^1.1.5", "@noble/hashes@^1.2.0", "@noble/hashes@~1.3.0", "@noble/hashes@1.3.0": + version "1.3.0" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz" + integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== + +"@noble/hashes@~1.1.1", "@noble/hashes@1.1.5": + version "1.1.5" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.5.tgz" + integrity sha512-LTMZiiLc+V4v1Yi16TD6aX2gmtKszNye0pQgbaLqkvhIqP7nVsSaJsWloGQjJfJ8offaoP5GtX3yY5swbcJxxQ== + +"@noble/secp256k1@^1.7.0", "@noble/secp256k1@~1.7.0", "@noble/secp256k1@1.7.1": + version "1.7.1" + resolved "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz" + integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== + +"@octokit/auth-token@^2.4.4": + version "2.5.0" + resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz" + integrity sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g== + dependencies: + "@octokit/types" "^6.0.3" + +"@octokit/core@^3.5.1", "@octokit/core@>=2", "@octokit/core@>=3": + version "3.6.0" + resolved "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz" + integrity sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q== + dependencies: + "@octokit/auth-token" "^2.4.4" + "@octokit/graphql" "^4.5.8" + "@octokit/request" "^5.6.3" + "@octokit/request-error" "^2.0.5" + "@octokit/types" "^6.0.3" + before-after-hook "^2.2.0" + universal-user-agent "^6.0.0" + +"@octokit/endpoint@^6.0.1": + version "6.0.12" + resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz" + integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== + dependencies: + "@octokit/types" "^6.0.3" + is-plain-object "^5.0.0" + universal-user-agent "^6.0.0" + +"@octokit/graphql@^4.5.8": + version "4.8.0" + resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz" + integrity sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg== + dependencies: + "@octokit/request" "^5.6.0" + "@octokit/types" "^6.0.3" + universal-user-agent "^6.0.0" + +"@octokit/openapi-types@^12.11.0": + version "12.11.0" + resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz" + integrity sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ== + +"@octokit/plugin-paginate-rest@^2.16.8": + version "2.21.3" + resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz" + integrity sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw== + dependencies: + "@octokit/types" "^6.40.0" + +"@octokit/plugin-request-log@^1.0.4": + version "1.0.4" + resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz" + integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== + +"@octokit/plugin-rest-endpoint-methods@^5.12.0": + version "5.16.2" + resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz" + integrity sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw== + dependencies: + "@octokit/types" "^6.39.0" + deprecation "^2.3.1" + +"@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": + version "2.1.0" + resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz" + integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== + dependencies: + "@octokit/types" "^6.0.3" + deprecation "^2.0.0" + once "^1.4.0" + +"@octokit/request@^5.6.0", "@octokit/request@^5.6.3": + version "5.6.3" + resolved "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz" + integrity sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A== + dependencies: + "@octokit/endpoint" "^6.0.1" + "@octokit/request-error" "^2.1.0" + "@octokit/types" "^6.16.1" + is-plain-object "^5.0.0" + node-fetch "^2.6.7" + universal-user-agent "^6.0.0" + +"@octokit/rest@18.12.0": + version "18.12.0" + resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz" + integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== + dependencies: + "@octokit/core" "^3.5.1" + "@octokit/plugin-paginate-rest" "^2.16.8" + "@octokit/plugin-request-log" "^1.0.4" + "@octokit/plugin-rest-endpoint-methods" "^5.12.0" + +"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.39.0", "@octokit/types@^6.40.0": + version "6.41.0" + resolved "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz" + integrity sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg== + dependencies: + "@octokit/openapi-types" "^12.11.0" + +"@polka/url@^1.0.0-next.20": + version "1.0.0-next.21" + resolved "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz" + integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== + +"@scure/base@^1.1.1", "@scure/base@~1.1.0", "@scure/base@~1.1.1", "@scure/base@1.1.1": + version "1.1.1" + resolved "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz" + integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== + +"@scure/bip32@^1.1.1": + version "1.2.0" + resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.2.0.tgz" + integrity sha512-O+vT/hBVk+ag2i6j2CDemwd1E1MtGt+7O1KzrPNsaNvSsiEK55MyPIxJIMI2PS8Ijj464B2VbQlpRoQXxw1uHg== + dependencies: + "@noble/curves" "~0.8.3" + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + +"@scure/bip39@^1.1.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.0.tgz" + integrity sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg== + dependencies: + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + +"@scure/bip39@1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz" + integrity sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w== + dependencies: + "@noble/hashes" "~1.1.1" + "@scure/base" "~1.1.0" + +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@sinonjs/commons@^1.7.0": + version "1.8.6" + resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz" + integrity sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^8.0.1": + version "8.1.0" + resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz" + integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@stacks/common@^6.0.0": + version "6.0.0" + resolved "https://registry.npmjs.org/@stacks/common/-/common-6.0.0.tgz" + integrity sha512-tETwccvbYvaZ7u3ZucWNMOIPN97r6IPeZXKIFhLc1KSVaWSGEPTtZcwVp+Rz3mu2XgI2pg37SUrOWXSL7OOkDw== + dependencies: + "@types/bn.js" "^5.1.0" + "@types/node" "^18.0.4" + +"@stacks/encryption@^6.5.0": + version "6.5.0" + resolved "https://registry.npmjs.org/@stacks/encryption/-/encryption-6.5.0.tgz" + integrity sha512-QE1+gy1x6spGkpK5PnZxKoX1hL8eeIYxYa5HNMl4cbdIVKaFgqjoGFKMtTA/tQMc91T/saXLqbQLyh/U4AVpTA== + dependencies: + "@noble/hashes" "1.1.5" + "@noble/secp256k1" "1.7.1" + "@scure/bip39" "1.1.0" + "@stacks/common" "^6.0.0" + "@types/node" "^18.0.4" + base64-js "^1.5.1" + bs58 "^5.0.0" + ripemd160-min "^0.0.6" + varuint-bitcoin "^1.1.2" + +"@stacks/network@^6.0.0", "@stacks/network@^6.3.0": + version "6.3.0" + resolved "https://registry.npmjs.org/@stacks/network/-/network-6.3.0.tgz" + integrity sha512-573ZldQ+Iy0nCCxprXLLvkAo1AMEXncfmMUvqQ+5TN3m7VqCVADtb5G5WzMZsyR4m/k9oPsv076Lmqyl8AtR2A== + dependencies: + "@stacks/common" "^6.0.0" + cross-fetch "^3.1.5" + +"@stacks/stacking@^6.0.2": + version "6.5.0" + resolved "https://registry.npmjs.org/@stacks/stacking/-/stacking-6.5.0.tgz" + integrity sha512-Kb8FK+NxEFDXP2r9xXGdeR7cZp52Cz5btpW4xKAEM7Ze3QPB6cM8IALvMBh3e53qqw8sO/dpRqcIjrNgLf5gqg== + dependencies: + "@scure/base" "1.1.1" + "@stacks/common" "^6.0.0" + "@stacks/encryption" "^6.5.0" + "@stacks/network" "^6.3.0" + "@stacks/stacks-blockchain-api-types" "^0.61.0" + "@stacks/transactions" "^6.5.0" + bs58 "^5.0.0" + +"@stacks/stacks-blockchain-api-types@^0.61.0": + version "0.61.0" + resolved "https://registry.npmjs.org/@stacks/stacks-blockchain-api-types/-/stacks-blockchain-api-types-0.61.0.tgz" + integrity sha512-yPOfTUboo5eA9BZL/hqMcM71GstrFs9YWzOrJFPeP4cOO1wgYvAcckgBRbgiE3NqeX0A7SLZLDAXLZbATuRq9w== + +"@stacks/transactions@^6.5.0": + version "6.5.0" + resolved "https://registry.npmjs.org/@stacks/transactions/-/transactions-6.5.0.tgz" + integrity sha512-kwE8cZq+QdAum4/LC+lSlAXVvzkdsSHTkCbfg4+VCWPBqA+gdXEqZe6R9SNBtMb8yGQrqUY8uIGRLVCWcXJ8zQ== + dependencies: + "@noble/hashes" "1.1.5" + "@noble/secp256k1" "1.7.1" + "@stacks/common" "^6.0.0" + "@stacks/network" "^6.3.0" + c32check "^2.0.0" + lodash.clonedeep "^4.5.0" + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": + version "7.20.0" + resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz" + integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.4" + resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz" + integrity sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.1" + resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz" + integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": + version "7.18.3" + resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz" + integrity sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w== + dependencies: + "@babel/types" "^7.3.0" + +"@types/bn.js@^5.1.0": + version "5.1.1" + resolved "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz" + integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g== + dependencies: + "@types/node" "*" + +"@types/chai-subset@^1.3.3": + version "1.3.3" + resolved "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.3.tgz" + integrity sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw== + dependencies: + "@types/chai" "*" + +"@types/chai@*", "@types/chai@^4.3.5": + version "4.3.5" + resolved "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz" + integrity sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng== + +"@types/graceful-fs@^4.1.2": + version "4.1.6" + resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz" + integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.4" + resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz" + integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.1" + resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@^27.0.0", "@types/jest@^27.5.2": + version "27.5.2" + resolved "https://registry.npmjs.org/@types/jest/-/jest-27.5.2.tgz" + integrity sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA== + dependencies: + jest-matcher-utils "^27.0.0" + pretty-format "^27.0.0" + +"@types/node@*", "@types/node@^16.7.13", "@types/node@>= 14": + version "16.18.23" + resolved "https://registry.npmjs.org/@types/node/-/node-16.18.23.tgz" + integrity sha512-XAMpaw1s1+6zM+jn2tmw8MyaRDIJfXxqmIQIS0HfoGYPuf7dUWeiUKopwq13KFX9lEp1+THGtlaaYx39Nxr58g== + +"@types/node@^18.0.4": + version "18.15.11" + resolved "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz" + integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== + +"@types/prettier@^2.1.5": + version "2.7.2" + resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz" + integrity sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg== + +"@types/stack-utils@^2.0.0": + version "2.0.1" + resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz" + integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== + +"@types/yargs-parser@*": + version "21.0.0" + resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz" + integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== + +"@types/yargs@^16.0.0": + version "16.0.5" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz" + integrity sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ== + dependencies: + "@types/yargs-parser" "*" + +"@vitest/expect@0.32.4": + version "0.32.4" + resolved "https://registry.npmjs.org/@vitest/expect/-/expect-0.32.4.tgz" + integrity sha512-m7EPUqmGIwIeoU763N+ivkFjTzbaBn0n9evsTOcde03ugy2avPs3kZbYmw3DkcH1j5mxhMhdamJkLQ6dM1bk/A== + dependencies: + "@vitest/spy" "0.32.4" + "@vitest/utils" "0.32.4" + chai "^4.3.7" + +"@vitest/runner@0.32.4": + version "0.32.4" + resolved "https://registry.npmjs.org/@vitest/runner/-/runner-0.32.4.tgz" + integrity sha512-cHOVCkiRazobgdKLnczmz2oaKK9GJOw6ZyRcaPdssO1ej+wzHVIkWiCiNacb3TTYPdzMddYkCgMjZ4r8C0JFCw== + dependencies: + "@vitest/utils" "0.32.4" + p-limit "^4.0.0" + pathe "^1.1.1" + +"@vitest/snapshot@0.32.4": + version "0.32.4" + resolved "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.32.4.tgz" + integrity sha512-IRpyqn9t14uqsFlVI2d7DFMImGMs1Q9218of40bdQQgMePwVdmix33yMNnebXcTzDU5eiV3eUsoxxH5v0x/IQA== + dependencies: + magic-string "^0.30.0" + pathe "^1.1.1" + pretty-format "^29.5.0" + +"@vitest/spy@0.32.4": + version "0.32.4" + resolved "https://registry.npmjs.org/@vitest/spy/-/spy-0.32.4.tgz" + integrity sha512-oA7rCOqVOOpE6rEoXuCOADX7Lla1LIa4hljI2MSccbpec54q+oifhziZIJXxlE/CvI2E+ElhBHzVu0VEvJGQKQ== + dependencies: + tinyspy "^2.1.1" + +"@vitest/ui@*", "@vitest/ui@^0.25.8": + version "0.25.8" + resolved "https://registry.npmjs.org/@vitest/ui/-/ui-0.25.8.tgz" + integrity sha512-wfuhghldD5QHLYpS46GK8Ru8P3XcMrWvFjRQD21KNzc9Y/qtJsqoC8KmT6xWVkMNw4oHYixpo3a4ZySRJdserw== + dependencies: + sirv "^2.0.2" + +"@vitest/utils@0.32.4": + version "0.32.4" + resolved "https://registry.npmjs.org/@vitest/utils/-/utils-0.32.4.tgz" + integrity sha512-Gwnl8dhd1uJ+HXrYyV0eRqfmk9ek1ASE/LWfTCuWMw+d07ogHqp4hEAV28NiecimK6UY9DpSEPh+pXBA5gtTBg== + dependencies: + diff-sequences "^29.4.3" + loupe "^2.3.6" + pretty-format "^29.5.0" + +abab@^2.0.3, abab@^2.0.5: + version "2.0.6" + resolved "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== + +abbrev@1: + version "1.1.1" + resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +acorn-globals@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz" + integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== + dependencies: + acorn "^7.1.1" + acorn-walk "^7.1.1" + +acorn-walk@^7.1.1: + version "7.2.0" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + +acorn-walk@^8.2.0: + version "8.2.0" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +acorn@^8.2.4, acorn@^8.8.2, acorn@^8.9.0: + version "8.10.0" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== + +agent-base@6: + version "6.0.2" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +anymatch@^3.0.3, anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +"aproba@^1.0.3 || ^2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + +are-we-there-yet@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz" + integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-back@^3.0.1, array-back@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz" + integrity sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q== + +array-back@^4.0.1: + version "4.0.2" + resolved "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz" + integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== + +array-back@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz" + integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +babel-jest@^27.5.1, "babel-jest@>=27.0.0 <28": + version "27.5.1" + resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz" + integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== + dependencies: + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^27.5.1" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + +babel-plugin-istanbul@^6.1.1: + version "6.1.1" + resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^5.0.4" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz" + integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" + "@types/babel__traverse" "^7.0.6" + +babel-preset-current-node-syntax@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz" + integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + +babel-preset-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz" + integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== + dependencies: + babel-plugin-jest-hoist "^27.5.1" + babel-preset-current-node-syntax "^1.0.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base-x@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz" + integrity sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw== + +base64-js@^1.3.1, base64-js@^1.5.1: + version "1.5.1" + resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bech32@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz" + integrity sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg== + +before-after-hook@^2.2.0: + version "2.2.3" + resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz" + integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +bip174@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/bip174/-/bip174-2.1.0.tgz" + integrity sha512-lkc0XyiX9E9KiVAS1ZiOqK1xfiwvf4FXDDdkDq5crcDzOq+xGytY+14qCsqz7kCiy8rpN1CRNfacRhf9G3JNSA== + +bitcoinjs-lib@^6.1.3: + version "6.1.3" + resolved "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.1.3.tgz" + integrity sha512-TYXs/Qf+GNk2nnsB9HrXWqzFuEgCg0Gx+v3UW3B8VuceFHXVvhT+7hRnTSvwkX0i8rz2rtujeU6gFaDcFqYFDw== + dependencies: + "@noble/hashes" "^1.2.0" + bech32 "^2.0.0" + bip174 "^2.1.0" + bs58check "^3.0.1" + typeforce "^1.11.3" + varuint-bitcoin "^1.1.2" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +browserslist@^4.21.3, "browserslist@>= 4.21.0": + version "4.21.5" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz" + integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== + dependencies: + caniuse-lite "^1.0.30001449" + electron-to-chromium "^1.4.284" + node-releases "^2.0.8" + update-browserslist-db "^1.0.10" + +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + +bs58@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz" + integrity sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ== + dependencies: + base-x "^4.0.0" + +bs58check@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/bs58check/-/bs58check-3.0.1.tgz" + integrity sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ== + dependencies: + "@noble/hashes" "^1.2.0" + bs58 "^5.0.0" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +builtins@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz" + integrity sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ== + +c32check@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/c32check/-/c32check-2.0.0.tgz" + integrity sha512-rpwfAcS/CMqo0oCqDf3r9eeLgScRE3l/xHDCXhM3UyrfvIn7PrLq63uHh7yYbv8NzaZn5MVsVhIRpQ+5GZ5HyA== + dependencies: + "@noble/hashes" "^1.1.2" + base-x "^4.0.0" + +cac@^6.7.14: + version "6.7.14" + resolved "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.0.0: + version "6.3.0" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001449: + version "1.0.30001474" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001474.tgz" + integrity sha512-iaIZ8gVrWfemh5DG3T9/YqarVZoYf0r188IjaGwx68j4Pf0SGY6CQkmJUIE+NZHkkecQGohzXmBGEwWDr9aM3Q== + +chai@^4.3.7: + version "4.3.7" + resolved "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz" + integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^4.1.2" + get-func-name "^2.0.0" + loupe "^2.3.1" + pathval "^1.1.1" + type-detect "^4.0.5" + +chalk@^2.0.0: + version "2.4.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz" + integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== + +chokidar@3.5.3: + version "3.5.3" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +ci-info@^3.2.0: + version "3.8.0" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz" + integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== + +cjs-module-lexer@^1.0.0: + version "1.2.2" + resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz" + integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz" + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== + +collect-v8-coverage@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz" + integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-support@^1.1.2: + version "1.1.3" + resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +command-line-args@^5.1.1: + version "5.2.1" + resolved "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz" + integrity sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg== + dependencies: + array-back "^3.1.0" + find-replace "^3.0.0" + lodash.camelcase "^4.3.0" + typical "^4.0.0" + +command-line-commands@^3.0.1: + version "3.0.2" + resolved "https://registry.npmjs.org/command-line-commands/-/command-line-commands-3.0.2.tgz" + integrity sha512-ac6PdCtdR6q7S3HN+JiVLIWGHY30PRYIEl2qPo+FuEuzwAUk0UYyimrngrg7FvF/mCr4Jgoqv5ZnHZgads50rw== + dependencies: + array-back "^4.0.1" + +command-line-usage@^6.1.0: + version "6.1.3" + resolved "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.3.tgz" + integrity sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw== + dependencies: + array-back "^4.0.2" + chalk "^2.4.2" + table-layout "^1.0.2" + typical "^5.2.0" + +commander@7.2.0: + version "7.2.0" + resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +console-control-strings@^1.0.0, console-control-strings@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" + integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== + +convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: + version "1.9.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +cross-fetch@^3.1.5: + version "3.1.5" + resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz" + integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== + dependencies: + node-fetch "2.6.7" + +cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cssom@^0.4.4: + version "0.4.4" + resolved "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz" + integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" + +data-urls@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz" + integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== + dependencies: + abab "^2.0.3" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.0.0" + +debug@^4.1.0, debug@^4.1.1, debug@^4.3.4, debug@4, debug@4.3.4: + version "4.3.4" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + +decimal.js@^10.2.1: + version "10.4.3" + resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== + +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz" + integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== + +deep-eql@^4.1.2: + version "4.1.3" + resolved "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz" + integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== + dependencies: + type-detect "^4.0.0" + +deep-extend@~0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deep-is@~0.1.3: + version "0.1.4" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== + +deprecation@^2.0.0, deprecation@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz" + integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== + +detect-libc@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz" + integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== + +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + +diff-sequences@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz" + integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== + +diff-sequences@^29.4.3: + version "29.4.3" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz" + integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== + +diff@5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz" + integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== + +domexception@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz" + integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== + dependencies: + webidl-conversions "^5.0.0" + +electron-to-chromium@^1.4.284: + version "1.4.352" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.352.tgz" + integrity sha512-ikFUEyu5/q+wJpMOxWxTaEVk2M1qKqTGKKyfJmod1CPZxKfYnxVS41/GCBQg21ItBpZybyN8sNpRqCUGm+Zc4Q== + +emittery@^0.8.1: + version "0.8.1" + resolved "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz" + integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +esbuild@^0.17.5: + version "0.17.15" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.17.15.tgz" + integrity sha512-LBUV2VsUIc/iD9ME75qhT4aJj0r75abCVS0jakhFzOtR7TQsqQA5w0tZ+KTKnwl3kXE0MhskNdHDh/I5aCR1Zw== + optionalDependencies: + "@esbuild/android-arm" "0.17.15" + "@esbuild/android-arm64" "0.17.15" + "@esbuild/android-x64" "0.17.15" + "@esbuild/darwin-arm64" "0.17.15" + "@esbuild/darwin-x64" "0.17.15" + "@esbuild/freebsd-arm64" "0.17.15" + "@esbuild/freebsd-x64" "0.17.15" + "@esbuild/linux-arm" "0.17.15" + "@esbuild/linux-arm64" "0.17.15" + "@esbuild/linux-ia32" "0.17.15" + "@esbuild/linux-loong64" "0.17.15" + "@esbuild/linux-mips64el" "0.17.15" + "@esbuild/linux-ppc64" "0.17.15" + "@esbuild/linux-riscv64" "0.17.15" + "@esbuild/linux-s390x" "0.17.15" + "@esbuild/linux-x64" "0.17.15" + "@esbuild/netbsd-x64" "0.17.15" + "@esbuild/openbsd-x64" "0.17.15" + "@esbuild/sunos-x64" "0.17.15" + "@esbuild/win32-arm64" "0.17.15" + "@esbuild/win32-ia32" "0.17.15" + "@esbuild/win32-x64" "0.17.15" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +escodegen@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz" + integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== + +expect@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz" + integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== + dependencies: + "@jest/types" "^27.5.1" + jest-get-type "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@2.x: + version "2.1.0" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fb-watchman@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== + dependencies: + bser "2.1.1" + +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-replace@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz" + integrity sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ== + dependencies: + array-back "^3.0.1" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@^2.3.2, fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +gauge@^3.0.0: + version "3.0.2" + resolved "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz" + integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.2" + console-control-strings "^1.0.0" + has-unicode "^2.0.1" + object-assign "^4.1.1" + signal-exit "^3.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.2" + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz" + integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +git-config@0.0.7: + version "0.0.7" + resolved "https://registry.npmjs.org/git-config/-/git-config-0.0.7.tgz" + integrity sha512-LidZlYZXWzVjS+M3TEwhtYBaYwLeOZrXci1tBgqp/vDdZTBMl02atvwb6G35L64ibscYoPnxfbwwUS+VZAISLA== + dependencies: + iniparser "~1.0.5" + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: + version "7.2.3" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@7.2.0: + version "7.2.0" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +handlebars@^4.7.6: + version "4.7.7" + resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz" + integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.0" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" + integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +html-encoding-sniffer@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz" + integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== + dependencies: + whatwg-encoding "^1.0.5" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +iconv-lite@^0.4.24, iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@^2.0.3, inherits@2: + version "2.0.4" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +iniparser@~1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/iniparser/-/iniparser-1.0.5.tgz" + integrity sha512-i40MWqgTU6h/70NtMsDVVDLjDYWwcIR1yIEVDPfxZIJno9z9L4s83p/V7vAu2i48Vj0gpByrkGFub7ko9XvPrw== + +inquirer@^7.3.3: + version "7.3.3" + resolved "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.19" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.6.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-core-module@^2.9.0: + version "2.11.0" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== + dependencies: + has "^1.0.3" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz" + integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== + +istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: + version "5.2.1" + resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^6.3.0" + +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.1.3: + version "3.1.5" + resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz" + integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jest-changed-files@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz" + integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== + dependencies: + "@jest/types" "^27.5.1" + execa "^5.0.0" + throat "^6.0.1" + +jest-circus@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz" + integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + expect "^27.5.1" + is-generator-fn "^2.0.0" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + slash "^3.0.0" + stack-utils "^2.0.3" + throat "^6.0.1" + +jest-cli@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz" + integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== + dependencies: + "@jest/core" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + import-local "^3.0.2" + jest-config "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + prompts "^2.0.1" + yargs "^16.2.0" + +jest-config@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz" + integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== + dependencies: + "@babel/core" "^7.8.0" + "@jest/test-sequencer" "^27.5.1" + "@jest/types" "^27.5.1" + babel-jest "^27.5.1" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.1" + graceful-fs "^4.2.9" + jest-circus "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-get-type "^27.5.1" + jest-jasmine2 "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runner "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^27.5.1" + slash "^3.0.0" + strip-json-comments "^3.1.1" + +jest-diff@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz" + integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== + dependencies: + chalk "^4.0.0" + diff-sequences "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + +jest-docblock@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz" + integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== + dependencies: + detect-newline "^3.0.0" + +jest-each@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz" + integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ== + dependencies: + "@jest/types" "^27.5.1" + chalk "^4.0.0" + jest-get-type "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + +jest-environment-jsdom@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz" + integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" + jest-util "^27.5.1" + jsdom "^16.6.0" + +jest-environment-node@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz" + integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" + jest-util "^27.5.1" + +jest-get-type@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz" + integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== + +jest-github-actions-reporter@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/jest-github-actions-reporter/-/jest-github-actions-reporter-1.0.3.tgz" + integrity sha512-IwLAKLSWLN8ZVfcfEEv6rfeWb78wKDeOhvOmH9KKXayKsKLSCwceopBcB+KUtwxfB5wYnT8Y9s2eZ+WdhA5yng== + dependencies: + "@actions/core" "^1.2.0" + +jest-haste-map@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz" + integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== + dependencies: + "@jest/types" "^27.5.1" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^27.5.1" + jest-serializer "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" + micromatch "^4.0.4" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.3.2" + +jest-jasmine2@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz" + integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + expect "^27.5.1" + is-generator-fn "^2.0.0" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + throat "^6.0.1" + +jest-leak-detector@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz" + integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ== + dependencies: + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + +jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz" + integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== + dependencies: + chalk "^4.0.0" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + +jest-message-util@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz" + integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^27.5.1" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^27.5.1" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz" + integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== + dependencies: + "@jest/types" "^27.5.1" + "@types/node" "*" + +jest-pnp-resolver@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== + +jest-regex-util@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz" + integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== + +jest-resolve-dependencies@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz" + integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg== + dependencies: + "@jest/types" "^27.5.1" + jest-regex-util "^27.5.1" + jest-snapshot "^27.5.1" + +jest-resolve@*, jest-resolve@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz" + integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== + dependencies: + "@jest/types" "^27.5.1" + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-pnp-resolver "^1.2.2" + jest-util "^27.5.1" + jest-validate "^27.5.1" + resolve "^1.20.0" + resolve.exports "^1.1.0" + slash "^3.0.0" + +jest-runner@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz" + integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ== + dependencies: + "@jest/console" "^27.5.1" + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.8.1" + graceful-fs "^4.2.9" + jest-docblock "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-haste-map "^27.5.1" + jest-leak-detector "^27.5.1" + jest-message-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runtime "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" + source-map-support "^0.5.6" + throat "^6.0.1" + +jest-runtime@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz" + integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/globals" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + execa "^5.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + slash "^3.0.0" + strip-bom "^4.0.0" + +jest-serializer@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz" + integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== + dependencies: + "@types/node" "*" + graceful-fs "^4.2.9" + +jest-snapshot@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz" + integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA== + dependencies: + "@babel/core" "^7.7.2" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" + "@babel/types" "^7.0.0" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/babel__traverse" "^7.0.4" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^27.5.1" + graceful-fs "^4.2.9" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + jest-haste-map "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-util "^27.5.1" + natural-compare "^1.4.0" + pretty-format "^27.5.1" + semver "^7.3.2" + +jest-util@^27.0.0, jest-util@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz" + integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== + dependencies: + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-validate@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz" + integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== + dependencies: + "@jest/types" "^27.5.1" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^27.5.1" + leven "^3.1.0" + pretty-format "^27.5.1" + +jest-watcher@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz" + integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw== + dependencies: + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + jest-util "^27.5.1" + string-length "^4.0.1" + +jest-worker@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@^27.0.0, jest@^27.4.5: + version "27.5.1" + resolved "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz" + integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== + dependencies: + "@jest/core" "^27.5.1" + import-local "^3.0.2" + jest-cli "^27.5.1" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsdom@*, jsdom@^16.6.0: + version "16.7.0" + resolved "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz" + integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== + dependencies: + abab "^2.0.5" + acorn "^8.2.4" + acorn-globals "^6.0.0" + cssom "^0.4.4" + cssstyle "^2.3.0" + data-urls "^2.0.0" + decimal.js "^10.2.1" + domexception "^2.0.1" + escodegen "^2.0.0" + form-data "^3.0.0" + html-encoding-sniffer "^2.0.1" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.0" + parse5 "6.0.1" + saxes "^5.0.1" + symbol-tree "^3.2.4" + tough-cookie "^4.0.0" + w3c-hr-time "^1.0.2" + w3c-xmlserializer "^2.0.0" + webidl-conversions "^6.1.0" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.5.0" + ws "^7.4.6" + xml-name-validator "^3.0.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json5@^2.2.2, json5@2.x: + version "2.2.3" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonc-parser@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz" + integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz" + integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +local-pkg@^0.4.3: + version "0.4.3" + resolved "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz" + integrity sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== + +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz" + integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== + +lodash.memoize@4.x: + version "4.1.2" + resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + +lodash@^4.17.19, lodash@^4.7.0: + version "4.17.21" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +loupe@^2.3.1, loupe@^2.3.6: + version "2.3.6" + resolved "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz" + integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== + dependencies: + get-func-name "^2.0.0" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +magic-string@^0.30.0: + version "0.30.1" + resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz" + integrity sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.15" + +make-dir@^3.0.0, make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +make-error@1.x: + version "1.3.6" + resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +make-promises-safe@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/make-promises-safe/-/make-promises-safe-5.1.0.tgz" + integrity sha512-AfdZ49rtyhQR/6cqVKGoH7y4ql7XkS5HJI1lZm0/5N6CQosy1eYbBJ/qbhkKHzo17UH7M918Bysf6XB9f3kS1g== + +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== + dependencies: + tmpl "1.0.5" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +micro-btc-signer@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/micro-btc-signer/-/micro-btc-signer-0.2.0.tgz" + integrity sha512-Rho4MgGnDoEt/nHKHc86+nNCU2xUu+u1XIn4+Qy3e2QeJ2FILr8f/EU80I3QDavJBGvcByrsG6qICuV178HmVg== + dependencies: + "@noble/hashes" "~1.1.1" + "@noble/secp256k1" "~1.7.0" + "@scure/base" "~1.1.0" + micro-packed "~0.3.0" + +micro-packed@~0.3.0: + version "0.3.2" + resolved "https://registry.npmjs.org/micro-packed/-/micro-packed-0.3.2.tgz" + integrity sha512-D1Bq0/lVOzdxhnX5vylCxZpdw5LylH7Vd81py0DfRsKUP36XYpwvy8ZIsECVo3UfnoROn8pdKqkOzL7Cd82sGA== + dependencies: + "@scure/base" "~1.1.1" + +micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimatch@^3.0.4, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.5: + version "1.2.8" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +minipass@^3.0.0: + version "3.3.6" + resolved "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^4.0.0: + version "4.2.5" + resolved "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz" + integrity sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q== + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mlly@^1.2.0, mlly@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/mlly/-/mlly-1.4.0.tgz" + integrity sha512-ua8PAThnTwpprIaU47EPeZ/bPUVp2QYBbWMphUQpVdBI3Lgqzm5KZQ45Agm3YJedHXaIHl6pBGabaLSUPPSptg== + dependencies: + acorn "^8.9.0" + pathe "^1.1.1" + pkg-types "^1.0.3" + ufo "^1.1.2" + +mocha@^10.2.0: + version "10.2.0" + resolved "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz" + integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== + dependencies: + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.3" + debug "4.3.4" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.2.0" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "5.0.1" + ms "2.1.3" + nanoid "3.3.3" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + workerpool "6.2.1" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + +mrmime@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz" + integrity sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +nanoid@^3.3.4: + version "3.3.6" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz" + integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== + +nanoid@3.3.3: + version "3.3.3" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz" + integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +neo-async@^2.6.0: + version "2.6.2" + resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +neon-cli@^0.9.1: + version "0.9.1" + resolved "https://registry.npmjs.org/neon-cli/-/neon-cli-0.9.1.tgz" + integrity sha512-iWwKjI1Q4o1lWVc4vkr59BXEYlBl5HggKx5ETw5kStpuvT1nVcolPtTdydisx8pLRRxN+gjER571DOGnA0Yxcg== + dependencies: + chalk "^4.1.0" + command-line-args "^5.1.1" + command-line-commands "^3.0.1" + command-line-usage "^6.1.0" + git-config "0.0.7" + handlebars "^4.7.6" + inquirer "^7.3.3" + make-promises-safe "^5.1.0" + rimraf "^3.0.2" + semver "^7.3.2" + toml "^3.0.0" + ts-typed-json "^0.3.2" + validate-npm-package-license "^3.0.4" + validate-npm-package-name "^3.0.0" + +node-fetch@^2.6.7: + version "2.6.9" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz" + integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== + dependencies: + whatwg-url "^5.0.0" + +node-fetch@2.6.7: + version "2.6.7" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== + +node-pre-gyp-github@^1.4.3: + version "1.4.4" + resolved "https://registry.npmjs.org/node-pre-gyp-github/-/node-pre-gyp-github-1.4.4.tgz" + integrity sha512-oE9JD1aXRi4+1jSH7Q+ybEhfujW5bJ66n4YMGpaUp/k2/X/8i09ouK1seznf3wOagcKjytRJCkf71DdEJx2zhA== + dependencies: + "@octokit/rest" "18.12.0" + commander "7.2.0" + +node-releases@^2.0.8: + version "2.0.10" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz" + integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== + +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +npmlog@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz" + integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== + dependencies: + are-we-there-yet "^2.0.0" + console-control-strings "^1.1.0" + gauge "^3.0.0" + set-blocking "^2.0.0" + +nwsapi@^2.2.0: + version "2.2.2" + resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz" + integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== + +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +once@^1.3.0, once@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.0, onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-limit@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz" + integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ== + dependencies: + yocto-queue "^1.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse5@6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +pathe@^1.1.0, pathe@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/pathe/-/pathe-1.1.1.tgz" + integrity sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q== + +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pirates@^4.0.4: + version "4.0.5" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz" + integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +pkg-types@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz" + integrity sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A== + dependencies: + jsonc-parser "^3.2.0" + mlly "^1.2.0" + pathe "^1.1.0" + +postcss@^8.4.21: + version "8.4.21" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz" + integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== + dependencies: + nanoid "^3.3.4" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" + integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== + +prettier@2.8.1: + version "2.8.1" + resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.1.tgz" + integrity sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg== + +pretty-format@^27.0.0, pretty-format@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + +pretty-format@^29.5.0: + version "29.6.1" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.1.tgz" + integrity sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog== + dependencies: + "@jest/schemas" "^29.6.0" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +prompts@^2.0.1: + version "2.4.2" + resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +psl@^1.1.33: + version "1.9.0" + resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== + +punycode@^2.1.1: + version "2.3.0" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + +readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +reduce-flatten@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz" + integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve.exports@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz" + integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== + +resolve@^1.20.0, resolve@^1.22.1: + version "1.22.1" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +ripemd160-min@^0.0.6: + version "0.0.6" + resolved "https://registry.npmjs.org/ripemd160-min/-/ripemd160-min-0.0.6.tgz" + integrity sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A== + +rollup@^3.18.0: + version "3.20.2" + resolved "https://registry.npmjs.org/rollup/-/rollup-3.20.2.tgz" + integrity sha512-3zwkBQl7Ai7MFYQE0y1MeQ15+9jsi7XxfrqwTb/9EK8D9C9+//EBR4M+CuA1KODRaNbFez/lWxA5vhEGZp4MUg== + optionalDependencies: + fsevents "~2.3.2" + +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +rxjs@^6.6.0: + version "6.6.7" + resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + +safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +saxes@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz" + integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== + dependencies: + xmlchars "^2.2.0" + +semver@^6.0.0: + version "6.3.0" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^6.3.0: + version "6.3.0" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.2, semver@^7.3.5, semver@7.x: + version "7.3.8" + resolved "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== + dependencies: + lru-cache "^6.0.0" + +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +siginfo@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz" + integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== + +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +sirv@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/sirv/-/sirv-2.0.2.tgz" + integrity sha512-4Qog6aE29nIjAOKe/wowFTxOdmbEZKb+3tsLljaBRzJwtqto0BChD2zzH0LhgCSXiI+V7X+Y45v14wBZQ1TK3w== + dependencies: + "@polka/url" "^1.0.0-next.20" + mrmime "^1.0.0" + totalist "^3.0.0" + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map-support@^0.5.6: + version "0.5.21" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.3: + version "0.7.4" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + +spdx-correct@^3.0.0: + version "3.2.0" + resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.13" + resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz" + integrity sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stack-utils@^2.0.3: + version "2.0.6" + resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== + dependencies: + escape-string-regexp "^2.0.0" + +stackback@0.0.2: + version "0.0.2" + resolved "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz" + integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== + +std-env@^3.3.3: + version "3.3.3" + resolved "https://registry.npmjs.org/std-env/-/std-env-3.3.3.tgz" + integrity sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg== + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== + dependencies: + char-regex "^1.0.2" + strip-ansi "^6.0.0" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-json-comments@^3.1.1, strip-json-comments@3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +strip-literal@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/strip-literal/-/strip-literal-1.0.1.tgz" + integrity sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q== + dependencies: + acorn "^8.8.2" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-color@8.1.1: + version "8.1.1" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-hyperlinks@^2.0.0: + version "2.3.0" + resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz" + integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +table-layout@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz" + integrity sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A== + dependencies: + array-back "^4.0.1" + deep-extend "~0.6.0" + typical "^5.2.0" + wordwrapjs "^4.0.0" + +tar@^6.1.11: + version "6.1.13" + resolved "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz" + integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^4.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +terminal-link@^2.0.0: + version "2.1.1" + resolved "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz" + integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== + dependencies: + ansi-escapes "^4.2.1" + supports-hyperlinks "^2.0.0" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +throat@^6.0.1: + version "6.0.2" + resolved "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz" + integrity sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ== + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +tinybench@^2.5.0: + version "2.5.0" + resolved "https://registry.npmjs.org/tinybench/-/tinybench-2.5.0.tgz" + integrity sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA== + +tinypool@^0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/tinypool/-/tinypool-0.5.0.tgz" + integrity sha512-paHQtnrlS1QZYKF/GnLoOM/DN9fqaGOFbCbxzAhwniySnzl9Ebk8w73/dd34DAhe/obUbPAOldTyYXQZxnPBPQ== + +tinyspy@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/tinyspy/-/tinyspy-2.1.1.tgz" + integrity sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w== + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +tmpl@1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toml@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz" + integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== + +totalist@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz" + integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== + +tough-cookie@^4.0.0: + version "4.1.2" + resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz" + integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + +tr46@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz" + integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== + dependencies: + punycode "^2.1.1" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +ts-jest@^27.1.2: + version "27.1.5" + resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.5.tgz" + integrity sha512-Xv6jBQPoBEvBq/5i2TeSG9tt/nqkbpcurrEG1b+2yfBrcJelOZF9Ml6dmyMh7bcW9JyFbRYpR5rxROSlBLTZHA== + dependencies: + bs-logger "0.x" + fast-json-stable-stringify "2.x" + jest-util "^27.0.0" + json5 "2.x" + lodash.memoize "4.x" + make-error "1.x" + semver "7.x" + yargs-parser "20.x" + +ts-typed-json@^0.3.2: + version "0.3.2" + resolved "https://registry.npmjs.org/ts-typed-json/-/ts-typed-json-0.3.2.tgz" + integrity sha512-Tdu3BWzaer7R5RvBIJcg9r8HrTZgpJmsX+1meXMJzYypbkj8NK2oJN0yvm4Dp/Iv6tzFa/L5jKRmEVTga6K3nA== + +tslib@^1.9.0: + version "1.14.1" + resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tunnel@^0.0.6: + version "0.0.6" + resolved "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz" + integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz" + integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== + dependencies: + prelude-ls "~1.1.2" + +type-detect@^4.0.0, type-detect@^4.0.5, type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typeforce@^1.11.3: + version "1.18.0" + resolved "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz" + integrity sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g== + +typescript@^4.5.5, typescript@^4.9.0, "typescript@>=3.8 <5.0": + version "4.9.5" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + +typical@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz" + integrity sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw== + +typical@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz" + integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== + +ufo@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/ufo/-/ufo-1.1.2.tgz" + integrity sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ== + +uglify-js@^3.1.4: + version "3.17.4" + resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz" + integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== + +universal-user-agent@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz" + integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== + +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + +update-browserslist-db@^1.0.10: + version "1.0.10" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz" + integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +v8-to-istanbul@^8.1.0: + version "8.1.1" + resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz" + integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" + +validate-npm-package-license@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +validate-npm-package-name@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz" + integrity sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw== + dependencies: + builtins "^1.0.3" + +varuint-bitcoin@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz" + integrity sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw== + dependencies: + safe-buffer "^5.1.1" + +vite-node@0.32.4: + version "0.32.4" + resolved "https://registry.npmjs.org/vite-node/-/vite-node-0.32.4.tgz" + integrity sha512-L2gIw+dCxO0LK14QnUMoqSYpa9XRGnTTTDjW2h19Mr+GR0EFj4vx52W41gFXfMLqpA00eK4ZjOVYo1Xk//LFEw== + dependencies: + cac "^6.7.14" + debug "^4.3.4" + mlly "^1.4.0" + pathe "^1.1.1" + picocolors "^1.0.0" + vite "^3.0.0 || ^4.0.0" + +"vite@^3.0.0 || ^4.0.0", vite@^4.0.4: + version "4.2.1" + resolved "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz" + integrity sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg== + dependencies: + esbuild "^0.17.5" + postcss "^8.4.21" + resolve "^1.22.1" + rollup "^3.18.0" + optionalDependencies: + fsevents "~2.3.2" + +vitest-github-actions-reporter@^0.9.0: + version "0.9.0" + resolved "https://registry.npmjs.org/vitest-github-actions-reporter/-/vitest-github-actions-reporter-0.9.0.tgz" + integrity sha512-tCC9exD0HLH4oTdZBuAMapwkVU9RAcU2GpbtO4a5nqYG4Ty2LW61z90srbWqk3rZHL8IxGo2WfL8r7U+EeeYug== + dependencies: + "@actions/core" "^1.10.0" + +vitest@^0.32.0, vitest@>=0.16.0: + version "0.32.4" + resolved "https://registry.npmjs.org/vitest/-/vitest-0.32.4.tgz" + integrity sha512-3czFm8RnrsWwIzVDu/Ca48Y/M+qh3vOnF16czJm98Q/AN1y3B6PBsyV8Re91Ty5s7txKNjEhpgtGPcfdbh2MZg== + dependencies: + "@types/chai" "^4.3.5" + "@types/chai-subset" "^1.3.3" + "@types/node" "*" + "@vitest/expect" "0.32.4" + "@vitest/runner" "0.32.4" + "@vitest/snapshot" "0.32.4" + "@vitest/spy" "0.32.4" + "@vitest/utils" "0.32.4" + acorn "^8.9.0" + acorn-walk "^8.2.0" + cac "^6.7.14" + chai "^4.3.7" + debug "^4.3.4" + local-pkg "^0.4.3" + magic-string "^0.30.0" + pathe "^1.1.1" + picocolors "^1.0.0" + std-env "^3.3.3" + strip-literal "^1.0.1" + tinybench "^2.5.0" + tinypool "^0.5.0" + vite "^3.0.0 || ^4.0.0" + vite-node "0.32.4" + why-is-node-running "^2.2.2" + +w3c-hr-time@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== + dependencies: + browser-process-hrtime "^1.0.0" + +w3c-xmlserializer@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz" + integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== + dependencies: + xml-name-validator "^3.0.0" + +walker@^1.0.7: + version "1.0.8" + resolved "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== + dependencies: + makeerror "1.0.12" + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +webidl-conversions@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz" + integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== + +webidl-conversions@^6.1.0: + version "6.1.0" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz" + integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== + +whatwg-encoding@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + +whatwg-mimetype@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +whatwg-url@^8.0.0, whatwg-url@^8.5.0: + version "8.7.0" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz" + integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== + dependencies: + lodash "^4.7.0" + tr46 "^2.1.0" + webidl-conversions "^6.1.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +why-is-node-running@^2.2.2: + version "2.2.2" + resolved "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz" + integrity sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA== + dependencies: + siginfo "^2.0.0" + stackback "0.0.2" + +wide-align@^1.1.2: + version "1.1.5" + resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== + +wordwrapjs@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz" + integrity sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA== + dependencies: + reduce-flatten "^2.0.0" + typical "^5.2.0" + +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +ws@^7.4.6: + version "7.5.9" + resolved "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@^20.2.2, yargs-parser@20.x: + version "20.2.9" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@^16.2.0, yargs@16.2.0: + version "16.2.0" + resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +yocto-queue@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz" + integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== diff --git a/stacks-coordinator-mini/Cargo.toml b/stacks-coordinator-mini/Cargo.toml deleted file mode 100644 index bc58234c..00000000 --- a/stacks-coordinator-mini/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "stacks-coordinator-mini" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/stacks-coordinator-mini/README.md b/stacks-coordinator-mini/README.md deleted file mode 100644 index e3d28013..00000000 --- a/stacks-coordinator-mini/README.md +++ /dev/null @@ -1,103 +0,0 @@ -The following sequence diagrams illustrate the communication process between DKG signers (Signer 1, Signer 2, ..., Signer N) and the Smart Contract in the Stacks blockchain. It shows the sequence of events and flow of data between the signers, the smart contract, and the blockchain for key registration, public key retrieval, shared public key computation, message signing, and signature verification. - -```mermaid -sequenceDiagram -participant Signer1 -participant SignerN -participant SmartContract -participant Blockchain - -Note over Signer1, Blockchain: Key Generation -Signer1->>SmartContract: Register Public Key 1 -SignerN->>SmartContract: Register Public Key N - -Note over Signer1, Blockchain: Retrieve Transactions -Signer1->>Blockchain: Retrieve Unsigned Transactions -Blockchain-->>Signer1: Unsigned Transactions -SignerN->>Blockchain: Retrieve Unsigned Transactions -Blockchain-->>SignerN: Unsigned Transactions - -Note over Signer1, Blockchain: Signing Transactions -loop for each Pending Transaction - Signer1->>Signer1: Create Partial Signature 1 - SignerN->>SignerN: Create Partial Signature N - Signer1->>SignerN: Share Partial Signature 1 - SignerN->>Signer1: Share Partial Signature N - Signer1->>SmartContract: Check Partial Signature N - SignerN->>SmartContract: Check Partial Signature 1 - Signer1->>Signer1: Combine Partial Signatures 1..N - SignerN->>SignerN: Combine Partial Signatures 1..N - Signer1->>Signer1: Sign Transaction - SignerN->>SignerN: Sign Transaction - - Note over Signer1, Blockchain: Broadcast Transaction - Signer1->>Blockchain: Broadcast Signed Transaction - Blockchain-->>Signer1: Transaction Confirmation - SignerN->>Blockchain: Broadcast Signed Transaction - Blockchain-->>SignerN: Transaction Confirmation -end -``` - -This diagram shows the sequence of events that occur when a client activates an autosigner. The AutoSigner API retrieves pending transactions from the blockchain and loops through each transaction. For each transaction, it asks the Signer to determine whether to approve or reject it based on the signer's configuration. If the Signer can determine the decision, it signs the transaction and the AutoSigner API broadcasts it to the blockchain. If the Signer cannot determine the decision, the AutoSigner API notifies the client UI about the decision failure. - -```mermaid -sequenceDiagram -participant Blockchain -participant Signer -participant AutoSignerAPI -participant ClientUI - -Note over Blockchain, ClientUI: Start Signer - -ClientUI->>AutoSignerAPI: Activate Auto Signer -AutoSignerAPI->>Signer: Activate Auto Signer -Signer-->>AutoSignerAPI: Acknowledge Activation -AutoSignerAPI-->>ClientUI: Acknowledge Activation -Signer->>Blockchain: Retrieve Transactions -Blockchain-->>Signer: List of Transactions - -Note over Blockchain, ClientUI: Sign Transactions -loop for each Pending Transaction - Signer->>Signer: Determine Approval/Rejection - alt Approval/Rejection Determined - Signer->>Signer: Sign Transaction - Signer->>Blockchain: Broadcast Signed Transaction - Blockchain-->>Signer: Acknowledge Broadcast - else Cannot Determine - Note over Blockchain, ClientUI: Notify Client UI - Signer->>AutoSignerAPI: Notify Decision Failure - AutoSignerAPI-->>Signer: Acknowledge Notification - AutoSignerAPI->>ClientUI: Notify Decision Failure - ClientUI-->>AutoSignerAPI: Acknowledge Notification - end -end -``` - - -The following sequence diagram shows how a signer server app initializes and interacts with a signer lib, and how it registers and responds to requests via a signer API. The server app relies on the signer lib for cryptographic functions like signing and verifying, while the signer API provides a way for external clients such as a Web Client to interact with the signer server app. - -```mermaid -sequenceDiagram -participant SignerServerApp -participant SignerLib -participant SignerAPI -participant WebClient - -SignerServerApp->>SignerLib: Initialize Signer Lib -SignerLib-->>SignerServerApp: Signer Lib Initialized - -Note over SignerServerApp: Signer Server App Starts - -SignerServerApp->>SignerAPI: Register Signer API Endpoints -SignerAPI-->>SignerServerApp: Signer API Endpoints Registered - -Note over SignerServerApp: Wait for API Requests - -WebClient->>SignerAPI: API Request (e.g., /sign, /verify) -SignerAPI->>SignerServerApp: Incoming API Request -SignerServerApp->>SignerLib: Call Signer Function (e.g., sign, verify) -SignerLib-->>SignerServerApp: Function Result - -SignerServerApp->>SignerAPI: API Response -SignerAPI->>WebClient: Response Sent to WebClient -``` \ No newline at end of file diff --git a/stacks-coordinator-mini/src/main.rs b/stacks-coordinator-mini/src/main.rs deleted file mode 100644 index e7a11a96..00000000 --- a/stacks-coordinator-mini/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/stacks-coordinator/conf/coordinator.toml b/stacks-coordinator/conf/coordinator.toml deleted file mode 100644 index fd3eca16..00000000 --- a/stacks-coordinator/conf/coordinator.toml +++ /dev/null @@ -1,9 +0,0 @@ -sbtc_contract = "SP2BJA4JYFJ7SDMNFJZ9TJ3GB80P9Z80ADPGK1C2F.sbtc-alpha" -stacks_private_key = "" -stacks_node_rpc_url = "http://localhost:20443" -bitcoin_node_rpc_url = "http://abcd:abcd@localhost:18445" -frost_dkg_round_id = 0 -transaction_fee = 2000 -http_relay_url = "http://localhost:9776" -frost_state_file = "frost.state.bin" -network_private_key = "9aSCCR6eirt1NAHwJtSz4HMwBHTyMo62SyPMvVDt5DQn" \ No newline at end of file diff --git a/stacks-coordinator/src/lib.rs b/stacks-coordinator/src/lib.rs deleted file mode 100644 index 15960fe9..00000000 --- a/stacks-coordinator/src/lib.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub mod bitcoin_node; -pub mod bitcoin_wallet; -pub mod cli; -pub mod config; -pub mod coordinator; -pub mod peg_queue; -pub mod peg_wallet; -pub mod stacks_node; -pub mod stacks_wallet; -mod util; diff --git a/stacks-coordinator/src/stacks_node/client.rs b/stacks-coordinator/src/stacks_node/client.rs deleted file mode 100644 index c046d407..00000000 --- a/stacks-coordinator/src/stacks_node/client.rs +++ /dev/null @@ -1,716 +0,0 @@ -use std::time::{Duration, Instant}; - -use crate::stacks_node::{Error as StacksNodeError, PegInOp, PegOutRequestOp, StacksNode}; -use bitcoin::XOnlyPublicKey; -use blockstack_lib::{ - chainstate::stacks::StacksTransaction, - codec::StacksMessageCodec, - types::chainstate::StacksAddress, - vm::{types::SequenceData, ClarityName, ContractName, Value as ClarityValue}, -}; -use frost_signer::config::{PublicKeys, SignerKeyIds}; -use reqwest::{ - blocking::{Client, Response}, - StatusCode, -}; -use serde_json::{json, Value}; -use tracing::debug; -use url::Url; -use wsts::ecdsa::PublicKey; - -/// Kinds of stacks node broadcast errors that can occur -#[derive(Debug, thiserror::Error)] -pub enum BroadcastError { - #[error("Fee too low. Expected: {0}, Actual: {1}")] - FeeTooLow(u64, u64), - #[error("Not enough funds: {0}")] - NotEnoughFunds(String), - #[error("Conflicting nonce in mempool")] - ConflictingNonceInMempool, - #[error("{0}")] - Other(String), -} - -impl From<&serde_json::Value> for BroadcastError { - fn from(value: &serde_json::Value) -> Self { - let reason = value - .get("reason") - .and_then(|reason| reason.as_str()) - .unwrap_or("Unknown Reason"); - let reason_data = value.get("reason_data"); - match reason { - "FeeTooLow" => { - let expected = value - .get("expected") - .and_then(|expected| expected.as_u64()) - .unwrap_or(0); - let actual = value - .get("actual") - .and_then(|actual| actual.as_u64()) - .unwrap_or(0); - BroadcastError::FeeTooLow(expected, actual) - } - "NotEnoughFunds" => BroadcastError::NotEnoughFunds( - reason_data.unwrap_or(&json!("No Reason Data")).to_string(), - ), - "ConflictingNonceInMempool" => BroadcastError::ConflictingNonceInMempool, - _ => BroadcastError::Other(reason.to_string()), - } - } -} - -pub struct NodeClient { - node_url: Url, - client: Client, - contract_name: ContractName, - contract_address: StacksAddress, - next_nonce: Option, -} - -impl NodeClient { - pub fn new( - node_url: Url, - contract_name: ContractName, - contract_address: StacksAddress, - ) -> Self { - Self { - node_url, - client: Client::new(), - contract_name, - contract_address, - next_nonce: None, - } - } - - fn build_url(&self, route: &str) -> Result { - Ok(self.node_url.join(route)?) - } - - fn get_response(&self, route: &str) -> Result { - let url = self.build_url(route)?; - debug!("Sending Request to Stacks Node: {}", &url); - let now = Instant::now(); - let notify = |_err, dur| { - debug!("Failed to connect to {}. Next attempt in {:?}", &url, dur); - }; - - let send_request = || { - if now.elapsed().as_secs() > 5 { - debug!("Timeout exceeded."); - return Err(backoff::Error::Permanent(StacksNodeError::Timeout)); - } - let request = self.client.get(url.as_str()); - let response = request.send().map_err(StacksNodeError::ReqwestError)?; - Ok(response) - }; - let backoff_timer = backoff::ExponentialBackoffBuilder::new() - .with_initial_interval(Duration::from_millis(2)) - .with_max_interval(Duration::from_millis(128)) - .build(); - - let response = backoff::retry_notify(backoff_timer, send_request, notify) - .map_err(|_| StacksNodeError::Timeout)?; - - Ok(response) - } - - fn get_burn_ops(&self, block_height: u64, op: &str) -> Result, StacksNodeError> - where - T: serde::de::DeserializeOwned, - { - let json = self - .get_response(&format!("/v2/burn_ops/{block_height}/{op}"))? - .json::() - .map_err(|_| StacksNodeError::UnknownBlockHeight(block_height))?; - Ok(serde_json::from_value(json[op].clone())?) - } - - fn num_signers(&self, sender: &StacksAddress) -> Result { - let function_name = "get-num-signers"; - let total_signers_hex = self.call_read(sender, function_name, &[])?; - let total_signers = ClarityValue::try_deserialize_hex_untyped(&total_signers_hex)?; - if let ClarityValue::UInt(total_signers) = total_signers { - Ok(total_signers) - } else { - Err(StacksNodeError::MalformedClarityValue( - function_name.to_string(), - total_signers, - )) - } - } - - fn signer_data( - &self, - sender: &StacksAddress, - id: u128, - public_keys: &mut PublicKeys, - signer_key_ids: &mut SignerKeyIds, - ) -> Result<(), StacksNodeError> { - let function_name = "get-signer-data"; - let signer_data_hex = self.call_read( - sender, - function_name, - &[&ClarityValue::UInt(id).to_string()], - )?; - let signer_data = ClarityValue::try_deserialize_hex_untyped(&signer_data_hex)?; - if let ClarityValue::Optional(optional_data) = signer_data.clone() { - if let Some(ClarityValue::Tuple(tuple_data)) = optional_data.data.map(|boxed| *boxed) { - let public_key = - if let Some(ClarityValue::Sequence(SequenceData::Buffer(public_key))) = - tuple_data.data_map.get(&ClarityName::from("public-key")) - { - PublicKey::try_from(public_key.data.as_slice()).map_err(|_| { - StacksNodeError::MalformedClarityValue( - function_name.to_string(), - signer_data.clone(), - ) - })? - } else { - return Err(StacksNodeError::MalformedClarityValue( - function_name.to_string(), - signer_data, - )); - }; - public_keys - .signers - .insert(id.try_into().unwrap(), public_key); - if let Some(ClarityValue::Sequence(SequenceData::List(keys_ids))) = - tuple_data.data_map.get(&ClarityName::from("key-ids")) - { - let mut this_signer_key_ids = Vec::new(); - for key_id in &keys_ids.data { - if let ClarityValue::UInt(key_id) = key_id { - public_keys - .key_ids - .insert((*key_id).try_into().unwrap(), public_key); - this_signer_key_ids.push((*key_id).try_into().unwrap()); - } else { - return Err(StacksNodeError::MalformedClarityValue( - function_name.to_string(), - signer_data, - )); - } - } - signer_key_ids.insert(id.try_into().unwrap(), this_signer_key_ids); - } - } else { - return Err(StacksNodeError::NoSignerData(id)); - } - } - Err(StacksNodeError::MalformedClarityValue( - function_name.to_string(), - signer_data, - )) - } - - fn call_read( - &self, - sender: &StacksAddress, - function_name: &str, - function_args: &[&str], - ) -> Result { - debug!("Calling read-only function {}...", function_name); - let body = json!({"sender": sender.to_string(), "arguments": function_args}).to_string(); - let url = self.build_url(&format!( - "/v2/contracts/call-read/{}/{}/{function_name}", - self.contract_address, - self.contract_name.as_str() - ))?; - let response = self - .client - .post(url) - .header("content-type", "application/json") - .body(body) - .send()? - .json::()?; - debug!("response: {:?}", response); - if !response - .get("okay") - .map(|val| val.as_bool().unwrap_or(false)) - .unwrap_or(false) - { - let cause = response - .get("cause") - .ok_or(StacksNodeError::InvalidJsonEntry("cause".to_string()))?; - return Err(StacksNodeError::ReadOnlyFailure(format!( - "{}: {}", - function_name, cause - ))); - } - let result = response - .get("result") - .ok_or(StacksNodeError::InvalidJsonEntry("result".to_string()))? - .as_str() - .ok_or_else(|| StacksNodeError::ReadOnlyFailure("Expected string result.".to_string()))? - .to_string(); - Ok(result) - } -} - -impl StacksNode for NodeClient { - fn get_peg_in_ops(&self, block_height: u64) -> Result, StacksNodeError> { - debug!("Retrieving peg-in ops..."); - self.get_burn_ops::(block_height, "peg_in") - } - - fn get_peg_out_request_ops( - &self, - block_height: u64, - ) -> Result, StacksNodeError> { - debug!("Retrieving peg-out request ops..."); - self.get_burn_ops::(block_height, "peg_out_request") - } - - fn burn_block_height(&self) -> Result { - debug!("Retrieving burn block height..."); - let json = self.get_response("/v2/info")?.json::()?; - let entry = "burn_block_height"; - json[entry] - .as_u64() - .ok_or_else(|| StacksNodeError::InvalidJsonEntry(entry.to_string())) - } - - fn next_nonce(&mut self, address: &StacksAddress) -> Result { - debug!("Retrieving next nonce..."); - if let Some(nonce) = self.next_nonce { - let next_nonce = nonce.wrapping_add(1); - self.next_nonce = Some(next_nonce); - return Ok(next_nonce); - } - let address = address.to_string(); - let entry = "nonce"; - let route = format!("/v2/accounts/{}", address); - let response = self.get_response(&route)?; - if response.status() == StatusCode::NOT_FOUND { - return Err(StacksNodeError::UnknownAddress(address)); - } - let json = response - .json::() - .map_err(|_| StacksNodeError::BehindChainTip)?; - let nonce = json - .get(entry) - .and_then(|nonce| nonce.as_u64()) - .ok_or_else(|| StacksNodeError::InvalidJsonEntry(entry.to_string()))?; - self.next_nonce = Some(nonce); - Ok(nonce) - } - - fn broadcast_transaction(&self, tx: &StacksTransaction) -> Result<(), StacksNodeError> { - debug!("Broadcasting transaction..."); - debug!("Transaction: {:?}", tx); - let url = self.build_url("/v2/transactions")?; - let mut buffer = vec![]; - - tx.consensus_serialize(&mut buffer)?; - - let response = self - .client - .post(url) - .header("content-type", "application/octet-stream") - .body(buffer) - .send()?; - - if response.status() != StatusCode::OK { - let json_response = response.json::()?; - return Err(StacksNodeError::from(BroadcastError::from(&json_response))); - } - Ok(()) - } - - fn keys_threshold(&self, sender: &StacksAddress) -> Result { - let function_name = "get-threshold"; - let threshold_hex = self.call_read(sender, function_name, &[])?; - let threshold = ClarityValue::try_deserialize_hex_untyped(&threshold_hex)?; - if let ClarityValue::UInt(keys_threshold) = threshold { - Ok(keys_threshold) - } else { - Err(StacksNodeError::MalformedClarityValue( - function_name.to_string(), - threshold, - )) - } - } - - fn public_keys(&self, sender: &StacksAddress) -> Result { - let total_signers = self.num_signers(sender)?; - // Retrieve all the signers - let mut public_keys = PublicKeys::default(); - let mut signer_key_ids = SignerKeyIds::default(); - for id in 1..=total_signers { - self.signer_data(sender, id, &mut public_keys, &mut signer_key_ids)?; - } - Ok(public_keys) - } - - fn signer_key_ids(&self, sender: &StacksAddress) -> Result { - let total_signers = self.num_signers(sender)?; - // Retrieve all the signers - let mut public_keys = PublicKeys::default(); - let mut signer_key_ids = SignerKeyIds::default(); - for id in 1..=total_signers { - self.signer_data(sender, id, &mut public_keys, &mut signer_key_ids)?; - } - Ok(signer_key_ids) - } - - fn coordinator_public_key( - &self, - sender: &StacksAddress, - ) -> Result, StacksNodeError> { - let function_name = "get-coordinator-data"; - let coordinator_data_hex = self.call_read(sender, function_name, &[])?; - let coordinator_data = ClarityValue::try_deserialize_hex_untyped(&coordinator_data_hex)?; - if let ClarityValue::Optional(optional_data) = coordinator_data.clone() { - if let Some(ClarityValue::Tuple(tuple_data)) = optional_data.data.map(|boxed| *boxed) { - let value = tuple_data - .data_map - .get(&ClarityName::from("key")) - .ok_or_else(|| { - StacksNodeError::MalformedClarityValue( - function_name.to_string(), - coordinator_data.clone(), - ) - })?; - if let ClarityValue::Sequence(SequenceData::Buffer(coordinator_public_key)) = value - { - let public_key = PublicKey::try_from(coordinator_public_key.data.as_slice()) - .map_err(|_| { - StacksNodeError::MalformedClarityValue( - function_name.to_string(), - coordinator_data, - ) - })?; - return Ok(Some(public_key)); - } else { - return Err(StacksNodeError::MalformedClarityValue( - function_name.to_string(), - coordinator_data, - )); - } - } - Err(StacksNodeError::MalformedClarityValue( - function_name.to_string(), - coordinator_data, - )) - } else { - Ok(None) - } - } - - fn bitcoin_wallet_public_key( - &self, - sender: &StacksAddress, - ) -> Result, StacksNodeError> { - let function_name = "get-bitcoin-wallet-public-key"; - let bitcoin_wallet_public_key_hex = self.call_read(sender, function_name, &[])?; - let bitcoin_wallet_public_key = - ClarityValue::try_deserialize_hex_untyped(&bitcoin_wallet_public_key_hex)?; - if let ClarityValue::Optional(optional_data) = bitcoin_wallet_public_key.clone() { - if let Some(ClarityValue::Sequence(SequenceData::Buffer(public_key))) = - optional_data.data.map(|boxed| *boxed) - { - let xonly_pubkey = XOnlyPublicKey::from_slice(&public_key.data).map_err(|_| { - StacksNodeError::MalformedClarityValue( - function_name.to_string(), - bitcoin_wallet_public_key, - ) - })?; - return Ok(Some(xonly_pubkey)); - } else { - return Ok(None); - } - } - Err(StacksNodeError::MalformedClarityValue( - function_name.to_string(), - bitcoin_wallet_public_key, - )) - } -} - -#[cfg(test)] -mod tests { - use std::{ - io::{BufWriter, Read, Write}, - net::{SocketAddr, TcpListener}, - thread::spawn, - }; - - use blockstack_lib::{ - address::{AddressHashMode, C32_ADDRESS_VERSION_TESTNET_SINGLESIG}, - burnchains::Address, - chainstate::stacks::{ - CoinbasePayload, SinglesigHashMode, SinglesigSpendingCondition, TransactionAnchorMode, - TransactionAuth, TransactionPayload, TransactionPostConditionMode, - TransactionPublicKeyEncoding, TransactionSpendingCondition, TransactionVersion, - }, - types::chainstate::{StacksPrivateKey, StacksPublicKey}, - util::{hash::Hash160, secp256k1::MessageSignature}, - }; - - use crate::util::test::PRIVATE_KEY_HEX; - - use super::*; - - struct TestConfig { - sender: StacksAddress, - mock_server: TcpListener, - client: NodeClient, - } - - impl TestConfig { - pub fn new() -> Self { - let sender_key = StacksPrivateKey::from_hex(PRIVATE_KEY_HEX) - .expect("Unable to generate stacks private key from hex string"); - - let pk = StacksPublicKey::from_private(&sender_key); - - let sender = StacksAddress::from_public_keys( - C32_ADDRESS_VERSION_TESTNET_SINGLESIG, - &AddressHashMode::SerializeP2PKH, - 1, - &vec![pk], - ) - .expect("Failed to generate address from private key"); - - let mut mock_server_addr = SocketAddr::from(([127, 0, 0, 1], 0)); - let mock_server = TcpListener::bind(mock_server_addr).unwrap(); - - mock_server_addr.set_port(mock_server.local_addr().unwrap().port()); - let client = NodeClient::new( - Url::parse(&format!("http://{}", mock_server_addr)) - .expect("Failed to parse mock server address"), - ContractName::from("sbtc-alpha"), - StacksAddress::from_string("SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE").unwrap(), - ); - Self { - sender, - mock_server, - client, - } - } - } - - fn write_response(mock_server: TcpListener, bytes: &[u8]) -> [u8; 1024] { - let mut request_bytes = [0u8; 1024]; - { - let mut stream = mock_server.accept().unwrap().0; - - stream.read(&mut request_bytes).unwrap(); - stream.write(bytes).unwrap(); - } - request_bytes - } - - #[test] - fn call_read_success_test() { - let config = TestConfig::new(); - let h = spawn(move || { - config - .client - .call_read(&config.sender, "function-name", &[]) - }); - write_response( - config.mock_server, - b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x070d0000000473425443\"}", - ); - let result = h.join().unwrap().unwrap(); - assert_eq!(result, "0x070d0000000473425443"); - } - - #[test] - fn call_read_failure_test() { - let config = TestConfig::new(); - let h = spawn(move || { - config - .client - .call_read(&config.sender, "function-name", &[]) - }); - write_response( - config.mock_server, - b"HTTP/1.1 200 OK\n\n{\"okay\":false,\"cause\":\"Some reason\"}", - ); - let result = h.join().unwrap(); - assert!(matches!(result, Err(StacksNodeError::ReadOnlyFailure(_)))); - } - - #[test] - fn signer_data_none_test() { - let config = TestConfig::new(); - - let h = spawn(move || { - let mut public_keys = PublicKeys::default(); - let mut signer_key_ids = SignerKeyIds::default(); - config - .client - .signer_data(&config.sender, 1u128, &mut public_keys, &mut signer_key_ids) - }); - write_response( - config.mock_server, - b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x09\"}", - ); - let result = h.join().unwrap(); - assert!(matches!(result, Err(StacksNodeError::NoSignerData(_)))); - } - - #[test] - fn keys_threshold_test() { - let config = TestConfig::new(); - - let h = spawn(move || config.client.keys_threshold(&config.sender)); - - write_response(config.mock_server, b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x0100000000000000000000000000000af0\"}"); - let result = h.join().unwrap().unwrap(); - assert_eq!(result, 2800); - } - - #[test] - fn keys_threshold_invalid_test() { - let config = TestConfig::new(); - - let h = spawn(move || config.client.keys_threshold(&config.sender)); - write_response( - config.mock_server, - b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x09\"}", - ); - let result = h.join().unwrap(); - assert!(matches!( - result, - Err(StacksNodeError::MalformedClarityValue(..)) - )); - } - - #[test] - fn num_signers_test() { - let config = TestConfig::new(); - - let h = spawn(move || config.client.num_signers(&config.sender)); - write_response(config.mock_server, - b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x0100000000000000000000000000000fa0\"}" - ); - let result = h.join().unwrap().unwrap(); - assert_eq!(result, 4000); - } - - #[test] - fn num_signers_invalid_test() { - let config = TestConfig::new(); - - let h = spawn(move || config.client.num_signers(&config.sender)); - write_response( - config.mock_server, - b"HTTP/1.1 200 OK\n\n{\"okay\":true,\"result\":\"0x09\"}", - ); - let result = h.join().unwrap(); - assert!(matches!( - result, - Err(StacksNodeError::MalformedClarityValue(..)) - )); - } - - #[test] - fn next_nonce_success_test() { - let mut config = TestConfig::new(); - - let h = spawn(move || { - let nonce = config.client.next_nonce(&config.sender).unwrap(); - let next_nonce = config.client.next_nonce(&config.sender).unwrap(); - (nonce, next_nonce) - }); - write_response(config.mock_server, - b"HTTP/1.1 200 OK\n\n{\"balance\":\"0x00000000000000000000000000000000\",\"locked\":\"0x00000000000000000000000000000000\",\"unlock_height\":0,\"nonce\":20,\"balance_proof\":\"\",\"nonce_proof\":\"\"}" - ); - let (nonce, next_nonce) = h.join().unwrap(); - assert_eq!(nonce, 20); - assert_eq!(next_nonce, 21); - } - - #[test] - fn next_nonce_failure_test() { - let mut config = TestConfig::new(); - - let h = spawn(move || config.client.next_nonce(&config.sender)); - write_response( - config.mock_server, - b"HTTP/1.1 404 Not Found\n\n/v2/accounts/SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE", - ); - let result = h.join().unwrap(); - assert!(matches!(result, Err(StacksNodeError::UnknownAddress(_)))); - } - - #[test] - fn burn_block_height_success_test() { - let config = TestConfig::new(); - - let h = spawn(move || config.client.burn_block_height()); - write_response( - config.mock_server, - b"HTTP/1.1 200 OK\n\n{\"peer_version\":420759911,\"burn_block_height\":2430220}", - ); - let result = h.join().unwrap().unwrap(); - assert_eq!(result, 2430220); - } - - #[test] - fn burn_block_height_failure_test() { - let config = TestConfig::new(); - - let h = spawn(move || config.client.burn_block_height()); - write_response( - config.mock_server, - b"HTTP/1.1 200 OK\n\n{\"peer_version\":420759911,\"burn_block_height2\":2430220}", - ); - let result = h.join().unwrap(); - assert!(matches!(result, Err(StacksNodeError::InvalidJsonEntry(_)))); - } - - #[test] - fn should_send_tx_bytes_to_node() { - let config = TestConfig::new(); - let tx = StacksTransaction { - version: TransactionVersion::Testnet, - chain_id: 0, - auth: TransactionAuth::Standard(TransactionSpendingCondition::Singlesig( - SinglesigSpendingCondition { - hash_mode: SinglesigHashMode::P2PKH, - signer: Hash160([0; 20]), - nonce: 0, - tx_fee: 0, - key_encoding: TransactionPublicKeyEncoding::Uncompressed, - signature: MessageSignature([0; 65]), - }, - )), - anchor_mode: TransactionAnchorMode::Any, - post_condition_mode: TransactionPostConditionMode::Allow, - post_conditions: vec![], - payload: TransactionPayload::Coinbase(CoinbasePayload([0; 32]), None), - }; - - let mut tx_bytes = [0u8; 1024]; - { - let mut tx_bytes_writer = BufWriter::new(&mut tx_bytes[..]); - tx.consensus_serialize(&mut tx_bytes_writer).unwrap(); - tx_bytes_writer.flush().unwrap(); - } - - let bytes_len = tx_bytes - .iter() - .enumerate() - .rev() - .find(|(_, &x)| x != 0) - .unwrap() - .0 - + 1; - - let h = spawn(move || config.client.broadcast_transaction(&tx)); - - let request_bytes = write_response(config.mock_server, b"HTTP/1.1 200 OK\n\n"); - h.join().unwrap().unwrap(); - - assert!( - request_bytes - .windows(bytes_len) - .any(|window| window == &tx_bytes[..bytes_len]), - "Request bytes did not contain the transaction bytes" - ); - } -} diff --git a/stacks-doctor/Cargo.toml b/stacks-doctor/Cargo.toml deleted file mode 100644 index 1b96668b..00000000 --- a/stacks-doctor/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "stacks-doctor" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -clap.workspace = true -reqwest = { workspace = true, features = ["blocking", "json"] } -serde_json.workspace = true -serde = { workspace = true, features = ["derive"] } -rusqlite.workspace = true -anyhow.workspace = true diff --git a/stacks-doctor/README.md b/stacks-doctor/README.md deleted file mode 100644 index 32527731..00000000 --- a/stacks-doctor/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# stacks-doctor - CLI Tool for Debugging Running Stacks Nodes - -This CLI tool is designed to help developers debug running nodes by sourcing node information from various sources such as the node's RPC API, logs, and database. - -### (Potential) Features - -- Easy-to-use CLI interface: The CLI interface is user-friendly and easy to use, allowing developers to quickly get the information they need to debug their nodes. -- Simple HTML interface: In addition to the CLI, this tool provides a simple HTML interface that allows developers to easily view node information in their web browser. - - -### Usage - -To use this tool, simply run the command with the appropriate options and arguments. For example: - -``` -stacks-doctor \ - --rpc_url=http://localhost:8545 \ - --log-file=/path/to/node.log \ - --db-file=/path/to/db.sqlite \ - analyze -``` - -If you want to simplify the command it's possible to move arguments to environment variables. - -``` -export RPC_URL=http://localhost:8545; -export LOG_FILE=/path/to/node.log; -export DB_FILE=/path/to/db.sqlite; - -stacks-doctor analyze -``` - -This command will get data from all sources provided and output information about any potential problems. - -### Installation - -To install this tool, simply run the following command: - -``` -cargo install --path core-eng/stacks-doctor -``` diff --git a/stacks-doctor/src/cli.rs b/stacks-doctor/src/cli.rs deleted file mode 100644 index edbb373a..00000000 --- a/stacks-doctor/src/cli.rs +++ /dev/null @@ -1,75 +0,0 @@ -use core::fmt::{self, Formatter}; -use std::{ - fmt::{Debug, Display}, - path::PathBuf, - str::FromStr, -}; - -use clap::{Parser, Subcommand}; - -#[derive(Clone, Debug)] -pub enum Network { - Mainnet, - Testnet, -} - -impl Display for Network { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl FromStr for Network { - type Err = String; - - fn from_str(s: &str) -> Result { - match s.to_ascii_lowercase().as_str() { - "mainnet" => Ok(Network::Mainnet), - "testnet" => Ok(Network::Testnet), - _ => Err(format!("Could not parse Network: {}", s)), - } - } -} - -#[derive(Parser, Clone, Debug)] -pub struct BlocksArgs { - // How many recent blocks to take into account - #[arg(short, long, default_value_t = 1000)] - pub blocks: u64, -} - -#[derive(Subcommand, Clone, Debug)] -pub enum Commands { - /// Analyze miner - Analyze, - /// Print burn fee information - Burns(BlocksArgs), - /// Print reorg information - Reorgs(BlocksArgs), - /// Print related environment variables that are set - Env, -} - -/// Tool for debugging running Stacks nodes -#[derive(Parser, Clone, Debug)] -#[command(author, version, about)] -pub struct Args { - /// Which network to analyze - #[arg(short, long, default_value_t = Network::Mainnet, env = "DOCTOR_NETWORK")] - pub network: Network, - - /// URL to the node RPC API - #[arg(short, long, env = "DOCTOR_RPC_URL")] - pub rpc_url: Option, - - /// Path to the node log file - #[arg(short, long, env = "DOCTOR_LOG_FILE")] - pub log_file: Option, - - /// Path to the node directory with all the databases, usually contains a / dir such as xenon/ - #[arg(short, long, env = "DOCTOR_DB_DIR")] - pub db_dir: Option, - - #[command(subcommand)] - pub cmd: Commands, -} diff --git a/stacks-doctor/src/commands/analyze_logs.rs b/stacks-doctor/src/commands/analyze_logs.rs deleted file mode 100644 index 2c804011..00000000 --- a/stacks-doctor/src/commands/analyze_logs.rs +++ /dev/null @@ -1,40 +0,0 @@ -use std::{ - fs::File, - io::{BufRead, BufReader}, - path::PathBuf, -}; - -use anyhow::{Context, Result}; - -/* -Run commands below to get a sample log file locally and analyze it: - -``` -cd stacks-blockchain/testnet/stacks-node; -cargo run -p stacks-node --bin stacks-node -- testnet 2>&1 | tee node.log; - -# Set other variables accordingly -stacks-doctor -l /path/to/node.log analyze -``` -*/ -pub fn analyze_logs(log_file: PathBuf) -> Result<()> { - let file = BufReader::new(File::open(log_file).context("Could not open log file")?); - let mut is_okay = true; - - file.lines().map_while(Result::ok).for_each(|line| { - if line.contains("mined anchored block") { - is_okay = true; - } else if line.contains("Failure mining") { - is_okay = false; - println!("Found problem in logs: {}", line); - } - }); - - if is_okay { - println!("No problems detected in logs"); - } else { - println!("Problems detected in logs"); - } - - Ok(()) -} diff --git a/stacks-doctor/src/commands/burns.rs b/stacks-doctor/src/commands/burns.rs deleted file mode 100644 index d7f76eb9..00000000 --- a/stacks-doctor/src/commands/burns.rs +++ /dev/null @@ -1,73 +0,0 @@ -use std::path::Path; - -use anyhow::{anyhow, Context, Result}; -use rusqlite::OpenFlags; - -use crate::cli::{BlocksArgs, Network}; - -pub fn burns(network: Network, db_dir: &Path, args: &BlocksArgs) -> Result<()> { - let mode = match network { - Network::Mainnet => "mainnet/", - Network::Testnet => "xenon/", - }; - let db_file = db_dir.join(mode).join("burnchain/burnchain.sqlite"); - let conn = rusqlite::Connection::open_with_flags(db_file, OpenFlags::SQLITE_OPEN_READ_ONLY) - .context("Could not open database connection")?; - - let mut statement = conn - .prepare(r#" - SELECT JSON_EXTRACT(op, "$.LeaderBlockCommit.block_height") as block_height, JSON_EXTRACT(op, "$.LeaderBlockCommit.burn_fee") as burn_fee - FROM burnchain_db_block_ops - ORDER BY block_height DESC - "#,) - .context("Could not prepare SQL statement")?; - - let mut rows = statement - .query::<[u8; 0]>([]) - .context("Could not execute query")?; - - let mut height_fee_pairs: Vec<(u64, u64)> = vec![]; - - while let Some(row) = rows.next().context("Could not get row")? { - let Some(height) = row - .get::<_, Option>(0) - .context("Could not get block height from row")? - else { - continue; - }; - let Some(fee) = row - .get::<_, Option>(1) - .context("Could not get burn fee from row")? - else { - continue; - }; - - height_fee_pairs.push((height as u64, fee as u64)); - } - - if height_fee_pairs.is_empty() { - return Err(anyhow!("Query returned no data")); - } - - let last_block = height_fee_pairs.first().unwrap().0; - let cutoff_block = last_block - args.blocks; - - let mut burn_fees: Vec = height_fee_pairs - .into_iter() - .filter(|(height, _)| *height >= cutoff_block) - .map(|(_, fee)| fee) - .collect(); - - burn_fees.sort(); - - println!( - "Burn fee stats for last {} blocks: min={} max={} mean={} avg={}", - args.blocks, - burn_fees.first().unwrap(), - burn_fees.last().unwrap(), - burn_fees[burn_fees.len() / 2], - burn_fees.iter().sum::() / burn_fees.len() as u64 - ); - - Ok(()) -} diff --git a/stacks-doctor/src/commands/env.rs b/stacks-doctor/src/commands/env.rs deleted file mode 100644 index 06c42b89..00000000 --- a/stacks-doctor/src/commands/env.rs +++ /dev/null @@ -1,11 +0,0 @@ -use std::env::vars; - -use anyhow::Result; - -pub fn show_env() -> Result<()> { - vars() - .filter(|var| var.0.contains("DOCTOR")) - .for_each(|var| println!("{}={}", var.0, var.1)); - - Ok(()) -} diff --git a/stacks-doctor/src/commands/mod.rs b/stacks-doctor/src/commands/mod.rs deleted file mode 100644 index 3a4b9fec..00000000 --- a/stacks-doctor/src/commands/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod analyze_logs; -mod burns; -mod env; -mod reorgs; - -pub use analyze_logs::analyze_logs; -pub use burns::burns; -pub use env::show_env; -pub use reorgs::reorgs; diff --git a/stacks-doctor/src/commands/reorgs.rs b/stacks-doctor/src/commands/reorgs.rs deleted file mode 100644 index 83b26de1..00000000 --- a/stacks-doctor/src/commands/reorgs.rs +++ /dev/null @@ -1,107 +0,0 @@ -use std::path::Path; - -use anyhow::{Context, Result}; -use rusqlite::OpenFlags; -use serde::Serialize; -use serde_json::to_string_pretty; - -use crate::cli::{BlocksArgs, Network}; - -#[derive(Serialize)] -struct Item { - burn_height: i64, - stacks_block_id: String, - miner: String, - stacks_height: i64, - reorg_depth: i64, -} - -#[derive(Serialize)] -struct Response { - message: String, - max_reorg_depth: i64, - max_reorg_blocks_ago: usize, - data: Vec, -} - -pub fn reorgs(network: Network, db_dir: &Path, args: &BlocksArgs) -> Result<()> { - let mode = match network { - Network::Mainnet => "mainnet/", - Network::Testnet => "xenon/", - }; - let db_file = db_dir.join(mode).join("chainstate/vm/index.sqlite"); - let conn = rusqlite::Connection::open_with_flags(db_file, OpenFlags::SQLITE_OPEN_READ_ONLY) - .context("Could not open database connection")?; - - let mut statement = conn - .prepare( - r#" - SELECT - block_headers.burn_header_height as burn_height, - b.index_block_hash as index_block_hash, - payments.address as miner, - block_headers.block_height as stacks_height - FROM staging_blocks as b - INNER JOIN block_headers ON block_headers.index_block_hash = b.index_block_hash - INNER JOIN payments ON payments.index_block_hash = b.index_block_hash - WHERE payments.miner = 1 GROUP BY block_headers.burn_header_height - ORDER BY burn_height DESC - LIMIT ?1; - "#, - ) - .context("Could not prepare SQL statement")?; - - let mut rows = statement - .query::<[i64; 1]>([args.blocks as i64 + 1]) - .context("Could not execute query")?; - - let mut data = Vec::new(); - - while let Some(row) = rows.next().context("Could not get row")? { - let burn_height: i64 = row.get(0).context("Could not get burn height")?; - let stacks_block_id: String = row.get(1).context("Could not get stacks block id")?; - let miner: String = row.get(2).context("Could not get miner")?; - let stacks_height: i64 = row.get(3).context("Could not get stacks height")?; - - data.push(Item { - burn_height, - stacks_block_id, - miner, - stacks_height, - reorg_depth: 0, - }); - } - - let mut last_stacks_height = data - .pop() - .context("No blocks returned from query")? - .stacks_height; - - data.iter_mut().rev().for_each(|item| { - item.reorg_depth = (1 + last_stacks_height - item.stacks_height).max(0); - last_stacks_height = item.stacks_height; - }); - - let max_reorg_depth = data - .iter() - .max_by_key(|item| item.reorg_depth) - .unwrap() - .reorg_depth; - let max_reorg_blocks_ago = data - .iter() - .enumerate() - .find(|(_, item)| item.reorg_depth == max_reorg_depth) - .unwrap() - .0; - - let res = Response { - message: format!("Reorg data for last {} blocks", args.blocks), - max_reorg_depth, - max_reorg_blocks_ago, - data, - }; - - println!("{}", to_string_pretty(&res).unwrap()); - - Ok(()) -} diff --git a/stacks-doctor/src/main.rs b/stacks-doctor/src/main.rs deleted file mode 100644 index 8d52d46e..00000000 --- a/stacks-doctor/src/main.rs +++ /dev/null @@ -1,41 +0,0 @@ -use clap::Parser; -use cli::Commands; -use commands::{analyze_logs, burns, reorgs, show_env}; - -use crate::cli::Args; - -mod cli; -mod commands; - -fn main() { - let args = Args::parse(); - - match &args.cmd { - Commands::Analyze => { - if let Some(log_file) = args.log_file { - analyze_logs(log_file) - } else { - eprintln!("Log file path needs to be passed"); - Ok(()) - } - } - Commands::Burns(inner_args) => { - if let Some(db_dir) = args.db_dir { - burns(args.network, &db_dir, inner_args) - } else { - eprintln!("Database directory path needs to be passed"); - Ok(()) - } - } - Commands::Reorgs(inner_args) => { - if let Some(db_dir) = args.db_dir { - reorgs(args.network, &db_dir, inner_args) - } else { - eprintln!("Database directory path needs to be passed"); - Ok(()) - } - } - Commands::Env => show_env(), - } - .unwrap(); -} diff --git a/stacks-signer-api/.env b/stacks-signer-api/.env deleted file mode 100644 index b513a52a..00000000 --- a/stacks-signer-api/.env +++ /dev/null @@ -1,2 +0,0 @@ -DATABASE_URL=sqlite:///$PWD/dev-signer-api.sqlite -SQLX_OFFLINE=true \ No newline at end of file diff --git a/stacks-signer-api/Cargo.toml b/stacks-signer-api/Cargo.toml deleted file mode 100644 index 283fafd1..00000000 --- a/stacks-signer-api/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "stacks-signer-api" -version = "0.1.0" -edition = "2021" - -[dependencies] -anyhow.workspace = true -clap.workspace = true -hex.workspace = true -parse-display = "0.8.1" -rand.workspace = true -secp256k1.workspace = true -serde.workspace = true -serde_json.workspace = true -serde_urlencoded = "0.7" -sqlx = { version = "0.5", features = ["sqlite", "runtime-tokio-native-tls", "offline"] } -tokio = { version = "1.28.1", features = ["macros", "rt-multi-thread"] } -toml.workspace = true -thiserror.workspace = true -tracing.workspace = true -tracing-subscriber.workspace = true -utoipa = "3.3.0" -utoipa-swagger-ui = { version = "3.1.3" } -warp = "0.3.5" -dotenv = "0.15.0" - -[dev-dependencies] -ntest = "0.9.0" diff --git a/stacks-signer-api/README.md b/stacks-signer-api/README.md deleted file mode 100644 index c4170ccb..00000000 --- a/stacks-signer-api/README.md +++ /dev/null @@ -1,147 +0,0 @@ -# Stacks Signer API - -This is a CLI application for the Stacks Signer API. It provides an API server for interacting with a Signer. It can run an API server with or without simulated data, generate API documentation, and run a Swagger UI server. - -## Requirements - -- Rust -- SQLite - - -## Getting Started - -To get started, first, make sure you have Rust installed. You can follow the Rust installation instructions [here](https://www.rust-lang.org/tools/install). - -Next, clone this project and navigate to its root directory in your command prompt or terminal. - -1. Clone the repository: - -```bash -git clone https://github.com/Trust-Machines/stacks-sbtc.git -``` - -2. Navigate to the project folder: - -```bash -cd stacks-sbtc/stacks-signer-api -``` - -3. Build the CLI: - -```bash -cargo build --release -``` - -4. Navigate to the output folder: - -```bash -cd target/release -``` - -## Usage - -### Run - -To run the API server, run the following command - -```shell -./stacks-signer-api run -``` - -#### Arguments -- `--address` - Address to run the API server on (Default: `0.0.0.0`) -- `--port` - Port to run the API server on (Default: `3030`) - - -### Simulator - -To run the API server with simulated data, run the following command - -```shell -./stacks-signer-api simulator -``` - -#### Arguments -- `--address` - Address to run the API server on (Default: `0.0.0.0`) -- `--port` - Port to run the API server on (Default: `3030`) - -### Swagger - -To run a local instance of Swagger UI for this API, execute the following command: - -```shell -./stacks-signer-api swagger -``` - -#### Arguments -- `--address` - Address to run the API server on (Default: `0.0.0.0`) -- `--port` - Port to run the API server on (Default: `3030`) - -By default, the Swagger UI server will be accessible at `http://0.0.0.0:3030/swagger-ui/`. - -### Docs - -To generate OpenAPI json documentation for the API, run the following command: - -```shell -./stacks-signer-api docs -``` - -#### Args -- `--output` - Output file to save the API documentation to. If not provided, it prints to stdout. - - - -## Endpoints - -Run the Swagger CLI option to obtain a detailed breakdown of the API endpoints, requests, and responses. - - -## Error Handling - -The Signer API uses a custom error type `Error`, which wraps SQLx and parse errors. The error type is defined in the root of the project and implements the `warp::reject::Reject` trait to enable proper handling in Warp filters. - -## Database - -The Signer API uses SQLite as the backend database, and the `sqlx` crate is utilized for handling database connections and queries. - -Database operations are organized in separate modules according to their purpose (e.g., `keys`, `signers`, `transaction`, `vote`). Each module contains functions for interacting with the database, such as retrieving records, inserting records, and deleting records. - -### Database Configuration - -To use a file-based SQLite database, set the `DATABASE_URL` environment variable with the file path: - -```shell -export DATABASE_URL="sqlite://path/to/your/database.sqlite3" -``` - -If the `DATABASE_URL` environment variable is not set, the API will use an in-memory SQLite database by default. Note that using an in-memory database is suitable for testing purposes but not recommended for production use, as the data will not be persisted after terminating the application. - -To run the server with the database specified in the `DATABASE_URL` environment variable (or an in-memory database if the variable is not set), use the following command: - -```shell -./stacks-signer-api run -``` - -Alternatively, run the following command: - -```shell -DATABASE_URL="sqlite://path/to/your/database.sqlite3" ./stacks-signer-api run -``` - -## Dev/Compilation setup - -To make use of `sqlx` and verify the sql queries on your own, you should follow the following steps: - -1. remove `sqlx-data.json` -2. install `sqlx-cli` version `0.5.13`. So `cargo install sqlx-cli --version=0.5.13` -3. make sure you have sqlite installed -4. create a `.env` file in the `stacks-signer-api` root folder with the env variable `DATABASE_URL` -5. The url for sqlite is in the format `DATABASE_URL=sqlite://$(pwd)/stacks-signer-api/dev-signer-api.sqlite` -6. generate the test db using `sqlx database create` -7. run the `init` migration `sqlx migrate run` -8. prepare the `offline` static check cache `cargo sqlx prepare -- --lib` - -## License - -This project is licensed under [GPLv3](../LICENSE). \ No newline at end of file diff --git a/stacks-signer-api/dev-signer-api.sqlite b/stacks-signer-api/dev-signer-api.sqlite deleted file mode 100644 index 3e67ff63..00000000 Binary files a/stacks-signer-api/dev-signer-api.sqlite and /dev/null differ diff --git a/stacks-signer-api/migrations/20230614163551_init.sql b/stacks-signer-api/migrations/20230614163551_init.sql deleted file mode 100644 index b216aa04..00000000 --- a/stacks-signer-api/migrations/20230614163551_init.sql +++ /dev/null @@ -1,49 +0,0 @@ -CREATE TABLE IF NOT EXISTS config ( - id INTEGER NOT NULL PRIMARY KEY, - secret_key TEXT NOT NULL, - auto_approve_max_amount INTEGER NOT NULL, - delegate_public_key TEXT NOT NULL -); - -CREATE TABLE IF NOT EXISTS delegator_public_keys ( - public_key TEXT NOT NULL PRIMARY KEY -); - -CREATE TABLE IF NOT EXISTS auto_deny_addresses ( - address TEXT NOT NULL PRIMARY KEY -); - -CREATE TABLE IF NOT EXISTS transactions ( - txid TEXT NOT NULL PRIMARY KEY, - transaction_kind TEXT NOT NULL, - transaction_block_height INTEGER, - transaction_deadline_block_height INTEGER NOT NULL, - transaction_amount INTEGER NOT NULL, - transaction_fees INTEGER NOT NULL, - memo BLOB NOT NULL, - transaction_originator_address TEXT NOT NULL, - transaction_debit_address TEXT NOT NULL, - transaction_credit_address TEXT NOT NULL -); - -CREATE TABLE IF NOT EXISTS votes ( - txid TEXT NOT NULL PRIMARY KEY, - vote_status TEXT NOT NULL, - vote_choice TEXT, - vote_mechanism TEXT NOT NULL, - target_consensus INTEGER NOT NULL, - current_consensus INTEGER NOT NULL, - - FOREIGN KEY(txid) REFERENCES transactions(txid) ON DELETE CASCADE -); -CREATE TRIGGER add_empty_vote - AFTER INSERT ON transactions - FOR EACH ROW - WHEN NEW.txid NOT IN (SELECT txid FROM votes) - BEGIN - INSERT INTO votes ( - txid, vote_status, vote_choice, vote_mechanism, target_consensus, current_consensus - ) VALUES ( - NEW.txid, 'pending', NULL, 'manual', 70, 0 - ); - END; \ No newline at end of file diff --git a/stacks-signer-api/sqlx-data.json b/stacks-signer-api/sqlx-data.json deleted file mode 100644 index 1e69e812..00000000 --- a/stacks-signer-api/sqlx-data.json +++ /dev/null @@ -1,317 +0,0 @@ -{ - "db": "SQLite", - "04eac66be54316091db4b190894c71d63de586fecf198fda9f714b485e784846": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Right": 6 - } - }, - "query": "REPLACE INTO votes (\n txid, vote_status, vote_choice, vote_mechanism, target_consensus, current_consensus\n ) VALUES (?, ?, ?, ?, ?, ?);" - }, - "08137aa3c1118b24d82059e1fb6ee03ab0758c3e81f4e0169cca7dc16aff7b4b": { - "describe": { - "columns": [ - { - "name": "txid", - "ordinal": 0, - "type_info": "Text" - }, - { - "name": "transaction_kind", - "ordinal": 1, - "type_info": "Text" - }, - { - "name": "transaction_block_height", - "ordinal": 2, - "type_info": "Int64" - }, - { - "name": "transaction_deadline_block_height", - "ordinal": 3, - "type_info": "Int64" - }, - { - "name": "transaction_amount", - "ordinal": 4, - "type_info": "Int64" - }, - { - "name": "transaction_fees", - "ordinal": 5, - "type_info": "Int64" - }, - { - "name": "memo", - "ordinal": 6, - "type_info": "Blob" - }, - { - "name": "transaction_originator_address", - "ordinal": 7, - "type_info": "Text" - }, - { - "name": "transaction_debit_address", - "ordinal": 8, - "type_info": "Text" - }, - { - "name": "transaction_credit_address", - "ordinal": 9, - "type_info": "Text" - } - ], - "nullable": [ - false, - false, - true, - false, - false, - false, - false, - false, - false, - false - ], - "parameters": { - "Right": 1 - } - }, - "query": "SELECT * FROM transactions WHERE txid = ?" - }, - "1133fc2de36483b2ca3a3d2f82f4831d466e8ca3de859f5ab6ea0505706973ea": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Right": 1 - } - }, - "query": "REPLACE INTO auto_deny_addresses (address) VALUES (?1)" - }, - "32facf3bf257dfe2a701826f480e26a72f380e7c7e446d7b6b5f9e0a3c8fdb70": { - "describe": { - "columns": [ - { - "name": "txid", - "ordinal": 0, - "type_info": "Text" - }, - { - "name": "transaction_kind", - "ordinal": 1, - "type_info": "Text" - }, - { - "name": "transaction_block_height", - "ordinal": 2, - "type_info": "Int64" - }, - { - "name": "transaction_deadline_block_height", - "ordinal": 3, - "type_info": "Int64" - }, - { - "name": "transaction_amount", - "ordinal": 4, - "type_info": "Int64" - }, - { - "name": "transaction_fees", - "ordinal": 5, - "type_info": "Int64" - }, - { - "name": "memo", - "ordinal": 6, - "type_info": "Blob" - }, - { - "name": "transaction_originator_address", - "ordinal": 7, - "type_info": "Text" - }, - { - "name": "transaction_debit_address", - "ordinal": 8, - "type_info": "Text" - }, - { - "name": "transaction_credit_address", - "ordinal": 9, - "type_info": "Text" - } - ], - "nullable": [ - false, - false, - true, - false, - false, - false, - false, - false, - false, - false - ], - "parameters": { - "Right": 0 - } - }, - "query": "SELECT * FROM transactions" - }, - "41a748402641fa37e06f2cbfc36377f8c6be62e37df8020d3d7bdc3b8410bf9d": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Right": 10 - } - }, - "query": "\n REPLACE INTO transactions (\n txid, transaction_kind, transaction_block_height, transaction_deadline_block_height,\n transaction_amount, transaction_fees, memo, transaction_originator_address,\n transaction_debit_address, transaction_credit_address\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" - }, - "72cb8920f8bae1db08dd875bd985e8ecc7f2a606c54be05ae5b8a966e1a5cab4": { - "describe": { - "columns": [ - { - "name": "address", - "ordinal": 0, - "type_info": "Text" - } - ], - "nullable": [ - false - ], - "parameters": { - "Right": 0 - } - }, - "query": "SELECT address FROM auto_deny_addresses" - }, - "922dda0d16b61a25068183d3b7c7892ad15d72e175433a4e35cab33d0263acfb": { - "describe": { - "columns": [ - { - "name": "id", - "ordinal": 0, - "type_info": "Int64" - }, - { - "name": "secret_key", - "ordinal": 1, - "type_info": "Text" - }, - { - "name": "auto_approve_max_amount", - "ordinal": 2, - "type_info": "Int64" - }, - { - "name": "delegate_public_key", - "ordinal": 3, - "type_info": "Text" - } - ], - "nullable": [ - false, - false, - false, - false - ], - "parameters": { - "Right": 0 - } - }, - "query": "SELECT * FROM config" - }, - "cc0d2bf94abcb62747a8b8c697e24e2729f1e8d83d40f5eabbd1ab39332cfd3d": { - "describe": { - "columns": [ - { - "name": "txid", - "ordinal": 0, - "type_info": "Text" - }, - { - "name": "vote_status", - "ordinal": 1, - "type_info": "Text" - }, - { - "name": "vote_choice", - "ordinal": 2, - "type_info": "Text" - }, - { - "name": "vote_mechanism", - "ordinal": 3, - "type_info": "Text" - }, - { - "name": "target_consensus", - "ordinal": 4, - "type_info": "Int64" - }, - { - "name": "current_consensus", - "ordinal": 5, - "type_info": "Int64" - } - ], - "nullable": [ - false, - false, - true, - false, - false, - false - ], - "parameters": { - "Right": 1 - } - }, - "query": "SELECT * FROM votes WHERE txid = ?" - }, - "db1c2e502660d97175db877568e52b125bd588f3bf5c045df0d36314c9135219": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Right": 4 - } - }, - "query": "REPLACE INTO config (id, secret_key, delegate_public_key, auto_approve_max_amount) VALUES (?1, ?2, ?3, ?4)" - }, - "f8ba69ac8f881555a4987cc82bf4bf15326465c3446a4ffb87a3815fa4b8a118": { - "describe": { - "columns": [ - { - "name": "public_key", - "ordinal": 0, - "type_info": "Text" - } - ], - "nullable": [ - false - ], - "parameters": { - "Right": 0 - } - }, - "query": "SELECT public_key FROM delegator_public_keys" - }, - "ffea6f30b6abdfd408acd6b4bb8f823bd2ba26c25aca3d15dab3b483366da724": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Right": 1 - } - }, - "query": "REPLACE INTO delegator_public_keys (public_key) VALUES (?1)" - } -} \ No newline at end of file diff --git a/stacks-signer-api/src/conf/signer.toml b/stacks-signer-api/src/conf/signer.toml deleted file mode 100644 index 391b2778..00000000 --- a/stacks-signer-api/src/conf/signer.toml +++ /dev/null @@ -1,5 +0,0 @@ -secret_key = "26F85CE8B2C635AD92F6148E4443FE415F512F3F29F44AB0E2CBDA819295BBD5" -delegate_public_key = "025972a1f2532b44348501075075b31eb21c02eef276b91db99d30703f2081b773" -delgator_public_keys = [] -auto_deny_addresses = [] -auto_approve_max_amount = 100_000 \ No newline at end of file diff --git a/stacks-signer-api/src/config.rs b/stacks-signer-api/src/config.rs deleted file mode 100644 index e4e1394a..00000000 --- a/stacks-signer-api/src/config.rs +++ /dev/null @@ -1,108 +0,0 @@ -use secp256k1::{PublicKey, Secp256k1, SecretKey}; -use serde::{Deserialize, Serialize}; -use utoipa::{ToResponse, ToSchema}; - -const DEFAULT_MAX_AMOUNT: u64 = 100_000; - -/// Custom error type for this database module -#[derive(thiserror::Error, Debug)] -pub enum Error { - /// Config error due to an invalid secret key - #[error("Hex Error: {0}")] - HexError(#[from] hex::FromHexError), - /// Config error due to secp256k1 internal error - #[error("Secp256k1 Error: {0}")] - Secp256k1Error(#[from] secp256k1::Error), - /// Config error due to an IO error - #[error("IO Error: {0}")] - IOError(#[from] std::io::Error), - /// Config error due to toml deserialization error - #[error("Toml Error: {0}")] - TomlError(#[from] toml::de::Error), -} - -#[derive(serde::Deserialize)] -/// A raw signer configuration that can be deserialized from a TOML file. -pub struct RawConfig { - /// The signer's secret key. - pub secret_key: SecretKey, - /// The maximum dollar amount of a transaction that will be auto approved - pub delegate_public_key: Option, - /// The public keys of signers that this signer has agreed to sign on behalf of - pub delegator_public_keys: Option>, - /// The addresses to be auto denied - pub auto_deny_addresses: Option>, - /// The maximum dollar amount of a transaction that will be auto approved - pub auto_approve_max_amount: Option, -} - -impl RawConfig { - /// Try to create a raw configuration from a given path to a TOML file. - pub fn from_path(path: impl AsRef) -> Result { - let raw_config: RawConfig = toml::from_str(&std::fs::read_to_string(path)?)?; - Ok(raw_config) - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, ToResponse, ToSchema)] -/// A signer configuration. -pub struct Config { - /// The signer's secret key. - #[schema(value_type = String)] - pub secret_key: SecretKey, - /// The maximum dollar amount of a transaction that will be auto approved - pub auto_approve_max_amount: u64, - /// The public key of the signer being delegated to - #[schema(value_type = String)] - pub delegate_public_key: PublicKey, - /// The public keys of signers that this signer has agreed to sign on behalf of - #[schema(value_type = Vec)] - pub delegator_public_keys: Vec, - /// The addresses to be auto denied - pub auto_deny_addresses: Vec, -} - -impl Config { - /// Create a new signer configuration with a given secret key. - pub fn new(secret_key: SecretKey) -> Self { - let secp = Secp256k1::new(); - let public_key = PublicKey::from_secret_key(&secp, &secret_key); - Self { - secret_key, - delegate_public_key: public_key, - auto_approve_max_amount: DEFAULT_MAX_AMOUNT, - delegator_public_keys: vec![], - auto_deny_addresses: vec![], - } - } - - /// Try to create a new signer configuration with a given hex encoded secret key string. - pub fn from_secret_key(secret_key: &str) -> Result { - let secret_bytes = hex::decode(secret_key)?; - let secret_key = SecretKey::from_slice(&secret_bytes)?; - Ok(Self::new(secret_key)) - } - - /// Try to create a configuration from a given path to a TOML file. - pub fn from_path(path: impl AsRef) -> Result { - let raw_config = RawConfig::from_path(path)?; - Config::try_from(raw_config) - } -} - -impl TryFrom for Config { - type Error = Error; - fn try_from(raw_config: RawConfig) -> Result { - let secp = Secp256k1::new(); - let public_key = PublicKey::from_secret_key(&secp, &raw_config.secret_key); - Ok(Config { - secret_key: raw_config.secret_key, - delegate_public_key: raw_config.delegate_public_key.unwrap_or(public_key), - auto_approve_max_amount: raw_config - .auto_approve_max_amount - .unwrap_or(DEFAULT_MAX_AMOUNT), - delegator_public_keys: raw_config.delegator_public_keys.unwrap_or(vec![]), - auto_deny_addresses: raw_config.auto_deny_addresses.unwrap_or(vec![]), - }) - } -} diff --git a/stacks-signer-api/src/db/config.rs b/stacks-signer-api/src/db/config.rs deleted file mode 100644 index 72fda71d..00000000 --- a/stacks-signer-api/src/db/config.rs +++ /dev/null @@ -1,197 +0,0 @@ -use crate::{config::Config, db::Error}; - -use secp256k1::{PublicKey, SecretKey}; -use sqlx::SqlitePool; -use std::str::FromStr; - -/// Helper function for retriving a signer from the database given a signer ID. -pub async fn get_config(pool: &SqlitePool) -> Result { - let row = sqlx::query!("SELECT * FROM config").fetch_one(pool).await?; - let secret_bytes = hex::decode(row.secret_key)?; - Ok(Config { - secret_key: SecretKey::from_slice(&secret_bytes)?, - auto_approve_max_amount: row.auto_approve_max_amount as u64, - delegate_public_key: PublicKey::from_str(row.delegate_public_key.as_str())?, - delegator_public_keys: get_delegator_public_keys(pool).await?, - auto_deny_addresses: get_auto_deny_addresses(pool).await?, - }) -} - -/// Helper function for adding a signer to the database. -pub async fn update_config(pool: &SqlitePool, config: &Config) -> Result<(), Error> { - let secret_key = hex::encode(config.secret_key.secret_bytes()).to_string(); - let auto_approve_max_amount = config.auto_approve_max_amount as i64; - let delegate_public_key = config.delegate_public_key.to_string(); - sqlx::query!( - "REPLACE INTO config (id, secret_key, delegate_public_key, auto_approve_max_amount) VALUES (?1, ?2, ?3, ?4)", - 1, - secret_key, - delegate_public_key, - auto_approve_max_amount, - ) - .execute(pool) - .await?; - for delegator_public_key in &config.delegator_public_keys { - let delegator_public_key = delegator_public_key.to_string(); - sqlx::query!( - "REPLACE INTO delegator_public_keys (public_key) VALUES (?1)", - delegator_public_key, - ) - .execute(pool) - .await?; - } - for address in &config.auto_deny_addresses { - sqlx::query!( - "REPLACE INTO auto_deny_addresses (address) VALUES (?1)", - address, - ) - .execute(pool) - .await?; - } - Ok(()) -} - -/// Helper function for retrieving the auto deny addresses for a signer. -async fn get_auto_deny_addresses(pool: &SqlitePool) -> Result, Error> { - let addresses: Vec = sqlx::query!("SELECT address FROM auto_deny_addresses") - .fetch_all(pool) - .await? - .iter() - .map(|row| row.address.clone()) - .collect(); - Ok(addresses) -} - -/// Helper function for retrieving the delegator public keys for a signer. -async fn get_delegator_public_keys(pool: &SqlitePool) -> Result, Error> { - sqlx::query!("SELECT public_key FROM delegator_public_keys") - .fetch_all(pool) - .await? - .iter() - .map(|row| PublicKey::from_str(row.public_key.as_str()).map_err(Error::from)) - .collect() -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{config::Config, db::init_pool}; - - //const TEST_PUBLIC_KEY_1: &str = - // "025972a1f2532b44348501075075b31eb21c02eef276b91db99d30703f2081b773"; - const TEST_SECRET_KEY_1: &str = - "26F85CE8B2C635AD92F6148E4443FE415F512F3F29F44AB0E2CBDA819295BBD5"; - const TEST_PUBLIC_KEY_2: &str = - "039d3a5ea41730c84e3dd3b513a0a8349b2ed7d178fb026b7b771cea6c395b7870"; - const TEST_PUBLIC_KEY_3: &str = - "0355f69447d2fb4212c20360c67506656c39578ce1ccb0b2e5c1976edd5a51ea4d"; - - async fn init_db() -> SqlitePool { - init_pool(None) - .await - .expect("Failed to initialize a new database pool.") - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] - #[ntest::timeout(1000)] - async fn test_update_config() { - let pool = init_db().await; - let mut expected_config = - Config::from_secret_key(TEST_SECRET_KEY_1).expect("Failed to create config."); - // Insert an initial config: - update_config(&pool, &expected_config) - .await - .expect("failed to add config"); - let config = get_config(&pool).await.expect("Failed to get signers"); - assert_eq!( - config.auto_approve_max_amount, - expected_config.auto_approve_max_amount - ); - // Update the config and verify the database updated - expected_config.auto_approve_max_amount = 10; - update_config(&pool, &expected_config) - .await - .expect("failed to add config"); - - let config = get_config(&pool).await.expect("Failed to get signers"); - assert_eq!( - config.auto_approve_max_amount, - expected_config.auto_approve_max_amount - ); - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] - #[ntest::timeout(1000)] - async fn test_get_config() { - let pool = init_db().await; - // When the database is empty, it should fail to retrieve a config - assert!(get_config(&pool).await.is_err()); - - let expected_config = - Config::from_secret_key(TEST_SECRET_KEY_1).expect("Failed to create config."); - // Insert an initial config: - update_config(&pool, &expected_config) - .await - .expect("failed to add config"); - let config = get_config(&pool).await.expect("Failed to get signers"); - - assert_eq!(config.secret_key, expected_config.secret_key); - assert_eq!( - config.auto_approve_max_amount, - expected_config.auto_approve_max_amount - ); - assert_eq!( - config.delegate_public_key, - expected_config.delegate_public_key - ); - assert_eq!( - config.delegator_public_keys, - expected_config.delegator_public_keys - ); - assert_eq!( - config.auto_deny_addresses, - expected_config.auto_deny_addresses - ); - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] - #[ntest::timeout(1000)] - async fn test_get_delegator_public_keys() { - let pool = init_db().await; - let mut config = - Config::from_secret_key(TEST_SECRET_KEY_1).expect("Failed to create config."); - config.delegator_public_keys = vec![ - PublicKey::from_str(TEST_PUBLIC_KEY_2).expect("Failed to parse public key."), - PublicKey::from_str(TEST_PUBLIC_KEY_3).expect("Failed to parse public key."), - ]; - - update_config(&pool, &config) - .await - .expect("failed to add config"); - let keys = get_delegator_public_keys(&pool) - .await - .expect("failed to get delegator public keys"); - assert_eq!(keys, config.delegator_public_keys); - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] - #[ntest::timeout(1000)] - async fn test_get_auto_deny_addresses() { - let pool = init_db().await; - let mut config = - Config::from_secret_key(TEST_SECRET_KEY_1).expect("Failed to create config."); - config.auto_deny_addresses = vec![ - "address1".to_string(), - "address2".to_string(), - "address3".to_string(), - ]; - - update_config(&pool, &config) - .await - .expect("failed to add config"); - let addresses = get_auto_deny_addresses(&pool) - .await - .expect("failed to get auto deny addresses"); - assert_eq!(addresses, config.auto_deny_addresses); - } -} diff --git a/stacks-signer-api/src/db/mod.rs b/stacks-signer-api/src/db/mod.rs deleted file mode 100644 index 04edf5cb..00000000 --- a/stacks-signer-api/src/db/mod.rs +++ /dev/null @@ -1,48 +0,0 @@ -#![deny(missing_docs)] -/// Module that handles signers-related database operations -pub mod config; -/// Module that handles transaction-related database operations -pub mod transaction; -/// Module that handles vote-related database operations -pub mod vote; - -use parse_display::ParseError; -use sqlx::SqlitePool; - -/// Custom error type for this database module -#[derive(thiserror::Error, Debug)] -pub enum Error { - /// Sqlx related error - #[error("Sqlx Error: {0}")] - SqlxError(#[from] sqlx::Error), - /// Parse related error - #[error("Parsing error occurred due to malformed data")] - MalformedData(#[from] ParseError), - /// Secp256k1 related error - #[error("Secp256k1 Error: {0}")] - Secp256k1Error(#[from] secp256k1::Error), - /// Sqlx Migration Error - #[error("Sqlx Migration Error: {0}")] - SqlxMigrationError(#[from] sqlx::migrate::MigrateError), - /// Secret key parsing related error - #[error("Invalid Secret Key: {0}")] - InvalidSecretKey(#[from] hex::FromHexError), -} - -impl warp::reject::Reject for Error {} - -/// Initialize the database pool from the given file path or in memory if none is provided. -/// -/// # Params -/// * path: Option<&str> - Optional file path to the SQLite database, or None to use in-memory storage. -/// -/// # Returns -/// * Result: Result containing the initialized SqlitePool, or an Error if initialization failed. -pub async fn init_pool(path: Option) -> Result { - let pool = match path { - Some(path) => SqlitePool::connect(&path).await?, - None => SqlitePool::connect("sqlite::memory:").await?, - }; - sqlx::migrate!().run(&pool).await?; - Ok(pool) -} diff --git a/stacks-signer-api/src/db/transaction.rs b/stacks-signer-api/src/db/transaction.rs deleted file mode 100644 index a186506f..00000000 --- a/stacks-signer-api/src/db/transaction.rs +++ /dev/null @@ -1,138 +0,0 @@ -use crate::{ - db::Error, - transaction::{Transaction, TransactionAddress}, -}; - -use sqlx::SqlitePool; - -/// Add a given transaction to the database. -/// -/// # Params -/// * pool: SqlitePool - The reference to the SQLite database connection pool. -/// * transaction: Transaction - The transaction object to add to the database. -/// -/// # Returns -/// * Result<(), DatabaseError>: The result of the database operation. -pub async fn add_transaction(pool: &SqlitePool, transaction: &Transaction) -> Result<(), Error> { - let txid = transaction.txid.clone(); - let transaction_kind = &transaction.transaction_kind.to_string(); - let transaction_block_height = transaction - .transaction_block_height - .map(|height| height as i64); - let transaction_deadline_block_height = transaction.transaction_deadline_block_height as i64; - let transaction_amount = transaction.transaction_amount as i64; - let transaction_fees = transaction.transaction_fees as i64; - let memo = &transaction.memo; - let transaction_originator_address = ""; //&transaction.transaction_originator_address.0; - let transaction_debit_address = ""; //&transaction.transaction_debit_address.0; - let transaction_credit_address = ""; //&transaction.transaction_credit_address.0; - - sqlx::query!( - r#" - REPLACE INTO transactions ( - txid, transaction_kind, transaction_block_height, transaction_deadline_block_height, - transaction_amount, transaction_fees, memo, transaction_originator_address, - transaction_debit_address, transaction_credit_address - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"#, - txid, - transaction_kind, - transaction_block_height, - transaction_deadline_block_height, - transaction_amount, - transaction_fees, - memo, - transaction_originator_address, - transaction_debit_address, - transaction_credit_address - ) - .execute(pool) - .await?; - Ok(()) -} - -/// Get all transactions from the database. -/// -/// # Params -/// * pool: SqlitePool - The reference to the SQLite database connection pool. -/// -/// # Returns -/// * Result>: The transactions found in the database. -pub async fn get_transactions(pool: &SqlitePool) -> Result, Error> { - sqlx::query!("SELECT * FROM transactions") - .fetch_all(pool) - .await? - .iter() - .map(|row| { - let txid = row.txid.clone(); - let transaction_kind = row.transaction_kind.parse()?; - let transaction_block_height = row.transaction_block_height.map(|height| height as u64); - let transaction_deadline_block_height = row.transaction_deadline_block_height as u64; - let transaction_amount = row.transaction_amount as u64; - let transaction_fees = row.transaction_fees as u64; - let memo = row.memo.clone(); - let transaction_originator_address = - TransactionAddress::Bitcoin(row.transaction_originator_address.clone()); - - let transaction_debit_address = - TransactionAddress::Bitcoin(row.transaction_debit_address.clone()); - - let transaction_credit_address = - TransactionAddress::Bitcoin(row.transaction_credit_address.clone()); - - Ok(Transaction { - txid, - transaction_kind, - transaction_block_height, - transaction_deadline_block_height, - transaction_amount, - transaction_fees, - memo, - transaction_originator_address, - transaction_debit_address, - transaction_credit_address, - }) - }) - .collect() -} - -/// Get a transaction with a specific transaction ID from the database. -/// -/// # Params -/// * txid: String - The transaction ID to search for. -/// * pool: SqlitePool - The reference to the SQLite database connection pool. -/// -/// # Returns -/// * Result: The transaction found in the database. -pub async fn get_transaction_by_id(txid: &str, pool: &SqlitePool) -> Result { - let row = sqlx::query!("SELECT * FROM transactions WHERE txid = ?", txid) - .fetch_one(pool) - .await?; - let txid = row.txid.clone(); - let transaction_kind = row.transaction_kind.parse()?; - let transaction_block_height = row.transaction_block_height.map(|height| height as u64); - let transaction_deadline_block_height = row.transaction_deadline_block_height as u64; - let transaction_amount = row.transaction_amount as u64; - let transaction_fees = row.transaction_fees as u64; - let memo = row.memo.clone(); - let transaction_originator_address = - TransactionAddress::Bitcoin(row.transaction_originator_address.clone()); - - let transaction_debit_address = - TransactionAddress::Bitcoin(row.transaction_debit_address.clone()); - - let transaction_credit_address = - TransactionAddress::Bitcoin(row.transaction_credit_address.clone()); - - Ok(Transaction { - txid, - transaction_kind, - transaction_block_height, - transaction_deadline_block_height, - transaction_amount, - transaction_fees, - memo, - transaction_originator_address, - transaction_debit_address, - transaction_credit_address, - }) -} diff --git a/stacks-signer-api/src/db/vote.rs b/stacks-signer-api/src/db/vote.rs deleted file mode 100644 index 87689a7b..00000000 --- a/stacks-signer-api/src/db/vote.rs +++ /dev/null @@ -1,72 +0,0 @@ -use sqlx::SqlitePool; - -use crate::{ - db::Error, - vote::{Vote, VoteTally}, -}; - -/// Add a given vote to the database. -/// -/// # Params -/// * pool: SqlitePool - The reference to the SQLite database connection pool. -/// * vote: Vote - The vote object to add to the database. -/// -/// # Returns -/// * Result<(), Error>: The result of the database operation. -pub async fn add_vote(vote: &Vote, pool: &SqlitePool) -> Result<(), Error> { - let txid = vote.txid.clone(); - let vote_status = vote.vote_tally.vote_status.to_string(); - let vote_choice = vote.vote_choice.map(|choice| choice.to_string()); - let vote_mechanism = vote.vote_mechanism.to_string(); - let target_consenus = vote.vote_tally.target_consensus as i64; - let current_consensus = vote.vote_tally.current_consensus as i64; - sqlx::query!( - r#"REPLACE INTO votes ( - txid, vote_status, vote_choice, vote_mechanism, target_consensus, current_consensus - ) VALUES (?, ?, ?, ?, ?, ?);"#, - txid, - vote_status, - vote_choice, - vote_mechanism, - target_consenus, - current_consensus - ) - .execute(pool) - .await?; - Ok(()) -} - -/// Get a vote with a specific transaction ID from the database. -/// -/// # Params -/// * txid: String - The transaction ID to search for. -/// * pool: SqlitePool - The reference to the SQLite database connection pool. -/// -/// # Returns -/// * Result: The vote found in the database. -pub async fn get_vote_by_id(txid: &str, pool: &SqlitePool) -> Result { - let row = sqlx::query!("SELECT * FROM votes WHERE txid = ?", txid) - .fetch_one(pool) - .await?; - let txid = row.txid.clone(); - - let vote_status = row.vote_status.parse()?; - let vote_choice = row.vote_choice.map(|choice| choice.parse()).transpose()?; - - let vote_mechanism = row.vote_mechanism.parse()?; - - let target_consensus = row.target_consensus as u64; - let current_consensus = row.current_consensus as u64; - - let vote = Vote { - txid, - vote_tally: VoteTally { - vote_status, - target_consensus, - current_consensus, - }, - vote_choice, - vote_mechanism, - }; - Ok(vote) -} diff --git a/stacks-signer-api/src/error.rs b/stacks-signer-api/src/error.rs deleted file mode 100644 index 9d086f9c..00000000 --- a/stacks-signer-api/src/error.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::db::Error as DatabaseError; -use serde::{Deserialize, Serialize}; -use utoipa::{ToResponse, ToSchema}; -use warp::{http::StatusCode, reply::json, Reply}; - -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, ToSchema)] -/// The error code that ocurred -pub enum ErrorCode { - /// Database error - DatabaseError, - /// Signer not found - SignerNotFound, - /// Key not found - KeyNotFound, - /// Address not found - AddressNotFound, -} - -impl From for ErrorResponse { - fn from(e: DatabaseError) -> Self { - Self { - error: ErrorCode::DatabaseError, - message: Some(e.to_string()), - } - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, ToSchema, ToResponse)] -/// An error response -pub struct ErrorResponse { - /// The error code - pub error: ErrorCode, - /// A more detailed error message if available - pub message: Option, -} - -impl ErrorResponse { - /// Create new warp reply with the status code and error response - pub fn warp_reply(&self, status: StatusCode) -> Box { - Box::new(warp::reply::with_status(json(self), status)) - } -} diff --git a/stacks-signer-api/src/lib.rs b/stacks-signer-api/src/lib.rs deleted file mode 100644 index e6f3cf42..00000000 --- a/stacks-signer-api/src/lib.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![deny(missing_docs)] -/*! -# stacks-signer-api: an API for configuring and interacting with a Stacks signer. - -This library contains API calls to configure a signer to auto-sign transactions or manually sign a specific transaction upon request. - -Usage documentation can be found in the [README](https://github.com/Trust-Machines/core-eng/stacks-signer-api/README.md). -*/ -/// Signer configuration -pub mod config; -/// Sqlite database -pub mod db; -/// Signer API Errors -pub mod error; -/// Signer API Routes -pub mod routes; -/// Transactions -pub mod transaction; -/// Vote -pub mod vote; diff --git a/stacks-signer-api/src/main.rs b/stacks-signer-api/src/main.rs deleted file mode 100644 index ed5639a7..00000000 --- a/stacks-signer-api/src/main.rs +++ /dev/null @@ -1,390 +0,0 @@ -use clap::Parser; -use rand::Rng; -use sqlx::SqlitePool; -use stacks_signer_api::{ - config::Config, - db::{self, transaction::add_transaction, vote::add_vote}, - error::{ErrorCode, ErrorResponse}, - routes::all_routes, - transaction::{Transaction, TransactionAddress, TransactionKind, TransactionResponse}, - vote::{Vote, VoteChoice, VoteMechanism, VoteRequest, VoteResponse, VoteStatus, VoteTally}, -}; -use std::{ - env, - net::{IpAddr, SocketAddr}, - path::Path, - sync::Arc, -}; -use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; -use utoipa::OpenApi; -use utoipa_swagger_ui::Config as SwaggerConfig; -use warp::{ - http::Uri, - hyper::{Response, StatusCode}, - path::{FullPath, Tail}, - Filter, Rejection, Reply, -}; - -// Secret key used to generate a default signer config -const TEST_SECRET_KEY: &str = "26F85CE8B2C635AD92F6148E4443FE415F512F3F29F44AB0E2CBDA819295BBD5"; - -#[derive(OpenApi)] -#[openapi( - paths( - stacks_signer_api::routes::transactions::get_transaction_by_id, - stacks_signer_api::routes::transactions::get_transactions, - stacks_signer_api::routes::vote::vote, - stacks_signer_api::routes::config::get_config, - stacks_signer_api::routes::config::update_config, - ), - components( - schemas( - Transaction, - TransactionAddress, - TransactionKind, - TransactionResponse, - VoteResponse, - VoteChoice, - VoteMechanism, - VoteRequest, - VoteStatus, - VoteTally, - ErrorCode, - ErrorResponse, - Config - ), - responses(TransactionResponse, VoteResponse, Config, ErrorResponse) - ) -)] -struct ApiDoc; - -pub fn initiate_tracing_subscriber() { - tracing_subscriber::registry() - .with(tracing_subscriber::fmt::layer()) - .with(tracing_subscriber::EnvFilter::from_default_env()) - .init(); -} - -/// The available CLI subcommands -#[derive(clap::Subcommand, Debug, Clone)] -enum Command { - Docs(DocsArgs), - Swagger(SwaggerArgs), - Simulator(SimulatorArgs), - Run(RunArgs), -} - -#[derive(Parser, Debug, Clone)] -struct ServerArgs { - /// Port to run API server on - #[arg(short, long, default_value = "3030")] - pub port: u16, - /// Address to run API server on - #[arg(short, long, default_value = "0.0.0.0")] - pub address: IpAddr, -} - -#[derive(Parser, Debug, Clone)] -struct DocsArgs { - //Output file to save docs to. Prints to stdout if not provided - #[arg(long, short)] - pub output: Option, -} - -#[derive(Parser, Debug, Clone)] -struct SwaggerArgs { - /// Path of hosted open api doc file - #[arg(long, default_value = "/api-docs.json")] - pub path: String, - /// Port and Address to run Swagger UI server on - #[command(flatten)] - pub server: ServerArgs, -} - -#[derive(Parser, Debug, Clone)] -struct SimulatorArgs { - /// Port and address to run API server on - #[command(flatten)] - pub server: ServerArgs, -} - -#[derive(Parser, Debug, Clone)] -struct RunArgs { - /// Port and address to run API server on - #[command(flatten)] - pub server: ServerArgs, - /// Optional path to a signer configuration file - /// Required if env DATABASE_URL is not set or no config is found in the database - #[arg(short, long)] - pub config: Option, -} - -#[derive(Parser, Debug)] -#[command(author, version, about, long_about = None)] -struct Cli { - /// Subcommand action to take - #[command(subcommand)] - command: Command, -} - -async fn verify_config(pool: &SqlitePool, config_path: Option) -> anyhow::Result<()> { - let database_url = env::var("DATABASE_URL").ok(); - if database_url.is_none() && config_path.is_none() { - return Err(anyhow::anyhow!( - "No DATABASE_URL env variable set or config path provided" - )); - } - // If we have a config path, try to load the config from the file - if let Some(path) = config_path { - let config = Config::from_path(path) - .map_err(|e| anyhow::anyhow!("Failed to load config from file: {}", e))?; - db::config::update_config(pool, &config).await?; - } else { - db::config::get_config(pool).await.map_err(|_| { - anyhow::anyhow!( - "No configuration loaded in database. Must run with --config option set" - ) - })?; - } - Ok(()) -} - -async fn init_pool() -> anyhow::Result { - let _ = dotenv::dotenv(); - // Initialize the connection pool__ - let pool = db::init_pool(env::var("DATABASE_URL").ok()) - .await - .map_err(|e| anyhow::anyhow!("Failed to initialize database connection pool: {}", e))?; - Ok(pool) -} - -/// Run the Signer API server on the provided port and address -async fn run(pool: SqlitePool, server_args: ServerArgs) -> anyhow::Result<()> { - // Create the routes - let routes = all_routes(pool); - - // Run the warp server - let socket = SocketAddr::new(server_args.address, server_args.port); - println!("Serving warp server on {}", socket); - warp::serve(routes).run(socket).await; - Ok(()) -} - -/// Generate the OpenAPI json docs and save to file or print to stdout -fn generate_docs(output: &Option) -> anyhow::Result<()> { - let docs = ApiDoc::openapi(); - let openapi_json = docs - .to_pretty_json() - .map_err(|e| anyhow::anyhow!("Could not generate openapi json file: {}", e.to_string()))?; - if let Some(output_file) = output { - std::fs::write(output_file, openapi_json) - .map_err(|e| anyhow::anyhow!("Failed to write OpenAPI json docs to file: {}", e))?; - return Ok(()); - } - println!("{}", openapi_json); - Ok(()) -} - -/// Run the Signer API server with a database of simulated data -async fn run_simulator(args: SimulatorArgs) -> anyhow::Result<()> { - // Initialize the connection pool - let pool = init_pool().await?; - let config = Config::from_secret_key(TEST_SECRET_KEY) - .map_err(|e| anyhow::anyhow!("Failed to generate config from secret key: {}", e))?; - db::config::update_config(&pool, &config) - .await - .map_err(|e| anyhow::anyhow!("Failed to update config: {}", e))?; - let (txs, votes) = generate_txs_votes(); - for tx in txs { - add_transaction(&pool, &tx) - .await - .map_err(|e| anyhow::anyhow!("Failed to add tx: {}", e))?; - } - for vote in votes { - add_vote(&vote, &pool) - .await - .map_err(|e| anyhow::anyhow!("Failed to add vote: {}", e))?; - } - - run(pool, args.server).await -} - -/// Serve the Swagger UI page on the provided address and port using the generated OpenAPI json doc -async fn run_swagger(args: &SwaggerArgs) -> anyhow::Result<()> { - // Initialize the connection pool - let pool = init_pool().await?; - // Configure where we host the doc in swagger-ui - let path_buf = Path::new(&args.path); - let config = Arc::new(SwaggerConfig::from(args.path.clone())); - let file_name = path_buf - .file_name() - .ok_or(anyhow::anyhow!("Invalid file path provided."))? - .to_str() - .ok_or(anyhow::anyhow!("Invalid file path provided."))? - .to_string(); - let api_doc = warp::path(file_name) - .and(warp::get()) - .map(|| warp::reply::json(&ApiDoc::openapi())); - - let swagger_ui = warp::path("swagger-ui") - .and(warp::get()) - .and(warp::path::full()) - .and(warp::path::tail()) - .and(warp::any().map(move || config.clone())) - .and_then(serve_swagger); - - let socket = SocketAddr::new(args.server.address, args.server.port); - println!( - "Serving swagger UI on http://{}:{}/swagger-ui/", - args.server.address, args.server.port - ); - - warp::serve(api_doc.or(swagger_ui).or(all_routes(pool))) - .run(socket) - .await; - Ok(()) -} - -async fn serve_swagger( - full_path: FullPath, - tail: Tail, - config: Arc>, -) -> Result, Rejection> { - if full_path.as_str() == "/swagger-ui" { - return Ok(Box::new(warp::redirect::found(Uri::from_static( - "/swagger-ui/", - )))); - } - - let path = tail.as_str(); - match utoipa_swagger_ui::serve(path, config) { - Ok(file) => { - if let Some(file) = file { - Ok(Box::new( - Response::builder() - .header("Content-Type", file.content_type) - .body(file.bytes), - )) - } else { - Ok(Box::new(StatusCode::NOT_FOUND)) - } - } - Err(error) => Ok(Box::new( - Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(error.to_string()), - )), - } -} - -#[tokio::main] -async fn main() { - let cli = Cli::parse(); - - // First enable tracing - initiate_tracing_subscriber(); - - if let Err(error) = match cli.command { - Command::Docs(args) => generate_docs(&args.output), - Command::Swagger(args) => run_swagger(&args).await, - Command::Simulator(args) => run_simulator(args).await, - Command::Run(args) => { - // Initialize the connection pool - match init_pool().await { - Ok(pool) => { - if let Err(e) = verify_config(&pool, args.config).await { - println!("Error occurred running API server: {}", e); - return; - } - run(pool, args.server).await - } - Err(e) => Err(e), - } - } - } { - println!("Error occurred running command: {}", error); - } -} - -/// Generate some simulated transactions for mocked backend -fn generate_txs_votes() -> (Vec, Vec) { - let mut txs = vec![]; - let mut votes = vec![]; - for i in 0..10 { - let tx = generate_transaction(i); - votes.push(generate_vote(tx.txid.clone())); - txs.push(tx); - } - (txs, votes) -} - -fn generate_vote(txid: String) -> Vote { - let mut rng = rand::thread_rng(); - let vote_mechanism = if rng.gen_range(0..2) == 0 { - VoteMechanism::Auto - } else { - VoteMechanism::Manual - }; - let status = rng.gen_range(0..4); - let (vote_status, current_consensus) = if status == 0 { - (VoteStatus::Pending, rng.gen_range(1..55)) - } else if status == 1 { - (VoteStatus::Approved, rng.gen_range(70..100)) - } else if status == 2 { - (VoteStatus::Rejected, rng.gen_range(70..100)) - } else { - (VoteStatus::NoConsensus, rng.gen_range(1..69)) - }; - let choice = rng.gen_range(0..3); - let vote_choice = if vote_mechanism == VoteMechanism::Auto { - if choice == 0 { - Some(VoteChoice::Approve) - } else { - Some(VoteChoice::Reject) - } - } else if choice == 0 { - Some(VoteChoice::Approve) - } else if choice == 1 { - Some(VoteChoice::Reject) - } else { - None - }; - Vote { - txid, - vote_mechanism, - vote_tally: VoteTally { - current_consensus, - target_consensus: 70, - vote_status, - }, - vote_choice, - } -} - -fn generate_transaction(i: usize) -> Transaction { - let mut rng = rand::thread_rng(); - let rand_kind = rng.gen_range(0..4); - let transaction_kind = if rand_kind == 0 { - TransactionKind::DepositReveal - } else if rand_kind == 1 { - TransactionKind::WithdrawalReveal - } else if rand_kind == 2 { - TransactionKind::WithdrawalFulfill - } else { - TransactionKind::WalletHandoff - }; - let transaction_block_height = rng.gen(); - Transaction { - txid: hex::encode([i as u8; 32]), - transaction_kind, - transaction_block_height, - transaction_deadline_block_height: transaction_block_height.unwrap_or(0) - + rng.gen_range(1..10), - transaction_amount: rng.gen(), - transaction_fees: rng.gen_range(10..1000), - memo: vec![], - transaction_originator_address: TransactionAddress::Bitcoin("originator".to_string()), - transaction_debit_address: TransactionAddress::Bitcoin("escrow bitcoin wallet".to_string()), - transaction_credit_address: TransactionAddress::Stacks("sBTC protocol address".to_string()), - } -} diff --git a/stacks-signer-api/src/routes/config.rs b/stacks-signer-api/src/routes/config.rs deleted file mode 100644 index aa4f2f13..00000000 --- a/stacks-signer-api/src/routes/config.rs +++ /dev/null @@ -1,195 +0,0 @@ -use std::convert::Infallible; - -use crate::{ - config::Config, - db, - routes::{json_body, with_pool}, -}; -use sqlx::SqlitePool; -use warp::{hyper::StatusCode, Filter, Reply}; - -/// Route for updating the signer config. -/// -/// # Params -/// * pool: SqlitePool - The reference to the Sqlite database connection pool. -/// -/// # Returns -/// * impl Filter + Clone: -/// The Warp filter for the update_config_route endpoint for routing HTTP requests. -pub fn update_config_route( - pool: SqlitePool, -) -> impl Filter + Clone { - warp::post() - .and(warp::path!("v1" / "config")) - .and(warp::path::end()) - .and(json_body::()) - .and(with_pool(pool)) - .and_then(update_config) -} - -/// Route for fetching the signer's configuration. -/// -/// # Params -/// * pool: SqlitePool - The reference to the Sqlite database connection pool. -/// -/// # Returns -/// * impl Filter + Clone: -/// The Warp filter for the get_config_route endpoint for routing HTTP requests. -pub fn get_config_route( - pool: SqlitePool, -) -> impl Filter + Clone { - warp::get() - .and(warp::path!("v1" / "config")) - .and(warp::path::end()) - .and(with_pool(pool)) - .and_then(get_config) -} - -/// Update the signer's configuration. -#[utoipa::path( - post, - path = "/v1/config", - request_body = Config, - responses( - (status = OK, description = "Config updated successfully."), - (status = INTERNAL_SERVER_ERROR, description = "Internal server error occurred.", body = ErrorResponse) - ), -)] -pub async fn update_config(config: Config, pool: SqlitePool) -> Result, Infallible> { - if db::config::update_config(&pool, &config).await.is_ok() { - Ok(Box::new(StatusCode::OK)) - } else { - Ok(Box::new(StatusCode::NOT_FOUND)) - } -} - -/// Get the signer's configuration. -#[utoipa::path( - get, - path = "/v1/config", - responses( - (status = OK, description = "Config retrieved successfully.", body = Config), - (status = INTERNAL_SERVER_ERROR, description = "Internal server error occurred.", body = ErrorResponse) - ), -)] -pub async fn get_config(pool: SqlitePool) -> Result, Infallible> { - if let Ok(config) = db::config::get_config(&pool).await { - Ok(Box::new(warp::reply::with_status( - warp::reply::json(&config), - StatusCode::OK, - ))) - } else { - Ok(Box::new(StatusCode::NOT_FOUND)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::config::Config; - use crate::db::init_pool; - use secp256k1::PublicKey; - use std::str::FromStr; - - const TEST_PUBLIC_KEY_1: &str = - "025972a1f2532b44348501075075b31eb21c02eef276b91db99d30703f2081b773"; - const TEST_SECRET_KEY_1: &str = - "26F85CE8B2C635AD92F6148E4443FE415F512F3F29F44AB0E2CBDA819295BBD5"; - const TEST_PUBLIC_KEY_2: &str = - "039d3a5ea41730c84e3dd3b513a0a8349b2ed7d178fb026b7b771cea6c395b7870"; - - async fn init_db() -> SqlitePool { - init_pool(None) - .await - .expect("Failed to initialize a new database pool.") - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] - #[ntest::timeout(1000)] - async fn test_update_config() { - let pool = init_db().await; - let mut expected_config = - Config::from_secret_key(TEST_SECRET_KEY_1).expect("Failed to create config"); - // First add a config. - db::config::update_config(&pool, &expected_config) - .await - .expect("Failed to add configuration to database."); - - // Update the config and use the API to update the database - expected_config.auto_approve_max_amount = 10; - expected_config.delegator_public_keys = vec![ - PublicKey::from_str(TEST_PUBLIC_KEY_1).expect("Failed to create public key"), - PublicKey::from_str(TEST_PUBLIC_KEY_2).expect("Failed to create public key"), - ]; - expected_config.auto_deny_addresses = vec!["Address1".to_string(), "Address2".to_string()]; - - let api = warp::test::request() - .path("/v1/config") - .method("POST") - .json(&expected_config) - .reply(&update_config_route(pool.clone())) - .await; - - assert_eq!(api.status(), StatusCode::OK); - assert!(api.body().is_empty()); - - let config = db::config::get_config(&pool) - .await - .expect("Failed to get configuration from the database"); - assert_eq!(config.secret_key, expected_config.secret_key); - assert_eq!( - config.delegate_public_key, - expected_config.delegate_public_key - ); - assert_eq!( - config.auto_approve_max_amount, - expected_config.auto_approve_max_amount - ); - assert_eq!( - config.auto_deny_addresses, - expected_config.auto_deny_addresses - ); - assert_eq!( - config.delegator_public_keys, - expected_config.delegator_public_keys - ); - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] - #[ntest::timeout(1000)] - async fn test_get_config() { - let pool = init_db().await; - let expected_config = - Config::from_secret_key(TEST_SECRET_KEY_1).expect("Failed to create config"); - db::config::update_config(&pool, &expected_config) - .await - .expect("Failed to add configuration to database."); - - let api = warp::test::request() - .path("/v1/config") - .method("GET") - .header("content-type", "application/json") - .reply(&get_config_route(pool)) - .await; - let body = api.body(); - let config: Config = serde_json::from_slice(body).expect("failed to deserialize config"); - - assert_eq!(config.secret_key, expected_config.secret_key); - assert_eq!( - config.delegate_public_key, - expected_config.delegate_public_key - ); - assert_eq!( - config.auto_approve_max_amount, - expected_config.auto_approve_max_amount - ); - assert_eq!( - config.auto_deny_addresses, - expected_config.auto_deny_addresses - ); - assert_eq!( - config.delegator_public_keys, - expected_config.delegator_public_keys - ); - } -} diff --git a/stacks-signer-api/src/routes/mod.rs b/stacks-signer-api/src/routes/mod.rs deleted file mode 100644 index c8a2bfd6..00000000 --- a/stacks-signer-api/src/routes/mod.rs +++ /dev/null @@ -1,91 +0,0 @@ -#![deny(missing_docs)] -/// Config Routes -pub mod config; -/// Transaction Routes -pub mod transactions; -/// Vote Routes -pub mod vote; - -use serde::{de::DeserializeOwned, Deserialize}; -use sqlx::SqlitePool; -use std::convert::Infallible; -use warp::{Filter, Rejection, Reply}; - -use self::{ - config::{get_config_route, update_config_route}, - transactions::{get_transaction_by_id_route, get_transactions_route}, - vote::vote_route, -}; - -#[derive(Debug, Deserialize, Clone)] -/// The query parameters for get routes that return a vector of items. -pub struct Pagination { - /// The page number. - pub page: Option, - /// The limit of items per page. - pub limit: Option, -} - -/// Paginate a slice of items. -/// -/// This utility function slices a given set of items based on the specified `page` and `limit`. -/// If `page` and/or `limit` are not provided (None), the function will use default values. -/// -/// # Params -/// * items: &[T] - The reference to the slice of items to be paginated. -/// * page: Option - The optional page number for pagination (1-based index). -/// * limit: Option - The optional limit representing the maximum number of items per page. -/// -/// # Returns -/// * &[T]: A slice of the original items, paginated according to the provided page and limit. -pub fn paginate_items(items: &[T], page: Option, limit: Option) -> &[T] { - let page = page.unwrap_or(1); - let limit = limit.unwrap_or(usize::MAX); - - let start_index = items.len().min((page - 1) * limit); - let end_index = items.len().min(start_index + limit); - &items[start_index.min(end_index)..end_index] -} - -/// A helper function to extract a JSON body from a request. -pub fn json_body( -) -> impl Filter + Clone { - // When accepting a body, we want a JSON body - // (and to reject huge payloads)... - warp::body::content_length_limit(1024 * 16).and(warp::body::json::()) -} - -/// A helper function to extract a database pool from a request. -pub fn with_pool( - pool: SqlitePool, -) -> impl Filter + Clone { - warp::any().map(move || pool.clone()) -} - -/// A helper function to combine all routes into one Warp Filter -/// -/// # Params -/// * pool: SqlitePool - The reference to the Sqlite database connection pool. -/// -/// # Returns -/// * impl Filter + Clone: The Warp filter for the routes. -pub fn all_routes( - pool: SqlitePool, -) -> impl Filter + Clone { - // Set up the routes - // Config routes - let update_config_route = update_config_route(pool.clone()); - let get_config_route = get_config_route(pool.clone()); - // Transaction routes - let get_transactions_route = get_transactions_route(pool.clone()); - let get_transaction_by_id_route = get_transaction_by_id_route(pool.clone()); - // Vote routes - let vote_route = vote_route(pool); - - // Combine and return the routes in a single filter - update_config_route - .or(get_config_route) - .or(get_transactions_route) - .or(get_transaction_by_id_route) - .or(vote_route) -} diff --git a/stacks-signer-api/src/routes/transactions.rs b/stacks-signer-api/src/routes/transactions.rs deleted file mode 100644 index 84f83bf2..00000000 --- a/stacks-signer-api/src/routes/transactions.rs +++ /dev/null @@ -1,143 +0,0 @@ -use std::convert::Infallible; - -use crate::{ - db::{self, vote::get_vote_by_id}, - routes::{paginate_items, with_pool}, - transaction::TransactionResponse, - vote::VoteStatus, -}; -use serde::Deserialize; -use sqlx::SqlitePool; -use tracing::error; -use utoipa::IntoParams; -use warp::{hyper::StatusCode, Filter, Reply}; - -#[derive(Debug, Deserialize, IntoParams)] -#[into_params(parameter_in = Query)] -/// Query parameters for the transaction list -pub struct TransactionQuery { - /// The page number. - pub page: Option, - /// The limit of transactions per page. - pub limit: Option, - /// The transaction status to filter by. - pub status: Option, -} -/// Get transaction by id -#[utoipa::path( - get, - path = "/v1/transactions/{id}", - responses( - (status = 200, description = "Transaction found successfully", body = TransactionResponse), - (status = NOT_FOUND, description = "No transaction was found") - ), - params( - ("id" = String, Path, description = "Transaction id for retrieving a specific Transaction"), - ) -)] -async fn get_transaction_by_id(id: String, pool: SqlitePool) -> Result, Infallible> { - if let Ok(tx) = db::transaction::get_transaction_by_id(id.as_str(), &pool).await { - if let Ok(vote) = db::vote::get_vote_by_id(id.as_str(), &pool).await { - let tx_response = TransactionResponse { - transaction: tx, - vote_tally: vote.vote_tally, - vote_choice: vote.vote_choice, - vote_mechanism: vote.vote_mechanism, - }; - Ok(Box::new(warp::reply::with_status( - warp::reply::json(&tx_response), - StatusCode::OK, - ))) - } else { - error!( - "No vote found for transaction: {}. Database may be corrupted!", - id - ); - Ok(Box::new(StatusCode::INTERNAL_SERVER_ERROR)) - } - } else { - Ok(Box::new(StatusCode::NOT_FOUND)) - } -} - -/// Get list of all transactions -#[utoipa::path( -get, -path = "/v1/transactions", -responses( - (status = 200, description = "Transaction list returned succesfully", body = Vec), - (status = INTERNAL_SERVER_ERROR, description = "Internal server error") -), -params(TransactionQuery) -)] -async fn get_transactions( - query: TransactionQuery, - pool: SqlitePool, -) -> Result, Infallible> { - if query.page == Some(0) { - return Ok(Box::new(StatusCode::BAD_REQUEST)); - } - let mut filtered_transactions: Vec = vec![]; - if let Ok(txs) = db::transaction::get_transactions(&pool).await { - for tx in txs { - if let Ok(vote) = get_vote_by_id(&tx.txid, &pool).await { - let tx_response = TransactionResponse { - transaction: tx, - vote_tally: vote.vote_tally, - vote_mechanism: vote.vote_mechanism, - vote_choice: vote.vote_choice, - }; - if let Some(status) = query.status { - if vote.vote_tally.vote_status == status { - filtered_transactions.push(tx_response); - } - } else { - filtered_transactions.push(tx_response); - } - } else { - error!( - "Could not find cooresponding vote for transaction: {}. Database may be corrupted!", - tx.txid - ); - return Ok(Box::new(StatusCode::INTERNAL_SERVER_ERROR)); - } - } - let results = paginate_items(&filtered_transactions, query.page, query.limit); - Ok(Box::new(warp::reply::with_status( - warp::reply::json(&results), - StatusCode::OK, - ))) - } else { - Ok(Box::new(StatusCode::INTERNAL_SERVER_ERROR)) - } -} - -/// Route for getting a list of transactions. -/// -/// # Returns -/// * impl Filter + Clone: -/// The Warp filter for the get_transactions_route endpoint for routing HTTP requests. -pub fn get_transactions_route( - pool: SqlitePool, -) -> impl Filter + Clone { - warp::get() - .and(warp::path!("v1" / "transactions")) - .and(warp::path::end()) - .and(warp::query::()) - .and(with_pool(pool)) - .and_then(get_transactions) -} - -/// Route for getting a transaction by ID. -/// -/// # Returns -/// * impl Filter + Clone: -/// The Warp filter for the get_transaction_by_id_route endpoint for routing HTTP requests. -pub fn get_transaction_by_id_route( - pool: SqlitePool, -) -> impl Filter + Clone { - warp::get() - .and(warp::path!("v1" / "transactions" / String)) - .and(with_pool(pool)) - .and_then(get_transaction_by_id) -} diff --git a/stacks-signer-api/src/routes/vote.rs b/stacks-signer-api/src/routes/vote.rs deleted file mode 100644 index a0c17f7e..00000000 --- a/stacks-signer-api/src/routes/vote.rs +++ /dev/null @@ -1,66 +0,0 @@ -use std::convert::Infallible; - -use crate::{ - db::vote::{add_vote, get_vote_by_id}, - routes::{json_body, with_pool}, - vote::{VoteRequest, VoteResponse, VoteStatus}, -}; -use sqlx::SqlitePool; -use warp::{hyper::StatusCode, Filter, Reply}; - -/// Vote for a transaction -#[utoipa::path( - post, - path = "/v1/vote", - request_body = VoteRequest, - responses( - (status = OK, description = "Vote was cast.", body = VoteResponse), - (status = NOT_FOUND, description = "Requested transaction not found."), - (status = CONFLICT, description = "Vote has already been cast."), - (status = BAD_REQUEST, description = "Invalid vote."), - (status = FORBIDDEN, description = "Voting period has ended.") - ) -)] -async fn vote(vote_request: VoteRequest, pool: SqlitePool) -> Result, Infallible> { - if let Ok(mut vote) = get_vote_by_id(&vote_request.txid, &pool).await { - let vote_choice = vote_request.vote_choice; - if vote.vote_choice.is_some() { - return Ok(Box::new(StatusCode::CONFLICT)); - } - if vote.vote_tally.vote_status != VoteStatus::Pending { - return Ok(Box::new(StatusCode::FORBIDDEN)); - } - vote.vote_choice = Some(vote_choice); - // TODO: update consensus correctly - vote.vote_tally.current_consensus += 1; - if add_vote(&vote, &pool).await.is_ok() { - Ok(Box::new(warp::reply::with_status( - warp::reply::json(&VoteResponse { - vote_choice, - vote_tally: vote.vote_tally, - }), - StatusCode::OK, - ))) - } else { - Ok(Box::new(StatusCode::INTERNAL_SERVER_ERROR)) - } - } else { - Ok(Box::new(StatusCode::NOT_FOUND)) - } -} - -/// Route for voting to approve or reject a specific transaction. -/// -/// # Returns -/// * impl Filter + Clone: -/// The Warp filter for the get_transactions_route endpoint for routing HTTP requests. -pub fn vote_route( - pool: SqlitePool, -) -> impl Filter + Clone { - warp::post() - .and(warp::path!("v1" / "vote")) - .and(warp::path::end()) - .and(json_body::()) - .and(with_pool(pool)) - .and_then(vote) -} diff --git a/stacks-signer-api/src/transaction.rs b/stacks-signer-api/src/transaction.rs deleted file mode 100644 index 3aa6ca87..00000000 --- a/stacks-signer-api/src/transaction.rs +++ /dev/null @@ -1,91 +0,0 @@ -use parse_display::{Display, FromStr}; -use serde::{Deserialize, Serialize}; -use utoipa::{ToResponse, ToSchema}; - -use crate::vote::{VoteChoice, VoteMechanism, VoteTally}; - -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, ToSchema)] -/// The address of either a credit or debit transaction -pub enum TransactionAddress { - /// A Bitcoin address - Bitcoin(String), - /// A Stacks address - Stacks(String), -} - -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, ToSchema, Display, FromStr)] -#[serde(rename_all = "lowercase")] -#[display(style = "lowercase")] -/// The type of transaction being requested -pub enum TransactionKind { - /// A BTC to sBTC deposit reveal transaction - DepositReveal, - /// An sBTC to BTC withdrawal transaction - WithdrawalReveal, - /// A BTC withdrawal Fullfill transaction - WithdrawalFulfill, - /// A sBTC wallet handoff transaction - WalletHandoff, -} - -impl Default for Transaction { - fn default() -> Self { - Self { - txid: "0000000000000000000000000000000000000000000000000000000000000000".to_string(), - transaction_kind: TransactionKind::DepositReveal, - transaction_block_height: Default::default(), - transaction_deadline_block_height: Default::default(), - transaction_amount: Default::default(), - transaction_fees: Default::default(), - memo: Default::default(), - transaction_originator_address: TransactionAddress::Bitcoin("originator".to_string()), - transaction_debit_address: TransactionAddress::Bitcoin( - "escrow bitcoin wallet".to_string(), - ), - transaction_credit_address: TransactionAddress::Stacks( - "sBTC protocol address".to_string(), - ), - } - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, ToSchema)] -/// A transaction -pub struct Transaction { - /// The hexadecimal bitcoin transaction ID - pub txid: String, - /// The type of transaction being voted on - pub transaction_kind: TransactionKind, - /// The height of the Bitcoin block that mined the commit transaction. - pub transaction_block_height: Option, - /// The height of the Bitcoin block at which a vote is due - pub transaction_deadline_block_height: u64, - /// The amount of sats in the transaction - pub transaction_amount: u64, - /// The amount of sats in the fee subsidy - pub transaction_fees: u64, - /// A message from the user in the transaction. - pub memo: Vec, - /// Originating address - pub transaction_originator_address: TransactionAddress, - /// The address of the debit wallet - pub transaction_debit_address: TransactionAddress, - /// The address of the credit account - pub transaction_credit_address: TransactionAddress, -} - -#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, ToResponse, ToSchema)] -#[response( - description = "Transaction response returns single Transaction entity and its current vote status" -)] -/// The response returned from a transaction request -pub struct TransactionResponse { - /// The transaction - pub transaction: Transaction, - /// The current vote tally of the given transaction - pub vote_tally: VoteTally, - /// The vote choice - pub vote_choice: Option, - /// The vote mechanism used - pub vote_mechanism: VoteMechanism, -} diff --git a/stacks-signer-api/src/vote.rs b/stacks-signer-api/src/vote.rs deleted file mode 100644 index 92af7418..00000000 --- a/stacks-signer-api/src/vote.rs +++ /dev/null @@ -1,84 +0,0 @@ -use parse_display::{Display, FromStr}; -use serde::{Deserialize, Serialize}; -use utoipa::{ToResponse, ToSchema}; - -#[derive(FromStr, Display, Debug, PartialEq, Eq, Clone, Copy, Deserialize, Serialize, ToSchema)] -#[serde(rename_all = "lowercase")] -#[display(style = "lowercase")] -/// Vote options for a transaction ballot. -pub enum VoteChoice { - /// Approve the transaction. - Approve, - /// Reject the transaction - Reject, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Deserialize, Serialize, ToSchema, Display, FromStr)] -#[serde(rename_all = "lowercase")] -#[display(style = "lowercase")] -/// Mechanism by which a vote was cast -pub enum VoteMechanism { - /// The vote was cast automatically - Auto, - /// The vote was cast manually - Manual, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Deserialize, Serialize, ToSchema, Display, FromStr)] -#[serde(rename_all = "lowercase")] -#[display(style = "lowercase")] -/// The status of a transaction vote -pub enum VoteStatus { - /// The vote is incomplete and pending votes - Pending, - /// The vote is complete and the transaction is approved - Approved, - /// The vote is complete and the transaction rejected - Rejected, - /// The vote is complete, but consensus not reached - NoConsensus, -} - -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, ToSchema)] -/// A vote request for a transaction. -pub struct VoteRequest { - /// The hexadecimal transaction ID. - pub txid: String, - /// The public key of the signer delegator - pub signing_delegator: String, - /// The vote choice. - pub vote_choice: VoteChoice, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Deserialize, Serialize, ToResponse, ToSchema)] -/// A response for a cast vote. -pub struct VoteResponse { - /// The caller's vote - pub vote_choice: VoteChoice, - /// The vote's current status - pub vote_tally: VoteTally, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Deserialize, Serialize, ToSchema)] -/// The current vote tally for a transaction. -pub struct VoteTally { - /// The percentage votes required for consensus - pub target_consensus: u64, - /// the current consensus - pub current_consensus: u64, - /// the vote status - pub vote_status: VoteStatus, -} - -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] -/// The current vote info for a transaction -pub struct Vote { - /// The voted on hexadecimal transaction ID. - pub txid: String, - /// The vote tally. - pub vote_tally: VoteTally, - /// The vote choice. - pub vote_choice: Option, - /// The current vote mechanism of the vote choice - pub vote_mechanism: VoteMechanism, -} diff --git a/stacks-signer-mini/Cargo.toml b/stacks-signer-mini/Cargo.toml deleted file mode 100644 index f9236e0d..00000000 --- a/stacks-signer-mini/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "stacks-signer-mini" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/stacks-signer-mini/src/main.rs b/stacks-signer-mini/src/main.rs deleted file mode 100644 index e7a11a96..00000000 --- a/stacks-signer-mini/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/stacks-signer/conf/signer.toml b/stacks-signer/conf/signer.toml deleted file mode 100644 index f6221107..00000000 --- a/stacks-signer/conf/signer.toml +++ /dev/null @@ -1,10 +0,0 @@ -http_relay_url = "http://localhost:9776" -keys_threshold = 4 -frost_state_file = "frost.state.bin" -network_private_key = "9aSCCR6eirt1NAHwJtSz4HMwBHTyMo62SyPMvVDt5DQn" -signers = [ - {public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj", key_ids = [1, 2]}, - {public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj", key_ids = [3, 4]}, - {public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj", key_ids = [5, 6]} -] -coordinator_public_key = "22Rm48xUdpuTuva5gz9S7yDaaw9f8sjMcPSTHYVzPLNcj" \ No newline at end of file diff --git a/test-utils/src/lib.rs b/test-utils/src/lib.rs index c723f768..4c37272e 100644 --- a/test-utils/src/lib.rs +++ b/test-utils/src/lib.rs @@ -381,7 +381,7 @@ impl SignerHelper { ) -> (Vec, Point, bitcoin::PublicKey) { // DKG (Distributed Key Generation) - let public_commitments = dkg(&mut self.signers, &mut self.rng, merkle_root) + let public_commitments = dkg(&mut self.signers, &mut self.rng) .expect("Failed to run distributed key generation."); let group_public_key_point = public_commitments .iter() @@ -413,11 +413,9 @@ impl SignerHelper { let mut agg = SignatureAggregator::new(self.total, self.threshold, public_commitments) .expect("Failed to create signature aggregator."); - let sig = agg - .sign_taproot(message, &nonces, &shares, merkle_root) - .expect("Failed to create signature."); - SchnorrProof::new(&sig).expect("Failed to create Schnorr proof.") + agg.sign_taproot(message, &nonces, &shares, merkle_root) + .expect("Failed to create taproot schnorr proof.") } } diff --git a/test-vectors/Cargo.toml b/test-vectors/Cargo.toml deleted file mode 100644 index d6580d51..00000000 --- a/test-vectors/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "test-vectors" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -array-bytes.workspace = true -bitcoin = { version = "0.30.0", features = ["serde"] } -clap.workspace = true -secp256k1.workspace = true -sha256.workspace = true diff --git a/test-vectors/README.md b/test-vectors/README.md deleted file mode 100644 index 5c2d0af1..00000000 --- a/test-vectors/README.md +++ /dev/null @@ -1,7 +0,0 @@ - -# About - -Generates Test-vectors for various kinds of SBTC transactions. - -Each of the individual files has a public function for generating a test vector that returns a `bitcoin::Transaction` data type. -This can then be serialized into a hex-string using the `serialize_tx` public function defined in utils \ No newline at end of file diff --git a/test-vectors/src/lib.rs b/test-vectors/src/lib.rs deleted file mode 100644 index 8019df41..00000000 --- a/test-vectors/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod peg_handoff; -pub mod peg_in; -pub mod peg_out; -mod utils; - -// Re-exporting -pub use crate::utils::serialize_tx; diff --git a/test-vectors/src/main.rs b/test-vectors/src/main.rs deleted file mode 100644 index 5977a80a..00000000 --- a/test-vectors/src/main.rs +++ /dev/null @@ -1,57 +0,0 @@ -use bitcoin::consensus::encode::serialize; -use clap::Parser; - -fn main() { - let args = Args::parse(); - - let generate_test_vector = match (args.operation, args.protocol) { - (Operation::PegIn, Protocol::OpReturn) => test_vectors::peg_in::generate_peg_in_test_vector, - (Operation::PegIn, Protocol::CommitReveal) => { - test_vectors::peg_in::generate_peg_in_reveal_test_vector - } - (Operation::PegOutRequest, Protocol::OpReturn) => { - test_vectors::peg_out::generate_peg_out_request_test_vector - } - (Operation::PegOutRequest, Protocol::CommitReveal) => { - test_vectors::peg_out::generate_peg_out_request_reveal_test_vector - } - (Operation::PegHandoff, Protocol::OpReturn) => { - test_vectors::peg_handoff::generate_peg_handoff_test_vector - } - (Operation::PegHandoff, Protocol::CommitReveal) => { - unimplemented!() - } - }; - - let tx = generate_test_vector(); - let hex_tx = array_bytes::bytes2hex("", serialize(&tx)); - - println!("{}", hex_tx); -} - -/// Generates test vectors for sBTC bitcoin operations -#[derive(Parser, Debug)] -#[command(author, version, about, long_about = None)] -struct Args { - /// Which sBTC op to generate for - #[arg(value_enum)] - operation: Operation, - - /// Which wire format protocol to use - #[arg(value_enum)] - protocol: Protocol, -} - -#[allow(clippy::enum_variant_names)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)] -enum Operation { - PegIn, - PegOutRequest, - PegHandoff, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)] -enum Protocol { - OpReturn, - CommitReveal, -} diff --git a/test-vectors/src/peg_handoff.rs b/test-vectors/src/peg_handoff.rs deleted file mode 100644 index 00ba6015..00000000 --- a/test-vectors/src/peg_handoff.rs +++ /dev/null @@ -1,51 +0,0 @@ -use bitcoin::{ - opcodes, script::Builder, OutPoint, Script, Sequence, Transaction, TxIn, TxOut, Witness, -}; - -use crate::utils::generate_test_vector; - -pub fn generate_peg_handoff_test_vector() -> Transaction { - let input = TxIn { - previous_output: OutPoint::null(), - script_sig: Script::empty().into(), - sequence: Sequence(65535), - witness: Witness::new(), - }; - - let input = vec![input]; - - let peg_wallet_address = [4; 32]; - let reward_cycle = 67; - - let output = generate_peg_handoff_output(peg_wallet_address, reward_cycle); - - generate_test_vector(input, output) -} - -fn generate_peg_handoff_output(peg_wallet_address: [u8; 32], reward_cycle: u64) -> Vec { - let op_bytes = [105, 100, b'H']; - let padding_bytes = [0; 68]; - - let op_return_script = Builder::new() - .push_opcode(opcodes::all::OP_RETURN) - .push_slice::<[u8; 3]>(op_bytes) - .push_slice::<[u8; 8]>(reward_cycle.to_be_bytes()) - .push_slice::<[u8; 68]>(padding_bytes) - .into_script(); - - let peg_wallet_script = Builder::new() - .push_int(1) - .push_slice::<[u8; 32]>(peg_wallet_address) - .into_script(); - - vec![ - TxOut { - value: 0, - script_pubkey: op_return_script, - }, - TxOut { - value: 0, - script_pubkey: peg_wallet_script, - }, - ] -} diff --git a/test-vectors/src/peg_in.rs b/test-vectors/src/peg_in.rs deleted file mode 100644 index 0b7d5ebe..00000000 --- a/test-vectors/src/peg_in.rs +++ /dev/null @@ -1,173 +0,0 @@ -use std::iter::{once, repeat}; - -use bitcoin::{ - opcodes, - script::{Builder, PushBytes}, - OutPoint, Script, Sequence, Transaction, TxIn, TxOut, Witness, -}; - -use crate::utils::generate_test_vector; - -pub const C32_ADDRESS_VERSION_TESTNET_SINGLESIG: u8 = 26; // T - -pub fn generate_peg_in_test_vector() -> Transaction { - let input = TxIn { - previous_output: OutPoint::null(), - script_sig: Script::empty().into(), - sequence: Sequence::MAX, - witness: Witness::new(), - }; - - let input = vec![input]; - - let stacks_address = [1u8; 20]; - let current_peg_wallet = [4; 32]; - let amount = 1337; - - let output = generate_peg_in_output( - stacks_address, - current_peg_wallet, - C32_ADDRESS_VERSION_TESTNET_SINGLESIG, - amount, - ); - - generate_test_vector(input, output) -} - -/// Peg in reveal test vector with a taproot witness -pub fn generate_peg_in_reveal_test_vector() -> Transaction { - let mut input = TxIn { - previous_output: OutPoint::null(), - script_sig: Script::empty().into(), - sequence: Sequence::MAX, - witness: Witness::new(), - }; - - let stacks_address = [1u8; 20]; - let current_peg_wallet = [4; 32]; - let amount = 1337; - - let data = peg_in_data( - None, - stacks_address, - C32_ADDRESS_VERSION_TESTNET_SINGLESIG, - Some("sbtc-receiver-contract".to_string()), - None, - ); - let data_as_push_bytes: &PushBytes = data.as_slice().try_into().unwrap(); - - let witness_script = Builder::new() - .push_slice(data_as_push_bytes) - .push_opcode(opcodes::all::OP_DROP) - .push_opcode(opcodes::all::OP_DUP) - .push_opcode(opcodes::all::OP_HASH160) - .push_slice(current_peg_wallet) - .push_opcode(opcodes::all::OP_EQUAL) - .push_opcode(opcodes::all::OP_CHECKSIGVERIFY) - .into_script(); - - let witness = vec![witness_script.as_bytes().to_vec(), [60; 97].to_vec()]; - - input.witness = Witness::from_slice(&witness); - - let op_bytes = [105, 100, b'w']; - - let op_return_script = Builder::new() - .push_opcode(opcodes::all::OP_RETURN) - .push_slice(op_bytes) - .into_script(); - - let peg_wallet_script = Builder::new() - .push_int(1) - .push_slice(current_peg_wallet) - .into_script(); - - let output = vec![ - TxOut { - value: 0, - script_pubkey: op_return_script, - }, - TxOut { - value: amount, - script_pubkey: peg_wallet_script, - }, - ]; - - generate_test_vector(vec![input], output) -} - -fn generate_peg_in_output( - stacks_address: [u8; 20], - current_peg_wallet: [u8; 32], - stacks_address_version: u8, - amount: u64, -) -> Vec { - let op_bytes = [105, 100]; - - let data = peg_in_data( - op_bytes, - stacks_address, - stacks_address_version, - Some("sbtc-receiver-contract".to_string()), - None, - ); - let data_as_push_bytes: &PushBytes = data.as_slice().try_into().unwrap(); - - let op_return_script = Builder::new() - .push_opcode(opcodes::all::OP_RETURN) - .push_slice(data_as_push_bytes) - .into_script(); - - let peg_wallet_script = Builder::new() - .push_int(1) - .push_slice(current_peg_wallet) - .into_script(); - - vec![ - TxOut { - value: 0, - script_pubkey: op_return_script, - }, - TxOut { - value: amount, - script_pubkey: peg_wallet_script, - }, - ] -} - -fn peg_in_data( - magic_bytes: impl IntoIterator, - stacks_address: [u8; 20], - stacks_address_version: u8, - contract_name: Option, - maybe_fee_subsidy: Option, -) -> Vec { - let principal_byte: u8 = match contract_name { - Some(_) => 0x06, - None => 0x05, - }; - - magic_bytes - .into_iter() - .chain(once(b'<')) - .chain(once(principal_byte)) - .chain(once(stacks_address_version)) - .chain(stacks_address) - .chain( - contract_name - .map(|contract_name| { - once(contract_name.len() as u8).chain(contract_name.as_bytes().to_vec()) - }) - .into_iter() - .flatten(), - ) - .chain(repeat(0)) - .take(78) - .chain( - maybe_fee_subsidy - .map(|fee_subsidy| fee_subsidy.to_be_bytes().to_vec()) - .into_iter() - .flatten(), - ) - .collect() -} diff --git a/test-vectors/src/peg_out.rs b/test-vectors/src/peg_out.rs deleted file mode 100644 index 3e9f90e2..00000000 --- a/test-vectors/src/peg_out.rs +++ /dev/null @@ -1,203 +0,0 @@ -use std::iter::{once, repeat}; - -use bitcoin::{ - opcodes, - script::{Builder, PushBytes}, - OutPoint, Script, Sequence, Transaction, TxIn, TxOut, Witness, -}; -use secp256k1::SecretKey; - -use crate::utils::{generate_signature, generate_test_vector}; - -pub fn generate_peg_out_request_test_vector() -> Transaction { - let input = TxIn { - previous_output: OutPoint::null(), - script_sig: Script::empty().into(), - sequence: Sequence(65535), - witness: Witness::new(), - }; - - let input = vec![input]; - - // Arbitrary key, copy-pasted from src/chainstate/stacks/tests/accounting.rs - let secret_key_hex = "42faca653724860da7a41bfcef7e6ba78db55146f6900de8cb2a9f760ffac70c01"; - let secret_key_vec = array_bytes::hex2bytes(secret_key_hex).unwrap(); - let secret_key = SecretKey::from_slice(&secret_key_vec[..32]).unwrap(); - - let peg_wallet_address = [4; 32]; - let recipient_address = [5; 20]; - let amount = 1337; - let fulfillment_fee = 42; - let dust_amount = 21; - - let output = generate_peg_out_request_output( - amount, - secret_key, - peg_wallet_address, - recipient_address, - fulfillment_fee, - dust_amount, - ); - - generate_test_vector(input, output) -} - -pub fn generate_peg_out_request_reveal_test_vector() -> Transaction { - // Arbitrary key, copy-pasted from src/chainstate/stacks/tests/accounting.rs - let secret_key_hex = "42faca653724860da7a41bfcef7e6ba78db55146f6900de8cb2a9f760ffac70c01"; - let secret_key_vec = array_bytes::hex2bytes(secret_key_hex).unwrap(); - let secret_key = SecretKey::from_slice(&secret_key_vec[..32]).unwrap(); - - let peg_wallet_address = [4; 32]; - let recipient_address = [5; 20]; - let amount = 1337_u64; - let fulfillment_fee = 42; - let dust_amount = 21; - - let mut input = TxIn { - previous_output: OutPoint::null(), - script_sig: Script::empty().into(), - sequence: Sequence::MAX, - witness: Witness::new(), - }; - - let recipient_script = Builder::new() - .push_opcode(opcodes::all::OP_DUP) - .push_opcode(opcodes::all::OP_HASH160) - .push_slice(recipient_address) - .push_opcode(opcodes::all::OP_EQUAL) - .push_opcode(opcodes::all::OP_CHECKSIGVERIFY) - .into_script(); - - let mut msg = amount.to_be_bytes().to_vec(); - msg.extend_from_slice(recipient_script.as_bytes()); - - let (recovery_id, signature) = generate_signature(msg, secret_key); - - let data = peg_out_request_data(None, amount, recovery_id.to_i32() as u8, signature, None); - let data_as_push_bytes: &PushBytes = data.as_slice().try_into().unwrap(); - - let witness_script = Builder::new() - .push_slice(data_as_push_bytes) - .push_opcode(opcodes::all::OP_DROP) - .push_opcode(opcodes::all::OP_DUP) - .push_opcode(opcodes::all::OP_HASH160) - .push_slice(peg_wallet_address) - .push_opcode(opcodes::all::OP_EQUAL) - .push_opcode(opcodes::all::OP_CHECKSIGVERIFY) - .into_script(); - - let witness = vec![witness_script.as_bytes().to_vec(), [60; 97].to_vec()]; - - input.witness = Witness::from_slice(&witness); - - let op_bytes = [105, 100, b'w']; - - let op_return_script = Builder::new() - .push_opcode(opcodes::all::OP_RETURN) - .push_slice(op_bytes) - .into_script(); - - let peg_wallet_script = Builder::new() - .push_int(1) - .push_slice(peg_wallet_address) - .into_script(); - - let output = vec![ - TxOut { - value: 0, - script_pubkey: op_return_script, - }, - TxOut { - value: dust_amount, - script_pubkey: recipient_script.clone(), - }, - TxOut { - value: fulfillment_fee, - script_pubkey: peg_wallet_script, - }, - ]; - - generate_test_vector(vec![input], output) -} - -fn generate_peg_out_request_output( - amount: u64, - secret_key: SecretKey, - peg_wallet_address: [u8; 32], - recipient_address: [u8; 20], - fulfillment_fee: u64, - dust_amount: u64, -) -> Vec { - let recipient_script = Builder::new() - .push_opcode(opcodes::all::OP_DUP) - .push_opcode(opcodes::all::OP_HASH160) - .push_slice(recipient_address) - .push_opcode(opcodes::all::OP_EQUAL) - .push_opcode(opcodes::all::OP_CHECKSIGVERIFY) - .into_script(); - - let mut msg = amount.to_be_bytes().to_vec(); - msg.extend_from_slice(recipient_script.as_bytes()); - - let op_bytes = [105, 100]; - let (recovery_id, signature) = generate_signature(msg, secret_key); - - let data = peg_out_request_data( - op_bytes, - amount, - recovery_id.to_i32() as u8, - signature, - None, - ); - let data_as_push_bytes: &PushBytes = data.as_slice().try_into().unwrap(); - - let op_return_script = Builder::new() - .push_opcode(opcodes::all::OP_RETURN) - .push_slice(data_as_push_bytes) - .into_script(); - - let peg_wallet_script = Builder::new() - .push_int(1) - .push_slice(peg_wallet_address) - .into_script(); - - vec![ - TxOut { - value: 0, - script_pubkey: op_return_script, - }, - TxOut { - value: dust_amount, - script_pubkey: recipient_script, - }, - TxOut { - value: fulfillment_fee, - script_pubkey: peg_wallet_script, - }, - ] -} - -fn peg_out_request_data( - magic_bytes: impl IntoIterator, - amount: u64, - recovery_id: u8, - signature_bytes: [u8; 64], - maybe_fee_subsidy: Option, -) -> Vec { - magic_bytes - .into_iter() - .chain(once(b'>')) - .chain(amount.to_be_bytes()) - .chain(once(recovery_id)) - .chain(signature_bytes) - .chain(repeat(0)) - .take(78) - .chain( - maybe_fee_subsidy - .map(|fee_subsidy| fee_subsidy.to_be_bytes().to_vec()) - .into_iter() - .flatten(), - ) - .collect() -} diff --git a/test-vectors/src/utils.rs b/test-vectors/src/utils.rs deleted file mode 100644 index bf5f226c..00000000 --- a/test-vectors/src/utils.rs +++ /dev/null @@ -1,28 +0,0 @@ -use bitcoin::{consensus::encode::serialize, Transaction, TxIn, TxOut}; -use secp256k1::{ecdsa::RecoveryId, Message, SecretKey}; - -/// Creates a serialized hex string from a `Transaction` struct -pub fn serialize_tx(tx: Transaction) -> String { - array_bytes::bytes2hex("", serialize(&tx)) -} - -/// Generates a Recovery Id and a Signature from a msg and a secret key -pub fn generate_signature(msg: Vec, secret_key: SecretKey) -> (RecoveryId, [u8; 64]) { - let msg_hash = sha256::digest(msg.as_slice()); - let msg_hash_bytes = array_bytes::hex2bytes(msg_hash).unwrap(); - let msg_ecdsa = Message::from_slice(&msg_hash_bytes).unwrap(); - - secp256k1::Secp256k1::new() - .sign_ecdsa_recoverable(&msg_ecdsa, &secret_key) - .serialize_compact() -} - -/// Generates a test vector from a series of inputs and outputs -pub fn generate_test_vector(input: Vec, output: Vec) -> Transaction { - bitcoin::Transaction { - version: 2, - lock_time: bitcoin::absolute::LockTime::ZERO, - input, - output, - } -} diff --git a/utils/automations/fund-btc.sh b/utils/automations/fund-btc.sh new file mode 100644 index 00000000..34dd62a7 --- /dev/null +++ b/utils/automations/fund-btc.sh @@ -0,0 +1,2 @@ +# fund-btc.sh +cat fund.sh | docker exec -i bitcoin-node.project-name.devnet sh \ No newline at end of file diff --git a/utils/automations/fund.sh b/utils/automations/fund.sh new file mode 100644 index 00000000..659fe1a1 --- /dev/null +++ b/utils/automations/fund.sh @@ -0,0 +1,13 @@ +# fund.sh + +bitcoin-cli -regtest -rpcpassword=devnet -rpcuser=devnet generatetoaddress 1 bcrt1phvt5tfz4hlkth0k7ls9djweuv9rwv5a0s5sa9085umupftnyalxq0zx28d # signer 1 +bitcoin-cli -regtest -rpcpassword=devnet -rpcuser=devnet generatetoaddress 1 bcrt1pdsavc4yrdq0sdmjcmf7967eeem2ny6vzr4f8m7dyemcvncs0xtwsc85zdq # signer 2 +bitcoin-cli -regtest -rpcpassword=devnet -rpcuser=devnet generatetoaddress 101 bcrt1pa4w2alr6x7tuxwg7anpep90rzn09sxhwppxvq9xt99p20lwqmlcqz9q8e3 # signer 3 + +bitcoin-cli -regtest -rpcpassword=devnet -rpcuser=devnet generatetoaddress 1 BCRT1P8M4KHK8A06CUCAWGPPQ3GDKEXTH7MZF6DGW54KZ67TKFQ3RCUU5QNKHUDG # signer 1 tweaked +bitcoin-cli -regtest -rpcpassword=devnet -rpcuser=devnet generatetoaddress 1 BCRT1P6HNYZU0USW758H2F04GMWDGYWXAK9PAMA9S7AQ7NZEMXVGG0JTHSN9DNZN # signer 2 tweaked +bitcoin-cli -regtest -rpcpassword=devnet -rpcuser=devnet generatetoaddress 101 BCRT1P6DDX97EN6YMWLHCF4RA7WK3KEXD0U5DPCEV2YTXLRXFZMVZ67GUQDLARFQ # signer 3 tweaked + +bitcoin-cli -regtest -rpcpassword=devnet -rpcuser=devnet generatetoaddress 1 BCRT1PQCHMH7V5KW2F0XYR6Y6S6C7A68MQUFS6MSTS34HWUE8GQXVRN7QS5V6AFV # script address - signer 1 +bitcoin-cli -regtest -rpcpassword=devnet -rpcuser=devnet generatetoaddress 1 BCRT1PTW0UU2GHXUSFTV5AL8AKQKLZ34Y69H4PNL5057CD3328Q842K9XSMEE5S3 # script address - signer 2 +bitcoin-cli -regtest -rpcpassword=devnet -rpcuser=devnet generatetoaddress 101 BCRT1PRAFYSZ9H3CYLVR9YWJD28NNYPPFGK0P6PKKXFU9VNKE02ND8WRRS596P3F # script address - signer 3 \ No newline at end of file diff --git a/frost-test/Cargo.toml b/utils/frost-test/Cargo.toml similarity index 78% rename from frost-test/Cargo.toml rename to utils/frost-test/Cargo.toml index 01480986..c248ce96 100644 --- a/frost-test/Cargo.toml +++ b/utils/frost-test/Cargo.toml @@ -7,9 +7,9 @@ license = "GPLv3" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -relay-server = { path = "../relay-server" } -yarpc = { path = "../yarpc" } -frost-coordinator = { path = "../frost-coordinator" } +relay-server = { path = "../../relay-server" } +yarpc = { path = "../../yarpc" } +degen-base-coordinator = { path = "../../degen-base-coordinator" } frost-signer = { path = "../frost-signer" } rand_core = { workspace = true } hashbrown = { workspace = true } diff --git a/frost-test/docs/frost-btc.md b/utils/frost-test/docs/frost-btc.md similarity index 100% rename from frost-test/docs/frost-btc.md rename to utils/frost-test/docs/frost-btc.md diff --git a/frost-test/src/lib.rs b/utils/frost-test/src/lib.rs similarity index 100% rename from frost-test/src/lib.rs rename to utils/frost-test/src/lib.rs diff --git a/frost-test/src/sync_test.rs b/utils/frost-test/src/sync_test.rs similarity index 96% rename from frost-test/src/sync_test.rs rename to utils/frost-test/src/sync_test.rs index 9c4d7118..c5137341 100644 --- a/frost-test/src/sync_test.rs +++ b/utils/frost-test/src/sync_test.rs @@ -1,6 +1,6 @@ #[cfg(test)] mod tests { - use frost_signer::signing_round::SigningRound; + use degen_base_signer::signing_round::SigningRound; use relay_server::Server; use yarpc::http::{Call, Method, Request}; diff --git a/frost-test/src/v1.rs b/utils/frost-test/src/v1.rs similarity index 100% rename from frost-test/src/v1.rs rename to utils/frost-test/src/v1.rs diff --git a/frost-test/tests/frost-btc.rs b/utils/frost-test/tests/frost-btc.rs similarity index 100% rename from frost-test/tests/frost-btc.rs rename to utils/frost-test/tests/frost-btc.rs diff --git a/utils/frost-test/tests/pure_frost.rs b/utils/frost-test/tests/pure_frost.rs new file mode 100644 index 00000000..d7c67ac7 --- /dev/null +++ b/utils/frost-test/tests/pure_frost.rs @@ -0,0 +1,31 @@ +use rand_core::OsRng; +use wsts::taproot::test_helpers::{dkg, sign}; +use wsts::v1::{self, SignatureAggregator}; + +#[test] +#[allow(non_snake_case)] +fn pure_frost_test() { + const MSG: &[u8] = "It was many and many a year ago".as_bytes(); + let T = 3; + let N = 4; + let mut rng = OsRng; + let mut signers = [ + v1::Signer::new(1, &[0, 1], N, T, &mut rng), + v1::Signer::new(2, &[2], N, T, &mut rng), + v1::Signer::new(3, &[3], N, T, &mut rng), + ]; + + // DKG (Distributed Key Generation) + let A = dkg(&mut signers[..], &mut rng).unwrap(); + + // signing. Signers: 0 (parties: 0, 1) and 1 (parties: 2) + let mut signers = [signers[0].clone(), signers[1].clone()]; + + // get nonces and shares + let (nonces, shares) = sign(MSG, &mut signers, &mut rng, None); + + SignatureAggregator::new(N, T, A.clone()) + .unwrap() + .sign_taproot(&MSG, &nonces, &shares, None) + .expect("failed to create valid schnorr proof"); +} diff --git a/misc/btctool/btctool/__init__.py b/utils/misc/__init__.py similarity index 100% rename from misc/btctool/btctool/__init__.py rename to utils/misc/__init__.py diff --git a/misc/bitcoin_rpc_scripts/createwallet.sh b/utils/misc/bitcoin_rpc_scripts/createwallet.sh similarity index 100% rename from misc/bitcoin_rpc_scripts/createwallet.sh rename to utils/misc/bitcoin_rpc_scripts/createwallet.sh diff --git a/misc/bitcoin_rpc_scripts/getdescriptorinfo.sh b/utils/misc/bitcoin_rpc_scripts/getdescriptorinfo.sh similarity index 100% rename from misc/bitcoin_rpc_scripts/getdescriptorinfo.sh rename to utils/misc/bitcoin_rpc_scripts/getdescriptorinfo.sh diff --git a/misc/bitcoin_rpc_scripts/importaddress.sh b/utils/misc/bitcoin_rpc_scripts/importaddress.sh similarity index 100% rename from misc/bitcoin_rpc_scripts/importaddress.sh rename to utils/misc/bitcoin_rpc_scripts/importaddress.sh diff --git a/misc/bitcoin_rpc_scripts/listunspent.sh b/utils/misc/bitcoin_rpc_scripts/listunspent.sh similarity index 100% rename from misc/bitcoin_rpc_scripts/listunspent.sh rename to utils/misc/bitcoin_rpc_scripts/listunspent.sh diff --git a/misc/bitcoin_rpc_scripts/listwallets.sh b/utils/misc/bitcoin_rpc_scripts/listwallets.sh similarity index 100% rename from misc/bitcoin_rpc_scripts/listwallets.sh rename to utils/misc/bitcoin_rpc_scripts/listwallets.sh diff --git a/misc/bitcoin_rpc_scripts/unloadwallet.sh b/utils/misc/bitcoin_rpc_scripts/unloadwallet.sh similarity index 100% rename from misc/bitcoin_rpc_scripts/unloadwallet.sh rename to utils/misc/bitcoin_rpc_scripts/unloadwallet.sh diff --git a/misc/btctool/.gitignore b/utils/misc/btctool/.gitignore similarity index 100% rename from misc/btctool/.gitignore rename to utils/misc/btctool/.gitignore diff --git a/misc/btctool/Dockerfile b/utils/misc/btctool/Dockerfile similarity index 100% rename from misc/btctool/Dockerfile rename to utils/misc/btctool/Dockerfile diff --git a/misc/btctool/Makefile b/utils/misc/btctool/Makefile similarity index 100% rename from misc/btctool/Makefile rename to utils/misc/btctool/Makefile diff --git a/misc/btctool/README.md b/utils/misc/btctool/README.md similarity index 100% rename from misc/btctool/README.md rename to utils/misc/btctool/README.md diff --git a/utils/misc/btctool/__init__.py b/utils/misc/btctool/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/misc/btctool/btctool-cli b/utils/misc/btctool/btctool-cli similarity index 100% rename from misc/btctool/btctool-cli rename to utils/misc/btctool/btctool-cli diff --git a/utils/misc/btctool/btctool/__init__.py b/utils/misc/btctool/btctool/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/misc/btctool/btctool/btctool.py b/utils/misc/btctool/btctool/btctool.py similarity index 100% rename from misc/btctool/btctool/btctool.py rename to utils/misc/btctool/btctool/btctool.py diff --git a/misc/btctool/btctool/commands.py b/utils/misc/btctool/btctool/commands.py similarity index 100% rename from misc/btctool/btctool/commands.py rename to utils/misc/btctool/btctool/commands.py diff --git a/misc/btctool/btctool/meat.py b/utils/misc/btctool/btctool/meat.py similarity index 99% rename from misc/btctool/btctool/meat.py rename to utils/misc/btctool/btctool/meat.py index 3509ed13..ff8dea41 100644 --- a/misc/btctool/btctool/meat.py +++ b/utils/misc/btctool/btctool/meat.py @@ -1,5 +1,3 @@ -import json - import click import pandas as pd from trezorlib.protobuf import to_dict diff --git a/misc/btctool/btctool/sign.py b/utils/misc/btctool/btctool/sign.py similarity index 100% rename from misc/btctool/btctool/sign.py rename to utils/misc/btctool/btctool/sign.py diff --git a/misc/btctool/btctool/tx_utils.py b/utils/misc/btctool/btctool/tx_utils.py similarity index 100% rename from misc/btctool/btctool/tx_utils.py rename to utils/misc/btctool/btctool/tx_utils.py diff --git a/misc/btctool/btctool/utils.py b/utils/misc/btctool/btctool/utils.py similarity index 100% rename from misc/btctool/btctool/utils.py rename to utils/misc/btctool/btctool/utils.py diff --git a/misc/btctool/btctool/utxo_utils.py b/utils/misc/btctool/btctool/utxo_utils.py similarity index 100% rename from misc/btctool/btctool/utxo_utils.py rename to utils/misc/btctool/btctool/utxo_utils.py diff --git a/misc/btctool/devenv-install.sh b/utils/misc/btctool/devenv-install.sh similarity index 100% rename from misc/btctool/devenv-install.sh rename to utils/misc/btctool/devenv-install.sh diff --git a/misc/btctool/devenv-run.sh b/utils/misc/btctool/devenv-run.sh similarity index 100% rename from misc/btctool/devenv-run.sh rename to utils/misc/btctool/devenv-run.sh diff --git a/misc/btctool/docker-deploy.sh b/utils/misc/btctool/docker-deploy.sh similarity index 100% rename from misc/btctool/docker-deploy.sh rename to utils/misc/btctool/docker-deploy.sh diff --git a/misc/btctool/docker-run.sh b/utils/misc/btctool/docker-run.sh similarity index 100% rename from misc/btctool/docker-run.sh rename to utils/misc/btctool/docker-run.sh diff --git a/misc/btctool/poetry.lock b/utils/misc/btctool/poetry.lock similarity index 100% rename from misc/btctool/poetry.lock rename to utils/misc/btctool/poetry.lock diff --git a/misc/btctool/pyproject.toml b/utils/misc/btctool/pyproject.toml similarity index 100% rename from misc/btctool/pyproject.toml rename to utils/misc/btctool/pyproject.toml diff --git a/misc/btctool/release.sh b/utils/misc/btctool/release.sh similarity index 100% rename from misc/btctool/release.sh rename to utils/misc/btctool/release.sh diff --git a/sbtc-mini/.gitignore b/utils/sbtc-mini/.gitignore similarity index 100% rename from sbtc-mini/.gitignore rename to utils/sbtc-mini/.gitignore diff --git a/sbtc-mini/Clarinet.toml b/utils/sbtc-mini/Clarinet.toml similarity index 100% rename from sbtc-mini/Clarinet.toml rename to utils/sbtc-mini/Clarinet.toml diff --git a/sbtc-mini/README.md b/utils/sbtc-mini/README.md similarity index 100% rename from sbtc-mini/README.md rename to utils/sbtc-mini/README.md diff --git a/sbtc-mini/contracts/clarity-bitcoin.clar b/utils/sbtc-mini/contracts/clarity-bitcoin.clar similarity index 100% rename from sbtc-mini/contracts/clarity-bitcoin.clar rename to utils/sbtc-mini/contracts/clarity-bitcoin.clar diff --git a/sbtc-mini/contracts/pox-3.clar b/utils/sbtc-mini/contracts/pox-3.clar similarity index 100% rename from sbtc-mini/contracts/pox-3.clar rename to utils/sbtc-mini/contracts/pox-3.clar diff --git a/sbtc-mini/contracts/sbtc-btc-tx-helper.clar b/utils/sbtc-mini/contracts/sbtc-btc-tx-helper.clar similarity index 100% rename from sbtc-mini/contracts/sbtc-btc-tx-helper.clar rename to utils/sbtc-mini/contracts/sbtc-btc-tx-helper.clar diff --git a/sbtc-mini/contracts/sbtc-controller.clar b/utils/sbtc-mini/contracts/sbtc-controller.clar similarity index 100% rename from sbtc-mini/contracts/sbtc-controller.clar rename to utils/sbtc-mini/contracts/sbtc-controller.clar diff --git a/sbtc-mini/contracts/sbtc-deposit-verifier.clar b/utils/sbtc-mini/contracts/sbtc-deposit-verifier.clar similarity index 100% rename from sbtc-mini/contracts/sbtc-deposit-verifier.clar rename to utils/sbtc-mini/contracts/sbtc-deposit-verifier.clar diff --git a/sbtc-mini/contracts/sbtc-hand-off.clar b/utils/sbtc-mini/contracts/sbtc-hand-off.clar similarity index 100% rename from sbtc-mini/contracts/sbtc-hand-off.clar rename to utils/sbtc-mini/contracts/sbtc-hand-off.clar diff --git a/sbtc-mini/contracts/sbtc-registry.clar b/utils/sbtc-mini/contracts/sbtc-registry.clar similarity index 100% rename from sbtc-mini/contracts/sbtc-registry.clar rename to utils/sbtc-mini/contracts/sbtc-registry.clar diff --git a/sbtc-mini/contracts/sbtc-stacking-pool.clar b/utils/sbtc-mini/contracts/sbtc-stacking-pool.clar similarity index 100% rename from sbtc-mini/contracts/sbtc-stacking-pool.clar rename to utils/sbtc-mini/contracts/sbtc-stacking-pool.clar diff --git a/sbtc-mini/contracts/sbtc-testnet-debug-controller.clar b/utils/sbtc-mini/contracts/sbtc-testnet-debug-controller.clar similarity index 100% rename from sbtc-mini/contracts/sbtc-testnet-debug-controller.clar rename to utils/sbtc-mini/contracts/sbtc-testnet-debug-controller.clar diff --git a/sbtc-mini/contracts/sbtc-token.clar b/utils/sbtc-mini/contracts/sbtc-token.clar similarity index 100% rename from sbtc-mini/contracts/sbtc-token.clar rename to utils/sbtc-mini/contracts/sbtc-token.clar diff --git a/sbtc-mini/contracts/sbtc-wallet-vote.clar b/utils/sbtc-mini/contracts/sbtc-wallet-vote.clar similarity index 100% rename from sbtc-mini/contracts/sbtc-wallet-vote.clar rename to utils/sbtc-mini/contracts/sbtc-wallet-vote.clar diff --git a/sbtc-mini/contracts/sbtc-withdrawal-request-btc.clar b/utils/sbtc-mini/contracts/sbtc-withdrawal-request-btc.clar similarity index 100% rename from sbtc-mini/contracts/sbtc-withdrawal-request-btc.clar rename to utils/sbtc-mini/contracts/sbtc-withdrawal-request-btc.clar diff --git a/sbtc-mini/contracts/sbtc-withdrawal-request-reclaim.clar b/utils/sbtc-mini/contracts/sbtc-withdrawal-request-reclaim.clar similarity index 100% rename from sbtc-mini/contracts/sbtc-withdrawal-request-reclaim.clar rename to utils/sbtc-mini/contracts/sbtc-withdrawal-request-reclaim.clar diff --git a/sbtc-mini/contracts/sbtc-withdrawal-request-stx.clar b/utils/sbtc-mini/contracts/sbtc-withdrawal-request-stx.clar similarity index 100% rename from sbtc-mini/contracts/sbtc-withdrawal-request-stx.clar rename to utils/sbtc-mini/contracts/sbtc-withdrawal-request-stx.clar diff --git a/sbtc-mini/contracts/sbtc-withdrawal-verifier.clar b/utils/sbtc-mini/contracts/sbtc-withdrawal-verifier.clar similarity index 100% rename from sbtc-mini/contracts/sbtc-withdrawal-verifier.clar rename to utils/sbtc-mini/contracts/sbtc-withdrawal-verifier.clar diff --git a/sbtc-mini/deployments/bootstrap.devnet-plan.yaml b/utils/sbtc-mini/deployments/bootstrap.devnet-plan.yaml similarity index 100% rename from sbtc-mini/deployments/bootstrap.devnet-plan.yaml rename to utils/sbtc-mini/deployments/bootstrap.devnet-plan.yaml diff --git a/sbtc-mini/deployments/default.devnet-plan.yaml b/utils/sbtc-mini/deployments/default.devnet-plan.yaml similarity index 100% rename from sbtc-mini/deployments/default.devnet-plan.yaml rename to utils/sbtc-mini/deployments/default.devnet-plan.yaml diff --git a/sbtc-mini/deployments/signer-1-pre-register.devnet-plan.yaml b/utils/sbtc-mini/deployments/signer-1-pre-register.devnet-plan.yaml similarity index 100% rename from sbtc-mini/deployments/signer-1-pre-register.devnet-plan.yaml rename to utils/sbtc-mini/deployments/signer-1-pre-register.devnet-plan.yaml diff --git a/sbtc-mini/deployments/signer-2-register.devnet-plan.yaml b/utils/sbtc-mini/deployments/signer-2-register.devnet-plan.yaml similarity index 100% rename from sbtc-mini/deployments/signer-2-register.devnet-plan.yaml rename to utils/sbtc-mini/deployments/signer-2-register.devnet-plan.yaml diff --git a/sbtc-mini/deployments/signer-3-vote.devnet-plan.yaml b/utils/sbtc-mini/deployments/signer-3-vote.devnet-plan.yaml similarity index 100% rename from sbtc-mini/deployments/signer-3-vote.devnet-plan.yaml rename to utils/sbtc-mini/deployments/signer-3-vote.devnet-plan.yaml diff --git a/sbtc-mini/deployments/signer-4-transfer-wallet.devnet-plan.yaml b/utils/sbtc-mini/deployments/signer-4-transfer-wallet.devnet-plan.yaml similarity index 100% rename from sbtc-mini/deployments/signer-4-transfer-wallet.devnet-plan.yaml rename to utils/sbtc-mini/deployments/signer-4-transfer-wallet.devnet-plan.yaml diff --git a/sbtc-mini/deployments/signer-5-prove-transfer.devnet-plan.yaml b/utils/sbtc-mini/deployments/signer-5-prove-transfer.devnet-plan.yaml similarity index 100% rename from sbtc-mini/deployments/signer-5-prove-transfer.devnet-plan.yaml rename to utils/sbtc-mini/deployments/signer-5-prove-transfer.devnet-plan.yaml diff --git a/sbtc-mini/deployments/user-1-deposit.devnet-plan.yaml b/utils/sbtc-mini/deployments/user-1-deposit.devnet-plan.yaml similarity index 100% rename from sbtc-mini/deployments/user-1-deposit.devnet-plan.yaml rename to utils/sbtc-mini/deployments/user-1-deposit.devnet-plan.yaml diff --git a/sbtc-mini/ext/deps.ts b/utils/sbtc-mini/ext/deps.ts similarity index 100% rename from sbtc-mini/ext/deps.ts rename to utils/sbtc-mini/ext/deps.ts diff --git a/sbtc-mini/ext/extract-error-codes.ts b/utils/sbtc-mini/ext/extract-error-codes.ts similarity index 100% rename from sbtc-mini/ext/extract-error-codes.ts rename to utils/sbtc-mini/ext/extract-error-codes.ts diff --git a/sbtc-mini/ext/generate-flow-tests.ts b/utils/sbtc-mini/ext/generate-flow-tests.ts similarity index 100% rename from sbtc-mini/ext/generate-flow-tests.ts rename to utils/sbtc-mini/ext/generate-flow-tests.ts diff --git a/sbtc-mini/ext/generate-proof.ts b/utils/sbtc-mini/ext/generate-proof.ts similarity index 100% rename from sbtc-mini/ext/generate-proof.ts rename to utils/sbtc-mini/ext/generate-proof.ts diff --git a/sbtc-mini/ext/generate-test-vectors.ts b/utils/sbtc-mini/ext/generate-test-vectors.ts similarity index 100% rename from sbtc-mini/ext/generate-test-vectors.ts rename to utils/sbtc-mini/ext/generate-test-vectors.ts diff --git a/sbtc-mini/ext/generate-tests.ts b/utils/sbtc-mini/ext/generate-tests.ts similarity index 100% rename from sbtc-mini/ext/generate-tests.ts rename to utils/sbtc-mini/ext/generate-tests.ts diff --git a/sbtc-mini/ext/utils/clarity-parser.ts b/utils/sbtc-mini/ext/utils/clarity-parser.ts similarity index 100% rename from sbtc-mini/ext/utils/clarity-parser.ts rename to utils/sbtc-mini/ext/utils/clarity-parser.ts diff --git a/sbtc-mini/ext/utils/generate.ts b/utils/sbtc-mini/ext/utils/generate.ts similarity index 100% rename from sbtc-mini/ext/utils/generate.ts rename to utils/sbtc-mini/ext/utils/generate.ts diff --git a/sbtc-mini/scripts/coverage-report.sh b/utils/sbtc-mini/scripts/coverage-report.sh similarity index 100% rename from sbtc-mini/scripts/coverage-report.sh rename to utils/sbtc-mini/scripts/coverage-report.sh diff --git a/sbtc-mini/scripts/extract-error-codes.sh b/utils/sbtc-mini/scripts/extract-error-codes.sh similarity index 100% rename from sbtc-mini/scripts/extract-error-codes.sh rename to utils/sbtc-mini/scripts/extract-error-codes.sh diff --git a/sbtc-mini/scripts/integration-test.sh b/utils/sbtc-mini/scripts/integration-test.sh similarity index 100% rename from sbtc-mini/scripts/integration-test.sh rename to utils/sbtc-mini/scripts/integration-test.sh diff --git a/sbtc-mini/scripts/test.sh b/utils/sbtc-mini/scripts/test.sh similarity index 100% rename from sbtc-mini/scripts/test.sh rename to utils/sbtc-mini/scripts/test.sh diff --git a/sbtc-mini/settings/Devnet.toml b/utils/sbtc-mini/settings/Devnet.toml similarity index 100% rename from sbtc-mini/settings/Devnet.toml rename to utils/sbtc-mini/settings/Devnet.toml diff --git a/sbtc-mini/tests/bootstrap.ts b/utils/sbtc-mini/tests/bootstrap.ts similarity index 100% rename from sbtc-mini/tests/bootstrap.ts rename to utils/sbtc-mini/tests/bootstrap.ts diff --git a/sbtc-mini/tests/clarity-bitcoin_test.clar b/utils/sbtc-mini/tests/clarity-bitcoin_test.clar similarity index 100% rename from sbtc-mini/tests/clarity-bitcoin_test.clar rename to utils/sbtc-mini/tests/clarity-bitcoin_test.clar diff --git a/sbtc-mini/tests/sbtc-btc-tx-helper_test.clar b/utils/sbtc-mini/tests/sbtc-btc-tx-helper_test.clar similarity index 100% rename from sbtc-mini/tests/sbtc-btc-tx-helper_test.clar rename to utils/sbtc-mini/tests/sbtc-btc-tx-helper_test.clar diff --git a/sbtc-mini/tests/sbtc-controller_test.clar b/utils/sbtc-mini/tests/sbtc-controller_test.clar similarity index 100% rename from sbtc-mini/tests/sbtc-controller_test.clar rename to utils/sbtc-mini/tests/sbtc-controller_test.clar diff --git a/sbtc-mini/tests/sbtc-deposit-verifier_test.clar b/utils/sbtc-mini/tests/sbtc-deposit-verifier_test.clar similarity index 100% rename from sbtc-mini/tests/sbtc-deposit-verifier_test.clar rename to utils/sbtc-mini/tests/sbtc-deposit-verifier_test.clar diff --git a/sbtc-mini/tests/sbtc-hand-off_test.clar b/utils/sbtc-mini/tests/sbtc-hand-off_test.clar similarity index 100% rename from sbtc-mini/tests/sbtc-hand-off_test.clar rename to utils/sbtc-mini/tests/sbtc-hand-off_test.clar diff --git a/sbtc-mini/tests/sbtc-registry_test.clar b/utils/sbtc-mini/tests/sbtc-registry_test.clar similarity index 100% rename from sbtc-mini/tests/sbtc-registry_test.clar rename to utils/sbtc-mini/tests/sbtc-registry_test.clar diff --git a/sbtc-mini/tests/sbtc-stacking-pool_allow_flow_test.clar b/utils/sbtc-mini/tests/sbtc-stacking-pool_allow_flow_test.clar similarity index 100% rename from sbtc-mini/tests/sbtc-stacking-pool_allow_flow_test.clar rename to utils/sbtc-mini/tests/sbtc-stacking-pool_allow_flow_test.clar diff --git a/sbtc-mini/tests/sbtc-stacking-pool_amounts_flow_test.clar b/utils/sbtc-mini/tests/sbtc-stacking-pool_amounts_flow_test.clar similarity index 100% rename from sbtc-mini/tests/sbtc-stacking-pool_amounts_flow_test.clar rename to utils/sbtc-mini/tests/sbtc-stacking-pool_amounts_flow_test.clar diff --git a/sbtc-mini/tests/sbtc-stacking-pool_as_protocol_test.clar b/utils/sbtc-mini/tests/sbtc-stacking-pool_as_protocol_test.clar similarity index 100% rename from sbtc-mini/tests/sbtc-stacking-pool_as_protocol_test.clar rename to utils/sbtc-mini/tests/sbtc-stacking-pool_as_protocol_test.clar diff --git a/sbtc-mini/tests/sbtc-stacking-pool_flow_test.clar b/utils/sbtc-mini/tests/sbtc-stacking-pool_flow_test.clar similarity index 100% rename from sbtc-mini/tests/sbtc-stacking-pool_flow_test.clar rename to utils/sbtc-mini/tests/sbtc-stacking-pool_flow_test.clar diff --git a/sbtc-mini/tests/sbtc-stacking-pool_inactive_flow_test.clar b/utils/sbtc-mini/tests/sbtc-stacking-pool_inactive_flow_test.clar similarity index 100% rename from sbtc-mini/tests/sbtc-stacking-pool_inactive_flow_test.clar rename to utils/sbtc-mini/tests/sbtc-stacking-pool_inactive_flow_test.clar diff --git a/sbtc-mini/tests/sbtc-stacking-pool_signer_test.clar b/utils/sbtc-mini/tests/sbtc-stacking-pool_signer_test.clar similarity index 100% rename from sbtc-mini/tests/sbtc-stacking-pool_signer_test.clar rename to utils/sbtc-mini/tests/sbtc-stacking-pool_signer_test.clar diff --git a/sbtc-mini/tests/sbtc-stacking-pool_test.clar b/utils/sbtc-mini/tests/sbtc-stacking-pool_test.clar similarity index 100% rename from sbtc-mini/tests/sbtc-stacking-pool_test.clar rename to utils/sbtc-mini/tests/sbtc-stacking-pool_test.clar diff --git a/sbtc-mini/tests/sbtc-stacking-pool_two_signers_flow_test.clar b/utils/sbtc-mini/tests/sbtc-stacking-pool_two_signers_flow_test.clar similarity index 100% rename from sbtc-mini/tests/sbtc-stacking-pool_two_signers_flow_test.clar rename to utils/sbtc-mini/tests/sbtc-stacking-pool_two_signers_flow_test.clar diff --git a/sbtc-mini/tests/sbtc-token_test.clar b/utils/sbtc-mini/tests/sbtc-token_test.clar similarity index 100% rename from sbtc-mini/tests/sbtc-token_test.clar rename to utils/sbtc-mini/tests/sbtc-token_test.clar diff --git a/sbtc-mini/tests/sbtc-withdrawal-request-stx_test.clar b/utils/sbtc-mini/tests/sbtc-withdrawal-request-stx_test.clar similarity index 100% rename from sbtc-mini/tests/sbtc-withdrawal-request-stx_test.clar rename to utils/sbtc-mini/tests/sbtc-withdrawal-request-stx_test.clar diff --git a/sbtc-mini/tests/sbtc-withdrawal-verifier_test.clar b/utils/sbtc-mini/tests/sbtc-withdrawal-verifier_test.clar similarity index 100% rename from sbtc-mini/tests/sbtc-withdrawal-verifier_test.clar rename to utils/sbtc-mini/tests/sbtc-withdrawal-verifier_test.clar diff --git a/sbtc-planning/README.md b/utils/sbtc-planning/README.md similarity index 100% rename from sbtc-planning/README.md rename to utils/sbtc-planning/README.md diff --git a/sbtc-planning/commit-reveal-ops.md b/utils/sbtc-planning/commit-reveal-ops.md similarity index 100% rename from sbtc-planning/commit-reveal-ops.md rename to utils/sbtc-planning/commit-reveal-ops.md diff --git a/stacks-dev-guide/guide.md b/utils/stacks-dev-guide/guide.md similarity index 99% rename from stacks-dev-guide/guide.md rename to utils/stacks-dev-guide/guide.md index 4faaf3bc..d35e8f2d 100644 --- a/stacks-dev-guide/guide.md +++ b/utils/stacks-dev-guide/guide.md @@ -47,7 +47,7 @@ Cargo packages | Package Name | Cargo file location | Description | --- | --- | --- -| `blockstack-core` | `/Cargo.toml` | +| `stackslib` | `/Cargo.toml` | | `clarity` | `/clarity/Cargo.toml` | | `stacks-node` | `/testnet/stacks-node/Cargo.toml` | | `stacks-common` | `/stacks-common/Cargo.toml` | diff --git a/stacks-dev-guide/tools.md b/utils/stacks-dev-guide/tools.md similarity index 100% rename from stacks-dev-guide/tools.md rename to utils/stacks-dev-guide/tools.md diff --git a/stacks-dev-guide/using-blockstack-cli.md b/utils/stacks-dev-guide/using-blockstack-cli.md similarity index 100% rename from stacks-dev-guide/using-blockstack-cli.md rename to utils/stacks-dev-guide/using-blockstack-cli.md diff --git a/stacks-dev-guide/using-stacks-js/call.mjs b/utils/stacks-dev-guide/using-stacks-js/call.mjs similarity index 100% rename from stacks-dev-guide/using-stacks-js/call.mjs rename to utils/stacks-dev-guide/using-stacks-js/call.mjs diff --git a/stacks-dev-guide/using-stacks-js/deploy.mjs b/utils/stacks-dev-guide/using-stacks-js/deploy.mjs similarity index 100% rename from stacks-dev-guide/using-stacks-js/deploy.mjs rename to utils/stacks-dev-guide/using-stacks-js/deploy.mjs diff --git a/stacks-dev-guide/using-stacks-js/guide.md b/utils/stacks-dev-guide/using-stacks-js/guide.md similarity index 100% rename from stacks-dev-guide/using-stacks-js/guide.md rename to utils/stacks-dev-guide/using-stacks-js/guide.md diff --git a/stacks-dev-guide/using-stacks-js/hello-world.clar b/utils/stacks-dev-guide/using-stacks-js/hello-world.clar similarity index 100% rename from stacks-dev-guide/using-stacks-js/hello-world.clar rename to utils/stacks-dev-guide/using-stacks-js/hello-world.clar diff --git a/stacks-dev-guide/working-with-projects.md b/utils/stacks-dev-guide/working-with-projects.md similarity index 100% rename from stacks-dev-guide/working-with-projects.md rename to utils/stacks-dev-guide/working-with-projects.md