-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
31720c3
commit ef072a8
Showing
7 changed files
with
266 additions
and
7 deletions.
There are no files selected for viewing
32 changes: 32 additions & 0 deletions
32
...in-plugins/network-manager/request-handlers/handlers/accounts/automatic-sender-handler.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { assertHardhatInvariant } from "@ignored/hardhat-vnext-errors"; | ||
|
||
import { SenderHandler } from "./sender.js"; | ||
|
||
/** | ||
* This class automatically retrieves and caches the first available account from the connected provider. | ||
* It overrides the getSender method of the base class to request the list of accounts if the first account has not been fetched yet, | ||
* ensuring dynamic selection of the sender for all JSON-RPC requests without requiring manual input. | ||
*/ | ||
export class AutomaticSenderHandler extends SenderHandler { | ||
#alreadyFetchedAccounts = false; | ||
#firstAccount: string | undefined; | ||
|
||
protected async getSender(): Promise<string | undefined> { | ||
if (this.#alreadyFetchedAccounts === false) { | ||
const accounts = await this.provider.request({ | ||
method: "eth_accounts", | ||
}); | ||
|
||
// TODO: This shouldn't be an exception but a failed JSON response! | ||
assertHardhatInvariant( | ||
Array.isArray(accounts), | ||
"eth_accounts response should be an array", | ||
); | ||
|
||
this.#firstAccount = accounts[0]; | ||
this.#alreadyFetchedAccounts = true; | ||
} | ||
|
||
return this.#firstAccount; | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
...uiltin-plugins/network-manager/request-handlers/handlers/accounts/fixed-sender-handler.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import type { EthereumProvider } from "../../../../../../types/providers.js"; | ||
|
||
import { SenderHandler } from "./sender.js"; | ||
|
||
/** | ||
* This class provides a fixed sender address for transactions. | ||
* It overrides the getSender method of the base class to always return the sender address specified during instantiation, | ||
* ensuring that all JSON-RPC requests use this fixed sender. | ||
*/ | ||
export class FixedSenderHandler extends SenderHandler { | ||
readonly #sender: string; | ||
|
||
constructor(provider: EthereumProvider, sender: string) { | ||
super(provider); | ||
this.#sender = sender; | ||
} | ||
|
||
protected async getSender(): Promise<string | undefined> { | ||
return this.#sender; | ||
} | ||
} |
57 changes: 57 additions & 0 deletions
57
...src/internal/builtin-plugins/network-manager/request-handlers/handlers/accounts/sender.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import type { JsonRpcTransactionData } from "./types.js"; | ||
import type { | ||
EthereumProvider, | ||
JsonRpcRequest, | ||
JsonRpcResponse, | ||
} from "../../../../../../types/providers.js"; | ||
import type { RequestHandler } from "../../types.js"; | ||
|
||
import { HardhatError } from "@ignored/hardhat-vnext-errors"; | ||
|
||
import { getRequestParams } from "../../../json-rpc.js"; | ||
|
||
/** | ||
* This class modifies JSON-RPC requests. | ||
* It checks if the request is related to transactions and ensures that the "from" field is populated with a sender account if it's missing. | ||
* If no account is available for sending transactions, it throws an error. | ||
* The class also provides a mechanism to retrieve the sender account, which must be implemented by subclasses. | ||
*/ | ||
export abstract class SenderHandler implements RequestHandler { | ||
protected readonly provider: EthereumProvider; | ||
|
||
constructor(provider: EthereumProvider) { | ||
this.provider = provider; | ||
} | ||
|
||
public async handle( | ||
jsonRpcRequest: JsonRpcRequest, | ||
): Promise<JsonRpcRequest | JsonRpcResponse> { | ||
const method = jsonRpcRequest.method; | ||
const params = getRequestParams(jsonRpcRequest); | ||
|
||
if ( | ||
method === "eth_sendTransaction" || | ||
method === "eth_call" || | ||
method === "eth_estimateGas" | ||
) { | ||
// TODO: from V2 - Should we validate this type? | ||
const tx: JsonRpcTransactionData = params[0]; | ||
|
||
if (tx !== undefined && tx.from === undefined) { | ||
const senderAccount = await this.getSender(); | ||
|
||
if (senderAccount !== undefined) { | ||
tx.from = senderAccount; | ||
} else if (method === "eth_sendTransaction") { | ||
throw new HardhatError( | ||
HardhatError.ERRORS.NETWORK.NO_REMOTE_ACCOUNT_AVAILABLE, | ||
); | ||
} | ||
} | ||
} | ||
|
||
return jsonRpcRequest; | ||
} | ||
|
||
protected abstract getSender(): Promise<string | undefined>; | ||
} |
9 changes: 9 additions & 0 deletions
9
.../src/internal/builtin-plugins/network-manager/request-handlers/handlers/accounts/types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export interface JsonRpcTransactionData { | ||
from?: string; | ||
to?: string; | ||
gas?: string | number; | ||
gasPrice?: string | number; | ||
value?: string | number; | ||
data?: string; | ||
nonce?: string | number; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
71 changes: 71 additions & 0 deletions
71
...in-plugins/network-manager/request-handlers/handlers/accounts/automatic-sender-handler.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import type { JsonRpcTransactionData } from "../../../../../../../src/internal/builtin-plugins/network-manager/request-handlers/handlers/accounts/types.js"; | ||
|
||
import assert from "node:assert/strict"; | ||
import { before, describe, it } from "node:test"; | ||
|
||
import { numberToHexString } from "@ignored/hardhat-vnext-utils/hex"; | ||
|
||
import { | ||
getJsonRpcRequest, | ||
getRequestParams, | ||
} from "../../../../../../../src/internal/builtin-plugins/network-manager/json-rpc.js"; | ||
import { AutomaticSenderHandler } from "../../../../../../../src/internal/builtin-plugins/network-manager/request-handlers/handlers/accounts/automatic-sender-handler.js"; | ||
import { EthereumMockedProvider } from "../../ethereum-mocked-provider.js"; | ||
|
||
describe("AutomaticSenderHandler", function () { | ||
let automaticSenderHandler: AutomaticSenderHandler; | ||
let mockedProvider: EthereumMockedProvider; | ||
let tx: JsonRpcTransactionData; | ||
|
||
before(() => { | ||
mockedProvider = new EthereumMockedProvider(); | ||
|
||
mockedProvider.setReturnValue("eth_accounts", [ | ||
"0x123006d4548a3ac17d72b372ae1e416bf65b8eaf", | ||
]); | ||
|
||
automaticSenderHandler = new AutomaticSenderHandler(mockedProvider); | ||
|
||
tx = { | ||
to: "0xb5bc06d4548a3ac17d72b372ae1e416bf65b8ead", | ||
gas: numberToHexString(21000), | ||
gasPrice: numberToHexString(678912), | ||
nonce: numberToHexString(0), | ||
value: numberToHexString(1), | ||
}; | ||
}); | ||
|
||
it("should set the from value into the transaction", async () => { | ||
const jsonRpcRequest = getJsonRpcRequest(1, "eth_sendTransaction", [tx]); | ||
|
||
await automaticSenderHandler.handle(jsonRpcRequest); | ||
|
||
assert.equal( | ||
getRequestParams(jsonRpcRequest)[0].from, | ||
"0x123006d4548a3ac17d72b372ae1e416bf65b8eaf", | ||
); | ||
}); | ||
|
||
it("should not replace transaction's from", async () => { | ||
tx.from = "0x000006d4548a3ac17d72b372ae1e416bf65b8ead"; | ||
|
||
const jsonRpcRequest = getJsonRpcRequest(1, "eth_sendTransaction", [tx]); | ||
|
||
assert.equal( | ||
getRequestParams(jsonRpcRequest)[0].from, | ||
"0x000006d4548a3ac17d72b372ae1e416bf65b8ead", | ||
); | ||
}); | ||
|
||
it("should not fail on eth_calls if provider doesn't have any accounts", async () => { | ||
mockedProvider.setReturnValue("eth_accounts", []); | ||
|
||
tx.value = "asd"; | ||
|
||
const jsonRpcRequest = getJsonRpcRequest(1, "eth_call", [tx]); | ||
|
||
await automaticSenderHandler.handle(jsonRpcRequest); | ||
|
||
assert.equal(getRequestParams(jsonRpcRequest)[0].value, "asd"); | ||
}); | ||
}); |
60 changes: 60 additions & 0 deletions
60
...uiltin-plugins/network-manager/request-handlers/handlers/accounts/fixed-sender-handler.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import type { JsonRpcTransactionData } from "../../../../../../../src/internal/builtin-plugins/network-manager/request-handlers/handlers/accounts/types.js"; | ||
|
||
import assert from "node:assert/strict"; | ||
import { before, describe, it } from "node:test"; | ||
|
||
import { numberToHexString } from "@ignored/hardhat-vnext-utils/hex"; | ||
|
||
import { | ||
getJsonRpcRequest, | ||
getRequestParams, | ||
} from "../../../../../../../src/internal/builtin-plugins/network-manager/json-rpc.js"; | ||
import { FixedSenderHandler } from "../../../../../../../src/internal/builtin-plugins/network-manager/request-handlers/handlers/accounts/fixed-sender-handler.js"; | ||
import { EthereumMockedProvider } from "../../ethereum-mocked-provider.js"; | ||
|
||
describe("FixedSenderHandler", function () { | ||
let fixedSenderHandler: FixedSenderHandler; | ||
let mockedProvider: EthereumMockedProvider; | ||
let tx: JsonRpcTransactionData; | ||
|
||
before(() => { | ||
mockedProvider = new EthereumMockedProvider(); | ||
|
||
fixedSenderHandler = new FixedSenderHandler( | ||
mockedProvider, | ||
"0x2a97a65d5673a2c61e95ce33cecadf24f654f96d", | ||
); | ||
|
||
tx = { | ||
to: "0xb5bc06d4548a3ac17d72b372ae1e416bf65b8ead", | ||
gas: numberToHexString(21000), | ||
gasPrice: numberToHexString(678912), | ||
nonce: numberToHexString(0), | ||
value: numberToHexString(1), | ||
}; | ||
}); | ||
|
||
it("should set the from value into the transaction", async () => { | ||
const jsonRpcRequest = getJsonRpcRequest(1, "eth_sendTransaction", [tx]); | ||
|
||
await fixedSenderHandler.handle(jsonRpcRequest); | ||
|
||
assert.equal( | ||
getRequestParams(jsonRpcRequest)[0].from, | ||
"0x2a97a65d5673a2c61e95ce33cecadf24f654f96d", | ||
); | ||
}); | ||
|
||
it("should not replace transaction's from", async () => { | ||
tx.from = "0x000006d4548a3ac17d72b372ae1e416bf65b8ead"; | ||
|
||
const jsonRpcRequest = getJsonRpcRequest(1, "eth_sendTransaction", [tx]); | ||
|
||
await fixedSenderHandler.handle(jsonRpcRequest); | ||
|
||
assert.equal( | ||
getRequestParams(jsonRpcRequest)[0].from, | ||
"0x000006d4548a3ac17d72b372ae1e416bf65b8ead", | ||
); | ||
}); | ||
}); |