tronz_contract/trc20/
mod.rs1#[cfg(feature = "provider")]
8pub mod instance;
9
10use alloy_sol_macro::sol;
11use alloy_sol_types::SolCall;
12#[cfg(feature = "provider")]
13pub use instance::{Trc20Error, Trc20Ext, Trc20Instance};
14use tronz_primitives::{Address, Bytes, U256};
15
16sol! {
17 #[derive(Debug, PartialEq, Eq)]
18 interface ITRC20 {
19 function name() external view returns (string);
20 function symbol() external view returns (string);
21 function decimals() external view returns (uint8);
22 function totalSupply() external view returns (uint256);
23 function balanceOf(address account) external view returns (uint256);
24 function transfer(address to, uint256 amount) external returns (bool);
25 function approve(address spender, uint256 amount) external returns (bool);
26 function allowance(address owner, address spender) external view returns (uint256);
27 function transferFrom(address from, address to, uint256 amount) external returns (bool);
28
29 event Transfer(address indexed from, address indexed to, uint256 value);
30 event Approval(address indexed owner, address indexed spender, uint256 value);
31 }
32}
33
34pub fn encode_transfer(to: Address, amount: U256) -> Bytes {
37 ITRC20::transferCall { to: to.into(), amount }.abi_encode().into()
38}
39
40pub fn encode_approve(spender: Address, amount: U256) -> Bytes {
42 ITRC20::approveCall { spender: spender.into(), amount }.abi_encode().into()
43}
44
45pub fn encode_transfer_from(from: Address, to: Address, amount: U256) -> Bytes {
47 ITRC20::transferFromCall { from: from.into(), to: to.into(), amount }.abi_encode().into()
48}
49
50pub fn encode_balance_of(account: Address) -> Bytes {
52 ITRC20::balanceOfCall { account: account.into() }.abi_encode().into()
53}
54
55pub fn encode_allowance(owner: Address, spender: Address) -> Bytes {
57 ITRC20::allowanceCall { owner: owner.into(), spender: spender.into() }.abi_encode().into()
58}
59
60pub fn decode_uint256_return(output: &[u8]) -> Result<U256, alloy_sol_types::Error> {
62 ITRC20::balanceOfCall::abi_decode_returns(output)
63}
64
65pub fn decode_decimals_return(output: &[u8]) -> Result<u8, alloy_sol_types::Error> {
67 ITRC20::decimalsCall::abi_decode_returns(output)
68}
69
70pub fn decode_string_return(output: &[u8]) -> Result<String, alloy_sol_types::Error> {
72 ITRC20::nameCall::abi_decode_returns(output)
73}
74
75pub fn selector<C: SolCall>() -> [u8; 4] {
77 C::SELECTOR
78}
79
80#[cfg(test)]
81mod tests {
82 use alloy_sol_types::SolValue;
83
84 use super::*;
85
86 const ADDR: &str = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t";
87
88 fn addr() -> Address {
89 ADDR.parse().unwrap()
90 }
91
92 #[test]
93 fn transfer_selector_matches_erc20() {
94 let data = encode_transfer(addr(), U256::from(1u64));
96 assert_eq!(&data[..4], &[0xa9, 0x05, 0x9c, 0xbb]);
97 assert_eq!(ITRC20::transferCall::SELECTOR, [0xa9, 0x05, 0x9c, 0xbb]);
98 }
99
100 #[test]
101 fn balance_of_selector() {
102 let data = encode_balance_of(addr());
104 assert_eq!(&data[..4], &[0x70, 0xa0, 0x82, 0x31]);
105 }
106
107 #[test]
108 fn transfer_encodes_evm_address_not_tron() {
109 let data = encode_transfer(addr(), U256::from(0u64));
110 let arg = &data[4..36];
113 assert_eq!(&arg[..12], &[0u8; 12], "address must be left-padded");
114 assert_eq!(&arg[12..], addr().as_evm_bytes());
115 }
116
117 #[test]
118 fn uint256_return_roundtrip() {
119 let value = U256::from(123_456_789u64);
120 let encoded = value.abi_encode();
121 assert_eq!(decode_uint256_return(&encoded).unwrap(), value);
122 }
123
124 #[test]
125 fn string_return_roundtrip() {
126 let encoded = "Tether USD".to_string().abi_encode();
127 assert_eq!(decode_string_return(&encoded).unwrap(), "Tether USD");
128 }
129
130 #[test]
131 fn decimals_return_roundtrip() {
132 let mut encoded = [0u8; 32];
134 encoded[31] = 6;
135 assert_eq!(decode_decimals_return(&encoded).unwrap(), 6u8);
136 }
137}