mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge branch 'develop' of github.com:MetaMask/metamask-extension into minimal
This commit is contained in:
commit
bd45b0eca2
@ -263,12 +263,12 @@ const state = {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
id: 'local:http://localhost:8080/',
|
id: 'local:http://localhost:8080/',
|
||||||
initialPermissions: {
|
initialPermissions: {
|
||||||
snap_confirm: {},
|
snap_dialog: {},
|
||||||
},
|
},
|
||||||
manifest: {
|
manifest: {
|
||||||
description: 'An example MetaMask Snap.',
|
description: 'An example MetaMask Snap.',
|
||||||
initialPermissions: {
|
initialPermissions: {
|
||||||
snap_confirm: {},
|
snap_dialog: {},
|
||||||
},
|
},
|
||||||
manifestVersion: '0.1',
|
manifestVersion: '0.1',
|
||||||
proposedName: 'MetaMask Example Snap',
|
proposedName: 'MetaMask Example Snap',
|
||||||
@ -298,7 +298,7 @@ const state = {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
id: 'npm:http://localhost:8080/',
|
id: 'npm:http://localhost:8080/',
|
||||||
initialPermissions: {
|
initialPermissions: {
|
||||||
snap_confirm: {},
|
snap_dialog: {},
|
||||||
eth_accounts: {},
|
eth_accounts: {},
|
||||||
snap_manageState: {},
|
snap_manageState: {},
|
||||||
},
|
},
|
||||||
@ -306,7 +306,7 @@ const state = {
|
|||||||
description:
|
description:
|
||||||
'This swap provides developers everywhere access to an entirely new data storage paradigm, even letting your programs store data autonomously. Learn more.',
|
'This swap provides developers everywhere access to an entirely new data storage paradigm, even letting your programs store data autonomously. Learn more.',
|
||||||
initialPermissions: {
|
initialPermissions: {
|
||||||
snap_confirm: {},
|
snap_dialog: {},
|
||||||
eth_accounts: {},
|
eth_accounts: {},
|
||||||
snap_manageState: {},
|
snap_manageState: {},
|
||||||
},
|
},
|
||||||
@ -1349,9 +1349,9 @@ const state = {
|
|||||||
},
|
},
|
||||||
'local:http://localhost:8080/': {
|
'local:http://localhost:8080/': {
|
||||||
permissions: {
|
permissions: {
|
||||||
snap_confirm: {
|
snap_dialog: {
|
||||||
invoker: 'local:http://localhost:8080/',
|
invoker: 'local:http://localhost:8080/',
|
||||||
parentCapability: 'snap_confirm',
|
parentCapability: 'snap_dialog',
|
||||||
id: 'a7342F4b-beae-4525-a36c-c0635fd03359',
|
id: 'a7342F4b-beae-4525-a36c-c0635fd03359',
|
||||||
date: 1620710693178,
|
date: 1620710693178,
|
||||||
caveats: [],
|
caveats: [],
|
||||||
|
4
app/_locales/de/messages.json
generated
4
app/_locales/de/messages.json
generated
@ -2697,10 +2697,6 @@
|
|||||||
"message": "Regelmäßige Transaktionen planen und ausführen.",
|
"message": "Regelmäßige Transaktionen planen und ausführen.",
|
||||||
"description": "The description for the `snap_cronjob` permission"
|
"description": "The description for the `snap_cronjob` permission"
|
||||||
},
|
},
|
||||||
"permission_customConfirmation": {
|
|
||||||
"message": "Bestätigung in MetaMask anzeigen.",
|
|
||||||
"description": "The description for the `snap_confirm` permission"
|
|
||||||
},
|
|
||||||
"permission_dialog": {
|
"permission_dialog": {
|
||||||
"message": "Dialogfenster in MetaMask anzeigen.",
|
"message": "Dialogfenster in MetaMask anzeigen.",
|
||||||
"description": "The description for the `snap_dialog` permission"
|
"description": "The description for the `snap_dialog` permission"
|
||||||
|
4
app/_locales/el/messages.json
generated
4
app/_locales/el/messages.json
generated
@ -2697,10 +2697,6 @@
|
|||||||
"message": "Προγραμματισμός και εκτέλεση περιοδικών ενεργειών.",
|
"message": "Προγραμματισμός και εκτέλεση περιοδικών ενεργειών.",
|
||||||
"description": "The description for the `snap_cronjob` permission"
|
"description": "The description for the `snap_cronjob` permission"
|
||||||
},
|
},
|
||||||
"permission_customConfirmation": {
|
|
||||||
"message": "Εμφάνιση επιβεβαίωσης στο MetaMask.",
|
|
||||||
"description": "The description for the `snap_confirm` permission"
|
|
||||||
},
|
|
||||||
"permission_dialog": {
|
"permission_dialog": {
|
||||||
"message": "Εμφάνιση παραθύρων διαλόγου στο MetaMask.",
|
"message": "Εμφάνιση παραθύρων διαλόγου στο MetaMask.",
|
||||||
"description": "The description for the `snap_dialog` permission"
|
"description": "The description for the `snap_dialog` permission"
|
||||||
|
8
app/_locales/en/messages.json
generated
8
app/_locales/en/messages.json
generated
@ -2914,14 +2914,6 @@
|
|||||||
"message": "Allow the snap to perform actions that run periodically at fixed times, dates, or intervals. This can be used to trigger time-sensitive interactions or notifications.",
|
"message": "Allow the snap to perform actions that run periodically at fixed times, dates, or intervals. This can be used to trigger time-sensitive interactions or notifications.",
|
||||||
"description": "An extended description for the `snap_cronjob` permission"
|
"description": "An extended description for the `snap_cronjob` permission"
|
||||||
},
|
},
|
||||||
"permission_customConfirmation": {
|
|
||||||
"message": "Display a confirmation in MetaMask.",
|
|
||||||
"description": "The description for the `snap_confirm` permission"
|
|
||||||
},
|
|
||||||
"permission_customConfirmationDescription": {
|
|
||||||
"message": "Allow the snap to display MetaMask popups with custom text, and buttons to approve or reject an action.",
|
|
||||||
"description": "An extended description for the `snap_confirm` permission"
|
|
||||||
},
|
|
||||||
"permission_dialog": {
|
"permission_dialog": {
|
||||||
"message": "Display dialog windows in MetaMask.",
|
"message": "Display dialog windows in MetaMask.",
|
||||||
"description": "The description for the `snap_dialog` permission"
|
"description": "The description for the `snap_dialog` permission"
|
||||||
|
4
app/_locales/es/messages.json
generated
4
app/_locales/es/messages.json
generated
@ -2697,10 +2697,6 @@
|
|||||||
"message": "Programar y ejecutar acciones periódicas.",
|
"message": "Programar y ejecutar acciones periódicas.",
|
||||||
"description": "The description for the `snap_cronjob` permission"
|
"description": "The description for the `snap_cronjob` permission"
|
||||||
},
|
},
|
||||||
"permission_customConfirmation": {
|
|
||||||
"message": "Mostrar una confirmación en MetaMask.",
|
|
||||||
"description": "The description for the `snap_confirm` permission"
|
|
||||||
},
|
|
||||||
"permission_dialog": {
|
"permission_dialog": {
|
||||||
"message": "Mostrar ventanas de diálogo en MetaMask.",
|
"message": "Mostrar ventanas de diálogo en MetaMask.",
|
||||||
"description": "The description for the `snap_dialog` permission"
|
"description": "The description for the `snap_dialog` permission"
|
||||||
|
4
app/_locales/fr/messages.json
generated
4
app/_locales/fr/messages.json
generated
@ -2697,10 +2697,6 @@
|
|||||||
"message": "Planifiez et exécutez des actions périodiques.",
|
"message": "Planifiez et exécutez des actions périodiques.",
|
||||||
"description": "The description for the `snap_cronjob` permission"
|
"description": "The description for the `snap_cronjob` permission"
|
||||||
},
|
},
|
||||||
"permission_customConfirmation": {
|
|
||||||
"message": "Afficher une confirmation dans MetaMask.",
|
|
||||||
"description": "The description for the `snap_confirm` permission"
|
|
||||||
},
|
|
||||||
"permission_dialog": {
|
"permission_dialog": {
|
||||||
"message": "Afficher les boîtes de dialogue dans MetaMask.",
|
"message": "Afficher les boîtes de dialogue dans MetaMask.",
|
||||||
"description": "The description for the `snap_dialog` permission"
|
"description": "The description for the `snap_dialog` permission"
|
||||||
|
4
app/_locales/hi/messages.json
generated
4
app/_locales/hi/messages.json
generated
@ -2697,10 +2697,6 @@
|
|||||||
"message": "समय-समय पर आने वाले क्रियाओं को शेड्यूल और निष्पादित करें।",
|
"message": "समय-समय पर आने वाले क्रियाओं को शेड्यूल और निष्पादित करें।",
|
||||||
"description": "The description for the `snap_cronjob` permission"
|
"description": "The description for the `snap_cronjob` permission"
|
||||||
},
|
},
|
||||||
"permission_customConfirmation": {
|
|
||||||
"message": "MetaMask में पुष्टि को दर्शाएं।",
|
|
||||||
"description": "The description for the `snap_confirm` permission"
|
|
||||||
},
|
|
||||||
"permission_dialog": {
|
"permission_dialog": {
|
||||||
"message": "MetaMask में डायलॉग विंडो प्रदर्शित करें।",
|
"message": "MetaMask में डायलॉग विंडो प्रदर्शित करें।",
|
||||||
"description": "The description for the `snap_dialog` permission"
|
"description": "The description for the `snap_dialog` permission"
|
||||||
|
4
app/_locales/id/messages.json
generated
4
app/_locales/id/messages.json
generated
@ -2697,10 +2697,6 @@
|
|||||||
"message": "Jadwalkan dan lakukan tindakan berkala.",
|
"message": "Jadwalkan dan lakukan tindakan berkala.",
|
||||||
"description": "The description for the `snap_cronjob` permission"
|
"description": "The description for the `snap_cronjob` permission"
|
||||||
},
|
},
|
||||||
"permission_customConfirmation": {
|
|
||||||
"message": "Tampilkan konfirmasi di MetaMask.",
|
|
||||||
"description": "The description for the `snap_confirm` permission"
|
|
||||||
},
|
|
||||||
"permission_dialog": {
|
"permission_dialog": {
|
||||||
"message": "Tampilkan jendela dialog di MetaMask.",
|
"message": "Tampilkan jendela dialog di MetaMask.",
|
||||||
"description": "The description for the `snap_dialog` permission"
|
"description": "The description for the `snap_dialog` permission"
|
||||||
|
4
app/_locales/ja/messages.json
generated
4
app/_locales/ja/messages.json
generated
@ -2697,10 +2697,6 @@
|
|||||||
"message": "定期的なアクションのスケジュール設定と実行。",
|
"message": "定期的なアクションのスケジュール設定と実行。",
|
||||||
"description": "The description for the `snap_cronjob` permission"
|
"description": "The description for the `snap_cronjob` permission"
|
||||||
},
|
},
|
||||||
"permission_customConfirmation": {
|
|
||||||
"message": "MetaMask に確認を表示します。",
|
|
||||||
"description": "The description for the `snap_confirm` permission"
|
|
||||||
},
|
|
||||||
"permission_dialog": {
|
"permission_dialog": {
|
||||||
"message": "MetaMask にダイアログウィンドウを表示します。",
|
"message": "MetaMask にダイアログウィンドウを表示します。",
|
||||||
"description": "The description for the `snap_dialog` permission"
|
"description": "The description for the `snap_dialog` permission"
|
||||||
|
4
app/_locales/ko/messages.json
generated
4
app/_locales/ko/messages.json
generated
@ -2697,10 +2697,6 @@
|
|||||||
"message": "정기적 활동 예약 및 실행",
|
"message": "정기적 활동 예약 및 실행",
|
||||||
"description": "The description for the `snap_cronjob` permission"
|
"description": "The description for the `snap_cronjob` permission"
|
||||||
},
|
},
|
||||||
"permission_customConfirmation": {
|
|
||||||
"message": "MetaMask에 확인을 표시합니다.",
|
|
||||||
"description": "The description for the `snap_confirm` permission"
|
|
||||||
},
|
|
||||||
"permission_dialog": {
|
"permission_dialog": {
|
||||||
"message": "MetaMask 대화창 표시",
|
"message": "MetaMask 대화창 표시",
|
||||||
"description": "The description for the `snap_dialog` permission"
|
"description": "The description for the `snap_dialog` permission"
|
||||||
|
4
app/_locales/pt/messages.json
generated
4
app/_locales/pt/messages.json
generated
@ -2697,10 +2697,6 @@
|
|||||||
"message": "Agende e execute ações periódicas.",
|
"message": "Agende e execute ações periódicas.",
|
||||||
"description": "The description for the `snap_cronjob` permission"
|
"description": "The description for the `snap_cronjob` permission"
|
||||||
},
|
},
|
||||||
"permission_customConfirmation": {
|
|
||||||
"message": "Exibir uma confirmação na MetaMask.",
|
|
||||||
"description": "The description for the `snap_confirm` permission"
|
|
||||||
},
|
|
||||||
"permission_dialog": {
|
"permission_dialog": {
|
||||||
"message": "Exibir janelas de diálogo na MetaMask.",
|
"message": "Exibir janelas de diálogo na MetaMask.",
|
||||||
"description": "The description for the `snap_dialog` permission"
|
"description": "The description for the `snap_dialog` permission"
|
||||||
|
4
app/_locales/ru/messages.json
generated
4
app/_locales/ru/messages.json
generated
@ -2697,10 +2697,6 @@
|
|||||||
"message": "Планируйте и выполняйте периодические действия.",
|
"message": "Планируйте и выполняйте периодические действия.",
|
||||||
"description": "The description for the `snap_cronjob` permission"
|
"description": "The description for the `snap_cronjob` permission"
|
||||||
},
|
},
|
||||||
"permission_customConfirmation": {
|
|
||||||
"message": "Показать подтверждение в MetaMask.",
|
|
||||||
"description": "The description for the `snap_confirm` permission"
|
|
||||||
},
|
|
||||||
"permission_dialog": {
|
"permission_dialog": {
|
||||||
"message": "Отображение диалоговых окон в MetaMask.",
|
"message": "Отображение диалоговых окон в MetaMask.",
|
||||||
"description": "The description for the `snap_dialog` permission"
|
"description": "The description for the `snap_dialog` permission"
|
||||||
|
4
app/_locales/tl/messages.json
generated
4
app/_locales/tl/messages.json
generated
@ -2697,10 +2697,6 @@
|
|||||||
"message": "Mag-iskedyul at magsagawa ng mga pana-panahong mga aksyon.",
|
"message": "Mag-iskedyul at magsagawa ng mga pana-panahong mga aksyon.",
|
||||||
"description": "The description for the `snap_cronjob` permission"
|
"description": "The description for the `snap_cronjob` permission"
|
||||||
},
|
},
|
||||||
"permission_customConfirmation": {
|
|
||||||
"message": "Ipakita ang kumpirmasyon sa MetaMask.",
|
|
||||||
"description": "The description for the `snap_confirm` permission"
|
|
||||||
},
|
|
||||||
"permission_dialog": {
|
"permission_dialog": {
|
||||||
"message": "Ipakita ang mga dialog window sa MetaMask.",
|
"message": "Ipakita ang mga dialog window sa MetaMask.",
|
||||||
"description": "The description for the `snap_dialog` permission"
|
"description": "The description for the `snap_dialog` permission"
|
||||||
|
4
app/_locales/tr/messages.json
generated
4
app/_locales/tr/messages.json
generated
@ -2697,10 +2697,6 @@
|
|||||||
"message": "Periyodik eylemleri planla ve gerçekleştir.",
|
"message": "Periyodik eylemleri planla ve gerçekleştir.",
|
||||||
"description": "The description for the `snap_cronjob` permission"
|
"description": "The description for the `snap_cronjob` permission"
|
||||||
},
|
},
|
||||||
"permission_customConfirmation": {
|
|
||||||
"message": "MetaMask'te bir onay görüntüle.",
|
|
||||||
"description": "The description for the `snap_confirm` permission"
|
|
||||||
},
|
|
||||||
"permission_dialog": {
|
"permission_dialog": {
|
||||||
"message": "MetaMask'te iletişim kutusu pencerelerini göster.",
|
"message": "MetaMask'te iletişim kutusu pencerelerini göster.",
|
||||||
"description": "The description for the `snap_dialog` permission"
|
"description": "The description for the `snap_dialog` permission"
|
||||||
|
4
app/_locales/vi/messages.json
generated
4
app/_locales/vi/messages.json
generated
@ -2697,10 +2697,6 @@
|
|||||||
"message": "Lên lịch và thực hiện các hành động theo định kỳ.",
|
"message": "Lên lịch và thực hiện các hành động theo định kỳ.",
|
||||||
"description": "The description for the `snap_cronjob` permission"
|
"description": "The description for the `snap_cronjob` permission"
|
||||||
},
|
},
|
||||||
"permission_customConfirmation": {
|
|
||||||
"message": "Hiển thị xác nhận trong MetaMask.",
|
|
||||||
"description": "The description for the `snap_confirm` permission"
|
|
||||||
},
|
|
||||||
"permission_dialog": {
|
"permission_dialog": {
|
||||||
"message": "Hiển thị cửa sổ hộp thoại trong MetaMask.",
|
"message": "Hiển thị cửa sổ hộp thoại trong MetaMask.",
|
||||||
"description": "The description for the `snap_dialog` permission"
|
"description": "The description for the `snap_dialog` permission"
|
||||||
|
4
app/_locales/zh_CN/messages.json
generated
4
app/_locales/zh_CN/messages.json
generated
@ -2697,10 +2697,6 @@
|
|||||||
"message": "规划并执行定期操作。",
|
"message": "规划并执行定期操作。",
|
||||||
"description": "The description for the `snap_cronjob` permission"
|
"description": "The description for the `snap_cronjob` permission"
|
||||||
},
|
},
|
||||||
"permission_customConfirmation": {
|
|
||||||
"message": "在MetaMask中显示确认。",
|
|
||||||
"description": "The description for the `snap_confirm` permission"
|
|
||||||
},
|
|
||||||
"permission_dialog": {
|
"permission_dialog": {
|
||||||
"message": "在 MetaMask 中显示对话框窗口。",
|
"message": "在 MetaMask 中显示对话框窗口。",
|
||||||
"description": "The description for the `snap_dialog` permission"
|
"description": "The description for the `snap_dialog` permission"
|
||||||
|
@ -278,7 +278,7 @@ describe('DetectTokensController', function () {
|
|||||||
|
|
||||||
it('should be called on every polling period', async function () {
|
it('should be called on every polling period', async function () {
|
||||||
const clock = sandbox.useFakeTimers();
|
const clock = sandbox.useFakeTimers();
|
||||||
network.setProviderType(NETWORK_TYPES.MAINNET);
|
await network.setProviderType(NETWORK_TYPES.MAINNET);
|
||||||
const controller = new DetectTokensController({
|
const controller = new DetectTokensController({
|
||||||
preferences,
|
preferences,
|
||||||
network,
|
network,
|
||||||
@ -304,7 +304,7 @@ describe('DetectTokensController', function () {
|
|||||||
|
|
||||||
it('should not check and add tokens while on unsupported networks', async function () {
|
it('should not check and add tokens while on unsupported networks', async function () {
|
||||||
sandbox.useFakeTimers();
|
sandbox.useFakeTimers();
|
||||||
network.setProviderType(NETWORK_TYPES.SEPOLIA);
|
await network.setProviderType(NETWORK_TYPES.SEPOLIA);
|
||||||
const tokenListMessengerSepolia = new ControllerMessenger().getRestricted({
|
const tokenListMessengerSepolia = new ControllerMessenger().getRestricted({
|
||||||
name: 'TokenListController',
|
name: 'TokenListController',
|
||||||
});
|
});
|
||||||
@ -337,7 +337,7 @@ describe('DetectTokensController', function () {
|
|||||||
|
|
||||||
it('should skip adding tokens listed in ignoredTokens array', async function () {
|
it('should skip adding tokens listed in ignoredTokens array', async function () {
|
||||||
sandbox.useFakeTimers();
|
sandbox.useFakeTimers();
|
||||||
network.setProviderType(NETWORK_TYPES.MAINNET);
|
await network.setProviderType(NETWORK_TYPES.MAINNET);
|
||||||
const controller = new DetectTokensController({
|
const controller = new DetectTokensController({
|
||||||
preferences,
|
preferences,
|
||||||
network,
|
network,
|
||||||
@ -388,7 +388,7 @@ describe('DetectTokensController', function () {
|
|||||||
|
|
||||||
it('should check and add tokens while on supported networks', async function () {
|
it('should check and add tokens while on supported networks', async function () {
|
||||||
sandbox.useFakeTimers();
|
sandbox.useFakeTimers();
|
||||||
network.setProviderType(NETWORK_TYPES.MAINNET);
|
await network.setProviderType(NETWORK_TYPES.MAINNET);
|
||||||
const controller = new DetectTokensController({
|
const controller = new DetectTokensController({
|
||||||
preferences,
|
preferences,
|
||||||
network,
|
network,
|
||||||
@ -483,7 +483,7 @@ describe('DetectTokensController', function () {
|
|||||||
|
|
||||||
it('should not trigger detect new tokens when not unlocked', async function () {
|
it('should not trigger detect new tokens when not unlocked', async function () {
|
||||||
const clock = sandbox.useFakeTimers();
|
const clock = sandbox.useFakeTimers();
|
||||||
network.setProviderType(NETWORK_TYPES.MAINNET);
|
await network.setProviderType(NETWORK_TYPES.MAINNET);
|
||||||
const controller = new DetectTokensController({
|
const controller = new DetectTokensController({
|
||||||
preferences,
|
preferences,
|
||||||
network,
|
network,
|
||||||
@ -504,7 +504,7 @@ describe('DetectTokensController', function () {
|
|||||||
|
|
||||||
it('should not trigger detect new tokens when not open', async function () {
|
it('should not trigger detect new tokens when not open', async function () {
|
||||||
const clock = sandbox.useFakeTimers();
|
const clock = sandbox.useFakeTimers();
|
||||||
network.setProviderType(NETWORK_TYPES.MAINNET);
|
await network.setProviderType(NETWORK_TYPES.MAINNET);
|
||||||
const controller = new DetectTokensController({
|
const controller = new DetectTokensController({
|
||||||
preferences,
|
preferences,
|
||||||
network,
|
network,
|
||||||
|
@ -730,7 +730,7 @@ describe('NetworkController', () => {
|
|||||||
await expect(async () => {
|
await expect(async () => {
|
||||||
await controller.initializeProvider();
|
await controller.initializeProvider();
|
||||||
}).rejects.toThrow(
|
}).rejects.toThrow(
|
||||||
'NetworkController - _configureProvider - unknown type "undefined"',
|
'NetworkController - #configureProvider - unknown type "undefined"',
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -1135,7 +1135,7 @@ describe('NetworkController', () => {
|
|||||||
});
|
});
|
||||||
expect(oldChainIdResult).toBe('0x1337');
|
expect(oldChainIdResult).toBe('0x1337');
|
||||||
|
|
||||||
controller.setProviderType(networkType);
|
await controller.setProviderType(networkType);
|
||||||
const promisifiedSendAsync2 = promisify(provider.sendAsync).bind(
|
const promisifiedSendAsync2 = promisify(provider.sendAsync).bind(
|
||||||
provider,
|
provider,
|
||||||
);
|
);
|
||||||
@ -2652,21 +2652,9 @@ describe('NetworkController', () => {
|
|||||||
network1.mockEssentialRpcCalls({
|
network1.mockEssentialRpcCalls({
|
||||||
eth_getBlockByNumber: {
|
eth_getBlockByNumber: {
|
||||||
beforeCompleting: async () => {
|
beforeCompleting: async () => {
|
||||||
await waitForPublishedEvents({
|
await controller.setProviderType(
|
||||||
messenger: unrestrictedMessenger,
|
anotherNetwork.networkType,
|
||||||
eventType: NetworkControllerEventType.NetworkDidChange,
|
);
|
||||||
operation: async () => {
|
|
||||||
await waitForStateChanges({
|
|
||||||
controller,
|
|
||||||
propertyPath: ['networkStatus'],
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType(
|
|
||||||
anotherNetwork.networkType,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -3513,13 +3501,7 @@ describe('NetworkController', () => {
|
|||||||
{
|
{
|
||||||
response: SUCCESSFUL_NET_VERSION_RESPONSE,
|
response: SUCCESSFUL_NET_VERSION_RESPONSE,
|
||||||
beforeCompleting: async () => {
|
beforeCompleting: async () => {
|
||||||
await waitForStateChanges({
|
await controller.setProviderType('goerli');
|
||||||
controller,
|
|
||||||
propertyPath: ['networkStatus'],
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType('goerli');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -3618,13 +3600,7 @@ describe('NetworkController', () => {
|
|||||||
result: '111',
|
result: '111',
|
||||||
},
|
},
|
||||||
beforeCompleting: async () => {
|
beforeCompleting: async () => {
|
||||||
await waitForStateChanges({
|
await controller.setProviderType('goerli');
|
||||||
controller,
|
|
||||||
propertyPath: ['networkStatus'],
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType('goerli');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -3675,13 +3651,7 @@ describe('NetworkController', () => {
|
|||||||
net_version: {
|
net_version: {
|
||||||
response: SUCCESSFUL_NET_VERSION_RESPONSE,
|
response: SUCCESSFUL_NET_VERSION_RESPONSE,
|
||||||
beforeCompleting: async () => {
|
beforeCompleting: async () => {
|
||||||
await waitForStateChanges({
|
await controller.setProviderType('goerli');
|
||||||
controller,
|
|
||||||
propertyPath: ['networkDetails'],
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType('goerli');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -3738,13 +3708,7 @@ describe('NetworkController', () => {
|
|||||||
network1.mockEssentialRpcCalls({
|
network1.mockEssentialRpcCalls({
|
||||||
net_version: {
|
net_version: {
|
||||||
beforeCompleting: async () => {
|
beforeCompleting: async () => {
|
||||||
await waitForStateChanges({
|
await controller.setProviderType('goerli');
|
||||||
controller,
|
|
||||||
propertyPath: ['networkDetails'],
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType('goerli');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -3841,13 +3805,7 @@ describe('NetworkController', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
beforeCompleting: async () => {
|
beforeCompleting: async () => {
|
||||||
await waitForStateChanges({
|
await controller.setProviderType('goerli');
|
||||||
controller,
|
|
||||||
propertyPath: ['networkStatus'],
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType('goerli');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -3904,13 +3862,7 @@ describe('NetworkController', () => {
|
|||||||
network1.mockEssentialRpcCalls({
|
network1.mockEssentialRpcCalls({
|
||||||
eth_getBlockByNumber: {
|
eth_getBlockByNumber: {
|
||||||
beforeCompleting: async () => {
|
beforeCompleting: async () => {
|
||||||
await waitForStateChanges({
|
await controller.setProviderType('goerli');
|
||||||
controller,
|
|
||||||
propertyPath: ['networkStatus'],
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType('goerli');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
net_version: {
|
net_version: {
|
||||||
@ -3965,13 +3917,7 @@ describe('NetworkController', () => {
|
|||||||
latestBlock: POST_1559_BLOCK,
|
latestBlock: POST_1559_BLOCK,
|
||||||
eth_getBlockByNumber: {
|
eth_getBlockByNumber: {
|
||||||
beforeCompleting: async () => {
|
beforeCompleting: async () => {
|
||||||
await waitForStateChanges({
|
await controller.setProviderType('goerli');
|
||||||
controller,
|
|
||||||
propertyPath: ['networkDetails'],
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType('goerli');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -4028,13 +3974,7 @@ describe('NetworkController', () => {
|
|||||||
network1.mockEssentialRpcCalls({
|
network1.mockEssentialRpcCalls({
|
||||||
eth_getBlockByNumber: {
|
eth_getBlockByNumber: {
|
||||||
beforeCompleting: async () => {
|
beforeCompleting: async () => {
|
||||||
await waitForStateChanges({
|
await controller.setProviderType('goerli');
|
||||||
controller,
|
|
||||||
propertyPath: ['networkDetails'],
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType('goerli');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -4629,7 +4569,7 @@ describe('NetworkController', () => {
|
|||||||
});
|
});
|
||||||
network.mockEssentialRpcCalls();
|
network.mockEssentialRpcCalls();
|
||||||
|
|
||||||
controller.setProviderType(networkType);
|
await controller.setProviderType(networkType);
|
||||||
|
|
||||||
expect(controller.store.getState().provider).toStrictEqual({
|
expect(controller.store.getState().provider).toStrictEqual({
|
||||||
type: networkType,
|
type: networkType,
|
||||||
@ -4662,6 +4602,8 @@ describe('NetworkController', () => {
|
|||||||
messenger: unrestrictedMessenger,
|
messenger: unrestrictedMessenger,
|
||||||
eventType: NetworkControllerEventType.NetworkWillChange,
|
eventType: NetworkControllerEventType.NetworkWillChange,
|
||||||
operation: () => {
|
operation: () => {
|
||||||
|
// Intentionally not awaited because we're capturing an event
|
||||||
|
// emitted partway through the operation
|
||||||
controller.setProviderType(networkType);
|
controller.setProviderType(networkType);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -4714,6 +4656,8 @@ describe('NetworkController', () => {
|
|||||||
// happens before networkDidChange
|
// happens before networkDidChange
|
||||||
count: 1,
|
count: 1,
|
||||||
operation: () => {
|
operation: () => {
|
||||||
|
// Intentionally not awaited because we're checking the state
|
||||||
|
// partway through the operation
|
||||||
controller.setProviderType(networkType);
|
controller.setProviderType(networkType);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -4767,6 +4711,8 @@ describe('NetworkController', () => {
|
|||||||
// happens before networkDidChange
|
// happens before networkDidChange
|
||||||
count: 1,
|
count: 1,
|
||||||
operation: () => {
|
operation: () => {
|
||||||
|
// Intentionally not awaited because we're checking the state
|
||||||
|
// partway through the operation
|
||||||
controller.setProviderType(networkType);
|
controller.setProviderType(networkType);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -4786,7 +4732,7 @@ describe('NetworkController', () => {
|
|||||||
});
|
});
|
||||||
network.mockEssentialRpcCalls();
|
network.mockEssentialRpcCalls();
|
||||||
|
|
||||||
controller.setProviderType(networkType);
|
await controller.setProviderType(networkType);
|
||||||
|
|
||||||
const { provider } = controller.getProviderAndBlockTracker();
|
const { provider } = controller.getProviderAndBlockTracker();
|
||||||
assert(provider, 'Provider is somehow unset');
|
assert(provider, 'Provider is somehow unset');
|
||||||
@ -4813,7 +4759,7 @@ describe('NetworkController', () => {
|
|||||||
|
|
||||||
const { provider: providerBefore } =
|
const { provider: providerBefore } =
|
||||||
controller.getProviderAndBlockTracker();
|
controller.getProviderAndBlockTracker();
|
||||||
controller.setProviderType(networkType);
|
await controller.setProviderType(networkType);
|
||||||
const { provider: providerAfter } =
|
const { provider: providerAfter } =
|
||||||
controller.getProviderAndBlockTracker();
|
controller.getProviderAndBlockTracker();
|
||||||
|
|
||||||
@ -4836,8 +4782,8 @@ describe('NetworkController', () => {
|
|||||||
const networkDidChange = await waitForPublishedEvents({
|
const networkDidChange = await waitForPublishedEvents({
|
||||||
messenger: unrestrictedMessenger,
|
messenger: unrestrictedMessenger,
|
||||||
eventType: NetworkControllerEventType.NetworkDidChange,
|
eventType: NetworkControllerEventType.NetworkDidChange,
|
||||||
operation: () => {
|
operation: async () => {
|
||||||
controller.setProviderType(networkType);
|
await controller.setProviderType(networkType);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -4872,7 +4818,7 @@ describe('NetworkController', () => {
|
|||||||
eventType: NetworkControllerEventType.InfuraIsBlocked,
|
eventType: NetworkControllerEventType.InfuraIsBlocked,
|
||||||
});
|
});
|
||||||
|
|
||||||
controller.setProviderType(networkType);
|
await controller.setProviderType(networkType);
|
||||||
|
|
||||||
expect(await promiseForNoInfuraIsUnblockedEvents).toBeTruthy();
|
expect(await promiseForNoInfuraIsUnblockedEvents).toBeTruthy();
|
||||||
expect(await promiseForInfuraIsBlocked).toBeTruthy();
|
expect(await promiseForInfuraIsBlocked).toBeTruthy();
|
||||||
@ -4891,13 +4837,7 @@ describe('NetworkController', () => {
|
|||||||
latestBlock: BLOCK,
|
latestBlock: BLOCK,
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitForStateChanges({
|
await controller.setProviderType(networkType);
|
||||||
controller,
|
|
||||||
propertyPath: ['networkStatus'],
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType(networkType);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(controller.store.getState().networkStatus).toBe('available');
|
expect(controller.store.getState().networkStatus).toBe('available');
|
||||||
});
|
});
|
||||||
@ -4921,16 +4861,7 @@ describe('NetworkController', () => {
|
|||||||
latestBlock: POST_1559_BLOCK,
|
latestBlock: POST_1559_BLOCK,
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitForStateChanges({
|
await controller.setProviderType(networkType);
|
||||||
controller,
|
|
||||||
propertyPath: ['networkDetails'],
|
|
||||||
// setProviderType clears networkDetails first, and then updates
|
|
||||||
// it to what we expect it to be
|
|
||||||
count: 2,
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType(networkType);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(controller.store.getState().networkDetails).toStrictEqual({
|
expect(controller.store.getState().networkDetails).toStrictEqual({
|
||||||
EIPS: {
|
EIPS: {
|
||||||
@ -4946,7 +4877,7 @@ describe('NetworkController', () => {
|
|||||||
describe('given a type of "rpc"', () => {
|
describe('given a type of "rpc"', () => {
|
||||||
it('throws', async () => {
|
it('throws', async () => {
|
||||||
await withController(async ({ controller }) => {
|
await withController(async ({ controller }) => {
|
||||||
expect(() => controller.setProviderType('rpc')).toThrow(
|
await expect(() => controller.setProviderType('rpc')).rejects.toThrow(
|
||||||
new Error(
|
new Error(
|
||||||
'NetworkController - cannot call "setProviderType" with type "rpc". Use "setActiveNetwork"',
|
'NetworkController - cannot call "setProviderType" with type "rpc". Use "setActiveNetwork"',
|
||||||
),
|
),
|
||||||
@ -4958,7 +4889,9 @@ describe('NetworkController', () => {
|
|||||||
describe('given an invalid Infura network name', () => {
|
describe('given an invalid Infura network name', () => {
|
||||||
it('throws', async () => {
|
it('throws', async () => {
|
||||||
await withController(async ({ controller }) => {
|
await withController(async ({ controller }) => {
|
||||||
expect(() => controller.setProviderType('sadlflaksdj')).toThrow(
|
await expect(() =>
|
||||||
|
controller.setProviderType('sadlflaksdj'),
|
||||||
|
).rejects.toThrow(
|
||||||
new Error('Unknown Infura provider type "sadlflaksdj".'),
|
new Error('Unknown Infura provider type "sadlflaksdj".'),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -4992,6 +4925,8 @@ describe('NetworkController', () => {
|
|||||||
messenger: unrestrictedMessenger,
|
messenger: unrestrictedMessenger,
|
||||||
eventType: NetworkControllerEventType.NetworkWillChange,
|
eventType: NetworkControllerEventType.NetworkWillChange,
|
||||||
operation: () => {
|
operation: () => {
|
||||||
|
// Intentionally not awaited because we want to capture an
|
||||||
|
// event emitted partway throught this operation
|
||||||
controller.resetConnection();
|
controller.resetConnection();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -5032,6 +4967,8 @@ describe('NetworkController', () => {
|
|||||||
// happens before networkDidChange
|
// happens before networkDidChange
|
||||||
count: 1,
|
count: 1,
|
||||||
operation: () => {
|
operation: () => {
|
||||||
|
// Intentionally not awaited because we want to capture a
|
||||||
|
// state change made partway through the operation
|
||||||
controller.resetConnection();
|
controller.resetConnection();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -5074,6 +5011,8 @@ describe('NetworkController', () => {
|
|||||||
// happens before networkDidChange
|
// happens before networkDidChange
|
||||||
count: 1,
|
count: 1,
|
||||||
operation: () => {
|
operation: () => {
|
||||||
|
// Intentionally not awaited because we want to check state
|
||||||
|
// partway through the operation
|
||||||
controller.resetConnection();
|
controller.resetConnection();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -5101,7 +5040,7 @@ describe('NetworkController', () => {
|
|||||||
async ({ controller, network }) => {
|
async ({ controller, network }) => {
|
||||||
network.mockEssentialRpcCalls();
|
network.mockEssentialRpcCalls();
|
||||||
|
|
||||||
controller.resetConnection();
|
await controller.resetConnection();
|
||||||
|
|
||||||
const { provider } = controller.getProviderAndBlockTracker();
|
const { provider } = controller.getProviderAndBlockTracker();
|
||||||
assert(provider, 'Provider is somehow unset');
|
assert(provider, 'Provider is somehow unset');
|
||||||
@ -5140,7 +5079,7 @@ describe('NetworkController', () => {
|
|||||||
|
|
||||||
const { provider: providerBefore } =
|
const { provider: providerBefore } =
|
||||||
controller.getProviderAndBlockTracker();
|
controller.getProviderAndBlockTracker();
|
||||||
controller.resetConnection();
|
await controller.resetConnection();
|
||||||
const { provider: providerAfter } =
|
const { provider: providerAfter } =
|
||||||
controller.getProviderAndBlockTracker();
|
controller.getProviderAndBlockTracker();
|
||||||
|
|
||||||
@ -5171,8 +5110,8 @@ describe('NetworkController', () => {
|
|||||||
const networkDidChange = await waitForPublishedEvents({
|
const networkDidChange = await waitForPublishedEvents({
|
||||||
messenger: unrestrictedMessenger,
|
messenger: unrestrictedMessenger,
|
||||||
eventType: NetworkControllerEventType.NetworkDidChange,
|
eventType: NetworkControllerEventType.NetworkDidChange,
|
||||||
operation: () => {
|
operation: async () => {
|
||||||
controller.resetConnection();
|
await controller.resetConnection();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -5214,7 +5153,7 @@ describe('NetworkController', () => {
|
|||||||
eventType: NetworkControllerEventType.InfuraIsBlocked,
|
eventType: NetworkControllerEventType.InfuraIsBlocked,
|
||||||
});
|
});
|
||||||
|
|
||||||
controller.resetConnection();
|
await controller.resetConnection();
|
||||||
|
|
||||||
expect(await promiseForNoInfuraIsUnblockedEvents).toBeTruthy();
|
expect(await promiseForNoInfuraIsUnblockedEvents).toBeTruthy();
|
||||||
expect(await promiseForInfuraIsBlocked).toBeTruthy();
|
expect(await promiseForInfuraIsBlocked).toBeTruthy();
|
||||||
@ -5237,13 +5176,7 @@ describe('NetworkController', () => {
|
|||||||
async ({ controller, network }) => {
|
async ({ controller, network }) => {
|
||||||
network.mockEssentialRpcCalls();
|
network.mockEssentialRpcCalls();
|
||||||
|
|
||||||
await waitForStateChanges({
|
await controller.resetConnection();
|
||||||
controller,
|
|
||||||
propertyPath: ['networkStatus'],
|
|
||||||
operation: () => {
|
|
||||||
controller.resetConnection();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(controller.store.getState().networkStatus).toBe(
|
expect(controller.store.getState().networkStatus).toBe(
|
||||||
'available',
|
'available',
|
||||||
@ -5275,13 +5208,7 @@ describe('NetworkController', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitForStateChanges({
|
await controller.resetConnection();
|
||||||
controller,
|
|
||||||
propertyPath: ['networkDetails'],
|
|
||||||
operation: () => {
|
|
||||||
controller.resetConnection();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(controller.store.getState().networkDetails).toStrictEqual({
|
expect(controller.store.getState().networkDetails).toStrictEqual({
|
||||||
EIPS: {
|
EIPS: {
|
||||||
@ -5326,6 +5253,8 @@ describe('NetworkController', () => {
|
|||||||
messenger: unrestrictedMessenger,
|
messenger: unrestrictedMessenger,
|
||||||
eventType: NetworkControllerEventType.NetworkWillChange,
|
eventType: NetworkControllerEventType.NetworkWillChange,
|
||||||
operation: () => {
|
operation: () => {
|
||||||
|
// Intentionally not awaited because we're capturing an event
|
||||||
|
// emitted partway through the operation
|
||||||
controller.resetConnection();
|
controller.resetConnection();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -5376,6 +5305,8 @@ describe('NetworkController', () => {
|
|||||||
// before networkDidChange
|
// before networkDidChange
|
||||||
count: 1,
|
count: 1,
|
||||||
operation: () => {
|
operation: () => {
|
||||||
|
// Intentionally not awaited because we want to check state
|
||||||
|
// partway through the operation
|
||||||
controller.resetConnection();
|
controller.resetConnection();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -5426,6 +5357,8 @@ describe('NetworkController', () => {
|
|||||||
// before networkDidChange
|
// before networkDidChange
|
||||||
count: 1,
|
count: 1,
|
||||||
operation: () => {
|
operation: () => {
|
||||||
|
// Intentionally not awaited because we want to check state
|
||||||
|
// partway through the operation
|
||||||
controller.resetConnection();
|
controller.resetConnection();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -5461,7 +5394,7 @@ describe('NetworkController', () => {
|
|||||||
async ({ controller, network }) => {
|
async ({ controller, network }) => {
|
||||||
network.mockEssentialRpcCalls();
|
network.mockEssentialRpcCalls();
|
||||||
|
|
||||||
controller.resetConnection();
|
await controller.resetConnection();
|
||||||
|
|
||||||
const { provider } = controller.getProviderAndBlockTracker();
|
const { provider } = controller.getProviderAndBlockTracker();
|
||||||
assert(provider, 'Provider is somehow unset');
|
assert(provider, 'Provider is somehow unset');
|
||||||
@ -5508,12 +5441,7 @@ describe('NetworkController', () => {
|
|||||||
|
|
||||||
const { provider: providerBefore } =
|
const { provider: providerBefore } =
|
||||||
controller.getProviderAndBlockTracker();
|
controller.getProviderAndBlockTracker();
|
||||||
await waitForLookupNetworkToComplete({
|
await controller.resetConnection();
|
||||||
controller,
|
|
||||||
operation: () => {
|
|
||||||
controller.resetConnection();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const { provider: providerAfter } =
|
const { provider: providerAfter } =
|
||||||
controller.getProviderAndBlockTracker();
|
controller.getProviderAndBlockTracker();
|
||||||
|
|
||||||
@ -5552,8 +5480,8 @@ describe('NetworkController', () => {
|
|||||||
const networkDidChange = await waitForPublishedEvents({
|
const networkDidChange = await waitForPublishedEvents({
|
||||||
messenger: unrestrictedMessenger,
|
messenger: unrestrictedMessenger,
|
||||||
eventType: NetworkControllerEventType.NetworkDidChange,
|
eventType: NetworkControllerEventType.NetworkDidChange,
|
||||||
operation: () => {
|
operation: async () => {
|
||||||
controller.resetConnection();
|
await controller.resetConnection();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -5592,8 +5520,8 @@ describe('NetworkController', () => {
|
|||||||
const infuraIsUnblocked = await waitForPublishedEvents({
|
const infuraIsUnblocked = await waitForPublishedEvents({
|
||||||
messenger: unrestrictedMessenger,
|
messenger: unrestrictedMessenger,
|
||||||
eventType: NetworkControllerEventType.InfuraIsUnblocked,
|
eventType: NetworkControllerEventType.InfuraIsUnblocked,
|
||||||
operation: () => {
|
operation: async () => {
|
||||||
controller.resetConnection();
|
await controller.resetConnection();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -5630,13 +5558,7 @@ describe('NetworkController', () => {
|
|||||||
});
|
});
|
||||||
expect(controller.store.getState().networkStatus).toBe('unknown');
|
expect(controller.store.getState().networkStatus).toBe('unknown');
|
||||||
|
|
||||||
await waitForStateChanges({
|
await controller.resetConnection();
|
||||||
controller,
|
|
||||||
propertyPath: ['networkStatus'],
|
|
||||||
operation: () => {
|
|
||||||
controller.resetConnection();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(controller.store.getState().networkStatus).toBe('available');
|
expect(controller.store.getState().networkStatus).toBe('available');
|
||||||
},
|
},
|
||||||
@ -5674,13 +5596,7 @@ describe('NetworkController', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitForStateChanges({
|
await controller.resetConnection();
|
||||||
controller,
|
|
||||||
propertyPath: ['networkDetails'],
|
|
||||||
operation: () => {
|
|
||||||
controller.resetConnection();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(controller.store.getState().networkDetails).toStrictEqual({
|
expect(controller.store.getState().networkDetails).toStrictEqual({
|
||||||
EIPS: {
|
EIPS: {
|
||||||
@ -6393,12 +6309,7 @@ describe('NetworkController', () => {
|
|||||||
currentNetwork.mockEssentialRpcCalls();
|
currentNetwork.mockEssentialRpcCalls();
|
||||||
previousNetwork.mockEssentialRpcCalls();
|
previousNetwork.mockEssentialRpcCalls();
|
||||||
|
|
||||||
await waitForLookupNetworkToComplete({
|
await controller.setProviderType('goerli');
|
||||||
controller,
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType('goerli');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(controller.store.getState().provider).toStrictEqual({
|
expect(controller.store.getState().provider).toStrictEqual({
|
||||||
type: 'goerli',
|
type: 'goerli',
|
||||||
rpcUrl: '',
|
rpcUrl: '',
|
||||||
@ -6466,12 +6377,7 @@ describe('NetworkController', () => {
|
|||||||
});
|
});
|
||||||
currentNetwork.mockEssentialRpcCalls();
|
currentNetwork.mockEssentialRpcCalls();
|
||||||
previousNetwork.mockEssentialRpcCalls();
|
previousNetwork.mockEssentialRpcCalls();
|
||||||
await waitForLookupNetworkToComplete({
|
await controller.setProviderType('goerli');
|
||||||
controller,
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType('goerli');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await waitForLookupNetworkToComplete({
|
await waitForLookupNetworkToComplete({
|
||||||
controller,
|
controller,
|
||||||
@ -6518,12 +6424,7 @@ describe('NetworkController', () => {
|
|||||||
currentNetwork.mockEssentialRpcCalls();
|
currentNetwork.mockEssentialRpcCalls();
|
||||||
previousNetwork.mockEssentialRpcCalls();
|
previousNetwork.mockEssentialRpcCalls();
|
||||||
|
|
||||||
await waitForLookupNetworkToComplete({
|
await controller.setProviderType('goerli');
|
||||||
controller,
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType('goerli');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(controller.store.getState().networkStatus).toBe('available');
|
expect(controller.store.getState().networkStatus).toBe('available');
|
||||||
|
|
||||||
await waitForLookupNetworkToComplete({
|
await waitForLookupNetworkToComplete({
|
||||||
@ -6579,12 +6480,7 @@ describe('NetworkController', () => {
|
|||||||
});
|
});
|
||||||
previousNetwork.mockEssentialRpcCalls();
|
previousNetwork.mockEssentialRpcCalls();
|
||||||
|
|
||||||
await waitForLookupNetworkToComplete({
|
await controller.setProviderType('goerli');
|
||||||
controller,
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType('goerli');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(controller.store.getState().networkDetails).toStrictEqual({
|
expect(controller.store.getState().networkDetails).toStrictEqual({
|
||||||
EIPS: {
|
EIPS: {
|
||||||
1559: true,
|
1559: true,
|
||||||
@ -6645,12 +6541,7 @@ describe('NetworkController', () => {
|
|||||||
});
|
});
|
||||||
currentNetwork.mockEssentialRpcCalls();
|
currentNetwork.mockEssentialRpcCalls();
|
||||||
previousNetwork.mockEssentialRpcCalls();
|
previousNetwork.mockEssentialRpcCalls();
|
||||||
await waitForLookupNetworkToComplete({
|
await controller.setProviderType('goerli');
|
||||||
controller,
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType('goerli');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await waitForLookupNetworkToComplete({
|
await waitForLookupNetworkToComplete({
|
||||||
controller,
|
controller,
|
||||||
@ -6700,12 +6591,7 @@ describe('NetworkController', () => {
|
|||||||
});
|
});
|
||||||
currentNetwork.mockEssentialRpcCalls();
|
currentNetwork.mockEssentialRpcCalls();
|
||||||
previousNetwork.mockEssentialRpcCalls();
|
previousNetwork.mockEssentialRpcCalls();
|
||||||
await waitForLookupNetworkToComplete({
|
await controller.setProviderType('goerli');
|
||||||
controller,
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType('goerli');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { provider: providerBefore } =
|
const { provider: providerBefore } =
|
||||||
controller.getProviderAndBlockTracker();
|
controller.getProviderAndBlockTracker();
|
||||||
@ -6754,12 +6640,7 @@ describe('NetworkController', () => {
|
|||||||
currentNetwork.mockEssentialRpcCalls();
|
currentNetwork.mockEssentialRpcCalls();
|
||||||
previousNetwork.mockEssentialRpcCalls();
|
previousNetwork.mockEssentialRpcCalls();
|
||||||
|
|
||||||
await waitForLookupNetworkToComplete({
|
await controller.setProviderType('goerli');
|
||||||
controller,
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType('goerli');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await waitForLookupNetworkToComplete({
|
await waitForLookupNetworkToComplete({
|
||||||
controller,
|
controller,
|
||||||
@ -6809,12 +6690,7 @@ describe('NetworkController', () => {
|
|||||||
currentNetwork.mockEssentialRpcCalls();
|
currentNetwork.mockEssentialRpcCalls();
|
||||||
previousNetwork.mockEssentialRpcCalls();
|
previousNetwork.mockEssentialRpcCalls();
|
||||||
|
|
||||||
await waitForLookupNetworkToComplete({
|
await controller.setProviderType('goerli');
|
||||||
controller,
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType('goerli');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await waitForLookupNetworkToComplete({
|
await waitForLookupNetworkToComplete({
|
||||||
controller,
|
controller,
|
||||||
@ -6869,12 +6745,7 @@ describe('NetworkController', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitForLookupNetworkToComplete({
|
await controller.setProviderType('goerli');
|
||||||
controller,
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType('goerli');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(controller.store.getState().networkStatus).toBe('available');
|
expect(controller.store.getState().networkStatus).toBe('available');
|
||||||
|
|
||||||
await waitForLookupNetworkToComplete({
|
await waitForLookupNetworkToComplete({
|
||||||
@ -6919,12 +6790,7 @@ describe('NetworkController', () => {
|
|||||||
latestBlock: POST_1559_BLOCK,
|
latestBlock: POST_1559_BLOCK,
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitForLookupNetworkToComplete({
|
await controller.setProviderType('goerli');
|
||||||
controller,
|
|
||||||
operation: () => {
|
|
||||||
controller.setProviderType('goerli');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(controller.store.getState().networkDetails).toStrictEqual({
|
expect(controller.store.getState().networkDetails).toStrictEqual({
|
||||||
EIPS: {
|
EIPS: {
|
||||||
1559: false,
|
1559: false,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { strict as assert } from 'assert';
|
import { strict as assert } from 'assert';
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
import { ComposedStore, ObservableStore } from '@metamask/obs-store';
|
import { ObservableStore } from '@metamask/obs-store';
|
||||||
import log from 'loglevel';
|
import log from 'loglevel';
|
||||||
import {
|
import {
|
||||||
createSwappableProxy,
|
createSwappableProxy,
|
||||||
@ -379,6 +379,21 @@ function buildDefaultNetworkConfigurationsState(): NetworkConfigurations {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the default state for the network controller.
|
||||||
|
*
|
||||||
|
* @returns The default network controller state.
|
||||||
|
*/
|
||||||
|
function buildDefaultState() {
|
||||||
|
return {
|
||||||
|
provider: buildDefaultProviderConfigState(),
|
||||||
|
networkId: buildDefaultNetworkIdState(),
|
||||||
|
networkStatus: buildDefaultNetworkStatusState(),
|
||||||
|
networkDetails: buildDefaultNetworkDetailsState(),
|
||||||
|
networkConfigurations: buildDefaultNetworkConfigurationsState(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the given argument is a type that our Infura middleware
|
* Returns whether the given argument is a type that our Infura middleware
|
||||||
* recognizes. We can't calculate this inline because the usual type of `type`,
|
* recognizes. We can't calculate this inline because the usual type of `type`,
|
||||||
@ -412,12 +427,7 @@ export class NetworkController extends EventEmitter {
|
|||||||
/**
|
/**
|
||||||
* The messenger that NetworkController uses to publish events.
|
* The messenger that NetworkController uses to publish events.
|
||||||
*/
|
*/
|
||||||
messenger: NetworkControllerMessenger;
|
#messenger: NetworkControllerMessenger;
|
||||||
|
|
||||||
/**
|
|
||||||
* Observable store containing the provider configuration.
|
|
||||||
*/
|
|
||||||
providerStore: ObservableStore<ProviderConfiguration>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Observable store containing the provider configuration for the previously
|
* Observable store containing the provider configuration for the previously
|
||||||
@ -425,44 +435,23 @@ export class NetworkController extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
#previousProviderConfig: ProviderConfiguration;
|
#previousProviderConfig: ProviderConfiguration;
|
||||||
|
|
||||||
/**
|
|
||||||
* Observable store containing the network ID for the current network or null
|
|
||||||
* if there is no current network.
|
|
||||||
*/
|
|
||||||
networkIdStore: ObservableStore<NetworkIdState>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Observable store for the network status.
|
|
||||||
*/
|
|
||||||
networkStatusStore: ObservableStore<NetworkStatus>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Observable store for details about the network.
|
|
||||||
*/
|
|
||||||
networkDetails: ObservableStore<NetworkDetails>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Observable store for network configurations.
|
|
||||||
*/
|
|
||||||
networkConfigurationsStore: ObservableStore<NetworkConfigurations>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Observable store containing a combination of data from all of the
|
* Observable store containing a combination of data from all of the
|
||||||
* individual stores.
|
* individual stores.
|
||||||
*/
|
*/
|
||||||
store: ComposedStore<NetworkControllerState>;
|
store: ObservableStore<NetworkControllerState>;
|
||||||
|
|
||||||
_provider: SafeEventEmitterProvider | null;
|
#provider: SafeEventEmitterProvider | null;
|
||||||
|
|
||||||
_blockTracker: PollingBlockTracker | null;
|
#blockTracker: PollingBlockTracker | null;
|
||||||
|
|
||||||
_providerProxy: SwappableProxy<SafeEventEmitterProvider> | null;
|
#providerProxy: SwappableProxy<SafeEventEmitterProvider> | null;
|
||||||
|
|
||||||
_blockTrackerProxy: SwappableProxy<PollingBlockTracker> | null;
|
#blockTrackerProxy: SwappableProxy<PollingBlockTracker> | null;
|
||||||
|
|
||||||
_infuraProjectId: NetworkControllerOptions['infuraProjectId'];
|
#infuraProjectId: NetworkControllerOptions['infuraProjectId'];
|
||||||
|
|
||||||
_trackMetaMetricsEvent: NetworkControllerOptions['trackMetaMetricsEvent'];
|
#trackMetaMetricsEvent: NetworkControllerOptions['trackMetaMetricsEvent'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a network controller.
|
* Constructs a network controller.
|
||||||
@ -482,51 +471,27 @@ export class NetworkController extends EventEmitter {
|
|||||||
}: NetworkControllerOptions) {
|
}: NetworkControllerOptions) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.messenger = messenger;
|
this.#messenger = messenger;
|
||||||
|
|
||||||
// create stores
|
this.store = new ObservableStore({
|
||||||
this.providerStore = new ObservableStore(
|
...buildDefaultState(),
|
||||||
state.provider || buildDefaultProviderConfigState(),
|
...state,
|
||||||
);
|
|
||||||
this.#previousProviderConfig = this.providerStore.getState();
|
|
||||||
this.networkIdStore = new ObservableStore(buildDefaultNetworkIdState());
|
|
||||||
this.networkStatusStore = new ObservableStore(
|
|
||||||
buildDefaultNetworkStatusState(),
|
|
||||||
);
|
|
||||||
// We need to keep track of a few details about the current network.
|
|
||||||
// Ideally we'd merge this.networkStatusStore with this new store, but doing
|
|
||||||
// so will require a decent sized refactor of how we're accessing network
|
|
||||||
// state. Currently this is only used for detecting EIP-1559 support but can
|
|
||||||
// be extended to track other network details.
|
|
||||||
this.networkDetails = new ObservableStore(
|
|
||||||
state.networkDetails || buildDefaultNetworkDetailsState(),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.networkConfigurationsStore = new ObservableStore(
|
|
||||||
state.networkConfigurations || buildDefaultNetworkConfigurationsState(),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.store = new ComposedStore<NetworkControllerState>({
|
|
||||||
provider: this.providerStore,
|
|
||||||
networkId: this.networkIdStore,
|
|
||||||
networkStatus: this.networkStatusStore,
|
|
||||||
networkDetails: this.networkDetails,
|
|
||||||
networkConfigurations: this.networkConfigurationsStore,
|
|
||||||
});
|
});
|
||||||
|
this.#previousProviderConfig = this.store.getState().provider;
|
||||||
|
|
||||||
// provider and block tracker
|
// provider and block tracker
|
||||||
this._provider = null;
|
this.#provider = null;
|
||||||
this._blockTracker = null;
|
this.#blockTracker = null;
|
||||||
|
|
||||||
// provider and block tracker proxies - because the network changes
|
// provider and block tracker proxies - because the network changes
|
||||||
this._providerProxy = null;
|
this.#providerProxy = null;
|
||||||
this._blockTrackerProxy = null;
|
this.#blockTrackerProxy = null;
|
||||||
|
|
||||||
if (!infuraProjectId || typeof infuraProjectId !== 'string') {
|
if (!infuraProjectId || typeof infuraProjectId !== 'string') {
|
||||||
throw new Error('Invalid Infura project ID');
|
throw new Error('Invalid Infura project ID');
|
||||||
}
|
}
|
||||||
this._infuraProjectId = infuraProjectId;
|
this.#infuraProjectId = infuraProjectId;
|
||||||
this._trackMetaMetricsEvent = trackMetaMetricsEvent;
|
this.#trackMetaMetricsEvent = trackMetaMetricsEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -535,7 +500,7 @@ export class NetworkController extends EventEmitter {
|
|||||||
* In-progress requests will not be aborted.
|
* In-progress requests will not be aborted.
|
||||||
*/
|
*/
|
||||||
async destroy(): Promise<void> {
|
async destroy(): Promise<void> {
|
||||||
await this._blockTracker?.destroy();
|
await this.#blockTracker?.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -543,8 +508,8 @@ export class NetworkController extends EventEmitter {
|
|||||||
* using the provider to gather details about the network.
|
* using the provider to gather details about the network.
|
||||||
*/
|
*/
|
||||||
async initializeProvider(): Promise<void> {
|
async initializeProvider(): Promise<void> {
|
||||||
const { type, rpcUrl, chainId } = this.providerStore.getState();
|
const { type, rpcUrl, chainId } = this.store.getState().provider;
|
||||||
this._configureProvider({ type, rpcUrl, chainId });
|
this.#configureProvider({ type, rpcUrl, chainId });
|
||||||
await this.lookupNetwork();
|
await this.lookupNetwork();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -555,8 +520,8 @@ export class NetworkController extends EventEmitter {
|
|||||||
provider: SwappableProxy<SafeEventEmitterProvider> | null;
|
provider: SwappableProxy<SafeEventEmitterProvider> | null;
|
||||||
blockTracker: SwappableProxy<PollingBlockTracker> | null;
|
blockTracker: SwappableProxy<PollingBlockTracker> | null;
|
||||||
} {
|
} {
|
||||||
const provider = this._providerProxy;
|
const provider = this.#providerProxy;
|
||||||
const blockTracker = this._blockTrackerProxy;
|
const blockTracker = this.#blockTrackerProxy;
|
||||||
return { provider, blockTracker };
|
return { provider, blockTracker };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -569,7 +534,7 @@ export class NetworkController extends EventEmitter {
|
|||||||
* and false otherwise.
|
* and false otherwise.
|
||||||
*/
|
*/
|
||||||
async getEIP1559Compatibility(): Promise<boolean> {
|
async getEIP1559Compatibility(): Promise<boolean> {
|
||||||
const { EIPS } = this.networkDetails.getState();
|
const { EIPS } = this.store.getState().networkDetails;
|
||||||
// NOTE: This isn't necessary anymore because the block cache middleware
|
// NOTE: This isn't necessary anymore because the block cache middleware
|
||||||
// already prevents duplicate requests from taking place
|
// already prevents duplicate requests from taking place
|
||||||
if (EIPS[1559] !== undefined) {
|
if (EIPS[1559] !== undefined) {
|
||||||
@ -584,11 +549,15 @@ export class NetworkController extends EventEmitter {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const supportsEIP1559 = await this._determineEIP1559Compatibility(provider);
|
const supportsEIP1559 = await this.#determineEIP1559Compatibility(provider);
|
||||||
this.networkDetails.updateState({
|
const { networkDetails } = this.store.getState();
|
||||||
EIPS: {
|
this.store.updateState({
|
||||||
...this.networkDetails.getState().EIPS,
|
networkDetails: {
|
||||||
1559: supportsEIP1559,
|
...networkDetails,
|
||||||
|
EIPS: {
|
||||||
|
...networkDetails.EIPS,
|
||||||
|
1559: supportsEIP1559,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return supportsEIP1559;
|
return supportsEIP1559;
|
||||||
@ -606,7 +575,7 @@ export class NetworkController extends EventEmitter {
|
|||||||
* blocking requests, or if the network is not Infura-supported.
|
* blocking requests, or if the network is not Infura-supported.
|
||||||
*/
|
*/
|
||||||
async lookupNetwork(): Promise<void> {
|
async lookupNetwork(): Promise<void> {
|
||||||
const { chainId, type } = this.providerStore.getState();
|
const { chainId, type } = this.store.getState().provider;
|
||||||
const { provider } = this.getProviderAndBlockTracker();
|
const { provider } = this.getProviderAndBlockTracker();
|
||||||
let networkChanged = false;
|
let networkChanged = false;
|
||||||
let networkId: NetworkIdState = null;
|
let networkId: NetworkIdState = null;
|
||||||
@ -624,9 +593,9 @@ export class NetworkController extends EventEmitter {
|
|||||||
log.warn(
|
log.warn(
|
||||||
'NetworkController - lookupNetwork aborted due to missing chainId',
|
'NetworkController - lookupNetwork aborted due to missing chainId',
|
||||||
);
|
);
|
||||||
this._resetNetworkId();
|
this.#resetNetworkId();
|
||||||
this._resetNetworkStatus();
|
this.#resetNetworkStatus();
|
||||||
this._resetNetworkDetails();
|
this.#resetNetworkDetails();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -634,20 +603,20 @@ export class NetworkController extends EventEmitter {
|
|||||||
|
|
||||||
const listener = () => {
|
const listener = () => {
|
||||||
networkChanged = true;
|
networkChanged = true;
|
||||||
this.messenger.unsubscribe(
|
this.#messenger.unsubscribe(
|
||||||
NetworkControllerEventType.NetworkDidChange,
|
NetworkControllerEventType.NetworkDidChange,
|
||||||
listener,
|
listener,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
this.messenger.subscribe(
|
this.#messenger.subscribe(
|
||||||
NetworkControllerEventType.NetworkDidChange,
|
NetworkControllerEventType.NetworkDidChange,
|
||||||
listener,
|
listener,
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const results = await Promise.all([
|
const results = await Promise.all([
|
||||||
this._getNetworkId(provider),
|
this.#getNetworkId(provider),
|
||||||
this._determineEIP1559Compatibility(provider),
|
this.#determineEIP1559Compatibility(provider),
|
||||||
]);
|
]);
|
||||||
const possibleNetworkId = results[0];
|
const possibleNetworkId = results[0];
|
||||||
assertNetworkId(possibleNetworkId);
|
assertNetworkId(possibleNetworkId);
|
||||||
@ -687,37 +656,43 @@ export class NetworkController extends EventEmitter {
|
|||||||
// in the process of being called, so we don't need to go further.
|
// in the process of being called, so we don't need to go further.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.messenger.unsubscribe(
|
this.#messenger.unsubscribe(
|
||||||
NetworkControllerEventType.NetworkDidChange,
|
NetworkControllerEventType.NetworkDidChange,
|
||||||
listener,
|
listener,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.networkStatusStore.putState(networkStatus);
|
this.store.updateState({
|
||||||
|
networkStatus,
|
||||||
|
});
|
||||||
|
|
||||||
if (networkStatus === NetworkStatus.Available) {
|
if (networkStatus === NetworkStatus.Available) {
|
||||||
this.networkIdStore.putState(networkId);
|
const { networkDetails } = this.store.getState();
|
||||||
this.networkDetails.updateState({
|
this.store.updateState({
|
||||||
EIPS: {
|
networkId,
|
||||||
...this.networkDetails.getState().EIPS,
|
networkDetails: {
|
||||||
1559: supportsEIP1559,
|
...networkDetails,
|
||||||
|
EIPS: {
|
||||||
|
...networkDetails.EIPS,
|
||||||
|
1559: supportsEIP1559,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this._resetNetworkId();
|
this.#resetNetworkId();
|
||||||
this._resetNetworkDetails();
|
this.#resetNetworkDetails();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isInfura) {
|
if (isInfura) {
|
||||||
if (networkStatus === NetworkStatus.Available) {
|
if (networkStatus === NetworkStatus.Available) {
|
||||||
this.messenger.publish(NetworkControllerEventType.InfuraIsUnblocked);
|
this.#messenger.publish(NetworkControllerEventType.InfuraIsUnblocked);
|
||||||
} else if (networkStatus === NetworkStatus.Blocked) {
|
} else if (networkStatus === NetworkStatus.Blocked) {
|
||||||
this.messenger.publish(NetworkControllerEventType.InfuraIsBlocked);
|
this.#messenger.publish(NetworkControllerEventType.InfuraIsBlocked);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Always publish infuraIsUnblocked regardless of network status to
|
// Always publish infuraIsUnblocked regardless of network status to
|
||||||
// prevent consumers from being stuck in a blocked state if they were
|
// prevent consumers from being stuck in a blocked state if they were
|
||||||
// previously connected to an Infura network that was blocked
|
// previously connected to an Infura network that was blocked
|
||||||
this.messenger.publish(NetworkControllerEventType.InfuraIsUnblocked);
|
this.#messenger.publish(NetworkControllerEventType.InfuraIsUnblocked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -731,7 +706,7 @@ export class NetworkController extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
setActiveNetwork(networkConfigurationId: NetworkConfigurationId): string {
|
setActiveNetwork(networkConfigurationId: NetworkConfigurationId): string {
|
||||||
const targetNetwork =
|
const targetNetwork =
|
||||||
this.networkConfigurationsStore.getState()[networkConfigurationId];
|
this.store.getState().networkConfigurations[networkConfigurationId];
|
||||||
|
|
||||||
if (!targetNetwork) {
|
if (!targetNetwork) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -739,7 +714,7 @@ export class NetworkController extends EventEmitter {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._setProviderConfig({
|
this.#setProviderConfig({
|
||||||
type: NETWORK_TYPES.RPC,
|
type: NETWORK_TYPES.RPC,
|
||||||
...targetNetwork,
|
...targetNetwork,
|
||||||
});
|
});
|
||||||
@ -754,7 +729,7 @@ export class NetworkController extends EventEmitter {
|
|||||||
* @throws if the `type` is "rpc" or if it is not a known Infura-supported
|
* @throws if the `type` is "rpc" or if it is not a known Infura-supported
|
||||||
* network.
|
* network.
|
||||||
*/
|
*/
|
||||||
setProviderType(type: string): void {
|
async setProviderType(type: string) {
|
||||||
assert.notStrictEqual(
|
assert.notStrictEqual(
|
||||||
type,
|
type,
|
||||||
NETWORK_TYPES.RPC,
|
NETWORK_TYPES.RPC,
|
||||||
@ -765,7 +740,7 @@ export class NetworkController extends EventEmitter {
|
|||||||
`Unknown Infura provider type "${type}".`,
|
`Unknown Infura provider type "${type}".`,
|
||||||
);
|
);
|
||||||
const network = BUILT_IN_INFURA_NETWORKS[type];
|
const network = BUILT_IN_INFURA_NETWORKS[type];
|
||||||
this._setProviderConfig({
|
await this.#setProviderConfig({
|
||||||
type,
|
type,
|
||||||
rpcUrl: '',
|
rpcUrl: '',
|
||||||
chainId: network.chainId,
|
chainId: network.chainId,
|
||||||
@ -778,8 +753,8 @@ export class NetworkController extends EventEmitter {
|
|||||||
/**
|
/**
|
||||||
* Re-initializes the provider and block tracker for the current network.
|
* Re-initializes the provider and block tracker for the current network.
|
||||||
*/
|
*/
|
||||||
resetConnection(): void {
|
async resetConnection() {
|
||||||
this._setProviderConfig(this.providerStore.getState());
|
await this.#setProviderConfig(this.store.getState().provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -789,8 +764,10 @@ export class NetworkController extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
async rollbackToPreviousProvider() {
|
async rollbackToPreviousProvider() {
|
||||||
const config = this.#previousProviderConfig;
|
const config = this.#previousProviderConfig;
|
||||||
this.providerStore.putState(config);
|
this.store.updateState({
|
||||||
await this._switchNetwork(config);
|
provider: config,
|
||||||
|
});
|
||||||
|
await this.#switchNetwork(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -800,7 +777,7 @@ export class NetworkController extends EventEmitter {
|
|||||||
* @returns A promise that either resolves to the block header or null if
|
* @returns A promise that either resolves to the block header or null if
|
||||||
* there is no latest block, or rejects with an error.
|
* there is no latest block, or rejects with an error.
|
||||||
*/
|
*/
|
||||||
_getLatestBlock(provider: SafeEventEmitterProvider): Promise<Block | null> {
|
#getLatestBlock(provider: SafeEventEmitterProvider): Promise<Block | null> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const ethQuery = new EthQuery(provider);
|
const ethQuery = new EthQuery(provider);
|
||||||
ethQuery.sendAsync<['latest', false], Block | null>(
|
ethQuery.sendAsync<['latest', false], Block | null>(
|
||||||
@ -823,7 +800,7 @@ export class NetworkController extends EventEmitter {
|
|||||||
* @returns A promise that either resolves to the network ID, or rejects with
|
* @returns A promise that either resolves to the network ID, or rejects with
|
||||||
* an error.
|
* an error.
|
||||||
*/
|
*/
|
||||||
async _getNetworkId(provider: SafeEventEmitterProvider): Promise<string> {
|
async #getNetworkId(provider: SafeEventEmitterProvider): Promise<string> {
|
||||||
const ethQuery = new EthQuery(provider);
|
const ethQuery = new EthQuery(provider);
|
||||||
return await new Promise((resolve, reject) => {
|
return await new Promise((resolve, reject) => {
|
||||||
ethQuery.sendAsync<never[], string>(
|
ethQuery.sendAsync<never[], string>(
|
||||||
@ -842,22 +819,28 @@ export class NetworkController extends EventEmitter {
|
|||||||
/**
|
/**
|
||||||
* Clears the stored network ID.
|
* Clears the stored network ID.
|
||||||
*/
|
*/
|
||||||
_resetNetworkId(): void {
|
#resetNetworkId(): void {
|
||||||
this.networkIdStore.putState(buildDefaultNetworkIdState());
|
this.store.updateState({
|
||||||
|
networkId: buildDefaultNetworkIdState(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets network status to the default ("unknown").
|
* Resets network status to the default ("unknown").
|
||||||
*/
|
*/
|
||||||
_resetNetworkStatus(): void {
|
#resetNetworkStatus(): void {
|
||||||
this.networkStatusStore.putState(buildDefaultNetworkStatusState());
|
this.store.updateState({
|
||||||
|
networkStatus: buildDefaultNetworkStatusState(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears details previously stored for the network.
|
* Clears details previously stored for the network.
|
||||||
*/
|
*/
|
||||||
_resetNetworkDetails(): void {
|
#resetNetworkDetails(): void {
|
||||||
this.networkDetails.putState(buildDefaultNetworkDetailsState());
|
this.store.updateState({
|
||||||
|
networkDetails: buildDefaultNetworkDetailsState(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -866,10 +849,10 @@ export class NetworkController extends EventEmitter {
|
|||||||
*
|
*
|
||||||
* @param providerConfig - The provider configuration.
|
* @param providerConfig - The provider configuration.
|
||||||
*/
|
*/
|
||||||
async _setProviderConfig(providerConfig: ProviderConfiguration) {
|
async #setProviderConfig(providerConfig: ProviderConfiguration) {
|
||||||
this.#previousProviderConfig = this.providerStore.getState();
|
this.#previousProviderConfig = this.store.getState().provider;
|
||||||
this.providerStore.putState(providerConfig);
|
this.store.updateState({ provider: providerConfig });
|
||||||
await this._switchNetwork(providerConfig);
|
await this.#switchNetwork(providerConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -881,10 +864,10 @@ export class NetworkController extends EventEmitter {
|
|||||||
* @returns A promise that resolves to true if the network supports EIP-1559
|
* @returns A promise that resolves to true if the network supports EIP-1559
|
||||||
* and false otherwise.
|
* and false otherwise.
|
||||||
*/
|
*/
|
||||||
async _determineEIP1559Compatibility(
|
async #determineEIP1559Compatibility(
|
||||||
provider: SafeEventEmitterProvider,
|
provider: SafeEventEmitterProvider,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const latestBlock = await this._getLatestBlock(provider);
|
const latestBlock = await this.#getLatestBlock(provider);
|
||||||
return latestBlock?.baseFeePerGas !== undefined;
|
return latestBlock?.baseFeePerGas !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -900,13 +883,13 @@ export class NetworkController extends EventEmitter {
|
|||||||
* @param providerConfig - The provider configuration object that specifies
|
* @param providerConfig - The provider configuration object that specifies
|
||||||
* the new network.
|
* the new network.
|
||||||
*/
|
*/
|
||||||
async _switchNetwork(providerConfig: ProviderConfiguration) {
|
async #switchNetwork(providerConfig: ProviderConfiguration) {
|
||||||
this.messenger.publish(NetworkControllerEventType.NetworkWillChange);
|
this.#messenger.publish(NetworkControllerEventType.NetworkWillChange);
|
||||||
this._resetNetworkId();
|
this.#resetNetworkId();
|
||||||
this._resetNetworkStatus();
|
this.#resetNetworkStatus();
|
||||||
this._resetNetworkDetails();
|
this.#resetNetworkDetails();
|
||||||
this._configureProvider(providerConfig);
|
this.#configureProvider(providerConfig);
|
||||||
this.messenger.publish(NetworkControllerEventType.NetworkDidChange);
|
this.#messenger.publish(NetworkControllerEventType.NetworkDidChange);
|
||||||
await this.lookupNetwork();
|
await this.lookupNetwork();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -924,20 +907,20 @@ export class NetworkController extends EventEmitter {
|
|||||||
* any Infura-supported network).
|
* any Infura-supported network).
|
||||||
* @throws if the `type` if not a known Infura-supported network.
|
* @throws if the `type` if not a known Infura-supported network.
|
||||||
*/
|
*/
|
||||||
_configureProvider({ type, rpcUrl, chainId }: ProviderConfiguration): void {
|
#configureProvider({ type, rpcUrl, chainId }: ProviderConfiguration): void {
|
||||||
const isInfura = isInfuraProviderType(type);
|
const isInfura = isInfuraProviderType(type);
|
||||||
if (isInfura) {
|
if (isInfura) {
|
||||||
// infura type-based endpoints
|
// infura type-based endpoints
|
||||||
this._configureInfuraProvider({
|
this.#configureInfuraProvider({
|
||||||
type,
|
type,
|
||||||
infuraProjectId: this._infuraProjectId,
|
infuraProjectId: this.#infuraProjectId,
|
||||||
});
|
});
|
||||||
} else if (type === NETWORK_TYPES.RPC && rpcUrl) {
|
} else if (type === NETWORK_TYPES.RPC && rpcUrl) {
|
||||||
// url-based rpc endpoints
|
// url-based rpc endpoints
|
||||||
this._configureStandardProvider(rpcUrl, chainId);
|
this.#configureStandardProvider(rpcUrl, chainId);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`NetworkController - _configureProvider - unknown type "${type}"`,
|
`NetworkController - #configureProvider - unknown type "${type}"`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -952,20 +935,20 @@ export class NetworkController extends EventEmitter {
|
|||||||
* @param args.infuraProjectId - An Infura API key. ("Project ID" is a
|
* @param args.infuraProjectId - An Infura API key. ("Project ID" is a
|
||||||
* now-obsolete term we've retained for backward compatibility.)
|
* now-obsolete term we've retained for backward compatibility.)
|
||||||
*/
|
*/
|
||||||
_configureInfuraProvider({
|
#configureInfuraProvider({
|
||||||
type,
|
type,
|
||||||
infuraProjectId,
|
infuraProjectId,
|
||||||
}: {
|
}: {
|
||||||
type: BuiltInInfuraNetwork;
|
type: BuiltInInfuraNetwork;
|
||||||
infuraProjectId: NetworkControllerOptions['infuraProjectId'];
|
infuraProjectId: NetworkControllerOptions['infuraProjectId'];
|
||||||
}): void {
|
}): void {
|
||||||
log.info('NetworkController - configureInfuraProvider', type);
|
log.info('NetworkController - #configureInfuraProvider', type);
|
||||||
const { provider, blockTracker } = createNetworkClient({
|
const { provider, blockTracker } = createNetworkClient({
|
||||||
network: type,
|
network: type,
|
||||||
infuraProjectId,
|
infuraProjectId,
|
||||||
type: NetworkClientType.Infura,
|
type: NetworkClientType.Infura,
|
||||||
});
|
});
|
||||||
this._setProviderAndBlockTracker({ provider, blockTracker });
|
this.#setProviderAndBlockTracker({ provider, blockTracker });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -975,14 +958,14 @@ export class NetworkController extends EventEmitter {
|
|||||||
* @param rpcUrl - The URL of the RPC endpoint that represents the network.
|
* @param rpcUrl - The URL of the RPC endpoint that represents the network.
|
||||||
* @param chainId - The chain ID of the network (as per EIP-155).
|
* @param chainId - The chain ID of the network (as per EIP-155).
|
||||||
*/
|
*/
|
||||||
_configureStandardProvider(rpcUrl: string, chainId: ChainId): void {
|
#configureStandardProvider(rpcUrl: string, chainId: ChainId): void {
|
||||||
log.info('NetworkController - configureStandardProvider', rpcUrl);
|
log.info('NetworkController - #configureStandardProvider', rpcUrl);
|
||||||
const { provider, blockTracker } = createNetworkClient({
|
const { provider, blockTracker } = createNetworkClient({
|
||||||
chainId,
|
chainId,
|
||||||
rpcUrl,
|
rpcUrl,
|
||||||
type: NetworkClientType.Custom,
|
type: NetworkClientType.Custom,
|
||||||
});
|
});
|
||||||
this._setProviderAndBlockTracker({ provider, blockTracker });
|
this.#setProviderAndBlockTracker({ provider, blockTracker });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -994,7 +977,7 @@ export class NetworkController extends EventEmitter {
|
|||||||
* @param args.provider - The provider.
|
* @param args.provider - The provider.
|
||||||
* @param args.blockTracker - The block tracker.
|
* @param args.blockTracker - The block tracker.
|
||||||
*/
|
*/
|
||||||
_setProviderAndBlockTracker({
|
#setProviderAndBlockTracker({
|
||||||
provider,
|
provider,
|
||||||
blockTracker,
|
blockTracker,
|
||||||
}: {
|
}: {
|
||||||
@ -1002,21 +985,21 @@ export class NetworkController extends EventEmitter {
|
|||||||
blockTracker: PollingBlockTracker;
|
blockTracker: PollingBlockTracker;
|
||||||
}): void {
|
}): void {
|
||||||
// update or initialize proxies
|
// update or initialize proxies
|
||||||
if (this._providerProxy) {
|
if (this.#providerProxy) {
|
||||||
this._providerProxy.setTarget(provider);
|
this.#providerProxy.setTarget(provider);
|
||||||
} else {
|
} else {
|
||||||
this._providerProxy = createSwappableProxy(provider);
|
this.#providerProxy = createSwappableProxy(provider);
|
||||||
}
|
}
|
||||||
if (this._blockTrackerProxy) {
|
if (this.#blockTrackerProxy) {
|
||||||
this._blockTrackerProxy.setTarget(blockTracker);
|
this.#blockTrackerProxy.setTarget(blockTracker);
|
||||||
} else {
|
} else {
|
||||||
this._blockTrackerProxy = createEventEmitterProxy(blockTracker, {
|
this.#blockTrackerProxy = createEventEmitterProxy(blockTracker, {
|
||||||
eventFilter: 'skipInternal',
|
eventFilter: 'skipInternal',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// set new provider and blockTracker
|
// set new provider and blockTracker
|
||||||
this._provider = provider;
|
this.#provider = provider;
|
||||||
this._blockTracker = blockTracker;
|
this.#blockTracker = blockTracker;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1105,7 +1088,7 @@ export class NetworkController extends EventEmitter {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const networkConfigurations = this.networkConfigurationsStore.getState();
|
const { networkConfigurations } = this.store.getState();
|
||||||
const newNetworkConfiguration = {
|
const newNetworkConfiguration = {
|
||||||
rpcUrl,
|
rpcUrl,
|
||||||
chainId,
|
chainId,
|
||||||
@ -1120,16 +1103,18 @@ export class NetworkController extends EventEmitter {
|
|||||||
)?.id;
|
)?.id;
|
||||||
|
|
||||||
const newNetworkConfigurationId = oldNetworkConfigurationId || uuid();
|
const newNetworkConfigurationId = oldNetworkConfigurationId || uuid();
|
||||||
this.networkConfigurationsStore.putState({
|
this.store.updateState({
|
||||||
...networkConfigurations,
|
networkConfigurations: {
|
||||||
[newNetworkConfigurationId]: {
|
...networkConfigurations,
|
||||||
...newNetworkConfiguration,
|
[newNetworkConfigurationId]: {
|
||||||
id: newNetworkConfigurationId,
|
...newNetworkConfiguration,
|
||||||
|
id: newNetworkConfigurationId,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!oldNetworkConfigurationId) {
|
if (!oldNetworkConfigurationId) {
|
||||||
this._trackMetaMetricsEvent({
|
this.#trackMetaMetricsEvent({
|
||||||
event: 'Custom Network Added',
|
event: 'Custom Network Added',
|
||||||
category: MetaMetricsEventCategory.Network,
|
category: MetaMetricsEventCategory.Network,
|
||||||
referrer: {
|
referrer: {
|
||||||
@ -1160,9 +1145,11 @@ export class NetworkController extends EventEmitter {
|
|||||||
networkConfigurationId: NetworkConfigurationId,
|
networkConfigurationId: NetworkConfigurationId,
|
||||||
): void {
|
): void {
|
||||||
const networkConfigurations = {
|
const networkConfigurations = {
|
||||||
...this.networkConfigurationsStore.getState(),
|
...this.store.getState().networkConfigurations,
|
||||||
};
|
};
|
||||||
delete networkConfigurations[networkConfigurationId];
|
delete networkConfigurations[networkConfigurationId];
|
||||||
this.networkConfigurationsStore.putState(networkConfigurations);
|
this.store.updateState({
|
||||||
|
networkConfigurations,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ describe('buildSnapRestrictedMethodSpecifications', () => {
|
|||||||
getSnap: () => undefined,
|
getSnap: () => undefined,
|
||||||
getSnapRpcHandler: () => undefined,
|
getSnapRpcHandler: () => undefined,
|
||||||
getSnapState: () => undefined,
|
getSnapState: () => undefined,
|
||||||
showConfirmation: () => undefined,
|
|
||||||
updateSnapState: () => undefined,
|
updateSnapState: () => undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,36 +2,13 @@ import { strict as assert } from 'assert';
|
|||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
import { ControllerMessenger } from '@metamask/base-controller';
|
import { ControllerMessenger } from '@metamask/base-controller';
|
||||||
import { TokenListController } from '@metamask/assets-controllers';
|
import { TokenListController } from '@metamask/assets-controllers';
|
||||||
import { CHAIN_IDS } from '../../../shared/constants/network';
|
|
||||||
import PreferencesController from './preferences';
|
import PreferencesController from './preferences';
|
||||||
import { NetworkController } from './network';
|
|
||||||
|
|
||||||
describe('preferences controller', function () {
|
describe('preferences controller', function () {
|
||||||
let preferencesController;
|
let preferencesController;
|
||||||
let network;
|
|
||||||
let currentChainId;
|
|
||||||
let provider;
|
|
||||||
let tokenListController;
|
let tokenListController;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
const sandbox = sinon.createSandbox();
|
|
||||||
currentChainId = CHAIN_IDS.MAINNET;
|
|
||||||
const networkControllerProviderConfig = {
|
|
||||||
getAccounts: () => undefined,
|
|
||||||
};
|
|
||||||
const networkControllerMessenger = new ControllerMessenger();
|
|
||||||
network = new NetworkController({
|
|
||||||
infuraProjectId: 'foo',
|
|
||||||
messenger: networkControllerMessenger,
|
|
||||||
state: {
|
|
||||||
provider: {
|
|
||||||
type: 'mainnet',
|
|
||||||
chainId: currentChainId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
network.initializeProvider(networkControllerProviderConfig);
|
|
||||||
provider = network.getProviderAndBlockTracker().provider;
|
|
||||||
const tokenListMessenger = new ControllerMessenger().getRestricted({
|
const tokenListMessenger = new ControllerMessenger().getRestricted({
|
||||||
name: 'TokenListController',
|
name: 'TokenListController',
|
||||||
});
|
});
|
||||||
@ -43,14 +20,8 @@ describe('preferences controller', function () {
|
|||||||
messenger: tokenListMessenger,
|
messenger: tokenListMessenger,
|
||||||
});
|
});
|
||||||
|
|
||||||
sandbox
|
|
||||||
.stub(network, '_getLatestBlock')
|
|
||||||
.callsFake(() => Promise.resolve({}));
|
|
||||||
|
|
||||||
preferencesController = new PreferencesController({
|
preferencesController = new PreferencesController({
|
||||||
initLangCode: 'en_US',
|
initLangCode: 'en_US',
|
||||||
network,
|
|
||||||
provider,
|
|
||||||
tokenListController,
|
tokenListController,
|
||||||
onInfuraIsBlocked: sinon.spy(),
|
onInfuraIsBlocked: sinon.spy(),
|
||||||
onInfuraIsUnblocked: sinon.spy(),
|
onInfuraIsUnblocked: sinon.spy(),
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { errorCodes } from 'eth-rpc-errors';
|
import { errorCodes } from 'eth-rpc-errors';
|
||||||
import { detectSIWE } from '@metamask/controller-utils';
|
import { detectSIWE } from '@metamask/controller-utils';
|
||||||
|
import { isValidAddress } from 'ethereumjs-util';
|
||||||
|
|
||||||
import { MESSAGE_TYPE, ORIGIN_METAMASK } from '../../../shared/constants/app';
|
import { MESSAGE_TYPE, ORIGIN_METAMASK } from '../../../shared/constants/app';
|
||||||
import { TransactionStatus } from '../../../shared/constants/transaction';
|
import { TransactionStatus } from '../../../shared/constants/transaction';
|
||||||
import { SECOND } from '../../../shared/constants/time';
|
import { SECOND } from '../../../shared/constants/time';
|
||||||
@ -168,8 +170,17 @@ export default function createRPCMethodTrackingMiddleware({
|
|||||||
if (event === MetaMetricsEventName.SignatureRequested) {
|
if (event === MetaMetricsEventName.SignatureRequested) {
|
||||||
eventProperties.signature_type = method;
|
eventProperties.signature_type = method;
|
||||||
|
|
||||||
const data = req?.params?.[0];
|
// In personal messages the first param is data while in typed messages second param is data
|
||||||
const from = req?.params?.[1];
|
// if condition below is added to ensure that the right params are captured as data and address.
|
||||||
|
let data;
|
||||||
|
let from;
|
||||||
|
if (isValidAddress(req?.params?.[1])) {
|
||||||
|
data = req?.params?.[0];
|
||||||
|
from = req?.params?.[1];
|
||||||
|
} else {
|
||||||
|
data = req?.params?.[1];
|
||||||
|
from = req?.params?.[0];
|
||||||
|
}
|
||||||
const paramsExamplePassword = req?.params?.[2];
|
const paramsExamplePassword = req?.params?.[2];
|
||||||
|
|
||||||
const msgData = {
|
const msgData = {
|
||||||
|
@ -383,5 +383,66 @@ describe('createRPCMethodTrackingMiddleware', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when signature requests are received', () => {
|
||||||
|
let securityProviderReq, fnHandler;
|
||||||
|
beforeEach(() => {
|
||||||
|
securityProviderReq = jest.fn().mockReturnValue(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
flagAsDangerous: 0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
fnHandler = createRPCMethodTrackingMiddleware({
|
||||||
|
trackEvent,
|
||||||
|
getMetricsState,
|
||||||
|
rateLimitSeconds: 1,
|
||||||
|
securityProviderRequest: securityProviderReq,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it(`should pass correct data for personal sign`, async () => {
|
||||||
|
const req = {
|
||||||
|
method: 'personal_sign',
|
||||||
|
params: [
|
||||||
|
'0x4578616d706c652060706572736f6e616c5f7369676e60206d657373616765',
|
||||||
|
'0x8eeee1781fd885ff5ddef7789486676961873d12',
|
||||||
|
'Example password',
|
||||||
|
],
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
id: 1142196570,
|
||||||
|
origin: 'https://metamask.github.io',
|
||||||
|
tabId: 1048582817,
|
||||||
|
};
|
||||||
|
const res = { id: 1142196570, jsonrpc: '2.0' };
|
||||||
|
const { next } = getNext();
|
||||||
|
|
||||||
|
await fnHandler(req, res, next);
|
||||||
|
|
||||||
|
expect(securityProviderReq).toHaveBeenCalledTimes(1);
|
||||||
|
const call = securityProviderReq.mock.calls[0][0];
|
||||||
|
expect(call.msgParams.data).toStrictEqual(req.params[0]);
|
||||||
|
});
|
||||||
|
it(`should pass correct data for typed sign`, async () => {
|
||||||
|
const req = {
|
||||||
|
method: 'eth_signTypedData_v4',
|
||||||
|
params: [
|
||||||
|
'0x8eeee1781fd885ff5ddef7789486676961873d12',
|
||||||
|
'{"domain":{"chainId":"5","name":"Ether Mail","verifyingContract":"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC","version":"1"},"message":{"contents":"Hello, Bob!","from":{"name":"Cow","wallets":["0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826","0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF"]},"to":[{"name":"Bob","wallets":["0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB","0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57","0xB0B0b0b0b0b0B000000000000000000000000000"]}]},"primaryType":"Mail","types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Group":[{"name":"name","type":"string"},{"name":"members","type":"Person[]"}],"Mail":[{"name":"from","type":"Person"},{"name":"to","type":"Person[]"},{"name":"contents","type":"string"}],"Person":[{"name":"name","type":"string"},{"name":"wallets","type":"address[]"}]}}',
|
||||||
|
],
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
id: 1142196571,
|
||||||
|
origin: 'https://metamask.github.io',
|
||||||
|
tabId: 1048582817,
|
||||||
|
};
|
||||||
|
const res = { id: 1142196571, jsonrpc: '2.0' };
|
||||||
|
const { next } = getNext();
|
||||||
|
|
||||||
|
await fnHandler(req, res, next);
|
||||||
|
|
||||||
|
expect(securityProviderReq).toHaveBeenCalledTimes(1);
|
||||||
|
const call = securityProviderReq.mock.calls[0][0];
|
||||||
|
expect(call.msgParams.data).toStrictEqual(req.params[1]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -113,7 +113,7 @@ async function switchEthereumChainHandler(
|
|||||||
approvedRequestData.type !== NETWORK_TYPES.LOCALHOST &&
|
approvedRequestData.type !== NETWORK_TYPES.LOCALHOST &&
|
||||||
approvedRequestData.type !== NETWORK_TYPES.LINEA_TESTNET
|
approvedRequestData.type !== NETWORK_TYPES.LINEA_TESTNET
|
||||||
) {
|
) {
|
||||||
setProviderType(approvedRequestData.type);
|
await setProviderType(approvedRequestData.type);
|
||||||
} else {
|
} else {
|
||||||
await setActiveNetwork(approvedRequestData.id);
|
await setActiveNetwork(approvedRequestData.id);
|
||||||
}
|
}
|
||||||
|
@ -544,7 +544,7 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
messenger: currencyRateMessenger,
|
messenger: currencyRateMessenger,
|
||||||
state: {
|
state: {
|
||||||
...initState.CurrencyController,
|
...initState.CurrencyController,
|
||||||
nativeCurrency: this.networkController.providerStore.getState().ticker,
|
nativeCurrency: this.networkController.store.getState().provider.ticker,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -981,8 +981,16 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
getNetworkId: () => this.networkController.store.getState().networkId,
|
getNetworkId: () => this.networkController.store.getState().networkId,
|
||||||
getNetworkStatus: () =>
|
getNetworkStatus: () =>
|
||||||
this.networkController.store.getState().networkStatus,
|
this.networkController.store.getState().networkStatus,
|
||||||
onNetworkStateChange: (listener) =>
|
onNetworkStateChange: (listener) => {
|
||||||
this.networkController.networkIdStore.subscribe(listener),
|
let previousNetworkId =
|
||||||
|
this.networkController.store.getState().networkId;
|
||||||
|
this.networkController.store.subscribe((state) => {
|
||||||
|
if (previousNetworkId !== state.networkId) {
|
||||||
|
listener();
|
||||||
|
previousNetworkId = state.networkId;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
getCurrentChainId: () =>
|
getCurrentChainId: () =>
|
||||||
this.networkController.store.getState().provider.chainId,
|
this.networkController.store.getState().provider.chainId,
|
||||||
preferencesStore: this.preferencesController.store,
|
preferencesStore: this.preferencesController.store,
|
||||||
@ -1166,6 +1174,9 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
preferencesController: this.preferencesController,
|
preferencesController: this.preferencesController,
|
||||||
getState: this.getState.bind(this),
|
getState: this.getState.bind(this),
|
||||||
securityProviderRequest: this.securityProviderRequest.bind(this),
|
securityProviderRequest: this.securityProviderRequest.bind(this),
|
||||||
|
metricsEvent: this.metaMetricsController.trackEvent.bind(
|
||||||
|
this.metaMetricsController,
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.swapsController = new SwapsController({
|
this.swapsController = new SwapsController({
|
||||||
@ -1524,12 +1535,6 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
this.controllerMessenger,
|
this.controllerMessenger,
|
||||||
'SnapController:getSnapState',
|
'SnapController:getSnapState',
|
||||||
),
|
),
|
||||||
showConfirmation: (origin, confirmationData) =>
|
|
||||||
this.approvalController.addAndShowApprovalRequest({
|
|
||||||
origin,
|
|
||||||
type: MESSAGE_TYPE.SNAP_DIALOG_CONFIRMATION,
|
|
||||||
requestData: confirmationData,
|
|
||||||
}),
|
|
||||||
showDialog: (origin, type, content, placeholder) =>
|
showDialog: (origin, type, content, placeholder) =>
|
||||||
this.approvalController.addAndShowApprovalRequest({
|
this.approvalController.addAndShowApprovalRequest({
|
||||||
origin,
|
origin,
|
||||||
@ -3237,32 +3242,6 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
return await this.txController.newUnapprovedTransaction(txParams, req);
|
return await this.txController.newUnapprovedTransaction(txParams, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
|
||||||
/**
|
|
||||||
* Gets an "app key" corresponding to an Ethereum address. An app key is more
|
|
||||||
* or less an addrdess hashed together with some string, in this case a
|
|
||||||
* subject identifier / origin.
|
|
||||||
*
|
|
||||||
* @todo Figure out a way to derive app keys that doesn't depend on the user's
|
|
||||||
* Ethereum addresses.
|
|
||||||
* @param {string} subject - The identifier of the subject whose app key to
|
|
||||||
* retrieve.
|
|
||||||
* @param {string} [requestedAccount] - The account whose app key to retrieve.
|
|
||||||
* The first account in the keyring will be used by default.
|
|
||||||
*/
|
|
||||||
async getAppKeyForSubject(subject, requestedAccount) {
|
|
||||||
let account;
|
|
||||||
|
|
||||||
if (requestedAccount) {
|
|
||||||
account = requestedAccount;
|
|
||||||
} else {
|
|
||||||
[account] = await this.keyringController.getAccounts();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.keyringController.exportAppKeyForAddress(account, subject);
|
|
||||||
}
|
|
||||||
///: END:ONLY_INCLUDE_IN
|
|
||||||
|
|
||||||
// eth_decrypt methods
|
// eth_decrypt methods
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3868,7 +3847,6 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||||
engine.push(
|
engine.push(
|
||||||
createSnapMethodMiddleware(subjectType === SubjectType.Snap, {
|
createSnapMethodMiddleware(subjectType === SubjectType.Snap, {
|
||||||
getAppKey: this.getAppKeyForSubject.bind(this, origin),
|
|
||||||
getUnlockPromise: this.appStateController.getUnlockPromise.bind(
|
getUnlockPromise: this.appStateController.getUnlockPromise.bind(
|
||||||
this.appStateController,
|
this.appStateController,
|
||||||
),
|
),
|
||||||
|
@ -17,7 +17,7 @@ import {
|
|||||||
TextVariant,
|
TextVariant,
|
||||||
} from '../../../helpers/constants/design-system';
|
} from '../../../helpers/constants/design-system';
|
||||||
import { I18nContext } from '../../../contexts/i18n';
|
import { I18nContext } from '../../../contexts/i18n';
|
||||||
import GasDetailsItem from '../gas-details-item/gas-details-item';
|
import { ConfirmGasDisplay } from '../confirm-gas-display';
|
||||||
import MultiLayerFeeMessage from '../multilayer-fee-message/multi-layer-fee-message';
|
import MultiLayerFeeMessage from '../multilayer-fee-message/multi-layer-fee-message';
|
||||||
import { formatCurrency } from '../../../helpers/utils/confirm-tx.util';
|
import { formatCurrency } from '../../../helpers/utils/confirm-tx.util';
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ export default function ApproveContentCard({
|
|||||||
(!isMultiLayerFeeNetwork &&
|
(!isMultiLayerFeeNetwork &&
|
||||||
supportsEIP1559 &&
|
supportsEIP1559 &&
|
||||||
!renderSimulationFailureWarning ? (
|
!renderSimulationFailureWarning ? (
|
||||||
<GasDetailsItem
|
<ConfirmGasDisplay
|
||||||
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
|
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
@ -0,0 +1,157 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`ConfirmGasDisplay should match snapshot 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="transaction-detail-item"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="transaction-detail-item__row"
|
||||||
|
>
|
||||||
|
<h6
|
||||||
|
class="box box--margin-top-1 box--margin-bottom-1 box--display-flex box--flex-direction-row box--flex-wrap-nowrap box--align-items-center typography typography--h6 typography--weight-bold typography--style-normal typography--color-text-default"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box box--display-flex box--flex-direction-row"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box box--margin-right-1 box--flex-direction-row"
|
||||||
|
>
|
||||||
|
Gas
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
class="gas-details-item-title__estimate"
|
||||||
|
>
|
||||||
|
(
|
||||||
|
estimated
|
||||||
|
)
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
class="info-tooltip"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
aria-describedby="tippy-tooltip-1"
|
||||||
|
class="info-tooltip__tooltip-container"
|
||||||
|
data-original-title="null"
|
||||||
|
data-tooltipped=""
|
||||||
|
style="display: inline;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 10 10"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M5 0C2.2 0 0 2.2 0 5s2.2 5 5 5 5-2.2 5-5-2.2-5-5-5zm0 2c.4 0 .7.3.7.7s-.3.7-.7.7-.7-.2-.7-.6.3-.8.7-.8zm.7 6H4.3V4.3h1.5V8z"
|
||||||
|
fill="var(--color-icon-alternative)"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</h6>
|
||||||
|
<div
|
||||||
|
class="transaction-detail-item__detail-values"
|
||||||
|
>
|
||||||
|
<h6
|
||||||
|
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography typography--h6 typography--weight-normal typography--style-normal typography--color-text-alternative"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="gas-details-item__currency-container"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="currency-display-component"
|
||||||
|
title="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="currency-display-component__prefix"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class="currency-display-component__text"
|
||||||
|
>
|
||||||
|
0
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</h6>
|
||||||
|
<h6
|
||||||
|
class="box box--margin-top-1 box--margin-bottom-1 box--margin-left-1 box--flex-direction-row box--text-align-right typography typography--h6 typography--weight-bold typography--style-normal typography--color-text-default"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="gas-details-item__currency-container"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="currency-display-component"
|
||||||
|
title="0 ETH"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="currency-display-component__prefix"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class="currency-display-component__text"
|
||||||
|
>
|
||||||
|
0
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="currency-display-component__suffix"
|
||||||
|
>
|
||||||
|
ETH
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="transaction-detail-item__row"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<h6
|
||||||
|
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography gas-timing gas-timing--positive typography--h7 typography--weight-normal typography--style-normal typography--color-text-default"
|
||||||
|
>
|
||||||
|
Maybe in 1 seconds
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
<h6
|
||||||
|
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography transaction-detail-item__row-subText typography--h7 typography--weight-normal typography--style-normal typography--align-end typography--color-text-alternative"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box gas-details-item__gasfee-label box--display-inline-flex box--flex-direction-row"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box box--margin-right-1 box--flex-direction-row"
|
||||||
|
>
|
||||||
|
<strong>
|
||||||
|
Max fee:
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="gas-details-item__currency-container"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="currency-display-component"
|
||||||
|
title="0 ETH"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="currency-display-component__prefix"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class="currency-display-component__text"
|
||||||
|
>
|
||||||
|
0
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="currency-display-component__suffix"
|
||||||
|
>
|
||||||
|
ETH
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
43
ui/components/app/confirm-gas-display/confirm-gas-display.js
Normal file
43
ui/components/app/confirm-gas-display/confirm-gas-display.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
|
||||||
|
import {
|
||||||
|
checkNetworkAndAccountSupports1559,
|
||||||
|
txDataSelector,
|
||||||
|
} from '../../../selectors';
|
||||||
|
import { isLegacyTransaction } from '../../../helpers/utils/transactions.util';
|
||||||
|
import GasDetailsItem from '../gas-details-item';
|
||||||
|
import { getCurrentDraftTransaction } from '../../../ducks/send';
|
||||||
|
import { TransactionEnvelopeType } from '../../../../shared/constants/transaction';
|
||||||
|
import { ConfirmLegacyGasDisplay } from './confirm-legacy-gas-display';
|
||||||
|
|
||||||
|
const ConfirmGasDisplay = ({ userAcknowledgedGasMissing = false }) => {
|
||||||
|
const { txParams } = useSelector((state) => txDataSelector(state));
|
||||||
|
|
||||||
|
const draftTransaction = useSelector(getCurrentDraftTransaction);
|
||||||
|
const transactionType = draftTransaction?.transactionType;
|
||||||
|
let isLegacyTxn;
|
||||||
|
if (transactionType) {
|
||||||
|
isLegacyTxn = transactionType === TransactionEnvelopeType.legacy;
|
||||||
|
} else {
|
||||||
|
isLegacyTxn = isLegacyTransaction(txParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
const networkAndAccountSupports1559 = useSelector(
|
||||||
|
checkNetworkAndAccountSupports1559,
|
||||||
|
);
|
||||||
|
const supportsEIP1559 = networkAndAccountSupports1559 && !isLegacyTxn;
|
||||||
|
|
||||||
|
return supportsEIP1559 ? (
|
||||||
|
<GasDetailsItem userAcknowledgedGasMissing={userAcknowledgedGasMissing} />
|
||||||
|
) : (
|
||||||
|
<ConfirmLegacyGasDisplay />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ConfirmGasDisplay.propTypes = {
|
||||||
|
userAcknowledgedGasMissing: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConfirmGasDisplay;
|
@ -0,0 +1,101 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { screen } from '@testing-library/react';
|
||||||
|
|
||||||
|
import { GasEstimateTypes } from '../../../../shared/constants/gas';
|
||||||
|
import mockEstimates from '../../../../test/data/mock-estimates.json';
|
||||||
|
import mockState from '../../../../test/data/mock-state.json';
|
||||||
|
import { renderWithProvider } from '../../../../test/jest';
|
||||||
|
import configureStore from '../../../store/store';
|
||||||
|
|
||||||
|
import { GasFeeContextProvider } from '../../../contexts/gasFee';
|
||||||
|
import ConfirmGasDisplay from './confirm-gas-display';
|
||||||
|
|
||||||
|
jest.mock('../../../store/actions', () => ({
|
||||||
|
disconnectGasFeeEstimatePoller: jest.fn(),
|
||||||
|
getGasFeeEstimatesAndStartPolling: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(() => Promise.resolve()),
|
||||||
|
addPollingTokenToAppState: jest.fn(),
|
||||||
|
getGasFeeTimeEstimate: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const render = ({ transactionProp = {}, contextProps = {} } = {}) => {
|
||||||
|
const store = configureStore({
|
||||||
|
...mockState,
|
||||||
|
...contextProps,
|
||||||
|
metamask: {
|
||||||
|
...mockState.metamask,
|
||||||
|
accounts: {
|
||||||
|
[mockState.metamask.selectedAddress]: {
|
||||||
|
address: mockState.metamask.selectedAddress,
|
||||||
|
balance: '0x1F4',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
preferences: {
|
||||||
|
useNativeCurrencyAsPrimaryCurrency: true,
|
||||||
|
},
|
||||||
|
gasFeeEstimates: mockEstimates[GasEstimateTypes.feeMarket],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return renderWithProvider(
|
||||||
|
<GasFeeContextProvider transaction={transactionProp}>
|
||||||
|
<ConfirmGasDisplay />
|
||||||
|
</GasFeeContextProvider>,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('ConfirmGasDisplay', () => {
|
||||||
|
it('should match snapshot', async () => {
|
||||||
|
const { container } = render();
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
it('should render gas display labels for EIP1559 transcations', () => {
|
||||||
|
render({
|
||||||
|
transactionProp: {
|
||||||
|
txParams: {
|
||||||
|
gas: '0x5208',
|
||||||
|
maxFeePerGas: '0x59682f10',
|
||||||
|
maxPriorityFeePerGas: '0x59682f00',
|
||||||
|
},
|
||||||
|
userFeeLevel: 'medium',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(screen.queryByText('Gas')).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText('(estimated)')).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText('Max fee:')).toBeInTheDocument();
|
||||||
|
expect(screen.queryAllByText('ETH').length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
it('should render gas display labels for legacy transcations', () => {
|
||||||
|
render({
|
||||||
|
contextProps: {
|
||||||
|
metamask: {
|
||||||
|
networkDetails: {
|
||||||
|
EIPS: {
|
||||||
|
1559: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
confirmTransaction: {
|
||||||
|
txData: {
|
||||||
|
id: 8393540981007587,
|
||||||
|
status: 'unapproved',
|
||||||
|
chainId: '0x5',
|
||||||
|
txParams: {
|
||||||
|
from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
|
||||||
|
to: '0xc42edfcc21ed14dda456aa0756c153f7985d8813',
|
||||||
|
value: '0x0',
|
||||||
|
gas: '0x5208',
|
||||||
|
gasPrice: '0x3b9aca00',
|
||||||
|
type: '0x0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(screen.queryByText('Estimated gas fee')).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText('Max fee:')).toBeInTheDocument();
|
||||||
|
expect(screen.queryAllByText('ETH').length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,9 @@
|
|||||||
|
import { Story, Canvas, ArgsTable } from '@storybook/addon-docs';
|
||||||
|
import { ConfirmLegacyGasDisplay } from '.';
|
||||||
|
|
||||||
|
# Confirm Legacy Gas Display
|
||||||
|
Confirm Legacy Gas Display is used on confirmation screen and send screen to display gas details for legacy transaction.
|
||||||
|
|
||||||
|
<Canvas>
|
||||||
|
<Story id="components-app-ConfirmLegacyGasDisplay--default-story" />
|
||||||
|
</Canvas>
|
@ -0,0 +1,124 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`ConfirmLegacyGasDisplay should match snapshot 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="transaction-detail-item"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="transaction-detail-item__row"
|
||||||
|
>
|
||||||
|
<h6
|
||||||
|
class="box box--margin-top-1 box--margin-bottom-1 box--display-flex box--flex-direction-row box--flex-wrap-nowrap box--align-items-center typography typography--h6 typography--weight-bold typography--style-normal typography--color-text-default"
|
||||||
|
>
|
||||||
|
Estimated gas fee
|
||||||
|
<div
|
||||||
|
class="info-tooltip"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
aria-describedby="tippy-tooltip-1"
|
||||||
|
class="info-tooltip__tooltip-container"
|
||||||
|
data-original-title="null"
|
||||||
|
data-tooltipped=""
|
||||||
|
style="display: inline;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 10 10"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M5 0C2.2 0 0 2.2 0 5s2.2 5 5 5 5-2.2 5-5-2.2-5-5-5zm0 2c.4 0 .7.3.7.7s-.3.7-.7.7-.7-.2-.7-.6.3-.8.7-.8zm.7 6H4.3V4.3h1.5V8z"
|
||||||
|
fill="var(--color-icon-alternative)"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</h6>
|
||||||
|
<div
|
||||||
|
class="transaction-detail-item__detail-values"
|
||||||
|
>
|
||||||
|
<h6
|
||||||
|
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography typography--h6 typography--weight-normal typography--style-normal typography--color-text-alternative"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="currency-display-component"
|
||||||
|
title="0.000021"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="currency-display-component__prefix"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class="currency-display-component__text"
|
||||||
|
>
|
||||||
|
0.000021
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</h6>
|
||||||
|
<h6
|
||||||
|
class="box box--margin-top-1 box--margin-bottom-1 box--margin-left-1 box--flex-direction-row box--text-align-right typography typography--h6 typography--weight-bold typography--style-normal typography--color-text-default"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="currency-display-component"
|
||||||
|
title="0.000021 ETH"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="currency-display-component__prefix"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class="currency-display-component__text"
|
||||||
|
>
|
||||||
|
0.000021
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="currency-display-component__suffix"
|
||||||
|
>
|
||||||
|
ETH
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="transaction-detail-item__row"
|
||||||
|
>
|
||||||
|
<div />
|
||||||
|
<h6
|
||||||
|
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography transaction-detail-item__row-subText typography--h7 typography--weight-normal typography--style-normal typography--align-end typography--color-text-alternative"
|
||||||
|
>
|
||||||
|
<strong>
|
||||||
|
Max fee:
|
||||||
|
</strong>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="currency-display-component"
|
||||||
|
title="0.000021 ETH"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="currency-display-component__prefix"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class="currency-display-component__text"
|
||||||
|
>
|
||||||
|
0.000021
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="currency-display-component__suffix"
|
||||||
|
>
|
||||||
|
ETH
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`ConfirmLegacyGasDisplay should match snapshot 2`] = `<div />`;
|
@ -0,0 +1,149 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
|
||||||
|
import { useI18nContext } from '../../../../hooks/useI18nContext';
|
||||||
|
import {
|
||||||
|
getIsMainnet,
|
||||||
|
getPreferences,
|
||||||
|
getUnapprovedTransactions,
|
||||||
|
getUseCurrencyRateCheck,
|
||||||
|
transactionFeeSelector,
|
||||||
|
txDataSelector,
|
||||||
|
} from '../../../../selectors';
|
||||||
|
import { PRIMARY, SECONDARY } from '../../../../helpers/constants/common';
|
||||||
|
|
||||||
|
import TransactionDetailItem from '../../transaction-detail-item';
|
||||||
|
import UserPreferencedCurrencyDisplay from '../../user-preferenced-currency-display';
|
||||||
|
import InfoTooltip from '../../../ui/info-tooltip';
|
||||||
|
import LoadingHeartBeat from '../../../ui/loading-heartbeat';
|
||||||
|
import { Text } from '../../../component-library/text';
|
||||||
|
import {
|
||||||
|
FONT_STYLE,
|
||||||
|
TextVariant,
|
||||||
|
TextColor,
|
||||||
|
} from '../../../../helpers/constants/design-system';
|
||||||
|
import { useDraftTransactionGasValues } from '../../../../hooks/useDraftTransactionGasValues';
|
||||||
|
|
||||||
|
const renderHeartBeatIfNotInTest = () =>
|
||||||
|
process.env.IN_TEST ? null : <LoadingHeartBeat />;
|
||||||
|
|
||||||
|
const ConfirmLegacyGasDisplay = () => {
|
||||||
|
const t = useI18nContext();
|
||||||
|
|
||||||
|
// state selectors
|
||||||
|
const isMainnet = useSelector(getIsMainnet);
|
||||||
|
const useCurrencyRateCheck = useSelector(getUseCurrencyRateCheck);
|
||||||
|
const { useNativeCurrencyAsPrimaryCurrency } = useSelector(getPreferences);
|
||||||
|
const unapprovedTxs = useSelector(getUnapprovedTransactions);
|
||||||
|
const { transactionData } = useDraftTransactionGasValues();
|
||||||
|
const txData = useSelector((state) => txDataSelector(state));
|
||||||
|
const { id: transactionId, dappSuggestedGasFees } = txData;
|
||||||
|
const transaction = Object.keys(transactionData).length
|
||||||
|
? transactionData
|
||||||
|
: unapprovedTxs[transactionId] || {};
|
||||||
|
const { hexMinimumTransactionFee, hexMaximumTransactionFee } = useSelector(
|
||||||
|
(state) => transactionFeeSelector(state, transaction),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TransactionDetailItem
|
||||||
|
key="legacy-gas-details"
|
||||||
|
detailTitle={
|
||||||
|
dappSuggestedGasFees ? (
|
||||||
|
<>
|
||||||
|
{t('transactionDetailGasHeading')}
|
||||||
|
<InfoTooltip
|
||||||
|
contentText={t('transactionDetailDappGasTooltip')}
|
||||||
|
position="top"
|
||||||
|
>
|
||||||
|
<i className="fa fa-info-circle" />
|
||||||
|
</InfoTooltip>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{t('transactionDetailGasHeading')}
|
||||||
|
<InfoTooltip
|
||||||
|
contentText={
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
{t('transactionDetailGasTooltipIntro', [
|
||||||
|
isMainnet ? t('networkNameEthereum') : '',
|
||||||
|
])}
|
||||||
|
</p>
|
||||||
|
<p>{t('transactionDetailGasTooltipExplanation')}</p>
|
||||||
|
<p>
|
||||||
|
<a
|
||||||
|
href="https://community.metamask.io/t/what-is-gas-why-do-transactions-take-so-long/3172"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
{t('transactionDetailGasTooltipConversion')}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
position="top"
|
||||||
|
>
|
||||||
|
<i className="fa fa-info-circle" />
|
||||||
|
</InfoTooltip>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
detailText={
|
||||||
|
useCurrencyRateCheck && (
|
||||||
|
<div>
|
||||||
|
{renderHeartBeatIfNotInTest()}
|
||||||
|
<UserPreferencedCurrencyDisplay
|
||||||
|
type={SECONDARY}
|
||||||
|
value={hexMinimumTransactionFee}
|
||||||
|
hideLabel={Boolean(useNativeCurrencyAsPrimaryCurrency)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
detailTotal={
|
||||||
|
<div>
|
||||||
|
{renderHeartBeatIfNotInTest()}
|
||||||
|
<UserPreferencedCurrencyDisplay
|
||||||
|
type={PRIMARY}
|
||||||
|
value={hexMinimumTransactionFee}
|
||||||
|
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
|
||||||
|
numberOfDecimals={6}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
subText={
|
||||||
|
<>
|
||||||
|
<strong key="editGasSubTextFeeLabel">
|
||||||
|
{t('editGasSubTextFeeLabel')}
|
||||||
|
</strong>
|
||||||
|
<div key="editGasSubTextFeeValue">
|
||||||
|
{renderHeartBeatIfNotInTest()}
|
||||||
|
<UserPreferencedCurrencyDisplay
|
||||||
|
key="editGasSubTextFeeAmount"
|
||||||
|
type={PRIMARY}
|
||||||
|
value={hexMaximumTransactionFee}
|
||||||
|
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
subTitle={
|
||||||
|
<>
|
||||||
|
{dappSuggestedGasFees && (
|
||||||
|
<Text
|
||||||
|
variant={TextVariant.bodySm}
|
||||||
|
fontStyle={FONT_STYLE.ITALIC}
|
||||||
|
color={TextColor.textAlternative}
|
||||||
|
as="h6"
|
||||||
|
>
|
||||||
|
{t('transactionDetailDappGasMoreInfo')}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConfirmLegacyGasDisplay;
|
@ -0,0 +1,28 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
|
||||||
|
import mockState from '../../../../../test/data/mock-state.json';
|
||||||
|
import configureStore from '../../../../store/store';
|
||||||
|
|
||||||
|
import README from './README.mdx';
|
||||||
|
import ConfirmLegacyGasDisplay from './confirm-legacy-gas-display';
|
||||||
|
|
||||||
|
const store = configureStore(mockState);
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Components/App/ConfirmLegacyGasDisplay',
|
||||||
|
|
||||||
|
component: ConfirmLegacyGasDisplay,
|
||||||
|
decorators: [(story) => <Provider store={store}>{story()}</Provider>],
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
page: README,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultStory = () => {
|
||||||
|
return <ConfirmLegacyGasDisplay />;
|
||||||
|
};
|
||||||
|
|
||||||
|
DefaultStory.storyName = 'Default';
|
@ -0,0 +1,110 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { screen, waitFor } from '@testing-library/react';
|
||||||
|
|
||||||
|
import mockState from '../../../../../test/data/mock-state.json';
|
||||||
|
import { renderWithProvider } from '../../../../../test/jest';
|
||||||
|
import configureStore from '../../../../store/store';
|
||||||
|
|
||||||
|
import ConfirmLegacyGasDisplay from './confirm-legacy-gas-display';
|
||||||
|
|
||||||
|
const render = ({ contextProps } = {}) => {
|
||||||
|
const store = configureStore({
|
||||||
|
...mockState,
|
||||||
|
...contextProps,
|
||||||
|
metamask: {
|
||||||
|
...mockState.metamask,
|
||||||
|
accounts: {
|
||||||
|
[mockState.metamask.selectedAddress]: {
|
||||||
|
address: mockState.metamask.selectedAddress,
|
||||||
|
balance: '0x1F4',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
unapprovedTxs: {
|
||||||
|
8393540981007587: {
|
||||||
|
...mockState.metamask.unapprovedTxs[8393540981007587],
|
||||||
|
txParams: {
|
||||||
|
from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
|
||||||
|
to: '0xc42edfcc21ed14dda456aa0756c153f7985d8813',
|
||||||
|
value: '0x0',
|
||||||
|
gas: '0x5208',
|
||||||
|
gasPrice: '0x3b9aca00',
|
||||||
|
type: '0x0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
preferences: {
|
||||||
|
useNativeCurrencyAsPrimaryCurrency: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
confirmTransaction: {
|
||||||
|
txData: {
|
||||||
|
id: 8393540981007587,
|
||||||
|
status: 'unapproved',
|
||||||
|
chainId: '0x5',
|
||||||
|
txParams: {
|
||||||
|
from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
|
||||||
|
to: '0xc42edfcc21ed14dda456aa0756c153f7985d8813',
|
||||||
|
value: '0x0',
|
||||||
|
gas: '0x5208',
|
||||||
|
gasPrice: '0x3b9aca00',
|
||||||
|
type: '0x0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return renderWithProvider(<ConfirmLegacyGasDisplay />, store);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('ConfirmLegacyGasDisplay', () => {
|
||||||
|
it('should match snapshot', async () => {
|
||||||
|
const { container } = render();
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render label', async () => {
|
||||||
|
render();
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.queryByText('Estimated gas fee')).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText('Max fee:')).toBeInTheDocument();
|
||||||
|
expect(screen.queryAllByText('ETH').length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render gas fee details', async () => {
|
||||||
|
render();
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.queryAllByTitle('0.000021 ETH').length).toBeGreaterThan(0);
|
||||||
|
expect(screen.queryAllByText('ETH').length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render label and gas details with draftTransaction', async () => {
|
||||||
|
render({
|
||||||
|
send: {
|
||||||
|
currentTransactionUUID: '1d40b578-6184-4607-8513-762c24d0a19b',
|
||||||
|
draftTransactions: {
|
||||||
|
'1d40b578-6184-4607-8513-762c24d0a19b': {
|
||||||
|
gas: {
|
||||||
|
error: null,
|
||||||
|
gasLimit: '0x5208',
|
||||||
|
gasPrice: '0x3b9aca00',
|
||||||
|
gasTotal: '0x157c9fbb9a000',
|
||||||
|
maxFeePerGas: '0x0',
|
||||||
|
maxPriorityFeePerGas: '0x0',
|
||||||
|
wasManuallyEdited: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.queryByText('Estimated gas fee')).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText('Max fee:')).toBeInTheDocument();
|
||||||
|
expect(screen.queryAllByText('ETH').length).toBeGreaterThan(0);
|
||||||
|
expect(screen.queryAllByTitle('0.000021 ETH').length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1 @@
|
|||||||
|
export { default as ConfirmLegacyGasDisplay } from './confirm-legacy-gas-display';
|
1
ui/components/app/confirm-gas-display/index.js
Normal file
1
ui/components/app/confirm-gas-display/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as ConfirmGasDisplay } from './confirm-gas-display';
|
@ -5,7 +5,12 @@ import { useSelector } from 'react-redux';
|
|||||||
|
|
||||||
import { TextColor } from '../../../helpers/constants/design-system';
|
import { TextColor } from '../../../helpers/constants/design-system';
|
||||||
import { PRIMARY, SECONDARY } from '../../../helpers/constants/common';
|
import { PRIMARY, SECONDARY } from '../../../helpers/constants/common';
|
||||||
import { getPreferences, getUseCurrencyRateCheck } from '../../../selectors';
|
import {
|
||||||
|
getPreferences,
|
||||||
|
getUseCurrencyRateCheck,
|
||||||
|
transactionFeeSelector,
|
||||||
|
} from '../../../selectors';
|
||||||
|
import { getCurrentDraftTransaction } from '../../../ducks/send';
|
||||||
import { useGasFeeContext } from '../../../contexts/gasFee';
|
import { useGasFeeContext } from '../../../contexts/gasFee';
|
||||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||||
|
|
||||||
@ -14,10 +19,20 @@ import LoadingHeartBeat from '../../ui/loading-heartbeat';
|
|||||||
import GasTiming from '../gas-timing/gas-timing.component';
|
import GasTiming from '../gas-timing/gas-timing.component';
|
||||||
import TransactionDetailItem from '../transaction-detail-item/transaction-detail-item.component';
|
import TransactionDetailItem from '../transaction-detail-item/transaction-detail-item.component';
|
||||||
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display';
|
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display';
|
||||||
|
import { hexWEIToDecGWEI } from '../../../../shared/modules/conversion.utils';
|
||||||
|
import { useDraftTransactionGasValues } from '../../../hooks/useDraftTransactionGasValues';
|
||||||
import GasDetailsItemTitle from './gas-details-item-title';
|
import GasDetailsItemTitle from './gas-details-item-title';
|
||||||
|
|
||||||
const GasDetailsItem = ({ userAcknowledgedGasMissing = false }) => {
|
const GasDetailsItem = ({ userAcknowledgedGasMissing = false }) => {
|
||||||
const t = useI18nContext();
|
const t = useI18nContext();
|
||||||
|
const draftTransaction = useSelector(getCurrentDraftTransaction);
|
||||||
|
const { transactionData } = useDraftTransactionGasValues();
|
||||||
|
|
||||||
|
const {
|
||||||
|
hexMinimumTransactionFee: draftHexMinimumTransactionFee,
|
||||||
|
hexMaximumTransactionFee: draftHexMaximumTransactionFee,
|
||||||
|
} = useSelector((state) => transactionFeeSelector(state, transactionData));
|
||||||
|
|
||||||
const {
|
const {
|
||||||
estimateUsed,
|
estimateUsed,
|
||||||
hasSimulationError,
|
hasSimulationError,
|
||||||
@ -41,7 +56,8 @@ const GasDetailsItem = ({ userAcknowledgedGasMissing = false }) => {
|
|||||||
detailTitle={<GasDetailsItemTitle />}
|
detailTitle={<GasDetailsItemTitle />}
|
||||||
detailTitleColor={TextColor.textDefault}
|
detailTitleColor={TextColor.textDefault}
|
||||||
detailText={
|
detailText={
|
||||||
useCurrencyRateCheck && (
|
useCurrencyRateCheck &&
|
||||||
|
Object.keys(draftTransaction).length === 0 && (
|
||||||
<div className="gas-details-item__currency-container">
|
<div className="gas-details-item__currency-container">
|
||||||
<LoadingHeartBeat estimateUsed={estimateUsed} />
|
<LoadingHeartBeat estimateUsed={estimateUsed} />
|
||||||
<UserPreferencedCurrencyDisplay
|
<UserPreferencedCurrencyDisplay
|
||||||
@ -57,7 +73,7 @@ const GasDetailsItem = ({ userAcknowledgedGasMissing = false }) => {
|
|||||||
<LoadingHeartBeat estimateUsed={estimateUsed} />
|
<LoadingHeartBeat estimateUsed={estimateUsed} />
|
||||||
<UserPreferencedCurrencyDisplay
|
<UserPreferencedCurrencyDisplay
|
||||||
type={PRIMARY}
|
type={PRIMARY}
|
||||||
value={hexMinimumTransactionFee}
|
value={hexMinimumTransactionFee || draftHexMinimumTransactionFee}
|
||||||
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
|
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -86,7 +102,9 @@ const GasDetailsItem = ({ userAcknowledgedGasMissing = false }) => {
|
|||||||
<UserPreferencedCurrencyDisplay
|
<UserPreferencedCurrencyDisplay
|
||||||
key="editGasSubTextFeeAmount"
|
key="editGasSubTextFeeAmount"
|
||||||
type={PRIMARY}
|
type={PRIMARY}
|
||||||
value={hexMaximumTransactionFee}
|
value={
|
||||||
|
hexMaximumTransactionFee || draftHexMaximumTransactionFee
|
||||||
|
}
|
||||||
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
|
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -95,8 +113,14 @@ const GasDetailsItem = ({ userAcknowledgedGasMissing = false }) => {
|
|||||||
}
|
}
|
||||||
subTitle={
|
subTitle={
|
||||||
<GasTiming
|
<GasTiming
|
||||||
maxPriorityFeePerGas={maxPriorityFeePerGas.toString()}
|
maxPriorityFeePerGas={(
|
||||||
maxFeePerGas={maxFeePerGas.toString()}
|
maxPriorityFeePerGas ||
|
||||||
|
hexWEIToDecGWEI(transactionData.txParams.maxPriorityFeePerGas)
|
||||||
|
).toString()}
|
||||||
|
maxFeePerGas={(
|
||||||
|
maxFeePerGas ||
|
||||||
|
hexWEIToDecGWEI(transactionData.txParams.maxFeePerGas)
|
||||||
|
).toString()}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -112,11 +112,11 @@ import { TextColor, BackgroundColor } from '../../../helpers/constants/design-sy
|
|||||||
|
|
||||||
### Font Weight
|
### Font Weight
|
||||||
|
|
||||||
Use the `fontWeight` prop and the `FONT_WEIGHT` object from `./ui/helpers/constants/design-system.js` to change the font weight of the `Text` component. There are 3 font weights:
|
Use the `fontWeight` prop and the `FontWeight` enum from `./ui/helpers/constants/design-system.js` to change the font weight of the `Text` component. There are 3 font weights:
|
||||||
|
|
||||||
- `FONT_WEIGHT.NORMAL` = `normal` || `400`
|
- `FontWeight.Normal` = `normal` || `400`
|
||||||
- `FONT_WEIGHT.MEDIUM` = `medium` || `500`
|
- `FontWeight.Medium` = `medium` || `500`
|
||||||
- `FONT_WEIGHT.BOLD` = `bold` || `700`
|
- `FontWeight.Bold` = `bold` || `700`
|
||||||
|
|
||||||
<Canvas>
|
<Canvas>
|
||||||
<Story id="components-componentlibrary-text--font-weight" />
|
<Story id="components-componentlibrary-text--font-weight" />
|
||||||
@ -124,25 +124,25 @@ Use the `fontWeight` prop and the `FONT_WEIGHT` object from `./ui/helpers/consta
|
|||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { Text } from '../../component-library';
|
import { Text } from '../../component-library';
|
||||||
import { FONT_WEIGHT } from '../../../helpers/constants/design-system';
|
import { FontWeight } from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
<Text fontWeight={FONT_WEIGHT.NORMAL}>
|
<Text fontWeight={FontWeight.Normal}>
|
||||||
normal
|
normal
|
||||||
</Text>
|
</Text>
|
||||||
<Text fontWeight={FONT_WEIGHT.MEDIUM}>
|
<Text fontWeight={FontWeight.Medium}>
|
||||||
medium
|
medium
|
||||||
</Text>
|
</Text>
|
||||||
<Text fontWeight={FONT_WEIGHT.BOLD}>
|
<Text fontWeight={FontWeight.Bold}>
|
||||||
bold
|
bold
|
||||||
</Text>
|
</Text>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Font Style
|
### Font Style
|
||||||
|
|
||||||
Use the `fontStyle` prop and the `FONT_STYLE` object from `./ui/helpers/constants/design-system.js` to change the font style of the `Text` component. There are 2 font styles:
|
Use the `fontStyle` prop and the `FontStyle` enum from `./ui/helpers/constants/design-system.js` to change the font style of the `Text` component. There are 2 font styles:
|
||||||
|
|
||||||
- `FONT_STYLE.NORMAL`
|
- `FontStyle.Normal`
|
||||||
- `FONT_STYLE.ITALIC`
|
- `FontStyle.Italic`
|
||||||
|
|
||||||
<Canvas>
|
<Canvas>
|
||||||
<Story id="components-componentlibrary-text--font-style" />
|
<Story id="components-componentlibrary-text--font-style" />
|
||||||
@ -150,19 +150,19 @@ Use the `fontStyle` prop and the `FONT_STYLE` object from `./ui/helpers/constant
|
|||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { Text } from '../../component-library';
|
import { Text } from '../../component-library';
|
||||||
import { FONT_STYLE } from '../../../helpers/constants/design-system';
|
import { FontStyle } from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
<Text fontStyle={FONT_STYLE.NORMAL}>
|
<Text fontStyle={FontStyle.Normal}>
|
||||||
normal
|
normal
|
||||||
</Text>
|
</Text>
|
||||||
<Text fontStyle={FONT_STYLE.ITALIC}>
|
<Text fontStyle={FontStyle.Italic}>
|
||||||
bold
|
bold
|
||||||
</Text>
|
</Text>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Text Transform
|
### Text Transform
|
||||||
|
|
||||||
Use the `textTransform` prop and the `TEXT_TRANSFORM` object from `./ui/helpers/constants/design-system.js` to change the text alignment of the `Text` component
|
Use the `textTransform` prop and the `TextTransform` enum from `./ui/helpers/constants/design-system.ts` to change the text alignment of the `Text` component
|
||||||
|
|
||||||
<Canvas>
|
<Canvas>
|
||||||
<Story id="components-componentlibrary-text--text-transform" />
|
<Story id="components-componentlibrary-text--text-transform" />
|
||||||
@ -170,22 +170,22 @@ Use the `textTransform` prop and the `TEXT_TRANSFORM` object from `./ui/helpers/
|
|||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { Text } from '../../component-library';
|
import { Text } from '../../component-library';
|
||||||
import { TEXT_TRANSFORM } from '../../../helpers/constants/design-system';
|
import { TextTransform } from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
<Text textAlign={TEXT_TRANSFORM.UPPERCASE}>
|
<Text textAlign={TextTransform.Uppercase}>
|
||||||
uppercase
|
uppercase
|
||||||
</Text>
|
</Text>
|
||||||
<Text textAlign={TEXT_TRANSFORM.LOWERCASE}>
|
<Text textAlign={TextTransform.Lowercase}>
|
||||||
lowercase
|
lowercase
|
||||||
</Text>
|
</Text>
|
||||||
<Text textAlign={TEXT_TRANSFORM.CAPITALIZE}>
|
<Text textAlign={TextTransform.Capitalize}>
|
||||||
capitalize
|
capitalize
|
||||||
</Text>
|
</Text>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Text Align
|
### Text Align
|
||||||
|
|
||||||
Use the `textAlign` prop and the `TEXT_ALIGN` object from `./ui/helpers/constants/design-system.js` to change the text alignment of the `Text` component
|
Use the `textAlign` prop and the `TextAlign` enum from `./ui/helpers/constants/design-system.ts` to change the text alignment of the `Text` component
|
||||||
|
|
||||||
<Canvas>
|
<Canvas>
|
||||||
<Story id="components-componentlibrary-text--text-align" />
|
<Story id="components-componentlibrary-text--text-align" />
|
||||||
@ -193,28 +193,28 @@ Use the `textAlign` prop and the `TEXT_ALIGN` object from `./ui/helpers/constant
|
|||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { Text } from '../../component-library';
|
import { Text } from '../../component-library';
|
||||||
import { TEXT_ALIGN } from '../../../helpers/constants/design-system';
|
import { TextAlign } from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
<Text textAlign={TEXT_ALIGN.LEFT}>
|
<Text textAlign={TextAlign.Left}>
|
||||||
left
|
left
|
||||||
</Text>
|
</Text>
|
||||||
<Text textAlign={TEXT_ALIGN.CENTER}>
|
<Text textAlign={TextAlign.Center}>
|
||||||
center
|
center
|
||||||
</Text>
|
</Text>
|
||||||
<Text textAlign={TEXT_ALIGN.RIGHT}>
|
<Text textAlign={TextAlign.Right}>
|
||||||
right
|
right
|
||||||
</Text>
|
</Text>
|
||||||
<Text textAlign={TEXT_ALIGN.JUSTIFY}>
|
<Text textAlign={TextAlign.Justify}>
|
||||||
justify
|
justify
|
||||||
</Text>
|
</Text>
|
||||||
<Text textAlign={TEXT_ALIGN.END}>
|
<Text textAlign={TextAlign.End}>
|
||||||
end
|
end
|
||||||
</Text>
|
</Text>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Overflow Wrap
|
### Overflow Wrap
|
||||||
|
|
||||||
Use the `overflowWrap` prop and the `OVERFLOW_WRAP` object from `./ui/helpers/constants/design-system.js` to change the overflow wrap of the `Text` component
|
Use the `overflowWrap` prop and the `OverflowWrap` enum from `./ui/helpers/constants/design-system.ts` to change the overflow wrap of the `Text` component
|
||||||
|
|
||||||
<Canvas>
|
<Canvas>
|
||||||
<Story id="components-componentlibrary-text--overflow-wrap" />
|
<Story id="components-componentlibrary-text--overflow-wrap" />
|
||||||
@ -222,7 +222,7 @@ Use the `overflowWrap` prop and the `OVERFLOW_WRAP` object from `./ui/helpers/co
|
|||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { Text } from '../../component-library';
|
import { Text } from '../../component-library';
|
||||||
import { OVERFLOW_WRAP } from '../../../helpers/constants/design-system';
|
import { OverflowWrap } from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@ -231,11 +231,11 @@ import { OVERFLOW_WRAP } from '../../../helpers/constants/design-system';
|
|||||||
display: 'block',
|
display: 'block',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text overflowWrap={OVERFLOW_WRAP.NORMAL}>
|
<Text overflowWrap={OverflowWrap.Normal}>
|
||||||
{OVERFLOW_WRAP.NORMAL}: 0x39013f961c378f02c2b82a6e1d31e9812786fd9d
|
{OverflowWrap.Normal}: 0x39013f961c378f02c2b82a6e1d31e9812786fd9d
|
||||||
</Text>
|
</Text>
|
||||||
<Text overflowWrap={OVERFLOW_WRAP.BREAK_WORD}>
|
<Text overflowWrap={OverflowWrap.BreakWord}>
|
||||||
{OVERFLOW_WRAP.BREAK_WORD}: 0x39013f961c378f02c2b82a6e1d31e9812786fd9d
|
{OverflowWrap.BreakWord}: 0x39013f961c378f02c2b82a6e1d31e9812786fd9d
|
||||||
</Text>
|
</Text>
|
||||||
</div>;
|
</div>;
|
||||||
```
|
```
|
||||||
@ -538,14 +538,14 @@ import { TextVariant } from '../../../helpers/constants/design-system';
|
|||||||
|
|
||||||
The prop name `align` has been deprecated in favor of `textAlign`
|
The prop name `align` has been deprecated in favor of `textAlign`
|
||||||
|
|
||||||
Values and using the `TEXT_ALIGN` object from `./ui/helpers/constants/design-system.js` remain the same
|
Values using the `TextAlign` object from `./ui/helpers/constants/design-system.js` remain the same
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
// Before
|
// Before
|
||||||
<Typograpghy align={TEXT_ALIGN.CENTER}>Demo</Typograpghy>;
|
<Typography align={TEXT_ALIGN.CENTER}>Demo</Typography>;
|
||||||
|
|
||||||
// After
|
// After
|
||||||
<Text textAlign={TEXT_ALIGN.CENTER}>Demo</Text>;
|
<Text textAlign={TextAlign.Center}>Demo</Text>;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Box Props
|
### Box Props
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import {
|
import {
|
||||||
FONT_STYLE,
|
FontStyle,
|
||||||
FONT_WEIGHT,
|
FontWeight,
|
||||||
OVERFLOW_WRAP,
|
OverflowWrap,
|
||||||
TEXT_ALIGN,
|
TextAlign,
|
||||||
TextColor,
|
TextColor,
|
||||||
TEXT_TRANSFORM,
|
TextTransform,
|
||||||
TextVariant,
|
TextVariant,
|
||||||
Color,
|
Color,
|
||||||
} from '../../../helpers/constants/design-system';
|
} from '../../../helpers/constants/design-system';
|
||||||
@ -99,9 +99,9 @@ describe('Text', () => {
|
|||||||
it('should render the Text with proper font weight class name', () => {
|
it('should render the Text with proper font weight class name', () => {
|
||||||
const { getByText } = render(
|
const { getByText } = render(
|
||||||
<>
|
<>
|
||||||
<Text fontWeight={FONT_WEIGHT.BOLD}>bold</Text>
|
<Text fontWeight={FontWeight.Bold}>bold</Text>
|
||||||
<Text fontWeight={FONT_WEIGHT.MEDIUM}>medium</Text>
|
<Text fontWeight={FontWeight.Medium}>medium</Text>
|
||||||
<Text fontWeight={FONT_WEIGHT.NORMAL}>normal</Text>
|
<Text fontWeight={FontWeight.Normal}>normal</Text>
|
||||||
</>,
|
</>,
|
||||||
);
|
);
|
||||||
expect(getByText('bold')).toHaveClass('mm-text--font-weight-bold');
|
expect(getByText('bold')).toHaveClass('mm-text--font-weight-bold');
|
||||||
@ -156,8 +156,8 @@ describe('Text', () => {
|
|||||||
it('should render the Text with proper font style class name', () => {
|
it('should render the Text with proper font style class name', () => {
|
||||||
const { getByText } = render(
|
const { getByText } = render(
|
||||||
<>
|
<>
|
||||||
<Text fontStyle={FONT_STYLE.ITALIC}>italic</Text>
|
<Text fontStyle={FontStyle.Italic}>italic</Text>
|
||||||
<Text fontStyle={FONT_STYLE.NORMAL}>normal</Text>
|
<Text fontStyle={FontStyle.Normal}>normal</Text>
|
||||||
</>,
|
</>,
|
||||||
);
|
);
|
||||||
expect(getByText('italic')).toHaveClass('mm-text--font-style-italic');
|
expect(getByText('italic')).toHaveClass('mm-text--font-style-italic');
|
||||||
@ -167,11 +167,11 @@ describe('Text', () => {
|
|||||||
it('should render the Text with proper text align class name', () => {
|
it('should render the Text with proper text align class name', () => {
|
||||||
const { getByText } = render(
|
const { getByText } = render(
|
||||||
<>
|
<>
|
||||||
<Text textAlign={TEXT_ALIGN.LEFT}>left</Text>
|
<Text textAlign={TextAlign.Left}>left</Text>
|
||||||
<Text textAlign={TEXT_ALIGN.CENTER}>center</Text>
|
<Text textAlign={TextAlign.Center}>center</Text>
|
||||||
<Text textAlign={TEXT_ALIGN.RIGHT}>right</Text>
|
<Text textAlign={TextAlign.Right}>right</Text>
|
||||||
<Text textAlign={TEXT_ALIGN.JUSTIFY}>justify</Text>
|
<Text textAlign={TextAlign.Justify}>justify</Text>
|
||||||
<Text textAlign={TEXT_ALIGN.END}>end</Text>
|
<Text textAlign={TextAlign.End}>end</Text>
|
||||||
</>,
|
</>,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -185,8 +185,8 @@ describe('Text', () => {
|
|||||||
it('should render the Text with proper overflow wrap class name', () => {
|
it('should render the Text with proper overflow wrap class name', () => {
|
||||||
const { getByText } = render(
|
const { getByText } = render(
|
||||||
<>
|
<>
|
||||||
<Text overflowWrap={OVERFLOW_WRAP.BREAK_WORD}>break-word</Text>
|
<Text overflowWrap={OverflowWrap.BreakWord}>break-word</Text>
|
||||||
<Text overflowWrap={OVERFLOW_WRAP.NORMAL}>normal</Text>
|
<Text overflowWrap={OverflowWrap.Normal}>normal</Text>
|
||||||
</>,
|
</>,
|
||||||
);
|
);
|
||||||
expect(getByText('break-word')).toHaveClass(
|
expect(getByText('break-word')).toHaveClass(
|
||||||
@ -207,9 +207,9 @@ describe('Text', () => {
|
|||||||
it('should render the Text with proper text transform class name', () => {
|
it('should render the Text with proper text transform class name', () => {
|
||||||
const { getByText } = render(
|
const { getByText } = render(
|
||||||
<>
|
<>
|
||||||
<Text textTransform={TEXT_TRANSFORM.UPPERCASE}>uppercase</Text>
|
<Text textTransform={TextTransform.Uppercase}>uppercase</Text>
|
||||||
<Text textTransform={TEXT_TRANSFORM.LOWERCASE}>lowercase</Text>
|
<Text textTransform={TextTransform.Lowercase}>lowercase</Text>
|
||||||
<Text textTransform={TEXT_TRANSFORM.CAPITALIZE}>capitalize</Text>
|
<Text textTransform={TextTransform.Capitalize}>capitalize</Text>
|
||||||
</>,
|
</>,
|
||||||
);
|
);
|
||||||
expect(getByText('uppercase')).toHaveClass(
|
expect(getByText('uppercase')).toHaveClass(
|
||||||
|
@ -2,7 +2,7 @@ import React, { forwardRef, Ref } from 'react';
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import Box from '../../ui/box';
|
import Box from '../../ui/box';
|
||||||
import {
|
import {
|
||||||
FONT_WEIGHT,
|
FontWeight,
|
||||||
TextVariant,
|
TextVariant,
|
||||||
TextColor,
|
TextColor,
|
||||||
} from '../../../helpers/constants/design-system';
|
} from '../../../helpers/constants/design-system';
|
||||||
@ -49,7 +49,7 @@ export const Text = forwardRef(function Text(
|
|||||||
let strongTagFontWeight;
|
let strongTagFontWeight;
|
||||||
|
|
||||||
if (Tag === 'strong') {
|
if (Tag === 'strong') {
|
||||||
strongTagFontWeight = FONT_WEIGHT.BOLD;
|
strongTagFontWeight = FontWeight.Bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
const computedClassName = classnames(
|
const computedClassName = classnames(
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { BoxProps } from '../../ui/box/box.d';
|
import type { BoxProps } from '../../ui/box/box.d';
|
||||||
import {
|
import {
|
||||||
FONT_WEIGHT,
|
FontWeight,
|
||||||
FONT_STYLE,
|
FontStyle,
|
||||||
TextVariant,
|
TextVariant,
|
||||||
TEXT_ALIGN,
|
TextAlign,
|
||||||
TEXT_TRANSFORM,
|
TextTransform,
|
||||||
OVERFLOW_WRAP,
|
OverflowWrap,
|
||||||
TextColor,
|
TextColor,
|
||||||
Color,
|
Color,
|
||||||
} from '../../../helpers/constants/design-system';
|
} from '../../../helpers/constants/design-system';
|
||||||
@ -70,35 +70,35 @@ export interface TextProps extends BoxProps {
|
|||||||
*/
|
*/
|
||||||
color?: TextColor | Color;
|
color?: TextColor | Color;
|
||||||
/**
|
/**
|
||||||
* The font-weight of the Text component. Should use the FONT_WEIGHT object from
|
* The font-weight of the Text component. Should use the FontWeight enum from
|
||||||
* ./ui/helpers/constants/design-system.js
|
* ./ui/helpers/constants/design-system.js
|
||||||
*/
|
*/
|
||||||
fontWeight?: keyof typeof FONT_WEIGHT;
|
fontWeight?: FontWeight;
|
||||||
/**
|
/**
|
||||||
* The font-style of the Text component. Should use the FONT_STYLE object from
|
* The font-style of the Text component. Should use the FontStyle enum from
|
||||||
* ./ui/helpers/constants/design-system.js
|
* ./ui/helpers/constants/design-system.js
|
||||||
*/
|
*/
|
||||||
fontStyle?: keyof typeof FONT_STYLE;
|
fontStyle?: FontStyle;
|
||||||
/**
|
/**
|
||||||
* The textTransform of the Text component. Should use the TEXT_TRANSFORM object from
|
* The textTransform of the Text component. Should use the TextTransform enum from
|
||||||
* ./ui/helpers/constants/design-system.js
|
* ./ui/helpers/constants/design-system.js
|
||||||
*/
|
*/
|
||||||
textTransform?: keyof typeof TEXT_TRANSFORM;
|
textTransform?: TextTransform;
|
||||||
/**
|
/**
|
||||||
* The text-align of the Text component. Should use the TEXT_ALIGN object from
|
* The text-align of the Text component. Should use the TextAlign enum from
|
||||||
* ./ui/helpers/constants/design-system.js
|
* ./ui/helpers/constants/design-system.js
|
||||||
*/
|
*/
|
||||||
textAlign?: keyof typeof TEXT_ALIGN;
|
textAlign?: TextAlign;
|
||||||
/**
|
/**
|
||||||
* Change the dir (direction) global attribute of text to support the direction a language is written
|
* Change the dir (direction) global attribute of text to support the direction a language is written
|
||||||
* Possible values: `LEFT_TO_RIGHT` (default), `RIGHT_TO_LEFT`, `AUTO` (user agent decides)
|
* Possible values: `LEFT_TO_RIGHT` (default), `RIGHT_TO_LEFT`, `AUTO` (user agent decides)
|
||||||
*/
|
*/
|
||||||
textDirection?: TextDirection;
|
textDirection?: TextDirection;
|
||||||
/**
|
/**
|
||||||
* The overflow-wrap of the Text component. Should use the OVERFLOW_WRAP object from
|
* The overflow-wrap of the Text component. Should use the OverflowWrap enum from
|
||||||
* ./ui/helpers/constants/design-system.js
|
* ./ui/helpers/constants/design-system.js
|
||||||
*/
|
*/
|
||||||
overflowWrap?: keyof typeof OVERFLOW_WRAP;
|
overflowWrap?: OverflowWrap;
|
||||||
/**
|
/**
|
||||||
* Used for long strings that can be cut off...
|
* Used for long strings that can be cut off...
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`CustodyLabels Component should render correctly 1`] = `
|
||||||
|
<div>
|
||||||
|
<label
|
||||||
|
class="box mm-text mm-label mm-label--html-for mm-text--body-md mm-text--font-weight-bold box--display-flex box--flex-direction-row box--align-items-center box--color-text-default"
|
||||||
|
for="address-index"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="box mm-text custody-label mm-text--h9 mm-text--font-weight-normal mm-text--text-transform-uppercase box--margin-top-1 box--margin-right-1 box--margin-bottom-2 box--padding-top-1 box--padding-right-2 box--padding-bottom-1 box--padding-left-2 box--flex-direction-row box--color-text-muted box--background-color-background-alternative box--rounded-sm"
|
||||||
|
>
|
||||||
|
value
|
||||||
|
</p>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
`;
|
58
ui/components/institutional/custody-labels/custody-labels.js
Normal file
58
ui/components/institutional/custody-labels/custody-labels.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Text, Label } from '../../component-library';
|
||||||
|
import {
|
||||||
|
TEXT_TRANSFORM,
|
||||||
|
BackgroundColor,
|
||||||
|
TextColor,
|
||||||
|
FONT_WEIGHT,
|
||||||
|
BorderRadius,
|
||||||
|
TypographyVariant,
|
||||||
|
} from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
|
const CustodyLabels = (props) => {
|
||||||
|
const { labels, index, background, hideNetwork } = props;
|
||||||
|
const filteredLabels = hideNetwork
|
||||||
|
? labels.filter((item) => item.key !== 'network_name')
|
||||||
|
: labels;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Label
|
||||||
|
display={['flex']}
|
||||||
|
flexDirection={['row']}
|
||||||
|
htmlFor={`address-${index || 0}`}
|
||||||
|
>
|
||||||
|
{filteredLabels.map((item) => (
|
||||||
|
<Text
|
||||||
|
key={item.key}
|
||||||
|
textTransform={TEXT_TRANSFORM.UPPERCASE}
|
||||||
|
className="custody-label"
|
||||||
|
style={background ? { background } : {}}
|
||||||
|
marginTop={1}
|
||||||
|
marginRight={1}
|
||||||
|
marginBottom={2}
|
||||||
|
paddingTop={1}
|
||||||
|
paddingBottom={1}
|
||||||
|
paddingLeft={2}
|
||||||
|
paddingRight={2}
|
||||||
|
backgroundColor={BackgroundColor.backgroundAlternative}
|
||||||
|
color={TextColor.textMuted}
|
||||||
|
fontWeight={FONT_WEIGHT.NORMAL}
|
||||||
|
borderRadius={BorderRadius.SM}
|
||||||
|
variant={TypographyVariant.H9}
|
||||||
|
>
|
||||||
|
{item.value}
|
||||||
|
</Text>
|
||||||
|
))}
|
||||||
|
</Label>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
CustodyLabels.propTypes = {
|
||||||
|
labels: PropTypes.array,
|
||||||
|
index: PropTypes.string,
|
||||||
|
background: PropTypes.string,
|
||||||
|
hideNetwork: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CustodyLabels;
|
@ -0,0 +1,9 @@
|
|||||||
|
.custody-label {
|
||||||
|
z-index: 1;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
white-space: nowrap;
|
||||||
|
max-width: 80px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
display: block;
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import CustodyLabels from '.';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Components/Institutional/CustodyLabels',
|
||||||
|
component: CustodyLabels,
|
||||||
|
args: {
|
||||||
|
labels: [{ key: 'testKey', value: 'value' }],
|
||||||
|
index: 'index',
|
||||||
|
hideNetwork: 'true',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultStory = (args) => <CustodyLabels {...args} />;
|
||||||
|
|
||||||
|
DefaultStory.storyName = 'CustodyLabels';
|
@ -0,0 +1,17 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { render } from '@testing-library/react';
|
||||||
|
import CustodyLabels from './custody-labels';
|
||||||
|
|
||||||
|
describe('CustodyLabels Component', () => {
|
||||||
|
it('should render correctly', () => {
|
||||||
|
const props = {
|
||||||
|
labels: [{ key: 'testKey', value: 'value' }],
|
||||||
|
index: 'index',
|
||||||
|
hideNetwork: 'true',
|
||||||
|
};
|
||||||
|
|
||||||
|
const { container } = render(<CustodyLabels {...props} />);
|
||||||
|
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
1
ui/components/institutional/custody-labels/index.js
Normal file
1
ui/components/institutional/custody-labels/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './custody-labels';
|
@ -297,6 +297,15 @@ export const BLOCK_SIZES = {
|
|||||||
FULL: 'full',
|
FULL: 'full',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum TextAlign {
|
||||||
|
Left = 'left',
|
||||||
|
Center = 'center',
|
||||||
|
Right = 'right',
|
||||||
|
Justify = 'justify',
|
||||||
|
End = 'end',
|
||||||
|
Start = 'start',
|
||||||
|
}
|
||||||
|
|
||||||
export const TEXT_ALIGN = {
|
export const TEXT_ALIGN = {
|
||||||
LEFT: 'left',
|
LEFT: 'left',
|
||||||
CENTER: 'center',
|
CENTER: 'center',
|
||||||
@ -306,24 +315,50 @@ export const TEXT_ALIGN = {
|
|||||||
START: 'start',
|
START: 'start',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum TextTransform {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||||
|
Uppercase = 'uppercase',
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||||
|
Lowercase = 'lowercase',
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||||
|
Capitalize = 'capitalize',
|
||||||
|
}
|
||||||
|
|
||||||
export const TEXT_TRANSFORM = {
|
export const TEXT_TRANSFORM = {
|
||||||
UPPERCASE: 'uppercase',
|
UPPERCASE: 'uppercase',
|
||||||
LOWERCASE: 'lowercase',
|
LOWERCASE: 'lowercase',
|
||||||
CAPITALIZE: 'capitalize',
|
CAPITALIZE: 'capitalize',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum FontWeight {
|
||||||
|
Bold = 'bold',
|
||||||
|
Medium = 'medium',
|
||||||
|
Normal = 'normal',
|
||||||
|
}
|
||||||
|
|
||||||
export const FONT_WEIGHT = {
|
export const FONT_WEIGHT = {
|
||||||
BOLD: 'bold',
|
BOLD: 'bold',
|
||||||
MEDIUM: 'medium',
|
MEDIUM: 'medium',
|
||||||
NORMAL: 'normal',
|
NORMAL: 'normal',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum OverflowWrap {
|
||||||
|
BreakWord = 'break-word',
|
||||||
|
Anywhere = 'anywhere',
|
||||||
|
Normal = 'normal',
|
||||||
|
}
|
||||||
|
|
||||||
export const OVERFLOW_WRAP = {
|
export const OVERFLOW_WRAP = {
|
||||||
BREAK_WORD: 'break-word',
|
BREAK_WORD: 'break-word',
|
||||||
ANYWHERE: 'anywhere',
|
ANYWHERE: 'anywhere',
|
||||||
NORMAL: 'normal',
|
NORMAL: 'normal',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum FontStyle {
|
||||||
|
Italic = 'italic',
|
||||||
|
Normal = 'normal',
|
||||||
|
}
|
||||||
|
|
||||||
export const FONT_STYLE = {
|
export const FONT_STYLE = {
|
||||||
ITALIC: 'italic',
|
ITALIC: 'italic',
|
||||||
NORMAL: 'normal',
|
NORMAL: 'normal',
|
||||||
|
@ -71,12 +71,6 @@ export const PERMISSION_DESCRIPTIONS = deepFreeze({
|
|||||||
weight: 2,
|
weight: 2,
|
||||||
}),
|
}),
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||||
[RestrictedMethods.snap_confirm]: ({ t }) => ({
|
|
||||||
label: t('permission_customConfirmation'),
|
|
||||||
description: t('permission_customConfirmationDescription'),
|
|
||||||
leftIcon: ICON_NAMES.SECURITY_TICK,
|
|
||||||
weight: 3,
|
|
||||||
}),
|
|
||||||
[RestrictedMethods.snap_dialog]: ({ t }) => ({
|
[RestrictedMethods.snap_dialog]: ({ t }) => ({
|
||||||
label: t('permission_dialog'),
|
label: t('permission_dialog'),
|
||||||
description: t('permission_dialogDescription'),
|
description: t('permission_dialogDescription'),
|
||||||
|
40
ui/hooks/useDraftTransactionGasValues.js
Normal file
40
ui/hooks/useDraftTransactionGasValues.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { getCurrentDraftTransaction } from '../ducks/send';
|
||||||
|
import { getUnapprovedTransactions } from '../selectors';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an object that resembles the txData.txParams from the Transactions state.
|
||||||
|
* While processing gas details for send transaction and edit transaction,
|
||||||
|
* the gas data from draftTransaction and unapprovedTx has to be reorganized
|
||||||
|
* to mimic the txdata.txParam from a confirmTransaction
|
||||||
|
*
|
||||||
|
* @returns {Object txData.txParams}
|
||||||
|
*/
|
||||||
|
export const useDraftTransactionGasValues = () => {
|
||||||
|
const draftTransaction = useSelector(getCurrentDraftTransaction);
|
||||||
|
const unapprovedTxs = useSelector(getUnapprovedTransactions);
|
||||||
|
|
||||||
|
let transactionData = {};
|
||||||
|
if (Object.keys(draftTransaction).length !== 0) {
|
||||||
|
const editingTransaction = unapprovedTxs[draftTransaction.id];
|
||||||
|
transactionData = {
|
||||||
|
txParams: {
|
||||||
|
gasPrice: draftTransaction.gas?.gasPrice,
|
||||||
|
gas: editingTransaction?.userEditedGasLimit
|
||||||
|
? editingTransaction?.txParams?.gas
|
||||||
|
: draftTransaction.gas?.gasLimit,
|
||||||
|
maxFeePerGas: editingTransaction?.txParams?.maxFeePerGas
|
||||||
|
? editingTransaction?.txParams?.maxFeePerGas
|
||||||
|
: draftTransaction.gas?.maxFeePerGas,
|
||||||
|
maxPriorityFeePerGas: editingTransaction?.txParams?.maxPriorityFeePerGas
|
||||||
|
? editingTransaction?.txParams?.maxPriorityFeePerGas
|
||||||
|
: draftTransaction.gas?.maxPriorityFeePerGas,
|
||||||
|
value: draftTransaction.amount?.value,
|
||||||
|
type: draftTransaction.transactionType,
|
||||||
|
},
|
||||||
|
userFeeLevel: editingTransaction?.userFeeLevel,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { transactionData };
|
||||||
|
};
|
@ -23,7 +23,6 @@ import {
|
|||||||
AlignItems,
|
AlignItems,
|
||||||
} from '../../../helpers/constants/design-system';
|
} from '../../../helpers/constants/design-system';
|
||||||
import { ConfirmPageContainerWarning } from '../../../components/app/confirm-page-container/confirm-page-container-content';
|
import { ConfirmPageContainerWarning } from '../../../components/app/confirm-page-container/confirm-page-container-content';
|
||||||
import GasDetailsItem from '../../../components/app/gas-details-item';
|
|
||||||
import LedgerInstructionField from '../../../components/app/ledger-instruction-field';
|
import LedgerInstructionField from '../../../components/app/ledger-instruction-field';
|
||||||
import { TokenStandard } from '../../../../shared/constants/transaction';
|
import { TokenStandard } from '../../../../shared/constants/transaction';
|
||||||
import { CHAIN_IDS, TEST_CHAINS } from '../../../../shared/constants/network';
|
import { CHAIN_IDS, TEST_CHAINS } from '../../../../shared/constants/network';
|
||||||
@ -34,6 +33,7 @@ import {
|
|||||||
} from '../../../components/component-library/icon/deprecated';
|
} from '../../../components/component-library/icon/deprecated';
|
||||||
import { ButtonIcon } from '../../../components/component-library/button-icon/deprecated';
|
import { ButtonIcon } from '../../../components/component-library/button-icon/deprecated';
|
||||||
import { Text } from '../../../components/component-library';
|
import { Text } from '../../../components/component-library';
|
||||||
|
import { ConfirmGasDisplay } from '../../../components/app/confirm-gas-display';
|
||||||
|
|
||||||
export default class ConfirmApproveContent extends Component {
|
export default class ConfirmApproveContent extends Component {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
@ -170,7 +170,7 @@ export default class ConfirmApproveContent extends Component {
|
|||||||
!renderSimulationFailureWarning
|
!renderSimulationFailureWarning
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<GasDetailsItem
|
<ConfirmGasDisplay
|
||||||
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
|
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -374,9 +374,7 @@ exports[`Confirm Transaction Base should match snapshot 1`] = `
|
|||||||
<h6
|
<h6
|
||||||
class="box box--margin-top-1 box--margin-bottom-1 box--margin-left-1 box--flex-direction-row box--text-align-right typography typography--h6 typography--weight-bold typography--style-normal typography--color-text-default"
|
class="box box--margin-top-1 box--margin-bottom-1 box--margin-left-1 box--flex-direction-row box--text-align-right typography typography--h6 typography--weight-bold typography--style-normal typography--color-text-default"
|
||||||
>
|
>
|
||||||
<div
|
<div>
|
||||||
class="confirm-page-container-content__currency-container"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="currency-display-component"
|
class="currency-display-component"
|
||||||
title="0.000021"
|
title="0.000021"
|
||||||
@ -397,18 +395,14 @@ exports[`Confirm Transaction Base should match snapshot 1`] = `
|
|||||||
<div
|
<div
|
||||||
class="transaction-detail-item__row"
|
class="transaction-detail-item__row"
|
||||||
>
|
>
|
||||||
<div>
|
<div />
|
||||||
|
|
||||||
</div>
|
|
||||||
<h6
|
<h6
|
||||||
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography transaction-detail-item__row-subText typography--h7 typography--weight-normal typography--style-normal typography--align-end typography--color-text-alternative"
|
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography transaction-detail-item__row-subText typography--h7 typography--weight-normal typography--style-normal typography--align-end typography--color-text-alternative"
|
||||||
>
|
>
|
||||||
<strong>
|
<strong>
|
||||||
Max fee:
|
Max fee:
|
||||||
</strong>
|
</strong>
|
||||||
<div
|
<div>
|
||||||
class="confirm-page-container-content__currency-container"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="currency-display-component"
|
class="currency-display-component"
|
||||||
title="0.000021"
|
title="0.000021"
|
||||||
|
@ -28,18 +28,9 @@ import {
|
|||||||
import { TransactionModalContextProvider } from '../../contexts/transaction-modal';
|
import { TransactionModalContextProvider } from '../../contexts/transaction-modal';
|
||||||
import TransactionDetail from '../../components/app/transaction-detail/transaction-detail.component';
|
import TransactionDetail from '../../components/app/transaction-detail/transaction-detail.component';
|
||||||
import TransactionDetailItem from '../../components/app/transaction-detail-item/transaction-detail-item.component';
|
import TransactionDetailItem from '../../components/app/transaction-detail-item/transaction-detail-item.component';
|
||||||
import InfoTooltip from '../../components/ui/info-tooltip/info-tooltip';
|
|
||||||
import LoadingHeartBeat from '../../components/ui/loading-heartbeat';
|
import LoadingHeartBeat from '../../components/ui/loading-heartbeat';
|
||||||
import GasDetailsItem from '../../components/app/gas-details-item';
|
|
||||||
import GasTiming from '../../components/app/gas-timing/gas-timing.component';
|
|
||||||
import LedgerInstructionField from '../../components/app/ledger-instruction-field';
|
import LedgerInstructionField from '../../components/app/ledger-instruction-field';
|
||||||
import MultiLayerFeeMessage from '../../components/app/multilayer-fee-message';
|
import MultiLayerFeeMessage from '../../components/app/multilayer-fee-message';
|
||||||
import Typography from '../../components/ui/typography/typography';
|
|
||||||
import {
|
|
||||||
TextColor,
|
|
||||||
FONT_STYLE,
|
|
||||||
TypographyVariant,
|
|
||||||
} from '../../helpers/constants/design-system';
|
|
||||||
import {
|
import {
|
||||||
disconnectGasFeeEstimatePoller,
|
disconnectGasFeeEstimatePoller,
|
||||||
getGasFeeEstimatesAndStartPolling,
|
getGasFeeEstimatesAndStartPolling,
|
||||||
@ -53,16 +44,13 @@ import { NETWORK_TO_NAME_MAP } from '../../../shared/constants/network';
|
|||||||
import {
|
import {
|
||||||
addHexes,
|
addHexes,
|
||||||
hexToDecimal,
|
hexToDecimal,
|
||||||
hexWEIToDecGWEI,
|
|
||||||
} from '../../../shared/modules/conversion.utils';
|
} from '../../../shared/modules/conversion.utils';
|
||||||
import TransactionAlerts from '../../components/app/transaction-alerts';
|
import TransactionAlerts from '../../components/app/transaction-alerts';
|
||||||
import { ConfirmHexData } from '../../components/app/confirm-hexdata';
|
import { ConfirmHexData } from '../../components/app/confirm-hexdata';
|
||||||
import { ConfirmData } from '../../components/app/confirm-data';
|
import { ConfirmData } from '../../components/app/confirm-data';
|
||||||
import { ConfirmTitle } from '../../components/app/confirm-title';
|
import { ConfirmTitle } from '../../components/app/confirm-title';
|
||||||
import { ConfirmSubTitle } from '../../components/app/confirm-subtitle';
|
import { ConfirmSubTitle } from '../../components/app/confirm-subtitle';
|
||||||
|
import { ConfirmGasDisplay } from '../../components/app/confirm-gas-display';
|
||||||
const renderHeartBeatIfNotInTest = () =>
|
|
||||||
process.env.IN_TEST ? null : <LoadingHeartBeat />;
|
|
||||||
|
|
||||||
export default class ConfirmTransactionBase extends Component {
|
export default class ConfirmTransactionBase extends Component {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
@ -136,7 +124,6 @@ export default class ConfirmTransactionBase extends Component {
|
|||||||
maxFeePerGas: PropTypes.string,
|
maxFeePerGas: PropTypes.string,
|
||||||
maxPriorityFeePerGas: PropTypes.string,
|
maxPriorityFeePerGas: PropTypes.string,
|
||||||
baseFeePerGas: PropTypes.string,
|
baseFeePerGas: PropTypes.string,
|
||||||
isMainnet: PropTypes.bool,
|
|
||||||
gasFeeIsCustom: PropTypes.bool,
|
gasFeeIsCustom: PropTypes.bool,
|
||||||
showLedgerSteps: PropTypes.bool.isRequired,
|
showLedgerSteps: PropTypes.bool.isRequired,
|
||||||
nativeCurrency: PropTypes.string,
|
nativeCurrency: PropTypes.string,
|
||||||
@ -319,11 +306,7 @@ export default class ConfirmTransactionBase extends Component {
|
|||||||
txData,
|
txData,
|
||||||
useNativeCurrencyAsPrimaryCurrency,
|
useNativeCurrencyAsPrimaryCurrency,
|
||||||
primaryTotalTextOverrideMaxAmount,
|
primaryTotalTextOverrideMaxAmount,
|
||||||
maxFeePerGas,
|
|
||||||
maxPriorityFeePerGas,
|
|
||||||
isMainnet,
|
|
||||||
showLedgerSteps,
|
showLedgerSteps,
|
||||||
supportsEIP1559,
|
|
||||||
isMultiLayerFeeNetwork,
|
isMultiLayerFeeNetwork,
|
||||||
nativeCurrency,
|
nativeCurrency,
|
||||||
isBuyableChain,
|
isBuyableChain,
|
||||||
@ -439,128 +422,6 @@ export default class ConfirmTransactionBase extends Component {
|
|||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
const renderGasDetailsItem = () => {
|
|
||||||
return this.supportsEIP1559 ? (
|
|
||||||
<GasDetailsItem
|
|
||||||
key="gas_details"
|
|
||||||
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<TransactionDetailItem
|
|
||||||
key="gas-item"
|
|
||||||
detailTitle={
|
|
||||||
txData.dappSuggestedGasFees ? (
|
|
||||||
<>
|
|
||||||
{t('transactionDetailGasHeading')}
|
|
||||||
<InfoTooltip
|
|
||||||
contentText={t('transactionDetailDappGasTooltip')}
|
|
||||||
position="top"
|
|
||||||
>
|
|
||||||
<i className="fa fa-info-circle" />
|
|
||||||
</InfoTooltip>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{t('transactionDetailGasHeading')}
|
|
||||||
<InfoTooltip
|
|
||||||
contentText={
|
|
||||||
<>
|
|
||||||
<p>
|
|
||||||
{t('transactionDetailGasTooltipIntro', [
|
|
||||||
isMainnet ? t('networkNameEthereum') : '',
|
|
||||||
])}
|
|
||||||
</p>
|
|
||||||
<p>{t('transactionDetailGasTooltipExplanation')}</p>
|
|
||||||
<p>
|
|
||||||
<a
|
|
||||||
href="https://community.metamask.io/t/what-is-gas-why-do-transactions-take-so-long/3172"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
{t('transactionDetailGasTooltipConversion')}
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
position="top"
|
|
||||||
>
|
|
||||||
<i className="fa fa-info-circle" />
|
|
||||||
</InfoTooltip>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
detailText={
|
|
||||||
useCurrencyRateCheck && (
|
|
||||||
<div className="confirm-page-container-content__currency-container test">
|
|
||||||
{renderHeartBeatIfNotInTest()}
|
|
||||||
<UserPreferencedCurrencyDisplay
|
|
||||||
type={SECONDARY}
|
|
||||||
value={hexMinimumTransactionFee}
|
|
||||||
hideLabel={Boolean(useNativeCurrencyAsPrimaryCurrency)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
detailTotal={
|
|
||||||
<div className="confirm-page-container-content__currency-container">
|
|
||||||
{renderHeartBeatIfNotInTest()}
|
|
||||||
<UserPreferencedCurrencyDisplay
|
|
||||||
type={PRIMARY}
|
|
||||||
value={hexMinimumTransactionFee}
|
|
||||||
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
|
|
||||||
numberOfDecimals={6}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
subText={
|
|
||||||
<>
|
|
||||||
<strong key="editGasSubTextFeeLabel">
|
|
||||||
{t('editGasSubTextFeeLabel')}
|
|
||||||
</strong>
|
|
||||||
<div
|
|
||||||
key="editGasSubTextFeeValue"
|
|
||||||
className="confirm-page-container-content__currency-container"
|
|
||||||
>
|
|
||||||
{renderHeartBeatIfNotInTest()}
|
|
||||||
<UserPreferencedCurrencyDisplay
|
|
||||||
key="editGasSubTextFeeAmount"
|
|
||||||
type={PRIMARY}
|
|
||||||
value={hexMaximumTransactionFee}
|
|
||||||
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
subTitle={
|
|
||||||
<>
|
|
||||||
{txData.dappSuggestedGasFees ? (
|
|
||||||
<Typography
|
|
||||||
variant={TypographyVariant.H7}
|
|
||||||
fontStyle={FONT_STYLE.ITALIC}
|
|
||||||
color={TextColor.textAlternative}
|
|
||||||
>
|
|
||||||
{t('transactionDetailDappGasMoreInfo')}
|
|
||||||
</Typography>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
{supportsEIP1559 && (
|
|
||||||
<GasTiming
|
|
||||||
maxPriorityFeePerGas={hexWEIToDecGWEI(
|
|
||||||
maxPriorityFeePerGas ||
|
|
||||||
txData.txParams.maxPriorityFeePerGas,
|
|
||||||
).toString()}
|
|
||||||
maxFeePerGas={hexWEIToDecGWEI(
|
|
||||||
maxFeePerGas || txData.txParams.maxFeePerGas,
|
|
||||||
).toString()}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const simulationFailureWarning = () => (
|
const simulationFailureWarning = () => (
|
||||||
<div className="confirm-page-container-content__error-container">
|
<div className="confirm-page-container-content__error-container">
|
||||||
<SimulationErrorMessage
|
<SimulationErrorMessage
|
||||||
@ -594,9 +455,11 @@ export default class ConfirmTransactionBase extends Component {
|
|||||||
}
|
}
|
||||||
rows={[
|
rows={[
|
||||||
renderSimulationFailureWarning && simulationFailureWarning(),
|
renderSimulationFailureWarning && simulationFailureWarning(),
|
||||||
!renderSimulationFailureWarning &&
|
!renderSimulationFailureWarning && !isMultiLayerFeeNetwork && (
|
||||||
!isMultiLayerFeeNetwork &&
|
<ConfirmGasDisplay
|
||||||
renderGasDetailsItem(),
|
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
|
||||||
|
/>
|
||||||
|
),
|
||||||
!renderSimulationFailureWarning && isMultiLayerFeeNetwork && (
|
!renderSimulationFailureWarning && isMultiLayerFeeNetwork && (
|
||||||
<MultiLayerFeeMessage
|
<MultiLayerFeeMessage
|
||||||
transaction={txData}
|
transaction={txData}
|
||||||
|
@ -23,7 +23,11 @@ setBackgroundConnection({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const baseStore = {
|
const baseStore = {
|
||||||
send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
|
send: {
|
||||||
|
...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
|
||||||
|
currentTransactionUUID: null,
|
||||||
|
draftTransactions: {},
|
||||||
|
},
|
||||||
DNS: domainInitialState,
|
DNS: domainInitialState,
|
||||||
gas: {
|
gas: {
|
||||||
customData: { limit: null, price: null },
|
customData: { limit: null, price: null },
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import { TypographyVariant } from '../../../../../helpers/constants/design-system';
|
|
||||||
import { mapToTemplate } from '../../../../../components/app/flask/snap-ui-renderer';
|
import { mapToTemplate } from '../../../../../components/app/flask/snap-ui-renderer';
|
||||||
import { DelineatorType } from '../../../../../helpers/constants/flask';
|
import { DelineatorType } from '../../../../../helpers/constants/flask';
|
||||||
|
|
||||||
function getValues(pendingApproval, t, actions) {
|
function getValues(pendingApproval, t, actions) {
|
||||||
const {
|
const {
|
||||||
snapName,
|
snapName,
|
||||||
requestData: { content, title, description, textAreaContent },
|
requestData: { content },
|
||||||
} = pendingApproval;
|
} = pendingApproval;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -25,49 +24,7 @@ function getValues(pendingApproval, t, actions) {
|
|||||||
snapName,
|
snapName,
|
||||||
},
|
},
|
||||||
// TODO: Replace with SnapUIRenderer when we don't need to inject the input manually.
|
// TODO: Replace with SnapUIRenderer when we don't need to inject the input manually.
|
||||||
// TODO: Remove ternary once snap_confirm has been removed.
|
children: mapToTemplate(content),
|
||||||
children: content
|
|
||||||
? mapToTemplate(content)
|
|
||||||
: [
|
|
||||||
{
|
|
||||||
element: 'Typography',
|
|
||||||
key: 'title',
|
|
||||||
children: title,
|
|
||||||
props: {
|
|
||||||
variant: TypographyVariant.H3,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
boxProps: {
|
|
||||||
marginBottom: 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
...(description
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
element: 'Typography',
|
|
||||||
key: 'subtitle',
|
|
||||||
children: description,
|
|
||||||
props: {
|
|
||||||
variant: TypographyVariant.H6,
|
|
||||||
boxProps: {
|
|
||||||
marginBottom: 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
...(textAreaContent
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
element: 'Copyable',
|
|
||||||
key: 'snap-dialog-content-text',
|
|
||||||
props: {
|
|
||||||
text: textAreaContent,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -0,0 +1,188 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`CustodyAccountList renders accounts 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="box box--padding-top-4 box--padding-right-7 box--padding-bottom-7 box--padding-left-7 box--flex-direction-row"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box custody-account-list box--display-flex box--flex-direction-column box--width-full"
|
||||||
|
data-testid="custody-account-list"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box custody-account-list__item box--display-flex box--flex-direction-row"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box box--display-flex box--flex-direction-row box--align-items-flex-start"
|
||||||
|
data-testid="custody-account-list-item-radio-button"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="address-0"
|
||||||
|
name="selectedAccount"
|
||||||
|
type="checkbox"
|
||||||
|
value="0x1234567890123456789012345678901234567890"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box box--margin-left-2 box--display-flex box--flex-direction-column box--width-full"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="box mm-text mm-label mm-label--html-for custody-account-list__item__title mm-text--body-md mm-text--font-weight-bold box--margin-top-2 box--margin-left-2 box--display-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-text-default"
|
||||||
|
for="address-0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="box mm-text custody-account-list__item__name mm-text--inherit box--padding-right-1 box--flex-direction-row box--color-text-default"
|
||||||
|
>
|
||||||
|
Test Account 1
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label
|
||||||
|
class="box mm-text mm-label mm-label--html-for mm-text--body-md mm-text--font-weight-bold box--margin-top-2 box--margin-right-3 box--display-flex box--flex-direction-row box--align-items-center box--color-text-default"
|
||||||
|
for="address-0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="box mm-text custody-account-list__item mm-text--body-md box--display-flex box--flex-direction-row box--color-text-default"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="box mm-text mm-button-base mm-button-link mm-button-link--size-auto mm-text--body-md box--display-inline-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-primary-default box--background-color-transparent"
|
||||||
|
href="https://etherscan.io/address/0x1234567890123456789012345678901234567890"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="box mm-text mm-text--inherit box--flex-direction-row box--color-primary-default"
|
||||||
|
>
|
||||||
|
0x123...7890
|
||||||
|
<span
|
||||||
|
class="box mm-icon mm-icon--size-md box--margin-left-1 box--display-inline-block box--flex-direction-row box--color-primary-default"
|
||||||
|
style="mask-image: url('./images/icons/undefined.svg');"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
aria-describedby="tippy-tooltip-1"
|
||||||
|
class=""
|
||||||
|
data-original-title="[copyToClipboard]"
|
||||||
|
data-tooltipped=""
|
||||||
|
style="display: inline; background-color: transparent;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="custody-account-list__item__clipboard"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="box mm-icon mm-icon--size-md box--display-inline-block box--flex-direction-row box--color-icon-muted"
|
||||||
|
style="mask-image: url('./images/icons/undefined.svg');"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
class="box box--display-flex box--flex-direction-row box--justify-content-space-between"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box custody-account-list__item box--display-flex box--flex-direction-row"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box box--display-flex box--flex-direction-row box--align-items-flex-start"
|
||||||
|
data-testid="custody-account-list-item-radio-button"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="address-1"
|
||||||
|
name="selectedAccount"
|
||||||
|
type="checkbox"
|
||||||
|
value="0x0987654321098765432109876543210987654321"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box box--margin-left-2 box--display-flex box--flex-direction-column box--width-full"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="box mm-text mm-label mm-label--html-for custody-account-list__item__title mm-text--body-md mm-text--font-weight-bold box--margin-top-2 box--margin-left-2 box--display-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-text-default"
|
||||||
|
for="address-1"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="box mm-text custody-account-list__item__name mm-text--inherit box--padding-right-1 box--flex-direction-row box--color-text-default"
|
||||||
|
>
|
||||||
|
Test Account 2
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label
|
||||||
|
class="box mm-text mm-label mm-label--html-for mm-text--body-md mm-text--font-weight-bold box--margin-top-2 box--margin-right-3 box--display-flex box--flex-direction-row box--align-items-center box--color-text-default"
|
||||||
|
for="address-1"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="box mm-text custody-account-list__item mm-text--body-md box--display-flex box--flex-direction-row box--color-text-default"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="box mm-text mm-button-base mm-button-link mm-button-link--size-auto mm-text--body-md box--display-inline-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-primary-default box--background-color-transparent"
|
||||||
|
href="https://etherscan.io/address/0x0987654321098765432109876543210987654321"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="box mm-text mm-text--inherit box--flex-direction-row box--color-primary-default"
|
||||||
|
>
|
||||||
|
0x098...4321
|
||||||
|
<span
|
||||||
|
class="box mm-icon mm-icon--size-md box--margin-left-1 box--display-inline-block box--flex-direction-row box--color-primary-default"
|
||||||
|
style="mask-image: url('./images/icons/undefined.svg');"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
aria-describedby="tippy-tooltip-2"
|
||||||
|
class=""
|
||||||
|
data-original-title="[copyToClipboard]"
|
||||||
|
data-tooltipped=""
|
||||||
|
style="display: inline; background-color: transparent;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="custody-account-list__item__clipboard"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="box mm-icon mm-icon--size-md box--display-inline-block box--flex-direction-row box--color-icon-muted"
|
||||||
|
style="mask-image: url('./images/icons/undefined.svg');"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
class="box box--display-flex box--flex-direction-row box--justify-content-space-between"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box custody-account-list__buttons box--padding-top-5 box--padding-right-7 box--padding-bottom-7 box--padding-left-7 box--display-flex box--flex-direction-row box--justify-content-space-between box--width-full"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="button btn--rounded btn-default btn--large custody-account-list__button"
|
||||||
|
data-testid="custody-account-cancel-button"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
[cancel]
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button btn--rounded btn-primary btn--large custody-account-list__button"
|
||||||
|
data-testid="custody-account-connect-button"
|
||||||
|
disabled=""
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
[connect]
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
@ -0,0 +1,218 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Button from '../../../../components/ui/button';
|
||||||
|
import CustodyLabels from '../../../../components/institutional/custody-labels';
|
||||||
|
import { SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP } from '../../../../../shared/constants/swaps';
|
||||||
|
import { CHAIN_IDS } from '../../../../../shared/constants/network';
|
||||||
|
import { shortenAddress } from '../../../../helpers/utils/util';
|
||||||
|
import Tooltip from '../../../../components/ui/tooltip';
|
||||||
|
import {
|
||||||
|
TextVariant,
|
||||||
|
JustifyContent,
|
||||||
|
BLOCK_SIZES,
|
||||||
|
DISPLAY,
|
||||||
|
IconColor,
|
||||||
|
} from '../../../../helpers/constants/design-system';
|
||||||
|
import { useI18nContext } from '../../../../hooks/useI18nContext';
|
||||||
|
import Box from '../../../../components/ui/box';
|
||||||
|
import {
|
||||||
|
Text,
|
||||||
|
Label,
|
||||||
|
Icon,
|
||||||
|
IconName,
|
||||||
|
IconSize,
|
||||||
|
ButtonLink,
|
||||||
|
} from '../../../../components/component-library';
|
||||||
|
import { useCopyToClipboard } from '../../../../hooks/useCopyToClipboard';
|
||||||
|
|
||||||
|
const getButtonLinkHref = (account) => {
|
||||||
|
const url = SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[CHAIN_IDS.MAINNET];
|
||||||
|
return `${url}address/${account.address}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function CustodyAccountList({
|
||||||
|
rawList,
|
||||||
|
accounts,
|
||||||
|
onAccountChange,
|
||||||
|
selectedAccounts,
|
||||||
|
onCancel,
|
||||||
|
onAddAccounts,
|
||||||
|
custody,
|
||||||
|
}) {
|
||||||
|
const t = useI18nContext();
|
||||||
|
const [copied, handleCopy] = useCopyToClipboard();
|
||||||
|
const tooltipText = copied ? t('copiedExclamation') : t('copyToClipboard');
|
||||||
|
const disabled = Object.keys(selectedAccounts).length === 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Box paddingTop={4} paddingRight={7} paddingBottom={7} paddingLeft={7}>
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
flexDirection={['column']}
|
||||||
|
width={BLOCK_SIZES.FULL}
|
||||||
|
className="custody-account-list"
|
||||||
|
data-testid="custody-account-list"
|
||||||
|
>
|
||||||
|
{accounts.map((account, idx) => (
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
className="custody-account-list__item"
|
||||||
|
key={account.address}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
alignItems={['flex-start']}
|
||||||
|
data-testid="custody-account-list-item-radio-button"
|
||||||
|
>
|
||||||
|
{!rawList && (
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="selectedAccount"
|
||||||
|
id={`address-${idx}`}
|
||||||
|
value={account.address}
|
||||||
|
onChange={(e) =>
|
||||||
|
onAccountChange({
|
||||||
|
name: account.name,
|
||||||
|
address: e.target.value,
|
||||||
|
custodianDetails: account.custodianDetails,
|
||||||
|
labels: account.labels,
|
||||||
|
chainId: account.chainId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
checked={
|
||||||
|
selectedAccounts && selectedAccounts[account.address]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
flexDirection={['column']}
|
||||||
|
marginLeft={2}
|
||||||
|
width={BLOCK_SIZES.FULL}
|
||||||
|
>
|
||||||
|
<Label
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
justifyContent={JustifyContent.center}
|
||||||
|
marginTop={2}
|
||||||
|
marginLeft={2}
|
||||||
|
htmlFor={`address-${idx}`}
|
||||||
|
className="custody-account-list__item__title"
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
as="span"
|
||||||
|
variant={TextVariant.inherit}
|
||||||
|
size={TextVariant.bodySm}
|
||||||
|
paddingRight={1}
|
||||||
|
className="custody-account-list__item__name"
|
||||||
|
>
|
||||||
|
{account.name}
|
||||||
|
</Text>
|
||||||
|
</Label>
|
||||||
|
<Label
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
size={TextVariant.bodySm}
|
||||||
|
marginTop={2}
|
||||||
|
marginRight={3}
|
||||||
|
htmlFor={`address-${idx}`}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
as="span"
|
||||||
|
variant={TextVariant.bodyMd}
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
className="custody-account-list__item"
|
||||||
|
>
|
||||||
|
<ButtonLink
|
||||||
|
href={getButtonLinkHref(account)}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
{shortenAddress(account.address)}
|
||||||
|
<Icon
|
||||||
|
name={IconSize.EXPORT}
|
||||||
|
size={IconName.SM}
|
||||||
|
color={IconColor.primaryDefault}
|
||||||
|
marginLeft={1}
|
||||||
|
/>
|
||||||
|
</ButtonLink>
|
||||||
|
<Tooltip
|
||||||
|
position="bottom"
|
||||||
|
title={tooltipText}
|
||||||
|
style={{ backgroundColor: 'transparent' }}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="custody-account-list__item__clipboard"
|
||||||
|
onClick={() => handleCopy(account.address)}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name={IconSize.COPY}
|
||||||
|
size={IconName.XS}
|
||||||
|
color={IconColor.iconMuted}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
</Text>
|
||||||
|
</Label>
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
justifyContent={JustifyContent.spaceBetween}
|
||||||
|
>
|
||||||
|
{account.labels && (
|
||||||
|
<CustodyLabels
|
||||||
|
labels={account.labels}
|
||||||
|
index={idx.toString()}
|
||||||
|
hideNetwork
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
{!rawList && (
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
width={BLOCK_SIZES.FULL}
|
||||||
|
justifyContent={JustifyContent.spaceBetween}
|
||||||
|
paddingTop={5}
|
||||||
|
paddingRight={7}
|
||||||
|
paddingBottom={7}
|
||||||
|
paddingLeft={7}
|
||||||
|
className="custody-account-list__buttons"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
data-testid="custody-account-cancel-button"
|
||||||
|
type="default"
|
||||||
|
large
|
||||||
|
className="custody-account-list__button"
|
||||||
|
onClick={onCancel}
|
||||||
|
>
|
||||||
|
{t('cancel')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
data-testid="custody-account-connect-button"
|
||||||
|
type="primary"
|
||||||
|
large
|
||||||
|
className="custody-account-list__button"
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={() => onAddAccounts(custody)}
|
||||||
|
>
|
||||||
|
{t('connect')}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
CustodyAccountList.propTypes = {
|
||||||
|
custody: PropTypes.string,
|
||||||
|
accounts: PropTypes.array.isRequired,
|
||||||
|
onAccountChange: PropTypes.func,
|
||||||
|
selectedAccounts: PropTypes.object,
|
||||||
|
onAddAccounts: PropTypes.func,
|
||||||
|
onCancel: PropTypes.func,
|
||||||
|
rawList: PropTypes.bool,
|
||||||
|
};
|
@ -0,0 +1,34 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import CustodyAccountList from '.';
|
||||||
|
|
||||||
|
const testAccounts = [
|
||||||
|
{
|
||||||
|
address: '0x1234567890123456789012345678901234567890',
|
||||||
|
name: 'Test Account 1',
|
||||||
|
chainId: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
address: '0x0987654321098765432109876543210987654321',
|
||||||
|
name: 'Test Account 2',
|
||||||
|
chainId: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Pages/Institutional/CustodyAccountList',
|
||||||
|
component: CustodyAccountList,
|
||||||
|
args: {
|
||||||
|
custody: 'Test',
|
||||||
|
accounts: testAccounts,
|
||||||
|
onAccountChange: () => undefined,
|
||||||
|
selectedAccounts: {},
|
||||||
|
onAddAccounts: () => undefined,
|
||||||
|
onCancel: () => undefined,
|
||||||
|
provider: 'Test',
|
||||||
|
rawList: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultStory = (args) => <CustodyAccountList {...args} />;
|
||||||
|
|
||||||
|
DefaultStory.storyName = 'CustodyAccountList';
|
@ -0,0 +1,109 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { fireEvent, render, screen } from '@testing-library/react';
|
||||||
|
import CustodyAccountList from './account-list';
|
||||||
|
|
||||||
|
const testAccounts = [
|
||||||
|
{
|
||||||
|
address: '0x1234567890123456789012345678901234567890',
|
||||||
|
name: 'Test Account 1',
|
||||||
|
chainId: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
address: '0x0987654321098765432109876543210987654321',
|
||||||
|
name: 'Test Account 2',
|
||||||
|
chainId: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
describe('CustodyAccountList', () => {
|
||||||
|
const onAccountChangeMock = jest.fn();
|
||||||
|
const onCancelMock = jest.fn();
|
||||||
|
const onAddAccountsMock = jest.fn();
|
||||||
|
const selectedAccountsMock = {};
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders accounts', () => {
|
||||||
|
const { container } = render(
|
||||||
|
<CustodyAccountList
|
||||||
|
accounts={testAccounts}
|
||||||
|
selectedAccounts={selectedAccountsMock}
|
||||||
|
onAccountChange={onAccountChangeMock}
|
||||||
|
onCancel={onCancelMock}
|
||||||
|
onAddAccounts={onAddAccountsMock}
|
||||||
|
custody="Test"
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls onAccountChange when an account is selected', () => {
|
||||||
|
render(
|
||||||
|
<CustodyAccountList
|
||||||
|
accounts={testAccounts}
|
||||||
|
selectedAccounts={selectedAccountsMock}
|
||||||
|
onAccountChange={onAccountChangeMock}
|
||||||
|
onCancel={onCancelMock}
|
||||||
|
onAddAccounts={onAddAccountsMock}
|
||||||
|
custody="Test"
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const firstAccountCheckbox = screen.getAllByRole('checkbox')[0];
|
||||||
|
fireEvent.click(firstAccountCheckbox);
|
||||||
|
|
||||||
|
expect(onAccountChangeMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onAccountChangeMock).toHaveBeenCalledWith({
|
||||||
|
name: 'Test Account 1',
|
||||||
|
address: '0x1234567890123456789012345678901234567890',
|
||||||
|
custodianDetails: undefined,
|
||||||
|
labels: undefined,
|
||||||
|
chainId: 1,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls onCancel when the Cancel button is clicked', () => {
|
||||||
|
render(
|
||||||
|
<CustodyAccountList
|
||||||
|
accounts={testAccounts}
|
||||||
|
selectedAccounts={selectedAccountsMock}
|
||||||
|
onAccountChange={onAccountChangeMock}
|
||||||
|
onCancel={onCancelMock}
|
||||||
|
onAddAccounts={onAddAccountsMock}
|
||||||
|
custody="Test"
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const cancelButton = screen.getByTestId('custody-account-cancel-button');
|
||||||
|
fireEvent.click(cancelButton);
|
||||||
|
|
||||||
|
expect(onCancelMock).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls onAddAccounts when the Connect button is clicked', () => {
|
||||||
|
selectedAccountsMock['0x1234567890123456789012345678901234567890'] = true;
|
||||||
|
selectedAccountsMock['0x0987654321098765432109876543210987654321'] = true;
|
||||||
|
|
||||||
|
render(
|
||||||
|
<CustodyAccountList
|
||||||
|
accounts={testAccounts}
|
||||||
|
selectedAccounts={selectedAccountsMock}
|
||||||
|
onAccountChange={onAccountChangeMock}
|
||||||
|
onCancel={onCancelMock}
|
||||||
|
onAddAccounts={onAddAccountsMock}
|
||||||
|
custody="Test"
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const addAccountsButton = screen.getByTestId(
|
||||||
|
'custody-account-connect-button',
|
||||||
|
);
|
||||||
|
fireEvent.click(addAccountsButton);
|
||||||
|
|
||||||
|
expect(onAddAccountsMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onAddAccountsMock).toHaveBeenCalledWith('Test');
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1 @@
|
|||||||
|
export { default } from './account-list';
|
@ -0,0 +1,49 @@
|
|||||||
|
.custody-account-list {
|
||||||
|
flex: 1;
|
||||||
|
max-height: 50vh;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
border-bottom: 1px solid #d2d8dd;
|
||||||
|
|
||||||
|
input {
|
||||||
|
margin: 12px 0 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
flex-flow: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__name {
|
||||||
|
display: block;
|
||||||
|
white-space: nowrap;
|
||||||
|
max-width: 200px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__clipboard {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__item:first-child {
|
||||||
|
border-top: 1px solid #d2d8dd;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__item:last-child {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__item:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__buttons {
|
||||||
|
border-top: 1px solid #d2d8dd;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__button:not(:last-child) {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
}
|
@ -304,6 +304,7 @@ export default class Home extends PureComponent {
|
|||||||
iconName={ICON_NAMES.CLOSE}
|
iconName={ICON_NAMES.CLOSE}
|
||||||
size={ICON_SIZES.SM}
|
size={ICON_SIZES.SM}
|
||||||
ariaLabel={t('close')}
|
ariaLabel={t('close')}
|
||||||
|
onClick={onAutoHide}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,9 @@
|
|||||||
@import 'connected-accounts/index';
|
@import 'connected-accounts/index';
|
||||||
@import 'connected-sites/index';
|
@import 'connected-sites/index';
|
||||||
@import 'create-account/index';
|
@import 'create-account/index';
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(mmi)
|
||||||
|
@import "create-account/institutional/connect-custody/index";
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
@import 'error/index';
|
@import 'error/index';
|
||||||
@import 'send/gas-display/index';
|
@import 'send/gas-display/index';
|
||||||
@import 'home/index';
|
@import 'home/index';
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { I18nContext } from '../../../contexts/i18n';
|
import { I18nContext } from '../../../contexts/i18n';
|
||||||
import { useGasFeeContext } from '../../../contexts/gasFee';
|
import { useGasFeeContext } from '../../../contexts/gasFee';
|
||||||
import { PRIMARY, SECONDARY } from '../../../helpers/constants/common';
|
import { PRIMARY, SECONDARY } from '../../../helpers/constants/common';
|
||||||
import UserPreferencedCurrencyDisplay from '../../../components/app/user-preferenced-currency-display';
|
import UserPreferencedCurrencyDisplay from '../../../components/app/user-preferenced-currency-display';
|
||||||
import GasTiming from '../../../components/app/gas-timing';
|
|
||||||
import InfoTooltip from '../../../components/ui/info-tooltip';
|
|
||||||
import Typography from '../../../components/ui/typography';
|
import Typography from '../../../components/ui/typography';
|
||||||
import Button from '../../../components/ui/button';
|
import Button from '../../../components/ui/button';
|
||||||
import Box from '../../../components/ui/box';
|
import Box from '../../../components/ui/box';
|
||||||
@ -16,13 +13,11 @@ import {
|
|||||||
DISPLAY,
|
DISPLAY,
|
||||||
FLEX_DIRECTION,
|
FLEX_DIRECTION,
|
||||||
BLOCK_SIZES,
|
BLOCK_SIZES,
|
||||||
Color,
|
|
||||||
FONT_STYLE,
|
|
||||||
FONT_WEIGHT,
|
|
||||||
} from '../../../helpers/constants/design-system';
|
} from '../../../helpers/constants/design-system';
|
||||||
import { TokenStandard } from '../../../../shared/constants/transaction';
|
import { TokenStandard } from '../../../../shared/constants/transaction';
|
||||||
import LoadingHeartBeat from '../../../components/ui/loading-heartbeat';
|
import LoadingHeartBeat from '../../../components/ui/loading-heartbeat';
|
||||||
import TransactionDetailItem from '../../../components/app/transaction-detail-item';
|
import TransactionDetailItem from '../../../components/app/transaction-detail-item';
|
||||||
|
import { ConfirmGasDisplay } from '../../../components/app/confirm-gas-display';
|
||||||
import { NETWORK_TO_NAME_MAP } from '../../../../shared/constants/network';
|
import { NETWORK_TO_NAME_MAP } from '../../../../shared/constants/network';
|
||||||
import TransactionDetail from '../../../components/app/transaction-detail';
|
import TransactionDetail from '../../../components/app/transaction-detail';
|
||||||
import ActionableMessage from '../../../components/ui/actionable-message';
|
import ActionableMessage from '../../../components/ui/actionable-message';
|
||||||
@ -31,7 +26,6 @@ import {
|
|||||||
getPreferences,
|
getPreferences,
|
||||||
getIsBuyableChain,
|
getIsBuyableChain,
|
||||||
transactionFeeSelector,
|
transactionFeeSelector,
|
||||||
getIsMainnet,
|
|
||||||
getIsTestnet,
|
getIsTestnet,
|
||||||
getUseCurrencyRateCheck,
|
getUseCurrencyRateCheck,
|
||||||
} from '../../../selectors';
|
} from '../../../selectors';
|
||||||
@ -43,7 +37,6 @@ import { showModal } from '../../../store/actions';
|
|||||||
import {
|
import {
|
||||||
addHexes,
|
addHexes,
|
||||||
hexWEIToDecETH,
|
hexWEIToDecETH,
|
||||||
hexWEIToDecGWEI,
|
|
||||||
} from '../../../../shared/modules/conversion.utils';
|
} from '../../../../shared/modules/conversion.utils';
|
||||||
import {
|
import {
|
||||||
MetaMetricsEventCategory,
|
MetaMetricsEventCategory,
|
||||||
@ -61,7 +54,6 @@ export default function GasDisplay({ gasError }) {
|
|||||||
const { openBuyCryptoInPdapp } = useRamps();
|
const { openBuyCryptoInPdapp } = useRamps();
|
||||||
|
|
||||||
const currentProvider = useSelector(getProvider);
|
const currentProvider = useSelector(getProvider);
|
||||||
const isMainnet = useSelector(getIsMainnet);
|
|
||||||
const isTestnet = useSelector(getIsTestnet);
|
const isTestnet = useSelector(getIsTestnet);
|
||||||
const isBuyableChain = useSelector(getIsBuyableChain);
|
const isBuyableChain = useSelector(getIsBuyableChain);
|
||||||
const draftTransaction = useSelector(getCurrentDraftTransaction);
|
const draftTransaction = useSelector(getCurrentDraftTransaction);
|
||||||
@ -95,11 +87,9 @@ export default function GasDisplay({ gasError }) {
|
|||||||
userFeeLevel: editingTransaction?.userFeeLevel,
|
userFeeLevel: editingTransaction?.userFeeLevel,
|
||||||
};
|
};
|
||||||
|
|
||||||
const {
|
const { hexMaximumTransactionFee, hexTransactionTotal } = useSelector(
|
||||||
hexMinimumTransactionFee,
|
(state) => transactionFeeSelector(state, transactionData),
|
||||||
hexMaximumTransactionFee,
|
);
|
||||||
hexTransactionTotal,
|
|
||||||
} = useSelector((state) => transactionFeeSelector(state, transactionData));
|
|
||||||
|
|
||||||
let title;
|
let title;
|
||||||
if (
|
if (
|
||||||
@ -158,119 +148,13 @@ export default function GasDisplay({ gasError }) {
|
|||||||
detailTotal = primaryTotalTextOverrideMaxAmount;
|
detailTotal = primaryTotalTextOverrideMaxAmount;
|
||||||
maxAmount = primaryTotalTextOverrideMaxAmount;
|
maxAmount = primaryTotalTextOverrideMaxAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box className="gas-display">
|
<Box className="gas-display">
|
||||||
<TransactionDetail
|
<TransactionDetail
|
||||||
userAcknowledgedGasMissing={false}
|
userAcknowledgedGasMissing={false}
|
||||||
rows={[
|
rows={[
|
||||||
<TransactionDetailItem
|
<ConfirmGasDisplay key="gas-display" />,
|
||||||
key="gas-item"
|
|
||||||
detailTitle={
|
|
||||||
<Box display={DISPLAY.FLEX}>
|
|
||||||
<Box marginRight={1}>{t('gas')}</Box>
|
|
||||||
<Typography
|
|
||||||
as="span"
|
|
||||||
marginTop={0}
|
|
||||||
color={Color.textMuted}
|
|
||||||
fontStyle={FONT_STYLE.ITALIC}
|
|
||||||
fontWeight={FONT_WEIGHT.NORMAL}
|
|
||||||
className="gas-display__title__estimate"
|
|
||||||
>
|
|
||||||
({t('transactionDetailGasInfoV2')})
|
|
||||||
</Typography>
|
|
||||||
<InfoTooltip
|
|
||||||
contentText={
|
|
||||||
<>
|
|
||||||
<Typography variant={TypographyVariant.H7}>
|
|
||||||
{t('transactionDetailGasTooltipIntro', [
|
|
||||||
isMainnet ? t('networkNameEthereum') : '',
|
|
||||||
])}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant={TypographyVariant.H7}>
|
|
||||||
{t('transactionDetailGasTooltipExplanation')}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant={TypographyVariant.H7}>
|
|
||||||
<a
|
|
||||||
href="https://community.metamask.io/t/what-is-gas-why-do-transactions-take-so-long/3172"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
{t('transactionDetailGasTooltipConversion')}
|
|
||||||
</a>
|
|
||||||
</Typography>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
position="right"
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
}
|
|
||||||
detailTitleColor={Color.textDefault}
|
|
||||||
detailText={
|
|
||||||
showCurrencyRateCheck && (
|
|
||||||
<Box className="gas-display__currency-container">
|
|
||||||
<LoadingHeartBeat estimateUsed={estimateUsed} />
|
|
||||||
<UserPreferencedCurrencyDisplay
|
|
||||||
type={SECONDARY}
|
|
||||||
value={hexMinimumTransactionFee}
|
|
||||||
hideLabel={Boolean(useNativeCurrencyAsPrimaryCurrency)}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
detailTotal={
|
|
||||||
<Box className="gas-display__currency-container">
|
|
||||||
<LoadingHeartBeat estimateUsed={estimateUsed} />
|
|
||||||
<UserPreferencedCurrencyDisplay
|
|
||||||
type={PRIMARY}
|
|
||||||
value={hexMinimumTransactionFee}
|
|
||||||
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
}
|
|
||||||
subText={
|
|
||||||
<>
|
|
||||||
<Box
|
|
||||||
key="editGasSubTextFeeLabel"
|
|
||||||
display={DISPLAY.INLINE_FLEX}
|
|
||||||
className={classNames('gas-display__gas-fee-label', {
|
|
||||||
'gas-display__gas-fee-warning': estimateUsed === 'high',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<LoadingHeartBeat estimateUsed={estimateUsed} />
|
|
||||||
<Box marginRight={1}>
|
|
||||||
<strong>
|
|
||||||
{estimateUsed === 'high' && '⚠ '}
|
|
||||||
{t('editGasSubTextFeeLabel')}
|
|
||||||
</strong>
|
|
||||||
</Box>
|
|
||||||
<Box
|
|
||||||
key="editGasSubTextFeeValue"
|
|
||||||
className="gas-display__currency-container"
|
|
||||||
>
|
|
||||||
<LoadingHeartBeat estimateUsed={estimateUsed} />
|
|
||||||
<UserPreferencedCurrencyDisplay
|
|
||||||
key="editGasSubTextFeeAmount"
|
|
||||||
type={PRIMARY}
|
|
||||||
value={hexMaximumTransactionFee}
|
|
||||||
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
subTitle={
|
|
||||||
<GasTiming
|
|
||||||
maxPriorityFeePerGas={hexWEIToDecGWEI(
|
|
||||||
draftTransaction.gas.maxPriorityFeePerGas,
|
|
||||||
)}
|
|
||||||
maxFeePerGas={hexWEIToDecGWEI(
|
|
||||||
draftTransaction.gas.maxFeePerGas,
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>,
|
|
||||||
(gasError || isInsufficientTokenError) && (
|
(gasError || isInsufficientTokenError) && (
|
||||||
<TransactionDetailItem
|
<TransactionDetailItem
|
||||||
key="total-item"
|
key="total-item"
|
||||||
|
@ -35,7 +35,6 @@
|
|||||||
height: 120px;
|
height: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__currency-container,
|
|
||||||
&__total-amount,
|
&__total-amount,
|
||||||
&__total-value {
|
&__total-value {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -197,7 +197,7 @@ exports[`SendContent Component render should match snapshot 1`] = `
|
|||||||
Gas
|
Gas
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
class="box box--margin-bottom-1 box--flex-direction-row typography gas-display__title__estimate typography--p typography--weight-normal typography--style-italic typography--color-text-muted"
|
class="gas-details-item-title__estimate"
|
||||||
>
|
>
|
||||||
(
|
(
|
||||||
estimated
|
estimated
|
||||||
@ -236,7 +236,7 @@ exports[`SendContent Component render should match snapshot 1`] = `
|
|||||||
class="box box--margin-top-1 box--margin-bottom-1 box--margin-left-1 box--flex-direction-row box--text-align-right typography typography--h6 typography--weight-bold typography--style-normal typography--color-text-default"
|
class="box box--margin-top-1 box--margin-bottom-1 box--margin-left-1 box--flex-direction-row box--text-align-right typography typography--h6 typography--weight-bold typography--style-normal typography--color-text-default"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="box gas-display__currency-container box--flex-direction-row"
|
class="gas-details-item__currency-container"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="currency-display-component"
|
class="currency-display-component"
|
||||||
@ -274,7 +274,7 @@ exports[`SendContent Component render should match snapshot 1`] = `
|
|||||||
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography transaction-detail-item__row-subText typography--h7 typography--weight-normal typography--style-normal typography--align-end typography--color-text-alternative"
|
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography transaction-detail-item__row-subText typography--h7 typography--weight-normal typography--style-normal typography--align-end typography--color-text-alternative"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="box gas-display__gas-fee-label box--display-inline-flex box--flex-direction-row"
|
class="box gas-details-item__gasfee-label box--display-inline-flex box--flex-direction-row"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="box box--margin-right-1 box--flex-direction-row"
|
class="box box--margin-right-1 box--flex-direction-row"
|
||||||
@ -284,7 +284,7 @@ exports[`SendContent Component render should match snapshot 1`] = `
|
|||||||
</strong>
|
</strong>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="box gas-display__currency-container box--flex-direction-row"
|
class="gas-details-item__currency-container"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="currency-display-component"
|
class="currency-display-component"
|
||||||
|
@ -79,6 +79,9 @@ const state = {
|
|||||||
confirmTransaction: {
|
confirmTransaction: {
|
||||||
txData: {},
|
txData: {},
|
||||||
},
|
},
|
||||||
|
send: {
|
||||||
|
draftTransactions: {},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.mock('../../store/actions', () => ({
|
jest.mock('../../store/actions', () => ({
|
||||||
|
64
ui/selectors/institutional/selectors.js
Normal file
64
ui/selectors/institutional/selectors.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import { toChecksumAddress } from 'ethereumjs-util';
|
||||||
|
import { getSelectedIdentity, getAccountType, getProvider } from '../selectors';
|
||||||
|
|
||||||
|
export function getWaitForConfirmDeepLinkDialog(state) {
|
||||||
|
return state.metamask.waitForConfirmDeepLinkDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTransactionStatusMap(state) {
|
||||||
|
return state.metamask.custodyStatusMaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCustodyAccountDetails(state) {
|
||||||
|
return state.metamask.custodyAccountDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCustodyAccountSupportedChains(state, address) {
|
||||||
|
return state.metamask.custodianSupportedChains
|
||||||
|
? state.metamask.custodianSupportedChains[toChecksumAddress(address)]
|
||||||
|
: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMmiPortfolioEnabled(state) {
|
||||||
|
return state.metamask.mmiConfiguration?.portfolio?.enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMmiPortfolioUrl(state) {
|
||||||
|
return state.metamask.mmiConfiguration?.portfolio?.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getConfiguredCustodians(state) {
|
||||||
|
return state.metamask.mmiConfiguration?.custodians || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCustodianIconForAddress(state, address) {
|
||||||
|
let custodianIcon;
|
||||||
|
|
||||||
|
const checksummedAddress = toChecksumAddress(address);
|
||||||
|
if (state.metamask.custodyAccountDetails?.[checksummedAddress]) {
|
||||||
|
const { custodianName } =
|
||||||
|
state.metamask.custodyAccountDetails[checksummedAddress];
|
||||||
|
custodianIcon = state.metamask.mmiConfiguration?.custodians?.find(
|
||||||
|
(custodian) => custodian.name === custodianName,
|
||||||
|
)?.iconUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return custodianIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getIsCustodianSupportedChain(state) {
|
||||||
|
const selectedIdentity = getSelectedIdentity(state);
|
||||||
|
const accountType = getAccountType(state);
|
||||||
|
const provider = getProvider(state);
|
||||||
|
|
||||||
|
const supportedChains =
|
||||||
|
accountType === 'custody'
|
||||||
|
? getCustodyAccountSupportedChains(state, selectedIdentity.address)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return supportedChains?.supportedChains
|
||||||
|
? supportedChains.supportedChains.includes(
|
||||||
|
Number(provider.chainId).toString(),
|
||||||
|
)
|
||||||
|
: true;
|
||||||
|
}
|
151
ui/selectors/institutional/selectors.test.js
Normal file
151
ui/selectors/institutional/selectors.test.js
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
import { toChecksumAddress } from 'ethereumjs-util';
|
||||||
|
import {
|
||||||
|
getConfiguredCustodians,
|
||||||
|
getCustodianIconForAddress,
|
||||||
|
getCustodyAccountDetails,
|
||||||
|
getCustodyAccountSupportedChains,
|
||||||
|
getMmiPortfolioEnabled,
|
||||||
|
getMmiPortfolioUrl,
|
||||||
|
getTransactionStatusMap,
|
||||||
|
getWaitForConfirmDeepLinkDialog,
|
||||||
|
getIsCustodianSupportedChain,
|
||||||
|
} from './selectors';
|
||||||
|
|
||||||
|
describe('Institutional selectors', () => {
|
||||||
|
const state = {
|
||||||
|
metamask: {
|
||||||
|
provider: {
|
||||||
|
type: 'test',
|
||||||
|
chainId: '1',
|
||||||
|
},
|
||||||
|
identities: {
|
||||||
|
'0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275': {
|
||||||
|
name: 'Custody Account A',
|
||||||
|
address: '0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
selectedAddress: '0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275',
|
||||||
|
waitForConfirmDeepLinkDialog: '123',
|
||||||
|
keyrings: [
|
||||||
|
{
|
||||||
|
type: 'Custody',
|
||||||
|
accounts: ['0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
custodyStatusMaps: '123',
|
||||||
|
custodyAccountDetails: {
|
||||||
|
'0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275': {
|
||||||
|
custodianName: 'saturn',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
custodianSupportedChains: {
|
||||||
|
'0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275': {
|
||||||
|
supportedChains: ['1', '2'],
|
||||||
|
custodianName: 'saturn',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mmiConfiguration: {
|
||||||
|
portfolio: {
|
||||||
|
enabled: true,
|
||||||
|
url: 'https://dashboard.metamask-institutional.io',
|
||||||
|
},
|
||||||
|
custodians: [
|
||||||
|
{
|
||||||
|
type: 'saturn',
|
||||||
|
name: 'saturn',
|
||||||
|
apiUrl: 'https://saturn-custody.dev.metamask-institutional.io',
|
||||||
|
iconUrl: 'images/saturn.svg',
|
||||||
|
displayName: 'Saturn Custody',
|
||||||
|
production: true,
|
||||||
|
refreshTokenUrl: null,
|
||||||
|
isNoteToTraderSupported: false,
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('getWaitForConfirmDeepLinkDialog', () => {
|
||||||
|
it('extracts a state property', () => {
|
||||||
|
const result = getWaitForConfirmDeepLinkDialog(state);
|
||||||
|
expect(result).toStrictEqual(state.metamask.waitForConfirmDeepLinkDialog);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getCustodyAccountDetails', () => {
|
||||||
|
it('extracts a state property', () => {
|
||||||
|
const result = getCustodyAccountDetails(state);
|
||||||
|
expect(result).toStrictEqual(state.metamask.custodyAccountDetails);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getTransactionStatusMap', () => {
|
||||||
|
it('extracts a state property', () => {
|
||||||
|
const result = getTransactionStatusMap(state);
|
||||||
|
expect(result).toStrictEqual(state.metamask.custodyStatusMaps);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getCustodianSupportedChains', () => {
|
||||||
|
it('extracts a state property', () => {
|
||||||
|
const result = getCustodyAccountSupportedChains(
|
||||||
|
state,
|
||||||
|
'0x5ab19e7091dd208f352f8e727b6dcc6f8abb6275',
|
||||||
|
);
|
||||||
|
expect(result).toStrictEqual(
|
||||||
|
state.metamask.custodianSupportedChains[
|
||||||
|
toChecksumAddress('0x5ab19e7091dd208f352f8e727b6dcc6f8abb6275')
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getMmiPortfolioEnabled', () => {
|
||||||
|
it('extracts a state property', () => {
|
||||||
|
const result = getMmiPortfolioEnabled(state);
|
||||||
|
expect(result).toStrictEqual(
|
||||||
|
state.metamask.mmiConfiguration.portfolio.enabled,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getMmiPortfolioUrl', () => {
|
||||||
|
it('extracts a state property', () => {
|
||||||
|
const result = getMmiPortfolioUrl(state);
|
||||||
|
expect(result).toStrictEqual(
|
||||||
|
state.metamask.mmiConfiguration.portfolio.url,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getConfiguredCustodians', () => {
|
||||||
|
it('extracts a state property', () => {
|
||||||
|
const result = getConfiguredCustodians(state);
|
||||||
|
expect(result).toStrictEqual(state.metamask.mmiConfiguration.custodians);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getCustodianIconForAddress', () => {
|
||||||
|
it('extracts a state property', () => {
|
||||||
|
const result = getCustodianIconForAddress(
|
||||||
|
state,
|
||||||
|
'0x5ab19e7091dd208f352f8e727b6dcc6f8abb6275',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toStrictEqual(
|
||||||
|
state.metamask.mmiConfiguration.custodians[0].iconUrl,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getIsCustodianSupportedChain', () => {
|
||||||
|
it('extracts a state property', () => {
|
||||||
|
const result = getIsCustodianSupportedChain(
|
||||||
|
state,
|
||||||
|
'0x5ab19e7091dd208f352f8e727b6dcc6f8abb6275',
|
||||||
|
);
|
||||||
|
expect(result).toStrictEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -228,6 +228,12 @@ export function getAccountType(state) {
|
|||||||
const currentKeyring = getCurrentKeyring(state);
|
const currentKeyring = getCurrentKeyring(state);
|
||||||
const type = currentKeyring && currentKeyring.type;
|
const type = currentKeyring && currentKeyring.type;
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(mmi)
|
||||||
|
if (type.startsWith('Custody')) {
|
||||||
|
return 'custody';
|
||||||
|
}
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case KeyringType.trezor:
|
case KeyringType.trezor:
|
||||||
case KeyringType.ledger:
|
case KeyringType.ledger:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user