# transaction-processor-rs [Documentation](https://docs.rs/transaction-processor) Write transaction processors for Wavelet in Rust. ### What's a transaction processor? In Wavelet when a valid transaction is accepted, it is applied to the ledger state. A transaction processor defines what effects a transaction should have on the ledger state. For example, as defined in `builtin/money`, a transaction tagged `transfer`, if valid, should deduct `amount` PERLs from the sender account's balance and add the deducted balance to the `recipient` account. ### Write your own transaction processor A transaction processor is invoked every time a transaction gets applied. The entry function of a transaction processor should have the signature `(context: TransactionContext) -> ProcessResult<()>`. The parameter `context` contains the **tag** and **payload** of the transaction, where **tag** is for its type, and **payload** contains processor-defined information. Inside the entry function, you should match `context.tag` against your own defined tag, and only execute your logic if the tag is what you want. Otherwise, do nothing and return `Err(ProcessError::Ignore)`. After you've written your entry function, use the macro `processor_entry!(your_entry);` to register it. A transaction processor can only have one entry function. Let's take the builtin `money` processor (which handles the `transfer` transaction) as an example. First, we define all the required data structures for serializing/deserializing: ```rust #[derive(Deserialize)] pub struct Transfer { recipient: String, amount: u64, } #[derive(Serialize)] pub struct ContractReason { pub kind: String, pub details: TransferReason, } #[derive(Serialize)] pub struct TransferReason { pub amount: u64, pub sender: String, } ``` Followed by the entry function: ```rust // We name the entry as `handle_transaction`; Can change to whatever your want. fn handle_transaction(context: TransactionContext) -> ProcessResult<()> { // Match on the transaction tag. match context.tag.as_str() { "transfer" => { // Read and decode the payload as type `Transfer`. let payload: Transfer = context.read_payload()?; // Load transaction sender. // Currently, most information about the transaction is kept as immutable global state. let sender = Account::sender(); // Load transaction recipient with `payload.recipient` as its ID. let recipient = Account::load(&payload.recipient); // Invoke the builtin `transfer` operation. transaction_processor::transfer::transfer(&sender, &recipient, payload.amount)?; // Build activation reason for the smart contract system. let reason = transaction_processor::serde_json::to_vec(&ContractReason { kind: "transfer".into(), details: TransferReason { amount: payload.amount, sender: Account::sender_id(), }, }).unwrap(); // If the recipient is a smart contract, activate it. transaction_processor::contract::activate(&payload.recipient, &reason)?; Ok(()) } _ => Err(ProcessError::Ignore), // Ignore any transactions we don't understand. } } ``` Register the entry: ```rust processor_entry!(handle_transaction); ``` ### Build Make sure you have the latest stable Rust toolchain with the `wasm32-unknown-unknown` target installed. If you don't have the target installed yet, install it with: ``` rustup target add wasm32-unknown-unknown ``` Then, run in your project directory: ``` cargo build --release --target wasm32-unknown-unknown ``` ## Contributions We at Perlin love reaching out to the open-source community and are open to accepting issues and pull-requests. For all code contributions, please ensure they adhere as close as possible to the following guidelines: 1. Fix all warnings from the Rust compiler, unless in some very special cases. 2. Commit messages are in the format `module_name: Change typed down as a sentence.` This allows our maintainers and everyone else to know what specific code changes you wish to address. 3. Consider backwards compatibility. New methods are perfectly fine, though changing the existing public API for example should only be done should there be a good reason. If you... 1. love the work we are doing, 2. want to work full-time with us, 3. or are interested in getting paid for working on open-source projects ... **we're hiring**. To grab our attention, just make a PR and start contributing.