@pushchain/core 6.0.25 → 6.0.26
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/CHANGELOG.md +66 -0
- package/package.json +1 -1
- package/src/lib/constants/chain.js +1 -1
- package/src/lib/constants/index.d.ts +5 -3
- package/src/lib/constants/index.js +1 -0
- package/src/lib/constants/read-state.d.ts +6 -2
- package/src/lib/constants/read-state.js +7 -3
- package/src/lib/index.d.ts +1 -1
- package/src/lib/index.js +3 -2
- package/src/lib/progress-hook/progress-hook.js +21 -0
- package/src/lib/progress-hook/progress-hook.types.d.ts +4 -1
- package/src/lib/progress-hook/progress-hook.types.js +3 -0
- package/src/lib/push-chain/push-chain.d.ts +2 -1
- package/src/lib/push-chain/push-chain.js +4 -3
- package/src/lib/push-client/push-client.d.ts +7 -0
- package/src/lib/push-client/push-client.js +41 -15
- package/src/lib/read-state/envelopes/evm.js +2 -3
- package/src/lib/read-state/envelopes/svm.js +11 -1
- package/src/lib/read-state/read-executor.js +4 -0
- package/src/lib/read-state/read-params.d.ts +68 -23
- package/src/lib/read-state/read-params.js +57 -13
- package/src/lib/read-state/read-state.types.d.ts +41 -0
- package/src/lib/read-state/read-state.types.js +23 -1
- package/src/lib/read-state/read-tracker.d.ts +6 -1
- package/src/lib/read-state/read-tracker.js +81 -15
- package/src/lib/read-state/registry.js +2 -3
- package/src/lib/read-state/result-decoder.js +6 -0
- package/src/lib/read-state/spec-builder.js +1 -1
- package/src/lib/read-state/svm-account.d.ts +20 -0
- package/src/lib/read-state/svm-account.js +114 -0
- package/src/lib/read-state/svm-pda.d.ts +13 -0
- package/src/lib/read-state/svm-pda.js +147 -0
|
@@ -3,13 +3,17 @@
|
|
|
3
3
|
*
|
|
4
4
|
* read(user, { chain }) native balance (ETH / lamports)
|
|
5
5
|
* read(user, { chain, token }) token balance (ERC-20 / SPL via ATA)
|
|
6
|
-
* read(contract, { chain, abi, functionName, args }) typed call — abi encodes AND decodes
|
|
6
|
+
* read(contract, { chain, abi, functionName, args }) typed call — abi encodes AND decodes (any mutability; eth_call simulation)
|
|
7
|
+
* read(account, { chain, idl, functionName? }) Anchor account, layout picked by discriminator
|
|
8
|
+
* read(program, { chain, idl, functionName, args }) Anchor PDA — args are its IDL-declared seeds
|
|
7
9
|
* read(contract, { chain, storageSlot }) storage word
|
|
8
10
|
* read(url, { chain: CHAIN.WEB2, web2 }) web2
|
|
9
11
|
*
|
|
10
12
|
* Query keys are mutually exclusive; the namespace decides which are legal.
|
|
11
13
|
*/
|
|
12
|
-
import type { Abi, Address, Hex, ContractFunctionReturnType, ContractFunctionName, ContractFunctionArgs, ExtractAbiFunctionForArgs } from 'viem';
|
|
14
|
+
import type { Abi, AbiStateMutability, Address, Hex, ContractFunctionReturnType, ContractFunctionName, ContractFunctionArgs, ExtractAbiFunctionForArgs } from 'viem';
|
|
15
|
+
import type { Idl, IdlAccounts } from '@coral-xyz/anchor';
|
|
16
|
+
import { PublicKey } from '@solana/web3.js';
|
|
13
17
|
import { CHAIN, PUSH_NETWORK } from '../constants/enums';
|
|
14
18
|
import type { BuildReadSpecParams, ReadChain, ReadDestination, ReadLifecycleOptions, ReadQuery, ReadResultShape, Web2Extract } from './read-state.types';
|
|
15
19
|
import type { ReadCallback } from './read-state.types';
|
|
@@ -32,11 +36,23 @@ interface ReadOptionsInput {
|
|
|
32
36
|
chain: ReadChain;
|
|
33
37
|
/** Token balance of `subject`: ERC-20 `balanceOf` on EVM, the SPL ATA on SVM. */
|
|
34
38
|
token?: string;
|
|
35
|
-
/**
|
|
36
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Anchor IDL — the Solana counterpart of `abi`. The subject account is decoded
|
|
41
|
+
* with the layout whose discriminator matches its data. Reads never execute an
|
|
42
|
+
* instruction.
|
|
43
|
+
*/
|
|
44
|
+
idl?: Idl;
|
|
37
45
|
/** Typed contract call — encodes the call and decodes the result. */
|
|
38
46
|
abi?: Abi;
|
|
47
|
+
/**
|
|
48
|
+
* With `abi`: the function to call (required). With `idl`: the account layout
|
|
49
|
+
* (optional — inferred from the discriminator; snake_case or camelCase).
|
|
50
|
+
*/
|
|
39
51
|
functionName?: string;
|
|
52
|
+
/**
|
|
53
|
+
* With `abi`: positional call args. With `idl` and the program id as subject:
|
|
54
|
+
* the PDA's non-constant seeds, in IDL order.
|
|
55
|
+
*/
|
|
40
56
|
args?: readonly unknown[];
|
|
41
57
|
/** EVM storage word. */
|
|
42
58
|
storageSlot?: Hex | bigint;
|
|
@@ -60,6 +76,8 @@ interface ReadOptionsInput {
|
|
|
60
76
|
type NoQuery = {
|
|
61
77
|
token?: never;
|
|
62
78
|
tokenProgram?: never;
|
|
79
|
+
idl?: never;
|
|
80
|
+
accountName?: never;
|
|
63
81
|
abi?: never;
|
|
64
82
|
functionName?: never;
|
|
65
83
|
args?: never;
|
|
@@ -79,9 +97,14 @@ export type ReadQueryOptions = ({
|
|
|
79
97
|
token: string;
|
|
80
98
|
}>) | ({
|
|
81
99
|
chain: SvmChain;
|
|
82
|
-
} & Query<'token'
|
|
100
|
+
} & Query<'token', {
|
|
83
101
|
token: string;
|
|
84
|
-
|
|
102
|
+
}>) | ({
|
|
103
|
+
chain: SvmChain;
|
|
104
|
+
} & Query<'idl' | 'functionName' | 'args', {
|
|
105
|
+
idl: Idl;
|
|
106
|
+
functionName?: string;
|
|
107
|
+
args?: readonly unknown[];
|
|
85
108
|
}>) | ({
|
|
86
109
|
chain: EvmChain;
|
|
87
110
|
} & Query<'abi' | 'functionName' | 'args', {
|
|
@@ -100,7 +123,7 @@ export type ReadQueryOptions = ({
|
|
|
100
123
|
export type ReadCallbackOptions = {
|
|
101
124
|
callback?: ({
|
|
102
125
|
target: Address;
|
|
103
|
-
gasLimit
|
|
126
|
+
gasLimit?: bigint;
|
|
104
127
|
} & Pick<ReadCallback, 'abi' | 'functionName' | 'args'>) | {
|
|
105
128
|
target?: never;
|
|
106
129
|
gasLimit?: bigint;
|
|
@@ -128,7 +151,7 @@ export type ReadPrepareOptions = SharedPrepareOptions & ((Extract<ReadQueryOptio
|
|
|
128
151
|
export type ReadExecuteOptions = Pick<ReadOptionsInput, 'advanced' | 'waitForCompletion' | 'progressHook'>;
|
|
129
152
|
type WithExecutionOptions<T> = T extends unknown ? Omit<T, 'advanced' | 'waitForCompletion' | 'progressHook'> & ReadExecuteOptions : never;
|
|
130
153
|
export type ReadOptions = WithExecutionOptions<ReadPrepareOptions> & {
|
|
131
|
-
callback?: (Required<Pick<ReadCallback, 'target' | '
|
|
154
|
+
callback?: (Required<Pick<ReadCallback, 'target' | 'abi' | 'functionName'>> & Pick<ReadCallback, 'gasLimit' | 'args'>) | {
|
|
132
155
|
target?: never;
|
|
133
156
|
gasLimit?: bigint;
|
|
134
157
|
abi?: never;
|
|
@@ -148,11 +171,11 @@ type Web2Values = {
|
|
|
148
171
|
string: string;
|
|
149
172
|
};
|
|
150
173
|
type Web2Value<E extends Web2Extract> = Web2Values[E['valueType']];
|
|
151
|
-
type ReadFunctionName<A extends Abi, F extends string> = F & ContractFunctionName<A,
|
|
174
|
+
type ReadFunctionName<A extends Abi, F extends string> = F & ContractFunctionName<A, AbiStateMutability>;
|
|
152
175
|
type ReadFunctionArgs<O, A extends Abi, F extends string> = O extends {
|
|
153
|
-
args: infer Args extends ContractFunctionArgs<A,
|
|
154
|
-
} ? Args : readonly [] extends ContractFunctionArgs<A,
|
|
155
|
-
type ReadCallValue<A extends Abi, F extends string, Args extends ContractFunctionArgs<A,
|
|
176
|
+
args: infer Args extends ContractFunctionArgs<A, AbiStateMutability, ReadFunctionName<A, F>>;
|
|
177
|
+
} ? Args : readonly [] extends ContractFunctionArgs<A, AbiStateMutability, ReadFunctionName<A, F>> ? readonly [] : ContractFunctionArgs<A, AbiStateMutability, ReadFunctionName<A, F>>;
|
|
178
|
+
type ReadCallValue<A extends Abi, F extends string, Args extends ContractFunctionArgs<A, AbiStateMutability, ReadFunctionName<A, F>>> = ExtractAbiFunctionForArgs<A, AbiStateMutability, ReadFunctionName<A, F>, Args> extends infer Selected ? Selected extends Abi[number] & {
|
|
156
179
|
type: 'function';
|
|
157
180
|
outputs: readonly unknown[];
|
|
158
181
|
} ? ContractFunctionReturnType<readonly [Selected]> : never : never;
|
|
@@ -160,7 +183,7 @@ type ReadCallValue<A extends Abi, F extends string, Args extends ContractFunctio
|
|
|
160
183
|
export type ReadValue<O> = O extends {
|
|
161
184
|
abi: infer A extends Abi;
|
|
162
185
|
functionName: infer F extends string;
|
|
163
|
-
} ? number extends A['length'] ?
|
|
186
|
+
} ? number extends A['length'] ? unknown : ReadCallValue<A, F, ReadFunctionArgs<O, A, F>> : O extends {
|
|
164
187
|
web2: {
|
|
165
188
|
extract: infer E extends readonly Web2Extract[];
|
|
166
189
|
};
|
|
@@ -168,7 +191,19 @@ export type ReadValue<O> = O extends {
|
|
|
168
191
|
readonly [K in keyof E]: Web2Value<E[K]>;
|
|
169
192
|
} : O extends {
|
|
170
193
|
storageSlot: unknown;
|
|
171
|
-
} ? Hex :
|
|
194
|
+
} ? Hex : O extends {
|
|
195
|
+
idl: infer I extends Idl;
|
|
196
|
+
} ? IdlReadValue<I, O extends {
|
|
197
|
+
functionName: infer N extends string;
|
|
198
|
+
} ? N : undefined> : bigint;
|
|
199
|
+
/** snake_case / camelCase / PascalCase spellings compare equal (mirrors `idlNameKey`). */
|
|
200
|
+
type IdlNameKey<S extends string> = Lowercase<S extends `${infer H}_${infer T}` ? `${H}${IdlNameKey<T>}` : S>;
|
|
201
|
+
type IdlAccountName<I extends Idl> = Extract<keyof IdlAccounts<I>, string>;
|
|
202
|
+
type MatchAccount<I extends Idl, N extends string> = {
|
|
203
|
+
[K in IdlAccountName<I>]: IdlNameKey<K> extends IdlNameKey<N> ? K : never;
|
|
204
|
+
}[IdlAccountName<I>];
|
|
205
|
+
/** A literal IDL types the account; a dynamic one (names typed `string`) yields `unknown`. */
|
|
206
|
+
type IdlReadValue<I extends Idl, N extends string | undefined> = string extends IdlAccountName<I> ? unknown : N extends string ? IdlAccounts<I>[MatchAccount<I, N>] : IdlAccounts<I>[IdlAccountName<I>];
|
|
172
207
|
/** Just `balanceOf` — it also types the decoded result as uint256. */
|
|
173
208
|
export declare const ERC20_BALANCE_OF_ABI: readonly [{
|
|
174
209
|
readonly type: "function";
|
|
@@ -185,21 +220,31 @@ export declare const ERC20_BALANCE_OF_ABI: readonly [{
|
|
|
185
220
|
}];
|
|
186
221
|
export declare function toReadDestination(chain: ReadChain): ReadDestination;
|
|
187
222
|
/** Build the internal query from the public grammar. Pure. */
|
|
188
|
-
export declare function toReadQuery(subject: string, o: ReadOptionsInput): ReadQuery;
|
|
223
|
+
export declare function toReadQuery(subject: string, o: ReadOptionsInput, resolvedTokenProgram?: PublicKey): ReadQuery;
|
|
189
224
|
/** Public options → the spec builder's params. Pure; throws `InvalidReadQueryError` on a malformed request. */
|
|
190
|
-
export declare function toBuildReadSpecParams(subject: string, o: ReadOptionsInput, network?: PUSH_NETWORK): BuildReadSpecParams;
|
|
191
|
-
/**
|
|
225
|
+
export declare function toBuildReadSpecParams(subject: string, o: ReadOptionsInput, network?: PUSH_NETWORK, resolvedTokenProgram?: PublicKey): BuildReadSpecParams;
|
|
226
|
+
/** Resolve Solana mint ownership before the otherwise-pure options conversion. */
|
|
227
|
+
export declare function resolveReadSpecParams(subject: string, o: ReadOptionsInput, network: PUSH_NETWORK, rpcUrls: Partial<Record<CHAIN, string[]>>): Promise<BuildReadSpecParams>;
|
|
228
|
+
/** Internal offline key helper. Solana token callers must use PreparedRead.queryKey after mint resolution. */
|
|
192
229
|
export declare function getReadQueryKey<const O extends ReadQueryOptions>(subject: string, options: O & ValidateReadCall<O>): Hex;
|
|
193
230
|
export declare function toLifecycleOptions(o?: ReadTrackOptions): ReadLifecycleOptions;
|
|
194
|
-
/**
|
|
231
|
+
/**
|
|
232
|
+
* Restrict known ABIs to declared functions and their argument tuple. Every mutability
|
|
233
|
+
* is allowed: the read is an eth_call simulation, so state-changing functions only return data.
|
|
234
|
+
*/
|
|
195
235
|
export type ValidateReadCall<O> = O extends {
|
|
196
236
|
abi: infer A extends Abi;
|
|
197
237
|
functionName: infer F extends string;
|
|
198
238
|
} ? {
|
|
199
|
-
functionName: ContractFunctionName<A,
|
|
200
|
-
} & (readonly [] extends ContractFunctionArgs<A,
|
|
201
|
-
args?: ContractFunctionArgs<A,
|
|
239
|
+
functionName: ContractFunctionName<A, AbiStateMutability>;
|
|
240
|
+
} & (readonly [] extends ContractFunctionArgs<A, AbiStateMutability, F & ContractFunctionName<A, AbiStateMutability>> ? {
|
|
241
|
+
args?: ContractFunctionArgs<A, AbiStateMutability, F & ContractFunctionName<A, AbiStateMutability>>;
|
|
202
242
|
} : {
|
|
203
|
-
args: ContractFunctionArgs<A,
|
|
204
|
-
}) :
|
|
243
|
+
args: ContractFunctionArgs<A, AbiStateMutability, F & ContractFunctionName<A, AbiStateMutability>>;
|
|
244
|
+
}) : O extends {
|
|
245
|
+
idl: infer I extends Idl;
|
|
246
|
+
functionName: infer N extends string;
|
|
247
|
+
} ? string extends IdlAccountName<I> ? unknown : [MatchAccount<I, N>] extends [never] ? {
|
|
248
|
+
functionName: IdlAccountName<I>;
|
|
249
|
+
} : unknown : unknown;
|
|
205
250
|
export {};
|
|
@@ -4,9 +4,13 @@ exports.ERC20_BALANCE_OF_ABI = void 0;
|
|
|
4
4
|
exports.toReadDestination = toReadDestination;
|
|
5
5
|
exports.toReadQuery = toReadQuery;
|
|
6
6
|
exports.toBuildReadSpecParams = toBuildReadSpecParams;
|
|
7
|
+
exports.resolveReadSpecParams = resolveReadSpecParams;
|
|
7
8
|
exports.getReadQueryKey = getReadQueryKey;
|
|
8
9
|
exports.toLifecycleOptions = toLifecycleOptions;
|
|
9
10
|
const viem_1 = require("viem");
|
|
11
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
12
|
+
const svm_account_1 = require("./svm-account");
|
|
13
|
+
const svm_pda_1 = require("./svm-pda");
|
|
10
14
|
const enums_1 = require("../constants/enums");
|
|
11
15
|
const registry_1 = require("./registry");
|
|
12
16
|
const read_state_1 = require("../constants/read-state");
|
|
@@ -23,14 +27,18 @@ exports.ERC20_BALANCE_OF_ABI = [
|
|
|
23
27
|
outputs: [{ name: '', type: 'uint256' }],
|
|
24
28
|
},
|
|
25
29
|
];
|
|
26
|
-
const QUERY_KEYS = ['token', 'abi', 'storageSlot', 'web2'];
|
|
30
|
+
const QUERY_KEYS = ['token', 'abi', 'idl', 'storageSlot', 'web2'];
|
|
27
31
|
function queryKind(o) {
|
|
32
|
+
if ('tokenProgram' in o)
|
|
33
|
+
throw new errors_1.InvalidReadQueryError('tokenProgram is detected from the mint; remove options.tokenProgram');
|
|
34
|
+
if ('accountName' in o)
|
|
35
|
+
throw new errors_1.InvalidReadQueryError('accountName was removed; pass the account layout as functionName (optional — inferred from the discriminator)');
|
|
28
36
|
const present = QUERY_KEYS.filter((k) => o[k] !== undefined);
|
|
29
37
|
if (present.length > 1) {
|
|
30
38
|
throw new errors_1.InvalidReadQueryError(`query keys are mutually exclusive, got ${present.join(' + ')}`);
|
|
31
39
|
}
|
|
32
|
-
if ((o.functionName !== undefined || o.args !== undefined) && o.abi === undefined) {
|
|
33
|
-
throw new errors_1.InvalidReadQueryError('functionName and args need abi');
|
|
40
|
+
if ((o.functionName !== undefined || o.args !== undefined) && o.abi === undefined && o.idl === undefined) {
|
|
41
|
+
throw new errors_1.InvalidReadQueryError('functionName and args need abi or idl');
|
|
34
42
|
}
|
|
35
43
|
return present[0] ?? 'native';
|
|
36
44
|
}
|
|
@@ -38,12 +46,9 @@ function toReadDestination(chain) {
|
|
|
38
46
|
return chain === enums_1.CHAIN.WEB2 ? read_state_1.WEB2_DESTINATION : { chain };
|
|
39
47
|
}
|
|
40
48
|
/** Build the internal query from the public grammar. Pure. */
|
|
41
|
-
function toReadQuery(subject, o) {
|
|
49
|
+
function toReadQuery(subject, o, resolvedTokenProgram) {
|
|
42
50
|
const dest = (0, destination_1.resolveDestination)(toReadDestination(o.chain));
|
|
43
51
|
const kind = queryKind(o);
|
|
44
|
-
if (o.tokenProgram !== undefined && (dest.namespace !== read_state_1.READ_NAMESPACE.SVM || kind !== 'token' || !['spl-token', 'token-2022'].includes(o.tokenProgram))) {
|
|
45
|
-
throw new errors_1.InvalidReadQueryError('tokenProgram requires a Solana token balance query and must be spl-token or token-2022');
|
|
46
|
-
}
|
|
47
52
|
if (dest.namespace === read_state_1.READ_NAMESPACE.WEB2) {
|
|
48
53
|
if (kind !== 'web2' || !o.web2)
|
|
49
54
|
throw new errors_1.InvalidReadQueryError('a web2 read needs the `web2` option and no other query key');
|
|
@@ -75,24 +80,52 @@ function toReadQuery(subject, o) {
|
|
|
75
80
|
throw new errors_1.InvalidReadQueryError(`unsupported query for eip155: ${kind}`);
|
|
76
81
|
}
|
|
77
82
|
}
|
|
78
|
-
// SVM:
|
|
83
|
+
// SVM: token-program ownership has already been resolved by the async public path.
|
|
79
84
|
switch (kind) {
|
|
80
85
|
case 'native':
|
|
81
86
|
return { type: 'lamportBalance', account: subject };
|
|
82
87
|
case 'token':
|
|
83
|
-
|
|
88
|
+
if (!resolvedTokenProgram)
|
|
89
|
+
throw new errors_1.InvalidReadQueryError('Solana token reads need mint-owner resolution; use universal.prepareRead or universal.read');
|
|
90
|
+
return { type: 'splTokenAccount', account: (0, svm_1.deriveAssociatedTokenAddress)(subject, o.token, resolvedTokenProgram).toBase58() };
|
|
91
|
+
case 'idl':
|
|
92
|
+
return toIdlAccountQuery(subject, o);
|
|
84
93
|
default:
|
|
85
|
-
throw new errors_1.InvalidReadQueryError(`unsupported query for solana: ${kind}
|
|
94
|
+
throw new errors_1.InvalidReadQueryError(`unsupported query for solana: ${kind}; use idl for account-state decoding`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* `idl` → a raw account read that decodes with the IDL. The subject is the account,
|
|
99
|
+
* or the program id when `args` are the seeds of the `functionName` PDA.
|
|
100
|
+
*/
|
|
101
|
+
function toIdlAccountQuery(subject, o) {
|
|
102
|
+
const idl = o.idl;
|
|
103
|
+
(0, svm_account_1.validateIdl)(idl);
|
|
104
|
+
const accountName = o.functionName === undefined ? undefined : (0, svm_account_1.resolveAccountName)(idl, o.functionName);
|
|
105
|
+
if ((0, svm_pda_1.isProgramSubject)(idl, subject)) {
|
|
106
|
+
if (!o.functionName)
|
|
107
|
+
throw new errors_1.InvalidReadQueryError('the subject is the program id: pass functionName (the account layout) and args (its PDA seeds) to derive the account');
|
|
108
|
+
const pda = (0, svm_pda_1.deriveIdlPda)(idl, o.functionName, o.args ?? []);
|
|
109
|
+
return { type: 'rawAccountData', account: pda.toBase58(), idl, accountName };
|
|
110
|
+
}
|
|
111
|
+
if (o.args !== undefined)
|
|
112
|
+
throw new errors_1.InvalidReadQueryError('with idl, args are PDA seeds and need the program id as the subject');
|
|
113
|
+
try {
|
|
114
|
+
new web3_js_1.PublicKey(subject);
|
|
86
115
|
}
|
|
116
|
+
catch {
|
|
117
|
+
throw new errors_1.InvalidReadQueryError(`Solana subject must be a base58 pubkey, got ${subject}`);
|
|
118
|
+
}
|
|
119
|
+
return { type: 'rawAccountData', account: subject, idl, accountName };
|
|
87
120
|
}
|
|
88
121
|
/** Public options → the spec builder's params. Pure; throws `InvalidReadQueryError` on a malformed request. */
|
|
89
|
-
function toBuildReadSpecParams(subject, o, network = enums_1.PUSH_NETWORK.TESTNET_DONUT) {
|
|
122
|
+
function toBuildReadSpecParams(subject, o, network = enums_1.PUSH_NETWORK.TESTNET_DONUT, resolvedTokenProgram) {
|
|
90
123
|
const destination = toReadDestination(o.chain);
|
|
91
124
|
const namespace = (0, destination_1.resolveDestination)(destination).namespace;
|
|
92
125
|
if (namespace !== read_state_1.READ_NAMESPACE.EVM && (o.blockNumber !== undefined || o.minConfirmations !== undefined)) {
|
|
93
126
|
throw new errors_1.InvalidReadQueryError(`${namespace} reads do not expose blockNumber or minConfirmations; the SDK selects the read reference internally`);
|
|
94
127
|
}
|
|
95
|
-
const query = toReadQuery(subject, o);
|
|
128
|
+
const query = toReadQuery(subject, o, resolvedTokenProgram);
|
|
96
129
|
const callback = (0, registry_1.resolveReadCallback)(o.callback, network, (0, registry_1.computeReadQueryKey)(destination, query));
|
|
97
130
|
return {
|
|
98
131
|
callback,
|
|
@@ -106,7 +139,18 @@ function toBuildReadSpecParams(subject, o, network = enums_1.PUSH_NETWORK.TESTNE
|
|
|
106
139
|
maxFee: o.maxFee,
|
|
107
140
|
};
|
|
108
141
|
}
|
|
109
|
-
/**
|
|
142
|
+
/** Resolve Solana mint ownership before the otherwise-pure options conversion. */
|
|
143
|
+
async function resolveReadSpecParams(subject, o, network, rpcUrls) {
|
|
144
|
+
const kind = queryKind(o);
|
|
145
|
+
const namespace = (0, destination_1.resolveDestination)(toReadDestination(o.chain)).namespace;
|
|
146
|
+
if (namespace === read_state_1.READ_NAMESPACE.SVM && kind === 'token') {
|
|
147
|
+
new web3_js_1.PublicKey(subject);
|
|
148
|
+
const program = await (0, svm_account_1.detectTokenProgram)(o.token, o.chain, rpcUrls[o.chain]);
|
|
149
|
+
return toBuildReadSpecParams(subject, o, network, program);
|
|
150
|
+
}
|
|
151
|
+
return toBuildReadSpecParams(subject, o, network);
|
|
152
|
+
}
|
|
153
|
+
/** Internal offline key helper. Solana token callers must use PreparedRead.queryKey after mint resolution. */
|
|
110
154
|
function getReadQueryKey(subject, options) {
|
|
111
155
|
return (0, registry_1.computeReadQueryKey)(toReadDestination(options.chain), toReadQuery(subject, options));
|
|
112
156
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Abi, Address, Hex } from 'viem';
|
|
2
|
+
import type { Idl } from '@coral-xyz/anchor';
|
|
2
3
|
import type { CHAIN } from '../constants/enums';
|
|
3
4
|
import type { ReadNamespace } from '../constants/read-state';
|
|
4
5
|
/**
|
|
@@ -48,6 +49,8 @@ export type SvmReadQuery = {
|
|
|
48
49
|
} | {
|
|
49
50
|
type: 'rawAccountData';
|
|
50
51
|
account: string | Uint8Array;
|
|
52
|
+
idl?: Idl; /** Omitted → layout inferred from the discriminator. */
|
|
53
|
+
accountName?: string;
|
|
51
54
|
};
|
|
52
55
|
export type Web2ValueType = 'uint256' | 'int256' | 'bool' | 'string' | 'bytes';
|
|
53
56
|
export interface Web2Extract {
|
|
@@ -71,6 +74,10 @@ export interface Web2ReadQuery {
|
|
|
71
74
|
}
|
|
72
75
|
export type ReadQuery = EvmReadQuery | SvmReadQuery | Web2ReadQuery;
|
|
73
76
|
export type ReadResultShape = {
|
|
77
|
+
kind: 'svmAccount';
|
|
78
|
+
idl: Idl; /** Omitted → layout inferred from the discriminator. */
|
|
79
|
+
accountName?: string;
|
|
80
|
+
} | {
|
|
74
81
|
kind: 'uint256';
|
|
75
82
|
} | {
|
|
76
83
|
kind: 'bytes32';
|
|
@@ -98,6 +105,10 @@ export interface EncodedReadQuery {
|
|
|
98
105
|
warnings: string[];
|
|
99
106
|
}
|
|
100
107
|
export type DecodedReadResult = {
|
|
108
|
+
kind: 'svmAccount';
|
|
109
|
+
value: unknown; /** The IDL layout the data decoded as. */
|
|
110
|
+
accountName: string;
|
|
111
|
+
} | {
|
|
101
112
|
kind: 'uint256';
|
|
102
113
|
value: bigint;
|
|
103
114
|
} | {
|
|
@@ -314,11 +325,20 @@ export interface ReadFees {
|
|
|
314
325
|
}
|
|
315
326
|
export interface UniversalReadResponse<T = unknown> {
|
|
316
327
|
requestId: Hex;
|
|
328
|
+
/** `requestId` as the `uint256` contracts key results by (e.g. registry `resultByRequestId`). */
|
|
329
|
+
requestIdUint: bigint;
|
|
317
330
|
/** Push tx that carried the request */
|
|
318
331
|
txHash: Hex;
|
|
319
332
|
destination: ResolvedDestination;
|
|
320
333
|
/** Required read destination, including `CHAIN.WEB2`. */
|
|
321
334
|
chain: ReadChain;
|
|
335
|
+
/**
|
|
336
|
+
* The one field that answers "did it work": `SUCCESS` means the source read
|
|
337
|
+
* succeeded, your callback ran, and `value` is set (when a shape is known).
|
|
338
|
+
* Derived by the SDK from `status`, `raw.status`, `callbackDelivered` and
|
|
339
|
+
* `decodeError`, which stay available for debugging.
|
|
340
|
+
*/
|
|
341
|
+
outcome: READ_OUTCOME;
|
|
322
342
|
status: UNIVERSAL_READ_STATUS;
|
|
323
343
|
isTerminal: boolean;
|
|
324
344
|
/**
|
|
@@ -405,6 +425,27 @@ export declare enum READ_ERROR_CODE {
|
|
|
405
425
|
INVALID_RESULT = 5,
|
|
406
426
|
REJECTED = 6
|
|
407
427
|
}
|
|
428
|
+
/**
|
|
429
|
+
* SDK-derived single outcome. FULFILLED on the node only means the fulfil tx
|
|
430
|
+
* landed; this separates the ways a FULFILLED read can still not have worked.
|
|
431
|
+
*/
|
|
432
|
+
export declare enum READ_OUTCOME {
|
|
433
|
+
/** Not terminal yet. */
|
|
434
|
+
PENDING = "PENDING",
|
|
435
|
+
/** Source SUCCESS, callback delivered, result decoded (or no shape to decode with). */
|
|
436
|
+
SUCCESS = "SUCCESS",
|
|
437
|
+
/** FULFILLED, but the destination returned an error (`raw.errorCode`). */
|
|
438
|
+
SOURCE_ERROR = "SOURCE_ERROR",
|
|
439
|
+
/** FULFILLED, but the receiver callback reverted or ran out of gas (`callbackFailReason`). */
|
|
440
|
+
CALLBACK_FAILED = "CALLBACK_FAILED",
|
|
441
|
+
/** Delivered, but `resultData` did not match the expected shape (`decodeError`). */
|
|
442
|
+
DECODE_FAILED = "DECODE_FAILED",
|
|
443
|
+
EXPIRED = "EXPIRED",
|
|
444
|
+
FAILED = "FAILED",
|
|
445
|
+
ABORTED = "ABORTED",
|
|
446
|
+
/** FULFILLED, but the fulfil receipt was unavailable, so delivery cannot be confirmed. */
|
|
447
|
+
UNKNOWN = "UNKNOWN"
|
|
448
|
+
}
|
|
408
449
|
/** On-chain lifecycle (`RequestStatus` in ReadTypes.sol). */
|
|
409
450
|
export declare enum CONTRACT_REQUEST_STATUS {
|
|
410
451
|
NONE = 0,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TERMINAL_READ_STATUSES = exports.CONTRACT_REQUEST_STATUS = exports.READ_ERROR_CODE = exports.READ_STATUS = exports.UNIVERSAL_READ_STATUS = void 0;
|
|
3
|
+
exports.TERMINAL_READ_STATUSES = exports.CONTRACT_REQUEST_STATUS = exports.READ_OUTCOME = exports.READ_ERROR_CODE = exports.READ_STATUS = exports.UNIVERSAL_READ_STATUS = void 0;
|
|
4
4
|
// ---------------------------------------------------------------------------
|
|
5
5
|
// Status enums (mirror ucallback.v1 protos; re-exported for the public surface)
|
|
6
6
|
// ---------------------------------------------------------------------------
|
|
@@ -32,6 +32,28 @@ var READ_ERROR_CODE;
|
|
|
32
32
|
READ_ERROR_CODE[READ_ERROR_CODE["INVALID_RESULT"] = 5] = "INVALID_RESULT";
|
|
33
33
|
READ_ERROR_CODE[READ_ERROR_CODE["REJECTED"] = 6] = "REJECTED";
|
|
34
34
|
})(READ_ERROR_CODE || (exports.READ_ERROR_CODE = READ_ERROR_CODE = {}));
|
|
35
|
+
/**
|
|
36
|
+
* SDK-derived single outcome. FULFILLED on the node only means the fulfil tx
|
|
37
|
+
* landed; this separates the ways a FULFILLED read can still not have worked.
|
|
38
|
+
*/
|
|
39
|
+
var READ_OUTCOME;
|
|
40
|
+
(function (READ_OUTCOME) {
|
|
41
|
+
/** Not terminal yet. */
|
|
42
|
+
READ_OUTCOME["PENDING"] = "PENDING";
|
|
43
|
+
/** Source SUCCESS, callback delivered, result decoded (or no shape to decode with). */
|
|
44
|
+
READ_OUTCOME["SUCCESS"] = "SUCCESS";
|
|
45
|
+
/** FULFILLED, but the destination returned an error (`raw.errorCode`). */
|
|
46
|
+
READ_OUTCOME["SOURCE_ERROR"] = "SOURCE_ERROR";
|
|
47
|
+
/** FULFILLED, but the receiver callback reverted or ran out of gas (`callbackFailReason`). */
|
|
48
|
+
READ_OUTCOME["CALLBACK_FAILED"] = "CALLBACK_FAILED";
|
|
49
|
+
/** Delivered, but `resultData` did not match the expected shape (`decodeError`). */
|
|
50
|
+
READ_OUTCOME["DECODE_FAILED"] = "DECODE_FAILED";
|
|
51
|
+
READ_OUTCOME["EXPIRED"] = "EXPIRED";
|
|
52
|
+
READ_OUTCOME["FAILED"] = "FAILED";
|
|
53
|
+
READ_OUTCOME["ABORTED"] = "ABORTED";
|
|
54
|
+
/** FULFILLED, but the fulfil receipt was unavailable, so delivery cannot be confirmed. */
|
|
55
|
+
READ_OUTCOME["UNKNOWN"] = "UNKNOWN";
|
|
56
|
+
})(READ_OUTCOME || (exports.READ_OUTCOME = READ_OUTCOME = {}));
|
|
35
57
|
/** On-chain lifecycle (`RequestStatus` in ReadTypes.sol). */
|
|
36
58
|
var CONTRACT_REQUEST_STATUS;
|
|
37
59
|
(function (CONTRACT_REQUEST_STATUS) {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import { type Hex } from 'viem';
|
|
19
19
|
import { PUSH_NETWORK } from '../constants/enums';
|
|
20
20
|
import type { PushClient } from '../push-client/push-client';
|
|
21
|
-
import { type ReadLifecycleOptions, type ReadResultShape, type UniversalReadResponse } from './read-state.types';
|
|
21
|
+
import { READ_OUTCOME, READ_STATUS, UNIVERSAL_READ_STATUS, type ReadLifecycleOptions, type ReadResultShape, type UniversalReadResponse } from './read-state.types';
|
|
22
22
|
export type ReadHookEmitter = (hookId: string, ...args: unknown[]) => void;
|
|
23
23
|
export interface TrackReadDeps {
|
|
24
24
|
pushClient: Pick<PushClient, 'getUniversalRead' | 'getReadsByTx' | 'getTransactionReceiptWithArchiveFallback' | 'getBlockResultEvents'> & {
|
|
@@ -38,6 +38,11 @@ export declare function trackRead(deps: TrackReadDeps, ref: {
|
|
|
38
38
|
}, opts?: ReadLifecycleOptions): Promise<UniversalReadResponse>;
|
|
39
39
|
/** Normalise a bigint / hex requestId to the node's lowercase 32-byte key. */
|
|
40
40
|
export declare function toRequestIdHex(id: Hex | bigint): Hex;
|
|
41
|
+
/**
|
|
42
|
+
* One answer to "did it work". Order matters: a source error is delivered to the
|
|
43
|
+
* callback as an ERROR result, so it is reported before any callback outcome.
|
|
44
|
+
*/
|
|
45
|
+
export declare function deriveReadOutcome(status: UNIVERSAL_READ_STATUS, rawStatus: READ_STATUS | undefined, callbackDelivered: boolean | undefined, decodeError: string | undefined): READ_OUTCOME;
|
|
41
46
|
/** Best-effort shape from the on-chain envelope. Contract calls carry no ABI → raw. */
|
|
42
47
|
export declare function inferResultShape(namespace: string, query: Hex): ReadResultShape;
|
|
43
48
|
export declare function statusName(status: number): string;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.trackRead = trackRead;
|
|
4
4
|
exports.toRequestIdHex = toRequestIdHex;
|
|
5
|
+
exports.deriveReadOutcome = deriveReadOutcome;
|
|
5
6
|
exports.inferResultShape = inferResultShape;
|
|
6
7
|
exports.statusName = statusName;
|
|
7
8
|
exports.defaultWaitTimeoutMs = defaultWaitTimeoutMs;
|
|
@@ -61,16 +62,23 @@ async function fetchRecords(deps, ref) {
|
|
|
61
62
|
/** A request is indexed when its block is processed — briefly retry a miss. */
|
|
62
63
|
async function lookupWithRetry(deps, ref, opts) {
|
|
63
64
|
const now = deps.now ?? Date.now;
|
|
65
|
+
const emit = deps.emit ?? (() => undefined);
|
|
64
66
|
const interval = pollInterval(opts);
|
|
65
67
|
const start = now();
|
|
68
|
+
const hookRef = 'txHash' in ref ? { txHash: ref.txHash } : { requestId: toRequestIdHex(ref.requestId) };
|
|
69
|
+
emit(progress_hook_types_1.PROGRESS_HOOK.READ_TX_104_03, hookRef);
|
|
66
70
|
for (;;) {
|
|
67
71
|
const records = await fetchRecords(deps, ref);
|
|
68
|
-
if (records.length > 0)
|
|
72
|
+
if (records.length > 0) {
|
|
73
|
+
for (const r of records)
|
|
74
|
+
emit(progress_hook_types_1.PROGRESS_HOOK.READ_TX_104_04, toRequestIdHex(r.id), statusName(r.status));
|
|
69
75
|
return records;
|
|
76
|
+
}
|
|
70
77
|
if (now() - start + interval > read_state_1.READ_TRACK_LOOKUP_TIMEOUT_MS)
|
|
71
78
|
break;
|
|
72
79
|
await sleep(interval);
|
|
73
80
|
}
|
|
81
|
+
emit(progress_hook_types_1.PROGRESS_HOOK.READ_TX_104_05, hookRef, now() - start);
|
|
74
82
|
const label = 'txHash' in ref ? ref.txHash : toRequestIdHex(ref.requestId);
|
|
75
83
|
throw new errors_1.ReadNotFoundError(label, 'txHash' in ref ? { txHash: ref.txHash } : { requestId: label });
|
|
76
84
|
}
|
|
@@ -144,6 +152,8 @@ async function buildResponse(deps, record, opts) {
|
|
|
144
152
|
}
|
|
145
153
|
const txHash = req.requestedTxHash.toLowerCase();
|
|
146
154
|
const response = {
|
|
155
|
+
requestIdUint: BigInt(requestId),
|
|
156
|
+
outcome: deriveReadOutcome(status, raw?.status, callbackDelivered, decodeError),
|
|
147
157
|
requestId,
|
|
148
158
|
txHash,
|
|
149
159
|
destination,
|
|
@@ -174,23 +184,51 @@ async function buildResponse(deps, record, opts) {
|
|
|
174
184
|
};
|
|
175
185
|
return response;
|
|
176
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* One answer to "did it work". Order matters: a source error is delivered to the
|
|
189
|
+
* callback as an ERROR result, so it is reported before any callback outcome.
|
|
190
|
+
*/
|
|
191
|
+
function deriveReadOutcome(status, rawStatus, callbackDelivered, decodeError) {
|
|
192
|
+
switch (status) {
|
|
193
|
+
case read_state_types_1.UNIVERSAL_READ_STATUS.EXPIRED: return read_state_types_1.READ_OUTCOME.EXPIRED;
|
|
194
|
+
case read_state_types_1.UNIVERSAL_READ_STATUS.FAILED: return read_state_types_1.READ_OUTCOME.FAILED;
|
|
195
|
+
case read_state_types_1.UNIVERSAL_READ_STATUS.ABORTED: return read_state_types_1.READ_OUTCOME.ABORTED;
|
|
196
|
+
case read_state_types_1.UNIVERSAL_READ_STATUS.FULFILLED:
|
|
197
|
+
if (rawStatus !== undefined && rawStatus !== read_state_types_1.READ_STATUS.SUCCESS)
|
|
198
|
+
return read_state_types_1.READ_OUTCOME.SOURCE_ERROR;
|
|
199
|
+
if (callbackDelivered === false)
|
|
200
|
+
return read_state_types_1.READ_OUTCOME.CALLBACK_FAILED;
|
|
201
|
+
if (callbackDelivered !== true)
|
|
202
|
+
return read_state_types_1.READ_OUTCOME.UNKNOWN;
|
|
203
|
+
if (decodeError !== undefined)
|
|
204
|
+
return read_state_types_1.READ_OUTCOME.DECODE_FAILED;
|
|
205
|
+
return rawStatus === read_state_types_1.READ_STATUS.SUCCESS ? read_state_types_1.READ_OUTCOME.SUCCESS : read_state_types_1.READ_OUTCOME.UNKNOWN;
|
|
206
|
+
default:
|
|
207
|
+
return read_state_types_1.READ_OUTCOME.PENDING;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
177
210
|
/** Parse every pc_tx receipt (fulfil, settle) into one merged outcome. Missing receipts are skipped. */
|
|
178
211
|
async function collectOutcome(deps, record, requestId) {
|
|
179
212
|
const uc = chain_1.UNIVERSAL_CALLBACK_ADDRESSES[deps.pushNetwork];
|
|
180
213
|
const merged = {};
|
|
181
|
-
|
|
214
|
+
// Fetch in parallel, merge in pc_tx order (settle facts overwrite fulfil facts as before).
|
|
215
|
+
const outcomes = await Promise.all(record.pcTx.map(async (t) => {
|
|
182
216
|
if (!t.txHash)
|
|
183
|
-
|
|
217
|
+
return undefined;
|
|
184
218
|
try {
|
|
185
219
|
const receipt = await deps.pushClient.getTransactionReceiptWithArchiveFallback(t.txHash);
|
|
186
|
-
|
|
187
|
-
for (const [k, v] of Object.entries(o)) {
|
|
188
|
-
if (v !== undefined)
|
|
189
|
-
merged[k] = v;
|
|
190
|
-
}
|
|
220
|
+
return (0, read_events_1.parseFulfilOutcome)(receipt, uc, requestId);
|
|
191
221
|
}
|
|
192
222
|
catch {
|
|
193
|
-
// pruned or not yet available — leave those fields undefined rather than guess
|
|
223
|
+
return undefined; // pruned or not yet available — leave those fields undefined rather than guess
|
|
224
|
+
}
|
|
225
|
+
}));
|
|
226
|
+
for (const o of outcomes) {
|
|
227
|
+
if (!o)
|
|
228
|
+
continue;
|
|
229
|
+
for (const [k, v] of Object.entries(o)) {
|
|
230
|
+
if (v !== undefined)
|
|
231
|
+
merged[k] = v;
|
|
194
232
|
}
|
|
195
233
|
}
|
|
196
234
|
return merged;
|
|
@@ -277,7 +315,7 @@ function chainFromCaip2(caip2) {
|
|
|
277
315
|
}
|
|
278
316
|
function explorerTxUrl(network, txHash) {
|
|
279
317
|
const chain = network === enums_1.PUSH_NETWORK.MAINNET ? enums_1.CHAIN.PUSH_MAINNET : network === enums_1.PUSH_NETWORK.LOCALNET ? enums_1.CHAIN.PUSH_LOCALNET : enums_1.CHAIN.PUSH_TESTNET_DONUT;
|
|
280
|
-
const base = chain_1.PUSH_CHAIN_INFO[chain].explorerUrl ?? 'https://
|
|
318
|
+
const base = chain_1.PUSH_CHAIN_INFO[chain].explorerUrl ?? 'https://donut.push.network';
|
|
281
319
|
return `${base}/tx/${txHash}`;
|
|
282
320
|
}
|
|
283
321
|
// ---------------------------------------------------------------------------
|
|
@@ -312,6 +350,8 @@ async function waitForRead(deps, initial, opts) {
|
|
|
312
350
|
const interval = pollInterval(opts);
|
|
313
351
|
const start = now();
|
|
314
352
|
let timeoutMs = opts.timeoutMs ?? read_state_1.READ_TRACK_MAX_TIMEOUT_MS;
|
|
353
|
+
// Push head at a known time; later heights are estimated from the block time (no extra RPC per poll).
|
|
354
|
+
let head;
|
|
315
355
|
// Resolve a fresh head for each wait/resume, only when a default is needed.
|
|
316
356
|
// Bound this RPC too: a stalled height lookup must not hang the promise forever.
|
|
317
357
|
if (opts.timeoutMs === undefined && !initial.isTerminal) {
|
|
@@ -327,6 +367,7 @@ async function waitForRead(deps, initial, opts) {
|
|
|
327
367
|
}, read_state_1.READ_TRACK_MAX_TIMEOUT_MS);
|
|
328
368
|
}),
|
|
329
369
|
]);
|
|
370
|
+
head = { height, at: now() };
|
|
330
371
|
timeoutMs = Math.min(read_state_1.READ_TRACK_MAX_TIMEOUT_MS, now() - start + defaultWaitTimeoutMs(height, initial.request.spec.expiryPushChainHeight));
|
|
331
372
|
}
|
|
332
373
|
finally {
|
|
@@ -334,6 +375,23 @@ async function waitForRead(deps, initial, opts) {
|
|
|
334
375
|
clearTimeout(timer);
|
|
335
376
|
}
|
|
336
377
|
}
|
|
378
|
+
else if (!initial.isTerminal) {
|
|
379
|
+
// Explicit timeout: the head only feeds the expiry warning, so never block on it.
|
|
380
|
+
deps.pushClient.publicClient.getBlockNumber({ cacheTime: 0 })
|
|
381
|
+
.then((height) => { head = { height, at: now() }; })
|
|
382
|
+
.catch(() => undefined);
|
|
383
|
+
}
|
|
384
|
+
let expiryWarned = false;
|
|
385
|
+
const warnNearExpiry = (r) => {
|
|
386
|
+
if (expiryWarned || !head)
|
|
387
|
+
return;
|
|
388
|
+
const estimated = head.height + BigInt(Math.floor((now() - head.at) / read_state_1.PUSH_BLOCK_TIME_MS));
|
|
389
|
+
const left = r.request.spec.expiryPushChainHeight - estimated;
|
|
390
|
+
if (left > read_state_1.READ_EXPIRY_WARNING_BLOCKS)
|
|
391
|
+
return;
|
|
392
|
+
expiryWarned = true;
|
|
393
|
+
emit(progress_hook_types_1.PROGRESS_HOOK.READ_TX_105_04, r.requestId, left > 0n ? left : 0n);
|
|
394
|
+
};
|
|
337
395
|
emit(progress_hook_types_1.PROGRESS_HOOK.READ_TX_104_02, initial.txHash, initial.requestId, initial.request.logIndex);
|
|
338
396
|
// Every snapshot taken from here on is built with the wait() options, so a
|
|
339
397
|
// resultShape given to wait() decodes even a record that is already terminal.
|
|
@@ -388,6 +446,7 @@ async function waitForRead(deps, initial, opts) {
|
|
|
388
446
|
else if (current.status === read_state_types_1.UNIVERSAL_READ_STATUS.VOTING)
|
|
389
447
|
emit(progress_hook_types_1.PROGRESS_HOOK.READ_TX_105_02, current.requestId);
|
|
390
448
|
}
|
|
449
|
+
warnNearExpiry(current);
|
|
391
450
|
const elapsed = now() - start;
|
|
392
451
|
if (elapsed >= timeoutMs)
|
|
393
452
|
timeout();
|
|
@@ -403,13 +462,20 @@ function emitTerminal(emit, r) {
|
|
|
403
462
|
emit(progress_hook_types_1.PROGRESS_HOOK.READ_TX_106_03, r.requestId, r.callbackFailReason);
|
|
404
463
|
if (r.fees.burned !== undefined)
|
|
405
464
|
emit(progress_hook_types_1.PROGRESS_HOOK.READ_TX_106_04, r.requestId, r.fees.burned, r.fees.refunded ?? 0n);
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
465
|
+
}
|
|
466
|
+
// Refund facts exist for FULFILLED/FAILED (settle logs) and EXPIRED (EndBlock logs).
|
|
467
|
+
if (r.fees.refundFailed === true)
|
|
468
|
+
emit(progress_hook_types_1.PROGRESS_HOOK.READ_TX_106_06, r.requestId, r.fees.refunded, r.request.refundTo);
|
|
469
|
+
else if (r.fees.refunded !== undefined)
|
|
470
|
+
emit(progress_hook_types_1.PROGRESS_HOOK.READ_TX_106_05, r.requestId, r.fees.refunded, r.request.refundTo);
|
|
471
|
+
// 199-01 means it worked. A FULFILLED read that did not (source error, callback
|
|
472
|
+
// failure, undecodable, unconfirmed delivery) ends on 199-02 named by its outcome.
|
|
473
|
+
if (r.outcome === read_state_types_1.READ_OUTCOME.SUCCESS) {
|
|
410
474
|
emit(progress_hook_types_1.PROGRESS_HOOK.READ_TX_199_01, r.requestId, r.value, r.raw?.resultData ?? '0x', r.callbackDelivered);
|
|
411
475
|
return;
|
|
412
476
|
}
|
|
413
|
-
|
|
477
|
+
const ended = r.status === read_state_types_1.UNIVERSAL_READ_STATUS.FULFILLED ? r.outcome : statusName(r.status);
|
|
478
|
+
const detail = r.errorMsg || r.decodeError || '';
|
|
479
|
+
emit(progress_hook_types_1.PROGRESS_HOOK.READ_TX_199_02, r.requestId, ended, r.raw?.errorCode, detail, r.fees.refunded);
|
|
414
480
|
}
|
|
415
481
|
//# sourceMappingURL=read-tracker.js.map
|
|
@@ -28,9 +28,8 @@ function resolveReadCallback(callback, network, queryKey) {
|
|
|
28
28
|
if (callback && 'request' in callback)
|
|
29
29
|
throw new errors_1.InvalidReadQueryError('callback.request was removed; use callback.abi, functionName and args');
|
|
30
30
|
if (callback?.target !== undefined) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return { ...callback, gasLimit: callback.gasLimit };
|
|
31
|
+
// Same default as the registry: unused callback budget is refunded.
|
|
32
|
+
return { ...callback, gasLimit: callback.gasLimit ?? read_state_1.REGISTRY_CALLBACK_GAS };
|
|
34
33
|
}
|
|
35
34
|
if (callback?.abi !== undefined || callback?.functionName !== undefined || callback?.args !== undefined) {
|
|
36
35
|
throw new errors_1.InvalidReadQueryError('callback abi, functionName and args require callback.target');
|