Skip to content

Commit 9c51eca

Browse files
committed
refactor for subaccounts, binance working
1 parent c98653c commit 9c51eca

25 files changed

Lines changed: 641 additions & 246 deletions

client/account.go

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ import (
1616
// - core/margin
1717

1818
type AccountName string
19+
type AccountType string
1920

20-
var CoreFunding AccountName = "core/funding"
21-
var CoreTrading AccountName = "core/trading"
22-
var CoreMargin AccountName = "core/margin"
21+
// var CoreFunding AccountName = "core/funding"
22+
// var CoreTrading AccountName = "core/trading"
23+
// var CoreMargin AccountName = "core/margin"
2324

2425
func (name AccountName) Id() string {
2526
if strings.HasPrefix(string(name), "core/") {
@@ -33,27 +34,43 @@ func NewAccountName(id string) AccountName {
3334
}
3435

3536
type AccountTransferArgs struct {
36-
from AccountName
37-
to AccountName
37+
from AccountName
38+
to AccountName
39+
40+
fromType AccountType
41+
toType AccountType
42+
3843
symbol oc.SymbolId
3944
amount oc.Amount
4045
}
4146

42-
func NewAccountTransferArgs(from AccountName, to AccountName, symbol oc.SymbolId, amount oc.Amount) AccountTransferArgs {
47+
func NewAccountTransferArgs(symbol oc.SymbolId, amount oc.Amount) AccountTransferArgs {
4348
return AccountTransferArgs{
44-
from,
45-
to,
49+
"",
50+
"",
51+
"",
52+
"",
4653
symbol,
4754
amount,
4855
}
4956
}
5057

51-
func (args *AccountTransferArgs) GetFrom() AccountName {
52-
return args.from
58+
func (args *AccountTransferArgs) SetFrom(from AccountName, fromType AccountType) {
59+
args.from = from
60+
args.fromType = fromType
61+
}
62+
63+
func (args *AccountTransferArgs) SetTo(to AccountName, toType AccountType) {
64+
args.to = to
65+
args.toType = toType
66+
}
67+
68+
func (args *AccountTransferArgs) GetFrom() (AccountName, AccountType) {
69+
return args.from, args.fromType
5370
}
5471

55-
func (args *AccountTransferArgs) GetTo() AccountName {
56-
return args.to
72+
func (args *AccountTransferArgs) GetTo() (AccountName, AccountType) {
73+
return args.to, args.toType
5774
}
5875

5976
func (args *AccountTransferArgs) GetSymbol() oc.SymbolId {

client/balance.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
package client
22

33
type GetBalanceArgs struct {
4-
account AccountName
4+
accountType AccountType
55
}
66

7-
func NewGetBalanceArgs(account AccountName) GetBalanceArgs {
7+
func NewGetBalanceArgs(accountType AccountType) GetBalanceArgs {
88
return GetBalanceArgs{
9-
account: account,
9+
accountType: accountType,
1010
}
1111
}
1212

13-
func (args *GetBalanceArgs) GetAccount() AccountName {
14-
return args.account
13+
func (args *GetBalanceArgs) GetAccountType() AccountType {
14+
return args.accountType
1515
}

client/client.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,21 @@ type WithdrawalHistory struct {
4848
}
4949

5050
type Client interface {
51+
// List all of the support assets on the exchange
5152
ListAssets() ([]*oc.Asset, error)
53+
54+
// List of all of the balances on an account
5255
ListBalances(args GetBalanceArgs) ([]*BalanceDetail, error)
56+
57+
// Create a transfer between accounts (e.g. main to sub, sub to sub, etc) on the exchange
5358
CreateAccountTransfer(args AccountTransferArgs) (*TransferStatus, error)
59+
60+
// Withdraw funds to an external wallet
5461
CreateWithdrawal(args WithdrawalArgs) (*WithdrawalResponse, error)
62+
63+
// Get a deposit address for an asset
5564
GetDepositAddress(args GetDepositAddressArgs) (oc.Address, error)
65+
66+
// List paginated withdrawal history on an account in descending order
5667
ListWithdrawalHistory(args WithdrawalHistoryArgs) ([]*WithdrawalHistory, error)
5768
}

cmd/cmd.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package cmd
2+
3+
import (
4+
"log/slog"
5+
6+
"github.com/spf13/cobra"
7+
)
8+
9+
func SetVerbosityFromCmd(cmd *cobra.Command) {
10+
verbose, err := cmd.Flags().GetCount("verbose")
11+
if err != nil {
12+
panic(err)
13+
}
14+
SetVerbosity(verbose)
15+
}
16+
func SetVerbosity(verbose int) {
17+
switch verbose {
18+
case 0:
19+
slog.SetLogLoggerLevel(slog.LevelWarn)
20+
case 1:
21+
slog.SetLogLoggerLevel(slog.LevelInfo)
22+
case 2:
23+
slog.SetLogLoggerLevel(slog.LevelDebug)
24+
default:
25+
slog.SetLogLoggerLevel(slog.LevelDebug)
26+
}
27+
}

cmd/oc/config.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,16 @@ import (
99
)
1010

1111
func NewConfigCmd() *cobra.Command {
12+
var configPath string
1213
cmd := &cobra.Command{
1314
SilenceUsage: true,
1415
Use: "config",
1516
Short: "Print out the current config",
1617
RunE: func(cmd *cobra.Command, args []string) error {
17-
config := cmd.Context().Value(configContextKey).(*loader.Config)
18+
config, err := loader.LoadConfig(configPath)
19+
if err != nil {
20+
return err
21+
}
1822
out, err := yaml.Marshal(config)
1923
if err != nil {
2024
return err
@@ -23,5 +27,12 @@ func NewConfigCmd() *cobra.Command {
2327
return nil
2428
},
2529
}
30+
cmd.Flags().StringVarP(
31+
&configPath,
32+
"config",
33+
"c",
34+
"",
35+
fmt.Sprintf("path to the config file (may set %s)", loader.ENV_OFFCHAIN_CONFIG),
36+
)
2637
return cmd
2738
}
Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package main
1+
package exchange
22

33
import (
44
"fmt"
@@ -12,15 +12,18 @@ import (
1212
func NewAccountTransferCmd() *cobra.Command {
1313
var from string
1414
var to string
15+
var fromType string
16+
var toType string
17+
1518
var symbol string
1619
var amountS string
1720
cmd := &cobra.Command{
1821
SilenceUsage: true,
1922
Use: "transfer",
2023
Short: "Transfer funds between accounts on exchange",
2124
RunE: func(cmd *cobra.Command, args []string) error {
22-
exchangeConfig := unwrapExchangeConfig(cmd.Context())
23-
cli, err := loader.NewClient(exchangeConfig)
25+
exchangeConfig, secrets := unwrapAccountConfig(cmd.Context())
26+
cli, err := loader.NewClient(exchangeConfig.ExchangeId, &exchangeConfig.ExchangeClientConfig, secrets)
2427
if err != nil {
2528
return err
2629
}
@@ -35,21 +38,29 @@ func NewAccountTransferCmd() *cobra.Command {
3538
return fmt.Errorf("--symbol is required")
3639
}
3740

38-
resp, err := cli.CreateAccountTransfer(client.NewAccountTransferArgs(
39-
client.AccountName(from),
40-
client.AccountName(to),
41+
transferArgs := client.NewAccountTransferArgs(
42+
// client.AccountName(from),
43+
// client.AccountName(to),
4144
oc.SymbolId(symbol),
4245
amount,
43-
))
46+
)
47+
transferArgs.SetFrom(client.AccountName(from), client.AccountType(fromType))
48+
transferArgs.SetTo(client.AccountName(to), client.AccountType(toType))
49+
50+
resp, err := cli.CreateAccountTransfer(transferArgs)
4451
if err != nil {
4552
return err
4653
}
54+
4755
printJson(resp)
4856
return nil
4957
},
5058
}
51-
cmd.Flags().StringVar(&from, "from", string(client.CoreFunding), "The account to transfer from")
52-
cmd.Flags().StringVar(&to, "to", string(client.CoreTrading), "The account to transfer to")
59+
cmd.Flags().StringVar(&from, "from", "", "The account to transfer from (defaults to the main account)")
60+
cmd.Flags().StringVar(&to, "to", "", "The account to transfer to (defaults to the main account)")
61+
cmd.Flags().StringVar(&fromType, "from-type", "", "The type of account to transfer from (defaults to core-funding)")
62+
cmd.Flags().StringVar(&toType, "to-type", "", "The type of account to transfer to (defaults to core-trading)")
63+
5364
cmd.Flags().StringVar(&symbol, "symbol", "", "The symbol to transfer")
5465
cmd.Flags().StringVar(&amountS, "amount", "", "The amount to transfer")
5566
return cmd

cmd/oc/exchange/cmd.go

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package exchange
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"log/slog"
8+
9+
oc "github.com/cordialsys/offchain"
10+
"github.com/cordialsys/offchain/cmd"
11+
"github.com/cordialsys/offchain/loader"
12+
"github.com/spf13/cobra"
13+
)
14+
15+
func printJson(data interface{}) {
16+
bz, err := json.MarshalIndent(data, "", " ")
17+
if err != nil {
18+
panic(err)
19+
}
20+
fmt.Println(string(bz))
21+
}
22+
23+
type contextKey string
24+
25+
const exchangeConfigKey contextKey = "exchange_config"
26+
const exchangeAccountSecretsKey contextKey = "exchange_account_secrets"
27+
const configContextKey contextKey = "config"
28+
29+
func unwrapExchangeConfig(ctx context.Context) *oc.ExchangeConfig {
30+
return ctx.Value(exchangeConfigKey).(*oc.ExchangeConfig)
31+
}
32+
33+
func unwrapAccountSecrets(ctx context.Context) *oc.MultiSecret {
34+
return ctx.Value(exchangeAccountSecretsKey).(*oc.MultiSecret)
35+
}
36+
37+
func unwrapAccountConfig(ctx context.Context) (*oc.ExchangeConfig, *oc.MultiSecret) {
38+
exchangeConfig := unwrapExchangeConfig(ctx)
39+
secrets := unwrapAccountSecrets(ctx)
40+
return exchangeConfig, secrets
41+
}
42+
43+
func NewExchangeCmd() *cobra.Command {
44+
var configPath string
45+
var exchange string
46+
var subaccountId string
47+
cmd := &cobra.Command{
48+
Use: "exchange",
49+
Short: "Run commands on an exchange",
50+
Aliases: []string{"ex", "x", "exchanges"},
51+
SilenceUsage: true,
52+
PersistentPreRunE: func(preCmd *cobra.Command, args []string) error {
53+
cmd.SetVerbosityFromCmd(preCmd)
54+
config, err := loader.LoadConfig(configPath)
55+
if err != nil {
56+
return err
57+
}
58+
ctx := preCmd.Context()
59+
60+
if exchange == "" {
61+
return fmt.Errorf("--exchange is required")
62+
}
63+
exchangeConfig, ok := config.GetExchange(oc.ExchangeId(exchange))
64+
if !ok {
65+
return fmt.Errorf("exchange not found. options are: %v", oc.ValidExchangeIds)
66+
}
67+
slog.Info("Using exchange", "exchange", exchange)
68+
ctx = context.WithValue(ctx, exchangeConfigKey, exchangeConfig)
69+
var secrets *oc.MultiSecret
70+
71+
if subaccountId != "" {
72+
for _, subaccount := range exchangeConfig.SubAccounts {
73+
if subaccount.Id == subaccountId {
74+
secrets = &subaccount.MultiSecret
75+
}
76+
}
77+
if secrets == nil {
78+
return fmt.Errorf("subaccount %s not found in configuration for %s", subaccountId, exchange)
79+
}
80+
err = secrets.LoadSecrets()
81+
if err != nil {
82+
return fmt.Errorf("could not load secrets for %s subaccount %s: %w", exchange, subaccountId, err)
83+
}
84+
} else {
85+
secrets = &exchangeConfig.MultiSecret
86+
err = secrets.LoadSecrets()
87+
if err != nil {
88+
return fmt.Errorf("could not load secrets for %s: %w", exchange, err)
89+
}
90+
}
91+
92+
ctx = context.WithValue(ctx, configContextKey, config)
93+
ctx = context.WithValue(ctx, exchangeAccountSecretsKey, secrets)
94+
preCmd.SetContext(ctx)
95+
96+
return nil
97+
},
98+
}
99+
cmd.AddCommand(NewGetAssetsCmd())
100+
cmd.AddCommand(NewListBalancesCmd())
101+
cmd.AddCommand(NewAccountTransferCmd())
102+
cmd.AddCommand(NewWithdrawCmd())
103+
cmd.AddCommand(NewGetDepositAddressCmd())
104+
cmd.AddCommand(NewListWithdrawalHistoryCmd())
105+
cmd.AddCommand(NewListSubaccountsCmd())
106+
107+
cmd.PersistentFlags().StringVarP(
108+
&exchange,
109+
"exchange",
110+
"x",
111+
"",
112+
fmt.Sprintf("The exchange to use (%v)", oc.ValidExchangeIds),
113+
)
114+
115+
cmd.PersistentFlags().StringVarP(
116+
&subaccountId,
117+
"subaccount",
118+
"s",
119+
"",
120+
"The subaccount to use. Defaults to using the main account.",
121+
)
122+
123+
cmd.PersistentFlags().StringVarP(
124+
&configPath,
125+
"config",
126+
"c",
127+
"",
128+
fmt.Sprintf("path to the config file (may set %s)", loader.ENV_OFFCHAIN_CONFIG),
129+
)
130+
131+
return cmd
132+
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package main
1+
package exchange
22

33
import (
44
"github.com/cordialsys/offchain/loader"
@@ -11,8 +11,8 @@ func NewGetAssetsCmd() *cobra.Command {
1111
Use: "assets",
1212
Short: "list the asset symbols and networks of the exchange",
1313
RunE: func(cmd *cobra.Command, args []string) error {
14-
exchangeConfig := unwrapExchangeConfig(cmd.Context())
15-
cli, err := loader.NewClient(exchangeConfig)
14+
exchangeConfig, secrets := unwrapAccountConfig(cmd.Context())
15+
cli, err := loader.NewClient(exchangeConfig.ExchangeId, &exchangeConfig.ExchangeClientConfig, secrets)
1616
if err != nil {
1717
return err
1818
}

0 commit comments

Comments
 (0)