* 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 { |
||||
BaseController, |
||||
BaseControllerV2, |
||||
ControllerMessenger, |
||||
} from '@metamask/controllers'; |
||||
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 () { |
||||
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'); |
||||
}); |
||||
|
||||
it('should register initial structure', function () { |
||||
const controllerMessenger = new ControllerMessenger(); |
||||
const testStore = new ObservableStore(); |
||||
const store = new ComposableObservableStore(null, { TestStore: testStore }); |
||||
const store = new ComposableObservableStore({ |
||||
config: { TestStore: testStore }, |
||||
controllerMessenger, |
||||
}); |
||||
testStore.putState('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 store = new ComposableObservableStore(); |
||||
const store = new ComposableObservableStore({ controllerMessenger }); |
||||
store.updateStructure({ TestStore: testStore }); |
||||
testStore.putState('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 () { |
||||
const controllerMessenger = new ControllerMessenger(); |
||||
const fooStore = new ObservableStore({ foo: 'foo' }); |
||||
const barStore = new ObservableStore({ bar: 'bar' }); |
||||
const store = new ComposableObservableStore(null, { |
||||
FooStore: fooStore, |
||||
BarStore: barStore, |
||||
const barController = new ExampleController({ |
||||
messenger: controllerMessenger, |
||||
}); |
||||
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