token_swap.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. import {
  2. Account,
  3. AssociatedTokenAccount,
  4. Constraint,
  5. Mint,
  6. PoseidonError,
  7. Pubkey,
  8. Seeds,
  9. Signer,
  10. TokenAccount,
  11. TokenProgram,
  12. i64,
  13. u8,
  14. u16,
  15. u64,
  16. } from '@solanaturbine/poseidon';
  17. //Poseidon cannot support custom instructions currently , so most of the amm logic has been commented out
  18. export default class tokenSwap {
  19. static PROGRAM_ID = new Pubkey('3dDaJxmPcmQVfSx9rX4xHyP5rJvkwdKcNujcX2z9KB9h');
  20. create_amm(payer: Signer, amm: AMM, admin: Admin, id: u64, fee: u16) {
  21. amm
  22. .derive([id.toBytes()])
  23. //Custom constraints don't transpile to corresponding anchor constraints yet
  24. .constraints([new Constraint(fee < new u16(10000), new PoseidonError('invalid fee'))])
  25. .init();
  26. admin.derive(['admin']).init();
  27. amm.id = id;
  28. amm.admin = admin.key;
  29. amm.fee = fee;
  30. }
  31. create_pool(
  32. payer: Signer,
  33. amm: AMM,
  34. pool: Pool,
  35. pool_authority: PoolAuthority,
  36. pool_account_a: AssociatedTokenAccount,
  37. pool_account_b: AssociatedTokenAccount,
  38. mint_liquidity: Mint,
  39. mint_a: Mint,
  40. mint_b: Mint,
  41. id: u64,
  42. ) {
  43. amm.derive([id.toBytes()]).init();
  44. pool.derive([amm.key, mint_a.key, mint_b.key]).init();
  45. pool_authority.derive([amm.key, mint_a.key, mint_b.key, 'authority']);
  46. mint_liquidity.derive([amm.key, mint_a.key, mint_b.key, 'liquidity']).init();
  47. pool_account_a.derive(mint_a, pool_authority.key).init();
  48. pool_account_b.derive(mint_b, pool_authority.key).init();
  49. pool.amm = amm.key;
  50. pool.mint_a = mint_a.key;
  51. pool.mint_b = mint_b.key;
  52. }
  53. // The liquidity is a constant value here for testing purposes since theres no way to make custom logic
  54. deposit_liquidity(
  55. payer: Signer,
  56. depositor: Signer,
  57. pool: Pool,
  58. pool_authority: PoolAuthority,
  59. pool_account_a: AssociatedTokenAccount,
  60. pool_account_b: AssociatedTokenAccount,
  61. depositor_account_a: AssociatedTokenAccount,
  62. depositor_account_b: AssociatedTokenAccount,
  63. depositor_account_liquidity: AssociatedTokenAccount,
  64. amm: AMM,
  65. mint_liquidity: Mint,
  66. mint_a: Mint,
  67. mint_b: Mint,
  68. amount_a: u64,
  69. amount_b: u64,
  70. ) {
  71. pool.derive([amm.key, mint_a.key, mint_b.key]).has([mint_a, mint_b]);
  72. pool_authority.derive([amm.key, mint_a.key, mint_b.key, 'authority']);
  73. mint_liquidity.derive([amm.key, mint_a.key, mint_b.key, 'liquidity']).init();
  74. pool_account_a.derive(mint_a, pool_authority.key).init();
  75. pool_account_b.derive(mint_b, pool_authority.key).init();
  76. depositor_account_liquidity.derive(mint_liquidity, depositor.key).init();
  77. depositor_account_a.derive(mint_a, depositor.key).init();
  78. depositor_account_b.derive(mint_b, depositor.key).init();
  79. // prevent depositing assets the depositor does not own
  80. // let amount_a = new i64(amount_a); // Set from actual initial value for `amount_a`
  81. // let amount_b = new i64(amount_b); // Set from actual initial value for `amount_b`
  82. // const depositor_account_a_amount = new i64(depositor_account_a.amount);
  83. // const depositor_account_b_amount = new i64(depositor_account_b.amount);
  84. // // Limit `amount_a` and `amount_b` to the depositor account balances
  85. // if (amount_a.gt(depositor_account_a_amount)) {
  86. // amount_a = depositor_account_a_amount;
  87. // }
  88. // if (amount_b.gt(depositor_account_b_amount)) {
  89. // amount_b = depositor_account_b_amount;
  90. // }
  91. // // Define pool account balances
  92. // const pool_account_a_amount = new i64(pool_account_a.amount);
  93. // const pool_account_b_amount = new i64(pool_account_b.amount);
  94. // // Check if pool creation is happening (no liquidity yet)
  95. // const pool_creation =
  96. // pool_account_a_amount.eq(new i64(0)) &&
  97. // pool_account_b_amount.eq(new i64(0));
  98. // // Calculate `amount_a` and `amount_b` based on existing liquidity
  99. // if (pool_creation) {
  100. // // If creating a new pool, add `amount_a` and `amount_b` as is
  101. // // (already limited by depositor account balances above)
  102. // } else {
  103. // // Calculate the pool ratio to maintain proper liquidity proportions
  104. // const ratio = pool_account_a_amount.mul(pool_account_b_amount);
  105. // if (pool_account_a_amount.gt(pool_account_b_amount)) {
  106. // amount_a = amount_b.mul(ratio).toNum();
  107. // } else {
  108. // amount_b = amount_a.div(ratio).toNum();
  109. // }
  110. // }
  111. // Computing the amount of liquidity about to be deposited
  112. // let liquidity = new i64(amount_a)
  113. // .mul(new i64(amount_b))
  114. // .sqrt()
  115. // if pool_creation {
  116. // if liquidity < MINIMUM_LIQUIDITY {
  117. // return new PoseidonError("DepositTooSmall");
  118. // }
  119. // liquidity -= MINIMUM_LIQUIDITY;
  120. // }
  121. // let liquidity = amount_a.mul(Number(amount_b));
  122. // Transfer tokens to the pool;
  123. // TokenProgram.transfer(
  124. // depositor_account_a,
  125. // pool_account_a,
  126. // depositor,
  127. // amount_a
  128. // );
  129. // TokenProgram.transfer(
  130. // depositor_account_b,
  131. // pool_account_b,
  132. // depositor,
  133. // amount_b
  134. // );
  135. // // mint the liquidity to the user
  136. // TokenProgram.mintTo(
  137. // mint_liquidity,
  138. // depositor_account_liquidity,
  139. // pool_authority,
  140. // liquidity
  141. // );
  142. }
  143. swap_exact_tokens_for_tokens(
  144. payer: Signer,
  145. trader: Signer,
  146. pool: Pool,
  147. pool_authority: PoolAuthority,
  148. pool_account_a: AssociatedTokenAccount,
  149. pool_account_b: AssociatedTokenAccount,
  150. trader_account_a: AssociatedTokenAccount,
  151. trader_account_b: AssociatedTokenAccount,
  152. amm: AMM,
  153. mint_a: Mint,
  154. mint_b: Mint,
  155. fee: u16,
  156. amount_a: u64,
  157. amount_b: u64,
  158. // swap_a:bool
  159. input_amount: u64,
  160. min_input_amount: u64,
  161. id: u64,
  162. ) {
  163. amm.derive([id.toBytes()]);
  164. pool.derive([amm.key, mint_a.key, mint_b.key]).has([amm, mint_a, mint_b]);
  165. pool_authority.derive([amm.key, mint_a.key, mint_b.key, 'authority']);
  166. pool_account_a.derive(mint_a, pool_authority.key).init();
  167. pool_account_b.derive(mint_b, pool_authority.key).init();
  168. trader_account_a.derive(mint_a, trader.key).init();
  169. trader_account_b.derive(mint_b, trader.key).init();
  170. // Prevent depositing assets the depositor does not own
  171. // let input;
  172. // if (swap_a && input_amount.gt(trader_account_a.amount)) {
  173. // input = trader_account_a.amount;
  174. // } else if (!swap_a && input_amount.gt(trader_account_b.amount)) {
  175. // input = trader_account_b.amount;
  176. // } else {
  177. // input = input_amount;
  178. // }
  179. // // Apply trading fee, used to compute the output
  180. // const taxed_input = input.sub(input.mul(amm.fee).div(new i64(10000)));
  181. // // Define pool accounts
  182. // const pool_a = pool_account_a;
  183. // const pool_b = pool_account_b;
  184. // // Calculate output based on the pool and trading direction
  185. // let output;
  186. // if (swap_a) {
  187. // output = taxed_input
  188. // .mul(pool_b.amount)
  189. // .div(pool_a.amount.add(taxed_input))
  190. // .toNum();
  191. // } else {
  192. // output = taxed_input
  193. // .mul(pool_a.amount)
  194. // .div(pool_b.amount.add(taxed_input))
  195. // .toNum();
  196. // }
  197. // // Ensure output is greater than the minimum required output
  198. // if (output.lt(min_output_amount)) {
  199. // throw new Error("OutputTooSmall");
  200. // }
  201. // // Compute the invariant before the trade
  202. // const invariant = pool_a.amount.mul(pool_b.amount);
  203. //Transfer tokens to the pool
  204. // if (swap_a) {
  205. // TokenProgram.transfer(
  206. // trader_account_a,
  207. // pool_account_a,
  208. // trader,
  209. // input
  210. // )
  211. // TokenProgram.transfer(
  212. // pool_account_a,
  213. // trader_account_a,
  214. // pool_authority,
  215. // output
  216. // )
  217. // } else {
  218. // TokenProgram.transfer(
  219. // pool_account_a,
  220. // trader_account_a,
  221. // pool_authority,
  222. // input
  223. // )
  224. // TokenProgram.transfer(
  225. // trader_account_b,
  226. // pool_account_b,
  227. // trader,
  228. // output
  229. // )
  230. // }
  231. // Verify the invariant still holds
  232. // Reload accounts because of the CPIs
  233. // We tolerate if the new invariant is higher because it means a rounding error for LPs
  234. //pool_account_a.reload()
  235. //pool_account_b.reload()
  236. // if invariant > pool_account_a.amount.mul(pool_account_a.amount) {
  237. // return new PoseidonErr("Invariant Violated");
  238. // }
  239. }
  240. withdraw_liquidity(
  241. payer: Signer,
  242. depositor: Signer,
  243. pool: Pool,
  244. pool_authority: PoolAuthority,
  245. pool_account_a: AssociatedTokenAccount,
  246. pool_account_b: AssociatedTokenAccount,
  247. depositor_account_a: AssociatedTokenAccount,
  248. depositor_account_b: AssociatedTokenAccount,
  249. depositor_account_liquidity: AssociatedTokenAccount,
  250. amm: AMM,
  251. mint_liquidity: Mint,
  252. mint_a: Mint,
  253. mint_b: Mint,
  254. amount: u64,
  255. id: u64,
  256. ) {
  257. amm.derive([id.toBytes()]);
  258. pool.derive([amm.key, mint_a.key, mint_b.key]).has([mint_a, mint_b]);
  259. pool_authority.derive([amm.key, mint_a.key, mint_b.key, 'authority']);
  260. pool_account_a.derive(mint_a, pool_authority.key).init();
  261. pool_account_b.derive(mint_b, pool_authority.key).init();
  262. pool_account_a.derive(mint_a, pool_authority.key).init();
  263. pool_account_b.derive(mint_b, pool_authority.key).init();
  264. depositor_account_liquidity.derive(mint_liquidity, depositor.key).init();
  265. depositor_account_a.derive(mint_a, depositor.key).initIfNeeded();
  266. depositor_account_b.derive(mint_b, depositor.key).initIfNeeded();
  267. // let MINIMUM_LIQUIDITY = 1;
  268. // Transfer tokens from the pool
  269. // let amount_a = new i64(amount)
  270. // .mul(new i64(pool_account_a.amount))
  271. // .div(new i64(mint_liquidity.supply.add(MINIMUM_LIQUIDITY)))
  272. // .floor()
  273. // .toNum();
  274. // TokenProgram.transfer(
  275. // pool_account_a,
  276. // depositor_account_a,
  277. // pool_authority,
  278. // amount_a
  279. // )
  280. // let amount_b = new i64(amount)
  281. // .mul(new i64(pool_account_b.amount))
  282. // .div(new i64(mint_liquidity.supply.add(MINIMUM_LIQUIDITY)))
  283. // .floor()
  284. // .toNum();
  285. // TokenProgram.transfer(
  286. // pool_account_b,
  287. // depositor_account_b,
  288. // pool_authority,
  289. // amount_b
  290. // );
  291. // TokenProgram.burn(
  292. // mint_liquidity,
  293. // depositor_account_liquidity,
  294. // depositor,
  295. // amount
  296. // );
  297. }
  298. }
  299. export interface AMM extends Account {
  300. /// The primary key of the AMM
  301. id: u64;
  302. /// Account that has admin authority over the AMM
  303. admin: Pubkey;
  304. /// The LP fee taken on each trade, in basis points
  305. fee: u16;
  306. }
  307. export interface Pool extends Account {
  308. /// Primary key of the AMM
  309. amm: Pubkey;
  310. /// Mint of token A
  311. mint_a: Pubkey;
  312. /// Mint of token B
  313. mint_b: Pubkey;
  314. }
  315. // The admin of the AMM
  316. //Read only delegatable creation
  317. export interface Admin extends Account {}
  318. //Read only authority
  319. export interface PoolAuthority extends Account {}