# whisky

Whisky is an open-source Cardano Rust SDK, containing the following modules:

* `whisky`- The core Rust crate supporting Cardano DApp development in Rust.
* `whisky-common`- Serving universal types and utilities.
* `whisky-csl`- The crate to implement most `cardano-serialization-lib` wrapper
* `whisky-js`- A point of output for the wasm package for `@meshsdk/core-csl`.

With whisky, you can

* Builder transaction with cardano-cli-like APIs, supporting serious DApps’ backend on the Rust codebase.
* Handling transaction signing in Rust
* Interacting with blockchain with provider services like `Maestro` and `Blockfrost`
* Off-node evaluation on transaction execution units, and updating the transaction accordingly with TxPipe's `uplc` integrated.

### Installation

#### Rust Library

```sh
cargo add whisky
```

#### JS / TS WASM Lib

```sh
# For nodejs package
yarn add @sidan-lab/whisky-js-nodejs
# For browser package
yarn add @sidan-lab/whisky-js-browser
```

### Full API Documentation

Please refer to the [hosted documentation](https://sidan-lab.github.io/whisky/whisky/index.html) for the list of endpoints.


# WASM - whisky-js

All TypeScript libraries, like [MeshJS](https://meshjs.dev/), can build complex transactions on top of infrastructure from `whisky` as long as it is with a compliant JSON string object as defined [here](https://sidan-lab.github.io/whisky/sidan_csl_rs/model/struct.MeshTxBuilderBody.html). The wasm endpoint implementation is documented [here](https://sidan-lab.github.io/whisky/sidan_csl_rs/builder/fn.js_serialize_tx_body.html).

For other utils outputted by `whisky-js` the wrapper would be implemented in Mesh V2.0.


# Getting Started

To start building an customized transaction, you need to first initialize `TxBuilder`:

```rust
let tx_builder_params = TxBuilderParam {
    evaluator: None,
    fetcher: None,
    submitter: None,
    params: None,
};
let mut tx_builder = TxBuilder::new(tx_builder_params);
```

There are 4 optional fields to pass in to initialize the `TxBuilder` instance:

1. `fetcher` - Refer to [service integration](/tx-builder/service-integration).
2. `submitter` - Refer to [service integration](/tx-builder/service-integration).
3. `evaluator` - Refer to [service integration](/tx-builder/service-integration).
4. `params`You can pass in the protocol parameters directly.

For details about providers' eligibility for each service role, please refer to the [providers'](/services/providers) session.

Alternatively, if you do not need any of the provider services, you can initialize a `TxBuilder` with the `new_core` method:

```rust
let mut tx_builder = TxBuilder::new_core();
```

### Remarks

* Only `evaluator` service with off-node evaluation is integrated at the moment. Other integrations, as described above have been planned and are coming soon.


# Lock Fund

### Build a transaction to send funds to a smart contract

The following shows a simple example of building a transaction to lock fund in a smart contact.

```rust
use whisky::*;

pub fn lock_fund(
    script_address: &str,
    datum: &str,
    my_address: &str,
    inputs: &[UTxO],
) -> Result<String, WError> {
    let mut tx_builder = TxBuilder::new_core();
    tx_builder
        .tx_out(script_address, &[])
        .tx_out_inline_datum_value(&WData::JSON(datum.to_string())) // JSON string datum
        // .tx_out_datum_hash_value(WData::JSON(datum.to_string())) // Datum hash
        .change_address(my_address)
        .select_utxos_from(inputs, 5000000)
        .complete_sync(None)?;

    Ok(tx_builder.tx_hex())
}
```


# Unlock Fund

### Build a transaction to unlock funds from a smart contract

The following shows a simple example of building a transaction to unlock funds from a smart contract. In this example, we use an async function `.complete()` instead of `.completeSync()`. With this completing logic, it would perform auto redeemer update with `aiken's uplc`.

```rust
use whisky::*;

pub async fn unlock_fund(
    script_utxo: &UTxO,
    redeemer: &str,
    script: &ProvidedScriptSource,
    my_address: &str,
    inputs: &[UTxO],
    collateral: &UTxO,
) -> Result<String, WError> {
    let mut tx_builder = TxBuilder::new_core();
    let pub_key_hash = deserialize_address(my_address).pub_key_hash;

    tx_builder
        // .spending_plutus_script_v1()
        .spending_plutus_script_v2()
        // .spending_plutus_script_v3()
        .tx_in(
            &script_utxo.input.tx_hash,
            script_utxo.input.output_index,
            &script_utxo.output.amount,
            &script_utxo.output.address,
        )
        .tx_in_inline_datum_present()
        // .tx_in_datum_value(datum here) or provide datum value
        .tx_in_redeemer_value(&WRedeemer {
            data: WData::JSON(redeemer.to_string()),
            ex_units: Budget { mem: 0, steps: 0 },
        })
        .tx_in_script(&script.script_cbor)
        // .spending_tx_in_reference(tx_hash, tx_index, script_hash, script_size)
        .change_address(my_address)
        .required_signer_hash(&pub_key_hash) // Extra logic impl
        .tx_in_collateral(
            &collateral.input.tx_hash,
            collateral.input.output_index,
            &collateral.output.amount,
            &collateral.output.address,
        )
        .input_for_evaluation(script_utxo)
        .select_utxos_from(inputs, 5000000)
        .complete(None)
        .await?;

    Ok(tx_builder.tx_hex())
}
```


# Mint Tokens

### Build a transaction to (register stake certificate and) delegate stake to a pool

The following shows a simple example of building a transaction to mint a token with a smart contract.

```rust
use whisky::*;

pub async fn mint_tokens(
    to_mint_asset: &Asset,
    redeemer: &str,
    script: &ProvidedScriptSource,
    my_address: &str,
    inputs: &[UTxO],
    collateral: &UTxO,
) -> Result<String, WError> {
    let mut tx_builder = TxBuilder::new_core();

    tx_builder
        // .mint_plutus_script_v1()
        .mint_plutus_script_v2()
        // .mint_plutus_script_v3()
        .mint(
            to_mint_asset.quantity_i128(),
            &to_mint_asset.policy(),
            &to_mint_asset.name(),
        )
        .minting_script(&script.script_cbor)
        // .mint_tx_in_reference(tx_hash, tx_index, script_hash, script_size) // For reference scripts
        .mint_redeemer_value(&WRedeemer {
            data: WData::JSON(redeemer.to_string()),
            ex_units: Budget { mem: 0, steps: 0 },
        })
        .change_address(my_address)
        .tx_in_collateral(
            &collateral.input.tx_hash,
            collateral.input.output_index,
            &collateral.output.amount,
            &collateral.output.address,
        )
        .select_utxos_from(inputs, 5000000)
        .complete(None)
        .await?;

    Ok(tx_builder.tx_hex())
}
```


# Delegate Stake

### Build a transaction to (register stake certificate and) delegate stake to a pool

The following shows a simple example of building a transaction to (register stake certificate and) delegate stake to a pool for the first time.

```rust
use whisky::*;

pub fn delegate_stake(
    stake_key_hash: &str,
    pool_id: &str, // In the form of 'poolxxxxxx'
    my_address: &str,
    inputs: &[UTxO],
) -> Result<String, WError> {
    let mut tx_builder = TxBuilder::new_core();
    tx_builder
        .register_stake_certificate(stake_key_hash)
        .delegate_stake_certificate(stake_key_hash, pool_id)
        .change_address(my_address)
        .select_utxos_from(inputs, 5000000)
        .complete_sync(None)?;

    Ok(tx_builder.tx_hex())
}
```


# Complex Transaction

The following is a simple example of building a transaction of unlocking from the script and minting multiple Plutus tokens.

```rust
use whisky::*;

pub struct UnlockUtxo {
    pub script_utxo: UTxO,
    pub redeemer: String,
    pub script: ProvidedScriptSource,
}

pub struct MintToken {
    pub to_mint_asset: Asset,
    pub redeemer: String,
    pub script: ProvidedScriptSource,
}

pub async fn complex_transaction(
    to_unlock: &UnlockUtxo,
    to_mint_1: &MintToken,
    to_mint_2: &MintToken,
    my_address: &str,
    inputs: &[UTxO],
    collateral: &UTxO,
) -> Result<String, WError> {
    let UnlockUtxo {
        script_utxo,
        redeemer,
        script,
    } = to_unlock;

    let MintToken {
        to_mint_asset: to_mint_asset_1,
        redeemer: redeemer_1,
        script: script_1,
    } = to_mint_1;

    let MintToken {
        to_mint_asset: to_mint_asset_2,
        redeemer: redeemer_2,
        script: script_2,
    } = to_mint_2;

    let mut tx_builder = TxBuilder::new_core();
    tx_builder
        .spending_plutus_script_v2()
        .tx_in(
            &script_utxo.input.tx_hash,
            script_utxo.input.output_index,
            &script_utxo.output.amount,
            &script_utxo.output.address,
        )
        .tx_in_inline_datum_present()
        // .tx_in_datum_value(datum here) or provide datum value
        .tx_in_redeemer_value(&WRedeemer {
            data: WData::JSON(redeemer.to_string()),
            ex_units: Budget { mem: 0, steps: 0 },
        })
        .tx_in_script(&script.script_cbor)
        .mint_plutus_script_v2()
        .mint(
            to_mint_asset_1.quantity_i128(),
            &to_mint_asset_1.policy(),
            &to_mint_asset_1.name(),
        )
        .mint_redeemer_value(&WRedeemer {
            data: WData::JSON(redeemer_1.to_string()),
            ex_units: Budget { mem: 0, steps: 0 },
        })
        .minting_script(&script_1.script_cbor)
        .mint_plutus_script_v2()
        .mint(
            to_mint_asset_2.quantity_i128(),
            &to_mint_asset_2.policy(),
            &to_mint_asset_2.name(),
        )
        .mint_redeemer_value(&WRedeemer {
            data: WData::JSON(redeemer_2.to_string()),
            ex_units: Budget { mem: 0, steps: 0 },
        })
        .minting_script(&script_2.script_cbor)
        .change_address(my_address)
        .tx_in_collateral(
            &collateral.input.tx_hash,
            collateral.input.output_index,
            &collateral.output.amount,
            &collateral.output.address,
        )
        .select_utxos_from(inputs, 5000000)
        .input_for_evaluation(script_utxo)
        .complete(None)
        .await?;

    Ok(tx_builder.tx_hex())
}
```


# Service Integration

`TxBuilder` can be integrated with services to streamline transaction building.

### Evaluator

{% hint style="success" %}
Integration is completed
{% endhint %}

The evaluator service provided helps perform redeemer execution unit optimization, returning an error message in case of an invalid transaction.

### Fetcher

{% hint style="info" %}
Integration is not live yet
{% endhint %}

The fetcher service helps with auto-completing missing information in transaction building by fetching information from the blockchain. Affected APIs are `tx_in`, `tx_in_collateral`, `spending_tx_in_reference`.

### Submitter

{% hint style="info" %}
Integration is not live yet
{% endhint %}

The submitter service provides alias function(s) for handy transaction submission.


# Parse Transaction CBOR

You can parse the transaction CBOR back to the `TxBuilderBody` type for multiple purposes:

* Unit testing the transaction Cbor&#x20;
* Rebuilding the augmented transaction

To parse a transaction Cbor:

```rust
let utxo_1: UTxO = serde_json::from_str("{\"input\":{\"outputIndex\":0,\"txHash\":\"1a6157c0c9e170d716aee64b25384cad275770e2ef86df31eeebda4892980723\"},\"output\":{\"address\":\"addr_test1qrs3jlcsapdufgagzt35ug3nncwl26mlkcux49gs673sflmrjfm6y2eu7del3pprckzt4jaal9s7w9gq5kguqs5pf6fq542mmq\",\"amount\":[{\"quantity\":\"10000000000\",\"unit\":\"lovelace\"}],\"dataHash\":null,\"plutusData\":null,\"scriptHash\":null,\"scriptRef\":null}}").unwrap();
let utxo_2: UTxO = serde_json::from_str("{\"input\":{\"outputIndex\":5,\"txHash\":\"158a0bff150e9c6f68a14fdb1623c363f54e36cb22efc800911bffafa4e53442\"},\"output\":{\"address\":\"addr_test1qra9zdhfa8kteyr3mfe7adkf5nlh8jl5xcg9e7pcp5w9yhyf5tek6vpnha97yd5yw9pezm3wyd77fyrfs3ynftyg7njs5cfz2x\",\"amount\":[{\"quantity\":\"5000000\",\"unit\":\"lovelace\"}],\"dataHash\":null,\"plutusData\":null,\"scriptHash\":null,\"scriptRef\":null}}").unwrap();

let utxos = vec![utxo_1, utxo_2];
let tx_hex = "84a700d90102818258201a6157c0c9e170d716aee64b25384cad275770e2ef86df31eeebda4892980723000183a300581d70506245b8d10428549499ecfcd0435d5a0b9a3aac2c5bccc824441a7201821a001e8480a1581ceab3a1d125a3bf4cd941a6a0b5d7752af96fae7f5bcc641e8a0b6762a14001028201d818586ad8799fd8799fd8799f5041bfc7325343428683bbd0b94a4da41cd8799f581ce1197f10e85bc4a3a812e34e22339e1df56b7fb6386a9510d7a304ffffd8799f581c7c87b6b5a0963af3eadb107da2ac4e1d34747a4df363858b649aa845ffffffa140a1401a00989680ff82581d70ba3efbd72650cbc7d5d7e6bede007cd3cb6730ba1972debf1c2c098f1a007a120082583900e1197f10e85bc4a3a812e34e22339e1df56b7fb6386a9510d7a304ff639277a22b3cf373f88423c584bacbbdf961e71500a591c042814e921b0000000253704b3f021a0003024109a1581ceab3a1d125a3bf4cd941a6a0b5d7752af96fae7f5bcc641e8a0b6762a140010b5820d88d41dd788fcf7c3b1f15808e11b01d71e0413d57265ddb7fc5b5776ff16e720dd9010281825820158a0bff150e9c6f68a14fdb1623c363f54e36cb22efc800911bffafa4e53442050ed9010281581cfa5136e9e9ecbc9071da73eeb6c9a4ff73cbf436105cf8380d1c525ca207d901028158b558b30101009800aba2a6011e581cfa5136e9e9ecbc9071da73eeb6c9a4ff73cbf436105cf8380d1c525c00a6010746332d6d696e740048c8c8c8c88c88966002646464646464660020026eb0c038c03cc03cc03cc03cc03cc03cc03cc03cc030dd5180718061baa0072259800800c52844c96600266e3cdd71808001005c528c4cc00c00c00500d1808000a01c300c300d002300b001300b002300900130063754003149a26cac8028dd7000ab9a5573caae7d5d0905a182010082d87980821956861a0066ad1cf5f6";

let mut tx_parser = TxParser::new();
let tx_parser = tx_parser.parse(tx_hex, &utxos).unwrap(); // custom error handling
```

There are 2 necessary fields to pass in:

1. `tx_hex` - The transaction CBOR to be parsed
2. `utxos` - The input information, for all inputs, reference inputs, and collateral. You can either construct it manually or obtain it from [providers](/services/providers).


# Unit Testing Transaction

The parsed transaction can be used for inspection, primarily in the scenario of unit testing the transaction-building process.

To obtain the `TxTester`:

```rust
let mut tx_tester = tx_parser.to_tester();
```

### Interpret result

After adding checks, the success case:

```rust
assert!(tx_tester.success()); // passing
 
let error_msg = tx_tester.errors();
println!(error_msg) // "No errors"
```

If case there is an error, there would be tracing about where it fails the checks:

```rust
println!("Errors: {:?}", tx_tester.errors());

// For example, failing at finding inline datum at outputs
Errors: "[Error - outputs_inline_datum_exist]: No outputs with inline datum matching: d905039fd8799fd8799f5041bfc7325343428683bbd0b94a4da41cd8799f581ce1197f10e85bc4a3a812e34e22339e1df56b7fb6386a9510d7a304ffffd8799f581c7c87b6b5a0963af3eadb107da2ac4e1d34747a4df363858b649aa845ffffffa140a1401a00989680ff"
```

### Testing inputs

Testing inputs starts with locating the inputs you want to test. The filtering will not reset until the filtering methods are called again.

* `all_inputs` - not apply filters
* `inputs_at` - filtering inputs with address
* `inputs_with` - filtering inputs with token
* `inputs_with_policy` - filtering inputs with policy id
* `inputs_at_with` - filtering inputs with address and token
* `inputs_at_with_policy` - filtering inputs with address and policy id

Then it comes with the checks:

* `inputs_value` - check the aggregated value of filtered inputs
* `inputs_inline_datum_exist` - check whether any of the filtered inputs with the inline datum

Example

```rust
tx_tester
    .inputs_at("addr_test1qrs3jlcsapdufgagzt35ug3nncwl26mlkcux49gs673sflmrjfm6y2eu7del3pprckzt4jaal9s7w9gq5kguqs5pf6fq542mmq")
    .inputs_value(Value::from_asset(&Asset::new_from_str("lovelace", "10000000000")))
    .inputs_inline_datum_exist(WData::JSON(output_datum.to_string()).to_cbor().unwrap().as_str())
```

### Testing outputs

Testing outputs starts with locating the outputs you want to test. The filtering will not reset until the filtering methods are called again.

* `all_outputs` - not apply filters
* `outputs_at` - filtering outputs with address
* `outputs_with` - filtering outputs with token
* `outputs_with_policy` - filtering outputs with policy id
* `outputs_at_with` - filtering outputs with address and token
* `outputs_at_with_policy` - filtering outputs with address and policy id

Then it comes with the checks:

* `outputs_value` - check the aggregated value of filtered outputs
* `outputs_inline_datum_exist` - check whether any of the filtered outputs with the inline datum

Example

```rust
tx_tester
    .outputs_value(Value::from_asset(&Asset::new_from_str("lovelace", "8000000")))
    .outputs_at_with("addr_test1wpgxy3dc6yzzs4y5n8k0e5zrt4dqhx364sk9hnxgy3zp5usfh3tau", "eab3a1d125a3bf4cd941a6a0b5d7752af96fae7f5bcc641e8a0b6762")
    .outputs_inline_datum_exist(WData::JSON(output_datum.to_string()).to_cbor().unwrap().as_str())
```

### Testing mints

Testing mints with below APIs:

* `token_minted` - checks if a specific token is minted in the transaction
* `only_token_minted` - checks if a specific token is minted in the transaction and that it is the only mint
* `policy_only_minted_token` - checks if a specific token is minted in the transaction, ensuring that it is the only mint for the given policy ID
* `check_policy_only_burn` - checks if a specific policy ID is burned in the transaction, ensuring that it is the only minting (i.e. burning item).

### Testing time

Testing time with below APIs:

* `valid_after` - checks if the transaction is valid after a specified timestamp
* `valid_before` - checks if the transaction is valid before a specified timestamp

### Testing signature

Testing whether the signature is required in the transaction with below APIs:

* `key_signed` - checks if a specific key is signed in the transaction
* `one_of_keys_signed` - checks if any one of the specified keys is signed in the transaction
* `all_keys_signed` - checks if all specified keys are signed in the transaction


# Rebuilding Transaction

Rebuilding a transaction starts from obtaining the `TxBuilderBody` , which can be obtained by either of the methods from `TxParser`:

* `get_builder_body` - get the entire `TxBuilderBody` of the current transaction parsed
* `get_builder_body_without_change` - get the `TxBuilderBody` without the last output
  * This is the recommended method for rebuilding the transaction since the `complete` method of `TxBuilder` would re-calculate the change as per the manipulation. Thus, the rebuilt transaction will be in an expected shape without one extra output.

### Full Example

```rust
// let body = tx_parser.get_builder_body();
let body = tx_parser.get_builder_body_without_change();

let mut new_tx_builder = TxBuilder::new_core();

// manipulation of the `tx_builder_body` as needed here
new_tx_builder.tx_builder_body = body.clone();

new_tx_builder
    // extra tx building methods as needed here
    .complete_sync(None)
    .unwrap();
```


# Providers

Providers are services that help with:

* `fetcher` - fetching the Cardano blockchain information
* `evaluator` - evaluating transactions with execution units (exUnits) calculation
* `submitter` - submitting transactions on-chain

## Supported Providers

### Blockfrost Provider

<table><thead><tr><th width="143.0234375">Role</th><th>Implementation</th></tr></thead><tbody><tr><td>Fetcher</td><td>Completed</td></tr><tr><td>Evaluator</td><td>Completed</td></tr><tr><td>Submitter</td><td>Completed</td></tr></tbody></table>

### Maestro Provider

<table><thead><tr><th width="143.0234375">Role</th><th>Implementation</th></tr></thead><tbody><tr><td>Fetcher</td><td>Completed</td></tr><tr><td>Evaluator</td><td>Completed</td></tr><tr><td>Submitter</td><td>Completed</td></tr></tbody></table>

### Offline Provider

Offline provider integrates TxPipe's `uplc` crate to perform transaction evaluation and exUnits update automatically in `TxBuilder`, without a need to external service providers, natively in Rust code base.&#x20;

<table><thead><tr><th width="143.0234375">Role</th><th>Implementation</th></tr></thead><tbody><tr><td>Evaluator</td><td>Completed</td></tr></tbody></table>


# Fetcher

`fetcher` implements the following interface:

```rust
#[async_trait]
pub trait Fetcher: Send + Sync {
    async fn fetch_account_info(&self, address: &str) -> Result<AccountInfo, WError>;
    async fn fetch_address_utxos(
        &self,
        address: &str,
        asset: Option<&str>,
    ) -> Result<Vec<UTxO>, WError>;

    async fn fetch_asset_addresses(&self, asset: &str) -> Result<Vec<(String, String)>, WError>;
    async fn fetch_asset_metadata(
        &self,
        asset: &str,
    ) -> Result<Option<HashMap<String, serde_json::Value>>, WError>;
    async fn fetch_block_info(&self, hash: &str) -> Result<BlockInfo, WError>;
    async fn fetch_collection_assets(
        &self,
        policy_id: &str,
        cursor: Option<String>,
    ) -> Result<(Vec<(String, String)>, Option<String>), WError>;
    async fn fetch_protocol_parameters(&self, epoch: Option<u32>) -> Result<Protocol, WError>;
    async fn fetch_tx_info(&self, hash: &str) -> Result<TransactionInfo, WError>;
    async fn fetch_utxos(&self, hash: &str, index: Option<u32>) -> Result<Vec<UTxO>, WError>;
    async fn get(&self, url: &str) -> Result<serde_json::Value, WError>;
}
```

## Endpoints

### fetch\_account\_info

Fetch the account information of a given address

* `address` - The address to fetch the information

### fetch\_address\_utxos

Fetch the utxos at an address

* `address` - The targeted address to search utxos for
* `asset` - The optional asset to filter the utxos value at the address

### fetch\_asset\_addresses

Fetch the asset addresses for a given asset

* `asset` - The policy ID + the asset name in hex

### fetch\_asset\_metadata

Fetch the metadata of a given asset

* `asset` - The policy ID + the asset name in hex

### fetch\_block\_info

Fetch the block information

* `hash` - The block hash to search for

### fetch\_collection\_assets

Fetch a collection's existing assets on-chain

* &#x20;`policy_id` - The policy ID of the collection to search for
* `cursor` - The optional cursor to search for the next page of an assets list

### fetch\_protocol\_parameters

Fetch the current protocol parameters

* `epoch` - Optional, to search for a particular epoch's protocol parameters

### fetch\_tx\_info

Fetch transaction information by transaction hash

* `hash` - The transaction hash to search for

### fetch\_utxos

Fetch output `utxos` Information by transaction hash

* `hash` - The transaction hash to search for
* `index` - The optional output index to filter

### get

The generic get endpoint to help calling other un-unified get requests on the provider service.


# Evaluator

`evaluator` implements the following interface:

```rust
#[async_trait]
pub trait Evaluator: Send {
    async fn evaluate_tx(
        &self,
        tx_hex: &str,
        inputs: &[UTxO],
        additional_txs: &[String],
        network: &Network,
        slot_config: &SlotConfig,
    ) -> Result<Vec<Action>, WError>;
}
```

## Endpoints

### evaluate\_tx

* `tx_hex` - The transaction hex for evaluation.
* `inputs` - The extra input information provided for consideration (useful for offline evaluator or transaction chaining)
* `additional_txs` - The extra transaction (in hex) for parsing input information provided for consideration (useful for offline evaluator or transaction chaining)
* `network` - Cardano blockchain network
* `slot_config` - Useful for offline evaluation


# Submitter

TBC


