tronz-contract 0.4.0

ABI bindings and contract instances for the tronz TRON SDK.
Documentation
//! TRC721 ABI bindings and (optionally) the provider-bound [`Trc721Instance`].
//!
//! TRC721 is byte-for-byte compatible with the EVM ERC721 ABI, so the
//! interface and all call/return codecs are generated by `alloy`'s
//! [`sol!`](alloy_sol_macro::sol) macro.

#[cfg(feature = "provider")]
pub mod instance;

use alloy_sol_macro::sol;
#[cfg(feature = "provider")]
pub use instance::{Trc721Error, Trc721Ext, Trc721Instance};
use tronz_primitives::{Address, Bytes, U256};

sol! {
    #[derive(Debug, PartialEq, Eq)]
    interface ITRC721 {
        // ── Core ERC721 ──────────────────────────────────────────────────────
        function balanceOf(address owner)                                    external view returns (uint256);
        function ownerOf(uint256 tokenId)                                    external view returns (address);
        function transferFrom(address from, address to, uint256 tokenId)     external;
        function approve(address to, uint256 tokenId)                        external;
        function getApproved(uint256 tokenId)                                external view returns (address);
        function setApprovalForAll(address operator, bool approved)          external;
        function isApprovedForAll(address owner, address operator)           external view returns (bool);
        function safeTransferFrom(address from, address to, uint256 tokenId) external;
        function safeTransferFrom(address from, address to, uint256 tokenId, bytes data) external;

        // ── ERC721Metadata ───────────────────────────────────────────────────
        function name()                         external view returns (string);
        function symbol()                       external view returns (string);
        function tokenURI(uint256 tokenId)      external view returns (string);

        // ── Events ───────────────────────────────────────────────────────────
        event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
        event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
        event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
    }
}

/// ABI-encode the `transferFrom(from, to, tokenId)` call.
pub fn encode_transfer_from(from: Address, to: Address, token_id: U256) -> Bytes {
    use alloy_sol_types::SolCall as _;
    ITRC721::transferFromCall { from: from.into(), to: to.into(), tokenId: token_id }
        .abi_encode()
        .into()
}

/// ABI-encode the `safeTransferFrom(from, to, tokenId)` call (no data).
pub fn encode_safe_transfer_from(from: Address, to: Address, token_id: U256) -> Bytes {
    use alloy_sol_types::SolCall as _;
    ITRC721::safeTransferFrom_0Call { from: from.into(), to: to.into(), tokenId: token_id }
        .abi_encode()
        .into()
}

/// ABI-encode the `approve(to, tokenId)` call.
pub fn encode_approve(to: Address, token_id: U256) -> Bytes {
    use alloy_sol_types::SolCall as _;
    ITRC721::approveCall { to: to.into(), tokenId: token_id }.abi_encode().into()
}

/// ABI-encode the `balanceOf(owner)` constant call.
pub fn encode_balance_of(owner: Address) -> Bytes {
    use alloy_sol_types::SolCall as _;
    ITRC721::balanceOfCall { owner: owner.into() }.abi_encode().into()
}

/// ABI-encode the `ownerOf(tokenId)` constant call.
pub fn encode_owner_of(token_id: U256) -> Bytes {
    use alloy_sol_types::SolCall as _;
    ITRC721::ownerOfCall { tokenId: token_id }.abi_encode().into()
}

/// Decode the `uint256` returned by `balanceOf`.
pub fn decode_uint256_return(output: &[u8]) -> Result<U256, alloy_sol_types::Error> {
    use alloy_sol_types::SolCall as _;
    ITRC721::balanceOfCall::abi_decode_returns(output)
}

/// Decode the `address` returned by `ownerOf` / `getApproved`.
pub fn decode_address_return(output: &[u8]) -> Result<Address, alloy_sol_types::Error> {
    use alloy_sol_types::SolCall as _;
    Ok(ITRC721::ownerOfCall::abi_decode_returns(output)?.into())
}

/// Decode the `string` returned by `name` / `symbol` / `tokenURI`.
pub fn decode_string_return(output: &[u8]) -> Result<String, alloy_sol_types::Error> {
    use alloy_sol_types::SolCall as _;
    ITRC721::nameCall::abi_decode_returns(output)
}

/// Decode the `bool` returned by `isApprovedForAll`.
pub fn decode_bool_return(output: &[u8]) -> Result<bool, alloy_sol_types::Error> {
    use alloy_sol_types::SolCall as _;
    ITRC721::isApprovedForAllCall::abi_decode_returns(output)
}

#[cfg(test)]
mod tests {
    use alloy_sol_types::SolCall as _;

    use super::*;

    fn addr() -> Address {
        "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t".parse().unwrap()
    }

    #[test]
    fn transfer_from_selector() {
        // keccak256("transferFrom(address,address,uint256)")[..4] == 0x23b872dd
        assert_eq!(ITRC721::transferFromCall::SELECTOR, [0x23, 0xb8, 0x72, 0xdd]);
    }

    #[test]
    fn safe_transfer_from_selector() {
        // keccak256("safeTransferFrom(address,address,uint256)")[..4] == 0x42842e0e
        assert_eq!(ITRC721::safeTransferFrom_0Call::SELECTOR, [0x42, 0x84, 0x2e, 0x0e]);
    }

    #[test]
    fn balance_of_selector() {
        // keccak256("balanceOf(address)")[..4] == 0x70a08231
        assert_eq!(ITRC721::balanceOfCall::SELECTOR, [0x70, 0xa0, 0x82, 0x31]);
    }

    #[test]
    fn owner_of_selector() {
        // keccak256("ownerOf(uint256)")[..4] == 0x6352211e
        assert_eq!(ITRC721::ownerOfCall::SELECTOR, [0x63, 0x52, 0x21, 0x1e]);
    }

    #[test]
    fn balance_of_encodes_correctly() {
        let data = encode_balance_of(addr());
        assert_eq!(&data[..4], ITRC721::balanceOfCall::SELECTOR);
    }
}