@@ -40,6 +40,7 @@ describe('isolated transfer margin checks', () => {
4040
4141 let solUsd ;
4242 let ethUsd ;
43+ let btcUsd ;
4344
4445 // ammInvariant == k == x * y
4546 const mantissaSqrtScale = new BN ( 100000 ) ;
@@ -87,9 +88,10 @@ describe('isolated transfer margin checks', () => {
8788 bankrunContextWrapper
8889 ) ;
8990
90- // Create oracles for SOL and ETH
91+ // Create oracles for SOL, ETH and BTC
9192 solUsd = await mockOracleNoProgram ( bankrunContextWrapper , 100 ) ; // $100 per SOL
9293 ethUsd = await mockOracleNoProgram ( bankrunContextWrapper , 1000 ) ; // $1000 per ETH
94+ btcUsd = await mockOracleNoProgram ( bankrunContextWrapper , 100000 ) ; // $100000 per BTC
9395
9496 eventSubscriber = new EventSubscriber (
9597 bankrunContextWrapper . connection . toConnection ( ) ,
@@ -106,12 +108,13 @@ describe('isolated transfer margin checks', () => {
106108 commitment : 'confirmed' ,
107109 } ,
108110 activeSubAccountId : 0 ,
109- perpMarketIndexes : [ 0 , 1 ] ,
111+ perpMarketIndexes : [ 0 , 1 , 2 ] ,
110112 spotMarketIndexes : [ 0 ] ,
111113 subAccountIds : [ ] ,
112114 oracleInfos : [
113115 { publicKey : solUsd , source : OracleSource . PYTH } ,
114116 { publicKey : ethUsd , source : OracleSource . PYTH } ,
117+ { publicKey : btcUsd , source : OracleSource . PYTH } ,
115118 ] ,
116119 userStats : true ,
117120 accountSubscription : {
@@ -149,6 +152,16 @@ describe('isolated transfer margin checks', () => {
149152 new BN ( 1000 * PEG_PRECISION . toNumber ( ) )
150153 ) ;
151154
155+ // Initialize BTC-PERP market (index 2)
156+ await driftClient . initializePerpMarket (
157+ 2 ,
158+ btcUsd ,
159+ ammInitialBaseAssetAmount ,
160+ ammInitialQuoteAssetAmount ,
161+ periodicity ,
162+ new BN ( 100000 * PEG_PRECISION . toNumber ( ) )
163+ ) ;
164+
152165 // Set step sizes
153166 await driftClient . updatePerpMarketStepSizeAndTickSize (
154167 0 ,
@@ -160,6 +173,11 @@ describe('isolated transfer margin checks', () => {
160173 new BN ( 1 ) ,
161174 new BN ( 1 )
162175 ) ;
176+ await driftClient . updatePerpMarketStepSizeAndTickSize (
177+ 2 ,
178+ new BN ( 1 ) ,
179+ new BN ( 1 )
180+ ) ;
163181
164182 // Set margin ratios: 50% initial, 33% maintenance
165183 await driftClient . updatePerpMarketMarginRatio (
@@ -172,6 +190,11 @@ describe('isolated transfer margin checks', () => {
172190 MARGIN_PRECISION . toNumber ( ) / 2 , // 50% IM
173191 MARGIN_PRECISION . toNumber ( ) / 3 // 33% MM
174192 ) ;
193+ await driftClient . updatePerpMarketMarginRatio (
194+ 2 ,
195+ MARGIN_PRECISION . toNumber ( ) / 2 , // 50% IM
196+ MARGIN_PRECISION . toNumber ( ) / 3 // 33% MM
197+ ) ;
175198
176199 // Initialize user account
177200 await driftClient . initializeUserAccount ( ) ;
@@ -189,6 +212,7 @@ describe('isolated transfer margin checks', () => {
189212 // Restore oracle feeds to default prices so tests start with deterministic state
190213 await setFeedPriceNoProgram ( bankrunContextWrapper , 100 , solUsd ) ;
191214 await setFeedPriceNoProgram ( bankrunContextWrapper , 1000 , ethUsd ) ;
215+ await setFeedPriceNoProgram ( bankrunContextWrapper , 100000 , btcUsd ) ;
192216
193217 await driftClient . fetchAccounts ( ) ;
194218
@@ -226,6 +250,15 @@ describe('isolated transfer margin checks', () => {
226250 } catch ( e ) {
227251 // Ignore
228252 }
253+ try {
254+ await driftClient . settlePNL (
255+ await driftClient . getUserAccountPublicKey ( ) ,
256+ driftClient . getUserAccount ( ) ,
257+ 2
258+ ) ;
259+ } catch ( e ) {
260+ // Ignore
261+ }
229262
230263 await driftClient . fetchAccounts ( ) ;
231264
@@ -332,7 +365,11 @@ describe('isolated transfer margin checks', () => {
332365 ) ;
333366 assert ( false , 'Transfer should have failed - cross would fail IM' ) ;
334367 } catch ( e ) {
335- assert ( true , 'Transfer correctly failed' ) ;
368+ if ( e . message . includes ( '0x1773' ) ) {
369+ assert ( true , 'Transfer correctly failed' ) ;
370+ } else {
371+ throw e ;
372+ }
336373 } finally {
337374 restoreConsole ( ) ;
338375 }
@@ -348,7 +385,8 @@ describe('isolated transfer margin checks', () => {
348385 it ( 'should fail transfer when cross already fails IM' , async ( ) => {
349386 await resetUserState ( ) ;
350387
351- // Cross: $400, 10 SOL long @ $100 -> $500 IM required, cross fails IM
388+ // Cross: $600, 10 SOL long @ $100 -> $500 IM required
389+ // SOL price moves to $80, cross has $300 effective collateral, IM is 10 x 70 x .5 = $350
352390 // Transfer $100 to isolated. Should fail (cross already below IM)
353391
354392 await driftClient . deposit (
@@ -361,8 +399,8 @@ describe('isolated transfer margin checks', () => {
361399 new BN ( 10 * 10 ** 9 ) ,
362400 0
363401 ) ;
364- // 10 SOL @ 100->80 : loss 200, effective 400 , need 500 IM
365- await setFeedPriceNoProgram ( bankrunContextWrapper , 80 , solUsd ) ;
402+ // 10 SOL @ 100->70 : loss 200, effective 300 collateral , need 350 IM
403+ await setFeedPriceNoProgram ( bankrunContextWrapper , 70 , solUsd ) ;
366404 await driftClient . fetchAccounts ( ) ;
367405
368406 const restoreConsole = suppressConsole ( ) ;
@@ -377,7 +415,11 @@ describe('isolated transfer margin checks', () => {
377415 ) ;
378416 assert ( false , 'Transfer should have failed - cross fails IM' ) ;
379417 } catch ( e ) {
380- assert ( true , 'Transfer correctly failed' ) ;
418+ if ( e . message . includes ( '0x1773' ) ) {
419+ assert ( true , 'Transfer correctly failed' ) ;
420+ } else {
421+ throw e ;
422+ }
381423 } finally {
382424 restoreConsole ( ) ;
383425 }
@@ -415,7 +457,11 @@ describe('isolated transfer margin checks', () => {
415457 ) ;
416458 assert ( false , 'Transfer should have failed - cross fails MM' ) ;
417459 } catch ( e ) {
418- assert ( true , 'Transfer correctly failed' ) ;
460+ if ( e . message . includes ( '0x1773' ) ) {
461+ assert ( true , 'Transfer correctly failed' ) ;
462+ } else {
463+ throw e ;
464+ }
419465 } finally {
420466 restoreConsole ( ) ;
421467 }
@@ -426,7 +472,7 @@ describe('isolated transfer margin checks', () => {
426472 it ( 'should fail transfer when other isolated fails MM' , async ( ) => {
427473 await resetUserState ( ) ;
428474
429- // Cross: $800 , no cross positions. Other isolated: SOL 10 long with $300 collateral,
475+ // Cross: $2000 , no cross positions. Other isolated: SOL 10 long with $600 collateral,
430476 // SOL at 70 -> effective $300 < $333 MM. Transfer $200 to isolated ETH.
431477 // Cross after $600, no IM. But other isolated fails MM -> FAIL
432478
@@ -435,18 +481,17 @@ describe('isolated transfer margin checks', () => {
435481 0 ,
436482 userUSDCAccount . publicKey
437483 ) ;
438- await driftClient . depositIntoIsolatedPerpPosition (
484+ await driftClient . transferIsolatedPerpPositionDeposit (
439485 new BN ( 600 * 10 ** 6 ) ,
440- 0 ,
441- userUSDCAccount . publicKey
486+ 0
442487 ) ;
443488 await driftClient . openPosition (
444489 PositionDirection . LONG ,
445490 new BN ( 10 * 10 ** 9 ) ,
446491 0
447492 ) ;
448493 // SOL at 70: 10*(100-70)=300 loss, 600-300=300 < 333 MM
449- await setFeedPriceNoProgram ( bankrunContextWrapper , 70 , solUsd ) ;
494+ await setFeedPriceNoProgram ( bankrunContextWrapper , 50 , solUsd ) ;
450495 await driftClient . fetchAccounts ( ) ;
451496
452497 // Cross has 1400, isolated SOL has 300 effective (fails MM)
@@ -462,7 +507,11 @@ describe('isolated transfer margin checks', () => {
462507 ) ;
463508 assert ( false , 'Transfer should have failed - other isolated fails MM' ) ;
464509 } catch ( e ) {
465- assert ( true , 'Transfer correctly failed' ) ;
510+ if ( e . message . includes ( '0x1773' ) ) {
511+ assert ( true , 'Transfer correctly failed' ) ;
512+ } else {
513+ throw e ;
514+ }
466515 } finally {
467516 restoreConsole ( ) ;
468517 }
@@ -520,12 +569,12 @@ describe('isolated transfer margin checks', () => {
520569 it ( 'should fail when both cross would fail IM after and other isolated fails MM' , async ( ) => {
521570 await resetUserState ( ) ;
522571
523- // Cross: $700 , 10 SOL long ($500 IM). Other isolated: ETH 1 long with $300 ,
572+ // Cross: $1000 , 10 SOL long ($500 IM). Other isolated: ETH 1 long with $600 ,
524573 // ETH at 700 -> effective $300 < $333 MM. Transfer $250.
525574 // Cross after $450 < $500 IM. Other isolated fails MM. FAIL
526575
527576 await driftClient . deposit (
528- new BN ( 1000 * 10 ** 6 ) ,
577+ new BN ( 700 * 10 ** 6 ) ,
529578 0 ,
530579 userUSDCAccount . publicKey
531580 ) ;
@@ -544,26 +593,30 @@ describe('isolated transfer margin checks', () => {
544593 new BN ( 1 * 10 ** 9 ) ,
545594 1
546595 ) ;
547- // Cross: 1000 , 10 SOL @ 100. Sol at 100, cross IM 500, cross ok.
596+ // Cross: 700 , 10 SOL @ 100. Sol at 100, cross IM 500, cross ok.
548597 // ETH at 700: 1*(1000-700)=300 loss, 600-300=300 < 333 MM
549598 await setFeedPriceNoProgram ( bankrunContextWrapper , 700 , ethUsd ) ;
550599 await driftClient . fetchAccounts ( ) ;
551600
552- const restoreConsole = suppressConsole ( ) ;
601+ // const restoreConsole = suppressConsole();
553602 try {
554603 await driftClient . transferIsolatedPerpPositionDeposit (
555604 new BN ( 250 * 10 ** 6 ) ,
556- 1 ,
605+ 2 ,
557606 undefined ,
558607 undefined ,
559608 undefined ,
560609 true
561610 ) ;
562611 assert ( false , 'Transfer should have failed' ) ;
563612 } catch ( e ) {
564- assert ( true , 'Transfer correctly failed' ) ;
613+ if ( e . message . includes ( '0x1773' ) ) {
614+ assert ( true , 'Transfer correctly failed' ) ;
615+ } else {
616+ throw e ;
617+ }
565618 } finally {
566- restoreConsole ( ) ;
619+ // restoreConsole();
567620 }
568621 } ) ;
569622 } ) ;
@@ -572,12 +625,12 @@ describe('isolated transfer margin checks', () => {
572625 it ( 'should fail when cross would fail IM after even if other isolated passes MM' , async ( ) => {
573626 await resetUserState ( ) ;
574627
575- // Cross: $700, 10 SOL long ($500 IM). Other isolated: ETH 1 long with $400 ,
576- // ETH at 800 -> effective $400 > $333 MM. Transfer $250.
577- // Cross after $450 < $500 IM -> FAIL (other isolated is fine)
628+ // Cross: $700, 10 SOL long ($500 IM). Other isolated: ETH 1 long with $600 ,
629+ // ETH at 800 -> $200 loss, effective collateral $400 > $333 MM. Transfer $250.
630+ // Cross after transfer is $450 < $500 IM -> FAIL (other isolated is fine)
578631
579632 await driftClient . deposit (
580- new BN ( 1000 * 10 ** 6 ) ,
633+ new BN ( 700 * 10 ** 6 ) ,
581634 0 ,
582635 userUSDCAccount . publicKey
583636 ) ;
@@ -612,19 +665,23 @@ describe('isolated transfer margin checks', () => {
612665 ) ;
613666 assert ( false , 'Transfer should have failed - cross would fail IM' ) ;
614667 } catch ( e ) {
615- assert ( true , 'Transfer correctly failed' ) ;
668+ if ( e . message . includes ( '0x1773' ) ) {
669+ assert ( true , 'Transfer correctly failed' ) ;
670+ } else {
671+ throw e ;
672+ }
616673 } finally {
617674 restoreConsole ( ) ;
618675 }
619676 } ) ;
620677 } ) ;
621678
622679 describe ( 'Multi-isolated: one passes MM one fails blocks transfer' , ( ) => {
623- it ( 'should fail when any of multiple other isolated fails MM' , async ( ) => {
680+ it . only ( 'should fail when any of multiple other isolated fails MM' , async ( ) => {
624681 await resetUserState ( ) ;
625682
626683 // Cross: $2000. Isolated SOL: 10 long, $400 collateral at 80 -> passes MM.
627- // Isolated ETH: 1 long, $300 collateral at 700 -> fails MM ($333).
684+ // Isolated ETH: 1 long, $300 collateral at 600 -> fails MM ($333).
628685 // Transfer $100 to... we need a third market. We only have SOL and ETH.
629686 // So: SOL isolated passes, ETH isolated fails. Transfer cross->ETH isolated (adding to failing one)
630687 // actually that would improve ETH. Let me reconsider.
@@ -654,9 +711,9 @@ describe('isolated transfer margin checks', () => {
654711 new BN ( 1 * 10 ** 9 ) ,
655712 1
656713 ) ;
657- // SOL at 80: passes MM. ETH at 700 : fails MM
714+ // SOL at 80: passes MM. ETH at 600 : fails MM
658715 await setFeedPriceNoProgram ( bankrunContextWrapper , 80 , solUsd ) ;
659- await setFeedPriceNoProgram ( bankrunContextWrapper , 700 , ethUsd ) ;
716+ await setFeedPriceNoProgram ( bankrunContextWrapper , 600 , ethUsd ) ;
660717 await driftClient . fetchAccounts ( ) ;
661718
662719 const restoreConsole = suppressConsole ( ) ;
@@ -671,7 +728,11 @@ describe('isolated transfer margin checks', () => {
671728 ) ;
672729 assert ( false , 'Transfer should fail - ETH isolated fails MM' ) ;
673730 } catch ( e ) {
674- assert ( true , 'Transfer correctly failed' ) ;
731+ if ( e . message . includes ( '0x1773' ) ) {
732+ assert ( true , 'Transfer correctly failed' ) ;
733+ } else {
734+ throw e ;
735+ }
675736 } finally {
676737 restoreConsole ( ) ;
677738 }
0 commit comments