@@ -5,6 +5,7 @@ import os from 'os';
55import path from 'path' ;
66
77import {
8+ Address ,
89 address ,
910 Commitment ,
1011 createKeyPairSignerFromBytes ,
@@ -25,7 +26,18 @@ import chalk from 'chalk';
2526import { Command , Option } from 'commander' ;
2627import { parse as parseYaml } from 'yaml' ;
2728import { downloadMetadata } from './downloadMetadata' ;
28- import { Compression , Encoding , Format } from './generated' ;
29+ import {
30+ Compression ,
31+ Encoding ,
32+ Format ,
33+ getSetAuthorityInstruction ,
34+ } from './generated' ;
35+ import {
36+ getComputeUnitInstructions ,
37+ getDefaultMessageFactory ,
38+ getMetadataInstructionPlanExecutor ,
39+ getPdaDetails ,
40+ } from './internals' ;
2941import {
3042 packDirectData ,
3143 PackedData ,
@@ -131,8 +143,10 @@ program
131143 seed : string ,
132144 programAddress : string ,
133145 content : string | undefined ,
134- options : UploadOptions
146+ _ ,
147+ cmd : Command
135148 ) => {
149+ const options = cmd . optsWithGlobals ( ) as UploadOptions ;
136150 const client = getClient ( options ) ;
137151 const [ keypair , payer ] = await getKeyPairSigners ( options , client . configs ) ;
138152 const { authority : programAuthority } = await getProgramAuthority (
@@ -154,6 +168,7 @@ program
154168 format : getFormat ( options ) ,
155169 buffer : options . bufferOnly ? true : undefined ,
156170 extractLastTransaction : options . bufferOnly ,
171+ closeBuffer : true ,
157172 priorityFees : options . priorityFees
158173 ? ( BigInt ( options . priorityFees ) as MicroLamports )
159174 : undefined ,
@@ -192,43 +207,113 @@ program
192207 'When provided, a non-canonical metadata account will be downloaded using the provided address or the active keypair as the authority.' ,
193208 false
194209 )
195- . action (
196- async ( seed : string , programAddress : string , options : DownloadOptions ) => {
197- const client = getClient ( options ) ;
198- const authority =
199- options . thirdParty === true
200- ? ( await getKeyPairSigners ( options , client . configs ) ) [ 0 ] . address
201- : options . thirdParty
202- ? address ( options . thirdParty )
203- : undefined ;
204- try {
205- const content = await downloadMetadata (
206- client . rpc ,
207- address ( programAddress ) ,
208- seed ,
209- authority
210- ) ;
211- if ( options . output ) {
212- fs . mkdirSync ( path . dirname ( options . output ) , { recursive : true } ) ;
213- fs . writeFileSync ( options . output , content ) ;
214- logSuccess ( `Metadata content saved to ${ chalk . bold ( options . output ) } ` ) ;
215- } else {
216- console . log ( content ) ;
217- }
218- } catch ( error ) {
219- if ( isSolanaError ( error ) ) logErrorAndExit ( error . message ) ;
220- throw error ;
210+ . action ( async ( seed : string , programAddress : string , _ , cmd : Command ) => {
211+ const options = cmd . optsWithGlobals ( ) as DownloadOptions ;
212+ const client = getClient ( options ) ;
213+ const authority =
214+ options . thirdParty === true
215+ ? ( await getKeyPairSigners ( options , client . configs ) ) [ 0 ] . address
216+ : options . thirdParty
217+ ? address ( options . thirdParty )
218+ : undefined ;
219+ try {
220+ const content = await downloadMetadata (
221+ client . rpc ,
222+ address ( programAddress ) ,
223+ seed ,
224+ authority
225+ ) ;
226+ if ( options . output ) {
227+ fs . mkdirSync ( path . dirname ( options . output ) , { recursive : true } ) ;
228+ fs . writeFileSync ( options . output , content ) ;
229+ logSuccess ( `Metadata content saved to ${ chalk . bold ( options . output ) } ` ) ;
230+ } else {
231+ console . log ( content ) ;
221232 }
233+ } catch ( error ) {
234+ if ( isSolanaError ( error ) ) logErrorAndExit ( error . message ) ;
235+ throw error ;
222236 }
223- ) ;
237+ } ) ;
224238
225239program
226- . command ( 'set-authority <seed> <program-id>' )
240+ . command ( 'set-authority <seed> <program-id> <new-authority> ' )
227241 . description (
228- 'Set, update or remove an additional authority on canonical metadata accounts'
242+ 'Set or update an additional authority on canonical metadata accounts'
229243 )
230- . action ( async ( ) => {
231- // TODO
244+ . action (
245+ async (
246+ seed : string ,
247+ programAddress : string ,
248+ newAuthority : string ,
249+ _ ,
250+ cmd : Command
251+ ) => {
252+ const options = cmd . optsWithGlobals ( ) as GlobalOptions ;
253+ const client = getClient ( options ) ;
254+ const [ keypair , payer ] = await getKeyPairSigners ( options , client . configs ) ;
255+ const program = address ( programAddress ) ;
256+ const { metadata, programData } = await getPdaDetails ( {
257+ rpc : client . rpc ,
258+ program,
259+ authority : keypair ,
260+ seed,
261+ } ) ;
262+ const planExecutor = await getCliPlanExecutor ( client , payer , metadata ) ;
263+ await planExecutor ( {
264+ kind : 'message' ,
265+ instructions : [
266+ ...getComputeUnitInstructions ( {
267+ computeUnitPrice : options . priorityFees
268+ ? ( BigInt ( options . priorityFees ) as MicroLamports )
269+ : undefined ,
270+ computeUnitLimit : 'simulated' ,
271+ } ) ,
272+ getSetAuthorityInstruction ( {
273+ account : metadata ,
274+ authority : keypair ,
275+ newAuthority : address ( newAuthority ) ,
276+ program,
277+ programData,
278+ } ) ,
279+ ] ,
280+ } ) ;
281+ }
282+ ) ;
283+
284+ program
285+ . command ( 'remove-authority <seed> <program-id>' )
286+ . description ( 'Remove the additional authority on canonical metadata accounts' )
287+ . action ( async ( seed : string , programAddress : string , _ , cmd : Command ) => {
288+ const options = cmd . optsWithGlobals ( ) as GlobalOptions ;
289+ const client = getClient ( options ) ;
290+ const [ keypair , payer ] = await getKeyPairSigners ( options , client . configs ) ;
291+ const program = address ( programAddress ) ;
292+ const { metadata, programData } = await getPdaDetails ( {
293+ rpc : client . rpc ,
294+ program,
295+ authority : keypair ,
296+ seed,
297+ } ) ;
298+ const planExecutor = await getCliPlanExecutor ( client , payer , metadata ) ;
299+ await planExecutor ( {
300+ kind : 'message' ,
301+ instructions : [
302+ ...getComputeUnitInstructions ( {
303+ computeUnitPrice : options . priorityFees
304+ ? ( BigInt ( options . priorityFees ) as MicroLamports )
305+ : undefined ,
306+ computeUnitLimit : 'simulated' ,
307+ } ) ,
308+ getSetAuthorityInstruction ( {
309+ account : metadata ,
310+ authority : keypair ,
311+ newAuthority : null ,
312+ program,
313+ programData,
314+ } ) ,
315+ ] ,
316+ } ) ;
232317 } ) ;
233318
234319program
@@ -302,6 +387,25 @@ async function getKeyPairSignerFromPath(
302387 return await createKeyPairSignerFromBytes ( keypairData ) ;
303388}
304389
390+ async function getCliPlanExecutor (
391+ client : Client ,
392+ payer : KeyPairSigner ,
393+ metadata : Address
394+ ) {
395+ const getDefaultMessage = getDefaultMessageFactory ( {
396+ rpc : client . rpc ,
397+ payer,
398+ } ) ;
399+ const [ defaultMessage ] = await Promise . all ( [ getDefaultMessage ( ) ] ) ;
400+ return getMetadataInstructionPlanExecutor ( {
401+ ...client ,
402+ getDefaultMessage,
403+ payer,
404+ metadata,
405+ defaultMessage,
406+ } ) ;
407+ }
408+
305409type Client = {
306410 rpc : Rpc < SolanaRpcApi > ;
307411 rpcSubscriptions : RpcSubscriptions < SolanaRpcSubscriptionsApi > ;
0 commit comments