#[cfg(feature = "provider")]
pub mod instance;
use alloy_sol_macro::sol;
use alloy_sol_types::SolCall;
#[cfg(feature = "provider")]
pub use instance::{Trc20Error, Trc20Ext, Trc20Instance};
use tronz_primitives::{Address, Bytes, U256};
sol! {
#[derive(Debug, PartialEq, Eq)]
interface ITRC20 {
function name() external view returns (string);
function symbol() external view returns (string);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
}
pub fn encode_transfer(to: Address, amount: U256) -> Bytes {
ITRC20::transferCall { to: to.into(), amount }.abi_encode().into()
}
pub fn encode_approve(spender: Address, amount: U256) -> Bytes {
ITRC20::approveCall { spender: spender.into(), amount }.abi_encode().into()
}
pub fn encode_transfer_from(from: Address, to: Address, amount: U256) -> Bytes {
ITRC20::transferFromCall { from: from.into(), to: to.into(), amount }.abi_encode().into()
}
pub fn encode_balance_of(account: Address) -> Bytes {
ITRC20::balanceOfCall { account: account.into() }.abi_encode().into()
}
pub fn encode_allowance(owner: Address, spender: Address) -> Bytes {
ITRC20::allowanceCall { owner: owner.into(), spender: spender.into() }.abi_encode().into()
}
pub fn decode_uint256_return(output: &[u8]) -> Result<U256, alloy_sol_types::Error> {
ITRC20::balanceOfCall::abi_decode_returns(output)
}
pub fn decode_decimals_return(output: &[u8]) -> Result<u8, alloy_sol_types::Error> {
ITRC20::decimalsCall::abi_decode_returns(output)
}
pub fn decode_string_return(output: &[u8]) -> Result<String, alloy_sol_types::Error> {
ITRC20::nameCall::abi_decode_returns(output)
}
pub fn selector<C: SolCall>() -> [u8; 4] {
C::SELECTOR
}
#[cfg(test)]
mod tests {
use alloy_sol_types::SolValue;
use super::*;
const ADDR: &str = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t";
fn addr() -> Address {
ADDR.parse().unwrap()
}
#[test]
fn transfer_selector_matches_erc20() {
let data = encode_transfer(addr(), U256::from(1u64));
assert_eq!(&data[..4], &[0xa9, 0x05, 0x9c, 0xbb]);
assert_eq!(ITRC20::transferCall::SELECTOR, [0xa9, 0x05, 0x9c, 0xbb]);
}
#[test]
fn balance_of_selector() {
let data = encode_balance_of(addr());
assert_eq!(&data[..4], &[0x70, 0xa0, 0x82, 0x31]);
}
#[test]
fn transfer_encodes_evm_address_not_tron() {
let data = encode_transfer(addr(), U256::from(0u64));
let arg = &data[4..36];
assert_eq!(&arg[..12], &[0u8; 12], "address must be left-padded");
assert_eq!(&arg[12..], addr().as_evm_bytes());
}
#[test]
fn uint256_return_roundtrip() {
let value = U256::from(123_456_789u64);
let encoded = value.abi_encode();
assert_eq!(decode_uint256_return(&encoded).unwrap(), value);
}
#[test]
fn string_return_roundtrip() {
let encoded = "Tether USD".to_string().abi_encode();
assert_eq!(decode_string_return(&encoded).unwrap(), "Tether USD");
}
#[test]
fn decimals_return_roundtrip() {
let mut encoded = [0u8; 32];
encoded[31] = 6;
assert_eq!(decode_decimals_return(&encoded).unwrap(), 6u8);
}
}