-
Notifications
You must be signed in to change notification settings - Fork 17
/
tx_hash.go
160 lines (131 loc) · 4.11 KB
/
tx_hash.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
package types
import (
"encoding/base32"
"encoding/hex"
"fmt"
"strings"
"github.com/mr-tron/base58"
)
const (
suiMinTxHashLen = 43
suiMaxTxHashLen = 44
algorandTxHashLen = 52
wormholeMinTxHashLen = 64
wormholeMaxTxHashLen = 66
solanaMinTxHashLen = 87
solanaMaxTxHashLen = 88
)
// TxHash represents a transaction hash passed by query params.
type TxHash struct {
hash string
isWormhole bool
isSolana bool
}
// ParseTxHash parses a transaction hash from a string.
//
// The transaction hash can be provided in different formats,
// depending on the blockchain it belongs to:
// * Solana: 64 bytes, encoded as base58.
// * All other chains: 32 bytes, encoded as hex.
//
// More cases could be added in the future as needed.
func ParseTxHash(value string) (*TxHash, error) {
// Solana txHashes are 64 bytes long, encoded as base58.
if len(value) >= solanaMinTxHashLen && len(value) <= solanaMaxTxHashLen {
return parseSolanaTxHash(value)
}
// Algorand txHashes are 32 bytes long, encoded as base32.
if len(value) == algorandTxHashLen {
return parseAlgorandTxHash(value)
}
// Sui txHashes are 32 bytes long, encoded as base32.
if len(value) >= suiMinTxHashLen && len(value) <= suiMaxTxHashLen {
return parseSuiTxHash(value)
}
// Wormhole txHashes are 32 bytes long, encoded as hex.
// Optionally, they can be prefixed with "0x" or "0X".
if len(value) >= wormholeMinTxHashLen && len(value) <= wormholeMaxTxHashLen {
return parseWormholeTxHash(value)
}
return nil, fmt.Errorf("invalid txHash length: %d", len(value))
}
func parseSolanaTxHash(value string) (*TxHash, error) {
// Decode the string from base58 to binary
bytes, err := base58.Decode(value)
if err != nil {
return nil, fmt.Errorf("failed to decode solana txHash from base58: %w", err)
}
// Make sure we have the expected amount of bytes
if len(bytes) != 64 {
return nil, fmt.Errorf("solana txHash must be exactly 64 bytes, but got %d bytes", len(bytes))
}
// Populate the result struct and return
result := TxHash{
hash: base58.Encode(bytes),
isSolana: true,
}
return &result, nil
}
func parseAlgorandTxHash(value string) (*TxHash, error) {
// Decode the string from base32 to binary
bytes, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(value)
if err != nil {
return nil, fmt.Errorf("failed to decode algorand txHash from base32: %w", err)
}
// Make sure we have the expected amount of bytes
if len(bytes) != 32 {
return nil, fmt.Errorf("algorand txHash must be exactly 32 bytes, but got %d bytes", len(bytes))
}
// Populate the result struct and return
result := TxHash{
hash: base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(bytes),
isWormhole: true,
}
return &result, nil
}
func parseSuiTxHash(value string) (*TxHash, error) {
// Decode the string from base58 to binary
bytes, err := base58.Decode(value)
if err != nil {
return nil, fmt.Errorf("failed to decode sui txHash from base58: %w", err)
}
// Make sure we have the expected amount of bytes
if len(bytes) != 32 {
return nil, fmt.Errorf("sui txHash must be exactly 32 bytes, but got %d bytes", len(bytes))
}
// Populate the result struct and return
result := TxHash{
hash: base58.Encode(bytes),
isWormhole: true,
}
return &result, nil
}
func parseWormholeTxHash(value string) (*TxHash, error) {
// Trim any preceding "0x" to the address
value = strings.TrimPrefix(value, "0x")
value = strings.TrimPrefix(value, "0X")
// Decode the string from hex to binary
bytes, err := hex.DecodeString(value)
if err != nil {
return nil, fmt.Errorf("failed to decode txHash from hex: %w", err)
}
// Make sure we have the expected amount of bytes
if len(bytes) != 32 {
return nil, fmt.Errorf("wormhole txHash must be exactly 32 bytes, but got %d bytes", len(bytes))
}
// Populate the result struct and return
result := TxHash{
hash: hex.EncodeToString(bytes),
isWormhole: true,
}
return &result, nil
}
func (h *TxHash) IsSolanaTxHash() bool {
return h.isSolana
}
func (h *TxHash) IsWormholeTxHash() bool {
return h.isWormhole
}
func (h *TxHash) String() string {
return h.hash
}