external-delegate-token-master.test.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import { start } from 'solana-bankrun';
  2. import { expect } from 'chai';
  3. import { PublicKey, SystemProgram, Keypair, Connection } from '@solana/web3.js';
  4. import { TOKEN_PROGRAM_ID, createMint, getOrCreateAssociatedTokenAccount, mintTo } from '@solana/spl-token';
  5. jest.setTimeout(30000); // Set timeout to 30 seconds
  6. const ACCOUNT_SIZE = 8 + 32 + 20; // Define your account size here
  7. async function retryWithBackoff(fn: () => Promise<any>, retries = 5, delay = 500): Promise<any> {
  8. try {
  9. return await fn();
  10. } catch (err) {
  11. if (retries === 0) throw err;
  12. await new Promise(resolve => setTimeout(resolve, delay));
  13. return retryWithBackoff(fn, retries - 1, delay * 2);
  14. }
  15. }
  16. describe('External Delegate Token Master Tests', () => {
  17. let context: any;
  18. let program: any;
  19. let authority: Keypair;
  20. let userAccount: Keypair;
  21. let mint: PublicKey;
  22. let userTokenAccount: PublicKey;
  23. let recipientTokenAccount: PublicKey;
  24. let userPda: PublicKey;
  25. let bumpSeed: number;
  26. beforeEach(async () => {
  27. authority = Keypair.generate();
  28. userAccount = Keypair.generate();
  29. const programs = [
  30. {
  31. name: "external_delegate_token_master",
  32. programId: new PublicKey("FYPkt5VWMvtyWZDMGCwoKFkE3wXTzphicTpnNGuHWVbD"),
  33. program: "target/deploy/external_delegate_token_master.so",
  34. },
  35. ];
  36. context = await retryWithBackoff(async () => await start(programs, []));
  37. const connection = new Connection("https://api.devnet.solana.com", "confirmed");
  38. context.connection = connection;
  39. // Airdrop SOL to authority with retry logic
  40. await retryWithBackoff(async () => {
  41. await connection.requestAirdrop(authority.publicKey, 1000000000);
  42. });
  43. // Create mint with retry logic
  44. mint = await retryWithBackoff(async () =>
  45. await createMint(connection, authority, authority.publicKey, null, 6)
  46. );
  47. const userTokenAccountInfo = await retryWithBackoff(async () =>
  48. await getOrCreateAssociatedTokenAccount(connection, authority, mint, authority.publicKey)
  49. );
  50. userTokenAccount = userTokenAccountInfo.address;
  51. const recipientTokenAccountInfo = await retryWithBackoff(async () =>
  52. await getOrCreateAssociatedTokenAccount(connection, authority, mint, Keypair.generate().publicKey)
  53. );
  54. recipientTokenAccount = recipientTokenAccountInfo.address;
  55. // Mint tokens to the user's account
  56. await retryWithBackoff(async () =>
  57. await mintTo(connection, authority, mint, userTokenAccount, authority, 1000000000)
  58. );
  59. // Find program-derived address (PDA)
  60. [userPda, bumpSeed] = await retryWithBackoff(async () =>
  61. await PublicKey.findProgramAddress([userAccount.publicKey.toBuffer()], context.program.programId)
  62. );
  63. });
  64. it('should initialize user account', async () => {
  65. const space = ACCOUNT_SIZE;
  66. const rentExempt = await retryWithBackoff(async () => {
  67. return await context.connection.getMinimumBalanceForRentExemption(space);
  68. });
  69. await context.program.methods
  70. .initialize()
  71. .accounts({
  72. userAccount: userAccount.publicKey,
  73. authority: authority.publicKey,
  74. systemProgram: SystemProgram.programId,
  75. })
  76. .preInstructions([
  77. SystemProgram.createAccount({
  78. fromPubkey: authority.publicKey,
  79. newAccountPubkey: userAccount.publicKey,
  80. lamports: rentExempt,
  81. space: space,
  82. programId: context.program.programId,
  83. }),
  84. ])
  85. .signers([authority, userAccount])
  86. .rpc();
  87. const account = await context.program.account.userAccount.fetch(userAccount.publicKey);
  88. expect(account.authority.toString()).to.equal(authority.publicKey.toString());
  89. expect(account.ethereumAddress).to.deep.equal(new Array(20).fill(0));
  90. });
  91. it('should set ethereum address', async () => {
  92. const ethereumAddress = Buffer.from('1C8cd0c38F8DE35d6056c7C7aBFa7e65D260E816', 'hex');
  93. await context.program.methods
  94. .setEthereumAddress(ethereumAddress)
  95. .accounts({
  96. userAccount: userAccount.publicKey,
  97. authority: authority.publicKey,
  98. })
  99. .signers([authority])
  100. .rpc();
  101. const account = await context.program.account.userAccount.fetch(userAccount.publicKey);
  102. expect(account.ethereumAddress).to.deep.equal(Array.from(ethereumAddress));
  103. });
  104. it('should perform authority transfer', async () => {
  105. const newAuthority = Keypair.generate();
  106. await context.program.methods
  107. .transferAuthority(newAuthority.publicKey)
  108. .accounts({
  109. userAccount: userAccount.publicKey,
  110. authority: authority.publicKey,
  111. })
  112. .signers([authority])
  113. .rpc();
  114. const account = await context.program.account.userAccount.fetch(userAccount.publicKey);
  115. expect(account.authority.toString()).to.equal(newAuthority.publicKey.toString());
  116. });
  117. afterEach(async () => {
  118. if (context && typeof context.terminate === 'function') {
  119. await context.terminate();
  120. }
  121. });
  122. });