Injective Typescript Cosmos
Космос
Все транзакции в Injective проходят по одному и тому же сценарию. Этот поток состоит из трех этапов: подготовка, подписание и трансляция транзакции. Давайте рассмотрим каждый этап в отдельности и подробно объясним процесс (включая примеры), чтобы понять весь поток транзакций.
Подготовка транзакции
Прежде всего, необходимо подготовить транзакцию к подписанию.
На данном этапе нельзя воспользоваться некоторыми онлайн-абстракциями, которые обеспечивают быстрый способ подготовки транзакции на основе предоставленного сообщения и подписывающего лица (например, с помощью пакета @cosmjs/stargate). Причина в том, что эти пакеты не поддерживают Injective's publicKey typeUrl, поэтому нам приходится выполнять подготовку адреса на стороне клиента.
Чтобы решить эту проблему, мы предоставили функции, которые могут подготовить транзакцию txRaw внутри пакета @injectivelabs/sdk-ts. txRaw - это интерфейс транзакции, используемый в Cosmos, который содержит информацию о транзакции и самом подписавшемся.
import { MsgSend, ChainRestAuthApi, ChainRestTendermintApi, BaseAccount, DEFAULT_STD_FEE, createTransaction, } from '@injectivelabs/sdk-ts' import { DEFAULT_STD_FEE, DEFAULT_BLOCK_TIMEOUT_HEIGHT } from '@injectivelabs/utils' import { ChainId } from '@injectivelabs/ts-types' import { Network, getNetworkEndpoints } from '@injectivelabs/networks'
const injectiveAddress = 'inj1' const chainId = 'injective-1' /* ChainId.Mainnet */ const restEndpoint = 'https://lcd.injective.network' /* getNetworkEndpoints(Network.Mainnet).rest */ const amount = { amount: new BigNumberInBase(0.01).toWei().toFixed(), denom: "inj", };
/** Account Details **/ const chainRestAuthApi = new ChainRestAuthApi( restEndpoint, ) const accountDetailsResponse = await chainRestAuthApi.fetchAccount( injectiveAddress, ) const baseAccount = BaseAccount.fromRestApi(accountDetailsResponse) const accountDetails = baseAccount.toAccountDetails()
/** Block Details */ const chainRestTendermintApi = new ChainRestTendermintApi( restEndpoint, ) const latestBlock = await chainRestTendermintApi.fetchLatestBlock() const latestHeight = latestBlock.header.height const timeoutHeight = new BigNumberInBase(latestHeight).plus( DEFAULT_BLOCK_TIMEOUT_HEIGHT, )
/** Preparing the transaction */ const msg = MsgSend.fromJSON({ amount, srcInjectiveAddress: injectiveAddress, dstInjectiveAddress: injectiveAddress, });
/** Get the PubKey of the Signer from the Wallet/Private Key */ const pubKey = await getPubKey()
/** Prepare the Transaction **/ const { txRaw, signDoc } = createTransaction({ pubKey, chainId, fee: DEFAULT_STD_FEE, message: msgs, sequence: baseAccount.sequence, timeoutHeight: timeoutHeight.toNumber(), accountNumber: baseAccount.accountNumber, })
Подписание сделки
После подготовки транзакции переходим к ее подписанию. После получения транзакции txRaw на предыдущем шаге используйте для подписания любой кошелек Cosmos native (например, Keplr),
import { ChainId } from '@injectivelabs/ts-types'
const getKeplr = async (chainId) => { await window.keplr.enable(chainId); const offlineSigner = window.keplr.getOfflineSigner(chainId); const accounts = await offlineSigner.getAccounts(); const key = await window.keplr.getKey(chainId);
return { offlineSigner, accounts, key } }
const { offlineSigner, accounts, key } = await getKeplr(ChainId.Mainnet)
/* Sign the Transaction */ const address = 'inj1' const signDoc = /* From the previous step */ const directSignResponse = await offlineSigner.signDirect(address, signDoc)
Вы также можете использовать наш пакет @injectivelabs/wallet-ts для получения готового кошелька, который предоставит вам абстрактные методы, которые вы можете использовать для подписания транзакций. Обратитесь к документации по этому пакету, он очень прост в настройке и использовании. Этот способ рекомендуется использовать, так как у вас есть доступ к нескольким кошелькам для использования в вашем dApp. WalletStrategy предоставляет не только абстракции подписания транзакций.
Трансляция транзакции
После того как подпись готова, нам необходимо транслировать транзакцию в цепочку Injective. Получив подпись на втором шаге, мы должны включить ее в подписанную транзакцию и транслировать ее в цепочку.
import { ChainId } from '@injectivelabs/ts-types' import { getTxRawFromTxRawOrDirectSignResponse, TxRestClient } from '@injectivelabs/sdk-ts' import { Network, getNetworkEndpoints } from '@injectivelabs/networks'
/** * IMPORTANT NOTE: * If we use Keplr/Leap wallets * after signing the transaction we get a `directSignResponse`, * and instead of adding the signature to the `txRaw` we create * using the `createTransaction` function we need to append the * signature from the `directSignResponse` to the transaction that * got actually signed (i.e `directSignResponse.signed`) and * the reason why is that the user can make some changes on the original * transaction (i.e change gas limit or gas prices) and the transaction * that get's signed and the one that gets broadcasted are not the same. */ const directSignResponse = /* From the second step above */ const txRaw = getTxRawFromTxRawOrDirectSignResponse(directSignResponse)
const broadcastTx = async (chainId, txRaw) => { const getKeplr = async (chainId) => { await window.keplr.enable(chainId);
const keplr = await getKeplr(ChainId.Mainnet) const result = await keplr.sendTx( chainId, txRaw.serializeBinary(), BroadcastMode.Sync, )
if (!result || result.length === 0) { throw new TransactionException( new Error('Transaction failed to be broadcasted'), { contextModule: 'Keplr' }, ) }
return Buffer.from(result).toString('hex') }
const txHash = await broadcastTx(ChainId.Mainnet, txRaw)
/** * Once we get the txHash, because we use the Sync mode we * are not sure that the transaction is included in the block, * it can happen that it's still in the mempool so we need to query * the chain to see when the transaction will be included */ const restEndpoint = 'https://lcd.injective.network' /* getNetworkEndpoints(Network.Mainnet).rest */ const txRestClient = new TxRestClient(restEndpoint)
/** This will poll querying the transaction and await for it's inclusion in the block */ const response = await txRestClient.fetchTxPoll(txHash)
Пример (подготовка + подписание + трансляция)
Рассмотрим весь поток (с использованием Keplr в качестве подписывающего кошелька)
import { MsgSend, ChainRestAuthApi, ChainRestTendermintApi, BaseAccount, DEFAULT_STD_FEE, createTransaction, } from '@injectivelabs/sdk-ts' import { DEFAULT_STD_FEE, DEFAULT_BLOCK_TIMEOUT_HEIGHT } from '@injectivelabs/utils' import { ChainId } from '@injectivelabs/ts-types' import { Network, getNetworkEndpoints } from '@injectivelabs/networks'
const getKeplr = async (chainId) => { await window.keplr.enable(chainId); const offlineSigner = window.keplr.getOfflineSigner(chainId); const accounts = await offlineSigner.getAccounts(); const key = await window.keplr.getKey(chainId);
return { offlineSigner, accounts, key } }
const broadcastTx = async (chainId, txRaw) => { const keplr = await getKeplr(ChainId.Mainnet) const result = await keplr.sendTx( chainId, txRaw.serializeBinary(), BroadcastMode.Sync, )
if (!result || result.length === 0) { throw new TransactionException( new Error('Transaction failed to be broadcasted'), { contextModule: 'Keplr' }, ) }
return Buffer.from(result).toString('hex') }
const injectiveAddress = 'inj1' const chainId = 'injective-1' /* ChainId.Mainnet */ const restEndpoint = 'https://lcd.injective.network' /* getNetworkEndpoints(Network.Mainnet).rest */ const amount = { amount: new BigNumberInBase(0.01).toWei().toFixed(), denom: "inj", };
/** Account Details **/ const chainRestAuthApi = new ChainRestAuthApi( restEndpoint, ) const accountDetailsResponse = await chainRestAuthApi.fetchAccount( injectiveAddress, ) const baseAccount = BaseAccount.fromRestApi(accountDetailsResponse) const accountDetails = baseAccount.toAccountDetails()
/** Block Details */ const chainRestTendermintApi = new ChainRestTendermintApi( restEndpoint, ) const latestBlock = await chainRestTendermintApi.fetchLatestBlock() const latestHeight = latestBlock.header.height const timeoutHeight = new BigNumberInBase(latestHeight).plus( DEFAULT_BLOCK_TIMEOUT_HEIGHT, )
/** Preparing the transaction */ const msg = MsgSend.fromJSON({ amount, srcInjectiveAddress: injectiveAddress, dstInjectiveAddress: injectiveAddress, });
/** Get the PubKey of the Signer from the Wallet/Private Key */ const pubKey = await getPubKey()
/** Prepare the Transaction **/ const { txRaw, signDoc } = createTransaction({ pubKey, chainId, fee: DEFAULT_STD_FEE, message: msgs, sequence: baseAccount.sequence, timeoutHeight: timeoutHeight.toNumber(), accountNumber: baseAccount.accountNumber, })
const directSignResponse = await offlineSigner.signDirect(injectiveAddress, signDoc) const txRaw = getTxRawFromTxRawOrDirectSignResponse(directSignResponse) const txHash = await broadcastTx(ChainId.Mainnet, txRaw) const response = await new TxRestClient(restEndpoint).fetchTxPoll(txHash)