Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/mighty-cooks-glow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@thirdweb-dev/sdk": patch
---

add a new StaticJsonRpcBatchProvider to stop calling `eth_getChainId` when possible
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"eslint": "^8.21.0",
"postinstall-postinstall": "^2.1.0",
"prettier": "^2.7.1",
"turbo": "latest"
"turbo": "^1.4.6"
},
"manypkg": {
"defaultBranch": "main"
Expand Down
8 changes: 7 additions & 1 deletion packages/sdk/src/constants/urls.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { SignerOrProvider } from "../core/types";
import { StaticJsonRpcBatchProvider } from "../lib/static-batch-rpc";
import { ethers, providers } from "ethers";

/**
Expand Down Expand Up @@ -134,7 +135,12 @@ export function getReadOnlyProvider(network: string, chainId?: number) {
if (match) {
switch (match[1]) {
case "http":
return new providers.JsonRpcBatchProvider(network, chainId);
return chainId
? // if we know the chainId we should use the StaticJsonRpcBatchProvider
new StaticJsonRpcBatchProvider(network, chainId)
: // otherwise fall back to the built in json rpc batch provider
new providers.JsonRpcBatchProvider(network, chainId);

case "ws":
return new providers.WebSocketProvider(network, chainId);
default:
Expand Down
14 changes: 14 additions & 0 deletions packages/sdk/src/core/classes/contract-publisher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
fetchPreDeployMetadata,
fetchRawPredeployMetadata,
fetchSourceFilesFromMetadata,
resolveContractUriFromAddress,
} from "../../common/feature-detection";
import { isIncrementalVersion } from "../../common/version-checker";
import { getContractPublisherAddress } from "../../constants";
Expand Down Expand Up @@ -190,6 +191,19 @@ export class ContractPublisher extends RPCConnectionHandler {
);
}

/**
* @internal
* TODO clean this up (see method above, too)
*/
public async resolveReleasesFromAddress(address: string) {
const contractUri = await resolveContractUriFromAddress(
address,
this.getProvider(),
);
invariant(contractUri, "Could not resolve contract URI from address");
return await this.resolvePublishMetadataFromCompilerMetadata(contractUri);
}

/**
* @internal
* @param address
Expand Down
99 changes: 99 additions & 0 deletions packages/sdk/src/lib/static-batch-rpc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { providers, utils } from "ethers";


// mostly copied from ethers.js directly but make it a StaticJsonRpcProvider
export class StaticJsonRpcBatchProvider extends providers.StaticJsonRpcProvider {
_pendingBatchAggregator: NodeJS.Timer | null;
_pendingBatch: Array<{
request: { method: string, params: Array<any>, id: number, jsonrpc: "2.0" },
resolve: (result: any) => void,
reject: (error: Error) => void
}> | null;

constructor(url: string | utils.ConnectionInfo | undefined, network: providers.Networkish | undefined){
super(url, network);
this._pendingBatchAggregator = null;
this._pendingBatch = null;
}

send(method: string, params: Array<any>): Promise<any> {
const request = {
method: method,
params: params,
id: (this._nextId++),
jsonrpc: "2.0"
};

if (this._pendingBatch === null) {
this._pendingBatch = [ ];
}

const inflightRequest: any = { request, resolve: null, reject: null };

const promise = new Promise((resolve, reject) => {
inflightRequest.resolve = resolve;
inflightRequest.reject = reject;
});

this._pendingBatch.push(inflightRequest);

if (!this._pendingBatchAggregator) {
// Schedule batch for next event loop + short duration
this._pendingBatchAggregator = setTimeout(() => {

// Get the current batch and clear it, so new requests
// go into the next batch
const batch = this._pendingBatch || [];
this._pendingBatch = null;
this._pendingBatchAggregator = null;

// Get the request as an array of requests
const request_ = batch.map((inflight) => inflight.request);

this.emit("debug", {
action: "requestBatch",
request: utils.deepCopy(request),
provider: this
});

return utils.fetchJson(this.connection, JSON.stringify(request_)).then((result) => {
this.emit("debug", {
action: "response",
request: request_,
response: result,
provider: this
});

// For each result, feed it to the correct Promise, depending
// on whether it was a success or error
batch.forEach((inflightRequest_, index) => {
const payload = result[index];
if (payload.error) {
const error = new Error(payload.error.message);
(<any>error).code = payload.error.code;
(<any>error).data = payload.error.data;
inflightRequest_.reject(error);
} else {
inflightRequest_.resolve(payload.result);
}
});

}, (error) => {
this.emit("debug", {
action: "response",
error: error,
request: request,
provider: this
});

batch.forEach((inflightRequest_) => {
inflightRequest_.reject(error);
});
});

}, 10);
}

return promise;
}
}
Loading