Skip to main content

tronz_contract/trc721/
mod.rs

1//! TRC721 ABI bindings and (optionally) the provider-bound [`Trc721Instance`].
2//!
3//! TRC721 is byte-for-byte compatible with the EVM ERC721 ABI, so the
4//! interface and all call/return codecs are generated by `alloy`'s
5//! [`sol!`](alloy_sol_macro::sol) macro.
6
7#[cfg(feature = "provider")]
8pub mod instance;
9
10use alloy_sol_macro::sol;
11#[cfg(feature = "provider")]
12pub use instance::{Trc721Error, Trc721Ext, Trc721Instance};
13use tronz_primitives::{Address, Bytes, U256};
14
15sol! {
16    #[derive(Debug, PartialEq, Eq)]
17    interface ITRC721 {
18        // ── Core ERC721 ──────────────────────────────────────────────────────
19        function balanceOf(address owner)                                    external view returns (uint256);
20        function ownerOf(uint256 tokenId)                                    external view returns (address);
21        function transferFrom(address from, address to, uint256 tokenId)     external;
22        function approve(address to, uint256 tokenId)                        external;
23        function getApproved(uint256 tokenId)                                external view returns (address);
24        function setApprovalForAll(address operator, bool approved)          external;
25        function isApprovedForAll(address owner, address operator)           external view returns (bool);
26        function safeTransferFrom(address from, address to, uint256 tokenId) external;
27        function safeTransferFrom(address from, address to, uint256 tokenId, bytes data) external;
28
29        // ── ERC721Metadata ───────────────────────────────────────────────────
30        function name()                         external view returns (string);
31        function symbol()                       external view returns (string);
32        function tokenURI(uint256 tokenId)      external view returns (string);
33
34        // ── Events ───────────────────────────────────────────────────────────
35        event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
36        event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
37        event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
38    }
39}
40
41/// ABI-encode the `transferFrom(from, to, tokenId)` call.
42pub fn encode_transfer_from(from: Address, to: Address, token_id: U256) -> Bytes {
43    use alloy_sol_types::SolCall as _;
44    ITRC721::transferFromCall { from: from.into(), to: to.into(), tokenId: token_id }
45        .abi_encode()
46        .into()
47}
48
49/// ABI-encode the `safeTransferFrom(from, to, tokenId)` call (no data).
50pub fn encode_safe_transfer_from(from: Address, to: Address, token_id: U256) -> Bytes {
51    use alloy_sol_types::SolCall as _;
52    ITRC721::safeTransferFrom_0Call { from: from.into(), to: to.into(), tokenId: token_id }
53        .abi_encode()
54        .into()
55}
56
57/// ABI-encode the `approve(to, tokenId)` call.
58pub fn encode_approve(to: Address, token_id: U256) -> Bytes {
59    use alloy_sol_types::SolCall as _;
60    ITRC721::approveCall { to: to.into(), tokenId: token_id }.abi_encode().into()
61}
62
63/// ABI-encode the `balanceOf(owner)` constant call.
64pub fn encode_balance_of(owner: Address) -> Bytes {
65    use alloy_sol_types::SolCall as _;
66    ITRC721::balanceOfCall { owner: owner.into() }.abi_encode().into()
67}
68
69/// ABI-encode the `ownerOf(tokenId)` constant call.
70pub fn encode_owner_of(token_id: U256) -> Bytes {
71    use alloy_sol_types::SolCall as _;
72    ITRC721::ownerOfCall { tokenId: token_id }.abi_encode().into()
73}
74
75/// Decode the `uint256` returned by `balanceOf`.
76pub fn decode_uint256_return(output: &[u8]) -> Result<U256, alloy_sol_types::Error> {
77    use alloy_sol_types::SolCall as _;
78    ITRC721::balanceOfCall::abi_decode_returns(output)
79}
80
81/// Decode the `address` returned by `ownerOf` / `getApproved`.
82pub fn decode_address_return(output: &[u8]) -> Result<Address, alloy_sol_types::Error> {
83    use alloy_sol_types::SolCall as _;
84    Ok(ITRC721::ownerOfCall::abi_decode_returns(output)?.into())
85}
86
87/// Decode the `string` returned by `name` / `symbol` / `tokenURI`.
88pub fn decode_string_return(output: &[u8]) -> Result<String, alloy_sol_types::Error> {
89    use alloy_sol_types::SolCall as _;
90    ITRC721::nameCall::abi_decode_returns(output)
91}
92
93/// Decode the `bool` returned by `isApprovedForAll`.
94pub fn decode_bool_return(output: &[u8]) -> Result<bool, alloy_sol_types::Error> {
95    use alloy_sol_types::SolCall as _;
96    ITRC721::isApprovedForAllCall::abi_decode_returns(output)
97}
98
99#[cfg(test)]
100mod tests {
101    use alloy_sol_types::SolCall as _;
102
103    use super::*;
104
105    fn addr() -> Address {
106        "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t".parse().unwrap()
107    }
108
109    #[test]
110    fn transfer_from_selector() {
111        // keccak256("transferFrom(address,address,uint256)")[..4] == 0x23b872dd
112        assert_eq!(ITRC721::transferFromCall::SELECTOR, [0x23, 0xb8, 0x72, 0xdd]);
113    }
114
115    #[test]
116    fn safe_transfer_from_selector() {
117        // keccak256("safeTransferFrom(address,address,uint256)")[..4] == 0x42842e0e
118        assert_eq!(ITRC721::safeTransferFrom_0Call::SELECTOR, [0x42, 0x84, 0x2e, 0x0e]);
119    }
120
121    #[test]
122    fn balance_of_selector() {
123        // keccak256("balanceOf(address)")[..4] == 0x70a08231
124        assert_eq!(ITRC721::balanceOfCall::SELECTOR, [0x70, 0xa0, 0x82, 0x31]);
125    }
126
127    #[test]
128    fn owner_of_selector() {
129        // keccak256("ownerOf(uint256)")[..4] == 0x6352211e
130        assert_eq!(ITRC721::ownerOfCall::SELECTOR, [0x63, 0x52, 0x21, 0x1e]);
131    }
132
133    #[test]
134    fn balance_of_encodes_correctly() {
135        let data = encode_balance_of(addr());
136        assert_eq!(&data[..4], ITRC721::balanceOfCall::SELECTOR);
137    }
138}