Skip to content

Commit 20edb6c

Browse files
committed
feat: add broadcastWithFeePayerSig to handle rfq gw/rfq executor
1 parent 703c0a5 commit 20edb6c

2 files changed

Lines changed: 142 additions & 3 deletions

File tree

packages/sdk-ts/src/core/tx/broadcaster/MsgBroadcasterWithPk.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
getNetworkInfo,
55
getNetworkEndpoints,
66
} from '@injectivelabs/networks'
7+
import * as CosmosTxV1Beta1TxPb from '@injectivelabs/core-proto-ts-v2/generated/cosmos/tx/v1beta1/tx_pb'
78
import {
89
getStdFee,
910
toBigNumber,
@@ -14,7 +15,6 @@ import { createTransaction } from '../tx.js'
1415
import { TxGrpcApi } from '../api/TxGrpcApi.js'
1516
import { ofacList } from '../../../utils/ofac.js'
1617
import { PrivateKey } from '../../accounts/index.js'
17-
import { uint8ArrayToHex } from '../../../utils/encoding.js'
1818
import { IndexerGrpcWeb3GwApi } from '../../../client/index.js'
1919
import { getGasPriceBasedOnMessage } from '../../../utils/msgs.js'
2020
import {
@@ -25,9 +25,13 @@ import {
2525
ChainRestAuthApi,
2626
ChainRestTendermintApi,
2727
} from '../../../client/chain/rest/index.js'
28+
import {
29+
uint8ArrayToHex,
30+
hexToUint8Array,
31+
base64ToUint8Array,
32+
} from '../../../utils/encoding.js'
2833
import type { NetworkEndpoints } from '@injectivelabs/networks'
2934
import type { ChainId, EvmChainId } from '@injectivelabs/ts-types'
30-
import type * as CosmosTxV1Beta1TxPb from '@injectivelabs/core-proto-ts-v2/generated/cosmos/tx/v1beta1/tx_pb'
3135
import type { Msgs } from '../../modules/msgs.js'
3236
import type { AccountDetails } from '../../../types/auth.js'
3337
import type { CreateTransactionArgs } from '../types/index.js'
@@ -442,6 +446,39 @@ export class MsgBroadcasterWithPk {
442446
return toBigNumber(latestHeight.toString()).plus(txTimeout)
443447
}
444448

449+
async broadcastWithFeePayerSig({
450+
tx,
451+
feePayerSig,
452+
accountNumber,
453+
}: {
454+
feePayerSig: string
455+
accountNumber: number
456+
tx: Uint8Array | string
457+
}) {
458+
const { chainId, privateKey } = this
459+
460+
const txBytes = typeof tx === 'string' ? base64ToUint8Array(tx) : tx
461+
const txRaw = CosmosTxV1Beta1TxPb.TxRaw.fromBinary(txBytes)
462+
463+
const signDoc = CosmosTxV1Beta1TxPb.SignDoc.create({
464+
chainId,
465+
bodyBytes: txRaw.bodyBytes,
466+
authInfoBytes: txRaw.authInfoBytes,
467+
accountNumber: BigInt(accountNumber),
468+
})
469+
470+
const signDocBytes = CosmosTxV1Beta1TxPb.SignDoc.toBinary(signDoc)
471+
const signature = await privateKey.sign(signDocBytes)
472+
473+
const feePayerSigBytes = feePayerSig.startsWith('0x')
474+
? hexToUint8Array(feePayerSig.slice(2))
475+
: base64ToUint8Array(feePayerSig)
476+
477+
txRaw.signatures = [signature, feePayerSigBytes]
478+
479+
return await this.broadcastTxRaw(txRaw)
480+
}
481+
445482
private async broadcastTxRaw(txRaw: CosmosTxV1Beta1TxPb.TxRaw) {
446483
const { endpoints, txTimeout } = this
447484
const txResponse = await new TxGrpcApi(endpoints.grpc).broadcast(txRaw, {

packages/wallets/wallet-core/src/broadcaster/MsgBroadcaster.ts

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,14 @@ import {
6161
getAminoStdSignDoc,
6262
getEip712TypedData,
6363
createWeb3Extension,
64+
CosmosTxV1Beta1TxPb,
6465
getEip712TypedDataV2,
6566
createTxRawFromSigResponse,
6667
createTransactionWithSigners,
6768
} from '@injectivelabs/sdk-ts/core/tx'
6869
import { checkIfTxRunOutOfGas } from '../utils/index.js'
6970
import type { NetworkEndpoints } from '@injectivelabs/networks'
7071
import type { ThrownException } from '@injectivelabs/exceptions'
71-
import type { CosmosTxV1Beta1TxPb } from '@injectivelabs/sdk-ts'
7272
import type { DirectSignResponse } from '@injectivelabs/sdk-ts/types'
7373
import type { Wallet as WalletType } from '@injectivelabs/wallet-base'
7474
import type {
@@ -348,6 +348,108 @@ export class MsgBroadcaster {
348348
}
349349
}
350350

351+
/**
352+
* Sign and broadcast a pre-built transaction that already includes
353+
* a fee payer's signature, directly to chain (bypassing web3gw).
354+
*/
355+
async broadcastWithFeePayerSig({
356+
tx,
357+
feePayerSig,
358+
accountNumber,
359+
injectiveAddress,
360+
}: {
361+
feePayerSig: string
362+
accountNumber: number
363+
tx: Uint8Array | string
364+
injectiveAddress: string
365+
}): Promise<TxResponse> {
366+
const {
367+
chainId,
368+
endpoints,
369+
walletStrategy,
370+
txTimeout: txTimeoutInBlocks,
371+
} = this
372+
373+
if (!isCosmosWallet(walletStrategy.wallet)) {
374+
throw new GeneralException(
375+
new Error(
376+
'broadcastWithFeePayerSig only supports Cosmos wallets (Keplr, Leap, etc.). EVM wallets are not supported for SIGN_MODE_DIRECT.',
377+
),
378+
)
379+
}
380+
381+
const txBytes = typeof tx === 'string' ? base64ToUint8Array(tx) : tx
382+
const txRaw = CosmosTxV1Beta1TxPb.TxRaw.fromBinary(txBytes)
383+
384+
const cosmosWallet = walletStrategy.getCosmosWallet(chainId)
385+
const canDisableCosmosGasCheck = (
386+
[Wallet.Keplr, Wallet.OWallet] as WalletType[]
387+
).includes(walletStrategy.wallet)
388+
389+
if (canDisableCosmosGasCheck && cosmosWallet.disableGasCheck) {
390+
cosmosWallet.disableGasCheck(chainId)
391+
}
392+
393+
try {
394+
walletStrategy.emit(
395+
WalletStrategyEmitterEventType.TransactionPreparationStart,
396+
)
397+
398+
const directSignResponse = (await walletStrategy.signCosmosTransaction({
399+
txRaw,
400+
chainId,
401+
accountNumber,
402+
address: injectiveAddress,
403+
})) as DirectSignResponse
404+
405+
walletStrategy.emit(
406+
WalletStrategyEmitterEventType.TransactionPreparationEnd,
407+
)
408+
409+
const signedTxRaw = CosmosTxV1Beta1TxPb.TxRaw.create()
410+
signedTxRaw.bodyBytes = directSignResponse.signed.bodyBytes
411+
signedTxRaw.authInfoBytes = directSignResponse.signed.authInfoBytes
412+
413+
const takerSig = base64ToUint8Array(
414+
directSignResponse.signature.signature,
415+
)
416+
const feePayerSigBytes = feePayerSig.startsWith('0x')
417+
? hexToUint8Array(feePayerSig.slice(2))
418+
: base64ToUint8Array(feePayerSig)
419+
420+
signedTxRaw.signatures = [takerSig, feePayerSigBytes]
421+
422+
walletStrategy.emit(
423+
WalletStrategyEmitterEventType.TransactionBroadcastStart,
424+
)
425+
426+
const txResponse = await new TxGrpcApi(endpoints.grpc).broadcast(
427+
signedTxRaw,
428+
{ txTimeout: txTimeoutInBlocks },
429+
)
430+
431+
walletStrategy.emit(
432+
WalletStrategyEmitterEventType.TransactionBroadcastEnd,
433+
)
434+
435+
return txResponse
436+
} catch (e) {
437+
const error = e as any
438+
439+
walletStrategy.emit(WalletStrategyEmitterEventType.TransactionFail)
440+
441+
if (isThrownException(error)) {
442+
throw error
443+
}
444+
445+
throw new TransactionException(new Error(error))
446+
} finally {
447+
if (canDisableCosmosGasCheck && cosmosWallet.enableGasCheck) {
448+
cosmosWallet.enableGasCheck(chainId)
449+
}
450+
}
451+
}
452+
351453
/**
352454
* Prepare/sign/broadcast transaction using
353455
* Ethereum native wallets on the client side.

0 commit comments

Comments
 (0)