@usearete/sdk 0.8.2 → 0.9.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/README.md +10 -8
- package/dist/index.cjs +400 -376
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +16 -2
- package/dist/index.esm.js +400 -376
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3324,6 +3324,274 @@ function createChainClient(httpBaseUrl, fetchImpl) {
|
|
|
3324
3324
|
};
|
|
3325
3325
|
}
|
|
3326
3326
|
|
|
3327
|
+
class TransactionTransportError extends Error {
|
|
3328
|
+
constructor(status, body) {
|
|
3329
|
+
super(body.message);
|
|
3330
|
+
this.name = 'TransactionTransportError';
|
|
3331
|
+
this.status = status;
|
|
3332
|
+
this.code = body.code;
|
|
3333
|
+
this.retryable = body.retryable;
|
|
3334
|
+
this.requestId = body.request_id;
|
|
3335
|
+
this.submissionState = body.submission_state;
|
|
3336
|
+
this.signature = body.signature;
|
|
3337
|
+
this.details = body.details;
|
|
3338
|
+
}
|
|
3339
|
+
}
|
|
3340
|
+
function decimal(value) {
|
|
3341
|
+
return value === undefined ? undefined : value.toString(10);
|
|
3342
|
+
}
|
|
3343
|
+
function bigintField(value, field) {
|
|
3344
|
+
if (typeof value !== 'string' || !/^\d+$/.test(value)) {
|
|
3345
|
+
throw new Error(`Invalid decimal u64 field '${field}' in transaction response`);
|
|
3346
|
+
}
|
|
3347
|
+
return BigInt(value);
|
|
3348
|
+
}
|
|
3349
|
+
function optionalBigint(value, field) {
|
|
3350
|
+
return value === undefined || value === null ? undefined : bigintField(value, field);
|
|
3351
|
+
}
|
|
3352
|
+
function requestBody(value) {
|
|
3353
|
+
return JSON.stringify(Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined)));
|
|
3354
|
+
}
|
|
3355
|
+
async function parseError(response) {
|
|
3356
|
+
let body = {};
|
|
3357
|
+
try {
|
|
3358
|
+
body = await response.json();
|
|
3359
|
+
}
|
|
3360
|
+
catch {
|
|
3361
|
+
// Public errors are deliberately synthesized without reflecting raw bodies.
|
|
3362
|
+
}
|
|
3363
|
+
return new TransactionTransportError(response.status, {
|
|
3364
|
+
code: typeof body.code === 'string' ? body.code : 'transaction_transport_error',
|
|
3365
|
+
message: typeof body.message === 'string' ? body.message : `Transaction request failed (${response.status})`,
|
|
3366
|
+
retryable: body.retryable === true,
|
|
3367
|
+
request_id: body.request_id ?? body.requestId,
|
|
3368
|
+
submission_state: body.submission_state
|
|
3369
|
+
?? body.submissionState,
|
|
3370
|
+
signature: body.signature,
|
|
3371
|
+
details: body.details,
|
|
3372
|
+
});
|
|
3373
|
+
}
|
|
3374
|
+
function createTransactionTransport(baseUrl, authenticatedFetch) {
|
|
3375
|
+
const root = `${baseUrl.replace(/\/$/, '')}/transactions/v1`;
|
|
3376
|
+
const post = async (route, body, scope) => {
|
|
3377
|
+
const response = await authenticatedFetch(`${root}/${route}`, {
|
|
3378
|
+
method: 'POST',
|
|
3379
|
+
headers: { 'content-type': 'application/json' },
|
|
3380
|
+
body: requestBody(body),
|
|
3381
|
+
}, scope, true);
|
|
3382
|
+
if (!response.ok)
|
|
3383
|
+
throw await parseError(response);
|
|
3384
|
+
return response.json();
|
|
3385
|
+
};
|
|
3386
|
+
return {
|
|
3387
|
+
async getLatestBlockhash(options = {}) {
|
|
3388
|
+
const value = await post('latest-blockhash', {
|
|
3389
|
+
commitment: options.commitment,
|
|
3390
|
+
minContextSlot: decimal(options.minContextSlot),
|
|
3391
|
+
}, 'transaction:inspect');
|
|
3392
|
+
return {
|
|
3393
|
+
blockhash: String(value.blockhash),
|
|
3394
|
+
contextSlot: bigintField(value.contextSlot, 'contextSlot'),
|
|
3395
|
+
lastValidBlockHeight: bigintField(value.lastValidBlockHeight, 'lastValidBlockHeight'),
|
|
3396
|
+
};
|
|
3397
|
+
},
|
|
3398
|
+
async getFeeForMessage(message, options = {}) {
|
|
3399
|
+
const value = await post('fee', {
|
|
3400
|
+
message,
|
|
3401
|
+
commitment: options.commitment,
|
|
3402
|
+
minContextSlot: decimal(options.minContextSlot),
|
|
3403
|
+
}, 'transaction:inspect');
|
|
3404
|
+
return {
|
|
3405
|
+
feeLamports: value.feeLamports === null ? null : bigintField(value.feeLamports, 'feeLamports'),
|
|
3406
|
+
contextSlot: bigintField(value.contextSlot, 'contextSlot'),
|
|
3407
|
+
};
|
|
3408
|
+
},
|
|
3409
|
+
async simulateTransaction(transaction, options = {}) {
|
|
3410
|
+
const value = await post('simulate', {
|
|
3411
|
+
transaction,
|
|
3412
|
+
commitment: options.commitment,
|
|
3413
|
+
minContextSlot: decimal(options.minContextSlot),
|
|
3414
|
+
accounts: options.accounts ? { addresses: options.accounts } : undefined,
|
|
3415
|
+
innerInstructions: options.innerInstructions,
|
|
3416
|
+
replaceRecentBlockhash: options.replaceRecentBlockhash,
|
|
3417
|
+
}, 'transaction:inspect');
|
|
3418
|
+
return {
|
|
3419
|
+
contextSlot: bigintField(value.contextSlot, 'contextSlot'),
|
|
3420
|
+
err: value.err ?? null,
|
|
3421
|
+
logs: value.logs ?? null,
|
|
3422
|
+
unitsConsumed: optionalBigint(value.unitsConsumed, 'unitsConsumed'),
|
|
3423
|
+
accounts: value.accounts,
|
|
3424
|
+
};
|
|
3425
|
+
},
|
|
3426
|
+
sendTransaction(transaction, options = {}) {
|
|
3427
|
+
return post('send', {
|
|
3428
|
+
transaction,
|
|
3429
|
+
skipPreflight: options.skipPreflight,
|
|
3430
|
+
preflightCommitment: options.preflightCommitment,
|
|
3431
|
+
minContextSlot: decimal(options.minContextSlot),
|
|
3432
|
+
}, 'transaction:send');
|
|
3433
|
+
},
|
|
3434
|
+
async getSignatureStatus(signature, options = {}) {
|
|
3435
|
+
const value = await post('signature-status', {
|
|
3436
|
+
signature,
|
|
3437
|
+
searchTransactionHistory: options.searchTransactionHistory,
|
|
3438
|
+
}, 'transaction:inspect');
|
|
3439
|
+
const status = value?.status;
|
|
3440
|
+
if (!status)
|
|
3441
|
+
return null;
|
|
3442
|
+
return {
|
|
3443
|
+
signature,
|
|
3444
|
+
slot: status.slot === null ? null : bigintField(status.slot, 'slot'),
|
|
3445
|
+
confirmationStatus: status.confirmationStatus,
|
|
3446
|
+
err: status.err ?? null,
|
|
3447
|
+
};
|
|
3448
|
+
},
|
|
3449
|
+
async getBlockHeight(options = {}) {
|
|
3450
|
+
const value = await post('block-height', {
|
|
3451
|
+
commitment: options.commitment,
|
|
3452
|
+
minContextSlot: decimal(options.minContextSlot),
|
|
3453
|
+
}, 'transaction:inspect');
|
|
3454
|
+
return bigintField(value.blockHeight, 'blockHeight');
|
|
3455
|
+
},
|
|
3456
|
+
};
|
|
3457
|
+
}
|
|
3458
|
+
|
|
3459
|
+
function isSecureOrLoopbackHttpUrl$1(value) {
|
|
3460
|
+
try {
|
|
3461
|
+
const url = new URL(value);
|
|
3462
|
+
return url.protocol === 'https:'
|
|
3463
|
+
|| (url.protocol === 'http:'
|
|
3464
|
+
&& ['localhost', '127.0.0.1', '::1'].includes(url.hostname));
|
|
3465
|
+
}
|
|
3466
|
+
catch {
|
|
3467
|
+
return false;
|
|
3468
|
+
}
|
|
3469
|
+
}
|
|
3470
|
+
function validateBinding(capability, binding, requiredScopes) {
|
|
3471
|
+
const auth = binding?.auth;
|
|
3472
|
+
const complete = binding
|
|
3473
|
+
&& isSecureOrLoopbackHttpUrl$1(binding.endpoint)
|
|
3474
|
+
&& /^sgb_[A-Za-z0-9_-]{32}$/.test(binding.solanaGatewayBindingId)
|
|
3475
|
+
&& binding.cluster.trim().length > 0
|
|
3476
|
+
&& binding.region.trim().length > 0
|
|
3477
|
+
&& auth
|
|
3478
|
+
&& typeof auth.required === 'boolean'
|
|
3479
|
+
&& auth.mode === binding.authPolicy
|
|
3480
|
+
&& isSecureOrLoopbackHttpUrl$1(auth.sessionEndpoint)
|
|
3481
|
+
&& isSecureOrLoopbackHttpUrl$1(auth.jwksUrl)
|
|
3482
|
+
&& auth.tokenTransport === 'bearer'
|
|
3483
|
+
&& auth.audience === 'arete:solana-gateway'
|
|
3484
|
+
&& auth.targetKind === 'solana-gateway-binding'
|
|
3485
|
+
&& auth.targetId === binding.solanaGatewayBindingId
|
|
3486
|
+
&& Array.isArray(auth.scopes)
|
|
3487
|
+
&& requiredScopes.every((scope) => auth.scopes.includes(scope))
|
|
3488
|
+
&& Array.isArray(auth.acceptedKeyClasses)
|
|
3489
|
+
&& typeof auth.transactionEntitlementRequired === 'boolean';
|
|
3490
|
+
if (!complete) {
|
|
3491
|
+
throw new AreteError(`Hosted Solana gateway ${capability} binding is incomplete or inconsistent`, 'INVALID_CONFIG');
|
|
3492
|
+
}
|
|
3493
|
+
}
|
|
3494
|
+
function resolveFetch(fetchImpl) {
|
|
3495
|
+
if (fetchImpl)
|
|
3496
|
+
return fetchImpl;
|
|
3497
|
+
if (typeof globalThis.fetch === 'function')
|
|
3498
|
+
return globalThis.fetch.bind(globalThis);
|
|
3499
|
+
throw new AreteError('A fetch implementation is required for hosted Solana gateway transports', 'INVALID_CONFIG');
|
|
3500
|
+
}
|
|
3501
|
+
function hasRuntimeAuthStrategy(auth) {
|
|
3502
|
+
return auth?.token !== undefined
|
|
3503
|
+
|| auth?.getToken !== undefined
|
|
3504
|
+
|| auth?.tokenEndpoint !== undefined;
|
|
3505
|
+
}
|
|
3506
|
+
function bindingAuthConfig(binding, runtimeAuth) {
|
|
3507
|
+
if (hasRuntimeAuthStrategy(runtimeAuth))
|
|
3508
|
+
return runtimeAuth;
|
|
3509
|
+
if (!binding.auth.required)
|
|
3510
|
+
return runtimeAuth;
|
|
3511
|
+
return { ...runtimeAuth, tokenEndpoint: binding.auth.sessionEndpoint };
|
|
3512
|
+
}
|
|
3513
|
+
function refreshableErrorCode(response) {
|
|
3514
|
+
const wireCode = response.headers.get('X-Error-Code');
|
|
3515
|
+
return wireCode !== null
|
|
3516
|
+
&& shouldRefreshToken(parseErrorCode(wireCode.trim().replace(/_/g, '-')));
|
|
3517
|
+
}
|
|
3518
|
+
/**
|
|
3519
|
+
* Construct explicit hosted chain and transaction transports from generated
|
|
3520
|
+
* gateway descriptors. Tokens are isolated by exact binding target and scope.
|
|
3521
|
+
*/
|
|
3522
|
+
function createHostedSolanaGatewayTransports(bindings, options = {}) {
|
|
3523
|
+
validateBinding('chain', bindings.chain, ['read']);
|
|
3524
|
+
validateBinding('transactions', bindings.transactions, ['transaction:inspect', 'transaction:send']);
|
|
3525
|
+
const fetchImpl = resolveFetch(options.fetch);
|
|
3526
|
+
const managers = new Map();
|
|
3527
|
+
const bindingAuthentication = (binding) => {
|
|
3528
|
+
const auth = bindingAuthConfig(binding, options.auth);
|
|
3529
|
+
if (auth?.token !== undefined) {
|
|
3530
|
+
if (!auth.token) {
|
|
3531
|
+
throw new AreteError('Authentication token is empty', 'TOKEN_INVALID');
|
|
3532
|
+
}
|
|
3533
|
+
return {
|
|
3534
|
+
staticToken: auth.token,
|
|
3535
|
+
target: {
|
|
3536
|
+
targetKind: 'solana-gateway-binding',
|
|
3537
|
+
targetId: binding.solanaGatewayBindingId,
|
|
3538
|
+
},
|
|
3539
|
+
};
|
|
3540
|
+
}
|
|
3541
|
+
let manager;
|
|
3542
|
+
if (auth && (auth.getToken || auth.tokenEndpoint)) {
|
|
3543
|
+
const identity = hasRuntimeAuthStrategy(options.auth)
|
|
3544
|
+
? 'runtime-auth-strategy'
|
|
3545
|
+
: `session-endpoint:${binding.auth.sessionEndpoint}`;
|
|
3546
|
+
manager = managers.get(identity);
|
|
3547
|
+
if (!manager) {
|
|
3548
|
+
manager = new ConnectionManager({ websocketUrl: null, auth, fetch: fetchImpl });
|
|
3549
|
+
managers.set(identity, manager);
|
|
3550
|
+
}
|
|
3551
|
+
}
|
|
3552
|
+
return {
|
|
3553
|
+
manager,
|
|
3554
|
+
target: {
|
|
3555
|
+
targetKind: 'solana-gateway-binding',
|
|
3556
|
+
targetId: binding.solanaGatewayBindingId,
|
|
3557
|
+
},
|
|
3558
|
+
};
|
|
3559
|
+
};
|
|
3560
|
+
const authenticatedFetch = (authentication) => async (input, init, scope, requirePreDispatchMarker) => {
|
|
3561
|
+
const request = { ...authentication.target, scopes: [scope] };
|
|
3562
|
+
const attempt = async (forceRefresh) => {
|
|
3563
|
+
const token = authentication.staticToken
|
|
3564
|
+
?? (authentication.manager
|
|
3565
|
+
? await authentication.manager.getHttpAuthToken(request, forceRefresh)
|
|
3566
|
+
: undefined);
|
|
3567
|
+
const headers = new Headers(init?.headers);
|
|
3568
|
+
if (token)
|
|
3569
|
+
headers.set('authorization', `Bearer ${token}`);
|
|
3570
|
+
return fetchImpl(input, { ...init, headers });
|
|
3571
|
+
};
|
|
3572
|
+
let response = await attempt(false);
|
|
3573
|
+
const explicitlyNotDispatched = response.headers.get('X-Arete-Upstream-Attempted') === 'false';
|
|
3574
|
+
if (!response.ok
|
|
3575
|
+
&& authentication.manager
|
|
3576
|
+
&& refreshableErrorCode(response)
|
|
3577
|
+
&& (!requirePreDispatchMarker || explicitlyNotDispatched)) {
|
|
3578
|
+
authentication.manager.clearHttpAuthToken(request);
|
|
3579
|
+
response = await attempt(true);
|
|
3580
|
+
}
|
|
3581
|
+
return response;
|
|
3582
|
+
};
|
|
3583
|
+
const chainAuthentication = bindingAuthentication(bindings.chain);
|
|
3584
|
+
const transactionAuthentication = bindingAuthentication(bindings.transactions);
|
|
3585
|
+
const chainFetch = authenticatedFetch(chainAuthentication);
|
|
3586
|
+
const transactionFetch = authenticatedFetch(transactionAuthentication);
|
|
3587
|
+
return {
|
|
3588
|
+
chain: createChainClient(bindings.chain.endpoint, ((input, init) => chainFetch(typeof input === 'string'
|
|
3589
|
+
? input
|
|
3590
|
+
: input instanceof URL ? input.toString() : input.url, init, 'read', false))),
|
|
3591
|
+
transactions: createTransactionTransport(bindings.transactions.endpoint, (input, init, scope, requirePreDispatchMarker) => transactionFetch(input, init, scope, requirePreDispatchMarker)),
|
|
3592
|
+
};
|
|
3593
|
+
}
|
|
3594
|
+
|
|
3327
3595
|
/*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) */
|
|
3328
3596
|
/**
|
|
3329
3597
|
* 4KB JS implementation of ed25519 EdDSA signatures.
|
|
@@ -5954,237 +6222,105 @@ function isRefreshableErrorCode(code) {
|
|
|
5954
6222
|
}
|
|
5955
6223
|
function parseProgramReadResponse(response, path, body) {
|
|
5956
6224
|
if (!response.ok) {
|
|
5957
|
-
throw new ReadRequestError({
|
|
5958
|
-
status: response.status,
|
|
5959
|
-
path,
|
|
5960
|
-
body,
|
|
5961
|
-
serverErrorCode: responseErrorCode(response, body),
|
|
5962
|
-
});
|
|
5963
|
-
}
|
|
5964
|
-
try {
|
|
5965
|
-
return JSON.parse(body);
|
|
5966
|
-
}
|
|
5967
|
-
catch (error) {
|
|
5968
|
-
throw new AreteError(`Program read '${path}' returned invalid JSON`, 'INVALID_RESPONSE', error);
|
|
5969
|
-
}
|
|
5970
|
-
}
|
|
5971
|
-
function hostedAuthConfig(binding, runtimeAuth) {
|
|
5972
|
-
const metadata = binding.auth;
|
|
5973
|
-
const runtimeStrategyConfigured = runtimeAuth?.token !== undefined
|
|
5974
|
-
|| runtimeAuth?.getToken !== undefined
|
|
5975
|
-
|| runtimeAuth?.tokenEndpoint !== undefined;
|
|
5976
|
-
if (runtimeStrategyConfigured)
|
|
5977
|
-
return runtimeAuth;
|
|
5978
|
-
if (metadata.required === false)
|
|
5979
|
-
return runtimeAuth;
|
|
5980
|
-
return { ...runtimeAuth, tokenEndpoint: metadata.sessionEndpoint };
|
|
5981
|
-
}
|
|
5982
|
-
function hostedAuth(config) {
|
|
5983
|
-
const auth = hostedAuthConfig(config.binding, config.auth);
|
|
5984
|
-
if (auth?.token !== undefined && !auth.token) {
|
|
5985
|
-
throw new AreteError('Authentication token is empty', 'TOKEN_INVALID');
|
|
5986
|
-
}
|
|
5987
|
-
const bindingId = config.binding.programReadBindingId;
|
|
5988
|
-
if (!auth || auth.token !== undefined) {
|
|
5989
|
-
return { staticToken: auth?.token };
|
|
5990
|
-
}
|
|
5991
|
-
return {
|
|
5992
|
-
manager: new ConnectionManager({ websocketUrl: null, auth, fetch: config.fetch }),
|
|
5993
|
-
target: {
|
|
5994
|
-
targetKind: 'program-read-binding',
|
|
5995
|
-
targetId: bindingId,
|
|
5996
|
-
programReleaseHash: config.release.programReleaseHash,
|
|
5997
|
-
},
|
|
5998
|
-
};
|
|
5999
|
-
}
|
|
6000
|
-
function requestPath(release, request) {
|
|
6001
|
-
const releaseHash = encodeURIComponent(release.programReleaseHash).replace(/%3A/g, ':');
|
|
6002
|
-
const root = `/v1/releases/${releaseHash}`
|
|
6003
|
-
+ `/accounts/${encodeURIComponent(request.account)}`;
|
|
6004
|
-
if (request.operation === 'fetchMany')
|
|
6005
|
-
return root;
|
|
6006
|
-
const addressPath = `${root.replace(/\/+$/, '')}/${encodeURIComponent(request.address)}`;
|
|
6007
|
-
return request.operation === 'exists' ? `${addressPath}/exists` : addressPath;
|
|
6008
|
-
}
|
|
6009
|
-
function createProgramReadTransport(config) {
|
|
6010
|
-
if (config.kind === 'unavailable') {
|
|
6011
|
-
return {
|
|
6012
|
-
async read() {
|
|
6013
|
-
throw new AreteError(config.message, 'INVALID_CONFIG');
|
|
6014
|
-
},
|
|
6015
|
-
};
|
|
6016
|
-
}
|
|
6017
|
-
const auth = config.kind === 'hosted-binding' ? hostedAuth(config) : undefined;
|
|
6018
|
-
const endpoint = config.kind === 'hosted-binding'
|
|
6019
|
-
? config.binding.endpoint
|
|
6020
|
-
: config.endpoint;
|
|
6021
|
-
return {
|
|
6022
|
-
async read(request) {
|
|
6023
|
-
const path = requestPath(config.release, request);
|
|
6024
|
-
const input = appendUrl(endpoint, path);
|
|
6025
|
-
const attempt = async (forceRefresh) => {
|
|
6026
|
-
const token = auth?.staticToken
|
|
6027
|
-
?? (auth?.manager && auth.target
|
|
6028
|
-
? await auth.manager.getHttpAuthToken(auth.target, ['read'], forceRefresh)
|
|
6029
|
-
: undefined);
|
|
6030
|
-
const headers = new Headers(request.operation === 'fetchMany'
|
|
6031
|
-
? { 'content-type': 'application/json' }
|
|
6032
|
-
: undefined);
|
|
6033
|
-
if (token)
|
|
6034
|
-
headers.set('authorization', `Bearer ${token}`);
|
|
6035
|
-
return config.fetch(input, {
|
|
6036
|
-
method: request.operation === 'fetchMany' ? 'POST' : 'GET',
|
|
6037
|
-
headers,
|
|
6038
|
-
body: request.operation === 'fetchMany'
|
|
6039
|
-
? JSON.stringify({ addresses: request.addresses })
|
|
6040
|
-
: undefined,
|
|
6041
|
-
});
|
|
6042
|
-
};
|
|
6043
|
-
let response = await attempt(false);
|
|
6044
|
-
let body = await response.text();
|
|
6045
|
-
const wireCode = responseErrorCode(response, body);
|
|
6046
|
-
if (response.status === 401
|
|
6047
|
-
&& wireCode
|
|
6048
|
-
&& isRefreshableErrorCode(wireCode)
|
|
6049
|
-
&& auth?.manager
|
|
6050
|
-
&& auth.target) {
|
|
6051
|
-
auth.manager.clearHttpAuthToken(auth.target);
|
|
6052
|
-
response = await attempt(true);
|
|
6053
|
-
body = await response.text();
|
|
6054
|
-
}
|
|
6055
|
-
return parseProgramReadResponse(response, path, body);
|
|
6056
|
-
},
|
|
6057
|
-
};
|
|
6058
|
-
}
|
|
6059
|
-
|
|
6060
|
-
class TransactionTransportError extends Error {
|
|
6061
|
-
constructor(status, body) {
|
|
6062
|
-
super(body.message);
|
|
6063
|
-
this.name = 'TransactionTransportError';
|
|
6064
|
-
this.status = status;
|
|
6065
|
-
this.code = body.code;
|
|
6066
|
-
this.retryable = body.retryable;
|
|
6067
|
-
this.requestId = body.request_id;
|
|
6068
|
-
this.submissionState = body.submission_state;
|
|
6069
|
-
this.signature = body.signature;
|
|
6070
|
-
this.details = body.details;
|
|
6071
|
-
}
|
|
6072
|
-
}
|
|
6073
|
-
function decimal(value) {
|
|
6074
|
-
return value === undefined ? undefined : value.toString(10);
|
|
6075
|
-
}
|
|
6076
|
-
function bigintField(value, field) {
|
|
6077
|
-
if (typeof value !== 'string' || !/^\d+$/.test(value)) {
|
|
6078
|
-
throw new Error(`Invalid decimal u64 field '${field}' in transaction response`);
|
|
6079
|
-
}
|
|
6080
|
-
return BigInt(value);
|
|
6081
|
-
}
|
|
6082
|
-
function optionalBigint(value, field) {
|
|
6083
|
-
return value === undefined || value === null ? undefined : bigintField(value, field);
|
|
6084
|
-
}
|
|
6085
|
-
function requestBody(value) {
|
|
6086
|
-
return JSON.stringify(Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined)));
|
|
6087
|
-
}
|
|
6088
|
-
async function parseError(response) {
|
|
6089
|
-
let body = {};
|
|
6090
|
-
try {
|
|
6091
|
-
body = await response.json();
|
|
6092
|
-
}
|
|
6093
|
-
catch {
|
|
6094
|
-
// Public errors are deliberately synthesized without reflecting raw bodies.
|
|
6095
|
-
}
|
|
6096
|
-
return new TransactionTransportError(response.status, {
|
|
6097
|
-
code: typeof body.code === 'string' ? body.code : 'transaction_transport_error',
|
|
6098
|
-
message: typeof body.message === 'string' ? body.message : `Transaction request failed (${response.status})`,
|
|
6099
|
-
retryable: body.retryable === true,
|
|
6100
|
-
request_id: body.request_id ?? body.requestId,
|
|
6101
|
-
submission_state: body.submission_state
|
|
6102
|
-
?? body.submissionState,
|
|
6103
|
-
signature: body.signature,
|
|
6104
|
-
details: body.details,
|
|
6105
|
-
});
|
|
6106
|
-
}
|
|
6107
|
-
function createTransactionTransport(baseUrl, authenticatedFetch) {
|
|
6108
|
-
const root = `${baseUrl.replace(/\/$/, '')}/transactions/v1`;
|
|
6109
|
-
const post = async (route, body, scope) => {
|
|
6110
|
-
const response = await authenticatedFetch(`${root}/${route}`, {
|
|
6111
|
-
method: 'POST',
|
|
6112
|
-
headers: { 'content-type': 'application/json' },
|
|
6113
|
-
body: requestBody(body),
|
|
6114
|
-
}, scope, true);
|
|
6115
|
-
if (!response.ok)
|
|
6116
|
-
throw await parseError(response);
|
|
6117
|
-
return response.json();
|
|
6118
|
-
};
|
|
6119
|
-
return {
|
|
6120
|
-
async getLatestBlockhash(options = {}) {
|
|
6121
|
-
const value = await post('latest-blockhash', {
|
|
6122
|
-
commitment: options.commitment,
|
|
6123
|
-
minContextSlot: decimal(options.minContextSlot),
|
|
6124
|
-
}, 'transaction:inspect');
|
|
6125
|
-
return {
|
|
6126
|
-
blockhash: String(value.blockhash),
|
|
6127
|
-
contextSlot: bigintField(value.contextSlot, 'contextSlot'),
|
|
6128
|
-
lastValidBlockHeight: bigintField(value.lastValidBlockHeight, 'lastValidBlockHeight'),
|
|
6129
|
-
};
|
|
6130
|
-
},
|
|
6131
|
-
async getFeeForMessage(message, options = {}) {
|
|
6132
|
-
const value = await post('fee', {
|
|
6133
|
-
message,
|
|
6134
|
-
commitment: options.commitment,
|
|
6135
|
-
minContextSlot: decimal(options.minContextSlot),
|
|
6136
|
-
}, 'transaction:inspect');
|
|
6137
|
-
return {
|
|
6138
|
-
feeLamports: value.feeLamports === null ? null : bigintField(value.feeLamports, 'feeLamports'),
|
|
6139
|
-
contextSlot: bigintField(value.contextSlot, 'contextSlot'),
|
|
6140
|
-
};
|
|
6141
|
-
},
|
|
6142
|
-
async simulateTransaction(transaction, options = {}) {
|
|
6143
|
-
const value = await post('simulate', {
|
|
6144
|
-
transaction,
|
|
6145
|
-
commitment: options.commitment,
|
|
6146
|
-
minContextSlot: decimal(options.minContextSlot),
|
|
6147
|
-
accounts: options.accounts ? { addresses: options.accounts } : undefined,
|
|
6148
|
-
innerInstructions: options.innerInstructions,
|
|
6149
|
-
replaceRecentBlockhash: options.replaceRecentBlockhash,
|
|
6150
|
-
}, 'transaction:inspect');
|
|
6151
|
-
return {
|
|
6152
|
-
contextSlot: bigintField(value.contextSlot, 'contextSlot'),
|
|
6153
|
-
err: value.err ?? null,
|
|
6154
|
-
logs: value.logs ?? null,
|
|
6155
|
-
unitsConsumed: optionalBigint(value.unitsConsumed, 'unitsConsumed'),
|
|
6156
|
-
accounts: value.accounts,
|
|
6157
|
-
};
|
|
6158
|
-
},
|
|
6159
|
-
sendTransaction(transaction, options = {}) {
|
|
6160
|
-
return post('send', {
|
|
6161
|
-
transaction,
|
|
6162
|
-
skipPreflight: options.skipPreflight,
|
|
6163
|
-
preflightCommitment: options.preflightCommitment,
|
|
6164
|
-
minContextSlot: decimal(options.minContextSlot),
|
|
6165
|
-
}, 'transaction:send');
|
|
6225
|
+
throw new ReadRequestError({
|
|
6226
|
+
status: response.status,
|
|
6227
|
+
path,
|
|
6228
|
+
body,
|
|
6229
|
+
serverErrorCode: responseErrorCode(response, body),
|
|
6230
|
+
});
|
|
6231
|
+
}
|
|
6232
|
+
try {
|
|
6233
|
+
return JSON.parse(body);
|
|
6234
|
+
}
|
|
6235
|
+
catch (error) {
|
|
6236
|
+
throw new AreteError(`Program read '${path}' returned invalid JSON`, 'INVALID_RESPONSE', error);
|
|
6237
|
+
}
|
|
6238
|
+
}
|
|
6239
|
+
function hostedAuthConfig(binding, runtimeAuth) {
|
|
6240
|
+
const metadata = binding.auth;
|
|
6241
|
+
const runtimeStrategyConfigured = runtimeAuth?.token !== undefined
|
|
6242
|
+
|| runtimeAuth?.getToken !== undefined
|
|
6243
|
+
|| runtimeAuth?.tokenEndpoint !== undefined;
|
|
6244
|
+
if (runtimeStrategyConfigured)
|
|
6245
|
+
return runtimeAuth;
|
|
6246
|
+
if (metadata.required === false)
|
|
6247
|
+
return runtimeAuth;
|
|
6248
|
+
return { ...runtimeAuth, tokenEndpoint: metadata.sessionEndpoint };
|
|
6249
|
+
}
|
|
6250
|
+
function hostedAuth(config) {
|
|
6251
|
+
const auth = hostedAuthConfig(config.binding, config.auth);
|
|
6252
|
+
if (auth?.token !== undefined && !auth.token) {
|
|
6253
|
+
throw new AreteError('Authentication token is empty', 'TOKEN_INVALID');
|
|
6254
|
+
}
|
|
6255
|
+
const bindingId = config.binding.programReadBindingId;
|
|
6256
|
+
if (!auth || auth.token !== undefined) {
|
|
6257
|
+
return { staticToken: auth?.token };
|
|
6258
|
+
}
|
|
6259
|
+
return {
|
|
6260
|
+
manager: new ConnectionManager({ websocketUrl: null, auth, fetch: config.fetch }),
|
|
6261
|
+
target: {
|
|
6262
|
+
targetKind: 'program-read-binding',
|
|
6263
|
+
targetId: bindingId,
|
|
6264
|
+
programReleaseHash: config.release.programReleaseHash,
|
|
6166
6265
|
},
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6266
|
+
};
|
|
6267
|
+
}
|
|
6268
|
+
function requestPath(release, request) {
|
|
6269
|
+
const releaseHash = encodeURIComponent(release.programReleaseHash).replace(/%3A/g, ':');
|
|
6270
|
+
const root = `/v1/releases/${releaseHash}`
|
|
6271
|
+
+ `/accounts/${encodeURIComponent(request.account)}`;
|
|
6272
|
+
if (request.operation === 'fetchMany')
|
|
6273
|
+
return root;
|
|
6274
|
+
const addressPath = `${root.replace(/\/+$/, '')}/${encodeURIComponent(request.address)}`;
|
|
6275
|
+
return request.operation === 'exists' ? `${addressPath}/exists` : addressPath;
|
|
6276
|
+
}
|
|
6277
|
+
function createProgramReadTransport(config) {
|
|
6278
|
+
if (config.kind === 'unavailable') {
|
|
6279
|
+
return {
|
|
6280
|
+
async read() {
|
|
6281
|
+
throw new AreteError(config.message, 'INVALID_CONFIG');
|
|
6282
|
+
},
|
|
6283
|
+
};
|
|
6284
|
+
}
|
|
6285
|
+
const auth = config.kind === 'hosted-binding' ? hostedAuth(config) : undefined;
|
|
6286
|
+
const endpoint = config.kind === 'hosted-binding'
|
|
6287
|
+
? config.binding.endpoint
|
|
6288
|
+
: config.endpoint;
|
|
6289
|
+
return {
|
|
6290
|
+
async read(request) {
|
|
6291
|
+
const path = requestPath(config.release, request);
|
|
6292
|
+
const input = appendUrl(endpoint, path);
|
|
6293
|
+
const attempt = async (forceRefresh) => {
|
|
6294
|
+
const token = auth?.staticToken
|
|
6295
|
+
?? (auth?.manager && auth.target
|
|
6296
|
+
? await auth.manager.getHttpAuthToken(auth.target, ['read'], forceRefresh)
|
|
6297
|
+
: undefined);
|
|
6298
|
+
const headers = new Headers(request.operation === 'fetchMany'
|
|
6299
|
+
? { 'content-type': 'application/json' }
|
|
6300
|
+
: undefined);
|
|
6301
|
+
if (token)
|
|
6302
|
+
headers.set('authorization', `Bearer ${token}`);
|
|
6303
|
+
return config.fetch(input, {
|
|
6304
|
+
method: request.operation === 'fetchMany' ? 'POST' : 'GET',
|
|
6305
|
+
headers,
|
|
6306
|
+
body: request.operation === 'fetchMany'
|
|
6307
|
+
? JSON.stringify({ addresses: request.addresses })
|
|
6308
|
+
: undefined,
|
|
6309
|
+
});
|
|
6180
6310
|
};
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
const
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6311
|
+
let response = await attempt(false);
|
|
6312
|
+
let body = await response.text();
|
|
6313
|
+
const wireCode = responseErrorCode(response, body);
|
|
6314
|
+
if (response.status === 401
|
|
6315
|
+
&& wireCode
|
|
6316
|
+
&& isRefreshableErrorCode(wireCode)
|
|
6317
|
+
&& auth?.manager
|
|
6318
|
+
&& auth.target) {
|
|
6319
|
+
auth.manager.clearHttpAuthToken(auth.target);
|
|
6320
|
+
response = await attempt(true);
|
|
6321
|
+
body = await response.text();
|
|
6322
|
+
}
|
|
6323
|
+
return parseProgramReadResponse(response, path, body);
|
|
6188
6324
|
},
|
|
6189
6325
|
};
|
|
6190
6326
|
}
|
|
@@ -6204,7 +6340,7 @@ function validateReleaseIdentity(programName, definition, release) {
|
|
|
6204
6340
|
function isNonEmptyString(value) {
|
|
6205
6341
|
return typeof value === 'string' && value.trim().length > 0;
|
|
6206
6342
|
}
|
|
6207
|
-
function isSecureOrLoopbackHttpUrl
|
|
6343
|
+
function isSecureOrLoopbackHttpUrl(value) {
|
|
6208
6344
|
try {
|
|
6209
6345
|
const url = new URL(value);
|
|
6210
6346
|
return url.protocol === 'https:'
|
|
@@ -6235,12 +6371,12 @@ function validateProgramReadDescriptor(programName, descriptor) {
|
|
|
6235
6371
|
}
|
|
6236
6372
|
const { binding } = descriptor.transport;
|
|
6237
6373
|
if (!binding
|
|
6238
|
-
|| !isSecureOrLoopbackHttpUrl
|
|
6374
|
+
|| !isSecureOrLoopbackHttpUrl(binding.endpoint)
|
|
6239
6375
|
|| !/^prb_[A-Za-z0-9_-]{32}$/.test(binding.programReadBindingId)
|
|
6240
6376
|
|| !binding.auth
|
|
6241
6377
|
|| binding.auth.targetKind !== 'program-read-binding'
|
|
6242
6378
|
|| binding.auth.targetId !== binding.programReadBindingId
|
|
6243
|
-
|| !isSecureOrLoopbackHttpUrl
|
|
6379
|
+
|| !isSecureOrLoopbackHttpUrl(binding.auth.sessionEndpoint)) {
|
|
6244
6380
|
throw new AreteError(`Program '${programName}' hosted binding requires secure endpoints, a canonical binding ID, and matching program-read-binding auth metadata`, 'INVALID_CONFIG');
|
|
6245
6381
|
}
|
|
6246
6382
|
}
|
|
@@ -6402,12 +6538,19 @@ let Arete$1 = class Arete {
|
|
|
6402
6538
|
});
|
|
6403
6539
|
this._views = createTypedViews(this.stack, this.storage, this.subscriptionRegistry);
|
|
6404
6540
|
this._queries = this.buildQueries();
|
|
6405
|
-
|
|
6541
|
+
const hostedGateway = this.stack.gateway
|
|
6542
|
+
&& (options.chain === undefined || options.transactions === undefined)
|
|
6543
|
+
? createHostedSolanaGatewayTransports(this.stack.gateway, {
|
|
6544
|
+
auth: options.auth,
|
|
6545
|
+
fetch: this.fetchImpl,
|
|
6546
|
+
})
|
|
6547
|
+
: undefined;
|
|
6548
|
+
this._chain = options.chain ?? hostedGateway?.chain ?? createChainClient(this.httpBaseUrl ?? '', ((input, init) => this.authenticatedStackFetch(typeof input === 'string'
|
|
6406
6549
|
? input
|
|
6407
6550
|
: input instanceof URL
|
|
6408
6551
|
? input.toString()
|
|
6409
6552
|
: input.url, init, ['read'])));
|
|
6410
|
-
this._transactions = options.transactions ?? createTransactionTransport(this.httpBaseUrl ?? '', (input, init, scope, requirePreDispatchMarker) => this.authenticatedStackFetch(input, init, [scope], requirePreDispatchMarker));
|
|
6553
|
+
this._transactions = options.transactions ?? hostedGateway?.transactions ?? createTransactionTransport(this.httpBaseUrl ?? '', (input, init, scope, requirePreDispatchMarker) => this.authenticatedStackFetch(input, init, [scope], requirePreDispatchMarker));
|
|
6411
6554
|
this._programs = this.buildPrograms();
|
|
6412
6555
|
}
|
|
6413
6556
|
resolveFetchImpl() {
|
|
@@ -6941,6 +7084,7 @@ function programAsStack(name, program, descriptor) {
|
|
|
6941
7084
|
views: {},
|
|
6942
7085
|
programs: { [name]: program },
|
|
6943
7086
|
...(descriptor ? { programReads: { [name]: descriptor } } : {}),
|
|
7087
|
+
...(program.gateway ? { gateway: program.gateway } : {}),
|
|
6944
7088
|
};
|
|
6945
7089
|
}
|
|
6946
7090
|
async function createSession(definition, ...args) {
|
|
@@ -6950,8 +7094,16 @@ async function createSession(definition, ...args) {
|
|
|
6950
7094
|
if (stackEntries.length === 0 && programEntries.length === 0) {
|
|
6951
7095
|
throw new AreteError('createSession requires at least one stack or program member', 'INVALID_CONFIG');
|
|
6952
7096
|
}
|
|
6953
|
-
|
|
6954
|
-
|
|
7097
|
+
const generatedGateway = definition.gateway && (!options?.chain || !options?.transactions)
|
|
7098
|
+
? createHostedSolanaGatewayTransports(definition.gateway, {
|
|
7099
|
+
auth: options?.auth,
|
|
7100
|
+
fetch: options?.fetch,
|
|
7101
|
+
})
|
|
7102
|
+
: undefined;
|
|
7103
|
+
const sharedChain = options?.chain ?? generatedGateway?.chain;
|
|
7104
|
+
const sharedTransactions = options?.transactions ?? generatedGateway?.transactions;
|
|
7105
|
+
if (definition.mode === 'composition' && (!sharedChain || !sharedTransactions)) {
|
|
7106
|
+
throw new AreteError('composition sessions require generated or explicit chain and transaction transports', 'INVALID_CONFIG');
|
|
6955
7107
|
}
|
|
6956
7108
|
if (definition.mode === 'composition' && options?.endpoints !== undefined) {
|
|
6957
7109
|
throw new AreteError('composition sessions require per-member live endpoints, not shared fallback endpoints', 'INVALID_CONFIG');
|
|
@@ -6978,7 +7130,11 @@ async function createSession(definition, ...args) {
|
|
|
6978
7130
|
const connectedStacks = await Promise.all(stackEntries.map(async ([key, stack]) => {
|
|
6979
7131
|
const memberOptions = options?.stacks?.[key];
|
|
6980
7132
|
const effectiveStack = withPrograms(stack, memberOptions?.programs);
|
|
6981
|
-
const connectOptions = resolveMemberConnectOptions(effectiveStack, memberOptions,
|
|
7133
|
+
const connectOptions = resolveMemberConnectOptions(effectiveStack, memberOptions, {
|
|
7134
|
+
...options,
|
|
7135
|
+
chain: sharedChain,
|
|
7136
|
+
transactions: sharedTransactions,
|
|
7137
|
+
}, false);
|
|
6982
7138
|
const client = await Arete$1.connect(stack, {
|
|
6983
7139
|
...connectOptions,
|
|
6984
7140
|
programs: memberOptions?.programs,
|
|
@@ -6988,7 +7144,11 @@ async function createSession(definition, ...args) {
|
|
|
6988
7144
|
}));
|
|
6989
7145
|
const connectedPrograms = await Promise.all(programEntries.map(async ([key, program]) => {
|
|
6990
7146
|
const syntheticStack = programAsStack(key, program, definition.programReads?.[key]);
|
|
6991
|
-
const connectOptions = resolveMemberConnectOptions(syntheticStack, options?.programs?.[key],
|
|
7147
|
+
const connectOptions = resolveMemberConnectOptions(syntheticStack, options?.programs?.[key], {
|
|
7148
|
+
...options,
|
|
7149
|
+
chain: sharedChain,
|
|
7150
|
+
transactions: sharedTransactions,
|
|
7151
|
+
}, true);
|
|
6992
7152
|
const client = await Arete$1.connect(syntheticStack, connectOptions);
|
|
6993
7153
|
return [key, client];
|
|
6994
7154
|
}));
|
|
@@ -6997,7 +7157,7 @@ async function createSession(definition, ...args) {
|
|
|
6997
7157
|
...connectedPrograms.map(([, client]) => client),
|
|
6998
7158
|
];
|
|
6999
7159
|
const executionHost = memberClients[0];
|
|
7000
|
-
const transactions =
|
|
7160
|
+
const transactions = sharedTransactions ?? executionHost.transactions;
|
|
7001
7161
|
const stacks = Object.fromEntries(connectedStacks);
|
|
7002
7162
|
const explicitProgramKeys = new Set(connectedPrograms.map(([key]) => key));
|
|
7003
7163
|
const programOwners = new Map();
|
|
@@ -7026,7 +7186,7 @@ async function createSession(definition, ...args) {
|
|
|
7026
7186
|
}
|
|
7027
7187
|
}
|
|
7028
7188
|
const programs = promotedPrograms;
|
|
7029
|
-
const chain =
|
|
7189
|
+
const chain = sharedChain ?? (options?.endpoints?.http !== undefined
|
|
7030
7190
|
? createChainClient(options.endpoints.http, (options?.fetch ?? globalThis.fetch?.bind(globalThis)))
|
|
7031
7191
|
: executionHost.chain);
|
|
7032
7192
|
const session = {
|
|
@@ -7103,142 +7263,6 @@ function flowOperation(prepare) {
|
|
|
7103
7263
|
};
|
|
7104
7264
|
}
|
|
7105
7265
|
|
|
7106
|
-
function isSecureOrLoopbackHttpUrl(value) {
|
|
7107
|
-
try {
|
|
7108
|
-
const url = new URL(value);
|
|
7109
|
-
return url.protocol === 'https:'
|
|
7110
|
-
|| (url.protocol === 'http:'
|
|
7111
|
-
&& ['localhost', '127.0.0.1', '::1'].includes(url.hostname));
|
|
7112
|
-
}
|
|
7113
|
-
catch {
|
|
7114
|
-
return false;
|
|
7115
|
-
}
|
|
7116
|
-
}
|
|
7117
|
-
function validateBinding(capability, binding, requiredScopes) {
|
|
7118
|
-
const auth = binding?.auth;
|
|
7119
|
-
const complete = binding
|
|
7120
|
-
&& isSecureOrLoopbackHttpUrl(binding.endpoint)
|
|
7121
|
-
&& /^sgb_[A-Za-z0-9_-]{32}$/.test(binding.solanaGatewayBindingId)
|
|
7122
|
-
&& binding.cluster.trim().length > 0
|
|
7123
|
-
&& binding.region.trim().length > 0
|
|
7124
|
-
&& auth
|
|
7125
|
-
&& typeof auth.required === 'boolean'
|
|
7126
|
-
&& auth.mode === binding.authPolicy
|
|
7127
|
-
&& isSecureOrLoopbackHttpUrl(auth.sessionEndpoint)
|
|
7128
|
-
&& isSecureOrLoopbackHttpUrl(auth.jwksUrl)
|
|
7129
|
-
&& auth.tokenTransport === 'bearer'
|
|
7130
|
-
&& auth.audience === 'arete:solana-gateway'
|
|
7131
|
-
&& auth.targetKind === 'solana-gateway-binding'
|
|
7132
|
-
&& auth.targetId === binding.solanaGatewayBindingId
|
|
7133
|
-
&& Array.isArray(auth.scopes)
|
|
7134
|
-
&& requiredScopes.every((scope) => auth.scopes.includes(scope))
|
|
7135
|
-
&& Array.isArray(auth.acceptedKeyClasses)
|
|
7136
|
-
&& typeof auth.transactionEntitlementRequired === 'boolean';
|
|
7137
|
-
if (!complete) {
|
|
7138
|
-
throw new AreteError(`Hosted Solana gateway ${capability} binding is incomplete or inconsistent`, 'INVALID_CONFIG');
|
|
7139
|
-
}
|
|
7140
|
-
}
|
|
7141
|
-
function resolveFetch(fetchImpl) {
|
|
7142
|
-
if (fetchImpl)
|
|
7143
|
-
return fetchImpl;
|
|
7144
|
-
if (typeof globalThis.fetch === 'function')
|
|
7145
|
-
return globalThis.fetch.bind(globalThis);
|
|
7146
|
-
throw new AreteError('A fetch implementation is required for hosted Solana gateway transports', 'INVALID_CONFIG');
|
|
7147
|
-
}
|
|
7148
|
-
function hasRuntimeAuthStrategy(auth) {
|
|
7149
|
-
return auth?.token !== undefined
|
|
7150
|
-
|| auth?.getToken !== undefined
|
|
7151
|
-
|| auth?.tokenEndpoint !== undefined;
|
|
7152
|
-
}
|
|
7153
|
-
function bindingAuthConfig(binding, runtimeAuth) {
|
|
7154
|
-
if (hasRuntimeAuthStrategy(runtimeAuth))
|
|
7155
|
-
return runtimeAuth;
|
|
7156
|
-
if (!binding.auth.required)
|
|
7157
|
-
return runtimeAuth;
|
|
7158
|
-
return { ...runtimeAuth, tokenEndpoint: binding.auth.sessionEndpoint };
|
|
7159
|
-
}
|
|
7160
|
-
function refreshableErrorCode(response) {
|
|
7161
|
-
const wireCode = response.headers.get('X-Error-Code');
|
|
7162
|
-
return wireCode !== null
|
|
7163
|
-
&& shouldRefreshToken(parseErrorCode(wireCode.trim().replace(/_/g, '-')));
|
|
7164
|
-
}
|
|
7165
|
-
/**
|
|
7166
|
-
* Construct explicit hosted chain and transaction transports from generated
|
|
7167
|
-
* gateway descriptors. Tokens are isolated by exact binding target and scope.
|
|
7168
|
-
*/
|
|
7169
|
-
function createHostedSolanaGatewayTransports(bindings, options = {}) {
|
|
7170
|
-
validateBinding('chain', bindings.chain, ['read']);
|
|
7171
|
-
validateBinding('transactions', bindings.transactions, ['transaction:inspect', 'transaction:send']);
|
|
7172
|
-
const fetchImpl = resolveFetch(options.fetch);
|
|
7173
|
-
const managers = new Map();
|
|
7174
|
-
const bindingAuthentication = (binding) => {
|
|
7175
|
-
const auth = bindingAuthConfig(binding, options.auth);
|
|
7176
|
-
if (auth?.token !== undefined) {
|
|
7177
|
-
if (!auth.token) {
|
|
7178
|
-
throw new AreteError('Authentication token is empty', 'TOKEN_INVALID');
|
|
7179
|
-
}
|
|
7180
|
-
return {
|
|
7181
|
-
staticToken: auth.token,
|
|
7182
|
-
target: {
|
|
7183
|
-
targetKind: 'solana-gateway-binding',
|
|
7184
|
-
targetId: binding.solanaGatewayBindingId,
|
|
7185
|
-
},
|
|
7186
|
-
};
|
|
7187
|
-
}
|
|
7188
|
-
let manager;
|
|
7189
|
-
if (auth && (auth.getToken || auth.tokenEndpoint)) {
|
|
7190
|
-
const identity = hasRuntimeAuthStrategy(options.auth)
|
|
7191
|
-
? 'runtime-auth-strategy'
|
|
7192
|
-
: `session-endpoint:${binding.auth.sessionEndpoint}`;
|
|
7193
|
-
manager = managers.get(identity);
|
|
7194
|
-
if (!manager) {
|
|
7195
|
-
manager = new ConnectionManager({ websocketUrl: null, auth, fetch: fetchImpl });
|
|
7196
|
-
managers.set(identity, manager);
|
|
7197
|
-
}
|
|
7198
|
-
}
|
|
7199
|
-
return {
|
|
7200
|
-
manager,
|
|
7201
|
-
target: {
|
|
7202
|
-
targetKind: 'solana-gateway-binding',
|
|
7203
|
-
targetId: binding.solanaGatewayBindingId,
|
|
7204
|
-
},
|
|
7205
|
-
};
|
|
7206
|
-
};
|
|
7207
|
-
const authenticatedFetch = (authentication) => async (input, init, scope, requirePreDispatchMarker) => {
|
|
7208
|
-
const request = { ...authentication.target, scopes: [scope] };
|
|
7209
|
-
const attempt = async (forceRefresh) => {
|
|
7210
|
-
const token = authentication.staticToken
|
|
7211
|
-
?? (authentication.manager
|
|
7212
|
-
? await authentication.manager.getHttpAuthToken(request, forceRefresh)
|
|
7213
|
-
: undefined);
|
|
7214
|
-
const headers = new Headers(init?.headers);
|
|
7215
|
-
if (token)
|
|
7216
|
-
headers.set('authorization', `Bearer ${token}`);
|
|
7217
|
-
return fetchImpl(input, { ...init, headers });
|
|
7218
|
-
};
|
|
7219
|
-
let response = await attempt(false);
|
|
7220
|
-
const explicitlyNotDispatched = response.headers.get('X-Arete-Upstream-Attempted') === 'false';
|
|
7221
|
-
if (!response.ok
|
|
7222
|
-
&& authentication.manager
|
|
7223
|
-
&& refreshableErrorCode(response)
|
|
7224
|
-
&& (!requirePreDispatchMarker || explicitlyNotDispatched)) {
|
|
7225
|
-
authentication.manager.clearHttpAuthToken(request);
|
|
7226
|
-
response = await attempt(true);
|
|
7227
|
-
}
|
|
7228
|
-
return response;
|
|
7229
|
-
};
|
|
7230
|
-
const chainAuthentication = bindingAuthentication(bindings.chain);
|
|
7231
|
-
const transactionAuthentication = bindingAuthentication(bindings.transactions);
|
|
7232
|
-
const chainFetch = authenticatedFetch(chainAuthentication);
|
|
7233
|
-
const transactionFetch = authenticatedFetch(transactionAuthentication);
|
|
7234
|
-
return {
|
|
7235
|
-
chain: createChainClient(bindings.chain.endpoint, ((input, init) => chainFetch(typeof input === 'string'
|
|
7236
|
-
? input
|
|
7237
|
-
: input instanceof URL ? input.toString() : input.url, init, 'read', false))),
|
|
7238
|
-
transactions: createTransactionTransport(bindings.transactions.endpoint, (input, init, scope, requirePreDispatchMarker) => transactionFetch(input, init, scope, requirePreDispatchMarker)),
|
|
7239
|
-
};
|
|
7240
|
-
}
|
|
7241
|
-
|
|
7242
7266
|
function chainAccountLoader(chain) {
|
|
7243
7267
|
return {
|
|
7244
7268
|
async getAccount(address) {
|