From d2047657c7dd0e49be50801b1546cd5e55957b34 Mon Sep 17 00:00:00 2001 From: Braqzen <103777923+Braqzen@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:23:40 +0100 Subject: [PATCH] Added batch, open and close order commands --- spark-cli/src/commands/core/batch_fulfill.rs | 67 +++++++++++++++++- spark-cli/src/commands/core/cli.rs | 14 ++-- spark-cli/src/commands/core/close_order.rs | 47 ++++++++++++- spark-cli/src/commands/core/open_order.rs | 72 +++++++++++++++++++- spark-cli/src/commands/core/withdraw.rs | 1 + spark-cli/src/commands/info/cli.rs | 12 ++-- spark-cli/src/main.rs | 6 +- 7 files changed, 194 insertions(+), 25 deletions(-) diff --git a/spark-cli/src/commands/core/batch_fulfill.rs b/spark-cli/src/commands/core/batch_fulfill.rs index aeb8b40..68da595 100644 --- a/spark-cli/src/commands/core/batch_fulfill.rs +++ b/spark-cli/src/commands/core/batch_fulfill.rs @@ -1,11 +1,72 @@ +use crate::utils::{setup, validate_contract_id}; use clap::Args; +use fuels::{ + accounts::ViewOnlyAccount, + types::{AssetId, Bits256}, +}; +use spark_market_sdk::MarketContract; #[derive(Args, Clone)] -#[command(about = "TODO")] -pub(crate) struct BatchCommand {} +#[command(about = "Matches orders")] +pub(crate) struct BatchCommand { + /// The b256 id of the order + #[clap(long)] + pub(crate) order_id: String, + + /// The b256 ids for orders to match against `order_id` + #[clap(long)] + pub(crate) order_ids: Vec, + + /// The contract id of the market + #[clap(long)] + pub(crate) contract_id: String, + + /// The URL to query + /// Ex. beta-5.fuel.network + #[clap(long)] + pub(crate) rpc: String, +} impl BatchCommand { - pub(crate) fn run(&self) -> anyhow::Result<()> { + pub(crate) async fn run(&self) -> anyhow::Result<()> { + let wallet = setup(&self.rpc).await?; + let contract_id = validate_contract_id(&self.contract_id)?; + + let order_id = Bits256::from_hex_str(&self.order_id)?; + + if self.order_id.len() as u64 != 64 { + anyhow::bail!("Invalid order id length"); + } + + if self.order_ids.is_empty() { + anyhow::bail!("At least one order ID must be added to the list of ids"); + } + + let mut order_ids: Vec = Vec::with_capacity(self.order_ids.len()); + for id in self.order_ids.iter() { + if id.len() as u64 != 64 { + anyhow::bail!("Invalid order id length: {}", id); + } + + order_ids.push(Bits256::from_hex_str(id)?); + } + + // Initial balance prior to contract call - used to calculate contract interaction cost + let balance = wallet.get_asset_balance(&AssetId::BASE).await?; + + // Connect to the deployed contract via the rpc + let contract = MarketContract::new(contract_id, wallet.clone()).await; + + let _ = contract.batch_fulfill(order_id, order_ids).await?; + + // Balance post-call + let new_balance = wallet.get_asset_balance(&AssetId::BASE).await?; + + // TODO: replace println with tracing + println!("\nContract call cost: {}", balance - new_balance); + // TODO: adjust contract to inform which orders have not been fulfilled and report here? + // this could be via a return value of incomplete orders + Ok(()) } } diff --git a/spark-cli/src/commands/core/cli.rs b/spark-cli/src/commands/core/cli.rs index d803704..7843d95 100644 --- a/spark-cli/src/commands/core/cli.rs +++ b/spark-cli/src/commands/core/cli.rs @@ -7,31 +7,31 @@ use clap::Subcommand; #[derive(Clone, Subcommand)] pub(crate) enum CoreCommands { - /// + /// Attempt to batch solve orders #[clap(short_flag = 'B')] Batch(BatchCommand), - /// + /// Close an open order #[clap(short_flag = 'C')] Close(CloseCommand), - /// + /// Deploy a new market contract #[clap(short_flag = 'D')] Deploy(DeployCommand), - /// + /// Deposit into the market contract #[clap(short_flag = 'P')] Deposit(DepositCommand), - /// + /// Open an order #[clap(short_flag = 'O')] Open(OpenCommand), - /// + /// Set a fee for a specific user or the market #[clap(short_flag = 'S')] SetFee(SetFeeCommand), - /// + /// Withdraw from the market contract #[clap(short_flag = 'W')] Withdraw(WithdrawCommand), } diff --git a/spark-cli/src/commands/core/close_order.rs b/spark-cli/src/commands/core/close_order.rs index 4f40e8a..4d61672 100644 --- a/spark-cli/src/commands/core/close_order.rs +++ b/spark-cli/src/commands/core/close_order.rs @@ -1,11 +1,52 @@ +use crate::utils::{setup, validate_contract_id}; use clap::Args; +use fuels::{ + accounts::ViewOnlyAccount, + types::{AssetId, Bits256}, +}; +use spark_market_sdk::MarketContract; #[derive(Args, Clone)] -#[command(about = "TODO")] -pub(crate) struct CloseCommand {} +#[command(about = "Closes an open order")] +pub(crate) struct CloseCommand { + /// The b256 id of the order + #[clap(long)] + pub(crate) order_id: String, + + /// The contract id of the market + #[clap(long)] + pub(crate) contract_id: String, + + /// The URL to query + /// Ex. beta-5.fuel.network + #[clap(long)] + pub(crate) rpc: String, +} impl CloseCommand { - pub(crate) fn run(&self) -> anyhow::Result<()> { + pub(crate) async fn run(&self) -> anyhow::Result<()> { + let wallet = setup(&self.rpc).await?; + let contract_id = validate_contract_id(&self.contract_id)?; + let order_id = Bits256::from_hex_str(&self.order_id)?; + + if self.order_id.len() as u64 != 64 { + anyhow::bail!("Invalid order id length"); + } + + // Initial balance prior to contract call - used to calculate contract interaction cost + let balance = wallet.get_asset_balance(&AssetId::BASE).await?; + + // Connect to the deployed contract via the rpc + let contract = MarketContract::new(contract_id, wallet.clone()).await; + + let _ = contract.cancel_order(order_id).await?; + + // Balance post-call + let new_balance = wallet.get_asset_balance(&AssetId::BASE).await?; + + // TODO: replace println with tracing + println!("\nContract call cost: {}", balance - new_balance); + Ok(()) } } diff --git a/spark-cli/src/commands/core/open_order.rs b/spark-cli/src/commands/core/open_order.rs index 5d9cbd4..26caa96 100644 --- a/spark-cli/src/commands/core/open_order.rs +++ b/spark-cli/src/commands/core/open_order.rs @@ -1,11 +1,77 @@ +use crate::utils::{setup, validate_contract_id, OrderType}; use clap::Args; +use fuels::{ + accounts::ViewOnlyAccount, + types::{AssetId, ContractId}, +}; +use spark_market_sdk::{MarketContract, OrderType as ContractOrderType}; +use std::str::FromStr; #[derive(Args, Clone)] -#[command(about = "TODO")] -pub(crate) struct OpenCommand {} +#[command(about = "Opens a new order")] +pub(crate) struct OpenCommand { + /// The amount of asset + #[clap(long)] + pub(crate) amount: u64, + + /// The id of the asset + #[clap(long)] + pub(crate) asset: String, + + /// The type of order + #[clap(long)] + pub(crate) order_type: OrderType, + + /// The price of the order + #[clap(long)] + pub(crate) price: u64, + + /// The contract id of the market + #[clap(long)] + pub(crate) contract_id: String, + + /// The URL to query + /// Ex. beta-5.fuel.network + #[clap(long)] + pub(crate) rpc: String, +} impl OpenCommand { - pub(crate) fn run(&self) -> anyhow::Result<()> { + pub(crate) async fn run(&self) -> anyhow::Result<()> { + let wallet = setup(&self.rpc).await?; + let contract_id = validate_contract_id(&self.contract_id)?; + + if self.asset.len() as u64 != 66 { + anyhow::bail!("Invalid asset length"); + } + + let asset = AssetId::from_str(&self.asset).expect("Invalid asset"); + + // TODO: cli parsing + let order_type = match self.order_type { + OrderType::Buy => ContractOrderType::Buy, + OrderType::Sell => ContractOrderType::Sell, + }; + + // Initial balance prior to contract call - used to calculate contract interaction cost + let balance = wallet.get_asset_balance(&AssetId::BASE).await?; + + // Connect to the deployed contract via the rpc + let contract = MarketContract::new(contract_id, wallet.clone()).await; + + let order_id = contract + .open_order(self.amount, asset, order_type, self.price) + .await? + .value; + + // Balance post-call + let new_balance = wallet.get_asset_balance(&AssetId::BASE).await?; + + // TODO: replace println with tracing + println!("\nContract call cost: {}", balance - new_balance); + // TODO: hack to display, turn into hex manually? + println!("Order ID: {}", ContractId::from(order_id.0)); + Ok(()) } } diff --git a/spark-cli/src/commands/core/withdraw.rs b/spark-cli/src/commands/core/withdraw.rs index b8af6e6..394fb4f 100644 --- a/spark-cli/src/commands/core/withdraw.rs +++ b/spark-cli/src/commands/core/withdraw.rs @@ -43,6 +43,7 @@ impl WithdrawCommand { let contract = MarketContract::new(contract_id, wallet.clone()).await; let r = contract.withdraw(self.amount, asset).await?; + // TODO: sdk debugging dbg!(r); // Balance post-call diff --git a/spark-cli/src/commands/info/cli.rs b/spark-cli/src/commands/info/cli.rs index bf117d1..ef1fe4e 100644 --- a/spark-cli/src/commands/info/cli.rs +++ b/spark-cli/src/commands/info/cli.rs @@ -6,27 +6,27 @@ use clap::Subcommand; #[derive(Clone, Subcommand)] pub(crate) enum InfoCommands { - /// + /// Query account information #[clap(short_flag = 'A')] Account(AccountCommand), - /// + /// Query configuration information for a market contract #[clap(short_flag = 'C')] Config(ConfigCommand), - /// + /// Query fee information for a specific user or the market contract #[clap(short_flag = 'F')] Fee(FeeCommand), - /// + /// Calculate the order id given the provided arguments #[clap(short_flag = 'I')] OrderId(OrderIdCommand), - /// + /// Query order information #[clap(short_flag = 'O')] Order(OrderCommand), - /// + /// Query orders associated with an #[clap(short_flag = 'U')] UserOrders(UserOrdersCommand), } diff --git a/spark-cli/src/main.rs b/spark-cli/src/main.rs index adfba1f..0518420 100644 --- a/spark-cli/src/main.rs +++ b/spark-cli/src/main.rs @@ -20,11 +20,11 @@ async fn main() -> anyhow::Result<()> { match cli.command { Command::Core(args) => match args.commands { - CoreCommands::Batch(args) => args.run(), - CoreCommands::Close(args) => args.run(), + CoreCommands::Batch(args) => args.run().await, + CoreCommands::Close(args) => args.run().await, CoreCommands::Deploy(args) => args.run().await, CoreCommands::Deposit(args) => args.run().await, - CoreCommands::Open(args) => args.run(), + CoreCommands::Open(args) => args.run().await, CoreCommands::SetFee(args) => args.run().await, CoreCommands::Withdraw(args) => args.run().await, },