Merge pull request #7688 from Gamboster/feat/shapeshift1

[V4] Feat: shapeshift implementation
This commit is contained in:
Gustavo Maximiliano Cortez 2017-12-29 17:59:48 -03:00 committed by GitHub
commit 4ed4abe5f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1467 additions and 23 deletions

View File

@ -14,6 +14,7 @@ import { CoinbaseProvider } from '../providers/coinbase/coinbase';
import { AmazonProvider } from '../providers/amazon/amazon';
import { BitPayCardProvider } from '../providers/bitpay-card/bitpay-card';
import { MercadoLibreProvider } from '../providers/mercado-libre/mercado-libre';
import { ShapeshiftProvider } from '../providers/shapeshift/shapeshift';
//pages
import { TabsPage } from '../pages/tabs/tabs';
@ -42,7 +43,8 @@ export class CopayApp {
private coinbaseProvider: CoinbaseProvider,
private amazonProvider: AmazonProvider,
private bitPayCardProvider: BitPayCardProvider,
private mercadoLibreProvider: MercadoLibreProvider
private mercadoLibreProvider: MercadoLibreProvider,
private shapeshiftProvider: ShapeshiftProvider,
) {
this.initializeApp();
@ -111,5 +113,6 @@ export class CopayApp {
this.glideraProvider.register();
this.coinbaseProvider.setCredentials();
this.coinbaseProvider.register();
this.shapeshiftProvider.register();
}
}

View File

@ -76,6 +76,10 @@ import { BuyMercadoLibrePage } from '../pages/integrations/mercado-libre/buy-mer
import { MercadoLibreCardDetailsPage } from '../pages/integrations/mercado-libre/mercado-libre-card-details/mercado-libre-card-details';
import { MercadoLibreCardsPage } from '../pages/integrations/mercado-libre/mercado-libre-cards/mercado-libre-cards';
import { MercadoLibrePage } from '../pages/integrations/mercado-libre/mercado-libre';
// Integrations: ShapeShift
import { ShapeshiftConfirmPage } from '../pages/integrations/shapeshift/shapeshift-confirm/shapeshift-confirm';
import { ShapeshiftPage } from '../pages/integrations/shapeshift/shapeshift';
import { ShapeshiftShiftPage } from '../pages/integrations/shapeshift/shapeshift-shift/shapeshift-shift';
/*Includes */
import { FeedbackCardPage } from '../pages/includes/feedback-card/feedback-card';
@ -176,6 +180,7 @@ import { ProfileProvider } from '../providers/profile/profile';
import { PushNotificationsProvider } from '../providers/push-notifications/push-notifications';
import { RateProvider } from '../providers/rate/rate';
import { ReleaseProvider } from '../providers/release/release';
import { ShapeshiftProvider } from '../providers/shapeshift/shapeshift';
import { ScanProvider } from '../providers/scan/scan';
import { TimeProvider } from '../providers/time/time';
import { TouchIdProvider } from '../providers/touchid/touchid';
@ -251,11 +256,14 @@ let pages: any = [
PinModalPage,
ProposalsPage,
ReceivePage,
SendPage,
ScanPage,
SendPage,
SettingsPage,
SellCoinbasePage,
SellGlideraPage,
ShapeshiftConfirmPage,
ShapeshiftPage,
ShapeshiftShiftPage,
TermsOfUsePage,
MercadoLibreCardDetailsPage,
MercadoLibreCardsPage,
@ -323,6 +331,7 @@ let providers: any = [
PushNotificationsProvider,
RateProvider,
ReleaseProvider,
ShapeshiftProvider,
StatusBar,
SplashScreen,
ScanProvider,

View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 223 223" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
<g transform="matrix(1,0,0,1,0,-65)">
<g transform="matrix(0.557484,0,0,0.553806,46,81.643)">
<path d="M216,82.1L230.5,-0.7L169.9,24.6L103.3,24.6L42.7,-0.8L57.3,82.1L43.6,128.3L56.5,136.4L0,186L0,232.6L64.6,321.6L125.5,342L125.6,342.1L173.2,317.7L173.3,317.6L173.3,281.5L146.3,266.7L188.7,153.8L188.4,153.8L229.6,128.3L216,82.1Z" style="fill:rgb(39,60,81);fill-rule:nonzero;"/>
</g>
<g transform="matrix(0.547536,0,0,0.548347,46,83.5105)">
<clipPath id="_clip1">
<path d="M97.7,100.3L0,186L136.1,264.3L136.6,102.9L97.7,100.3Z" clip-rule="nonzero"/>
</clipPath>
<g clip-path="url(#_clip1)">
<g transform="matrix(1.82636,-0,-0,1.82366,-84.0127,-33.757)">
<use xlink:href="#_Image2" x="45.123" y="72.564" width="76.792px" height="91.93px" transform="matrix(0.997298,0,0,0.999243,0,0)"/>
</g>
</g>
<path d="M83.8,153.3L136.2,293.4L136.6,161.1L83.8,153.3Z" style="fill:rgb(70,98,132);fill-rule:nonzero;"/>
<path d="M188.7,153.8L136.2,293.4L136.6,161.1L188.7,153.8Z" style="fill:rgb(53,77,106);fill-rule:nonzero;"/>
<clipPath id="_clip3">
<path d="M230.5,-0.7L178.4,26.7L136.7,35.8L94.6,26.7L42.7,-0.8L60.6,81.9L43.6,128.3L103.2,165.3L136.3,201.3L136.3,201.6L136.5,201.4L136.7,201.6L136.7,201.3L169.8,165.3L229.6,128.3L212.6,82L230.5,-0.7Z" clip-rule="nonzero"/>
</clipPath>
<g clip-path="url(#_clip3)">
<g transform="matrix(1.82636,-0,-0,1.82366,-84.0127,-33.757)">
<use xlink:href="#_Image4" x="68.494" y="17.073" width="104.826px" height="112.987px" transform="matrix(0.998341,0,0,0.999884,0,0)"/>
</g>
</g>
<path d="M230.5,-0.7L216,82.1L229.6,128.3L212.6,82L230.5,-0.7Z" style="fill:url(#_Linear5);fill-rule:nonzero;"/>
<path d="M42.7,-0.8L57.3,82.1L43.6,128.3L60.6,81.9L42.7,-0.8Z" style="fill:url(#_Linear6);fill-rule:nonzero;"/>
<clipPath id="_clip7">
<path d="M42.7,-0.8L103.3,24.6L169.9,24.6L230.5,-0.7L178.4,26.7L136.7,35.8L94.6,26.7L42.7,-0.8Z" clip-rule="nonzero"/>
</clipPath>
<g clip-path="url(#_clip7)">
<g transform="matrix(1.82636,-0,-0,1.82366,-84.0127,-33.757)">
<use xlink:href="#_Image8" x="68.494" y="17.79" width="104.826px" height="22.071px" transform="matrix(0.998341,0,0,0.959608,0,0)"/>
</g>
</g>
<clipPath id="_clip9">
<path d="M60.6,81.9L57.6,90.2L120.5,32.2L60.6,81.9Z" clip-rule="nonzero"/>
</clipPath>
<g clip-path="url(#_clip9)">
<g transform="matrix(1.82636,-0,-0,1.82366,-84.0127,-33.757)">
<use xlink:href="#_Image10" x="77.718" y="35.369" width="36.439px" height="33.806px" transform="matrix(0.984826,0,0,0.994282,0,0)"/>
</g>
</g>
<clipPath id="_clip11">
<path d="M212.6,82L153,32.2L215.5,89.8L212.6,82Z" clip-rule="nonzero"/>
</clipPath>
<g clip-path="url(#_clip11)">
<g transform="matrix(1.82636,-0,-0,1.82366,-84.0127,-33.757)">
<use xlink:href="#_Image12" x="131.549" y="35.6" width="36.22px" height="33.586px" transform="matrix(0.978906,0,0,0.987831,0,0)"/>
</g>
</g>
<clipPath id="_clip13">
<path d="M0,186L146.3,266.7L164.8,313L125.6,327.9L64.6,321.6L0,232.6L0,186Z" clip-rule="nonzero"/>
</clipPath>
<g clip-path="url(#_clip13)">
<g transform="matrix(1.82636,-0,-0,1.82366,-84.0127,-33.757)">
<use xlink:href="#_Image14" x="45.375" y="119.784" width="92.232px" height="79.812px" transform="matrix(0.991747,0,0,0.997649,0,0)"/>
</g>
</g>
<path d="M121.1,252.8L64.8,321.4L64.6,321.6L125.5,342L125.6,342.1L173.2,317.7L173.3,317.6L173.3,281.5L121.1,252.8Z" style="fill:white;fill-rule:nonzero;"/>
<clipPath id="_clip15">
<path d="M212.6,82L230.5,-0.7L178.4,26.7L153,32.2L212.6,82Z" clip-rule="nonzero"/>
</clipPath>
<g clip-path="url(#_clip15)">
<g transform="matrix(1.82636,-0,-0,1.82366,-84.0127,-33.757)">
<use xlink:href="#_Image16" x="130.418" y="17.361" width="44.433px" height="47.35px" transform="matrix(0.987391,0,0,0.986453,0,0)"/>
</g>
</g>
<path d="M136.6,201.6L120.1,183.5L136.6,165.3L153.2,183.5L136.6,201.6Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M135,35.4L136.7,35.8L138.2,35.5L136.6,141L135,35.4Z" style="fill:url(#_Linear17);fill-rule:nonzero;"/>
</g>
</g>
<defs>
<image id="_Image2" width="77px" height="92px" xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCABcAE0DAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDiGSvpLHhjClFgGFKQxjJSsBGUoAYUoHcYUpANKUWAYUpDGlKQXOkZK3sZXGNHSGRslADGSlYYxkoAYyUhkbJSsAxkoAYUoHcYUpWA6ZkraxlcYyUAMZKQxjJRYLkbJSGMZKAGMlKwyNkoAYUpDGFKVgOmKitzIYUosAxo6Vh3GMntSsFxhSgBjJSGRslFguMZKQxjJQAwx+1Kw7m5mtTMXNABwaAGlaAGlKQDGjoAYye1Kw7kZSgBjJSGMMftRYLmgGBqiRQaAFzTAWgBc0AHBoAaVoAaUpWAY0dAEZT2pWHcYso9aLjJFk96LisPD0wsPDCgQoNAC5pgLQAuaADANADdtFgMFLj3rG5rYnSf3qrisTJN707isSrKD3p3ESLJ70XFYkD0wsODCgQ7NABmmAtAHDx3XvXIpHRYsx3PvVJisWY7n3qkybE6T+9O4rE6T+9VcLEyTU7isTLID3p3ESK/vRcVh4emKw4MKAPPa4jqFVmHQ07gSpcMOtO4rFiO696pSFYsx3PvTuTYsx3PvVJisTpP707isTpP71VxWJkmp3FYlEox1p3A4auM6AoAKACgByuw6GncRKlww607hYsR3XvVKQrFmO596q5NixHcD1p3FYnWfjrTuKxzFc5sFABQAUAFABQAUAOV2HQ00xFmKRvWqTEyyjtjrTuKx//Z"/>
<image id="_Image4" width="105px" height="113px" xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCABxAGkDAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDgq+iPEFUZNAE6CmBMgqhEyCmSyVRTAeKYhwoAcBTEOpgFABQAUAFAHO1zmpLGKYE6CmhEyCqESoKBEgpiHAUwHCgQ4UwFpgFABQAUALigDnkGTXOak6CqAmQUySZBTESgUxDhTAcBQA8UxC0wCgAoAKAFFABQBhIKwNSdBTQiZBVCZKopiHigQ4CmA8UCHUwCmAUAFAAKAFoAKAMZBWJqTIKZJMgpiJAKYhwpgPAoAcKYhaYBQAUAFACigAoAKAMpBWRoSoKZLJlFMBwpiHigBwFMQ6gApgFABQACgBaACgAoAzUFZFkyCmIkFMQ4CmA4UCHCmAtMAoAKACgBaACgAoAKAKCCsyiVRTEPFMBwoAcBTEOpgFABQAUAKKACgAoAKACgCogrMoeKYhwFMBwoEOFMBaYBQAUAFAC0AFABQAUAFAFYCoGOFMB4FADhTELTAKACgAoAUUAFABQAUAFABQBXFQMeBTAcKBDqYBTAKACgAFAC0AFABQAUAFABQBABUjHigBwpiFpgFABQAUAKKACgAoAKACgAoAKAIhUjHAUxDqYBQAUAFAAKAFoAKACgAoAKACgAoAjFSMcKYhaYBQAUAFAC0AFABQAUAFABQAUAFADB0qRjqYgpgFABQACgBaACgAoAKACgAoAKACgD/9k="/>
<linearGradient id="_Linear5" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-192.064,2.35211e-14,-2.35211e-14,-192.064,342.528,63.8296)"><stop offset="0" style="stop-color:rgb(32,52,76);stop-opacity:1"/><stop offset="0.25" style="stop-color:rgb(32,52,76);stop-opacity:1"/><stop offset="0.41" style="stop-color:rgb(39,61,87);stop-opacity:1"/><stop offset="0.67" style="stop-color:rgb(57,83,115);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(84,118,158);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear6" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(198.562,0,0,198.562,-74.3281,63.7777)"><stop offset="0" style="stop-color:rgb(84,118,158);stop-opacity:1"/><stop offset="0.25" style="stop-color:rgb(84,118,158);stop-opacity:1"/><stop offset="0.41" style="stop-color:rgb(77,110,147);stop-opacity:1"/><stop offset="0.69" style="stop-color:rgb(60,87,119);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(35,56,80);stop-opacity:1"/></linearGradient>
<image id="_Image8" width="105px" height="23px" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGkAAAAXCAYAAAAIqmGLAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAB6klEQVRogdVZS7LEIAjEqhztnWTuMufOW0xVEhWlhdYkvZpMBeTTIsT09/nukiF1ngJ4nqL4ypApcXs32a85SiKS56zIoN8GUxGK0t5xU0w0FFUuQMGJO77tWZIuvxPo8qANvH3wbkLp/+jYdsWiJCL5DhtR2QfN/wWEEmF5re8olFRbnQxNmLNtXTCiNJNQIiyvy2N/jFRFuUMWGQ+AKkEqPW8i1PFK1Qf0cSQJixkQAEWRKjEtjjFCqVLEsyx/BSPUUe6qV1XDgLKygMQzCSWa1BJCibQMUhsHVb5CPwgzp5k3EaqxcuMt3SC1ccDQLysU/2llxt5Vy0jVXKgdT7Bx6MFxUDuCHwuifU6FSTWRUIQknfJp4rzCqUxrCDUi1lf004KXO2DVOuH3fWfTUfRWCKmc7IiT6iRUu3EIrxqfV9wpRjf04LyyFqdt4XLXdi0eAKyLQwQtcAZgl5eAUKC7+8GW9s8rjsVM2G0D5zRh+kRoHC4gzCuzi47diD+PUFiSUvfxBGFeWfbVjTQAr5ivNscHp0AgFxzUilooH6qU7ekKUnHLHYw8ANRbaMUdn4fPIRScJK6Z+YUdZMFNtw/ajqKRCiQU3N25YgQZOmcAnnbtdwOpfOWO0OHoKngXdq5RCVLML4OWln+0j7xUBJmB9gAAAABJRU5ErkJggg=="/>
<image id="_Image10" width="37px" height="34px" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAAiCAYAAADYmxC7AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAByUlEQVRYhc2WUXLFIAhFr+ytK+laum76oRFEUJKXpJ3JREYETkCM5ev7h4HSnjoCBaUUAABKaVMFTRK5r62j+BCbwUe3h/LVfIgCxMwAuD11BBh1HgBzm2I0SeS+FsaH2Aw+uj2ULz1UHyS2GTA8BKY1LJnKgXEazGY5AoMBA3qmVmC4BOaV3wNjA8YAqKcuBNP6w4sBAxs5nsuAkf6KLNjp5rDl1ZlzwMhL71tgHIARTKcI4N+BkQT+P2A0Br4TzDZHBGbWgkET+QRm9VkwT++BCdkhk3/4bVJuAgtABhxK74NRHDgPduseRDvRw1ov98LhGLeD0a7Wsd7L6D1gNAb2yd8Gm/59b4Gt4rr/vjfAVnGnf98dYKKPwKx+jEsZ8rNg/l1ql1GJS81Nf6O/eZRNJuLm+Bwsf8kLu8jLaAQ2l9oDGzb6bBSBZUrtgfl70ILROOkZeWDZPQilX4PpjNbyPQp2vjmkfCkwjGC75ui6eW6QDZiUz9R1V3+dnTCjySzqj2OwOqecFs6ArU7tU2DzRr8OxqfArN4HI1k3g8nCuaOugXn6GWy4eVqw7b06BYYkmMSrl7xHwc43h1zyUmD7TXrE+qQ5fgFUaJJhxBls6wAAAABJRU5ErkJggg=="/>
<image id="_Image12" width="37px" height="34px" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAAiCAYAAADYmxC7AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAChElEQVRYhb2XYVIFIQyDY6/oWbyNd60/HpQ0tPDWGXUcRRbhaxLq+vH59e3uDgCAO14jH58y7/Pp63msmPPuY+34Pte4rz3HeK0d+8c8YJMnznkABJ5PQLxXB4QFhAUEOGyHwPpg4kROIKDD+HlSQoCiOCqCVLV06KaGWHUCcl4n1iQlClVlD7vnRUQrgMaZvTWarwKIXbHwvgSi+TmKYZOVizVsaVTDRbhmqgNS+zgrEKCTNUl2VtUD6BX0p0C0JltTAIUSEdB09XcgjJbA1nRAbLHfskLWHHpR2iP2nkpF1Qegd5tjaU0BBAbKCsJ9v320Ex1SWUNqbNZIViBAjYJzLveplJ15CB3GwK01rHZ3A5eC6RaPOdtFqYFmZstKkxKFqkWDhRTKgbdkD5DHXS/irFwCXzXH9gYOQSykTvbxgQJ0tYYzultzvIHjLPv/5ogCKKtqGxBWpX/THDlLpCpFxZZ5TRVNVgKmvIGFNQwUgSdVyRlb/qsSD5sjCGhT4p3Ar9+xGih0a6ypAt83x6oXtar6vH3SM0prroFnILXmQeDhsOcvahXQyZoOSIGXgut96lfWzC+nwMteReATsPP71LvW3ALf3sAilxQVVs5KWVPgxZpi4zIrUCAqaouKp7HVWUGq4vfWdDdQ19DYff4zerKmAdqUqKyhvSBFRHYTL4DZ0bmKLisQoAa4voGSL4mKPovmeb+BaIGyNZCDFUijIrBOf2ayEkXgj9Z0gRcVWMEiS3PO9iq8BeqtaYASMLbDwUBYkNYDzdx31lS9SIBC1f3w+bPeVjhG8/RcfbImAu8XoJxL+CqqOryee51jM0tt4EtrkCuuAq8KaH5yLAMI7vgBdTzcL9uCvFAAAAAASUVORK5CYII="/>
<image id="_Image14" width="93px" height="80px" xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCABQAF0DAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDFr6o+fCgAoAKACgBDSAQ0ANNIBhoGNNIBp6UhjTQBdrQkKACgAoAKAENIBppANNADDQMaaQDTSGNoAvVoSFABQAUABpANNADTSAa1ADDSGNNIBpoGNNAF+tCQoAKACgBDSAQ0AMNIBpoGNNIBppDGGgBtAGhWhIUAFABQAhpANNIBpoAYaBjTSAaaQxhoAaaANGtCQoAKAA0gGmgBppAMNADT1pDGmgBppDGmgBpoGjRrQgKACgBDSAaaQDTQA00DGHpSAQ0hjDQA00ANPWkM0q1ICgApAIaAGnpSAY1ADTQMaaQDTSGMNADTQCGmkM061ICgANIBpoAaaQDDQA00hjTSAaaBjTQAw0hjT1oA1K1ICgBD1pANNIBpoAYaBjTSAaaQxpoAaaAGGkMaaAP/2Q=="/>
<image id="_Image16" width="45px" height="48px" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC0AAAAwCAYAAACFUvPfAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAE80lEQVRoga1YbXLrQAjj4Sv2LD1Oj7q8H8uHYMFO0nYm09axEwFCgv1Hv/z5+v5hIrqI5CKhi4hYiC6/RmTX7RoT0SV+XeI9kUuIWH/r//a5wiL7Xv4DwEwkTEJ8AzjuM8BUAJOw7HsccHxuABaRz0F/ff/8UyCeScnZ9EAQoN7DJGLv7WA16yJ07fcErsmuoD7zEWgFXCmRsplfDtiCSe+LZlXEnlfwG7AGZZX7INMBmGZKbFpgEBZU5bFmL7LqvPYgjCq7AkLyUaY1W1Ip4UBIWkp4Q5FTQqBam7+pEXcQdo2tKd8C/fX905cbgETpBRqQvKEoUYIUqDAJqVK4grA2J6rIe5kGwJGRyt0A7A3YSJs3FEraBi5QRW9KDSKa/iXQRdqAEp1SQDYbSgA/FahlWlIFrCmlBvFKpqt5AOAAWNSArPEKJcA0CiVyBaSARGV51GkAjJprGZ3Mo1cTwoaiCwAf5oGmgk0p+l0jaDAPb67G7SbzuEbzON3uAsBAQa8su4LcZboxj1MpXNY6t8tN2pjH5HapAoKZFr23Aw3mUd2ulDvxOhzxffOYmjObigKeMm0fNPETlaIzD+f4rXnUII7mjAoo4P03FdCNeaRsRiZLEFkpGCgBZS7mobocDoh6DUFbhklfmOnBPLKBNG7XzcXF7YCnRXdDBkEtkOs7swZWA9jqoYAn86iU6NwOzePO7Zynd+aBei3QgPY3g9tN5tFRwgBP5lHcLldAmzN4XczD7jUuI2Cjh3OPCF/HqhSai7NAbx7F7WR2OzMbMI8mu/v5kDwnfmceVw2iVRPU3dM8btyOMq/13kqHgx7F7ap5dG5XLR22DBgph1Upu11REGg2kzoHvBvSJQ9XpWoe3oiDebj1itOlbyhquQ7Xh+xSpx4P5pENpne7uO5SaZl2tXgyj9xs5GBbumD5W6XIlOhXpWq94nr/lnmwlT8AaiWBKgY6uDuvSnfmMa1Kh3mE/B1cvVIA/lotZZq5eFyVOCgxmgdsIEkGL6BRcL0vP3cBoF4XpTjMA90Op66HValMdqYsjXkMDahcp049uB6cVPOobtc0FDblYB4+7FMCDPI2BLCSeuA8jUrx4jlbDFb35lFXpdDfkl1Odq3VmeaPwTw+PWd7XpVu3Y7kyurRBISN+DfnbHRwva5KFM0GI6pT4BiOagAii9ERJ/N465ytmkddlRxMYx4Y0AB4c/qX52zYnGkpLdOar0rudvfqoddW17RcV6V3zOPG7ZrZgbLWnvLW6TXZMyyy2BRlGICezMOb8nlVeszmaR4RZADV35bpY1W6APDb52zRnJTcrarBk3rEMyvxHCQvrUoMCuDzyGge46q0mlmisetTPRizGmAN/FYPnBGmczYG+btm83helQSyOjfkYlknLcjUI5nHuSpdkD2mUoGoTDIPP4wpYJJ6HHqd6ZBokd5fwlaiT8/ZeDaPFYDL+BnZJQSP3AWwOdtbPR7O2SzTZbLLptJx12eVU2ejAgufgUBtjnagSUW4NQ8I4oVV6Y6rfUCNXmvj7u9aOTj/LL3+V+dszaqUzGPUawii1WSkhdEFAf/JORuCO+QtV4QDbG2+TAeliwfgDYXDz2/O2eL/Zm5I6pGAHgaS6HIstn98zhZfcqxKIIdSgUa21+rpsoIuXLN3rErZPLrsTuaR1AOeYTWPgxYk6yW6/AdyxQlNZJfFBgAAAABJRU5ErkJggg=="/>
<linearGradient id="_Linear17" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(2.71996e-14,-444.203,444.203,2.71996e-14,136.61,347.973)"><stop offset="0" style="stop-color:rgb(84,118,158);stop-opacity:1"/><stop offset="0.25" style="stop-color:rgb(84,118,158);stop-opacity:1"/><stop offset="0.41" style="stop-color:rgb(77,110,147);stop-opacity:1"/><stop offset="0.68" style="stop-color:rgb(60,87,119);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(34,54,78);stop-opacity:1"/></linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,147 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1050 350" style="enable-background:new 0 0 1050 350;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill:#273C51;}
.st2{fill:url(#SVGID_1_);}
.st3{fill:#466284;}
.st4{fill:#354D6A;}
.st5{fill:url(#SVGID_2_);}
.st6{fill:url(#SVGID_3_);}
.st7{fill:url(#SVGID_4_);}
.st8{fill:url(#SVGID_5_);}
.st9{fill:url(#SVGID_6_);}
.st10{fill:url(#SVGID_7_);}
.st11{fill:url(#SVGID_8_);}
.st12{fill:url(#SVGID_9_);}
.st13{fill:url(#SVGID_10_);}
.st14{fill:none;}
</style>
<g>
<g>
<g>
<path class="st0" d="M280.6,198.7c-15.2,0-31.5,6-31.5,20.6c0,13,14.9,16.8,32.6,19.7c24,3.8,47.6,8.6,47.6,35.6
c-0.2,26.9-25.9,35.6-48.8,35.6c-21.2,0-41.5-7.7-50.7-27.8l12.3-7.2c7.7,14.2,23.8,21.1,38.6,21.1c14.6,0,33.9-4.6,33.9-22.3
c0.2-14.9-16.6-19.2-34.6-21.9c-23.1-3.6-45.6-8.9-45.6-33.2c-0.3-25,25.2-33.6,45.9-33.6c17.8,0,34.8,3.6,45.4,21.8l-11.3,7
C307.9,203.7,294,198.9,280.6,198.7z"/>
<path class="st0" d="M353.8,188.1v49.2c7.2-11.1,18.5-14.9,29.3-15.1c23.8,0,35.5,15.8,35.5,39.1v46.6h-13.9v-46.4
c0-16.6-8.6-26-24-26S354,247.5,354,263v44.9h-14V187.9h13.9V188.1z"/>
<path class="st0" d="M504.4,308.2l-0.3-15.4c-6.7,11.7-19.5,17.1-31.2,17.1c-24.3,0-43.3-16.8-43.3-44.4
c0-27.4,19.4-43.9,43.5-43.7c12.7,0,25.2,5.8,31.4,16.8l0.2-15.4h13.7v84.6h-13.5L504.4,308.2z M473.5,235.2
c-16.8,0-30.3,12-30.3,30.8s13.5,31,30.3,31c40.8,0,40.8-62,0.2-62L473.5,235.2z"/>
<path class="st0" d="M529.5,223.6h13.4l0.7,16.3c6.7-11.3,19.2-17.8,32.6-17.8c24.3,0.5,42.1,17.6,42.1,43.7
c0,26.7-17.6,44-43,44c-12,0-25.4-5.1-32-17.1v55.2h-13.7V223.6z M604.2,266c0-19-12.5-30.3-29.8-30.3c-17.6,0-29.6,13-29.6,30.3
s12.5,30.3,29.6,30.5C591.3,296.5,604.2,285.1,604.2,266z"/>
<path class="st0" d="M708.9,294.3c-8.6,10.1-23.3,15.1-36.5,15.1c-26.2,0-44.5-17.3-44.5-44.2c0-25.5,18.3-43.9,43.9-43.9
c25.9,0,45.6,15.9,42.3,49.7h-72c1.5,15.6,14.4,25.4,30.7,25.4c9.6,0,21.2-3.8,26.9-10.6l9.4,8.6H708.9z M700.6,259.4
c-0.7-16.4-12-25.4-28.6-25.4c-14.7,0-27.6,8.9-30,25.2h58.6V259.4z"/>
</g>
<g>
<path class="st0" d="M771.2,198.7c-15.2,0-31.5,6-31.5,20.6c0,13,14.9,16.8,32.6,19.7c24,3.8,47.6,8.6,47.6,35.6
c-0.2,26.9-25.9,35.6-48.8,35.6c-21.2,0-41.5-7.7-50.7-27.8l12.3-7.2c7.7,14.2,23.8,21.1,38.6,21.1c14.6,0,33.9-4.6,33.9-22.3
c0.2-14.9-16.6-19.2-34.6-21.9c-23.1-3.6-45.6-8.9-45.6-33.2c-0.3-25,25.2-33.6,45.9-33.6c17.8,0,34.8,3.6,45.4,21.8l-11.3,7
C798.5,203.7,784.6,198.9,771.2,198.7z"/>
<path class="st0" d="M844.4,188.1v49.2c7.2-11.1,18.5-14.9,29.3-15.1c23.8,0,35.5,15.8,35.5,39.1v46.6h-13.9v-46.4
c0-16.6-8.6-26-24-26s-26.7,12.2-26.7,27.6v44.9h-14V187.9h13.9V188.1z"/>
<path class="st0" d="M920.6,307.8h14v-83.4h-14V307.8z M927.8,211.7l-11.1-12.2l11.1-12.2l11.1,12.2L927.8,211.7z"/>
<g>
<polygon class="st0" points="960.8,308 960.8,307.8 960.7,307.8 "/>
<path class="st0" d="M974.7,217.6c0-12.7,5.8-18.3,14.9-18.3c0.2,0,0.4,0,0.5,0l3.2-12.1c-1.3-0.2-2.7-0.3-4-0.3
c-17.8,0-28.4,11.3-28.4,30.7v6.7h-16.6v12.3h16.6v71.3h13.9v-71.3h16.8v-12.3h-16.8V217.6z"/>
</g>
<path class="st0" d="M1046.1,296.1c-1.1,0.2-2.2,0.3-3.3,0.3c-10.1,0-13.4-6.3-13.4-16.3v-43.6h18v-12.2h-17.9v-25.8l-14,1.5
v24.2h-17v12.2h17v43.6c0,18.7,8.6,29.3,26.7,29c2.2-0.1,4.4-0.3,6.5-0.8L1046.1,296.1z"/>
</g>
</g>
<polygon class="st1" points="216,82.1 230.5,-0.7 169.9,24.6 103.3,24.6 42.7,-0.8 57.3,82.1 43.6,128.3 56.5,136.4 0,186 0,232.6
64.6,321.6 125.5,342 125.6,342.1 173.2,317.7 173.3,317.6 173.3,281.5 146.3,266.7 146.3,266.7 146.3,266.7 188.7,153.8
188.4,153.8 229.6,128.3 "/>
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="136.7364" y1="25.9921" x2="60.3198" y2="247.6647">
<stop offset="0.1345" style="stop-color:#2B415B"/>
<stop offset="0.3762" style="stop-color:#3B5676"/>
<stop offset="0.6923" style="stop-color:#54769E"/>
<stop offset="0.7901" style="stop-color:#52749B"/>
<stop offset="0.8614" style="stop-color:#4D6C92"/>
<stop offset="0.9244" style="stop-color:#436082"/>
<stop offset="0.9822" style="stop-color:#364F6C"/>
<stop offset="1" style="stop-color:#314863"/>
</linearGradient>
<polygon class="st2" points="97.7,100.3 0,186 136.1,264.3 136.6,102.9 "/>
<polygon class="st3" points="83.8,153.3 136.2,293.4 136.6,161.1 "/>
<polygon class="st4" points="188.7,153.8 136.2,293.4 136.6,161.1 "/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="230.1033" y1="127.4219" x2="34.0475" y2="14.229">
<stop offset="0" style="stop-color:#54769E"/>
<stop offset="0.4802" style="stop-color:#53749C"/>
<stop offset="0.6878" style="stop-color:#4F6F95"/>
<stop offset="0.8423" style="stop-color:#486588"/>
<stop offset="0.9095" style="stop-color:#435F80"/>
</linearGradient>
<polygon class="st5" points="230.5,-0.7 178.4,26.7 136.7,35.8 94.6,26.7 42.7,-0.8 60.6,81.9 43.6,128.3 103.2,165.3
136.3,201.3 136.3,201.6 136.5,201.4 136.7,201.6 136.7,201.3 169.8,165.3 229.6,128.3 212.6,82 "/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="342.5284" y1="63.8296" x2="150.4648" y2="63.8296">
<stop offset="0.2539" style="stop-color:#20344C"/>
<stop offset="0.4072" style="stop-color:#273D57"/>
<stop offset="0.6733" style="stop-color:#395373"/>
<stop offset="1" style="stop-color:#54769E"/>
</linearGradient>
<polygon class="st6" points="230.5,-0.7 216,82.1 229.6,128.3 212.6,82 "/>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="-74.3281" y1="63.7777" x2="124.2335" y2="63.7777">
<stop offset="0.2539" style="stop-color:#54769E"/>
<stop offset="0.4133" style="stop-color:#4D6E93"/>
<stop offset="0.6897" style="stop-color:#3C5777"/>
<stop offset="1" style="stop-color:#233850"/>
</linearGradient>
<polygon class="st7" points="42.7,-0.8 57.3,82.1 43.6,128.3 60.6,81.9 "/>
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="138.4299" y1="-77.4169" x2="134.5027" y2="85.5632">
<stop offset="6.545247e-03" style="stop-color:#54769E"/>
<stop offset="0.1993" style="stop-color:#507198"/>
<stop offset="0.4502" style="stop-color:#466488"/>
<stop offset="0.7318" style="stop-color:#354F6D"/>
<stop offset="1" style="stop-color:#21354D"/>
</linearGradient>
<polygon class="st8" points="42.7,-0.8 103.3,24.6 169.9,24.6 230.5,-0.7 178.4,26.7 136.7,35.8 94.6,26.7 "/>
<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="173.2798" y1="-23.2345" x2="12.7505" y2="132.5687">
<stop offset="0.2539" style="stop-color:#54769E"/>
<stop offset="0.4102" style="stop-color:#4D6E93"/>
<stop offset="0.6813" style="stop-color:#3C5777"/>
<stop offset="1" style="stop-color:#22364E"/>
</linearGradient>
<polygon class="st9" points="60.6,81.9 57.6,90.2 120.5,32.2 "/>
<linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="114.997" y1="-2.4443" x2="248.7759" y2="116.8474">
<stop offset="0.2539" style="stop-color:#54769E"/>
<stop offset="0.4102" style="stop-color:#4D6E93"/>
<stop offset="0.6813" style="stop-color:#3C5777"/>
<stop offset="1" style="stop-color:#22364E"/>
</linearGradient>
<polygon class="st10" points="212.6,82 153,32.2 215.5,89.8 "/>
<linearGradient id="SVGID_8_" gradientUnits="userSpaceOnUse" x1="-31.9333" y1="230.8414" x2="255.118" y2="333.2895">
<stop offset="0.2664" style="stop-color:#54769E"/>
<stop offset="1" style="stop-color:#425E7F"/>
</linearGradient>
<polygon class="st11" points="0,186 146.3,266.7 164.8,313 125.6,327.9 64.6,321.6 0,232.6 "/>
<polygon class="st0" points="121.1,252.8 64.8,321.4 64.6,321.6 125.5,342 125.6,342.1 173.2,317.7 173.3,317.6 173.3,281.5 "/>
<linearGradient id="SVGID_9_" gradientUnits="userSpaceOnUse" x1="97.761" y1="-67.9411" x2="268.6103" y2="84.2801">
<stop offset="0.4609" style="stop-color:#54769E;stop-opacity:0"/>
<stop offset="0.5699" style="stop-color:#52739A;stop-opacity:0.2156"/>
<stop offset="0.6764" style="stop-color:#4A698E;stop-opacity:0.4266"/>
<stop offset="0.782" style="stop-color:#3D597B;stop-opacity:0.6356"/>
<stop offset="0.8863" style="stop-color:#2C435F;stop-opacity:0.8422"/>
<stop offset="0.9661" style="stop-color:#1B2E45"/>
</linearGradient>
<polygon class="st12" points="212.6,82 230.5,-0.7 178.4,26.7 153,32.2 "/>
<polygon class="st0" points="136.6,201.6 120.1,183.5 136.6,165.3 153.2,183.5 "/>
<linearGradient id="SVGID_10_" gradientUnits="userSpaceOnUse" x1="136.6099" y1="347.9733" x2="136.6099" y2="-96.2296">
<stop offset="0.2539" style="stop-color:#54769E"/>
<stop offset="0.4102" style="stop-color:#4D6E93"/>
<stop offset="0.6813" style="stop-color:#3C5777"/>
<stop offset="1" style="stop-color:#22364E"/>
</linearGradient>
<polygon class="st13" points="135,35.4 136.7,35.8 138.2,35.5 136.6,141 "/>
<path class="st14" d="M77.6,35.5"/>
<path class="st14" d="M75.1,35.5"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

View File

@ -11,6 +11,7 @@ import { CopayersPage } from '../add/copayers/copayers';
import { GlideraPage } from '../integrations/glidera/glidera';
import { MercadoLibrePage } from '../integrations/mercado-libre/mercado-libre';
import { ProposalsPage } from './proposals/proposals';
import { ShapeshiftPage } from '../integrations/shapeshift/shapeshift';
import { TxDetailsPage } from '../tx-details/tx-details';
import { TxpDetailsPage } from '../txp-details/txp-details';
import { WalletDetailsPage } from '../wallet-details/wallet-details';
@ -360,9 +361,6 @@ export class HomePage {
public goTo(page): void {
switch (page) {
case 'MercadoLibrePage':
this.navCtrl.push(MercadoLibrePage);
break;
case 'AmazonPage':
this.navCtrl.push(AmazonPage);
break;
@ -375,6 +373,12 @@ export class HomePage {
case 'GlideraPage':
this.navCtrl.push(GlideraPage);
break;
case 'MercadoLibrePage':
this.navCtrl.push(MercadoLibrePage);
break;
case 'ShapeshiftPage':
this.navCtrl.push(ShapeshiftPage);
break;
}
}

View File

@ -0,0 +1,88 @@
<ion-header>
<ion-navbar>
<ion-title>{{'Confirm'|translate}}</ion-title>
</ion-navbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item>
<div class="sending-label">
<img src="assets/img/shapeshift/icon-shapeshift.svg" alt="shapeshift">
<span translate>Shift</span>
</div>
<div class="amount-label">
<div class="amount">{{amountStr}}</div>
<div class="alternative" *ngIf="fiatAmount">{{fiatAmount | currency}} {{currencyIsoCode}}</div>
</div>
</ion-item>
<div class="info">
<ion-item>
<span translate>From</span>
<img item-end *ngIf="fromWallet.coin == 'btc'" src="assets/img/icon-bitcoin.svg" width="18">
<img item-end *ngIf="fromWallet.coin == 'bch'" src="assets/img/bitcoin-cash-logo.svg" width="22">
</ion-item>
<ion-item class="wallet-selector">
<img src="assets/img/icon-wallet.svg" class="icon-wallet" />
<div class="wallet-balance">
<span>
{{fromWallet ? fromWallet.name : '...'}}
</span>
</div>
</ion-item>
<ion-item>
<span translate>To</span>
<img item-end *ngIf="toWallet.coin == 'btc'" src="assets/img/icon-bitcoin.svg" width="18">
<img item-end *ngIf="toWallet.coin == 'bch'" src="assets/img/bitcoin-cash-logo.svg" width="22">
</ion-item>
<ion-item class="wallet-selector">
<img src="assets/img/icon-wallet.svg" class="icon-wallet" />
<div class="wallet-balance">
<span>
{{toWallet ? toWallet.name : '...'}}
</span>
</div>
</ion-item>
</div>
<ion-item *ngIf="fiatFee && feeRatePerStr">
{{'Fee'|translate}}
<ion-note item-end>
<span>
{{feeStr}} ({{fiatFee | currency}} {{currencyIsoCode}}, {{feeRatePerStr}})
</span>
</ion-note>
</ion-item>
<ion-item *ngIf="fiatTotalAmount">
{{'Total'|translate}}
<ion-note item-end>
<span>
{{totalAmountStr}} ({{fiatTotalAmount | currency}} {{currencyIsoCode}})
</span>
</ion-note>
</ion-item>
</ion-list>
<p class="text-confirm" *ngIf="withdrawalStr">
A total of {{amountStr}} ({{fiatAmount | currency}} {{currencyIsoCode}}) will be exchanged for {{withdrawalStr}} ({{fiatWithdrawal
| currency}} {{currencyIsoCode}}). Would you like to proceed?
</p>
</ion-content>
<ion-footer *ngIf="fromWallet && toWallet && totalAmountStr">
<button ion-button block class="button-footer" (click)="confirmTx()" [disabled]="!fromWallet || !totalAmountStr" translate>
Click to shift
</button>
</ion-footer>
<ion-footer *ngIf="sendStatus === 'success'">
<button ion-button block class="button-footer" (click)="goBackHome()" [disabled]="!(wallet && totalAmountStr)" translate>
Transaction Sent
</button>
</ion-footer>

View File

@ -0,0 +1,44 @@
page-shapeshift-confirm {
.sending-label {
display: flex;
font-size: 18px;
align-items: center;
margin-bottom: 1.8rem;
img {
margin-right: 1rem;
height: 35px;
width: 35px;
}
.big-icon-svg {
margin-right: 0.6rem;
}
}
.amount-label{
line-height: 30px;
.amount{
font-size: 38px;
margin-bottom: .5rem;
}
.alternative {
font-size: 12px;
color: #9B9B9B;
}
}
.info {
.wallet-selector {
ion-label {
display: flex;
align-items: center;
}
.wallet-balance {
margin-left: 1.5rem;
}
}
}
.text-confirm {
margin-left: 2rem;
}
}

View File

@ -0,0 +1,356 @@
import { Component } from '@angular/core';
import { NavController, NavParams } from 'ionic-angular';
import { Logger } from '@nsalaun/ng-logger';
import * as moment from 'moment';
import * as _ from 'lodash';
// Pages
import { ShapeshiftPage } from '../shapeshift';
// Providers
import { BwcProvider } from '../../../../providers/bwc/bwc';
import { BwcErrorProvider } from '../../../../providers/bwc-error/bwc-error';
import { ConfigProvider } from '../../../../providers/config/config';
import { ExternalLinkProvider } from '../../../../providers/external-link/external-link';
import { OnGoingProcessProvider } from "../../../../providers/on-going-process/on-going-process";
import { PlatformProvider } from '../../../../providers/platform/platform';
import { PopupProvider } from '../../../../providers/popup/popup';
import { ProfileProvider } from '../../../../providers/profile/profile';
import { ShapeshiftProvider } from '../../../../providers/shapeshift/shapeshift';
import { TxFormatProvider } from '../../../../providers/tx-format/tx-format';
import { WalletProvider } from '../../../../providers/wallet/wallet';
@Component({
selector: 'page-shapeshift-confirm',
templateUrl: 'shapeshift-confirm.html',
})
export class ShapeshiftConfirmPage {
private amount: number;
private currency: string;
private fromWalletId: string;
private toWalletId: string;
private createdTx: any;
private message: string;
private configWallet: any;
private bitcore: any;
private bitcoreCash: any;
private useSendMax: boolean;
public currencyIsoCode: string;
public isCordova: boolean;
public sendStatus: string;
public toWallet: any;
public fromWallet: any;
public fiatWithdrawal: number;
public fiatAmount: number;
public fiatFee: number;
public fiatTotalAmount: number;
public shapeInfo: any;
public amountUnitStr: string;
public feeRatePerStr: string;
public amountStr: string;
public withdrawalStr: string;
public feeStr: string;
public totalAmountStr: string;
public txSent: any;
public network: string;
constructor(
private bwcProvider: BwcProvider,
private bwcErrorProvider: BwcErrorProvider,
private configProvider: ConfigProvider,
private externalLinkProvider: ExternalLinkProvider,
private onGoingProcessProvider: OnGoingProcessProvider,
private logger: Logger,
private navCtrl: NavController,
private navParams: NavParams,
private platformProvider: PlatformProvider,
private popupProvider: PopupProvider,
private profileProvider: ProfileProvider,
private shapeshiftProvider: ShapeshiftProvider,
private txFormatProvider: TxFormatProvider,
private walletProvider: WalletProvider
) {
this.configWallet = this.configProvider.get().wallet;
this.currencyIsoCode = 'USD'; // Only USD
this.isCordova = this.platformProvider.isCordova;
this.bitcore = this.bwcProvider.getBitcore();
this.bitcoreCash = this.bwcProvider.getBitcoreCash();
this.useSendMax = this.navParams.data.useSendMax ? true : false;
this.amount = this.navParams.data.amount / 1e8;
this.currency = this.navParams.data.currency;
this.fromWalletId = this.navParams.data.walletId;
this.toWalletId = this.navParams.data.toWalletId;
this.network = this.shapeshiftProvider.getNetwork();
this.fromWallet = this.profileProvider.getWallet(this.fromWalletId);
this.toWallet = this.profileProvider.getWallet(this.toWalletId);
if (_.isEmpty(this.fromWallet) || _.isEmpty(this.toWallet)) {
this.showErrorAndBack(null, 'No wallet found'); // TODO: gettextCatalog
return;
}
this.shapeshiftProvider.getLimit(this.getCoinPair(), (err: any, lim: any) => {
let min = Number(lim.min);
let max = Number(lim.limit);
if (this.useSendMax) this.amount = max;
let amountNumber = Number(this.amount);
if (amountNumber < min) {
this.showErrorAndBack(null, 'Minimum amount required is ' + min); // TODO: gettextCatalog
return;
}
if (amountNumber > max) {
this.showErrorAndBack(null, 'Maximum amount allowed is ' + max); // TODO: gettextCatalog
return;
}
this.createShift();
});
}
ionViewDidLoad() {
this.logger.info('ionViewDidLoad ShapeshiftConfirmPage');
}
public openExternalLink(url: string) {
this.externalLinkProvider.open(url);
};
private showErrorAndBack(title: string, msg: any) {
title = title ? title : 'Error'; // TODO: gettextCatalog
this.sendStatus = '';
this.logger.error(msg);
msg = (msg && msg.errors) ? msg.errors[0].message : msg;
this.popupProvider.ionicAlert(title, msg).then(() => {
this.navCtrl.pop();
});
};
private publishAndSign(wallet: any, txp: any, onSendStatusChange: any): Promise<any> {
return new Promise((resolve, reject) => {
if (!wallet.canSign() && !wallet.isPrivKeyExternal()) {
let err = 'No signing proposal: No private key'; // TODO: gettextCatalog
this.logger.info(err);
return reject(err);
}
this.walletProvider.publishAndSign(wallet, txp, onSendStatusChange).then((txp: any) => {
return resolve(txp);
}).catch((err: any) => {
return reject(err);
});
});
}
private statusChangeHandler(processName: string, showName: string, isOn: boolean) {
this.logger.debug('statusChangeHandler: ', processName, showName, isOn);
if (processName == 'sendingTx' && !isOn) {
this.sendStatus = 'success';
} else if (showName) {
this.sendStatus = showName;
}
}
private satToFiat(coin: string, sat: number, isoCode: string): Promise<any> {
return new Promise((resolve, reject) => {
this.txFormatProvider.toFiat(coin, sat, isoCode).then((value: any) => {
return resolve(value);
});
});
}
private setFiatTotalAmount(amountSat: number, feeSat: number, withdrawalSat: number) {
this.satToFiat(this.toWallet.coin, withdrawalSat, this.currencyIsoCode, ).then((w: any) => {
this.fiatWithdrawal = Number(w);
this.satToFiat(this.fromWallet.coin, amountSat, this.currencyIsoCode, ).then((a: any) => {
this.fiatAmount = Number(a);
this.satToFiat(this.fromWallet.coin, feeSat, this.currencyIsoCode, ).then((i: any) => {
this.fiatFee = Number(i);
this.fiatTotalAmount = this.fiatAmount + this.fiatFee;
});
});
});
}
private saveShapeshiftData(): void {
let address = this.shapeInfo.deposit;
let now = moment().unix() * 1000;
this.shapeshiftProvider.getStatus(address, (err: any, st: any) => {
let newData = {
address: address,
status: st.status,
date: now,
amount: this.amountStr,
title: this.fromWallet.coin.toUpperCase() + ' to ' + this.toWallet.coin.toUpperCase()
};
this.shapeshiftProvider.saveShapeshift(newData, null, (err: any) => {
this.logger.debug("Saved shift with status: " + newData.status);
});
});
}
private createTx(wallet: any, toAddress: string): Promise<any> {
return new Promise((resolve, reject) => {
let parsedAmount = this.txFormatProvider.parseAmount(wallet.coin, this.amount, this.currency);
this.amountUnitStr = parsedAmount.amountUnitStr;
this.message = 'ShapeShift: ' + this.fromWallet.coin.toUpperCase() + ' to ' + this.toWallet.coin.toUpperCase();
let outputs = [];
outputs.push({
'toAddress': toAddress,
'amount': parsedAmount.amountSat,
'message': this.message
});
let txp = {
toAddress: toAddress,
amount: parsedAmount.amountSat,
outputs: outputs,
message: this.message,
excludeUnconfirmedUtxos: this.configWallet.spendUnconfirmed ? false : true,
feeLevel: this.configWallet.settings.feeLevel || 'normal',
customData: {
'shapeShift': toAddress
}
};
this.walletProvider.createTx(wallet, txp).then((ctxp: any) => {
return resolve(ctxp);
}).catch((err: any) => {
return reject({
title: 'Could not create transaction', // TODO: gettextCatalog
message: this.bwcErrorProvider.msg(err)
});
});
});
}
private getLegacyAddressFormat(addr: string, coin: string): string {
if (coin == 'btc') return addr;
let a = this.bitcoreCash.Address(addr).toObject();
return this.bitcore.Address.fromObject(a).toString();
}
private getNewAddressFormat(addr: string, coin: string): string {
if (coin == 'btc') return addr;
let a = this.bitcore.Address(addr).toObject();
return this.bitcoreCash.Address.fromObject(a).toString();
}
private getCoinPair(): string {
return this.fromWallet.coin + '_' + this.toWallet.coin;
}
private createShift(): void {
this.onGoingProcessProvider.set('connectingShapeshift', true);
this.walletProvider.getAddress(this.toWallet, false).then((withdrawalAddress: string) => {
withdrawalAddress = this.getLegacyAddressFormat(withdrawalAddress, this.toWallet.coin);
this.walletProvider.getAddress(this.fromWallet, false).then((returnAddress: string) => {
returnAddress = this.getLegacyAddressFormat(returnAddress, this.fromWallet.coin);
let data = {
withdrawal: withdrawalAddress,
pair: this.getCoinPair(),
returnAddress: returnAddress
}
this.shapeshiftProvider.shift(data, (err: any, shapeData: any) => {
if (err || shapeData.error) {
this.onGoingProcessProvider.set('connectingShapeshift', false);
this.showErrorAndBack(null, err || shapeData.error);
return;
}
let toAddress = this.getNewAddressFormat(shapeData.deposit, this.fromWallet.coin);
this.createTx(this.fromWallet, toAddress).then((ctxp: any) => {
// Save in memory
this.createdTx = ctxp;
this.shapeInfo = shapeData;
this.shapeshiftProvider.getRate(this.getCoinPair(), (err: any, r: any) => {
this.onGoingProcessProvider.set('connectingShapeshift', false);
let rateUnit = r.rate;
let amountUnit = this.txFormatProvider.satToUnit(ctxp.amount);
let withdrawalSat = Number((rateUnit * amountUnit * 100000000).toFixed());
// Fee rate
let per = (ctxp.fee / (ctxp.amount + ctxp.fee) * 100);
this.feeRatePerStr = per.toFixed(2) + '%';
// Amount + Unit
this.amountStr = this.txFormatProvider.formatAmountStr(this.fromWallet.coin, ctxp.amount);
this.withdrawalStr = this.txFormatProvider.formatAmountStr(this.toWallet.coin, withdrawalSat);
this.feeStr = this.txFormatProvider.formatAmountStr(this.fromWallet.coin, ctxp.fee);
this.totalAmountStr = this.txFormatProvider.formatAmountStr(this.fromWallet.coin, ctxp.amount + ctxp.fee);
// Convert to fiat
this.setFiatTotalAmount(ctxp.amount, ctxp.fee, withdrawalSat);
});
}).catch((err: any) => {
this.onGoingProcessProvider.set('connectingShapeshift', false);
this.showErrorAndBack(err.title, err.message);
return;
});
});
}).catch((err: any) => {
this.onGoingProcessProvider.set('connectingShapeshift', false);
this.showErrorAndBack(null, 'Could not get address');
return;
});
}).catch((err: any) => {
this.onGoingProcessProvider.set('connectingShapeshift', false);
this.showErrorAndBack(null, 'Could not get address');
return;
});
}
public confirmTx(): void {
if (!this.createdTx) {
this.showErrorAndBack(null, 'Transaction has not been created'); // TODO: gettextCatalog
return;
}
let fromCoin = this.fromWallet.coin.toUpperCase();
let toCoin = this.toWallet.coin.toUpperCase();
let title = 'Confirm to shift ' + fromCoin + ' to ' + toCoin; // TODO: gettextCatalog
let okText = 'OK'; // TODO: gettextCatalog
let cancelText = 'Cancel'; // TODO: gettextCatalog
this.popupProvider.ionicConfirm(title, '', okText, cancelText).then((ok: any) => {
if (!ok) {
this.sendStatus = '';
return;
}
this.onGoingProcessProvider.set('sendingTx', true, this.statusChangeHandler);
this.publishAndSign(this.fromWallet, this.createdTx, function () { }).then((txSent: any) => {
this.onGoingProcessProvider.set('sendingTx', false, this.statusChangeHandler);
this.txSent = txSent;
this.saveShapeshiftData();
}).catch((err: any) => {
this.showErrorAndBack(null, 'Could not send transaction'), err; // TODO: gettextCatalog
return;
});
});
};
public goBackHome() {
this.sendStatus = '';
this.navCtrl.remove(3, 1);
this.navCtrl.pop();
this.navCtrl.push(ShapeshiftPage);
}
}

View File

@ -0,0 +1,121 @@
<ion-header>
<ion-navbar>
<ion-title>Shift</ion-title>
</ion-navbar>
</ion-header>
<ion-content>
<ion-list *ngIf="fromWallets.length > 0 && toWallets.length > 0">
<div *ngIf="fromWallets.length > 0">
<ion-item>
<span translate>From</span>
<img item-end *ngIf="fromWallet.coin == 'btc'" src="assets/img/icon-bitcoin.svg" width="18">
<img item-end *ngIf="fromWallet.coin == 'bch'" src="assets/img/bitcoin-cash-logo.svg" width="22">
</ion-item>
<button ion-item class="wallet-selector" (click)="showWallets('from')">
<img *ngIf="fromWallet.network != 'testnet'" src="assets/img/icon-wallet.svg" class="icon-wallet" />
<div class="wallet-balance">
<span>
{{fromWallet.name || fromWallet.id}}
</span>
<p>
<span *ngIf="!fromWallet.isComplete()" class="assertive" translate>
Incomplete
</span>
<span *ngIf="fromWallet.isComplete()">
<span *ngIf="!fromWallet.balanceHidden && !fromWallet.scanning"> {{fromWallet.status.totalBalanceStr ? fromWallet.status.totalBalanceStr : ( fromWallet.cachedBalance ? fromWallet.cachedBalance
+ (fromWallet.cachedBalanceUpdatedOn ? ' &middot; ' + ( fromWallet.cachedBalanceUpdatedOn * 1000 | amTimeAgo)
: '') : '' ) }} </span>
<span *ngIf="fromWallet.scanning" translate> Scanning funds... </span>
<span *ngIf="fromWallet.balanceHidden && !fromWallet.scanning" translate>[Balance Hidden]</span>
<span *ngIf="fromWallet.n > 1">
{{fromWallet.m}}-of-{{fromWallet.n}}
</span>
<ion-icon *ngIf="!fromWallet.balanceHidden && (fromWallet.status.totalBalanceSat != fromWallet.status.spendableAmount)" name="ios-timer-outline"></ion-icon>
<span class="assertive" *ngIf="fromWallet.error">{{fromWallet.error}}</span>
</span>
&nbsp;
</p>
</div>
</button>
</div>
<div *ngIf="toWallets.length > 0">
<ion-item>
<span translate>To</span>
<img item-end *ngIf="toWallet.coin == 'btc'" src="assets/img/icon-bitcoin.svg" width="18">
<img item-end *ngIf="toWallet.coin == 'bch'" src="assets/img/bitcoin-cash-logo.svg" width="22">
</ion-item>
<button ion-item class="wallet-selector" (click)="showWallets('to')">
<img *ngIf="fromWallet.network != 'testnet'" src="assets/img/icon-wallet.svg" class="icon-wallet" />
<div class="wallet-balance">
<span>
{{toWallet.name || toWallet.id}}
</span>
<p>
<span *ngIf="!toWallet.isComplete()" class="assertive" translate>
Incomplete
</span>
<span *ngIf="toWallet.isComplete()">
<span *ngIf="!toWallet.balanceHidden && !toWallet.scanning"> {{toWallet.status.totalBalanceStr ? toWallet.status.totalBalanceStr : ( toWallet.cachedBalance ? toWallet.cachedBalance
+ (toWallet.cachedBalanceUpdatedOn ? ' &middot; ' + ( toWallet.cachedBalanceUpdatedOn * 1000 | amTimeAgo)
: '') : '' ) }} </span>
<span *ngIf="toWallet.scanning" translate> Scanning funds... </span>
<span *ngIf="toWallet.balanceHidden && !toWallet.scanning" translate>[Balance Hidden]</span>
<span class="tab-home__wallet__multisig-number" *ngIf="toWallet.n > 1">
{{toWallet.m}}-of-{{toWallet.n}}
</span>
<ion-icon *ngIf="!fromWallet.balanceHidden && (fromWallet.status.totalBalanceSat != fromWallet.status.spendableAmount)" name="ios-timer-outline"></ion-icon>
<span class="assertive" *ngIf="toWallet.error">{{toWallet.error}}</span>
</span>
&nbsp;
</p>
</div>
</button>
</div>
<ion-item-divider color="light"></ion-item-divider>
<ion-item>
Rate
<ion-note item-end>
<span *ngIf="!rate">...</span>
<span *ngIf="rate">
{{rate.rate}} {{toWallet.coin | uppercase}} per {{fromWallet.coin | uppercase}}
</span>
</ion-note>
</ion-item>
<ion-item>
Limit
<ion-note item-end>
<span *ngIf="!limit">...</span>
<span *ngIf="limit">
{{limit.limit}} {{fromWallet.coin | uppercase}}
</span>
</ion-note>
</ion-item>
<ion-item>
Minimum
<ion-note item-end>
<span *ngIf="!limit">...</span>
<span *ngIf="limit">
{{limit.min}} {{fromWallet.coin | uppercase}}
</span>
</ion-note>
</ion-item>
</ion-list>
<div class="buttons-container">
<!-- TODO: noLowFee -->
<button ion-button type="button" *ngIf="fromWallets.length > 0 && toWallets.length > 0" [disabled]="!fromWallet || !toWallet"
(click)="setAmount()">
Continue
</button>
</div>
</ion-content>

View File

@ -0,0 +1,23 @@
page-shapeshift-shift {
.wallet-selector {
ion-label {
display: flex;
align-items: center;
}
.wallet-balance {
margin-left: 1.5rem;
}
}
.buttons-container {
position: absolute;
bottom: 5vh;
width: 100%;
button {
width: 85%;
max-width: 300px;
margin-left: auto;
margin-right: auto;
display: block;
}
}
}

View File

@ -0,0 +1,158 @@
import { Component } from '@angular/core';
import { NavController, ActionSheetController } from 'ionic-angular';
import { Logger } from '@nsalaun/ng-logger';
import * as _ from 'lodash';
// Pages
import { AmountPage } from './../../../send/amount/amount';
// Providers
import { PopupProvider } from '../../../../providers/popup/popup';
import { ProfileProvider } from '../../../../providers/profile/profile';
import { ShapeshiftProvider } from '../../../../providers/shapeshift/shapeshift';
@Component({
selector: 'page-shapeshift-shift',
templateUrl: 'shapeshift-shift.html',
})
export class ShapeshiftShiftPage {
private walletsBtc: Array<any>;
private walletsBch: Array<any>;
public toWallets: Array<any>;
public fromWallets: Array<any>;
public fromWallet: any;
public toWallet: any;
public rate: number;
public limit: any;
public network: string;
public fromWalletSelectorTitle: string;
public toWalletSelectorTitle: string;
constructor(
private actionSheetCtrl: ActionSheetController,
private logger: Logger,
private navCtrl: NavController,
private popupProvider: PopupProvider,
private profileProvider: ProfileProvider,
private shapeshiftProvider: ShapeshiftProvider
) {
this.walletsBtc = [];
this.walletsBch = [];
this.fromWalletSelectorTitle = 'From';
this.toWalletSelectorTitle = 'To';
this.network = this.shapeshiftProvider.getNetwork();
this.walletsBtc = this.profileProvider.getWallets({
onlyComplete: true,
network: this.network,
coin: 'btc'
});
this.walletsBch = this.profileProvider.getWallets({
onlyComplete: true,
network: this.network,
coin: 'bch'
});
if (_.isEmpty(this.walletsBtc) || _.isEmpty(this.walletsBch)) {
this.showErrorAndBack(null, 'No wallets available to use ShapeShift'); // TODO: gettextCatalog
return;
}
this.fromWallets = _.filter(this.walletsBtc.concat(this.walletsBch), (w: any) => {
// Available balance and 1-signature wallet
return w.status.balance.availableAmount > 0 && w.credentials.m == 1;
});
this.onFromWalletSelect(this.fromWallets[0]);
}
ionViewDidLoad() {
this.logger.info('ionViewDidLoad ShapeshiftShiftPage');
}
private showErrorAndBack(title: string, msg: any): void {
title = title ? title : 'Error'; // TODO: gettextCatalog
this.logger.error(msg);
msg = (msg && msg.errors) ? msg.errors[0].message : msg;
this.popupProvider.ionicAlert(title, msg).then(() => {
this.navCtrl.pop();
});
}
private showToWallets(): void {
this.toWallets = this.fromWallet.coin == 'btc' ? this.walletsBch : this.walletsBtc;
this.onToWalletSelect(this.toWallets[0]);
let pair = this.fromWallet.coin + '_' + this.toWallet.coin;
this.shapeshiftProvider.getRate(pair, (err: any, rate: number) => {
this.rate = rate;
});
this.shapeshiftProvider.getLimit(pair, (err: any, limit: any) => {
this.limit = limit;
});
}
public onFromWalletSelect(wallet: any): void {
this.fromWallet = wallet;
this.showToWallets();
}
public onToWalletSelect(wallet: any): void {
this.toWallet = wallet;
}
public setAmount(): void {
this.navCtrl.push(AmountPage, {
nextPage: 'ShapeshiftConfirmPage',
fixedUnit: true,
coin: this.fromWallet.coin,
walletId: this.fromWallet.id,
toWalletId: this.toWallet.id,
currency: this.fromWallet.coin.toUpperCase(),
shiftMax: this.limit.limit + ' ' + this.fromWallet.coin.toUpperCase(),
shiftMin: this.limit.min + ' ' + this.fromWallet.coin.toUpperCase()
});
}
public showWallets(selector: string): void {
let buttons: Array<any> = [];
let walletsForActionSheet: Array<any> = [];
if (selector == 'from') {
walletsForActionSheet = this.fromWallets;
} else if (selector == 'to') {
walletsForActionSheet = this.toWallets;
}
_.each(walletsForActionSheet, (w: any) => {
let walletButton: Object = {
text: w.credentials.walletName,
cssClass: 'wallet-' + w.network,
icon: 'wallet',
handler: () => {
this.onWalletSelect(w, selector);
}
}
buttons.push(walletButton);
});
const actionSheet = this.actionSheetCtrl.create({
title: selector == 'from' ? this.fromWalletSelectorTitle : this.toWalletSelectorTitle,
buttons: buttons
});
actionSheet.present();
}
public onWalletSelect(wallet: any, selector: string): void {
if (selector == 'from') {
this.onFromWalletSelect(wallet);
} else if (selector == 'to') {
this.onToWalletSelect(wallet);
}
}
}

View File

@ -0,0 +1,63 @@
<ion-header>
<ion-navbar transparent>
<ion-title>shapeShift</ion-title>
</ion-navbar>
</ion-header>
<ion-content *ngIf="!shifts.data">
<div class="box-notification warning" *ngIf="network == 'testnet'">
Sandbox version. Only for testing purpose.
</div>
<div class="integration-onboarding">
<div class="integration-onboarding-logo">
<img src="assets/img/shapeshift/logo-shapeshift.svg" width="200" alt="Shapeshift">
</div>
<div class="integration-onboarding-description">
<h4>The Safest, Fastest Asset Exchange on Earth</h4>
<p>Trade any leading blockchain asset for any other. Protection by Design. No Account Needed.</p>
</div>
<div class="integration-onboarding-cta">
<!-- TODO: no-low-fee -->
<button ion-button (click)="goTo('Shift')">Start</button>
<button ion-button (click)="openExternalLink('https://shapeshift.io')">Visit Shapeshift.io &rarr;</button>
</div>
</div>
</ion-content>
<ion-content *ngIf="shifts.data">
<div class="main-header" (click)="update()">
<img src="assets/img/shapeshift/logo-shapeshift.svg" width="200">
</div>
<ion-list class="shift">
<button ion-item (click)="goTo('Shift')">
<!-- TODO: no-low-fee -->
<img src="assets/img/shapeshift/icon-shapeshift.svg">
<span>Shift</span>
</button>
</ion-list>
<ion-list>
<ion-item-divider color="light">
Transactions
</ion-item-divider>
<button ion-item *ngFor="let item of shifts.data | keys">
<div class="shapeshift-address">
<h2 copy-to-clipboard="{{item.value.address}}">
<span class="item-amount">{{ item.value.amount }}</span>
<span class="ellipsis">{{item.value.title || item.value.address}}</span>
</h2>
<ion-note>{{item.value.date | amTimeAgo}}</ion-note>
<span>
<span class="assertive" *ngIf="item.value.status == 'failed'">Failed</span>
<span class="balanced" *ngIf="item.value.status == 'complete'">Completed</span>
<span class="dark" *ngIf="item.value.status == 'received'">Pending</span>
<span class="text-gray" *ngIf="item.value.status == 'no_deposits'">Pending</span>
</span>
</div>
</button>
</ion-list>
</ion-content>

View File

@ -0,0 +1,66 @@
page-shapeshift {
.toolbar {
background-color: #28394d;
.back-button, &-title {
color: white;
}
.back-button:hover {
color: white;
}
}
.integration-onboarding {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
background: url('../assets/img/shapeshift/shapeshift_background.jpg') center center no-repeat #28394d;
&-logo {
display: inline-block;
margin-bottom: 1em;
img {
max-width: 170px;
}
}
&-description {
color: white;
margin-top: 0;
margin-left: 2rem;
margin-right: 2rem;
margin-bottom: 130px;
opacity: .6;
max-width: 300px;
}
&-cta {
position: absolute;
bottom: 5vh;
width: 100%;
button {
width: 85%;
max-width: 300px;
margin-left: auto;
margin-right: auto;
display: block;
}
}
}
.main-header {
height: 120px;
background: url('../assets/img/shapeshift/shapeshift_background.jpg') center center no-repeat #28394d;
text-align: center;
img {
padding-top: 20px;
}
}
.shift {
margin: 0rem;
img {
width: 30px;
}
ion-label {
display: flex;
align-items: center;
}
}
}

View File

@ -0,0 +1,88 @@
import { Component } from '@angular/core';
import { NavController, Events } from 'ionic-angular';
import { Logger } from '@nsalaun/ng-logger';
import * as _ from 'lodash';
// Pages
import { ShapeshiftShiftPage } from './shapeshift-shift/shapeshift-shift';
// Providers
import { ShapeshiftProvider } from '../../../providers/shapeshift/shapeshift';
import { ExternalLinkProvider } from '../../../providers/external-link/external-link';
@Component({
selector: 'page-shapeshift',
templateUrl: 'shapeshift.html',
})
export class ShapeshiftPage {
public shifts: any;
public network: string;
constructor(
private events: Events,
private externalLinkProvider: ExternalLinkProvider,
private logger: Logger,
private navCtrl: NavController,
private shapeshiftProvider: ShapeshiftProvider
) {
this.network = this.shapeshiftProvider.getNetwork();
this.shifts = { data: {} };
this.init();
}
ionViewDidLoad() {
this.logger.info('ionViewDidLoad ShapeshiftPage');
}
ionViewWillEnter() {
this.events.subscribe('bwsEvent', (walletId: string, type: string, n: any) => {
if (type == 'NewBlock') this.updateShift(this.shifts);
});
}
ionViewWillLeave() {
this.events.unsubscribe('bwsEvent');
}
public openExternalLink(url: string): void {
this.externalLinkProvider.open(url);
}
private updateShift = _.debounce((shifts: any) => {
if (_.isEmpty(shifts.data)) return;
_.forEach(shifts.data, (dataFromStorage: any) => {
this.shapeshiftProvider.getStatus(dataFromStorage.address, (err: any, st: any) => {
if (err) return;
this.shifts.data[st.address]['status'] = st.status;
this.shapeshiftProvider.saveShapeshift(this.shifts.data[st.address], null, (err: any) => {
this.logger.debug("Saved shift with status: " + st.status);
});
});
});
}, 1000, {
'leading': true
});
private init(): void {
this.shapeshiftProvider.getShapeshift((err: any, ss: any) => {
if (err) this.logger.error(err);
this.shifts = { data: ss };
this.updateShift(this.shifts);
});
}
public update(): void {
this.updateShift(this.shifts);
}
public goTo(page: string): void {
switch (page) {
case 'Shift':
this.navCtrl.push(ShapeshiftShiftPage);
break;
}
}
}

View File

@ -44,6 +44,11 @@
<span translate>Amount</span>
</div>
<div *ngIf="shiftMin && shiftMax">
<div>Min: {{shiftMin}}</div>
<div>Max: {{shiftMax}}</div>
</div>
<div class="amount-content">
<div class="expression">{{expression || '0.00'}}
<a class="unit-button" (click)="updateUnit()">{{unit}}</a>

View File

@ -15,6 +15,7 @@ import { SellGlideraPage } from '../../integrations/glidera/sell-glidera/sell-gl
import { ConfirmPage } from '../confirm/confirm';
import { CustomAmountPage } from '../../receive/custom-amount/custom-amount';
import { BuyMercadoLibrePage } from '../../integrations/mercado-libre/buy-mercado-libre/buy-mercado-libre';
import { ShapeshiftConfirmPage } from '../../integrations/shapeshift/shapeshift-confirm/shapeshift-confirm';
@Component({
selector: 'page-amount',
@ -33,6 +34,9 @@ export class AmountPage {
public expression: any;
public amount: any;
public showExpressionResult: boolean;
public shiftMax: number;
public shiftMin: number;
public showSendMax: boolean;
public allowSend: boolean;
public recipientType: string;
@ -46,6 +50,7 @@ export class AmountPage {
public useSendMax: boolean;
public config: any;
public showRecipient: boolean;
public toWalletId: string;
private walletId: any;
@ -58,6 +63,7 @@ export class AmountPage {
private configProvider: ConfigProvider,
private rateProvider: RateProvider,
) {
this.showSendMax = false;
this.config = this.configProvider.get();
this.recipientType = this.navParams.data.recipientType;
this.showRecipient = true;
@ -80,15 +86,18 @@ export class AmountPage {
this.reOp = /^[\*\+\-\/]$/;
this.nextView = this.getNextView();
// Use only with ShapeShift
this.toWalletId = this.navParams.data.toWalletId;
this.shiftMax = this.navParams.data.shiftMax;
this.shiftMin = this.navParams.data.shiftMin;
let unit = this.navParams.data.currency ? this.navParams.data.currency : this.config.wallet.settings.alternativeIsoCode;
this.availableUnits.push(this.coin.toUpperCase());
this.availableUnits.push(unit);
if (this.navParams.data.currency) {
this.availableUnits.push(this.coin.toUpperCase());
this.availableUnits.push(this.navParams.data.currency);
this.unit = this.navParams.data.currency;
}
else {
let unit = this.config.wallet.settings.alternativeIsoCode;
this.availableUnits.push(this.coin.toUpperCase());
this.availableUnits.push(unit);
} else {
this.unit = this.availableUnits[0];
}
this.isFiatAmount = this.unit != 'BCH' && this.unit != 'BTC' ? true : false;
@ -138,7 +147,13 @@ export class AmountPage {
this.showRecipient = false;
nextPage = BuyMercadoLibrePage;
break;
case 'ShapeshiftConfirmPage':
this.showSendMax = true;
this.showRecipient = false;
nextPage = ShapeshiftConfirmPage;
break;
default:
this.showSendMax = true;
nextPage = ConfirmPage;
}
return nextPage;
@ -269,7 +284,8 @@ export class AmountPage {
coin: this.coin,
network: this.network,
useSendMax: this.useSendMax,
walletId: this.walletId
walletId: this.walletId,
toWalletId: this.toWalletId ? this.toWalletId : null
}
this.navCtrl.push(this.nextView, data);
}

View File

@ -1,13 +1,13 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Logger } from '@nsalaun/ng-logger';
import * as _ from 'lodash';
//providers
import { HomeIntegrationsProvider } from '../home-integrations/home-integrations';
import { PersistenceProvider } from '../persistence/persistence';
import { NextStepsProvider } from '../next-steps/next-steps';
import * as _ from 'lodash';
@Injectable()
export class AmazonProvider {
@ -76,12 +76,10 @@ export class AmazonProvider {
}
inv = JSON.stringify(inv);
this.persistenceProvider.setAmazonGiftCards(network, inv).then((err) => {
this.homeIntegrationsProvider.register(this.homeItem);
this.nextStepsProvider.unregister(this.nextStepItem.name);
return cb(err);
});
this.persistenceProvider.setAmazonGiftCards(network, inv);
this.homeIntegrationsProvider.register(this.homeItem);
this.nextStepsProvider.unregister(this.nextStepItem.name);
return cb(null);
});
}

View File

@ -474,16 +474,28 @@ export class PersistenceProvider {
});
};
setMercadoLibreGiftCards(network, gcs): Promise<void> {
setMercadoLibreGiftCards(network: string, gcs): Promise<void> {
return this.storage.set((Keys.MERCADO_LIBRE(network)), gcs);
};
getMercadoLibreGiftCards(network): Promise<void> {
getMercadoLibreGiftCards(network: string): Promise<void> {
return this.storage.get((Keys.MERCADO_LIBRE(network)));
};
removeMercadoLibreGiftCards(network): Promise<void> {
removeMercadoLibreGiftCards(network: string): Promise<void> {
return this.storage.remove((Keys.MERCADO_LIBRE(network)));
};
setShapeshift(network: string, gcs: any): Promise<void> {
return this.storage.set('shapeShift-' + network, gcs);
};
getShapeshift(network: string): Promise<void> {
return this.storage.get('shapeShift-' + network);
};
removeShapeshift(network: string): Promise<void> {
return this.storage.remove('shapeShift-' + network);
};
}

View File

@ -0,0 +1,157 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Logger } from '@nsalaun/ng-logger';
import * as _ from 'lodash';
//providers
import { AppProvider } from '../app/app';
import { HomeIntegrationsProvider } from '../home-integrations/home-integrations';
import { NextStepsProvider } from '../next-steps/next-steps';
import { PersistenceProvider } from '../persistence/persistence';
@Injectable()
export class ShapeshiftProvider {
private credentials: any;
private homeItem: any;
constructor(
private appProvider: AppProvider,
private homeIntegrationsProvider: HomeIntegrationsProvider,
private http: HttpClient,
private logger: Logger,
private nextStepsProvider: NextStepsProvider,
private persistenceProvider: PersistenceProvider
) {
this.logger.info('Hello ShapeshiftProvider Provider');
this.credentials = {};
// (Optional) Affiliate PUBLIC KEY, for volume tracking, affiliate payments, split-shifts, etc.
if (this.appProvider.servicesInfo && this.appProvider.servicesInfo.shapeshift) {
this.credentials.API_KEY = this.appProvider.servicesInfo.shapeshift.api_key || null;
}
/*
* Development: 'testnet'
* Production: 'livenet'
*/
this.credentials.NETWORK = 'livenet';
//credentials.NETWORK = 'testnet';
if (this.credentials.NETWORK == 'testnet') {
this.credentials.API_URL = "";
} else {
// CORS: cors.shapeshift.io
this.credentials.API_URL = "https://shapeshift.io";
}
this.homeItem = {
name: 'shapeshift',
title: 'ShapeShift',
icon: 'assets/img/shapeshift/icon-shapeshift.svg',
page: 'ShapeshiftPage',
};
}
public getNetwork() {
return this.credentials.NETWORK;
}
public shift(data: any, cb): any {
let dataSrc = {
withdrawal: data.withdrawal,
pair: data.pair,
returnAddress: data.returnAddress,
apiKey: this.credentials.API_KEY
};
this.http.post(this.credentials.API_URL + '/shift', dataSrc).subscribe((data: any) => {
this.logger.info('Shapeshift SHIFT: SUCCESS');
return cb(null, data);
}, (data) => {
this.logger.error('Shapeshift SHIFT ERROR: ' + data.error.message);
return cb(data);
});
}
public saveShapeshift(data: any, opts: any, cb): void {
let network = this.getNetwork();
this.persistenceProvider.getShapeshift(network).then((oldData: any) => {
if (_.isString(oldData)) {
oldData = JSON.parse(oldData);
}
if (_.isString(data)) {
data = JSON.parse(data);
}
let inv = oldData ? oldData : {};
inv[data.address] = data;
if (opts && (opts.error || opts.status)) {
inv[data.address] = _.assign(inv[data.address], opts);
}
if (opts && opts.remove) {
delete (inv[data.address]);
}
inv = JSON.stringify(inv);
this.persistenceProvider.setShapeshift(network, inv);
this.homeIntegrationsProvider.register(this.homeItem);
this.nextStepsProvider.unregister(this.homeItem.name);
return cb(null);
}).catch((err: any) => {
return cb(err);
});
}
public getShapeshift(cb) {
var network = this.getNetwork();
this.persistenceProvider.getShapeshift(network).then((ss: any) => {
var _gcds = ss ? JSON.parse(ss) : null;
return cb(null, _gcds);
}).catch((err: any) => {
return cb(err, null);
});
}
public getRate(pair: string, cb) {
this.http.get(this.credentials.API_URL + '/rate/' + pair).subscribe((data: any) => {
this.logger.info('Shapeshift PAIR: SUCCESS');
return cb(null, data);
}, (data: any) => {
this.logger.error('Shapeshift PAIR ERROR: ' + data.error.message);
return cb(data);
});
}
public getLimit(pair: string, cb) {
this.http.get(this.credentials.API_URL + '/limit/' + pair).subscribe((data: any) => {
this.logger.info('Shapeshift LIMIT: SUCCESS');
return cb(null, data);
}, (data: any) => {
this.logger.error('Shapeshift LIMIT ERROR: ' + data.error.message);
return cb(data);
});
}
public getStatus(addr: string, cb) {
this.http.get(this.credentials.API_URL + '/txStat/' + addr).subscribe((data: any) => {
this.logger.info('Shapeshift STATUS: SUCCESS');
return cb(null, data);
}, (data: any) => {
this.logger.error('Shapeshift STATUS ERROR: ' + data.error.message);
return cb(data);
});
}
public register(): void {
this.persistenceProvider.getShapeshift(this.getNetwork()).then((ss: any) => {
if (ss) {
this.homeIntegrationsProvider.register(this.homeItem);
} else {
this.nextStepsProvider.register(this.homeItem);
}
});
}
}