viem
Version:
209 lines • 9.83 kB
JavaScript
import { BaseError } from '../errors/base.js';
import { HttpRequestError, } from '../errors/request.js';
import { AtomicityNotSupportedError, AtomicReadyWalletRejectedUpgradeError, BundleTooLargeError, ChainDisconnectedError, DuplicateIdError, InternalRpcError, InvalidInputRpcError, InvalidParamsRpcError, InvalidRequestRpcError, JsonRpcVersionUnsupportedError, LimitExceededRpcError, MethodNotFoundRpcError, MethodNotSupportedRpcError, ParseRpcError, ProviderDisconnectedError, ResourceNotFoundRpcError, ResourceUnavailableRpcError, SwitchChainError, TransactionRejectedRpcError, UnauthorizedProviderError, UnknownBundleIdError, UnknownRpcError, UnsupportedChainIdError, UnsupportedNonOptionalCapabilityError, UnsupportedProviderMethodError, UserRejectedRequestError, WalletConnectSessionSettlementError, } from '../errors/rpc.js';
import { getAbortError, isAbortError, } from '../errors/utils.js';
import { withDedupe } from './promise/withDedupe.js';
import { withRetry } from './promise/withRetry.js';
import { stringify } from './stringify.js';
export function buildRequest(request, options = {}) {
return async (args, overrideOptions = {}) => {
const { dedupe = false, methods, retryDelay = 150, retryCount = 3, signal, uid, } = {
...options,
...overrideOptions,
};
const { method } = args;
if (methods?.exclude?.includes(method))
throw new MethodNotSupportedRpcError(new Error('method not supported'), {
method,
});
if (methods?.include && !methods.include.includes(method))
throw new MethodNotSupportedRpcError(new Error('method not supported'), {
method,
});
if (signal?.aborted)
throw getAbortError(signal);
const requestId = dedupe
? hashString(`${uid}.${stringify(args)}`)
: undefined;
return withDedupe(() => withRetry(async () => {
try {
return await request(args, signal ? { signal } : undefined);
}
catch (err_) {
if (signal?.aborted)
throw getAbortError(signal);
if (isAbortError(err_))
throw err_;
const err = err_;
switch (err.code) {
// -32700
case ParseRpcError.code:
throw new ParseRpcError(err);
// -32600
case InvalidRequestRpcError.code:
throw new InvalidRequestRpcError(err);
// -32601
case MethodNotFoundRpcError.code:
throw new MethodNotFoundRpcError(err, { method: args.method });
// -32602
case InvalidParamsRpcError.code:
throw new InvalidParamsRpcError(err);
// -32603
case InternalRpcError.code:
throw new InternalRpcError(err);
// -32000
case InvalidInputRpcError.code:
throw new InvalidInputRpcError(err);
// -32001
case ResourceNotFoundRpcError.code:
throw new ResourceNotFoundRpcError(err);
// -32002
case ResourceUnavailableRpcError.code:
throw new ResourceUnavailableRpcError(err);
// -32003
case TransactionRejectedRpcError.code:
throw new TransactionRejectedRpcError(err);
// -32004
case MethodNotSupportedRpcError.code:
throw new MethodNotSupportedRpcError(err, {
method: args.method,
});
// -32005
case LimitExceededRpcError.code:
throw new LimitExceededRpcError(err);
// -32006
case JsonRpcVersionUnsupportedError.code:
throw new JsonRpcVersionUnsupportedError(err);
// 4001
case UserRejectedRequestError.code:
throw new UserRejectedRequestError(err);
// 4100
case UnauthorizedProviderError.code:
throw new UnauthorizedProviderError(err);
// 4200
case UnsupportedProviderMethodError.code:
throw new UnsupportedProviderMethodError(err);
// 4900
case ProviderDisconnectedError.code:
throw new ProviderDisconnectedError(err);
// 4901
case ChainDisconnectedError.code:
throw new ChainDisconnectedError(err);
// 4902
case SwitchChainError.code:
throw new SwitchChainError(err);
// 5700
case UnsupportedNonOptionalCapabilityError.code:
throw new UnsupportedNonOptionalCapabilityError(err);
// 5710
case UnsupportedChainIdError.code:
throw new UnsupportedChainIdError(err);
// 5720
case DuplicateIdError.code:
throw new DuplicateIdError(err);
// 5730
case UnknownBundleIdError.code:
throw new UnknownBundleIdError(err);
// 5740
case BundleTooLargeError.code:
throw new BundleTooLargeError(err);
// 5750
case AtomicReadyWalletRejectedUpgradeError.code:
throw new AtomicReadyWalletRejectedUpgradeError(err);
// 5760
case AtomicityNotSupportedError.code:
throw new AtomicityNotSupportedError(err);
// CAIP-25: User Rejected Error
// https://docs.walletconnect.com/2.0/specs/clients/sign/error-codes#rejected-caip-25
case 5000:
throw new UserRejectedRequestError(err);
// WalletConnect: Session Settlement Failed
// https://docs.walletconnect.com/2.0/specs/clients/sign/error-codes
case WalletConnectSessionSettlementError.code:
throw new WalletConnectSessionSettlementError(err);
default:
if (err_ instanceof BaseError)
throw err_;
throw new UnknownRpcError(err);
}
}
}, {
delay: ({ count, error }) => {
// If we find a Retry-After header, let's retry after the given time.
if (error && error instanceof HttpRequestError) {
const retryAfter = error?.headers?.get('Retry-After');
if (retryAfter?.match(/\d/))
return Number.parseInt(retryAfter, 10) * 1000;
}
// Otherwise, let's retry with an exponential backoff.
return ~~(1 << count) * retryDelay;
},
retryCount,
signal,
shouldRetry: ({ error }) => shouldRetry(error),
}), { enabled: dedupe, id: requestId });
};
}
/** @internal */
export function shouldRetry(error) {
if (isAbortError(error))
return false;
if ('code' in error && typeof error.code === 'number') {
if (error.code === -1)
return true; // Unknown error
if (error.code === LimitExceededRpcError.code)
return true;
if (error.code === InternalRpcError.code)
return true;
// Too Many Requests — some providers (e.g. Alchemy in batch mode) return
// HTTP 200 with a JSON-RPC body of `{ code: 429 }` instead of an HTTP 429,
// so we need to handle this code in addition to the HTTP status check below.
if (error.code === 429)
return true;
return false;
}
if (error instanceof HttpRequestError && error.status) {
// Forbidden
if (error.status === 403)
return true;
// Request Timeout
if (error.status === 408)
return true;
// Request Entity Too Large
if (error.status === 413)
return true;
// Too Many Requests
if (error.status === 429)
return true;
// Internal Server Error
if (error.status === 500)
return true;
// Bad Gateway
if (error.status === 502)
return true;
// Service Unavailable
if (error.status === 503)
return true;
// Gateway Timeout
if (error.status === 504)
return true;
return false;
}
return true;
}
/** @internal cyrb53 – fast, non-cryptographic 53-bit string hash */
function hashString(str, seed = 0) {
let h1 = 0xdeadbeef ^ seed;
let h2 = 0x41c6ce57 ^ seed;
for (let i = 0; i < str.length; i++) {
const ch = str.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
}
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
h1 ^= Math.imul(h2 ^ (h2 >>> 16), 3266489909);
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
h2 ^= Math.imul(h1 ^ (h1 >>> 16), 3266489909);
return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(36);
}
//# sourceMappingURL=buildRequest.js.map