Skip to content

Commit

Permalink
fix: eip1559 type transaction signing and sending (#147)
Browse files Browse the repository at this point in the history
  • Loading branch information
koraykoska authored Nov 17, 2022
1 parent 58da054 commit 1fa4b47
Show file tree
Hide file tree
Showing 7 changed files with 336 additions and 46 deletions.
38 changes: 28 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ targets: [
.target(
name: "MyProject",
dependencies: [
.product(name: "Web3", package: "Web3"),
.product(name: "Web3PromiseKit", package: "Web3"),
.product(name: "Web3ContractABI", package: "Web3"),
.product(name: "Web3", package: "Web3.swift"),
.product(name: "Web3PromiseKit", package: "Web3.swift"),
.product(name: "Web3ContractABI", package: "Web3.swift"),
]
),
.testTarget(
Expand Down Expand Up @@ -333,12 +333,16 @@ let myPrivateKey = try EthereumPrivateKey(hexPrivateKey: "...")
firstly {
web3.eth.getTransactionCount(address: myPrivateKey.address, block: .latest)
}.then { nonce in
try contract.transfer(to: EthereumAddress(hex: "0x3edB3b95DDe29580FFC04b46A68a31dD46106a4a", eip55: true), value: 100000).createTransaction(
try contract.transfer(to: EthereumAddress(hex: "0x3edB3b95DDe29580FFC04b46A68a31dD46106a4a", eip55: true), value: 100000).createTransaction(
nonce: nonce,
gasPrice: EthereumQuantity(quantity: 21.gwei),
maxFeePerGas: nil,
maxPriorityFeePerGas: nil,
gasLimit: 100000,
from: myPrivateKey.address,
value: 0,
gas: 100000,
gasPrice: EthereumQuantity(quantity: 21.gwei)
accessList: [:],
transactionType: .legacy
)!.sign(with: myPrivateKey).promise
}.then { tx in
web3.eth.sendRawTransaction(transaction: tx)
Expand All @@ -353,12 +357,16 @@ let myAddress = try EthereumAddress(hex: "0x1f04ef7263804fafb839f0d04e2b5a6a1a57
firstly {
web3.eth.getTransactionCount(address: myAddress, block: .latest)
}.then { nonce in
try contract.transfer(to: EthereumAddress(hex: "0x3edB3b95DDe29580FFC04b46A68a31dD46106a4a", eip55: true), value: 100000).send(
try contract.transfer(to: EthereumAddress(hex: "0x3edB3b95DDe29580FFC04b46A68a31dD46106a4a", eip55: true), value: 100000).send(
nonce: nonce,
gasPrice: EthereumQuantity(quantity: 21.gwei),
maxFeePerGas: nil,
maxPriorityFeePerGas: nil,
gasLimit: 150000,
from: myAddress,
value: 0,
gas: 150000,
gasPrice: EthereumQuantity(quantity: 21.gwei)
accessList: [:],
transactionType: .legacy
)
}.done { txHash in
print(txHash)
Expand Down Expand Up @@ -394,7 +402,17 @@ firstly {

// Send some tokens to another address (locally signing the transaction)
let myPrivateKey = try EthereumPrivateKey(hexPrivateKey: "...")
guard let transaction = contract["transfer"]?(EthereumAddress.testAddress, BigUInt(100000)).createTransaction(nonce: 0, from: myPrivateKey.address, value: 0, gas: 150000, gasPrice: EthereumQuantity(quantity: 21.gwei)) else {
guard let transaction = contract["transfer"]?(EthereumAddress.testAddress, BigUInt(100000)).createTransaction(
nonce: 0,
gasPrice: EthereumQuantity(quantity: 21.gwei),
maxFeePerGas: nil,
maxPriorityFeePerGas: nil,
gasLimit: 150000,
from: myPrivateKey.address,
value: 0,
accessList: [:],
transactionType: .legacy
)) else {
return
}
let signedTx = try transaction.sign(with: myPrivateKey)
Expand Down
213 changes: 194 additions & 19 deletions Sources/ContractABI/Contract/SolidityInvocation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//

import Foundation
import Collections
#if !Web3CocoaPods
import Web3
#endif
Expand All @@ -32,13 +33,34 @@ public protocol SolidityInvocation {
func createCall() -> EthereumCall?

/// Generates an EthereumTransaction object
func createTransaction(nonce: EthereumQuantity?, from: EthereumAddress, value: EthereumQuantity?, gas: EthereumQuantity, gasPrice: EthereumQuantity?) -> EthereumTransaction?
func createTransaction(
nonce: EthereumQuantity?,
gasPrice: EthereumQuantity?,
maxFeePerGas: EthereumQuantity?,
maxPriorityFeePerGas: EthereumQuantity?,
gasLimit: EthereumQuantity?,
from: EthereumAddress?,
value: EthereumQuantity?,
accessList: OrderedDictionary<EthereumAddress, [EthereumData]>,
transactionType: EthereumTransaction.TransactionType
) -> EthereumTransaction?

/// Read data from the blockchain. Only available for constant functions.
func call(block: EthereumQuantityTag, completion: @escaping ([String: Any]?, Error?) -> Void)

/// Write data to the blockchain. Only available for non-constant functions.
func send(nonce: EthereumQuantity?, from: EthereumAddress, value: EthereumQuantity?, gas: EthereumQuantity, gasPrice: EthereumQuantity?, completion: @escaping (EthereumData?, Error?) -> Void)
func send(
nonce: EthereumQuantity?,
gasPrice: EthereumQuantity?,
maxFeePerGas: EthereumQuantity?,
maxPriorityFeePerGas: EthereumQuantity?,
gasLimit: EthereumQuantity?,
from: EthereumAddress,
value: EthereumQuantity?,
accessList: OrderedDictionary<EthereumAddress, [EthereumData]>,
transactionType: EthereumTransaction.TransactionType,
completion: @escaping (EthereumData?, Error?) -> Void
)

/// Estimate how much gas is needed to execute this transaction.
func estimateGas(from: EthereumAddress?, gas: EthereumQuantity?, value: EthereumQuantity?, completion: @escaping (EthereumQuantity?, Error?) -> Void)
Expand Down Expand Up @@ -78,7 +100,18 @@ public struct SolidityReadInvocation: SolidityInvocation {
handler.call(call, outputs: outputs, block: block, completion: completion)
}

public func send(nonce: EthereumQuantity? = nil, from: EthereumAddress, value: EthereumQuantity?, gas: EthereumQuantity, gasPrice: EthereumQuantity?, completion: @escaping (EthereumData?, Error?) -> Void) {
public func send(
nonce: EthereumQuantity?,
gasPrice: EthereumQuantity?,
maxFeePerGas: EthereumQuantity?,
maxPriorityFeePerGas: EthereumQuantity?,
gasLimit: EthereumQuantity?,
from: EthereumAddress,
value: EthereumQuantity?,
accessList: OrderedDictionary<EthereumAddress, [EthereumData]>,
transactionType: EthereumTransaction.TransactionType,
completion: @escaping (EthereumData?, Error?) -> Void
) {
completion(nil, InvocationError.invalidInvocation)
}

Expand All @@ -88,7 +121,17 @@ public struct SolidityReadInvocation: SolidityInvocation {
return EthereumCall(from: nil, to: to, gas: nil, gasPrice: nil, value: nil, data: data)
}

public func createTransaction(nonce: EthereumQuantity?, from: EthereumAddress, value: EthereumQuantity?, gas: EthereumQuantity, gasPrice: EthereumQuantity?) -> EthereumTransaction? {
public func createTransaction(
nonce: EthereumQuantity?,
gasPrice: EthereumQuantity?,
maxFeePerGas: EthereumQuantity?,
maxPriorityFeePerGas: EthereumQuantity?,
gasLimit: EthereumQuantity?,
from: EthereumAddress?,
value: EthereumQuantity?,
accessList: OrderedDictionary<EthereumAddress, [EthereumData]>,
transactionType: EthereumTransaction.TransactionType
) -> EthereumTransaction? {
return nil
}

Expand All @@ -109,11 +152,34 @@ public struct SolidityPayableInvocation: SolidityInvocation {
self.parameters = zip(parameters, method.inputs).map { SolidityWrappedValue(value: $0, type: $1.type) }
self.handler = handler
}

public func createTransaction(nonce: EthereumQuantity? = nil, from: EthereumAddress, value: EthereumQuantity?, gas: EthereumQuantity, gasPrice: EthereumQuantity?) -> EthereumTransaction? {

public func createTransaction(
nonce: EthereumQuantity? = nil,
gasPrice: EthereumQuantity? = nil,
maxFeePerGas: EthereumQuantity? = nil,
maxPriorityFeePerGas: EthereumQuantity? = nil,
gasLimit: EthereumQuantity? = nil,
from: EthereumAddress? = nil,
value: EthereumQuantity? = nil,
accessList: OrderedDictionary<EthereumAddress, [EthereumData]> = [:],
transactionType: EthereumTransaction.TransactionType = .legacy
) -> EthereumTransaction? {
guard let data = encodeABI() else { return nil }
guard let to = handler.address else { return nil }
return EthereumTransaction(nonce: nonce, gasPrice: gasPrice, gasLimit: gas, from: from, to: to, value: value ?? 0, data: data)

return EthereumTransaction(
nonce: nonce,
gasPrice: gasPrice,
maxFeePerGas: maxFeePerGas,
maxPriorityFeePerGas: maxPriorityFeePerGas,
gasLimit: gasLimit,
from: from,
to: to,
value: value,
data: data,
accessList: accessList,
transactionType: transactionType
)
}

public func createCall() -> EthereumCall? {
Expand All @@ -124,12 +190,33 @@ public struct SolidityPayableInvocation: SolidityInvocation {
completion(nil, InvocationError.invalidInvocation)
}

public func send(nonce: EthereumQuantity? = nil, from: EthereumAddress, value: EthereumQuantity?, gas: EthereumQuantity, gasPrice: EthereumQuantity?, completion: @escaping (EthereumData?, Error?) -> Void) {
public func send(
nonce: EthereumQuantity? = nil,
gasPrice: EthereumQuantity? = nil,
maxFeePerGas: EthereumQuantity? = nil,
maxPriorityFeePerGas: EthereumQuantity? = nil,
gasLimit: EthereumQuantity? = nil,
from: EthereumAddress,
value: EthereumQuantity? = nil,
accessList: OrderedDictionary<EthereumAddress, [EthereumData]> = [:],
transactionType: EthereumTransaction.TransactionType = .legacy,
completion: @escaping (EthereumData?, Error?) -> Void
) {
guard handler.address != nil else {
completion(nil, InvocationError.contractNotDeployed)
return
}
guard let transaction = createTransaction(nonce: nonce, from: from, value: value, gas: gas, gasPrice: gasPrice) else {
guard let transaction = createTransaction(
nonce: nonce,
gasPrice: gasPrice,
maxFeePerGas: maxFeePerGas,
maxPriorityFeePerGas: maxPriorityFeePerGas,
gasLimit: gasLimit,
from: from,
value: value,
accessList: accessList,
transactionType: transactionType
) else {
completion(nil, InvocationError.encodingError)
return
}
Expand All @@ -152,10 +239,33 @@ public struct SolidityNonPayableInvocation: SolidityInvocation {
self.handler = handler
}

public func createTransaction(nonce: EthereumQuantity? = nil, from: EthereumAddress, value: EthereumQuantity?, gas: EthereumQuantity, gasPrice: EthereumQuantity?) -> EthereumTransaction? {
public func createTransaction(
nonce: EthereumQuantity? = nil,
gasPrice: EthereumQuantity? = nil,
maxFeePerGas: EthereumQuantity? = nil,
maxPriorityFeePerGas: EthereumQuantity? = nil,
gasLimit: EthereumQuantity? = nil,
from: EthereumAddress? = nil,
value: EthereumQuantity? = nil,
accessList: OrderedDictionary<EthereumAddress, [EthereumData]> = [:],
transactionType: EthereumTransaction.TransactionType = .legacy
) -> EthereumTransaction? {
guard let data = encodeABI() else { return nil }
guard let to = handler.address else { return nil }
return EthereumTransaction(nonce: nonce, gasPrice: gasPrice, gasLimit: gas, from: from, to: to, value: value ?? 0, data: data)

return EthereumTransaction(
nonce: nonce,
gasPrice: gasPrice,
maxFeePerGas: maxFeePerGas,
maxPriorityFeePerGas: maxPriorityFeePerGas,
gasLimit: gasLimit,
from: from,
to: to,
value: value,
data: data,
accessList: accessList,
transactionType: transactionType
)
}

public func createCall() -> EthereumCall? {
Expand All @@ -166,12 +276,33 @@ public struct SolidityNonPayableInvocation: SolidityInvocation {
completion(nil, InvocationError.invalidInvocation)
}

public func send(nonce: EthereumQuantity? = nil, from: EthereumAddress, value: EthereumQuantity?, gas: EthereumQuantity, gasPrice: EthereumQuantity?, completion: @escaping (EthereumData?, Error?) -> Void) {
public func send(
nonce: EthereumQuantity? = nil,
gasPrice: EthereumQuantity? = nil,
maxFeePerGas: EthereumQuantity? = nil,
maxPriorityFeePerGas: EthereumQuantity? = nil,
gasLimit: EthereumQuantity? = nil,
from: EthereumAddress,
value: EthereumQuantity? = nil,
accessList: OrderedDictionary<EthereumAddress, [EthereumData]> = [:],
transactionType: EthereumTransaction.TransactionType = .legacy,
completion: @escaping (EthereumData?, Error?) -> Void
) {
guard handler.address != nil else {
completion(nil, InvocationError.contractNotDeployed)
return
}
guard let transaction = createTransaction(nonce: nonce, from: from, value: value, gas: gas, gasPrice: gasPrice) else {
guard let transaction = createTransaction(
nonce: nonce,
gasPrice: gasPrice,
maxFeePerGas: maxFeePerGas,
maxPriorityFeePerGas: maxPriorityFeePerGas,
gasLimit: gasLimit,
from: from,
value: value,
accessList: accessList,
transactionType: transactionType
) else {
completion(nil, InvocationError.encodingError)
return
}
Expand Down Expand Up @@ -225,18 +356,62 @@ public struct SolidityConstructorInvocation {
self.handler = handler
self.payable = payable
}

public func createTransaction(nonce: EthereumQuantity? = nil, from: EthereumAddress, value: EthereumQuantity = 0, gas: EthereumQuantity, gasPrice: EthereumQuantity?) -> EthereumTransaction? {

public func createTransaction(
nonce: EthereumQuantity? = nil,
gasPrice: EthereumQuantity? = nil,
maxFeePerGas: EthereumQuantity? = nil,
maxPriorityFeePerGas: EthereumQuantity? = nil,
gasLimit: EthereumQuantity? = nil,
from: EthereumAddress? = nil,
value: EthereumQuantity? = nil,
accessList: OrderedDictionary<EthereumAddress, [EthereumData]> = [:],
transactionType: EthereumTransaction.TransactionType = .legacy
) -> EthereumTransaction? {
guard let data = encodeABI() else { return nil }
return EthereumTransaction(nonce: nonce, gasPrice: gasPrice, gasLimit: gas, from: from, to: nil, value: value, data: data)

return EthereumTransaction(
nonce: nonce,
gasPrice: gasPrice,
maxFeePerGas: maxFeePerGas,
maxPriorityFeePerGas: maxPriorityFeePerGas,
gasLimit: gasLimit,
from: from,
to: nil,
value: value,
data: data,
accessList: accessList,
transactionType: transactionType
)
}

public func send(nonce: EthereumQuantity? = nil, from: EthereumAddress, value: EthereumQuantity = 0, gas: EthereumQuantity, gasPrice: EthereumQuantity?, completion: @escaping (EthereumData?, Error?) -> Void) {
guard payable == true || value == 0 else {
public func send(
nonce: EthereumQuantity? = nil,
gasPrice: EthereumQuantity? = nil,
maxFeePerGas: EthereumQuantity? = nil,
maxPriorityFeePerGas: EthereumQuantity? = nil,
gasLimit: EthereumQuantity? = nil,
from: EthereumAddress,
value: EthereumQuantity? = nil,
accessList: OrderedDictionary<EthereumAddress, [EthereumData]> = [:],
transactionType: EthereumTransaction.TransactionType = .legacy,
completion: @escaping (EthereumData?, Error?) -> Void
) {
guard payable == true || value == nil || value == 0 else {
completion(nil, InvocationError.invalidInvocation)
return
}
guard let transaction = createTransaction(nonce: nonce, from: from, value: value, gas: gas, gasPrice: gasPrice) else {
guard let transaction = createTransaction(
nonce: nonce,
gasPrice: gasPrice,
maxFeePerGas: maxFeePerGas,
maxPriorityFeePerGas: maxPriorityFeePerGas,
gasLimit: gasLimit,
from: from,
value: value,
accessList: accessList,
transactionType: transactionType
) else {
completion(nil, InvocationError.encodingError)
return
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/Core/Web3/Web3.swift
Original file line number Diff line number Diff line change
Expand Up @@ -305,12 +305,12 @@ public struct Web3 {
public func sendRawTransaction(
transaction: EthereumSignedTransaction,
response: @escaping Web3ResponseCompletion<EthereumData>
) {
let req = BasicRPCRequest(
) throws {
let req = try BasicRPCRequest(
id: properties.rpcId,
jsonrpc: Web3.jsonrpc,
method: "eth_sendRawTransaction",
params: [transaction.rlp()]
params: [transaction.rawTransaction()]
)

properties.provider.send(request: req, response: response)
Expand Down
Loading

0 comments on commit 1fa4b47

Please sign in to comment.