lushdog@outlook.com 1 maand geleden
bovenliggende
commit
a87ecf9f4b
99 gewijzigde bestanden met toevoegingen van 30416 en 65 verwijderingen
  1. 10 1
      package.json
  2. 319 0
      pnpm-lock.yaml
  3. 24 40
      src/app/api/my-lp/route.ts
  4. 18 23
      src/app/my-lp/page.tsx
  5. 52 0
      src/lib/byreal-clmm-sdk/.gitignore
  6. 10 0
      src/lib/byreal-clmm-sdk/.prettierignore
  7. 4 0
      src/lib/byreal-clmm-sdk/.prettierrc
  8. 58 0
      src/lib/byreal-clmm-sdk/README.md
  9. 38 0
      src/lib/byreal-clmm-sdk/package.json
  10. 80 0
      src/lib/byreal-clmm-sdk/src/calculate.ts
  11. 6 0
      src/lib/byreal-clmm-sdk/src/client/apis/commonModels.ts
  12. 89 0
      src/lib/byreal-clmm-sdk/src/client/apis/index.ts
  13. 107 0
      src/lib/byreal-clmm-sdk/src/client/apis/ky.ts
  14. 87 0
      src/lib/byreal-clmm-sdk/src/client/apis/poolsModels.ts
  15. 22 0
      src/lib/byreal-clmm-sdk/src/client/apis/positionModels.ts
  16. 43 0
      src/lib/byreal-clmm-sdk/src/client/apis/swapModels.ts
  17. 10 0
      src/lib/byreal-clmm-sdk/src/client/apis/tickModels.ts
  18. 1371 0
      src/lib/byreal-clmm-sdk/src/client/chain/index.ts
  19. 177 0
      src/lib/byreal-clmm-sdk/src/client/chain/models.ts
  20. 408 0
      src/lib/byreal-clmm-sdk/src/client/chain/utils.ts
  21. 84 0
      src/lib/byreal-clmm-sdk/src/client/index.ts
  22. 42 0
      src/lib/byreal-clmm-sdk/src/client/liquidity/abstract.ts
  23. 76 0
      src/lib/byreal-clmm-sdk/src/client/liquidity/clmm.ts
  24. 14 0
      src/lib/byreal-clmm-sdk/src/client/liquidity/types.ts
  25. 105 0
      src/lib/byreal-clmm-sdk/src/client/token.ts
  26. 30 0
      src/lib/byreal-clmm-sdk/src/constants.ts
  27. 5 0
      src/lib/byreal-clmm-sdk/src/index.ts
  28. 1203 0
      src/lib/byreal-clmm-sdk/src/instructions/baseInstruction.ts
  29. 41 0
      src/lib/byreal-clmm-sdk/src/instructions/constants.ts
  30. 155 0
      src/lib/byreal-clmm-sdk/src/instructions/getRawData.ts
  31. 8 0
      src/lib/byreal-clmm-sdk/src/instructions/index.ts
  32. 547 0
      src/lib/byreal-clmm-sdk/src/instructions/instruction.ts
  33. 241 0
      src/lib/byreal-clmm-sdk/src/instructions/layout.ts
  34. 213 0
      src/lib/byreal-clmm-sdk/src/instructions/libs/marshmallow/bufferLayout.ts
  35. 373 0
      src/lib/byreal-clmm-sdk/src/instructions/libs/marshmallow/index.ts
  36. 70 0
      src/lib/byreal-clmm-sdk/src/instructions/models.ts
  37. 131 0
      src/lib/byreal-clmm-sdk/src/instructions/pda.ts
  38. 7242 0
      src/lib/byreal-clmm-sdk/src/instructions/target/idl/byreal_amm_v3.json
  39. 5328 0
      src/lib/byreal-clmm-sdk/src/instructions/target/types/byreal_amm_v3.ts
  40. 4148 0
      src/lib/byreal-clmm-sdk/src/instructions/target/types/raydium_amm_v3.ts
  41. 79 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/binaryUtils.ts
  42. 111 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/fetchWalletTokenAccounts.ts
  43. 29 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/getTickArrayBitmapExtension.ts
  44. 105 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/getTickArrayInfo.ts
  45. 28 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/index.ts
  46. 617 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/liquidityMath.test.ts
  47. 212 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/liquidityMath.ts
  48. 42 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/mathUtils.ts
  49. 97 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/models.ts
  50. 164 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/poolStateUtils.ts
  51. 412 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/poolUtils.ts
  52. 160 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/position.ts
  53. 220 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/sqrtPriceMath.test.ts
  54. 194 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/sqrtPriceMath.ts
  55. 393 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/swapMath.ts
  56. 567 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/tick.ts
  57. 189 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/tickArrayUtils.ts
  58. 227 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/tickMath.test.ts
  59. 60 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/tickMath.ts
  60. 227 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/tickarrayBitmap.ts
  61. 97 0
      src/lib/byreal-clmm-sdk/src/instructions/utils/transfer.ts
  62. 14 0
      src/lib/byreal-clmm-sdk/src/playgrounds/01_get_raw_position_info_list.ts
  63. 17 0
      src/lib/byreal-clmm-sdk/src/playgrounds/02_get_raw_position_info.ts
  64. 13 0
      src/lib/byreal-clmm-sdk/src/playgrounds/03_get_raw_pool_info.ts
  65. 42 0
      src/lib/byreal-clmm-sdk/src/playgrounds/04_get_position_info_list.ts
  66. 23 0
      src/lib/byreal-clmm-sdk/src/playgrounds/05_align_price.ts
  67. 32 0
      src/lib/byreal-clmm-sdk/src/playgrounds/06_get_amount_from_another_amount.ts
  68. 83 0
      src/lib/byreal-clmm-sdk/src/playgrounds/07_create_position_baseA.ts
  69. 82 0
      src/lib/byreal-clmm-sdk/src/playgrounds/07_create_position_baseB.ts
  70. 40 0
      src/lib/byreal-clmm-sdk/src/playgrounds/08_decrease_liquidity.ts
  71. 82 0
      src/lib/byreal-clmm-sdk/src/playgrounds/09_create_position_side_tokenA.ts
  72. 82 0
      src/lib/byreal-clmm-sdk/src/playgrounds/10_create_position_side_tokenB.ts
  73. 69 0
      src/lib/byreal-clmm-sdk/src/playgrounds/11_decrease_liquidity_simulate_amount.ts
  74. 101 0
      src/lib/byreal-clmm-sdk/src/playgrounds/12_decrease_liquidity_simulate_amount_2.ts
  75. 21 0
      src/lib/byreal-clmm-sdk/src/playgrounds/13_decrease_full_liquidity.ts
  76. 21 0
      src/lib/byreal-clmm-sdk/src/playgrounds/14_collect_fees.ts
  77. 75 0
      src/lib/byreal-clmm-sdk/src/playgrounds/15_add_liquidity.ts
  78. 75 0
      src/lib/byreal-clmm-sdk/src/playgrounds/16_add_liquidity_2.ts
  79. 50 0
      src/lib/byreal-clmm-sdk/src/playgrounds/17_collect_all_fees.ts
  80. 72 0
      src/lib/byreal-clmm-sdk/src/playgrounds/18_calculate_apr.ts
  81. 42 0
      src/lib/byreal-clmm-sdk/src/playgrounds/19_calculate_apr_2.ts
  82. 76 0
      src/lib/byreal-clmm-sdk/src/playgrounds/20_calculate_apr_3.ts
  83. 44 0
      src/lib/byreal-clmm-sdk/src/playgrounds/21_swap_a_to_b.ts
  84. 44 0
      src/lib/byreal-clmm-sdk/src/playgrounds/22_swap_b_to_a.ts
  85. 45 0
      src/lib/byreal-clmm-sdk/src/playgrounds/23_swap_exact_out_a_to_b.ts
  86. 45 0
      src/lib/byreal-clmm-sdk/src/playgrounds/24_swap_exact_out_b_to_a.ts
  87. 44 0
      src/lib/byreal-clmm-sdk/src/playgrounds/config.ts
  88. 147 0
      src/lib/byreal-clmm-sdk/src/utils/accountInfo.ts
  89. 51 0
      src/lib/byreal-clmm-sdk/src/utils/checkV0TxSize.ts
  90. 72 0
      src/lib/byreal-clmm-sdk/src/utils/estimateComputeUnits.ts
  91. 23 0
      src/lib/byreal-clmm-sdk/src/utils/generatePubKey.ts
  92. 11 0
      src/lib/byreal-clmm-sdk/src/utils/index.ts
  93. 15 0
      src/lib/byreal-clmm-sdk/src/utils/token.ts
  94. 250 0
      src/lib/byreal-clmm-sdk/src/utils/transactionUtils.ts
  95. 52 0
      src/lib/byreal-clmm-sdk/src/utils/validateAndParsePublicKey.ts
  96. 16 0
      src/lib/byreal-clmm-sdk/tsconfig.json
  97. 1212 0
      src/lib/byreal-clmm-sdk/yarn.lock
  98. 13 0
      src/lib/config.ts
  99. 1 1
      src/lib/solana-config.ts

+ 10 - 1
package.json

@@ -12,11 +12,20 @@
 	},
 	"dependencies": {
 		"@ant-design/icons": "^6.1.0",
+		"@coral-xyz/anchor": "^0.32.1",
+		"@noble/hashes": "^1.8.0",
+		"@solana/buffer-layout": "^4.0.1",
+		"@solana/spl-token": "^0.4.14",
 		"@solana/web3.js": "^1.98.4",
 		"antd": "^6.0.1",
+		"bn.js": "^5.2.2",
+		"decimal.js": "^10.6.0",
+		"ky": "^1.14.2",
+		"lodash-es": "^4.17.22",
 		"next": "16.0.7",
 		"react": "19.2.0",
-		"react-dom": "19.2.0"
+		"react-dom": "19.2.0",
+		"sha256": "link:@noble/hashes/sha256"
 	},
 	"devDependencies": {
 		"@tailwindcss/postcss": "^4",

+ 319 - 0
pnpm-lock.yaml

@@ -11,12 +11,36 @@ importers:
       '@ant-design/icons':
         specifier: ^6.1.0
         version: 6.1.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+      '@coral-xyz/anchor':
+        specifier: ^0.32.1
+        version: 0.32.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)
+      '@noble/hashes':
+        specifier: ^1.8.0
+        version: 1.8.0
+      '@solana/buffer-layout':
+        specifier: ^4.0.1
+        version: 4.0.1
+      '@solana/spl-token':
+        specifier: ^0.4.14
+        version: 0.4.14(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)
       '@solana/web3.js':
         specifier: ^1.98.4
         version: 1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)
       antd:
         specifier: ^6.0.1
         version: 6.1.3(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+      bn.js:
+        specifier: ^5.2.2
+        version: 5.2.2
+      decimal.js:
+        specifier: ^10.6.0
+        version: 10.6.0
+      ky:
+        specifier: ^1.14.2
+        version: 1.14.2
+      lodash-es:
+        specifier: ^4.17.22
+        version: 4.17.22
       next:
         specifier: 16.0.7
         version: 16.0.7(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
@@ -26,6 +50,9 @@ importers:
       react-dom:
         specifier: 19.2.0
         version: 19.2.0(react@19.2.0)
+      sha256:
+        specifier: link:@noble/hashes/sha256
+        version: link:@noble/hashes/sha256
     devDependencies:
       '@tailwindcss/postcss':
         specifier: ^4
@@ -167,6 +194,20 @@ packages:
     resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
     engines: {node: '>=6.9.0'}
 
+  '@coral-xyz/anchor-errors@0.31.1':
+    resolution: {integrity: sha512-NhNEku4F3zzUSBtrYz84FzYWm48+9OvmT1Hhnwr6GnPQry2dsEqH/ti/7ASjjpoFTWRnPXrjAIT1qM6Isop+LQ==}
+    engines: {node: '>=10'}
+
+  '@coral-xyz/anchor@0.32.1':
+    resolution: {integrity: sha512-zAyxFtfeje2FbMA1wzgcdVs7Hng/MijPKpRijoySPCicnvcTQs/+dnPZ/cR+LcXM9v9UYSyW81uRNYZtN5G4yg==}
+    engines: {node: '>=17'}
+
+  '@coral-xyz/borsh@0.31.1':
+    resolution: {integrity: sha512-9N8AU9F0ubriKfNE3g1WF0/4dtlGXoBN/hd1PvbNBamBNwRgHxH4P+o3Zt7rSEloW1HUs6LfZEchlx9fW7POYw==}
+    engines: {node: '>=10'}
+    peerDependencies:
+      '@solana/web3.js': ^1.69.0
+
   '@emnapi/core@1.8.1':
     resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==}
 
@@ -756,22 +797,58 @@ packages:
   '@rtsao/scc@1.1.0':
     resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
 
+  '@solana/buffer-layout-utils@0.2.0':
+    resolution: {integrity: sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==}
+    engines: {node: '>= 10'}
+
   '@solana/buffer-layout@4.0.1':
     resolution: {integrity: sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==}
     engines: {node: '>=5.10'}
 
+  '@solana/codecs-core@2.0.0-rc.1':
+    resolution: {integrity: sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ==}
+    peerDependencies:
+      typescript: '>=5'
+
   '@solana/codecs-core@2.3.0':
     resolution: {integrity: sha512-oG+VZzN6YhBHIoSKgS5ESM9VIGzhWjEHEGNPSibiDTxFhsFWxNaz8LbMDPjBUE69r9wmdGLkrQ+wVPbnJcZPvw==}
     engines: {node: '>=20.18.0'}
     peerDependencies:
       typescript: '>=5.3.3'
 
+  '@solana/codecs-data-structures@2.0.0-rc.1':
+    resolution: {integrity: sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog==}
+    peerDependencies:
+      typescript: '>=5'
+
+  '@solana/codecs-numbers@2.0.0-rc.1':
+    resolution: {integrity: sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ==}
+    peerDependencies:
+      typescript: '>=5'
+
   '@solana/codecs-numbers@2.3.0':
     resolution: {integrity: sha512-jFvvwKJKffvG7Iz9dmN51OGB7JBcy2CJ6Xf3NqD/VP90xak66m/Lg48T01u5IQ/hc15mChVHiBm+HHuOFDUrQg==}
     engines: {node: '>=20.18.0'}
     peerDependencies:
       typescript: '>=5.3.3'
 
+  '@solana/codecs-strings@2.0.0-rc.1':
+    resolution: {integrity: sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g==}
+    peerDependencies:
+      fastestsmallesttextencoderdecoder: ^1.0.22
+      typescript: '>=5'
+
+  '@solana/codecs@2.0.0-rc.1':
+    resolution: {integrity: sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ==}
+    peerDependencies:
+      typescript: '>=5'
+
+  '@solana/errors@2.0.0-rc.1':
+    resolution: {integrity: sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ==}
+    hasBin: true
+    peerDependencies:
+      typescript: '>=5'
+
   '@solana/errors@2.3.0':
     resolution: {integrity: sha512-66RI9MAbwYV0UtP7kGcTBVLxJgUxoZGm8Fbc0ah+lGiAw17Gugco6+9GrJCV83VyF2mDWyYnYM9qdI3yjgpnaQ==}
     engines: {node: '>=20.18.0'}
@@ -779,6 +856,29 @@ packages:
     peerDependencies:
       typescript: '>=5.3.3'
 
+  '@solana/options@2.0.0-rc.1':
+    resolution: {integrity: sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA==}
+    peerDependencies:
+      typescript: '>=5'
+
+  '@solana/spl-token-group@0.0.7':
+    resolution: {integrity: sha512-V1N/iX7Cr7H0uazWUT2uk27TMqlqedpXHRqqAbVO2gvmJyT0E0ummMEAVQeXZ05ZhQ/xF39DLSdBp90XebWEug==}
+    engines: {node: '>=16'}
+    peerDependencies:
+      '@solana/web3.js': ^1.95.3
+
+  '@solana/spl-token-metadata@0.1.6':
+    resolution: {integrity: sha512-7sMt1rsm/zQOQcUWllQX9mD2O6KhSAtY1hFR2hfFwgqfFWzSY9E9GDvFVNYUI1F0iQKcm6HmePU9QbKRXTEBiA==}
+    engines: {node: '>=16'}
+    peerDependencies:
+      '@solana/web3.js': ^1.95.3
+
+  '@solana/spl-token@0.4.14':
+    resolution: {integrity: sha512-u09zr96UBpX4U685MnvQsNzlvw9TiY005hk1vJmJr7gMJldoPG1eYU5/wNEyOA5lkMLiR/gOi9SFD4MefOYEsA==}
+    engines: {node: '>=16'}
+    peerDependencies:
+      '@solana/web3.js': ^1.95.5
+
   '@solana/web3.js@1.98.4':
     resolution: {integrity: sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw==}
 
@@ -1166,6 +1266,16 @@ packages:
     resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==}
     hasBin: true
 
+  bigint-buffer@1.1.5:
+    resolution: {integrity: sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==}
+    engines: {node: '>= 10.0.0'}
+
+  bignumber.js@9.3.1:
+    resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==}
+
+  bindings@1.5.0:
+    resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
+
   bn.js@5.2.2:
     resolution: {integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==}
 
@@ -1190,6 +1300,10 @@ packages:
   bs58@4.0.1:
     resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==}
 
+  buffer-layout@1.2.2:
+    resolution: {integrity: sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==}
+    engines: {node: '>=4.5'}
+
   buffer@6.0.3:
     resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
 
@@ -1213,6 +1327,10 @@ packages:
     resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
     engines: {node: '>=6'}
 
+  camelcase@6.3.0:
+    resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
+    engines: {node: '>=10'}
+
   caniuse-lite@1.0.30001762:
     resolution: {integrity: sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==}
 
@@ -1238,6 +1356,10 @@ packages:
   color-name@1.1.4:
     resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
 
+  commander@12.1.0:
+    resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==}
+    engines: {node: '>=18'}
+
   commander@14.0.2:
     resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==}
     engines: {node: '>=20'}
@@ -1254,6 +1376,9 @@ packages:
   convert-source-map@2.0.0:
     resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
 
+  cross-fetch@3.2.0:
+    resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==}
+
   cross-spawn@7.0.6:
     resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
     engines: {node: '>= 8'}
@@ -1296,6 +1421,9 @@ packages:
       supports-color:
         optional: true
 
+  decimal.js@10.6.0:
+    resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
+
   deep-is@0.1.4:
     resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
 
@@ -1495,6 +1623,9 @@ packages:
     resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
     engines: {node: '>=0.10.0'}
 
+  eventemitter3@4.0.7:
+    resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
+
   eventemitter3@5.0.1:
     resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
 
@@ -1518,6 +1649,9 @@ packages:
   fast-stable-stringify@1.0.0:
     resolution: {integrity: sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==}
 
+  fastestsmallesttextencoderdecoder@1.0.22:
+    resolution: {integrity: sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==}
+
   fastq@1.20.1:
     resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==}
 
@@ -1534,6 +1668,9 @@ packages:
     resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
     engines: {node: '>=16.0.0'}
 
+  file-uri-to-path@1.0.0:
+    resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
+
   fill-range@7.1.1:
     resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
     engines: {node: '>=8'}
@@ -1845,6 +1982,10 @@ packages:
   keyv@4.5.4:
     resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
 
+  ky@1.14.2:
+    resolution: {integrity: sha512-q3RBbsO5A5zrPhB6CaCS8ZUv+NWCXv6JJT4Em0i264G9W0fdPB8YRfnnEi7Dm7X7omAkBIPojzYJ2D1oHTHqug==}
+    engines: {node: '>=18'}
+
   language-subtag-registry@0.3.23:
     resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==}
 
@@ -1930,6 +2071,9 @@ packages:
     resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
     engines: {node: '>=10'}
 
+  lodash-es@4.17.22:
+    resolution: {integrity: sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q==}
+
   lodash.merge@4.6.2:
     resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
 
@@ -2067,6 +2211,9 @@ packages:
     resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
     engines: {node: '>=10'}
 
+  pako@2.1.0:
+    resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==}
+
   parent-module@1.0.1:
     resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
     engines: {node: '>=6'}
@@ -2310,6 +2457,9 @@ packages:
   stylis@4.3.6:
     resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==}
 
+  superstruct@0.15.5:
+    resolution: {integrity: sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==}
+
   superstruct@2.0.2:
     resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==}
     engines: {node: '>=14.0.0'}
@@ -2344,6 +2494,9 @@ packages:
     resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
     engines: {node: '>=8.0'}
 
+  toml@3.0.0:
+    resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==}
+
   tr46@0.0.3:
     resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
 
@@ -2641,6 +2794,35 @@ snapshots:
       '@babel/helper-string-parser': 7.27.1
       '@babel/helper-validator-identifier': 7.28.5
 
+  '@coral-xyz/anchor-errors@0.31.1': {}
+
+  '@coral-xyz/anchor@0.32.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)':
+    dependencies:
+      '@coral-xyz/anchor-errors': 0.31.1
+      '@coral-xyz/borsh': 0.31.1(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))
+      '@noble/hashes': 1.8.0
+      '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)
+      bn.js: 5.2.2
+      bs58: 4.0.1
+      buffer-layout: 1.2.2
+      camelcase: 6.3.0
+      cross-fetch: 3.2.0
+      eventemitter3: 4.0.7
+      pako: 2.1.0
+      superstruct: 0.15.5
+      toml: 3.0.0
+    transitivePeerDependencies:
+      - bufferutil
+      - encoding
+      - typescript
+      - utf-8-validate
+
+  '@coral-xyz/borsh@0.31.1(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))':
+    dependencies:
+      '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)
+      bn.js: 5.2.2
+      buffer-layout: 1.2.2
+
   '@emnapi/core@1.8.1':
     dependencies:
       '@emnapi/wasi-threads': 1.1.0
@@ -3239,27 +3421,124 @@ snapshots:
 
   '@rtsao/scc@1.1.0': {}
 
+  '@solana/buffer-layout-utils@0.2.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)':
+    dependencies:
+      '@solana/buffer-layout': 4.0.1
+      '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)
+      bigint-buffer: 1.1.5
+      bignumber.js: 9.3.1
+    transitivePeerDependencies:
+      - bufferutil
+      - encoding
+      - typescript
+      - utf-8-validate
+
   '@solana/buffer-layout@4.0.1':
     dependencies:
       buffer: 6.0.3
 
+  '@solana/codecs-core@2.0.0-rc.1(typescript@5.9.3)':
+    dependencies:
+      '@solana/errors': 2.0.0-rc.1(typescript@5.9.3)
+      typescript: 5.9.3
+
   '@solana/codecs-core@2.3.0(typescript@5.9.3)':
     dependencies:
       '@solana/errors': 2.3.0(typescript@5.9.3)
       typescript: 5.9.3
 
+  '@solana/codecs-data-structures@2.0.0-rc.1(typescript@5.9.3)':
+    dependencies:
+      '@solana/codecs-core': 2.0.0-rc.1(typescript@5.9.3)
+      '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.9.3)
+      '@solana/errors': 2.0.0-rc.1(typescript@5.9.3)
+      typescript: 5.9.3
+
+  '@solana/codecs-numbers@2.0.0-rc.1(typescript@5.9.3)':
+    dependencies:
+      '@solana/codecs-core': 2.0.0-rc.1(typescript@5.9.3)
+      '@solana/errors': 2.0.0-rc.1(typescript@5.9.3)
+      typescript: 5.9.3
+
   '@solana/codecs-numbers@2.3.0(typescript@5.9.3)':
     dependencies:
       '@solana/codecs-core': 2.3.0(typescript@5.9.3)
       '@solana/errors': 2.3.0(typescript@5.9.3)
       typescript: 5.9.3
 
+  '@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)':
+    dependencies:
+      '@solana/codecs-core': 2.0.0-rc.1(typescript@5.9.3)
+      '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.9.3)
+      '@solana/errors': 2.0.0-rc.1(typescript@5.9.3)
+      fastestsmallesttextencoderdecoder: 1.0.22
+      typescript: 5.9.3
+
+  '@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)':
+    dependencies:
+      '@solana/codecs-core': 2.0.0-rc.1(typescript@5.9.3)
+      '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.9.3)
+      '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.9.3)
+      '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)
+      '@solana/options': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)
+      typescript: 5.9.3
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+
+  '@solana/errors@2.0.0-rc.1(typescript@5.9.3)':
+    dependencies:
+      chalk: 5.6.2
+      commander: 12.1.0
+      typescript: 5.9.3
+
   '@solana/errors@2.3.0(typescript@5.9.3)':
     dependencies:
       chalk: 5.6.2
       commander: 14.0.2
       typescript: 5.9.3
 
+  '@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)':
+    dependencies:
+      '@solana/codecs-core': 2.0.0-rc.1(typescript@5.9.3)
+      '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.9.3)
+      '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.9.3)
+      '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)
+      '@solana/errors': 2.0.0-rc.1(typescript@5.9.3)
+      typescript: 5.9.3
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+
+  '@solana/spl-token-group@0.0.7(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)':
+    dependencies:
+      '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)
+      '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+      - typescript
+
+  '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)':
+    dependencies:
+      '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)
+      '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+      - typescript
+
+  '@solana/spl-token@0.4.14(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)':
+    dependencies:
+      '@solana/buffer-layout': 4.0.1
+      '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)
+      '@solana/spl-token-group': 0.0.7(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)
+      '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)
+      '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)
+      buffer: 6.0.3
+    transitivePeerDependencies:
+      - bufferutil
+      - encoding
+      - fastestsmallesttextencoderdecoder
+      - typescript
+      - utf-8-validate
+
   '@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)':
     dependencies:
       '@babel/runtime': 7.28.4
@@ -3720,6 +3999,16 @@ snapshots:
 
   baseline-browser-mapping@2.9.11: {}
 
+  bigint-buffer@1.1.5:
+    dependencies:
+      bindings: 1.5.0
+
+  bignumber.js@9.3.1: {}
+
+  bindings@1.5.0:
+    dependencies:
+      file-uri-to-path: 1.0.0
+
   bn.js@5.2.2: {}
 
   borsh@0.7.0:
@@ -3753,6 +4042,8 @@ snapshots:
     dependencies:
       base-x: 3.0.11
 
+  buffer-layout@1.2.2: {}
+
   buffer@6.0.3:
     dependencies:
       base64-js: 1.5.1
@@ -3782,6 +4073,8 @@ snapshots:
 
   callsites@3.1.0: {}
 
+  camelcase@6.3.0: {}
+
   caniuse-lite@1.0.30001762: {}
 
   chalk@4.1.2:
@@ -3801,6 +4094,8 @@ snapshots:
 
   color-name@1.1.4: {}
 
+  commander@12.1.0: {}
+
   commander@14.0.2: {}
 
   commander@2.20.3: {}
@@ -3811,6 +4106,12 @@ snapshots:
 
   convert-source-map@2.0.0: {}
 
+  cross-fetch@3.2.0:
+    dependencies:
+      node-fetch: 2.7.0
+    transitivePeerDependencies:
+      - encoding
+
   cross-spawn@7.0.6:
     dependencies:
       path-key: 3.1.1
@@ -3849,6 +4150,8 @@ snapshots:
     dependencies:
       ms: 2.1.3
 
+  decimal.js@10.6.0: {}
+
   deep-is@0.1.4: {}
 
   define-data-property@1.1.4:
@@ -4200,6 +4503,8 @@ snapshots:
 
   esutils@2.0.3: {}
 
+  eventemitter3@4.0.7: {}
+
   eventemitter3@5.0.1: {}
 
   eyes@0.1.8: {}
@@ -4220,6 +4525,8 @@ snapshots:
 
   fast-stable-stringify@1.0.0: {}
 
+  fastestsmallesttextencoderdecoder@1.0.22: {}
+
   fastq@1.20.1:
     dependencies:
       reusify: 1.1.0
@@ -4232,6 +4539,8 @@ snapshots:
     dependencies:
       flat-cache: 4.0.1
 
+  file-uri-to-path@1.0.0: {}
+
   fill-range@7.1.1:
     dependencies:
       to-regex-range: 5.0.1
@@ -4557,6 +4866,8 @@ snapshots:
     dependencies:
       json-buffer: 3.0.1
 
+  ky@1.14.2: {}
+
   language-subtag-registry@0.3.23: {}
 
   language-tags@1.0.9:
@@ -4621,6 +4932,8 @@ snapshots:
     dependencies:
       p-locate: 5.0.0
 
+  lodash-es@4.17.22: {}
+
   lodash.merge@4.6.2: {}
 
   loose-envify@1.4.0:
@@ -4759,6 +5072,8 @@ snapshots:
     dependencies:
       p-limit: 3.1.0
 
+  pako@2.1.0: {}
+
   parent-module@1.0.1:
     dependencies:
       callsites: 3.1.0
@@ -5068,6 +5383,8 @@ snapshots:
 
   stylis@4.3.6: {}
 
+  superstruct@0.15.5: {}
+
   superstruct@2.0.2: {}
 
   supports-color@7.2.0:
@@ -5093,6 +5410,8 @@ snapshots:
     dependencies:
       is-number: 7.0.0
 
+  toml@3.0.0: {}
+
   tr46@0.0.3: {}
 
   ts-api-utils@2.4.0(typescript@5.9.3):

+ 24 - 40
src/app/api/my-lp/route.ts

@@ -1,12 +1,13 @@
 import { NextRequest, NextResponse } from 'next/server'
 import { Connection, PublicKey } from '@solana/web3.js'
 import { getSolanaRpcUrl } from '@/lib/solana-config'
-
+import { chain } from '@/lib/config'
 interface LPInfo {
-	mint: string
-	amount: string
-	uiAmount: number
-	decimals: number
+	nftMint: string
+	PriceRange: string
+	Token: string
+	TokenA: number
+	TokenB: number
 }
 
 export async function GET(request: NextRequest) {
@@ -32,47 +33,30 @@ export async function GET(request: NextRequest) {
 			)
 		}
 
-		// 创建 RPC 连接
-		const rpcUrl = getSolanaRpcUrl()
-		const connection = new Connection(
-			'https://api.mainnet-beta.solana.com',
-			'confirmed'
-		)
-		// 获取地址的所有 token 账户
-		const tokenAccounts = await connection.getParsedTokenAccountsByOwner(
-			publicKey,
-			{
-				programId: new PublicKey('TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb'), // SPL Token 程序 ID
-			}
-		)
-
-		// 过滤出有余额的 token 账户
-		const lpList: LPInfo[] = tokenAccounts.value
-			.filter((account) => {
-				const parsedInfo = account.account.data.parsed?.info
-				return (
-					parsedInfo &&
-					parsedInfo.tokenAmount &&
-					parsedInfo.tokenAmount.uiAmount > 0
-				)
-			})
-			.map((account) => {
-				const parsedInfo = account.account.data.parsed.info
-				return {
-					mint: parsedInfo.mint,
-					amount: parsedInfo.tokenAmount.amount,
-					uiAmount: parsedInfo.tokenAmount.uiAmount,
-					decimals: parsedInfo.tokenAmount.decimals,
-				}
+		const lpList = await chain.getRawPositionInfoListByUserAddress(publicKey)
+		const list = lpList.slice(0, 10)
+		console.log(list, 'list')
+		const lpListData = []
+		for (const item of list) {
+			const positionInfo = await chain.getPositionInfoByNftMint(item.nftMint)
+			if (!positionInfo) continue
+			const { uiPriceLower, uiPriceUpper, tokenA, tokenB } = positionInfo
+			console.log(positionInfo, 'positionInfo')
+			lpListData.push({
+				nftMint: item.nftMint.toBase58(),
+				PriceRange: `${uiPriceLower} - ${uiPriceUpper}`,
+				Token: `${tokenA.address.toBase58().slice(0, 4)}/${tokenB.address.toBase58().slice(0, 4)}`,
+				TokenA: tokenA.uiAmount,
+				TokenB: tokenB.uiAmount,
 			})
-
+		}
 		return NextResponse.json({
 			code: 200,
 			message: '查询成功',
 			data: {
 				address: address,
-				lpList: lpList,
-				total: lpList.length,
+				lpList: lpListData,
+				total: lpListData.length,
 			},
 		})
 	} catch (error) {

+ 18 - 23
src/app/my-lp/page.tsx

@@ -90,37 +90,32 @@ export default function MyLPPage() {
 
 	const columns: ColumnsType<LPInfo> = [
 		{
-			title: 'Mint 地址',
-			dataIndex: 'mint',
-			key: 'mint',
+			title: 'NFT Token Address',
+			dataIndex: 'nftMint',
+			key: 'nftMint',
 			render: (text: string) => (
 				<span className="font-mono text-sm">{text}</span>
 			),
 		},
 		{
-			title: '数量',
-			dataIndex: 'uiAmount',
-			key: 'uiAmount',
-			render: (value: number, record: LPInfo) => (
-				<span>
-					{value.toLocaleString(undefined, {
-						maximumFractionDigits: 6,
-					})}
-				</span>
-			),
+			title: '价格范围',
+			dataIndex: 'PriceRange',
+			key: 'PriceRange',
 		},
 		{
-			title: '原始数量',
-			dataIndex: 'amount',
-			key: 'amount',
-			render: (text: string) => (
-				<span className="font-mono text-sm">{text}</span>
-			),
+			title: 'Token',
+			dataIndex: 'Token',
+			key: 'Token',
+		},
+		{
+			title: 'TokenA',
+			dataIndex: 'TokenA',
+			key: 'TokenA',
 		},
 		{
-			title: '精度',
-			dataIndex: 'decimals',
-			key: 'decimals',
+			title: 'TokenB',
+			dataIndex: 'TokenB',
+			key: 'TokenB',
 		},
 	]
 
@@ -149,7 +144,7 @@ export default function MyLPPage() {
 					<Table
 						columns={columns}
 						dataSource={lpList}
-						rowKey="mint"
+						rowKey="nftMint"
 						pagination={{
 							pageSize: 10,
 							showSizeChanger: true,

+ 52 - 0
src/lib/byreal-clmm-sdk/.gitignore

@@ -0,0 +1,52 @@
+# compiled output
+dist
+tmp
+out-tsc
+
+# dependencies
+node_modules
+output
+
+# IDEs and editors
+/.idea
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+
+# IDE - VSCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+
+# misc
+/.sass-cache
+/connect.lock
+/coverage
+/libpeerconnection.log
+npm-debug.log
+yarn-error.log
+testem.log
+/typings
+
+# System Files
+.DS_Store
+Thumbs.db
+
+
+test-output
+
+.env.local
+.env
+
+vite.config.*.timestamp*
+vitest.config.*.timestamp*
+
+.cursor/mcp.json
+
+.cursor/rules/nx-rules.mdc
+.github/instructions/nx.instructions.md

+ 10 - 0
src/lib/byreal-clmm-sdk/.prettierignore

@@ -0,0 +1,10 @@
+# 忽略以下文件夹不被 Prettier 格式化
+node_modules
+/dist
+/.next
+/out
+/tmp
+/coverage
+/.nx/cache
+/.nx/workspace-data
+pnpm-lock.yaml

+ 4 - 0
src/lib/byreal-clmm-sdk/.prettierrc

@@ -0,0 +1,4 @@
+{
+  "singleQuote": true,
+  "printWidth": 120
+}

+ 58 - 0
src/lib/byreal-clmm-sdk/README.md

@@ -0,0 +1,58 @@
+# byreal-clmm-sdk
+
+## Install
+
+```bash
+yarn
+```
+
+## Running Playground Code
+
+```bash
+bun run src/playgrounds/xxxx.ts
+```
+
+Tips: When running examples in playgrounds, you need to set the private key in .env
+
+## Running Unit Tests
+
+```bash
+yarn vitest
+```
+
+## Build
+
+```bash
+yarn build
+```
+
+## Utility functions
+
+```ts
+// price -> tick
+SqrtPriceMath.getTickFromPrice;
+// tick -> sqrtPriceX64
+SqrtPriceMath.getSqrtPriceX64FromTick;
+// sqrtPriceX64 -> price
+SqrtPriceMath.sqrtPriceX64ToPrice;
+
+// price -> sqrtPriceX64
+SqrtPriceMath.priceToSqrtPriceX64;
+
+// sqrtPriceX64 -> tick
+SqrtPriceMath.getTickFromSqrtPriceX64;
+
+// price -> tick
+SqrtPriceMath.getTickFromPrice;
+
+// tick -> price
+TickMath.getPriceFromTick;
+
+// price -> tick
+// price -> sqrtPriceX64
+// price -> roundedPrice
+TickMath.getTickAlignedPriceDetails;
+
+// Calculate the position address from the nft address
+getPdaPersonalPositionAddress;
+```

+ 38 - 0
src/lib/byreal-clmm-sdk/package.json

@@ -0,0 +1,38 @@
+{
+  "name": "@byreal/clmm-sdk",
+  "version": "0.0.1",
+  "private": true,
+  "type": "module",
+  "main": "./dist/index.js",
+  "module": "./dist/index.js",
+  "types": "./dist/index.d.ts",
+  "exports": {
+    "./package.json": "./package.json",
+    ".": {
+      "development": "./src/index.ts",
+      "types": "./dist/index.d.ts",
+      "import": "./dist/index.js",
+      "default": "./dist/index.js"
+    }
+  },
+  "scripts": {
+    "test": "vitest",
+    "build": "tsc"
+  },
+  "dependencies": {
+    "@coral-xyz/anchor": "^0.31.1",
+    "@noble/hashes": "^1.8.0",
+    "@solana/buffer-layout": "^4.0.1",
+    "@solana/spl-token": "^0.4.13",
+    "@solana/web3.js": "^1.98.2",
+    "bn.js": "^5.2.1",
+    "bs58": "^6.0.0",
+    "decimal.js": "^10.5.0",
+    "dotenv": "^16.5.0",
+    "ky": "^1.8.1",
+    "lodash-es": "^4.17.21",
+    "tslib": "^2.3.0",
+    "typescript": "~5.7.2",
+    "vitest": "^3.0.9"
+  }
+}

+ 80 - 0
src/lib/byreal-clmm-sdk/src/calculate.ts

@@ -0,0 +1,80 @@
+// Various calculations
+
+import { Decimal } from 'decimal.js';
+
+import { MAX_TICK, MIN_TICK, PoolUtils, SqrtPriceMath, TickMath } from './instructions/index';
+
+/**
+ * Calculate tick-aligned price range
+ * @param poolInfo
+ * @param startPrice
+ * @param endPrice
+ * @returns
+ */
+export function calculateTickAlignedPriceRange(info: {
+  tickSpacing: number;
+  mintDecimalsA: number;
+  mintDecimalsB: number;
+  startPrice: string | number;
+  endPrice: string | number;
+}) {
+  const priceInTickLower = TickMath.getTickAlignedPriceDetails(
+    new Decimal(info.startPrice),
+    info.tickSpacing,
+    info.mintDecimalsA,
+    info.mintDecimalsB,
+  );
+  const priceInTickUpper = TickMath.getTickAlignedPriceDetails(
+    new Decimal(info.endPrice),
+    info.tickSpacing,
+    info.mintDecimalsA,
+    info.mintDecimalsB,
+  );
+  return {
+    priceInTickLower,
+    priceInTickUpper,
+  };
+}
+
+/**
+  Using the following process, reassemble a function that takes a tick + tickSpacing and returns a new price
+
+// tick + tickSpacing -> sqrtPriceX64
+SqrtPriceMath.getSqrtPriceX64FromTick;
+// sqrtPriceX64 -> price
+SqrtPriceMath.sqrtPriceX64ToPrice;
+ */
+
+export function calculatePriceFromTick(
+  tick: number | string,
+  mintDecimalsA: number,
+  mintDecimalsB: number,
+  tickSpacing: number | string,
+) {
+  let tickWithSpacing = Number(tick) + Number(tickSpacing);
+
+  // If tickWithSpacing is out of MIN_TICK and MAX_TICK range, use the nearest max or min tick
+  if (tickWithSpacing < MIN_TICK || tickWithSpacing > MAX_TICK) {
+    console.warn(`!!! tickWithSpacing ${tickWithSpacing} is out of range, using the nearest tick !!!`);
+    tickWithSpacing = Number(tick);
+  }
+  const sqrtPriceX64 = SqrtPriceMath.getSqrtPriceX64FromTick(tickWithSpacing);
+  const price = SqrtPriceMath.sqrtPriceX64ToPrice(sqrtPriceX64, mintDecimalsA, mintDecimalsB);
+  return {
+    price: price.toString(),
+    tick: tickWithSpacing,
+  };
+}
+
+/**
+ * Calculate the available price range from a TickSpacing, returns an object containing min and max prices
+ * @param tickSpacing
+ * @returns
+ */
+export function calculatePriceRangeFromTickSpacing(tickSpacing: number, mintDecimalsA: number, mintDecimalsB: number) {
+  const { minTickBoundary, maxTickBoundary } = PoolUtils.tickRange(tickSpacing);
+  return {
+    min: calculatePriceFromTick(minTickBoundary, mintDecimalsA, mintDecimalsB, tickSpacing),
+    max: calculatePriceFromTick(maxTickBoundary, mintDecimalsA, mintDecimalsB, tickSpacing),
+  };
+}

+ 6 - 0
src/lib/byreal-clmm-sdk/src/client/apis/commonModels.ts

@@ -0,0 +1,6 @@
+export interface IDynamicFeeResp {
+  extreme: number;
+  high: number;
+  low: number;
+  medium: number;
+}

+ 89 - 0
src/lib/byreal-clmm-sdk/src/client/apis/index.ts

@@ -0,0 +1,89 @@
+import { IDynamicFeeResp } from './commonModels';
+import { createApiInstance, ApiInstance } from './ky';
+import { IPoolsByIdsReq, IPoolsReq, IPoolsResp } from './poolsModels';
+import { IMyPositionsReq, IMyPositionsResp } from './positionModels';
+import { ITick, ITickReq } from './tickModels';
+
+export type Cluster = 'mainnet' | 'devnet';
+
+export const API_URLS = {
+  BASE_HOST: 'https://your-api-host.com',
+  // ========= swap scenario =========
+  // Price inquiry
+  SWAP: 'swap',
+  // ========= Others =========
+
+  // Get pool information
+  POOL_INFO_LIST: 'dex/v1/pools/info/list',
+  POOL_INFO_BY_IDS: 'dex/v1/pools/info/ids',
+
+  // Get user positions
+  MY_POSITIONS: 'dex/v1/position/list',
+
+  // Get tick information
+  TICK: 'router/v1/query-service/list-line-position',
+
+  // Dynamic fees
+  DYNAMIC_FEE: 'dex/v1/main/auto-fee',
+};
+
+export type ApiConfigInfo = Partial<typeof API_URLS>;
+
+export interface IApiParams {
+  cluster: Cluster;
+  timeout: number;
+  urlConfigs: ApiConfigInfo;
+}
+
+export class Api {
+  public cluster: Cluster;
+
+  public api: ApiInstance;
+
+  public urlConfigs: ApiConfigInfo = API_URLS;
+
+  constructor(params?: IApiParams) {
+    const { cluster = 'mainnet', timeout = 10000, urlConfigs } = params || {};
+
+    this.cluster = cluster;
+    this.urlConfigs = {
+      ...API_URLS,
+      ...urlConfigs,
+    };
+
+    this.api = createApiInstance(this.urlConfigs.BASE_HOST || API_URLS.BASE_HOST, timeout);
+  }
+
+  async getPools(params: IPoolsReq) {
+    const data = await this.api.get<IPoolsResp>(`${this.urlConfigs.POOL_INFO_LIST || API_URLS.POOL_INFO_LIST}`, {
+      params,
+    });
+    return data;
+  }
+
+  async getPoolsByIds(params: IPoolsByIdsReq) {
+    const result = await this.api.get<IPoolsResp>(`${this.urlConfigs.POOL_INFO_BY_IDS || API_URLS.POOL_INFO_BY_IDS}`, {
+      params,
+    });
+    return result;
+  }
+
+  async getMyPositions(params: IMyPositionsReq) {
+    const data = await this.api.get<IMyPositionsResp>(`${this.urlConfigs.MY_POSITIONS || API_URLS.MY_POSITIONS}`, {
+      params,
+    });
+    return data;
+  }
+
+  async getTicks(params: ITickReq) {
+    const data = await this.api.get<ITick[]>(this.urlConfigs.TICK || API_URLS.TICK, {
+      params,
+    });
+    return data;
+  }
+
+  async getDynamicFee() {
+    const data = await this.api.get<IDynamicFeeResp>(this.urlConfigs.DYNAMIC_FEE || API_URLS.DYNAMIC_FEE);
+    return data;
+  }
+}

+ 107 - 0
src/lib/byreal-clmm-sdk/src/client/apis/ky.ts

@@ -0,0 +1,107 @@
+import ky, { Input, Options, KyInstance } from 'ky';
+
+export class RequestError extends Error {
+  code?: number;
+  url?: string;
+  constructor(message: string, code?: number, url?: string) {
+    super(message);
+    this.code = code;
+    this.url = url;
+  }
+}
+
+function handleResponseData(data: any, url?: string) {
+  const retCode = data?.ret_code ?? data?.retCode;
+  if (retCode === 0) {
+    return {
+      // FIXME temporary adaptation for swap, will be modified later
+      data: data.result.data || data.result,
+      retCode: data.result.code || retCode,
+      retMsg: data.result.msg,
+      // FIXME temporary adaptation for swap, will be modified later
+      success: data.result.success || true,
+    };
+  } else {
+    throw new RequestError(data?.ret_msg || data?.retMsg || 'Unknown error', data?.ret_code || data?.retCode, url);
+  }
+}
+
+export interface IApiInstanceResp<T> {
+  data: T;
+  retCode: number;
+  retMsg: string;
+  success: boolean;
+}
+
+export type ApiInstance = {
+  get<T>(input: Input, options?: Options & { params?: Record<string, any> }): Promise<IApiInstanceResp<T>>;
+  post<T>(input: Input, options?: Options): Promise<IApiInstanceResp<T>>;
+  put<T>(input: Input, options?: Options): Promise<IApiInstanceResp<T>>;
+  delete<T>(input: Input, options?: Options): Promise<IApiInstanceResp<T>>;
+  _kyInstance: KyInstance;
+  _ky: KyInstance;
+};
+
+/**
+ * 
+ * // GET
+const { data } = await api.get('your/path', { searchParams: { foo: 1 } });
+
+// POST
+const { data } = await api.post('your/path', { json: { foo: 1 } });
+
+// PUT
+const { data } = await api.put('your/path', { json: { foo: 1 } });
+
+// DELETE
+const { data } = await api.delete('your/path');
+ * 
+ * @param baseURL 
+ * @param timeout 
+ * @returns 
+ */
+export function createApiInstance(baseURL: string, timeout = 10000): ApiInstance {
+  const kyInstance = ky.create({
+    prefixUrl: baseURL,
+    timeout,
+    hooks: {
+      afterResponse: [
+        async (_request, _options, response) => {
+          const data = await response.clone().json();
+          const resp = handleResponseData(data, response.url);
+          // Key: wrap into Response object
+          return new Response(JSON.stringify(resp), {
+            status: response.status,
+            statusText: response.statusText,
+            headers: response.headers,
+          });
+        },
+      ],
+    },
+  });
+
+  // Common request method
+  async function request<T>(input: Input, options?: Options): Promise<IApiInstanceResp<T>> {
+    const result = await kyInstance(input, options).json<IApiInstanceResp<T>>();
+    return result;
+  }
+
+  return {
+    get: <T>(input: Input, options?: Options & { params?: Record<string, any> }) => {
+      let finalOptions = { method: 'get', ...options } as Options;
+      if (options?.params) {
+        finalOptions = {
+          ...finalOptions,
+          searchParams: options.params,
+        };
+        delete (finalOptions as any).params;
+      }
+      return request<T>(input, finalOptions);
+    },
+    post: <T>(input: Input, options?: Options) => request<T>(input, { method: 'post', ...options }),
+    put: <T>(input: Input, options?: Options) => request<T>(input, { method: 'put', ...options }),
+    delete: <T>(input: Input, options?: Options) => request<T>(input, { method: 'delete', ...options }),
+    _kyInstance: kyInstance,
+    _ky: ky,
+  };
+}

+ 87 - 0
src/lib/byreal-clmm-sdk/src/client/apis/poolsModels.ts

@@ -0,0 +1,87 @@
+export enum PoolCategory {
+  DEFAULT = 'default',
+  STABLES = 'stables',
+  RESET_LAUNCH_PAD = 'resetLaunchPad',
+}
+export interface PoolReward {
+  address: string;
+  apr: string;
+  dailyAmount: string;
+  decimals: number;
+  endTimestamp: number;
+  logoURI: string;
+  name: string;
+  price: string;
+  symbol: string;
+}
+
+export interface IPoolsReq {
+  // ids?: string[];
+  category?: PoolCategory;
+  sortField?: 'tvl' | 'volumeUsd24h' | 'feeUsd24h' | 'apr24h';
+  sortType?: 'asc' | 'desc';
+  incentiveOnly?: boolean; // todo: field to be determined, need to confirm with server
+  page: number;
+  pageSize: number;
+}
+
+export interface IPoolsResp {
+  list: PoolInfo[];
+  total: number;
+}
+
+export interface IPoolsByIdsReq {
+  ids: string[];
+}
+
+export type IPoolsByIdsResp = IPoolsResp;
+
+export interface Mint {
+  address: string;
+  decimals: number;
+  logoURI: string;
+  name: string;
+  symbol: string;
+  programId: string;
+  price: string;
+  priceChange24h: string;
+  tvl: string;
+  volumeUsd24h: string;
+}
+
+export interface PoolInfo {
+  poolAddress: string;
+  programId: string;
+  feeRate: string;
+  mintA: Mint;
+  mintB: Mint;
+  mintAVaultAddress: string;
+  mintBVaultAddress: string;
+  mintAmountA: string;
+  mintAmountB: string;
+  openTime: string;
+  config: {
+    defaultRange: number;
+    defaultRangePoint: number[];
+    fundFeeRate: string;
+    id: string;
+    index: number;
+    protocolFeeRate: string;
+    tickSpacing: number;
+    tradeFeeRate: string;
+  };
+  apr24h: string;
+  apr24hChange: string;
+  feeUsd24h: string;
+  apr7d: string;
+  feeUsd7d: string;
+  volumeUsd24h: string;
+  tvl: string;
+  ratio: string; // tokenB per tokenA
+  day: {
+    ratioHigh: string;
+    ratioLow: string;
+  };
+  category?: PoolCategory;
+  rewards?: PoolReward[];
+}

+ 22 - 0
src/lib/byreal-clmm-sdk/src/client/apis/positionModels.ts

@@ -0,0 +1,22 @@
+import { PoolInfo } from './poolsModels';
+export interface IMyPositionsReq {
+  userAddress: string;
+}
+
+export interface IMyPositionsResp {
+  positions: IPosition[];
+  poolMap: IPoolMap;
+}
+
+export interface IPosition {
+  activeBurnNft: boolean; // If the NFT is actively destroyed in the case of non-reduction of liquidity, the position is still there, but the deposit tokens cannot be withdrawn
+  lpNftMintAddress: string;
+  lpNftTokenAddress: string;
+  lpProviderAddress: string;
+  personalPositionAddress: string;
+  poolAddress: string;
+}
+
+export interface IPoolMap {
+  [address: string]: PoolInfo;
+}

+ 43 - 0
src/lib/byreal-clmm-sdk/src/client/apis/swapModels.ts

@@ -0,0 +1,43 @@
+/**
+ * Swap request interface, used to handle token swaps
+ *
+ * @property {string} inputMint - Input token contract address
+ * @property {string} outputMint - Output token contract address
+ * @property {string} amount - Swap amount, unit is Lamport
+ * @property {('in'|'out')} swapMode - Swap mode, 'in' means fixed input amount, 'out' means fixed output amount
+ * @property {string} slippageBps - Slippage tolerance, in basis points (1 basis point = 0.01%)
+ * @property {string} computeUnitPriceMicroLamports - Compute unit price, unit is microLamport
+ * @property {string} [quoteOnly] - Whether to only get quotes without executing transactions
+ * @property {string} [userPublicKey] - User wallet address
+ */
+export interface ISwapRequest {
+  inputMint: string;
+  outputMint: string;
+  amount: string;
+  swapMode: 'in' | 'out';
+  slippageBps: string;
+  computeUnitPriceMicroLamports: string;
+  quoteOnly?: string;
+  userPublicKey?: string;
+}
+
+/**
+ * 交换响应接口,包含交换结果信息
+ *
+ * @property {string} [transaction] - 构建后的交易编码
+ * @property {string} inputMint - 输入代币合约地址
+ * @property {string} outputMint - 输出代币合约地址
+ * @property {string} outMount - 输出数量
+ * @property {string} otherAmountThreshold - 最少收到数量
+ * @property {number} priceImpactPct - 价格影响百分比
+ * @property {string} inMount - 输入数量
+ */
+export interface ISwapResponse {
+  transaction?: string;
+  inputMint: string;
+  outputMint: string;
+  outMount: string;
+  otherAmountThreshold: string;
+  priceImpactPct: number;
+  inMount: string;
+}

+ 10 - 0
src/lib/byreal-clmm-sdk/src/client/apis/tickModels.ts

@@ -0,0 +1,10 @@
+export interface ITickReq {
+  poolId: string;
+}
+
+export interface ITick {
+  tick: number;
+  price: string;
+  originalPrice: string;
+  liquidity: string;
+}

+ 1371 - 0
src/lib/byreal-clmm-sdk/src/client/chain/index.ts

@@ -0,0 +1,1371 @@
+import {
+  TOKEN_PROGRAM_ID,
+  NATIVE_MINT,
+  createInitializeAccountInstruction,
+  createCloseAccountInstruction,
+  createAssociatedTokenAccountIdempotentInstruction,
+  AccountLayout,
+  MintLayout,
+  TOKEN_2022_PROGRAM_ID,
+} from '@solana/spl-token';
+import { Connection, PublicKey, VersionedTransaction, SystemProgram, TransactionInstruction } from '@solana/web3.js';
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+import { BYREAL_CLMM_PROGRAM_ID, U64_IGNORE_RANGE } from '../../constants';
+import {
+  IPoolLayoutWithId,
+  IPersonalPositionLayout,
+  PositionUtils,
+  RawDataUtils,
+  SqrtPriceMath,
+  TickArrayLayout,
+  TickMath,
+  TickUtils,
+  Instruction,
+  getATAAddress,
+  ITokenInfo,
+  getPdaMintExAccount,
+  getPdaTickArrayAddress,
+  PoolUtils,
+  getTickArrayBitmapExtension,
+  getTickArrayInfo,
+  getPdaExBitmapAccount,
+  MIN_SQRT_PRICE_X64,
+  MAX_SQRT_PRICE_X64,
+  TickArrayUtils,
+  TickArrayContainer,
+} from '../../instructions/index';
+import { generatePubKey } from '../../utils/generatePubKey';
+import { makeTransaction, sendTransaction, estimateComputeUnits, DEFAULT_COMPUTE_UNIT_PRICE } from '../../utils/index';
+
+import {
+  IAddLiquidityParams,
+  IClosePositionParams,
+  ICollectFeesParams,
+  ICreatePoolParams,
+  IQuoteSwapExactOutParams,
+  IQuoteSwapExactOutReturn,
+  ISwapExactOutParams,
+  ICreatePositionParams,
+  IDecreaseFullLiquidityParams,
+  IDecreaseLiquidityParams,
+  IInstructionReturn,
+  ParamsWithSignerCallback,
+  ICollectAllFeesParams,
+  SignerCallback,
+  IGetPositionInfoByNftMintReturn,
+  ICalculateCreatePositionFee,
+  IQouteSwapParams,
+  IQouteSwapReturn,
+  ISwapParams,
+} from './models';
+import {
+  alignPriceToTickPrice,
+  calculateApr,
+  calculateRewardApr,
+  calculateRangeAprs,
+  getAmountAFromAmountB,
+  getAmountBFromAmountA,
+  getTokenProgramId,
+} from './utils';
+
+/*
+ * Chain class: Encapsulates chain-level operations related to CLMM (Concentrated Liquidity Market Maker)
+
+ * Includes position, pool, token information retrieval, liquidity operations, fee collection, etc.
+ * Mainly depends on Solana web3.js, @solana/spl-token instruction tools
+ */
+export class Chain {
+  public connection: Connection;
+  public programId: PublicKey;
+  // Cache rent fee calculation results
+  private rentFeeCache: { [key: number]: number } = {};
+
+  /**
+   * Constructor
+   * @param params.connection Solana chain connection object
+   * @param params.programId CLMM program ID, default is CLMM_PROGRAM_ID
+   */
+  constructor(params: { connection: Connection; programId?: PublicKey }) {
+    const { connection, programId = BYREAL_CLMM_PROGRAM_ID } = params;
+    this.connection = connection;
+    this.programId = programId;
+  }
+
+  /**
+   * Get all CLMM position information for a specified account
+   * @param userAddress User wallet address
+   * @returns Promise<IPersonalPositionLayout[]> Position information list
+   */
+  public async getRawPositionInfoListByUserAddress(userAddress: PublicKey): Promise<IPersonalPositionLayout[]> {
+    return RawDataUtils.getRawPositionInfoListByUserAddress({
+      connection: this.connection,
+      programId: this.programId,
+      userAddress,
+    });
+  }
+
+  /**
+   * Get the corresponding position information based on the NFT mint address
+   * @param nftMint NFT mint address
+   * @returns Promise<IPersonalPositionLayout | null> Position information
+   */
+  public async getRawPositionInfoByNftMint(nftMint: PublicKey): Promise<IPersonalPositionLayout | null> {
+    return RawDataUtils.getRawPositionInfoByNftMint({
+      connection: this.connection,
+      programId: this.programId,
+      nftMint,
+    });
+  }
+
+  /**
+   * Get the corresponding pool information based on the pool address
+   * @param poolId Pool address or PublicKey
+   * @returns Promise<IPoolLayoutWithId> Pool information
+   */
+  public async getRawPoolInfoByPoolId(poolId: string | PublicKey): Promise<IPoolLayoutWithId> {
+    const poolInfo = await RawDataUtils.getRawPoolInfoByPoolId({
+      connection: this.connection,
+      poolId,
+    });
+    if (!poolInfo) throw new Error(`pool info not found, poolId: ${String(poolId)}`);
+    return poolInfo;
+  }
+
+  /**
+   * Get the corresponding token information based on the token mint address
+   * @param mintAddress Token mint address
+   * @returns Promise<...> Token information
+   */
+  public async getRawTokenInfoByMint(
+    mintAddress: PublicKey,
+  ): Promise<(ReturnType<typeof MintLayout.decode> & { owner: PublicKey }) | null> {
+    const tokenInfo = await RawDataUtils.getRawTokenInfoByMint({
+      connection: this.connection,
+      mintAddress,
+    });
+    if (!tokenInfo) throw new Error(`token info not found, mintAddress: ${String(mintAddress)}`);
+    return tokenInfo;
+  }
+
+  /**
+   * Get the simplified token information (including address, precision, and programId)
+   * @param mintAddress Token mint address
+   * @returns Promise<ITokenInfo>
+   */
+  public async getTokenInfoByMint(mintAddress: PublicKey): Promise<ITokenInfo> {
+    const tokenInfo = await this.getRawTokenInfoByMint(mintAddress);
+    if (!tokenInfo) throw new Error(`token info not found, mintAddress: ${String(mintAddress)}`);
+    return {
+      address: mintAddress.toBase58(),
+      decimals: tokenInfo.decimals,
+      programId: tokenInfo.owner.toBase58(),
+    };
+  }
+
+  /**
+   * Get the detailed position information, including price range, token amount, fee, etc.
+   * @param nftMint NFT mint address
+   * @returns Promise<{...}> Detailed position information
+   */
+  public async getPositionInfoByNftMint(nftMint: PublicKey): Promise<IGetPositionInfoByNftMintReturn | null> {
+    const rawPositionInfo = await this.getRawPositionInfoByNftMint(nftMint);
+    if (!rawPositionInfo) return null;
+    const rawPoolInfo = await this.getRawPoolInfoByPoolId(rawPositionInfo.poolId);
+    const { mintDecimalsA, mintDecimalsB, tickSpacing, programId } = rawPoolInfo;
+
+    // Calculate the price corresponding to tickLower/tickUpper
+    const priceLower = TickMath.getPriceFromTick({
+      tick: rawPositionInfo.tickLower,
+      decimalsA: mintDecimalsA,
+      decimalsB: mintDecimalsB,
+    });
+    const priceUpper = TickMath.getPriceFromTick({
+      tick: rawPositionInfo.tickUpper,
+      decimalsA: mintDecimalsA,
+      decimalsB: mintDecimalsB,
+    });
+    // Calculate the actual token amount held in the position
+    const { amountA, amountB } = PositionUtils.getAmountsFromLiquidity({
+      poolInfo: rawPoolInfo,
+      ownerPosition: rawPositionInfo,
+      liquidity: rawPositionInfo.liquidity,
+      slippage: 0,
+      add: false,
+      epochInfo: await this.connection.getEpochInfo(),
+    });
+    // Calculate the amount of tokens displayed on the UI
+    const [pooledAmountA, pooledAmountB] = [
+      new Decimal(amountA.amount.toString()).div(10 ** mintDecimalsA),
+      new Decimal(amountB.amount.toString()).div(10 ** mintDecimalsB),
+    ];
+    // Get the tickArray address
+    const [tickLowerArrayAddress, tickUpperArrayAddress] = [
+      TickUtils.getTickArrayAddressByTick(
+        new PublicKey(programId),
+        new PublicKey(rawPositionInfo.poolId),
+        rawPositionInfo.tickLower,
+        tickSpacing,
+      ),
+      TickUtils.getTickArrayAddressByTick(
+        new PublicKey(programId),
+        new PublicKey(rawPositionInfo.poolId),
+        rawPositionInfo.tickUpper,
+        tickSpacing,
+      ),
+    ];
+    // Get the tickArray data
+    const tickArrayRes = await this.connection.getMultipleAccountsInfo([tickLowerArrayAddress, tickUpperArrayAddress]);
+    if (!tickArrayRes[0] || !tickArrayRes[1]) throw new Error('tick data not found');
+
+    // Parse as containers (supports both fixed and dynamic tick arrays)
+    const tickArrayLowerContainer = TickArrayUtils.parseTickArrayContainer(tickArrayRes[0].data, tickLowerArrayAddress);
+    const tickArrayUpperContainer = TickArrayUtils.parseTickArrayContainer(tickArrayRes[1].data, tickUpperArrayAddress);
+
+    // Get the tick state using container helper
+    const tickLowerState = TickArrayUtils.getTickStateFromContainer(
+      tickArrayLowerContainer,
+      rawPositionInfo.tickLower,
+      rawPoolInfo.tickSpacing,
+    );
+    const tickUpperState = TickArrayUtils.getTickStateFromContainer(
+      tickArrayUpperContainer,
+      rawPositionInfo.tickUpper,
+      rawPoolInfo.tickSpacing,
+    );
+
+    // Validate tick states
+    if (!tickLowerState || !tickUpperState) {
+      throw new Error('Tick state not found in tick array');
+    }
+
+    // Calculate the fee (original logic unchanged)
+    const tokenFees = PositionUtils.getPositionFees(rawPoolInfo, rawPositionInfo, tickLowerState, tickUpperState);
+    // Filter out abnormal fees
+    const [tokenFeeAmountA, tokenFeeAmountB] = [
+      tokenFees.tokenFeeAmountA.gte(new BN(0)) && tokenFees.tokenFeeAmountA.lt(U64_IGNORE_RANGE)
+        ? tokenFees.tokenFeeAmountA
+        : new BN(0),
+      tokenFees.tokenFeeAmountB.gte(new BN(0)) && tokenFees.tokenFeeAmountB.lt(U64_IGNORE_RANGE)
+        ? tokenFees.tokenFeeAmountB
+        : new BN(0),
+    ];
+    return {
+      priceLower,
+      priceUpper,
+      uiPriceLower: priceLower.toFixed(mintDecimalsA),
+      uiPriceUpper: priceUpper.toFixed(mintDecimalsB),
+      tokenA: {
+        address: rawPoolInfo.mintA,
+        decimals: rawPoolInfo.mintDecimalsA,
+        amount: amountA.amount,
+        feeAmount: tokenFeeAmountA,
+        uiAmount: pooledAmountA.toString(),
+        uiFeeAmount: new Decimal(tokenFeeAmountA.toString())
+          .dividedBy(new Decimal(10).pow(mintDecimalsA))
+          .toFixed(mintDecimalsA),
+      },
+      tokenB: {
+        address: rawPoolInfo.mintB,
+        decimals: rawPoolInfo.mintDecimalsB,
+        amount: amountB.amount,
+        feeAmount: tokenFeeAmountB,
+        uiAmount: pooledAmountB.toString(),
+        uiFeeAmount: new Decimal(tokenFeeAmountB.toString())
+          .dividedBy(new Decimal(10).pow(mintDecimalsB))
+          .toFixed(mintDecimalsB),
+      },
+      rawPositionInfo,
+      rawPoolInfo,
+    };
+  }
+
+  /**
+   * Create position instructions on the chain (does not directly send transactions)
+   * @param params Parameters required for creating a position
+   * @returns IInstructionReturn Contains instructions, signers, and transaction objects
+   */
+  public async createPositionInstructions(params: ICreatePositionParams): Promise<IInstructionReturn> {
+    const { userAddress, poolInfo, tickLower, tickUpper, base, baseAmount, otherAmountMax, transactionOptions } =
+      params;
+    const { mintA, mintB } = poolInfo;
+
+    // Calculate the actual required tokenA/B amount
+    const amountA = base === 'MintA' ? baseAmount : otherAmountMax;
+    const amountB = base === 'MintB' ? baseAmount : otherAmountMax;
+
+    const { tokenAccountA, tokenAccountB, preInstructions, endInstructions } = await this.handleTokenAccount({
+      userAddress,
+      mintA,
+      mintB,
+      amountA,
+      amountB,
+    });
+
+    // Generate position creation instructions
+    const { instructions: positionInstructions, signers: positionSigners } =
+      await Instruction.openPositionFromBaseInstruction({
+        poolInfo,
+        ownerInfo: {
+          feePayer: userAddress,
+          wallet: userAddress,
+          tokenAccountA,
+          tokenAccountB,
+        },
+        tickLower,
+        tickUpper,
+        base,
+        baseAmount,
+        otherAmountMax,
+        withMetadata: 'create',
+      });
+
+    // Merge all instructions: ATA creation → pre → position → end
+    const instructions = [
+      ...preInstructions, // SOL/WSOL handling
+      ...positionInstructions, // Position creation
+      ...endInstructions, // Cleanup
+    ];
+
+    const signers = [...positionSigners];
+
+    // Construct transaction object
+    const transaction = await makeTransaction({
+      connection: this.connection,
+      payerPublicKey: userAddress,
+      instructions,
+      signers,
+      options: transactionOptions,
+    });
+
+    return {
+      instructions,
+      signers,
+      nftAddress: positionSigners[0].publicKey.toString(),
+      transaction,
+    };
+  }
+
+  /**
+   * Calculate the rent fee required for creating a position
+   *
+   * @param params Parameters required for creating a position and options
+   */
+  public async calculateCreatePositionFee(
+    params: ICreatePositionParams & {
+      useCache?: boolean; // Whether to use cache, if true, the rent fee will not be recalculated through rpc; default is true
+    },
+  ): Promise<ICalculateCreatePositionFee> {
+    const {
+      userAddress,
+      poolInfo,
+      tickLower,
+      tickUpper,
+      base,
+      baseAmount,
+      otherAmountMax,
+      transactionOptions,
+      useCache = true,
+    } = params;
+
+    const computeUnitPrice = transactionOptions?.computeUnitPrice || DEFAULT_COMPUTE_UNIT_PRICE;
+
+    // Define account size constants (this is fixed hardcode)
+    const ACCOUNT_SIZES = {
+      NFT_MINT: 480,
+      NFT_HOLDER: 170,
+      PERSONAL_POSITION: 281,
+      TICK_ARRAY: 10240,
+    };
+
+    const { programId, poolId, tickSpacing } = poolInfo;
+    const tickArrayLowerStartIndex = TickUtils.getTickArrayStartIndexByTick(tickLower, tickSpacing);
+    const tickArrayUpperStartIndex = TickUtils.getTickArrayStartIndexByTick(tickUpper, tickSpacing);
+    const { publicKey: tickArrayLower } = getPdaTickArrayAddress(programId, poolId, tickArrayLowerStartIndex);
+    const { publicKey: tickArrayUpper } = getPdaTickArrayAddress(programId, poolId, tickArrayUpperStartIndex);
+
+    const [
+      blockhashData,
+      tickArrayLowerInfo,
+      tickArrayUpperInfo,
+      nftMintRentLamports,
+      nftHolderRentLamports,
+      personalPositionRentLamports,
+      tickArrayRentLamports,
+    ] = await Promise.all([
+      this.connection.getLatestBlockhash(),
+      this.connection.getAccountInfo(tickArrayLower),
+      this.connection.getAccountInfo(tickArrayUpper),
+      this.estimateRentFee(ACCOUNT_SIZES.NFT_MINT, useCache),
+      this.estimateRentFee(ACCOUNT_SIZES.NFT_HOLDER, useCache),
+      this.estimateRentFee(ACCOUNT_SIZES.PERSONAL_POSITION, useCache),
+      this.estimateRentFee(ACCOUNT_SIZES.TICK_ARRAY, useCache),
+    ]);
+
+    // Check if the tick array exists
+    const isTickArrayLowerExists = !!tickArrayLowerInfo;
+    const isTickArrayUpperExists = !!tickArrayUpperInfo;
+
+    // Prepare instructions to estimate compute units
+    const { mintA, mintB } = poolInfo;
+    const amountA = base === 'MintA' ? baseAmount : otherAmountMax;
+    const amountB = base === 'MintB' ? baseAmount : otherAmountMax;
+
+    const { tokenAccountA, tokenAccountB, preInstructions, endInstructions } = await this.handleTokenAccount({
+      userAddress,
+      mintA,
+      mintB,
+      amountA,
+      amountB,
+    });
+
+    let positionInstructions: TransactionInstruction[] = [];
+
+    // Try to generate position creation instructions
+    try {
+      const { instructions } = await Instruction.openPositionFromBaseInstruction({
+        poolInfo,
+        ownerInfo: {
+          feePayer: userAddress,
+          wallet: userAddress,
+          tokenAccountA,
+          tokenAccountB,
+        },
+        tickLower,
+        tickUpper,
+        base,
+        baseAmount,
+        otherAmountMax,
+        withMetadata: 'create',
+      });
+      positionInstructions = instructions;
+    } catch {
+      // console.error('error ==> ', error);
+    }
+
+    // Estimate compute units and transaction fees
+    const computeUnits = await estimateComputeUnits(
+      this.connection,
+      [...preInstructions, ...positionInstructions, ...endInstructions],
+      userAddress,
+      blockhashData.blockhash,
+    );
+
+    // 1 SOL = 10^9 lamports, 1 lamport = 10^6 microLamports
+    const transactionNetFee = (computeUnits * computeUnitPrice) / 10 ** 15;
+
+    const refundableFees = (nftMintRentLamports + nftHolderRentLamports + personalPositionRentLamports) / 10 ** 9;
+
+    // Unrefundable fees include transaction fees and newly created shared tick array accounts
+    let createTickFee = 0;
+
+    if (!isTickArrayLowerExists) {
+      createTickFee += tickArrayRentLamports / 10 ** 9;
+    }
+    if (!isTickArrayUpperExists) {
+      createTickFee += tickArrayRentLamports / 10 ** 9;
+    }
+
+    return {
+      unRefundableFees: transactionNetFee + createTickFee, // Unrefundable fees
+      transactionNetFee, // Transaction network fee
+      refundableFees, // Refundable fees
+      createTickFee, // Create tick array account fee
+    };
+  }
+
+  /**
+   * Create a new position and send a transaction
+   * @param params Parameters required for creating a position, including a signature callback
+   * @returns Promise<string> Transaction signature
+   */
+  public async createPosition(params: ParamsWithSignerCallback<ICreatePositionParams>): Promise<string> {
+    const { signerCallback } = params;
+    const { transaction } = await this.createPositionInstructions(params);
+    return sendTransaction({
+      connection: this.connection,
+      signTx: () => signerCallback(transaction),
+    });
+  }
+
+  /**
+   * Close the specified position (only when the liquidity is 0, it can be closed)
+   * @param params.userAddress User wallet address
+   * @param params.nftMint NFT mint address
+   * @returns IInstructionReturn Contains instructions and transaction objects
+   */
+  public async closePositionInstructions(params: IClosePositionParams): Promise<IInstructionReturn> {
+    const { userAddress, nftMint } = params;
+    // Get position detailed information
+    const positionInfo = await this.getPositionInfoByNftMint(nftMint);
+    if (!positionInfo) throw new Error('Position not found');
+    const mintA = positionInfo.rawPoolInfo.mintA;
+    const mintB = positionInfo.rawPoolInfo.mintB;
+    // Handle SOL/WSOL packaging
+    const { preInstructions, endInstructions } = await this.handleTokenAccount({
+      userAddress,
+      mintA,
+      mintB,
+    });
+    // Generate close position instructions
+    const { instructions: closeInstructions } = await Instruction.closePositionInstruction({
+      programId: this.programId,
+      nftMint,
+      ownerWallet: userAddress,
+    });
+    // Merge all instructions
+    const instructions = [...preInstructions, ...closeInstructions, ...endInstructions];
+    // Construct transaction object
+    const transaction = await makeTransaction({
+      connection: this.connection,
+      payerPublicKey: userAddress,
+      instructions,
+    });
+    return {
+      instructions,
+      transaction,
+    };
+  }
+
+  /**
+   * Close the specified position and send a transaction
+   * @param params.userAddress User wallet address
+   * @param params.nftMint NFT mint address
+   * @param params.signerCallback Signature callback
+   * @returns Promise<string> Transaction signature
+   */
+  public async closePosition(params: {
+    userAddress: PublicKey;
+    nftMint: PublicKey;
+    signerCallback: SignerCallback;
+  }): Promise<string> {
+    const { userAddress, nftMint, signerCallback } = params;
+    const { transaction } = await this.closePositionInstructions({
+      userAddress,
+      nftMint,
+    });
+    return sendTransaction({
+      connection: this.connection,
+      signTx: () => signerCallback(transaction),
+    });
+  }
+
+  /**
+   * Partially remove position liquidity, generate chain instructions
+   * @param params Contains user, position, removed liquidity amount, slippage, etc.
+   * @returns IInstructionReturn
+   */
+  public async decreaseLiquidityInstructions(params: IDecreaseLiquidityParams): Promise<IInstructionReturn> {
+    // Slippage is set to 2% by default
+    const { userAddress, nftMint, liquidity, slippage = 0.02 } = params;
+    // Get position raw information
+    const positionInfo = await this.getRawPositionInfoByNftMint(nftMint);
+    if (!positionInfo) throw new Error('Position not found');
+    // Check if the removed liquidity amount is valid
+    if (liquidity.gt(positionInfo.liquidity)) throw new Error('Liquidity is greater than position liquidity');
+    // Get pool information
+    const poolInfo = await this.getRawPoolInfoByPoolId(positionInfo.poolId);
+    // Handle SOL/WSOL packaging
+    const { tokenAccountA, tokenAccountB, preInstructions, endInstructions } = await this.handleTokenAccount({
+      userAddress,
+      mintA: poolInfo.mintA,
+      mintB: poolInfo.mintB,
+    });
+    // Calculate the expected token amount after removing liquidity (considering slippage)
+    const { amountSlippageA, amountSlippageB } = PositionUtils.getAmountsFromLiquidity({
+      poolInfo,
+      ownerPosition: positionInfo,
+      liquidity,
+      slippage,
+      add: false,
+      epochInfo: await this.connection.getEpochInfo(),
+    });
+    // Calculate the minimum accepted token amount
+    const amountMinA = amountSlippageA.amount;
+    const amountMinB = amountSlippageB.amount;
+    // Generate remove liquidity instructions
+    const { instructions: decreaseInstructions } = await Instruction.decreaseLiquidityInstructions({
+      poolInfo,
+      ownerPosition: positionInfo,
+      ownerInfo: {
+        wallet: userAddress,
+        tokenAccountA,
+        tokenAccountB,
+      },
+      liquidity,
+      amountMinA,
+      amountMinB,
+    });
+    // Merge all instructions
+    const instructions = [...preInstructions, ...decreaseInstructions, ...endInstructions];
+    // Construct transaction object
+    const transaction = await makeTransaction({
+      connection: this.connection,
+      payerPublicKey: userAddress,
+      instructions,
+    });
+    return {
+      instructions,
+      transaction,
+    };
+  }
+
+  /**
+   * Partially remove position liquidity and send a transaction
+   * @param params Contains signature callback, etc.
+   * @returns Promise<string> Transaction signature
+   */
+  public async decreaseLiquidity(params: ParamsWithSignerCallback<IDecreaseLiquidityParams>): Promise<string> {
+    const { signerCallback } = params;
+    const { transaction } = await this.decreaseLiquidityInstructions(params);
+    return sendTransaction({
+      connection: this.connection,
+      signTx: () => signerCallback(transaction),
+    });
+  }
+
+  /**
+   * Remove all position liquidity (optional to automatically close position)
+   * @param params.closePosition Whether to close position automatically
+   * @param params Other parameters are the same as decreaseLiquidityInstructions
+   * @returns IInstructionReturn
+   */
+  public async decreaseFullLiquidityInstructions(params: IDecreaseFullLiquidityParams): Promise<IInstructionReturn> {
+    // Slippage is set to 2% by default
+    const { userAddress, nftMint, closePosition = true, slippage = 0.02 } = params;
+    // Get position raw information
+    const positionInfo = await this.getRawPositionInfoByNftMint(nftMint);
+    if (!positionInfo) throw new Error('Position not found');
+    // Get pool information
+    const poolInfo = await this.getRawPoolInfoByPoolId(positionInfo.poolId);
+    // Handle SOL/WSOL packaging
+    const { tokenAccountA, tokenAccountB, preInstructions, endInstructions } = await this.handleTokenAccount({
+      userAddress,
+      mintA: poolInfo.mintA,
+      mintB: poolInfo.mintB,
+    });
+    // Use all position liquidity
+    const liquidity = positionInfo.liquidity;
+    // Calculate the expected token amount after removing liquidity (considering slippage)
+    const { amountSlippageA, amountSlippageB } = PositionUtils.getAmountsFromLiquidity({
+      poolInfo,
+      ownerPosition: positionInfo,
+      liquidity,
+      slippage,
+      add: false, // When reducing liquidity, set to false
+      epochInfo: await this.connection.getEpochInfo(),
+    });
+    // Minimum token amount after slippage adjustment
+    const amountMinA = amountSlippageA.amount;
+    const amountMinB = amountSlippageB.amount;
+    // Generate remove liquidity instructions
+    const { instructions: decreaseInstructions } = await Instruction.decreaseLiquidityInstructions({
+      poolInfo,
+      ownerPosition: positionInfo,
+      ownerInfo: {
+        wallet: userAddress,
+        tokenAccountA,
+        tokenAccountB,
+      },
+      liquidity,
+      amountMinA,
+      amountMinB,
+    });
+    // Merge all instructions
+    const instructions = [...preInstructions, ...decreaseInstructions, ...endInstructions];
+    // If you need to close the position, append the close instruction
+    if (closePosition) {
+      const { instructions: closeInstructions } = await Instruction.closePositionInstruction({
+        programId: this.programId,
+        nftMint,
+        ownerWallet: userAddress,
+      });
+      instructions.push(...closeInstructions);
+    }
+    // Construct transaction object
+    const transaction = await makeTransaction({
+      connection: this.connection,
+      payerPublicKey: userAddress,
+      instructions,
+    });
+    return {
+      instructions,
+      transaction,
+    };
+  }
+
+  /**
+   * Remove all position liquidity and send a transaction
+   * @param params Contains signature callback, etc.
+   * @returns Promise<string> Transaction signature
+   */
+  public async decreaseFullLiquidity(params: ParamsWithSignerCallback<IDecreaseFullLiquidityParams>): Promise<string> {
+    const { signerCallback } = params;
+    const { transaction } = await this.decreaseFullLiquidityInstructions(params);
+    return sendTransaction({
+      connection: this.connection,
+      signTx: () => signerCallback(transaction),
+    });
+  }
+
+  /**
+   * Collect fees for a single position (essentially removing 0 liquidity)
+   * @param params Contains user, position, etc.
+   * @returns IInstructionReturn
+   */
+  public async collectFeesInstructions(params: ICollectFeesParams): Promise<IInstructionReturn> {
+    const { userAddress, nftMint } = params;
+    // Reuse the decreaseLiquidity function, pass in liquidity as 0
+    return await this.decreaseLiquidityInstructions({
+      userAddress,
+      nftMint,
+      liquidity: new BN(0),
+    });
+  }
+
+  /**
+   * Collect fees for all positions of a user, automatically batch to avoid exceeding transaction size limit
+   * @param params.userAddress User wallet address
+   * @param params.nftMintList NFT mint list
+   * @returns { instructionsList, transactions } Batch instructions and transaction objects
+   */
+  public async collectAllPositionFeesInstructions(params: ICollectAllFeesParams): Promise<{
+    instructionsList: TransactionInstruction[][];
+    transactions: VersionedTransaction[];
+  }> {
+    const { userAddress, nftMintList } = params;
+    // Cache pool information to avoid duplicate requests
+    const poolInfoMap: Map<string, IPoolLayoutWithId> = new Map();
+    const allInstructions: TransactionInstruction[][] = [];
+    // let currentInstructions: TransactionInstruction[] = [];
+    // Get rent exemption lamports
+    const rentExemptLamports = await this.estimateRentFee(AccountLayout.span);
+    for (const nftMint of nftMintList) {
+      try {
+        const positionInfo = await this.getRawPositionInfoByNftMint(nftMint);
+        if (!positionInfo) throw new Error(`Position not found: ${nftMint.toBase58()}`);
+        const poolId = positionInfo.poolId.toBase58();
+        // Get or cache pool information
+        if (!poolInfoMap.has(poolId)) {
+          poolInfoMap.set(poolId, await this.getRawPoolInfoByPoolId(positionInfo.poolId));
+        }
+        const poolInfo = poolInfoMap.get(poolId);
+        if (!poolInfo) throw new Error(`Pool not found: ${poolId}`);
+        // Handle SOL/WSOL related
+        const { tokenAccountA, tokenAccountB, preInstructions, endInstructions } = await this.handleTokenAccount({
+          userAddress,
+          mintA: poolInfo.mintA,
+          mintB: poolInfo.mintB,
+          rentExemptLamports,
+        });
+        // Generate instructions to collect fees (essentially removing 0 liquidity)
+        const { instructions: decreaseInstructions } = await Instruction.decreaseLiquidityInstructions({
+          poolInfo,
+          ownerPosition: positionInfo,
+          ownerInfo: {
+            wallet: userAddress,
+            tokenAccountA,
+            tokenAccountB,
+          },
+          liquidity: new BN(0),
+          amountMinA: new BN(0),
+          amountMinB: new BN(0),
+        });
+        // All instructions for the current NFT
+        const nftInstructions = [...preInstructions, ...decreaseInstructions, ...endInstructions];
+
+        // Check if adding this NFT's instructions will exceed the transaction size limit
+        // const tempInstructions = [...currentInstructions, ...nftInstructions];
+        // const isValidSize = checkV0TxSize({
+        //   instructions: tempInstructions,
+        //   payer: userAddress,
+        // });
+        // if (!isValidSize && currentInstructions.length > 0) {
+        // If adding will exceed the limit and there are existing instructions, save the current batch and start a new batch
+        allInstructions.push(nftInstructions);
+        // currentInstructions = nftInstructions;
+        // } else {
+        //   // If it doesn't exceed the limit or the current batch is empty, add to the current batch
+        //   currentInstructions.push(...nftInstructions);
+        // }
+      } catch (error) {
+        console.error('[collectAllPositionFeesInstructions] Collect position fees failed:', {
+          nftMint: nftMint.toBase58(),
+          error,
+        });
+      }
+    }
+    // Ensure the last batch of instructions is also added
+    // if (currentInstructions.length > 0) {
+    //   allInstructions.push(currentInstructions);
+    // }
+    // Create a transaction for each batch of instructions
+    const transactions: VersionedTransaction[] = [];
+    for (const instructions of allInstructions) {
+      try {
+        const transaction = await makeTransaction({
+          connection: this.connection,
+          payerPublicKey: userAddress,
+          instructions,
+        });
+        transactions.push(transaction);
+      } catch (error) {
+        console.error('[collectAllPositionFeesInstructions] Create transaction failed:', error);
+      }
+    }
+    return { instructionsList: allInstructions, transactions };
+  }
+
+  /**
+   * Collect fees for a single position and send a transaction
+   * @param params Contains signature callback, etc.
+   * @returns Promise<string> Transaction signature
+   */
+  public async collectFees(params: ParamsWithSignerCallback<ICollectFeesParams>): Promise<string> {
+    try {
+      const { signerCallback } = params;
+      const { transaction } = await this.collectFeesInstructions(params);
+      return await sendTransaction({
+        connection: this.connection,
+        signTx: async () => await signerCallback(transaction),
+      });
+    } catch (error) {
+      console.warn('collectFees failed:', error);
+      throw error;
+    }
+  }
+
+  /**
+   * Add liquidity to an existing position, generate chain instructions
+   * @param params Contains user, position, liquidity amount, etc.
+   * @returns IInstructionReturn
+   */
+  public async addLiquidityInstructions(params: IAddLiquidityParams): Promise<IInstructionReturn> {
+    const { userAddress, nftMint, base, baseAmount, otherAmountMax, computeBudgetOptions = {} } = params;
+    // Get position raw information
+    const ownerPosition = await this.getRawPositionInfoByNftMint(nftMint);
+    if (!ownerPosition) throw new Error('Position not found');
+    // Get pool information
+    const poolInfo = await this.getRawPoolInfoByPoolId(ownerPosition.poolId);
+    // Calculate the required amount
+    const amountA = base === 'MintA' ? baseAmount : otherAmountMax;
+    const amountB = base === 'MintB' ? baseAmount : otherAmountMax;
+    // Handle SOL/WSOL packaging
+    const { tokenAccountA, tokenAccountB, preInstructions, endInstructions } = await this.handleTokenAccount({
+      userAddress,
+      mintA: poolInfo.mintA,
+      mintB: poolInfo.mintB,
+      amountA,
+      amountB,
+    });
+    // Generate add liquidity instructions
+    const { instructions: increaseInstructions } = await Instruction.increasePositionFromBaseInstructions({
+      poolInfo,
+      ownerPosition,
+      ownerInfo: {
+        wallet: userAddress,
+        tokenAccountA,
+        tokenAccountB,
+      },
+      base,
+      baseAmount,
+      otherAmountMax,
+    });
+    // Merge all instructions
+    const instructions = [...preInstructions, ...increaseInstructions, ...endInstructions];
+    // Construct transaction object
+    const transaction = await makeTransaction({
+      connection: this.connection,
+      payerPublicKey: userAddress,
+      instructions,
+      options: {
+        ...computeBudgetOptions,
+      },
+    });
+    return {
+      instructions,
+      transaction,
+    };
+  }
+
+  /**
+   * Add liquidity to an existing position and send a transaction
+   * @param params Contains signature callback, etc.
+   * @returns Promise<string> Transaction signature
+   */
+  public async addLiquidity(params: ParamsWithSignerCallback<IAddLiquidityParams>): Promise<string> {
+    const { signerCallback } = params;
+    const { transaction } = await this.addLiquidityInstructions(params);
+    return sendTransaction({
+      connection: this.connection,
+      signTx: () => signerCallback(transaction),
+    });
+  }
+
+  /**
+   * Create pool instructions
+   * @param params Create pool parameters
+   * @returns IInstructionReturn
+   */
+  public async createPoolInstructions(params: ICreatePoolParams): Promise<IInstructionReturn> {
+    const { userAddress, poolManager, openTime, mintA, mintB, ammConfigId, initialPrice } = params;
+    // Convert price to BN format
+    const initialPriceDecimal = new Decimal(initialPrice);
+    const initialPriceX64 = SqrtPriceMath.priceToSqrtPriceX64(initialPriceDecimal, mintA.decimals, mintB.decimals);
+    const mintAAddress = new PublicKey(mintA.address);
+    const mintBAddress = new PublicKey(mintB.address);
+    // Handle Token-2022 extension account
+    const extendMintAccount: PublicKey[] = [];
+    const fetchAccounts: PublicKey[] = [];
+    if (mintA.programId === TOKEN_2022_PROGRAM_ID.toBase58()) {
+      fetchAccounts.push(getPdaMintExAccount(this.programId, mintAAddress).publicKey);
+    }
+    if (mintB.programId === TOKEN_2022_PROGRAM_ID.toBase58()) {
+      fetchAccounts.push(getPdaMintExAccount(this.programId, mintBAddress).publicKey);
+    }
+
+    // Verify account existence
+    if (fetchAccounts.length > 0) {
+      const extMintRes = await this.connection.getMultipleAccountsInfo(fetchAccounts);
+      extMintRes.forEach((r, idx) => {
+        if (r) extendMintAccount.push(fetchAccounts[idx]);
+      });
+    }
+
+    // Generate pool creation instructions
+    const { instructions } = await Instruction.createPoolInstruction({
+      programId: this.programId,
+      owner: userAddress,
+      poolManager,
+      mintA,
+      mintB,
+      ammConfigId,
+      initialPriceX64,
+      openTime,
+      extendMintAccount,
+    });
+    // Construct transaction object
+    const transaction = await makeTransaction({
+      connection: this.connection,
+      payerPublicKey: userAddress,
+      instructions,
+    });
+    return {
+      instructions,
+      transaction,
+    };
+  }
+
+  /**
+   * Create a new pool and send a transaction
+   * @param params Contains signature callback, etc.
+   * @returns Promise<string> Transaction signature
+   */
+  public async createPool(params: ParamsWithSignerCallback<ICreatePoolParams>): Promise<string> {
+    const { signerCallback } = params;
+    const { transaction } = await this.createPoolInstructions(params);
+    return sendTransaction({
+      connection: this.connection,
+      signTx: () => signerCallback(transaction),
+    });
+  }
+
+  public async qouteSwap(params: IQouteSwapParams): Promise<IQouteSwapReturn> {
+    // Slippage is set to 2% by default
+    const {
+      poolInfo,
+      inputTokenMint,
+      amountIn,
+      priceLimit = new Decimal(0),
+      slippage = 0.02,
+      catchLiquidityInsufficient,
+    } = params;
+
+    let sqrtPriceLimitX64: BN;
+    const isInputMintA = inputTokenMint.toBase58() === poolInfo.mintA.toBase58();
+
+    // TODO: Consider fee calculation for token2022 in the future
+
+    if (priceLimit.equals(new Decimal(0))) {
+      sqrtPriceLimitX64 = isInputMintA ? MIN_SQRT_PRICE_X64.add(new BN(1)) : MAX_SQRT_PRICE_X64.sub(new BN(1));
+    } else {
+      sqrtPriceLimitX64 = SqrtPriceMath.priceToSqrtPriceX64(priceLimit, poolInfo.mintDecimalsA, poolInfo.mintDecimalsB);
+    }
+
+    const exBitmapInfo = await getTickArrayBitmapExtension(this.programId, poolInfo.poolId, this.connection);
+
+    const ammConfig = await RawDataUtils.getRawAmmConfigByConfigId({
+      connection: this.connection,
+      configId: poolInfo.ammConfig,
+    });
+
+    const tickArrayInfo = await getTickArrayInfo({
+      connection: this.connection,
+      poolInfo,
+      exBitmapInfo,
+    });
+
+    if (!exBitmapInfo || !ammConfig) throw new Error('Failed to get tick array bitmap extension or amm config');
+
+    const { allTrade, expectedAmountOut, remainingAccounts, executionPrice, feeAmount } =
+      await PoolUtils.getOutputAmountAndRemainAccounts({
+        poolInfo,
+        exBitmapInfo,
+        ammConfig,
+        tickArrayInfo,
+        inputTokenMint,
+        inputAmount: amountIn,
+        sqrtPriceLimitX64,
+        catchLiquidityInsufficient,
+      });
+
+    const minAmountOut = expectedAmountOut
+      .mul(new BN(Math.floor((1 - slippage) * 10000000000)))
+      .div(new BN(10000000000));
+
+    return {
+      allTrade,
+      isInputMintA,
+      amountIn,
+      expectedAmountOut,
+      minAmountOut,
+      remainingAccounts,
+      executionPrice,
+      feeAmount,
+    };
+  }
+
+  public async swapInstructions(params: ISwapParams): Promise<IInstructionReturn> {
+    const { poolInfo, quoteReturn, userAddress } = params;
+
+    // Determine amounts based on which token is input
+    const amountA = quoteReturn.isInputMintA ? quoteReturn.amountIn : new BN(0);
+    const amountB = !quoteReturn.isInputMintA ? quoteReturn.amountIn : new BN(0);
+
+    const { tokenAccountA, tokenAccountB, preInstructions, endInstructions } = await this.handleTokenAccount({
+      userAddress,
+      mintA: poolInfo.mintA,
+      mintB: poolInfo.mintB,
+      amountA,
+      amountB,
+    });
+
+    const exBitmapAddress = getPdaExBitmapAccount(poolInfo.programId, poolInfo.poolId).publicKey;
+
+    // quoteReturn.isBaseIn
+    const { instructions: swapInstruction } = await Instruction.swapBaseInInstruction({
+      poolInfo,
+      ownerInfo: {
+        wallet: userAddress,
+        tokenAccountA,
+        tokenAccountB,
+      },
+      amount: quoteReturn.amountIn,
+      // The minimum output amount after slippage calculation is passed here
+      otherAmountThreshold: quoteReturn.minAmountOut,
+      sqrtPriceLimitX64: quoteReturn.executionPrice,
+      isInputMintA: quoteReturn.isInputMintA,
+      tickArray: quoteReturn.remainingAccounts,
+      exTickArrayBitmap: exBitmapAddress,
+    });
+
+    const instructions = [...preInstructions, ...swapInstruction, ...endInstructions];
+
+    const transaction = await makeTransaction({
+      connection: this.connection,
+      payerPublicKey: userAddress,
+      instructions,
+    });
+
+    return {
+      transaction,
+      instructions,
+    };
+  }
+
+  /**
+   * Create swap exact out instructions
+   * @param params Contains pool info, quote return, and user address
+   * @returns IInstructionReturn
+   */
+  public async swapExactOutInstructions(params: {
+    poolInfo: IPoolLayoutWithId;
+    quoteReturn: IQuoteSwapExactOutReturn;
+    userAddress: PublicKey;
+  }): Promise<IInstructionReturn> {
+    const { poolInfo, quoteReturn, userAddress } = params;
+
+    // For exact output, we need to determine input amounts
+    // If outputting tokenA, we input tokenB
+    // If outputting tokenB, we input tokenA
+    const isInputMintA = !quoteReturn.isOutputMintA;
+    const amountA = isInputMintA ? quoteReturn.maxAmountIn : new BN(0);
+    const amountB = !isInputMintA ? quoteReturn.maxAmountIn : new BN(0);
+
+    const { tokenAccountA, tokenAccountB, preInstructions, endInstructions } = await this.handleTokenAccount({
+      userAddress,
+      mintA: poolInfo.mintA,
+      mintB: poolInfo.mintB,
+      amountA,
+      amountB,
+    });
+
+    const exBitmapAddress = getPdaExBitmapAccount(poolInfo.programId, poolInfo.poolId).publicKey;
+
+    // For exact output, we need to use swapBaseOutInstruction
+    const { instructions: swapInstruction } = await Instruction.swapBaseOutInstruction({
+      poolInfo,
+      ownerInfo: {
+        wallet: userAddress,
+        tokenAccountA,
+        tokenAccountB,
+      },
+      amount: quoteReturn.amountOut,
+      // The maximum input amount (including slippage) is passed here
+      otherAmountThreshold: quoteReturn.maxAmountIn,
+      sqrtPriceLimitX64: quoteReturn.executionPrice,
+      isOutputMintA: quoteReturn.isOutputMintA,
+      tickArray: quoteReturn.remainingAccounts,
+      exTickArrayBitmap: exBitmapAddress,
+    });
+
+    const instructions = [...preInstructions, ...swapInstruction, ...endInstructions];
+
+    const transaction = await makeTransaction({
+      connection: this.connection,
+      payerPublicKey: userAddress,
+      instructions,
+    });
+
+    return {
+      transaction,
+      instructions,
+    };
+  }
+
+  public async swap(params: ParamsWithSignerCallback<ISwapParams>): Promise<string> {
+    const { signerCallback } = params;
+    const { transaction } = await this.swapInstructions(params);
+    return sendTransaction({
+      connection: this.connection,
+      signTx: () => signerCallback(transaction),
+    });
+  }
+
+  /**
+   * Execute swap exact out transaction
+   * @param params Contains signature callback, etc.
+   * @returns Promise<string> Transaction signature
+   */
+  public async swapExactOut(params: ParamsWithSignerCallback<ISwapExactOutParams>): Promise<string> {
+    const { signerCallback } = params;
+    const { transaction } = await this.swapExactOutInstructions(params);
+    return sendTransaction({
+      connection: this.connection,
+      signTx: () => signerCallback(transaction),
+    });
+  }
+
+  /**
+   * Quote swap exact output - calculate required input amount for desired output
+   * @param params Quote parameters including output amount and slippage
+   * @returns Quote result with expected input amount and other swap details
+   */
+  public async quoteSwapExactOut(params: IQuoteSwapExactOutParams): Promise<IQuoteSwapExactOutReturn> {
+    const {
+      poolInfo,
+      outputTokenMint,
+      amountOut,
+      priceLimit = new Decimal(0),
+      slippage = 0.02,
+      catchLiquidityInsufficient,
+    } = params;
+
+    let sqrtPriceLimitX64: BN;
+    const isOutputMintA = outputTokenMint.toBase58() === poolInfo.mintA.toBase58();
+
+    // For exact output, we need to determine if we're inputting token A or B
+    // If outputting A, we input B (zeroForOne = false)
+    // If outputting B, we input A (zeroForOne = true)
+    const zeroForOne = !isOutputMintA;
+
+    if (priceLimit.equals(new Decimal(0))) {
+      sqrtPriceLimitX64 = zeroForOne ? MIN_SQRT_PRICE_X64.add(new BN(1)) : MAX_SQRT_PRICE_X64.sub(new BN(1));
+    } else {
+      sqrtPriceLimitX64 = SqrtPriceMath.priceToSqrtPriceX64(priceLimit, poolInfo.mintDecimalsA, poolInfo.mintDecimalsB);
+    }
+
+    const exBitmapInfo = await getTickArrayBitmapExtension(this.programId, poolInfo.poolId, this.connection);
+
+    const ammConfig = await RawDataUtils.getRawAmmConfigByConfigId({
+      connection: this.connection,
+      configId: poolInfo.ammConfig,
+    });
+
+    const tickArrayInfo = await getTickArrayInfo({
+      connection: this.connection,
+      poolInfo,
+      exBitmapInfo,
+    });
+
+    if (!exBitmapInfo || !ammConfig) throw new Error('Failed to get tick array bitmap extension or amm config');
+
+    const { allTrade, expectedAmountIn, remainingAccounts, executionPrice, feeAmount } =
+      await PoolUtils.getInputAmountAndRemainAccounts({
+        poolInfo,
+        exBitmapInfo,
+        ammConfig,
+        tickArrayInfo,
+        outputTokenMint,
+        outputAmount: amountOut,
+        sqrtPriceLimitX64,
+        catchLiquidityInsufficient,
+      });
+
+    // For exact output, we calculate max input amount with slippage
+    // This is the maximum amount user is willing to pay
+    const maxAmountIn = expectedAmountIn.mul(new BN(Math.ceil((1 + slippage) * 10000000000))).div(new BN(10000000000));
+
+    return {
+      allTrade,
+      isOutputMintA,
+      amountOut,
+      expectedAmountIn,
+      maxAmountIn,
+      remainingAccounts,
+      executionPrice,
+      feeAmount,
+    };
+  }
+
+  /**
+   * Handle SOL/WSOL packaging logic, automatically generate related instructions
+   *
+   * @param params.userAddress User wallet address
+   * @param params.mintA Pool tokenA mint
+   * @param params.mintB Pool tokenB mint
+   * @param params.amountA Optional, tokenA quantity
+   * @param params.amountB Optional, tokenB quantity
+   * @param params.rentExemptLamports Optional, WSOL account rent exemption lamports
+   * @returns tokenAccountA/B, pre-instructions, post-instructions
+   */
+  private async handleTokenAccount(params: {
+    userAddress: PublicKey;
+    mintA: PublicKey;
+    mintB: PublicKey;
+    amountA?: BN;
+    amountB?: BN;
+    rentExemptLamports?: number;
+  }): Promise<{
+    tokenAccountA: PublicKey;
+    tokenAccountB: PublicKey;
+    tokenProgramIdA: PublicKey;
+    tokenProgramIdB: PublicKey;
+    preInstructions: TransactionInstruction[];
+    endInstructions: TransactionInstruction[];
+  }> {
+    const { userAddress, mintA, mintB, amountA, amountB } = params;
+    // Check if there is SOL involved
+    const isTokenASOL = mintA.toString() === NATIVE_MINT.toString();
+    const isTokenBSOL = mintB.toString() === NATIVE_MINT.toString();
+    // Default to use ATA account
+    const tokenProgramIdA = await getTokenProgramId(this.connection, mintA);
+    const tokenProgramIdB = await getTokenProgramId(this.connection, mintB);
+    let tokenAccountA = getATAAddress(userAddress, mintA, tokenProgramIdA).publicKey;
+    let tokenAccountB = getATAAddress(userAddress, mintB, tokenProgramIdB).publicKey;
+    const preInstructions: TransactionInstruction[] = [];
+    const endInstructions: TransactionInstruction[] = [];
+
+    if (!isTokenASOL) {
+      const accA = await this.connection.getAccountInfo(tokenAccountA);
+      if (!accA) {
+        preInstructions.push(
+          createAssociatedTokenAccountIdempotentInstruction(
+            userAddress,
+            tokenAccountA,
+            userAddress,
+            mintA,
+            tokenProgramIdA,
+          ),
+        );
+      }
+    }
+
+    if (!isTokenBSOL) {
+      const accB = await this.connection.getAccountInfo(tokenAccountB);
+      if (!accB) {
+        preInstructions.push(
+          createAssociatedTokenAccountIdempotentInstruction(
+            userAddress,
+            tokenAccountB,
+            userAddress,
+            mintB,
+            tokenProgramIdB,
+          ),
+        );
+      }
+    }
+
+    if (!isTokenASOL && !isTokenBSOL) {
+      return { tokenAccountA, tokenAccountB, preInstructions, endInstructions, tokenProgramIdA, tokenProgramIdB };
+    }
+    // Handle SOL -> WSOL packaging
+    if (isTokenASOL || isTokenBSOL) {
+      const newAccount = generatePubKey({
+        fromPublicKey: userAddress,
+        programId: TOKEN_PROGRAM_ID,
+      });
+      const wsolAccount = newAccount.publicKey;
+      const rentExemptLamports = params.rentExemptLamports || (await this.estimateRentFee(AccountLayout.span));
+      // Calculate how much SOL is needed
+      let amountNeeded = 0;
+      if (isTokenASOL && amountA) {
+        amountNeeded = amountA.toNumber();
+      } else if (isTokenBSOL && amountB) {
+        amountNeeded = amountB.toNumber();
+      }
+
+      // Create WSOL account
+      preInstructions.push(
+        SystemProgram.createAccountWithSeed({
+          fromPubkey: userAddress,
+          basePubkey: userAddress,
+          seed: newAccount.seed,
+          newAccountPubkey: wsolAccount,
+          space: AccountLayout.span,
+          lamports: rentExemptLamports + amountNeeded,
+          programId: TOKEN_PROGRAM_ID,
+        }),
+      );
+      // Initialize WSOL account
+      preInstructions.push(createInitializeAccountInstruction(wsolAccount, NATIVE_MINT, userAddress));
+      // Add instructions to close WSOL account
+      endInstructions.push(createCloseAccountInstruction(wsolAccount, userAddress, userAddress, []));
+      // Update the corresponding token account
+      if (isTokenASOL) {
+        tokenAccountA = wsolAccount;
+      }
+      if (isTokenBSOL) {
+        tokenAccountB = wsolAccount;
+      }
+    }
+    return { tokenAccountA, tokenAccountB, preInstructions, endInstructions, tokenProgramIdA, tokenProgramIdB };
+  }
+
+  private async estimateRentFee(space: number, useCache = true): Promise<number> {
+    if (useCache && this.rentFeeCache[space] !== undefined) {
+      return this.rentFeeCache[space];
+    }
+    const lamports = await this.connection.getMinimumBalanceForRentExemption(space);
+    this.rentFeeCache[space] = lamports;
+    return lamports;
+  }
+
+  public calculateApr = calculateApr;
+  public calculateRewardApr = calculateRewardApr;
+  public calculateRangeAprs = calculateRangeAprs;
+  public alignPriceToTickPrice = alignPriceToTickPrice;
+  public getAmountBFromAmountA = getAmountBFromAmountA;
+  public getAmountAFromAmountB = getAmountAFromAmountB;
+}

+ 177 - 0
src/lib/byreal-clmm-sdk/src/client/chain/models.ts

@@ -0,0 +1,177 @@
+import { PublicKey, Signer, TransactionInstruction, VersionedTransaction } from '@solana/web3.js';
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+import {
+  IPersonalPositionLayout,
+  IPoolLayoutWithId,
+  ITokenInfo,
+  TickArrayBitmapExtensionType,
+} from '../../instructions/index';
+import { IMakeTransactionOptions } from '../../utils/index';
+
+export type SignerCallback = (transaction: VersionedTransaction) => Promise<VersionedTransaction>;
+
+export type ParamsWithSignerCallback<T> = T & {
+  signerCallback: SignerCallback;
+};
+
+export interface IInstructionReturn {
+  instructions: TransactionInstruction[];
+  signers?: Signer[];
+  transaction: VersionedTransaction;
+  nftAddress?: string;
+}
+
+export interface ICreatePositionParams {
+  userAddress: PublicKey;
+  poolInfo: IPoolLayoutWithId;
+  tickLower: number;
+  tickUpper: number;
+  base: 'MintA' | 'MintB';
+  baseAmount: BN;
+  otherAmountMax: BN;
+  transactionOptions?: IMakeTransactionOptions;
+}
+
+export interface IClosePositionParams {
+  userAddress: PublicKey;
+  nftMint: PublicKey;
+}
+
+export interface IDecreaseLiquidityParams {
+  userAddress: PublicKey;
+  nftMint: PublicKey;
+  liquidity: BN;
+  slippage?: number;
+}
+
+export interface IDecreaseFullLiquidityParams {
+  userAddress: PublicKey;
+  nftMint: PublicKey;
+  closePosition?: boolean;
+  slippage?: number;
+}
+
+export interface IAddLiquidityParams {
+  userAddress: PublicKey;
+  nftMint: PublicKey;
+  base: 'MintA' | 'MintB';
+  baseAmount: BN;
+  otherAmountMax: BN;
+  computeBudgetOptions?: IComputeBudgetOptions;
+}
+
+export interface ICreatePoolParams {
+  userAddress: PublicKey;
+  poolManager: PublicKey;
+  mintA: ITokenInfo;
+  mintB: ITokenInfo;
+  ammConfigId: PublicKey;
+  initialPrice: number;
+  openTime?: BN;
+  computeBudgetOptions?: IComputeBudgetOptions;
+}
+
+export interface ICollectFeesParams {
+  userAddress: PublicKey;
+  nftMint: PublicKey;
+}
+
+export interface ICollectAllFeesParams {
+  userAddress: PublicKey;
+  nftMintList: PublicKey[];
+}
+
+export interface IGetPositionInfoByNftMintReturn {
+  priceLower: Decimal;
+  priceUpper: Decimal;
+  uiPriceLower: string;
+  uiPriceUpper: string;
+  tokenA: {
+    address: PublicKey;
+    decimals: number;
+    amount: BN;
+    feeAmount: BN;
+    uiAmount: string;
+    uiFeeAmount: string;
+  };
+  tokenB: {
+    address: PublicKey;
+    decimals: number;
+    amount: BN;
+    feeAmount: BN;
+    uiAmount: string;
+    uiFeeAmount: string;
+  };
+  rawPositionInfo: IPersonalPositionLayout;
+  rawPoolInfo: IPoolLayoutWithId;
+}
+
+export interface ICalculateCreatePositionFee {
+  transactionNetFee: number; // Transaction fee
+  refundableFees: number; // Refundable fees (NFT minting fee + NFT holder fee + personal position fee)
+  unRefundableFees: number; // Unrefundable fees (Newly created tick array account + transaction fee)
+
+  createTickFee: number; // Create tick array account fee
+}
+
+/**
+ * maxFee and exactFee are mutually exclusive
+ */
+export type IComputeBudgetOptions =
+  | { maxFee: number; exactFee?: never; computeUnitPrice?: number }
+  | { maxFee?: never; exactFee: number; computeUnitPrice?: never }
+  | { maxFee?: undefined; exactFee?: undefined; computeUnitPrice?: number };
+
+export interface IQouteSwapParams {
+  poolInfo: IPoolLayoutWithId;
+  inputTokenMint: PublicKey;
+  amountIn: BN;
+  slippage?: number;
+  priceLimit?: Decimal;
+  catchLiquidityInsufficient?: boolean;
+}
+
+export interface IQouteSwapReturn {
+  allTrade: boolean;
+  amountIn: BN;
+  isInputMintA: boolean;
+  expectedAmountOut: BN;
+  minAmountOut: BN;
+  remainingAccounts: PublicKey[];
+  executionPrice: BN;
+  feeAmount: BN;
+}
+
+export interface ISwapParams {
+  poolInfo: IPoolLayoutWithId;
+  userAddress: PublicKey;
+  quoteReturn: IQouteSwapReturn;
+}
+
+export interface ISwapExactOutParams {
+  poolInfo: IPoolLayoutWithId;
+  userAddress: PublicKey;
+  quoteReturn: IQuoteSwapExactOutReturn;
+}
+
+export interface IQuoteSwapExactOutParams {
+  poolInfo: IPoolLayoutWithId;
+  outputTokenMint: PublicKey;
+  amountOut: BN;
+  slippage?: number;
+  priceLimit?: Decimal;
+  catchLiquidityInsufficient?: boolean;
+}
+
+export interface IQuoteSwapExactOutReturn {
+  allTrade: boolean;
+  amountOut: BN;
+  isOutputMintA: boolean;
+  expectedAmountIn: BN;
+  maxAmountIn: BN;
+  remainingAccounts: PublicKey[];
+  executionPrice: BN;
+  feeAmount: BN;
+}

+ 408 - 0
src/lib/byreal-clmm-sdk/src/client/chain/utils.ts

@@ -0,0 +1,408 @@
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+import {
+  IPoolLayout,
+  IPoolLayoutWithId,
+  LiquidityMath,
+  PoolUtils,
+  SqrtPriceMath,
+  TickMath,
+} from '../../instructions/index';
+import { TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from '@solana/spl-token';
+import { Connection, PublicKey } from '@solana/web3.js';
+
+/**
+ * Used to round the price to the corresponding tick when creating a position
+ * @param price Input price
+ * @param poolInfo Pool information
+ * @returns Decimal rounded price
+ */
+export function alignPriceToTickPrice(price: Decimal, poolInfo: IPoolLayout): Decimal {
+  const { price: roundedPrice } = TickMath.getTickAlignedPriceDetails(
+    price,
+    poolInfo.tickSpacing,
+    poolInfo.mintDecimalsA,
+    poolInfo.mintDecimalsB,
+  );
+  return roundedPrice;
+}
+
+/**
+ * Calculate the amount of tokenB needed to be invested after the specified tokenA amount has been invested
+ * @param params.priceLower Lower price
+ * @param params.priceUpper Upper price
+ * @param params.amountA Amount of tokenA to be invested
+ * @param params.poolInfo Pool information
+ * @returns BN amount of tokenB to be invested
+ */
+export function getAmountBFromAmountA(params: {
+  priceLower: Decimal | number | string;
+  priceUpper: Decimal | number | string;
+  amountA: BN;
+  poolInfo: IPoolLayout;
+}): BN {
+  // console.log('[clmm sdk] getAmountBFromAmountA fn params:', JSON.stringify(params, null, 2));
+  const { priceLower, priceUpper, amountA, poolInfo } = params;
+
+  const priceLowerDecimal = alignPriceToTickPrice(new Decimal(priceLower), poolInfo);
+  const priceUpperDecimal = alignPriceToTickPrice(new Decimal(priceUpper), poolInfo);
+
+  const sqrtPriceX64A = SqrtPriceMath.priceToSqrtPriceX64(
+    priceLowerDecimal,
+    poolInfo.mintDecimalsA,
+    poolInfo.mintDecimalsB,
+  );
+  const sqrtPriceX64B = SqrtPriceMath.priceToSqrtPriceX64(
+    priceUpperDecimal,
+    poolInfo.mintDecimalsA,
+    poolInfo.mintDecimalsB,
+  );
+
+  const amountB = LiquidityMath.getAmountBFromAmountA(sqrtPriceX64A, sqrtPriceX64B, poolInfo.sqrtPriceX64, amountA);
+
+  return amountB;
+}
+
+/**
+ * Calculate the amount of tokenA needed to be invested after the specified tokenB amount has been invested
+ * @param params.priceLower Lower price
+ * @param params.priceUpper Upper price
+ * @param params.amountB Amount of tokenB to be invested
+ * @param params.poolInfo Pool information
+ * @returns BN amount of tokenA to be invested
+ */
+export function getAmountAFromAmountB(params: {
+  priceLower: Decimal | number | string;
+  priceUpper: Decimal | number | string;
+  amountB: BN;
+  poolInfo: IPoolLayout;
+}): BN {
+  const { priceLower, priceUpper, amountB, poolInfo } = params;
+
+  const priceLowerDecimal = alignPriceToTickPrice(new Decimal(priceLower), poolInfo);
+  const priceUpperDecimal = alignPriceToTickPrice(new Decimal(priceUpper), poolInfo);
+
+  const sqrtPriceX64A = SqrtPriceMath.priceToSqrtPriceX64(
+    priceLowerDecimal,
+    poolInfo.mintDecimalsA,
+    poolInfo.mintDecimalsB,
+  );
+  const sqrtPriceX64B = SqrtPriceMath.priceToSqrtPriceX64(
+    priceUpperDecimal,
+    poolInfo.mintDecimalsA,
+    poolInfo.mintDecimalsB,
+  );
+
+  return LiquidityMath.getAmountAFromAmountB(sqrtPriceX64A, sqrtPriceX64B, poolInfo.sqrtPriceX64, amountB);
+}
+
+/**
+ * Calculate the expected annualized return rate of adding liquidity (APR)
+ */
+export function calculateApr(params: {
+  volume24h: number; // 24h volume in USD
+  feeRate: number; // Fee rate, e.g. 0.003 means 0.3%
+  positionUsdValue: number; // USD value of the tokens invested by the user
+  amountA: BN; // Amount of tokenA invested
+  amountB: BN; // Amount of tokenB invested
+  tickLower: number; // Tick corresponding to the lower price
+  tickUpper: number; // Tick corresponding to the upper price
+  poolInfo: IPoolLayoutWithId; // Pool information
+  existLiquidity?: BN; // Existing liquidity (required when adding liquidity)
+  scene?: 'create' | 'add' | 'exist'; // Calculation scenario, create, add, exist
+}): number {
+  // console.log('[clmm sdk] calculateApr fn params:', JSON.stringify(params, null, 2));
+  const {
+    volume24h,
+    feeRate,
+    positionUsdValue,
+    amountA,
+    amountB,
+    tickLower,
+    tickUpper,
+    poolInfo,
+    scene = 'exist',
+    existLiquidity = new BN(0),
+  } = params;
+
+  return _calculateApr({
+    fee24hUsdValue: volume24h * feeRate,
+    positionUsdValue,
+    amountA,
+    amountB,
+    tickLower,
+    tickUpper,
+    poolInfo,
+    scene,
+    existLiquidity,
+  });
+}
+
+/**
+ * Calculate the expected annualized return rate of reward
+ */
+export function calculateRewardApr(params: {
+  reward24hUsdValue: number; // Reward received in 24h
+  positionUsdValue: number; // USD value of the tokens invested by the user
+  amountA: BN; // Amount of tokenA invested
+  amountB: BN; // Amount of tokenB invested
+  tickLower: number; // Tick corresponding to the lower price
+  tickUpper: number; // Tick corresponding to the upper price
+  poolInfo: IPoolLayoutWithId; // Pool information
+  existLiquidity?: BN; // Existing liquidity (required when adding liquidity)
+  scene?: 'create' | 'add' | 'exist'; // Calculation scenario, create, add, exist
+}): number {
+  const {
+    reward24hUsdValue,
+    positionUsdValue,
+    amountA,
+    amountB,
+    tickLower,
+    tickUpper,
+    poolInfo,
+    scene = 'exist',
+    existLiquidity = new BN(0),
+  } = params;
+
+  return _calculateApr({
+    fee24hUsdValue: reward24hUsdValue,
+    positionUsdValue,
+    amountA,
+    amountB,
+    tickLower,
+    tickUpper,
+    poolInfo,
+    scene,
+    existLiquidity,
+  });
+}
+
+/**
+ * Calculate the expected annualized return rate of adding liquidity (APR)
+ *
+ */
+export function _calculateApr(params: {
+  fee24hUsdValue: number; // 24h fee or reward received
+  positionUsdValue: number; // USD value of the tokens invested by the user
+  amountA: BN; // Amount of tokenA invested
+  amountB: BN; // Amount of tokenB invested
+  tickLower: number; // Tick corresponding to the lower price
+  tickUpper: number; // Tick corresponding to the upper price
+  poolInfo: IPoolLayoutWithId; // Pool information
+  existLiquidity?: BN; // Existing liquidity (required when adding liquidity)
+  scene?: 'create' | 'add' | 'exist'; // Calculation scenario, create, add, exist
+}): number {
+  const {
+    fee24hUsdValue,
+    positionUsdValue,
+    amountA,
+    amountB,
+    tickLower,
+    tickUpper,
+    poolInfo,
+    scene = 'exist',
+    existLiquidity = new BN(0),
+  } = params;
+
+  // Get the active liquidity of the pool
+  const poolActiveLiquidity = poolInfo.liquidity;
+
+  // Calculate the sqrtPrice of the price range
+  const sqrtPriceCurrentX64 = poolInfo.sqrtPriceX64;
+  const sqrtPriceLowerX64 = SqrtPriceMath.getSqrtPriceX64FromTick(tickLower);
+  const sqrtPriceUpperX64 = SqrtPriceMath.getSqrtPriceX64FromTick(tickUpper);
+
+  // Calculate the active liquidity of the user
+  const userActiveLiquidity = LiquidityMath.getLiquidityFromTokenAmounts(
+    sqrtPriceCurrentX64,
+    sqrtPriceLowerX64,
+    sqrtPriceUpperX64,
+    amountA,
+    amountB,
+  );
+
+  // Check if the current price is within the user's range
+  const isInRange = poolInfo.tickCurrent >= tickLower && poolInfo.tickCurrent < tickUpper;
+
+  // If not in range, active liquidity is 0
+  if (!isInRange) {
+    return 0;
+  }
+
+  if (poolActiveLiquidity.isZero()) {
+    return 0;
+  }
+
+  // Calculate the annualized return rate
+  // Formula source: https://uponly.larksuite.com/wiki/GznTwz3kGi3D0ikaLp8uDlYcsKd?fromScene=spaceOverview
+  // For creating liquidity: (Volume * FeeRate / Position) * (userAL / (AL + userAL)) * 365 * 100
+  // For adding liquidity: (Volume * FeeRate / Position) * ((userAL + addAL) / (AL + addAL)) * 365 * 100
+  // For existing positions: (Volume * FeeRate / Position) * (userAL / AL) * 365 * 100
+  const fee24hDec = new Decimal(fee24hUsdValue);
+  const positionUsdValueDec = new Decimal(positionUsdValue);
+  const userActiveLiquidityDec = new Decimal(userActiveLiquidity.toString());
+  const poolActiveLiquidityDec = new Decimal(poolActiveLiquidity.toString());
+  const existLiquidityDec = new Decimal(existLiquidity.toString());
+
+  // Daily return rate
+  const dailyReturn = fee24hDec.div(positionUsdValueDec);
+
+  // Calculate the liquidity share using Decimal
+  let liquidityShare: Decimal;
+  if (scene === 'create') {
+    liquidityShare = userActiveLiquidityDec.div(poolActiveLiquidityDec.plus(userActiveLiquidityDec));
+  } else if (scene === 'add') {
+    liquidityShare = existLiquidityDec
+      .plus(userActiveLiquidityDec)
+      .div(poolActiveLiquidityDec.plus(userActiveLiquidityDec));
+  } else {
+    liquidityShare = userActiveLiquidityDec.div(poolActiveLiquidityDec);
+  }
+
+  // Calculate the annualized return rate
+  const apr = dailyReturn.mul(liquidityShare).mul(365).mul(100).toNumber();
+
+  return apr;
+}
+
+/**
+ * Calculate the annualized return rate for different price ranges
+ * Calculate APR for different price ranges based on the percentage offset from the current price
+ *
+ * @param params Calculation parameters
+ * @returns Mapping of annualized return rates for different price ranges, using -1 to represent the full range
+ */
+export function calculateRangeAprs(params: {
+  percentRanges: number[]; // Price range percentage list, e.g. [1, 5, 10, 20, 50]
+  volume24h: number; // 24h volume in USD
+  feeRate: number; // Fee rate, e.g. 0.003 means 0.3%
+  tokenAPriceUsd: number; // USD value of tokenA
+  tokenBPriceUsd: number; // USD value of tokenB
+  poolInfo: IPoolLayoutWithId; // Pool information
+}): Record<number, number> {
+  const { percentRanges, volume24h, feeRate, tokenAPriceUsd, tokenBPriceUsd, poolInfo } = params;
+
+  // Get the current price, liquidity and sample liquidity
+  const currentPrice = TickMath.getPriceFromTick({
+    tick: poolInfo.tickCurrent,
+    decimalsA: poolInfo.mintDecimalsA,
+    decimalsB: poolInfo.mintDecimalsB,
+  });
+
+  const poolLiquidity = poolInfo.liquidity;
+  const _sampleLiquidity = poolLiquidity.div(new BN(10000));
+  const _minLiquidity = new BN(1000);
+  const liquidityToUse = _sampleLiquidity.lt(_minLiquidity) ? _minLiquidity : _sampleLiquidity;
+
+  // Result mapping
+  const result: Record<number, number> = {};
+
+  const { minTickBoundary, maxTickBoundary } = PoolUtils.tickRange(poolInfo.tickSpacing);
+
+  // Calculate APR for each range
+  for (const range of percentRanges) {
+    let tickLower: number;
+    let tickUpper: number;
+
+    if (range === -1) {
+      tickLower = minTickBoundary;
+      tickUpper = maxTickBoundary;
+    } else {
+      const lowerPriceRatio = new Decimal(1).minus(new Decimal(range).div(100));
+      const upperPriceRatio = new Decimal(1).plus(new Decimal(range).div(100));
+      const lowerPrice = currentPrice.mul(lowerPriceRatio);
+      const upperPrice = currentPrice.mul(upperPriceRatio);
+
+      tickLower = TickMath.getTickWithPriceAndTickspacing(
+        lowerPrice,
+        poolInfo.tickSpacing,
+        poolInfo.mintDecimalsA,
+        poolInfo.mintDecimalsB,
+      );
+
+      tickUpper = TickMath.getTickWithPriceAndTickspacing(
+        upperPrice,
+        poolInfo.tickSpacing,
+        poolInfo.mintDecimalsA,
+        poolInfo.mintDecimalsB,
+      );
+    }
+
+    // Ensure that lower and upper are at least 1 tickSpacing apart
+    if (tickLower >= tickUpper) {
+      tickLower = Math.max(minTickBoundary, poolInfo.tickCurrent - poolInfo.tickSpacing);
+      tickUpper = Math.min(maxTickBoundary, poolInfo.tickCurrent + poolInfo.tickSpacing);
+    }
+
+    // Calculate the square root of the price (X64 format)
+    const sqrtPriceCurrentX64 = poolInfo.sqrtPriceX64;
+    const sqrtPriceLowerX64 = SqrtPriceMath.getSqrtPriceX64FromTick(tickLower);
+    const sqrtPriceUpperX64 = SqrtPriceMath.getSqrtPriceX64FromTick(tickUpper);
+
+    // Calculate the corresponding token amounts based on the liquidity
+    const { amountA, amountB } = LiquidityMath.getAmountsFromLiquidity(
+      sqrtPriceCurrentX64,
+      sqrtPriceLowerX64,
+      sqrtPriceUpperX64,
+      liquidityToUse,
+      false, // Do not round up
+    );
+
+    // Calculate the USD value of the tokens
+    const tokenAUsdAmount = new Decimal(amountA.toString())
+      .div(new Decimal(10).pow(poolInfo.mintDecimalsA))
+      .mul(tokenAPriceUsd);
+
+    const tokenBUsdAmount = new Decimal(amountB.toString())
+      .div(new Decimal(10).pow(poolInfo.mintDecimalsB))
+      .mul(tokenBPriceUsd);
+
+    // Calculate the total investment value
+    const positionUsdValue = tokenAUsdAmount.plus(tokenBUsdAmount);
+
+    // If the USD value is too small, set a minimum value to avoid division issues
+    const effectivePositionUsdValue = positionUsdValue.lt(0.01) ? new Decimal(0.01) : positionUsdValue;
+
+    // Calculate the actual annualized return rate
+    const apr = calculateApr({
+      volume24h,
+      feeRate,
+      positionUsdValue: effectivePositionUsdValue.toNumber(),
+      amountA,
+      amountB,
+      tickLower,
+      tickUpper,
+      poolInfo,
+      scene: 'create',
+    });
+
+    result[range] = apr;
+  }
+
+  return result;
+}
+
+/**
+ * 获取 mint 对应的 token program ID
+ */
+export async function getTokenProgramId(connection: Connection, mintAddress: PublicKey): Promise<PublicKey> {
+  try {
+    const mintAccountInfo = await connection.getAccountInfo(mintAddress);
+    if (!mintAccountInfo) {
+      throw new Error(`Mint account not found: ${mintAddress.toBase58()}`);
+    }
+
+    // 检查 mint 账户的 owner 来确定使用的是哪个 token program
+    if (mintAccountInfo.owner.equals(TOKEN_2022_PROGRAM_ID)) {
+      return TOKEN_2022_PROGRAM_ID;
+    } else if (mintAccountInfo.owner.equals(TOKEN_PROGRAM_ID)) {
+      return TOKEN_PROGRAM_ID;
+    } else {
+      throw new Error(`Unknown token program for mint: ${mintAddress.toBase58()}`);
+    }
+  } catch (error) {
+    console.warn(`Failed to get token program for ${mintAddress.toBase58()}, defaulting to TOKEN_PROGRAM_ID`);
+    return TOKEN_PROGRAM_ID;
+  }
+}

+ 84 - 0
src/lib/byreal-clmm-sdk/src/client/index.ts

@@ -0,0 +1,84 @@
+import { Connection, PublicKey } from '@solana/web3.js';
+
+import { BYREAL_CLMM_PROGRAM_ID } from '../constants';
+import { IPoolLayoutWithId } from '../instructions/models';
+
+import { Api, IApiParams } from './apis/index';
+import { IPoolsReq, IPoolsByIdsReq } from './apis/poolsModels';
+import { IMyPositionsReq } from './apis/positionModels';
+import { ITickReq } from './apis/tickModels';
+import { Chain } from './chain/index';
+import { ICollectFeesParams } from './chain/models';
+import { ParamsWithSignerCallback } from './chain/models';
+import { CLMMClient } from './liquidity/clmm';
+import { Token } from './token';
+
+export class SdkClient {
+  public api: Api;
+  public chain: Chain;
+  public clmmClient: CLMMClient;
+  public connection: Connection;
+  public programId: PublicKey;
+  public token: Token;
+
+  constructor(params: { connection: Connection; programId?: PublicKey; apiConfig?: IApiParams }) {
+    const { connection, programId = BYREAL_CLMM_PROGRAM_ID, apiConfig } = params;
+    this.connection = connection;
+    this.programId = programId;
+    this.api = new Api(apiConfig);
+    this.chain = new Chain({ connection, programId });
+    this.clmmClient = new CLMMClient({ chain: this.chain });
+    this.token = new Token(connection);
+  }
+
+  // Get pool list
+  async getPools(req: IPoolsReq) {
+    return this.api.getPools(req);
+  }
+
+  // Get pool details by ids
+  async getPoolsByIds(req: IPoolsByIdsReq) {
+    const result = await this.api.getPoolsByIds(req);
+
+    return result;
+  }
+
+  // Get user positions
+  async getMyPositions(req: IMyPositionsReq) {
+    return this.api.getMyPositions(req);
+  }
+
+  // Collect single position fees
+  collectFees(params: ParamsWithSignerCallback<ICollectFeesParams>) {
+    return this.chain.collectFees(params);
+  }
+
+  // Get tick information
+  async getTicks(req: ITickReq) {
+    return this.api.getTicks(req);
+  }
+
+  /**
+   *
+   * @param id Pool address
+   * @returns
+   */
+  async getRawPoolInfoByPoolId(id: string): Promise<IPoolLayoutWithId> {
+    return this.chain.getRawPoolInfoByPoolId(id);
+  }
+
+  async getPositionNftMintListByUserAddress(userAddress: PublicKey): Promise<PublicKey[]> {
+    const positionList = await this.chain.getRawPositionInfoListByUserAddress(userAddress);
+    return positionList.map((position) => position.nftMint);
+  }
+}
+
+export * from './chain/index';
+export * from './chain/models';
+export * from './apis/index';
+export * from './apis/ky';
+export * from './liquidity/clmm';
+export * from './apis/poolsModels';
+export * from './apis/swapModels';
+export * from './apis/positionModels';
+export * from './apis/tickModels';

+ 42 - 0
src/lib/byreal-clmm-sdk/src/client/liquidity/abstract.ts

@@ -0,0 +1,42 @@
+import { PublicKey } from '@solana/web3.js';
+
+import { Chain } from '../chain/index';
+import { IInstructionReturn } from '../chain/models';
+
+export abstract class AbstractLiquidityClient {
+  protected _chain: Chain;
+
+  constructor(params: { chain: Chain }) {
+    this._chain = params.chain;
+  }
+  /**
+   * Create a position
+   * @param opts
+   * @returns
+   */
+  abstract createPosition(opts: any): Promise<IInstructionReturn>;
+
+  /**
+   * Add liquidity
+   * @param userAddress
+   * @param nftMint
+   * @returns
+   */
+  abstract addLiquidity(opts: any): Promise<IInstructionReturn>;
+
+  /**
+   * Decrease liquidity
+   * @param walletAddress
+   * @param nftMint
+   * @param decreasePercentage
+   * @returns
+   */
+  abstract decreaseLiquidity(walletAddress: PublicKey, nftMint: string, decreasePercentage: number): Promise<any>;
+
+  /**
+   * Remove all liquidity
+   * @param userAddress
+   * @param nftMint
+   */
+  abstract removeFullLiquidity(userAddress: string, nftMint: string): Promise<any>;
+}

+ 76 - 0
src/lib/byreal-clmm-sdk/src/client/liquidity/clmm.ts

@@ -0,0 +1,76 @@
+import { PublicKey } from '@solana/web3.js';
+import BN from 'bn.js';
+
+import { Chain } from '../chain/index';
+import { IAddLiquidityParams } from '../chain/models';
+
+import { AbstractLiquidityClient } from './abstract';
+import { ICLMMCreatePositionOptions } from './types';
+
+export class CLMMClient extends AbstractLiquidityClient {
+  constructor(params: { chain: Chain }) {
+    super(params);
+  }
+
+  /**
+   * Create a position
+   * @param options
+   * @returns
+   */
+  async createPosition(options: ICLMMCreatePositionOptions) {
+    return this._chain.createPositionInstructions({
+      userAddress: options.walletAddress,
+      poolInfo: options.rpcPoolInfo,
+      tickLower: options.tickLower,
+      tickUpper: options.tickUpper,
+      base: options.baseType,
+      baseAmount: options.baseTokenAmount,
+      otherAmountMax: options.quoteTokenAmount,
+    });
+  }
+
+  async addLiquidity(options: IAddLiquidityParams) {
+    return this._chain.addLiquidityInstructions(options);
+  }
+
+  /**
+   * Decrease liquidity
+   * @param nftMint
+   * @returns
+   */
+  async decreaseLiquidity(walletAddress: PublicKey, nftMint: string, decreasePercentage: number) {
+    const _nftMint = new PublicKey(nftMint);
+    const positionInfo = await this._chain.getRawPositionInfoByNftMint(_nftMint);
+
+    if (!positionInfo) {
+      throw new Error('Position not found');
+    }
+
+    const { liquidity } = positionInfo;
+    const liquidityToDecrease = liquidity.mul(new BN(decreasePercentage)).div(new BN(100));
+
+    const params = {
+      userAddress: walletAddress,
+      nftMint: _nftMint,
+      liquidity: liquidityToDecrease,
+    };
+
+    const { transaction } = await this._chain.decreaseLiquidityInstructions(params);
+    return transaction;
+  }
+
+  /**
+   * Remove all liquidity
+   * @param userAddress
+   * @param nftMint
+   */
+  async removeFullLiquidity(userAddress: string, nftMint: string) {
+    const _nftMint = new PublicKey(nftMint);
+
+    const { transaction } = await this._chain.decreaseFullLiquidityInstructions({
+      userAddress: new PublicKey(userAddress),
+      nftMint: _nftMint,
+    });
+    return transaction;
+  }
+}

+ 14 - 0
src/lib/byreal-clmm-sdk/src/client/liquidity/types.ts

@@ -0,0 +1,14 @@
+import { PublicKey } from '@solana/web3.js';
+import BN from 'bn.js';
+
+import { IPoolLayoutWithId } from '../../instructions/index.js';
+
+export interface ICLMMCreatePositionOptions {
+  walletAddress: PublicKey;
+  rpcPoolInfo: IPoolLayoutWithId;
+  baseTokenAmount: BN;
+  quoteTokenAmount: BN;
+  tickLower: number;
+  tickUpper: number;
+  baseType: 'MintA' | 'MintB';
+}

+ 105 - 0
src/lib/byreal-clmm-sdk/src/client/token.ts

@@ -0,0 +1,105 @@
+import {
+  TOKEN_PROGRAM_ID,
+  TOKEN_2022_PROGRAM_ID,
+  getAssociatedTokenAddress,
+  getMint,
+  AccountLayout,
+} from '@solana/spl-token';
+import { Connection, PublicKey } from '@solana/web3.js';
+
+import { isToken2022 } from '../utils/token';
+
+export class Token {
+  private readonly _connection: Connection;
+
+  constructor(connection: Connection) {
+    this._connection = connection;
+  }
+
+  async isToken2022(mintAddress: string) {
+    return isToken2022(mintAddress, this._connection);
+  }
+
+  /**
+   * Detect token type and get balance
+   * @param walletAddress Wallet address
+   * @param tokenMintAddress Token Mint address
+   * @returns Promise object containing balance and whether it is Token-2022
+   */
+  async detectTokenTypeAndGetBalance(
+    walletAddress: string,
+    tokenMintAddress: string,
+  ): Promise<{ balance: number; isToken2022: boolean }> {
+    const walletPublicKey = new PublicKey(walletAddress);
+    const mintPublicKey = new PublicKey(tokenMintAddress);
+
+    // Compatible with native SOL
+    if (tokenMintAddress === 'So11111111111111111111111111111111111111112') {
+      const accountInfo = await this._connection.getAccountInfo(walletPublicKey);
+      if (accountInfo) {
+        const balance = Number(accountInfo.lamports) / Math.pow(10, 9); // SOL精度固定9位
+        return { balance, isToken2022: false };
+      }
+      return { balance: 0, isToken2022: false };
+    }
+
+    // First try to query as Token-2022
+    try {
+      const mint = await getMint(this._connection, mintPublicKey, 'confirmed', TOKEN_2022_PROGRAM_ID);
+      const tokenAccount = await getAssociatedTokenAddress(
+        mintPublicKey,
+        walletPublicKey,
+        false,
+        TOKEN_2022_PROGRAM_ID,
+      );
+
+      const accountInfo = await this._connection.getAccountInfo(tokenAccount);
+      if (accountInfo) {
+        // Parse account data to get balance
+        const data = AccountLayout.decode(accountInfo.data);
+        const balance = Number(data.amount) / Math.pow(10, mint.decimals);
+        return { balance, isToken2022: true };
+      }
+    } catch (error) {
+      // If it is not Token-2022, try to query as a standard SPL Token
+      console.log('Not a Token-2022, trying standard SPL Token...');
+    }
+
+    // Try to query as a standard SPL Token
+    try {
+      const mint = await getMint(this._connection, mintPublicKey, 'confirmed', TOKEN_PROGRAM_ID);
+      const tokenAccount = await getAssociatedTokenAddress(mintPublicKey, walletPublicKey, false, TOKEN_PROGRAM_ID);
+
+      const accountInfo = await this._connection.getAccountInfo(tokenAccount);
+      if (accountInfo) {
+        const data = AccountLayout.decode(accountInfo.data);
+        const balance = Number(data.amount) / Math.pow(10, mint.decimals);
+        return { balance, isToken2022: false };
+      }
+    } catch (error) {
+      console.error('Error detecting token type:', error);
+    }
+
+    return { balance: 0, isToken2022: false };
+  }
+
+  /**
+   * Batch detect token type and get balance
+   * @param walletAddress Wallet address
+   * @param tokenMintAddresses Token Mint address array
+   * @returns Promise<{ tokenMintAddress: string; balance: number; isToken2022: boolean }[]>
+   */
+  async batchDetectTokenTypeAndGetBalance(
+    walletAddress: string,
+    tokenMintAddresses: string[],
+  ): Promise<{ tokenMintAddress: string; balance: number; isToken2022: boolean }[]> {
+    // Concurrent processing of all tokens
+    const results = await Promise.all(
+      tokenMintAddresses.map(async (tokenMintAddress) => {
+        const res = await this.detectTokenTypeAndGetBalance(walletAddress, tokenMintAddress);
+        return { tokenMintAddress, ...res };
+      }),
+    );
+    return results;
+  }
+}

+ 30 - 0
src/lib/byreal-clmm-sdk/src/constants.ts

@@ -0,0 +1,30 @@
+import { PublicKey, SystemProgram } from '@solana/web3.js';
+import BN from 'bn.js';
+
+// Number of price points contained in each tick array
+export const TICK_ARRAY_SIZE = 60;
+// Size of tick array bitmap, used to track which tick arrays have been initialized
+export const TICK_ARRAY_BITMAP_SIZE = 512;
+// Size of extended tick array bitmap, used to handle additional liquidity ranges
+export const EXTENSION_TICKARRAY_BITMAP_SIZE = 14;
+
+// Dynamic Tick Array constants
+// Struct length: 208 bytes
+// Header length: 8 (discriminator) + 208 (struct) = 216 bytes
+export const DYN_TICK_ARRAY_STRUCT_LEN = 208;
+export const DYN_TICK_ARRAY_HEADER_LEN = 216;
+// TickState size: 168 bytes
+export const TICK_STATE_LEN = 168;
+
+export const U64_IGNORE_RANGE = new BN('18446744073700000000');
+
+export const BYREAL_CLMM_PROGRAM_ID = new PublicKey('REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2');
+
+export const MEMO_PROGRAM_ID = new PublicKey('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr');
+export const RENT_PROGRAM_ID = new PublicKey('SysvarRent111111111111111111111111111111111');
+export const CLOCK_PROGRAM_ID = new PublicKey('SysvarC1ock11111111111111111111111111111111');
+export const METADATA_PROGRAM_ID = new PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s');
+export const INSTRUCTION_PROGRAM_ID = new PublicKey('Sysvar1nstructions1111111111111111111111111');
+export const WSOLMint = new PublicKey('So11111111111111111111111111111111111111112');
+export const SOLMint = PublicKey.default;
+export const SYSTEM_PROGRAM_ID = SystemProgram.programId;

+ 5 - 0
src/lib/byreal-clmm-sdk/src/index.ts

@@ -0,0 +1,5 @@
+export * from './constants';
+export * from './instructions/index';
+export * from './client/index';
+export * from './utils/index';
+export * from './calculate';

+ 1203 - 0
src/lib/byreal-clmm-sdk/src/instructions/baseInstruction.ts

@@ -0,0 +1,1203 @@
+import * as anchor from '@coral-xyz/anchor';
+import { Program } from '@coral-xyz/anchor';
+import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
+import { TOKEN_2022_PROGRAM_ID } from '@solana/spl-token';
+import { PublicKey, TransactionInstruction, Connection, clusterApiUrl } from '@solana/web3.js';
+import BN from 'bn.js';
+
+import { BYREAL_CLMM_PROGRAM_ID } from '../constants';
+
+import ByrealClmmIDL from './target/idl/byreal_amm_v3.json';
+import { ByrealClmm } from './target/types/byreal_amm_v3';
+
+export const getAmmV3Program = (programId: PublicKey): Program<ByrealClmm> => {
+  // Create a read-only provider, no wallet needed
+  const provider = new anchor.AnchorProvider(
+    new Connection(clusterApiUrl('mainnet-beta'), 'confirmed'),
+    {} as any, // Empty wallet object, since only instruction creation is needed
+    { commitment: 'confirmed' },
+  );
+
+  if (programId.toBase58() === BYREAL_CLMM_PROGRAM_ID.toBase58()) {
+    return new Program<ByrealClmm>(ByrealClmmIDL as any, provider);
+  }
+
+  throw new Error('[getAmmV3Program error]: Invalid program id');
+};
+
+export class BaseInstruction {
+  static async createPoolInstruction(
+    programId: PublicKey,
+    poolCreator: PublicKey,
+    poolManager: PublicKey,
+    ammConfigId: PublicKey,
+    mintA: PublicKey,
+    mintProgramIdA: PublicKey,
+    mintB: PublicKey,
+    mintProgramIdB: PublicKey,
+    sqrtPriceX64: BN,
+    openTime?: BN,
+    extendMintAccount?: PublicKey[],
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const _openTime = openTime || new BN(0);
+
+    const instruction = program.methods.createPool(sqrtPriceX64, _openTime).accounts({
+      poolCreator,
+      poolManager,
+      ammConfig: ammConfigId,
+      tokenMint0: mintA,
+      tokenMint1: mintB,
+      tokenProgram0: mintProgramIdA,
+      tokenProgram1: mintProgramIdB,
+    });
+
+    // If there are additional mint accounts, add them as remaining accounts
+    if (extendMintAccount && extendMintAccount.length > 0) {
+      instruction.remainingAccounts(
+        extendMintAccount.map((k) => ({
+          pubkey: k,
+          isSigner: false,
+          isWritable: false,
+        })),
+      );
+    }
+
+    return await instruction.instruction();
+  }
+
+  static async openPositionFromLiquidityInstruction(
+    programId: PublicKey,
+    payer: PublicKey,
+    poolId: PublicKey,
+    positionNftOwner: PublicKey,
+    positionNftMint: PublicKey,
+    positionNftAccount: PublicKey,
+    metadataAccount: PublicKey,
+    protocolPosition: PublicKey,
+    tickArrayLower: PublicKey,
+    tickArrayUpper: PublicKey,
+    personalPosition: PublicKey,
+    ownerTokenAccountA: PublicKey,
+    ownerTokenAccountB: PublicKey,
+    tokenVaultA: PublicKey,
+    tokenVaultB: PublicKey,
+    tokenMintA: PublicKey,
+    tokenMintB: PublicKey,
+
+    tickLowerIndex: number,
+    tickUpperIndex: number,
+    tickArrayLowerStartIndex: number,
+    tickArrayUpperStartIndex: number,
+    liquidity: BN,
+    amountMaxA: BN,
+    amountMaxB: BN,
+    withMetadata: 'create' | 'no-create',
+
+    exTickArrayBitmap?: PublicKey,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods
+      .openPositionV2(
+        tickLowerIndex,
+        tickUpperIndex,
+        tickArrayLowerStartIndex,
+        tickArrayUpperStartIndex,
+        liquidity,
+        amountMaxA,
+        amountMaxB,
+        withMetadata === 'create',
+        null, // Set baseFlag to null, since this is based on liquidity
+      )
+      .accountsPartial({
+        payer,
+        positionNftOwner,
+        positionNftMint,
+        positionNftAccount,
+        metadataAccount,
+        poolState: poolId,
+        protocolPosition,
+        tickArrayLower,
+        tickArrayUpper,
+        personalPosition,
+        tokenAccount0: ownerTokenAccountA,
+        tokenAccount1: ownerTokenAccountB,
+        tokenVault0: tokenVaultA,
+        tokenVault1: tokenVaultB,
+        vault0Mint: tokenMintA,
+        vault1Mint: tokenMintB,
+      });
+
+    // If there are additional tick array bitmaps, add them as remaining accounts
+    if (exTickArrayBitmap) {
+      instruction.remainingAccounts([{ pubkey: exTickArrayBitmap, isSigner: false, isWritable: true }]);
+    }
+
+    return await instruction.instruction();
+  }
+
+  static async openPositionFromLiquidityInstruction22(
+    programId: PublicKey,
+    payer: PublicKey,
+    poolId: PublicKey,
+    positionNftOwner: PublicKey,
+    positionNftMint: PublicKey,
+    positionNftAccount: PublicKey,
+    protocolPosition: PublicKey,
+    tickArrayLower: PublicKey,
+    tickArrayUpper: PublicKey,
+    personalPosition: PublicKey,
+    ownerTokenAccountA: PublicKey,
+    ownerTokenAccountB: PublicKey,
+    tokenVaultA: PublicKey,
+    tokenVaultB: PublicKey,
+    tokenMintA: PublicKey,
+    tokenMintB: PublicKey,
+
+    tickLowerIndex: number,
+    tickUpperIndex: number,
+    tickArrayLowerStartIndex: number,
+    tickArrayUpperStartIndex: number,
+    liquidity: BN,
+    amountMaxA: BN,
+    amountMaxB: BN,
+    withMetadata: 'create' | 'no-create',
+
+    exTickArrayBitmap?: PublicKey,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods
+      .openPositionWithToken22Nft(
+        tickLowerIndex,
+        tickUpperIndex,
+        tickArrayLowerStartIndex,
+        tickArrayUpperStartIndex,
+        liquidity,
+        amountMaxA,
+        amountMaxB,
+        withMetadata === 'create',
+        null,
+      )
+      .accountsPartial({
+        payer,
+        positionNftOwner,
+        positionNftMint,
+        positionNftAccount,
+        poolState: poolId,
+        // Here we need to manually pass in the generated PDA account, because anchor auto-generation uses little endian, while raydium's contract uses big endian (tick_lower_index.to_be_bytes()), using anchor auto-generation will cause errors;
+        protocolPosition,
+        tickArrayLower,
+        tickArrayUpper,
+        personalPosition,
+
+        tokenAccount0: ownerTokenAccountA,
+        tokenAccount1: ownerTokenAccountB,
+        tokenVault0: tokenVaultA,
+        tokenVault1: tokenVaultB,
+        vault0Mint: tokenMintA,
+        vault1Mint: tokenMintB,
+      });
+
+    // If there are additional tick array bitmaps, add them as remaining accounts
+    if (exTickArrayBitmap) {
+      instruction.remainingAccounts([{ pubkey: exTickArrayBitmap, isSigner: false, isWritable: true }]);
+    }
+
+    return await instruction.instruction();
+  }
+
+  static async openPositionFromBaseInstruction(
+    programId: PublicKey,
+    payer: PublicKey,
+    poolId: PublicKey,
+    positionNftOwner: PublicKey,
+    positionNftMint: PublicKey,
+    positionNftAccount: PublicKey,
+    metadataAccount: PublicKey,
+    protocolPosition: PublicKey,
+    tickArrayLower: PublicKey,
+    tickArrayUpper: PublicKey,
+    personalPosition: PublicKey,
+    ownerTokenAccountA: PublicKey,
+    ownerTokenAccountB: PublicKey,
+    tokenVaultA: PublicKey,
+    tokenVaultB: PublicKey,
+    tokenMintA: PublicKey,
+    tokenMintB: PublicKey,
+
+    tickLowerIndex: number,
+    tickUpperIndex: number,
+    tickArrayLowerStartIndex: number,
+    tickArrayUpperStartIndex: number,
+
+    withMetadata: 'create' | 'no-create',
+    base: 'MintA' | 'MintB',
+    baseAmount: BN,
+    otherAmountMax: BN,
+
+    exTickArrayBitmap?: PublicKey,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods
+      .openPositionV2(
+        tickLowerIndex,
+        tickUpperIndex,
+        tickArrayLowerStartIndex,
+        tickArrayUpperStartIndex,
+        new BN(0), // Set liquidity to 0, since this is based on base amount
+        base === 'MintA' ? baseAmount : otherAmountMax, // amount0Max
+        base === 'MintA' ? otherAmountMax : baseAmount, // amount1Max
+        withMetadata === 'create',
+        base === 'MintA', // baseFlag
+      )
+      .accountsPartial({
+        payer,
+        positionNftOwner,
+        positionNftMint,
+        positionNftAccount,
+        metadataAccount,
+        poolState: poolId,
+        protocolPosition,
+        tickArrayLower,
+        tickArrayUpper,
+        personalPosition,
+        tokenAccount0: ownerTokenAccountA,
+        tokenAccount1: ownerTokenAccountB,
+        tokenVault0: tokenVaultA,
+        tokenVault1: tokenVaultB,
+        vault0Mint: tokenMintA,
+        vault1Mint: tokenMintB,
+      });
+
+    // If there are additional tick array bitmaps, add them as remaining accounts
+    if (exTickArrayBitmap) {
+      instruction.remainingAccounts([{ pubkey: exTickArrayBitmap, isSigner: false, isWritable: true }]);
+    }
+
+    return await instruction.instruction();
+  }
+
+  static async openPositionFromBaseInstruction22(
+    programId: PublicKey,
+    payer: PublicKey,
+    poolId: PublicKey,
+    positionNftOwner: PublicKey,
+    positionNftMint: PublicKey,
+    positionNftAccount: PublicKey,
+    protocolPosition: PublicKey,
+    tickArrayLower: PublicKey,
+    tickArrayUpper: PublicKey,
+    personalPosition: PublicKey,
+    ownerTokenAccountA: PublicKey,
+    ownerTokenAccountB: PublicKey,
+    tokenVaultA: PublicKey,
+    tokenVaultB: PublicKey,
+    tokenMintA: PublicKey,
+    tokenMintB: PublicKey,
+
+    tickLowerIndex: number,
+    tickUpperIndex: number,
+    tickArrayLowerStartIndex: number,
+    tickArrayUpperStartIndex: number,
+
+    withMetadata: 'create' | 'no-create',
+    base: 'MintA' | 'MintB',
+    baseAmount: BN,
+    otherAmountMax: BN,
+
+    exTickArrayBitmap?: PublicKey,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods
+      .openPositionWithToken22Nft(
+        tickLowerIndex,
+        tickUpperIndex,
+        tickArrayLowerStartIndex,
+        tickArrayUpperStartIndex,
+        new BN(0), // Set liquidity to 0, since this is based on base amount
+        base === 'MintA' ? baseAmount : otherAmountMax, // amountMaxA
+        base === 'MintA' ? otherAmountMax : baseAmount, // amountMaxB
+        withMetadata === 'create',
+        base === 'MintA', // baseFlag
+      )
+      .accountsPartial({
+        payer,
+        positionNftOwner,
+        positionNftMint,
+        positionNftAccount,
+        poolState: poolId,
+        // Here we need to manually pass in the generated PDA account, because anchor auto-generation uses little endian; while raydium's contract uses big endian (tick_lower_index.to_be_bytes())
+        protocolPosition,
+        tickArrayLower,
+        tickArrayUpper,
+        personalPosition,
+
+        tokenAccount0: ownerTokenAccountA,
+        tokenAccount1: ownerTokenAccountB,
+        tokenVault0: tokenVaultA,
+        tokenVault1: tokenVaultB,
+        vault0Mint: tokenMintA,
+        vault1Mint: tokenMintB,
+      });
+
+    // If there are additional tick array bitmaps, add them as remaining accounts
+    if (exTickArrayBitmap) {
+      instruction.remainingAccounts([{ pubkey: exTickArrayBitmap, isSigner: false, isWritable: true }]);
+    }
+
+    return await instruction.instruction();
+  }
+
+  static async closePositionInstruction(
+    programId: PublicKey,
+    positionNftOwner: PublicKey,
+    positionNftMint: PublicKey,
+    positionNftAccount: PublicKey,
+    nft2022?: boolean,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods.closePosition().accounts({
+      nftOwner: positionNftOwner,
+      positionNftMint,
+      positionNftAccount,
+      tokenProgram: nft2022 ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID,
+    });
+
+    return await instruction.instruction();
+  }
+
+  static async increasePositionFromLiquidityInstruction(
+    programId: PublicKey,
+    positionNftOwner: PublicKey,
+    positionNftAccount: PublicKey,
+    personalPosition: PublicKey,
+
+    poolId: PublicKey,
+    protocolPosition: PublicKey,
+    tickArrayLower: PublicKey,
+    tickArrayUpper: PublicKey,
+    ownerTokenAccountA: PublicKey,
+    ownerTokenAccountB: PublicKey,
+    mintVaultA: PublicKey,
+    mintVaultB: PublicKey,
+    mintMintA: PublicKey,
+    mintMintB: PublicKey,
+
+    liquidity: BN,
+    amountMaxA: BN,
+    amountMaxB: BN,
+
+    exTickArrayBitmap?: PublicKey,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods
+      .increaseLiquidityV2(
+        liquidity,
+        amountMaxA,
+        amountMaxB,
+        null, // Set baseFlag to null, since this is based on liquidity instruction
+      )
+      .accountsPartial({
+        nftOwner: positionNftOwner,
+        nftAccount: positionNftAccount,
+        poolState: poolId,
+        protocolPosition,
+        personalPosition,
+        tickArrayLower,
+        tickArrayUpper,
+        tokenAccount0: ownerTokenAccountA,
+        tokenAccount1: ownerTokenAccountB,
+        tokenVault0: mintVaultA,
+        tokenVault1: mintVaultB,
+        vault0Mint: mintMintA,
+        vault1Mint: mintMintB,
+      });
+
+    // If there are additional tick array bitmaps, add them as remaining accounts
+    if (exTickArrayBitmap) {
+      instruction.remainingAccounts([{ pubkey: exTickArrayBitmap, isSigner: false, isWritable: true }]);
+    }
+
+    return await instruction.instruction();
+  }
+
+  static async increasePositionFromBaseInstruction(
+    programId: PublicKey,
+    positionNftOwner: PublicKey,
+    positionNftAccount: PublicKey,
+    personalPosition: PublicKey,
+
+    poolId: PublicKey,
+    protocolPosition: PublicKey,
+    tickArrayLower: PublicKey,
+    tickArrayUpper: PublicKey,
+    ownerTokenAccountA: PublicKey,
+    ownerTokenAccountB: PublicKey,
+    mintVaultA: PublicKey,
+    mintVaultB: PublicKey,
+    mintMintA: PublicKey,
+    mintMintB: PublicKey,
+
+    base: 'MintA' | 'MintB',
+    baseAmount: BN,
+    otherAmountMax: BN,
+
+    exTickArrayBitmap?: PublicKey,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods
+      .increaseLiquidityV2(
+        new BN(0), // Set liquidity to 0, since this is based on base amount
+        base === 'MintA' ? baseAmount : otherAmountMax, // amount0Max
+        base === 'MintA' ? otherAmountMax : baseAmount, // amount1Max
+        base === 'MintA', // baseFlag
+      )
+      .accountsPartial({
+        nftOwner: positionNftOwner,
+        nftAccount: positionNftAccount,
+        poolState: poolId,
+        protocolPosition,
+        personalPosition,
+        tickArrayLower,
+        tickArrayUpper,
+        tokenAccount0: ownerTokenAccountA,
+        tokenAccount1: ownerTokenAccountB,
+        tokenVault0: mintVaultA,
+        tokenVault1: mintVaultB,
+        vault0Mint: mintMintA,
+        vault1Mint: mintMintB,
+      });
+
+    // If there are additional tick array bitmaps, add them as remaining accounts
+    if (exTickArrayBitmap) {
+      instruction.remainingAccounts([{ pubkey: exTickArrayBitmap, isSigner: false, isWritable: true }]);
+    }
+
+    return await instruction.instruction();
+  }
+
+  static async decreaseLiquidityInstruction(
+    programId: PublicKey,
+    positionNftOwner: PublicKey,
+    positionNftAccount: PublicKey,
+    personalPosition: PublicKey,
+
+    poolId: PublicKey,
+    protocolPosition: PublicKey,
+    tickArrayLower: PublicKey,
+    tickArrayUpper: PublicKey,
+    ownerTokenAccountA: PublicKey,
+    ownerTokenAccountB: PublicKey,
+    mintVaultA: PublicKey,
+    mintVaultB: PublicKey,
+    mintMintA: PublicKey,
+    mintMintB: PublicKey,
+    rewardAccounts: {
+      poolRewardVault: PublicKey;
+      ownerRewardVault: PublicKey;
+      rewardMint: PublicKey;
+    }[],
+
+    liquidity: BN,
+    amountMinA: BN,
+    amountMinB: BN,
+
+    exTickArrayBitmap?: PublicKey,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods.decreaseLiquidityV2(liquidity, amountMinA, amountMinB).accountsPartial({
+      nftOwner: positionNftOwner,
+      nftAccount: positionNftAccount,
+      personalPosition,
+      poolState: poolId,
+      protocolPosition,
+      tokenVault0: mintVaultA,
+      tokenVault1: mintVaultB,
+      tickArrayLower,
+      tickArrayUpper,
+      recipientTokenAccount0: ownerTokenAccountA,
+      recipientTokenAccount1: ownerTokenAccountB,
+      vault0Mint: mintMintA,
+      vault1Mint: mintMintB,
+    });
+
+    // Build remaining accounts
+    const remainingAccounts = [
+      ...(exTickArrayBitmap ? [{ pubkey: exTickArrayBitmap, isSigner: false, isWritable: true }] : []),
+      ...rewardAccounts
+        .map((i) => [
+          { pubkey: i.poolRewardVault, isSigner: false, isWritable: true },
+          { pubkey: i.ownerRewardVault, isSigner: false, isWritable: true },
+          { pubkey: i.rewardMint, isSigner: false, isWritable: false },
+        ])
+        .flat(),
+    ];
+
+    if (remainingAccounts.length > 0) {
+      instruction.remainingAccounts(remainingAccounts);
+    }
+
+    return await instruction.instruction();
+  }
+
+  static async swapInstruction(
+    programId: PublicKey,
+    payer: PublicKey,
+    poolId: PublicKey,
+    ammConfigId: PublicKey,
+    inputTokenAccount: PublicKey,
+    outputTokenAccount: PublicKey,
+    inputVault: PublicKey,
+    outputVault: PublicKey,
+    inputMint: PublicKey,
+    outputMint: PublicKey,
+    tickArray: PublicKey[],
+    observationId: PublicKey,
+
+    amount: BN,
+    otherAmountThreshold: BN,
+    sqrtPriceLimitX64: BN,
+    isBaseInput: boolean,
+
+    exTickArrayBitmap?: PublicKey,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods.swapV2(amount, otherAmountThreshold, sqrtPriceLimitX64, isBaseInput).accounts({
+      payer,
+      ammConfig: ammConfigId,
+      poolState: poolId,
+      inputTokenAccount,
+      outputTokenAccount,
+      inputVault,
+      outputVault,
+      observationState: observationId,
+      inputVaultMint: inputMint,
+      outputVaultMint: outputMint,
+    });
+
+    // Build remaining accounts
+    const remainingAccounts = [
+      ...(exTickArrayBitmap ? [{ pubkey: exTickArrayBitmap, isSigner: false, isWritable: true }] : []),
+      ...tickArray.map((i) => ({
+        pubkey: i,
+        isSigner: false,
+        isWritable: true,
+      })),
+    ];
+
+    if (remainingAccounts.length > 0) {
+      instruction.remainingAccounts(remainingAccounts);
+    }
+
+    return await instruction.instruction();
+  }
+
+  static async initRewardInstruction(
+    programId: PublicKey,
+    rewardFunder: PublicKey,
+    funderTokenAccount: PublicKey,
+    ammConfigId: PublicKey,
+    poolId: PublicKey,
+    rewardMint: PublicKey,
+    rewardProgramId: PublicKey,
+
+    openTime: number,
+    endTime: number,
+    emissionsPerSecondX64: BN,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods
+      .initializeReward({
+        openTime: new BN(openTime),
+        endTime: new BN(endTime),
+        emissionsPerSecondX64,
+      })
+      .accounts({
+        rewardFunder,
+        funderTokenAccount,
+        ammConfig: ammConfigId,
+        poolState: poolId,
+        rewardTokenMint: rewardMint,
+        rewardTokenProgram: rewardProgramId,
+      });
+
+    return await instruction.instruction();
+  }
+
+  static async setRewardInstruction(
+    programId: PublicKey,
+    authority: PublicKey,
+    ammConfigId: PublicKey,
+    poolId: PublicKey,
+
+    rewardIndex: number,
+    emissionsPerSecondX64: BN,
+    openTime: number,
+    endTime: number,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods
+      .setRewardParams(rewardIndex, emissionsPerSecondX64, new BN(openTime), new BN(endTime))
+      .accounts({
+        authority,
+        ammConfig: ammConfigId,
+        poolState: poolId,
+      });
+
+    return await instruction.instruction();
+  }
+
+  static async collectRewardInstruction(
+    programId: PublicKey,
+    rewardFunder: PublicKey,
+    funderTokenAccount: PublicKey,
+    poolId: PublicKey,
+    rewardVault: PublicKey,
+    rewardMint: PublicKey,
+
+    rewardIndex: number,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods.collectRemainingRewards(rewardIndex).accounts({
+      rewardFunder,
+      funderTokenAccount,
+      poolState: poolId,
+      rewardTokenVault: rewardVault,
+      rewardVaultMint: rewardMint,
+    });
+
+    return await instruction.instruction();
+  }
+
+  static async createAmmConfigInstruction(
+    programId: PublicKey,
+    owner: PublicKey,
+    ammConfigId: PublicKey,
+    index: number,
+    tickSpacing: number,
+    tradeFeeRate: number,
+    protocolFeeRate: number,
+    fundFeeRate: number,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods
+      .createAmmConfig(index, tickSpacing, tradeFeeRate, protocolFeeRate, fundFeeRate)
+      .accountsPartial({
+        owner,
+        ammConfig: ammConfigId,
+      });
+
+    return await instruction.instruction();
+  }
+
+  static async updateAmmConfigInstruction(
+    programId: PublicKey,
+    ammConfigId: PublicKey,
+    owner: PublicKey,
+    param: number,
+    value: number,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods.updateAmmConfig(param, value).accounts({
+      owner,
+      ammConfig: ammConfigId,
+    });
+
+    return await instruction.instruction();
+  }
+
+  static async updatePoolStatusInstruction(
+    programId: PublicKey,
+    authority: PublicKey,
+    poolState: PublicKey,
+    status: number,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods.updatePoolStatus(status).accounts({
+      poolState,
+      authority,
+    });
+
+    return await instruction.instruction();
+  }
+
+  static async createSupportMintAssociatedInstruction(
+    programId: PublicKey,
+    owner: PublicKey,
+    tokenMint: PublicKey,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods.createSupportMintAssociated().accounts({
+      owner,
+      tokenMint,
+    });
+
+    return await instruction.instruction();
+  }
+
+  static async createOperationAccountInstruction(
+    programId: PublicKey,
+    owner: PublicKey,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods.createOperationAccount().accounts({
+      owner,
+    });
+
+    return await instruction.instruction();
+  }
+
+  static async updateOperationAccountInstruction(
+    programId: PublicKey,
+    owner: PublicKey,
+    param: number,
+    keys: PublicKey[],
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods.updateOperationAccount(param, keys).accounts({
+      owner,
+    });
+
+    return await instruction.instruction();
+  }
+
+  static async transferRewardOwnerInstruction(
+    programId: PublicKey,
+    authority: PublicKey,
+    poolState: PublicKey,
+    newOwner: PublicKey,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods.transferRewardOwner(newOwner).accounts({
+      poolState,
+      authority,
+    });
+
+    return await instruction.instruction();
+  }
+
+  static async updateRewardInfosInstruction(
+    programId: PublicKey,
+    poolState: PublicKey,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods.updateRewardInfos().accounts({
+      poolState,
+    });
+
+    return await instruction.instruction();
+  }
+
+  static async collectProtocolFeeInstruction(
+    programId: PublicKey,
+    poolState: PublicKey,
+    tokenVault0: PublicKey,
+    tokenVault1: PublicKey,
+    vault0Mint: PublicKey,
+    vault1Mint: PublicKey,
+
+    amount0Requested: BN,
+    amount1Requested: BN,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods.collectProtocolFee(amount0Requested, amount1Requested).accounts({
+      poolState,
+      tokenVault0,
+      tokenVault1,
+      vault0Mint,
+      vault1Mint,
+    });
+
+    return await instruction.instruction();
+  }
+
+  static async collectFundFeeInstruction(
+    programId: PublicKey,
+    poolState: PublicKey,
+    tokenVault0: PublicKey,
+    tokenVault1: PublicKey,
+    vault0Mint: PublicKey,
+    vault1Mint: PublicKey,
+
+    amount0Requested: BN,
+    amount1Requested: BN,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods.collectFundFee(amount0Requested, amount1Requested).accounts({
+      poolState,
+      tokenVault0,
+      tokenVault1,
+      vault0Mint,
+      vault1Mint,
+    });
+
+    return await instruction.instruction();
+  }
+
+  static async _legacy_openPosition(
+    programId: PublicKey,
+    payer: PublicKey,
+    positionNftOwner: PublicKey,
+    positionNftMint: PublicKey,
+    positionNftAccount: PublicKey,
+    metadataAccount: PublicKey,
+    poolState: PublicKey,
+    protocolPosition: PublicKey,
+    tickArrayLower: PublicKey,
+    tickArrayUpper: PublicKey,
+    personalPosition: PublicKey,
+    tokenAccount0: PublicKey,
+    tokenAccount1: PublicKey,
+    tokenVault0: PublicKey,
+    tokenVault1: PublicKey,
+
+    tickLowerIndex: number,
+    tickUpperIndex: number,
+    tickArrayLowerStartIndex: number,
+    tickArrayUpperStartIndex: number,
+    liquidity: BN,
+    amount0Max: BN,
+    amount1Max: BN,
+
+    remainingAccounts?: {
+      pubkey: PublicKey;
+      isSigner: boolean;
+      isWritable: boolean;
+    }[],
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods
+      .openPosition(
+        tickLowerIndex,
+        tickUpperIndex,
+        tickArrayLowerStartIndex,
+        tickArrayUpperStartIndex,
+        liquidity,
+        amount0Max,
+        amount1Max,
+      )
+      .accountsPartial({
+        payer,
+        positionNftOwner,
+        positionNftMint,
+        positionNftAccount,
+        metadataAccount,
+        poolState,
+        protocolPosition,
+        tickArrayLower,
+        tickArrayUpper,
+        personalPosition,
+        tokenAccount0,
+        tokenAccount1,
+        tokenVault0,
+        tokenVault1,
+      });
+
+    // Add remaining accounts
+    if (remainingAccounts && remainingAccounts.length > 0) {
+      instruction.remainingAccounts(remainingAccounts);
+    }
+
+    return await instruction.instruction();
+  }
+
+  static async _legacy_increaseLiquidity(
+    programId: PublicKey,
+    nftOwner: PublicKey,
+    nftAccount: PublicKey,
+    poolState: PublicKey,
+    protocolPosition: PublicKey,
+    personalPosition: PublicKey,
+    tickArrayLower: PublicKey,
+    tickArrayUpper: PublicKey,
+    tokenAccount0: PublicKey,
+    tokenAccount1: PublicKey,
+    tokenVault0: PublicKey,
+    tokenVault1: PublicKey,
+
+    liquidity: BN,
+    amount0Max: BN,
+    amount1Max: BN,
+
+    remainingAccounts?: {
+      pubkey: PublicKey;
+      isSigner: boolean;
+      isWritable: boolean;
+    }[],
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods.increaseLiquidity(liquidity, amount0Max, amount1Max).accountsPartial({
+      nftOwner,
+      nftAccount,
+      poolState,
+      protocolPosition,
+      personalPosition,
+      tickArrayLower,
+      tickArrayUpper,
+      tokenAccount0,
+      tokenAccount1,
+      tokenVault0,
+      tokenVault1,
+    });
+
+    // Add remaining accounts
+    if (remainingAccounts && remainingAccounts.length > 0) {
+      instruction.remainingAccounts(remainingAccounts);
+    }
+
+    return await instruction.instruction();
+  }
+
+  static async _legacy_decreaseLiquidity(
+    programId: PublicKey,
+    nftOwner: PublicKey,
+    nftAccount: PublicKey,
+    personalPosition: PublicKey,
+    poolState: PublicKey,
+    protocolPosition: PublicKey,
+    tokenVaultA: PublicKey,
+    tokenVaultB: PublicKey,
+    tickArrayLower: PublicKey,
+    tickArrayUpper: PublicKey,
+    recipientTokenAccountA: PublicKey,
+    recipientTokenAccountB: PublicKey,
+
+    liquidity: BN,
+    amountAMin: BN,
+    amountBMin: BN,
+
+    remainingAccounts?: {
+      pubkey: PublicKey;
+      isSigner: boolean;
+      isWritable: boolean;
+    }[],
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods.decreaseLiquidity(liquidity, amountAMin, amountBMin).accountsPartial({
+      nftOwner,
+      nftAccount,
+      personalPosition,
+      poolState,
+      protocolPosition,
+      tokenVault0: tokenVaultA,
+      tokenVault1: tokenVaultB,
+      tickArrayLower,
+      tickArrayUpper,
+      recipientTokenAccount0: recipientTokenAccountA,
+      recipientTokenAccount1: recipientTokenAccountB,
+    });
+
+    // Add remaining accounts
+    if (remainingAccounts && remainingAccounts.length > 0) {
+      instruction.remainingAccounts(remainingAccounts);
+    }
+
+    return await instruction.instruction();
+  }
+
+  static async _legacy_swap(
+    programId: PublicKey,
+    payer: PublicKey,
+    ammConfig: PublicKey,
+    poolState: PublicKey,
+    inputTokenAccount: PublicKey,
+    outputTokenAccount: PublicKey,
+    inputVault: PublicKey,
+    outputVault: PublicKey,
+    observationState: PublicKey,
+    tickArray: PublicKey[],
+
+    amount: BN,
+    otherAmountThreshold: BN,
+    sqrtPriceLimitX64: BN,
+    isBaseInput: boolean,
+
+    remainingAccounts?: {
+      pubkey: PublicKey;
+      isSigner: boolean;
+      isWritable: boolean;
+    }[],
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    // First tick array as required account, the rest as remaining accounts
+    const [firstTickArray, ...restTickArrays] = tickArray;
+
+    const instruction = program.methods.swap(amount, otherAmountThreshold, sqrtPriceLimitX64, isBaseInput).accounts({
+      payer,
+      ammConfig,
+      poolState,
+      inputTokenAccount,
+      outputTokenAccount,
+      inputVault,
+      outputVault,
+      observationState,
+      tickArray: firstTickArray,
+    });
+
+    // Build remaining accounts, including remaining tick arrays and additional accounts
+    const allRemainingAccounts = [
+      ...restTickArrays.map((pubkey) => ({
+        pubkey,
+        isSigner: false,
+        isWritable: true,
+      })),
+      ...(remainingAccounts || []),
+    ];
+
+    if (allRemainingAccounts.length > 0) {
+      instruction.remainingAccounts(allRemainingAccounts);
+    }
+
+    return await instruction.instruction();
+  }
+
+  // Initialize admin group
+  static async initAmmAdminGroupInstruction(
+    programId: PublicKey,
+    params: {
+      feeKeeper: PublicKey;
+      rewardConfigManager: PublicKey;
+      rewardClaimManager: PublicKey;
+      poolManager: PublicKey;
+      emergencyManager: PublicKey;
+      normalManager: PublicKey;
+    },
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods.initAmmAdminGroup({
+      feeKeeper: params.feeKeeper,
+      rewardConfigManager: params.rewardConfigManager,
+      rewardClaimManager: params.rewardClaimManager,
+      poolManager: params.poolManager,
+      emergencyManager: params.emergencyManager,
+      normalManager: params.normalManager,
+    });
+
+    return await instruction.instruction();
+  }
+
+  // Update admin group
+  static async updateAmmAdminGroupInstruction(
+    programId: PublicKey,
+    params: {
+      feeKeeper?: PublicKey;
+      rewardConfigManager?: PublicKey;
+      rewardClaimManager?: PublicKey;
+      poolManager?: PublicKey;
+      emergencyManager?: PublicKey;
+      normalManager?: PublicKey;
+    },
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods.updateAmmAdminGroup({
+      feeKeeper: params.feeKeeper ?? null,
+      rewardConfigManager: params.rewardConfigManager ?? null,
+      rewardClaimManager: params.rewardClaimManager ?? null,
+      poolManager: params.poolManager ?? null,
+      emergencyManager: params.emergencyManager ?? null,
+      normalManager: params.normalManager ?? null,
+    });
+
+    return await instruction.instruction();
+  }
+
+  // New chain-off reward related instructions
+  static async depositOffchainRewardInstruction(
+    programId: PublicKey,
+    poolId: PublicKey,
+    payer: PublicKey,
+    authority: PublicKey,
+    tokenMint: PublicKey,
+    payerTokenAccount: PublicKey,
+    tokenProgram: PublicKey,
+    amount: BN,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods.depositOffchainReward(amount).accountsPartial({
+      payer,
+      poolId,
+      authority,
+      tokenMint,
+      payerTokenAccount,
+      tokenProgram,
+    });
+
+    return await instruction.instruction();
+  }
+
+  static async claimOffchainRewardInstruction(
+    programId: PublicKey,
+    poolId: PublicKey,
+    claimer: PublicKey,
+    authority: PublicKey,
+    tokenMint: PublicKey,
+    claimerTokenAccount: PublicKey,
+    tokenProgram: PublicKey,
+    amount: BN,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods.claimOffchainReward(amount).accountsPartial({
+      claimer,
+      authority,
+      poolId,
+      tokenMint,
+      claimerTokenAccount,
+      tokenProgram,
+    });
+
+    return await instruction.instruction();
+  }
+
+  static async withdrawOffchainRewardInstruction(
+    programId: PublicKey,
+    poolId: PublicKey,
+    authority: PublicKey,
+    tokenMint: PublicKey,
+    receiverTokenAccount: PublicKey,
+    tokenProgram: PublicKey,
+    amount: BN,
+  ): Promise<TransactionInstruction> {
+    const program = getAmmV3Program(programId);
+
+    const instruction = program.methods.withdrawOffchainReward(amount).accountsPartial({
+      authority,
+      tokenMint,
+      poolId,
+      receiverTokenAccount,
+      tokenProgram,
+    });
+
+    return await instruction.instruction();
+  }
+}

+ 41 - 0
src/lib/byreal-clmm-sdk/src/instructions/constants.ts

@@ -0,0 +1,41 @@
+import BN from 'bn.js';
+
+export const ZERO = new BN(0);
+export const ONE = new BN(1);
+export const NEGATIVE_ONE = new BN(-1);
+
+export const Q64 = new BN(1).shln(64);
+export const Q128 = new BN(1).shln(128);
+
+export const MaxU64 = Q64.sub(ONE);
+
+export const U64Resolution = 64;
+
+export const MaxUint128 = Q128.subn(1);
+
+export const MIN_TICK = -443636;
+export const MAX_TICK = -MIN_TICK;
+
+export const MIN_SQRT_PRICE_X64: BN = new BN('4295048016');
+export const MAX_SQRT_PRICE_X64: BN = new BN('79226673521066979257578248091');
+
+export const MIN_SQRT_PRICE_X64_ADD_ONE: BN = new BN('4295048017');
+export const MAX_SQRT_PRICE_X64_SUB_ONE: BN = new BN('79226673521066979257578248090');
+
+export const BIT_PRECISION = 16;
+export const LOG_B_2_X32 = '59543866431248';
+export const LOG_B_P_ERR_MARGIN_LOWER_X64 = '184467440737095516';
+export const LOG_B_P_ERR_MARGIN_UPPER_X64 = '15793534762490258745';
+
+export const FEE_RATE_DENOMINATOR = new BN(10).pow(new BN(6));
+
+export enum Fee {
+  rate_500 = 500, //  500 / 10e6 = 0.0005
+  rate_3000 = 3000, // 3000/ 10e6 = 0.003
+  rate_10000 = 10000, // 10000 /10e6 = 0.01
+}
+export const TICK_SPACINGS: { [amount in Fee]: number } = {
+  [Fee.rate_500]: 10,
+  [Fee.rate_3000]: 60,
+  [Fee.rate_10000]: 200,
+};

+ 155 - 0
src/lib/byreal-clmm-sdk/src/instructions/getRawData.ts

@@ -0,0 +1,155 @@
+import { MintLayout } from '@solana/spl-token';
+import { Connection, PublicKey } from '@solana/web3.js';
+import BN from 'bn.js';
+
+import {
+  AmmConfigLayout,
+  IAmmConfigLayout,
+  IObservationLayout,
+  IPersonalPositionLayout,
+  ObservationLayout,
+  PersonalPositionLayout,
+  PoolLayout,
+} from './layout';
+import { IPoolLayoutWithId } from './models';
+import { getPdaPersonalPositionAddress } from './pda';
+import { fetchWalletTokenAccounts } from './utils/fetchWalletTokenAccounts';
+import { SqrtPriceMath } from './utils/index';
+
+export class RawDataUtils {
+  /**
+   * Get position list for specified account
+   */
+  static async getRawPositionInfoListByUserAddress(params: {
+    connection: Connection;
+    programId: PublicKey;
+    userAddress: PublicKey;
+  }): Promise<IPersonalPositionLayout[]> {
+    const { connection, programId, userAddress } = params;
+    const { rawTokenAccountInfos } = await fetchWalletTokenAccounts(connection, userAddress);
+    const balanceMints = rawTokenAccountInfos.filter((acc) => acc.accountInfo.amount.eq(new BN(1)));
+    const allPositionKey = balanceMints.map(
+      (acc) => getPdaPersonalPositionAddress(new PublicKey(programId), acc.accountInfo.mint).publicKey,
+    );
+
+    const accountInfo = await connection.getMultipleAccountsInfo(allPositionKey);
+    const allPosition: IPersonalPositionLayout[] = [];
+    accountInfo.forEach((positionRes) => {
+      if (!positionRes) return;
+      const position = PersonalPositionLayout.decode(positionRes.data);
+      allPosition.push(position);
+    });
+
+    return allPosition;
+  }
+
+  /**
+   * Get position information from specified position list
+   */
+  static async getRawPositionInfoByNftMint(params: {
+    connection: Connection;
+    programId: PublicKey;
+    nftMint: PublicKey;
+  }): Promise<IPersonalPositionLayout | null> {
+    const { connection, programId, nftMint } = params;
+    const positionKey = getPdaPersonalPositionAddress(new PublicKey(programId), nftMint).publicKey;
+    const positionRes = await connection.getAccountInfo(positionKey);
+    if (!positionRes) return null;
+    const position = PersonalPositionLayout.decode(positionRes.data);
+    return position;
+  }
+
+  /**
+   * Get on-chain information for a single CLMM pool
+   */
+  static async getRawPoolInfoByPoolId(params: {
+    connection: Connection;
+    poolId: string | PublicKey;
+  }): Promise<IPoolLayoutWithId | null> {
+    const { connection, poolId } = params;
+    const poolPublicKey = new PublicKey(poolId);
+
+    const accountInfo = await connection.getAccountInfo(poolPublicKey);
+
+    if (!accountInfo || !accountInfo.data) return null;
+
+    const poolRawInfo = PoolLayout.decode(accountInfo.data);
+
+    const currentPrice = SqrtPriceMath.sqrtPriceX64ToPrice(
+      poolRawInfo.sqrtPriceX64,
+      poolRawInfo.mintDecimalsA,
+      poolRawInfo.mintDecimalsB,
+    ).toNumber();
+
+    return {
+      ...poolRawInfo,
+      currentPrice,
+      programId: accountInfo.owner,
+      poolId: poolPublicKey,
+    };
+  }
+
+  /**
+   * Get on-chain information for specified token
+   */
+  static async getRawTokenInfoByMint(params: {
+    connection: Connection;
+    mintAddress: PublicKey;
+  }): Promise<(ReturnType<typeof MintLayout.decode> & { owner: PublicKey }) | null> {
+    const { connection, mintAddress } = params;
+    const accountInfo = await connection.getAccountInfo(mintAddress);
+    if (!accountInfo || !accountInfo.data) return null;
+    const tokenInfo = MintLayout.decode(accountInfo.data);
+    return {
+      ...tokenInfo,
+      owner: accountInfo.owner,
+    };
+  }
+
+  /**
+   * Get AMM configuration on-chain information
+   */
+  static async getRawAmmConfigByConfigId(params: {
+    connection: Connection;
+    configId: string | PublicKey;
+  }): Promise<(IAmmConfigLayout & { configId: PublicKey; owner: PublicKey }) | null> {
+    const { connection, configId } = params;
+    const configPublicKey = new PublicKey(configId);
+
+    const accountInfo = await connection.getAccountInfo(configPublicKey);
+
+    if (!accountInfo || !accountInfo.data) return null;
+
+    const configRawInfo = AmmConfigLayout.decode(accountInfo.data);
+
+    return {
+      ...configRawInfo,
+      configId: configPublicKey,
+      owner: accountInfo.owner,
+    };
+  }
+
+  /**
+   * Get observation account on-chain information
+   * Observation account is used to record historical data of price changes in the pool
+   */
+  static async getRawObservationByObservationId(params: {
+    connection: Connection;
+    observationId: string | PublicKey;
+  }): Promise<(IObservationLayout & { observationId: PublicKey; owner: PublicKey }) | null> {
+    const { connection, observationId } = params;
+    const observationPublicKey = new PublicKey(observationId);
+
+    const accountInfo = await connection.getAccountInfo(observationPublicKey);
+
+    if (!accountInfo || !accountInfo.data) return null;
+
+    const observationRawInfo = ObservationLayout.decode(accountInfo.data);
+
+    return {
+      ...observationRawInfo,
+      observationId: observationPublicKey,
+      owner: accountInfo.owner,
+    };
+  }
+}

+ 8 - 0
src/lib/byreal-clmm-sdk/src/instructions/index.ts

@@ -0,0 +1,8 @@
+export * from './utils/index';
+export * from './constants';
+export * from './baseInstruction';
+export * from './instruction';
+export * from './models';
+export * from './layout';
+export * from './pda';
+export * from './getRawData';

+ 547 - 0
src/lib/byreal-clmm-sdk/src/instructions/instruction.ts

@@ -0,0 +1,547 @@
+import { TOKEN_2022_PROGRAM_ID } from '@solana/spl-token';
+import { Keypair, PublicKey, Signer, TransactionInstruction } from '@solana/web3.js';
+import BN from 'bn.js';
+
+import { BaseInstruction } from './baseInstruction';
+import { IPersonalPositionLayout } from './layout';
+import { IPoolLayoutWithId, ITokenInfo } from './models';
+import {
+  getATAAddress,
+  getPdaExBitmapAccount,
+  getPdaPersonalPositionAddress,
+  getPdaProtocolPositionAddress,
+  getPdaTickArrayAddress,
+} from './pda';
+import { PoolUtils } from './utils/poolUtils';
+import { TickUtils } from './utils/tick';
+
+export class Instruction {
+  static async createPoolInstruction(props: {
+    programId: PublicKey;
+    owner: PublicKey;
+    poolManager: PublicKey;
+    mintA: ITokenInfo;
+    mintB: ITokenInfo;
+    ammConfigId: PublicKey;
+    initialPriceX64: BN;
+    openTime?: BN;
+    extendMintAccount?: PublicKey[];
+  }): Promise<{
+    instructions: TransactionInstruction[];
+  }> {
+    // console.log('[clmm sdk] createPoolInstruction fn params:', JSON.stringify(props, null, 2));
+
+    const { programId, owner, poolManager, mintA, mintB, ammConfigId, initialPriceX64, extendMintAccount, openTime } =
+      props;
+
+    const mintAAddress = new PublicKey(mintA.address);
+    const mintBAddress = new PublicKey(mintB.address);
+
+    const instruction = await BaseInstruction.createPoolInstruction(
+      programId,
+      owner,
+      poolManager,
+      ammConfigId,
+      mintAAddress,
+      new PublicKey(mintA.programId),
+      mintBAddress,
+      new PublicKey(mintB.programId),
+      initialPriceX64,
+      openTime,
+      extendMintAccount,
+    );
+
+    return {
+      instructions: [instruction],
+    };
+  }
+
+  static async openPositionFromLiquidityInstruction(params: {
+    poolInfo: IPoolLayoutWithId;
+    ownerInfo: {
+      feePayer: PublicKey;
+      wallet: PublicKey;
+      tokenAccountA: PublicKey;
+      tokenAccountB: PublicKey;
+    };
+    tickLower: number;
+    tickUpper: number;
+    liquidity: BN;
+    amountMaxA: BN;
+    amountMaxB: BN;
+    withMetadata: 'create' | 'no-create';
+  }): Promise<{
+    instructions: TransactionInstruction[];
+    signers: Signer[];
+  }> {
+    // console.log('[clmm sdk] openPositionFromLiquidityInstruction fn params:', JSON.stringify(params, null, 2));
+
+    const { poolInfo, ownerInfo, tickLower, tickUpper, liquidity, amountMaxA, amountMaxB, withMetadata } = params;
+    const signers: Signer[] = [];
+
+    const { programId, poolId, tickSpacing } = poolInfo;
+
+    const keypair = Keypair.generate();
+    signers.push(keypair);
+    const nftMintAccount = keypair.publicKey;
+
+    const tickArrayLowerStartIndex = TickUtils.getTickArrayStartIndexByTick(tickLower, tickSpacing);
+    const tickArrayUpperStartIndex = TickUtils.getTickArrayStartIndexByTick(tickUpper, tickSpacing);
+
+    const { publicKey: tickArrayLower } = getPdaTickArrayAddress(programId, poolId, tickArrayLowerStartIndex);
+    const { publicKey: tickArrayUpper } = getPdaTickArrayAddress(programId, poolId, tickArrayUpperStartIndex);
+
+    const { publicKey: positionNftAccount } = getATAAddress(ownerInfo.wallet, nftMintAccount, TOKEN_2022_PROGRAM_ID);
+
+    const { publicKey: personalPosition } = getPdaPersonalPositionAddress(programId, nftMintAccount);
+    const { publicKey: protocolPosition } = getPdaProtocolPositionAddress(programId, poolId, tickLower, tickUpper);
+
+    const instruction = await BaseInstruction.openPositionFromLiquidityInstruction22(
+      programId,
+      ownerInfo.feePayer,
+      poolId,
+      ownerInfo.wallet,
+      nftMintAccount,
+      positionNftAccount,
+      protocolPosition,
+      tickArrayLower,
+      tickArrayUpper,
+      personalPosition,
+      ownerInfo.tokenAccountA,
+      ownerInfo.tokenAccountB,
+      poolInfo.vaultA,
+      poolInfo.vaultB,
+      poolInfo.mintA,
+      poolInfo.mintB,
+      tickLower,
+      tickUpper,
+      tickArrayLowerStartIndex,
+      tickArrayUpperStartIndex,
+      liquidity,
+      amountMaxA,
+      amountMaxB,
+      withMetadata,
+      PoolUtils.isOverflowDefaultTickarrayBitmap(tickSpacing, [tickArrayLowerStartIndex, tickArrayUpperStartIndex])
+        ? getPdaExBitmapAccount(programId, poolId).publicKey
+        : undefined,
+    );
+
+    return {
+      signers,
+      instructions: [instruction],
+    };
+  }
+
+  static async openPositionFromBaseInstruction(params: {
+    poolInfo: IPoolLayoutWithId;
+    ownerInfo: {
+      feePayer: PublicKey;
+      wallet: PublicKey;
+      tokenAccountA: PublicKey;
+      tokenAccountB: PublicKey;
+    };
+    tickLower: number;
+    tickUpper: number;
+    base: 'MintA' | 'MintB';
+    baseAmount: BN;
+    otherAmountMax: BN;
+    withMetadata: 'create' | 'no-create';
+  }): Promise<{
+    instructions: TransactionInstruction[];
+    signers: Signer[];
+  }> {
+    // console.log('[clmm sdk] openPositionFromBaseInstruction fn params:', JSON.stringify(params, null, 2));
+
+    const { poolInfo, ownerInfo, tickLower, tickUpper, base, baseAmount, otherAmountMax, withMetadata } = params;
+    const signers: Signer[] = [];
+
+    const { programId, poolId, tickSpacing } = poolInfo;
+
+    const keypair = Keypair.generate();
+    signers.push(keypair);
+    const nftMintAccount = keypair.publicKey;
+
+    const tickArrayLowerStartIndex = TickUtils.getTickArrayStartIndexByTick(tickLower, tickSpacing);
+    const tickArrayUpperStartIndex = TickUtils.getTickArrayStartIndexByTick(tickUpper, tickSpacing);
+
+    const { publicKey: tickArrayLower } = getPdaTickArrayAddress(programId, poolId, tickArrayLowerStartIndex);
+    const { publicKey: tickArrayUpper } = getPdaTickArrayAddress(programId, poolId, tickArrayUpperStartIndex);
+
+    const { publicKey: positionNftAccount } = getATAAddress(ownerInfo.wallet, nftMintAccount, TOKEN_2022_PROGRAM_ID);
+    const { publicKey: personalPosition } = getPdaPersonalPositionAddress(programId, nftMintAccount);
+    const { publicKey: protocolPosition } = getPdaProtocolPositionAddress(programId, poolId, tickLower, tickUpper);
+
+    const instruction = await BaseInstruction.openPositionFromBaseInstruction22(
+      programId,
+      ownerInfo.feePayer,
+      poolId,
+      ownerInfo.wallet,
+      nftMintAccount,
+      positionNftAccount,
+      protocolPosition,
+      tickArrayLower,
+      tickArrayUpper,
+      personalPosition,
+      ownerInfo.tokenAccountA,
+      ownerInfo.tokenAccountB,
+      poolInfo.vaultA,
+      poolInfo.vaultB,
+      poolInfo.mintA,
+      poolInfo.mintB,
+      tickLower,
+      tickUpper,
+      tickArrayLowerStartIndex,
+      tickArrayUpperStartIndex,
+      withMetadata,
+      base,
+      baseAmount,
+      otherAmountMax,
+      PoolUtils.isOverflowDefaultTickarrayBitmap(tickSpacing, [tickArrayLowerStartIndex, tickArrayUpperStartIndex])
+        ? getPdaExBitmapAccount(programId, poolId).publicKey
+        : undefined,
+    );
+
+    return {
+      instructions: [instruction],
+      signers,
+    };
+  }
+
+  static async closePositionInstruction(params: {
+    programId: PublicKey;
+    nftMint: PublicKey;
+    ownerWallet: PublicKey;
+  }): Promise<{
+    instructions: TransactionInstruction[];
+  }> {
+    // console.log('[clmm sdk] closePositionInstruction fn params:', JSON.stringify(params, null, 2));
+
+    const { programId, nftMint, ownerWallet } = params;
+    const positionNftAccount = getATAAddress(ownerWallet, nftMint, TOKEN_2022_PROGRAM_ID).publicKey;
+
+    const instruction = await BaseInstruction.closePositionInstruction(
+      programId,
+      ownerWallet,
+      nftMint,
+      positionNftAccount,
+      true,
+    );
+
+    return {
+      instructions: [instruction],
+    };
+  }
+
+  static async increasePositionFromLiquidityInstructions(params: {
+    poolInfo: IPoolLayoutWithId;
+    ownerPosition: IPersonalPositionLayout;
+    ownerInfo: {
+      wallet: PublicKey;
+      tokenAccountA: PublicKey;
+      tokenAccountB: PublicKey;
+    };
+
+    liquidity: BN;
+    amountMaxA: BN;
+    amountMaxB: BN;
+  }): Promise<{
+    instructions: TransactionInstruction[];
+  }> {
+    // console.log('[clmm sdk] increasePositionFromLiquidityInstructions fn params:', JSON.stringify(params, null, 2));
+
+    const { poolInfo, ownerPosition, ownerInfo, liquidity, amountMaxA, amountMaxB } = params;
+    const { programId, poolId, tickSpacing } = poolInfo;
+
+    const tickArrayLowerStartIndex = TickUtils.getTickArrayStartIndexByTick(ownerPosition.tickLower, tickSpacing);
+    const tickArrayUpperStartIndex = TickUtils.getTickArrayStartIndexByTick(ownerPosition.tickUpper, tickSpacing);
+
+    const { publicKey: tickArrayLower } = getPdaTickArrayAddress(programId, poolId, tickArrayLowerStartIndex);
+    const { publicKey: tickArrayUpper } = getPdaTickArrayAddress(programId, poolId, tickArrayUpperStartIndex);
+
+    const { publicKey: positionNftAccount } = getATAAddress(
+      ownerInfo.wallet,
+      ownerPosition.nftMint,
+      TOKEN_2022_PROGRAM_ID,
+    );
+
+    const { publicKey: personalPosition } = getPdaPersonalPositionAddress(programId, ownerPosition.nftMint);
+    const { publicKey: protocolPosition } = getPdaProtocolPositionAddress(
+      programId,
+      poolId,
+      ownerPosition.tickLower,
+      ownerPosition.tickUpper,
+    );
+
+    const instruction = await BaseInstruction.increasePositionFromLiquidityInstruction(
+      programId,
+      ownerInfo.wallet,
+      positionNftAccount,
+      personalPosition,
+      poolId,
+      protocolPosition,
+      tickArrayLower,
+      tickArrayUpper,
+      ownerInfo.tokenAccountA,
+      ownerInfo.tokenAccountB,
+      poolInfo.vaultA,
+      poolInfo.vaultB,
+      poolInfo.mintA,
+      poolInfo.mintB,
+      liquidity,
+      amountMaxA,
+      amountMaxB,
+      PoolUtils.isOverflowDefaultTickarrayBitmap(tickSpacing, [tickArrayLowerStartIndex, tickArrayUpperStartIndex])
+        ? getPdaExBitmapAccount(programId, poolId).publicKey
+        : undefined,
+    );
+
+    return {
+      instructions: [instruction],
+    };
+  }
+
+  static async increasePositionFromBaseInstructions(params: {
+    poolInfo: IPoolLayoutWithId;
+    ownerPosition: IPersonalPositionLayout;
+    ownerInfo: {
+      wallet: PublicKey;
+      tokenAccountA: PublicKey;
+      tokenAccountB: PublicKey;
+    };
+    base: 'MintA' | 'MintB';
+    baseAmount: BN;
+    otherAmountMax: BN;
+  }): Promise<{
+    instructions: TransactionInstruction[];
+  }> {
+    // console.log('[clmm sdk] increasePositionFromBaseInstructions fn params:', JSON.stringify(params, null, 2));
+
+    const { poolInfo, ownerPosition, ownerInfo, base, baseAmount, otherAmountMax } = params;
+    const { programId, poolId, tickSpacing } = poolInfo;
+
+    const tickArrayLowerStartIndex = TickUtils.getTickArrayStartIndexByTick(ownerPosition.tickLower, tickSpacing);
+    const tickArrayUpperStartIndex = TickUtils.getTickArrayStartIndexByTick(ownerPosition.tickUpper, tickSpacing);
+
+    const { publicKey: tickArrayLower } = getPdaTickArrayAddress(programId, poolId, tickArrayLowerStartIndex);
+    const { publicKey: tickArrayUpper } = getPdaTickArrayAddress(programId, poolId, tickArrayUpperStartIndex);
+
+    const { publicKey: positionNftAccount } = getATAAddress(
+      ownerInfo.wallet,
+      ownerPosition.nftMint,
+      TOKEN_2022_PROGRAM_ID,
+    );
+
+    const { publicKey: personalPosition } = getPdaPersonalPositionAddress(programId, ownerPosition.nftMint);
+    const { publicKey: protocolPosition } = getPdaProtocolPositionAddress(
+      programId,
+      poolId,
+      ownerPosition.tickLower,
+      ownerPosition.tickUpper,
+    );
+
+    const instruction = await BaseInstruction.increasePositionFromBaseInstruction(
+      programId,
+      ownerInfo.wallet,
+      positionNftAccount,
+      personalPosition,
+      poolId,
+      protocolPosition,
+      tickArrayLower,
+      tickArrayUpper,
+      ownerInfo.tokenAccountA,
+      ownerInfo.tokenAccountB,
+      poolInfo.vaultA,
+      poolInfo.vaultB,
+      poolInfo.mintA,
+      poolInfo.mintB,
+      base,
+      baseAmount,
+      otherAmountMax,
+      PoolUtils.isOverflowDefaultTickarrayBitmap(tickSpacing, [tickArrayLowerStartIndex, tickArrayUpperStartIndex])
+        ? getPdaExBitmapAccount(programId, poolId).publicKey
+        : undefined,
+    );
+
+    return {
+      instructions: [instruction],
+    };
+  }
+
+  static async decreaseLiquidityInstructions(params: {
+    poolInfo: IPoolLayoutWithId;
+    ownerPosition: IPersonalPositionLayout;
+    ownerInfo: {
+      wallet: PublicKey;
+      tokenAccountA: PublicKey;
+      tokenAccountB: PublicKey;
+      // rewardAccounts: PublicKey[];
+    };
+    liquidity: BN;
+    amountMinA: BN;
+    amountMinB: BN;
+  }): Promise<{
+    instructions: TransactionInstruction[];
+  }> {
+    // console.log('[clmm sdk] decreaseLiquidityInstructions fn params:', JSON.stringify(params, null, 2));
+
+    const { poolInfo, ownerPosition, ownerInfo, liquidity, amountMinA, amountMinB } = params;
+    const { programId, poolId, tickSpacing } = poolInfo;
+
+    const tickArrayLowerStartIndex = TickUtils.getTickArrayStartIndexByTick(ownerPosition.tickLower, tickSpacing);
+    const tickArrayUpperStartIndex = TickUtils.getTickArrayStartIndexByTick(ownerPosition.tickUpper, tickSpacing);
+
+    const { publicKey: tickArrayLower } = getPdaTickArrayAddress(programId, poolId, tickArrayLowerStartIndex);
+    const { publicKey: tickArrayUpper } = getPdaTickArrayAddress(programId, poolId, tickArrayUpperStartIndex);
+    const { publicKey: positionNftAccount } = getATAAddress(
+      ownerInfo.wallet,
+      ownerPosition.nftMint,
+      TOKEN_2022_PROGRAM_ID,
+    );
+    const { publicKey: personalPosition } = getPdaPersonalPositionAddress(programId, ownerPosition.nftMint);
+    const { publicKey: protocolPosition } = getPdaProtocolPositionAddress(
+      programId,
+      poolId,
+      ownerPosition.tickLower,
+      ownerPosition.tickUpper,
+    );
+
+    const rewardAccounts = poolInfo.rewardInfos
+      .filter((info) => !info.openTime.isZero() && !info.tokenMint.equals(PublicKey.default))
+      .map((rewardInfo) => ({
+        poolRewardVault: rewardInfo.tokenVault,
+        ownerRewardVault: getATAAddress(ownerInfo.wallet, rewardInfo.tokenMint).publicKey,
+        rewardMint: rewardInfo.tokenMint,
+      }));
+
+    const instruction = await BaseInstruction.decreaseLiquidityInstruction(
+      programId,
+      ownerInfo.wallet,
+      positionNftAccount,
+      personalPosition,
+      poolId,
+      protocolPosition,
+      tickArrayLower,
+      tickArrayUpper,
+      ownerInfo.tokenAccountA,
+      ownerInfo.tokenAccountB,
+      poolInfo.vaultA,
+      poolInfo.vaultB,
+      poolInfo.mintA,
+      poolInfo.mintB,
+      rewardAccounts,
+
+      liquidity,
+      amountMinA,
+      amountMinB,
+      PoolUtils.isOverflowDefaultTickarrayBitmap(tickSpacing, [tickArrayLowerStartIndex, tickArrayUpperStartIndex])
+        ? getPdaExBitmapAccount(programId, poolId).publicKey
+        : undefined,
+    );
+
+    return {
+      instructions: [instruction],
+    };
+  }
+
+  static async swapBaseInInstruction(params: {
+    poolInfo: IPoolLayoutWithId;
+    ownerInfo: {
+      wallet: PublicKey;
+      tokenAccountA: PublicKey;
+      tokenAccountB: PublicKey;
+    };
+    amount: BN;
+    otherAmountThreshold: BN;
+    sqrtPriceLimitX64: BN;
+    isInputMintA: boolean;
+    exTickArrayBitmap?: PublicKey;
+    tickArray: PublicKey[];
+  }) {
+    const {
+      poolInfo,
+      ownerInfo,
+      amount,
+      otherAmountThreshold,
+      sqrtPriceLimitX64,
+      exTickArrayBitmap,
+      tickArray,
+      isInputMintA,
+    } = params;
+
+    const instruction = await BaseInstruction.swapInstruction(
+      poolInfo.programId,
+      ownerInfo.wallet,
+      poolInfo.poolId,
+      poolInfo.ammConfig,
+      isInputMintA ? ownerInfo.tokenAccountA : ownerInfo.tokenAccountB,
+      isInputMintA ? ownerInfo.tokenAccountB : ownerInfo.tokenAccountA,
+      isInputMintA ? poolInfo.vaultA : poolInfo.vaultB,
+      isInputMintA ? poolInfo.vaultB : poolInfo.vaultA,
+      isInputMintA ? poolInfo.mintA : poolInfo.mintB,
+      isInputMintA ? poolInfo.mintB : poolInfo.mintA,
+      tickArray,
+      poolInfo.observationId,
+      amount,
+      otherAmountThreshold,
+      sqrtPriceLimitX64,
+      true,
+      exTickArrayBitmap,
+    );
+
+    return {
+      instructions: [instruction],
+    };
+  }
+
+  static async swapBaseOutInstruction(params: {
+    poolInfo: IPoolLayoutWithId;
+    ownerInfo: {
+      wallet: PublicKey;
+      tokenAccountA: PublicKey;
+      tokenAccountB: PublicKey;
+    };
+    amount: BN;
+    otherAmountThreshold: BN;
+    sqrtPriceLimitX64: BN;
+    isOutputMintA: boolean;
+    exTickArrayBitmap?: PublicKey;
+    tickArray: PublicKey[];
+  }) {
+    const {
+      poolInfo,
+      ownerInfo,
+      amount,
+      otherAmountThreshold,
+      sqrtPriceLimitX64,
+      exTickArrayBitmap,
+      tickArray,
+      isOutputMintA,
+    } = params;
+
+    // For exact output, input and output are reversed
+    // If output is mintA, then input is mintB
+    const instruction = await BaseInstruction.swapInstruction(
+      poolInfo.programId,
+      ownerInfo.wallet,
+      poolInfo.poolId,
+      poolInfo.ammConfig,
+      isOutputMintA ? ownerInfo.tokenAccountB : ownerInfo.tokenAccountA,
+      isOutputMintA ? ownerInfo.tokenAccountA : ownerInfo.tokenAccountB,
+      isOutputMintA ? poolInfo.vaultB : poolInfo.vaultA,
+      isOutputMintA ? poolInfo.vaultA : poolInfo.vaultB,
+      isOutputMintA ? poolInfo.mintB : poolInfo.mintA,
+      isOutputMintA ? poolInfo.mintA : poolInfo.mintB,
+      tickArray,
+      poolInfo.observationId,
+      amount,
+      otherAmountThreshold,
+      sqrtPriceLimitX64,
+      false, // isBaseInput = false for exact output
+      exTickArrayBitmap,
+    );
+
+    return {
+      instructions: [instruction],
+    };
+  }
+}

+ 241 - 0
src/lib/byreal-clmm-sdk/src/instructions/layout.ts

@@ -0,0 +1,241 @@
+import { TICK_ARRAY_SIZE, EXTENSION_TICKARRAY_BITMAP_SIZE } from '../constants';
+
+import { blob, bool, i128, i64, publicKey, s32, seq, struct, u128, u16, u32, u64, u8 } from './libs/marshmallow/index';
+
+/**
+ * @description Pool configuration layout
+ *
+ * @example https://solscan.io/account/EdPxg8QaeFSrTYqdWJn6Kezwy9McWncTYueD9eMGCuzR#anchorData
+ */
+export const AmmConfigLayout = struct([
+  blob(8),
+  u8('bump'),
+  u16('index'),
+  publicKey('owner'),
+  u32('protocolFeeRate'),
+  u32('tradeFeeRate'),
+  u16('tickSpacing'),
+  u32('fundFeeRate'),
+  u32('padding'),
+  publicKey('fundOwner'),
+]);
+
+export type IAmmConfigLayout = ReturnType<typeof AmmConfigLayout.decode>;
+
+/**
+ * @description Observation account layout
+ *
+ * @example https://solscan.io/account/AA5RaVvyGyZgtmAsJJHT5ZVBxVPtAXuYaMwfgeFJW4Mk#anchorData
+ */
+export const ObservationLayout = struct([
+  blob(8),
+  bool('initialized'),
+  u64('recentEpoch'),
+  u16('observationIndex'),
+  publicKey('poolId'),
+  seq(struct([u32('blockTimestamp'), i64('tickCumulative'), seq(u64(), 4)]), 100, 'observations'),
+]);
+
+export type IObservationLayout = ReturnType<typeof ObservationLayout.decode>;
+
+/**
+ * @description Pool information layout
+ *
+ * @example https://solscan.io/account/CYbD9RaToYMtWKA7QZyoLahnHdWq553Vm62Lh6qWtuxq#anchorData
+ */
+export const PoolLayout = struct([
+  blob(8),
+  u8('bump'),
+  publicKey('ammConfig'),
+  publicKey('creator'),
+  publicKey('mintA'),
+  publicKey('mintB'),
+  publicKey('vaultA'),
+  publicKey('vaultB'),
+  publicKey('observationId'),
+  u8('mintDecimalsA'),
+  u8('mintDecimalsB'),
+  u16('tickSpacing'),
+  u128('liquidity'),
+  u128('sqrtPriceX64'),
+  s32('tickCurrent'),
+  u32(),
+  u128('feeGrowthGlobalX64A'),
+  u128('feeGrowthGlobalX64B'),
+  u64('protocolFeesTokenA'),
+  u64('protocolFeesTokenB'),
+
+  u128('swapInAmountTokenA'),
+  u128('swapOutAmountTokenB'),
+  u128('swapInAmountTokenB'),
+  u128('swapOutAmountTokenA'),
+
+  u8('status'),
+
+  seq(u8(), 7, ''),
+
+  seq(
+    struct([
+      u8('rewardState'),
+      u64('openTime'),
+      u64('endTime'),
+      u64('lastUpdateTime'),
+      u128('emissionsPerSecondX64'),
+      u64('rewardTotalEmissioned'),
+      u64('rewardClaimed'),
+      publicKey('tokenMint'),
+      publicKey('tokenVault'),
+      publicKey('creator'),
+      u128('rewardGrowthGlobalX64'),
+    ]),
+    3,
+    'rewardInfos',
+  ),
+  seq(u64(), 16, 'tickArrayBitmap'),
+
+  u64('totalFeesTokenA'),
+  u64('totalFeesClaimedTokenA'),
+  u64('totalFeesTokenB'),
+  u64('totalFeesClaimedTokenB'),
+
+  u64('fundFeesTokenA'),
+  u64('fundFeesTokenB'),
+
+  u64('openTime'),
+  u64('recentEpoch'),
+
+  u8('decayFeeFlag'),
+  u8('decayFeeInitFeeRate'),
+  u8('decayFeeDecreaseRate'),
+  u8('decayFeeDecreaseInterval'),
+  seq(u8(), 4, 'padding1_1'),
+  seq(u64(), 23, 'padding1'),
+  seq(u64(), 32, 'padding2'),
+]);
+
+export type IPoolLayout = ReturnType<typeof PoolLayout.decode>;
+
+/**
+ * @description Personal position information layout
+ *
+ * @example https://solscan.io/account/CLYRosA3oGsx6WjuebDYmEL3kukTCSsncmYU6at8nDsn#anchorData
+ */
+export const PersonalPositionLayout = struct([
+  blob(8),
+  u8('bump'),
+  publicKey('nftMint'),
+  publicKey('poolId'),
+
+  s32('tickLower'),
+  s32('tickUpper'),
+  u128('liquidity'),
+  u128('feeGrowthInsideLastX64A'),
+  u128('feeGrowthInsideLastX64B'),
+  u64('tokenFeesOwedA'),
+  u64('tokenFeesOwedB'),
+
+  seq(struct([u128('growthInsideLastX64'), u64('rewardAmountOwed')]), 3, 'rewardInfos'),
+]);
+
+export type IPersonalPositionLayout = ReturnType<typeof PersonalPositionLayout.decode>;
+
+/**
+ * @description Protocol position information layout, used to track liquidity within specific price ranges
+ *
+ * @example https://solscan.io/account/38GUhmh7vPyWStAV3YKEEYPHrLk2Mnw5jvaUQkMGS1hb#anchorData
+ */
+export const ProtocolPositionLayout = struct([
+  blob(8),
+  u8('bump'),
+  publicKey('poolId'),
+  s32('tickLowerIndex'),
+  s32('tickUpperIndex'),
+  u128('liquidity'),
+  u128('feeGrowthInsideLastX64A'),
+  u128('feeGrowthInsideLastX64B'),
+  u64('tokenFeesOwedA'),
+  u64('tokenFeesOwedB'),
+  seq(u128(), 3, 'rewardGrowthInside'),
+]);
+
+/**
+ * @description Price tick array layout
+ *
+ * @example https://solscan.io/account/4vGLPwfohNUd2o4NwZPMx7q8AH98DQ9Eth5tS1p8dew1#anchorData
+ */
+/**
+ * @description TickState layout (168 bytes)
+ */
+export const TickStateLayout = struct([
+  s32('tick'),
+  i128('liquidityNet'),
+  u128('liquidityGross'),
+  u128('feeGrowthOutsideX64A'),
+  u128('feeGrowthOutsideX64B'),
+  seq(u128(), 3, 'rewardGrowthsOutsideX64'),
+  seq(u32(), 13, ''),
+]);
+
+export type ITickStateLayout = ReturnType<typeof TickStateLayout.decode>;
+
+export const TickArrayLayout = struct([
+  blob(8),
+  publicKey('poolId'),
+  s32('startTickIndex'),
+  seq(TickStateLayout, TICK_ARRAY_SIZE, 'ticks'),
+  u8('initializedTickCount'),
+]);
+
+export type ITickArrayLayout = ReturnType<typeof TickArrayLayout.decode>;
+
+/**
+ * @description Dynamic Tick Array layout
+ *
+ * Dynamic tick arrays use a sparse storage model with a mapping table (tick_offset_index)
+ * to track which logical tick positions have allocated TickStates.
+ * This allows for more efficient memory usage compared to fixed tick arrays.
+ *
+ * Struct size: 208 bytes (32+4+4+60+1+1+2+8+96)
+ * Header size: 216 bytes (8 discriminator + 208 struct)
+ * Followed by dynamic number of TickStates (max 60, each 168 bytes)
+ */
+export const DynTickArrayLayout = struct([
+  blob(8), // discriminator (8 bytes)
+  publicKey('poolId'), // 32 bytes
+  s32('startTickIndex'), // 4 bytes
+  blob(4, 'padding0'), // 4 bytes
+  seq(u8(), TICK_ARRAY_SIZE, 'tickOffsetIndex'), // 60 bytes - Mapping table: offset -> physical position + 1
+  u8('allocTickCount'), // 1 byte - Number of allocated ticks
+  u8('initializedTickCount'), // 1 byte - Number of initialized ticks
+  blob(2, 'padding1'), // 2 bytes
+  u64('recentEpoch'), // 8 bytes
+  blob(96, 'padding2'), // 96 bytes
+]);
+
+export type IDynTickArrayLayout = ReturnType<typeof DynTickArrayLayout.decode>;
+
+/**
+ * @description Price tick array bitmap extension layout
+ *
+ * @example https://solscan.io/account/DoPuiZfJu7sypqwR4eiU7C5TMcmmiFoU4HaF5SoD8mRy#anchorData
+ */
+export const TickArrayBitmapExtensionLayout = struct([
+  blob(8),
+  publicKey('poolId'),
+  seq(seq(u64(), 8), EXTENSION_TICKARRAY_BITMAP_SIZE, 'positiveTickArrayBitmap'),
+  seq(seq(u64(), 8), EXTENSION_TICKARRAY_BITMAP_SIZE, 'negativeTickArrayBitmap'),
+]);
+
+export const SPLTokenAccountLayout = struct([
+  publicKey('mint'),
+  publicKey('owner'),
+  u64('amount'),
+  u32('delegateOption'),
+  publicKey('delegate'),
+  u8('state'),
+  u32('isNativeOption'),
+  u64('isNative'),
+  u64('delegatedAmount'),
+  u32('closeAuthorityOption'),
+  publicKey('closeAuthority'),
+]);

+ 213 - 0
src/lib/byreal-clmm-sdk/src/instructions/libs/marshmallow/bufferLayout.ts

@@ -0,0 +1,213 @@
+import {
+  bits as _bits,
+  BitStructure as _BitStructure,
+  blob as _blob,
+  Blob as _Blob,
+  cstr as _cstr,
+  f32 as _f32,
+  f32be as _f32be,
+  f64 as _f64,
+  f64be as _f64be,
+  greedy as _greedy,
+  Layout as _Layout,
+  ns64 as _ns64,
+  ns64be as _ns64be,
+  nu64 as _nu64,
+  nu64be as _nu64be,
+  offset as _offset,
+  s16 as _s16,
+  s16be as _s16be,
+  s24 as _s24,
+  s24be as _s24be,
+  s32 as _s32,
+  s32be as _s32be,
+  s40 as _s40,
+  s40be as _s40be,
+  s48 as _s48,
+  s48be as _s48be,
+  s8 as _s8,
+  seq as _seq,
+  struct as _struct,
+  Structure as _Structure,
+  u16 as _u16,
+  u16be as _u16be,
+  u24 as _u24,
+  u24be as _u24be,
+  u32 as _u32,
+  u32be as _u32be,
+  u40 as _u40,
+  u40be as _u40be,
+  u48 as _u48,
+  u48be as _u48be,
+  u8 as _u8,
+  UInt as _UInt,
+  union as _union,
+  Union as _Union,
+  unionLayoutDiscriminator as _unionLayoutDiscriminator,
+  utf8 as _utf8,
+} from '@solana/buffer-layout';
+
+//#region ------------------- Layout -------------------
+export interface Layout<T = any, P = ''> {
+  span: number;
+  property?: P;
+  decode(b: Buffer, offset?: number): T;
+  encode(src: T, b: Buffer, offset?: number): number;
+  getSpan(b: Buffer, offset?: number): number;
+  replicate<AP extends string>(name: AP): Layout<T, AP>;
+}
+export interface LayoutConstructor {
+  new <T, P>(): Layout<T, P>; // for class extends syntex
+  new <T, P>(span?: T, property?: P): Layout<T, P>;
+  readonly prototype: Layout;
+}
+export const Layout = _Layout as unknown as LayoutConstructor;
+//#endregion
+
+//#region ------------------- Structure -------------------
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+export interface Structure<T = any, P = '', DecodeSchema extends { [key: string]: any } = any> extends Layout<
+  DecodeSchema,
+  P
+> {
+  span: number;
+  decode(b: Buffer, offset?: number): DecodeSchema;
+  layoutFor<AP extends string>(property: AP): Layout<DecodeSchema[AP]>;
+  offsetOf<AP extends string>(property: AP): number;
+}
+interface StructureConstructor {
+  new <T = any, P = '', DecodeSchema extends { [key: string]: any } = any>(): Structure<T, P, DecodeSchema>;
+  new <T = any, P = '', DecodeSchema extends { [key: string]: any } = any>(
+    fields: T,
+    property?: P,
+    decodePrefixes?: boolean,
+  ): Structure<T, P, DecodeSchema>;
+}
+export const Structure = _Structure as unknown as StructureConstructor;
+//#endregion
+
+//#region ------------------- Union -------------------
+export interface Union<UnionSchema extends { [key: string]: any } = any> extends Layout {
+  registry: object;
+  decode(b: Buffer, offset?: number): Partial<UnionSchema>;
+  addVariant(
+    variant: number,
+    layout: Structure<any, any, Partial<UnionSchema>> | Layout<any, keyof UnionSchema>,
+    property?: string,
+  ): any /* TEMP: code in layout 1809 */;
+}
+interface UnionConstructor {
+  new <UnionSchema extends { [key: string]: any } = any>(): Union<UnionSchema>;
+  new <UnionSchema extends { [key: string]: any } = any>(
+    discr: Layout<any, any>,
+    defaultLayout: Layout<any, any>,
+    property?: string,
+  ): Union<UnionSchema>;
+}
+export const Union = _Union as unknown as UnionConstructor;
+//#endregion
+
+//#region ------------------- BitStructure -------------------
+export type BitStructure<T = unknown /* TEMP */, P = ''> = Layout<T, P>;
+interface BitStructureConstructor {
+  new (...params: any[]): BitStructure;
+}
+export const BitStructure = _BitStructure as BitStructureConstructor;
+//#endregion
+
+//#region ------------------- UInt -------------------
+export type UInt<T = any, P = ''> = Layout<T, P>;
+interface UIntConstructor {
+  new <T, P>(span?: T, property?: P): UInt<T, P>;
+}
+export const UInt = _UInt as UIntConstructor;
+//#endregion
+
+//#region ------------------- Blob -------------------
+export type Blob<P extends string = ''> = Layout<Buffer, P>;
+interface BlobConstructor {
+  new (...params: ConstructorParameters<LayoutConstructor>): Blob;
+}
+export const Blob = _Blob as unknown as BlobConstructor;
+//#endregion
+
+export const greedy = _greedy as <P extends string = ''>(elementSpan?: number, property?: P) => Layout<number, P>;
+export const u8 = _u8 as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const u16 = _u16 as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const u24 = _u24 as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const u32 = _u32 as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const u40 = _u40 as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const u48 = _u48 as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const nu64 = _nu64 as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const u16be = _u16be as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const u24be = _u24be as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const u32be = _u32be as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const u40be = _u40be as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const u48be = _u48be as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const nu64be = _nu64be as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const s8 = _s8 as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const s16 = _s16 as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const s24 = _s24 as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const s32 = _s32 as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const s40 = _s40 as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const s48 = _s48 as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const ns64 = _ns64 as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const s16be = _s16be as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const s24be = _s24be as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const s32be = _s32be as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const s40be = _s40be as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const s48be = _s48be as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const ns64be = _ns64be as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const f32 = _f32 as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const f32be = _f32be as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const f64 = _f64 as <P extends string = ''>(property?: P) => Layout<number, P>;
+export const f64be = _f64be as <P extends string = ''>(property?: P) => Layout<number, P>;
+
+export const struct = _struct as <T, P extends string = ''>(
+  fields: T,
+  property?: P,
+  decodePrefixes?: boolean,
+) => T extends Layout<infer Value, infer Property>[]
+  ? Structure<
+      Value,
+      P,
+      {
+        [K in Exclude<Extract<Property, string>, ''>]: Extract<T[number], Layout<any, K>> extends Layout<infer V, any>
+          ? V
+          : any;
+      }
+    >
+  : any;
+
+export const seq = _seq as unknown as <T, P>(
+  elementLayout: Layout<T, string>,
+  count: number | Layout<number, string>,
+  property?: P,
+) => Layout<T[]>;
+export const union = _union as <UnionSchema extends { [key: string]: any } = any>(
+  discr: Layout<any, any>,
+  defaultLayout?: any,
+  property?: string,
+) => Union<UnionSchema>;
+export const unionLayoutDiscriminator = _unionLayoutDiscriminator as <P extends string = ''>(
+  layout: Layout<any, P>,
+  property?: P,
+) => any;
+export const blob = _blob as unknown as <P extends string = ''>(
+  length: number | Layout<number, P>,
+  property?: P,
+) => Blob<P>;
+export const cstr = _cstr as <P extends string = ''>(property?: P) => Layout<string, P>;
+export const utf8 = _utf8 as <P extends string = ''>(maxSpan: number, property?: P) => Layout<string, P>;
+export const bits = _bits as unknown as <T, P extends string = ''>(
+  word: Layout<T>,
+  msb?: boolean,
+  property?: P,
+) => BitStructure<T, P>;
+export const offset = _offset as unknown as <T, P extends string = ''>(
+  layout: Layout<T, P>,
+  offset?: number,
+  property?: P,
+) => Layout<T, P>;
+
+export type GetStructureSchema<T extends Structure> = T extends Structure<any, any, infer S> ? S : unknown;

+ 373 - 0
src/lib/byreal-clmm-sdk/src/instructions/libs/marshmallow/index.ts

@@ -0,0 +1,373 @@
+import { PublicKey } from '@solana/web3.js';
+import BN from 'bn.js';
+
+import {
+  bits,
+  blob,
+  Blob,
+  Layout,
+  offset as _offset,
+  seq as _seq,
+  Structure as _Structure,
+  u32 as _u32,
+  u8 as _u8,
+  UInt,
+  union as _union,
+  Union as _Union,
+} from './bufferLayout';
+
+export * from './bufferLayout';
+export { blob };
+
+export class BNLayout<P extends string = ''> extends Layout<BN, P> {
+  blob: Layout<Buffer>;
+  signed: boolean;
+
+  constructor(span: number, signed: boolean, property?: P) {
+    super(span, property);
+    this.blob = blob(span);
+    this.signed = signed;
+  }
+
+  /** @override */
+  override decode(b: Buffer, offset = 0): BN {
+    const num = new BN(this.blob.decode(b, offset), 10, 'le');
+    if (this.signed) {
+      return num.fromTwos(this.span * 8).clone();
+    }
+    return num;
+  }
+
+  /** @override */
+  override encode(src: BN, b: Buffer, offset = 0): number {
+    if (typeof src === 'number') src = new BN(src); // src will pass a number accidently in union
+    if (this.signed) {
+      src = src.toTwos(this.span * 8);
+    }
+    return this.blob.encode(src.toArrayLike(Buffer, 'le', this.span), b, offset);
+  }
+}
+
+export class WideBits<P extends string = ''> extends Layout<Record<string, boolean>, P> {
+  _lower: any;
+  _upper: any;
+  constructor(property?: P) {
+    //@ts-expect-error type wrong for super()'s type different from extends , but it desn't matter
+    super(8, property);
+    this._lower = bits(_u32(), false);
+    this._upper = bits(_u32(), false);
+  }
+
+  addBoolean(property: string): void {
+    if (this._lower.fields.length < 32) {
+      this._lower.addBoolean(property);
+    } else {
+      this._upper.addBoolean(property);
+    }
+  }
+
+  override decode(b: Buffer, offset = 0): Record<string, boolean> {
+    const lowerDecoded = this._lower.decode(b, offset);
+    const upperDecoded = this._upper.decode(b, offset + this._lower.span);
+    return { ...lowerDecoded, ...upperDecoded };
+  }
+
+  override encode(src: any /* TEMP */, b: Buffer, offset = 0): any {
+    return this._lower.encode(src, b, offset) + this._upper.encode(src, b, offset + this._lower.span);
+  }
+}
+
+export function u8<P extends string = ''>(property?: P): UInt<number, P> {
+  return new UInt(1, property);
+}
+
+export function u32<P extends string = ''>(property?: P): UInt<number, P> {
+  return new UInt(4, property);
+}
+
+export function u64<P extends string = ''>(property?: P): BNLayout<P> {
+  return new BNLayout(8, false, property);
+}
+
+export function u128<P extends string = ''>(property?: P): BNLayout<P> {
+  return new BNLayout(16, false, property);
+}
+
+export function i8<P extends string = ''>(property?: P): BNLayout<P> {
+  return new BNLayout(1, true, property);
+}
+
+export function i64<P extends string = ''>(property?: P): BNLayout<P> {
+  return new BNLayout(8, true, property);
+}
+
+export function i128<P extends string = ''>(property?: P): BNLayout<P> {
+  return new BNLayout(16, true, property);
+}
+
+export class WrappedLayout<T, U, P extends string = ''> extends Layout<U, P> {
+  layout: Layout<T>;
+  decoder: (data: T) => U;
+  encoder: (src: U) => T;
+
+  constructor(layout: Layout<T>, decoder: (data: T) => U, encoder: (src: U) => T, property?: P) {
+    //@ts-expect-error type wrong for super()'s type different from extends , but it desn't matter
+    super(layout.span, property);
+    this.layout = layout;
+    this.decoder = decoder;
+    this.encoder = encoder;
+  }
+
+  override decode(b: Buffer, offset?: number): U {
+    return this.decoder(this.layout.decode(b, offset));
+  }
+
+  override encode(src: U, b: Buffer, offset?: number): number {
+    return this.layout.encode(this.encoder(src), b, offset);
+  }
+
+  override getSpan(b: Buffer, offset?: number): number {
+    return this.layout.getSpan(b, offset);
+  }
+}
+
+export function publicKey<P extends string = ''>(property?: P): Layout<PublicKey, P> {
+  return new WrappedLayout(
+    blob(32),
+    (b: Buffer) => new PublicKey(b),
+    (key: PublicKey) => key.toBuffer(),
+    property,
+  );
+}
+
+export class OptionLayout<T, P> extends Layout<T | null, P> {
+  layout: Layout<T>;
+  discriminator: Layout<number>;
+
+  constructor(layout: Layout<T>, property?: P) {
+    //@ts-expect-error type wrong for super()'s type different from extends , but it desn't matter
+    super(-1, property);
+    this.layout = layout;
+    this.discriminator = _u8();
+  }
+
+  override encode(src: T | null, b: Buffer, offset = 0): number {
+    if (src === null || src === undefined) {
+      return this.discriminator.encode(0, b, offset);
+    }
+    this.discriminator.encode(1, b, offset);
+    return this.layout.encode(src, b, offset + 1) + 1;
+  }
+
+  override decode(b: Buffer, offset = 0): T | null {
+    const discriminator = this.discriminator.decode(b, offset);
+    if (discriminator === 0) {
+      return null;
+    } else if (discriminator === 1) {
+      return this.layout.decode(b, offset + 1);
+    }
+    throw new Error('Invalid option ' + this.property);
+  }
+
+  override getSpan(b: Buffer, offset = 0): number {
+    const discriminator = this.discriminator.decode(b, offset);
+    if (discriminator === 0) {
+      return 1;
+    } else if (discriminator === 1) {
+      return this.layout.getSpan(b, offset + 1) + 1;
+    }
+    throw new Error('Invalid option ' + this.property);
+  }
+}
+
+export function option<T, P extends string = ''>(layout: Layout<T>, property?: P): Layout<T | null, P> {
+  return new OptionLayout<T, P>(layout, property);
+}
+
+export function bool<P extends string = ''>(property?: P): Layout<boolean, P> {
+  return new WrappedLayout(_u8(), decodeBool, encodeBool, property);
+}
+
+export function decodeBool(value: number): boolean {
+  if (value === 0) {
+    return false;
+  } else if (value === 1) {
+    return true;
+  }
+  throw new Error('Invalid bool: ' + value);
+}
+
+export function encodeBool(value: boolean): number {
+  return value ? 1 : 0;
+}
+
+export function vec<T, P extends string = ''>(elementLayout: Layout<T>, property?: P): Layout<T[], P> {
+  const length = _u32('length');
+  const layout: Layout<{ values: T[] }> = struct([
+    length,
+    seq(elementLayout, _offset(length, -length.span), 'values'),
+  ]) as any; // Something I don't know
+  return new WrappedLayout(
+    layout,
+    ({ values }) => values,
+    (values) => ({ values }),
+    property,
+  );
+}
+
+export function tagged<T, P extends string = ''>(tag: BN, layout: Layout<T>, property?: P): Layout<T, P> {
+  const wrappedLayout: Layout<{ tag: BN; data: T }> = struct([u64('tag'), layout.replicate('data')]) as any; // Something I don't know
+
+  function decodeTag({ tag: receivedTag, data }: { tag: BN; data: T }): T {
+    if (!receivedTag.eq(tag)) {
+      throw new Error('Invalid tag, expected: ' + tag.toString('hex') + ', got: ' + receivedTag.toString('hex'));
+    }
+    return data;
+  }
+
+  return new WrappedLayout(wrappedLayout, decodeTag, (data) => ({ tag, data }), property);
+}
+
+export function vecU8<P extends string = ''>(property?: P): Layout<Buffer, P> {
+  const length = _u32('length');
+  const layout: Layout<{ data: Buffer }> = struct([length, blob(_offset(length, -length.span), 'data')]) as any; // Something I don't know
+  return new WrappedLayout(
+    layout,
+    ({ data }) => data,
+    (data) => ({ data }),
+    property,
+  );
+}
+
+export function str<P extends string = ''>(property?: P): Layout<string, P> {
+  return new WrappedLayout(
+    vecU8(),
+    (data) => data.toString('utf-8'),
+    (s) => Buffer.from(s, 'utf-8'),
+    property,
+  );
+}
+
+export interface EnumLayout<T, P extends string = ''> extends Layout<T, P> {
+  registry: Record<string, Layout<any>>;
+}
+
+export function rustEnum<T, P extends string = ''>(variants: Layout<any>[], property?: P): EnumLayout<T, P> {
+  const unionLayout = _union(_u8(), property);
+  variants.forEach((variant, index) => unionLayout.addVariant(index, variant, variant.property));
+  return unionLayout as any; // ?why use UnionLayout? This must be a fault
+}
+
+export function array<T, P extends string = ''>(
+  elementLayout: Layout<T>,
+  length: number,
+  property?: P,
+): Layout<T[], P> {
+  const layout = struct([seq(elementLayout, length, 'values')]) as any as Layout<{ values: T[] }>; // Something I don't know
+  return new WrappedLayout(
+    layout,
+    ({ values }) => values,
+    (values) => ({ values }),
+    property,
+  );
+}
+
+export class Structure<T, P, D extends { [key: string]: any }> extends _Structure<T, P, D> {
+  /** @override */
+  override decode(b: Buffer, offset?: number): D {
+    return super.decode(b, offset);
+  }
+}
+
+export function struct<T, P extends string = ''>(
+  fields: T,
+  property?: P,
+  decodePrefixes?: boolean,
+): T extends Layout<infer Value, infer Property>[]
+  ? Structure<
+      Value,
+      P,
+      {
+        [K in Exclude<Extract<Property, string>, ''>]: Extract<T[number], Layout<any, K>> extends Layout<infer V, any>
+          ? V
+          : any;
+      }
+    >
+  : any {
+  //@ts-expect-error this type is not quite satisfied the define, but, never no need to worry about.
+  return new Structure(fields, property, decodePrefixes);
+}
+
+export type GetLayoutSchemaFromStructure<T extends Structure<any, any, any>> =
+  T extends Structure<any, any, infer S> ? S : any;
+export type GetStructureFromLayoutSchema<S extends { [key: string]: any }> = Structure<any, any, S>;
+
+export class Union<Schema extends { [key: string]: any }> extends _Union<Schema> {
+  encodeInstruction(instruction: any): Buffer {
+    const instructionMaxSpan = Math.max(...Object.values(this.registry).map((r) => r.span));
+    const b = Buffer.alloc(instructionMaxSpan);
+    return b.slice(0, this.encode(instruction, b));
+  }
+
+  decodeInstruction(instruction: any): Partial<Schema> {
+    return this.decode(instruction);
+  }
+}
+export function union<UnionSchema extends { [key: string]: any } = any>(
+  discr: any,
+  defaultLayout?: any,
+  property?: string,
+): Union<UnionSchema> {
+  return new Union(discr, defaultLayout, property);
+}
+
+class Zeros extends Blob {
+  override decode(b: Buffer, offset: number): Buffer {
+    const slice = super.decode(b, offset);
+    if (!slice.every((v) => v === 0)) {
+      throw new Error('nonzero padding bytes');
+    }
+    return slice;
+  }
+}
+
+export function zeros(length: number): Zeros {
+  return new Zeros(length);
+}
+
+export function seq<T, P extends string = '', AnotherP extends string = ''>(
+  elementLayout: Layout<T, P>,
+  count: number | BN | Layout<BN | number, P>,
+  property?: AnotherP,
+): Layout<T[], AnotherP> {
+  let parsedCount: number;
+  const superCount =
+    typeof count === 'number'
+      ? count
+      : BN.isBN(count)
+        ? count.toNumber()
+        : new Proxy(count as unknown as Layout<number> /* pretend to be Layout<number> */, {
+            get(target, property): any {
+              if (!parsedCount) {
+                // get count in targetLayout. note that count may be BN
+                const countProperty = Reflect.get(target, 'count');
+
+                // let targetLayout's  property:count be a number
+                parsedCount = BN.isBN(countProperty) ? countProperty.toNumber() : countProperty;
+
+                // record the count
+                Reflect.set(target, 'count', parsedCount);
+              }
+              return Reflect.get(target, property);
+            },
+            set(target, property, value): any {
+              if (property === 'count') {
+                parsedCount = value;
+              }
+              return Reflect.set(target, property, value);
+            },
+          });
+
+  // @ts-expect-error force type
+  return _seq(elementLayout, superCount, property);
+}

+ 70 - 0
src/lib/byreal-clmm-sdk/src/instructions/models.ts

@@ -0,0 +1,70 @@
+import { PublicKey } from '@solana/web3.js';
+import BN from 'bn.js';
+
+import { IPoolLayout } from './layout';
+
+// Get transfer amount and fee
+export interface IGetTransferAmountFee {
+  amount: BN;
+  fee: BN | undefined;
+  expirationTime: number | undefined;
+}
+
+// Bitmap extension type
+export interface TickArrayBitmapExtensionType {
+  poolId: PublicKey;
+  positiveTickArrayBitmap: BN[][];
+  negativeTickArrayBitmap: BN[][];
+}
+
+// Transfer fee configuration
+export interface TransferFeeDataBaseType {
+  transferFeeConfigAuthority: string;
+  withdrawWithheldAuthority: string;
+  withheldAmount: string;
+  olderTransferFee: {
+    epoch: string;
+    maximumFee: string;
+    transferFeeBasisPoints: number;
+  };
+  newerTransferFee: {
+    epoch: string;
+    maximumFee: string;
+    transferFeeBasisPoints: number;
+  };
+}
+
+// Token information
+export interface ITokenInfo {
+  decimals: number;
+  address: string;
+  programId: string;
+  // extensions: {
+  //   feeConfig: TransferFeeDataBaseType;
+  // };
+}
+
+export type IPoolLayoutWithId = IPoolLayout & {
+  currentPrice: number;
+  programId: PublicKey;
+  poolId: PublicKey;
+};
+
+export interface IAccountInfo {
+  pubkey: PublicKey;
+  isSigner: boolean;
+  isWritable: boolean;
+}
+
+// Pool information (to be completed and improved)
+// export interface IPoolInfo {
+//   id: string;
+//   programId: string;
+//   tickSpacing: number;
+//   tickCurrent: number;
+//   sqrtPriceX64: BN;
+//   mintA: ITokenInfo;
+//   mintB: ITokenInfo;
+//   vaultA: PublicKey;
+//   vaultB: PublicKey;
+// }

+ 131 - 0
src/lib/byreal-clmm-sdk/src/instructions/pda.ts

@@ -0,0 +1,131 @@
+import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
+import { PublicKey } from '@solana/web3.js';
+
+import { i32ToBytes, u16ToBytes } from './utils/binaryUtils';
+import { findProgramAddress } from './utils/index';
+
+export const AMM_CONFIG_SEED = Buffer.from('amm_config', 'utf8');
+export const POOL_SEED = Buffer.from('pool', 'utf8');
+export const POOL_VAULT_SEED = Buffer.from('pool_vault', 'utf8');
+export const POOL_REWARD_VAULT_SEED = Buffer.from('pool_reward_vault', 'utf8');
+export const POSITION_SEED = Buffer.from('position', 'utf8');
+export const TICK_ARRAY_SEED = Buffer.from('tick_array', 'utf8');
+export const OPERATION_SEED = Buffer.from('operation', 'utf8');
+export const POOL_TICK_ARRAY_BITMAP_SEED = Buffer.from('pool_tick_array_bitmap_extension', 'utf8');
+export const OBSERVATION_SEED = Buffer.from('observation', 'utf8');
+export const SUPPORT_MINT_SEED = Buffer.from('support_mint', 'utf8');
+
+export function getPdaAmmConfigId(
+  programId: PublicKey,
+  index: number,
+): {
+  publicKey: PublicKey;
+  nonce: number;
+} {
+  return findProgramAddress([AMM_CONFIG_SEED, u16ToBytes(index)], programId);
+}
+
+export function getPdaPoolId(
+  programId: PublicKey,
+  ammConfigId: PublicKey,
+  mintA: PublicKey,
+  mintB: PublicKey,
+): {
+  publicKey: PublicKey;
+  nonce: number;
+} {
+  return findProgramAddress([POOL_SEED, ammConfigId.toBuffer(), mintA.toBuffer(), mintB.toBuffer()], programId);
+}
+
+export function getPdaPoolVaultId(
+  programId: PublicKey,
+  poolId: PublicKey,
+  vaultMint: PublicKey,
+): {
+  publicKey: PublicKey;
+  nonce: number;
+} {
+  return findProgramAddress([POOL_VAULT_SEED, poolId.toBuffer(), vaultMint.toBuffer()], programId);
+}
+
+export function getPdaTickArrayAddress(
+  programId: PublicKey,
+  poolId: PublicKey,
+  startIndex: number,
+): {
+  publicKey: PublicKey;
+  nonce: number;
+} {
+  return findProgramAddress([TICK_ARRAY_SEED, poolId.toBuffer(), i32ToBytes(startIndex)], programId);
+}
+
+export function getPdaProtocolPositionAddress(
+  programId: PublicKey,
+  poolId: PublicKey,
+  tickLower: number,
+  tickUpper: number,
+): {
+  publicKey: PublicKey;
+  nonce: number;
+} {
+  return findProgramAddress(
+    [POSITION_SEED, poolId.toBuffer(), i32ToBytes(tickLower), i32ToBytes(tickUpper)],
+    programId,
+  );
+}
+
+export function getPdaPersonalPositionAddress(
+  programId: PublicKey,
+  nftMint: PublicKey,
+): {
+  publicKey: PublicKey;
+  nonce: number;
+} {
+  return findProgramAddress([POSITION_SEED, nftMint.toBuffer()], programId);
+}
+
+export function getPdaExBitmapAccount(
+  programId: PublicKey,
+  poolId: PublicKey,
+): {
+  publicKey: PublicKey;
+  nonce: number;
+} {
+  return findProgramAddress([POOL_TICK_ARRAY_BITMAP_SEED, poolId.toBuffer()], programId);
+}
+
+export function getPdaObservationAccount(
+  programId: PublicKey,
+  poolId: PublicKey,
+): {
+  publicKey: PublicKey;
+  nonce: number;
+} {
+  return findProgramAddress([OBSERVATION_SEED, poolId.toBuffer()], programId);
+}
+
+// New PDA seed, used to support mint extensions (e.g. token 2022)
+export function getPdaMintExAccount(
+  programId: PublicKey,
+  mintAddress: PublicKey,
+): {
+  publicKey: PublicKey;
+  nonce: number;
+} {
+  return findProgramAddress([SUPPORT_MINT_SEED, mintAddress.toBuffer()], programId);
+}
+
+// Get ATA address
+export function getATAAddress(
+  owner: PublicKey,
+  mint: PublicKey,
+  programId?: PublicKey,
+): {
+  publicKey: PublicKey;
+  nonce: number;
+} {
+  return findProgramAddress(
+    [owner.toBuffer(), (programId ?? TOKEN_PROGRAM_ID).toBuffer(), mint.toBuffer()],
+    new PublicKey('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL'),
+  );
+}

+ 7242 - 0
src/lib/byreal-clmm-sdk/src/instructions/target/idl/byreal_amm_v3.json

@@ -0,0 +1,7242 @@
+{
+  "address": "REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2",
+  "metadata": {
+    "name": "byreal_clmm",
+    "version": "0.1.0",
+    "spec": "0.1.0",
+    "description": "Anchor client and source for Byreal concentrated liquidity AMM"
+  },
+  "instructions": [
+    {
+      "name": "claim_offchain_reward",
+      "docs": [
+        "claim offchain reward from the pool"
+      ],
+      "discriminator": [
+        195,
+        87,
+        221,
+        149,
+        141,
+        195,
+        146,
+        19
+      ],
+      "accounts": [
+        {
+          "name": "claimer",
+          "docs": [
+            "the address who claim the offchain reward."
+          ],
+          "writable": true,
+          "signer": true
+        },
+        {
+          "name": "authority",
+          "docs": [
+            "The authority make decision that who can claim the offchain reward."
+          ],
+          "signer": true
+        },
+        {
+          "name": "admin_group",
+          "docs": [
+            "Initialize amm admin group account to store admin permissions."
+          ],
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  97,
+                  100,
+                  109,
+                  105,
+                  110,
+                  95,
+                  103,
+                  114,
+                  111,
+                  117,
+                  112
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "pool_id",
+          "docs": [
+            "the pool id, which is the pool state account."
+          ],
+          "relations": [
+            "reward_config"
+          ]
+        },
+        {
+          "name": "token_mint"
+        },
+        {
+          "name": "claimer_token_account",
+          "docs": [
+            ""
+          ],
+          "writable": true
+        },
+        {
+          "name": "reward_vault_token_account",
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "account",
+                "path": "reward_config"
+              },
+              {
+                "kind": "account",
+                "path": "token_program"
+              },
+              {
+                "kind": "account",
+                "path": "token_mint"
+              }
+            ],
+            "program": {
+              "kind": "const",
+              "value": [
+                140,
+                151,
+                37,
+                143,
+                78,
+                36,
+                137,
+                241,
+                187,
+                61,
+                16,
+                41,
+                20,
+                142,
+                13,
+                131,
+                11,
+                90,
+                19,
+                153,
+                218,
+                255,
+                16,
+                132,
+                4,
+                142,
+                123,
+                216,
+                219,
+                233,
+                248,
+                89
+              ]
+            }
+          }
+        },
+        {
+          "name": "reward_config",
+          "docs": [
+            "The offchain reward config account, it also is the reward vault account."
+          ],
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  111,
+                  102,
+                  102,
+                  99,
+                  104,
+                  97,
+                  105,
+                  110,
+                  95,
+                  114,
+                  101,
+                  119,
+                  97,
+                  114,
+                  100
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_id"
+              }
+            ]
+          }
+        },
+        {
+          "name": "token_program",
+          "docs": [
+            "Spl token program or token program 2022"
+          ]
+        },
+        {
+          "name": "associated_token_program",
+          "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
+        }
+      ],
+      "args": [
+        {
+          "name": "amount",
+          "type": "u64"
+        }
+      ]
+    },
+    {
+      "name": "close_position",
+      "docs": [
+        "Close the user's position and NFT account. If the NFT mint belongs to token2022, it will also be closed and the funds returned to the NFT owner.",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx` - The context of accounts",
+        ""
+      ],
+      "discriminator": [
+        123,
+        134,
+        81,
+        0,
+        49,
+        68,
+        98,
+        98
+      ],
+      "accounts": [
+        {
+          "name": "nft_owner",
+          "docs": [
+            "The position nft owner"
+          ],
+          "writable": true,
+          "signer": true
+        },
+        {
+          "name": "position_nft_mint",
+          "docs": [
+            "Mint address bound to the personal position."
+          ],
+          "writable": true
+        },
+        {
+          "name": "position_nft_account",
+          "docs": [
+            "User token account where position NFT be minted to"
+          ],
+          "writable": true
+        },
+        {
+          "name": "personal_position",
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  112,
+                  111,
+                  115,
+                  105,
+                  116,
+                  105,
+                  111,
+                  110
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "position_nft_mint"
+              }
+            ]
+          }
+        },
+        {
+          "name": "system_program",
+          "docs": [
+            "System program to close the position state account"
+          ],
+          "address": "11111111111111111111111111111111"
+        },
+        {
+          "name": "token_program",
+          "docs": [
+            "Token/Token2022 program to close token/mint account"
+          ]
+        }
+      ],
+      "args": []
+    },
+    {
+      "name": "collect_fund_fee",
+      "docs": [
+        "Collect the fund fee accrued to the pool",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx` - The context of accounts",
+        "* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1",
+        "* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",
+        ""
+      ],
+      "discriminator": [
+        167,
+        138,
+        78,
+        149,
+        223,
+        194,
+        6,
+        126
+      ],
+      "accounts": [
+        {
+          "name": "admin_group",
+          "docs": [
+            "amm admin group account to store admin permissions.",
+            "anyone can collect fee, but only fee-manager in admin group can receive fee"
+          ],
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  97,
+                  100,
+                  109,
+                  105,
+                  110,
+                  95,
+                  103,
+                  114,
+                  111,
+                  117,
+                  112
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "pool_state",
+          "docs": [
+            "Pool state stores accumulated protocol fee amount"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_vault_0",
+          "docs": [
+            "The address that holds pool tokens for token_0"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_vault_1",
+          "docs": [
+            "The address that holds pool tokens for token_1"
+          ],
+          "writable": true
+        },
+        {
+          "name": "vault_0_mint",
+          "docs": [
+            "The mint of token vault 0"
+          ]
+        },
+        {
+          "name": "vault_1_mint",
+          "docs": [
+            "The mint of token vault 1"
+          ]
+        },
+        {
+          "name": "recipient_token_account_0",
+          "docs": [
+            "The address that receives the collected token_0 protocol fees"
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "account",
+                "path": "admin_group.fee_keeper",
+                "account": "AmmAdminGroup"
+              },
+              {
+                "kind": "const",
+                "value": [
+                  6,
+                  221,
+                  246,
+                  225,
+                  215,
+                  101,
+                  161,
+                  147,
+                  217,
+                  203,
+                  225,
+                  70,
+                  206,
+                  235,
+                  121,
+                  172,
+                  28,
+                  180,
+                  133,
+                  237,
+                  95,
+                  91,
+                  55,
+                  145,
+                  58,
+                  140,
+                  245,
+                  133,
+                  126,
+                  255,
+                  0,
+                  169
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "vault_0_mint"
+              }
+            ],
+            "program": {
+              "kind": "const",
+              "value": [
+                140,
+                151,
+                37,
+                143,
+                78,
+                36,
+                137,
+                241,
+                187,
+                61,
+                16,
+                41,
+                20,
+                142,
+                13,
+                131,
+                11,
+                90,
+                19,
+                153,
+                218,
+                255,
+                16,
+                132,
+                4,
+                142,
+                123,
+                216,
+                219,
+                233,
+                248,
+                89
+              ]
+            }
+          }
+        },
+        {
+          "name": "recipient_token_account_1",
+          "docs": [
+            "The address that receives the collected token_1 protocol fees"
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "account",
+                "path": "admin_group.fee_keeper",
+                "account": "AmmAdminGroup"
+              },
+              {
+                "kind": "const",
+                "value": [
+                  6,
+                  221,
+                  246,
+                  225,
+                  215,
+                  101,
+                  161,
+                  147,
+                  217,
+                  203,
+                  225,
+                  70,
+                  206,
+                  235,
+                  121,
+                  172,
+                  28,
+                  180,
+                  133,
+                  237,
+                  95,
+                  91,
+                  55,
+                  145,
+                  58,
+                  140,
+                  245,
+                  133,
+                  126,
+                  255,
+                  0,
+                  169
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "vault_1_mint"
+              }
+            ],
+            "program": {
+              "kind": "const",
+              "value": [
+                140,
+                151,
+                37,
+                143,
+                78,
+                36,
+                137,
+                241,
+                187,
+                61,
+                16,
+                41,
+                20,
+                142,
+                13,
+                131,
+                11,
+                90,
+                19,
+                153,
+                218,
+                255,
+                16,
+                132,
+                4,
+                142,
+                123,
+                216,
+                219,
+                233,
+                248,
+                89
+              ]
+            }
+          }
+        },
+        {
+          "name": "token_program",
+          "docs": [
+            "The SPL program to perform token transfers"
+          ],
+          "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
+        },
+        {
+          "name": "token_program_2022",
+          "docs": [
+            "The SPL program 2022 to perform token transfers"
+          ],
+          "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
+        },
+        {
+          "name": "associated_token_program",
+          "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
+        }
+      ],
+      "args": [
+        {
+          "name": "amount_0_requested",
+          "type": "u64"
+        },
+        {
+          "name": "amount_1_requested",
+          "type": "u64"
+        }
+      ]
+    },
+    {
+      "name": "collect_protocol_fee",
+      "docs": [
+        "Collect the protocol fee accrued to the pool",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx` - The context of accounts",
+        "* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1",
+        "* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",
+        ""
+      ],
+      "discriminator": [
+        136,
+        136,
+        252,
+        221,
+        194,
+        66,
+        126,
+        89
+      ],
+      "accounts": [
+        {
+          "name": "admin_group",
+          "docs": [
+            "amm admin group account to store admin permissions.",
+            "anyone can collect fee, but only fee-manager in admin group can receive fee"
+          ],
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  97,
+                  100,
+                  109,
+                  105,
+                  110,
+                  95,
+                  103,
+                  114,
+                  111,
+                  117,
+                  112
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "pool_state",
+          "docs": [
+            "Pool state stores accumulated protocol fee amount"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_vault_0",
+          "docs": [
+            "The address that holds pool tokens for token_0"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_vault_1",
+          "docs": [
+            "The address that holds pool tokens for token_1"
+          ],
+          "writable": true
+        },
+        {
+          "name": "vault_0_mint",
+          "docs": [
+            "The mint of token vault 0"
+          ]
+        },
+        {
+          "name": "vault_1_mint",
+          "docs": [
+            "The mint of token vault 1"
+          ]
+        },
+        {
+          "name": "recipient_token_account_0",
+          "docs": [
+            "The address that receives the collected token_0 protocol fees"
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "account",
+                "path": "admin_group.fee_keeper",
+                "account": "AmmAdminGroup"
+              },
+              {
+                "kind": "const",
+                "value": [
+                  6,
+                  221,
+                  246,
+                  225,
+                  215,
+                  101,
+                  161,
+                  147,
+                  217,
+                  203,
+                  225,
+                  70,
+                  206,
+                  235,
+                  121,
+                  172,
+                  28,
+                  180,
+                  133,
+                  237,
+                  95,
+                  91,
+                  55,
+                  145,
+                  58,
+                  140,
+                  245,
+                  133,
+                  126,
+                  255,
+                  0,
+                  169
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "vault_0_mint"
+              }
+            ],
+            "program": {
+              "kind": "const",
+              "value": [
+                140,
+                151,
+                37,
+                143,
+                78,
+                36,
+                137,
+                241,
+                187,
+                61,
+                16,
+                41,
+                20,
+                142,
+                13,
+                131,
+                11,
+                90,
+                19,
+                153,
+                218,
+                255,
+                16,
+                132,
+                4,
+                142,
+                123,
+                216,
+                219,
+                233,
+                248,
+                89
+              ]
+            }
+          }
+        },
+        {
+          "name": "recipient_token_account_1",
+          "docs": [
+            "The address that receives the collected token_1 protocol fees"
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "account",
+                "path": "admin_group.fee_keeper",
+                "account": "AmmAdminGroup"
+              },
+              {
+                "kind": "const",
+                "value": [
+                  6,
+                  221,
+                  246,
+                  225,
+                  215,
+                  101,
+                  161,
+                  147,
+                  217,
+                  203,
+                  225,
+                  70,
+                  206,
+                  235,
+                  121,
+                  172,
+                  28,
+                  180,
+                  133,
+                  237,
+                  95,
+                  91,
+                  55,
+                  145,
+                  58,
+                  140,
+                  245,
+                  133,
+                  126,
+                  255,
+                  0,
+                  169
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "vault_1_mint"
+              }
+            ],
+            "program": {
+              "kind": "const",
+              "value": [
+                140,
+                151,
+                37,
+                143,
+                78,
+                36,
+                137,
+                241,
+                187,
+                61,
+                16,
+                41,
+                20,
+                142,
+                13,
+                131,
+                11,
+                90,
+                19,
+                153,
+                218,
+                255,
+                16,
+                132,
+                4,
+                142,
+                123,
+                216,
+                219,
+                233,
+                248,
+                89
+              ]
+            }
+          }
+        },
+        {
+          "name": "token_program",
+          "docs": [
+            "The SPL program to perform token transfers"
+          ],
+          "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
+        },
+        {
+          "name": "token_program_2022",
+          "docs": [
+            "The SPL program 2022 to perform token transfers"
+          ],
+          "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
+        },
+        {
+          "name": "associated_token_program",
+          "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
+        }
+      ],
+      "args": [
+        {
+          "name": "amount_0_requested",
+          "type": "u64"
+        },
+        {
+          "name": "amount_1_requested",
+          "type": "u64"
+        }
+      ]
+    },
+    {
+      "name": "collect_remaining_rewards",
+      "docs": [
+        "Collect remaining reward token for reward founder",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx`- The context of accounts",
+        "* `reward_index` - the index to reward info",
+        ""
+      ],
+      "discriminator": [
+        18,
+        237,
+        166,
+        197,
+        34,
+        16,
+        213,
+        144
+      ],
+      "accounts": [
+        {
+          "name": "reward_funder",
+          "docs": [
+            "The founder who init reward info previously"
+          ],
+          "signer": true
+        },
+        {
+          "name": "admin_group",
+          "docs": [
+            "amm admin group account to store admin permissions."
+          ],
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  97,
+                  100,
+                  109,
+                  105,
+                  110,
+                  95,
+                  103,
+                  114,
+                  111,
+                  117,
+                  112
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "funder_token_account",
+          "docs": [
+            "The funder's reward token account"
+          ],
+          "writable": true
+        },
+        {
+          "name": "pool_state",
+          "docs": [
+            "Set reward for this pool"
+          ],
+          "writable": true
+        },
+        {
+          "name": "reward_token_vault",
+          "docs": [
+            "Reward vault transfer remaining token to founder token account"
+          ]
+        },
+        {
+          "name": "reward_vault_mint",
+          "docs": [
+            "The mint of reward token vault"
+          ]
+        },
+        {
+          "name": "token_program",
+          "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
+        },
+        {
+          "name": "token_program_2022",
+          "docs": [
+            "Token program 2022"
+          ],
+          "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
+        },
+        {
+          "name": "memo_program",
+          "docs": [
+            "memo program"
+          ],
+          "address": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"
+        }
+      ],
+      "args": [
+        {
+          "name": "reward_index",
+          "type": "u8"
+        }
+      ]
+    },
+    {
+      "name": "create_amm_config",
+      "docs": [
+        "# Arguments",
+        "",
+        "* `ctx`- The accounts needed by instruction.",
+        "* `index` - The index of amm config, there may be multiple config.",
+        "* `tick_spacing` - The tickspacing binding with config, cannot be changed.",
+        "* `trade_fee_rate` - Trade fee rate, can be changed.",
+        "* `protocol_fee_rate` - The rate of protocol fee within trade fee.",
+        "* `fund_fee_rate` - The rate of fund fee within trade fee.",
+        ""
+      ],
+      "discriminator": [
+        137,
+        52,
+        237,
+        212,
+        215,
+        117,
+        108,
+        104
+      ],
+      "accounts": [
+        {
+          "name": "owner",
+          "docs": [
+            "Address to be set as normal manager in admin group."
+          ],
+          "writable": true,
+          "signer": true
+        },
+        {
+          "name": "admin_group",
+          "docs": [
+            "amm admin group account to store admin permissions."
+          ],
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  97,
+                  100,
+                  109,
+                  105,
+                  110,
+                  95,
+                  103,
+                  114,
+                  111,
+                  117,
+                  112
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "amm_config",
+          "docs": [
+            "Initialize config state account to store protocol owner address and fee rates."
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  97,
+                  109,
+                  109,
+                  95,
+                  99,
+                  111,
+                  110,
+                  102,
+                  105,
+                  103
+                ]
+              },
+              {
+                "kind": "arg",
+                "path": "index"
+              }
+            ]
+          }
+        },
+        {
+          "name": "system_program",
+          "address": "11111111111111111111111111111111"
+        }
+      ],
+      "args": [
+        {
+          "name": "index",
+          "type": "u16"
+        },
+        {
+          "name": "tick_spacing",
+          "type": "u16"
+        },
+        {
+          "name": "trade_fee_rate",
+          "type": "u32"
+        },
+        {
+          "name": "protocol_fee_rate",
+          "type": "u32"
+        },
+        {
+          "name": "fund_fee_rate",
+          "type": "u32"
+        }
+      ]
+    },
+    {
+      "name": "create_operation_account",
+      "docs": [
+        "Creates an operation account for the program",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx`- The context of accounts",
+        ""
+      ],
+      "discriminator": [
+        63,
+        87,
+        148,
+        33,
+        109,
+        35,
+        8,
+        104
+      ],
+      "accounts": [
+        {
+          "name": "owner",
+          "docs": [
+            "Address to be set as operation account owner."
+          ],
+          "writable": true,
+          "signer": true
+        },
+        {
+          "name": "admin_group",
+          "docs": [
+            "amm admin group account to store admin permissions."
+          ],
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  97,
+                  100,
+                  109,
+                  105,
+                  110,
+                  95,
+                  103,
+                  114,
+                  111,
+                  117,
+                  112
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "operation_state",
+          "docs": [
+            "Initialize operation state account to store operation owner address and white list mint."
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  111,
+                  112,
+                  101,
+                  114,
+                  97,
+                  116,
+                  105,
+                  111,
+                  110
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "system_program",
+          "address": "11111111111111111111111111111111"
+        }
+      ],
+      "args": []
+    },
+    {
+      "name": "create_pool",
+      "docs": [
+        "Creates a pool for the given token pair and the initial price",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx`- The context of accounts",
+        "* `sqrt_price_x64` - the initial sqrt price (amount_token_1 / amount_token_0) of the pool as a Q64.64",
+        "Note: The open_time must be smaller than the current block_timestamp on chain."
+      ],
+      "discriminator": [
+        233,
+        146,
+        209,
+        142,
+        207,
+        104,
+        64,
+        188
+      ],
+      "accounts": [
+        {
+          "name": "pool_creator",
+          "docs": [
+            "Address paying to create the pool. Can be anyone"
+          ],
+          "writable": true,
+          "signer": true
+        },
+        {
+          "name": "pool_manager",
+          "docs": [
+            "with pool_manager permission, the pool creator can create a pool."
+          ],
+          "signer": true
+        },
+        {
+          "name": "admin_group",
+          "docs": [
+            "amm admin group account to store admin permissions."
+          ],
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  97,
+                  100,
+                  109,
+                  105,
+                  110,
+                  95,
+                  103,
+                  114,
+                  111,
+                  117,
+                  112
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "amm_config",
+          "docs": [
+            "Which config the pool belongs to."
+          ]
+        },
+        {
+          "name": "pool_state",
+          "docs": [
+            "Initialize an account to store the pool state"
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  112,
+                  111,
+                  111,
+                  108
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "amm_config"
+              },
+              {
+                "kind": "account",
+                "path": "token_mint_0"
+              },
+              {
+                "kind": "account",
+                "path": "token_mint_1"
+              }
+            ]
+          }
+        },
+        {
+          "name": "offchain_reward_config",
+          "docs": [
+            "Initialize an account to store the off-chain reward config"
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  111,
+                  102,
+                  102,
+                  99,
+                  104,
+                  97,
+                  105,
+                  110,
+                  95,
+                  114,
+                  101,
+                  119,
+                  97,
+                  114,
+                  100
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_state"
+              }
+            ]
+          }
+        },
+        {
+          "name": "token_mint_0",
+          "docs": [
+            "Token_0 mint, the key must be smaller then token_1 mint."
+          ]
+        },
+        {
+          "name": "token_mint_1",
+          "docs": [
+            "Token_1 mint"
+          ]
+        },
+        {
+          "name": "token_vault_0",
+          "docs": [
+            "Token_0 vault for the pool"
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  112,
+                  111,
+                  111,
+                  108,
+                  95,
+                  118,
+                  97,
+                  117,
+                  108,
+                  116
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_state"
+              },
+              {
+                "kind": "account",
+                "path": "token_mint_0"
+              }
+            ]
+          }
+        },
+        {
+          "name": "token_vault_1",
+          "docs": [
+            "Token_1 vault for the pool"
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  112,
+                  111,
+                  111,
+                  108,
+                  95,
+                  118,
+                  97,
+                  117,
+                  108,
+                  116
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_state"
+              },
+              {
+                "kind": "account",
+                "path": "token_mint_1"
+              }
+            ]
+          }
+        },
+        {
+          "name": "observation_state",
+          "docs": [
+            "Initialize an account to store oracle observations"
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  111,
+                  98,
+                  115,
+                  101,
+                  114,
+                  118,
+                  97,
+                  116,
+                  105,
+                  111,
+                  110
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_state"
+              }
+            ]
+          }
+        },
+        {
+          "name": "tick_array_bitmap",
+          "docs": [
+            "Initialize an account to store if a tick array is initialized."
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  112,
+                  111,
+                  111,
+                  108,
+                  95,
+                  116,
+                  105,
+                  99,
+                  107,
+                  95,
+                  97,
+                  114,
+                  114,
+                  97,
+                  121,
+                  95,
+                  98,
+                  105,
+                  116,
+                  109,
+                  97,
+                  112,
+                  95,
+                  101,
+                  120,
+                  116,
+                  101,
+                  110,
+                  115,
+                  105,
+                  111,
+                  110
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_state"
+              }
+            ]
+          }
+        },
+        {
+          "name": "token_program_0",
+          "docs": [
+            "Spl token program or token program 2022"
+          ]
+        },
+        {
+          "name": "token_program_1",
+          "docs": [
+            "Spl token program or token program 2022"
+          ]
+        },
+        {
+          "name": "system_program",
+          "docs": [
+            "To create a new program account"
+          ],
+          "address": "11111111111111111111111111111111"
+        },
+        {
+          "name": "rent",
+          "docs": [
+            "Sysvar for program account"
+          ],
+          "address": "SysvarRent111111111111111111111111111111111"
+        }
+      ],
+      "args": [
+        {
+          "name": "sqrt_price_x64",
+          "type": "u128"
+        },
+        {
+          "name": "open_time",
+          "type": "u64"
+        }
+      ]
+    },
+    {
+      "name": "create_support_mint_associated",
+      "docs": [
+        "Create support token22 mint account which can create pool and send rewards with ignoring the not support extensions."
+      ],
+      "discriminator": [
+        17,
+        251,
+        65,
+        92,
+        136,
+        242,
+        14,
+        169
+      ],
+      "accounts": [
+        {
+          "name": "owner",
+          "docs": [
+            "Address to be set as protocol owner."
+          ],
+          "writable": true,
+          "signer": true
+        },
+        {
+          "name": "admin_group",
+          "docs": [
+            "amm admin group account to store admin permissions."
+          ],
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  97,
+                  100,
+                  109,
+                  105,
+                  110,
+                  95,
+                  103,
+                  114,
+                  111,
+                  117,
+                  112
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "token_mint",
+          "docs": [
+            "Support token mint"
+          ]
+        },
+        {
+          "name": "support_mint_associated",
+          "docs": [
+            "Initialize support mint state account to store support mint address and bump."
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  115,
+                  117,
+                  112,
+                  112,
+                  111,
+                  114,
+                  116,
+                  95,
+                  109,
+                  105,
+                  110,
+                  116
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "token_mint"
+              }
+            ]
+          }
+        },
+        {
+          "name": "system_program",
+          "address": "11111111111111111111111111111111"
+        }
+      ],
+      "args": []
+    },
+    {
+      "name": "decrease_liquidity",
+      "docs": [
+        "#[deprecated(note = \"Use `decrease_liquidity_v2` instead.\")]",
+        "Decreases liquidity for an existing position",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx` -  The context of accounts",
+        "* `liquidity` - The amount by which liquidity will be decreased",
+        "* `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity",
+        "* `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity",
+        ""
+      ],
+      "discriminator": [
+        160,
+        38,
+        208,
+        111,
+        104,
+        91,
+        44,
+        1
+      ],
+      "accounts": [
+        {
+          "name": "nft_owner",
+          "docs": [
+            "The position owner or delegated authority"
+          ],
+          "signer": true
+        },
+        {
+          "name": "nft_account",
+          "docs": [
+            "The token account for the tokenized position"
+          ]
+        },
+        {
+          "name": "personal_position",
+          "docs": [
+            "Decrease liquidity for this position"
+          ],
+          "writable": true
+        },
+        {
+          "name": "pool_state",
+          "writable": true
+        },
+        {
+          "name": "protocol_position",
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  112,
+                  111,
+                  115,
+                  105,
+                  116,
+                  105,
+                  111,
+                  110
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_state"
+              },
+              {
+                "kind": "account",
+                "path": "personal_position.tick_lower_index",
+                "account": "PersonalPositionState"
+              },
+              {
+                "kind": "account",
+                "path": "personal_position.tick_upper_index",
+                "account": "PersonalPositionState"
+              }
+            ]
+          }
+        },
+        {
+          "name": "token_vault_0",
+          "docs": [
+            "Token_0 vault"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_vault_1",
+          "docs": [
+            "Token_1 vault"
+          ],
+          "writable": true
+        },
+        {
+          "name": "tick_array_lower",
+          "docs": [
+            "Stores init state for the lower tick"
+          ],
+          "writable": true
+        },
+        {
+          "name": "tick_array_upper",
+          "docs": [
+            "Stores init state for the upper tick"
+          ],
+          "writable": true
+        },
+        {
+          "name": "recipient_token_account_0",
+          "docs": [
+            "The destination token account for receive amount_0"
+          ],
+          "writable": true
+        },
+        {
+          "name": "recipient_token_account_1",
+          "docs": [
+            "The destination token account for receive amount_1"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_program",
+          "docs": [
+            "SPL program to transfer out tokens"
+          ],
+          "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
+        }
+      ],
+      "args": [
+        {
+          "name": "liquidity",
+          "type": "u128"
+        },
+        {
+          "name": "amount_0_min",
+          "type": "u64"
+        },
+        {
+          "name": "amount_1_min",
+          "type": "u64"
+        }
+      ]
+    },
+    {
+      "name": "decrease_liquidity_v2",
+      "docs": [
+        "Decreases liquidity for an existing position, support Token2022",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx` -  The context of accounts",
+        "* `liquidity` - The amount by which liquidity will be decreased",
+        "* `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity",
+        "* `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity",
+        ""
+      ],
+      "discriminator": [
+        58,
+        127,
+        188,
+        62,
+        79,
+        82,
+        196,
+        96
+      ],
+      "accounts": [
+        {
+          "name": "nft_owner",
+          "docs": [
+            "The position owner or delegated authority"
+          ],
+          "signer": true
+        },
+        {
+          "name": "nft_account",
+          "docs": [
+            "The token account for the tokenized position"
+          ]
+        },
+        {
+          "name": "personal_position",
+          "docs": [
+            "Decrease liquidity for this position"
+          ],
+          "writable": true
+        },
+        {
+          "name": "pool_state",
+          "writable": true
+        },
+        {
+          "name": "protocol_position",
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  112,
+                  111,
+                  115,
+                  105,
+                  116,
+                  105,
+                  111,
+                  110
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_state"
+              },
+              {
+                "kind": "account",
+                "path": "personal_position.tick_lower_index",
+                "account": "PersonalPositionState"
+              },
+              {
+                "kind": "account",
+                "path": "personal_position.tick_upper_index",
+                "account": "PersonalPositionState"
+              }
+            ]
+          }
+        },
+        {
+          "name": "token_vault_0",
+          "docs": [
+            "Token_0 vault"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_vault_1",
+          "docs": [
+            "Token_1 vault"
+          ],
+          "writable": true
+        },
+        {
+          "name": "tick_array_lower",
+          "docs": [
+            "Stores init state for the lower tick"
+          ],
+          "writable": true
+        },
+        {
+          "name": "tick_array_upper",
+          "docs": [
+            "Stores init state for the upper tick"
+          ],
+          "writable": true
+        },
+        {
+          "name": "recipient_token_account_0",
+          "docs": [
+            "The destination token account for receive amount_0"
+          ],
+          "writable": true
+        },
+        {
+          "name": "recipient_token_account_1",
+          "docs": [
+            "The destination token account for receive amount_1"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_program",
+          "docs": [
+            "SPL program to transfer out tokens"
+          ],
+          "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
+        },
+        {
+          "name": "token_program_2022",
+          "docs": [
+            "Token program 2022"
+          ],
+          "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
+        },
+        {
+          "name": "memo_program",
+          "docs": [
+            "memo program"
+          ],
+          "address": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"
+        },
+        {
+          "name": "vault_0_mint",
+          "docs": [
+            "The mint of token vault 0"
+          ]
+        },
+        {
+          "name": "vault_1_mint",
+          "docs": [
+            "The mint of token vault 1"
+          ]
+        }
+      ],
+      "args": [
+        {
+          "name": "liquidity",
+          "type": "u128"
+        },
+        {
+          "name": "amount_0_min",
+          "type": "u64"
+        },
+        {
+          "name": "amount_1_min",
+          "type": "u64"
+        }
+      ]
+    },
+    {
+      "name": "deposit_offchain_reward",
+      "docs": [
+        "deposit offchain reward into the pool"
+      ],
+      "discriminator": [
+        97,
+        125,
+        48,
+        169,
+        92,
+        241,
+        44,
+        142
+      ],
+      "accounts": [
+        {
+          "name": "payer",
+          "docs": [
+            "the address paying to deposit the offchain reward."
+          ],
+          "writable": true,
+          "signer": true
+        },
+        {
+          "name": "authority",
+          "docs": [
+            "The authority make decision that who can deposit the offchain reward."
+          ],
+          "signer": true
+        },
+        {
+          "name": "admin_group",
+          "docs": [
+            "Initialize amm admin group account to store admin permissions."
+          ],
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  97,
+                  100,
+                  109,
+                  105,
+                  110,
+                  95,
+                  103,
+                  114,
+                  111,
+                  117,
+                  112
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "pool_id",
+          "docs": [
+            "the pool id, which is the pool state account."
+          ],
+          "relations": [
+            "reward_config"
+          ]
+        },
+        {
+          "name": "token_mint"
+        },
+        {
+          "name": "payer_token_account",
+          "docs": [
+            ""
+          ],
+          "writable": true
+        },
+        {
+          "name": "reward_vault_token_account",
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "account",
+                "path": "reward_config"
+              },
+              {
+                "kind": "account",
+                "path": "token_program"
+              },
+              {
+                "kind": "account",
+                "path": "token_mint"
+              }
+            ],
+            "program": {
+              "kind": "const",
+              "value": [
+                140,
+                151,
+                37,
+                143,
+                78,
+                36,
+                137,
+                241,
+                187,
+                61,
+                16,
+                41,
+                20,
+                142,
+                13,
+                131,
+                11,
+                90,
+                19,
+                153,
+                218,
+                255,
+                16,
+                132,
+                4,
+                142,
+                123,
+                216,
+                219,
+                233,
+                248,
+                89
+              ]
+            }
+          }
+        },
+        {
+          "name": "reward_config",
+          "docs": [
+            "The offchain reward config account, it also is the reward vault account."
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  111,
+                  102,
+                  102,
+                  99,
+                  104,
+                  97,
+                  105,
+                  110,
+                  95,
+                  114,
+                  101,
+                  119,
+                  97,
+                  114,
+                  100
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_id"
+              }
+            ]
+          }
+        },
+        {
+          "name": "token_program",
+          "docs": [
+            "Spl token program or token program 2022"
+          ]
+        },
+        {
+          "name": "associated_token_program",
+          "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
+        },
+        {
+          "name": "system_program",
+          "address": "11111111111111111111111111111111"
+        }
+      ],
+      "args": [
+        {
+          "name": "amount",
+          "type": "u64"
+        }
+      ]
+    },
+    {
+      "name": "increase_liquidity",
+      "docs": [
+        "#[deprecated(note = \"Use `increase_liquidity_v2` instead.\")]",
+        "Increases liquidity for an existing position, with amount paid by `payer`",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx` - The context of accounts",
+        "* `liquidity` - The desired liquidity to be added, can't be zero",
+        "* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check",
+        "* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check",
+        ""
+      ],
+      "discriminator": [
+        46,
+        156,
+        243,
+        118,
+        13,
+        205,
+        251,
+        178
+      ],
+      "accounts": [
+        {
+          "name": "nft_owner",
+          "docs": [
+            "Pays to mint the position"
+          ],
+          "signer": true
+        },
+        {
+          "name": "nft_account",
+          "docs": [
+            "The token account for nft"
+          ]
+        },
+        {
+          "name": "pool_state",
+          "writable": true
+        },
+        {
+          "name": "protocol_position",
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  112,
+                  111,
+                  115,
+                  105,
+                  116,
+                  105,
+                  111,
+                  110
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_state"
+              },
+              {
+                "kind": "account",
+                "path": "personal_position.tick_lower_index",
+                "account": "PersonalPositionState"
+              },
+              {
+                "kind": "account",
+                "path": "personal_position.tick_upper_index",
+                "account": "PersonalPositionState"
+              }
+            ]
+          }
+        },
+        {
+          "name": "personal_position",
+          "docs": [
+            "Increase liquidity for this position"
+          ],
+          "writable": true
+        },
+        {
+          "name": "tick_array_lower",
+          "docs": [
+            "Stores init state for the lower tick"
+          ],
+          "writable": true
+        },
+        {
+          "name": "tick_array_upper",
+          "docs": [
+            "Stores init state for the upper tick"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_account_0",
+          "docs": [
+            "The payer's token account for token_0"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_account_1",
+          "docs": [
+            "The token account spending token_1 to mint the position"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_vault_0",
+          "docs": [
+            "The address that holds pool tokens for token_0"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_vault_1",
+          "docs": [
+            "The address that holds pool tokens for token_1"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_program",
+          "docs": [
+            "Program to create mint account and mint tokens"
+          ],
+          "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
+        }
+      ],
+      "args": [
+        {
+          "name": "liquidity",
+          "type": "u128"
+        },
+        {
+          "name": "amount_0_max",
+          "type": "u64"
+        },
+        {
+          "name": "amount_1_max",
+          "type": "u64"
+        }
+      ]
+    },
+    {
+      "name": "increase_liquidity_v2",
+      "docs": [
+        "Increases liquidity for an existing position, with amount paid by `payer`, support Token2022",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx` - The context of accounts",
+        "* `liquidity` - The desired liquidity to be added, if zero, calculate liquidity base amount_0 or amount_1 according base_flag",
+        "* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check",
+        "* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check",
+        "* `base_flag` - must be specified if liquidity is zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",
+        ""
+      ],
+      "discriminator": [
+        133,
+        29,
+        89,
+        223,
+        69,
+        238,
+        176,
+        10
+      ],
+      "accounts": [
+        {
+          "name": "nft_owner",
+          "docs": [
+            "Pays to mint the position"
+          ],
+          "signer": true
+        },
+        {
+          "name": "nft_account",
+          "docs": [
+            "The token account for nft"
+          ]
+        },
+        {
+          "name": "pool_state",
+          "writable": true
+        },
+        {
+          "name": "protocol_position",
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  112,
+                  111,
+                  115,
+                  105,
+                  116,
+                  105,
+                  111,
+                  110
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_state"
+              },
+              {
+                "kind": "account",
+                "path": "personal_position.tick_lower_index",
+                "account": "PersonalPositionState"
+              },
+              {
+                "kind": "account",
+                "path": "personal_position.tick_upper_index",
+                "account": "PersonalPositionState"
+              }
+            ]
+          }
+        },
+        {
+          "name": "personal_position",
+          "docs": [
+            "Increase liquidity for this position"
+          ],
+          "writable": true
+        },
+        {
+          "name": "tick_array_lower",
+          "docs": [
+            "Stores init state for the lower tick"
+          ],
+          "writable": true
+        },
+        {
+          "name": "tick_array_upper",
+          "docs": [
+            "Stores init state for the upper tick"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_account_0",
+          "docs": [
+            "The payer's token account for token_0"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_account_1",
+          "docs": [
+            "The token account spending token_1 to mint the position"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_vault_0",
+          "docs": [
+            "The address that holds pool tokens for token_0"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_vault_1",
+          "docs": [
+            "The address that holds pool tokens for token_1"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_program",
+          "docs": [
+            "Program to create mint account and mint tokens"
+          ],
+          "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
+        },
+        {
+          "name": "token_program_2022",
+          "docs": [
+            "Token program 2022"
+          ],
+          "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
+        },
+        {
+          "name": "vault_0_mint",
+          "docs": [
+            "The mint of token vault 0"
+          ]
+        },
+        {
+          "name": "vault_1_mint",
+          "docs": [
+            "The mint of token vault 1"
+          ]
+        }
+      ],
+      "args": [
+        {
+          "name": "liquidity",
+          "type": "u128"
+        },
+        {
+          "name": "amount_0_max",
+          "type": "u64"
+        },
+        {
+          "name": "amount_1_max",
+          "type": "u64"
+        },
+        {
+          "name": "base_flag",
+          "type": {
+            "option": "bool"
+          }
+        }
+      ]
+    },
+    {
+      "name": "init_amm_admin_group",
+      "docs": [
+        "Initialize the AMM admin group account, which is used to manage the AMM protocol."
+      ],
+      "discriminator": [
+        209,
+        108,
+        32,
+        246,
+        157,
+        214,
+        237,
+        86
+      ],
+      "accounts": [
+        {
+          "name": "payer",
+          "docs": [
+            "only super admin can create admin group"
+          ],
+          "writable": true,
+          "signer": true,
+          "address": "AY196f8U5EvM999PVnvLmyvaUnzL4GLiFaGKUgnJXN6o"
+        },
+        {
+          "name": "admin_group",
+          "docs": [
+            "Initialize amm admin group account to store admin permissions."
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  97,
+                  100,
+                  109,
+                  105,
+                  110,
+                  95,
+                  103,
+                  114,
+                  111,
+                  117,
+                  112
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "system_program",
+          "address": "11111111111111111111111111111111"
+        }
+      ],
+      "args": [
+        {
+          "name": "params",
+          "type": {
+            "defined": {
+              "name": "InitAdminGroupParams"
+            }
+          }
+        }
+      ]
+    },
+    {
+      "name": "initialize_reward",
+      "docs": [
+        "Initialize a reward info for a given pool and reward index",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx`- The context of accounts",
+        "* `reward_index` - the index to reward info",
+        "* `open_time` - reward open timestamp",
+        "* `end_time` - reward end timestamp",
+        "* `emissions_per_second_x64` - Token reward per second are earned per unit of liquidity.",
+        ""
+      ],
+      "discriminator": [
+        95,
+        135,
+        192,
+        196,
+        242,
+        129,
+        230,
+        68
+      ],
+      "accounts": [
+        {
+          "name": "reward_funder",
+          "docs": [
+            "The founder deposit reward token to vault"
+          ],
+          "writable": true,
+          "signer": true
+        },
+        {
+          "name": "admin_group",
+          "docs": [
+            "amm admin group account to store admin permissions."
+          ],
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  97,
+                  100,
+                  109,
+                  105,
+                  110,
+                  95,
+                  103,
+                  114,
+                  111,
+                  117,
+                  112
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "funder_token_account",
+          "writable": true
+        },
+        {
+          "name": "amm_config",
+          "docs": [
+            "For check the reward_funder authority"
+          ]
+        },
+        {
+          "name": "pool_state",
+          "docs": [
+            "Set reward for this pool"
+          ],
+          "writable": true
+        },
+        {
+          "name": "operation_state",
+          "docs": [
+            "load info from the account to judge reward permission"
+          ],
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  111,
+                  112,
+                  101,
+                  114,
+                  97,
+                  116,
+                  105,
+                  111,
+                  110
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "reward_token_mint",
+          "docs": [
+            "Reward mint"
+          ]
+        },
+        {
+          "name": "reward_token_vault",
+          "docs": [
+            "A pda, reward vault"
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  112,
+                  111,
+                  111,
+                  108,
+                  95,
+                  114,
+                  101,
+                  119,
+                  97,
+                  114,
+                  100,
+                  95,
+                  118,
+                  97,
+                  117,
+                  108,
+                  116
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_state"
+              },
+              {
+                "kind": "account",
+                "path": "reward_token_mint"
+              }
+            ]
+          }
+        },
+        {
+          "name": "reward_token_program"
+        },
+        {
+          "name": "system_program",
+          "address": "11111111111111111111111111111111"
+        },
+        {
+          "name": "rent",
+          "address": "SysvarRent111111111111111111111111111111111"
+        }
+      ],
+      "args": [
+        {
+          "name": "param",
+          "type": {
+            "defined": {
+              "name": "InitializeRewardParam"
+            }
+          }
+        }
+      ]
+    },
+    {
+      "name": "open_position",
+      "docs": [
+        "#[deprecated(note = \"Use `open_position_with_token22_nft` instead.\")]",
+        "Creates a new position wrapped in a NFT",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx` - The context of accounts",
+        "* `tick_lower_index` - The low boundary of market",
+        "* `tick_upper_index` - The upper boundary of market",
+        "* `tick_array_lower_start_index` - The start index of tick array which include tick low",
+        "* `tick_array_upper_start_index` - The start index of tick array which include tick upper",
+        "* `liquidity` - The liquidity to be added",
+        "* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check",
+        "* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check",
+        ""
+      ],
+      "discriminator": [
+        135,
+        128,
+        47,
+        77,
+        15,
+        152,
+        240,
+        49
+      ],
+      "accounts": [
+        {
+          "name": "payer",
+          "docs": [
+            "Pays to mint the position"
+          ],
+          "writable": true,
+          "signer": true
+        },
+        {
+          "name": "position_nft_owner"
+        },
+        {
+          "name": "position_nft_mint",
+          "docs": [
+            "Unique token mint address"
+          ],
+          "writable": true,
+          "signer": true
+        },
+        {
+          "name": "position_nft_account",
+          "docs": [
+            "Token account where position NFT will be minted",
+            "This account created in the contract by cpi to avoid large stack variables"
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "account",
+                "path": "position_nft_owner"
+              },
+              {
+                "kind": "const",
+                "value": [
+                  6,
+                  221,
+                  246,
+                  225,
+                  215,
+                  101,
+                  161,
+                  147,
+                  217,
+                  203,
+                  225,
+                  70,
+                  206,
+                  235,
+                  121,
+                  172,
+                  28,
+                  180,
+                  133,
+                  237,
+                  95,
+                  91,
+                  55,
+                  145,
+                  58,
+                  140,
+                  245,
+                  133,
+                  126,
+                  255,
+                  0,
+                  169
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "position_nft_mint"
+              }
+            ],
+            "program": {
+              "kind": "const",
+              "value": [
+                140,
+                151,
+                37,
+                143,
+                78,
+                36,
+                137,
+                241,
+                187,
+                61,
+                16,
+                41,
+                20,
+                142,
+                13,
+                131,
+                11,
+                90,
+                19,
+                153,
+                218,
+                255,
+                16,
+                132,
+                4,
+                142,
+                123,
+                216,
+                219,
+                233,
+                248,
+                89
+              ]
+            }
+          }
+        },
+        {
+          "name": "metadata_account",
+          "docs": [
+            "To store metaplex metadata"
+          ],
+          "writable": true
+        },
+        {
+          "name": "pool_state",
+          "docs": [
+            "Add liquidity for this pool"
+          ],
+          "writable": true
+        },
+        {
+          "name": "protocol_position",
+          "docs": [
+            "Store the information of market marking in range"
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  112,
+                  111,
+                  115,
+                  105,
+                  116,
+                  105,
+                  111,
+                  110
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_state"
+              },
+              {
+                "kind": "arg",
+                "path": "tick_lower_index"
+              },
+              {
+                "kind": "arg",
+                "path": "tick_upper_index"
+              }
+            ]
+          }
+        },
+        {
+          "name": "tick_array_lower",
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  116,
+                  105,
+                  99,
+                  107,
+                  95,
+                  97,
+                  114,
+                  114,
+                  97,
+                  121
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_state"
+              },
+              {
+                "kind": "arg",
+                "path": "tick_array_lower_start_index"
+              }
+            ]
+          }
+        },
+        {
+          "name": "tick_array_upper",
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  116,
+                  105,
+                  99,
+                  107,
+                  95,
+                  97,
+                  114,
+                  114,
+                  97,
+                  121
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_state"
+              },
+              {
+                "kind": "arg",
+                "path": "tick_array_upper_start_index"
+              }
+            ]
+          }
+        },
+        {
+          "name": "personal_position",
+          "docs": [
+            "personal position state"
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  112,
+                  111,
+                  115,
+                  105,
+                  116,
+                  105,
+                  111,
+                  110
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "position_nft_mint"
+              }
+            ]
+          }
+        },
+        {
+          "name": "token_account_0",
+          "docs": [
+            "The token_0 account deposit token to the pool"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_account_1",
+          "docs": [
+            "The token_1 account deposit token to the pool"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_vault_0",
+          "docs": [
+            "The address that holds pool tokens for token_0"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_vault_1",
+          "docs": [
+            "The address that holds pool tokens for token_1"
+          ],
+          "writable": true
+        },
+        {
+          "name": "rent",
+          "docs": [
+            "Sysvar for token mint and ATA creation"
+          ],
+          "address": "SysvarRent111111111111111111111111111111111"
+        },
+        {
+          "name": "system_program",
+          "docs": [
+            "Program to create the position manager state account"
+          ],
+          "address": "11111111111111111111111111111111"
+        },
+        {
+          "name": "token_program",
+          "docs": [
+            "Program to create mint account and mint tokens"
+          ],
+          "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
+        },
+        {
+          "name": "associated_token_program",
+          "docs": [
+            "Program to create an ATA for receiving position NFT"
+          ],
+          "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
+        },
+        {
+          "name": "metadata_program",
+          "docs": [
+            "Program to create NFT metadata"
+          ],
+          "address": "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
+        }
+      ],
+      "args": [
+        {
+          "name": "tick_lower_index",
+          "type": "i32"
+        },
+        {
+          "name": "tick_upper_index",
+          "type": "i32"
+        },
+        {
+          "name": "tick_array_lower_start_index",
+          "type": "i32"
+        },
+        {
+          "name": "tick_array_upper_start_index",
+          "type": "i32"
+        },
+        {
+          "name": "liquidity",
+          "type": "u128"
+        },
+        {
+          "name": "amount_0_max",
+          "type": "u64"
+        },
+        {
+          "name": "amount_1_max",
+          "type": "u64"
+        }
+      ]
+    },
+    {
+      "name": "open_position_v2",
+      "docs": [
+        "#[deprecated(note = \"Use `open_position_with_token22_nft` instead.\")]",
+        "Creates a new position wrapped in a NFT, support Token2022",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx` - The context of accounts",
+        "* `tick_lower_index` - The low boundary of market",
+        "* `tick_upper_index` - The upper boundary of market",
+        "* `tick_array_lower_start_index` - The start index of tick array which include tick low",
+        "* `tick_array_upper_start_index` - The start index of tick array which include tick upper",
+        "* `liquidity` - The liquidity to be added, if zero, and the base_flag is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity",
+        "* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check",
+        "* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check",
+        "* `with_metadata` - The flag indicating whether to create NFT mint metadata",
+        "* `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",
+        ""
+      ],
+      "discriminator": [
+        77,
+        184,
+        74,
+        214,
+        112,
+        86,
+        241,
+        199
+      ],
+      "accounts": [
+        {
+          "name": "payer",
+          "docs": [
+            "Pays to mint the position"
+          ],
+          "writable": true,
+          "signer": true
+        },
+        {
+          "name": "position_nft_owner"
+        },
+        {
+          "name": "position_nft_mint",
+          "docs": [
+            "Unique token mint address"
+          ],
+          "writable": true,
+          "signer": true
+        },
+        {
+          "name": "position_nft_account",
+          "docs": [
+            "Token account where position NFT will be minted"
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "account",
+                "path": "position_nft_owner"
+              },
+              {
+                "kind": "const",
+                "value": [
+                  6,
+                  221,
+                  246,
+                  225,
+                  215,
+                  101,
+                  161,
+                  147,
+                  217,
+                  203,
+                  225,
+                  70,
+                  206,
+                  235,
+                  121,
+                  172,
+                  28,
+                  180,
+                  133,
+                  237,
+                  95,
+                  91,
+                  55,
+                  145,
+                  58,
+                  140,
+                  245,
+                  133,
+                  126,
+                  255,
+                  0,
+                  169
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "position_nft_mint"
+              }
+            ],
+            "program": {
+              "kind": "const",
+              "value": [
+                140,
+                151,
+                37,
+                143,
+                78,
+                36,
+                137,
+                241,
+                187,
+                61,
+                16,
+                41,
+                20,
+                142,
+                13,
+                131,
+                11,
+                90,
+                19,
+                153,
+                218,
+                255,
+                16,
+                132,
+                4,
+                142,
+                123,
+                216,
+                219,
+                233,
+                248,
+                89
+              ]
+            }
+          }
+        },
+        {
+          "name": "metadata_account",
+          "docs": [
+            "To store metaplex metadata"
+          ],
+          "writable": true
+        },
+        {
+          "name": "pool_state",
+          "docs": [
+            "Add liquidity for this pool"
+          ],
+          "writable": true
+        },
+        {
+          "name": "protocol_position",
+          "docs": [
+            "Store the information of market marking in range"
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  112,
+                  111,
+                  115,
+                  105,
+                  116,
+                  105,
+                  111,
+                  110
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_state"
+              },
+              {
+                "kind": "arg",
+                "path": "tick_lower_index"
+              },
+              {
+                "kind": "arg",
+                "path": "tick_upper_index"
+              }
+            ]
+          }
+        },
+        {
+          "name": "tick_array_lower",
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  116,
+                  105,
+                  99,
+                  107,
+                  95,
+                  97,
+                  114,
+                  114,
+                  97,
+                  121
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_state"
+              },
+              {
+                "kind": "arg",
+                "path": "tick_array_lower_start_index"
+              }
+            ]
+          }
+        },
+        {
+          "name": "tick_array_upper",
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  116,
+                  105,
+                  99,
+                  107,
+                  95,
+                  97,
+                  114,
+                  114,
+                  97,
+                  121
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_state"
+              },
+              {
+                "kind": "arg",
+                "path": "tick_array_upper_start_index"
+              }
+            ]
+          }
+        },
+        {
+          "name": "personal_position",
+          "docs": [
+            "personal position state"
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  112,
+                  111,
+                  115,
+                  105,
+                  116,
+                  105,
+                  111,
+                  110
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "position_nft_mint"
+              }
+            ]
+          }
+        },
+        {
+          "name": "token_account_0",
+          "docs": [
+            "The token_0 account deposit token to the pool"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_account_1",
+          "docs": [
+            "The token_1 account deposit token to the pool"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_vault_0",
+          "docs": [
+            "The address that holds pool tokens for token_0"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_vault_1",
+          "docs": [
+            "The address that holds pool tokens for token_1"
+          ],
+          "writable": true
+        },
+        {
+          "name": "rent",
+          "docs": [
+            "Sysvar for token mint and ATA creation"
+          ],
+          "address": "SysvarRent111111111111111111111111111111111"
+        },
+        {
+          "name": "system_program",
+          "docs": [
+            "Program to create the position manager state account"
+          ],
+          "address": "11111111111111111111111111111111"
+        },
+        {
+          "name": "token_program",
+          "docs": [
+            "Program to create mint account and mint tokens"
+          ],
+          "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
+        },
+        {
+          "name": "associated_token_program",
+          "docs": [
+            "Program to create an ATA for receiving position NFT"
+          ],
+          "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
+        },
+        {
+          "name": "metadata_program",
+          "docs": [
+            "Program to create NFT metadata"
+          ],
+          "address": "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
+        },
+        {
+          "name": "token_program_2022",
+          "docs": [
+            "Program to create mint account and mint tokens"
+          ],
+          "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
+        },
+        {
+          "name": "vault_0_mint",
+          "docs": [
+            "The mint of token vault 0"
+          ]
+        },
+        {
+          "name": "vault_1_mint",
+          "docs": [
+            "The mint of token vault 1"
+          ]
+        }
+      ],
+      "args": [
+        {
+          "name": "tick_lower_index",
+          "type": "i32"
+        },
+        {
+          "name": "tick_upper_index",
+          "type": "i32"
+        },
+        {
+          "name": "tick_array_lower_start_index",
+          "type": "i32"
+        },
+        {
+          "name": "tick_array_upper_start_index",
+          "type": "i32"
+        },
+        {
+          "name": "liquidity",
+          "type": "u128"
+        },
+        {
+          "name": "amount_0_max",
+          "type": "u64"
+        },
+        {
+          "name": "amount_1_max",
+          "type": "u64"
+        },
+        {
+          "name": "with_metadata",
+          "type": "bool"
+        },
+        {
+          "name": "base_flag",
+          "type": {
+            "option": "bool"
+          }
+        }
+      ]
+    },
+    {
+      "name": "open_position_with_token22_nft",
+      "docs": [
+        "Creates a new position wrapped in a Token2022 NFT without relying on metadata_program and metadata_account, reduce the cost for user to create a personal position.",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx` - The context of accounts",
+        "* `tick_lower_index` - The low boundary of market",
+        "* `tick_upper_index` - The upper boundary of market",
+        "* `tick_array_lower_start_index` - The start index of tick array which include tick low",
+        "* `tick_array_upper_start_index` - The start index of tick array which include tick upper",
+        "* `liquidity` - The liquidity to be added, if zero, and the base_flag is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity",
+        "* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check",
+        "* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check",
+        "* `with_metadata` - The flag indicating whether to create NFT mint metadata",
+        "* `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",
+        ""
+      ],
+      "discriminator": [
+        77,
+        255,
+        174,
+        82,
+        125,
+        29,
+        201,
+        46
+      ],
+      "accounts": [
+        {
+          "name": "payer",
+          "docs": [
+            "Pays to mint the position"
+          ],
+          "writable": true,
+          "signer": true
+        },
+        {
+          "name": "position_nft_owner"
+        },
+        {
+          "name": "position_nft_mint",
+          "docs": [
+            "Unique token mint address, initialize in contract"
+          ],
+          "writable": true,
+          "signer": true
+        },
+        {
+          "name": "position_nft_account",
+          "writable": true
+        },
+        {
+          "name": "pool_state",
+          "docs": [
+            "Add liquidity for this pool"
+          ],
+          "writable": true
+        },
+        {
+          "name": "protocol_position",
+          "docs": [
+            "Store the information of market marking in range"
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  112,
+                  111,
+                  115,
+                  105,
+                  116,
+                  105,
+                  111,
+                  110
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_state"
+              },
+              {
+                "kind": "arg",
+                "path": "tick_lower_index"
+              },
+              {
+                "kind": "arg",
+                "path": "tick_upper_index"
+              }
+            ]
+          }
+        },
+        {
+          "name": "tick_array_lower",
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  116,
+                  105,
+                  99,
+                  107,
+                  95,
+                  97,
+                  114,
+                  114,
+                  97,
+                  121
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_state"
+              },
+              {
+                "kind": "arg",
+                "path": "tick_array_lower_start_index"
+              }
+            ]
+          }
+        },
+        {
+          "name": "tick_array_upper",
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  116,
+                  105,
+                  99,
+                  107,
+                  95,
+                  97,
+                  114,
+                  114,
+                  97,
+                  121
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_state"
+              },
+              {
+                "kind": "arg",
+                "path": "tick_array_upper_start_index"
+              }
+            ]
+          }
+        },
+        {
+          "name": "personal_position",
+          "docs": [
+            "personal position state"
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  112,
+                  111,
+                  115,
+                  105,
+                  116,
+                  105,
+                  111,
+                  110
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "position_nft_mint"
+              }
+            ]
+          }
+        },
+        {
+          "name": "token_account_0",
+          "docs": [
+            "The token_0 account deposit token to the pool"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_account_1",
+          "docs": [
+            "The token_1 account deposit token to the pool"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_vault_0",
+          "docs": [
+            "The address that holds pool tokens for token_0"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_vault_1",
+          "docs": [
+            "The address that holds pool tokens for token_1"
+          ],
+          "writable": true
+        },
+        {
+          "name": "rent",
+          "docs": [
+            "Sysvar for token mint and ATA creation"
+          ],
+          "address": "SysvarRent111111111111111111111111111111111"
+        },
+        {
+          "name": "system_program",
+          "docs": [
+            "Program to create the position manager state account"
+          ],
+          "address": "11111111111111111111111111111111"
+        },
+        {
+          "name": "token_program",
+          "docs": [
+            "Program to transfer for token account"
+          ],
+          "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
+        },
+        {
+          "name": "associated_token_program",
+          "docs": [
+            "Program to create an ATA for receiving position NFT"
+          ],
+          "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
+        },
+        {
+          "name": "token_program_2022",
+          "docs": [
+            "Program to create NFT mint/token account and transfer for token22 account"
+          ],
+          "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
+        },
+        {
+          "name": "vault_0_mint",
+          "docs": [
+            "The mint of token vault 0"
+          ]
+        },
+        {
+          "name": "vault_1_mint",
+          "docs": [
+            "The mint of token vault 1"
+          ]
+        }
+      ],
+      "args": [
+        {
+          "name": "tick_lower_index",
+          "type": "i32"
+        },
+        {
+          "name": "tick_upper_index",
+          "type": "i32"
+        },
+        {
+          "name": "tick_array_lower_start_index",
+          "type": "i32"
+        },
+        {
+          "name": "tick_array_upper_start_index",
+          "type": "i32"
+        },
+        {
+          "name": "liquidity",
+          "type": "u128"
+        },
+        {
+          "name": "amount_0_max",
+          "type": "u64"
+        },
+        {
+          "name": "amount_1_max",
+          "type": "u64"
+        },
+        {
+          "name": "with_metadata",
+          "type": "bool"
+        },
+        {
+          "name": "base_flag",
+          "type": {
+            "option": "bool"
+          }
+        }
+      ]
+    },
+    {
+      "name": "set_reward_params",
+      "docs": [
+        "Reset reward param, start a new reward cycle or extend the current cycle.",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx` - The context of accounts",
+        "* `reward_index` - The index of reward token in the pool.",
+        "* `emissions_per_second_x64` - The per second emission reward, when extend the current cycle,",
+        "new value can't be less than old value",
+        "* `open_time` - reward open timestamp, must be set when starting a new cycle",
+        "* `end_time` - reward end timestamp",
+        ""
+      ],
+      "discriminator": [
+        112,
+        52,
+        167,
+        75,
+        32,
+        201,
+        211,
+        137
+      ],
+      "accounts": [
+        {
+          "name": "authority",
+          "docs": [
+            "Address to be set as protocol owner. It pays to create factory state account."
+          ],
+          "signer": true
+        },
+        {
+          "name": "admin_group",
+          "docs": [
+            "amm admin group account to store admin permissions."
+          ],
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  97,
+                  100,
+                  109,
+                  105,
+                  110,
+                  95,
+                  103,
+                  114,
+                  111,
+                  117,
+                  112
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "amm_config"
+        },
+        {
+          "name": "pool_state",
+          "writable": true
+        },
+        {
+          "name": "operation_state",
+          "docs": [
+            "load info from the account to judge reward permission"
+          ],
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  111,
+                  112,
+                  101,
+                  114,
+                  97,
+                  116,
+                  105,
+                  111,
+                  110
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "token_program",
+          "docs": [
+            "Token program"
+          ],
+          "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
+        },
+        {
+          "name": "token_program_2022",
+          "docs": [
+            "Token program 2022"
+          ],
+          "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
+        }
+      ],
+      "args": [
+        {
+          "name": "reward_index",
+          "type": "u8"
+        },
+        {
+          "name": "emissions_per_second_x64",
+          "type": "u128"
+        },
+        {
+          "name": "open_time",
+          "type": "u64"
+        },
+        {
+          "name": "end_time",
+          "type": "u64"
+        }
+      ]
+    },
+    {
+      "name": "swap",
+      "docs": [
+        "#[deprecated(note = \"Use `swap_v2` instead.\")]",
+        "Swaps one token for as much as possible of another token across a single pool",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx` - The context of accounts",
+        "* `amount` - Arranged in pairs with other_amount_threshold. (amount_in, amount_out_minimum) or (amount_out, amount_in_maximum)",
+        "* `other_amount_threshold` - For slippage check",
+        "* `sqrt_price_limit` - The Q64.64 sqrt price √P limit. If zero for one, the price cannot",
+        "* `is_base_input` - swap base input or swap base output",
+        ""
+      ],
+      "discriminator": [
+        248,
+        198,
+        158,
+        145,
+        225,
+        117,
+        135,
+        200
+      ],
+      "accounts": [
+        {
+          "name": "payer",
+          "docs": [
+            "The user performing the swap"
+          ],
+          "signer": true
+        },
+        {
+          "name": "amm_config",
+          "docs": [
+            "The factory state to read protocol fees"
+          ]
+        },
+        {
+          "name": "pool_state",
+          "docs": [
+            "The program account of the pool in which the swap will be performed"
+          ],
+          "writable": true
+        },
+        {
+          "name": "input_token_account",
+          "docs": [
+            "The user token account for input token"
+          ],
+          "writable": true
+        },
+        {
+          "name": "output_token_account",
+          "docs": [
+            "The user token account for output token"
+          ],
+          "writable": true
+        },
+        {
+          "name": "input_vault",
+          "docs": [
+            "The vault token account for input token"
+          ],
+          "writable": true
+        },
+        {
+          "name": "output_vault",
+          "docs": [
+            "The vault token account for output token"
+          ],
+          "writable": true
+        },
+        {
+          "name": "observation_state",
+          "docs": [
+            "The program account for the most recent oracle observation"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_program",
+          "docs": [
+            "SPL program for token transfers"
+          ],
+          "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
+        },
+        {
+          "name": "tick_array",
+          "writable": true
+        }
+      ],
+      "args": [
+        {
+          "name": "amount",
+          "type": "u64"
+        },
+        {
+          "name": "other_amount_threshold",
+          "type": "u64"
+        },
+        {
+          "name": "sqrt_price_limit_x64",
+          "type": "u128"
+        },
+        {
+          "name": "is_base_input",
+          "type": "bool"
+        }
+      ]
+    },
+    {
+      "name": "swap_v2",
+      "docs": [
+        "Swaps one token for as much as possible of another token across a single pool, support token program 2022",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx` - The context of accounts",
+        "* `amount` - Arranged in pairs with other_amount_threshold. (amount_in, amount_out_minimum) or (amount_out, amount_in_maximum)",
+        "* `other_amount_threshold` - For slippage check",
+        "* `sqrt_price_limit` - The Q64.64 sqrt price √P limit. If zero for one, the price cannot",
+        "* `is_base_input` - swap base input or swap base output",
+        ""
+      ],
+      "discriminator": [
+        43,
+        4,
+        237,
+        11,
+        26,
+        201,
+        30,
+        98
+      ],
+      "accounts": [
+        {
+          "name": "payer",
+          "docs": [
+            "The user performing the swap"
+          ],
+          "signer": true
+        },
+        {
+          "name": "amm_config",
+          "docs": [
+            "The factory state to read protocol fees"
+          ]
+        },
+        {
+          "name": "pool_state",
+          "docs": [
+            "The program account of the pool in which the swap will be performed"
+          ],
+          "writable": true
+        },
+        {
+          "name": "input_token_account",
+          "docs": [
+            "The user token account for input token"
+          ],
+          "writable": true
+        },
+        {
+          "name": "output_token_account",
+          "docs": [
+            "The user token account for output token"
+          ],
+          "writable": true
+        },
+        {
+          "name": "input_vault",
+          "docs": [
+            "The vault token account for input token"
+          ],
+          "writable": true
+        },
+        {
+          "name": "output_vault",
+          "docs": [
+            "The vault token account for output token"
+          ],
+          "writable": true
+        },
+        {
+          "name": "observation_state",
+          "docs": [
+            "The program account for the most recent oracle observation"
+          ],
+          "writable": true
+        },
+        {
+          "name": "token_program",
+          "docs": [
+            "SPL program for token transfers"
+          ],
+          "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
+        },
+        {
+          "name": "token_program_2022",
+          "docs": [
+            "SPL program 2022 for token transfers"
+          ],
+          "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
+        },
+        {
+          "name": "memo_program",
+          "docs": [
+            "Memo program"
+          ],
+          "address": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"
+        },
+        {
+          "name": "input_vault_mint",
+          "docs": [
+            "The mint of token vault 0"
+          ]
+        },
+        {
+          "name": "output_vault_mint",
+          "docs": [
+            "The mint of token vault 1"
+          ]
+        }
+      ],
+      "args": [
+        {
+          "name": "amount",
+          "type": "u64"
+        },
+        {
+          "name": "other_amount_threshold",
+          "type": "u64"
+        },
+        {
+          "name": "sqrt_price_limit_x64",
+          "type": "u128"
+        },
+        {
+          "name": "is_base_input",
+          "type": "bool"
+        }
+      ]
+    },
+    {
+      "name": "transfer_reward_owner",
+      "docs": [
+        "Transfer reward owner",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx`- The context of accounts",
+        "* `new_owner`- new owner pubkey",
+        ""
+      ],
+      "discriminator": [
+        7,
+        22,
+        12,
+        83,
+        242,
+        43,
+        48,
+        121
+      ],
+      "accounts": [
+        {
+          "name": "authority",
+          "docs": [
+            "Address to be set as operation account owner."
+          ],
+          "signer": true
+        },
+        {
+          "name": "admin_group",
+          "docs": [
+            "amm admin group account to store admin permissions."
+          ],
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  97,
+                  100,
+                  109,
+                  105,
+                  110,
+                  95,
+                  103,
+                  114,
+                  111,
+                  117,
+                  112
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "pool_state",
+          "writable": true
+        }
+      ],
+      "args": [
+        {
+          "name": "new_owner",
+          "type": "pubkey"
+        }
+      ]
+    },
+    {
+      "name": "update_amm_admin_group",
+      "docs": [
+        "Update the AMM admin group account, which is used to manage the AMM protocol."
+      ],
+      "discriminator": [
+        61,
+        183,
+        185,
+        188,
+        82,
+        81,
+        141,
+        197
+      ],
+      "accounts": [
+        {
+          "name": "payer",
+          "docs": [
+            "only super admin can create admin group"
+          ],
+          "writable": true,
+          "signer": true,
+          "address": "AY196f8U5EvM999PVnvLmyvaUnzL4GLiFaGKUgnJXN6o"
+        },
+        {
+          "name": "admin_group",
+          "docs": [
+            "update amm admin group account to store admin permissions."
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  97,
+                  100,
+                  109,
+                  105,
+                  110,
+                  95,
+                  103,
+                  114,
+                  111,
+                  117,
+                  112
+                ]
+              }
+            ]
+          }
+        }
+      ],
+      "args": [
+        {
+          "name": "params",
+          "type": {
+            "defined": {
+              "name": "UpdateAdminGroupParams"
+            }
+          }
+        }
+      ]
+    },
+    {
+      "name": "update_amm_config",
+      "docs": [
+        "Updates the owner of the amm config",
+        "Must be called by the current owner or admin",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx`- The context of accounts",
+        "* `trade_fee_rate`- The new trade fee rate of amm config, be set when `param` is 0",
+        "* `protocol_fee_rate`- The new protocol fee rate of amm config, be set when `param` is 1",
+        "* `fund_fee_rate`- The new fund fee rate of amm config, be set when `param` is 2",
+        "* `new_owner`- The config's new owner, be set when `param` is 3",
+        "* `new_fund_owner`- The config's new fund owner, be set when `param` is 4",
+        "* `param`- The value can be 0 | 1 | 2 | 3 | 4, otherwise will report a error",
+        ""
+      ],
+      "discriminator": [
+        49,
+        60,
+        174,
+        136,
+        154,
+        28,
+        116,
+        200
+      ],
+      "accounts": [
+        {
+          "name": "owner",
+          "docs": [
+            "The amm config owner or admin"
+          ],
+          "signer": true
+        },
+        {
+          "name": "admin_group",
+          "docs": [
+            "amm admin group account to store admin permissions."
+          ],
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  97,
+                  100,
+                  109,
+                  105,
+                  110,
+                  95,
+                  103,
+                  114,
+                  111,
+                  117,
+                  112
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "amm_config",
+          "docs": [
+            "Amm config account to be changed"
+          ],
+          "writable": true
+        }
+      ],
+      "args": [
+        {
+          "name": "param",
+          "type": "u8"
+        },
+        {
+          "name": "value",
+          "type": "u32"
+        }
+      ]
+    },
+    {
+      "name": "update_operation_account",
+      "docs": [
+        "Update the operation account",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx`- The context of accounts",
+        "* `param`- The value can be 0 | 1 | 2 | 3, otherwise will report a error",
+        "* `keys`- update operation owner when the `param` is 0",
+        "remove operation owner when the `param` is 1",
+        "update whitelist mint when the `param` is 2",
+        "remove whitelist mint when the `param` is 3",
+        ""
+      ],
+      "discriminator": [
+        127,
+        70,
+        119,
+        40,
+        188,
+        227,
+        61,
+        7
+      ],
+      "accounts": [
+        {
+          "name": "owner",
+          "docs": [
+            "Address to be set as operation account owner."
+          ],
+          "signer": true
+        },
+        {
+          "name": "admin_group",
+          "docs": [
+            "amm admin group account to store admin permissions."
+          ],
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  97,
+                  100,
+                  109,
+                  105,
+                  110,
+                  95,
+                  103,
+                  114,
+                  111,
+                  117,
+                  112
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "operation_state",
+          "docs": [
+            "Initialize operation state account to store operation owner address and white list mint."
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  111,
+                  112,
+                  101,
+                  114,
+                  97,
+                  116,
+                  105,
+                  111,
+                  110
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "system_program",
+          "address": "11111111111111111111111111111111"
+        }
+      ],
+      "args": [
+        {
+          "name": "param",
+          "type": "u8"
+        },
+        {
+          "name": "keys",
+          "type": {
+            "vec": "pubkey"
+          }
+        }
+      ]
+    },
+    {
+      "name": "update_pool_status",
+      "docs": [
+        "Update pool status for given value",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx`- The context of accounts",
+        "* `status` - The value of status",
+        ""
+      ],
+      "discriminator": [
+        130,
+        87,
+        108,
+        6,
+        46,
+        224,
+        117,
+        123
+      ],
+      "accounts": [
+        {
+          "name": "authority",
+          "signer": true
+        },
+        {
+          "name": "admin_group",
+          "docs": [
+            "amm admin group account to store admin permissions."
+          ],
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  97,
+                  100,
+                  109,
+                  105,
+                  110,
+                  95,
+                  103,
+                  114,
+                  111,
+                  117,
+                  112
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "pool_state",
+          "writable": true
+        }
+      ],
+      "args": [
+        {
+          "name": "status",
+          "type": "u8"
+        }
+      ]
+    },
+    {
+      "name": "update_reward_infos",
+      "docs": [
+        "Update rewards info of the given pool, can be called for everyone",
+        "",
+        "# Arguments",
+        "",
+        "* `ctx`- The context of accounts",
+        ""
+      ],
+      "discriminator": [
+        163,
+        172,
+        224,
+        52,
+        11,
+        154,
+        106,
+        223
+      ],
+      "accounts": [
+        {
+          "name": "pool_state",
+          "docs": [
+            "The liquidity pool for which reward info to update"
+          ],
+          "writable": true
+        }
+      ],
+      "args": []
+    },
+    {
+      "name": "withdraw_offchain_reward",
+      "docs": [
+        "withdraw offchain reward from the pool"
+      ],
+      "discriminator": [
+        86,
+        53,
+        59,
+        76,
+        217,
+        38,
+        71,
+        213
+      ],
+      "accounts": [
+        {
+          "name": "authority",
+          "docs": [
+            "The authority make decision that who can withdraw the offchain reward."
+          ],
+          "signer": true
+        },
+        {
+          "name": "admin_group",
+          "docs": [
+            "Initialize amm admin group account to store admin permissions."
+          ],
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  97,
+                  100,
+                  109,
+                  105,
+                  110,
+                  95,
+                  103,
+                  114,
+                  111,
+                  117,
+                  112
+                ]
+              }
+            ]
+          }
+        },
+        {
+          "name": "pool_id",
+          "docs": [
+            "the pool id, which is the pool state account."
+          ],
+          "relations": [
+            "reward_config"
+          ]
+        },
+        {
+          "name": "token_mint"
+        },
+        {
+          "name": "receiver_token_account",
+          "docs": [
+            "the address who receive the withdrawn offchain reward."
+          ],
+          "writable": true
+        },
+        {
+          "name": "reward_vault_token_account",
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "account",
+                "path": "reward_config"
+              },
+              {
+                "kind": "account",
+                "path": "token_program"
+              },
+              {
+                "kind": "account",
+                "path": "token_mint"
+              }
+            ],
+            "program": {
+              "kind": "const",
+              "value": [
+                140,
+                151,
+                37,
+                143,
+                78,
+                36,
+                137,
+                241,
+                187,
+                61,
+                16,
+                41,
+                20,
+                142,
+                13,
+                131,
+                11,
+                90,
+                19,
+                153,
+                218,
+                255,
+                16,
+                132,
+                4,
+                142,
+                123,
+                216,
+                219,
+                233,
+                248,
+                89
+              ]
+            }
+          }
+        },
+        {
+          "name": "reward_config",
+          "docs": [
+            "The offchain reward config account, it also is the reward vault account."
+          ],
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  111,
+                  102,
+                  102,
+                  99,
+                  104,
+                  97,
+                  105,
+                  110,
+                  95,
+                  114,
+                  101,
+                  119,
+                  97,
+                  114,
+                  100
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "pool_id"
+              }
+            ]
+          }
+        },
+        {
+          "name": "token_program",
+          "docs": [
+            "Spl token program or token program 2022"
+          ]
+        },
+        {
+          "name": "associated_token_program",
+          "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
+        }
+      ],
+      "args": [
+        {
+          "name": "amount",
+          "type": "u64"
+        }
+      ]
+    }
+  ],
+  "accounts": [
+    {
+      "name": "AmmAdminGroup",
+      "discriminator": [
+        128,
+        128,
+        234,
+        30,
+        61,
+        172,
+        188,
+        123
+      ]
+    },
+    {
+      "name": "AmmConfig",
+      "discriminator": [
+        218,
+        244,
+        33,
+        104,
+        203,
+        203,
+        43,
+        111
+      ]
+    },
+    {
+      "name": "ObservationState",
+      "discriminator": [
+        122,
+        174,
+        197,
+        53,
+        129,
+        9,
+        165,
+        132
+      ]
+    },
+    {
+      "name": "OffchainRewardConfig",
+      "discriminator": [
+        118,
+        52,
+        115,
+        150,
+        99,
+        69,
+        164,
+        76
+      ]
+    },
+    {
+      "name": "OperationState",
+      "discriminator": [
+        19,
+        236,
+        58,
+        237,
+        81,
+        222,
+        183,
+        252
+      ]
+    },
+    {
+      "name": "PersonalPositionState",
+      "discriminator": [
+        70,
+        111,
+        150,
+        126,
+        230,
+        15,
+        25,
+        117
+      ]
+    },
+    {
+      "name": "PoolState",
+      "discriminator": [
+        247,
+        237,
+        227,
+        245,
+        215,
+        195,
+        222,
+        70
+      ]
+    },
+    {
+      "name": "ProtocolPositionState",
+      "discriminator": [
+        100,
+        226,
+        145,
+        99,
+        146,
+        218,
+        160,
+        106
+      ]
+    },
+    {
+      "name": "SupportMintAssociated",
+      "discriminator": [
+        134,
+        40,
+        183,
+        79,
+        12,
+        112,
+        162,
+        53
+      ]
+    },
+    {
+      "name": "TickArrayBitmapExtension",
+      "discriminator": [
+        60,
+        150,
+        36,
+        219,
+        97,
+        128,
+        139,
+        153
+      ]
+    },
+    {
+      "name": "TickArrayState",
+      "discriminator": [
+        192,
+        155,
+        85,
+        205,
+        49,
+        249,
+        129,
+        42
+      ]
+    }
+  ],
+  "events": [
+    {
+      "name": "CollectPersonalFeeEvent",
+      "discriminator": [
+        166,
+        174,
+        105,
+        192,
+        81,
+        161,
+        83,
+        105
+      ]
+    },
+    {
+      "name": "CollectProtocolFeeEvent",
+      "discriminator": [
+        206,
+        87,
+        17,
+        79,
+        45,
+        41,
+        213,
+        61
+      ]
+    },
+    {
+      "name": "ConfigChangeEvent",
+      "discriminator": [
+        247,
+        189,
+        7,
+        119,
+        106,
+        112,
+        95,
+        151
+      ]
+    },
+    {
+      "name": "CreatePersonalPositionEvent",
+      "discriminator": [
+        100,
+        30,
+        87,
+        249,
+        196,
+        223,
+        154,
+        206
+      ]
+    },
+    {
+      "name": "DecreaseLiquidityEvent",
+      "discriminator": [
+        58,
+        222,
+        86,
+        58,
+        68,
+        50,
+        85,
+        56
+      ]
+    },
+    {
+      "name": "IncreaseLiquidityEvent",
+      "discriminator": [
+        49,
+        79,
+        105,
+        212,
+        32,
+        34,
+        30,
+        84
+      ]
+    },
+    {
+      "name": "LiquidityCalculateEvent",
+      "discriminator": [
+        237,
+        112,
+        148,
+        230,
+        57,
+        84,
+        180,
+        162
+      ]
+    },
+    {
+      "name": "LiquidityChangeEvent",
+      "discriminator": [
+        126,
+        240,
+        175,
+        206,
+        158,
+        88,
+        153,
+        107
+      ]
+    },
+    {
+      "name": "ModifyAmmAdminGroupEvent",
+      "discriminator": [
+        218,
+        118,
+        87,
+        81,
+        53,
+        80,
+        18,
+        235
+      ]
+    },
+    {
+      "name": "PoolCreatedEvent",
+      "discriminator": [
+        25,
+        94,
+        75,
+        47,
+        112,
+        99,
+        53,
+        63
+      ]
+    },
+    {
+      "name": "SwapEvent",
+      "discriminator": [
+        64,
+        198,
+        205,
+        232,
+        38,
+        8,
+        113,
+        226
+      ]
+    },
+    {
+      "name": "UpdateRewardInfosEvent",
+      "discriminator": [
+        109,
+        127,
+        186,
+        78,
+        114,
+        65,
+        37,
+        236
+      ]
+    }
+  ],
+  "errors": [
+    {
+      "code": 6000,
+      "name": "LOK",
+      "msg": "LOK"
+    },
+    {
+      "code": 6001,
+      "name": "NotApproved",
+      "msg": "Not approved"
+    },
+    {
+      "code": 6002,
+      "name": "InvalidUpdateConfigFlag",
+      "msg": "invalid update amm config flag"
+    },
+    {
+      "code": 6003,
+      "name": "AccountLack",
+      "msg": "Account lack"
+    },
+    {
+      "code": 6004,
+      "name": "ClosePositionErr",
+      "msg": "Remove liquitity, collect fees owed and reward then you can close position account"
+    },
+    {
+      "code": 6005,
+      "name": "ZeroMintAmount",
+      "msg": "Minting amount should be greater than 0"
+    },
+    {
+      "code": 6006,
+      "name": "InvalidTickIndex",
+      "msg": "Tick out of range"
+    },
+    {
+      "code": 6007,
+      "name": "TickInvalidOrder",
+      "msg": "The lower tick must be below the upper tick"
+    },
+    {
+      "code": 6008,
+      "name": "TickLowerOverflow",
+      "msg": "The tick must be greater, or equal to the minimum tick(-443636)"
+    },
+    {
+      "code": 6009,
+      "name": "TickUpperOverflow",
+      "msg": "The tick must be lesser than, or equal to the maximum tick(443636)"
+    },
+    {
+      "code": 6010,
+      "name": "TickAndSpacingNotMatch",
+      "msg": "tick % tick_spacing must be zero"
+    },
+    {
+      "code": 6011,
+      "name": "InvalidTickArray",
+      "msg": "Invalid tick array account"
+    },
+    {
+      "code": 6012,
+      "name": "InvalidTickArrayBoundary",
+      "msg": "Invalid tick array boundary"
+    },
+    {
+      "code": 6013,
+      "name": "SqrtPriceLimitOverflow",
+      "msg": "Square root price limit overflow"
+    },
+    {
+      "code": 6014,
+      "name": "SqrtPriceX64",
+      "msg": "sqrt_price_x64 out of range"
+    },
+    {
+      "code": 6015,
+      "name": "LiquiditySubValueErr",
+      "msg": "Liquidity sub delta L must be smaller than before"
+    },
+    {
+      "code": 6016,
+      "name": "LiquidityAddValueErr",
+      "msg": "Liquidity add delta L must be greater, or equal to before"
+    },
+    {
+      "code": 6017,
+      "name": "InvalidLiquidity",
+      "msg": "Invalid liquidity when update position"
+    },
+    {
+      "code": 6018,
+      "name": "ForbidBothZeroForSupplyLiquidity",
+      "msg": "Both token amount must not be zero while supply liquidity"
+    },
+    {
+      "code": 6019,
+      "name": "LiquidityInsufficient",
+      "msg": "Liquidity insufficient"
+    },
+    {
+      "code": 6020,
+      "name": "TransactionTooOld",
+      "msg": "Transaction too old"
+    },
+    {
+      "code": 6021,
+      "name": "PriceSlippageCheck",
+      "msg": "Price slippage check"
+    },
+    {
+      "code": 6022,
+      "name": "TooLittleOutputReceived",
+      "msg": "Too little output received"
+    },
+    {
+      "code": 6023,
+      "name": "TooMuchInputPaid",
+      "msg": "Too much input paid"
+    },
+    {
+      "code": 6024,
+      "name": "ZeroAmountSpecified",
+      "msg": "Swap special amount can not be zero"
+    },
+    {
+      "code": 6025,
+      "name": "InvalidInputPoolVault",
+      "msg": "Input pool vault is invalid"
+    },
+    {
+      "code": 6026,
+      "name": "TooSmallInputOrOutputAmount",
+      "msg": "Swap input or output amount is too small"
+    },
+    {
+      "code": 6027,
+      "name": "NotEnoughTickArrayAccount",
+      "msg": "Not enought tick array account"
+    },
+    {
+      "code": 6028,
+      "name": "InvalidFirstTickArrayAccount",
+      "msg": "Invalid first tick array account"
+    },
+    {
+      "code": 6029,
+      "name": "InvalidRewardIndex",
+      "msg": "Invalid reward index"
+    },
+    {
+      "code": 6030,
+      "name": "FullRewardInfo",
+      "msg": "The init reward token reach to the max"
+    },
+    {
+      "code": 6031,
+      "name": "RewardTokenAlreadyInUse",
+      "msg": "The init reward token already in use"
+    },
+    {
+      "code": 6032,
+      "name": "ExceptRewardMint",
+      "msg": "The reward tokens must contain one of pool vault mint except the last reward"
+    },
+    {
+      "code": 6033,
+      "name": "InvalidRewardInitParam",
+      "msg": "Invalid reward init param"
+    },
+    {
+      "code": 6034,
+      "name": "InvalidRewardDesiredAmount",
+      "msg": "Invalid collect reward desired amount"
+    },
+    {
+      "code": 6035,
+      "name": "InvalidRewardInputAccountNumber",
+      "msg": "Invalid collect reward input account number"
+    },
+    {
+      "code": 6036,
+      "name": "InvalidRewardPeriod",
+      "msg": "Invalid reward period"
+    },
+    {
+      "code": 6037,
+      "name": "NotApproveUpdateRewardEmissiones",
+      "msg": "Modification of emissiones is allowed within 72 hours from the end of the previous cycle"
+    },
+    {
+      "code": 6038,
+      "name": "UnInitializedRewardInfo",
+      "msg": "uninitialized reward info"
+    },
+    {
+      "code": 6039,
+      "name": "NotSupportMint",
+      "msg": "Not support token_2022 mint extension"
+    },
+    {
+      "code": 6040,
+      "name": "MissingTickArrayBitmapExtensionAccount",
+      "msg": "Missing tickarray bitmap extension account"
+    },
+    {
+      "code": 6041,
+      "name": "InsufficientLiquidityForDirection",
+      "msg": "Insufficient liquidity for this direction"
+    },
+    {
+      "code": 6042,
+      "name": "MaxTokenOverflow",
+      "msg": "Max token overflow"
+    },
+    {
+      "code": 6043,
+      "name": "CalculateOverflow",
+      "msg": "Calculate overflow"
+    },
+    {
+      "code": 6044,
+      "name": "TransferFeeCalculateNotMatch",
+      "msg": "TransferFee calculate not match"
+    },
+    {
+      "code": 6045,
+      "name": "IllegalAccountOwner",
+      "msg": "invalid account owner"
+    },
+    {
+      "code": 6046,
+      "name": "InvalidAccount",
+      "msg": "Invalid account"
+    }
+  ],
+  "types": [
+    {
+      "name": "AmmAdminGroup",
+      "docs": [
+        "Holds the admin group information."
+      ],
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "fee_keeper",
+            "docs": [
+              "the address who can hold the fee,",
+              "anyone can trigger the fee collection action,"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "reward_config_manager",
+            "docs": [
+              "the address who can config the reward(config, deposit, withdraw),",
+              "deposit reward, set the account who can deposit the reward, withdraw the remaining reward(withdraw)"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "reward_claim_manager",
+            "docs": [
+              "the address who can manage the offchain reward claim"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "pool_manager",
+            "docs": [
+              "the address who can manage the pool create action,",
+              "without this account's permission, no one can create a pool"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "emergency_manager",
+            "docs": [
+              "the address who can manage the emergency action,",
+              "emergency action includes stop/resume the pool, stop/resume withdraw lp"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "normal_manager",
+            "docs": [
+              "normal action manager,",
+              "such as create amm config, update amm config"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "pad",
+            "docs": [
+              "The space required for the account. may be used for future extensions."
+            ],
+            "type": {
+              "array": [
+                "pubkey",
+                6
+              ]
+            }
+          }
+        ]
+      }
+    },
+    {
+      "name": "AmmConfig",
+      "docs": [
+        "Holds the current owner of the factory"
+      ],
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "bump",
+            "docs": [
+              "Bump to identify PDA"
+            ],
+            "type": "u8"
+          },
+          {
+            "name": "index",
+            "type": "u16"
+          },
+          {
+            "name": "owner",
+            "docs": [
+              "Address of the protocol owner"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "protocol_fee_rate",
+            "docs": [
+              "The protocol fee"
+            ],
+            "type": "u32"
+          },
+          {
+            "name": "trade_fee_rate",
+            "docs": [
+              "The trade fee, denominated in hundredths of a bip (10^-6)"
+            ],
+            "type": "u32"
+          },
+          {
+            "name": "tick_spacing",
+            "docs": [
+              "The tick spacing"
+            ],
+            "type": "u16"
+          },
+          {
+            "name": "fund_fee_rate",
+            "docs": [
+              "The fund fee, denominated in hundredths of a bip (10^-6)"
+            ],
+            "type": "u32"
+          },
+          {
+            "name": "padding_u32",
+            "type": "u32"
+          },
+          {
+            "name": "fund_owner",
+            "type": "pubkey"
+          },
+          {
+            "name": "padding",
+            "type": {
+              "array": [
+                "u64",
+                3
+              ]
+            }
+          }
+        ]
+      }
+    },
+    {
+      "name": "CollectPersonalFeeEvent",
+      "docs": [
+        "Emitted when tokens are collected for a position"
+      ],
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "position_nft_mint",
+            "docs": [
+              "The ID of the token for which underlying tokens were collected"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "recipient_token_account_0",
+            "docs": [
+              "The token account that received the collected token_0 tokens"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "recipient_token_account_1",
+            "docs": [
+              "The token account that received the collected token_1 tokens"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "amount_0",
+            "docs": [
+              "The amount of token_0 owed to the position that was collected"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "amount_1",
+            "docs": [
+              "The amount of token_1 owed to the position that was collected"
+            ],
+            "type": "u64"
+          }
+        ]
+      }
+    },
+    {
+      "name": "CollectProtocolFeeEvent",
+      "docs": [
+        "Emitted when the collected protocol fees are withdrawn by the factory owner"
+      ],
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "pool_state",
+            "docs": [
+              "The pool whose protocol fee is collected"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "recipient_token_account_0",
+            "docs": [
+              "The address that receives the collected token_0 protocol fees"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "recipient_token_account_1",
+            "docs": [
+              "The address that receives the collected token_1 protocol fees"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "amount_0",
+            "docs": [
+              "The amount of token_0 protocol fees that is withdrawn"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "amount_1",
+            "docs": [
+              "The amount of token_0 protocol fees that is withdrawn"
+            ],
+            "type": "u64"
+          }
+        ]
+      }
+    },
+    {
+      "name": "ConfigChangeEvent",
+      "docs": [
+        "Emitted when create or update a config"
+      ],
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "index",
+            "type": "u16"
+          },
+          {
+            "name": "owner",
+            "type": "pubkey"
+          },
+          {
+            "name": "protocol_fee_rate",
+            "type": "u32"
+          },
+          {
+            "name": "trade_fee_rate",
+            "type": "u32"
+          },
+          {
+            "name": "tick_spacing",
+            "type": "u16"
+          },
+          {
+            "name": "fund_fee_rate",
+            "type": "u32"
+          },
+          {
+            "name": "fund_owner",
+            "type": "pubkey"
+          }
+        ]
+      }
+    },
+    {
+      "name": "CreatePersonalPositionEvent",
+      "docs": [
+        "Emitted when create a new position"
+      ],
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "pool_state",
+            "docs": [
+              "The pool for which liquidity was added"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "minter",
+            "docs": [
+              "The address that create the position"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "nft_owner",
+            "docs": [
+              "The owner of the position and recipient of any minted liquidity"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "tick_lower_index",
+            "docs": [
+              "The lower tick of the position"
+            ],
+            "type": "i32"
+          },
+          {
+            "name": "tick_upper_index",
+            "docs": [
+              "The upper tick of the position"
+            ],
+            "type": "i32"
+          },
+          {
+            "name": "liquidity",
+            "docs": [
+              "The amount of liquidity minted to the position range"
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "deposit_amount_0",
+            "docs": [
+              "The amount of token_0 was deposit for the liquidity"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "deposit_amount_1",
+            "docs": [
+              "The amount of token_1 was deposit for the liquidity"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "deposit_amount_0_transfer_fee",
+            "docs": [
+              "The token transfer fee for deposit_amount_0"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "deposit_amount_1_transfer_fee",
+            "docs": [
+              "The token transfer fee for deposit_amount_1"
+            ],
+            "type": "u64"
+          }
+        ]
+      }
+    },
+    {
+      "name": "DecreaseLiquidityEvent",
+      "docs": [
+        "Emitted when liquidity is decreased."
+      ],
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "position_nft_mint",
+            "docs": [
+              "The ID of the token for which liquidity was decreased"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "liquidity",
+            "docs": [
+              "The amount by which liquidity for the position was decreased"
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "decrease_amount_0",
+            "docs": [
+              "The amount of token_0 that was paid for the decrease in liquidity"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "decrease_amount_1",
+            "docs": [
+              "The amount of token_1 that was paid for the decrease in liquidity"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "fee_amount_0",
+            "type": "u64"
+          },
+          {
+            "name": "fee_amount_1",
+            "docs": [
+              "The amount of token_1 fee"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "reward_amounts",
+            "docs": [
+              "The amount of rewards"
+            ],
+            "type": {
+              "array": [
+                "u64",
+                3
+              ]
+            }
+          },
+          {
+            "name": "transfer_fee_0",
+            "docs": [
+              "The amount of token_0 transfer fee"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "transfer_fee_1",
+            "docs": [
+              "The amount of token_1 transfer fee"
+            ],
+            "type": "u64"
+          }
+        ]
+      }
+    },
+    {
+      "name": "IncreaseLiquidityEvent",
+      "docs": [
+        "Emitted when liquidity is increased."
+      ],
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "position_nft_mint",
+            "docs": [
+              "The ID of the token for which liquidity was increased"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "liquidity",
+            "docs": [
+              "The amount by which liquidity for the NFT position was increased"
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "amount_0",
+            "docs": [
+              "The amount of token_0 that was paid for the increase in liquidity"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "amount_1",
+            "docs": [
+              "The amount of token_1 that was paid for the increase in liquidity"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "amount_0_transfer_fee",
+            "docs": [
+              "The token transfer fee for amount_0"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "amount_1_transfer_fee",
+            "docs": [
+              "The token transfer fee for amount_1"
+            ],
+            "type": "u64"
+          }
+        ]
+      }
+    },
+    {
+      "name": "InitAdminGroupParams",
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "fee_keeper",
+            "docs": [
+              "the address who can hold the fee,",
+              "anyone can trigger the fee collection action,"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "reward_config_manager",
+            "docs": [
+              "the address who can config the reward(config, deposit, withdraw),",
+              "deposit reward, set the account who can deposit the reward, withdraw the remaining reward(withdraw)"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "reward_claim_manager",
+            "docs": [
+              "the address who can manage the offchain reward claim"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "pool_manager",
+            "docs": [
+              "the address who can manage the pool create action,",
+              "without this account's permission, no one can create a pool"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "emergency_manager",
+            "docs": [
+              "the address who can manage the emergency action,",
+              "emergency action includes stop/resume the pool, stop/resume withdraw lp"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "normal_manager",
+            "docs": [
+              "normal action manager,",
+              "such as create amm config, update amm config"
+            ],
+            "type": "pubkey"
+          }
+        ]
+      }
+    },
+    {
+      "name": "InitializeRewardParam",
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "open_time",
+            "docs": [
+              "Reward open time"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "end_time",
+            "docs": [
+              "Reward end time"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "emissions_per_second_x64",
+            "docs": [
+              "Token reward per second are earned per unit of liquidity"
+            ],
+            "type": "u128"
+          }
+        ]
+      }
+    },
+    {
+      "name": "LiquidityCalculateEvent",
+      "docs": [
+        "Emitted when liquidity decreased or increase."
+      ],
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "pool_liquidity",
+            "docs": [
+              "The pool liquidity before decrease or increase"
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "pool_sqrt_price_x64",
+            "docs": [
+              "The pool price when decrease or increase in liquidity"
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "pool_tick",
+            "docs": [
+              "The pool tick when decrease or increase in liquidity"
+            ],
+            "type": "i32"
+          },
+          {
+            "name": "calc_amount_0",
+            "docs": [
+              "The amount of token_0 that was calculated for the decrease or increase in liquidity"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "calc_amount_1",
+            "docs": [
+              "The amount of token_1 that was calculated for the decrease or increase in liquidity"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "trade_fee_owed_0",
+            "type": "u64"
+          },
+          {
+            "name": "trade_fee_owed_1",
+            "docs": [
+              "The amount of token_1 fee"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "transfer_fee_0",
+            "docs": [
+              "The amount of token_0 transfer fee without trade_fee_amount_0"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "transfer_fee_1",
+            "docs": [
+              "The amount of token_1 transfer fee without trade_fee_amount_0"
+            ],
+            "type": "u64"
+          }
+        ]
+      }
+    },
+    {
+      "name": "LiquidityChangeEvent",
+      "docs": [
+        "Emitted pool liquidity change when increase and decrease liquidity"
+      ],
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "pool_state",
+            "docs": [
+              "The pool for swap"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "tick",
+            "docs": [
+              "The tick of the pool"
+            ],
+            "type": "i32"
+          },
+          {
+            "name": "tick_lower",
+            "docs": [
+              "The tick lower of position"
+            ],
+            "type": "i32"
+          },
+          {
+            "name": "tick_upper",
+            "docs": [
+              "The tick lower of position"
+            ],
+            "type": "i32"
+          },
+          {
+            "name": "liquidity_before",
+            "docs": [
+              "The liquidity of the pool before liquidity change"
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "liquidity_after",
+            "docs": [
+              "The liquidity of the pool after liquidity change"
+            ],
+            "type": "u128"
+          }
+        ]
+      }
+    },
+    {
+      "name": "ModifyAmmAdminGroupEvent",
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "fee_keeper",
+            "type": "pubkey"
+          },
+          {
+            "name": "reward_config_manager",
+            "type": "pubkey"
+          },
+          {
+            "name": "reward_claim_manager",
+            "type": "pubkey"
+          },
+          {
+            "name": "pool_manager",
+            "type": "pubkey"
+          },
+          {
+            "name": "emergency_manager",
+            "type": "pubkey"
+          },
+          {
+            "name": "normal_manager",
+            "type": "pubkey"
+          }
+        ]
+      }
+    },
+    {
+      "name": "Observation",
+      "docs": [
+        "The element of observations in ObservationState"
+      ],
+      "serialization": "bytemuckunsafe",
+      "repr": {
+        "kind": "c",
+        "packed": true
+      },
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "block_timestamp",
+            "docs": [
+              "The block timestamp of the observation"
+            ],
+            "type": "u32"
+          },
+          {
+            "name": "tick_cumulative",
+            "docs": [
+              "the cumulative of tick during the duration time"
+            ],
+            "type": "i64"
+          },
+          {
+            "name": "padding",
+            "docs": [
+              "padding for feature update"
+            ],
+            "type": {
+              "array": [
+                "u64",
+                4
+              ]
+            }
+          }
+        ]
+      }
+    },
+    {
+      "name": "ObservationState",
+      "serialization": "bytemuckunsafe",
+      "repr": {
+        "kind": "c",
+        "packed": true
+      },
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "initialized",
+            "docs": [
+              "Whether the ObservationState is initialized"
+            ],
+            "type": "bool"
+          },
+          {
+            "name": "recent_epoch",
+            "docs": [
+              "recent update epoch"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "observation_index",
+            "docs": [
+              "the most-recently updated index of the observations array"
+            ],
+            "type": "u16"
+          },
+          {
+            "name": "pool_id",
+            "docs": [
+              "belongs to which pool"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "observations",
+            "docs": [
+              "observation array"
+            ],
+            "type": {
+              "array": [
+                {
+                  "defined": {
+                    "name": "Observation"
+                  }
+                },
+                100
+              ]
+            }
+          },
+          {
+            "name": "padding",
+            "docs": [
+              "padding for feature update"
+            ],
+            "type": {
+              "array": [
+                "u64",
+                4
+              ]
+            }
+          }
+        ]
+      }
+    },
+    {
+      "name": "OffchainRewardConfig",
+      "docs": [
+        "Holds the current owner of the factory"
+      ],
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "pool_id",
+            "docs": [
+              "the pool state address"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "reward_vault",
+            "docs": [
+              "the vault to hold the reward",
+              "vault address is this config address"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "vault_bump",
+            "docs": [
+              "Bump to identify vault PDA"
+            ],
+            "type": {
+              "array": [
+                "u8",
+                1
+              ]
+            }
+          },
+          {
+            "name": "reward_mint_vec",
+            "docs": [
+              "reward token mint address list",
+              "reward token account is ATA(reward_vault, reward_mint)"
+            ],
+            "type": {
+              "vec": "pubkey"
+            }
+          }
+        ]
+      }
+    },
+    {
+      "name": "OperationState",
+      "docs": [
+        "Holds the current owner of the factory"
+      ],
+      "serialization": "bytemuckunsafe",
+      "repr": {
+        "kind": "c",
+        "packed": true
+      },
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "bump",
+            "docs": [
+              "Bump to identify PDA"
+            ],
+            "type": "u8"
+          },
+          {
+            "name": "operation_owners",
+            "docs": [
+              "Address of the operation owner"
+            ],
+            "type": {
+              "array": [
+                "pubkey",
+                10
+              ]
+            }
+          },
+          {
+            "name": "whitelist_mints",
+            "docs": [
+              "The mint address of whitelist to emit reward"
+            ],
+            "type": {
+              "array": [
+                "pubkey",
+                100
+              ]
+            }
+          }
+        ]
+      }
+    },
+    {
+      "name": "PersonalPositionState",
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "bump",
+            "docs": [
+              "Bump to identify PDA"
+            ],
+            "type": {
+              "array": [
+                "u8",
+                1
+              ]
+            }
+          },
+          {
+            "name": "nft_mint",
+            "docs": [
+              "Mint address of the tokenized position"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "pool_id",
+            "docs": [
+              "The ID of the pool with which this token is connected"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "tick_lower_index",
+            "docs": [
+              "The lower bound tick of the position"
+            ],
+            "type": "i32"
+          },
+          {
+            "name": "tick_upper_index",
+            "docs": [
+              "The upper bound tick of the position"
+            ],
+            "type": "i32"
+          },
+          {
+            "name": "liquidity",
+            "docs": [
+              "The amount of liquidity owned by this position"
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "fee_growth_inside_0_last_x64",
+            "docs": [
+              "The token_0 fee growth of the aggregate position as of the last action on the individual position"
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "fee_growth_inside_1_last_x64",
+            "docs": [
+              "The token_1 fee growth of the aggregate position as of the last action on the individual position"
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "token_fees_owed_0",
+            "docs": [
+              "The fees owed to the position owner in token_0, as of the last computation"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "token_fees_owed_1",
+            "docs": [
+              "The fees owed to the position owner in token_1, as of the last computation"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "reward_infos",
+            "type": {
+              "array": [
+                {
+                  "defined": {
+                    "name": "PositionRewardInfo"
+                  }
+                },
+                3
+              ]
+            }
+          },
+          {
+            "name": "recent_epoch",
+            "type": "u64"
+          },
+          {
+            "name": "padding",
+            "type": {
+              "array": [
+                "u64",
+                7
+              ]
+            }
+          }
+        ]
+      }
+    },
+    {
+      "name": "PoolCreatedEvent",
+      "docs": [
+        "Emitted when a pool is created and initialized with a starting price",
+        ""
+      ],
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "token_mint_0",
+            "docs": [
+              "The first token of the pool by address sort order"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "token_mint_1",
+            "docs": [
+              "The second token of the pool by address sort order"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "tick_spacing",
+            "docs": [
+              "The minimum number of ticks between initialized ticks"
+            ],
+            "type": "u16"
+          },
+          {
+            "name": "pool_state",
+            "docs": [
+              "The address of the created pool"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "sqrt_price_x64",
+            "docs": [
+              "The initial sqrt price of the pool, as a Q64.64"
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "tick",
+            "docs": [
+              "The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool"
+            ],
+            "type": "i32"
+          },
+          {
+            "name": "token_vault_0",
+            "docs": [
+              "Vault of token_0"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "token_vault_1",
+            "docs": [
+              "Vault of token_1"
+            ],
+            "type": "pubkey"
+          }
+        ]
+      }
+    },
+    {
+      "name": "PoolState",
+      "docs": [
+        "The pool state",
+        "",
+        "PDA of `[POOL_SEED, config, token_mint_0, token_mint_1]`",
+        ""
+      ],
+      "serialization": "bytemuckunsafe",
+      "repr": {
+        "kind": "c",
+        "packed": true
+      },
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "bump",
+            "docs": [
+              "Bump to identify PDA"
+            ],
+            "type": {
+              "array": [
+                "u8",
+                1
+              ]
+            }
+          },
+          {
+            "name": "amm_config",
+            "type": "pubkey"
+          },
+          {
+            "name": "owner",
+            "type": "pubkey"
+          },
+          {
+            "name": "token_mint_0",
+            "docs": [
+              "Token pair of the pool, where token_mint_0 address < token_mint_1 address"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "token_mint_1",
+            "type": "pubkey"
+          },
+          {
+            "name": "token_vault_0",
+            "docs": [
+              "Token pair vault"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "token_vault_1",
+            "type": "pubkey"
+          },
+          {
+            "name": "observation_key",
+            "docs": [
+              "observation account key"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "mint_decimals_0",
+            "docs": [
+              "mint0 and mint1 decimals"
+            ],
+            "type": "u8"
+          },
+          {
+            "name": "mint_decimals_1",
+            "type": "u8"
+          },
+          {
+            "name": "tick_spacing",
+            "docs": [
+              "The minimum number of ticks between initialized ticks"
+            ],
+            "type": "u16"
+          },
+          {
+            "name": "liquidity",
+            "docs": [
+              "The currently in range liquidity available to the pool."
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "sqrt_price_x64",
+            "docs": [
+              "The current price of the pool as a sqrt(token_1/token_0) Q64.64 value"
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "tick_current",
+            "docs": [
+              "The current tick of the pool, i.e. according to the last tick transition that was run."
+            ],
+            "type": "i32"
+          },
+          {
+            "name": "padding3",
+            "type": "u16"
+          },
+          {
+            "name": "padding4",
+            "type": "u16"
+          },
+          {
+            "name": "fee_growth_global_0_x64",
+            "docs": [
+              "The fee growth as a Q64.64 number, i.e. fees of token_0 and token_1 collected per",
+              "unit of liquidity for the entire life of the pool."
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "fee_growth_global_1_x64",
+            "type": "u128"
+          },
+          {
+            "name": "protocol_fees_token_0",
+            "docs": [
+              "The amounts of token_0 and token_1 that are owed to the protocol."
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "protocol_fees_token_1",
+            "type": "u64"
+          },
+          {
+            "name": "swap_in_amount_token_0",
+            "docs": [
+              "The amounts in and out of swap token_0 and token_1"
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "swap_out_amount_token_1",
+            "type": "u128"
+          },
+          {
+            "name": "swap_in_amount_token_1",
+            "type": "u128"
+          },
+          {
+            "name": "swap_out_amount_token_0",
+            "type": "u128"
+          },
+          {
+            "name": "status",
+            "docs": [
+              "Bitwise representation of the state of the pool",
+              "bit0, 1: disable open position and increase liquidity, 0: normal",
+              "bit1, 1: disable decrease liquidity, 0: normal",
+              "bit2, 1: disable collect fee, 0: normal",
+              "bit3, 1: disable collect reward, 0: normal",
+              "bit4, 1: disable swap, 0: normal"
+            ],
+            "type": "u8"
+          },
+          {
+            "name": "padding",
+            "docs": [
+              "Leave blank for future use"
+            ],
+            "type": {
+              "array": [
+                "u8",
+                7
+              ]
+            }
+          },
+          {
+            "name": "reward_infos",
+            "type": {
+              "array": [
+                {
+                  "defined": {
+                    "name": "RewardInfo"
+                  }
+                },
+                3
+              ]
+            }
+          },
+          {
+            "name": "tick_array_bitmap",
+            "docs": [
+              "Packed initialized tick array state"
+            ],
+            "type": {
+              "array": [
+                "u64",
+                16
+              ]
+            }
+          },
+          {
+            "name": "total_fees_token_0",
+            "docs": [
+              "except protocol_fee and fund_fee"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "total_fees_claimed_token_0",
+            "docs": [
+              "except protocol_fee and fund_fee"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "total_fees_token_1",
+            "type": "u64"
+          },
+          {
+            "name": "total_fees_claimed_token_1",
+            "type": "u64"
+          },
+          {
+            "name": "fund_fees_token_0",
+            "type": "u64"
+          },
+          {
+            "name": "fund_fees_token_1",
+            "type": "u64"
+          },
+          {
+            "name": "open_time",
+            "type": "u64"
+          },
+          {
+            "name": "recent_epoch",
+            "type": "u64"
+          },
+          {
+            "name": "padding1",
+            "type": {
+              "array": [
+                "u64",
+                24
+              ]
+            }
+          },
+          {
+            "name": "padding2",
+            "type": {
+              "array": [
+                "u64",
+                32
+              ]
+            }
+          }
+        ]
+      }
+    },
+    {
+      "name": "PositionRewardInfo",
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "growth_inside_last_x64",
+            "type": "u128"
+          },
+          {
+            "name": "reward_amount_owed",
+            "type": "u64"
+          }
+        ]
+      }
+    },
+    {
+      "name": "ProtocolPositionState",
+      "docs": [
+        "Info stored for each user's position"
+      ],
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "bump",
+            "docs": [
+              "Bump to identify PDA"
+            ],
+            "type": "u8"
+          },
+          {
+            "name": "pool_id",
+            "docs": [
+              "The ID of the pool with which this token is connected"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "tick_lower_index",
+            "docs": [
+              "The lower bound tick of the position"
+            ],
+            "type": "i32"
+          },
+          {
+            "name": "tick_upper_index",
+            "docs": [
+              "The upper bound tick of the position"
+            ],
+            "type": "i32"
+          },
+          {
+            "name": "liquidity",
+            "docs": [
+              "The amount of liquidity owned by this position"
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "fee_growth_inside_0_last_x64",
+            "docs": [
+              "The token_0 fee growth per unit of liquidity as of the last update to liquidity or fees owed"
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "fee_growth_inside_1_last_x64",
+            "docs": [
+              "The token_1 fee growth per unit of liquidity as of the last update to liquidity or fees owed"
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "token_fees_owed_0",
+            "docs": [
+              "The fees owed to the position owner in token_0"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "token_fees_owed_1",
+            "docs": [
+              "The fees owed to the position owner in token_1"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "reward_growth_inside",
+            "docs": [
+              "The reward growth per unit of liquidity as of the last update to liquidity"
+            ],
+            "type": {
+              "array": [
+                "u128",
+                3
+              ]
+            }
+          },
+          {
+            "name": "recent_epoch",
+            "type": "u64"
+          },
+          {
+            "name": "padding",
+            "type": {
+              "array": [
+                "u64",
+                7
+              ]
+            }
+          }
+        ]
+      }
+    },
+    {
+      "name": "RewardInfo",
+      "serialization": "bytemuckunsafe",
+      "repr": {
+        "kind": "c",
+        "packed": true
+      },
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "reward_state",
+            "docs": [
+              "Reward state"
+            ],
+            "type": "u8"
+          },
+          {
+            "name": "open_time",
+            "docs": [
+              "Reward open time"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "end_time",
+            "docs": [
+              "Reward end time"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "last_update_time",
+            "docs": [
+              "Reward last update time"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "emissions_per_second_x64",
+            "docs": [
+              "Q64.64 number indicates how many tokens per second are earned per unit of liquidity."
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "reward_total_emissioned",
+            "docs": [
+              "The total amount of reward emissioned"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "reward_claimed",
+            "docs": [
+              "The total amount of claimed reward"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "token_mint",
+            "docs": [
+              "Reward token mint."
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "token_vault",
+            "docs": [
+              "Reward vault token account."
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "authority",
+            "docs": [
+              "The owner that has permission to set reward param"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "reward_growth_global_x64",
+            "docs": [
+              "Q64.64 number that tracks the total tokens earned per unit of liquidity since the reward",
+              "emissions were turned on."
+            ],
+            "type": "u128"
+          }
+        ]
+      }
+    },
+    {
+      "name": "SupportMintAssociated",
+      "docs": [
+        "Holds the current owner of the factory"
+      ],
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "bump",
+            "docs": [
+              "Bump to identify PDA"
+            ],
+            "type": "u8"
+          },
+          {
+            "name": "mint",
+            "docs": [
+              "Address of the supported token22 mint"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "padding",
+            "type": {
+              "array": [
+                "u64",
+                8
+              ]
+            }
+          }
+        ]
+      }
+    },
+    {
+      "name": "SwapEvent",
+      "docs": [
+        "Emitted by when a swap is performed for a pool"
+      ],
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "pool_state",
+            "docs": [
+              "The pool for which token_0 and token_1 were swapped"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "sender",
+            "docs": [
+              "The address that initiated the swap call, and that received the callback"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "token_account_0",
+            "docs": [
+              "The payer token account in zero for one swaps, or the recipient token account",
+              "in one for zero swaps"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "token_account_1",
+            "docs": [
+              "The payer token account in one for zero swaps, or the recipient token account",
+              "in zero for one swaps"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "amount_0",
+            "docs": [
+              "The real delta amount of the token_0 of the pool or user"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "transfer_fee_0",
+            "docs": [
+              "The transfer fee charged by the withheld_amount of the token_0"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "amount_1",
+            "docs": [
+              "The real delta of the token_1 of the pool or user"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "transfer_fee_1",
+            "docs": [
+              "The transfer fee charged by the withheld_amount of the token_1"
+            ],
+            "type": "u64"
+          },
+          {
+            "name": "zero_for_one",
+            "docs": [
+              "if true, amount_0 is negtive and amount_1 is positive"
+            ],
+            "type": "bool"
+          },
+          {
+            "name": "sqrt_price_x64",
+            "docs": [
+              "The sqrt(price) of the pool after the swap, as a Q64.64"
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "liquidity",
+            "docs": [
+              "The liquidity of the pool after the swap"
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "tick",
+            "docs": [
+              "The log base 1.0001 of price of the pool after the swap"
+            ],
+            "type": "i32"
+          }
+        ]
+      }
+    },
+    {
+      "name": "TickArrayBitmapExtension",
+      "serialization": "bytemuckunsafe",
+      "repr": {
+        "kind": "c",
+        "packed": true
+      },
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "pool_id",
+            "type": "pubkey"
+          },
+          {
+            "name": "positive_tick_array_bitmap",
+            "docs": [
+              "Packed initialized tick array state for start_tick_index is positive"
+            ],
+            "type": {
+              "array": [
+                {
+                  "array": [
+                    "u64",
+                    8
+                  ]
+                },
+                14
+              ]
+            }
+          },
+          {
+            "name": "negative_tick_array_bitmap",
+            "docs": [
+              "Packed initialized tick array state for start_tick_index is negitive"
+            ],
+            "type": {
+              "array": [
+                {
+                  "array": [
+                    "u64",
+                    8
+                  ]
+                },
+                14
+              ]
+            }
+          }
+        ]
+      }
+    },
+    {
+      "name": "TickArrayState",
+      "serialization": "bytemuckunsafe",
+      "repr": {
+        "kind": "c",
+        "packed": true
+      },
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "pool_id",
+            "type": "pubkey"
+          },
+          {
+            "name": "start_tick_index",
+            "type": "i32"
+          },
+          {
+            "name": "ticks",
+            "type": {
+              "array": [
+                {
+                  "defined": {
+                    "name": "TickState"
+                  }
+                },
+                60
+              ]
+            }
+          },
+          {
+            "name": "initialized_tick_count",
+            "type": "u8"
+          },
+          {
+            "name": "recent_epoch",
+            "type": "u64"
+          },
+          {
+            "name": "padding",
+            "type": {
+              "array": [
+                "u8",
+                107
+              ]
+            }
+          }
+        ]
+      }
+    },
+    {
+      "name": "TickState",
+      "serialization": "bytemuckunsafe",
+      "repr": {
+        "kind": "c",
+        "packed": true
+      },
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "tick",
+            "type": "i32"
+          },
+          {
+            "name": "liquidity_net",
+            "docs": [
+              "Amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left)"
+            ],
+            "type": "i128"
+          },
+          {
+            "name": "liquidity_gross",
+            "docs": [
+              "The total position liquidity that references this tick"
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "fee_growth_outside_0_x64",
+            "docs": [
+              "Fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)",
+              "only has relative meaning, not absolute — the value depends on when the tick is initialized"
+            ],
+            "type": "u128"
+          },
+          {
+            "name": "fee_growth_outside_1_x64",
+            "type": "u128"
+          },
+          {
+            "name": "reward_growths_outside_x64",
+            "type": {
+              "array": [
+                "u128",
+                3
+              ]
+            }
+          },
+          {
+            "name": "padding",
+            "type": {
+              "array": [
+                "u32",
+                13
+              ]
+            }
+          }
+        ]
+      }
+    },
+    {
+      "name": "UpdateAdminGroupParams",
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "fee_keeper",
+            "docs": [
+              "the address who can hold the fee,",
+              "anyone can trigger the fee collection action,"
+            ],
+            "type": {
+              "option": "pubkey"
+            }
+          },
+          {
+            "name": "reward_config_manager",
+            "docs": [
+              "the address who can config the reward(config, deposit, withdraw),",
+              "deposit reward, set the account who can deposit the reward, withdraw the remaining reward(withdraw)"
+            ],
+            "type": {
+              "option": "pubkey"
+            }
+          },
+          {
+            "name": "reward_claim_manager",
+            "docs": [
+              "the address who can manage the offchain reward claim"
+            ],
+            "type": {
+              "option": "pubkey"
+            }
+          },
+          {
+            "name": "pool_manager",
+            "docs": [
+              "the address who can manage the pool create action,",
+              "without this account's permission, no one can create a pool"
+            ],
+            "type": {
+              "option": "pubkey"
+            }
+          },
+          {
+            "name": "emergency_manager",
+            "docs": [
+              "the address who can manage the emergency action,",
+              "emergency action includes stop/resume the pool, stop/resume withdraw lp"
+            ],
+            "type": {
+              "option": "pubkey"
+            }
+          },
+          {
+            "name": "normal_manager",
+            "docs": [
+              "normal action manager,",
+              "such as create amm config, update amm config"
+            ],
+            "type": {
+              "option": "pubkey"
+            }
+          }
+        ]
+      }
+    },
+    {
+      "name": "UpdateRewardInfosEvent",
+      "docs": [
+        "Emitted when Reward are updated for a pool"
+      ],
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "reward_growth_global_x64",
+            "docs": [
+              "Reward info"
+            ],
+            "type": {
+              "array": [
+                "u128",
+                3
+              ]
+            }
+          }
+        ]
+      }
+    }
+  ]
+}

+ 5328 - 0
src/lib/byreal-clmm-sdk/src/instructions/target/types/byreal_amm_v3.ts

@@ -0,0 +1,5328 @@
+/**
+ * Program IDL in camelCase format in order to be used in JS/TS.
+ *
+ * Note that this is only a type helper and is not the actual IDL. The original
+ * IDL can be found at `target/idl/byreal_clmm.json`.
+ */
+export type ByrealClmm = {
+  address: 'REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2';
+  metadata: {
+    name: 'byrealClmm';
+    version: '0.1.0';
+    spec: '0.1.0';
+    description: 'Anchor client and source for Byreal concentrated liquidity AMM';
+  };
+  instructions: [
+    {
+      name: 'claimOffchainReward';
+      docs: ['claim offchain reward from the pool'];
+      discriminator: [195, 87, 221, 149, 141, 195, 146, 19];
+      accounts: [
+        {
+          name: 'claimer';
+          docs: ['the address who claim the offchain reward.'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'authority';
+          docs: ['The authority make decision that who can claim the offchain reward.'];
+          signer: true;
+        },
+        {
+          name: 'adminGroup';
+          docs: ['Initialize amm admin group account to store admin permissions.'];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [97, 100, 109, 105, 110, 95, 103, 114, 111, 117, 112];
+              }
+            ];
+          };
+        },
+        {
+          name: 'poolId';
+          docs: ['the pool id, which is the pool state account.'];
+          relations: ['rewardConfig'];
+        },
+        {
+          name: 'tokenMint';
+        },
+        {
+          name: 'claimerTokenAccount';
+          docs: [''];
+          writable: true;
+        },
+        {
+          name: 'rewardVaultTokenAccount';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'account';
+                path: 'rewardConfig';
+              },
+              {
+                kind: 'account';
+                path: 'tokenProgram';
+              },
+              {
+                kind: 'account';
+                path: 'tokenMint';
+              }
+            ];
+            program: {
+              kind: 'const';
+              value: [
+                140,
+                151,
+                37,
+                143,
+                78,
+                36,
+                137,
+                241,
+                187,
+                61,
+                16,
+                41,
+                20,
+                142,
+                13,
+                131,
+                11,
+                90,
+                19,
+                153,
+                218,
+                255,
+                16,
+                132,
+                4,
+                142,
+                123,
+                216,
+                219,
+                233,
+                248,
+                89
+              ];
+            };
+          };
+        },
+        {
+          name: 'rewardConfig';
+          docs: ['The offchain reward config account, it also is the reward vault account.'];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [111, 102, 102, 99, 104, 97, 105, 110, 95, 114, 101, 119, 97, 114, 100];
+              },
+              {
+                kind: 'account';
+                path: 'poolId';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['Spl token program or token program 2022'];
+        },
+        {
+          name: 'associatedTokenProgram';
+          address: 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL';
+        }
+      ];
+      args: [
+        {
+          name: 'amount';
+          type: 'u64';
+        }
+      ];
+    },
+    {
+      name: 'closePosition';
+      docs: [
+        "Close the user's position and NFT account. If the NFT mint belongs to token2022, it will also be closed and the funds returned to the NFT owner.",
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        ''
+      ];
+      discriminator: [123, 134, 81, 0, 49, 68, 98, 98];
+      accounts: [
+        {
+          name: 'nftOwner';
+          docs: ['The position nft owner'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'positionNftMint';
+          docs: ['Mint address bound to the personal position.'];
+          writable: true;
+        },
+        {
+          name: 'positionNftAccount';
+          docs: ['User token account where position NFT be minted to'];
+          writable: true;
+        },
+        {
+          name: 'personalPosition';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'positionNftMint';
+              }
+            ];
+          };
+        },
+        {
+          name: 'systemProgram';
+          docs: ['System program to close the position state account'];
+          address: '11111111111111111111111111111111';
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['Token/Token2022 program to close token/mint account'];
+        }
+      ];
+      args: [];
+    },
+    {
+      name: 'collectFundFee';
+      docs: [
+        'Collect the fund fee accrued to the pool',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        '* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1',
+        '* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0',
+        ''
+      ];
+      discriminator: [167, 138, 78, 149, 223, 194, 6, 126];
+      accounts: [
+        {
+          name: 'adminGroup';
+          docs: [
+            'amm admin group account to store admin permissions.',
+            'anyone can collect fee, but only fee-manager in admin group can receive fee'
+          ];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [97, 100, 109, 105, 110, 95, 103, 114, 111, 117, 112];
+              }
+            ];
+          };
+        },
+        {
+          name: 'poolState';
+          docs: ['Pool state stores accumulated protocol fee amount'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault0';
+          docs: ['The address that holds pool tokens for token_0'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault1';
+          docs: ['The address that holds pool tokens for token_1'];
+          writable: true;
+        },
+        {
+          name: 'vault0Mint';
+          docs: ['The mint of token vault 0'];
+        },
+        {
+          name: 'vault1Mint';
+          docs: ['The mint of token vault 1'];
+        },
+        {
+          name: 'recipientTokenAccount0';
+          docs: ['The address that receives the collected token_0 protocol fees'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'account';
+                path: 'admin_group.fee_keeper';
+                account: 'ammAdminGroup';
+              },
+              {
+                kind: 'const';
+                value: [
+                  6,
+                  221,
+                  246,
+                  225,
+                  215,
+                  101,
+                  161,
+                  147,
+                  217,
+                  203,
+                  225,
+                  70,
+                  206,
+                  235,
+                  121,
+                  172,
+                  28,
+                  180,
+                  133,
+                  237,
+                  95,
+                  91,
+                  55,
+                  145,
+                  58,
+                  140,
+                  245,
+                  133,
+                  126,
+                  255,
+                  0,
+                  169
+                ];
+              },
+              {
+                kind: 'account';
+                path: 'vault0Mint';
+              }
+            ];
+            program: {
+              kind: 'const';
+              value: [
+                140,
+                151,
+                37,
+                143,
+                78,
+                36,
+                137,
+                241,
+                187,
+                61,
+                16,
+                41,
+                20,
+                142,
+                13,
+                131,
+                11,
+                90,
+                19,
+                153,
+                218,
+                255,
+                16,
+                132,
+                4,
+                142,
+                123,
+                216,
+                219,
+                233,
+                248,
+                89
+              ];
+            };
+          };
+        },
+        {
+          name: 'recipientTokenAccount1';
+          docs: ['The address that receives the collected token_1 protocol fees'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'account';
+                path: 'admin_group.fee_keeper';
+                account: 'ammAdminGroup';
+              },
+              {
+                kind: 'const';
+                value: [
+                  6,
+                  221,
+                  246,
+                  225,
+                  215,
+                  101,
+                  161,
+                  147,
+                  217,
+                  203,
+                  225,
+                  70,
+                  206,
+                  235,
+                  121,
+                  172,
+                  28,
+                  180,
+                  133,
+                  237,
+                  95,
+                  91,
+                  55,
+                  145,
+                  58,
+                  140,
+                  245,
+                  133,
+                  126,
+                  255,
+                  0,
+                  169
+                ];
+              },
+              {
+                kind: 'account';
+                path: 'vault1Mint';
+              }
+            ];
+            program: {
+              kind: 'const';
+              value: [
+                140,
+                151,
+                37,
+                143,
+                78,
+                36,
+                137,
+                241,
+                187,
+                61,
+                16,
+                41,
+                20,
+                142,
+                13,
+                131,
+                11,
+                90,
+                19,
+                153,
+                218,
+                255,
+                16,
+                132,
+                4,
+                142,
+                123,
+                216,
+                219,
+                233,
+                248,
+                89
+              ];
+            };
+          };
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['The SPL program to perform token transfers'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'tokenProgram2022';
+          docs: ['The SPL program 2022 to perform token transfers'];
+          address: 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
+        },
+        {
+          name: 'associatedTokenProgram';
+          address: 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL';
+        }
+      ];
+      args: [
+        {
+          name: 'amount0Requested';
+          type: 'u64';
+        },
+        {
+          name: 'amount1Requested';
+          type: 'u64';
+        }
+      ];
+    },
+    {
+      name: 'collectProtocolFee';
+      docs: [
+        'Collect the protocol fee accrued to the pool',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        '* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1',
+        '* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0',
+        ''
+      ];
+      discriminator: [136, 136, 252, 221, 194, 66, 126, 89];
+      accounts: [
+        {
+          name: 'adminGroup';
+          docs: [
+            'amm admin group account to store admin permissions.',
+            'anyone can collect fee, but only fee-manager in admin group can receive fee'
+          ];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [97, 100, 109, 105, 110, 95, 103, 114, 111, 117, 112];
+              }
+            ];
+          };
+        },
+        {
+          name: 'poolState';
+          docs: ['Pool state stores accumulated protocol fee amount'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault0';
+          docs: ['The address that holds pool tokens for token_0'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault1';
+          docs: ['The address that holds pool tokens for token_1'];
+          writable: true;
+        },
+        {
+          name: 'vault0Mint';
+          docs: ['The mint of token vault 0'];
+        },
+        {
+          name: 'vault1Mint';
+          docs: ['The mint of token vault 1'];
+        },
+        {
+          name: 'recipientTokenAccount0';
+          docs: ['The address that receives the collected token_0 protocol fees'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'account';
+                path: 'admin_group.fee_keeper';
+                account: 'ammAdminGroup';
+              },
+              {
+                kind: 'const';
+                value: [
+                  6,
+                  221,
+                  246,
+                  225,
+                  215,
+                  101,
+                  161,
+                  147,
+                  217,
+                  203,
+                  225,
+                  70,
+                  206,
+                  235,
+                  121,
+                  172,
+                  28,
+                  180,
+                  133,
+                  237,
+                  95,
+                  91,
+                  55,
+                  145,
+                  58,
+                  140,
+                  245,
+                  133,
+                  126,
+                  255,
+                  0,
+                  169
+                ];
+              },
+              {
+                kind: 'account';
+                path: 'vault0Mint';
+              }
+            ];
+            program: {
+              kind: 'const';
+              value: [
+                140,
+                151,
+                37,
+                143,
+                78,
+                36,
+                137,
+                241,
+                187,
+                61,
+                16,
+                41,
+                20,
+                142,
+                13,
+                131,
+                11,
+                90,
+                19,
+                153,
+                218,
+                255,
+                16,
+                132,
+                4,
+                142,
+                123,
+                216,
+                219,
+                233,
+                248,
+                89
+              ];
+            };
+          };
+        },
+        {
+          name: 'recipientTokenAccount1';
+          docs: ['The address that receives the collected token_1 protocol fees'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'account';
+                path: 'admin_group.fee_keeper';
+                account: 'ammAdminGroup';
+              },
+              {
+                kind: 'const';
+                value: [
+                  6,
+                  221,
+                  246,
+                  225,
+                  215,
+                  101,
+                  161,
+                  147,
+                  217,
+                  203,
+                  225,
+                  70,
+                  206,
+                  235,
+                  121,
+                  172,
+                  28,
+                  180,
+                  133,
+                  237,
+                  95,
+                  91,
+                  55,
+                  145,
+                  58,
+                  140,
+                  245,
+                  133,
+                  126,
+                  255,
+                  0,
+                  169
+                ];
+              },
+              {
+                kind: 'account';
+                path: 'vault1Mint';
+              }
+            ];
+            program: {
+              kind: 'const';
+              value: [
+                140,
+                151,
+                37,
+                143,
+                78,
+                36,
+                137,
+                241,
+                187,
+                61,
+                16,
+                41,
+                20,
+                142,
+                13,
+                131,
+                11,
+                90,
+                19,
+                153,
+                218,
+                255,
+                16,
+                132,
+                4,
+                142,
+                123,
+                216,
+                219,
+                233,
+                248,
+                89
+              ];
+            };
+          };
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['The SPL program to perform token transfers'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'tokenProgram2022';
+          docs: ['The SPL program 2022 to perform token transfers'];
+          address: 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
+        },
+        {
+          name: 'associatedTokenProgram';
+          address: 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL';
+        }
+      ];
+      args: [
+        {
+          name: 'amount0Requested';
+          type: 'u64';
+        },
+        {
+          name: 'amount1Requested';
+          type: 'u64';
+        }
+      ];
+    },
+    {
+      name: 'collectRemainingRewards';
+      docs: [
+        'Collect remaining reward token for reward founder',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx`- The context of accounts',
+        '* `reward_index` - the index to reward info',
+        ''
+      ];
+      discriminator: [18, 237, 166, 197, 34, 16, 213, 144];
+      accounts: [
+        {
+          name: 'rewardFunder';
+          docs: ['The founder who init reward info previously'];
+          signer: true;
+        },
+        {
+          name: 'adminGroup';
+          docs: ['amm admin group account to store admin permissions.'];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [97, 100, 109, 105, 110, 95, 103, 114, 111, 117, 112];
+              }
+            ];
+          };
+        },
+        {
+          name: 'funderTokenAccount';
+          docs: ["The funder's reward token account"];
+          writable: true;
+        },
+        {
+          name: 'poolState';
+          docs: ['Set reward for this pool'];
+          writable: true;
+        },
+        {
+          name: 'rewardTokenVault';
+          docs: ['Reward vault transfer remaining token to founder token account'];
+        },
+        {
+          name: 'rewardVaultMint';
+          docs: ['The mint of reward token vault'];
+        },
+        {
+          name: 'tokenProgram';
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'tokenProgram2022';
+          docs: ['Token program 2022'];
+          address: 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
+        },
+        {
+          name: 'memoProgram';
+          docs: ['memo program'];
+          address: 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr';
+        }
+      ];
+      args: [
+        {
+          name: 'rewardIndex';
+          type: 'u8';
+        }
+      ];
+    },
+    {
+      name: 'createAmmConfig';
+      docs: [
+        '# Arguments',
+        '',
+        '* `ctx`- The accounts needed by instruction.',
+        '* `index` - The index of amm config, there may be multiple config.',
+        '* `tick_spacing` - The tickspacing binding with config, cannot be changed.',
+        '* `trade_fee_rate` - Trade fee rate, can be changed.',
+        '* `protocol_fee_rate` - The rate of protocol fee within trade fee.',
+        '* `fund_fee_rate` - The rate of fund fee within trade fee.',
+        ''
+      ];
+      discriminator: [137, 52, 237, 212, 215, 117, 108, 104];
+      accounts: [
+        {
+          name: 'owner';
+          docs: ['Address to be set as normal manager in admin group.'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'adminGroup';
+          docs: ['amm admin group account to store admin permissions.'];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [97, 100, 109, 105, 110, 95, 103, 114, 111, 117, 112];
+              }
+            ];
+          };
+        },
+        {
+          name: 'ammConfig';
+          docs: ['Initialize config state account to store protocol owner address and fee rates.'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [97, 109, 109, 95, 99, 111, 110, 102, 105, 103];
+              },
+              {
+                kind: 'arg';
+                path: 'index';
+              }
+            ];
+          };
+        },
+        {
+          name: 'systemProgram';
+          address: '11111111111111111111111111111111';
+        }
+      ];
+      args: [
+        {
+          name: 'index';
+          type: 'u16';
+        },
+        {
+          name: 'tickSpacing';
+          type: 'u16';
+        },
+        {
+          name: 'tradeFeeRate';
+          type: 'u32';
+        },
+        {
+          name: 'protocolFeeRate';
+          type: 'u32';
+        },
+        {
+          name: 'fundFeeRate';
+          type: 'u32';
+        }
+      ];
+    },
+    {
+      name: 'createOperationAccount';
+      docs: [
+        'Creates an operation account for the program',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx`- The context of accounts',
+        ''
+      ];
+      discriminator: [63, 87, 148, 33, 109, 35, 8, 104];
+      accounts: [
+        {
+          name: 'owner';
+          docs: ['Address to be set as operation account owner.'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'adminGroup';
+          docs: ['amm admin group account to store admin permissions.'];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [97, 100, 109, 105, 110, 95, 103, 114, 111, 117, 112];
+              }
+            ];
+          };
+        },
+        {
+          name: 'operationState';
+          docs: ['Initialize operation state account to store operation owner address and white list mint.'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [111, 112, 101, 114, 97, 116, 105, 111, 110];
+              }
+            ];
+          };
+        },
+        {
+          name: 'systemProgram';
+          address: '11111111111111111111111111111111';
+        }
+      ];
+      args: [];
+    },
+    {
+      name: 'createPool';
+      docs: [
+        'Creates a pool for the given token pair and the initial price',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx`- The context of accounts',
+        '* `sqrt_price_x64` - the initial sqrt price (amount_token_1 / amount_token_0) of the pool as a Q64.64',
+        'Note: The open_time must be smaller than the current block_timestamp on chain.'
+      ];
+      discriminator: [233, 146, 209, 142, 207, 104, 64, 188];
+      accounts: [
+        {
+          name: 'poolCreator';
+          docs: ['Address paying to create the pool. Can be anyone'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'poolManager';
+          docs: ['with pool_manager permission, the pool creator can create a pool.'];
+          signer: true;
+        },
+        {
+          name: 'adminGroup';
+          docs: ['amm admin group account to store admin permissions.'];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [97, 100, 109, 105, 110, 95, 103, 114, 111, 117, 112];
+              }
+            ];
+          };
+        },
+        {
+          name: 'ammConfig';
+          docs: ['Which config the pool belongs to.'];
+        },
+        {
+          name: 'poolState';
+          docs: ['Initialize an account to store the pool state'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 111, 108];
+              },
+              {
+                kind: 'account';
+                path: 'ammConfig';
+              },
+              {
+                kind: 'account';
+                path: 'tokenMint0';
+              },
+              {
+                kind: 'account';
+                path: 'tokenMint1';
+              }
+            ];
+          };
+        },
+        {
+          name: 'offchainRewardConfig';
+          docs: ['Initialize an account to store the off-chain reward config'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [111, 102, 102, 99, 104, 97, 105, 110, 95, 114, 101, 119, 97, 114, 100];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenMint0';
+          docs: ['Token_0 mint, the key must be smaller then token_1 mint.'];
+        },
+        {
+          name: 'tokenMint1';
+          docs: ['Token_1 mint'];
+        },
+        {
+          name: 'tokenVault0';
+          docs: ['Token_0 vault for the pool'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 111, 108, 95, 118, 97, 117, 108, 116];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'account';
+                path: 'tokenMint0';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenVault1';
+          docs: ['Token_1 vault for the pool'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 111, 108, 95, 118, 97, 117, 108, 116];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'account';
+                path: 'tokenMint1';
+              }
+            ];
+          };
+        },
+        {
+          name: 'observationState';
+          docs: ['Initialize an account to store oracle observations'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [111, 98, 115, 101, 114, 118, 97, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tickArrayBitmap';
+          docs: ['Initialize an account to store if a tick array is initialized.'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [
+                  112,
+                  111,
+                  111,
+                  108,
+                  95,
+                  116,
+                  105,
+                  99,
+                  107,
+                  95,
+                  97,
+                  114,
+                  114,
+                  97,
+                  121,
+                  95,
+                  98,
+                  105,
+                  116,
+                  109,
+                  97,
+                  112,
+                  95,
+                  101,
+                  120,
+                  116,
+                  101,
+                  110,
+                  115,
+                  105,
+                  111,
+                  110
+                ];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenProgram0';
+          docs: ['Spl token program or token program 2022'];
+        },
+        {
+          name: 'tokenProgram1';
+          docs: ['Spl token program or token program 2022'];
+        },
+        {
+          name: 'systemProgram';
+          docs: ['To create a new program account'];
+          address: '11111111111111111111111111111111';
+        },
+        {
+          name: 'rent';
+          docs: ['Sysvar for program account'];
+          address: 'SysvarRent111111111111111111111111111111111';
+        }
+      ];
+      args: [
+        {
+          name: 'sqrtPriceX64';
+          type: 'u128';
+        },
+        {
+          name: 'openTime';
+          type: 'u64';
+        }
+      ];
+    },
+    {
+      name: 'createSupportMintAssociated';
+      docs: [
+        'Create support token22 mint account which can create pool and send rewards with ignoring the not support extensions.'
+      ];
+      discriminator: [17, 251, 65, 92, 136, 242, 14, 169];
+      accounts: [
+        {
+          name: 'owner';
+          docs: ['Address to be set as protocol owner.'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'adminGroup';
+          docs: ['amm admin group account to store admin permissions.'];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [97, 100, 109, 105, 110, 95, 103, 114, 111, 117, 112];
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenMint';
+          docs: ['Support token mint'];
+        },
+        {
+          name: 'supportMintAssociated';
+          docs: ['Initialize support mint state account to store support mint address and bump.'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [115, 117, 112, 112, 111, 114, 116, 95, 109, 105, 110, 116];
+              },
+              {
+                kind: 'account';
+                path: 'tokenMint';
+              }
+            ];
+          };
+        },
+        {
+          name: 'systemProgram';
+          address: '11111111111111111111111111111111';
+        }
+      ];
+      args: [];
+    },
+    {
+      name: 'decreaseLiquidity';
+      docs: [
+        '#[deprecated(note = "Use `decrease_liquidity_v2` instead.")]',
+        'Decreases liquidity for an existing position',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` -  The context of accounts',
+        '* `liquidity` - The amount by which liquidity will be decreased',
+        '* `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity',
+        '* `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity',
+        ''
+      ];
+      discriminator: [160, 38, 208, 111, 104, 91, 44, 1];
+      accounts: [
+        {
+          name: 'nftOwner';
+          docs: ['The position owner or delegated authority'];
+          signer: true;
+        },
+        {
+          name: 'nftAccount';
+          docs: ['The token account for the tokenized position'];
+        },
+        {
+          name: 'personalPosition';
+          docs: ['Decrease liquidity for this position'];
+          writable: true;
+        },
+        {
+          name: 'poolState';
+          writable: true;
+        },
+        {
+          name: 'protocolPosition';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'account';
+                path: 'personal_position.tick_lower_index';
+                account: 'personalPositionState';
+              },
+              {
+                kind: 'account';
+                path: 'personal_position.tick_upper_index';
+                account: 'personalPositionState';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenVault0';
+          docs: ['Token_0 vault'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault1';
+          docs: ['Token_1 vault'];
+          writable: true;
+        },
+        {
+          name: 'tickArrayLower';
+          docs: ['Stores init state for the lower tick'];
+          writable: true;
+        },
+        {
+          name: 'tickArrayUpper';
+          docs: ['Stores init state for the upper tick'];
+          writable: true;
+        },
+        {
+          name: 'recipientTokenAccount0';
+          docs: ['The destination token account for receive amount_0'];
+          writable: true;
+        },
+        {
+          name: 'recipientTokenAccount1';
+          docs: ['The destination token account for receive amount_1'];
+          writable: true;
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['SPL program to transfer out tokens'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        }
+      ];
+      args: [
+        {
+          name: 'liquidity';
+          type: 'u128';
+        },
+        {
+          name: 'amount0Min';
+          type: 'u64';
+        },
+        {
+          name: 'amount1Min';
+          type: 'u64';
+        }
+      ];
+    },
+    {
+      name: 'decreaseLiquidityV2';
+      docs: [
+        'Decreases liquidity for an existing position, support Token2022',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` -  The context of accounts',
+        '* `liquidity` - The amount by which liquidity will be decreased',
+        '* `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity',
+        '* `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity',
+        ''
+      ];
+      discriminator: [58, 127, 188, 62, 79, 82, 196, 96];
+      accounts: [
+        {
+          name: 'nftOwner';
+          docs: ['The position owner or delegated authority'];
+          signer: true;
+        },
+        {
+          name: 'nftAccount';
+          docs: ['The token account for the tokenized position'];
+        },
+        {
+          name: 'personalPosition';
+          docs: ['Decrease liquidity for this position'];
+          writable: true;
+        },
+        {
+          name: 'poolState';
+          writable: true;
+        },
+        {
+          name: 'protocolPosition';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'account';
+                path: 'personal_position.tick_lower_index';
+                account: 'personalPositionState';
+              },
+              {
+                kind: 'account';
+                path: 'personal_position.tick_upper_index';
+                account: 'personalPositionState';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenVault0';
+          docs: ['Token_0 vault'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault1';
+          docs: ['Token_1 vault'];
+          writable: true;
+        },
+        {
+          name: 'tickArrayLower';
+          docs: ['Stores init state for the lower tick'];
+          writable: true;
+        },
+        {
+          name: 'tickArrayUpper';
+          docs: ['Stores init state for the upper tick'];
+          writable: true;
+        },
+        {
+          name: 'recipientTokenAccount0';
+          docs: ['The destination token account for receive amount_0'];
+          writable: true;
+        },
+        {
+          name: 'recipientTokenAccount1';
+          docs: ['The destination token account for receive amount_1'];
+          writable: true;
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['SPL program to transfer out tokens'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'tokenProgram2022';
+          docs: ['Token program 2022'];
+          address: 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
+        },
+        {
+          name: 'memoProgram';
+          docs: ['memo program'];
+          address: 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr';
+        },
+        {
+          name: 'vault0Mint';
+          docs: ['The mint of token vault 0'];
+        },
+        {
+          name: 'vault1Mint';
+          docs: ['The mint of token vault 1'];
+        }
+      ];
+      args: [
+        {
+          name: 'liquidity';
+          type: 'u128';
+        },
+        {
+          name: 'amount0Min';
+          type: 'u64';
+        },
+        {
+          name: 'amount1Min';
+          type: 'u64';
+        }
+      ];
+    },
+    {
+      name: 'depositOffchainReward';
+      docs: ['deposit offchain reward into the pool'];
+      discriminator: [97, 125, 48, 169, 92, 241, 44, 142];
+      accounts: [
+        {
+          name: 'payer';
+          docs: ['the address paying to deposit the offchain reward.'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'authority';
+          docs: ['The authority make decision that who can deposit the offchain reward.'];
+          signer: true;
+        },
+        {
+          name: 'adminGroup';
+          docs: ['Initialize amm admin group account to store admin permissions.'];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [97, 100, 109, 105, 110, 95, 103, 114, 111, 117, 112];
+              }
+            ];
+          };
+        },
+        {
+          name: 'poolId';
+          docs: ['the pool id, which is the pool state account.'];
+          relations: ['rewardConfig'];
+        },
+        {
+          name: 'tokenMint';
+        },
+        {
+          name: 'payerTokenAccount';
+          docs: [''];
+          writable: true;
+        },
+        {
+          name: 'rewardVaultTokenAccount';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'account';
+                path: 'rewardConfig';
+              },
+              {
+                kind: 'account';
+                path: 'tokenProgram';
+              },
+              {
+                kind: 'account';
+                path: 'tokenMint';
+              }
+            ];
+            program: {
+              kind: 'const';
+              value: [
+                140,
+                151,
+                37,
+                143,
+                78,
+                36,
+                137,
+                241,
+                187,
+                61,
+                16,
+                41,
+                20,
+                142,
+                13,
+                131,
+                11,
+                90,
+                19,
+                153,
+                218,
+                255,
+                16,
+                132,
+                4,
+                142,
+                123,
+                216,
+                219,
+                233,
+                248,
+                89
+              ];
+            };
+          };
+        },
+        {
+          name: 'rewardConfig';
+          docs: ['The offchain reward config account, it also is the reward vault account.'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [111, 102, 102, 99, 104, 97, 105, 110, 95, 114, 101, 119, 97, 114, 100];
+              },
+              {
+                kind: 'account';
+                path: 'poolId';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['Spl token program or token program 2022'];
+        },
+        {
+          name: 'associatedTokenProgram';
+          address: 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL';
+        },
+        {
+          name: 'systemProgram';
+          address: '11111111111111111111111111111111';
+        }
+      ];
+      args: [
+        {
+          name: 'amount';
+          type: 'u64';
+        }
+      ];
+    },
+    {
+      name: 'increaseLiquidity';
+      docs: [
+        '#[deprecated(note = "Use `increase_liquidity_v2` instead.")]',
+        'Increases liquidity for an existing position, with amount paid by `payer`',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        "* `liquidity` - The desired liquidity to be added, can't be zero",
+        '* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check',
+        '* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check',
+        ''
+      ];
+      discriminator: [46, 156, 243, 118, 13, 205, 251, 178];
+      accounts: [
+        {
+          name: 'nftOwner';
+          docs: ['Pays to mint the position'];
+          signer: true;
+        },
+        {
+          name: 'nftAccount';
+          docs: ['The token account for nft'];
+        },
+        {
+          name: 'poolState';
+          writable: true;
+        },
+        {
+          name: 'protocolPosition';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'account';
+                path: 'personal_position.tick_lower_index';
+                account: 'personalPositionState';
+              },
+              {
+                kind: 'account';
+                path: 'personal_position.tick_upper_index';
+                account: 'personalPositionState';
+              }
+            ];
+          };
+        },
+        {
+          name: 'personalPosition';
+          docs: ['Increase liquidity for this position'];
+          writable: true;
+        },
+        {
+          name: 'tickArrayLower';
+          docs: ['Stores init state for the lower tick'];
+          writable: true;
+        },
+        {
+          name: 'tickArrayUpper';
+          docs: ['Stores init state for the upper tick'];
+          writable: true;
+        },
+        {
+          name: 'tokenAccount0';
+          docs: ["The payer's token account for token_0"];
+          writable: true;
+        },
+        {
+          name: 'tokenAccount1';
+          docs: ['The token account spending token_1 to mint the position'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault0';
+          docs: ['The address that holds pool tokens for token_0'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault1';
+          docs: ['The address that holds pool tokens for token_1'];
+          writable: true;
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['Program to create mint account and mint tokens'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        }
+      ];
+      args: [
+        {
+          name: 'liquidity';
+          type: 'u128';
+        },
+        {
+          name: 'amount0Max';
+          type: 'u64';
+        },
+        {
+          name: 'amount1Max';
+          type: 'u64';
+        }
+      ];
+    },
+    {
+      name: 'increaseLiquidityV2';
+      docs: [
+        'Increases liquidity for an existing position, with amount paid by `payer`, support Token2022',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        '* `liquidity` - The desired liquidity to be added, if zero, calculate liquidity base amount_0 or amount_1 according base_flag',
+        '* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check',
+        '* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check',
+        '* `base_flag` - must be specified if liquidity is zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max',
+        ''
+      ];
+      discriminator: [133, 29, 89, 223, 69, 238, 176, 10];
+      accounts: [
+        {
+          name: 'nftOwner';
+          docs: ['Pays to mint the position'];
+          signer: true;
+        },
+        {
+          name: 'nftAccount';
+          docs: ['The token account for nft'];
+        },
+        {
+          name: 'poolState';
+          writable: true;
+        },
+        {
+          name: 'protocolPosition';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'account';
+                path: 'personal_position.tick_lower_index';
+                account: 'personalPositionState';
+              },
+              {
+                kind: 'account';
+                path: 'personal_position.tick_upper_index';
+                account: 'personalPositionState';
+              }
+            ];
+          };
+        },
+        {
+          name: 'personalPosition';
+          docs: ['Increase liquidity for this position'];
+          writable: true;
+        },
+        {
+          name: 'tickArrayLower';
+          docs: ['Stores init state for the lower tick'];
+          writable: true;
+        },
+        {
+          name: 'tickArrayUpper';
+          docs: ['Stores init state for the upper tick'];
+          writable: true;
+        },
+        {
+          name: 'tokenAccount0';
+          docs: ["The payer's token account for token_0"];
+          writable: true;
+        },
+        {
+          name: 'tokenAccount1';
+          docs: ['The token account spending token_1 to mint the position'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault0';
+          docs: ['The address that holds pool tokens for token_0'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault1';
+          docs: ['The address that holds pool tokens for token_1'];
+          writable: true;
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['Program to create mint account and mint tokens'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'tokenProgram2022';
+          docs: ['Token program 2022'];
+          address: 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
+        },
+        {
+          name: 'vault0Mint';
+          docs: ['The mint of token vault 0'];
+        },
+        {
+          name: 'vault1Mint';
+          docs: ['The mint of token vault 1'];
+        }
+      ];
+      args: [
+        {
+          name: 'liquidity';
+          type: 'u128';
+        },
+        {
+          name: 'amount0Max';
+          type: 'u64';
+        },
+        {
+          name: 'amount1Max';
+          type: 'u64';
+        },
+        {
+          name: 'baseFlag';
+          type: {
+            option: 'bool';
+          };
+        }
+      ];
+    },
+    {
+      name: 'initAmmAdminGroup';
+      docs: ['Initialize the AMM admin group account, which is used to manage the AMM protocol.'];
+      discriminator: [209, 108, 32, 246, 157, 214, 237, 86];
+      accounts: [
+        {
+          name: 'payer';
+          docs: ['only super admin can create admin group'];
+          writable: true;
+          signer: true;
+          address: 'AY196f8U5EvM999PVnvLmyvaUnzL4GLiFaGKUgnJXN6o';
+        },
+        {
+          name: 'adminGroup';
+          docs: ['Initialize amm admin group account to store admin permissions.'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [97, 100, 109, 105, 110, 95, 103, 114, 111, 117, 112];
+              }
+            ];
+          };
+        },
+        {
+          name: 'systemProgram';
+          address: '11111111111111111111111111111111';
+        }
+      ];
+      args: [
+        {
+          name: 'params';
+          type: {
+            defined: {
+              name: 'initAdminGroupParams';
+            };
+          };
+        }
+      ];
+    },
+    {
+      name: 'initializeReward';
+      docs: [
+        'Initialize a reward info for a given pool and reward index',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx`- The context of accounts',
+        '* `reward_index` - the index to reward info',
+        '* `open_time` - reward open timestamp',
+        '* `end_time` - reward end timestamp',
+        '* `emissions_per_second_x64` - Token reward per second are earned per unit of liquidity.',
+        ''
+      ];
+      discriminator: [95, 135, 192, 196, 242, 129, 230, 68];
+      accounts: [
+        {
+          name: 'rewardFunder';
+          docs: ['The founder deposit reward token to vault'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'adminGroup';
+          docs: ['amm admin group account to store admin permissions.'];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [97, 100, 109, 105, 110, 95, 103, 114, 111, 117, 112];
+              }
+            ];
+          };
+        },
+        {
+          name: 'funderTokenAccount';
+          writable: true;
+        },
+        {
+          name: 'ammConfig';
+          docs: ['For check the reward_funder authority'];
+        },
+        {
+          name: 'poolState';
+          docs: ['Set reward for this pool'];
+          writable: true;
+        },
+        {
+          name: 'operationState';
+          docs: ['load info from the account to judge reward permission'];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [111, 112, 101, 114, 97, 116, 105, 111, 110];
+              }
+            ];
+          };
+        },
+        {
+          name: 'rewardTokenMint';
+          docs: ['Reward mint'];
+        },
+        {
+          name: 'rewardTokenVault';
+          docs: ['A pda, reward vault'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 111, 108, 95, 114, 101, 119, 97, 114, 100, 95, 118, 97, 117, 108, 116];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'account';
+                path: 'rewardTokenMint';
+              }
+            ];
+          };
+        },
+        {
+          name: 'rewardTokenProgram';
+        },
+        {
+          name: 'systemProgram';
+          address: '11111111111111111111111111111111';
+        },
+        {
+          name: 'rent';
+          address: 'SysvarRent111111111111111111111111111111111';
+        }
+      ];
+      args: [
+        {
+          name: 'param';
+          type: {
+            defined: {
+              name: 'initializeRewardParam';
+            };
+          };
+        }
+      ];
+    },
+    {
+      name: 'openPosition';
+      docs: [
+        '#[deprecated(note = "Use `open_position_with_token22_nft` instead.")]',
+        'Creates a new position wrapped in a NFT',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        '* `tick_lower_index` - The low boundary of market',
+        '* `tick_upper_index` - The upper boundary of market',
+        '* `tick_array_lower_start_index` - The start index of tick array which include tick low',
+        '* `tick_array_upper_start_index` - The start index of tick array which include tick upper',
+        '* `liquidity` - The liquidity to be added',
+        '* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check',
+        '* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check',
+        ''
+      ];
+      discriminator: [135, 128, 47, 77, 15, 152, 240, 49];
+      accounts: [
+        {
+          name: 'payer';
+          docs: ['Pays to mint the position'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'positionNftOwner';
+        },
+        {
+          name: 'positionNftMint';
+          docs: ['Unique token mint address'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'positionNftAccount';
+          docs: [
+            'Token account where position NFT will be minted',
+            'This account created in the contract by cpi to avoid large stack variables'
+          ];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'account';
+                path: 'positionNftOwner';
+              },
+              {
+                kind: 'const';
+                value: [
+                  6,
+                  221,
+                  246,
+                  225,
+                  215,
+                  101,
+                  161,
+                  147,
+                  217,
+                  203,
+                  225,
+                  70,
+                  206,
+                  235,
+                  121,
+                  172,
+                  28,
+                  180,
+                  133,
+                  237,
+                  95,
+                  91,
+                  55,
+                  145,
+                  58,
+                  140,
+                  245,
+                  133,
+                  126,
+                  255,
+                  0,
+                  169
+                ];
+              },
+              {
+                kind: 'account';
+                path: 'positionNftMint';
+              }
+            ];
+            program: {
+              kind: 'const';
+              value: [
+                140,
+                151,
+                37,
+                143,
+                78,
+                36,
+                137,
+                241,
+                187,
+                61,
+                16,
+                41,
+                20,
+                142,
+                13,
+                131,
+                11,
+                90,
+                19,
+                153,
+                218,
+                255,
+                16,
+                132,
+                4,
+                142,
+                123,
+                216,
+                219,
+                233,
+                248,
+                89
+              ];
+            };
+          };
+        },
+        {
+          name: 'metadataAccount';
+          docs: ['To store metaplex metadata'];
+          writable: true;
+        },
+        {
+          name: 'poolState';
+          docs: ['Add liquidity for this pool'];
+          writable: true;
+        },
+        {
+          name: 'protocolPosition';
+          docs: ['Store the information of market marking in range'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'arg';
+                path: 'tickLowerIndex';
+              },
+              {
+                kind: 'arg';
+                path: 'tickUpperIndex';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tickArrayLower';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [116, 105, 99, 107, 95, 97, 114, 114, 97, 121];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'arg';
+                path: 'tickArrayLowerStartIndex';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tickArrayUpper';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [116, 105, 99, 107, 95, 97, 114, 114, 97, 121];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'arg';
+                path: 'tickArrayUpperStartIndex';
+              }
+            ];
+          };
+        },
+        {
+          name: 'personalPosition';
+          docs: ['personal position state'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'positionNftMint';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenAccount0';
+          docs: ['The token_0 account deposit token to the pool'];
+          writable: true;
+        },
+        {
+          name: 'tokenAccount1';
+          docs: ['The token_1 account deposit token to the pool'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault0';
+          docs: ['The address that holds pool tokens for token_0'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault1';
+          docs: ['The address that holds pool tokens for token_1'];
+          writable: true;
+        },
+        {
+          name: 'rent';
+          docs: ['Sysvar for token mint and ATA creation'];
+          address: 'SysvarRent111111111111111111111111111111111';
+        },
+        {
+          name: 'systemProgram';
+          docs: ['Program to create the position manager state account'];
+          address: '11111111111111111111111111111111';
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['Program to create mint account and mint tokens'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'associatedTokenProgram';
+          docs: ['Program to create an ATA for receiving position NFT'];
+          address: 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL';
+        },
+        {
+          name: 'metadataProgram';
+          docs: ['Program to create NFT metadata'];
+          address: 'metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s';
+        }
+      ];
+      args: [
+        {
+          name: 'tickLowerIndex';
+          type: 'i32';
+        },
+        {
+          name: 'tickUpperIndex';
+          type: 'i32';
+        },
+        {
+          name: 'tickArrayLowerStartIndex';
+          type: 'i32';
+        },
+        {
+          name: 'tickArrayUpperStartIndex';
+          type: 'i32';
+        },
+        {
+          name: 'liquidity';
+          type: 'u128';
+        },
+        {
+          name: 'amount0Max';
+          type: 'u64';
+        },
+        {
+          name: 'amount1Max';
+          type: 'u64';
+        }
+      ];
+    },
+    {
+      name: 'openPositionV2';
+      docs: [
+        '#[deprecated(note = "Use `open_position_with_token22_nft` instead.")]',
+        'Creates a new position wrapped in a NFT, support Token2022',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        '* `tick_lower_index` - The low boundary of market',
+        '* `tick_upper_index` - The upper boundary of market',
+        '* `tick_array_lower_start_index` - The start index of tick array which include tick low',
+        '* `tick_array_upper_start_index` - The start index of tick array which include tick upper',
+        '* `liquidity` - The liquidity to be added, if zero, and the base_flag is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity',
+        '* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check',
+        '* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check',
+        '* `with_metadata` - The flag indicating whether to create NFT mint metadata',
+        '* `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max',
+        ''
+      ];
+      discriminator: [77, 184, 74, 214, 112, 86, 241, 199];
+      accounts: [
+        {
+          name: 'payer';
+          docs: ['Pays to mint the position'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'positionNftOwner';
+        },
+        {
+          name: 'positionNftMint';
+          docs: ['Unique token mint address'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'positionNftAccount';
+          docs: ['Token account where position NFT will be minted'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'account';
+                path: 'positionNftOwner';
+              },
+              {
+                kind: 'const';
+                value: [
+                  6,
+                  221,
+                  246,
+                  225,
+                  215,
+                  101,
+                  161,
+                  147,
+                  217,
+                  203,
+                  225,
+                  70,
+                  206,
+                  235,
+                  121,
+                  172,
+                  28,
+                  180,
+                  133,
+                  237,
+                  95,
+                  91,
+                  55,
+                  145,
+                  58,
+                  140,
+                  245,
+                  133,
+                  126,
+                  255,
+                  0,
+                  169
+                ];
+              },
+              {
+                kind: 'account';
+                path: 'positionNftMint';
+              }
+            ];
+            program: {
+              kind: 'const';
+              value: [
+                140,
+                151,
+                37,
+                143,
+                78,
+                36,
+                137,
+                241,
+                187,
+                61,
+                16,
+                41,
+                20,
+                142,
+                13,
+                131,
+                11,
+                90,
+                19,
+                153,
+                218,
+                255,
+                16,
+                132,
+                4,
+                142,
+                123,
+                216,
+                219,
+                233,
+                248,
+                89
+              ];
+            };
+          };
+        },
+        {
+          name: 'metadataAccount';
+          docs: ['To store metaplex metadata'];
+          writable: true;
+        },
+        {
+          name: 'poolState';
+          docs: ['Add liquidity for this pool'];
+          writable: true;
+        },
+        {
+          name: 'protocolPosition';
+          docs: ['Store the information of market marking in range'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'arg';
+                path: 'tickLowerIndex';
+              },
+              {
+                kind: 'arg';
+                path: 'tickUpperIndex';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tickArrayLower';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [116, 105, 99, 107, 95, 97, 114, 114, 97, 121];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'arg';
+                path: 'tickArrayLowerStartIndex';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tickArrayUpper';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [116, 105, 99, 107, 95, 97, 114, 114, 97, 121];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'arg';
+                path: 'tickArrayUpperStartIndex';
+              }
+            ];
+          };
+        },
+        {
+          name: 'personalPosition';
+          docs: ['personal position state'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'positionNftMint';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenAccount0';
+          docs: ['The token_0 account deposit token to the pool'];
+          writable: true;
+        },
+        {
+          name: 'tokenAccount1';
+          docs: ['The token_1 account deposit token to the pool'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault0';
+          docs: ['The address that holds pool tokens for token_0'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault1';
+          docs: ['The address that holds pool tokens for token_1'];
+          writable: true;
+        },
+        {
+          name: 'rent';
+          docs: ['Sysvar for token mint and ATA creation'];
+          address: 'SysvarRent111111111111111111111111111111111';
+        },
+        {
+          name: 'systemProgram';
+          docs: ['Program to create the position manager state account'];
+          address: '11111111111111111111111111111111';
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['Program to create mint account and mint tokens'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'associatedTokenProgram';
+          docs: ['Program to create an ATA for receiving position NFT'];
+          address: 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL';
+        },
+        {
+          name: 'metadataProgram';
+          docs: ['Program to create NFT metadata'];
+          address: 'metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s';
+        },
+        {
+          name: 'tokenProgram2022';
+          docs: ['Program to create mint account and mint tokens'];
+          address: 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
+        },
+        {
+          name: 'vault0Mint';
+          docs: ['The mint of token vault 0'];
+        },
+        {
+          name: 'vault1Mint';
+          docs: ['The mint of token vault 1'];
+        }
+      ];
+      args: [
+        {
+          name: 'tickLowerIndex';
+          type: 'i32';
+        },
+        {
+          name: 'tickUpperIndex';
+          type: 'i32';
+        },
+        {
+          name: 'tickArrayLowerStartIndex';
+          type: 'i32';
+        },
+        {
+          name: 'tickArrayUpperStartIndex';
+          type: 'i32';
+        },
+        {
+          name: 'liquidity';
+          type: 'u128';
+        },
+        {
+          name: 'amount0Max';
+          type: 'u64';
+        },
+        {
+          name: 'amount1Max';
+          type: 'u64';
+        },
+        {
+          name: 'withMetadata';
+          type: 'bool';
+        },
+        {
+          name: 'baseFlag';
+          type: {
+            option: 'bool';
+          };
+        }
+      ];
+    },
+    {
+      name: 'openPositionWithToken22Nft';
+      docs: [
+        'Creates a new position wrapped in a Token2022 NFT without relying on metadata_program and metadata_account, reduce the cost for user to create a personal position.',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        '* `tick_lower_index` - The low boundary of market',
+        '* `tick_upper_index` - The upper boundary of market',
+        '* `tick_array_lower_start_index` - The start index of tick array which include tick low',
+        '* `tick_array_upper_start_index` - The start index of tick array which include tick upper',
+        '* `liquidity` - The liquidity to be added, if zero, and the base_flag is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity',
+        '* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check',
+        '* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check',
+        '* `with_metadata` - The flag indicating whether to create NFT mint metadata',
+        '* `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max',
+        ''
+      ];
+      discriminator: [77, 255, 174, 82, 125, 29, 201, 46];
+      accounts: [
+        {
+          name: 'payer';
+          docs: ['Pays to mint the position'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'positionNftOwner';
+        },
+        {
+          name: 'positionNftMint';
+          docs: ['Unique token mint address, initialize in contract'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'positionNftAccount';
+          writable: true;
+        },
+        {
+          name: 'poolState';
+          docs: ['Add liquidity for this pool'];
+          writable: true;
+        },
+        {
+          name: 'protocolPosition';
+          docs: ['Store the information of market marking in range'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'arg';
+                path: 'tickLowerIndex';
+              },
+              {
+                kind: 'arg';
+                path: 'tickUpperIndex';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tickArrayLower';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [116, 105, 99, 107, 95, 97, 114, 114, 97, 121];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'arg';
+                path: 'tickArrayLowerStartIndex';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tickArrayUpper';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [116, 105, 99, 107, 95, 97, 114, 114, 97, 121];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'arg';
+                path: 'tickArrayUpperStartIndex';
+              }
+            ];
+          };
+        },
+        {
+          name: 'personalPosition';
+          docs: ['personal position state'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'positionNftMint';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenAccount0';
+          docs: ['The token_0 account deposit token to the pool'];
+          writable: true;
+        },
+        {
+          name: 'tokenAccount1';
+          docs: ['The token_1 account deposit token to the pool'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault0';
+          docs: ['The address that holds pool tokens for token_0'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault1';
+          docs: ['The address that holds pool tokens for token_1'];
+          writable: true;
+        },
+        {
+          name: 'rent';
+          docs: ['Sysvar for token mint and ATA creation'];
+          address: 'SysvarRent111111111111111111111111111111111';
+        },
+        {
+          name: 'systemProgram';
+          docs: ['Program to create the position manager state account'];
+          address: '11111111111111111111111111111111';
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['Program to transfer for token account'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'associatedTokenProgram';
+          docs: ['Program to create an ATA for receiving position NFT'];
+          address: 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL';
+        },
+        {
+          name: 'tokenProgram2022';
+          docs: ['Program to create NFT mint/token account and transfer for token22 account'];
+          address: 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
+        },
+        {
+          name: 'vault0Mint';
+          docs: ['The mint of token vault 0'];
+        },
+        {
+          name: 'vault1Mint';
+          docs: ['The mint of token vault 1'];
+        }
+      ];
+      args: [
+        {
+          name: 'tickLowerIndex';
+          type: 'i32';
+        },
+        {
+          name: 'tickUpperIndex';
+          type: 'i32';
+        },
+        {
+          name: 'tickArrayLowerStartIndex';
+          type: 'i32';
+        },
+        {
+          name: 'tickArrayUpperStartIndex';
+          type: 'i32';
+        },
+        {
+          name: 'liquidity';
+          type: 'u128';
+        },
+        {
+          name: 'amount0Max';
+          type: 'u64';
+        },
+        {
+          name: 'amount1Max';
+          type: 'u64';
+        },
+        {
+          name: 'withMetadata';
+          type: 'bool';
+        },
+        {
+          name: 'baseFlag';
+          type: {
+            option: 'bool';
+          };
+        }
+      ];
+    },
+    {
+      name: 'setRewardParams';
+      docs: [
+        'Reset reward param, start a new reward cycle or extend the current cycle.',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        '* `reward_index` - The index of reward token in the pool.',
+        '* `emissions_per_second_x64` - The per second emission reward, when extend the current cycle,',
+        "new value can't be less than old value",
+        '* `open_time` - reward open timestamp, must be set when starting a new cycle',
+        '* `end_time` - reward end timestamp',
+        ''
+      ];
+      discriminator: [112, 52, 167, 75, 32, 201, 211, 137];
+      accounts: [
+        {
+          name: 'authority';
+          docs: ['Address to be set as protocol owner. It pays to create factory state account.'];
+          signer: true;
+        },
+        {
+          name: 'adminGroup';
+          docs: ['amm admin group account to store admin permissions.'];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [97, 100, 109, 105, 110, 95, 103, 114, 111, 117, 112];
+              }
+            ];
+          };
+        },
+        {
+          name: 'ammConfig';
+        },
+        {
+          name: 'poolState';
+          writable: true;
+        },
+        {
+          name: 'operationState';
+          docs: ['load info from the account to judge reward permission'];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [111, 112, 101, 114, 97, 116, 105, 111, 110];
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['Token program'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'tokenProgram2022';
+          docs: ['Token program 2022'];
+          address: 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
+        }
+      ];
+      args: [
+        {
+          name: 'rewardIndex';
+          type: 'u8';
+        },
+        {
+          name: 'emissionsPerSecondX64';
+          type: 'u128';
+        },
+        {
+          name: 'openTime';
+          type: 'u64';
+        },
+        {
+          name: 'endTime';
+          type: 'u64';
+        }
+      ];
+    },
+    {
+      name: 'swap';
+      docs: [
+        '#[deprecated(note = "Use `swap_v2` instead.")]',
+        'Swaps one token for as much as possible of another token across a single pool',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        '* `amount` - Arranged in pairs with other_amount_threshold. (amount_in, amount_out_minimum) or (amount_out, amount_in_maximum)',
+        '* `other_amount_threshold` - For slippage check',
+        '* `sqrt_price_limit` - The Q64.64 sqrt price √P limit. If zero for one, the price cannot',
+        '* `is_base_input` - swap base input or swap base output',
+        ''
+      ];
+      discriminator: [248, 198, 158, 145, 225, 117, 135, 200];
+      accounts: [
+        {
+          name: 'payer';
+          docs: ['The user performing the swap'];
+          signer: true;
+        },
+        {
+          name: 'ammConfig';
+          docs: ['The factory state to read protocol fees'];
+        },
+        {
+          name: 'poolState';
+          docs: ['The program account of the pool in which the swap will be performed'];
+          writable: true;
+        },
+        {
+          name: 'inputTokenAccount';
+          docs: ['The user token account for input token'];
+          writable: true;
+        },
+        {
+          name: 'outputTokenAccount';
+          docs: ['The user token account for output token'];
+          writable: true;
+        },
+        {
+          name: 'inputVault';
+          docs: ['The vault token account for input token'];
+          writable: true;
+        },
+        {
+          name: 'outputVault';
+          docs: ['The vault token account for output token'];
+          writable: true;
+        },
+        {
+          name: 'observationState';
+          docs: ['The program account for the most recent oracle observation'];
+          writable: true;
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['SPL program for token transfers'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'tickArray';
+          writable: true;
+        }
+      ];
+      args: [
+        {
+          name: 'amount';
+          type: 'u64';
+        },
+        {
+          name: 'otherAmountThreshold';
+          type: 'u64';
+        },
+        {
+          name: 'sqrtPriceLimitX64';
+          type: 'u128';
+        },
+        {
+          name: 'isBaseInput';
+          type: 'bool';
+        }
+      ];
+    },
+    {
+      name: 'swapV2';
+      docs: [
+        'Swaps one token for as much as possible of another token across a single pool, support token program 2022',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        '* `amount` - Arranged in pairs with other_amount_threshold. (amount_in, amount_out_minimum) or (amount_out, amount_in_maximum)',
+        '* `other_amount_threshold` - For slippage check',
+        '* `sqrt_price_limit` - The Q64.64 sqrt price √P limit. If zero for one, the price cannot',
+        '* `is_base_input` - swap base input or swap base output',
+        ''
+      ];
+      discriminator: [43, 4, 237, 11, 26, 201, 30, 98];
+      accounts: [
+        {
+          name: 'payer';
+          docs: ['The user performing the swap'];
+          signer: true;
+        },
+        {
+          name: 'ammConfig';
+          docs: ['The factory state to read protocol fees'];
+        },
+        {
+          name: 'poolState';
+          docs: ['The program account of the pool in which the swap will be performed'];
+          writable: true;
+        },
+        {
+          name: 'inputTokenAccount';
+          docs: ['The user token account for input token'];
+          writable: true;
+        },
+        {
+          name: 'outputTokenAccount';
+          docs: ['The user token account for output token'];
+          writable: true;
+        },
+        {
+          name: 'inputVault';
+          docs: ['The vault token account for input token'];
+          writable: true;
+        },
+        {
+          name: 'outputVault';
+          docs: ['The vault token account for output token'];
+          writable: true;
+        },
+        {
+          name: 'observationState';
+          docs: ['The program account for the most recent oracle observation'];
+          writable: true;
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['SPL program for token transfers'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'tokenProgram2022';
+          docs: ['SPL program 2022 for token transfers'];
+          address: 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
+        },
+        {
+          name: 'memoProgram';
+          docs: ['Memo program'];
+          address: 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr';
+        },
+        {
+          name: 'inputVaultMint';
+          docs: ['The mint of token vault 0'];
+        },
+        {
+          name: 'outputVaultMint';
+          docs: ['The mint of token vault 1'];
+        }
+      ];
+      args: [
+        {
+          name: 'amount';
+          type: 'u64';
+        },
+        {
+          name: 'otherAmountThreshold';
+          type: 'u64';
+        },
+        {
+          name: 'sqrtPriceLimitX64';
+          type: 'u128';
+        },
+        {
+          name: 'isBaseInput';
+          type: 'bool';
+        }
+      ];
+    },
+    {
+      name: 'transferRewardOwner';
+      docs: [
+        'Transfer reward owner',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx`- The context of accounts',
+        '* `new_owner`- new owner pubkey',
+        ''
+      ];
+      discriminator: [7, 22, 12, 83, 242, 43, 48, 121];
+      accounts: [
+        {
+          name: 'authority';
+          docs: ['Address to be set as operation account owner.'];
+          signer: true;
+        },
+        {
+          name: 'adminGroup';
+          docs: ['amm admin group account to store admin permissions.'];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [97, 100, 109, 105, 110, 95, 103, 114, 111, 117, 112];
+              }
+            ];
+          };
+        },
+        {
+          name: 'poolState';
+          writable: true;
+        }
+      ];
+      args: [
+        {
+          name: 'newOwner';
+          type: 'pubkey';
+        }
+      ];
+    },
+    {
+      name: 'updateAmmAdminGroup';
+      docs: ['Update the AMM admin group account, which is used to manage the AMM protocol.'];
+      discriminator: [61, 183, 185, 188, 82, 81, 141, 197];
+      accounts: [
+        {
+          name: 'payer';
+          docs: ['only super admin can create admin group'];
+          writable: true;
+          signer: true;
+          address: 'AY196f8U5EvM999PVnvLmyvaUnzL4GLiFaGKUgnJXN6o';
+        },
+        {
+          name: 'adminGroup';
+          docs: ['update amm admin group account to store admin permissions.'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [97, 100, 109, 105, 110, 95, 103, 114, 111, 117, 112];
+              }
+            ];
+          };
+        }
+      ];
+      args: [
+        {
+          name: 'params';
+          type: {
+            defined: {
+              name: 'updateAdminGroupParams';
+            };
+          };
+        }
+      ];
+    },
+    {
+      name: 'updateAmmConfig';
+      docs: [
+        'Updates the owner of the amm config',
+        'Must be called by the current owner or admin',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx`- The context of accounts',
+        '* `trade_fee_rate`- The new trade fee rate of amm config, be set when `param` is 0',
+        '* `protocol_fee_rate`- The new protocol fee rate of amm config, be set when `param` is 1',
+        '* `fund_fee_rate`- The new fund fee rate of amm config, be set when `param` is 2',
+        "* `new_owner`- The config's new owner, be set when `param` is 3",
+        "* `new_fund_owner`- The config's new fund owner, be set when `param` is 4",
+        '* `param`- The value can be 0 | 1 | 2 | 3 | 4, otherwise will report a error',
+        ''
+      ];
+      discriminator: [49, 60, 174, 136, 154, 28, 116, 200];
+      accounts: [
+        {
+          name: 'owner';
+          docs: ['The amm config owner or admin'];
+          signer: true;
+        },
+        {
+          name: 'adminGroup';
+          docs: ['amm admin group account to store admin permissions.'];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [97, 100, 109, 105, 110, 95, 103, 114, 111, 117, 112];
+              }
+            ];
+          };
+        },
+        {
+          name: 'ammConfig';
+          docs: ['Amm config account to be changed'];
+          writable: true;
+        }
+      ];
+      args: [
+        {
+          name: 'param';
+          type: 'u8';
+        },
+        {
+          name: 'value';
+          type: 'u32';
+        }
+      ];
+    },
+    {
+      name: 'updateOperationAccount';
+      docs: [
+        'Update the operation account',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx`- The context of accounts',
+        '* `param`- The value can be 0 | 1 | 2 | 3, otherwise will report a error',
+        '* `keys`- update operation owner when the `param` is 0',
+        'remove operation owner when the `param` is 1',
+        'update whitelist mint when the `param` is 2',
+        'remove whitelist mint when the `param` is 3',
+        ''
+      ];
+      discriminator: [127, 70, 119, 40, 188, 227, 61, 7];
+      accounts: [
+        {
+          name: 'owner';
+          docs: ['Address to be set as operation account owner.'];
+          signer: true;
+        },
+        {
+          name: 'adminGroup';
+          docs: ['amm admin group account to store admin permissions.'];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [97, 100, 109, 105, 110, 95, 103, 114, 111, 117, 112];
+              }
+            ];
+          };
+        },
+        {
+          name: 'operationState';
+          docs: ['Initialize operation state account to store operation owner address and white list mint.'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [111, 112, 101, 114, 97, 116, 105, 111, 110];
+              }
+            ];
+          };
+        },
+        {
+          name: 'systemProgram';
+          address: '11111111111111111111111111111111';
+        }
+      ];
+      args: [
+        {
+          name: 'param';
+          type: 'u8';
+        },
+        {
+          name: 'keys';
+          type: {
+            vec: 'pubkey';
+          };
+        }
+      ];
+    },
+    {
+      name: 'updatePoolStatus';
+      docs: [
+        'Update pool status for given value',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx`- The context of accounts',
+        '* `status` - The value of status',
+        ''
+      ];
+      discriminator: [130, 87, 108, 6, 46, 224, 117, 123];
+      accounts: [
+        {
+          name: 'authority';
+          signer: true;
+        },
+        {
+          name: 'adminGroup';
+          docs: ['amm admin group account to store admin permissions.'];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [97, 100, 109, 105, 110, 95, 103, 114, 111, 117, 112];
+              }
+            ];
+          };
+        },
+        {
+          name: 'poolState';
+          writable: true;
+        }
+      ];
+      args: [
+        {
+          name: 'status';
+          type: 'u8';
+        }
+      ];
+    },
+    {
+      name: 'updateRewardInfos';
+      docs: [
+        'Update rewards info of the given pool, can be called for everyone',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx`- The context of accounts',
+        ''
+      ];
+      discriminator: [163, 172, 224, 52, 11, 154, 106, 223];
+      accounts: [
+        {
+          name: 'poolState';
+          docs: ['The liquidity pool for which reward info to update'];
+          writable: true;
+        }
+      ];
+      args: [];
+    },
+    {
+      name: 'withdrawOffchainReward';
+      docs: ['withdraw offchain reward from the pool'];
+      discriminator: [86, 53, 59, 76, 217, 38, 71, 213];
+      accounts: [
+        {
+          name: 'authority';
+          docs: ['The authority make decision that who can withdraw the offchain reward.'];
+          signer: true;
+        },
+        {
+          name: 'adminGroup';
+          docs: ['Initialize amm admin group account to store admin permissions.'];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [97, 100, 109, 105, 110, 95, 103, 114, 111, 117, 112];
+              }
+            ];
+          };
+        },
+        {
+          name: 'poolId';
+          docs: ['the pool id, which is the pool state account.'];
+          relations: ['rewardConfig'];
+        },
+        {
+          name: 'tokenMint';
+        },
+        {
+          name: 'receiverTokenAccount';
+          docs: ['the address who receive the withdrawn offchain reward.'];
+          writable: true;
+        },
+        {
+          name: 'rewardVaultTokenAccount';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'account';
+                path: 'rewardConfig';
+              },
+              {
+                kind: 'account';
+                path: 'tokenProgram';
+              },
+              {
+                kind: 'account';
+                path: 'tokenMint';
+              }
+            ];
+            program: {
+              kind: 'const';
+              value: [
+                140,
+                151,
+                37,
+                143,
+                78,
+                36,
+                137,
+                241,
+                187,
+                61,
+                16,
+                41,
+                20,
+                142,
+                13,
+                131,
+                11,
+                90,
+                19,
+                153,
+                218,
+                255,
+                16,
+                132,
+                4,
+                142,
+                123,
+                216,
+                219,
+                233,
+                248,
+                89
+              ];
+            };
+          };
+        },
+        {
+          name: 'rewardConfig';
+          docs: ['The offchain reward config account, it also is the reward vault account.'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [111, 102, 102, 99, 104, 97, 105, 110, 95, 114, 101, 119, 97, 114, 100];
+              },
+              {
+                kind: 'account';
+                path: 'poolId';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['Spl token program or token program 2022'];
+        },
+        {
+          name: 'associatedTokenProgram';
+          address: 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL';
+        }
+      ];
+      args: [
+        {
+          name: 'amount';
+          type: 'u64';
+        }
+      ];
+    }
+  ];
+  accounts: [
+    {
+      name: 'ammAdminGroup';
+      discriminator: [128, 128, 234, 30, 61, 172, 188, 123];
+    },
+    {
+      name: 'ammConfig';
+      discriminator: [218, 244, 33, 104, 203, 203, 43, 111];
+    },
+    {
+      name: 'observationState';
+      discriminator: [122, 174, 197, 53, 129, 9, 165, 132];
+    },
+    {
+      name: 'offchainRewardConfig';
+      discriminator: [118, 52, 115, 150, 99, 69, 164, 76];
+    },
+    {
+      name: 'operationState';
+      discriminator: [19, 236, 58, 237, 81, 222, 183, 252];
+    },
+    {
+      name: 'personalPositionState';
+      discriminator: [70, 111, 150, 126, 230, 15, 25, 117];
+    },
+    {
+      name: 'poolState';
+      discriminator: [247, 237, 227, 245, 215, 195, 222, 70];
+    },
+    {
+      name: 'protocolPositionState';
+      discriminator: [100, 226, 145, 99, 146, 218, 160, 106];
+    },
+    {
+      name: 'supportMintAssociated';
+      discriminator: [134, 40, 183, 79, 12, 112, 162, 53];
+    },
+    {
+      name: 'tickArrayBitmapExtension';
+      discriminator: [60, 150, 36, 219, 97, 128, 139, 153];
+    },
+    {
+      name: 'tickArrayState';
+      discriminator: [192, 155, 85, 205, 49, 249, 129, 42];
+    }
+  ];
+  events: [
+    {
+      name: 'collectPersonalFeeEvent';
+      discriminator: [166, 174, 105, 192, 81, 161, 83, 105];
+    },
+    {
+      name: 'collectProtocolFeeEvent';
+      discriminator: [206, 87, 17, 79, 45, 41, 213, 61];
+    },
+    {
+      name: 'configChangeEvent';
+      discriminator: [247, 189, 7, 119, 106, 112, 95, 151];
+    },
+    {
+      name: 'createPersonalPositionEvent';
+      discriminator: [100, 30, 87, 249, 196, 223, 154, 206];
+    },
+    {
+      name: 'decreaseLiquidityEvent';
+      discriminator: [58, 222, 86, 58, 68, 50, 85, 56];
+    },
+    {
+      name: 'increaseLiquidityEvent';
+      discriminator: [49, 79, 105, 212, 32, 34, 30, 84];
+    },
+    {
+      name: 'liquidityCalculateEvent';
+      discriminator: [237, 112, 148, 230, 57, 84, 180, 162];
+    },
+    {
+      name: 'liquidityChangeEvent';
+      discriminator: [126, 240, 175, 206, 158, 88, 153, 107];
+    },
+    {
+      name: 'modifyAmmAdminGroupEvent';
+      discriminator: [218, 118, 87, 81, 53, 80, 18, 235];
+    },
+    {
+      name: 'poolCreatedEvent';
+      discriminator: [25, 94, 75, 47, 112, 99, 53, 63];
+    },
+    {
+      name: 'swapEvent';
+      discriminator: [64, 198, 205, 232, 38, 8, 113, 226];
+    },
+    {
+      name: 'updateRewardInfosEvent';
+      discriminator: [109, 127, 186, 78, 114, 65, 37, 236];
+    }
+  ];
+  errors: [
+    {
+      code: 6000;
+      name: 'lok';
+      msg: 'lok';
+    },
+    {
+      code: 6001;
+      name: 'notApproved';
+      msg: 'Not approved';
+    },
+    {
+      code: 6002;
+      name: 'invalidUpdateConfigFlag';
+      msg: 'invalid update amm config flag';
+    },
+    {
+      code: 6003;
+      name: 'accountLack';
+      msg: 'Account lack';
+    },
+    {
+      code: 6004;
+      name: 'closePositionErr';
+      msg: 'Remove liquitity, collect fees owed and reward then you can close position account';
+    },
+    {
+      code: 6005;
+      name: 'zeroMintAmount';
+      msg: 'Minting amount should be greater than 0';
+    },
+    {
+      code: 6006;
+      name: 'invalidTickIndex';
+      msg: 'Tick out of range';
+    },
+    {
+      code: 6007;
+      name: 'tickInvalidOrder';
+      msg: 'The lower tick must be below the upper tick';
+    },
+    {
+      code: 6008;
+      name: 'tickLowerOverflow';
+      msg: 'The tick must be greater, or equal to the minimum tick(-443636)';
+    },
+    {
+      code: 6009;
+      name: 'tickUpperOverflow';
+      msg: 'The tick must be lesser than, or equal to the maximum tick(443636)';
+    },
+    {
+      code: 6010;
+      name: 'tickAndSpacingNotMatch';
+      msg: 'tick % tick_spacing must be zero';
+    },
+    {
+      code: 6011;
+      name: 'invalidTickArray';
+      msg: 'Invalid tick array account';
+    },
+    {
+      code: 6012;
+      name: 'invalidTickArrayBoundary';
+      msg: 'Invalid tick array boundary';
+    },
+    {
+      code: 6013;
+      name: 'sqrtPriceLimitOverflow';
+      msg: 'Square root price limit overflow';
+    },
+    {
+      code: 6014;
+      name: 'sqrtPriceX64';
+      msg: 'sqrt_price_x64 out of range';
+    },
+    {
+      code: 6015;
+      name: 'liquiditySubValueErr';
+      msg: 'Liquidity sub delta L must be smaller than before';
+    },
+    {
+      code: 6016;
+      name: 'liquidityAddValueErr';
+      msg: 'Liquidity add delta L must be greater, or equal to before';
+    },
+    {
+      code: 6017;
+      name: 'invalidLiquidity';
+      msg: 'Invalid liquidity when update position';
+    },
+    {
+      code: 6018;
+      name: 'forbidBothZeroForSupplyLiquidity';
+      msg: 'Both token amount must not be zero while supply liquidity';
+    },
+    {
+      code: 6019;
+      name: 'liquidityInsufficient';
+      msg: 'Liquidity insufficient';
+    },
+    {
+      code: 6020;
+      name: 'transactionTooOld';
+      msg: 'Transaction too old';
+    },
+    {
+      code: 6021;
+      name: 'priceSlippageCheck';
+      msg: 'Price slippage check';
+    },
+    {
+      code: 6022;
+      name: 'tooLittleOutputReceived';
+      msg: 'Too little output received';
+    },
+    {
+      code: 6023;
+      name: 'tooMuchInputPaid';
+      msg: 'Too much input paid';
+    },
+    {
+      code: 6024;
+      name: 'zeroAmountSpecified';
+      msg: 'Swap special amount can not be zero';
+    },
+    {
+      code: 6025;
+      name: 'invalidInputPoolVault';
+      msg: 'Input pool vault is invalid';
+    },
+    {
+      code: 6026;
+      name: 'tooSmallInputOrOutputAmount';
+      msg: 'Swap input or output amount is too small';
+    },
+    {
+      code: 6027;
+      name: 'notEnoughTickArrayAccount';
+      msg: 'Not enought tick array account';
+    },
+    {
+      code: 6028;
+      name: 'invalidFirstTickArrayAccount';
+      msg: 'Invalid first tick array account';
+    },
+    {
+      code: 6029;
+      name: 'invalidRewardIndex';
+      msg: 'Invalid reward index';
+    },
+    {
+      code: 6030;
+      name: 'fullRewardInfo';
+      msg: 'The init reward token reach to the max';
+    },
+    {
+      code: 6031;
+      name: 'rewardTokenAlreadyInUse';
+      msg: 'The init reward token already in use';
+    },
+    {
+      code: 6032;
+      name: 'exceptRewardMint';
+      msg: 'The reward tokens must contain one of pool vault mint except the last reward';
+    },
+    {
+      code: 6033;
+      name: 'invalidRewardInitParam';
+      msg: 'Invalid reward init param';
+    },
+    {
+      code: 6034;
+      name: 'invalidRewardDesiredAmount';
+      msg: 'Invalid collect reward desired amount';
+    },
+    {
+      code: 6035;
+      name: 'invalidRewardInputAccountNumber';
+      msg: 'Invalid collect reward input account number';
+    },
+    {
+      code: 6036;
+      name: 'invalidRewardPeriod';
+      msg: 'Invalid reward period';
+    },
+    {
+      code: 6037;
+      name: 'notApproveUpdateRewardEmissiones';
+      msg: 'Modification of emissiones is allowed within 72 hours from the end of the previous cycle';
+    },
+    {
+      code: 6038;
+      name: 'unInitializedRewardInfo';
+      msg: 'uninitialized reward info';
+    },
+    {
+      code: 6039;
+      name: 'notSupportMint';
+      msg: 'Not support token_2022 mint extension';
+    },
+    {
+      code: 6040;
+      name: 'missingTickArrayBitmapExtensionAccount';
+      msg: 'Missing tickarray bitmap extension account';
+    },
+    {
+      code: 6041;
+      name: 'insufficientLiquidityForDirection';
+      msg: 'Insufficient liquidity for this direction';
+    },
+    {
+      code: 6042;
+      name: 'maxTokenOverflow';
+      msg: 'Max token overflow';
+    },
+    {
+      code: 6043;
+      name: 'calculateOverflow';
+      msg: 'Calculate overflow';
+    },
+    {
+      code: 6044;
+      name: 'transferFeeCalculateNotMatch';
+      msg: 'TransferFee calculate not match';
+    },
+    {
+      code: 6045;
+      name: 'illegalAccountOwner';
+      msg: 'invalid account owner';
+    },
+    {
+      code: 6046;
+      name: 'invalidAccount';
+      msg: 'Invalid account';
+    }
+  ];
+  types: [
+    {
+      name: 'ammAdminGroup';
+      docs: ['Holds the admin group information.'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'feeKeeper';
+            docs: ['the address who can hold the fee,', 'anyone can trigger the fee collection action,'];
+            type: 'pubkey';
+          },
+          {
+            name: 'rewardConfigManager';
+            docs: [
+              'the address who can config the reward(config, deposit, withdraw),',
+              'deposit reward, set the account who can deposit the reward, withdraw the remaining reward(withdraw)'
+            ];
+            type: 'pubkey';
+          },
+          {
+            name: 'rewardClaimManager';
+            docs: ['the address who can manage the offchain reward claim'];
+            type: 'pubkey';
+          },
+          {
+            name: 'poolManager';
+            docs: [
+              'the address who can manage the pool create action,',
+              "without this account's permission, no one can create a pool"
+            ];
+            type: 'pubkey';
+          },
+          {
+            name: 'emergencyManager';
+            docs: [
+              'the address who can manage the emergency action,',
+              'emergency action includes stop/resume the pool, stop/resume withdraw lp'
+            ];
+            type: 'pubkey';
+          },
+          {
+            name: 'normalManager';
+            docs: ['normal action manager,', 'such as create amm config, update amm config'];
+            type: 'pubkey';
+          },
+          {
+            name: 'pad';
+            docs: ['The space required for the account. may be used for future extensions.'];
+            type: {
+              array: ['pubkey', 6];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'ammConfig';
+      docs: ['Holds the current owner of the factory'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'bump';
+            docs: ['Bump to identify PDA'];
+            type: 'u8';
+          },
+          {
+            name: 'index';
+            type: 'u16';
+          },
+          {
+            name: 'owner';
+            docs: ['Address of the protocol owner'];
+            type: 'pubkey';
+          },
+          {
+            name: 'protocolFeeRate';
+            docs: ['The protocol fee'];
+            type: 'u32';
+          },
+          {
+            name: 'tradeFeeRate';
+            docs: ['The trade fee, denominated in hundredths of a bip (10^-6)'];
+            type: 'u32';
+          },
+          {
+            name: 'tickSpacing';
+            docs: ['The tick spacing'];
+            type: 'u16';
+          },
+          {
+            name: 'fundFeeRate';
+            docs: ['The fund fee, denominated in hundredths of a bip (10^-6)'];
+            type: 'u32';
+          },
+          {
+            name: 'paddingU32';
+            type: 'u32';
+          },
+          {
+            name: 'fundOwner';
+            type: 'pubkey';
+          },
+          {
+            name: 'padding';
+            type: {
+              array: ['u64', 3];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'collectPersonalFeeEvent';
+      docs: ['Emitted when tokens are collected for a position'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'positionNftMint';
+            docs: ['The ID of the token for which underlying tokens were collected'];
+            type: 'pubkey';
+          },
+          {
+            name: 'recipientTokenAccount0';
+            docs: ['The token account that received the collected token_0 tokens'];
+            type: 'pubkey';
+          },
+          {
+            name: 'recipientTokenAccount1';
+            docs: ['The token account that received the collected token_1 tokens'];
+            type: 'pubkey';
+          },
+          {
+            name: 'amount0';
+            docs: ['The amount of token_0 owed to the position that was collected'];
+            type: 'u64';
+          },
+          {
+            name: 'amount1';
+            docs: ['The amount of token_1 owed to the position that was collected'];
+            type: 'u64';
+          }
+        ];
+      };
+    },
+    {
+      name: 'collectProtocolFeeEvent';
+      docs: ['Emitted when the collected protocol fees are withdrawn by the factory owner'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'poolState';
+            docs: ['The pool whose protocol fee is collected'];
+            type: 'pubkey';
+          },
+          {
+            name: 'recipientTokenAccount0';
+            docs: ['The address that receives the collected token_0 protocol fees'];
+            type: 'pubkey';
+          },
+          {
+            name: 'recipientTokenAccount1';
+            docs: ['The address that receives the collected token_1 protocol fees'];
+            type: 'pubkey';
+          },
+          {
+            name: 'amount0';
+            docs: ['The amount of token_0 protocol fees that is withdrawn'];
+            type: 'u64';
+          },
+          {
+            name: 'amount1';
+            docs: ['The amount of token_0 protocol fees that is withdrawn'];
+            type: 'u64';
+          }
+        ];
+      };
+    },
+    {
+      name: 'configChangeEvent';
+      docs: ['Emitted when create or update a config'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'index';
+            type: 'u16';
+          },
+          {
+            name: 'owner';
+            type: 'pubkey';
+          },
+          {
+            name: 'protocolFeeRate';
+            type: 'u32';
+          },
+          {
+            name: 'tradeFeeRate';
+            type: 'u32';
+          },
+          {
+            name: 'tickSpacing';
+            type: 'u16';
+          },
+          {
+            name: 'fundFeeRate';
+            type: 'u32';
+          },
+          {
+            name: 'fundOwner';
+            type: 'pubkey';
+          }
+        ];
+      };
+    },
+    {
+      name: 'createPersonalPositionEvent';
+      docs: ['Emitted when create a new position'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'poolState';
+            docs: ['The pool for which liquidity was added'];
+            type: 'pubkey';
+          },
+          {
+            name: 'minter';
+            docs: ['The address that create the position'];
+            type: 'pubkey';
+          },
+          {
+            name: 'nftOwner';
+            docs: ['The owner of the position and recipient of any minted liquidity'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tickLowerIndex';
+            docs: ['The lower tick of the position'];
+            type: 'i32';
+          },
+          {
+            name: 'tickUpperIndex';
+            docs: ['The upper tick of the position'];
+            type: 'i32';
+          },
+          {
+            name: 'liquidity';
+            docs: ['The amount of liquidity minted to the position range'];
+            type: 'u128';
+          },
+          {
+            name: 'depositAmount0';
+            docs: ['The amount of token_0 was deposit for the liquidity'];
+            type: 'u64';
+          },
+          {
+            name: 'depositAmount1';
+            docs: ['The amount of token_1 was deposit for the liquidity'];
+            type: 'u64';
+          },
+          {
+            name: 'depositAmount0TransferFee';
+            docs: ['The token transfer fee for deposit_amount_0'];
+            type: 'u64';
+          },
+          {
+            name: 'depositAmount1TransferFee';
+            docs: ['The token transfer fee for deposit_amount_1'];
+            type: 'u64';
+          }
+        ];
+      };
+    },
+    {
+      name: 'decreaseLiquidityEvent';
+      docs: ['Emitted when liquidity is decreased.'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'positionNftMint';
+            docs: ['The ID of the token for which liquidity was decreased'];
+            type: 'pubkey';
+          },
+          {
+            name: 'liquidity';
+            docs: ['The amount by which liquidity for the position was decreased'];
+            type: 'u128';
+          },
+          {
+            name: 'decreaseAmount0';
+            docs: ['The amount of token_0 that was paid for the decrease in liquidity'];
+            type: 'u64';
+          },
+          {
+            name: 'decreaseAmount1';
+            docs: ['The amount of token_1 that was paid for the decrease in liquidity'];
+            type: 'u64';
+          },
+          {
+            name: 'feeAmount0';
+            type: 'u64';
+          },
+          {
+            name: 'feeAmount1';
+            docs: ['The amount of token_1 fee'];
+            type: 'u64';
+          },
+          {
+            name: 'rewardAmounts';
+            docs: ['The amount of rewards'];
+            type: {
+              array: ['u64', 3];
+            };
+          },
+          {
+            name: 'transferFee0';
+            docs: ['The amount of token_0 transfer fee'];
+            type: 'u64';
+          },
+          {
+            name: 'transferFee1';
+            docs: ['The amount of token_1 transfer fee'];
+            type: 'u64';
+          }
+        ];
+      };
+    },
+    {
+      name: 'increaseLiquidityEvent';
+      docs: ['Emitted when liquidity is increased.'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'positionNftMint';
+            docs: ['The ID of the token for which liquidity was increased'];
+            type: 'pubkey';
+          },
+          {
+            name: 'liquidity';
+            docs: ['The amount by which liquidity for the NFT position was increased'];
+            type: 'u128';
+          },
+          {
+            name: 'amount0';
+            docs: ['The amount of token_0 that was paid for the increase in liquidity'];
+            type: 'u64';
+          },
+          {
+            name: 'amount1';
+            docs: ['The amount of token_1 that was paid for the increase in liquidity'];
+            type: 'u64';
+          },
+          {
+            name: 'amount0TransferFee';
+            docs: ['The token transfer fee for amount_0'];
+            type: 'u64';
+          },
+          {
+            name: 'amount1TransferFee';
+            docs: ['The token transfer fee for amount_1'];
+            type: 'u64';
+          }
+        ];
+      };
+    },
+    {
+      name: 'initAdminGroupParams';
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'feeKeeper';
+            docs: ['the address who can hold the fee,', 'anyone can trigger the fee collection action,'];
+            type: 'pubkey';
+          },
+          {
+            name: 'rewardConfigManager';
+            docs: [
+              'the address who can config the reward(config, deposit, withdraw),',
+              'deposit reward, set the account who can deposit the reward, withdraw the remaining reward(withdraw)'
+            ];
+            type: 'pubkey';
+          },
+          {
+            name: 'rewardClaimManager';
+            docs: ['the address who can manage the offchain reward claim'];
+            type: 'pubkey';
+          },
+          {
+            name: 'poolManager';
+            docs: [
+              'the address who can manage the pool create action,',
+              "without this account's permission, no one can create a pool"
+            ];
+            type: 'pubkey';
+          },
+          {
+            name: 'emergencyManager';
+            docs: [
+              'the address who can manage the emergency action,',
+              'emergency action includes stop/resume the pool, stop/resume withdraw lp'
+            ];
+            type: 'pubkey';
+          },
+          {
+            name: 'normalManager';
+            docs: ['normal action manager,', 'such as create amm config, update amm config'];
+            type: 'pubkey';
+          }
+        ];
+      };
+    },
+    {
+      name: 'initializeRewardParam';
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'openTime';
+            docs: ['Reward open time'];
+            type: 'u64';
+          },
+          {
+            name: 'endTime';
+            docs: ['Reward end time'];
+            type: 'u64';
+          },
+          {
+            name: 'emissionsPerSecondX64';
+            docs: ['Token reward per second are earned per unit of liquidity'];
+            type: 'u128';
+          }
+        ];
+      };
+    },
+    {
+      name: 'liquidityCalculateEvent';
+      docs: ['Emitted when liquidity decreased or increase.'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'poolLiquidity';
+            docs: ['The pool liquidity before decrease or increase'];
+            type: 'u128';
+          },
+          {
+            name: 'poolSqrtPriceX64';
+            docs: ['The pool price when decrease or increase in liquidity'];
+            type: 'u128';
+          },
+          {
+            name: 'poolTick';
+            docs: ['The pool tick when decrease or increase in liquidity'];
+            type: 'i32';
+          },
+          {
+            name: 'calcAmount0';
+            docs: ['The amount of token_0 that was calculated for the decrease or increase in liquidity'];
+            type: 'u64';
+          },
+          {
+            name: 'calcAmount1';
+            docs: ['The amount of token_1 that was calculated for the decrease or increase in liquidity'];
+            type: 'u64';
+          },
+          {
+            name: 'tradeFeeOwed0';
+            type: 'u64';
+          },
+          {
+            name: 'tradeFeeOwed1';
+            docs: ['The amount of token_1 fee'];
+            type: 'u64';
+          },
+          {
+            name: 'transferFee0';
+            docs: ['The amount of token_0 transfer fee without trade_fee_amount_0'];
+            type: 'u64';
+          },
+          {
+            name: 'transferFee1';
+            docs: ['The amount of token_1 transfer fee without trade_fee_amount_0'];
+            type: 'u64';
+          }
+        ];
+      };
+    },
+    {
+      name: 'liquidityChangeEvent';
+      docs: ['Emitted pool liquidity change when increase and decrease liquidity'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'poolState';
+            docs: ['The pool for swap'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tick';
+            docs: ['The tick of the pool'];
+            type: 'i32';
+          },
+          {
+            name: 'tickLower';
+            docs: ['The tick lower of position'];
+            type: 'i32';
+          },
+          {
+            name: 'tickUpper';
+            docs: ['The tick lower of position'];
+            type: 'i32';
+          },
+          {
+            name: 'liquidityBefore';
+            docs: ['The liquidity of the pool before liquidity change'];
+            type: 'u128';
+          },
+          {
+            name: 'liquidityAfter';
+            docs: ['The liquidity of the pool after liquidity change'];
+            type: 'u128';
+          }
+        ];
+      };
+    },
+    {
+      name: 'modifyAmmAdminGroupEvent';
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'feeKeeper';
+            type: 'pubkey';
+          },
+          {
+            name: 'rewardConfigManager';
+            type: 'pubkey';
+          },
+          {
+            name: 'rewardClaimManager';
+            type: 'pubkey';
+          },
+          {
+            name: 'poolManager';
+            type: 'pubkey';
+          },
+          {
+            name: 'emergencyManager';
+            type: 'pubkey';
+          },
+          {
+            name: 'normalManager';
+            type: 'pubkey';
+          }
+        ];
+      };
+    },
+    {
+      name: 'observation';
+      docs: ['The element of observations in ObservationState'];
+      serialization: 'bytemuckunsafe';
+      repr: {
+        kind: 'c';
+        packed: true;
+      };
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'blockTimestamp';
+            docs: ['The block timestamp of the observation'];
+            type: 'u32';
+          },
+          {
+            name: 'tickCumulative';
+            docs: ['the cumulative of tick during the duration time'];
+            type: 'i64';
+          },
+          {
+            name: 'padding';
+            docs: ['padding for feature update'];
+            type: {
+              array: ['u64', 4];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'observationState';
+      serialization: 'bytemuckunsafe';
+      repr: {
+        kind: 'c';
+        packed: true;
+      };
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'initialized';
+            docs: ['Whether the ObservationState is initialized'];
+            type: 'bool';
+          },
+          {
+            name: 'recentEpoch';
+            docs: ['recent update epoch'];
+            type: 'u64';
+          },
+          {
+            name: 'observationIndex';
+            docs: ['the most-recently updated index of the observations array'];
+            type: 'u16';
+          },
+          {
+            name: 'poolId';
+            docs: ['belongs to which pool'];
+            type: 'pubkey';
+          },
+          {
+            name: 'observations';
+            docs: ['observation array'];
+            type: {
+              array: [
+                {
+                  defined: {
+                    name: 'observation';
+                  };
+                },
+                100
+              ];
+            };
+          },
+          {
+            name: 'padding';
+            docs: ['padding for feature update'];
+            type: {
+              array: ['u64', 4];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'offchainRewardConfig';
+      docs: ['Holds the current owner of the factory'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'poolId';
+            docs: ['the pool state address'];
+            type: 'pubkey';
+          },
+          {
+            name: 'rewardVault';
+            docs: ['the vault to hold the reward', 'vault address is this config address'];
+            type: 'pubkey';
+          },
+          {
+            name: 'vaultBump';
+            docs: ['Bump to identify vault PDA'];
+            type: {
+              array: ['u8', 1];
+            };
+          },
+          {
+            name: 'rewardMintVec';
+            docs: ['reward token mint address list', 'reward token account is ATA(reward_vault, reward_mint)'];
+            type: {
+              vec: 'pubkey';
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'operationState';
+      docs: ['Holds the current owner of the factory'];
+      serialization: 'bytemuckunsafe';
+      repr: {
+        kind: 'c';
+        packed: true;
+      };
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'bump';
+            docs: ['Bump to identify PDA'];
+            type: 'u8';
+          },
+          {
+            name: 'operationOwners';
+            docs: ['Address of the operation owner'];
+            type: {
+              array: ['pubkey', 10];
+            };
+          },
+          {
+            name: 'whitelistMints';
+            docs: ['The mint address of whitelist to emit reward'];
+            type: {
+              array: ['pubkey', 100];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'personalPositionState';
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'bump';
+            docs: ['Bump to identify PDA'];
+            type: {
+              array: ['u8', 1];
+            };
+          },
+          {
+            name: 'nftMint';
+            docs: ['Mint address of the tokenized position'];
+            type: 'pubkey';
+          },
+          {
+            name: 'poolId';
+            docs: ['The ID of the pool with which this token is connected'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tickLowerIndex';
+            docs: ['The lower bound tick of the position'];
+            type: 'i32';
+          },
+          {
+            name: 'tickUpperIndex';
+            docs: ['The upper bound tick of the position'];
+            type: 'i32';
+          },
+          {
+            name: 'liquidity';
+            docs: ['The amount of liquidity owned by this position'];
+            type: 'u128';
+          },
+          {
+            name: 'feeGrowthInside0LastX64';
+            docs: ['The token_0 fee growth of the aggregate position as of the last action on the individual position'];
+            type: 'u128';
+          },
+          {
+            name: 'feeGrowthInside1LastX64';
+            docs: ['The token_1 fee growth of the aggregate position as of the last action on the individual position'];
+            type: 'u128';
+          },
+          {
+            name: 'tokenFeesOwed0';
+            docs: ['The fees owed to the position owner in token_0, as of the last computation'];
+            type: 'u64';
+          },
+          {
+            name: 'tokenFeesOwed1';
+            docs: ['The fees owed to the position owner in token_1, as of the last computation'];
+            type: 'u64';
+          },
+          {
+            name: 'rewardInfos';
+            type: {
+              array: [
+                {
+                  defined: {
+                    name: 'positionRewardInfo';
+                  };
+                },
+                3
+              ];
+            };
+          },
+          {
+            name: 'recentEpoch';
+            type: 'u64';
+          },
+          {
+            name: 'padding';
+            type: {
+              array: ['u64', 7];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'poolCreatedEvent';
+      docs: ['Emitted when a pool is created and initialized with a starting price', ''];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'tokenMint0';
+            docs: ['The first token of the pool by address sort order'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tokenMint1';
+            docs: ['The second token of the pool by address sort order'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tickSpacing';
+            docs: ['The minimum number of ticks between initialized ticks'];
+            type: 'u16';
+          },
+          {
+            name: 'poolState';
+            docs: ['The address of the created pool'];
+            type: 'pubkey';
+          },
+          {
+            name: 'sqrtPriceX64';
+            docs: ['The initial sqrt price of the pool, as a Q64.64'];
+            type: 'u128';
+          },
+          {
+            name: 'tick';
+            docs: ['The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool'];
+            type: 'i32';
+          },
+          {
+            name: 'tokenVault0';
+            docs: ['Vault of token_0'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tokenVault1';
+            docs: ['Vault of token_1'];
+            type: 'pubkey';
+          }
+        ];
+      };
+    },
+    {
+      name: 'poolState';
+      docs: ['The pool state', '', 'PDA of `[POOL_SEED, config, token_mint_0, token_mint_1]`', ''];
+      serialization: 'bytemuckunsafe';
+      repr: {
+        kind: 'c';
+        packed: true;
+      };
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'bump';
+            docs: ['Bump to identify PDA'];
+            type: {
+              array: ['u8', 1];
+            };
+          },
+          {
+            name: 'ammConfig';
+            type: 'pubkey';
+          },
+          {
+            name: 'owner';
+            type: 'pubkey';
+          },
+          {
+            name: 'tokenMint0';
+            docs: ['Token pair of the pool, where token_mint_0 address < token_mint_1 address'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tokenMint1';
+            type: 'pubkey';
+          },
+          {
+            name: 'tokenVault0';
+            docs: ['Token pair vault'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tokenVault1';
+            type: 'pubkey';
+          },
+          {
+            name: 'observationKey';
+            docs: ['observation account key'];
+            type: 'pubkey';
+          },
+          {
+            name: 'mintDecimals0';
+            docs: ['mint0 and mint1 decimals'];
+            type: 'u8';
+          },
+          {
+            name: 'mintDecimals1';
+            type: 'u8';
+          },
+          {
+            name: 'tickSpacing';
+            docs: ['The minimum number of ticks between initialized ticks'];
+            type: 'u16';
+          },
+          {
+            name: 'liquidity';
+            docs: ['The currently in range liquidity available to the pool.'];
+            type: 'u128';
+          },
+          {
+            name: 'sqrtPriceX64';
+            docs: ['The current price of the pool as a sqrt(token_1/token_0) Q64.64 value'];
+            type: 'u128';
+          },
+          {
+            name: 'tickCurrent';
+            docs: ['The current tick of the pool, i.e. according to the last tick transition that was run.'];
+            type: 'i32';
+          },
+          {
+            name: 'padding3';
+            type: 'u16';
+          },
+          {
+            name: 'padding4';
+            type: 'u16';
+          },
+          {
+            name: 'feeGrowthGlobal0X64';
+            docs: [
+              'The fee growth as a Q64.64 number, i.e. fees of token_0 and token_1 collected per',
+              'unit of liquidity for the entire life of the pool.'
+            ];
+            type: 'u128';
+          },
+          {
+            name: 'feeGrowthGlobal1X64';
+            type: 'u128';
+          },
+          {
+            name: 'protocolFeesToken0';
+            docs: ['The amounts of token_0 and token_1 that are owed to the protocol.'];
+            type: 'u64';
+          },
+          {
+            name: 'protocolFeesToken1';
+            type: 'u64';
+          },
+          {
+            name: 'swapInAmountToken0';
+            docs: ['The amounts in and out of swap token_0 and token_1'];
+            type: 'u128';
+          },
+          {
+            name: 'swapOutAmountToken1';
+            type: 'u128';
+          },
+          {
+            name: 'swapInAmountToken1';
+            type: 'u128';
+          },
+          {
+            name: 'swapOutAmountToken0';
+            type: 'u128';
+          },
+          {
+            name: 'status';
+            docs: [
+              'Bitwise representation of the state of the pool',
+              'bit0, 1: disable open position and increase liquidity, 0: normal',
+              'bit1, 1: disable decrease liquidity, 0: normal',
+              'bit2, 1: disable collect fee, 0: normal',
+              'bit3, 1: disable collect reward, 0: normal',
+              'bit4, 1: disable swap, 0: normal'
+            ];
+            type: 'u8';
+          },
+          {
+            name: 'padding';
+            docs: ['Leave blank for future use'];
+            type: {
+              array: ['u8', 7];
+            };
+          },
+          {
+            name: 'rewardInfos';
+            type: {
+              array: [
+                {
+                  defined: {
+                    name: 'rewardInfo';
+                  };
+                },
+                3
+              ];
+            };
+          },
+          {
+            name: 'tickArrayBitmap';
+            docs: ['Packed initialized tick array state'];
+            type: {
+              array: ['u64', 16];
+            };
+          },
+          {
+            name: 'totalFeesToken0';
+            docs: ['except protocol_fee and fund_fee'];
+            type: 'u64';
+          },
+          {
+            name: 'totalFeesClaimedToken0';
+            docs: ['except protocol_fee and fund_fee'];
+            type: 'u64';
+          },
+          {
+            name: 'totalFeesToken1';
+            type: 'u64';
+          },
+          {
+            name: 'totalFeesClaimedToken1';
+            type: 'u64';
+          },
+          {
+            name: 'fundFeesToken0';
+            type: 'u64';
+          },
+          {
+            name: 'fundFeesToken1';
+            type: 'u64';
+          },
+          {
+            name: 'openTime';
+            type: 'u64';
+          },
+          {
+            name: 'recentEpoch';
+            type: 'u64';
+          },
+          {
+            name: 'padding1';
+            type: {
+              array: ['u64', 24];
+            };
+          },
+          {
+            name: 'padding2';
+            type: {
+              array: ['u64', 32];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'positionRewardInfo';
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'growthInsideLastX64';
+            type: 'u128';
+          },
+          {
+            name: 'rewardAmountOwed';
+            type: 'u64';
+          }
+        ];
+      };
+    },
+    {
+      name: 'protocolPositionState';
+      docs: ["Info stored for each user's position"];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'bump';
+            docs: ['Bump to identify PDA'];
+            type: 'u8';
+          },
+          {
+            name: 'poolId';
+            docs: ['The ID of the pool with which this token is connected'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tickLowerIndex';
+            docs: ['The lower bound tick of the position'];
+            type: 'i32';
+          },
+          {
+            name: 'tickUpperIndex';
+            docs: ['The upper bound tick of the position'];
+            type: 'i32';
+          },
+          {
+            name: 'liquidity';
+            docs: ['The amount of liquidity owned by this position'];
+            type: 'u128';
+          },
+          {
+            name: 'feeGrowthInside0LastX64';
+            docs: ['The token_0 fee growth per unit of liquidity as of the last update to liquidity or fees owed'];
+            type: 'u128';
+          },
+          {
+            name: 'feeGrowthInside1LastX64';
+            docs: ['The token_1 fee growth per unit of liquidity as of the last update to liquidity or fees owed'];
+            type: 'u128';
+          },
+          {
+            name: 'tokenFeesOwed0';
+            docs: ['The fees owed to the position owner in token_0'];
+            type: 'u64';
+          },
+          {
+            name: 'tokenFeesOwed1';
+            docs: ['The fees owed to the position owner in token_1'];
+            type: 'u64';
+          },
+          {
+            name: 'rewardGrowthInside';
+            docs: ['The reward growth per unit of liquidity as of the last update to liquidity'];
+            type: {
+              array: ['u128', 3];
+            };
+          },
+          {
+            name: 'recentEpoch';
+            type: 'u64';
+          },
+          {
+            name: 'padding';
+            type: {
+              array: ['u64', 7];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'rewardInfo';
+      serialization: 'bytemuckunsafe';
+      repr: {
+        kind: 'c';
+        packed: true;
+      };
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'rewardState';
+            docs: ['Reward state'];
+            type: 'u8';
+          },
+          {
+            name: 'openTime';
+            docs: ['Reward open time'];
+            type: 'u64';
+          },
+          {
+            name: 'endTime';
+            docs: ['Reward end time'];
+            type: 'u64';
+          },
+          {
+            name: 'lastUpdateTime';
+            docs: ['Reward last update time'];
+            type: 'u64';
+          },
+          {
+            name: 'emissionsPerSecondX64';
+            docs: ['Q64.64 number indicates how many tokens per second are earned per unit of liquidity.'];
+            type: 'u128';
+          },
+          {
+            name: 'rewardTotalEmissioned';
+            docs: ['The total amount of reward emissioned'];
+            type: 'u64';
+          },
+          {
+            name: 'rewardClaimed';
+            docs: ['The total amount of claimed reward'];
+            type: 'u64';
+          },
+          {
+            name: 'tokenMint';
+            docs: ['Reward token mint.'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tokenVault';
+            docs: ['Reward vault token account.'];
+            type: 'pubkey';
+          },
+          {
+            name: 'authority';
+            docs: ['The owner that has permission to set reward param'];
+            type: 'pubkey';
+          },
+          {
+            name: 'rewardGrowthGlobalX64';
+            docs: [
+              'Q64.64 number that tracks the total tokens earned per unit of liquidity since the reward',
+              'emissions were turned on.'
+            ];
+            type: 'u128';
+          }
+        ];
+      };
+    },
+    {
+      name: 'supportMintAssociated';
+      docs: ['Holds the current owner of the factory'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'bump';
+            docs: ['Bump to identify PDA'];
+            type: 'u8';
+          },
+          {
+            name: 'mint';
+            docs: ['Address of the supported token22 mint'];
+            type: 'pubkey';
+          },
+          {
+            name: 'padding';
+            type: {
+              array: ['u64', 8];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'swapEvent';
+      docs: ['Emitted by when a swap is performed for a pool'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'poolState';
+            docs: ['The pool for which token_0 and token_1 were swapped'];
+            type: 'pubkey';
+          },
+          {
+            name: 'sender';
+            docs: ['The address that initiated the swap call, and that received the callback'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tokenAccount0';
+            docs: [
+              'The payer token account in zero for one swaps, or the recipient token account',
+              'in one for zero swaps'
+            ];
+            type: 'pubkey';
+          },
+          {
+            name: 'tokenAccount1';
+            docs: [
+              'The payer token account in one for zero swaps, or the recipient token account',
+              'in zero for one swaps'
+            ];
+            type: 'pubkey';
+          },
+          {
+            name: 'amount0';
+            docs: ['The real delta amount of the token_0 of the pool or user'];
+            type: 'u64';
+          },
+          {
+            name: 'transferFee0';
+            docs: ['The transfer fee charged by the withheld_amount of the token_0'];
+            type: 'u64';
+          },
+          {
+            name: 'amount1';
+            docs: ['The real delta of the token_1 of the pool or user'];
+            type: 'u64';
+          },
+          {
+            name: 'transferFee1';
+            docs: ['The transfer fee charged by the withheld_amount of the token_1'];
+            type: 'u64';
+          },
+          {
+            name: 'zeroForOne';
+            docs: ['if true, amount_0 is negtive and amount_1 is positive'];
+            type: 'bool';
+          },
+          {
+            name: 'sqrtPriceX64';
+            docs: ['The sqrt(price) of the pool after the swap, as a Q64.64'];
+            type: 'u128';
+          },
+          {
+            name: 'liquidity';
+            docs: ['The liquidity of the pool after the swap'];
+            type: 'u128';
+          },
+          {
+            name: 'tick';
+            docs: ['The log base 1.0001 of price of the pool after the swap'];
+            type: 'i32';
+          }
+        ];
+      };
+    },
+    {
+      name: 'tickArrayBitmapExtension';
+      serialization: 'bytemuckunsafe';
+      repr: {
+        kind: 'c';
+        packed: true;
+      };
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'poolId';
+            type: 'pubkey';
+          },
+          {
+            name: 'positiveTickArrayBitmap';
+            docs: ['Packed initialized tick array state for start_tick_index is positive'];
+            type: {
+              array: [
+                {
+                  array: ['u64', 8];
+                },
+                14
+              ];
+            };
+          },
+          {
+            name: 'negativeTickArrayBitmap';
+            docs: ['Packed initialized tick array state for start_tick_index is negitive'];
+            type: {
+              array: [
+                {
+                  array: ['u64', 8];
+                },
+                14
+              ];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'tickArrayState';
+      serialization: 'bytemuckunsafe';
+      repr: {
+        kind: 'c';
+        packed: true;
+      };
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'poolId';
+            type: 'pubkey';
+          },
+          {
+            name: 'startTickIndex';
+            type: 'i32';
+          },
+          {
+            name: 'ticks';
+            type: {
+              array: [
+                {
+                  defined: {
+                    name: 'tickState';
+                  };
+                },
+                60
+              ];
+            };
+          },
+          {
+            name: 'initializedTickCount';
+            type: 'u8';
+          },
+          {
+            name: 'recentEpoch';
+            type: 'u64';
+          },
+          {
+            name: 'padding';
+            type: {
+              array: ['u8', 107];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'tickState';
+      serialization: 'bytemuckunsafe';
+      repr: {
+        kind: 'c';
+        packed: true;
+      };
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'tick';
+            type: 'i32';
+          },
+          {
+            name: 'liquidityNet';
+            docs: [
+              'Amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left)'
+            ];
+            type: 'i128';
+          },
+          {
+            name: 'liquidityGross';
+            docs: ['The total position liquidity that references this tick'];
+            type: 'u128';
+          },
+          {
+            name: 'feeGrowthOutside0X64';
+            docs: [
+              'Fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)',
+              'only has relative meaning, not absolute — the value depends on when the tick is initialized'
+            ];
+            type: 'u128';
+          },
+          {
+            name: 'feeGrowthOutside1X64';
+            type: 'u128';
+          },
+          {
+            name: 'rewardGrowthsOutsideX64';
+            type: {
+              array: ['u128', 3];
+            };
+          },
+          {
+            name: 'padding';
+            type: {
+              array: ['u32', 13];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'updateAdminGroupParams';
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'feeKeeper';
+            docs: ['the address who can hold the fee,', 'anyone can trigger the fee collection action,'];
+            type: {
+              option: 'pubkey';
+            };
+          },
+          {
+            name: 'rewardConfigManager';
+            docs: [
+              'the address who can config the reward(config, deposit, withdraw),',
+              'deposit reward, set the account who can deposit the reward, withdraw the remaining reward(withdraw)'
+            ];
+            type: {
+              option: 'pubkey';
+            };
+          },
+          {
+            name: 'rewardClaimManager';
+            docs: ['the address who can manage the offchain reward claim'];
+            type: {
+              option: 'pubkey';
+            };
+          },
+          {
+            name: 'poolManager';
+            docs: [
+              'the address who can manage the pool create action,',
+              "without this account's permission, no one can create a pool"
+            ];
+            type: {
+              option: 'pubkey';
+            };
+          },
+          {
+            name: 'emergencyManager';
+            docs: [
+              'the address who can manage the emergency action,',
+              'emergency action includes stop/resume the pool, stop/resume withdraw lp'
+            ];
+            type: {
+              option: 'pubkey';
+            };
+          },
+          {
+            name: 'normalManager';
+            docs: ['normal action manager,', 'such as create amm config, update amm config'];
+            type: {
+              option: 'pubkey';
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'updateRewardInfosEvent';
+      docs: ['Emitted when Reward are updated for a pool'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'rewardGrowthGlobalX64';
+            docs: ['Reward info'];
+            type: {
+              array: ['u128', 3];
+            };
+          }
+        ];
+      };
+    }
+  ];
+};

+ 4148 - 0
src/lib/byreal-clmm-sdk/src/instructions/target/types/raydium_amm_v3.ts

@@ -0,0 +1,4148 @@
+/**
+ * Program IDL in camelCase format in order to be used in JS/TS.
+ *
+ * Note that this is only a type helper and is not the actual IDL. The original
+ * IDL can be found at `target/idl/amm_v3.json`.
+ */
+export type AmmV3 = {
+  address: 'CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK';
+  metadata: {
+    name: 'ammV3';
+    version: '0.1.0';
+    spec: '0.1.0';
+    description: 'Anchor client and source for Raydium concentrated liquidity AMM';
+  };
+  instructions: [
+    {
+      name: 'closePosition';
+      docs: [
+        "Close the user's position and NFT account. If the NFT mint belongs to token2022, it will also be closed and the funds returned to the NFT owner.",
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        ''
+      ];
+      discriminator: [123, 134, 81, 0, 49, 68, 98, 98];
+      accounts: [
+        {
+          name: 'nftOwner';
+          docs: ['The position nft owner'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'positionNftMint';
+          docs: ['Mint address bound to the personal position.'];
+          writable: true;
+        },
+        {
+          name: 'positionNftAccount';
+          docs: ['User token account where position NFT be minted to'];
+          writable: true;
+        },
+        {
+          name: 'personalPosition';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'positionNftMint';
+              }
+            ];
+          };
+        },
+        {
+          name: 'systemProgram';
+          docs: ['System program to close the position state account'];
+          address: '11111111111111111111111111111111';
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['Token/Token2022 program to close token/mint account'];
+        }
+      ];
+      args: [];
+    },
+    {
+      name: 'collectFundFee';
+      docs: [
+        'Collect the fund fee accrued to the pool',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        '* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1',
+        '* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0',
+        ''
+      ];
+      discriminator: [167, 138, 78, 149, 223, 194, 6, 126];
+      accounts: [
+        {
+          name: 'owner';
+          docs: ['Only admin or fund_owner can collect fee now'];
+          signer: true;
+        },
+        {
+          name: 'poolState';
+          docs: ['Pool state stores accumulated protocol fee amount'];
+          writable: true;
+        },
+        {
+          name: 'ammConfig';
+          docs: ['Amm config account stores fund_owner'];
+        },
+        {
+          name: 'tokenVault0';
+          docs: ['The address that holds pool tokens for token_0'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault1';
+          docs: ['The address that holds pool tokens for token_1'];
+          writable: true;
+        },
+        {
+          name: 'vault0Mint';
+          docs: ['The mint of token vault 0'];
+        },
+        {
+          name: 'vault1Mint';
+          docs: ['The mint of token vault 1'];
+        },
+        {
+          name: 'recipientTokenAccount0';
+          docs: ['The address that receives the collected token_0 protocol fees'];
+          writable: true;
+        },
+        {
+          name: 'recipientTokenAccount1';
+          docs: ['The address that receives the collected token_1 protocol fees'];
+          writable: true;
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['The SPL program to perform token transfers'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'tokenProgram2022';
+          docs: ['The SPL program 2022 to perform token transfers'];
+          address: 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
+        }
+      ];
+      args: [
+        {
+          name: 'amount0Requested';
+          type: 'u64';
+        },
+        {
+          name: 'amount1Requested';
+          type: 'u64';
+        }
+      ];
+    },
+    {
+      name: 'collectProtocolFee';
+      docs: [
+        'Collect the protocol fee accrued to the pool',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        '* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1',
+        '* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0',
+        ''
+      ];
+      discriminator: [136, 136, 252, 221, 194, 66, 126, 89];
+      accounts: [
+        {
+          name: 'owner';
+          docs: ['Only admin or config owner can collect fee now'];
+          signer: true;
+        },
+        {
+          name: 'poolState';
+          docs: ['Pool state stores accumulated protocol fee amount'];
+          writable: true;
+        },
+        {
+          name: 'ammConfig';
+          docs: ['Amm config account stores owner'];
+        },
+        {
+          name: 'tokenVault0';
+          docs: ['The address that holds pool tokens for token_0'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault1';
+          docs: ['The address that holds pool tokens for token_1'];
+          writable: true;
+        },
+        {
+          name: 'vault0Mint';
+          docs: ['The mint of token vault 0'];
+        },
+        {
+          name: 'vault1Mint';
+          docs: ['The mint of token vault 1'];
+        },
+        {
+          name: 'recipientTokenAccount0';
+          docs: ['The address that receives the collected token_0 protocol fees'];
+          writable: true;
+        },
+        {
+          name: 'recipientTokenAccount1';
+          docs: ['The address that receives the collected token_1 protocol fees'];
+          writable: true;
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['The SPL program to perform token transfers'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'tokenProgram2022';
+          docs: ['The SPL program 2022 to perform token transfers'];
+          address: 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
+        }
+      ];
+      args: [
+        {
+          name: 'amount0Requested';
+          type: 'u64';
+        },
+        {
+          name: 'amount1Requested';
+          type: 'u64';
+        }
+      ];
+    },
+    {
+      name: 'collectRemainingRewards';
+      docs: [
+        'Collect remaining reward token for reward founder',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx`- The context of accounts',
+        '* `reward_index` - the index to reward info',
+        ''
+      ];
+      discriminator: [18, 237, 166, 197, 34, 16, 213, 144];
+      accounts: [
+        {
+          name: 'rewardFunder';
+          docs: ['The founder who init reward info previously'];
+          signer: true;
+        },
+        {
+          name: 'funderTokenAccount';
+          docs: ["The funder's reward token account"];
+          writable: true;
+        },
+        {
+          name: 'poolState';
+          docs: ['Set reward for this pool'];
+          writable: true;
+        },
+        {
+          name: 'rewardTokenVault';
+          docs: ['Reward vault transfer remaining token to founder token account'];
+        },
+        {
+          name: 'rewardVaultMint';
+          docs: ['The mint of reward token vault'];
+        },
+        {
+          name: 'tokenProgram';
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'tokenProgram2022';
+          docs: ['Token program 2022'];
+          address: 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
+        },
+        {
+          name: 'memoProgram';
+          docs: ['memo program'];
+          address: 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr';
+        }
+      ];
+      args: [
+        {
+          name: 'rewardIndex';
+          type: 'u8';
+        }
+      ];
+    },
+    {
+      name: 'createAmmConfig';
+      docs: [
+        '# Arguments',
+        '',
+        '* `ctx`- The accounts needed by instruction.',
+        '* `index` - The index of amm config, there may be multiple config.',
+        '* `tick_spacing` - The tickspacing binding with config, cannot be changed.',
+        '* `trade_fee_rate` - Trade fee rate, can be changed.',
+        '* `protocol_fee_rate` - The rate of protocol fee within trade fee.',
+        '* `fund_fee_rate` - The rate of fund fee within trade fee.',
+        ''
+      ];
+      discriminator: [137, 52, 237, 212, 215, 117, 108, 104];
+      accounts: [
+        {
+          name: 'owner';
+          docs: ['Address to be set as protocol owner.'];
+          writable: true;
+          signer: true;
+          address: 'GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ';
+        },
+        {
+          name: 'ammConfig';
+          docs: ['Initialize config state account to store protocol owner address and fee rates.'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [97, 109, 109, 95, 99, 111, 110, 102, 105, 103];
+              },
+              {
+                kind: 'arg';
+                path: 'index';
+              }
+            ];
+          };
+        },
+        {
+          name: 'systemProgram';
+          address: '11111111111111111111111111111111';
+        }
+      ];
+      args: [
+        {
+          name: 'index';
+          type: 'u16';
+        },
+        {
+          name: 'tickSpacing';
+          type: 'u16';
+        },
+        {
+          name: 'tradeFeeRate';
+          type: 'u32';
+        },
+        {
+          name: 'protocolFeeRate';
+          type: 'u32';
+        },
+        {
+          name: 'fundFeeRate';
+          type: 'u32';
+        }
+      ];
+    },
+    {
+      name: 'createOperationAccount';
+      docs: [
+        'Creates an operation account for the program',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx`- The context of accounts',
+        ''
+      ];
+      discriminator: [63, 87, 148, 33, 109, 35, 8, 104];
+      accounts: [
+        {
+          name: 'owner';
+          docs: ['Address to be set as operation account owner.'];
+          writable: true;
+          signer: true;
+          address: 'GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ';
+        },
+        {
+          name: 'operationState';
+          docs: ['Initialize operation state account to store operation owner address and white list mint.'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [111, 112, 101, 114, 97, 116, 105, 111, 110];
+              }
+            ];
+          };
+        },
+        {
+          name: 'systemProgram';
+          address: '11111111111111111111111111111111';
+        }
+      ];
+      args: [];
+    },
+    {
+      name: 'createPool';
+      docs: [
+        'Creates a pool for the given token pair and the initial price',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx`- The context of accounts',
+        '* `sqrt_price_x64` - the initial sqrt price (amount_token_1 / amount_token_0) of the pool as a Q64.64',
+        'Note: The open_time must be smaller than the current block_timestamp on chain.'
+      ];
+      discriminator: [233, 146, 209, 142, 207, 104, 64, 188];
+      accounts: [
+        {
+          name: 'poolCreator';
+          docs: ['Address paying to create the pool. Can be anyone'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'ammConfig';
+          docs: ['Which config the pool belongs to.'];
+        },
+        {
+          name: 'poolState';
+          docs: ['Initialize an account to store the pool state'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 111, 108];
+              },
+              {
+                kind: 'account';
+                path: 'ammConfig';
+              },
+              {
+                kind: 'account';
+                path: 'tokenMint0';
+              },
+              {
+                kind: 'account';
+                path: 'tokenMint1';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenMint0';
+          docs: ['Token_0 mint, the key must be smaller then token_1 mint.'];
+        },
+        {
+          name: 'tokenMint1';
+          docs: ['Token_1 mint'];
+        },
+        {
+          name: 'tokenVault0';
+          docs: ['Token_0 vault for the pool'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 111, 108, 95, 118, 97, 117, 108, 116];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'account';
+                path: 'tokenMint0';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenVault1';
+          docs: ['Token_1 vault for the pool'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 111, 108, 95, 118, 97, 117, 108, 116];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'account';
+                path: 'tokenMint1';
+              }
+            ];
+          };
+        },
+        {
+          name: 'observationState';
+          docs: ['Initialize an account to store oracle observations'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [111, 98, 115, 101, 114, 118, 97, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tickArrayBitmap';
+          docs: ['Initialize an account to store if a tick array is initialized.'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [
+                  112,
+                  111,
+                  111,
+                  108,
+                  95,
+                  116,
+                  105,
+                  99,
+                  107,
+                  95,
+                  97,
+                  114,
+                  114,
+                  97,
+                  121,
+                  95,
+                  98,
+                  105,
+                  116,
+                  109,
+                  97,
+                  112,
+                  95,
+                  101,
+                  120,
+                  116,
+                  101,
+                  110,
+                  115,
+                  105,
+                  111,
+                  110
+                ];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenProgram0';
+          docs: ['Spl token program or token program 2022'];
+        },
+        {
+          name: 'tokenProgram1';
+          docs: ['Spl token program or token program 2022'];
+        },
+        {
+          name: 'systemProgram';
+          docs: ['To create a new program account'];
+          address: '11111111111111111111111111111111';
+        },
+        {
+          name: 'rent';
+          docs: ['Sysvar for program account'];
+          address: 'SysvarRent111111111111111111111111111111111';
+        }
+      ];
+      args: [
+        {
+          name: 'sqrtPriceX64';
+          type: 'u128';
+        },
+        {
+          name: 'openTime';
+          type: 'u64';
+        }
+      ];
+    },
+    {
+      name: 'createSupportMintAssociated';
+      docs: [
+        'Create support token22 mint account which can create pool and send rewards with ignoring the not support extensions.'
+      ];
+      discriminator: [17, 251, 65, 92, 136, 242, 14, 169];
+      accounts: [
+        {
+          name: 'owner';
+          docs: ['Address to be set as protocol owner.'];
+          writable: true;
+          signer: true;
+          address: 'GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ';
+        },
+        {
+          name: 'tokenMint';
+          docs: ['Support token mint'];
+        },
+        {
+          name: 'supportMintAssociated';
+          docs: ['Initialize support mint state account to store support mint address and bump.'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [115, 117, 112, 112, 111, 114, 116, 95, 109, 105, 110, 116];
+              },
+              {
+                kind: 'account';
+                path: 'tokenMint';
+              }
+            ];
+          };
+        },
+        {
+          name: 'systemProgram';
+          address: '11111111111111111111111111111111';
+        }
+      ];
+      args: [];
+    },
+    {
+      name: 'decreaseLiquidity';
+      docs: [
+        '#[deprecated(note = "Use `decrease_liquidity_v2` instead.")]',
+        'Decreases liquidity for an existing position',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` -  The context of accounts',
+        '* `liquidity` - The amount by which liquidity will be decreased',
+        '* `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity',
+        '* `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity',
+        ''
+      ];
+      discriminator: [160, 38, 208, 111, 104, 91, 44, 1];
+      accounts: [
+        {
+          name: 'nftOwner';
+          docs: ['The position owner or delegated authority'];
+          signer: true;
+        },
+        {
+          name: 'nftAccount';
+          docs: ['The token account for the tokenized position'];
+        },
+        {
+          name: 'personalPosition';
+          docs: ['Decrease liquidity for this position'];
+          writable: true;
+        },
+        {
+          name: 'poolState';
+          writable: true;
+        },
+        {
+          name: 'protocolPosition';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'account';
+                path: 'personal_position.tick_lower_index';
+                account: 'personalPositionState';
+              },
+              {
+                kind: 'account';
+                path: 'personal_position.tick_upper_index';
+                account: 'personalPositionState';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenVault0';
+          docs: ['Token_0 vault'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault1';
+          docs: ['Token_1 vault'];
+          writable: true;
+        },
+        {
+          name: 'tickArrayLower';
+          docs: ['Stores init state for the lower tick'];
+          writable: true;
+        },
+        {
+          name: 'tickArrayUpper';
+          docs: ['Stores init state for the upper tick'];
+          writable: true;
+        },
+        {
+          name: 'recipientTokenAccount0';
+          docs: ['The destination token account for receive amount_0'];
+          writable: true;
+        },
+        {
+          name: 'recipientTokenAccount1';
+          docs: ['The destination token account for receive amount_1'];
+          writable: true;
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['SPL program to transfer out tokens'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        }
+      ];
+      args: [
+        {
+          name: 'liquidity';
+          type: 'u128';
+        },
+        {
+          name: 'amount0Min';
+          type: 'u64';
+        },
+        {
+          name: 'amount1Min';
+          type: 'u64';
+        }
+      ];
+    },
+    {
+      name: 'decreaseLiquidityV2';
+      docs: [
+        'Decreases liquidity for an existing position, support Token2022',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` -  The context of accounts',
+        '* `liquidity` - The amount by which liquidity will be decreased',
+        '* `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity',
+        '* `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity',
+        ''
+      ];
+      discriminator: [58, 127, 188, 62, 79, 82, 196, 96];
+      accounts: [
+        {
+          name: 'nftOwner';
+          docs: ['The position owner or delegated authority'];
+          signer: true;
+        },
+        {
+          name: 'nftAccount';
+          docs: ['The token account for the tokenized position'];
+        },
+        {
+          name: 'personalPosition';
+          docs: ['Decrease liquidity for this position'];
+          writable: true;
+        },
+        {
+          name: 'poolState';
+          writable: true;
+        },
+        {
+          name: 'protocolPosition';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'account';
+                path: 'personal_position.tick_lower_index';
+                account: 'personalPositionState';
+              },
+              {
+                kind: 'account';
+                path: 'personal_position.tick_upper_index';
+                account: 'personalPositionState';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenVault0';
+          docs: ['Token_0 vault'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault1';
+          docs: ['Token_1 vault'];
+          writable: true;
+        },
+        {
+          name: 'tickArrayLower';
+          docs: ['Stores init state for the lower tick'];
+          writable: true;
+        },
+        {
+          name: 'tickArrayUpper';
+          docs: ['Stores init state for the upper tick'];
+          writable: true;
+        },
+        {
+          name: 'recipientTokenAccount0';
+          docs: ['The destination token account for receive amount_0'];
+          writable: true;
+        },
+        {
+          name: 'recipientTokenAccount1';
+          docs: ['The destination token account for receive amount_1'];
+          writable: true;
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['SPL program to transfer out tokens'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'tokenProgram2022';
+          docs: ['Token program 2022'];
+          address: 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
+        },
+        {
+          name: 'memoProgram';
+          docs: ['memo program'];
+          address: 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr';
+        },
+        {
+          name: 'vault0Mint';
+          docs: ['The mint of token vault 0'];
+        },
+        {
+          name: 'vault1Mint';
+          docs: ['The mint of token vault 1'];
+        }
+      ];
+      args: [
+        {
+          name: 'liquidity';
+          type: 'u128';
+        },
+        {
+          name: 'amount0Min';
+          type: 'u64';
+        },
+        {
+          name: 'amount1Min';
+          type: 'u64';
+        }
+      ];
+    },
+    {
+      name: 'increaseLiquidity';
+      docs: [
+        '#[deprecated(note = "Use `increase_liquidity_v2` instead.")]',
+        'Increases liquidity for an existing position, with amount paid by `payer`',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        "* `liquidity` - The desired liquidity to be added, can't be zero",
+        '* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check',
+        '* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check',
+        ''
+      ];
+      discriminator: [46, 156, 243, 118, 13, 205, 251, 178];
+      accounts: [
+        {
+          name: 'nftOwner';
+          docs: ['Pays to mint the position'];
+          signer: true;
+        },
+        {
+          name: 'nftAccount';
+          docs: ['The token account for nft'];
+        },
+        {
+          name: 'poolState';
+          writable: true;
+        },
+        {
+          name: 'protocolPosition';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'account';
+                path: 'personal_position.tick_lower_index';
+                account: 'personalPositionState';
+              },
+              {
+                kind: 'account';
+                path: 'personal_position.tick_upper_index';
+                account: 'personalPositionState';
+              }
+            ];
+          };
+        },
+        {
+          name: 'personalPosition';
+          docs: ['Increase liquidity for this position'];
+          writable: true;
+        },
+        {
+          name: 'tickArrayLower';
+          docs: ['Stores init state for the lower tick'];
+          writable: true;
+        },
+        {
+          name: 'tickArrayUpper';
+          docs: ['Stores init state for the upper tick'];
+          writable: true;
+        },
+        {
+          name: 'tokenAccount0';
+          docs: ["The payer's token account for token_0"];
+          writable: true;
+        },
+        {
+          name: 'tokenAccount1';
+          docs: ['The token account spending token_1 to mint the position'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault0';
+          docs: ['The address that holds pool tokens for token_0'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault1';
+          docs: ['The address that holds pool tokens for token_1'];
+          writable: true;
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['Program to create mint account and mint tokens'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        }
+      ];
+      args: [
+        {
+          name: 'liquidity';
+          type: 'u128';
+        },
+        {
+          name: 'amount0Max';
+          type: 'u64';
+        },
+        {
+          name: 'amount1Max';
+          type: 'u64';
+        }
+      ];
+    },
+    {
+      name: 'increaseLiquidityV2';
+      docs: [
+        'Increases liquidity for an existing position, with amount paid by `payer`, support Token2022',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        '* `liquidity` - The desired liquidity to be added, if zero, calculate liquidity base amount_0 or amount_1 according base_flag',
+        '* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check',
+        '* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check',
+        '* `base_flag` - must be specified if liquidity is zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max',
+        ''
+      ];
+      discriminator: [133, 29, 89, 223, 69, 238, 176, 10];
+      accounts: [
+        {
+          name: 'nftOwner';
+          docs: ['Pays to mint the position'];
+          signer: true;
+        },
+        {
+          name: 'nftAccount';
+          docs: ['The token account for nft'];
+        },
+        {
+          name: 'poolState';
+          writable: true;
+        },
+        {
+          name: 'protocolPosition';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'account';
+                path: 'personal_position.tick_lower_index';
+                account: 'personalPositionState';
+              },
+              {
+                kind: 'account';
+                path: 'personal_position.tick_upper_index';
+                account: 'personalPositionState';
+              }
+            ];
+          };
+        },
+        {
+          name: 'personalPosition';
+          docs: ['Increase liquidity for this position'];
+          writable: true;
+        },
+        {
+          name: 'tickArrayLower';
+          docs: ['Stores init state for the lower tick'];
+          writable: true;
+        },
+        {
+          name: 'tickArrayUpper';
+          docs: ['Stores init state for the upper tick'];
+          writable: true;
+        },
+        {
+          name: 'tokenAccount0';
+          docs: ["The payer's token account for token_0"];
+          writable: true;
+        },
+        {
+          name: 'tokenAccount1';
+          docs: ['The token account spending token_1 to mint the position'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault0';
+          docs: ['The address that holds pool tokens for token_0'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault1';
+          docs: ['The address that holds pool tokens for token_1'];
+          writable: true;
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['Program to create mint account and mint tokens'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'tokenProgram2022';
+          docs: ['Token program 2022'];
+          address: 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
+        },
+        {
+          name: 'vault0Mint';
+          docs: ['The mint of token vault 0'];
+        },
+        {
+          name: 'vault1Mint';
+          docs: ['The mint of token vault 1'];
+        }
+      ];
+      args: [
+        {
+          name: 'liquidity';
+          type: 'u128';
+        },
+        {
+          name: 'amount0Max';
+          type: 'u64';
+        },
+        {
+          name: 'amount1Max';
+          type: 'u64';
+        },
+        {
+          name: 'baseFlag';
+          type: {
+            option: 'bool';
+          };
+        }
+      ];
+    },
+    {
+      name: 'initializeReward';
+      docs: [
+        'Initialize a reward info for a given pool and reward index',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx`- The context of accounts',
+        '* `reward_index` - the index to reward info',
+        '* `open_time` - reward open timestamp',
+        '* `end_time` - reward end timestamp',
+        '* `emissions_per_second_x64` - Token reward per second are earned per unit of liquidity.',
+        ''
+      ];
+      discriminator: [95, 135, 192, 196, 242, 129, 230, 68];
+      accounts: [
+        {
+          name: 'rewardFunder';
+          docs: ['The founder deposit reward token to vault'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'funderTokenAccount';
+          writable: true;
+        },
+        {
+          name: 'ammConfig';
+          docs: ['For check the reward_funder authority'];
+        },
+        {
+          name: 'poolState';
+          docs: ['Set reward for this pool'];
+          writable: true;
+        },
+        {
+          name: 'operationState';
+          docs: ['load info from the account to judge reward permission'];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [111, 112, 101, 114, 97, 116, 105, 111, 110];
+              }
+            ];
+          };
+        },
+        {
+          name: 'rewardTokenMint';
+          docs: ['Reward mint'];
+        },
+        {
+          name: 'rewardTokenVault';
+          docs: ['A pda, reward vault'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 111, 108, 95, 114, 101, 119, 97, 114, 100, 95, 118, 97, 117, 108, 116];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'account';
+                path: 'rewardTokenMint';
+              }
+            ];
+          };
+        },
+        {
+          name: 'rewardTokenProgram';
+        },
+        {
+          name: 'systemProgram';
+          address: '11111111111111111111111111111111';
+        },
+        {
+          name: 'rent';
+          address: 'SysvarRent111111111111111111111111111111111';
+        }
+      ];
+      args: [
+        {
+          name: 'param';
+          type: {
+            defined: {
+              name: 'initializeRewardParam';
+            };
+          };
+        }
+      ];
+    },
+    {
+      name: 'openPosition';
+      docs: [
+        '#[deprecated(note = "Use `open_position_with_token22_nft` instead.")]',
+        'Creates a new position wrapped in a NFT',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        '* `tick_lower_index` - The low boundary of market',
+        '* `tick_upper_index` - The upper boundary of market',
+        '* `tick_array_lower_start_index` - The start index of tick array which include tick low',
+        '* `tick_array_upper_start_index` - The start index of tick array which include tick upper',
+        '* `liquidity` - The liquidity to be added',
+        '* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check',
+        '* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check',
+        ''
+      ];
+      discriminator: [135, 128, 47, 77, 15, 152, 240, 49];
+      accounts: [
+        {
+          name: 'payer';
+          docs: ['Pays to mint the position'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'positionNftOwner';
+        },
+        {
+          name: 'positionNftMint';
+          docs: ['Unique token mint address'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'positionNftAccount';
+          docs: [
+            'Token account where position NFT will be minted',
+            'This account created in the contract by cpi to avoid large stack variables'
+          ];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'account';
+                path: 'positionNftOwner';
+              },
+              {
+                kind: 'const';
+                value: [
+                  6,
+                  221,
+                  246,
+                  225,
+                  215,
+                  101,
+                  161,
+                  147,
+                  217,
+                  203,
+                  225,
+                  70,
+                  206,
+                  235,
+                  121,
+                  172,
+                  28,
+                  180,
+                  133,
+                  237,
+                  95,
+                  91,
+                  55,
+                  145,
+                  58,
+                  140,
+                  245,
+                  133,
+                  126,
+                  255,
+                  0,
+                  169
+                ];
+              },
+              {
+                kind: 'account';
+                path: 'positionNftMint';
+              }
+            ];
+            program: {
+              kind: 'const';
+              value: [
+                140,
+                151,
+                37,
+                143,
+                78,
+                36,
+                137,
+                241,
+                187,
+                61,
+                16,
+                41,
+                20,
+                142,
+                13,
+                131,
+                11,
+                90,
+                19,
+                153,
+                218,
+                255,
+                16,
+                132,
+                4,
+                142,
+                123,
+                216,
+                219,
+                233,
+                248,
+                89
+              ];
+            };
+          };
+        },
+        {
+          name: 'metadataAccount';
+          docs: ['To store metaplex metadata'];
+          writable: true;
+        },
+        {
+          name: 'poolState';
+          docs: ['Add liquidity for this pool'];
+          writable: true;
+        },
+        {
+          name: 'protocolPosition';
+          docs: ['Store the information of market marking in range'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'arg';
+                path: 'tickLowerIndex';
+              },
+              {
+                kind: 'arg';
+                path: 'tickUpperIndex';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tickArrayLower';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [116, 105, 99, 107, 95, 97, 114, 114, 97, 121];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'arg';
+                path: 'tickArrayLowerStartIndex';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tickArrayUpper';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [116, 105, 99, 107, 95, 97, 114, 114, 97, 121];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'arg';
+                path: 'tickArrayUpperStartIndex';
+              }
+            ];
+          };
+        },
+        {
+          name: 'personalPosition';
+          docs: ['personal position state'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'positionNftMint';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenAccount0';
+          docs: ['The token_0 account deposit token to the pool'];
+          writable: true;
+        },
+        {
+          name: 'tokenAccount1';
+          docs: ['The token_1 account deposit token to the pool'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault0';
+          docs: ['The address that holds pool tokens for token_0'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault1';
+          docs: ['The address that holds pool tokens for token_1'];
+          writable: true;
+        },
+        {
+          name: 'rent';
+          docs: ['Sysvar for token mint and ATA creation'];
+          address: 'SysvarRent111111111111111111111111111111111';
+        },
+        {
+          name: 'systemProgram';
+          docs: ['Program to create the position manager state account'];
+          address: '11111111111111111111111111111111';
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['Program to create mint account and mint tokens'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'associatedTokenProgram';
+          docs: ['Program to create an ATA for receiving position NFT'];
+          address: 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL';
+        },
+        {
+          name: 'metadataProgram';
+          docs: ['Program to create NFT metadata'];
+          address: 'metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s';
+        }
+      ];
+      args: [
+        {
+          name: 'tickLowerIndex';
+          type: 'i32';
+        },
+        {
+          name: 'tickUpperIndex';
+          type: 'i32';
+        },
+        {
+          name: 'tickArrayLowerStartIndex';
+          type: 'i32';
+        },
+        {
+          name: 'tickArrayUpperStartIndex';
+          type: 'i32';
+        },
+        {
+          name: 'liquidity';
+          type: 'u128';
+        },
+        {
+          name: 'amount0Max';
+          type: 'u64';
+        },
+        {
+          name: 'amount1Max';
+          type: 'u64';
+        }
+      ];
+    },
+    {
+      name: 'openPositionV2';
+      docs: [
+        '#[deprecated(note = "Use `open_position_with_token22_nft` instead.")]',
+        'Creates a new position wrapped in a NFT, support Token2022',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        '* `tick_lower_index` - The low boundary of market',
+        '* `tick_upper_index` - The upper boundary of market',
+        '* `tick_array_lower_start_index` - The start index of tick array which include tick low',
+        '* `tick_array_upper_start_index` - The start index of tick array which include tick upper',
+        '* `liquidity` - The liquidity to be added, if zero, and the base_flag is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity',
+        '* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check',
+        '* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check',
+        '* `with_metadata` - The flag indicating whether to create NFT mint metadata',
+        '* `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max',
+        ''
+      ];
+      discriminator: [77, 184, 74, 214, 112, 86, 241, 199];
+      accounts: [
+        {
+          name: 'payer';
+          docs: ['Pays to mint the position'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'positionNftOwner';
+        },
+        {
+          name: 'positionNftMint';
+          docs: ['Unique token mint address'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'positionNftAccount';
+          docs: ['Token account where position NFT will be minted'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'account';
+                path: 'positionNftOwner';
+              },
+              {
+                kind: 'const';
+                value: [
+                  6,
+                  221,
+                  246,
+                  225,
+                  215,
+                  101,
+                  161,
+                  147,
+                  217,
+                  203,
+                  225,
+                  70,
+                  206,
+                  235,
+                  121,
+                  172,
+                  28,
+                  180,
+                  133,
+                  237,
+                  95,
+                  91,
+                  55,
+                  145,
+                  58,
+                  140,
+                  245,
+                  133,
+                  126,
+                  255,
+                  0,
+                  169
+                ];
+              },
+              {
+                kind: 'account';
+                path: 'positionNftMint';
+              }
+            ];
+            program: {
+              kind: 'const';
+              value: [
+                140,
+                151,
+                37,
+                143,
+                78,
+                36,
+                137,
+                241,
+                187,
+                61,
+                16,
+                41,
+                20,
+                142,
+                13,
+                131,
+                11,
+                90,
+                19,
+                153,
+                218,
+                255,
+                16,
+                132,
+                4,
+                142,
+                123,
+                216,
+                219,
+                233,
+                248,
+                89
+              ];
+            };
+          };
+        },
+        {
+          name: 'metadataAccount';
+          docs: ['To store metaplex metadata'];
+          writable: true;
+        },
+        {
+          name: 'poolState';
+          docs: ['Add liquidity for this pool'];
+          writable: true;
+        },
+        {
+          name: 'protocolPosition';
+          docs: ['Store the information of market marking in range'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'arg';
+                path: 'tickLowerIndex';
+              },
+              {
+                kind: 'arg';
+                path: 'tickUpperIndex';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tickArrayLower';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [116, 105, 99, 107, 95, 97, 114, 114, 97, 121];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'arg';
+                path: 'tickArrayLowerStartIndex';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tickArrayUpper';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [116, 105, 99, 107, 95, 97, 114, 114, 97, 121];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'arg';
+                path: 'tickArrayUpperStartIndex';
+              }
+            ];
+          };
+        },
+        {
+          name: 'personalPosition';
+          docs: ['personal position state'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'positionNftMint';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenAccount0';
+          docs: ['The token_0 account deposit token to the pool'];
+          writable: true;
+        },
+        {
+          name: 'tokenAccount1';
+          docs: ['The token_1 account deposit token to the pool'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault0';
+          docs: ['The address that holds pool tokens for token_0'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault1';
+          docs: ['The address that holds pool tokens for token_1'];
+          writable: true;
+        },
+        {
+          name: 'rent';
+          docs: ['Sysvar for token mint and ATA creation'];
+          address: 'SysvarRent111111111111111111111111111111111';
+        },
+        {
+          name: 'systemProgram';
+          docs: ['Program to create the position manager state account'];
+          address: '11111111111111111111111111111111';
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['Program to create mint account and mint tokens'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'associatedTokenProgram';
+          docs: ['Program to create an ATA for receiving position NFT'];
+          address: 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL';
+        },
+        {
+          name: 'metadataProgram';
+          docs: ['Program to create NFT metadata'];
+          address: 'metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s';
+        },
+        {
+          name: 'tokenProgram2022';
+          docs: ['Program to create mint account and mint tokens'];
+          address: 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
+        },
+        {
+          name: 'vault0Mint';
+          docs: ['The mint of token vault 0'];
+        },
+        {
+          name: 'vault1Mint';
+          docs: ['The mint of token vault 1'];
+        }
+      ];
+      args: [
+        {
+          name: 'tickLowerIndex';
+          type: 'i32';
+        },
+        {
+          name: 'tickUpperIndex';
+          type: 'i32';
+        },
+        {
+          name: 'tickArrayLowerStartIndex';
+          type: 'i32';
+        },
+        {
+          name: 'tickArrayUpperStartIndex';
+          type: 'i32';
+        },
+        {
+          name: 'liquidity';
+          type: 'u128';
+        },
+        {
+          name: 'amount0Max';
+          type: 'u64';
+        },
+        {
+          name: 'amount1Max';
+          type: 'u64';
+        },
+        {
+          name: 'withMetadata';
+          type: 'bool';
+        },
+        {
+          name: 'baseFlag';
+          type: {
+            option: 'bool';
+          };
+        }
+      ];
+    },
+    {
+      name: 'openPositionWithToken22Nft';
+      docs: [
+        'Creates a new position wrapped in a Token2022 NFT without relying on metadata_program and metadata_account, reduce the cost for user to create a personal position.',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        '* `tick_lower_index` - The low boundary of market',
+        '* `tick_upper_index` - The upper boundary of market',
+        '* `tick_array_lower_start_index` - The start index of tick array which include tick low',
+        '* `tick_array_upper_start_index` - The start index of tick array which include tick upper',
+        '* `liquidity` - The liquidity to be added, if zero, and the base_flag is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity',
+        '* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check',
+        '* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check',
+        '* `with_metadata` - The flag indicating whether to create NFT mint metadata',
+        '* `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max',
+        ''
+      ];
+      discriminator: [77, 255, 174, 82, 125, 29, 201, 46];
+      accounts: [
+        {
+          name: 'payer';
+          docs: ['Pays to mint the position'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'positionNftOwner';
+        },
+        {
+          name: 'positionNftMint';
+          docs: ['Unique token mint address, initialize in contract'];
+          writable: true;
+          signer: true;
+        },
+        {
+          name: 'positionNftAccount';
+          writable: true;
+        },
+        {
+          name: 'poolState';
+          docs: ['Add liquidity for this pool'];
+          writable: true;
+        },
+        {
+          name: 'protocolPosition';
+          docs: ['Store the information of market marking in range'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'arg';
+                path: 'tickLowerIndex';
+              },
+              {
+                kind: 'arg';
+                path: 'tickUpperIndex';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tickArrayLower';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [116, 105, 99, 107, 95, 97, 114, 114, 97, 121];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'arg';
+                path: 'tickArrayLowerStartIndex';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tickArrayUpper';
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [116, 105, 99, 107, 95, 97, 114, 114, 97, 121];
+              },
+              {
+                kind: 'account';
+                path: 'poolState';
+              },
+              {
+                kind: 'arg';
+                path: 'tickArrayUpperStartIndex';
+              }
+            ];
+          };
+        },
+        {
+          name: 'personalPosition';
+          docs: ['personal position state'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [112, 111, 115, 105, 116, 105, 111, 110];
+              },
+              {
+                kind: 'account';
+                path: 'positionNftMint';
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenAccount0';
+          docs: ['The token_0 account deposit token to the pool'];
+          writable: true;
+        },
+        {
+          name: 'tokenAccount1';
+          docs: ['The token_1 account deposit token to the pool'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault0';
+          docs: ['The address that holds pool tokens for token_0'];
+          writable: true;
+        },
+        {
+          name: 'tokenVault1';
+          docs: ['The address that holds pool tokens for token_1'];
+          writable: true;
+        },
+        {
+          name: 'rent';
+          docs: ['Sysvar for token mint and ATA creation'];
+          address: 'SysvarRent111111111111111111111111111111111';
+        },
+        {
+          name: 'systemProgram';
+          docs: ['Program to create the position manager state account'];
+          address: '11111111111111111111111111111111';
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['Program to transfer for token account'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'associatedTokenProgram';
+          docs: ['Program to create an ATA for receiving position NFT'];
+          address: 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL';
+        },
+        {
+          name: 'tokenProgram2022';
+          docs: ['Program to create NFT mint/token account and transfer for token22 account'];
+          address: 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
+        },
+        {
+          name: 'vault0Mint';
+          docs: ['The mint of token vault 0'];
+        },
+        {
+          name: 'vault1Mint';
+          docs: ['The mint of token vault 1'];
+        }
+      ];
+      args: [
+        {
+          name: 'tickLowerIndex';
+          type: 'i32';
+        },
+        {
+          name: 'tickUpperIndex';
+          type: 'i32';
+        },
+        {
+          name: 'tickArrayLowerStartIndex';
+          type: 'i32';
+        },
+        {
+          name: 'tickArrayUpperStartIndex';
+          type: 'i32';
+        },
+        {
+          name: 'liquidity';
+          type: 'u128';
+        },
+        {
+          name: 'amount0Max';
+          type: 'u64';
+        },
+        {
+          name: 'amount1Max';
+          type: 'u64';
+        },
+        {
+          name: 'withMetadata';
+          type: 'bool';
+        },
+        {
+          name: 'baseFlag';
+          type: {
+            option: 'bool';
+          };
+        }
+      ];
+    },
+    {
+      name: 'setRewardParams';
+      docs: [
+        'Reset reward param, start a new reward cycle or extend the current cycle.',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        '* `reward_index` - The index of reward token in the pool.',
+        '* `emissions_per_second_x64` - The per second emission reward, when extend the current cycle,',
+        "new value can't be less than old value",
+        '* `open_time` - reward open timestamp, must be set when starting a new cycle',
+        '* `end_time` - reward end timestamp',
+        ''
+      ];
+      discriminator: [112, 52, 167, 75, 32, 201, 211, 137];
+      accounts: [
+        {
+          name: 'authority';
+          docs: ['Address to be set as protocol owner. It pays to create factory state account.'];
+          signer: true;
+        },
+        {
+          name: 'ammConfig';
+        },
+        {
+          name: 'poolState';
+          writable: true;
+        },
+        {
+          name: 'operationState';
+          docs: ['load info from the account to judge reward permission'];
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [111, 112, 101, 114, 97, 116, 105, 111, 110];
+              }
+            ];
+          };
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['Token program'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'tokenProgram2022';
+          docs: ['Token program 2022'];
+          address: 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
+        }
+      ];
+      args: [
+        {
+          name: 'rewardIndex';
+          type: 'u8';
+        },
+        {
+          name: 'emissionsPerSecondX64';
+          type: 'u128';
+        },
+        {
+          name: 'openTime';
+          type: 'u64';
+        },
+        {
+          name: 'endTime';
+          type: 'u64';
+        }
+      ];
+    },
+    {
+      name: 'swap';
+      docs: [
+        '#[deprecated(note = "Use `swap_v2` instead.")]',
+        'Swaps one token for as much as possible of another token across a single pool',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        '* `amount` - Arranged in pairs with other_amount_threshold. (amount_in, amount_out_minimum) or (amount_out, amount_in_maximum)',
+        '* `other_amount_threshold` - For slippage check',
+        '* `sqrt_price_limit` - The Q64.64 sqrt price √P limit. If zero for one, the price cannot',
+        '* `is_base_input` - swap base input or swap base output',
+        ''
+      ];
+      discriminator: [248, 198, 158, 145, 225, 117, 135, 200];
+      accounts: [
+        {
+          name: 'payer';
+          docs: ['The user performing the swap'];
+          signer: true;
+        },
+        {
+          name: 'ammConfig';
+          docs: ['The factory state to read protocol fees'];
+        },
+        {
+          name: 'poolState';
+          docs: ['The program account of the pool in which the swap will be performed'];
+          writable: true;
+        },
+        {
+          name: 'inputTokenAccount';
+          docs: ['The user token account for input token'];
+          writable: true;
+        },
+        {
+          name: 'outputTokenAccount';
+          docs: ['The user token account for output token'];
+          writable: true;
+        },
+        {
+          name: 'inputVault';
+          docs: ['The vault token account for input token'];
+          writable: true;
+        },
+        {
+          name: 'outputVault';
+          docs: ['The vault token account for output token'];
+          writable: true;
+        },
+        {
+          name: 'observationState';
+          docs: ['The program account for the most recent oracle observation'];
+          writable: true;
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['SPL program for token transfers'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'tickArray';
+          writable: true;
+        }
+      ];
+      args: [
+        {
+          name: 'amount';
+          type: 'u64';
+        },
+        {
+          name: 'otherAmountThreshold';
+          type: 'u64';
+        },
+        {
+          name: 'sqrtPriceLimitX64';
+          type: 'u128';
+        },
+        {
+          name: 'isBaseInput';
+          type: 'bool';
+        }
+      ];
+    },
+    {
+      name: 'swapRouterBaseIn';
+      docs: [
+        'Swap token for as much as possible of another token across the path provided, base input',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        '* `amount_in` - Token amount to be swapped in',
+        '* `amount_out_minimum` - Panic if output amount is below minimum amount. For slippage.',
+        ''
+      ];
+      discriminator: [69, 125, 115, 218, 245, 186, 242, 196];
+      accounts: [
+        {
+          name: 'payer';
+          docs: ['The user performing the swap'];
+          signer: true;
+        },
+        {
+          name: 'inputTokenAccount';
+          docs: ['The token account that pays input tokens for the swap'];
+          writable: true;
+        },
+        {
+          name: 'inputTokenMint';
+          docs: ['The mint of input token'];
+          writable: true;
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['SPL program for token transfers'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'tokenProgram2022';
+          docs: ['SPL program 2022 for token transfers'];
+          address: 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
+        },
+        {
+          name: 'memoProgram';
+          docs: ['Memo program'];
+          address: 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr';
+        }
+      ];
+      args: [
+        {
+          name: 'amountIn';
+          type: 'u64';
+        },
+        {
+          name: 'amountOutMinimum';
+          type: 'u64';
+        }
+      ];
+    },
+    {
+      name: 'swapV2';
+      docs: [
+        'Swaps one token for as much as possible of another token across a single pool, support token program 2022',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx` - The context of accounts',
+        '* `amount` - Arranged in pairs with other_amount_threshold. (amount_in, amount_out_minimum) or (amount_out, amount_in_maximum)',
+        '* `other_amount_threshold` - For slippage check',
+        '* `sqrt_price_limit` - The Q64.64 sqrt price √P limit. If zero for one, the price cannot',
+        '* `is_base_input` - swap base input or swap base output',
+        ''
+      ];
+      discriminator: [43, 4, 237, 11, 26, 201, 30, 98];
+      accounts: [
+        {
+          name: 'payer';
+          docs: ['The user performing the swap'];
+          signer: true;
+        },
+        {
+          name: 'ammConfig';
+          docs: ['The factory state to read protocol fees'];
+        },
+        {
+          name: 'poolState';
+          docs: ['The program account of the pool in which the swap will be performed'];
+          writable: true;
+        },
+        {
+          name: 'inputTokenAccount';
+          docs: ['The user token account for input token'];
+          writable: true;
+        },
+        {
+          name: 'outputTokenAccount';
+          docs: ['The user token account for output token'];
+          writable: true;
+        },
+        {
+          name: 'inputVault';
+          docs: ['The vault token account for input token'];
+          writable: true;
+        },
+        {
+          name: 'outputVault';
+          docs: ['The vault token account for output token'];
+          writable: true;
+        },
+        {
+          name: 'observationState';
+          docs: ['The program account for the most recent oracle observation'];
+          writable: true;
+        },
+        {
+          name: 'tokenProgram';
+          docs: ['SPL program for token transfers'];
+          address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
+        },
+        {
+          name: 'tokenProgram2022';
+          docs: ['SPL program 2022 for token transfers'];
+          address: 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
+        },
+        {
+          name: 'memoProgram';
+          docs: ['Memo program'];
+          address: 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr';
+        },
+        {
+          name: 'inputVaultMint';
+          docs: ['The mint of token vault 0'];
+        },
+        {
+          name: 'outputVaultMint';
+          docs: ['The mint of token vault 1'];
+        }
+      ];
+      args: [
+        {
+          name: 'amount';
+          type: 'u64';
+        },
+        {
+          name: 'otherAmountThreshold';
+          type: 'u64';
+        },
+        {
+          name: 'sqrtPriceLimitX64';
+          type: 'u128';
+        },
+        {
+          name: 'isBaseInput';
+          type: 'bool';
+        }
+      ];
+    },
+    {
+      name: 'transferRewardOwner';
+      docs: [
+        'Transfer reward owner',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx`- The context of accounts',
+        '* `new_owner`- new owner pubkey',
+        ''
+      ];
+      discriminator: [7, 22, 12, 83, 242, 43, 48, 121];
+      accounts: [
+        {
+          name: 'authority';
+          docs: ['Address to be set as operation account owner.'];
+          signer: true;
+          address: 'GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ';
+        },
+        {
+          name: 'poolState';
+          writable: true;
+        }
+      ];
+      args: [
+        {
+          name: 'newOwner';
+          type: 'pubkey';
+        }
+      ];
+    },
+    {
+      name: 'updateAmmConfig';
+      docs: [
+        'Updates the owner of the amm config',
+        'Must be called by the current owner or admin',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx`- The context of accounts',
+        '* `trade_fee_rate`- The new trade fee rate of amm config, be set when `param` is 0',
+        '* `protocol_fee_rate`- The new protocol fee rate of amm config, be set when `param` is 1',
+        '* `fund_fee_rate`- The new fund fee rate of amm config, be set when `param` is 2',
+        "* `new_owner`- The config's new owner, be set when `param` is 3",
+        "* `new_fund_owner`- The config's new fund owner, be set when `param` is 4",
+        '* `param`- The value can be 0 | 1 | 2 | 3 | 4, otherwise will report a error',
+        ''
+      ];
+      discriminator: [49, 60, 174, 136, 154, 28, 116, 200];
+      accounts: [
+        {
+          name: 'owner';
+          docs: ['The amm config owner or admin'];
+          signer: true;
+          address: 'GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ';
+        },
+        {
+          name: 'ammConfig';
+          docs: ['Amm config account to be changed'];
+          writable: true;
+        }
+      ];
+      args: [
+        {
+          name: 'param';
+          type: 'u8';
+        },
+        {
+          name: 'value';
+          type: 'u32';
+        }
+      ];
+    },
+    {
+      name: 'updateOperationAccount';
+      docs: [
+        'Update the operation account',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx`- The context of accounts',
+        '* `param`- The value can be 0 | 1 | 2 | 3, otherwise will report a error',
+        '* `keys`- update operation owner when the `param` is 0',
+        'remove operation owner when the `param` is 1',
+        'update whitelist mint when the `param` is 2',
+        'remove whitelist mint when the `param` is 3',
+        ''
+      ];
+      discriminator: [127, 70, 119, 40, 188, 227, 61, 7];
+      accounts: [
+        {
+          name: 'owner';
+          docs: ['Address to be set as operation account owner.'];
+          signer: true;
+          address: 'GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ';
+        },
+        {
+          name: 'operationState';
+          docs: ['Initialize operation state account to store operation owner address and white list mint.'];
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: 'const';
+                value: [111, 112, 101, 114, 97, 116, 105, 111, 110];
+              }
+            ];
+          };
+        },
+        {
+          name: 'systemProgram';
+          address: '11111111111111111111111111111111';
+        }
+      ];
+      args: [
+        {
+          name: 'param';
+          type: 'u8';
+        },
+        {
+          name: 'keys';
+          type: {
+            vec: 'pubkey';
+          };
+        }
+      ];
+    },
+    {
+      name: 'updatePoolStatus';
+      docs: [
+        'Update pool status for given value',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx`- The context of accounts',
+        '* `status` - The value of status',
+        ''
+      ];
+      discriminator: [130, 87, 108, 6, 46, 224, 117, 123];
+      accounts: [
+        {
+          name: 'authority';
+          signer: true;
+          address: 'GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ';
+        },
+        {
+          name: 'poolState';
+          writable: true;
+        }
+      ];
+      args: [
+        {
+          name: 'status';
+          type: 'u8';
+        }
+      ];
+    },
+    {
+      name: 'updateRewardInfos';
+      docs: [
+        'Update rewards info of the given pool, can be called for everyone',
+        '',
+        '# Arguments',
+        '',
+        '* `ctx`- The context of accounts',
+        ''
+      ];
+      discriminator: [163, 172, 224, 52, 11, 154, 106, 223];
+      accounts: [
+        {
+          name: 'poolState';
+          docs: ['The liquidity pool for which reward info to update'];
+          writable: true;
+        }
+      ];
+      args: [];
+    }
+  ];
+  accounts: [
+    {
+      name: 'ammConfig';
+      discriminator: [218, 244, 33, 104, 203, 203, 43, 111];
+    },
+    {
+      name: 'observationState';
+      discriminator: [122, 174, 197, 53, 129, 9, 165, 132];
+    },
+    {
+      name: 'operationState';
+      discriminator: [19, 236, 58, 237, 81, 222, 183, 252];
+    },
+    {
+      name: 'personalPositionState';
+      discriminator: [70, 111, 150, 126, 230, 15, 25, 117];
+    },
+    {
+      name: 'poolState';
+      discriminator: [247, 237, 227, 245, 215, 195, 222, 70];
+    },
+    {
+      name: 'protocolPositionState';
+      discriminator: [100, 226, 145, 99, 146, 218, 160, 106];
+    },
+    {
+      name: 'supportMintAssociated';
+      discriminator: [134, 40, 183, 79, 12, 112, 162, 53];
+    },
+    {
+      name: 'tickArrayBitmapExtension';
+      discriminator: [60, 150, 36, 219, 97, 128, 139, 153];
+    },
+    {
+      name: 'tickArrayState';
+      discriminator: [192, 155, 85, 205, 49, 249, 129, 42];
+    }
+  ];
+  events: [
+    {
+      name: 'collectPersonalFeeEvent';
+      discriminator: [166, 174, 105, 192, 81, 161, 83, 105];
+    },
+    {
+      name: 'collectProtocolFeeEvent';
+      discriminator: [206, 87, 17, 79, 45, 41, 213, 61];
+    },
+    {
+      name: 'configChangeEvent';
+      discriminator: [247, 189, 7, 119, 106, 112, 95, 151];
+    },
+    {
+      name: 'createPersonalPositionEvent';
+      discriminator: [100, 30, 87, 249, 196, 223, 154, 206];
+    },
+    {
+      name: 'decreaseLiquidityEvent';
+      discriminator: [58, 222, 86, 58, 68, 50, 85, 56];
+    },
+    {
+      name: 'increaseLiquidityEvent';
+      discriminator: [49, 79, 105, 212, 32, 34, 30, 84];
+    },
+    {
+      name: 'liquidityCalculateEvent';
+      discriminator: [237, 112, 148, 230, 57, 84, 180, 162];
+    },
+    {
+      name: 'liquidityChangeEvent';
+      discriminator: [126, 240, 175, 206, 158, 88, 153, 107];
+    },
+    {
+      name: 'poolCreatedEvent';
+      discriminator: [25, 94, 75, 47, 112, 99, 53, 63];
+    },
+    {
+      name: 'swapEvent';
+      discriminator: [64, 198, 205, 232, 38, 8, 113, 226];
+    },
+    {
+      name: 'updateRewardInfosEvent';
+      discriminator: [109, 127, 186, 78, 114, 65, 37, 236];
+    }
+  ];
+  errors: [
+    {
+      code: 6000;
+      name: 'lok';
+      msg: 'lok';
+    },
+    {
+      code: 6001;
+      name: 'notApproved';
+      msg: 'Not approved';
+    },
+    {
+      code: 6002;
+      name: 'invalidUpdateConfigFlag';
+      msg: 'invalid update amm config flag';
+    },
+    {
+      code: 6003;
+      name: 'accountLack';
+      msg: 'Account lack';
+    },
+    {
+      code: 6004;
+      name: 'closePositionErr';
+      msg: 'Remove liquitity, collect fees owed and reward then you can close position account';
+    },
+    {
+      code: 6005;
+      name: 'zeroMintAmount';
+      msg: 'Minting amount should be greater than 0';
+    },
+    {
+      code: 6006;
+      name: 'invaildTickIndex';
+      msg: 'Tick out of range';
+    },
+    {
+      code: 6007;
+      name: 'tickInvaildOrder';
+      msg: 'The lower tick must be below the upper tick';
+    },
+    {
+      code: 6008;
+      name: 'tickLowerOverflow';
+      msg: 'The tick must be greater, or equal to the minimum tick(-443636)';
+    },
+    {
+      code: 6009;
+      name: 'tickUpperOverflow';
+      msg: 'The tick must be lesser than, or equal to the maximum tick(443636)';
+    },
+    {
+      code: 6010;
+      name: 'tickAndSpacingNotMatch';
+      msg: 'tick % tick_spacing must be zero';
+    },
+    {
+      code: 6011;
+      name: 'invalidTickArray';
+      msg: 'Invaild tick array account';
+    },
+    {
+      code: 6012;
+      name: 'invalidTickArrayBoundary';
+      msg: 'Invaild tick array boundary';
+    },
+    {
+      code: 6013;
+      name: 'sqrtPriceLimitOverflow';
+      msg: 'Square root price limit overflow';
+    },
+    {
+      code: 6014;
+      name: 'sqrtPriceX64';
+      msg: 'sqrt_price_x64 out of range';
+    },
+    {
+      code: 6015;
+      name: 'liquiditySubValueErr';
+      msg: 'Liquidity sub delta L must be smaller than before';
+    },
+    {
+      code: 6016;
+      name: 'liquidityAddValueErr';
+      msg: 'Liquidity add delta L must be greater, or equal to before';
+    },
+    {
+      code: 6017;
+      name: 'invaildLiquidity';
+      msg: 'Invaild liquidity when update position';
+    },
+    {
+      code: 6018;
+      name: 'forbidBothZeroForSupplyLiquidity';
+      msg: 'Both token amount must not be zero while supply liquidity';
+    },
+    {
+      code: 6019;
+      name: 'liquidityInsufficient';
+      msg: 'Liquidity insufficient';
+    },
+    {
+      code: 6020;
+      name: 'transactionTooOld';
+      msg: 'Transaction too old';
+    },
+    {
+      code: 6021;
+      name: 'priceSlippageCheck';
+      msg: 'Price slippage check';
+    },
+    {
+      code: 6022;
+      name: 'tooLittleOutputReceived';
+      msg: 'Too little output received';
+    },
+    {
+      code: 6023;
+      name: 'tooMuchInputPaid';
+      msg: 'Too much input paid';
+    },
+    {
+      code: 6024;
+      name: 'zeroAmountSpecified';
+      msg: 'Swap special amount can not be zero';
+    },
+    {
+      code: 6025;
+      name: 'invalidInputPoolVault';
+      msg: 'Input pool vault is invalid';
+    },
+    {
+      code: 6026;
+      name: 'tooSmallInputOrOutputAmount';
+      msg: 'Swap input or output amount is too small';
+    },
+    {
+      code: 6027;
+      name: 'notEnoughTickArrayAccount';
+      msg: 'Not enought tick array account';
+    },
+    {
+      code: 6028;
+      name: 'invalidFirstTickArrayAccount';
+      msg: 'Invaild first tick array account';
+    },
+    {
+      code: 6029;
+      name: 'invalidRewardIndex';
+      msg: 'Invalid reward index';
+    },
+    {
+      code: 6030;
+      name: 'fullRewardInfo';
+      msg: 'The init reward token reach to the max';
+    },
+    {
+      code: 6031;
+      name: 'rewardTokenAlreadyInUse';
+      msg: 'The init reward token already in use';
+    },
+    {
+      code: 6032;
+      name: 'exceptRewardMint';
+      msg: 'The reward tokens must contain one of pool vault mint except the last reward';
+    },
+    {
+      code: 6033;
+      name: 'invalidRewardInitParam';
+      msg: 'Invalid reward init param';
+    },
+    {
+      code: 6034;
+      name: 'invalidRewardDesiredAmount';
+      msg: 'Invalid collect reward desired amount';
+    },
+    {
+      code: 6035;
+      name: 'invalidRewardInputAccountNumber';
+      msg: 'Invalid collect reward input account number';
+    },
+    {
+      code: 6036;
+      name: 'invalidRewardPeriod';
+      msg: 'Invalid reward period';
+    },
+    {
+      code: 6037;
+      name: 'notApproveUpdateRewardEmissiones';
+      msg: 'Modification of emissiones is allowed within 72 hours from the end of the previous cycle';
+    },
+    {
+      code: 6038;
+      name: 'unInitializedRewardInfo';
+      msg: 'uninitialized reward info';
+    },
+    {
+      code: 6039;
+      name: 'notSupportMint';
+      msg: 'Not support token_2022 mint extension';
+    },
+    {
+      code: 6040;
+      name: 'missingTickArrayBitmapExtensionAccount';
+      msg: 'Missing tickarray bitmap extension account';
+    },
+    {
+      code: 6041;
+      name: 'insufficientLiquidityForDirection';
+      msg: 'Insufficient liquidity for this direction';
+    },
+    {
+      code: 6042;
+      name: 'maxTokenOverflow';
+      msg: 'Max token overflow';
+    },
+    {
+      code: 6043;
+      name: 'calculateOverflow';
+      msg: 'calculate overflow';
+    }
+  ];
+  types: [
+    {
+      name: 'ammConfig';
+      docs: ['Holds the current owner of the factory'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'bump';
+            docs: ['Bump to identify PDA'];
+            type: 'u8';
+          },
+          {
+            name: 'index';
+            type: 'u16';
+          },
+          {
+            name: 'owner';
+            docs: ['Address of the protocol owner'];
+            type: 'pubkey';
+          },
+          {
+            name: 'protocolFeeRate';
+            docs: ['The protocol fee'];
+            type: 'u32';
+          },
+          {
+            name: 'tradeFeeRate';
+            docs: ['The trade fee, denominated in hundredths of a bip (10^-6)'];
+            type: 'u32';
+          },
+          {
+            name: 'tickSpacing';
+            docs: ['The tick spacing'];
+            type: 'u16';
+          },
+          {
+            name: 'fundFeeRate';
+            docs: ['The fund fee, denominated in hundredths of a bip (10^-6)'];
+            type: 'u32';
+          },
+          {
+            name: 'paddingU32';
+            type: 'u32';
+          },
+          {
+            name: 'fundOwner';
+            type: 'pubkey';
+          },
+          {
+            name: 'padding';
+            type: {
+              array: ['u64', 3];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'collectPersonalFeeEvent';
+      docs: ['Emitted when tokens are collected for a position'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'positionNftMint';
+            docs: ['The ID of the token for which underlying tokens were collected'];
+            type: 'pubkey';
+          },
+          {
+            name: 'recipientTokenAccount0';
+            docs: ['The token account that received the collected token_0 tokens'];
+            type: 'pubkey';
+          },
+          {
+            name: 'recipientTokenAccount1';
+            docs: ['The token account that received the collected token_1 tokens'];
+            type: 'pubkey';
+          },
+          {
+            name: 'amount0';
+            docs: ['The amount of token_0 owed to the position that was collected'];
+            type: 'u64';
+          },
+          {
+            name: 'amount1';
+            docs: ['The amount of token_1 owed to the position that was collected'];
+            type: 'u64';
+          }
+        ];
+      };
+    },
+    {
+      name: 'collectProtocolFeeEvent';
+      docs: ['Emitted when the collected protocol fees are withdrawn by the factory owner'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'poolState';
+            docs: ['The pool whose protocol fee is collected'];
+            type: 'pubkey';
+          },
+          {
+            name: 'recipientTokenAccount0';
+            docs: ['The address that receives the collected token_0 protocol fees'];
+            type: 'pubkey';
+          },
+          {
+            name: 'recipientTokenAccount1';
+            docs: ['The address that receives the collected token_1 protocol fees'];
+            type: 'pubkey';
+          },
+          {
+            name: 'amount0';
+            docs: ['The amount of token_0 protocol fees that is withdrawn'];
+            type: 'u64';
+          },
+          {
+            name: 'amount1';
+            docs: ['The amount of token_0 protocol fees that is withdrawn'];
+            type: 'u64';
+          }
+        ];
+      };
+    },
+    {
+      name: 'configChangeEvent';
+      docs: ['Emitted when create or update a config'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'index';
+            type: 'u16';
+          },
+          {
+            name: 'owner';
+            type: 'pubkey';
+          },
+          {
+            name: 'protocolFeeRate';
+            type: 'u32';
+          },
+          {
+            name: 'tradeFeeRate';
+            type: 'u32';
+          },
+          {
+            name: 'tickSpacing';
+            type: 'u16';
+          },
+          {
+            name: 'fundFeeRate';
+            type: 'u32';
+          },
+          {
+            name: 'fundOwner';
+            type: 'pubkey';
+          }
+        ];
+      };
+    },
+    {
+      name: 'createPersonalPositionEvent';
+      docs: ['Emitted when create a new position'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'poolState';
+            docs: ['The pool for which liquidity was added'];
+            type: 'pubkey';
+          },
+          {
+            name: 'minter';
+            docs: ['The address that create the position'];
+            type: 'pubkey';
+          },
+          {
+            name: 'nftOwner';
+            docs: ['The owner of the position and recipient of any minted liquidity'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tickLowerIndex';
+            docs: ['The lower tick of the position'];
+            type: 'i32';
+          },
+          {
+            name: 'tickUpperIndex';
+            docs: ['The upper tick of the position'];
+            type: 'i32';
+          },
+          {
+            name: 'liquidity';
+            docs: ['The amount of liquidity minted to the position range'];
+            type: 'u128';
+          },
+          {
+            name: 'depositAmount0';
+            docs: ['The amount of token_0 was deposit for the liquidity'];
+            type: 'u64';
+          },
+          {
+            name: 'depositAmount1';
+            docs: ['The amount of token_1 was deposit for the liquidity'];
+            type: 'u64';
+          },
+          {
+            name: 'depositAmount0TransferFee';
+            docs: ['The token transfer fee for deposit_amount_0'];
+            type: 'u64';
+          },
+          {
+            name: 'depositAmount1TransferFee';
+            docs: ['The token transfer fee for deposit_amount_1'];
+            type: 'u64';
+          }
+        ];
+      };
+    },
+    {
+      name: 'decreaseLiquidityEvent';
+      docs: ['Emitted when liquidity is decreased.'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'positionNftMint';
+            docs: ['The ID of the token for which liquidity was decreased'];
+            type: 'pubkey';
+          },
+          {
+            name: 'liquidity';
+            docs: ['The amount by which liquidity for the position was decreased'];
+            type: 'u128';
+          },
+          {
+            name: 'decreaseAmount0';
+            docs: ['The amount of token_0 that was paid for the decrease in liquidity'];
+            type: 'u64';
+          },
+          {
+            name: 'decreaseAmount1';
+            docs: ['The amount of token_1 that was paid for the decrease in liquidity'];
+            type: 'u64';
+          },
+          {
+            name: 'feeAmount0';
+            type: 'u64';
+          },
+          {
+            name: 'feeAmount1';
+            docs: ['The amount of token_1 fee'];
+            type: 'u64';
+          },
+          {
+            name: 'rewardAmounts';
+            docs: ['The amount of rewards'];
+            type: {
+              array: ['u64', 3];
+            };
+          },
+          {
+            name: 'transferFee0';
+            docs: ['The amount of token_0 transfer fee'];
+            type: 'u64';
+          },
+          {
+            name: 'transferFee1';
+            docs: ['The amount of token_1 transfer fee'];
+            type: 'u64';
+          }
+        ];
+      };
+    },
+    {
+      name: 'increaseLiquidityEvent';
+      docs: ['Emitted when liquidity is increased.'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'positionNftMint';
+            docs: ['The ID of the token for which liquidity was increased'];
+            type: 'pubkey';
+          },
+          {
+            name: 'liquidity';
+            docs: ['The amount by which liquidity for the NFT position was increased'];
+            type: 'u128';
+          },
+          {
+            name: 'amount0';
+            docs: ['The amount of token_0 that was paid for the increase in liquidity'];
+            type: 'u64';
+          },
+          {
+            name: 'amount1';
+            docs: ['The amount of token_1 that was paid for the increase in liquidity'];
+            type: 'u64';
+          },
+          {
+            name: 'amount0TransferFee';
+            docs: ['The token transfer fee for amount_0'];
+            type: 'u64';
+          },
+          {
+            name: 'amount1TransferFee';
+            docs: ['The token transfer fee for amount_1'];
+            type: 'u64';
+          }
+        ];
+      };
+    },
+    {
+      name: 'initializeRewardParam';
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'openTime';
+            docs: ['Reward open time'];
+            type: 'u64';
+          },
+          {
+            name: 'endTime';
+            docs: ['Reward end time'];
+            type: 'u64';
+          },
+          {
+            name: 'emissionsPerSecondX64';
+            docs: ['Token reward per second are earned per unit of liquidity'];
+            type: 'u128';
+          }
+        ];
+      };
+    },
+    {
+      name: 'liquidityCalculateEvent';
+      docs: ['Emitted when liquidity decreased or increase.'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'poolLiquidity';
+            docs: ['The pool liquidity before decrease or increase'];
+            type: 'u128';
+          },
+          {
+            name: 'poolSqrtPriceX64';
+            docs: ['The pool price when decrease or increase in liquidity'];
+            type: 'u128';
+          },
+          {
+            name: 'poolTick';
+            docs: ['The pool tick when decrease or increase in liquidity'];
+            type: 'i32';
+          },
+          {
+            name: 'calcAmount0';
+            docs: ['The amount of token_0 that was calculated for the decrease or increase in liquidity'];
+            type: 'u64';
+          },
+          {
+            name: 'calcAmount1';
+            docs: ['The amount of token_1 that was calculated for the decrease or increase in liquidity'];
+            type: 'u64';
+          },
+          {
+            name: 'tradeFeeOwed0';
+            type: 'u64';
+          },
+          {
+            name: 'tradeFeeOwed1';
+            docs: ['The amount of token_1 fee'];
+            type: 'u64';
+          },
+          {
+            name: 'transferFee0';
+            docs: ['The amount of token_0 transfer fee without trade_fee_amount_0'];
+            type: 'u64';
+          },
+          {
+            name: 'transferFee1';
+            docs: ['The amount of token_1 transfer fee without trade_fee_amount_0'];
+            type: 'u64';
+          }
+        ];
+      };
+    },
+    {
+      name: 'liquidityChangeEvent';
+      docs: ['Emitted pool liquidity change when increase and decrease liquidity'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'poolState';
+            docs: ['The pool for swap'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tick';
+            docs: ['The tick of the pool'];
+            type: 'i32';
+          },
+          {
+            name: 'tickLower';
+            docs: ['The tick lower of position'];
+            type: 'i32';
+          },
+          {
+            name: 'tickUpper';
+            docs: ['The tick lower of position'];
+            type: 'i32';
+          },
+          {
+            name: 'liquidityBefore';
+            docs: ['The liquidity of the pool before liquidity change'];
+            type: 'u128';
+          },
+          {
+            name: 'liquidityAfter';
+            docs: ['The liquidity of the pool after liquidity change'];
+            type: 'u128';
+          }
+        ];
+      };
+    },
+    {
+      name: 'observation';
+      docs: ['The element of observations in ObservationState'];
+      serialization: 'bytemuckunsafe';
+      repr: {
+        kind: 'c';
+        packed: true;
+      };
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'blockTimestamp';
+            docs: ['The block timestamp of the observation'];
+            type: 'u32';
+          },
+          {
+            name: 'tickCumulative';
+            docs: ['the cumulative of tick during the duration time'];
+            type: 'i64';
+          },
+          {
+            name: 'padding';
+            docs: ['padding for feature update'];
+            type: {
+              array: ['u64', 4];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'observationState';
+      serialization: 'bytemuckunsafe';
+      repr: {
+        kind: 'c';
+        packed: true;
+      };
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'initialized';
+            docs: ['Whether the ObservationState is initialized'];
+            type: 'bool';
+          },
+          {
+            name: 'recentEpoch';
+            docs: ['recent update epoch'];
+            type: 'u64';
+          },
+          {
+            name: 'observationIndex';
+            docs: ['the most-recently updated index of the observations array'];
+            type: 'u16';
+          },
+          {
+            name: 'poolId';
+            docs: ['belongs to which pool'];
+            type: 'pubkey';
+          },
+          {
+            name: 'observations';
+            docs: ['observation array'];
+            type: {
+              array: [
+                {
+                  defined: {
+                    name: 'observation';
+                  };
+                },
+                100
+              ];
+            };
+          },
+          {
+            name: 'padding';
+            docs: ['padding for feature update'];
+            type: {
+              array: ['u64', 4];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'operationState';
+      docs: ['Holds the current owner of the factory'];
+      serialization: 'bytemuckunsafe';
+      repr: {
+        kind: 'c';
+        packed: true;
+      };
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'bump';
+            docs: ['Bump to identify PDA'];
+            type: 'u8';
+          },
+          {
+            name: 'operationOwners';
+            docs: ['Address of the operation owner'];
+            type: {
+              array: ['pubkey', 10];
+            };
+          },
+          {
+            name: 'whitelistMints';
+            docs: ['The mint address of whitelist to emit reward'];
+            type: {
+              array: ['pubkey', 100];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'personalPositionState';
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'bump';
+            docs: ['Bump to identify PDA'];
+            type: {
+              array: ['u8', 1];
+            };
+          },
+          {
+            name: 'nftMint';
+            docs: ['Mint address of the tokenized position'];
+            type: 'pubkey';
+          },
+          {
+            name: 'poolId';
+            docs: ['The ID of the pool with which this token is connected'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tickLowerIndex';
+            docs: ['The lower bound tick of the position'];
+            type: 'i32';
+          },
+          {
+            name: 'tickUpperIndex';
+            docs: ['The upper bound tick of the position'];
+            type: 'i32';
+          },
+          {
+            name: 'liquidity';
+            docs: ['The amount of liquidity owned by this position'];
+            type: 'u128';
+          },
+          {
+            name: 'feeGrowthInside0LastX64';
+            docs: ['The token_0 fee growth of the aggregate position as of the last action on the individual position'];
+            type: 'u128';
+          },
+          {
+            name: 'feeGrowthInside1LastX64';
+            docs: ['The token_1 fee growth of the aggregate position as of the last action on the individual position'];
+            type: 'u128';
+          },
+          {
+            name: 'tokenFeesOwed0';
+            docs: ['The fees owed to the position owner in token_0, as of the last computation'];
+            type: 'u64';
+          },
+          {
+            name: 'tokenFeesOwed1';
+            docs: ['The fees owed to the position owner in token_1, as of the last computation'];
+            type: 'u64';
+          },
+          {
+            name: 'rewardInfos';
+            type: {
+              array: [
+                {
+                  defined: {
+                    name: 'positionRewardInfo';
+                  };
+                },
+                3
+              ];
+            };
+          },
+          {
+            name: 'recentEpoch';
+            type: 'u64';
+          },
+          {
+            name: 'padding';
+            type: {
+              array: ['u64', 7];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'poolCreatedEvent';
+      docs: ['Emitted when a pool is created and initialized with a starting price', ''];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'tokenMint0';
+            docs: ['The first token of the pool by address sort order'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tokenMint1';
+            docs: ['The second token of the pool by address sort order'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tickSpacing';
+            docs: ['The minimum number of ticks between initialized ticks'];
+            type: 'u16';
+          },
+          {
+            name: 'poolState';
+            docs: ['The address of the created pool'];
+            type: 'pubkey';
+          },
+          {
+            name: 'sqrtPriceX64';
+            docs: ['The initial sqrt price of the pool, as a Q64.64'];
+            type: 'u128';
+          },
+          {
+            name: 'tick';
+            docs: ['The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool'];
+            type: 'i32';
+          },
+          {
+            name: 'tokenVault0';
+            docs: ['Vault of token_0'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tokenVault1';
+            docs: ['Vault of token_1'];
+            type: 'pubkey';
+          }
+        ];
+      };
+    },
+    {
+      name: 'poolState';
+      docs: ['The pool state', '', 'PDA of `[POOL_SEED, config, token_mint_0, token_mint_1]`', ''];
+      serialization: 'bytemuckunsafe';
+      repr: {
+        kind: 'c';
+        packed: true;
+      };
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'bump';
+            docs: ['Bump to identify PDA'];
+            type: {
+              array: ['u8', 1];
+            };
+          },
+          {
+            name: 'ammConfig';
+            type: 'pubkey';
+          },
+          {
+            name: 'owner';
+            type: 'pubkey';
+          },
+          {
+            name: 'tokenMint0';
+            docs: ['Token pair of the pool, where token_mint_0 address < token_mint_1 address'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tokenMint1';
+            type: 'pubkey';
+          },
+          {
+            name: 'tokenVault0';
+            docs: ['Token pair vault'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tokenVault1';
+            type: 'pubkey';
+          },
+          {
+            name: 'observationKey';
+            docs: ['observation account key'];
+            type: 'pubkey';
+          },
+          {
+            name: 'mintDecimals0';
+            docs: ['mint0 and mint1 decimals'];
+            type: 'u8';
+          },
+          {
+            name: 'mintDecimals1';
+            type: 'u8';
+          },
+          {
+            name: 'tickSpacing';
+            docs: ['The minimum number of ticks between initialized ticks'];
+            type: 'u16';
+          },
+          {
+            name: 'liquidity';
+            docs: ['The currently in range liquidity available to the pool.'];
+            type: 'u128';
+          },
+          {
+            name: 'sqrtPriceX64';
+            docs: ['The current price of the pool as a sqrt(token_1/token_0) Q64.64 value'];
+            type: 'u128';
+          },
+          {
+            name: 'tickCurrent';
+            docs: ['The current tick of the pool, i.e. according to the last tick transition that was run.'];
+            type: 'i32';
+          },
+          {
+            name: 'padding3';
+            type: 'u16';
+          },
+          {
+            name: 'padding4';
+            type: 'u16';
+          },
+          {
+            name: 'feeGrowthGlobal0X64';
+            docs: [
+              'The fee growth as a Q64.64 number, i.e. fees of token_0 and token_1 collected per',
+              'unit of liquidity for the entire life of the pool.'
+            ];
+            type: 'u128';
+          },
+          {
+            name: 'feeGrowthGlobal1X64';
+            type: 'u128';
+          },
+          {
+            name: 'protocolFeesToken0';
+            docs: ['The amounts of token_0 and token_1 that are owed to the protocol.'];
+            type: 'u64';
+          },
+          {
+            name: 'protocolFeesToken1';
+            type: 'u64';
+          },
+          {
+            name: 'swapInAmountToken0';
+            docs: ['The amounts in and out of swap token_0 and token_1'];
+            type: 'u128';
+          },
+          {
+            name: 'swapOutAmountToken1';
+            type: 'u128';
+          },
+          {
+            name: 'swapInAmountToken1';
+            type: 'u128';
+          },
+          {
+            name: 'swapOutAmountToken0';
+            type: 'u128';
+          },
+          {
+            name: 'status';
+            docs: [
+              'Bitwise representation of the state of the pool',
+              'bit0, 1: disable open position and increase liquidity, 0: normal',
+              'bit1, 1: disable decrease liquidity, 0: normal',
+              'bit2, 1: disable collect fee, 0: normal',
+              'bit3, 1: disable collect reward, 0: normal',
+              'bit4, 1: disable swap, 0: normal'
+            ];
+            type: 'u8';
+          },
+          {
+            name: 'padding';
+            docs: ['Leave blank for future use'];
+            type: {
+              array: ['u8', 7];
+            };
+          },
+          {
+            name: 'rewardInfos';
+            type: {
+              array: [
+                {
+                  defined: {
+                    name: 'rewardInfo';
+                  };
+                },
+                3
+              ];
+            };
+          },
+          {
+            name: 'tickArrayBitmap';
+            docs: ['Packed initialized tick array state'];
+            type: {
+              array: ['u64', 16];
+            };
+          },
+          {
+            name: 'totalFeesToken0';
+            docs: ['except protocol_fee and fund_fee'];
+            type: 'u64';
+          },
+          {
+            name: 'totalFeesClaimedToken0';
+            docs: ['except protocol_fee and fund_fee'];
+            type: 'u64';
+          },
+          {
+            name: 'totalFeesToken1';
+            type: 'u64';
+          },
+          {
+            name: 'totalFeesClaimedToken1';
+            type: 'u64';
+          },
+          {
+            name: 'fundFeesToken0';
+            type: 'u64';
+          },
+          {
+            name: 'fundFeesToken1';
+            type: 'u64';
+          },
+          {
+            name: 'openTime';
+            type: 'u64';
+          },
+          {
+            name: 'recentEpoch';
+            type: 'u64';
+          },
+          {
+            name: 'padding1';
+            type: {
+              array: ['u64', 24];
+            };
+          },
+          {
+            name: 'padding2';
+            type: {
+              array: ['u64', 32];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'positionRewardInfo';
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'growthInsideLastX64';
+            type: 'u128';
+          },
+          {
+            name: 'rewardAmountOwed';
+            type: 'u64';
+          }
+        ];
+      };
+    },
+    {
+      name: 'protocolPositionState';
+      docs: ["Info stored for each user's position"];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'bump';
+            docs: ['Bump to identify PDA'];
+            type: 'u8';
+          },
+          {
+            name: 'poolId';
+            docs: ['The ID of the pool with which this token is connected'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tickLowerIndex';
+            docs: ['The lower bound tick of the position'];
+            type: 'i32';
+          },
+          {
+            name: 'tickUpperIndex';
+            docs: ['The upper bound tick of the position'];
+            type: 'i32';
+          },
+          {
+            name: 'liquidity';
+            docs: ['The amount of liquidity owned by this position'];
+            type: 'u128';
+          },
+          {
+            name: 'feeGrowthInside0LastX64';
+            docs: ['The token_0 fee growth per unit of liquidity as of the last update to liquidity or fees owed'];
+            type: 'u128';
+          },
+          {
+            name: 'feeGrowthInside1LastX64';
+            docs: ['The token_1 fee growth per unit of liquidity as of the last update to liquidity or fees owed'];
+            type: 'u128';
+          },
+          {
+            name: 'tokenFeesOwed0';
+            docs: ['The fees owed to the position owner in token_0'];
+            type: 'u64';
+          },
+          {
+            name: 'tokenFeesOwed1';
+            docs: ['The fees owed to the position owner in token_1'];
+            type: 'u64';
+          },
+          {
+            name: 'rewardGrowthInside';
+            docs: ['The reward growth per unit of liquidity as of the last update to liquidity'];
+            type: {
+              array: ['u128', 3];
+            };
+          },
+          {
+            name: 'recentEpoch';
+            type: 'u64';
+          },
+          {
+            name: 'padding';
+            type: {
+              array: ['u64', 7];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'rewardInfo';
+      serialization: 'bytemuckunsafe';
+      repr: {
+        kind: 'c';
+        packed: true;
+      };
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'rewardState';
+            docs: ['Reward state'];
+            type: 'u8';
+          },
+          {
+            name: 'openTime';
+            docs: ['Reward open time'];
+            type: 'u64';
+          },
+          {
+            name: 'endTime';
+            docs: ['Reward end time'];
+            type: 'u64';
+          },
+          {
+            name: 'lastUpdateTime';
+            docs: ['Reward last update time'];
+            type: 'u64';
+          },
+          {
+            name: 'emissionsPerSecondX64';
+            docs: ['Q64.64 number indicates how many tokens per second are earned per unit of liquidity.'];
+            type: 'u128';
+          },
+          {
+            name: 'rewardTotalEmissioned';
+            docs: ['The total amount of reward emissioned'];
+            type: 'u64';
+          },
+          {
+            name: 'rewardClaimed';
+            docs: ['The total amount of claimed reward'];
+            type: 'u64';
+          },
+          {
+            name: 'tokenMint';
+            docs: ['Reward token mint.'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tokenVault';
+            docs: ['Reward vault token account.'];
+            type: 'pubkey';
+          },
+          {
+            name: 'authority';
+            docs: ['The owner that has permission to set reward param'];
+            type: 'pubkey';
+          },
+          {
+            name: 'rewardGrowthGlobalX64';
+            docs: [
+              'Q64.64 number that tracks the total tokens earned per unit of liquidity since the reward',
+              'emissions were turned on.'
+            ];
+            type: 'u128';
+          }
+        ];
+      };
+    },
+    {
+      name: 'supportMintAssociated';
+      docs: ['Holds the current owner of the factory'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'bump';
+            docs: ['Bump to identify PDA'];
+            type: 'u8';
+          },
+          {
+            name: 'mint';
+            docs: ['Address of the supported token22 mint'];
+            type: 'pubkey';
+          },
+          {
+            name: 'padding';
+            type: {
+              array: ['u64', 8];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'swapEvent';
+      docs: ['Emitted by when a swap is performed for a pool'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'poolState';
+            docs: ['The pool for which token_0 and token_1 were swapped'];
+            type: 'pubkey';
+          },
+          {
+            name: 'sender';
+            docs: ['The address that initiated the swap call, and that received the callback'];
+            type: 'pubkey';
+          },
+          {
+            name: 'tokenAccount0';
+            docs: [
+              'The payer token account in zero for one swaps, or the recipient token account',
+              'in one for zero swaps'
+            ];
+            type: 'pubkey';
+          },
+          {
+            name: 'tokenAccount1';
+            docs: [
+              'The payer token account in one for zero swaps, or the recipient token account',
+              'in zero for one swaps'
+            ];
+            type: 'pubkey';
+          },
+          {
+            name: 'amount0';
+            docs: ['The real delta amount of the token_0 of the pool or user'];
+            type: 'u64';
+          },
+          {
+            name: 'transferFee0';
+            docs: ['The transfer fee charged by the withheld_amount of the token_0'];
+            type: 'u64';
+          },
+          {
+            name: 'amount1';
+            docs: ['The real delta of the token_1 of the pool or user'];
+            type: 'u64';
+          },
+          {
+            name: 'transferFee1';
+            docs: ['The transfer fee charged by the withheld_amount of the token_1'];
+            type: 'u64';
+          },
+          {
+            name: 'zeroForOne';
+            docs: ['if true, amount_0 is negtive and amount_1 is positive'];
+            type: 'bool';
+          },
+          {
+            name: 'sqrtPriceX64';
+            docs: ['The sqrt(price) of the pool after the swap, as a Q64.64'];
+            type: 'u128';
+          },
+          {
+            name: 'liquidity';
+            docs: ['The liquidity of the pool after the swap'];
+            type: 'u128';
+          },
+          {
+            name: 'tick';
+            docs: ['The log base 1.0001 of price of the pool after the swap'];
+            type: 'i32';
+          }
+        ];
+      };
+    },
+    {
+      name: 'tickArrayBitmapExtension';
+      serialization: 'bytemuckunsafe';
+      repr: {
+        kind: 'c';
+        packed: true;
+      };
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'poolId';
+            type: 'pubkey';
+          },
+          {
+            name: 'positiveTickArrayBitmap';
+            docs: ['Packed initialized tick array state for start_tick_index is positive'];
+            type: {
+              array: [
+                {
+                  array: ['u64', 8];
+                },
+                14
+              ];
+            };
+          },
+          {
+            name: 'negativeTickArrayBitmap';
+            docs: ['Packed initialized tick array state for start_tick_index is negitive'];
+            type: {
+              array: [
+                {
+                  array: ['u64', 8];
+                },
+                14
+              ];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'tickArrayState';
+      serialization: 'bytemuckunsafe';
+      repr: {
+        kind: 'c';
+        packed: true;
+      };
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'poolId';
+            type: 'pubkey';
+          },
+          {
+            name: 'startTickIndex';
+            type: 'i32';
+          },
+          {
+            name: 'ticks';
+            type: {
+              array: [
+                {
+                  defined: {
+                    name: 'tickState';
+                  };
+                },
+                60
+              ];
+            };
+          },
+          {
+            name: 'initializedTickCount';
+            type: 'u8';
+          },
+          {
+            name: 'recentEpoch';
+            type: 'u64';
+          },
+          {
+            name: 'padding';
+            type: {
+              array: ['u8', 107];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'tickState';
+      serialization: 'bytemuckunsafe';
+      repr: {
+        kind: 'c';
+        packed: true;
+      };
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'tick';
+            type: 'i32';
+          },
+          {
+            name: 'liquidityNet';
+            docs: [
+              'Amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left)'
+            ];
+            type: 'i128';
+          },
+          {
+            name: 'liquidityGross';
+            docs: ['The total position liquidity that references this tick'];
+            type: 'u128';
+          },
+          {
+            name: 'feeGrowthOutside0X64';
+            docs: [
+              'Fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)',
+              'only has relative meaning, not absolute — the value depends on when the tick is initialized'
+            ];
+            type: 'u128';
+          },
+          {
+            name: 'feeGrowthOutside1X64';
+            type: 'u128';
+          },
+          {
+            name: 'rewardGrowthsOutsideX64';
+            type: {
+              array: ['u128', 3];
+            };
+          },
+          {
+            name: 'padding';
+            type: {
+              array: ['u32', 13];
+            };
+          }
+        ];
+      };
+    },
+    {
+      name: 'updateRewardInfosEvent';
+      docs: ['Emitted when Reward are updated for a pool'];
+      type: {
+        kind: 'struct';
+        fields: [
+          {
+            name: 'rewardGrowthGlobalX64';
+            docs: ['Reward info'];
+            type: {
+              array: ['u128', 3];
+            };
+          }
+        ];
+      };
+    }
+  ];
+};

+ 79 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/binaryUtils.ts

@@ -0,0 +1,79 @@
+import BN from 'bn.js';
+
+// Convert 16-bit unsigned integer to byte array
+export function u16ToBytes(num: number): Uint8Array {
+  const arr = new ArrayBuffer(2);
+  const view = new DataView(arr);
+  view.setUint16(0, num, false);
+  return new Uint8Array(arr);
+}
+
+// Convert 16-bit integer to byte array
+export function i16ToBytes(num: number): Uint8Array {
+  const arr = new ArrayBuffer(2);
+  const view = new DataView(arr);
+  view.setInt16(0, num, false);
+  return new Uint8Array(arr);
+}
+
+// Convert 32-bit unsigned integer to byte array
+export function u32ToBytes(num: number): Uint8Array {
+  const arr = new ArrayBuffer(4);
+  const view = new DataView(arr);
+  view.setUint32(0, num, false);
+  return new Uint8Array(arr);
+}
+
+// Convert 32-bit integer to byte array
+export function i32ToBytes(num: number): Uint8Array {
+  const arr = new ArrayBuffer(4);
+  const view = new DataView(arr);
+  view.setInt32(0, num, false);
+  return new Uint8Array(arr);
+}
+
+// Find the position of the highest bit '1' in the bitmap
+export function leadingZeros(bitNum: number, data: BN): number {
+  let i = 0;
+  for (let j = bitNum - 1; j >= 0; j--) {
+    if (!data.testn(j)) {
+      i++;
+    } else {
+      break;
+    }
+  }
+  return i;
+}
+
+// Find the position of the lowest '0' in the bitmap
+export function trailingZeros(bitNum: number, data: BN): number {
+  let i = 0;
+  for (let j = 0; j < bitNum; j++) {
+    if (!data.testn(j)) {
+      i++;
+    } else {
+      break;
+    }
+  }
+  return i;
+}
+
+// Check if the bitmap is empty
+export function isZero(bitNum: number, data: BN): boolean {
+  for (let i = 0; i < bitNum; i++) {
+    if (data.testn(i)) return false;
+  }
+  return true;
+}
+
+// Find the position of the highest bit '1' in the bitmap
+export function mostSignificantBit(bitNum: number, data: BN): number | null {
+  if (isZero(bitNum, data)) return null;
+  else return leadingZeros(bitNum, data);
+}
+
+// Find the position of the lowest bit '1' in the bitmap
+export function leastSignificantBit(bitNum: number, data: BN): number | null {
+  if (isZero(bitNum, data)) return null;
+  else return trailingZeros(bitNum, data);
+}

+ 111 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/fetchWalletTokenAccounts.ts

@@ -0,0 +1,111 @@
+import { TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from '@solana/spl-token';
+import {
+  AccountInfo,
+  Commitment,
+  Connection,
+  GetProgramAccountsResponse,
+  PublicKey,
+  RpcResponseAndContext,
+} from '@solana/web3.js';
+import BN from 'bn.js';
+
+import { SPLTokenAccountLayout } from '../layout';
+import { GetStructureSchema } from '../libs/marshmallow/index';
+import { getATAAddress } from '../pda';
+
+export interface ParseTokenAccount {
+  owner: PublicKey;
+  solAccountResp?: AccountInfo<Buffer> | null;
+  tokenAccountResp: RpcResponseAndContext<GetProgramAccountsResponse>;
+}
+
+export type ISPLTokenAccount = GetStructureSchema<typeof SPLTokenAccountLayout>;
+
+export interface TokenAccountRaw {
+  programId: PublicKey;
+  pubkey: PublicKey;
+  accountInfo: ISPLTokenAccount;
+}
+
+export interface TokenAccount {
+  publicKey?: PublicKey;
+  mint: PublicKey;
+  isAssociated?: boolean;
+  amount: BN;
+  isNative: boolean;
+  programId: PublicKey;
+}
+
+/**
+ * Get all token account information in the wallet (pure function version)
+ *
+ * This function will simultaneously query user's SOL account, standard SPL token accounts, and Token-2022 accounts.
+ *
+ * @param connection - Solana connection instance
+ * @param ownerPubKey - Public key of the wallet owner
+ * @param commitment - Optional commitment level
+ * @returns Promise containing token accounts and raw account information
+ */
+export async function fetchWalletTokenAccounts(
+  connection: Connection,
+  ownerPubKey: PublicKey,
+  commitment?: Commitment,
+): Promise<{
+  tokenAccounts: TokenAccount[];
+  rawTokenAccountInfos: TokenAccountRaw[];
+}> {
+  // Request all account information in parallel
+  const [solAccountResp, ownerTokenAccountResp, ownerToken2022AccountResp] = await Promise.all([
+    connection.getAccountInfo(ownerPubKey, commitment),
+    connection.getTokenAccountsByOwner(ownerPubKey, { programId: TOKEN_PROGRAM_ID }, commitment),
+    connection.getTokenAccountsByOwner(ownerPubKey, { programId: TOKEN_2022_PROGRAM_ID }, commitment),
+  ]);
+
+  // Parse and merge all token account information
+  const { tokenAccounts, rawTokenAccountInfos } = parseTokenAccountResp({
+    owner: ownerPubKey,
+    solAccountResp,
+    tokenAccountResp: {
+      context: ownerTokenAccountResp.context,
+      value: [...ownerTokenAccountResp.value, ...ownerToken2022AccountResp.value],
+    },
+  });
+
+  return { tokenAccounts, rawTokenAccountInfos };
+}
+
+export function parseTokenAccountResp({ owner, solAccountResp, tokenAccountResp }: ParseTokenAccount): {
+  tokenAccounts: TokenAccount[];
+  rawTokenAccountInfos: TokenAccountRaw[];
+} {
+  const tokenAccounts: TokenAccount[] = [];
+  const rawTokenAccountInfos: TokenAccountRaw[] = [];
+
+  for (const { pubkey, account } of tokenAccountResp.value) {
+    const accountInfo = SPLTokenAccountLayout.decode(account.data);
+    const { mint, amount } = accountInfo;
+    tokenAccounts.push({
+      publicKey: pubkey,
+      mint,
+      amount,
+      isAssociated: getATAAddress(owner, mint, account.owner).publicKey.equals(pubkey),
+      isNative: false,
+      programId: account.owner,
+    });
+    rawTokenAccountInfos.push({ pubkey, accountInfo, programId: account.owner });
+  }
+
+  if (solAccountResp) {
+    tokenAccounts.push({
+      mint: PublicKey.default,
+      amount: new BN(String(solAccountResp.lamports)),
+      isNative: true,
+      programId: solAccountResp.owner,
+    });
+  }
+
+  return {
+    tokenAccounts,
+    rawTokenAccountInfos,
+  };
+}

+ 29 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/getTickArrayBitmapExtension.ts

@@ -0,0 +1,29 @@
+import { Connection, PublicKey } from '@solana/web3.js';
+
+import { TickArrayBitmapExtensionLayout } from '../layout';
+import { getPdaExBitmapAccount } from '../pda';
+
+import { TickArrayBitmapExtensionType } from './models';
+
+/**
+ * @description Get the tick array bitmap extension
+ *
+ */
+export async function getTickArrayBitmapExtension(
+  programId: PublicKey,
+  poolId: PublicKey,
+  connection: Connection,
+): Promise<TickArrayBitmapExtensionType> {
+  const exBitmapAddress = getPdaExBitmapAccount(programId, new PublicKey(poolId)).publicKey;
+  const exBitmapInfo = await connection.getAccountInfo(exBitmapAddress);
+  if (!exBitmapInfo) {
+    throw new Error('exBitmapInfo not found');
+  }
+  const exBitmapInfoData = TickArrayBitmapExtensionLayout.decode(exBitmapInfo.data);
+  return {
+    poolId: new PublicKey(poolId),
+    exBitmapAddress,
+    positiveTickArrayBitmap: exBitmapInfoData.positiveTickArrayBitmap,
+    negativeTickArrayBitmap: exBitmapInfoData.negativeTickArrayBitmap,
+  };
+}

+ 105 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/getTickArrayInfo.ts

@@ -0,0 +1,105 @@
+import { Connection, PublicKey } from '@solana/web3.js';
+import BN from 'bn.js';
+
+import { TickArrayLayout } from '../layout';
+import { IPoolLayoutWithId, TickArrayBitmapExtensionType } from '../models';
+import { getPdaTickArrayAddress } from '../pda';
+
+import { TickArrayContainer } from './models';
+import { TickUtils } from './tick';
+import { TickArrayUtils } from './tickArrayUtils';
+
+/**
+ * Get tick array cache
+ * Now returns TickArrayContainer which supports both fixed and dynamic tick arrays
+ */
+export async function getTickArrayInfo({
+  connection,
+  poolInfo,
+  exBitmapInfo,
+  expectedCount = 7, // Default get 7 tick arrays
+}: {
+  connection: Connection;
+  poolInfo: IPoolLayoutWithId;
+  exBitmapInfo: TickArrayBitmapExtensionType;
+  expectedCount?: number;
+}): Promise<{ [key: string]: TickArrayContainer }> {
+  poolInfo;
+  // 1. Calculate the starting index of the tick array where the current tick is located
+  const currentTickArrayStartIndex = TickUtils.getTickArrayStartIndexByTick(poolInfo.tickCurrent, poolInfo.tickSpacing);
+
+  // 2. Get the list of tick array start indices that need to be loaded
+  const startIndexArray = TickUtils.getInitializedTickArrayInRange(
+    poolInfo.tickArrayBitmap,
+    exBitmapInfo,
+    poolInfo.tickSpacing,
+    currentTickArrayStartIndex,
+    expectedCount,
+  );
+
+  // 3. Calculate PDA addresses for all tick arrays
+  const tickArrayAddresses: PublicKey[] = [];
+  const startIndexToAddress: { [key: number]: PublicKey } = {};
+
+  for (const startIndex of startIndexArray) {
+    const { publicKey: tickArrayAddress } = getPdaTickArrayAddress(poolInfo.programId, poolInfo.poolId, startIndex);
+    tickArrayAddresses.push(tickArrayAddress);
+    startIndexToAddress[startIndex] = tickArrayAddress;
+  }
+
+  // 4. Batch fetch on-chain account data
+  const accountInfos = await connection.getMultipleAccountsInfo(tickArrayAddresses);
+
+  // 5. Decode and build cache (now supports both fixed and dynamic tick arrays)
+  const tickArrayCache: { [key: string]: TickArrayContainer } = {};
+
+  for (let i = 0; i < accountInfos.length; i++) {
+    const accountInfo = accountInfos[i];
+    const startIndex = startIndexArray[i];
+
+    if (!accountInfo || !accountInfo.data) {
+      console.warn(`Tick array at index ${startIndex} not found`);
+      continue;
+    }
+
+    try {
+      // Identify tick array type
+      const tickArrayType = TickArrayUtils.identifyTickArrayType(accountInfo.data);
+
+      if (tickArrayType === 'Fixed') {
+        // Fixed tick array: use TickArrayLayout
+        const decoded = TickArrayLayout.decode(accountInfo.data);
+
+        tickArrayCache[startIndex.toString()] = {
+          type: 'Fixed',
+          data: {
+            address: tickArrayAddresses[i],
+            poolId: decoded.poolId,
+            startTickIndex: decoded.startTickIndex,
+            ticks: decoded.ticks.map((tick: any) => ({
+              tick: tick.tick,
+              liquidityNet: new BN(tick.liquidityNet.toString()),
+              liquidityGross: new BN(tick.liquidityGross.toString()),
+              feeGrowthOutsideX64A: new BN(tick.feeGrowthOutsideX64A.toString()),
+              feeGrowthOutsideX64B: new BN(tick.feeGrowthOutsideX64B.toString()),
+              rewardGrowthsOutsideX64: tick.rewardGrowthsOutsideX64.map((r: any) => new BN(r.toString())),
+            })),
+            initializedTickCount: decoded.initializedTickCount,
+          },
+        };
+      } else {
+        // Dynamic tick array: use decodeDynTickArray
+        const decoded = TickArrayUtils.decodeDynTickArray(accountInfo.data, tickArrayAddresses[i]);
+
+        tickArrayCache[startIndex.toString()] = {
+          type: 'Dynamic',
+          data: decoded,
+        };
+      }
+    } catch (error) {
+      console.error(`Failed to decode tick array at index ${startIndex}:`, error);
+    }
+  }
+
+  return tickArrayCache;
+}

+ 28 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/index.ts

@@ -0,0 +1,28 @@
+import { PublicKey } from '@solana/web3.js';
+
+export function findProgramAddress(
+  seeds: Array<Buffer | Uint8Array>,
+  programId: PublicKey,
+): {
+  publicKey: PublicKey;
+  nonce: number;
+} {
+  const [publicKey, nonce] = PublicKey.findProgramAddressSync(seeds, programId);
+  return { publicKey, nonce };
+}
+
+export * from './binaryUtils';
+export * from './liquidityMath';
+export * from './mathUtils';
+export * from './poolUtils';
+export * from './sqrtPriceMath';
+export * from './tickMath';
+export * from './tick';
+export * from './tickarrayBitmap';
+export * from './transfer';
+export * from './position';
+export * from './getTickArrayInfo';
+export * from './swapMath';
+export * from './getTickArrayBitmapExtension';
+export * from './tickArrayUtils';
+export * from './models';

+ 617 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/liquidityMath.test.ts

@@ -0,0 +1,617 @@
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+import { describe, test, expect } from 'vitest';
+
+import { LiquidityMath } from './liquidityMath';
+import { SqrtPriceMath } from './sqrtPriceMath';
+
+describe('LiquidityMath.getLiquidityFromTokenAmounts', () => {
+  test('current price is below the lower limit - only consider tokenA', () => {
+    // price: current < A < B
+    const sqrtPriceCurrentX64 = new BN('100000');
+    const sqrtPriceX64A = new BN('150000');
+    const sqrtPriceX64B = new BN('300000');
+    const amountA = new BN('1000000');
+    const amountB = new BN('500000');
+
+    // manually calculate the expected result
+    const expectedLiquidity = LiquidityMath.getLiquidityFromTokenAmountA(sqrtPriceX64A, sqrtPriceX64B, amountA, false);
+
+    const liquidity = LiquidityMath.getLiquidityFromTokenAmounts(
+      sqrtPriceCurrentX64,
+      sqrtPriceX64A,
+      sqrtPriceX64B,
+      amountA,
+      amountB,
+    );
+
+    // verify the result
+    expect(liquidity.toString()).toBe(expectedLiquidity.toString());
+  });
+
+  test('current price is in the range - take the minimum of the two liquiditys', () => {
+    // price: A < current < B
+    const sqrtPriceX64A = new BN('100000');
+    const sqrtPriceCurrentX64 = new BN('200000');
+    const sqrtPriceX64B = new BN('300000');
+    const amountA = new BN('1000000');
+    const amountB = new BN('500000');
+
+    // calculate the two liquiditys
+    const liquidityA = LiquidityMath.getLiquidityFromTokenAmountA(sqrtPriceCurrentX64, sqrtPriceX64B, amountA, false);
+
+    const liquidityB = LiquidityMath.getLiquidityFromTokenAmountB(sqrtPriceX64A, sqrtPriceCurrentX64, amountB);
+
+    // the expected result is the minimum of the two liquiditys
+    const expectedLiquidity = BN.min(liquidityA, liquidityB);
+
+    const liquidity = LiquidityMath.getLiquidityFromTokenAmounts(
+      sqrtPriceCurrentX64,
+      sqrtPriceX64A,
+      sqrtPriceX64B,
+      amountA,
+      amountB,
+    );
+
+    // verify the result
+    expect(liquidity.toString()).toBe(expectedLiquidity.toString());
+  });
+
+  test('current price is above the upper limit - only consider tokenB', () => {
+    // price: A < B < current
+    const sqrtPriceX64A = new BN('100000');
+    const sqrtPriceX64B = new BN('200000');
+    const sqrtPriceCurrentX64 = new BN('300000');
+    const amountA = new BN('1000000');
+    const amountB = new BN('500000');
+
+    // manually calculate the expected result
+    const expectedLiquidity = LiquidityMath.getLiquidityFromTokenAmountB(sqrtPriceX64A, sqrtPriceX64B, amountB);
+
+    const liquidity = LiquidityMath.getLiquidityFromTokenAmounts(
+      sqrtPriceCurrentX64,
+      sqrtPriceX64A,
+      sqrtPriceX64B,
+      amountA,
+      amountB,
+    );
+
+    // verify the result
+    expect(liquidity.toString()).toBe(expectedLiquidity.toString());
+  });
+
+  test('price range order adjustment - ensure A is always less than B', () => {
+    // test the case of reversed price order: B < A
+    const sqrtPriceCurrentX64 = new BN('200000');
+    const sqrtPriceX64B = new BN('100000'); // deliberately reversed
+    const sqrtPriceX64A = new BN('300000'); // deliberately reversed
+    const amountA = new BN('1000000');
+    const amountB = new BN('500000');
+
+    // call in the correct order
+    const expectedLiquidity = LiquidityMath.getLiquidityFromTokenAmounts(
+      sqrtPriceCurrentX64,
+      sqrtPriceX64A,
+      sqrtPriceX64B,
+      amountA,
+      amountB,
+    );
+
+    // call in the reversed order
+    const liquidity = LiquidityMath.getLiquidityFromTokenAmounts(
+      sqrtPriceCurrentX64,
+      sqrtPriceX64B,
+      sqrtPriceX64A,
+      amountA,
+      amountB,
+    );
+
+    // verify the result is the same
+    expect(liquidity.toString()).toBe(expectedLiquidity.toString());
+  });
+});
+
+describe('LiquidityMath.getAmountsFromLiquidity', () => {
+  test('current price is below the lower limit - only return tokenA', () => {
+    // price: current < A < B
+    const sqrtPriceCurrentX64 = new BN('100000');
+    const sqrtPriceX64A = new BN('150000');
+    const sqrtPriceX64B = new BN('300000');
+    const liquidity = new BN('10000000');
+    const roundUp = true;
+
+    // manually calculate the expected tokenA quantity
+    const expectedAmountA = LiquidityMath.getTokenAmountAFromLiquidity(
+      sqrtPriceX64A,
+      sqrtPriceX64B,
+      liquidity,
+      roundUp,
+    );
+
+    const { amountA, amountB } = LiquidityMath.getAmountsFromLiquidity(
+      sqrtPriceCurrentX64,
+      sqrtPriceX64A,
+      sqrtPriceX64B,
+      liquidity,
+      roundUp,
+    );
+
+    // verify the result
+    expect(amountA.toString()).toBe(expectedAmountA.toString());
+    expect(amountB.toString()).toBe('0');
+  });
+
+  test('current price is in the range - return two tokens', () => {
+    // price: A < current < B
+    const sqrtPriceX64A = new BN('100000');
+    const sqrtPriceCurrentX64 = new BN('200000');
+    const sqrtPriceX64B = new BN('300000');
+    const liquidity = new BN('10000000');
+    const roundUp = true;
+
+    // manually calculate the expected quantity
+    const expectedAmountA = LiquidityMath.getTokenAmountAFromLiquidity(
+      sqrtPriceCurrentX64,
+      sqrtPriceX64B,
+      liquidity,
+      roundUp,
+    );
+
+    const expectedAmountB = LiquidityMath.getTokenAmountBFromLiquidity(
+      sqrtPriceX64A,
+      sqrtPriceCurrentX64,
+      liquidity,
+      roundUp,
+    );
+
+    const { amountA, amountB } = LiquidityMath.getAmountsFromLiquidity(
+      sqrtPriceCurrentX64,
+      sqrtPriceX64A,
+      sqrtPriceX64B,
+      liquidity,
+      roundUp,
+    );
+
+    // verify the result
+    expect(amountA.toString()).toBe(expectedAmountA.toString());
+    expect(amountB.toString()).toBe(expectedAmountB.toString());
+  });
+
+  test('current price is above the upper limit - only return tokenB', () => {
+    // price: A < B < current
+    const sqrtPriceX64A = new BN('100000');
+    const sqrtPriceX64B = new BN('200000');
+    const sqrtPriceCurrentX64 = new BN('300000');
+    const liquidity = new BN('10000000');
+    const roundUp = true;
+
+    // manually calculate the expected tokenB quantity
+    const expectedAmountB = LiquidityMath.getTokenAmountBFromLiquidity(
+      sqrtPriceX64A,
+      sqrtPriceX64B,
+      liquidity,
+      roundUp,
+    );
+
+    const { amountA, amountB } = LiquidityMath.getAmountsFromLiquidity(
+      sqrtPriceCurrentX64,
+      sqrtPriceX64A,
+      sqrtPriceX64B,
+      liquidity,
+      roundUp,
+    );
+
+    // verify the result
+    expect(amountA.toString()).toBe('0');
+    expect(amountB.toString()).toBe(expectedAmountB.toString());
+  });
+
+  test('rounding behavior test - roundUp = true vs false', () => {
+    const sqrtPriceX64A = new BN('100000');
+    const sqrtPriceCurrentX64 = new BN('200000');
+    const sqrtPriceX64B = new BN('300000');
+    const liquidity = new BN('10000000');
+
+    // use roundUp = true
+    const resultRoundUp = LiquidityMath.getAmountsFromLiquidity(
+      sqrtPriceCurrentX64,
+      sqrtPriceX64A,
+      sqrtPriceX64B,
+      liquidity,
+      true,
+    );
+
+    // use roundUp = false
+    const resultRoundDown = LiquidityMath.getAmountsFromLiquidity(
+      sqrtPriceCurrentX64,
+      sqrtPriceX64A,
+      sqrtPriceX64B,
+      liquidity,
+      false,
+    );
+
+    expect(new BN(resultRoundUp.amountA).gte(new BN(resultRoundDown.amountA))).toBeTruthy();
+    expect(new BN(resultRoundUp.amountB).gte(new BN(resultRoundDown.amountB))).toBeTruthy();
+  });
+});
+
+describe('LiquidityMath.getAmountBFromAmountA', () => {
+  test('calculate tokenB from tokenA - normal case', () => {
+    // price: start < current < end
+    const startSqrtPriceX64 = new BN('100000');
+    const currentSqrtPriceX64 = new BN('200000');
+    const endSqrtPriceX64 = new BN('300000');
+    const amountA = new BN('1000000');
+
+    const amountB = LiquidityMath.getAmountBFromAmountA(
+      startSqrtPriceX64,
+      endSqrtPriceX64,
+      currentSqrtPriceX64,
+      amountA,
+    );
+
+    // verify the correctness of the calculation process
+    // 1. calculate the liquidity
+    const liquidity = LiquidityMath.getLiquidityFromTokenAmountA(currentSqrtPriceX64, endSqrtPriceX64, amountA, false);
+
+    // 2. calculate the tokenB quantity from the liquidity
+    const expectedAmountB = LiquidityMath.getTokenAmountBFromLiquidity(
+      startSqrtPriceX64,
+      currentSqrtPriceX64,
+      liquidity,
+      true,
+    );
+
+    // verify the result
+    expect(amountB.toString()).toBe(expectedAmountB.toString());
+  });
+
+  test('current price is below the lower limit - return 0', () => {
+    // price: current < start < end
+    const startSqrtPriceX64 = new BN('200000');
+    const currentSqrtPriceX64 = new BN('100000'); // below the lower limit
+    const endSqrtPriceX64 = new BN('300000');
+    const amountA = new BN('1000000');
+
+    const amountB = LiquidityMath.getAmountBFromAmountA(
+      startSqrtPriceX64,
+      endSqrtPriceX64,
+      currentSqrtPriceX64,
+      amountA,
+    );
+
+    // verify the result is 0
+    expect(amountB.toString()).toBe('0');
+  });
+
+  test('current price is above the upper limit - return 0', () => {
+    // price: start < end < current
+    const startSqrtPriceX64 = new BN('100000');
+    const endSqrtPriceX64 = new BN('200000');
+    const currentSqrtPriceX64 = new BN('300000'); // above the upper limit
+    const amountA = new BN('1000000');
+
+    const amountB = LiquidityMath.getAmountBFromAmountA(
+      startSqrtPriceX64,
+      endSqrtPriceX64,
+      currentSqrtPriceX64,
+      amountA,
+    );
+
+    // verify the result is 0
+    expect(amountB.toString()).toBe('0');
+  });
+
+  test('price range order adjustment - ensure start is always less than end', () => {
+    // test the case of reversed price order: end < start
+    const endSqrtPriceX64 = new BN('100000'); // deliberately reversed
+    const currentSqrtPriceX64 = new BN('150000');
+    const startSqrtPriceX64 = new BN('200000'); // deliberately reversed
+    const amountA = new BN('1000000');
+
+    // call in the correct order
+    const expectedAmountB = LiquidityMath.getAmountBFromAmountA(
+      endSqrtPriceX64,
+      startSqrtPriceX64,
+      currentSqrtPriceX64,
+      amountA,
+    );
+
+    // call in the reversed order
+    const amountB = LiquidityMath.getAmountBFromAmountA(
+      startSqrtPriceX64,
+      endSqrtPriceX64,
+      currentSqrtPriceX64,
+      amountA,
+    );
+
+    // verify the result is the same
+    expect(amountB.toString()).toBe(expectedAmountB.toString());
+  });
+});
+
+describe('LiquidityMath.getAmountAFromAmountB', () => {
+  test('calculate tokenA from tokenB - normal case', () => {
+    // price: start < current < end
+    const startSqrtPriceX64 = new BN('100000');
+    const currentSqrtPriceX64 = new BN('200000');
+    const endSqrtPriceX64 = new BN('300000');
+    const amountB = new BN('500000');
+
+    const amountA = LiquidityMath.getAmountAFromAmountB(
+      startSqrtPriceX64,
+      endSqrtPriceX64,
+      currentSqrtPriceX64,
+      amountB,
+    );
+
+    // verify the correctness of the calculation process
+    // 1. calculate the liquidity
+    const liquidity = LiquidityMath.getLiquidityFromTokenAmountB(startSqrtPriceX64, currentSqrtPriceX64, amountB);
+
+    // 2. calculate the tokenA quantity from the liquidity
+    const expectedAmountA = LiquidityMath.getTokenAmountAFromLiquidity(
+      currentSqrtPriceX64,
+      endSqrtPriceX64,
+      liquidity,
+      true,
+    );
+
+    // verify the result
+    expect(amountA.toString()).toBe(expectedAmountA.toString());
+  });
+
+  test('current price is below the lower limit - return 0', () => {
+    // price: current < start < end
+    const startSqrtPriceX64 = new BN('200000');
+    const currentSqrtPriceX64 = new BN('100000'); // below the lower limit
+    const endSqrtPriceX64 = new BN('300000');
+    const amountB = new BN('500000');
+
+    const amountA = LiquidityMath.getAmountAFromAmountB(
+      startSqrtPriceX64,
+      endSqrtPriceX64,
+      currentSqrtPriceX64,
+      amountB,
+    );
+
+    // verify the result is 0
+    expect(amountA.toString()).toBe('0');
+  });
+
+  test('current price is above the upper limit - return 0', () => {
+    // price: start < end < current
+    const startSqrtPriceX64 = new BN('100000');
+    const endSqrtPriceX64 = new BN('200000');
+    const currentSqrtPriceX64 = new BN('300000'); // above the upper limit
+    const amountB = new BN('500000');
+
+    const amountA = LiquidityMath.getAmountAFromAmountB(
+      startSqrtPriceX64,
+      endSqrtPriceX64,
+      currentSqrtPriceX64,
+      amountB,
+    );
+
+    // verify the result is 0
+    expect(amountA.toString()).toBe('0');
+  });
+
+  test('price range order adjustment - ensure start is always less than end', () => {
+    // test the case of reversed price order: end < start
+    const endSqrtPriceX64 = new BN('100000'); // deliberately reversed
+    const currentSqrtPriceX64 = new BN('150000');
+    const startSqrtPriceX64 = new BN('200000'); // deliberately reversed
+    const amountB = new BN('500000');
+
+    // call in the correct order
+    const expectedAmountA = LiquidityMath.getAmountAFromAmountB(
+      endSqrtPriceX64,
+      startSqrtPriceX64,
+      currentSqrtPriceX64,
+      amountB,
+    );
+
+    // call in the reversed order
+    const amountA = LiquidityMath.getAmountAFromAmountB(
+      startSqrtPriceX64,
+      endSqrtPriceX64,
+      currentSqrtPriceX64,
+      amountB,
+    );
+
+    // verify the result is the same
+    expect(amountA.toString()).toBe(expectedAmountA.toString());
+  });
+});
+
+describe('LiquidityMath consistency test', () => {
+  test('liquidity calculation and token quantity calculation consistency', () => {
+    const startPirce = '1';
+    const currentPrice = '2';
+    const endPrice = '3';
+
+    const decimalsA = 6;
+    const decimalsB = 6;
+
+    // set test parameters
+    const sqrtPriceX64A = SqrtPriceMath.priceToSqrtPriceX64(new Decimal(startPirce), decimalsA, decimalsB);
+    const sqrtPriceCurrentX64 = SqrtPriceMath.priceToSqrtPriceX64(new Decimal(currentPrice), decimalsA, decimalsB);
+    const sqrtPriceX64B = SqrtPriceMath.priceToSqrtPriceX64(new Decimal(endPrice), decimalsA, decimalsB);
+    const amountA = new BN('1000000');
+    const amountB = new BN('500000');
+
+    // 1. use token quantity to calculate liquidity
+    const liquidity = LiquidityMath.getLiquidityFromTokenAmounts(
+      sqrtPriceCurrentX64,
+      sqrtPriceX64A,
+      sqrtPriceX64B,
+      amountA,
+      amountB,
+    );
+
+    // 2. use the calculated liquidity to calculate the token quantity
+    const { amountA: calculatedAmountA, amountB: calculatedAmountB } = LiquidityMath.getAmountsFromLiquidity(
+      sqrtPriceCurrentX64,
+      sqrtPriceX64A,
+      sqrtPriceX64B,
+      liquidity,
+      false, // do not round up for comparison
+    );
+
+    // verify: the calculated token quantity should be less than or equal to the original token quantity
+    // (since the liquidity depends on the smaller of the two tokens provided)
+    expect(new BN(calculatedAmountA).lte(amountA)).toBeTruthy();
+    expect(new BN(calculatedAmountB).lte(amountB)).toBeTruthy();
+
+    // verify: at least one of the calculated token quantities should be close to the original token quantity
+    const precisionThreshold = 0.0001; // allow 0.01% error
+
+    const amountADiff = Math.abs(1 - Number(calculatedAmountA.toString()) / Number(amountA.toString()));
+
+    const amountBDiff = Math.abs(1 - Number(calculatedAmountB.toString()) / Number(amountB.toString()));
+
+    expect(amountADiff < precisionThreshold || amountBDiff < precisionThreshold).toBeTruthy();
+  });
+
+  test('consistency test between getAmountBFromAmountA and getAmountAFromAmountB', () => {
+    const startPirce = '1';
+    const currentPrice = '2';
+    const endPrice = '3';
+
+    const decimalsA = 6;
+    const decimalsB = 6;
+
+    // set test parameters
+    const startSqrtPriceX64 = SqrtPriceMath.priceToSqrtPriceX64(new Decimal(startPirce), decimalsA, decimalsB);
+    const currentSqrtPriceX64 = SqrtPriceMath.priceToSqrtPriceX64(new Decimal(currentPrice), decimalsA, decimalsB);
+    const endSqrtPriceX64 = SqrtPriceMath.priceToSqrtPriceX64(new Decimal(endPrice), decimalsA, decimalsB);
+    const initialAmountA = new BN('1000000');
+
+    // 1. A -> B
+    const calculatedAmountB = LiquidityMath.getAmountBFromAmountA(
+      startSqrtPriceX64,
+      endSqrtPriceX64,
+      currentSqrtPriceX64,
+      initialAmountA,
+    );
+
+    // 2. B -> A
+    const calculatedAmountA = LiquidityMath.getAmountAFromAmountB(
+      startSqrtPriceX64,
+      endSqrtPriceX64,
+      currentSqrtPriceX64,
+      calculatedAmountB,
+    );
+
+    // verify: the calculated A quantity should be close to the original value
+    // due to rounding errors, it may not be exactly equal, so use relative error
+    const relativeError =
+      Math.abs(Number(initialAmountA.toString()) - Number(calculatedAmountA.toString())) /
+      Number(initialAmountA.toString());
+
+    expect(relativeError).toBeLessThan(0.0001); // 允许 0.01% 误差
+  });
+});
+
+describe('LiquidityMath boundary and special case test', () => {
+  test('zero input test', () => {
+    const sqrtPriceX64A = new BN('100000');
+    const sqrtPriceCurrentX64 = new BN('200000');
+    const sqrtPriceX64B = new BN('300000');
+
+    // zero liquidity
+    const zeroLiquidity = new BN(0);
+    const { amountA, amountB } = LiquidityMath.getAmountsFromLiquidity(
+      sqrtPriceCurrentX64,
+      sqrtPriceX64A,
+      sqrtPriceX64B,
+      zeroLiquidity,
+      true,
+    );
+
+    expect(amountA.toString()).toBe('0');
+    expect(amountB.toString()).toBe('0');
+
+    // zero token quantity
+    const zeroAmount = new BN(0);
+    const amountAResult = LiquidityMath.getAmountAFromAmountB(
+      sqrtPriceX64A,
+      sqrtPriceX64B,
+      sqrtPriceCurrentX64,
+      zeroAmount,
+    );
+
+    const amountBResult = LiquidityMath.getAmountBFromAmountA(
+      sqrtPriceX64A,
+      sqrtPriceX64B,
+      sqrtPriceCurrentX64,
+      zeroAmount,
+    );
+
+    expect(amountAResult.toString()).toBe('0');
+    expect(amountBResult.toString()).toBe('0');
+  });
+
+  test('extreme liquidity value test', () => {
+    const sqrtPriceX64A = new BN('100000');
+    const sqrtPriceCurrentX64 = new BN('200000');
+    const sqrtPriceX64B = new BN('300000');
+
+    // extremely large liquidity value
+    const largeLiquidity = new BN('340282366920938463463374607431768211455'); // MaxUint128 - 1
+
+    // this test is mainly to ensure that no exceptions or overflows are thrown
+    const { amountA, amountB } = LiquidityMath.getAmountsFromLiquidity(
+      sqrtPriceCurrentX64,
+      sqrtPriceX64A,
+      sqrtPriceX64B,
+      largeLiquidity,
+      true,
+    );
+
+    expect(amountA.toString()).not.toBe('NaN');
+    expect(amountB.toString()).not.toBe('NaN');
+  });
+
+  test('equal price point test', () => {
+    // test the case of sqrtPriceX64A = sqrtPriceX64B
+    const sqrtPriceX64 = new BN('200000');
+    const sqrtPriceCurrentX64 = new BN('150000');
+    const liquidity = new BN('10000000');
+
+    // when the price is equal
+    const { amountA, amountB } = LiquidityMath.getAmountsFromLiquidity(
+      sqrtPriceCurrentX64,
+      sqrtPriceX64,
+      sqrtPriceX64,
+      liquidity,
+      true,
+    );
+
+    // expected to be zero or very small
+    expect(amountA.toString()).toBe('0');
+    expect(amountB.toString()).toBe('0');
+  });
+
+  test('price point near current price test', () => {
+    // test the case of current ≈ A or current ≈ B
+    const sqrtPriceX64A = new BN('200000');
+    const sqrtPriceCurrentX64 = new BN('200001'); // very close to A
+    const sqrtPriceX64B = new BN('300000');
+    const liquidity = new BN('10000000');
+
+    // call the function
+    const result = LiquidityMath.getAmountsFromLiquidity(
+      sqrtPriceCurrentX64,
+      sqrtPriceX64A,
+      sqrtPriceX64B,
+      liquidity,
+      true,
+    );
+
+    // verify the result
+    expect(result.amountA.toString()).not.toBe('NaN');
+    expect(result.amountB.toString()).not.toBe('NaN');
+  });
+});

+ 212 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/liquidityMath.ts

@@ -0,0 +1,212 @@
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+import { MaxU64, ONE, Q64, U64Resolution, ZERO } from '../constants';
+
+import { MathUtils } from './mathUtils';
+
+export class LiquidityMath {
+  public static addDelta(x: BN, y: BN): BN {
+    return x.add(y);
+  }
+
+  public static getTokenAmountAFromLiquidity(
+    sqrtPriceX64A: BN,
+    sqrtPriceX64B: BN,
+    liquidity: BN,
+    roundUp: boolean,
+  ): BN {
+    if (sqrtPriceX64A.gt(sqrtPriceX64B)) {
+      [sqrtPriceX64A, sqrtPriceX64B] = [sqrtPriceX64B, sqrtPriceX64A];
+    }
+
+    if (!sqrtPriceX64A.gt(ZERO)) {
+      throw new Error('sqrtPriceX64A must greater than 0');
+    }
+
+    const numerator1 = liquidity.ushln(U64Resolution);
+    const numerator2 = sqrtPriceX64B.sub(sqrtPriceX64A);
+
+    return roundUp
+      ? MathUtils.mulDivRoundingUp(MathUtils.mulDivCeil(numerator1, numerator2, sqrtPriceX64B), ONE, sqrtPriceX64A)
+      : MathUtils.mulDivFloor(numerator1, numerator2, sqrtPriceX64B).div(sqrtPriceX64A);
+  }
+
+  public static getTokenAmountBFromLiquidity(
+    sqrtPriceX64A: BN,
+    sqrtPriceX64B: BN,
+    liquidity: BN,
+    roundUp: boolean,
+  ): BN {
+    if (sqrtPriceX64A.gt(sqrtPriceX64B)) {
+      [sqrtPriceX64A, sqrtPriceX64B] = [sqrtPriceX64B, sqrtPriceX64A];
+    }
+    if (!sqrtPriceX64A.gt(ZERO)) {
+      throw new Error('sqrtPriceX64A must greater than 0');
+    }
+
+    return roundUp
+      ? MathUtils.mulDivCeil(liquidity, sqrtPriceX64B.sub(sqrtPriceX64A), Q64)
+      : MathUtils.mulDivFloor(liquidity, sqrtPriceX64B.sub(sqrtPriceX64A), Q64);
+  }
+
+  public static getLiquidityFromTokenAmountA(sqrtPriceX64A: BN, sqrtPriceX64B: BN, amountA: BN, roundUp: boolean): BN {
+    if (sqrtPriceX64A.gt(sqrtPriceX64B)) {
+      [sqrtPriceX64A, sqrtPriceX64B] = [sqrtPriceX64B, sqrtPriceX64A];
+    }
+
+    const numerator = amountA.mul(sqrtPriceX64A).mul(sqrtPriceX64B);
+    const denominator = sqrtPriceX64B.sub(sqrtPriceX64A);
+    const result = numerator.div(denominator);
+
+    if (roundUp) {
+      return MathUtils.mulDivRoundingUp(result, ONE, MaxU64);
+    } else {
+      return result.shrn(U64Resolution);
+    }
+  }
+
+  public static getLiquidityFromTokenAmountB(sqrtPriceX64A: BN, sqrtPriceX64B: BN, amountB: BN): BN {
+    if (sqrtPriceX64A.gt(sqrtPriceX64B)) {
+      [sqrtPriceX64A, sqrtPriceX64B] = [sqrtPriceX64B, sqrtPriceX64A];
+    }
+    return MathUtils.mulDivFloor(amountB, MaxU64, sqrtPriceX64B.sub(sqrtPriceX64A));
+  }
+
+  public static getLiquidityFromTokenAmounts(
+    sqrtPriceCurrentX64: BN,
+    sqrtPriceX64A: BN,
+    sqrtPriceX64B: BN,
+    amountA: BN,
+    amountB: BN,
+  ): BN {
+    if (sqrtPriceX64A.gt(sqrtPriceX64B)) {
+      [sqrtPriceX64A, sqrtPriceX64B] = [sqrtPriceX64B, sqrtPriceX64A];
+    }
+
+    if (sqrtPriceCurrentX64.lte(sqrtPriceX64A)) {
+      return LiquidityMath.getLiquidityFromTokenAmountA(sqrtPriceX64A, sqrtPriceX64B, amountA, false);
+    } else if (sqrtPriceCurrentX64.lt(sqrtPriceX64B)) {
+      const liquidity0 = LiquidityMath.getLiquidityFromTokenAmountA(sqrtPriceCurrentX64, sqrtPriceX64B, amountA, false);
+      const liquidity1 = LiquidityMath.getLiquidityFromTokenAmountB(sqrtPriceX64A, sqrtPriceCurrentX64, amountB);
+      return liquidity0.lt(liquidity1) ? liquidity0 : liquidity1;
+    } else {
+      return LiquidityMath.getLiquidityFromTokenAmountB(sqrtPriceX64A, sqrtPriceX64B, amountB);
+    }
+  }
+
+  public static getAmountsFromLiquidity(
+    sqrtPriceCurrentX64: BN,
+    sqrtPriceX64A: BN,
+    sqrtPriceX64B: BN,
+    liquidity: BN,
+    roundUp: boolean,
+  ): { amountA: BN; amountB: BN } {
+    if (sqrtPriceX64A.gt(sqrtPriceX64B)) {
+      [sqrtPriceX64A, sqrtPriceX64B] = [sqrtPriceX64B, sqrtPriceX64A];
+    }
+
+    if (sqrtPriceCurrentX64.lte(sqrtPriceX64A)) {
+      return {
+        amountA: LiquidityMath.getTokenAmountAFromLiquidity(sqrtPriceX64A, sqrtPriceX64B, liquidity, roundUp),
+        amountB: new BN(0),
+      };
+    } else if (sqrtPriceCurrentX64.lt(sqrtPriceX64B)) {
+      const amountA = LiquidityMath.getTokenAmountAFromLiquidity(
+        sqrtPriceCurrentX64,
+        sqrtPriceX64B,
+        liquidity,
+        roundUp,
+      );
+      const amountB = LiquidityMath.getTokenAmountBFromLiquidity(
+        sqrtPriceX64A,
+        sqrtPriceCurrentX64,
+        liquidity,
+        roundUp,
+      );
+      return { amountA, amountB };
+    } else {
+      return {
+        amountA: new BN(0),
+        amountB: LiquidityMath.getTokenAmountBFromLiquidity(sqrtPriceX64A, sqrtPriceX64B, liquidity, roundUp),
+      };
+    }
+  }
+
+  public static getAmountsFromLiquidityWithSlippage(
+    sqrtPriceCurrentX64: BN,
+    sqrtPriceX64A: BN,
+    sqrtPriceX64B: BN,
+    liquidity: BN,
+    amountMax: boolean,
+    roundUp: boolean,
+    amountSlippage: number,
+  ): { amountSlippageA: BN; amountSlippageB: BN } {
+    const { amountA, amountB } = LiquidityMath.getAmountsFromLiquidity(
+      sqrtPriceCurrentX64,
+      sqrtPriceX64A,
+      sqrtPriceX64B,
+      liquidity,
+      roundUp,
+    );
+    const coefficient = amountMax ? 1 + amountSlippage : 1 - amountSlippage;
+
+    const amount0Slippage = new BN(new Decimal(amountA.toString()).mul(coefficient).toFixed(0));
+    const amount1Slippage = new BN(new Decimal(amountB.toString()).mul(coefficient).toFixed(0));
+    return {
+      amountSlippageA: amount0Slippage,
+      amountSlippageB: amount1Slippage,
+    };
+  }
+
+  /**
+   * Given a price range, calculate the required tokenB amount after investing a specified tokenA amount
+   *
+   * Similar implementation in raydium: getLiquidityAmountOutFromAmountIn
+   */
+  public static getAmountBFromAmountA(
+    startSqrtPriceX64: BN,
+    endSqrtPriceX64: BN,
+    currentSqrtPriceX64: BN,
+    amountA: BN,
+  ): BN {
+    if (startSqrtPriceX64.gt(endSqrtPriceX64)) {
+      [startSqrtPriceX64, endSqrtPriceX64] = [endSqrtPriceX64, startSqrtPriceX64];
+    }
+
+    if (currentSqrtPriceX64.lte(startSqrtPriceX64)) {
+      return new BN(0);
+    }
+
+    if (currentSqrtPriceX64.gt(endSqrtPriceX64)) {
+      return new BN(0);
+    }
+    const liquidity = LiquidityMath.getLiquidityFromTokenAmountA(currentSqrtPriceX64, endSqrtPriceX64, amountA, false);
+    return LiquidityMath.getTokenAmountBFromLiquidity(startSqrtPriceX64, currentSqrtPriceX64, liquidity, true);
+  }
+
+  /**
+   * Given a price range, calculate the required tokenA amount after investing a specified tokenB amount
+   *
+   * Similar implementation in raydium: getLiquidityAmountOutFromAmountIn
+   */
+  public static getAmountAFromAmountB(
+    startSqrtPriceX64: BN,
+    endSqrtPriceX64: BN,
+    currentSqrtPriceX64: BN,
+    amountB: BN,
+  ): BN {
+    if (startSqrtPriceX64.gt(endSqrtPriceX64)) {
+      [startSqrtPriceX64, endSqrtPriceX64] = [endSqrtPriceX64, startSqrtPriceX64];
+    }
+
+    if (currentSqrtPriceX64.lte(startSqrtPriceX64)) {
+      return new BN(0);
+    }
+    if (currentSqrtPriceX64.gt(endSqrtPriceX64)) {
+      return new BN(0);
+    }
+    const liquidity = LiquidityMath.getLiquidityFromTokenAmountB(startSqrtPriceX64, currentSqrtPriceX64, amountB);
+    return LiquidityMath.getTokenAmountAFromLiquidity(currentSqrtPriceX64, endSqrtPriceX64, liquidity, true);
+  }
+}

+ 42 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/mathUtils.ts

@@ -0,0 +1,42 @@
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+import { ONE, Q128, ZERO } from '../constants';
+
+export class MathUtils {
+  public static mulDivRoundingUp(a: BN, b: BN, denominator: BN): BN {
+    const numerator = a.mul(b);
+    let result = numerator.div(denominator);
+    if (!numerator.mod(denominator).eq(ZERO)) {
+      result = result.add(ONE);
+    }
+    return result;
+  }
+
+  public static mulDivFloor(a: BN, b: BN, denominator: BN): BN {
+    if (denominator.eq(ZERO)) {
+      throw new Error('division by 0');
+    }
+    return a.mul(b).div(denominator);
+  }
+
+  public static mulDivCeil(a: BN, b: BN, denominator: BN): BN {
+    if (denominator.eq(ZERO)) {
+      throw new Error('division by 0');
+    }
+    const numerator = a.mul(b).add(denominator.sub(ONE));
+    return numerator.div(denominator);
+  }
+
+  public static x64ToDecimal(num: BN, decimalPlaces?: number): Decimal {
+    return new Decimal(num.toString()).div(Decimal.pow(2, 64)).toDecimalPlaces(decimalPlaces);
+  }
+
+  public static decimalToX64(num: Decimal): BN {
+    return new BN(num.mul(Decimal.pow(2, 64)).floor().toFixed());
+  }
+
+  public static wrappingSubU128(n0: BN, n1: BN): BN {
+    return n0.add(Q128).sub(n1).mod(Q128);
+  }
+}

+ 97 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/models.ts

@@ -0,0 +1,97 @@
+import { PublicKey } from '@solana/web3.js';
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+export interface ReturnTypeGetPriceAndTick {
+  tick: number;
+  price: Decimal;
+}
+
+export type Tick = {
+  tick: number;
+  liquidityNet: BN;
+  liquidityGross: BN;
+  feeGrowthOutsideX64A: BN;
+  feeGrowthOutsideX64B: BN;
+  rewardGrowthsOutsideX64: BN[];
+};
+
+export type TickArray = {
+  address: PublicKey;
+  poolId: PublicKey;
+  startTickIndex: number;
+  ticks: Tick[];
+  initializedTickCount: number;
+};
+
+/**
+ * Dynamic Tick Array type
+ * Uses sparse storage with a mapping table to track allocated tick positions
+ */
+export type DynTickArray = {
+  address: PublicKey;
+  poolId: PublicKey;
+  startTickIndex: number;
+  tickOffsetIndex: number[]; // Mapping table: tickOffsetIndex[offset] = position + 1 (0 = unallocated)
+  allocTickCount: number; // Number of allocated ticks
+  initializedTickCount: number; // Number of initialized ticks
+  ticks: Tick[]; // Dynamic array of tick states (length = allocTickCount)
+};
+
+/**
+ * Unified Tick Array Container
+ * Discriminated union to handle both fixed and dynamic tick arrays
+ */
+export type TickArrayContainer = { type: 'Fixed'; data: TickArray } | { type: 'Dynamic'; data: DynTickArray };
+
+/**
+ * Type guard to check if a container is a fixed tick array
+ */
+export function isFixedTickArray(container: TickArrayContainer): container is { type: 'Fixed'; data: TickArray } {
+  return container.type === 'Fixed';
+}
+
+/**
+ * Type guard to check if a container is a dynamic tick array
+ */
+export function isDynamicTickArray(
+  container: TickArrayContainer
+): container is { type: 'Dynamic'; data: DynTickArray } {
+  return container.type === 'Dynamic';
+}
+
+export type TickState = {
+  tick: number;
+  liquidityNet: BN;
+  liquidityGross: BN;
+  feeGrowthOutsideX64A: BN;
+  feeGrowthOutsideX64B: BN;
+  tickCumulativeOutside: BN;
+  secondsPerLiquidityOutsideX64: BN;
+  secondsOutside: number;
+  rewardGrowthsOutside: BN[];
+};
+
+export type TickArrayState = {
+  ammPool: PublicKey;
+  startTickIndex: number;
+  ticks: TickState[];
+  initializedTickCount: number;
+};
+
+export interface TickArrayBitmapExtensionType {
+  poolId: PublicKey;
+  exBitmapAddress: PublicKey;
+  positiveTickArrayBitmap: BN[][];
+  negativeTickArrayBitmap: BN[][];
+}
+
+export interface StepComputations {
+  sqrtPriceStartX64: BN;
+  tickNext: number;
+  initialized: boolean;
+  sqrtPriceNextX64: BN;
+  amountIn: BN;
+  amountOut: BN;
+  feeAmount: BN;
+}

+ 164 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/poolStateUtils.ts

@@ -0,0 +1,164 @@
+import { Connection, PublicKey } from '@solana/web3.js';
+
+import { RawDataUtils } from '../getRawData';
+import { IPoolLayout } from '../layout';
+
+/**
+ * Pool State Utilities for querying pool information with decay fee support
+ */
+export class PoolStateUtils {
+  /**
+   * Check if the pool has decay fee enabled
+   * @param poolInfo Pool layout information
+   * @returns boolean indicating if decay fee is enabled
+   */
+  static isDecayFeeEnabled(poolInfo: IPoolLayout): boolean {
+    return (poolInfo.decayFeeFlag & (1 << 0)) !== 0;
+  }
+
+  /**
+   * Check if decay fee is enabled for selling mint0
+   * @param poolInfo Pool layout information
+   * @returns boolean indicating if decay fee is enabled for mint0 sell
+   */
+  static isDecayFeeOnSellMint0(poolInfo: IPoolLayout): boolean {
+    return (poolInfo.decayFeeFlag & (1 << 1)) !== 0;
+  }
+
+  /**
+   * Check if decay fee is enabled for selling mint1
+   * @param poolInfo Pool layout information
+   * @returns boolean indicating if decay fee is enabled for mint1 sell
+   */
+  static isDecayFeeOnSellMint1(poolInfo: IPoolLayout): boolean {
+    return (poolInfo.decayFeeFlag & (1 << 2)) !== 0;
+  }
+
+  /**
+   * Calculate the current decay fee rate based on the current time
+   * @param poolInfo Pool layout information
+   * @param currentTimestamp Current timestamp in seconds
+   * @returns Decay fee rate in hundredths of a bip (10^-6)
+   */
+  static getDecayFeeRate(poolInfo: IPoolLayout, currentTimestamp: number): number {
+    if (!this.isDecayFeeEnabled(poolInfo)) {
+      return 0;
+    }
+
+    // Pool is not open yet
+    if (currentTimestamp < poolInfo.openTime.toNumber()) {
+      return 0;
+    }
+
+    const intervalCount = Math.floor(
+      (currentTimestamp - poolInfo.openTime.toNumber()) / poolInfo.decayFeeDecreaseInterval,
+    );
+
+    const decayFeeDecreaseRate = poolInfo.decayFeeDecreaseRate * 10000; // Convert to basis points
+    const FEE_RATE_DENOMINATOR_VALUE = 1000000; // 10^6
+    const hundredthsOfABip = FEE_RATE_DENOMINATOR_VALUE;
+    let rate = hundredthsOfABip;
+
+    // Fast power calculation: (1 - x)^c
+    // where x = decayFeeDecreaseRate / 10^6, c = intervalCount
+    if (intervalCount > 0) {
+      let exp = intervalCount;
+      let base = hundredthsOfABip - decayFeeDecreaseRate;
+
+      while (exp > 0) {
+        if (exp % 2 === 1) {
+          rate = Math.ceil((rate * base) / hundredthsOfABip);
+        }
+        base = Math.ceil((base * base) / hundredthsOfABip);
+        exp = Math.floor(exp / 2);
+      }
+    }
+
+    // Apply initial fee rate (convert from percentage)
+    rate = Math.ceil((rate * poolInfo.decayFeeInitFeeRate) / 100);
+
+    return rate;
+  }
+
+  /**
+   * Get comprehensive decay fee information for a pool
+   * @param poolInfo Pool layout information
+   * @param currentTimestamp Current timestamp in seconds
+   * @returns Object containing all decay fee related information
+   */
+  static getDecayFeeInfo(poolInfo: IPoolLayout, currentTimestamp?: number) {
+    const timestamp = currentTimestamp || Math.floor(Date.now() / 1000);
+
+    return {
+      isEnabled: this.isDecayFeeEnabled(poolInfo),
+      onSellMint0: this.isDecayFeeOnSellMint0(poolInfo),
+      onSellMint1: this.isDecayFeeOnSellMint1(poolInfo),
+      initFeeRate: poolInfo.decayFeeInitFeeRate, // Percentage (1 = 1%)
+      decreaseRate: poolInfo.decayFeeDecreaseRate, // Percentage (1 = 1%)
+      decreaseInterval: poolInfo.decayFeeDecreaseInterval, // Seconds
+      currentFeeRate: this.getDecayFeeRate(poolInfo, timestamp), // In hundredths of a bip (10^-6)
+      openTime: poolInfo.openTime.toNumber(),
+    };
+  }
+
+  /**
+   * Get pool state with decay fee information from chain
+   * @param connection Solana connection
+   * @param poolId Pool address
+   * @param currentTimestamp Optional current timestamp
+   * @returns Pool information with decay fee details
+   */
+  static async getPoolStateWithDecayFee(connection: Connection, poolId: string | PublicKey, currentTimestamp?: number) {
+    const poolInfo = await RawDataUtils.getRawPoolInfoByPoolId({
+      connection,
+      poolId,
+    });
+
+    if (!poolInfo) {
+      throw new Error(`Pool not found: ${poolId}`);
+    }
+
+    const decayFeeInfo = this.getDecayFeeInfo(poolInfo, currentTimestamp);
+
+    return {
+      ...poolInfo,
+      decayFeeInfo,
+    };
+  }
+
+  /**
+   * Check if decay fee is currently active for a specific direction
+   * @param poolInfo Pool layout information
+   * @param zeroForOne True if swapping token0 for token1 (selling token0)
+   * @param currentTimestamp Current timestamp in seconds
+   * @returns Object with active status and current fee rate
+   */
+  static getDecayFeeForDirection(poolInfo: IPoolLayout, zeroForOne: boolean, currentTimestamp?: number) {
+    const timestamp = currentTimestamp || Math.floor(Date.now() / 1000);
+
+    if (!this.isDecayFeeEnabled(poolInfo)) {
+      return {
+        isActive: false,
+        feeRate: 0,
+      };
+    }
+
+    const isActiveForDirection = zeroForOne
+      ? this.isDecayFeeOnSellMint0(poolInfo)
+      : this.isDecayFeeOnSellMint1(poolInfo);
+
+    if (!isActiveForDirection) {
+      return {
+        isActive: false,
+        feeRate: 0,
+      };
+    }
+
+    const currentFeeRate = this.getDecayFeeRate(poolInfo, timestamp);
+
+    return {
+      isActive: true,
+      feeRate: currentFeeRate,
+    };
+  }
+}

+ 412 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/poolUtils.ts

@@ -0,0 +1,412 @@
+import { PublicKey } from '@solana/web3.js';
+import BN from 'bn.js';
+
+import { MAX_TICK, MIN_TICK, NEGATIVE_ONE } from '../constants';
+import { IAmmConfigLayout } from '../layout';
+import { IPoolLayoutWithId } from '../models';
+import { getPdaTickArrayAddress } from '../pda';
+
+import { TickArrayContainer, TickArrayBitmapExtensionType } from './models';
+import { SwapMath } from './swapMath';
+import { TickQuery, TickUtils } from './tick';
+import { TickArrayBitmap, TickArrayBitmapExtensionUtils } from './tickarrayBitmap';
+
+export class PoolUtils {
+  // Used to check if a set of tickarray start indices exceed the boundary range of the default bitmap
+  public static isOverflowDefaultTickarrayBitmap(tickSpacing: number, tickarrayStartIndexs: number[]): boolean {
+    const { maxTickBoundary, minTickBoundary } = this._tickRange(tickSpacing);
+
+    for (const tickIndex of tickarrayStartIndexs) {
+      const tickarrayStartIndex = TickUtils.getTickArrayStartIndexByTick(tickIndex, tickSpacing);
+
+      if (tickarrayStartIndex >= maxTickBoundary || tickarrayStartIndex < minTickBoundary) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  public static _tickRange(tickSpacing: number): {
+    maxTickBoundary: number;
+    minTickBoundary: number;
+  } {
+    let maxTickBoundary = TickArrayBitmap.maxTickInTickarrayBitmap(tickSpacing);
+    let minTickBoundary = -maxTickBoundary;
+
+    if (maxTickBoundary > MAX_TICK) {
+      maxTickBoundary = TickQuery.getArrayStartIndex(MAX_TICK, tickSpacing) + TickQuery.tickCount(tickSpacing);
+    }
+    if (minTickBoundary < MIN_TICK) {
+      minTickBoundary = TickQuery.getArrayStartIndex(MIN_TICK, tickSpacing);
+    }
+    return { maxTickBoundary, minTickBoundary };
+  }
+
+  /**
+   * Calculate the maximum and minimum ticks selectable by users in the UI
+   * Unlike _tickRange, this method directly returns the available tick value range, not the tickarray boundaries
+   *
+   * @param tickSpacing tick spacing
+   * @returns Maximum and minimum tick values selectable by users
+   */
+  public static tickRange(tickSpacing: number): {
+    maxTickBoundary: number;
+    minTickBoundary: number;
+  } {
+    // Use protocol-defined hard boundaries
+    let maxTickBoundary = MAX_TICK;
+    let minTickBoundary = MIN_TICK;
+
+    // Ensure returned tick values are divisible by tickSpacing to meet UI selection requirements
+    maxTickBoundary = Math.floor(maxTickBoundary / tickSpacing) * tickSpacing;
+    minTickBoundary = Math.ceil(minTickBoundary / tickSpacing) * tickSpacing;
+
+    return { maxTickBoundary, minTickBoundary };
+  }
+
+  public static nextInitializedTickArrayStartIndex(
+    poolInfo: {
+      tickCurrent: number;
+      tickSpacing: number;
+      tickArrayBitmap: BN[];
+      exBitmapInfo: TickArrayBitmapExtensionType;
+    },
+    lastTickArrayStartIndex: number,
+    zeroForOne: boolean,
+  ): { isExist: boolean; nextStartIndex: number } {
+    // Start scanning from the provided lastTickArrayStartIndex instead of recomputing from tickCurrent
+    // This avoids skipping the immediate adjacent array when tickCurrent happens to equal a start index.
+
+    while (true) {
+      const { isInit: startIsInit, tickIndex: startIndex } = TickArrayBitmap.nextInitializedTickArrayStartIndex(
+        TickUtils.mergeTickArrayBitmap(poolInfo.tickArrayBitmap),
+        lastTickArrayStartIndex,
+        poolInfo.tickSpacing,
+        zeroForOne,
+      );
+      if (startIsInit) {
+        return { isExist: true, nextStartIndex: startIndex };
+      }
+      lastTickArrayStartIndex = startIndex;
+
+      const { isInit, tickIndex } = TickArrayBitmapExtensionUtils.nextInitializedTickArrayFromOneBitmap(
+        lastTickArrayStartIndex,
+        poolInfo.tickSpacing,
+        zeroForOne,
+        poolInfo.exBitmapInfo,
+      );
+      if (isInit) return { isExist: true, nextStartIndex: tickIndex };
+
+      lastTickArrayStartIndex = tickIndex;
+
+      if (lastTickArrayStartIndex < MIN_TICK || lastTickArrayStartIndex > MAX_TICK)
+        return { isExist: false, nextStartIndex: 0 };
+    }
+  }
+
+  public static getFirstInitializedTickArray(
+    poolInfo: {
+      programId: PublicKey;
+      poolId: PublicKey;
+      tickCurrent: number;
+      tickSpacing: number;
+      tickArrayBitmap: BN[];
+      exBitmapInfo: TickArrayBitmapExtensionType;
+    },
+    zeroForOne: boolean,
+  ):
+    | { isExist: true; startIndex: number; nextAccountMeta: PublicKey }
+    | { isExist: false; startIndex: undefined; nextAccountMeta: undefined } {
+    const { isInitialized, startIndex } = PoolUtils.isOverflowDefaultTickarrayBitmap(poolInfo.tickSpacing, [
+      poolInfo.tickCurrent,
+    ])
+      ? TickArrayBitmapExtensionUtils.checkTickArrayIsInit(
+          TickQuery.getArrayStartIndex(poolInfo.tickCurrent, poolInfo.tickSpacing),
+          poolInfo.tickSpacing,
+          poolInfo.exBitmapInfo,
+        )
+      : TickUtils.checkTickArrayIsInitialized(
+          TickUtils.mergeTickArrayBitmap(poolInfo.tickArrayBitmap),
+          poolInfo.tickCurrent,
+          poolInfo.tickSpacing,
+        );
+
+    if (isInitialized) {
+      const { publicKey: address } = getPdaTickArrayAddress(poolInfo.programId, poolInfo.poolId, startIndex);
+      return {
+        isExist: true,
+        startIndex,
+        nextAccountMeta: address,
+      };
+    }
+    const { isExist, nextStartIndex } = this.nextInitializedTickArrayStartIndex(
+      poolInfo,
+      TickQuery.getArrayStartIndex(poolInfo.tickCurrent, poolInfo.tickSpacing),
+      zeroForOne,
+    );
+    if (isExist) {
+      const { publicKey: address } = getPdaTickArrayAddress(poolInfo.programId, poolInfo.poolId, nextStartIndex);
+      return {
+        isExist: true,
+        startIndex: nextStartIndex,
+        nextAccountMeta: address,
+      };
+    }
+    return { isExist: false, nextAccountMeta: undefined, startIndex: undefined };
+  }
+
+  /**
+   * Calculate the output amount and required account list for token swap
+   *
+   * This function is the core of CLMM (Concentrated Liquidity Market Maker) swap logic, used to calculate the expected output amount for a given input amount,
+   * and determine all tick array accounts that need to be accessed during trade execution.
+   *
+   * @param params Swap calculation parameter object
+   * @param params.poolInfo - Complete information of the liquidity pool, including current price, liquidity, tick and other states
+   * @param params.exBitmapInfo - Tick array bitmap extension information, used to handle ticks beyond the default range
+   * @param params.ammConfig - AMM configuration information, including transaction fee rates and other parameters
+   * @param params.tickArrayInfo - Loaded tick array information cache, with keys as tick array start indices
+   * @param params.inputTokenMint - Input token mint address, used to determine trade direction
+   * @param params.inputAmount - Input token amount (using minimum units)
+   * @param params.sqrtPriceLimitX64 - Optional price limit, representing the worst price for the trade (Q64.64 format)
+   * @param params.catchLiquidityInsufficient - Whether to catch liquidity insufficient situations, default false
+   *
+   * @returns Swap calculation result object
+   * @returns allTrade - Boolean value indicating whether the specified input amount can be fully traded
+   *                    true: All amount can be traded
+   *                    false: Only partial trading due to insufficient liquidity or price limit
+   * @returns expectedAmountOut - Expected output token amount to be obtained (using minimum units)
+   * @returns remainingAccounts - List of all tick array account addresses that need to be accessed during trading
+   *                             These accounts need to be passed as remaining accounts in the trade instruction
+   * @returns executionPrice - Final price after trade execution (square root price in Q64.64 format)
+   * @returns feeAmount - Total fees generated by the trade (using input token minimum units)
+   *
+   * @throws Error Throws 'Invalid tick array' error when no valid tick array is found
+   */
+  public static getOutputAmountAndRemainAccounts(params: {
+    poolInfo: IPoolLayoutWithId;
+    exBitmapInfo: TickArrayBitmapExtensionType;
+    ammConfig: IAmmConfigLayout;
+    tickArrayInfo: { [key: string]: TickArrayContainer };
+    inputTokenMint: PublicKey;
+    inputAmount: BN;
+    sqrtPriceLimitX64?: BN;
+    catchLiquidityInsufficient?: boolean;
+  }): {
+    allTrade: boolean;
+    expectedAmountOut: BN;
+    remainingAccounts: PublicKey[];
+    executionPrice: BN;
+    feeAmount: BN;
+  } {
+    const {
+      poolInfo,
+      exBitmapInfo,
+      ammConfig,
+      tickArrayInfo,
+      inputTokenMint,
+      inputAmount,
+      sqrtPriceLimitX64,
+      catchLiquidityInsufficient = false,
+    } = params;
+
+    // Step 1: Determine trade direction
+    // zeroForOne = true: means trading tokenA (token0) for tokenB (token1), price decreases
+    // zeroForOne = false: means trading tokenB (token1) for tokenA (token0), price increases
+    const zeroForOne = inputTokenMint.toBase58() === poolInfo.mintA.toBase58();
+
+    // Step 2: Initialize account list to store all tick array accounts needed during trading
+    const allNeededAccounts: PublicKey[] = [];
+
+    // Step 3: Find the first tick array that needs to be accessed
+    // Based on current tick position and trade direction, find the array containing current tick or next initialized tick
+    const {
+      isExist,
+      startIndex: firstTickArrayStartIndex,
+      nextAccountMeta,
+    } = this.getFirstInitializedTickArray(
+      {
+        programId: poolInfo.programId,
+        poolId: poolInfo.poolId,
+        tickCurrent: poolInfo.tickCurrent,
+        tickSpacing: poolInfo.tickSpacing,
+        tickArrayBitmap: poolInfo.tickArrayBitmap,
+        exBitmapInfo,
+      },
+      zeroForOne,
+    );
+
+    // If no valid tick array is found, it indicates abnormal pool state or no liquidity
+    if (!isExist || firstTickArrayStartIndex === undefined || !nextAccountMeta) throw new Error('Invalid tick array');
+
+    // Step 4: Add the first tick array account to the list
+    allNeededAccounts.push(nextAccountMeta);
+
+    // Step 5: Execute the core logic of swap calculation
+    // SwapMath.swapCompute simulates the entire swap process, calculating:
+    // - Actual tradable amount
+    // - Output token amount
+    // - Other tick array accounts needed on the trade path
+    // - Final execution price and fees
+    const {
+      allTrade,
+      amountCalculated: outputAmount,
+      accounts: reaminAccounts,
+      sqrtPriceX64: executionPrice,
+      feeAmount,
+    } = SwapMath.swapCompute(
+      poolInfo.programId,
+      poolInfo.poolId,
+      tickArrayInfo,
+      poolInfo.tickArrayBitmap,
+      exBitmapInfo,
+      zeroForOne,
+      ammConfig.tradeFeeRate,
+      poolInfo.liquidity,
+      poolInfo.tickCurrent,
+      poolInfo.tickSpacing,
+      poolInfo.sqrtPriceX64,
+      inputAmount,
+      firstTickArrayStartIndex,
+      sqrtPriceLimitX64,
+      catchLiquidityInsufficient,
+    );
+
+    // Step 6: Add other necessary accounts discovered during swap to the list
+    allNeededAccounts.push(...reaminAccounts);
+
+    // Step 7: Return calculation results
+    // Note: outputAmount is negative (indicating outflow), needs to be multiplied by -1 to convert to positive
+    return {
+      allTrade,
+      expectedAmountOut: outputAmount.mul(NEGATIVE_ONE),
+      remainingAccounts: allNeededAccounts,
+      executionPrice,
+      feeAmount,
+    };
+  }
+
+  /**
+   * Calculate the required input token amount and account list for exact output
+   *
+   * This function is used for "exact output" scenarios, where the user specifies the desired output token amount,
+   * and the function calculates how many tokens need to be input to obtain the specified output amount.
+   *
+   * @param params Swap calculation parameter object
+   * @param params.poolInfo - Complete information of the liquidity pool
+   * @param params.exBitmapInfo - Tick array bitmap extension information
+   * @param params.ammConfig - AMM configuration information
+   * @param params.tickArrayInfo - Loaded tick array information cache
+   * @param params.outputTokenMint - Output token mint address
+   * @param params.outputAmount - Expected output token amount (using minimum units)
+   * @param params.sqrtPriceLimitX64 - Optional price limit (Q64.64 format)
+   * @param params.catchLiquidityInsufficient - Whether to catch liquidity insufficient situations
+   *
+   * @returns Swap calculation result object
+   * @returns allTrade - Whether the specified output amount can be fully obtained
+   * @returns expectedAmountIn - Required input token amount (including fees)
+   * @returns remainingAccounts - List of tick array accounts that need to be accessed during trading
+   * @returns executionPrice - Final price after trade execution
+   * @returns feeAmount - Total fees generated by the trade
+   */
+  public static getInputAmountAndRemainAccounts(params: {
+    poolInfo: IPoolLayoutWithId;
+    exBitmapInfo: TickArrayBitmapExtensionType;
+    ammConfig: IAmmConfigLayout;
+    tickArrayInfo: { [key: string]: TickArrayContainer };
+    outputTokenMint: PublicKey;
+    outputAmount: BN;
+    sqrtPriceLimitX64?: BN;
+    catchLiquidityInsufficient?: boolean;
+  }): {
+    allTrade: boolean;
+    expectedAmountIn: BN;
+    remainingAccounts: PublicKey[];
+    executionPrice: BN;
+    feeAmount: BN;
+  } {
+    const {
+      poolInfo,
+      exBitmapInfo,
+      ammConfig,
+      tickArrayInfo,
+      outputTokenMint,
+      outputAmount,
+      sqrtPriceLimitX64,
+      catchLiquidityInsufficient = false,
+    } = params;
+
+    // 步骤1: 确定交易方向
+    // 注意:对于精确输出,交易方向的判断基于输出代币
+    // 如果输出 tokenB,则需要输入 tokenA(zeroForOne = true)
+    // 如果输出 tokenA,则需要输入 tokenB(zeroForOne = false)
+    const zeroForOne = outputTokenMint.toBase58() === poolInfo.mintB.toBase58();
+
+    // 步骤2: 初始化账户列表
+    const allNeededAccounts: PublicKey[] = [];
+
+    // 步骤3: 查找第一个需要访问的 tick 数组
+    const {
+      isExist,
+      startIndex: firstTickArrayStartIndex,
+      nextAccountMeta,
+    } = this.getFirstInitializedTickArray(
+      {
+        programId: poolInfo.programId,
+        poolId: poolInfo.poolId,
+        tickCurrent: poolInfo.tickCurrent,
+        tickSpacing: poolInfo.tickSpacing,
+        tickArrayBitmap: poolInfo.tickArrayBitmap,
+        exBitmapInfo,
+      },
+      zeroForOne,
+    );
+
+    if (!isExist || firstTickArrayStartIndex === undefined || !nextAccountMeta) throw new Error('Invalid tick array');
+
+    // Step 4: Add the first tick array account to the list
+    allNeededAccounts.push(nextAccountMeta);
+
+    // 步骤5: 执行交换计算
+    // 对于精确输出,amountSpecified 需要是负数
+    const amountSpecified = outputAmount.mul(NEGATIVE_ONE);
+
+    const {
+      allTrade,
+      amountCalculated: inputAmount,
+      accounts: reaminAccounts,
+      sqrtPriceX64: executionPrice,
+      feeAmount,
+    } = SwapMath.swapCompute(
+      poolInfo.programId,
+      poolInfo.poolId,
+      tickArrayInfo,
+      poolInfo.tickArrayBitmap,
+      exBitmapInfo,
+      zeroForOne,
+      ammConfig.tradeFeeRate,
+      poolInfo.liquidity,
+      poolInfo.tickCurrent,
+      poolInfo.tickSpacing,
+      poolInfo.sqrtPriceX64,
+      amountSpecified,
+      firstTickArrayStartIndex,
+      sqrtPriceLimitX64,
+      catchLiquidityInsufficient,
+    );
+
+    // 步骤6: 将其他必需账户添加到列表中
+    allNeededAccounts.push(...reaminAccounts);
+
+    // 步骤7: 返回计算结果
+    // 注意:对于精确输出,inputAmount 包含了手续费
+    return {
+      allTrade,
+      expectedAmountIn: inputAmount,
+      remainingAccounts: allNeededAccounts,
+      executionPrice,
+      feeAmount,
+    };
+  }
+}

+ 160 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/position.ts

@@ -0,0 +1,160 @@
+import { EpochInfo } from '@solana/web3.js';
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+import { Q64 } from '../constants';
+import { IPersonalPositionLayout, IPoolLayout } from '../layout';
+
+import { LiquidityMath } from './liquidityMath';
+import { MathUtils } from './mathUtils';
+import { SqrtPriceMath } from './sqrtPriceMath';
+import { getTransferAmountFee, minExpirationTime } from './transfer';
+import { Tick } from './models';
+
+export interface GetTransferAmountFee {
+  amount: BN;
+  fee: BN | undefined;
+  expirationTime: number | undefined;
+}
+
+export interface ReturnTypeGetLiquidityAmountOut {
+  liquidity: BN;
+  amountSlippageA: GetTransferAmountFee;
+  amountSlippageB: GetTransferAmountFee;
+  amountA: GetTransferAmountFee;
+  amountB: GetTransferAmountFee;
+  expirationTime: number | undefined;
+}
+
+export class PositionUtils {
+  static getfeeGrowthInside(
+    poolInfo: Pick<IPoolLayout, 'tickCurrent' | 'feeGrowthGlobalX64A' | 'feeGrowthGlobalX64B'>,
+    tickLowerState: Tick,
+    tickUpperState: Tick,
+  ): { feeGrowthInsideX64A: BN; feeGrowthInsideBX64: BN } {
+    let feeGrowthBelowX64A = new BN(0);
+    let feeGrowthBelowX64B = new BN(0);
+    if (poolInfo.tickCurrent >= tickLowerState.tick) {
+      feeGrowthBelowX64A = tickLowerState.feeGrowthOutsideX64A;
+      feeGrowthBelowX64B = tickLowerState.feeGrowthOutsideX64B;
+    } else {
+      feeGrowthBelowX64A = poolInfo.feeGrowthGlobalX64A.sub(tickLowerState.feeGrowthOutsideX64A);
+      feeGrowthBelowX64B = poolInfo.feeGrowthGlobalX64B.sub(tickLowerState.feeGrowthOutsideX64B);
+    }
+
+    let feeGrowthAboveX64A = new BN(0);
+    let feeGrowthAboveX64B = new BN(0);
+    if (poolInfo.tickCurrent < tickUpperState.tick) {
+      feeGrowthAboveX64A = tickUpperState.feeGrowthOutsideX64A;
+      feeGrowthAboveX64B = tickUpperState.feeGrowthOutsideX64B;
+    } else {
+      feeGrowthAboveX64A = poolInfo.feeGrowthGlobalX64A.sub(tickUpperState.feeGrowthOutsideX64A);
+      feeGrowthAboveX64B = poolInfo.feeGrowthGlobalX64B.sub(tickUpperState.feeGrowthOutsideX64B);
+    }
+
+    const feeGrowthInsideX64A = MathUtils.wrappingSubU128(
+      MathUtils.wrappingSubU128(poolInfo.feeGrowthGlobalX64A, feeGrowthBelowX64A),
+      feeGrowthAboveX64A,
+    );
+    const feeGrowthInsideBX64 = MathUtils.wrappingSubU128(
+      MathUtils.wrappingSubU128(poolInfo.feeGrowthGlobalX64B, feeGrowthBelowX64B),
+      feeGrowthAboveX64B,
+    );
+    return { feeGrowthInsideX64A, feeGrowthInsideBX64 };
+  }
+
+  static getPositionFees(
+    poolInfo: Pick<IPoolLayout, 'tickCurrent' | 'feeGrowthGlobalX64A' | 'feeGrowthGlobalX64B'>,
+    positionState: IPersonalPositionLayout,
+    tickLowerState: Tick,
+    tickUpperState: Tick,
+  ): { tokenFeeAmountA: BN; tokenFeeAmountB: BN } {
+    const { feeGrowthInsideX64A, feeGrowthInsideBX64 } = this.getfeeGrowthInside(
+      poolInfo,
+      tickLowerState,
+      tickUpperState,
+    );
+
+    const feeGrowthdeltaA = MathUtils.mulDivFloor(
+      MathUtils.wrappingSubU128(feeGrowthInsideX64A, positionState.feeGrowthInsideLastX64A),
+      positionState.liquidity,
+      Q64,
+    );
+    const tokenFeeAmountA = positionState.tokenFeesOwedA.add(feeGrowthdeltaA);
+
+    const feeGrowthdelta1 = MathUtils.mulDivFloor(
+      MathUtils.wrappingSubU128(feeGrowthInsideBX64, positionState.feeGrowthInsideLastX64B),
+      positionState.liquidity,
+      Q64,
+    );
+    const tokenFeeAmountB = positionState.tokenFeesOwedB.add(feeGrowthdelta1);
+
+    return { tokenFeeAmountA, tokenFeeAmountB };
+  }
+
+  static getAmountsFromLiquidity(params: {
+    poolInfo: IPoolLayout;
+    ownerPosition: IPersonalPositionLayout;
+    liquidity: BN;
+    slippage: number;
+    add: boolean;
+    epochInfo: EpochInfo;
+  }): ReturnTypeGetLiquidityAmountOut {
+    const { poolInfo, ownerPosition, liquidity, slippage, add, epochInfo } = params;
+    const sqrtPriceX64 = poolInfo.sqrtPriceX64;
+    const sqrtPriceX64A = SqrtPriceMath.getSqrtPriceX64FromTick(ownerPosition.tickLower);
+    const sqrtPriceX64B = SqrtPriceMath.getSqrtPriceX64FromTick(ownerPosition.tickUpper);
+
+    const coefficientRe = add ? 1 + slippage : 1 - slippage;
+
+    const amounts = LiquidityMath.getAmountsFromLiquidity(sqrtPriceX64, sqrtPriceX64A, sqrtPriceX64B, liquidity, add);
+
+    // TODO: Temporarily not considering Token 2022 transfer fee scenarios. If needed in the future, this should be added here;
+    const [amountA, amountB] = [
+      getTransferAmountFee(amounts.amountA, undefined, epochInfo, true),
+      getTransferAmountFee(amounts.amountB, undefined, epochInfo, true),
+    ];
+    const [amountSlippageA, amountSlippageB] = [
+      getTransferAmountFee(
+        new BN(new Decimal(amounts.amountA.toString()).mul(coefficientRe).toFixed(0)),
+        undefined,
+        epochInfo,
+        true,
+      ),
+      getTransferAmountFee(
+        new BN(new Decimal(amounts.amountB.toString()).mul(coefficientRe).toFixed(0)),
+        undefined,
+        epochInfo,
+        true,
+      ),
+    ];
+
+    // const [amountA, amountB] = [
+    //   getTransferAmountFee(amounts.amountA, poolInfo.mintA.extensions?.feeConfig, epochInfo, true),
+    //   getTransferAmountFee(amounts.amountB, poolInfo.mintB.extensions?.feeConfig, epochInfo, true),
+    // ];
+    // const [amountSlippageA, amountSlippageB] = [
+    //   getTransferAmountFee(
+    //     new BN(new Decimal(amounts.amountA.toString()).mul(coefficientRe).toFixed(0)),
+    //     poolInfo.mintA.extensions?.feeConfig,
+    //     epochInfo,
+    //     true,
+    //   ),
+    //   getTransferAmountFee(
+    //     new BN(new Decimal(amounts.amountB.toString()).mul(coefficientRe).toFixed(0)),
+    //     poolInfo.mintB.extensions?.feeConfig,
+    //     epochInfo,
+    //     true,
+    //   ),
+    // ];
+
+    return {
+      liquidity,
+      amountA,
+      amountB,
+      amountSlippageA,
+      amountSlippageB,
+      expirationTime: minExpirationTime(amountA.expirationTime, amountB.expirationTime),
+    };
+  }
+}

+ 220 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/sqrtPriceMath.test.ts

@@ -0,0 +1,220 @@
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+import { describe, test, expect } from 'vitest';
+
+import { MIN_TICK, MAX_TICK, MIN_SQRT_PRICE_X64, MAX_SQRT_PRICE_X64 } from '../constants';
+
+import { SqrtPriceMath } from './sqrtPriceMath';
+
+describe('SqrtPriceMath basic functionality test', () => {
+  test('price, sqrtPriceX64 and tick conversion', () => {
+    const price = new Decimal('1.0001');
+    const decimalsA = 6;
+    const decimalsB = 6;
+
+    // price -> tick
+    const tick = SqrtPriceMath.getTickFromPrice(price, decimalsA, decimalsB);
+
+    // tick -> sqrtPriceX64
+    const sqrtPriceX64 = SqrtPriceMath.getSqrtPriceX64FromTick(tick);
+
+    // sqrtPriceX64 -> price
+    const calculatedPrice = SqrtPriceMath.sqrtPriceX64ToPrice(sqrtPriceX64, decimalsA, decimalsB);
+
+    // Verify that the converted price is close to the original price
+    expect(calculatedPrice.toNumber()).toBeCloseTo(price.toNumber(), 1);
+
+    // sqrtPriceX64 -> tick
+    const calculatedTick = SqrtPriceMath.getTickFromSqrtPriceX64(sqrtPriceX64);
+
+    // Verify that the converted tick is the same as the original tick
+    expect(calculatedTick).toBe(tick);
+  });
+});
+
+describe('SqrtPriceMath boundary condition test', () => {
+  test('MIN_TICK and MAX_TICK boundary test', () => {
+    // Test minimum tick
+    const minTickSqrtPrice = SqrtPriceMath.getSqrtPriceX64FromTick(MIN_TICK);
+    const minTickCalculated = SqrtPriceMath.getTickFromSqrtPriceX64(minTickSqrtPrice);
+    expect(minTickCalculated).toBe(MIN_TICK);
+
+    // Test maximum tick
+    const maxTickSqrtPrice = SqrtPriceMath.getSqrtPriceX64FromTick(MAX_TICK);
+    const maxTickCalculated = SqrtPriceMath.getTickFromSqrtPriceX64(maxTickSqrtPrice);
+    expect(maxTickCalculated).toBe(MAX_TICK);
+  });
+
+  test('MIN_SQRT_PRICE_X64 and MAX_SQRT_PRICE_X64 boundary test', () => {
+    // Test minimum sqrtPriceX64
+    const minTick = SqrtPriceMath.getTickFromSqrtPriceX64(MIN_SQRT_PRICE_X64);
+    const recalculatedSqrtPrice = SqrtPriceMath.getSqrtPriceX64FromTick(minTick);
+    // Check if it is within the reasonable error range
+    expect(recalculatedSqrtPrice.gte(MIN_SQRT_PRICE_X64)).toBeTruthy();
+
+    // Test maximum sqrtPriceX64
+    const maxTick = SqrtPriceMath.getTickFromSqrtPriceX64(MAX_SQRT_PRICE_X64);
+    const recalculatedMaxSqrtPrice = SqrtPriceMath.getSqrtPriceX64FromTick(maxTick);
+    // Check if it is within the reasonable error range
+    expect(recalculatedMaxSqrtPrice.lte(MAX_SQRT_PRICE_X64)).toBeTruthy();
+  });
+
+  test('Exception handling for out-of-bounds', () => {
+    // Test for less than MIN_TICK
+    expect(() => {
+      SqrtPriceMath.getSqrtPriceX64FromTick(MIN_TICK - 1);
+    }).toThrow();
+
+    // Test for greater than MAX_TICK
+    expect(() => {
+      SqrtPriceMath.getSqrtPriceX64FromTick(MAX_TICK + 1);
+    }).toThrow();
+
+    // Test for less than MIN_SQRT_PRICE_X64
+    expect(() => {
+      SqrtPriceMath.getTickFromSqrtPriceX64(MIN_SQRT_PRICE_X64.sub(new BN(1)));
+    }).toThrow();
+
+    // Test for greater than MAX_SQRT_PRICE_X64
+    expect(() => {
+      SqrtPriceMath.getTickFromSqrtPriceX64(MAX_SQRT_PRICE_X64.add(new BN(1)));
+    }).toThrow();
+  });
+});
+
+describe('SqrtPriceMath precision and rounding behavior test', () => {
+  test('tick boundary value rounding behavior_1', () => {
+    const tick = 1000;
+    const sqrtPriceX64 = SqrtPriceMath.getSqrtPriceX64FromTick(tick);
+
+    // Create a slightly larger sqrtPriceX64 value than the current tick
+    const slightlyLargerSqrtPrice = sqrtPriceX64.add(new BN(1));
+    const nextTick = SqrtPriceMath.getTickFromSqrtPriceX64(slightlyLargerSqrtPrice);
+
+    expect(nextTick).toBe(tick);
+  });
+
+  test('tick boundary value rounding behavior_2', () => {
+    const tick = 1000;
+    const sqrtPriceX64 = SqrtPriceMath.getSqrtPriceX64FromTick(tick);
+
+    // Create a slightly smaller sqrtPriceX64 value than the current tick
+    const slightlyLargerSqrtPrice = sqrtPriceX64.sub(new BN(1));
+    const nextTick = SqrtPriceMath.getTickFromSqrtPriceX64(slightlyLargerSqrtPrice);
+
+    expect(nextTick).toBe(tick - 1);
+  });
+
+  test('Different decimal places affect conversion', () => {
+    const price = new Decimal('8.7');
+
+    // Test different decimal place combinations
+    const decimalCombinations = [
+      { decimalsA: 6, decimalsB: 6 },
+      { decimalsA: 8, decimalsB: 6 },
+      { decimalsA: 6, decimalsB: 8 },
+      { decimalsA: 9, decimalsB: 18 },
+    ];
+
+    for (const { decimalsA, decimalsB } of decimalCombinations) {
+      // price -> sqrtPriceX64
+      const sqrtPriceX64 = SqrtPriceMath.priceToSqrtPriceX64(price, decimalsA, decimalsB);
+
+      // sqrtPriceX64 -> price
+      const calculatedPrice = SqrtPriceMath.sqrtPriceX64ToPrice(sqrtPriceX64, decimalsA, decimalsB);
+
+      // Verify that the converted price is close to the original price
+      expect(calculatedPrice.toNumber()).toBeCloseTo(price.toNumber(), 0.01);
+    }
+  });
+
+  test('Price precision boundary case', () => {
+    // Test for very small and very large price values
+    const extremePrices = [
+      new Decimal('0.0000001'), // Very small price
+      new Decimal('1000000'), // Very large price
+    ];
+
+    for (const price of extremePrices) {
+      const decimalsA = 6;
+      const decimalsB = 6;
+
+      // price -> tick -> sqrtPriceX64 -> price complete loop test
+      const tick = SqrtPriceMath.getTickFromPrice(price, decimalsA, decimalsB);
+      const sqrtPriceX64 = SqrtPriceMath.getSqrtPriceX64FromTick(tick);
+      const calculatedPrice = SqrtPriceMath.sqrtPriceX64ToPrice(sqrtPriceX64, decimalsA, decimalsB);
+
+      // Verify that the relative error is within the acceptable range
+      const relativeError = price.minus(calculatedPrice).abs().div(price);
+      expect(relativeError.toNumber()).toBeLessThan(0.001); // 0.1% error
+    }
+  });
+});
+
+describe('SqrtPriceMath special case test', () => {
+  test('Non-integer tick handling', () => {
+    // Test for non-integer tick
+    expect(() => {
+      SqrtPriceMath.getSqrtPriceX64FromTick(1.5);
+    }).toThrow();
+  });
+});
+
+describe('SqrtPriceMath loop consistency test', () => {
+  test('price -> tick -> price conversion consistency', () => {
+    const testPrices = [
+      new Decimal('0.01'),
+      new Decimal('0.5'),
+      new Decimal('1'),
+      new Decimal('2'),
+      new Decimal('10'),
+      new Decimal('100'),
+    ];
+
+    const decimalsA = 6;
+    const decimalsB = 9;
+
+    for (const initialPrice of testPrices) {
+      // price -> tick
+      const tick = SqrtPriceMath.getTickFromPrice(initialPrice, decimalsA, decimalsB);
+
+      // tick -> sqrtPriceX64
+      const sqrtPriceX64 = SqrtPriceMath.getSqrtPriceX64FromTick(tick);
+
+      // sqrtPriceX64 -> price
+      const finalPrice = SqrtPriceMath.sqrtPriceX64ToPrice(sqrtPriceX64, decimalsA, decimalsB);
+
+      // Verify that the relative error is within the acceptable range
+      const relativeError = initialPrice.minus(finalPrice).abs().div(initialPrice);
+      expect(relativeError.toNumber()).toBeLessThan(0.001); // 0.1% error
+    }
+  });
+
+  test('sqrtPriceX64 -> tick -> sqrtPriceX64 conversion consistency', () => {
+    // Generate a series of test sqrtPriceX64 values
+    const testValues = [
+      // Close to MIN_SQRT_PRICE_X64
+      MIN_SQRT_PRICE_X64.add(new BN('1000')),
+      // Middle value
+      new BN('79226673521066979257578248091').div(new BN(2)),
+      // Close to MAX_SQRT_PRICE_X64
+      MAX_SQRT_PRICE_X64.sub(new BN('1000')),
+    ];
+
+    for (const initialSqrtPriceX64 of testValues) {
+      // sqrtPriceX64 -> tick
+      const tick = SqrtPriceMath.getTickFromSqrtPriceX64(initialSqrtPriceX64);
+
+      // tick -> sqrtPriceX64
+      const finalSqrtPriceX64 = SqrtPriceMath.getSqrtPriceX64FromTick(tick);
+
+      // Calculate relative error
+      const diff = initialSqrtPriceX64.sub(finalSqrtPriceX64).abs();
+
+      const relativeError = diff.mul(new BN(1000000)).div(initialSqrtPriceX64);
+
+      // Verify that the relative error is within the acceptable range
+      expect(relativeError.ltn(1000)).toBeTruthy(); // 0.1% error
+    }
+  });
+});

+ 194 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/sqrtPriceMath.ts

@@ -0,0 +1,194 @@
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+import {
+  BIT_PRECISION,
+  LOG_B_2_X32,
+  LOG_B_P_ERR_MARGIN_LOWER_X64,
+  LOG_B_P_ERR_MARGIN_UPPER_X64,
+  MAX_SQRT_PRICE_X64,
+  MAX_TICK,
+  MaxUint128,
+  MIN_SQRT_PRICE_X64,
+  MIN_TICK,
+  ONE,
+  U64Resolution,
+  ZERO,
+} from '../constants';
+
+import { MathUtils } from './mathUtils';
+
+function mulRightShift(val: BN, mulBy: BN): BN {
+  return signedRightShift(val.mul(mulBy), 64, 256);
+}
+
+function signedLeftShift(n0: BN, shiftBy: number, bitWidth: number): BN {
+  const twosN0 = n0.toTwos(bitWidth).shln(shiftBy);
+  twosN0.imaskn(bitWidth + 1);
+  return twosN0.fromTwos(bitWidth);
+}
+
+function signedRightShift(n0: BN, shiftBy: number, bitWidth: number): BN {
+  const twoN0 = n0.toTwos(bitWidth).shrn(shiftBy);
+  twoN0.imaskn(bitWidth - shiftBy + 1);
+  return twoN0.fromTwos(bitWidth - shiftBy);
+}
+
+export class SqrtPriceMath {
+  public static sqrtPriceX64ToPrice(sqrtPriceX64: BN, decimalsA: number, decimalsB: number): Decimal {
+    return MathUtils.x64ToDecimal(sqrtPriceX64)
+      .pow(2)
+      .mul(Decimal.pow(10, decimalsA - decimalsB));
+  }
+
+  public static priceToSqrtPriceX64(price: Decimal, decimalsA: number, decimalsB: number): BN {
+    return MathUtils.decimalToX64(price.mul(Decimal.pow(10, decimalsB - decimalsA)).sqrt());
+  }
+
+  public static getNextSqrtPriceX64FromInput(sqrtPriceX64: BN, liquidity: BN, amountIn: BN, zeroForOne: boolean): BN {
+    if (!sqrtPriceX64.gt(ZERO)) {
+      throw new Error('sqrtPriceX64 must greater than 0');
+    }
+    if (!liquidity.gt(ZERO)) {
+      throw new Error('liquidity must greater than 0');
+    }
+
+    return zeroForOne
+      ? this.getNextSqrtPriceFromTokenAmountARoundingUp(sqrtPriceX64, liquidity, amountIn, true)
+      : this.getNextSqrtPriceFromTokenAmountBRoundingDown(sqrtPriceX64, liquidity, amountIn, true);
+  }
+
+  public static getNextSqrtPriceX64FromOutput(sqrtPriceX64: BN, liquidity: BN, amountOut: BN, zeroForOne: boolean): BN {
+    if (!sqrtPriceX64.gt(ZERO)) {
+      throw new Error('sqrtPriceX64 must greater than 0');
+    }
+    if (!liquidity.gt(ZERO)) {
+      throw new Error('liquidity must greater than 0');
+    }
+
+    return zeroForOne
+      ? this.getNextSqrtPriceFromTokenAmountBRoundingDown(sqrtPriceX64, liquidity, amountOut, false)
+      : this.getNextSqrtPriceFromTokenAmountARoundingUp(sqrtPriceX64, liquidity, amountOut, false);
+  }
+
+  private static getNextSqrtPriceFromTokenAmountARoundingUp(
+    sqrtPriceX64: BN,
+    liquidity: BN,
+    amount: BN,
+    add: boolean,
+  ): BN {
+    if (amount.eq(ZERO)) return sqrtPriceX64;
+    const liquidityLeftShift = liquidity.shln(U64Resolution);
+
+    if (add) {
+      const numerator1 = liquidityLeftShift;
+      const denominator = liquidityLeftShift.add(amount.mul(sqrtPriceX64));
+      if (denominator.gte(numerator1)) {
+        return MathUtils.mulDivCeil(numerator1, sqrtPriceX64, denominator);
+      }
+      return MathUtils.mulDivRoundingUp(numerator1, ONE, numerator1.div(sqrtPriceX64).add(amount));
+    } else {
+      const amountMulSqrtPrice = amount.mul(sqrtPriceX64);
+      if (!liquidityLeftShift.gt(amountMulSqrtPrice)) {
+        throw new Error('getNextSqrtPriceFromTokenAmountARoundingUp,liquidityLeftShift must gt amountMulSqrtPrice');
+      }
+      const denominator = liquidityLeftShift.sub(amountMulSqrtPrice);
+      return MathUtils.mulDivCeil(liquidityLeftShift, sqrtPriceX64, denominator);
+    }
+  }
+
+  private static getNextSqrtPriceFromTokenAmountBRoundingDown(
+    sqrtPriceX64: BN,
+    liquidity: BN,
+    amount: BN,
+    add: boolean,
+  ): BN {
+    const deltaY = amount.shln(U64Resolution);
+    if (add) {
+      return sqrtPriceX64.add(deltaY.div(liquidity));
+    } else {
+      const amountDivLiquidity = MathUtils.mulDivRoundingUp(deltaY, ONE, liquidity);
+      if (!sqrtPriceX64.gt(amountDivLiquidity)) {
+        throw new Error('getNextSqrtPriceFromTokenAmountBRoundingDown sqrtPriceX64 must gt amountDivLiquidity');
+      }
+      return sqrtPriceX64.sub(amountDivLiquidity);
+    }
+  }
+
+  public static getSqrtPriceX64FromTick(tick: number): BN {
+    if (!Number.isInteger(tick)) {
+      throw new Error('tick must be integer');
+    }
+    if (tick < MIN_TICK || tick > MAX_TICK) {
+      throw new Error('tick must be in MIN_TICK and MAX_TICK');
+    }
+    const tickAbs: number = tick < 0 ? tick * -1 : tick;
+
+    let ratio: BN = (tickAbs & 0x1) != 0 ? new BN('18445821805675395072') : new BN('18446744073709551616');
+    if ((tickAbs & 0x2) != 0) ratio = mulRightShift(ratio, new BN('18444899583751176192'));
+    if ((tickAbs & 0x4) != 0) ratio = mulRightShift(ratio, new BN('18443055278223355904'));
+    if ((tickAbs & 0x8) != 0) ratio = mulRightShift(ratio, new BN('18439367220385607680'));
+    if ((tickAbs & 0x10) != 0) ratio = mulRightShift(ratio, new BN('18431993317065453568'));
+    if ((tickAbs & 0x20) != 0) ratio = mulRightShift(ratio, new BN('18417254355718170624'));
+    if ((tickAbs & 0x40) != 0) ratio = mulRightShift(ratio, new BN('18387811781193609216'));
+    if ((tickAbs & 0x80) != 0) ratio = mulRightShift(ratio, new BN('18329067761203558400'));
+    if ((tickAbs & 0x100) != 0) ratio = mulRightShift(ratio, new BN('18212142134806163456'));
+    if ((tickAbs & 0x200) != 0) ratio = mulRightShift(ratio, new BN('17980523815641700352'));
+    if ((tickAbs & 0x400) != 0) ratio = mulRightShift(ratio, new BN('17526086738831433728'));
+    if ((tickAbs & 0x800) != 0) ratio = mulRightShift(ratio, new BN('16651378430235570176'));
+    if ((tickAbs & 0x1000) != 0) ratio = mulRightShift(ratio, new BN('15030750278694412288'));
+    if ((tickAbs & 0x2000) != 0) ratio = mulRightShift(ratio, new BN('12247334978884435968'));
+    if ((tickAbs & 0x4000) != 0) ratio = mulRightShift(ratio, new BN('8131365268886854656'));
+    if ((tickAbs & 0x8000) != 0) ratio = mulRightShift(ratio, new BN('3584323654725218816'));
+    if ((tickAbs & 0x10000) != 0) ratio = mulRightShift(ratio, new BN('696457651848324352'));
+    if ((tickAbs & 0x20000) != 0) ratio = mulRightShift(ratio, new BN('26294789957507116'));
+    if ((tickAbs & 0x40000) != 0) ratio = mulRightShift(ratio, new BN('37481735321082'));
+
+    if (tick > 0) ratio = MaxUint128.div(ratio);
+    return ratio;
+  }
+
+  public static getTickFromPrice(price: Decimal, decimalsA: number, decimalsB: number): number {
+    return SqrtPriceMath.getTickFromSqrtPriceX64(SqrtPriceMath.priceToSqrtPriceX64(price, decimalsA, decimalsB));
+  }
+
+  public static getTickFromSqrtPriceX64(sqrtPriceX64: BN): number {
+    if (sqrtPriceX64.gt(MAX_SQRT_PRICE_X64) || sqrtPriceX64.lt(MIN_SQRT_PRICE_X64)) {
+      throw new Error('Provided sqrtPrice is not within the supported sqrtPrice range.');
+    }
+
+    const msb = sqrtPriceX64.bitLength() - 1;
+    const adjustedMsb = new BN(msb - 64);
+    const log2pIntegerX32 = signedLeftShift(adjustedMsb, 32, 128);
+
+    let bit = new BN('8000000000000000', 'hex');
+    let precision = 0;
+    let log2pFractionX64 = new BN(0);
+
+    let r = msb >= 64 ? sqrtPriceX64.shrn(msb - 63) : sqrtPriceX64.shln(63 - msb);
+
+    while (bit.gt(new BN(0)) && precision < BIT_PRECISION) {
+      r = r.mul(r);
+      const rMoreThanTwo = r.shrn(127);
+      r = r.shrn(63 + rMoreThanTwo.toNumber());
+      log2pFractionX64 = log2pFractionX64.add(bit.mul(rMoreThanTwo));
+      bit = bit.shrn(1);
+      precision += 1;
+    }
+
+    const log2pFractionX32 = log2pFractionX64.shrn(32);
+
+    const log2pX32 = log2pIntegerX32.add(log2pFractionX32);
+    const logbpX64 = log2pX32.mul(new BN(LOG_B_2_X32));
+
+    const tickLow = signedRightShift(logbpX64.sub(new BN(LOG_B_P_ERR_MARGIN_LOWER_X64)), 64, 128).toNumber();
+    const tickHigh = signedRightShift(logbpX64.add(new BN(LOG_B_P_ERR_MARGIN_UPPER_X64)), 64, 128).toNumber();
+
+    if (tickLow == tickHigh) {
+      return tickLow;
+    } else {
+      const derivedTickHighSqrtPriceX64 = SqrtPriceMath.getSqrtPriceX64FromTick(tickHigh);
+      return derivedTickHighSqrtPriceX64.lte(sqrtPriceX64) ? tickHigh : tickLow;
+    }
+  }
+}

+ 393 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/swapMath.ts

@@ -0,0 +1,393 @@
+import { PublicKey } from '@solana/web3.js';
+import BN from 'bn.js';
+
+import {
+  FEE_RATE_DENOMINATOR,
+  MAX_SQRT_PRICE_X64,
+  MAX_TICK,
+  MIN_SQRT_PRICE_X64,
+  MIN_TICK,
+  NEGATIVE_ONE,
+  ONE,
+  ZERO,
+} from '../constants';
+import { getPdaTickArrayAddress } from '../pda';
+
+import { LiquidityMath } from './liquidityMath';
+import { MathUtils } from './mathUtils';
+import { StepComputations, Tick, TickArrayContainer, TickArrayBitmapExtensionType } from './models';
+import { PoolUtils } from './poolUtils';
+import { SqrtPriceMath } from './sqrtPriceMath';
+import { TickQuery, TickUtils } from './tick';
+
+type SwapStep = {
+  sqrtPriceX64Next: BN;
+  amountIn: BN;
+  amountOut: BN;
+  feeAmount: BN;
+};
+
+export abstract class SwapMath {
+  /**
+   * Calculate swap path and output amount
+   *
+   * @param programId - Public key address of the CLMM program
+   * @param poolId - Public key address of the liquidity pool
+   * @param tickArrayCache - Tick array cache with keys as tick array start indices
+   * @param tickArrayBitmap - Tick array bitmap for quickly finding initialized ticks
+   * @param tickarrayBitmapExtension - Tick array bitmap extension information
+   * @param zeroForOne - Swap direction: true means token0 to token1, false means token1 to token0
+   * @param fee - Transaction fee rate (base 1000000)
+   * @param liquidity - Current liquidity amount
+   * @param currentTick - Current tick position
+   * @param tickSpacing - Tick spacing
+   * @param currentSqrtPriceX64 - Square root of current price (Q64.64 format)
+   * @param amountSpecified - Specified swap amount (positive for input amount, negative for output amount)
+   * @param lastSavedTickArrayStartIndex - Last saved tick array start index
+   * @param sqrtPriceLimitX64 - Square root of price limit (Q64.64 format), optional parameter
+   * @param catchLiquidityInsufficient - Whether to catch liquidity insufficient errors, default false
+   *
+   * @returns Swap calculation result object
+   * @returns allTrade - Whether all trades were completed
+   * @returns amountSpecifiedRemaining - Remaining untraded amount
+   * @returns amountCalculated - Calculated amount of the other token
+   * @returns feeAmount - Total fees
+   * @returns sqrtPriceX64 - Square root of price after swap (Q64.64 format)
+   * @returns liquidity - Liquidity after swap
+   * @returns tickCurrent - Tick position after swap
+   * @returns accounts - List of tick array accounts that need to be accessed
+   */
+  public static swapCompute(
+    programId: PublicKey,
+    poolId: PublicKey,
+    tickArrayInfo: { [key: string]: TickArrayContainer },
+    tickArrayBitmap: BN[],
+    tickarrayBitmapExtension: TickArrayBitmapExtensionType,
+    zeroForOne: boolean,
+    fee: number,
+    liquidity: BN,
+    currentTick: number,
+    tickSpacing: number,
+    currentSqrtPriceX64: BN,
+    amountSpecified: BN,
+    lastSavedTickArrayStartIndex: number,
+    sqrtPriceLimitX64?: BN,
+    catchLiquidityInsufficient = false,
+  ): {
+    allTrade: boolean;
+    amountSpecifiedRemaining: BN;
+    amountCalculated: BN;
+    feeAmount: BN;
+    sqrtPriceX64: BN;
+    liquidity: BN;
+    tickCurrent: number;
+    accounts: PublicKey[];
+  } {
+    if (amountSpecified.eq(ZERO)) {
+      throw new Error('amountSpecified must not be 0');
+    }
+    if (!sqrtPriceLimitX64) sqrtPriceLimitX64 = zeroForOne ? MIN_SQRT_PRICE_X64.add(ONE) : MAX_SQRT_PRICE_X64.sub(ONE);
+
+    if (zeroForOne) {
+      if (sqrtPriceLimitX64.lt(MIN_SQRT_PRICE_X64)) {
+        throw new Error('sqrtPriceX64 must greater than MIN_SQRT_PRICE_X64');
+      }
+
+      if (sqrtPriceLimitX64.gte(currentSqrtPriceX64)) {
+        throw new Error('sqrtPriceX64 must smaller than current');
+      }
+    } else {
+      if (sqrtPriceLimitX64.gt(MAX_SQRT_PRICE_X64)) {
+        throw new Error('sqrtPriceX64 must smaller than MAX_SQRT_PRICE_X64');
+      }
+
+      if (sqrtPriceLimitX64.lte(currentSqrtPriceX64)) {
+        throw new Error('sqrtPriceX64 must greater than current');
+      }
+    }
+
+    const baseInput = amountSpecified.gt(ZERO);
+
+    const state = {
+      amountSpecifiedRemaining: amountSpecified,
+      amountCalculated: ZERO,
+      sqrtPriceX64: currentSqrtPriceX64,
+      tick:
+        currentTick > lastSavedTickArrayStartIndex
+          ? Math.min(lastSavedTickArrayStartIndex + TickQuery.tickCount(tickSpacing) - 1, currentTick)
+          : lastSavedTickArrayStartIndex,
+      accounts: [] as PublicKey[],
+      liquidity,
+      feeAmount: new BN(0),
+    };
+    let tickAarrayStartIndex = lastSavedTickArrayStartIndex;
+    let tickArrayCurrent = tickArrayInfo[lastSavedTickArrayStartIndex];
+
+    // Verify if tickArrayInfo contains necessary data
+    if (!tickArrayCurrent) {
+      throw new Error(
+        `TickArray not found in cache for startIndex: ${lastSavedTickArrayStartIndex}. Please ensure tickArrayCache is properly initialized.`,
+      );
+    }
+
+    let loopCount = 0;
+    let t = !zeroForOne && tickArrayCurrent.data.startTickIndex === state.tick;
+    while (
+      !state.amountSpecifiedRemaining.eq(ZERO) &&
+      !state.sqrtPriceX64.eq(sqrtPriceLimitX64)
+      // state.tick < MAX_TICK &&
+      // state.tick > MIN_TICK
+    ) {
+      if (loopCount > 10) {
+        // throw Error('liquidity limit')
+      }
+      const step: Partial<StepComputations> = {};
+      step.sqrtPriceStartX64 = state.sqrtPriceX64;
+
+      const tickState: Tick | null = TickUtils.nextInitTick(tickArrayCurrent, state.tick, tickSpacing, zeroForOne, t);
+
+      let nextInitTick: Tick | null = tickState ? tickState : null; // TickUtils.firstInitializedTick(tickArrayCurrent, zeroForOne)
+      let tickArrayAddress: null | PublicKey = null;
+
+      if (!nextInitTick?.liquidityGross.gtn(0)) {
+        const nextInitTickArrayIndex = PoolUtils.nextInitializedTickArrayStartIndex(
+          {
+            tickCurrent: state.tick,
+            tickSpacing,
+            tickArrayBitmap,
+            exBitmapInfo: tickarrayBitmapExtension,
+          },
+          tickAarrayStartIndex,
+          zeroForOne,
+        );
+        if (!nextInitTickArrayIndex.isExist) {
+          if (catchLiquidityInsufficient) {
+            return {
+              allTrade: false,
+              amountSpecifiedRemaining: state.amountSpecifiedRemaining,
+              amountCalculated: state.amountCalculated,
+              feeAmount: state.feeAmount,
+              sqrtPriceX64: state.sqrtPriceX64,
+              liquidity: state.liquidity,
+              tickCurrent: state.tick,
+              accounts: state.accounts,
+            };
+          }
+          throw Error('swapCompute LiquidityInsufficient');
+        }
+        tickAarrayStartIndex = nextInitTickArrayIndex.nextStartIndex;
+
+        const { publicKey: expectedNextTickArrayAddress } = getPdaTickArrayAddress(
+          programId,
+          poolId,
+          tickAarrayStartIndex,
+        );
+        tickArrayAddress = expectedNextTickArrayAddress;
+        tickArrayCurrent = tickArrayInfo[tickAarrayStartIndex];
+
+        // Verify if new tick array is in cache
+        if (!tickArrayCurrent) {
+          throw new Error(
+            `TickArray not found in cache for startIndex: ${tickAarrayStartIndex}. Missing tick array data during swap computation.`,
+          );
+        }
+
+        try {
+          nextInitTick = TickUtils.firstInitializedTick(tickArrayCurrent, zeroForOne);
+        } catch (error) {
+          throw new Error(
+            `Failed to find initialized tick in array ${tickAarrayStartIndex}: ${
+              error instanceof Error ? error.message : 'Unknown error'
+            }`,
+          );
+        }
+      }
+
+      step.tickNext = nextInitTick.tick;
+      step.initialized = nextInitTick.liquidityGross.gtn(0);
+      if (lastSavedTickArrayStartIndex !== tickAarrayStartIndex && tickArrayAddress) {
+        state.accounts.push(tickArrayAddress);
+        lastSavedTickArrayStartIndex = tickAarrayStartIndex;
+      }
+      if (step.tickNext < MIN_TICK) {
+        step.tickNext = MIN_TICK;
+      } else if (step.tickNext > MAX_TICK) {
+        step.tickNext = MAX_TICK;
+      }
+
+      step.sqrtPriceNextX64 = SqrtPriceMath.getSqrtPriceX64FromTick(step.tickNext);
+      let targetPrice: BN;
+      if (
+        (zeroForOne && step.sqrtPriceNextX64.lt(sqrtPriceLimitX64)) ||
+        (!zeroForOne && step.sqrtPriceNextX64.gt(sqrtPriceLimitX64))
+      ) {
+        targetPrice = sqrtPriceLimitX64;
+      } else {
+        targetPrice = step.sqrtPriceNextX64;
+      }
+      [state.sqrtPriceX64, step.amountIn, step.amountOut, step.feeAmount] = SwapMath.swapStepCompute(
+        state.sqrtPriceX64,
+        targetPrice,
+        state.liquidity,
+        state.amountSpecifiedRemaining,
+        fee,
+        zeroForOne,
+      );
+
+      state.feeAmount = state.feeAmount.add(step.feeAmount);
+
+      if (baseInput) {
+        state.amountSpecifiedRemaining = state.amountSpecifiedRemaining.sub(step.amountIn.add(step.feeAmount));
+        state.amountCalculated = state.amountCalculated.sub(step.amountOut);
+      } else {
+        state.amountSpecifiedRemaining = state.amountSpecifiedRemaining.add(step.amountOut);
+        state.amountCalculated = state.amountCalculated.add(step.amountIn.add(step.feeAmount));
+      }
+      if (state.sqrtPriceX64.eq(step.sqrtPriceNextX64)) {
+        if (step.initialized) {
+          let liquidityNet = nextInitTick.liquidityNet;
+          if (zeroForOne) liquidityNet = liquidityNet.mul(NEGATIVE_ONE);
+          state.liquidity = LiquidityMath.addDelta(state.liquidity, liquidityNet);
+        }
+
+        t = step.tickNext != state.tick && !zeroForOne && tickArrayCurrent.data.startTickIndex === step.tickNext;
+        state.tick = zeroForOne ? step.tickNext - 1 : step.tickNext; //
+      } else if (state.sqrtPriceX64 != step.sqrtPriceStartX64) {
+        const _T = SqrtPriceMath.getTickFromSqrtPriceX64(state.sqrtPriceX64);
+        t = _T != state.tick && !zeroForOne && tickArrayCurrent.data.startTickIndex === _T;
+        state.tick = _T;
+      }
+      ++loopCount;
+    }
+
+    try {
+      const { nextStartIndex: tickAarrayStartIndex, isExist } = TickQuery.nextInitializedTickArray(
+        state.tick,
+        tickSpacing,
+        zeroForOne,
+        tickArrayBitmap,
+        tickarrayBitmapExtension,
+      );
+      if (isExist && lastSavedTickArrayStartIndex !== tickAarrayStartIndex) {
+        state.accounts.push(getPdaTickArrayAddress(programId, poolId, tickAarrayStartIndex).publicKey);
+        lastSavedTickArrayStartIndex = tickAarrayStartIndex;
+      }
+    } catch (e) {
+      /* empty */
+    }
+
+    return {
+      allTrade: true,
+      amountSpecifiedRemaining: ZERO,
+      amountCalculated: state.amountCalculated,
+      feeAmount: state.feeAmount,
+      sqrtPriceX64: state.sqrtPriceX64,
+      liquidity: state.liquidity,
+      tickCurrent: state.tick,
+      accounts: state.accounts,
+    };
+  }
+
+  private static swapStepCompute(
+    sqrtPriceX64Current: BN,
+    sqrtPriceX64Target: BN,
+    liquidity: BN,
+    amountRemaining: BN,
+    feeRate: number, // Base 1000000
+    zeroForOne: boolean,
+  ): [BN, BN, BN, BN] {
+    const swapStep: SwapStep = {
+      sqrtPriceX64Next: new BN(0),
+      amountIn: new BN(0),
+      amountOut: new BN(0),
+      feeAmount: new BN(0),
+    };
+
+    const baseInput = amountRemaining.gte(ZERO);
+
+    if (baseInput) {
+      const amountRemainingSubtractFee = MathUtils.mulDivFloor(
+        amountRemaining,
+        FEE_RATE_DENOMINATOR.sub(new BN(feeRate.toString())),
+        FEE_RATE_DENOMINATOR,
+      );
+      swapStep.amountIn = zeroForOne
+        ? LiquidityMath.getTokenAmountAFromLiquidity(sqrtPriceX64Target, sqrtPriceX64Current, liquidity, true)
+        : LiquidityMath.getTokenAmountBFromLiquidity(sqrtPriceX64Current, sqrtPriceX64Target, liquidity, true);
+      if (amountRemainingSubtractFee.gte(swapStep.amountIn)) {
+        swapStep.sqrtPriceX64Next = sqrtPriceX64Target;
+      } else {
+        swapStep.sqrtPriceX64Next = SqrtPriceMath.getNextSqrtPriceX64FromInput(
+          sqrtPriceX64Current,
+          liquidity,
+          amountRemainingSubtractFee,
+          zeroForOne,
+        );
+      }
+    } else {
+      swapStep.amountOut = zeroForOne
+        ? LiquidityMath.getTokenAmountBFromLiquidity(sqrtPriceX64Target, sqrtPriceX64Current, liquidity, false)
+        : LiquidityMath.getTokenAmountAFromLiquidity(sqrtPriceX64Current, sqrtPriceX64Target, liquidity, false);
+      if (amountRemaining.mul(NEGATIVE_ONE).gte(swapStep.amountOut)) {
+        swapStep.sqrtPriceX64Next = sqrtPriceX64Target;
+      } else {
+        swapStep.sqrtPriceX64Next = SqrtPriceMath.getNextSqrtPriceX64FromOutput(
+          sqrtPriceX64Current,
+          liquidity,
+          amountRemaining.mul(NEGATIVE_ONE),
+          zeroForOne,
+        );
+      }
+    }
+
+    const reachTargetPrice = sqrtPriceX64Target.eq(swapStep.sqrtPriceX64Next);
+
+    if (zeroForOne) {
+      if (!(reachTargetPrice && baseInput)) {
+        swapStep.amountIn = LiquidityMath.getTokenAmountAFromLiquidity(
+          swapStep.sqrtPriceX64Next,
+          sqrtPriceX64Current,
+          liquidity,
+          true,
+        );
+      }
+
+      if (!(reachTargetPrice && !baseInput)) {
+        swapStep.amountOut = LiquidityMath.getTokenAmountBFromLiquidity(
+          swapStep.sqrtPriceX64Next,
+          sqrtPriceX64Current,
+          liquidity,
+          false,
+        );
+      }
+    } else {
+      swapStep.amountIn =
+        reachTargetPrice && baseInput
+          ? swapStep.amountIn
+          : LiquidityMath.getTokenAmountBFromLiquidity(sqrtPriceX64Current, swapStep.sqrtPriceX64Next, liquidity, true);
+      swapStep.amountOut =
+        reachTargetPrice && !baseInput
+          ? swapStep.amountOut
+          : LiquidityMath.getTokenAmountAFromLiquidity(
+              sqrtPriceX64Current,
+              swapStep.sqrtPriceX64Next,
+              liquidity,
+              false,
+            );
+    }
+
+    if (!baseInput && swapStep.amountOut.gt(amountRemaining.mul(NEGATIVE_ONE))) {
+      swapStep.amountOut = amountRemaining.mul(NEGATIVE_ONE);
+    }
+    if (baseInput && !swapStep.sqrtPriceX64Next.eq(sqrtPriceX64Target)) {
+      swapStep.feeAmount = amountRemaining.sub(swapStep.amountIn);
+    } else {
+      swapStep.feeAmount = MathUtils.mulDivCeil(
+        swapStep.amountIn,
+        new BN(feeRate),
+        FEE_RATE_DENOMINATOR.sub(new BN(feeRate)),
+      );
+    }
+    return [swapStep.sqrtPriceX64Next, swapStep.amountIn, swapStep.amountOut, swapStep.feeAmount];
+  }
+}

+ 567 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/tick.ts

@@ -0,0 +1,567 @@
+import { PublicKey } from '@solana/web3.js';
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+import { TICK_ARRAY_SIZE, TICK_ARRAY_BITMAP_SIZE } from '../../constants';
+import { MAX_TICK, MIN_TICK } from '../constants';
+import { IPoolLayout } from '../layout';
+import { TickArrayBitmapExtensionType } from '../models';
+import { TickArrayBitmapExtensionUtils } from './tickarrayBitmap';
+import { getPdaTickArrayAddress } from '../pda';
+
+import {
+  ReturnTypeGetPriceAndTick,
+  Tick,
+  TickArray,
+  TickArrayState,
+  TickState,
+  TickArrayContainer,
+  DynTickArray,
+} from './models';
+import { SqrtPriceMath } from './sqrtPriceMath';
+import { TickMath } from './tickMath';
+
+export class TickUtils {
+  // Calculate the address of the TickArray corresponding to the given tick index
+  public static getTickArrayAddressByTick(
+    programId: PublicKey,
+    poolId: PublicKey,
+    tickIndex: number,
+    tickSpacing: number,
+  ): PublicKey {
+    const startIndex = TickUtils.getTickArrayStartIndexByTick(tickIndex, tickSpacing);
+    const { publicKey: tickArrayAddress } = getPdaTickArrayAddress(programId, poolId, startIndex);
+    return tickArrayAddress;
+  }
+
+  // Calculate the offset of the given tick index within the corresponding TickArray
+  public static getTickOffsetInArray(tickIndex: number, tickSpacing: number): number {
+    if (tickIndex % tickSpacing != 0) {
+      throw new Error('tickIndex % tickSpacing not equal 0');
+    }
+    const startTickIndex = TickUtils.getTickArrayStartIndexByTick(tickIndex, tickSpacing);
+    const offsetInArray = Math.floor((tickIndex - startTickIndex) / tickSpacing);
+    if (offsetInArray < 0 || offsetInArray >= TICK_ARRAY_SIZE) {
+      throw new Error('tick offset in array overflow');
+    }
+    return offsetInArray;
+  }
+
+  // Calculate the index position of the TickArray corresponding to the given tick index in the bitmap.
+  public static getTickArrayBitIndex(tickIndex: number, tickSpacing: number): number {
+    const ticksInArray = TickQuery.tickCount(tickSpacing);
+
+    let startIndex: number = tickIndex / ticksInArray;
+    if (tickIndex < 0 && tickIndex % ticksInArray != 0) {
+      startIndex = Math.ceil(startIndex) - 1;
+    } else {
+      startIndex = Math.floor(startIndex);
+    }
+    return startIndex;
+  }
+
+  // Calculate the start index of the TickArray corresponding to the given tick index
+  public static getTickArrayStartIndexByTick(tickIndex: number, tickSpacing: number): number {
+    return this.getTickArrayBitIndex(tickIndex, tickSpacing) * TickQuery.tickCount(tickSpacing);
+  }
+
+  // Calculate the offset of the TickArray corresponding to the given tick index in the default bitmap
+  public static getTickArrayOffsetInBitmapByTick(tick: number, tickSpacing: number): number {
+    const multiplier = tickSpacing * TICK_ARRAY_SIZE;
+    const compressed = Math.floor(tick / multiplier) + 512;
+    return Math.abs(compressed);
+  }
+
+  // Check if the TickArray corresponding to the given tick index has been initialized
+  public static checkTickArrayIsInitialized(
+    bitmap: BN,
+    tick: number,
+    tickSpacing: number,
+  ): {
+    isInitialized: boolean;
+    startIndex: number;
+  } {
+    const multiplier = tickSpacing * TICK_ARRAY_SIZE;
+    const compressed = Math.floor(tick / multiplier) + 512;
+    const bitPos = Math.abs(compressed);
+    return {
+      isInitialized: bitmap.testn(bitPos),
+      startIndex: (bitPos - 512) * multiplier,
+    };
+  }
+
+  // Calculate the start index of the next TickArray
+  // Can be ignored, referenced by nextInitializedTick, but nextInitializedTick is not used
+  public static getNextTickArrayStartIndex(
+    lastTickArrayStartIndex: number,
+    tickSpacing: number,
+    zeroForOne: boolean,
+  ): number {
+    return zeroForOne
+      ? lastTickArrayStartIndex - tickSpacing * TICK_ARRAY_SIZE
+      : lastTickArrayStartIndex + tickSpacing * TICK_ARRAY_SIZE;
+  }
+
+  // Merge bitmaps of multiple TickArrays
+  public static mergeTickArrayBitmap(bns: BN[]): BN {
+    let b = new BN(0);
+    for (let i = 0; i < bns.length; i++) {
+      b = b.add(bns[i].shln(64 * i));
+    }
+    return b;
+  }
+
+  /**
+   * Search for initialized TickArrays around the current position to find the starting indices of a specified number of initialized TickArrays
+   *
+   * @param tickArrayBitmap - Main tick array bitmap used to mark initialized tick arrays
+   * @param exTickArrayBitmap - Extended tick array bitmap containing positive and negative direction extension bitmap information
+   * @param tickSpacing - Tick spacing
+   * @param tickArrayStartIndex - Starting tick array index for the search
+   * @param expectedCount - Expected number of initialized TickArrays to find
+   *
+   * @returns Array of initialized TickArray starting indices
+   *
+   * @description
+   * This function is used to locate active liquidity around the current price. It searches on both sides of the given starting position:
+   * - Search right (higher tick positions)
+   * - Search left (lower tick positions)
+   * By combining the search results from both directions, it provides the liquidity distribution around the current price
+   */
+  public static getInitializedTickArrayInRange(
+    tickArrayBitmap: BN[],
+    exTickArrayBitmap: TickArrayBitmapExtensionType,
+    tickSpacing: number,
+    tickArrayStartIndex: number,
+    expectedCount: number,
+  ): number[] {
+    const tickArrayOffset = Math.floor(tickArrayStartIndex / (tickSpacing * TICK_ARRAY_SIZE));
+    return [
+      // find right of currenct offset
+      ...TickUtils.searchLowBitFromStart(
+        tickArrayBitmap,
+        exTickArrayBitmap,
+        tickArrayOffset - 1,
+        expectedCount,
+        tickSpacing,
+      ),
+
+      // find left of current offset
+      ...TickUtils.searchHightBitFromStart(
+        tickArrayBitmap,
+        exTickArrayBitmap,
+        tickArrayOffset,
+        expectedCount,
+        tickSpacing,
+      ),
+    ];
+  }
+
+  // Get start indices of all initialized TickArrays
+  // TODO: The TICK_ARRAY_BITMAP_SIZE passed in this function may not be able to retrieve all initialized TickArrays
+  public static getAllInitializedTickArrayStartIndex(
+    tickArrayBitmap: BN[],
+    exTickArrayBitmap: TickArrayBitmapExtensionType,
+    tickSpacing: number,
+  ): number[] {
+    // find from offset 0 to 1024
+    return TickUtils.searchHightBitFromStart(
+      tickArrayBitmap,
+      exTickArrayBitmap,
+      -7680,
+      TICK_ARRAY_BITMAP_SIZE,
+      tickSpacing,
+    );
+  }
+
+  // Get information of all initialized TickArrays
+  public static getAllInitializedTickArrayInfo(
+    programId: PublicKey,
+    poolId: PublicKey,
+    tickArrayBitmap: BN[],
+    exTickArrayBitmap: TickArrayBitmapExtensionType,
+    tickSpacing: number,
+  ): {
+    tickArrayStartIndex: number;
+    tickArrayAddress: PublicKey;
+  }[] {
+    const result: {
+      tickArrayStartIndex: number;
+      tickArrayAddress: PublicKey;
+    }[] = [];
+    const allInitializedTickArrayIndex: number[] = TickUtils.getAllInitializedTickArrayStartIndex(
+      tickArrayBitmap,
+      exTickArrayBitmap,
+      tickSpacing,
+    );
+    for (const startIndex of allInitializedTickArrayIndex) {
+      const { publicKey: address } = getPdaTickArrayAddress(programId, poolId, startIndex);
+      result.push({
+        tickArrayStartIndex: startIndex,
+        tickArrayAddress: address,
+      });
+    }
+    return result;
+  }
+
+  // Get all initialized Ticks in the given TickArray
+  public static getAllInitializedTickInTickArray(tickArray: TickArrayState): TickState[] {
+    return tickArray.ticks.filter((i) => i.liquidityGross.gtn(0));
+  }
+
+  /**
+   * Search for initialized TickArrays to the left from the starting position
+   * Used to locate active liquidity near the current price
+   */
+  public static searchLowBitFromStart(
+    tickArrayBitmap: BN[],
+    exTickArrayBitmap: TickArrayBitmapExtensionType,
+    currentTickArrayBitStartIndex: number,
+    expectedCount: number,
+    tickSpacing: number,
+  ): number[] {
+    const tickArrayBitmaps = [
+      ...[...exTickArrayBitmap.negativeTickArrayBitmap].reverse(),
+      tickArrayBitmap.slice(0, 8),
+      tickArrayBitmap.slice(8, 16),
+      ...exTickArrayBitmap.positiveTickArrayBitmap,
+    ].map((i) => TickUtils.mergeTickArrayBitmap(i));
+    const result: number[] = [];
+    while (currentTickArrayBitStartIndex >= -7680) {
+      const arrayIndex = Math.floor((currentTickArrayBitStartIndex + 7680) / 512);
+      const searchIndex = (currentTickArrayBitStartIndex + 7680) % 512;
+
+      if (tickArrayBitmaps[arrayIndex].testn(searchIndex)) result.push(currentTickArrayBitStartIndex);
+
+      currentTickArrayBitStartIndex--;
+      if (result.length === expectedCount) break;
+    }
+
+    const tickCount = TickQuery.tickCount(tickSpacing);
+    return result.map((i) => i * tickCount);
+  }
+
+  /**
+   * Search for initialized TickArrays to the right from the starting position
+   * Used to locate active liquidity near the current price
+   */
+  public static searchHightBitFromStart(
+    tickArrayBitmap: BN[],
+    exTickArrayBitmap: TickArrayBitmapExtensionType,
+    currentTickArrayBitStartIndex: number,
+    expectedCount: number,
+    tickSpacing: number,
+  ): number[] {
+    const tickArrayBitmaps = [
+      ...[...exTickArrayBitmap.negativeTickArrayBitmap].reverse(),
+      tickArrayBitmap.slice(0, 8),
+      tickArrayBitmap.slice(8, 16),
+      ...exTickArrayBitmap.positiveTickArrayBitmap,
+    ].map((i) => TickUtils.mergeTickArrayBitmap(i));
+    const result: number[] = [];
+    while (currentTickArrayBitStartIndex < 7680) {
+      const arrayIndex = Math.floor((currentTickArrayBitStartIndex + 7680) / 512);
+      const searchIndex = (currentTickArrayBitStartIndex + 7680) % 512;
+
+      if (tickArrayBitmaps[arrayIndex].testn(searchIndex)) result.push(currentTickArrayBitStartIndex);
+
+      currentTickArrayBitStartIndex++;
+      if (result.length === expectedCount) break;
+    }
+
+    const tickCount = TickQuery.tickCount(tickSpacing);
+    return result.map((i) => i * tickCount);
+  }
+
+  // Check if the given tick index is out of bounds
+  public static checkIsOutOfBoundary(tick: number): boolean {
+    return tick < MIN_TICK || tick > MAX_TICK;
+  }
+
+  /**
+   * Get the next initialized Tick
+   * Used to locate active liquidity near the current price
+   * Now supports both fixed and dynamic tick arrays through the container pattern
+   */
+  public static nextInitTick(
+    tickArrayCurrent: TickArrayContainer,
+    currentTickIndex: number,
+    tickSpacing: number,
+    zeroForOne: boolean,
+    t: boolean,
+  ): Tick | null {
+    if (tickArrayCurrent.type === 'Fixed') {
+      return this._nextInitTickFixed(tickArrayCurrent.data, currentTickIndex, tickSpacing, zeroForOne, t);
+    } else {
+      return this._nextInitTickDynamic(tickArrayCurrent.data, currentTickIndex, tickSpacing, zeroForOne, t);
+    }
+  }
+
+  /**
+   * Fixed tick array implementation (original logic)
+   */
+  private static _nextInitTickFixed(
+    tickArray: TickArray,
+    currentTickIndex: number,
+    tickSpacing: number,
+    zeroForOne: boolean,
+    t: boolean,
+  ): Tick | null {
+    const currentTickArrayStartIndex = TickQuery.getArrayStartIndex(currentTickIndex, tickSpacing);
+    if (currentTickArrayStartIndex != tickArray.startTickIndex) {
+      return null;
+    }
+    let offsetInArray = Math.floor((currentTickIndex - tickArray.startTickIndex) / tickSpacing);
+
+    if (zeroForOne) {
+      while (offsetInArray >= 0) {
+        if (tickArray.ticks[offsetInArray].liquidityGross.gtn(0)) {
+          return tickArray.ticks[offsetInArray];
+        }
+        offsetInArray = offsetInArray - 1;
+      }
+    } else {
+      if (!t) offsetInArray = offsetInArray + 1;
+      while (offsetInArray < TICK_ARRAY_SIZE) {
+        if (tickArray.ticks[offsetInArray].liquidityGross.gtn(0)) {
+          return tickArray.ticks[offsetInArray];
+        }
+        offsetInArray = offsetInArray + 1;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Dynamic tick array implementation
+   * Uses the mapping table (tickOffsetIndex) to find allocated ticks
+   */
+  private static _nextInitTickDynamic(
+    dynTickArray: DynTickArray,
+    currentTickIndex: number,
+    tickSpacing: number,
+    zeroForOne: boolean,
+    t: boolean,
+  ): Tick | null {
+    const currentTickArrayStartIndex = TickQuery.getArrayStartIndex(currentTickIndex, tickSpacing);
+    if (currentTickArrayStartIndex !== dynTickArray.startTickIndex) {
+      return null;
+    }
+
+    let offsetInArray = Math.floor((currentTickIndex - dynTickArray.startTickIndex) / tickSpacing);
+
+    if (zeroForOne) {
+      while (offsetInArray >= 0) {
+        // Check mapping table
+        const physicalIndex = dynTickArray.tickOffsetIndex[offsetInArray];
+
+        if (physicalIndex > 0) {
+          const tick = dynTickArray.ticks[physicalIndex - 1];
+          if (tick.liquidityGross.gtn(0)) {
+            return tick;
+          }
+        }
+
+        offsetInArray = offsetInArray - 1;
+      }
+    } else {
+      if (!t) offsetInArray = offsetInArray + 1;
+
+      while (offsetInArray < TICK_ARRAY_SIZE) {
+        const physicalIndex = dynTickArray.tickOffsetIndex[offsetInArray];
+
+        if (physicalIndex > 0) {
+          const tick = dynTickArray.ticks[physicalIndex - 1];
+          if (tick.liquidityGross.gtn(0)) {
+            return tick;
+          }
+        }
+
+        offsetInArray = offsetInArray + 1;
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * Find the first initialized Tick in the given TickArray, where "first" is defined based on the trading direction (zeroForOne)
+   * Now supports both fixed and dynamic tick arrays through the container pattern
+   */
+  public static firstInitializedTick(tickArrayCurrent: TickArrayContainer, zeroForOne: boolean): Tick {
+    if (tickArrayCurrent.type === 'Fixed') {
+      return this._firstInitializedTickFixed(tickArrayCurrent.data, zeroForOne);
+    } else {
+      return this._firstInitializedTickDynamic(tickArrayCurrent.data, zeroForOne);
+    }
+  }
+
+  /**
+   * Fixed tick array implementation (original logic)
+   */
+  private static _firstInitializedTickFixed(tickArray: TickArray, zeroForOne: boolean): Tick {
+    if (zeroForOne) {
+      let i = TICK_ARRAY_SIZE - 1;
+      while (i >= 0) {
+        if (tickArray.ticks[i].liquidityGross.gtn(0)) {
+          return tickArray.ticks[i];
+        }
+        i = i - 1;
+      }
+    } else {
+      let i = 0;
+      while (i < TICK_ARRAY_SIZE) {
+        if (tickArray.ticks[i].liquidityGross.gtn(0)) {
+          return tickArray.ticks[i];
+        }
+        i = i + 1;
+      }
+    }
+
+    throw Error(`firstInitializedTick check error: ${tickArray} - ${zeroForOne}`);
+  }
+
+  /**
+   * Dynamic tick array implementation
+   * Uses the mapping table (tickOffsetIndex) to find the first allocated tick
+   */
+  private static _firstInitializedTickDynamic(dynTickArray: DynTickArray, zeroForOne: boolean): Tick {
+    if (zeroForOne) {
+      // Search from right to left (highest tick index first)
+      let i = TICK_ARRAY_SIZE - 1;
+      while (i >= 0) {
+        const physicalIndex = dynTickArray.tickOffsetIndex[i];
+
+        if (physicalIndex > 0) {
+          const tick = dynTickArray.ticks[physicalIndex - 1];
+          if (tick.liquidityGross.gtn(0)) {
+            return tick;
+          }
+        }
+
+        i = i - 1;
+      }
+    } else {
+      // Search from left to right (lowest tick index first)
+      let i = 0;
+      while (i < TICK_ARRAY_SIZE) {
+        const physicalIndex = dynTickArray.tickOffsetIndex[i];
+
+        if (physicalIndex > 0) {
+          const tick = dynTickArray.ticks[physicalIndex - 1];
+          if (tick.liquidityGross.gtn(0)) {
+            return tick;
+          }
+        }
+
+        i = i + 1;
+      }
+    }
+
+    throw Error(`firstInitializedTick check error: ${dynTickArray} - ${zeroForOne}`);
+  }
+
+  public static getPriceAndTick({
+    poolInfo,
+    price,
+    baseIn,
+  }: {
+    poolInfo: IPoolLayout;
+    price: Decimal;
+    baseIn: boolean;
+  }): ReturnTypeGetPriceAndTick {
+    const _price = baseIn ? price : new Decimal(1).div(price);
+
+    const tick = TickMath.getTickWithPriceAndTickspacing(
+      _price,
+      poolInfo.tickSpacing,
+      poolInfo.mintDecimalsA,
+      poolInfo.mintDecimalsB,
+    );
+    const tickSqrtPriceX64 = SqrtPriceMath.getSqrtPriceX64FromTick(tick);
+    const tickPrice = SqrtPriceMath.sqrtPriceX64ToPrice(
+      tickSqrtPriceX64,
+      poolInfo.mintDecimalsA,
+      poolInfo.mintDecimalsB,
+    );
+
+    return baseIn ? { tick, price: tickPrice } : { tick, price: new Decimal(1).div(tickPrice) };
+  }
+}
+
+export declare type PoolVars = {
+  key: PublicKey;
+  tokenA: PublicKey;
+  tokenB: PublicKey;
+  fee: number;
+};
+
+export class TickQuery {
+  /**
+   * Find the next initialized TickArray
+   */
+  public static nextInitializedTickArray(
+    tickIndex: number,
+    tickSpacing: number,
+    zeroForOne: boolean,
+    tickArrayBitmap: BN[],
+    exBitmapInfo: TickArrayBitmapExtensionType,
+  ): {
+    isExist: boolean;
+    nextStartIndex: number;
+  } {
+    // First, include the array that contains tickIndex itself if initialized (handles boundary inclusively)
+    const startIndex = TickQuery.getArrayStartIndex(tickIndex, tickSpacing);
+    const merged = TickUtils.mergeTickArrayBitmap(tickArrayBitmap);
+    try {
+      const { isInitialized } = TickUtils.checkTickArrayIsInitialized(merged, startIndex, tickSpacing);
+      if (isInitialized) return { isExist: true, nextStartIndex: startIndex };
+    } catch {
+      /* ignore */
+    }
+    try {
+      const { isInitialized } = TickArrayBitmapExtensionUtils.checkTickArrayIsInit(
+        startIndex,
+        tickSpacing,
+        exBitmapInfo,
+      );
+      if (isInitialized) return { isExist: true, nextStartIndex: startIndex };
+    } catch {
+      /* ignore */
+    }
+
+    // Otherwise, search outward in the given direction
+    const currentOffset = TickUtils.getTickArrayBitIndex(tickIndex, tickSpacing);
+    const result: number[] = zeroForOne
+      ? TickUtils.searchLowBitFromStart(tickArrayBitmap, exBitmapInfo, currentOffset - 1, 1, tickSpacing)
+      : TickUtils.searchHightBitFromStart(tickArrayBitmap, exBitmapInfo, currentOffset + 1, 1, tickSpacing);
+
+    return result.length > 0 ? { isExist: true, nextStartIndex: result[0] } : { isExist: false, nextStartIndex: 0 };
+  }
+
+  // Calculate the start index of the TickArray corresponding to the given tick index
+  public static getArrayStartIndex(tickIndex: number, tickSpacing: number): number {
+    const ticksInArray = this.tickCount(tickSpacing);
+    const start = Math.floor(tickIndex / ticksInArray);
+
+    return start * ticksInArray;
+  }
+
+  // Check if the given tick index is a valid start index
+  public static checkIsValidStartIndex(tickIndex: number, tickSpacing: number): boolean {
+    if (TickUtils.checkIsOutOfBoundary(tickIndex)) {
+      if (tickIndex > MAX_TICK) {
+        return false;
+      }
+      // In extreme scenarios, tickIndex may be less than MIN_TICK
+      const minStartIndex = TickUtils.getTickArrayStartIndexByTick(MIN_TICK, tickSpacing);
+      return tickIndex == minStartIndex;
+    }
+    return tickIndex % this.tickCount(tickSpacing) == 0;
+  }
+
+  // Calculate the number of ticks contained in one tickarray
+  public static tickCount(tickSpacing: number): number {
+    return TICK_ARRAY_SIZE * tickSpacing;
+  }
+}

+ 189 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/tickArrayUtils.ts

@@ -0,0 +1,189 @@
+import { PublicKey } from '@solana/web3.js';
+
+import { DYN_TICK_ARRAY_HEADER_LEN, TICK_STATE_LEN } from '../../constants';
+import { DynTickArrayLayout, TickArrayLayout, TickStateLayout } from '../layout';
+
+import { Tick, DynTickArray, TickArrayContainer } from './models';
+import { TickUtils } from './tick';
+import BN from 'bn.js';
+
+/**
+ * Tick Array 工具类
+ * 处理固定和动态 Tick Array 的统一操作
+ */
+export class TickArrayUtils {
+  /**
+   * Discriminator 常量
+   * 这些值通过 Anchor 框架自动生成(SHA256("account:<StructName>") 的前 8 字节)
+   */
+  private static readonly FIXED_TICK_ARRAY_DISCRIMINATOR = Buffer.from('c09b55cd31f9812a', 'hex');
+  private static readonly DYN_TICK_ARRAY_DISCRIMINATOR = Buffer.from('6a8b98247599b838', 'hex');
+
+  /**
+   * 识别 Tick Array 的类型
+   * @param accountData 账户数据
+   * @returns 'Fixed' 或 'Dynamic'
+   */
+  public static identifyTickArrayType(accountData: Buffer): 'Fixed' | 'Dynamic' {
+    if (accountData.length < 8) {
+      throw new Error('Invalid tick array data: too short to contain discriminator');
+    }
+
+    const discriminator = accountData.slice(0, 8);
+
+    if (discriminator.equals(this.FIXED_TICK_ARRAY_DISCRIMINATOR)) {
+      return 'Fixed';
+    } else if (discriminator.equals(this.DYN_TICK_ARRAY_DISCRIMINATOR)) {
+      return 'Dynamic';
+    } else {
+      throw new Error(
+        `Unknown tick array discriminator: ${discriminator.toString('hex')}. ` +
+          `Expected Fixed: ${this.FIXED_TICK_ARRAY_DISCRIMINATOR.toString('hex')} ` +
+          `or Dynamic: ${this.DYN_TICK_ARRAY_DISCRIMINATOR.toString('hex')}`,
+      );
+    }
+  }
+
+  /**
+   * 解码动态 Tick Array
+   * @param accountData 账户数据
+   * @param address 账户地址(可选)
+   * @returns DynTickArray
+   */
+  public static decodeDynTickArray(accountData: Buffer, address?: PublicKey): DynTickArray {
+    // DynTickArrayState Header 长度:8 (discriminator) + 208 (struct) = 216 bytes
+
+    if (accountData.length < DYN_TICK_ARRAY_HEADER_LEN) {
+      throw new Error(
+        `Invalid dyn tick array data: expected at least ${DYN_TICK_ARRAY_HEADER_LEN} bytes, got ${accountData.length}`,
+      );
+    }
+
+    // 1. 解析 header
+    const headerData = accountData.slice(0, DYN_TICK_ARRAY_HEADER_LEN);
+    const header = DynTickArrayLayout.decode(headerData);
+
+    // 2. 验证数据长度
+    const expectedSize = DYN_TICK_ARRAY_HEADER_LEN + header.allocTickCount * TICK_STATE_LEN;
+    if (accountData.length !== expectedSize) {
+      throw new Error(`Invalid dyn tick array data size: expected ${expectedSize} bytes, got ${accountData.length}`);
+    }
+
+    // 3. 解析 TickState 数组
+    const ticksData = accountData.slice(DYN_TICK_ARRAY_HEADER_LEN);
+    const ticks: Tick[] = [];
+
+    for (let i = 0; i < header.allocTickCount; i++) {
+      const offset = i * TICK_STATE_LEN;
+      const tickData = ticksData.slice(offset, offset + TICK_STATE_LEN);
+      const tick = TickStateLayout.decode(tickData);
+      ticks.push(tick);
+    }
+
+    // 4. 构建结果
+    return {
+      address: address || PublicKey.default,
+      poolId: header.poolId,
+      startTickIndex: header.startTickIndex,
+      tickOffsetIndex: Array.from(header.tickOffsetIndex),
+      allocTickCount: header.allocTickCount,
+      initializedTickCount: header.initializedTickCount,
+      ticks,
+    };
+  }
+
+  /**
+   * 从容器中获取 Tick State
+   * @param container TickArrayContainer
+   * @param tickIndex Tick 索引
+   * @param tickSpacing Tick 间距
+   * @returns Tick 或 null(如果未分配)
+   */
+  public static getTickStateFromContainer(
+    container: TickArrayContainer,
+    tickIndex: number,
+    tickSpacing: number,
+  ): Tick | null {
+    if (container.type === 'Fixed') {
+      // 固定 Tick Array: 直接数组访问
+      const offset = TickUtils.getTickOffsetInArray(tickIndex, tickSpacing);
+      return container.data.ticks[offset];
+    } else {
+      // 动态 Tick Array: 通过映射表访问
+      return this.getTickStateFromDynArray(container.data, tickIndex, tickSpacing);
+    }
+  }
+
+  /**
+   * 从动态 Tick Array 中获取 Tick State
+   * @param dynTickArray DynTickArray
+   * @param tickIndex Tick 索引
+   * @param tickSpacing Tick 间距
+   * @returns Tick 或 null(如果未分配)
+   */
+  private static getTickStateFromDynArray(
+    dynTickArray: DynTickArray,
+    tickIndex: number,
+    tickSpacing: number,
+  ): Tick | null {
+    // 1. 计算逻辑偏移 (0-59)
+    const logicalOffset = TickUtils.getTickOffsetInArray(tickIndex, tickSpacing);
+
+    // 2. 查映射表
+    const physicalIndexPlusOne = dynTickArray.tickOffsetIndex[logicalOffset];
+
+    // 3. 检查是否已分配
+    if (physicalIndexPlusOne === 0) {
+      return null; // 未分配
+    }
+
+    // 4. 计算实际索引 (position + 1 - 1 = position)
+    const actualIndex = physicalIndexPlusOne - 1;
+
+    // 5. 边界检查
+    if (actualIndex >= dynTickArray.ticks.length) {
+      throw new Error(
+        `Tick state index out of bounds: ${actualIndex} >= ${dynTickArray.ticks.length}. ` +
+          `This indicates corrupted tick array data.`,
+      );
+    }
+
+    return dynTickArray.ticks[actualIndex];
+  }
+
+  /**
+   * Parse tick array account data into a container
+   * Supports both fixed and dynamic tick arrays
+   * @private
+   */
+  public static parseTickArrayContainer(accountData: Buffer, address: PublicKey): TickArrayContainer {
+    const tickArrayType = this.identifyTickArrayType(accountData);
+
+    if (tickArrayType === 'Fixed') {
+      const decoded = TickArrayLayout.decode(accountData);
+      return {
+        type: 'Fixed',
+        data: {
+          address,
+          poolId: decoded.poolId,
+          startTickIndex: decoded.startTickIndex,
+          ticks: decoded.ticks.map((tick: any) => ({
+            tick: tick.tick,
+            liquidityNet: new BN(tick.liquidityNet.toString()),
+            liquidityGross: new BN(tick.liquidityGross.toString()),
+            feeGrowthOutsideX64A: new BN(tick.feeGrowthOutsideX64A.toString()),
+            feeGrowthOutsideX64B: new BN(tick.feeGrowthOutsideX64B.toString()),
+            rewardGrowthsOutsideX64: tick.rewardGrowthsOutsideX64.map((r: any) => new BN(r.toString())),
+          })),
+          initializedTickCount: decoded.initializedTickCount,
+        },
+      };
+    } else {
+      const decoded = this.decodeDynTickArray(accountData, address);
+      return {
+        type: 'Dynamic',
+        data: decoded,
+      };
+    }
+  }
+}

+ 227 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/tickMath.test.ts

@@ -0,0 +1,227 @@
+import { Decimal } from 'decimal.js';
+import { describe, test, expect } from 'vitest';
+
+import { SqrtPriceMath } from './sqrtPriceMath';
+import { TickMath } from './tickMath';
+
+describe('TickMath basic functionality test', () => {
+  test('getTickWithPriceAndTickspacing basic functionality', () => {
+    const priceList = [
+      new Decimal('0.001'),
+      new Decimal('0.01'),
+      new Decimal('0.1'),
+      new Decimal('1'),
+      new Decimal('10'),
+      new Decimal('100'),
+      new Decimal('1000'),
+      new Decimal('10000'),
+    ];
+
+    const tickSpacing = 10;
+    const decimalsA = 6;
+    const decimalsB = 6;
+
+    for (const price of priceList) {
+      const tick = TickMath.getTickWithPriceAndTickspacing(price, tickSpacing, decimalsA, decimalsB);
+
+      expect(Math.abs(tick % tickSpacing)).toBe(0);
+    }
+  });
+
+  test('getPriceFromTick basic functionality', () => {
+    const tick = 100;
+    const decimalsA = 6;
+    const decimalsB = 6;
+
+    const price = TickMath.getPriceFromTick({ tick, decimalsA, decimalsB });
+
+    // Verify that the price is positive
+    expect(price.isPositive()).toBeTruthy();
+  });
+
+  test('getTickAlignedPriceDetails returns the correct structure', () => {
+    const price = new Decimal('1.5');
+    const tickSpacing = 10;
+    const decimalsA = 6;
+    const decimalsB = 6;
+
+    const details = TickMath.getTickAlignedPriceDetails(price, tickSpacing, decimalsA, decimalsB);
+
+    // Verify that the returned object contains all necessary fields
+    expect(details).toHaveProperty('tick');
+    expect(details).toHaveProperty('sqrtPriceX64');
+    expect(details).toHaveProperty('price');
+    expect(details.tick % tickSpacing).toBe(0);
+  });
+});
+
+describe('TickMath rounding behavior test', () => {
+  test('positive tick rounding up', () => {
+    // Select a positive tick that is not a multiple of tickSpacing
+    const originalTick = 15;
+
+    const tickSpacing = 10;
+    const decimalsA = 9;
+    const decimalsB = 6;
+
+    const price = TickMath.getPriceFromTick({ tick: originalTick, decimalsA, decimalsB });
+    const alignedTick = TickMath.getTickWithPriceAndTickspacing(price, tickSpacing, decimalsA, decimalsB);
+
+    expect(alignedTick).toBe(20);
+  });
+
+  test('negative tick rounding down', () => {
+    // Similar to above, but using a negative tick
+    const originalTick = -15;
+
+    const tickSpacing = 10;
+    const decimalsA = 9;
+    const decimalsB = 6;
+
+    const price = TickMath.getPriceFromTick({ tick: originalTick, decimalsA, decimalsB });
+    const alignedTick = TickMath.getTickWithPriceAndTickspacing(price, tickSpacing, decimalsA, decimalsB);
+
+    // 负数应该向下舍入到 -20
+    expect(alignedTick).toBe(-20);
+  });
+});
+
+describe('TickMath boundary condition test', () => {
+  test('extreme price values', () => {
+    const extremePrices = [
+      new Decimal('0.0000001'), // Very small price
+      new Decimal('1000000'), // Very large price
+    ];
+
+    const tickSpacing = 60;
+    const decimalsA = 6;
+    const decimalsB = 6;
+
+    for (const price of extremePrices) {
+      const tick = TickMath.getTickWithPriceAndTickspacing(price, tickSpacing, decimalsA, decimalsB);
+
+      // Verify that the tick is a multiple of tickSpacing
+      expect(Math.abs(tick % tickSpacing)).toBe(0);
+
+      // Verify that there is no exception when converting back from tick to price
+      const details = TickMath.getTickAlignedPriceDetails(price, tickSpacing, decimalsA, decimalsB);
+      expect(details.price.isPositive()).toBeTruthy();
+    }
+  });
+
+  test('different tickSpacing values', () => {
+    const price = new Decimal('1.5');
+    const decimalsA = 6;
+    const decimalsB = 6;
+
+    const tickSpacings = [1, 10, 60, 200];
+
+    for (const spacing of tickSpacings) {
+      const tick = TickMath.getTickWithPriceAndTickspacing(price, spacing, decimalsA, decimalsB);
+
+      // Verify that the tick is a multiple of tickSpacing
+      expect(tick % spacing).toBe(0);
+
+      // Verify that the difference between the original tick and the aligned tick should not exceed tickSpacing
+      const originalTick = SqrtPriceMath.getTickFromPrice(price, decimalsA, decimalsB);
+      expect(Math.abs(tick - originalTick)).toBeLessThan(spacing);
+    }
+  });
+});
+
+describe('TickMath consistency test', () => {
+  test('price -> tick -> price conversion consistency', () => {
+    const testPrices = [new Decimal('0.01'), new Decimal('0.5'), new Decimal('1'), new Decimal('2'), new Decimal('10')];
+
+    const tickSpacing = 10;
+    const decimalsA = 6;
+    const decimalsB = 6;
+
+    for (const initialPrice of testPrices) {
+      // Get the aligned price information
+      const details = TickMath.getTickAlignedPriceDetails(initialPrice, tickSpacing, decimalsA, decimalsB);
+
+      // Convert back from tick to price
+      const recalculatedPrice = TickMath.getPriceFromTick({
+        tick: details.tick,
+        decimalsA,
+        decimalsB,
+      });
+
+      // Verify that the two price values are equal within a reasonable error range
+      expect(details.price.toString()).toBe(recalculatedPrice.toString());
+    }
+  });
+
+  test('different decimals combinations', () => {
+    const price = new Decimal('2.5');
+    const tickSpacing = 60;
+
+    const decimalCombinations = [
+      { decimalsA: 6, decimalsB: 6 },
+      { decimalsA: 8, decimalsB: 6 },
+      { decimalsA: 6, decimalsB: 8 },
+      { decimalsA: 9, decimalsB: 18 },
+    ];
+
+    for (const { decimalsA, decimalsB } of decimalCombinations) {
+      const details = TickMath.getTickAlignedPriceDetails(price, tickSpacing, decimalsA, decimalsB);
+
+      // Verify that the tick is a multiple of tickSpacing
+      expect(Math.abs(details.tick % tickSpacing)).toBe(0);
+
+      // Verify that the magnitude of the adjusted price is similar to the original price
+      const magnitudeDiff = Math.abs(details.price.div(price).toNumber() - 1);
+
+      expect(magnitudeDiff).toBeLessThan(0.01);
+    }
+  });
+});
+
+describe('TickMath and SqrtPriceMath combination test', () => {
+  test('getTickWithPriceAndTickspacing and SqrtPriceMath consistency', () => {
+    const price = new Decimal('1.2345');
+    const tickSpacing = 60;
+    const decimalsA = 6;
+    const decimalsB = 6;
+
+    // Get the original tick
+    const originalTick = SqrtPriceMath.getTickFromPrice(price, decimalsA, decimalsB);
+
+    // Get the aligned tick
+    const alignedTick = TickMath.getTickWithPriceAndTickspacing(price, tickSpacing, decimalsA, decimalsB);
+
+    // Verify that the aligned tick is the closest multiple of tickSpacing to the original tick
+    const expectedAlignedTick =
+      originalTick >= 0
+        ? Math.ceil(originalTick / tickSpacing) * tickSpacing
+        : Math.floor(originalTick / tickSpacing) * tickSpacing;
+
+    expect(alignedTick).toBe(expectedAlignedTick);
+  });
+
+  test('the effect of baseIn parameter on price calculation', () => {
+    const tick = 100;
+    const decimalsA = 6;
+    const decimalsB = 6;
+
+    // baseIn = true
+    const priceBaseIn = TickMath.getPriceFromTick({
+      tick,
+      decimalsA,
+      decimalsB,
+      baseIn: true,
+    });
+
+    // baseIn = false
+    const priceBaseOut = TickMath.getPriceFromTick({
+      tick,
+      decimalsA,
+      decimalsB,
+      baseIn: false,
+    });
+
+    // Verify that the price when baseIn = false should be the reciprocal of the price when baseIn = true
+    expect(priceBaseIn.mul(priceBaseOut).toNumber()).toBeCloseTo(1, 0.00001);
+  });
+});

+ 60 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/tickMath.ts

@@ -0,0 +1,60 @@
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+import { SqrtPriceMath } from './sqrtPriceMath';
+
+export interface TickAlignedPriceDetails {
+  tick: number;
+  sqrtPriceX64: BN;
+  price: Decimal;
+}
+export class TickMath {
+  // Convert price to tick
+  public static getTickWithPriceAndTickspacing(
+    price: Decimal,
+    tickSpacing: number,
+    mintDecimalsA: number,
+    mintDecimalsB: number,
+  ): number {
+    const tick = SqrtPriceMath.getTickFromSqrtPriceX64(
+      SqrtPriceMath.priceToSqrtPriceX64(price, mintDecimalsA, mintDecimalsB),
+    );
+    let result = tick / tickSpacing;
+    if (result < 0) {
+      result = Math.floor(result);
+    } else {
+      result = Math.ceil(result);
+    }
+    return result * tickSpacing;
+  }
+
+  public static getPriceFromTick(params: {
+    tick: number;
+    decimalsA: number;
+    decimalsB: number;
+    baseIn?: boolean;
+  }): Decimal {
+    const { tick, decimalsA, decimalsB, baseIn = true } = params;
+    const tickSqrtPriceX64 = SqrtPriceMath.getSqrtPriceX64FromTick(tick);
+    const tickPrice = SqrtPriceMath.sqrtPriceX64ToPrice(tickSqrtPriceX64, decimalsA, decimalsB);
+
+    return baseIn ? tickPrice : new Decimal(1).div(tickPrice);
+  }
+
+  // Align price to the nearest valid tick and return related data
+  public static getTickAlignedPriceDetails(
+    price: Decimal,
+    tickSpacing: number,
+    mintDecimalsA: number,
+    mintDecimalsB: number,
+  ): TickAlignedPriceDetails {
+    const tick = TickMath.getTickWithPriceAndTickspacing(price, tickSpacing, mintDecimalsA, mintDecimalsB);
+    const sqrtPriceX64 = SqrtPriceMath.getSqrtPriceX64FromTick(tick);
+    const roundedPrice = SqrtPriceMath.sqrtPriceX64ToPrice(sqrtPriceX64, mintDecimalsA, mintDecimalsB);
+    return {
+      tick,
+      sqrtPriceX64,
+      price: roundedPrice,
+    };
+  }
+}

+ 227 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/tickarrayBitmap.ts

@@ -0,0 +1,227 @@
+import BN from 'bn.js';
+
+import { TICK_ARRAY_BITMAP_SIZE, TICK_ARRAY_SIZE } from '../../constants';
+import { MAX_TICK, MIN_TICK } from '../constants';
+import { TickArrayBitmapExtensionType } from '../models';
+
+import { isZero, leadingZeros, leastSignificantBit, mostSignificantBit, trailingZeros } from './binaryUtils';
+import { TickUtils, TickQuery } from './tick';
+
+export class TickArrayBitmap {
+  // Calculate the maximum tick range that a single bitmap can represent
+  public static maxTickInTickarrayBitmap(tickSpacing: number): number {
+    return tickSpacing * TICK_ARRAY_SIZE * TICK_ARRAY_BITMAP_SIZE;
+  }
+
+  // Calculate the bitmap boundaries corresponding to the given tick range
+  public static getBitmapTickBoundary(
+    tickarrayStartIndex: number,
+    tickSpacing: number,
+  ): {
+    minValue: number;
+    maxValue: number;
+  } {
+    const ticksInOneBitmap = this.maxTickInTickarrayBitmap(tickSpacing);
+    let m = Math.floor(Math.abs(tickarrayStartIndex) / ticksInOneBitmap);
+    if (tickarrayStartIndex < 0 && Math.abs(tickarrayStartIndex) % ticksInOneBitmap != 0) m += 1;
+
+    const minValue = ticksInOneBitmap * m;
+
+    return tickarrayStartIndex < 0
+      ? { minValue: -minValue, maxValue: -minValue + ticksInOneBitmap }
+      : { minValue, maxValue: minValue + ticksInOneBitmap };
+  }
+
+  // Calculate the start index of the next initialized TickArray
+  public static nextInitializedTickArrayStartIndex(
+    bitMap: BN,
+    lastTickArrayStartIndex: number,
+    tickSpacing: number,
+    zeroForOne: boolean,
+  ): { isInit: boolean; tickIndex: number } {
+    if (!TickQuery.checkIsValidStartIndex(lastTickArrayStartIndex, tickSpacing))
+      throw Error('nextInitializedTickArrayStartIndex check error');
+
+    const tickBoundary = this.maxTickInTickarrayBitmap(tickSpacing);
+    const nextTickArrayStartIndex = zeroForOne
+      ? lastTickArrayStartIndex - TickQuery.tickCount(tickSpacing)
+      : lastTickArrayStartIndex + TickQuery.tickCount(tickSpacing);
+
+    if (nextTickArrayStartIndex < -tickBoundary || nextTickArrayStartIndex >= tickBoundary) {
+      return { isInit: false, tickIndex: lastTickArrayStartIndex };
+    }
+
+    const multiplier = tickSpacing * TICK_ARRAY_SIZE;
+    let compressed = nextTickArrayStartIndex / multiplier + 512;
+
+    if (nextTickArrayStartIndex < 0 && nextTickArrayStartIndex % multiplier != 0) {
+      compressed--;
+    }
+
+    const bitPos = Math.abs(compressed);
+
+    if (zeroForOne) {
+      const offsetBitMap = bitMap.shln(1024 - bitPos - 1);
+      const nextBit = mostSignificantBit(1024, offsetBitMap);
+      if (nextBit !== null) {
+        const nextArrayStartIndex = (bitPos - nextBit - 512) * multiplier;
+        return { isInit: true, tickIndex: nextArrayStartIndex };
+      } else {
+        return { isInit: false, tickIndex: -tickBoundary };
+      }
+    } else {
+      const offsetBitMap = bitMap.shrn(bitPos);
+      const nextBit = leastSignificantBit(1024, offsetBitMap);
+      if (nextBit !== null) {
+        const nextArrayStartIndex = (bitPos + nextBit - 512) * multiplier;
+        return { isInit: true, tickIndex: nextArrayStartIndex };
+      } else {
+        return { isInit: false, tickIndex: tickBoundary - TickQuery.tickCount(tickSpacing) };
+      }
+    }
+  }
+}
+
+export class TickArrayBitmapExtensionUtils {
+  public static getBitmapOffset(tickIndex: number, tickSpacing: number): number {
+    if (!TickQuery.checkIsValidStartIndex(tickIndex, tickSpacing)) {
+      throw new Error('No enough initialized tickArray');
+    }
+
+    const { positiveTickBoundary, negativeTickBoundary } = this.extensionTickBoundary(tickSpacing);
+
+    if (tickIndex >= negativeTickBoundary && tickIndex < positiveTickBoundary) {
+      throw Error('checkExtensionBoundary -> InvalidTickArrayBoundary');
+    }
+
+    const ticksInOneBitmap = TickArrayBitmap.maxTickInTickarrayBitmap(tickSpacing);
+    let offset = Math.floor(Math.abs(tickIndex) / ticksInOneBitmap) - 1;
+
+    if (tickIndex < 0 && Math.abs(tickIndex) % ticksInOneBitmap === 0) offset--;
+    return offset;
+  }
+
+  public static getBitmap(
+    tickIndex: number,
+    tickSpacing: number,
+    tickArrayBitmapExtension: TickArrayBitmapExtensionType,
+  ): { offset: number; tickarrayBitmap: BN[] } {
+    const offset = this.getBitmapOffset(tickIndex, tickSpacing);
+    if (tickIndex < 0) {
+      return { offset, tickarrayBitmap: tickArrayBitmapExtension.negativeTickArrayBitmap[offset] };
+    } else {
+      return { offset, tickarrayBitmap: tickArrayBitmapExtension.positiveTickArrayBitmap[offset] };
+    }
+  }
+
+  // Calculate the boundary values of the extended bitmap
+  public static extensionTickBoundary(tickSpacing: number): {
+    positiveTickBoundary: number;
+    negativeTickBoundary: number;
+  } {
+    const positiveTickBoundary = TickArrayBitmap.maxTickInTickarrayBitmap(tickSpacing);
+
+    const negativeTickBoundary = -positiveTickBoundary;
+
+    if (MAX_TICK <= positiveTickBoundary)
+      throw Error(`extensionTickBoundary check error: ${MAX_TICK}, ${positiveTickBoundary}`);
+    if (negativeTickBoundary <= MIN_TICK)
+      throw Error(`extensionTickBoundary check error: ${negativeTickBoundary}, ${MIN_TICK}`);
+
+    return { positiveTickBoundary, negativeTickBoundary };
+  }
+
+  // Check if a specific TickArray has been initialized
+  public static checkTickArrayIsInit(
+    tickArrayStartIndex: number,
+    tickSpacing: number,
+    tickArrayBitmapExtension: TickArrayBitmapExtensionType,
+  ): { isInitialized: boolean; startIndex: number } {
+    const { tickarrayBitmap } = this.getBitmap(tickArrayStartIndex, tickSpacing, tickArrayBitmapExtension);
+
+    const tickArrayOffsetInBitmap = this.tickArrayOffsetInBitmap(tickArrayStartIndex, tickSpacing);
+
+    return {
+      isInitialized: TickUtils.mergeTickArrayBitmap(tickarrayBitmap).testn(tickArrayOffsetInBitmap),
+      startIndex: tickArrayStartIndex,
+    };
+  }
+
+  // Find the start index of the next initialized TickArray from a single bitmap
+  public static nextInitializedTickArrayFromOneBitmap(
+    lastTickArrayStartIndex: number,
+    tickSpacing: number,
+    zeroForOne: boolean,
+    tickArrayBitmapExtension: TickArrayBitmapExtensionType,
+  ): {
+    isInit: boolean;
+    tickIndex: number;
+  } {
+    const multiplier = TickQuery.tickCount(tickSpacing);
+    const nextTickArrayStartIndex = zeroForOne
+      ? lastTickArrayStartIndex - multiplier
+      : lastTickArrayStartIndex + multiplier;
+    const { tickarrayBitmap } = this.getBitmap(nextTickArrayStartIndex, tickSpacing, tickArrayBitmapExtension);
+
+    return this.nextInitializedTickArrayInBitmap(tickarrayBitmap, nextTickArrayStartIndex, tickSpacing, zeroForOne);
+  }
+
+  // Find the start index of the next initialized TickArray in the bitmap
+  public static nextInitializedTickArrayInBitmap(
+    tickarrayBitmap: BN[],
+    nextTickArrayStartIndex: number,
+    tickSpacing: number,
+    zeroForOne: boolean,
+  ): {
+    isInit: boolean;
+    tickIndex: number;
+  } {
+    const { minValue: bitmapMinTickBoundary, maxValue: bitmapMaxTickBoundary } = TickArrayBitmap.getBitmapTickBoundary(
+      nextTickArrayStartIndex,
+      tickSpacing,
+    );
+
+    const tickArrayOffsetInBitmap = this.tickArrayOffsetInBitmap(nextTickArrayStartIndex, tickSpacing);
+    if (zeroForOne) {
+      // tick from upper to lower
+      // find from highter bits to lower bits
+      const offsetBitMap = TickUtils.mergeTickArrayBitmap(tickarrayBitmap).shln(
+        TICK_ARRAY_BITMAP_SIZE - 1 - tickArrayOffsetInBitmap,
+      );
+
+      const nextBit = isZero(512, offsetBitMap) ? null : leadingZeros(512, offsetBitMap);
+
+      if (nextBit !== null) {
+        const nextArrayStartIndex = nextTickArrayStartIndex - nextBit * TickQuery.tickCount(tickSpacing);
+        return { isInit: true, tickIndex: nextArrayStartIndex };
+      } else {
+        // not found til to the end
+        return { isInit: false, tickIndex: bitmapMinTickBoundary };
+      }
+    } else {
+      // tick from lower to upper
+      // find from lower bits to highter bits
+      const offsetBitMap = TickUtils.mergeTickArrayBitmap(tickarrayBitmap).shrn(tickArrayOffsetInBitmap);
+
+      const nextBit = isZero(512, offsetBitMap) ? null : trailingZeros(512, offsetBitMap);
+
+      if (nextBit !== null) {
+        const nextArrayStartIndex = nextTickArrayStartIndex + nextBit * TickQuery.tickCount(tickSpacing);
+        return { isInit: true, tickIndex: nextArrayStartIndex };
+      } else {
+        // not found til to the end
+        return { isInit: false, tickIndex: bitmapMaxTickBoundary - TickQuery.tickCount(tickSpacing) };
+      }
+    }
+  }
+
+  // Calculate the offset position (index) of a TickArray within its belonging bitmap
+  public static tickArrayOffsetInBitmap(tickArrayStartIndex: number, tickSpacing: number): number {
+    const m = Math.abs(tickArrayStartIndex) % TickArrayBitmap.maxTickInTickarrayBitmap(tickSpacing);
+    let tickArrayOffsetInBitmap = Math.floor(m / TickQuery.tickCount(tickSpacing));
+    if (tickArrayStartIndex < 0 && m != 0) {
+      tickArrayOffsetInBitmap = TICK_ARRAY_BITMAP_SIZE - tickArrayOffsetInBitmap;
+    }
+    return tickArrayOffsetInBitmap;
+  }
+}

+ 97 - 0
src/lib/byreal-clmm-sdk/src/instructions/utils/transfer.ts

@@ -0,0 +1,97 @@
+import { TransferFee } from '@solana/spl-token';
+import { EpochInfo } from '@solana/web3.js';
+import BN from 'bn.js';
+
+import { TransferFeeDataBaseType, IGetTransferAmountFee } from '../models';
+
+const POINT = 10_000;
+
+// This function is a specialized utility function for calculating transfer fees and final amounts for Token-2022 token transfers. It handles the transfer fee feature introduced in the Solana Token-2022 program, calculating precise transfer amounts and fees based on the current epoch and fee configuration
+export function getTransferAmountFee(
+  amount: BN,
+  _feeConfig: TransferFeeDataBaseType | undefined,
+  epochInfo: EpochInfo,
+  addFee: boolean,
+): IGetTransferAmountFee {
+  if (_feeConfig === undefined) {
+    return {
+      amount,
+      fee: undefined,
+      expirationTime: undefined,
+    };
+  }
+  const feeConfig = {
+    ..._feeConfig,
+    olderTransferFee: {
+      epoch: BigInt(_feeConfig.olderTransferFee.epoch),
+      maximumFee: BigInt(_feeConfig.olderTransferFee.maximumFee),
+      transferFeeBasisPoints: _feeConfig.olderTransferFee.transferFeeBasisPoints,
+    },
+    newerTransferFee: {
+      epoch: BigInt(_feeConfig.newerTransferFee.epoch),
+      maximumFee: BigInt(_feeConfig.newerTransferFee.maximumFee),
+      transferFeeBasisPoints: _feeConfig.newerTransferFee.transferFeeBasisPoints,
+    },
+  };
+
+  const nowFeeConfig: TransferFee =
+    epochInfo.epoch < feeConfig.newerTransferFee.epoch ? feeConfig.olderTransferFee : feeConfig.newerTransferFee;
+  const maxFee = new BN(nowFeeConfig.maximumFee.toString());
+  const expirationTime: number | undefined =
+    epochInfo.epoch < feeConfig.newerTransferFee.epoch
+      ? ((Number(feeConfig.newerTransferFee.epoch) * epochInfo.slotsInEpoch - epochInfo.absoluteSlot) * 400) / 1000
+      : undefined;
+
+  if (addFee) {
+    if (nowFeeConfig.transferFeeBasisPoints === POINT) {
+      const nowMaxFee = new BN(nowFeeConfig.maximumFee.toString());
+      return {
+        amount: amount.add(nowMaxFee),
+        fee: nowMaxFee,
+        expirationTime,
+      };
+    } else {
+      const _TAmount = BNDivCeil(amount.mul(new BN(POINT)), new BN(POINT - nowFeeConfig.transferFeeBasisPoints));
+
+      const nowMaxFee = new BN(nowFeeConfig.maximumFee.toString());
+      const TAmount = _TAmount.sub(amount).gt(nowMaxFee) ? amount.add(nowMaxFee) : _TAmount;
+
+      const _fee = BNDivCeil(TAmount.mul(new BN(nowFeeConfig.transferFeeBasisPoints)), new BN(POINT));
+      const fee = _fee.gt(maxFee) ? maxFee : _fee;
+      return {
+        amount: TAmount,
+        fee,
+        expirationTime,
+      };
+    }
+  } else {
+    const _fee = BNDivCeil(amount.mul(new BN(nowFeeConfig.transferFeeBasisPoints)), new BN(POINT));
+    const fee = _fee.gt(maxFee) ? maxFee : _fee;
+
+    return {
+      amount,
+      fee,
+      expirationTime,
+    };
+  }
+}
+
+export function minExpirationTime(
+  expirationTime1: number | undefined,
+  expirationTime2: number | undefined,
+): number | undefined {
+  if (expirationTime1 === undefined) return expirationTime2;
+  if (expirationTime2 === undefined) return expirationTime1;
+
+  return Math.min(expirationTime1, expirationTime2);
+}
+
+export function BNDivCeil(bn1: BN, bn2: BN): BN {
+  const { div, mod } = bn1.divmod(bn2);
+
+  if (mod.gt(new BN(0))) {
+    return div.add(new BN(1));
+  } else {
+    return div;
+  }
+}

+ 14 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/01_get_raw_position_info_list.ts

@@ -0,0 +1,14 @@
+/**
+ * Get the list of positions for a specified user (on-chain way)
+ */
+
+import { userAddress, chain } from './config.js';
+
+async function main(): Promise<void> {
+  // Get the list of positions for a specified user
+  console.log('userAddress =>', userAddress.toBase58());
+  const positionList = await chain.getRawPositionInfoListByUserAddress(userAddress);
+  console.log(positionList);
+}
+
+main();

+ 17 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/02_get_raw_position_info.ts

@@ -0,0 +1,17 @@
+/**
+ * Get the position information corresponding to the NFT mint address (on-chain way)
+ */
+import { PublicKey } from '@solana/web3.js';
+
+import { chain } from './config.js';
+
+async function main(): Promise<void> {
+  // Change to your own NFT mint address
+  const nftMint = new PublicKey('2GfNC4r784awMFDW2d1RDDebTzStTY6aMr78K5Q6DGyM');
+
+  // Get the position information corresponding to the NFT mint address
+  const positionInfo = await chain.getRawPositionInfoByNftMint(nftMint);
+  console.log(positionInfo);
+}
+
+main();

+ 13 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/03_get_raw_pool_info.ts

@@ -0,0 +1,13 @@
+/**
+ * Get the on-chain information of the specified pool
+ */
+
+import { chain, PoolAddress } from './config.js';
+
+async function main(): Promise<void> {
+  // Get the on-chain information of the specified pool
+  const poolInfo = await chain.getRawPoolInfoByPoolId(PoolAddress.SOL_USDC);
+  console.log(poolInfo);
+}
+
+main();

+ 42 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/04_get_position_info_list.ts

@@ -0,0 +1,42 @@
+/**
+ * Get the position information corresponding to the NFT mint address
+ */
+
+import { chain, userAddress } from './config.js';
+
+async function main(): Promise<void> {
+  console.log('userAddress ==>', userAddress.toBase58());
+
+  // Get the list of positions for a specified user
+  const positionList = await chain.getRawPositionInfoListByUserAddress(userAddress);
+
+  const nftMints = positionList.map((position) => position.nftMint);
+
+  for (const nftMint of nftMints) {
+    const positionInfo = await chain.getPositionInfoByNftMint(nftMint);
+
+    if (!positionInfo) continue;
+
+    const { uiPriceLower, uiPriceUpper, tokenA, tokenB } = positionInfo;
+
+    console.log(`\n=========== Position information ===========`);
+    console.log(`NFT mint address: ${nftMint.toBase58()}`);
+
+    console.log(`Price range: ${uiPriceLower} - ${uiPriceUpper}`);
+
+    console.log(`Liquidity assets:`);
+    console.log(
+      `  TokenA(${positionInfo.tokenA.address.toBase58().slice(0, 4)}...${positionInfo.tokenA.address
+        .toBase58()
+        .slice(-4)}):`
+    );
+    console.log(`    • Amount: ${tokenA.uiAmount}`);
+    console.log(`    • Fee income: ${tokenA.uiFeeAmount}`);
+
+    console.log(`  TokenB(${tokenB.address.toBase58().slice(0, 4)}...${tokenB.address.toBase58().slice(-4)}):`);
+    console.log(`    • Amount: ${tokenB.uiAmount}`);
+    console.log(`    • Fee income: ${tokenB.uiFeeAmount}`);
+  }
+}
+
+main();

+ 23 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/05_align_price.ts

@@ -0,0 +1,23 @@
+/**
+ * Round the price to the price of the corresponding tick (used when creating a position)
+ */
+
+import { Decimal } from 'decimal.js';
+
+import { chain, PoolAddress } from './config.js';
+
+async function main(): Promise<void> {
+  const poolInfo = await chain.getRawPoolInfoByPoolId(PoolAddress.SOL_USDC);
+
+  const userInputPriceLower = 120;
+  const userInputPriceUpper = 160;
+
+  // When creating a position, round the price to the price of the corresponding tick
+  const roundPriceLower = chain.alignPriceToTickPrice(new Decimal(userInputPriceLower), poolInfo);
+  const roundPriceUpper = chain.alignPriceToTickPrice(new Decimal(userInputPriceUpper), poolInfo);
+
+  console.log(`User input price: ${userInputPriceLower} - ${userInputPriceUpper}`);
+  console.log(`Aligned to tick price: ${roundPriceLower} - ${roundPriceUpper}`);
+}
+
+main();

+ 32 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/06_get_amount_from_another_amount.ts

@@ -0,0 +1,32 @@
+/**
+ * Round the price to the price of the corresponding tick (used when creating a position)
+ */
+
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+import { chain, PoolAddress } from './config.js';
+
+async function main(): Promise<void> {
+  const poolInfo = await chain.getRawPoolInfoByPoolId(PoolAddress.SOL_USDC);
+
+  const userInputPriceLower = 120;
+  const userInputPriceUpper = 160;
+  const userInputAmountA = new BN(1000000000); // 1 SOL
+
+  // Calculate the amount of tokenB needed to be invested in the specified price range after the specified tokenA amount has been invested
+  const amountB = chain.getAmountBFromAmountA({
+    priceLower: new Decimal(userInputPriceLower),
+    priceUpper: new Decimal(userInputPriceUpper),
+    amountA: userInputAmountA,
+    poolInfo,
+  });
+
+  // Tips: This is the amount of tokens that will be accurately invested in the vault pool;
+  console.log(
+    'Estimated amount of tokenB to be invested =>',
+    Number(amountB.toString()) / 10 ** poolInfo.mintDecimalsB
+  );
+}
+
+main();

+ 83 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/07_create_position_baseA.ts

@@ -0,0 +1,83 @@
+/**
+ * Create a position
+ */
+
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+import { SignerCallback } from '../client/chain/models.js';
+import { TickMath } from '../index.js';
+
+import { userKeypair, userAddress, chain, PoolAddress } from './config.js';
+
+async function main(): Promise<void> {
+  // step 1: User selects the pool
+  const poolInfo = await chain.getRawPoolInfoByPoolId(PoolAddress.USDC_USDT);
+
+  console.log('========= step 1: Select the pool =========');
+  console.log('Selected pool address:', poolInfo.poolId.toBase58());
+
+  // step 2: User inputs the price range
+  const userStartPrice = '0.998';
+  const userEndPrice = '1.002';
+
+  // Calculate the accurate tick price, and show it to the user
+  const priceInTickLower = TickMath.getTickAlignedPriceDetails(
+    new Decimal(userStartPrice),
+    poolInfo.tickSpacing,
+    poolInfo.mintDecimalsA,
+    poolInfo.mintDecimalsB
+  );
+  const priceInTickUpper = TickMath.getTickAlignedPriceDetails(
+    new Decimal(userEndPrice),
+    poolInfo.tickSpacing,
+    poolInfo.mintDecimalsA,
+    poolInfo.mintDecimalsB
+  );
+
+  console.log('========= step 2: User inputs the price range =========');
+  console.log(`User input price range: ${userStartPrice} - ${userEndPrice}`);
+  console.log(`Accurate price range: ${priceInTickLower.price.toNumber()} - ${priceInTickUpper.price.toNumber()}`);
+  console.log(`Accurate price tick range: ${priceInTickLower.tick} - ${priceInTickUpper.tick}`);
+
+  // step 3: User inputs the amount of TokenB and the token type
+  const base = 'MintA';
+  const baseAmount = new BN(2 * 10 ** poolInfo.mintDecimalsA);
+
+  // Calculate the amount of TokenA needed
+  const amountB = chain.getAmountBFromAmountA({
+    priceLower: priceInTickLower.price,
+    priceUpper: priceInTickUpper.price,
+    amountA: baseAmount,
+    poolInfo,
+  });
+
+  // Add a 2% slippage
+  const amountBWithSlippage = new BN(amountB).mul(new BN(10000 * (1 + 0.02))).div(new BN(10000));
+
+  console.log('========= step 3: User inputs the amount of TokenB and the token type =========');
+  console.log('Amount of TokenA to be invested =>', Number(baseAmount.toString()) / 10 ** poolInfo.mintDecimalsA);
+  console.log('Estimated amount of TokenB needed =>', Number(amountB.toString()) / 10 ** poolInfo.mintDecimalsB);
+
+  // Signer callback, later this can be implemented with a wallet plugin
+  const signerCallback: SignerCallback = async (tx) => {
+    tx.sign([userKeypair]);
+    return tx;
+  };
+
+  console.log('======= step 4: After confirming that there is no error, start creating a position =======');
+  const txid = await chain.createPosition({
+    userAddress: userAddress,
+    poolInfo,
+    tickLower: priceInTickLower.tick,
+    tickUpper: priceInTickUpper.tick,
+    base,
+    baseAmount,
+    otherAmountMax: amountBWithSlippage,
+    signerCallback,
+  });
+
+  console.log('Create position successfully, txid:', txid);
+}
+
+main();

+ 82 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/07_create_position_baseB.ts

@@ -0,0 +1,82 @@
+/**
+ * Create a position
+ */
+
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+import { SignerCallback } from '../client/chain/models.js';
+import { TickMath } from '../index.js';
+
+import { userKeypair, userAddress, PoolAddress, chain } from './config.js';
+
+async function main(): Promise<void> {
+  // step 1: User selects the pool
+  const poolInfo = await chain.getRawPoolInfoByPoolId(PoolAddress.USDC_USDT);
+
+  console.log('========= step 1: Select the pool =========');
+  console.log('Selected pool address:', poolInfo.poolId.toBase58());
+
+  // step 2: User inputs the price range
+  const userStartPrice = '0.998';
+  const userEndPrice = '1.002';
+
+  // Calculate the accurate tick price, and show it to the user
+  const priceInTickLower = TickMath.getTickAlignedPriceDetails(
+    new Decimal(userStartPrice),
+    poolInfo.tickSpacing,
+    poolInfo.mintDecimalsA,
+    poolInfo.mintDecimalsB
+  );
+  const priceInTickUpper = TickMath.getTickAlignedPriceDetails(
+    new Decimal(userEndPrice),
+    poolInfo.tickSpacing,
+    poolInfo.mintDecimalsA,
+    poolInfo.mintDecimalsB
+  );
+
+  console.log('========= step 2: User inputs the price range =========');
+  console.log(`User input price range: ${userStartPrice} - ${userEndPrice}`);
+  console.log(`Accurate price range: ${priceInTickLower.price.toNumber()} - ${priceInTickUpper.price.toNumber()}`);
+
+  // step 3: User inputs the amount of TokenB and the token type
+  const base = 'MintB';
+  const baseAmount = new BN(2 * 10 ** poolInfo.mintDecimalsB);
+
+  // Calculate the amount of TokenA needed
+  const amountA = chain.getAmountAFromAmountB({
+    priceLower: priceInTickLower.price,
+    priceUpper: priceInTickUpper.price,
+    amountB: baseAmount,
+    poolInfo,
+  });
+
+  // Add a 2% slippage
+  const amountAWithSlippage = new BN(amountA).mul(new BN(10000 * (1 + 0.02))).div(new BN(10000));
+
+  console.log('========= step 3: User inputs the amount of TokenB and the token type =========');
+  console.log('Amount of TokenB to be invested =>', Number(baseAmount.toString()) / 10 ** poolInfo.mintDecimalsB);
+  console.log('Estimated amount of TokenA needed =>', Number(amountA.toString()) / 10 ** poolInfo.mintDecimalsA);
+
+  // Signer callback, later this can be implemented with a wallet plugin
+  const signerCallback: SignerCallback = async (tx) => {
+    tx.sign([userKeypair]);
+    return tx;
+  };
+
+  console.log('======= step 4: After confirming that there is no error, start creating a position =======');
+  const txid = await chain.createPosition({
+    userAddress: userAddress,
+    poolInfo,
+    tickLower: priceInTickLower.tick,
+    tickUpper: priceInTickUpper.tick,
+    base,
+    baseAmount,
+    otherAmountMax: amountAWithSlippage,
+    signerCallback,
+  });
+
+  console.log('Create position successfully, txid:', txid);
+}
+
+main();

+ 40 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/08_decrease_liquidity.ts

@@ -0,0 +1,40 @@
+/**
+ * Decrease liquidity proportionally
+ */
+
+import { PublicKey } from '@solana/web3.js';
+import BN from 'bn.js';
+
+import { chain, signerCallback, userAddress } from './config.js';
+
+async function main(): Promise<void> {
+  // Change to your own NFT mint address
+  const nftMint = new PublicKey('3eunC8kdMEwBRQHd91cyQ36FVQFXDuicGk7xuVS6hFfk');
+
+  const positionInfo = await chain.getRawPositionInfoByNftMint(nftMint);
+
+  if (!positionInfo) {
+    throw new Error('Position not found');
+  }
+
+  const { liquidity } = positionInfo;
+
+  const decreasePercentage = 50; // Decrease 50% of the liquidity
+
+  const liquidityToDecrease = liquidity.mul(new BN(decreasePercentage)).div(new BN(100));
+
+  console.log('Current liquidity amount:', liquidity.toString());
+  console.log('Liquidity to decrease:', liquidityToDecrease.toString());
+
+  const txid = await chain.decreaseLiquidity({
+    userAddress,
+    nftMint,
+    // Decrease half of the liquidity
+    liquidity: liquidityToDecrease,
+    signerCallback,
+  });
+
+  console.log('Decrease liquidity successfully, txid:', txid);
+}
+
+main();

+ 82 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/09_create_position_side_tokenA.ts

@@ -0,0 +1,82 @@
+/**
+ * Create a position
+ */
+
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+import { SignerCallback } from '../client/chain/models.js';
+import { TickMath } from '../index.js';
+
+import { userKeypair, userAddress, PoolAddress, chain } from './config.js';
+
+async function main(): Promise<void> {
+  // step 1: User selects the pool
+  const poolInfo = await chain.getRawPoolInfoByPoolId(PoolAddress.USDC_USDT);
+
+  console.log('========= step 1: Select the pool =========');
+  console.log('Selected pool address:', poolInfo.poolId.toBase58());
+
+  // step 2: User inputs the price range
+  const userStartPrice = '1.1';
+  const userEndPrice = '1.2';
+
+  // Calculate the accurate tick price, and show it to the user
+  const priceInTickLower = TickMath.getTickAlignedPriceDetails(
+    new Decimal(userStartPrice),
+    poolInfo.tickSpacing,
+    poolInfo.mintDecimalsA,
+    poolInfo.mintDecimalsB
+  );
+  const priceInTickUpper = TickMath.getTickAlignedPriceDetails(
+    new Decimal(userEndPrice),
+    poolInfo.tickSpacing,
+    poolInfo.mintDecimalsA,
+    poolInfo.mintDecimalsB
+  );
+
+  console.log('========= step 2: User inputs the price range =========');
+  console.log(`User input price range: ${userStartPrice} - ${userEndPrice}`);
+  console.log(`Accurate price range: ${priceInTickLower.price.toNumber()} - ${priceInTickUpper.price.toNumber()}`);
+
+  // step 3: User inputs the amount of TokenA and the token type
+  const base = 'MintA';
+  const baseAmount = new BN(2 * 10 ** poolInfo.mintDecimalsA);
+
+  // Calculate the amount of TokenB needed
+  const amountB = chain.getAmountBFromAmountA({
+    priceLower: priceInTickLower.price,
+    priceUpper: priceInTickUpper.price,
+    amountA: baseAmount,
+    poolInfo,
+  });
+
+  // Add a 2% slippage
+  const amountBWithSlippage = new BN(amountB).mul(new BN(10000 * (1 + 0.02))).div(new BN(10000));
+
+  console.log('========= step 3: User inputs the amount of TokenA and the token type =========');
+  console.log('Amount of TokenA to be invested =>', Number(baseAmount.toString()) / 10 ** poolInfo.mintDecimalsA);
+  console.log('Estimated amount of TokenB needed =>', Number(amountB.toString()) / 10 ** poolInfo.mintDecimalsB);
+
+  // Signer callback, later this can be implemented with a wallet plugin
+  const signerCallback: SignerCallback = async (tx) => {
+    tx.sign([userKeypair]);
+    return tx;
+  };
+
+  console.log('======= step 4: After confirming that there is no error, start creating a position =======');
+  const txid = await chain.createPosition({
+    userAddress: userAddress,
+    poolInfo,
+    tickLower: priceInTickLower.tick,
+    tickUpper: priceInTickUpper.tick,
+    base,
+    baseAmount,
+    otherAmountMax: amountBWithSlippage,
+    signerCallback,
+  });
+
+  console.log('Create position successfully, txid:', txid);
+}
+
+main();

+ 82 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/10_create_position_side_tokenB.ts

@@ -0,0 +1,82 @@
+/**
+ * Create a position
+ */
+
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+import { SignerCallback } from '../client/chain/models.js';
+import { TickMath } from '../index.js';
+
+import { userKeypair, userAddress, PoolAddress, chain } from './config.js';
+
+async function main(): Promise<void> {
+  // step 1: User selects the pool
+  const poolInfo = await chain.getRawPoolInfoByPoolId(PoolAddress.USDC_USDT);
+
+  console.log('========= step 1: Select the pool =========');
+  console.log('Selected pool address:', poolInfo.poolId.toBase58());
+
+  // step 2: User inputs the price range
+  const userStartPrice = '0.8';
+  const userEndPrice = '0.9';
+
+  // Calculate the accurate tick price, and show it to the user
+  const priceInTickLower = TickMath.getTickAlignedPriceDetails(
+    new Decimal(userStartPrice),
+    poolInfo.tickSpacing,
+    poolInfo.mintDecimalsA,
+    poolInfo.mintDecimalsB
+  );
+  const priceInTickUpper = TickMath.getTickAlignedPriceDetails(
+    new Decimal(userEndPrice),
+    poolInfo.tickSpacing,
+    poolInfo.mintDecimalsA,
+    poolInfo.mintDecimalsB
+  );
+
+  console.log('========= step 2: User inputs the price range =========');
+  console.log(`User input price range: ${userStartPrice} - ${userEndPrice}`);
+  console.log(`Accurate price range: ${priceInTickLower.price.toNumber()} - ${priceInTickUpper.price.toNumber()}`);
+
+  // step 3: User inputs the amount of TokenB and the token type
+  const base = 'MintB';
+  const baseAmount = new BN(2 * 10 ** poolInfo.mintDecimalsB);
+
+  // Calculate the amount of TokenA needed
+  const amountA = chain.getAmountAFromAmountB({
+    priceLower: priceInTickLower.price,
+    priceUpper: priceInTickUpper.price,
+    amountB: baseAmount,
+    poolInfo,
+  });
+
+  // Add a 2% slippage
+  const amountAWithSlippage = new BN(amountA).mul(new BN(10000 * (1 + 0.02))).div(new BN(10000));
+
+  console.log('========= step 3: User inputs the amount of TokenB and the token type =========');
+  console.log('Amount of TokenB to be invested =>', Number(baseAmount.toString()) / 10 ** poolInfo.mintDecimalsB);
+  console.log('Estimated amount of TokenA needed =>', Number(amountA.toString()) / 10 ** poolInfo.mintDecimalsA);
+
+  // Signer callback, later this can be implemented with a wallet plugin
+  const signerCallback: SignerCallback = async (tx) => {
+    tx.sign([userKeypair]);
+    return tx;
+  };
+
+  console.log('======= step 4: After confirming that there is no error, start creating a position =======');
+  const txid = await chain.createPosition({
+    userAddress: userAddress,
+    poolInfo,
+    tickLower: priceInTickLower.tick,
+    tickUpper: priceInTickUpper.tick,
+    base,
+    baseAmount,
+    otherAmountMax: amountAWithSlippage,
+    signerCallback,
+  });
+
+  console.log('Create position successfully, txid:', txid);
+}
+
+main();

+ 69 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/11_decrease_liquidity_simulate_amount.ts

@@ -0,0 +1,69 @@
+/**
+ * Decrease liquidity proportionally, and display the estimated tokenA and tokenB amounts
+ */
+
+import { PublicKey } from '@solana/web3.js';
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+import { LiquidityMath, SqrtPriceMath } from '../index.js';
+
+import { chain } from './config.js';
+
+async function main(): Promise<void> {
+  // Change to your own NFT mint address
+  const nftMint = new PublicKey('CjouQkvVP5XkABWYQYzCAJMpB3g8yJhADtRcRtEZTj8M');
+
+  const positionInfo = await chain.getRawPositionInfoByNftMint(nftMint);
+
+  if (!positionInfo) {
+    throw new Error('Position not found');
+  }
+
+  const { liquidity, tickLower, tickUpper } = positionInfo;
+
+  const decreasePercentage = 50; // decrease 50% of the liquidity
+
+  const liquidityToDecrease = liquidity.mul(new BN(decreasePercentage)).div(new BN(100));
+
+  console.log('Current liquidity amount:', liquidity.toString());
+  console.log('Liquidity to decrease:', liquidityToDecrease.toString());
+
+  const sqrtPriceLowerX64 = SqrtPriceMath.getSqrtPriceX64FromTick(tickLower);
+  const sqrtPriceUpperX64 = SqrtPriceMath.getSqrtPriceX64FromTick(tickUpper);
+
+  const poolInfo = await chain.getRawPoolInfoByPoolId(positionInfo.poolId);
+
+  const amounts = LiquidityMath.getAmountsFromLiquidity(
+    poolInfo.sqrtPriceX64,
+    sqrtPriceLowerX64,
+    sqrtPriceUpperX64,
+    liquidityToDecrease,
+    true
+  );
+
+  console.log(
+    'Estimated tokenA amount:',
+    new Decimal(amounts.amountA.toString())
+      .div(new Decimal(10 ** poolInfo.mintDecimalsA))
+      .toFixed(poolInfo.mintDecimalsA)
+  );
+  console.log(
+    'Estimated tokenB amount:',
+    new Decimal(amounts.amountB.toString())
+      .div(new Decimal(10 ** poolInfo.mintDecimalsB))
+      .toFixed(poolInfo.mintDecimalsB)
+  );
+
+  // const txid = chain.decreaseLiquidity({
+  //   userAddress,
+  //   nftMint,
+  //   // Decrease half of the liquidity
+  //   liquidity: liquidityToDecrease,
+  //   signerCallback,
+  // });
+
+  // console.log(txid);
+}
+
+main();

+ 101 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/12_decrease_liquidity_simulate_amount_2.ts

@@ -0,0 +1,101 @@
+/**
+ * User manually inputs the amount of token to be decreased, and calculates the amount of token to be decreased on the other side
+ */
+
+import { PublicKey } from '@solana/web3.js';
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+import { LiquidityMath, SqrtPriceMath } from '../index.js';
+
+import { chain } from './config.js';
+
+async function main(): Promise<void> {
+  // Change to your own NFT mint address
+  const nftMint = new PublicKey('CjouQkvVP5XkABWYQYzCAJMpB3g8yJhADtRcRtEZTj8M');
+
+  const positionInfo = await chain.getRawPositionInfoByNftMint(nftMint);
+
+  if (!positionInfo) {
+    throw new Error('Position not found');
+  }
+
+  const { liquidity, tickLower, tickUpper } = positionInfo;
+
+  const sqrtPriceLowerX64 = SqrtPriceMath.getSqrtPriceX64FromTick(tickLower);
+  const sqrtPriceUpperX64 = SqrtPriceMath.getSqrtPriceX64FromTick(tickUpper);
+
+  const poolInfo = await chain.getRawPoolInfoByPoolId(positionInfo.poolId);
+
+  const amounts = LiquidityMath.getAmountsFromLiquidity(
+    poolInfo.sqrtPriceX64,
+    sqrtPriceLowerX64,
+    sqrtPriceUpperX64,
+    liquidity,
+    true
+  );
+
+  console.log('Amount of tokenA in the position:', new Decimal(amounts.amountA.toString()));
+  console.log('Amount of tokenB in the position:', new Decimal(amounts.amountB.toString()));
+
+  console.log('==== User manually inputs the amount of token to be decreased ====');
+
+  // For example, decrease 70% of the liquidity
+  const userInputAmountA = amounts.amountA.mul(new BN(70)).div(new BN(100));
+
+  const userGetAmountB = LiquidityMath.getAmountBFromAmountA(
+    sqrtPriceLowerX64,
+    sqrtPriceUpperX64,
+    poolInfo.sqrtPriceX64,
+    userInputAmountA
+  );
+
+  console.log(
+    `After inputting the amount of tokenA: ${new Decimal(
+      userInputAmountA.toString()
+    )} , the estimated amount of tokenB: ${new Decimal(userGetAmountB.toString())}`
+  );
+
+  const decreaseLiquidity = LiquidityMath.getLiquidityFromTokenAmounts(
+    poolInfo.sqrtPriceX64,
+    sqrtPriceLowerX64,
+    sqrtPriceUpperX64,
+    userInputAmountA,
+    userGetAmountB
+  );
+
+  console.log(
+    'Decrease ratio:',
+    new Decimal(decreaseLiquidity.toString()).div(new Decimal(liquidity.toString())).toFixed(2)
+  );
+
+  // const liquidity = LiquidityMath.getLiquidityFromTokenAmounts(
+  //   poolInfo.sqrtPriceX64,
+  //   sqrtPriceLowerX64,
+  //   sqrtPriceUpperX64,
+  //   amountA,
+  //   amountB,
+  // );
+
+  // const amounts = LiquidityMath.getAmountsFromLiquidity(
+  //   poolInfo.sqrtPriceX64,
+  //   sqrtPriceLowerX64,
+  //   sqrtPriceUpperX64,
+  //   liquidity,
+  //   true,
+  // );
+
+  // console.log("Estimated liquidity amount:", new Decimal(liquidity.toString()));
+
+  // const txid = chain.decreaseLiquidity({
+  //   userAddress,
+  //   nftMint,
+  //   // Decrease half of the liquidity
+  //   liquidity: liquidityToDecrease,
+  //   signerCallback,
+  // });
+
+  // console.log(txid);
+}
+
+main();

+ 21 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/13_decrease_full_liquidity.ts

@@ -0,0 +1,21 @@
+/**
+ * Remove all liquidity from the position, and close the position
+ */
+
+import { PublicKey } from '@solana/web3.js';
+
+import { chain, signerCallback, userAddress } from './config.js';
+
+async function main(): Promise<void> {
+  const nftMint = new PublicKey('3eunC8kdMEwBRQHd91cyQ36FVQFXDuicGk7xuVS6hFfk');
+
+  const txid = await chain.decreaseFullLiquidity({
+    userAddress,
+    nftMint,
+    signerCallback,
+  });
+
+  console.log('Decrease liquidity successfully, txid:', txid);
+}
+
+main();

+ 21 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/14_collect_fees.ts

@@ -0,0 +1,21 @@
+/**
+ * Collect fees from the specified position
+ */
+
+import { PublicKey } from '@solana/web3.js';
+
+import { chain, signerCallback, userAddress } from './config.js';
+
+async function main(): Promise<void> {
+  // Change to your own NFT mint address
+  const nftMint = new PublicKey('CVqLrFi5n3HLzRJdGHdChtoXhycNeveShYkmGfeaXGHC');
+  const txid = await chain.collectFees({
+    userAddress,
+    nftMint,
+    signerCallback,
+  });
+
+  console.log('Collect fees successfully, txid:', txid);
+}
+
+main();

+ 75 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/15_add_liquidity.ts

@@ -0,0 +1,75 @@
+/**
+ * Add liquidity to an existing position (user inputs the amount of TokenB, and calculates the amount of TokenA needed)
+ */
+
+import { PublicKey } from '@solana/web3.js';
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+import { SignerCallback } from '../client/chain/models';
+
+import { chain, userKeypair, userAddress } from './config';
+
+async function main(): Promise<void> {
+  // step 1: Select the position to add liquidity
+  // Change to your own NFT mint address
+  const nftMint = new PublicKey('CVqLrFi5n3HLzRJdGHdChtoXhycNeveShYkmGfeaXGHC');
+
+  const positionInfo = await chain.getPositionInfoByNftMint(nftMint);
+  if (!positionInfo) {
+    console.error('Position does not exist');
+    return;
+  }
+
+  console.log('========= step 1: Select the position =========');
+  console.log('Position NFT address:', nftMint.toBase58());
+  console.log(`Price range: ${positionInfo.uiPriceLower} - ${positionInfo.uiPriceUpper}`);
+  console.log(`Current TokenA: ${positionInfo.tokenA.uiAmount}`);
+  console.log(`Current TokenB: ${positionInfo.tokenB.uiAmount}`);
+
+  // step 2: User inputs the amount of TokenB and the token type (TokenB as an example)
+  const base = 'MintB';
+  const baseAmount = new BN(1000000);
+
+  // Get the pool information for calculation
+  const poolInfo = await chain.getRawPoolInfoByPoolId(positionInfo.rawPositionInfo.poolId);
+
+  // Calculate the amount of TokenA needed
+  const amountA = chain.getAmountAFromAmountB({
+    priceLower: new Decimal(positionInfo.uiPriceLower),
+    priceUpper: new Decimal(positionInfo.uiPriceUpper),
+    amountB: baseAmount,
+    poolInfo,
+  });
+
+  // Add a 2% slippage
+  const amountAWithSlippage = new BN(amountA).mul(new BN(10000 * (1 + 0.02))).div(new BN(10000));
+
+  console.log('========= step 2: User inputs the amount of TokenB and the token type =========');
+  console.log('Amount of TokenB to be added =>', Number(baseAmount.toString()) / 10 ** positionInfo.tokenB.decimals);
+  console.log('Estimated amount of TokenA needed =>', Number(amountA.toString()) / 10 ** positionInfo.tokenA.decimals);
+
+  // Signer callback
+  const signerCallback: SignerCallback = async (tx) => {
+    tx.sign([userKeypair]);
+    return tx;
+  };
+
+  console.log('======= step 3: After confirming that there is no error, start adding liquidity =======');
+  try {
+    const txid = await chain.addLiquidity({
+      userAddress,
+      nftMint,
+      base,
+      baseAmount,
+      otherAmountMax: amountAWithSlippage,
+      signerCallback,
+    });
+
+    console.log('Add liquidity successfully, txid:', txid);
+  } catch (error) {
+    console.error('Add liquidity failed:', error);
+  }
+}
+
+main();

+ 75 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/16_add_liquidity_2.ts

@@ -0,0 +1,75 @@
+/**
+ * Add liquidity to an existing position (user inputs the amount of TokenA, and calculates the amount of TokenB needed)
+ */
+
+import { PublicKey } from '@solana/web3.js';
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+import { SignerCallback } from '../client/chain/models';
+
+import { chain, userKeypair, userAddress } from './config';
+
+async function main(): Promise<void> {
+  // step 1: Select the position to add liquidity
+  // Change to your own NFT mint address
+  const nftMint = new PublicKey('CjouQkvVP5XkABWYQYzCAJMpB3g8yJhADtRcRtEZTj8M');
+
+  const positionInfo = await chain.getPositionInfoByNftMint(nftMint);
+  if (!positionInfo) {
+    console.error('Position does not exist');
+    return;
+  }
+
+  console.log('========= step 1: Select the position =========');
+  console.log('Position NFT address:', nftMint.toBase58());
+  console.log(`Price range: ${positionInfo.uiPriceLower} - ${positionInfo.uiPriceUpper}`);
+  console.log(`Current TokenA: ${positionInfo.tokenA.uiAmount}`);
+  console.log(`Current TokenB: ${positionInfo.tokenB.uiAmount}`);
+
+  // step 2: User inputs the amount of TokenA and the token type (TokenA as an example)
+  const base = 'MintA';
+  const baseAmount = new BN(1000000);
+
+  // Get the pool information for calculation
+  const poolInfo = await chain.getRawPoolInfoByPoolId(positionInfo.rawPositionInfo.poolId);
+
+  // Calculate the amount of TokenB needed
+  const amountB = chain.getAmountBFromAmountA({
+    priceLower: new Decimal(positionInfo.uiPriceLower),
+    priceUpper: new Decimal(positionInfo.uiPriceUpper),
+    amountA: baseAmount,
+    poolInfo,
+  });
+
+  // Add a 2% slippage
+  const amountBWithSlippage = new BN(amountB).mul(new BN(10000 * (1 + 0.02))).div(new BN(10000));
+
+  console.log('========= step 2: User inputs the amount of TokenA and the token type =========');
+  console.log('Amount of TokenA to be added =>', Number(baseAmount.toString()) / 10 ** positionInfo.tokenB.decimals);
+  console.log('Estimated amount of TokenB needed =>', Number(amountB.toString()) / 10 ** positionInfo.tokenA.decimals);
+
+  // Signer callback
+  const signerCallback: SignerCallback = async (tx) => {
+    tx.sign([userKeypair]);
+    return tx;
+  };
+
+  console.log('======= step 3: After confirming that there is no error, start adding liquidity =======');
+  try {
+    const txid = await chain.addLiquidity({
+      userAddress,
+      nftMint,
+      base,
+      baseAmount,
+      otherAmountMax: amountBWithSlippage,
+      signerCallback,
+    });
+
+    console.log('Add liquidity successfully, txid:', txid);
+  } catch (error) {
+    console.error('Add liquidity failed:', error);
+  }
+}
+
+main();

+ 50 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/17_collect_all_fees.ts

@@ -0,0 +1,50 @@
+/**
+ * Collect fees from all positions of the specified user
+ */
+
+import { PublicKey } from '@solana/web3.js';
+
+import { makeTransaction, sendTransaction } from '../index';
+
+import { connection, chain, userAddress, signerCallback } from './config';
+
+async function main(): Promise<void> {
+  // Get the list of positions under the specified account
+  // const positionList = await chain.getRawPositionInfoListByUserAddress(userAddress);
+
+  // const nftMintList = positionList.map((position) => position.nftMint);
+
+  const nftMintList = [
+    new PublicKey('C4y5bhKwD7ai9iJ5h5XP1ct4GQPusS4MsV4mYaSKuxAu'),
+    new PublicKey('FSNhhUH1qxDk5ZHbGCcsRJzqaFGqvjE46DP2Z8RsM1is'),
+    new PublicKey('2N92457ubvpj1Q1mVpbSuw5QDmDb6dpzNUa6PwVuCvSm'),
+    new PublicKey('3piGZRqykLKmKob42id8E61NnHqyrhWE2kziWmHZ7zqV'),
+    new PublicKey('Frpbn2b7sA6Vy1Zbz35kbgVNRkXu3tSexqJJXJRwPV6w'),
+  ];
+
+  console.log(
+    'nftMintList ==>',
+    nftMintList.map((nftMint) => nftMint.toBase58()),
+  );
+
+  const { instructionsList } = await chain.collectAllPositionFeesInstructions({
+    userAddress,
+    nftMintList,
+  });
+
+  for (const instructions of instructionsList) {
+    console.log('Start sending transaction ==>');
+    const transaction = await makeTransaction({
+      connection,
+      payerPublicKey: userAddress,
+      instructions,
+    });
+    const txid = await sendTransaction({
+      connection,
+      signTx: () => signerCallback(transaction),
+    });
+    console.log('Collect fees successfully, txid:', txid);
+  }
+}
+
+main();

+ 72 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/18_calculate_apr.ts

@@ -0,0 +1,72 @@
+/**
+ * Calculate the annualized return when adding liquidity
+ */
+
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+import { TickMath } from '../index';
+
+import { chain, PoolAddress } from './config';
+
+async function main(): Promise<void> {
+  const poolInfo = await chain.getRawPoolInfoByPoolId(PoolAddress.SOL_USDC);
+
+  // mock data
+  const volume24h = 91114900; // data from server
+  const feeRate = 0.0002; // fee rate from server
+  const solUsdValue = 166.82; // current sol price in USD, from server
+
+  // User input price range
+  const userStartPrice = '150';
+  const userEndPrice = '190';
+  const uiAmountA = '100';
+
+  const amountA = new BN(new Decimal(uiAmountA).mul(new Decimal(10 ** poolInfo.mintDecimalsA)).toFixed(0));
+
+  const priceInTickLower = TickMath.getTickAlignedPriceDetails(
+    new Decimal(userStartPrice),
+    poolInfo.tickSpacing,
+    poolInfo.mintDecimalsA,
+    poolInfo.mintDecimalsB,
+  );
+  const priceInTickUpper = TickMath.getTickAlignedPriceDetails(
+    new Decimal(userEndPrice),
+    poolInfo.tickSpacing,
+    poolInfo.mintDecimalsA,
+    poolInfo.mintDecimalsB,
+  );
+
+  // Calculate the amount of tokenB needed
+  const amountB = chain.getAmountBFromAmountA({
+    priceLower: priceInTickLower.price,
+    priceUpper: priceInTickUpper.price,
+    amountA,
+    poolInfo,
+  });
+
+  const uiAmountB = new Decimal(amountB.toString()).div(new Decimal(10 ** poolInfo.mintDecimalsB)).toString();
+
+  // console.log('amountA ==> ', amountA.toString());
+  // console.log('amountB ==> ', amountB.toString());
+  // console.log('uiAmountA ==> ', uiAmountA);
+  // console.log('uiAmountB ==> ', uiAmountB);
+
+  const positionUsdValue = Number(uiAmountA) * solUsdValue + Number(uiAmountB);
+
+  const apr = await chain.calculateApr({
+    volume24h,
+    feeRate,
+    positionUsdValue,
+    amountA,
+    amountB,
+    tickLower: priceInTickLower.tick,
+    tickUpper: priceInTickUpper.tick,
+    poolInfo,
+    scene: 'create',
+  });
+
+  console.log('Annualized return:', apr);
+}
+
+main();

+ 42 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/19_calculate_apr_2.ts

@@ -0,0 +1,42 @@
+/**
+ * Calculate the annualized return when adding liquidity
+ */
+
+import { PublicKey } from '@solana/web3.js';
+
+import { chain, PoolAddress } from './config';
+
+async function main(): Promise<void> {
+  const poolInfo = await chain.getRawPoolInfoByPoolId(PoolAddress.SOL_USDC);
+
+  // mock data
+  const volume24h = 33860684; // data from server
+  const feeRate = 0.0002; // fee rate from server
+  const solUsdValue = 166.82; // current sol price in USD, from server
+
+  // Change to your own NFT mint address
+  const nftMint = new PublicKey('3piGZRqykLKmKob42id8E61NnHqyrhWE2kziWmHZ7zqV');
+
+  const positionInfo = await chain.getPositionInfoByNftMint(nftMint);
+
+  if (!positionInfo) {
+    throw new Error('positionInfo is null');
+  }
+
+  const positionUsdValue = Number(positionInfo.tokenA.uiAmount) * solUsdValue + Number(positionInfo.tokenB.uiAmount);
+
+  const apr = await chain.calculateApr({
+    volume24h,
+    feeRate,
+    positionUsdValue,
+    amountA: positionInfo.tokenA.amount,
+    amountB: positionInfo.tokenB.amount,
+    tickLower: positionInfo.rawPositionInfo.tickLower,
+    tickUpper: positionInfo.rawPositionInfo.tickUpper,
+    poolInfo,
+  });
+
+  console.log('Annualized return:', apr);
+}
+
+main();

+ 76 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/20_calculate_apr_3.ts

@@ -0,0 +1,76 @@
+/**
+ * Calculate the annualized return when adding liquidity
+ */
+
+import { PublicKey } from '@solana/web3.js';
+import BN from 'bn.js';
+import { Decimal } from 'decimal.js';
+
+import { chain, PoolAddress } from './config';
+
+async function main(): Promise<void> {
+  const poolInfo = await chain.getRawPoolInfoByPoolId(PoolAddress.SOL_USDC);
+
+  // mock data
+  const volume24h = 33860684; // data from server
+  const feeRate = 0.0002; // fee rate from server
+  const solUsdValue = 170.23; // current sol price in USD, from server
+
+  // Change to your own NFT mint address
+  const nftMint = new PublicKey('2eBDwL4zU1gR2oPbDDaawbYZHkoZXwQeFP1Th9Pn8DbC');
+
+  const positionInfo = await chain.getPositionInfoByNftMint(nftMint);
+
+  if (!positionInfo) {
+    throw new Error('positionInfo is null');
+  }
+
+  const amountB = new BN(1000000 * 1); // 1 USDC
+
+  const amountA = chain.getAmountAFromAmountB({
+    priceLower: new Decimal(positionInfo.uiPriceLower),
+    priceUpper: new Decimal(positionInfo.uiPriceUpper),
+    amountB,
+    poolInfo,
+  });
+
+  const uiAmountA = Number(amountA) / 10 ** positionInfo.tokenA.decimals;
+  const uiAmountB = Number(amountB) / 10 ** positionInfo.tokenB.decimals;
+
+  const existPositionUsdValue =
+    Number(positionInfo.tokenA.uiAmount) * solUsdValue + Number(positionInfo.tokenB.uiAmount);
+
+  const newPositionUsdValue = Number(uiAmountA) * solUsdValue + Number(uiAmountB);
+
+  console.log(
+    `User already has position information: TokenA: ${positionInfo.tokenA.uiAmount}, TokenB: ${positionInfo.tokenB.uiAmount}, USD value: ${existPositionUsdValue}`,
+  );
+
+  console.log(`User is going to invest: TokenA: ${uiAmountA}, TokenB: ${uiAmountB}, USD value: ${newPositionUsdValue}`);
+
+  const existLiquidity = positionInfo.rawPositionInfo.liquidity;
+
+  console.log(`User already has liquidity: ${existLiquidity}`);
+
+  const positionUsdValue = newPositionUsdValue + existPositionUsdValue;
+
+  console.log(`USD value of the position: ${positionUsdValue}`);
+
+  const apr = await chain.calculateApr({
+    volume24h,
+    feeRate,
+    positionUsdValue,
+    amountA, // User is going to invest TokenA
+    amountB, // User is going to invest TokenB
+    tickLower: positionInfo.rawPositionInfo.tickLower,
+    tickUpper: positionInfo.rawPositionInfo.tickUpper,
+    // When adding liquidity, you need to pass in the existing liquidity
+    existLiquidity: positionInfo.rawPositionInfo.liquidity,
+    poolInfo,
+    scene: 'add',
+  });
+
+  console.log('Estimated annualized return:', apr);
+}
+
+main();

+ 44 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/21_swap_a_to_b.ts

@@ -0,0 +1,44 @@
+import BN from 'bn.js';
+
+import { chain, PoolAddress, signerCallback, userAddress } from './config';
+
+async function main(): Promise<void> {
+  console.log('userAddress ==>', userAddress.toBase58());
+
+  const poolInfo = await chain.getRawPoolInfoByPoolId(PoolAddress.USDC_USDT);
+
+  const amountIn = new BN(1 * 10 ** poolInfo.mintDecimalsA);
+
+  console.log('amountIn ==>', amountIn.toString());
+
+  // step 1: Get quote trading information
+  const quoteReturn = await chain.qouteSwap({
+    poolInfo,
+    slippage: 0.01,
+    inputTokenMint: poolInfo.mintA,
+    amountIn,
+  });
+
+  console.log('quoteReturn', {
+    allTrade: quoteReturn.allTrade,
+    minAmountOut: quoteReturn.minAmountOut.toString(),
+    expectedAmountOut: quoteReturn.expectedAmountOut.toString(),
+    amountIn: quoteReturn.amountIn.toString(),
+    isInputMintA: quoteReturn.isInputMintA,
+    remainingAccounts: quoteReturn.remainingAccounts.map((account) => account.toBase58()),
+    executionPrice: quoteReturn.executionPrice.toString(),
+    feeAmount: quoteReturn.feeAmount.toString(),
+  });
+
+  // step 2: Execute transaction
+  const txid = await chain.swap({
+    poolInfo,
+    quoteReturn,
+    userAddress,
+    signerCallback,
+  });
+
+  console.log('txid', txid);
+}
+
+main();

+ 44 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/22_swap_b_to_a.ts

@@ -0,0 +1,44 @@
+import BN from 'bn.js';
+
+import { chain, PoolAddress, signerCallback, userAddress } from './config';
+
+async function main(): Promise<void> {
+  console.log('userAddress ==>', userAddress.toBase58());
+
+  const poolInfo = await chain.getRawPoolInfoByPoolId(PoolAddress.USDC_USDT);
+
+  const amountIn = new BN(1 * 10 ** poolInfo.mintDecimalsB);
+
+  console.log('amountIn ==>', amountIn.toString());
+
+  // step 1: Get quote trading information
+  const quoteReturn = await chain.qouteSwap({
+    poolInfo,
+    slippage: 0.01,
+    inputTokenMint: poolInfo.mintB,
+    amountIn,
+  });
+
+  console.log('quoteReturn', {
+    allTrade: quoteReturn.allTrade,
+    minAmountOut: quoteReturn.minAmountOut.toString(),
+    expectedAmountOut: quoteReturn.expectedAmountOut.toString(),
+    amountIn: quoteReturn.amountIn.toString(),
+    isInputMintA: quoteReturn.isInputMintA,
+    remainingAccounts: quoteReturn.remainingAccounts.map((account) => account.toBase58()),
+    executionPrice: quoteReturn.executionPrice.toString(),
+    feeAmount: quoteReturn.feeAmount.toString(),
+  });
+
+  // step 2: Execute transaction
+  const txid = await chain.swap({
+    poolInfo,
+    quoteReturn,
+    userAddress,
+    signerCallback,
+  });
+
+  console.log('txid', txid);
+}
+
+main();

+ 45 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/23_swap_exact_out_a_to_b.ts

@@ -0,0 +1,45 @@
+import BN from 'bn.js';
+
+import { chain, PoolAddress, signerCallback, userAddress } from './config';
+
+async function main(): Promise<void> {
+  console.log('userAddress ==>', userAddress.toBase58());
+
+  const poolInfo = await chain.getRawPoolInfoByPoolId(PoolAddress.USDC_USDT);
+
+  // Specify the desired output amount: 1 USDT
+  const amountOut = new BN(1 * 10 ** poolInfo.mintDecimalsB);
+
+  console.log('amountOut ==>', amountOut.toString());
+
+  // step 1: Get exact output quote information
+  const quoteReturn = await chain.quoteSwapExactOut({
+    poolInfo,
+    slippage: 0.01, // 1% slippage
+    outputTokenMint: poolInfo.mintB, // Output USDT (tokenB)
+    amountOut,
+  });
+
+  console.log('quoteReturn', {
+    allTrade: quoteReturn.allTrade,
+    expectedAmountIn: quoteReturn.expectedAmountIn.toString(),
+    maxAmountIn: quoteReturn.maxAmountIn.toString(),
+    amountOut: quoteReturn.amountOut.toString(),
+    isOutputMintA: quoteReturn.isOutputMintA,
+    remainingAccounts: quoteReturn.remainingAccounts.map((account) => account.toBase58()),
+    executionPrice: quoteReturn.executionPrice.toString(),
+    feeAmount: quoteReturn.feeAmount.toString(),
+  });
+
+  // step 2: Execute exact output transaction
+  const txid = await chain.swapExactOut({
+    poolInfo,
+    quoteReturn,
+    userAddress,
+    signerCallback,
+  });
+
+  console.log('txid', txid);
+}
+
+main();

+ 45 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/24_swap_exact_out_b_to_a.ts

@@ -0,0 +1,45 @@
+import BN from 'bn.js';
+
+import { chain, PoolAddress, signerCallback, userAddress } from './config';
+
+async function main(): Promise<void> {
+  console.log('userAddress ==>', userAddress.toBase58());
+
+  const poolInfo = await chain.getRawPoolInfoByPoolId(PoolAddress.USDC_USDT);
+
+  // Specify the desired output amount: 1 USDC
+  const amountOut = new BN(1 * 10 ** poolInfo.mintDecimalsA);
+
+  console.log('amountOut ==>', amountOut.toString());
+
+  // step 1: Get exact output quote information
+  const quoteReturn = await chain.quoteSwapExactOut({
+    poolInfo,
+    slippage: 0.01, // 1% slippage
+    outputTokenMint: poolInfo.mintA, // Output USDC (tokenA)
+    amountOut,
+  });
+
+  console.log('quoteReturn', {
+    allTrade: quoteReturn.allTrade,
+    expectedAmountIn: quoteReturn.expectedAmountIn.toString(),
+    maxAmountIn: quoteReturn.maxAmountIn.toString(),
+    amountOut: quoteReturn.amountOut.toString(),
+    isOutputMintA: quoteReturn.isOutputMintA,
+    remainingAccounts: quoteReturn.remainingAccounts.map((account) => account.toBase58()),
+    executionPrice: quoteReturn.executionPrice.toString(),
+    feeAmount: quoteReturn.feeAmount.toString(),
+  });
+
+  // step 2: Execute exact output transaction
+  const txid = await chain.swapExactOut({
+    poolInfo,
+    quoteReturn,
+    userAddress,
+    signerCallback,
+  });
+
+  console.log('txid', txid);
+}
+
+main();

+ 44 - 0
src/lib/byreal-clmm-sdk/src/playgrounds/config.ts

@@ -0,0 +1,44 @@
+import path from 'path';
+
+import { Connection, Keypair, PublicKey, clusterApiUrl } from '@solana/web3.js';
+import bs58 from 'bs58';
+import dotenv from 'dotenv';
+
+dotenv.config({ path: path.resolve(__dirname, '../../.env') });
+
+import { SignerCallback } from '../client/chain/models';
+import { Chain } from '../client/index';
+import { BYREAL_CLMM_PROGRAM_ID } from '../constants';
+
+const endpoint = process.env.SOL_ENDPOINT || clusterApiUrl('mainnet-beta');
+const secretKey = process.env.SOL_SECRET_KEY;
+
+if (!secretKey) {
+  throw new Error('Please set your SOL_SECRET_KEY in .env');
+}
+
+export const connection = new Connection(endpoint);
+
+export const userKeypair = Keypair.fromSecretKey(bs58.decode(secretKey));
+
+export const userAddress = userKeypair.publicKey;
+
+export const chain = new Chain({ connection, programId: BYREAL_CLMM_PROGRAM_ID });
+
+export const signerCallback: SignerCallback = async (tx) => {
+  tx.sign([userKeypair]);
+  return tx;
+};
+
+export const TokenAddress = {
+  USDC: new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'),
+  USDT: new PublicKey('Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB'),
+  SOL: new PublicKey('So11111111111111111111111111111111111111112'),
+  BBSOL: new PublicKey('Bybit2vBJGhPF52GBdNaQfUJ6ZpThSgHBobjWZpLPb4B'),
+};
+
+export const PoolAddress = {
+  SOL_BBSOL: new PublicKey('87pbGHxigtjdMovzkAAFEe8XFVTETjDomoEFfpSFd2yD'),
+  USDC_USDT: new PublicKey('23XoPQqGw9WMsLoqTu8HMzJLD6RnXsufbKyWPLJywsCT'),
+  SOL_USDC: new PublicKey('9GTj99g9tbz9U6UYDsX6YeRTgUnkYG6GTnHv3qLa5aXq'),
+};

+ 147 - 0
src/lib/byreal-clmm-sdk/src/utils/accountInfo.ts

@@ -0,0 +1,147 @@
+import { MINT_SIZE, Mint, TOKEN_PROGRAM_ID, getTransferFeeConfig, unpackMint } from '@solana/spl-token';
+import { AccountInfo, Commitment, Connection, PublicKey } from '@solana/web3.js';
+import { chunk } from 'lodash-es';
+
+import { WSOLMint } from '../constants';
+
+import { solToWSol } from './validateAndParsePublicKey';
+
+export interface ReturnTypeFetchMultipleMintInfos {
+  [mint: string]: Mint & { feeConfig: ReturnType<typeof getTransferFeeConfig> | undefined; programId: PublicKey };
+}
+
+interface MultipleAccountsJsonRpcResponse {
+  jsonrpc: string;
+  id: string;
+  error?: {
+    code: number;
+    message: string;
+  };
+  result: {
+    context: { slot: number };
+    value: { data: Array<string>; executable: boolean; lamports: number; owner: string; rentEpoch: number }[];
+  };
+}
+
+export interface GetMultipleAccountsInfoConfig {
+  batchRequest?: boolean;
+  commitment?: Commitment;
+  chunkCount?: number;
+}
+
+export async function getMultipleAccountsInfo(
+  connection: Connection,
+  publicKeys: PublicKey[],
+  config?: GetMultipleAccountsInfoConfig,
+): Promise<(AccountInfo<Buffer> | null)[]> {
+  const {
+    batchRequest,
+    commitment = 'confirmed',
+    chunkCount = 100,
+  } = {
+    batchRequest: false,
+    ...config,
+  };
+
+  const chunkedKeys = chunk(publicKeys, chunkCount);
+  let results: (AccountInfo<Buffer> | null)[][] = new Array(chunkedKeys.length).fill([]);
+
+  if (batchRequest) {
+    const batch = chunkedKeys.map((keys) => {
+      const args = connection._buildArgs([keys.map((key) => key.toBase58())], commitment, 'base64');
+      return {
+        methodName: 'getMultipleAccounts',
+        args,
+      };
+    });
+
+    const _batch = chunk(batch, 10);
+
+    const unsafeResponse: MultipleAccountsJsonRpcResponse[] = await (
+      await Promise.all(_batch.map(async (i) => await (connection as any)._rpcBatchRequest(i)))
+    ).flat();
+    results = unsafeResponse.map((unsafeRes: MultipleAccountsJsonRpcResponse) => {
+      if (unsafeRes.error)
+        console.error(`failed to get info for multiple accounts, RPC_ERROR, ${unsafeRes.error.message}`);
+
+      return unsafeRes.result.value.map((accountInfo) => {
+        if (accountInfo) {
+          const { data, executable, lamports, owner, rentEpoch } = accountInfo;
+
+          if (data.length !== 2 && data[1] !== 'base64') {
+            console.error(`info must be base64 encoded, RPC_ERROR`);
+          }
+
+          return {
+            data: Buffer.from(data[0], 'base64'),
+            executable,
+            lamports,
+            owner: new PublicKey(owner),
+            rentEpoch,
+          };
+        }
+        return null;
+      });
+    });
+  } else {
+    try {
+      results = (await Promise.all(
+        chunkedKeys.map((keys) => connection.getMultipleAccountsInfo(keys, commitment)),
+      )) as (AccountInfo<Buffer> | null)[][];
+    } catch (error) {
+      if (error instanceof Error) {
+        console.error(`failed to get info for multiple accounts, RPC_ERROR, ${error.message}`);
+      }
+    }
+  }
+
+  return results.flat();
+}
+
+export async function getMultipleAccountsInfoWithCustomFlags<T extends { pubkey: PublicKey }>(
+  connection: Connection,
+  publicKeysWithCustomFlag: T[],
+  config?: GetMultipleAccountsInfoConfig,
+): Promise<({ accountInfo: AccountInfo<Buffer> | null } & T)[]> {
+  const multipleAccountsInfo = await getMultipleAccountsInfo(
+    connection,
+    publicKeysWithCustomFlag.map((o) => o.pubkey),
+    config,
+  );
+
+  return publicKeysWithCustomFlag.map((o, idx) => ({ ...o, accountInfo: multipleAccountsInfo[idx] }));
+}
+
+export async function fetchMultipleMintInfos({
+  connection,
+  mints,
+  config,
+}: {
+  connection: Connection;
+  mints: PublicKey[];
+  config?: { batchRequest?: boolean };
+}): Promise<ReturnTypeFetchMultipleMintInfos> {
+  if (mints.length === 0) return {};
+  const mintInfos = await getMultipleAccountsInfoWithCustomFlags(
+    connection,
+    mints.map((i) => ({ pubkey: solToWSol(i) })),
+    config,
+  );
+
+  const mintK: ReturnTypeFetchMultipleMintInfos = {};
+  for (const i of mintInfos) {
+    if (!i.accountInfo || i.accountInfo.data.length < MINT_SIZE) {
+      console.log('invalid mint account', i.pubkey.toBase58());
+      continue;
+    }
+    const t = unpackMint(i.pubkey, i.accountInfo, i.accountInfo?.owner);
+    mintK[i.pubkey.toString()] = {
+      ...t,
+      programId: i.accountInfo?.owner || TOKEN_PROGRAM_ID,
+      feeConfig: getTransferFeeConfig(t) ?? undefined,
+    };
+  }
+  mintK[PublicKey.default.toBase58()] = mintK[WSOLMint.toBase58()];
+
+  return mintK;
+}

+ 51 - 0
src/lib/byreal-clmm-sdk/src/utils/checkV0TxSize.ts

@@ -0,0 +1,51 @@
+import { Keypair, PublicKey, TransactionInstruction, TransactionMessage, VersionedTransaction } from '@solana/web3.js';
+
+export const MAX_BASE64_SIZE = 1644;
+
+// export function checkV0TxSizeTemp({
+//   instructions,
+//   payer,
+//   recentBlockhash = Keypair.generate().publicKey.toString(),
+// }: {
+//   instructions: TransactionInstruction[];
+//   payer: PublicKey;
+//   recentBlockhash?: string;
+// }) {
+//   const transactionMessage = new TransactionMessage({
+//     payerKey: payer,
+//     recentBlockhash,
+//     instructions,
+//   });
+
+//   const messageV0 = transactionMessage.compileToV0Message();
+//   try {
+//     const buildLength = Buffer.from(new VersionedTransaction(messageV0).serialize()).toString('base64').length;
+//     console.log('checkV0TxSizeTemp, buildLength ==>', buildLength);
+//   } catch {
+//     console.error('checkV0TxSizeTemp error');
+//   }
+// }
+
+export function checkV0TxSize({
+  instructions,
+  payer,
+  recentBlockhash = Keypair.generate().publicKey.toString(),
+}: {
+  instructions: TransactionInstruction[];
+  payer: PublicKey;
+  recentBlockhash?: string;
+}): boolean {
+  const transactionMessage = new TransactionMessage({
+    payerKey: payer,
+    recentBlockhash,
+    instructions,
+  });
+
+  const messageV0 = transactionMessage.compileToV0Message();
+  try {
+    const buildLength = Buffer.from(new VersionedTransaction(messageV0).serialize()).toString('base64').length;
+    return buildLength < MAX_BASE64_SIZE;
+  } catch {
+    return false;
+  }
+}

+ 72 - 0
src/lib/byreal-clmm-sdk/src/utils/estimateComputeUnits.ts

@@ -0,0 +1,72 @@
+import {
+  ComputeBudgetProgram,
+  Connection,
+  PublicKey,
+  TransactionInstruction,
+  TransactionMessage,
+  VersionedTransaction,
+} from '@solana/web3.js';
+
+/**
+ * Estimate compute units required for a transaction
+ * @param connection - Solana connection instance
+ * @param instructions - Instructions to estimate
+ * @param payerPublicKey - Payer public key
+ * @param _blockhash - Optional blockhash
+ * @returns Estimated compute unit count
+ */
+export async function estimateComputeUnits(
+  connection: Connection,
+  instructions: TransactionInstruction[],
+  payerPublicKey: PublicKey,
+  _blockhash?: string
+): Promise<number> {
+  try {
+    const blockhash = _blockhash || (await connection.getLatestBlockhash()).blockhash;
+
+    const filteredInstructions = instructions.filter((ix) => {
+      if (!ix.programId.equals(ComputeBudgetProgram.programId)) {
+        return true;
+      }
+
+      const instructionType = ix.data[0];
+      return instructionType !== 2; // Keep if not setComputeUnitLimit
+    });
+
+    const simulationInstructions = [
+      ComputeBudgetProgram.setComputeUnitLimit({ units: 1_400_000 }), // Max CU limit
+      ...filteredInstructions,
+    ];
+
+    const messageV0 = new TransactionMessage({
+      payerKey: payerPublicKey,
+      recentBlockhash: blockhash,
+      instructions: simulationInstructions,
+    }).compileToV0Message();
+
+    const simulationTx = new VersionedTransaction(messageV0);
+
+    // Simulate transaction execution with max CU limit
+    const simulation = await connection.simulateTransaction(simulationTx);
+
+    if (simulation.value.logs && simulation.value.unitsConsumed) {
+      const consumedUnits = simulation.value.unitsConsumed;
+
+      // Calculate final CU limit with safety margin
+      const estimatedUnits = Math.min(
+        Math.max(
+          consumedUnits + 100_000, // 100k buffer for safety
+          Math.ceil(consumedUnits * 1.3) // 30% buffer
+        ),
+        1_400_000 // Solana transaction max CU limit
+      );
+
+      return Math.max(estimatedUnits, 100_000); // Ensure at least 100k CU
+    }
+
+    return 400_000;
+  } catch (error) {
+    console.warn('Estimate compute units failed, using default value:', error);
+    return 400_000;
+  }
+}

+ 23 - 0
src/lib/byreal-clmm-sdk/src/utils/generatePubKey.ts

@@ -0,0 +1,23 @@
+import { sha256 } from '@noble/hashes/sha256';
+import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
+import { Keypair, PublicKey } from '@solana/web3.js';
+
+export function generatePubKey({
+  fromPublicKey,
+  programId = TOKEN_PROGRAM_ID,
+  assignSeed,
+}: {
+  fromPublicKey: PublicKey;
+  programId: PublicKey;
+  assignSeed?: string;
+}): { publicKey: PublicKey; seed: string } {
+  const seed = assignSeed ? btoa(assignSeed).slice(0, 32) : Keypair.generate().publicKey.toBase58().slice(0, 32);
+  const publicKey = createWithSeed(fromPublicKey, seed, programId);
+  return { publicKey, seed };
+}
+
+function createWithSeed(fromPublicKey: PublicKey, seed: string, programId: PublicKey): PublicKey {
+  const buffer = Buffer.concat([fromPublicKey.toBuffer(), Buffer.from(seed), programId.toBuffer()]);
+  const publicKeyBytes = sha256(buffer);
+  return new PublicKey(publicKeyBytes);
+}

+ 11 - 0
src/lib/byreal-clmm-sdk/src/utils/index.ts

@@ -0,0 +1,11 @@
+export * from './accountInfo';
+export * from './validateAndParsePublicKey';
+export * from './transactionUtils';
+export * from './checkV0TxSize';
+export * from './estimateComputeUnits';
+export * from './generatePubKey';
+export * from './token';
+
+export async function sleep(ms: number): Promise<void> {
+  new Promise((resolve) => setTimeout(resolve, ms));
+}

+ 15 - 0
src/lib/byreal-clmm-sdk/src/utils/token.ts

@@ -0,0 +1,15 @@
+import { TOKEN_2022_PROGRAM_ID } from '@solana/spl-token';
+import { Connection, PublicKey } from '@solana/web3.js';
+
+/**
+ * Check if a mint is Token2022
+ * @param mintAddress token mint address
+ * @param connection Solana connection
+ * @returns
+ */
+export async function isToken2022(mintAddress: string, connection: Connection): Promise<boolean> {
+  const mintPubkey = new PublicKey(mintAddress);
+  const info = await connection.getAccountInfo(mintPubkey);
+  if (!info) return false;
+  return info.owner.toBase58() === TOKEN_2022_PROGRAM_ID.toBase58();
+}

+ 250 - 0
src/lib/byreal-clmm-sdk/src/utils/transactionUtils.ts

@@ -0,0 +1,250 @@
+import {
+  ComputeBudgetProgram,
+  Connection,
+  PublicKey,
+  Signer,
+  TransactionInstruction,
+  TransactionMessage,
+  VersionedTransaction,
+} from '@solana/web3.js';
+
+import { estimateComputeUnits } from './estimateComputeUnits';
+
+export const DEFAULT_COMPUTE_UNIT_PRICE = 50000;
+
+export type IMakeTransactionOptions = {
+  /**
+   * Compute unit limit
+   */
+  computeUnitLimit?: number;
+  /**
+   * Compute unit price (microLamports)
+   */
+  computeUnitPrice?: number;
+  /**
+   * Whether to automatically add compute budget instructions
+   */
+  addComputeBudget?: boolean;
+} & /**
+ * maxFee and exactFee are mutually exclusive type definitions
+ * maxFee: Maximum transaction fee (SOL)
+ * exactFee: Exact transaction fee (SOL)
+ */ (
+  | { maxFee: number; exactFee?: never }
+  | { maxFee?: never; exactFee: number }
+  | { maxFee?: undefined; exactFee?: undefined }
+);
+
+export type ISendTransactionOptions = {
+  /**
+   * Skip preflight
+   */
+  skipPreflight?: boolean;
+  /**
+   * Commit level
+   */
+  preflightCommitment?: 'processed' | 'confirmed' | 'finalized';
+  /**
+   * Maximum retry times
+   */
+  maxRetries?: number;
+  /**
+   * Transaction confirmation timeout (milliseconds)
+   */
+  confirmationTimeout?: number;
+  /**
+   * Transaction confirmation retry interval (milliseconds)
+   */
+  confirmationRetryInterval?: number;
+  /**
+   * Transaction confirmation retry times
+   */
+  confirmationRetries?: number;
+};
+
+/**
+ * Create transaction object
+ * @param connection - Solana connection instance
+ * @param payerPublicKey - Payer public key
+ * @param instructions - Transaction instruction list
+ * @param options - Transaction options
+ * @param signers - Additional signers (usually new Keypairs created by the program)
+ * @returns Transaction object
+ */
+export async function makeTransaction(params: {
+  connection: Connection;
+  payerPublicKey: PublicKey;
+  instructions: TransactionInstruction[];
+  signers?: Signer[];
+  options?: IMakeTransactionOptions;
+}): Promise<VersionedTransaction> {
+  const { connection, payerPublicKey, instructions, options = {}, signers = [] } = params;
+
+  const { computeUnitPrice = DEFAULT_COMPUTE_UNIT_PRICE, addComputeBudget = true, maxFee, exactFee } = options;
+
+  const { blockhash } = await connection.getLatestBlockhash();
+
+  const finalInstructions: TransactionInstruction[] = [];
+
+  if (addComputeBudget) {
+    const computeUnitLimit =
+      options.computeUnitLimit || (await estimateComputeUnits(connection, instructions, payerPublicKey, blockhash));
+
+    console.info(`Compute unit limit: ${computeUnitLimit}`);
+
+    // If maxFee is set, check if computeUnitPrice needs to be adjusted
+    let finalComputeUnitPrice = computeUnitPrice;
+
+    if (maxFee !== undefined) {
+      // Convert maxFee from SOL to microLamports
+      // 1 SOL = 10^9 lamports, 1 lamport = 10^6 microLamports
+      const maxFeeInMicroLamports = maxFee * 1_000_000_000_000_000;
+      // Calculate estimated fee under current settings
+      const estimatedFeeInMicroLamports = computeUnitLimit * computeUnitPrice;
+
+      // If estimated fee exceeds the maximum limit, adjust computeUnitPrice
+      if (estimatedFeeInMicroLamports > maxFeeInMicroLamports) {
+        finalComputeUnitPrice = Math.floor(maxFeeInMicroLamports / computeUnitLimit);
+        console.info(`Adjust compute unit price to meet maximum fee limit: ${finalComputeUnitPrice} microLamports`);
+      }
+    } else if (typeof exactFee === 'number') {
+      // Convert exactFee from SOL to microLamports
+      const exactFeeInMicroLamports = exactFee * 1_000_000_000_000_000;
+      // Compute unit price = exact fee / compute unit limit
+      finalComputeUnitPrice = Math.ceil(exactFeeInMicroLamports / computeUnitLimit);
+    }
+
+    finalInstructions.push(
+      ComputeBudgetProgram.setComputeUnitLimit({ units: computeUnitLimit }),
+      ComputeBudgetProgram.setComputeUnitPrice({ microLamports: finalComputeUnitPrice }),
+    );
+  }
+
+  // Add other instructions after compute budget
+  finalInstructions.push(...instructions);
+
+  // Create transaction message
+  const transactionMessage = new TransactionMessage({
+    payerKey: payerPublicKey,
+    recentBlockhash: blockhash,
+    instructions: finalInstructions,
+  });
+
+  // Compile to v0 message and create VersionedTransaction
+  const transaction = new VersionedTransaction(transactionMessage.compileToV0Message());
+
+  // Sign with additional signers first
+  if (signers.length > 0) {
+    transaction.sign(signers);
+  }
+
+  return transaction;
+}
+
+/**
+ * Generic function for sending transactions, supporting front-end wallet signatures
+ * @param connection - Solana connection instance
+ * @param signTx - Method for signing transactions, usually calling the signTransaction method of the plugin wallet and signing with the new Keypair created by the program
+ * @param options - Transaction options
+ * @returns Transaction hash
+ */
+export async function sendTransaction(params: {
+  connection: Connection;
+  signTx: () => Promise<VersionedTransaction>;
+  options?: ISendTransactionOptions;
+}): Promise<string> {
+  const { connection, options = {}, signTx } = params;
+  // Set default options
+  const {
+    skipPreflight = false,
+    preflightCommitment = 'confirmed',
+    maxRetries = 3,
+    confirmationTimeout = 30000,
+    confirmationRetryInterval = 2000,
+    confirmationRetries = 10,
+  } = options;
+
+  const signedTx = await signTx?.();
+
+  // Send transaction
+  const txid = await connection.sendRawTransaction(signedTx.serialize(), {
+    skipPreflight,
+    preflightCommitment,
+    maxRetries,
+  });
+
+  console.info(`Transaction sent: ${txid}`);
+
+  try {
+    // Solution 1: Use connection.confirmTransaction
+    await connection.confirmTransaction(txid, 'confirmed');
+    console.info(`Transaction confirmed: ${txid}`);
+  } catch (err: any) {
+    console.warn(`Transaction confirmation failed, falling back to polling: ${err.message}`);
+
+    // Solution 2: If confirmTransaction fails, fallback to optimized polling
+    const confirmed = await confirmTransactionWithOptimizedPolling(connection, txid, {
+      confirmationTimeout,
+      confirmationRetryInterval,
+      confirmationRetries,
+    });
+
+    if (!confirmed) {
+      console.warn(`Transaction may not be confirmed: ${txid}`);
+    }
+  }
+
+  return txid;
+}
+
+/**
+ * Optimized polling confirmation method - as a fallback solution
+ */
+async function confirmTransactionWithOptimizedPolling(
+  connection: Connection,
+  txid: string,
+  options: {
+    confirmationTimeout: number;
+    confirmationRetryInterval: number;
+    confirmationRetries: number;
+  },
+): Promise<boolean> {
+  const { confirmationTimeout, confirmationRetryInterval, confirmationRetries } = options;
+
+  const startTime = Date.now();
+  let confirmed = false;
+  let retries = confirmationRetries;
+
+  // Use exponential backoff algorithm to reduce RPC call frequency
+  let currentInterval = confirmationRetryInterval;
+  const maxInterval = 5000; // Maximum interval 5 seconds
+  const backoffMultiplier = 1.5;
+
+  while (!confirmed && retries > 0 && Date.now() - startTime < confirmationTimeout) {
+    try {
+      // Use getSignatureStatuses for batch query (if there are multiple transactions to query together)
+      const statuses = await connection.getSignatureStatuses([txid]);
+      const status = statuses.value[0];
+
+      if (status && status.confirmationStatus === 'confirmed') {
+        confirmed = true;
+      } else if (status && status.err) {
+        // If transaction failed, exit immediately
+        console.error(`Transaction failed: ${JSON.stringify(status.err)}`);
+        break;
+      } else {
+        // Exponential backoff: gradually increase polling interval
+        await new Promise((resolve) => setTimeout(resolve, currentInterval));
+        currentInterval = Math.min(currentInterval * backoffMultiplier, maxInterval);
+        retries--;
+      }
+    } catch (err: any) {
+      console.warn(`Failed to check transaction status, reason: ${err.message}`);
+      retries--;
+      await new Promise((resolve) => setTimeout(resolve, currentInterval));
+      currentInterval = Math.min(currentInterval * backoffMultiplier, maxInterval);
+    }
+  }
+
+  return confirmed;
+}

+ 52 - 0
src/lib/byreal-clmm-sdk/src/utils/validateAndParsePublicKey.ts

@@ -0,0 +1,52 @@
+import { PublicKey } from '@solana/web3.js';
+
+import { SOLMint, WSOLMint } from '../constants';
+
+export type PublicKeyish = PublicKey | string;
+
+export function tryParsePublicKey(v: string): PublicKey | string {
+  try {
+    return new PublicKey(v);
+  } catch (e) {
+    return v;
+  }
+}
+
+export function solToWSol(mint: PublicKeyish): PublicKey {
+  return validateAndParsePublicKey({ publicKey: mint, solToWSol: true });
+}
+
+/**
+ * Validate and parse public key
+ *
+ * @param publicKey Public key
+ * @param solToWSol Whether to convert SOL to WSOL
+ */
+export function validateAndParsePublicKey({
+  publicKey: orgPubKey,
+  solToWSol,
+}: {
+  publicKey: PublicKeyish;
+  solToWSol?: boolean;
+}): PublicKey {
+  const publicKey = tryParsePublicKey(orgPubKey.toString());
+
+  if (publicKey instanceof PublicKey) {
+    if (solToWSol && publicKey.equals(SOLMint)) return WSOLMint;
+    return publicKey;
+  }
+
+  if (solToWSol && publicKey.toString() === SOLMint.toBase58()) return WSOLMint;
+
+  if (typeof publicKey === 'string') {
+    if (publicKey === PublicKey.default.toBase58()) return PublicKey.default;
+    try {
+      const key = new PublicKey(publicKey);
+      return key;
+    } catch {
+      throw new Error('invalid public key');
+    }
+  }
+
+  throw new Error('invalid public key');
+}

+ 16 - 0
src/lib/byreal-clmm-sdk/tsconfig.json

@@ -0,0 +1,16 @@
+{
+  "compilerOptions": {
+    "types": ["node"],
+    "typeRoots": ["./node_modules/@types"],
+    "lib": ["es2022"],
+    "module": "commonjs",
+    "outDir": "./dist",
+    "target": "es2022",
+    "esModuleInterop": true,
+    "moduleResolution": "node",
+    "resolveJsonModule": true,
+    "strict": false,         
+    "noImplicitAny": false,  
+    "skipLibCheck": true    
+  },
+}

+ 1212 - 0
src/lib/byreal-clmm-sdk/yarn.lock

@@ -0,0 +1,1212 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/runtime@^7.25.0":
+  version "7.27.6"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.6.tgz#ec4070a04d76bae8ddbb10770ba55714a417b7c6"
+  integrity sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==
+
+"@coral-xyz/anchor-errors@^0.31.1":
+  version "0.31.1"
+  resolved "https://registry.yarnpkg.com/@coral-xyz/anchor-errors/-/anchor-errors-0.31.1.tgz#d635cbac2533973ae6bfb5d3ba1de89ce5aece2d"
+  integrity sha512-NhNEku4F3zzUSBtrYz84FzYWm48+9OvmT1Hhnwr6GnPQry2dsEqH/ti/7ASjjpoFTWRnPXrjAIT1qM6Isop+LQ==
+
+"@coral-xyz/anchor@^0.31.1":
+  version "0.31.1"
+  resolved "https://registry.yarnpkg.com/@coral-xyz/anchor/-/anchor-0.31.1.tgz#0fdeebf45a3cb2e47e8ebbb815ca98542152962c"
+  integrity sha512-QUqpoEK+gi2S6nlYc2atgT2r41TT3caWr/cPUEL8n8Md9437trZ68STknq897b82p5mW0XrTBNOzRbmIRJtfsA==
+  dependencies:
+    "@coral-xyz/anchor-errors" "^0.31.1"
+    "@coral-xyz/borsh" "^0.31.1"
+    "@noble/hashes" "^1.3.1"
+    "@solana/web3.js" "^1.69.0"
+    bn.js "^5.1.2"
+    bs58 "^4.0.1"
+    buffer-layout "^1.2.2"
+    camelcase "^6.3.0"
+    cross-fetch "^3.1.5"
+    eventemitter3 "^4.0.7"
+    pako "^2.0.3"
+    superstruct "^0.15.4"
+    toml "^3.0.0"
+
+"@coral-xyz/borsh@^0.31.1":
+  version "0.31.1"
+  resolved "https://registry.yarnpkg.com/@coral-xyz/borsh/-/borsh-0.31.1.tgz#5328e1e0921b75d7f4a62dd3f61885a938bc7241"
+  integrity sha512-9N8AU9F0ubriKfNE3g1WF0/4dtlGXoBN/hd1PvbNBamBNwRgHxH4P+o3Zt7rSEloW1HUs6LfZEchlx9fW7POYw==
+  dependencies:
+    bn.js "^5.1.2"
+    buffer-layout "^1.2.0"
+
+"@esbuild/aix-ppc64@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz#4e0f91776c2b340e75558f60552195f6fad09f18"
+  integrity sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==
+
+"@esbuild/android-arm64@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz#bc766407f1718923f6b8079c8c61bf86ac3a6a4f"
+  integrity sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==
+
+"@esbuild/android-arm@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.5.tgz#4290d6d3407bae3883ad2cded1081a234473ce26"
+  integrity sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==
+
+"@esbuild/android-x64@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.5.tgz#40c11d9cbca4f2406548c8a9895d321bc3b35eff"
+  integrity sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==
+
+"@esbuild/darwin-arm64@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz#49d8bf8b1df95f759ac81eb1d0736018006d7e34"
+  integrity sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==
+
+"@esbuild/darwin-x64@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz#e27a5d92a14886ef1d492fd50fc61a2d4d87e418"
+  integrity sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==
+
+"@esbuild/freebsd-arm64@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz#97cede59d638840ca104e605cdb9f1b118ba0b1c"
+  integrity sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==
+
+"@esbuild/freebsd-x64@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz#71c77812042a1a8190c3d581e140d15b876b9c6f"
+  integrity sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==
+
+"@esbuild/linux-arm64@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz#f7b7c8f97eff8ffd2e47f6c67eb5c9765f2181b8"
+  integrity sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==
+
+"@esbuild/linux-arm@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz#2a0be71b6cd8201fa559aea45598dffabc05d911"
+  integrity sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==
+
+"@esbuild/linux-ia32@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz#763414463cd9ea6fa1f96555d2762f9f84c61783"
+  integrity sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==
+
+"@esbuild/linux-loong64@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz#428cf2213ff786a502a52c96cf29d1fcf1eb8506"
+  integrity sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==
+
+"@esbuild/linux-mips64el@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz#5cbcc7fd841b4cd53358afd33527cd394e325d96"
+  integrity sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==
+
+"@esbuild/linux-ppc64@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz#0d954ab39ce4f5e50f00c4f8c4fd38f976c13ad9"
+  integrity sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==
+
+"@esbuild/linux-riscv64@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz#0e7dd30730505abd8088321e8497e94b547bfb1e"
+  integrity sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==
+
+"@esbuild/linux-s390x@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz#5669af81327a398a336d7e40e320b5bbd6e6e72d"
+  integrity sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==
+
+"@esbuild/linux-x64@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz#b2357dd153aa49038967ddc1ffd90c68a9d2a0d4"
+  integrity sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==
+
+"@esbuild/netbsd-arm64@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz#53b4dfb8fe1cee93777c9e366893bd3daa6ba63d"
+  integrity sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==
+
+"@esbuild/netbsd-x64@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz#a0206f6314ce7dc8713b7732703d0f58de1d1e79"
+  integrity sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==
+
+"@esbuild/openbsd-arm64@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz#2a796c87c44e8de78001d808c77d948a21ec22fd"
+  integrity sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==
+
+"@esbuild/openbsd-x64@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz#28d0cd8909b7fa3953af998f2b2ed34f576728f0"
+  integrity sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==
+
+"@esbuild/sunos-x64@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz#a28164f5b997e8247d407e36c90d3fd5ddbe0dc5"
+  integrity sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==
+
+"@esbuild/win32-arm64@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz#6eadbead38e8bd12f633a5190e45eff80e24007e"
+  integrity sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==
+
+"@esbuild/win32-ia32@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz#bab6288005482f9ed2adb9ded7e88eba9a62cc0d"
+  integrity sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==
+
+"@esbuild/win32-x64@0.25.5":
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz#7fc114af5f6563f19f73324b5d5ff36ece0803d1"
+  integrity sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==
+
+"@jridgewell/sourcemap-codec@^1.5.0":
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
+  integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
+
+"@noble/curves@^1.4.2":
+  version "1.9.2"
+  resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.9.2.tgz#73388356ce733922396214a933ff7c95afcef911"
+  integrity sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==
+  dependencies:
+    "@noble/hashes" "1.8.0"
+
+"@noble/hashes@1.8.0", "@noble/hashes@^1.3.1", "@noble/hashes@^1.4.0", "@noble/hashes@^1.8.0":
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.8.0.tgz#cee43d801fcef9644b11b8194857695acd5f815a"
+  integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==
+
+"@rollup/rollup-android-arm-eabi@4.43.0":
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.43.0.tgz#9241b59af721beb7e3587a56c6c245d6c465753d"
+  integrity sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw==
+
+"@rollup/rollup-android-arm64@4.43.0":
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.43.0.tgz#f70ee53ba991fdd65c277b0716c559736d490a58"
+  integrity sha512-ss4YJwRt5I63454Rpj+mXCXicakdFmKnUNxr1dLK+5rv5FJgAxnN7s31a5VchRYxCFWdmnDWKd0wbAdTr0J5EA==
+
+"@rollup/rollup-darwin-arm64@4.43.0":
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.43.0.tgz#9f59000e817cf5760d87515ce899f8b93fe8756a"
+  integrity sha512-eKoL8ykZ7zz8MjgBenEF2OoTNFAPFz1/lyJ5UmmFSz5jW+7XbH1+MAgCVHy72aG59rbuQLcJeiMrP8qP5d/N0A==
+
+"@rollup/rollup-darwin-x64@4.43.0":
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.43.0.tgz#c92aebd02725ae1b88bdce40f08f7823e8055c78"
+  integrity sha512-SYwXJgaBYW33Wi/q4ubN+ldWC4DzQY62S4Ll2dgfr/dbPoF50dlQwEaEHSKrQdSjC6oIe1WgzosoaNoHCdNuMg==
+
+"@rollup/rollup-freebsd-arm64@4.43.0":
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.43.0.tgz#b128dbe7b353922ddd729a4fc4e408ddcbf338b5"
+  integrity sha512-SV+U5sSo0yujrjzBF7/YidieK2iF6E7MdF6EbYxNz94lA+R0wKl3SiixGyG/9Klab6uNBIqsN7j4Y/Fya7wAjQ==
+
+"@rollup/rollup-freebsd-x64@4.43.0":
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.43.0.tgz#88297a0ddfadddd61d7d9b73eb42b3f227301d30"
+  integrity sha512-J7uCsiV13L/VOeHJBo5SjasKiGxJ0g+nQTrBkAsmQBIdil3KhPnSE9GnRon4ejX1XDdsmK/l30IYLiAaQEO0Cg==
+
+"@rollup/rollup-linux-arm-gnueabihf@4.43.0":
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.43.0.tgz#a59afc092523ebe43d3899f33da9cdd2ec01fb87"
+  integrity sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw==
+
+"@rollup/rollup-linux-arm-musleabihf@4.43.0":
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.43.0.tgz#3095c1327b794bd187d03e372e633717fb69b4c0"
+  integrity sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw==
+
+"@rollup/rollup-linux-arm64-gnu@4.43.0":
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.43.0.tgz#e43bb77df3a6de85312e991d1e3ad352d1abb00d"
+  integrity sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA==
+
+"@rollup/rollup-linux-arm64-musl@4.43.0":
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.43.0.tgz#34873a437bcd87618f702dc66f0cbce170aebf9f"
+  integrity sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA==
+
+"@rollup/rollup-linux-loongarch64-gnu@4.43.0":
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.43.0.tgz#224ff524349e365baa56f1f512822548c2d76910"
+  integrity sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg==
+
+"@rollup/rollup-linux-powerpc64le-gnu@4.43.0":
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.43.0.tgz#43c3c053b26ace18a1d3dab204596a466c1b0e34"
+  integrity sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw==
+
+"@rollup/rollup-linux-riscv64-gnu@4.43.0":
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.43.0.tgz#e7df825d71daefa7037605015455aa58be43cd7a"
+  integrity sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g==
+
+"@rollup/rollup-linux-riscv64-musl@4.43.0":
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.43.0.tgz#d76ad93a7f4c0b2855a024d8d859196acf38acf5"
+  integrity sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q==
+
+"@rollup/rollup-linux-s390x-gnu@4.43.0":
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.43.0.tgz#0852608843d05852af3f447bf43bb63d80d62b6a"
+  integrity sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw==
+
+"@rollup/rollup-linux-x64-gnu@4.43.0":
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.43.0.tgz#d16a57f86357a4e697142bee244afed59b24e6c5"
+  integrity sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ==
+
+"@rollup/rollup-linux-x64-musl@4.43.0":
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.43.0.tgz#51cbc8b1eb46ebc0e284725418b6fbf48686e4e2"
+  integrity sha512-3yATWgdeXyuHtBhrLt98w+5fKurdqvs8B53LaoKD7P7H7FKOONLsBVMNl9ghPQZQuYcceV5CDyPfyfGpMWD9mQ==
+
+"@rollup/rollup-win32-arm64-msvc@4.43.0":
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.43.0.tgz#d6d84aace2b211119bf0ab1c586e29d01e32aa01"
+  integrity sha512-wVzXp2qDSCOpcBCT5WRWLmpJRIzv23valvcTwMHEobkjippNf+C3ys/+wf07poPkeNix0paTNemB2XrHr2TnGw==
+
+"@rollup/rollup-win32-ia32-msvc@4.43.0":
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.43.0.tgz#4af33168de2f65b97a8f36bd1d8d21cea34d3ccb"
+  integrity sha512-fYCTEyzf8d+7diCw8b+asvWDCLMjsCEA8alvtAutqJOJp/wL5hs1rWSqJ1vkjgW0L2NB4bsYJrpKkiIPRR9dvw==
+
+"@rollup/rollup-win32-x64-msvc@4.43.0":
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.43.0.tgz#42a88207659e404e8ffa655cae763cbad94906ab"
+  integrity sha512-SnGhLiE5rlK0ofq8kzuDkM0g7FN1s5VYY+YSMTibP7CqShxCQvqtNxTARS4xX4PFJfHjG0ZQYX9iGzI3FQh5Aw==
+
+"@solana/buffer-layout-utils@^0.2.0":
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/@solana/buffer-layout-utils/-/buffer-layout-utils-0.2.0.tgz#b45a6cab3293a2eb7597cceb474f229889d875ca"
+  integrity sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==
+  dependencies:
+    "@solana/buffer-layout" "^4.0.0"
+    "@solana/web3.js" "^1.32.0"
+    bigint-buffer "^1.1.5"
+    bignumber.js "^9.0.1"
+
+"@solana/buffer-layout@^4.0.0", "@solana/buffer-layout@^4.0.1":
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz#b996235eaec15b1e0b5092a8ed6028df77fa6c15"
+  integrity sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==
+  dependencies:
+    buffer "~6.0.3"
+
+"@solana/codecs-core@2.0.0-rc.1":
+  version "2.0.0-rc.1"
+  resolved "https://registry.yarnpkg.com/@solana/codecs-core/-/codecs-core-2.0.0-rc.1.tgz#1a2d76b9c7b9e7b7aeb3bd78be81c2ba21e3ce22"
+  integrity sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ==
+  dependencies:
+    "@solana/errors" "2.0.0-rc.1"
+
+"@solana/codecs-core@2.1.1":
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/@solana/codecs-core/-/codecs-core-2.1.1.tgz#5d09d7f35b0266789d7c1f9306c08051128a6a64"
+  integrity sha512-iPQW3UZ2Vi7QFBo2r9tw0NubtH8EdrhhmZulx6lC8V5a+qjaxovtM/q/UW2BTNpqqHLfO0tIcLyBLrNH4HTWPg==
+  dependencies:
+    "@solana/errors" "2.1.1"
+
+"@solana/codecs-data-structures@2.0.0-rc.1":
+  version "2.0.0-rc.1"
+  resolved "https://registry.yarnpkg.com/@solana/codecs-data-structures/-/codecs-data-structures-2.0.0-rc.1.tgz#d47b2363d99fb3d643f5677c97d64a812982b888"
+  integrity sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog==
+  dependencies:
+    "@solana/codecs-core" "2.0.0-rc.1"
+    "@solana/codecs-numbers" "2.0.0-rc.1"
+    "@solana/errors" "2.0.0-rc.1"
+
+"@solana/codecs-numbers@2.0.0-rc.1":
+  version "2.0.0-rc.1"
+  resolved "https://registry.yarnpkg.com/@solana/codecs-numbers/-/codecs-numbers-2.0.0-rc.1.tgz#f34978ddf7ea4016af3aaed5f7577c1d9869a614"
+  integrity sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ==
+  dependencies:
+    "@solana/codecs-core" "2.0.0-rc.1"
+    "@solana/errors" "2.0.0-rc.1"
+
+"@solana/codecs-numbers@^2.1.0":
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/@solana/codecs-numbers/-/codecs-numbers-2.1.1.tgz#b7a69024e2397e236bbfb11b75ff4a077236b9d2"
+  integrity sha512-m20IUPJhPUmPkHSlZ2iMAjJ7PaYUvlMtFhCQYzm9BEBSI6OCvXTG3GAPpAnSGRBfg5y+QNqqmKn4QHU3B6zzCQ==
+  dependencies:
+    "@solana/codecs-core" "2.1.1"
+    "@solana/errors" "2.1.1"
+
+"@solana/codecs-strings@2.0.0-rc.1":
+  version "2.0.0-rc.1"
+  resolved "https://registry.yarnpkg.com/@solana/codecs-strings/-/codecs-strings-2.0.0-rc.1.tgz#e1d9167075b8c5b0b60849f8add69c0f24307018"
+  integrity sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g==
+  dependencies:
+    "@solana/codecs-core" "2.0.0-rc.1"
+    "@solana/codecs-numbers" "2.0.0-rc.1"
+    "@solana/errors" "2.0.0-rc.1"
+
+"@solana/codecs@2.0.0-rc.1":
+  version "2.0.0-rc.1"
+  resolved "https://registry.yarnpkg.com/@solana/codecs/-/codecs-2.0.0-rc.1.tgz#146dc5db58bd3c28e04b4c805e6096c2d2a0a875"
+  integrity sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ==
+  dependencies:
+    "@solana/codecs-core" "2.0.0-rc.1"
+    "@solana/codecs-data-structures" "2.0.0-rc.1"
+    "@solana/codecs-numbers" "2.0.0-rc.1"
+    "@solana/codecs-strings" "2.0.0-rc.1"
+    "@solana/options" "2.0.0-rc.1"
+
+"@solana/errors@2.0.0-rc.1":
+  version "2.0.0-rc.1"
+  resolved "https://registry.yarnpkg.com/@solana/errors/-/errors-2.0.0-rc.1.tgz#3882120886eab98a37a595b85f81558861b29d62"
+  integrity sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ==
+  dependencies:
+    chalk "^5.3.0"
+    commander "^12.1.0"
+
+"@solana/errors@2.1.1":
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/@solana/errors/-/errors-2.1.1.tgz#009ebf387b0c014a8fc60a59d65757fef942e4fd"
+  integrity sha512-sj6DaWNbSJFvLzT8UZoabMefQUfSW/8tXK7NTiagsDmh+Q87eyQDDC9L3z+mNmx9b6dEf6z660MOIplDD2nfEw==
+  dependencies:
+    chalk "^5.4.1"
+    commander "^13.1.0"
+
+"@solana/options@2.0.0-rc.1":
+  version "2.0.0-rc.1"
+  resolved "https://registry.yarnpkg.com/@solana/options/-/options-2.0.0-rc.1.tgz#06924ba316dc85791fc46726a51403144a85fc4d"
+  integrity sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA==
+  dependencies:
+    "@solana/codecs-core" "2.0.0-rc.1"
+    "@solana/codecs-data-structures" "2.0.0-rc.1"
+    "@solana/codecs-numbers" "2.0.0-rc.1"
+    "@solana/codecs-strings" "2.0.0-rc.1"
+    "@solana/errors" "2.0.0-rc.1"
+
+"@solana/spl-token-group@^0.0.7":
+  version "0.0.7"
+  resolved "https://registry.yarnpkg.com/@solana/spl-token-group/-/spl-token-group-0.0.7.tgz#83c00f0cd0bda33115468cd28b89d94f8ec1fee4"
+  integrity sha512-V1N/iX7Cr7H0uazWUT2uk27TMqlqedpXHRqqAbVO2gvmJyT0E0ummMEAVQeXZ05ZhQ/xF39DLSdBp90XebWEug==
+  dependencies:
+    "@solana/codecs" "2.0.0-rc.1"
+
+"@solana/spl-token-metadata@^0.1.6":
+  version "0.1.6"
+  resolved "https://registry.yarnpkg.com/@solana/spl-token-metadata/-/spl-token-metadata-0.1.6.tgz#d240947aed6e7318d637238022a7b0981b32ae80"
+  integrity sha512-7sMt1rsm/zQOQcUWllQX9mD2O6KhSAtY1hFR2hfFwgqfFWzSY9E9GDvFVNYUI1F0iQKcm6HmePU9QbKRXTEBiA==
+  dependencies:
+    "@solana/codecs" "2.0.0-rc.1"
+
+"@solana/spl-token@^0.4.13":
+  version "0.4.13"
+  resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.4.13.tgz#8f65c3c2b315e1a00a91b8d0f60922c6eb71de62"
+  integrity sha512-cite/pYWQZZVvLbg5lsodSovbetK/eA24gaR0eeUeMuBAMNrT8XFCwaygKy0N2WSg3gSyjjNpIeAGBAKZaY/1w==
+  dependencies:
+    "@solana/buffer-layout" "^4.0.0"
+    "@solana/buffer-layout-utils" "^0.2.0"
+    "@solana/spl-token-group" "^0.0.7"
+    "@solana/spl-token-metadata" "^0.1.6"
+    buffer "^6.0.3"
+
+"@solana/web3.js@^1.32.0", "@solana/web3.js@^1.69.0", "@solana/web3.js@^1.98.2":
+  version "1.98.2"
+  resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.98.2.tgz#45167a5cfb64436944bf4dc1e8be8482bd6d4c14"
+  integrity sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A==
+  dependencies:
+    "@babel/runtime" "^7.25.0"
+    "@noble/curves" "^1.4.2"
+    "@noble/hashes" "^1.4.0"
+    "@solana/buffer-layout" "^4.0.1"
+    "@solana/codecs-numbers" "^2.1.0"
+    agentkeepalive "^4.5.0"
+    bn.js "^5.2.1"
+    borsh "^0.7.0"
+    bs58 "^4.0.1"
+    buffer "6.0.3"
+    fast-stable-stringify "^1.0.0"
+    jayson "^4.1.1"
+    node-fetch "^2.7.0"
+    rpc-websockets "^9.0.2"
+    superstruct "^2.0.2"
+
+"@swc/helpers@^0.5.11":
+  version "0.5.17"
+  resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.17.tgz#5a7be95ac0f0bf186e7e6e890e7a6f6cda6ce971"
+  integrity sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==
+  dependencies:
+    tslib "^2.8.0"
+
+"@types/chai@^5.2.2":
+  version "5.2.2"
+  resolved "https://registry.yarnpkg.com/@types/chai/-/chai-5.2.2.tgz#6f14cea18180ffc4416bc0fd12be05fdd73bdd6b"
+  integrity sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==
+  dependencies:
+    "@types/deep-eql" "*"
+
+"@types/connect@^3.4.33":
+  version "3.4.38"
+  resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858"
+  integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==
+  dependencies:
+    "@types/node" "*"
+
+"@types/deep-eql@*":
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/@types/deep-eql/-/deep-eql-4.0.2.tgz#334311971d3a07121e7eb91b684a605e7eea9cbd"
+  integrity sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==
+
+"@types/estree@1.0.7":
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.7.tgz#4158d3105276773d5b7695cd4834b1722e4f37a8"
+  integrity sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==
+
+"@types/estree@^1.0.0":
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e"
+  integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==
+
+"@types/node@*":
+  version "24.0.1"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-24.0.1.tgz#e9bfcb1c35547437c294403b7bec497772a88b0a"
+  integrity sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw==
+  dependencies:
+    undici-types "~7.8.0"
+
+"@types/node@^12.12.54":
+  version "12.20.55"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240"
+  integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==
+
+"@types/uuid@^8.3.4":
+  version "8.3.4"
+  resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc"
+  integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==
+
+"@types/ws@^7.4.4":
+  version "7.4.7"
+  resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702"
+  integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==
+  dependencies:
+    "@types/node" "*"
+
+"@types/ws@^8.2.2":
+  version "8.18.1"
+  resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9"
+  integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==
+  dependencies:
+    "@types/node" "*"
+
+"@vitest/expect@3.2.3":
+  version "3.2.3"
+  resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-3.2.3.tgz#45be98d6036c6dedbbbc51abdeca3bbd1f12450d"
+  integrity sha512-W2RH2TPWVHA1o7UmaFKISPvdicFJH+mjykctJFoAkUw+SPTJTGjUNdKscFBrqM7IPnCVu6zihtKYa7TkZS1dkQ==
+  dependencies:
+    "@types/chai" "^5.2.2"
+    "@vitest/spy" "3.2.3"
+    "@vitest/utils" "3.2.3"
+    chai "^5.2.0"
+    tinyrainbow "^2.0.0"
+
+"@vitest/mocker@3.2.3":
+  version "3.2.3"
+  resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-3.2.3.tgz#95d8371182d0e9d1dee36bd3d698149e94fbe78a"
+  integrity sha512-cP6fIun+Zx8he4rbWvi+Oya6goKQDZK+Yq4hhlggwQBbrlOQ4qtZ+G4nxB6ZnzI9lyIb+JnvyiJnPC2AGbKSPA==
+  dependencies:
+    "@vitest/spy" "3.2.3"
+    estree-walker "^3.0.3"
+    magic-string "^0.30.17"
+
+"@vitest/pretty-format@3.2.3", "@vitest/pretty-format@^3.2.3":
+  version "3.2.3"
+  resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-3.2.3.tgz#ddd30f689fdd8191dbfd0cce8ae769e5de6b7f23"
+  integrity sha512-yFglXGkr9hW/yEXngO+IKMhP0jxyFw2/qys/CK4fFUZnSltD+MU7dVYGrH8rvPcK/O6feXQA+EU33gjaBBbAng==
+  dependencies:
+    tinyrainbow "^2.0.0"
+
+"@vitest/runner@3.2.3":
+  version "3.2.3"
+  resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-3.2.3.tgz#e45318d833c8bf8b9f292a700fc06a011f70d542"
+  integrity sha512-83HWYisT3IpMaU9LN+VN+/nLHVBCSIUKJzGxC5RWUOsK1h3USg7ojL+UXQR3b4o4UBIWCYdD2fxuzM7PQQ1u8w==
+  dependencies:
+    "@vitest/utils" "3.2.3"
+    pathe "^2.0.3"
+    strip-literal "^3.0.0"
+
+"@vitest/snapshot@3.2.3":
+  version "3.2.3"
+  resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-3.2.3.tgz#786dc1939174e1ac6b674d6fd3259bd4ea35a804"
+  integrity sha512-9gIVWx2+tysDqUmmM1L0hwadyumqssOL1r8KJipwLx5JVYyxvVRfxvMq7DaWbZZsCqZnu/dZedaZQh4iYTtneA==
+  dependencies:
+    "@vitest/pretty-format" "3.2.3"
+    magic-string "^0.30.17"
+    pathe "^2.0.3"
+
+"@vitest/spy@3.2.3":
+  version "3.2.3"
+  resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-3.2.3.tgz#c91715ca4db58a1f0dec636d393a76cf9945b695"
+  integrity sha512-JHu9Wl+7bf6FEejTCREy+DmgWe+rQKbK+y32C/k5f4TBIAlijhJbRBIRIOCEpVevgRsCQR2iHRUH2/qKVM/plw==
+  dependencies:
+    tinyspy "^4.0.3"
+
+"@vitest/utils@3.2.3":
+  version "3.2.3"
+  resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-3.2.3.tgz#388afbed1fb3c25ca64c5846a9afb904e3d63bf2"
+  integrity sha512-4zFBCU5Pf+4Z6v+rwnZ1HU1yzOKKvDkMXZrymE2PBlbjKJRlrOxbvpfPSvJTGRIwGoahaOGvp+kbCoxifhzJ1Q==
+  dependencies:
+    "@vitest/pretty-format" "3.2.3"
+    loupe "^3.1.3"
+    tinyrainbow "^2.0.0"
+
+agentkeepalive@^4.5.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz#35f73e94b3f40bf65f105219c623ad19c136ea6a"
+  integrity sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==
+  dependencies:
+    humanize-ms "^1.2.1"
+
+assertion-error@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7"
+  integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==
+
+base-x@^3.0.2:
+  version "3.0.11"
+  resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.11.tgz#40d80e2a1aeacba29792ccc6c5354806421287ff"
+  integrity sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==
+  dependencies:
+    safe-buffer "^5.0.1"
+
+base-x@^5.0.0:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/base-x/-/base-x-5.0.1.tgz#16bf35254be1df8aca15e36b7c1dda74b2aa6b03"
+  integrity sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==
+
+base64-js@^1.3.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
+  integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
+
+bigint-buffer@^1.1.5:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442"
+  integrity sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==
+  dependencies:
+    bindings "^1.3.0"
+
+bignumber.js@^9.0.1:
+  version "9.3.0"
+  resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.3.0.tgz#bdba7e2a4c1a2eba08290e8dcad4f36393c92acd"
+  integrity sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==
+
+bindings@^1.3.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
+  integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
+  dependencies:
+    file-uri-to-path "1.0.0"
+
+bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1:
+  version "5.2.2"
+  resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.2.tgz#82c09f9ebbb17107cd72cb7fd39bd1f9d0aaa566"
+  integrity sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==
+
+borsh@^0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.7.0.tgz#6e9560d719d86d90dc589bca60ffc8a6c51fec2a"
+  integrity sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==
+  dependencies:
+    bn.js "^5.2.0"
+    bs58 "^4.0.0"
+    text-encoding-utf-8 "^1.0.2"
+
+bs58@^4.0.0, bs58@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
+  integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==
+  dependencies:
+    base-x "^3.0.2"
+
+bs58@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/bs58/-/bs58-6.0.0.tgz#a2cda0130558535dd281a2f8697df79caaf425d8"
+  integrity sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==
+  dependencies:
+    base-x "^5.0.0"
+
+buffer-layout@^1.2.0, buffer-layout@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/buffer-layout/-/buffer-layout-1.2.2.tgz#b9814e7c7235783085f9ca4966a0cfff112259d5"
+  integrity sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==
+
+buffer@6.0.3, buffer@^6.0.3, buffer@~6.0.3:
+  version "6.0.3"
+  resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
+  integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
+  dependencies:
+    base64-js "^1.3.1"
+    ieee754 "^1.2.1"
+
+bufferutil@^4.0.1:
+  version "4.0.9"
+  resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.9.tgz#6e81739ad48a95cad45a279588e13e95e24a800a"
+  integrity sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==
+  dependencies:
+    node-gyp-build "^4.3.0"
+
+cac@^6.7.14:
+  version "6.7.14"
+  resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959"
+  integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==
+
+camelcase@^6.3.0:
+  version "6.3.0"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
+  integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
+
+chai@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/chai/-/chai-5.2.0.tgz#1358ee106763624114addf84ab02697e411c9c05"
+  integrity sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==
+  dependencies:
+    assertion-error "^2.0.1"
+    check-error "^2.1.1"
+    deep-eql "^5.0.1"
+    loupe "^3.1.0"
+    pathval "^2.0.0"
+
+chalk@^5.3.0, chalk@^5.4.1:
+  version "5.4.1"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.4.1.tgz#1b48bf0963ec158dce2aacf69c093ae2dd2092d8"
+  integrity sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==
+
+check-error@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc"
+  integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==
+
+commander@^12.1.0:
+  version "12.1.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3"
+  integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==
+
+commander@^13.1.0:
+  version "13.1.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-13.1.0.tgz#776167db68c78f38dcce1f9b8d7b8b9a488abf46"
+  integrity sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==
+
+commander@^2.20.3:
+  version "2.20.3"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+  integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
+cross-fetch@^3.1.5:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.2.0.tgz#34e9192f53bc757d6614304d9e5e6fb4edb782e3"
+  integrity sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==
+  dependencies:
+    node-fetch "^2.7.0"
+
+debug@^4.4.1:
+  version "4.4.1"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b"
+  integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==
+  dependencies:
+    ms "^2.1.3"
+
+decimal.js@^10.5.0:
+  version "10.5.0"
+  resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.5.0.tgz#0f371c7cf6c4898ce0afb09836db73cd82010f22"
+  integrity sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==
+
+deep-eql@^5.0.1:
+  version "5.0.2"
+  resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341"
+  integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==
+
+delay@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d"
+  integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==
+
+dotenv@^16.5.0:
+  version "16.5.0"
+  resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.5.0.tgz#092b49f25f808f020050051d1ff258e404c78692"
+  integrity sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==
+
+es-module-lexer@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a"
+  integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==
+
+es6-promise@^4.0.3:
+  version "4.2.8"
+  resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
+  integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
+
+es6-promisify@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
+  integrity sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==
+  dependencies:
+    es6-promise "^4.0.3"
+
+esbuild@^0.25.0:
+  version "0.25.5"
+  resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.5.tgz#71075054993fdfae76c66586f9b9c1f8d7edd430"
+  integrity sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==
+  optionalDependencies:
+    "@esbuild/aix-ppc64" "0.25.5"
+    "@esbuild/android-arm" "0.25.5"
+    "@esbuild/android-arm64" "0.25.5"
+    "@esbuild/android-x64" "0.25.5"
+    "@esbuild/darwin-arm64" "0.25.5"
+    "@esbuild/darwin-x64" "0.25.5"
+    "@esbuild/freebsd-arm64" "0.25.5"
+    "@esbuild/freebsd-x64" "0.25.5"
+    "@esbuild/linux-arm" "0.25.5"
+    "@esbuild/linux-arm64" "0.25.5"
+    "@esbuild/linux-ia32" "0.25.5"
+    "@esbuild/linux-loong64" "0.25.5"
+    "@esbuild/linux-mips64el" "0.25.5"
+    "@esbuild/linux-ppc64" "0.25.5"
+    "@esbuild/linux-riscv64" "0.25.5"
+    "@esbuild/linux-s390x" "0.25.5"
+    "@esbuild/linux-x64" "0.25.5"
+    "@esbuild/netbsd-arm64" "0.25.5"
+    "@esbuild/netbsd-x64" "0.25.5"
+    "@esbuild/openbsd-arm64" "0.25.5"
+    "@esbuild/openbsd-x64" "0.25.5"
+    "@esbuild/sunos-x64" "0.25.5"
+    "@esbuild/win32-arm64" "0.25.5"
+    "@esbuild/win32-ia32" "0.25.5"
+    "@esbuild/win32-x64" "0.25.5"
+
+estree-walker@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d"
+  integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==
+  dependencies:
+    "@types/estree" "^1.0.0"
+
+eventemitter3@^4.0.7:
+  version "4.0.7"
+  resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
+  integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
+
+eventemitter3@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4"
+  integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==
+
+expect-type@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.2.1.tgz#af76d8b357cf5fa76c41c09dafb79c549e75f71f"
+  integrity sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==
+
+eyes@^0.1.8:
+  version "0.1.8"
+  resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0"
+  integrity sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==
+
+fast-stable-stringify@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz#5c5543462b22aeeefd36d05b34e51c78cb86d313"
+  integrity sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==
+
+fdir@^6.4.4:
+  version "6.4.6"
+  resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.6.tgz#2b268c0232697063111bbf3f64810a2a741ba281"
+  integrity sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==
+
+file-uri-to-path@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
+  integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
+
+fsevents@~2.3.2, fsevents@~2.3.3:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
+  integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
+
+humanize-ms@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed"
+  integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==
+  dependencies:
+    ms "^2.0.0"
+
+ieee754@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
+  integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
+
+isomorphic-ws@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc"
+  integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==
+
+jayson@^4.1.1:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/jayson/-/jayson-4.2.0.tgz#b71762393fa40bc9637eaf734ca6f40d3b8c0c93"
+  integrity sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg==
+  dependencies:
+    "@types/connect" "^3.4.33"
+    "@types/node" "^12.12.54"
+    "@types/ws" "^7.4.4"
+    commander "^2.20.3"
+    delay "^5.0.0"
+    es6-promisify "^5.0.0"
+    eyes "^0.1.8"
+    isomorphic-ws "^4.0.1"
+    json-stringify-safe "^5.0.1"
+    stream-json "^1.9.1"
+    uuid "^8.3.2"
+    ws "^7.5.10"
+
+js-tokens@^9.0.1:
+  version "9.0.1"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-9.0.1.tgz#2ec43964658435296f6761b34e10671c2d9527f4"
+  integrity sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==
+
+json-stringify-safe@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+  integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==
+
+ky@^1.8.1:
+  version "1.8.1"
+  resolved "https://registry.yarnpkg.com/ky/-/ky-1.8.1.tgz#b1adaa473bc30aced2bab4c408ec177b78d198f0"
+  integrity sha512-7Bp3TpsE+L+TARSnnDpk3xg8Idi8RwSLdj6CMbNWoOARIrGrbuLGusV0dYwbZOm4bB3jHNxSw8Wk/ByDqJEnDw==
+
+lodash-es@^4.17.21:
+  version "4.17.21"
+  resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
+  integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
+
+loupe@^3.1.0, loupe@^3.1.3:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.3.tgz#042a8f7986d77f3d0f98ef7990a2b2fef18b0fd2"
+  integrity sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==
+
+magic-string@^0.30.17:
+  version "0.30.17"
+  resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453"
+  integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==
+  dependencies:
+    "@jridgewell/sourcemap-codec" "^1.5.0"
+
+ms@^2.0.0, ms@^2.1.3:
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+  integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
+nanoid@^3.3.11:
+  version "3.3.11"
+  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b"
+  integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==
+
+node-fetch@^2.7.0:
+  version "2.7.0"
+  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
+  integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
+  dependencies:
+    whatwg-url "^5.0.0"
+
+node-gyp-build@^4.3.0:
+  version "4.8.4"
+  resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.4.tgz#8a70ee85464ae52327772a90d66c6077a900cfc8"
+  integrity sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==
+
+pako@^2.0.3:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86"
+  integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==
+
+pathe@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716"
+  integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==
+
+pathval@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25"
+  integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==
+
+picocolors@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
+  integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
+
+picomatch@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab"
+  integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==
+
+postcss@^8.5.3:
+  version "8.5.5"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.5.tgz#04de7797f6911fb1c96550e96616d08681537ef3"
+  integrity sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==
+  dependencies:
+    nanoid "^3.3.11"
+    picocolors "^1.1.1"
+    source-map-js "^1.2.1"
+
+rollup@^4.34.9:
+  version "4.43.0"
+  resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.43.0.tgz#275c09119eb7eaf0c3dea040523b81ef43c57b8c"
+  integrity sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg==
+  dependencies:
+    "@types/estree" "1.0.7"
+  optionalDependencies:
+    "@rollup/rollup-android-arm-eabi" "4.43.0"
+    "@rollup/rollup-android-arm64" "4.43.0"
+    "@rollup/rollup-darwin-arm64" "4.43.0"
+    "@rollup/rollup-darwin-x64" "4.43.0"
+    "@rollup/rollup-freebsd-arm64" "4.43.0"
+    "@rollup/rollup-freebsd-x64" "4.43.0"
+    "@rollup/rollup-linux-arm-gnueabihf" "4.43.0"
+    "@rollup/rollup-linux-arm-musleabihf" "4.43.0"
+    "@rollup/rollup-linux-arm64-gnu" "4.43.0"
+    "@rollup/rollup-linux-arm64-musl" "4.43.0"
+    "@rollup/rollup-linux-loongarch64-gnu" "4.43.0"
+    "@rollup/rollup-linux-powerpc64le-gnu" "4.43.0"
+    "@rollup/rollup-linux-riscv64-gnu" "4.43.0"
+    "@rollup/rollup-linux-riscv64-musl" "4.43.0"
+    "@rollup/rollup-linux-s390x-gnu" "4.43.0"
+    "@rollup/rollup-linux-x64-gnu" "4.43.0"
+    "@rollup/rollup-linux-x64-musl" "4.43.0"
+    "@rollup/rollup-win32-arm64-msvc" "4.43.0"
+    "@rollup/rollup-win32-ia32-msvc" "4.43.0"
+    "@rollup/rollup-win32-x64-msvc" "4.43.0"
+    fsevents "~2.3.2"
+
+rpc-websockets@^9.0.2:
+  version "9.1.1"
+  resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-9.1.1.tgz#5764336f3623ee1c5cc8653b7335183e3c0c78bd"
+  integrity sha512-1IXGM/TfPT6nfYMIXkJdzn+L4JEsmb0FL1O2OBjaH03V3yuUDdKFulGLMFG6ErV+8pZ5HVC0limve01RyO+saA==
+  dependencies:
+    "@swc/helpers" "^0.5.11"
+    "@types/uuid" "^8.3.4"
+    "@types/ws" "^8.2.2"
+    buffer "^6.0.3"
+    eventemitter3 "^5.0.1"
+    uuid "^8.3.2"
+    ws "^8.5.0"
+  optionalDependencies:
+    bufferutil "^4.0.1"
+    utf-8-validate "^5.0.2"
+
+safe-buffer@^5.0.1:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+  integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+siginfo@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30"
+  integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==
+
+source-map-js@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
+  integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
+
+stackback@0.0.2:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b"
+  integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==
+
+std-env@^3.9.0:
+  version "3.9.0"
+  resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.9.0.tgz#1a6f7243b339dca4c9fd55e1c7504c77ef23e8f1"
+  integrity sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==
+
+stream-chain@^2.2.5:
+  version "2.2.5"
+  resolved "https://registry.yarnpkg.com/stream-chain/-/stream-chain-2.2.5.tgz#b30967e8f14ee033c5b9a19bbe8a2cba90ba0d09"
+  integrity sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==
+
+stream-json@^1.9.1:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/stream-json/-/stream-json-1.9.1.tgz#e3fec03e984a503718946c170db7d74556c2a187"
+  integrity sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==
+  dependencies:
+    stream-chain "^2.2.5"
+
+strip-literal@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-3.0.0.tgz#ce9c452a91a0af2876ed1ae4e583539a353df3fc"
+  integrity sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==
+  dependencies:
+    js-tokens "^9.0.1"
+
+superstruct@^0.15.4:
+  version "0.15.5"
+  resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.15.5.tgz#0f0a8d3ce31313f0d84c6096cd4fa1bfdedc9dab"
+  integrity sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==
+
+superstruct@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-2.0.2.tgz#3f6d32fbdc11c357deff127d591a39b996300c54"
+  integrity sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==
+
+text-encoding-utf-8@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13"
+  integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==
+
+tinybench@^2.9.0:
+  version "2.9.0"
+  resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b"
+  integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==
+
+tinyexec@^0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2"
+  integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==
+
+tinyglobby@^0.2.13, tinyglobby@^0.2.14:
+  version "0.2.14"
+  resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.14.tgz#5280b0cf3f972b050e74ae88406c0a6a58f4079d"
+  integrity sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==
+  dependencies:
+    fdir "^6.4.4"
+    picomatch "^4.0.2"
+
+tinypool@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.1.0.tgz#4252913ec76ef8f728f2524e2118f3bef9cf23f4"
+  integrity sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ==
+
+tinyrainbow@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-2.0.0.tgz#9509b2162436315e80e3eee0fcce4474d2444294"
+  integrity sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==
+
+tinyspy@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-4.0.3.tgz#d1d0f0602f4c15f1aae083a34d6d0df3363b1b52"
+  integrity sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==
+
+toml@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee"
+  integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==
+
+tr46@~0.0.3:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
+  integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
+
+tslib@^2.3.0, tslib@^2.8.0:
+  version "2.8.1"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
+  integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
+
+typescript@~5.7.2:
+  version "5.7.3"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.3.tgz#919b44a7dbb8583a9b856d162be24a54bf80073e"
+  integrity sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==
+
+undici-types@~7.8.0:
+  version "7.8.0"
+  resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.8.0.tgz#de00b85b710c54122e44fbfd911f8d70174cd294"
+  integrity sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==
+
+utf-8-validate@^5.0.2:
+  version "5.0.10"
+  resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.10.tgz#d7d10ea39318171ca982718b6b96a8d2442571a2"
+  integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==
+  dependencies:
+    node-gyp-build "^4.3.0"
+
+uuid@^8.3.2:
+  version "8.3.2"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
+  integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
+
+vite-node@3.2.3:
+  version "3.2.3"
+  resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-3.2.3.tgz#1c5a2282fe100114c26fd221daf506e69d392a36"
+  integrity sha512-gc8aAifGuDIpZHrPjuHyP4dpQmYXqWw7D1GmDnWeNWP654UEXzVfQ5IHPSK5HaHkwB/+p1atpYpSdw/2kOv8iQ==
+  dependencies:
+    cac "^6.7.14"
+    debug "^4.4.1"
+    es-module-lexer "^1.7.0"
+    pathe "^2.0.3"
+    vite "^5.0.0 || ^6.0.0 || ^7.0.0-0"
+
+"vite@^5.0.0 || ^6.0.0 || ^7.0.0-0":
+  version "6.3.5"
+  resolved "https://registry.yarnpkg.com/vite/-/vite-6.3.5.tgz#fec73879013c9c0128c8d284504c6d19410d12a3"
+  integrity sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==
+  dependencies:
+    esbuild "^0.25.0"
+    fdir "^6.4.4"
+    picomatch "^4.0.2"
+    postcss "^8.5.3"
+    rollup "^4.34.9"
+    tinyglobby "^0.2.13"
+  optionalDependencies:
+    fsevents "~2.3.3"
+
+vitest@^3.0.9:
+  version "3.2.3"
+  resolved "https://registry.yarnpkg.com/vitest/-/vitest-3.2.3.tgz#c2497733cf51f8ec2a3327f80789b269324edb1c"
+  integrity sha512-E6U2ZFXe3N/t4f5BwUaVCKRLHqUpk1CBWeMh78UT4VaTPH/2dyvH6ALl29JTovEPu9dVKr/K/J4PkXgrMbw4Ww==
+  dependencies:
+    "@types/chai" "^5.2.2"
+    "@vitest/expect" "3.2.3"
+    "@vitest/mocker" "3.2.3"
+    "@vitest/pretty-format" "^3.2.3"
+    "@vitest/runner" "3.2.3"
+    "@vitest/snapshot" "3.2.3"
+    "@vitest/spy" "3.2.3"
+    "@vitest/utils" "3.2.3"
+    chai "^5.2.0"
+    debug "^4.4.1"
+    expect-type "^1.2.1"
+    magic-string "^0.30.17"
+    pathe "^2.0.3"
+    picomatch "^4.0.2"
+    std-env "^3.9.0"
+    tinybench "^2.9.0"
+    tinyexec "^0.3.2"
+    tinyglobby "^0.2.14"
+    tinypool "^1.1.0"
+    tinyrainbow "^2.0.0"
+    vite "^5.0.0 || ^6.0.0 || ^7.0.0-0"
+    vite-node "3.2.3"
+    why-is-node-running "^2.3.0"
+
+webidl-conversions@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
+  integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
+
+whatwg-url@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
+  integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
+  dependencies:
+    tr46 "~0.0.3"
+    webidl-conversions "^3.0.0"
+
+why-is-node-running@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04"
+  integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==
+  dependencies:
+    siginfo "^2.0.0"
+    stackback "0.0.2"
+
+ws@^7.5.10:
+  version "7.5.10"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9"
+  integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==
+
+ws@^8.5.0:
+  version "8.18.2"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.2.tgz#42738b2be57ced85f46154320aabb51ab003705a"
+  integrity sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==

+ 13 - 0
src/lib/config.ts

@@ -0,0 +1,13 @@
+import { getSolanaRpcUrl } from '@/lib/solana-config'
+import { Connection, Keypair, PublicKey, clusterApiUrl } from '@solana/web3.js'
+import { BYREAL_CLMM_PROGRAM_ID } from '@/lib/byreal-clmm-sdk/src/constants'
+import { Chain } from '@/lib/byreal-clmm-sdk/src/client/index'
+
+const rpcUrl = getSolanaRpcUrl()
+
+export const connection = new Connection(rpcUrl)
+
+export const chain = new Chain({
+	connection,
+	programId: BYREAL_CLMM_PROGRAM_ID,
+})

+ 1 - 1
src/lib/solana-config.ts

@@ -4,7 +4,7 @@
  */
 
 export function getSolanaRpcUrl(): string {
-	const rpcUrl = process.env.SOLANA_RPC_URL
+	const rpcUrl = process.env.SOL_ENDPOINT
 
 	if (!rpcUrl) {
 		throw new Error(