Babylon Transaction Signing

1. Prepare the Unsigned Transaction

Obtain the unsigned transaction in the required format (Babylon transaction object). Typically, this is provided by the API endpoint or a transaction builder.
Example (object form):

{
  "messages": [/*...Babylon messages...*/],
  "fee": {
    "amount": [{"denom": "ubbn", "amount": "1000"}],
    "gas": "200000"
  },
  "memo": ""
}

2. Sign the Transaction Using the Script Below

import { DirectSecp256k1HdWallet, Registry, GeneratedType } from '@cosmjs/proto-signing';
import { SigningCosmWasmClient, defaultRegistryTypes } from '@cosmjs/cosmwasm-stargate';
import { TxRaw } from 'cosmjs-types/cosmos/tx/v1beta1/tx';
import axios from 'axios';

// Replace with actual Babylon message types if needed
import {
  MsgWrappedDelegate,
  MsgWrappedUndelegate,
} from '@babylonlabs-io/babylon-proto-ts/dist/generated/babylon/epoching/v1/tx';

const BABYLON_RPC = '<BABYLON_NODE_RPC>'; // e.g. https://rpc.testnet.babylonchain.io:443
const BABYLON_API_URL = '<P2P_API_URL>';  // e.g. https://api.testnet.p2p.org
const BABYLON_API_KEY = '<P2P_API_KEY>';
const BABYLON_NETWORK = '<NETWORK_NAME>'; // e.g. testnet, mainnet
const BABYLON_MNEMONIC = '<MNEMONIC>';
const BABYLON_ADDRESS = '<BBN_ADDRESS>';  // e.g. bbn1xyz...

const UNSIGNED_TX = {
  // Paste your unsigned transaction object here
  messages: [],
  fee: {
    amount: [{ denom: 'ubbn', amount: '1000' }],
    gas: '200000',
  },
  memo: '',
};

function getBabylonRegistry(): Registry {
  const registry = new Registry([...defaultRegistryTypes]);
  registry.register(
    '/babylon.epoching.v1.MsgWrappedDelegate',
    MsgWrappedDelegate as unknown as GeneratedType
  );
  registry.register(
    '/babylon.epoching.v1.MsgWrappedUndelegate',
    MsgWrappedUndelegate as unknown as GeneratedType
  );
  return registry;
}

async function signTransaction(
  mnemonic: string,
  address: string,
  unsignedTx: any
): Promise<string> {
  const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix: 'bbn' });
  const registry = getBabylonRegistry();
  const client = await SigningCosmWasmClient.connectWithSigner(BABYLON_RPC, wallet, { registry });

  const { bodyBytes, authInfoBytes, signatures } = await client.sign(
    address,
    unsignedTx.messages,
    unsignedTx.fee,
    unsignedTx.memo
  );

  const txRaw = TxRaw.fromPartial({
    bodyBytes,
    authInfoBytes,
    signatures,
  });

  // Return the signed transaction as hex string
  return Buffer.from(TxRaw.encode(txRaw).finish()).toString('hex');
}

async function broadcastTransaction(signedTxHex: string): Promise<string> {
  const endpoint = `${BABYLON_API_URL}/api/v1/babylon/${BABYLON_NETWORK}/transaction/send`;
  const response = await axios.post(
    endpoint,
    { signedTransaction: signedTxHex },
    {
      headers: {
        accept: 'application/json',
        'content-type': 'application/json',
        authorization: `Bearer ${BABYLON_API_KEY}`,
      },
    }
  );
  if (!response.data.result?.transactionHash) {
    throw new Error(`Broadcast failed: ${JSON.stringify(response.data)}`);
  }
  return response.data.result.transactionHash;
}

async function main() {
  if (!BABYLON_MNEMONIC || !BABYLON_ADDRESS) {
    throw new Error('❌ Babylon mnemonic or address is missing.');
  }
  if (!UNSIGNED_TX.messages || !Array.isArray(UNSIGNED_TX.messages)) {
    throw new Error('❌ Invalid unsigned transaction.');
  }

  const signedTxHex = await signTransaction(BABYLON_MNEMONIC, BABYLON_ADDRESS, UNSIGNED_TX);

  console.log('✅ Signed Babylon Tx (hex):');
  console.log(signedTxHex);

  // Broadcast (optional, can be run separately)
  const txHash = await broadcastTransaction(signedTxHex);

  console.log('✅ Broadcasted Tx Hash:', txHash);
}

main().catch(console.error);

3. On Successful Execution

The script prints the signed transaction in hex format, then broadcasts it. Example output:

✅ Signed Babylon Tx (hex):
aabbcc...deadbeef

✅ Broadcasted Tx Hash:
BABYLON_TX_HASH_HERE

What's Next?