diff --git a/client/chain/chain.go b/client/chain/chain.go index 0969c3e5..b114a0d5 100644 --- a/client/chain/chain.go +++ b/client/chain/chain.go @@ -105,12 +105,14 @@ type ChainClient interface { ) *authztypes.MsgGrant DefaultSubaccount(acc cosmtypes.AccAddress) eth.Hash + Subaccount(account cosmtypes.AccAddress, index int) eth.Hash GetSubAccountNonce(ctx context.Context, subaccountId eth.Hash) (*exchangetypes.QuerySubaccountTradeNonceResponse, error) GetFeeDiscountInfo(ctx context.Context, account string) (*exchangetypes.QueryFeeDiscountAccountInfoResponse, error) UpdateSubaccountNonceFromChain() error - ComputeOrderHashes(spotOrders []exchangetypes.SpotOrder, derivativeOrders []exchangetypes.DerivativeOrder) (OrderHashes, error) + SynchronizeSubaccountNonce(subaccountId eth.Hash) error + ComputeOrderHashes(spotOrders []exchangetypes.SpotOrder, derivativeOrders []exchangetypes.DerivativeOrder, subaccountId eth.Hash) (OrderHashes, error) SpotOrder(defaultSubaccountID eth.Hash, network common.Network, d *SpotOrderData) *exchangetypes.SpotOrder DerivativeOrder(defaultSubaccountID eth.Hash, network common.Network, d *DerivativeOrderData) *exchangetypes.DerivativeOrder @@ -938,7 +940,15 @@ func (c *chainClient) GetGasFee() (string, error) { } func (c *chainClient) DefaultSubaccount(acc cosmtypes.AccAddress) eth.Hash { - return eth.BytesToHash(eth.RightPadBytes(acc.Bytes(), 32)) + return c.Subaccount(acc, 0) +} + +func (c *chainClient) Subaccount(account cosmtypes.AccAddress, index int) eth.Hash { + ethAddress := eth.BytesToAddress(account.Bytes()) + ethLowerAddress := strings.ToLower(ethAddress.String()) + + subaccountId := fmt.Sprintf("%s%024x", ethLowerAddress, index) + return eth.HexToHash(subaccountId) } func (c *chainClient) GetSubAccountNonce(ctx context.Context, subaccountId eth.Hash) (*exchangetypes.QuerySubaccountTradeNonceResponse, error) { diff --git a/client/chain/chain_test.go b/client/chain/chain_test.go new file mode 100644 index 00000000..ca89e73d --- /dev/null +++ b/client/chain/chain_test.go @@ -0,0 +1,104 @@ +package chain + +import ( + "github.com/InjectiveLabs/sdk-go/client/common" + rpchttp "github.com/cometbft/cometbft/rpc/client/http" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + cosmtypes "github.com/cosmos/cosmos-sdk/types" + eth "github.com/ethereum/go-ethereum/common" + "os" + "testing" +) + +func accountForTests() (cosmtypes.AccAddress, keyring.Keyring, error) { + senderAddress, cosmosKeyring, err := InitCosmosKeyring( + os.Getenv("HOME")+"/.injectived", + "injectived", + "file", + "inj-user", + "12345678", + "5d386fbdbf11f1141010f81a46b40f94887367562bd33b452bbaa6ce1cd1381e", // keyring will be used if pk not provided + false, + ) + + return senderAddress, cosmosKeyring, err +} + +func createClient(senderAddress cosmtypes.AccAddress, cosmosKeyring keyring.Keyring, network common.Network) (ChainClient, error) { + tmClient, _ := rpchttp.New(network.TmEndpoint, "/websocket") + clientCtx, err := NewClientContext( + network.ChainId, + senderAddress.String(), + cosmosKeyring, + ) + + if err != nil { + return nil, err + } + + clientCtx = clientCtx.WithNodeURI(network.TmEndpoint).WithClient(tmClient) + + chainClient, err := NewChainClient( + clientCtx, + network.ChainGrpcEndpoint, + common.OptionTLSCert(network.ChainTlsCert), + common.OptionGasPrices("500000000inj"), + ) + + return chainClient, err +} + +func TestDefaultSubaccount(t *testing.T) { + network := common.LoadNetwork("testnet", "k8s") + senderAddress, cosmosKeyring, err := accountForTests() + + if err != nil { + t.Errorf("Error creating the address %v", err) + } + + chainClient, err := createClient(senderAddress, cosmosKeyring, network) + + if err != nil { + t.Errorf("Error creating the client %v", err) + } + + defaultSubaccountID := chainClient.DefaultSubaccount(senderAddress) + + expectedSubaccountId := "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000" + expectedSubaccountIdHash := eth.HexToHash(expectedSubaccountId) + if defaultSubaccountID != expectedSubaccountIdHash { + t.Error("The default subaccount is calculated incorrectly") + } +} + +func TestGetSubaccountWithIndes(t *testing.T) { + network := common.LoadNetwork("testnet", "k8s") + senderAddress, cosmosKeyring, err := accountForTests() + + if err != nil { + t.Errorf("Error creating the address %v", err) + } + + chainClient, err := createClient(senderAddress, cosmosKeyring, network) + + if err != nil { + t.Errorf("Error creating the client %v", err) + } + + subaccountOne := chainClient.Subaccount(senderAddress, 1) + subaccountThirty := chainClient.Subaccount(senderAddress, 30) + + expectedSubaccounOnetId := "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000001" + expectedSubaccountOneIdHash := eth.HexToHash(expectedSubaccounOnetId) + + expectedSubaccounThirtytId := "0xaf79152ac5df276d9a8e1e2e22822f971347490200000000000000000000001e" + expectedSubaccountThirtyIdHash := eth.HexToHash(expectedSubaccounThirtytId) + + if subaccountOne != expectedSubaccountOneIdHash { + t.Error("The subaccount with index 1 was calculated incorrectly") + } + if subaccountThirty != expectedSubaccountThirtyIdHash { + t.Error("The subaccount with index 30 was calculated incorrectly") + } + +} diff --git a/client/chain/orderhash.go b/client/chain/orderhash.go index 3f871e9f..c0459f77 100644 --- a/client/chain/orderhash.go +++ b/client/chain/orderhash.go @@ -58,26 +58,33 @@ var domain = gethsigner.TypedDataDomain{ } func (c *chainClient) UpdateSubaccountNonceFromChain() error { - subaccountId := c.DefaultSubaccount(c.ctx.FromAddress) + for subaccountId := range c.subaccountToNonce { + err := c.SynchronizeSubaccountNonce(subaccountId) + if err != nil { + return err + } + } + return nil +} + +func (c *chainClient) SynchronizeSubaccountNonce(subaccountId common.Hash) error { res, err := c.GetSubAccountNonce(context.Background(), subaccountId) if err != nil { return err } - c.subaccountToNonce[subaccountId] = res.Nonce return nil } -func (c *chainClient) ComputeOrderHashes(spotOrders []exchangetypes.SpotOrder, derivativeOrders []exchangetypes.DerivativeOrder) (OrderHashes, error) { +func (c *chainClient) ComputeOrderHashes(spotOrders []exchangetypes.SpotOrder, derivativeOrders []exchangetypes.DerivativeOrder, subaccountId common.Hash) (OrderHashes, error) { if len(spotOrders)+len(derivativeOrders) == 0 { return OrderHashes{}, nil } orderHashes := OrderHashes{} // get nonce - subaccountId := c.DefaultSubaccount(c.ctx.FromAddress) if _, exist := c.subaccountToNonce[subaccountId]; !exist { - if err := c.UpdateSubaccountNonceFromChain(); err != nil { + if err := c.SynchronizeSubaccountNonce(subaccountId); err != nil { return OrderHashes{}, err } } diff --git a/examples/chain/0_LocalOrderHash/example.go b/examples/chain/0_LocalOrderHash/example.go index 9d0d1ec3..be67e852 100644 --- a/examples/chain/0_LocalOrderHash/example.go +++ b/examples/chain/0_LocalOrderHash/example.go @@ -59,7 +59,7 @@ func main() { } // prepare tx msg - defaultSubaccountID := chainClient.DefaultSubaccount(senderAddress) + defaultSubaccountID := chainClient.Subaccount(senderAddress, 1) spotOrder := chainClient.SpotOrder(defaultSubaccountID, network, &chainclient.SpotOrderData{ OrderType: exchangetypes.OrderType_BUY, @@ -87,7 +87,7 @@ func main() { msg1.Orders = []exchangetypes.DerivativeOrder{*derivativeOrder, *derivativeOrder} // compute local order hashes - orderHashes, err := chainClient.ComputeOrderHashes(msg.Orders, msg1.Orders) + orderHashes, err := chainClient.ComputeOrderHashes(msg.Orders, msg1.Orders, defaultSubaccountID) if err != nil { fmt.Println(err)