Skip to content

Commit

Permalink
✨ Add FeeCalculator and FeeCalculatorTests
Browse files Browse the repository at this point in the history
  • Loading branch information
usatie committed Sep 19, 2019
1 parent 6886a1f commit 11f1cd3
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 0 deletions.
8 changes: 8 additions & 0 deletions BitcoinKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@
2949920620F22DCA00D078B6 /* UnsignedTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2949920520F22DCA00D078B6 /* UnsignedTransaction.swift */; };
294CE2DF232CA88800922458 /* CoinType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 294CE2DE232CA88800922458 /* CoinType.swift */; };
294CE2E9232CBE8F00922458 /* HDWalletTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 294CE2E7232CBE7100922458 /* HDWalletTests.swift */; };
294CE2EB232CE9AE00922458 /* FeeCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 294CE2EA232CE9AE00922458 /* FeeCalculator.swift */; };
294CE2EF232CECCD00922458 /* FeeCalculatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 294CE2EC232CEBAB00922458 /* FeeCalculatorTests.swift */; };
294DDE32211A05D200B7F645 /* OP_RETURN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 294DDE31211A05D200B7F645 /* OP_RETURN.swift */; };
294DDE3B211B31B100B7F645 /* OP_NOP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 294DDE3A211B31B100B7F645 /* OP_NOP.swift */; };
294DDE3D211B31C100B7F645 /* OP_VER.swift in Sources */ = {isa = PBXBuildFile; fileRef = 294DDE3C211B31C100B7F645 /* OP_VER.swift */; };
Expand Down Expand Up @@ -405,6 +407,8 @@
2949920520F22DCA00D078B6 /* UnsignedTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsignedTransaction.swift; sourceTree = "<group>"; };
294CE2DE232CA88800922458 /* CoinType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoinType.swift; sourceTree = "<group>"; };
294CE2E7232CBE7100922458 /* HDWalletTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HDWalletTests.swift; sourceTree = "<group>"; };
294CE2EA232CE9AE00922458 /* FeeCalculator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeeCalculator.swift; sourceTree = "<group>"; };
294CE2EC232CEBAB00922458 /* FeeCalculatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeeCalculatorTests.swift; sourceTree = "<group>"; };
294DDE31211A05D200B7F645 /* OP_RETURN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OP_RETURN.swift; sourceTree = "<group>"; };
294DDE3A211B31B100B7F645 /* OP_NOP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OP_NOP.swift; sourceTree = "<group>"; };
294DDE3C211B31C100B7F645 /* OP_VER.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OP_VER.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -730,6 +734,7 @@
isa = PBXGroup;
children = (
294CE2E7232CBE7100922458 /* HDWalletTests.swift */,
294CE2EC232CEBAB00922458 /* FeeCalculatorTests.swift */,
);
path = Wallet;
sourceTree = "<group>";
Expand Down Expand Up @@ -907,6 +912,7 @@
isa = PBXGroup;
children = (
14A2961E2032317B00E19177 /* HDWallet.swift */,
294CE2EA232CE9AE00922458 /* FeeCalculator.swift */,
);
path = Wallet;
sourceTree = "<group>";
Expand Down Expand Up @@ -1300,6 +1306,7 @@
2914BE4E211BCF7600B349CB /* OP_RESERVED.swift in Sources */,
14839A81202FE5CA00A6CB34 /* VerackMessage.swift in Sources */,
29E1ED71210EC751007F4627 /* OP_1NEGATE.swift in Sources */,
294CE2EB232CE9AE00922458 /* FeeCalculator.swift in Sources */,
0C66CD032125425D0049DB89 /* OP_INVERT.swift in Sources */,
0C0900242116935E0077E9BC /* OP_MAX.swift in Sources */,
14839A95202FE6BE00A6CB34 /* GetBlocksMessage.swift in Sources */,
Expand Down Expand Up @@ -1395,6 +1402,7 @@
6E20AECF2112C66C008A9810 /* HDKeyChainTests.swift in Sources */,
CF432AF420F0DFAC00AD4020 /* Bech32Tests.swift in Sources */,
29089F0E2122BE7200E0C305 /* MockHelperTests.swift in Sources */,
294CE2EF232CECCD00922458 /* FeeCalculatorTests.swift in Sources */,
6E20AECD2112C559008A9810 /* CashAddrTests.swift in Sources */,
A2DAA5F7211F0579009DAB7B /* UInt32MathTests.swift in Sources */,
29F5D1E02110495F007DA3BF /* OpCodeTests.swift in Sources */,
Expand Down
52 changes: 52 additions & 0 deletions Sources/BitcoinKit/Wallet/FeeCalculator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// FeeCalculator.swift
//
// Copyright © 2019 BitcoinKit developers
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

import Foundation

public struct FeeCalculator {
/// Calclate minimum utxo amount that will be accepted by full nodes
/// Check here : https://github.com/Bitcoin-ABC/bitcoin-abc/blob/1da1ddd10d3a52de49fcf3b399917cfbc28ae8d6/src/test/transaction_tests.cpp#L641
static func calculateDust(feePerByte: UInt64) -> UInt64 {
return 3 * 182 * feePerByte
}

/// Calculate fee
// txin(P2PKH) : 148 bytes
// txout(P2PKH) : 34 bytes
// cf. txin(P2SH) : cannot be decided
// cf. txout(P2SH) : 32 bytes
// cf. txout(OP_RETURN + String) : Roundup([#Characters]/32) + [#Characters] + 11 bytes
static func calculateFee(inputs: UInt64, outputs: UInt64, feePerByte: UInt64) -> UInt64 {
guard inputs > 0 else {
return 0
}
let txByteSize: UInt64 = (inputs * 148) + (outputs * 34) + 10
return txByteSize * feePerByte
}

/// Calculate fee per single input (Not including other inputs, outputs and tx of itself)
static func calculateSingleInputFee(feePerByte: UInt64) -> UInt64 {
return 148 * feePerByte
}
}
53 changes: 53 additions & 0 deletions Tests/BitcoinKitTests/Wallet/FeeCalculatorTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// FeeCalculatorTests.swift
//
// Copyright © 2019 BitcoinKit developers
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

import XCTest
@testable import BitcoinKit

final class FeeCalculatorTests: XCTestCase {
func testCalculateDust() {
XCTAssertEqual(FeeCalculator.calculateDust(feePerByte: 1), 546)
XCTAssertEqual(FeeCalculator.calculateDust(feePerByte: 2), 1092)
XCTAssertEqual(FeeCalculator.calculateDust(feePerByte: 123), 67158)

}

func testCalculateSingleInputFee() {
XCTAssertEqual(FeeCalculator.calculateSingleInputFee(feePerByte: 1), 148)
XCTAssertEqual(FeeCalculator.calculateSingleInputFee(feePerByte: 2), 296)
XCTAssertEqual(FeeCalculator.calculateSingleInputFee(feePerByte: 123), 18_204)

}

func testCalculateFee() {
XCTAssertEqual(FeeCalculator.calculateFee(inputs: 0, outputs: 0, feePerByte: 1), 0)
XCTAssertEqual(FeeCalculator.calculateFee(inputs: 1, outputs: 0, feePerByte: 1), 158)
XCTAssertEqual(FeeCalculator.calculateFee(inputs: 0, outputs: 1, feePerByte: 1), 0)
XCTAssertEqual(FeeCalculator.calculateFee(inputs: 1, outputs: 1, feePerByte: 1), 192)
XCTAssertEqual(FeeCalculator.calculateFee(inputs: 10, outputs: 20, feePerByte: 1), 2170)
XCTAssertEqual(FeeCalculator.calculateFee(inputs: 10, outputs: 2, feePerByte: 1), 1558)
XCTAssertEqual(FeeCalculator.calculateFee(inputs: 10, outputs: 2, feePerByte: 123), 191_634)
}
}

0 comments on commit 11f1cd3

Please sign in to comment.