diff --git a/src/modules/utils/modals/sendAsset/AssetSendCtrl.js b/src/modules/utils/modals/sendAsset/AssetSendCtrl.js index 340032d8cc..e8e72f5976 100644 --- a/src/modules/utils/modals/sendAsset/AssetSendCtrl.js +++ b/src/modules/utils/modals/sendAsset/AssetSendCtrl.js @@ -11,9 +11,12 @@ * @param {User} user * @param {$mdDialog} $mdDialog * @param {BalanceWatcher} balanceWatcher + * @param {IOuterBlockchains} outerBlockchains + * @param {GatewayService} gatewayService * @return {AssetSendCtrl} */ - const controller = function ($scope, waves, Base, utils, user, $mdDialog, balanceWatcher) { + const controller = function ($scope, waves, Base, utils, user, $mdDialog, balanceWatcher, outerBlockchains, + gatewayService) { class AssetSendCtrl extends Base { @@ -25,18 +28,16 @@ } /** - * @return {boolean} + * @return {IGatewayDetails} */ - get outerSendMode() { - return this.state.outerSendMode; + get gatewayDetails() { + return this.state.gatewayData.details; } /** - * @return {IGatewayDetails} + * @type {ISendMode} */ - get gatewayDetails() { - return this.state.gatewayDetails; - } + sendMode; /** * @param {IAssetSendCtrl.IOptions} options @@ -48,10 +49,6 @@ * @type {string} */ this.headerPath = 'modules/utils/modals/sendAsset/send-header.html'; - /** - * @type {typeof WavesApp.defaultAssets} - */ - this.defaultAssets = WavesApp.defaultAssets; /** * @type {Array} */ @@ -74,19 +71,25 @@ this.state = { assetId: options.assetId || WavesApp.defaultAssets.WAVES, mirrorId: user.getSetting('baseAssetId'), - outerSendMode: false, - gatewayDetails: null, + gatewayData: { + details: null, + error: null + }, + paymentId: '', moneyHash: null, singleSend: utils.liteObject({ type: SIGN_TYPE.TRANSFER }), - massSend: utils.liteObject({ type: SIGN_TYPE.MASS_TRANSFER }), - toBankMode: false + massSend: utils.liteObject({ type: SIGN_TYPE.MASS_TRANSFER }) }; + this.receive(balanceWatcher.change, this._updateBalanceList, this); this._updateBalanceList(); - /** - * @type {*} - */ - this.txInfo = Object.create(null); + + this.observe('sendMode', this._onChangeSendMode); + + this.receive(utils.observe(this.state, 'assetId'), this.checkSendMode, this); + this.receive(utils.observe(this.state.singleSend, 'recipient'), this.checkSendMode, this); + this.checkSendMode(); + /** * @type {string} */ @@ -115,7 +118,7 @@ this.state.singleSend.recipient = options.recipient; this.state.singleSend.attachment = options.attachment; - const toGateway = this.outerSendMode && this.gatewayDetails; + const toGateway = this.sendMode === 'gateway' && this.gatewayDetails; const attachment = toGateway ? this.gatewayDetails.attachment : options.attachment; const attachmentString = attachment ? attachment.toString() : ''; const bytesAttachment = utils.stringToBytes(attachmentString); @@ -166,6 +169,38 @@ } } + setMode(mode) { + this.sendMode = mode; + } + + checkSendMode() { + if (this._isOuterBlockchains()) { + this.setMode('gateway'); + } else { + this.setMode('waves'); + } + } + + updateGatewayData() { + if (gatewayService.hasSupportOf(this.balance.asset, 'deposit')) { + return gatewayService + .getWithdrawDetails(this.balance.asset, this.state.singleSend.recipient, this.state.paymentId) + .then(details => { + this.state.gatewayData = { + error: null, + details + }; + $scope.$apply(); + }, error => { + this.state.gatewayData = { + details: null, + error + }; + $scope.$apply(); + }); + } + } + /** * @private */ @@ -214,6 +249,32 @@ }); } + /** + * @return {boolean} + * @private + */ + _isOuterBlockchains() { + const outerChain = outerBlockchains[this.state.assetId]; + const isValidWavesAddress = user.isValidAddress(this.state.singleSend.recipient); + const isGatewayAddress = !isValidWavesAddress && + outerChain && outerChain.isValidAddress(this.state.singleSend.recipient); + return isGatewayAddress; + } + + /** + * @private + */ + _onChangeSendMode() { + if (this.sendMode === 'gateway') { + this.updateGatewayData(); + } else { + this.gatewayData = { + details: null, + error: null + }; + } + } + /** * @return {boolean} * @private @@ -234,7 +295,9 @@ 'utils', 'user', '$mdDialog', - 'balanceWatcher' + 'balanceWatcher', + 'outerBlockchains', + 'gatewayService' ]; angular.module('app.utils') @@ -274,8 +337,6 @@ * @typedef {object} ISendState * @property {string} assetId * @property {string} mirrorId - * @property {boolean} outerSendMode - * @property {boolean} toBankMode * @property {IGatewayDetails} gatewayDetails * @property {Object.} moneyHash * @property {ISingleSendTx} singleSend @@ -297,3 +358,7 @@ * @property {boolean} [strict] * @property {string} [referrer] */ + +/** + * @typedef {'waves' | 'bank' | 'gataway'} ISendMode + */ diff --git a/src/modules/utils/modals/sendAsset/components/singleSend/SingleSend.js b/src/modules/utils/modals/sendAsset/components/singleSend/SingleSend.js index 44a09b69d8..bcc6af1e53 100644 --- a/src/modules/utils/modals/sendAsset/components/singleSend/SingleSend.js +++ b/src/modules/utils/modals/sendAsset/components/singleSend/SingleSend.js @@ -1,66 +1,19 @@ (function () { 'use strict'; - const FIAT_ASSETS = { - [WavesApp.defaultAssets.USD]: true, - [WavesApp.defaultAssets.EUR]: true, - [WavesApp.defaultAssets.TRY]: true - }; - - const { Money } = require('@waves/data-entities'); - const { BigNumber } = require('@waves/bignumber'); - const ds = require('data-service'); const { SIGN_TYPE } = require('@waves/signature-adapter'); const analytics = require('@waves/event-sender'); - const BANK_RECIPIENT = WavesApp.bankRecipient; - const MIN_TOKEN_COUNT = 100; - const MAX_TOKEN_COUNT = 50000; - /** - * @param {Base} Base - * @param {$rootScope.Scope} $scope + * @param Base * @param {app.utils} utils - * @param {IPollCreate} createPoll - * @param {ConfigService} configService - * @param {IOuterBlockchains} outerBlockchains - * @param {User} user - * @param {GatewayService} gatewayService * @param {Waves} waves + * @return {SingleSend} */ - const controller = function (Base, - $scope, - utils, - createPoll, - waves, - configService, - outerBlockchains, - user, - gatewayService) { + const factory = function (Base, utils, waves) { class SingleSend extends Base { - /** - * @return {boolean} - */ - get hasSendToBank() { - return FIAT_ASSETS[this.assetId] || false; - } - - /** - * @return {boolean} - */ - get isLira() { - return this.assetId === WavesApp.defaultAssets.TRY; - } - - /** - * @return {ISingleSendTx} - */ - get tx() { - return this.state.singleSend; - } - /** * @return {string} */ @@ -72,13 +25,6 @@ this.state.assetId = id; } - /** - * @return {string} - */ - get mirrorId() { - return this.state.mirrorId; - } - /** * @return {Object} */ @@ -86,13 +32,6 @@ return this.state.moneyHash; } - /** - * @return {Object.} - */ - get feeHash() { - return utils.groupMoney(this.feeList || []); - } - /** * @return {Money} */ @@ -100,119 +39,24 @@ return this.moneyHash[this.assetId]; } - /** - * @return {boolean} - */ - get outerSendMode() { - return this.state.outerSendMode; - } - - set outerSendMode(value) { - this.state.outerSendMode = value; - } - /** * @return {string} */ - get paymentId() { - return this.state.paymentId; - } - - set paymentId(value) { - this.state.paymentId = value; - } - - /** - * @return {IGatewayDetails} - */ - get gatewayDetails() { - return this.state.gatewayDetails; - } - - set gatewayDetails(value) { - this.state.gatewayDetails = value; - } - - get isBankPending() { - return this.toBankMode && this.termsIsPending; - } - - get isBankError() { - return this.toBankMode && this.termsLoadError; - } - - get isGatewayAccepted() { - return !configService - .get('PERMISSIONS.CANT_TRANSFER_GATEWAY').includes(this.balance.asset.id); - } - - get isBankAccepted() { - return this.toBankMode ? this.isGatewayAccepted : true; - } - - get isBankPendingOrError() { - return this.isBankError || this.isBankPending; - } - - get hasOuterError() { - return this.outerSendMode && this.gatewayError || this.isBankError; - } - - get minimumAmount() { - return this.gatewayDetails && - this.gatewayDetails.minimumAmount || - this.toBankMode && - new BigNumber(MIN_TOKEN_COUNT); - } - - get maximumAmount() { - return this.maxGatewayAmount || this.toBankMode && this.balance.cloneWithTokens(MAX_TOKEN_COUNT); + get mirrorId() { + return this.state.mirrorId; } /** - * @type {string} - */ - txType = WavesApp.TRANSACTION_TYPES.NODE.TRANSFER; - - /** - * @type {boolean} + * @return {ISingleSendTx} */ - get toBankMode() { - return this.state.toBankMode; - } - - set toBankMode(value) { - this.state.toBankMode = value; + get tx() { + return this.state.singleSend; } - /** - * @type {string} - */ - digiLiraUserLink = 'https://www.digilira.com/'; /** * @type {Function} */ - onContinue = null; - /** - * @type {string} - */ - focus = null; - /** - * @type {Money} - */ - mirror = null; - /** - * @type {boolean} - */ - noMirror = false; - /** - * @type {boolean} - */ - hasComission = true; - /** - * @type {Array} - */ - feeList = null; + onSign = null; /** * @type {Money} */ @@ -222,41 +66,25 @@ */ maxAmount = null; /** - * @type {ISendState} - */ - state = Object.create(null); - /** - * @type {Money} - */ - maxGatewayAmount = null; - /** - * @type {boolean} - */ - gatewayDetailsError = false; - /** - * @type {boolean} - */ - gatewayAddressError = false; - /** - * @type {boolean} + * @type {Array} */ - gatewayWrongAddress = false; + feeList = []; /** * @type {boolean} */ - gatewayError = false; + hasFee = true; /** - * @type {boolean} + * @type {Money} */ - termsIsPending = true; + mirror = null; /** * @type {boolean} */ - termsLoadError = false; + noMirror = false; /** - * @type {boolean} + * @type {string} */ - signInProgress = false; + focus = ''; /** * @type {ISingleSendTx} */ @@ -268,126 +96,25 @@ recipient: '', assetId: '' }; + /** + * @type {$rootScope.Scope} + * @private + */ + __$scope = null; /** * @type {boolean} * @private */ _noCurrentRate = false; - constructor() { - super(); - - $scope.WavesApp = WavesApp; - } - - $postLink() { - this.receive(utils.observe(this.tx, 'fee'), this._currentHasCommission, this); - const onHasMoneyHash = () => { - this.receive(utils.observe(this.state, 'toBankMode'), this._onChangeBankMode, this); - this.observe('gatewayDetails', this._currentHasCommission); - - this.minAmount = this.state.moneyHash[this.state.assetId].cloneWithTokens('0'); - this.tx.amount = this.tx.amount || this.moneyHash[this.assetId].cloneWithTokens('0'); - this._fillMirror(); - - this.receive(utils.observe(this.state, 'assetId'), this._onChangeAssetId, this); - this.receive(utils.observe(this.state, 'mirrorId'), this._onChangeMirrorId, this); - - this.receive(utils.observe(this.state, 'paymentId'), this._updateGatewayDetails, this); - this.receive(utils.observe(this.tx, 'recipient'), this._updateGatewayDetails, this); - - this.receive(utils.observe(this.state, 'paymentId'), this._updateGatewayPermisson, this); - this.receive(utils.observe(this.tx, 'recipient'), this._updateGatewayPermisson, this); - - this.receive(utils.observe(this.tx, 'amount'), this._onChangeAmount, this); - - this.observe('gatewayDetails', this._updateWavesTxObject); - this.receive(utils.observe(this.tx, 'amount'), this._updateWavesTxObject, this); - this.receive(utils.observe(this.tx, 'recipient'), this._updateWavesTxObject, this); - this.receive(utils.observe(this.tx, 'attachment'), this._updateWavesTxObject, this); - - this.observe('mirror', this._onChangeAmountMirror); - this.observe(['gatewayAddressError', 'gatewayDetailsError', 'gatewayWrongAddress'], - this._updateGatewayError); - - this._currentHasCommission(); - this._onChangeBaseAssets(); - this._updateGatewayDetails(); - this._updateGatewayPermisson(); - }; - if (!this.state.moneyHash) { - this.receiveOnce(utils.observe(this.state, 'moneyHash'), onHasMoneyHash); - } else { - onHasMoneyHash(); - } - this.receive(utils.observe(this.state, 'moneyHash'), () => { - this._currentHasCommission(); - this._onChangeBaseAssets(); - this._updateGatewayDetails(); - }); - this._onChangeBaseAssets(); - this._updateWavesTxObject(); - } - - onSignCoinomatStart() { - this.signInProgress = true; - } - - onSignCoinomatEnd() { - this.signInProgress = false; - } - - createTx() { - const toGateway = this.outerSendMode && this.gatewayDetails; - const fee = toGateway ? this.tx.amount.cloneWithTokens(toGateway.gatewayFee) : null; - const attachmentString = this.tx.attachment ? this.tx.attachment.toString() : ''; - const tx = waves.node.transactions.createTransaction({ - ...this.tx, - recipient: toGateway ? this.gatewayDetails.address : this.tx.recipient, - attachment: utils.stringToBytes(toGateway ? this.gatewayDetails.attachment : attachmentString), - amount: toGateway ? this.tx.amount.add(fee) : this.tx.amount - }); - - const signable = ds.signature.getSignatureApi().makeSignable({ - type: tx.type, - data: tx - }); - - return signable; + constructor($scope) { + super($scope); + this.__$scope = $scope; } onSignTx(signable) { analytics.send({ name: 'Transfer Continue Click', target: 'ui' }); - this.onContinue({ signable }); - } - - fillMax() { - let amount = null; - const moneyHash = utils.groupMoney(this.feeList); - if (moneyHash[this.assetId]) { - amount = this.balance.sub(moneyHash[this.assetId]); - } else { - amount = this.balance; - } - - if (amount.getTokens().lt(0)) { - amount = this.moneyHash[this.assetId].cloneWithTokens('0'); - } - - waves.utils.getRate(this.assetId, this.mirrorId).then(rate => { - this._noCurrentRate = true; - this.mirror = amount.convertTo(this.moneyHash[this.mirrorId].asset, rate); - this.tx.amount = amount; - this._noCurrentRate = false; - $scope.$apply(); - }); - } - - onBlurMirror() { - if (!this.mirror) { - this._fillMirror(); - } - this.focus = ''; + this.onSign({ signable }); } /** @@ -397,31 +124,28 @@ onReadQrCode(url) { if (!url.includes('https://')) { this.tx.recipient = url; - $scope.$apply(); + this.__$scope.$apply(); return null; } - const routeData = utils.getRouterParams(utils.getUrlForRoute(url)); + const routerData = utils.getRouterParams(utils.getUrlForRoute(url)); - if (!routeData || routeData.name !== 'SEND_ASSET') { + if (!routerData || routerData.name !== 'SEND_ASSET') { return null; } - const result = routeData.data; + const result = routerData.data; this.tx.recipient = result.recipient; - // analytics.push('Send', `Send.QrCodeRead.${WavesApp.type}`, - // `Send.QrCodeRead.${WavesApp.type}.Success`); - if (result) { const applyAmount = () => { if (result.amount) { - this.tx.amount = this.moneyHash[this.assetId].cloneWithTokens(result.amount); + this.tx.amount = this.balance.cloneWithTokens(result.amount); this._fillMirror(); } - $scope.$apply(); + this.__$scope.$apply(); }; result.assetId = result.asset || result.assetId; @@ -446,158 +170,106 @@ } } - /** - * @return {boolean} - */ - isMoneroNotIntegratedAddress() { - const moneroAddressLength = 95; - const assetIsMonero = this.state.assetId === WavesApp.defaultAssets.XMR; - return assetIsMonero && this.tx.recipient.length === moneroAddressLength; - } + fillMax() { + let amount = null; + const moneyHash = utils.groupMoney(this.feeList); + if (moneyHash[this.assetId]) { + amount = this.balance.sub(moneyHash[this.assetId]); + } else { + amount = this.balance; + } - getGatewayDetails() { - this._onChangeAssetId(); - } + if (amount.getTokens().lt(0)) { + amount = this.balance.cloneWithTokens('0'); + } - /** - * @private - */ - _updateWavesTxObject() { - const toGateway = this.outerSendMode && this.gatewayDetails; - const fee = toGateway ? this.tx.amount.cloneWithTokens(toGateway.gatewayFee) : null; - const attachmentString = this.tx.attachment ? this.tx.attachment.toString() : ''; - const isWavesAddress = user.isValidAddress(this.tx.recipient); - this.wavesTx = { - ...this.wavesTx, - recipient: toGateway ? this.gatewayDetails.address : isWavesAddress && this.tx.recipient || '', - attachment: utils.stringToBytes(toGateway ? this.gatewayDetails.attachment : attachmentString), - amount: toGateway ? this.tx.amount.add(fee) : this.tx.amount, - assetId: this.assetId - }; + waves.utils.getRate(this.assetId, this.mirrorId).then(rate => { + this._noCurrentRate = true; + this.mirror = amount.convertTo(this.moneyHash[this.mirrorId].asset, rate); + this.tx.amount = amount; + this._noCurrentRate = false; + this.__$scope.$apply(); + }); } - /** - * @private - */ - _validateForm() { - if (this.tx.amount.getTokens().gt(0) || this.tx.recipient) { - this.send.$setDirty(true); - this.send.$setSubmitted(true); + onBlurMirror() { + if (!this.mirror) { + this._fillMirror(); } + this.focus = ''; } /** * @private */ - _onChangeBankMode() { - if (this.toBankMode && !this.isLira) { - this.tx.recipient = BANK_RECIPIENT; - this.termsIsPending = true; + _onChangeBaseAssets() { + if (this.assetId === this.mirrorId) { + this.noMirror = true; } else { - this.tx.recipient = ''; - this.termsIsPending = false; + waves.utils.getRate(this.assetId, this.mirrorId).then(rate => { + this.noMirror = rate.eq(0); + }); } - - this._setMinAmount(); - this._updateGatewayPermisson(); } /** * @private */ - _setMinAmount() { - if (this.toBankMode && !this.isLira) { - const maxCoinomatAmount = this.balance.cloneWithTokens(50000); - const minCoinomatAmount = this.balance.cloneWithTokens(100); + _onChangeFee() { + this.feeList = [this.tx.fee]; - this.maxAmount = Money.min(maxCoinomatAmount, this.balance); - this.minAmount = minCoinomatAmount; - } else { - this.minAmount = this.state.moneyHash[this.assetId].cloneWithTokens('0'); - this.maxAmount = this.moneyHash[this.assetId]; - } + const feeHash = utils.groupMoney(this.feeList); + const balanceHash = this.moneyHash; + + this.hasFee = Object.keys(feeHash).every((feeAssetId) => { + const fee = feeHash[feeAssetId]; + return balanceHash[fee.asset.id] && balanceHash[fee.asset.id].gte(fee); + }); - this._validateForm(); } /** * @private */ - _onChangeMirrorId() { - if (!this.mirrorId) { + _onChangeAssetId() { + if (!this.assetId) { throw new Error('Has no asset id!'); } this._onChangeBaseAssets(); - if (!this.moneyHash[this.mirrorId]) { + if (!this.balance) { return null; } + this.tx.amount = this.balance.cloneWithTokens('0'); this.mirror = this.moneyHash[this.mirrorId].cloneWithTokens('0'); - this._onChangeAmount(); } /** * @private */ - _onChangeAssetId() { - if (!this.assetId) { + _onChangeMirrorId() { + if (!this.mirrorId) { throw new Error('Has no asset id!'); } this._onChangeBaseAssets(); - if (!this.moneyHash[this.assetId]) { + if (!this.moneyHash[this.mirrorId]) { return null; } - this.tx.amount = this.moneyHash[this.assetId].cloneWithTokens('0'); this.mirror = this.moneyHash[this.mirrorId].cloneWithTokens('0'); - this._updateGatewayDetails(); - this._updateGatewayPermisson(); - } - - /** - * @private - */ - _currentHasCommission() { - if (!this.moneyHash) { - return null; - } - - const details = this.gatewayDetails; - - const check = (feeList) => { - const feeHash = utils.groupMoney(feeList); - const balanceHash = this.moneyHash; - - this.hasComission = Object.keys(feeHash).every((feeAssetId) => { - const fee = feeHash[feeAssetId]; - return balanceHash[fee.asset.id] && balanceHash[fee.asset.id].gte(fee); - }); - }; - - if (details) { - const gatewayFee = this.balance.cloneWithTokens(details.gatewayFee); - this.feeList = [this.tx.fee, gatewayFee]; - check(this.feeList.concat(this.balance.cloneWithTokens(details.minimumAmount))); - } else { - this.feeList = [this.tx.fee]; - check(this.feeList); - } + this._onChangeAmount(); } /** * @private */ - _onChangeBaseAssets() { - if (this.assetId === this.mirrorId) { - this.noMirror = true; - } else { - waves.utils.getRate(this.assetId, this.mirrorId).then((rate) => { - this.noMirror = rate.eq(0); - }); + _onChangeAmountMirror() { + if (!this._noCurrentRate && this.focus === 'mirror') { + this._fillAmount(); } } @@ -608,32 +280,20 @@ if (!this._noCurrentRate && !this.noMirror && this.focus === 'amount') { this._fillMirror(); } - - } - - /** - * @private - */ - _onChangeAmountMirror() { - if (!this._noCurrentRate && this.focus === 'mirror') { - this._fillAmount(); - } } /** * @private */ _fillMirror() { - if (!this.tx.amount) { this.mirror = null; - setTimeout(() => $scope.$digest(), 0); - return null; + return; } - waves.utils.getRate(this.assetId, this.mirrorId).then((rate) => { + waves.utils.getRate(this.assetId, this.mirrorId).then(rate => { this.mirror = this.tx.amount.convertTo(this.moneyHash[this.mirrorId].asset, rate); - $scope.$digest(); + this.__$scope.$digest(); }); } @@ -641,128 +301,23 @@ * @private */ _fillAmount() { - if (!this.mirror) { this.tx.amount = null; - setTimeout(() => $scope.$digest(), 0); return null; } - waves.utils.getRate(this.mirrorId, this.assetId).then((rate) => { - this.tx.amount = this.mirror.convertTo(this.moneyHash[this.assetId].asset, rate); - $scope.$digest(); + waves.utils.getRate(this.mirrorId, this.assetId).then(rate => { + this.tx.amount = this.mirror.convertTo(this.balance.asset, rate); + this.__$scope.$digest(); }); } - /** - * @private - */ - _updateGatewayDetails() { - if (this.toBankMode && !this.isLira) { - this._setMinAmount(); - return; - } - - const outerChain = outerBlockchains[this.assetId]; - const isValidWavesAddress = user.isValidAddress(this.tx.recipient); - - if (this.gatewayDetailsError) { - this.outerSendMode = false; - this.gatewayDetailsError = false; - } - - if (this.gatewayAddressError) { - this.gatewayAddressError = false; - } - - if (this.gatewayWrongAddress) { - this.gatewayWrongAddress = false; - } - - this.outerSendMode = !isValidWavesAddress && outerChain && outerChain.isValidAddress(this.tx.recipient); - - if (this.outerSendMode) { - return gatewayService.getWithdrawDetails(this.balance.asset, this.tx.recipient, this.paymentId) - .then((details) => { - const max = BigNumber.min( - details.maximumAmount.add(details.gatewayFee), - this.moneyHash[this.assetId].getTokens() - ); - - this.gatewayDetails = details; - this.minAmount = this.moneyHash[this.assetId] - .cloneWithTokens(details.minimumAmount.sub('0.00000001')); - this.maxAmount = this.moneyHash[this.assetId].cloneWithTokens(max); - this.maxGatewayAmount = Money.fromTokens(details.maximumAmount, this.balance.asset); - $scope.$apply(); - }, (e) => { - this.gatewayDetails = null; - if (e.message === gatewayService.getAddressErrorMessage(this.balance.asset, - this.tx.recipient, 'errorAddressMessage')) { - this.gatewayAddressError = true; - } else if (e.message === gatewayService.getWrongAddressMessage(this.balance.asset, - this.tx.recipient, 'wrongAddressMessage')) { - this.gatewayWrongAddress = true; - } else { - this.gatewayDetailsError = true; - } - $scope.$apply(); - }); - } else { - this.minAmount = this.state.moneyHash[this.assetId].cloneWithTokens('0'); - this.maxAmount = this.moneyHash[this.assetId]; - this.gatewayDetails = null; - this._validateForm(); - } - return Promise.resolve(); - } - - /** - * @private - */ - _updateGatewayPermisson() { - this.gatewayDetailsError = this.outerSendMode ? !this.isGatewayAccepted : this.gatewayDetailsError; - } - - /** - * @private - */ - _updateGatewayError() { - this.gatewayError = this.gatewayAddressError || this.gatewayDetailsError || this.gatewayWrongAddress; - } - } - return new SingleSend(); + return SingleSend; }; - controller.$inject = [ - 'Base', - '$scope', - 'utils', - 'createPoll', - 'waves', - 'configService', - 'outerBlockchains', - 'user', - 'gatewayService' - ]; - - angular.module('app.ui').component('wSingleSend', { - bindings: { - state: '<', - onContinue: '&' - }, - templateUrl: 'modules/utils/modals/sendAsset/components/singleSend/single-send.html', - transclude: true, - controller - }); -})(); + factory.$inject = ['Base', 'utils', 'waves']; -/** - * @typedef {object} ISendTx - * @property {Money} fee - * @property {Money} amount - * @property {string} recipient - * @property {string} attachment - */ + angular.module('app.utils').factory('SingleSend', factory); +})(); diff --git a/src/modules/utils/modals/sendAsset/components/singleSend/bankSend/BankSend.js b/src/modules/utils/modals/sendAsset/components/singleSend/bankSend/BankSend.js new file mode 100644 index 0000000000..9d305aed8a --- /dev/null +++ b/src/modules/utils/modals/sendAsset/components/singleSend/bankSend/BankSend.js @@ -0,0 +1,184 @@ +(function () { + 'use strict'; + + const { Money } = require('@waves/data-entities'); + + const MIN_TOKEN_COUNT = 100; + const MAX_TOKEN_COUNT = 50000; + + /** + * @param {SingleSend} SingleSend + * @param {$rootScope.Scope} $scope + * @param {ConfigService} configService + * @param {Waves} waves + * @param {app.utils} utils + */ + const controller = function (SingleSend, $scope, configService, waves, utils) { + + class BankSend extends SingleSend { + + /** + * @return {boolean} + */ + get isBankAccepted() { + return !configService + .get('PERMISSIONS.CANT_TRANSFER_GATEWAY').includes(this.balance.asset.id); + } + + /** + * @return {boolean} + */ + get isBankPendingOrError() { + return this.termsLoadError || this.termsIsPending; + } + + /** + * @type {boolean} + */ + termsIsPending = true; + /** + * @type {boolean} + */ + termsLoadError = false; + /** + * @type {boolean} + */ + termsAccepted = false; + /** + * @type {Money | null} + */ + maxCoinomatAmount = null; + /** + * @type {Function} + */ + onChangeMode = null; + + constructor(props) { + super(props); + this.wavesTx.recipient = WavesApp.bankRecipient; + } + + $postLink() { + const onHasMoneyHash = () => { + this.receive(utils.observe(this.tx, 'fee'), this._onChangeFee, this); + this.receive(utils.observe(this.tx, 'amount'), this._onChangeAmount, this); + + this.receive(utils.observe(this.state, 'assetId'), this._onChangeAssetId, this); + this.receive(utils.observe(this.state, 'mirrorId'), this._onChangeMirrorId, this); + + this.observe('mirror', this._onChangeAmountMirror); + + this.receive(utils.observe(this.tx, 'amount'), this._updateWavesTxObject, this); + this.receive(utils.observe(this.tx, 'attachment'), this._updateWavesTxObject, this); + + this._onChangeBaseAssets(); + this._onChangeFee(); + this._updateWavesTxObject(); + this._setMinAndMaxAmount(); + this._validateForm(); + }; + + if (!this.state.moneyHash) { + this.receiveOnce(utils.observe(this.state, 'moneyHash'), onHasMoneyHash); + } else { + onHasMoneyHash(); + } + + this.receive(utils.observe(this.state, 'moneyHash'), () => { + this._onChangeBaseAssets(); + this._onChangeFee(); + this._setMinAndMaxAmount(); + this._validateForm(); + }); + + $scope.$watch('$ctrl.bankSend.amount', () => this._validateForm()); + } + + setSendMode(mode) { + this.onChangeMode({ mode }); + } + + createTx() { + const attachmentString = this.tx.attachment ? this.tx.attachment.toString() : ''; + const tx = waves.node.transactions.createTransaction({ + ...this.tx, + recipient: WavesApp.bankRecipient, + attachment: utils.stringToBytes(attachmentString), + amount: this.tx.amount + }); + + const signable = ds.signature.getSignatureApi().makeSignable({ + type: tx.type, + data: tx + }); + + return signable; + } + + /** + * @private + */ + _setMinAndMaxAmount() { + const maxCoinomatAmount = this.balance.cloneWithTokens(MAX_TOKEN_COUNT); + const minCoinomatAmount = this.balance.cloneWithTokens(MIN_TOKEN_COUNT); + + this.maxAmount = Money.min(maxCoinomatAmount, this.balance); + this.minAmount = minCoinomatAmount; + this.maxCoinomatAmount = maxCoinomatAmount; + } + + /** + * @private + */ + _validateForm() { + if (this.bankSend.amount && this.tx.amount.getTokens().gt(0)) { + this.bankSend.amount.$setTouched(true); + } + } + + /** + * @private + */ + _updateWavesTxObject() { + const attachmentString = this.tx.attachment ? this.tx.attachment.toString() : ''; + this.wavesTx = { + ...this.wavesTx, + attachment: utils.stringToBytes(attachmentString), + amount: this.tx.amount, + assetId: this.assetId + }; + } + + /** + * @private + */ + _onChangeAssetId() { + super._onChangeAssetId(); + this._setMinAndMaxAmount(); + this._validateForm(); + } + + } + + return new BankSend($scope); + }; + + controller.$inject = [ + 'SingleSend', + '$scope', + 'configService', + 'waves', + 'utils' + ]; + + angular.module('app.ui').component('wBankSend', { + bindings: { + state: '<', + onSign: '&', + onChangeMode: '&' + }, + templateUrl: 'modules/utils/modals/sendAsset/components/singleSend/bankSend/bank-send.html', + transclude: true, + controller + }); +})(); diff --git a/src/modules/utils/modals/sendAsset/components/singleSend/bankSend/bank-send.html b/src/modules/utils/modals/sendAsset/components/singleSend/bankSend/bank-send.html new file mode 100644 index 0000000000..c12e91c1fc --- /dev/null +++ b/src/modules/utils/modals/sendAsset/components/singleSend/bankSend/bank-send.html @@ -0,0 +1,166 @@ +
+ + + +
+
+
+
+
+
+ +
+
+
+
+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ + +
+
+ + + +
+ + + +
+ + +
+ + +
+ + +
+
+
+
+ + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ + + +
+ +
diff --git a/src/modules/utils/modals/sendAsset/components/singleSend/gatewaySend/GatewaySend.js b/src/modules/utils/modals/sendAsset/components/singleSend/gatewaySend/GatewaySend.js new file mode 100644 index 0000000000..96661ca800 --- /dev/null +++ b/src/modules/utils/modals/sendAsset/components/singleSend/gatewaySend/GatewaySend.js @@ -0,0 +1,269 @@ +(function () { + 'use strict'; + + const { Money } = require('@waves/data-entities'); + const { BigNumber } = require('@waves/bignumber'); + + /** + * @param {SingleSend} SingleSend + * @param {$rootScope.Scope} $scope + * @param {app.utils} utils + * @param {User} user + * @param {Waves} waves + * @param {GatewayService} gatewayService + * @param {ConfigService} configService + */ + const controller = function (SingleSend, $scope, utils, user, waves, gatewayService, configService) { + + class GatewaySend extends SingleSend { + + /** + * @return {IGatewayDetails|null} + */ + get gatewayDetails() { + return this.state.gatewayData.details; + } + + /** + * @return {string} + */ + get paymentId() { + return this.state.paymentId; + } + + /** + * @return {boolean} + */ + get isGatewayAccepted() { + return !configService + .get('PERMISSIONS.CANT_TRANSFER_GATEWAY').includes(this.balance.asset.id); + } + + /** + * @type {boolean} + */ + gatewayError = false; + /** + * @type {boolean} + */ + gatewayDetailsError = false; + /** + * @type {boolean} + */ + gatewayAddressError = false; + /** + * @type {boolean} + */ + gatewayWrongAddress = false; + /** + * @type {Money} + */ + maxGatewayAmount = null; + + $postLink() { + this.receive(utils.observe(this.tx, 'recipient'), this._onUpdateRecipient, this); + this.observe(['gatewayAddressError', 'gatewayDetailsError', 'gatewayWrongAddress'], + this._updateGatewayError); + + const onHasMoneyHash = () => { + + this.receive(utils.observe(this.tx, 'fee'), this._onChangeFee, this); + this.receive(utils.observe(this.tx, 'amount'), this._onChangeAmount, this); + + this.receive(utils.observe(this.state, 'assetId'), this._onChangeAssetId, this); + this.receive(utils.observe(this.state, 'mirrorId'), this._onChangeMirrorId, this); + this.receive(utils.observe(this.state, 'paymentId'), this.updateGatewayData, this); + this.receive(utils.observe(this.state, 'gatewayData'), this._onUpdateGatewayData, this); + + this.observe('mirror', this._onChangeAmountMirror); + + this.receive(utils.observe(this.tx, 'amount'), this._updateWavesTxObject, this); + this.receive(utils.observe(this.tx, 'recipient'), this._updateWavesTxObject, this); + + this._onChangeFee(); + this._fillMirror(); + }; + + if (!this.state.moneyHash) { + this.receiveOnce(utils.observe(this.state, 'moneyHash'), onHasMoneyHash); + } else { + onHasMoneyHash(); + } + + this.receive(utils.observe(this.state, 'moneyHash'), () => { + this._onChangeFee(); + }); + + $scope.$watch('$ctrl.gatewaySend.recipient', () => { + if (this.tx.recipient) { + this.gatewaySend.recipient.$$element.focus(); + } + }); + } + + /** + * @return {boolean} + */ + isMoneroNotIntegratedAddress() { + const moneroAddressLength = 95; + const assetIsMonero = this.assetId === WavesApp.defaultAssets.XMR; + return assetIsMonero && this.tx.recipient.length === moneroAddressLength; + } + + createTx() { + const fee = this.tx.amount.cloneWithTokens(this.gatewayDetails.gatewayFee); + const tx = waves.node.transactions.createTransaction({ + ...this.tx, + recipient: this.gatewayDetails.address, + attachment: utils.stringToBytes(this.gatewayDetails.attachment), + amount: this.tx.amount.add(fee) + }); + + const signable = ds.signature.getSignatureApi().makeSignable({ + type: tx.type, + data: tx + }); + + return signable; + } + + /** + * @private + */ + _onUpdateGatewayData() { + const { details, error } = this.state.gatewayData; + + if (details) { + this.gatewayDetailsError = false; + this.gatewayAddressError = false; + this.gatewayWrongAddress = false; + + const max = BigNumber.min( + details.maximumAmount.add(details.gatewayFee), + this.balance.getTokens() + ); + + this.minAmount = this.balance.cloneWithTokens(details.minimumAmount); + this.maxAmount = this.balance.cloneWithTokens(max); + this.maxGatewayAmount = Money.fromTokens(details.maximumAmount, this.balance.asset); + + this._onChangeGatewayDetails(); + } else if (error) { + if (error.message === gatewayService.getAddressErrorMessage(this.balance.asset, + this.tx.recipient, 'errorAddressMessage')) { + this.gatewayAddressError = true; + } else if (error.message === gatewayService.getWrongAddressMessage(this.balance.asset, + this.tx.recipient, 'wrongAddressMessage')) { + this.gatewayWrongAddress = true; + } else { + this.gatewayDetailsError = true; + } + } else { + this.gatewayDetailsError = true; + } + + $scope.$apply(); + } + + /** + * @private + */ + _onUpdateRecipient() { + this.onChangeRecipient(); + this.updateGatewayData(); + } + + /** + * @private + */ + _updateGatewayError() { + this.gatewayError = this.gatewayAddressError || this.gatewayDetailsError || this.gatewayWrongAddress; + } + + /** + * @private + */ + _onChangeFee() { + const details = this.gatewayDetails; + + const check = (feeList) => { + const feeHash = utils.groupMoney(feeList); + const balanceHash = this.moneyHash; + + this.hasFee = Object.keys(feeHash).every((feeAssetId) => { + const fee = feeHash[feeAssetId]; + return balanceHash[fee.asset.id] && balanceHash[fee.asset.id].gte(fee); + }); + }; + + if (details) { + const gatewayFee = this.balance.cloneWithTokens(details.gatewayFee); + this.feeList = [this.tx.fee, gatewayFee]; + check(this.feeList.concat(this.balance.cloneWithTokens(details.minimumAmount))); + } + } + + /** + * @private + */ + _updateWavesTxObject() { + const fee = this.tx.amount ? this.tx.amount.cloneWithTokens(this.gatewayDetails.gatewayFee) : null; + this.wavesTx = { + ...this.wavesTx, + recipient: this.gatewayDetails.address, + attachment: utils.stringToBytes(this.gatewayDetails.attachment), + amount: this.tx.amount.add(fee), + assetId: this.assetId + }; + } + + /** + * @private + */ + _onChangeGatewayDetails() { + this._updateWavesTxObject(); + this._onChangeFee(); + this._validateForm(); + } + + /** + * @private + */ + _validateForm() { + if (this.tx.amount && this.tx.amount.getTokens().gt(0)) { + this.gatewaySend.amount.$setTouched(true); + } + } + + _onChangeAssetId() { + super._onChangeAssetId(); + this.updateGatewayData(); + } + + } + + return new GatewaySend($scope); + }; + + controller.$inject = [ + 'SingleSend', + '$scope', + 'utils', + 'user', + 'waves', + 'gatewayService', + 'configService' + ]; + + angular.module('app.ui').component('wGatewaySend', { + bindings: { + state: '<', + onSign: '&', + onChangeRecipient: '&', + updateGatewayData: '&' + }, + templateUrl: 'modules/utils/modals/sendAsset/components/singleSend/gatewaySend/gateway-send.html', + transclude: true, + controller + }); +})(); diff --git a/src/modules/utils/modals/sendAsset/components/singleSend/gatewaySend/gateway-send.html b/src/modules/utils/modals/sendAsset/components/singleSend/gatewaySend/gateway-send.html new file mode 100644 index 0000000000..5e0e18f535 --- /dev/null +++ b/src/modules/utils/modals/sendAsset/components/singleSend/gatewaySend/gateway-send.html @@ -0,0 +1,186 @@ +
+ + + +
+ +
+
+
+ + +
+ + + +
+
+
+ +
+
+
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + +
+
+ + + +
+ + + +
+ + +
+ +
+ +
+ +
+ + +
+ +
+ + + +
+ +
diff --git a/src/modules/utils/modals/sendAsset/components/singleSend/single-send.html b/src/modules/utils/modals/sendAsset/components/singleSend/single-send.html deleted file mode 100644 index 4711a9d1ba..0000000000 --- a/src/modules/utils/modals/sendAsset/components/singleSend/single-send.html +++ /dev/null @@ -1,318 +0,0 @@ -
- - - -
-
-
-
-
-
- -
-
-
-
- - -
-
- -
- - -
- - -
- - - -
- - - - - - - -
-
- -
- -
- -
-
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
- -
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
- -
- -
- - - - - - - - - - - - - -
-
- - -
-
- - - -
- - - -
- - -
- - -
- - -
-
-
-
- - - - -
- -
-
- - - - - - - -
- -
- -
- -
- - -
- -
- -
- -
-
-
-
-
- -
- -
-
-
- -
- - - - - - - -
-
diff --git a/src/modules/utils/modals/sendAsset/components/singleSend/wavesSend/WavesSend.js b/src/modules/utils/modals/sendAsset/components/singleSend/wavesSend/WavesSend.js new file mode 100644 index 0000000000..779e9b8d2c --- /dev/null +++ b/src/modules/utils/modals/sendAsset/components/singleSend/wavesSend/WavesSend.js @@ -0,0 +1,166 @@ +(function () { + 'use strict'; + + const FIAT_ASSETS = { + [WavesApp.defaultAssets.USD]: true, + [WavesApp.defaultAssets.EUR]: true + }; + + const ds = require('data-service'); + + /** + * @param {SingleSend} SingleSend + * @param {$rootScope.Scope} $scope + * @param {app.utils} utils + * @param {User} user + * @param {Waves} waves + */ + const controller = function (SingleSend, $scope, utils, waves, user) { + + class WavesSend extends SingleSend { + + /** + * @return {boolean} + */ + get canSendToBank() { + return FIAT_ASSETS[this.assetId] || false; + } + + /** + * @type {Function} + */ + onChangeMode = null; + + $postLink() { + this.receive(utils.observe(this.tx, 'recipient'), this.onChangeRecipient, this); + + const onHasMoneyHash = () => { + this.tx.amount = this.tx.amount || this.balance.cloneWithTokens('0'); + this.receive(utils.observe(this.tx, 'fee'), this._onChangeFee, this); + this.receive(utils.observe(this.tx, 'amount'), this._onChangeAmount, this); + + this.receive(utils.observe(this.state, 'assetId'), this._onChangeAssetId, this); + this.receive(utils.observe(this.state, 'mirrorId'), this._onChangeMirrorId, this); + + this.observe('mirror', this._onChangeAmountMirror); + + this.receive(utils.observe(this.tx, 'amount'), this._updateWavesTxObject, this); + this.receive(utils.observe(this.tx, 'recipient'), this._updateWavesTxObject, this); + this.receive(utils.observe(this.tx, 'attachment'), this._updateWavesTxObject, this); + + this.onChangeRecipient(); + this._onChangeFee(); + this._setMinAndMaxAmount(); + this._onChangeBaseAssets(); + this._updateWavesTxObject(); + this._fillMirror(); + }; + + if (!this.state.moneyHash) { + this.receiveOnce(utils.observe(this.state, 'moneyHash'), onHasMoneyHash); + } else { + onHasMoneyHash(); + } + + this.receive(utils.observe(this.state, 'moneyHash'), () => { + this._onChangeFee(); + this._setMinAndMaxAmount(); + this._onChangeBaseAssets(); + }); + + $scope.$watch('$ctrl.wavesSend.amount', () => this._validateForm()); + $scope.$watch('$ctrl.wavesSend.recipient', () => { + if (this.tx.recipient) { + this.wavesSend.recipient.$$element.focus(); + } + }); + } + + setSendMode(mode) { + this.onChangeMode({ mode }); + } + + /** + * @return {Signable} + */ + createTx() { + const attachmentString = this.tx.attachment ? this.tx.attachment.toString() : ''; + const tx = waves.node.transactions.createTransaction({ + ...this.tx, + attachment: utils.stringToBytes(attachmentString) + }); + + const signable = ds.signature.getSignatureApi().makeSignable({ + type: tx.type, + data: tx + }); + + return signable; + } + + /** + * @private + */ + _updateWavesTxObject() { + const attachmentString = this.tx.attachment ? this.tx.attachment.toString() : ''; + const isWavesAddress = user.isValidAddress(this.tx.recipient); + this.wavesTx = { + ...this.wavesTx, + recipient: isWavesAddress && this.tx.recipient || '', + attachment: utils.stringToBytes(attachmentString), + amount: this.tx.amount, + assetId: this.assetId + }; + } + + /** + * @private + */ + _validateForm() { + const amountGtZero = this.tx.amount && this.tx.amount.getTokens().gt(0); + if (this.wavesSend.amount && (amountGtZero || this.tx.recipient)) { + this.wavesSend.amount.$setTouched(true); + } + } + + /** + * @private + */ + _setMinAndMaxAmount() { + this.minAmount = this.balance.cloneWithTokens('0'); + this.maxAmount = this.balance; + } + + /** + * @private + */ + _onChangeAssetId() { + super._onChangeAssetId(); + this._setMinAndMaxAmount(); + } + + } + + return new WavesSend($scope); + }; + + controller.$inject = [ + 'SingleSend', + '$scope', + 'utils', + 'waves', + 'user' + ]; + + angular.module('app.ui').component('wWavesSend', { + bindings: { + state: '<', + onSign: '&', + onChangeMode: '&', + onChangeRecipient: '&' + }, + templateUrl: 'modules/utils/modals/sendAsset/components/singleSend/wavesSend/waves-send.html', + transclude: true, + controller + }); +})(); diff --git a/src/modules/utils/modals/sendAsset/components/singleSend/wavesSend/waves-send.html b/src/modules/utils/modals/sendAsset/components/singleSend/wavesSend/waves-send.html new file mode 100644 index 0000000000..dcd9248899 --- /dev/null +++ b/src/modules/utils/modals/sendAsset/components/singleSend/wavesSend/waves-send.html @@ -0,0 +1,125 @@ +
+ + + +
+
+
+
+ +
+
+ + +
+ + + +
+ + + + + + + +
+
+ + +
+
+ + + +
+ + + +
+ + +
+ +
+
+ + + + + + + + +
+ +
+ + +
+ +
+ +
+ +
+ + + +
+
diff --git a/src/modules/utils/modals/sendAsset/send.modal.html b/src/modules/utils/modals/sendAsset/send.modal.html index 7e3965db00..48c7da5640 100644 --- a/src/modules/utils/modals/sendAsset/send.modal.html +++ b/src/modules/utils/modals/sendAsset/send.modal.html @@ -3,15 +3,36 @@