Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: onProgramAccountChange() and onAccountChange() now accept an encoding #2861

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
76 changes: 72 additions & 4 deletions packages/library-legacy/src/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2787,6 +2787,39 @@ export type GetNonceAndContextConfig = {
minContextSlot?: number;
};

export type AccountSubscriptionConfig = Readonly<{
/** Optional commitment level */
commitment?: Commitment;
/**
* Encoding format for Account data
* - `base58` is slow.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I laughed at this, until I realized it's actually what's in the docs. 😅

* - `jsonParsed` encoding attempts to use program-specific state parsers to return more
* human-readable and explicit account state data
* - If `jsonParsed` is requested but a parser cannot be found, the field falls back to `base64`
* encoding, detectable when the `data` field is type `string`.
*/
encoding?: 'base58' | 'base64' | 'base64+zstd' | 'jsonParsed';
}>;

export type ProgramAccountSubscriptionConfig = Readonly<{
/** Optional commitment level */
commitment?: Commitment;
/**
* Encoding format for Account data
* - `base58` is slow.
* - `jsonParsed` encoding attempts to use program-specific state parsers to return more
* human-readable and explicit account state data
* - If `jsonParsed` is requested but a parser cannot be found, the field falls back to `base64`
* encoding, detectable when the `data` field is type `string`.
*/
encoding?: 'base58' | 'base64' | 'base64+zstd' | 'jsonParsed';
/**
* Filter results using various filter objects
* The resultant account must meet ALL filter criteria to be included in the returned results
*/
filters?: GetProgramAccountsFilter[];
}>;

/**
* Information describing an account
*/
Expand Down Expand Up @@ -6334,18 +6367,34 @@ export class Connection {
*
* @param publicKey Public key of the account to monitor
* @param callback Function to invoke whenever the account is changed
* @param commitment Specify the commitment level account changes must reach before notification
* @param config
* @return subscription id
*/
onAccountChange(
publicKey: PublicKey,
callback: AccountChangeCallback,
config?: AccountSubscriptionConfig,
): ClientSubscriptionId;
/** @deprecated Instead, pass in an {@link AccountSubscriptionConfig} */
// eslint-disable-next-line no-dupe-class-members
onAccountChange(
publicKey: PublicKey,
callback: AccountChangeCallback,
commitment?: Commitment,
): ClientSubscriptionId;
Comment on lines 6380 to +6384
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Existing method signature for backward compatibility.

// eslint-disable-next-line no-dupe-class-members
onAccountChange(
publicKey: PublicKey,
callback: AccountChangeCallback,
commitmentOrConfig?: Commitment | AccountSubscriptionConfig,
): ClientSubscriptionId {
const {commitment, config} =
extractCommitmentFromConfig(commitmentOrConfig);
const args = this._buildArgs(
[publicKey.toBase58()],
commitment || this._commitment || 'finalized', // Apply connection/server default.
'base64',
config,
);
return this._makeSubscription(
{
Expand Down Expand Up @@ -6394,21 +6443,40 @@ export class Connection {
*
* @param programId Public key of the program to monitor
* @param callback Function to invoke whenever the account is changed
* @param commitment Specify the commitment level account changes must reach before notification
* @param filters The program account filters to pass into the RPC method
* @param config
* @return subscription id
*/
onProgramAccountChange(
programId: PublicKey,
callback: ProgramAccountChangeCallback,
config?: ProgramAccountSubscriptionConfig,
): ClientSubscriptionId;
/** @deprecated Instead, pass in a {@link ProgramAccountSubscriptionConfig} */
// eslint-disable-next-line no-dupe-class-members
onProgramAccountChange(
programId: PublicKey,
callback: ProgramAccountChangeCallback,
commitment?: Commitment,
filters?: GetProgramAccountsFilter[],
): ClientSubscriptionId;
Comment on lines 6456 to +6461
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Existing method signature for backward compatibility.

// eslint-disable-next-line no-dupe-class-members
onProgramAccountChange(
programId: PublicKey,
callback: ProgramAccountChangeCallback,
commitmentOrConfig?: Commitment | ProgramAccountSubscriptionConfig,
maybeFilters?: GetProgramAccountsFilter[],
): ClientSubscriptionId {
const {commitment, config} =
extractCommitmentFromConfig(commitmentOrConfig);
const args = this._buildArgs(
[programId.toBase58()],
commitment || this._commitment || 'finalized', // Apply connection/server default.
'base64' /* encoding */,
filters ? {filters: filters} : undefined /* extra */,
config
? config
: maybeFilters
? {filters: maybeFilters}
: undefined /* extra */,
Comment on lines +6475 to +6479
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to TypeScript, you either have a config, or you have commitment/filters, but never both.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Little gross, but wfm.

);
return this._makeSubscription(
{
Expand Down
125 changes: 123 additions & 2 deletions packages/library-legacy/test/connection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {expect, use} from 'chai';
import chaiAsPromised from 'chai-as-promised';
import {Agent as HttpAgent} from 'http';
import {Agent as HttpsAgent} from 'https';
import {match, mock, spy, useFakeTimers, SinonFakeTimers} from 'sinon';
import {match, mock, spy, stub, useFakeTimers, SinonFakeTimers} from 'sinon';
import sinonChai from 'sinon-chai';
import {fail} from 'assert';

Expand Down Expand Up @@ -5914,7 +5914,7 @@ describe('Connection', function () {
subscriptionId = connection.onAccountChange(
owner.publicKey,
resolve,
'confirmed',
{commitment: 'confirmed'},
);
},
);
Expand Down Expand Up @@ -6394,4 +6394,125 @@ describe('Connection', function () {
});
}).timeout(5 * 1000);
}

it('passes the commitment/encoding to the RPC when calling `onAccountChange`', () => {
const connection = new Connection(url);
const rpcRequestMethod = stub(
connection,
// @ts-expect-error This method is private, but none the less this spy will work.
'_makeSubscription',
);
const mockCallback = () => {};
connection.onAccountChange(PublicKey.default, mockCallback, {
commitment: 'processed',
encoding: 'base64+zstd',
});
expect(rpcRequestMethod).to.have.been.calledWithExactly(
{
callback: mockCallback,
method: 'accountSubscribe',
unsubscribeMethod: 'accountUnsubscribe',
},
[
match.any,
match
.has('commitment', 'processed')
.and(match.has('encoding', 'base64+zstd')),
],
);
});
it('passes the commitment to the RPC when the deprecated signature of `onAccountChange` is used', () => {
const connection = new Connection(url);
const rpcRequestMethod = stub(
connection,
// @ts-expect-error This method is private, but none the less this spy will work.
'_makeSubscription',
);
const mockCallback = () => {};
connection.onAccountChange(PublicKey.default, mockCallback, 'processed');
expect(rpcRequestMethod).to.have.been.calledWithExactly(
{
callback: mockCallback,
method: 'accountSubscribe',
unsubscribeMethod: 'accountUnsubscribe',
},
[match.any, match.has('commitment', 'processed')],
);
});
it('passes the commitment to the RPC when the deprecated signature of `onProgramAccountChange` is used', () => {
const connection = new Connection(url);
const rpcRequestMethod = stub(
connection,
// @ts-expect-error This method is private, but none the less this spy will work.
'_makeSubscription',
);
const mockCallback = () => {};
connection.onProgramAccountChange(
PublicKey.default,
mockCallback,
'processed' /* commitment */,
);
expect(rpcRequestMethod).to.have.been.calledWithExactly(
{
callback: mockCallback,
method: 'programSubscribe',
unsubscribeMethod: 'programUnsubscribe',
},
[match.any, match.has('commitment', 'processed')],
);
});
it('passes the filters to the RPC when the deprecated signature of `onProgramAccountChange` is used', () => {
const connection = new Connection(url);
const rpcRequestMethod = stub(
connection,
// @ts-expect-error This method is private, but none the less this spy will work.
'_makeSubscription',
);
const mockCallback = () => {};
connection.onProgramAccountChange(
PublicKey.default,
mockCallback,
/* commitment */ undefined,
/* filters */ [{dataSize: 123}],
);
expect(rpcRequestMethod).to.have.been.calledWithExactly(
{
callback: mockCallback,
method: 'programSubscribe',
unsubscribeMethod: 'programUnsubscribe',
},
[match.any, match.has('filters', [{dataSize: 123}])],
);
});
it('passes the commitment/encoding/filters to the RPC when calling `onProgramAccountChange`', () => {
const connection = new Connection(url);
const rpcRequestMethod = stub(
connection,
// @ts-expect-error This method is private, but none the less this spy will work.
'_makeSubscription',
);
const mockCallback = () => {};
connection.onProgramAccountChange(PublicKey.default, mockCallback, {
commitment: 'processed',
encoding: 'base64+zstd',
filters: [{dataSize: 123}],
});
expect(rpcRequestMethod).to.have.been.calledWithExactly(
{
callback: mockCallback,
method: 'programSubscribe',
unsubscribeMethod: 'programUnsubscribe',
},
[
match.any,
match
.has('commitment', 'processed')
.and(
match
.has('encoding', 'base64+zstd')
.and(match.has('filters', [{dataSize: 123}])),
),
],
);
});
});
Loading