Skip to content

Commit

Permalink
[wip]: Use extractLastTransaction option
Browse files Browse the repository at this point in the history
  • Loading branch information
lorisleiva committed Jan 23, 2025
1 parent 73b5e48 commit d34177c
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 22 deletions.
10 changes: 6 additions & 4 deletions clients/js/src/createMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
messageFitsInOneTransaction,
MessageInstructionPlan,
PdaDetails,
sendInstructionPlan,
sendInstructionPlanAndGetMetadataResponse,
} from './internals';
import { getAccountSize, MetadataInput, MetadataResponse } from './utils';

Expand All @@ -34,9 +34,11 @@ export async function createMetadata(
context.createMessage(),
]);
const extendedInput = { ...input, ...pdaDetails, defaultMessage };
const plan = await getCreateMetadataInstructions(extendedInput);
await sendInstructionPlan(plan, context);
return { metadata: extendedInput.metadata };
return await sendInstructionPlanAndGetMetadataResponse(
await getCreateMetadataInstructions(extendedInput),
context,
extendedInput
);
}

export async function getCreateMetadataInstructions(
Expand Down
69 changes: 68 additions & 1 deletion clients/js/src/internals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@ import {
SignatureNotificationsApi,
signTransactionMessageWithSigners,
SlotNotificationsApi,
Transaction,
TransactionMessageWithBlockhashLifetime,
TransactionSigner,
} from '@solana/web3.js';
import { findMetadataPda, getWriteInstruction, SeedArgs } from './generated';
import { getProgramAuthority } from './utils';
import { getProgramAuthority, MetadataResponse } from './utils';

const TRANSACTION_SIZE_LIMIT =
1_280 -
Expand Down Expand Up @@ -291,3 +292,69 @@ export function getWriteInstructionPlan(input: {
],
};
}

export async function sendInstructionPlanAndGetMetadataResponse(
plan: InstructionPlan,
context: SendInstructionPlanContext,
input: {
metadata: Address;
defaultMessage: CompilableTransactionMessage;
extractLastTransaction?: boolean;
}
): Promise<MetadataResponse> {
const [planToSend, lastTransaction] = extractLastTransactionIfRequired(
plan,
input
);
await sendInstructionPlan(planToSend, context);
return { metadata: input.metadata, lastTransaction };
}

function extractLastTransactionIfRequired(
plan: InstructionPlan,
input: {
defaultMessage: CompilableTransactionMessage;
extractLastTransaction?: boolean;
}
): [InstructionPlan, Transaction | undefined] {
if (!input.extractLastTransaction) {
return [plan, undefined];
}
const result = extractLastMessageFromPlan(plan);
const lastMessage = getTransactionMessageFromPlan(
input.defaultMessage,
result.lastMessage
);
return [result.plan, compileTransaction(lastMessage)];
}

function extractLastMessageFromPlan(plan: InstructionPlan): {
plan: InstructionPlan;
lastMessage: MessageInstructionPlan;
} {
switch (plan.kind) {
case 'sequential':
// eslint-disable-next-line no-case-declarations
const lastMessage = plan.plans[plan.plans.length - 1];
if (lastMessage.kind !== 'message') {
throw Error(
`Expected last plan to be a message plan, got: ${lastMessage.kind}`
);
}
return {
plan: { kind: 'sequential', plans: plan.plans.slice(0, -1) },
lastMessage,
};
case 'message':
throw new Error(
'This operation can be executed without a buffer. ' +
'Therefore, the `extractLastTransaction` option is redundant. ' +
'Use the `buffer` option to force the use of a buffer.'
);
case 'parallel':
default:
throw Error(
`Cannot extract last transaction from plan kind: "${plan.kind}"`
);
}
}
10 changes: 6 additions & 4 deletions clients/js/src/updateMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
messageFitsInOneTransaction,
MessageInstructionPlan,
PdaDetails,
sendInstructionPlan,
sendInstructionPlanAndGetMetadataResponse,
} from './internals';
import { getAccountSize, MetadataInput, MetadataResponse } from './utils';

Expand All @@ -47,9 +47,11 @@ export async function updateMetadata(
...input,
...pdaDetails,
};
const plan = await getUpdateMetadataInstructions(extendedInput);
await sendInstructionPlan(plan, context);
return { metadata: extendedInput.metadata };
return await sendInstructionPlanAndGetMetadataResponse(
await getUpdateMetadataInstructions(extendedInput),
context,
extendedInput
);
}

export async function getUpdateMetadataInstructions(
Expand Down
26 changes: 15 additions & 11 deletions clients/js/src/uploadMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { fetchMaybeMetadata } from './generated';
import {
getDefaultInstructionPlanContext,
getPdaDetails,
sendInstructionPlan,
sendInstructionPlanAndGetMetadataResponse,
} from './internals';
import { getUpdateMetadataInstructions } from './updateMetadata';
import { MetadataInput } from './utils';
Expand All @@ -14,27 +14,31 @@ export async function uploadMetadata(input: MetadataInput) {
getPdaDetails(input),
context.createMessage(),
]);
const extendedInput = { ...input, ...pdaDetails, defaultMessage };
const metadataAccount = await fetchMaybeMetadata(
input.rpc,
pdaDetails.metadata
);
const extendedInput = { ...input, ...pdaDetails, defaultMessage };

// Create metadata if it doesn't exist.
if (!metadataAccount.exists) {
const plan = await getCreateMetadataInstructions(extendedInput);
await sendInstructionPlan(plan, context);
return { metadata: extendedInput.metadata };
return await sendInstructionPlanAndGetMetadataResponse(
await getCreateMetadataInstructions(extendedInput),
context,
extendedInput
);
}

// Update metadata if it exists.
if (!metadataAccount.data.mutable) {
throw new Error('Metadata account is immutable');
}
const plan = await getUpdateMetadataInstructions({
...extendedInput,
currentDataLength: BigInt(metadataAccount.data.data.length),
});
await sendInstructionPlan(plan, context);
return { metadata: extendedInput.metadata };
return await sendInstructionPlanAndGetMetadataResponse(
await getUpdateMetadataInstructions({
...extendedInput,
currentDataLength: BigInt(metadataAccount.data.data.length),
}),
context,
extendedInput
);
}
6 changes: 4 additions & 2 deletions clients/js/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,17 @@ export type MetadataInput = {
buffer?: TransactionSigner | boolean;
/**
* When using a buffer, whether to close the buffer account after the operation.
* This is only relevant when updating a metadata account since, when creating
* them, buffer accounts are transformed into metadata accounts.
* Defaults to `true`.
*/
closeBuffer?: boolean; // TODO: use this.
closeBuffer?: boolean;
/**
* When using a buffer, whether to extract the last transaction from the buffer
* and return it as serialized bytes instead of sending it.
* Defaults to `false`.
*/
extractLastTransaction?: boolean; // TODO: use this.
extractLastTransaction?: boolean;
};

export type MetadataResponse = {
Expand Down

0 comments on commit d34177c

Please sign in to comment.