import {
ChainRestAuthApi,
ChainRestTendermintApi,
BaseAccount,
DEFAULT_STD_FEE
TxRestApi,
createTransaction,
createTxRawEIP712,
createWeb3Extension,
SIGN_AMINO
} from '@injectivelabs/sdk-ts'
import { TypedDataUtils } from 'eth-sig-util'
import { bufferToHex, addHexPrefix } from 'ethereumjs-util'
import EthereumApp from '@ledgerhq/hw-app-eth'
import {
getEip712TypedData,
Eip712ConvertTxArgs,
Eip712ConvertFeeArgs
} from '@injectivelabs/sdk-ts/dist/core/eip712'
import { EtherumChainId, CosmosChainId } from '@injectivelabs/ts-types'
import { BigNumberInBase, DEFAULT_BLOCK_TIMEOUT_HEIGHT } from '@injectivelabs/utils'
const domainHash = (message: any) =>
TypedDataUtils.hashStruct('EIP712Domain', message.domain, message.types, true)
const messageHash = (message: any) =>
TypedDataUtils.hashStruct(
message.primaryType,
message.message,
message.types,
true,
)
const signTransaction = async (eip712TypedData: any) => {
const transport = /* Get the transport from Ledger */
const ledger = new EthereumApp(transport)
const derivationPath = /* Get the derivation path for the address */
/* eip712TypedData from Step 1 */
const result = await ledger.signEIP712HashedMessage(
derivationPath,
bufferToHex(domainHash(eip712TypedData)),
bufferToHex(messageHash(eip712TypedData)),
)
const combined = `${result.r}${result.s}${result.v.toString(16)}`
const signature = combined.startsWith('0x') ? combined : `0x${combined}`
return signature;
}
const getAccountDetails = (address: string): BaseAccount => {
const chainRestAuthApi = new ChainRestAuthApi(
lcdEndpoint,
)
const accountDetailsResponse = await chainRestAuthApi.fetchAccount(
address,
)
const baseAccount = BaseAccount.fromRestApi(accountDetailsResponse)
const accountDetails = baseAccount.toAccountDetails()
return accountDetails
}
const getTimeoutHeight = () => {
const chainRestTendermintApi = new ChainRestTendermintApi(
lcdEndpoint,
)
const latestBlock = await chainRestTendermintApi.fetchLatestBlock()
const latestHeight = latestBlock.header.height
const timeoutHeight = latestHeight + DEFAULT_BLOCK_TIMEOUT_HEIGHT
return timeoutHeight
}
const address = 'inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku'
const chainId = CosmosChainId.Injective
const ethereumChainId = EthereumChainId.Mainnet
const accountDetails = getAccountDetails()
const timeoutHeight = getTimeoutHeight
const txArgs: Eip712ConvertTxArgs = {
accountNumber: accountDetails.accountNumber.toString(),
sequence: accountDetails.sequence.toString(),
timeoutHeight: timeoutHeight.toString(),
chainId: chainId,
}
const txFeeArgs: Eip712ConvertFeeArgs = DEFAULT_STD_FEE
const injectiveAddress = 'inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku'
const amount = {
amount: new BigNumberInBase(0.01).toWei().toFixed(),
denom: "inj",
};
const msg = MsgSend.fromJSON({
amount,
srcInjectiveAddress: injectiveAddress,
dstInjectiveAddress: injectiveAddress,
});
/** The EIP712 TypedData that can be used for signing **/
const eip712TypedData = getEip712Tx({
msgs: msg,
tx: txArgs,
fee: txFeeArgs
ethereumChainId: ethereumChainId,
})
/** Signing on Ethereum */
const signature = await signTransaction(eip712TypedData)
/** Preparing the transaction for client broadcasting */
const { txRaw } = createTransaction({
message: msg,
memo: '',
signMode: SIGN_AMINO,
fee: DEFAULT_STD_FEE,
pubKey: publicKeyBase64,
sequence: accountDetails.sequence,
timeoutHeight: timeoutHeight.toNumber(),
accountNumber: accountDetails.accountNumber,
chainId: chainId,
})
const web3Extension = createWeb3Extension({
ethereumChainId,
})
const txRawEip712 = createTxRawEIP712(txRaw, web3Extension)
/** Append Signatures */
const signatureBuff = Buffer.from(signature.replace('0x', ''), 'hex')
txRawEip712.signatures = [signatureBuff]
/** Broadcast the transaction **/
const txRestApi = new TxRestApi(lcdEndpoint)
const response = await txRestApi.broadcast(txRawEip712)
if (response.code !== 0) {
throw new Error(`Transaction failed: ${response.rawLog}`)
}
return response.txhash