Skip to content

Commit d0ab867

Browse files
committed
feat: add support for accountChanged for Tron network
1 parent bd22ff9 commit d0ab867

File tree

6 files changed

+437
-91
lines changed

6 files changed

+437
-91
lines changed

app/scripts/metamask-controller.js

Lines changed: 250 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ import {
131131
} from '@metamask/seedless-onboarding-controller';
132132
import { PRODUCT_TYPES } from '@metamask/subscription-controller';
133133
import { isSnapId } from '@metamask/snaps-utils';
134+
import { address } from '@solana/addresses';
134135
import {
135136
findAtomicBatchSupportForChain,
136137
checkEip7702Support,
@@ -1638,8 +1639,9 @@ export default class MetamaskController extends EventEmitter {
16381639
setupControllerEventSubscriptions() {
16391640
let lastSelectedAddress;
16401641
let lastSelectedSolanaAccountAddress;
1642+
let lastSelectedTronAccountAddress;
16411643

1642-
// this throws if there is no solana account... perhaps we should handle this better at the controller level
1644+
// this throws if there is no solana or Tron account... perhaps we should handle this better at the controller level
16431645
try {
16441646
lastSelectedSolanaAccountAddress =
16451647
this.accountsController.getSelectedMultichainAccount(
@@ -1648,6 +1650,14 @@ export default class MetamaskController extends EventEmitter {
16481650
} catch {
16491651
// noop
16501652
}
1653+
try {
1654+
lastSelectedTronAccountAddress =
1655+
this.accountsController.getSelectedMultichainAccount(
1656+
MultichainNetworks.TRON,
1657+
)?.address;
1658+
} catch {
1659+
// noop
1660+
}
16511661

16521662
this.controllerMessenger.subscribe(
16531663
'PreferencesController:stateChange',
@@ -1943,6 +1953,194 @@ export default class MetamaskController extends EventEmitter {
19431953
},
19441954
);
19451955

1956+
// wallet_notify for tron accountChanged when permission changes
1957+
this.controllerMessenger.subscribe(
1958+
`${this.permissionController.name}:stateChange`,
1959+
async (currentValue, previousValue) => {
1960+
const origins = uniq([...previousValue.keys(), ...currentValue.keys()]);
1961+
origins.forEach((origin) => {
1962+
const previousCaveatValue = previousValue.get(origin);
1963+
const currentCaveatValue = currentValue.get(origin);
1964+
1965+
const previousTronAccountChangedNotificationsEnabled = Boolean(
1966+
previousCaveatValue?.sessionProperties?.[
1967+
KnownSessionProperties.TronAccountChangedNotifications
1968+
],
1969+
);
1970+
const currentTronAccountChangedNotificationsEnabled = Boolean(
1971+
currentCaveatValue?.sessionProperties?.[
1972+
KnownSessionProperties.TronAccountChangedNotifications
1973+
],
1974+
);
1975+
1976+
if (
1977+
!previousTronAccountChangedNotificationsEnabled &&
1978+
!currentTronAccountChangedNotificationsEnabled
1979+
) {
1980+
return;
1981+
}
1982+
1983+
const previousTronCaipAccountIds = previousCaveatValue
1984+
? getPermittedAccountsForScopes(previousCaveatValue, [
1985+
MultichainNetworks.TRON,
1986+
MultichainNetworks.TRON_SHASTA,
1987+
MultichainNetworks.TRON_NILE,
1988+
])
1989+
: [];
1990+
const previousNonUniqueTronHexAccountAddresses =
1991+
previousTronCaipAccountIds.map((caipAccountId) => {
1992+
const { address } = parseCaipAccountId(caipAccountId);
1993+
return address;
1994+
});
1995+
const previousTronHexAccountAddresses = uniq(
1996+
previousNonUniqueTronHexAccountAddresses,
1997+
);
1998+
const [previousSelectedTronAccountAddress] =
1999+
this.sortMultichainAccountsByLastSelected(
2000+
previousTronHexAccountAddresses,
2001+
);
2002+
2003+
const currentTronCaipAccountIds = currentCaveatValue
2004+
? getPermittedAccountsForScopes(currentCaveatValue, [
2005+
MultichainNetworks.TRON,
2006+
MultichainNetworks.TRON_SHASTA,
2007+
MultichainNetworks.TRON_NILE,
2008+
])
2009+
: [];
2010+
const currentNonUniqueTronHexAccountAddresses =
2011+
currentTronCaipAccountIds.map((caipAccountId) => {
2012+
const { address } = parseCaipAccountId(caipAccountId);
2013+
return address;
2014+
});
2015+
const currentTronHexAccountAddresses = uniq(
2016+
currentNonUniqueTronHexAccountAddresses,
2017+
);
2018+
const [currentSelectedTronAccountAddress] =
2019+
this.sortMultichainAccountsByLastSelected(
2020+
currentTronHexAccountAddresses,
2021+
);
2022+
2023+
if (
2024+
previousSelectedTronAccountAddress !==
2025+
currentSelectedTronAccountAddress
2026+
) {
2027+
this._notifyTronAccountChange(
2028+
origin,
2029+
currentSelectedTronAccountAddress
2030+
? [currentSelectedTronAccountAddress]
2031+
: [],
2032+
);
2033+
}
2034+
});
2035+
},
2036+
getAuthorizedScopesByOrigin,
2037+
);
2038+
2039+
// TODO: To be removed when state 2 is fully transitioned.
2040+
// wallet_notify for tron accountChanged when selected account changes
2041+
this.controllerMessenger.subscribe(
2042+
`${this.accountsController.name}:selectedAccountChange`,
2043+
async (account) => {
2044+
if (
2045+
account.type === TrxAccountType.Eoa &&
2046+
account.address !== lastSelectedTronAccountAddress
2047+
) {
2048+
lastSelectedTronAccountAddress = account.address;
2049+
2050+
const originsWithTronAccountChangedNotifications =
2051+
getOriginsWithSessionProperty(
2052+
this.permissionController.state,
2053+
KnownSessionProperties.TronAccountChangedNotifications,
2054+
);
2055+
2056+
// returns a map of origins to permitted tron accounts
2057+
const tronAccounts = getPermittedAccountsForScopesByOrigin(
2058+
this.permissionController.state,
2059+
[
2060+
MultichainNetworks.TRON,
2061+
MultichainNetworks.TRON_SHASTA,
2062+
MultichainNetworks.TRON_NILE,
2063+
],
2064+
);
2065+
2066+
if (tronAccounts.size > 0) {
2067+
for (const [origin, accounts] of tronAccounts.entries()) {
2068+
const parsedTronAddresses = accounts.map((caipAccountId) => {
2069+
const { address } = parseCaipAccountId(caipAccountId);
2070+
return address;
2071+
});
2072+
2073+
if (
2074+
parsedTronAddresses.includes(account.address) &&
2075+
originsWithTronAccountChangedNotifications[origin]
2076+
) {
2077+
this._notifyTronAccountChange(origin, [account.address]);
2078+
}
2079+
}
2080+
}
2081+
}
2082+
},
2083+
);
2084+
2085+
// wallet_notify for tron accountChanged when selected account group changes
2086+
this.controllerMessenger.subscribe(
2087+
`${this.accountTreeController.name}:selectedAccountGroupChange`,
2088+
(groupId) => {
2089+
// TODO: Move this logic to the SnapKeyring directly.
2090+
// Forward selected accounts to the Snap keyring, so each Snaps can fetch those accounts.
2091+
// eslint-disable-next-line no-void
2092+
void this.forwardSelectedAccountGroupToSnapKeyring(
2093+
this.getSnapKeyringIfAvailable(),
2094+
groupId,
2095+
);
2096+
2097+
const [account] =
2098+
this.accountTreeController.getAccountsFromSelectedAccountGroup({
2099+
scopes: [TrxScope.Mainnet],
2100+
});
2101+
if (
2102+
account &&
2103+
account.type === TrxAccountType.Eoa &&
2104+
account.address !== lastSelectedTronAccountAddress
2105+
) {
2106+
lastSelectedTronAccountAddress = account.address;
2107+
2108+
const originsWithTronAccountChangedNotifications =
2109+
getOriginsWithSessionProperty(
2110+
this.permissionController.state,
2111+
KnownSessionProperties.TronAccountChangedNotifications,
2112+
);
2113+
2114+
// returns a map of origins to permitted tron accounts
2115+
const tronAccounts = getPermittedAccountsForScopesByOrigin(
2116+
this.permissionController.state,
2117+
[
2118+
MultichainNetworks.TRON,
2119+
MultichainNetworks.TRON_SHASTA,
2120+
MultichainNetworks.TRON_NILE,
2121+
],
2122+
);
2123+
2124+
if (tronAccounts.size > 0) {
2125+
for (const [origin, accounts] of tronAccounts.entries()) {
2126+
const parsedTronAddresses = accounts.map((caipAccountId) => {
2127+
const { address } = parseCaipAccountId(caipAccountId);
2128+
return address;
2129+
});
2130+
2131+
if (
2132+
parsedTronAddresses.includes(account.address) &&
2133+
originsWithTronAccountChangedNotifications[origin]
2134+
// originsWithTronAccountChangedNotifications[origin]
2135+
) {
2136+
this._notifyTronAccountChange(origin, [account.address]);
2137+
}
2138+
}
2139+
}
2140+
}
2141+
},
2142+
);
2143+
19462144
// TODO: Move this logic to the SnapKeyring directly.
19472145
// Forward selected accounts to the Snap keyring, so each Snaps can fetch those accounts.
19482146
this.controllerMessenger.subscribe(
@@ -6259,13 +6457,13 @@ export default class MetamaskController extends EventEmitter {
62596457
}
62606458

62616459
/**
6262-
* For origins with a solana scope permitted, sends a wallet_notify -> metamask_accountChanged
6263-
* event to fire for the solana scope with the currently selected solana account if any are
6460+
* For origins with a solana or tron scope permitted, sends a wallet_notify -> metamask_accountChanged
6461+
* event to fire for the scope with the currently selected account if any are
62646462
* permitted or empty array otherwise.
62656463
*
6266-
* @param {string} origin - The origin to notify with the current solana account
6464+
* @param {string} origin - The origin to notify with the current account
62676465
*/
6268-
notifySolanaAccountChangedForCurrentAccount(origin) {
6466+
notifyNonEVMAccountChangedForCurrentAccount(origin) {
62696467
let caip25Caveat;
62706468
try {
62716469
caip25Caveat = this.permissionController.getCaveat(
@@ -6292,10 +6490,16 @@ export default class MetamaskController extends EventEmitter {
62926490
KnownSessionProperties.SolanaAccountChangedNotifications
62936491
];
62946492

6493+
const tronAccountsChangedNotifications =
6494+
caip25Caveat.value.sessionProperties?.[
6495+
KnownSessionProperties.TronAccountChangedNotifications
6496+
];
6497+
62956498
const sessionScopes = getSessionScopes(caip25Caveat.value, {
62966499
getNonEvmSupportedMethods: this.getNonEvmSupportedMethods.bind(this),
62976500
});
62986501

6502+
// Handle Solana account notifications
62996503
const solanaScope =
63006504
sessionScopes[MultichainNetworks.SOLANA] ||
63016505
sessionScopes[MultichainNetworks.SOLANA_DEVNET] ||
@@ -6316,9 +6520,29 @@ export default class MetamaskController extends EventEmitter {
63166520
this._notifySolanaAccountChange(origin, [accountAddressToEmit]);
63176521
}
63186522
}
6319-
}
63206523

6321-
// ---------------------------------------------------------------------------
6524+
// Handle Tron account notifications
6525+
const tronScope =
6526+
sessionScopes[MultichainNetworks.TRON] ||
6527+
sessionScopes[MultichainNetworks.TRON_DEVNET] ||
6528+
sessionScopes[MultichainNetworks.TRON_TESTNET];
6529+
6530+
if (tronAccountsChangedNotifications && tronScope) {
6531+
const { accounts } = tronScope;
6532+
const parsedPermittedTronAddresses = accounts.map((caipAccountId) => {
6533+
const { address } = parseCaipAccountId(caipAccountId);
6534+
return address;
6535+
});
6536+
6537+
const [accountAddressToEmit] = this.sortMultichainAccountsByLastSelected(
6538+
parsedPermittedTronAddresses,
6539+
);
6540+
6541+
if (accountAddressToEmit) {
6542+
this._notifyTronAccountChange(origin, [accountAddressToEmit]);
6543+
}
6544+
}
6545+
} // ---------------------------------------------------------------------------
63226546
// Identity Management (signature operations)
63236547

63246548
getAddTransactionRequest({
@@ -6932,13 +7156,13 @@ export default class MetamaskController extends EventEmitter {
69327156
engine,
69337157
});
69347158

6935-
// solana account changed notifications
7159+
// solana and Tron account changed notifications
69367160
// This delay is needed because it's possible for a dapp to not have listeners
69377161
// setup in time right after a connection is established.
69387162
// This can be resolved if we amend the caip standards to include a liveliness
69397163
// handshake as part of the initial connection.
69407164
setTimeout(
6941-
() => this.notifySolanaAccountChangedForCurrentAccount(origin),
7165+
() => this.notifyNonEVMAccountChangedForCurrentAccount(origin),
69427166
500,
69437167
);
69447168

@@ -8677,6 +8901,23 @@ export default class MetamaskController extends EventEmitter {
86778901
);
86788902
}
86798903

8904+
async _notifyTronAccountChange(origin, accountAddressArray) {
8905+
this.notifyConnections(
8906+
origin,
8907+
{
8908+
method: MultichainApiNotifications.walletNotify,
8909+
params: {
8910+
scope: MultichainNetworks.TRON,
8911+
notification: {
8912+
method: NOTIFICATION_NAMES.accountsChanged,
8913+
params: accountAddressArray,
8914+
},
8915+
},
8916+
},
8917+
API_TYPE.CAIP_MULTICHAIN,
8918+
);
8919+
}
8920+
86808921
async _notifyChainChange() {
86818922
this.notifyAllConnections(
86828923
async (origin) => ({

0 commit comments

Comments
 (0)