Skip to content

Commit e30d812

Browse files
Merge pull request #510 from yaroslavyaroslav/feature/EIP-1559
2 parents 85e2d24 + f48066b commit e30d812

File tree

11 files changed

+668
-402
lines changed

11 files changed

+668
-402
lines changed

Sources/web3swift/Convenience/Decodable+Extensions.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Created by levantAJ on 1/18/19.
66
// Copyright © 2019 levantAJ. All rights reserved.
77
//
8+
import BigInt
89
import Foundation
910

1011
struct AnyCodingKey: CodingKey {
@@ -93,6 +94,50 @@ extension KeyedDecodingContainer {
9394
try decodeNil(forKey: key) == false else { return nil }
9495
return try decode(type, forKey: key)
9596
}
97+
98+
/// Decodes a value of the given key from Hex to BigUInt
99+
///
100+
/// Currently this method supports only `Data.Type`, `BigUInt.Type`, `Date.Type`
101+
///
102+
/// - Parameter type: Generic type `T` wich conforms to `DecodableFromHex` protocol
103+
/// - Parameter key: The key that the decoded value is associated with.
104+
/// - Returns: A decoded value of type `BigUInt`
105+
/// - throws: `Web3Error.dataError` if value associated with key are unable
106+
/// to be initialized as `BigUInt`.
107+
public func decodeHex<T: DecodableFromHex>(to type: T.Type, key: KeyedDecodingContainer<K>.Key) throws -> T {
108+
let string = try self.decode(String.self, forKey: key)
109+
guard let number = T(from: string) else { throw Web3Error.dataError }
110+
return number
111+
}
112+
}
113+
114+
public protocol DecodableFromHex: Decodable {
115+
init?(from hexString: String)
116+
}
117+
118+
extension Data: DecodableFromHex {
119+
public init?(from hexString: String) {
120+
self.init()
121+
guard let tmp = Self.fromHex(hexString) else { return nil }
122+
self = tmp
123+
}
124+
}
125+
126+
extension BigUInt: DecodableFromHex {
127+
public init?(from hexString: String) {
128+
self.init()
129+
guard let tmp = BigUInt(hexString.stripHexPrefix(), radix: 16) else { return nil }
130+
self = tmp
131+
}
132+
}
133+
134+
extension Date: DecodableFromHex {
135+
public init?(from hexString: String) {
136+
self.init()
137+
let stripedHexString = hexString.stripHexPrefix()
138+
guard let timestampInt = UInt64(stripedHexString, radix: 16) else { return nil }
139+
self = Date(timeIntervalSince1970: TimeInterval(timestampInt))
140+
}
96141
}
97142

98143
private extension KeyedDecodingContainer {

Sources/web3swift/EthereumAddress/EthereumAddress.swift

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,15 @@ public struct EthereumAddress: Equatable {
7272
return ret
7373
}
7474

75-
public init?(_ addressString: String, type: AddressType = .normal, ignoreChecksum: Bool = false) {
75+
public static func contractDeploymentAddress() -> EthereumAddress {
76+
return EthereumAddress("0x", type: .contractDeployment)!
77+
}
78+
}
79+
80+
/// In swift structs it's better to implement initializers in extension
81+
/// Since it's make available syntetized initializer then for free.
82+
extension EthereumAddress {
83+
public init?(_ addressString:String, type: AddressType = .normal, ignoreChecksum: Bool = false) {
7684
switch type {
7785
case .normal:
7886
guard let data = Data.fromHex(addressString) else {return nil}
@@ -108,16 +116,12 @@ public struct EthereumAddress: Equatable {
108116
}
109117
}
110118

111-
public init?(_ addressData: Data, type: AddressType = .normal) {
119+
public init?(_ addressData:Data, type: AddressType = .normal) {
112120
guard addressData.count == 20 else {return nil}
113121
self._address = addressData.toHexString().addHexPrefix()
114122
self.type = type
115123
}
116124

117-
public static func contractDeploymentAddress() -> EthereumAddress {
118-
return EthereumAddress("0x", type: .contractDeployment)!
119-
}
120-
121125
}
122126

123127
extension EthereumAddress: Hashable { }

Sources/web3swift/Transaction/EthereumTransaction.swift

Lines changed: 89 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,40 @@ import Foundation
88
import BigInt
99

1010
public struct EthereumTransaction: CustomStringConvertible {
11+
// FIXME: Add Type value https://blog.mycrypto.com/new-transaction-types-on-ethereum
1112
public var nonce: BigUInt
12-
public var gasPrice: BigUInt = BigUInt(0)
13-
public var gasLimit: BigUInt = BigUInt(0)
13+
public var gasPrice: BigUInt = 0
14+
public var gasLimit: BigUInt = 0
15+
16+
// MARK: - EIP-1559
17+
/// Value of the tip to the miner for transaction processing.
18+
///
19+
/// Full amount of this variable goes to a miner.
20+
public var maxPriorityFeePerGas: BigUInt = 0
21+
22+
/// Value of the fee for one gas unit
23+
///
24+
/// This value should be greather than sum of:
25+
/// - `Block.nextBlockBaseFeePerGas` - baseFee which will burnt during the transaction processing
26+
/// - `self.maxPriorityFeePerGas` - explicit amount of a tip to the miner of the given block which will include this transaction
27+
///
28+
/// If amount of this will be **greather** than sum of `Block.baseFeePerGas` and `maxPriorityFeePerGas`
29+
/// all exceed funds will be returned to the sender.
30+
///
31+
/// If amount of this will be **lower** than sum of `Block.baseFeePerGas` and `maxPriorityFeePerGas`
32+
/// miner will recieve amount of the follow equation: `maxFeePerGas - Block.baseFeePerGas` if any,
33+
/// where Block is a block to which transaction will be included.
34+
public var maxFeePerGas: BigUInt = 0
35+
1436
// The destination address of the message, left undefined for a contract-creation transaction.
1537
public var to: EthereumAddress
1638
// (optional) The value transferred for the transaction in wei, also the endowment if it’s a contract-creation transaction.
1739
// TODO - split EthereumTransaction to two classes: with optional and required value property, depends on type of transaction
1840
public var value: BigUInt?
1941
public var data: Data
20-
public var v: BigUInt = BigUInt(1)
21-
public var r: BigUInt = BigUInt(0)
22-
public var s: BigUInt = BigUInt(0)
42+
public var v: BigUInt = 1
43+
public var r: BigUInt = 0
44+
public var s: BigUInt = 0
2345
var chainID: BigUInt? = nil
2446

2547
public var inferedChainID: BigUInt? {
@@ -33,93 +55,69 @@ public struct EthereumTransaction: CustomStringConvertible {
3355
}
3456
}
3557
}
36-
37-
public var intrinsicChainID: BigUInt? {
38-
get {
39-
return self.chainID
40-
}
41-
}
42-
58+
59+
public var intrinsicChainID: BigUInt? { chainID }
60+
4361
public mutating func UNSAFE_setChainID(_ chainID: BigUInt?) {
4462
self.chainID = chainID
4563
}
4664

4765
public var hash: Data? {
4866
var encoded: Data
4967
let inferedChainID = self.inferedChainID
50-
if inferedChainID != nil {
51-
guard let enc = self.self.encode(forSignature: false, chainID: inferedChainID) else {return nil}
68+
if let inferedChainID = inferedChainID {
69+
guard let enc = self.self.encode(forSignature: false, chainID: inferedChainID) else { return nil }
5270
encoded = enc
5371
} else {
54-
guard let enc = self.self.encode(forSignature: false, chainID: self.chainID) else {return nil}
72+
guard let enc = self.self.encode(forSignature: false, chainID: chainID) else { return nil }
5573
encoded = enc
5674
}
5775
let hash = encoded.sha3(.keccak256)
5876
return hash
5977
}
60-
61-
public init(gasPrice: BigUInt, gasLimit: BigUInt, to: EthereumAddress, value: BigUInt, data: Data) {
62-
self.nonce = BigUInt(0)
63-
self.gasPrice = gasPrice
64-
self.gasLimit = gasLimit
65-
self.value = value
66-
self.data = data
67-
self.to = to
68-
}
69-
70-
public init (nonce: BigUInt, gasPrice: BigUInt, gasLimit: BigUInt, to: EthereumAddress, value: BigUInt, data: Data, v: BigUInt, r: BigUInt, s: BigUInt) {
71-
self.nonce = nonce
72-
self.gasPrice = gasPrice
73-
self.gasLimit = gasLimit
74-
self.to = to
75-
self.value = value
76-
self.data = data
77-
self.v = v
78-
self.r = r
79-
self.s = s
80-
}
81-
78+
8279
public var description: String {
83-
get {
84-
var toReturn = ""
85-
toReturn = toReturn + "Transaction" + "\n"
86-
toReturn = toReturn + "Nonce: " + String(self.nonce) + "\n"
87-
toReturn = toReturn + "Gas price: " + String(self.gasPrice) + "\n"
88-
toReturn = toReturn + "Gas limit: " + String(describing: self.gasLimit) + "\n"
89-
toReturn = toReturn + "To: " + self.to.address + "\n"
90-
toReturn = toReturn + "Value: " + String(self.value ?? "nil") + "\n"
91-
toReturn = toReturn + "Data: " + self.data.toHexString().addHexPrefix().lowercased() + "\n"
92-
toReturn = toReturn + "v: " + String(self.v) + "\n"
93-
toReturn = toReturn + "r: " + String(self.r) + "\n"
94-
toReturn = toReturn + "s: " + String(self.s) + "\n"
95-
toReturn = toReturn + "Intrinsic chainID: " + String(describing: self.chainID) + "\n"
96-
toReturn = toReturn + "Infered chainID: " + String(describing: self.inferedChainID) + "\n"
97-
toReturn = toReturn + "sender: " + String(describing: self.sender?.address) + "\n"
98-
toReturn = toReturn + "hash: " + String(describing: self.hash?.toHexString().addHexPrefix()) + "\n"
99-
return toReturn
100-
}
101-
80+
var toReturn = ""
81+
toReturn += "Transaction" + "\n"
82+
toReturn += "Nonce: " + String(self.nonce) + "\n"
83+
toReturn += "Gas price: " + String(self.gasPrice) + "\n"
84+
toReturn += "Gas limit: " + String(describing: self.gasLimit) + "\n"
85+
toReturn += "Max priority fee per gas: " + String(describing: self.maxPriorityFeePerGas)
86+
toReturn += "Max fee per gas: " + String(describing: maxFeePerGas)
87+
toReturn += "To: " + self.to.address + "\n"
88+
toReturn += "Value: " + String(self.value ?? "nil") + "\n"
89+
toReturn += "Data: " + self.data.toHexString().addHexPrefix().lowercased() + "\n"
90+
toReturn += "v: " + String(self.v) + "\n"
91+
toReturn += "r: " + String(self.r) + "\n"
92+
toReturn += "s: " + String(self.s) + "\n"
93+
toReturn += "Intrinsic chainID: " + String(describing:self.chainID) + "\n"
94+
toReturn += "Infered chainID: " + String(describing:self.inferedChainID) + "\n"
95+
toReturn += "sender: " + String(describing: self.sender?.address) + "\n"
96+
toReturn += "hash: " + String(describing: self.hash?.toHexString().addHexPrefix()) + "\n"
97+
return toReturn
10298
}
99+
103100
public var sender: EthereumAddress? {
104-
get {
105-
guard let publicKey = self.recoverPublicKey() else {return nil}
106-
return Web3.Utils.publicToAddress(publicKey)
107-
}
101+
guard let publicKey = self.recoverPublicKey() else { return nil }
102+
return Web3.Utils.publicToAddress(publicKey)
108103
}
109104

110105
public func recoverPublicKey() -> Data? {
111-
if (self.r == BigUInt(0) && self.s == BigUInt(0)) {
112-
return nil
113-
}
114-
var normalizedV: BigUInt = BigUInt(27)
106+
// FIXME: AND not OR condition
107+
guard r != 0, s != 0 else { return nil }
108+
// if (self.r == 0 && self.s == 0) {
109+
// return nil
110+
// }
111+
var normalizedV: BigUInt = 27
115112
let inferedChainID = self.inferedChainID
116-
var d = BigUInt(0)
113+
var d: BigUInt = 0
114+
117115
if self.v >= 35 && self.v <= 38 {
118-
d = BigUInt(35)
116+
d = 35
119117
} else if self.v >= 31 && self.v <= 34 {
120-
d = BigUInt(31)
118+
d = 31
121119
} else if self.v >= 27 && self.v <= 30 {
122-
d = BigUInt(27)
120+
d = 27
123121
}
124122
if let testID = self.chainID, testID != BigUInt(0) && self.v >= (d + testID + testID) {
125123
normalizedV = self.v - d - testID - testID
@@ -146,22 +144,16 @@ public struct EthereumTransaction: CustomStringConvertible {
146144
}
147145

148146
public var txhash: String? {
149-
get {
150-
guard self.sender != nil else {return nil}
151-
guard let hash = self.hash else {return nil}
152-
let txid = hash.toHexString().addHexPrefix().lowercased()
153-
return txid
154-
}
155-
}
156-
157-
public var txid: String? {
158-
get {
159-
return self.txhash
160-
}
147+
guard sender != nil else { return nil }
148+
guard let hash = hash else { return nil }
149+
let txid = hash.toHexString().addHexPrefix().lowercased()
150+
return txid
161151
}
162-
152+
153+
public var txid: String? { txhash }
154+
163155
public func encode(forSignature: Bool = false, chainID: BigUInt? = nil) -> Data? {
164-
if (forSignature) {
156+
if forSignature {
165157
if chainID != nil {
166158
let fields = [self.nonce, self.gasPrice, self.gasLimit, self.to.addressData, self.value!, self.data, chainID!, BigUInt(0), BigUInt(0)] as [AnyObject]
167159
return RLP.encode(fields)
@@ -286,12 +278,12 @@ public struct EthereumTransaction: CustomStringConvertible {
286278
}
287279
}
288280

289-
public extension EthereumTransaction {
281+
extension EthereumTransaction {
290282
init(to: EthereumAddress, data: Data, options: TransactionOptions) {
291283
let defaults = TransactionOptions.defaultOptions
292284
let merged = defaults.merge(options)
293-
self.nonce = BigUInt(0)
294-
285+
nonce = 0
286+
295287
if let gP = merged.gasPrice {
296288
switch gP {
297289
case .manual(let value):
@@ -318,6 +310,18 @@ public extension EthereumTransaction {
318310
self.data = data
319311
}
320312

313+
}
314+
315+
public extension EthereumTransaction {
316+
init(gasPrice: BigUInt, gasLimit: BigUInt, to: EthereumAddress, value: BigUInt, data: Data) {
317+
self.nonce = BigUInt(0)
318+
self.gasPrice = gasPrice
319+
self.gasLimit = gasLimit
320+
self.value = value
321+
self.data = data
322+
self.to = to
323+
}
324+
321325
func mergedWithOptions(_ options: TransactionOptions) -> EthereumTransaction {
322326
var tx = self
323327

Sources/web3swift/Web3/Web3+Constants.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,21 @@
77
//
88

99
import Foundation
10+
import BigInt
1011

1112
struct Constants {
1213
static let infuraHttpScheme = ".infura.io/v3/"
1314
static let infuraWsScheme = ".infura.io/ws/v3/"
1415
static let infuraToken = "4406c3acf862426c83991f1752c46dd8"
1516
}
17+
18+
extension Web3 {
19+
static let GasLimitBoundDivisor: BigUInt = 1024 // The bound divisor of the gas limit, used in update calculations.
20+
static let MinGasLimit: BigUInt = 5000 // Minimum the gas limit may ever be.
21+
static let MaxGasLimit: BigUInt = 0x7fffffffffffffff // Maximum the gas limit (2^63-1).
22+
static let GenesisGasLimit: BigUInt = 4712388 // Gas limit of the Genesis block.
23+
24+
static let BaseFeeChangeDenominator: BigUInt = 8 // Bounds the amount the base fee can change between blocks.
25+
static let ElasticityMultiplier: BigUInt = 2 // Bounds the maximum gas limit an EIP-1559 block may have.
26+
static let InitialBaseFee: BigUInt = 1000000000 // Initial base fee for EIP-1559 blocks.
27+
}

0 commit comments

Comments
 (0)