pub use IRolesAuth::{IRolesAuthErrors as RolesAuthError, IRolesAuthEvents as RolesAuthEvent};
pub use ITIP20::{ITIP20Errors as TIP20Error, ITIP20Events as TIP20Event};
use alloy_primitives::{Address, U256};
use alloy_sol_types::{SolCall, SolType};
pub const DECIMALS: u8 = 6;
pub const USD_CURRENCY: &str = "USD";
pub const ISO4217_CODES: &[&str] = &[
"AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", "BAM", "BBD", "BDT",
"BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BOV", "BRL", "BSD", "BTN", "BWP", "BYN", "BZD",
"CAD", "CDF", "CHE", "CHF", "CHW", "CLP", "CLF", "CNY", "COP", "COU", "CRC", "CUP", "CVE",
"CZK", "DJF", "DKK", "DOP", "DZD", "EGP", "ERN", "ETB", "EUR", "FJD", "FKP", "GBP", "GEL",
"GHS", "GIP", "GMD", "GNF", "GTQ", "GYD", "HKD", "HNL", "HRK", "HTG", "HUF", "IDR", "ILS",
"INR", "IQD", "IRR", "ISK", "JMD", "JOD", "JPY", "KES", "KGS", "KHR", "KMF", "KPW", "KRW",
"KWD", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "LSL", "LYD", "MAD", "MDL", "MGA", "MKD",
"MMK", "MNT", "MOP", "MRU", "MUR", "MVR", "MWK", "MXN", "MXV", "MYR", "MZN", "NAD", "NGN",
"NIO", "NOK", "NPR", "NZD", "OMR", "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG", "QAR",
"RON", "RSD", "RUB", "RWF", "SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLE", "SOS",
"SRD", "SSP", "STN", "SVC", "SYP", "SZL", "THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD",
"TWD", "TZS", "UAH", "UGX", "USD", "USN", "UYI", "UYU", "UYW", "UZS", "VED", "VES", "VND",
"VUV", "WST", "XAF", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "XCD", "XDR", "XOF", "XPD",
"XPF", "XPT", "XSU", "XTS", "XUA", "XXX", "YER", "ZAR", "ZMW", "ZWL",
];
pub fn is_iso4217_currency(code: &str) -> bool {
ISO4217_CODES.binary_search(&code).is_ok()
}
crate::sol! {
#[derive(Debug, PartialEq, Eq)]
#[sol(abi)]
interface IRolesAuth {
function hasRole(address account, bytes32 role) external view returns (bool);
function getRoleAdmin(bytes32 role) external view returns (bytes32);
function grantRole(bytes32 role, address account) external;
function revokeRole(bytes32 role, address account) external;
function renounceRole(bytes32 role) external;
function setRoleAdmin(bytes32 role, bytes32 adminRole) external;
event RoleMembershipUpdated(bytes32 indexed role, address indexed account, address indexed sender, bool hasRole);
event RoleAdminUpdated(bytes32 indexed role, bytes32 indexed newAdminRole, address indexed sender);
error Unauthorized();
}
}
crate::sol! {
#[derive(Debug, PartialEq, Eq)]
#[sol(abi)]
#[allow(clippy::too_many_arguments)]
interface ITIP20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function quoteToken() external view returns (address);
function nextQuoteToken() external view returns (address);
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);
function mint(address to, uint256 amount) external;
function burn(uint256 amount) external;
function currency() external view returns (string memory);
function supplyCap() external view returns (uint256);
function paused() external view returns (bool);
function transferPolicyId() external view returns (uint64);
function burnBlocked(address from, uint256 amount) external;
function mintWithMemo(address to, uint256 amount, bytes32 memo) external;
function burnWithMemo(uint256 amount, bytes32 memo) external;
function transferWithMemo(address to, uint256 amount, bytes32 memo) external;
function transferFromWithMemo(address from, address to, uint256 amount, bytes32 memo) external returns (bool);
function changeTransferPolicyId(uint64 newPolicyId) external;
function setSupplyCap(uint256 newSupplyCap) external;
function pause() external;
function unpause() external;
function setNextQuoteToken(address newQuoteToken) external;
function completeQuoteTokenUpdate() external;
function PAUSE_ROLE() external view returns (bytes32);
function UNPAUSE_ROLE() external view returns (bytes32);
function ISSUER_ROLE() external view returns (bytes32);
function BURN_BLOCKED_ROLE() external view returns (bytes32);
function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
struct UserRewardInfo {
address rewardRecipient;
uint256 rewardPerToken;
uint256 rewardBalance;
}
function distributeReward(uint256 amount) external;
function setRewardRecipient(address recipient) external;
function claimRewards() external returns (uint256);
function optedInSupply() external view returns (uint128);
function globalRewardPerToken() external view returns (uint256);
function userRewardInfo(address account) external view returns (UserRewardInfo memory);
function getPendingRewards(address account) external view returns (uint128);
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
event Mint(address indexed to, uint256 amount);
event Burn(address indexed from, uint256 amount);
event BurnBlocked(address indexed from, uint256 amount);
event TransferWithMemo(address indexed from, address indexed to, uint256 amount, bytes32 indexed memo);
event TransferPolicyUpdate(address indexed updater, uint64 indexed newPolicyId);
event SupplyCapUpdate(address indexed updater, uint256 indexed newSupplyCap);
event PauseStateUpdate(address indexed updater, bool isPaused);
event NextQuoteTokenSet(address indexed updater, address indexed nextQuoteToken);
event QuoteTokenUpdate(address indexed updater, address indexed newQuoteToken);
event RewardDistributed(address indexed funder, uint256 amount);
event RewardRecipientSet(address indexed holder, address indexed recipient);
error InsufficientBalance(uint256 available, uint256 required, address token);
error InsufficientAllowance();
error SupplyCapExceeded();
error InvalidSupplyCap();
error InvalidPayload();
error StringTooLong();
error PolicyForbids();
error InvalidRecipient();
error ContractPaused();
error InvalidCurrency();
error InvalidQuoteToken();
error TransfersDisabled();
error InvalidAmount();
error NoOptedInSupply();
error Unauthorized();
error ProtectedAddress();
error InvalidToken();
error Uninitialized();
error InvalidTransferPolicyId();
error PermitExpired();
error InvalidSignature();
}
}
impl ITIP20::ITIP20Calls {
pub fn is_payment(input: &[u8]) -> bool {
fn is_call<C: SolCall>(input: &[u8]) -> bool {
input.first_chunk::<4>() == Some(&C::SELECTOR)
&& input.len()
== 4 + <C::Parameters<'_> as SolType>::ENCODED_SIZE.unwrap_or_default()
}
is_call::<ITIP20::transferCall>(input)
|| is_call::<ITIP20::transferWithMemoCall>(input)
|| is_call::<ITIP20::transferFromCall>(input)
|| is_call::<ITIP20::transferFromWithMemoCall>(input)
|| is_call::<ITIP20::approveCall>(input)
|| is_call::<ITIP20::mintCall>(input)
|| is_call::<ITIP20::mintWithMemoCall>(input)
|| is_call::<ITIP20::burnCall>(input)
|| is_call::<ITIP20::burnWithMemoCall>(input)
}
}
impl RolesAuthError {
pub const fn unauthorized() -> Self {
Self::Unauthorized(IRolesAuth::Unauthorized {})
}
}
impl TIP20Error {
pub const fn insufficient_balance(available: U256, required: U256, token: Address) -> Self {
Self::InsufficientBalance(ITIP20::InsufficientBalance {
available,
required,
token,
})
}
pub const fn insufficient_allowance() -> Self {
Self::InsufficientAllowance(ITIP20::InsufficientAllowance {})
}
pub const fn unauthorized() -> Self {
Self::Unauthorized(ITIP20::Unauthorized {})
}
pub const fn invalid_supply_cap() -> Self {
Self::InvalidSupplyCap(ITIP20::InvalidSupplyCap {})
}
pub const fn supply_cap_exceeded() -> Self {
Self::SupplyCapExceeded(ITIP20::SupplyCapExceeded {})
}
pub const fn invalid_payload() -> Self {
Self::InvalidPayload(ITIP20::InvalidPayload {})
}
pub const fn invalid_quote_token() -> Self {
Self::InvalidQuoteToken(ITIP20::InvalidQuoteToken {})
}
pub const fn string_too_long() -> Self {
Self::StringTooLong(ITIP20::StringTooLong {})
}
pub const fn policy_forbids() -> Self {
Self::PolicyForbids(ITIP20::PolicyForbids {})
}
pub const fn invalid_recipient() -> Self {
Self::InvalidRecipient(ITIP20::InvalidRecipient {})
}
pub const fn contract_paused() -> Self {
Self::ContractPaused(ITIP20::ContractPaused {})
}
pub const fn invalid_currency() -> Self {
Self::InvalidCurrency(ITIP20::InvalidCurrency {})
}
pub const fn transfers_disabled() -> Self {
Self::TransfersDisabled(ITIP20::TransfersDisabled {})
}
pub const fn invalid_amount() -> Self {
Self::InvalidAmount(ITIP20::InvalidAmount {})
}
pub const fn no_opted_in_supply() -> Self {
Self::NoOptedInSupply(ITIP20::NoOptedInSupply {})
}
pub const fn protected_address() -> Self {
Self::ProtectedAddress(ITIP20::ProtectedAddress {})
}
pub const fn invalid_token() -> Self {
Self::InvalidToken(ITIP20::InvalidToken {})
}
pub const fn invalid_transfer_policy_id() -> Self {
Self::InvalidTransferPolicyId(ITIP20::InvalidTransferPolicyId {})
}
pub const fn uninitialized() -> Self {
Self::Uninitialized(ITIP20::Uninitialized {})
}
pub const fn permit_expired() -> Self {
Self::PermitExpired(ITIP20::PermitExpired {})
}
pub const fn invalid_signature() -> Self {
Self::InvalidSignature(ITIP20::InvalidSignature {})
}
}
#[cfg(test)]
mod test {
use super::*;
use alloc::vec::Vec;
use alloy_primitives::{Address, B256, U256};
#[rustfmt::skip]
fn payment_calldatas() -> [Vec<u8>; 9] {
let (to, from, amount, memo) = (Address::random(), Address::random(), U256::random(), B256::random());
[
ITIP20::transferCall { to, amount }.abi_encode(),
ITIP20::transferWithMemoCall { to, amount, memo }.abi_encode(),
ITIP20::transferFromCall { from, to, amount }.abi_encode(),
ITIP20::transferFromWithMemoCall { from, to, amount, memo }.abi_encode(),
ITIP20::approveCall { spender: to, amount }.abi_encode(),
ITIP20::mintCall { to, amount }.abi_encode(),
ITIP20::mintWithMemoCall { to, amount, memo }.abi_encode(),
ITIP20::burnCall { amount }.abi_encode(),
ITIP20::burnWithMemoCall { amount, memo }.abi_encode(),
]
}
#[rustfmt::skip]
fn non_payment_calldatas() -> [Vec<u8>; 3] {
let mut data = ITIP20::transferCall { to: Address::random(), amount: U256::random() }.abi_encode();
data[..4].copy_from_slice(&[0xde, 0xad, 0xbe, 0xef]);
[
ITIP20::claimRewardsCall {}.abi_encode(),
ITIP20::permitCall {
owner: Address::random(), spender: Address::random(), value: U256::random(), deadline: U256::random(),
v: u8::MAX, r: B256::random(), s: B256::random() }.abi_encode(),
data,
]
}
#[test]
fn test_is_payment() {
for calldata in payment_calldatas() {
assert!(ITIP20::ITIP20Calls::is_payment(&calldata))
}
for calldata in non_payment_calldatas() {
assert!(!ITIP20::ITIP20Calls::is_payment(&calldata))
}
}
}