-
Notifications
You must be signed in to change notification settings - Fork 169
/
Copy pathZKSyncProvider.swift
148 lines (126 loc) · 4.76 KB
/
ZKSyncProvider.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//
// web3.swift
// Copyright © 2022 Argent Labs Limited. All rights reserved.
//
import web3
import BigInt
import Logging
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
public protocol ZKSyncClientProtocol: EthereumRPCProtocol {
func eth_sendRawZKSyncTransaction(_ transaction: ZKSyncTransaction, withAccount account: EthereumAccountProtocol) async throws -> String
func gasPrice() async throws -> BigUInt
func estimateGas(_ transaction: ZKSyncTransaction) async throws -> BigUInt
}
extension ZKSyncClientProtocol {
public func eth_sendRawZKSyncTransaction(_ transaction: ZKSyncTransaction, withAccount account: EthereumAccountProtocol) async throws -> String {
// Inject pending nonce
let nonce = try await self.eth_getTransactionCount(address: account.address, block: .Pending)
var transaction = transaction
transaction.nonce = nonce
if transaction.chainId == nil {
transaction.chainId = network.intValue
}
guard let signedTx = try? account.sign(zkTransaction: transaction),
let transactionHex = signedTx.raw?.web3.hexString else {
throw EthereumClientError.encodeIssue
}
guard let txHash = try await networkProvider.send(
method: "eth_sendRawTransaction",
params: [transactionHex],
receive: String.self
) as? String else {
throw EthereumClientError.unexpectedReturnValue
}
return txHash
}
public func gasPrice() async throws -> BigUInt {
let emptyParams: [Bool] = []
guard let data = try await networkProvider.send(method: "eth_gasPrice", params: emptyParams, receive: String.self) as? String else {
throw EthereumClientError.unexpectedReturnValue
}
guard let value = BigUInt(hex: data) else {
throw EthereumClientError.unexpectedReturnValue
}
return value
}
public func estimateGas(_ transaction: ZKSyncTransaction) async throws -> BigUInt {
let value = transaction.value > .zero ? transaction.value : nil
let params = EstimateGasParams(
from: transaction.from.asString(),
to: transaction.to.asString(),
gas: transaction.gasLimit?.web3.hexString,
gasPrice: transaction.gasPrice?.web3.hexString,
value: value?.web3.hexString,
data: transaction.data.web3.hexString
)
guard let data = try await networkProvider.send(
method: "eth_estimateGas",
params: params,
receive: String.self
) as? String else {
throw EthereumClientError.unexpectedReturnValue
}
guard let value = BigUInt(hex: data) else {
throw EthereumClientError.unexpectedReturnValue
}
return value
}
}
struct EstimateGasParams: Encodable {
let from: String?
let to: String
let gas: String?
let gasPrice: String?
let value: String?
let data: String?
enum TransactionCodingKeys: String, CodingKey {
case from
case to
case gas
case gasPrice
case value
case data
}
func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
var nested = container.nestedContainer(keyedBy: TransactionCodingKeys.self)
if let from = from {
try nested.encode(from, forKey: .from)
}
try nested.encode(to, forKey: .to)
let jsonRPCAmount: (String) -> String = { amount in
amount == "0x00" ? "0x0" : amount
}
if let gas = gas.map(jsonRPCAmount) {
try nested.encode(gas, forKey: .gas)
}
if let gasPrice = gasPrice.map(jsonRPCAmount) {
try nested.encode(gasPrice, forKey: .gasPrice)
}
if let value = value.map(jsonRPCAmount) {
try nested.encode(value, forKey: .value)
}
if let data = data {
try nested.encode(data, forKey: .data)
}
}
}
public class ZKSyncClient: BaseEthereumClient, ZKSyncClientProtocol {
let networkQueue: OperationQueue
public init(
url: URL,
sessionConfig: URLSessionConfiguration = URLSession.shared.configuration,
logger: Logger? = nil,
network: EthereumNetwork
) {
let networkQueue = OperationQueue()
networkQueue.name = "web3swift.client.networkQueue"
networkQueue.maxConcurrentOperationCount = 4
self.networkQueue = networkQueue
let session = URLSession(configuration: sessionConfig, delegate: nil, delegateQueue: networkQueue)
super.init(networkProvider: HttpNetworkProvider(session: session, url: url), url: url, logger: logger, network: network)
}
}