Skip to content

Commit 3774a79

Browse files
authored
Merge pull request #48 from renproject/feat/balance-helper-fns
Add helper functions for fetching account balances
2 parents 39234fe + 3cdd4e8 commit 3774a79

File tree

7 files changed

+135
-32
lines changed

7 files changed

+135
-32
lines changed

Diff for: api/account/account.go

+3
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ type TxBuilder interface {
6262
// The Client interface defines the functionality required to interact with a
6363
// chain over RPC.
6464
type Client interface {
65+
// AccountBalance returns the current balance of the given account.
66+
AccountBalance(context.Context, address.Address) (pack.U256, error)
67+
6568
// AccountNonce is the current nonce of this account, which must be used to
6669
// build a new transaction.
6770
AccountNonce(context.Context, address.Address) (pack.U256, error)

Diff for: chain/cosmos/client.go

+57-5
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@ const (
2525
DefaultClientTimeoutRetry = time.Second
2626
// DefaultClientHost used by the Client. This should only be used for local
2727
// deployments of the multichain.
28-
DefaultClientHost = "http://0.0.0.0:26657"
28+
DefaultClientHost = pack.String("http://0.0.0.0:26657")
2929
// DefaultBroadcastMode configures the behaviour of a cosmos client while it
3030
// interacts with the cosmos node. Allowed broadcast modes can be async, sync
3131
// and block. "async" returns immediately after broadcasting, "sync" returns
3232
// after the transaction has been checked and "block" waits until the
3333
// transaction is committed to the chain.
34-
DefaultBroadcastMode = "sync"
34+
DefaultBroadcastMode = pack.String("sync")
35+
// DefaultCoinDenom used by the Client.
36+
DefaultCoinDenom = pack.String("uluna")
3537
)
3638

3739
// ClientOptions are used to parameterise the behaviour of the Client.
@@ -40,6 +42,7 @@ type ClientOptions struct {
4042
TimeoutRetry time.Duration
4143
Host pack.String
4244
BroadcastMode pack.String
45+
CoinDenom pack.String
4346
}
4447

4548
// DefaultClientOptions returns ClientOptions with the default settings. These
@@ -49,17 +52,43 @@ func DefaultClientOptions() ClientOptions {
4952
return ClientOptions{
5053
Timeout: DefaultClientTimeout,
5154
TimeoutRetry: DefaultClientTimeoutRetry,
52-
Host: pack.String(DefaultClientHost),
53-
BroadcastMode: pack.String(DefaultBroadcastMode),
55+
Host: DefaultClientHost,
56+
BroadcastMode: DefaultBroadcastMode,
57+
CoinDenom: DefaultCoinDenom,
5458
}
5559
}
5660

57-
// WithHost sets the URL of the Bitcoin node.
61+
// WithTimeout sets the timeout used by the Client.
62+
func (opts ClientOptions) WithTimeout(timeout time.Duration) ClientOptions {
63+
opts.Timeout = timeout
64+
return opts
65+
}
66+
67+
// WithTimeoutRetry sets the timeout retry used by the Client.
68+
func (opts ClientOptions) WithTimeoutRetry(timeoutRetry time.Duration) ClientOptions {
69+
opts.TimeoutRetry = timeoutRetry
70+
return opts
71+
}
72+
73+
// WithHost sets the URL of the node.
5874
func (opts ClientOptions) WithHost(host pack.String) ClientOptions {
5975
opts.Host = host
6076
return opts
6177
}
6278

79+
// WithBroadcastMode sets the behaviour of the Client when interacting with the
80+
// underlying node.
81+
func (opts ClientOptions) WithBroadcastMode(broadcastMode pack.String) ClientOptions {
82+
opts.BroadcastMode = broadcastMode
83+
return opts
84+
}
85+
86+
// WithCoinDenom sets the coin denomination used by the Client.
87+
func (opts ClientOptions) WithCoinDenom(coinDenom pack.String) ClientOptions {
88+
opts.CoinDenom = coinDenom
89+
return opts
90+
}
91+
6392
// Client interacts with an instance of the Cosmos based network using the REST
6493
// interface exposed by a lightclient node.
6594
type Client struct {
@@ -155,3 +184,26 @@ func (client *Client) AccountNumber(_ context.Context, addr address.Address) (pa
155184

156185
return pack.U64(acc.GetAccountNumber()), nil
157186
}
187+
188+
// AccountBalance returns the account balancee for a given address.
189+
func (client *Client) AccountBalance(_ context.Context, addr address.Address) (pack.U256, error) {
190+
cosmosAddr, err := types.AccAddressFromBech32(string(addr))
191+
if err != nil {
192+
return pack.U256{}, fmt.Errorf("bad address: '%v': %v", addr, err)
193+
}
194+
195+
accGetter := auth.NewAccountRetriever(client.cliCtx)
196+
acc, err := accGetter.GetAccount(Address(cosmosAddr).AccAddress())
197+
if err != nil {
198+
return pack.U256{}, err
199+
}
200+
201+
balance := acc.GetCoins().AmountOf(string(client.opts.CoinDenom)).BigInt()
202+
203+
// If the balance exceeds `MaxU256`, return an error.
204+
if pack.MaxU256.Int().Cmp(balance) == -1 {
205+
return pack.U256{}, fmt.Errorf("balance %v for %v exceeds MaxU256", balance.String(), addr)
206+
}
207+
208+
return pack.NewU256FromInt(balance), nil
209+
}

Diff for: chain/cosmos/tx.go

+32-15
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,32 @@ import (
1919
"github.com/tendermint/tendermint/crypto/tmhash"
2020
)
2121

22-
type txBuilder struct {
23-
client *Client
24-
coinDenom pack.String
25-
chainID pack.String
26-
}
22+
const (
23+
// DefaultChainID used by the Client.
24+
DefaultChainID = pack.String("testnet")
25+
)
2726

2827
// TxBuilderOptions only contains necessary options to build tx from tx builder
2928
type TxBuilderOptions struct {
30-
ChainID pack.String `json:"chain_id"`
31-
CoinDenom pack.String `json:"coin_denom"`
29+
ChainID pack.String
30+
}
31+
32+
// DefaultTxBuilderOptions returns TxBuilderOptions with the default settings.
33+
func DefaultTxBuilderOptions() TxBuilderOptions {
34+
return TxBuilderOptions{
35+
ChainID: DefaultChainID,
36+
}
37+
}
38+
39+
// WithChainID sets the chain ID used by the transaction builder.
40+
func (opts TxBuilderOptions) WithChainID(chainID pack.String) TxBuilderOptions {
41+
opts.ChainID = chainID
42+
return opts
43+
}
44+
45+
type txBuilder struct {
46+
client *Client
47+
chainID pack.String
3248
}
3349

3450
// NewTxBuilder returns an implementation of the transaction builder interface
@@ -40,9 +56,8 @@ func NewTxBuilder(options TxBuilderOptions, client *Client) account.TxBuilder {
4056
}
4157

4258
return txBuilder{
43-
client: client,
44-
coinDenom: options.CoinDenom,
45-
chainID: options.ChainID,
59+
client: client,
60+
chainID: options.ChainID,
4661
}
4762
}
4863

@@ -63,14 +78,16 @@ func (builder txBuilder) BuildTx(ctx context.Context, from, to address.Address,
6378
sendMsg := MsgSend{
6479
FromAddress: Address(fromAddr),
6580
ToAddress: Address(toAddr),
66-
Amount: []Coin{Coin{
67-
Denom: builder.coinDenom,
68-
Amount: pack.NewU64(value.Int().Uint64()),
69-
}},
81+
Amount: []Coin{
82+
{
83+
Denom: builder.client.opts.CoinDenom,
84+
Amount: pack.NewU64(value.Int().Uint64()),
85+
},
86+
},
7087
}
7188

7289
fees := Coins{Coin{
73-
Denom: builder.coinDenom,
90+
Denom: builder.client.opts.CoinDenom,
7491
Amount: pack.NewU64(gasPrice.Mul(gasLimit).Int().Uint64()),
7592
}}
7693

Diff for: chain/filecoin/client.go

+22
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,25 @@ func (client *Client) AccountNonce(ctx context.Context, addr address.Address) (p
167167

168168
return pack.NewU256FromU64(pack.NewU64(actor.Nonce)), nil
169169
}
170+
171+
// AccountBalance returns the account balancee for a given address.
172+
func (client *Client) AccountBalance(ctx context.Context, addr address.Address) (pack.U256, error) {
173+
filAddr, err := filaddress.NewFromString(string(addr))
174+
if err != nil {
175+
return pack.U256{}, fmt.Errorf("bad address '%v': %v", addr, err)
176+
}
177+
178+
actor, err := client.node.StateGetActor(ctx, filAddr, types.NewTipSetKey(cid.Undef))
179+
if err != nil {
180+
return pack.U256{}, fmt.Errorf("searching state for addr: %v", addr)
181+
}
182+
183+
balance := actor.Balance.Int
184+
185+
// If the balance exceeds `MaxU256`, return an error.
186+
if pack.MaxU256.Int().Cmp(balance) == -1 {
187+
return pack.U256{}, fmt.Errorf("balance %v for %v exceeds MaxU256", balance.String(), addr)
188+
}
189+
190+
return pack.NewU256FromInt(balance), nil
191+
}

Diff for: chain/terra/terra.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,17 @@ type (
1818
)
1919

2020
var (
21-
// DefaultClientOptions re-exports default cosmos-compatible client options
21+
// DefaultClientOptions re-exports cosmos.DefaultClientOptions
2222
DefaultClientOptions = cosmos.DefaultClientOptions
2323

24+
// DefaultTxBuilderOptions re-exports cosmos.DefaultTxBuilderOptions
25+
DefaultTxBuilderOptions = cosmos.DefaultTxBuilderOptions
26+
2427
// NewGasEstimator re-exports cosmos.NewGasEstimator
2528
NewGasEstimator = cosmos.NewGasEstimator
2629
)
2730

28-
// NewClient returns returns a new Client with terra codec
31+
// NewClient returns returns a new Client with Terra codec.
2932
func NewClient(opts ClientOptions) *Client {
3033
return cosmos.NewClient(opts, app.MakeCodec())
3134
}

Diff for: chain/terra/terra_test.go

+9-5
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,19 @@ var _ = Describe("Terra", func() {
5353
Expect(err).NotTo(HaveOccurred())
5454

5555
// instantiate a new client
56-
client := terra.NewClient(terra.DefaultClientOptions())
56+
client := terra.NewClient(
57+
terra.DefaultClientOptions().
58+
WithCoinDenom("uluna"),
59+
)
5760
nonce, err := client.AccountNonce(ctx, multichain.Address(addr.String()))
5861
Expect(err).NotTo(HaveOccurred())
5962

6063
// create a new cosmos-compatible transaction builder
61-
txBuilder := terra.NewTxBuilder(terra.TxBuilderOptions{
62-
ChainID: "testnet",
63-
CoinDenom: "uluna",
64-
}, client)
64+
txBuilder := terra.NewTxBuilder(
65+
terra.DefaultTxBuilderOptions().
66+
WithChainID("testnet"),
67+
client,
68+
)
6569

6670
// build the transaction
6771
payload := pack.NewBytes([]byte("multichain"))

Diff for: multichain_test.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -346,12 +346,14 @@ var _ = Describe("Multichain", func() {
346346
func(rpcURL pack.String) (multichain.AccountClient, multichain.AccountTxBuilder) {
347347
client := terra.NewClient(
348348
terra.DefaultClientOptions().
349-
WithHost(rpcURL),
349+
WithHost(rpcURL).
350+
WithCoinDenom("uluna"),
351+
)
352+
txBuilder := terra.NewTxBuilder(
353+
terra.DefaultTxBuilderOptions().
354+
WithChainID("testnet"),
355+
client,
350356
)
351-
txBuilder := terra.NewTxBuilder(terra.TxBuilderOptions{
352-
ChainID: "testnet",
353-
CoinDenom: "uluna",
354-
}, client)
355357

356358
return client, txBuilder
357359
},

0 commit comments

Comments
 (0)