aboutsummaryrefslogtreecommitdiffstats
path: root/library
diff options
context:
space:
mode:
Diffstat (limited to 'library')
-rw-r--r--library/README.md24
-rw-r--r--library/controller.js159
-rw-r--r--library/example/index.html17
-rw-r--r--library/example/index.js56
-rw-r--r--library/index.js43
-rw-r--r--library/lib/setup-iframe.js19
-rw-r--r--library/lib/setup-provider.js25
-rw-r--r--library/popup.js19
-rw-r--r--library/server.js96
-rw-r--r--library/server/index.html20
10 files changed, 478 insertions, 0 deletions
diff --git a/library/README.md b/library/README.md
new file mode 100644
index 000000000..6a6574dbd
--- /dev/null
+++ b/library/README.md
@@ -0,0 +1,24 @@
+start the dual servers (dapp + mascara)
+```
+node server.js
+```
+
+open the example dapp at `http://localhost:9002/`
+
+*You will need to build MetaMask in order for this to work*
+```
+gulp dev
+```
+to build MetaMask and have it live reload if you make changes
+
+
+## First time use:
+
+- navigate to: http://127.0.0.1:9001/popup/popup.html
+- Create an Account
+- go back to http://localhost:9002/
+- open devTools
+- click Sync Tx
+
+### Todos
+- Look into using [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API)
diff --git a/library/controller.js b/library/controller.js
new file mode 100644
index 000000000..5823287cc
--- /dev/null
+++ b/library/controller.js
@@ -0,0 +1,159 @@
+const urlUtil = require('url')
+const extend = require('xtend')
+const Dnode = require('dnode')
+const eos = require('end-of-stream')
+const ParentStream = require('iframe-stream').ParentStream
+const PortStream = require('../app/scripts/lib/port-stream.js')
+const notification = require('../app/scripts/lib/notifications.js')
+const messageManager = require('../app/scripts/lib/message-manager')
+const setupMultiplex = require('../app/scripts/lib/stream-utils.js').setupMultiplex
+const MetamaskController = require('../app/scripts/metamask-controller')
+const extension = require('../app/scripts/lib/extension')
+
+const STORAGE_KEY = 'metamask-config'
+
+
+initializeZeroClient()
+
+function initializeZeroClient() {
+
+ const controller = new MetamaskController({
+ // User confirmation callbacks:
+ showUnconfirmedMessage,
+ unlockAccountMessage,
+ showUnapprovedTx,
+ // Persistence Methods:
+ setData,
+ loadData,
+ })
+ const idStore = controller.idStore
+
+ function unlockAccountMessage () {
+ console.log('notif stub - unlockAccountMessage')
+ }
+
+ function showUnconfirmedMessage (msgParams, msgId) {
+ console.log('notif stub - showUnconfirmedMessage')
+ }
+
+ function showUnapprovedTx (txParams, txData, onTxDoneCb) {
+ console.log('notif stub - showUnapprovedTx')
+ }
+
+ //
+ // connect to other contexts
+ //
+
+ var connectionStream = new ParentStream()
+
+ connectRemote(connectionStream, getParentHref())
+
+ function connectRemote (connectionStream, originDomain) {
+ var isMetaMaskInternalProcess = (originDomain === '127.0.0.1:9001')
+ if (isMetaMaskInternalProcess) {
+ // communication with popup
+ setupTrustedCommunication(connectionStream, 'MetaMask')
+ } else {
+ // communication with page
+ setupUntrustedCommunication(connectionStream, originDomain)
+ }
+ }
+
+ function setupUntrustedCommunication (connectionStream, originDomain) {
+ // setup multiplexing
+ var mx = setupMultiplex(connectionStream)
+ // connect features
+ controller.setupProviderConnection(mx.createStream('provider'), originDomain)
+ controller.setupPublicConfig(mx.createStream('publicConfig'))
+ }
+
+ function setupTrustedCommunication (connectionStream, originDomain) {
+ // setup multiplexing
+ var mx = setupMultiplex(connectionStream)
+ // connect features
+ setupControllerConnection(mx.createStream('controller'))
+ controller.setupProviderConnection(mx.createStream('provider'), originDomain)
+ }
+
+ //
+ // remote features
+ //
+
+ function setupControllerConnection (stream) {
+ controller.stream = stream
+ var api = controller.getApi()
+ var dnode = Dnode(api)
+ stream.pipe(dnode).pipe(stream)
+ dnode.on('remote', (remote) => {
+ // push updates to popup
+ controller.ethStore.on('update', controller.sendUpdate.bind(controller))
+ controller.listeners.push(remote)
+ idStore.on('update', controller.sendUpdate.bind(controller))
+
+ // teardown on disconnect
+ eos(stream, () => {
+ controller.ethStore.removeListener('update', controller.sendUpdate.bind(controller))
+ })
+ })
+ }
+
+ function loadData () {
+ var oldData = getOldStyleData()
+ var newData
+ try {
+ newData = JSON.parse(window.localStorage[STORAGE_KEY])
+ } catch (e) {}
+
+ var data = extend({
+ meta: {
+ version: 0,
+ },
+ data: {
+ config: {
+ provider: {
+ type: 'testnet',
+ },
+ },
+ },
+ }, oldData || null, newData || null)
+ return data
+ }
+
+ function getOldStyleData () {
+ var config, wallet, seedWords
+
+ var result = {
+ meta: { version: 0 },
+ data: {},
+ }
+
+ try {
+ config = JSON.parse(window.localStorage['config'])
+ result.data.config = config
+ } catch (e) {}
+ try {
+ wallet = JSON.parse(window.localStorage['lightwallet'])
+ result.data.wallet = wallet
+ } catch (e) {}
+ try {
+ seedWords = window.localStorage['seedWords']
+ result.data.seedWords = seedWords
+ } catch (e) {}
+
+ return result
+ }
+
+ function setData (data) {
+ window.localStorage[STORAGE_KEY] = JSON.stringify(data)
+ }
+
+ function getParentHref(){
+ try {
+ var parentLocation = window.parent.location
+ return parentLocation.hostname + ':' + parentLocation.port
+ } catch (err) {
+ return 'unknown'
+ }
+ }
+
+}
diff --git a/library/example/index.html b/library/example/index.html
new file mode 100644
index 000000000..47d6da34f
--- /dev/null
+++ b/library/example/index.html
@@ -0,0 +1,17 @@
+<!doctype html>
+
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+
+ <title>MetaMask ZeroClient Example</title>
+
+</head>
+
+<body>
+ <button class="action-button-1">SYNC TX</button>
+ <button class="action-button-2">ASYNC TX</button>
+ <script src="./zero.js"></script>
+ <script src="./app.js"></script>
+</body>
+</html> \ No newline at end of file
diff --git a/library/example/index.js b/library/example/index.js
new file mode 100644
index 000000000..4a107df6a
--- /dev/null
+++ b/library/example/index.js
@@ -0,0 +1,56 @@
+
+window.addEventListener('load', web3Detect)
+
+function web3Detect() {
+ if (global.web3) {
+ logToDom('web3 detected!')
+ startApp()
+ } else {
+ logToDom('no web3 detected!')
+ }
+}
+
+function startApp(){
+ console.log('app started')
+
+ var primaryAccount = null
+ console.log('getting main account...')
+ web3.eth.getAccounts(function(err, addresses){
+ if (err) throw err
+ console.log('set address', addresses[0])
+ primaryAccount = addresses[0]
+ })
+
+ document.querySelector('.action-button-1').addEventListener('click', function(){
+ console.log('saw click')
+ console.log('sending tx')
+ web3.eth.sendTransaction({
+ from: primaryAccount,
+ to: primaryAccount,
+ value: 0,
+ }, function(err, txHash){
+ if (err) throw err
+ console.log('sendTransaction result:', err || txHash)
+ })
+ })
+ document.querySelector('.action-button-2').addEventListener('click', function(){
+ console.log('saw click')
+ setTimeout(function(){
+ console.log('sending tx')
+ web3.eth.sendTransaction({
+ from: primaryAccount,
+ to: primaryAccount,
+ value: 0,
+ }, function(err, txHash){
+ if (err) throw err
+ console.log('sendTransaction result:', err || txHash)
+ })
+ })
+ })
+
+}
+
+function logToDom(message){
+ document.body.appendChild(document.createTextNode(message))
+ console.log(message)
+} \ No newline at end of file
diff --git a/library/index.js b/library/index.js
new file mode 100644
index 000000000..b5f4f6637
--- /dev/null
+++ b/library/index.js
@@ -0,0 +1,43 @@
+const Web3 = require('web3')
+const setupProvider = require('./lib/setup-provider.js')
+
+//
+// setup web3
+//
+var provider = setupProvider()
+hijackProvider(provider)
+var web3 = new Web3(provider)
+web3.setProvider = function(){
+ console.log('MetaMask - overrode web3.setProvider')
+}
+console.log('metamask lib hijacked provider')
+
+//
+// export web3
+//
+
+global.web3 = web3
+
+//
+// ui stuff
+//
+
+var shouldPop = false
+window.addEventListener('click', function(){
+ if (!shouldPop) return
+ shouldPop = false
+ window.open('http://127.0.0.1:9001/popup/popup.html', '', 'width=360 height=500')
+ console.log('opening window...')
+})
+
+
+function hijackProvider(provider){
+ var _super = provider.sendAsync.bind(provider)
+ provider.sendAsync = function(payload, cb){
+ if (payload.method === 'eth_sendTransaction') {
+ console.log('saw send')
+ shouldPop = true
+ }
+ _super(payload, cb)
+ }
+}
diff --git a/library/lib/setup-iframe.js b/library/lib/setup-iframe.js
new file mode 100644
index 000000000..db67163df
--- /dev/null
+++ b/library/lib/setup-iframe.js
@@ -0,0 +1,19 @@
+const Iframe = require('iframe')
+const IframeStream = require('iframe-stream').IframeStream
+
+module.exports = setupIframe
+
+
+function setupIframe(opts) {
+ opts = opts || {}
+ var frame = Iframe({
+ src: opts.zeroClientProvider || 'https://zero.metamask.io/',
+ container: opts.container || document.head,
+ sandboxAttributes: opts.sandboxAttributes || ['allow-scripts', 'allow-popups'],
+ })
+ var iframe = frame.iframe
+ iframe.style.setProperty('display', 'none')
+ var iframeStream = new IframeStream(iframe)
+
+ return iframeStream
+}
diff --git a/library/lib/setup-provider.js b/library/lib/setup-provider.js
new file mode 100644
index 000000000..9efd209cb
--- /dev/null
+++ b/library/lib/setup-provider.js
@@ -0,0 +1,25 @@
+const setupIframe = require('./setup-iframe.js')
+const MetamaskInpageProvider = require('../../app/scripts/lib/inpage-provider.js')
+
+module.exports = getProvider
+
+
+function getProvider(){
+
+ if (global.web3) {
+ console.log('MetaMask ZeroClient - using environmental web3 provider')
+ return global.web3.currentProvider
+ }
+
+ console.log('MetaMask ZeroClient - injecting zero-client iframe!')
+ var iframeStream = setupIframe({
+ zeroClientProvider: 'http://127.0.0.1:9001',
+ sandboxAttributes: ['allow-scripts', 'allow-popups', 'allow-same-origin'],
+ container: document.body,
+ })
+
+ var inpageProvider = new MetamaskInpageProvider(iframeStream)
+ return inpageProvider
+
+}
+
diff --git a/library/popup.js b/library/popup.js
new file mode 100644
index 000000000..667b13371
--- /dev/null
+++ b/library/popup.js
@@ -0,0 +1,19 @@
+const injectCss = require('inject-css')
+const MetaMaskUiCss = require('../ui/css')
+const startPopup = require('../app/scripts/popup-core')
+const setupIframe = require('./lib/setup-iframe.js')
+
+
+var css = MetaMaskUiCss()
+injectCss(css)
+
+var name = 'popup'
+window.METAMASK_UI_TYPE = name
+
+var iframeStream = setupIframe({
+ zeroClientProvider: 'http://127.0.0.1:9001',
+ sandboxAttributes: ['allow-scripts', 'allow-popups', 'allow-same-origin'],
+ container: document.body,
+})
+
+startPopup(iframeStream)
diff --git a/library/server.js b/library/server.js
new file mode 100644
index 000000000..797ad8a77
--- /dev/null
+++ b/library/server.js
@@ -0,0 +1,96 @@
+const express = require('express')
+const browserify = require('browserify')
+const watchify = require('watchify')
+const babelify = require('babelify')
+
+const zeroBundle = createBundle('./index.js')
+const controllerBundle = createBundle('./controller.js')
+const popupBundle = createBundle('./popup.js')
+const appBundle = createBundle('./example/index.js')
+
+//
+// Iframe Server
+//
+
+const iframeServer = express()
+
+// serve popup window
+iframeServer.get('/popup/scripts/popup.js', function(req, res){
+ res.send(popupBundle.latest)
+})
+iframeServer.use('/popup', express.static('../dist/chrome'))
+
+// serve controller bundle
+iframeServer.get('/controller.js', function(req, res){
+ res.send(controllerBundle.latest)
+})
+
+// serve background controller
+iframeServer.use(express.static('./server'))
+
+// start the server
+const mascaraPort = 9001
+iframeServer.listen(mascaraPort)
+console.log(`Mascara service listening on port ${mascaraPort}`)
+
+
+//
+// Dapp Server
+//
+
+const dappServer = express()
+
+// serve metamask-lib bundle
+dappServer.get('/zero.js', function(req, res){
+ res.send(zeroBundle.latest)
+})
+
+// serve dapp bundle
+dappServer.get('/app.js', function(req, res){
+ res.send(appBundle.latest)
+})
+
+// serve static
+dappServer.use(express.static('./example'))
+
+// start the server
+const dappPort = '9002'
+dappServer.listen(dappPort)
+console.log(`Dapp listening on port ${dappPort}`)
+
+//
+// util
+//
+
+function serveBundle(entryPoint){
+ const bundle = createBundle(entryPoint)
+ return function(req, res){
+ res.send(bundle.latest)
+ }
+}
+
+function createBundle(entryPoint){
+
+ var bundleContainer = {}
+
+ var bundler = browserify({
+ entries: [entryPoint],
+ cache: {},
+ packageCache: {},
+ plugin: [watchify],
+ })
+
+ bundler.on('update', bundle)
+ bundle()
+
+ return bundleContainer
+
+ function bundle() {
+ bundler.bundle(function(err, result){
+ if (err) throw err
+ console.log(`Bundle updated! (${entryPoint})`)
+ bundleContainer.latest = result.toString()
+ })
+ }
+
+}
diff --git a/library/server/index.html b/library/server/index.html
new file mode 100644
index 000000000..2308dd98b
--- /dev/null
+++ b/library/server/index.html
@@ -0,0 +1,20 @@
+<!doctype html>
+
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+
+ <title>MetaMask ZeroClient Iframe</title>
+ <meta name="description" content="MetaMask ZeroClient">
+ <meta name="author" content="MetaMask">
+
+ <!--[if lt IE 9]>
+ <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
+ <![endif]-->
+</head>
+
+<body>
+ Hello! I am the MetaMask iframe.
+ <script src="/controller.js"></script>
+</body>
+</html> \ No newline at end of file