spark_orderbook_sdk/
lib.rs

1use fuels::{
2    prelude::{
3        abigen, AssetId, Bech32ContractId, Contract, ContractId, LoadConfiguration,
4        StorageConfiguration, TxPolicies, WalletUnlocked,
5    },
6    programs::responses::CallResponse,
7    types::{Address, Bytes32},
8};
9use rand::Rng;
10use std::path::PathBuf;
11
12abigen!(Contract(
13    name = "Orderbook",
14    abi = "orderbook-contract/out/release/orderbook-contract-abi.json"
15));
16
17const ORDERBOOK_CONTRACT_BINARY_PATH: &str =
18    "orderbook-contract/out/release/orderbook-contract.bin";
19const ORDERBOOK_CONTRACT_STORAGE_PATH: &str =
20    "orderbook-contract/out/release/orderbook-contract-storage_slots.json";
21
22pub struct OrderbookContract {
23    instance: Orderbook<WalletUnlocked>,
24}
25
26impl OrderbookContract {
27    pub async fn deploy(owner: WalletUnlocked, version: u32) -> anyhow::Result<Self> {
28        let mut rng = rand::thread_rng();
29        let salt = rng.gen::<[u8; 32]>();
30
31        let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
32        let storage_configuration = StorageConfiguration::default()
33            .add_slot_overrides_from_file(root.join(ORDERBOOK_CONTRACT_STORAGE_PATH));
34
35        let configurables = OrderbookConfigurables::default()
36            .with_OWNER(owner.address().into())
37            .unwrap()
38            .with_VERSION(version)
39            .unwrap();
40
41        let contract_configuration = LoadConfiguration::default()
42            .with_storage_configuration(storage_configuration?)
43            .with_configurables(configurables);
44
45        let contract_id = Contract::load_from(
46            root.join(ORDERBOOK_CONTRACT_BINARY_PATH),
47            contract_configuration,
48        )?
49        .with_salt(salt)
50        .deploy(&owner, TxPolicies::default())
51        .await?;
52
53        let orderbook = Orderbook::new(contract_id.clone(), owner.clone());
54
55        Ok(Self {
56            instance: orderbook,
57        })
58    }
59
60    pub async fn new(contract_id: ContractId, wallet: WalletUnlocked) -> Self {
61        let _self = Self {
62            instance: Orderbook::new(contract_id, wallet),
63        };
64        assert!(
65            _self.contract_version().await.unwrap() & 0xFF0000 == Self::sdk_version() & 0xFF0000,
66            "Orderbook contract version mismatch with SDK version"
67        );
68        _self
69    }
70
71    pub async fn with_account(&self, account: &WalletUnlocked) -> anyhow::Result<Self> {
72        Ok(Self {
73            instance: self.instance.clone().with_account(account.clone()),
74        })
75    }
76
77    pub fn id(&self) -> Bytes32 {
78        self.instance.contract_id().hash
79    }
80
81    pub fn contract_id(&self) -> &Bech32ContractId {
82        self.instance.contract_id()
83    }
84
85    pub async fn contract_version(&self) -> anyhow::Result<u32> {
86        let (_, version) = self.config().await?.value;
87        Ok(version)
88    }
89
90    pub async fn contract_str_version(&self) -> anyhow::Result<String> {
91        let version = self.contract_version().await?;
92        Ok(format!(
93            "{}.{}.{}",
94            (version & 0xFF0000) >> 16,
95            (version & 0xFF00) >> 8,
96            version & 0xFF
97        ))
98    }
99
100    pub fn sdk_version() -> u32 {
101        let s_version = Self::sdk_str_version();
102        // Converts "0.1.1" string version to 257u32 (0x000101)
103        let version = s_version.split('.').collect::<Vec<&str>>();
104        let len = version.len();
105        version
106            .iter()
107            .enumerate()
108            .map(|(i, &x)| x.parse::<u32>().unwrap() << (8 * (len - i - 1)))
109            .collect::<Vec<u32>>()
110            .iter()
111            .sum()
112    }
113
114    pub fn sdk_str_version() -> String {
115        env!("CARGO_PKG_VERSION").into()
116    }
117
118    pub async fn register_market(&self, market: ContractId) -> anyhow::Result<CallResponse<()>> {
119        Ok(self
120            .instance
121            .methods()
122            .register_market(market)
123            .with_contract_ids(&[market.into()])
124            .call()
125            .await?)
126    }
127
128    pub async fn unregister_market(&self, market: ContractId) -> anyhow::Result<CallResponse<()>> {
129        Ok(self
130            .instance
131            .methods()
132            .unregister_market(market)
133            .with_contract_ids(&[market.into()])
134            .call()
135            .await?)
136    }
137
138    pub async fn config(&self) -> anyhow::Result<CallResponse<(Address, u32)>> {
139        Ok(self.instance.methods().config().simulate().await?)
140    }
141
142    pub async fn markets(
143        &self,
144        assets: Vec<(AssetId, AssetId)>,
145    ) -> anyhow::Result<CallResponse<Vec<(AssetId, AssetId, Option<ContractId>)>>> {
146        Ok(self.instance.methods().markets(assets).simulate().await?)
147    }
148}