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 {
38 to: to.into(),
39 amount,
40 }
41 .abi_encode()
42 .into()
43}
44
45pub fn encode_approve(spender: Address, amount: U256) -> Bytes {
47 ITRC20::approveCall {
48 spender: spender.into(),
49 amount,
50 }
51 .abi_encode()
52 .into()
53}
54
55pub fn encode_transfer_from(from: Address, to: Address, amount: U256) -> Bytes {
57 ITRC20::transferFromCall {
58 from: from.into(),
59 to: to.into(),
60 amount,
61 }
62 .abi_encode()
63 .into()
64}
65
66pub fn encode_balance_of(account: Address) -> Bytes {
68 ITRC20::balanceOfCall {
69 account: account.into(),
70 }
71 .abi_encode()
72 .into()
73}
74
75pub fn encode_allowance(owner: Address, spender: Address) -> Bytes {
77 ITRC20::allowanceCall {
78 owner: owner.into(),
79 spender: spender.into(),
80 }
81 .abi_encode()
82 .into()
83}
84
85pub fn decode_uint256_return(output: &[u8]) -> Result<U256, alloy_sol_types::Error> {
87 ITRC20::balanceOfCall::abi_decode_returns(output)
88}
89
90pub fn decode_decimals_return(output: &[u8]) -> Result<u8, alloy_sol_types::Error> {
92 ITRC20::decimalsCall::abi_decode_returns(output)
93}
94
95pub fn decode_string_return(output: &[u8]) -> Result<String, alloy_sol_types::Error> {
97 ITRC20::nameCall::abi_decode_returns(output)
98}
99
100pub fn selector<C: SolCall>() -> [u8; 4] {
102 C::SELECTOR
103}
104
105#[cfg(test)]
106mod tests {
107 use alloy_sol_types::SolValue;
108
109 use super::*;
110
111 const ADDR: &str = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t";
112
113 fn addr() -> Address {
114 ADDR.parse().unwrap()
115 }
116
117 #[test]
118 fn transfer_selector_matches_erc20() {
119 let data = encode_transfer(addr(), U256::from(1u64));
121 assert_eq!(&data[..4], &[0xa9, 0x05, 0x9c, 0xbb]);
122 assert_eq!(ITRC20::transferCall::SELECTOR, [0xa9, 0x05, 0x9c, 0xbb]);
123 }
124
125 #[test]
126 fn balance_of_selector() {
127 let data = encode_balance_of(addr());
129 assert_eq!(&data[..4], &[0x70, 0xa0, 0x82, 0x31]);
130 }
131
132 #[test]
133 fn transfer_encodes_evm_address_not_tron() {
134 let data = encode_transfer(addr(), U256::from(0u64));
135 let arg = &data[4..36];
138 assert_eq!(&arg[..12], &[0u8; 12], "address must be left-padded");
139 assert_eq!(&arg[12..], addr().as_evm_bytes());
140 }
141
142 #[test]
143 fn uint256_return_roundtrip() {
144 let value = U256::from(123_456_789u64);
145 let encoded = value.abi_encode();
146 assert_eq!(decode_uint256_return(&encoded).unwrap(), value);
147 }
148
149 #[test]
150 fn string_return_roundtrip() {
151 let encoded = "Tether USD".to_string().abi_encode();
152 assert_eq!(decode_string_return(&encoded).unwrap(), "Tether USD");
153 }
154
155 #[test]
156 fn decimals_return_roundtrip() {
157 let mut encoded = [0u8; 32];
159 encoded[31] = 6;
160 assert_eq!(decode_decimals_return(&encoded).unwrap(), 6u8);
161 }
162}