fund_and_trade.rs 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  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::debit_must_not_exceed_credit(alice))
  23. .await?;
  24. ledger
  25. .create_account(Account::debit_must_not_exceed_credit(bob))
  26. .await?;
  27. ledger.create_account(Account::new(external)).await?;
  28. // Fund: $100.00 to Alice, €90.00 to Bob.
  29. ledger
  30. .commit(
  31. TransferBuilder::new()
  32. .deposit(alice, usd, money.parse("100.00")?, external)?
  33. .build(),
  34. )
  35. .await?;
  36. ledger
  37. .commit(
  38. TransferBuilder::new()
  39. .deposit(bob, eur, money.parse("90.00")?, external)?
  40. .build(),
  41. )
  42. .await?;
  43. println!("after funding:");
  44. print_balances(&ledger, alice, bob, usd, eur).await?;
  45. // Trade: Alice gives 100 USD to Bob; Bob gives 90 EUR to Alice. Both legs
  46. // settle in one atomic transfer — each asset is conserved independently.
  47. let trade = TransferBuilder::new()
  48. .movement(alice, bob, usd, money.parse("100.00")?)
  49. .movement(bob, alice, eur, money.parse("90.00")?)
  50. .build();
  51. ledger.commit(trade).await?;
  52. println!("after trade:");
  53. print_balances(&ledger, alice, bob, usd, eur).await?;
  54. Ok(())
  55. }
  56. async fn print_balances(
  57. ledger: &Arc<Ledger>,
  58. alice: AccountId,
  59. bob: AccountId,
  60. usd: AssetId,
  61. eur: AssetId,
  62. ) -> Result<(), Box<dyn std::error::Error>> {
  63. let money = Amount::new(2);
  64. println!(
  65. " alice: {} USD, {} EUR",
  66. money.format(ledger.balance(&alice, &usd).await?),
  67. money.format(ledger.balance(&alice, &eur).await?),
  68. );
  69. println!(
  70. " bob: {} USD, {} EUR",
  71. money.format(ledger.balance(&bob, &usd).await?),
  72. money.format(ledger.balance(&bob, &eur).await?),
  73. );
  74. Ok(())
  75. }
  76. async fn connect() -> Result<Arc<Ledger>, Box<dyn std::error::Error>> {
  77. sqlx::any::install_default_drivers();
  78. let pool = sqlx::any::AnyPoolOptions::new()
  79. .max_connections(1)
  80. .connect("sqlite::memory:")
  81. .await?;
  82. let store = SqlStore::new(pool);
  83. store.migrate().await?;
  84. let ledger = Arc::new(Ledger::new(store));
  85. // On startup, finish any commit a crash interrupted (idempotent roll-forward).
  86. ledger.recover().await?;
  87. Ok(ledger)
  88. }