Skip to content

Commit 922a0c4

Browse files
committed
Add set and remove authority CLI commands
1 parent 10aba1e commit 922a0c4

1 file changed

Lines changed: 137 additions & 33 deletions

File tree

clients/js/src/cli.ts

Lines changed: 137 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import os from 'os';
55
import path from 'path';
66

77
import {
8+
Address,
89
address,
910
Commitment,
1011
createKeyPairSignerFromBytes,
@@ -25,7 +26,18 @@ import chalk from 'chalk';
2526
import { Command, Option } from 'commander';
2627
import { parse as parseYaml } from 'yaml';
2728
import { 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';
2941
import {
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

225239
program
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

234319
program
@@ -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+
305409
type Client = {
306410
rpc: Rpc<SolanaRpcApi>;
307411
rpcSubscriptions: RpcSubscriptions<SolanaRpcSubscriptionsApi>;

0 commit comments

Comments
 (0)