@@ -130,6 +130,7 @@ describe("example-native-token-transfers", () => {
130
130
let sender : AccountAddress < "Solana" > ;
131
131
let multisig : anchor . web3 . PublicKey ;
132
132
let tokenAddress : string ;
133
+ let multisigTokenAuthority : anchor . web3 . PublicKey ;
133
134
134
135
beforeAll ( async ( ) => {
135
136
try {
@@ -226,7 +227,7 @@ describe("example-native-token-transfers", () => {
226
227
describe ( "Burning" , ( ) => {
227
228
beforeAll ( async ( ) => {
228
229
try {
229
- multisig = await spl . createMultisig (
230
+ multisigTokenAuthority = await spl . createMultisig (
230
231
connection ,
231
232
payer ,
232
233
[ owner . publicKey , ntt . pdas . tokenAuthority ( ) ] ,
@@ -241,7 +242,7 @@ describe("example-native-token-transfers", () => {
241
242
mint . publicKey ,
242
243
owner ,
243
244
spl . AuthorityType . MintTokens ,
244
- multisig ,
245
+ multisigTokenAuthority ,
245
246
[ ] ,
246
247
undefined ,
247
248
TOKEN_PROGRAM
@@ -252,7 +253,7 @@ describe("example-native-token-transfers", () => {
252
253
mint : mint . publicKey ,
253
254
outboundLimit : 1000000n ,
254
255
mode : "burning" ,
255
- multisig,
256
+ multisig : multisigTokenAuthority ,
256
257
} ) ;
257
258
await signSendWait ( ctx , initTxs , signer ) ;
258
259
@@ -357,6 +358,327 @@ describe("example-native-token-transfers", () => {
357
358
expect ( balance . value . amount ) . toBe ( "9900000" ) ;
358
359
} ) ;
359
360
361
+ describe ( "Can transfer mint authority to-and-from NTT manager" , ( ) => {
362
+ const newAuthority = anchor . web3 . Keypair . generate ( ) ;
363
+ let newMultisigAuthority : anchor . web3 . PublicKey ;
364
+
365
+ beforeAll ( async ( ) => {
366
+ newMultisigAuthority = await spl . createMultisig (
367
+ connection ,
368
+ payer ,
369
+ [ owner . publicKey , newAuthority . publicKey ] ,
370
+ 2 ,
371
+ anchor . web3 . Keypair . generate ( ) ,
372
+ undefined ,
373
+ TOKEN_PROGRAM
374
+ ) ;
375
+ } ) ;
376
+
377
+ it ( "Fails when contract is not paused" , async ( ) => {
378
+ try {
379
+ const transaction = new anchor . web3 . Transaction ( ) . add (
380
+ await NTT . createSetTokenAuthorityOneStepUncheckedInstruction (
381
+ ntt . program ,
382
+ await ntt . getConfig ( ) ,
383
+ {
384
+ owner : new SolanaAddress ( await ntt . getOwner ( ) ) . unwrap ( ) ,
385
+ newAuthority : newAuthority . publicKey ,
386
+ multisigTokenAuthority,
387
+ }
388
+ )
389
+ ) ;
390
+ transaction . feePayer = payer . publicKey ;
391
+ const { blockhash } = await connection . getLatestBlockhash ( ) ;
392
+ transaction . recentBlockhash = blockhash ;
393
+ await anchor . web3 . sendAndConfirmTransaction ( connection , transaction , [
394
+ payer ,
395
+ ] ) ;
396
+ // tx should fail so this expect should never be hit
397
+ expect ( false ) . toBeTruthy ( ) ;
398
+ } catch ( e ) {
399
+ expect ( e ) . toBeInstanceOf ( anchor . web3 . SendTransactionError ) ;
400
+ const parsedError = anchor . AnchorError . parse (
401
+ ( e as anchor . web3 . SendTransactionError ) . logs ?? [ ]
402
+ ) ;
403
+ expect ( parsedError ?. error . errorCode ) . toEqual ( {
404
+ code : "NotPaused" ,
405
+ number : 6024 ,
406
+ } ) ;
407
+ } finally {
408
+ const mintInfo = await spl . getMint (
409
+ connection ,
410
+ mint . publicKey ,
411
+ undefined ,
412
+ TOKEN_PROGRAM
413
+ ) ;
414
+ expect ( mintInfo . mintAuthority ) . toEqual ( multisigTokenAuthority ) ;
415
+ }
416
+ } ) ;
417
+
418
+ test ( "Multisig(owner, TA) -> newAuthority" , async ( ) => {
419
+ // retry after pausing contract
420
+ const pauseTxs = await ntt . pause ( new SolanaAddress ( payer . publicKey ) ) ;
421
+ await signSendWait ( ctx , pauseTxs , signer ) ;
422
+
423
+ const transaction = new anchor . web3 . Transaction ( ) . add (
424
+ await NTT . createSetTokenAuthorityOneStepUncheckedInstruction (
425
+ ntt . program ,
426
+ await ntt . getConfig ( ) ,
427
+ {
428
+ owner : new SolanaAddress ( await ntt . getOwner ( ) ) . unwrap ( ) ,
429
+ newAuthority : newAuthority . publicKey ,
430
+ multisigTokenAuthority,
431
+ }
432
+ )
433
+ ) ;
434
+ transaction . feePayer = payer . publicKey ;
435
+ const { blockhash } = await connection . getLatestBlockhash ( ) ;
436
+ transaction . recentBlockhash = blockhash ;
437
+ await anchor . web3 . sendAndConfirmTransaction ( connection , transaction , [
438
+ payer ,
439
+ ] ) ;
440
+
441
+ const mintInfo = await spl . getMint (
442
+ connection ,
443
+ mint . publicKey ,
444
+ undefined ,
445
+ TOKEN_PROGRAM
446
+ ) ;
447
+ expect ( mintInfo . mintAuthority ) . toEqual ( newAuthority . publicKey ) ;
448
+ } ) ;
449
+
450
+ test ( "newAuthority -> TA" , async ( ) => {
451
+ const transaction = new anchor . web3 . Transaction ( ) . add (
452
+ await NTT . createAcceptTokenAuthorityInstruction (
453
+ ntt . program ,
454
+ await ntt . getConfig ( ) ,
455
+ {
456
+ currentAuthority : newAuthority . publicKey ,
457
+ }
458
+ )
459
+ ) ;
460
+ transaction . feePayer = payer . publicKey ;
461
+ const { blockhash } = await connection . getLatestBlockhash ( ) ;
462
+ transaction . recentBlockhash = blockhash ;
463
+ await anchor . web3 . sendAndConfirmTransaction ( connection , transaction , [
464
+ payer ,
465
+ newAuthority ,
466
+ ] ) ;
467
+
468
+ const mintInfo = await spl . getMint (
469
+ connection ,
470
+ mint . publicKey ,
471
+ undefined ,
472
+ TOKEN_PROGRAM
473
+ ) ;
474
+ expect ( mintInfo . mintAuthority ) . toEqual ( ntt . pdas . tokenAuthority ( ) ) ;
475
+ } ) ;
476
+
477
+ test ( "TA -> Multisig(owner, newAuthority)" , async ( ) => {
478
+ // set token authority: TA -> newMultisigAuthority
479
+ const setTransaction = new anchor . web3 . Transaction ( ) . add (
480
+ await NTT . createSetTokenAuthorityInstruction (
481
+ ntt . program ,
482
+ await ntt . getConfig ( ) ,
483
+ {
484
+ rentPayer : new SolanaAddress ( await ntt . getOwner ( ) ) . unwrap ( ) ,
485
+ owner : new SolanaAddress ( await ntt . getOwner ( ) ) . unwrap ( ) ,
486
+ newAuthority : newMultisigAuthority ,
487
+ }
488
+ )
489
+ ) ;
490
+ setTransaction . feePayer = payer . publicKey ;
491
+ const { blockhash } = await connection . getLatestBlockhash ( ) ;
492
+ setTransaction . recentBlockhash = blockhash ;
493
+ await anchor . web3 . sendAndConfirmTransaction (
494
+ connection ,
495
+ setTransaction ,
496
+ [ payer ]
497
+ ) ;
498
+
499
+ // claim token authority: newMultisigAuthority <- TA
500
+ const claimTransaction = new anchor . web3 . Transaction ( ) . add (
501
+ await NTT . createClaimTokenAuthorityToMultisigInstruction (
502
+ ntt . program ,
503
+ await ntt . getConfig ( ) ,
504
+ {
505
+ rentPayer : new SolanaAddress ( await ntt . getOwner ( ) ) . unwrap ( ) ,
506
+ newMultisigAuthority,
507
+ additionalSigners : [ newAuthority . publicKey , owner . publicKey ] ,
508
+ }
509
+ )
510
+ ) ;
511
+ claimTransaction . feePayer = payer . publicKey ;
512
+ claimTransaction . recentBlockhash = blockhash ;
513
+ await anchor . web3 . sendAndConfirmTransaction (
514
+ connection ,
515
+ claimTransaction ,
516
+ [ payer , newAuthority , owner ]
517
+ ) ;
518
+
519
+ const mintInfo = await spl . getMint (
520
+ connection ,
521
+ mint . publicKey ,
522
+ undefined ,
523
+ TOKEN_PROGRAM
524
+ ) ;
525
+ expect ( mintInfo . mintAuthority ) . toEqual ( newMultisigAuthority ) ;
526
+ } ) ;
527
+
528
+ test ( "Multisig(owner, newAuthority) -> Multisig(owner, TA)" , async ( ) => {
529
+ const transaction = new anchor . web3 . Transaction ( ) . add (
530
+ await NTT . createAcceptTokenAuthorityFromMultisigInstruction (
531
+ ntt . program ,
532
+ await ntt . getConfig ( ) ,
533
+ {
534
+ currentMultisigAuthority : newMultisigAuthority ,
535
+ additionalSigners : [ newAuthority . publicKey , owner . publicKey ] ,
536
+ multisigTokenAuthority,
537
+ }
538
+ )
539
+ ) ;
540
+ transaction . feePayer = payer . publicKey ;
541
+ const { blockhash } = await connection . getLatestBlockhash ( ) ;
542
+ transaction . recentBlockhash = blockhash ;
543
+ await anchor . web3 . sendAndConfirmTransaction ( connection , transaction , [
544
+ payer ,
545
+ newAuthority ,
546
+ owner ,
547
+ ] ) ;
548
+
549
+ const mintInfo = await spl . getMint (
550
+ connection ,
551
+ mint . publicKey ,
552
+ undefined ,
553
+ TOKEN_PROGRAM
554
+ ) ;
555
+ expect ( mintInfo . mintAuthority ) . toEqual ( multisigTokenAuthority ) ;
556
+ } ) ;
557
+
558
+ it ( "Fails on claim after revert" , async ( ) => {
559
+ try {
560
+ // fund newAuthority for it to be rent payer
561
+ const signature = await connection . requestAirdrop (
562
+ newAuthority . publicKey ,
563
+ anchor . web3 . LAMPORTS_PER_SOL
564
+ ) ;
565
+ const { blockhash, lastValidBlockHeight } =
566
+ await connection . getLatestBlockhash ( ) ;
567
+ await connection . confirmTransaction ( {
568
+ blockhash,
569
+ lastValidBlockHeight,
570
+ signature,
571
+ } ) ;
572
+ let newAuthorityBalance = (
573
+ await connection . getAccountInfo ( newAuthority . publicKey )
574
+ ) ?. lamports ;
575
+ expect ( newAuthorityBalance ) . toBe ( anchor . web3 . LAMPORTS_PER_SOL ) ;
576
+
577
+ // set token authority: multisigTokenAuthority -> newAuthority
578
+ const setTransaction = new anchor . web3 . Transaction ( ) . add (
579
+ await NTT . createSetTokenAuthorityInstruction (
580
+ ntt . program ,
581
+ await ntt . getConfig ( ) ,
582
+ {
583
+ rentPayer : newAuthority . publicKey ,
584
+ owner : new SolanaAddress ( await ntt . getOwner ( ) ) . unwrap ( ) ,
585
+ newAuthority : newAuthority . publicKey ,
586
+ multisigTokenAuthority,
587
+ }
588
+ )
589
+ ) ;
590
+ setTransaction . feePayer = payer . publicKey ;
591
+ setTransaction . recentBlockhash = blockhash ;
592
+ await anchor . web3 . sendAndConfirmTransaction (
593
+ connection ,
594
+ setTransaction ,
595
+ [ payer , newAuthority ]
596
+ ) ;
597
+ newAuthorityBalance = (
598
+ await connection . getAccountInfo ( newAuthority . publicKey )
599
+ ) ?. lamports ;
600
+ const pendingTokenAuthorityRentExemptAmount =
601
+ await connection . getMinimumBalanceForRentExemption (
602
+ ntt . program . account . pendingTokenAuthority . size
603
+ ) ;
604
+ expect ( newAuthorityBalance ) . toBe (
605
+ anchor . web3 . LAMPORTS_PER_SOL - pendingTokenAuthorityRentExemptAmount
606
+ ) ;
607
+
608
+ // revert token authority: multisigTokenAuthority
609
+ const revertTransaction = new anchor . web3 . Transaction ( ) . add (
610
+ await NTT . createRevertTokenAuthorityInstruction (
611
+ ntt . program ,
612
+ await ntt . getConfig ( ) ,
613
+ {
614
+ rentPayer : newAuthority . publicKey ,
615
+ owner : new SolanaAddress ( await ntt . getOwner ( ) ) . unwrap ( ) ,
616
+ multisigTokenAuthority,
617
+ }
618
+ )
619
+ ) ;
620
+ revertTransaction . feePayer = payer . publicKey ;
621
+ revertTransaction . recentBlockhash = blockhash ;
622
+ await anchor . web3 . sendAndConfirmTransaction (
623
+ connection ,
624
+ revertTransaction ,
625
+ [ payer ]
626
+ ) ;
627
+ newAuthorityBalance = (
628
+ await connection . getAccountInfo ( newAuthority . publicKey )
629
+ ) ?. lamports ;
630
+ expect ( newAuthorityBalance ) . toBe ( anchor . web3 . LAMPORTS_PER_SOL ) ;
631
+
632
+ // claim token authority: newAuthority <- multisigTokenAuthority
633
+ const claimTransaction = new anchor . web3 . Transaction ( ) . add (
634
+ await NTT . createClaimTokenAuthorityInstruction (
635
+ ntt . program ,
636
+ await ntt . getConfig ( ) ,
637
+ {
638
+ rentPayer : newAuthority . publicKey ,
639
+ newAuthority : newAuthority . publicKey ,
640
+ multisigTokenAuthority,
641
+ }
642
+ )
643
+ ) ;
644
+ claimTransaction . feePayer = payer . publicKey ;
645
+ claimTransaction . recentBlockhash = blockhash ;
646
+ await anchor . web3 . sendAndConfirmTransaction (
647
+ connection ,
648
+ claimTransaction ,
649
+ [ payer , newAuthority ]
650
+ ) ;
651
+ // tx should fail so this expect should never be hit
652
+ expect ( false ) . toBeTruthy ( ) ;
653
+ } catch ( e ) {
654
+ expect ( e ) . toBeInstanceOf ( anchor . web3 . SendTransactionError ) ;
655
+ const parsedError = anchor . AnchorError . parse (
656
+ ( e as anchor . web3 . SendTransactionError ) . logs ?? [ ]
657
+ ) ;
658
+ expect ( parsedError ?. error . errorCode ) . toEqual ( {
659
+ code : "AccountNotInitialized" ,
660
+ number : 3012 ,
661
+ } ) ;
662
+ } finally {
663
+ const mintInfo = await spl . getMint (
664
+ connection ,
665
+ mint . publicKey ,
666
+ undefined ,
667
+ TOKEN_PROGRAM
668
+ ) ;
669
+ expect ( mintInfo . mintAuthority ) . toEqual ( multisigTokenAuthority ) ;
670
+ }
671
+ } ) ;
672
+
673
+ afterAll ( async ( ) => {
674
+ // unpause
675
+ const unpauseTxs = await ntt . unpause (
676
+ new SolanaAddress ( payer . publicKey )
677
+ ) ;
678
+ await signSendWait ( ctx , unpauseTxs , signer ) ;
679
+ } ) ;
680
+ } ) ;
681
+
360
682
it ( "Can receive tokens" , async ( ) => {
361
683
const emitter = new testing . mocks . MockEmitter (
362
684
remoteXcvr . address as UniversalAddress ,
@@ -396,7 +718,7 @@ describe("example-native-token-transfers", () => {
396
718
const published = emitter . publishMessage ( 0 , serialized , 200 ) ;
397
719
const rawVaa = guardians . addSignatures ( published , [ 0 ] ) ;
398
720
const vaa = deserialize ( "Ntt:WormholeTransfer" , serialize ( rawVaa ) ) ;
399
- const redeemTxs = ntt . redeem ( [ vaa ] , sender , multisig ) ;
721
+ const redeemTxs = ntt . redeem ( [ vaa ] , sender , multisigTokenAuthority ) ;
400
722
try {
401
723
await signSendWait ( ctx , redeemTxs , signer ) ;
402
724
} catch ( e ) {
@@ -423,7 +745,7 @@ describe("example-native-token-transfers", () => {
423
745
payer ,
424
746
mint . publicKey ,
425
747
dest . address ,
426
- multisig ,
748
+ multisigTokenAuthority ,
427
749
1 ,
428
750
[ owner ] ,
429
751
undefined ,
0 commit comments