1mod account;
14mod indexer;
15mod perp;
16
17use std::{
18 str::FromStr,
19 sync::{
20 Arc,
21 atomic::{AtomicBool, Ordering},
22 },
23 time::Duration,
24};
25
26pub use account::*;
27use alloy::{
28 hex::ToHexExt,
29 network::TransactionBuilder,
30 node_bindings::{Anvil, AnvilInstance},
31 primitives::{Address, Bytes, U256, address, hex},
32 providers::{DynProvider, Provider, ProviderBuilder, ext::AnvilApi},
33 rpc::{client::RpcClient, types::TransactionRequest},
34};
35use dashmap::{DashMap, DashSet};
36use fastnum::{UD64, udec64};
37pub use indexer::*;
38pub use perp::*;
39
40use crate::{
41 Chain,
42 abi::{dex::Exchange, erc1967_proxy::ERC1967Proxy, testing::TestToken},
43 error::DexError,
44 num, state, types,
45};
46
47const CHAIN_ID: u64 = 1337;
48const BLOCK_TIME_SEC: f64 = 0.4;
49const POLL_INTERVAL_MS: u64 = 50;
50
51const USD_DECIMALS: u8 = 6;
52
53const PREVIOUS_IMPLEMENTATION: &str =
62 include_str!("../../abi/dex/legacy/Exchange.v1.1.7.3.bin");
63
64#[derive(Debug)]
65pub struct TestExchange {
66 pub chain_id: u64,
67 pub rpc_url: String,
68 pub provider: DynProvider,
69 pub exchange: Exchange::ExchangeInstance<DynProvider>,
70 pub token: TestToken::TestTokenInstance<DynProvider>,
71 pub owner: Address,
72 pub owner_pk: String,
73 pub admin: Address,
74 pub admin_pk: String,
75 pub price_admin: Address,
76 pub price_admin_pk: String,
77 pub collateral_converter: num::Converter,
78 perpetual_ids: Arc<DashSet<types::PerpetualId>>,
79 account_address: Arc<DashMap<types::AccountId, Address>>,
80 legacy: AtomicBool,
85 anvil: AnvilInstance,
86}
87
88impl TestExchange {
89 pub async fn new() -> Self { Self::deploy(None).await }
92
93 pub async fn new_at_previous_version() -> Self {
101 Self::deploy(Some(PREVIOUS_IMPLEMENTATION)).await
102 }
103
104 pub async fn upgrade(
112 &self,
113 taker_fees: [UD64; state::FEE_TIERS],
114 maker_fees: [UD64; state::FEE_TIERS],
115 ) {
116 let implementation = Exchange::deploy(self.provider.clone())
117 .await
118 .map_err::<DexError, _>(|err| DexError::Provider(err.into()))
119 .unwrap();
120 let fee_converter = num::fee_converter();
121 let initialize = self
122 .exchange
123 .initializeV3(
124 taker_fees.map(|fee| fee_converter.to_unsigned(fee)),
125 maker_fees.map(|fee| fee_converter.to_unsigned(fee)),
126 [U256::ZERO; state::FEE_TIERS],
128 [U256::ZERO; state::FEE_TIERS],
129 self.perpetual_ids.iter().map(|p| U256::from(*p)).collect(),
132 )
133 .calldata()
134 .clone();
135 self.exchange
136 .upgradeToAndCall(*implementation.address(), initialize)
137 .gas(30_000_000)
138 .send()
139 .await
140 .map_err::<DexError, _>(|err| DexError::Provider(err.into()))
141 .unwrap()
142 .get_receipt()
143 .await
144 .unwrap();
145 self.legacy.store(false, Ordering::Relaxed);
148 }
149
150 async fn deploy(implementation: Option<&str>) -> Self {
151 let anvil = Anvil::new()
152 .block_time_f64(BLOCK_TIME_SEC)
153 .chain_id(CHAIN_ID)
154 .args(vec!["--code-size-limit", "131072"])
155 .args(vec!["--gas-limit", "200000000"])
156 .args(vec!["--base-fee", "100000000000"])
157 .args(vec!["--order", "fifo"])
158 .args(vec!["--max-persisted-states", "1000"])
159 .args(vec!["--slots-in-an-epoch", "0"])
160 .try_spawn()
161 .unwrap();
162 let client = RpcClient::builder().http(anvil.endpoint_url());
163 client.set_poll_interval(Duration::from_millis(POLL_INTERVAL_MS));
164 let provider = DynProvider::new(
165 ProviderBuilder::new()
166 .wallet(anvil.wallet().unwrap())
167 .connect_client(client),
168 );
169 provider
171 .anvil_set_balance(
172 address!("0x05f32b3cc3888453ff71b01135b34ff8e41263f2"),
173 U256::from(1e18 as u64),
174 )
175 .await
176 .unwrap();
177 _ = provider.send_raw_transaction(&hex!("0xf90f538085174876e800830f42408080b90f00608060405234801561001057600080fd5b50610ee0806100206000396000f3fe6080604052600436106100f35760003560e01c80634d2301cc1161008a578063a8b0574e11610059578063a8b0574e1461025a578063bce38bd714610275578063c3077fa914610288578063ee82ac5e1461029b57600080fd5b80634d2301cc146101ec57806372425d9d1461022157806382ad56cb1461023457806386d516e81461024757600080fd5b80633408e470116100c65780633408e47014610191578063399542e9146101a45780633e64a696146101c657806342cbb15c146101d957600080fd5b80630f28c97d146100f8578063174dea711461011a578063252dba421461013a57806327e86d6e1461015b575b600080fd5b34801561010457600080fd5b50425b6040519081526020015b60405180910390f35b61012d610128366004610a85565b6102ba565b6040516101119190610bbe565b61014d610148366004610a85565b6104ef565b604051610111929190610bd8565b34801561016757600080fd5b50437fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0140610107565b34801561019d57600080fd5b5046610107565b6101b76101b2366004610c60565b610690565b60405161011193929190610cba565b3480156101d257600080fd5b5048610107565b3480156101e557600080fd5b5043610107565b3480156101f857600080fd5b50610107610207366004610ce2565b73ffffffffffffffffffffffffffffffffffffffff163190565b34801561022d57600080fd5b5044610107565b61012d610242366004610a85565b6106ab565b34801561025357600080fd5b5045610107565b34801561026657600080fd5b50604051418152602001610111565b61012d610283366004610c60565b61085a565b6101b7610296366004610a85565b610a1a565b3480156102a757600080fd5b506101076102b6366004610d18565b4090565b60606000828067ffffffffffffffff8111156102d8576102d8610d31565b60405190808252806020026020018201604052801561031e57816020015b6040805180820190915260008152606060208201528152602001906001900390816102f65790505b5092503660005b8281101561047757600085828151811061034157610341610d60565b6020026020010151905087878381811061035d5761035d610d60565b905060200281019061036f9190610d8f565b6040810135958601959093506103886020850185610ce2565b73ffffffffffffffffffffffffffffffffffffffff16816103ac6060870187610dcd565b6040516103ba929190610e32565b60006040518083038185875af1925050503d80600081146103f7576040519150601f19603f3d011682016040523d82523d6000602084013e6103fc565b606091505b50602080850191909152901515808452908501351761046d577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260846000fd5b5050600101610325565b508234146104e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d756c746963616c6c333a2076616c7565206d69736d6174636800000000000060448201526064015b60405180910390fd5b50505092915050565b436060828067ffffffffffffffff81111561050c5761050c610d31565b60405190808252806020026020018201604052801561053f57816020015b606081526020019060019003908161052a5790505b5091503660005b8281101561068657600087878381811061056257610562610d60565b90506020028101906105749190610e42565b92506105836020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166105a66020850185610dcd565b6040516105b4929190610e32565b6000604051808303816000865af19150503d80600081146105f1576040519150601f19603f3d011682016040523d82523d6000602084013e6105f6565b606091505b5086848151811061060957610609610d60565b602090810291909101015290508061067d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b50600101610546565b5050509250929050565b43804060606106a086868661085a565b905093509350939050565b6060818067ffffffffffffffff8111156106c7576106c7610d31565b60405190808252806020026020018201604052801561070d57816020015b6040805180820190915260008152606060208201528152602001906001900390816106e55790505b5091503660005b828110156104e657600084828151811061073057610730610d60565b6020026020010151905086868381811061074c5761074c610d60565b905060200281019061075e9190610e76565b925061076d6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166107906040850185610dcd565b60405161079e929190610e32565b6000604051808303816000865af19150503d80600081146107db576040519150601f19603f3d011682016040523d82523d6000602084013e6107e0565b606091505b506020808401919091529015158083529084013517610851577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260646000fd5b50600101610714565b6060818067ffffffffffffffff81111561087657610876610d31565b6040519080825280602002602001820160405280156108bc57816020015b6040805180820190915260008152606060208201528152602001906001900390816108945790505b5091503660005b82811015610a105760008482815181106108df576108df610d60565b602002602001015190508686838181106108fb576108fb610d60565b905060200281019061090d9190610e42565b925061091c6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff1661093f6020850185610dcd565b60405161094d929190610e32565b6000604051808303816000865af19150503d806000811461098a576040519150601f19603f3d011682016040523d82523d6000602084013e61098f565b606091505b506020830152151581528715610a07578051610a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b506001016108c3565b5050509392505050565b6000806060610a2b60018686610690565b919790965090945092505050565b60008083601f840112610a4b57600080fd5b50813567ffffffffffffffff811115610a6357600080fd5b6020830191508360208260051b8501011115610a7e57600080fd5b9250929050565b60008060208385031215610a9857600080fd5b823567ffffffffffffffff811115610aaf57600080fd5b610abb85828601610a39565b90969095509350505050565b6000815180845260005b81811015610aed57602081850181015186830182015201610ad1565b81811115610aff576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600082825180855260208086019550808260051b84010181860160005b84811015610bb1578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001895281518051151584528401516040858501819052610b9d81860183610ac7565b9a86019a9450505090830190600101610b4f565b5090979650505050505050565b602081526000610bd16020830184610b32565b9392505050565b600060408201848352602060408185015281855180845260608601915060608160051b870101935082870160005b82811015610c52577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0888703018452610c40868351610ac7565b95509284019290840190600101610c06565b509398975050505050505050565b600080600060408486031215610c7557600080fd5b83358015158114610c8557600080fd5b9250602084013567ffffffffffffffff811115610ca157600080fd5b610cad86828701610a39565b9497909650939450505050565b838152826020820152606060408201526000610cd96060830184610b32565b95945050505050565b600060208284031215610cf457600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610bd157600080fd5b600060208284031215610d2a57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112610dc357600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610e0257600080fd5b83018035915067ffffffffffffffff821115610e1d57600080fd5b602001915036819003821315610a7e57600080fd5b8183823760009101908152919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112610dc357600080fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1833603018112610dc357600080fdfea2646970667358221220bb2b5c71a328032f97c676ae39a1ec2148d3e5d6f73d95e9b17910152d61f16264736f6c634300080c00331ca0edce47092c0f398cebf3ffc267f05c8e7076e3b89445e0fe50f6332273d4569ba01b0b9d000e19b24c5869b0fc3b22b0d6fa47cd63316875cbbd577d76e6fde086")).await.unwrap();
178
179 let (owner, admin, price_admin) =
180 (anvil.addresses()[0], anvil.addresses()[1], anvil.addresses()[2]);
181
182 let token = TestToken::deploy(
184 provider.clone(),
185 "Test USD".to_string(),
186 "USD".to_string(),
187 USD_DECIMALS,
188 )
189 .await
190 .unwrap();
191
192 token
194 .mint(owner, usd(1_000_000_000))
195 .send()
196 .await
197 .map_err::<DexError, _>(|err| DexError::Provider(err.into()))
198 .unwrap()
199 .get_receipt()
200 .await
201 .unwrap();
202
203 let exchange_impl = match implementation {
207 None => *Exchange::deploy(provider.clone())
208 .await
209 .map_err::<DexError, _>(|err| DexError::Provider(err.into()))
210 .unwrap()
211 .address(),
212 Some(bytecode) => deploy_bytecode(&provider, bytecode).await,
213 };
214 let init_calldata = Exchange::new(exchange_impl, provider.clone())
215 .initialize(*token.address())
216 .calldata()
217 .clone();
218 let proxy = ERC1967Proxy::deploy(provider.clone(), exchange_impl, init_calldata)
219 .await
220 .map_err::<DexError, _>(|err| DexError::Provider(err.into()))
221 .unwrap();
222 let exchange = Exchange::new(*proxy.address(), provider.clone());
223
224 exchange
226 .setWhitelistingEnabled(false)
227 .send()
228 .await
229 .map_err::<DexError, _>(|err| DexError::Provider(err.into()))
230 .unwrap()
231 .get_receipt()
232 .await
233 .unwrap();
234
235 exchange
237 .setAdministrator(admin, true)
238 .send()
239 .await
240 .map_err::<DexError, _>(|err| DexError::Provider(err.into()))
241 .unwrap()
242 .get_receipt()
243 .await
244 .unwrap();
245 exchange
246 .setPriceAdministrator(price_admin, true)
247 .send()
248 .await
249 .map_err::<DexError, _>(|err| DexError::Provider(err.into()))
250 .unwrap()
251 .get_receipt()
252 .await
253 .unwrap();
254
255 Self {
256 chain_id: anvil.chain_id(),
257 rpc_url: anvil.endpoint_url().to_string(),
258 provider,
259 exchange,
260 token,
261 owner,
262 owner_pk: anvil.nth_key(0).unwrap().to_bytes().encode_hex(),
263 admin,
264 admin_pk: anvil.nth_key(1).unwrap().to_bytes().encode_hex(),
265 price_admin,
266 price_admin_pk: anvil.nth_key(2).unwrap().to_bytes().encode_hex(),
267 collateral_converter: num::Converter::new(USD_DECIMALS),
268 perpetual_ids: Arc::new(DashSet::new()),
269 account_address: Arc::new(DashMap::new()),
270 legacy: AtomicBool::new(implementation.is_some()),
273 anvil,
274 }
275 }
276
277 pub fn chain(&self) -> Chain {
278 Chain::custom(
279 self.chain_id,
280 *self.token.address(),
281 0,
282 *self.exchange.address(),
283 self.perpetual_ids.iter().map(|p| *p).collect(),
284 )
285 }
286
287 pub fn chain_with_perpetual_discovery(&self) -> Chain { self.chain().with_perpetuals(vec![]) }
290
291 pub async fn account(&self, idx: usize, usd_balance: u64) -> TestAccount<'_> {
292 let address = self.anvil.addresses()[idx + 3]; let target_balance = usd(usd_balance);
294 let cur_balance = self.token.balanceOf(address).call().await.unwrap();
295 if target_balance > cur_balance {
296 self.token
297 .mint(address, target_balance - cur_balance)
298 .send()
299 .await
300 .map_err::<DexError, _>(|err| DexError::Provider(err.into()))
301 .unwrap()
302 .get_receipt()
303 .await
304 .unwrap();
305 }
306 self.token
307 .approve(*self.exchange.address(), target_balance)
308 .from(address)
309 .send()
310 .await
311 .map_err::<DexError, _>(|err| DexError::Provider(err.into()))
312 .unwrap()
313 .get_receipt()
314 .await
315 .unwrap();
316 let receipt = self
317 .exchange
318 .createAccount(target_balance)
319 .from(address)
320 .send()
321 .await
322 .map_err::<DexError, _>(|err| DexError::Provider(err.into()))
323 .unwrap()
324 .get_receipt()
325 .await
326 .unwrap();
327 let log = receipt.decoded_log::<Exchange::AccountCreated>().unwrap();
328 self.account_address.insert(log.id.to(), log.account);
329 TestAccount { id: log.id.to(), address: log.account, exchange: self }
330 }
331
332 pub async fn set_fee_schedule(
336 &self,
337 taker_fees: [UD64; state::FEE_TIERS],
338 maker_fees: [UD64; state::FEE_TIERS],
339 ) {
340 let fee_converter = num::fee_converter();
341 self.exchange
342 .setDefaultPerpFeeSchedValues(
343 taker_fees.map(|fee| fee_converter.to_unsigned(fee)),
344 maker_fees.map(|fee| fee_converter.to_unsigned(fee)),
345 )
346 .send()
347 .await
348 .map_err::<DexError, _>(|err| DexError::Provider(err.into()))
349 .unwrap()
350 .get_receipt()
351 .await
352 .unwrap();
353 }
354
355 pub async fn set_rwa_fee_schedule(
358 &self,
359 taker_fees: [UD64; state::FEE_TIERS],
360 maker_fees: [UD64; state::FEE_TIERS],
361 ) {
362 let fee_converter = num::fee_converter();
363 self.exchange
364 .setDefaultRwaFeeSchedValues(
365 taker_fees.map(|fee| fee_converter.to_unsigned(fee)),
366 maker_fees.map(|fee| fee_converter.to_unsigned(fee)),
367 )
368 .send()
369 .await
370 .map_err::<DexError, _>(|err| DexError::Provider(err.into()))
371 .unwrap()
372 .get_receipt()
373 .await
374 .unwrap();
375 }
376
377 pub async fn set_account_fee_tiers(&self, tiers: Vec<(types::AccountId, types::FeeTier)>) {
380 self.exchange
381 .setAccountFeeTiers(
382 tiers
383 .into_iter()
384 .map(|(account_id, tier)| Exchange::AccountFeeTier {
385 accountId: U256::from(account_id),
386 tier: U256::from(tier),
387 })
388 .collect(),
389 )
390 .from(self.admin)
391 .send()
392 .await
393 .map_err::<DexError, _>(|err| DexError::Provider(err.into()))
394 .unwrap()
395 .get_receipt()
396 .await
397 .unwrap();
398 }
399
400 #[allow(clippy::too_many_arguments)]
409 pub async fn perp(
410 &self,
411 name: &str,
412 perp_id: types::PerpetualId,
413 base_price: UD64,
414 price_decimals: u8,
415 size_decimals: u8,
416 initial_margin: UD64,
417 maintenance_margin: UD64,
418 ) -> TestPerp<'_> {
419 let price_converter = num::Converter::new(price_decimals);
420 let leverage_converter = num::Converter::new(2); if self.legacy.load(Ordering::Relaxed) {
422 crate::abi::dex_legacy::LegacyExchange::new(
428 *self.exchange.address(),
429 self.provider.clone(),
430 )
431 .addContract(
432 name.to_string(),
433 name.to_string(),
434 U256::from(perp_id),
435 price_converter.to_unsigned(base_price),
436 U256::from(price_decimals),
437 U256::from(size_decimals),
438 U256::ZERO,
439 U256::ZERO,
440 leverage_converter.to_unsigned(initial_margin),
441 leverage_converter.to_unsigned(maintenance_margin),
442 )
443 .send()
444 .await
445 .map_err::<DexError, _>(|err| DexError::Provider(err.into()))
446 .unwrap()
447 .get_receipt()
448 .await
449 .unwrap();
450 } else {
451 self.exchange
452 .addContract(
453 name.to_string(),
454 name.to_string(),
455 U256::from(perp_id),
456 price_converter.to_unsigned(base_price),
457 U256::from(price_decimals),
458 U256::from(size_decimals),
459 leverage_converter.to_unsigned(initial_margin),
460 leverage_converter.to_unsigned(maintenance_margin),
461 )
462 .send()
463 .await
464 .map_err::<DexError, _>(|err| DexError::Provider(err.into()))
465 .unwrap()
466 .get_receipt()
467 .await
468 .unwrap();
469 }
470 self.exchange
472 .setIgnOracle(U256::from(perp_id), true)
473 .send()
474 .await
475 .map_err::<DexError, _>(|err| DexError::Provider(err.into()))
476 .unwrap()
477 .get_receipt()
478 .await
479 .unwrap();
480 self.perpetual_ids.insert(perp_id);
481 TestPerp {
482 id: perp_id,
483 name: name.to_string(),
484 price_converter,
485 size_converter: num::Converter::new(size_decimals),
486 leverage_converter,
487 exchange: self,
488 }
489 }
490
491 pub async fn btc_perp(&self) -> TestPerp<'_> {
492 self.perp("BTC", 0x10, udec64!(5000), 1, 5, udec64!(10), udec64!(20))
493 .await
494 .with_mark_price(udec64!(100000))
495 .await
496 .unpause()
497 .await
498 }
499
500 pub async fn eth_perp(&self) -> TestPerp<'_> {
501 self.perp("ETH", 0x20, udec64!(1), 2, 3, udec64!(10), udec64!(20))
502 .await
503 .with_mark_price(udec64!(4000))
504 .await
505 .unpause()
506 .await
507 }
508
509 pub async fn sol_perp(&self) -> TestPerp<'_> {
510 self.perp("SOL", 0x30, udec64!(1), 2, 3, udec64!(10), udec64!(20))
511 .await
512 .with_mark_price(udec64!(200))
513 .await
514 .unpause()
515 .await
516 }
517
518 pub async fn trx_perp(&self) -> TestPerp<'_> {
519 self.perp("TRX", 0x40, udec64!(1), 5, 0, udec64!(10), udec64!(20))
520 .await
521 .with_mark_price(udec64!(0.3))
522 .await
523 .unpause()
524 .await
525 }
526}
527
528async fn deploy_bytecode(provider: &DynProvider, bytecode: &str) -> Address {
531 let code = Bytes::from_str(bytecode.trim()).expect("implementation bytecode");
532 provider
533 .send_transaction(TransactionRequest::default().with_deploy_code(code))
534 .await
535 .map_err::<DexError, _>(|err| DexError::Provider(err.into()))
536 .unwrap()
537 .get_receipt()
538 .await
539 .unwrap()
540 .contract_address
541 .expect("implementation deployed")
542}
543
544pub fn scale(amount: u64, decimals: u8) -> U256 {
545 U256::from(amount) * U256::from(10).pow(U256::from(decimals))
546}
547
548pub fn usd(amount: u64) -> U256 { scale(amount, USD_DECIMALS) }