feat: implement new response format for __WithCommitment() methods
This commit is contained in:
committed by
Michael Vines
parent
890f6f4d9d
commit
5212a98b83
@ -47,6 +47,13 @@ declare module '@solana/web3.js' {
|
|||||||
/* TODO */
|
/* TODO */
|
||||||
|
|
||||||
// === src/connection.js ===
|
// === src/connection.js ===
|
||||||
|
declare export type RpcResponseAndContext<T> = {
|
||||||
|
context: {
|
||||||
|
slot: number,
|
||||||
|
},
|
||||||
|
value: T,
|
||||||
|
};
|
||||||
|
|
||||||
declare export type Commitment = 'max' | 'recent';
|
declare export type Commitment = 'max' | 'recent';
|
||||||
|
|
||||||
declare export type AccountInfo = {
|
declare export type AccountInfo = {
|
||||||
@ -111,6 +118,10 @@ declare module '@solana/web3.js' {
|
|||||||
|
|
||||||
declare export class Connection {
|
declare export class Connection {
|
||||||
constructor(endpoint: string, commitment: ?Commitment): Connection;
|
constructor(endpoint: string, commitment: ?Commitment): Connection;
|
||||||
|
getAccountInfoAndContext(
|
||||||
|
publicKey: PublicKey,
|
||||||
|
commitment: ?Commitment,
|
||||||
|
): Promise<RpcResponseAndContext<AccountInfo>>;
|
||||||
getAccountInfo(
|
getAccountInfo(
|
||||||
publicKey: PublicKey,
|
publicKey: PublicKey,
|
||||||
commitment: ?Commitment,
|
commitment: ?Commitment,
|
||||||
@ -119,9 +130,17 @@ declare module '@solana/web3.js' {
|
|||||||
programId: PublicKey,
|
programId: PublicKey,
|
||||||
commitment: ?Commitment,
|
commitment: ?Commitment,
|
||||||
): Promise<Array<[PublicKey, AccountInfo]>>;
|
): Promise<Array<[PublicKey, AccountInfo]>>;
|
||||||
|
getBalanceAndContext(
|
||||||
|
publicKey: PublicKey,
|
||||||
|
commitment: ?Commitment,
|
||||||
|
): Promise<RpcResponseAndContext<number>>;
|
||||||
getBalance(publicKey: PublicKey, commitment: ?Commitment): Promise<number>;
|
getBalance(publicKey: PublicKey, commitment: ?Commitment): Promise<number>;
|
||||||
getClusterNodes(): Promise<Array<ContactInfo>>;
|
getClusterNodes(): Promise<Array<ContactInfo>>;
|
||||||
getVoteAccounts(commitment: ?Commitment): Promise<VoteAccountStatus>;
|
getVoteAccounts(commitment: ?Commitment): Promise<VoteAccountStatus>;
|
||||||
|
confirmTransactionAndContext(
|
||||||
|
signature: TransactionSignature,
|
||||||
|
commitment: ?Commitment,
|
||||||
|
): Promise<RpcResponseAndContext<boolean>>;
|
||||||
confirmTransaction(
|
confirmTransaction(
|
||||||
signature: TransactionSignature,
|
signature: TransactionSignature,
|
||||||
commitment: ?Commitment,
|
commitment: ?Commitment,
|
||||||
@ -137,6 +156,9 @@ declare module '@solana/web3.js' {
|
|||||||
getVersion(): Promise<Version>;
|
getVersion(): Promise<Version>;
|
||||||
getInflation(commitment: ?Commitment): Promise<Inflation>;
|
getInflation(commitment: ?Commitment): Promise<Inflation>;
|
||||||
getEpochSchedule(): Promise<EpochSchedule>;
|
getEpochSchedule(): Promise<EpochSchedule>;
|
||||||
|
getRecentBlockhashAndContext(
|
||||||
|
commitment: ?Commitment,
|
||||||
|
): Promise<RpcResponseAndContext<[Blockhash, FeeCalculator]>>;
|
||||||
getRecentBlockhash(
|
getRecentBlockhash(
|
||||||
commitment: ?Commitment,
|
commitment: ?Commitment,
|
||||||
): Promise<[Blockhash, FeeCalculator]>;
|
): Promise<[Blockhash, FeeCalculator]>;
|
||||||
|
@ -18,6 +18,50 @@ import type {TransactionSignature} from './transaction';
|
|||||||
|
|
||||||
type RpcRequest = (methodName: string, args: Array<any>) => any;
|
type RpcRequest = (methodName: string, args: Array<any>) => any;
|
||||||
|
|
||||||
|
type RpcResponseAndContext<T> = {
|
||||||
|
context: {
|
||||||
|
slot: number,
|
||||||
|
},
|
||||||
|
value: T,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function jsonRpcResultAndContext(resultDescription: any) {
|
||||||
|
return struct.union([
|
||||||
|
// those same methods return results with context in v0.21+ servers
|
||||||
|
jsonRpcResult({
|
||||||
|
context: struct({
|
||||||
|
slot: 'number',
|
||||||
|
}),
|
||||||
|
value: resultDescription,
|
||||||
|
}),
|
||||||
|
// selected methods return "bare" results in pre-v0.21 servers
|
||||||
|
jsonRpcResult(resultDescription),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function jsonRpcResult(resultDescription: any) {
|
||||||
|
const jsonRpcVersion = struct.literal('2.0');
|
||||||
|
return struct.union([
|
||||||
|
struct({
|
||||||
|
jsonrpc: jsonRpcVersion,
|
||||||
|
id: 'string',
|
||||||
|
error: 'any',
|
||||||
|
}),
|
||||||
|
struct({
|
||||||
|
jsonrpc: jsonRpcVersion,
|
||||||
|
id: 'string',
|
||||||
|
error: 'null?',
|
||||||
|
result: resultDescription,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The level of commitment desired when querying state
|
* The level of commitment desired when querying state
|
||||||
* 'max': Query the most recent block which has reached max voter lockout
|
* 'max': Query the most recent block which has reached max voter lockout
|
||||||
@ -207,12 +251,7 @@ const GetEpochScheduleRpcResult = struct({
|
|||||||
/**
|
/**
|
||||||
* Expected JSON RPC response for the "getBalance" message
|
* Expected JSON RPC response for the "getBalance" message
|
||||||
*/
|
*/
|
||||||
const GetBalanceRpcResult = struct({
|
const GetBalanceAndContextRpcResult = jsonRpcResultAndContext('number?');
|
||||||
jsonrpc: struct.literal('2.0'),
|
|
||||||
id: 'string',
|
|
||||||
error: 'any?',
|
|
||||||
result: 'number?',
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expected JSON RPC response for the "getVersion" message
|
* Expected JSON RPC response for the "getVersion" message
|
||||||
@ -224,26 +263,6 @@ const GetVersionRpcResult = struct({
|
|||||||
result: Version,
|
result: Version,
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
function jsonRpcResult(resultDescription: any) {
|
|
||||||
const jsonRpcVersion = struct.literal('2.0');
|
|
||||||
return struct.union([
|
|
||||||
struct({
|
|
||||||
jsonrpc: jsonRpcVersion,
|
|
||||||
id: 'string',
|
|
||||||
error: 'any',
|
|
||||||
}),
|
|
||||||
struct({
|
|
||||||
jsonrpc: jsonRpcVersion,
|
|
||||||
id: 'string',
|
|
||||||
error: 'null?',
|
|
||||||
result: resultDescription,
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
@ -258,7 +277,9 @@ const AccountInfoResult = struct({
|
|||||||
/**
|
/**
|
||||||
* Expected JSON RPC response for the "getAccountInfo" message
|
* Expected JSON RPC response for the "getAccountInfo" message
|
||||||
*/
|
*/
|
||||||
const GetAccountInfoRpcResult = jsonRpcResult(AccountInfoResult);
|
const GetAccountInfoAndContextRpcResult = jsonRpcResultAndContext(
|
||||||
|
struct.union(['null', AccountInfoResult]),
|
||||||
|
);
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Expected JSON RPC response for the "accountNotification" message
|
* Expected JSON RPC response for the "accountNotification" message
|
||||||
@ -291,7 +312,9 @@ const GetProgramAccountsRpcResult = jsonRpcResult(
|
|||||||
/**
|
/**
|
||||||
* Expected JSON RPC response for the "confirmTransaction" message
|
* Expected JSON RPC response for the "confirmTransaction" message
|
||||||
*/
|
*/
|
||||||
const ConfirmTransactionRpcResult = jsonRpcResult('boolean');
|
const ConfirmTransactionAndContextRpcResult = jsonRpcResultAndContext(
|
||||||
|
'boolean',
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expected JSON RPC response for the "getSlot" message
|
* Expected JSON RPC response for the "getSlot" message
|
||||||
@ -405,7 +428,7 @@ const GetBlockRpcResult = jsonRpcResult(
|
|||||||
/**
|
/**
|
||||||
* Expected JSON RPC response for the "getRecentBlockhash" message
|
* Expected JSON RPC response for the "getRecentBlockhash" message
|
||||||
*/
|
*/
|
||||||
const GetRecentBlockhash = jsonRpcResult([
|
const GetRecentBlockhashAndContextRpcResult = jsonRpcResultAndContext([
|
||||||
'string',
|
'string',
|
||||||
struct({
|
struct({
|
||||||
burnPercent: 'number',
|
burnPercent: 'number',
|
||||||
@ -416,6 +439,7 @@ const GetRecentBlockhash = jsonRpcResult([
|
|||||||
targetSignaturesPerSlot: 'number',
|
targetSignaturesPerSlot: 'number',
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
@ -591,43 +615,94 @@ export class Connection {
|
|||||||
/**
|
/**
|
||||||
* Fetch the balance for the specified public key
|
* Fetch the balance for the specified public key
|
||||||
*/
|
*/
|
||||||
async getBalance(
|
async getBalanceAndContext(
|
||||||
publicKey: PublicKey,
|
publicKey: PublicKey,
|
||||||
commitment: ?Commitment,
|
commitment: ?Commitment,
|
||||||
): Promise<number> {
|
): Promise<RpcResponseAndContext<number>> {
|
||||||
const args = this._argsWithCommitment([publicKey.toBase58()], commitment);
|
const args = this._argsWithCommitment([publicKey.toBase58()], commitment);
|
||||||
const unsafeRes = await this._rpcRequest('getBalance', args);
|
const unsafeRes = await this._rpcRequest('getBalance', args);
|
||||||
const res = GetBalanceRpcResult(unsafeRes);
|
const res = GetBalanceAndContextRpcResult(unsafeRes);
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
throw new Error(res.error.message);
|
throw new Error(res.error.message);
|
||||||
}
|
}
|
||||||
assert(typeof res.result !== 'undefined');
|
assert(typeof res.result !== 'undefined');
|
||||||
return res.result;
|
|
||||||
|
const isV021 =
|
||||||
|
typeof res.result.context !== 'undefined' &&
|
||||||
|
typeof res.result.value !== 'undefined';
|
||||||
|
|
||||||
|
if (isV021) {
|
||||||
|
return res.result;
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
context: {
|
||||||
|
slot: NaN,
|
||||||
|
},
|
||||||
|
value: res.result,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getBalance(
|
||||||
|
publicKey: PublicKey,
|
||||||
|
commitment: ?Commitment,
|
||||||
|
): Promise<number> {
|
||||||
|
return await this.getBalanceAndContext(publicKey, commitment)
|
||||||
|
.then(x => x.value)
|
||||||
|
.catch(e => {
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch all the account info for the specified public key
|
* Fetch all the account info for the specified public key
|
||||||
*/
|
*/
|
||||||
|
async getAccountInfoAndContext(
|
||||||
|
publicKey: PublicKey,
|
||||||
|
commitment: ?Commitment,
|
||||||
|
): Promise<RpcResponseAndContext<AccountInfo>> {
|
||||||
|
const args = this._argsWithCommitment([publicKey.toBase58()], commitment);
|
||||||
|
const unsafeRes = await this._rpcRequest('getAccountInfo', args);
|
||||||
|
const res = GetAccountInfoAndContextRpcResult(unsafeRes);
|
||||||
|
if (res.error) {
|
||||||
|
throw new Error(res.error.message);
|
||||||
|
}
|
||||||
|
assert(typeof res.result !== 'undefined');
|
||||||
|
|
||||||
|
const isV021 =
|
||||||
|
typeof res.result.context !== 'undefined' &&
|
||||||
|
typeof res.result.value !== 'undefined';
|
||||||
|
|
||||||
|
const slot = isV021 ? res.result.context.slot : NaN;
|
||||||
|
const resultValue = isV021 ? res.result.value : res.result;
|
||||||
|
|
||||||
|
if (!resultValue) {
|
||||||
|
throw new Error('Invalid request');
|
||||||
|
}
|
||||||
|
|
||||||
|
const {executable, owner, lamports, data} = resultValue;
|
||||||
|
const value = {
|
||||||
|
executable,
|
||||||
|
owner: new PublicKey(owner),
|
||||||
|
lamports,
|
||||||
|
data: Buffer.from(data),
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
context: {
|
||||||
|
slot,
|
||||||
|
},
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
}
|
||||||
async getAccountInfo(
|
async getAccountInfo(
|
||||||
publicKey: PublicKey,
|
publicKey: PublicKey,
|
||||||
commitment: ?Commitment,
|
commitment: ?Commitment,
|
||||||
): Promise<AccountInfo> {
|
): Promise<AccountInfo> {
|
||||||
const args = this._argsWithCommitment([publicKey.toBase58()], commitment);
|
return await this.getAccountInfoAndContext(publicKey, commitment)
|
||||||
const unsafeRes = await this._rpcRequest('getAccountInfo', args);
|
.then(x => x.value)
|
||||||
const res = GetAccountInfoRpcResult(unsafeRes);
|
.catch(e => {
|
||||||
if (res.error) {
|
throw e;
|
||||||
throw new Error(res.error.message);
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const {result} = res;
|
|
||||||
assert(typeof result !== 'undefined');
|
|
||||||
|
|
||||||
return {
|
|
||||||
executable: result.executable,
|
|
||||||
owner: new PublicKey(result.owner),
|
|
||||||
lamports: result.lamports,
|
|
||||||
data: Buffer.from(result.data),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -663,18 +738,42 @@ export class Connection {
|
|||||||
/**
|
/**
|
||||||
* Confirm the transaction identified by the specified signature
|
* Confirm the transaction identified by the specified signature
|
||||||
*/
|
*/
|
||||||
async confirmTransaction(
|
async confirmTransactionAndContext(
|
||||||
signature: TransactionSignature,
|
signature: TransactionSignature,
|
||||||
commitment: ?Commitment,
|
commitment: ?Commitment,
|
||||||
): Promise<boolean> {
|
): Promise<RpcResponseAndContext<boolean>> {
|
||||||
const args = this._argsWithCommitment([signature], commitment);
|
const args = this._argsWithCommitment([signature], commitment);
|
||||||
const unsafeRes = await this._rpcRequest('confirmTransaction', args);
|
const unsafeRes = await this._rpcRequest('confirmTransaction', args);
|
||||||
const res = ConfirmTransactionRpcResult(unsafeRes);
|
const res = ConfirmTransactionAndContextRpcResult(unsafeRes);
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
throw new Error(res.error.message);
|
throw new Error(res.error.message);
|
||||||
}
|
}
|
||||||
assert(typeof res.result !== 'undefined');
|
assert(typeof res.result !== 'undefined');
|
||||||
return res.result;
|
|
||||||
|
const isV021 =
|
||||||
|
typeof res.result.context !== 'undefined' &&
|
||||||
|
typeof res.result.value !== 'undefined';
|
||||||
|
|
||||||
|
if (isV021) {
|
||||||
|
return res.result;
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
context: {
|
||||||
|
slot: NaN,
|
||||||
|
},
|
||||||
|
value: res.result,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async confirmTransaction(
|
||||||
|
signature: TransactionSignature,
|
||||||
|
commitment: ?Commitment,
|
||||||
|
): Promise<boolean> {
|
||||||
|
return await this.confirmTransactionAndContext(signature, commitment)
|
||||||
|
.then(x => x.value)
|
||||||
|
.catch(e => {
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -862,9 +961,9 @@ export class Connection {
|
|||||||
/**
|
/**
|
||||||
* Fetch a recent blockhash from the cluster
|
* Fetch a recent blockhash from the cluster
|
||||||
*/
|
*/
|
||||||
async getRecentBlockhash(
|
async getRecentBlockhashAndContext(
|
||||||
commitment: ?Commitment,
|
commitment: ?Commitment,
|
||||||
): Promise<BlockhashAndFeeCalculator> {
|
): Promise<RpcResponseAndContext<BlockhashAndFeeCalculator>> {
|
||||||
const args = this._argsWithCommitment([], commitment);
|
const args = this._argsWithCommitment([], commitment);
|
||||||
const unsafeRes = await this._rpcRequest('getRecentBlockhash', args);
|
const unsafeRes = await this._rpcRequest('getRecentBlockhash', args);
|
||||||
|
|
||||||
@ -876,18 +975,47 @@ export class Connection {
|
|||||||
}
|
}
|
||||||
const [blockhash, feeCalculator] = res_016.result;
|
const [blockhash, feeCalculator] = res_016.result;
|
||||||
feeCalculator.burnPercent = 0;
|
feeCalculator.burnPercent = 0;
|
||||||
return [blockhash, feeCalculator];
|
|
||||||
|
return {
|
||||||
|
context: {
|
||||||
|
slot: NaN,
|
||||||
|
},
|
||||||
|
value: [blockhash, feeCalculator],
|
||||||
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Not legacy format
|
// Not legacy format
|
||||||
}
|
}
|
||||||
// End Legacy v0.16 response
|
// End Legacy v0.16 response
|
||||||
|
|
||||||
const res = GetRecentBlockhash(unsafeRes);
|
const res = GetRecentBlockhashAndContextRpcResult(unsafeRes);
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
throw new Error(res.error.message);
|
throw new Error(res.error.message);
|
||||||
}
|
}
|
||||||
assert(typeof res.result !== 'undefined');
|
assert(typeof res.result !== 'undefined');
|
||||||
return res.result;
|
|
||||||
|
const isV021 =
|
||||||
|
typeof res.result.context !== 'undefined' &&
|
||||||
|
typeof res.result.value !== 'undefined';
|
||||||
|
|
||||||
|
if (isV021) {
|
||||||
|
return res.result;
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
context: {
|
||||||
|
slot: NaN,
|
||||||
|
},
|
||||||
|
value: res.result,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getRecentBlockhash(
|
||||||
|
commitment: ?Commitment,
|
||||||
|
): Promise<BlockhashAndFeeCalculator> {
|
||||||
|
return await this.getRecentBlockhashAndContext(commitment)
|
||||||
|
.then(x => x.value)
|
||||||
|
.catch(e => {
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user