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