Skip to content

Commit

Permalink
Merge pull request #675 from Syn-McJ/feat/crowdnode-dynamic-fees
Browse files Browse the repository at this point in the history
feat(crowdnode): dynamic fees
  • Loading branch information
Syn-McJ authored Dec 17, 2024
2 parents ef42dbb + fb2d577 commit 1ef9e4b
Show file tree
Hide file tree
Showing 13 changed files with 156 additions and 87 deletions.
2 changes: 1 addition & 1 deletion DashSyncCurrentCommit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
751595434308e0761cceb15b428a41ca5eefdb8b
1828f755c499de14261343e0426c83f2aa88a9bd
100 changes: 50 additions & 50 deletions DashWallet.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

13 changes: 7 additions & 6 deletions DashWallet/Sources/Categories/DSChain+DashWallet.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
#import "DSChainsManager.h"
#import "DSCheckpoint.h"
#import "DSMasternodeManager.h"
#import "DSSimplifiedMasternodeEntry.h"
#import "NSDate+Utils.h"
#import <objc/runtime.h>
#import "DSSimplifiedMasternodeEntry.h"


NS_ASSUME_NONNULL_BEGIN
Expand Down Expand Up @@ -91,17 +91,18 @@ - (NSNumber *_Nullable)calculateMasternodeAPY {
return nil;

DSMasternodeList *masternodeList = self.chainManager.masternodeManager.currentMasternodeList;

if (masternodeList.validMasternodeCount == 0)
return nil;

NSInteger virtualMNCount = 0;

for (DSSimplifiedMasternodeEntry *entry in masternodeList.simplifiedMasternodeEntries) {
if (entry.isValid) {
if (entry.type == 1) { // HPMN
virtualMNCount += 4;
} else {
}
else {
virtualMNCount += 1;
}
}
Expand Down Expand Up @@ -188,14 +189,14 @@ - (uint64_t)calculateMasternodePaymentWithHeight:(uint64_t)height blockReward:(u
// Activated but we have to wait for the next cycle to start realocation, nothing to do
return ret;
}

if ([self isCore20ActiveAtHeight:height]) {
// Once MNRewardReallocated activates, block reward is 80% of block subsidy (+ tx fees) since treasury is 20%
// Since the MN reward needs to be equal to 60% of the block subsidy (according to the proposal), MN reward is set to 75% of the block reward.
// Previous reallocation periods are dropped.
return blockReward * 3 / 4;
}

NSUInteger reallocCycle = superblockCycle * 3;
NSUInteger nCurrentPeriod = MIN((height - reallocStart) / reallocCycle, periodsCount - 1);
return (blockReward * periods[nCurrentPeriod]) / 1000;
Expand Down
20 changes: 0 additions & 20 deletions DashWallet/Sources/Models/CrowdNode/API/CrowdNodeAPI.swift

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public enum CrowdNodeEndpoint {
case hasDefaultEmail(String)
case sendSignedMessage(address: String, message: String, signature: String, messagetype: MessageType)
case getMessages(String)
case getFees(String)
}

// MARK: TargetType
Expand All @@ -53,9 +54,10 @@ extension CrowdNodeEndpoint: TargetType {
case .hasDefaultEmail(let address): return "odata/apiaddresses/UsingDefaultApiEmail(address='\(address)')"
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)')"
case .getFees(let address): return "odata/apifundings/GetFeeJson(address='\(address)')"
}
}

public var method: Moya.Method {
.get
}
Expand All @@ -68,3 +70,7 @@ extension CrowdNodeEndpoint: TargetType {
[:]
}
}

final class CrowdNodeAPI: HTTPClient<CrowdNodeEndpoint> {
static let shared = CrowdNodeAPI()
}
46 changes: 46 additions & 0 deletions DashWallet/Sources/Models/CrowdNode/API/DTOs/FeeInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// Created by Andrei Ashikhmin
// Copyright © 2024 Dash Core Group. All rights reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

private let kDefaultAmount = 100.0
private let kTypeNormal = "Normal"

struct FeeInfo: Codable {
static let empty = FeeInfo(feeLadder: [FeeLadder.empty])
let feeLadder: [FeeLadder]

enum CodingKeys: String, CodingKey {
case feeLadder = "FeeLadder"
}

func getNormalFee() -> FeeLadder? {
return feeLadder.first { $0.type == kTypeNormal }
}
}

struct FeeLadder: Codable {
let name: String
let type: String
let amount: Double
let fee: Double

static let empty = FeeLadder(
name: "",
type: kTypeNormal,
amount: kDefaultAmount,
fee: CrowdNode.defaultFee * 100
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ extension CrowdNode {
static let minimumDeposit = UInt64(kOneDash / 2)
static let minimumLeftoverBalance: UInt64 = 30_000
static let apiConfirmationDashAmount: UInt64 = 54321
static let defaultFee = 0.35

static let notificationID = "CrowdNode"

Expand Down
10 changes: 10 additions & 0 deletions DashWallet/Sources/Models/CrowdNode/CrowdNode+UserDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ private let kOnlineInfoShown = "crowdNodeOnlineInfoShownKey"
private let kSignedEmailMessageId = "crowdNodeSignedEmailMessageId"
private let kShouldShowConfirmedNotification = "shouldShowConfirmedNotification"
private let kLastWithdrawalBlock = "lastWithdrawalBlockKey"
private let kFeePercentage = "feePercentageKey"

// MARK: - CrowdNodeDefaults

Expand Down Expand Up @@ -91,6 +92,15 @@ class CrowdNodeDefaults {
UserDefaults.standard.set(value, forKey: kWithdrawalLimitPerDay)
}
}

private var _feePercentage: Double? = nil
var feePercentage: Double {
get { _feePercentage ?? UserDefaults.standard.value(forKey: kFeePercentage) as? Double ?? CrowdNode.defaultFee }
set(value) {
_feePercentage = value
UserDefaults.standard.set(value, forKey: kFeePercentage)
}
}

private var _withdrawalLimitsInfoShown: Bool? = nil
var withdrawalLimitsInfoShown: Bool {
Expand Down
21 changes: 19 additions & 2 deletions DashWallet/Sources/Models/CrowdNode/CrowdNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ public final class CrowdNode {

init() {
masternodeAPY = DWEnvironment.sharedInstance().apy.doubleValue
crowdnodeAPY = masternodeAPY * 0.85
crowdnodeAPY = masternodeAPY * (1 - prefs.feePercentage)
print("CrowdNode: masternodeAPY: \(masternodeAPY), crowdnodeAPY: \(crowdnodeAPY)")

NotificationCenter.default.publisher(for: NSNotification.Name.DWWillWipeWallet)
.sink { [weak self] _ in self?.reset() }
Expand Down Expand Up @@ -195,6 +196,7 @@ extension CrowdNode {

if tryRestoreSignUp() {
refreshWithdrawalLimits()
refreshFees()
restoreCreatedOnlineAccount(accountAddress)
return
}
Expand Down Expand Up @@ -306,7 +308,8 @@ extension CrowdNode {

if let apy = chain.calculateMasternodeAPY()?.doubleValue {
masternodeAPY = apy
crowdnodeAPY = masternodeAPY * 0.85
let multiplier = 1 - prefs.feePercentage
crowdnodeAPY = masternodeAPY * multiplier
chain.apy = NSNumber(value: apy)
}
}
Expand Down Expand Up @@ -612,6 +615,20 @@ extension CrowdNode {
}
}
}

private func refreshFees() {
Task {
do {
let feeInfo = try await webService.getFees(address: accountAddress)

if let value = feeInfo.getNormalFee() {
prefs.feePercentage = value.fee / 100
}
} catch {
DSLogger.log("CrowdNode refreshFees error: \(error.localizedDescription)")
}
}
}

private func getWithdrawalLimit(_ period: WithdrawalLimitPeriod) -> UInt64 {
switch period {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,8 @@ extension CrowdNodeService {
return nil
}
}

func getFees(address: String) async throws -> FeeInfo {
try await httpClient.request(.getFees(address))
}
}
2 changes: 1 addition & 1 deletion DashWallet/Sources/Models/DWGlobalOptions.m
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ - (void)restoreToDefaults {
self.dateHistoricalRatesActivated = nil;
self.exploreDashMerchantsInfoShown = NO;
self.coinbaseInfoShown = NO;

#ifdef DASHPAY
self.dashpayUsername = nil;
self.dashpayRegistrationCompleted = NO;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class CrowdNodeAPYView: UIView {
}

private var apy: String {
let apyValue = DWEnvironment.sharedInstance().apy.doubleValue * 0.85
let apyValue = CrowdNode.shared.crowdnodeAPY

let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .percent
Expand Down
14 changes: 9 additions & 5 deletions Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,7 @@ PODS:
- abseil/base/base_internal
- abseil/base/config
- abseil/meta/type_traits
- Alamofire (5.10.0)
- Alamofire (5.10.1)
- BlueCryptor (1.0.32)
- BlueECC (1.2.5)
- BlueRSA (1.0.200)
Expand All @@ -591,9 +591,11 @@ PODS:
- "!ProtoCompiler-gRPCPlugin (~> 1.0)"
- DAPI-GRPC/Messages
- gRPC-ProtoRPC
- DashSharedCore (0.4.19)
- DashSync (0.1.0):
- CocoaLumberjack (= 3.7.2)
- DAPI-GRPC (= 0.22.0-dev.8)
- DashSharedCore (= 0.4.19)
- DSDynamicOptions (= 0.1.2)
- DWAlertController (= 0.2.1)
- TinyCborObjc (= 0.4.6)
Expand Down Expand Up @@ -708,7 +710,7 @@ PODS:
- nanopb/decode (2.30908.0)
- nanopb/encode (2.30908.0)
- PromisesObjC (2.4.0)
- Protobuf (3.28.2)
- Protobuf (3.28.3)
- SDWebImage (5.13.2):
- SDWebImage/Core (= 5.13.2)
- SDWebImage/Core (5.13.2)
Expand Down Expand Up @@ -764,6 +766,7 @@ SPEC REPOS:
- CloudInAppMessaging
- CocoaLumberjack
- DAPI-GRPC
- DashSharedCore
- DSDynamicOptions
- DWAlertController
- Firebase
Expand Down Expand Up @@ -819,7 +822,7 @@ SPEC CHECKSUMS:
"!ProtoCompiler": e9c09244955a8565817aa59a4787b6bb849a63c6
"!ProtoCompiler-gRPCPlugin": 755f0ee414a0d5f0028e0dcfe98c23bdbc3e6fa3
abseil: 926fb7a82dc6d2b8e1f2ed7f3a718bce691d1e46
Alamofire: cd0b98508df05796dd2ff278f3bb055a631b5390
Alamofire: 840d2a1ad82355b536ec6ba5f97e5bfa54600ca3
BlueCryptor: b0aee3d9b8f367b49b30de11cda90e1735571c24
BlueECC: 0d18e93347d3ec6d41416de21c1ffa4d4cd3c2cc
BlueRSA: dfeef51db96bcc4edec654956c1581adbda4e6a3
Expand All @@ -828,7 +831,8 @@ SPEC CHECKSUMS:
CocoaImageHashing: 8656031d0899abe6c1c415827de43e9798189c53
CocoaLumberjack: b7e05132ff94f6ae4dfa9d5bce9141893a21d9da
DAPI-GRPC: 138d62523bbfe7e88a39896f1053c0bc12390d9f
DashSync: 2438dbf626f13a8633ccc19c718c1c223c8ee831
DashSharedCore: 009f29640756017406ee2b0ab5f1073f1856f85e
DashSync: f0ee76fe1409c9071bcee21202cc8002944c801d
DSDynamicOptions: 347cc5d2c4e080eb3de6a86719ad3d861b82adfc
DWAlertController: 5f4cd8adf90336331c054857f709f5f8d4b16a5b
Firebase: 5f8193dff4b5b7c5d5ef72ae54bb76c08e2b841d
Expand All @@ -852,7 +856,7 @@ SPEC CHECKSUMS:
Moya: 138f0573e53411fb3dc17016add0b748dfbd78ee
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
Protobuf: 28c89b24435762f60244e691544ed80f50d82701
Protobuf: 5a8a7781d8e1004302f108977ac2d5b99323146f
SDWebImage: 72f86271a6f3139cc7e4a89220946489d4b9a866
SQLite.swift: 8d054987f02728cc912b0eb5a9659650573a65a2
SQLiteMigrationManager.swift: b63bb5eaf834f8e8cc78b37fdf2ce064e35914cd
Expand Down

0 comments on commit 1ef9e4b

Please sign in to comment.