Skip to content

Commit

Permalink
Merge pull request #614 from Syn-McJ/feature/crowdnode-withdrawal-pre…
Browse files Browse the repository at this point in the history
…cision

feat(crowdnode): improve withdrawal precision
  • Loading branch information
Syn-McJ authored Oct 31, 2023
2 parents e400af2 + b770fe4 commit 90c15ae
Show file tree
Hide file tree
Showing 50 changed files with 52 additions and 1,048 deletions.
5 changes: 1 addition & 4 deletions DashWallet.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
110C679A29227948006B580C /* UINavigationController+CrowdNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110C679929227948006B580C /* UINavigationController+CrowdNode.swift */; };
110D1781298BA9AF005BEB30 /* WKWebView+CrowdNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110D1780298BA9AF005BEB30 /* WKWebView+CrowdNode.swift */; };
110D1784298E68A8005BEB30 /* OnlineAccountInfoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110D1783298E68A8005BEB30 /* OnlineAccountInfoController.swift */; };
111B8C00299BD973004A4129 /* WithdrawalConfirmationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 111B8BFF299BD973004A4129 /* WithdrawalConfirmationController.swift */; };
111C3C4C296C51B500788E18 /* WithdrawalLimitsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 111C3C4B296C51B500788E18 /* WithdrawalLimitsController.swift */; };
111C3C4E296C52F800788E18 /* WithdrawalLimit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 111C3C4D296C52F800788E18 /* WithdrawalLimit.swift */; };
111C3C50296D5A4700788E18 /* TxWithinTimePeriod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 111C3C4F296D5A4700788E18 /* TxWithinTimePeriod.swift */; };
Expand Down Expand Up @@ -1586,7 +1585,6 @@
110C679929227948006B580C /* UINavigationController+CrowdNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationController+CrowdNode.swift"; sourceTree = "<group>"; };
110D1780298BA9AF005BEB30 /* WKWebView+CrowdNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WKWebView+CrowdNode.swift"; sourceTree = "<group>"; };
110D1783298E68A8005BEB30 /* OnlineAccountInfoController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnlineAccountInfoController.swift; sourceTree = "<group>"; };
111B8BFF299BD973004A4129 /* WithdrawalConfirmationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawalConfirmationController.swift; sourceTree = "<group>"; };
111C3C4B296C51B500788E18 /* WithdrawalLimitsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawalLimitsController.swift; sourceTree = "<group>"; };
111C3C4D296C52F800788E18 /* WithdrawalLimit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawalLimit.swift; sourceTree = "<group>"; };
111C3C4F296D5A4700788E18 /* TxWithinTimePeriod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TxWithinTimePeriod.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3208,7 +3206,6 @@
1147687D294B789800FB1EEE /* CrowdNodePortalViewController.swift */,
111C3C4B296C51B500788E18 /* WithdrawalLimitsController.swift */,
11ED906A29681773003784F9 /* StakingInfoDialogController.swift */,
111B8BFF299BD973004A4129 /* WithdrawalConfirmationController.swift */,
);
path = Portal;
sourceTree = "<group>";
Expand Down Expand Up @@ -8447,7 +8444,7 @@
47AE8C1128C5430300490F5E /* PointOfUseInfoViewController.swift in Sources */,
47C661B428FDCF7800028A8D /* ActionButtonViewController.swift in Sources */,
4774DCE528F4668B008CF87D /* PortalServiceItemCell.swift in Sources */,
111B8C00299BD973004A4129 /* WithdrawalConfirmationController.swift in Sources */,
2A7AF35624824F97001D74F9 /* DWDPImageStatusCell.m in Sources */,
2A7A7BC92347E0D700451078 /* DWBaseFormTableViewCell.m in Sources */,
4751CAC7296FAEBB00F63AC4 /* AccountCell.swift in Sources */,
2A913EA823A79AD2006A2A59 /* DWTransactionListDataProviderStub.m in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@

import Moya

public enum MessageType: Int {
case registerEmail = 1
case withdrawal = 4
}

// MARK: - CrowdNodeEndpoint

Expand All @@ -27,7 +31,7 @@ public enum CrowdNodeEndpoint {
case isAddressInUse(String)
case addressStatus(String)
case hasDefaultEmail(String)
case sendSignedMessage(address: String, message: String, signature: String)
case sendSignedMessage(address: String, message: String, signature: String, messagetype: MessageType)
case getMessages(String)
}

Expand All @@ -47,8 +51,7 @@ extension CrowdNodeEndpoint: TargetType {
case .isAddressInUse(let address): return "odata/apiaddresses/IsApiAddressInUse(address='\(address)')"
case .addressStatus(let address): return "odata/apiaddresses/AddressStatus(address='\(address)')"
case .hasDefaultEmail(let address): return "odata/apiaddresses/UsingDefaultApiEmail(address='\(address)')"
case .sendSignedMessage(let address, let message,
let signature): return "odata/apimessages/SendMessage(address='\(address)',message='\(message)',signature='\(signature)',messagetype=1)"
case .sendSignedMessage(let address, let message, let signature, let messagetype): return "odata/apimessages/SendMessage(address='\(address)',message='\(message)',signature='\(signature)',messagetype=\(messagetype.rawValue))"
case .getMessages(let address): return "odata/apimessages/GetMessages(address='\(address)')"
}
}
Expand Down
42 changes: 13 additions & 29 deletions DashWallet/Sources/Models/CrowdNode/CrowdNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -418,41 +418,25 @@ extension CrowdNode {
}
}

func withdraw(amount: UInt64) async throws {
func withdraw(amount: UInt64, signature: String) async throws {
guard !accountAddress.isEmpty else { return }
guard amount <= balance else { return }

try checkWithdrawalLimits(amount)

let requestPermil = calculateWithdrawalPermil(forAmount: amount)
let requestValue = CrowdNode.apiOffset + UInt64(requestPermil)
let requiredTopUp = requestValue + TX_FEE_PER_INPUT
let account = DWEnvironment.sharedInstance().currentAccount
let finalTopUp = min(account.maxOutputAmount, requiredTopUp)

let topUpTx = try await topUpAccount(accountAddress, finalTopUp)
DSLogger.log("CrowdNode withdraw topup tx hash: \(topUpTx.txHashHexString)")

let withdrawTx = try await sendCoinsService.sendCoins(address: CrowdNode.crowdNodeAddress,
amount: requestValue,
inputSelector: SingleInputAddressSelector(candidates: [topUpTx],
address: accountAddress))
DSLogger.log("CrowdNode withdraw tx hash: \(withdrawTx.txHashHexString)")

Task {
let receivedWithdrawalTx = CrowdNodeWithdrawalReceivedTx()
let errorResponse = CrowdNodeErrorResponse(errorValue: requestValue,
accountAddress: accountAddress)
let withdrawalDeniedResponse = CrowdNodeResponse(responseCode: ApiCode.withdrawalDenied,
accountAddress: accountAddress)

let responseTx = await txObserver.first(filters: errorResponse, withdrawalDeniedResponse, receivedWithdrawalTx)
DSLogger.log("CrowdNode withdraw response tx hash: \(responseTx.txHashHexString)")
DSLogger.log("CrowdNode: request withdrawal")
let result = try await webService.requestWithdrawal(address: accountAddress, amount: amount, signature: signature)

if errorResponse.matches(tx: responseTx) || withdrawalDeniedResponse.matches(tx: responseTx) {
handleError(error: CrowdNode.Error.withdraw)
if result.messageStatus.lowercased() == kMessageReceivedStatus {
DSLogger.log("CrowdNode: withdrawal request sent successfully")
refreshBalance(afterWithdrawal: true)
} else {
DSLogger.log("CrowdNode: sendMessage not received, status: \(String(describing: result.messageStatus)). Result: \(String(describing: result.result))")

if let msg = result.result {
handleError(error: CrowdNode.Error.messageStatus(error: msg))
} else {
refreshBalance(afterWithdrawal: true)
handleError(error: CrowdNode.Error.withdraw)
}
}
}
Expand Down Expand Up @@ -644,7 +628,7 @@ extension CrowdNode {
guard !accountAddress.isEmpty else { return }

DSLogger.log("CrowdNode: sending signed email message")
let result = try await webService.sendSignedMessage(address: accountAddress, message: email, signature: signature)
let result = try await webService.registerEmail(address: accountAddress, email: email, signature: signature)

if result.messageStatus.lowercased() == kMessageReceivedStatus {
DSLogger.log("CrowdNode: signed email sent successfully")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,14 @@ extension CrowdNodeService {
}
}

func sendSignedMessage(address: String, message: String, signature: String) async throws -> MessageStatus {
func registerEmail(address: String, email: String, signature: String) async throws -> MessageStatus {
let encodedSignature = signature.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
return try await httpClient.request(.sendSignedMessage(address: address, message: message, signature: encodedSignature!))
return try await httpClient.request(.sendSignedMessage(address: address, message: email, signature: encodedSignature!, messagetype: MessageType.registerEmail))
}

func requestWithdrawal(address: String, amount: UInt64, signature: String) async throws -> MessageStatus {
let encodedSignature = signature.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
return try await httpClient.request(.sendSignedMessage(address: address, message: amount.value, signature: encodedSignature!, messagetype: MessageType.withdrawal))
}

func isDefaultEmail(address: String) async -> Bool {
Expand Down
15 changes: 13 additions & 2 deletions DashWallet/Sources/UI/CrowdNode/CrowdNodeModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,19 @@ extension CrowdNodeModel {

func withdraw(amount: UInt64) async throws -> Bool {
guard amount > 0 && walletBalance >= CrowdNode.minimumLeftoverBalance else { return false }
try await crowdNode.withdraw(amount: amount)
return true

let wallet = DWEnvironment.sharedInstance().currentWallet
let result = await wallet.seed(withPrompt: NSLocalizedString("Sign the message", comment: "CrowdNode"), forAmount: 1)

if !result.1 {
if let key = wallet.privateKey(forAddress: crowdNode.accountAddress, fromSeed: result.0!) {
let signature = DSKeyManager.signMesasageDigest(key, digest: amount.value.magicDigest())
try await crowdNode.withdraw(amount: amount, signature: signature.base64EncodedString())
return true
}
}

return false
}

func adjustedWithdrawalAmount(requestedAmount: UInt64) -> UInt64 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,27 +128,21 @@ final class CrowdNodeTransferController: SendAmountViewController, NetworkReacha
}

private func handleWithdraw(amount: UInt64) {
let vc = WithdrawalConfirmationController.controller(amount: amount, currency: model.localCurrencyCode)
vc.confirmedHandler = { [weak self] in
guard let wSelf = self else { return }

Task {
wSelf.showActivityIndicator()

do {
if try await wSelf.viewModel.withdraw(amount: amount) {
wSelf.showSuccessfulStatus()
}
} catch CrowdNode.Error.withdrawLimit(_, let period) {
wSelf.showWithdrawalLimitsError(period: period)
} catch {
wSelf.showErrorStatus(err: error)
showActivityIndicator()

Task {
do {
if try await viewModel.withdraw(amount: amount) {
showSuccessfulStatus()
}

wSelf.hideActivityIndicator()
} catch CrowdNode.Error.withdrawLimit(_, let period) {
showWithdrawalLimitsError(period: period)
} catch {
showErrorStatus(err: error)
}

hideActivityIndicator()
}
present(vc, animated: true, completion: nil)
}

deinit {
Expand Down
Loading

0 comments on commit 90c15ae

Please sign in to comment.