Coygo: A cryptocurrency trading app built with Meteor

Hey everyone,

I thought you all might be interested in an app that I’ve built with Meteor called Coygo, we’ve just launched an open beta this week after being in a closed invite-only beta for about two years. I quit my job to focus on Coygo full-time this past January after working a corporate job at a fintech company for just over four years, it’s been a wild ride. Our website is www.coygo.app.

The product
Coygo is a tool for cryptocurrency and digital asset traders to manage their accounts on various exchanges, track their portfolio, submit trades, find arbitrage opportunities, view real-time insights, transfer between accounts, and a ton of other things.

We went with the approach of a desktop application because we use our users’ API keys to interact with their financial accounts. Since it’s on the desktop, we only store their API keys encrypted on their own hard drives. Our servers never have access to our users’ exchange accounts or funds. Being a desktop application also lets us offload most real-time calculations to our users’ machines, so the workload of our servers is very light.

The tech stack
Our marketing website (www.coygo.app) is built with Webflow, a really great tool for generating static website. Our blog is published on Medium but then converted to static HTML pages and hosted on our own domain with Hugo.

Our main webapp (app.coygo.app) and our downloadable desktop application are built w/Meteor, Vue + Vuex for the front end, iView as a front end component framework, and meteor-desktop to bundle it as an Electron app, with a few AWS lambdas here and there to do minor one off tasks. We don’t really use Tracker at all, all reactivity is done w/Vuex. It’s forked off of my Meteor Vue Enterprise Starter repo, the structure is still the same but with a ton of modifications for Electron and the various things involved with running a desktop app.

I have run into a lot of very interesting challenges along the way, including a number of various tricks for desktop performance in Electron (we do a TON of real-time data processing with rather large data sets), replicating the ValidatedMethod model for desktop IPC methods, and plenty of other things.

If you’ve got any questions about development, the company, or really anything else I’d be happy to chat.

18 Likes

you are limiting password length to 16 characters … nobody today does this especially for anything money related … I realize a randomized 16 chars pwd gives at least 62 ^ 16 possibilities however crypto sites like coinbase.com allow 72 characters … you might put off folks when they encounter this 16 char limit … see https://support.coinbase.com/customer/en/portal/articles/2545777-password-requirements

PS your linux app runs like a charm !

3 Likes

First of all, congratulations! Coygo looks awesome.

My team and I are developing an app that needs desktop native functionality for TCP communications. We had some trouble building a production version of the app with meteor-desktop but got it working in development mode. We’d appreciate it a lot if you have a couple of minutes to chat about it.

You can reach me via gunnartorfis@noona.is, I can invite you to a Slack channel or reach you in whatever way you’d prefer.

Thanks for pointing this out, I’ll go ahead and get it fixed within the next couple of releases :slightly_smiling_face: I’m glad it works well on your machine! Ensuring performance on each OS has been a real task. Out of curiosity, which Linux distro are you using?

Very cool!

Nerd question: Is your landing page rendered with Meteor, or is it only once you sign in that you’re using Meteor? If not, what are you using to render the static part of your site? It loads pretty much immediately for me.

Our marketing website (www.coygo.app) is built with Webflow, a great GUI editor for static sites. I really recommend it, especially if you want your designer to be able to modify your website without changing any code. That is hosted on Firebase, which is good for static hosting and has a fairly generous free tier that we just now are reaching the limit of. Our main webapp (app.coygo.app) where you can sign up and sign in, manage your account and subscription, and download the application itself is built with Meteor.

What kind of issues have you run into?

To provide helpful info for others in the community, first I will detail what I’ve done for communication in our app here. If this isn’t enough info then I can follow up in Slack or a DM as well, but I’d like to keep it public if we can so that others can benefit from the info as there isn’t much documentation out there for how to run a production app using meteor-desktop and Electron and we had to pave the way for a lot of this.

Here is our internal architecture diagram, with any proprietary information removed and a couple AWS lambdas not included as they’re not part of the main application.

So our desktop app client has a few forms of communication:

  • Between desktop app client and server
    • websockets using Meteor’s RPC system (ValidatedMethod)
    • A couple HTTP REST endpoints
  • Between desktop app client (renderer process) and desktop app main process
    • IPC, Electron’s standard communication protocol
    • websockets, we spin up a websocket server in a child process and use this for real-time data to avoid blocking the main processes’ event loop and keep IPC messages snappy

There were a few small caveats, like importing modules in the child process websocket server since it is spawned outside the Electron context so we have to monkey patch the require() method to look in the correct node_modules/ directory for the Electron app, but that’s the main idea. We also have some node modules that are supposed to be built for each OS separately since they are built with C++, but instead we just include all three binaries (macOS, Windows and Linux) in the build and dynamically choose which to binary to import at runtime, that ended up being a lot easier for us.

1 Like

Aha, great, thanks for the info! Much appreciated.

2 Likes

Thanks for the super quick reply!
My first confusion was that I didn’t know what ddp_url was when using the meteor-desktop CLI. I ended up using the URL of our Meteor production web app. Please correct me if that’s wrong.

The second issue I had was with build-installer. I always got the error _this.packager is not a function and I posted the issue on Github. As you know, the repo isn’t that active so I’m not expecting an answer there.

I was also not sure how desktop apps built with meteor-desktop are signed/provisioned for App Store or register it for Windows so our users don’t get strange warnings about the app. That’s not a requirement for us but would be optimal.

The final issue I had was that no localhost TCP communication worked in the production build, not on macOS nor Windows.

We’re using the TCP to communicate with a terminal (card reader, pos) and send amounts to it. It’s unfortunately not possible to do such connections on the browser. It works fine in development mode but once we run it as an .exe/.app, it doesn’t work. It’s kind of hard to debug because I didn’t find any logs. I could send the error outputs to our web service to diagnose this further.

We produce four versions of builds: local dev, local mimicking prod, preprod and prod. Our preprod builds point directly to our hosted preprod environment, and the prod points to our hosted prod environment. So we first deploy our server so that the environment gets the updated client code, then we build and publish our desktop app.

The relevant section of our package.json looks like this, we run the appropriate commands in our CI environment or on our local machines:

    "start:web": "cross-env NO_HMR=1 APP_NAME=coygo-webapp  meteor run --mobile-server=127.0.0.1:3000 --settings config/config.development.json --raw-logs  | ./node_modules/.bin/bunyan -o short",
    "start:desktop": "meteor npm run desktop -- run 127.0.0.1:3000",
    "start:web-mimic-prod": "APP_NAME=coygo-webapp meteor run --mobile-server=127.0.0.1:3000 --settings config/config.prd.json --production --raw-logs | ./node_modules/.bin/bunyan -o short",
    "build:desktop-local": "cross-env GH_TOKEN=0 meteor npm run desktop -- build-installer 127.0.0.1:3000 --prod-debug",
    "build:desktop-preprod-mac": "meteor npm run desktop -- build-installer <our preprod app url> --mac --production",
    "build:desktop-preprod-win-linux": "meteor npm run desktop -- build-installer <our preprod app url> --win --linux  --production",
    "build:desktop-prod-mac": "meteor npm run desktop -- build-installer <our prod app url> --mac  --production",
    "build:desktop-prod-win-linux": "meteor npm run desktop -- build-installer <our prod app url> --win --linux  --production",

local dev build

When we build locally for dev we first run meteor npm run start:web to spin up the dev web server then in another terminal we run meteor npm run start:desktop to build and run the local dev build.


local build that mimicks prod build

Sometimes I want to produce a local build that mimics a prod build. This is because there are a few slight differences between local dev build and prod build with meteor-desktop, and I want to catch any issues before pushing up, waiting for preprod build to finish, and realizing the bug after we’ve produced a preprod build which can take awhile. For that I first run meteor npm run start:web-mimic-prod, wait for that to finish, then in another terminal I run meteor npm run build:deskop-local.


I hope that helps! meteor-desktop is awesome but is definitely a beast to work with. And as you’ve mentioned it’s not been maintained lately, it’s 5 or 6 versions of Electron behind now. I really wish that this was treated as a first-class citizen in the Meteor ecosystem because it is the perfect way to add a build target for desktop apps. I may have to dive in and update it to a newer Electron myself as we really rely on it for everything, but I just don’t have the time right now as I’m lead applications engineer and I also do most of our marketing efforts, and meteor-desktop is built with a lot of hacks.


Tiny, I’m looking at you! Help support meteor-desktop!

It provides a great value proposition to easily build desktop applications with Meteor and the various web technologies available. Our company wouldn’t exist without it.

2 Likes

Latest release includes up to 64 character passwords. Thanks again for bringing this to my attention :slightly_smiling_face:

Thank you so much for the reply, you’ve been incredibly helpful.

Have you enabled copy & paste using this method? https://pracucci.com/atom-electron-enable-copy-and-paste.html

I’m not sure where to put this :sweat_smile:

Yup we’re using that same idea. in our deskop.js file there is a createMenu() method on the export default class Desktop { } class

 createMenu () {
    const platform = os.platform();
    const application = {
      label: 'Application',
      submenu: []
    };
    if (platform === 'darwin') {
      // on OSX add 'About application' button
      application.submenu.push({
        label: 'About Application',
        selector: 'orderFrontStandardAboutPanel:'
      });
      application.submenu.push({
        type: 'separator'
      });
    }
    // add 'Quit' command on every OS
    application.submenu.push({
      label: 'Quit',
      accelerator: 'Command+Q',
      click: () => {
        app.quit()
      }
    });

    const edit = {
      label: 'Edit',
      submenu: [
        {
          label: 'Undo',
          accelerator: 'CmdOrCtrl+Z',
          selector: 'undo:'
        },
        {
          label: 'Redo',
          accelerator: 'Shift+CmdOrCtrl+Z',
          selector: 'redo:'
        },
        {
          type: 'separator'
        },
        {
          label: 'Cut',
          accelerator: 'CmdOrCtrl+X',
          selector: 'cut:'
        },
        {
          label: 'Copy',
          accelerator: 'CmdOrCtrl+C',
          selector: 'copy:'
        },
        {
          label: 'Paste',
          accelerator: 'CmdOrCtrl+V',
          selector: 'paste:'
        },
        {
          label: 'Select All',
          accelerator: 'CmdOrCtrl+A',
          selector: 'selectAll:'
        }
      ]
    };

    const template = [
      application,
      edit
    ];

    Menu.setApplicationMenu(Menu.buildFromTemplate(template));
  }

then in the constructor of that same class we hook it up once the window has been created

constructor() {
   eventsBus.on('windowCreated', (window) => {
      this.mainWindow = window;
      window.webContents.on('crashed', Desktop.windowCrashedHandler);
      window.on('unresponsive', Desktop.windowUnresponsiveHandler);

      // Register menu manually
      this.createMenu();
    });
}

This gets copy/paste working w/keyboard or top application menu on every OS. Hopefully our pain of working with this can save you some :grinning: if I had the time I would love to open source a barebones version of our app as a reference for a fully working, production-ready meteor-desktop implementation since it’s a ton of setup and it took us about a year to get every little piece ready for production, but that would be a ton of work and I don’t have the bandwidth unfortunately

2 Likes

Thank you so much :smiley:
Your pain indeed saved me a lot of time!

The only limitation I find now is that the native TCP connections don’t seem to work at all. It works perfectly when running a local build but it doesn’t work at all in a production build. Here’s an issue I created on the Github repo: https://github.com/wojtkowiak/meteor-desktop/issues/251

Do you do freelance work for a few hours? We’d love to hire you to help us make those TCP connections work.

We use IPC and a local websocket server, I’ve never tried TCP so I can’t speak for it in meteor-desktop. Although I don’t see why something like fetch('http://localhost:3000') shouldn’t work. I’m not available for freelance sorry :slight_smile:

1 Like

Yes, I agree. It’s strange that it doesn’t work. It’s not HTTP though, it’s a “native” socket connection.
But thank you so much again for the replies. I’ll reply here if I ever find a solution.