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

View fee breakdown and change fee tip #654

Merged
Merged
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
1a36953
wip
GhenadieVP Jul 31, 2023
ae65eb3
wip
GhenadieVP Jul 31, 2023
d71433e
wip
GhenadieVP Jul 31, 2023
a555b59
to not fail when no fee payers are found
GhenadieVP Jul 31, 2023
c2e1633
wip
GhenadieVP Aug 3, 2023
4e9a105
Merge branch 'main' into fix/ABW-1948-i-os-view-fee-breakdown-and-cha…
GhenadieVP Aug 3, 2023
ae3b665
wip
GhenadieVP Aug 3, 2023
cadb09f
wip
GhenadieVP Aug 3, 2023
765365f
wip
GhenadieVP Aug 3, 2023
bc81370
wip
GhenadieVP Aug 4, 2023
5cce56e
approve button validation
GhenadieVP Aug 8, 2023
81ce42a
send tip percentage
GhenadieVP Aug 8, 2023
522439f
wip
GhenadieVP Aug 8, 2023
e88f2cd
wip
GhenadieVP Aug 8, 2023
5fa6728
wip
GhenadieVP Aug 8, 2023
08c8a50
wip
GhenadieVP Aug 8, 2023
4adf6b1
update ret
GhenadieVP Aug 9, 2023
b65842d
gw update and manifest builder
GhenadieVP Aug 10, 2023
4629d58
wip
GhenadieVP Aug 10, 2023
14143cc
wip
GhenadieVP Aug 10, 2023
3c4a0ef
wip
GhenadieVP Aug 10, 2023
59db589
Merge branch 'main' into fix/ABW-2020-update-to-fig
GhenadieVP Aug 10, 2023
5f2d5d9
wip
GhenadieVP Aug 10, 2023
6906725
wip
GhenadieVP Aug 11, 2023
918cc6b
nft claim
GhenadieVP Aug 11, 2023
bfde171
tests
GhenadieVP Aug 11, 2023
dfecf6f
polish
GhenadieVP Aug 11, 2023
25d1fd1
wip
GhenadieVP Aug 11, 2023
6bfcf9d
Merge branch 'fix/ABW-2020-update-to-fig' into fix/ABW-1948-i-os-view…
GhenadieVP Aug 11, 2023
0bfb3ff
Update GatewayAPIClient+Live.swift
GhenadieVP Aug 11, 2023
8a1a53e
wip
GhenadieVP Aug 14, 2023
a96b3f2
wip
GhenadieVP Aug 14, 2023
af28fef
review comments
GhenadieVP Aug 14, 2023
4912995
Merge branch 'main' into fix/ABW-2020-update-to-fig
GhenadieVP Aug 14, 2023
201c3b4
Merge branch 'fix/ABW-2020-update-to-fig' into fix/ABW-1948-i-os-view…
GhenadieVP Aug 14, 2023
9a43795
wip
GhenadieVP Aug 14, 2023
eee0046
wip
GhenadieVP Aug 15, 2023
fdd4301
wip
GhenadieVP Aug 15, 2023
d9b4253
Merge branch 'main' into fix/ABW-1948-i-os-view-fee-breakdown-and-cha…
GhenadieVP Aug 15, 2023
ba39f72
wip
GhenadieVP Aug 15, 2023
f58cd50
New translations target: macosx (English)
radixbot Aug 14, 2023
b7eba17
New translations target: macosx (English)
radixbot Aug 15, 2023
003f4ba
generate
GhenadieVP Aug 15, 2023
833b083
wip
GhenadieVP Aug 15, 2023
7ffb524
fix
GhenadieVP Aug 15, 2023
fd22bc5
package update
GhenadieVP Aug 15, 2023
9192df6
Merge branch 'l10n_main2' into fix/ABW-1948-i-os-view-fee-breakdown-a…
GhenadieVP Aug 15, 2023
3e7f659
wip
GhenadieVP Aug 15, 2023
c919c28
Merge branch 'main' into fix/ABW-1948-i-os-view-fee-breakdown-and-cha…
GhenadieVP Aug 15, 2023
64acbd3
wip
GhenadieVP Aug 16, 2023
1fc84d2
wip
GhenadieVP Aug 16, 2023
048a70e
wip
GhenadieVP Aug 16, 2023
86abd1c
Merge branch 'main' into fix/ABW-1948-i-os-view-fee-breakdown-and-cha…
GhenadieVP Aug 16, 2023
e4d3afb
wip
GhenadieVP Aug 16, 2023
43a8bac
wip
GhenadieVP Aug 16, 2023
5db84ee
wip
GhenadieVP Aug 17, 2023
c54787f
in progress
GhenadieVP Aug 21, 2023
842a86e
wip
GhenadieVP Aug 21, 2023
759fefb
wip
GhenadieVP Aug 21, 2023
b33798c
review update
GhenadieVP Aug 22, 2023
fc99bd4
wip
GhenadieVP Aug 22, 2023
cb86c88
Merge branch 'main' into fix/ABW-1948-i-os-view-fee-breakdown-and-cha…
GhenadieVP Aug 22, 2023
ad2c5bf
fix
GhenadieVP Aug 22, 2023
e0b0f6b
format
GhenadieVP Aug 23, 2023
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
1 change: 1 addition & 0 deletions Sources/Clients/FaucetClient/FaucetClient+Live.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ extension FaucetClient: DependencyKey {
networkID: networkID,
manifest: manifest,
message: .none,
makeTransactionHeaderInput: .default,
isFaucetTransaction: true,
ephemeralNotaryPublicKey: ephemeralNotary.publicKey
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ extension TransactionFailure {
switch self {
case let .failedToPrepareForTXSigning(error), let .failedToPrepareTXReview(.failedSigning(error)):
switch error {
case .failedToFindAccountWithEnoughFundsToLockFee:
return (errorKind: .failedToFindAccountWithEnoughFundsToLockFee, message: error.errorDescription)
case .failedToGetEpoch, .failedToLoadNotaryAndSigners, .failedToLoadNotaryPublicKey, .failedToLoadSignerPublicKeys, .failedToParseTXItIsProbablyInvalid:
return (errorKind: .failedToPrepareTransaction, message: error.errorDescription)
}
Expand Down Expand Up @@ -110,7 +108,6 @@ extension TransactionFailure {
case failedToLoadNotaryAndSigners
case failedToLoadNotaryPublicKey
case failedToLoadSignerPublicKeys
case failedToFindAccountWithEnoughFundsToLockFee

public var errorDescription: String? {
switch self {
Expand All @@ -124,8 +121,6 @@ extension TransactionFailure {
return "Failed to load signer public keys"
case .failedToLoadNotaryAndSigners:
return "Failed to load notary and signers"
case .failedToFindAccountWithEnoughFundsToLockFee:
return "Failed to find an account with enough funds to lock fee"
}
}
}
Expand Down
233 changes: 190 additions & 43 deletions Sources/Clients/TransactionClient/Models/TransactionModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ public struct BuildTransactionIntentRequest: Sendable {
manifest: TransactionManifest,
message: Message,
nonce: Nonce = .secureRandom(),
makeTransactionHeaderInput: MakeTransactionHeaderInput = .default,
makeTransactionHeaderInput: MakeTransactionHeaderInput,
isFaucetTransaction: Bool = false,
ephemeralNotaryPublicKey: Curve25519.Signing.PublicKey
) {
Expand Down Expand Up @@ -198,7 +198,6 @@ public struct ManifestReviewRequest: Sendable {
public let manifestToSign: TransactionManifest
public let message: Message
public let nonce: Nonce
public let feeToAdd: BigDecimal
public let makeTransactionHeaderInput: MakeTransactionHeaderInput
public let ephemeralNotaryPublicKey: Curve25519.Signing.PublicKey

Expand All @@ -207,76 +206,224 @@ public struct ManifestReviewRequest: Sendable {
message: Message,
nonce: Nonce,
makeTransactionHeaderInput: MakeTransactionHeaderInput = .default,
feeToAdd: BigDecimal,
ephemeralNotaryPublicKey: Curve25519.Signing.PublicKey = Curve25519.Signing.PrivateKey().publicKey
) {
self.manifestToSign = manifestToSign
self.message = message
self.nonce = nonce
self.feeToAdd = feeToAdd
self.makeTransactionHeaderInput = makeTransactionHeaderInput
self.ephemeralNotaryPublicKey = ephemeralNotaryPublicKey
}
}

// MARK: - FeePayerCandiate
public struct FeePayerCandiate: Sendable, Hashable, Identifiable {
// MARK: - FeePayerCandidate
public struct FeePayerCandidate: Sendable, Hashable, Identifiable {
public let account: Profile.Network.Account
public let xrdBalance: BigDecimal
public typealias ID = Profile.Network.Account.ID
public var id: ID { account.id }
GhenadieVP marked this conversation as resolved.
Show resolved Hide resolved
}

// MARK: - AddFeeToManifestOutcome
public enum AddFeeToManifestOutcome: Sendable, Equatable {
case includesLockFee(AddFeeToManifestOutcomeIncludesLockFee)
case excludesLockFee(AddFeeToManifestOutcomeExcludesLockFee)
}

// MARK: - AddFeeToManifestOutcomeIncludesLockFee
public struct AddFeeToManifestOutcomeIncludesLockFee: Sendable, Equatable {
public let manifestWithLockFee: TransactionManifest
public let feePayerSelectionAmongstCandidates: FeePayerSelectionAmongstCandidates
}

// MARK: - AddFeeToManifestOutcomeExcludesLockFee
public struct AddFeeToManifestOutcomeExcludesLockFee: Sendable, Equatable {
public let manifestExcludingLockFee: TransactionManifest
public let feePayerCandidates: NonEmpty<IdentifiedArrayOf<FeePayerCandiate>>
public let feeNotYetAdded: BigDecimal
}

// MARK: - TransactionToReview
public struct TransactionToReview: Sendable, Equatable {
public let analyzedManifestToReview: ExecutionAnalysis
public let addFeeToManifestOutcome: AddFeeToManifestOutcome
public let networkID: NetworkID
public let feePayerSelectionAmongstCandidates: FeePayerSelectionAmongstCandidates
}

// MARK: - FeePayerSelectionAmongstCandidates
public struct FeePayerSelectionAmongstCandidates: Sendable, Hashable {
public enum Selection: Sendable, Hashable {
case selectedByUser
case auto
}

public let selected: FeePayerCandiate
public var selected: FeePayerCandidate?
/// contains `selected`
public let candidates: NonEmpty<IdentifiedArrayOf<FeePayerCandiate>>

public let fee: BigDecimal

public let selection: Selection
public let candidates: NonEmpty<IdentifiedArrayOf<FeePayerCandidate>>
public var transactionFee: TransactionFee

public init(
selected: FeePayerCandiate,
candidates: NonEmpty<IdentifiedArrayOf<FeePayerCandiate>>,
fee: BigDecimal,
selection: Selection
selected: FeePayerCandidate?,
candidates: NonEmpty<IdentifiedArrayOf<FeePayerCandidate>>,
transactionFee: TransactionFee
) {
self.selected = selected
self.candidates = candidates
self.fee = fee
self.selection = selection
self.transactionFee = transactionFee
}
}

// MARK: - TransactionFee
public struct TransactionFee: Hashable, Sendable {
/// FeeSummary after transaction was analyzed
let feeSummary: FeeSummary

/// FeeLocks after transaction was analyzed
let feeLocks: FeeLocks

/// The calculaton mode
public var mode: Mode

public init(feeSummary: FeeSummary, feeLocks: FeeLocks, mode: Mode) {
self.feeSummary = feeSummary
self.feeLocks = feeLocks
self.mode = mode
}

public init(executionAnalysis: ExecutionAnalysis) throws {
let feeSummary: FeeSummary = try .init(
executionCost: .init(executionAnalysis.feeSummary.executionCost),
finalizationCost: .init(executionAnalysis.feeSummary.finalizationCost),
storageExpansionCost: .init(executionAnalysis.feeSummary.storageExpansionCost),
royaltyCost: .init(executionAnalysis.feeSummary.royaltyCost)
)

let feeLocks: FeeLocks = try .init(
nonContingentLock: .init(executionAnalysis.feeLocks.lock),
contingentLock: .init(executionAnalysis.feeLocks.contingentLock)
)

self.init(
feeSummary: feeSummary,
feeLocks: feeLocks,
mode: .normal(.init(feeSummary: feeSummary, feeLocks: feeLocks))
)
}
}

extension TransactionFee {
/// Calculates the totalFee for the transaction based on the `mode`
public var totalFee: TotalFee {
switch mode {
case let .normal(normalCustomization):
let maxFee = normalCustomization.total
let minFee = maxFee.clampedDiff(feeLocks.contingentLock)
return .init(min: minFee, max: maxFee)
case let .advanced(advancedCustomization):
return .init(min: advancedCustomization.total, max: advancedCustomization.total)
}
}

public mutating func toggleMode() {
switch mode {
case .normal:
mode = .advanced(.init(feeSummary: feeSummary))
case .advanced:
mode = .normal(.init(feeSummary: feeSummary, feeLocks: feeLocks))
}
}

public var isNormalMode: Bool {
if case .normal = mode {
return true
}
return false
}
}

extension TransactionFee {
enum PredefinedFeeConstants {
/// 15% margin is added here to make up for the ambiguity of the transaction preview estimate)
static let networkFeeMultiplier: BigDecimal = 0.15

// TODO: Add WalletFees table. Which is yet to be determined.
}

public enum Mode: Hashable, Sendable {
case normal(NormalFeeCustomization)
case advanced(AdvancedFeeCustomization)
}

public struct FeeSummary: Hashable, Sendable {
public let executionCost: BigDecimal
public let finalizationCost: BigDecimal
public let storageExpansionCost: BigDecimal
public let royaltyCost: BigDecimal

public var total: BigDecimal {
executionCost + finalizationCost + storageExpansionCost + royaltyCost
}

public init(
executionCost: BigDecimal,
finalizationCost: BigDecimal,
storageExpansionCost: BigDecimal,
royaltyCost: BigDecimal
) {
self.executionCost = executionCost
self.finalizationCost = finalizationCost
self.storageExpansionCost = storageExpansionCost
self.royaltyCost = royaltyCost
}
}

public struct FeeLocks: Hashable, Sendable {
let nonContingentLock: BigDecimal
let contingentLock: BigDecimal

public init(nonContingentLock: BigDecimal, contingentLock: BigDecimal) {
self.nonContingentLock = nonContingentLock
self.contingentLock = contingentLock
}
}

public struct AdvancedFeeCustomization: Hashable, Sendable {
private let networkFee: BigDecimal

public let feeSummary: FeeSummary
public var paddingFee: BigDecimal
public var tipPercentage: BigDecimal

public var tipAmount: BigDecimal {
(tipPercentage / 100) * networkFee
}

public var total: BigDecimal {
feeSummary.total + paddingFee + tipAmount
}

public init(feeSummary: FeeSummary) {
self.feeSummary = feeSummary
// The networkFee is used to derive the base PaddingFee as well the tipAmount
self.networkFee = feeSummary.executionCost + feeSummary.finalizationCost
self.paddingFee = networkFee * PredefinedFeeConstants.networkFeeMultiplier
self.tipPercentage = .zero
}
}

public struct NormalFeeCustomization: Hashable, Sendable {
public let networkFee: BigDecimal
public let royaltyFee: BigDecimal
public let total: BigDecimal

public init(networkFee: BigDecimal, royaltyFee: BigDecimal) {
self.networkFee = networkFee
self.royaltyFee = royaltyFee
self.total = networkFee + royaltyFee
}

public init(feeSummary: FeeSummary, feeLocks: FeeLocks) {
var networkFee = feeSummary.executionCost + feeSummary.finalizationCost + feeSummary.storageExpansionCost
GhenadieVP marked this conversation as resolved.
Show resolved Hide resolved
networkFee += networkFee * PredefinedFeeConstants.networkFeeMultiplier
let remainingNonContingentLock = feeLocks.nonContingentLock.clampedDiff(networkFee)

self.init(
networkFee: networkFee.clampedDiff(feeLocks.nonContingentLock),
royaltyFee: feeSummary.royaltyCost.clampedDiff(remainingNonContingentLock)
)
}
}

public struct TotalFee: Hashable, Sendable {
let min: BigDecimal
let max: BigDecimal

public var lockFee: BigDecimal {
// We always lock the max amount
max
}

public var displayedTotalFee: String {
if max > min {
return "\(min.format()) - \(max.format()) XRD"
}
return "\(max.format()) XRD"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import FactorSourcesClient

// MARK: - TransactionClient
public struct TransactionClient: Sendable, DependencyKey {
public var lockFeeBySearchingForSuitablePayer: LockFeeBySearchingForSuitablePayer
public var lockFeeWithSelectedPayer: LockFeeWithSelectedPayer
public var getTransactionReview: GetTransactionReview
public var buildTransactionIntent: BuildTransactionIntent
public var notarizeTransaction: NotarizeTransaction
Expand All @@ -16,9 +14,6 @@ public struct TransactionClient: Sendable, DependencyKey {

// MARK: TransactionClient.SignAndSubmitTransaction
extension TransactionClient {
public typealias LockFeeBySearchingForSuitablePayer = @Sendable (TransactionManifest, _ fee: BigDecimal) async throws -> AddFeeToManifestOutcome
public typealias LockFeeWithSelectedPayer = @Sendable (TransactionManifest, _ fee: BigDecimal, _ payer: AccountAddress) async throws -> TransactionManifest

public typealias GetTransactionReview = @Sendable (ManifestReviewRequest) async throws -> TransactionToReview
public typealias BuildTransactionIntent = @Sendable (BuildTransactionIntentRequest) async throws -> TransactionIntentWithSigners
public typealias NotarizeTransaction = @Sendable (NotarizeTransactionRequest) async throws -> NotarizeTransactionResponse
Expand All @@ -34,48 +29,29 @@ extension DependencyValues {
}
}

// MARK: - AddInstructionToManifestLocation
public enum AddInstructionToManifestLocation: Sendable, Hashable {
case first
public static let lockFee: Self = .first
}

// MARK: - AddInstructionToManifestRequest
public struct AddInstructionToManifestRequest: Sendable, Hashable {
public let instruction: Instruction
public let manifest: TransactionManifest
public let location: AddInstructionToManifestLocation
public init(
instruction: Instruction,
to manifest: TransactionManifest,
at location: AddInstructionToManifestLocation
) {
self.instruction = instruction
self.manifest = manifest
self.location = location
}
}

extension TransactionClient {
public struct PrepareForSigningRequest: Equatable, Sendable {
public let nonce: Nonce
public let manifest: TransactionManifest
public let message: Message
public let feePayer: Profile.Network.Account
// Optional, as there might not be any fee
public let feePayer: Profile.Network.Account?
public let networkID: NetworkID
public let purpose: SigningPurpose

public var compiledIntent: [UInt8]? = nil
public let ephemeralNotaryPublicKey: Curve25519.Signing.PublicKey
public let transactionHeader: MakeTransactionHeaderInput

public init(
nonce: Nonce,
manifest: TransactionManifest,
message: Message,
networkID: NetworkID,
feePayer: Profile.Network.Account,
feePayer: Profile.Network.Account?,
purpose: SigningPurpose,
ephemeralNotaryPublicKey: Curve25519.Signing.PublicKey
ephemeralNotaryPublicKey: Curve25519.Signing.PublicKey,
transactionHeader: MakeTransactionHeaderInput
) {
self.nonce = nonce
self.manifest = manifest
Expand All @@ -84,6 +60,7 @@ extension TransactionClient {
self.feePayer = feePayer
self.purpose = purpose
self.ephemeralNotaryPublicKey = ephemeralNotaryPublicKey
self.transactionHeader = transactionHeader
}
}

Expand Down
Loading