Helper function to convert Uint8Array to JSON.
Copy 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.
Copy 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.
Copy 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.
Copy 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.
Copy 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.
Copy 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.
Copy 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.
Copy 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.
Copy 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.
Copy 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.
Copy 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
Copy export interface EncodeObject {
readonly typeUrl : string ;
readonly value : any ;
}
simulateMulti
Simulates all given messages and returns a gas fee estimate.
Copy 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.
Copy async simulate (message: EncodeObject , fee ?: StdFee , memo = "" ) {
return this .simulateMulti ([message] , fee , memo);
}
execute
Executes a message on the specified contract.
Copy 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
Copy export type Msg = Record < string , unknown >;
simulateExecute
Simulates an execute message and returns a gas fee estimate.
Copy 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.
Copy 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.
Copy 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.
Copy 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.
Copy 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.
Copy 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.
Copy 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.
Copy 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);
}