-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Fix typing compatibility when linking a contract to a context #5416
Fix typing compatibility when linking a contract to a context #5416
Conversation
| public link<T extends Web3Context<API, RegisteredSubs>>(parentContext: T) { | ||
| public link<T extends Web3Context>(parentContext: T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
API should be passed here otherwise the specification of ChainlinkPluginAPI is meaningless. The purpose of specifying the ChainlinkPluginAPI (as we do with EthExecutionApi) is to provide type safety when using the requestManager e.g. API specifies what methods are available to call and their corresponding typed params when doing things like so:
which gives us type safety like so:
Removing <API, RegisteredSubs> for link means API gets default to unknown as you've added API extends Web3APISpec = unknown, which yields:
which means TypeScript is no longer aware of what's available for the requestManager to call, what params the specified method takes, and what the return type of the request is
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, requestManager and link<T extends Web3Context> are separate. And so not passing API to link<T extends Web3Context> does not affect requestManager. And I kept it untouched as-is:
export class Web3RequestManager<
API extends Web3APISpec = EthExecutionAPI,
> extends Web3EventEmitter<{
[key in Web3RequestManagerEvent]: SupportedProviders<API> | undefined;
}> {
...And so the safe typing is working well:

And, if I provided no params to eth_getBalance then running yarn build would give the following error:
web3-eth: src/rpc_methods.ts(99,3): error TS2322: Type '[]' is not assignable to type '[address: string, blockNumber: BlockNumberOrTag]'.
So, I think you just had a cashing problem when checking out this branch. Kindly try again by running yarn build. And close vscode and reopen it, if it was still not showing you the correct error...
And let me know, please, if there is any type-safe feature that is not working as expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The third screenshot is the inferred type when using the requestManager for the plugin. The EthExecutionApi (i.e. the requestManager when used within the web3-eth package) still resolves as expected with your approach, but the same API type resolution does not occur for ChainlinkPluginAPI - this is what #5480 is attempting to solve. I don't think making use of unknown is the best solution for this issue as we do know what the API is because we specify it. The changes in #5480 should allow us to pass an API that's not EthExecutionApi (or extends it as Web3EthExecutionApi does), but as mentioned on our call and below, our code around subscriptions/logs is hardcoded to handle EthExecutionApi and therefore throws type errors when trying to use an API that doesn't have eth_subscribe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the plugin writer would like to use this._requestManager, then the plugin api would need to be something like:
// This is defined as a type, because defining this as an interface will fool the compiler to not well-detect
// the API at EthExecutionAPI when passing it to Web3PluginBase.
export type ChainlinkPluginAPI = EthExecutionAPI & {
getPrice: () => Promise<Price>;
};
And then all the type-safety for the request manager would work inside the plugin.
However, I also I caught the source of confusion for getPrice. This has nothing to do with the ChainlinkPluginAPI. This should be only inside ChainlinkPlugin. And, actually, when the user writes web3.chainlink, it should be of the type ChainlinkPlugin and not ChainlinkPluginAPI.
So, 2 places needed to be fixed for that:
First is the definition of the chainlink variable:
declare module 'web3' {
interface Web3 {
chainlink: ChainlinkPlugin; // Not ChainlinkPluginAPI
}
}
And is that the type of ChainlinkPluginAPI should contain only the JSON RPC methods for the connected node. So, ti should not contain getPrice. But if the node provides some non-standard JSON RPC method. Or JSON RPC methods for substrate, for example (if the plugin was expanded later for such a possibility).
So, I pushed a commit that addresses those points in addition to a few enhancements....
Please, let me know if you have any other concerns about the provided solution. And if so, please provide a code snippet of what you would like to do exactly and I will help investigating that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea I was totally thinking about ChainlinkPluginAPI incorrectly - it's not needed given that there are no JSON RPC methods being added as you've mentioned. I think it's fine for Web3PluginBase to just default to Web3APISpec (so the user doesn't have to worry about passing the generic if they're not adding a JSON RPC API) as added by this commit
The rest of the changes you've made in this PR (namely around I've also been thinking about this incorrectly, the issue I thought we had regarding instantiating a Web3Context) I don't think are needed for #5393, but probably do need to be incorporated into #5480. #5480 is actually trying to solve an issue not strictly related to #5393 and that's fixing the existing architecture around Web3Context and the RequestManager when you pass an API that doesn't extend EthExecutionApi - which I don't believe should be a requirement when a plugin/user wants to add JSON RPC methods (it's currently a requirement since code around log subscriptions is currently hardcoded to use EthExecutionApi as mentioned here)Web3Context with an API that does not extend EthExecutionApi is non-existent as demonstrated by the following:
export type FooApi = {
foo_bar: (arg1: string, arg2: number) => boolean;
bar_foo: () => string;
}
const web3Context = new Web3Context<FooApi>('http://127.0.0.1:8545');
web3Context.requestManager.send({
method: 'foo_bar',
params: [] // This errors as expected (see screenshot)
})
web3Context.requestManager.send({
method: 'bar_foo',
params: []
})The error I was actually seeing is fixed by extending EthExecutionApi like you've mentioned above:
export type ChainlinkPluginAPI = EthExecutionAPI & {
getPrice: () => Promise<Price>;
};Even though this ChainlinkPluginAPI is not actually needed for the plugin (since we're not adding support for any new JSON RPC methods), for the sake of this example, let's just assume it's needed. When attempting to do
interface Price {
roundId: string;
answer: string;
startedAt: string;
updatedAt: string;
answeredInRound: string;
}
interface ChainlinkPluginAPI extends Web3APISpec {
getPrice: () => Promise<Price>;
}
declare module 'web3/dist/web3' {
interface Web3 {
chainlink: ChainlinkPluginAPI;
}
}
export class ChainlinkPlugin extends Web3PluginBase<Web3APISpec> {
public pluginNamespace = 'chainlink';
protected readonly _contract: Contract<typeof AggregatorV3InterfaceABI>;
public constructor(abi: ContractAbi, address: Address) {
super();
this._contract = new Contract(abi, address);
}
public async getPrice() {
if (this._contract.currentProvider === undefined) this._contract.link(this);
return this._contract.methods.latestRoundData().call();
}
}You receive the following error for the line
if (this._contract.currentProvider === undefined) this._contract.link(this);Argument of type 'this' is not assignable to parameter of type 'Web3Context<EthExecutionAPI, { logs: typeof LogsSubscription; newHeads: typeof NewHeadsSubscription; newBlockHeaders: typeof NewHeadsSubscription; }>'.
Type 'ChainlinkPlugin' is not assignable to type 'Web3Context<EthExecutionAPI, { logs: typeof LogsSubscription; newHeads: typeof NewHeadsSubscription; newBlockHeaders: typeof NewHeadsSubscription; }>'.
Types of property '_requestManager' are incompatible.
Type 'Web3RequestManager<ChainlinkPluginAPI>' is not assignable to type 'Web3RequestManager<EthExecutionAPI>'.
Type 'ChainlinkPluginAPI' is missing the following properties from type 'EthExecutionAPI': eth_getBlockByHash, eth_getBlockByNumber, eth_getBlockTransactionCountByHash, eth_getBlockTransactionCountByNumber, and 44 more.ts(2345)I was thinking ChainlinkPluginAPI shouldn't need to extend EthExeuctionAPI in this case because it would add a bunch of JSON RPC methods that aren't going to be used for the plugin. However, ChainlinkPluginAPI needs to extend EthExeuctionAPI because it's using web3-eth-contract which depends on JSON RPC methods defined by EthExeuctionAPI to perform certain actions. The error in my thinking was that you don't need EthExecutionAPI just to call methods on a deployed contract (as the example Chainlink plugin is doing), but that would entail removing functionality from the web3-eth-contract to get it to work which just doesn't make sense
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great @spacesailor24 👍
I am happy to see that this MR helped resolve the 2 blockers for #5393 and provided clarifications 😄
And so it is time to merge this MR.
However, because I think that, there are some copied ideas from this MR to #5393 (and this is also the reason for having conflicts in the MR with the destination branch). I suggest you do one of the following:
- Ideally, it would be better to preserve the history and the orders of the commits. And so I suggest you to
rebaseyour branch to an earlier commit and then re-push the commits from this branch and from your branch in the order they came. And before doing this I recommend that you save the commits from both branches and the date of each one. And also to have a full copy of the local folder, that you can use in case things were messed-up. This will keep the contribution clean of who did what at which time and it would be easier to trace if later needed and this is the best practice, I think. - The other option is easier but will vanish the valuable history. It is to resolve the conflicts in this MR and merge it with yours. This is not the best option because there were some ideas copied from this MR to the other MR in parallel instead of merging it early. But I am OK with that if you faced issues when trying the first option.
And by the way, I also pushed some more enhancements and comments to the code so when someone later would implement a plugin would have a clearer understanding, I hope. And this included 2 classes Web3PluginBase and Web3EthPluginBase 😄.
* Add tests * Update geth script * Fix geth script * Reset to initial chainId * Remove uneeded space * Ganace chainId
* sample abi of a contract * unit tests contract * contract events test * unit test data * additional tests for more coverage * default common check * test contract source and updated abi * common type fix
…eb3context-generic-type
* Init abstract Web3PluginBase class. Init registerPlugin method * Update web3.ts to have a named export instead of using default * Init web3-plugin-example package * Update CHANGELOG and README * Update packages/web3-plugin-example/test/unit/plugin.test.ts * Remove unneeded npm scripts * Remove webpack config * Init ExistingPluginNamespaceError * registerPluging - throw error is namespace not undefined * Update Web3 to use default and named exports * Add peerDependencies * Default API generic to Web3APISpec for Web3PluginBase * Remove use of ChainlinkPluginAPI. Define return type for getPrice * Remove no longer needed generic values * Override link method for plugin * Remove no longer used import * Remove ChainlinkPlugin code * Init custom_rpc_methods plugin example * Init contract_method_wrappers plugin example * Update web3 import to remove import ts error * Fix typing compatibility when linking a contract to a context (#5416) * fix typing compatibility when linking a contract to a context * replace any with unknown at a suggested code for Web3Context * Web3 net system tests (#5457) * Add tests * Update geth script * Fix geth script * Reset to initial chainId * Remove uneeded space * Ganace chainId * more fixes and sample calls for the plugin draft * tiny code revert for unused variable * add comments and replace some `any`s with `unknown`s for plugin related code * 5427 contracts unit tests (#5465) * sample abi of a contract * unit tests contract * contract events test * unit test data * additional tests for more coverage * default common check * test contract source and updated abi * common type fix * Merge with 5091 Co-authored-by: Nikos Iliakis <[email protected]> Co-authored-by: Junaid <[email protected]> Co-authored-by: Wyatt Barnes <[email protected]> * Fix lint error * Move web3-plugin-example to tools/. Remove @ts-expect-errors. Move Web3 module redeclarations to test files * Update CHANGELOGs * Fix tsc and linter errors * Update packages/web3-types/CHANGELOG.md * Add CHANGELOG changes to root CHANGELOG * Update packages/web3-types/CHANGELOG.md * Update CHANGELOG.md Co-authored-by: Muhammad Altabba <[email protected]> * Update CHANGELOG.md * Add web3-plugin-example CHANGELOG * Remove unnecessary web3 imports * Revert change to Web3 export * Init web3_export_helper.ts * Update packages/web3/CHANGELOG.md Co-authored-by: Muhammad Altabba <[email protected]> * Update CHANGELOG.md Co-authored-by: Muhammad Altabba <[email protected]> * Update comments for mocking in contract_method_wrappers.test.ts Co-authored-by: Junaid <[email protected]> Co-authored-by: Muhammad Altabba <[email protected]> Co-authored-by: Nikos Iliakis <[email protected]>
* fix typing compatibility when linking a contract to a context * replace any with unknown at a suggested code for Web3Context * Web3 net system tests (#5457) * Add tests * Update geth script * Fix geth script * Reset to initial chainId * Remove uneeded space * Ganace chainId * more fixes and sample calls for the plugin draft * tiny code revert for unused variable * add comments and replace some `any`s with `unknown`s for plugin related code * 5427 contracts unit tests (#5465) * sample abi of a contract * unit tests contract * contract events test * unit test data * additional tests for more coverage * default common check * test contract source and updated abi * common type fix * Merge with 5091 Co-authored-by: Nikos Iliakis <[email protected]> Co-authored-by: Junaid <[email protected]> Co-authored-by: Wyatt Barnes <[email protected]>
* Update web3.ts to have a named export instead of using default * Init web3-plugin-example package * Update CHANGELOG and README * Update packages/web3-plugin-example/test/unit/plugin.test.ts * Remove unneeded npm scripts * Remove webpack config * Update Web3 to use default and named exports * Add peerDependencies * Remove use of ChainlinkPluginAPI. Define return type for getPrice * Override link method for plugin * Remove ChainlinkPlugin code * Init custom_rpc_methods plugin example * Init contract_method_wrappers plugin example * Update web3 import to remove import ts error * Fix typing compatibility when linking a contract to a context (#5416) * fix typing compatibility when linking a contract to a context * replace any with unknown at a suggested code for Web3Context * Web3 net system tests (#5457) * Add tests * Update geth script * Fix geth script * Reset to initial chainId * Remove uneeded space * Ganace chainId * more fixes and sample calls for the plugin draft * tiny code revert for unused variable * add comments and replace some `any`s with `unknown`s for plugin related code * 5427 contracts unit tests (#5465) * sample abi of a contract * unit tests contract * contract events test * unit test data * additional tests for more coverage * default common check * test contract source and updated abi * common type fix * Merge with 5091 Co-authored-by: Nikos Iliakis <[email protected]> Co-authored-by: Junaid <[email protected]> Co-authored-by: Wyatt Barnes <[email protected]> * Move web3-plugin-example to tools/. Remove @ts-expect-errors. Move Web3 module redeclarations to test files * Update CHANGELOGs * Update packages/web3-types/CHANGELOG.md * Update CHANGELOG.md Co-authored-by: Muhammad Altabba <[email protected]> * Revert change to Web3 export * WIP plugin docs * Update plugin_authors doc * Update plugin_users doc * Rewording * Update packages/web3/CHANGELOG.md * Update CHANGELOG.md * Update docs/docs/guides/web3_plugin_guide/plugin_authors.md Co-authored-by: Junaid <[email protected]> * Update docs/docs/guides/web3_plugin_guide/plugin_users.md Co-authored-by: Junaid <[email protected]> * Update docs/docs/guides/web3_plugin_guide/plugin_users.md Co-authored-by: Junaid <[email protected]> * Apply suggestions from code review Co-authored-by: Junaid <[email protected]> * Update tree shaking guide to sidebar position 2 * Update plugin authors sidebar position to 0 * Update docs/docs/guides/web3_plugin_guide/plugin_users.md Co-authored-by: Alex <[email protected]> Co-authored-by: Muhammad Altabba <[email protected]> Co-authored-by: Nikos Iliakis <[email protected]> Co-authored-by: Junaid <[email protected]> Co-authored-by: Alex <[email protected]>




Description
A suggested edit addressing the discussion points mentioned at #5393 (comment)
This is an MR to another MR (to #5393)
The basic idea is to make the first generic parameter of
Web3Contextdefault to beanyand to use this in all relevant places to allow for flexibility when accepting the typeWeb3Context. As atContractthe methodlink<T extends Web3Context<API, RegisteredSubs>>it becomeslink<T extends Web3Context>which is basically equivalent tolink<T extends Web3Context<any,any>>which allow for accepting any Web3Context that could be defined by any plugin. However, to achieve that, some other types of changes were required and they are done in this MR. But those changes are best to look at as a suggestion that encourages discussions...So, this MR is a draft suggestion that could be taken as-is. Or its commit(s) could be
cherry-pickfrom, and then modified as needed.Type of change
Checklist for 4.x:
yarnsuccessfullyyarn lintsuccessfullyyarn build:websuccessfullyyarn test:unitsuccessfullyyarn test:integrationsuccessfullycompile:contractssuccessfullyCHANGELOG.mdfile in the packages I have edited.