Raw signing
Raw signing with Palisade offers the ability to sign any transaction type available to a blockchain. It can be used on any transaction type if enabled.
This is an important feature of the Palisade platform as it means customers can benefit from all natively supported transaction types of a blockchain as soon as they become available, regardless of whether that blockchain or transaction type is currently supported by Palisade.
Raw signing can be enabled and disabled for a wallet from wallet settings.
Important: disabled by defaultRaw signing is a powerful and insecure signing method. It is therefore disabled by default. Please only enable raw signing for individual wallets if you fully understand this feature.
API docsSee our Raw Signing API docs for information on how to submit raw transactions via the API.
Transaction Encoding Formats
When using the Raw Transaction API, the encodedTransaction field must be encoded in a blockchain-specific format. This section details the exact encoding requirements for each supported blockchain.
EVM Chains (Ethereum, Base, Polygon, Avalanche, Arbitrum, BNB Chain)
The encodedTransaction field must match the output of go-ethereum's rlp.EncodeToBytes(types.Transaction).
Format:
RLP(type_byte || RLP([chainId, nonce, maxPriorityFeePerGas, maxFeePerGas, gas, to, value, data, accessList, v, r, s]))
Key requirements:
- Include v, r, s signature fields — Set to
0for unsigned transactions - Apply outer RLP wrapper — The typed transaction must be wrapped as an RLP byte string
Common errorIf you receive
"typed transaction too short", you're likely using the standard unsigned format (0x02 || RLP([9 fields])) which is missing the v/r/s placeholders and outer RLP wrapper.
Python example:
from eth_account.typed_transactions import DynamicFeeTransaction
import rlp
# Use the signed transaction serializer (includes v, r, s fields)
serializer = DynamicFeeTransaction._signed_transaction_serializer
tx = serializer(
chainId=84532, # Base Sepolia
nonce=nonce,
maxPriorityFeePerGas=max_priority,
maxFeePerGas=max_fee,
gas=gas_limit,
to=bytes.fromhex(to_address[2:]),
value=0,
data=calldata_bytes,
accessList=(),
v=0, # Placeholder for unsigned
r=0, # Placeholder for unsigned
s=0, # Placeholder for unsigned
)
# RLP encode the fields
tx_rlp = rlp.encode(tx)
# Add EIP-1559 type prefix (0x02)
typed_tx = bytes([2]) + tx_rlp
# Wrap in RLP string (matches go-ethereum's rlp.EncodeToBytes)
encoded_transaction = rlp.encode(typed_tx).hex()Go example:
import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
)
tx := types.NewTx(&types.DynamicFeeTx{
ChainID: big.NewInt(84532),
Nonce: nonce,
GasTipCap: maxPriorityFeePerGas,
GasFeeCap: maxFeePerGas,
Gas: gasLimit,
To: &toAddress,
Value: big.NewInt(0),
Data: calldata,
})
encodedBytes, _ := rlp.EncodeToBytes(tx)
encodedTransaction := hex.EncodeToString(encodedBytes)XRP Ledger
The encodedTransaction field must use the XRP Binary Codec format with encodeForSigning.
Format: Hex-encoded binary codec output of the transaction JSON
Key requirements:
- Include the
SigningPubKeyfield with the wallet's public key - Use
encodeForSigning(notencode) for unsigned transactions
JavaScript example:
const { encodeForSigning } = require('ripple-binary-codec');
const tx = {
Account: "rSourceAddress...",
Destination: "rDestAddress...",
Amount: "1000000", // In drops
Fee: "10",
Sequence: 12345,
SigningPubKey: "02ECE63017B0FEFC...", // Required
TransactionType: "Payment"
};
const encodedTransaction = encodeForSigning(tx);Python example:
from xrpl.core.binarycodec import encode_for_signing
tx = {
"Account": "rSourceAddress...",
"Destination": "rDestAddress...",
"Amount": "1000000",
"Fee": "10",
"Sequence": 12345,
"SigningPubKey": "02ECE63017B0FEFC...",
"TransactionType": "Payment"
}
encoded_transaction = encode_for_signing(tx)Solana
The encodedTransaction field must be a base64-encoded serialized Solana transaction.
Format: base64(transaction.MarshalBinary())
Key requirements:
- Transaction must include a valid recent blockhash
- Account keys must be properly ordered (fee payer first)
- Use standard base64 encoding (not base58)
Python example:
from solders.transaction import Transaction
from solders.message import Message
import base64
# Build your transaction message
message = Message.new_with_blockhash(
instructions,
payer,
blockhash
)
# Create unsigned transaction
tx = Transaction.new_unsigned(message)
# Serialize and base64 encode
tx_bytes = bytes(tx)
encoded_transaction = base64.b64encode(tx_bytes).decode('utf-8')JavaScript example:
const { Transaction } = require('@solana/web3.js');
const tx = new Transaction();
tx.recentBlockhash = blockhash;
tx.feePayer = payerPublicKey;
tx.add(instruction);
// Serialize (without signing)
const serialized = tx.serialize({ requireAllSignatures: false });
const encodedTransaction = serialized.toString('base64');TRON
The encodedTransaction field must be a hex-encoded TRON Transaction protobuf.
Format: hex(proto.Marshal(Transaction))
Key requirements:
- Transaction must include valid
ref_block_bytesandref_block_hashfrom a recent block - Expiration timestamp must be in the future (typically current time + 60 seconds, in milliseconds)
- Fee limit must be set for TRC-20 transfers (recommended: 15,000,000 SUN = 15 TRX)
- The signing hash is
SHA256(raw_data)— Palisade computes this automatically
Supported contract types:
| Contract Type | Description |
|---|---|
TransferContract | Native TRX transfers |
TriggerSmartContract | TRC-20 token transfers and smart contract calls |
Python example (using tronpy):
from tronpy import Tron
client = Tron(network='shasta') # or 'mainnet'
# Build a TRX transfer transaction
txn = (
client.trx.transfer(
from_="TSourceAddress...",
to="TDestAddress...",
amount=1_000_000 # 1 TRX in SUN
)
.fee_limit(1_000_000) # 1 TRX fee limit
.build()
)
# Get the raw transaction bytes (protobuf serialized)
raw_bytes = txn._raw_data.SerializeToString()
# Hex encode for Palisade
encoded_transaction = raw_bytes.hex()
# For TRC-20 transfers, use trigger_smart_contract insteadJavaScript example (using TronWeb):
const TronWeb = require('tronweb');
const tronWeb = new TronWeb({
fullHost: 'https://api.shasta.trongrid.io', // or mainnet
});
// Build a TRX transfer transaction
const tx = await tronWeb.transactionBuilder.sendTrx(
'TDestAddress...', // to
1000000, // amount in SUN (1 TRX)
'TSourceAddress...' // from
);
// The transaction object contains raw_data_hex
const encodedTransaction = tx.raw_data_hex;Go example:
import (
"encoding/hex"
"time"
"github.com/fbsobreira/gotron-sdk/pkg/proto/core"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
)
// Build a TRX transfer
transfer := &core.TransferContract{
OwnerAddress: ownerAddrBytes, // 21-byte TRON address
ToAddress: toAddrBytes,
Amount: 1000000, // 1 TRX in SUN
}
anyValue, _ := anypb.New(transfer)
tx := &core.Transaction{
RawData: &core.TransactionRaw{
Contract: []*core.Transaction_Contract{{
Type: core.Transaction_Contract_TransferContract,
Parameter: anyValue,
}},
RefBlockBytes: refBlockBytes, // From recent block
RefBlockHash: refBlockHash,
Expiration: time.Now().Add(60*time.Second).UnixMilli(),
Timestamp: time.Now().UnixMilli(),
},
}
txBytes, _ := proto.Marshal(tx)
encodedTransaction := hex.EncodeToString(txBytes)
TRON address formatTRON addresses can be in base58 format (starts with
T) or hex format (starts with41). The API accepts base58 addresses, but internally they are converted to 21-byte hex addresses in the protobuf.
Reference block requirementsTRON transactions require
ref_block_bytesandref_block_hashfrom a recent block (within ~18 hours). If you're building transactions manually, fetch the latest block and extract bytes 6-8 of the block number and bytes 8-16 of the block hash.
Updated about 2 months ago
