Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions client/chain/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import (
evmtypes "github.com/InjectiveLabs/sdk-go/chain/evm/types"
exchangetypes "github.com/InjectiveLabs/sdk-go/chain/exchange/types"
exchangev2types "github.com/InjectiveLabs/sdk-go/chain/exchange/types/v2"
insurancetypes "github.com/InjectiveLabs/sdk-go/chain/insurance/types"
permissionstypes "github.com/InjectiveLabs/sdk-go/chain/permissions/types"
chainstreamtypes "github.com/InjectiveLabs/sdk-go/chain/stream/types"
chainstreamv2types "github.com/InjectiveLabs/sdk-go/chain/stream/types/v2"
Expand Down Expand Up @@ -468,6 +469,12 @@ type ChainClient interface {
FetchEVMCode(ctx context.Context, address string) (*evmtypes.QueryCodeResponse, error)
FetchEVMBaseFee(ctx context.Context) (*evmtypes.QueryBaseFeeResponse, error)

// Insurance module
FetchInsuranceFund(ctx context.Context, marketId string) (*insurancetypes.QueryInsuranceFundResponse, error)
FetchInsuranceFunds(ctx context.Context) (*insurancetypes.QueryInsuranceFundsResponse, error)
FetchPendingRedemptions(ctx context.Context, marketId string, address string) (*insurancetypes.QueryPendingRedemptionsResponse, error)
FetchEstimatedRedemptions(ctx context.Context, marketId string, address string) (*insurancetypes.QueryEstimatedRedemptionsResponse, error)

CurrentChainGasPrice() int64
SetGasPrice(gasPrice int64)

Expand Down Expand Up @@ -522,6 +529,7 @@ type chainClient struct {
txfeesQueryClient txfeestypes.QueryClient
txClient txtypes.ServiceClient
wasmQueryClient wasmtypes.QueryClient
insuranceQueryClient insurancetypes.QueryClient
subaccountToNonce map[ethcommon.Hash]uint32

closed int64
Expand Down Expand Up @@ -628,6 +636,7 @@ func NewChainClient(
txfeesQueryClient: txfeestypes.NewQueryClient(conn),
txClient: txtypes.NewServiceClient(conn),
wasmQueryClient: wasmtypes.NewQueryClient(conn),
insuranceQueryClient: insurancetypes.NewQueryClient(conn),
subaccountToNonce: make(map[ethcommon.Hash]uint32),
}

Expand Down Expand Up @@ -3694,6 +3703,40 @@ func (c *chainClient) FetchEVMBaseFee(ctx context.Context) (*evmtypes.QueryBaseF
return res, err
}

// Insurance module

func (c *chainClient) FetchInsuranceFund(ctx context.Context, marketId string) (*insurancetypes.QueryInsuranceFundResponse, error) {
req := &insurancetypes.QueryInsuranceFundRequest{
MarketId: marketId,
}
res, err := common.ExecuteCall(ctx, c.network.ChainCookieAssistant, c.insuranceQueryClient.InsuranceFund, req)
return res, err
}

func (c *chainClient) FetchInsuranceFunds(ctx context.Context) (*insurancetypes.QueryInsuranceFundsResponse, error) {
req := &insurancetypes.QueryInsuranceFundsRequest{}
res, err := common.ExecuteCall(ctx, c.network.ChainCookieAssistant, c.insuranceQueryClient.InsuranceFunds, req)
return res, err
}

func (c *chainClient) FetchPendingRedemptions(ctx context.Context, marketId string, address string) (*insurancetypes.QueryPendingRedemptionsResponse, error) {
req := &insurancetypes.QueryPendingRedemptionsRequest{
MarketId: marketId,
Address: address,
}
res, err := common.ExecuteCall(ctx, c.network.ChainCookieAssistant, c.insuranceQueryClient.PendingRedemptions, req)
return res, err
}

func (c *chainClient) FetchEstimatedRedemptions(ctx context.Context, marketId string, address string) (*insurancetypes.QueryEstimatedRedemptionsResponse, error) {
req := &insurancetypes.QueryEstimatedRedemptionsRequest{
MarketId: marketId,
Address: address,
}
res, err := common.ExecuteCall(ctx, c.network.ChainCookieAssistant, c.insuranceQueryClient.EstimatedRedemptions, req)
return res, err
}

// SyncBroadcastMsg sends Tx to chain and waits until Tx is included in block.
func (c *chainClient) SyncBroadcastMsg(msgs ...sdk.Msg) (*txtypes.BroadcastTxResponse, error) {
req, res, err := c.BroadcastMsg(txtypes.BroadcastMode_BROADCAST_MODE_SYNC, msgs...)
Expand Down
19 changes: 19 additions & 0 deletions client/chain/chain_test_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
evmtypes "github.com/InjectiveLabs/sdk-go/chain/evm/types"
exchangetypes "github.com/InjectiveLabs/sdk-go/chain/exchange/types"
exchangev2types "github.com/InjectiveLabs/sdk-go/chain/exchange/types/v2"
insurancetypes "github.com/InjectiveLabs/sdk-go/chain/insurance/types"
permissionstypes "github.com/InjectiveLabs/sdk-go/chain/permissions/types"
chainstreamtypes "github.com/InjectiveLabs/sdk-go/chain/stream/types"
chainstreamv2types "github.com/InjectiveLabs/sdk-go/chain/stream/types/v2"
Expand Down Expand Up @@ -1209,6 +1210,24 @@ func (c *MockChainClient) FetchEVMBaseFee(ctx context.Context) (*evmtypes.QueryB
return &evmtypes.QueryBaseFeeResponse{}, nil
}

// Insurance module

func (c *MockChainClient) FetchInsuranceFund(ctx context.Context, marketId string) (*insurancetypes.QueryInsuranceFundResponse, error) {
return &insurancetypes.QueryInsuranceFundResponse{}, nil
}

func (c *MockChainClient) FetchInsuranceFunds(ctx context.Context) (*insurancetypes.QueryInsuranceFundsResponse, error) {
return &insurancetypes.QueryInsuranceFundsResponse{}, nil
}

func (c *MockChainClient) FetchPendingRedemptions(ctx context.Context, marketId string, address string) (*insurancetypes.QueryPendingRedemptionsResponse, error) {
return &insurancetypes.QueryPendingRedemptionsResponse{}, nil
}

func (c *MockChainClient) FetchEstimatedRedemptions(ctx context.Context, marketId string, address string) (*insurancetypes.QueryEstimatedRedemptionsResponse, error) {
return &insurancetypes.QueryEstimatedRedemptionsResponse{}, nil
}
Comment on lines +1213 to +1229
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Mock does not allow configurable responses for new insurance queries

Other mock methods (e.g. FetchChainSpotMarkets) pop predefined responses from slices, enabling deterministic tests. The four newly added insurance stubs always return empty structs, preventing unit tests from asserting specific data.

Consider extending MockChainClient with response queues similar to existing patterns:

type MockChainClient struct {
     ...
+    InsuranceFundResponses           []*insurancetypes.QueryInsuranceFundResponse
+    InsuranceFundsResponses          []*insurancetypes.QueryInsuranceFundsResponse
+    PendingRedemptionsResponses      []*insurancetypes.QueryPendingRedemptionsResponse
+    EstimatedRedemptionsResponses    []*insurancetypes.QueryEstimatedRedemptionsResponse
}

and pop from those slices inside each method.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In client/chain/chain_test_support.go around lines 1213 to 1229, the new
insurance-related mock methods always return empty structs, which prevents tests
from asserting specific data. To fix this, add response slices or queues to the
MockChainClient struct for each insurance query type, then modify each method to
pop and return the next predefined response from the corresponding slice. This
will allow tests to set up deterministic, configurable mock responses similar to
existing mock methods.


func (c *MockChainClient) CurrentChainGasPrice() int64 {
return int64(injectiveclient.DefaultGasPrice)
}
Expand Down
53 changes: 53 additions & 0 deletions examples/chain/insurance/1_InsuranceFund/example.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package main

import (
"context"
"encoding/json"
"fmt"

"github.com/InjectiveLabs/sdk-go/client"
chainclient "github.com/InjectiveLabs/sdk-go/client/chain"
"github.com/InjectiveLabs/sdk-go/client/common"
rpchttp "github.com/cometbft/cometbft/rpc/client/http"
)

func main() {
network := common.LoadNetwork("testnet", "lb")
tmClient, err := rpchttp.New(network.TmEndpoint)
if err != nil {
panic(err)
}

clientCtx, err := chainclient.NewClientContext(
network.ChainId,
"",
nil,
)

if err != nil {
panic(err)
}

clientCtx = clientCtx.WithNodeURI(network.TmEndpoint).WithClient(tmClient)

chainClient, err := chainclient.NewChainClient(
clientCtx,
network,
common.OptionGasPrices(client.DefaultGasPriceWithDenom),
)

if err != nil {
panic(err)
}

ctx := context.Background()
marketId := "0xfc615d2dfe6cc437c77c0d2b1721bba6947f821ce45c1aa4094dcdf333f46881"

res, err := chainClient.FetchInsuranceFund(ctx, marketId)
if err != nil {
fmt.Println(err)
}

str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
52 changes: 52 additions & 0 deletions examples/chain/insurance/2_InsuranceFunds/example.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package main

import (
"context"
"encoding/json"
"fmt"

"github.com/InjectiveLabs/sdk-go/client"
chainclient "github.com/InjectiveLabs/sdk-go/client/chain"
"github.com/InjectiveLabs/sdk-go/client/common"
rpchttp "github.com/cometbft/cometbft/rpc/client/http"
)

func main() {
network := common.LoadNetwork("testnet", "lb")
tmClient, err := rpchttp.New(network.TmEndpoint)
if err != nil {
panic(err)
}

clientCtx, err := chainclient.NewClientContext(
network.ChainId,
"",
nil,
)

if err != nil {
panic(err)
}

clientCtx = clientCtx.WithNodeURI(network.TmEndpoint).WithClient(tmClient)

chainClient, err := chainclient.NewChainClient(
clientCtx,
network,
common.OptionGasPrices(client.DefaultGasPriceWithDenom),
)

if err != nil {
panic(err)
}

ctx := context.Background()

res, err := chainClient.FetchInsuranceFunds(ctx)
if err != nil {
fmt.Println(err)
}

str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
69 changes: 69 additions & 0 deletions examples/chain/insurance/3_PendingRedemptions/example.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package main

import (
"context"
"encoding/json"
"fmt"
"os"

"github.com/InjectiveLabs/sdk-go/client"
chainclient "github.com/InjectiveLabs/sdk-go/client/chain"
"github.com/InjectiveLabs/sdk-go/client/common"
rpchttp "github.com/cometbft/cometbft/rpc/client/http"
)

func main() {
network := common.LoadNetwork("testnet", "lb")
tmClient, err := rpchttp.New(network.TmEndpoint)
if err != nil {
panic(err)
}

userAddress, _, err := chainclient.InitCosmosKeyring(
os.Getenv("HOME")+"/.injectived",
"injectived",
"file",
"inj-user",
"12345678",
"5d386fbdbf11f1141010f81a46b40f94887367562bd33b452bbaa6ce1cd1381e", // keyring will be used if pk not provided
false,
)
Comment on lines +22 to +30
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Private key & passphrase committed to source

Embedding a plaintext passphrase ("12345678") and a raw private key exposes test funds and promotes insecure habits. Read credentials from environment variables or a secure store instead.

-    "12345678",
-    "5d386fbd...1381e",
+    os.Getenv("INJ_KEYRING_PASSPHRASE"),
+    os.Getenv("INJ_PRIV_KEY"),
🤖 Prompt for AI Agents
In examples/chain/insurance/3_PendingRedemptions/example.go around lines 22 to
30, the passphrase and private key are hardcoded as plaintext strings, which is
insecure. Replace these hardcoded values by reading the passphrase and private
key from environment variables or a secure secrets store. Update the code to
fetch these credentials dynamically at runtime instead of embedding them
directly in the source code.


if err != nil {
panic(err)
}

clientCtx, err := chainclient.NewClientContext(
network.ChainId,
"",
nil,
)

if err != nil {
panic(err)
}

clientCtx = clientCtx.WithNodeURI(network.TmEndpoint).WithClient(tmClient)

chainClient, err := chainclient.NewChainClient(
clientCtx,
network,
common.OptionGasPrices(client.DefaultGasPriceWithDenom),
)

if err != nil {
panic(err)
}

ctx := context.Background()
marketId := "0xfc615d2dfe6cc437c77c0d2b1721bba6947f821ce45c1aa4094dcdf333f46881"
userAddressStr := userAddress.String()

res, err := chainClient.FetchPendingRedemptions(ctx, marketId, userAddressStr)
if err != nil {
fmt.Println(err)
}

str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
69 changes: 69 additions & 0 deletions examples/chain/insurance/4_EstimatedRedemptions/example.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package main

import (
"context"
"encoding/json"
"fmt"
"os"

"github.com/InjectiveLabs/sdk-go/client"
chainclient "github.com/InjectiveLabs/sdk-go/client/chain"
"github.com/InjectiveLabs/sdk-go/client/common"
rpchttp "github.com/cometbft/cometbft/rpc/client/http"
)

func main() {
network := common.LoadNetwork("testnet", "lb")
tmClient, err := rpchttp.New(network.TmEndpoint)
if err != nil {
panic(err)
}

userAddress, _, err := chainclient.InitCosmosKeyring(
os.Getenv("HOME")+"/.injectived",
"injectived",
"file",
"inj-user",
"12345678",
"5d386fbdbf11f1141010f81a46b40f94887367562bd33b452bbaa6ce1cd1381e", // keyring will be used if pk not provided
false,
)
Comment on lines +22 to +30
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Private key & passphrase committed to source

Same security concern as in the PendingRedemptions example; move secrets to environment variables.

🤖 Prompt for AI Agents
In examples/chain/insurance/4_EstimatedRedemptions/example.go around lines 22 to
30, the private key and passphrase are hardcoded in the source code, which is a
security risk. Remove the hardcoded private key and passphrase and instead read
them from environment variables using os.Getenv. Update the InitCosmosKeyring
call to use these environment variables for the private key and passphrase
values.


if err != nil {
panic(err)
}

clientCtx, err := chainclient.NewClientContext(
network.ChainId,
"",
nil,
)

if err != nil {
panic(err)
}

clientCtx = clientCtx.WithNodeURI(network.TmEndpoint).WithClient(tmClient)

chainClient, err := chainclient.NewChainClient(
clientCtx,
network,
common.OptionGasPrices(client.DefaultGasPriceWithDenom),
)

if err != nil {
panic(err)
}

ctx := context.Background()
marketId := "0xfc615d2dfe6cc437c77c0d2b1721bba6947f821ce45c1aa4094dcdf333f46881"
userAddressStr := userAddress.String()

res, err := chainClient.FetchEstimatedRedemptions(ctx, marketId, userAddressStr)
if err != nil {
fmt.Println(err)
}

str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Loading