Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

Commit

Permalink
Default commitment to confirmed when not explicitly specified
Browse files Browse the repository at this point in the history
  • Loading branch information
steveluscher committed Dec 19, 2023
1 parent 2962a3f commit cb7702c
Show file tree
Hide file tree
Showing 6 changed files with 435 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/library/src/rpc-default-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createSolanaRpcApi } from '@solana/rpc-core';
import { SolanaJsonRpcIntegerOverflowError } from './rpc-integer-overflow-error';

export const DEFAULT_RPC_CONFIG: Partial<Parameters<typeof createSolanaRpcApi>[0]> = {
defaultCommitment: 'confirmed',
onIntegerOverflow(methodName, keyPath, value) {
throw new SolanaJsonRpcIntegerOverflowError(methodName, keyPath, value);
},
Expand Down
121 changes: 121 additions & 0 deletions packages/rpc-core/src/__tests__/default-commitment-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { Commitment } from '@solana/rpc-types';

import { applyDefaultCommitment } from '../default-commitment';

const MOCK_COMMITMENT_PROPERTY_NAME = 'commitmentProperty';

describe('applyDefaultCommitment', () => {
describe.each([0, 1, 2])('in relation to a method whose commitment config is argument #%s', expectedPosition => {
it('adds the default commitment when absent from the call', () => {
expect.assertions(1);
expect(
applyDefaultCommitment({
commitmentPropertyName: MOCK_COMMITMENT_PROPERTY_NAME,
optionsObjectPositionInParams: expectedPosition,
overrideCommitment: 'processed',
params: [],
}),
).toEqual([
...new Array(expectedPosition).map(() => expect.anything()),
{ [MOCK_COMMITMENT_PROPERTY_NAME]: 'processed' },
]);
});
describe.each(['confirmed', 'finalized', 'processed'] as Commitment[])(
'when the default commitment is set to `%s`',
defaultCommitment => {
describe.each(['confirmed', 'processed'])(
'and the params already specify a commitment of `%s`',
existingCommitment => {
it('does not overwrite it', () => {
const params = [
...new Array(expectedPosition),
{ [MOCK_COMMITMENT_PROPERTY_NAME]: existingCommitment },
];
expect(
applyDefaultCommitment({
commitmentPropertyName: MOCK_COMMITMENT_PROPERTY_NAME,
optionsObjectPositionInParams: expectedPosition,
overrideCommitment: defaultCommitment,
params,
}),
).toBe(params);
});
},
);
describe.each(['finalized', undefined])(
'and the params already specify a commitment of `%s`',
existingCommitment => {
it('removes the commitment property when there are other properties in the config object', () => {
expect.assertions(1);
const params = [
...new Array(expectedPosition),
{ [MOCK_COMMITMENT_PROPERTY_NAME]: existingCommitment, other: 'property' },
];
expect(
applyDefaultCommitment({
commitmentPropertyName: MOCK_COMMITMENT_PROPERTY_NAME,
optionsObjectPositionInParams: expectedPosition,
overrideCommitment: defaultCommitment,
params,
}),
).toStrictEqual([
...new Array(expectedPosition).map(() => expect.anything()),
{ other: 'property' },
]);
});
it('sets the config object to `undefined` when there are no other properties left and the config object is not the last param', () => {
expect.assertions(1);
const params = [
...new Array(expectedPosition),
{ [MOCK_COMMITMENT_PROPERTY_NAME]: existingCommitment },
'someParam',
];
expect(
applyDefaultCommitment({
commitmentPropertyName: MOCK_COMMITMENT_PROPERTY_NAME,
optionsObjectPositionInParams: expectedPosition,
overrideCommitment: defaultCommitment,
params,
}),
).toStrictEqual([
...new Array(expectedPosition).map(() => expect.anything()),
undefined,
'someParam',
]);
});
it('truncates the params when there are no other properties left and the config object is the last param', () => {
expect.assertions(1);
const params = [
...new Array(expectedPosition),
{ [MOCK_COMMITMENT_PROPERTY_NAME]: existingCommitment },
];
expect(
applyDefaultCommitment({
commitmentPropertyName: MOCK_COMMITMENT_PROPERTY_NAME,
optionsObjectPositionInParams: expectedPosition,
overrideCommitment: defaultCommitment,
params,
}),
).toStrictEqual([...new Array(expectedPosition).map(() => expect.anything())]);
});
},
);
},
);
it.each([null, 1, '1', 1n, [1, 2, 3]])(
"does not overwrite the existing param when it's a non-object like `%s`",
paramInConfigPosition => {
expect.assertions(1);
const params = [...new Array(expectedPosition), paramInConfigPosition];
expect(
applyDefaultCommitment({
commitmentPropertyName: MOCK_COMMITMENT_PROPERTY_NAME,
optionsObjectPositionInParams: expectedPosition,
overrideCommitment: 'processed',
params,
}),
).toBe(params);
},
);
});
});
183 changes: 183 additions & 0 deletions packages/rpc-core/src/__tests__/params-patcher-test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { Commitment } from '@solana/rpc-types';

import { getParamsPatcherForSolanaLabsRpc } from '../params-patcher';
import { OPTIONS_OBJECT_POSITION_BY_METHOD } from '../params-patcher-options-object-position-config';

describe('getParamsPatcherForSolanaLabsRpc', () => {
describe('given no config', () => {
Expand Down Expand Up @@ -29,6 +32,186 @@ describe('getParamsPatcherForSolanaLabsRpc', () => {
});
});
});
describe('with respect to the default commitment', () => {
const METHODS_SUBJECT_TO_COMMITMENT_DEFAULTING = [
'accountNotifications',
'blockNotifications',
'getAccountInfo',
'getBalance',
'getBlock',
'getBlockHeight',
'getBlockProduction',
'getBlocks',
'getBlocksWithLimit',
'getConfirmedBlock',
'getConfirmedBlocks',
'getConfirmedBlocksWithLimit',
'getConfirmedSignaturesForAddress2',
'getConfirmedTransaction',
'getEpochInfo',
'getFeeCalculatorForBlockhash',
'getFeeForMessage',
'getFees',
'getInflationGovernor',
'getInflationReward',
'getLargestAccounts',
'getLatestBlockhash',
'getLeaderSchedule',
'getMinimumBalanceForRentExemption',
'getMultipleAccounts',
'getProgramAccounts',
'getRecentBlockhash',
'getSignaturesForAddress',
'getSlot',
'getSlotLeader',
'getStakeActivation',
'getStakeMinimumDelegation',
'getSupply',
'getTokenAccountBalance',
'getTokenAccountsByDelegate',
'getTokenAccountsByOwner',
'getTokenLargestAccounts',
'getTokenSupply',
'getTransaction',
'getTransactionCount',
'getVoteAccounts',
'isBlockhashValid',
'logsNotifications',
'programNotifications',
'requestAirdrop',
'signatureNotifications',
'simulateTransaction',
];
describe.each(['processed', 'confirmed'] as Commitment[])(
'with the default commitment set to `%s`',
defaultCommitment => {
let patcher: ReturnType<typeof getParamsPatcherForSolanaLabsRpc>;
beforeEach(() => {
patcher = getParamsPatcherForSolanaLabsRpc({ defaultCommitment });
});
it.each(METHODS_SUBJECT_TO_COMMITMENT_DEFAULTING)(
'adds a default commitment on calls for `%s`',
methodName => {
expect(patcher([], methodName)).toContainEqual({ commitment: defaultCommitment });
},
);
it('adds a default preflight commitment on calls to `sendTransaction`', () => {
expect(patcher([], 'sendTransaction')).toContainEqual({ preflightCommitment: defaultCommitment });
});
},
);
describe('with the default commitment set to `finalized`', () => {
let patcher: ReturnType<typeof getParamsPatcherForSolanaLabsRpc>;
beforeEach(() => {
patcher = getParamsPatcherForSolanaLabsRpc({ defaultCommitment: 'finalized' });
});
it.each(METHODS_SUBJECT_TO_COMMITMENT_DEFAULTING)('adds no commitment on calls for `%s`', methodName => {
expect(patcher([], methodName)).not.toContainEqual(
expect.objectContaining({ commitment: expect.anything() }),
);
});
it('adds no preflight commitment on calls to `sendTransaction`', () => {
expect(patcher([], 'sendTransaction')).not.toContainEqual(
expect.objectContaining({ preflightCommitment: expect.anything() }),
);
});
});
describe('with no default commitment set', () => {
let patcher: ReturnType<typeof getParamsPatcherForSolanaLabsRpc>;
beforeEach(() => {
patcher = getParamsPatcherForSolanaLabsRpc();
});
it.each(METHODS_SUBJECT_TO_COMMITMENT_DEFAULTING)('sets no commitment on calls to `%s`', methodName => {
expect(patcher([], methodName)).not.toContainEqual(
expect.objectContaining({ commitment: expect.anything() }),
);
});
it('adds no preflight commitment on calls to `sendTransaction`', () => {
expect(patcher([], 'sendTransaction')).not.toContainEqual(
expect.objectContaining({ preflightCommitment: expect.anything() }),
);
});
});
describe.each(['finalized', undefined])(
'when the params already specify a commitment of `%s`',
existingCommitment => {
describe.each(METHODS_SUBJECT_TO_COMMITMENT_DEFAULTING)('on calls to `%s`', methodName => {
const optionsObjectPosition = OPTIONS_OBJECT_POSITION_BY_METHOD[methodName]!;
it('removes the commitment property on calls to `%s` when there are other properties in the config object', () => {
expect.assertions(1);
const params = [
...new Array(optionsObjectPosition),
{ commitment: existingCommitment, other: 'property' },
];
const patcher = getParamsPatcherForSolanaLabsRpc();
expect(patcher(params, methodName)).toStrictEqual([
...new Array(optionsObjectPosition).map(() => expect.anything()),
{ other: 'property' },
]);
});
it('deletes the commitment on calls to `%s` when there are no other properties left and the config object is not the last param', () => {
expect.assertions(1);
const params = [
...new Array(optionsObjectPosition),
{ commitment: existingCommitment },
'someParam',
];
const patcher = getParamsPatcherForSolanaLabsRpc();
expect(patcher(params, methodName)).toStrictEqual([
...new Array(optionsObjectPosition).map(() => expect.anything()),
undefined,
'someParam',
]);
});
it('truncates the params on calls to `%s` when there are no other properties left and the config object is the last param', () => {
expect.assertions(1);
const params = [...new Array(optionsObjectPosition), { commitment: existingCommitment }];
const patcher = getParamsPatcherForSolanaLabsRpc();
expect(patcher(params, methodName)).toStrictEqual([
...new Array(optionsObjectPosition).map(() => expect.anything()),
]);
});
});
it('removes the preflight commitment property on calls to `%s` when there are other properties in the config object', () => {
expect.assertions(1);
const optionsObjectPosition = OPTIONS_OBJECT_POSITION_BY_METHOD['sendTransaction']!;
const params = [
...new Array(optionsObjectPosition),
{ other: 'property', preflightCommitment: existingCommitment },
];
const patcher = getParamsPatcherForSolanaLabsRpc();
expect(patcher(params, 'sendTransaction')).toStrictEqual([
...new Array(optionsObjectPosition).map(() => expect.anything()),
{ other: 'property' },
]);
});
it('deletes the preflight commitment on calls to `%s` when there are no other properties left and the config object is not the last param', () => {
expect.assertions(1);
const optionsObjectPosition = OPTIONS_OBJECT_POSITION_BY_METHOD['sendTransaction']!;
const params = [
...new Array(optionsObjectPosition),
{ preflightCommitment: existingCommitment },
'someParam',
];
const patcher = getParamsPatcherForSolanaLabsRpc();
expect(patcher(params, 'sendTransaction')).toStrictEqual([
...new Array(optionsObjectPosition).map(() => expect.anything()),
undefined,
'someParam',
]);
});
it('truncates the params on calls to `%s` when there are no other properties left and the config object is the last param', () => {
expect.assertions(1);
const optionsObjectPosition = OPTIONS_OBJECT_POSITION_BY_METHOD['sendTransaction']!;
const params = [...new Array(optionsObjectPosition), { preflightCommitment: existingCommitment }];
const patcher = getParamsPatcherForSolanaLabsRpc();
expect(patcher(params, 'sendTransaction')).toStrictEqual([
...new Array(optionsObjectPosition).map(() => expect.anything()),
]);
});
},
);
});
describe('given an integer overflow handler', () => {
let onIntegerOverflow: jest.Mock;
let paramsTransformer: (value: unknown) => unknown;
Expand Down
60 changes: 60 additions & 0 deletions packages/rpc-core/src/default-commitment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Commitment } from '@solana/rpc-types';

type Config = Readonly<{
params: unknown[];
commitmentPropertyName: string;
optionsObjectPositionInParams: number;
overrideCommitment?: Commitment;
}>;

export function applyDefaultCommitment({
commitmentPropertyName,
params,
optionsObjectPositionInParams,
overrideCommitment,
}: Config) {
const paramInTargetPosition = params[optionsObjectPositionInParams];
if (
// There's no config.
paramInTargetPosition === undefined ||
// There is a config object.
(paramInTargetPosition && typeof paramInTargetPosition === 'object' && !Array.isArray(paramInTargetPosition))
) {
if (
// The config object already has a commitment set.
paramInTargetPosition &&
commitmentPropertyName in paramInTargetPosition
) {
if (
!paramInTargetPosition[commitmentPropertyName as keyof typeof paramInTargetPosition] ||
paramInTargetPosition[commitmentPropertyName as keyof typeof paramInTargetPosition] === 'finalized'
) {
// Delete the commitment property; `finalized` is already the server default.
const nextParams = [...params];
const {
[commitmentPropertyName as keyof typeof paramInTargetPosition]: _, // eslint-disable-line @typescript-eslint/no-unused-vars
...rest
} = paramInTargetPosition;
if (Object.keys(rest).length > 0) {
nextParams[optionsObjectPositionInParams] = rest;
} else {
if (optionsObjectPositionInParams === nextParams.length - 1) {
nextParams.length--;
} else {
nextParams[optionsObjectPositionInParams] = undefined;
}
}
return nextParams;
}
} else if (overrideCommitment !== 'finalized') {
// Apply the default commitment.
const nextParams = [...params];
nextParams[optionsObjectPositionInParams] = {
...paramInTargetPosition,
[commitmentPropertyName]: overrideCommitment,
};
return nextParams;
}
}
return params;
}
Loading

0 comments on commit cb7702c

Please sign in to comment.