/ src / index.js
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;