InjectiveClient

Helper function to convert Uint8Array to JSON.

function arrayToJson(array: Uint8Array) {
  const jsonString = Buffer.from(array).toString("utf8");

  const parsedData = JSON.parse(jsonString);
  return parsedData;
}

Class

A client to interact with the Injective chain.

export default class InjectiveClient
  extends BaseChainClient
  implements ChainClient
{
  public signingClient?: TxGrpcClient;
  public queryClient?: CosmWasmClient;

  private directSigner?: OfflineDirectSigner | CosmosOfflineDirectSigner;
  private chainRestTendermintApi?: ChainRestTendermintApi;
  private chainRestAuthApi?: ChainRestAuthApi;
  private gasPrice?: GasPrice;
  private chainId?: string;
  
//A pre-message hook to check that the client is connected and functioning
//@param signed Whether the message is signed
  protected preMessage(signed = true) {
    super.preMessage(signed);
    if (signed && !this.directSigner) {
      throw new Error("No signer assigned");
    }
  }

Methods

connect

Connects to the given chain. Assigns all clients used within the chain client, if a signer is provided a signing client is assigned.

async connect(
    endpoint: string,
    signer?: CosmosOfflineDirectSigner | OfflineDirectSigner
  ): Promise<void> {
    delete this.signingClient;
    delete this.queryClient;
    const network = endpoint.includes("testnet")
      ? Network.TestnetK8s
      : Network.MainnetK8s;
    this.chainId = getNetworkChainInfo(network).chainId;
    const { rest, rpc, grpc } = getNetworkEndpoints(network);

    this.queryClient = await CosmWasmClient.connect(rpc!);
    this.chainRestTendermintApi = new ChainRestTendermintApi(rest);
    this.chainRestAuthApi = new ChainRestAuthApi(rest);

    if (signer) {
      this.signingClient = new TxGrpcClient(grpc);
      this.directSigner = signer;

      const [account] = await signer.getAccounts();
      this.signer = account.address;
    }
  }

disconnect

Disconnects the assigned clients.

async disconnect(): Promise<void> {
    delete this.signingClient;
    this.queryClient?.disconnect();
    delete this.queryClient;
    this.signer = "";
    delete this.directSigner;
    delete this.gasPrice;
    delete this.chainRestTendermintApi;
    delete this.chainRestAuthApi;
  }

getTimeoutHeight

Gets the block height for which a transaction will time out if not executed prior.

You need to have provided a chainRestTendermintAPI for the class.

private async getTimeoutHeight() {
    if (!this.chainRestTendermintApi)
      throw new Error("Chain Tendermint Rest API not connected");
    const latestBlock = await this.chainRestTendermintApi.fetchLatestBlock();
    const latestHeight = latestBlock.header.height;
    return new BigNumberInBase(latestHeight)
      .plus(DEFAULT_BLOCK_TIMEOUT_HEIGHT)
      .toNumber();
  }

getBaseAccount

Gets information on the connected account.

You need to have provided a chainRestAuthApi for the class.

private async getBaseAccount() {
    if (!this.chainRestAuthApi)
      throw new Error("Chain Auth Rest API not connected");
    const accountDetailsResponse = await this.chainRestAuthApi.fetchAccount(
      this.signer
    );
    const baseAccount = BaseAccount.fromRestApi(accountDetailsResponse);
    return baseAccount.toAccountDetails();
  }

getPubKey

Gets the public key of the connected signer.

 private async getPubKey() {
    const [account] = await this.directSigner!.getAccounts();

    return Buffer.from(account.pubkey).toString("base64");
  }

signInj

Signs a given message with the connected signer.

private async signInj(
    messages: EncodeObject[],
    fee: StdFee = getStdFee((DEFAULT_GAS_LIMIT * 50).toString()),
    memo: string = ""
  ) {
    this.preMessage();
    const timeoutHeight = await this.getTimeoutHeight();
    const baseAccount = await this.getBaseAccount();
    const pubKey = await this.getPubKey();
    const { signDoc, txRaw } = createTransaction({
      pubKey,
      chainId: this.chainId!,
      message: encodeObjectToMsgArgs(messages),
      fee,
      timeoutHeight,
      sequence: baseAccount.sequence,
      accountNumber: baseAccount.accountNumber,
      memo,
    });
    const signed = await this.directSigner!.signDirect(this.signer!, {
      ...signDoc,
      chainId: signDoc.getChainId(),
      bodyBytes: signDoc.getBodyBytes_asU8(),
      authInfoBytes: signDoc.getAuthInfoBytes_asU8(),
      accountNumber: Long.fromInt(signDoc.getAccountNumber()),
    });
    txRaw.setSignaturesList([signed.signature.signature]);
    return txRaw;
  }

sign

Signs a given message with the connected signer.

async sign(messages: EncodeObject[], fee?: StdFee, memo?: string) {
    const injTxRaw = await this.signInj(messages, fee, memo);
    return {
      bodyBytes: injTxRaw.getBodyBytes_asU8(),
      authInfoBytes: injTxRaw.getAuthInfoBytes_asU8(),
      signatures: injTxRaw.getSignaturesList_asU8(),
    };
  }

broadcast

Broadcasts a given transaction to the connected client.

 async broadcast(
    tx: InjTxRaw,
    timeoutMs = 60000,
    pollIntervalMs = 3000
  ): Promise<DeliverTxResponse> {
    const resp = await this.signingClient!.broadcastBlock(tx);
    let timedOut = false;
    const txPollTimeout = setTimeout(() => {
      timedOut = true;
    }, timeoutMs);

    const pollTx: (
      txId: string
    ) => ReturnType<ChainClient["broadcast"]> = async (txId) => {
      if (timedOut)
        throw new Error(
          `Transaction with ID ${txId} was submitted but was not yet found on the chain. You might want to check later. There was a wait of ${
            timeoutMs / 1000
          } seconds`
        );
      await sleep(pollIntervalMs);
      const result = await this.queryClient!.getTx(txId);
      return result
        ? ({ ...result, transactionHash: result.hash } as DeliverTxResponse)
        : pollTx(txId);
    };

    return new Promise((resolve, reject) =>
      pollTx(resp.txHash).then(
        (val) => {
          clearTimeout(txPollTimeout);
          resolve(val);
        },
        (err) => {
          clearTimeout(txPollTimeout);
          reject(err);
        }
      )
    );
  }

signAndBroadcast

Signs a given message before broadcasting it to the connected chain.

async signAndBroadcast(
    messages: EncodeObject[],
    fee?: StdFee,
    memo?: string
  ): Promise<DeliverTxResponse & { logs: readonly Log[] }> {
    const signed = await this.signInj(messages, fee, memo);
    const resp = await this.broadcast(signed);
    return {
      ...resp,
      logs: parseRawLog(resp.rawLog),
    };
  }

EncodeObject

export interface EncodeObject {
    readonly typeUrl: string;
    readonly value: any;
}

simulateMulti

Simulates all given messages and returns a gas fee estimate.

 async simulateMulti(messages: EncodeObject[], fee?: StdFee, memo = "") {
    const txRaw = await this.signInj(messages, fee, memo);
    const resp = await this.signingClient!.simulate(txRaw);
    return resp.gasInfo.gasUsed;
  }

simulate

Simulates the given message and returns a gas fee estimate.

async simulate(message: EncodeObject, fee?: StdFee, memo = "") {
    return this.simulateMulti([message], fee, memo);
  }

execute

Executes a message on the specified contract.

 async execute(
    contractAddress: string,
    msg: Msg,
    fee?: StdFee,
    memo?: string | undefined,
    funds: Coin[] = []
  ): Promise<ExecuteResult> {
    const message = this.encodeExecuteMsg(contractAddress, msg, funds);
    return await this.signAndBroadcast([message], fee, memo);
  }

Msg

export type Msg = Record<string, unknown>;

simulateExecute

Simulates an execute message and returns a gas fee estimate.

async simulateExecute(
    address: string,
    msg: Msg,
    funds: Coin[],
    fee?: StdFee,
    memo?: string | undefined
  ) {
    const message = this.encodeExecuteMsg(address, msg, funds);
    return this.simulate(message, fee, memo);
  }

upload

Uploads given contract code (Uint8Array) to the chain.

async upload(
    code: Uint8Array,
    fee?: StdFee,
    memo = ""
  ): ReturnType<ChainClient["upload"]> {
    const compressed = gzip(code, { level: 9 });
    const message = this.encodeUploadMessage(compressed);
    const resp = await this.signAndBroadcast([message], fee, memo);

    const codeIdAttr = findAttribute(resp.logs, "store_code", "code_id");

    const originalChecksum = toHex(sha256(code));
    const compressedChecksum = toHex(sha256(compressed));
    return {
      ...resp,
      codeId: parseInt(codeIdAttr.value, 10),
      originalSize: code.length,
      originalChecksum,
      compressedSize: compressed.length,
      compressedChecksum,
    };
  }

simulateUpload

Simulate an upload message and returns a gas fee estimate.

async simulateUpload(
    code: Uint8Array,
    fee?: StdFee,
    memo?: string | undefined
  ): Promise<number | undefined> {
    const compressed = gzip(code, { level: 9 });
    const message = this.encodeUploadMessage(compressed);
    return this.simulate(message, fee, memo);
  }

instantiate

Instantiates a contract with the given code id using the provided instantiate message.

async instantiate(
    codeId: number,
    msg: Msg,
    label: string,
    fee?: StdFee,
    options?: InstantiateOptions
  ): Promise<InstantiateResult> {
    const message = this.encodeInstantiateMsg(codeId, msg, label);
    const resp = await this.signAndBroadcast(
      [message],
      fee,
      options ? options.memo : ""
    );
    const contractAddressAttr = findAttribute(
      resp.logs,
      "wasm",
      "_contract_address"
    );

    return {
      ...resp,
      contractAddress: contractAddressAttr.value,
    };
  }

simulateInstantiate

Simulates an instantiation message and returns a gas fee estimate.

async simulateInstantiate(
    codeId: number,
    msg: Msg,
    label: string,
    fee?: StdFee,
    options?: InstantiateOptions
  ): Promise<number | undefined> {
    const message = this.encodeInstantiateMsg(codeId, msg, label);
    return this.simulate(message, fee, options?.memo);
  }

migrate

Migrates a contract to a given code id.

async migrate(
    contractAddress: string,
    codeId: number,
    msg: Msg,
    fee?: StdFee,
    memo?: string
  ): Promise<MigrateResult> {
    const message = this.encodeMigrateMessage(contractAddress, codeId, msg);
    const resp = await this.signAndBroadcast([message], fee, memo);
    return resp;
  }

simulateMigrate

Simulates a migrate message for a given contract address, code id and migrate message and returns a gas estimate.

async simulateMigrate(
    contractAddress: string,
    codeId: number,
    msg: Msg,
    fee?: StdFee,
    memo?: string | undefined
  ): Promise<number | undefined> {
    const message = this.encodeMigrateMessage(contractAddress, codeId, msg);
    return this.simulate(message, fee, memo);
  }

sendTokens

Sends tokens from the signing address to the provided receiving address.

You can only send one amount at a time.

async sendTokens(
    receivingAddress: string,
    amount: readonly Coin[],
    fee?: StdFee,
    memo?: string
  ): Promise<DeliverTxResponse> {
    if (amount.length > 1)
      throw new Error(
        "Injective only enables the sending of one amount at a time, please only send one token type."
      );

    const message = this.encodeSendMessage(receivingAddress, [...amount]);

    return this.signAndBroadcast([message], fee, memo);
  }

Last updated