listeners.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import { LIQUIDITY_STATE_LAYOUT_V4, MAINNET_PROGRAM_ID, MARKET_STATE_LAYOUT_V3, Token } from '@raydium-io/raydium-sdk';
  2. import bs58 from 'bs58';
  3. import { Connection, PublicKey } from '@solana/web3.js';
  4. import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
  5. import { EventEmitter } from 'events';
  6. export class Listeners extends EventEmitter {
  7. private subscriptions: number[] = [];
  8. constructor(private readonly connection: Connection) {
  9. super();
  10. }
  11. public async start(config: {
  12. walletPublicKey: PublicKey;
  13. quoteToken: Token;
  14. autoSell: boolean;
  15. cacheNewMarkets: boolean;
  16. }) {
  17. if (config.cacheNewMarkets) {
  18. const openBookSubscription = await this.subscribeToOpenBookMarkets(config);
  19. this.subscriptions.push(openBookSubscription);
  20. }
  21. const raydiumSubscription = await this.subscribeToRaydiumPools(config);
  22. this.subscriptions.push(raydiumSubscription);
  23. if (config.autoSell) {
  24. const walletSubscription = await this.subscribeToWalletChanges(config);
  25. this.subscriptions.push(walletSubscription);
  26. }
  27. }
  28. private async subscribeToOpenBookMarkets(config: { quoteToken: Token }) {
  29. return this.connection.onProgramAccountChange(
  30. MAINNET_PROGRAM_ID.OPENBOOK_MARKET,
  31. async (updatedAccountInfo) => {
  32. this.emit('market', updatedAccountInfo);
  33. },
  34. this.connection.commitment,
  35. [
  36. { dataSize: MARKET_STATE_LAYOUT_V3.span },
  37. {
  38. memcmp: {
  39. offset: MARKET_STATE_LAYOUT_V3.offsetOf('quoteMint'),
  40. bytes: config.quoteToken.mint.toBase58(),
  41. },
  42. },
  43. ],
  44. );
  45. }
  46. private async subscribeToRaydiumPools(config: { quoteToken: Token }) {
  47. return this.connection.onProgramAccountChange(
  48. MAINNET_PROGRAM_ID.AmmV4,
  49. async (updatedAccountInfo) => {
  50. this.emit('pool', updatedAccountInfo);
  51. },
  52. this.connection.commitment,
  53. [
  54. { dataSize: LIQUIDITY_STATE_LAYOUT_V4.span },
  55. {
  56. memcmp: {
  57. offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf('quoteMint'),
  58. bytes: config.quoteToken.mint.toBase58(),
  59. },
  60. },
  61. {
  62. memcmp: {
  63. offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf('marketProgramId'),
  64. bytes: MAINNET_PROGRAM_ID.OPENBOOK_MARKET.toBase58(),
  65. },
  66. },
  67. {
  68. memcmp: {
  69. offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf('status'),
  70. bytes: bs58.encode([6, 0, 0, 0, 0, 0, 0, 0]),
  71. },
  72. },
  73. ],
  74. );
  75. }
  76. private async subscribeToWalletChanges(config: { walletPublicKey: PublicKey }) {
  77. return this.connection.onProgramAccountChange(
  78. TOKEN_PROGRAM_ID,
  79. async (updatedAccountInfo) => {
  80. this.emit('wallet', updatedAccountInfo);
  81. },
  82. this.connection.commitment,
  83. [
  84. {
  85. dataSize: 165,
  86. },
  87. {
  88. memcmp: {
  89. offset: 32,
  90. bytes: config.walletPublicKey.toBase58(),
  91. },
  92. },
  93. ],
  94. );
  95. }
  96. public async stop() {
  97. for (let i = this.subscriptions.length; i >= 0; --i) {
  98. const subscription = this.subscriptions[i];
  99. await this.connection.removeAccountChangeListener(subscription);
  100. this.subscriptions.splice(i, 1);
  101. }
  102. }
  103. }