-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
193 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
/* eslint-disable import/no-commonjs */ | ||
/* eslint-disable @typescript-eslint/no-require-imports */ | ||
/* eslint-disable @typescript-eslint/no-var-requires */ | ||
// eslint-disable-next-line import/no-nodejs-modules | ||
import { Duplex } from 'stream'; | ||
import { | ||
createSwappableProxy, | ||
createEventEmitterProxy, | ||
} from 'swappable-obj-proxy'; | ||
import { JsonRpcEngine } from 'json-rpc-engine'; | ||
import { createEngineStream } from 'json-rpc-middleware-stream'; | ||
import { NetworksChainId } from '@metamask/controllers'; | ||
|
||
import Engine from '../Engine'; | ||
import { setupMultiplex } from '../../util/streams'; | ||
import { | ||
createOriginMiddleware, | ||
createLoggerMiddleware, | ||
} from '../../util/middlewares'; | ||
import Logger from '../../util/Logger'; | ||
import { getAllNetworks } from '../../util/networks'; | ||
|
||
const ObjectMultiplex = require('obj-multiplex'); | ||
const createFilterMiddleware = require('eth-json-rpc-filters'); | ||
const createSubscriptionManager = require('eth-json-rpc-filters/subscriptionManager'); | ||
const providerAsMiddleware = require('eth-json-rpc-middleware/providerAsMiddleware'); | ||
const pump = require('pump'); | ||
|
||
interface ISnapBridgeProps { | ||
snapId: string; | ||
stream: Duplex; | ||
getRPCMethodMiddleware: (args: any) => any; | ||
} | ||
|
||
export default class SnapBridge { | ||
snapId: string; | ||
stream: Duplex; | ||
getRPCMethodMiddleware: (args: any) => any; | ||
provider: any; | ||
blockTracker: any; | ||
|
||
#mux: typeof ObjectMultiplex; | ||
#providerProxy: any; | ||
#blockTrackerProxy: any; | ||
|
||
constructor({ snapId, stream, getRPCMethodMiddleware }: ISnapBridgeProps) { | ||
this.snapId = snapId; | ||
this.stream = stream; | ||
this.getRPCMethodMiddleware = getRPCMethodMiddleware; | ||
|
||
const { NetworkController, PreferencesController } = Engine.context as any; | ||
|
||
const provider = NetworkController.provider; | ||
const blockTracker = provider._blockTracker; | ||
|
||
this.#providerProxy = null; | ||
this.#blockTrackerProxy = null; | ||
|
||
this.#setProvider(provider); | ||
this.#setBlockTracker(blockTracker); | ||
|
||
this.#mux = setupMultiplex(this.stream); | ||
} | ||
|
||
#setProvider = (provider: any): void => { | ||
if (this.#providerProxy) { | ||
this.#providerProxy.setTarget(provider); | ||
} else { | ||
this.#providerProxy = createSwappableProxy(provider); | ||
} | ||
this.provider = provider; | ||
}; | ||
|
||
#setBlockTracker = (blockTracker: any): void => { | ||
if (this.#blockTrackerProxy) { | ||
this.#blockTrackerProxy.setTarget(blockTracker); | ||
} else { | ||
this.#blockTrackerProxy = createEventEmitterProxy(blockTracker, { | ||
eventFilter: 'skipInternal', | ||
}); | ||
} | ||
this.blockTracker = blockTracker; | ||
}; | ||
|
||
getProviderState() { | ||
const memState = this.getState(); | ||
return { | ||
isUnlocked: this.isUnlocked(), | ||
...this.getProviderNetworkState(memState), | ||
}; | ||
} | ||
|
||
setupProviderConnection = () => { | ||
console.log('method setupProviderConnection'); | ||
const outStream = this.#mux.createStream('metamask-provider'); | ||
const engine = this.setupProviderEngine(); | ||
const providerStream = createEngineStream({ engine }); | ||
pump(outStream, providerStream, outStream, (err: any) => { | ||
// handle any middleware cleanup | ||
engine._middleware.forEach((mid: any) => { | ||
if (mid.destroy && typeof mid.destroy === 'function') { | ||
mid.destroy(); | ||
} | ||
}); | ||
if (err) Logger.log('Error with provider stream conn', err); | ||
}); | ||
}; | ||
|
||
setupProviderEngine = () => { | ||
const engine = new JsonRpcEngine(); | ||
|
||
// create filter polyfill middleware | ||
const filterMiddleware = createFilterMiddleware({ | ||
provider: this.#providerProxy, | ||
blockTracker: this.#blockTrackerProxy, | ||
}); | ||
|
||
// create subscription polyfill middleware | ||
const subscriptionManager = createSubscriptionManager({ | ||
provider: this.#providerProxy, | ||
blockTracker: this.#blockTrackerProxy, | ||
}); | ||
|
||
subscriptionManager.events.on('notification', (message: any) => | ||
engine.emit('notification', message), | ||
); | ||
|
||
engine.push(createOriginMiddleware({ origin: this.snapId })); | ||
engine.push(createLoggerMiddleware({ origin: this.snapId })); | ||
|
||
// Filter and subscription polyfills | ||
engine.push(filterMiddleware); | ||
engine.push(subscriptionManager.middleware); | ||
|
||
// User-Facing RPC methods | ||
engine.push( | ||
this.getRPCMethodMiddleware({ | ||
hostname: this.snapId, | ||
getProviderState: this.getProviderState.bind(this), | ||
}), | ||
); | ||
|
||
// Forward to metamask primary provider | ||
engine.push(providerAsMiddleware(this.#providerProxy)); | ||
return engine; | ||
}; | ||
|
||
getNetworkState = ({ network }: { network: string }) => { | ||
const { NetworkController } = Engine.context as any; | ||
const networkType = NetworkController.state.provider.type; | ||
const networkProvider = NetworkController.state.provider; | ||
|
||
const isInitialNetwork = | ||
networkType && getAllNetworks().includes(networkType); | ||
let chainId; | ||
|
||
if (isInitialNetwork) { | ||
chainId = NetworksChainId[networkType]; | ||
} else if (networkType === 'rpc') { | ||
chainId = networkProvider.chainId; | ||
} | ||
if (chainId && !chainId.startsWith('0x')) { | ||
// Convert to hex | ||
chainId = `0x${parseInt(chainId, 10).toString(16)}`; | ||
} | ||
|
||
const result = { | ||
networkVersion: network, | ||
chainId, | ||
}; | ||
return result; | ||
}; | ||
|
||
isUnlocked = (): boolean => { | ||
const { KeyringController } = Engine.context as any; | ||
return KeyringController.isUnlocked(); | ||
}; | ||
|
||
getState = () => { | ||
const { context, datamodel } = Engine; | ||
const { KeyringController } = context as any; | ||
const vault = KeyringController.state.vault; | ||
const { network, selectedAddress } = datamodel.flatState as any; | ||
return { | ||
isInitialized: !!vault, | ||
isUnlocked: true, | ||
network, | ||
selectedAddress, | ||
}; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
import SnapExecutionService from './SnapExecutionService'; | ||
import SnapBridge from './SnapBridge'; | ||
|
||
// eslint-disable-next-line import/prefer-default-export | ||
export { SnapExecutionService }; | ||
export { SnapExecutionService, SnapBridge }; |