Add Monero swap support (#1985)
* Adjust emailTo to desired email addresses * Prettierize SupportFooter * Add monero swap support * Adjust styling and wording for the PaymentInfo screen for XMR * Only show warning on XMR swaps (duh) * Replicate comment * Fix styling for rates and payment info * Add a Monero donation address and use it as the placeholder for the ReceivingAddress Input
This commit is contained in:
parent
0fa1b7004d
commit
3ae3622ff4
|
@ -26,7 +26,7 @@ export const SHAPESHIFT_TOKEN_WHITELIST = [
|
||||||
'TRST',
|
'TRST',
|
||||||
'GUP'
|
'GUP'
|
||||||
];
|
];
|
||||||
export const SHAPESHIFT_WHITELIST = [...SHAPESHIFT_TOKEN_WHITELIST, 'ETH', 'ETC', 'BTC'];
|
export const SHAPESHIFT_WHITELIST = [...SHAPESHIFT_TOKEN_WHITELIST, 'ETH', 'ETC', 'BTC', 'XMR'];
|
||||||
|
|
||||||
interface IPairData {
|
interface IPairData {
|
||||||
limit: number;
|
limit: number;
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
@import 'common/sass/variables';
|
||||||
|
@import 'common/sass/mixins';
|
||||||
|
|
||||||
|
.Warning {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-top: 2px solid $brand-danger;
|
||||||
|
padding: $space-sm;
|
||||||
|
font-size: $font-size-base;
|
||||||
|
line-height: 1.5;
|
||||||
|
font-weight: 500;
|
||||||
|
box-shadow: 0 1px 1px 1px rgba(#000, 0.12);
|
||||||
|
margin-bottom: $space;
|
||||||
|
|
||||||
|
&.highlighted {
|
||||||
|
background-color: lighten($brand-danger, 30%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
width: 32px;
|
||||||
|
margin-left: $space * 0.4;
|
||||||
|
margin-right: $space * 0.8;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 32px;
|
||||||
|
color: $brand-danger;
|
||||||
|
}
|
||||||
|
&-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0 $space;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import './Warning.scss';
|
||||||
|
|
||||||
|
interface WarningProps {
|
||||||
|
highlighted?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Warning: React.SFC<WarningProps> = ({ highlighted, children }) => {
|
||||||
|
const className = `Warning ${highlighted ? 'highlighted' : ''}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={className}>
|
||||||
|
<section className="Warning-icon">
|
||||||
|
<i className="fa fa-exclamation-triangle" />
|
||||||
|
</section>
|
||||||
|
<section className="Warning-content">{children}</section>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Warning;
|
|
@ -16,5 +16,6 @@ export { default as TextArea } from './TextArea';
|
||||||
export { default as Address } from './Address';
|
export { default as Address } from './Address';
|
||||||
export { default as CodeBlock } from './CodeBlock';
|
export { default as CodeBlock } from './CodeBlock';
|
||||||
export { default as Toggle } from './Toggle';
|
export { default as Toggle } from './Toggle';
|
||||||
|
export { default as Warning } from './Warning';
|
||||||
export * from './Expandable';
|
export * from './Expandable';
|
||||||
export * from './InlineSpinner';
|
export * from './InlineSpinner';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { BTCTxExplorer, ETHTxExplorer } from './data';
|
import { BTCTxExplorer, ETHTxExplorer } from './data';
|
||||||
|
|
||||||
export type WhitelistedCoins = 'BTC' | 'REP' | 'ETH';
|
export type WhitelistedCoins = 'BTC' | 'REP' | 'ETH' | 'XMR';
|
||||||
const serverURL = 'https://bity.myetherapi.com';
|
const serverURL = 'https://bity.myetherapi.com';
|
||||||
const bityURL = 'https://bity.com/api';
|
const bityURL = 'https://bity.com/api';
|
||||||
const BTCMin = 0.01;
|
const BTCMin = 0.01;
|
||||||
|
@ -11,7 +11,8 @@ const BTCMax = 3;
|
||||||
// value = percent higher/lower than 0.01 BTC worth
|
// value = percent higher/lower than 0.01 BTC worth
|
||||||
const buffers = {
|
const buffers = {
|
||||||
ETH: 0.1,
|
ETH: 0.1,
|
||||||
REP: 0.2
|
REP: 0.2,
|
||||||
|
XMR: 0.3
|
||||||
};
|
};
|
||||||
|
|
||||||
// rate must be BTC[KIND]
|
// rate must be BTC[KIND]
|
||||||
|
|
|
@ -38,7 +38,9 @@ export const etherChainExplorerInst = makeExplorer({
|
||||||
export const donationAddressMap = {
|
export const donationAddressMap = {
|
||||||
BTC: '32oirLEzZRhi33RCXDF9WHJjEb8RsrSss3',
|
BTC: '32oirLEzZRhi33RCXDF9WHJjEb8RsrSss3',
|
||||||
ETH: '0x4bbeEB066eD09B7AEd07bF39EEe0460DFa261520',
|
ETH: '0x4bbeEB066eD09B7AEd07bF39EEe0460DFa261520',
|
||||||
REP: '0x4bbeEB066eD09B7AEd07bF39EEe0460DFa261520'
|
REP: '0x4bbeEB066eD09B7AEd07bF39EEe0460DFa261520',
|
||||||
|
XMR:
|
||||||
|
'4GdoN7NCTi8a5gZug7PrwZNKjvHFmKeV11L6pNJPgj5QNEHsN6eeX3DaAQFwZ1ufD4LYCZKArktt113W7QjWvQ7CW7F7tDFvS511SNfZV7'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const gasEstimateCacheTime = 60000;
|
export const gasEstimateCacheTime = 60000;
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
@import 'common/sass/variables';
|
|
||||||
@import 'common/sass/mixins';
|
|
||||||
|
|
||||||
.WelcomeSlide {
|
|
||||||
&-alert {
|
|
||||||
display: flex;
|
|
||||||
border-top: 2px solid color(brand-danger);
|
|
||||||
padding: $space-sm;
|
|
||||||
font-size: $font-size-base;
|
|
||||||
line-height: 1.5;
|
|
||||||
font-weight: 500;
|
|
||||||
box-shadow: 0 1px 1px 1px rgba(#000, 0.12);
|
|
||||||
margin-bottom: $space;
|
|
||||||
|
|
||||||
&-icon {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
width: 32px;
|
|
||||||
margin-left: $space * 0.4;
|
|
||||||
margin-right: $space * 0.8;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 32px;
|
|
||||||
color: color(brand-danger);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,31 +2,20 @@ import React from 'react';
|
||||||
|
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import onboardIconOne from 'assets/images/onboarding/slide-01.svg';
|
import onboardIconOne from 'assets/images/onboarding/slide-01.svg';
|
||||||
|
import { Warning } from 'components/ui';
|
||||||
import OnboardSlide from './OnboardSlide';
|
import OnboardSlide from './OnboardSlide';
|
||||||
|
|
||||||
import './WelcomeSlide.scss';
|
|
||||||
|
|
||||||
const WelcomeSlide = () => {
|
const WelcomeSlide = () => {
|
||||||
const header = translate('ONBOARD_WELCOME_TITLE');
|
const header = translate('ONBOARD_WELCOME_TITLE');
|
||||||
const subheader = <small>{translate('ONBOARD_WELCOME_CONTENT__3')}</small>;
|
const subheader = <small>{translate('ONBOARD_WELCOME_CONTENT__3')}</small>;
|
||||||
|
|
||||||
const content = (
|
const content = (
|
||||||
<div>
|
<div>
|
||||||
<div className="WelcomeSlide-alert">
|
<Warning>
|
||||||
<div className="WelcomeSlide-alert-icon">
|
{translate('ONBOARD_WELCOME_CONTENT__1')}
|
||||||
<i className="fa fa-exclamation-triangle" />
|
{translate('ONBOARD_WELCOME_CONTENT__2')}
|
||||||
</div>
|
</Warning>
|
||||||
<span>
|
<Warning>{translate('ONBOARD_WELCOME_CONTENT__8')}</Warning>
|
||||||
{translate('ONBOARD_WELCOME_CONTENT__1')}
|
|
||||||
{translate('ONBOARD_WELCOME_CONTENT__2')}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="WelcomeSlide-alert">
|
|
||||||
<div className="WelcomeSlide-alert-icon">
|
|
||||||
<i className="fa fa-exclamation-triangle" />
|
|
||||||
</div>
|
|
||||||
{translate('ONBOARD_WELCOME_CONTENT__8')}
|
|
||||||
</div>
|
|
||||||
<h5>{translate('ONBOARD_WELCOME_CONTENT__4')}</h5>
|
<h5>{translate('ONBOARD_WELCOME_CONTENT__4')}</h5>
|
||||||
<ul>
|
<ul>
|
||||||
<li>{translate('ONBOARD_WELCOME_CONTENT__5')}</li>
|
<li>{translate('ONBOARD_WELCOME_CONTENT__5')}</li>
|
||||||
|
|
|
@ -40,12 +40,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&-input {
|
&-input {
|
||||||
|
color: #fff;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 16%;
|
width: 16%;
|
||||||
min-width: 3.5rem;
|
min-width: 3.5rem;
|
||||||
height: 2rem;
|
height: 2rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
margin: .5rem auto;
|
margin: 0.5rem auto;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
margin-right: $space;
|
margin-right: $space;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|
|
@ -27,6 +27,8 @@ interface ReduxStateProps {
|
||||||
bityOrderStatus: string | null;
|
bityOrderStatus: string | null;
|
||||||
shapeshiftOrderStatus: string | null;
|
shapeshiftOrderStatus: string | null;
|
||||||
outputTx: any;
|
outputTx: any;
|
||||||
|
paymentId: string | null;
|
||||||
|
xmrPaymentAddress: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ReduxActionProps {
|
interface ReduxActionProps {
|
||||||
|
@ -68,6 +70,8 @@ export default class PartThree extends PureComponent<ReduxActionProps & ReduxSta
|
||||||
shapeshiftOrderStatus,
|
shapeshiftOrderStatus,
|
||||||
destinationAddress,
|
destinationAddress,
|
||||||
outputTx,
|
outputTx,
|
||||||
|
paymentId,
|
||||||
|
xmrPaymentAddress,
|
||||||
// ACTIONS
|
// ACTIONS
|
||||||
showNotificationWithComponent
|
showNotificationWithComponent
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
@ -85,7 +89,9 @@ export default class PartThree extends PureComponent<ReduxActionProps & ReduxSta
|
||||||
|
|
||||||
const PaymentInfoProps = {
|
const PaymentInfoProps = {
|
||||||
origin,
|
origin,
|
||||||
paymentAddress
|
paymentAddress,
|
||||||
|
paymentId,
|
||||||
|
xmrPaymentAddress
|
||||||
};
|
};
|
||||||
|
|
||||||
const BitcoinQRProps = {
|
const BitcoinQRProps = {
|
||||||
|
|
|
@ -1,18 +1,31 @@
|
||||||
@import "common/sass/variables";
|
@import 'common/sass/variables';
|
||||||
@import "common/sass/mixins";
|
@import 'common/sass/mixins';
|
||||||
|
|
||||||
.SwapPayment {
|
.SwapPayment {
|
||||||
text-align: center;
|
> * {
|
||||||
margin-bottom: $space;
|
margin: $space auto 0;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 620px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
& > input {
|
||||||
|
margin-top: $space;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
&-address {
|
&-address {
|
||||||
|
@include mono;
|
||||||
|
|
||||||
display: block;
|
display: block;
|
||||||
margin: $space auto 0;
|
|
||||||
max-width: 620px;
|
|
||||||
width: 100%;
|
|
||||||
font-size: $font-size-medium;
|
font-size: $font-size-medium;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@include mono;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $screen-sm) {
|
@media screen and (max-width: $screen-sm) {
|
||||||
|
@ -20,4 +33,16 @@
|
||||||
font-size: $font-size-base;
|
font-size: $font-size-base;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-payment-id {
|
||||||
|
h2 {
|
||||||
|
font-weight: bolder;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-link {
|
||||||
|
font-size: $font-size-small;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,19 +2,47 @@ import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import { SwapInput } from 'features/swap/types';
|
import { SwapInput } from 'features/swap/types';
|
||||||
import { Input } from 'components/ui';
|
import { Input, Warning } from 'components/ui';
|
||||||
import './PaymentInfo.scss';
|
import './PaymentInfo.scss';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
origin: SwapInput;
|
origin: SwapInput;
|
||||||
paymentAddress: string | null;
|
paymentAddress: string | null;
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* For XMR swaps, the "deposit" property in the response
|
||||||
|
* actually refers to the "paymentId", not the payment address.
|
||||||
|
*/
|
||||||
|
paymentId: string | null;
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* For XMR swap, the actual payment address is the "sAddress"
|
||||||
|
* property in the response.
|
||||||
|
*/
|
||||||
|
xmrPaymentAddress: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class PaymentInfo extends PureComponent<Props, {}> {
|
export default class PaymentInfo extends PureComponent<Props, {}> {
|
||||||
public render() {
|
public render() {
|
||||||
const { origin } = this.props;
|
const { origin, paymentAddress, paymentId, xmrPaymentAddress } = this.props;
|
||||||
|
const isXMRSwap = origin.label === 'XMR';
|
||||||
|
const actualPaymentAddress = isXMRSwap ? xmrPaymentAddress : paymentAddress;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="SwapPayment">
|
<section className="SwapPayment">
|
||||||
|
{isXMRSwap && (
|
||||||
|
<section className="SwapPayment-payment-id">
|
||||||
|
<h2>
|
||||||
|
{translate('USING_PAYMENT_ID')}
|
||||||
|
<Input
|
||||||
|
className="SwapPayment-address"
|
||||||
|
isValid={!!paymentId}
|
||||||
|
value={paymentId || undefined}
|
||||||
|
disabled={true}
|
||||||
|
/>
|
||||||
|
</h2>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
<h2>
|
<h2>
|
||||||
{translate('SWAP_SEND_TO', {
|
{translate('SWAP_SEND_TO', {
|
||||||
$origin_amount: origin.amount.toString(),
|
$origin_amount: origin.amount.toString(),
|
||||||
|
@ -22,11 +50,16 @@ export default class PaymentInfo extends PureComponent<Props, {}> {
|
||||||
})}
|
})}
|
||||||
<Input
|
<Input
|
||||||
className="SwapPayment-address"
|
className="SwapPayment-address"
|
||||||
isValid={!!this.props.paymentAddress}
|
isValid={!!actualPaymentAddress}
|
||||||
value={this.props.paymentAddress || undefined}
|
value={actualPaymentAddress || undefined}
|
||||||
disabled={true}
|
disabled={true}
|
||||||
/>
|
/>
|
||||||
</h2>
|
</h2>
|
||||||
|
{isXMRSwap && (
|
||||||
|
<Warning highlighted={true}>
|
||||||
|
<h4>{translate('PAYMENT_ID_WARNING')}</h4>
|
||||||
|
</Warning>
|
||||||
|
)}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
import { donationAddressMap } from 'config';
|
import { donationAddressMap, WhitelistedCoins } from 'config';
|
||||||
import translate, { translateRaw } from 'translations';
|
import translate, { translateRaw } from 'translations';
|
||||||
import { isValidBTCAddress, isValidETHAddress } from 'libs/validators';
|
import { isValidBTCAddress, isValidETHAddress, isValidXMRAddress } from 'libs/validators';
|
||||||
import { combineAndUpper } from 'utils/formatters';
|
import { combineAndUpper } from 'utils/formatters';
|
||||||
import { SwapInput } from 'features/swap/types';
|
import { SwapInput } from 'features/swap/types';
|
||||||
import {
|
import {
|
||||||
|
@ -18,7 +18,7 @@ import './ReceivingAddress.scss';
|
||||||
|
|
||||||
export interface StateProps {
|
export interface StateProps {
|
||||||
origin: SwapInput;
|
origin: SwapInput;
|
||||||
destinationId: keyof typeof donationAddressMap;
|
destinationId: WhitelistedCoins;
|
||||||
isPostingOrder: boolean;
|
isPostingOrder: boolean;
|
||||||
destinationAddress: string;
|
destinationAddress: string;
|
||||||
destinationKind: number;
|
destinationKind: number;
|
||||||
|
@ -62,13 +62,22 @@ export default class ReceivingAddress extends PureComponent<StateProps & ActionP
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const { destinationId, destinationAddress, isPostingOrder } = this.props;
|
const { destinationId, destinationAddress, isPostingOrder } = this.props;
|
||||||
let validAddress;
|
|
||||||
// TODO - find better pattern here once currencies move beyond BTC, ETH, REP
|
const addressValidators: { [coinOrToken: string]: (address: string) => boolean } = {
|
||||||
if (destinationId === 'BTC') {
|
BTC: isValidBTCAddress,
|
||||||
validAddress = isValidBTCAddress(destinationAddress);
|
XMR: isValidXMRAddress,
|
||||||
} else {
|
ETH: isValidETHAddress
|
||||||
validAddress = isValidETHAddress(destinationAddress);
|
};
|
||||||
}
|
// If there is no matching validator for the ID, assume it's a token and use ETH.
|
||||||
|
const addressValidator = addressValidators[destinationId] || addressValidators.ETH;
|
||||||
|
const validAddress = addressValidator(destinationAddress);
|
||||||
|
|
||||||
|
const placeholders: { [coinOrToken: string]: string } = {
|
||||||
|
BTC: donationAddressMap.BTC,
|
||||||
|
XMR: donationAddressMap.XMR,
|
||||||
|
ETH: donationAddressMap.ETH
|
||||||
|
};
|
||||||
|
const placeholder = placeholders[destinationId] || donationAddressMap.ETH;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="SwapAddress block">
|
<section className="SwapAddress block">
|
||||||
|
@ -85,11 +94,7 @@ export default class ReceivingAddress extends PureComponent<StateProps & ActionP
|
||||||
type="text"
|
type="text"
|
||||||
value={destinationAddress}
|
value={destinationAddress}
|
||||||
onChange={this.onChangeDestinationAddress}
|
onChange={this.onChangeDestinationAddress}
|
||||||
placeholder={
|
placeholder={placeholder}
|
||||||
destinationId === 'BTC'
|
|
||||||
? donationAddressMap[destinationId]
|
|
||||||
: donationAddressMap.ETH
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -34,6 +34,8 @@ interface ReduxStateProps {
|
||||||
bityOrderStatus: string | null;
|
bityOrderStatus: string | null;
|
||||||
shapeshiftOrderStatus: string | null;
|
shapeshiftOrderStatus: string | null;
|
||||||
paymentAddress: string | null;
|
paymentAddress: string | null;
|
||||||
|
paymentId: string | null;
|
||||||
|
xmrPaymentAddress: string | null;
|
||||||
isOffline: boolean;
|
isOffline: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +79,8 @@ class Swap extends Component<ReduxActionProps & ReduxStateProps & RouteComponent
|
||||||
shapeshiftOrderStatus,
|
shapeshiftOrderStatus,
|
||||||
isPostingOrder,
|
isPostingOrder,
|
||||||
outputTx,
|
outputTx,
|
||||||
|
paymentId,
|
||||||
|
xmrPaymentAddress,
|
||||||
// ACTIONS
|
// ACTIONS
|
||||||
initSwap,
|
initSwap,
|
||||||
restartSwap,
|
restartSwap,
|
||||||
|
@ -159,7 +163,9 @@ class Swap extends Component<ReduxActionProps & ReduxStateProps & RouteComponent
|
||||||
stopPollShapeshiftOrderStatus,
|
stopPollShapeshiftOrderStatus,
|
||||||
showNotificationWithComponent,
|
showNotificationWithComponent,
|
||||||
destinationAddress,
|
destinationAddress,
|
||||||
outputTx
|
outputTx,
|
||||||
|
paymentId,
|
||||||
|
xmrPaymentAddress
|
||||||
};
|
};
|
||||||
|
|
||||||
const SupportProps = {
|
const SupportProps = {
|
||||||
|
@ -220,6 +226,8 @@ function mapStateToProps(state: AppState) {
|
||||||
bityOrderStatus: state.swap.bityOrderStatus,
|
bityOrderStatus: state.swap.bityOrderStatus,
|
||||||
shapeshiftOrderStatus: state.swap.shapeshiftOrderStatus,
|
shapeshiftOrderStatus: state.swap.shapeshiftOrderStatus,
|
||||||
paymentAddress: state.swap.paymentAddress,
|
paymentAddress: state.swap.paymentAddress,
|
||||||
|
paymentId: state.swap.paymentId,
|
||||||
|
xmrPaymentAddress: state.swap.xmrPaymentAddress,
|
||||||
isOffline: getOffline(state)
|
isOffline: getOffline(state)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -234,7 +234,8 @@ describe('swap reducer', () => {
|
||||||
maxLimit: 7.04575258,
|
maxLimit: 7.04575258,
|
||||||
apiPubKey:
|
apiPubKey:
|
||||||
'0ca1ccd50b708a3f8c02327f0caeeece06d3ddc1b0ac749a987b453ee0f4a29bdb5da2e53bc35e57fb4bb7ae1f43c93bb098c3c4716375fc1001c55d8c94c160',
|
'0ca1ccd50b708a3f8c02327f0caeeece06d3ddc1b0ac749a987b453ee0f4a29bdb5da2e53bc35e57fb4bb7ae1f43c93bb098c3c4716375fc1001c55d8c94c160',
|
||||||
minerFee: '1.05'
|
minerFee: '1.05',
|
||||||
|
sAddress: '0x055ed77933388642fdn4px9v73j4fa3582d10c4'
|
||||||
};
|
};
|
||||||
|
|
||||||
const swapState = reducer.swapReducer(
|
const swapState = reducer.swapReducer(
|
||||||
|
@ -254,8 +255,10 @@ describe('swap reducer', () => {
|
||||||
validFor: swapState.validFor,
|
validFor: swapState.validFor,
|
||||||
orderTimestampCreatedISOString: swapState.orderTimestampCreatedISOString,
|
orderTimestampCreatedISOString: swapState.orderTimestampCreatedISOString,
|
||||||
paymentAddress: mockedShapeshiftOrder.deposit,
|
paymentAddress: mockedShapeshiftOrder.deposit,
|
||||||
|
paymentId: mockedShapeshiftOrder.deposit,
|
||||||
shapeshiftOrderStatus: 'no_deposits',
|
shapeshiftOrderStatus: 'no_deposits',
|
||||||
orderId: mockedShapeshiftOrder.orderId
|
orderId: mockedShapeshiftOrder.orderId,
|
||||||
|
xmrPaymentAddress: mockedShapeshiftOrder.sAddress
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,9 @@ export const INITIAL_STATE: types.SwapState = {
|
||||||
paymentAddress: null,
|
paymentAddress: null,
|
||||||
validFor: null,
|
validFor: null,
|
||||||
orderId: null,
|
orderId: null,
|
||||||
showLiteSend: false
|
showLiteSend: false,
|
||||||
|
paymentId: null,
|
||||||
|
xmrPaymentAddress: null
|
||||||
};
|
};
|
||||||
|
|
||||||
export function swapReducer(state: types.SwapState = INITIAL_STATE, action: types.SwapAction) {
|
export function swapReducer(state: types.SwapState = INITIAL_STATE, action: types.SwapAction) {
|
||||||
|
@ -151,8 +153,8 @@ export function swapReducer(state: types.SwapState = INITIAL_STATE, action: type
|
||||||
};
|
};
|
||||||
case types.SwapActions.SHAPESHIFT_ORDER_CREATE_SUCCEEDED:
|
case types.SwapActions.SHAPESHIFT_ORDER_CREATE_SUCCEEDED:
|
||||||
const currDate = Date.now();
|
const currDate = Date.now();
|
||||||
|
|
||||||
const secondsRemaining = Math.floor((+new Date(action.payload.expiration) - currDate) / 1000);
|
const secondsRemaining = Math.floor((+new Date(action.payload.expiration) - currDate) / 1000);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
shapeshiftOrder: {
|
shapeshiftOrder: {
|
||||||
|
@ -166,7 +168,10 @@ export function swapReducer(state: types.SwapState = INITIAL_STATE, action: type
|
||||||
orderTimestampCreatedISOString: new Date(currDate).toISOString(),
|
orderTimestampCreatedISOString: new Date(currDate).toISOString(),
|
||||||
paymentAddress: action.payload.deposit,
|
paymentAddress: action.payload.deposit,
|
||||||
shapeshiftOrderStatus: 'no_deposits',
|
shapeshiftOrderStatus: 'no_deposits',
|
||||||
orderId: action.payload.orderId
|
orderId: action.payload.orderId,
|
||||||
|
// For XMR swaps
|
||||||
|
paymentId: action.payload.deposit,
|
||||||
|
xmrPaymentAddress: action.payload.sAddress
|
||||||
};
|
};
|
||||||
case types.SwapActions.BITY_ORDER_STATUS_SUCCEEDED:
|
case types.SwapActions.BITY_ORDER_STATUS_SUCCEEDED:
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -24,6 +24,18 @@ export interface SwapState {
|
||||||
validFor: number | null;
|
validFor: number | null;
|
||||||
orderId: string | null;
|
orderId: string | null;
|
||||||
showLiteSend: boolean;
|
showLiteSend: boolean;
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* For XMR swaps, the "deposit" property in the response
|
||||||
|
* actually refers to the "paymentId", not the payment address.
|
||||||
|
*/
|
||||||
|
paymentId: string | null;
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* For XMR swap, the actual payment address is the "sAddress"
|
||||||
|
* property in the response.
|
||||||
|
*/
|
||||||
|
xmrPaymentAddress: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SwapActions {
|
export enum SwapActions {
|
||||||
|
@ -208,6 +220,7 @@ export interface ShapeshiftOrderResponse {
|
||||||
quotedRate: string;
|
quotedRate: string;
|
||||||
withdrawal: string;
|
withdrawal: string;
|
||||||
withdrawalAmount: string;
|
withdrawalAmount: string;
|
||||||
|
sAddress?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ShapeshiftStatusResponse {
|
export interface ShapeshiftStatusResponse {
|
||||||
|
|
|
@ -59,6 +59,12 @@ export function isValidBTCAddress(address: string): boolean {
|
||||||
return WalletAddressValidator.validate(address, 'BTC');
|
return WalletAddressValidator.validate(address, 'BTC');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isValidXMRAddress(address: string): boolean {
|
||||||
|
return !!address.match(
|
||||||
|
/4[0-9AB][123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{93}/
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function isValidHex(str: string): boolean {
|
export function isValidHex(str: string): boolean {
|
||||||
if (str === '') {
|
if (str === '') {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -660,6 +660,9 @@
|
||||||
"NETWORK_2": "network",
|
"NETWORK_2": "network",
|
||||||
"PROVIDED_BY": "provided by",
|
"PROVIDED_BY": "provided by",
|
||||||
"YOU_ARE_INTERACTING": "You are interacting with the",
|
"YOU_ARE_INTERACTING": "You are interacting with the",
|
||||||
|
"USING_PAYMENT_ID": "Using the required Payment ID of:",
|
||||||
|
"PAYMENT_ID_WARNING": "Don't forget to send your XMR with the payment ID [[?]](https://getmonero.org/resources/moneropedia/paymentid.html) above, or you WILL lose your funds.",
|
||||||
|
"WHAT_IS_PAYMENT_ID": "what's a payment ID?",
|
||||||
"ANNOUNCEMENT_MESSAGE": "Welcome to the new MyCrypto. We hope you like it! If it's urgent and you need the old site, you can still use [MyCrypto Legacy](https://legacy.mycrypto.com)",
|
"ANNOUNCEMENT_MESSAGE": "Welcome to the new MyCrypto. We hope you like it! If it's urgent and you need the old site, you can still use [MyCrypto Legacy](https://legacy.mycrypto.com)",
|
||||||
"U2F_NOT_SUPPORTED": "The U2F standard that hardware wallets use does not seem to be supported by your browser. Please try again using Google Chrome."
|
"U2F_NOT_SUPPORTED": "The U2F standard that hardware wallets use does not seem to be supported by your browser. Please try again using Google Chrome."
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,7 @@ exports[`render snapshot 1`] = `
|
||||||
}
|
}
|
||||||
outputTx={null}
|
outputTx={null}
|
||||||
paymentAddress={null}
|
paymentAddress={null}
|
||||||
|
paymentId={null}
|
||||||
provider="shapeshift"
|
provider="shapeshift"
|
||||||
restartSwap={[Function]}
|
restartSwap={[Function]}
|
||||||
secondsRemaining={null}
|
secondsRemaining={null}
|
||||||
|
@ -100,5 +101,6 @@ exports[`render snapshot 1`] = `
|
||||||
stopPollBityOrderStatus={[Function]}
|
stopPollBityOrderStatus={[Function]}
|
||||||
stopPollShapeshiftOrderStatus={[Function]}
|
stopPollShapeshiftOrderStatus={[Function]}
|
||||||
swapProvider={[Function]}
|
swapProvider={[Function]}
|
||||||
|
xmrPaymentAddress={null}
|
||||||
/>
|
/>
|
||||||
`;
|
`;
|
||||||
|
|
Loading…
Reference in New Issue