77 isSolanaError ,
88 pipe ,
99 SOLANA_ERROR__INSTRUCTION_ERROR__INVALID_ACCOUNT_DATA ,
10+ SOLANA_ERROR__INSTRUCTION_ERROR__INVALID_REALLOC ,
1011} from '@solana/web3.js' ;
1112import test from 'ava' ;
1213import {
@@ -15,6 +16,7 @@ import {
1516 Encoding ,
1617 fetchMetadata ,
1718 Format ,
19+ getExtendInstruction ,
1820 getSetAuthorityInstruction ,
1921 getSetDataInstruction ,
2022 getSetImmutableInstruction ,
@@ -28,6 +30,8 @@ import {
2830 createKeypairBuffer ,
2931 createNonCanonicalMetadata ,
3032 generateKeyPairSignerWithSol ,
33+ getRentWithoutHeader ,
34+ REALLOC_LIMIT ,
3135 signAndSendTransaction ,
3236} from './_setup' ;
3337
@@ -53,10 +57,10 @@ test('the program authority of a canonical metadata account can update its data
5357
5458 // And given we fund the metadata account for the extra space needed for the new data.
5559 const newData = getUtf8Encoder ( ) . encode ( 'https://example.com/new-data.json' ) ;
56- const extraSpace = BigInt ( newData . length - originalData . length ) ;
57- const extraRent = await client . rpc
58- . getMinimumBalanceForRentExemption ( extraSpace )
59- . send ( ) ;
60+ const extraRent = await getRentWithoutHeader (
61+ client ,
62+ newData . length - originalData . length
63+ ) ;
6064 const transferIx = getTransferSolInstruction ( {
6165 source : authority ,
6266 destination : metadata ,
@@ -126,10 +130,10 @@ test('the explicit authority of a canonical metadata account can update its data
126130
127131 // And given we fund the metadata account for the extra space needed for the new data.
128132 const newData = getUtf8Encoder ( ) . encode ( 'https://example.com/new-data.json' ) ;
129- const extraSpace = BigInt ( newData . length - originalData . length ) ;
130- const extraRent = await client . rpc
131- . getMinimumBalanceForRentExemption ( extraSpace )
132- . send ( ) ;
133+ const extraRent = await getRentWithoutHeader (
134+ client ,
135+ newData . length - originalData . length
136+ ) ;
133137 const transferIx = getTransferSolInstruction ( {
134138 source : authority ,
135139 destination : metadata ,
@@ -190,10 +194,10 @@ test('the authority of a non-canonical metadata account can update its data usin
190194
191195 // And given we fund the metadata account for the extra space needed for the new data.
192196 const newData = getUtf8Encoder ( ) . encode ( 'https://example.com/new-data.json' ) ;
193- const extraSpace = BigInt ( newData . length - originalData . length ) ;
194- const extraRent = await client . rpc
195- . getMinimumBalanceForRentExemption ( extraSpace )
196- . send ( ) ;
197+ const extraRent = await getRentWithoutHeader (
198+ client ,
199+ newData . length - originalData . length
200+ ) ;
197201 const transferIx = getTransferSolInstruction ( {
198202 source : authority ,
199203 destination : metadata ,
@@ -256,10 +260,10 @@ test('the program authority of a canonical metadata account can update its data
256260 } ) ;
257261
258262 // When the program authority updates the data of the metadata account using the buffer.
259- const extraSize = BigInt ( newData . length - originalData . length ) ;
260- const extraRent = await client . rpc
261- . getMinimumBalanceForRentExemption ( extraSize )
262- . send ( ) ;
263+ const extraRent = await getRentWithoutHeader (
264+ client ,
265+ newData . length - originalData . length
266+ ) ;
263267 const fundMetadataIx = getTransferSolInstruction ( {
264268 source : authority ,
265269 destination : metadata ,
@@ -334,10 +338,10 @@ test('the explicit authority of a canonical metadata account can update its data
334338 } ) ;
335339
336340 // When the explicit authority updates the data of the metadata account using the buffer.
337- const extraSize = BigInt ( newData . length - originalData . length ) ;
338- const extraRent = await client . rpc
339- . getMinimumBalanceForRentExemption ( extraSize )
340- . send ( ) ;
341+ const extraRent = await getRentWithoutHeader (
342+ client ,
343+ newData . length - originalData . length
344+ ) ;
341345 const fundMetadataIx = getTransferSolInstruction ( {
342346 source : authority ,
343347 destination : metadata ,
@@ -402,10 +406,10 @@ test('the authority of a non-canonical metadata account can update its data usin
402406 } ) ;
403407
404408 // When the metadata authority updates the account using the pre-allocated buffer.
405- const extraSize = BigInt ( newData . length - originalData . length ) ;
406- const extraRent = await client . rpc
407- . getMinimumBalanceForRentExemption ( extraSize )
408- . send ( ) ;
409+ const extraRent = await getRentWithoutHeader (
410+ client ,
411+ newData . length - originalData . length
412+ ) ;
409413 const fundMetadataIx = getTransferSolInstruction ( {
410414 source : authority ,
411415 destination : metadata ,
@@ -540,6 +544,90 @@ test('an immutable non-canonical metadata account cannot be updated', async (t)
540544 ) ;
541545} ) ;
542546
543- test . todo (
544- 'The metadata account needs to be extended for data changes that add more than 1KB'
545- ) ;
547+ test ( 'The metadata account needs to be extended for data changes that add more than 1KB' , async ( t ) => {
548+ // Given the following authority and deployed program.
549+ const client = createDefaultSolanaClient ( ) ;
550+ const authority = await generateKeyPairSignerWithSol ( client ) ;
551+ const program = address ( 'TokenKEGQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA' ) ;
552+
553+ // And the following initialized metadata account with 200 bytes of data.
554+ const originalData = getUtf8Encoder ( ) . encode ( 'x' . repeat ( 200 ) ) ;
555+ const [ metadata ] = await createNonCanonicalMetadata ( client , {
556+ authority,
557+ program,
558+ seed : 'dummy' ,
559+ encoding : Encoding . None ,
560+ compression : Compression . None ,
561+ format : Format . None ,
562+ dataSource : DataSource . Direct ,
563+ data : originalData ,
564+ } ) ;
565+
566+ // And the following pre-allocated buffer account with written data.
567+ const newData = getUtf8Encoder ( ) . encode (
568+ 'x' . repeat ( originalData . length + REALLOC_LIMIT + 1 )
569+ ) ;
570+ const buffer = await createKeypairBuffer ( client , {
571+ payer : authority ,
572+ data : newData ,
573+ } ) ;
574+
575+ // And given the following instructions to fund extra rent, extend extra space and update the data.
576+ const extraSize = newData . length - originalData . length ;
577+ const extraRent = await getRentWithoutHeader ( client , extraSize ) ;
578+ const transferIx = getTransferSolInstruction ( {
579+ source : authority ,
580+ destination : metadata ,
581+ amount : extraRent ,
582+ } ) ;
583+ const extendIx = getExtendInstruction ( {
584+ account : metadata ,
585+ authority,
586+ length : REALLOC_LIMIT ,
587+ } ) ;
588+ const setDataIx = getSetDataInstruction ( {
589+ metadata,
590+ authority,
591+ program,
592+ encoding : Encoding . Utf8 ,
593+ compression : Compression . Gzip ,
594+ format : Format . Json ,
595+ dataSource : DataSource . Url ,
596+ buffer : buffer . address ,
597+ } ) ;
598+
599+ // When we try to update the data without extending the account.
600+ const promise = pipe (
601+ await createDefaultTransaction ( client , authority ) ,
602+ ( tx ) => appendTransactionMessageInstructions ( [ transferIx , setDataIx ] , tx ) ,
603+ ( tx ) => signAndSendTransaction ( client , tx )
604+ ) ;
605+
606+ // Then we expect a program error.
607+ const error = await t . throwsAsync ( promise ) ;
608+ t . true ( isSolanaError ( error ) ) ;
609+ t . true (
610+ isSolanaError ( error . cause , SOLANA_ERROR__INSTRUCTION_ERROR__INVALID_REALLOC )
611+ ) ;
612+
613+ // But when we extend the account and try again.
614+ await pipe (
615+ await createDefaultTransaction ( client , authority ) ,
616+ ( tx ) =>
617+ appendTransactionMessageInstructions (
618+ [ transferIx , extendIx , setDataIx ] ,
619+ tx
620+ ) ,
621+ ( tx ) => signAndSendTransaction ( client , tx )
622+ ) ;
623+
624+ // Then we expect the metadata account have the new data.
625+ const account = await fetchMetadata ( client . rpc , metadata ) ;
626+ t . like ( account . data , < Metadata > {
627+ encoding : Encoding . Utf8 ,
628+ compression : Compression . Gzip ,
629+ format : Format . Json ,
630+ dataSource : DataSource . Url ,
631+ data : newData ,
632+ } ) ;
633+ } ) ;
0 commit comments