diff --git a/client/chain/chain.go b/client/chain/chain.go index e19298fa..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 @@ -392,6 +394,7 @@ func (c *chainClient) fetchCookie(ctx context.Context) context.Context { c.txClient.GetTx(context.Background(), &txtypes.GetTxRequest{}, grpc.Header(&header)) c.setCookie(header) time.Sleep(defaultBlockTime) + return metadata.NewOutgoingContext(ctx, metadata.Pairs("cookie", c.sessionCookie)) } @@ -412,6 +415,10 @@ func (c *chainClient) getCookieExpirationTime(cookies []*http.Cookie) (time.Time expiresAt = strings.Replace(cookie.Value, "-", " ", -1) } else { cookie := cookieByName(cookies, "Expires") + if cookie == nil { + return time.Time{}, nil + } + expiresAt = strings.Replace(cookie.Value, "-", " ", -1) yyyy := fmt.Sprintf("20%s", expiresAt[12:14]) expiresAt = expiresAt[:12] + yyyy + expiresAt[14:] @@ -439,10 +446,12 @@ func (c *chainClient) getCookie(ctx context.Context) context.Context { panic(err) } - // renew session if timestamp diff < offset - timestampDiff := expiresTimestamp.Unix() - time.Now().Unix() - if timestampDiff < defaultSessionRenewalOffset { - return c.fetchCookie(ctx) + if !expiresTimestamp.IsZero() { + // renew session if timestamp diff < offset + timestampDiff := expiresTimestamp.Unix() - time.Now().Unix() + if timestampDiff < defaultSessionRenewalOffset { + return c.fetchCookie(ctx) + } } } else { return c.fetchCookie(ctx) @@ -931,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/client/common/network.go b/client/common/network.go index e9e0992e..2840d2a7 100644 --- a/client/common/network.go +++ b/client/common/network.go @@ -96,7 +96,7 @@ func LoadNetwork(name string, node string) Network { Name: "testnet", } case "mainnet": - validNodes := []string{"k8s", "lb", "sentry0", "sentry1", "sentry2", "sentry3"} + validNodes := []string{"k8s", "lb", "sentry", "sentry0", "sentry1", "sentry2", "sentry3"} if !contains(validNodes, node) { panic(fmt.Sprintf("invalid node %s for %s", node, name)) } @@ -122,6 +122,15 @@ func LoadNetwork(name string, node string) Network { explorerGrpcEndpoint = "tcp://k8s.mainnet.explorer.grpc.injective.network:443" exchangeTlsCert = credentials.NewServerTLSFromCert(&tls.Certificate{}) explorerTlsCert = LoadTlsCert(certPath, explorerGrpcEndpoint) + } else if node == "sentry" { + lcdEndpoint = "https://sentry.lcd.injective.network" + tmEndpoint = "http://sentry.tm.injective.network:26657" + chainGrpcEndpoint = "sentry.chain.grpc.injective.network:443" + exchangeGrpcEndpoint = "sentry.exchange.grpc.injective.network:443" + explorerGrpcEndpoint = "k8s.global.mainnet.explorer.grpc.injective.network:443" + chainTlsCert = credentials.NewTLS(&tls.Config{InsecureSkipVerify: false}) + exchangeTlsCert = credentials.NewTLS(&tls.Config{InsecureSkipVerify: false}) + explorerTlsCert = credentials.NewServerTLSFromCert(&tls.Certificate{}) } else { lcdEndpoint = fmt.Sprintf("http://%s.injective.network:10337", node) tmEndpoint = fmt.Sprintf("http://%s.injective.network:26657", node) @@ -131,7 +140,8 @@ func LoadNetwork(name string, node string) Network { if node == "sentry2" { explorerGrpcEndpoint = "tcp://sentry2.injective.network:9911" } else { - explorerGrpcEndpoint = "tcp://api.injective.network:9911" + explorerGrpcEndpoint = "k8s.global.mainnet.explorer.grpc.injective.network:443" + explorerTlsCert = credentials.NewServerTLSFromCert(&tls.Certificate{}) } } 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) diff --git a/examples/exchange/accounts/2_SubaccountBalance/example.go b/examples/exchange/accounts/2_SubaccountBalance/example.go index 78ebac80..a81e96c2 100644 --- a/examples/exchange/accounts/2_SubaccountBalance/example.go +++ b/examples/exchange/accounts/2_SubaccountBalance/example.go @@ -11,7 +11,7 @@ import ( func main() { //network := common.LoadNetwork("mainnet", "k8s") - network := common.LoadNetwork("mainnet", "lb") + network := common.LoadNetwork("mainnet", "sentry") exchangeClient, err := exchangeclient.NewExchangeClient(network.ExchangeGrpcEndpoint, common.OptionTLSCert(network.ExchangeTlsCert)) if err != nil { panic(err) diff --git a/examples/explorer/1_GetTxByHash/example.go b/examples/explorer/1_GetTxByHash/example.go index eb03e0b4..833ca12e 100644 --- a/examples/explorer/1_GetTxByHash/example.go +++ b/examples/explorer/1_GetTxByHash/example.go @@ -10,7 +10,7 @@ import ( ) func main() { - network := common.LoadNetwork("testnet", "k8s") + network := common.LoadNetwork("mainnet", "sentry") explorerClient, err := explorerclient.NewExplorerClient(network.ExplorerGrpcEndpoint, common.OptionTLSCert(network.ExplorerTlsCert)) if err != nil { panic(err)