This repository has been archived by the owner on Oct 31, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
/
main.go
207 lines (178 loc) · 7.63 KB
/
main.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
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"math/big"
"os"
"time"
"github.com/alecthomas/kingpin"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/rpc"
"github.com/linki/etherdelta-go/contract"
)
const (
version = "v0.3.0"
defaultEndpoint = "https://mainnet.infura.io"
defaultEtherDelta = "0x8d12A197cB00D4747a1fe03395095ce2A5CC6819"
defaultTokenRegistry = "0x926a74c5C36adf004C87399e65f75628b0f98D2C"
defaultTimeout = "5s"
defaultGasPrice = "1000000000"
defaultGasLimit = "100000"
)
var (
endpoint string
etherDeltaAddress string
tokenRegistryAddress string
keyStorePath string
passphrase string
withdrawAll bool
gasPrice int64
gasLimit uint64
timeout time.Duration
)
// KeyStore is a subset of an Ethereum KeyStore file for parsing the Address only.
type KeyStore struct {
Address string `json:"address"`
}
func init() {
kingpin.Flag("keystore-file", "The owner's Keystore file location (required)").Required().ExistingFileVar(&keyStorePath)
kingpin.Flag("withdraw-all", "Withdraw all deposited tokens (optional, default: false)").BoolVar(&withdrawAll)
kingpin.Flag("passphrase", "The Keystore file's passphrase (optional, required when using --withdraw-all)").StringVar(&passphrase)
kingpin.Flag("endpoint", "Ethereum RPC endpoint (optional, default: https://mainnet.infura.io)").Default(defaultEndpoint).StringVar(&endpoint)
kingpin.Flag("etherdelta", "EtherDelta contract address (optional, default: 0x8d12A197cB00D4747a1fe03395095ce2A5CC6819)").Default(defaultEtherDelta).StringVar(ðerDeltaAddress)
kingpin.Flag("token-registry", "TokenRegistry contract address (optional, default: 0x926a74c5C36adf004C87399e65f75628b0f98D2C)").Default(defaultTokenRegistry).StringVar(&tokenRegistryAddress)
kingpin.Flag("gas-price", "The gas price in wei (optional, default: 1000000000, i.e. 1 Gwei)").Default(defaultGasPrice).Int64Var(&gasPrice)
kingpin.Flag("gas-limit", "The gas limit (optional, default: 100000)").Default(defaultGasLimit).Uint64Var(&gasLimit)
kingpin.Flag("timeout", "The timeout to submit a transaction to the Ethereum endpoint (optional, default: 5 seconds)").Default(defaultTimeout).DurationVar(&timeout)
}
func main() {
// Parse command line flags.
kingpin.Version(version)
kingpin.Parse()
// When balances should be withdrawn, a passphrase to unlock the keystore file is required.
if withdrawAll && passphrase == "" {
log.Fatalf("--passphrase is mandatory when using --withdraw-all.")
}
// Create an Ethereum client connecting to the provided RPC endpoint.
c, err := rpc.DialHTTP(endpoint)
if err != nil {
log.Fatalf("Failed to connect to the Ethereum network: %v", err)
}
conn := ethclient.NewClient(c)
// Create an instance of the TokenRegistry contract at the specific address.
tokenRegistry, err := contract.NewTokenRegistry(common.HexToAddress(tokenRegistryAddress), conn)
if err != nil {
log.Fatalf("Failed to create TokenRegistry contract instance: %v", err)
}
// Fetch the list of registered tokens from the token registry.
tokens, err := tokenRegistry.GetTokenAddresses(nil)
if err != nil {
log.Fatalf("Failed to fetch registered tokens: %v", err)
}
fmt.Println("Found tokens:", len(tokens))
// Create an instance of the EtherDelta contract at the specific address.
etherDelta, err := contract.NewEtherDelta(common.HexToAddress(etherDeltaAddress), conn)
if err != nil {
log.Fatalf("Failed to create EtherDelta contract instance: %v", err)
}
// Open the provided Keystore file.
keyStoreFile, err := os.OpenFile(keyStorePath, os.O_RDONLY, 0400)
if err != nil {
log.Fatalf("Failed to open Keystore file: %v", err)
}
defer keyStoreFile.Close()
// Read the Keystore file's JSON content.
keyStoreJSON, err := ioutil.ReadAll(keyStoreFile)
if err != nil {
log.Fatalf("Failed to parse Keystore file: %v", err)
}
// Parse the content for the Address field.
keyStore := KeyStore{}
if err = json.Unmarshal(keyStoreJSON, &keyStore); err != nil {
log.Fatalf("Failed to parse Keystore file: %v", err)
}
ownerAddress := common.HexToAddress(keyStore.Address)
fmt.Println("Your address:", ownerAddress.Hex())
// EtherDelta manages the ETH deposits as "Token at address 0".
ethTokenAddress := common.Address{}
// Retrieve the owner's deposited ETH balance.
balance, err := etherDelta.BalanceOf(nil, ethTokenAddress, ownerAddress)
if err != nil {
log.Fatalf("Failed to retrieve balance: %v", err)
}
fmt.Printf("Deposited ETH: %s\n", balance)
// If withdrawal is enabled and there's something to withdraw, withdraw it.
if withdrawAll && balance.Cmp(common.Big0) == 1 {
// Withdraw the entire deposited balance.
fmt.Println("Withdrawing ETH:", balance)
// Add a timeout to submitting the transaction.
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
// The transaction needs additional metadata, e.g. the private key for
// authorization as well as gas price and limit.
opts, err := bind.NewTransactor(bytes.NewReader(keyStoreJSON), passphrase)
if err != nil {
log.Fatalf("Failed to create Transactor from Keystore file: %v", err)
}
opts.Context = ctx
opts.GasLimit = gasLimit
opts.GasPrice = big.NewInt(gasPrice)
// Execute the Withdraw function of the EtherDelta contract given the required
// arguments: the amount. Also provide the metadata created above in order to
// successfully create and sign the transaction.
tx, err := etherDelta.Withdraw(opts, balance)
if err == nil {
// Print the hash of the submitted transaction for tracking.
fmt.Println("Transaction hash:", tx.Hash().String())
} else {
log.Printf("Failed to withdraw ETH: %v", err)
}
}
// Go through each token known to the TokenRegistry and try to withdraw any balance.
for _, token := range tokens {
// Get the token's symbol for display later.
_, _, symbol, _, _, _, err := tokenRegistry.GetTokenMetaData(nil, token)
if err != nil {
log.Fatalf("Failed to fetch token metadata: %v", err)
}
// Retrieve the owner's deposited balance of the token.
balance, err := etherDelta.BalanceOf(nil, token, ownerAddress)
if err != nil {
log.Fatalf("Failed to retrieve balance: %v", err)
}
fmt.Printf("Deposited %s: %s\n", symbol, balance)
// If withdrawal is enabled and there's something to withdraw, withdraw it.
if withdrawAll && balance.Cmp(common.Big0) == 1 {
// Withdraw the entire deposited balance.
fmt.Printf("Withdrawing %s: %s\n", symbol, balance)
// Add a timeout to submitting the transaction.
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
// The transaction needs additional metadata, e.g. the private key for
// authorization as well as gas price and limit.
opts, err := bind.NewTransactor(bytes.NewReader(keyStoreJSON), passphrase)
if err != nil {
log.Fatalf("Failed to create Transactor from Keystore file: %v", err)
}
opts.Context = ctx
opts.GasLimit = gasLimit
opts.GasPrice = big.NewInt(gasPrice)
// Execute the WithdrawToken function of the EtherDelta contract given the
// required arguments: the token address and amount. Also provide the metadata
// created above in order to successfully create and sign the transaction.
tx, err := etherDelta.WithdrawToken(opts, token, balance)
if err == nil {
// Print the hash of the submitted transaction for tracking.
fmt.Println("Transaction hash:", tx.Hash().String())
} else {
log.Printf("Failed to withdraw %s: %v", symbol, err)
}
}
}
}