-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Add support for Tron account change #38536
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -131,6 +131,7 @@ import { | |
| } from '@metamask/seedless-onboarding-controller'; | ||
| import { PRODUCT_TYPES } from '@metamask/subscription-controller'; | ||
| import { isSnapId } from '@metamask/snaps-utils'; | ||
| import { address } from '@solana/addresses'; | ||
| import { | ||
| findAtomicBatchSupportForChain, | ||
| checkEip7702Support, | ||
|
|
@@ -1638,8 +1639,9 @@ export default class MetamaskController extends EventEmitter { | |
| setupControllerEventSubscriptions() { | ||
| let lastSelectedAddress; | ||
| let lastSelectedSolanaAccountAddress; | ||
| let lastSelectedTronAccountAddress; | ||
|
|
||
| // this throws if there is no solana account... perhaps we should handle this better at the controller level | ||
| // this throws if there is no solana or Tron account... perhaps we should handle this better at the controller level | ||
| try { | ||
| lastSelectedSolanaAccountAddress = | ||
| this.accountsController.getSelectedMultichainAccount( | ||
|
|
@@ -1648,6 +1650,14 @@ export default class MetamaskController extends EventEmitter { | |
| } catch { | ||
| // noop | ||
| } | ||
| try { | ||
| lastSelectedTronAccountAddress = | ||
| this.accountsController.getSelectedMultichainAccount( | ||
| MultichainNetworks.TRON, | ||
| )?.address; | ||
| } catch { | ||
| // noop | ||
| } | ||
|
|
||
| this.controllerMessenger.subscribe( | ||
| 'PreferencesController:stateChange', | ||
|
|
@@ -1943,6 +1953,194 @@ export default class MetamaskController extends EventEmitter { | |
| }, | ||
| ); | ||
|
|
||
| // wallet_notify for tron accountChanged when permission changes | ||
| this.controllerMessenger.subscribe( | ||
| `${this.permissionController.name}:stateChange`, | ||
| async (currentValue, previousValue) => { | ||
| const origins = uniq([...previousValue.keys(), ...currentValue.keys()]); | ||
| origins.forEach((origin) => { | ||
| const previousCaveatValue = previousValue.get(origin); | ||
| const currentCaveatValue = currentValue.get(origin); | ||
|
|
||
| const previousTronAccountChangedNotificationsEnabled = Boolean( | ||
| previousCaveatValue?.sessionProperties?.[ | ||
| KnownSessionProperties.TronAccountChangedNotifications | ||
| ], | ||
| ); | ||
| const currentTronAccountChangedNotificationsEnabled = Boolean( | ||
| currentCaveatValue?.sessionProperties?.[ | ||
| KnownSessionProperties.TronAccountChangedNotifications | ||
| ], | ||
| ); | ||
|
|
||
| if ( | ||
| !previousTronAccountChangedNotificationsEnabled && | ||
| !currentTronAccountChangedNotificationsEnabled | ||
| ) { | ||
| return; | ||
| } | ||
|
|
||
| const previousTronCaipAccountIds = previousCaveatValue | ||
| ? getPermittedAccountsForScopes(previousCaveatValue, [ | ||
| MultichainNetworks.TRON, | ||
| MultichainNetworks.TRON_SHASTA, | ||
| MultichainNetworks.TRON_NILE, | ||
| ]) | ||
| : []; | ||
| const previousNonUniqueTronHexAccountAddresses = | ||
| previousTronCaipAccountIds.map((caipAccountId) => { | ||
| const { address } = parseCaipAccountId(caipAccountId); | ||
| return address; | ||
| }); | ||
| const previousTronHexAccountAddresses = uniq( | ||
| previousNonUniqueTronHexAccountAddresses, | ||
| ); | ||
| const [previousSelectedTronAccountAddress] = | ||
| this.sortMultichainAccountsByLastSelected( | ||
| previousTronHexAccountAddresses, | ||
| ); | ||
|
|
||
| const currentTronCaipAccountIds = currentCaveatValue | ||
| ? getPermittedAccountsForScopes(currentCaveatValue, [ | ||
| MultichainNetworks.TRON, | ||
| MultichainNetworks.TRON_SHASTA, | ||
| MultichainNetworks.TRON_NILE, | ||
| ]) | ||
| : []; | ||
| const currentNonUniqueTronHexAccountAddresses = | ||
| currentTronCaipAccountIds.map((caipAccountId) => { | ||
| const { address } = parseCaipAccountId(caipAccountId); | ||
| return address; | ||
| }); | ||
| const currentTronHexAccountAddresses = uniq( | ||
| currentNonUniqueTronHexAccountAddresses, | ||
| ); | ||
| const [currentSelectedTronAccountAddress] = | ||
| this.sortMultichainAccountsByLastSelected( | ||
| currentTronHexAccountAddresses, | ||
| ); | ||
|
|
||
| if ( | ||
| previousSelectedTronAccountAddress !== | ||
| currentSelectedTronAccountAddress | ||
| ) { | ||
| this._notifyTronAccountChange( | ||
| origin, | ||
| currentSelectedTronAccountAddress | ||
| ? [currentSelectedTronAccountAddress] | ||
| : [], | ||
| ); | ||
| } | ||
| }); | ||
| }, | ||
| getAuthorizedScopesByOrigin, | ||
| ); | ||
|
|
||
| // TODO: To be removed when state 2 is fully transitioned. | ||
| // wallet_notify for tron accountChanged when selected account changes | ||
| this.controllerMessenger.subscribe( | ||
| `${this.accountsController.name}:selectedAccountChange`, | ||
| async (account) => { | ||
| if ( | ||
| account.type === TrxAccountType.Eoa && | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: Tron types used outside conditional compilation guardThe code uses Additional Locations (2) |
||
| account.address !== lastSelectedTronAccountAddress | ||
| ) { | ||
| lastSelectedTronAccountAddress = account.address; | ||
|
|
||
| const originsWithTronAccountChangedNotifications = | ||
| getOriginsWithSessionProperty( | ||
| this.permissionController.state, | ||
| KnownSessionProperties.TronAccountChangedNotifications, | ||
| ); | ||
|
|
||
| // returns a map of origins to permitted tron accounts | ||
| const tronAccounts = getPermittedAccountsForScopesByOrigin( | ||
| this.permissionController.state, | ||
| [ | ||
| MultichainNetworks.TRON, | ||
| MultichainNetworks.TRON_SHASTA, | ||
| MultichainNetworks.TRON_NILE, | ||
| ], | ||
| ); | ||
|
|
||
| if (tronAccounts.size > 0) { | ||
| for (const [origin, accounts] of tronAccounts.entries()) { | ||
| const parsedTronAddresses = accounts.map((caipAccountId) => { | ||
| const { address } = parseCaipAccountId(caipAccountId); | ||
| return address; | ||
| }); | ||
|
|
||
| if ( | ||
| parsedTronAddresses.includes(account.address) && | ||
| originsWithTronAccountChangedNotifications[origin] | ||
| ) { | ||
| this._notifyTronAccountChange(origin, [account.address]); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| ); | ||
|
|
||
| // wallet_notify for tron accountChanged when selected account group changes | ||
| this.controllerMessenger.subscribe( | ||
| `${this.accountTreeController.name}:selectedAccountGroupChange`, | ||
| (groupId) => { | ||
| // TODO: Move this logic to the SnapKeyring directly. | ||
| // Forward selected accounts to the Snap keyring, so each Snaps can fetch those accounts. | ||
| // eslint-disable-next-line no-void | ||
| void this.forwardSelectedAccountGroupToSnapKeyring( | ||
| this.getSnapKeyringIfAvailable(), | ||
| groupId, | ||
| ); | ||
|
|
||
| const [account] = | ||
| this.accountTreeController.getAccountsFromSelectedAccountGroup({ | ||
| scopes: [TrxScope.Mainnet], | ||
| }); | ||
| if ( | ||
| account && | ||
| account.type === TrxAccountType.Eoa && | ||
| account.address !== lastSelectedTronAccountAddress | ||
| ) { | ||
| lastSelectedTronAccountAddress = account.address; | ||
|
|
||
| const originsWithTronAccountChangedNotifications = | ||
| getOriginsWithSessionProperty( | ||
| this.permissionController.state, | ||
| KnownSessionProperties.TronAccountChangedNotifications, | ||
| ); | ||
|
|
||
| // returns a map of origins to permitted tron accounts | ||
| const tronAccounts = getPermittedAccountsForScopesByOrigin( | ||
| this.permissionController.state, | ||
| [ | ||
| MultichainNetworks.TRON, | ||
| MultichainNetworks.TRON_SHASTA, | ||
| MultichainNetworks.TRON_NILE, | ||
| ], | ||
| ); | ||
|
|
||
| if (tronAccounts.size > 0) { | ||
| for (const [origin, accounts] of tronAccounts.entries()) { | ||
| const parsedTronAddresses = accounts.map((caipAccountId) => { | ||
| const { address } = parseCaipAccountId(caipAccountId); | ||
| return address; | ||
| }); | ||
|
|
||
| if ( | ||
| parsedTronAddresses.includes(account.address) && | ||
| originsWithTronAccountChangedNotifications[origin] | ||
| // originsWithTronAccountChangedNotifications[origin] | ||
| ) { | ||
| this._notifyTronAccountChange(origin, [account.address]); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| ); | ||
|
|
||
| // TODO: Move this logic to the SnapKeyring directly. | ||
| // Forward selected accounts to the Snap keyring, so each Snaps can fetch those accounts. | ||
| this.controllerMessenger.subscribe( | ||
|
|
@@ -6259,13 +6457,13 @@ export default class MetamaskController extends EventEmitter { | |
| } | ||
|
|
||
| /** | ||
| * For origins with a solana scope permitted, sends a wallet_notify -> metamask_accountChanged | ||
| * event to fire for the solana scope with the currently selected solana account if any are | ||
| * For origins with a solana or tron scope permitted, sends a wallet_notify -> metamask_accountChanged | ||
| * event to fire for the scope with the currently selected account if any are | ||
| * permitted or empty array otherwise. | ||
| * | ||
| * @param {string} origin - The origin to notify with the current solana account | ||
| * @param {string} origin - The origin to notify with the current account | ||
| */ | ||
| notifySolanaAccountChangedForCurrentAccount(origin) { | ||
| notifyNonEVMAccountChangedForCurrentAccount(origin) { | ||
| let caip25Caveat; | ||
| try { | ||
| caip25Caveat = this.permissionController.getCaveat( | ||
|
|
@@ -6292,10 +6490,16 @@ export default class MetamaskController extends EventEmitter { | |
| KnownSessionProperties.SolanaAccountChangedNotifications | ||
| ]; | ||
|
|
||
| const tronAccountsChangedNotifications = | ||
| caip25Caveat.value.sessionProperties?.[ | ||
| KnownSessionProperties.TronAccountChangedNotifications | ||
| ]; | ||
|
|
||
| const sessionScopes = getSessionScopes(caip25Caveat.value, { | ||
| getNonEvmSupportedMethods: this.getNonEvmSupportedMethods.bind(this), | ||
| }); | ||
|
|
||
| // Handle Solana account notifications | ||
| const solanaScope = | ||
| sessionScopes[MultichainNetworks.SOLANA] || | ||
| sessionScopes[MultichainNetworks.SOLANA_DEVNET] || | ||
|
|
@@ -6316,9 +6520,29 @@ export default class MetamaskController extends EventEmitter { | |
| this._notifySolanaAccountChange(origin, [accountAddressToEmit]); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // Handle Tron account notifications | ||
| const tronScope = | ||
| sessionScopes[MultichainNetworks.TRON] || | ||
| sessionScopes[MultichainNetworks.TRON_DEVNET] || | ||
| sessionScopes[MultichainNetworks.TRON_TESTNET]; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: Non-existent enum values for Tron testnet networksThe code references |
||
|
|
||
| if (tronAccountsChangedNotifications && tronScope) { | ||
| const { accounts } = tronScope; | ||
| const parsedPermittedTronAddresses = accounts.map((caipAccountId) => { | ||
| const { address } = parseCaipAccountId(caipAccountId); | ||
| return address; | ||
| }); | ||
|
|
||
| const [accountAddressToEmit] = this.sortMultichainAccountsByLastSelected( | ||
| parsedPermittedTronAddresses, | ||
| ); | ||
|
|
||
| if (accountAddressToEmit) { | ||
| this._notifyTronAccountChange(origin, [accountAddressToEmit]); | ||
| } | ||
| } | ||
| } // --------------------------------------------------------------------------- | ||
| // Identity Management (signature operations) | ||
|
|
||
| getAddTransactionRequest({ | ||
|
|
@@ -6932,13 +7156,13 @@ export default class MetamaskController extends EventEmitter { | |
| engine, | ||
| }); | ||
|
|
||
| // solana account changed notifications | ||
| // solana and Tron account changed notifications | ||
| // This delay is needed because it's possible for a dapp to not have listeners | ||
| // setup in time right after a connection is established. | ||
| // This can be resolved if we amend the caip standards to include a liveliness | ||
| // handshake as part of the initial connection. | ||
| setTimeout( | ||
| () => this.notifySolanaAccountChangedForCurrentAccount(origin), | ||
| () => this.notifyNonEVMAccountChangedForCurrentAccount(origin), | ||
| 500, | ||
| ); | ||
|
|
||
|
|
@@ -8677,6 +8901,23 @@ export default class MetamaskController extends EventEmitter { | |
| ); | ||
| } | ||
|
|
||
| async _notifyTronAccountChange(origin, accountAddressArray) { | ||
| this.notifyConnections( | ||
| origin, | ||
| { | ||
| method: MultichainApiNotifications.walletNotify, | ||
| params: { | ||
| scope: MultichainNetworks.TRON, | ||
| notification: { | ||
| method: NOTIFICATION_NAMES.accountsChanged, | ||
| params: accountAddressArray, | ||
| }, | ||
| }, | ||
| }, | ||
| API_TYPE.CAIP_MULTICHAIN, | ||
| ); | ||
| } | ||
|
|
||
| async _notifyChainChange() { | ||
| this.notifyAllConnections( | ||
| async (origin) => ({ | ||
|
|
||
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.
Bug: Unused and misplaced Solana address import
The
addressfunction is imported from@solana/addressesbut never used in this file. All other occurrences ofaddressin the file are object destructuring fromparseCaipAccountId(), not calls to this imported function. This appears to be accidentally committed debug code or a copy-paste error.