lwk_bindings 0.14.0

Liquid Wallet Kit - Bindings for other languages
Documentation
//! Liquid address

use elements::{
    bitcoin::{self, address::NetworkUnchecked},
    AddressParams,
};

use crate::{LwkError, Network, Script};
use std::{fmt::Display, str::FromStr, sync::Arc};

/// A Liquid address
#[derive(uniffi::Object)]
#[uniffi::export(Display)]
pub struct Address {
    inner: elements::Address,
}

impl From<elements::Address> for Address {
    fn from(inner: elements::Address) -> Self {
        Self { inner }
    }
}

impl AsRef<elements::Address> for Address {
    fn as_ref(&self) -> &elements::Address {
        &self.inner
    }
}

impl From<Address> for elements::Address {
    fn from(addr: Address) -> Self {
        addr.inner
    }
}

impl From<&Address> for elements::Address {
    fn from(addr: &Address) -> Self {
        addr.inner.clone()
    }
}

impl Display for Address {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.inner)
    }
}

#[uniffi::export]
impl Address {
    /// Construct an Address object
    #[uniffi::constructor]
    pub fn new(s: &str) -> Result<Arc<Self>, LwkError> {
        let inner: elements::Address = s.parse()?;
        Ok(Arc::new(Self { inner }))
    }

    /// Return the script pubkey of the address.
    pub fn script_pubkey(&self) -> Arc<Script> {
        Arc::new(self.inner.script_pubkey().into())
    }

    /// Return true if the address is blinded.
    pub fn is_blinded(&self) -> bool {
        self.inner.is_blinded()
    }

    /// Return the unconfidential address.
    pub fn to_unconfidential(&self) -> Arc<Self> {
        Arc::new(self.inner.to_unconfidential().into())
    }

    /// Returns a string encoding an image in a uri
    ///
    /// The string can be open in the browser or be used as `src` field in `img` in HTML
    ///
    /// For max efficiency we suggest to pass `None` to `pixel_per_module`, get a very small image
    /// and use styling to scale up the image in the browser. eg
    /// `style="image-rendering: pixelated; border: 20px solid white;"`
    pub fn qr_code_uri(&self, pixel_per_module: Option<u8>) -> Result<String, LwkError> {
        Ok(lwk_common::address_to_qr(&self.inner, pixel_per_module)?)
    }

    /// Returns a string of the QR code printable in a terminal environment
    pub fn qr_code_text(&self) -> Result<String, LwkError> {
        Ok(lwk_common::address_to_text_qr(&self.inner)?)
    }

    /// Returns the network of the address
    pub fn network(&self) -> Network {
        if self.inner.params == &AddressParams::LIQUID {
            lwk_wollet::ElementsNetwork::Liquid.into()
        } else if self.inner.params == &AddressParams::LIQUID_TESTNET {
            lwk_wollet::ElementsNetwork::LiquidTestnet.into()
        } else {
            lwk_wollet::ElementsNetwork::default_regtest().into()
        }
    }
}

/// A valid Bitcoin address
#[derive(uniffi::Object)]
#[uniffi::export(Display)]
pub struct BitcoinAddress {
    inner: bitcoin::Address,
}

impl From<bitcoin::Address> for BitcoinAddress {
    fn from(inner: bitcoin::Address) -> Self {
        Self { inner }
    }
}

impl From<bitcoin::Address<NetworkUnchecked>> for BitcoinAddress {
    fn from(inner: bitcoin::Address<NetworkUnchecked>) -> Self {
        Self {
            inner: inner.assume_checked(),
        }
    }
}

impl AsRef<bitcoin::Address> for BitcoinAddress {
    fn as_ref(&self) -> &bitcoin::Address {
        &self.inner
    }
}

impl From<BitcoinAddress> for bitcoin::Address {
    fn from(addr: BitcoinAddress) -> Self {
        addr.inner
    }
}

impl Display for BitcoinAddress {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.inner)
    }
}

#[uniffi::export]
impl BitcoinAddress {
    /// Construct an Address object
    #[uniffi::constructor]
    pub fn new(s: &str) -> Result<Arc<Self>, LwkError> {
        let inner = bitcoin::Address::from_str(s)?.assume_checked();
        Ok(Arc::new(Self { inner }))
    }

    /// Returns the network of the address
    pub fn is_mainnet(&self) -> bool {
        self.inner
            .as_unchecked()
            .is_valid_for_network(bitcoin::Network::Bitcoin)
    }
}

#[cfg(test)]
mod tests {

    use super::Address;

    #[test]
    fn address() {
        let address_str = "tlq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f3mmz5l7uw5pqmx6xf5xy50hsn6vhkm5euwt72x878eq6zxx2z58hd7zrsg9qn";

        let address = Address::new(address_str).unwrap();
        assert_eq!(address.to_string(), address_str);

        assert_eq!(
            address.script_pubkey().to_string(),
            "0014d0c4a3ef09e997b6e99e397e518fe3e41a118ca1"
        );

        assert!(address.is_blinded());

        assert_eq!(
            address.to_unconfidential().to_string(),
            "tex1q6rz28mcfaxtmd6v789l9rrlrusdprr9p634wu8"
        );

        println!("{}", address.qr_code_text().unwrap());

        let expected = "
███████████████████████████████████████████████
███████████████████████████████████████████████
███ ▄▄▄▄▄ █▄▄▀▄▄▄▀▀▄█▀ ▀  ▀▀  █  █  █ ▄▄▄▄▄ ███
███ █   █ █▀▀ ███▄██ ▄▀ ▀▄█▄█▄▄▀▀▀▄▄█ █   █ ███
███ █▄▄▄█ █ █  ▄▄████▀█  ▀█▄▄▀▄█▀ ▀▀█ █▄▄▄█ ███
███▄▄▄▄▄▄▄█ ▀▄▀ ▀ █▄█▄█ ▀ █▄█ █▄▀ █▄█▄▄▄▄▄▄▄███
███▄▀█▀ █▄▄ █ ███▀█▀▄ ▄▄█▄▄▀█▄ █ █▄█▄▄▄ ▄██▄███
███ ▀▄██ ▄▄▀█▀██▄  █▄▀▀▄   ▄▀▄ ▄▀ █ █▀█▄▀█▀▄███
███▄ ▀▄█▄▄ █▀█ ▄▄▄█▀ █ ▄█▄█▀▄▀█▀█  ▀ ▀█▄ ▀ ▀███
███▀  ▄▀▄▄██▄ █▄▀    █ ▀▀  ▄███▄▄▀█▄▄▀ ▀ █▀████
███ █▀██▄▄▄▀  ▀▀▀█▄█▀███▄▄▀ ▄ ▄▀█▀▄▀▄▀▀▀█▀▀▀███
███ █▄  ▄▄█▄█▄█▄██▀▄█▄▄▀ ▄▄  ▄█  ▀▀█  ██ █  ███
███  █▄▄▀▄█ ▄ ▀▀▄▄▄ ▀▀█▀▀▀ ▀▄▀▄▀█▄▄█ █▄█▀ ▄▀███
████ ▄▄█▄▄██▀▀ ▄ ▄▄▀██▄▄▀  ▄  █  ▄█▄▄█▄▄█ █ ███
███▄▄ ▀▀█▄█▀█ █ ████ ██▄█ ▀▄▄█▀ ▄▀▄▄▀█▀▄▀▄▄▄███
███▄▄█▄▀█▄▀ ▄▀▄█▀█  ▄ ▄███ ▄▄▄ █▀██▀  ▀  █ ████
███ ▄▀█ █▄ █ █▀ ▀ █▄ █▀▀▄▀▄▄ ▄█▄ ▄ ▄██▄█▀▀▀████
█████▄   ▄█▄▀  ▀ ▄▀▀▀▄█▄▀▄█▀ ▀ ▀   ▀▀ ▀█  ▄████
███▄▄█▄▄█▄▄ ██ ▀ █  ██ █ █ ▄ █▀ ▀ ▀ ▄▄▄  ▀  ███
███ ▄▄▄▄▄ █▄██▀█▄▀ ▀ ▀█▀█▀ ▄█▀█▄█▄█ █▄█  █▀▀███
███ █   █ █▄▀█▀█▄ ▀█▀▄ █▀▄▄▄▄▄▄ ▀▀█  ▄ ▄███▄███
███ █▄▄▄█ ██ ▀█▄   █▄▀█▄█▀▄██▀▄▄▀▄▀▄▀███▄   ███
███▄▄▄▄▄▄▄█▄█▄█▄▄▄█▄▄█▄▄█▄▄███▄█▄█▄██▄█▄▄▄▄████
███████████████████████████████████████████████";

        assert!(address.qr_code_text().unwrap().contains(expected.trim()));

        assert_eq!(address.qr_code_uri(None).unwrap(), "data:image/bmp;base64,Qk2GAQAAAAAAAD4AAAAoAAAAKQAAACkAAAABAAEAAAAAAEgBAAAAAgAAAAIAAAIAAAACAAAA////AAAAAAD+rtsVLwAAAIJnIiVDgAAAuk9JGoeAAAC6U1QO0AAAALqGM/j4gAAAghPrII2AAAD+hUGKrAAAAACdlV+PgAAA25WVyv2AAAAcfcT/9gAAAD62KlcnAAAAqV5aRQcAAADLSsXvkAAAAAmloRT9AAAA0tHx8G0AAAA4qEMaVAAAAOIoSs2LgAAAQHSHbAKAAAB2Hxvu2oAAAMS476hGgAAA2ueBU1MAAACYAQzPZYAAAL6ot+xlgAAAoPxBqruAAACHYQbxQAAAAOgn3wI9AAAAdmvTjNQAAABhUNCr54AAANceWkNNAAAAxKM3VqUAAACnB0+6iIAAAFiioJQIAAAAi6B7NXyAAAAA3g4mAAAAAP6qqqq/gAAAgrAuJ6CAAAC6vAzSLoAAALrgXA4ugAAAuiJqsa6AAACCIz/toIAAAP7clm2/gAAA");
    }
}