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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import { TOKEN_PROGRAM_ID, createMint, getOrCreateAssociatedTokenAccount, mintTo } from '@solana/spl-token';
  2. import { Connection, Keypair, PublicKey, SystemProgram } from '@solana/web3.js';
  3. import { expect } from 'chai';
  4. import { start } from 'solana-bankrun';
  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 () => await createMint(connection, authority, authority.publicKey, null, 6));
  45. const userTokenAccountInfo = await retryWithBackoff(
  46. async () => await getOrCreateAssociatedTokenAccount(connection, authority, mint, authority.publicKey),
  47. );
  48. userTokenAccount = userTokenAccountInfo.address;
  49. const recipientTokenAccountInfo = await retryWithBackoff(
  50. async () => await getOrCreateAssociatedTokenAccount(connection, authority, mint, Keypair.generate().publicKey),
  51. );
  52. recipientTokenAccount = recipientTokenAccountInfo.address;
  53. // Mint tokens to the user's account
  54. await retryWithBackoff(async () => await mintTo(connection, authority, mint, userTokenAccount, authority, 1000000000));
  55. // Find program-derived address (PDA)
  56. [userPda, bumpSeed] = await retryWithBackoff(
  57. async () => await PublicKey.findProgramAddress([userAccount.publicKey.toBuffer()], context.program.programId),
  58. );
  59. });
  60. it('should initialize user account', async () => {
  61. const space = ACCOUNT_SIZE;
  62. const rentExempt = await retryWithBackoff(async () => {
  63. return await context.connection.getMinimumBalanceForRentExemption(space);
  64. });
  65. await context.program.methods
  66. .initialize()
  67. .accounts({
  68. userAccount: userAccount.publicKey,
  69. authority: authority.publicKey,
  70. systemProgram: SystemProgram.programId,
  71. })
  72. .preInstructions([
  73. SystemProgram.createAccount({
  74. fromPubkey: authority.publicKey,
  75. newAccountPubkey: userAccount.publicKey,
  76. lamports: rentExempt,
  77. space: space,
  78. programId: context.program.programId,
  79. }),
  80. ])
  81. .signers([authority, userAccount])
  82. .rpc();
  83. const account = await context.program.account.userAccount.fetch(userAccount.publicKey);
  84. expect(account.authority.toString()).to.equal(authority.publicKey.toString());
  85. expect(account.ethereumAddress).to.deep.equal(new Array(20).fill(0));
  86. });
  87. it('should set ethereum address', async () => {
  88. const ethereumAddress = Buffer.from('1C8cd0c38F8DE35d6056c7C7aBFa7e65D260E816', 'hex');
  89. await context.program.methods
  90. .setEthereumAddress(ethereumAddress)
  91. .accounts({
  92. userAccount: userAccount.publicKey,
  93. authority: authority.publicKey,
  94. })
  95. .signers([authority])
  96. .rpc();
  97. const account = await context.program.account.userAccount.fetch(userAccount.publicKey);
  98. expect(account.ethereumAddress).to.deep.equal(Array.from(ethereumAddress));
  99. });
  100. it('should perform authority transfer', async () => {
  101. const newAuthority = Keypair.generate();
  102. await context.program.methods
  103. .transferAuthority(newAuthority.publicKey)
  104. .accounts({
  105. userAccount: userAccount.publicKey,
  106. authority: authority.publicKey,
  107. })
  108. .signers([authority])
  109. .rpc();
  110. const account = await context.program.account.userAccount.fetch(userAccount.publicKey);
  111. expect(account.authority.toString()).to.equal(newAuthority.publicKey.toString());
  112. });
  113. afterEach(async () => {
  114. if (context && typeof context.terminate === 'function') {
  115. await context.terminate();
  116. }
  117. });
  118. });