1use anyhow::Result;
2use fuels::{
3 prelude::*,
4 tx::StorageSlot,
5 types::{
6 ContractId,
7 Identity,
8 },
9};
10
11abigen!(
12 Contract(
13 name = "OrderBookRegistry",
14 abi = "artifacts/order-book-registry/order-book-registry-abi.json"
15 ),
16 Contract(
17 name = "OrderBookRegistryProxy",
18 abi = "artifacts/order-book-registry-proxy/order-book-registry-proxy-abi.json"
19 )
20);
21
22pub const ORDER_BOOK_REGISTER_BYTECODE: &[u8] =
23 include_bytes!("../artifacts/order-book-registry/order-book-registry.bin");
24pub const ORDER_BOOK_REGISTER_STORAGE: &[u8] = include_bytes!(
25 "../artifacts/order-book-registry/order-book-registry-storage_slots.json"
26);
27pub const ORDER_BOOK_REGISTER_PROXY_BYTECODE: &[u8] = include_bytes!(
28 "../artifacts/order-book-registry-proxy/order-book-registry-proxy.bin"
29);
30pub const ORDER_BOOK_REGISTER_PROXY_STORAGE: &[u8] = include_bytes!(
31 "../artifacts/order-book-registry-proxy/order-book-registry-proxy-storage_slots.json"
32);
33
34#[derive(Clone)]
37pub struct OrderBookRegistryDeployConfig {
38 pub registry_bytecode: Vec<u8>,
40 pub registry_storage_slots: Vec<StorageSlot>,
42 pub registry_configurables: OrderBookRegistryConfigurables,
44 pub registry_proxy_bytecode: Vec<u8>,
46 pub registry_proxy_storage_slots: Vec<StorageSlot>,
48 pub registry_proxy_config: OrderBookRegistryProxyConfigurables,
50 pub max_words_per_blob: usize,
52 pub proxy_owner: Option<Identity>,
54 pub registry_owner: Option<Identity>,
56 pub salt: Salt,
58}
59
60impl Default for OrderBookRegistryDeployConfig {
61 fn default() -> Self {
62 Self {
63 registry_bytecode: ORDER_BOOK_REGISTER_BYTECODE.to_vec(),
64 registry_storage_slots: serde_json::from_slice(ORDER_BOOK_REGISTER_STORAGE)
65 .unwrap(),
66 registry_configurables: OrderBookRegistryConfigurables::default(),
67 registry_proxy_bytecode: ORDER_BOOK_REGISTER_PROXY_BYTECODE.to_vec(),
68 registry_proxy_storage_slots: serde_json::from_slice(
69 ORDER_BOOK_REGISTER_PROXY_STORAGE,
70 )
71 .unwrap(),
72 registry_proxy_config: OrderBookRegistryProxyConfigurables::default(),
73 max_words_per_blob: 10_000,
74 proxy_owner: None,
75 registry_owner: None,
76 salt: Salt::default(),
77 }
78 }
79}
80
81#[derive(Clone)]
84pub struct OrderBookRegistryManager<W> {
85 pub registry_proxy: OrderBookRegistryProxy<W>,
87 pub registry: OrderBookRegistry<W>,
89 pub contract_id: ContractId,
91 pub deployer_wallet: W,
93}
94
95pub struct OrderBookBlob {
96 pub id: BlobId,
98 pub exists: bool,
100 pub blob: Blob,
102}
103
104impl<W> OrderBookRegistryManager<W>
105where
106 W: Account + Clone,
107{
108 pub fn new(deployer_wallet: W, contract_id: ContractId) -> Self {
109 let proxy = OrderBookRegistryProxy::new(contract_id, deployer_wallet.clone());
110 let registry = OrderBookRegistry::new(contract_id, deployer_wallet.clone());
111 Self {
112 registry_proxy: proxy,
113 registry,
114 contract_id,
115 deployer_wallet,
116 }
117 }
118
119 pub async fn register_blob(
120 deployer_wallet: &W,
121 config: &OrderBookRegistryDeployConfig,
122 ) -> Result<OrderBookBlob> {
123 let registry_owner = config
124 .registry_owner
125 .unwrap_or(Identity::Address(deployer_wallet.address()));
126 let configurables = config
127 .registry_configurables
128 .clone()
129 .with_INITIAL_OWNER(State::Initialized(registry_owner))?;
130 let blobs = Contract::regular(
131 config.registry_bytecode.clone(),
132 config.salt,
133 config.registry_storage_slots.clone(),
134 )
135 .with_configurables(configurables.clone())
136 .convert_to_loader(config.max_words_per_blob)?
137 .blobs()
138 .to_vec();
139 let blob = blobs[0].clone();
140 let blob_id = blob.id();
141 let blob_exists = deployer_wallet.try_provider()?.blob_exists(blob_id).await?;
142
143 Ok(OrderBookBlob {
144 id: blob_id,
145 exists: blob_exists,
146 blob: blob.clone(),
147 })
148 }
149
150 pub async fn deploy_register_blob(
151 deployer_wallet: &W,
152 config: &OrderBookRegistryDeployConfig,
153 ) -> Result<BlobId> {
154 let register_blob = Self::register_blob(deployer_wallet, config).await?;
155
156 if !register_blob.exists {
157 let mut builder =
158 BlobTransactionBuilder::default().with_blob(register_blob.blob);
159 deployer_wallet.adjust_for_fee(&mut builder, 0).await?;
160 deployer_wallet.add_witnesses(&mut builder)?;
161 let tx = builder.build(&deployer_wallet.try_provider()?).await?;
162
163 deployer_wallet
164 .try_provider()?
165 .send_transaction_and_await_commit(tx)
166 .await?
167 .check(None)?;
168 }
169
170 Ok(register_blob.id)
171 }
172
173 pub async fn deploy_register_proxy(
174 deployer_wallet: &W,
175 registry_blob_id: &BlobId,
176 config: &OrderBookRegistryDeployConfig,
177 ) -> Result<(OrderBookRegistryProxy<W>, bool)> {
178 let proxy_owner = config
179 .proxy_owner
180 .unwrap_or(Identity::Address(deployer_wallet.address()));
181 let configurables = config
182 .registry_proxy_config
183 .clone()
184 .with_INITIAL_OWNER(State::Initialized(proxy_owner))?
185 .with_INITIAL_TARGET(ContractId::new(*registry_blob_id))?;
186 let contract = Contract::regular(
187 config.registry_proxy_bytecode.clone(),
188 config.salt,
189 config.registry_proxy_storage_slots.clone(),
190 )
191 .with_configurables(configurables);
192 let contract_id = contract.contract_id();
193 let already_deployed = deployer_wallet
194 .try_provider()?
195 .contract_exists(&contract_id)
196 .await?;
197 let proxy = OrderBookRegistryProxy::new(contract_id, deployer_wallet.clone());
198
199 if !already_deployed {
200 contract
201 .deploy(deployer_wallet, TxPolicies::default())
202 .await?;
203 }
204 let requires_initialization = !already_deployed;
205
206 Ok((proxy, requires_initialization))
207 }
208
209 pub async fn deploy(
223 deployer_wallet: &W,
224 config: &OrderBookRegistryDeployConfig,
225 ) -> Result<OrderBookRegistryManager<W>> {
226 let register_blob_id =
227 OrderBookRegistryManager::deploy_register_blob(deployer_wallet, config)
228 .await?;
229 let (proxy, requires_initialization) =
230 OrderBookRegistryManager::deploy_register_proxy(
231 deployer_wallet,
232 ®ister_blob_id,
233 config,
234 )
235 .await?;
236 let register_deploy =
237 OrderBookRegistryManager::new(deployer_wallet.clone(), proxy.contract_id());
238
239 if requires_initialization {
241 register_deploy
243 .registry_proxy
244 .methods()
245 .initialize_proxy()
246 .call()
247 .await?;
248 register_deploy
250 .registry
251 .methods()
252 .initialize()
253 .call()
254 .await?;
255 }
256
257 Ok(register_deploy)
258 }
259
260 pub async fn upgrade(
261 &self,
262 config: &OrderBookRegistryDeployConfig,
263 ) -> Result<BlobId> {
264 let new_blob_id =
266 Self::deploy_register_blob(&self.deployer_wallet, config).await?;
267
268 self.registry_proxy
270 .methods()
271 .set_proxy_target(ContractId::new(new_blob_id))
272 .call()
273 .await?;
274
275 Ok(new_blob_id)
276 }
277
278 pub async fn get_order_book(
279 &self,
280 market_id: MarketId,
281 ) -> Result<Option<ContractId>> {
282 Ok(self
283 .registry
284 .methods()
285 .get_order_book(market_id)
286 .simulate(Execution::state_read_only())
287 .await?
288 .value)
289 }
290
291 pub async fn register_order_book(
292 &self,
293 market_id: MarketId,
294 order_book_id: ContractId,
295 ) -> Result<ContractId> {
296 let _ = self
297 .registry
298 .methods()
299 .register_order_book(order_book_id, market_id)
300 .call()
301 .await?;
302 Ok(order_book_id)
303 }
304}
305
306#[cfg(test)]
307mod tests_order_book_registry {
308 use super::*;
309 use fuels::test_helpers::{
310 WalletsConfig,
311 launch_custom_provider_and_get_wallets,
312 };
313
314 #[tokio::test]
315 async fn test_order_book_registry_deployment() {
316 let mut wallets = launch_custom_provider_and_get_wallets(
318 WalletsConfig::new(Some(2), Some(1), Some(1_000_000_000)),
319 None,
320 Some(::fuels::test_helpers::ChainConfig::local_testnet()),
321 )
322 .await
323 .unwrap();
324 let deployer_wallet = wallets.pop().unwrap();
325 let owner_wallet = wallets.pop().unwrap();
326
327 let config = OrderBookRegistryDeployConfig {
329 proxy_owner: Some(Identity::Address(owner_wallet.address())),
330 registry_owner: Some(Identity::Address(owner_wallet.address())),
331 ..Default::default()
332 };
333 let deployment = OrderBookRegistryManager::deploy(&deployer_wallet, &config)
334 .await
335 .unwrap();
336
337 let provider = deployer_wallet.try_provider().unwrap();
339 let contract_exists = provider
340 .contract_exists(&deployment.contract_id)
341 .await
342 .unwrap();
343 assert!(contract_exists, "Register contract should exist");
344
345 let contract_owner = deployment
347 .registry
348 .methods()
349 .owner()
350 .simulate(Execution::state_read_only())
351 .await
352 .unwrap()
353 .value;
354
355 match contract_owner {
356 State::Initialized(identity) => match identity {
357 Identity::Address(address) => {
358 assert_eq!(address, owner_wallet.address(), "Owner should match");
359 }
360 _ => panic!("Owner should be an address"),
361 },
362 _ => panic!("Owner should be initialized"),
363 }
364 }
365
366 #[tokio::test]
367 async fn test_order_book_registry_register_order_book() {
368 let mut wallets = launch_custom_provider_and_get_wallets(
370 WalletsConfig::new(Some(2), Some(1), Some(1_000_000_000)),
371 None,
372 Some(::fuels::test_helpers::ChainConfig::local_testnet()),
373 )
374 .await
375 .unwrap();
376 let deployer_wallet = wallets.pop().unwrap();
377
378 let config = OrderBookRegistryDeployConfig::default();
380 let order_book_registry_deploy =
381 OrderBookRegistryManager::deploy(&deployer_wallet, &config)
382 .await
383 .unwrap();
384 let order_book_registry_manager = OrderBookRegistryManager::new(
385 deployer_wallet.clone(),
386 order_book_registry_deploy.contract_id,
387 );
388
389 let market_id = MarketId {
390 base_asset: AssetId::new([1; 32]),
391 quote_asset: AssetId::new([2; 32]),
392 };
393 let order_book_id = ContractId::new([3; 32]);
394
395 order_book_registry_manager
396 .register_order_book(market_id.clone(), order_book_id)
397 .await
398 .unwrap();
399 let order_book_id_result = order_book_registry_manager
400 .get_order_book(market_id.clone())
401 .await
402 .unwrap();
403 assert_eq!(
404 order_book_id_result,
405 Some(order_book_id),
406 "Should return the correct order book id"
407 );
408 }
409}