-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Improved flexibility of FetchMiddleware for Web3.js #22433
Conversation
@arguiot if i update connection.ts according to your code my problem will be solved |
Yes, I think it would solve the problem for sure. I hope the Solana team will review this PR quickly so we can both get our projects running 😄! |
@arguiot Thank you this problem took me a long time these past few days |
@arguiot Can you explain to me why when i import solana/web3js my system fails to getBalance of token ? |
|
You can remove the For the getBalance I don't know, what errors are you getting? |
@arguiot can i fork this to use in my project ? |
Of course!! That's open source |
@arguiot i fork solana/web3js and fix the code connect.ts and build file to 1 new lib folder then i replace tell original lib folder in my project with new folder and it still doesn't work |
Just to make sure, did you also change the fetch middleware? Because this way you can use a function that is supported on your platform |
Any update on this? would love to use Solana web3.js with CF workers 😃 |
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. |
Please don't close this - I'd love to be able to use this on CF Workers! |
I managed to make this run of CF workers! Just a bit of hacking, but it works well. Here's a sample code to connect without import * as web3 from "@solana/web3.js"
import RpcClient from 'jayson/lib/client/browser';
type RpcParams = {
methodName: string;
args: Array<any>;
};
type RpcRequest = (methodName: string, args: Array<any>) => any;
type RpcBatchRequest = (requests: RpcParams[]) => any;
function createRpcClient(
url: string,
): RpcClient {
const clientBrowser = new RpcClient(async (request, callback) => {
const options = {
method: 'POST',
body: request,
headers: Object.assign({
'Content-Type': 'application/json',
}),
};
try {
let too_many_requests_retries = 5;
let res: Response;
let waitTime = 500;
for (;;) {
res = await fetch(url, options);
if (res.status !== 429 /* Too many requests */ ) {
break;
}
too_many_requests_retries -= 1;
if (too_many_requests_retries === 0) {
break;
}
console.log(
`Server responded with ${res.status} ${res.statusText}. Retrying after ${waitTime}ms delay...`,
);
// await sleep(waitTime);
waitTime *= 2;
}
const text = await res.text();
if (res.ok) {
callback(null, text);
} else {
callback(new Error(`${res.status} ${res.statusText}: ${text}`));
}
} catch (err) {
if (err instanceof Error) callback(err);
}
}, {});
return clientBrowser;
}
function createRpcRequest(client: RpcClient): RpcRequest {
return (method, args) => {
return new Promise((resolve, reject) => {
client.request(method, args, (err: any, response: any) => {
if (err) {
reject(err);
return;
}
resolve(response);
});
});
};
}
function createRpcBatchRequest(client: RpcClient): RpcBatchRequest {
return (requests: RpcParams[]) => {
return new Promise((resolve, reject) => {
// Do nothing if requests is empty
if (requests.length === 0) resolve([]);
const batch = requests.map((params: RpcParams) => {
return client.request(params.methodName, params.args);
});
client.request(batch, (err: any, response: any) => {
if (err) {
reject(err);
return;
}
resolve(response);
});
});
};
}
export default async function Connect(): Promise < web3.Connection > {
const mainnet = (global as any).ENVIRONMENT === 'production';
const endpoint = web3.clusterApiUrl(mainnet ? 'mainnet-beta' : 'devnet')
// Connect to cluster
const connection = new web3.Connection(endpoint, {
commitment: 'confirmed'
});
// Override the default RPC client to use the Workers's fetch
const client = createRpcClient(endpoint);
const request = createRpcRequest(client);
const batchRequest = createRpcBatchRequest(client);
// assign connection._rpcClient to the new client using Object.assign
Object.assign(connection, {
_rpcClient: client,
_rpcRequest: request,
_rpcBatchRequest: batchRequest,
});
return connection
} The main idea is to use |
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. |
Support overriding the fetch function was added in #24367 and released in https://github.com/solana-labs/solana-web3.js/releases/tag/v1.39.0 Is there any more flexibility that you'd like added beyond that? |
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. |
Closing because I think that overriding the fetch function is probably sufficient here. @arguiot please let us know if you still need this |
Problem
Currently, the fetch client is
cross-fetch
which is fine in most situations, but in some cases, it can be very problematic. Personally, I'm trying to use web3.js on Cloudflare Workers which has a custom fetch client. In other cases, like React Native, the library may also not work, see: #22421My point is that there should be an option to use another fetch function.
Proposed Solution
Currently, there is a Middleware option in the Connection constructor. The issue with this middleware is that it's very limited as we have to use the provided
fetch
function as a callback. I propose to have aFetchMiddleware
function like that:It really shouldn't be hard to implement it. I can probably do it, but I prefer to wait for feedback.
Summary of changes
connection.ts
Fixes #22429