Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(crowdnode): dynamic fees #675

Merged
merged 1 commit into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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