Skip to content

Commit f483098

Browse files
Merge pull request #509 from mloit/experimental/EIP-1559-combined
2 parents 41337df + 11ab291 commit f483098

27 files changed

+2725
-679
lines changed

Sources/web3swift/Contract/EthereumContract.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,15 +114,18 @@ public struct EthereumContract: ContractProtocol {
114114
} else if extraData != Data() {
115115
fullData.append(extraData)
116116
}
117-
let transaction = EthereumTransaction(gasPrice: BigUInt(0), gasLimit: BigUInt(0), to: to, value: BigUInt(0), data: fullData)
117+
let params = EthereumParameters(gasLimit: BigUInt(0), gasPrice: BigUInt(0))
118+
let transaction = EthereumTransaction(to: to, value: BigUInt(0), data: fullData, parameters: params)
118119
return transaction
119120
}
120121

121122
public func method(_ method: String = "fallback", parameters: [AnyObject] = [AnyObject](), extraData: Data = Data()) -> EthereumTransaction? {
122123
guard let to = self.address else {return nil}
123124

124125
if (method == "fallback") {
125-
let transaction = EthereumTransaction(gasPrice: BigUInt(0), gasLimit: BigUInt(0), to: to, value: BigUInt(0), data: extraData)
126+
let params = EthereumParameters(gasLimit: BigUInt(0), gasPrice: BigUInt(0))
127+
let transaction = EthereumTransaction(to: to, value: BigUInt(0), data: extraData, parameters: params)
128+
126129
return transaction
127130
}
128131
let foundMethod = self.methods.filter { (key, value) -> Bool in
@@ -131,7 +134,8 @@ public struct EthereumContract: ContractProtocol {
131134
guard foundMethod.count == 1 else {return nil}
132135
let abiMethod = foundMethod[method]
133136
guard let encodedData = abiMethod?.encodeParameters(parameters) else {return nil}
134-
let transaction = EthereumTransaction(gasPrice: BigUInt(0), gasLimit: BigUInt(0), to: to, value: BigUInt(0), data: encodedData)
137+
let params = EthereumParameters(gasLimit: BigUInt(0), gasPrice: BigUInt(0))
138+
let transaction = EthereumTransaction(to: to, value: BigUInt(0), data: encodedData, parameters: params)
135139
return transaction
136140
}
137141

Sources/web3swift/Convenience/Decodable+Extensions.swift

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,11 @@ extension KeyedDecodingContainer {
101101

102102
/// Decodes a value of the given key from Hex to `DecodableFromHex`
103103
///
104-
/// Currently this method supports only `Data.Type`, `BigUInt.Type`, `Date.Type`
104+
/// Currently this method supports only `Data.Type`, `BigUInt.Type`, `Date.Type`, `UInt.Type`
105105
///
106106
/// - Parameter type: Generic type `T` wich conforms to `DecodableFromHex` protocol
107107
/// - Parameter key: The key that the decoded value is associated with.
108-
/// - Returns: A decoded value of type `BigUInt`
108+
/// - Returns: A decoded value of type `T`
109109
/// - throws: `Web3Error.dataError` if value associated with key are unable to be initialized as `DecodableFromHex`.
110110
public func decodeHex<T: DecodableFromHex>(_ type: T.Type, forKey: KeyedDecodingContainer<K>.Key) throws -> T {
111111
let hexString = try self.decode(String.self, forKey: forKey)
@@ -115,11 +115,11 @@ extension KeyedDecodingContainer {
115115

116116
/// Decodes a value of the given key from Hex to `[DecodableFromHex]`
117117
///
118-
/// Currently this method supports only `Data.Type`, `BigUInt.Type`, `Date.Type`
118+
/// Currently this method supports only `Data.Type`, `BigUInt.Type`, `Date.Type`, `UInt.Type`
119119
///
120120
/// - Parameter type: Array of a generic type `T` wich conforms to `DecodableFromHex` protocol
121121
/// - Parameter key: The key that the decoded value is associated with.
122-
/// - Returns: A decoded value of type `BigUInt`
122+
/// - Returns: A decoded value of type `T`
123123
/// - throws: `Web3Error.dataError` if value associated with key are unable to be initialized as `[[DecodableFromHex]]`.
124124
public func decodeHex<T: DecodableFromHex>(_ type: [T].Type, forKey: KeyedDecodingContainer<K>.Key) throws -> [T] {
125125
var container = try nestedUnkeyedContainer(forKey: forKey)
@@ -129,17 +129,31 @@ extension KeyedDecodingContainer {
129129

130130
/// Decodes a value of the given key from Hex to `[DecodableFromHex]`
131131
///
132-
/// Currently this method supports only `Data.Type`, `BigUInt.Type`, `Date.Type`, `EthereumAddress`
132+
/// Currently this method supports only `Data.Type`, `BigUInt.Type`, `Date.Type`, `EthereumAddress`, `UInt.Type`
133133
///
134134
/// - Parameter type: Array of a generic type `T` wich conforms to `DecodableFromHex` protocol
135135
/// - Parameter key: The key that the decoded value is associated with.
136-
/// - Returns: A decoded value of type `BigUInt`
136+
/// - Returns: A decoded value of type `T`
137137
/// - throws: `Web3Error.dataError` if value associated with key are unable to be initialized as `[[DecodableFromHex]]`.
138138
public func decodeHex<T: DecodableFromHex>(_ type: [[T]].Type, forKey: KeyedDecodingContainer<K>.Key) throws -> [[T]] {
139139
var container = try nestedUnkeyedContainer(forKey: forKey)
140140
guard let array = try? container.decodeHex(type) else { throw Web3Error.dataError }
141141
return array
142142
}
143+
144+
/// Decodes a value of the given key from Hex to `DecodableFromHex`
145+
///
146+
/// Currently this method supports only `Data.Type`, `BigUInt.Type`, `Date.Type`, `UInt.Type`
147+
///
148+
/// - Parameter type: Generic type `T` wich conforms to `DecodableFromHex` protocol
149+
/// - Parameter key: The key that the decoded value is associated with.
150+
/// - Returns: A decoded value of type `T`, or nil if key is not present
151+
/// - throws: `Web3Error.dataError` if value associated with key are unable to be initialized as `DecodableFromHex`.
152+
public func decodeHexIfPresent<T: DecodableFromHex>(_ type: T.Type, forKey: KeyedDecodingContainer<K>.Key) throws -> T? {
153+
guard contains(forKey) else { return nil }
154+
return try decodeHex(type, forKey: forKey)
155+
}
156+
143157
}
144158

145159
public extension UnkeyedDecodingContainer {
@@ -193,6 +207,12 @@ extension Data: DecodableFromHex {
193207
}
194208
}
195209

210+
extension UInt: DecodableFromHex {
211+
public init?(fromHex hexString: String) {
212+
self.init(hexString.stripHexPrefix(), radix: 16)
213+
}
214+
}
215+
196216
extension BigUInt: DecodableFromHex {
197217
public init?(fromHex hexString: String) {
198218
self.init(hexString.stripHexPrefix(), radix: 16)

Sources/web3swift/HookedFunctions/Web3+BrowserFunctions.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,10 @@ extension web3.BrowserFunctions {
134134
var options = opts
135135
guard let _ = options.from else {return (nil, nil)}
136136
let gasPrice = try self.web3.eth.getGasPrice()
137-
transaction.gasPrice = gasPrice
137+
transaction.parameters.gasPrice = gasPrice
138138
options.gasPrice = .manual(gasPrice)
139139
guard let gasEstimate = self.estimateGas(transaction, transactionOptions: options) else {return (nil, nil)}
140-
transaction.gasLimit = gasEstimate
140+
transaction.parameters.gasLimit = gasEstimate
141141
options.gasLimit = .limited(gasEstimate)
142142
print(transaction)
143143
return (transaction, options)
@@ -176,18 +176,18 @@ extension web3.BrowserFunctions {
176176
guard let noncePolicy = transactionOptions.nonce else {return nil}
177177
switch gasPricePolicy {
178178
case .manual(let gasPrice):
179-
transaction.gasPrice = gasPrice
179+
transaction.parameters.gasPrice = gasPrice
180180
default:
181181
let gasPrice = try self.web3.eth.getGasPrice()
182-
transaction.gasPrice = gasPrice
182+
transaction.parameters.gasPrice = gasPrice
183183
}
184184

185185
switch gasLimitPolicy {
186186
case .manual(let gasLimit):
187-
transaction.gasLimit = gasLimit
187+
transaction.parameters.gasLimit = gasLimit
188188
default:
189189
let gasLimit = try self.web3.eth.estimateGas(transaction, transactionOptions: transactionOptions)
190-
transaction.gasLimit = gasLimit
190+
transaction.parameters.gasLimit = gasLimit
191191
}
192192

193193
switch noncePolicy {
@@ -205,7 +205,7 @@ extension web3.BrowserFunctions {
205205
guard let keystore = keystoreManager.walletForAddress(from) else {return nil}
206206
try Web3Signer.signTX(transaction: &transaction, keystore: keystore, account: from, password: password)
207207
print(transaction)
208-
let signedData = transaction.encode(forSignature: false, chainID: nil)?.toHexString().addHexPrefix()
208+
let signedData = transaction.encode()?.toHexString().addHexPrefix()
209209
return signedData
210210
} catch {
211211
return nil

Sources/web3swift/Promises/Promise+Web3+Eth+SendRawTransaction.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import PromiseKit
99

1010
extension web3.Eth {
1111
public func sendRawTransactionPromise(_ transaction: Data) -> Promise<TransactionSendingResult> {
12-
guard let deserializedTX = EthereumTransaction.fromRaw(transaction) else {
12+
guard let deserializedTX = EthereumTransaction(rawValue: transaction) else {
1313
let promise = Promise<TransactionSendingResult>.pending()
1414
promise.resolver.reject(Web3Error.processingError(desc: "Serialized TX is invalid"))
1515
return promise.promise
@@ -18,7 +18,6 @@ extension web3.Eth {
1818
}
1919

2020
public func sendRawTransactionPromise(_ transaction: EthereumTransaction) -> Promise<TransactionSendingResult>{
21-
// print(transaction)
2221
let queue = web3.requestDispatcher.queue
2322
do {
2423
guard let request = EthereumTransaction.createRawTransaction(transaction: transaction) else {

Sources/web3swift/SwiftRLP/RLP.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ public struct RLP {
113113
return encoded.bytes[0]
114114
}
115115

116+
// FIXME: Make encode generic to avoid casting it's argument to [AnyObject]
116117
public static func encode(_ elements: Array<AnyObject>) -> Data? {
117118
var encodedData = Data()
118119
for e in elements {
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
// Package: web3swift
2+
// Created by Alex Vlasov.
3+
// Copyright © 2018 Alex Vlasov. All rights reserved.
4+
//
5+
// Support for EIP-2718 by Mark Loit 2022
6+
7+
import Foundation
8+
import BigInt
9+
10+
/*
11+
AbstractEnvelope is the main protocol definition to enable support for different transaction types.
12+
it defines the basic parameters and methods required by all transaction types.
13+
Other than `Legacy` (untyped transaction) and `EIP2718Envelope`, no other transaction type should inherit directly
14+
from AbstractEnvelope. All typed transactions should inherit from `EIP2718Envelope` instead, as it provides default
15+
implementations for some required methods.
16+
17+
Adding a new transaction type in the future should be as straight forward as adding to the TransactionType enum here
18+
then creating a new struct that implements to `EIP2718Envelope`, and implementing the required elements for the type
19+
Finally adding the type specific inits to the factory routines in `EnvelopeFactory` so that objectts of the new type
20+
will get generated when `EthereumTransaction` is being created with data for the new type
21+
*/
22+
23+
/// Enumeration for supported transaction types
24+
public enum TransactionType: UInt, CustomStringConvertible, CaseIterable {
25+
26+
/// For untyped and type 0 transactions EIP155 and older
27+
case legacy
28+
29+
/// For type 1 transactions conforming to EIP2930
30+
case eip2930
31+
32+
/// For type 2 transactions conforming to EIP1559
33+
case eip1559
34+
35+
/// range-checking value, not a valid type, will never be returned as a type
36+
// case total // always keep immediately after last valid type
37+
// should there be a need to handle an unknown type, place it after total
38+
39+
public var description: String {
40+
switch self {
41+
case .legacy: return "Legacy" // legacy is a pseudo-type, no EIP-2718 transaction will ever be encoded with type = 0
42+
// though nodes do appear to return a type of 0 for legacy transactions in their JSON
43+
case .eip2930: return "EIP-2930"
44+
case .eip1559: return "EIP-1559"
45+
}
46+
}
47+
}
48+
49+
/// Encoding selector for transaction transmission/hashing or signing
50+
public enum EncodeType {
51+
52+
/// Encode the transaction for transmission or hashing
53+
case transaction
54+
55+
/// Encode the transaction for signing
56+
case signature
57+
}
58+
59+
/// Protocol definition for all transaction envelope types
60+
/// All envelopes must conform to this protocol to work with `EthereumTransaction`
61+
/// each implememtation holds all the type specific data
62+
/// and implments the type specific encoding/decoding
63+
public protocol AbstractEnvelope: CustomStringConvertible { // possibly add Codable?
64+
65+
/// The type of transaction this envelope represents
66+
var type: TransactionType { get }
67+
68+
// MARK: common parameters for any transaction
69+
/// the nonce value for the transaction
70+
var nonce: BigUInt { get set }
71+
72+
/// On chain address that this transaction is being sent to
73+
var to: EthereumAddress { get set }
74+
75+
// /// The native value of the transaction in Wei
76+
// var value: BigUInt { get set }
77+
78+
// /// Any encoded data accompanying the transaction
79+
// var data: Data { get set }
80+
81+
// Signature data should not be set directly
82+
/// signature V compoonent
83+
var v: BigUInt { get set }
84+
85+
/// signature R compoonent
86+
var r: BigUInt { get set }
87+
88+
/// signature S compoonent
89+
var s: BigUInt { get set }
90+
91+
/// Transaction Parameters object
92+
/// used to provide external access to the otherwise
93+
/// protected parameters of the object
94+
var parameters: EthereumParameters { get set }
95+
96+
// required initializers
97+
// for Decodable support
98+
/// initializer for creating an `EthereumTransaction` with the Decodable protocol
99+
/// will return an new `EthereumTransaction` object on success
100+
/// thows a `Web3.dataError` if an error occurs while trying to decode a value
101+
/// returns nil if a required field is not found in the decoder stream
102+
init?(from decoder: Decoder) throws // Decodable Protocol
103+
104+
// initializes from a raw stream of bytes
105+
// can fail if input stream is not of the right size/cannot be decoded
106+
/// initializer for creating an `EthereumTransaction` with raw bytestream data
107+
/// will return an new `EthereumTransaction` object on success
108+
/// returns nil if a required field is not found in the decoder stream, or can't be decoded
109+
init?(rawValue: Data) // Decode from Ethereum Data
110+
111+
// pseudo memberwise initializer
112+
// accepts all common parameters (full description with default implementation below)
113+
// Note the `nil` parameters, even though non-optional in the struct, are there to allow
114+
// fallback to pulling the value from `options`. If options also does not have a value, a suitable default is used
115+
// precedence is as follows: direct parameter > options value > default value
116+
/// Default memberwse initializer that all envelopes must support
117+
/// - Parameters:
118+
/// - to: EthereumAddress of destination
119+
/// - nonce: nonce for the transaction
120+
/// - v: Signature V component
121+
/// - r: Signature R component
122+
/// - s: Signature S component
123+
/// - parameters: EthereumParameters struct containing any other required parameters
124+
init(to: EthereumAddress, nonce: BigUInt?, v: BigUInt, r: BigUInt, s: BigUInt, parameters: EthereumParameters?)
125+
126+
/// Applies the passed options to the transaction envelope
127+
/// - Parameters:
128+
/// - {default}: TransactionOptions struct
129+
mutating func applyOptions(_ options: TransactionOptions)
130+
131+
/// Transaction encoder for transmission or signing
132+
/// - Parameters:
133+
/// - {default}: EncodeType enum
134+
/// - when type is .signature the transaction is encoded for signing
135+
/// - when type is .transaction the thransaction is encoded for hashing or transmission to the blockchain
136+
/// - Returns: a raw encoding stream representing the transaction, encoded according to it's type
137+
func encode(for type: EncodeType) -> Data?
138+
139+
/// Encodes the transaction as a set of strings for JSON transmission
140+
/// - Returns: A TransactionParameters object containg all the parameters for the transaction
141+
func encodeAsDictionary(from: EthereumAddress?) -> TransactionParameters?
142+
143+
/// used by the signing algorithm to set the v, r, s parameters
144+
/// - Parameters:
145+
/// - {default}: UnmarshalledSignature struct containing the v, r, s parameters
146+
mutating func setUnmarshalledSignatureData(_ unmarshalledSignature: SECP256K1.UnmarshaledSignature)
147+
148+
/// used by the public-key recovery algorithm for determining the signer of the transaction
149+
/// - Returns: returns the 'unmarshalled' v,r,s psignatrure parameters
150+
func getUnmarshalledSignatureData() -> SECP256K1.UnmarshaledSignature?
151+
152+
/// used by Transaction.unsign() to reset/clear the signature
153+
mutating func clearSignatureData()
154+
}
155+
156+
public extension AbstractEnvelope {
157+
158+
mutating func clearSignatureData() {
159+
self.v = 1
160+
self.r = 0
161+
self.s = 0
162+
}
163+
}

0 commit comments

Comments
 (0)