fund_and_trade.rs 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. //! Fund two accounts with different assets, then trade between them atomically.
  2. //!
  3. //! Run with:
  4. //! ```sh
  5. //! cargo run -p kuatia --example fund_and_trade
  6. //! ```
  7. use std::sync::Arc;
  8. use kuatia::ledger::Ledger;
  9. use kuatia_core::*;
  10. use kuatia_storage_sql::SqlStore;
  11. #[tokio::main]
  12. async fn main() -> Result<(), Box<dyn std::error::Error>> {
  13. let ledger = connect().await?;
  14. let alice = AccountId::new(1);
  15. let bob = AccountId::new(2);
  16. let external = AccountId::new(99);
  17. let usd = AssetId::new(1);
  18. let eur = AssetId::new(2);
  19. // Two-decimal money: `money.parse("100.00")` -> Cent in minor units.
  20. let money = Amount::new(2);
  21. ledger
  22. .create_account(Account::new(alice, AccountPolicy::NoOverdraft))
  23. .await?;
  24. ledger
  25. .create_account(Account::new(bob, AccountPolicy::NoOverdraft))
  26. .await?;
  27. ledger
  28. .create_account(Account::new(external, AccountPolicy::ExternalAccount))
  29. .await?;
  30. // Fund: $100.00 to Alice, €90.00 to Bob.
  31. ledger
  32. .commit(
  33. TransferBuilder::new()
  34. .deposit(alice, usd, money.parse("100.00")?, external)?
  35. .build(),
  36. )
  37. .await?;
  38. ledger
  39. .commit(
  40. TransferBuilder::new()
  41. .deposit(bob, eur, money.parse("90.00")?, external)?
  42. .build(),
  43. )
  44. .await?;
  45. println!("after funding:");
  46. print_balances(&ledger, alice, bob, usd, eur).await?;
  47. // Trade: Alice gives 100 USD to Bob; Bob gives 90 EUR to Alice. Both legs
  48. // settle in one atomic transfer — each asset is conserved independently.
  49. let trade = TransferBuilder::new()
  50. .movement(alice, bob, usd, money.parse("100.00")?)
  51. .movement(bob, alice, eur, money.parse("90.00")?)
  52. .build();
  53. ledger.commit(trade).await?;
  54. println!("after trade:");
  55. print_balances(&ledger, alice, bob, usd, eur).await?;
  56. Ok(())
  57. }
  58. async fn print_balances(
  59. ledger: &Arc<Ledger>,
  60. alice: AccountId,
  61. bob: AccountId,
  62. usd: AssetId,
  63. eur: AssetId,
  64. ) -> Result<(), Box<dyn std::error::Error>> {
  65. let money = Amount::new(2);
  66. println!(
  67. " alice: {} USD, {} EUR",
  68. money.format(ledger.balance(&alice, &usd).await?),
  69. money.format(ledger.balance(&alice, &eur).await?),
  70. );
  71. println!(
  72. " bob: {} USD, {} EUR",
  73. money.format(ledger.balance(&bob, &usd).await?),
  74. money.format(ledger.balance(&bob, &eur).await?),
  75. );
  76. Ok(())
  77. }
  78. async fn connect() -> Result<Arc<Ledger>, Box<dyn std::error::Error>> {
  79. sqlx::any::install_default_drivers();
  80. let pool = sqlx::any::AnyPoolOptions::new()
  81. .max_connections(1)
  82. .connect("sqlite::memory:")
  83. .await?;
  84. let store = SqlStore::new(pool);
  85. store.migrate().await?;
  86. let ledger = Arc::new(Ledger::new(store));
  87. // On startup, finish any commit a crash interrupted (idempotent roll-forward).
  88. ledger.recover().await?;
  89. Ok(ledger)
  90. }