escrow.ts 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. import * as anchor from "@coral-xyz/anchor";
  2. import { Program, BN, IdlAccounts } from "@coral-xyz/anchor";
  3. import { PublicKey, Keypair, SystemProgram } from "@solana/web3.js";
  4. import { TOKEN_PROGRAM_ID, Token } from "@solana/spl-token";
  5. import { assert } from "chai";
  6. import { Escrow } from "../target/types/escrow";
  7. type EscrowAccount = IdlAccounts<Escrow>["escrowAccount"];
  8. describe("escrow", () => {
  9. const provider = anchor.AnchorProvider.env();
  10. anchor.setProvider(provider);
  11. const TOKEN_2022_PROGRAM_ID = new anchor.web3.PublicKey(
  12. "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
  13. );
  14. const TEST_PROGRAM_IDS = [
  15. [TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID],
  16. [TOKEN_2022_PROGRAM_ID, TOKEN_2022_PROGRAM_ID],
  17. [TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID],
  18. ];
  19. const program = anchor.workspace.Escrow as Program<Escrow>;
  20. let mintA: Token = null;
  21. let mintB: Token = null;
  22. let initializerTokenAccountA: PublicKey = null;
  23. let initializerTokenAccountB: PublicKey = null;
  24. let takerTokenAccountA: PublicKey = null;
  25. let takerTokenAccountB: PublicKey = null;
  26. let pda: PublicKey = null;
  27. const takerAmount = 1000;
  28. const initializerAmount = 500;
  29. const payer = Keypair.generate();
  30. const mintAuthority = Keypair.generate();
  31. TEST_PROGRAM_IDS.forEach((tokenProgramIds) => {
  32. const escrowAccount = Keypair.generate();
  33. const [tokenProgramIdA, tokenProgramIdB] = tokenProgramIds;
  34. let name;
  35. if (tokenProgramIdA === tokenProgramIdB) {
  36. name = tokenProgramIdA === TOKEN_PROGRAM_ID ? "token" : "token-2022";
  37. } else {
  38. name = "mixed";
  39. }
  40. describe(name, () => {
  41. it("Initialise escrow state", async () => {
  42. // Airdropping tokens to a payer.
  43. await provider.connection.confirmTransaction(
  44. await provider.connection.requestAirdrop(
  45. payer.publicKey,
  46. 10000000000
  47. ),
  48. "confirmed"
  49. );
  50. mintA = await Token.createMint(
  51. provider.connection,
  52. payer,
  53. mintAuthority.publicKey,
  54. null,
  55. 0,
  56. tokenProgramIdA
  57. );
  58. mintB = await Token.createMint(
  59. provider.connection,
  60. payer,
  61. mintAuthority.publicKey,
  62. null,
  63. 0,
  64. tokenProgramIdB
  65. );
  66. initializerTokenAccountA = await mintA.createAccount(
  67. provider.wallet.publicKey
  68. );
  69. takerTokenAccountA = await mintA.createAccount(
  70. provider.wallet.publicKey
  71. );
  72. initializerTokenAccountB = await mintB.createAccount(
  73. provider.wallet.publicKey
  74. );
  75. takerTokenAccountB = await mintB.createAccount(
  76. provider.wallet.publicKey
  77. );
  78. await mintA.mintTo(
  79. initializerTokenAccountA,
  80. mintAuthority.publicKey,
  81. [mintAuthority],
  82. initializerAmount
  83. );
  84. await mintB.mintTo(
  85. takerTokenAccountB,
  86. mintAuthority.publicKey,
  87. [mintAuthority],
  88. takerAmount
  89. );
  90. let _initializerTokenAccountA = await mintA.getAccountInfo(
  91. initializerTokenAccountA
  92. );
  93. let _takerTokenAccountB = await mintB.getAccountInfo(
  94. takerTokenAccountB
  95. );
  96. assert.strictEqual(
  97. _initializerTokenAccountA.amount.toNumber(),
  98. initializerAmount
  99. );
  100. assert.strictEqual(_takerTokenAccountB.amount.toNumber(), takerAmount);
  101. });
  102. it("Initialize escrow", async () => {
  103. await program.rpc.initializeEscrow(
  104. new BN(initializerAmount),
  105. new BN(takerAmount),
  106. {
  107. accounts: {
  108. initializer: provider.wallet.publicKey,
  109. initializerDepositTokenAccount: initializerTokenAccountA,
  110. initializerReceiveTokenAccount: initializerTokenAccountB,
  111. escrowAccount: escrowAccount.publicKey,
  112. systemProgram: SystemProgram.programId,
  113. tokenProgram: tokenProgramIdA,
  114. },
  115. signers: [escrowAccount],
  116. }
  117. );
  118. // Get the PDA that is assigned authority to token account.
  119. const [_pda, _nonce] = await PublicKey.findProgramAddress(
  120. [Buffer.from(anchor.utils.bytes.utf8.encode("escrow"))],
  121. program.programId
  122. );
  123. pda = _pda;
  124. let _initializerTokenAccountA = await mintA.getAccountInfo(
  125. initializerTokenAccountA
  126. );
  127. let _escrowAccount: EscrowAccount =
  128. await program.account.escrowAccount.fetch(escrowAccount.publicKey);
  129. // Check that the new owner is the PDA.
  130. assert.isTrue(_initializerTokenAccountA.owner.equals(pda));
  131. // Check that the values in the escrow account match what we expect.
  132. assert.isTrue(
  133. _escrowAccount.initializerKey.equals(provider.wallet.publicKey)
  134. );
  135. assert.strictEqual(
  136. _escrowAccount.initializerAmount.toNumber(),
  137. initializerAmount
  138. );
  139. assert.strictEqual(_escrowAccount.takerAmount.toNumber(), takerAmount);
  140. assert.isTrue(
  141. _escrowAccount.initializerDepositTokenAccount.equals(
  142. initializerTokenAccountA
  143. )
  144. );
  145. assert.isTrue(
  146. _escrowAccount.initializerReceiveTokenAccount.equals(
  147. initializerTokenAccountB
  148. )
  149. );
  150. });
  151. it("Exchange escrow", async () => {
  152. await program.rpc.exchange({
  153. accounts: {
  154. taker: provider.wallet.publicKey,
  155. takerDepositTokenAccount: takerTokenAccountB,
  156. takerReceiveTokenAccount: takerTokenAccountA,
  157. pdaDepositTokenAccount: initializerTokenAccountA,
  158. initializerReceiveTokenAccount: initializerTokenAccountB,
  159. initializerMainAccount: provider.wallet.publicKey,
  160. escrowAccount: escrowAccount.publicKey,
  161. pdaAccount: pda,
  162. depositMint: mintB.publicKey,
  163. receiveMint: mintA.publicKey,
  164. depositTokenProgram: tokenProgramIdB,
  165. receiveTokenProgram: tokenProgramIdA,
  166. },
  167. });
  168. let _takerTokenAccountA = await mintA.getAccountInfo(
  169. takerTokenAccountA
  170. );
  171. let _takerTokenAccountB = await mintB.getAccountInfo(
  172. takerTokenAccountB
  173. );
  174. let _initializerTokenAccountA = await mintA.getAccountInfo(
  175. initializerTokenAccountA
  176. );
  177. let _initializerTokenAccountB = await mintB.getAccountInfo(
  178. initializerTokenAccountB
  179. );
  180. // Check that the initializer gets back ownership of their token account.
  181. assert.isTrue(
  182. _takerTokenAccountA.owner.equals(provider.wallet.publicKey)
  183. );
  184. assert.strictEqual(
  185. _takerTokenAccountA.amount.toNumber(),
  186. initializerAmount
  187. );
  188. assert.strictEqual(_initializerTokenAccountA.amount.toNumber(), 0);
  189. assert.strictEqual(
  190. _initializerTokenAccountB.amount.toNumber(),
  191. takerAmount
  192. );
  193. assert.strictEqual(_takerTokenAccountB.amount.toNumber(), 0);
  194. });
  195. let newEscrow = Keypair.generate();
  196. it("Initialize escrow and cancel escrow", async () => {
  197. // Put back tokens into initializer token A account.
  198. await mintA.mintTo(
  199. initializerTokenAccountA,
  200. mintAuthority.publicKey,
  201. [mintAuthority],
  202. initializerAmount
  203. );
  204. await program.rpc.initializeEscrow(
  205. new BN(initializerAmount),
  206. new BN(takerAmount),
  207. {
  208. accounts: {
  209. initializer: provider.wallet.publicKey,
  210. initializerDepositTokenAccount: initializerTokenAccountA,
  211. initializerReceiveTokenAccount: initializerTokenAccountB,
  212. escrowAccount: newEscrow.publicKey,
  213. systemProgram: SystemProgram.programId,
  214. tokenProgram: tokenProgramIdA,
  215. },
  216. signers: [newEscrow],
  217. }
  218. );
  219. let _initializerTokenAccountA = await mintA.getAccountInfo(
  220. initializerTokenAccountA
  221. );
  222. // Check that the new owner is the PDA.
  223. assert.isTrue(_initializerTokenAccountA.owner.equals(pda));
  224. // Cancel the escrow.
  225. await program.rpc.cancelEscrow({
  226. accounts: {
  227. initializer: provider.wallet.publicKey,
  228. pdaDepositTokenAccount: initializerTokenAccountA,
  229. pdaAccount: pda,
  230. escrowAccount: newEscrow.publicKey,
  231. tokenProgram: tokenProgramIdA,
  232. },
  233. });
  234. // Check the final owner should be the provider public key.
  235. _initializerTokenAccountA = await mintA.getAccountInfo(
  236. initializerTokenAccountA
  237. );
  238. assert.isTrue(
  239. _initializerTokenAccountA.owner.equals(provider.wallet.publicKey)
  240. );
  241. // Check all the funds are still there.
  242. assert.strictEqual(
  243. _initializerTokenAccountA.amount.toNumber(),
  244. initializerAmount
  245. );
  246. });
  247. });
  248. });
  249. });