diff --git a/src/BaseProvider.rpc.test.ts b/src/BaseProvider.rpc.test.ts index 707aa463..bb1b6389 100644 --- a/src/BaseProvider.rpc.test.ts +++ b/src/BaseProvider.rpc.test.ts @@ -309,4 +309,25 @@ describe('BaseProvider: RPC', () => { }); }); }); + describe('provider events', () => { + it('calls chainChanged when it chainId changes ', async () => { + const mockStream = new MockDuplexStream(); + const baseProvider = new BaseProvider(mockStream); + (baseProvider as any)._state.initialized = true; + await new Promise((resolve) => { + baseProvider.on('chainChanged', (changed) => { + expect(changed).toBeDefined(); + resolve(undefined); + }); + mockStream.push({ + name: 'metamask-provider', + data: { + jsonrpc: '2.0', + method: 'metamask_chainChanged', + params: { chainId: '0x1', networkVersion: '0x1' }, + }, + }); + }); + }); + }); }); diff --git a/src/MetaMaskInpageProvider.rpc.test.ts b/src/MetaMaskInpageProvider.rpc.test.ts index f9950da7..14b62a79 100644 --- a/src/MetaMaskInpageProvider.rpc.test.ts +++ b/src/MetaMaskInpageProvider.rpc.test.ts @@ -636,4 +636,46 @@ describe('MetaMaskInpageProvider: RPC', () => { ); }); }); + + describe('provider events', () => { + it('calls chainChanged when it chainId changes ', async () => { + const mockStream = new MockDuplexStream(); + const inpageProvider = new MetaMaskInpageProvider(mockStream); + (inpageProvider as any)._state.initialized = true; + await new Promise((resolve) => { + inpageProvider.on('chainChanged', (changed) => { + expect(changed).toBe('0x1'); + resolve(undefined); + }); + mockStream.push({ + name: 'metamask-provider', + data: { + jsonrpc: '2.0', + method: 'metamask_chainChanged', + params: { chainId: '0x1', networkVersion: '0x1' }, + }, + }); + }); + }); + + it('calls networkChanged when it networkVersion changes ', async () => { + const mockStream = new MockDuplexStream(); + const inpageProvider = new MetaMaskInpageProvider(mockStream); + (inpageProvider as any)._state.initialized = true; + await new Promise((resolve) => { + inpageProvider.on('networkChanged', (changed) => { + expect(changed).toBe('0x1'); + resolve(undefined); + }); + mockStream.push({ + name: 'metamask-provider', + data: { + jsonrpc: '2.0', + method: 'metamask_chainChanged', + params: { chainId: '0x1', networkVersion: '0x1' }, + }, + }); + }); + }); + }); }); diff --git a/src/MetaMaskInpageProvider.ts b/src/MetaMaskInpageProvider.ts index 3b3e7319..1caddd4b 100644 --- a/src/MetaMaskInpageProvider.ts +++ b/src/MetaMaskInpageProvider.ts @@ -395,4 +395,34 @@ export default class MetaMaskInpageProvider extends BaseProvider { }, ); } + + /** + * Upon receipt of a new chainId and networkVersion, emits corresponding + * events and sets relevant public state. + * Does nothing if neither the chainId nor the networkVersion are different + * from existing values. + * + * @emits MetamaskInpageProvider#chainChanged + * @emits MetamaskInpageProvider#networkChanged + * @param networkInfo - An object with network info. + * @param networkInfo.chainId - The latest chain ID. + * @param networkInfo.networkVersion - The latest network ID. + */ + protected _handleChainChanged({ + chainId, + networkVersion, + }: { chainId?: string; networkVersion?: string } = {}) { + super._handleChainChanged({ chainId, networkVersion }); + + if ( + networkVersion && + networkVersion !== 'loading' && + networkVersion !== this.networkVersion + ) { + this.networkVersion = networkVersion; + if (this._state.initialized) { + this.emit('networkChanged', this.networkVersion); + } + } + } }