diff --git a/src/app/wallet/view.nim b/src/app/wallet/view.nim index dba1336c95..98bfc99a68 100644 --- a/src/app/wallet/view.nim +++ b/src/app/wallet/view.nim @@ -518,6 +518,9 @@ QtObject: if self.fetchingHistoryState.hasKey(address): return self.fetchingHistoryState[address] return true + + proc isKnownTokenContract*(self: WalletView, address: string): bool {.slot.} = + return self.status.wallet.getKnownTokenContract(parseAddress(address)) != nil proc isHistoryFetched*(self: WalletView, address: string): bool {.slot.} = return self.currentTransactions.rowCount() > 0 diff --git a/src/status/wallet.nim b/src/status/wallet.nim index 8a41a9131d..876b39667d 100644 --- a/src/status/wallet.nim +++ b/src/status/wallet.nim @@ -67,6 +67,9 @@ proc buildTokenTransaction(self: WalletModel, source, to, assetAddress: Address, transfer = Transfer(to: to, value: eth2Wei(value, contract.decimals)) transactions.buildTokenTransaction(source, assetAddress, gas, gasPrice) +proc getKnownTokenContract*(self: WalletModel, address: Address): Erc20Contract = + getErc20Contracts().concat(getCustomTokens()).getErc20ContractByAddress(address) + proc estimateGas*(self: WalletModel, source, to, value: string, success: var bool): int = var tx = transactions.buildTransaction( parseAddress(source), diff --git a/ui/app/AppLayouts/Wallet/SendModal.qml b/ui/app/AppLayouts/Wallet/SendModal.qml index d8738a86dc..e02c780ffc 100644 --- a/ui/app/AppLayouts/Wallet/SendModal.qml +++ b/ui/app/AppLayouts/Wallet/SendModal.qml @@ -193,6 +193,14 @@ ModalPopup { }) } } + SendToContractWarning { + id: sendToContractWarning + anchors.top: pvwTransaction.bottom + selectedRecipient: selectRecipient.selectedRecipient + reset: function() { + selectedRecipient = Qt.binding(function() { return selectRecipient.selectedRecipient }) + } + } } TransactionFormGroup { id: group4 diff --git a/ui/nim-status-client.pro b/ui/nim-status-client.pro index 0361ac7a16..661e7b6dbe 100644 --- a/ui/nim-status-client.pro +++ b/ui/nim-status-client.pro @@ -359,6 +359,7 @@ DISTFILES += \ shared/RoundedImage.qml \ shared/SearchBox.qml \ shared/Select.qml \ + shared/SendToContractWarning.qml \ shared/Separator.qml \ shared/SeparatorWithIcon.qml \ shared/SplitViewHandle.qml \ diff --git a/ui/shared/SendToContractWarning.qml b/ui/shared/SendToContractWarning.qml new file mode 100644 index 0000000000..e59bd78b9a --- /dev/null +++ b/ui/shared/SendToContractWarning.qml @@ -0,0 +1,67 @@ +import QtQuick 2.13 +import QtQuick.Controls 2.13 +import QtQuick.Layouts 1.13 +import "../imports" +import "./" + +Item { + id: root + anchors.left: parent.left + anchors.right: parent.right + property string sendToContractWarningMessage: qsTr("Tokens will be sent directly to a contract address, which may result in a loss of funds. To transfer ERC-20 tokens, ensure the recipient address is the address of the destination wallet.") + property var selectedRecipient + property bool isValid: true + property var reset: function() {} + + onSelectedRecipientChanged: validate() + + function resetInternal() { + selectedRecipient = undefined + isValid = true + } + + function validate() { + let isValid = true + if (!(selectedRecipient && selectedRecipient.address)) { + return root.isValid + } + txtValidationError.text = "" + if (walletModel.isKnownTokenContract(selectedRecipient.address)) { + // do not set isValid = false here because it would make the + // TransactionStackGroup invalid and therefore not let the user + // continue in the modal + txtValidationError.text = sendToContractWarningMessage + } + return isValid + } + + Column { + id: colValidation + anchors.horizontalCenter: parent.horizontalCenter + visible: txtValidationError.text !== "" + spacing: 5 + + SVGImage { + id: imgExclamation + width: 13.33 + height: 13.33 + sourceSize.height: height * 2 + sourceSize.width: width * 2 + anchors.horizontalCenter: parent.horizontalCenter + fillMode: Image.PreserveAspectFit + source: "../app/img/exclamation_outline.svg" + } + StyledText { + id: txtValidationError + text: "" + width: root.width + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + font.pixelSize: 13 + color: Style.current.danger + lineHeight: 18 + lineHeightMode: Text.FixedHeight + wrapMode: Text.WordWrap + } + } +}