├── frontend
├── .meteor
│ ├── release
│ ├── .gitignore
│ ├── platforms
│ ├── .id
│ ├── .finished-upgraders
│ ├── packages
│ └── versions
├── public
│ ├── fonts
│ │ ├── Montserrat-Medium.woff
│ │ ├── Montserrat-Medium.woff2
│ │ ├── Montserrat-SemiBold.woff
│ │ └── Montserrat-SemiBold.woff2
│ ├── favicon.ico
│ ├── favicon-128.png
│ ├── logo-oasis.png
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── favicon-96x96.png
│ ├── mstile-144x144.png
│ ├── mstile-150x150.png
│ ├── mstile-310x150.png
│ ├── mstile-310x310.png
│ ├── mstile-70x70.png
│ ├── favicon-196x196.png
│ ├── logo-oasis-hover.png
│ ├── apple-touch-icon-114x114.png
│ ├── apple-touch-icon-120x120.png
│ ├── apple-touch-icon-144x144.png
│ ├── apple-touch-icon-152x152.png
│ ├── apple-touch-icon-57x57.png
│ ├── apple-touch-icon-60x60.png
│ ├── apple-touch-icon-72x72.png
│ ├── apple-touch-icon-76x76.png
│ ├── ic_remove_24px.svg
│ ├── ic_add_circle_24px.svg
│ ├── myallowance-maximum.svg
│ ├── myallowance-personal.svg
│ ├── order-warning.svg
│ ├── order-warning-red.svg
│ ├── loading.svg
│ ├── loadingLarge.svg
│ ├── remove_button.svg
│ ├── close_x.svg
│ ├── cross_normal.svg
│ ├── cross_pressed.svg
│ ├── dot_red.svg
│ ├── counter_3.svg
│ ├── counter_1.svg
│ ├── counter_2.svg
│ ├── ic_compare_arrows_black_24px.svg
│ ├── clock.svg
│ ├── eth_circle_icon.svg
│ ├── eth_circle_icon_full.svg
│ ├── maker_circle_icon.svg
│ ├── maker_circle_icon_full.svg
│ ├── ethereum-logo.svg
│ └── dapphub_icn_metamask.svg
├── client
│ ├── main.js
│ ├── templates
│ │ └── head.html
│ └── style
│ │ └── index.css
├── imports
│ ├── ui
│ │ └── client
│ │ │ ├── widgets
│ │ │ ├── lasttrades.js
│ │ │ ├── progressblock.js
│ │ │ ├── maintransfer.js
│ │ │ ├── transferconfirmation.js
│ │ │ ├── myorders.js
│ │ │ ├── progress-bar.html
│ │ │ ├── progress-bar.js
│ │ │ ├── cancelmodal.js
│ │ │ ├── maintransfer.html
│ │ │ ├── lasttrades.html
│ │ │ ├── progressblock.html
│ │ │ ├── maindeposit.js
│ │ │ ├── maindeposit.html
│ │ │ ├── orderbook.html
│ │ │ ├── depositbalance.js
│ │ │ ├── orders.js
│ │ │ ├── orders.html
│ │ │ ├── orderrow.js
│ │ │ ├── depositbalance.html
│ │ │ ├── myorders.html
│ │ │ ├── orderrow.html
│ │ │ ├── history.js
│ │ │ ├── redeemer-modal.js
│ │ │ ├── transferconfirmation.html
│ │ │ ├── redeemer-modal.html
│ │ │ ├── wrapper-update.html
│ │ │ ├── cancelmodal.html
│ │ │ ├── chart.html
│ │ │ ├── maintrades.html
│ │ │ ├── newallowance.js
│ │ │ ├── markets.html
│ │ │ ├── gnttokens.html
│ │ │ ├── wrapper-update.js
│ │ │ ├── ethtokens.html
│ │ │ ├── sendtokens.html
│ │ │ ├── sendtokens.js
│ │ │ ├── history.html
│ │ │ ├── ethtokens.test.js
│ │ │ ├── newallowance.html
│ │ │ ├── neworder.html
│ │ │ ├── ethtokens.js
│ │ │ └── gnttokens.js
│ │ │ ├── headers
│ │ │ ├── volumes.html
│ │ │ ├── balance.js
│ │ │ ├── messages.js
│ │ │ ├── balance.html
│ │ │ ├── tabs.js
│ │ │ ├── marketdetails.html
│ │ │ ├── tabs.html
│ │ │ ├── accountselector.js
│ │ │ ├── accountselector.html
│ │ │ ├── currencyselector.html
│ │ │ ├── messages.html
│ │ │ ├── networkstatus.html
│ │ │ └── currencyselector.js
│ │ │ ├── whatisthis.html
│ │ │ ├── shared.js
│ │ │ ├── noaccount.js
│ │ │ ├── noaccount.html
│ │ │ ├── index.html
│ │ │ ├── footer.html
│ │ │ └── noethereum.html
│ ├── test
│ │ └── dapple-token-spy.js
│ ├── utils
│ │ ├── conversion.js
│ │ └── functions.js
│ ├── api
│ │ ├── limits.js
│ │ ├── transactions.js
│ │ ├── weth.js
│ │ ├── tokens.js
│ │ └── wgnt.js
│ └── startup
│ │ └── client
│ │ └── index.js
├── .eslintignore
├── packages
│ └── dapple
│ │ ├── package.js
│ │ ├── package-pre-init.js
│ │ ├── contracts-abi
│ │ ├── maker-otc.js
│ │ └── simple-market.js
│ │ ├── config.json
│ │ └── package-post-init.js
└── package.json
├── .gitignore
├── .editorconfig
├── package.json
├── gulpfile.js
└── README.md
/frontend/.meteor/release:
--------------------------------------------------------------------------------
1 | METEOR@1.4.2.3
2 |
--------------------------------------------------------------------------------
/frontend/public/fonts/Montserrat-Medium.woff:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/.meteor/.gitignore:
--------------------------------------------------------------------------------
1 | dev_bundle
2 | local
3 |
--------------------------------------------------------------------------------
/frontend/.meteor/platforms:
--------------------------------------------------------------------------------
1 | server
2 | browser
3 |
--------------------------------------------------------------------------------
/frontend/client/main.js:
--------------------------------------------------------------------------------
1 | import '/imports/startup/client';
2 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/lasttrades.js:
--------------------------------------------------------------------------------
1 | import './lasttrades.html';
2 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/headers/volumes.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/progressblock.js:
--------------------------------------------------------------------------------
1 | import './progressblock.html';
2 |
--------------------------------------------------------------------------------
/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/frontend/public/favicon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/favicon-128.png
--------------------------------------------------------------------------------
/frontend/public/logo-oasis.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/logo-oasis.png
--------------------------------------------------------------------------------
/frontend/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/favicon-16x16.png
--------------------------------------------------------------------------------
/frontend/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/favicon-32x32.png
--------------------------------------------------------------------------------
/frontend/public/favicon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/favicon-96x96.png
--------------------------------------------------------------------------------
/frontend/public/mstile-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/mstile-144x144.png
--------------------------------------------------------------------------------
/frontend/public/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/mstile-150x150.png
--------------------------------------------------------------------------------
/frontend/public/mstile-310x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/mstile-310x150.png
--------------------------------------------------------------------------------
/frontend/public/mstile-310x310.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/mstile-310x310.png
--------------------------------------------------------------------------------
/frontend/public/mstile-70x70.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/mstile-70x70.png
--------------------------------------------------------------------------------
/frontend/public/favicon-196x196.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/favicon-196x196.png
--------------------------------------------------------------------------------
/frontend/public/logo-oasis-hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/logo-oasis-hover.png
--------------------------------------------------------------------------------
/frontend/public/apple-touch-icon-114x114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/apple-touch-icon-114x114.png
--------------------------------------------------------------------------------
/frontend/public/apple-touch-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/apple-touch-icon-120x120.png
--------------------------------------------------------------------------------
/frontend/public/apple-touch-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/apple-touch-icon-144x144.png
--------------------------------------------------------------------------------
/frontend/public/apple-touch-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/apple-touch-icon-152x152.png
--------------------------------------------------------------------------------
/frontend/public/apple-touch-icon-57x57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/apple-touch-icon-57x57.png
--------------------------------------------------------------------------------
/frontend/public/apple-touch-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/apple-touch-icon-60x60.png
--------------------------------------------------------------------------------
/frontend/public/apple-touch-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/apple-touch-icon-72x72.png
--------------------------------------------------------------------------------
/frontend/public/apple-touch-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/apple-touch-icon-76x76.png
--------------------------------------------------------------------------------
/frontend/public/fonts/Montserrat-Medium.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/fonts/Montserrat-Medium.woff2
--------------------------------------------------------------------------------
/frontend/public/fonts/Montserrat-SemiBold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/fonts/Montserrat-SemiBold.woff
--------------------------------------------------------------------------------
/frontend/public/fonts/Montserrat-SemiBold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OasisDEX/oasis/HEAD/frontend/public/fonts/Montserrat-SemiBold.woff2
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.sw*
2 | .DS_Store
3 | .publish/
4 | .vscode/
5 | .idea
6 | *.iml
7 | scripts/blockchain/tmp
8 | dist/
9 | node_modules/
10 | npm-debug.log
11 | nohup.out
12 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/whatisthis.html:
--------------------------------------------------------------------------------
1 |
2 | ?
3 |
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | indent_style = space
8 | indent_size = 2
9 | end_of_line = lf
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/maintransfer.js:
--------------------------------------------------------------------------------
1 | import { Template } from 'meteor/templating';
2 | import './maintransfer.html';
3 | import { doTabShow } from '../../../utils/functions.js';
4 |
5 | Template.maintransfer.onRendered(() => {
6 | doTabShow();
7 | });
8 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/shared.js:
--------------------------------------------------------------------------------
1 | import { ViewModel } from 'meteor/manuel:viewmodel';
2 |
3 | ViewModel.share({
4 | newOffer: {
5 | offerAmount: 0,
6 | offerPrice: 0,
7 | offerTotal: 0,
8 | offerType: '',
9 | offerError: '',
10 | },
11 | });
12 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/transferconfirmation.js:
--------------------------------------------------------------------------------
1 | import { Template } from 'meteor/templating';
2 |
3 | import './transferconfirmation.html';
4 |
5 | Template.transferconfirmation.viewmodel({
6 | confirm() {
7 | $('#transferconfirmation').trigger('transfer:confirmed');
8 | },
9 | });
10 |
11 |
--------------------------------------------------------------------------------
/frontend/.eslintignore:
--------------------------------------------------------------------------------
1 | packages/dapple/package.js
2 | packages/dapple/package-pre-init.js
3 | packages/dapple/package-post-init.js
4 | packages/dapple/maker.js
5 | packages/dapple/contracts-abi/maker-otc.js
6 | packages/dapple/contracts-abi/token-wrapper.js
7 | packages/dapple/contracts-abi/ds-eth-token.js
8 | imports/utils/Chart.min.js
9 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/myorders.js:
--------------------------------------------------------------------------------
1 | import { Template } from 'meteor/templating';
2 | import { Status } from '/imports/api/offers';
3 |
4 | import './myorders.html';
5 |
6 | Template.myorders.viewmodel({
7 | status: Status,
8 | orderStatus: [Status.OPEN, Status.CLOSED],
9 | filterByStatus: Status.OPEN,
10 | });
11 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/progress-bar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{#if message }}
6 |
7 | {{message}}
8 |
9 | {{/if}}
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/frontend/public/ic_remove_24px.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/progress-bar.js:
--------------------------------------------------------------------------------
1 | import { Template } from 'meteor/templating';
2 |
3 | import './progress-bar.html';
4 |
5 | Template.progressbar.viewmodel({
6 | autorun() {
7 | const newWidth = (this.value() / this.max()) * 100;
8 | this.templateInstance.$('.dex-progress').width(`${newWidth}%`);
9 | },
10 | });
11 |
--------------------------------------------------------------------------------
/frontend/.meteor/.id:
--------------------------------------------------------------------------------
1 | # This file contains a token that is unique to your project.
2 | # Check it into your repository along with the rest of this directory.
3 | # It can be used for purposes such as:
4 | # - ensuring you don't accidentally deploy one app on top of another
5 | # - providing package authors with aggregated statistics
6 |
7 | 133f4p51avjp8h1qfjf57
8 |
--------------------------------------------------------------------------------
/frontend/public/ic_add_circle_24px.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/cancelmodal.js:
--------------------------------------------------------------------------------
1 | import { Template } from 'meteor/templating';
2 |
3 | import { Offers } from '/imports/api/offers';
4 |
5 | import './cancelmodal.html';
6 |
7 |
8 | Template.cancelmodal.viewmodel({
9 | cancel() {
10 | const offerId = this.templateInstance.data.offer._id;
11 | Offers.cancelOffer(offerId);
12 | },
13 | });
14 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/headers/balance.js:
--------------------------------------------------------------------------------
1 | import { Template } from 'meteor/templating';
2 |
3 | import './balance.html';
4 |
5 | Template.balance.events({
6 | 'click button.btn-change-allowance': (event, templateInstance) => {
7 | const token = templateInstance.data.currency;
8 | $(`#allowanceModal${token}`).data('refer', '');
9 | $(`#allowanceModal${token}`).modal('show');
10 | },
11 | });
12 |
13 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/headers/messages.js:
--------------------------------------------------------------------------------
1 | import { Template } from 'meteor/templating';
2 | // import { Session } from 'meteor/session';
3 |
4 | import './messages.html';
5 |
6 | Template.messages.viewmodel({
7 | warningOpen: true,
8 | updateOpen: true,
9 | closeMessage(type) {
10 | if (type === 'update') {
11 | this.updateOpen(false);
12 | } else if (type === 'warning') {
13 | this.warningOpen(false);
14 | }
15 | },
16 | });
17 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/maintransfer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 | {{> history historyType='transferHistory'}}
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/frontend/public/myallowance-maximum.svg:
--------------------------------------------------------------------------------
1 | Created with Sketch.
2 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/noaccount.js:
--------------------------------------------------------------------------------
1 | import { Template } from 'meteor/templating';
2 | import { web3Obj } from 'meteor/makerotc:dapple';
3 |
4 | import './noaccount.html';
5 |
6 | Template.noAccount.helpers({
7 | metamask: function metamask() {
8 | return web3Obj &&
9 | web3Obj.currentProvider &&
10 | (
11 | web3Obj.currentProvider.isMetaMask ||
12 | web3Obj.currentProvider.constructor.name === 'MetamaskInpageProvider'
13 | );
14 | },
15 | });
16 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/lasttrades.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | MARKET HISTORY
5 |
6 |
7 | {{#if loadingTradeHistory}}
8 |
{{{loadingIcon}}} Loading history...
9 | {{/if}}
10 | {{> orders orders=lastTrades type='lastTrades' priceLabel='PRICE' showCancel=true showType=true showDate=true canOpenTxLink=true}}
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/frontend/public/myallowance-personal.svg:
--------------------------------------------------------------------------------
1 | Created with Sketch.
2 |
--------------------------------------------------------------------------------
/frontend/.meteor/.finished-upgraders:
--------------------------------------------------------------------------------
1 | # This file contains information which helps Meteor properly upgrade your
2 | # app when you run 'meteor update'. You should check it into version control
3 | # with your project.
4 |
5 | notices-for-0.9.0
6 | notices-for-0.9.1
7 | 0.9.4-platform-file
8 | notices-for-facebook-graph-api-2
9 | 1.2.0-standard-minifiers-package
10 | 1.2.0-meteor-platform-split
11 | 1.2.0-cordova-changes
12 | 1.2.0-breaking-changes
13 | 1.3.0-split-minifiers-package
14 | 1.4.0-remove-old-dev-bundle-link
15 | 1.4.1-add-shell-server-package
16 |
--------------------------------------------------------------------------------
/frontend/public/order-warning.svg:
--------------------------------------------------------------------------------
1 | Created with Sketch.
--------------------------------------------------------------------------------
/frontend/imports/ui/client/headers/balance.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{#let token=(findToken currency)}}
4 |
{{formatBalance token.balance}} {{currency}}
5 |
6 | ALLOWANCE
7 |
8 | {{formatBalance token.allowance}} {{currency}}
9 |
10 |
11 |
change allowance
12 | {{/let}}
13 |
14 |
15 |
--------------------------------------------------------------------------------
/frontend/public/order-warning-red.svg:
--------------------------------------------------------------------------------
1 | Created with Sketch.
2 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/headers/tabs.js:
--------------------------------------------------------------------------------
1 | import { Template } from 'meteor/templating';
2 | import { Session } from 'meteor/session';
3 | import { doHashChange } from '/imports/utils/functions';
4 |
5 | import './tabs.html';
6 |
7 | Template.tabs.viewmodel({
8 | currentTab: '',
9 | changeTab(e) {
10 | const tab = e.target.hash;
11 | if (tab !== this.currentTab) {
12 | this.currentTab = tab;
13 | if (tab !== '#trade') {
14 | location.hash = tab;
15 | } else {
16 | location.hash = `#trade/${Session.get('baseCurrency')}/${Session.get('quoteCurrency')}`;
17 | }
18 | doHashChange();
19 | }
20 | },
21 | });
22 |
--------------------------------------------------------------------------------
/frontend/public/loading.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/public/loadingLarge.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/progressblock.html:
--------------------------------------------------------------------------------
1 |
2 | {{#if progress}}
3 |
6 | {{/if}}
7 | {{#if progressMessage}}
8 |
9 | {{{progressMessage}}}
10 |
11 | {{/if}}
12 | {{#if errorMessage}}
13 |
14 | {{{errorMessage}}}
15 |
16 | {{/if}}
17 |
18 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/headers/marketdetails.html:
--------------------------------------------------------------------------------
1 |
2 | {{#if contractExists}}
3 | {{#if isMarketOpen}}
4 |
5 | Market open until {{timestampToString marketCloseTime true true}}
6 | {{> whatisthis section="expiring-market"}}
7 |
8 | {{else}}
9 |
10 | Market closed on {{timestampToString marketCloseTime true true}}.
11 | {{> whatisthis section="expiring-market"}}
12 | Please cancel your orders.
13 |
14 | {{/if}}
15 | {{else}}
16 |
17 | Could not find the contract on your current blockchain environment.
18 |
19 | {{/if}}
20 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/maindeposit.js:
--------------------------------------------------------------------------------
1 | import { Session } from 'meteor/session';
2 | import { Template } from 'meteor/templating';
3 | import './maindeposit.html';
4 | import { doTabShow } from '../../../utils/functions.js';
5 |
6 | Template.maindeposit.viewmodel({
7 | depositData() {
8 | return { title: 'WRAP', depositType: 'deposit' };
9 | },
10 | withdrawData() {
11 | return { title: 'UNWRAP', depositType: 'withdraw' };
12 | },
13 | tokenTemplate() {
14 | if (typeof Session.get('tokenTemplate') === 'undefined') {
15 | Session.set('tokenTemplate', 'ethtokens');
16 | }
17 | return Session.get('tokenTemplate');
18 | },
19 | });
20 |
21 | Template.maindeposit.onRendered(() => {
22 | doTabShow();
23 | });
24 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/headers/tabs.html:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/headers/accountselector.js:
--------------------------------------------------------------------------------
1 | import { Session } from 'meteor/session';
2 | import { Template } from 'meteor/templating';
3 | import { web3Obj } from 'meteor/makerotc:dapple';
4 |
5 | import Tokens from '/imports/api/tokens';
6 |
7 | import './accountselector.html';
8 |
9 | Template.accountSelector.helpers({
10 | accounts() {
11 | return Session.get('accounts');
12 | },
13 | currentAccount() {
14 | return Session.get('address');
15 | },
16 | });
17 |
18 | Template.accountSelector.events({
19 | change(event) {
20 | Session.set('address', event.target.value);
21 | localStorage.setItem('address', event.target.value);
22 | web3Obj.eth.defaultAccount = event.target.value;
23 | Tokens.sync();
24 | },
25 | });
26 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/maindeposit.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{> depositbalance title='BALANCES'}}
6 |
7 |
8 | {{> history historyType='depositHistory'}}
9 |
10 |
11 |
12 |
22 |
23 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/orderbook.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{title}}
5 |
6 | {{> whatisthis section="orderbook"}}
7 |
8 |
9 | {{#if equals priceClass 'bid'}}
10 | {{#if loadingBuyOrders}}
11 |
{{{loadingIcon}}} Loading buy orders...
12 | {{/if}}
13 | {{/if}}
14 |
15 | {{#if equals priceClass 'ask'}}
16 | {{#if loadingSellOrders}}
17 |
{{{loadingIcon}}} Loading sell orders...
18 | {{/if}}
19 | {{/if}}
20 | {{> orders orders=orders type=priceClass priceLabel=priceLabel priceClass=priceClass canAccept=(isMarketOpen) showCancel=(not isMarketOpen)}}
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/headers/accountselector.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Account:
5 |
6 |
7 | {{#each accounts}}
8 | {{this}}
9 | {{/each}}
10 |
11 |
12 |
13 |
14 | {{#if ready}}
15 |
16 | Market:
17 |
18 | {{contractAddress}}
19 | {{> whatisthis section="contract"}}
20 |
21 |
22 | {{/if}}
23 |
24 |
25 |
--------------------------------------------------------------------------------
/frontend/public/remove_button.svg:
--------------------------------------------------------------------------------
1 | Created with Sketch.
--------------------------------------------------------------------------------
/frontend/packages/dapple/package.js:
--------------------------------------------------------------------------------
1 | Package.describe({
2 | name: 'makerotc:dapple',
3 | version: '0.0.1',
4 | summary: 'Dapple related code for MakerOTC',
5 | git: '',
6 | documentation: 'README.md',
7 | });
8 |
9 | Package.onUse((api) => {
10 | api.versionsFrom('1.4.0.1');
11 |
12 | api.use('ecmascript', 'client');
13 | api.use('ethereum:web3', 'client');
14 |
15 | api.addFiles(['package-pre-init.js'], 'client');
16 | api.addFiles(['contracts-abi/maker-otc.js'], 'client');
17 | api.addFiles(['contracts-abi/ds-eth-token.js'], 'client');
18 | api.addFiles(['contracts-abi/token-wrapper.js'], 'client');
19 | api.addFiles(['contracts-abi/simple-market.js'], 'client');
20 | api.addFiles(['contracts-abi/expiring-market.js'], 'client');
21 | api.addFiles(['contracts-abi/matching-market.js'], 'client');
22 | api.addFiles(['package-post-init.js'], 'client');
23 |
24 | api.export('web3Obj', 'client');
25 | api.export('Dapple', 'client');
26 | });
27 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/noaccount.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{#if metamask}}
4 |
5 |
6 |
METAMASK ACCOUNT LOCKED
7 |
8 | You are trying to access OasisDEX without an unlocked account.
9 | Unlock your account on the Metamask Extension.
10 |
11 | {{else}}
12 |
13 |
14 |
NO ACCOUNT FOUND
15 |
You are trying to access OasisDEX without an Ethereum account loaded.
16 | Select an account to use in your Ethereum browser.
17 |
If you are using Mist, please click the CONNECT button in the top right corner and authorize an account.
18 | {{/if}}
19 |
20 |
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Maker-Market",
3 | "scripts": {
4 | "build": "gulp build",
5 | "test": "standard"
6 | },
7 | "devDependencies": {
8 | "babel-eslint": "6.0.0",
9 | "gulp": "^3.9.1",
10 | "gulp-gh-pages": "^0.5.4",
11 | "gulp-rename": "^1.2.2",
12 | "gulp-surge": "^0.1.0",
13 | "gulp-sync": "^0.1.4",
14 | "meteor-build-client": "^0.3.0",
15 | "standard": "6.0.7"
16 | },
17 | "standard": {
18 | "parser": "babel-eslint",
19 | "globals": [
20 | "Meteor",
21 | "Session",
22 | "localStorage",
23 | "Template",
24 | "EthTools",
25 | "Dapple",
26 | "BigNumber",
27 | "web3",
28 | "Spacebars",
29 | "Offers",
30 | "Trades",
31 | "Status",
32 | "PRICE_CURRENCY",
33 | "Tokens",
34 | "Transactions",
35 | "$",
36 | "_"
37 | ],
38 | "ignore": [
39 | "dapple_packages/**",
40 | "frontend/.meteor/**",
41 | "frontend/packages/**",
42 | "scripts/**"
43 | ]
44 | },
45 | "dependencies": {
46 | "surge": "^0.19.0"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/depositbalance.js:
--------------------------------------------------------------------------------
1 | import { Session } from 'meteor/session';
2 | import { Template } from 'meteor/templating';
3 |
4 | import Tokens from '/imports/api/tokens';
5 |
6 | import './depositbalance.html';
7 |
8 | Template.depositbalance.viewmodel({
9 | selectedToken: 'ETH',
10 | wethBalance() {
11 | try {
12 | const token = Tokens.findOne('W-ETH');
13 | return token.balance;
14 | } catch (e) {
15 | return '0';
16 | }
17 | },
18 | wgntBalance() {
19 | try {
20 | const token = Tokens.findOne('W-GNT');
21 | return token.balance;
22 | } catch (e) {
23 | return '0';
24 | }
25 | },
26 | selected(token) {
27 | return token === this.selectedToken() ? 'selected' : '';
28 | },
29 | baseChange(e) {
30 | if (e.currentTarget.id === 'eth-balance') {
31 | Session.set('tokenTemplate', 'ethtokens');
32 | this.selectedToken('ETH');
33 | } else if (e.currentTarget.id === 'gnt-balance') {
34 | Session.set('tokenTemplate', 'gnttokens');
35 | this.selectedToken('GNT');
36 | }
37 | },
38 | });
39 |
--------------------------------------------------------------------------------
/frontend/imports/test/dapple-token-spy.js:
--------------------------------------------------------------------------------
1 | export default class DappleTokenSpy {
2 | constructor(canLookup, canCall) {
3 | this.canLookup = canLookup;
4 | this.canCall = canCall;
5 | this.lastCall = {};
6 | }
7 | getToken(tokenName, tokenCb) {
8 | if (this.canLookup) {
9 | const token = {
10 | deposit: (options, cb) => {
11 | this.lastCall = {
12 | fn: 'deposit',
13 | options,
14 | };
15 | if (this.canCall) {
16 | cb(false, 'txid');
17 | } else {
18 | cb('token.deposit call error', null);
19 | }
20 | },
21 | withdraw: (amount, options, cb) => {
22 | this.lastCall = {
23 | fn: 'withdraw',
24 | amount,
25 | options,
26 | };
27 | if (this.canCall) {
28 | cb(false, 'txid');
29 | } else {
30 | cb('token.withdraw call error', null);
31 | }
32 | },
33 | };
34 | tokenCb(false, token);
35 | } else {
36 | tokenCb('token lookup error', null);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var exec = require('child_process').exec
2 |
3 | var gulp = require('gulp')
4 | var gulpsync = require('gulp-sync')(gulp)
5 | var ghPages = require('gulp-gh-pages')
6 | var surge = require('gulp-surge')
7 | var rename = require('gulp-rename');
8 |
9 | // meteor-build-client ../build
10 | gulp.task('build-meteor', function (cb) {
11 | exec('meteor-build-client ../dist --path ""', {cwd: 'frontend'}, function (err, res, failed) {
12 | if (err) {
13 | console.log(err)
14 | } else if (failed) {
15 | process.stdout.write(failed)
16 | } else {
17 | process.stdout.write('\u001b[32mMeteor build completed!\n')
18 | }
19 | cb(err)
20 | })
21 | })
22 |
23 | // gh-pages
24 | gulp.task('deploy-gh-pages', function () {
25 | require('fs').writeFileSync('./dist/CNAME', 'oasisdex.com');
26 | return gulp.src('./dist/**/*')
27 | .pipe(ghPages())
28 | })
29 |
30 | gulp.task('deploy-surge', [], function () {
31 | return surge({
32 | project: './dist', // Path to your static build directory
33 | domain: 'https://oasisdex.surge.sh' // Your domain or Surge subdomain
34 | })
35 | })
36 |
37 | gulp.task('deploy', gulpsync.sync(['build-meteor', 'deploy-gh-pages']))
38 |
--------------------------------------------------------------------------------
/frontend/public/close_x.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | finish_button
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/frontend/packages/dapple/package-pre-init.js:
--------------------------------------------------------------------------------
1 | // console.log('package-pre-init start')
2 | import { Session } from 'meteor/session';
3 |
4 | web3Obj = new Web3();
5 | Session.set('web3ObjReady', false);
6 | Session.set('web3Counter', 0);
7 |
8 | const web3Interval = setInterval(
9 | function() {
10 | if (window.web3) {
11 | web3Obj.setProvider(window.web3.currentProvider);
12 | console.log('Using current provider');
13 | initWeb3();
14 | } else {
15 | let counter = Session.get('web3Counter');
16 | counter++;
17 | Session.set('web3Counter', counter);
18 | if (counter >= 3) {
19 | web3Obj.setProvider(new Web3.providers.HttpProvider('http://localhost:8545'));
20 | console.log('Using new provider');
21 | initWeb3();
22 | }
23 | }
24 | }, 300
25 | );
26 |
27 | function initWeb3() {
28 | window.web3 = web3Obj;
29 | clearInterval(Session.get('web3Interval'));
30 | Session.delete('web3Interval');
31 | Session.set('web3ObjReady', true);
32 | }
33 |
34 | Session.set('web3Interval', web3Interval);
35 | // console.log('package-pre-init done')
36 |
37 | if (typeof module !== 'undefined' && module.exports) {
38 | module.exports = web3Obj;
39 | }
40 |
--------------------------------------------------------------------------------
/frontend/public/cross_normal.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | finish_button copy
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/frontend/public/cross_pressed.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | finish_button copy 2
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/frontend/public/dot_red.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Group 12 Copy
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/headers/currencyselector.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{#if showDropdownQuoteCurrencies}}
6 |
7 | {{/if}}
8 |
9 |
10 |
11 | {{#if showDropdownBaseCurrencies}}
12 |
13 | {{/if}}
14 |
15 |
16 | TOKEN PAIR
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/orders.js:
--------------------------------------------------------------------------------
1 | import { Blaze } from 'meteor/blaze';
2 | import { Template } from 'meteor/templating';
3 |
4 | import './orders.html';
5 | import './orderrow.js';
6 |
7 | Template.orders.helpers({
8 | /* eslint-disable no-underscore-dangle */
9 | /* replaced by scrolling orders
10 | moreBtn: function showMoreBtn() {
11 | const type = Template.instance().data.type;
12 | if (type && type === 'lastTrades') {
13 | const totalOrders = Blaze._globalHelpers.countLastTrades();
14 | return (this.orders.count() < totalOrders);
15 | }
16 | const totalOffers = Blaze._globalHelpers.countOffers(type);
17 | return (this.orders.count() < totalOffers);
18 | },
19 | */
20 | ordersCount: function ordersCount() {
21 | return Template.instance().data.orders.count();
22 | },
23 | section: function section() {
24 | return Template.instance().data.type;
25 | },
26 | orderCount: function countOffersOrders() {
27 | const type = Template.instance().data.type;
28 | if (type && type === 'lastTrades') {
29 | return parseInt(Blaze._globalHelpers.countLastTrades(), 10);
30 | }
31 | return parseInt(Blaze._globalHelpers.countOffers(this.priceClass), 10);
32 | },
33 | /* eslint-enable no-underscore-dangle */
34 | });
35 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/orders.html:
--------------------------------------------------------------------------------
1 |
2 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/frontend/imports/utils/conversion.js:
--------------------------------------------------------------------------------
1 | import { Dapple } from 'meteor/makerotc:dapple';
2 | import { BigNumber } from 'meteor/ethereum:web3';
3 |
4 | export function convertToTokenPrecision(amount, token) {
5 | if (typeof token !== 'undefined' && token !== '') {
6 | const tokenSpecs = Dapple.getTokenSpecs(token);
7 | if (tokenSpecs) {
8 | let value = amount;
9 | if (!(amount instanceof BigNumber)) {
10 | value = new BigNumber(amount);
11 | }
12 | return value.times(new BigNumber(10).pow(tokenSpecs.precision)).valueOf();
13 | }
14 | throw new Error('Precision not found when converting');
15 | }
16 | throw new Error('Token not found when converting');
17 | }
18 |
19 | export function convertTo18Precision(amount, token) {
20 | if (typeof token !== 'undefined' && token !== '') {
21 | const tokenSpecs = Dapple.getTokenSpecs(token);
22 | if (tokenSpecs) {
23 | if (tokenSpecs.precision === 18) {
24 | return amount;
25 | }
26 | let value = amount;
27 | if (!(amount instanceof BigNumber)) {
28 | value = new BigNumber(amount);
29 | }
30 | return value.times(new BigNumber(10).pow(18 - tokenSpecs.precision)).valueOf();
31 | }
32 | throw new Error('Precision not found when converting');
33 | }
34 | throw new Error('Token not found when converting');
35 | }
36 |
--------------------------------------------------------------------------------
/frontend/public/counter_3.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Group 14 Copy 2
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | 3
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{#if and isConnected hasAccount}}
4 |
15 |
18 | {{#if ready}}
19 | {{#if contractExists}}
20 |
21 | {{> tabs }}
22 |
23 |
24 |
{{> maintrades}}
25 |
{{> maintransfer}}
26 |
{{> maindeposit}}
27 |
28 | {{/if}}
29 | {{/if}}
30 | {{/if}}
31 | {{> wrapperUpdate }}
32 | {{> redeemer }}
33 | {{#unless isConnected}}
34 | {{> noEthereum}}
35 | {{else}}
36 | {{#unless hasAccount}}
37 | {{> noAccount}}
38 | {{/unless}}
39 | {{/unless}}
40 |
41 | {{> footer}}
42 |
43 |
--------------------------------------------------------------------------------
/frontend/public/counter_1.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Group 12
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | 1
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/frontend/.meteor/packages:
--------------------------------------------------------------------------------
1 | # Meteor packages used by this project, one per line.
2 | # Check this file (and the other files in this directory) into your repository.
3 | #
4 | # 'meteor add' and 'meteor remove' will edit this file for you,
5 | # but you can also edit it by hand.
6 |
7 | meteor-base@1.0.4 # Packages every Meteor app needs to have
8 | mobile-experience@1.0.4 # Packages for a great mobile UX
9 | mongo@1.1.14 # The database Meteor supports right now
10 | blaze-html-templates@1.0.4 # Compile .html files into Meteor Blaze views
11 | session@1.1.7 # Client-side reactive dictionary for your app
12 | jquery@1.11.10 # Helpful client-side library
13 | tracker@1.1.1 # Meteor's client-side reactive programming library
14 |
15 | es5-shim@4.6.15 # ECMAScript 5 compatibility for older browsers.
16 | ecmascript@0.6.1 # Enable ECMAScript2015+ syntax in app code
17 |
18 | ethereum:web3
19 | makerotc:dapple
20 | numeral:numeral
21 | dburles:collection-helpers
22 | ethereum:tools
23 | manuel:viewmodel
24 | twbs:bootstrap
25 | practicalmeteor:mocha
26 | velocity:meteor-stubs
27 | hwillson:stub-collections
28 | dispatch:mocha-phantomjs
29 | standard-minifier-css@1.3.2
30 | standard-minifier-js@1.2.1
31 | shell-server@0.2.1
32 | fourseven:scss
33 | momentjs:moment
34 | mikowals:batch-insert
35 |
--------------------------------------------------------------------------------
/frontend/public/counter_2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Group 12 Copy 2
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | 2
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/frontend/public/ic_compare_arrows_black_24px.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ic_compare_arrows_black_24px
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/orderrow.js:
--------------------------------------------------------------------------------
1 | import { Session } from 'meteor/session';
2 | import { Blaze } from 'meteor/blaze';
3 | import { Template } from 'meteor/templating';
4 | import { $ } from 'meteor/jquery';
5 |
6 | import { txHref } from '/imports/utils/functions';
7 |
8 | import './orderrow.html';
9 |
10 | Template.orderRow.events({
11 | 'click .cancel': function cancel(event, templateInstance) {
12 | event.preventDefault();
13 | event.stopPropagation();
14 | const orderId = templateInstance.data.order._id;
15 | Session.set('selectedOffer', orderId);
16 | $('#cancelModal').modal('show');
17 | },
18 | 'click tr': function offer(event, templateInstance) {
19 | event.preventDefault();
20 | if (templateInstance.data.canAccept) {
21 | const orderId = templateInstance.data.order._id;
22 | /* eslint-disable no-underscore-dangle */
23 | if (Blaze._globalHelpers.isBuyEnabled()) {
24 | $('#offerModal').modal('show');
25 | Session.set('selectedOffer', orderId);
26 | } else {
27 | const order = {
28 | id: orderId,
29 | type: templateInstance.data.section,
30 | };
31 | Session.set('selectedOrder', order);
32 | }
33 | }
34 | if (templateInstance.data.canOpenTxLink) {
35 | window.open(txHref(templateInstance.data.order.transactionHash), '_blank');
36 | }
37 | },
38 | });
39 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/depositbalance.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | BALANCES
7 |
8 |
9 |
10 |
41 |
42 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/myorders.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {{#if address}}
21 | {{#if loadingIndividualTradeHistory}}
22 |
{{{loadingIcon}}} Loading closed trades...
23 | {{/if}}
24 | {{> orders orders=(findOrders filterByStatus) type='myOrders' priceLabel='PRICE' showCancel=true showType=true showDate=(equals filterByStatus status.CLOSED) canOpenTxLink=(equals filterByStatus status.CLOSED) }}
25 | {{else}}
26 |
Select an account to display your orders
27 | {{/if}}
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/frontend/imports/api/limits.js:
--------------------------------------------------------------------------------
1 | import { Mongo } from 'meteor/mongo';
2 | import { Session } from 'meteor/session';
3 | import { Dapple } from 'meteor/makerotc:dapple';
4 | import { BigNumber } from 'meteor/ethereum:web3';
5 |
6 | class LimitsCollection extends Mongo.Collection {
7 | // Sync token sell limits asynchronously
8 | sync() {
9 | function getMinSell(sellToken) {
10 | const sellTokenAddress = Dapple.getTokenAddress(sellToken);
11 |
12 | return new Promise((resolve, reject) => {
13 | Dapple['maker-otc'].objects.otc.getMinSell(sellTokenAddress, (error, amount) => {
14 | if (!error) {
15 | resolve([sellToken, amount]);
16 | } else {
17 | reject(error);
18 | }
19 | });
20 | });
21 | }
22 |
23 | const promises = Dapple.getTokens()
24 | .map((token) => getMinSell(token))
25 | .map((promise) => {
26 | promise.then((tokenAndAmount) => {
27 | const token = tokenAndAmount[0];
28 | const amount = tokenAndAmount[1];
29 | super.upsert(token, { $set: { limit: amount.toString() } });
30 | });
31 | });
32 |
33 | Promise.all(promises).then(() => {
34 | Session.set('limitsLoaded', true);
35 | });
36 | }
37 |
38 | limitForToken(token) {
39 | const record = super.findOne(token);
40 | return record ? new BigNumber(record.limit) : new BigNumber(0);
41 | }
42 | }
43 |
44 | export default new LimitsCollection(null);
45 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/orderrow.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{#if order.canCancel}}
5 | {{#if showCancel}}
6 |
7 |
8 |
9 | {{/if}}
10 | {{/if}}
11 |
12 | {{#if showDate}}
13 | {{timestampToString order.timestamp true true}}
14 | {{/if}}
15 | {{#if showType}}
16 |
17 | {{#if equals (determineOrderType order section) 'bid'}}
18 | BUY
19 | {{else}}
20 | SELL
21 | {{/if}}
22 |
23 | {{/if}}
24 | {{#unless showType}}
25 | {{#if or (equals (determineOrderType order) 'ask') (equals (determineOrderType order) 'bid')}}
26 | {{{formatNumber order.price '' true}}}
27 | {{/if}}
28 | {{else}}
29 | {{{formatNumber order.price '' true}}}
30 | {{/unless}}
31 | {{{formatBalance (order.volume quoteCurrency) '' quoteCurrency true}}}
32 | {{{formatBalance (order.volume baseCurrency) '' baseCurrency true}}}
33 |
34 |
35 | {{#if order.helper}}
36 |
37 | {{order.helper}}
38 |
39 | {{/if}}
40 |
41 |
--------------------------------------------------------------------------------
/frontend/imports/api/transactions.js:
--------------------------------------------------------------------------------
1 | import { Mongo } from 'meteor/mongo';
2 | import { web3Obj } from 'meteor/makerotc:dapple';
3 |
4 | class TransactionsCollection extends Mongo.Collection {
5 |
6 | add(type, transactionHash, object) {
7 | // console.log('tx', type, transactionHash, object);
8 | super.insert({ type, tx: transactionHash, object });
9 | }
10 |
11 | findType(type) {
12 | return super.find({ type }).map(value => value.object);
13 | }
14 |
15 | observeRemoved(type, callback) {
16 | return super.find({ type }).observe({ removed: callback });
17 | }
18 |
19 | sync() {
20 | const open = super.find().fetch();
21 |
22 | // Sync all open transactions non-blocking and asynchronously
23 | const syncTransaction = (index) => {
24 | if (index >= 0 && index < open.length) {
25 | const document = open[index];
26 | web3Obj.eth.getTransactionReceipt(document.tx, (error, result) => {
27 | if (!error && result != null) {
28 | if (result.logs.length > 0) {
29 | console.log('tx_success', document.tx, result.gasUsed);
30 | } else {
31 | console.error('tx_oog', document.tx, result.gasUsed);
32 | }
33 | super.update({ tx: document.tx }, { $set: { receipt: result } }, () => {
34 | super.remove({ tx: document.tx });
35 | });
36 | }
37 | // Sync next transaction
38 | syncTransaction(index + 1);
39 | });
40 | }
41 | };
42 | syncTransaction(0);
43 | }
44 | }
45 |
46 | export default new TransactionsCollection(null);
47 |
--------------------------------------------------------------------------------
/frontend/imports/api/weth.js:
--------------------------------------------------------------------------------
1 | import { Meteor } from 'meteor/meteor';
2 | import { Session } from 'meteor/session';
3 | import Transactions from './transactions';
4 |
5 | class WETH {
6 | watchDeposit() {
7 | Transactions.observeRemoved('ethtokens_deposit', (document) => {
8 | if (document.receipt.logs.length === 0) {
9 | Session.set('ETHDepositProgress', 0);
10 | Session.set('ETHDepositProgressMessage', '');
11 | Session.set('ETHDepositErrorMessage', 'Wrap went wrong. Please execute the wrap again.');
12 | } else {
13 | Session.set('ETHDepositProgress', 100);
14 | Session.set('ETHDepositProgressMessage', 'Wrap Done!');
15 | Meteor.setTimeout(() => {
16 | Session.set('ETHDepositProgress', 0);
17 | Session.set('ETHDepositProgressMessage', '');
18 | }, 10000);
19 | }
20 | });
21 | }
22 |
23 | watchWithdraw() {
24 | Transactions.observeRemoved('ethtokens_withdraw', (document) => {
25 | if (document.receipt.logs.length === 0) {
26 | Session.set('ETHWithdrawProgress', 0);
27 | Session.set('ETHWithdrawProgressMessage', '');
28 | Session.set('ETHWithdrawErrorMessage', 'Unwrapping went wrong. Please execute the withdraw again.');
29 | } else {
30 | Session.set('ETHWithdrawProgress', 100);
31 | Session.set('ETHWithdrawProgressMessage', 'Unwrap Done!');
32 | Meteor.setTimeout(() => {
33 | Session.set('ETHWithdrawProgress', 0);
34 | Session.set('ETHWithdrawProgressMessage', '');
35 | }, 10000);
36 | }
37 | });
38 | }
39 | }
40 |
41 | export default new WETH();
42 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/headers/messages.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{#if updateOpen}}
4 |
5 |
6 | Get Started!
7 |
8 |
9 |
10 | Go to wrap/unwrap and wrap ETH to turn it into W-ETH.
11 | Go to Trade and place new orders or trade existing orders by clicking on an open order on the order book to buy/sell into it .
12 |
13 |
14 | The first time you make a transaction you will be prompted to set your allowance .
15 |
16 |
17 |
18 | {{/if}}
19 | {{#if warningOpen}}
20 |
21 |
22 | Warning!
23 |
24 |
25 |
26 | Oasis is undergoing alpha testing: Any funds deposited on the exchange could be lost in the event of a security breach.
27 |
28 | {{#if not isMatchingEnabled}}
29 | There is no automatic order matching between orders.
30 | {{/if}}
31 |
32 |
33 | {{/if}}
34 |
35 |
36 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/history.js:
--------------------------------------------------------------------------------
1 | import { Session } from 'meteor/session';
2 | import { Template } from 'meteor/templating';
3 |
4 | import TokenEvents from '/imports/api/tokenEvents';
5 | import { txHref } from '/imports/utils/functions';
6 |
7 | import './history.html';
8 |
9 | Template.history.viewmodel({
10 | autorun() {
11 | if (this.historyType() === 'depositHistory') {
12 | Session.set('loadingWrapHistory', true);
13 | }
14 | if (this.historyType() === 'transferHistory') {
15 | Session.set('loadingTransferHistory', true);
16 | }
17 | },
18 | currencyClass(token) {
19 | return token === Session.get('quoteCurrency') ? 'quote-currency' : 'base-currency';
20 | },
21 | historyCount() {
22 | return this.history().count();
23 | },
24 | history() {
25 | const address = Session.get('address');
26 | return TokenEvents.find({
27 | type: { $in: ['deposit', 'withdrawal'] },
28 | $or: [{ to: address }, { from: address }],
29 | }, { sort: { blockNumber: -1 } });
30 | },
31 | transferHistory() {
32 | const address = Session.get('address');
33 | return TokenEvents.find({
34 | type: { $in: ['transfer'] },
35 | $or: [{ to: address }, { from: address }], //this triggers reactiveness when the user switches between addresses
36 | }, { sort: { blockNumber: -1 } });
37 | },
38 | transferHistoryCount() {
39 | return this.transferHistory().count();
40 | },
41 | });
42 |
43 | Template.history.events({
44 | 'click tr.clickable': function offer(event) {
45 | event.preventDefault();
46 | if (this.transactionHash) {
47 | window.open(txHref(this.transactionHash), '_blank');
48 | }
49 | },
50 | });
51 |
--------------------------------------------------------------------------------
/frontend/imports/startup/client/index.js:
--------------------------------------------------------------------------------
1 | // Import to load these templates
2 | import '../../ui/client/widgets/depositbalance.js';
3 | import '../../ui/client/widgets/cancelmodal.js';
4 | import '../../ui/client/widgets/chart.js';
5 | import '../../ui/client/widgets/progressblock.js';
6 | import '../../ui/client/widgets/ethtokens.js';
7 | import '../../ui/client/widgets/gnttokens.js';
8 | import '../../ui/client/widgets/history.js';
9 | import '../../ui/client/widgets/lasttrades.html';
10 | import '../../ui/client/widgets/maintrades.html';
11 | import '../../ui/client/widgets/maindeposit.js';
12 | import '../../ui/client/widgets/maintransfer.js';
13 | import '../../ui/client/widgets/markets.js';
14 | import '../../ui/client/widgets/myorders.js';
15 | import '../../ui/client/widgets/newallowance.js';
16 | import '../../ui/client/widgets/neworder.js';
17 | import '../../ui/client/widgets/offermodal.js';
18 | import '../../ui/client/widgets/orders.js';
19 | import '../../ui/client/widgets/orderbook.html';
20 | import '../../ui/client/widgets/sendtokens.js';
21 | import '../../ui/client/widgets/wrapper-update';
22 | import '../../ui/client/widgets/redeemer-modal';
23 |
24 | import '../../ui/client/headers/accountselector.js';
25 | import '../../ui/client/headers/balance.js';
26 | import '../../ui/client/headers/currencyselector.js';
27 | import '../../ui/client/headers/marketdetails.html';
28 | import '../../ui/client/headers/messages.js';
29 | import '../../ui/client/headers/networkstatus.html';
30 | import '../../ui/client/headers/tabs.js';
31 | import '../../ui/client/headers/volumes.html';
32 |
33 |
34 | import '../../ui/client/index.html';
35 | import '../../ui/client/footer.html';
36 | import '../../ui/client/noethereum.html';
37 | import '../../ui/client/noaccount.js';
38 | import '../../ui/client/whatisthis.html';
39 |
40 | import '../../ui/client/helpers.js';
41 |
42 | // Start network
43 | import './network.js';
44 |
--------------------------------------------------------------------------------
/frontend/public/clock.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | clock
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/redeemer-modal.js:
--------------------------------------------------------------------------------
1 | import { Template } from 'meteor/templating';
2 | import { Session } from 'meteor/session';
3 | import Redeemer from '/imports/utils/redeemer';
4 | import './progress-bar.js';
5 | import './redeemer-modal.html';
6 |
7 | Template.redeemer.viewmodel({
8 | message: '',
9 | current: 0,
10 | inProgress: false,
11 |
12 | resetProgressBar() {
13 | this.current(0);
14 | this.message('');
15 | this.inProgress(false);
16 | },
17 | onInterruptedWrapping(error) {
18 | this.resetProgressBar();
19 | this.message('Unwrapping interrupted! Please try again!');
20 | console.debug('Received error during unwrapping: ', error);
21 | },
22 |
23 | async redeem() {
24 | const account = Session.get('address');
25 | const redeemer = new Redeemer(Dapple.env);
26 | this.inProgress(true);
27 |
28 | const allowance = await redeemer.allowanceOf(account);
29 | const balance = await redeemer.balanceOf(account);
30 |
31 | if (allowance < balance) {
32 | this.current(33);
33 | this.message('Confirming redeem process...');
34 | await redeemer.approve(account);
35 | }
36 |
37 | this.current(66);
38 | this.message('Redeeming new MKR tokens');
39 | await redeemer.redeem();
40 |
41 | this.current(100);
42 | this.message('Redeeming Done!');
43 | setTimeout(() => {
44 | this.inProgress(false);
45 | /**
46 | * Nasty workaround because $('#redeemer').modal('hide') not working on surge.
47 | * Even invoked within dev console it's still not closing the modal.
48 | * @type {*}
49 | */
50 | const modal = $('#redeemer');
51 | modal.removeClass('in');
52 | modal.css('display', 'none');
53 |
54 | const body = $('body');
55 | body.removeClass('modal-open');
56 | body.css('padding-right', '0');
57 |
58 | const redeemerModal = document.getElementById('redeemer');
59 | redeemerModal.dispatchEvent(new Event('hide-modal'));
60 | }, 1000);
61 | },
62 | });
63 |
64 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/transferconfirmation.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | WARNING You are attempting to transfer a wrapped token.
19 |
20 |
21 | This is not the same as transferring the token itself, and many wallets do not yet support these wrapped tokens.
22 | This means it could get stuck or be very difficult to transfer back out of this address.
23 | Are you sure you don't want to unwrap the token first and then transfer that?
24 |
25 |
26 |
27 |
28 |
29 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/frontend/public/eth_circle_icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | eth
5 | Created with Sketch.
6 |
7 |
8 |
29 |
30 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/footer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
67 |
68 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/headers/networkstatus.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 | {{network}}
10 | {{> whatisthis section="network"}}
11 |
12 |
13 | {{#unless isConnected}}
14 | (not connected)
15 | {{else}}
16 | {{#if syncing}}
17 |
18 | (syncing {{syncingPercentage}}%)
19 | {{else}}
20 | {{#if outOfSync}}
21 | (out of sync, waiting for synchronization)
22 | {{else}}
23 | {{#if loading}}
24 | (loading offers {{loadingProgress}}%)
25 | {{else}}
26 | {{#if ready}}
27 |
28 | CONNECTED
29 | {{/if}}
30 | {{/if}}
31 | {{/if}}
32 | {{/if}}
33 | {{/unless}}
34 |
35 |
36 |
37 | {{#if isMarketOpen}}
38 |
39 | CLOSING TIME
40 | {{> whatisthis section="network"}}
41 |
42 |
43 |
44 | {{formatDateMarketClose marketCloseTime true}}
45 |
46 | {{else}}
47 |
48 | MARKET STATUS
49 | {{> whatisthis section="network"}}
50 |
51 |
52 |
53 | CLOSED
54 |
55 | {{/if}}
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/redeemer-modal.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | We have updated and improved the MKR token.
16 | Please redeem your new MKR tokens.
17 |
18 |
19 |
20 |
21 |
22 | MKR
23 |
24 |
25 |
26 |
27 |
28 | MKR
29 |
30 |
31 |
32 |
33 | {{#if inProgress}}
34 |
35 |
36 | {{> progressbar message=message value=current max=100}}
37 |
38 |
39 | {{/if}}
40 |
41 |
42 |
43 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/frontend/client/templates/head.html:
--------------------------------------------------------------------------------
1 |
2 | Oasis
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/frontend/public/eth_circle_icon_full.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | eth
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
30 |
31 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "maker-market",
3 | "private": true,
4 | "scripts": {
5 | "lint": "eslint .",
6 | "test": "meteor test --port 3100 --once --driver-package dispatch:mocha-phantomjs",
7 | "test:browser": "meteor test --port 3100 --driver-package practicalmeteor:mocha",
8 | "test:watch": "TEST_WATCH=1 meteor test --port 3100 --driver-package dispatch:mocha-phantomjs",
9 | "start": "meteor run"
10 | },
11 | "eslintConfig": {
12 | "parser": "babel-eslint",
13 | "parserOptions": {
14 | "allowImportExportEverywhere": true
15 | },
16 | "plugins": [
17 | "meteor"
18 | ],
19 | "extends": [
20 | "airbnb",
21 | "plugin:meteor/recommended"
22 | ],
23 | "settings": {
24 | "import/resolver": "meteor"
25 | },
26 | "rules": {
27 | "import/no-unresolved": [
28 | 2,
29 | {
30 | "ignore": [
31 | "^meteor/"
32 | ]
33 | }
34 | ],
35 | "meteor/no-session": [
36 | 0
37 | ],
38 | "max-len": [
39 | 2,
40 | 120,
41 | 2,
42 | {
43 | "ignoreComments": true
44 | }
45 | ],
46 | "import/no-extraneous-dependencies": 0,
47 | "no-underscore-dangle": [
48 | "error",
49 | {
50 | "allow": [
51 | "_id"
52 | ]
53 | }
54 | ]
55 | },
56 | "env": {
57 | "browser": true,
58 | "jquery": true,
59 | "node": true
60 | },
61 | "globals": {
62 | "Dapple": true,
63 | "describe": false,
64 | "it": false,
65 | "before": false,
66 | "beforeEach": false,
67 | "after": false,
68 | "afterEach": false
69 | }
70 | },
71 | "dependencies": {
72 | "babel-runtime": "^6.18.0",
73 | "meteor-node-stubs": "~0.2.0",
74 | "promise-latest": "^1.0.4"
75 | },
76 | "devDependencies": {
77 | "babel-eslint": "^6.1.2",
78 | "eslint": "^3.2.2",
79 | "eslint-config-airbnb": "^10.0.0",
80 | "eslint-import-resolver-meteor": "^0.3.1",
81 | "eslint-plugin-import": "^1.12.0",
82 | "eslint-plugin-jsx-a11y": "^2.0.1",
83 | "eslint-plugin-meteor": "^4.0.0",
84 | "eslint-plugin-react": "^6.0.0"
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/wrapper-update.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | We have updated the wrapper contract.
16 | Please unwrap your W-ETH to use the upgraded wrapper.
17 |
18 |
19 |
20 |
21 |
22 | W-ETH
23 |
24 |
25 |
26 |
27 |
28 | ETH
29 |
30 |
31 |
32 |
33 | {{#if inProgress}}
34 |
35 |
36 | {{> progressbar message=message value=current max=100}}
37 |
38 |
39 | {{/if}}
40 |
41 |
42 |
43 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/noethereum.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
NOT CONNECTED TO ETHEREUM
7 |
OasisDEX requires an Ethereum client to be running and current. OasisDEX could not detect a client running which probably means it's not installed,
8 | running or is misconfigured.
9 |
Please use one of the following clients to connect to Ethereum:
10 |
24 |
While you can also connect with geth or parity , a more advanced configuration is needed. See the
25 | OasisDEX documentation for instructions.
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/frontend/public/maker_circle_icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ICON_MAKER
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/frontend/public/maker_circle_icon_full.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ICON_MAKER Copy
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/cancelmodal.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | {{#if offer}}
18 |
19 | {{#if equals offer.type 'bid'}}
20 | This action will return {{{formatBalance (offer.volume quoteCurrency) '' '' true}}} {{quoteCurrency}} {{formatPrice (offer.volume quoteCurrency) quoteCurrency}}
21 | {{else}}
22 | This action will return {{{formatBalance (offer.volume baseCurrency) '' '' true}}} {{baseCurrency}} {{formatPrice (offer.volume baseCurrency) baseCurrency}}
23 | {{/if}}
24 | to
25 | {{#if offer.isOurs}}
26 | your token balance.
27 | {{else}}
28 | the token balance of its owner.
29 | {{/if}}
30 | {{#if isMarketOpen}}
31 | If someone (partially) fills this order before you cancel it, your order may not or only be partially cancelled.
32 | {{/if}}
33 |
34 | {{/if}}
35 |
36 |
37 |
38 |
39 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/chart.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | CHART
7 |
8 |
9 |
10 |
11 |
12 | PRICE
13 | DEPTH
14 | VOLUME
15 |
16 |
17 |
18 |
19 |
20 |
21 | {{#unless loadedCurrencies}}
22 |
{{{loadingIcon}}} Loading token...
23 | {{else}}
24 | {{#if loadingTradeHistory}}
25 |
{{{loadingIcon}}} Loading chart...
26 | {{/if}}
27 | {{/unless}}
28 |
29 | {{fillPriceChart}}
30 |
31 |
32 | {{#unless loadedCurrencies}}
33 |
{{{loadingIcon}}} Loading token...
34 | {{else}}
35 | {{#if outOfSync}}
36 |
(out of sync, waiting for synchronization)
37 | {{else}}
38 | {{#if loading}}
39 |
{{{loadingIcon}}} Loading offers
40 | {{#if not isMatchingEnabled }}
41 | {{loadingProgress}}%
42 | {{/if}}
43 | {{/if}}
44 | {{/if}}
45 | {{/unless}}
46 |
47 | {{fillDepthChart}}
48 |
49 |
50 | {{#unless loadedCurrencies}}
51 |
{{{loadingIcon}}} Loading token...
52 | {{else}}
53 | {{#if loadingTradeHistory}}
54 |
{{{loadingIcon}}} Loading chart...
55 | {{/if}}
56 | {{/unless}}
57 |
58 | {{fillVolumeChart}}
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/maintrades.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | {{> markets title='MARKETS'}}
17 |
18 |
19 |
20 |
21 | {{> chart title='CHART'}}
22 |
23 |
24 |
25 | {{#if loadedCurrencies}}
26 |
27 |
28 |
29 | {{> neworder title='BUY' orderType='buy'}}
30 |
31 |
32 | {{> neworder title='SELL' orderType='sell'}}
33 |
34 |
35 |
36 |
37 |
38 |
39 | {{> orderbook title='BUY ORDERS' subtitle=(concat '(click on an order listing to sell ' baseCurrency ')') priceClass='bid' priceLabel='BID PRICE' orders=(findOffers 'bid') showType=false}}
40 |
41 |
42 | {{> orderbook title='SELL ORDERS' subtitle=(concat '(click on an order listing to buy ' baseCurrency ')') priceClass='ask' priceLabel='ASK PRICE' orders=(findOffers 'ask')}}
43 |
44 |
45 |
46 |
47 |
48 |
51 |
52 | {{> lasttrades}}
53 |
54 |
55 |
56 | {{> offermodal offer=(findOffer selectedOffer)}}
57 | {{> cancelmodal offer=(findOffer selectedOffer)}}
58 | {{else}}
59 |
60 |
61 | {{{loadingIcon 'large'}}}
62 |
63 |
64 | {{/if}}
65 |
66 |
--------------------------------------------------------------------------------
/frontend/.meteor/versions:
--------------------------------------------------------------------------------
1 | 3stack:bignumber@2.0.7
2 | allow-deny@1.0.5
3 | amplify@1.0.0
4 | autoupdate@1.3.12
5 | babel-compiler@6.13.0
6 | babel-runtime@1.0.1
7 | base64@1.0.10
8 | binary-heap@1.0.10
9 | blaze@2.1.9
10 | blaze-html-templates@1.0.5
11 | blaze-tools@1.0.10
12 | boilerplate-generator@1.0.11
13 | caching-compiler@1.1.9
14 | caching-html-compiler@1.0.7
15 | callback-hook@1.0.10
16 | check@1.2.4
17 | coffeescript@1.11.1_4
18 | cosmos:browserify@0.10.0
19 | dburles:collection-helpers@1.1.0
20 | ddp@1.2.5
21 | ddp-client@1.3.2
22 | ddp-common@1.2.8
23 | ddp-server@1.3.12
24 | deps@1.0.12
25 | diff-sequence@1.0.7
26 | dispatch:mocha-phantomjs@0.1.7
27 | dispatch:phantomjs-tests@0.0.5
28 | ecmascript@0.6.1
29 | ecmascript-runtime@0.3.15
30 | ejson@1.0.13
31 | es5-shim@4.6.15
32 | ethereum:tools@0.6.0
33 | ethereum:web3@0.15.3
34 | fastclick@1.0.13
35 | fourseven:scss@3.13.0
36 | frozeman:persistent-minimongo@0.1.8
37 | frozeman:storage@0.1.9
38 | geojson-utils@1.0.10
39 | hot-code-push@1.0.4
40 | html-tools@1.0.11
41 | htmljs@1.0.11
42 | http@1.2.10
43 | hwillson:stub-collections@1.0.3
44 | id-map@1.0.9
45 | jquery@1.11.10
46 | launch-screen@1.0.12
47 | livedata@1.0.18
48 | localstorage@1.0.12
49 | logging@1.1.16
50 | makerotc:dapple@0.0.1
51 | manuel:isdev@1.0.0
52 | manuel:reactivearray@1.0.5
53 | manuel:viewmodel@4.1.9
54 | manuel:viewmodel-debug@2.7.1
55 | meteor@1.6.0
56 | meteor-base@1.0.4
57 | mikowals:batch-insert@1.1.14
58 | minifier-css@1.2.15
59 | minifier-js@1.2.15
60 | minimongo@1.0.19
61 | mobile-experience@1.0.4
62 | mobile-status-bar@1.0.13
63 | modules@0.7.7
64 | modules-runtime@0.7.7
65 | momentjs:moment@2.17.1
66 | mongo@1.1.14
67 | mongo-id@1.0.6
68 | npm-mongo@2.2.11_2
69 | numeral:numeral@1.5.3_1
70 | observe-sequence@1.0.14
71 | ordered-dict@1.0.9
72 | practicalmeteor:chai@2.1.0_1
73 | practicalmeteor:loglevel@1.2.0_2
74 | practicalmeteor:mocha@2.4.5_6
75 | practicalmeteor:mocha-core@1.0.1
76 | practicalmeteor:sinon@1.14.1_2
77 | promise@0.8.8
78 | random@1.0.10
79 | reactive-dict@1.1.8
80 | reactive-var@1.0.11
81 | reload@1.1.11
82 | retry@1.0.9
83 | routepolicy@1.0.12
84 | session@1.1.7
85 | sha@1.0.9
86 | shell-server@0.2.1
87 | spacebars@1.0.13
88 | spacebars-compiler@1.0.13
89 | standard-minifier-css@1.3.2
90 | standard-minifier-js@1.2.1
91 | templating@1.2.15
92 | templating-compiler@1.2.15
93 | templating-runtime@1.2.15
94 | templating-tools@1.0.5
95 | tmeasday:test-reporter-helpers@0.2.1
96 | tracker@1.1.1
97 | twbs:bootstrap@3.3.6
98 | ui@1.0.12
99 | underscore@1.0.10
100 | url@1.0.11
101 | velocity:meteor-stubs@1.1.1
102 | webapp@1.3.12
103 | webapp-hashing@1.0.9
104 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | []()
2 | ---
3 | [](https://waffle.io/MakerDAO/maker-market)
4 | [](https://travis-ci.org/makerdao/maker-market)
5 |
6 |
7 | This is a simple on-chain OTC market for ERC20 Standard Tokens on the Ethereum Blockchain. You can either pick an order from the order book (in which case delivery will happen instantly), or submit a new order yourself.
8 |
9 | **Oasis is undergoing alpha testing: Proceed at your own risk, and use only small amounts of ETH and MKR.**
10 |
11 | ## Overview
12 |
13 | This dapp uses Meteor as frontend; the contract side can be tested and deployed using dapple.
14 |
15 | ## Usage (for Users)
16 |
17 | Ensure you have a locally running ethereum node.
18 |
19 | ## Installation (for Developers)
20 |
21 | Requirements:
22 |
23 | * geth `brew install ethereum` (or [`apt-get` for ubuntu](https://github.com/ethereum/go-ethereum/wiki/Installation-Instructions-for-Ubuntu))
24 | * solidity https://solidity.readthedocs.org/en/latest/installing-solidity.html
25 | * meteor `curl https://install.meteor.com/ | sh`
26 | * meteor-build-client, `npm install -g meteor-build-client`
27 |
28 | Clone and install:
29 |
30 | ```bash
31 | git clone https://github.com/OasisDEX/oasis.git
32 | cd oasis
33 | npm install
34 | ```
35 |
36 | ## Usage (for Developers)
37 |
38 | To run the frontend, start meteor:
39 |
40 | ```bash
41 | cd frontend
42 | npm install
43 | meteor
44 | ```
45 |
46 | You can access the user interface on [http://localhost:3000/](http://localhost:3000/)
47 |
48 | To deploy the frontend to Github Pages:
49 |
50 | ```bash
51 | gulp deploy
52 | ```
53 |
54 | ## Development
55 |
56 | This project uses the [AirBnB style guide](https://github.com/airbnb/javascript) for coding standard guidelines.
57 | We use [ESLint](http://eslint.org/docs/user-guide/getting-started) to automatically check for common code problems or style errors.
58 | There's an eslintConfig section in frontend/package.json for the configuration of ESLint.
59 | You can run the linter with:
60 |
61 | ```bash
62 | cd frontend
63 | meteor npm run lint
64 | ```
65 |
66 | ## License
67 | This project is licensed under the terms of the Apache License 2.0.
68 |
69 | ## TODOs
70 | See https://waffle.io/MakerDAO/maker-market
71 |
72 | ## Acknowledgements
73 | * Simple Market contract by [Nikolai Mushegian](https://github.com/nmushegian)
74 | * User interface design by [Daniel Brockman](https://github.com/dbrock)
75 | * Blockchain Script by [Chris Hitchcott](https://github.com/hitchcott)
76 |
--------------------------------------------------------------------------------
/frontend/public/ethereum-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Group
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/frontend/packages/dapple/contracts-abi/maker-otc.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | import MatchingMarketABI from './matching-market';
4 | import ExpiringMarketABI from './expiring-market';
5 | import SimpleMarketABI from './simple-market';
6 |
7 | if (typeof Dapple === 'undefined') {
8 | Dapple = {};
9 | }
10 |
11 | Dapple['maker-otc'] = (function builder() {
12 | var environments = {
13 | 'develop': {},
14 | 'live': {
15 | 'otc': {
16 | 'value': '',
17 | 'type': 'MatchingMarket',
18 | },
19 | },
20 | 'kovan': {
21 | 'otc': {
22 | 'value': '',
23 | 'type': 'MatchingMarket',
24 | },
25 | },
26 | };
27 |
28 | function ContractWrapper(headers, _web3) {
29 | if (!_web3) {
30 | throw new Error('Must supply a web3 connection!');
31 | }
32 |
33 | this.headers = headers;
34 | this._class = _web3.eth.contract(headers.interface);
35 | }
36 |
37 | ContractWrapper.prototype.deploy = function () {
38 | throw new Error('Module was built without any deploy data.');
39 | };
40 |
41 | ContractWrapper.prototype.new = function () {
42 | throw new Error('Module was built without any deploy data.');
43 | };
44 |
45 | var passthroughs = ['at'];
46 | for (var i = 0; i < passthroughs.length; i += 1) {
47 | ContractWrapper.prototype[passthroughs[i]] = (function (passthrough) {
48 | return function () {
49 | return this._class[passthrough].apply(this._class, arguments);
50 | };
51 | })(passthroughs[i]);
52 | }
53 |
54 | function constructor(_web3, env) {
55 | if (!env) {
56 | env = {
57 | 'objects': {},
58 | 'type': 'internal',
59 | };
60 | }
61 | if (!('objects' in env) && typeof env === 'object') {
62 | env = { objects: env };
63 | }
64 | while (typeof env !== 'object') {
65 | if (!(env in environments)) {
66 | throw new Error('Cannot resolve environment name: ' + env);
67 | }
68 | env = environments[env];
69 | }
70 |
71 | this.headers = {
72 | 'MatchingMarket': MatchingMarketABI,
73 | 'ExpiringMarket': ExpiringMarketABI,
74 | 'SimpleMarket': SimpleMarketABI,
75 | };
76 |
77 | this.classes = {};
78 | for (var key in this.headers) {
79 | this.classes[key] = new ContractWrapper(this.headers[key], _web3);
80 | }
81 |
82 | this.objects = {};
83 | for (var i in env.objects) {
84 | var obj = env.objects[i];
85 | if (!(obj['type'].split('[')[0] in this.classes)) continue;
86 | this.objects[i] = this.classes[obj['type'].split('[')[0]].at(obj.value);
87 | }
88 | }
89 |
90 | return {
91 | class: constructor,
92 | environments: environments,
93 | };
94 | })();
95 |
96 | if (typeof module !== 'undefined' && module.exports) {
97 | module.exports = Dapple['maker-otc'];
98 | }
99 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/newallowance.js:
--------------------------------------------------------------------------------
1 | import { Template } from 'meteor/templating';
2 | import { BigNumber } from 'meteor/ethereum:web3';
3 | import { Dapple, web3Obj } from 'meteor/makerotc:dapple';
4 | import { $ } from 'meteor/jquery';
5 |
6 | import Transactions from '/imports/api/transactions';
7 | import { formatError } from '/imports/utils/functions';
8 |
9 | import { convertToTokenPrecision } from '/imports/utils/conversion';
10 |
11 | import './newallowance.html';
12 |
13 | const APPROVE_GAS = 150000;
14 |
15 | Template.newallowance.viewmodel({
16 | value: '',
17 | allowance() {
18 | return Template.currentData().token.allowance;
19 | },
20 | pending() {
21 | return Transactions.findType('allowance_'.concat(Template.currentData().token._id));
22 | },
23 | lastError: '',
24 | autorun() {
25 | // Initialize value
26 | this.value(web3Obj.fromWei(this.templateInstance.data.token.allowance));
27 | },
28 | setTotalAllowance() {
29 | this.value(web3Obj.fromWei(this.templateInstance.data.token.balance));
30 | },
31 | canChange() {
32 | try {
33 | return this.pending().length === 0 && this.value() !== '' &&
34 | !(new BigNumber(this.value()).equals(new BigNumber(web3Obj.fromWei(this.allowance()))));
35 | } catch (e) {
36 | return false;
37 | }
38 | },
39 | change(event) {
40 | event.preventDefault();
41 |
42 | this.lastError('');
43 |
44 | const contractAddress = Dapple['maker-otc'].environments[Dapple.env].otc.value;
45 | const options = { gas: APPROVE_GAS };
46 |
47 | // XXX EIP20
48 | Dapple.getToken(this.templateInstance.data.token._id, (error, token) => {
49 | if (!error) {
50 | token.approve(contractAddress,
51 | convertToTokenPrecision(this.value(), this.templateInstance.data.token._id), options, (txError, tx) => {
52 | if (!txError) {
53 | Transactions.add('allowance_'.concat(this.templateInstance.data.token._id), tx,
54 | { value: this.value(), token: this.templateInstance.data.token._id });
55 | Transactions.observeRemoved('allowance_'.concat(this.templateInstance.data.token._id), () => {
56 | const refer = $(`#allowanceModal${this.templateInstance.data.token._id}`).data('refer');
57 | $(`#allowanceModal${this.templateInstance.data.token._id}`).modal('hide');
58 | if (refer === 'newOrder') {
59 | $('#newOrderModal').modal('show');
60 | } else if (refer === 'existingOrder') {
61 | $('#offerModal').modal('show');
62 | }
63 | });
64 | } else {
65 | this.lastError(formatError(txError));
66 | }
67 | });
68 | } else {
69 | this.lastError(error.toString());
70 | }
71 | });
72 | },
73 | });
74 |
--------------------------------------------------------------------------------
/frontend/packages/dapple/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "market": {
3 | "kovan": {
4 | "address": "0x8cf1cab422a0b6b554077a361f8419cdf122a9f9",
5 | "blockNumber": 5216718
6 | },
7 | "live": {
8 | "address": "0x14fbca95be7e99c15cc2996c6c9d841e54b79425",
9 | "blockNumber": 4751582
10 | }
11 | },
12 | "tokens": {
13 | "kovan": {
14 | "OW-ETH": "0x53eccc9246c1e537d79199d0c7231e425a40f896",
15 | "W-ETH": "0xd0a1e359811322d97991e03f863a0c30c2cf029c",
16 | "DAI": "0xc4375b7de8af5a38a93548eb8453a498222c4ff2",
17 | "SAI": "0x228bf3d5be3ee4b80718b89b68069b023c32131e",
18 | "MKR": "0xaaf64bfcc32d0f15873a02163e7e500671a4ffcd",
19 | "DGD": "0xbb7697d091a2b9428053e2d42d088fcd2a6a0aaf",
20 | "GNT": "0xece9fa304cc965b00afc186f5d0281a00d3dbbfd",
21 | "W-GNT": "0xbd1ceb35769eb44b641c8e257005817183fc2817",
22 | "REP": "0x99e846cfe0321260e51963a2114bc4008d092e24",
23 | "ICN": "0x8a55df5de91eceb816bd9263d2e5f35fd516d4d0",
24 | "1ST": "0x846f258ac72f8a60920d9b613ce9e91f8a7a7b54",
25 | "SNGLS": "0xf7d57c676ac2bc4997ca5d4d34adc0d072213d29",
26 | "VSL": "0x2e65483308968f5210167a23bdb46ec94752fe39",
27 | "PLU": "0x00a0fcaa32b47c4ab4a8fdda6d108e5c1ffd8e4f",
28 | "MLN": "0xc3ce96164012ed51c9b1e34a9323fdc38c96ad8a",
29 | "RHOC": "0x7352c20e00d3c89037a8959699741c341771ed59",
30 | "TIME": "0xd944954588061c969fbd828d1f00c297c3511dbd",
31 | "GUP": "0xa786d73316e43c3361145241755566e72424274c",
32 | "BAT": "0x485bd6f93f3cd63d5da117c8205173b542da8e7e",
33 | "NMR": "0x13ec2f6ab4be5a55800193e7b22e83042405bb64"
34 | },
35 | "live": {
36 | "OW-ETH": "0xecf8f87f810ecf450940c9f60066b4a7a501d6a7",
37 | "W-ETH": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
38 | "DAI": "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359",
39 | "SAI": "0x59adcf176ed2f6788a41b8ea4c4904518e62b6a4",
40 | "MKR": "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2",
41 | "DGD": "0xe0b7927c4af23765cb51314a0e0521a9645f0e2a",
42 | "GNT": "0xa74476443119a942de498590fe1f2454d7d4ac0d",
43 | "W-GNT": "0x01afc37f4f85babc47c0e2d0eababc7fb49793c8",
44 | "REP": "0xe94327d07fc17907b4db788e5adf2ed424addff6",
45 | "ICN": "0x888666ca69e0f178ded6d75b5726cee99a87d698",
46 | "1ST": "0xaf30d2a7e90d7dc361c8c4585e9bb7d2f6f15bc7",
47 | "SNGLS": "0xaec2e87e0a235266d9c5adc9deb4b2e29b54d009",
48 | "VSL": "0x5c543e7ae0a1104f78406c340e9c64fd9fce5170",
49 | "PLU": "0xd8912c10681d8b21fd3742244f44658dba12264e",
50 | "MLN": "0xbeb9ef514a379b997e0798fdcc901ee474b6d9a1",
51 | "RHOC": "0x168296bb09e24a88805cb9c33356536b980d3fc5",
52 | "TIME": "0x6531f133e6deebe7f2dce5a0441aa7ef330b4e53",
53 | "GUP": "0xf7b098298f7c69fc14610bf71d5e02c60792894c",
54 | "BAT": "0x0d8775f648430679a709e98d2b0cb6250d2887ef",
55 | "NMR": "0x1776e1f26f98b1a5df9cd347953a26dd3cb46671"
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/markets.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | MARKETS
7 |
8 |
9 | {{#if pairContains 'dai' }}
10 |
13 | {{/if}}
14 |
15 |
16 |
17 |
18 |
19 |
20 | PAIR
21 | PRICE
22 | WEEKLY VOLUME
23 |
24 |
25 |
26 |
27 | {{#each tradingPairs}}
28 | {{#if or showAll this.isVisible }}
29 |
30 |
31 |
32 | {{this.base}} / {{this.quote}}
33 |
34 |
35 | {{#if loadingTradeHistory}}
36 |
37 | {{{loadingIcon}}}
38 |
39 | {{else}}
40 | {{#if equals this.price 'N/A'}}
41 | N/A
42 | {{else}}
43 | {{{formatNumber this.price '' true}}}
44 | {{/if}}
45 | {{/if}}
46 |
47 |
48 | {{#if loadingTradeHistory}}
49 |
50 | {{{loadingIcon}}}
51 |
52 | {{else}}
53 | {{{formatBalance this.volume 2 '' true}}}
54 | {{/if}}
55 |
56 |
57 |
58 | {{/if }}
59 | {{/each}}
60 | {{#unless showAll}}
61 |
62 |
63 | SHOW MORE
64 |
65 |
66 | {{else}}
67 |
68 |
69 | SHOW LESS
70 |
71 |
72 | {{/unless}}
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/headers/currencyselector.js:
--------------------------------------------------------------------------------
1 | import { Session } from 'meteor/session';
2 | import { Template } from 'meteor/templating';
3 |
4 | import Tokens from '/imports/api/tokens';
5 |
6 | import './currencyselector.html';
7 |
8 | Template.currencySelector.viewmodel({
9 | autorun() {
10 | this.quoteCurrency(Session.get('quoteCurrency'));
11 | this.baseCurrency(Session.get('baseCurrency'));
12 | },
13 | quoteCurrencies: Dapple.getQuoteTokens(),
14 | baseCurrencies: Dapple.getBaseTokens(),
15 | showDropdownQuoteCurrencies() {
16 | return this.quoteCurrencies().length > 1;
17 | },
18 | showDropdownBaseCurrencies() {
19 | return this.baseCurrencies().length > 1;
20 | },
21 | quoteCurrency: '',
22 | baseCurrency: '',
23 | quoteHelper: '',
24 | baseHelper: '',
25 | quoteChange() {
26 | // XXX EIP20
27 | Dapple.getToken(this.quoteCurrency(), (error, token) => {
28 | if (!error) {
29 | token.totalSupply((callError) => {
30 | if (!callError) {
31 | this.quoteHelper('');
32 | localStorage.setItem('quoteCurrency', this.quoteCurrency());
33 | Session.set('quoteCurrency', this.quoteCurrency());
34 | if (this.baseCurrency() === this.quoteCurrency()) {
35 | this.baseHelper('Tokens are the same');
36 | }
37 | if (location.hash.indexOf('#trade') !== -1) {
38 | location.hash = `#trade/${this.baseCurrency()}/${this.quoteCurrency()}`;
39 | }
40 | Tokens.sync();
41 | } else {
42 | this.quoteHelper('Token not found');
43 | }
44 | });
45 | } else {
46 | this.quoteHelper('Token not found');
47 | }
48 | });
49 | },
50 | baseChange() {
51 | // XXX EIP20
52 | Dapple.getToken(this.baseCurrency(), (error, token) => {
53 | if (!error) {
54 | token.totalSupply((callError) => {
55 | if (!callError) {
56 | this.baseHelper('');
57 | localStorage.setItem('baseCurrency', this.baseCurrency());
58 | Session.set('baseCurrency', this.baseCurrency());
59 | if (this.baseCurrency() === this.quoteCurrency()) {
60 | this.baseHelper('Tokens are the same');
61 | }
62 | if (location.hash.indexOf('#trade') !== -1) {
63 | location.hash = `#trade/${this.baseCurrency()}/${this.quoteCurrency()}`;
64 | }
65 | Tokens.sync();
66 | } else {
67 | this.baseHelper('Token not found');
68 | }
69 | });
70 | } else {
71 | this.baseHelper('Token not found');
72 | }
73 | });
74 | },
75 | });
76 |
77 | Template.currencySelector.events({
78 | 'click #spnSwitchCurrencies': function switchCurrencies() {
79 | const quoteCurrency = Session.get('quoteCurrency');
80 | const baseCurrency = Session.get('baseCurrency');
81 | Session.set('quoteCurrency', baseCurrency);
82 | Session.set('baseCurrency', quoteCurrency);
83 | },
84 | });
85 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/gnttokens.html:
--------------------------------------------------------------------------------
1 |
2 |
80 |
81 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/wrapper-update.js:
--------------------------------------------------------------------------------
1 | import { Template } from 'meteor/templating';
2 | import { Session } from 'meteor/session';
3 | import { web3Obj } from 'meteor/makerotc:dapple';
4 |
5 | import Transactions from '/imports/api/transactions';
6 | import { formatError } from '/imports/utils/functions';
7 | import './progress-bar.js';
8 | import './wrapper-update.html';
9 |
10 | const TRANSACTION_TYPE_WITHDRAW = 'ethtokens_withdraw';
11 | const WITHDRAW_GAS = 150000;
12 | const WITHDRAW = 'withdraw';
13 |
14 | Template.wrapperUpdate.viewmodel({
15 | message: '',
16 | current: 0,
17 | inProgress: false,
18 |
19 | resetProgressBar() {
20 | this.current(0);
21 | this.message('');
22 | this.inProgress(false);
23 | },
24 | onInterruptedWrapping(error) {
25 | this.resetProgressBar();
26 | this.message('Unwrapping interrupted! Please try again!');
27 | console.debug('Received error during unwrapping: ', error);
28 | },
29 | unwrap() {
30 | const amount = Session.get('oldWrapperBalance');
31 | this.inProgress(true);
32 | Dapple.getToken('OW-ETH', (error, token) => {
33 | if (!error) {
34 | this.current(33);
35 | this.message('Start unwrapping ...');
36 | token.withdraw(amount.toString(10), { gas: WITHDRAW_GAS }, (txError, tx) => {
37 | if (!txError) {
38 | this.current(66);
39 | this.message('Unwrapping... (waiting for transaction confirmation)');
40 | const txChecking = setInterval(() => {
41 | web3Obj.eth.getTransactionReceipt(tx, (err, result) => {
42 | if (!err) {
43 | if (result) {
44 | console.log('Receipt status :', result.status);
45 | if (result.status === '0x1') {
46 | this.current(100);
47 | this.message('Unwrapping Done!');
48 | } else {
49 | this.onInterruptedWrapping('Missing logs inside the receipt!');
50 | }
51 |
52 | clearInterval(txChecking);
53 |
54 | setTimeout(() => {
55 | this.inProgress(false);
56 | /**
57 | * Nasty workaround because $('#wrapperUpdate').modal('hide') not working on surge.
58 | * Even invoked within dev console it's still not closing the modal.
59 | * @type {*}
60 | */
61 | const modal = $('#wrapperUpdate');
62 | modal.removeClass('in');
63 | modal.css('display', 'none');
64 |
65 | const body = $('body');
66 | body.removeClass('modal-open');
67 | body.css('padding-right', '0');
68 | }, 1000);
69 | }
70 | } else {
71 | this.onInterruptedWrapping(txError);
72 | }
73 | });
74 | }, 1000);
75 | } else {
76 | this.onInterruptedWrapping(txError);
77 | }
78 | });
79 | } else {
80 | this.onInterruptedWrapping(error);
81 | }
82 | });
83 | },
84 | });
85 |
86 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/ethtokens.html:
--------------------------------------------------------------------------------
1 |
2 |
84 |
85 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/sendtokens.html:
--------------------------------------------------------------------------------
1 |
2 |
101 | {{> transferconfirmation }}
102 |
103 |
--------------------------------------------------------------------------------
/frontend/client/style/index.css:
--------------------------------------------------------------------------------
1 | /** { font: inherit; margin: 0; padding: 0 }
2 | * { outline: none }
3 | * { box-sizing: border-box }
4 |
5 | body { font-family: sans-serif }
6 | body { line-height: 1rem }
7 | body { font-size: .8rem }
8 | body > div.container { padding: 1rem }
9 | body > div.container { padding-bottom: 3rem }
10 | body > div.container { max-width: 20rem }
11 | body > div.container { margin: auto }
12 |
13 | h1, h2, h3 { color: #546979 }
14 | h1, h2, h3 { line-height: 2rem }
15 | h1, h2, h3 { margin-top: 1rem }
16 |
17 | h1 { text-align: center }
18 | h1 { font-size: 1.5rem }
19 | h1 img { width: 8rem }
20 | h1 img { margin-left: 1rem }
21 | h1 span { position: relative }
22 | h1 span { top: 2.2rem }
23 | h1 span { left: -1.25rem }
24 |
25 | h2 { border-top: .1rem solid currentcolor }
26 | h2, h3 { font-weight: bold }
27 | h2 span { font-size: .6rem }
28 | h2 span { font-weight: normal }
29 | h2 span { margin-left: .5rem }
30 | h2 span { color: gray }
31 | h2 .nav-tabs a:focus { outline: none }
32 |
33 | p { max-width: 30rem }
34 | p { margin: 0 0 10px }
35 |
36 | table { border-collapse: collapse }
37 | table { width: 100% }
38 | th { text-transform: uppercase }
39 | th { font-size: .6rem }
40 | td, th { padding: .2rem }
41 |
42 | small { color: lightgray }
43 | .o td { text-align: right }
44 | .o th { text-align: right }
45 | .o th { color: gray }
46 | .o tbody tr.confirmed td.buy { cursor: pointer }
47 | .o tbody tr.confirmed td.cant-buy { cursor: not-allowed }
48 | .o tbody tr:hover { background: #eee }
49 | .o tbody tr:hover+tr.helper-row { background: #eee }
50 | .o tbody tr.cancelled { color: grey }
51 | .o tbody tr.cancelled td { position: relative }
52 | .o tbody tr.cancelled td:before { content: " " }
53 | .o tbody tr.cancelled td:before { position: absolute }
54 | .o tbody tr.cancelled td:before { bottom: 50% }
55 | .o tbody tr.cancelled td:before { left: 0 }
56 | .o tbody tr.cancelled td:before { border-bottom: 1px solid #333 }
57 | .o tbody tr.cancelled td:before { width: 100% }
58 | .o tbody tr.pending { color: grey }
59 | .o tbody tr.bought { font-weight: 600 }
60 | .o tbody tr.helper-row { color: grey }
61 | .o tbody tr.helper-row { font-size: smaller }
62 |
63 | .nostretch { width: 1% }
64 | .nostretch { white-space: nowrap }
65 |
66 | td.bid, th.bid { color: darkgreen }
67 | td.ask, th.ask { color: darkred }
68 |
69 | form th { text-align: right }
70 | form th { padding: .2rem 1rem }
71 | form th { color: gray }
72 | form th { width: 50% }
73 | form tfoot td { text-align: right }
74 | form tfoot td { padding-top: 1rem }
75 |
76 | .radio label { width: 8rem }
77 | .radio input[type=radio] { margin-top: 5px }
78 | [type=number] { width: 8rem }
79 |
80 | button { border: .075rem solid currentcolor }
81 | button { padding: .1rem .8rem }
82 | button { text-transform: uppercase }
83 | button { font-size: .6rem }
84 | button { border-radius: .2rem }
85 | button { cursor: pointer }
86 | button.btn-default:disabled { color: #eee }
87 |
88 | .input { border: none }
89 | .input { border-bottom: .075rem solid #546979 }
90 | .input { background: none }
91 | .input { padding: .1rem 0rem }
92 | .input { text-transform: uppercase }
93 | .input[disabled] { cursor: not-allowed }
94 |
95 | @media (min-width: 640px)
96 | { :root { font-size: 20pt } }
97 |
98 | @media (min-width: 1280px) {
99 | body > div.container { max-width: 40rem }
100 | h1 { text-align: left }
101 | h1 img { margin-left: -1.2rem }
102 | .o { overflow: hidden }
103 | .row { overflow: hidden }
104 | section { float: right; width: 48% }
105 | section:first-of-type { float: left }
106 | }
107 |
108 | #spnSwitchCurrencies {
109 | margin: 0px 10px;
110 | vertical-align: middle;
111 | cursor: pointer;
112 | }
113 | */
114 |
--------------------------------------------------------------------------------
/frontend/public/dapphub_icn_metamask.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | metamask copy
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/sendtokens.js:
--------------------------------------------------------------------------------
1 | import { Template } from 'meteor/templating';
2 | import { BigNumber } from 'meteor/ethereum:web3';
3 | import { Dapple, web3Obj } from 'meteor/makerotc:dapple';
4 |
5 | import Transactions from '/imports/api/transactions';
6 | import Tokens from '/imports/api/tokens';
7 | import { formatError } from '/imports/utils/functions';
8 |
9 | import { convertToTokenPrecision } from '/imports/utils/conversion';
10 |
11 | import './transferconfirmation';
12 | import './sendtokens.html';
13 |
14 | const TRANSFER_GAS = 150000;
15 | const TRANSACTION_TYPE = 'transfer';
16 |
17 | Template.sendtokens.viewmodel({
18 | currency: 'MKR',
19 | currencies: Dapple.getTokens(),
20 | recipient: '',
21 | lastError: '',
22 | amount: '',
23 | validAmount: true,
24 | shouldShowMaxBtn: false,
25 | onFocus() {
26 | this.shouldShowMaxBtn(true);
27 | },
28 | onBlur() {
29 | this.shouldShowMaxBtn(false);
30 | },
31 | focusOnInput(event) {
32 | $(event.target).find('input.with-max-btn').focus();
33 | },
34 | precision() {
35 | return Dapple.getTokenSpecs(this.currency()).precision;
36 | },
37 | pending() {
38 | return Transactions.findType(TRANSACTION_TYPE);
39 | },
40 | resetAmount() {
41 | this.amount(0);
42 | },
43 | maxAmount() {
44 | try {
45 | const token = Tokens.findOne(this.currency());
46 | return web3Obj.fromWei(token.balance).toString(10);
47 | } catch (e) {
48 | return '0';
49 | }
50 | },
51 | isWrappedToken() {
52 | return this.currency().indexOf('W-') !== -1;
53 | },
54 | canTransfer() {
55 | this.validAmount(true);
56 | if (this.precision() === 0 && this.amount() % 1 !== 0) {
57 | this.validAmount(false);
58 | return false;
59 | }
60 | try {
61 | const amount = new BigNumber(this.amount());
62 | const maxAmount = new BigNumber(this.maxAmount());
63 | const recipient = this.recipient();
64 | return /^(0x)?[0-9a-f]{40}$/i.test(recipient) && amount.gt(0) && amount.lte(maxAmount);
65 | } catch (e) {
66 | return false;
67 | }
68 | },
69 | fillAmount() {
70 | this.amount(this.maxAmount());
71 | },
72 | transfer(event) {
73 | function process(transaction) {
74 | let recipient = transaction.recipient().toLowerCase();
75 | if (!(/^0x/.test(recipient))) {
76 | recipient = '0x'.concat(recipient);
77 | }
78 |
79 | const options = { gas: TRANSFER_GAS };
80 |
81 | // XXX EIP20
82 | Dapple.getToken(transaction.currency(), (error, token) => {
83 | if (!error) {
84 | token.transfer(recipient, convertToTokenPrecision(transaction.amount(), transaction.currency()), options,
85 | (txError, tx) => {
86 | if (!txError) {
87 | Transactions.add(TRANSACTION_TYPE, tx, {
88 | recipient,
89 | amount: transaction.amount(),
90 | token: transaction.currency(),
91 | });
92 | } else {
93 | transaction.lastError(formatError(txError));
94 | }
95 | });
96 | } else {
97 | transaction.lastError(error.toString());
98 | }
99 | });
100 | }
101 |
102 | event.preventDefault();
103 | const transaction = this;
104 |
105 | if (this.isWrappedToken()) {
106 | // https://www.w3.org/TR/css-position-3/#painting-order - point 8
107 | // - for some reason the opacity of all order-s ection is 0.89. This creates stacking order. z-index of modal is ignored.
108 | $('.transfer-section').css('opacity', 1);
109 | $('#transferconfirmation').modal('show');
110 | $('#transferconfirmation').on('transfer:confirmed', (onConfirmation) => {
111 | onConfirmation.stopPropagation();
112 | process(transaction);
113 | });
114 | } else {
115 | process(transaction);
116 | }
117 | },
118 | });
119 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/history.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
HISTORY
4 |
5 |
6 |
7 |
8 |
9 | DATE
10 | {{#if equals historyType 'transferHistory'}}
11 | ACTION
12 | {{/if}}
13 | {{#if equals historyType 'depositHistory'}}
14 | ACTION
15 | COIN
16 | {{else}}
17 | RECIPIENT
18 | COIN
19 | {{/if}}
20 | AMOUNT
21 |
22 |
23 |
24 | {{#if equals historyType 'depositHistory'}}
25 | {{#if loadingWrapHistory}}
26 | {{{loadingIcon}}} Loading history...
27 | {{else}}
28 |
29 | {{#each history}}
30 |
31 |
32 |
33 | {{#if loadingTokenEvents this.transactionHash}}
34 |
35 | {{{loadingIcon}}}
36 |
37 | {{else}}
38 | {{timestampToString this.timestamp true true}}
39 | {{/if}}
40 |
41 |
42 | {{#if equals this.type 'deposit'}}
43 | WRAP
44 | {{else}}
45 | UNWRAP
46 | {{/if}}
47 |
48 | {{this.token}}
49 | {{{formatBalance this.amount '' this.token true}}}
50 |
51 |
52 | {{/each}}
53 |
54 | {{/if}}
55 | {{else}}
56 | {{#if loadingTransferHistory}}
57 | {{{loadingIcon}}} Loading history...
58 | {{else}}
59 |
60 | {{#each transferHistory}}
61 |
62 |
63 |
64 | {{#if loadingTokenEvents this.transactionHash}}
65 |
66 | {{{loadingIcon}}}
67 |
68 | {{else}}
69 | {{timestampToString this.timestamp true true}}
70 | {{/if}}
71 |
72 | {{#if equals historyType 'transferHistory'}}
73 |
74 | {{#if (isMyAddress this.to)}}
75 | IN
76 | {{else}}
77 | OUT
78 | {{/if}}
79 |
80 | {{/if}}
81 | {{#if equals historyType 'depositHistory'}}
82 | {{ friendlyAddress(this.to) }}
83 | {{else}}
84 | {{ friendlyAddress(this.to) }}
85 | {{/if}}
86 | {{this.token}}
87 | {{{formatBalance this.amount '' this.token
88 | true}}}
89 |
90 |
91 | {{/each}}
92 |
93 | {{/if}}
94 | {{/if}}
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/ethtokens.test.js:
--------------------------------------------------------------------------------
1 | import { chai } from 'meteor/practicalmeteor:chai';
2 | import { MeteorStubs } from 'meteor/velocity:meteor-stubs';
3 | import StubCollections from 'meteor/hwillson:stub-collections';
4 | import DappleTokenSpy from '/imports/test/dapple-token-spy';
5 | import { Session } from 'meteor/session';
6 | import { Template } from 'meteor/templating';
7 |
8 | import Transactions from '/imports/api/transactions';
9 | import Tokens from '/imports/api/tokens';
10 |
11 | import './ethtokens.js';
12 |
13 | const fakeEvent = {
14 | preventDefault() {
15 | return;
16 | },
17 | };
18 |
19 | describe('EthTokens View Model', () => {
20 | let vm;
21 | let dappleBackup;
22 | beforeEach(() => {
23 | vm = Template.ethtokens.createViewModel();
24 | MeteorStubs.install();
25 | StubCollections.stub([Tokens, Transactions]);
26 | dappleBackup = Dapple;
27 | });
28 | afterEach(() => {
29 | MeteorStubs.uninstall();
30 | StubCollections.restore();
31 | Dapple = dappleBackup;
32 | });
33 | it('should have default properties', () => {
34 | chai.assert.equal(vm.type(), 'deposit');
35 | chai.assert.equal(vm.amount(), '');
36 | chai.assert.equal(vm.lastError(), '');
37 | });
38 | describe('maxAmount', () => {
39 | it('should return ETH balance when depositing', () => {
40 | vm.type('deposit');
41 | Session.set('ETHBalance', '1230000000000000000');
42 | chai.assert.equal(vm.maxAmount(), '1.23');
43 | });
44 | it('should return W-ETH balance when withdrawing', () => {
45 | vm.type('withdraw');
46 | Tokens.insert({ _id: 'W-ETH', balance: '3450000000000000000' });
47 | chai.assert.equal(vm.maxAmount(), '3.45');
48 | });
49 | });
50 | describe('canDeposit', () => {
51 | beforeEach(() => {
52 | Session.set('ETHBalance', '1230000000000000000');
53 | });
54 | it('should return true when depositing more than zero and less than maxAmount', () => {
55 | vm.type('deposit');
56 | vm.amount('1.00');
57 | chai.assert.isTrue(vm.canDeposit());
58 | });
59 | it('should return false when depositing zero', () => {
60 | vm.type('deposit');
61 | vm.amount('0');
62 | chai.assert.isFalse(vm.canDeposit());
63 | });
64 | it('should return false when depositing more than maxAmount', () => {
65 | vm.type('deposit');
66 | vm.amount('3.50');
67 | chai.assert.isFalse(vm.canDeposit());
68 | });
69 | });
70 | describe('deposit', () => {
71 | beforeEach(() => {
72 | Session.set('ETHBalance', '1230000000000000000');
73 | Tokens.insert({ _id: 'W-ETH', balance: '3450000000000000000' });
74 | });
75 | it('should call token.deposit when depositing', () => {
76 | const tokenSpy = new DappleTokenSpy(true, true);
77 | Dapple = tokenSpy;
78 | vm.type('deposit');
79 | vm.amount('1.00');
80 | vm.deposit(fakeEvent);
81 | const tx = Transactions.findOne();
82 | chai.assert.equal(tx.tx, 'txid');
83 | chai.assert.equal(tx.type, 'ethtokens');
84 | chai.assert.deepEqual(tx.object, { type: 'deposit', amount: '1.00' });
85 | });
86 | it('should set lastError when an error happens getting the token upon deposit', () => {
87 | const tokenSpy = new DappleTokenSpy(false, true);
88 | Dapple = tokenSpy;
89 | vm.type('deposit');
90 | vm.amount('1.00');
91 | vm.deposit(fakeEvent);
92 | chai.assert.equal(vm.lastError(), 'token lookup error');
93 | });
94 | it('should set lastError when an error happens getting the token upon withdraw', () => {
95 | const tokenSpy = new DappleTokenSpy(false, true);
96 | Dapple = tokenSpy;
97 | vm.type('withdraw');
98 | vm.amount('1.00');
99 | vm.deposit(fakeEvent);
100 | chai.assert.equal(vm.lastError(), 'token lookup error');
101 | });
102 | it('should set lastError when an error happens calling the token upon deposit', () => {
103 | const tokenSpy = new DappleTokenSpy(true, false);
104 | Dapple = tokenSpy;
105 | vm.type('deposit');
106 | vm.amount('1.00');
107 | vm.deposit(fakeEvent);
108 | chai.assert.equal(vm.lastError(), 'token.deposit call error');
109 | });
110 | it('should set lastError when an error happens calling the token upon withdraw', () => {
111 | const tokenSpy = new DappleTokenSpy(true, false);
112 | Dapple = tokenSpy;
113 | vm.type('withdraw');
114 | vm.amount('1.00');
115 | vm.deposit(fakeEvent);
116 | chai.assert.equal(vm.lastError(), 'token.withdraw call error');
117 | });
118 | });
119 | });
120 |
--------------------------------------------------------------------------------
/frontend/imports/api/tokens.js:
--------------------------------------------------------------------------------
1 | import { Mongo } from 'meteor/mongo';
2 | import { Session } from 'meteor/session';
3 | import { Dapple, web3Obj } from 'meteor/makerotc:dapple';
4 | import { convertTo18Precision } from '/imports/utils/conversion';
5 |
6 | class TokensCollection extends Mongo.Collection {
7 | /**
8 | * Syncs the quote and base currencies' balances and allowances of selected account,
9 | * usually called for each new block
10 | */
11 | sync() {
12 | const network = Session.get('network');
13 | const address = web3Obj.eth.defaultAccount;
14 | if (address) {
15 | web3Obj.eth.getBalance(address, (error, balance) => {
16 | const newETHBalance = balance.toString(10);
17 | if (!error && !Session.equals('ETHBalance', newETHBalance)) {
18 | Session.set('ETHBalance', newETHBalance);
19 | }
20 | });
21 |
22 | // Get GNTBalance
23 | // XXX EIP20
24 | Dapple.getToken('GNT', (error, token) => {
25 | if (!error) {
26 | token.balanceOf(address, (callError, balance) => {
27 | const newGNTBalance = balance.toString(10);
28 | if (!error && !Session.equals('GNTBalance', newGNTBalance)) {
29 | Session.set('GNTBalance', newGNTBalance);
30 | }
31 | });
32 | const broker = Session.get('GNTBroker');
33 | if (typeof broker === 'undefined' || broker === '0x0000000000000000000000000000000000000000') {
34 | Session.set('GNTBrokerBalance', 0);
35 | } else {
36 | token.balanceOf(broker, (callError, balance) => {
37 | if (!callError) {
38 | const newGNTBrokerBalance = balance.toString(10);
39 | Session.set('GNTBrokerBalance', newGNTBrokerBalance);
40 | }
41 | });
42 | }
43 | }
44 | });
45 |
46 | // const ALL_TOKENS = _.uniq([Session.get('quoteCurrency'), Session.get('baseCurrency')]);
47 | const ALL_TOKENS = Dapple.getTokens();
48 |
49 | if (network !== 'private') {
50 | // Sync token balances and allowances asynchronously
51 | ALL_TOKENS.forEach((tokenId) => {
52 | // XXX EIP20
53 | Dapple.getToken(tokenId, (error, token) => {
54 | if (!error) {
55 | token.balanceOf(address, (callError, balance) => {
56 | if (!error) {
57 | super.upsert(tokenId, { $set: {
58 | balance: convertTo18Precision(balance, tokenId).toString(10),
59 | realBalance: balance.toString(10),
60 | } });
61 | Session.set('balanceLoaded', true);
62 | if (tokenId === 'W-GNT') {
63 | /**
64 | * https://github.com/makerdao/token-wrapper/blob/master/src/wrapper.sol#L63
65 | *
66 | * Basically the argument is not used but since some changes in web3
67 | * https://github.com/ethereum/web3.js/pull/866/commits/77da88a6718cf6eeb45e470104f95b8832f30e34
68 | *
69 | * which enforces you to use all arguments of a given method,
70 | * we pass arbitrary address in order to circumvent the issue.
71 | *
72 | * Usage of Session.get('address') has NO MEANING whatsoever.
73 | */
74 | token.getBroker.call(Session.get('address'), (e, broker) => {
75 | if (!e) {
76 | super.upsert('W-GNT', { $set: { broker } });
77 | Session.set('GNTBroker', broker);
78 | }
79 | });
80 | }
81 | }
82 | });
83 | const contractAddress = Dapple['maker-otc'].environments[Dapple.env].otc.value;
84 | token.allowance(address, contractAddress, (callError, allowance) => {
85 | if (!error) {
86 | super.upsert(tokenId, { $set: {
87 | allowance: convertTo18Precision(allowance, tokenId).toString(10),
88 | realAllowance: allowance.toString(10),
89 | } });
90 | Session.set('allowanceLoaded', true);
91 | }
92 | });
93 | }
94 | });
95 | });
96 | } else {
97 | ALL_TOKENS.forEach((token) => {
98 | super.upsert(token, { $set: { balance: '0', allowance: '0' } });
99 | });
100 | }
101 | }
102 | }
103 | }
104 |
105 | export default new TokensCollection(null);
106 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/newallowance.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
You need to first grant access to withdraw from your personal account by
18 | setting the allowance
19 |
20 |
21 |
22 |
23 |
24 |
Specify the maximum that can be withdrawn. It will decrease each time you
25 | place {{#if not isMatchingEnabled}} or fill {{/if}} an order
26 |
27 |
28 |
29 |
57 |
75 |
95 |
96 |
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/frontend/imports/api/wgnt.js:
--------------------------------------------------------------------------------
1 | import { Meteor } from 'meteor/meteor';
2 | import { Session } from 'meteor/session';
3 | import { Dapple, web3Obj } from 'meteor/makerotc:dapple';
4 | import { formatError } from '/imports/utils/functions';
5 | import Transactions from './transactions';
6 |
7 | const TRANSFER_TO_BROKER_GAS = 150000;
8 | const CLEAR_BROKER_GAS = 150000;
9 |
10 | class WGNT {
11 | watchBrokerCreation() {
12 | Transactions.observeRemoved('gnttokens_create_broker', (document) => {
13 | if (document.receipt.logs.length === 0) {
14 | Session.set('GNTDepositProgress', 0);
15 | Session.set('GNTDepositProgressMessage', '');
16 | Session.set('GNTDepositErrorMessage', 'Creating Broker went wrong. Please execute the desposit again.');
17 | } else {
18 | const broker = document.receipt.logs[0].topics[1];
19 | console.log('Broker: ', broker);
20 | Session.set('GNTDepositProgress', 40);
21 | Session.set('GNTDepositProgressMessage', 'Transfering to Broker... (Waiting for your approval)');
22 | // We get the broker, we transfer GNT to it
23 | Dapple.getToken('GNT', (err, gntToken) => {
24 | gntToken.transfer(broker, web3Obj.toWei(document.object.amount), { gas: TRANSFER_TO_BROKER_GAS },
25 | (txError, tx) => {
26 | if (!txError) {
27 | console.log('TX Transfer to Broker:', tx);
28 | Session.set('GNTDepositProgress', 50);
29 | Session.set('GNTDepositProgressMessage',
30 | 'Transfering to Broker... (waiting for transaction confirmation)');
31 | Transactions.add('gnttokens_transfer', tx, { type: 'deposit', broker });
32 | } else {
33 | Session.set('GNTDepositProgress', 0);
34 | Session.set('GNTDepositProgressMessage', '');
35 | Session.set('GNTDepositErrorMessage', formatError(txError));
36 | }
37 | });
38 | });
39 | }
40 | });
41 | }
42 |
43 | watchBrokerTransfer() {
44 | Transactions.observeRemoved('gnttokens_transfer', (document) => {
45 | if (document.receipt.logs.length === 0) {
46 | Session.set('GNTDepositProgress', 0);
47 | Session.set('GNTDepositProgressMessage', '');
48 | Session.set('GNTDepositErrorMessage', 'Transfering to Broker went wrong. Please execute the desposit again.');
49 | } else {
50 | console.log('Transfer to Broker done');
51 | Session.set('GNTDepositProgress', 75);
52 | Session.set('GNTDepositProgressMessage', 'Clearing Broker... (Waiting for your approval)');
53 | Dapple['token-wrapper'].classes.DepositBroker.at(
54 | document.object.broker.slice(-40)).clear({ gas: CLEAR_BROKER_GAS }, (txError, tx) => {
55 | if (!txError) {
56 | console.log('TX Clear Broker:', tx);
57 | Session.set('GNTDepositProgress', 90);
58 | Session.set('GNTDepositProgressMessage', 'Clearing Broker... (waiting for transaction confirmation)');
59 | Transactions.add('gnttokens_clear', tx, { type: 'deposit' });
60 | } else {
61 | Session.set('GNTDepositProgress', 0);
62 | Session.set('GNTDepositProgressMessage', '');
63 | Session.set('GNTDepositErrorMessage', formatError(txError));
64 | }
65 | });
66 | }
67 | });
68 | }
69 |
70 | watchBrokerClear() {
71 | Transactions.observeRemoved('gnttokens_clear', (document) => {
72 | if (document.receipt.logs.length === 0) {
73 | Session.set('GNTDepositProgress', 0);
74 | Session.set('GNTDepositProgressMessage', '');
75 | Session.set('GNTDepositErrorMessage',
76 | 'Clearing Broker went wrong. Please execute the clearing manually again to get the wrapped coin.');
77 | } else {
78 | Session.set('GNTDepositProgress', 100);
79 | Session.set('GNTDepositProgressMessage', 'Wrap Done!');
80 | Meteor.setTimeout(() => {
81 | Session.set('GNTDepositProgress', 0);
82 | Session.set('GNTDepositProgressMessage', '');
83 | }, 10000);
84 | }
85 | });
86 | }
87 |
88 | watchWithdraw() {
89 | Transactions.observeRemoved('gnttokens_withdraw', (document) => {
90 | if (document.receipt.logs.length === 0) {
91 | Session.set('GNTWithdrawProgress', 0);
92 | Session.set('GNTWithdrawProgressMessage', '');
93 | Session.set('GNTWithdrawErrorMessage', 'Withdrawing went wrong. Please execute the withdraw again.');
94 | } else {
95 | Session.set('GNTWithdrawProgress', 100);
96 | Session.set('GNTWithdrawProgressMessage', 'Withdraw Done!');
97 | Meteor.setTimeout(() => {
98 | Session.set('GNTWithdrawProgress', 0);
99 | Session.set('GNTWithdrawProgressMessage', '');
100 | }, 10000);
101 | }
102 | });
103 | }
104 | }
105 |
106 | export default new WGNT();
107 |
--------------------------------------------------------------------------------
/frontend/packages/dapple/package-post-init.js:
--------------------------------------------------------------------------------
1 | const config = require('./config.json');
2 | const ENVs = {
3 | 'test': 'kovan',
4 | 'main': 'live',
5 | 'private': 'default',
6 | };
7 |
8 | Dapple.init = function init(env) {
9 | var predefinedEnv = ENVs[env];
10 |
11 | if (!predefinedEnv) {
12 | predefinedEnv = env;
13 | }
14 |
15 | Dapple.env = predefinedEnv;
16 | Dapple['maker-otc']['environments'][Dapple.env].otc.value = config.market[Dapple.env].address;
17 | Dapple['maker-otc']['environments'][Dapple.env].otc.blockNumber = config.market[Dapple.env].blockNumber;
18 | Dapple['maker-otc'].class(web3Obj, Dapple['maker-otc'].environments[Dapple.env]);
19 | Dapple['ds-eth-token'].class(web3Obj, Dapple['ds-eth-token'].environments[Dapple.env]);
20 | Dapple['token-wrapper'].class(web3Obj, Dapple['token-wrapper'].environments[Dapple.env]);
21 |
22 | if (env) {
23 | // Check if contract exists on new environment
24 | const contractAddress = Dapple['maker-otc'].environments[Dapple.env].otc.value;
25 | web3Obj.eth.getCode(contractAddress, (error, code) => {
26 | Session.set('contractExists', !error && typeof code === 'string' && code !== '' && code !== '0x');
27 | });
28 | }
29 | };
30 |
31 | const tokens = config.tokens;
32 |
33 | // http://numeraljs.com/ for formats
34 | const tokenSpecs = {
35 | 'OW-ETH': { precision: 18, format: '0,0.00[0000000000000000]' },
36 | 'W-ETH': { precision: 18, format: '0,0.00[0000000000000000]' },
37 | DAI: { precision: 18, format: '0,0.00[0000000000000000]' },
38 | SAI: { precision: 18, format: '0,0.00[0000000000000000]' },
39 | MKR: { precision: 18, format: '0,0.00[0000000000000000]' },
40 | DGD: { precision: 9, format: '0,0.00[0000000]' },
41 | GNT: { precision: 18, format: '0,0.00[0000000000000000]' },
42 | 'W-GNT': { precision: 18, format: '0,0.00[0000000000000000]' },
43 | REP: { precision: 18, format: '0,0.00[0000000000000000]' },
44 | ICN: { precision: 18, format: '0,0.00[0000000000000000]' },
45 | '1ST': { precision: 18, format: '0,0.00[0000000000000000]' },
46 | SNGLS: { precision: 0, format: '0,0' },
47 | VSL: { precision: 18, format: '0,0.00[0000000000000000]' },
48 | PLU: { precision: 18, format: '0,0.00[0000000000000000]' },
49 | MLN: { precision: 18, format: '0,0.00[0000000000000000]' },
50 | RHOC: { precision: 8, format: '0,0.00[000000]' },
51 | TIME: { precision: 8, format: '0,0.00[000000]' },
52 | GUP: { precision: 3, format: '0,0.00[0]' },
53 | BAT: { precision: 18, format: '0,0.00[0000000000000000]' },
54 | NMR: { precision: 18, format: '0,0.00[0000000000000000]' },
55 | };
56 |
57 | Dapple.getQuoteTokens = () => ['W-ETH'];
58 |
59 | Dapple.getBaseTokens = () => ['W-GNT', 'DGD', 'ICN', '1ST', 'SNGLS', 'VSL', 'PLU', 'MLN', 'RHOC', 'TIME', 'GUP', 'BAT', 'NMR'];
60 |
61 | Dapple.getTokens = () => ['W-ETH', 'MKR', 'DGD', 'GNT', 'W-GNT', 'ICN', '1ST', 'SNGLS', 'VSL', 'PLU', 'MLN', 'RHOC', 'TIME', 'GUP', 'BAT', 'NMR', 'SAI', 'DAI'];
62 |
63 | Dapple.generatePairs = () => {
64 | const TradingPairs = [
65 | {
66 | base: 'MKR',
67 | quote: 'W-ETH',
68 | priority: 10,
69 | },
70 | {
71 | base: 'W-ETH',
72 | quote: 'DAI',
73 | priority: 9,
74 | },
75 | {
76 | base: 'MKR',
77 | quote: 'DAI',
78 | priority: 8,
79 | },
80 | {
81 | base: 'SAI',
82 | quote: 'DAI',
83 | priority: 7,
84 | },
85 | ];
86 |
87 | Dapple.getBaseTokens().forEach((base) => {
88 | Dapple.getQuoteTokens().forEach((quote) => {
89 | TradingPairs.push({
90 | base,
91 | quote,
92 | priority: 0,
93 | });
94 | });
95 | });
96 | return TradingPairs;
97 | };
98 |
99 | Dapple.getTokenSpecs = (symbol) => {
100 | if (typeof (tokenSpecs[symbol]) !== 'undefined') {
101 | return tokenSpecs[symbol];
102 | }
103 | return tokenSpecs['W-ETH'];
104 | };
105 |
106 | Dapple.getTokenAddress = (symbol) => tokens[Dapple.env][symbol];
107 |
108 | Dapple.getTokenByAddress = (address) => _.invert(tokens[Dapple.env])[address];
109 |
110 | Dapple.getToken = (symbol, callback) => {
111 | if (!(Dapple.env in tokens)) {
112 | callback('Unknown environment', null);
113 | return;
114 | }
115 | if (!(symbol in tokens[Dapple.env])) {
116 | callback(`Unknown token "${symbol}"`, null);
117 | return;
118 | }
119 |
120 | const address = Dapple.getTokenAddress(symbol);
121 | let tokenClass = 'DSTokenBase';
122 | let that = Dapple['ds-eth-token'];
123 |
124 | if (symbol === 'W-ETH' || symbol === 'OW-ETH') {
125 | tokenClass = 'DSEthToken';
126 | } else if (symbol === 'W-GNT') {
127 | tokenClass = 'TokenWrapper';
128 | that = Dapple['token-wrapper'];
129 | }
130 |
131 | try {
132 | that.classes[tokenClass].at(address, (error, token) => {
133 | if (!error) {
134 | token.abi = that.classes[tokenClass].abi;
135 | callback(false, token);
136 | } else {
137 | callback(error, token);
138 | }
139 | });
140 | } catch (e) {
141 | callback(e, null);
142 | }
143 | };
144 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/neworder.html:
--------------------------------------------------------------------------------
1 |
2 |
111 |
112 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/ethtokens.js:
--------------------------------------------------------------------------------
1 | import { Session } from 'meteor/session';
2 | import { Template } from 'meteor/templating';
3 | import { BigNumber } from 'meteor/ethereum:web3';
4 | import { web3Obj } from 'meteor/makerotc:dapple';
5 |
6 | import Transactions from '/imports/api/transactions';
7 | import Tokens from '/imports/api/tokens';
8 | import WETH from '/imports/api/weth';
9 | import { uppercaseFirstLetter, formatError } from '/imports/utils/functions';
10 |
11 | import './ethtokens.html';
12 |
13 | const TRANSACTION_TYPE_WITHDRAW = 'ethtokens_withdraw';
14 | const TRANSACTION_TYPE_DEPOSIT = 'ethtokens_deposit';
15 | const DEPOSIT_GAS = 150000;
16 | const WITHDRAW_GAS = 150000;
17 | const DEPOSIT = 'deposit';
18 | const WITHDRAW = 'withdraw';
19 |
20 | Template.ethtokens.viewmodel({
21 | type() {
22 | const depositType = (this !== null && this !== undefined) ? this.depositType() : '';
23 | return depositType;
24 | },
25 | amount: '',
26 | lastError: '',
27 | shouldShowMaxBtn: false,
28 | shouldShowWrapWarning: false,
29 | fillAmount() {
30 | let amount = '0';
31 | try {
32 | if (this.type() === DEPOSIT) {
33 | amount = web3Obj.fromWei(Session.get('ETHBalance'));
34 | } else if (this.type() === WITHDRAW) {
35 | amount = web3Obj.fromWei(Tokens.findOne('W-ETH').balance);
36 | }
37 | } catch (e) {
38 | amount = '0';
39 | }
40 | this.amount(amount);
41 | },
42 | onFocus() {
43 | if (this.title().toLowerCase() === 'unwrap') { this.shouldShowMaxBtn(true); }
44 | if (this.title().toLowerCase() === 'wrap') { this.shouldShowWrapWarning(true); }
45 | },
46 | onBlur() {
47 | if (this.title().toLowerCase() === 'unwrap') { this.shouldShowMaxBtn(false); }
48 | if (this.title().toLowerCase() === 'wrap') { this.shouldShowWrapWarning(false); }
49 | },
50 | focusOnInput(event) {
51 | $(event.target).find('input.with-max-btn').focus();
52 | },
53 | progress() {
54 | return Session.get(`ETH${uppercaseFirstLetter(this.type())}Progress`);
55 | },
56 | progressMessage() {
57 | return Session.get(`ETH${uppercaseFirstLetter(this.type())}ProgressMessage`);
58 | },
59 | errorMessage() {
60 | return Session.get(`ETH${uppercaseFirstLetter(this.type())}ErrorMessage`);
61 | },
62 | maxAmount() {
63 | let maxAmount = '0';
64 | try {
65 | if (this.type() === DEPOSIT) {
66 | maxAmount = web3Obj.fromWei(Session.get('ETHBalance'));
67 | } else if (this.type() === WITHDRAW) {
68 | maxAmount = web3Obj.fromWei(Tokens.findOne('W-ETH').balance);
69 | }
70 | } catch (e) {
71 | maxAmount = '0';
72 | }
73 | return maxAmount;
74 | },
75 | canDeposit() {
76 | try {
77 | const amount = new BigNumber(this.amount());
78 | const maxAmount = new BigNumber(this.maxAmount());
79 | return amount.gt(0) && amount.lte(maxAmount);
80 | } catch (e) {
81 | return false;
82 | }
83 | },
84 | deposit(event) {
85 | event.preventDefault();
86 | this.lastError('');
87 |
88 | if (this.type() === DEPOSIT) {
89 | const options = {
90 | gas: DEPOSIT_GAS,
91 | value: web3Obj.toWei(this.amount()),
92 | };
93 | // XXX EIP20
94 | Dapple.getToken('W-ETH', (error, token) => {
95 | if (!error) {
96 | Session.set('ETHDepositProgress', 33);
97 | Session.set('ETHDepositProgressMessage', 'Starting wrap... (waiting for your approval)');
98 | Session.set('ETHDepositErrorMessage', '');
99 | token.deposit(options, (txError, tx) => {
100 | if (!txError) {
101 | Session.set('ETHDepositProgress', 66);
102 | Session.set('ETHDepositProgressMessage', 'Executing wrap... (waiting for transaction confirmation)');
103 | Session.set('ETHDepositErrorMessage', '');
104 | Transactions.add(TRANSACTION_TYPE_DEPOSIT, tx, { type: DEPOSIT, amount: this.amount() });
105 | } else {
106 | Session.set('ETHDepositProgress', 0);
107 | Session.set('ETHDepositProgressMessage', '');
108 | Session.set('ETHDepositErrorMessage', formatError(txError));
109 | }
110 | });
111 | WETH.watchDeposit();
112 | } else {
113 | Session.set('ETHDepositProgress', 0);
114 | Session.set('ETHDepositProgressMessage', '');
115 | Session.set('ETHDepositErrorMessage', error.toString());
116 | }
117 | });
118 | } else {
119 | // XXX EIP20
120 | Dapple.getToken('W-ETH', (error, token) => {
121 | if (!error) {
122 | Session.set('ETHWithdrawProgress', 33);
123 | Session.set('ETHWithdrawProgressMessage', 'Starting unwrap... (waiting for your approval)');
124 | Session.set('ETHWithdrawErrorMessage', '');
125 | token.withdraw(web3Obj.toWei(this.amount()), { gas: WITHDRAW_GAS }, (txError, tx) => {
126 | if (!txError) {
127 | Session.set('ETHWithdrawProgress', 66);
128 | Session.set('ETHWithdrawProgressMessage', 'Executing unwrap... (waiting for transaction confirmation)');
129 | Transactions.add(TRANSACTION_TYPE_WITHDRAW, tx, { type: WITHDRAW, amount: this.amount() });
130 | } else {
131 | Session.set('ETHWithdrawProgress', 0);
132 | Session.set('ETHWithdrawProgressMessage', '');
133 | Session.set('ETHWithdrawErrorMessage', formatError(txError));
134 | }
135 | });
136 | WETH.watchWithdraw();
137 | } else {
138 | Session.set('ETHWithdrawProgress', 0);
139 | Session.set('ETHWithdrawProgressMessage', '');
140 | Session.set('ETHWithdrawErrorMessage', error.toString());
141 | }
142 | });
143 | }
144 | },
145 | });
146 |
--------------------------------------------------------------------------------
/frontend/imports/utils/functions.js:
--------------------------------------------------------------------------------
1 | import { Session } from 'meteor/session';
2 | import { $ } from 'meteor/jquery';
3 | import { BigNumber } from 'meteor/ethereum:web3';
4 | import { Dapple, web3Obj } from 'meteor/makerotc:dapple';
5 | import { Spacebars } from 'meteor/spacebars';
6 |
7 | /**
8 | * Best case scenario:
9 | * - a valid contract address is passed and we have corresponding token symbol
10 | * then we return the symbol for the token.
11 | *
12 | * If the token is a valid address but not known to the market, default value will be returned
13 | * If the token is not known, default value is returned
14 | *
15 | * @param addressOrToken - of type
16 | * @param defaultToken - of type - used as value if token is invalid value
17 | * (not an address, not an address known to the market, not a symbol, not a symbol known to market. (mandatory)
18 | *
19 | * @return string
20 | *
21 | * @throws error if the method is invoked with missing attributes or the types of the attributes are different than expected
22 | */
23 | function asToken(addressOrToken, defaultToken) {
24 | const allTokens = Dapple.getTokens();
25 |
26 | if (!defaultToken
27 | || typeof defaultToken !== 'string'
28 | || !allTokens.includes(defaultToken)) {
29 | throw Error('Wrong usage of the API. Read documentation');
30 | }
31 |
32 | if (!addressOrToken || typeof addressOrToken !== 'string') {
33 | return defaultToken;
34 | }
35 | const isAnAddress = web3Obj.isAddress(addressOrToken);
36 | let currency = defaultToken.toUpperCase();
37 | let token = addressOrToken.toUpperCase();
38 |
39 | if (isAnAddress) {
40 | token = Dapple.getTokenByAddress(addressOrToken).toUpperCase();
41 | }
42 |
43 | if (token && allTokens.includes(token)) {
44 | currency = token;
45 | }
46 |
47 | return currency;
48 | }
49 |
50 | export function uppercaseFirstLetter(word) {
51 | return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
52 | }
53 |
54 | // This is needed for Metamask errors
55 | // See: https://github.com/MetaMask/metamask-plugin/issues/672
56 | export function formatError(error) {
57 | return error.toString().split('\n')[0];
58 | }
59 |
60 | export function doTabShow() {
61 | if (location.hash.indexOf('#wrap') !== -1) {
62 | $('.nav-tabs a[href=#wrap]').tab('show');
63 | } else if (location.hash.indexOf('#transfer') !== -1) {
64 | $('.nav-tabs a[href=#transfer]').tab('show');
65 | } else {
66 | $('.nav-tabs a[href=#trade]').tab('show');
67 | }
68 | }
69 |
70 | export function doHashChange() {
71 | // For now is the only currency on the left side
72 | localStorage.setItem('quoteCurrency', 'W-ETH');
73 |
74 | let quoteCurrency = null;
75 | let baseCurrency = null;
76 |
77 | if (location.hash.indexOf('#wrap') === -1 && location.hash.indexOf('#transfer') === -1) {
78 | if (location.hash.indexOf('#trade') === -1) {
79 | location.hash = `#trade/${localStorage.getItem('baseCurrency') || 'MKR'}`
80 | + `/${localStorage.getItem('quoteCurrency') || 'W-ETH'}`;
81 | }
82 | const coins = location.hash.replace(/#trade\//g, '').split('/');
83 |
84 | /**
85 | * The default values for base and quote are respectively:
86 | * MKR and W-ETH in all scenarios. The reason for this is
87 | * because those are the main currencies that MAKER is dealing with.
88 | */
89 | const base = coins[0];
90 | baseCurrency = asToken(base, 'MKR');
91 |
92 | const quote = coins[1];
93 | quoteCurrency = asToken(quote, 'W-ETH');
94 |
95 | if (baseCurrency === quoteCurrency) {
96 | quoteCurrency = 'W-ETH';
97 | baseCurrency = 'MKR';
98 | }
99 |
100 | // Looking for any existing pair that contains the currencies provided in the URL
101 | const pair = Dapple.generatePairs().find((currentPair) =>
102 | (currentPair.base === baseCurrency && currentPair.quote === quoteCurrency)
103 | || (currentPair.base === quoteCurrency && currentPair.quote === baseCurrency));
104 |
105 | // if such pair exists we use it to set the base and quote otherwise we default
106 | if (pair) {
107 | baseCurrency = pair.base;
108 | quoteCurrency = pair.quote;
109 | } else {
110 | quoteCurrency = 'W-ETH';
111 | baseCurrency = 'MKR';
112 | }
113 |
114 | Session.set('newPairSelected', pair);
115 |
116 | location.hash = `#trade/${baseCurrency}/${quoteCurrency}`;
117 | }
118 |
119 | doTabShow();
120 |
121 | Session.set('quoteCurrency', quoteCurrency || localStorage.getItem('quoteCurrency'));
122 | Session.set('baseCurrency', baseCurrency || localStorage.getItem('baseCurrency'));
123 | }
124 |
125 | export function txHref(tx) {
126 | let txLink = '';
127 | if (Dapple['maker-otc'].objects) {
128 | const network = Session.get('network');
129 | let networkPrefix = '';
130 | if (network === 'kovan') {
131 | networkPrefix = 'kovan.';
132 | }
133 | txLink = `https://${networkPrefix}etherscan.io/tx/${tx}`;
134 | }
135 | return txLink;
136 | }
137 |
138 | export function fractionSeparator() {
139 | const formatter = new Intl.NumberFormat(navigator.language);
140 |
141 | // Usage of this line is define the separator of fractions based on users locale
142 | return formatter.format(0.123).charAt(1);
143 | }
144 |
145 | export function thousandSeparator(number) {
146 | const parts = number.toString().split('.');
147 | const formatter = new Intl.NumberFormat(navigator.language);
148 |
149 | const whole = formatter.format(parts[0]);
150 | const fraction = (parts[1] ? `${fractionSeparator()}${parts[1]}` : '');
151 |
152 | return whole + fraction;
153 | }
154 |
155 | export function formatNumber(number, dec) {
156 | let decimals = dec;
157 | if (decimals instanceof Spacebars.kw) {
158 | decimals = 5;
159 | }
160 | let n = number;
161 | if (typeof number !== 'object') {
162 | n = new BigNumber(`${number}`);
163 | }
164 | const d = (new BigNumber(10)).pow(decimals);
165 | n = n.mul(d).trunc().div(d).toFixed(decimals, 6);
166 | return thousandSeparator(n);
167 | }
168 |
169 | export function removeOutliersFromArray(data, fieldName, deviation) {
170 | const l = data.length;
171 | if (data.length <= 2) {
172 | return data;
173 | }
174 | let sum = 0; // stores sum of elements
175 | let sumsq = 0; // stores sum of squares
176 | for (let i = 0; i < l; ++i) {
177 | sum += data[i][fieldName];
178 | sumsq += data[i][fieldName] * data[i][fieldName];
179 | }
180 | const mean = sum / l;
181 | const varience = (sumsq / l) - (mean * mean);
182 | const sd = Math.sqrt(varience);
183 | const newData = []; // uses for data which is 3 standard deviations from the mean
184 | for (let i = 0; i < l; ++i) {
185 | if (data[i][fieldName] > mean - (deviation * sd) && data[i][fieldName] < mean + (deviation * sd)) {
186 | newData.push(data[i]);
187 | }
188 | }
189 | return newData;
190 | }
191 |
--------------------------------------------------------------------------------
/frontend/packages/dapple/contracts-abi/simple-market.js:
--------------------------------------------------------------------------------
1 | const SimpleMarketABI = {
2 | interface: [{
3 | constant: false,
4 | inputs: [{ name: 'pay_gem', type: 'address' }, { name: 'buy_gem', type: 'address' }, {
5 | name: 'pay_amt',
6 | type: 'uint128',
7 | }, { name: 'buy_amt', type: 'uint128' }],
8 | name: 'make',
9 | outputs: [{ name: 'id', type: 'bytes32' }],
10 | payable: false,
11 | stateMutability: 'nonpayable',
12 | type: 'function',
13 | }, {
14 | constant: true,
15 | inputs: [],
16 | name: 'last_offer_id',
17 | outputs: [{ name: '', type: 'uint256' }],
18 | payable: false,
19 | stateMutability: 'view',
20 | type: 'function',
21 | }, {
22 | constant: false,
23 | inputs: [{ name: 'id', type: 'uint256' }],
24 | name: 'cancel',
25 | outputs: [{ name: 'success', type: 'bool' }],
26 | payable: false,
27 | stateMutability: 'nonpayable',
28 | type: 'function',
29 | }, {
30 | constant: true,
31 | inputs: [{ name: 'id', type: 'uint256' }],
32 | name: 'getOffer',
33 | outputs: [{ name: '', type: 'uint256' }, { name: '', type: 'address' }, {
34 | name: '',
35 | type: 'uint256',
36 | }, { name: '', type: 'address' }],
37 | payable: false,
38 | stateMutability: 'view',
39 | type: 'function',
40 | }, {
41 | constant: false,
42 | inputs: [{ name: 'id', type: 'bytes32' }, { name: 'maxTakeAmount', type: 'uint128' }],
43 | name: 'take',
44 | outputs: [],
45 | payable: false,
46 | stateMutability: 'nonpayable',
47 | type: 'function',
48 | }, {
49 | constant: false,
50 | inputs: [{ name: 'id_', type: 'bytes32' }],
51 | name: 'bump',
52 | outputs: [],
53 | payable: false,
54 | stateMutability: 'nonpayable',
55 | type: 'function',
56 | }, {
57 | constant: true,
58 | inputs: [{ name: 'id', type: 'uint256' }],
59 | name: 'isActive',
60 | outputs: [{ name: 'active', type: 'bool' }],
61 | payable: false,
62 | stateMutability: 'view',
63 | type: 'function',
64 | }, {
65 | constant: true,
66 | inputs: [{ name: '', type: 'uint256' }],
67 | name: 'offers',
68 | outputs: [{ name: 'pay_amt', type: 'uint256' }, { name: 'pay_gem', type: 'address' }, {
69 | name: 'buy_amt',
70 | type: 'uint256',
71 | }, { name: 'buy_gem', type: 'address' }, { name: 'owner', type: 'address' }, {
72 | name: 'active',
73 | type: 'bool',
74 | }, { name: 'timestamp', type: 'uint64' }],
75 | payable: false,
76 | stateMutability: 'view',
77 | type: 'function',
78 | }, {
79 | constant: false,
80 | inputs: [{ name: 'id', type: 'bytes32' }],
81 | name: 'kill',
82 | outputs: [],
83 | payable: false,
84 | stateMutability: 'nonpayable',
85 | type: 'function',
86 | }, {
87 | constant: true,
88 | inputs: [{ name: 'id', type: 'uint256' }],
89 | name: 'getOwner',
90 | outputs: [{ name: 'owner', type: 'address' }],
91 | payable: false,
92 | stateMutability: 'view',
93 | type: 'function',
94 | }, {
95 | constant: false,
96 | inputs: [{ name: 'id', type: 'uint256' }, { name: 'quantity', type: 'uint256' }],
97 | name: 'buy',
98 | outputs: [{ name: '', type: 'bool' }],
99 | payable: false,
100 | stateMutability: 'nonpayable',
101 | type: 'function',
102 | }, {
103 | constant: false,
104 | inputs: [{ name: 'pay_amt', type: 'uint256' }, { name: 'pay_gem', type: 'address' }, {
105 | name: 'buy_amt',
106 | type: 'uint256',
107 | }, { name: 'buy_gem', type: 'address' }],
108 | name: 'offer',
109 | outputs: [{ name: 'id', type: 'uint256' }],
110 | payable: false,
111 | stateMutability: 'nonpayable',
112 | type: 'function',
113 | }, {
114 | anonymous: false,
115 | inputs: [{ indexed: false, name: 'id', type: 'uint256' }],
116 | name: 'LogItemUpdate',
117 | type: 'event',
118 | }, {
119 | anonymous: false,
120 | inputs: [{ indexed: false, name: 'pay_amt', type: 'uint256' }, {
121 | indexed: true,
122 | name: 'pay_gem',
123 | type: 'address',
124 | }, { indexed: false, name: 'buy_amt', type: 'uint256' }, {
125 | indexed: true,
126 | name: 'buy_gem',
127 | type: 'address',
128 | }],
129 | name: 'LogTrade',
130 | type: 'event',
131 | }, {
132 | anonymous: false,
133 | inputs: [{ indexed: true, name: 'id', type: 'bytes32' }, {
134 | indexed: true,
135 | name: 'pair',
136 | type: 'bytes32',
137 | }, { indexed: true, name: 'maker', type: 'address' }, {
138 | indexed: false,
139 | name: 'pay_gem',
140 | type: 'address',
141 | }, { indexed: false, name: 'buy_gem', type: 'address' }, {
142 | indexed: false,
143 | name: 'pay_amt',
144 | type: 'uint128',
145 | }, { indexed: false, name: 'buy_amt', type: 'uint128' }, {
146 | indexed: false,
147 | name: 'timestamp',
148 | type: 'uint64',
149 | }],
150 | name: 'LogMake',
151 | type: 'event',
152 | }, {
153 | anonymous: false,
154 | inputs: [{ indexed: true, name: 'id', type: 'bytes32' }, {
155 | indexed: true,
156 | name: 'pair',
157 | type: 'bytes32',
158 | }, { indexed: true, name: 'maker', type: 'address' }, {
159 | indexed: false,
160 | name: 'pay_gem',
161 | type: 'address',
162 | }, { indexed: false, name: 'buy_gem', type: 'address' }, {
163 | indexed: false,
164 | name: 'pay_amt',
165 | type: 'uint128',
166 | }, { indexed: false, name: 'buy_amt', type: 'uint128' }, {
167 | indexed: false,
168 | name: 'timestamp',
169 | type: 'uint64',
170 | }],
171 | name: 'LogBump',
172 | type: 'event',
173 | }, {
174 | anonymous: false,
175 | inputs: [{ indexed: false, name: 'id', type: 'bytes32' }, {
176 | indexed: true,
177 | name: 'pair',
178 | type: 'bytes32',
179 | }, { indexed: true, name: 'maker', type: 'address' }, {
180 | indexed: false,
181 | name: 'pay_gem',
182 | type: 'address',
183 | }, { indexed: false, name: 'buy_gem', type: 'address' }, {
184 | indexed: true,
185 | name: 'taker',
186 | type: 'address',
187 | }, { indexed: false, name: 'take_amt', type: 'uint128' }, {
188 | indexed: false,
189 | name: 'give_amt',
190 | type: 'uint128',
191 | }, { indexed: false, name: 'timestamp', type: 'uint64' }],
192 | name: 'LogTake',
193 | type: 'event',
194 | }, {
195 | anonymous: false,
196 | inputs: [{ indexed: true, name: 'id', type: 'bytes32' }, {
197 | indexed: true,
198 | name: 'pair',
199 | type: 'bytes32',
200 | }, { indexed: true, name: 'maker', type: 'address' }, {
201 | indexed: false,
202 | name: 'pay_gem',
203 | type: 'address',
204 | }, { indexed: false, name: 'buy_gem', type: 'address' }, {
205 | indexed: false,
206 | name: 'pay_amt',
207 | type: 'uint128',
208 | }, { indexed: false, name: 'buy_amt', type: 'uint128' }, {
209 | indexed: false,
210 | name: 'timestamp',
211 | type: 'uint64',
212 | }],
213 | name: 'LogKill',
214 | type: 'event',
215 | }],
216 | };
217 |
218 | export { SimpleMarketABI as default };
219 |
--------------------------------------------------------------------------------
/frontend/imports/ui/client/widgets/gnttokens.js:
--------------------------------------------------------------------------------
1 | import { Session } from 'meteor/session';
2 | import { Template } from 'meteor/templating';
3 | import { BigNumber } from 'meteor/ethereum:web3';
4 | import { web3Obj } from 'meteor/makerotc:dapple';
5 |
6 | import Transactions from '/imports/api/transactions';
7 | import Tokens from '/imports/api/tokens';
8 | import { uppercaseFirstLetter, formatError } from '/imports/utils/functions';
9 |
10 | import './gnttokens.html';
11 |
12 | const TRANSACTION_TYPE_WITHDRAW = 'gnttokens_withdraw';
13 | const WITHDRAW_GAS = 150000;
14 | const CLEAR_BROKER_GAS = 150000;
15 | const DEPOSIT = 'deposit';
16 | const WITHDRAW = 'withdraw';
17 |
18 | Template.gnttokens.viewmodel({
19 | type() {
20 | const depositType = (this !== null && this !== undefined) ? this.depositType() : '';
21 | return depositType;
22 | },
23 | amount: '',
24 | lastError: '',
25 | shouldShowMaxBtn: false,
26 | fillAmount() {
27 | let amount = '0';
28 | try {
29 | if (this.type() === DEPOSIT) {
30 | amount = web3Obj.fromWei(Session.get('GNTBalance'));
31 | } else if (this.type() === WITHDRAW) {
32 | amount = web3Obj.fromWei(Tokens.findOne('W-GNT').balance);
33 | }
34 | } catch (e) {
35 | amount = '0';
36 | }
37 | this.amount(amount);
38 | },
39 | onFocus() {
40 | this.shouldShowMaxBtn(true);
41 | },
42 | onBlur() {
43 | this.shouldShowMaxBtn(false);
44 | },
45 | focusOnInput(event) {
46 | $(event.target).find('input.with-max-btn').focus();
47 | },
48 | broker() {
49 | return Session.get('GNTBroker');
50 | },
51 | brokerBalance() {
52 | const balance = Session.get('GNTBrokerBalance');
53 | if (balance !== '0') {
54 | return balance;
55 | }
56 | return 0;
57 | },
58 | clearBroker(event) {
59 | event.preventDefault();
60 | Dapple['token-wrapper'].classes.DepositBroker.at(this.broker()).clear({ gas: CLEAR_BROKER_GAS }, (txError, tx) => {
61 | if (!txError) {
62 | Session.set('GNTDepositProgress', 90);
63 | Session.set('GNTDepositProgressMessage', 'Clearing Broker... (waiting for transaction confirmation)');
64 | Session.set('GNTDepositErrorMessage', '');
65 | Transactions.add('gnttokens_clear', tx, { type: 'deposit' });
66 | } else {
67 | Session.set('GNTDepositProgress', 0);
68 | Session.set('GNTDepositProgressMessage', '');
69 | Session.set('GNTDepositErrorMessage', formatError(txError));
70 | }
71 | });
72 | },
73 | progress() {
74 | return Session.get(`GNT${uppercaseFirstLetter(this.type())}Progress`);
75 | },
76 | progressMessage() {
77 | return Session.get(`GNT${uppercaseFirstLetter(this.type())}ProgressMessage`);
78 | },
79 | errorMessage() {
80 | return Session.get(`GNT${uppercaseFirstLetter(this.type())}ErrorMessage`);
81 | },
82 | maxAmount() {
83 | let maxAmount = '0';
84 | try {
85 | if (this.type() === DEPOSIT) {
86 | maxAmount = web3Obj.fromWei(Session.get('GNTBalance'));
87 | } else if (this.type() === WITHDRAW) {
88 | maxAmount = web3Obj.fromWei(Tokens.findOne('W-GNT').balance);
89 | }
90 | } catch (e) {
91 | maxAmount = '0';
92 | }
93 | return maxAmount;
94 | },
95 | canDeposit() {
96 | try {
97 | const amount = new BigNumber(this.amount());
98 | const maxAmount = new BigNumber(this.maxAmount());
99 | return amount.gt(0) && amount.lte(maxAmount);
100 | } catch (e) {
101 | return false;
102 | }
103 | },
104 | deposit(event) {
105 | event.preventDefault();
106 | this.lastError('');
107 |
108 | if (this.type() === DEPOSIT) {
109 | // XXX EIP20
110 | Dapple.getToken('W-GNT', (error, token) => {
111 | if (!error) {
112 | Session.set('GNTDepositProgress', 10);
113 | Session.set('GNTDepositProgressMessage', 'Checking if Broker already exists...');
114 | Session.set('GNTDepositErrorMessage', '');
115 | token.getBroker.call((e, broker) => {
116 | if (!e) {
117 | // Check value of broker
118 | if (broker !== '0x0000000000000000000000000000000000000000') {
119 | const tx = Session.get('address') + Date.now();
120 | Transactions.insert({
121 | type: 'gnttokens_create_broker',
122 | tx,
123 | object: {
124 | type: DEPOSIT,
125 | amount: this.amount(),
126 | },
127 | receipt: {
128 | logs: [{ topics: ['', broker] }],
129 | },
130 | });
131 | Transactions.remove({ tx });
132 | } else {
133 | // Create broker
134 | Session.set('GNTDepositProgress', 20);
135 | Session.set('GNTDepositProgressMessage', 'Creating Broker... (waiting for your approval)');
136 | token.createBroker((txError, tx) => {
137 | if (!txError) {
138 | Session.set('GNTDepositProgress', 30);
139 | Session.set('GNTDepositProgressMessage',
140 | 'Creating Broker... (waiting for transaction confirmation)');
141 | Transactions.add('gnttokens_create_broker', tx, { type: DEPOSIT, amount: this.amount() });
142 | } else {
143 | Session.set('GNTDepositProgress', 0);
144 | Session.set('GNTDepositProgressMessage', '');
145 | Session.set('GNTDepositErrorMessage', formatError(txError));
146 | }
147 | });
148 | }
149 | }
150 | });
151 | } else {
152 | Session.set('GNTDepositProgress', 0);
153 | Session.set('GNTDepositProgressMessage', '');
154 | Session.set('GNTDepositErrorMessage', error.toString());
155 | }
156 | });
157 | } else {
158 | // XXX EIP20
159 | Dapple.getToken('W-GNT', (error, token) => {
160 | if (!error) {
161 | Session.set('GNTWithdrawProgress', 33);
162 | Session.set('GNTWithdrawProgressMessage', 'Starting unwrapping... (waiting for your approval)');
163 | Session.set('GNTWithdrawErrorMessage', '');
164 | token.withdraw(web3Obj.toWei(this.amount()), { gas: WITHDRAW_GAS }, (txError, tx) => {
165 | if (!txError) {
166 | Session.set('GNTWithdrawProgress', 66);
167 | Session.set('GNTWithdrawProgressMessage',
168 | 'Executing unwrapping... (waiting for transaction confirmation)');
169 | Transactions.add(TRANSACTION_TYPE_WITHDRAW, tx, { type: WITHDRAW, amount: this.amount() });
170 | } else {
171 | Session.set('GNTWithdrawProgress', 0);
172 | Session.set('GNTWithdrawProgressMessage', '');
173 | Session.set('GNTWithdrawErrorMessage', formatError(txError));
174 | }
175 | });
176 | } else {
177 | Session.set('GNTWithdrawProgress', 0);
178 | Session.set('GNTWithdrawProgressMessage', '');
179 | Session.set('GNTWithdrawErrorMessage', error.toString());
180 | }
181 | });
182 | }
183 | },
184 | });
185 |
--------------------------------------------------------------------------------