├── .ebextensions └── disable-npm.config ├── .gitignore ├── README.md ├── build ├── arrow_down.svg ├── asset-manifest.json ├── checkmark.svg ├── favicon.ico ├── index.html ├── logo.svg ├── manifest.json ├── service-worker.js └── static │ ├── css │ ├── main.e9241897.css │ └── main.e9241897.css.map │ └── js │ ├── main.95f7d3fb.js │ └── main.95f7d3fb.js.map ├── buildspec.yml ├── package.json ├── public ├── arrow_down.svg ├── cDAI_screenshot.png ├── checkmark.svg ├── favicon.ico ├── index.html ├── logo.svg └── manifest.json └── src ├── app.js ├── components ├── amount.js ├── button.js ├── footer.js ├── info.js ├── payment.js ├── status.js ├── token.js └── totals.js ├── css └── footer.css ├── index.css ├── index.js └── utility.js /.ebextensions/disable-npm.config: -------------------------------------------------------------------------------- 1 | # This file is controlled by the `eb-disable-npm` Node module. If you'd like to 2 | # modify it, you either should publish a new version of that module and update 3 | # to that version; or, you should uninstall that module and then edit this file 4 | # --uninstallation won't take the file with it. 5 | 6 | # This file prevents Elastic Beanstalk from trying to run `npm install` or 7 | # `npm rebuild` on its EC2 instances. See the README for why. 8 | 9 | files: 10 | "/opt/elasticbeanstalk/hooks/appdeploy/pre/50npm.sh": 11 | mode: "000755" 12 | owner: root 13 | group: users 14 | content: | 15 | #!/usr/bin/env bash 16 | # 17 | # Prevent installing or rebuilding like Elastic Beanstalk tries to do by 18 | # default. 19 | # 20 | # Note that this *overwrites* Elastic Beanstalk's default 50npm.sh script 21 | # (https://gist.github.com/wearhere/de51bb799f5099cec0ed28b9d0eb3663). 22 | 23 | "/opt/elasticbeanstalk/hooks/configdeploy/pre/50npm.sh": 24 | mode: "000755" 25 | owner: root 26 | group: users 27 | content: | 28 | #!/usr/bin/env bash 29 | # 30 | # Prevent installing or rebuilding like Elastic Beanstalk tries to do by 31 | # default. 32 | # 33 | # Note that this *overwrites* Elastic Beanstalk's default 50npm.sh script. 34 | # But their default script actually doesn't work at all, since the app 35 | # staging dir, where they try to run `npm install`, doesn't exist during 36 | # config deploys, so ebnode.py just aborts: 37 | # https://gist.github.com/wearhere/de51bb799f5099cec0ed28b9d0eb3663#file-ebnode-py-L140 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .git 2 | node_modules 3 | package-lock.json 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Documentation 2 | See the [DEXAG Docs](https://docs.dex.ag) 3 | View the app running live [cDAI.io](https://cdai.io) 4 | 5 | ## Installation 6 | Install the packages with: 7 | ``` 8 | npm install 9 | ``` 10 | 11 | ## Start the app 12 | ``` 13 | npm run start 14 | ``` 15 | 16 | ## Main SDK Usage 17 | Query DEXes for the best price and execute the trade with a web3 browser. 18 | ``` 19 | import {DEXAG} from 'dexag-sdk' 20 | const sdk = DEXAG.fromProvider(window.ethereum) 21 | 22 | // receive status messages as the client executes the trade 23 | sdk.registerStatusHandler((status, data)=>{ 24 | console.log(status, data) 25 | }); 26 | 27 | // get trade 28 | const trade = await sdk.getTrade({to: 'DAI', from: 'ETH', toAmount: 1}) 29 | 30 | // checkout 31 | const valid = await sdk.validate(trade); 32 | if (valid) { 33 | // web3 is valid, trade order 34 | sdk.trade({tx: trade}); /** Metamask opens **/ 35 | } 36 | ``` 37 | 38 | ## More information 39 | Find more information about how to build DEX trading into your platform on the [DEXAG Docs](https://docs.dex.ag) 40 | -------------------------------------------------------------------------------- /build/arrow_down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Created with Sketch. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /build/asset-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "main.css": "static/css/main.e9241897.css", 3 | "main.css.map": "static/css/main.e9241897.css.map", 4 | "main.js": "static/js/main.95f7d3fb.js", 5 | "main.js.map": "static/js/main.95f7d3fb.js.map" 6 | } -------------------------------------------------------------------------------- /build/checkmark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /build/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SettleFinance/cDAI/292fd0f7ab454611678e277bd457ccfc07a37fb2/build/favicon.ico -------------------------------------------------------------------------------- /build/index.html: -------------------------------------------------------------------------------- 1 | cDAI Compound DAI
-------------------------------------------------------------------------------- /build/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Created with Sketch. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /build/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /build/service-worker.js: -------------------------------------------------------------------------------- 1 | "use strict";var precacheConfig=[["/index.html","278b556ad1d692d30f5f1eb4ab26d941"],["/static/css/main.e9241897.css","0a5de09ac617e6ff2a5a733e4e60eede"],["/static/js/main.95f7d3fb.js","6d70060e32da1fc10af7bb6721cdc1c4"]],cacheName="sw-precache-v3-sw-precache-webpack-plugin-"+(self.registration?self.registration.scope:""),ignoreUrlParametersMatching=[/^utm_/],addDirectoryIndex=function(e,t){var n=new URL(e);return"/"===n.pathname.slice(-1)&&(n.pathname+=t),n.toString()},cleanResponse=function(t){return t.redirected?("body"in t?Promise.resolve(t.body):t.blob()).then(function(e){return new Response(e,{headers:t.headers,status:t.status,statusText:t.statusText})}):Promise.resolve(t)},createCacheKey=function(e,t,n,r){var a=new URL(e);return r&&a.pathname.match(r)||(a.search+=(a.search?"&":"")+encodeURIComponent(t)+"="+encodeURIComponent(n)),a.toString()},isPathWhitelisted=function(e,t){if(0===e.length)return!0;var n=new URL(t).pathname;return e.some(function(e){return n.match(e)})},stripIgnoredUrlParameters=function(e,n){var t=new URL(e);return t.hash="",t.search=t.search.slice(1).split("&").map(function(e){return e.split("=")}).filter(function(t){return n.every(function(e){return!e.test(t[0])})}).map(function(e){return e.join("=")}).join("&"),t.toString()},hashParamName="_sw-precache",urlsToCacheKeys=new Map(precacheConfig.map(function(e){var t=e[0],n=e[1],r=new URL(t,self.location),a=createCacheKey(r,hashParamName,n,/\.\w{8}\./);return[r.toString(),a]}));function setOfCachedUrls(e){return e.keys().then(function(e){return e.map(function(e){return e.url})}).then(function(e){return new Set(e)})}self.addEventListener("install",function(e){e.waitUntil(caches.open(cacheName).then(function(r){return setOfCachedUrls(r).then(function(n){return Promise.all(Array.from(urlsToCacheKeys.values()).map(function(t){if(!n.has(t)){var e=new Request(t,{credentials:"same-origin"});return fetch(e).then(function(e){if(!e.ok)throw new Error("Request for "+t+" returned a response with status "+e.status);return cleanResponse(e).then(function(e){return r.put(t,e)})})}}))})}).then(function(){return self.skipWaiting()}))}),self.addEventListener("activate",function(e){var n=new Set(urlsToCacheKeys.values());e.waitUntil(caches.open(cacheName).then(function(t){return t.keys().then(function(e){return Promise.all(e.map(function(e){if(!n.has(e.url))return t.delete(e)}))})}).then(function(){return self.clients.claim()}))}),self.addEventListener("fetch",function(t){if("GET"===t.request.method){var e,n=stripIgnoredUrlParameters(t.request.url,ignoreUrlParametersMatching),r="index.html";(e=urlsToCacheKeys.has(n))||(n=addDirectoryIndex(n,r),e=urlsToCacheKeys.has(n));var a="/index.html";!e&&"navigate"===t.request.mode&&isPathWhitelisted(["^(?!\\/__).*"],t.request.url)&&(n=new URL(a,self.location).toString(),e=urlsToCacheKeys.has(n)),e&&t.respondWith(caches.open(cacheName).then(function(e){return e.match(urlsToCacheKeys.get(n)).then(function(e){if(e)return e;throw Error("The cached response that was expected is missing.")})}).catch(function(e){return console.warn('Couldn\'t serve response for "%s" from cache: %O',t.request.url,e),fetch(t.request)}))}}); -------------------------------------------------------------------------------- /build/static/css/main.e9241897.css: -------------------------------------------------------------------------------- 1 | .footer-container{text-align:center;color:#757575;font-size:13px;line-height:16px;font-family:Overpass Mono;margin-bottom:25px}.standalone-link{text-align:center;display:inline-block;color:#757575;font-weight:300}.footer-text{width:315px;margin:auto;text-align:left;margin-top:50px}.footer-text a{color:#757575}.footer-links{margin-top:50px;margin-bottom:10px}.footer-links a{display:inline-block;color:#757575}body{margin:0;padding:0;font-family:aleo;background:#fdf7e7}input[type=number]{-moz-appearance:textfield}input::-webkit-inner-spin-button,input::-webkit-outer-spin-button{-webkit-appearance:none}.container{text-align:center;display:block;margin:40px auto;color:#559995;padding:50px;width:300px;border-radius:5px;margin-top:0;padding-bottom:0}.title{font-weight:400;text-align:left}.title p{display:inline-block;margin:0;width:85px}.status-message div{font-size:14px;font-weight:400;color:#386861;margin-bottom:0;background:#66cbb71a;padding:13px 10px;margin-top:20px}.status-message h3{font-size:15px;font-weight:500;margin-bottom:10px;margin-top:0}.status-message h3 img{width:15px;margin-right:10px;margin-bottom:-2px}.status-message h2{margin:auto;font-size:13px;font-weight:400;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:pointer;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}.status-message h2:hover{color:#000}.status-message a{color:#559995}.container .price{font-weight:300}.container .price strong{margin:0 7px}.container .price-total{margin-top:35px;font-size:15px;margin-bottom:20px}.container .price-total span{text-transform:capitalize}.container .price-total{color:#386861}.container button{background:#f76010;border:none;color:#fff;padding:10px 0;font-size:14px;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:7px;width:100%}.container button:disabled{background:#f7601073;cursor:text}.container select{background:transparent;color:#559995;padding:5px 10px;cursor:pointer;border-radius:2px;border:none;font-size:15px;font-family:aleo;-webkit-appearance:none;-moz-appearance:none;appearance:none}.container select option{color:#559995;background:#fdf7e7}.container .amount{font-size:14px;display:inline-block}.container .amount input{background:transparent;border:1px solid #a6a6a6;padding:5px 10px;border:1px solid #559995;color:#757575;width:80px;margin-top:20px;border-radius:2px;margin-right:35px}.info{text-align:center;color:#fff;margin-top:70px}.info h3{margin-bottom:0;color:#f76010}.info a,.info h3{margin-top:10px;font-weight:300}.info a{color:#c7c7c7;display:inline-block;font-size:14px}.info img{margin-top:30px;width:330px;max-width:75%}.buy-sell span{margin-right:15px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:pointer}.buy-sell span.selected{border-bottom:1px solid #559995}.token-dropdown-container{position:relative;display:inline-block;width:65px}.token-dropdown-container select{width:100%;padding-left:1px;outline:none}.token-dropdown-container img{position:absolute;top:9px;margin-left:-12px}.container .amount .input-loading{color:#fdf7e7}@media(max-width:500px){.container{-webkit-box-sizing:border-box;box-sizing:border-box;padding:30px 5px;width:95%}.info h3{font-size:16px;padding:0 15px}} 2 | /*# sourceMappingURL=main.e9241897.css.map*/ -------------------------------------------------------------------------------- /build/static/css/main.e9241897.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["css/footer.css","index.css"],"names":[],"mappings":"AAAA,kBACE,kBACA,cACA,eACA,iBACA,0BACA,kBAAoB,CAEtB,iBACE,kBACA,qBACA,cACA,eAAiB,CAEnB,aACE,YACA,YACA,gBACA,eAAiB,CAEnB,eACE,aAAe,CAEjB,cACE,gBACA,kBAAoB,CAEtB,gBACE,qBACA,aAAe,CC7BjB,KACE,SACA,UACA,iBACA,kBAAoB,CAGtB,mBACI,yBAA0B,CAG9B,kEAEI,uBAAyB,CAG7B,WACE,kBACA,cACA,iBACA,cACA,aACA,YACA,kBACA,aACA,gBAAkB,CAGpB,OACE,gBACA,eAAiB,CAGnB,SACE,qBACA,SACA,UAAY,CAGd,oBACE,eACA,gBACA,cACA,gBACA,qBACA,kBACA,eAAiB,CAGnB,mBACE,eACA,gBACA,mBACA,YAAc,CAGhB,uBACE,WACA,kBACA,kBAAoB,CAGtB,mBACE,YACA,eACA,gBACA,yBACG,sBACC,qBACI,iBACR,eACA,0BACA,uBACA,iBAAmB,CAGrB,yBACE,UAAa,CAGf,kBACE,aAAe,CAGjB,kBACE,eAAiB,CAGnB,yBACE,YAAc,CAGhB,wBACE,gBACA,eACA,kBAAoB,CAGtB,6BACE,yBAA2B,CAG7B,wBACE,aAAe,CAGjB,kBACE,mBACA,YACA,WACA,eACA,eACA,eACA,yBACG,sBACC,qBACI,iBACR,kBACA,UAAY,CAGd,2BACE,qBACA,WAAa,CAGf,kBACE,uBACA,cACA,iBACA,eACA,kBACA,YACA,eACA,iBACA,wBACA,qBACA,eAAiB,CAGnB,yBACE,cACA,kBAAoB,CAGtB,mBACE,eACA,oBAAsB,CAGxB,yBACE,uBACA,yBACA,iBACA,yBACA,cACA,WACA,gBACA,kBACA,iBAAmB,CAGrB,MACE,kBACA,WACA,eAAiB,CAGnB,SACE,gBACA,aAAe,CAKjB,iBAJE,gBACA,eAAiB,CASlB,QALC,cAEA,qBACA,cAAgB,CAIlB,UACE,gBACA,YACA,aAAe,CAGjB,eACE,kBACA,yBACG,sBACC,qBACI,iBACR,cAAgB,CAGlB,wBACE,+BAAiC,CAGnC,0BACE,kBACA,qBACA,UAAY,CAGd,iCACE,WACA,iBACA,YAAc,CAGhB,8BACE,kBACA,QACA,iBAAmB,CAGrB,kCACE,aAAe,CAGjB,wBACE,WACE,8BACQ,sBACR,iBACA,SAAW,CAEb,SACE,eACA,cAAgB,CACjB","file":"static/css/main.e9241897.css","sourcesContent":[".footer-container{\n text-align: center;\n color: #757575;\n font-size: 13px;\n line-height: 16px;\n font-family: 'Overpass Mono';\n margin-bottom: 25px;\n}\n.standalone-link{\n text-align: center;\n display: inline-block;\n color: #757575;\n font-weight: 300;\n}\n.footer-text{\n width: 315px;\n margin: auto;\n text-align: left;\n margin-top: 50px;\n}\n.footer-text a{\n color: #757575;\n}\n.footer-links{\n margin-top: 50px;\n margin-bottom: 10px;\n}\n.footer-links a{\n display: inline-block;\n color: #757575;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/css/footer.css","body {\n margin: 0;\n padding: 0;\n font-family: 'aleo';\n background: #fdf7e7;\n}\n\ninput[type='number'] {\n -moz-appearance:textfield;\n}\n\ninput::-webkit-outer-spin-button,\ninput::-webkit-inner-spin-button {\n -webkit-appearance: none;\n}\n\n.container {\n text-align: center;\n display: block;\n margin: 40px auto;\n color: #559995;\n padding: 50px;\n width: 300px;\n border-radius: 5px;\n margin-top: 0;\n padding-bottom: 0;\n}\n\n.title {\n font-weight: 400;\n text-align: left;\n}\n\n.title p {\n display: inline-block;\n margin: 0;\n width: 85px;\n}\n\n.status-message div {\n font-size: 14px;\n font-weight: 400;\n color: rgb(56, 104, 97);\n margin-bottom: 0;\n background: #66cbb71a;\n padding: 13px 10px;\n margin-top: 20px;\n}\n\n.status-message h3 {\n font-size: 15px;\n font-weight: 500;\n margin-bottom: 10px;\n margin-top: 0;\n}\n\n.status-message h3 img {\n width: 15px;\n margin-right: 10px;\n margin-bottom: -2px;\n}\n\n.status-message h2 {\n margin: auto;\n font-size: 13px;\n font-weight: 400;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n cursor: pointer;\n width: -webkit-fit-content;\n width: -moz-fit-content;\n width: fit-content;\n}\n\n.status-message h2:hover {\n color: black;\n}\n\n.status-message a {\n color: #559995;\n}\n\n.container .price {\n font-weight: 300;\n}\n\n.container .price strong {\n margin: 0 7px;\n}\n\n.container .price-total {\n margin-top: 35px;\n font-size: 15px;\n margin-bottom: 20px;\n}\n\n.container .price-total span {\n text-transform: capitalize;\n}\n\n.container .price-total{\n color: #386861;\n}\n\n.container button {\n background: #F76010;\n border: none;\n color: white;\n padding: 10px 0;\n font-size: 14px;\n cursor: pointer;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n border-radius: 7px;\n width: 100%;\n}\n\n.container button:disabled {\n background: #f7601073;\n cursor: text;\n}\n\n.container select {\n background: transparent;\n color: #559995;\n padding: 5px 10px;\n cursor: pointer;\n border-radius: 2px;\n border: none;\n font-size: 15px;\n font-family: 'aleo';\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n.container select option {\n color: #559995;\n background: #fdf7e7;\n}\n\n.container .amount {\n font-size: 14px;\n display: inline-block;\n}\n\n.container .amount input {\n background: transparent;\n border: 1px solid rgb(166, 166, 166);\n padding: 5px 10px;\n border: 1px solid #559995;\n color: #757575;\n width: 80px;\n margin-top: 20px;\n border-radius: 2px;\n margin-right: 35px;\n}\n\n.info {\n text-align: center;\n color: white;\n margin-top: 70px;\n}\n\n.info h3 {\n margin-bottom: 0;\n color: #F76010;\n margin-top: 10px;\n font-weight: 300;\n}\n\n.info a {\n color: #c7c7c7;\n margin-top: 10px;\n display: inline-block;\n font-size: 14px;\n font-weight: 300;\n}\n\n.info img {\n margin-top: 30px;\n width: 330px;\n max-width: 75%;\n}\n\n.buy-sell span{\n margin-right: 15px;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n cursor: pointer;\n}\n\n.buy-sell span.selected{\n border-bottom: 1px solid #559995;\n}\n\n.token-dropdown-container{\n position: relative;\n display: inline-block;\n width: 65px;\n}\n\n.token-dropdown-container select{\n width: 100%;\n padding-left: 1px;\n outline: none;\n}\n\n.token-dropdown-container img{\n position: absolute;\n top: 9px;\n margin-left: -12px;\n}\n\n.container .amount .input-loading{\n color: #fdf7e7;\n}\n\n@media(max-width:500px) {\n .container {\n -webkit-box-sizing: border-box;\n box-sizing: border-box;\n padding: 30px 5px;\n width: 95%;\n }\n .info h3 {\n font-size: 16px;\n padding: 0 15px;\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/index.css"],"sourceRoot":""} -------------------------------------------------------------------------------- /buildspec.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | env: 4 | parameter-store: 5 | build_ssh_key: "build-ssh-key-secure" 6 | 7 | phases: 8 | install: 9 | runtime-versions: 10 | nodejs: 10 11 | build: 12 | commands: 13 | - mkdir -p ~/.ssh 14 | - echo "$build_ssh_key" > ~/.ssh/id_rsa 15 | - chmod 600 ~/.ssh/id_rsa 16 | - echo Beginning Build 17 | - npm install 18 | - npm run build 19 | - ls -la 20 | - echo Build Complete 21 | artifacts: 22 | files: 23 | - '**/*' -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cDAI.io", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "cross-env": "^5.2.0", 7 | "dexag-sdk": "^2.1.10", 8 | "isomorphic-fetch": "^2.2.1", 9 | "jquery": "^3.3.1", 10 | "react": "^16.4.1", 11 | "react-dom": "^16.4.1", 12 | "react-router": "^4.3.1", 13 | "react-router-dom": "^4.3.1", 14 | "react-router-native": "^4.3.0", 15 | "react-scripts": "1.1.4" 16 | }, 17 | "scripts": { 18 | "start": "cross-env PORT=3000 react-scripts start", 19 | "start:prod": "PORT=3000 node node_modules/react-scripts/bin/react-scripts start", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test --env=jsdom", 22 | "eject": "react-scripts eject" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /public/arrow_down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Created with Sketch. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /public/cDAI_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SettleFinance/cDAI/292fd0f7ab454611678e277bd457ccfc07a37fb2/public/cDAI_screenshot.png -------------------------------------------------------------------------------- /public/checkmark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SettleFinance/cDAI/292fd0f7ab454611678e277bd457ccfc07a37fb2/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | cDAI.io DEX Aggregator 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 37 | 38 | 39 | 40 | 43 |
44 | 45 | 46 | -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Created with Sketch. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import DEXAG from 'dexag-sdk' 3 | 4 | // Components 5 | import Totals from './components/totals' 6 | import Status from './components/status' 7 | import Payment from './components/payment' 8 | import Info from './components/info' 9 | import Footer from './components/footer' 10 | import Button from './components/button' 11 | import Utility from './utility' 12 | 13 | const sdk = DEXAG.fromProvider(window.ethereum) 14 | 15 | const orderModel = { 16 | metadata: { 17 | source: {} 18 | } 19 | } 20 | 21 | class App extends Component { 22 | constructor(props){ 23 | super(props); 24 | this.state = { 25 | order: orderModel, 26 | input: { 27 | top: 1, 28 | bottom: 1 29 | }, 30 | pair: { 31 | to: 'cDAI', 32 | from: 'DAI' 33 | }, 34 | type: 'buy', 35 | purchase_type: false, 36 | loaded: false 37 | } 38 | } 39 | componentDidMount(){ 40 | // register dexag callback for status messages 41 | sdk.registerStatusHandler((status, data)=>{ 42 | this.setState({web3Status: {status, data}}) 43 | this.timeoutStatus(status) 44 | console.log(status) 45 | }); 46 | // find the price for default pair 47 | this.findTrades() 48 | } 49 | findTrades = async(purchase_type) =>{ 50 | let {input, pair, type} = this.state; 51 | // reset order in UI 52 | this.setState({order: orderModel}) 53 | // get the best price for the pair and amount 54 | var request = {to: type=='buy' ? pair.to:pair.from, from: type=='buy' ? pair.from:pair.to, dex: 'best'}; 55 | // handle top/bottom inputs 56 | if(purchase_type==undefined) purchase_type = this.state.purchase_type 57 | request = Utility.inputAmount({type, request, purchase_type, input}) 58 | // get trade details 59 | let trade = await sdk.getTrade(request) 60 | this.setState({order: trade, purchase_type, loaded: true}, ()=>this.setInputs()) 61 | console.log(trade) 62 | } 63 | changeAmount = (amount, type) => { 64 | // update amount 65 | let {input} = this.state; 66 | input[type?'bottom':'top'] = amount; 67 | this.setState({input, order: orderModel}, ()=>{ 68 | Utility.debounce(this.findTrades, type) 69 | }) 70 | } 71 | changeToken = (type, token) =>{ 72 | var pair = this.state.pair; 73 | // reset order in UI 74 | this.setState({order: orderModel}) 75 | // change the token pair 76 | pair[type] = token; 77 | this.setState({pair: pair},()=>{ 78 | this.findTrades() 79 | }) 80 | } 81 | trade = async() =>{ 82 | let {order} = this.state; 83 | // start web3 validation process 84 | const valid = await sdk.validate(order); 85 | if (valid) { 86 | // web3 is valid, trade order 87 | sdk.trade(order); 88 | } 89 | } 90 | closeStatus = () => { 91 | // close status message container 92 | this.setState({web3Status: {}}) 93 | } 94 | timeoutStatus = (status) => { 95 | // hide rejected message 96 | if(status=='rejected') setTimeout(()=>{this.closeStatus()}, 3500) 97 | } 98 | changeType = async (type) => { 99 | var prev_type = this.state.type; 100 | this.setState({type}, ()=>{ 101 | if(prev_type != type) this.findTrades() 102 | }) 103 | } 104 | setInputs = () =>{ 105 | let {input, pair, type, purchase_type, order} = this.state; 106 | let {source} = order.metadata; 107 | // update inputs with price 108 | input = Utility.inputPrice({purchase_type, type, source, input}) 109 | this.setState({input}) 110 | } 111 | render() { 112 | let {source} = this.state.order.metadata; 113 | let {order, pair, web3Status, input, type, loaded} = this.state; 114 | return ( 115 |
116 | 117 | 118 |
119 | 128 | 129 | 130 | 131 |
142 | 143 |
145 | ); 146 | } 147 | } 148 | 149 | export default App; 150 | -------------------------------------------------------------------------------- /src/components/amount.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class Amount extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | this.state = { 7 | }; 8 | } 9 | render() { 10 | let {changeAmount, pair, bottom, input, loaded} = this.props; 11 | if(pair==undefined) pair = {} 12 | return ( 13 |
14 | changeAmount(e.target.value, bottom)} value={input[bottom?'bottom':'top']} /> {bottom&&pair.to} 15 |
16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/components/button.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Utility from '../utility' 4 | 5 | export default class Button extends React.Component { 6 | constructor(props) { 7 | super(props); 8 | this.state = { 9 | }; 10 | } 11 | render() { 12 | let {order, orderModel, trade, input, source, pair, type} = this.props; 13 | return ( 14 | 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/components/footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import '../css/footer.css' 4 | 5 | export default class Footer extends React.Component { 6 | constructor(props) { 7 | super(props); 8 | this.state = { 9 | }; 10 | } 11 | render() { 12 | return ( 13 |
14 | 15 | What is cDAI? 16 | 17 |
18 | cDAI.io searches Kyber, Bancor, Uniswap and Radar Relay. This site is powered by the DEX.AG SDK. 19 | Please fork it. 20 |
21 |
22 | DEX.AG| 23 | Twitter| 24 | Discord| 25 | Blog| 26 | Feedback 27 |
28 | 29 | Concourse Open Community 30 | 31 |
32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/components/info.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class Info extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | this.state = { 7 | rate: 0 8 | }; 9 | } 10 | componentDidMount(){ 11 | // get dai lend rate 12 | fetch('https://experimental.defipulse.com/get_dai_rate') 13 | .then(response => response.json()) 14 | .then(data => { 15 | this.setState({rate: data.current_DAI_rate}) 16 | console.log(data) 17 | }) 18 | .catch(error => console.error(error)) 19 | } 20 | format = (rate) => { 21 | return parseInt(rate * 100)/100 22 | } 23 | render() { 24 | let {rate} = this.state; 25 | return ( 26 |
27 | cDAI on DEXAG 28 |

Current rate: {rate?`${this.format(rate)}%`:'-'} APR

29 |
30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/components/payment.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Amount from './amount' 4 | import Token from './token' 5 | 6 | export default class Payment extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = { 10 | }; 11 | } 12 | render() { 13 | let {pair, changeAmount, findTrades, changeToken, changeType, type, input, loaded} = this.props; 14 | return ( 15 |
16 |
17 | changeType('buy')}>buy 18 | changeType('sell')}>sell 19 |
20 |
21 |

{type=='buy'?'pay':'receive'}:

22 | 23 | 24 |
25 |
26 |

for:

27 | 28 |
29 |
30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/components/status.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class Status extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | this.state = { 7 | }; 8 | } 9 | etherScan = (data) => { 10 | if(!data) return; 11 | return {data.substring(0,8)}; 12 | } 13 | render() { 14 | let {web3Status, closeStatus} = this.props; 15 | if(!web3Status) web3Status = {} // default state 16 | let{status, data} = web3Status; 17 | return ( 18 |
19 | {status&&
20 | {status=='init'&&

Preparing the trade…

} 21 | {status=='web3_undefined'&&

Enable your wallet (metamask, coinbase wallet, ect) and reload the page

} 22 | {status=='network'&&

Switch to main Ethereum network

} 23 | {status=='balance'&&

Your balance is insufficient

} 24 | {status=='rejected'&&

Trade rejected

} 25 | {status=='request_wrap'&&

Wrap ETH to fill the trade

} 26 | {status=='allowance'&&

Unlock the token to continue

} 27 | {status=='bad_tx'&&

The transaction is not valid, try refreshing the page.

} 28 | {status=='unlock_wallet'&&

Unlock your web3 wallet

} 29 | {status=='failed'&&

Trade failed - {this.etherScan(data)}

} 30 | {status=='bancor_notice'&&

Bancor Notice: Changing the gas price will result in a failed transaction

} 31 | {(status=='send_trade'||status=='send_wrap'||status=='send_approve')&&

Waiting to be mined - {this.etherScan(data)}

} 32 | {(status=='mined_trade'||status=='mined_approve'||status=='mined_wrap')&&

Transaction mined - {this.etherScan(data)}

} 33 |

Close

34 |
} 35 |
36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/components/token.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class Token extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | this.state = { 7 | }; 8 | } 9 | render() { 10 | let {type, changeToken, pair} = this.props; 11 | let token = type=='to'?'cDAI':'DAI'; 12 | let tokenLength = 25 + (pair[type].length * 10); 13 | return ( 14 | 15 | 32 | 33 | 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/components/totals.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Utility from '../utility' 4 | 5 | export default class Totals extends React.Component { 6 | constructor(props) { 7 | super(props); 8 | this.state = { 9 | }; 10 | } 11 | render() { 12 | let {pair, source} = this.props; 13 | let {price, dex} = source; 14 | return ( 15 |
16 |
17 | {price?
18 | Best price found on: {Utility.cleanDex(dex)} 19 |
:'Finding best price..'} 20 |
21 |
22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/css/footer.css: -------------------------------------------------------------------------------- 1 | .footer-container{ 2 | text-align: center; 3 | color: #757575; 4 | font-size: 13px; 5 | line-height: 16px; 6 | font-family: 'Overpass Mono'; 7 | margin-bottom: 25px; 8 | } 9 | .standalone-link{ 10 | text-align: center; 11 | display: inline-block; 12 | color: #757575; 13 | font-weight: 300; 14 | } 15 | .footer-text{ 16 | width: 315px; 17 | margin: auto; 18 | text-align: left; 19 | margin-top: 40px; 20 | } 21 | .footer-text a{ 22 | color: #757575; 23 | } 24 | .footer-links{ 25 | margin-top: 50px; 26 | margin-bottom: 10px; 27 | } 28 | .footer-links a{ 29 | display: inline-block; 30 | color: #757575; 31 | margin: 0 6px; 32 | } 33 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: 'aleo'; 5 | background: #fdf7e7; 6 | } 7 | 8 | input[type='number'] { 9 | -moz-appearance:textfield; 10 | } 11 | 12 | input::-webkit-outer-spin-button, 13 | input::-webkit-inner-spin-button { 14 | -webkit-appearance: none; 15 | } 16 | 17 | .container { 18 | text-align: center; 19 | display: block; 20 | margin: 40px auto; 21 | color: #559995; 22 | padding: 50px; 23 | width: 300px; 24 | border-radius: 5px; 25 | margin-top: 0; 26 | padding-bottom: 0; 27 | } 28 | 29 | .title { 30 | font-weight: 400; 31 | text-align: left; 32 | } 33 | 34 | .title p { 35 | display: inline-block; 36 | margin: 0; 37 | width: 85px; 38 | } 39 | 40 | .status-message div { 41 | font-size: 14px; 42 | font-weight: 400; 43 | color: rgb(56, 104, 97); 44 | margin-bottom: 0; 45 | background: #66cbb71a; 46 | padding: 13px 10px; 47 | margin-top: 20px; 48 | } 49 | 50 | .status-message h3 { 51 | font-size: 15px; 52 | font-weight: 500; 53 | margin-bottom: 10px; 54 | margin-top: 0; 55 | } 56 | 57 | .status-message h3 img { 58 | width: 15px; 59 | margin-right: 10px; 60 | margin-bottom: -2px; 61 | } 62 | 63 | .status-message h2 { 64 | margin: auto; 65 | font-size: 13px; 66 | font-weight: 400; 67 | user-select: none; 68 | cursor: pointer; 69 | width: fit-content; 70 | } 71 | 72 | .status-message h2:hover { 73 | color: black; 74 | } 75 | 76 | .status-message a { 77 | color: #559995; 78 | } 79 | 80 | .container .price { 81 | font-weight: 300; 82 | } 83 | 84 | .container .price strong { 85 | margin: 0 7px; 86 | } 87 | 88 | .container .price-total { 89 | margin-top: 35px; 90 | font-size: 15px; 91 | margin-bottom: 20px; 92 | } 93 | 94 | .container .price-total span { 95 | text-transform: capitalize; 96 | } 97 | 98 | .container .price-total{ 99 | color: #386861; 100 | } 101 | 102 | .container button { 103 | background: #F76010; 104 | border: none; 105 | color: white; 106 | padding: 10px 0; 107 | font-size: 14px; 108 | cursor: pointer; 109 | user-select: none; 110 | border-radius: 7px; 111 | width: 100%; 112 | } 113 | 114 | .container button:disabled { 115 | background: #f7601073; 116 | cursor: text; 117 | } 118 | 119 | .container select { 120 | background: transparent; 121 | color: #559995; 122 | padding: 5px 10px; 123 | cursor: pointer; 124 | border-radius: 2px; 125 | border: none; 126 | font-size: 15px; 127 | font-family: 'aleo'; 128 | -webkit-appearance: none; 129 | -moz-appearance: none; 130 | appearance: none; 131 | } 132 | 133 | .container select option { 134 | color: #559995; 135 | background: #fdf7e7; 136 | } 137 | 138 | .container .amount { 139 | font-size: 14px; 140 | display: inline-block; 141 | } 142 | 143 | .container .amount input { 144 | background: transparent; 145 | border: 1px solid rgb(166, 166, 166); 146 | padding: 5px 10px; 147 | border: 1px solid #559995; 148 | color: #757575; 149 | width: 90px; 150 | margin-top: 20px; 151 | border-radius: 2px; 152 | margin-right: 30px; 153 | font-size: 14px; 154 | } 155 | 156 | .info { 157 | text-align: center; 158 | color: white; 159 | margin-top: 20px; 160 | } 161 | 162 | .info h3 { 163 | margin-bottom: 0; 164 | color: #F76010; 165 | margin-top: 10px; 166 | font-weight: 300; 167 | } 168 | 169 | .info a { 170 | color: #c7c7c7; 171 | margin-top: 10px; 172 | display: inline-block; 173 | font-size: 14px; 174 | font-weight: 300; 175 | } 176 | 177 | .info img { 178 | margin-top: 30px; 179 | width: 330px; 180 | max-width: 75%; 181 | } 182 | 183 | .buy-sell span{ 184 | margin-right: 15px; 185 | user-select: none; 186 | cursor: pointer; 187 | } 188 | 189 | .buy-sell span.selected{ 190 | border-bottom: 1px solid #559995; 191 | } 192 | 193 | .token-dropdown-container{ 194 | position: relative; 195 | display: inline-block; 196 | width: 65px; 197 | } 198 | 199 | .token-dropdown-container select{ 200 | width: 100%; 201 | padding-left: 1px; 202 | outline: none; 203 | line-height: 15px; 204 | padding-right: 0; 205 | } 206 | 207 | .token-dropdown-container img{ 208 | position: absolute; 209 | top: 7.5px; 210 | margin-left: -16px; 211 | z-index: -1; 212 | } 213 | 214 | .container .amount .input-loading{ 215 | color: #fdf7e7; 216 | } 217 | 218 | @media(max-width:500px) { 219 | .container { 220 | box-sizing: border-box; 221 | padding: 30px 5px; 222 | width: 95%; 223 | } 224 | .info h3 { 225 | font-size: 16px; 226 | padding: 0 15px; 227 | } 228 | .title{ 229 | width: fit-content; 230 | margin: auto; 231 | } 232 | .footer-text{ 233 | max-width: 95%; 234 | } 235 | .container button{ 236 | width: 290px; 237 | } 238 | .container .amount input{ 239 | margin-right: 30px; 240 | } 241 | .footer-links span{ 242 | display: none; 243 | } 244 | .footer-links a{ 245 | margin: 2px 5px; 246 | } 247 | .footer-container{ 248 | width: 290px; 249 | margin: 25px auto; 250 | } 251 | } 252 | @media(min-height: 750px){ 253 | .info{ 254 | margin-top: 60px; 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | // Components 5 | import App from './app'; 6 | 7 | // Style 8 | import './index.css'; 9 | 10 | ReactDOM.render( 11 |
12 |
13 | 14 |
15 |
, document.getElementById('root')); 16 | -------------------------------------------------------------------------------- /src/utility.js: -------------------------------------------------------------------------------- 1 | var Utility = { 2 | formatPrice: (price) => { 3 | // handle large and small values 4 | price = parseFloat(price) 5 | if(Number.isInteger(price)) return price; 6 | return price>1?price.toFixed(2):price.toPrecision(4) 7 | }, 8 | cleanDex: (dex) => { 9 | // format text in UI 10 | if(dex!=undefined) return dex.replace(/-/g,' ').toLowerCase() 11 | return dex 12 | }, 13 | debounce: (func, args) => { 14 | if (this.timer) { 15 | clearTimeout(this.timer) 16 | } 17 | this.timer = setTimeout(function() { 18 | func(args) 19 | this.timer = null; 20 | }.bind(this), 750); 21 | }, 22 | inputAmount: ({type, request, purchase_type, input}) => { 23 | if(purchase_type){ // bottom 24 | request[type=='buy'?'toAmount':'fromAmount'] = input['bottom'] 25 | }else if(!purchase_type){ // top 26 | request[type=='buy'?'fromAmount':'toAmount'] = input['top'] 27 | } 28 | return request 29 | }, 30 | inputPrice: ({purchase_type, type, source, input}) => { 31 | if(purchase_type){ 32 | input['top'] = Utility.formatPrice(input['bottom']*source.price) 33 | }else if(!purchase_type){ 34 | input['bottom'] = Utility.formatPrice(input['top']*source.price) 35 | } 36 | return input; 37 | } 38 | } 39 | export default Utility; 40 | --------------------------------------------------------------------------------