1- import BigInt
21import CryptoSwift
32import Foundation
3+ import BigInt
44import Core
55
6- // TODO: Refactor me
7-
8- struct EIP712Domain : EIP712DomainHashable {
9- let chainId : EIP712 . UInt256 ?
10- let verifyingContract : EIP712 . Address
11- }
12-
13- protocol EIP712DomainHashable : EIP712Hashable { }
14-
15- public struct SafeTx : EIP712Hashable {
16- let to : EIP712 . Address
17- let value : EIP712 . UInt256
18- let data : EIP712 . Bytes
19- let operation : EIP712 . UInt8
20- let safeTxGas : EIP712 . UInt256
21- let baseGas : EIP712 . UInt256
22- let gasPrice : EIP712 . UInt256
23- let gasToken : EIP712 . Address
24- let refundReceiver : EIP712 . Address
25- let nonce : EIP712 . UInt256
26-
27- 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 ) {
28- self . to = to
29- self . value = value
30- self . data = data
31- self . operation = operation
32- self . safeTxGas = safeTxGas
33- self . baseGas = baseGas
34- self . gasPrice = gasPrice
35- self . gasToken = gasToken
36- self . refundReceiver = refundReceiver
37- self . nonce = nonce
38- }
39-
40- }
41-
426/// Protocol defines EIP712 struct encoding
437public protocol EIP712Hashable {
448 var typehash : Data { get }
@@ -52,27 +16,90 @@ public class EIP712 {
5216 public typealias Bytes = Data
5317}
5418
19+ public struct EIP712Domain : EIP712Hashable {
20+ public let chainId : EIP712 . UInt256 ?
21+ public let verifyingContract : EIP712 . Address
22+ public init ( chainId: EIP712 . UInt256 ? , verifyingContract: EIP712 . Address ) {
23+ self . chainId = chainId
24+ self . verifyingContract = verifyingContract
25+ }
26+ }
27+
5528public extension EIP712 . Address {
5629 static var zero : Self {
5730 EthereumAddress ( Data ( count: 20 ) ) !
5831 }
5932}
6033
34+ // MARK: - Default implementation for EIP712Hashable
35+ public extension EIP712Hashable {
36+ var typehash : Data {
37+ Data ( encodeType ( ) . bytes) . sha3 ( . keccak256)
38+ }
39+
40+ func hash( ) throws -> Data {
41+ typealias SolidityValue = ( value: Any , type: ABI . Element . ParameterType )
42+ var parameters : [ Data ] = [ typehash]
43+ for case let ( _, field) in Mirror ( reflecting: self ) . children {
44+ let result : Data
45+ switch field {
46+ case let string as String :
47+ result = Data ( string. bytes) . sha3 ( . keccak256)
48+ case let data as EIP712 . Bytes :
49+ result = data. sha3 ( . keccak256)
50+ case is EIP712 . UInt8 :
51+ result = ABIEncoder . encodeSingleType ( type: . uint( bits: 8 ) , value: field as AnyObject ) !
52+ case is EIP712 . UInt256 :
53+ result = ABIEncoder . encodeSingleType ( type: . uint( bits: 256 ) , value: field as AnyObject ) !
54+ case is EIP712 . Address :
55+ result = ABIEncoder . encodeSingleType ( type: . address, value: field as AnyObject ) !
56+ case let hashable as EIP712Hashable :
57+ result = try hashable. hash ( )
58+ default :
59+ if ( field as AnyObject ) is NSNull {
60+ continue
61+ } else {
62+ preconditionFailure ( " Not solidity type " )
63+ }
64+ }
65+ guard result. count == 32 else { preconditionFailure ( " ABI encode error " ) }
66+ parameters. append ( result)
67+ }
68+ return Data ( parameters. flatMap { $0. bytes } ) . sha3 ( . keccak256)
69+ }
70+ }
71+
72+ public func eip712encode( domainSeparator: EIP712Hashable , message: EIP712Hashable ) throws -> Data {
73+ let data = try Data ( [ UInt8 ( 0x19 ) , UInt8 ( 0x01 ) ] ) + domainSeparator. hash ( ) + message. hash ( )
74+ return data. sha3 ( . keccak256)
75+ }
76+
77+ // MARK: - Additional private and public extensions with support members
78+
6179public extension EIP712Hashable {
62- private var name : String {
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 {
6391 let fullName = " \( Self . self) "
6492 let name = fullName. components ( separatedBy: " . " ) . last ?? fullName
6593 return name
6694 }
6795
68- private func dependencies( ) -> [ EIP712Hashable ] {
69- let dependencies = Mirror ( reflecting: self ) . children
96+ func dependencies( ) -> [ EIP712Hashable ] {
97+ Mirror ( reflecting: self ) . children
7098 . compactMap { $0. value as? EIP712Hashable }
7199 . flatMap { [ $0] + $0. dependencies ( ) }
72- return dependencies
73100 }
74101
75- private func encodePrimaryType( ) -> String {
102+ func encodePrimaryType( ) -> String {
76103 let parametrs : [ String ] = Mirror ( reflecting: self ) . children. compactMap { key, value in
77104 guard let key = key else { return nil }
78105
@@ -100,71 +127,62 @@ public extension EIP712Hashable {
100127 }
101128 return typeName + " " + key
102129 }
103- return self . name + " ( " + parametrs. joined ( separator: " , " ) + " ) "
104- }
105-
106- func encodeType( ) -> String {
107- let dependencies = self . dependencies ( ) . map { $0. encodePrimaryType ( ) }
108- let selfPrimaryType = self . encodePrimaryType ( )
109-
110- let result = Set ( dependencies) . filter { $0 != selfPrimaryType }
111- return selfPrimaryType + result. sorted ( ) . joined ( )
112- }
113-
114- // MARK: - Default implementation
115-
116- var typehash : Data {
117- keccak256 ( encodeType ( ) )
118- }
119-
120- func hash( ) throws -> Data {
121- typealias SolidityValue = ( value: Any , type: ABI . Element . ParameterType )
122- var parametrs : [ Data ] = [ self . typehash]
123- for case let ( _, field) in Mirror ( reflecting: self ) . children {
124- let result : Data
125- switch field {
126- case let string as String :
127- result = keccak256 ( string)
128- case let data as EIP712 . Bytes :
129- result = keccak256 ( data)
130- case is EIP712 . UInt8 :
131- result = ABIEncoder . encodeSingleType ( type: . uint( bits: 8 ) , value: field as AnyObject ) !
132- case is EIP712 . UInt256 :
133- result = ABIEncoder . encodeSingleType ( type: . uint( bits: 256 ) , value: field as AnyObject ) !
134- case is EIP712 . Address :
135- result = ABIEncoder . encodeSingleType ( type: . address, value: field as AnyObject ) !
136- case let hashable as EIP712Hashable :
137- result = try hashable. hash ( )
138- default :
139- if ( field as AnyObject ) is NSNull {
140- continue
141- } else {
142- preconditionFailure ( " Not solidity type " )
143- }
144- }
145- guard result. count == 32 else { preconditionFailure ( " ABI encode error " ) }
146- parametrs. append ( result)
147- }
148- let encoded = parametrs. flatMap { $0. bytes }
149- return keccak256 ( encoded)
130+ return name + " ( " + parametrs. joined ( separator: " , " ) + " ) "
150131 }
151132}
152133
153- // Encode functions
154- func eip712encode( domainSeparator: EIP712Hashable , message: EIP712Hashable ) throws -> Data {
155- let data = try Data ( [ UInt8 ( 0x19 ) , UInt8 ( 0x01 ) ] ) + domainSeparator. hash ( ) + message. hash ( )
156- return keccak256 ( data)
157- }
158-
159- // MARK: - keccak256
160- private func keccak256( _ data: [ UInt8 ] ) -> Data {
161- Data ( SHA3 ( variant: . keccak256) . calculate ( for: data) )
162- }
134+ // MARK: - Gnosis Safe Transaction model
135+
136+ /// Gnosis Safe Transaction.
137+ /// https://docs.gnosis-safe.io/tutorials/tutorial_tx_service_initiate_sign
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 {
145+ /// Checksummed address
146+ let to : EIP712 . Address
147+ /// Value in wei
148+ let value : EIP712 . UInt256
149+ /// 0x prefixed hex string
150+ let data : EIP712 . Bytes
151+ /// `0` CALL, `1` DELEGATE_CALL
152+ let operation : EIP712 . UInt8
153+ /// Max gas to use in the transaction
154+ let safeTxGas : EIP712 . UInt256
155+ /// Gast costs not related to the transaction execution (signature check, refund payment...)
156+ let baseGas : EIP712 . UInt256
157+ /// Gas price used for the refund calculation
158+ 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
161+ /// Checksummed address of receiver of gas payment (or `null` if tx.origin)
162+ let refundReceiver : EIP712 . Address
163+ /// Nonce of the Safe, transaction cannot be executed until Safe's nonce is not equal to this nonce
164+ let nonce : EIP712 . UInt256
163165
164- private func keccak256( _ string: String ) -> Data {
165- keccak256 ( Array ( string. utf8) )
166- }
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 ) {
176+ self . to = to
177+ self . value = value
178+ self . data = data
179+ self . operation = operation
180+ self . safeTxGas = safeTxGas
181+ self . baseGas = baseGas
182+ self . gasPrice = gasPrice
183+ self . gasToken = gasToken
184+ self . refundReceiver = refundReceiver
185+ self . nonce = nonce
186+ }
167187
168- private func keccak256( _ data: Data ) -> Data {
169- keccak256 ( data. bytes)
170188}
0 commit comments