@solana/web3.js 0.0.0-next
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +158 -0
- package/lib/index.browser.cjs.js +10062 -0
- package/lib/index.browser.cjs.js.map +1 -0
- package/lib/index.browser.esm.js +9976 -0
- package/lib/index.browser.esm.js.map +1 -0
- package/lib/index.cjs.js +9568 -0
- package/lib/index.cjs.js.map +1 -0
- package/lib/index.d.ts +3311 -0
- package/lib/index.esm.js +9479 -0
- package/lib/index.esm.js.map +1 -0
- package/lib/index.iife.js +30136 -0
- package/lib/index.iife.js.map +1 -0
- package/lib/index.iife.min.js +40 -0
- package/lib/index.iife.min.js.map +1 -0
- package/package.json +140 -0
- package/src/account.ts +46 -0
- package/src/agent-manager.ts +44 -0
- package/src/blockhash.ts +4 -0
- package/src/bpf-loader-deprecated.ts +5 -0
- package/src/bpf-loader.ts +45 -0
- package/src/connection.ts +4935 -0
- package/src/ed25519-program.ts +157 -0
- package/src/epoch-schedule.ts +102 -0
- package/src/errors.ts +9 -0
- package/src/fee-calculator.ts +16 -0
- package/src/index.ts +31 -0
- package/src/instruction.ts +58 -0
- package/src/keypair.ts +98 -0
- package/src/layout.ts +147 -0
- package/src/loader.ts +236 -0
- package/src/message.ts +271 -0
- package/src/nonce-account.ts +78 -0
- package/src/publickey.ts +296 -0
- package/src/secp256k1-program.ts +229 -0
- package/src/stake-program.ts +923 -0
- package/src/system-program.ts +1007 -0
- package/src/sysvar.ts +37 -0
- package/src/timing.ts +23 -0
- package/src/transaction.ts +808 -0
- package/src/util/assert.ts +8 -0
- package/src/util/borsh-schema.ts +38 -0
- package/src/util/cluster.ts +31 -0
- package/src/util/promise-timeout.ts +14 -0
- package/src/util/send-and-confirm-raw-transaction.ts +46 -0
- package/src/util/send-and-confirm-transaction.ts +50 -0
- package/src/util/shortvec-encoding.ts +28 -0
- package/src/util/sleep.ts +4 -0
- package/src/util/to-buffer.ts +11 -0
- package/src/util/url.ts +18 -0
- package/src/validator-info.ts +106 -0
- package/src/vote-account.ts +236 -0
- package/src/vote-program.ts +413 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {Buffer} from 'buffer';
|
|
2
|
+
import {serialize, deserialize, deserializeUnchecked} from 'borsh';
|
|
3
|
+
|
|
4
|
+
// Class wrapping a plain object
|
|
5
|
+
export class Struct {
|
|
6
|
+
constructor(properties: any) {
|
|
7
|
+
Object.assign(this, properties);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
encode(): Buffer {
|
|
11
|
+
return Buffer.from(serialize(SOLANA_SCHEMA, this));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static decode(data: Buffer): any {
|
|
15
|
+
return deserialize(SOLANA_SCHEMA, this, data);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static decodeUnchecked(data: Buffer): any {
|
|
19
|
+
return deserializeUnchecked(SOLANA_SCHEMA, this, data);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Class representing a Rust-compatible enum, since enums are only strings or
|
|
24
|
+
// numbers in pure JS
|
|
25
|
+
export class Enum extends Struct {
|
|
26
|
+
enum: string = '';
|
|
27
|
+
constructor(properties: any) {
|
|
28
|
+
super(properties);
|
|
29
|
+
if (Object.keys(properties).length !== 1) {
|
|
30
|
+
throw new Error('Enum can only take single value');
|
|
31
|
+
}
|
|
32
|
+
Object.keys(properties).map(key => {
|
|
33
|
+
this.enum = key;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const SOLANA_SCHEMA: Map<Function, any> = new Map();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const endpoint = {
|
|
2
|
+
http: {
|
|
3
|
+
devnet: 'http://api.devnet.solana.com',
|
|
4
|
+
testnet: 'http://api.testnet.solana.com',
|
|
5
|
+
'mainnet-beta': 'http://api.mainnet-beta.solana.com/',
|
|
6
|
+
},
|
|
7
|
+
https: {
|
|
8
|
+
devnet: 'https://api.devnet.solana.com',
|
|
9
|
+
testnet: 'https://api.testnet.solana.com',
|
|
10
|
+
'mainnet-beta': 'https://api.mainnet-beta.solana.com/',
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type Cluster = 'devnet' | 'testnet' | 'mainnet-beta';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Retrieves the RPC API URL for the specified cluster
|
|
18
|
+
*/
|
|
19
|
+
export function clusterApiUrl(cluster?: Cluster, tls?: boolean): string {
|
|
20
|
+
const key = tls === false ? 'http' : 'https';
|
|
21
|
+
|
|
22
|
+
if (!cluster) {
|
|
23
|
+
return endpoint[key]['devnet'];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const url = endpoint[key][cluster];
|
|
27
|
+
if (!url) {
|
|
28
|
+
throw new Error(`Unknown ${key} cluster: ${cluster}`);
|
|
29
|
+
}
|
|
30
|
+
return url;
|
|
31
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function promiseTimeout<T>(
|
|
2
|
+
promise: Promise<T>,
|
|
3
|
+
timeoutMs: number,
|
|
4
|
+
): Promise<T | null> {
|
|
5
|
+
let timeoutId: ReturnType<typeof setTimeout>;
|
|
6
|
+
const timeoutPromise: Promise<null> = new Promise(resolve => {
|
|
7
|
+
timeoutId = setTimeout(() => resolve(null), timeoutMs);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
return Promise.race([promise, timeoutPromise]).then((result: T | null) => {
|
|
11
|
+
clearTimeout(timeoutId);
|
|
12
|
+
return result;
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type {Buffer} from 'buffer';
|
|
2
|
+
|
|
3
|
+
import {Connection} from '../connection';
|
|
4
|
+
import type {TransactionSignature} from '../transaction';
|
|
5
|
+
import type {ConfirmOptions} from '../connection';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Send and confirm a raw transaction
|
|
9
|
+
*
|
|
10
|
+
* If `commitment` option is not specified, defaults to 'max' commitment.
|
|
11
|
+
*
|
|
12
|
+
* @param {Connection} connection
|
|
13
|
+
* @param {Buffer} rawTransaction
|
|
14
|
+
* @param {ConfirmOptions} [options]
|
|
15
|
+
* @returns {Promise<TransactionSignature>}
|
|
16
|
+
*/
|
|
17
|
+
export async function sendAndConfirmRawTransaction(
|
|
18
|
+
connection: Connection,
|
|
19
|
+
rawTransaction: Buffer,
|
|
20
|
+
options?: ConfirmOptions,
|
|
21
|
+
): Promise<TransactionSignature> {
|
|
22
|
+
const sendOptions = options && {
|
|
23
|
+
skipPreflight: options.skipPreflight,
|
|
24
|
+
preflightCommitment: options.preflightCommitment || options.commitment,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const signature = await connection.sendRawTransaction(
|
|
28
|
+
rawTransaction,
|
|
29
|
+
sendOptions,
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const status = (
|
|
33
|
+
await connection.confirmTransaction(
|
|
34
|
+
signature,
|
|
35
|
+
options && options.commitment,
|
|
36
|
+
)
|
|
37
|
+
).value;
|
|
38
|
+
|
|
39
|
+
if (status.err) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`Raw transaction ${signature} failed (${JSON.stringify(status)})`,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return signature;
|
|
46
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {Connection} from '../connection';
|
|
2
|
+
import {Transaction} from '../transaction';
|
|
3
|
+
import type {ConfirmOptions} from '../connection';
|
|
4
|
+
import type {Signer} from '../keypair';
|
|
5
|
+
import type {TransactionSignature} from '../transaction';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Sign, send and confirm a transaction.
|
|
9
|
+
*
|
|
10
|
+
* If `commitment` option is not specified, defaults to 'max' commitment.
|
|
11
|
+
*
|
|
12
|
+
* @param {Connection} connection
|
|
13
|
+
* @param {Transaction} transaction
|
|
14
|
+
* @param {Array<Signer>} signers
|
|
15
|
+
* @param {ConfirmOptions} [options]
|
|
16
|
+
* @returns {Promise<TransactionSignature>}
|
|
17
|
+
*/
|
|
18
|
+
export async function sendAndConfirmTransaction(
|
|
19
|
+
connection: Connection,
|
|
20
|
+
transaction: Transaction,
|
|
21
|
+
signers: Array<Signer>,
|
|
22
|
+
options?: ConfirmOptions,
|
|
23
|
+
): Promise<TransactionSignature> {
|
|
24
|
+
const sendOptions = options && {
|
|
25
|
+
skipPreflight: options.skipPreflight,
|
|
26
|
+
preflightCommitment: options.preflightCommitment || options.commitment,
|
|
27
|
+
maxRetries: options.maxRetries,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const signature = await connection.sendTransaction(
|
|
31
|
+
transaction,
|
|
32
|
+
signers,
|
|
33
|
+
sendOptions,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const status = (
|
|
37
|
+
await connection.confirmTransaction(
|
|
38
|
+
signature,
|
|
39
|
+
options && options.commitment,
|
|
40
|
+
)
|
|
41
|
+
).value;
|
|
42
|
+
|
|
43
|
+
if (status.err) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Transaction ${signature} failed (${JSON.stringify(status)})`,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return signature;
|
|
50
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function decodeLength(bytes: Array<number>): number {
|
|
2
|
+
let len = 0;
|
|
3
|
+
let size = 0;
|
|
4
|
+
for (;;) {
|
|
5
|
+
let elem = bytes.shift() as number;
|
|
6
|
+
len |= (elem & 0x7f) << (size * 7);
|
|
7
|
+
size += 1;
|
|
8
|
+
if ((elem & 0x80) === 0) {
|
|
9
|
+
break;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return len;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function encodeLength(bytes: Array<number>, len: number) {
|
|
16
|
+
let rem_len = len;
|
|
17
|
+
for (;;) {
|
|
18
|
+
let elem = rem_len & 0x7f;
|
|
19
|
+
rem_len >>= 7;
|
|
20
|
+
if (rem_len == 0) {
|
|
21
|
+
bytes.push(elem);
|
|
22
|
+
break;
|
|
23
|
+
} else {
|
|
24
|
+
elem |= 0x80;
|
|
25
|
+
bytes.push(elem);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {Buffer} from 'buffer';
|
|
2
|
+
|
|
3
|
+
export const toBuffer = (arr: Buffer | Uint8Array | Array<number>): Buffer => {
|
|
4
|
+
if (Buffer.isBuffer(arr)) {
|
|
5
|
+
return arr;
|
|
6
|
+
} else if (arr instanceof Uint8Array) {
|
|
7
|
+
return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
8
|
+
} else {
|
|
9
|
+
return Buffer.from(arr);
|
|
10
|
+
}
|
|
11
|
+
};
|
package/src/util/url.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function makeWebsocketUrl(endpoint: string) {
|
|
2
|
+
let url = new URL(endpoint);
|
|
3
|
+
const useHttps = url.protocol === 'https:';
|
|
4
|
+
|
|
5
|
+
url.protocol = useHttps ? 'wss:' : 'ws:';
|
|
6
|
+
url.host = '';
|
|
7
|
+
|
|
8
|
+
// Only shift the port by +1 as a convention for ws(s) only if given endpoint
|
|
9
|
+
// is explictly specifying the endpoint port (HTTP-based RPC), assuming
|
|
10
|
+
// we're directly trying to connect to solana-validator's ws listening port.
|
|
11
|
+
// When the endpoint omits the port, we're connecting to the protocol
|
|
12
|
+
// default ports: http(80) or https(443) and it's assumed we're behind a reverse
|
|
13
|
+
// proxy which manages WebSocket upgrade and backend port redirection.
|
|
14
|
+
if (url.port !== '') {
|
|
15
|
+
url.port = String(Number(url.port) + 1);
|
|
16
|
+
}
|
|
17
|
+
return url.toString();
|
|
18
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import {Buffer} from 'buffer';
|
|
2
|
+
import {
|
|
3
|
+
assert as assertType,
|
|
4
|
+
optional,
|
|
5
|
+
string,
|
|
6
|
+
type as pick,
|
|
7
|
+
} from 'superstruct';
|
|
8
|
+
|
|
9
|
+
import * as Layout from './layout';
|
|
10
|
+
import * as shortvec from './util/shortvec-encoding';
|
|
11
|
+
import {PublicKey} from './publickey';
|
|
12
|
+
|
|
13
|
+
export const VALIDATOR_INFO_KEY = new PublicKey(
|
|
14
|
+
'Va1idator1nfo111111111111111111111111111111',
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
type ConfigKey = {
|
|
21
|
+
publicKey: PublicKey;
|
|
22
|
+
isSigner: boolean;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Info used to identity validators.
|
|
27
|
+
*/
|
|
28
|
+
export type Info = {
|
|
29
|
+
/** validator name */
|
|
30
|
+
name: string;
|
|
31
|
+
/** optional, validator website */
|
|
32
|
+
website?: string;
|
|
33
|
+
/** optional, extra information the validator chose to share */
|
|
34
|
+
details?: string;
|
|
35
|
+
/** optional, used to identify validators on keybase.io */
|
|
36
|
+
keybaseUsername?: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const InfoString = pick({
|
|
40
|
+
name: string(),
|
|
41
|
+
website: optional(string()),
|
|
42
|
+
details: optional(string()),
|
|
43
|
+
keybaseUsername: optional(string()),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* ValidatorInfo class
|
|
48
|
+
*/
|
|
49
|
+
export class ValidatorInfo {
|
|
50
|
+
/**
|
|
51
|
+
* validator public key
|
|
52
|
+
*/
|
|
53
|
+
key: PublicKey;
|
|
54
|
+
/**
|
|
55
|
+
* validator information
|
|
56
|
+
*/
|
|
57
|
+
info: Info;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Construct a valid ValidatorInfo
|
|
61
|
+
*
|
|
62
|
+
* @param key validator public key
|
|
63
|
+
* @param info validator information
|
|
64
|
+
*/
|
|
65
|
+
constructor(key: PublicKey, info: Info) {
|
|
66
|
+
this.key = key;
|
|
67
|
+
this.info = info;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Deserialize ValidatorInfo from the config account data. Exactly two config
|
|
72
|
+
* keys are required in the data.
|
|
73
|
+
*
|
|
74
|
+
* @param buffer config account data
|
|
75
|
+
* @return null if info was not found
|
|
76
|
+
*/
|
|
77
|
+
static fromConfigData(
|
|
78
|
+
buffer: Buffer | Uint8Array | Array<number>,
|
|
79
|
+
): ValidatorInfo | null {
|
|
80
|
+
const PUBKEY_LENGTH = 32;
|
|
81
|
+
|
|
82
|
+
let byteArray = [...buffer];
|
|
83
|
+
const configKeyCount = shortvec.decodeLength(byteArray);
|
|
84
|
+
if (configKeyCount !== 2) return null;
|
|
85
|
+
|
|
86
|
+
const configKeys: Array<ConfigKey> = [];
|
|
87
|
+
for (let i = 0; i < 2; i++) {
|
|
88
|
+
const publicKey = new PublicKey(byteArray.slice(0, PUBKEY_LENGTH));
|
|
89
|
+
byteArray = byteArray.slice(PUBKEY_LENGTH);
|
|
90
|
+
const isSigner = byteArray.slice(0, 1)[0] === 1;
|
|
91
|
+
byteArray = byteArray.slice(1);
|
|
92
|
+
configKeys.push({publicKey, isSigner});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (configKeys[0].publicKey.equals(VALIDATOR_INFO_KEY)) {
|
|
96
|
+
if (configKeys[1].isSigner) {
|
|
97
|
+
const rawInfo: any = Layout.rustString().decode(Buffer.from(byteArray));
|
|
98
|
+
const info = JSON.parse(rawInfo as string);
|
|
99
|
+
assertType(info, InfoString);
|
|
100
|
+
return new ValidatorInfo(configKeys[1].publicKey, info);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import * as BufferLayout from '@solana/buffer-layout';
|
|
2
|
+
import type {Buffer} from 'buffer';
|
|
3
|
+
|
|
4
|
+
import * as Layout from './layout';
|
|
5
|
+
import {PublicKey} from './publickey';
|
|
6
|
+
import {toBuffer} from './util/to-buffer';
|
|
7
|
+
|
|
8
|
+
export const VOTE_PROGRAM_ID = new PublicKey(
|
|
9
|
+
'Vote111111111111111111111111111111111111111',
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
export type Lockout = {
|
|
13
|
+
slot: number;
|
|
14
|
+
confirmationCount: number;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* History of how many credits earned by the end of each epoch
|
|
19
|
+
*/
|
|
20
|
+
export type EpochCredits = Readonly<{
|
|
21
|
+
epoch: number;
|
|
22
|
+
credits: number;
|
|
23
|
+
prevCredits: number;
|
|
24
|
+
}>;
|
|
25
|
+
|
|
26
|
+
export type AuthorizedVoter = Readonly<{
|
|
27
|
+
epoch: number;
|
|
28
|
+
authorizedVoter: PublicKey;
|
|
29
|
+
}>;
|
|
30
|
+
|
|
31
|
+
type AuthorizedVoterRaw = Readonly<{
|
|
32
|
+
authorizedVoter: Uint8Array;
|
|
33
|
+
epoch: number;
|
|
34
|
+
}>;
|
|
35
|
+
|
|
36
|
+
type PriorVoters = Readonly<{
|
|
37
|
+
buf: PriorVoterRaw[];
|
|
38
|
+
idx: number;
|
|
39
|
+
isEmpty: number;
|
|
40
|
+
}>;
|
|
41
|
+
|
|
42
|
+
export type PriorVoter = Readonly<{
|
|
43
|
+
authorizedPubkey: PublicKey;
|
|
44
|
+
epochOfLastAuthorizedSwitch: number;
|
|
45
|
+
targetEpoch: number;
|
|
46
|
+
}>;
|
|
47
|
+
|
|
48
|
+
type PriorVoterRaw = Readonly<{
|
|
49
|
+
authorizedPubkey: Uint8Array;
|
|
50
|
+
epochOfLastAuthorizedSwitch: number;
|
|
51
|
+
targetEpoch: number;
|
|
52
|
+
}>;
|
|
53
|
+
|
|
54
|
+
export type BlockTimestamp = Readonly<{
|
|
55
|
+
slot: number;
|
|
56
|
+
timestamp: number;
|
|
57
|
+
}>;
|
|
58
|
+
|
|
59
|
+
type VoteAccountData = Readonly<{
|
|
60
|
+
authorizedVoters: AuthorizedVoterRaw[];
|
|
61
|
+
authorizedWithdrawer: Uint8Array;
|
|
62
|
+
commission: number;
|
|
63
|
+
epochCredits: EpochCredits[];
|
|
64
|
+
lastTimestamp: BlockTimestamp;
|
|
65
|
+
nodePubkey: Uint8Array;
|
|
66
|
+
priorVoters: PriorVoters;
|
|
67
|
+
rootSlot: number;
|
|
68
|
+
rootSlotValid: number;
|
|
69
|
+
votes: Lockout[];
|
|
70
|
+
}>;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* See https://github.com/solana-labs/solana/blob/8a12ed029cfa38d4a45400916c2463fb82bbec8c/programs/vote_api/src/vote_state.rs#L68-L88
|
|
74
|
+
*
|
|
75
|
+
* @internal
|
|
76
|
+
*/
|
|
77
|
+
const VoteAccountLayout = BufferLayout.struct<VoteAccountData>([
|
|
78
|
+
Layout.publicKey('nodePubkey'),
|
|
79
|
+
Layout.publicKey('authorizedWithdrawer'),
|
|
80
|
+
BufferLayout.u8('commission'),
|
|
81
|
+
BufferLayout.nu64(), // votes.length
|
|
82
|
+
BufferLayout.seq<Lockout>(
|
|
83
|
+
BufferLayout.struct([
|
|
84
|
+
BufferLayout.nu64('slot'),
|
|
85
|
+
BufferLayout.u32('confirmationCount'),
|
|
86
|
+
]),
|
|
87
|
+
BufferLayout.offset(BufferLayout.u32(), -8),
|
|
88
|
+
'votes',
|
|
89
|
+
),
|
|
90
|
+
BufferLayout.u8('rootSlotValid'),
|
|
91
|
+
BufferLayout.nu64('rootSlot'),
|
|
92
|
+
BufferLayout.nu64(), // authorizedVoters.length
|
|
93
|
+
BufferLayout.seq<AuthorizedVoterRaw>(
|
|
94
|
+
BufferLayout.struct([
|
|
95
|
+
BufferLayout.nu64('epoch'),
|
|
96
|
+
Layout.publicKey('authorizedVoter'),
|
|
97
|
+
]),
|
|
98
|
+
BufferLayout.offset(BufferLayout.u32(), -8),
|
|
99
|
+
'authorizedVoters',
|
|
100
|
+
),
|
|
101
|
+
BufferLayout.struct<PriorVoters>(
|
|
102
|
+
[
|
|
103
|
+
BufferLayout.seq(
|
|
104
|
+
BufferLayout.struct([
|
|
105
|
+
Layout.publicKey('authorizedPubkey'),
|
|
106
|
+
BufferLayout.nu64('epochOfLastAuthorizedSwitch'),
|
|
107
|
+
BufferLayout.nu64('targetEpoch'),
|
|
108
|
+
]),
|
|
109
|
+
32,
|
|
110
|
+
'buf',
|
|
111
|
+
),
|
|
112
|
+
BufferLayout.nu64('idx'),
|
|
113
|
+
BufferLayout.u8('isEmpty'),
|
|
114
|
+
],
|
|
115
|
+
'priorVoters',
|
|
116
|
+
),
|
|
117
|
+
BufferLayout.nu64(), // epochCredits.length
|
|
118
|
+
BufferLayout.seq<EpochCredits>(
|
|
119
|
+
BufferLayout.struct([
|
|
120
|
+
BufferLayout.nu64('epoch'),
|
|
121
|
+
BufferLayout.nu64('credits'),
|
|
122
|
+
BufferLayout.nu64('prevCredits'),
|
|
123
|
+
]),
|
|
124
|
+
BufferLayout.offset(BufferLayout.u32(), -8),
|
|
125
|
+
'epochCredits',
|
|
126
|
+
),
|
|
127
|
+
BufferLayout.struct<BlockTimestamp>(
|
|
128
|
+
[BufferLayout.nu64('slot'), BufferLayout.nu64('timestamp')],
|
|
129
|
+
'lastTimestamp',
|
|
130
|
+
),
|
|
131
|
+
]);
|
|
132
|
+
|
|
133
|
+
type VoteAccountArgs = {
|
|
134
|
+
nodePubkey: PublicKey;
|
|
135
|
+
authorizedWithdrawer: PublicKey;
|
|
136
|
+
commission: number;
|
|
137
|
+
rootSlot: number | null;
|
|
138
|
+
votes: Lockout[];
|
|
139
|
+
authorizedVoters: AuthorizedVoter[];
|
|
140
|
+
priorVoters: PriorVoter[];
|
|
141
|
+
epochCredits: EpochCredits[];
|
|
142
|
+
lastTimestamp: BlockTimestamp;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* VoteAccount class
|
|
147
|
+
*/
|
|
148
|
+
export class VoteAccount {
|
|
149
|
+
nodePubkey: PublicKey;
|
|
150
|
+
authorizedWithdrawer: PublicKey;
|
|
151
|
+
commission: number;
|
|
152
|
+
rootSlot: number | null;
|
|
153
|
+
votes: Lockout[];
|
|
154
|
+
authorizedVoters: AuthorizedVoter[];
|
|
155
|
+
priorVoters: PriorVoter[];
|
|
156
|
+
epochCredits: EpochCredits[];
|
|
157
|
+
lastTimestamp: BlockTimestamp;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @internal
|
|
161
|
+
*/
|
|
162
|
+
constructor(args: VoteAccountArgs) {
|
|
163
|
+
this.nodePubkey = args.nodePubkey;
|
|
164
|
+
this.authorizedWithdrawer = args.authorizedWithdrawer;
|
|
165
|
+
this.commission = args.commission;
|
|
166
|
+
this.rootSlot = args.rootSlot;
|
|
167
|
+
this.votes = args.votes;
|
|
168
|
+
this.authorizedVoters = args.authorizedVoters;
|
|
169
|
+
this.priorVoters = args.priorVoters;
|
|
170
|
+
this.epochCredits = args.epochCredits;
|
|
171
|
+
this.lastTimestamp = args.lastTimestamp;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Deserialize VoteAccount from the account data.
|
|
176
|
+
*
|
|
177
|
+
* @param buffer account data
|
|
178
|
+
* @return VoteAccount
|
|
179
|
+
*/
|
|
180
|
+
static fromAccountData(
|
|
181
|
+
buffer: Buffer | Uint8Array | Array<number>,
|
|
182
|
+
): VoteAccount {
|
|
183
|
+
const versionOffset = 4;
|
|
184
|
+
const va = VoteAccountLayout.decode(toBuffer(buffer), versionOffset);
|
|
185
|
+
|
|
186
|
+
let rootSlot: number | null = va.rootSlot;
|
|
187
|
+
if (!va.rootSlotValid) {
|
|
188
|
+
rootSlot = null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return new VoteAccount({
|
|
192
|
+
nodePubkey: new PublicKey(va.nodePubkey),
|
|
193
|
+
authorizedWithdrawer: new PublicKey(va.authorizedWithdrawer),
|
|
194
|
+
commission: va.commission,
|
|
195
|
+
votes: va.votes,
|
|
196
|
+
rootSlot,
|
|
197
|
+
authorizedVoters: va.authorizedVoters.map(parseAuthorizedVoter),
|
|
198
|
+
priorVoters: getPriorVoters(va.priorVoters),
|
|
199
|
+
epochCredits: va.epochCredits,
|
|
200
|
+
lastTimestamp: va.lastTimestamp,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function parseAuthorizedVoter({
|
|
206
|
+
authorizedVoter,
|
|
207
|
+
epoch,
|
|
208
|
+
}: AuthorizedVoterRaw): AuthorizedVoter {
|
|
209
|
+
return {
|
|
210
|
+
epoch,
|
|
211
|
+
authorizedVoter: new PublicKey(authorizedVoter),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function parsePriorVoters({
|
|
216
|
+
authorizedPubkey,
|
|
217
|
+
epochOfLastAuthorizedSwitch,
|
|
218
|
+
targetEpoch,
|
|
219
|
+
}: PriorVoterRaw): PriorVoter {
|
|
220
|
+
return {
|
|
221
|
+
authorizedPubkey: new PublicKey(authorizedPubkey),
|
|
222
|
+
epochOfLastAuthorizedSwitch,
|
|
223
|
+
targetEpoch,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function getPriorVoters({buf, idx, isEmpty}: PriorVoters): PriorVoter[] {
|
|
228
|
+
if (isEmpty) {
|
|
229
|
+
return [];
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return [
|
|
233
|
+
...buf.slice(idx + 1).map(parsePriorVoters),
|
|
234
|
+
...buf.slice(0, idx).map(parsePriorVoters),
|
|
235
|
+
];
|
|
236
|
+
}
|