Skip to content

Commit

Permalink
fix: onProgramAccountChange() and onAccountChange() now accept an…
Browse files Browse the repository at this point in the history
… encoding (#2861)

Fixes #2725.
  • Loading branch information
steveluscher authored Jun 27, 2024
1 parent 131900b commit f9b0d6d
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 6 deletions.
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.
* - `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;
// 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;
// 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 */,
);
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}])),
),
],
);
});
});

0 comments on commit f9b0d6d

Please sign in to comment.