index.js
1 import EmbarkJSPlasma from "embarkjs-plasma"; 2 import {dappPath} from "embark-utils"; 3 import {formatDate} from "./utils"; 4 5 // Service check constants 6 const SERVICE_CHECK_ON = "on"; 7 const SERVICE_CHECK_OFF = "off"; 8 9 /** 10 * Plugin that allows Embark to connect to and interact with an existing Plama chain, 11 * and provides an EmbarkJS.Plasma API to allow the DApp to interact with the chain. 12 */ 13 class EmbarkPlasma extends EmbarkJSPlasma { 14 constructor(embark) { 15 super(embark); 16 17 this.embark = embark; 18 this.events = embark.events; 19 this.pluginConfig = embark.pluginConfig; 20 21 // gets hydrated blockchain config from embark, use it to init 22 // this.events.once("config:load:contracts", this.addCodeToEmbarkJs.bind(this)); 23 24 this.registerServiceCheck(); 25 this.registerConsoleCommands(); 26 27 this.events.request("blockchain:get", (web3) => { 28 this.events.request("blockchain:ready", () => { 29 this.events.request("blockchain:provider:contract:accounts:getAll", (_err, accounts) => { 30 this.accounts = accounts; 31 this.addCodeToEmbarkJs(); 32 this.init(web3, true); 33 }); 34 }); 35 }); 36 } 37 38 generateSymlink(varName, location) { 39 return new Promise((resolve, reject) => { 40 this.events.request("code-generator:symlink:generate", location, varName, (err, symlinkDest) => { 41 if (err) { 42 return reject(err); 43 } 44 resolve(symlinkDest); 45 }); 46 }); 47 } 48 49 codeGeneratorReady() { 50 return new Promise((resolve, _reject) => { 51 this.events.request("code-generator:ready", () => { 52 resolve(); 53 }); 54 }); 55 } 56 57 async addCodeToEmbarkJs() { 58 const nodePath = dappPath("node_modules"); 59 const embarkjsOmgPath = require.resolve("embarkjs-plasma", { 60 paths: [nodePath] 61 }); 62 let embarkJsOmgSymlinkPath; 63 64 await this.codeGeneratorReady(); 65 try { 66 embarkJsOmgSymlinkPath = await this.generateSymlink("embarkjs-plasma", embarkjsOmgPath); 67 } catch (err) { 68 this.logger.error(__("Error creating a symlink to embarkjs-plasma")); 69 return this.logger.error(err.message || err); 70 } 71 72 this.events.emit("runcode:register", "embarkjsOmg", require("embarkjs-plasma"), () => { 73 let code = ""; 74 code += `\nlet __embarkPlasma = global.embarkjsOmg || require('${embarkJsOmgSymlinkPath}').default;`; 75 code += `\nconst opts = { 76 logger: { 77 info: console.log, 78 warn: console.warn, 79 error: console.error, 80 trace: console.trace 81 }, 82 pluginConfig: ${JSON.stringify(this.pluginConfig)}, 83 accounts: ${JSON.stringify(this.accounts)} 84 };`; 85 code += "\nEmbarkJS.onReady(() => {"; 86 code += "\n EmbarkJS.Plasma = new __embarkPlasma(opts);"; 87 code += `\n const embarkJsWeb3Provider = EmbarkJS.Blockchain.Providers["web3"]`; 88 code += `\n if (!embarkJsWeb3Provider) { throw new Error("web3 cannot be found. Please ensure you have the 'embarkjs-connector-web3' plugin installed in your DApp."); }`; 89 code += `\n if (global.embarkjsOmg) EmbarkJS.Plasma.init(embarkJsWeb3Provider.web3, true).catch((err) => console.error(err));`; 90 code += "\n});"; 91 92 this.embark.addCodeToEmbarkJS(code); 93 }); 94 } 95 96 registerConsoleCommands() { 97 this.embark.registerConsoleCommand({ 98 description: `Initialises the Plasma chain using the account configured in the DApp's blockchain configuration. All transactions on the child chain will use this as the 'from' account.`, 99 matches: ["plasma init", "plasma init --force"], 100 usage: "plasma init [--force]", 101 process: (cmd, callback) => { 102 const force = cmd.endsWith("--force"); 103 if (this.inited && !force) { 104 const message = "The Plasma chain is already initialized. If you'd like to reinitialize the chain, use the --force option ('plasma init --force')."; 105 this.logger.error(message); 106 return callback(message); // passes a message back to cockpit console 107 } 108 this.init() 109 .then(message => { 110 this.logger.info(message); 111 callback(null, message); 112 }) 113 .catch(e => { 114 this.logger.error(e.message); 115 callback(e.message); 116 }); 117 } 118 }); 119 120 const depositRegex = /^plasma[\s]+deposit[\s]+([0-9]+)$/; 121 this.embark.registerConsoleCommand({ 122 description: "Deposits ETH (or ERC20) from the root chain to the Plasma child chain to be used for transacting on the Plasma chain.", 123 matches: cmd => { 124 return depositRegex.test(cmd); 125 }, 126 usage: "plasma deposit [amount]", 127 process: (cmd, callback) => { 128 if (!this.inited) { 129 return callback("The Plasma chain has not been initialized. Please initialize the Plamsa chain using 'plasma init' before continuting."); // passes a message back to cockpit console 130 } 131 const matches = cmd.match(depositRegex) || []; 132 if (matches.length <= 1) { 133 return callback("Invalid command format, please use the format 'plasma deposit [amount]', ie 'plasma deposit 100000'"); 134 } 135 this.deposit(matches[1]) 136 .then(message => { 137 this.logger.info(message); 138 callback(null, message); 139 }) 140 .catch(e => { 141 this.logger.error(e.message); 142 callback(e.message); 143 }); 144 } 145 }); 146 147 const sendRegex = /^plasma[\s]+transfer[\s]+(0x[0-9,a-f,A-F]{40,40})[\s]+([0-9]+)$/; 148 this.embark.registerConsoleCommand({ 149 description: "Sends an ETH tx on the Plasma chain from the account configured in the DApp's blockchain configuration to any other account on the Plasma chain.", 150 matches: cmd => { 151 return sendRegex.test(cmd); 152 }, 153 usage: "plasma transfer [to_address] [amount]", 154 process: (cmd, callback) => { 155 if (!this.inited) { 156 return callback("The Plasma chain has not been initialized. Please initialize the Plamsa chain using 'plasma init' before continuting."); // passes a message back to cockpit console 157 } 158 const matches = cmd.match(sendRegex) || []; 159 if (matches.length <= 2) { 160 return callback("Invalid command format, please use the format 'plasma transfer [to_address] [amount]', ie 'plasma transfer 0x38d5beb778b6e62d82e3ba4633e08987e6d0f990 555'"); 161 } 162 this.transfer(matches[1], matches[2]) 163 .then(message => { 164 this.logger.info(message); 165 callback(null, message); 166 }) 167 .catch(e => { 168 this.logger.error(e.message); 169 callback(e.message); 170 }); 171 } 172 }); 173 174 const exitRegex = /^plasma[\s]+exit[\s]+(0x[0-9,a-f,A-F]{40,40})$/; 175 this.embark.registerConsoleCommand({ 176 description: "Exits all UTXO's from the Plasma chain to the root chain.", 177 matches: cmd => { 178 return exitRegex.test(cmd); 179 }, 180 usage: "plasma exit [plasma_chain_address]", 181 process: (cmd, callback) => { 182 if (!this.inited) { 183 const message = "The Plasma chain has not been initialized. Please initialize the Plamsa chain using 'plasma init' before continuting."; 184 this.logger.error(message); 185 return callback(message); // passes a message back to cockpit console 186 } 187 const matches = cmd.match(exitRegex) || []; 188 if (matches.length <= 1) { 189 const message = "Invalid command format, please use the format 'plasma exit [plasma_chain_address]', ie 'plasma exit 0x38d5beb778b6e62d82e3ba4633e08987e6d0f990'"; 190 this.logger.error(message); 191 return callback(message); 192 } 193 this.exitAllUtxos(matches[1]) 194 .then(message => { 195 this.logger.info(message); 196 callback(null, message); 197 }) 198 .catch(e => { 199 this.logger.error(e.message); 200 callback(e.message); 201 }); 202 } 203 }); 204 205 this.embark.registerConsoleCommand({ 206 description: "Gets the status of the Plasma chain.", 207 matches: ["plasma status"], 208 process: (cmd, callback) => { 209 this.childChain.status() 210 .then(status => { 211 this.logger.info(status); 212 callback(null, status); 213 }) 214 .catch(e => { 215 this.logger.error(e.message); 216 callback(e.message); 217 }); 218 } 219 }); 220 } 221 222 /** 223 * Registers this plugin for Embark service checks and sets up log messages for 224 * connection and disconnection events. The service check pings the Status app. 225 * 226 * @returns {void} 227 */ 228 registerServiceCheck() { 229 const name = "OMG Plasma Chain"; 230 231 this.events.request( 232 "services:register", 233 name, 234 cb => { 235 if (!this.inited) { 236 return cb({name: "Loading...", status: SERVICE_CHECK_OFF}); 237 } 238 this.childChain.status() 239 .then(status => { 240 const serviceStatus = `Last block: ${formatDate(status.last_mined_child_block_timestamp)}`; 241 return cb({ 242 name: serviceStatus, 243 status: status ? SERVICE_CHECK_ON : SERVICE_CHECK_OFF 244 }); 245 }) 246 .catch(err => { 247 return cb(err); 248 }); 249 }, 250 5000, 251 "off" 252 ); 253 254 this.events.on("check:backOnline:OmiseGO", () => { 255 this.logger.info("------------------"); 256 this.logger.info("Connected to the Plama chain!"); 257 this.logger.info("------------------"); 258 }); 259 260 this.events.on("check:wentOffline:OmiseGO", () => { 261 this.logger.error("------------------"); 262 this.logger.error("Couldn't connect or lost connection to the Plasma chain..."); 263 this.logger.error("------------------"); 264 }); 265 } 266 } 267 268 export default EmbarkPlasma;