feat: add filters to getProgramAccounts and getParsedProgramAccounts (#16448)
* feat: add filters to getProgramAccounts and getParsedProgramAccounts * fix: documentation edits * fix: make connection interface match existing interface
This commit is contained in:
@ -1368,6 +1368,63 @@ export type StakeActivationData = {
|
|||||||
inactive: number;
|
inactive: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data slice argument for getProgramAccounts
|
||||||
|
*/
|
||||||
|
export type DataSlice = {
|
||||||
|
/** offset of data slice */
|
||||||
|
offset: number;
|
||||||
|
/** length of data slice */
|
||||||
|
length: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Memory comparison filter for getProgramAccounts
|
||||||
|
*/
|
||||||
|
export type MemcmpFilter = {
|
||||||
|
memcmp: {
|
||||||
|
/** offset into program account data to start comparison */
|
||||||
|
offset: number;
|
||||||
|
/** data to match, as base-58 encoded string and limited to less than 129 bytes */
|
||||||
|
bytes: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data size comparison filter for getProgramAccounts
|
||||||
|
*/
|
||||||
|
export type DataSizeFilter = {
|
||||||
|
/** Size of data for program account data length comparison */
|
||||||
|
dataSize: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A filter object for getProgramAccounts
|
||||||
|
*/
|
||||||
|
export type GetProgramAccountsFilter = MemcmpFilter | DataSizeFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration object for getProgramAccounts requests
|
||||||
|
*/
|
||||||
|
export type GetProgramAccountsConfig = {
|
||||||
|
/** Optional commitment level */
|
||||||
|
commitment?: Commitment;
|
||||||
|
/** Optional encoding for account data (default base64) */
|
||||||
|
encoding?: 'base64' | 'jsonParsed';
|
||||||
|
/** Optional data slice to limit the returned account data */
|
||||||
|
dataSlice?: DataSlice;
|
||||||
|
/** Optional array of filters to apply to accounts */
|
||||||
|
filters?: GetProgramAccountsFilter[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration object for getParsedProgramAccounts
|
||||||
|
*/
|
||||||
|
export type GetParsedProgramAccountsConfig = Exclude<
|
||||||
|
GetProgramAccountsConfig,
|
||||||
|
'encoding' | 'dataSlice'
|
||||||
|
>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information describing an account
|
* Information describing an account
|
||||||
*/
|
*/
|
||||||
@ -2080,9 +2137,34 @@ export class Connection {
|
|||||||
*/
|
*/
|
||||||
async getProgramAccounts(
|
async getProgramAccounts(
|
||||||
programId: PublicKey,
|
programId: PublicKey,
|
||||||
commitment?: Commitment,
|
configOrCommitment?: GetProgramAccountsConfig | Commitment,
|
||||||
): Promise<Array<{pubkey: PublicKey; account: AccountInfo<Buffer>}>> {
|
): Promise<Array<{pubkey: PublicKey; account: AccountInfo<Buffer>}>> {
|
||||||
const args = this._buildArgs([programId.toBase58()], commitment, 'base64');
|
const extra: Pick<GetProgramAccountsConfig, 'dataSlice' | 'filters'> = {};
|
||||||
|
|
||||||
|
let commitment;
|
||||||
|
let encoding;
|
||||||
|
if (configOrCommitment) {
|
||||||
|
if (typeof configOrCommitment === 'string') {
|
||||||
|
commitment = configOrCommitment;
|
||||||
|
} else {
|
||||||
|
commitment = configOrCommitment.commitment;
|
||||||
|
encoding = configOrCommitment.encoding;
|
||||||
|
|
||||||
|
if (configOrCommitment.dataSlice) {
|
||||||
|
extra.dataSlice = configOrCommitment.dataSlice;
|
||||||
|
}
|
||||||
|
if (configOrCommitment.filters) {
|
||||||
|
extra.filters = configOrCommitment.filters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = this._buildArgs(
|
||||||
|
[programId.toBase58()],
|
||||||
|
commitment,
|
||||||
|
encoding || 'base64',
|
||||||
|
extra,
|
||||||
|
);
|
||||||
const unsafeRes = await this._rpcRequest('getProgramAccounts', args);
|
const unsafeRes = await this._rpcRequest('getProgramAccounts', args);
|
||||||
const res = create(unsafeRes, jsonRpcResult(array(KeyedAccountInfoResult)));
|
const res = create(unsafeRes, jsonRpcResult(array(KeyedAccountInfoResult)));
|
||||||
if ('error' in res) {
|
if ('error' in res) {
|
||||||
@ -2103,17 +2185,33 @@ export class Connection {
|
|||||||
*/
|
*/
|
||||||
async getParsedProgramAccounts(
|
async getParsedProgramAccounts(
|
||||||
programId: PublicKey,
|
programId: PublicKey,
|
||||||
commitment?: Commitment,
|
configOrCommitment?: GetParsedProgramAccountsConfig | Commitment,
|
||||||
): Promise<
|
): Promise<
|
||||||
Array<{
|
Array<{
|
||||||
pubkey: PublicKey;
|
pubkey: PublicKey;
|
||||||
account: AccountInfo<Buffer | ParsedAccountData>;
|
account: AccountInfo<Buffer | ParsedAccountData>;
|
||||||
}>
|
}>
|
||||||
> {
|
> {
|
||||||
|
const extra: Pick<GetParsedProgramAccountsConfig, 'filters'> = {};
|
||||||
|
|
||||||
|
let commitment;
|
||||||
|
if (configOrCommitment) {
|
||||||
|
if (typeof configOrCommitment === 'string') {
|
||||||
|
commitment = configOrCommitment;
|
||||||
|
} else {
|
||||||
|
commitment = configOrCommitment.commitment;
|
||||||
|
|
||||||
|
if (configOrCommitment.filters) {
|
||||||
|
extra.filters = configOrCommitment.filters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const args = this._buildArgs(
|
const args = this._buildArgs(
|
||||||
[programId.toBase58()],
|
[programId.toBase58()],
|
||||||
commitment,
|
commitment,
|
||||||
'jsonParsed',
|
'jsonParsed',
|
||||||
|
extra,
|
||||||
);
|
);
|
||||||
const unsafeRes = await this._rpcRequest('getProgramAccounts', args);
|
const unsafeRes = await this._rpcRequest('getProgramAccounts', args);
|
||||||
const res = create(
|
const res = create(
|
||||||
|
@ -163,6 +163,59 @@ describe('Connection', () => {
|
|||||||
const feeCalculator = (await helpers.recentBlockhash({connection}))
|
const feeCalculator = (await helpers.recentBlockhash({connection}))
|
||||||
.feeCalculator;
|
.feeCalculator;
|
||||||
|
|
||||||
|
{
|
||||||
|
await mockRpcResponse({
|
||||||
|
method: 'getProgramAccounts',
|
||||||
|
params: [
|
||||||
|
programId.publicKey.toBase58(),
|
||||||
|
{commitment: 'confirmed', encoding: 'base64'},
|
||||||
|
],
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
account: {
|
||||||
|
data: ['', 'base64'],
|
||||||
|
executable: false,
|
||||||
|
lamports: LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
|
||||||
|
owner: programId.publicKey.toBase58(),
|
||||||
|
rentEpoch: 20,
|
||||||
|
},
|
||||||
|
pubkey: account0.publicKey.toBase58(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
account: {
|
||||||
|
data: ['', 'base64'],
|
||||||
|
executable: false,
|
||||||
|
lamports:
|
||||||
|
0.5 * LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
|
||||||
|
owner: programId.publicKey.toBase58(),
|
||||||
|
rentEpoch: 20,
|
||||||
|
},
|
||||||
|
pubkey: account1.publicKey.toBase58(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const programAccounts = await connection.getProgramAccounts(
|
||||||
|
programId.publicKey,
|
||||||
|
{
|
||||||
|
commitment: 'confirmed',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(programAccounts).to.have.length(2);
|
||||||
|
programAccounts.forEach(function (keyedAccount) {
|
||||||
|
if (keyedAccount.pubkey.equals(account0.publicKey)) {
|
||||||
|
expect(keyedAccount.account.lamports).to.eq(
|
||||||
|
LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
expect(keyedAccount.pubkey).to.eql(account1.publicKey);
|
||||||
|
expect(keyedAccount.account.lamports).to.eq(
|
||||||
|
0.5 * LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
await mockRpcResponse({
|
await mockRpcResponse({
|
||||||
method: 'getProgramAccounts',
|
method: 'getProgramAccounts',
|
||||||
@ -214,6 +267,95 @@ describe('Connection', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
await mockRpcResponse({
|
||||||
|
method: 'getProgramAccounts',
|
||||||
|
params: [
|
||||||
|
programId.publicKey.toBase58(),
|
||||||
|
{
|
||||||
|
commitment: 'confirmed',
|
||||||
|
encoding: 'base64',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
dataSize: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
account: {
|
||||||
|
data: ['', 'base64'],
|
||||||
|
executable: false,
|
||||||
|
lamports: LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
|
||||||
|
owner: programId.publicKey.toBase58(),
|
||||||
|
rentEpoch: 20,
|
||||||
|
},
|
||||||
|
pubkey: account0.publicKey.toBase58(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
account: {
|
||||||
|
data: ['', 'base64'],
|
||||||
|
executable: false,
|
||||||
|
lamports:
|
||||||
|
0.5 * LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
|
||||||
|
owner: programId.publicKey.toBase58(),
|
||||||
|
rentEpoch: 20,
|
||||||
|
},
|
||||||
|
pubkey: account1.publicKey.toBase58(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const programAccountsDoMatchFilter = await connection.getProgramAccounts(
|
||||||
|
programId.publicKey,
|
||||||
|
{
|
||||||
|
commitment: 'confirmed',
|
||||||
|
encoding: 'base64',
|
||||||
|
filters: [{dataSize: 0}],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(programAccountsDoMatchFilter).to.have.length(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
await mockRpcResponse({
|
||||||
|
method: 'getProgramAccounts',
|
||||||
|
params: [
|
||||||
|
programId.publicKey.toBase58(),
|
||||||
|
{
|
||||||
|
commitment: 'confirmed',
|
||||||
|
encoding: 'base64',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
memcmp: {
|
||||||
|
offset: 0,
|
||||||
|
bytes: 'XzdZ3w',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
value: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const programAccountsDontMatchFilter = await connection.getProgramAccounts(
|
||||||
|
programId.publicKey,
|
||||||
|
{
|
||||||
|
commitment: 'confirmed',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
memcmp: {
|
||||||
|
offset: 0,
|
||||||
|
bytes: 'XzdZ3w',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(programAccountsDontMatchFilter).to.have.length(0);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
await mockRpcResponse({
|
await mockRpcResponse({
|
||||||
method: 'getProgramAccounts',
|
method: 'getProgramAccounts',
|
||||||
@ -248,7 +390,9 @@ describe('Connection', () => {
|
|||||||
|
|
||||||
const programAccounts = await connection.getParsedProgramAccounts(
|
const programAccounts = await connection.getParsedProgramAccounts(
|
||||||
programId.publicKey,
|
programId.publicKey,
|
||||||
'confirmed',
|
{
|
||||||
|
commitment: 'confirmed',
|
||||||
|
},
|
||||||
);
|
);
|
||||||
expect(programAccounts).to.have.length(2);
|
expect(programAccounts).to.have.length(2);
|
||||||
|
|
||||||
@ -265,6 +409,94 @@ describe('Connection', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
await mockRpcResponse({
|
||||||
|
method: 'getProgramAccounts',
|
||||||
|
params: [
|
||||||
|
programId.publicKey.toBase58(),
|
||||||
|
{
|
||||||
|
commitment: 'confirmed',
|
||||||
|
encoding: 'jsonParsed',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
dataSize: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
value: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const programAccountsDontMatchFilter = await connection.getParsedProgramAccounts(
|
||||||
|
programId.publicKey,
|
||||||
|
{
|
||||||
|
commitment: 'confirmed',
|
||||||
|
filters: [{dataSize: 2}],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(programAccountsDontMatchFilter).to.have.length(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
await mockRpcResponse({
|
||||||
|
method: 'getProgramAccounts',
|
||||||
|
params: [
|
||||||
|
programId.publicKey.toBase58(),
|
||||||
|
{
|
||||||
|
commitment: 'confirmed',
|
||||||
|
encoding: 'jsonParsed',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
memcmp: {
|
||||||
|
offset: 0,
|
||||||
|
bytes: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
account: {
|
||||||
|
data: ['', 'base64'],
|
||||||
|
executable: false,
|
||||||
|
lamports: LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
|
||||||
|
owner: programId.publicKey.toBase58(),
|
||||||
|
rentEpoch: 20,
|
||||||
|
},
|
||||||
|
pubkey: account0.publicKey.toBase58(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
account: {
|
||||||
|
data: ['', 'base64'],
|
||||||
|
executable: false,
|
||||||
|
lamports:
|
||||||
|
0.5 * LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
|
||||||
|
owner: programId.publicKey.toBase58(),
|
||||||
|
rentEpoch: 20,
|
||||||
|
},
|
||||||
|
pubkey: account1.publicKey.toBase58(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const programAccountsDoMatchFilter = await connection.getParsedProgramAccounts(
|
||||||
|
programId.publicKey,
|
||||||
|
{
|
||||||
|
commitment: 'confirmed',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
memcmp: {
|
||||||
|
offset: 0,
|
||||||
|
bytes: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(programAccountsDoMatchFilter).to.have.length(2);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('get balance', async () => {
|
it('get balance', async () => {
|
||||||
|
Reference in New Issue
Block a user