* origin/develop: (227 commits) Improve UI + content for price difference notifications (#11145) Swaps: Create a new swap (#11124) Bump @metamask/controllers from 9.0.0 to 9.1.0 (#11150) Capture exception instead of throw error in useTransactionDisplayData (#11153) Fixing jest component test output errors (#11139) Avoid showing "Gas price extremely low" warning in advanced tab for testnets (#11111) @metamask/auto-changelog@2.0.1 (#11140) Migrate to new CurrencyRateController (#11005) bump allow scripts (#11134) Show Sentry CLI output when uploading artifacts (#11100) use etherscan-link customBlockExplorer methods with customNetwork usage tracking (#11017) Adding notification for updated seed phrase wording (#11131) Bumping package.json Fix a condition for checking if a token should be added (#11127) Removing support survey notification from What's New (#11118) Handling custom token decimal fetch failure due to network error (#10956) Hide basic tab in advanced gas modal for speedup and cancel when on testnets (#11115) Migrate Sentry settings to environment variables (#11085) Update eth-ledger-bridge-keyring to v0.5.0 (#11064) fix metaRPCClientFactory id handling (#11116) ...feature/default_network_editable
commit
203456d4c8
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 99 KiB |
@ -0,0 +1,116 @@ |
|||||||
|
WEBVTT |
||||||
|
|
||||||
|
1 |
||||||
|
00:00:00.780 --> 00:00:04.580 |
||||||
|
MetaMask is a new way to connect |
||||||
|
to sites and applications. |
||||||
|
|
||||||
|
2 |
||||||
|
00:00:04.580 --> 00:00:08.860 |
||||||
|
On traditional websites, a central database |
||||||
|
or bank is responsible for controlling and |
||||||
|
|
||||||
|
3 |
||||||
|
00:00:08.860 --> 00:00:10.179 |
||||||
|
recovering your accounts. |
||||||
|
|
||||||
|
4 |
||||||
|
00:00:10.179 --> 00:00:15.050 |
||||||
|
But on MetaMask, all of the power belongs |
||||||
|
to the holder of a master key. |
||||||
|
|
||||||
|
5 |
||||||
|
00:00:15.050 --> 00:00:18.460 |
||||||
|
Whoever holds the key, controls the accounts. |
||||||
|
|
||||||
|
6 |
||||||
|
00:00:18.460 --> 00:00:21.110 |
||||||
|
Your secret recovery phrase |
||||||
|
is your "master key". |
||||||
|
|
||||||
|
7 |
||||||
|
00:00:21.110 --> 00:00:26.070 |
||||||
|
It's a series of 12 words that are generated |
||||||
|
when you first set up MetaMask, which allow |
||||||
|
|
||||||
|
8 |
||||||
|
00:00:26.070 --> 00:00:30.120 |
||||||
|
you to recover your wallet and funds if you |
||||||
|
ever lose access. |
||||||
|
|
||||||
|
9 |
||||||
|
00:00:30.120 --> 00:00:33.451 |
||||||
|
It's important that you secure |
||||||
|
your wallet by keeping your |
||||||
|
|
||||||
|
10 |
||||||
|
00:00:33.451 --> 00:00:37.510 |
||||||
|
secret recovery phrase |
||||||
|
very safe, and very secret. |
||||||
|
|
||||||
|
11 |
||||||
|
00:00:37.510 --> 00:00:41.429 |
||||||
|
If anyone gets access to it, they will have |
||||||
|
the "master key" to your wallet and can |
||||||
|
|
||||||
|
12 |
||||||
|
00:00:41.429 --> 00:00:45.190 |
||||||
|
freely access and take all of your funds. |
||||||
|
|
||||||
|
13 |
||||||
|
00:00:45.190 --> 00:00:50.109 |
||||||
|
To secure your MetaMask wallet you'll want |
||||||
|
to safely save your secret recovery phrase. |
||||||
|
|
||||||
|
14 |
||||||
|
00:00:50.109 --> 00:00:54.930 |
||||||
|
You can write it down, hide it somewhere, |
||||||
|
put it in a safe deposit box |
||||||
|
|
||||||
|
15 |
||||||
|
00:00:54.930 --> 00:00:57.729 |
||||||
|
or use a secure password manager. |
||||||
|
|
||||||
|
16 |
||||||
|
00:00:57.729 --> 00:01:01.050 |
||||||
|
Some users even engrave their |
||||||
|
phrase onto a metal plate! |
||||||
|
|
||||||
|
17 |
||||||
|
00:01:01.050 --> 00:01:04.440 |
||||||
|
Nobody, not even the team |
||||||
|
at MetaMask, can help you |
||||||
|
|
||||||
|
18 |
||||||
|
00:01:04.440 --> 00:01:07.820 |
||||||
|
recover your wallet if you lose |
||||||
|
your secret recovery phrase. |
||||||
|
|
||||||
|
19 |
||||||
|
00:01:07.820 --> 00:01:12.072 |
||||||
|
If you haven't written down your secret recovery |
||||||
|
phrase and stored it somewhere safe, |
||||||
|
|
||||||
|
20 |
||||||
|
00:01:12.072 --> 00:01:15.492 |
||||||
|
do it now. We'll wait. |
||||||
|
|
||||||
|
21 |
||||||
|
00:01:15.500 --> 00:01:20.780 |
||||||
|
And remember, never share your secret recovery |
||||||
|
phrase with anyone: not even us. |
||||||
|
|
||||||
|
22 |
||||||
|
00:01:20.780 --> 00:01:24.910 |
||||||
|
If anyone ever asks you for it, |
||||||
|
they're trying to scam you. |
||||||
|
|
||||||
|
23 |
||||||
|
00:01:24.910 --> 00:01:26.250 |
||||||
|
That's it! |
||||||
|
|
||||||
|
24 |
||||||
|
00:01:26.250 --> 00:01:31.020 |
||||||
|
Now you know what a secret recovery phrase |
||||||
|
is and how to keep your wallet safe and secure. |
||||||
|
|
Binary file not shown.
@ -1,35 +1,194 @@ |
|||||||
import assert from 'assert'; |
import { strict as assert } from 'assert'; |
||||||
import { ObservableStore } from '@metamask/obs-store'; |
import { ObservableStore } from '@metamask/obs-store'; |
||||||
|
import { |
||||||
|
BaseController, |
||||||
|
BaseControllerV2, |
||||||
|
ControllerMessenger, |
||||||
|
} from '@metamask/controllers'; |
||||||
import ComposableObservableStore from './ComposableObservableStore'; |
import ComposableObservableStore from './ComposableObservableStore'; |
||||||
|
|
||||||
|
class OldExampleController extends BaseController { |
||||||
|
name = 'OldExampleController'; |
||||||
|
|
||||||
|
defaultState = { |
||||||
|
baz: 'baz', |
||||||
|
}; |
||||||
|
|
||||||
|
constructor() { |
||||||
|
super(); |
||||||
|
this.initialize(); |
||||||
|
} |
||||||
|
|
||||||
|
updateBaz(contents) { |
||||||
|
this.update({ baz: contents }); |
||||||
|
} |
||||||
|
} |
||||||
|
class ExampleController extends BaseControllerV2 { |
||||||
|
static defaultState = { |
||||||
|
bar: 'bar', |
||||||
|
}; |
||||||
|
|
||||||
|
static metadata = { |
||||||
|
bar: { persist: true, anonymous: true }, |
||||||
|
}; |
||||||
|
|
||||||
|
constructor({ messenger }) { |
||||||
|
super({ |
||||||
|
messenger, |
||||||
|
name: 'ExampleController', |
||||||
|
metadata: ExampleController.metadata, |
||||||
|
state: ExampleController.defaultState, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
updateBar(contents) { |
||||||
|
this.update(() => { |
||||||
|
return { bar: contents }; |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
describe('ComposableObservableStore', function () { |
describe('ComposableObservableStore', function () { |
||||||
it('should register initial state', function () { |
it('should register initial state', function () { |
||||||
const store = new ComposableObservableStore('state'); |
const controllerMessenger = new ControllerMessenger(); |
||||||
|
const store = new ComposableObservableStore({ |
||||||
|
controllerMessenger, |
||||||
|
state: 'state', |
||||||
|
}); |
||||||
assert.strictEqual(store.getState(), 'state'); |
assert.strictEqual(store.getState(), 'state'); |
||||||
}); |
}); |
||||||
|
|
||||||
it('should register initial structure', function () { |
it('should register initial structure', function () { |
||||||
|
const controllerMessenger = new ControllerMessenger(); |
||||||
const testStore = new ObservableStore(); |
const testStore = new ObservableStore(); |
||||||
const store = new ComposableObservableStore(null, { TestStore: testStore }); |
const store = new ComposableObservableStore({ |
||||||
|
config: { TestStore: testStore }, |
||||||
|
controllerMessenger, |
||||||
|
}); |
||||||
testStore.putState('state'); |
testStore.putState('state'); |
||||||
assert.deepEqual(store.getState(), { TestStore: 'state' }); |
assert.deepEqual(store.getState(), { TestStore: 'state' }); |
||||||
}); |
}); |
||||||
|
|
||||||
it('should update structure', function () { |
it('should update structure with observable store', function () { |
||||||
|
const controllerMessenger = new ControllerMessenger(); |
||||||
const testStore = new ObservableStore(); |
const testStore = new ObservableStore(); |
||||||
const store = new ComposableObservableStore(); |
const store = new ComposableObservableStore({ controllerMessenger }); |
||||||
store.updateStructure({ TestStore: testStore }); |
store.updateStructure({ TestStore: testStore }); |
||||||
testStore.putState('state'); |
testStore.putState('state'); |
||||||
assert.deepEqual(store.getState(), { TestStore: 'state' }); |
assert.deepEqual(store.getState(), { TestStore: 'state' }); |
||||||
}); |
}); |
||||||
|
|
||||||
|
it('should update structure with BaseController-based controller', function () { |
||||||
|
const controllerMessenger = new ControllerMessenger(); |
||||||
|
const oldExampleController = new OldExampleController(); |
||||||
|
const store = new ComposableObservableStore({ controllerMessenger }); |
||||||
|
store.updateStructure({ OldExample: oldExampleController }); |
||||||
|
oldExampleController.updateBaz('state'); |
||||||
|
assert.deepEqual(store.getState(), { OldExample: { baz: 'state' } }); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should update structure with BaseControllerV2-based controller', function () { |
||||||
|
const controllerMessenger = new ControllerMessenger(); |
||||||
|
const exampleController = new ExampleController({ |
||||||
|
messenger: controllerMessenger, |
||||||
|
}); |
||||||
|
const store = new ComposableObservableStore({ controllerMessenger }); |
||||||
|
store.updateStructure({ Example: exampleController }); |
||||||
|
exampleController.updateBar('state'); |
||||||
|
console.log(exampleController.state); |
||||||
|
assert.deepEqual(store.getState(), { Example: { bar: 'state' } }); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should update structure with all three types of stores', function () { |
||||||
|
const controllerMessenger = new ControllerMessenger(); |
||||||
|
const exampleStore = new ObservableStore(); |
||||||
|
const exampleController = new ExampleController({ |
||||||
|
messenger: controllerMessenger, |
||||||
|
}); |
||||||
|
const oldExampleController = new OldExampleController(); |
||||||
|
const store = new ComposableObservableStore({ controllerMessenger }); |
||||||
|
store.updateStructure({ |
||||||
|
Example: exampleController, |
||||||
|
OldExample: oldExampleController, |
||||||
|
Store: exampleStore, |
||||||
|
}); |
||||||
|
exampleStore.putState('state'); |
||||||
|
exampleController.updateBar('state'); |
||||||
|
oldExampleController.updateBaz('state'); |
||||||
|
assert.deepEqual(store.getState(), { |
||||||
|
Example: { bar: 'state' }, |
||||||
|
OldExample: { baz: 'state' }, |
||||||
|
Store: 'state', |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
it('should return flattened state', function () { |
it('should return flattened state', function () { |
||||||
|
const controllerMessenger = new ControllerMessenger(); |
||||||
const fooStore = new ObservableStore({ foo: 'foo' }); |
const fooStore = new ObservableStore({ foo: 'foo' }); |
||||||
const barStore = new ObservableStore({ bar: 'bar' }); |
const barController = new ExampleController({ |
||||||
const store = new ComposableObservableStore(null, { |
messenger: controllerMessenger, |
||||||
FooStore: fooStore, |
}); |
||||||
BarStore: barStore, |
const bazController = new OldExampleController(); |
||||||
|
const store = new ComposableObservableStore({ |
||||||
|
config: { |
||||||
|
FooStore: fooStore, |
||||||
|
BarStore: barController, |
||||||
|
BazStore: bazController, |
||||||
|
}, |
||||||
|
controllerMessenger, |
||||||
|
state: { |
||||||
|
FooStore: fooStore.getState(), |
||||||
|
BarStore: barController.state, |
||||||
|
BazStore: bazController.state, |
||||||
|
}, |
||||||
|
}); |
||||||
|
assert.deepEqual(store.getFlatState(), { |
||||||
|
foo: 'foo', |
||||||
|
bar: 'bar', |
||||||
|
baz: 'baz', |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should return empty flattened state when not configured', function () { |
||||||
|
const controllerMessenger = new ControllerMessenger(); |
||||||
|
const store = new ComposableObservableStore({ controllerMessenger }); |
||||||
|
assert.deepEqual(store.getFlatState(), {}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should throw if the controller messenger is omitted and the config includes a BaseControllerV2 controller', function () { |
||||||
|
const controllerMessenger = new ControllerMessenger(); |
||||||
|
const exampleController = new ExampleController({ |
||||||
|
messenger: controllerMessenger, |
||||||
|
}); |
||||||
|
assert.throws( |
||||||
|
() => |
||||||
|
new ComposableObservableStore({ |
||||||
|
config: { |
||||||
|
Example: exampleController, |
||||||
|
}, |
||||||
|
}), |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should throw if the controller messenger is omitted and updateStructure called with a BaseControllerV2 controller', function () { |
||||||
|
const controllerMessenger = new ControllerMessenger(); |
||||||
|
const exampleController = new ExampleController({ |
||||||
|
messenger: controllerMessenger, |
||||||
}); |
}); |
||||||
assert.deepEqual(store.getFlatState(), { foo: 'foo', bar: 'bar' }); |
const store = new ComposableObservableStore({}); |
||||||
|
assert.throws(() => store.updateStructure({ Example: exampleController })); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should throw if initialized with undefined config entry', function () { |
||||||
|
const controllerMessenger = new ControllerMessenger(); |
||||||
|
assert.throws( |
||||||
|
() => |
||||||
|
new ComposableObservableStore({ |
||||||
|
config: { |
||||||
|
Example: undefined, |
||||||
|
}, |
||||||
|
controllerMessenger, |
||||||
|
}), |
||||||
|
); |
||||||
}); |
}); |
||||||
}); |
}); |
||||||
|
@ -0,0 +1,97 @@ |
|||||||
|
import { ethErrors } from 'eth-rpc-errors'; |
||||||
|
import { omit } from 'lodash'; |
||||||
|
import { MESSAGE_TYPE } from '../../../../../shared/constants/app'; |
||||||
|
import { |
||||||
|
isPrefixedFormattedHexString, |
||||||
|
isSafeChainId, |
||||||
|
} from '../../../../../shared/modules/network.utils'; |
||||||
|
|
||||||
|
const switchEthereumChain = { |
||||||
|
methodNames: [MESSAGE_TYPE.SWITCH_ETHEREUM_CHAIN], |
||||||
|
implementation: switchEthereumChainHandler, |
||||||
|
}; |
||||||
|
export default switchEthereumChain; |
||||||
|
|
||||||
|
async function switchEthereumChainHandler( |
||||||
|
req, |
||||||
|
res, |
||||||
|
_next, |
||||||
|
end, |
||||||
|
{ getCurrentChainId, findCustomRpcBy, updateRpcTarget, requestUserApproval }, |
||||||
|
) { |
||||||
|
if (!req.params?.[0] || typeof req.params[0] !== 'object') { |
||||||
|
return end( |
||||||
|
ethErrors.rpc.invalidParams({ |
||||||
|
message: `Expected single, object parameter. Received:\n${JSON.stringify( |
||||||
|
req.params, |
||||||
|
)}`,
|
||||||
|
}), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
const { origin } = req; |
||||||
|
|
||||||
|
const { chainId } = req.params[0]; |
||||||
|
|
||||||
|
const otherKeys = Object.keys(omit(req.params[0], ['chainId'])); |
||||||
|
|
||||||
|
if (otherKeys.length > 0) { |
||||||
|
return end( |
||||||
|
ethErrors.rpc.invalidParams({ |
||||||
|
message: `Received unexpected keys on object parameter. Unsupported keys:\n${otherKeys}`, |
||||||
|
}), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
const _chainId = typeof chainId === 'string' && chainId.toLowerCase(); |
||||||
|
|
||||||
|
if (!isPrefixedFormattedHexString(_chainId)) { |
||||||
|
return end( |
||||||
|
ethErrors.rpc.invalidParams({ |
||||||
|
message: `Expected 0x-prefixed, unpadded, non-zero hexadecimal string 'chainId'. Received:\n${chainId}`, |
||||||
|
}), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
if (!isSafeChainId(parseInt(_chainId, 16))) { |
||||||
|
return end( |
||||||
|
ethErrors.rpc.invalidParams({ |
||||||
|
message: `Invalid chain ID "${_chainId}": numerical value greater than max safe value. Received:\n${chainId}`, |
||||||
|
}), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
const existingNetwork = findCustomRpcBy({ chainId: _chainId }); |
||||||
|
|
||||||
|
if (existingNetwork) { |
||||||
|
const currentChainId = getCurrentChainId(); |
||||||
|
if (currentChainId === _chainId) { |
||||||
|
res.result = null; |
||||||
|
return end(); |
||||||
|
} |
||||||
|
try { |
||||||
|
await updateRpcTarget( |
||||||
|
await requestUserApproval({ |
||||||
|
origin, |
||||||
|
type: MESSAGE_TYPE.SWITCH_ETHEREUM_CHAIN, |
||||||
|
requestData: { |
||||||
|
rpcUrl: existingNetwork.rpcUrl, |
||||||
|
chainId: existingNetwork.chainId, |
||||||
|
nickname: existingNetwork.nickname, |
||||||
|
ticker: existingNetwork.ticker, |
||||||
|
}, |
||||||
|
}), |
||||||
|
); |
||||||
|
res.result = null; |
||||||
|
} catch (error) { |
||||||
|
return end(error); |
||||||
|
} |
||||||
|
return end(); |
||||||
|
} |
||||||
|
return end( |
||||||
|
ethErrors.provider.custom({ |
||||||
|
code: 4902, // To-be-standardized "unrecognized chain ID" error
|
||||||
|
message: `Unrecognized chain ID "${chainId}". Try adding the chain using ${MESSAGE_TYPE.ADD_ETHEREUM_CHAIN} first.`, |
||||||
|
}), |
||||||
|
); |
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue