# Raw signing Raw signing with Wallet-as-a-Service (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 Wallet-as-a-Service (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 Wallet-as-a-Service (Palisade). Raw signing can be enabled and disabled for a wallet from wallet settings. Disabled by default Raw 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 documentation See our [Wallet-as-a-Service (Palisade) API reference](/products/wallet/api-docs/palisade-api/palisade-api) 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:** 1. **Include v, r, s signature fields** — Set to `0` for unsigned transactions 2. **Apply outer RLP wrapper** — The typed transaction must be wrapped as an RLP byte string Common error If 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:** ```python 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:** ```go 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:** 1. Include the `SigningPubKey` field with the wallet's public key 2. Use `encodeForSigning` (not `encode`) for unsigned transactions **JavaScript example:** ```javascript 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:** ```python 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:** 1. Transaction must include a valid recent blockhash 2. Account keys must be properly ordered (fee payer first) 3. Use standard base64 encoding (not base58) **Python example:** ```python 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:** ```javascript 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:** 1. Transaction must include valid `ref_block_bytes` and `ref_block_hash` from a recent block 2. Expiration timestamp must be in the future (typically current time + 60 seconds, in milliseconds) 3. Fee limit must be set for TRC-20 transfers (recommended: 15,000,000 SUN = 15 TRX) 4. The signing hash is `SHA256(raw_data)` — Wallet-as-a-Service (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):** ```python 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 Wallet-as-a-Service (Palisade) encoded_transaction = raw_bytes.hex() # For TRC-20 transfers, use trigger_smart_contract instead ``` **JavaScript example (using TronWeb):** ```javascript 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:** ```go 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 format TRON addresses can be in base58 format (starts with `T`) or hex format (starts with `41`). The API accepts base58 addresses, but internally they are converted to 21-byte hex addresses in the protobuf. Reference block requirements TRON transactions require `ref_block_bytes` and `ref_block_hash` from 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.