lib.rs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. // Note. This example depends on unreleased Serum DEX changes.
  2. use anchor_lang::prelude::*;
  3. use anchor_spl::dex::serum_dex::instruction::{CancelOrderInstructionV2, NewOrderInstructionV3};
  4. use anchor_spl::dex::{
  5. Context, Logger, MarketMiddleware, MarketProxy, OpenOrdersPda, ReferralFees,
  6. };
  7. use solana_program::account_info::AccountInfo;
  8. use solana_program::entrypoint::ProgramResult;
  9. use solana_program::pubkey::Pubkey;
  10. use solana_program::sysvar::rent;
  11. /// # Permissioned Markets
  12. ///
  13. /// This demonstrates how to create "permissioned markets" on Serum via a proxy.
  14. /// A permissioned market is a regular Serum market with an additional
  15. /// open orders authority, which must sign every transaction to create or
  16. /// close an open orders account.
  17. ///
  18. /// In practice, what this means is that one can create a program that acts
  19. /// as this authority *and* that marks its own PDAs as the *owner* of all
  20. /// created open orders accounts, making the program the sole arbiter over
  21. /// who can trade on a given market.
  22. ///
  23. /// For example, this example forces all trades that execute on this market
  24. /// to set the referral to a hardcoded address--`referral::ID`--and requires
  25. /// the client to pass in an identity token, authorizing the user.
  26. ///
  27. /// # Extending the proxy via middleware
  28. ///
  29. /// To implement a custom proxy, one can implement the `MarketMiddleware` trait
  30. /// to intercept, modify, and perform any access control on DEX requests before
  31. /// they get forwarded to the orderbook. These middleware can be mixed and
  32. /// matched. Note, however, that the order of middleware matters since they can
  33. /// mutate the request.
  34. ///
  35. /// One useful pattern is to treat the request like layers of an onion, where
  36. /// each middleware unwraps the request by stripping accounts and instruction
  37. /// data before relaying it to the next middleware and ultimately to the
  38. /// orderbook. This allows one to easily extend the behavior of a proxy by
  39. /// adding a custom middleware that may process information that is unknown to
  40. /// any other middleware or to the DEX.
  41. ///
  42. /// After adding a middleware, the only additional requirement, of course, is
  43. /// to make sure the client sending transactions does the same, but in reverse.
  44. /// It should wrap the transaction in the opposite order. For convenience, an
  45. /// identical abstraction is provided in the JavaScript client.
  46. ///
  47. /// # Alternatives to middleware
  48. ///
  49. /// Note that this middleware abstraction is not required to host a
  50. /// permissioned market. One could write a regular program that manages the PDAs
  51. /// and CPI invocations oneself, if desired.
  52. #[program]
  53. pub mod permissioned_markets_middleware {
  54. use super::*;
  55. pub fn entry(program_id: &Pubkey, accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
  56. MarketProxy::new()
  57. .middleware(&mut Logger)
  58. .middleware(&mut Identity)
  59. .middleware(&mut ReferralFees::new(referral::ID))
  60. .middleware(&mut OpenOrdersPda::new())
  61. .run(program_id, accounts, data)
  62. }
  63. }
  64. /// Performs token based authorization, confirming the identity of the user.
  65. /// The identity token must be given as the fist account.
  66. struct Identity;
  67. impl MarketMiddleware for Identity {
  68. /// Accounts:
  69. ///
  70. /// 0. Authorization token.
  71. /// ..
  72. fn init_open_orders(&self, ctx: &mut Context) -> ProgramResult {
  73. verify_and_strip_auth(ctx)
  74. }
  75. /// Accounts:
  76. ///
  77. /// 0. Authorization token.
  78. /// ..
  79. fn new_order_v3(&self, ctx: &mut Context, _ix: &NewOrderInstructionV3) -> ProgramResult {
  80. verify_and_strip_auth(ctx)
  81. }
  82. /// Accounts:
  83. ///
  84. /// 0. Authorization token.
  85. /// ..
  86. fn cancel_order_v2(&self, ctx: &mut Context, _ix: &CancelOrderInstructionV2) -> ProgramResult {
  87. verify_and_strip_auth(ctx)
  88. }
  89. /// Accounts:
  90. ///
  91. /// 0. Authorization token.
  92. /// ..
  93. fn cancel_order_by_client_id_v2(&self, ctx: &mut Context, _client_id: u64) -> ProgramResult {
  94. verify_and_strip_auth(ctx)
  95. }
  96. /// Accounts:
  97. ///
  98. /// 0. Authorization token.
  99. /// ..
  100. fn settle_funds(&self, ctx: &mut Context) -> ProgramResult {
  101. verify_and_strip_auth(ctx)
  102. }
  103. /// Accounts:
  104. ///
  105. /// 0. Authorization token.
  106. /// ..
  107. fn close_open_orders(&self, ctx: &mut Context) -> ProgramResult {
  108. verify_and_strip_auth(ctx)
  109. }
  110. /// Accounts:
  111. ///
  112. /// 0. Authorization token.
  113. /// ..
  114. fn fallback(&self, ctx: &mut Context) -> ProgramResult {
  115. verify_and_strip_auth(ctx)
  116. }
  117. }
  118. // Utils.
  119. fn verify_and_strip_auth(ctx: &mut Context) -> ProgramResult {
  120. // The rent sysvar is used as a dummy example of an identity token.
  121. let auth = &ctx.accounts[0];
  122. require!(auth.key == &rent::ID, InvalidAuth);
  123. // Strip off the account before possing on the message.
  124. ctx.accounts = (&ctx.accounts[1..]).to_vec();
  125. Ok(())
  126. }
  127. // Error.
  128. #[error]
  129. pub enum ErrorCode {
  130. #[msg("Invalid auth token provided")]
  131. InvalidAuth,
  132. }
  133. // Constants.
  134. pub mod referral {
  135. // This is a dummy address for testing. Do not use in production.
  136. solana_program::declare_id!("3oSfkjQZKCneYvsCTZc9HViGAPqR8pYr4h9YeGB5ZxHf");
  137. }