feat: implement new response format for __WithCommitment() methods

This commit is contained in:
Sunny Gleason 2019-11-13 17:31:31 -05:00 committed by Michael Vines
parent 890f6f4d9d
commit 5212a98b83
2 changed files with 208 additions and 58 deletions

View File

@ -47,6 +47,13 @@ declare module '@solana/web3.js' {
/* TODO */
// === src/connection.js ===
declare export type RpcResponseAndContext<T> = {
context: {
slot: number,
},
value: T,
};
declare export type Commitment = 'max' | 'recent';
declare export type AccountInfo = {
@ -111,6 +118,10 @@ declare module '@solana/web3.js' {
declare export class Connection {
constructor(endpoint: string, commitment: ?Commitment): Connection;
getAccountInfoAndContext(
publicKey: PublicKey,
commitment: ?Commitment,
): Promise<RpcResponseAndContext<AccountInfo>>;
getAccountInfo(
publicKey: PublicKey,
commitment: ?Commitment,
@ -119,9 +130,17 @@ declare module '@solana/web3.js' {
programId: PublicKey,
commitment: ?Commitment,
): Promise<Array<[PublicKey, AccountInfo]>>;
getBalanceAndContext(
publicKey: PublicKey,
commitment: ?Commitment,
): Promise<RpcResponseAndContext<number>>;
getBalance(publicKey: PublicKey, commitment: ?Commitment): Promise<number>;
getClusterNodes(): Promise<Array<ContactInfo>>;
getVoteAccounts(commitment: ?Commitment): Promise<VoteAccountStatus>;
confirmTransactionAndContext(
signature: TransactionSignature,
commitment: ?Commitment,
): Promise<RpcResponseAndContext<boolean>>;
confirmTransaction(
signature: TransactionSignature,
commitment: ?Commitment,
@ -137,6 +156,9 @@ declare module '@solana/web3.js' {
getVersion(): Promise<Version>;
getInflation(commitment: ?Commitment): Promise<Inflation>;
getEpochSchedule(): Promise<EpochSchedule>;
getRecentBlockhashAndContext(
commitment: ?Commitment,
): Promise<RpcResponseAndContext<[Blockhash, FeeCalculator]>>;
getRecentBlockhash(
commitment: ?Commitment,
): Promise<[Blockhash, FeeCalculator]>;

View File

@ -18,6 +18,50 @@ import type {TransactionSignature} from './transaction';
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
* '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
*/
const GetBalanceRpcResult = struct({
jsonrpc: struct.literal('2.0'),
id: 'string',
error: 'any?',
result: 'number?',
});
const GetBalanceAndContextRpcResult = jsonRpcResultAndContext('number?');
/**
* Expected JSON RPC response for the "getVersion" message
@ -224,26 +263,6 @@ const GetVersionRpcResult = struct({
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
*/
@ -258,7 +277,9 @@ const AccountInfoResult = struct({
/**
* 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
@ -291,7 +312,9 @@ const GetProgramAccountsRpcResult = jsonRpcResult(
/**
* Expected JSON RPC response for the "confirmTransaction" message
*/
const ConfirmTransactionRpcResult = jsonRpcResult('boolean');
const ConfirmTransactionAndContextRpcResult = jsonRpcResultAndContext(
'boolean',
);
/**
* Expected JSON RPC response for the "getSlot" message
@ -405,7 +428,7 @@ const GetBlockRpcResult = jsonRpcResult(
/**
* Expected JSON RPC response for the "getRecentBlockhash" message
*/
const GetRecentBlockhash = jsonRpcResult([
const GetRecentBlockhashAndContextRpcResult = jsonRpcResultAndContext([
'string',
struct({
burnPercent: 'number',
@ -416,6 +439,7 @@ const GetRecentBlockhash = jsonRpcResult([
targetSignaturesPerSlot: 'number',
}),
]);
/**
* @ignore
*/
@ -591,43 +615,94 @@ export class Connection {
/**
* Fetch the balance for the specified public key
*/
async getBalance(
async getBalanceAndContext(
publicKey: PublicKey,
commitment: ?Commitment,
): Promise<number> {
): Promise<RpcResponseAndContext<number>> {
const args = this._argsWithCommitment([publicKey.toBase58()], commitment);
const unsafeRes = await this._rpcRequest('getBalance', args);
const res = GetBalanceRpcResult(unsafeRes);
const res = GetBalanceAndContextRpcResult(unsafeRes);
if (res.error) {
throw new Error(res.error.message);
}
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
*/
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(
publicKey: PublicKey,
commitment: ?Commitment,
): Promise<AccountInfo> {
const args = this._argsWithCommitment([publicKey.toBase58()], commitment);
const unsafeRes = await this._rpcRequest('getAccountInfo', args);
const res = GetAccountInfoRpcResult(unsafeRes);
if (res.error) {
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),
};
return await this.getAccountInfoAndContext(publicKey, commitment)
.then(x => x.value)
.catch(e => {
throw e;
});
}
/**
@ -663,18 +738,42 @@ export class Connection {
/**
* Confirm the transaction identified by the specified signature
*/
async confirmTransaction(
async confirmTransactionAndContext(
signature: TransactionSignature,
commitment: ?Commitment,
): Promise<boolean> {
): Promise<RpcResponseAndContext<boolean>> {
const args = this._argsWithCommitment([signature], commitment);
const unsafeRes = await this._rpcRequest('confirmTransaction', args);
const res = ConfirmTransactionRpcResult(unsafeRes);
const res = ConfirmTransactionAndContextRpcResult(unsafeRes);
if (res.error) {
throw new Error(res.error.message);
}
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
*/
async getRecentBlockhash(
async getRecentBlockhashAndContext(
commitment: ?Commitment,
): Promise<BlockhashAndFeeCalculator> {
): Promise<RpcResponseAndContext<BlockhashAndFeeCalculator>> {
const args = this._argsWithCommitment([], commitment);
const unsafeRes = await this._rpcRequest('getRecentBlockhash', args);
@ -876,18 +975,47 @@ export class Connection {
}
const [blockhash, feeCalculator] = res_016.result;
feeCalculator.burnPercent = 0;
return [blockhash, feeCalculator];
return {
context: {
slot: NaN,
},
value: [blockhash, feeCalculator],
};
} catch (e) {
// Not legacy format
}
// End Legacy v0.16 response
const res = GetRecentBlockhash(unsafeRes);
const res = GetRecentBlockhashAndContextRpcResult(unsafeRes);
if (res.error) {
throw new Error(res.error.message);
}
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;
});
}
/**