-
Notifications
You must be signed in to change notification settings - Fork 1
/
transaction.go
234 lines (194 loc) · 6.29 KB
/
transaction.go
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
package zrx
import (
"encoding/json"
"errors"
"fmt"
"math/big"
"strings"
"github.com/0xProject/0x-mesh/ethereum"
"github.com/0xProject/0x-mesh/ethereum/signer"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/signer/core"
"github.com/ParadigmFoundation/go-eth"
)
// ZeroExProtocolName is the EIP-712 domain name of the 0x protocol
const ZeroExProtocolName = "0x Protocol"
// ZeroExProtocolVersion is the EIP-712 domain version of the 0x protocol
const ZeroExProtocolVersion = "3.0.0"
// Transaction represents 0x transaction (see ZEIP-18)
type Transaction struct {
Salt *big.Int
ExpirationTimeSeconds *big.Int
GasPrice *big.Int
SignerAddress common.Address
Data []byte
hash *common.Hash
}
// MarshalJSON implements json.Marshaler
func (tx *Transaction) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]string{
"salt": tx.Salt.String(),
"expirationTimeSeconds": tx.ExpirationTimeSeconds.String(),
"gasPrice": tx.GasPrice.String(),
"signerAddress": strings.ToLower(tx.SignerAddress.Hex()),
"data": hexutil.Encode(tx.Data),
})
}
// UnmarshalJSON implements json.Unmarshaler
func (tx *Transaction) UnmarshalJSON(data []byte) error {
var raw transactionJSON
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
if err := tx.fromJSON(&raw); err != nil {
return err
}
return nil
}
// ComputeHashForChainID calculates the 0x transaction hash for the provided chain ID.
// See https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md#hashing-a-transaction
func (tx *Transaction) ComputeHashForChainID(chainID int) (common.Hash, error) {
if tx.hash != nil {
return *tx.hash, nil
}
contractAddresses, err := ethereum.GetContractAddressesForChainID(chainID)
if err != nil {
return common.Hash{}, err
}
evmChainID := math.NewHexOrDecimal256(int64(chainID))
domain := core.TypedDataDomain{
Name: ZeroExProtocolName,
Version: ZeroExProtocolVersion,
ChainId: evmChainID,
VerifyingContract: contractAddresses.Exchange.Hex(),
}
typedData := core.TypedData{
Types: EIP712Types,
PrimaryType: TypeZeroExTransaction,
Domain: domain,
Message: tx.Map(),
}
domainSeparator, err := typedData.HashStruct(TypeEIP712Domain, typedData.Domain.Map())
if err != nil {
return common.Hash{}, err
}
typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
if err != nil {
return common.Hash{}, err
}
rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash)))
hashBytes := eth.Keccak256(rawData)
hash := common.BytesToHash(hashBytes)
tx.hash = &hash
return hash, nil
}
// Map returns the transaction as an un-typed map (useful when hashing)
func (tx *Transaction) Map() map[string]interface{} {
return map[string]interface{}{
"salt": tx.Salt.String(),
"expirationTimeSeconds": tx.ExpirationTimeSeconds.String(),
"gasPrice": tx.GasPrice.String(),
"signerAddress": tx.SignerAddress.Hex(),
"data": tx.Data,
}
}
// ResetHash returns the cached transaction hash to nil
func (tx *Transaction) ResetHash() {
tx.hash = nil
}
// set a 0x transaction values from a JSON representation
func (tx *Transaction) fromJSON(ztx *transactionJSON) error {
salt, ok := new(big.Int).SetString(ztx.Salt, 10)
if !ok {
return errors.New(`unable to unmarshal value for "salt"`)
}
expirationTimeSeconds, ok := new(big.Int).SetString(ztx.ExpirationTimeSeconds, 10)
if !ok {
return errors.New(`unable to unmarshal value for "expirationTimeSeconds"`)
}
gasPrice, ok := new(big.Int).SetString(ztx.GasPrice, 10)
if !ok {
return errors.New(`unable to unmarshal value for "gasPrice"`)
}
tx.Salt = salt
tx.ExpirationTimeSeconds = expirationTimeSeconds
tx.GasPrice = gasPrice
tx.SignerAddress = common.HexToAddress(ztx.SignerAddress)
if ztx.Data[:2] == "0x" {
tx.Data = common.Hex2Bytes(ztx.Data[2:])
} else {
tx.Data = common.Hex2Bytes(ztx.Data)
}
data, err := hexutil.Decode(ztx.Data)
if err != nil {
return err
}
tx.Data = data
return nil
}
// SignedTransaction represents a signed 0x transaction
type SignedTransaction struct {
Transaction
Signature []byte
}
// MarshalJSON implements json.Marshaler
func (stx *SignedTransaction) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]string{
"salt": stx.Salt.String(),
"expirationTimeSeconds": stx.ExpirationTimeSeconds.String(),
"gasPrice": stx.GasPrice.String(),
"signerAddress": strings.ToLower(stx.SignerAddress.Hex()),
"data": hexutil.Encode(stx.Data),
"signature": hexutil.Encode(stx.Signature),
})
}
// UnmarshalJSON implements json.Unmarshaler
func (stx *SignedTransaction) UnmarshalJSON(data []byte) error {
var raw transactionJSON
var rawStx signedTransactionJSON
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
if err := json.Unmarshal(data, &rawStx); err != nil {
return err
}
if err := stx.fromJSON(&raw); err != nil {
return err
}
sig, err := hexutil.Decode(rawStx.Signature)
if err != nil {
return err
}
stx.Signature = sig
return nil
}
// used to assist in un-marshalling 0x transactions
type transactionJSON struct {
Salt string `json:"salt"`
ExpirationTimeSeconds string `json:"expirationTimeSeconds"`
GasPrice string `json:"gasPrice"`
SignerAddress string `json:"signerAddress"`
Data string `json:"data"`
}
// used to assist in un-marshalling 0x transactions
type signedTransactionJSON struct {
transactionJSON
Signature string `json:"signature"`
}
// SignTransaction signs the 0x transaction with the supplied Signer
func SignTransaction(signer signer.Signer, tx *Transaction, chainID int) (*SignedTransaction, error) {
hash, err := tx.ComputeHashForChainID(chainID)
if err != nil {
return nil, err
}
ecSignature, err := signer.EthSign(hash.Bytes(), tx.SignerAddress)
if err != nil {
return nil, err
}
return &SignedTransaction{
Transaction: *tx,
Signature: ECSignatureToBytes(ecSignature),
}, nil
}