Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upgrade TokenRatesController to BaseControllerV2 #4314

Merged
merged 22 commits into from
Jun 8, 2024

Conversation

kanthesha
Copy link
Contributor

Explanation

In this, the TokenRatesController has been upgraded to BaseControllerV2. The upgrade includes TokenRatesController inheriting BaseController v2 instead of BaseController v1. This affects the constructor and the also the way state is updated inside the controller, but it also prompts other changes:

This also includes the changes to the unit tests, so that all the tests uses withController pattern.

References

Fixes #4076

Changelog

@metamask/assets-controller

Added

  • New types for TokenRatesController messenger actions
    • TokenRatesControllerGetStateAction
  • New types for TokenRatesController messenger events
    • TokenRatesControllerStateChangeEvent

Changed

  • BREAKING: Changed superclass of TokenRatesController from BaseController v1 to BaseController v2
  • BREAKING: Renamed TokenRatesState to TokenRatesControllerState

Removed

  • BREAKING: Removed TokenRatesConfig type

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've highlighted breaking changes using the "BREAKING" category above as appropriate

@kanthesha kanthesha marked this pull request as ready for review May 23, 2024 21:00
@kanthesha kanthesha requested a review from a team as a code owner May 23, 2024 21:00
@desi desi requested a review from a team May 23, 2024 21:18

// This interface was created before this ESLint rule was added.
// Convert to a `type` in a future major version.
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
export interface ContractExchangeRates {
[address: string]: number | undefined;
[address: string]: number;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

undefined is removed, because it is not a json type! We need to fix on the consumer side, in case this will cause an error, where it is consumed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could either delete keys that have an undefined value, or accept null instead of undefined

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also you can consider update interface to type

@mcmire mcmire self-requested a review May 28, 2024 15:58
Comment on lines 226 to 228
currentChainId,
currentTicker,
currentAddress,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Would it be possible to substitute these with messenger calls? I believe we can obtain the current chain id from NetworkController, and the current selected address from AccountsController

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sense.
I've removed the currentChainId, currentTicker and currentAddress and now currentChainId and currentTicker are assigned form messenger network state and selectedAddress from PreferencesController state.

this.#interval = interval;
this.#chainId = currentChainId;
this.#ticker = currentTicker;
this.#selectedAddress = currentAddress;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having the selected address as a class variable here means that we also need to keep this in sync. Perhaps it would be better to always take the fresh value from AccountsController?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sense.
TokenRatesController uses PreferencesController state to manage the selectedAddress. So for now, initial selected address will be assigned from the same to be consistent. But I believe that the selectedAddress has to be pointing to AccountsController rather than PreferencesController. I'll open up a separate ticket to make those changes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like using AccountsController to get the selected address will be addressed in #4219.

this.#tokenPricesService = tokenPricesService;
this.#disabled = disabled;
this.#interval = interval;
this.#chainId = currentChainId;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly to #selectedAddress, would it be easier to take this value from NetworkController instead of storing it in a class variable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, rather than referencing outside variables each time when we need selectedAddress within the class, accessing private class / instance variable will be easier and faster.

And I think, global truth for selectedAddress has to be from AccountsController.

Copy link
Contributor

@mcmire mcmire Jun 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pattern seems okay to me. In fact it may be advantageous to use private properties to cache values like this to mitigate synchronization issues. Like, if we have to pull a bunch of values from a bunch of controllers at the start of a polling iteration it might be better to do that all in one go and then cache those values rather than fetch them at various points during that iteration (which is what we'd have to do if we didn't use private properties, I believe).

@kanthesha
Copy link
Contributor Author

Now the default value for allTokens and allDetectedTokens has been assigned from TokensController:getState (messaging system).


// @ts-expect-error Intentionally incomplete state
controllerEvents.tokensStateChange({
triggerTokensStateChange({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in this test we want to Intentionally test incomplete state ? in this case we shouldn't be adding getDefaultTokensState ? same for the above test where it has been removed ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, the messenger publish expects complete state of the TokensControllerState!

@@ -152,13 +183,25 @@ async function getCurrencyConversionRate({
}
}

const metadata = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to align with other controllers this can be tokenRatesControllerMetadata

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


// This interface was created before this ESLint rule was added.
// Convert to a `type` in a future major version.
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
export interface ContractExchangeRates {
[address: string]: number | undefined;
[address: string]: number;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also you can consider update interface to type

@@ -111,9 +120,31 @@ enum PollState {
// This interface was created before this ESLint rule was added.
// Convert to a `type` in a future major version.
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the eslint disable can be removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

AllowedEvents['type']
>;

export type TokenRatesControllerStateChangeEvent = ControllerStateChangeEvent<
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this should be above TokenRatesControllerMessenger

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

chainId: '0x1',
selectedAddress: defaultSelectedAddress,
it('should set default state', async () => {
await withController({}, async ({ controller }) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be await withController(async ({ controller }) => {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

mcmire
mcmire previously requested changes Jun 5, 2024
this.#interval = interval;
this.#chainId = currentChainId;
this.#ticker = currentTicker;
this.#selectedAddress = currentAddress;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like using AccountsController to get the selected address will be addressed in #4219.

mockGetNetworkClientById.mockImplementation(handler);
},
callActionSpy,
triggerPreferencesStateChange: (state: PreferencesState) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: What are your thoughts on having tests use the messenger directly to do whatever they want rather than wrapping each of the actions in a function? It is already pretty short to say messenger.publish('whatever') in a test, so it doesn't seem that we are saving that many characters here. Dropping these wrappers would also simplify withController so that it is more copy-and-pasteable across files.

@mcmire
Copy link
Contributor

mcmire commented Jun 5, 2024

Oof I didn't mean to request changes, sorry.

TokensControllerGetStateAction,
TokensControllerStateChangeEvent,
TokensControllerState,
} from './TokensController';

/**
* @type Token
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we don't have jsdoc for aggregators, hasBalanceError, isErc721, name. This isn't due to any change introduced by this PR, but it would be nice to address this while we're doing this upgrade.

Copy link
Contributor

@cryptodev-2s cryptodev-2s Jun 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have noticed many types are missing some properties definition, I don't know if can force this with an eslint rule

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a starting point:

 * @property aggregators - An array containing the token's aggregators
 * @property hasBalanceError - 'true' if getting the balance of an ERC-20 token resulted in an error
 * @property isErc721 - 'true' if the token is an NFT
 * @property name - Name of the token

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cryptodev-2s I'm sure there are jsdoc eslint rules that are supposed to do that. I'll write up a ticket for enabling them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.

Copy link
Contributor

@MajorLift MajorLift Jun 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like we're moving away from jsdoc comments for types, since annotating each property individually gives the user better access to the comments, especially when hovering on the property.

(e.g. see https://github.com/MetaMask/core/pull/4286/files#diff-ea0aa37793484e278bd59970b7a6ce8380b75b723b34612081d402fb66261cfdR152-R196, and hover over any search result of "networkConfiguration." (networkConfiguration\. for regex) in the same file.

For functions, objects etc., we might still want to enforce jsdoc lint rules, so I'll still take a look into configuring this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding an ESLint rule to check for JSDoc'ing properties would be great! I did some initial research into this a while back and didn't find any existing rules but maybe someone has come up with something by now.

* @property image - Image of the token, url or bit32 image
* @property hasBalanceError - 'true' if there is an error while updating the token balance
* @property isERC721 - 'true' if the token is a ERC721 token
* @property name - Name of the token
*/

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit:
we can remove this empty space

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor

@mcmire mcmire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more suggestions but this is looking pretty good to me.

@mcmire mcmire dismissed their stale review June 7, 2024 17:08

Didn't mean to request changes :)

Copy link
Contributor

@cryptodev-2s cryptodev-2s left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Copy link
Contributor

@mcmire mcmire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@kanthesha kanthesha merged commit 4d77dde into main Jun 8, 2024
113 checks passed
@kanthesha kanthesha deleted the upgrade-token-rates-controller-to-base-controller-v2 branch June 8, 2024 12:27
ccharly added a commit that referenced this pull request Jun 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Convert TokenRatesController from BaseControllerV1 to BaseControllerV2
5 participants