├── .env.example ├── .github └── ISSUE_TEMPLATE │ └── bug_report.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Forc.lock ├── Forc.toml ├── LICENSE ├── README.md ├── build.sh ├── docs ├── scheme.drawio └── scheme.drawio.png ├── fuel-toolchain.toml ├── spark-cli ├── Cargo.lock ├── Cargo.toml ├── README.md └── src │ ├── commands │ ├── batch │ │ ├── cli.rs │ │ ├── deploy_all.rs │ │ ├── deploy_eth_usdc_proxy.rs │ │ ├── deploy_ezeth_usdc_proxy.rs │ │ ├── deploy_fuel_eth_proxy.rs │ │ ├── deploy_fuel_usdc_proxy.rs │ │ ├── deploy_proxy.rs │ │ ├── deploy_psycho_usdc_proxy.rs │ │ ├── deploy_pzeth_usdc_proxy.rs │ │ ├── deploy_teth_tusdc_impl.rs │ │ ├── deploy_teth_tusdc_proxy.rs │ │ ├── deploy_trump_eth_proxy.rs │ │ ├── deploy_usdc_usdt_proxy.rs │ │ ├── deploy_usdf_usdc_proxy.rs │ │ ├── deploy_usdt_eth_proxy.rs │ │ ├── deploy_usdt_usdc_proxy.rs │ │ ├── deploy_weth_usdc_proxy.rs │ │ └── mod.rs │ ├── cli.rs │ ├── core │ │ ├── cancel_order.rs │ │ ├── cancel_small_order.rs │ │ ├── cli.rs │ │ ├── deploy.rs │ │ ├── deposit.rs │ │ ├── deposit_for.rs │ │ ├── fulfill_many.rs │ │ ├── match_many.rs │ │ ├── mod.rs │ │ ├── open_market_order.rs │ │ ├── open_order.rs │ │ ├── set_epoch.rs │ │ ├── set_matcher_fee.rs │ │ ├── set_min_order_price.rs │ │ ├── set_min_order_size.rs │ │ ├── set_paused.rs │ │ ├── set_protocol_fee.rs │ │ ├── set_proxy_target.rs │ │ ├── withdraw.rs │ │ └── withdraw_to_market.rs │ ├── info │ │ ├── account.rs │ │ ├── cli.rs │ │ ├── config.rs │ │ ├── epoch.rs │ │ ├── market_order.rs │ │ ├── matcher_fee.rs │ │ ├── min_order_price.rs │ │ ├── min_order_size.rs │ │ ├── mod.rs │ │ ├── order.rs │ │ ├── order_id.rs │ │ ├── paused.rs │ │ ├── protocol_fee.rs │ │ ├── protocol_fee_user.rs │ │ ├── protocol_fee_user_amount.rs │ │ ├── proxy_owner.rs │ │ ├── proxy_target.rs │ │ └── user_orders.rs │ ├── mod.rs │ ├── registry │ │ ├── cli.rs │ │ ├── config.rs │ │ ├── deploy.rs │ │ ├── markets.rs │ │ ├── mod.rs │ │ ├── register.rs │ │ └── unregister.rs │ └── upgrade │ │ ├── cli.rs │ │ ├── mod.rs │ │ ├── upgrade_eth_usdc_proxy.rs │ │ ├── upgrade_ezeth_usdc_proxy.rs │ │ ├── upgrade_fuel_eth_proxy.rs │ │ ├── upgrade_fuel_usdc_proxy.rs │ │ ├── upgrade_psycho_usdc_proxy.rs │ │ ├── upgrade_pzeth_usdc_proxy.rs │ │ ├── upgrade_teth_tusdc_proxy.rs │ │ ├── upgrade_usdc_usdt_proxy.rs │ │ ├── upgrade_usdf_usdc_proxy.rs │ │ ├── upgrade_usdt_eth_proxy.rs │ │ ├── upgrade_usdt_usdc_proxy.rs │ │ └── upgrade_weth_usdc_proxy.rs │ ├── main.rs │ └── utils.rs ├── spark-market-sdk ├── Cargo.toml ├── README.md ├── spark-market │ └── out │ │ └── release │ │ ├── spark-market-abi.json │ │ ├── spark-market-storage_slots.json │ │ └── spark-market.bin ├── spark-proxy │ └── out │ │ └── release │ │ ├── spark-proxy-abi.json │ │ ├── spark-proxy-storage_slots.json │ │ └── spark-proxy.bin └── src │ └── lib.rs ├── spark-market ├── Cargo.toml ├── Forc.toml ├── out │ └── release │ │ ├── spark-market-abi.json │ │ ├── spark-market-storage_slots.json │ │ └── spark-market.bin ├── src │ ├── data_structures.sw │ ├── data_structures │ │ ├── account.sw │ │ ├── asset_type.sw │ │ ├── balance.sw │ │ ├── limit_type.sw │ │ ├── match_result.sw │ │ ├── order.sw │ │ ├── order_change.sw │ │ ├── order_type.sw │ │ ├── protocol_fee.sw │ │ └── user_volume.sw │ ├── errors.sw │ ├── events.sw │ ├── interface.sw │ ├── main.sw │ └── math.sw └── tests │ ├── functions │ ├── core │ │ ├── cancel_order.rs │ │ ├── cancel_small_order.rs │ │ ├── deposit.rs │ │ ├── deposit_for.rs │ │ ├── fulfill_order_many.rs │ │ ├── initialize_ownership.rs │ │ ├── match_order_many.rs │ │ ├── mod.rs │ │ ├── open_market_order.rs │ │ ├── open_order.rs │ │ ├── pause.rs │ │ ├── set_epoch.rs │ │ ├── set_matcher_fee.rs │ │ ├── set_min_order_price.rs │ │ ├── set_min_order_size.rs │ │ ├── set_protocol_fee.rs │ │ ├── transfer_ownership.rs │ │ ├── withdraw.rs │ │ └── withdraw_to_market.rs │ ├── info │ │ ├── account.rs │ │ ├── config.rs │ │ ├── matcher_fee.rs │ │ ├── min_order_price.rs │ │ ├── min_order_size.rs │ │ ├── mod.rs │ │ ├── order.rs │ │ ├── order_id.rs │ │ ├── protocol_fee.rs │ │ ├── protocol_fee_user.rs │ │ ├── protocol_fee_user_amount.rs │ │ └── user_orders.rs │ └── mod.rs │ ├── harness.rs │ └── setup │ └── mod.rs ├── spark-proxy-sdk ├── Cargo.toml ├── README.md ├── spark-proxy │ └── out │ │ └── release │ │ ├── spark-proxy-abi.json │ │ ├── spark-proxy-storage_slots.json │ │ └── spark-proxy.bin └── src │ └── lib.rs ├── spark-proxy ├── Cargo.toml ├── Forc.toml ├── out │ └── release │ │ ├── spark-proxy-abi.json │ │ ├── spark-proxy-storage_slots.json │ │ └── spark-proxy.bin ├── src │ └── main.sw └── tests │ ├── functions │ ├── core │ │ ├── mod.rs │ │ ├── set_proxy_owner.rs │ │ └── set_proxy_target.rs │ ├── info │ │ ├── mod.rs │ │ ├── proxy_owner.rs │ │ └── proxy_target.rs │ └── mod.rs │ ├── harness.rs │ └── setup │ └── mod.rs ├── spark-registry-sdk ├── Cargo.toml ├── README.md ├── spark-proxy │ └── out │ │ └── release │ │ ├── spark-proxy-abi.json │ │ ├── spark-proxy-storage_slots.json │ │ └── spark-proxy.bin ├── spark-registry │ └── out │ │ └── release │ │ ├── spark-registry-abi.json │ │ ├── spark-registry-storage_slots.json │ │ └── spark-registry.bin └── src │ └── lib.rs └── spark-registry ├── Cargo.toml ├── Forc.toml ├── out └── release │ ├── spark-registry-abi.json │ ├── spark-registry-storage_slots.json │ └── spark-registry.bin ├── src ├── errors.sw ├── events.sw └── main.sw └── tests ├── functions ├── core │ ├── initialize_ownership.rs │ ├── mod.rs │ ├── register_market.rs │ ├── transfer_ownership.rs │ └── unregister_market.rs ├── info │ ├── config.rs │ ├── markets.rs │ └── mod.rs └── mod.rs ├── harness.rs └── setup └── mod.rs /.env.example: -------------------------------------------------------------------------------- 1 | WALLET_SECRET= 2 | # MNEMONIC="or you can use a seed phrase" -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Use this template to report a bug. 3 | title: "[BUG] Title of the bug" 4 | labels: ["bug"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | **Please fill out the following details:** 10 | - type: input 11 | id: summary 12 | attributes: 13 | label: Brief summary of the issue 14 | description: A short description of the bug. 15 | placeholder: Describe the bug in one or two sentences. 16 | validations: 17 | required: true 18 | - type: textarea 19 | id: steps 20 | attributes: 21 | label: Steps to Reproduce 22 | description: Provide the steps to reproduce the bug. 23 | placeholder: Step 1, Step 2, ... 24 | validations: 25 | required: true 26 | - type: textarea 27 | id: expected 28 | attributes: 29 | label: Expected Behavior 30 | description: What did you expect to happen? 31 | validations: 32 | required: true 33 | - type: textarea 34 | id: actual 35 | attributes: 36 | label: Actual Behavior 37 | description: What actually happened? 38 | validations: 39 | required: true 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /coverage 3 | .idea 4 | .DS_Store 5 | .env 6 | *target/ 7 | .vscode 8 | note.txt 9 | log.txt 10 | dependencies 11 | */out/debug 12 | description.md -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "1" 3 | members = [ 4 | "spark-cli", 5 | "spark-market-sdk", 6 | "spark-market", 7 | "spark-proxy-sdk", 8 | "spark-proxy", 9 | "spark-registry-sdk", 10 | "spark-registry" 11 | ] 12 | 13 | [workspace.package] 14 | authors = ["ComposabilityLabs"] 15 | version = "0.7.1" 16 | edition = "2021" 17 | license = "Apache-2.0" 18 | rust-version = "1.82.0" 19 | 20 | [workspace.dependencies] 21 | anyhow = "1.0.96" 22 | fuels = { version = "0.66.10", features = ["fuel-core-lib"] } 23 | tokio = { version = "1.43.0", features = ["rt", "macros"] } 24 | 25 | -------------------------------------------------------------------------------- /Forc.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "core" 3 | source = "path+from-root-F64B6E1A883A7E7B" 4 | 5 | [[package]] 6 | name = "spark-market" 7 | source = "member" 8 | dependencies = [ 9 | "standards git+https://github.com/FuelLabs/sway-standards?tag=v0.6.1#792639cdf391565e6e6a02482ea8a46d9604a6f5", 10 | "std", 11 | "sway_libs", 12 | ] 13 | 14 | [[package]] 15 | name = "spark-proxy" 16 | source = "member" 17 | dependencies = [ 18 | "standards git+https://github.com/FuelLabs/sway-standards?tag=v0.6.1#792639cdf391565e6e6a02482ea8a46d9604a6f5", 19 | "std", 20 | "sway_libs", 21 | ] 22 | 23 | [[package]] 24 | name = "spark-registry" 25 | source = "member" 26 | dependencies = [ 27 | "standards git+https://github.com/FuelLabs/sway-standards?tag=v0.6.1#792639cdf391565e6e6a02482ea8a46d9604a6f5", 28 | "std", 29 | "sway_libs", 30 | ] 31 | 32 | [[package]] 33 | name = "standards" 34 | source = "git+https://github.com/FuelLabs/sway-standards?tag=v0.6.0#65e09f95ea8b9476b171a66c8a47108f352fa32c" 35 | dependencies = ["std"] 36 | 37 | [[package]] 38 | name = "standards" 39 | source = "git+https://github.com/FuelLabs/sway-standards?tag=v0.6.1#792639cdf391565e6e6a02482ea8a46d9604a6f5" 40 | dependencies = ["std"] 41 | 42 | [[package]] 43 | name = "std" 44 | source = "git+https://github.com/fuellabs/sway?tag=v0.66.4#d7dd104dac4394aa7af56f05b720c975744db853" 45 | dependencies = ["core"] 46 | 47 | [[package]] 48 | name = "sway_libs" 49 | source = "git+https://github.com/FuelLabs/sway-libs?tag=v0.24.0#e19f96f85ae12426d20adc176b70aa38fd9a2a5b" 50 | dependencies = [ 51 | "standards git+https://github.com/FuelLabs/sway-standards?tag=v0.6.0#65e09f95ea8b9476b171a66c8a47108f352fa32c", 52 | "std", 53 | ] 54 | -------------------------------------------------------------------------------- /Forc.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["spark-market", "spark-proxy", "spark-registry"] 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | Copyright (c) 2025 Composability Labs 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, 19 | OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spark Orderbook Contract 2 | 3 | The Spark Orderbook Contract is a decentralized order book implemented on the Fuel blockchain, designed to facilitate secure and transparent trading. 4 | 5 | ## Getting Started 6 | 7 | The Orderbook framework consists of five components, which can be found in the root folder: 8 | 9 | - **`spark-market`:** This Sway contract contains all the trading logic, including functionalities for opening, closing, and matching orders. It serves as the core of the trading operations, with corresponding tests included. 10 | - **`spark-registry`:** This Sway contract allows for the registration of new markets, specifically for asset trading pairs. It acts as a registry that keeps track of all available markets, with tests included. 11 | - **`spark-cli`:** Spark CLI tools for deploying and interacting with contracts. Detailed information is available in the README file within the subfolder. 12 | - **`spark-market-sdk`:** A Rust library (SDK) for interacting with the SparkMarket contract. Additional details can be found in the README file within its subfolder. 13 | - **`spark-registry-sdk`:** A Rust library (SDK) for interacting with the SparkRegistry contract. More information is available in the README file within its subfolder. 14 | 15 | 16 | ## Running All Tests 17 | 18 | To build the project and run all tests, execute the following command: 19 | 20 | ``` 21 | forc build --release & cargo test --release 22 | ``` 23 | 24 | ### Running Fuzz Tests: 25 | 26 | To run fuzz tests, use the following command: 27 | 28 | ``` 29 | cargo test --release -- --ignored fuzz --test-threads=$(nproc || sysctl -n hw.ncpu) 30 | ``` 31 | 32 | ## Contribution 33 | 34 | The liquidation mechanism, error codes, and all contract methods are open for community contributions. Your input is welcome to help improve and expand the Spark Orderbook Contract, making it a more robust and feature-rich platform. 35 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | forc clean 2 | rm -rf ./spark-market-sdk/spark-market/out 3 | rm -rf ./spark-registry-sdk/spark-registry/out 4 | forc build --release 5 | cp -r ./spark-market/out ./spark-market-sdk/spark-market/ 6 | cp -r ./spark-registry/out ./spark-registry-sdk/spark-registry/ 7 | 8 | cargo clean 9 | cargo build --release -------------------------------------------------------------------------------- /docs/scheme.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compolabs/orderbook-contract/bfbc7d99d42f6630b98cd7794641f64d2ac970f2/docs/scheme.drawio.png -------------------------------------------------------------------------------- /fuel-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "latest-x86_64-apple-darwin" 3 | 4 | [components] 5 | forc="0.66.4" 6 | fuel-core = "0.40.0" -------------------------------------------------------------------------------- /spark-cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "spark-cli" 3 | description = "Spark Rust CLI for Market & Orderbook contract interactions" 4 | version = "0.7.1" 5 | authors = { workspace = true } 6 | edition = { workspace = true } 7 | license = { workspace = true } 8 | rust-version = { workspace = true } 9 | repository = "https://github.com/compolabs/orderbook-contract" 10 | readme = "README.md" 11 | keywords = ["fuel", "sdk", "spark", "cli", "orderbook"] 12 | 13 | [dependencies] 14 | anyhow = { workspace = true } 15 | clap = { version = "4.5.31", features = ["derive"] } 16 | dotenv = "0.15.0" 17 | fuels = { workspace = true } 18 | spark-market-sdk = { version = "0.7.1" } 19 | spark-registry-sdk = { version = "0.6.4" } 20 | spark-proxy-sdk = { version = "0.0.2" } 21 | tokio = { workspace = true } 22 | 23 | [[bin]] 24 | name = "spark-cli" 25 | path = "src/main.rs" 26 | -------------------------------------------------------------------------------- /spark-cli/src/commands/batch/cli.rs: -------------------------------------------------------------------------------- 1 | use crate::commands::batch::{ 2 | deploy_all::DeployAllCommand, deploy_eth_usdc_proxy::DeployEthUsdcProxyCommand, 3 | deploy_ezeth_usdc_proxy::DeployEzethUsdcProxyCommand, 4 | deploy_fuel_eth_proxy::DeployFuelEthProxyCommand, 5 | deploy_fuel_usdc_proxy::DeployFuelUsdcProxyCommand, deploy_proxy::DeployProxyCommand, 6 | deploy_psycho_usdc_proxy::DeployPsychoUsdcProxyCommand, 7 | deploy_pzeth_usdc_proxy::DeployPzethUsdcProxyCommand, 8 | deploy_teth_tusdc_impl::DeployTethTusdcImplCommand, 9 | deploy_teth_tusdc_proxy::DeployTethTusdcProxyCommand, 10 | deploy_trump_eth_proxy::DeployTrumpEthProxyCommand, 11 | deploy_usdc_usdt_proxy::DeployUsdcUsdtProxyCommand, 12 | deploy_usdf_usdc_proxy::DeployUsdfUsdcProxyCommand, 13 | deploy_usdt_eth_proxy::DeployUsdtEthProxyCommand, 14 | deploy_usdt_usdc_proxy::DeployUsdtUsdcProxyCommand, 15 | deploy_weth_usdc_proxy::DeployWethUsdcProxyCommand, 16 | }; 17 | use clap::Subcommand; 18 | 19 | #[derive(Clone, Subcommand)] 20 | pub(crate) enum BatchCommands { 21 | /// Batch Deploy a new market contracts and setup them 22 | #[clap(short_flag = 'A')] 23 | DeployAll(DeployAllCommand), 24 | 25 | /// Deploy a new eth/usdc market proxy 26 | #[clap(short_flag = 'E')] 27 | DeployEthUsdcProxy(DeployEthUsdcProxyCommand), 28 | 29 | /// Deploy a new ezeth/usdc market proxy 30 | #[clap(short_flag = 'H')] 31 | DeployEzethUsdcProxy(DeployEzethUsdcProxyCommand), 32 | 33 | /// Deploy a new fuel/usdc market proxy 34 | #[clap(short_flag = 'G')] 35 | DeployFuelEthProxy(DeployFuelEthProxyCommand), 36 | 37 | /// Deploy a new fuel/usdc market proxy 38 | #[clap(short_flag = 'F')] 39 | DeployFuelUsdcProxy(DeployFuelUsdcProxyCommand), 40 | 41 | /// Deploy a new market and proxy contracts and setup them 42 | #[clap(short_flag = 'P')] 43 | DeployProxy(DeployProxyCommand), 44 | 45 | /// Deploy a new market and proxy contracts and setup them 46 | #[clap(short_flag = 'Y')] 47 | DeployPsychoUsdcProxy(DeployPsychoUsdcProxyCommand), 48 | 49 | /// Deploy a new ezeth/usdc market proxy 50 | #[clap(short_flag = 'Q')] 51 | DeployPzethUsdcProxy(DeployPzethUsdcProxyCommand), 52 | 53 | /// Deploy a new teth/tusdc market implementtion 54 | #[clap(short_flag = 'I')] 55 | DeployTethTusdcImpl(DeployTethTusdcImplCommand), 56 | 57 | /// Deploy a new teth/tusdc market and proxy contracts and setup them 58 | #[clap(short_flag = 'T')] 59 | DeployTethTusdcProxy(DeployTethTusdcProxyCommand), 60 | 61 | /// Deploy a new trump/eth market and proxy contracts and setup them 62 | #[clap(short_flag = 'X')] 63 | DeployTrumpEthProxy(DeployTrumpEthProxyCommand), 64 | 65 | /// Deploy a new usdc/usdt market proxy 66 | #[clap(short_flag = 'U')] 67 | DeployUsdcUsdtProxy(DeployUsdcUsdtProxyCommand), 68 | 69 | /// Deploy a new usdf/usdc market proxy 70 | #[clap(short_flag = 'J')] 71 | DeployUsdfUsdcProxy(DeployUsdfUsdcProxyCommand), 72 | 73 | /// Deploy a new usdt/usdc market proxy 74 | #[clap(short_flag = 'Z')] 75 | DeployUsdtEthProxy(DeployUsdtEthProxyCommand), 76 | 77 | /// Deploy a new usdt/usdc market proxy 78 | #[clap(short_flag = 'V')] 79 | DeployUsdtUsdcProxy(DeployUsdtUsdcProxyCommand), 80 | 81 | /// Deploy a new usdt/usdc market proxy 82 | #[clap(short_flag = 'W')] 83 | DeployWethUsdcProxy(DeployWethUsdcProxyCommand), 84 | } 85 | -------------------------------------------------------------------------------- /spark-cli/src/commands/batch/deploy_proxy.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::setup; 2 | use clap::Args; 3 | use fuels::{ 4 | accounts::ViewOnlyAccount, 5 | types::{AssetId, ContractId}, 6 | }; 7 | use spark_market_sdk::SparkMarketContract; 8 | use spark_proxy_sdk::SparkProxyContract; 9 | use std::str::FromStr; 10 | 11 | #[derive(Args, Clone)] 12 | #[command(about = "Deploys the market proxy to a network")] 13 | pub(crate) struct DeployProxyCommand { 14 | /// The asset id for the base asset of the market 15 | #[clap(long)] 16 | pub(crate) base_asset: String, 17 | 18 | /// The number of decimals the base asset implements 19 | #[clap(long)] 20 | pub(crate) base_decimals: u32, 21 | 22 | /// The asset id for the quote asset of the market 23 | #[clap(long)] 24 | pub(crate) quote_asset: String, 25 | 26 | /// The number of decimals the quote asset implements 27 | #[clap(long)] 28 | pub(crate) quote_decimals: u32, 29 | 30 | /// The number of decimals the price uses 31 | #[clap(long)] 32 | pub(crate) price_decimals: u32, 33 | 34 | /// The URL to deploy to 35 | /// Ex. testnet.fuel.network 36 | #[clap(long)] 37 | pub(crate) rpc: String, 38 | } 39 | 40 | impl DeployProxyCommand { 41 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 42 | let wallet = setup(&self.rpc).await?; 43 | 44 | if self.base_asset.len() as u64 != 66 { 45 | anyhow::bail!("Invalid base asset length"); 46 | } 47 | 48 | if self.quote_asset.len() as u64 != 66 { 49 | anyhow::bail!("Invalid quote asset length"); 50 | } 51 | 52 | let base_asset = AssetId::from_str(&self.base_asset).expect("Invalid base asset"); 53 | let quote_asset = AssetId::from_str(&self.quote_asset).expect("Invalid quote asset"); 54 | 55 | // Initial balance prior to contract call - used to calculate contract interaction cost 56 | let balance = wallet 57 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 58 | .await?; 59 | 60 | let version = SparkMarketContract::sdk_version(); 61 | 62 | // Deploy the contract 63 | let contract = SparkMarketContract::deploy( 64 | base_asset, 65 | self.base_decimals, 66 | quote_asset, 67 | self.quote_decimals, 68 | wallet.clone(), 69 | self.price_decimals, 70 | version, 71 | ) 72 | .await?; 73 | 74 | let target: ContractId = contract.contract_id().into(); 75 | let proxy = SparkProxyContract::deploy(target, wallet.clone()).await?; 76 | 77 | let market = SparkMarketContract::new(proxy.contract_id().into(), wallet.clone()).await; 78 | let _ = market.initialize_ownership(wallet.address().into()).await?; 79 | 80 | let epoch = 4611686020155120000; // 10/01/2024 81 | let epoch_duration = 5020000; // 60 days 82 | let min_price = 1_000_000_000; // 1:1 83 | let min_size = 1_000; 84 | 85 | let _ = market.set_epoch(epoch, epoch_duration).await?; 86 | let _ = market.set_min_order_size(min_size).await?; 87 | let _ = market.set_min_order_price(min_price).await?; 88 | 89 | // Balance post-deployment 90 | let new_balance = wallet 91 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 92 | .await?; 93 | 94 | println!( 95 | "\nMarket version {} ({}) deployed to: 0x{} 96 | Proxy deployed to: 0x{}", 97 | contract.contract_str_version().await?, 98 | version, 99 | contract.id(), 100 | proxy.id(), 101 | ); 102 | println!("Deployment cost: {}", balance - new_balance); 103 | println!("Owner address: {}", wallet.address()); 104 | println!(" 0x{}", wallet.address().hash()); 105 | 106 | Ok(()) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /spark-cli/src/commands/batch/deploy_teth_tusdc_impl.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::setup; 2 | use clap::Args; 3 | use fuels::{accounts::ViewOnlyAccount, types::AssetId}; 4 | use spark_market_sdk::SparkMarketContract; 5 | use std::str::FromStr; 6 | 7 | #[derive(Args, Clone)] 8 | #[command(about = "Deploys the teth/tusdc market impl to a network")] 9 | pub(crate) struct DeployTethTusdcImplCommand {} 10 | 11 | impl DeployTethTusdcImplCommand { 12 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 13 | let wallet = setup("mainnet.fuel.network").await?; 14 | 15 | let (teth, tusdc) = ( 16 | "0xf169e13e98ae8908199148380684894458b7916f074b85ebad2aaad489ce0d54", 17 | "0x22dfb618b9fc621a7d53f0f599dd427fb5688e280062a8de8883a27819d3f276", 18 | ); 19 | 20 | let base_asset = AssetId::from_str(&teth).unwrap(); 21 | let quote_asset = AssetId::from_str(&tusdc).unwrap(); 22 | let base_decimals = 9; 23 | let quote_decimals = 6; 24 | let price_decimals = 9; 25 | 26 | // Initial balance prior to contract call - used to calculate contract interaction cost 27 | let balance = wallet 28 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 29 | .await?; 30 | 31 | let version = SparkMarketContract::sdk_version(); 32 | 33 | // Deploy the contract 34 | let market = SparkMarketContract::deploy( 35 | base_asset, 36 | base_decimals, 37 | quote_asset, 38 | quote_decimals, 39 | wallet.clone(), 40 | price_decimals, 41 | version, 42 | ) 43 | .await?; 44 | 45 | let _ = market.pause().await?; 46 | 47 | // Balance post-deployment 48 | let new_balance = wallet 49 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 50 | .await?; 51 | 52 | println!( 53 | "\nMarket version {} ({}) deployed to: 0x{}", 54 | market.contract_str_version().await?, 55 | version, 56 | market.id(), 57 | ); 58 | println!("Deployment cost: {}", balance - new_balance); 59 | println!("Owner address: {}", wallet.address()); 60 | println!(" 0x{}", wallet.address().hash()); 61 | 62 | Ok(()) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /spark-cli/src/commands/batch/deploy_teth_tusdc_proxy.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::setup; 2 | use clap::Args; 3 | use fuels::{ 4 | accounts::ViewOnlyAccount, 5 | types::{AssetId, ContractId}, 6 | }; 7 | use spark_market_sdk::SparkMarketContract; 8 | use spark_proxy_sdk::{SparkProxyContract, State}; 9 | use std::str::FromStr; 10 | 11 | #[derive(Args, Clone)] 12 | #[command(about = "Deploys the teth/tusdc market proxy to a network")] 13 | pub(crate) struct DeployTethTusdcProxyCommand {} 14 | 15 | impl DeployTethTusdcProxyCommand { 16 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 17 | let wallet = setup("mainnet.fuel.network").await?; 18 | 19 | let (teth, tusdc) = ( 20 | "0xf169e13e98ae8908199148380684894458b7916f074b85ebad2aaad489ce0d54", 21 | "0x22dfb618b9fc621a7d53f0f599dd427fb5688e280062a8de8883a27819d3f276", 22 | ); 23 | 24 | let base_asset = AssetId::from_str(&teth).unwrap(); 25 | let quote_asset = AssetId::from_str(&tusdc).unwrap(); 26 | let base_decimals = 9; 27 | let quote_decimals = 6; 28 | let price_decimals = 9; 29 | 30 | // Initial balance prior to contract call - used to calculate contract interaction cost 31 | let balance = wallet 32 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 33 | .await?; 34 | 35 | let version = SparkMarketContract::sdk_version(); 36 | 37 | // Deploy the contract 38 | let contract = SparkMarketContract::deploy( 39 | base_asset, 40 | base_decimals, 41 | quote_asset, 42 | quote_decimals, 43 | wallet.clone(), 44 | price_decimals, 45 | version, 46 | ) 47 | .await?; 48 | 49 | let _ = contract.pause().await?; 50 | 51 | let target: ContractId = contract.contract_id().into(); 52 | let proxy = SparkProxyContract::deploy(target, wallet.clone()).await?; 53 | 54 | assert!(proxy.proxy_owner().await?.value == State::Initialized(wallet.address().into())); 55 | 56 | let market = SparkMarketContract::new(proxy.contract_id().into(), wallet.clone()).await; 57 | let _ = market.initialize_ownership(wallet.address().into()).await?; 58 | 59 | let epoch = 4611686020165700000; // 31/02/2025 60 | let epoch_duration = 2600000; // 30 days 61 | let min_price = 500_000_000_000; // 500 USDC 62 | let min_size = 400_000; // 0.0004 ETH 63 | 64 | let _ = market.set_epoch(epoch, epoch_duration).await?; 65 | let _ = market.set_min_order_size(min_size).await?; 66 | let _ = market.set_min_order_price(min_price).await?; 67 | 68 | // Balance post-deployment 69 | let new_balance = wallet 70 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 71 | .await?; 72 | 73 | println!( 74 | "\nMarket version {} ({}) deployed to: 0x{} 75 | Proxy deployed to: 0x{}", 76 | contract.contract_str_version().await?, 77 | version, 78 | contract.id(), 79 | proxy.id(), 80 | ); 81 | println!("Deployment cost: {}", balance - new_balance); 82 | println!("Owner address: {}", wallet.address()); 83 | println!(" 0x{}", wallet.address().hash()); 84 | 85 | Ok(()) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /spark-cli/src/commands/batch/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod cli; 2 | pub(crate) mod deploy_all; 3 | pub(crate) mod deploy_eth_usdc_proxy; 4 | pub(crate) mod deploy_ezeth_usdc_proxy; 5 | pub(crate) mod deploy_fuel_eth_proxy; 6 | pub(crate) mod deploy_fuel_usdc_proxy; 7 | pub(crate) mod deploy_proxy; 8 | pub(crate) mod deploy_psycho_usdc_proxy; 9 | pub(crate) mod deploy_pzeth_usdc_proxy; 10 | pub(crate) mod deploy_teth_tusdc_impl; 11 | pub(crate) mod deploy_teth_tusdc_proxy; 12 | pub(crate) mod deploy_trump_eth_proxy; 13 | pub(crate) mod deploy_usdc_usdt_proxy; 14 | pub(crate) mod deploy_usdf_usdc_proxy; 15 | pub(crate) mod deploy_usdt_eth_proxy; 16 | pub(crate) mod deploy_usdt_usdc_proxy; 17 | pub(crate) mod deploy_weth_usdc_proxy; 18 | -------------------------------------------------------------------------------- /spark-cli/src/commands/cli.rs: -------------------------------------------------------------------------------- 1 | use crate::commands::{ 2 | batch::cli::BatchCommands, core::cli::CoreCommands, info::cli::InfoCommands, 3 | registry::cli::RegistryCommands, upgrade::cli::UpgradeCommands, 4 | }; 5 | use clap::{Args, Parser, Subcommand}; 6 | 7 | #[derive(Parser)] 8 | #[command(about = "")] // TODO: about 9 | pub(crate) struct Cli { 10 | #[command(subcommand)] 11 | pub(crate) command: Command, 12 | } 13 | 14 | #[derive(Clone, Subcommand)] 15 | pub(crate) enum Command { 16 | /// 17 | #[clap(short_flag = 'B')] 18 | Batch(Batch), 19 | 20 | /// 21 | #[clap(short_flag = 'C')] 22 | Core(Core), 23 | 24 | /// 25 | #[clap(short_flag = 'I')] 26 | Info(Info), 27 | 28 | /// 29 | #[clap(short_flag = 'R')] 30 | Registry(Registry), 31 | 32 | /// 33 | #[clap(short_flag = 'U')] 34 | Upgrade(Upgrade), 35 | } 36 | 37 | #[derive(Args, Clone)] 38 | pub(crate) struct Batch { 39 | #[clap(subcommand)] 40 | pub(crate) commands: BatchCommands, 41 | } 42 | 43 | #[derive(Args, Clone)] 44 | pub(crate) struct Core { 45 | #[clap(subcommand)] 46 | pub(crate) commands: CoreCommands, 47 | } 48 | 49 | #[derive(Args, Clone)] 50 | pub(crate) struct Info { 51 | #[clap(subcommand)] 52 | pub(crate) commands: InfoCommands, 53 | } 54 | 55 | #[derive(Args, Clone)] 56 | pub(crate) struct Registry { 57 | #[clap(subcommand)] 58 | pub(crate) commands: RegistryCommands, 59 | } 60 | 61 | #[derive(Args, Clone)] 62 | pub(crate) struct Upgrade { 63 | #[clap(subcommand)] 64 | pub(crate) commands: UpgradeCommands, 65 | } 66 | -------------------------------------------------------------------------------- /spark-cli/src/commands/core/cancel_order.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use fuels::{accounts::ViewOnlyAccount, types::Bits256}; 4 | use spark_market_sdk::SparkMarketContract; 5 | 6 | #[derive(Args, Clone)] 7 | #[command(about = "Cancels an open order")] 8 | pub(crate) struct CancelCommand { 9 | /// The b256 id of the order 10 | #[clap(long)] 11 | pub(crate) order_id: String, 12 | 13 | /// The contract id of the market 14 | #[clap(long)] 15 | pub(crate) contract_id: String, 16 | 17 | /// The URL to query 18 | /// Ex. testnet.fuel.network 19 | #[clap(long)] 20 | pub(crate) rpc: String, 21 | } 22 | 23 | impl CancelCommand { 24 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 25 | let wallet = setup(&self.rpc).await?; 26 | let contract_id = validate_contract_id(&self.contract_id)?; 27 | let order_id = Bits256::from_hex_str(&self.order_id)?; 28 | 29 | if self.order_id.len() as u64 != 64 { 30 | anyhow::bail!("Invalid order id length"); 31 | } 32 | 33 | // Initial balance prior to contract call - used to calculate contract interaction cost 34 | let balance = wallet 35 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 36 | .await?; 37 | 38 | // Connect to the deployed contract via the rpc 39 | let contract = SparkMarketContract::new(contract_id, wallet.clone()).await; 40 | 41 | let _ = contract.cancel_order(order_id).await?; 42 | 43 | // Balance post-call 44 | let new_balance = wallet 45 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 46 | .await?; 47 | 48 | println!("\nContract call cost: {}", balance - new_balance); 49 | 50 | Ok(()) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /spark-cli/src/commands/core/cancel_small_order.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use fuels::{accounts::ViewOnlyAccount, types::Bits256}; 4 | use spark_market_sdk::SparkMarketContract; 5 | 6 | #[derive(Args, Clone)] 7 | #[command(about = "Cancels a small open order")] 8 | pub(crate) struct CancelSmallCommand { 9 | /// The b256 id of the order 10 | #[clap(long)] 11 | pub(crate) order_id: String, 12 | 13 | /// The contract id of the market 14 | #[clap(long)] 15 | pub(crate) contract_id: String, 16 | 17 | /// The URL to query 18 | /// Ex. testnet.fuel.network 19 | #[clap(long)] 20 | pub(crate) rpc: String, 21 | } 22 | 23 | impl CancelSmallCommand { 24 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 25 | let wallet = setup(&self.rpc).await?; 26 | let contract_id = validate_contract_id(&self.contract_id)?; 27 | let order_id = Bits256::from_hex_str(&self.order_id)?; 28 | 29 | if self.order_id.len() as u64 != 64 { 30 | anyhow::bail!("Invalid order id length"); 31 | } 32 | 33 | // Initial balance prior to contract call - used to calculate contract interaction cost 34 | let balance = wallet 35 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 36 | .await?; 37 | 38 | // Connect to the deployed contract via the rpc 39 | let contract = SparkMarketContract::new(contract_id, wallet.clone()).await; 40 | 41 | let _ = contract.cancel_small_order(order_id).await?; 42 | 43 | // Balance post-call 44 | let new_balance = wallet 45 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 46 | .await?; 47 | 48 | println!("\nContract call cost: {}", balance - new_balance); 49 | 50 | Ok(()) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /spark-cli/src/commands/core/cli.rs: -------------------------------------------------------------------------------- 1 | use crate::commands::core::{ 2 | cancel_order::CancelCommand, cancel_small_order::CancelSmallCommand, deploy::DeployCommand, 3 | deposit::DepositCommand, deposit_for::DepositForCommand, fulfill_many::FulfillManyCommand, 4 | match_many::MatchManyCommand, open_market_order::OpenMarketCommand, open_order::OpenCommand, 5 | set_epoch::SetEpochCommand, set_matcher_fee::SetMatcherFeeCommand, 6 | set_min_order_price::SetMinOrderPriceCommand, set_min_order_size::SetMinOrderSizeCommand, 7 | set_paused::SetPausedCommand, set_protocol_fee::SetProtocolFeeCommand, 8 | set_proxy_target::SetProxyTargetCommand, withdraw::WithdrawCommand, 9 | withdraw_to_market::WithdrawToMarketCommand, 10 | }; 11 | use clap::Subcommand; 12 | 13 | #[derive(Clone, Subcommand)] 14 | pub(crate) enum CoreCommands { 15 | /// Cancel an open order 16 | #[clap(short_flag = 'C')] 17 | Cancel(CancelCommand), 18 | 19 | /// Cancel an open order 20 | #[clap(short_flag = 'F')] 21 | CancelSmall(CancelSmallCommand), 22 | 23 | /// Deploy a new market contract 24 | #[clap(short_flag = 'D')] 25 | Deploy(DeployCommand), 26 | 27 | /// Deposit into the market contract for user 28 | #[clap(short_flag = 'E')] 29 | DepositFor(DepositForCommand), 30 | 31 | /// Deposit into the market contract 32 | #[clap(short_flag = 'P')] 33 | Deposit(DepositCommand), 34 | 35 | /// Fulfill multiple orders 36 | #[clap(short_flag = 'F')] 37 | FulfillMany(FulfillManyCommand), 38 | 39 | /// Match multiple orders 40 | #[clap(short_flag = 'M')] 41 | MatchMany(MatchManyCommand), 42 | 43 | /// Open an order 44 | #[clap(short_flag = 'N')] 45 | OpenMarket(OpenMarketCommand), 46 | 47 | /// Open an order 48 | #[clap(short_flag = 'O')] 49 | Open(OpenCommand), 50 | 51 | /// Set a protocol fee 52 | #[clap(short_flag = 'E')] 53 | SetEpoch(SetEpochCommand), 54 | 55 | /// Set a minimum order price for the market 56 | #[clap(short_flag = 'P')] 57 | SetMinOrderPrice(SetMinOrderPriceCommand), 58 | 59 | /// Set a minimum order price for the market 60 | #[clap(short_flag = 'Q')] 61 | SetPaused(SetPausedCommand), 62 | 63 | /// Set a protocol fee 64 | #[clap(short_flag = 'S')] 65 | SetProtocolFee(SetProtocolFeeCommand), 66 | 67 | /// Set a matcher fee for the market 68 | #[clap(short_flag = 'T')] 69 | SetMatcherFee(SetMatcherFeeCommand), 70 | 71 | /// Set a minimum order size for the market 72 | #[clap(short_flag = 'V')] 73 | SetMinOrderSize(SetMinOrderSizeCommand), 74 | 75 | /// Withdraw from the market contract 76 | #[clap(short_flag = 'W')] 77 | Withdraw(WithdrawCommand), 78 | 79 | /// Withdraw from the market contract 80 | #[clap(short_flag = 'X')] 81 | WithdrawToMarket(WithdrawToMarketCommand), 82 | 83 | /// Set a proxy target market contract 84 | #[clap(short_flag = 'Z')] 85 | SetProxyTarget(SetProxyTargetCommand), 86 | } 87 | -------------------------------------------------------------------------------- /spark-cli/src/commands/core/deploy.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::setup; 2 | use clap::Args; 3 | use fuels::{accounts::ViewOnlyAccount, types::AssetId}; 4 | use spark_market_sdk::SparkMarketContract; 5 | use std::str::FromStr; 6 | 7 | #[derive(Args, Clone)] 8 | #[command(about = "Deploys the market to a network")] 9 | pub(crate) struct DeployCommand { 10 | /// The asset id for the base asset of the market 11 | #[clap(long)] 12 | pub(crate) base_asset: String, 13 | 14 | /// The number of decimals the base asset implements 15 | #[clap(long)] 16 | pub(crate) base_decimals: u32, 17 | 18 | /// The asset id for the quote asset of the market 19 | #[clap(long)] 20 | pub(crate) quote_asset: String, 21 | 22 | /// The number of decimals the quote asset implements 23 | #[clap(long)] 24 | pub(crate) quote_decimals: u32, 25 | 26 | /// The number of decimals the price uses 27 | #[clap(long)] 28 | pub(crate) price_decimals: u32, 29 | 30 | /// The URL to deploy to 31 | /// Ex. testnet.fuel.network 32 | #[clap(long)] 33 | pub(crate) rpc: String, 34 | } 35 | 36 | impl DeployCommand { 37 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 38 | let wallet = setup(&self.rpc).await?; 39 | 40 | if self.base_asset.len() as u64 != 66 { 41 | anyhow::bail!("Invalid base asset length"); 42 | } 43 | 44 | if self.quote_asset.len() as u64 != 66 { 45 | anyhow::bail!("Invalid quote asset length"); 46 | } 47 | 48 | let base_asset = AssetId::from_str(&self.base_asset).expect("Invalid base asset"); 49 | let quote_asset = AssetId::from_str(&self.quote_asset).expect("Invalid quote asset"); 50 | 51 | // Initial balance prior to contract call - used to calculate contract interaction cost 52 | let balance = wallet 53 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 54 | .await?; 55 | 56 | let version = SparkMarketContract::sdk_version(); 57 | 58 | // Deploy the contract 59 | let contract = SparkMarketContract::deploy( 60 | base_asset, 61 | self.base_decimals, 62 | quote_asset, 63 | self.quote_decimals, 64 | wallet.clone(), 65 | self.price_decimals, 66 | version, 67 | ) 68 | .await?; 69 | 70 | // Balance post-deployment 71 | let new_balance = wallet 72 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 73 | .await?; 74 | 75 | println!( 76 | "\nMarket version {} ({}) deployed to: 0x{}", 77 | contract.contract_str_version().await?, 78 | version, 79 | contract.id() 80 | ); 81 | println!("Deployment cost: {}", balance - new_balance); 82 | println!("Owner address: {}", wallet.address()); 83 | println!(" 0x{}", wallet.address().hash()); 84 | 85 | Ok(()) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /spark-cli/src/commands/core/deposit.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id, AssetType}; 2 | use clap::Args; 3 | use fuels::accounts::ViewOnlyAccount; 4 | use spark_market_sdk::{AssetType as ContractAssetType, SparkMarketContract}; 5 | 6 | #[derive(Args, Clone)] 7 | #[command(about = "Deposits an asset from the wallet to the market")] 8 | pub(crate) struct DepositCommand { 9 | /// The amount to deposit 10 | #[clap(long)] 11 | pub(crate) amount: u64, 12 | 13 | /// The asset type of the market 14 | #[clap(long)] 15 | pub(crate) asset_type: AssetType, 16 | 17 | /// The contract id of the market 18 | #[clap(long)] 19 | pub(crate) contract_id: String, 20 | 21 | /// The URL to query 22 | /// Ex. testnet.fuel.network 23 | #[clap(long)] 24 | pub(crate) rpc: String, 25 | } 26 | 27 | impl DepositCommand { 28 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 29 | let wallet = setup(&self.rpc).await?; 30 | let contract_id = validate_contract_id(&self.contract_id)?; 31 | 32 | let asset_type = match self.asset_type { 33 | AssetType::Base => ContractAssetType::Base, 34 | AssetType::Quote => ContractAssetType::Quote, 35 | }; 36 | 37 | // Initial balance prior to contract call - used to calculate contract interaction cost 38 | let balance = wallet 39 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 40 | .await?; 41 | 42 | // Connect to the deployed contract via the rpc 43 | let contract = SparkMarketContract::new(contract_id, wallet.clone()).await; 44 | 45 | let config = contract.config().await?.value; 46 | let asset = if asset_type == ContractAssetType::Base { 47 | config.0 48 | } else { 49 | config.2 50 | }; 51 | let asset_balance = wallet.get_asset_balance(&asset).await?; 52 | 53 | let _ = contract.deposit(self.amount, asset).await?; 54 | 55 | // Balance post-call 56 | let new_balance = wallet 57 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 58 | .await?; 59 | let new_asset_balance = wallet.get_asset_balance(&asset).await?; 60 | 61 | println!("\nContract call cost: {}", balance - new_balance); 62 | println!( 63 | "Deposited {} amount of asset {}", 64 | self.amount, 65 | asset_balance - new_asset_balance 66 | ); 67 | 68 | Ok(()) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /spark-cli/src/commands/core/deposit_for.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id, AccountType, AssetType}; 2 | use clap::Args; 3 | use fuels::accounts::ViewOnlyAccount; 4 | use fuels::types::{Address, ContractId, Identity}; 5 | use spark_market_sdk::{AssetType as ContractAssetType, SparkMarketContract}; 6 | use std::str::FromStr; 7 | 8 | #[derive(Args, Clone)] 9 | #[command(about = "Deposits an asset from the wallet to the market for the account")] 10 | pub(crate) struct DepositForCommand { 11 | /// The amount to deposit 12 | #[clap(long)] 13 | pub(crate) amount: u64, 14 | 15 | /// The asset type of the market 16 | #[clap(long)] 17 | pub(crate) asset_type: AssetType, 18 | 19 | /// The b256 id of the account 20 | #[clap(long)] 21 | pub(crate) account_id: String, 22 | 23 | /// The type of account 24 | #[clap(long)] 25 | pub(crate) account_type: AccountType, 26 | 27 | /// The contract id of the market 28 | #[clap(long)] 29 | pub(crate) contract_id: String, 30 | 31 | /// The URL to query 32 | /// Ex. testnet.fuel.network 33 | #[clap(long)] 34 | pub(crate) rpc: String, 35 | } 36 | 37 | impl DepositForCommand { 38 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 39 | let wallet = setup(&self.rpc).await?; 40 | let contract_id = validate_contract_id(&self.contract_id)?; 41 | 42 | let asset_type = match self.asset_type { 43 | AssetType::Base => ContractAssetType::Base, 44 | AssetType::Quote => ContractAssetType::Quote, 45 | }; 46 | 47 | let account = match self.account_type { 48 | AccountType::Address => { 49 | let address = Address::from_str(&self.account_id).expect("Invalid address"); 50 | Identity::Address(address) 51 | } 52 | AccountType::Contract => { 53 | let address = ContractId::from_str(&self.account_id).expect("Invalid contract id"); 54 | Identity::ContractId(address) 55 | } 56 | }; 57 | 58 | // Initial balance prior to contract call - used to calculate contract interaction cost 59 | let balance = wallet 60 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 61 | .await?; 62 | 63 | // Connect to the deployed contract via the rpc 64 | let contract = SparkMarketContract::new(contract_id, wallet.clone()).await; 65 | 66 | let config = contract.config().await?.value; 67 | let asset = if asset_type == ContractAssetType::Base { 68 | config.0 69 | } else { 70 | config.2 71 | }; 72 | let asset_balance = wallet.get_asset_balance(&asset).await?; 73 | 74 | let _ = contract.deposit_for(self.amount, asset, account).await?; 75 | 76 | // Balance post-call 77 | let new_balance = wallet 78 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 79 | .await?; 80 | let new_asset_balance = wallet.get_asset_balance(&asset).await?; 81 | 82 | println!("\nContract call cost: {}", balance - new_balance); 83 | println!( 84 | "Deposited {} amount of asset {} for {:?}", 85 | self.amount, 86 | asset_balance - new_asset_balance, 87 | account, 88 | ); 89 | 90 | Ok(()) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /spark-cli/src/commands/core/fulfill_many.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id, /*AssetType,*/ LimitType, OrderType}; 2 | use clap::Args; 3 | use fuels::{ 4 | accounts::ViewOnlyAccount, 5 | types::{Bits256, ContractId}, 6 | }; 7 | use spark_market_sdk::{ 8 | /*AssetType as ContractAssetType,*/ LimitType as ContractLimitType, 9 | OrderType as ContractOrderType, SparkMarketContract, 10 | }; 11 | 12 | #[derive(Args, Clone)] 13 | #[command(about = "Fulfill a new order")] 14 | pub(crate) struct FulfillManyCommand { 15 | /// The amount of asset 16 | #[clap(long)] 17 | pub(crate) amount: u64, 18 | 19 | /// The asset type of the market 20 | /*#[clap(long)] 21 | pub(crate) asset_type: AssetType,*/ 22 | 23 | /// The type of order 24 | #[clap(long)] 25 | pub(crate) order_type: OrderType, 26 | 27 | /// The type of order 28 | #[clap(long)] 29 | pub(crate) limit_type: LimitType, 30 | 31 | /// The price of the order 32 | #[clap(long)] 33 | pub(crate) price: u64, 34 | 35 | /// The slippage of the order price 36 | #[clap(long)] 37 | pub(crate) slippage: u64, 38 | 39 | /// The b256 id of the order 40 | #[clap(long)] 41 | pub(crate) orders: Vec, 42 | 43 | /// The contract id of the market 44 | #[clap(long)] 45 | pub(crate) contract_id: String, 46 | 47 | /// The URL to query 48 | /// Ex. testnet.fuel.network 49 | #[clap(long)] 50 | pub(crate) rpc: String, 51 | } 52 | 53 | impl FulfillManyCommand { 54 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 55 | let wallet = setup(&self.rpc).await?; 56 | let contract_id = validate_contract_id(&self.contract_id)?; 57 | 58 | if self.orders.len() == 0 { 59 | anyhow::bail!("Invalid order array length == 0"); 60 | } 61 | 62 | let mut order_ids: Vec = Vec::new(); 63 | for order in self.orders.clone() { 64 | order_ids.push(Bits256::from_hex_str(&order).expect("Invalid order_id")); 65 | } 66 | 67 | let limit_type = match self.limit_type { 68 | LimitType::IOC => ContractLimitType::IOC, 69 | LimitType::FOK => ContractLimitType::FOK, 70 | }; 71 | let order_type = match self.order_type { 72 | OrderType::Buy => ContractOrderType::Buy, 73 | OrderType::Sell => ContractOrderType::Sell, 74 | }; 75 | 76 | // Initial balance prior to contract call - used to calculate contract interaction cost 77 | let balance = wallet 78 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 79 | .await?; 80 | 81 | // Connect to the deployed contract via the rpc 82 | let contract = SparkMarketContract::new(contract_id, wallet.clone()).await; 83 | 84 | let order_id = contract 85 | .fulfill_many( 86 | self.amount, 87 | order_type.clone(), 88 | limit_type.clone(), 89 | self.price, 90 | self.slippage, 91 | order_ids, 92 | ) 93 | .await? 94 | .value; 95 | 96 | // Balance post-call 97 | let new_balance = wallet 98 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 99 | .await?; 100 | 101 | println!("\nContract call cost: {}", balance - new_balance); 102 | println!("Order ID: {}", ContractId::from(order_id.0)); 103 | 104 | Ok(()) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /spark-cli/src/commands/core/match_many.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use fuels::{accounts::ViewOnlyAccount, types::Bits256}; 4 | use spark_market_sdk::SparkMarketContract; 5 | 6 | #[derive(Args, Clone)] 7 | #[command(about = "Matches many orders")] 8 | pub(crate) struct MatchManyCommand { 9 | /// The b256 id of the order 10 | #[clap(long)] 11 | pub(crate) orders: Vec, 12 | 13 | /// The contract id of the market 14 | #[clap(long)] 15 | pub(crate) contract_id: String, 16 | 17 | /// The URL to query 18 | /// Ex. testnet.fuel.network 19 | #[clap(long)] 20 | pub(crate) rpc: String, 21 | } 22 | 23 | impl MatchManyCommand { 24 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 25 | let wallet = setup(&self.rpc).await?; 26 | let contract_id = validate_contract_id(&self.contract_id)?; 27 | 28 | if self.orders.len() < 2 { 29 | anyhow::bail!("Invalid order array length < 2"); 30 | } 31 | 32 | let mut order_ids: Vec = Vec::new(); 33 | for order in self.orders.clone() { 34 | order_ids.push(Bits256::from_hex_str(&order).expect("Invalid order_id")); 35 | } 36 | 37 | // Initial balance prior to contract call - used to calculate contract interaction cost 38 | let balance = wallet 39 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 40 | .await?; 41 | 42 | // Connect to the deployed contract via the rpc 43 | let contract = SparkMarketContract::new(contract_id, wallet.clone()).await; 44 | 45 | let _ = contract.match_order_many(order_ids).await?; 46 | 47 | // Balance post-call 48 | let new_balance = wallet 49 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 50 | .await?; 51 | 52 | println!("Orders matched: {:?}", self.orders,); 53 | 54 | // TODO: replace println with tracing 55 | println!("Contract call cost: {}", balance - new_balance); 56 | 57 | Ok(()) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /spark-cli/src/commands/core/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod cancel_order; 2 | pub(crate) mod cancel_small_order; 3 | pub(crate) mod cli; 4 | pub(crate) mod deploy; 5 | pub(crate) mod deposit; 6 | pub(crate) mod deposit_for; 7 | pub(crate) mod fulfill_many; 8 | pub(crate) mod match_many; 9 | pub(crate) mod open_market_order; 10 | pub(crate) mod open_order; 11 | pub(crate) mod set_epoch; 12 | pub(crate) mod set_matcher_fee; 13 | pub(crate) mod set_min_order_price; 14 | pub(crate) mod set_min_order_size; 15 | pub(crate) mod set_paused; 16 | pub(crate) mod set_protocol_fee; 17 | pub(crate) mod set_proxy_target; 18 | pub(crate) mod withdraw; 19 | pub(crate) mod withdraw_to_market; 20 | -------------------------------------------------------------------------------- /spark-cli/src/commands/core/open_market_order.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id, /*AssetType,*/ OrderType}; 2 | use clap::Args; 3 | use fuels::{accounts::ViewOnlyAccount, types::ContractId}; 4 | use spark_market_sdk::{ 5 | OrderType as ContractOrderType, /*AssetType as ContractAssetType,*/ SparkMarketContract, 6 | }; 7 | 8 | #[derive(Args, Clone)] 9 | #[command(about = "Opens a new order")] 10 | pub(crate) struct OpenMarketCommand { 11 | /// The amount of asset 12 | #[clap(long)] 13 | pub(crate) amount: u64, 14 | 15 | /// The asset type of the market 16 | /*#[clap(long)] 17 | pub(crate) asset_type: AssetType,*/ 18 | 19 | /// The type of order 20 | #[clap(long)] 21 | pub(crate) order_type: OrderType, 22 | 23 | /// The price of the order 24 | #[clap(long)] 25 | pub(crate) price: u64, 26 | 27 | /// The contract id of the market 28 | #[clap(long)] 29 | pub(crate) contract_id: String, 30 | 31 | /// The URL to query 32 | /// Ex. testnet.fuel.network 33 | #[clap(long)] 34 | pub(crate) rpc: String, 35 | } 36 | 37 | impl OpenMarketCommand { 38 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 39 | let wallet = setup(&self.rpc).await?; 40 | let contract_id = validate_contract_id(&self.contract_id)?; 41 | 42 | let order_type = match self.order_type { 43 | OrderType::Buy => ContractOrderType::Buy, 44 | OrderType::Sell => ContractOrderType::Sell, 45 | }; 46 | 47 | // Initial balance prior to contract call - used to calculate contract interaction cost 48 | let balance = wallet 49 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 50 | .await?; 51 | 52 | // Connect to the deployed contract via the rpc 53 | let contract = SparkMarketContract::new(contract_id, wallet.clone()).await; 54 | 55 | let order_id = contract 56 | .open_market_order(self.amount, order_type.clone(), self.price) 57 | .await? 58 | .value; 59 | 60 | // Balance post-call 61 | let new_balance = wallet 62 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 63 | .await?; 64 | 65 | println!("\nContract call cost: {}", balance - new_balance); 66 | println!("Order ID: {}", ContractId::from(order_id.0)); 67 | 68 | Ok(()) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /spark-cli/src/commands/core/open_order.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id, /*AssetType,*/ OrderType}; 2 | use clap::Args; 3 | use fuels::{accounts::ViewOnlyAccount, types::ContractId}; 4 | use spark_market_sdk::{ 5 | OrderType as ContractOrderType, /*AssetType as ContractAssetType,*/ SparkMarketContract, 6 | }; 7 | 8 | #[derive(Args, Clone)] 9 | #[command(about = "Opens a new order")] 10 | pub(crate) struct OpenCommand { 11 | /// The amount of asset 12 | #[clap(long)] 13 | pub(crate) amount: u64, 14 | 15 | /// The asset type of the market 16 | /*#[clap(long)] 17 | pub(crate) asset_type: AssetType,*/ 18 | 19 | /// The type of order 20 | #[clap(long)] 21 | pub(crate) order_type: OrderType, 22 | 23 | /// The price of the order 24 | #[clap(long)] 25 | pub(crate) price: u64, 26 | 27 | /// The contract id of the market 28 | #[clap(long)] 29 | pub(crate) contract_id: String, 30 | 31 | /// The URL to query 32 | /// Ex. testnet.fuel.network 33 | #[clap(long)] 34 | pub(crate) rpc: String, 35 | } 36 | 37 | impl OpenCommand { 38 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 39 | let wallet = setup(&self.rpc).await?; 40 | let contract_id = validate_contract_id(&self.contract_id)?; 41 | 42 | let order_type = match self.order_type { 43 | OrderType::Buy => ContractOrderType::Buy, 44 | OrderType::Sell => ContractOrderType::Sell, 45 | }; 46 | 47 | // Initial balance prior to contract call - used to calculate contract interaction cost 48 | let balance = wallet 49 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 50 | .await?; 51 | 52 | // Connect to the deployed contract via the rpc 53 | let contract = SparkMarketContract::new(contract_id, wallet.clone()).await; 54 | 55 | let order_id = contract 56 | .open_order(self.amount, order_type.clone(), self.price) 57 | .await? 58 | .value; 59 | 60 | // Balance post-call 61 | let new_balance = wallet 62 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 63 | .await?; 64 | 65 | println!("\nContract call cost: {}", balance - new_balance); 66 | println!("Order ID: {}", ContractId::from(order_id.0)); 67 | 68 | Ok(()) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /spark-cli/src/commands/core/set_epoch.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use fuels::accounts::ViewOnlyAccount; 4 | use spark_market_sdk::SparkMarketContract; 5 | 6 | #[derive(Args, Clone)] 7 | #[command(about = "Change the epoch and epoch duration for the market")] 8 | pub(crate) struct SetEpochCommand { 9 | /// The epoch to set 10 | #[clap(long)] 11 | pub(crate) epoch: u64, 12 | 13 | /// The epoch duration to set 14 | #[clap(long)] 15 | pub(crate) epoch_duration: u64, 16 | 17 | /// The contract id of the market 18 | #[clap(long)] 19 | pub(crate) contract_id: String, 20 | 21 | /// The URL to query 22 | /// Ex. testnet.fuel.network 23 | #[clap(long)] 24 | pub(crate) rpc: String, 25 | } 26 | 27 | impl SetEpochCommand { 28 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 29 | let wallet = setup(&self.rpc).await?; 30 | let contract_id = validate_contract_id(&self.contract_id)?; 31 | 32 | // Initial balance prior to contract call - used to calculate contract interaction cost 33 | let balance = wallet 34 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 35 | .await?; 36 | 37 | // Connect to the deployed contract via the rpc 38 | let contract = SparkMarketContract::new(contract_id, wallet.clone()).await; 39 | 40 | let _ = contract.set_epoch(self.epoch, self.epoch_duration).await?; 41 | 42 | // Balance post-deployment 43 | let new_balance = wallet 44 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 45 | .await?; 46 | 47 | println!( 48 | "\nThe epoch and duration have been set to: {}, {}", 49 | self.epoch, self.epoch_duration 50 | ); 51 | println!("Contract call cost: {}", balance - new_balance); 52 | 53 | Ok(()) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /spark-cli/src/commands/core/set_matcher_fee.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use fuels::accounts::ViewOnlyAccount; 4 | use spark_market_sdk::SparkMarketContract; 5 | 6 | #[derive(Args, Clone)] 7 | #[command(about = "Change the matcher fee for the market")] 8 | pub(crate) struct SetMatcherFeeCommand { 9 | /// The fee to set 10 | #[clap(long)] 11 | pub(crate) amount: u64, 12 | 13 | /// The contract id of the market 14 | #[clap(long)] 15 | pub(crate) contract_id: String, 16 | 17 | /// The URL to query 18 | /// Ex. testnet.fuel.network 19 | #[clap(long)] 20 | pub(crate) rpc: String, 21 | } 22 | 23 | impl SetMatcherFeeCommand { 24 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 25 | let wallet = setup(&self.rpc).await?; 26 | let contract_id = validate_contract_id(&self.contract_id)?; 27 | 28 | // Initial balance prior to contract call - used to calculate contract interaction cost 29 | let balance = wallet 30 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 31 | .await?; 32 | 33 | // Connect to the deployed contract via the rpc 34 | let contract = SparkMarketContract::new(contract_id, wallet.clone()).await; 35 | 36 | let _ = contract.set_matcher_fee(self.amount).await?; 37 | 38 | // Balance post-deployment 39 | let new_balance = wallet 40 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 41 | .await?; 42 | 43 | println!("\nThe matcher fee has been set to: {}", self.amount); 44 | println!("Contract call cost: {}", balance - new_balance); 45 | 46 | Ok(()) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /spark-cli/src/commands/core/set_min_order_price.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use fuels::accounts::ViewOnlyAccount; 4 | use spark_market_sdk::SparkMarketContract; 5 | 6 | #[derive(Args, Clone)] 7 | #[command(about = "Change the min order price for the market")] 8 | pub(crate) struct SetMinOrderPriceCommand { 9 | /// The fee to set 10 | #[clap(long)] 11 | pub(crate) price: u64, 12 | 13 | /// The contract id of the market 14 | #[clap(long)] 15 | pub(crate) contract_id: String, 16 | 17 | /// The URL to query 18 | /// Ex. testnet.fuel.network 19 | #[clap(long)] 20 | pub(crate) rpc: String, 21 | } 22 | 23 | impl SetMinOrderPriceCommand { 24 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 25 | let wallet = setup(&self.rpc).await?; 26 | let contract_id = validate_contract_id(&self.contract_id)?; 27 | 28 | // Initial balance prior to contract call - used to calculate contract interaction cost 29 | let balance = wallet 30 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 31 | .await?; 32 | 33 | // Connect to the deployed contract via the rpc 34 | let contract = SparkMarketContract::new(contract_id, wallet.clone()).await; 35 | 36 | let _ = contract.set_min_order_price(self.price).await?; 37 | 38 | // Balance post-deployment 39 | let new_balance = wallet 40 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 41 | .await?; 42 | 43 | println!( 44 | "\nThe min order price has been set to: {}", 45 | contract.min_order_price().await?.value 46 | ); 47 | println!("Contract call cost: {}", balance - new_balance); 48 | 49 | Ok(()) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /spark-cli/src/commands/core/set_min_order_size.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use fuels::accounts::ViewOnlyAccount; 4 | use spark_market_sdk::SparkMarketContract; 5 | 6 | #[derive(Args, Clone)] 7 | #[command(about = "Change the min order size for the market")] 8 | pub(crate) struct SetMinOrderSizeCommand { 9 | /// The fee to set 10 | #[clap(long)] 11 | pub(crate) size: u64, 12 | 13 | /// The contract id of the market 14 | #[clap(long)] 15 | pub(crate) contract_id: String, 16 | 17 | /// The URL to query 18 | /// Ex. testnet.fuel.network 19 | #[clap(long)] 20 | pub(crate) rpc: String, 21 | } 22 | 23 | impl SetMinOrderSizeCommand { 24 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 25 | let wallet = setup(&self.rpc).await?; 26 | let contract_id = validate_contract_id(&self.contract_id)?; 27 | 28 | // Initial balance prior to contract call - used to calculate contract interaction cost 29 | let balance = wallet 30 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 31 | .await?; 32 | 33 | // Connect to the deployed contract via the rpc 34 | let contract = SparkMarketContract::new(contract_id, wallet.clone()).await; 35 | 36 | let _ = contract.set_min_order_size(self.size).await?; 37 | 38 | // Balance post-deployment 39 | let new_balance = wallet 40 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 41 | .await?; 42 | 43 | println!( 44 | "\nThe min order size has been set to: {}", 45 | contract.min_order_size().await?.value 46 | ); 47 | println!("Contract call cost: {}", balance - new_balance); 48 | 49 | Ok(()) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /spark-cli/src/commands/core/set_paused.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use fuels::accounts::ViewOnlyAccount; 4 | use spark_market_sdk::SparkMarketContract; 5 | 6 | #[derive(Args, Clone)] 7 | #[command(about = "Change the paused state for the market")] 8 | pub(crate) struct SetPausedCommand { 9 | /// The store to paused 10 | #[clap(long)] 11 | pub(crate) paused: bool, 12 | 13 | /// The contract id of the market 14 | #[clap(long)] 15 | pub(crate) contract_id: String, 16 | 17 | /// The URL to query 18 | /// Ex. testnet.fuel.network 19 | #[clap(long)] 20 | pub(crate) rpc: String, 21 | } 22 | 23 | impl SetPausedCommand { 24 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 25 | let wallet = setup(&self.rpc).await?; 26 | let contract_id = validate_contract_id(&self.contract_id)?; 27 | 28 | // Initial balance prior to contract call - used to calculate contract interaction cost 29 | let balance = wallet 30 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 31 | .await?; 32 | 33 | // Connect to the deployed contract via the rpc 34 | let contract = SparkMarketContract::new(contract_id, wallet.clone()).await; 35 | 36 | if self.paused { 37 | let _ = contract.pause().await?; 38 | } else { 39 | let _ = contract.unpause().await?; 40 | } 41 | 42 | // Balance post-deployment 43 | let new_balance = wallet 44 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 45 | .await?; 46 | 47 | println!("\nThe pause state of market is set to: {}", self.paused); 48 | println!("Contract call cost: {}", balance - new_balance); 49 | 50 | Ok(()) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /spark-cli/src/commands/core/set_protocol_fee.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use fuels::accounts::ViewOnlyAccount; 4 | use spark_market_sdk::{ProtocolFee, SparkMarketContract}; 5 | 6 | #[derive(Args, Clone)] 7 | #[command(about = "Change the protocol fee")] 8 | pub(crate) struct SetProtocolFeeCommand { 9 | /// The fee to set 10 | #[clap(long)] 11 | pub(crate) fee: Vec, 12 | 13 | /// The contract id of the market 14 | #[clap(long)] 15 | pub(crate) contract_id: String, 16 | 17 | /// The URL to query 18 | /// Ex. testnet.fuel.network 19 | #[clap(long)] 20 | pub(crate) rpc: String, 21 | } 22 | 23 | impl SetProtocolFeeCommand { 24 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 25 | let wallet = setup(&self.rpc).await?; 26 | let contract_id = validate_contract_id(&self.contract_id)?; 27 | 28 | let mut protocol_fee: Vec = Vec::new(); 29 | for fee in self.fee.clone() { 30 | let fee = fee.split(',').collect::>(); 31 | assert_eq!(fee.len(), 3); 32 | let fee = fee 33 | .iter() 34 | .map(|&x| x.parse::().unwrap()) 35 | .collect::>(); 36 | let fee = ProtocolFee { 37 | maker_fee: fee[0], 38 | taker_fee: fee[1], 39 | volume_threshold: fee[2], 40 | }; 41 | protocol_fee.push(fee); 42 | } 43 | 44 | // Initial balance prior to contract call - used to calculate contract interaction cost 45 | let balance = wallet 46 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 47 | .await?; 48 | 49 | // Connect to the deployed contract via the rpc 50 | let contract = SparkMarketContract::new(contract_id, wallet.clone()).await; 51 | 52 | let _ = contract.set_protocol_fee(protocol_fee).await?; 53 | 54 | // Balance post-deployment 55 | let new_balance = wallet 56 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 57 | .await?; 58 | 59 | println!("\nThe global fee has been set to: {:?}", self.fee); 60 | println!("Contract call cost: {}", balance - new_balance); 61 | 62 | Ok(()) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /spark-cli/src/commands/core/set_proxy_target.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use fuels::accounts::ViewOnlyAccount; 4 | use spark_proxy_sdk::SparkProxyContract; 5 | 6 | #[derive(Args, Clone)] 7 | #[command(about = "Change the proxy target")] 8 | pub(crate) struct SetProxyTargetCommand { 9 | /// The target to set 10 | #[clap(long)] 11 | pub(crate) target: String, 12 | 13 | /// The contract id of the market 14 | #[clap(long)] 15 | pub(crate) contract_id: String, 16 | 17 | /// The URL to query 18 | /// Ex. testnet.fuel.network 19 | #[clap(long)] 20 | pub(crate) rpc: String, 21 | } 22 | 23 | impl SetProxyTargetCommand { 24 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 25 | let wallet = setup(&self.rpc).await?; 26 | let contract_id = validate_contract_id(&self.contract_id)?; 27 | let target = validate_contract_id(&self.target)?; 28 | 29 | // Initial balance prior to contract call - used to calculate contract interaction cost 30 | let balance = wallet 31 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 32 | .await?; 33 | 34 | // Connect to the deployed contract via the rpc 35 | let contract = SparkProxyContract::new(contract_id, wallet.clone()).await; 36 | 37 | let _ = contract.set_proxy_target(target).await?; 38 | 39 | // Balance post-deployment 40 | let new_balance = wallet 41 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 42 | .await?; 43 | 44 | println!("\nThe proxy target has been set to: {:?}", self.target); 45 | println!("Contract call cost: {}", balance - new_balance); 46 | 47 | Ok(()) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /spark-cli/src/commands/core/withdraw.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id, AssetType}; 2 | use clap::Args; 3 | use fuels::accounts::ViewOnlyAccount; 4 | use spark_market_sdk::{AssetType as ContractAssetType, SparkMarketContract}; 5 | 6 | #[derive(Args, Clone)] 7 | #[command(about = "Withdraws an asset from the market to the caller")] 8 | pub(crate) struct WithdrawCommand { 9 | /// The amount to withdraw 10 | #[clap(long)] 11 | pub(crate) amount: u64, 12 | 13 | /// The asset type of the market 14 | #[clap(long)] 15 | pub(crate) asset_type: AssetType, 16 | 17 | /// The contract id of the market 18 | #[clap(long)] 19 | pub(crate) contract_id: String, 20 | 21 | /// The URL to query 22 | /// Ex. testnet.fuel.network 23 | #[clap(long)] 24 | pub(crate) rpc: String, 25 | } 26 | 27 | impl WithdrawCommand { 28 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 29 | let wallet = setup(&self.rpc).await?; 30 | let contract_id = validate_contract_id(&self.contract_id)?; 31 | 32 | let asset_type = match self.asset_type { 33 | AssetType::Base => ContractAssetType::Base, 34 | AssetType::Quote => ContractAssetType::Quote, 35 | }; 36 | 37 | // Initial balance prior to contract call - used to calculate contract interaction cost 38 | let balance = wallet 39 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 40 | .await?; 41 | 42 | // Connect to the deployed contract via the rpc 43 | let contract = SparkMarketContract::new(contract_id, wallet.clone()).await; 44 | let config = contract.config().await?.value; 45 | let asset = if asset_type == ContractAssetType::Base { 46 | config.0 47 | } else { 48 | config.2 49 | }; 50 | let asset_balance = wallet.get_asset_balance(&asset).await?; 51 | 52 | let _ = contract.withdraw(self.amount, asset_type.clone()).await?; 53 | 54 | // Balance post-call 55 | let new_balance = wallet 56 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 57 | .await?; 58 | let new_asset_balance = wallet.get_asset_balance(&asset).await?; 59 | 60 | println!("Contract call cost: {}", balance - new_balance); 61 | println!( 62 | "Withdrawn {} amount of {:?} asset", 63 | new_asset_balance - asset_balance, 64 | asset_type.clone() 65 | ); 66 | 67 | Ok(()) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /spark-cli/src/commands/core/withdraw_to_market.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id, AssetType}; 2 | use clap::Args; 3 | use fuels::accounts::ViewOnlyAccount; 4 | use spark_market_sdk::{AssetType as ContractAssetType, SparkMarketContract}; 5 | 6 | #[derive(Args, Clone)] 7 | #[command(about = "Withdraws an asset from the market to another market caller account")] 8 | pub(crate) struct WithdrawToMarketCommand { 9 | /// The amount to withdraw 10 | #[clap(long)] 11 | pub(crate) amount: u64, 12 | 13 | /// The asset type of the market 14 | #[clap(long)] 15 | pub(crate) asset_type: AssetType, 16 | 17 | /// The contract id of the deposit market 18 | #[clap(long)] 19 | pub(crate) market_id: String, 20 | 21 | /// The contract id of the withdraw market 22 | #[clap(long)] 23 | pub(crate) contract_id: String, 24 | 25 | /// The URL to query 26 | /// Ex. testnet.fuel.network 27 | #[clap(long)] 28 | pub(crate) rpc: String, 29 | } 30 | 31 | impl WithdrawToMarketCommand { 32 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 33 | let wallet = setup(&self.rpc).await?; 34 | let contract_id = validate_contract_id(&self.contract_id)?; 35 | let market_id = validate_contract_id(&self.market_id)?; 36 | 37 | let asset_type = match self.asset_type { 38 | AssetType::Base => ContractAssetType::Base, 39 | AssetType::Quote => ContractAssetType::Quote, 40 | }; 41 | 42 | // Initial balance prior to contract call - used to calculate contract interaction cost 43 | let balance = wallet 44 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 45 | .await?; 46 | 47 | // Connect to the deployed contract via the rpc 48 | let contract = SparkMarketContract::new(contract_id, wallet.clone()).await; 49 | let config = contract.config().await?.value; 50 | let asset = if asset_type == ContractAssetType::Base { 51 | config.0 52 | } else { 53 | config.2 54 | }; 55 | let asset_balance = wallet.get_asset_balance(&asset).await?; 56 | 57 | let _ = contract 58 | .withdraw_to_market(self.amount, asset_type.clone(), &market_id.into()) 59 | .await?; 60 | 61 | // Balance post-call 62 | let new_balance = wallet 63 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 64 | .await?; 65 | let new_asset_balance = wallet.get_asset_balance(&asset).await?; 66 | 67 | println!("Contract call cost: {}", balance - new_balance); 68 | println!( 69 | "Withdrawn {} amount of {:?} asset to {:?}", 70 | new_asset_balance - asset_balance, 71 | asset_type.clone(), 72 | market_id, 73 | ); 74 | 75 | Ok(()) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /spark-cli/src/commands/info/account.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id, AccountType}; 2 | use clap::Args; 3 | use fuels::accounts::ViewOnlyAccount; 4 | use fuels::types::{Address, ContractId, Identity}; 5 | use spark_market_sdk::SparkMarketContract; 6 | use std::str::FromStr; 7 | 8 | #[derive(Args, Clone)] 9 | #[command(about = "Query the account info for a user")] 10 | pub(crate) struct AccountCommand { 11 | /// The b256 id of the account 12 | #[clap(long)] 13 | pub(crate) account_id: String, 14 | 15 | /// The type of account 16 | #[clap(long)] 17 | pub(crate) account_type: AccountType, 18 | 19 | /// The contract id of the market 20 | #[clap(long)] 21 | pub(crate) contract_id: String, 22 | 23 | /// The URL to query 24 | /// Ex. testnet.fuel.network 25 | #[clap(long)] 26 | pub(crate) rpc: String, 27 | } 28 | 29 | impl AccountCommand { 30 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 31 | let wallet = setup(&self.rpc).await?; 32 | let contract_id = validate_contract_id(&self.contract_id)?; 33 | 34 | // Connect to the deployed contract via the rpc 35 | let contract = SparkMarketContract::new(contract_id, wallet.clone()).await; 36 | 37 | let account = match self.account_type { 38 | AccountType::Address => { 39 | let address = Address::from_str(&self.account_id).expect("Invalid address"); 40 | contract.account(Identity::Address(address)).await?.value 41 | } 42 | AccountType::Contract => { 43 | let address = ContractId::from_str(&self.account_id).expect("Invalid contract id"); 44 | contract.account(Identity::ContractId(address)).await?.value 45 | } 46 | }; 47 | 48 | let balance = wallet 49 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 50 | .await?; 51 | println!("\nContract base asset balance: {}", balance); 52 | 53 | println!("\n{:#?}", account); 54 | 55 | Ok(()) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /spark-cli/src/commands/info/cli.rs: -------------------------------------------------------------------------------- 1 | use crate::commands::info::{ 2 | account::AccountCommand, config::ConfigCommand, epoch::EpochCommand, 3 | market_order::MarketOrderCommand, matcher_fee::MatcherFeeCommand, 4 | min_order_price::MinOrderPriceCommand, min_order_size::MinOrderSizeCommand, 5 | order::OrderCommand, order_id::OrderIdCommand, paused::PausedCommand, 6 | protocol_fee::ProtocolFeeCommand, protocol_fee_user::ProtocolFeeUserCommand, 7 | protocol_fee_user_amount::ProtocolFeeUserAmountCommand, proxy_owner::ProxyOwnerCommand, 8 | proxy_target::ProxyTargetCommand, user_orders::UserOrdersCommand, 9 | }; 10 | use clap::Subcommand; 11 | 12 | #[derive(Clone, Subcommand)] 13 | pub(crate) enum InfoCommands { 14 | /// Query account information 15 | #[clap(short_flag = 'A')] 16 | Account(AccountCommand), 17 | 18 | /// Query configuration information for a market contract 19 | #[clap(short_flag = 'C')] 20 | Config(ConfigCommand), 21 | 22 | /// Query epoch information of the market contract 23 | #[clap(short_flag = 'W')] 24 | Epoch(EpochCommand), 25 | 26 | /// Query protocol fee information 27 | #[clap(short_flag = 'F')] 28 | ProtocolFee(ProtocolFeeCommand), 29 | 30 | /// Query fee information for a specific user and amount of the market contract 31 | #[clap(short_flag = 'E')] 32 | ProtocolFeeUserAmount(ProtocolFeeUserAmountCommand), 33 | 34 | /// Query fee information for a specific user of the market contract 35 | #[clap(short_flag = 'B')] 36 | ProtocolFeeUser(ProtocolFeeUserCommand), 37 | 38 | /// Query matcher fee information of the market contract 39 | #[clap(short_flag = 'M')] 40 | MatcherFee(MatcherFeeCommand), 41 | 42 | /// Query minimum order size of the market contract 43 | #[clap(short_flag = 'N')] 44 | MinOrderSize(MinOrderSizeCommand), 45 | 46 | /// Query minimum order price of the market contract 47 | #[clap(short_flag = 'P')] 48 | MinOrderPrice(MinOrderPriceCommand), 49 | 50 | /// Calculate the order id given the provided arguments 51 | #[clap(short_flag = 'I')] 52 | OrderId(OrderIdCommand), 53 | 54 | /// Query market order information 55 | #[clap(short_flag = 'M')] 56 | MarketOrder(MarketOrderCommand), 57 | 58 | /// Query order information 59 | #[clap(short_flag = 'O')] 60 | Order(OrderCommand), 61 | 62 | /// Query paused market state 63 | #[clap(short_flag = 'P')] 64 | Paused(PausedCommand), 65 | 66 | /// Query information for a proxy target 67 | #[clap(short_flag = 'T')] 68 | ProxyTarget(ProxyTargetCommand), 69 | 70 | /// Query orders associated with an 71 | #[clap(short_flag = 'U')] 72 | UserOrders(UserOrdersCommand), 73 | 74 | /// Query information for a proxy owner 75 | #[clap(short_flag = 'V')] 76 | ProxyOwner(ProxyOwnerCommand), 77 | } 78 | -------------------------------------------------------------------------------- /spark-cli/src/commands/info/config.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use spark_market_sdk::SparkMarketContract; 4 | 5 | #[derive(Args, Clone)] 6 | #[command(about = "Query the market for its configurable variables")] 7 | pub(crate) struct ConfigCommand { 8 | /// The contract id of the market 9 | #[clap(long)] 10 | pub(crate) contract_id: String, 11 | 12 | /// The URL to query 13 | /// Ex. testnet.fuel.network 14 | #[clap(long)] 15 | pub(crate) rpc: String, 16 | } 17 | 18 | impl ConfigCommand { 19 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 20 | let wallet = setup(&self.rpc).await?; 21 | let contract_id = validate_contract_id(&self.contract_id)?; 22 | 23 | // Connect to the deployed contract via the rpc 24 | let contract = SparkMarketContract::new(contract_id, wallet).await; 25 | 26 | let ( 27 | base_asset, 28 | base_asset_decimals, 29 | quote_asset, 30 | quote_asset_decimals, 31 | owner, 32 | price_decimals, 33 | version, 34 | ) = contract.config().await?.value; 35 | 36 | println!("\nBase Asset: 0x{}", base_asset); 37 | println!("Base Asset Decimals: {}", base_asset_decimals); 38 | println!("Quote Asset: 0x{}", quote_asset); 39 | println!("Quote Asset Decimals: {}", quote_asset_decimals); 40 | println!("Owner: {:?}", owner); 41 | println!("Price Decimals: {}", price_decimals); 42 | println!( 43 | "Version: {}.{}.{}", 44 | (version & 0xFF0000) >> 16, 45 | (version & 0xFF00) >> 8, 46 | version & 0xFF 47 | ); 48 | 49 | Ok(()) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /spark-cli/src/commands/info/epoch.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use spark_market_sdk::SparkMarketContract; 4 | 5 | #[derive(Args, Clone)] 6 | #[command(about = "Query the epoch")] 7 | pub(crate) struct EpochCommand { 8 | /// The contract id of the market 9 | #[clap(long)] 10 | pub(crate) contract_id: String, 11 | 12 | /// The URL to query 13 | /// Ex. testnet.fuel.network 14 | #[clap(long)] 15 | pub(crate) rpc: String, 16 | } 17 | 18 | impl EpochCommand { 19 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 20 | let wallet = setup(&self.rpc).await?; 21 | let contract_id = validate_contract_id(&self.contract_id)?; 22 | 23 | // Connect to the deployed contract via the rpc 24 | let contract = SparkMarketContract::new(contract_id, wallet).await; 25 | 26 | let epoch = contract.get_epoch().await?.value; 27 | 28 | println!("\nEpoch: epoch {}, duration {}", epoch.0, epoch.1); 29 | 30 | Ok(()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /spark-cli/src/commands/info/market_order.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use fuels::types::Bits256; 4 | use spark_market_sdk::SparkMarketContract; 5 | 6 | #[derive(Args, Clone)] 7 | #[command(about = "Query the market for information about a specific open market order")] 8 | pub(crate) struct MarketOrderCommand { 9 | /// The b256 id of the order 10 | #[clap(long)] 11 | pub(crate) order_id: String, 12 | 13 | /// The contract id of the market 14 | #[clap(long)] 15 | pub(crate) contract_id: String, 16 | 17 | /// The URL to query 18 | /// Ex. testnet.fuel.network 19 | #[clap(long)] 20 | pub(crate) rpc: String, 21 | } 22 | 23 | impl MarketOrderCommand { 24 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 25 | let wallet = setup(&self.rpc).await?; 26 | let contract_id = validate_contract_id(&self.contract_id)?; 27 | let order_id = Bits256::from_hex_str(&self.order_id)?; 28 | 29 | if self.order_id.len() as u64 != 64 { 30 | anyhow::bail!("Invalid order id length"); 31 | } 32 | 33 | // Connect to the deployed contract via the rpc 34 | let contract = SparkMarketContract::new(contract_id, wallet).await; 35 | 36 | let order = contract.market_order(order_id).await?.value; 37 | 38 | match order { 39 | Some(order) => { 40 | println!("Is market order: {}", order); 41 | } 42 | None => println!("No order found for id: {}", self.order_id), 43 | } 44 | 45 | Ok(()) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /spark-cli/src/commands/info/matcher_fee.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use spark_market_sdk::SparkMarketContract; 4 | 5 | #[derive(Args, Clone)] 6 | #[command(about = "Query the matcher fee")] 7 | pub(crate) struct MatcherFeeCommand { 8 | /// The contract id of the market 9 | #[clap(long)] 10 | pub(crate) contract_id: String, 11 | 12 | /// The URL to query 13 | /// Ex. testnet.fuel.network 14 | #[clap(long)] 15 | pub(crate) rpc: String, 16 | } 17 | 18 | impl MatcherFeeCommand { 19 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 20 | let wallet = setup(&self.rpc).await?; 21 | let contract_id = validate_contract_id(&self.contract_id)?; 22 | 23 | // Connect to the deployed contract via the rpc 24 | let contract = SparkMarketContract::new(contract_id, wallet).await; 25 | 26 | let matcher_fee = contract.matcher_fee().await?.value; 27 | 28 | /*let implementation = contract.implementation_contract_id(); 29 | if *contract.contract_id() != implementation { 30 | println!("\nProxied Implememntation: 0x{}", implementation.hash); 31 | }*/ 32 | println!("\nMatcher Fee: {}", matcher_fee); 33 | 34 | Ok(()) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /spark-cli/src/commands/info/min_order_price.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use spark_market_sdk::SparkMarketContract; 4 | 5 | #[derive(Args, Clone)] 6 | #[command(about = "Query the minimum order price")] 7 | pub(crate) struct MinOrderPriceCommand { 8 | /// The contract id of the market 9 | #[clap(long)] 10 | pub(crate) contract_id: String, 11 | 12 | /// The URL to query 13 | /// Ex. testnet.fuel.network 14 | #[clap(long)] 15 | pub(crate) rpc: String, 16 | } 17 | 18 | impl MinOrderPriceCommand { 19 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 20 | let wallet = setup(&self.rpc).await?; 21 | let contract_id = validate_contract_id(&self.contract_id)?; 22 | 23 | // Connect to the deployed contract via the rpc 24 | let contract = SparkMarketContract::new(contract_id, wallet).await; 25 | 26 | let min_order_price = contract.min_order_price().await?.value; 27 | 28 | println!("\nMinimum Order Price: {}", min_order_price); 29 | 30 | Ok(()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /spark-cli/src/commands/info/min_order_size.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use spark_market_sdk::SparkMarketContract; 4 | 5 | #[derive(Args, Clone)] 6 | #[command(about = "Query the minimum order size")] 7 | pub(crate) struct MinOrderSizeCommand { 8 | /// The contract id of the market 9 | #[clap(long)] 10 | pub(crate) contract_id: String, 11 | 12 | /// The URL to query 13 | /// Ex. testnet.fuel.network 14 | #[clap(long)] 15 | pub(crate) rpc: String, 16 | } 17 | 18 | impl MinOrderSizeCommand { 19 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 20 | let wallet = setup(&self.rpc).await?; 21 | let contract_id = validate_contract_id(&self.contract_id)?; 22 | 23 | // Connect to the deployed contract via the rpc 24 | let contract = SparkMarketContract::new(contract_id, wallet).await; 25 | 26 | let min_order_size = contract.min_order_size().await?.value; 27 | 28 | println!("\nMinimum Order Size: {}", min_order_size); 29 | 30 | Ok(()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /spark-cli/src/commands/info/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod account; 2 | pub(crate) mod cli; 3 | pub(crate) mod config; 4 | pub(crate) mod epoch; 5 | pub(crate) mod market_order; 6 | pub(crate) mod matcher_fee; 7 | pub(crate) mod min_order_price; 8 | pub(crate) mod min_order_size; 9 | pub(crate) mod order; 10 | pub(crate) mod order_id; 11 | pub(crate) mod paused; 12 | pub(crate) mod protocol_fee; 13 | pub(crate) mod protocol_fee_user; 14 | pub(crate) mod protocol_fee_user_amount; 15 | pub(crate) mod proxy_owner; 16 | pub(crate) mod proxy_target; 17 | pub(crate) mod user_orders; 18 | -------------------------------------------------------------------------------- /spark-cli/src/commands/info/order.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use fuels::types::Bits256; 4 | use spark_market_sdk::SparkMarketContract; 5 | 6 | #[derive(Args, Clone)] 7 | #[command(about = "Query the market for information about a specific open order")] 8 | pub(crate) struct OrderCommand { 9 | /// The b256 id of the order 10 | #[clap(long)] 11 | pub(crate) order_id: String, 12 | 13 | /// The contract id of the market 14 | #[clap(long)] 15 | pub(crate) contract_id: String, 16 | 17 | /// The URL to query 18 | /// Ex. testnet.fuel.network 19 | #[clap(long)] 20 | pub(crate) rpc: String, 21 | } 22 | 23 | impl OrderCommand { 24 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 25 | let wallet = setup(&self.rpc).await?; 26 | let contract_id = validate_contract_id(&self.contract_id)?; 27 | let order_id = Bits256::from_hex_str(&self.order_id)?; 28 | 29 | if self.order_id.len() as u64 != 64 { 30 | anyhow::bail!("Invalid order id length"); 31 | } 32 | 33 | // Connect to the deployed contract via the rpc 34 | let contract = SparkMarketContract::new(contract_id, wallet).await; 35 | 36 | let order = contract.order(order_id).await?.value; 37 | 38 | match order { 39 | Some(order) => { 40 | println!("{:#?}", order); 41 | } 42 | None => println!("No order found for id: {}", self.order_id), 43 | } 44 | 45 | Ok(()) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /spark-cli/src/commands/info/order_id.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id, AccountType, OrderType}; 2 | use clap::Args; 3 | use fuels::types::{Address, ContractId, Identity}; 4 | use spark_market_sdk::{OrderType as ContractOrderType, /*AssetType,*/ SparkMarketContract}; 5 | use std::str::FromStr; 6 | 7 | #[derive(Args, Clone)] 8 | #[command(about = "Create a sha256 hash (order id) of the provided information")] 9 | pub(crate) struct OrderIdCommand { 10 | /// The id of the asset 11 | /*#[clap(long)] 12 | pub(crate) asset_type: String,*/ 13 | 14 | /// The type of order 15 | #[clap(long)] 16 | pub(crate) order_type: OrderType, 17 | 18 | /// The b256 id of the account 19 | #[clap(long)] 20 | pub(crate) owner: String, 21 | 22 | /// The type of account 23 | #[clap(long)] 24 | pub(crate) account_type: AccountType, 25 | 26 | /// The price of the order 27 | #[clap(long)] 28 | pub(crate) price: u64, 29 | 30 | /// The price of the order 31 | #[clap(long)] 32 | pub(crate) block_height: u32, 33 | 34 | /// The price of the order 35 | #[clap(long)] 36 | pub(crate) order_height: u64, 37 | 38 | /// The contract id of the market 39 | #[clap(long)] 40 | pub(crate) contract_id: String, 41 | 42 | /// The URL to query 43 | /// Ex. testnet.fuel.network 44 | #[clap(long)] 45 | pub(crate) rpc: String, 46 | } 47 | 48 | impl OrderIdCommand { 49 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 50 | let wallet = setup(&self.rpc).await?; 51 | let contract_id = validate_contract_id(&self.contract_id)?; 52 | 53 | let order_type = match self.order_type { 54 | OrderType::Buy => ContractOrderType::Buy, 55 | OrderType::Sell => ContractOrderType::Sell, 56 | }; 57 | 58 | // Connect to the deployed contract via the rpc 59 | let contract = SparkMarketContract::new(contract_id, wallet).await; 60 | 61 | let account = match &self.account_type { 62 | AccountType::Address => { 63 | let address = Address::from_str(&self.owner).expect("Invalid address"); 64 | Identity::Address(address) 65 | } 66 | AccountType::Contract => { 67 | let address = ContractId::from_str(&self.owner).expect("Invalid contract id"); 68 | Identity::ContractId(address) 69 | } 70 | }; 71 | 72 | let hash = contract 73 | .order_id( 74 | order_type, 75 | account, 76 | self.price, 77 | self.block_height, 78 | self.order_height, 79 | ) 80 | .await? 81 | .value; 82 | 83 | println!("\nOrder ID: {}", ContractId::from(hash.0)); 84 | 85 | Ok(()) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /spark-cli/src/commands/info/paused.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use spark_market_sdk::SparkMarketContract; 4 | 5 | #[derive(Args, Clone)] 6 | #[command(about = "Query the paused state")] 7 | pub(crate) struct PausedCommand { 8 | /// The contract id of the market 9 | #[clap(long)] 10 | pub(crate) contract_id: String, 11 | 12 | /// The URL to query 13 | /// Ex. testnet.fuel.network 14 | #[clap(long)] 15 | pub(crate) rpc: String, 16 | } 17 | 18 | impl PausedCommand { 19 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 20 | let wallet = setup(&self.rpc).await?; 21 | let contract_id = validate_contract_id(&self.contract_id)?; 22 | 23 | // Connect to the deployed contract via the rpc 24 | let contract = SparkMarketContract::new(contract_id, wallet).await; 25 | 26 | let paused = contract.is_paused().await?.value; 27 | 28 | println!("Paused: {:?}", paused); 29 | 30 | Ok(()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /spark-cli/src/commands/info/protocol_fee.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use spark_market_sdk::SparkMarketContract; 4 | 5 | #[derive(Args, Clone)] 6 | #[command(about = "Query the protocol fee")] 7 | pub(crate) struct ProtocolFeeCommand { 8 | /// The contract id of the market 9 | #[clap(long)] 10 | pub(crate) contract_id: String, 11 | 12 | /// The URL to query 13 | /// Ex. testnet.fuel.network 14 | #[clap(long)] 15 | pub(crate) rpc: String, 16 | } 17 | 18 | impl ProtocolFeeCommand { 19 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 20 | let wallet = setup(&self.rpc).await?; 21 | let contract_id = validate_contract_id(&self.contract_id)?; 22 | 23 | // Connect to the deployed contract via the rpc 24 | let contract = SparkMarketContract::new(contract_id, wallet).await; 25 | 26 | let protocol_fee = contract.protocol_fee().await?.value; 27 | 28 | println!("Protocol Fee: {:?}", protocol_fee); 29 | 30 | Ok(()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /spark-cli/src/commands/info/protocol_fee_user.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id, AccountType}; 2 | use clap::Args; 3 | use fuels::types::{Address, ContractId, Identity}; 4 | use spark_market_sdk::SparkMarketContract; 5 | use std::str::FromStr; 6 | 7 | #[derive(Args, Clone)] 8 | #[command(about = "Query the protocol fee user")] 9 | pub(crate) struct ProtocolFeeUserCommand { 10 | /// The b256 id of the account 11 | #[clap(long)] 12 | pub(crate) account_id: String, 13 | 14 | /// The type of account 15 | #[clap(long)] 16 | pub(crate) account_type: AccountType, 17 | 18 | /// The contract id of the market 19 | #[clap(long)] 20 | pub(crate) contract_id: String, 21 | 22 | /// The URL to query 23 | /// Ex. testnet.fuel.network 24 | #[clap(long)] 25 | pub(crate) rpc: String, 26 | } 27 | 28 | impl ProtocolFeeUserCommand { 29 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 30 | let wallet = setup(&self.rpc).await?; 31 | let contract_id = validate_contract_id(&self.contract_id)?; 32 | 33 | // Connect to the deployed contract via the rpc 34 | let contract = SparkMarketContract::new(contract_id, wallet).await; 35 | 36 | let account = match self.account_type { 37 | AccountType::Address => { 38 | let address = Address::from_str(&self.account_id).expect("Invalid address"); 39 | Identity::Address(address) 40 | } 41 | AccountType::Contract => { 42 | let address = ContractId::from_str(&self.account_id).expect("Invalid contract id"); 43 | Identity::ContractId(address) 44 | } 45 | }; 46 | 47 | let protocol_fee_user = contract.protocol_fee_user(account).await?.value; 48 | 49 | println!( 50 | "Protocol Fee: for {:?} (maker_fee, taker_fee) ({}, {})", 51 | account, protocol_fee_user.0, protocol_fee_user.1 52 | ); 53 | 54 | Ok(()) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /spark-cli/src/commands/info/protocol_fee_user_amount.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id, AccountType}; 2 | use clap::Args; 3 | use fuels::types::{Address, ContractId, Identity}; 4 | use spark_market_sdk::SparkMarketContract; 5 | use std::str::FromStr; 6 | 7 | #[derive(Args, Clone)] 8 | #[command(about = "Query the protocol fee user amount")] 9 | pub(crate) struct ProtocolFeeUserAmountCommand { 10 | /// The amount of asset 11 | #[clap(long)] 12 | pub(crate) amount: u64, 13 | 14 | /// The b256 id of the account 15 | #[clap(long)] 16 | pub(crate) account_id: String, 17 | 18 | /// The type of account 19 | #[clap(long)] 20 | pub(crate) account_type: AccountType, 21 | 22 | /// The contract id of the market 23 | #[clap(long)] 24 | pub(crate) contract_id: String, 25 | 26 | /// The URL to query 27 | /// Ex. testnet.fuel.network 28 | #[clap(long)] 29 | pub(crate) rpc: String, 30 | } 31 | 32 | impl ProtocolFeeUserAmountCommand { 33 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 34 | let wallet = setup(&self.rpc).await?; 35 | let contract_id = validate_contract_id(&self.contract_id)?; 36 | 37 | // Connect to the deployed contract via the rpc 38 | let contract = SparkMarketContract::new(contract_id, wallet).await; 39 | 40 | let account = match self.account_type { 41 | AccountType::Address => { 42 | let address = Address::from_str(&self.account_id).expect("Invalid address"); 43 | Identity::Address(address) 44 | } 45 | AccountType::Contract => { 46 | let address = ContractId::from_str(&self.account_id).expect("Invalid contract id"); 47 | Identity::ContractId(address) 48 | } 49 | }; 50 | 51 | let protocol_fee_user_amount = contract 52 | .protocol_fee_user_amount(self.amount, account) 53 | .await? 54 | .value; 55 | 56 | println!( 57 | "Protocol Fee Amount: for {:?} of {} (maker_fee, taker_fee) ({}, {})", 58 | account, self.amount, protocol_fee_user_amount.0, protocol_fee_user_amount.1 59 | ); 60 | 61 | Ok(()) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /spark-cli/src/commands/info/proxy_owner.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use spark_proxy_sdk::SparkProxyContract; 4 | 5 | #[derive(Args, Clone)] 6 | #[command(about = "Query the proxy owner")] 7 | pub(crate) struct ProxyOwnerCommand { 8 | /// The contract id of the market 9 | #[clap(long)] 10 | pub(crate) contract_id: String, 11 | 12 | /// The URL to query 13 | /// Ex. testnet.fuel.network 14 | #[clap(long)] 15 | pub(crate) rpc: String, 16 | } 17 | 18 | impl ProxyOwnerCommand { 19 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 20 | let wallet = setup(&self.rpc).await?; 21 | let contract_id = validate_contract_id(&self.contract_id)?; 22 | 23 | // Connect to the deployed contract via the rpc 24 | let contract = SparkProxyContract::new(contract_id, wallet).await; 25 | 26 | let proxy_owner = contract.proxy_owner().await?.value; 27 | 28 | println!("Proxy owner: {:?}", proxy_owner); 29 | 30 | Ok(()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /spark-cli/src/commands/info/proxy_target.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use spark_proxy_sdk::SparkProxyContract; 4 | 5 | #[derive(Args, Clone)] 6 | #[command(about = "Query the proxy target")] 7 | pub(crate) struct ProxyTargetCommand { 8 | /// The contract id of the market 9 | #[clap(long)] 10 | pub(crate) contract_id: String, 11 | 12 | /// The URL to query 13 | /// Ex. testnet.fuel.network 14 | #[clap(long)] 15 | pub(crate) rpc: String, 16 | } 17 | 18 | impl ProxyTargetCommand { 19 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 20 | let wallet = setup(&self.rpc).await?; 21 | let contract_id = validate_contract_id(&self.contract_id)?; 22 | 23 | // Connect to the deployed contract via the rpc 24 | let contract = SparkProxyContract::new(contract_id, wallet).await; 25 | 26 | let proxy_target = contract.proxy_target().await?.value; 27 | 28 | println!("Proxy target: {:?}", proxy_target); 29 | 30 | Ok(()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /spark-cli/src/commands/info/user_orders.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id, AccountType}; 2 | use clap::Args; 3 | use fuels::types::{Address, ContractId, Identity}; 4 | use spark_market_sdk::SparkMarketContract; 5 | use std::str::FromStr; 6 | 7 | #[derive(Args, Clone)] 8 | #[command(about = "Query the market for the currently open orders for the user")] 9 | pub(crate) struct UserOrdersCommand { 10 | /// The b256 id of the account 11 | #[clap(long)] 12 | pub(crate) account_id: String, 13 | 14 | /// The type of account 15 | #[clap(long)] 16 | pub(crate) account_type: AccountType, 17 | 18 | /// The contract id of the market 19 | #[clap(long)] 20 | pub(crate) contract_id: String, 21 | 22 | /// The URL to query 23 | /// Ex. testnet.fuel.network 24 | #[clap(long)] 25 | pub(crate) rpc: String, 26 | } 27 | 28 | impl UserOrdersCommand { 29 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 30 | let wallet = setup(&self.rpc).await?; 31 | let contract_id = validate_contract_id(&self.contract_id)?; 32 | 33 | // Connect to the deployed contract via the rpc 34 | let contract = SparkMarketContract::new(contract_id, wallet).await; 35 | 36 | let account = match self.account_type { 37 | AccountType::Address => { 38 | let address = Address::from_str(&self.account_id).expect("Invalid address"); 39 | Identity::Address(address) 40 | } 41 | AccountType::Contract => { 42 | let address = ContractId::from_str(&self.account_id).expect("Invalid contract id"); 43 | Identity::ContractId(address) 44 | } 45 | }; 46 | 47 | let orders = contract.user_orders(account).await?.value; 48 | 49 | if orders.is_empty() { 50 | anyhow::bail!("User has no open orders"); 51 | } 52 | 53 | for order in orders { 54 | println!("{:?}", Address::new(order.0)); 55 | } 56 | 57 | Ok(()) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /spark-cli/src/commands/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod batch; 2 | pub(crate) mod cli; 3 | pub(crate) mod core; 4 | pub(crate) mod info; 5 | pub(crate) mod registry; 6 | pub(crate) mod upgrade; 7 | -------------------------------------------------------------------------------- /spark-cli/src/commands/registry/cli.rs: -------------------------------------------------------------------------------- 1 | use crate::commands::registry::{ 2 | config::ConfigCommand, deploy::DeployCommand, markets::MarketsCommand, 3 | register::RegisterCommand, unregister::UnregisterCommand, 4 | }; 5 | use clap::Subcommand; 6 | 7 | #[derive(Clone, Subcommand)] 8 | pub(crate) enum RegistryCommands { 9 | /// Deploy a new market registry contract 10 | #[clap(short_flag = 'C')] 11 | Config(ConfigCommand), 12 | 13 | /// Deploy a new market registry contract 14 | #[clap(short_flag = 'D')] 15 | Deploy(DeployCommand), 16 | 17 | /// Unregister a market in the market registry contract 18 | #[clap(short_flag = 'M')] 19 | Markets(MarketsCommand), 20 | 21 | /// Register a new market in the market registry contract 22 | #[clap(short_flag = 'R')] 23 | Register(RegisterCommand), 24 | 25 | /// Unregister a market in the market registry contract 26 | #[clap(short_flag = 'U')] 27 | Unregister(UnregisterCommand), 28 | } 29 | -------------------------------------------------------------------------------- /spark-cli/src/commands/registry/config.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use spark_registry_sdk::SparkRegistryContract; 4 | 5 | #[derive(Args, Clone)] 6 | #[command(about = "Query the MarketRegistry for its configurable variables")] 7 | pub(crate) struct ConfigCommand { 8 | /// The contract id of the market 9 | #[clap(long)] 10 | pub(crate) contract_id: String, 11 | 12 | /// The URL to query 13 | /// Ex. testnet.fuel.network 14 | #[clap(long)] 15 | pub(crate) rpc: String, 16 | } 17 | 18 | impl ConfigCommand { 19 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 20 | let wallet = setup(&self.rpc).await?; 21 | let contract_id = validate_contract_id(&self.contract_id)?; 22 | 23 | // Connect to the deployed contract via the rpc 24 | let contract = SparkRegistryContract::new(contract_id, wallet).await; 25 | 26 | let (owner, version) = contract.config().await?.value; 27 | 28 | println!("\nOwner: 0x{:?}", owner); 29 | println!( 30 | "Version: {} ({})", 31 | contract.contract_str_version().await?, 32 | version 33 | ); 34 | 35 | Ok(()) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /spark-cli/src/commands/registry/deploy.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::setup; 2 | use clap::Args; 3 | use fuels::accounts::ViewOnlyAccount; 4 | use spark_registry_sdk::SparkRegistryContract; 5 | 6 | #[derive(Args, Clone)] 7 | #[command(about = "Deploys the MarketRegistry to a network")] 8 | pub(crate) struct DeployCommand { 9 | /// The URL to deploy to 10 | /// Ex. testnet.fuel.network 11 | #[clap(long)] 12 | pub(crate) rpc: String, 13 | } 14 | 15 | impl DeployCommand { 16 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 17 | let wallet = setup(&self.rpc).await?; 18 | 19 | // Initial balance prior to contract call - used to calculate contract interaction cost 20 | let balance = wallet 21 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 22 | .await?; 23 | 24 | let version = SparkRegistryContract::sdk_version(); 25 | 26 | // Deploy the contract 27 | let contract = SparkRegistryContract::deploy(wallet.clone(), version).await?; 28 | 29 | // Balance post-deployment 30 | let new_balance = wallet 31 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 32 | .await?; 33 | 34 | println!( 35 | "\n MarketRegistry version {} ({}) deployed to: 0x{}", 36 | contract.contract_str_version().await?, 37 | version, 38 | contract.id() 39 | ); 40 | println!("Deployment cost: {}", balance - new_balance); 41 | println!("Owner address: {}", wallet.address()); 42 | println!(" 0x{}", wallet.address().hash()); 43 | 44 | Ok(()) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /spark-cli/src/commands/registry/markets.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use fuels::types::AssetId; 4 | use spark_registry_sdk::SparkRegistryContract; 5 | use std::str::FromStr; 6 | 7 | #[derive(Args, Clone)] 8 | #[command(about = "Gets market contract ids")] 9 | pub(crate) struct MarketsCommand { 10 | /// The base asset id 11 | #[clap(long)] 12 | pub(crate) base: String, 13 | 14 | /// The quote asset id 15 | #[clap(long)] 16 | pub(crate) quote: String, 17 | 18 | /// The contract id of the market 19 | #[clap(long)] 20 | pub(crate) contract_id: String, 21 | 22 | /// The URL to query 23 | /// Ex. testnet.fuel.network 24 | #[clap(long)] 25 | pub(crate) rpc: String, 26 | } 27 | 28 | impl MarketsCommand { 29 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 30 | let wallet = setup(&self.rpc).await?; 31 | let contract_id = validate_contract_id(&self.contract_id)?; 32 | 33 | let mut asset_ids: Vec<(AssetId, AssetId)> = Vec::new(); 34 | asset_ids.push(( 35 | AssetId::from_str(&self.base).expect("Invalid asset"), 36 | AssetId::from_str(&self.quote).expect("Invalid asset"), 37 | )); 38 | 39 | // Connect to the deployed contract via the rpc 40 | let contract = SparkRegistryContract::new(contract_id, wallet).await; 41 | 42 | let markets = contract.markets(asset_ids).await?.value; 43 | 44 | println!("\nMarkets: {:?}", markets); 45 | 46 | Ok(()) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /spark-cli/src/commands/registry/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod cli; 2 | pub(crate) mod config; 3 | pub(crate) mod deploy; 4 | pub(crate) mod markets; 5 | pub(crate) mod register; 6 | pub(crate) mod unregister; 7 | -------------------------------------------------------------------------------- /spark-cli/src/commands/registry/register.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use fuels::accounts::ViewOnlyAccount; 4 | use spark_registry_sdk::SparkRegistryContract; 5 | 6 | #[derive(Args, Clone)] 7 | #[command(about = "Registers a market in the market registry")] 8 | pub(crate) struct RegisterCommand { 9 | /// The contract id of the market 10 | #[clap(long)] 11 | pub(crate) market: String, 12 | 13 | /// The contract id of the market registry 14 | #[clap(long)] 15 | pub(crate) contract_id: String, 16 | 17 | /// The URL to query 18 | /// Ex. testnet.fuel.network 19 | #[clap(long)] 20 | pub(crate) rpc: String, 21 | } 22 | 23 | impl RegisterCommand { 24 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 25 | let wallet = setup(&self.rpc).await?; 26 | let contract_id = validate_contract_id(&self.contract_id)?; 27 | let market = validate_contract_id(&self.market)?; 28 | 29 | // Initial balance prior to contract call - used to calculate contract interaction cost 30 | let balance = wallet 31 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 32 | .await?; 33 | 34 | // Connect to the deployed contract via the rpc 35 | let contract = SparkRegistryContract::new(contract_id, wallet.clone()).await; 36 | 37 | let _ = contract.register_market(market).await?; 38 | 39 | // Balance post-call 40 | let new_balance = wallet 41 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 42 | .await?; 43 | 44 | println!("\nContract call cost: {}", balance - new_balance); 45 | 46 | Ok(()) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /spark-cli/src/commands/registry/unregister.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use fuels::accounts::ViewOnlyAccount; 4 | use spark_registry_sdk::SparkRegistryContract; 5 | 6 | #[derive(Args, Clone)] 7 | #[command(about = "Unregisters a market in the market registry")] 8 | pub(crate) struct UnregisterCommand { 9 | /// The contract id of the Market 10 | #[clap(long)] 11 | pub(crate) market: String, 12 | 13 | /// The contract id of the MarketRegistry 14 | #[clap(long)] 15 | pub(crate) contract_id: String, 16 | 17 | /// The URL to query 18 | /// Ex. testnet.fuel.network 19 | #[clap(long)] 20 | pub(crate) rpc: String, 21 | } 22 | 23 | impl UnregisterCommand { 24 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 25 | let wallet = setup(&self.rpc).await?; 26 | let contract_id = validate_contract_id(&self.contract_id)?; 27 | let market = validate_contract_id(&self.market)?; 28 | 29 | // Initial balance prior to contract call - used to calculate contract interaction cost 30 | let balance = wallet 31 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 32 | .await?; 33 | 34 | // Connect to the deployed contract via the rpc 35 | let contract = SparkRegistryContract::new(contract_id, wallet.clone()).await; 36 | 37 | let _ = contract.unregister_market(market).await?; 38 | 39 | // Balance post-call 40 | let new_balance = wallet 41 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 42 | .await?; 43 | 44 | println!("\nContract call cost: {}", balance - new_balance); 45 | 46 | Ok(()) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /spark-cli/src/commands/upgrade/cli.rs: -------------------------------------------------------------------------------- 1 | use crate::commands::upgrade::{ 2 | upgrade_eth_usdc_proxy::UpgradeEthUsdcProxyCommand, 3 | upgrade_ezeth_usdc_proxy::UpgradeEzethUsdcProxyCommand, 4 | upgrade_fuel_eth_proxy::UpgradeFuelEthProxyCommand, 5 | upgrade_fuel_usdc_proxy::UpgradeFuelUsdcProxyCommand, 6 | upgrade_psycho_usdc_proxy::UpgradePsychoUsdcProxyCommand, 7 | upgrade_pzeth_usdc_proxy::UpgradePzethUsdcProxyCommand, 8 | upgrade_teth_tusdc_proxy::UpgradeTethTusdcProxyCommand, 9 | upgrade_usdc_usdt_proxy::UpgradeUsdcUsdtProxyCommand, 10 | upgrade_usdf_usdc_proxy::UpgradeUsdfUsdcProxyCommand, 11 | upgrade_usdt_eth_proxy::UpgradeUsdtEthProxyCommand, 12 | upgrade_usdt_usdc_proxy::UpgradeUsdtUsdcProxyCommand, 13 | upgrade_weth_usdc_proxy::UpgradeWethUsdcProxyCommand, 14 | }; 15 | 16 | use clap::Subcommand; 17 | 18 | #[derive(Clone, Subcommand)] 19 | pub(crate) enum UpgradeCommands { 20 | /// Upgrade a eth/usdc market proxy 21 | #[clap(short_flag = 'E')] 22 | UpgradeEthUsdcProxy(UpgradeEthUsdcProxyCommand), 23 | /// Upgrade a ezeth/usdc market proxy 24 | #[clap(short_flag = 'Z')] 25 | UpgradeEzethUsdcProxy(UpgradeEzethUsdcProxyCommand), 26 | /// Upgrade a fuel/eth market proxy 27 | #[clap(short_flag = 'D')] 28 | UpgradeFuelEthProxy(UpgradeFuelEthProxyCommand), 29 | /// Upgrade a fuel/usdc market proxy 30 | #[clap(short_flag = 'F')] 31 | UpgradeFuelUsdcProxy(UpgradeFuelUsdcProxyCommand), 32 | /// Upgrade a psycho/usdc market proxy 33 | #[clap(short_flag = 'S')] 34 | UpgradePsychoUsdcProxy(UpgradePsychoUsdcProxyCommand), 35 | /// Upgrade a pzeth/usdc market proxy 36 | #[clap(short_flag = 'P')] 37 | UpgradePzethUsdcProxy(UpgradePzethUsdcProxyCommand), 38 | /// Upgrade a teth/tusdc market proxy 39 | #[clap(short_flag = 'T')] 40 | UpgradeTethTusdcProxy(UpgradeTethTusdcProxyCommand), 41 | /// Upgrade a usdc/usdt market proxy 42 | #[clap(short_flag = 'U')] 43 | UpgradeUsdcUsdtProxy(UpgradeUsdcUsdtProxyCommand), 44 | /// Upgrade a usdf/usdc market proxy 45 | #[clap(short_flag = 'H')] 46 | UpgradeUsdfUsdcProxy(UpgradeUsdfUsdcProxyCommand), 47 | /// Upgrade a usdt/eth market proxy 48 | #[clap(short_flag = 'I')] 49 | UpgradeUsdtEthProxy(UpgradeUsdtEthProxyCommand), 50 | /// Upgrade a usdt/usdc market proxy 51 | #[clap(short_flag = 'V')] 52 | UpgradeUsdtUsdcProxy(UpgradeUsdtUsdcProxyCommand), 53 | /// Upgrade a usdt/usdc market proxy 54 | #[clap(short_flag = 'W')] 55 | UpgradeWethUsdcProxy(UpgradeWethUsdcProxyCommand), 56 | } 57 | -------------------------------------------------------------------------------- /spark-cli/src/commands/upgrade/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod cli; 2 | 3 | pub(crate) mod upgrade_eth_usdc_proxy; 4 | pub(crate) mod upgrade_ezeth_usdc_proxy; 5 | pub(crate) mod upgrade_fuel_eth_proxy; 6 | pub(crate) mod upgrade_fuel_usdc_proxy; 7 | pub(crate) mod upgrade_psycho_usdc_proxy; 8 | pub(crate) mod upgrade_pzeth_usdc_proxy; 9 | pub(crate) mod upgrade_teth_tusdc_proxy; 10 | pub(crate) mod upgrade_usdc_usdt_proxy; 11 | pub(crate) mod upgrade_usdf_usdc_proxy; 12 | pub(crate) mod upgrade_usdt_eth_proxy; 13 | pub(crate) mod upgrade_usdt_usdc_proxy; 14 | pub(crate) mod upgrade_weth_usdc_proxy; 15 | -------------------------------------------------------------------------------- /spark-cli/src/commands/upgrade/upgrade_eth_usdc_proxy.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use fuels::accounts::ViewOnlyAccount; 4 | use spark_market_sdk::SparkMarketContract; 5 | use spark_proxy_sdk::SparkProxyContract; 6 | 7 | #[derive(Args, Clone)] 8 | #[command(about = "Upgrades the eth/usdc market proxy to a network")] 9 | pub(crate) struct UpgradeEthUsdcProxyCommand {} 10 | 11 | impl UpgradeEthUsdcProxyCommand { 12 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 13 | let wallet = setup("mainnet.fuel.network").await?; 14 | 15 | // Initial balance prior to contract call - used to calculate contract interaction cost 16 | let balance = wallet 17 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 18 | .await?; 19 | 20 | let contract_id_str = "0xfe2c524ad8e088f33d232a45dbea43e792861640b71aa1814b30506bf8430ee5"; 21 | let contract_id = validate_contract_id(contract_id_str)?; 22 | 23 | // Connect to the deployed contract via the rpc 24 | let proxy = SparkProxyContract::new(contract_id, wallet.clone()).await; 25 | let proxy_target = proxy.proxy_target().await?.value; 26 | 27 | let proxy = SparkMarketContract::new(contract_id, wallet.clone()).await; 28 | 29 | let ( 30 | base_asset, 31 | base_asset_decimals, 32 | quote_asset, 33 | quote_asset_decimals, 34 | owner, 35 | price_decimals, 36 | version, 37 | ) = proxy.config().await?.value; 38 | 39 | println!("\nProxy target: {:?}", proxy_target); 40 | println!("Base Asset: 0x{}", base_asset); 41 | println!("Base Asset Decimals: {}", base_asset_decimals); 42 | println!("Quote Asset: 0x{}", quote_asset); 43 | println!("Quote Asset Decimals: {}", quote_asset_decimals); 44 | println!("Owner: {:?}", owner); 45 | println!("Price Decimals: {}", price_decimals); 46 | println!( 47 | "Version: {}.{}.{}", 48 | (version & 0xFF0000) >> 16, 49 | (version & 0xFF00) >> 8, 50 | version & 0xFF 51 | ); 52 | 53 | let version = SparkMarketContract::sdk_version(); 54 | 55 | // Deploy the contract 56 | let contract = SparkMarketContract::deploy( 57 | base_asset, 58 | base_asset_decimals, 59 | quote_asset, 60 | quote_asset_decimals, 61 | wallet.clone(), 62 | price_decimals, 63 | version, 64 | ) 65 | .await?; 66 | 67 | let _ = contract.pause().await?; 68 | 69 | println!("\nNew target deployed: {:?}", contract.contract_id()); 70 | 71 | let proxy = SparkProxyContract::new(contract_id, wallet.clone()).await; 72 | let _ = proxy 73 | .set_proxy_target(contract.contract_id().into()) 74 | .await?; 75 | let proxy_target = proxy.proxy_target().await?.value; 76 | 77 | let proxy = SparkMarketContract::new(contract_id, wallet.clone()).await; 78 | 79 | let ( 80 | base_asset, 81 | base_asset_decimals, 82 | quote_asset, 83 | quote_asset_decimals, 84 | owner, 85 | price_decimals, 86 | version, 87 | ) = proxy.config().await?.value; 88 | 89 | println!("\nNew proxy target: {:?}", proxy_target); 90 | println!("Base Asset: 0x{}", base_asset); 91 | println!("Base Asset Decimals: {}", base_asset_decimals); 92 | println!("Quote Asset: 0x{}", quote_asset); 93 | println!("Quote Asset Decimals: {}", quote_asset_decimals); 94 | println!("Owner: {:?}", owner); 95 | println!("Price Decimals: {}", price_decimals); 96 | println!( 97 | "Version: {}.{}.{}", 98 | (version & 0xFF0000) >> 16, 99 | (version & 0xFF00) >> 8, 100 | version & 0xFF 101 | ); 102 | 103 | println!( 104 | "\nMarket {} upgraded to version {} ({}) with target {:?}", 105 | contract_id_str, 106 | contract.contract_str_version().await?, 107 | version, 108 | proxy_target, 109 | ); 110 | 111 | // Balance post-deployment 112 | let new_balance = wallet 113 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 114 | .await?; 115 | 116 | println!("\nDeployment cost: {}", balance - new_balance); 117 | println!("Owner address: {}", wallet.address()); 118 | println!(" 0x{}", wallet.address().hash()); 119 | 120 | Ok(()) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /spark-cli/src/commands/upgrade/upgrade_fuel_eth_proxy.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{setup, validate_contract_id}; 2 | use clap::Args; 3 | use fuels::accounts::ViewOnlyAccount; 4 | use spark_market_sdk::SparkMarketContract; 5 | use spark_proxy_sdk::SparkProxyContract; 6 | 7 | #[derive(Args, Clone)] 8 | #[command(about = "Upgrades the fuel/eth market proxy to a network")] 9 | pub(crate) struct UpgradeFuelEthProxyCommand {} 10 | 11 | impl UpgradeFuelEthProxyCommand { 12 | pub(crate) async fn run(&self) -> anyhow::Result<()> { 13 | let wallet = setup("mainnet.fuel.network").await?; 14 | 15 | // Initial balance prior to contract call - used to calculate contract interaction cost 16 | let balance = wallet 17 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 18 | .await?; 19 | 20 | let contract_id_str = "0x4391b39d9165917faffb9dcc69d19b6952a6ebf02db593747cf2f5d8298d28c7"; 21 | let contract_id = validate_contract_id(contract_id_str)?; 22 | 23 | // Connect to the deployed contract via the rpc 24 | let proxy = SparkProxyContract::new(contract_id, wallet.clone()).await; 25 | let proxy_target = proxy.proxy_target().await?.value; 26 | 27 | let proxy = SparkMarketContract::new(contract_id, wallet.clone()).await; 28 | 29 | let ( 30 | base_asset, 31 | base_asset_decimals, 32 | quote_asset, 33 | quote_asset_decimals, 34 | owner, 35 | price_decimals, 36 | version, 37 | ) = proxy.config().await?.value; 38 | 39 | println!("\nProxy target: {:?}", proxy_target); 40 | println!("Base Asset: 0x{}", base_asset); 41 | println!("Base Asset Decimals: {}", base_asset_decimals); 42 | println!("Quote Asset: 0x{}", quote_asset); 43 | println!("Quote Asset Decimals: {}", quote_asset_decimals); 44 | println!("Owner: {:?}", owner); 45 | println!("Price Decimals: {}", price_decimals); 46 | println!( 47 | "Version: {}.{}.{}", 48 | (version & 0xFF0000) >> 16, 49 | (version & 0xFF00) >> 8, 50 | version & 0xFF 51 | ); 52 | 53 | let version = SparkMarketContract::sdk_version(); 54 | 55 | // Deploy the contract 56 | let contract = SparkMarketContract::deploy( 57 | base_asset, 58 | base_asset_decimals, 59 | quote_asset, 60 | quote_asset_decimals, 61 | wallet.clone(), 62 | price_decimals, 63 | version, 64 | ) 65 | .await?; 66 | 67 | let _ = contract.pause().await?; 68 | 69 | println!("\nNew target deployed: {:?}", contract.contract_id()); 70 | 71 | let proxy = SparkProxyContract::new(contract_id, wallet.clone()).await; 72 | let _ = proxy 73 | .set_proxy_target(contract.contract_id().into()) 74 | .await?; 75 | let proxy_target = proxy.proxy_target().await?.value; 76 | 77 | let proxy = SparkMarketContract::new(contract_id, wallet.clone()).await; 78 | 79 | let ( 80 | base_asset, 81 | base_asset_decimals, 82 | quote_asset, 83 | quote_asset_decimals, 84 | owner, 85 | price_decimals, 86 | version, 87 | ) = proxy.config().await?.value; 88 | 89 | println!("\nNew proxy target: {:?}", proxy_target); 90 | println!("Base Asset: 0x{}", base_asset); 91 | println!("Base Asset Decimals: {}", base_asset_decimals); 92 | println!("Quote Asset: 0x{}", quote_asset); 93 | println!("Quote Asset Decimals: {}", quote_asset_decimals); 94 | println!("Owner: {:?}", owner); 95 | println!("Price Decimals: {}", price_decimals); 96 | println!( 97 | "Version: {}.{}.{}", 98 | (version & 0xFF0000) >> 16, 99 | (version & 0xFF00) >> 8, 100 | version & 0xFF 101 | ); 102 | 103 | println!( 104 | "\nMarket {} upgraded to version {} ({}) with target {:?}", 105 | contract_id_str, 106 | contract.contract_str_version().await?, 107 | version, 108 | proxy_target, 109 | ); 110 | 111 | // Balance post-deployment 112 | let new_balance = wallet 113 | .get_asset_balance(&wallet.provider().unwrap().base_asset_id()) 114 | .await?; 115 | 116 | println!("\nDeployment cost: {}", balance - new_balance); 117 | println!("Owner address: {}", wallet.address()); 118 | println!(" 0x{}", wallet.address().hash()); 119 | 120 | Ok(()) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /spark-cli/src/utils.rs: -------------------------------------------------------------------------------- 1 | use clap::ValueEnum; 2 | use fuels::prelude::{ContractId, Provider, WalletUnlocked}; 3 | use std::str::FromStr; 4 | 5 | pub(crate) async fn setup(rpc: &str) -> anyhow::Result { 6 | let provider = Provider::connect(rpc).await?; 7 | 8 | // First, try to get the private key from environment 9 | if let Ok(secret) = std::env::var("WALLET_SECRET") { 10 | let wallet = WalletUnlocked::new_from_private_key(secret.parse()?, Some(provider)); 11 | return Ok(wallet); 12 | } 13 | 14 | // If no private key is provided, try to get the mnemonic phrase from environment 15 | if let Ok(mnemonic) = std::env::var("MNEMONIC") { 16 | let wallet = WalletUnlocked::new_from_mnemonic_phrase(&mnemonic, Some(provider.clone()))?; 17 | return Ok(wallet); 18 | } 19 | 20 | // If neither WALLET_SECRET nor MNEMONIC are provided, return an error 21 | Err(anyhow::anyhow!( 22 | "No valid private key or mnemonic found in environment" 23 | )) 24 | } 25 | 26 | pub(crate) fn validate_contract_id(contract_id: &str) -> anyhow::Result { 27 | if contract_id.len() as u64 != 66 { 28 | anyhow::bail!("Invalid contract id length"); 29 | } 30 | 31 | Ok(ContractId::from_str(contract_id).expect("Invalid contract id")) 32 | } 33 | 34 | #[derive(Clone, ValueEnum)] 35 | pub(crate) enum AccountType { 36 | /// Externally Owned Account 37 | Address, 38 | /// Contract 39 | Contract, 40 | } 41 | 42 | #[derive(Clone, ValueEnum)] 43 | pub(crate) enum AssetType { 44 | /// Base asset 45 | Base, 46 | /// Quote asset 47 | Quote, 48 | } 49 | 50 | #[derive(Clone, ValueEnum)] 51 | pub(crate) enum LimitType { 52 | /// Immediatelly or Cancel 53 | IOC, 54 | /// Fill or Kill 55 | FOK, 56 | } 57 | 58 | #[derive(Clone, ValueEnum)] 59 | pub(crate) enum OrderType { 60 | /// Buy order 61 | Buy, 62 | /// Sell order 63 | Sell, 64 | } 65 | -------------------------------------------------------------------------------- /spark-market-sdk/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "spark-market-sdk" 3 | version = { workspace = true } 4 | authors = { workspace = true } 5 | edition = { workspace = true } 6 | license = { workspace = true } 7 | rust-version = { workspace = true } 8 | description = "SDK for interacting with the Spark Market" 9 | repository = "https://github.com/compolabs/orderbook-contract" 10 | readme = "README.md" 11 | keywords = ["fuel", "sdk", "spark", "market", "orderbook"] 12 | include = [ 13 | "spark-market/out/release/*", 14 | "spark-proxy/out/release/*", 15 | "src/lib.rs", 16 | "Cargo.toml", 17 | "README.md" 18 | ] 19 | 20 | [dependencies] 21 | anyhow = { workspace = true } 22 | fuels = { workspace = true } 23 | rand = "0.8.5" 24 | tokio = { workspace = true } 25 | -------------------------------------------------------------------------------- /spark-market-sdk/spark-market/out/release/spark-market-storage_slots.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "key": "08e09a22868bf842d72aa81dd2cc2d707c26c2498b853428e18bbf9133161d25", 4 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 5 | }, 6 | { 7 | "key": "0dbee7545bd5cbe89afb5ff6e66c02ee06d2105f0878cfb45b8a489221b9ea9d", 8 | "value": "00000000002820a8000000000000000000000000000000000000000000000000" 9 | }, 10 | { 11 | "key": "29f5dd154c31547f4b4e4ad8565741c416a935be9adc4c46d68a12b779a0e26a", 12 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 13 | }, 14 | { 15 | "key": "3493c96d5901ca6a92009e6e8732dee2d274b75731fc3355e9888ea296e15c0c", 16 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 17 | }, 18 | { 19 | "key": "b73c4b1683695c9091c69cd3ac58dcfa2df06e8f672cd57b72fb776c6e125242", 20 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 21 | }, 22 | { 23 | "key": "df16e0eaa1b1c1954bc0242c41707cd9dcbcd03b9162d45fd56db2d038049cf3", 24 | "value": "0100000000000000000000000000000000000000000000000000000000000000" 25 | } 26 | ] -------------------------------------------------------------------------------- /spark-market-sdk/spark-market/out/release/spark-market.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compolabs/orderbook-contract/bfbc7d99d42f6630b98cd7794641f64d2ac970f2/spark-market-sdk/spark-market/out/release/spark-market.bin -------------------------------------------------------------------------------- /spark-market-sdk/spark-proxy/out/release/spark-proxy-storage_slots.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "key": "7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd55", 4 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 5 | }, 6 | { 7 | "key": "7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd56", 8 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 9 | }, 10 | { 11 | "key": "bb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea754", 12 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 13 | }, 14 | { 15 | "key": "bb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea755", 16 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 17 | } 18 | ] -------------------------------------------------------------------------------- /spark-market-sdk/spark-proxy/out/release/spark-proxy.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compolabs/orderbook-contract/bfbc7d99d42f6630b98cd7794641f64d2ac970f2/spark-market-sdk/spark-proxy/out/release/spark-proxy.bin -------------------------------------------------------------------------------- /spark-market/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "spark-market" 3 | version = { workspace = true } 4 | authors = { workspace = true } 5 | edition = { workspace = true } 6 | license = { workspace = true } 7 | rust-version = { workspace = true } 8 | 9 | [dependencies] 10 | anyhow = { workspace = true } 11 | fuels = { workspace = true } 12 | rand = "0.8.5" 13 | spark-market-sdk = { path = "../spark-market-sdk" } 14 | spark-proxy-sdk = { path = "../spark-proxy-sdk" } 15 | tokio = { workspace = true } 16 | 17 | [[test]] 18 | harness = true 19 | name = "integration_tests" 20 | path = "tests/harness.rs" 21 | -------------------------------------------------------------------------------- /spark-market/Forc.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | authors = ["ComposabilityLabs"] 3 | entry = "main.sw" 4 | license = "Apache-2.0" 5 | name = "spark-market" 6 | 7 | [dependencies] 8 | standards = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.6.1" } 9 | sway_libs = { git = "https://github.com/FuelLabs/sway-libs", tag = "v0.24.0" } 10 | -------------------------------------------------------------------------------- /spark-market/out/release/spark-market-storage_slots.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "key": "08e09a22868bf842d72aa81dd2cc2d707c26c2498b853428e18bbf9133161d25", 4 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 5 | }, 6 | { 7 | "key": "0dbee7545bd5cbe89afb5ff6e66c02ee06d2105f0878cfb45b8a489221b9ea9d", 8 | "value": "00000000002820a8000000000000000000000000000000000000000000000000" 9 | }, 10 | { 11 | "key": "29f5dd154c31547f4b4e4ad8565741c416a935be9adc4c46d68a12b779a0e26a", 12 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 13 | }, 14 | { 15 | "key": "3493c96d5901ca6a92009e6e8732dee2d274b75731fc3355e9888ea296e15c0c", 16 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 17 | }, 18 | { 19 | "key": "b73c4b1683695c9091c69cd3ac58dcfa2df06e8f672cd57b72fb776c6e125242", 20 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 21 | }, 22 | { 23 | "key": "df16e0eaa1b1c1954bc0242c41707cd9dcbcd03b9162d45fd56db2d038049cf3", 24 | "value": "0100000000000000000000000000000000000000000000000000000000000000" 25 | } 26 | ] -------------------------------------------------------------------------------- /spark-market/out/release/spark-market.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compolabs/orderbook-contract/bfbc7d99d42f6630b98cd7794641f64d2ac970f2/spark-market/out/release/spark-market.bin -------------------------------------------------------------------------------- /spark-market/src/data_structures.sw: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | pub mod asset_type; 4 | pub mod balance; 5 | pub mod account; 6 | pub mod limit_type; 7 | pub mod match_result; 8 | pub mod order_type; 9 | pub mod order; 10 | pub mod order_change; 11 | pub mod protocol_fee; 12 | pub mod user_volume; 13 | -------------------------------------------------------------------------------- /spark-market/src/data_structures/account.sw: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | use ::errors::OrderError; 4 | use ::data_structures::{asset_type::AssetType, balance::Balance,}; 5 | 6 | pub struct Account { 7 | // Available funds 8 | pub liquid: Balance, 9 | // Open orders 10 | pub locked: Balance, 11 | } 12 | 13 | impl Account { 14 | pub fn new() -> Self { 15 | Self { 16 | liquid: Balance::new(), 17 | locked: Balance::new(), 18 | } 19 | } 20 | 21 | pub fn lock_amount(ref mut self, amount: u64, asset: AssetType) { 22 | require(amount != 0, OrderError::ZeroLockAmount); 23 | self.liquid.debit(amount, asset); 24 | self.locked.credit(amount, asset); 25 | } 26 | 27 | pub fn unlock_amount(ref mut self, amount: u64, asset: AssetType) { 28 | require(amount != 0, OrderError::ZeroUnlockAmount); 29 | self.liquid.credit(amount, asset); 30 | self.locked.debit(amount, asset); 31 | } 32 | 33 | pub fn transfer_locked_amount( 34 | ref mut self, 35 | ref mut to: Account, 36 | amount: u64, 37 | asset: AssetType, 38 | ) { 39 | require(amount != 0, OrderError::ZeroTransferAmount); 40 | to.liquid.credit(amount, asset); 41 | self.locked.debit(amount, asset); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /spark-market/src/data_structures/asset_type.sw: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | use std::hash::{Hash, Hasher}; 4 | 5 | pub enum AssetType { 6 | Base: (), 7 | Quote: (), 8 | } 9 | 10 | impl core::ops::Eq for AssetType { 11 | fn eq(self, other: Self) -> bool { 12 | match (self, other) { 13 | (Self::Base, Self::Base) => true, 14 | (Self::Quote, Self::Quote) => true, 15 | _ => false, 16 | } 17 | } 18 | } 19 | 20 | impl core::ops::Not for AssetType { 21 | fn not(self) -> Self { 22 | match self { 23 | Self::Base => Self::Quote, 24 | Self::Quote => Self::Base, 25 | } 26 | } 27 | } 28 | 29 | impl Hash for AssetType { 30 | fn hash(self, ref mut state: Hasher) { 31 | match self { 32 | Self::Base => { 33 | 0_u8.hash(state); 34 | } 35 | Self::Quote => { 36 | 1_u8.hash(state); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /spark-market/src/data_structures/balance.sw: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | use ::data_structures::asset_type::AssetType; 4 | use ::errors::AccountError; 5 | 6 | pub struct Balance { 7 | pub base: u64, 8 | pub quote: u64, 9 | } 10 | 11 | impl Balance { 12 | pub fn new() -> Self { 13 | Self { 14 | base: 0, 15 | quote: 0, 16 | } 17 | } 18 | 19 | pub fn credit(ref mut self, amount: u64, asset: AssetType) { 20 | match asset { 21 | AssetType::Base => self.base += amount, 22 | AssetType::Quote => self.quote += amount, 23 | }; 24 | } 25 | 26 | pub fn debit(ref mut self, amount: u64, asset: AssetType) { 27 | match asset { 28 | AssetType::Base => { 29 | require( 30 | amount <= self.base, 31 | AccountError::InsufficientBalance((self.base, amount, true)), 32 | ); 33 | self.base -= amount; 34 | }, 35 | AssetType::Quote => { 36 | require( 37 | amount <= self.quote, 38 | AccountError::InsufficientBalance((self.quote, amount, false)), 39 | ); 40 | self.quote -= amount; 41 | }, 42 | }; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /spark-market/src/data_structures/limit_type.sw: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | use std::hash::{Hash, Hasher}; 4 | 5 | pub enum LimitType { 6 | GTC: (), 7 | IOC: (), 8 | FOK: (), 9 | MKT: (), 10 | } 11 | 12 | impl core::ops::Eq for LimitType { 13 | fn eq(self, other: Self) -> bool { 14 | match (self, other) { 15 | (Self::GTC, Self::GTC) => true, 16 | (Self::IOC, Self::IOC) => true, 17 | (Self::FOK, Self::FOK) => true, 18 | (Self::MKT, Self::MKT) => true, 19 | _ => false, 20 | } 21 | } 22 | } 23 | 24 | impl Hash for LimitType { 25 | fn hash(self, ref mut state: Hasher) { 26 | match self { 27 | Self::GTC => { 28 | 0_u8.hash(state); 29 | } 30 | Self::IOC => { 31 | 1_u8.hash(state); 32 | } 33 | Self::FOK => { 34 | 2_u8.hash(state); 35 | } 36 | Self::MKT => { 37 | 3_u8.hash(state); 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /spark-market/src/data_structures/match_result.sw: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | pub enum MatchResult { 4 | ZeroMatch: (), 5 | PartialMatch: (), 6 | FullMatch: (), 7 | } 8 | 9 | impl core::ops::Eq for MatchResult { 10 | fn eq(self, other: Self) -> bool { 11 | match (self, other) { 12 | (Self::ZeroMatch, Self::ZeroMatch) => true, 13 | (Self::PartialMatch, Self::PartialMatch) => true, 14 | (Self::FullMatch, Self::FullMatch) => true, 15 | _ => false, 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /spark-market/src/data_structures/order.sw: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | use ::data_structures::asset_type::AssetType; 4 | use ::data_structures::order_type::OrderType; 5 | use ::errors::{AssetError, OrderError}; 6 | use ::math::{HUNDRED_PERCENT, max, min}; 7 | 8 | use std::hash::{Hash, sha256}; 9 | use std::contract_id::ContractId; 10 | 11 | pub struct Order { 12 | pub amount: u64, 13 | pub asset_type: AssetType, 14 | pub order_type: OrderType, 15 | pub owner: Identity, 16 | pub price: u64, 17 | pub block_height: u32, 18 | pub order_height: u64, 19 | pub matcher_fee: u64, 20 | pub protocol_maker_fee: u64, 21 | pub protocol_taker_fee: u64, 22 | } 23 | 24 | impl Order { 25 | pub fn new( 26 | amount: u64, 27 | asset_type: AssetType, 28 | order_type: OrderType, 29 | owner: Identity, 30 | price: u64, 31 | block_height: u32, 32 | order_height: u64, 33 | matcher_fee: u64, 34 | protocol_maker_fee: u64, 35 | protocol_taker_fee: u64, 36 | min_order_price: u64, 37 | ) -> Self { 38 | require(asset_type == AssetType::Base, AssetError::InvalidAsset); 39 | require( 40 | price >= min_order_price, 41 | OrderError::PriceTooSmall((price, min_order_price)), 42 | ); 43 | 44 | Self { 45 | amount, 46 | asset_type, 47 | order_type, 48 | owner, 49 | price, 50 | block_height, 51 | order_height, 52 | matcher_fee, 53 | protocol_maker_fee, 54 | protocol_taker_fee, 55 | } 56 | } 57 | 58 | pub fn id(self) -> b256 { 59 | sha256(( 60 | sha256((ContractId::this(), self.owner)), 61 | self.asset_type, 62 | self.order_type, 63 | self.price, 64 | self.order_height, 65 | )) 66 | } 67 | 68 | pub fn min_protocol_fee_of_amount(self, amount: u64) -> u64 { 69 | amount * min(self.protocol_maker_fee, self.protocol_taker_fee) / HUNDRED_PERCENT 70 | } 71 | 72 | pub fn max_protocol_fee_of_amount(self, amount: u64) -> u64 { 73 | amount * max(self.protocol_maker_fee, self.protocol_taker_fee) / HUNDRED_PERCENT 74 | } 75 | 76 | pub fn protocol_maker_fee_of_amount(self, amount: u64) -> u64 { 77 | amount * self.protocol_maker_fee / HUNDRED_PERCENT 78 | } 79 | 80 | pub fn protocol_taker_fee_of_amount(self, amount: u64) -> u64 { 81 | amount * self.protocol_taker_fee / HUNDRED_PERCENT 82 | } 83 | 84 | pub fn protocol_fee_of_amount(self, counterparty: Self, amount: u64) -> u64 { 85 | if self.block_height < counterparty.block_height { 86 | self.protocol_maker_fee_of_amount(amount) 87 | } else { 88 | self.protocol_taker_fee_of_amount(amount) 89 | } 90 | } 91 | 92 | pub fn matcher_fee_of_amount(self, amount: u64) -> u64 { 93 | self.matcher_fee * amount / self.amount 94 | } 95 | 96 | pub fn is_maker(self, counterparty: Self) -> bool { 97 | self.block_height < counterparty.block_height 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /spark-market/src/data_structures/order_change.sw: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | pub enum OrderChangeType { 4 | OrderOpened: (), 5 | OrderCancelled: (), 6 | OrderMatched: (), 7 | } 8 | 9 | pub struct OrderChangeInfo { 10 | change_type: OrderChangeType, 11 | block_height: u32, 12 | sender: Identity, 13 | tx_id: b256, 14 | amount_before: u64, 15 | amount_after: u64, 16 | } 17 | 18 | impl OrderChangeInfo { 19 | pub fn new( 20 | change_type: OrderChangeType, 21 | block_height: u32, 22 | sender: Identity, 23 | tx_id: b256, 24 | amount_before: u64, 25 | amount_after: u64, 26 | ) -> Self { 27 | Self { 28 | change_type, 29 | block_height, 30 | sender, 31 | tx_id, 32 | amount_before, 33 | amount_after, 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /spark-market/src/data_structures/order_type.sw: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | use std::hash::{Hash, Hasher}; 4 | 5 | pub enum OrderType { 6 | Buy: (), 7 | Sell: (), 8 | } 9 | 10 | impl core::ops::Eq for OrderType { 11 | fn eq(self, other: Self) -> bool { 12 | match (self, other) { 13 | (Self::Buy, Self::Buy) => true, 14 | (Self::Sell, Self::Sell) => true, 15 | _ => false, 16 | } 17 | } 18 | } 19 | 20 | impl Hash for OrderType { 21 | fn hash(self, ref mut state: Hasher) { 22 | match self { 23 | Self::Buy => { 24 | 0_u8.hash(state); 25 | } 26 | Self::Sell => { 27 | 1_u8.hash(state); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /spark-market/src/data_structures/protocol_fee.sw: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | use std::storage::storage_vec::*; 4 | 5 | pub struct ProtocolFee { 6 | pub maker_fee: u64, 7 | pub taker_fee: u64, 8 | pub volume_threshold: u64, 9 | } 10 | 11 | impl ProtocolFee { 12 | fn new() -> Self { 13 | Self { 14 | maker_fee: 0, 15 | taker_fee: 0, 16 | volume_threshold: 0, 17 | } 18 | } 19 | } 20 | 21 | impl Vec { 22 | pub fn is_volume_threshold_valid(self) -> bool { 23 | let mut iter = self.iter(); 24 | let mut item = iter.next(); 25 | let mut prev = 0u64; 26 | let mut valid = true; 27 | 28 | while item.is_some() { 29 | let volume_threshold = item.unwrap().volume_threshold; 30 | if prev < volume_threshold 31 | || (prev == 0 32 | && volume_threshold == 0) 33 | { 34 | prev = volume_threshold; 35 | item = iter.next(); 36 | } else { 37 | valid = false; 38 | break; 39 | } 40 | } 41 | valid 42 | } 43 | } 44 | 45 | impl StorageKey> { 46 | #[storage(read)] 47 | pub fn get_volume_protocol_fee(self, volume: u64) -> ProtocolFee { 48 | let len = self.len(); 49 | if len == 0 { 50 | return ProtocolFee::new(); 51 | } 52 | let mut index = 0; 53 | if volume > 0 { 54 | let mut left = 0; 55 | let mut right = self.len() - 1; 56 | while left <= right { 57 | let mid = left + (right - left) / 2; 58 | if self.get(mid).unwrap().read().volume_threshold <= volume 59 | { 60 | index = mid; 61 | left = mid + 1; 62 | } else { 63 | right = mid - 1; 64 | } 65 | } 66 | } 67 | self.get(index).unwrap().read() 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /spark-market/src/data_structures/user_volume.sw: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | pub struct UserVolume { 4 | epoch: u64, 5 | volume: u64, 6 | } 7 | 8 | impl UserVolume { 9 | pub fn new() -> Self { 10 | Self { 11 | epoch: 0, 12 | volume: 0, 13 | } 14 | } 15 | 16 | pub fn get(self, protocol_epoch: u64) -> u64 { 17 | if self.epoch < protocol_epoch { 18 | 0 19 | } else { 20 | self.volume 21 | } 22 | } 23 | 24 | pub fn update(ref mut self, protocol_epoch: u64, volume: u64) -> Self { 25 | if self.epoch < protocol_epoch { 26 | self.epoch = protocol_epoch; 27 | self.volume = volume; 28 | } else { 29 | self.volume += volume; 30 | } 31 | self 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /spark-market/src/errors.sw: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | pub enum AssetError { 4 | InvalidAsset: (), 5 | InvalidFeeAsset: (), 6 | InvalidMarketAsset: (), 7 | } 8 | 9 | pub enum ValueError { 10 | InvalidAmount: (), 11 | InvalidSlippage: (), 12 | InvalidArrayLength: (), 13 | InvalidFeeAmount: (u64, u64), 14 | InvalidEpoch: (u64, u64, u64, u64), 15 | InvalidFeeSorting: (), 16 | InvalidFeeZeroBased: (), 17 | InvalidValueSame: (), 18 | InvalidMarketSame: (), 19 | } 20 | 21 | pub enum OrderError { 22 | OrderDuplicate: b256, 23 | OrderNotFound: b256, 24 | PriceTooSmall: (u64, u64), 25 | OrderSizeTooSmall: u64, 26 | OrderSizeNotSmall: (), 27 | ZeroLockAmount: (), 28 | ZeroUnlockAmount: (), 29 | ZeroTransferAmount: (), 30 | FailedToRemove: b256, 31 | } 32 | 33 | pub enum MatchError { 34 | CantMatch: (b256, b256), 35 | CantMatchMany: (), 36 | CantFulfillMany: (), 37 | CantFulfillFOK: (), 38 | } 39 | 40 | pub enum AuthError { 41 | Unauthorized: (), 42 | } 43 | 44 | pub enum AccountError { 45 | InsufficientBalance: (u64, u64, bool), 46 | } 47 | 48 | pub enum MathError { 49 | Overflow: (), 50 | } 51 | -------------------------------------------------------------------------------- /spark-market/src/events.sw: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | use ::data_structures::{ 4 | account::Account, 5 | asset_type::AssetType, 6 | limit_type::LimitType, 7 | order_type::OrderType, 8 | protocol_fee::ProtocolFee, 9 | }; 10 | 11 | pub struct DepositEvent { 12 | pub amount: u64, 13 | pub asset: AssetId, 14 | pub user: Identity, 15 | pub account: Account, 16 | pub caller: Identity, 17 | } 18 | 19 | pub struct OpenOrderEvent { 20 | pub amount: u64, 21 | pub asset: AssetId, 22 | pub order_type: OrderType, 23 | pub order_id: b256, 24 | pub price: u64, 25 | pub user: Identity, 26 | pub balance: Account, 27 | pub limit_type: LimitType, 28 | } 29 | 30 | pub struct CancelOrderEvent { 31 | pub order_id: b256, 32 | pub user: Identity, 33 | pub balance: Account, 34 | } 35 | 36 | pub struct TradeOrderEvent { 37 | pub base_sell_order_id: b256, 38 | pub base_buy_order_id: b256, 39 | pub base_sell_order_limit: LimitType, 40 | pub base_buy_order_limit: LimitType, 41 | pub order_matcher: Identity, 42 | pub trade_size: u64, 43 | pub trade_price: u64, 44 | pub block_height: u32, 45 | pub tx_id: b256, 46 | pub order_seller: Identity, 47 | pub order_buyer: Identity, 48 | pub s_balance: Account, 49 | pub b_balance: Account, 50 | pub seller_is_maker: bool, 51 | } 52 | pub struct WithdrawEvent { 53 | pub amount: u64, 54 | pub asset: AssetId, 55 | pub user: Identity, 56 | pub account: Account, 57 | } 58 | 59 | pub struct WithdrawToMarketEvent { 60 | pub amount: u64, 61 | pub asset: AssetId, 62 | pub user: Identity, 63 | pub account: Account, 64 | pub market: ContractId, 65 | } 66 | 67 | pub struct SetEpochEvent { 68 | pub epoch: u64, 69 | pub epoch_duration: u64, 70 | } 71 | 72 | pub struct SetProtocolFeeEvent { 73 | pub protocol_fee: Vec, 74 | } 75 | 76 | pub struct SetMatcherRewardEvent { 77 | pub amount: u64, 78 | } 79 | 80 | pub struct SetStoreOrderChangeInfoEvent { 81 | pub store: bool, 82 | } 83 | 84 | pub struct SetMinOrderSizeEvent { 85 | pub size: u64, 86 | } 87 | 88 | pub struct SetMinOrderPriceEvent { 89 | pub price: u64, 90 | } 91 | -------------------------------------------------------------------------------- /spark-market/src/interface.sw: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | use ::data_structures::{ 4 | account::Account, 5 | asset_type::AssetType, 6 | limit_type::LimitType, 7 | order::Order, 8 | order_change::OrderChangeInfo, 9 | order_type::OrderType, 10 | protocol_fee::ProtocolFee, 11 | }; 12 | 13 | abi SparkMarket { 14 | #[storage(read, write)] 15 | fn initialize_ownership(new_owner: Identity); 16 | 17 | #[storage(read, write)] 18 | fn transfer_ownership(new_owner: Identity); 19 | 20 | #[payable] 21 | #[storage(read, write)] 22 | fn deposit(); 23 | 24 | #[payable] 25 | #[storage(read, write)] 26 | fn deposit_for(user: Identity); 27 | 28 | #[storage(read, write)] 29 | fn withdraw(amount: u64, asset_type: AssetType); 30 | 31 | #[storage(read, write)] 32 | fn withdraw_to_market(amount: u64, asset_type: AssetType, market: ContractId); 33 | 34 | #[storage(read, write)] 35 | fn open_order(amount: u64, order_type: OrderType, price: u64) -> b256; 36 | 37 | #[storage(read, write)] 38 | fn open_market_order(amount: u64, order_type: OrderType, price: u64) -> b256; 39 | 40 | #[storage(read, write)] 41 | fn cancel_order(order_id: b256); 42 | 43 | #[storage(read, write)] 44 | fn cancel_small_order(order_id: b256); 45 | 46 | #[storage(read, write)] 47 | fn match_order_many(orders: Vec); 48 | 49 | #[storage(read, write)] 50 | fn fulfill_order_many( 51 | amount: u64, 52 | order_type: OrderType, 53 | limit_type: LimitType, 54 | price: u64, 55 | slippage: u64, 56 | orders: Vec, 57 | ) -> b256; 58 | 59 | #[storage(write)] 60 | fn set_epoch(epoch: u64, epoch_duration: u64); 61 | 62 | #[storage(write)] 63 | fn set_protocol_fee(protocol_fee: Vec); 64 | 65 | #[storage(read, write)] 66 | fn set_matcher_fee(amount: u64); 67 | 68 | #[storage(read, write)] 69 | fn set_min_order_size(size: u64); 70 | 71 | #[storage(read, write)] 72 | fn set_min_order_price(price: u64); 73 | } 74 | 75 | abi SparkMarketInfo { 76 | #[storage(read)] 77 | fn account(user: Identity) -> Account; 78 | 79 | #[storage(read)] 80 | fn get_epoch() -> (u64, u64); 81 | 82 | #[storage(read)] 83 | fn matcher_fee() -> u64; 84 | 85 | #[storage(read)] 86 | fn protocol_fee() -> Vec; 87 | 88 | #[storage(read)] 89 | fn protocol_fee_user(user: Identity) -> (u64, u64); 90 | 91 | #[storage(read)] 92 | fn protocol_fee_user_amount(amount: u64, user: Identity) -> (u64, u64); 93 | 94 | #[storage(read)] 95 | fn order(order: b256) -> Option; 96 | 97 | #[storage(read)] 98 | fn market_order(order: b256) -> Option; 99 | 100 | #[storage(read)] 101 | fn user_orders(user: Identity) -> Vec; 102 | 103 | #[storage(read)] 104 | fn user_order_height(user: Identity) -> u64; 105 | 106 | #[storage(read)] 107 | fn min_order_size() -> u64; 108 | 109 | #[storage(read)] 110 | fn min_order_price() -> u64; 111 | 112 | #[storage(read)] 113 | fn config() -> (AssetId, u32, AssetId, u32, Option, u32, u32); 114 | 115 | fn order_id( 116 | order_type: OrderType, 117 | owner: Identity, 118 | price: u64, 119 | block_height: u32, 120 | order_height: u64, 121 | ) -> b256; 122 | } 123 | -------------------------------------------------------------------------------- /spark-market/src/math.sw: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | use std::u128::{U128, U128Error}; 4 | 5 | pub const HUNDRED_PERCENT = 10_000; 6 | 7 | impl u64 { 8 | pub fn mul_div(self, mul_to: u64, div_to: u64) -> Result { 9 | let mul_result = U128::from((0, self)) * U128::from((0, mul_to)); 10 | let div_result = mul_result / U128::from((0, div_to)); 11 | div_result.as_u64() 12 | } 13 | } 14 | 15 | pub fn lts(i: u64, k: u64, len: u64) -> bool { 16 | (i < len && k < len) 17 | } 18 | 19 | pub fn min(a: u64, b: u64) -> u64 { 20 | if a < b { a } else { b } 21 | } 22 | 23 | pub fn max(a: u64, b: u64) -> u64 { 24 | if a > b { a } else { b } 25 | } 26 | 27 | pub fn distance(a: u64, b: u64) -> u64 { 28 | if a > b { a - b } else { b - a } 29 | } 30 | -------------------------------------------------------------------------------- /spark-market/tests/functions/core/initialize_ownership.rs: -------------------------------------------------------------------------------- 1 | use crate::setup::{setup, Defaults}; 2 | use fuels::types::Identity; 3 | 4 | mod revert { 5 | 6 | use super::*; 7 | 8 | #[tokio::test] 9 | #[should_panic(expected = "CannotReinitialized")] 10 | async fn reverts_when_already_initialized() { 11 | let defaults = Defaults::default(); 12 | let (contract, _owner, user, _, _, _assets) = setup( 13 | defaults.base_decimals, 14 | defaults.quote_decimals, 15 | defaults.price_decimals, 16 | ) 17 | .await 18 | .unwrap(); 19 | 20 | let new_owner: Identity = user.wallet.address().into(); 21 | contract 22 | .with_account(&user.wallet) 23 | .initialize_ownership(new_owner) 24 | .await 25 | .unwrap(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /spark-market/tests/functions/core/mod.rs: -------------------------------------------------------------------------------- 1 | mod cancel_order; 2 | mod cancel_small_order; 3 | mod deposit; 4 | mod deposit_for; 5 | mod fulfill_order_many; 6 | mod initialize_ownership; 7 | mod match_order_many; 8 | mod open_market_order; 9 | mod open_order; 10 | mod pause; 11 | mod set_epoch; 12 | mod set_matcher_fee; 13 | mod set_min_order_price; 14 | mod set_min_order_size; 15 | mod set_protocol_fee; 16 | mod transfer_ownership; 17 | mod withdraw; 18 | mod withdraw_to_market; 19 | -------------------------------------------------------------------------------- /spark-market/tests/functions/core/pause.rs: -------------------------------------------------------------------------------- 1 | use crate::setup::{setup, Defaults}; 2 | 3 | mod success { 4 | 5 | use super::*; 6 | 7 | #[tokio::test] 8 | async fn pause() -> anyhow::Result<()> { 9 | let defaults = Defaults::default(); 10 | let (contract, _owner, _, _, _, _) = setup( 11 | defaults.base_decimals, 12 | defaults.quote_decimals, 13 | defaults.price_decimals, 14 | ) 15 | .await?; 16 | 17 | // Assert precondition of initial fee 18 | assert_eq!(contract.is_paused().await?.value, false); 19 | 20 | // Increase the fee to new_fee 21 | let _ = contract.pause().await?; 22 | 23 | // Check fee has changed from the initial fee 24 | assert_eq!(contract.is_paused().await?.value, true); 25 | 26 | // Increase the fee to new_fee 27 | let _ = contract.unpause().await?; 28 | 29 | // Assert precondition of initial fee 30 | assert_eq!(contract.is_paused().await?.value, false); 31 | 32 | Ok(()) 33 | } 34 | } 35 | 36 | mod revert { 37 | 38 | use super::*; 39 | 40 | #[tokio::test] 41 | #[should_panic(expected = "NotOwner")] 42 | async fn reverts_pause_when_non_owner() { 43 | let defaults = Defaults::default(); 44 | let (contract, _owner, user, _, _, _) = setup( 45 | defaults.base_decimals, 46 | defaults.quote_decimals, 47 | defaults.price_decimals, 48 | ) 49 | .await 50 | .unwrap(); 51 | 52 | // Reverts 53 | contract.with_account(&user.wallet).pause().await.unwrap(); 54 | } 55 | 56 | #[tokio::test] 57 | #[should_panic(expected = "NotOwner")] 58 | async fn reverts_unpause_when_non_owner() { 59 | let defaults = Defaults::default(); 60 | let (contract, _owner, user, _, _, _) = setup( 61 | defaults.base_decimals, 62 | defaults.quote_decimals, 63 | defaults.price_decimals, 64 | ) 65 | .await 66 | .unwrap(); 67 | 68 | // Reverts 69 | contract.with_account(&user.wallet).unpause().await.unwrap(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /spark-market/tests/functions/core/set_epoch.rs: -------------------------------------------------------------------------------- 1 | use crate::setup::{now_tai64, setup, Defaults}; 2 | use spark_market_sdk::SetEpochEvent; 3 | use tokio; 4 | 5 | mod success { 6 | 7 | use super::*; 8 | 9 | #[tokio::test] 10 | async fn set_epoch_fee() -> anyhow::Result<()> { 11 | let defaults = Defaults::default(); 12 | let (contract, _owner, _, _, _, _assets) = setup( 13 | defaults.base_decimals, 14 | defaults.quote_decimals, 15 | defaults.price_decimals, 16 | ) 17 | .await?; 18 | 19 | let one_month = (86400.0 * 365.25 / 12.0) as u64; 20 | 21 | // Fetch the initial epoch values 22 | let (uninitialized_epoch, uninitialized_epoch_duration) = contract.get_epoch().await?.value; 23 | 24 | assert_eq!(uninitialized_epoch, 0); 25 | assert_eq!(uninitialized_epoch_duration, one_month); 26 | 27 | let tai64_epoch = now_tai64(); 28 | 29 | // Define the new epoch duration (e.g., 1 day) 30 | let epoch_duration = 60 * 60 * 24; 31 | 32 | // Increase the epoch and duration 33 | let response = contract.set_epoch(tai64_epoch, epoch_duration).await?; 34 | 35 | // Log should be emitted when epoch is changed 36 | let log = response.decode_logs_with_type::().unwrap(); 37 | let event = log.first().unwrap(); 38 | assert_eq!( 39 | *event, 40 | SetEpochEvent { 41 | epoch: tai64_epoch, 42 | epoch_duration 43 | } 44 | ); 45 | 46 | // Check if the epoch values have been updated correctly 47 | let (new_epoch, new_epoch_duration) = contract.get_epoch().await?.value; 48 | 49 | assert_eq!(tai64_epoch, new_epoch); 50 | assert_eq!(epoch_duration, new_epoch_duration); 51 | 52 | Ok(()) 53 | } 54 | } 55 | 56 | mod revert { 57 | 58 | use super::*; 59 | 60 | #[tokio::test] 61 | #[should_panic(expected = "NotOwner")] 62 | async fn reverts_when_non_owner() { 63 | let defaults = Defaults::default(); 64 | let (contract, _, user, _, _, _assets) = setup( 65 | defaults.base_decimals, 66 | defaults.quote_decimals, 67 | defaults.price_decimals, 68 | ) 69 | .await 70 | .unwrap(); 71 | 72 | let new_epoch = 0; 73 | let epoch_duration = 60 * 60 * 24; 74 | 75 | // Attempt to set the epoch with a non-owner user 76 | contract 77 | .with_account(&user.wallet) 78 | .set_epoch(new_epoch, epoch_duration) 79 | .await 80 | .unwrap(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /spark-market/tests/functions/core/set_matcher_fee.rs: -------------------------------------------------------------------------------- 1 | use crate::setup::{setup, Defaults}; 2 | 3 | mod success { 4 | 5 | use super::*; 6 | use spark_market_sdk::SetMatcherRewardEvent; 7 | 8 | #[tokio::test] 9 | async fn sets_matcher_fee() -> anyhow::Result<()> { 10 | let defaults = Defaults::default(); 11 | let (contract, _owner, _, _, _, _assets) = setup( 12 | defaults.base_decimals, 13 | defaults.quote_decimals, 14 | defaults.price_decimals, 15 | ) 16 | .await?; 17 | 18 | let initial_fee = 0; 19 | let new_fee = 5; 20 | 21 | // Assert precondition of initial fee 22 | assert_eq!(contract.matcher_fee().await?.value, initial_fee); 23 | 24 | // Increase the fee to new_fee 25 | let response = contract.set_matcher_fee(new_fee).await?; 26 | 27 | // Log should be emitted when fee is changed 28 | let log = response 29 | .decode_logs_with_type::() 30 | .unwrap(); 31 | let event = log.first().unwrap(); 32 | assert_eq!(*event, SetMatcherRewardEvent { amount: new_fee }); 33 | 34 | // Check fee has changed from the initial fee 35 | assert_ne!(initial_fee, new_fee); 36 | assert_eq!(contract.matcher_fee().await?.value, new_fee); 37 | 38 | Ok(()) 39 | } 40 | } 41 | 42 | mod revert { 43 | 44 | use super::*; 45 | 46 | #[tokio::test] 47 | #[should_panic(expected = "NotOwner")] 48 | async fn reverts_when_non_owner() { 49 | let defaults = Defaults::default(); 50 | let (contract, _owner, user, _, _, _) = setup( 51 | defaults.base_decimals, 52 | defaults.quote_decimals, 53 | defaults.price_decimals, 54 | ) 55 | .await 56 | .unwrap(); 57 | 58 | let new_fee = 5; 59 | 60 | // Reverts 61 | contract 62 | .with_account(&user.wallet) 63 | .set_matcher_fee(new_fee) 64 | .await 65 | .unwrap(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /spark-market/tests/functions/core/set_min_order_price.rs: -------------------------------------------------------------------------------- 1 | use crate::setup::{setup, Defaults}; 2 | 3 | mod success { 4 | 5 | use super::*; 6 | use spark_market_sdk::SetMinOrderPriceEvent; 7 | 8 | #[tokio::test] 9 | async fn sets_min_order_price() -> anyhow::Result<()> { 10 | let defaults = Defaults::default(); 11 | let (contract, _, _, _, _, _) = setup( 12 | defaults.base_decimals, 13 | defaults.quote_decimals, 14 | defaults.price_decimals, 15 | ) 16 | .await?; 17 | 18 | let initial_price = 0; 19 | let new_price = 5; 20 | 21 | // Assert precondition of initial fee 22 | assert_eq!(contract.min_order_price().await?.value, initial_price); 23 | 24 | // Increase the fee to new_fee 25 | let response = contract.set_min_order_price(new_price).await?; 26 | 27 | // Log should be emitted when fee is changed 28 | let log = response 29 | .decode_logs_with_type::() 30 | .unwrap(); 31 | let event = log.first().unwrap(); 32 | assert_eq!(*event, SetMinOrderPriceEvent { price: new_price }); 33 | 34 | // Check fee has changed from the initial price 35 | assert_ne!(initial_price, new_price); 36 | assert_eq!(contract.min_order_price().await?.value, new_price); 37 | 38 | Ok(()) 39 | } 40 | } 41 | 42 | mod revert { 43 | 44 | use super::*; 45 | 46 | #[tokio::test] 47 | #[should_panic(expected = "NotOwner")] 48 | async fn reverts_when_non_owner() { 49 | let defaults = Defaults::default(); 50 | let (contract, _, user, _, _, _) = setup( 51 | defaults.base_decimals, 52 | defaults.quote_decimals, 53 | defaults.price_decimals, 54 | ) 55 | .await 56 | .unwrap(); 57 | 58 | let new_price = 5; 59 | 60 | // Reverts 61 | contract 62 | .with_account(&user.wallet) 63 | .set_min_order_price(new_price) 64 | .await 65 | .unwrap(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /spark-market/tests/functions/core/set_min_order_size.rs: -------------------------------------------------------------------------------- 1 | use crate::setup::{setup, Defaults}; 2 | 3 | mod success { 4 | 5 | use super::*; 6 | use spark_market_sdk::SetMinOrderSizeEvent; 7 | 8 | #[tokio::test] 9 | async fn sets_min_order_size() -> anyhow::Result<()> { 10 | let defaults = Defaults::default(); 11 | let (contract, _, _, _, _, _) = setup( 12 | defaults.base_decimals, 13 | defaults.quote_decimals, 14 | defaults.price_decimals, 15 | ) 16 | .await?; 17 | 18 | let initial_size = 0; 19 | let new_size = 5; 20 | 21 | // Assert precondition of initial fee 22 | assert_eq!(contract.min_order_size().await?.value, initial_size); 23 | 24 | // Increase the fee to new_fee 25 | let response = contract.set_min_order_size(new_size).await?; 26 | 27 | // Log should be emitted when fee is changed 28 | let log = response 29 | .decode_logs_with_type::() 30 | .unwrap(); 31 | let event = log.first().unwrap(); 32 | assert_eq!(*event, SetMinOrderSizeEvent { size: new_size }); 33 | 34 | // Check fee has changed from the initial size 35 | assert_ne!(initial_size, new_size); 36 | assert_eq!(contract.min_order_size().await?.value, new_size); 37 | 38 | Ok(()) 39 | } 40 | } 41 | 42 | mod revert { 43 | 44 | use super::*; 45 | 46 | #[tokio::test] 47 | #[should_panic(expected = "NotOwner")] 48 | async fn reverts_when_non_owner() { 49 | let defaults = Defaults::default(); 50 | let (contract, _, user, _, _, _) = setup( 51 | defaults.base_decimals, 52 | defaults.quote_decimals, 53 | defaults.price_decimals, 54 | ) 55 | .await 56 | .unwrap(); 57 | 58 | let new_size = 5; 59 | 60 | // Reverts 61 | contract 62 | .with_account(&user.wallet) 63 | .set_min_order_size(new_size) 64 | .await 65 | .unwrap(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /spark-market/tests/functions/core/transfer_ownership.rs: -------------------------------------------------------------------------------- 1 | use crate::setup::{setup, Defaults}; 2 | use fuels::types::Identity; 3 | 4 | mod success { 5 | 6 | use super::*; 7 | use spark_market_sdk::OwnershipTransferred; 8 | 9 | #[tokio::test] 10 | async fn transfer_ownership() -> anyhow::Result<()> { 11 | let defaults = Defaults::default(); 12 | let (contract, owner, user, _, _, _assets) = setup( 13 | defaults.base_decimals, 14 | defaults.quote_decimals, 15 | defaults.price_decimals, 16 | ) 17 | .await?; 18 | 19 | let new_owner: Identity = user.wallet.address().into(); 20 | let response = contract.transfer_ownership(new_owner).await?; 21 | 22 | // Log should be emitted when fee is changed 23 | let log = response 24 | .decode_logs_with_type::() 25 | .unwrap(); 26 | let event = log.first().unwrap(); 27 | assert_eq!( 28 | *event, 29 | OwnershipTransferred { 30 | new_owner: new_owner, 31 | previous_owner: owner.wallet.address().into(), 32 | } 33 | ); 34 | assert_eq!(contract.config().await?.value.4.unwrap(), new_owner); 35 | 36 | let _ = contract 37 | .with_account(&user.wallet) 38 | .set_matcher_fee(1) 39 | .await?; 40 | 41 | Ok(()) 42 | } 43 | } 44 | 45 | mod revert { 46 | 47 | use super::*; 48 | 49 | #[tokio::test] 50 | #[should_panic(expected = "NotOwner")] 51 | async fn reverts_when_non_owner() { 52 | let defaults = Defaults::default(); 53 | let (contract, _owner, user, _, _, _assets) = setup( 54 | defaults.base_decimals, 55 | defaults.quote_decimals, 56 | defaults.price_decimals, 57 | ) 58 | .await 59 | .unwrap(); 60 | 61 | let new_owner: Identity = user.wallet.address().into(); 62 | contract 63 | .with_account(&user.wallet) 64 | .transfer_ownership(new_owner) 65 | .await 66 | .unwrap(); 67 | } 68 | 69 | #[tokio::test] 70 | #[should_panic(expected = "NotOwner")] 71 | async fn reverts_when_already_transfered() { 72 | let defaults = Defaults::default(); 73 | let (contract, owner, user, _, _, _assets) = setup( 74 | defaults.base_decimals, 75 | defaults.quote_decimals, 76 | defaults.price_decimals, 77 | ) 78 | .await 79 | .unwrap(); 80 | 81 | let new_owner: Identity = user.wallet.address().into(); 82 | contract.transfer_ownership(new_owner).await.unwrap(); 83 | 84 | contract 85 | .with_account(&owner.wallet) 86 | .transfer_ownership(new_owner) 87 | .await 88 | .unwrap(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /spark-market/tests/functions/info/account.rs: -------------------------------------------------------------------------------- 1 | mod success { 2 | 3 | use crate::setup::{create_account, setup, Defaults}; 4 | 5 | #[tokio::test] 6 | async fn returns_account_zeros() -> anyhow::Result<()> { 7 | let defaults = Defaults::default(); 8 | let (contract, owner, _user, _, _, _assets) = setup( 9 | defaults.base_decimals, 10 | defaults.quote_decimals, 11 | defaults.price_decimals, 12 | ) 13 | .await?; 14 | let expected_account = create_account(0, 0, 0, 0); 15 | 16 | assert_eq!( 17 | contract.account(owner.identity()).await?.value, 18 | expected_account 19 | ); 20 | 21 | Ok(()) 22 | } 23 | 24 | #[tokio::test] 25 | async fn returns_account_info() -> anyhow::Result<()> { 26 | let defaults = Defaults::default(); 27 | let (contract, owner, _user, _, _, assets) = setup( 28 | defaults.base_decimals, 29 | defaults.quote_decimals, 30 | defaults.price_decimals, 31 | ) 32 | .await?; 33 | let deposit_amount = 100; 34 | let expected_account = create_account(deposit_amount, 0, 0, 0); 35 | 36 | let _ = contract.deposit(deposit_amount, assets.base.id).await?; 37 | 38 | let user_account = contract.account(owner.identity()).await?.value; 39 | 40 | assert_eq!(user_account, expected_account); 41 | 42 | Ok(()) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /spark-market/tests/functions/info/config.rs: -------------------------------------------------------------------------------- 1 | mod success { 2 | 3 | use crate::setup::{setup, Defaults}; 4 | use spark_market_sdk::SparkMarketContract; 5 | 6 | #[tokio::test] 7 | async fn returns_config() -> anyhow::Result<()> { 8 | let defaults = Defaults::default(); 9 | let (contract, owner, _user, _, _, assets) = setup( 10 | defaults.base_decimals, 11 | defaults.quote_decimals, 12 | defaults.price_decimals, 13 | ) 14 | .await?; 15 | 16 | assert_eq!( 17 | contract.config().await?.value, 18 | ( 19 | assets.base.id, 20 | assets.base.decimals, 21 | assets.quote.id, 22 | assets.quote.decimals, 23 | Some(owner.address().into()), 24 | defaults.price_decimals, 25 | SparkMarketContract::sdk_version(), 26 | ) 27 | ); 28 | 29 | Ok(()) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /spark-market/tests/functions/info/matcher_fee.rs: -------------------------------------------------------------------------------- 1 | mod success { 2 | 3 | use crate::setup::{setup, Defaults}; 4 | 5 | #[tokio::test] 6 | async fn returns_matcher_fee() -> anyhow::Result<()> { 7 | let defaults = Defaults::default(); 8 | let (contract, _owner, _user, _, _, _assets) = setup( 9 | defaults.base_decimals, 10 | defaults.quote_decimals, 11 | defaults.price_decimals, 12 | ) 13 | .await?; 14 | 15 | assert_eq!(contract.matcher_fee().await?.value, 0_u64); 16 | 17 | // Change fee to be non-zero for testing purposes 18 | let matcher_fee = 1000_u64; 19 | 20 | let _ = contract.set_matcher_fee(matcher_fee).await?; 21 | 22 | assert_eq!(contract.matcher_fee().await?.value, matcher_fee); 23 | 24 | Ok(()) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /spark-market/tests/functions/info/min_order_price.rs: -------------------------------------------------------------------------------- 1 | mod success { 2 | 3 | use crate::setup::{setup, Defaults}; 4 | 5 | #[tokio::test] 6 | async fn returns_min_order_price() -> anyhow::Result<()> { 7 | let defaults = Defaults::default(); 8 | let (contract, _, _, _, _, _) = setup( 9 | defaults.base_decimals, 10 | defaults.quote_decimals, 11 | defaults.price_decimals, 12 | ) 13 | .await?; 14 | 15 | assert_eq!(contract.min_order_price().await?.value, 0_u64); 16 | 17 | // Change fee to be non-zero for testing purposes 18 | let min_order_price = 1_000_000_000_u64; 19 | 20 | let _ = contract.set_min_order_price(min_order_price).await?; 21 | 22 | assert_eq!(contract.min_order_price().await?.value, min_order_price); 23 | 24 | Ok(()) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /spark-market/tests/functions/info/min_order_size.rs: -------------------------------------------------------------------------------- 1 | mod success { 2 | 3 | use crate::setup::{setup, Defaults}; 4 | 5 | #[tokio::test] 6 | async fn returns_min_order_size() -> anyhow::Result<()> { 7 | let defaults = Defaults::default(); 8 | let (contract, _, _, _, _, _) = setup( 9 | defaults.base_decimals, 10 | defaults.quote_decimals, 11 | defaults.price_decimals, 12 | ) 13 | .await?; 14 | 15 | assert_eq!(contract.min_order_size().await?.value, 0_u64); 16 | 17 | // Change fee to be non-zero for testing purposes 18 | let min_order_size = 10000_u64; 19 | 20 | let _ = contract.set_min_order_size(min_order_size).await?; 21 | 22 | assert_eq!(contract.min_order_size().await?.value, min_order_size); 23 | 24 | Ok(()) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /spark-market/tests/functions/info/mod.rs: -------------------------------------------------------------------------------- 1 | mod account; 2 | mod config; 3 | mod matcher_fee; 4 | mod min_order_price; 5 | mod min_order_size; 6 | mod order; 7 | mod order_id; 8 | mod protocol_fee; 9 | mod protocol_fee_user; 10 | mod protocol_fee_user_amount; 11 | mod user_orders; 12 | -------------------------------------------------------------------------------- /spark-market/tests/functions/info/order.rs: -------------------------------------------------------------------------------- 1 | mod success { 2 | 3 | use crate::setup::{setup, Defaults}; 4 | use fuels::accounts::ViewOnlyAccount; 5 | use fuels::types::Bits256; 6 | use spark_market_sdk::OrderType; 7 | 8 | #[tokio::test] 9 | async fn returns_none() -> anyhow::Result<()> { 10 | let defaults = Defaults::default(); 11 | let (contract, _owner, _user, _, _, _assets) = setup( 12 | defaults.base_decimals, 13 | defaults.quote_decimals, 14 | defaults.price_decimals, 15 | ) 16 | .await?; 17 | 18 | assert!(contract.order(Bits256([0u8; 32])).await?.value.is_none()); 19 | 20 | Ok(()) 21 | } 22 | 23 | #[tokio::test] 24 | async fn returns_order() -> anyhow::Result<()> { 25 | let defaults = Defaults::default(); 26 | let (contract, owner, _user, _, _, assets) = setup( 27 | defaults.base_decimals, 28 | defaults.quote_decimals, 29 | defaults.price_decimals, 30 | ) 31 | .await?; 32 | 33 | let _ = contract.deposit(100, assets.base.id).await?; 34 | let id = contract 35 | .open_order(1, OrderType::Sell, 70_000_000_000_000_u64) 36 | .await?; 37 | 38 | let order = contract.order(id.value).await?.value.unwrap(); 39 | let expected_id = contract 40 | .order_id( 41 | order.order_type, 42 | order.owner, 43 | order.price, 44 | owner.wallet.try_provider()?.latest_block_height().await?, 45 | 0, 46 | ) 47 | .await?; 48 | 49 | assert_eq!(id.value, expected_id.value); 50 | 51 | Ok(()) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /spark-market/tests/functions/info/order_id.rs: -------------------------------------------------------------------------------- 1 | use crate::setup::{setup, Defaults}; 2 | use spark_market_sdk::{/*AssetType,*/ OrderType}; 3 | 4 | mod success { 5 | 6 | use super::*; 7 | 8 | #[tokio::test] 9 | async fn orders_create_different_ids() -> anyhow::Result<()> { 10 | let defaults = Defaults::default(); 11 | let (contract, owner, _user, _, _, _) = setup( 12 | defaults.base_decimals, 13 | defaults.quote_decimals, 14 | defaults.price_decimals, 15 | ) 16 | .await?; 17 | 18 | let id1 = contract 19 | .order_id( 20 | OrderType::Buy, 21 | owner.identity(), 22 | 70_000_000_000_000_u64, 23 | 2, 24 | 0, 25 | ) 26 | .await?; 27 | let id2 = contract 28 | .order_id( 29 | OrderType::Buy, 30 | owner.identity(), 31 | 80_000_000_000_000_u64, 32 | 2, 33 | 1, 34 | ) 35 | .await?; 36 | 37 | assert_ne!(id1.value, id2.value); 38 | 39 | Ok(()) 40 | } 41 | 42 | #[tokio::test] 43 | async fn accepts_buy_order() -> anyhow::Result<()> { 44 | // In this test we only care about the test not reverting with the correct asset 45 | let defaults = Defaults::default(); 46 | let (contract, owner, _user, _, _, _) = setup( 47 | defaults.base_decimals, 48 | defaults.quote_decimals, 49 | defaults.price_decimals, 50 | ) 51 | .await?; 52 | 53 | let _ = contract 54 | .order_id( 55 | OrderType::Buy, 56 | owner.identity(), 57 | 70_000_000_000_000_u64, 58 | 2, 59 | 0, 60 | ) 61 | .await?; 62 | 63 | Ok(()) 64 | } 65 | 66 | #[tokio::test] 67 | async fn accepts_sell_order() -> anyhow::Result<()> { 68 | // In this test we only care about the test not reverting with the correct asset 69 | let defaults = Defaults::default(); 70 | let (contract, owner, _user, _, _, _) = setup( 71 | defaults.base_decimals, 72 | defaults.quote_decimals, 73 | defaults.price_decimals, 74 | ) 75 | .await?; 76 | 77 | let _ = contract 78 | .order_id( 79 | OrderType::Buy, 80 | owner.identity(), 81 | 70_000_000_000_000_u64, 82 | 2, 83 | 1, 84 | ) 85 | .await?; 86 | 87 | Ok(()) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /spark-market/tests/functions/info/protocol_fee.rs: -------------------------------------------------------------------------------- 1 | mod success { 2 | 3 | use crate::setup::{setup, Defaults}; 4 | use spark_market_sdk::ProtocolFee; 5 | 6 | #[tokio::test] 7 | async fn returns_protocol_fee() -> anyhow::Result<()> { 8 | let defaults = Defaults::default(); 9 | let (contract, _owner, _, _, _, _assets) = setup( 10 | defaults.base_decimals, 11 | defaults.quote_decimals, 12 | defaults.price_decimals, 13 | ) 14 | .await?; 15 | 16 | // Change fee to be non-zero for testing purposes 17 | let protocol_fee = vec![ 18 | ProtocolFee { 19 | maker_fee: 10, 20 | taker_fee: 15, 21 | volume_threshold: 0, 22 | }, 23 | ProtocolFee { 24 | maker_fee: 8, 25 | taker_fee: 13, 26 | volume_threshold: 10000, 27 | }, 28 | ProtocolFee { 29 | maker_fee: 6, 30 | taker_fee: 10, 31 | volume_threshold: 20000, 32 | }, 33 | ]; 34 | 35 | let _ = contract.set_protocol_fee(protocol_fee.clone()).await?; 36 | 37 | assert_eq!(contract.protocol_fee().await?.value, protocol_fee); 38 | 39 | Ok(()) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /spark-market/tests/functions/info/protocol_fee_user.rs: -------------------------------------------------------------------------------- 1 | mod success { 2 | 3 | use crate::setup::{setup, Defaults}; 4 | use spark_market_sdk::ProtocolFee; 5 | 6 | #[tokio::test] 7 | async fn returns_protocol_fee_user_base() -> anyhow::Result<()> { 8 | let defaults = Defaults::default(); 9 | let (contract, _owner, _user, _, _, _assets) = setup( 10 | defaults.base_decimals, 11 | defaults.quote_decimals, 12 | defaults.price_decimals, 13 | ) 14 | .await?; 15 | 16 | let protocol_fee = vec![ 17 | ProtocolFee { 18 | maker_fee: 10, 19 | taker_fee: 15, 20 | volume_threshold: 0, 21 | }, 22 | ProtocolFee { 23 | maker_fee: 8, 24 | taker_fee: 13, 25 | volume_threshold: 10000, 26 | }, 27 | ProtocolFee { 28 | maker_fee: 6, 29 | taker_fee: 10, 30 | volume_threshold: 20000, 31 | }, 32 | ]; 33 | 34 | let _ = contract.set_protocol_fee(protocol_fee.clone()).await?; 35 | assert_eq!( 36 | contract 37 | .protocol_fee_user(_user.address().into()) 38 | .await? 39 | .value, 40 | (protocol_fee[0].maker_fee, protocol_fee[0].taker_fee) 41 | ); 42 | 43 | Ok(()) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /spark-market/tests/functions/info/protocol_fee_user_amount.rs: -------------------------------------------------------------------------------- 1 | mod success { 2 | 3 | use crate::setup::{setup, Defaults}; 4 | use spark_market_sdk::ProtocolFee; 5 | 6 | #[tokio::test] 7 | async fn returns_protocol_fee_user_amount_base() -> anyhow::Result<()> { 8 | let defaults = Defaults::default(); 9 | let (contract, _owner, _user, _, _, _assets) = setup( 10 | defaults.base_decimals, 11 | defaults.quote_decimals, 12 | defaults.price_decimals, 13 | ) 14 | .await?; 15 | 16 | let protocol_fee = vec![ 17 | ProtocolFee { 18 | maker_fee: 10, 19 | taker_fee: 15, 20 | volume_threshold: 0, 21 | }, 22 | ProtocolFee { 23 | maker_fee: 8, 24 | taker_fee: 13, 25 | volume_threshold: 10000, 26 | }, 27 | ProtocolFee { 28 | maker_fee: 6, 29 | taker_fee: 10, 30 | volume_threshold: 20000, 31 | }, 32 | ]; 33 | 34 | let _ = contract.set_protocol_fee(protocol_fee.clone()).await?; 35 | let amount = 100_000_000u64; 36 | let handred_percent = 10_000u64; 37 | assert_eq!( 38 | contract 39 | .protocol_fee_user_amount(amount, _user.address().into()) 40 | .await? 41 | .value, 42 | ( 43 | amount * protocol_fee[0].maker_fee / handred_percent, 44 | amount * protocol_fee[0].taker_fee / handred_percent 45 | ) 46 | ); 47 | 48 | Ok(()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /spark-market/tests/functions/info/user_orders.rs: -------------------------------------------------------------------------------- 1 | mod success { 2 | 3 | use crate::setup::{setup, Defaults}; 4 | use spark_market_sdk::{/*AssetType,*/ OrderType}; 5 | 6 | #[tokio::test] 7 | async fn returns_zero_orders() -> anyhow::Result<()> { 8 | let defaults = Defaults::default(); 9 | let (contract, owner, _user, _, _, _assets) = setup( 10 | defaults.base_decimals, 11 | defaults.quote_decimals, 12 | defaults.price_decimals, 13 | ) 14 | .await?; 15 | 16 | let orders = contract.user_orders(owner.identity()).await?.value; 17 | assert_eq!(orders, vec![]); 18 | 19 | Ok(()) 20 | } 21 | 22 | #[tokio::test] 23 | async fn returns_orders() -> anyhow::Result<()> { 24 | let defaults = Defaults::default(); 25 | let (contract, owner, _user, _, _, assets) = setup( 26 | defaults.base_decimals, 27 | defaults.quote_decimals, 28 | defaults.price_decimals, 29 | ) 30 | .await?; 31 | 32 | let _ = contract.deposit(1000, assets.base.id).await?; 33 | let id1 = contract 34 | .open_order( 35 | 2, 36 | /*AssetType::Base,*/ OrderType::Sell, 37 | 70_000_000_000_000_u64, 38 | ) 39 | .await?; 40 | let id2 = contract 41 | .open_order( 42 | 1, 43 | /*AssetType::Base,*/ OrderType::Sell, 44 | 75_000_000_000_000_u64, 45 | ) 46 | .await?; 47 | 48 | let mut orders = contract.user_orders(owner.identity()).await?.value; 49 | 50 | assert_eq!(2, orders.len()); 51 | assert_eq!(id2.value, orders.pop().unwrap()); 52 | assert_eq!(id1.value, orders.pop().unwrap()); 53 | 54 | Ok(()) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /spark-market/tests/functions/mod.rs: -------------------------------------------------------------------------------- 1 | mod core; 2 | mod info; 3 | -------------------------------------------------------------------------------- /spark-market/tests/harness.rs: -------------------------------------------------------------------------------- 1 | mod functions; 2 | mod setup; 3 | -------------------------------------------------------------------------------- /spark-proxy-sdk/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "spark-proxy-sdk" 3 | version = "0.0.2" 4 | authors = { workspace = true } 5 | edition = { workspace = true } 6 | license = { workspace = true } 7 | rust-version = { workspace = true } 8 | description = "SDK for interacting with the Spark Proxy" 9 | repository = "https://github.com/compolabs/orderbook-contract" 10 | readme = "README.md" 11 | keywords = ["fuel", "sdk", "spark", "proxy", "orderbook"] 12 | include = [ 13 | "spark-proxy/out/release/*", 14 | "src/lib.rs", 15 | "Cargo.toml", 16 | "README.md" 17 | ] 18 | 19 | [dependencies] 20 | anyhow = { workspace = true } 21 | fuels = { workspace = true } 22 | rand = "0.8.5" 23 | tokio = { workspace = true } 24 | -------------------------------------------------------------------------------- /spark-proxy-sdk/README.md: -------------------------------------------------------------------------------- 1 | # Spark PROXY Contract Rust SDK 2 | 3 | The Spark Proxy Contract SDK designed for Spark Market contract communication. 4 | 5 | ## SparkProxyContract Type 6 | 7 | The sdk object as contract instance wrapper. 8 | 9 | ```rust 10 | pub struct SparkProxyContract { 11 | instance: SparkProxy, 12 | } 13 | ``` 14 | 15 | 16 | ## Transactional SparkProxyContract Common Methods 17 | 18 | ### Proxy Deploy 19 | 20 | ```rust 21 | pub async fn deploy(target: ContractId, owner: WalletUnlocked) -> anyhow::Result 22 | ``` 23 | 24 | Deploys proxy with market target. 25 | 26 | `target` The SparkMarketContract instance 27 | `owner` The Wallet object 28 | 29 | Returns a SparkProxyContract instance 30 | 31 | 32 | ### Set Proxy Target 33 | 34 | ```rust 35 | pub async fn set_proxy_target( 36 | &self, 37 | new_target: ContractId, 38 | ) -> anyhow::Result> 39 | ``` 40 | 41 | Sets a new target(implementation) for proxy. Only proxy owner can call. 42 | 43 | `self` The SparkProxyContract instance 44 | `new_target` The SparkMarketContract instance 45 | 46 | Returns a call result 47 | 48 | 49 | ### Get Proxy Target 50 | 51 | ```rust 52 | async fn proxy_target(&self) -> anyhow::Result>> 53 | ``` 54 | 55 | Withdraws assets from market caller account. 56 | 57 | `self` The SparkProxyContract instance 58 | 59 | Returns an optional market contract id 60 | 61 | 62 | ### Set Proxy Owner 63 | 64 | ```rust 65 | pub async fn set_proxy_owner( 66 | &self, 67 | new_proxy_owner: State, 68 | ) -> anyhow::Result> 69 | ``` 70 | 71 | Sets a new proxy owner. Ony proxy owner can call. 72 | 73 | `self` The SparkProxyContract instance 74 | `new_proxy_owner` A new proxy owner 75 | 76 | Returns a call result 77 | 78 | 79 | ### Get Proxy Owner 80 | 81 | ```rust 82 | pub async fn proxy_owner(&self) -> anyhow::Result> 83 | ``` 84 | 85 | Retrieves a proxy owner. 86 | 87 | `self` The SparkProxyContract instance 88 | 89 | Returns a proxy owner 90 | -------------------------------------------------------------------------------- /spark-proxy-sdk/spark-proxy/out/release/spark-proxy-storage_slots.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "key": "7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd55", 4 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 5 | }, 6 | { 7 | "key": "7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd56", 8 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 9 | }, 10 | { 11 | "key": "bb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea754", 12 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 13 | }, 14 | { 15 | "key": "bb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea755", 16 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 17 | } 18 | ] -------------------------------------------------------------------------------- /spark-proxy-sdk/spark-proxy/out/release/spark-proxy.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compolabs/orderbook-contract/bfbc7d99d42f6630b98cd7794641f64d2ac970f2/spark-proxy-sdk/spark-proxy/out/release/spark-proxy.bin -------------------------------------------------------------------------------- /spark-proxy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "spark-proxy" 3 | version = { workspace = true } 4 | authors = { workspace = true } 5 | edition = { workspace = true } 6 | license = { workspace = true } 7 | rust-version = { workspace = true } 8 | 9 | [dependencies] 10 | anyhow = { workspace = true } 11 | fuels = { workspace = true } 12 | spark-proxy-sdk = { path = "../spark-proxy-sdk" } 13 | tokio = { workspace = true } 14 | 15 | [[test]] 16 | harness = true 17 | name = "integration_tests" 18 | path = "tests/harness.rs" 19 | -------------------------------------------------------------------------------- /spark-proxy/Forc.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | authors = ["ComposabilityLabs"] 3 | entry = "main.sw" 4 | license = "Apache-2.0" 5 | name = "spark-proxy" 6 | 7 | [dependencies] 8 | standards = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.6.1" } 9 | sway_libs = { git = "https://github.com/FuelLabs/sway-libs", tag = "v0.24.0" } 10 | -------------------------------------------------------------------------------- /spark-proxy/out/release/spark-proxy-storage_slots.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "key": "7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd55", 4 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 5 | }, 6 | { 7 | "key": "7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd56", 8 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 9 | }, 10 | { 11 | "key": "bb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea754", 12 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 13 | }, 14 | { 15 | "key": "bb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea755", 16 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 17 | } 18 | ] -------------------------------------------------------------------------------- /spark-proxy/out/release/spark-proxy.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compolabs/orderbook-contract/bfbc7d99d42f6630b98cd7794641f64d2ac970f2/spark-proxy/out/release/spark-proxy.bin -------------------------------------------------------------------------------- /spark-proxy/src/main.sw: -------------------------------------------------------------------------------- 1 | contract; 2 | 3 | use sway_libs::upgradability::{ 4 | _proxy_owner, 5 | _proxy_target, 6 | _set_proxy_owner, 7 | _set_proxy_target, 8 | only_proxy_owner, 9 | }; 10 | use standards::{src14::{SRC14, SRC14Extension}, src5::State}; 11 | use std::execution::run_external; 12 | 13 | storage { 14 | SRC14 { 15 | /// The [ContractId] of the target contract. 16 | /// 17 | /// # Additional Information 18 | /// 19 | /// `target` is stored at sha256("storage_SRC14_0") 20 | target in 0x7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd55: Option = None, 21 | /// The [State] of the proxy owner. 22 | /// 23 | /// # Additional Information 24 | /// 25 | /// `proxy_owner` is stored at sha256("storage_SRC14_1") 26 | proxy_owner in 0xbb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea754: State = State::Uninitialized, 27 | }, 28 | } 29 | 30 | abi ProxyOwner { 31 | #[storage(read, write)] 32 | fn set_proxy_owner(new_proxy_owner: State); 33 | } 34 | 35 | impl SRC14 for Contract { 36 | #[storage(read, write)] 37 | fn set_proxy_target(new_target: ContractId) { 38 | only_proxy_owner(); 39 | _set_proxy_target(new_target); 40 | } 41 | 42 | #[storage(read)] 43 | fn proxy_target() -> Option { 44 | _proxy_target() 45 | } 46 | } 47 | 48 | impl ProxyOwner for Contract { 49 | #[storage(read, write)] 50 | fn set_proxy_owner(new_proxy_owner: State) { 51 | // checking twice because don't control sway_libs 52 | only_proxy_owner(); 53 | _set_proxy_owner(new_proxy_owner); 54 | } 55 | } 56 | 57 | impl SRC14Extension for Contract { 58 | #[storage(read)] 59 | fn proxy_owner() -> State { 60 | _proxy_owner() 61 | } 62 | } 63 | 64 | #[fallback] 65 | #[storage(read)] 66 | fn fallback() { 67 | // pass through any other method call to the target 68 | run_external(_proxy_target().unwrap()) 69 | } 70 | -------------------------------------------------------------------------------- /spark-proxy/tests/functions/core/mod.rs: -------------------------------------------------------------------------------- 1 | mod set_proxy_owner; 2 | mod set_proxy_target; 3 | -------------------------------------------------------------------------------- /spark-proxy/tests/functions/core/set_proxy_owner.rs: -------------------------------------------------------------------------------- 1 | use crate::setup::setup_proxy; 2 | use spark_proxy_sdk::State; 3 | 4 | mod success { 5 | 6 | use super::*; 7 | 8 | #[tokio::test] 9 | async fn set_proxy_owner() -> anyhow::Result<()> { 10 | let (contract, _, user) = setup_proxy().await?; 11 | 12 | contract 13 | .set_proxy_owner(State::Initialized(user.identity())) 14 | .await?; 15 | 16 | assert_eq!( 17 | contract.proxy_owner().await?.value, 18 | State::Initialized(user.identity()) 19 | ); 20 | 21 | Ok(()) 22 | } 23 | } 24 | 25 | mod revert { 26 | 27 | use super::*; 28 | 29 | #[tokio::test] 30 | #[should_panic(expected = "NotOwner")] 31 | async fn reverts_when_non_owner() { 32 | let (contract, _, user) = setup_proxy().await.unwrap(); 33 | 34 | // Attempt to set the owner with a non-owner user 35 | contract 36 | .with_account(&user.wallet) 37 | .set_proxy_owner(State::Initialized(user.identity())) 38 | .await 39 | .unwrap(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /spark-proxy/tests/functions/core/set_proxy_target.rs: -------------------------------------------------------------------------------- 1 | use crate::setup::setup_proxy; 2 | 3 | mod success { 4 | 5 | use super::*; 6 | 7 | #[tokio::test] 8 | async fn set_proxy_target() -> anyhow::Result<()> { 9 | let (contract, _, _) = setup_proxy().await?; 10 | 11 | contract 12 | .set_proxy_target(contract.contract_id().into()) 13 | .await?; 14 | 15 | assert_eq!( 16 | contract.proxy_target().await?.value, 17 | Some(contract.contract_id().into()) 18 | ); 19 | 20 | Ok(()) 21 | } 22 | } 23 | 24 | mod revert { 25 | 26 | use super::*; 27 | 28 | #[tokio::test] 29 | #[should_panic(expected = "NotOwner")] 30 | async fn reverts_when_non_owner() { 31 | let (contract, _, user) = setup_proxy().await.unwrap(); 32 | 33 | // Attempt to set the target with a non-owner user 34 | contract 35 | .with_account(&user.wallet) 36 | .set_proxy_target(contract.contract_id().into()) 37 | .await 38 | .unwrap(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /spark-proxy/tests/functions/info/mod.rs: -------------------------------------------------------------------------------- 1 | mod proxy_owner; 2 | mod proxy_target; 3 | -------------------------------------------------------------------------------- /spark-proxy/tests/functions/info/proxy_owner.rs: -------------------------------------------------------------------------------- 1 | mod success { 2 | 3 | use crate::setup::setup_proxy; 4 | use spark_proxy_sdk::State; 5 | 6 | #[tokio::test] 7 | async fn returns_proxy_target() -> anyhow::Result<()> { 8 | let (contract, owner, _) = setup_proxy().await?; 9 | 10 | assert_eq!( 11 | contract.proxy_owner().await?.value, 12 | State::Initialized(owner.identity()) 13 | ); 14 | 15 | Ok(()) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /spark-proxy/tests/functions/info/proxy_target.rs: -------------------------------------------------------------------------------- 1 | mod success { 2 | 3 | use fuels::types::ContractId; 4 | 5 | use crate::setup::setup_proxy; 6 | 7 | #[tokio::test] 8 | async fn returns_proxy_target() -> anyhow::Result<()> { 9 | let (contract, _, _) = setup_proxy().await?; 10 | 11 | assert_eq!( 12 | contract.proxy_target().await?.value, 13 | Some(ContractId::zeroed().into()) 14 | ); 15 | 16 | Ok(()) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /spark-proxy/tests/functions/mod.rs: -------------------------------------------------------------------------------- 1 | mod core; 2 | mod info; 3 | -------------------------------------------------------------------------------- /spark-proxy/tests/harness.rs: -------------------------------------------------------------------------------- 1 | mod functions; 2 | mod setup; 3 | -------------------------------------------------------------------------------- /spark-proxy/tests/setup/mod.rs: -------------------------------------------------------------------------------- 1 | use fuels::{ 2 | prelude::{launch_custom_provider_and_get_wallets, Address, WalletUnlocked, WalletsConfig}, 3 | types::{ContractId, Identity}, 4 | }; 5 | 6 | use spark_proxy_sdk::SparkProxyContract; 7 | 8 | pub(crate) struct User { 9 | pub(crate) wallet: WalletUnlocked, 10 | } 11 | 12 | impl User { 13 | pub(crate) fn address(&self) -> Address { 14 | Address::from(self.wallet.address()) 15 | } 16 | 17 | pub(crate) fn identity(&self) -> Identity { 18 | Identity::Address(self.address()) 19 | } 20 | } 21 | 22 | pub(crate) async fn setup_proxy() -> anyhow::Result<(SparkProxyContract, User, User)> { 23 | let number_of_wallets = 4; 24 | let num_coins = 1; 25 | let coin_amount = 1_000_000_000_000; 26 | 27 | let config = WalletsConfig::new(Some(number_of_wallets), Some(num_coins), Some(coin_amount)); 28 | 29 | let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?; 30 | let owner = wallets.pop().unwrap(); 31 | let user = wallets.pop().unwrap(); 32 | 33 | let contract = SparkProxyContract::deploy(ContractId::zeroed(), owner.clone()).await?; 34 | 35 | let owner = User { wallet: owner }; 36 | let user = User { wallet: user }; 37 | 38 | Ok((contract, owner, user)) 39 | } 40 | -------------------------------------------------------------------------------- /spark-registry-sdk/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "spark-registry-sdk" 3 | version = { workspace = true } 4 | authors = { workspace = true } 5 | edition = { workspace = true } 6 | license = { workspace = true } 7 | rust-version = { workspace = true } 8 | description = "SDK for interacting with the Spark Registry" 9 | repository = "https://github.com/compolabs/orderbook-contract" 10 | readme = "README.md" 11 | keywords = ["fuel", "sdk", "spark", "registry", "orderbook"] 12 | include = [ 13 | "spark-proxy/out/release/*", 14 | "spark-registry/out/release/*", 15 | "src/lib.rs", 16 | "Cargo.toml", 17 | "README.md" 18 | ] 19 | 20 | [dependencies] 21 | anyhow = { workspace = true } 22 | fuels = { workspace = true } 23 | rand = "0.8.5" 24 | tokio = { workspace = true } 25 | -------------------------------------------------------------------------------- /spark-registry-sdk/README.md: -------------------------------------------------------------------------------- 1 | # Spark MarketRegistry Contract Rust SDK 2 | 3 | The Spark MarketRegistry Contract SDK is designed for interacting with the Spark MarketRegistry contract. 4 | There are a set of transactional methods such as `deploy`, register/unregister owner methods and `markets` getter method. Given below a detailed explanation of every contract method. 5 | 6 | ## SparkRegistryContract Type 7 | 8 | ```rust 9 | pub struct SparkRegistryContract { 10 | instance: MarketRegistry, 11 | } 12 | ``` 13 | 14 | ## Transactional SparkMarketContract Owner Methods 15 | 16 | ### Contract Deployment 17 | 18 | ```rust 19 | pub async fn deploy(owner: WalletUnlocked) -> anyhow::Result 20 | ``` 21 | 22 | Deploys a new market registry contract with given owner. 23 | 24 | `owner` The owner of the market registry contract that manages market list. 25 | 26 | Returns a new instance of SparkRegistryContract type. 27 | 28 | 29 | ### Register new Market 30 | 31 | ```rust 32 | pub async fn register_market(&self, market: ContractId) -> anyhow::Result> 33 | ``` 34 | 35 | Registers a new market by owner. 36 | 37 | `self` The SparkRegistryContract instance. 38 | `market` The market contract id. 39 | 40 | Returns a call result 41 | 42 | 43 | ### Unregister Market 44 | 45 | ```rust 46 | pub async fn unregister_market(&self, market: ContractId) -> anyhow::Result> 47 | ``` 48 | 49 | Unregisters a market by owner. 50 | 51 | `self` The SparkRegistryContract instance. 52 | `market` The market contract id. 53 | 54 | Returns a call result 55 | 56 | 57 | ### Transfer Ownership 58 | 59 | ```rust 60 | pub async fn transfer_ownership( 61 | &self, 62 | new_owner: Identity, 63 | ) -> anyhow::Result> 64 | ``` 65 | 66 | Transfers ownership of regsitry. 67 | 68 | `self` The SparkRegistryContract instance. 69 | `new_owner` The new owner identity. 70 | 71 | Returns a call result 72 | 73 | 74 | 75 | ## SparkRegistryContract Getter Methods 76 | 77 | ### Markets Info 78 | 79 | ```rust 80 | pub async fn markets( 81 | &self, 82 | assets: Vec<(AssetId, AssetId)>, 83 | ) -> anyhow::Result) 84 | ``` 85 | 86 | Retrieves user account inforamtion. 87 | 88 | `self` The SparkRegistryContract instance. 89 | `assets` The asset pair array [(base_asst_id, quote_asset_id)]. 90 | 91 | Returns an asset pair and optional market contract id array 92 | 93 | ### Owner 94 | 95 | ```rust 96 | pub async fn owner(&self) -> anyhow::Result> 97 | ``` 98 | 99 | Retrieves contract owner. 100 | `self` The SparkRegistryContract instance. 101 | 102 | 103 | Returns a State of contract owner 104 | 105 | 106 | ### Config 107 | 108 | ```rust 109 | pub fn config(&self) -> (Option, u32); 110 | ``` 111 | 112 | Retrieves contract configurables. 113 | `self` The SparkRegistryContract instance. 114 | 115 | Returns an Option of owner identity and contract version 116 | -------------------------------------------------------------------------------- /spark-registry-sdk/spark-proxy/out/release/spark-proxy-storage_slots.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "key": "7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd55", 4 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 5 | }, 6 | { 7 | "key": "7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd56", 8 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 9 | }, 10 | { 11 | "key": "bb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea754", 12 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 13 | }, 14 | { 15 | "key": "bb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea755", 16 | "value": "0000000000000000000000000000000000000000000000000000000000000000" 17 | } 18 | ] -------------------------------------------------------------------------------- /spark-registry-sdk/spark-proxy/out/release/spark-proxy.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compolabs/orderbook-contract/bfbc7d99d42f6630b98cd7794641f64d2ac970f2/spark-registry-sdk/spark-proxy/out/release/spark-proxy.bin -------------------------------------------------------------------------------- /spark-registry-sdk/spark-registry/out/release/spark-registry-storage_slots.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /spark-registry-sdk/spark-registry/out/release/spark-registry.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compolabs/orderbook-contract/bfbc7d99d42f6630b98cd7794641f64d2ac970f2/spark-registry-sdk/spark-registry/out/release/spark-registry.bin -------------------------------------------------------------------------------- /spark-registry/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "spark-registry" 3 | version = { workspace = true } 4 | authors = { workspace = true } 5 | edition = { workspace = true } 6 | license = { workspace = true } 7 | rust-version = { workspace = true } 8 | 9 | [dependencies] 10 | anyhow = { workspace = true } 11 | fuels = { workspace = true } 12 | spark-market-sdk = { path = "../spark-market-sdk" } 13 | spark-registry-sdk = { path = "../spark-registry-sdk" } 14 | tokio = { workspace = true } 15 | 16 | [[test]] 17 | harness = true 18 | name = "integration_tests" 19 | path = "tests/harness.rs" 20 | -------------------------------------------------------------------------------- /spark-registry/Forc.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | authors = ["ComposabilityLabs"] 3 | entry = "main.sw" 4 | license = "Apache-2.0" 5 | name = "spark-registry" 6 | 7 | [dependencies] 8 | standards = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.6.1" } 9 | sway_libs = { git = "https://github.com/FuelLabs/sway-libs", tag = "v0.24.0" } 10 | -------------------------------------------------------------------------------- /spark-registry/out/release/spark-registry-storage_slots.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /spark-registry/out/release/spark-registry.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compolabs/orderbook-contract/bfbc7d99d42f6630b98cd7794641f64d2ac970f2/spark-registry/out/release/spark-registry.bin -------------------------------------------------------------------------------- /spark-registry/src/errors.sw: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | pub enum MarketRegistryError { 4 | MarketAlreadyRegistered: (), 5 | MarketNotRegistered: (), 6 | } 7 | -------------------------------------------------------------------------------- /spark-registry/src/events.sw: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | pub struct MarketRegisterEvent { 4 | pub base: AssetId, 5 | pub quote: AssetId, 6 | pub market: ContractId, 7 | } 8 | 9 | pub struct MarketUnregisterEvent { 10 | pub base: AssetId, 11 | pub quote: AssetId, 12 | pub market: ContractId, 13 | } 14 | -------------------------------------------------------------------------------- /spark-registry/tests/functions/core/initialize_ownership.rs: -------------------------------------------------------------------------------- 1 | use crate::setup::setup; 2 | use fuels::types::Identity; 3 | 4 | mod revert { 5 | 6 | use super::*; 7 | 8 | #[tokio::test] 9 | #[should_panic(expected = "CannotReinitialized")] 10 | async fn reverts_when_already_initialized() { 11 | let (contract, _, user) = setup().await.unwrap(); 12 | 13 | let new_owner: Identity = user.wallet.address().into(); 14 | contract 15 | .with_account(&user.wallet) 16 | .initialize_ownership(new_owner) 17 | .await 18 | .unwrap(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /spark-registry/tests/functions/core/mod.rs: -------------------------------------------------------------------------------- 1 | mod initialize_ownership; 2 | mod register_market; 3 | mod transfer_ownership; 4 | mod unregister_market; 5 | -------------------------------------------------------------------------------- /spark-registry/tests/functions/core/register_market.rs: -------------------------------------------------------------------------------- 1 | mod success { 2 | 3 | use crate::setup::{random_asset_id, setup}; 4 | use fuels::types::ContractId; 5 | use spark_market_sdk::SparkMarketContract; 6 | 7 | #[tokio::test] 8 | async fn succeeds_for_admin() -> anyhow::Result<()> { 9 | let (contract, admin, _) = setup().await.unwrap(); 10 | let base_asset = random_asset_id(20); 11 | let quote_asset = random_asset_id(21); 12 | 13 | let market = SparkMarketContract::deploy( 14 | base_asset, 15 | 1, 16 | quote_asset, 17 | 1, 18 | admin.wallet.clone(), 19 | 9, 20 | 0xFAFBFC, 21 | ) 22 | .await?; 23 | 24 | let contract_id: ContractId = market.contract_id().into(); 25 | contract 26 | .with_account(&admin.wallet) 27 | .register_market(contract_id) 28 | .await?; 29 | assert_eq!( 30 | contract 31 | .markets(vec![(base_asset, quote_asset)]) 32 | .await? 33 | .value, 34 | vec![(base_asset, quote_asset, Some(contract_id))] 35 | ); 36 | Ok(()) 37 | } 38 | } 39 | 40 | mod revert { 41 | 42 | use crate::setup::{random_asset_id, setup}; 43 | use fuels::types::ContractId; 44 | use spark_market_sdk::SparkMarketContract; 45 | 46 | #[tokio::test] 47 | #[should_panic(expected = "NotOwner")] 48 | async fn reverts_when_non_owner() { 49 | let (contract, _, user) = setup().await.unwrap(); 50 | let base_asset = random_asset_id(20); 51 | let quote_asset = random_asset_id(21); 52 | 53 | let market = SparkMarketContract::deploy( 54 | base_asset, 55 | 1, 56 | quote_asset, 57 | 1, 58 | user.wallet.clone(), 59 | 9, 60 | 0xFAFBFC, 61 | ) 62 | .await 63 | .unwrap(); 64 | 65 | let contract_id: ContractId = market.contract_id().into(); 66 | 67 | // Reverts 68 | contract 69 | .with_account(&user.wallet) 70 | .register_market(contract_id) 71 | .await 72 | .unwrap(); 73 | } 74 | 75 | #[tokio::test] 76 | #[should_panic(expected = "MarketAlreadyRegistered")] 77 | async fn reverts_when_registered() { 78 | let (contract, admin, _) = setup().await.unwrap(); 79 | 80 | let base_asset = random_asset_id(20); 81 | let quote_asset = random_asset_id(21); 82 | 83 | let market = SparkMarketContract::deploy( 84 | base_asset, 85 | 1, 86 | quote_asset, 87 | 1, 88 | admin.wallet.clone(), 89 | 9, 90 | 0xFAFBFC, 91 | ) 92 | .await 93 | .unwrap(); 94 | 95 | let contract_id: ContractId = market.contract_id().into(); 96 | 97 | contract 98 | .with_account(&admin.wallet) 99 | .register_market(contract_id) 100 | .await 101 | .unwrap(); 102 | 103 | // Reverts 104 | contract 105 | .with_account(&admin.wallet) 106 | .register_market(contract_id) 107 | .await 108 | .unwrap(); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /spark-registry/tests/functions/core/transfer_ownership.rs: -------------------------------------------------------------------------------- 1 | use crate::setup::{random_asset_id, setup}; 2 | use fuels::types::ContractId; 3 | use fuels::types::Identity; 4 | 5 | mod success { 6 | 7 | use super::*; 8 | use spark_market_sdk::SparkMarketContract; 9 | use spark_registry_sdk::{OwnershipTransferred, State}; 10 | 11 | #[tokio::test] 12 | async fn transfer_ownership() -> anyhow::Result<()> { 13 | let (contract, owner, user) = setup().await.unwrap(); 14 | 15 | let new_owner: Identity = user.wallet.address().into(); 16 | let response = contract.transfer_ownership(new_owner).await?; 17 | 18 | // Log should be emitted when fee is changed 19 | let log = response 20 | .decode_logs_with_type::() 21 | .unwrap(); 22 | let event = log.first().unwrap(); 23 | assert_eq!( 24 | *event, 25 | OwnershipTransferred { 26 | new_owner: new_owner, 27 | previous_owner: owner.wallet.address().into(), 28 | } 29 | ); 30 | assert_eq!(contract.owner().await?.value, State::Initialized(new_owner)); 31 | 32 | let base_asset = random_asset_id(20); 33 | let quote_asset = random_asset_id(21); 34 | 35 | let market = SparkMarketContract::deploy( 36 | base_asset, 37 | 1, 38 | quote_asset, 39 | 1, 40 | owner.wallet.clone(), 41 | 9, 42 | 0xFAFBFC, 43 | ) 44 | .await?; 45 | 46 | let contract_id: ContractId = market.contract_id().into(); 47 | contract 48 | .with_account(&user.wallet) 49 | .register_market(contract_id) 50 | .await?; 51 | 52 | Ok(()) 53 | } 54 | } 55 | 56 | mod revert { 57 | 58 | use super::*; 59 | 60 | #[tokio::test] 61 | #[should_panic(expected = "NotOwner")] 62 | async fn reverts_when_non_owner() { 63 | let (contract, _, user) = setup().await.unwrap(); 64 | 65 | let new_owner: Identity = user.wallet.address().into(); 66 | contract 67 | .with_account(&user.wallet) 68 | .transfer_ownership(new_owner) 69 | .await 70 | .unwrap(); 71 | } 72 | 73 | #[tokio::test] 74 | #[should_panic(expected = "NotOwner")] 75 | async fn reverts_when_already_transfered() { 76 | let (contract, owner, user) = setup().await.unwrap(); 77 | 78 | let new_owner: Identity = user.wallet.address().into(); 79 | contract.transfer_ownership(new_owner).await.unwrap(); 80 | 81 | contract 82 | .with_account(&owner.wallet) 83 | .transfer_ownership(new_owner) 84 | .await 85 | .unwrap(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /spark-registry/tests/functions/core/unregister_market.rs: -------------------------------------------------------------------------------- 1 | mod success { 2 | 3 | use crate::setup::{random_asset_id, setup}; 4 | use fuels::types::ContractId; 5 | use spark_market_sdk::SparkMarketContract; 6 | 7 | #[tokio::test] 8 | async fn succeeds_for_admin() -> anyhow::Result<()> { 9 | let (contract, admin, _) = setup().await.unwrap(); 10 | let base_asset = random_asset_id(20); 11 | let quote_asset = random_asset_id(21); 12 | 13 | let market = SparkMarketContract::deploy( 14 | base_asset, 15 | 1, 16 | quote_asset, 17 | 1, 18 | admin.wallet.clone(), 19 | 9, 20 | 0xFAFBFC, 21 | ) 22 | .await?; 23 | 24 | let contract_id: ContractId = market.contract_id().into(); 25 | 26 | contract 27 | .with_account(&admin.wallet) 28 | .register_market(contract_id) 29 | .await 30 | .unwrap(); 31 | contract 32 | .with_account(&admin.wallet) 33 | .unregister_market(contract_id) 34 | .await 35 | .unwrap(); 36 | assert_eq!( 37 | contract 38 | .markets(vec![(base_asset, quote_asset)]) 39 | .await? 40 | .value, 41 | vec![(base_asset, quote_asset, None)] 42 | ); 43 | Ok(()) 44 | } 45 | } 46 | 47 | mod revert { 48 | 49 | use crate::setup::{random_asset_id, setup}; 50 | use fuels::types::ContractId; 51 | use spark_market_sdk::SparkMarketContract; 52 | 53 | #[tokio::test] 54 | #[should_panic(expected = "NotOwner")] 55 | async fn reverts_when_non_owner() { 56 | let (contract, admin, user) = setup().await.unwrap(); 57 | let base_asset = random_asset_id(20); 58 | let quote_asset = random_asset_id(21); 59 | 60 | let market = SparkMarketContract::deploy( 61 | base_asset, 62 | 1, 63 | quote_asset, 64 | 1, 65 | admin.wallet.clone(), 66 | 9, 67 | 0xFAFBFC, 68 | ) 69 | .await 70 | .unwrap(); 71 | 72 | let contract_id: ContractId = market.contract_id().into(); 73 | contract 74 | .with_account(&admin.wallet) 75 | .register_market(contract_id) 76 | .await 77 | .unwrap(); 78 | // Reverts 79 | contract 80 | .with_account(&user.wallet) 81 | .unregister_market(contract_id) 82 | .await 83 | .unwrap(); 84 | } 85 | 86 | #[tokio::test] 87 | #[should_panic(expected = "MarketNotRegistered")] 88 | async fn reverts_when_not_registered() { 89 | let (contract, admin, _) = setup().await.unwrap(); 90 | let base_asset = random_asset_id(20); 91 | let quote_asset = random_asset_id(21); 92 | 93 | let market = SparkMarketContract::deploy( 94 | base_asset, 95 | 1, 96 | quote_asset, 97 | 1, 98 | admin.wallet.clone(), 99 | 9, 100 | 0xFAFBFC, 101 | ) 102 | .await 103 | .unwrap(); 104 | 105 | let contract_id: ContractId = market.contract_id().into(); 106 | // Reverts 107 | contract 108 | .with_account(&admin.wallet) 109 | .unregister_market(contract_id) 110 | .await 111 | .unwrap(); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /spark-registry/tests/functions/info/config.rs: -------------------------------------------------------------------------------- 1 | mod success { 2 | 3 | use crate::setup::setup; 4 | 5 | #[tokio::test] 6 | async fn returns_config() -> anyhow::Result<()> { 7 | let (contract, owner, _) = setup().await.unwrap(); 8 | assert_eq!( 9 | contract.config().await?.value, 10 | (Some(owner.address().into()), 0xFAFBFC), 11 | ); 12 | Ok(()) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /spark-registry/tests/functions/info/markets.rs: -------------------------------------------------------------------------------- 1 | mod success { 2 | 3 | use crate::setup::{random_asset_id, setup}; 4 | 5 | #[tokio::test] 6 | async fn returns_id_none() -> anyhow::Result<()> { 7 | let (contract, _, _) = setup().await.unwrap(); 8 | let asset_id = random_asset_id(20); 9 | assert_eq!( 10 | contract.markets(vec![(asset_id, asset_id)]).await?.value, 11 | vec![(asset_id, asset_id, None)] 12 | ); 13 | Ok(()) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /spark-registry/tests/functions/info/mod.rs: -------------------------------------------------------------------------------- 1 | mod config; 2 | mod markets; 3 | -------------------------------------------------------------------------------- /spark-registry/tests/functions/mod.rs: -------------------------------------------------------------------------------- 1 | mod core; 2 | mod info; 3 | -------------------------------------------------------------------------------- /spark-registry/tests/harness.rs: -------------------------------------------------------------------------------- 1 | mod functions; 2 | mod setup; 3 | -------------------------------------------------------------------------------- /spark-registry/tests/setup/mod.rs: -------------------------------------------------------------------------------- 1 | use fuels::prelude::{ 2 | launch_custom_provider_and_get_wallets, Address, AssetConfig, AssetId, WalletUnlocked, 3 | WalletsConfig, 4 | }; 5 | use spark_registry_sdk::SparkRegistryContract; 6 | 7 | pub(crate) struct User { 8 | pub(crate) wallet: WalletUnlocked, 9 | } 10 | 11 | impl User { 12 | pub(crate) fn address(&self) -> Address { 13 | Address::from(self.wallet.address()) 14 | } 15 | } 16 | 17 | pub(crate) fn random_asset_id(random: u8) -> AssetId { 18 | AssetId::new([random; 32]) 19 | } 20 | 21 | pub(crate) async fn setup() -> anyhow::Result<(SparkRegistryContract, User, User)> { 22 | let number_of_wallets = 2; 23 | let coins_per_wallet = 1; 24 | let amount_per_coin = 100_000_000; 25 | 26 | let base_asset_id = AssetId::new([0; 32]); 27 | let quote_asset_id = AssetId::new([1; 32]); 28 | let random_asset_id = AssetId::new([2; 32]); 29 | 30 | let ids = vec![base_asset_id, quote_asset_id, random_asset_id]; 31 | let mut assets: Vec = Vec::with_capacity(3); 32 | 33 | for id in ids { 34 | assets.push(AssetConfig { 35 | id, 36 | num_coins: coins_per_wallet, 37 | coin_amount: amount_per_coin, 38 | }); 39 | } 40 | let config = WalletsConfig::new_multiple_assets(number_of_wallets, assets); 41 | 42 | let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?; 43 | let owner = wallets.pop().unwrap(); 44 | let user = wallets.pop().unwrap(); 45 | 46 | let contract = SparkRegistryContract::deploy(owner.clone(), 0xFAFBFC).await?; 47 | 48 | let owner = User { wallet: owner }; 49 | let non_owner = User { wallet: user }; 50 | 51 | Ok((contract, owner, non_owner)) 52 | } 53 | --------------------------------------------------------------------------------