@solana/rpc-types 6.3.1 → 6.3.2-canary-20260313112147
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/package.json +9 -8
- package/src/account-filters.ts +46 -0
- package/src/account-info.ts +57 -0
- package/src/blockhash.ts +173 -0
- package/src/cluster-url.ts +17 -0
- package/src/commitment.ts +31 -0
- package/src/encoded-bytes.ts +12 -0
- package/src/index.ts +24 -0
- package/src/lamports.ts +195 -0
- package/src/rpc-api.ts +6 -0
- package/src/stringified-bigint.ts +81 -0
- package/src/stringified-number.ts +75 -0
- package/src/token-amount.ts +34 -0
- package/src/token-balance.ts +15 -0
- package/src/transaction-error.ts +101 -0
- package/src/transaction.ts +401 -0
- package/src/typed-numbers.ts +17 -0
- package/src/unix-timestamp.ts +84 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solana/rpc-types",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.2-canary-20260313112147",
|
|
4
4
|
"description": "Type definitions for values used in the Solana RPC, and helper functions for working with them",
|
|
5
5
|
"homepage": "https://www.solanakit.com/api#solanarpc-types",
|
|
6
6
|
"exports": {
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
"types": "./dist/types/index.d.ts",
|
|
34
34
|
"type": "commonjs",
|
|
35
35
|
"files": [
|
|
36
|
-
"./dist/"
|
|
36
|
+
"./dist/",
|
|
37
|
+
"./src/"
|
|
37
38
|
],
|
|
38
39
|
"sideEffects": false,
|
|
39
40
|
"keywords": [
|
|
@@ -55,12 +56,12 @@
|
|
|
55
56
|
"maintained node versions"
|
|
56
57
|
],
|
|
57
58
|
"dependencies": {
|
|
58
|
-
"@solana/
|
|
59
|
-
"@solana/
|
|
60
|
-
"@solana/codecs-numbers": "6.3.
|
|
61
|
-
"@solana/codecs-strings": "6.3.
|
|
62
|
-
"@solana/errors": "6.3.
|
|
63
|
-
"@solana/nominal-types": "6.3.
|
|
59
|
+
"@solana/addresses": "6.3.2-canary-20260313112147",
|
|
60
|
+
"@solana/codecs-core": "6.3.2-canary-20260313112147",
|
|
61
|
+
"@solana/codecs-numbers": "6.3.2-canary-20260313112147",
|
|
62
|
+
"@solana/codecs-strings": "6.3.2-canary-20260313112147",
|
|
63
|
+
"@solana/errors": "6.3.2-canary-20260313112147",
|
|
64
|
+
"@solana/nominal-types": "6.3.2-canary-20260313112147"
|
|
64
65
|
},
|
|
65
66
|
"peerDependencies": {
|
|
66
67
|
"typescript": "^5.0.0"
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Base58EncodedBytes, Base64EncodedBytes } from './encoded-bytes';
|
|
2
|
+
|
|
3
|
+
export type DataSlice = Readonly<{
|
|
4
|
+
/** The number of bytes to return */
|
|
5
|
+
length: number;
|
|
6
|
+
/** The byte offset from which to start reading */
|
|
7
|
+
offset: number;
|
|
8
|
+
}>;
|
|
9
|
+
|
|
10
|
+
type ProgramNotificationsMemcmpFilterBase58 = Readonly<{
|
|
11
|
+
/**
|
|
12
|
+
* The bytes to match, as a base-58 encoded string.
|
|
13
|
+
*
|
|
14
|
+
* Data is limited to a maximum of 128 decoded bytes.
|
|
15
|
+
*/
|
|
16
|
+
bytes: Base58EncodedBytes;
|
|
17
|
+
/** The encoding to use when decoding the supplied byte string */
|
|
18
|
+
encoding: 'base58';
|
|
19
|
+
/** The byte offset into the account data from which to start the comparison */
|
|
20
|
+
offset: bigint;
|
|
21
|
+
}>;
|
|
22
|
+
|
|
23
|
+
type ProgramNotificationsMemcmpFilterBase64 = Readonly<{
|
|
24
|
+
/**
|
|
25
|
+
* The bytes to match, as a base-64 encoded string.
|
|
26
|
+
*
|
|
27
|
+
* Data is limited to a maximum of 128 decoded bytes.
|
|
28
|
+
*/
|
|
29
|
+
bytes: Base64EncodedBytes;
|
|
30
|
+
/** The encoding to use when decoding the supplied byte string */
|
|
31
|
+
encoding: 'base64';
|
|
32
|
+
/** The byte offset into the account data from which to start the comparison */
|
|
33
|
+
offset: bigint;
|
|
34
|
+
}>;
|
|
35
|
+
|
|
36
|
+
export type GetProgramAccountsMemcmpFilter = Readonly<{
|
|
37
|
+
/**
|
|
38
|
+
* This filter matches when the bytes supplied are equal to the account data at the given offset
|
|
39
|
+
*/
|
|
40
|
+
memcmp: ProgramNotificationsMemcmpFilterBase58 | ProgramNotificationsMemcmpFilterBase64;
|
|
41
|
+
}>;
|
|
42
|
+
|
|
43
|
+
export type GetProgramAccountsDatasizeFilter = Readonly<{
|
|
44
|
+
/** This filter matches when the account data length is equal to this */
|
|
45
|
+
dataSize: bigint;
|
|
46
|
+
}>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Address } from '@solana/addresses';
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
Base58EncodedBytes,
|
|
5
|
+
Base58EncodedDataResponse,
|
|
6
|
+
Base64EncodedDataResponse,
|
|
7
|
+
Base64EncodedZStdCompressedDataResponse,
|
|
8
|
+
} from './encoded-bytes';
|
|
9
|
+
import type { Lamports } from './lamports';
|
|
10
|
+
|
|
11
|
+
export type AccountInfoBase = Readonly<{
|
|
12
|
+
/** Indicates if the account contains a program (and is strictly read-only) */
|
|
13
|
+
executable: boolean;
|
|
14
|
+
/** Number of {@link Lamports} assigned to this account */
|
|
15
|
+
lamports: Lamports;
|
|
16
|
+
/** Address of the program this account has been assigned to */
|
|
17
|
+
owner: Address;
|
|
18
|
+
/** The size of the account data in bytes (excluding the 128 bytes of header) */
|
|
19
|
+
space: bigint;
|
|
20
|
+
}>;
|
|
21
|
+
|
|
22
|
+
/** @deprecated */
|
|
23
|
+
export type AccountInfoWithBase58Bytes = Readonly<{
|
|
24
|
+
data: Base58EncodedBytes;
|
|
25
|
+
}>;
|
|
26
|
+
|
|
27
|
+
/** @deprecated */
|
|
28
|
+
export type AccountInfoWithBase58EncodedData = Readonly<{
|
|
29
|
+
data: Base58EncodedDataResponse;
|
|
30
|
+
}>;
|
|
31
|
+
|
|
32
|
+
export type AccountInfoWithBase64EncodedData = Readonly<{
|
|
33
|
+
data: Base64EncodedDataResponse;
|
|
34
|
+
}>;
|
|
35
|
+
|
|
36
|
+
export type AccountInfoWithBase64EncodedZStdCompressedData = Readonly<{
|
|
37
|
+
data: Base64EncodedZStdCompressedDataResponse;
|
|
38
|
+
}>;
|
|
39
|
+
|
|
40
|
+
export type AccountInfoWithJsonData = Readonly<{
|
|
41
|
+
data:
|
|
42
|
+
| Base64EncodedDataResponse // If `jsonParsed` encoding is requested but a parser cannot be found for the given account the `data` field falls back to `base64`.
|
|
43
|
+
| Readonly<{
|
|
44
|
+
parsed: {
|
|
45
|
+
info?: object;
|
|
46
|
+
type: string;
|
|
47
|
+
};
|
|
48
|
+
// Name of the program that owns this account.
|
|
49
|
+
program: string;
|
|
50
|
+
space: bigint;
|
|
51
|
+
}>;
|
|
52
|
+
}>;
|
|
53
|
+
|
|
54
|
+
export type AccountInfoWithPubkey<TAccount extends AccountInfoBase> = Readonly<{
|
|
55
|
+
account: TAccount;
|
|
56
|
+
pubkey: Address;
|
|
57
|
+
}>;
|
package/src/blockhash.ts
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { Address, assertIsAddress, getAddressDecoder, getAddressEncoder, isAddress } from '@solana/addresses';
|
|
2
|
+
import { combineCodec, createEncoder, FixedSizeCodec, FixedSizeDecoder, FixedSizeEncoder } from '@solana/codecs-core';
|
|
3
|
+
import {
|
|
4
|
+
isSolanaError,
|
|
5
|
+
SOLANA_ERROR__ADDRESSES__INVALID_BYTE_LENGTH,
|
|
6
|
+
SOLANA_ERROR__ADDRESSES__STRING_LENGTH_OUT_OF_RANGE,
|
|
7
|
+
SOLANA_ERROR__BLOCKHASH_STRING_LENGTH_OUT_OF_RANGE,
|
|
8
|
+
SOLANA_ERROR__INVALID_BLOCKHASH_BYTE_LENGTH,
|
|
9
|
+
SolanaError,
|
|
10
|
+
} from '@solana/errors';
|
|
11
|
+
import { Brand, EncodedString } from '@solana/nominal-types';
|
|
12
|
+
|
|
13
|
+
export type Blockhash = Brand<EncodedString<string, 'base58'>, 'Blockhash'>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A type guard that returns `true` if the input string conforms to the {@link Blockhash} type, and
|
|
17
|
+
* refines its type for use in your program.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { isBlockhash } from '@solana/rpc-types';
|
|
22
|
+
*
|
|
23
|
+
* if (isBlockhash(blockhash)) {
|
|
24
|
+
* // At this point, `blockhash` has been refined to a
|
|
25
|
+
* // `Blockhash` that can be used with the RPC.
|
|
26
|
+
* const { value: isValid } = await rpc.isBlockhashValid(blockhash).send();
|
|
27
|
+
* setBlockhashIsFresh(isValid);
|
|
28
|
+
* } else {
|
|
29
|
+
* setError(`${blockhash} is not a blockhash`);
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function isBlockhash(putativeBlockhash: string): putativeBlockhash is Blockhash {
|
|
34
|
+
return isAddress(putativeBlockhash);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* From time to time you might acquire a string, that you expect to validate as a blockhash, from an
|
|
39
|
+
* untrusted network API or user input. Use this function to assert that such an arbitrary string is
|
|
40
|
+
* a base58-encoded blockhash.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* import { assertIsBlockhash } from '@solana/rpc-types';
|
|
45
|
+
*
|
|
46
|
+
* // Imagine a function that determines whether a blockhash is fresh when a user submits a form.
|
|
47
|
+
* function handleSubmit() {
|
|
48
|
+
* // We know only that what the user typed conforms to the `string` type.
|
|
49
|
+
* const blockhash: string = blockhashInput.value;
|
|
50
|
+
* try {
|
|
51
|
+
* // If this type assertion function doesn't throw, then
|
|
52
|
+
* // Typescript will upcast `blockhash` to `Blockhash`.
|
|
53
|
+
* assertIsBlockhash(blockhash);
|
|
54
|
+
* // At this point, `blockhash` is a `Blockhash` that can be used with the RPC.
|
|
55
|
+
* const { value: isValid } = await rpc.isBlockhashValid(blockhash).send();
|
|
56
|
+
* } catch (e) {
|
|
57
|
+
* // `blockhash` turned out not to be a base58-encoded blockhash
|
|
58
|
+
* }
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export function assertIsBlockhash(putativeBlockhash: string): asserts putativeBlockhash is Blockhash {
|
|
63
|
+
try {
|
|
64
|
+
assertIsAddress(putativeBlockhash);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
if (isSolanaError(error, SOLANA_ERROR__ADDRESSES__STRING_LENGTH_OUT_OF_RANGE)) {
|
|
67
|
+
throw new SolanaError(SOLANA_ERROR__BLOCKHASH_STRING_LENGTH_OUT_OF_RANGE, error.context);
|
|
68
|
+
}
|
|
69
|
+
if (isSolanaError(error, SOLANA_ERROR__ADDRESSES__INVALID_BYTE_LENGTH)) {
|
|
70
|
+
throw new SolanaError(SOLANA_ERROR__INVALID_BLOCKHASH_BYTE_LENGTH, error.context);
|
|
71
|
+
}
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Combines _asserting_ that a string is a blockhash with _coercing_ it to the {@link Blockhash}
|
|
78
|
+
* type. It's most useful with untrusted input.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* import { blockhash } from '@solana/rpc-types';
|
|
83
|
+
*
|
|
84
|
+
* const { value: isValid } = await rpc.isBlockhashValid(blockhash(blockhashFromUserInput)).send();
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* > [!TIP]
|
|
88
|
+
* > When starting from a known-good blockhash as a string, it's more efficient to typecast it
|
|
89
|
+
* rather than to use the {@link blockhash} helper, because the helper unconditionally performs
|
|
90
|
+
* validation on its input.
|
|
91
|
+
* >
|
|
92
|
+
* > ```ts
|
|
93
|
+
* > import { Blockhash } from '@solana/rpc-types';
|
|
94
|
+
* >
|
|
95
|
+
* > const blockhash = 'ABmPH5KDXX99u6woqFS5vfBGSNyKG42SzpvBMWWqAy48' as Blockhash;
|
|
96
|
+
* > ```
|
|
97
|
+
*/
|
|
98
|
+
export function blockhash(putativeBlockhash: string): Blockhash {
|
|
99
|
+
assertIsBlockhash(putativeBlockhash);
|
|
100
|
+
return putativeBlockhash;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Returns an encoder that you can use to encode a base58-encoded blockhash to a byte array.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* import { getBlockhashEncoder } from '@solana/rpc-types';
|
|
109
|
+
*
|
|
110
|
+
* const blockhash = 'ABmPH5KDXX99u6woqFS5vfBGSNyKG42SzpvBMWWqAy48' as Blockhash;
|
|
111
|
+
* const blockhashEncoder = getBlockhashEncoder();
|
|
112
|
+
* const blockhashBytes = blockhashEncoder.encode(blockhash);
|
|
113
|
+
* // Uint8Array(32) [
|
|
114
|
+
* // 136, 123, 44, 249, 43, 19, 60, 14,
|
|
115
|
+
* // 144, 16, 168, 241, 121, 111, 70, 232,
|
|
116
|
+
* // 186, 26, 140, 202, 213, 64, 231, 82,
|
|
117
|
+
* // 179, 66, 103, 237, 52, 117, 217, 93
|
|
118
|
+
* // ]
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export function getBlockhashEncoder(): FixedSizeEncoder<Blockhash, 32> {
|
|
122
|
+
const addressEncoder = getAddressEncoder();
|
|
123
|
+
return createEncoder({
|
|
124
|
+
fixedSize: 32,
|
|
125
|
+
write: (value: string, bytes, offset) => {
|
|
126
|
+
assertIsBlockhash(value);
|
|
127
|
+
return addressEncoder.write(value as string as Address, bytes, offset);
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Returns a decoder that you can use to convert an array of 32 bytes representing a blockhash to
|
|
134
|
+
* the base58-encoded representation of that blockhash.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```ts
|
|
138
|
+
* import { getBlockhashDecoder } from '@solana/rpc-types';
|
|
139
|
+
*
|
|
140
|
+
* const blockhashBytes = new Uint8Array([
|
|
141
|
+
* 136, 123, 44, 249, 43, 19, 60, 14,
|
|
142
|
+
* 144, 16, 168, 241, 121, 111, 70, 232,
|
|
143
|
+
* 186, 26, 140, 202, 213, 64, 231, 82,
|
|
144
|
+
* 179, 66, 103, 237, 52, 117, 217, 93
|
|
145
|
+
* ]);
|
|
146
|
+
* const blockhashDecoder = getBlockhashDecoder();
|
|
147
|
+
* const blockhash = blockhashDecoder.decode(blockhashBytes); // ABmPH5KDXX99u6woqFS5vfBGSNyKG42SzpvBMWWqAy48
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export function getBlockhashDecoder(): FixedSizeDecoder<Blockhash, 32> {
|
|
151
|
+
return getAddressDecoder() as FixedSizeDecoder<string, 32> as FixedSizeDecoder<Blockhash, 32>;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Returns a codec that you can use to encode from or decode to a base-58 encoded blockhash.
|
|
156
|
+
*
|
|
157
|
+
* @see {@link getBlockhashDecoder}
|
|
158
|
+
* @see {@link getBlockhashEncoder}
|
|
159
|
+
*/
|
|
160
|
+
export function getBlockhashCodec(): FixedSizeCodec<Blockhash, Blockhash, 32> {
|
|
161
|
+
return combineCodec(getBlockhashEncoder(), getBlockhashDecoder());
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function getBlockhashComparator(): (x: string, y: string) => number {
|
|
165
|
+
return new Intl.Collator('en', {
|
|
166
|
+
caseFirst: 'lower',
|
|
167
|
+
ignorePunctuation: false,
|
|
168
|
+
localeMatcher: 'best fit',
|
|
169
|
+
numeric: false,
|
|
170
|
+
sensitivity: 'variant',
|
|
171
|
+
usage: 'sort',
|
|
172
|
+
}).compare;
|
|
173
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type MainnetUrl = string & { '~cluster': 'mainnet' };
|
|
2
|
+
export type DevnetUrl = string & { '~cluster': 'devnet' };
|
|
3
|
+
export type TestnetUrl = string & { '~cluster': 'testnet' };
|
|
4
|
+
export type ClusterUrl = DevnetUrl | MainnetUrl | TestnetUrl | string;
|
|
5
|
+
|
|
6
|
+
/** Given a URL casts it to a type that is only accepted where mainnet URLs are expected. */
|
|
7
|
+
export function mainnet(putativeString: string): MainnetUrl {
|
|
8
|
+
return putativeString as MainnetUrl;
|
|
9
|
+
}
|
|
10
|
+
/** Given a URL casts it to a type that is only accepted where devnet URLs are expected. */
|
|
11
|
+
export function devnet(putativeString: string): DevnetUrl {
|
|
12
|
+
return putativeString as DevnetUrl;
|
|
13
|
+
}
|
|
14
|
+
/** Given a URL casts it to a type that is only accepted where testnet URLs are expected. */
|
|
15
|
+
export function testnet(putativeString: string): TestnetUrl {
|
|
16
|
+
return putativeString as TestnetUrl;
|
|
17
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { SOLANA_ERROR__INVARIANT_VIOLATION__SWITCH_MUST_BE_EXHAUSTIVE, SolanaError } from '@solana/errors';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A union of all possible commitment statuses -- each a measure of the network confirmation and
|
|
5
|
+
* stake levels on a particular block.
|
|
6
|
+
*
|
|
7
|
+
* Read more about the statuses themselves, [here](https://docs.solana.com/cluster/commitments).
|
|
8
|
+
*/
|
|
9
|
+
export type Commitment = 'confirmed' | 'finalized' | 'processed';
|
|
10
|
+
|
|
11
|
+
function getCommitmentScore(commitment: Commitment): number {
|
|
12
|
+
switch (commitment) {
|
|
13
|
+
case 'finalized':
|
|
14
|
+
return 2;
|
|
15
|
+
case 'confirmed':
|
|
16
|
+
return 1;
|
|
17
|
+
case 'processed':
|
|
18
|
+
return 0;
|
|
19
|
+
default:
|
|
20
|
+
throw new SolanaError(SOLANA_ERROR__INVARIANT_VIOLATION__SWITCH_MUST_BE_EXHAUSTIVE, {
|
|
21
|
+
unexpectedValue: commitment satisfies never,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function commitmentComparator(a: Commitment, b: Commitment): -1 | 0 | 1 {
|
|
27
|
+
if (a === b) {
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
return getCommitmentScore(a) < getCommitmentScore(b) ? -1 : 1;
|
|
31
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Brand, CompressedData, EncodedString } from '@solana/nominal-types';
|
|
2
|
+
|
|
3
|
+
export type Base58EncodedBytes = Brand<EncodedString<string, 'base58'>, 'Base58EncodedBytes'>;
|
|
4
|
+
export type Base64EncodedBytes = Brand<EncodedString<string, 'base64'>, 'Base64EncodedBytes'>;
|
|
5
|
+
export type Base64EncodedZStdCompressedBytes = Brand<
|
|
6
|
+
EncodedString<CompressedData<string, 'zstd'>, 'base64'>,
|
|
7
|
+
'Base64EncodedZStdCompressedBytes'
|
|
8
|
+
>;
|
|
9
|
+
|
|
10
|
+
export type Base58EncodedDataResponse = [Base58EncodedBytes, 'base58'];
|
|
11
|
+
export type Base64EncodedDataResponse = [Base64EncodedBytes, 'base64'];
|
|
12
|
+
export type Base64EncodedZStdCompressedDataResponse = [Base64EncodedZStdCompressedBytes, 'base64+zstd'];
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This package defines types for values used in the
|
|
3
|
+
* [Solana JSON-RPC](https://docs.solana.com/api/http) and a series of helpers for working with
|
|
4
|
+
* them. It can be used standalone, but it is also exported as part of Kit
|
|
5
|
+
* [`@solana/kit`](https://github.com/anza-xyz/kit/tree/main/packages/kit).
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
export * from './account-filters';
|
|
10
|
+
export * from './account-info';
|
|
11
|
+
export * from './blockhash';
|
|
12
|
+
export * from './cluster-url';
|
|
13
|
+
export * from './commitment';
|
|
14
|
+
export * from './encoded-bytes';
|
|
15
|
+
export * from './lamports';
|
|
16
|
+
export * from './rpc-api';
|
|
17
|
+
export * from './stringified-bigint';
|
|
18
|
+
export * from './stringified-number';
|
|
19
|
+
export * from './token-amount';
|
|
20
|
+
export * from './token-balance';
|
|
21
|
+
export * from './transaction';
|
|
22
|
+
export * from './transaction-error';
|
|
23
|
+
export * from './typed-numbers';
|
|
24
|
+
export * from './unix-timestamp';
|
package/src/lamports.ts
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Codec,
|
|
3
|
+
combineCodec,
|
|
4
|
+
Decoder,
|
|
5
|
+
Encoder,
|
|
6
|
+
FixedSizeCodec,
|
|
7
|
+
FixedSizeDecoder,
|
|
8
|
+
FixedSizeEncoder,
|
|
9
|
+
transformDecoder,
|
|
10
|
+
} from '@solana/codecs-core';
|
|
11
|
+
import { getU64Decoder, getU64Encoder, NumberCodec, NumberDecoder, NumberEncoder } from '@solana/codecs-numbers';
|
|
12
|
+
import { SOLANA_ERROR__LAMPORTS_OUT_OF_RANGE, SolanaError } from '@solana/errors';
|
|
13
|
+
import { Brand } from '@solana/nominal-types';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Represents an integer value denominated in Lamports (ie. $1 \times 10^{-9}$ ◎).
|
|
17
|
+
*
|
|
18
|
+
* It is represented as a `bigint` in client code and an `u64` in server code.
|
|
19
|
+
*/
|
|
20
|
+
export type Lamports = Brand<bigint, 'Lamports'>;
|
|
21
|
+
|
|
22
|
+
// Largest possible value to be represented by a u64
|
|
23
|
+
const maxU64Value = 18446744073709551615n; // 2n ** 64n - 1n
|
|
24
|
+
|
|
25
|
+
let memoizedU64Encoder: FixedSizeEncoder<bigint | number, 8> | undefined;
|
|
26
|
+
let memoizedU64Decoder: FixedSizeDecoder<bigint, 8> | undefined;
|
|
27
|
+
|
|
28
|
+
function getMemoizedU64Encoder(): FixedSizeEncoder<bigint | number, 8> {
|
|
29
|
+
if (!memoizedU64Encoder) memoizedU64Encoder = getU64Encoder();
|
|
30
|
+
return memoizedU64Encoder;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function getMemoizedU64Decoder(): FixedSizeDecoder<bigint, 8> {
|
|
34
|
+
if (!memoizedU64Decoder) memoizedU64Decoder = getU64Decoder();
|
|
35
|
+
return memoizedU64Decoder;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* This is a type guard that accepts a `bigint` as input. It will both return `true` if the integer
|
|
40
|
+
* conforms to the {@link Lamports} type and will refine the type for use in your program.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* import { isLamports } from '@solana/rpc-types';
|
|
45
|
+
*
|
|
46
|
+
* if (isLamports(lamports)) {
|
|
47
|
+
* // At this point, `lamports` has been refined to a
|
|
48
|
+
* // `Lamports` that can be used anywhere Lamports are expected.
|
|
49
|
+
* await transfer(fromAddress, toAddress, lamports);
|
|
50
|
+
* } else {
|
|
51
|
+
* setError(`${lamports} is not a quantity of Lamports`);
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export function isLamports(putativeLamports: bigint): putativeLamports is Lamports {
|
|
56
|
+
return putativeLamports >= 0 && putativeLamports <= maxU64Value;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Lamport values returned from the RPC API conform to the type {@link Lamports}. You can use a
|
|
61
|
+
* value of that type wherever a quantity of Lamports is expected.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* From time to time you might acquire a number that you expect to be a quantity of Lamports, from
|
|
65
|
+
* an untrusted network API or user input. To assert that such an arbitrary number is usable as a
|
|
66
|
+
* quantity of Lamports, use this function.
|
|
67
|
+
*
|
|
68
|
+
* ```ts
|
|
69
|
+
* import { assertIsLamports } from '@solana/rpc-types';
|
|
70
|
+
*
|
|
71
|
+
* // Imagine a function that creates a transfer instruction when a user submits a form.
|
|
72
|
+
* function handleSubmit() {
|
|
73
|
+
* // We know only that what the user typed conforms to the `number` type.
|
|
74
|
+
* const lamports: number = parseInt(quantityInput.value, 10);
|
|
75
|
+
* try {
|
|
76
|
+
* // If this type assertion function doesn't throw, then
|
|
77
|
+
* // Typescript will upcast `lamports` to `Lamports`.
|
|
78
|
+
* assertIsLamports(lamports);
|
|
79
|
+
* // At this point, `lamports` is a `Lamports` that can be used anywhere Lamports are expected.
|
|
80
|
+
* await transfer(fromAddress, toAddress, lamports);
|
|
81
|
+
* } catch (e) {
|
|
82
|
+
* // `lamports` turned out not to validate as a quantity of Lamports.
|
|
83
|
+
* }
|
|
84
|
+
* }
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export function assertIsLamports(putativeLamports: bigint): asserts putativeLamports is Lamports {
|
|
88
|
+
if (putativeLamports < 0 || putativeLamports > maxU64Value) {
|
|
89
|
+
throw new SolanaError(SOLANA_ERROR__LAMPORTS_OUT_OF_RANGE);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* This helper combines _asserting_ that a number is a possible number of {@link Lamports} with
|
|
95
|
+
* _coercing_ it to the {@link Lamports} type. It's best used with untrusted input.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```ts
|
|
99
|
+
* import { lamports } from '@solana/rpc-types';
|
|
100
|
+
*
|
|
101
|
+
* await transfer(address(fromAddress), address(toAddress), lamports(100000n));
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export function lamports(putativeLamports: bigint): Lamports {
|
|
105
|
+
assertIsLamports(putativeLamports);
|
|
106
|
+
return putativeLamports;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
type ExtractAdditionalProps<T, U> = Omit<T, keyof U>;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Returns an encoder that you can use to encode a 64-bit {@link Lamports} value to 8 bytes in
|
|
113
|
+
* little endian order.
|
|
114
|
+
*/
|
|
115
|
+
export function getDefaultLamportsEncoder(): FixedSizeEncoder<Lamports, 8> {
|
|
116
|
+
return getLamportsEncoder(getMemoizedU64Encoder());
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Returns an encoder that you can use to encode a {@link Lamports} value to a byte array.
|
|
121
|
+
*
|
|
122
|
+
* You must supply a number decoder that will determine how encode the numeric value.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```ts
|
|
126
|
+
* import { getLamportsEncoder } from '@solana/rpc-types';
|
|
127
|
+
* import { getU16Encoder } from '@solana/codecs-numbers';
|
|
128
|
+
*
|
|
129
|
+
* const lamports = lamports(256n);
|
|
130
|
+
* const lamportsEncoder = getLamportsEncoder(getU16Encoder());
|
|
131
|
+
* const lamportsBytes = lamportsEncoder.encode(lamports);
|
|
132
|
+
* // Uint8Array(2) [ 0, 1 ]
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
export function getLamportsEncoder<TEncoder extends NumberEncoder>(
|
|
136
|
+
innerEncoder: TEncoder,
|
|
137
|
+
): Encoder<Lamports> & ExtractAdditionalProps<TEncoder, NumberEncoder> {
|
|
138
|
+
return innerEncoder;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Returns a decoder that you can use to decode a byte array representing a 64-bit little endian
|
|
143
|
+
* number to a {@link Lamports} value.
|
|
144
|
+
*/
|
|
145
|
+
export function getDefaultLamportsDecoder(): FixedSizeDecoder<Lamports, 8> {
|
|
146
|
+
return getLamportsDecoder(getMemoizedU64Decoder());
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Returns a decoder that you can use to convert an array of bytes representing a number to a
|
|
151
|
+
* {@link Lamports} value.
|
|
152
|
+
*
|
|
153
|
+
* You must supply a number decoder that will determine how many bits to use to decode the numeric
|
|
154
|
+
* value.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```ts
|
|
158
|
+
* import { getLamportsDecoder } from '@solana/rpc-types';
|
|
159
|
+
* import { getU16Decoder } from '@solana/codecs-numbers';
|
|
160
|
+
*
|
|
161
|
+
* const lamportsBytes = new Uint8Array([ 0, 1 ]);
|
|
162
|
+
* const lamportsDecoder = getLamportsDecoder(getU16Decoder());
|
|
163
|
+
* const lamports = lamportsDecoder.decode(lamportsBytes); // lamports(256n)
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
export function getLamportsDecoder<TDecoder extends NumberDecoder>(
|
|
167
|
+
innerDecoder: TDecoder,
|
|
168
|
+
): Decoder<Lamports> & ExtractAdditionalProps<TDecoder, NumberDecoder> {
|
|
169
|
+
return transformDecoder<bigint | number, Lamports>(innerDecoder, value =>
|
|
170
|
+
lamports(typeof value === 'bigint' ? value : BigInt(value)),
|
|
171
|
+
) as Decoder<Lamports> & ExtractAdditionalProps<TDecoder, NumberDecoder>;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Returns a codec that you can use to encode from or decode to a 64-bit {@link Lamports} value.
|
|
176
|
+
*
|
|
177
|
+
* @see {@link getDefaultLamportsDecoder}
|
|
178
|
+
* @see {@link getDefaultLamportsEncoder}
|
|
179
|
+
*/
|
|
180
|
+
export function getDefaultLamportsCodec(): FixedSizeCodec<Lamports, Lamports, 8> {
|
|
181
|
+
return combineCodec(getDefaultLamportsEncoder(), getDefaultLamportsDecoder());
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Returns a codec that you can use to encode from or decode to {@link Lamports} value.
|
|
186
|
+
*
|
|
187
|
+
* @see {@link getLamportsDecoder}
|
|
188
|
+
* @see {@link getLamportsEncoder}
|
|
189
|
+
*/
|
|
190
|
+
export function getLamportsCodec<TCodec extends NumberCodec>(
|
|
191
|
+
innerCodec: TCodec,
|
|
192
|
+
): Codec<Lamports, Lamports> & ExtractAdditionalProps<TCodec, NumberCodec> {
|
|
193
|
+
return combineCodec(getLamportsEncoder(innerCodec), getLamportsDecoder(innerCodec)) as Codec<Lamports, Lamports> &
|
|
194
|
+
ExtractAdditionalProps<TCodec, NumberCodec>;
|
|
195
|
+
}
|
package/src/rpc-api.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { SOLANA_ERROR__MALFORMED_BIGINT_STRING, SolanaError } from '@solana/errors';
|
|
2
|
+
import { Brand } from '@solana/nominal-types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* This type represents a `bigint` which has been encoded as a string for transit over a transport
|
|
6
|
+
* that does not support `bigint` values natively. The JSON-RPC is such a transport.
|
|
7
|
+
*/
|
|
8
|
+
export type StringifiedBigInt = Brand<string, 'StringifiedBigInt'>;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A type guard that returns `true` if the input string parses as a `BigInt`, and refines its type
|
|
12
|
+
* for use in your program.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { isStringifiedBigInt } from '@solana/rpc-types';
|
|
17
|
+
*
|
|
18
|
+
* if (isStringifiedBigInt(bigintString)) {
|
|
19
|
+
* // At this point, `bigintString` has been refined to a `StringifiedBigInt`
|
|
20
|
+
* bigintString satisfies StringifiedBigInt; // OK
|
|
21
|
+
* } else {
|
|
22
|
+
* setError(`${bigintString} does not represent a BigInt`);
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export function isStringifiedBigInt(putativeBigInt: string): putativeBigInt is StringifiedBigInt {
|
|
27
|
+
try {
|
|
28
|
+
BigInt(putativeBigInt);
|
|
29
|
+
return true;
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* From time to time you might acquire a string, that you expect to parse as a `BigInt`, from an
|
|
37
|
+
* untrusted network API or user input. Use this function to assert that such an arbitrary string
|
|
38
|
+
* will in fact parse as a `BigInt`.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* import { assertIsStringifiedBigInt } from '@solana/rpc-types';
|
|
43
|
+
*
|
|
44
|
+
* // Imagine having received a value that you presume represents the supply of some token.
|
|
45
|
+
* // At this point we know only that it conforms to the `string` type.
|
|
46
|
+
* try {
|
|
47
|
+
* // If this type assertion function doesn't throw, then
|
|
48
|
+
* // Typescript will upcast `supplyString` to `StringifiedBigInt`.
|
|
49
|
+
* assertIsStringifiedBigInt(supplyString);
|
|
50
|
+
* // At this point, `supplyString` is a `StringifiedBigInt`.
|
|
51
|
+
* supplyString satisfies StringifiedBigInt;
|
|
52
|
+
* } catch (e) {
|
|
53
|
+
* // `supplyString` turned out not to parse as a `BigInt`
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export function assertIsStringifiedBigInt(putativeBigInt: string): asserts putativeBigInt is StringifiedBigInt {
|
|
58
|
+
try {
|
|
59
|
+
BigInt(putativeBigInt);
|
|
60
|
+
} catch {
|
|
61
|
+
throw new SolanaError(SOLANA_ERROR__MALFORMED_BIGINT_STRING, {
|
|
62
|
+
value: putativeBigInt,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* This helper combines _asserting_ that a string will parse as a `BigInt` with _coercing_ it to the
|
|
69
|
+
* {@link StringifiedBigInt} type. It's best used with untrusted input.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* import { stringifiedBigInt } from '@solana/rpc-types';
|
|
74
|
+
*
|
|
75
|
+
* const supplyString = stringifiedBigInt('1000000000');
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export function stringifiedBigInt(putativeBigInt: string): StringifiedBigInt {
|
|
79
|
+
assertIsStringifiedBigInt(putativeBigInt);
|
|
80
|
+
return putativeBigInt;
|
|
81
|
+
}
|