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 {
45        from: from.into(),
46        to: to.into(),
47        tokenId: token_id,
48    }
49    .abi_encode()
50    .into()
51}
52
53/// ABI-encode the `safeTransferFrom(from, to, tokenId)` call (no data).
54pub fn encode_safe_transfer_from(from: Address, to: Address, token_id: U256) -> Bytes {
55    use alloy_sol_types::SolCall as _;
56    ITRC721::safeTransferFrom_0Call {
57        from: from.into(),
58        to: to.into(),
59        tokenId: token_id,
60    }
61    .abi_encode()
62    .into()
63}
64
65/// ABI-encode the `approve(to, tokenId)` call.
66pub fn encode_approve(to: Address, token_id: U256) -> Bytes {
67    use alloy_sol_types::SolCall as _;
68    ITRC721::approveCall {
69        to: to.into(),
70        tokenId: token_id,
71    }
72    .abi_encode()
73    .into()
74}
75
76/// ABI-encode the `balanceOf(owner)` constant call.
77pub fn encode_balance_of(owner: Address) -> Bytes {
78    use alloy_sol_types::SolCall as _;
79    ITRC721::balanceOfCall {
80        owner: owner.into(),
81    }
82    .abi_encode()
83    .into()
84}
85
86/// ABI-encode the `ownerOf(tokenId)` constant call.
87pub fn encode_owner_of(token_id: U256) -> Bytes {
88    use alloy_sol_types::SolCall as _;
89    ITRC721::ownerOfCall { tokenId: token_id }
90        .abi_encode()
91        .into()
92}
93
94/// Decode the `uint256` returned by `balanceOf`.
95pub fn decode_uint256_return(output: &[u8]) -> Result<U256, alloy_sol_types::Error> {
96    use alloy_sol_types::SolCall as _;
97    ITRC721::balanceOfCall::abi_decode_returns(output)
98}
99
100/// Decode the `address` returned by `ownerOf` / `getApproved`.
101pub fn decode_address_return(output: &[u8]) -> Result<Address, alloy_sol_types::Error> {
102    use alloy_sol_types::SolCall as _;
103    Ok(ITRC721::ownerOfCall::abi_decode_returns(output)?.into())
104}
105
106/// Decode the `string` returned by `name` / `symbol` / `tokenURI`.
107pub fn decode_string_return(output: &[u8]) -> Result<String, alloy_sol_types::Error> {
108    use alloy_sol_types::SolCall as _;
109    ITRC721::nameCall::abi_decode_returns(output)
110}
111
112/// Decode the `bool` returned by `isApprovedForAll`.
113pub fn decode_bool_return(output: &[u8]) -> Result<bool, alloy_sol_types::Error> {
114    use alloy_sol_types::SolCall as _;
115    ITRC721::isApprovedForAllCall::abi_decode_returns(output)
116}
117
118#[cfg(test)]
119mod tests {
120    use alloy_sol_types::SolCall as _;
121
122    use super::*;
123
124    fn addr() -> Address {
125        "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t".parse().unwrap()
126    }
127
128    #[test]
129    fn transfer_from_selector() {
130        // keccak256("transferFrom(address,address,uint256)")[..4] == 0x23b872dd
131        assert_eq!(
132            ITRC721::transferFromCall::SELECTOR,
133            [0x23, 0xb8, 0x72, 0xdd]
134        );
135    }
136
137    #[test]
138    fn safe_transfer_from_selector() {
139        // keccak256("safeTransferFrom(address,address,uint256)")[..4] == 0x42842e0e
140        assert_eq!(
141            ITRC721::safeTransferFrom_0Call::SELECTOR,
142            [0x42, 0x84, 0x2e, 0x0e]
143        );
144    }
145
146    #[test]
147    fn balance_of_selector() {
148        // keccak256("balanceOf(address)")[..4] == 0x70a08231
149        assert_eq!(ITRC721::balanceOfCall::SELECTOR, [0x70, 0xa0, 0x82, 0x31]);
150    }
151
152    #[test]
153    fn owner_of_selector() {
154        // keccak256("ownerOf(uint256)")[..4] == 0x6352211e
155        assert_eq!(ITRC721::ownerOfCall::SELECTOR, [0x63, 0x52, 0x21, 0x1e]);
156    }
157
158    #[test]
159    fn balance_of_encodes_correctly() {
160        let data = encode_balance_of(addr());
161        assert_eq!(&data[..4], ITRC721::balanceOfCall::SELECTOR);
162    }
163}