Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .changeset/little-paws-trade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
'@solana/instruction-plans': major
---

Add a new `planType` property to all `InstructionPlan`, `TransactionPlan`, and `TransactionPlanResult` types to distinguish them from each other at runtime. This property is a string literal with the value `'instructionPlan'`, `'transactionPlan'`, or `'transactionPlanResult'` respectively. It also adds new type guard functions that make use of that new property: `isInstructionPlan`, `isTransactionPlan`, and `isTransactionPlanResult`.

**BREAKING CHANGES**

**`InstructionPlan`, `TransactionPlan`, and `TransactionPlanResult` type guards updated.** All factories have been updated to add the new `planType` property but any custom instantiation of these types must be updated to include it as well.

```diff
const myInstructionPlan: InstructionPlan = {
kind: 'parallel',
plans: [/* ... */],
+ planType: 'instructionPlan',
};

const myTransactionPlan: TransactionPlan = {
kind: 'parallel',
plans: [/* ... */],
+ planType: 'transactionPlan',
};

const myTransactionPlanResult: TransactionPlanResult = {
kind: 'parallel',
plans: [/* ... */],
+ planType: 'transactionPlanResult',
};
```
2 changes: 2 additions & 0 deletions packages/instruction-plans/src/__tests__/__setup__.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export function createSingleInstructionAtATimeMessagePackerInstructionPlan(
};
},
kind: 'messagePacker',
planType: 'instructionPlan',
});
}

Expand Down Expand Up @@ -139,5 +140,6 @@ export function createMessagePackerInstructionPlan(
};
},
kind: 'messagePacker',
planType: 'instructionPlan',
});
}
54 changes: 52 additions & 2 deletions packages/instruction-plans/src/__tests__/instruction-plan-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
getLinearMessagePackerInstructionPlan,
getMessagePackerInstructionPlanFromInstructions,
getReallocMessagePackerInstructionPlan,
isInstructionPlan,
isMessagePackerInstructionPlan,
isNonDivisibleSequentialInstructionPlan,
isParallelInstructionPlan,
Expand All @@ -55,7 +56,7 @@ describe('singleInstructionPlan', () => {
it('creates SingleInstructionPlan objects', () => {
const instruction = createInstruction('A');
const plan = singleInstructionPlan(instruction);
expect(plan).toStrictEqual({ instruction, kind: 'single' });
expect(plan).toStrictEqual({ instruction, kind: 'single', planType: 'instructionPlan' });
});
it('freezes created SingleInstructionPlan objects', () => {
const instruction = createInstruction('A');
Expand All @@ -74,6 +75,7 @@ describe('parallelInstructionPlan', () => {
]);
expect(plan).toStrictEqual({
kind: 'parallel',
planType: 'instructionPlan',
plans: [singleInstructionPlan(instructionA), singleInstructionPlan(instructionB)],
});
});
Expand All @@ -83,6 +85,7 @@ describe('parallelInstructionPlan', () => {
const plan = parallelInstructionPlan([instructionA, instructionB]);
expect(plan).toStrictEqual({
kind: 'parallel',
planType: 'instructionPlan',
plans: [singleInstructionPlan(instructionA), singleInstructionPlan(instructionB)],
});
});
Expand All @@ -93,9 +96,14 @@ describe('parallelInstructionPlan', () => {
const plan = parallelInstructionPlan([instructionA, parallelInstructionPlan([instructionB, instructionC])]);
expect(plan).toStrictEqual({
kind: 'parallel',
planType: 'instructionPlan',
plans: [
singleInstructionPlan(instructionA),
{ kind: 'parallel', plans: [singleInstructionPlan(instructionB), singleInstructionPlan(instructionC)] },
{
kind: 'parallel',
planType: 'instructionPlan',
plans: [singleInstructionPlan(instructionB), singleInstructionPlan(instructionC)],
},
],
});
});
Expand All @@ -118,6 +126,7 @@ describe('sequentialInstructionPlan', () => {
expect(plan).toStrictEqual({
divisible: true,
kind: 'sequential',
planType: 'instructionPlan',
plans: [singleInstructionPlan(instructionA), singleInstructionPlan(instructionB)],
});
});
Expand All @@ -128,6 +137,7 @@ describe('sequentialInstructionPlan', () => {
expect(plan).toStrictEqual({
divisible: true,
kind: 'sequential',
planType: 'instructionPlan',
plans: [singleInstructionPlan(instructionA), singleInstructionPlan(instructionB)],
});
});
Expand All @@ -139,11 +149,13 @@ describe('sequentialInstructionPlan', () => {
expect(plan).toStrictEqual({
divisible: true,
kind: 'sequential',
planType: 'instructionPlan',
plans: [
singleInstructionPlan(instructionA),
{
divisible: true,
kind: 'sequential',
planType: 'instructionPlan',
plans: [singleInstructionPlan(instructionB), singleInstructionPlan(instructionC)],
},
],
Expand All @@ -168,6 +180,7 @@ describe('nonDivisibleSequentialInstructionPlan', () => {
expect(plan).toStrictEqual({
divisible: false,
kind: 'sequential',
planType: 'instructionPlan',
plans: [singleInstructionPlan(instructionA), singleInstructionPlan(instructionB)],
});
});
Expand All @@ -178,6 +191,7 @@ describe('nonDivisibleSequentialInstructionPlan', () => {
expect(plan).toStrictEqual({
divisible: false,
kind: 'sequential',
planType: 'instructionPlan',
plans: [singleInstructionPlan(instructionA), singleInstructionPlan(instructionB)],
});
});
Expand All @@ -192,11 +206,13 @@ describe('nonDivisibleSequentialInstructionPlan', () => {
expect(plan).toStrictEqual({
divisible: false,
kind: 'sequential',
planType: 'instructionPlan',
plans: [
singleInstructionPlan(instructionA),
{
divisible: false,
kind: 'sequential',
planType: 'instructionPlan',
plans: [singleInstructionPlan(instructionB), singleInstructionPlan(instructionC)],
},
],
Expand Down Expand Up @@ -925,3 +941,37 @@ describe('flattenInstructionPlan', () => {
]);
});
});

describe('isInstructionPlan', () => {
it('returns true for SingleInstructionPlan', () => {
expect(isInstructionPlan(singleInstructionPlan(createInstruction('A')))).toBe(true);
});
it('returns true for ParallelInstructionPlan', () => {
expect(isInstructionPlan(parallelInstructionPlan([]))).toBe(true);
});
it('returns true for SequentialInstructionPlan', () => {
expect(isInstructionPlan(sequentialInstructionPlan([]))).toBe(true);
});
it('returns true for non-divisible SequentialInstructionPlan', () => {
expect(isInstructionPlan(nonDivisibleSequentialInstructionPlan([]))).toBe(true);
});
it('returns true for MessagePackerInstructionPlan', () => {
expect(isInstructionPlan(getMessagePackerInstructionPlanFromInstructions([]))).toBe(true);
});
it('returns false for non-objects', () => {
expect(isInstructionPlan(null)).toBe(false);
expect(isInstructionPlan(undefined)).toBe(false);
expect(isInstructionPlan('string')).toBe(false);
expect(isInstructionPlan(123)).toBe(false);
expect(isInstructionPlan(true)).toBe(false);
});
it('returns false for objects without planType', () => {
expect(isInstructionPlan({ kind: 'single' })).toBe(false);
});
it('returns false for objects with wrong planType', () => {
expect(isInstructionPlan({ planType: 123 })).toBe(false);
expect(isInstructionPlan({ planType: null })).toBe(false);
expect(isInstructionPlan({ planType: 'transactionPlan' })).toBe(false);
expect(isInstructionPlan({ planType: 'transactionPlanResult' })).toBe(false);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
isSingleTransactionPlanResult,
isSuccessfulSingleTransactionPlanResult,
isSuccessfulTransactionPlanResult,
isTransactionPlanResult,
nonDivisibleSequentialTransactionPlanResult,
parallelTransactionPlanResult,
sequentialTransactionPlanResult,
Expand All @@ -49,6 +50,7 @@ describe('successfulSingleTransactionPlanResultFromTransaction', () => {
expect(result).toEqual({
context: { signature: 'A', transaction: transactionA },
kind: 'single',
planType: 'transactionPlanResult',
plannedMessage: messageA,
status: 'successful',
});
Expand All @@ -61,6 +63,7 @@ describe('successfulSingleTransactionPlanResultFromTransaction', () => {
expect(result).toEqual({
context: { ...context, signature: 'A', transaction: transactionA },
kind: 'single',
planType: 'transactionPlanResult',
plannedMessage: messageA,
status: 'successful',
});
Expand All @@ -87,6 +90,7 @@ describe('successfulSingleTransactionPlanResult', () => {
expect(result).toEqual({
context: { signature: 'A' },
kind: 'single',
planType: 'transactionPlanResult',
plannedMessage: messageA,
status: 'successful',
});
Expand All @@ -99,6 +103,7 @@ describe('successfulSingleTransactionPlanResult', () => {
expect(result).toEqual({
context: { ...context, signature: 'A' },
kind: 'single',
planType: 'transactionPlanResult',
plannedMessage: messageA,
status: 'successful',
});
Expand Down Expand Up @@ -126,6 +131,7 @@ describe('failedSingleTransactionPlanResult', () => {
context: {},
error,
kind: 'single',
planType: 'transactionPlanResult',
plannedMessage: messageA,
status: 'failed',
});
Expand All @@ -151,6 +157,7 @@ describe('canceledSingleTransactionPlanResult', () => {
expect(result).toEqual({
context: {},
kind: 'single',
planType: 'transactionPlanResult',
plannedMessage: messageA,
status: 'canceled',
});
Expand All @@ -174,6 +181,7 @@ describe('parallelTransactionPlanResult', () => {
const result = parallelTransactionPlanResult([planA, planB]);
expect(result).toEqual({
kind: 'parallel',
planType: 'transactionPlanResult',
plans: [planA, planB],
});
});
Expand All @@ -184,7 +192,8 @@ describe('parallelTransactionPlanResult', () => {
const result = parallelTransactionPlanResult([planA, parallelTransactionPlanResult([planB, planC])]);
expect(result).toEqual({
kind: 'parallel',
plans: [planA, { kind: 'parallel', plans: [planB, planC] }],
planType: 'transactionPlanResult',
plans: [planA, { kind: 'parallel', planType: 'transactionPlanResult', plans: [planB, planC] }],
});
});
it('freezes created ParallelTransactionPlanResult objects', () => {
Expand All @@ -203,6 +212,7 @@ describe('sequentialTransactionPlanResult', () => {
expect(result).toEqual({
divisible: true,
kind: 'sequential',
planType: 'transactionPlanResult',
plans: [planA, planB],
});
});
Expand All @@ -214,7 +224,11 @@ describe('sequentialTransactionPlanResult', () => {
expect(result).toEqual({
divisible: true,
kind: 'sequential',
plans: [planA, { divisible: true, kind: 'sequential', plans: [planB, planC] }],
planType: 'transactionPlanResult',
plans: [
planA,
{ divisible: true, kind: 'sequential', planType: 'transactionPlanResult', plans: [planB, planC] },
],
});
});
it('freezes created SequentialTransactionPlanResult objects', () => {
Expand All @@ -233,6 +247,7 @@ describe('nonDivisibleSequentialTransactionPlanResult', () => {
expect(result).toEqual({
divisible: false,
kind: 'sequential',
planType: 'transactionPlanResult',
plans: [planA, planB],
});
});
Expand All @@ -247,7 +262,11 @@ describe('nonDivisibleSequentialTransactionPlanResult', () => {
expect(result).toEqual({
divisible: false,
kind: 'sequential',
plans: [planA, { divisible: false, kind: 'sequential', plans: [planB, planC] }],
planType: 'transactionPlanResult',
plans: [
planA,
{ divisible: false, kind: 'sequential', planType: 'transactionPlanResult', plans: [planB, planC] },
],
});
});
it('freezes created SequentialTransactionPlanResult objects', () => {
Expand Down Expand Up @@ -1588,3 +1607,43 @@ describe('getFirstFailedSingleTransactionPlanResult', () => {
expect(Object.prototype.propertyIsEnumerable.call(caughtError!.context, 'transactionPlanResult')).toBe(false);
});
});

describe('isTransactionPlanResult', () => {
it('returns true for SuccessfulSingleTransactionPlanResult', () => {
const signature = 'A' as Signature;
expect(isTransactionPlanResult(successfulSingleTransactionPlanResult(createMessage('A'), { signature }))).toBe(
true,
);
});
it('returns true for FailedSingleTransactionPlanResult', () => {
expect(isTransactionPlanResult(failedSingleTransactionPlanResult(createMessage('A'), new Error()))).toBe(true);
});
it('returns true for CanceledSingleTransactionPlanResult', () => {
expect(isTransactionPlanResult(canceledSingleTransactionPlanResult(createMessage('A')))).toBe(true);
});
it('returns true for ParallelTransactionPlanResult', () => {
expect(isTransactionPlanResult(parallelTransactionPlanResult([]))).toBe(true);
});
it('returns true for SequentialTransactionPlanResult', () => {
expect(isTransactionPlanResult(sequentialTransactionPlanResult([]))).toBe(true);
});
it('returns true for non-divisible SequentialTransactionPlanResult', () => {
expect(isTransactionPlanResult(nonDivisibleSequentialTransactionPlanResult([]))).toBe(true);
});
it('returns false for non-objects', () => {
expect(isTransactionPlanResult(null)).toBe(false);
expect(isTransactionPlanResult(undefined)).toBe(false);
expect(isTransactionPlanResult('string')).toBe(false);
expect(isTransactionPlanResult(123)).toBe(false);
expect(isTransactionPlanResult(true)).toBe(false);
});
it('returns false for objects without planType', () => {
expect(isTransactionPlanResult({ kind: 'single', status: 'successful' })).toBe(false);
});
it('returns false for objects with wrong planType', () => {
expect(isTransactionPlanResult({ planType: 123 })).toBe(false);
expect(isTransactionPlanResult({ planType: null })).toBe(false);
expect(isTransactionPlanResult({ planType: 'instructionPlan' })).toBe(false);
expect(isTransactionPlanResult({ planType: 'transactionPlan' })).toBe(false);
});
});
Loading