Skip to content

Commit 4320e0b

Browse files
fix: a few additional updates to the EIP712.swift file and SafeTx struct
1 parent 101d995 commit 4320e0b

File tree

2 files changed

+79
-59
lines changed

2 files changed

+79
-59
lines changed

Sources/web3swift/Utils/EIP/EIP712.swift

Lines changed: 77 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -31,60 +31,8 @@ public extension EIP712.Address {
3131
}
3232
}
3333

34+
// MARK: - Default implementation for EIP712Hashable
3435
public extension EIP712Hashable {
35-
private var name: String {
36-
let fullName = "\(Self.self)"
37-
let name = fullName.components(separatedBy: ".").last ?? fullName
38-
return name
39-
}
40-
41-
private func dependencies() -> [EIP712Hashable] {
42-
Mirror(reflecting: self).children
43-
.compactMap { $0.value as? EIP712Hashable }
44-
.flatMap { [$0] + $0.dependencies() }
45-
}
46-
47-
private func encodePrimaryType() -> String {
48-
let parametrs: [String] = Mirror(reflecting: self).children.compactMap { key, value in
49-
guard let key = key else { return nil }
50-
51-
func checkIfValueIsNil(value: Any) -> Bool {
52-
let mirror = Mirror(reflecting: value)
53-
if mirror.displayStyle == .optional {
54-
if mirror.children.count == 0 {
55-
return true
56-
}
57-
}
58-
59-
return false
60-
}
61-
62-
guard !checkIfValueIsNil(value: value) else { return nil }
63-
64-
let typeName: String
65-
switch value {
66-
case is EIP712.UInt8: typeName = "uint8"
67-
case is EIP712.UInt256: typeName = "uint256"
68-
case is EIP712.Address: typeName = "address"
69-
case is EIP712.Bytes: typeName = "bytes"
70-
case let hashable as EIP712Hashable: typeName = hashable.name
71-
default: typeName = "\(type(of: value))".lowercased()
72-
}
73-
return typeName + " " + key
74-
}
75-
return self.name + "(" + parametrs.joined(separator: ",") + ")"
76-
}
77-
78-
func encodeType() -> String {
79-
let dependencies = self.dependencies().map { $0.encodePrimaryType() }
80-
let selfPrimaryType = self.encodePrimaryType()
81-
82-
let result = Set(dependencies).filter { $0 != selfPrimaryType }
83-
return selfPrimaryType + result.sorted().joined()
84-
}
85-
86-
// MARK: - Default implementation
87-
8836
var typehash: Data {
8937
Data(encodeType().bytes).sha3(.keccak256)
9038
}
@@ -126,11 +74,74 @@ public func eip712encode(domainSeparator: EIP712Hashable, message: EIP712Hashabl
12674
return data.sha3(.keccak256)
12775
}
12876

77+
// MARK: - Additional private and public extensions with support members
78+
79+
public extension EIP712Hashable {
80+
func encodeType() -> String {
81+
let dependencies = dependencies().map { $0.encodePrimaryType() }
82+
let selfPrimaryType = encodePrimaryType()
83+
84+
let result = Set(dependencies).filter { $0 != selfPrimaryType }
85+
return selfPrimaryType + result.sorted().joined()
86+
}
87+
}
88+
89+
fileprivate extension EIP712Hashable {
90+
var name: String {
91+
let fullName = "\(Self.self)"
92+
let name = fullName.components(separatedBy: ".").last ?? fullName
93+
return name
94+
}
95+
96+
func dependencies() -> [EIP712Hashable] {
97+
Mirror(reflecting: self).children
98+
.compactMap { $0.value as? EIP712Hashable }
99+
.flatMap { [$0] + $0.dependencies() }
100+
}
101+
102+
func encodePrimaryType() -> String {
103+
let parametrs: [String] = Mirror(reflecting: self).children.compactMap { key, value in
104+
guard let key = key else { return nil }
105+
106+
func checkIfValueIsNil(value: Any) -> Bool {
107+
let mirror = Mirror(reflecting: value)
108+
if mirror.displayStyle == .optional {
109+
if mirror.children.count == 0 {
110+
return true
111+
}
112+
}
113+
114+
return false
115+
}
116+
117+
guard !checkIfValueIsNil(value: value) else { return nil }
118+
119+
let typeName: String
120+
switch value {
121+
case is EIP712.UInt8: typeName = "uint8"
122+
case is EIP712.UInt256: typeName = "uint256"
123+
case is EIP712.Address: typeName = "address"
124+
case is EIP712.Bytes: typeName = "bytes"
125+
case let hashable as EIP712Hashable: typeName = hashable.name
126+
default: typeName = "\(type(of: value))".lowercased()
127+
}
128+
return typeName + " " + key
129+
}
130+
return self.name + "(" + parametrs.joined(separator: ",") + ")"
131+
}
132+
}
133+
129134
// MARK: - Gnosis Safe Transaction model
130135

131136
/// Gnosis Safe Transaction.
132137
/// https://docs.gnosis-safe.io/tutorials/tutorial_tx_service_initiate_sign
133-
public struct SafeTx: EIP712Hashable {
138+
///
139+
/// Note for web3swift developers: **DO NOT CHANGE THE ORDER OF VARIABLES**.
140+
///
141+
/// Changing the order will result in a different hash.
142+
/// Order must match the implementation of hash calculation in
143+
/// [`GnosisSafe.sol`](https://github.com/safe-global/safe-contracts/blob/main/contracts/GnosisSafe.sol#L126).
144+
public struct GnosisSafeTx: EIP712Hashable {
134145
/// Checksummed address
135146
let to: EIP712.Address
136147
/// Value in wei
@@ -139,20 +150,29 @@ public struct SafeTx: EIP712Hashable {
139150
let data: EIP712.Bytes
140151
/// `0` CALL, `1` DELEGATE_CALL
141152
let operation: EIP712.UInt8
142-
/// Token address, **must be checksummed**, (held by the Safe) to be used as a refund to the sender, if `null` is Ether
143-
let gasToken: EIP712.Address
144153
/// Max gas to use in the transaction
145154
let safeTxGas: EIP712.UInt256
146155
/// Gast costs not related to the transaction execution (signature check, refund payment...)
147156
let baseGas: EIP712.UInt256
148157
/// Gas price used for the refund calculation
149158
let gasPrice: EIP712.UInt256
159+
/// Token address, **must be checksummed**, (held by the Safe) to be used as a refund to the sender, if `null` is Ether
160+
let gasToken: EIP712.Address
150161
/// Checksummed address of receiver of gas payment (or `null` if tx.origin)
151162
let refundReceiver: EIP712.Address
152163
/// Nonce of the Safe, transaction cannot be executed until Safe's nonce is not equal to this nonce
153164
let nonce: EIP712.UInt256
154165

155-
public init(to: EIP712.Address, value: EIP712.UInt256, data: EIP712.Bytes, operation: EIP712.UInt8, safeTxGas: EIP712.UInt256, baseGas: EIP712.UInt256, gasPrice: EIP712.UInt256, gasToken: EIP712.Address, refundReceiver: EIP712.Address, nonce: EIP712.UInt256) {
166+
public init(to: EIP712.Address,
167+
value: EIP712.UInt256,
168+
data: EIP712.Bytes,
169+
operation: EIP712.UInt8,
170+
safeTxGas: EIP712.UInt256,
171+
baseGas: EIP712.UInt256,
172+
gasPrice: EIP712.UInt256,
173+
gasToken: EIP712.Address,
174+
refundReceiver: EIP712.Address,
175+
nonce: EIP712.UInt256) {
156176
self.to = to
157177
self.value = value
158178
self.data = data

Tests/web3swiftTests/localTests/EIP712Tests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class EIP712Tests: LocalTestCase {
2727
let gasToken = EthereumAddress("0x0000000000000000000000000000000000000000")!
2828
let refundReceiver = EthereumAddress("0x7c07D32e18D6495eFDC487A32F8D20daFBa53A5e")!
2929
let nonce: EIP712.UInt256 = .init(6)
30-
let safeTX = SafeTx(
30+
let safeTX = GnosisSafeTx(
3131
to: to,
3232
value: value,
3333
data: safeTxData,
@@ -78,7 +78,7 @@ class EIP712Tests: LocalTestCase {
7878
let gasToken = EthereumAddress("0x0000000000000000000000000000000000000000")!
7979
let refundReceiver = EthereumAddress("0x7c07D32e18D6495eFDC487A32F8D20daFBa53A5e")!
8080
let nonce: EIP712.UInt256 = .init(0)
81-
let safeTX = SafeTx(
81+
let safeTX = GnosisSafeTx(
8282
to: to,
8383
value: value,
8484
data: safeTxData,

0 commit comments

Comments
 (0)