lib.rs 5.1 KB

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