Skip to content
Open
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
24 changes: 23 additions & 1 deletion src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,32 @@ interface ClientConfig {

const getCustomTransportConfig = (config: ClientConfig, chainConfig: GenLayerChain) => {
const isAddress = typeof config.account !== "object";
const providerMethods = new Set<string>([
"eth_chainId",
"eth_accounts",
"eth_requestAccounts",
"eth_sendTransaction",
"eth_sign",
"eth_signTypedData",
"eth_signTypedData_v4",
"personal_sign",
"wallet_addEthereumChain",
"wallet_getPermissions",
"wallet_requestPermissions",
"wallet_switchEthereumChain",
]);

return {
async request({method, params = []}: {method: string; params: any[]}) {
if (method.startsWith("eth_") && isAddress) {
const shouldUseProvider =
isAddress &&
(
providerMethods.has(method) ||
method.startsWith("wallet_") ||
method.endsWith("_signTypedData") ||
method.endsWith("_signTypedData_v4")
);
if (shouldUseProvider) {
const provider = config.provider || (typeof window !== "undefined" ? window.ethereum : undefined);
if (provider) {
try {
Expand Down
122 changes: 122 additions & 0 deletions tests/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,125 @@
]);
});
});

describe("Provider routing", () => {
beforeEach(() => {
mockFetch.mockReset();

mockFetch.mockImplementation(async (_url, options) => {
const bodyString = typeof options?.body === "string" ? options.body : "{}";
const body = JSON.parse(bodyString) as {method?: string};

if (body.method === "sim_getConsensusContract") {
return {
ok: true,
json: async () => ({
result: {
address: "0x0000000000000000000000000000000000000001",
abi: [],
},
}),
};
}

if (body.method === "eth_getTransactionByHash") {
return {
ok: true,
json: async () => ({
result: {
hash: "0x" + "11".repeat(32),
status: "FINALIZED",
from_address: "0x" + "22".repeat(20),
to_address: "0x" + "33".repeat(20),
type: 2,
nonce: 0,
value: 0,
gaslimit: 0,
r: 0,
s: 0,
v: 0,
created_at: new Date(0).toISOString(),
data: {calldata: ""},
consensus_data: {
leader_receipt: [{execution_result: "SUCCESS"}],
validators: [],
votes: {},
},
},
}),
};
}

return {
ok: true,
json: async () => ({result: null}),
};
});
});

it("routes eth_sendTransaction via provider for injected accounts", async () => {
const providerRequest = vi.fn().mockResolvedValue("0x" + "aa".repeat(32));

const client = createClient({
chain: localnet,
account: "0x65e03a3e916CF1dC92d3C8E8186a89CfAB0D2bc2",
provider: {request: providerRequest} as any,
});

const txHash = await client.request({
method: "eth_sendTransaction",
params: [{from: "0x65e03a3e916CF1dC92d3C8E8186a89CfAB0D2bc2"}],
});

expect(txHash).toBe("0x" + "aa".repeat(32));
expect(providerRequest).toHaveBeenCalledWith({
method: "eth_sendTransaction",
params: [{from: "0x65e03a3e916CF1dC92d3C8E8186a89CfAB0D2bc2"}],
});
});

it("routes eth_getTransactionByHash to RPC fetch (not provider)", async () => {
const providerRequest = vi.fn().mockResolvedValue(null);

const client = createClient({
chain: localnet,
account: "0x65e03a3e916CF1dC92d3C8E8186a89CfAB0D2bc2",
provider: {request: providerRequest} as any,
});

const transaction = await client.request({

Check failure on line 284 in tests/client.test.ts

View workflow job for this annotation

GitHub Actions / test

Unhandled error

TypeCheckError: No overload matches this call. Overload 1 of 2, '(args { method "web3_clientVersion"; params? undefined; } | { method "web3_sha3"; params [data `0x${string}`]; } | { method "net_listening"; params? undefined; } | { method "net_peerCount"; params? undefined; } | ... 71 more ... | { ...; }, options? EIP1193RequestOptions | undefined) Promise<...>', gave the following error. Argument of type '{ method "eth_getTransactionByHash"; params [string]; }' is not assignable to parameter of type '{ method "web3_clientVersion"; params? undefined; } | { method "web3_sha3"; params [data `0x${string}`]; } | { method "net_listening"; params? undefined; } | { method "net_peerCount"; params? undefined; } | ... 71 more ... | { ...; }'. Types of property 'params' are incompatible. Type '[string]' is not assignable to type '[hash `0x${string}`]'. Type 'string' is not assignable to type '`0x${string}`'. Overload 2 of 2, '(args { method "sim_fundAccount"; params [address `0x${string}`, amount number]; } | { method "eth_getTransactionByHash"; params [hash Hash]; } | { method "eth_call"; params [requestParams any, blockNumberOrHash string]; } | { method "eth_sendRawTransaction"; params [signedTransaction ...]; } | ... 6 more ... | { ...; }) Promise<...>', gave the following error. Argument of type '{ method "eth_getTransactionByHash"; params [string]; }' is not assignable to parameter of type '{ method "sim_fundAccount"; params [address `0x${string}`, amount number]; } | { method "eth_getTransactionByHash"; params [hash Hash]; } | { method "eth_call"; params [requestParams any, blockNumberOrHash string]; } | { method "eth_sendRawTransaction"; params [signedTransaction ...]; } | ... 6 more .....'. Types of property 'params' are incompatible. Type '[string]' is not assignable to type '[hash Hash]'. Type 'string' is not assignable to type 'Hash'. Type 'string' is not assignable to type '`0x${string}`'. ❯ tests/client.test.ts:284:46
method: "eth_getTransactionByHash",
params: ["0x" + "11".repeat(32)],
});

expect(transaction).toMatchObject({
hash: "0x" + "11".repeat(32),
status: "FINALIZED",
});
expect(providerRequest).not.toHaveBeenCalled();
expect(
mockFetch.mock.calls.some(call => {
const body = JSON.parse(call[1]?.body as string) as {method?: string};
return body.method === "eth_getTransactionByHash";
}),
).toBe(true);
});

it("routes eth_chainId via provider for injected accounts", async () => {
const providerRequest = vi.fn().mockResolvedValue("0xf22f");

const client = createClient({
chain: localnet,
account: "0x65e03a3e916CF1dC92d3C8E8186a89CfAB0D2bc2",
provider: {request: providerRequest} as any,
});

const chainId = await client.request({method: "eth_chainId"});

expect(chainId).toBe("0xf22f");
expect(providerRequest).toHaveBeenCalledWith({
method: "eth_chainId",
params: [],
});
});
});
Loading