Sending raw transaction and signature to the network

I have generated a ScriptTransaction from typescript sdk and want to generate signature separately without using the wallet client (basically generate ECDSA signature from txn data hash separately) and then send signature and txn to the network.
Is there a method that supports sending raw txn hash/bytes and signature to the network ?

Hey @singhparshant, did you see the docs around signing transactions?

You can also find the source code example (unit test) here.

Please let me know if that helps.

1 Like

@anderson Thanks for the reply.
Yes, I checked this, but i don’t see the option to add a from address, i think it uses the wallet address as the sender.
I want to assemble a custom transaction.

1 Like

Hello @singhparshant, how are you?

I am unsure If I understand what you are trying to achieve.

But I assume that is to get the hash of a ScriptTransaction and use it to generate an ECDSA signature, then add this signature to the ScriptTransaction before submitting it.

If so this can be achieved by the following approach:

import { splitSignature } from '@ethersproject/bytes';
import { hexToBytes } from '@ethereumjs/util';
import Web3 from 'web3';


const provider = await Provider.create(FUEL_NETWORK_URL);
const chainId = provider.getChainId();

// Transaction Hash (ID)
const hashedTransaction = scriptTransactionRequest.getTransactionId(chainId);

// If you are using web3
const web3 = new Web3('https://provider.url.here');
const privateKey = '0xYOUR_PRIVATE_KEY';
const account = web3.eth.accounts.privateKeyToAccount(privateKey);
web3.eth.accounts.wallet.add(account);
const signature = await web3.eth.personal.sign(hashedTransaction, account.address, account.privateKey)

// Transform the signature into compact form for Sway to understand, in case it will be used within a Sway program
const compactSignature = splitSignature(hexToBytes(signature)).compact;

// Add signature to the transaction witnesses
scriptTransactionRequest.witnesses.push(compactSignature);

const tx = await fuelWallet.sendTransaction(scriptTransactionRequest)
const response = await tx.waitForResult()

Please let me know if I misunderstood your request.

2 Likes

thank you. I think this is what i require. Will give it a try.
Quick question :
const compactSignature = splitSignature(hexToBytes(signature)).compact;

Is this functionality present in fuel-ts somewhere ?

1 Like

@singhparshant

No, we don’t have this functionality explicit defined on the fuels-ts.

But you can use the following NPM packages:

import { splitSignature } from '@ethersproject/bytes';
import { hexToBytes } from '@ethereumjs/util';
2 Likes

@Torres-ssf Looks like the hexToBytes method is deprecated:

 export const hexToBytes = (hex: string): Uint8Array => {
......
  return _unprefixedHexToBytes(hex)
}
The signature '(hex: string): Uint8Array' of '_unprefixedHexToBytes' is deprecated.ts(6387)
1 Like

Also, in this unit test, request.updateWitnessByOwner is being used, as opposed to the one that you suggested scriptTransactionRequest.witnesses.push(compactSignature)
Are they both same, do these results in similar transaction request ?

1 Like

Hello @singhparshant

You can use arrayify (which can be imported from fuels) instead of hexToBytes.

import { arrayify } from 'fuels'

The request.updateWitnessByOwner replaces a placeholder zeroed witness entry that was added on behalf of one account. It will extract the witness index from the resources added to the transaction request. It is a different use case.

Did the proposed solution here solved your problem?

1 Like

I am still working on it. I have some issues with React-Native currently.
Issue

@Torres-ssf I am assembling the txn and trying to send to the network, however i am getting NoSpendableInput error. I am not adding anything in the inputs array of the txn request, just pushing the signature. What should i push to the input array to make it work ? I don’t want to use wallet and the fee should be deducted from the sender’s input.
My code:

    signTxInformation.addCoinOutput(
      new Address(toBech32(B256)),
      transferAmount.toString(),
      this.client.getBaseAssetId()
    );

 const provider = this.core.client;
    const signature = new secp256k1.Signature(
      BigInt(bufferToHex(signatures[0].slice(0, 32))), // r
      BigInt(bufferToHex(signatures[0].slice(32, 64))) // s
    );
    console.log("after signture");

    const recoveryId = recoveryIdKeccak256(
      publicKey,
      messages[0],
      signatures[0]
    );

    console.log("After recoverId");

    // https://github.com/FuelLabs/fuels-ts/blob/e165e37307d65deda99f675723ea26470bf807d8/packages/account/src/signer/signer.ts#L54
    const r = toBytes(`0x${signature.r.toString(16)}`, 32);
    const s = toBytes(`0x${signature.s.toString(16)}`, 32);

    console.log("After r and s");

    // add recoveryParam to first s byte
    s[0] |= (recoveryId || 0) << 7;

    const finalSignature = hexlify(hexlify(concat([r, s])));
    console.log("After final signature");

    const recoveredAddress = Signer.recoverAddress(
      signTxInformation.getTransactionId(this.core.client.getChainId()),
      finalSignature
    );

    console.log("After recovered address", recoveredAddress);

    // Add the signature to the transaction
    // signTxInformation.updateWitnessByOwner(recoveredAddress, finalSignature);
    signTxInformation.witnesses.push(finalSignature);

    // Send transaction to the network
    const res = await provider.sendTransaction(signTxInformation);

    // Return the transaction hash
    console.log("Exxxx: ", res);

    return res.id;

Nevermind, found this getResourcesToSpend method in provider.
But the signature is invalid for some reason. Not sure if my signature generation is correct.