@subsquid/solana-stream 1.0.0-portal-api.d5861e → 1.0.0
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/lib/builder.d.ts +92 -0
- package/lib/builder.d.ts.map +1 -0
- package/lib/builder.js +182 -0
- package/lib/builder.js.map +1 -0
- package/lib/data/fields.d.ts.map +1 -1
- package/lib/data/fields.js +0 -14
- package/lib/data/fields.js.map +1 -1
- package/lib/data/model.d.ts +16 -6
- package/lib/data/model.d.ts.map +1 -1
- package/lib/data/model.js +5 -0
- package/lib/data/model.js.map +1 -1
- package/lib/data/request.d.ts +2 -3
- package/lib/data/request.d.ts.map +1 -1
- package/lib/data/type-util.d.ts +35 -13
- package/lib/data/type-util.d.ts.map +1 -1
- package/lib/index.d.ts +3 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -0
- package/lib/index.js.map +1 -1
- package/lib/{merge.test.d.ts.map → portal/merge.test.d.ts.map} +1 -1
- package/lib/{merge.test.js → portal/merge.test.js} +34 -34
- package/lib/portal/merge.test.js.map +1 -0
- package/lib/portal/source.d.ts +14 -11
- package/lib/portal/source.d.ts.map +1 -1
- package/lib/portal/source.js +75 -56
- package/lib/portal/source.js.map +1 -1
- package/lib/query.d.ts +53 -0
- package/lib/query.d.ts.map +1 -0
- package/lib/query.js +102 -0
- package/lib/query.js.map +1 -0
- package/lib/source.d.ts +2 -69
- package/lib/source.d.ts.map +1 -1
- package/lib/source.js +0 -265
- package/lib/source.js.map +1 -1
- package/package.json +18 -16
- package/src/builder.ts +205 -0
- package/src/data/fields.ts +0 -20
- package/src/data/model.ts +23 -27
- package/src/data/request.ts +2 -3
- package/src/data/type-util.ts +39 -37
- package/src/index.ts +3 -0
- package/src/{merge.test.ts → portal/merge.test.ts} +2 -2
- package/src/portal/source.ts +115 -98
- package/src/query.ts +112 -0
- package/src/source.ts +3 -314
- package/lib/merge.test.js.map +0 -1
- package/lib/portal/schema.d.ts +0 -167
- package/lib/portal/schema.d.ts.map +0 -1
- package/lib/portal/schema.js +0 -117
- package/lib/portal/schema.js.map +0 -1
- package/src/portal/schema.ts +0 -136
- /package/lib/{merge.test.d.ts → portal/merge.test.d.ts} +0 -0
package/src/builder.ts
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import {PortalClient, PortalClientOptions} from '@subsquid/portal-client'
|
|
2
|
+
import {getOrGenerateSquidId} from '@subsquid/util-internal-processor-tools'
|
|
3
|
+
import {applyRangeBound, mergeRangeRequests, Range, RangeRequest, RangeRequestList} from '@subsquid/util-internal-range'
|
|
4
|
+
import assert from 'assert'
|
|
5
|
+
import {DEFAULT_FIELDS, FieldSelection} from './data/model'
|
|
6
|
+
import {
|
|
7
|
+
BalanceRequest,
|
|
8
|
+
DataRequest,
|
|
9
|
+
InstructionRequest,
|
|
10
|
+
LogRequest,
|
|
11
|
+
RewardRequest,
|
|
12
|
+
TokenBalanceRequest,
|
|
13
|
+
TransactionRequest,
|
|
14
|
+
} from './data/request'
|
|
15
|
+
import {PortalSolanaDataSource} from './portal/source'
|
|
16
|
+
import {type Query, QueryBuilder} from './query'
|
|
17
|
+
import type {SolanaDataSource} from './source'
|
|
18
|
+
|
|
19
|
+
interface BlockRange {
|
|
20
|
+
range?: Range
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class DataSourceBuilder<F extends FieldSelection = typeof DEFAULT_FIELDS> {
|
|
24
|
+
private requests: RangeRequest<DataRequest>[] = []
|
|
25
|
+
private fields?: FieldSelection
|
|
26
|
+
private blockRange?: Range
|
|
27
|
+
private portal?: PortalClient | PortalClientOptions
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Set SQD Network Portal endpoint.
|
|
31
|
+
*
|
|
32
|
+
* SQD Network allows to get data from blocks up to
|
|
33
|
+
* infinite times faster and more efficient than via regular RPC.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* source.setPortal('https://portal.sqd.dev/datasets/solana-mainnet')
|
|
37
|
+
*/
|
|
38
|
+
setPortal(portal: string | PortalClientOptions | PortalClient): this {
|
|
39
|
+
if (typeof portal == 'string') {
|
|
40
|
+
this.portal = {url: portal}
|
|
41
|
+
} else {
|
|
42
|
+
this.portal = portal
|
|
43
|
+
}
|
|
44
|
+
return this
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Limits the range of blocks to fetch.
|
|
49
|
+
*
|
|
50
|
+
* Note, that block heights should be used instead of slots.
|
|
51
|
+
*/
|
|
52
|
+
setBlockRange(range?: Range): this {
|
|
53
|
+
this.blockRange = range
|
|
54
|
+
return this
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Configure a set of fetched fields
|
|
59
|
+
*/
|
|
60
|
+
setFields<F extends FieldSelection>(fields: F): DataSourceBuilder<F> {
|
|
61
|
+
this.fields = fields
|
|
62
|
+
return this as any
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Add a portal query - a set of item filters that share a block range.
|
|
67
|
+
*
|
|
68
|
+
* Accepts either a prebuilt {@link Query} object or a
|
|
69
|
+
* {@link QueryBuilder}, in which case {@link QueryBuilder#build} is
|
|
70
|
+
* invoked automatically.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* source.addQuery(new QueryBuilder()
|
|
75
|
+
* .setRange({from: 250_000_000})
|
|
76
|
+
* .addInstruction({where: {programId: [PROGRAM_ID]}, include: {logs: true}})
|
|
77
|
+
* .addTransaction({where: {feePayer: [FEE_PAYER]}})
|
|
78
|
+
* )
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
addQuery(query: Query | QueryBuilder): this {
|
|
82
|
+
this.requests.push(query instanceof QueryBuilder ? query.build() : query)
|
|
83
|
+
return this
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Shorthand for {@link DataSourceBuilder#addQuery} with a query that
|
|
88
|
+
* only sets {@link QueryBuilder#includeAllBlocks} (and optionally a
|
|
89
|
+
* range).
|
|
90
|
+
*/
|
|
91
|
+
includeAllBlocks(range?: Range): this {
|
|
92
|
+
return this.addQuery({
|
|
93
|
+
range: range ?? {from: 0},
|
|
94
|
+
request: {includeAllBlocks: true},
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Shorthand for {@link DataSourceBuilder#addQuery} with a query that
|
|
100
|
+
* contains a single transaction filter.
|
|
101
|
+
*/
|
|
102
|
+
addTransaction(options: TransactionRequest & BlockRange): this {
|
|
103
|
+
let {range, ...req} = options
|
|
104
|
+
return this.addQuery({
|
|
105
|
+
range: range ?? {from: 0},
|
|
106
|
+
request: {transactions: [req]},
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Shorthand for {@link DataSourceBuilder#addQuery} with a query that
|
|
112
|
+
* contains a single instruction filter.
|
|
113
|
+
*/
|
|
114
|
+
addInstruction(options: InstructionRequest & BlockRange): this {
|
|
115
|
+
let {range, ...req} = options
|
|
116
|
+
return this.addQuery({
|
|
117
|
+
range: range ?? {from: 0},
|
|
118
|
+
request: {instructions: [req]},
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Shorthand for {@link DataSourceBuilder#addQuery} with a query that
|
|
124
|
+
* contains a single log filter.
|
|
125
|
+
*/
|
|
126
|
+
addLog(options: LogRequest & BlockRange): this {
|
|
127
|
+
let {range, ...req} = options
|
|
128
|
+
return this.addQuery({
|
|
129
|
+
range: range ?? {from: 0},
|
|
130
|
+
request: {logs: [req]},
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Shorthand for {@link DataSourceBuilder#addQuery} with a query that
|
|
136
|
+
* contains a single balance filter.
|
|
137
|
+
*/
|
|
138
|
+
addBalance(options: BalanceRequest & BlockRange): this {
|
|
139
|
+
let {range, ...req} = options
|
|
140
|
+
return this.addQuery({
|
|
141
|
+
range: range ?? {from: 0},
|
|
142
|
+
request: {balances: [req]},
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Shorthand for {@link DataSourceBuilder#addQuery} with a query that
|
|
148
|
+
* contains a single token balance filter.
|
|
149
|
+
*/
|
|
150
|
+
addTokenBalance(options: TokenBalanceRequest & BlockRange): this {
|
|
151
|
+
let {range, ...req} = options
|
|
152
|
+
return this.addQuery({
|
|
153
|
+
range: range ?? {from: 0},
|
|
154
|
+
request: {tokenBalances: [req]},
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Shorthand for {@link DataSourceBuilder#addQuery} with a query that
|
|
160
|
+
* contains a single reward filter.
|
|
161
|
+
*/
|
|
162
|
+
addReward(options: RewardRequest & BlockRange): this {
|
|
163
|
+
let {range, ...req} = options
|
|
164
|
+
return this.addQuery({
|
|
165
|
+
range: range ?? {from: 0},
|
|
166
|
+
request: {rewards: [req]},
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private getRequests(): RangeRequestList<DataRequest> {
|
|
171
|
+
function concat<T>(a?: T[], b?: T[]): T[] | undefined {
|
|
172
|
+
let result: T[] = []
|
|
173
|
+
if (a) {
|
|
174
|
+
result.push(...a)
|
|
175
|
+
}
|
|
176
|
+
if (b) {
|
|
177
|
+
result.push(...b)
|
|
178
|
+
}
|
|
179
|
+
return result.length == 0 ? undefined : result
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
let requests = mergeRangeRequests(this.requests, (a, b) => {
|
|
183
|
+
return {
|
|
184
|
+
includeAllBlocks: a.includeAllBlocks || b.includeAllBlocks,
|
|
185
|
+
transactions: concat(a.transactions, b.transactions),
|
|
186
|
+
instructions: concat(a.instructions, b.instructions),
|
|
187
|
+
logs: concat(a.logs, b.logs),
|
|
188
|
+
balances: concat(a.balances, b.balances),
|
|
189
|
+
tokenBalances: concat(a.tokenBalances, b.tokenBalances),
|
|
190
|
+
rewards: concat(a.rewards, b.rewards),
|
|
191
|
+
}
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
return applyRangeBound(requests, this.blockRange)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
build(): SolanaDataSource<F> {
|
|
198
|
+
assert(this.portal, 'Portal settings not set')
|
|
199
|
+
|
|
200
|
+
let portal = this.portal instanceof PortalClient ? this.portal : new PortalClient(this.portal)
|
|
201
|
+
return new PortalSolanaDataSource<F>(portal, this.fields ?? DEFAULT_FIELDS, () => this.getRequests(), {
|
|
202
|
+
squidId: getOrGenerateSquidId(),
|
|
203
|
+
})
|
|
204
|
+
}
|
|
205
|
+
}
|
package/src/data/fields.ts
CHANGED
|
@@ -1,26 +1,6 @@
|
|
|
1
|
-
import {DEFAULT_FIELDS, FieldSelection} from './model'
|
|
2
1
|
import {Selector} from './type-util'
|
|
3
2
|
|
|
4
3
|
|
|
5
|
-
function merge<Keys extends string>(def: Selector<Keys>, requested: Selector<Keys> = {}): Selector<Keys> {
|
|
6
|
-
let fields: Selector<Keys> = {}
|
|
7
|
-
|
|
8
|
-
for (let key in def) {
|
|
9
|
-
if (requested[key] !== false) {
|
|
10
|
-
fields[key] = def[key]
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
for (let key in requested) {
|
|
15
|
-
if (requested[key]) {
|
|
16
|
-
fields[key] = true
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return fields
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
4
|
export function project<T>(fields: Selector<keyof T> | undefined, obj: T): Partial<T> {
|
|
25
5
|
if (fields == null) return {}
|
|
26
6
|
let result: Partial<T> = {}
|
package/src/data/model.ts
CHANGED
|
@@ -10,16 +10,16 @@ import type {
|
|
|
10
10
|
} from './partial'
|
|
11
11
|
import type {GetFields, Select, Selector, Simplify} from './type-util'
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* Hex encoded binary string
|
|
15
|
-
*/
|
|
13
|
+
/** Hex encoded binary string. */
|
|
16
14
|
export type Bytes = string
|
|
17
15
|
|
|
18
|
-
/**
|
|
19
|
-
* Base58 encoded binary string
|
|
20
|
-
*/
|
|
16
|
+
/** Base58 encoded binary string. */
|
|
21
17
|
export type Base58Bytes = string
|
|
22
18
|
|
|
19
|
+
/**
|
|
20
|
+
* User-facing field selection. Each section is an optional `Selector`
|
|
21
|
+
* over that section's non-required fields.
|
|
22
|
+
*/
|
|
23
23
|
export interface FieldSelection {
|
|
24
24
|
block?: Selector<Exclude<keyof data.BlockHeader, BlockRequiredFields>>
|
|
25
25
|
transaction?: Selector<Exclude<keyof data.Transaction, TransactionRequiredFields>>
|
|
@@ -30,6 +30,13 @@ export interface FieldSelection {
|
|
|
30
30
|
reward?: Selector<Exclude<keyof data.Reward, RewardRequiredFields>>
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
export type FieldKey = keyof FieldSelection
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Default field set applied when {@link DataSourceBuilder#setFields} is not
|
|
37
|
+
* called. `as const` preserves the literal `true` values so they feed through
|
|
38
|
+
* {@link GetFields} correctly.
|
|
39
|
+
*/
|
|
33
40
|
export const DEFAULT_FIELDS = {
|
|
34
41
|
block: {
|
|
35
42
|
timestamp: true,
|
|
@@ -67,38 +74,27 @@ export const DEFAULT_FIELDS = {
|
|
|
67
74
|
lamports: true,
|
|
68
75
|
rewardType: true,
|
|
69
76
|
},
|
|
70
|
-
} as const
|
|
77
|
+
} as const satisfies FieldSelection
|
|
71
78
|
|
|
72
|
-
|
|
73
|
-
|
|
79
|
+
/**
|
|
80
|
+
* A concrete item type derived from a raw data shape, the always-present
|
|
81
|
+
* required keys, and a per-section field selection.
|
|
82
|
+
*/
|
|
83
|
+
type Item<Data, Required extends keyof Data, F extends FieldSelection, K extends FieldKey> = Simplify<
|
|
84
|
+
Pick<Data, Required> & Select<Data, GetFields<F, K>>
|
|
74
85
|
>
|
|
75
86
|
|
|
76
87
|
export type BlockHeader<F extends FieldSelection = {}> = Item<data.BlockHeader, BlockRequiredFields, F, 'block'>
|
|
77
88
|
|
|
78
|
-
export type Transaction<F extends FieldSelection = {}> = Item<
|
|
79
|
-
data.Transaction,
|
|
80
|
-
TransactionRequiredFields,
|
|
81
|
-
F,
|
|
82
|
-
'transaction'
|
|
83
|
-
>
|
|
89
|
+
export type Transaction<F extends FieldSelection = {}> = Item<data.Transaction, TransactionRequiredFields, F, 'transaction'>
|
|
84
90
|
|
|
85
|
-
export type Instruction<F extends FieldSelection = {}> = Item<
|
|
86
|
-
data.Instruction,
|
|
87
|
-
InstructionRequiredFields,
|
|
88
|
-
F,
|
|
89
|
-
'instruction'
|
|
90
|
-
>
|
|
91
|
+
export type Instruction<F extends FieldSelection = {}> = Item<data.Instruction, InstructionRequiredFields, F, 'instruction'>
|
|
91
92
|
|
|
92
93
|
export type LogMessage<F extends FieldSelection = {}> = Item<data.LogMessage, LogRequiredFields, F, 'log'>
|
|
93
94
|
|
|
94
95
|
export type Balance<F extends FieldSelection = {}> = Item<data.Balance, BalanceRequiredFields, F, 'balance'>
|
|
95
96
|
|
|
96
|
-
export type TokenBalance<F extends FieldSelection = {}> = Item<
|
|
97
|
-
data.TokenBalance,
|
|
98
|
-
TokenBalanceRequiredFields,
|
|
99
|
-
F,
|
|
100
|
-
'tokenBalance'
|
|
101
|
-
>
|
|
97
|
+
export type TokenBalance<F extends FieldSelection = {}> = Item<data.TokenBalance, TokenBalanceRequiredFields, F, 'tokenBalance'>
|
|
102
98
|
|
|
103
99
|
export type Reward<F extends FieldSelection = {}> = Item<data.Reward, RewardRequiredFields, F, 'reward'>
|
|
104
100
|
|
package/src/data/request.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import {LogMessage} from '@subsquid/solana-normalization'
|
|
2
|
-
import {Base58Bytes
|
|
2
|
+
import {Base58Bytes} from './model'
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
export interface DataRequest
|
|
6
|
-
fields?: F
|
|
5
|
+
export interface DataRequest {
|
|
7
6
|
includeAllBlocks?: boolean
|
|
8
7
|
transactions?: TransactionRequest[]
|
|
9
8
|
instructions?: InstructionRequest[]
|
package/src/data/type-util.ts
CHANGED
|
@@ -1,42 +1,44 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export type
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Flattens an intersection type into a single object shape for nicer IDE hover
|
|
3
|
+
* output. The trailing `& {}` is a no-op at the type level but forces the
|
|
4
|
+
* compiler to normalize the representation.
|
|
5
|
+
*/
|
|
6
|
+
export type Simplify<T> = {[K in keyof T]: T[K]} & {}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Extracts the string keys of `F` whose values include the literal `true`.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* type Keys = TrueFields<{a: true; b: false; c?: boolean}>
|
|
13
|
+
* // "a" | "c" -- `c`'s type includes `true` through `boolean`
|
|
14
|
+
*/
|
|
15
|
+
export type TrueFields<F> = {
|
|
16
|
+
[K in keyof F]-?: true extends F[K] ? K : never
|
|
17
|
+
}[keyof F]
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Picks the set of truthy field keys for a given section `K` of a
|
|
21
|
+
* `FieldSelection`-shaped type `F`.
|
|
22
|
+
*/
|
|
23
|
+
export type GetFields<F, K extends keyof F> = TrueFields<F[K]>
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Picks `Fields` from `T` while distributing over unions, so members of a
|
|
27
|
+
* discriminated union are narrowed independently.
|
|
28
|
+
*/
|
|
29
29
|
export type Select<T, Fields> = T extends any ? Simplify<Pick<T, Extract<keyof T, Fields>>> : never
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Optional boolean flag map over a finite set of string keys.
|
|
33
|
+
*
|
|
34
|
+
* Shape: `{[K in Fields]?: boolean}` - equivalent to `Partial<Record<Fields, boolean>>`.
|
|
35
|
+
*/
|
|
36
|
+
export type Selector<Fields extends PropertyKey> = {
|
|
33
37
|
[P in Fields]?: boolean
|
|
34
38
|
}
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
>
|
|
40
|
+
/**
|
|
41
|
+
* Produces a type with `Required` keys kept as-is and all other keys made
|
|
42
|
+
* optional.
|
|
43
|
+
*/
|
|
44
|
+
export type MakePartial<T, Required extends keyof T> = Simplify<Pick<T, Required> & Partial<Omit<T, Required>>>
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {describe, it} from '
|
|
1
|
+
import { describe, it } from 'vitest'
|
|
2
2
|
import assert from 'assert'
|
|
3
3
|
import {
|
|
4
4
|
mergeItems,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
BALANCE_FILTER_KEYS,
|
|
9
9
|
TOKEN_BALANCE_FILTER_KEYS,
|
|
10
10
|
REWARD_FILTER_KEYS,
|
|
11
|
-
} from './
|
|
11
|
+
} from './merge'
|
|
12
12
|
|
|
13
13
|
describe('mergeItems', function () {
|
|
14
14
|
describe('instructions', function () {
|