pub trait Chain: Send + Sync {
// Required methods
fn id(&self) -> &'static str;
fn parse(&self, raw: &[u8]) -> Result<ParsedTx, ParseError>;
fn curve(&self) -> CurveType;
// Provided method
fn supports_version(&self, version: u8) -> bool { ... }
}Expand description
Trait for blockchain transaction parsers.
Each supported blockchain implements this trait to parse raw transaction
bytes into a unified ParsedTx structure that can be evaluated by the
policy engine.
§Implementor Requirements
Implementations must:
- Parse raw transaction bytes according to the chain’s encoding format
- Extract recipient address, amount, and token information
- Compute the transaction hash that will be signed
- Detect token operations (ERC-20, SPL, TRC-20, etc.)
- Set appropriate
TxTypefor policy evaluation - Return appropriate errors for malformed or unsupported transactions
§Thread Safety
Implementations must be Send + Sync to allow concurrent parsing
in the server’s async runtime. This is typically achieved by:
- Not storing mutable state in the parser
- Using interior mutability with proper synchronization if state is needed
§Example: Using with Trait Objects
use txgate_chain::Chain;
use txgate_core::{ParsedTx, error::ParseError};
use txgate_crypto::CurveType;
// Create a registry of chain parsers
struct ChainRegistry {
chains: Vec<Box<dyn Chain>>,
}
impl ChainRegistry {
fn find(&self, id: &str) -> Option<&dyn Chain> {
self.chains.iter().find(|c| c.id() == id).map(|c| c.as_ref())
}
}Required Methods§
Sourcefn id(&self) -> &'static str
fn id(&self) -> &'static str
Returns the chain identifier (e.g., “ethereum”, “bitcoin”, “solana”).
This identifier is used for:
- Chain lookup in the registry
- Logging and metrics
- Configuration matching
- Correlation with key storage
§Naming Convention
Chain identifiers should be:
- Lowercase
- Alphanumeric with hyphens for multi-word names
- Consistent with industry conventions
Examples: "ethereum", "bitcoin", "solana", "polygon", "arbitrum-one"
§Example
use txgate_chain::Chain;
struct EthereumParser;
impl Chain for EthereumParser {
fn id(&self) -> &'static str {
"ethereum"
}
}
let parser = EthereumParser;
assert_eq!(parser.id(), "ethereum");Sourcefn parse(&self, raw: &[u8]) -> Result<ParsedTx, ParseError>
fn parse(&self, raw: &[u8]) -> Result<ParsedTx, ParseError>
Parse raw transaction bytes into a ParsedTx.
This is the core method of the trait. It transforms chain-specific transaction bytes into a normalized format for policy evaluation.
§Arguments
raw- The raw transaction bytes in the chain’s native format
§Returns
Ok(ParsedTx)- Successfully parsed transaction with all fields populatedErr(ParseError)- Parsing failed
§Errors
This method returns a ParseError in the following cases:
ParseError::UnknownTxType- Transaction type byte is not recognizedParseError::InvalidRlp- RLP decoding failed (for Ethereum)ParseError::MalformedTransaction- Transaction structure is invalidParseError::MalformedCalldata- Contract call data is invalidParseError::InvalidAddress- Address format is invalid
§Transaction Hash
The returned ParsedTx.hash must be the exact hash that will be signed.
This is critical for security:
- For Ethereum legacy: Keccak256 of RLP-encoded transaction without signature
- For Ethereum EIP-155: Includes
chain_idin the signing hash - For Ethereum typed: Domain-separated hash with type prefix
- For Bitcoin: Double SHA256 of serialized transaction
- For Solana: SHA256 of serialized message
§Example
use txgate_chain::Chain;
use txgate_core::{ParsedTx, TxType, error::ParseError};
struct SimpleParser;
impl Chain for SimpleParser {
fn id(&self) -> &'static str { "test" }
fn parse(&self, raw: &[u8]) -> Result<ParsedTx, ParseError> {
if raw.is_empty() {
return Err(ParseError::MalformedTransaction {
context: "empty transaction data".to_string(),
});
}
// Parse the transaction...
Ok(ParsedTx::default())
}
}Sourcefn curve(&self) -> CurveType
fn curve(&self) -> CurveType
Returns the elliptic curve used by this chain.
This is used to select the appropriate signer for the chain. Most EVM-compatible chains use secp256k1, while Solana uses Ed25519.
§Curve Selection
CurveType::Secp256k1- Ethereum, Bitcoin, Tron, Ripple, most EVM chainsCurveType::Ed25519- Solana, NEAR, Cosmos (some chains)
§Example
use txgate_chain::Chain;
use txgate_crypto::CurveType;
struct SolanaParser;
impl Chain for SolanaParser {
fn id(&self) -> &'static str { "solana" }
fn curve(&self) -> CurveType {
CurveType::Ed25519
}
}
let parser = SolanaParser;
assert_eq!(parser.curve(), CurveType::Ed25519);Provided Methods§
Sourcefn supports_version(&self, version: u8) -> bool
fn supports_version(&self, version: u8) -> bool
Check if this parser supports a specific transaction version/type.
This method allows parsers to declare which transaction versions they can handle. The registry can use this to select the appropriate parser for a given transaction.
§Arguments
version- Chain-specific version identifier
§Returns
trueif this parser can handle the versionfalseotherwise
§Default Implementation
The default implementation returns true for all versions, which is
suitable for parsers that handle all known transaction types.
§Version Semantics
The meaning of version is chain-specific:
- Ethereum: Transaction type (0=legacy, 1=EIP-2930, 2=EIP-1559, 3=EIP-4844)
- Bitcoin: Transaction version field
- Solana: Message version (0=legacy, 128+=versioned)
§Example
use txgate_chain::Chain;
struct LegacyEthereumParser;
impl Chain for LegacyEthereumParser {
fn id(&self) -> &'static str { "ethereum-legacy" }
fn supports_version(&self, version: u8) -> bool {
// Only support legacy transactions (type 0)
version == 0
}
}
let parser = LegacyEthereumParser;
assert!(parser.supports_version(0)); // Legacy
assert!(!parser.supports_version(2)); // EIP-1559 not supported